You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ab...@apache.org on 2018/08/26 21:18:55 UTC

svn commit: r1839255 [2/3] - in /poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel: ./ chart/ text/

Added: poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFParagraphProperties.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFParagraphProperties.java?rev=1839255&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFParagraphProperties.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFParagraphProperties.java Sun Aug 26 21:18:54 2018
@@ -0,0 +1,258 @@
+/* ====================================================================
+   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.xddf.usermodel.text;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.poi.util.Beta;
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.Units;
+import org.apache.poi.xddf.usermodel.XDDFExtensionList;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
+
+@Beta
+public class XDDFParagraphProperties {
+    private CTTextParagraphProperties props;
+    private XDDFParagraphBulletProperties bullet;
+
+    @Internal
+    protected XDDFParagraphProperties(CTTextParagraphProperties properties) {
+        this.props = properties;
+        this.bullet = new XDDFParagraphBulletProperties(properties);
+    }
+
+    @Internal
+    protected CTTextParagraphProperties getXmlObject() {
+        return props;
+    }
+
+    public XDDFParagraphBulletProperties getBulletProperties() {
+        return bullet;
+    }
+
+    public int getLevel() {
+        if (props.isSetLvl()) {
+            return 1 + props.getLvl();
+        } else {
+            return 0;
+        }
+    }
+
+    public void setLevel(Integer level) {
+        if (level == null) {
+            props.unsetLvl();
+        } else if (level < 1 || 9 < level) {
+            throw new IllegalArgumentException("Minimum inclusive: 1. Maximum inclusive: 9.");
+        } else {
+            props.setLvl(level - 1);
+        }
+    }
+
+    public XDDFRunProperties getDefaultRunProperties() {
+        if (props.isSetDefRPr()) {
+            return new XDDFRunProperties(props.getDefRPr());
+        } else {
+            return null;
+        }
+    }
+
+    public void setDefaultRunProperties(XDDFRunProperties properties) {
+        if (properties == null) {
+            props.unsetDefRPr();
+        } else {
+            props.setDefRPr(properties.getXmlObject());
+        }
+    }
+
+    public void setEastAsianLineBreak(Boolean value) {
+        if (value == null) {
+            props.unsetEaLnBrk();
+        } else {
+            props.setEaLnBrk(value);
+        }
+    }
+
+    public void setLatinLineBreak(Boolean value) {
+        if (value == null) {
+            props.unsetLatinLnBrk();
+        } else {
+            props.setLatinLnBrk(value);
+        }
+    }
+
+    public void setHangingPunctuation(Boolean value) {
+        if (value == null) {
+            props.unsetHangingPunct();
+        } else {
+            props.setHangingPunct(value);
+        }
+    }
+
+    public void setRightToLeft(Boolean value) {
+        if (value == null) {
+            props.unsetRtl();
+        } else {
+            props.setRtl(value);
+        }
+    }
+
+    public void setFontAlignment(FontAlignment align) {
+        if (align == null) {
+            props.unsetFontAlgn();
+        } else {
+            props.setFontAlgn(align.underlying);
+        }
+    }
+
+    public void setTextAlignment(TextAlignment align) {
+        if (align == null) {
+            props.unsetAlgn();
+        } else {
+            props.setAlgn(align.underlying);
+        }
+    }
+
+    public void setDefaultTabSize(Double points) {
+        if (points == null) {
+            props.unsetDefTabSz();
+        } else {
+            props.setDefTabSz(Units.toEMU(points));
+        }
+    }
+
+    public void setIndentation(Double points) {
+        if (points == null) {
+            props.unsetIndent();
+        } else if (points < -4032 || 4032 < points) {
+            throw new IllegalArgumentException("Minimum inclusive = -4032. Maximum inclusive = 4032.");
+        } else {
+            props.setIndent(Units.toEMU(points));
+        }
+    }
+
+    public void setMarginLeft(Double points) {
+        if (points == null) {
+            props.unsetMarL();
+        } else if (points < 0 || 4032 < points) {
+            throw new IllegalArgumentException("Minimum inclusive = 0. Maximum inclusive = 4032.");
+        } else {
+            props.setMarL(Units.toEMU(points));
+        }
+    }
+
+    public void setMarginRight(Double points) {
+        if (points == null) {
+            props.unsetMarR();
+        } else if (points < 0 || 4032 < points) {
+            throw new IllegalArgumentException("Minimum inclusive = 0. Maximum inclusive = 4032.");
+        } else {
+            props.setMarR(Units.toEMU(points));
+        }
+    }
+
+    public void setLineSpacing(XDDFSpacing spacing) {
+        if (spacing == null) {
+            props.unsetLnSpc();
+        } else {
+            props.setLnSpc(spacing.getXmlObject());
+        }
+    }
+
+    public void setSpaceAfter(XDDFSpacing spacing) {
+        if (spacing == null) {
+            props.unsetSpcAft();
+        } else {
+            props.setSpcAft(spacing.getXmlObject());
+        }
+    }
+
+    public void setSpaceBefore(XDDFSpacing spacing) {
+        if (spacing == null) {
+            props.unsetSpcBef();
+        } else {
+            props.setSpcBef(spacing.getXmlObject());
+        }
+    }
+
+    public XDDFTabStop addTabStop() {
+        if (!props.isSetTabLst()) {
+            props.addNewTabLst();
+        }
+        return new XDDFTabStop(props.getTabLst().addNewTab());
+    }
+
+    public XDDFTabStop insertTabStop(int index) {
+        if (!props.isSetTabLst()) {
+            props.addNewTabLst();
+        }
+        return new XDDFTabStop(props.getTabLst().insertNewTab(index));
+    }
+
+    public void removeTabStop(int index) {
+        if (props.isSetTabLst()) {
+            props.getTabLst().removeTab(index);
+        }
+    }
+
+    public XDDFTabStop getTabStop(int index) {
+        if (props.isSetTabLst()) {
+            return new XDDFTabStop(props.getTabLst().getTabArray(index));
+        } else {
+            return null;
+        }
+    }
+
+    public List<XDDFTabStop> getTabStops() {
+        if (props.isSetTabLst()) {
+            return Collections.unmodifiableList(props
+                .getTabLst()
+                .getTabList()
+                .stream()
+                .map(gs -> new XDDFTabStop(gs))
+                .collect(Collectors.toList()));
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    public int countTabStops() {
+        if (props.isSetTabLst()) {
+            return props.getTabLst().sizeOfTabArray();
+        } else {
+            return 0;
+        }
+    }
+
+    public XDDFExtensionList getExtensionList() {
+        if (props.isSetExtLst()) {
+            return new XDDFExtensionList(props.getExtLst());
+        } else {
+            return null;
+        }
+    }
+
+    public void setExtensionList(XDDFExtensionList list) {
+        if (list == null) {
+            props.unsetExtLst();
+        } else {
+            props.setExtLst(list.getXmlObject());
+        }
+    }
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFRunProperties.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFRunProperties.java?rev=1839255&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFRunProperties.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFRunProperties.java Sun Aug 26 21:18:54 2018
@@ -0,0 +1,335 @@
+/* ====================================================================
+   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.xddf.usermodel.text;
+
+import java.util.Locale;
+
+import org.apache.poi.util.Beta;
+import org.apache.poi.util.Internal;
+import org.apache.poi.xddf.usermodel.XDDFColor;
+import org.apache.poi.xddf.usermodel.XDDFEffectContainer;
+import org.apache.poi.xddf.usermodel.XDDFEffectList;
+import org.apache.poi.xddf.usermodel.XDDFExtensionList;
+import org.apache.poi.xddf.usermodel.XDDFFillProperties;
+import org.apache.poi.xddf.usermodel.XDDFGradientFillProperties;
+import org.apache.poi.xddf.usermodel.XDDFGroupFillProperties;
+import org.apache.poi.xddf.usermodel.XDDFLineProperties;
+import org.apache.poi.xddf.usermodel.XDDFNoFillProperties;
+import org.apache.poi.xddf.usermodel.XDDFPatternFillProperties;
+import org.apache.poi.xddf.usermodel.XDDFPictureFillProperties;
+import org.apache.poi.xddf.usermodel.XDDFSolidFillProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;
+
+@Beta
+public class XDDFRunProperties {
+    private CTTextCharacterProperties props;
+
+    public XDDFRunProperties() {
+        this(CTTextCharacterProperties.Factory.newInstance());
+    }
+
+    @Internal
+    protected XDDFRunProperties(CTTextCharacterProperties properties) {
+        this.props = properties;
+    }
+
+    @Internal
+    protected CTTextCharacterProperties getXmlObject() {
+        return props;
+    }
+
+    public void setBaseline(Integer value) {
+        if (value == null) {
+            props.unsetBaseline();
+        } else {
+            props.setBaseline(value);
+        }
+    }
+
+    public void setDirty(Boolean dirty) {
+        if (dirty == null) {
+            props.unsetDirty();
+        } else {
+            props.setDirty(dirty);
+        }
+    }
+
+    public void setSpellError(Boolean error) {
+        if (error == null) {
+            props.unsetErr();
+        } else {
+            props.setErr(error);
+        }
+    }
+
+    public void setNoProof(Boolean noproof) {
+        if (noproof == null) {
+            props.unsetNoProof();
+        } else {
+            props.setNoProof(noproof);
+        }
+    }
+
+    public void setNormalizeHeights(Boolean normalize) {
+        if (normalize == null) {
+            props.unsetNormalizeH();
+        } else {
+            props.setNormalizeH(normalize);
+        }
+    }
+
+    public void setKumimoji(Boolean kumimoji) {
+        if (kumimoji == null) {
+            props.unsetKumimoji();
+        } else {
+            props.setKumimoji(kumimoji);
+        }
+    }
+
+    public void setBold(Boolean bold) {
+        if (bold == null) {
+            props.unsetB();
+        } else {
+            props.setB(bold);
+        }
+    }
+
+    public void setItalic(Boolean italic) {
+        if (italic == null) {
+            props.unsetI();
+        } else {
+            props.setI(italic);
+        }
+    }
+
+    public void setFontSize(Double size) {
+        if (size == null) {
+            props.unsetSz();
+        } else if (size < 1 || 400 < size) {
+            throw new IllegalArgumentException("Minimum inclusive = 1. Maximum inclusive = 400.");
+        } else {
+            props.setSz((int)(100 * size));
+        }
+    }
+
+    public void setFillProperties(XDDFFillProperties properties) {
+        props.unsetBlipFill();
+        props.unsetGradFill();
+        props.unsetGrpFill();
+        props.unsetNoFill();
+        props.unsetPattFill();
+        props.unsetSolidFill();
+        if (properties == null) {
+            return;
+        }
+        if (properties instanceof XDDFGradientFillProperties) {
+            props.setGradFill(((XDDFGradientFillProperties) properties).getXmlObject());
+        } else if (properties instanceof XDDFGroupFillProperties) {
+            props.setGrpFill(((XDDFGroupFillProperties) properties).getXmlObject());
+        } else if (properties instanceof XDDFNoFillProperties) {
+            props.setNoFill(((XDDFNoFillProperties) properties).getXmlObject());
+        } else if (properties instanceof XDDFPatternFillProperties) {
+            props.setPattFill(((XDDFPatternFillProperties) properties).getXmlObject());
+        } else if (properties instanceof XDDFPictureFillProperties) {
+            props.setBlipFill(((XDDFPictureFillProperties) properties).getXmlObject());
+        } else if (properties instanceof XDDFSolidFillProperties) {
+            props.setSolidFill(((XDDFSolidFillProperties) properties).getXmlObject());
+        }
+    }
+
+    public void setCharacterKerning(Double kerning) {
+        if (kerning == null) {
+            props.unsetKern();
+        } else if (kerning < 0 || 4000 < kerning) {
+            throw new IllegalArgumentException("Minimum inclusive = 0. Maximum inclusive = 4000.");
+        } else {
+            props.setKern((int)(100*kerning));
+        }
+    }
+
+    public void setCharacterSpacing(Double spacing) {
+        if (spacing == null) {
+            props.unsetSpc();
+        } else if (spacing < -4000 || 4000 < spacing) {
+            throw new IllegalArgumentException("Minimum inclusive = -4000. Maximum inclusive = 4000.");
+        } else {
+            props.setSpc((int)(100*spacing));
+        }
+    }
+
+    public void setFonts(XDDFFont[] fonts) {
+        for (XDDFFont font: fonts) {
+            CTTextFont xml = font.getXmlObject();
+            switch (font.getGroup()) {
+            case COMPLEX_SCRIPT:
+                if (xml == null) {
+                    props.unsetCs();
+                } else {
+                    props.setCs(xml);
+                }
+            case EAST_ASIAN:
+                if (xml == null) {
+                    props.unsetEa();
+                } else {
+                    props.setEa(xml);
+                }
+            case LATIN:
+                if (xml == null) {
+                    props.unsetLatin();
+                } else {
+                    props.setLatin(xml);
+                }
+            case SYMBOL:
+                if (xml == null) {
+                    props.unsetSym();
+                } else {
+                    props.setSym(xml);
+                }
+            }
+        }
+    }
+
+    public void setUnderline(UnderlineType underline) {
+        if (underline == null) {
+            props.unsetU();
+        } else {
+            props.setU(underline.underlying);
+        }
+    }
+
+    public void setStrikeThrough(StrikeType strike) {
+        if (strike == null) {
+            props.unsetStrike();
+        } else {
+            props.setStrike(strike.underlying);
+        }
+    }
+
+    public void setCapitals(CapsType caps) {
+        if (caps == null) {
+            props.unsetCap();
+        } else {
+            props.setCap(caps.underlying);
+        }
+    }
+
+    public void setHyperlink(XDDFHyperlink link) {
+        if (link == null) {
+            props.unsetHlinkClick();
+        } else {
+            props.setHlinkClick(link.getXmlObject());
+        }
+    }
+
+    public void setMouseOver(XDDFHyperlink link) {
+        if (link == null) {
+            props.unsetHlinkMouseOver();
+        } else {
+            props.setHlinkMouseOver(link.getXmlObject());
+        }
+    }
+
+    public void setLanguage(Locale lang) {
+        if (lang == null) {
+            props.unsetLang();
+        } else {
+            props.setLang(lang.toLanguageTag());
+        }
+    }
+
+    public void setAlternativeLanguage(Locale lang) {
+        if (lang == null) {
+            props.unsetAltLang();
+        } else {
+            props.setAltLang(lang.toLanguageTag());
+        }
+    }
+
+    public void setHighlight(XDDFColor color) {
+        if (color == null) {
+            props.unsetHighlight();
+        } else {
+            props.setHighlight(color.getColorContainer());
+        }
+    }
+
+    public void setLineProperties(XDDFLineProperties properties) {
+        if (properties == null) {
+            props.unsetLn();
+        } else {
+            props.setLn(properties.getXmlObject());
+        }
+    }
+
+    public void setBookmark(String bookmark) {
+        if (bookmark == null) {
+            props.unsetBmk();
+        } else {
+            props.setBmk(bookmark);
+        }
+    }
+
+    public XDDFExtensionList getExtensionList() {
+        if (props.isSetExtLst()) {
+            return new XDDFExtensionList(props.getExtLst());
+        } else {
+            return null;
+        }
+    }
+
+    public void setExtensionList(XDDFExtensionList list) {
+        if (list == null) {
+            props.unsetExtLst();
+        } else {
+            props.setExtLst(list.getXmlObject());
+        }
+    }
+
+    public XDDFEffectContainer getEffectContainer() {
+        if (props.isSetEffectDag()) {
+            return new XDDFEffectContainer(props.getEffectDag());
+        } else {
+            return null;
+        }
+    }
+
+    public void setEffectContainer(XDDFEffectContainer container) {
+        if (container == null) {
+            props.unsetEffectDag();
+        } else {
+            props.setEffectDag(container.getXmlObject());
+        }
+    }
+
+    public XDDFEffectList getEffectList() {
+        if (props.isSetEffectLst()) {
+            return new XDDFEffectList(props.getEffectLst());
+        } else {
+            return null;
+        }
+    }
+
+    public void setEffectList(XDDFEffectList list) {
+        if (list == null) {
+            props.unsetEffectLst();
+        } else {
+            props.setEffectLst(list.getXmlObject());
+        }
+    }
+}

Copied: poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFShapeAutoFit.java (from r1839252, poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java)
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFShapeAutoFit.java?p2=poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFShapeAutoFit.java&p1=poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java&r1=1839252&r2=1839255&rev=1839255&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFShapeAutoFit.java Sun Aug 26 21:18:54 2018
@@ -19,23 +19,33 @@ package org.apache.poi.xddf.usermodel.te
 
 import org.apache.poi.util.Beta;
 import org.apache.poi.util.Internal;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextShapeAutofit;
 
 @Beta
-public class XDDFTextBody {
-    private CTTextBody body;
+public class XDDFShapeAutoFit implements XDDFAutoFit {
+    private CTTextShapeAutofit autofit;
 
-    public XDDFTextBody() {
-        this(CTTextBody.Factory.newInstance());
+    public XDDFShapeAutoFit() {
+        this(CTTextShapeAutofit.Factory.newInstance());
     }
 
     @Internal
-    public XDDFTextBody(CTTextBody body) {
-        this.body = body;
+    protected XDDFShapeAutoFit(CTTextShapeAutofit autofit) {
+        this.autofit = autofit;
     }
 
     @Internal
-    public CTTextBody getXmlObject() {
-        return body;
+    protected CTTextShapeAutofit getXmlObject() {
+        return autofit;
+    }
+
+    @Override
+    public int getFontScale() {
+        return 100_000;
+    }
+
+    @Override
+    public int getLineSpaceReduction() {
+        return 0;
     }
 }

Copied: poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFSpacing.java (from r1839252, poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java)
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFSpacing.java?p2=poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFSpacing.java&p1=poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java&r1=1839252&r2=1839255&rev=1839255&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFSpacing.java Sun Aug 26 21:18:54 2018
@@ -19,23 +19,26 @@ package org.apache.poi.xddf.usermodel.te
 
 import org.apache.poi.util.Beta;
 import org.apache.poi.util.Internal;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing;
 
 @Beta
-public class XDDFTextBody {
-    private CTTextBody body;
-
-    public XDDFTextBody() {
-        this(CTTextBody.Factory.newInstance());
+public abstract class XDDFSpacing {
+    public static enum Kind {
+        PERCENT,
+        POINTS;
     }
 
+    protected CTTextSpacing spacing;
+
     @Internal
-    public XDDFTextBody(CTTextBody body) {
-        this.body = body;
+    protected XDDFSpacing(CTTextSpacing spacing) {
+        this.spacing = spacing;
     }
 
+    public abstract Kind getType();
+
     @Internal
-    public CTTextBody getXmlObject() {
-        return body;
+    protected CTTextSpacing getXmlObject() {
+        return spacing;
     }
 }

Copied: poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFSpacingPercent.java (from r1839252, poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java)
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFSpacingPercent.java?p2=poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFSpacingPercent.java&p1=poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java&r1=1839252&r2=1839255&rev=1839255&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFSpacingPercent.java Sun Aug 26 21:18:54 2018
@@ -19,23 +19,38 @@ package org.apache.poi.xddf.usermodel.te
 
 import org.apache.poi.util.Beta;
 import org.apache.poi.util.Internal;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacingPercent;
 
 @Beta
-public class XDDFTextBody {
-    private CTTextBody body;
+public class XDDFSpacingPercent extends XDDFSpacing {
+    private CTTextSpacingPercent percent;
+    private Double scale;
 
-    public XDDFTextBody() {
-        this(CTTextBody.Factory.newInstance());
+    public XDDFSpacingPercent(double value) {
+        this(CTTextSpacing.Factory.newInstance(), CTTextSpacingPercent.Factory.newInstance(), null);
+        spacing.unsetSpcPts();
+        spacing.setSpcPct(percent);
+        setPercent(value);
     }
 
     @Internal
-    public XDDFTextBody(CTTextBody body) {
-        this.body = body;
+    protected XDDFSpacingPercent(CTTextSpacing parent, CTTextSpacingPercent percent, Double scale) {
+        super(parent);
+        this.percent = percent;
+        this.scale = (scale == null) ? 0.001 : scale * 0.001;
     }
 
-    @Internal
-    public CTTextBody getXmlObject() {
-        return body;
+    @Override
+    public Kind getType() {
+        return Kind.PERCENT;
+    }
+
+    public double getPercent() {
+        return percent.getVal() * scale;
+    }
+
+    public void setPercent(double value) {
+        percent.setVal((int)(1000 * value));
     }
 }

Copied: poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFSpacingPoints.java (from r1839252, poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java)
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFSpacingPoints.java?p2=poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFSpacingPoints.java&p1=poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java&r1=1839252&r2=1839255&rev=1839255&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFSpacingPoints.java Sun Aug 26 21:18:54 2018
@@ -19,23 +19,36 @@ package org.apache.poi.xddf.usermodel.te
 
 import org.apache.poi.util.Beta;
 import org.apache.poi.util.Internal;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacingPoint;
 
 @Beta
-public class XDDFTextBody {
-    private CTTextBody body;
+public class XDDFSpacingPoints extends XDDFSpacing {
+    private CTTextSpacingPoint points;
 
-    public XDDFTextBody() {
-        this(CTTextBody.Factory.newInstance());
+    public XDDFSpacingPoints(double value) {
+        this(CTTextSpacing.Factory.newInstance(), CTTextSpacingPoint.Factory.newInstance());
+        spacing.unsetSpcPct();
+        spacing.setSpcPts(points);
+        setPoints(value);
     }
 
     @Internal
-    public XDDFTextBody(CTTextBody body) {
-        this.body = body;
+    protected XDDFSpacingPoints(CTTextSpacing parent, CTTextSpacingPoint points) {
+        super(parent);
+        this.points = points;
     }
 
-    @Internal
-    public CTTextBody getXmlObject() {
-        return body;
+    @Override
+    public Kind getType() {
+        return Kind.POINTS;
+    }
+
+    public double getPoints() {
+        return points.getVal() * 0.01;
+    }
+
+    public void setPoints(double value) {
+        points.setVal((int)(100 * value));
     }
 }

Copied: poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTabStop.java (from r1839252, poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java)
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTabStop.java?p2=poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTabStop.java&p1=poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java&r1=1839252&r2=1839255&rev=1839255&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTabStop.java Sun Aug 26 21:18:54 2018
@@ -19,23 +19,52 @@ package org.apache.poi.xddf.usermodel.te
 
 import org.apache.poi.util.Beta;
 import org.apache.poi.util.Internal;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
+import org.apache.poi.util.Units;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStop;
 
 @Beta
-public class XDDFTextBody {
-    private CTTextBody body;
+public class XDDFTabStop {
+    private CTTextTabStop stop;
 
-    public XDDFTextBody() {
-        this(CTTextBody.Factory.newInstance());
+    @Internal
+    protected XDDFTabStop(CTTextTabStop stop) {
+        this.stop = stop;
     }
 
     @Internal
-    public XDDFTextBody(CTTextBody body) {
-        this.body = body;
+    protected CTTextTabStop getXmlObject() {
+        return stop;
     }
 
-    @Internal
-    public CTTextBody getXmlObject() {
-        return body;
+    public TabAlignment getAlignment() {
+        if (stop.isSetAlgn()) {
+            return TabAlignment.valueOf(stop.getAlgn());
+        } else {
+            return null;
+        }
+    }
+
+    public void setAlignment(TabAlignment align) {
+        if (align == null) {
+            stop.unsetAlgn();
+        } else {
+            stop.setAlgn(align.underlying);
+        }
+    }
+
+    public Double getPosition() {
+        if (stop.isSetPos()) {
+            return Units.toPoints(stop.getPos());
+        } else {
+            return null;
+        }
+    }
+
+    public void setPosition(Double position) {
+        if (position == null) {
+            stop.unsetPos();
+        } else {
+            stop.setPos(Units.toEMU(position));
+        }
     }
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java?rev=1839255&r1=1839254&r2=1839255&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextBody.java Sun Aug 26 21:18:54 2018
@@ -17,25 +17,350 @@
 
 package org.apache.poi.xddf.usermodel.text;
 
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
 import org.apache.poi.util.Beta;
 import org.apache.poi.util.Internal;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
 
 @Beta
 public class XDDFTextBody {
-    private CTTextBody body;
+    private CTTextBody _body;
+    private TextContainer _parent;
 
-    public XDDFTextBody() {
-        this(CTTextBody.Factory.newInstance());
+    public XDDFTextBody(TextContainer parent) {
+        this(parent, CTTextBody.Factory.newInstance());
+        this._body.addNewBodyPr();
     }
 
     @Internal
-    public XDDFTextBody(CTTextBody body) {
-        this.body = body;
+    public XDDFTextBody(TextContainer parent, CTTextBody body) {
+        this._parent = parent;
+        this._body = body;
     }
 
     @Internal
     public CTTextBody getXmlObject() {
-        return body;
+        return _body;
+    }
+
+    public TextContainer getParentShape() {
+        return _parent;
+    }
+
+    public XDDFTextParagraph addNewParagraph() {
+        return new XDDFTextParagraph(_body.addNewP(), this);
+    }
+
+    public XDDFTextParagraph insertNewParagraph(int index) {
+        return new XDDFTextParagraph(_body.insertNewP(index), this);
+    }
+
+    public void removeParagraph(int index) {
+        _body.removeP(index);
+    }
+
+    public XDDFTextParagraph getParagraph(int index) {
+        return new XDDFTextParagraph(_body.getPArray(index), this);
+    }
+
+    public List<XDDFTextParagraph> getParagraphs() {
+        return Collections.unmodifiableList(_body
+            .getPList()
+            .stream()
+            .map(ds -> new XDDFTextParagraph(ds, this))
+            .collect(Collectors.toList()));
+    }
+
+    public XDDFBodyProperties getBodyProperties() {
+        return new XDDFBodyProperties(_body.getBodyPr());
+    }
+
+    public void setBodyProperties(XDDFBodyProperties properties) {
+        if (properties == null) {
+            _body.addNewBodyPr();
+        } else {
+            _body.setBodyPr(properties.getXmlObject());
+        }
+    }
+
+    public XDDFParagraphProperties getDefaultProperties() {
+        if (_body.isSetLstStyle() && _body.getLstStyle().isSetDefPPr()) {
+            return new XDDFParagraphProperties(_body.getLstStyle().getDefPPr());
+        } else {
+            return null;
+        }
+    }
+
+    public void setDefaultProperties(XDDFParagraphProperties properties) {
+        if (properties == null) {
+            if (_body.isSetLstStyle()) {
+                _body.getLstStyle().unsetDefPPr();
+            }
+        } else {
+            CTTextListStyle style = _body.isSetLstStyle() ? _body.getLstStyle() : _body.addNewLstStyle();
+            style.setDefPPr(properties.getXmlObject());
+        }
+    }
+
+    public XDDFParagraphProperties getLevel1Properties() {
+        if (_body.isSetLstStyle() && _body.getLstStyle().isSetLvl1PPr()) {
+            return new XDDFParagraphProperties(_body.getLstStyle().getLvl1PPr());
+        } else {
+            return null;
+        }
+    }
+
+    public void setLevel1Properties(XDDFParagraphProperties properties) {
+        if (properties == null) {
+            if (_body.isSetLstStyle()) {
+                _body.getLstStyle().unsetLvl1PPr();
+            }
+        } else {
+            CTTextListStyle style = _body.isSetLstStyle() ? _body.getLstStyle() : _body.addNewLstStyle();
+            style.setLvl1PPr(properties.getXmlObject());
+        }
+    }
+
+    public XDDFParagraphProperties getLevel2Properties() {
+        if (_body.isSetLstStyle() && _body.getLstStyle().isSetLvl2PPr()) {
+            return new XDDFParagraphProperties(_body.getLstStyle().getLvl2PPr());
+        } else {
+            return null;
+        }
+    }
+
+    public void setLevel2Properties(XDDFParagraphProperties properties) {
+        if (properties == null) {
+            if (_body.isSetLstStyle()) {
+                _body.getLstStyle().unsetLvl2PPr();
+            }
+        } else {
+            CTTextListStyle style = _body.isSetLstStyle() ? _body.getLstStyle() : _body.addNewLstStyle();
+            style.setLvl2PPr(properties.getXmlObject());
+        }
+    }
+
+    public XDDFParagraphProperties getLevel3Properties() {
+        if (_body.isSetLstStyle() && _body.getLstStyle().isSetLvl3PPr()) {
+            return new XDDFParagraphProperties(_body.getLstStyle().getLvl3PPr());
+        } else {
+            return null;
+        }
+    }
+
+    public void setLevel3Properties(XDDFParagraphProperties properties) {
+        if (properties == null) {
+            if (_body.isSetLstStyle()) {
+                _body.getLstStyle().unsetLvl3PPr();
+            }
+        } else {
+            CTTextListStyle style = _body.isSetLstStyle() ? _body.getLstStyle() : _body.addNewLstStyle();
+            style.setLvl3PPr(properties.getXmlObject());
+        }
+    }
+
+    public XDDFParagraphProperties getLevel4Properties() {
+        if (_body.isSetLstStyle() && _body.getLstStyle().isSetLvl4PPr()) {
+            return new XDDFParagraphProperties(_body.getLstStyle().getLvl4PPr());
+        } else {
+            return null;
+        }
+    }
+
+    public void setLevel4Properties(XDDFParagraphProperties properties) {
+        if (properties == null) {
+            if (_body.isSetLstStyle()) {
+                _body.getLstStyle().unsetLvl4PPr();
+            }
+        } else {
+            CTTextListStyle style = _body.isSetLstStyle() ? _body.getLstStyle() : _body.addNewLstStyle();
+            style.setLvl4PPr(properties.getXmlObject());
+        }
+    }
+
+    public XDDFParagraphProperties getLevel5Properties() {
+        if (_body.isSetLstStyle() && _body.getLstStyle().isSetLvl5PPr()) {
+            return new XDDFParagraphProperties(_body.getLstStyle().getLvl5PPr());
+        } else {
+            return null;
+        }
+    }
+
+    public void setLevel5Properties(XDDFParagraphProperties properties) {
+        if (properties == null) {
+            if (_body.isSetLstStyle()) {
+                _body.getLstStyle().unsetLvl5PPr();
+            }
+        } else {
+            CTTextListStyle style = _body.isSetLstStyle() ? _body.getLstStyle() : _body.addNewLstStyle();
+            style.setLvl5PPr(properties.getXmlObject());
+        }
+    }
+
+    public XDDFParagraphProperties getLevel6Properties() {
+        if (_body.isSetLstStyle() && _body.getLstStyle().isSetLvl6PPr()) {
+            return new XDDFParagraphProperties(_body.getLstStyle().getLvl6PPr());
+        } else {
+            return null;
+        }
+    }
+
+    public void setLevel6Properties(XDDFParagraphProperties properties) {
+        if (properties == null) {
+            if (_body.isSetLstStyle()) {
+                _body.getLstStyle().unsetLvl6PPr();
+            }
+        } else {
+            CTTextListStyle style = _body.isSetLstStyle() ? _body.getLstStyle() : _body.addNewLstStyle();
+            style.setLvl6PPr(properties.getXmlObject());
+        }
+    }
+
+    public XDDFParagraphProperties getLevel7Properties() {
+        if (_body.isSetLstStyle() && _body.getLstStyle().isSetLvl7PPr()) {
+            return new XDDFParagraphProperties(_body.getLstStyle().getLvl7PPr());
+        } else {
+            return null;
+        }
+    }
+
+    public void setLevel7Properties(XDDFParagraphProperties properties) {
+        if (properties == null) {
+            if (_body.isSetLstStyle()) {
+                _body.getLstStyle().unsetLvl7PPr();
+            }
+        } else {
+            CTTextListStyle style = _body.isSetLstStyle() ? _body.getLstStyle() : _body.addNewLstStyle();
+            style.setLvl7PPr(properties.getXmlObject());
+        }
+    }
+
+    public XDDFParagraphProperties getLevel8Properties() {
+        if (_body.isSetLstStyle() && _body.getLstStyle().isSetLvl8PPr()) {
+            return new XDDFParagraphProperties(_body.getLstStyle().getLvl8PPr());
+        } else {
+            return null;
+        }
+    }
+
+    public void setLevel8Properties(XDDFParagraphProperties properties) {
+        if (properties == null) {
+            if (_body.isSetLstStyle()) {
+                _body.getLstStyle().unsetLvl8PPr();
+            }
+        } else {
+            CTTextListStyle style = _body.isSetLstStyle() ? _body.getLstStyle() : _body.addNewLstStyle();
+            style.setLvl8PPr(properties.getXmlObject());
+        }
+    }
+
+    public XDDFParagraphProperties getLevel9Properties() {
+        if (_body.isSetLstStyle() && _body.getLstStyle().isSetLvl9PPr()) {
+            return new XDDFParagraphProperties(_body.getLstStyle().getLvl9PPr());
+        } else {
+            return null;
+        }
+    }
+
+    public void setLevel9Properties(XDDFParagraphProperties properties) {
+        if (properties == null) {
+            if (_body.isSetLstStyle()) {
+                _body.getLstStyle().unsetLvl9PPr();
+            }
+        } else {
+            CTTextListStyle style = _body.isSetLstStyle() ? _body.getLstStyle() : _body.addNewLstStyle();
+            style.setLvl9PPr(properties.getXmlObject());
+        }
+    }
+
+    @Internal
+    protected <R> Optional<R> findDefinedParagraphProperty(Function<CTTextParagraphProperties, Boolean> isSet,
+        Function<CTTextParagraphProperties, R> getter, int level) {
+        if (_body.isSetLstStyle() && level >= 0) {
+            CTTextListStyle list = _body.getLstStyle();
+            CTTextParagraphProperties props = level == 0 ? list.getDefPPr() : retrieveProperties(list, level);
+            if (props != null && isSet.apply(props)) {
+                return Optional.of(getter.apply(props));
+            } else {
+                return findDefinedParagraphProperty(isSet, getter, level - 1);
+            }
+        } else {
+            return _parent.findDefinedParagraphProperty(isSet, getter);
+        }
+    }
+
+    @Internal
+    protected <R> Optional<R> findDefinedRunProperty(Function<CTTextCharacterProperties, Boolean> isSet,
+        Function<CTTextCharacterProperties, R> getter, int level) {
+        if (_body.isSetLstStyle() && level >= 0) {
+            CTTextListStyle list = _body.getLstStyle();
+            CTTextParagraphProperties props = level == 0 ? list.getDefPPr() : retrieveProperties(list, level);
+            if (props != null && props.isSetDefRPr() && isSet.apply(props.getDefRPr())) {
+                return Optional.of(getter.apply(props.getDefRPr()));
+            } else {
+                return findDefinedRunProperty(isSet, getter, level - 1);
+            }
+        } else {
+            return _parent.findDefinedRunProperty(isSet, getter);
+        }
+    }
+
+    private CTTextParagraphProperties retrieveProperties(CTTextListStyle list, int level) {
+        switch(level) {
+        case 1: if (list.isSetLvl1PPr()) {
+            return list.getLvl1PPr();
+        } else {
+            return null;
+        }
+        case 2: if (list.isSetLvl2PPr()) {
+            return list.getLvl2PPr();
+        } else {
+            return null;
+        }
+        case 3: if (list.isSetLvl3PPr()) {
+            return list.getLvl3PPr();
+        } else {
+            return null;
+        }
+        case 4: if (list.isSetLvl4PPr()) {
+            return list.getLvl4PPr();
+        } else {
+            return null;
+        }
+        case 5: if (list.isSetLvl5PPr()) {
+            return list.getLvl5PPr();
+        } else {
+            return null;
+        }
+        case 6: if (list.isSetLvl6PPr()) {
+            return list.getLvl6PPr();
+        } else {
+            return null;
+        }
+        case 7: if (list.isSetLvl7PPr()) {
+            return list.getLvl7PPr();
+        } else {
+            return null;
+        }
+        case 8: if (list.isSetLvl8PPr()) {
+            return list.getLvl8PPr();
+        } else {
+            return null;
+        }
+        case 9: if (list.isSetLvl9PPr()) {
+            return list.getLvl9PPr();
+        } else {
+            return null;
+        }
+        default: return null;
+        }
     }
 }

Added: poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextParagraph.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextParagraph.java?rev=1839255&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextParagraph.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xddf/usermodel/text/XDDFTextParagraph.java Sun Aug 26 21:18:54 2018
@@ -0,0 +1,723 @@
+/* ====================================================================
+   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.xddf.usermodel.text;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+
+import org.apache.poi.util.Beta;
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.LocaleUtil;
+import org.apache.poi.util.Units;
+import org.apache.poi.xddf.usermodel.XDDFColor;
+import org.apache.xmlbeans.QNameSet;
+import org.apache.xmlbeans.XmlObject;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing;
+
+/**
+ * Represents a paragraph of text within the containing text body.
+ * The paragraph is the highest level text separation mechanism.
+ */
+@Beta
+public class XDDFTextParagraph {
+    private XDDFTextBody _parent;
+    private XDDFParagraphProperties _properties;
+    private final CTTextParagraph _p;
+    private final ArrayList<XDDFTextRun> _runs;
+
+    @Internal
+    protected XDDFTextParagraph(CTTextParagraph paragraph, XDDFTextBody parent) {
+        this._p = paragraph;
+        this._parent = parent;
+
+        final int count = paragraph.sizeOfBrArray()
+            + paragraph.sizeOfFldArray()
+            + paragraph.sizeOfRArray();
+        this._runs = new ArrayList<>(count);
+
+        for (XmlObject xo : _p.selectChildren(QNameSet.ALL)) {
+            if (xo instanceof CTTextLineBreak) {
+                _runs.add(new XDDFTextRun((CTTextLineBreak) xo, this));
+            } else if (xo instanceof CTTextField) {
+                _runs.add(new XDDFTextRun((CTTextField) xo, this));
+            } else if (xo instanceof CTRegularTextRun) {
+                _runs.add(new XDDFTextRun((CTRegularTextRun) xo, this));
+            }
+        }
+    }
+
+    public String getText() {
+        StringBuilder out = new StringBuilder();
+        for (XDDFTextRun r : _runs) {
+            out.append(r.getText());
+        }
+        return out.toString();
+    }
+
+    public XDDFTextBody getParentBody() {
+        return _parent;
+    }
+
+    public List<XDDFTextRun> getTextRuns(){
+        return _runs;
+    }
+
+    public Iterator<XDDFTextRun> iterator(){
+        return _runs.iterator();
+    }
+
+    /**
+     * Append a line break.
+     *
+     * @return text run representing this line break ('\n').
+     */
+    public XDDFTextRun appendLineBreak(){
+        CTTextLineBreak br = _p.addNewBr();
+        // by default, line break has the font properties of the last text run
+        for (int i = _runs.size() - 1; i <= 0; i--){
+            CTTextCharacterProperties prevProps = _runs.get(i).getProperties();
+            // let's find one that is not undefined
+            if (prevProps != null) {
+                br.setRPr((CTTextCharacterProperties) prevProps.copy());
+                break;
+            }
+        }
+        XDDFTextRun run = new XDDFTextRun(br, this);
+        _runs.add(run);
+        return run;
+    }
+
+    /**
+     * Append a new text field.
+     *
+     * @return the new text field.
+     */
+    public XDDFTextRun appendField(String id, String type, String text){
+        CTTextField f = _p.addNewFld();
+        f.setId(id);
+        f.setType(type);
+        f.setT(text);
+        CTTextCharacterProperties rPr = f.addNewRPr();
+        rPr.setLang(LocaleUtil.getUserLocale().toLanguageTag());
+        XDDFTextRun run = new XDDFTextRun(f, this);
+        _runs.add(run);
+        return run;
+    }
+
+    /**
+     * Append a new run of text.
+     *
+     * @return the new run of text.
+     */
+    public XDDFTextRun appendRegularRun(String text){
+        CTRegularTextRun r = _p.addNewR();
+        r.setT(text);
+        CTTextCharacterProperties rPr = r.addNewRPr();
+        rPr.setLang(LocaleUtil.getUserLocale().toLanguageTag());
+        XDDFTextRun run = new XDDFTextRun(r, this);
+        _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 TextAlignment getTextAlignment() {
+        return findDefinedParagraphProperty(props -> props.isSetAlgn(), props -> props.getAlgn())
+            .map(align -> TextAlignment.valueOf(align))
+            .orElse(null);
+    }
+
+    /**
+     * Specifies the alignment that is to be applied to the paragraph.
+     * Possible values for this include left, right, centered, justified and distributed,
+     *
+     * @param align text alignment
+     */
+    public void setTextAlignment(TextAlignment align) {
+        if (align != null || _p.isSetPPr()) {
+            getOrCreateProperties().setTextAlignment(align);
+        }
+    }
+
+    /**
+     * 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 FontAlignment getFontAlignment() {
+        return findDefinedParagraphProperty(props -> props.isSetFontAlgn(), props -> props.getFontAlgn())
+            .map(align -> FontAlignment.valueOf(align))
+            .orElse(null);
+    }
+
+    /**
+     * 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 alignment
+     */
+    public void setFontAlignment(FontAlignment align) {
+        if (align != null || _p.isSetPPr()) {
+            getOrCreateProperties().setFontAlignment(align);
+        }
+    }
+
+    /**
+     *
+     * @return the indentation, in points, applied to the first line of text in the paragraph.
+     */
+    public Double getIndentation() {
+        return findDefinedParagraphProperty(props -> props.isSetIndent(), props -> props.getIndent())
+            .map(emu -> Units.toPoints(emu))
+            .orElse(null);
+    }
+
+    /**
+     * Specifies the indentation size that will be applied to the first line of text in the paragraph.
+     *
+     * @param points the indentation in points.
+     * The value <code>null</code> unsets the indentation for this paragraph.
+     * <dl>
+     * <dt>Minimum inclusive =</dt><dd>-4032</dd>
+     * <dt>Maximum inclusive =</dt><dd>4032</dd>
+     * </dt>
+     */
+    public void setIndentation(Double points) {
+        if (points != null || _p.isSetPPr()) {
+            getOrCreateProperties().setIndentation(points);;
+        }
+    }
+
+    /**
+     *
+     * @return the left margin, in points, of the paragraph.
+     */
+    public Double getMarginLeft() {
+        return findDefinedParagraphProperty(props -> props.isSetMarL(), props -> props.getMarL())
+            .map(emu -> Units.toPoints(emu))
+            .orElse(null);
+    }
+
+    /**
+     * 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 points the margin in points.
+     * The value <code>null</code> unsets the left margin for this paragraph.
+     * <dl>
+     * <dt>Minimum inclusive =</dt><dd>0</dd>
+     * <dt>Maximum inclusive =</dt><dd>4032</dd>
+     * </dt>
+     */
+    public void setMarginLeft(Double points) {
+        if (points != null || _p.isSetPPr()) {
+            getOrCreateProperties().setMarginLeft(points);
+        }
+    }
+
+    /**
+     *
+     * @return the right margin, in points, of the paragraph.
+     */
+    public Double getMarginRight() {
+        return findDefinedParagraphProperty(props -> props.isSetMarR(), props -> props.getMarR())
+            .map(emu -> Units.toPoints(emu))
+            .orElse(null);
+    }
+
+    /**
+     * 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 RightMargin
+     * attributes are additive with respect to the text position.
+     *
+     * @param points the margin in points.
+     * The value <code>null</code> unsets the right margin for this paragraph.
+     * <dl>
+     * <dt>Minimum inclusive =</dt><dd>0</dd>
+     * <dt>Maximum inclusive =</dt><dd>4032</dd>
+     * </dt>
+     */
+    public void setMarginRight(Double points) {
+        if (points != null || _p.isSetPPr()) {
+            getOrCreateProperties().setMarginRight(points);
+        }
+    }
+
+    /**
+     *
+     * @return the default size for a tab character within this paragraph in points.
+     */
+    public Double getDefaultTabSize() {
+        return findDefinedParagraphProperty(props -> props.isSetDefTabSz(), props -> props.getDefTabSz())
+            .map(emu -> Units.toPoints(emu))
+            .orElse(null);
+    }
+
+    /**
+     * Specifies the default size for a tab character within this paragraph.
+     *
+     * @param points the default tab size in points.
+     * The value <code>null</code> unsets the default tab size for this paragraph.
+     */
+    public void setDefaultTabSize(Double points) {
+        if (points != null || _p.isSetPPr()) {
+            getOrCreateProperties().setDefaultTabSize(points);
+        }
+    }
+
+    /**
+     * Returns the vertical line spacing that is to be used within a paragraph.
+     * This may be specified in two different ways, percentage spacing or font points spacing:
+     * <p>
+     * If line spacing is a percentage of normal line height, result is instance of XDDFSpacingPercent.
+     * If line spacing is expressed in points, result is instance of XDDFSpacingPoints.
+     * </p>
+     *
+     * @return the vertical line spacing.
+     */
+    public XDDFSpacing getLineSpacing() {
+        return findDefinedParagraphProperty(props -> props.isSetLnSpc(), props -> props.getLnSpc())
+            .map(spacing -> extractSpacing(spacing))
+            .orElse(null);
+
+    }
+
+    /**
+     * 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 or font points spacing:
+     * <p>
+     * If spacing is instance of XDDFSpacingPercent, then line spacing is a percentage of normal line height.
+     * If spacing is instance of XDDFSpacingPoints, then line spacing is expressed in points.
+     * </p>
+     * Examples:
+     * <pre><code>
+     *      // spacing will be 120% of the size of the largest text on each line
+     *      paragraph.setLineSpacing(new XDDFSpacingPercent(120));
+     *
+     *      // spacing will be 200% of the size of the largest text on each line
+     *      paragraph.setLineSpacing(new XDDFSpacingPercent(200));
+     *
+     *      // spacing will be 48 points
+     *      paragraph.setLineSpacing(new XDDFSpacingPoints(48.0));
+     * </code></pre>
+     *
+     * @param linespacing the vertical line spacing
+     */
+    public void setLineSpacing(XDDFSpacing linespacing) {
+        if (linespacing != null || _p.isSetPPr()) {
+            getOrCreateProperties().setLineSpacing(linespacing);
+        }
+    }
+
+    /**
+     * The amount of vertical white space before the paragraph.
+     * This may be specified in two different ways, percentage spacing or font points spacing:
+     * <p>
+     * If spacing is a percentage of normal line height, result is instance of XDDFSpacingPercent.
+     * If spacing is expressed in points, result is instance of XDDFSpacingPoints.
+     * </p>
+     *
+     * @return the vertical white space before the paragraph.
+     */
+    public XDDFSpacing getSpaceBefore() {
+        return findDefinedParagraphProperty(props -> props.isSetSpcBef(), props -> props.getSpcBef())
+            .map(spacing -> extractSpacing(spacing))
+            .orElse(null);
+    }
+
+    /**
+     * Set the amount of vertical white space that will be present before the paragraph.
+     * This may be specified in two different ways, percentage spacing or font points spacing:
+     * <p>
+     * If spacing is instance of XDDFSpacingPercent, then spacing is a percentage of normal line height.
+     * If spacing is instance of XDDFSpacingPoints, then spacing is expressed 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(new XDDFSpacingPercent(200));
+     *
+     *      // The spacing will be a size of 48 points
+     *      paragraph.setSpaceBefore(new XDDFSpacingPoints(48.0));
+     * </code></pre>
+     *
+     * @param spaceBefore the vertical white space before the paragraph.
+     */
+    public void setSpaceBefore(XDDFSpacing spaceBefore) {
+        if (spaceBefore != null || _p.isSetPPr()) {
+            getOrCreateProperties().setSpaceBefore(spaceBefore);
+        }
+    }
+
+    /**
+     * The amount of vertical white space after the paragraph.
+     * This may be specified in two different ways, percentage spacing or font points spacing:
+     * <p>
+     * If spacing is a percentage of normal line height, result is instance of XDDFSpacingPercent.
+     * If spacing is expressed in points, result is instance of XDDFSpacingPoints.
+     * </p>
+     *
+     * @return the vertical white space after the paragraph.
+     */
+    public XDDFSpacing getSpaceAfter() {
+        return findDefinedParagraphProperty(props -> props.isSetSpcAft(), props -> props.getSpcAft())
+            .map(spacing -> extractSpacing(spacing))
+            .orElse(null);
+    }
+
+    /**
+     * Set the amount of vertical white space that will be present after the paragraph.
+     * This may be specified in two different ways, percentage spacing or font points spacing:
+     * <p>
+     * If spacing is instance of XDDFSpacingPercent, then spacing is a percentage of normal line height.
+     * If spacing is instance of XDDFSpacingPoints, then spacing is expressed 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(new XDDFSpacingPercent(200));
+     *
+     *      // The spacing will be a size of 48 points
+     *      paragraph.setSpaceAfter(new XDDFSpacingPoints(48.0));
+     * </code></pre>
+     *
+     * @param spaceAfter the vertical white space after the paragraph.
+     */
+    public void setSpaceAfter(XDDFSpacing spaceAfter) {
+        if (spaceAfter != null || _p.isSetPPr()) {
+            getOrCreateProperties().setSpaceAfter(spaceAfter);
+        }
+    }
+
+    /**
+     *
+     * @return the color of bullet characters within a given paragraph.
+     * A <code>null</code> value means to use the text font color.
+     */
+    public XDDFColor getBulletColor(){
+        return findDefinedParagraphProperty(
+                props -> props.isSetBuClr() || props.isSetBuClrTx(),
+                props -> new XDDFParagraphBulletProperties(props).getBulletColor()
+            ).orElse(null);
+    }
+
+    /**
+     * Set the color to be used on bullet characters within a given paragraph.
+     *
+     * @param color the bullet color
+     */
+    public void setBulletColor(XDDFColor color) {
+        if (color != null || _p.isSetPPr()) {
+            getOrCreateBulletProperties().setBulletColor(color);
+        }
+    }
+
+    /**
+     * Specifies the color to be used on bullet characters has to follow text color within a given paragraph.
+     */
+    public void setBulletColorFollowText() {
+        getOrCreateBulletProperties().setBulletColorFollowText();
+    }
+
+    /**
+     *
+     * @return the font of bullet characters within a given paragraph.
+     * A <code>null</code> value means to use the text font font.
+     */
+    public XDDFFont getBulletFont(){
+        return findDefinedParagraphProperty(
+                props -> props.isSetBuFont() || props.isSetBuFontTx(),
+                props -> new XDDFParagraphBulletProperties(props).getBulletFont()
+            ).orElse(null);
+    }
+
+    /**
+     * Set the font to be used on bullet characters within a given paragraph.
+     *
+     * @param font the bullet font
+     */
+    public void setBulletFont(XDDFFont font) {
+        if (font != null || _p.isSetPPr()) {
+            getOrCreateBulletProperties().setBulletFont(font);
+        }
+    }
+
+    /**
+     * Specifies the font to be used on bullet characters has to follow text font within a given paragraph.
+     */
+    public void setBulletFontFollowText() {
+        getOrCreateBulletProperties().setBulletFontFollowText();
+    }
+
+    /**
+     * Returns the bullet size that is to be used within a paragraph.
+     * This may be specified in three different ways, follows text size, percentage size and font points size:
+     * <p>
+     * If returned value is instance of XDDFBulletSizeFollowText, then bullet size is text size;
+     * If returned value is instance of XDDFBulletSizePercent, then bullet size is a percentage of the font size;
+     * If returned value is instance of XDDFBulletSizePoints, then bullet size is specified in points.
+     * </p>
+     *
+     * @return the bullet size
+     */
+    public XDDFBulletSize getBulletSize(){
+        return findDefinedParagraphProperty(
+                props -> props.isSetBuSzPct() || props.isSetBuSzPts() || props.isSetBuSzTx(),
+                props -> new XDDFParagraphBulletProperties(props).getBulletSize()
+            ).orElse(null);
+    }
+
+    /**
+     * Sets the bullet size that is to be used within a paragraph.
+     * This may be specified in three different ways, follows text size, percentage size and font points size:
+     * <p>
+     * If given value is instance of XDDFBulletSizeFollowText, then bullet size is text size;
+     * If given value is instance of XDDFBulletSizePercent, then bullet size is a percentage of the font size;
+     * If given value is instance of XDDFBulletSizePoints, then bullet size is specified in points.
+     * </p>
+     *
+     * @param size the bullet size specification
+     */
+    public void setBulletSize(XDDFBulletSize size) {
+        if (size != null || _p.isSetPPr()) {
+            getOrCreateBulletProperties().setBulletSize(size);
+        }
+    }
+
+    public XDDFBulletStyle getBulletStyle(){
+        return findDefinedParagraphProperty(
+                props -> props.isSetBuAutoNum() || props.isSetBuBlip() || props.isSetBuChar() || props.isSetBuNone(),
+                props -> new XDDFParagraphBulletProperties(props).getBulletStyle()
+            ).orElse(null);
+    }
+
+    public void setBulletStyle(XDDFBulletStyle style) {
+        if (style != null || _p.isSetPPr()) {
+            getOrCreateBulletProperties().setBulletStyle(style);
+        }
+    }
+
+    public boolean hasEastAsianLineBreak() {
+        return findDefinedParagraphProperty(props -> props.isSetEaLnBrk(), props -> props.getEaLnBrk())
+            .orElse(false);
+    }
+
+    public void setEastAsianLineBreak(Boolean value) {
+        if (value != null || _p.isSetPPr()) {
+            getOrCreateProperties().setEastAsianLineBreak(value);
+        }
+    }
+
+    public boolean hasLatinLineBreak() {
+        return findDefinedParagraphProperty(props -> props.isSetLatinLnBrk(), props -> props.getLatinLnBrk())
+            .orElse(false);
+    }
+
+    public void setLatinLineBreak(Boolean value) {
+        if (value != null || _p.isSetPPr()) {
+            getOrCreateProperties().setLatinLineBreak(value);
+        }
+    }
+
+    public boolean hasHangingPunctuation() {
+        return findDefinedParagraphProperty(props -> props.isSetHangingPunct(), props -> props.getHangingPunct())
+            .orElse(false);
+    }
+
+    public void setHangingPunctuation(Boolean value) {
+        if (value != null || _p.isSetPPr()) {
+            getOrCreateProperties().setHangingPunctuation(value);
+        }
+    }
+
+    public boolean isRightToLeft() {
+        return findDefinedParagraphProperty(props -> props.isSetRtl(), props -> props.getRtl())
+            .orElse(false);
+    }
+
+    public void setRightToLeft(Boolean value) {
+        if (value != null || _p.isSetPPr()) {
+            getOrCreateProperties().setRightToLeft(value);
+        }
+    }
+
+    public XDDFTabStop addTabStop() {
+        return getOrCreateProperties().addTabStop();
+    }
+
+    public XDDFTabStop insertTabStop(int index) {
+        return getOrCreateProperties().insertTabStop(index);
+    }
+
+    public void removeTabStop(int index) {
+        if (_p.isSetPPr()) {
+            getProperties().removeTabStop(index);
+        }
+    }
+
+    public XDDFTabStop getTabStop(int index) {
+        if (_p.isSetPPr()) {
+            return getProperties().getTabStop(index);
+        } else {
+            return null;
+        }
+    }
+
+    public List<XDDFTabStop> getTabStops() {
+        if (_p.isSetPPr()) {
+            return getProperties().getTabStops();
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    public int countTabStops() {
+        if (_p.isSetPPr()) {
+            return getProperties().countTabStops();
+        } else {
+            return 0;
+        }
+    }
+
+    public XDDFParagraphBulletProperties getOrCreateBulletProperties() {
+        return getOrCreateProperties().getBulletProperties();
+    }
+
+    public XDDFParagraphBulletProperties getBulletProperties() {
+        if (_p.isSetPPr()) {
+            return getProperties().getBulletProperties();
+        } else {
+            return null;
+        }
+    }
+
+    public XDDFRunProperties getDefaultRunProperties() {
+        if (_p.isSetPPr()) {
+            return getProperties().getDefaultRunProperties();
+        } else {
+            return null;
+        }
+    }
+
+    public void setDefaultRunProperties(XDDFRunProperties properties) {
+        if (properties != null || _p.isSetPPr()) {
+            getOrCreateProperties().setDefaultRunProperties(properties);
+        }
+    }
+
+    public XDDFRunProperties getAfterLastRunProperties() {
+        if (_p.isSetEndParaRPr()) {
+            return new XDDFRunProperties(_p.getEndParaRPr());
+        } else {
+            return null;
+        }
+    }
+
+    public void setAfterLastRunProperties(XDDFRunProperties properties) {
+        if (properties == null) {
+            _p.unsetEndParaRPr();
+        } else {
+            _p.setEndParaRPr(properties.getXmlObject());
+        }
+    }
+
+    private XDDFSpacing extractSpacing(CTTextSpacing spacing) {
+        if (spacing.isSetSpcPct()) {
+            double scale = 1 - _parent.getBodyProperties().getAutoFit().getLineSpaceReduction() / 100_000.0;
+            return new XDDFSpacingPercent(spacing, spacing.getSpcPct(), scale);
+        } else if (spacing.isSetSpcPts()) {
+            return new XDDFSpacingPoints(spacing, spacing.getSpcPts());
+        }
+        return null;
+    }
+
+    private XDDFParagraphProperties getProperties() {
+        if (_properties == null) {
+            _properties = new XDDFParagraphProperties(_p.getPPr());
+        }
+        return _properties;
+    }
+
+    private XDDFParagraphProperties getOrCreateProperties() {
+        if (!_p.isSetPPr()) {
+            _properties = new XDDFParagraphProperties(_p.addNewPPr());
+        }
+        return getProperties();
+    }
+
+    protected <R> Optional<R> findDefinedParagraphProperty(Function<CTTextParagraphProperties, Boolean> isSet,
+        Function<CTTextParagraphProperties, R> getter) {
+        if (_p.isSetPPr()) {
+            int level = _p.getPPr().isSetLvl() ? 1 + _p.getPPr().getLvl() : 0;
+            return findDefinedParagraphProperty(isSet, getter, level);
+        } else {
+            return _parent.findDefinedParagraphProperty(isSet, getter, 0);
+        }
+    }
+
+    private <R> Optional<R> findDefinedParagraphProperty(Function<CTTextParagraphProperties, Boolean> isSet,
+        Function<CTTextParagraphProperties, R> getter, int level) {
+        final CTTextParagraphProperties props = _p.getPPr();
+        if (props != null && isSet.apply(props)) {
+            return Optional.ofNullable(getter.apply(props));
+        } else {
+            return _parent.findDefinedParagraphProperty(isSet, getter, level);
+        }
+    }
+
+    protected <R> Optional<R> findDefinedRunProperty(Function<CTTextCharacterProperties, Boolean> isSet,
+        Function<CTTextCharacterProperties, R> getter) {
+        if (_p.isSetPPr()) {
+            int level = _p.getPPr().isSetLvl() ? 1 + _p.getPPr().getLvl() : 0;
+            return findDefinedRunProperty(isSet, getter, level);
+        } else {
+            return _parent.findDefinedRunProperty(isSet, getter, 0);
+        }
+    }
+
+    private <R> Optional<R> findDefinedRunProperty(Function<CTTextCharacterProperties, Boolean> isSet,
+        Function<CTTextCharacterProperties, R> getter, int level) {
+        final CTTextCharacterProperties props = _p.getPPr().isSetDefRPr() ? _p.getPPr().getDefRPr() : null;
+        if (props != null && isSet.apply(props)) {
+            return Optional.ofNullable(getter.apply(props));
+        } else {
+            return _parent.findDefinedRunProperty(isSet, getter, level);
+        }
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org