Author: kiwiwings
Date: Sun Feb 11 20:39:18 2018
New Revision: 1823893

URL: http://svn.apache.org/viewvc?rev=1823893&view=rev
Log:
#62096 - Add support for tabstops

Added:
    poi/trunk/src/java/org/apache/poi/sl/usermodel/TabStop.java   (with props)
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTabStop.java   
(with props)
    
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java
   (with props)
    
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java
      - copied, changed from r1823892, 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TabStopPropCollection.java
Removed:
    
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TabStopPropCollection.java
Modified:
    poi/site/src/documentation/content/xdocs/status.xml
    poi/trunk/src/java/org/apache/poi/sl/usermodel/MasterSheet.java
    poi/trunk/src/java/org/apache/poi/sl/usermodel/TextParagraph.java
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java
    
poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
    
poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java
    
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java
    
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java
    
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFMasterSheet.java
    
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java
    
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
    
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java
    
poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java
    
poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java
    
poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestHSLFSlideShow.java
    poi/trunk/src/testcases/org/apache/poi/sl/usermodel/BaseTestSlideShow.java

Modified: poi/site/src/documentation/content/xdocs/status.xml
URL: 
http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/status.xml?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/status.xml (original)
+++ poi/site/src/documentation/content/xdocs/status.xml Sun Feb 11 20:39:18 2018
@@ -68,6 +68,7 @@
         <summary-item>Provide new ooxml-schemas-1.4.jar</summary-item>
       </summary>
       <actions>
+        <action dev="PD" type="add" fixes-bug="62096" module="SL 
Common,HSLF,XSLF">Add support for tabstops</action>
         <action dev="PD" type="add" fixes-bug="55954" module="XWPF">Add 
functions to get, set, remove outer borders for tables</action>
         <action dev="PD" type="add" fixes-bug="github-72" module="XDDF">Define 
XDDF user model for shape properties to be shared between XSLF, XSSF and 
XWPF</action>
         <action dev="PD" type="add" fixes-bug="61543" 
breaks-compatibility="true" module="XSSF">Do not fail with "part already 
exists" when tables are created/removed</action>

Modified: poi/trunk/src/java/org/apache/poi/sl/usermodel/MasterSheet.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/usermodel/MasterSheet.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/usermodel/MasterSheet.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/usermodel/MasterSheet.java Sun Feb 11 
20:39:18 2018
@@ -21,5 +21,12 @@ public interface MasterSheet<
     S extends Shape<S,P>,
     P extends TextParagraph<S,P,? extends TextRun>
 > extends Sheet<S,P> {
-
+       /**
+        * Return the placeholder shape for the specified type
+        * 
+        * @return the shape or {@code null} if it is not defined in this 
mastersheet
+        * 
+        * @since POI 4.0.0
+        */
+    SimpleShape<S,P> getPlaceholder(Placeholder type);
 }

Added: poi/trunk/src/java/org/apache/poi/sl/usermodel/TabStop.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/usermodel/TabStop.java?rev=1823893&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/usermodel/TabStop.java (added)
+++ poi/trunk/src/java/org/apache/poi/sl/usermodel/TabStop.java Sun Feb 11 
20:39:18 2018
@@ -0,0 +1,66 @@
+/* ====================================================================
+   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.sl.usermodel;
+
+public interface TabStop {
+
+    enum TabStopType {
+        LEFT(0,1), CENTER(1,2), RIGHT(2,3), DECIMAL(3,4);
+        public final int nativeId;
+        public final int ooxmlId;
+        
+        TabStopType(int nativeId, int ooxmlId) {
+            this.nativeId = nativeId;
+            this.ooxmlId = ooxmlId;
+        }
+        public static TabStopType fromNativeId(final int nativeId)  {
+            for (TabStopType tst : values()) {
+                if (tst.nativeId == nativeId) {
+                    return tst;
+                }
+            }
+            return null;
+        }
+        public static TabStopType fromOoxmlId(final int ooxmlId)  {
+            for (TabStopType tst : values()) {
+                if (tst.ooxmlId == ooxmlId) {
+                    return tst;
+                }
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Gets the position in points relative to the left side of the paragraph.
+     * 
+     * @return position in points
+     */
+    double getPositionInPoints();
+
+    /**
+     * Sets the position in points relative to the left side of the paragraph
+     *
+     * @param position position in points
+     */
+    void setPositionInPoints(double position);
+
+    TabStopType getType();
+
+    void setType(TabStopType type);
+}

Propchange: poi/trunk/src/java/org/apache/poi/sl/usermodel/TabStop.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/src/java/org/apache/poi/sl/usermodel/TextParagraph.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/usermodel/TextParagraph.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/usermodel/TextParagraph.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/usermodel/TextParagraph.java Sun Feb 
11 20:39:18 2018
@@ -374,4 +374,34 @@ public interface TextParagraph<
      * @since POI 3.15-beta2
      */
     boolean isHeaderOrFooter();
+    
+    
+    /**
+     * Get the {@link TabStop TabStops} - the list can't be and it's entries 
shouldn't be modified.
+     * Opposed to other properties, this method is not cascading to the master 
sheet,
+     * if the property is not defined on the normal slide level, i.e. the 
tabstops on
+     * different levels aren't merged.
+     *
+     * @return the tabstop collection or {@code null} if no tabstops are 
defined
+     * 
+     * @since POI 4.0.0
+     */
+    List<? extends TabStop> getTabStops();
+
+    /**
+     * Set the {@link TabStop} collection
+     *
+     * @param tabStops the {@link TabStop} collection
+     * 
+     * @since POI 4.0.0
+     */
+    void addTabStops(double positionInPoints, TabStop.TabStopType tabStopType);
+
+    /**
+     * Removes the tabstops of this paragraphs.
+     * This doesn't affect inherited tabstops, e.g. inherited by the slide 
master
+     * 
+     * @since POI 4.0.0
+     */
+    void clearTabStops();
 }
\ No newline at end of file

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java 
(original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java Sun 
Feb 11 20:39:18 2018
@@ -472,6 +472,10 @@ implements XSLFShapeContainer, Sheet<XSL
         return null;
     }
 
+    public XSLFSimpleShape getPlaceholder(Placeholder ph) {
+        return getPlaceholderByType(ph.ooxmlId);
+    }
+
     XSLFSimpleShape getPlaceholder(CTPlaceholder ph) {
         XSLFSimpleShape shape = null;
         if(ph.isSetIdx()) {

Added: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTabStop.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTabStop.java?rev=1823893&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTabStop.java 
(added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTabStop.java Sun 
Feb 11 20:39:18 2018
@@ -0,0 +1,60 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.xslf.usermodel;
+
+import org.apache.poi.sl.usermodel.TabStop;
+import org.apache.poi.util.Units;
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStop;
+import org.openxmlformats.schemas.drawingml.x2006.main.STTextTabAlignType;
+
+public class XSLFTabStop implements TabStop {
+
+    final CTTextTabStop tabStop;
+    
+    XSLFTabStop(CTTextTabStop tabStop) {
+        this.tabStop = tabStop;
+    }
+    
+    /** position in EMUs */
+    public int getPosition() {
+        return tabStop.getPos();
+    }
+
+    /** position in EMUs */
+    public void setPosition(final int position) {
+        tabStop.setPos(position);
+    }
+
+    @Override
+    public double getPositionInPoints() {
+        return Units.toPoints(getPosition());
+    }
+
+    @Override
+    public void setPositionInPoints(final double points) {
+        setPosition(Units.toEMU(points));
+    }
+    
+    public TabStopType getType() {
+        return TabStopType.fromOoxmlId(tabStop.getAlgn().intValue());
+    }
+
+    public void setType(final TabStopType tabStopType) {
+        tabStop.setAlgn(STTextTabAlignType.Enum.forInt(tabStopType.ooxmlId) );
+    }
+}

Propchange: 
poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTabStop.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- 
poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java 
(original)
+++ 
poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java 
Sun Feb 11 20:39:18 2018
@@ -25,6 +25,7 @@ import org.apache.poi.sl.draw.DrawPaint;
 import org.apache.poi.sl.usermodel.AutoNumberingScheme;
 import org.apache.poi.sl.usermodel.PaintStyle;
 import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
+import org.apache.poi.sl.usermodel.TabStop.TabStopType;
 import org.apache.poi.sl.usermodel.TextParagraph;
 import org.apache.poi.util.Beta;
 import org.apache.poi.util.Internal;
@@ -32,26 +33,7 @@ import org.apache.poi.util.Units;
 import org.apache.poi.xslf.model.ParagraphPropertyFetcher;
 import org.apache.xmlbeans.XmlCursor;
 import org.apache.xmlbeans.XmlObject;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTColor;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextAutonumberBullet;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePercent;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePoint;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharBullet;
-import 
org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
-import 
org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStop;
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextTabStopList;
-import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType;
-import org.openxmlformats.schemas.drawingml.x2006.main.STTextAutonumberScheme;
-import org.openxmlformats.schemas.drawingml.x2006.main.STTextFontAlignType;
+import org.openxmlformats.schemas.drawingml.x2006.main.*;
 import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;
 import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;
 
@@ -800,25 +782,27 @@ public class XSLFTextParagraph implement
 
     private <T> boolean fetchParagraphProperty(ParagraphPropertyFetcher<T> 
visitor){
         boolean ok = false;
-        XSLFTextShape shape = getParentShape();
-        XSLFSheet sheet = shape.getSheet();
+        final XSLFTextShape shape = getParentShape();
+        final XSLFSheet sheet = shape.getSheet();
         
-        if(_p.isSetPPr()) ok = visitor.fetch(_p.getPPr());
-        if (ok) return true;
-
-        ok = shape.fetchShapeProperty(visitor);
-        if (ok) return true;
-                
-        
-        CTPlaceholder ph = shape.getCTPlaceholder();
-        if(ph == null){
-            // if it is a plain text box then take defaults from 
presentation.xml
-            @SuppressWarnings("resource")
-            XMLSlideShow ppt = sheet.getSlideShow();
-            CTTextParagraphProperties themeProps = 
ppt.getDefaultParagraphStyle(getIndentLevel());
-            if (themeProps != null) ok = visitor.fetch(themeProps);
+        if (!(sheet instanceof XSLFSlideMaster)) {
+            if(_p.isSetPPr()) ok = visitor.fetch(_p.getPPr());
+            if (ok) return true;
+    
+            ok = shape.fetchShapeProperty(visitor);
+            if (ok) return true;
+                    
+            
+            CTPlaceholder ph = shape.getCTPlaceholder();
+            if(ph == null){
+                // if it is a plain text box then take defaults from 
presentation.xml
+                @SuppressWarnings("resource")
+                XMLSlideShow ppt = sheet.getSlideShow();
+                CTTextParagraphProperties themeProps = 
ppt.getDefaultParagraphStyle(getIndentLevel());
+                if (themeProps != null) ok = visitor.fetch(themeProps);
+            }
+            if (ok) return true;
         }
-        if (ok) return true;
 
         // defaults for placeholders are defined in the slide master
         CTTextParagraphProperties defaultProps = getDefaultMasterStyle();
@@ -1011,7 +995,51 @@ public class XSLFTextParagraph implement
             }
         }
     }
-    
+
+    @Override
+    public List<XSLFTabStop> getTabStops() {
+        ParagraphPropertyFetcher<List<XSLFTabStop>> fetcher = new 
ParagraphPropertyFetcher<List<XSLFTabStop>>(getIndentLevel()){
+            public boolean fetch(CTTextParagraphProperties props) {
+                if (props.isSetTabLst()) {
+                    final List<XSLFTabStop> list = new ArrayList<>();
+                    for (final CTTextTabStop ta : 
props.getTabLst().getTabArray()) {
+                        list.add(new XSLFTabStop(ta));
+                    }
+                    setValue(list);
+                    return true;
+                }
+                return false;
+            }
+        };
+        fetchParagraphProperty(fetcher);
+        return fetcher.getValue();        
+    }
+
+    @Override
+    public void addTabStops(double positionInPoints, TabStopType tabStopType) {
+        final XSLFSheet sheet = getParentShape().getSheet();
+        final CTTextParagraphProperties tpp;
+        if (sheet instanceof XSLFSlideMaster) {
+            tpp = getDefaultMasterStyle();
+        } else {
+            final CTTextParagraph xo = getXmlObject();
+            tpp = (xo.isSetPPr()) ? xo.getPPr() : xo.addNewPPr();
+        }
+        final CTTextTabStopList stl = (tpp.isSetTabLst()) ? tpp.getTabLst() : 
tpp.addNewTabLst();
+        XSLFTabStop tab = new XSLFTabStop(stl.addNewTab());
+        tab.setPositionInPoints(positionInPoints);
+        tab.setType(tabStopType);
+    }
+
+    @Override
+    public void clearTabStops() {
+        final XSLFSheet sheet = getParentShape().getSheet();
+        CTTextParagraphProperties tpp = (sheet instanceof XSLFSlideMaster) ? 
getDefaultMasterStyle() : getXmlObject().getPPr();
+        if (tpp != null && tpp.isSetTabLst()) {
+            tpp.unsetTabLst();
+        }
+    }
+
     /**
      * Helper method for appending text and keeping paragraph and character 
properties.
      * The character properties are moved to the end paragraph marker

Modified: 
poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- 
poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java
 (original)
+++ 
poi/trunk/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java
 Sun Feb 11 20:39:18 2018
@@ -20,12 +20,16 @@ import static org.junit.Assert.assertEqu
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
 import org.apache.poi.openxml4j.opc.OPCPackage;
 import org.apache.poi.openxml4j.opc.PackagePart;
 import org.apache.poi.sl.usermodel.BaseTestSlideShow;
+import org.apache.poi.sl.usermodel.SlideShow;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -179,4 +183,25 @@ public class TestXMLSlideShow extends Ba
       xmlComments.close();
       xml.close();
    }
+   
+   public SlideShow<?, ?> reopen(SlideShow<?, ?> show) {
+       return reopen((XMLSlideShow)show);
+   }
+
+   public static XMLSlideShow reopen(XMLSlideShow show) {
+       try {
+           BufAccessBAOS bos = new BufAccessBAOS();
+           show.write(bos);
+           return new XMLSlideShow(new ByteArrayInputStream(bos.getBuf()));
+       } catch (IOException e) {
+           fail(e.getMessage());
+           return null;
+       }
+   }
+
+   private static class BufAccessBAOS extends ByteArrayOutputStream {
+       public byte[] getBuf() {
+           return buf;
+       }
+   }
 }

Added: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java?rev=1823893&view=auto
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java
 (added)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java
 Sun Feb 11 20:39:18 2018
@@ -0,0 +1,116 @@
+/* ====================================================================
+   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.hslf.model.textproperties;
+
+import org.apache.poi.sl.usermodel.TabStop;
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.Units;
+
+@Internal
+public class HSLFTabStop implements TabStop, Cloneable {
+    /**
+     * A signed integer that specifies an offset, in master units, of the tab 
stop.
+     * 
+     * If the TextPFException record that contains this TabStop structure also 
contains a
+     * leftMargin, then the value of position is relative to the left margin 
of the paragraph;
+     * otherwise, the value is relative to the left side of the paragraph.
+     * 
+     * If a TextRuler record contains this TabStop structure, the value is 
relative to the
+     * left side of the text ruler.
+     */
+    private int position;
+
+    /**
+     * A enumeration that specifies how text aligns at the tab stop.
+     */
+    private TabStopType type;
+
+    public HSLFTabStop(int position, TabStopType type) {
+        this.position = position;
+        this.type = type;
+    }
+
+    public int getPosition() {
+        return position;
+    }
+    
+    public void setPosition(final int position) {
+        this.position = position;
+    }
+    
+    @Override
+    public double getPositionInPoints() {
+        return Units.masterToPoints(getPosition());
+    }
+
+    @Override
+    public void setPositionInPoints(final double points) {
+        setPosition(Units.pointsToMaster(points));
+    }
+
+    @Override
+    public TabStopType getType() {
+        return type;
+    }
+
+    @Override
+    public void setType(TabStopType type) {
+        this.type = type;
+    }
+
+    @Override
+    public HSLFTabStop clone() {
+        try {
+            return (HSLFTabStop)super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+    
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + position;
+        result = prime * result + ((type == null) ? 0 : type.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof HSLFTabStop)) {
+            return false;
+        }
+        HSLFTabStop other = (HSLFTabStop) obj;
+        if (position != other.position) {
+            return false;
+        }
+        if (type != other.type) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return type + " @ " + position;
+    }
+}

Propchange: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStop.java
------------------------------------------------------------------------------
    svn:eol-style = native

Copied: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java
 (from r1823892, 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TabStopPropCollection.java)
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java?p2=poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java&p1=poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TabStopPropCollection.java&r1=1823892&r2=1823893&rev=1823893&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TabStopPropCollection.java
 (original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/HSLFTabStopPropCollection.java
 Sun Feb 11 20:39:18 2018
@@ -17,72 +17,36 @@
 
 package org.apache.poi.hslf.model.textproperties;
 
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.sl.usermodel.TabStop.TabStopType;
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
 import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianInput;
+import org.apache.poi.util.LittleEndianOutput;
+import org.apache.poi.util.LittleEndianOutputStream;
 
 /**
  * Container for tabstop lists
  */
-public class TabStopPropCollection extends TextProp {
-    public enum TabStopType {
-        LEFT(0), CENTER(1), RIGHT(2), DECIMAL(3);
-        private final int val;
-        TabStopType(int val) {
-            this.val = val;
-        }
-        public static TabStopType fromRecordVal(int val)  {
-            for (TabStopType tst : values()) {
-                if (tst.val == val) return tst;
-            }
-            return LEFT;
-        }
-    }
-
-    public static class TabStop {
-        /**
-         * If the TextPFException record that contains this TabStop structure 
also contains a
-         * leftMargin, then the value of position is relative to the left 
margin of the paragraph;
-         * otherwise, the value is relative to the left side of the paragraph.
-         * 
-         * If a TextRuler record contains this TabStop structure, the value is 
relative to the
-         * left side of the text ruler.
-         */
-        private int position;
-
-        /**
-         * A enumeration that specifies how text aligns at the tab stop.
-         */
-        private TabStopType type;
-
-        public TabStop(int position, TabStopType type) {
-            this.position = position;
-            this.type = type;
-        }
-        
-        public int getPosition() {
-            return position;
-        }
-
-        public void setPosition(int position) {
-            this.position = position;
-        }
-
-        public TabStopType getType() {
-            return type;
-        }
-
-        public void setType(TabStopType type) {
-            this.type = type;
-        }
-    }
+@Internal
+public class HSLFTabStopPropCollection extends TextProp {
+    public static final String NAME = "tabStops";
+    
+    private final List<HSLFTabStop> tabStops = new ArrayList<>();
     
-    private List<TabStop> tabStops = new ArrayList<>();
+    public HSLFTabStopPropCollection() {
+        super(0, 0x100000, NAME);
+    }
     
-    public TabStopPropCollection() {
-        super(0, 0x100000, "tabStops");
+    public HSLFTabStopPropCollection(final HSLFTabStopPropCollection copy) {
+        super(0, copy.getMask(), copy.getName());
+        for (HSLFTabStop ts : copy.tabStops) {
+            tabStops.add(ts.clone());
+        }
     }
     
     /**
@@ -92,32 +56,103 @@ public class TabStopPropCollection exten
      * @param offset the offset within the data
      */
     public void parseProperty(byte data[], int offset) {
-        int count = LittleEndian.getUShort(data, offset);
-        int off = offset + LittleEndianConsts.SHORT_SIZE;
+        tabStops.addAll(readTabStops(new 
LittleEndianByteArrayInputStream(data, offset)));
+    }
+    
+    public static List<HSLFTabStop> readTabStops(final LittleEndianInput lei) {
+        final int count = lei.readUShort();
+        final List<HSLFTabStop> tabs = new ArrayList<>(count);
         for (int i=0; i<count; i++) {
-            int position = LittleEndian.getShort(data, off);
-            off += LittleEndianConsts.SHORT_SIZE;
-            int recVal = LittleEndian.getShort(data, off);
-            TabStopType type = TabStopType.fromRecordVal(recVal);
-            off += LittleEndianConsts.SHORT_SIZE;
-            tabStops.add(new TabStop(position, type));
-            
+            final int position = lei.readShort();
+            final TabStopType type = TabStopType.fromNativeId(lei.readShort());
+            tabs.add(new HSLFTabStop(position, type));
         }
+        return tabs;
+    }
+    
+
+    public void writeProperty(OutputStream out) {
+        writeTabStops(new LittleEndianOutputStream(out), tabStops);
+    }
+
+    public static void writeTabStops(final LittleEndianOutput leo, 
List<HSLFTabStop> tabStops) {
+        final int count = tabStops.size();
+        leo.writeShort(count);
+        for (HSLFTabStop ts : tabStops) {
+            leo.writeShort(ts.getPosition());
+            leo.writeShort(ts.getType().nativeId);
+        }
+        
     }
     
     @Override
+    public int getValue() { return tabStops.size(); }
+
+    
+    @Override
     public int getSize() {
         return LittleEndianConsts.SHORT_SIZE + 
tabStops.size()*LittleEndianConsts.INT_SIZE;
     }
     
+    public List<HSLFTabStop> getTabStops() {
+        return tabStops;
+    }
+
+    public void clearTabs() {
+        tabStops.clear();
+    }
+    
+    public void addTabStop(HSLFTabStop ts) {
+        tabStops.add(ts);
+    }
+    
+    @Override
+    public HSLFTabStopPropCollection clone() {
+        return new HSLFTabStopPropCollection(this);
+    }
+
     @Override
-    public TabStopPropCollection clone() {
-        TabStopPropCollection other = (TabStopPropCollection)super.clone();
-        other.tabStops = new ArrayList<>();
-        for (TabStop ts : tabStops) {
-            TabStop tso = new TabStop(ts.getPosition(), ts.getType());
-            other.tabStops.add(tso);
+    public int hashCode() {
+        final int prime = 31;
+        int result = super.hashCode();
+        result = prime * result
+                + ((tabStops == null) ? 0 : tabStops.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof HSLFTabStopPropCollection)) {
+            return false;
+        }
+        HSLFTabStopPropCollection other = (HSLFTabStopPropCollection) obj;
+        if (!super.equals(other)) {
+            return false;
+        }
+
+        return tabStops.equals(other.tabStops);
+    }
+    
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder(super.toString());
+        sb.append(" [ ");
+        boolean isFirst = true;
+        for (HSLFTabStop tabStop : tabStops) {
+            if (!isFirst) {
+                sb.append(", ");
+            }
+            sb.append(tabStop.getType());
+            sb.append(" @ ");
+            sb.append(tabStop.getPosition());
+            isFirst = false;
         }
-        return other;
+        sb.append(" ]");
+        
+        return sb.toString();
     }
+    
 }

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java
 (original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextProp.java
 Sun Feb 11 20:39:18 2018
@@ -96,7 +96,7 @@ public class TextProp implements Cloneab
                try {
                        return (TextProp)super.clone();
                } catch(CloneNotSupportedException e) {
-                       throw new InternalError(e.getMessage());
+                       throw new IllegalStateException(e);
                }
        }
        
@@ -145,11 +145,11 @@ public class TextProp implements Cloneab
     @Override
     public String toString() {
         int len;
-        switch (sizeOfDataBlock) {
+        switch (getSize()) {
         case 1: len = 4; break;
         case 2: len = 6; break;
         default: len = 10; break;
         }
-        return String.format(Locale.ROOT, "%s = %d (%0#"+len+"X mask / %d 
bytes)", propName, dataValue, maskInHeader, sizeOfDataBlock);
+        return String.format(Locale.ROOT, "%s = %d (%0#"+len+"X mask / %d 
bytes)", getName(), getValue(), getMask(), getSize());
     }
 }
\ No newline at end of file

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java
 (original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java
 Sun Feb 11 20:39:18 2018
@@ -58,7 +58,7 @@ public class TextPropCollection {
         // 0x200 - Undefined and MUST be ignored
         new TextProp(2, 0x400, "bullet.offset"), // indent
         new TextProp(2, 0x8000, "defaultTabSize"),
-        new TabStopPropCollection(), // tabstops size is variable!
+        new HSLFTabStopPropCollection(), // tabstops size is variable!
         new FontAlignmentProp(),
         new WrapFlagsTextProp(),
         new TextProp(2, 0x200000, "textDirection"),
@@ -130,12 +130,14 @@ public class TextPropCollection {
     }
        
        /** Fetch the TextProp with this name, or null if it isn't present */
-       public final TextProp findByName(String textPropName) {
-               return textProps.get(textPropName);
+       @SuppressWarnings("unchecked")
+    public final <T extends TextProp> T findByName(String textPropName) {
+               return (T)textProps.get(textPropName);
        }
 
-       public final TextProp removeByName(String name) {
-           return textProps.remove(name);
+    @SuppressWarnings("unchecked")
+       public final <T extends TextProp> T removeByName(String name) {
+           return (T)textProps.remove(name);
        }
        
        public final TextPropType getTextPropType() {
@@ -153,10 +155,11 @@ public class TextPropCollection {
         * @param name the property name
         * @return if found, the property template to copy from
         */
-       private TextProp validatePropName(String name) {
+    @SuppressWarnings("unchecked")
+       private <T extends TextProp> T validatePropName(final String name) {
        for (TextProp tp : getPotentialProperties()) {
             if (tp.getName().equals(name)) {
-                return tp;
+                return (T)tp;
             }
         }
        String errStr = 
@@ -166,13 +169,14 @@ public class TextPropCollection {
        }
        
     /** Add the TextProp with this name to the list */
-    public final TextProp addWithName(String name) {
+    @SuppressWarnings("unchecked")
+    public final <T extends TextProp> T addWithName(final String name) {
         // Find the base TextProp to base on
-        TextProp existing = findByName(name);
+        T existing = findByName(name);
         if (existing != null) return existing;
         
         // Add a copy of this property
-        TextProp textProp = validatePropName(name).clone();
+        T textProp = (T)validatePropName(name).clone();
         textProps.put(name,textProp);
         return textProp;
     }
@@ -218,11 +222,13 @@ public class TextPropCollection {
                                // Bingo, data contains this property
                                TextProp prop = tp.clone();
                                int val = 0;
-                               if (prop.getSize() == 2) {
+                               if (prop instanceof HSLFTabStopPropCollection) {
+                    ((HSLFTabStopPropCollection)prop).parseProperty(data, 
dataOffset+bytesPassed);
+                } else if (prop.getSize() == 2) {
                                        val = 
LittleEndian.getShort(data,dataOffset+bytesPassed);
                                } else if(prop.getSize() == 4) {
                                        val = 
LittleEndian.getInt(data,dataOffset+bytesPassed);
-                               } else if (prop.getSize() == 0 && !(prop 
instanceof TabStopPropCollection)) {
+                               } else if (prop.getSize() == 0) {
                     //remember "special" bits.
                     maskSpecial |= tp.getMask();
                     continue;
@@ -230,9 +236,7 @@ public class TextPropCollection {
                                
                                if (prop instanceof BitMaskTextProp) {
                                    
((BitMaskTextProp)prop).setValueWithMask(val, containsField);
-                               } else if (prop instanceof 
TabStopPropCollection) {
-                                   
((TabStopPropCollection)prop).parseProperty(data, dataOffset+bytesPassed);
-                               } else {
+                               } else if (!(prop instanceof 
HSLFTabStopPropCollection)) {
                                    prop.setValue(val);
                                }
                                bytesPassed += prop.getSize();
@@ -311,6 +315,8 @@ public class TextPropCollection {
                 StyleTextPropAtom.writeLittleEndian((short)val,o);
             } else if (textProp.getSize() == 4) {
                 StyleTextPropAtom.writeLittleEndian(val,o);
+            } else if (textProp instanceof HSLFTabStopPropCollection) {
+                ((HSLFTabStopPropCollection)textProp).writeProperty(o);
             }
                }
        }
@@ -359,8 +365,9 @@ public class TextPropCollection {
             out.append("  indent level: "+getIndentLevel()+"\n");
         }
         for(TextProp p : getTextPropList()) {
-            out.append("    " + p.getName() + " = " + p.getValue() );
-            out.append(" (0x" + HexDump.toHex(p.getValue()) + ")\n");
+            out.append("    ");
+            out.append(p.toString());
+            out.append("\n");
             if (p instanceof BitMaskTextProp) {
                 BitMaskTextProp bm = (BitMaskTextProp)p;
                 int i = 0;

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java 
(original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java 
Sun Feb 11 20:39:18 2018
@@ -17,11 +17,21 @@
 
 package org.apache.poi.hslf.record;
 
+import static org.apache.poi.util.BitFieldFactory.getInstance;
+
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-
-import org.apache.poi.util.IOUtils;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.poi.hslf.model.textproperties.HSLFTabStop;
+import org.apache.poi.hslf.model.textproperties.HSLFTabStopPropCollection;
+import org.apache.poi.util.BitField;
 import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
+import org.apache.poi.util.LittleEndianOutputStream;
 import org.apache.poi.util.POILogger;
 
 /**
@@ -31,33 +41,38 @@ public final class TextRulerAtom extends
 
     //arbitrarily selected; may need to increase
     private static final int MAX_RECORD_LENGTH = 100_000;
+    
+    private static final BitField DEFAULT_TAB_SIZE = getInstance(0x0001);
+    private static final BitField C_LEVELS = getInstance(0x0002);
+    private static final BitField TAB_STOPS = getInstance(0x0004);
+    private static final BitField[] LEFT_MARGIN = {
+        getInstance(0x0008), getInstance(0x0010), getInstance(0x0020),
+        getInstance(0x0040), getInstance(0x0080),
+    };
+    private static final BitField[] INDENT = {
+        getInstance(0x0100), getInstance(0x0200), getInstance(0x0400),
+        getInstance(0x0800), getInstance(0x1000),
+    };
 
     /**
      * Record header.
      */
-    private byte[] _header;
-
-    /**
-     * Record data.
-     */
-    private byte[] _data;
+    private final byte[] _header = new byte[8];
 
     //ruler internals
-    private int defaultTabSize;
-    private int numLevels;
-    private int[] tabStops;
-    private int[] bulletOffsets = new int[5];
-    private int[] textOffsets = new int[5];
+    private Integer defaultTabSize;
+    private Integer numLevels;
+    private final List<HSLFTabStop> tabStops = new ArrayList<>();
+    //bullet.offset
+    private final Integer[] leftMargin = new Integer[5];
+    //text.offset
+    private final Integer[] indent = new Integer[5];
 
     /**
      * Constructs a new empty ruler atom.
      */
     public TextRulerAtom() {
-        _header = new byte[8];
-        _data = new byte[0];
-
         LittleEndian.putShort(_header, 2, (short)getRecordType());
-        LittleEndian.putInt(_header, 4, _data.length);
     }
 
     /**
@@ -68,18 +83,17 @@ public final class TextRulerAtom extends
      * @param start the start offset into the byte array.
      * @param len the length of the slice in the byte array.
      */
-    protected TextRulerAtom(byte[] source, int start, int len) {
-        // Get the header.
-        _header = new byte[8];
-        System.arraycopy(source,start,_header,0,8);
-
-        // Get the record data.
-        _data = IOUtils.safelyAllocate(len-8, MAX_RECORD_LENGTH);
-        System.arraycopy(source,start+8,_data,0,len-8);
+    protected TextRulerAtom(final byte[] source, final int start, final int 
len) {
+        final LittleEndianByteArrayInputStream leis = new 
LittleEndianByteArrayInputStream(source, start, Math.min(len, 
MAX_RECORD_LENGTH));
+        
 
         try {
-            read();
-        } catch (Exception e){
+            // Get the header.
+            leis.read(_header);
+
+            // Get the record data.
+            read(leis);
+        } catch (IOException e){
             logger.log(POILogger.ERROR, "Failed to parse TextRulerAtom: " + 
e.getMessage());
         }
     }
@@ -89,6 +103,7 @@ public final class TextRulerAtom extends
      *
      * @return the record type.
      */
+    @Override
     public long getRecordType() {
         return RecordTypes.TextRulerAtom.typeID;
     }
@@ -100,109 +115,110 @@ public final class TextRulerAtom extends
      * @param out the output stream to write to.
      * @throws java.io.IOException if an error occurs.
      */
-    public void writeOut(OutputStream out) throws IOException {
+    @Override
+    public void writeOut(final OutputStream out) throws IOException {
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream(200);
+        final LittleEndianOutputStream lbos = new 
LittleEndianOutputStream(bos);
+        int mask = 0;
+        mask |= writeIf(lbos, numLevels, C_LEVELS);
+        mask |= writeIf(lbos, defaultTabSize, DEFAULT_TAB_SIZE);
+        mask |= writeIf(lbos, tabStops, TAB_STOPS);
+        for (int i=0; i<5; i++) {
+            mask |= writeIf(lbos, leftMargin[i], LEFT_MARGIN[i]);
+            mask |= writeIf(lbos, indent[i], INDENT[i]);
+        }
+        LittleEndian.putInt(_header, 4, bos.size()+4);
         out.write(_header);
-        out.write(_data);
+        LittleEndian.putUShort(mask, out);
+        LittleEndian.putUShort(0, out);
+        bos.writeTo(out);
     }
 
+    private static int writeIf(final LittleEndianOutputStream lbos, Integer 
value, BitField bit) {
+        boolean isSet = false;
+        if (value != null) {
+            lbos.writeShort(value);
+            isSet = true;
+        }
+        return bit.setBoolean(0, isSet);
+    }
+    
+    private static int writeIf(final LittleEndianOutputStream lbos, 
List<HSLFTabStop> value, BitField bit) {
+        boolean isSet = false;
+        if (value != null && !value.isEmpty()) {
+            HSLFTabStopPropCollection.writeTabStops(lbos, value);
+            isSet = true;
+        }
+        return bit.setBoolean(0, isSet);
+    }
+    
     /**
      * Read the record bytes and initialize the internal variables
      */
-    private void read(){
-        int pos = 0;
-        short mask = LittleEndian.getShort(_data);  pos += 4;
-        short val;
-        int[] bits = {1, 0, 2, 3, 8, 4, 9, 5, 10, 6, 11, 7, 12};
-        for (int i = 0; i < bits.length; i++) {
-            if((mask & 1 << bits[i]) != 0){
-                switch (bits[i]){
-                    case 0:
-                        //defaultTabSize
-                        defaultTabSize = LittleEndian.getShort(_data, pos); 
pos += 2;
-                        break;
-                    case 1:
-                        //numLevels
-                        numLevels = LittleEndian.getShort(_data, pos); pos += 
2;
-                        break;
-                    case 2:
-                        //tabStops
-                        val = LittleEndian.getShort(_data, pos); pos += 2;
-                        tabStops = new int[val*2];
-                        for (int j = 0; j < tabStops.length; j++) {
-                            tabStops[j] = LittleEndian.getUShort(_data, pos); 
pos += 2;
-                        }
-                        break;
-                    case 3:
-                    case 4:
-                    case 5:
-                    case 6:
-                    case 7:
-                        //bullet.offset
-                        val = LittleEndian.getShort(_data, pos); pos += 2;
-                        bulletOffsets[bits[i]-3] = val;
-                        break;
-                    case 8:
-                    case 9:
-                    case 10:
-                    case 11:
-                    case 12:
-                        //text.offset
-                        val = LittleEndian.getShort(_data, pos); pos += 2;
-                        textOffsets[bits[i]-8] = val;
-                        break;
-                    default:
-                        break;
-                }
-            }
+    private void read(final LittleEndianByteArrayInputStream leis) {
+        final int mask = leis.readInt();
+        numLevels = readIf(leis, mask, C_LEVELS);
+        defaultTabSize = readIf(leis, mask, DEFAULT_TAB_SIZE);
+        if (TAB_STOPS.isSet(mask)) {
+            tabStops.addAll(HSLFTabStopPropCollection.readTabStops(leis));
+        }
+        for (int i=0; i<5; i++) {
+            leftMargin[i] = readIf(leis, mask, LEFT_MARGIN[i]);
+            indent[i] = readIf(leis, mask, INDENT[i]);
         }
     }
 
+    private static Integer readIf(final LittleEndianByteArrayInputStream leis, 
final int mask, final BitField bit) {
+        return (bit.isSet(mask)) ? (int)leis.readShort() : null;
+    }    
+    
     /**
      * Default distance between tab stops, in master coordinates (576 dpi).
      */
     public int getDefaultTabSize(){
-        return defaultTabSize;
+        return defaultTabSize == null ? 0 : defaultTabSize;
     }
 
     /**
      * Number of indent levels (maximum 5).
      */
     public int getNumberOfLevels(){
-        return numLevels;
+        return numLevels == null ? 0 : numLevels;
     }
 
     /**
      * Default distance between tab stops, in master coordinates (576 dpi).
      */
-    public int[] getTabStops(){
+    public List<HSLFTabStop> getTabStops(){
         return tabStops;
     }
 
     /**
      * Paragraph's distance from shape's left margin, in master coordinates 
(576 dpi).
      */
-    public int[] getTextOffsets(){
-        return textOffsets;
+    public Integer[] getTextOffsets(){
+        return indent;
     }
 
     /**
      * First line of paragraph's distance from shape's left margin, in master 
coordinates (576 dpi).
      */
-    public int[] getBulletOffsets(){
-        return bulletOffsets;
+    public Integer[] getBulletOffsets(){
+        return leftMargin;
     }
 
     public static TextRulerAtom getParagraphInstance(){
-        byte[] data = new byte[] {
-            0x00, 0x00, (byte)0xA6, 0x0F, 0x0A, 0x00, 0x00, 0x00,
-            0x10, 0x03, 0x00, 0x00, (byte)0xF9, 0x00, 0x41, 0x01, 0x41, 0x01
-        };
-        return new TextRulerAtom(data, 0, data.length);
+        final TextRulerAtom tra = new TextRulerAtom();
+        tra.indent[0] = 249;
+        tra.indent[1] = tra.leftMargin[1] = 321;
+        return tra;
     }
 
-    public void setParagraphIndent(short tetxOffset, short bulletOffset){
-        LittleEndian.putShort(_data, 4, tetxOffset);
-        LittleEndian.putShort(_data, 6, bulletOffset);
-        LittleEndian.putShort(_data, 8, bulletOffset);
+    public void setParagraphIndent(short leftMargin, short indent) {
+        Arrays.fill(this.leftMargin, null);
+        Arrays.fill(this.indent, null);
+        this.leftMargin[0] = (int)leftMargin;
+        this.indent[0] = (int)indent;
+        this.indent[1] = (int)indent;
     }
 }

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFMasterSheet.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFMasterSheet.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFMasterSheet.java 
(original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFMasterSheet.java 
Sun Feb 11 20:39:18 2018
@@ -17,16 +17,13 @@
 
 package org.apache.poi.hslf.usermodel;
 
+import org.apache.poi.hslf.model.textproperties.TextPropCollection;
 import org.apache.poi.hslf.record.SheetContainer;
-import org.apache.poi.hslf.model.textproperties.TextProp;
+import org.apache.poi.hslf.record.TextHeaderAtom;
 import org.apache.poi.sl.usermodel.MasterSheet;
 
 /**
  * The superclass of all master sheets - Slide masters, Notes masters, etc.
- *
- * For now it's empty. When we understand more about masters in ppt we will 
add the common functionality here.
- *
- * @author Yegor Kozlov
  */
 public abstract class HSLFMasterSheet extends HSLFSheet implements 
MasterSheet<HSLFShape,HSLFTextParagraph> {
     public HSLFMasterSheet(SheetContainer container, int sheetNo){
@@ -34,10 +31,19 @@ public abstract class HSLFMasterSheet ex
     }
 
     /**
-     * Pickup a style attribute from the master.
-     * This is the "workhorse" which returns the default style attrubutes.
+     * Find the master collection for the given txtype/level/name.
+     * This is the "workhorse" which returns the default style attributes.
+     * If {@code name = "*"} return the current collection, otherwise if the 
name is not found
+     * in the current selection of txtype/level/name, first try lower levels 
then try parent types,
+     * if it wasn't found there return {@code null}.
+     * 
+     * @param txtype the {@link TextHeaderAtom} type
+     * @param level the indent level of the paragraph, if the level is not 
defined for the found
+     *      collection, the highest existing level will be used
+     * @param name the property name, 
+     * @param isCharacter if {@code true} use character styles, otherwise use 
paragraph styles
      */
-    public abstract TextProp getStyleAttribute(int txtype, int level, String 
name, boolean isCharacter) ;
+    public abstract TextPropCollection getPropCollection(int txtype, int 
level, String name, boolean isCharacter);
 
 
     /**

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java 
(original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java 
Sun Feb 11 20:39:18 2018
@@ -74,42 +74,50 @@ public final class HSLFSlideMaster exten
     }
 
     /**
-     * Pickup a style attribute from the master.
+     * Find the master collection for the given txtype/level/name.
      * This is the "workhorse" which returns the default style attributes.
+     * If {@code name = "*"} return the current collection, otherwise if the 
name is not found
+     * in the current selection of txtype/level/name, first try lower levels 
then try parent types,
+     * if it wasn't found there return {@code null}.
+     * 
+     * @param txtype the {@link TextHeaderAtom} type
+     * @param level the indent level of the paragraph, if the level is not 
defined for the found
+     *      collection, the highest existing level will be used
+     * @param name the property name, 
+     * @param isCharacter if {@code true} use character styles, otherwise use 
paragraph styles
      */
     @Override
-    public TextProp getStyleAttribute(int txtype, int level, String name, 
boolean isCharacter) {
-        if (_txmaster.length <= txtype) {
-            return null;
-        }
-        TxMasterStyleAtom t = _txmaster[txtype];
-        List<TextPropCollection> styles = isCharacter ? t.getCharacterStyles() 
: t.getParagraphStyles();
-        
-        TextProp prop = null;
-        for (int i = Math.min(level, styles.size()-1); prop == null && i >= 0; 
i--) {
-            prop = styles.get(i).findByName(name);
+    public TextPropCollection getPropCollection(final int txtype, final int 
level, final String name, final boolean isCharacter) {
+        if (txtype < _txmaster.length) {
+            final TxMasterStyleAtom t = _txmaster[txtype];
+            final List<TextPropCollection> styles = isCharacter ? 
t.getCharacterStyles() : t.getParagraphStyles();
+            // TODO: what is the reaction for readOnly=false and 
styles.isEmpty()?
+            final int minLevel = Math.min(level, styles.size()-1);
+            if ("*".equals(name)) {
+                return styles.get(minLevel);
+            }
+            
+            for (int i=minLevel; i >= 0; i--) {
+                final TextPropCollection col = styles.get(i);
+                final TextProp tp = col.findByName(name);
+                if (tp != null) {
+                    return col;
+                }
+            }
         }
 
-        if (prop != null) {
-            return prop;
-        }
-        
         switch (txtype) {
             case TextHeaderAtom.CENTRE_BODY_TYPE:
             case TextHeaderAtom.HALF_BODY_TYPE:
             case TextHeaderAtom.QUARTER_BODY_TYPE:
-                txtype = TextHeaderAtom.BODY_TYPE;
-                break;
+                return getPropCollection(TextHeaderAtom.BODY_TYPE, level, 
name, isCharacter);
             case TextHeaderAtom.CENTER_TITLE_TYPE:
-                txtype = TextHeaderAtom.TITLE_TYPE;
-                break;
+                return getPropCollection(TextHeaderAtom.TITLE_TYPE, level, 
name, isCharacter);
             default:
                 return null;
         }
-
-        return getStyleAttribute(txtype, level, name, isCharacter);
     }
-    
+
     /**
      * Assign SlideShow for this slide master.
      */
@@ -132,7 +140,7 @@ public final class HSLFSlideMaster exten
                 _txmaster[txType] = txrec[i];
             }
         }
-        
+
         for (List<HSLFTextParagraph> paras : getTextParagraphs()) {
             for (HSLFTextParagraph htp : paras) {
                 int txType = htp.getRunType();
@@ -148,11 +156,6 @@ public final class HSLFSlideMaster exten
                     charStyles.size() <= level || paragraphStyles.size() <= 
level) {
                     throw new HSLFException("Master styles not initialized");
                 }
-                
-                htp.setMasterStyleReference(paragraphStyles.get(level));
-                for (HSLFTextRun htr : htp.getTextRuns()) {
-                    htr.setMasterStyleReference(charStyles.get(level));
-                }
             }
         }
     }

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
 (original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
 Sun Feb 11 20:39:18 2018
@@ -24,12 +24,16 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 import org.apache.poi.common.usermodel.fonts.FontGroup;
 import org.apache.poi.common.usermodel.fonts.FontInfo;
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.hslf.model.textproperties.BitMaskTextProp;
 import org.apache.poi.hslf.model.textproperties.FontAlignmentProp;
+import org.apache.poi.hslf.model.textproperties.HSLFTabStop;
+import org.apache.poi.hslf.model.textproperties.HSLFTabStopPropCollection;
 import org.apache.poi.hslf.model.textproperties.IndentProp;
 import org.apache.poi.hslf.model.textproperties.ParagraphFlagsTextProp;
 import org.apache.poi.hslf.model.textproperties.TextAlignmentProp;
@@ -37,34 +41,15 @@ import org.apache.poi.hslf.model.textpro
 import org.apache.poi.hslf.model.textproperties.TextProp;
 import org.apache.poi.hslf.model.textproperties.TextPropCollection;
 import 
org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType;
-import org.apache.poi.hslf.record.ColorSchemeAtom;
-import org.apache.poi.hslf.record.EscherTextboxWrapper;
-import org.apache.poi.hslf.record.InteractiveInfo;
-import org.apache.poi.hslf.record.MasterTextPropAtom;
-import org.apache.poi.hslf.record.OutlineTextRefAtom;
-import org.apache.poi.hslf.record.PPDrawing;
-import org.apache.poi.hslf.record.Record;
-import org.apache.poi.hslf.record.RecordContainer;
-import org.apache.poi.hslf.record.RecordTypes;
-import org.apache.poi.hslf.record.RoundTripHFPlaceholder12;
-import org.apache.poi.hslf.record.SlideListWithText;
-import org.apache.poi.hslf.record.SlidePersistAtom;
-import org.apache.poi.hslf.record.StyleTextProp9Atom;
-import org.apache.poi.hslf.record.StyleTextPropAtom;
-import org.apache.poi.hslf.record.TextBytesAtom;
-import org.apache.poi.hslf.record.TextCharsAtom;
-import org.apache.poi.hslf.record.TextHeaderAtom;
-import org.apache.poi.hslf.record.TextRulerAtom;
-import org.apache.poi.hslf.record.TextSpecInfoAtom;
-import org.apache.poi.hslf.record.TxInteractiveInfoAtom;
+import org.apache.poi.hslf.record.*;
 import org.apache.poi.sl.draw.DrawPaint;
 import org.apache.poi.sl.usermodel.AutoNumberingScheme;
-import org.apache.poi.sl.usermodel.MasterSheet;
 import org.apache.poi.sl.usermodel.PaintStyle;
 import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
 import org.apache.poi.sl.usermodel.Placeholder;
+import org.apache.poi.sl.usermodel.TabStop;
+import org.apache.poi.sl.usermodel.TabStop.TabStopType;
 import org.apache.poi.sl.usermodel.TextParagraph;
-import org.apache.poi.util.Internal;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 import org.apache.poi.util.StringUtil;
@@ -93,7 +78,6 @@ public final class HSLFTextParagraph imp
     private TextBytesAtom _byteAtom;
     private TextCharsAtom _charAtom;
     private TextPropCollection _paragraphStyle = new TextPropCollection(1, 
TextPropType.paragraph);
-    private TextPropCollection _masterStyle;
 
     protected TextRulerAtom _ruler;
     protected final List<HSLFTextRun> _runs = new ArrayList<>();
@@ -107,6 +91,33 @@ public final class HSLFTextParagraph imp
 
     private final List<HSLFTextParagraph> parentList;
 
+    private class HSLFTabStopDecorator implements TabStop {
+        final HSLFTabStop tabStop;
+
+        HSLFTabStopDecorator(final HSLFTabStop tabStop) {
+            this.tabStop = tabStop;
+        }
+
+        public double getPositionInPoints() {
+            return tabStop.getPositionInPoints();
+        }
+
+        public void setPositionInPoints(double position) {
+            tabStop.setPositionInPoints(position);
+            setDirty();
+        }
+
+        public TabStopType getType() {
+            return tabStop.getType();
+        }
+
+        public void setType(TabStopType type) {
+            tabStop.setType(type);
+            setDirty();
+        }
+    }
+
+
     /**
     * Constructs a Text Run from a Unicode text block.
     * Either a {@link TextCharsAtom} or a {@link TextBytesAtom} needs to be 
provided.
@@ -161,18 +172,6 @@ public final class HSLFTextParagraph imp
     }
 
     /**
-     * Setting a master style reference
-     *
-     * @param paragraphStyle the master style reference
-     *
-     * @since POI 3.14-Beta1
-     */
-    @Internal
-    /* package */ void setMasterStyleReference(TextPropCollection masterStyle) 
{
-        _masterStyle = masterStyle;
-    }
-
-    /**
      * Supply the Sheet we belong to, which might have an assigned SlideShow
      * Also passes it on to our child RichTextRuns
      */
@@ -341,7 +340,7 @@ public final class HSLFTextParagraph imp
 
     @Override
     public Double getLeftMargin() {
-        TextProp tp = getPropVal(_paragraphStyle, _masterStyle, "text.offset");
+        TextProp tp = getPropVal(_paragraphStyle, "text.offset");
         return (tp == null) ? null : Units.masterToPoints(tp.getValue());
     }
 
@@ -364,7 +363,7 @@ public final class HSLFTextParagraph imp
 
     @Override
     public Double getIndent() {
-        TextProp tp = getPropVal(_paragraphStyle, _masterStyle, 
"bullet.offset");
+        TextProp tp = getPropVal(_paragraphStyle, "bullet.offset");
         return (tp == null) ? null : Units.masterToPoints(tp.getValue());
     }
 
@@ -388,7 +387,7 @@ public final class HSLFTextParagraph imp
         if (fontInfo == null) {
             fontInfo = HSLFFontInfoPredefined.ARIAL;
         }
-        
+
         return fontInfo.getTypeface();
     }
 
@@ -422,7 +421,7 @@ public final class HSLFTextParagraph imp
 
     @Override
     public TextAlign getTextAlign() {
-        TextProp tp = getPropVal(_paragraphStyle, _masterStyle, "alignment");
+        TextProp tp = getPropVal(_paragraphStyle, "alignment");
         if (tp == null) {
             return null;
         }
@@ -440,7 +439,7 @@ public final class HSLFTextParagraph imp
 
     @Override
     public FontAlign getFontAlign() {
-        TextProp tp = getPropVal(_paragraphStyle, _masterStyle, 
FontAlignmentProp.NAME);
+        TextProp tp = getPropVal(_paragraphStyle, FontAlignmentProp.NAME);
         if (tp == null) {
             return null;
         }
@@ -606,7 +605,7 @@ public final class HSLFTextParagraph imp
      * Returns the bullet character
      */
     public Character getBulletChar() {
-        TextProp tp = getPropVal(_paragraphStyle, _masterStyle, "bullet.char");
+        TextProp tp = getPropVal(_paragraphStyle, "bullet.char");
         return (tp == null) ? null : (char)tp.getValue();
     }
 
@@ -637,7 +636,7 @@ public final class HSLFTextParagraph imp
      * Returns the bullet color
      */
     public Color getBulletColor() {
-        TextProp tp = getPropVal(_paragraphStyle, _masterStyle, 
"bullet.color");
+        TextProp tp = getPropVal(_paragraphStyle, "bullet.color");
         boolean hasColor = 
getFlag(ParagraphFlagsTextProp.BULLET_HARDCOLOR_IDX);
         if (tp == null || !hasColor) {
             // if bullet color is undefined, return color of first run
@@ -661,7 +660,7 @@ public final class HSLFTextParagraph imp
      */
     public void setBulletFont(String typeface) {
         if (typeface == null) {
-            setPropVal(_paragraphStyle, _masterStyle, "bullet.font", null);
+            setPropVal(_paragraphStyle, "bullet.font", null);
             setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, false);
             return;
         }
@@ -677,7 +676,7 @@ public final class HSLFTextParagraph imp
      * Returns the bullet font
      */
     public String getBulletFont() {
-        TextProp tp = getPropVal(_paragraphStyle, _masterStyle, "bullet.font");
+        TextProp tp = getPropVal(_paragraphStyle, "bullet.font");
         boolean hasFont = getFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX);
         if (tp == null || !hasFont) {
             return getDefaultFontFamily();
@@ -723,8 +722,64 @@ public final class HSLFTextParagraph imp
         return null;
     }
 
+
+    @Override
+    public List<? extends TabStop> getTabStops() {
+        final List<HSLFTabStop> tabStops;
+        final TextRulerAtom textRuler;
+        if (getSheet() instanceof HSLFSlideMaster) {
+            final HSLFTabStopPropCollection tpc = 
getMasterPropVal(_paragraphStyle, HSLFTabStopPropCollection.NAME);
+            if (tpc == null) {
+                return null;
+            }
+            tabStops = tpc.getTabStops();
+            textRuler = null;
+        } else {
+            textRuler = 
(TextRulerAtom)_headerAtom.getParentRecord().findFirstOfType(RecordTypes.TextRulerAtom.typeID);
+            if (textRuler == null) {
+                return null;
+            }
+            tabStops = textRuler.getTabStops();
+        }
+
+        return tabStops.stream().map((tabStop) -> new 
HSLFTabStopDecorator(tabStop)).collect(Collectors.toList());
+    }
+
+    @Override
+    public void addTabStops(final double positionInPoints, final TabStopType 
tabStopType) {
+        final HSLFTabStop ts = new HSLFTabStop(0, tabStopType);
+        ts.setPositionInPoints(positionInPoints);
+
+        if (getSheet() instanceof HSLFSlideMaster) {
+            final Consumer<HSLFTabStopPropCollection> con = (tp) -> 
tp.addTabStop(ts);
+            setPropValInner(_paragraphStyle, HSLFTabStopPropCollection.NAME, 
con);
+        } else {
+            final RecordContainer cont = _headerAtom.getParentRecord();
+            TextRulerAtom textRuler = 
(TextRulerAtom)cont.findFirstOfType(RecordTypes.TextRulerAtom.typeID);
+            if (textRuler == null) {
+                textRuler = TextRulerAtom.getParagraphInstance();
+                cont.appendChildRecord(textRuler);
+            }
+            textRuler.getTabStops().add(ts);
+        }
+    }
+
+    @Override
+    public void clearTabStops() {
+        if (getSheet() instanceof HSLFSlideMaster) {
+            setPropValInner(_paragraphStyle, HSLFTabStopPropCollection.NAME, 
null);
+        } else {
+            final RecordContainer cont = _headerAtom.getParentRecord();
+            final TextRulerAtom textRuler = 
(TextRulerAtom)cont.findFirstOfType(RecordTypes.TextRulerAtom.typeID);
+            if (textRuler == null) {
+                return;
+            }
+            textRuler.getTabStops().clear();
+        }
+    }
+
     private Double getPctOrPoints(String propName) {
-        TextProp tp = getPropVal(_paragraphStyle, _masterStyle, propName);
+        TextProp tp = getPropVal(_paragraphStyle, propName);
         if (tp == null) {
             return null;
         }
@@ -741,7 +796,7 @@ public final class HSLFTextParagraph imp
     }
 
     private boolean getFlag(int index) {
-        BitMaskTextProp tp = (BitMaskTextProp)getPropVal(_paragraphStyle, 
_masterStyle, ParagraphFlagsTextProp.NAME);
+        BitMaskTextProp tp = getPropVal(_paragraphStyle, 
ParagraphFlagsTextProp.NAME);
         return (tp == null) ? false : tp.getSubValue(index);
     }
 
@@ -759,56 +814,54 @@ public final class HSLFTextParagraph imp
      * The propName can be a comma-separated list, in case multiple equivalent 
values
      * are queried
      */
-    protected TextProp getPropVal(TextPropCollection props, TextPropCollection 
masterProps, String propName) {
+    protected <T extends TextProp> T getPropVal(TextPropCollection props, 
String propName) {
         String propNames[] = propName.split(",");
         for (String pn : propNames) {
-            TextProp prop = props.findByName(pn);
+            T prop = props.findByName(pn);
             if (isValidProp(prop)) {
                 return prop;
             }
         }
 
-        return getMasterPropVal(props, masterProps, propName);
+        return getMasterPropVal(props, propName);
     }
 
-    private TextProp getMasterPropVal(TextPropCollection props, 
TextPropCollection masterProps, String propName) {
+    private <T extends TextProp> T getMasterPropVal(final TextPropCollection 
props, final String propName) {
         boolean isChar = props.getTextPropType() == TextPropType.character;
 
         // check if we can delegate to master for the property
         if (!isChar) {
-            BitMaskTextProp maskProp = (BitMaskTextProp) 
props.findByName(ParagraphFlagsTextProp.NAME);
+            BitMaskTextProp maskProp = 
props.findByName(ParagraphFlagsTextProp.NAME);
             boolean hardAttribute = (maskProp != null && maskProp.getValue() 
== 0);
             if (hardAttribute) {
                 return null;
             }
         }
 
-        String propNames[] = propName.split(",");
-        if (masterProps == null) {
-            HSLFSheet sheet = getSheet();
-            int txtype = getRunType();
-            HSLFMasterSheet master = sheet.getMasterSheet();
+        final String propNames[] = propName.split(",");
+        final HSLFSheet sheet = getSheet();
+        final int txtype = getRunType();
+        final HSLFMasterSheet master;
+        if (sheet instanceof HSLFMasterSheet) {
+            master = (HSLFMasterSheet)sheet;
+        } else {
+            master = sheet.getMasterSheet();
             if (master == null) {
                 logger.log(POILogger.WARN, "MasterSheet is not available");
                 return null;
             }
+        }
 
-            for (String pn : propNames) {
-                TextProp prop = master.getStyleAttribute(txtype, 
getIndentLevel(), pn, isChar);
-                if (isValidProp(prop)) {
-                    return prop;
-                }
-            }
-        } else {
-            for (String pn : propNames) {
-                TextProp prop = masterProps.findByName(pn);
+        for (String pn : propNames) {
+            TextPropCollection masterProps = master.getPropCollection(txtype, 
getIndentLevel(), pn, isChar);
+            if (masterProps != null) {
+                T prop = masterProps.findByName(pn);
                 if (isValidProp(prop)) {
                     return prop;
                 }
             }
         }
 
-
         return null;
     }
 
@@ -826,22 +879,33 @@ public final class HSLFTextParagraph imp
      * @param name the name of the TextProp to fetch/add
      * @param val the value, null if unset
      */
-    protected void setPropVal(TextPropCollection props, TextPropCollection 
masterProps, String name, Integer val) {
-        TextPropCollection pc = props;
-        if (getSheet() instanceof MasterSheet && masterProps != null) {
-            pc = masterProps;
+    protected void setPropVal(final TextPropCollection props, final String 
name, final Integer val) {
+        setPropValInner(props, name, val == null ? null : tp -> 
tp.setValue(val));
+    }
+
+    private void setPropValInner(final TextPropCollection props, final String 
name, Consumer<? extends TextProp> handler) {
+        final boolean isChar = props.getTextPropType() == 
TextPropType.character;
+
+        final TextPropCollection pc;
+        if (_sheet instanceof HSLFMasterSheet) {
+            pc = ((HSLFMasterSheet)_sheet).getPropCollection(getRunType(), 
getIndentLevel(), "*", isChar);
+            if (pc == null) {
+                throw new HSLFException("Master text property collection can't 
be determined.");
+            }
+        } else {
+            pc = props;
         }
 
-        if (val == null) {
+        if (handler == null) {
             pc.removeByName(name);
-            return;
+        } else {
+            // Fetch / Add the TextProp
+            handler.accept(pc.addWithName(name));
         }
-
-        // Fetch / Add the TextProp
-        TextProp tp = pc.addWithName(name);
-        tp.setValue(val);
+        setDirty();
     }
 
+
     /**
      * Check and add linebreaks to text runs leading other paragraphs
      *
@@ -1595,7 +1659,7 @@ public final class HSLFTextParagraph imp
      * @param val The value to set for the TextProp
      */
     public void setParagraphTextPropVal(String propName, Integer val) {
-        setPropVal(_paragraphStyle, _masterStyle, propName, val);
+        setPropVal(_paragraphStyle, propName, val);
         setDirty();
     }
 

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java 
(original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java 
Sun Feb 11 20:39:18 2018
@@ -36,7 +36,6 @@ import org.apache.poi.sl.usermodel.Place
 import org.apache.poi.sl.usermodel.TextParagraph;
 import org.apache.poi.sl.usermodel.TextRun;
 import org.apache.poi.sl.usermodel.TextShape;
-import org.apache.poi.util.Internal;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
@@ -61,8 +60,6 @@ public final class HSLFTextRun implement
         */
        private TextPropCollection characterStyle = new TextPropCollection(1, 
TextPropType.character);
 
-       private TextPropCollection masterStyle;
-
        /**
         * Create a new wrapper around a rich text string
         * @param parentParagraph the parent paragraph
@@ -80,19 +77,6 @@ public final class HSLFTextRun implement
            this.characterStyle.updateTextSize(_runText.length());
        }
 
-    /**
-     * Setting a master style reference
-     *
-     * @param characterStyle the master style reference
-     *
-     * @since POI 3.14-Beta1
-     */
-       @Internal
-    /* package */ void setMasterStyleReference(TextPropCollection masterStyle) 
{
-        this.masterStyle = masterStyle;
-    }
-
-
        /**
         * Supply the SlideShow we belong to
         */
@@ -149,28 +133,34 @@ public final class HSLFTextRun implement
        }
 
        protected boolean getFlag(int index) {
-           if (characterStyle == null) {
-            return false;
-        }
-
-               BitMaskTextProp prop = 
(BitMaskTextProp)characterStyle.findByName(CharFlagsTextProp.NAME);
+               BitMaskTextProp prop = (characterStyle == null) ? null : 
characterStyle.findByName(CharFlagsTextProp.NAME);
 
                if (prop == null || !prop.getSubPropMatches()[index]) {
-            int txtype = parentParagraph.getRunType();
-                       HSLFSheet sheet = parentParagraph.getSheet();
-                       if (sheet != null) {
-                               HSLFMasterSheet master = sheet.getMasterSheet();
-                               if (master != null){
-                                       prop = 
(BitMaskTextProp)master.getStyleAttribute(txtype, 
parentParagraph.getIndentLevel(), CharFlagsTextProp.NAME, true);
-                               }
-                       } else {
-                               logger.log(POILogger.WARN, "MasterSheet is not 
available");
-                       }
+                   prop = getMasterProp(CharFlagsTextProp.NAME);
                }
 
                return prop == null ? false : prop.getSubValue(index);
        }
 
+       private <T extends TextProp> T getMasterProp(final String name) {
+        final int txtype = parentParagraph.getRunType();
+        final HSLFSheet sheet = parentParagraph.getSheet();
+        if (sheet == null) {
+            logger.log(POILogger.ERROR, "Sheet is not available");
+            return null;
+        }
+        
+        final HSLFMasterSheet master = sheet.getMasterSheet();
+        if (master == null) {
+            logger.log(POILogger.WARN, "MasterSheet is not available");
+            return null;
+        }
+        
+        final TextPropCollection col = master.getPropCollection(txtype, 
parentParagraph.getIndentLevel(), name, true);
+        return (col == null) ? null : col.findByName(name);
+       }
+       
+       
        /**
         * Set the value of the given flag in the CharFlagsTextProp, adding
         *  it if required.
@@ -189,8 +179,7 @@ public final class HSLFTextRun implement
         * @param val The value to set for the TextProp
         */
        public void setCharTextPropVal(String propName, Integer val) {
-           getTextParagraph().setPropVal(characterStyle, masterStyle, 
propName, val);
-           getTextParagraph().setDirty();
+           getTextParagraph().setPropVal(characterStyle, propName, val);
        }
 
 
@@ -270,7 +259,7 @@ public final class HSLFTextRun implement
         * @return the percentage of the font size. If the value is positive, 
it is superscript, otherwise it is subscript
         */
        public int getSuperscript() {
-               TextProp tp = getTextParagraph().getPropVal(characterStyle, 
masterStyle, "superscript");
+               TextProp tp = getTextParagraph().getPropVal(characterStyle, 
"superscript");
                return tp == null ? 0 : tp.getValue();
        }
 
@@ -285,7 +274,7 @@ public final class HSLFTextRun implement
 
     @Override
        public Double getFontSize() {
-        TextProp tp = getTextParagraph().getPropVal(characterStyle, 
masterStyle, "font.size");
+        TextProp tp = getTextParagraph().getPropVal(characterStyle, 
"font.size");
         return tp == null ? null : (double)tp.getValue();
        }
 
@@ -300,7 +289,7 @@ public final class HSLFTextRun implement
         * Gets the font index
         */
        public int getFontIndex() {
-        TextProp tp = getTextParagraph().getPropVal(characterStyle, 
masterStyle, "font.index");
+        TextProp tp = getTextParagraph().getPropVal(characterStyle, 
"font.index");
         return tp == null ? -1 : tp.getValue();
        }
 
@@ -401,7 +390,7 @@ public final class HSLFTextRun implement
                break;
                }
 
-        TextProp tp = getTextParagraph().getPropVal(characterStyle, 
masterStyle, propName);
+        TextProp tp = getTextParagraph().getPropVal(characterStyle, propName);
                return (tp != null) ? slideShow.getFont(tp.getValue()) : null;
        }
 
@@ -410,7 +399,7 @@ public final class HSLFTextRun implement
         */
        @Override
        public SolidPaint getFontColor() {
-               TextProp tp = getTextParagraph().getPropVal(characterStyle, 
masterStyle, "font.color");
+               TextProp tp = getTextParagraph().getPropVal(characterStyle, 
"font.color");
                if (tp == null) {
             return null;
         }

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java 
(original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java 
Sun Feb 11 20:39:18 2018
@@ -20,7 +20,7 @@ package org.apache.poi.hslf.usermodel;
 import java.util.ArrayList;
 import java.util.List;
 
-import org.apache.poi.hslf.model.textproperties.TextProp;
+import org.apache.poi.hslf.model.textproperties.TextPropCollection;
 import org.apache.poi.hslf.record.SlideAtom;
 
 /**
@@ -55,11 +55,11 @@ public final class HSLFTitleMaster exten
      * Delegate the call to the underlying slide master.
      */
     @Override
-    public TextProp getStyleAttribute(int txtype, int level, String name, 
boolean isCharacter) {
-        HSLFMasterSheet master = getMasterSheet();
-        return (master == null) ? null : master.getStyleAttribute(txtype, 
level, name, isCharacter);
+    public TextPropCollection getPropCollection(final int txtype, final int 
level, final String name, final boolean isCharacter) {
+        final HSLFMasterSheet master = getMasterSheet();
+        return (master == null) ? null : master.getPropCollection(txtype, 
level, name, isCharacter);
     }
-
+    
     /**
      * Returns the slide master for this title master.
      */

Modified: 
poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java?rev=1823893&r1=1823892&r2=1823893&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java
 (original)
+++ 
poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/model/TestSlideMaster.java
 Sun Feb 11 20:39:18 2018
@@ -17,6 +17,10 @@
 
 package org.apache.poi.hslf.model;
 
+import static org.apache.poi.hslf.record.TextHeaderAtom.BODY_TYPE;
+import static org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE;
+import static org.apache.poi.hslf.record.TextHeaderAtom.CENTRE_BODY_TYPE;
+import static org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -27,6 +31,7 @@ import java.util.List;
 
 import org.apache.poi.POIDataSamples;
 import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp;
+import org.apache.poi.hslf.model.textproperties.TextProp;
 import org.apache.poi.hslf.record.Environment;
 import org.apache.poi.hslf.record.TextHeaderAtom;
 import org.apache.poi.hslf.usermodel.HSLFMasterSheet;
@@ -51,62 +56,70 @@ public final class TestSlideMaster {
      */
     @Test
     public void testSlideMaster() throws IOException {
-        HSLFSlideShow ppt = new 
HSLFSlideShow(_slTests.openResourceAsStream("slide_master.ppt"));
+        final HSLFSlideShow ppt = new 
HSLFSlideShow(_slTests.openResourceAsStream("slide_master.ppt"));
 
-        Environment env = ppt.getDocumentRecord().getEnvironment();
+        final Environment env = ppt.getDocumentRecord().getEnvironment();
 
-        List<HSLFSlideMaster> master = ppt.getSlideMasters();
-        assertEquals(2, master.size());
+        assertEquals(2, ppt.getSlideMasters().size());
 
         //character attributes
-        assertEquals(40, 
master.get(0).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 0, "font.size", 
true).getValue());
-        assertEquals(48, 
master.get(1).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 0, "font.size", 
true).getValue());
+        assertEquals(40, getMasterVal(ppt, 0, TITLE_TYPE, "font.size", true));
+        assertEquals(48, getMasterVal(ppt, 1, TITLE_TYPE, "font.size", true));
 
-        int font1 = master.get(0).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 
0, "font.index", true).getValue();
-        int font2 = master.get(1).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 
0, "font.index", true).getValue();
+        int font1 = getMasterVal(ppt, 0, TITLE_TYPE, "font.index", true);
+        int font2 = getMasterVal(ppt, 1, TITLE_TYPE, "font.index", true);
         assertEquals("Arial", 
env.getFontCollection().getFontInfo(font1).getTypeface());
         assertEquals("Georgia", 
env.getFontCollection().getFontInfo(font2).getTypeface());
 
-        CharFlagsTextProp prop1 = 
(CharFlagsTextProp)master.get(0).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 
0, "char_flags", true);
+        CharFlagsTextProp prop1 = getMasterProp(ppt, 0, TITLE_TYPE, 
"char_flags", true);
         assertEquals(false, prop1.getSubValue(CharFlagsTextProp.BOLD_IDX));
         assertEquals(false, prop1.getSubValue(CharFlagsTextProp.ITALIC_IDX));
         assertEquals(true, prop1.getSubValue(CharFlagsTextProp.UNDERLINE_IDX));
 
-        CharFlagsTextProp prop2 = 
(CharFlagsTextProp)master.get(1).getStyleAttribute(TextHeaderAtom.TITLE_TYPE, 
0, "char_flags", true);
+        CharFlagsTextProp prop2 = getMasterProp(ppt, 1, TITLE_TYPE, 
"char_flags", true);
         assertEquals(false, prop2.getSubValue(CharFlagsTextProp.BOLD_IDX));
         assertEquals(true, prop2.getSubValue(CharFlagsTextProp.ITALIC_IDX));
         assertEquals(false, 
prop2.getSubValue(CharFlagsTextProp.UNDERLINE_IDX));
 
         //now paragraph attributes
-        assertEquals(0x266B, 
master.get(0).getStyleAttribute(TextHeaderAtom.BODY_TYPE, 0, "bullet.char", 
false).getValue());
-        assertEquals(0x2022, 
master.get(1).getStyleAttribute(TextHeaderAtom.BODY_TYPE, 0, "bullet.char", 
false).getValue());
+        assertEquals(0x266B, getMasterVal(ppt, 0, BODY_TYPE, "bullet.char", 
false));
+        assertEquals(0x2022, getMasterVal(ppt, 1, BODY_TYPE, "bullet.char", 
false));
 
-        int b1 = master.get(0).getStyleAttribute(TextHeaderAtom.BODY_TYPE, 0, 
"bullet.font", false).getValue();
-        int b2 = master.get(1).getStyleAttribute(TextHeaderAtom.BODY_TYPE, 0, 
"bullet.font", false).getValue();
+        int b1 = getMasterVal(ppt, 0, BODY_TYPE, "bullet.font", false);
+        int b2 = getMasterVal(ppt, 1, BODY_TYPE, "bullet.font", false);
         assertEquals("Arial", 
env.getFontCollection().getFontInfo(b1).getTypeface());
         assertEquals("Georgia", 
env.getFontCollection().getFontInfo(b2).getTypeface());
 
         ppt.close();
     }
 
+    @SuppressWarnings("unchecked")
+    private static <T extends TextProp> T getMasterProp(HSLFSlideShow ppt, int 
masterIdx, int txtype, String propName, boolean isCharacter) {
+        return 
(T)ppt.getSlideMasters().get(masterIdx).getPropCollection(txtype, 0, propName, 
isCharacter).findByName(propName);
+    }
+
+    private static int getMasterVal(HSLFSlideShow ppt, int masterIdx, int 
txtype, String propName, boolean isCharacter) {
+        return getMasterProp(ppt, masterIdx, txtype, propName, 
isCharacter).getValue();
+    }
+
+    
     /**
      * Test we can read default text attributes for a title master sheet
      */
     @Test
     public void testTitleMasterTextAttributes() throws IOException {
         HSLFSlideShow ppt = new 
HSLFSlideShow(_slTests.openResourceAsStream("slide_master.ppt"));
-        List<HSLFTitleMaster> master = ppt.getTitleMasters();
-        assertEquals(1, master.size());
+        assertEquals(1, ppt.getTitleMasters().size());
 
-        assertEquals(32, 
master.get(0).getStyleAttribute(TextHeaderAtom.CENTER_TITLE_TYPE, 0, 
"font.size", true).getValue());
-        CharFlagsTextProp prop1 = 
(CharFlagsTextProp)master.get(0).getStyleAttribute(TextHeaderAtom.CENTER_TITLE_TYPE,
 0, "char_flags", true);
-        assertEquals(true, prop1.getSubValue(CharFlagsTextProp.BOLD_IDX));
+        assertEquals(40, getMasterVal(ppt, 0, CENTER_TITLE_TYPE, "font.size", 
true));
+        CharFlagsTextProp prop1 = getMasterProp(ppt, 0, CENTER_TITLE_TYPE, 
"char_flags", true);
+        assertEquals(false, prop1.getSubValue(CharFlagsTextProp.BOLD_IDX));
         assertEquals(false, prop1.getSubValue(CharFlagsTextProp.ITALIC_IDX));
         assertEquals(true, prop1.getSubValue(CharFlagsTextProp.UNDERLINE_IDX));
 
-        assertEquals(20, 
master.get(0).getStyleAttribute(TextHeaderAtom.CENTRE_BODY_TYPE, 0, 
"font.size", true).getValue());
-        CharFlagsTextProp prop2 = 
(CharFlagsTextProp)master.get(0).getStyleAttribute(TextHeaderAtom.CENTRE_BODY_TYPE,
 0, "char_flags", true);
-        assertEquals(true, prop2.getSubValue(CharFlagsTextProp.BOLD_IDX));
+        assertEquals(32, getMasterVal(ppt, 0, CENTRE_BODY_TYPE, "font.size", 
true));
+        CharFlagsTextProp prop2 = getMasterProp(ppt, 0, CENTRE_BODY_TYPE, 
"char_flags", true);
+        assertEquals(false, prop2.getSubValue(CharFlagsTextProp.BOLD_IDX));
         assertEquals(false, prop2.getSubValue(CharFlagsTextProp.ITALIC_IDX));
         assertEquals(false, 
prop2.getSubValue(CharFlagsTextProp.UNDERLINE_IDX));
 



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to