]> source.dussan.org Git - poi.git/commitdiff
Rendering fixes
authorAndreas Beeker <kiwiwings@apache.org>
Wed, 17 Jun 2015 22:21:13 +0000 (22:21 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Wed, 17 Jun 2015 22:21:13 +0000 (22:21 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/common_sl@1686117 13f79535-47bb-0310-9956-ffa450edef68

22 files changed:
src/examples/src/org/apache/poi/hslf/examples/BulletsDemo.java
src/examples/src/org/apache/poi/xslf/usermodel/Tutorial2.java
src/examples/src/org/apache/poi/xslf/usermodel/Tutorial7.java
src/java/org/apache/poi/util/Units.java
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java
src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/FontAlignmentProp.java
src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java
src/scratchpad/src/org/apache/poi/hslf/record/FontCollection.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java
src/scratchpad/src/org/apache/poi/sl/draw/DrawTextParagraph.java
src/scratchpad/src/org/apache/poi/sl/draw/DrawTextShape.java
src/scratchpad/src/org/apache/poi/sl/usermodel/TextParagraph.java
src/scratchpad/src/org/apache/poi/sl/usermodel/TextRun.java
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPicture.java
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextRun.java
test-data/slideshow/alterman_security2.pptx [new file with mode: 0644]

index d95f970ce7f70d3a0c8451d742bc98e0afb5cab2..0032bc80970c1fda56c304295dc2a87603eaa0f0 100644 (file)
@@ -39,8 +39,8 @@ public final class BulletsDemo {
         HSLFTextParagraph rt = shape.getTextParagraphs().get(0);
         rt.getTextRuns().get(0).setFontSize(42);
         rt.setBullet(true);
-        rt.setIndent(0);  //bullet offset
-        rt.setLeftMargin(50);   //text offset (should be greater than bullet offset)
+        rt.setIndent(0d);  //bullet offset
+        rt.setLeftMargin(50d);   //text offset (should be greater than bullet offset)
         rt.setBulletChar('\u263A'); //bullet character
         shape.setText(
                 "January\r" +
index 373f01f33fb9953be24fa8225f7a22fed6e4dee0..91366b9d068ab1d4e69fb126f6a7730892359f9c 100644 (file)
@@ -48,8 +48,8 @@ public class Tutorial2 {
         XSLFTextParagraph p2 = shape1.addNewTextParagraph();\r
         // If spaceBefore >= 0, then space is a percentage of normal line height.\r
         // If spaceBefore < 0, the absolute value of linespacing is the spacing in points\r
-        p2.setSpaceBefore(-20); // 20 pt from the previous paragraph\r
-        p2.setSpaceAfter(300); // 3 lines after the paragraph\r
+        p2.setSpaceBefore(-20d); // 20 pt from the previous paragraph\r
+        p2.setSpaceAfter(300d); // 3 lines after the paragraph\r
         XSLFTextRun r2 = p2.addNewTextRun();\r
         r2.setText("Paragraph  properties apply to all text residing within the corresponding paragraph.");\r
         r2.setFontSize(16);\r
@@ -62,8 +62,8 @@ public class Tutorial2 {
         r3.setFontColor(new Color(85, 142, 213));\r
 \r
         XSLFTextParagraph p4 = shape1.addNewTextParagraph();\r
-        p4.setSpaceBefore(-20); // 20 pt from the previous paragraph\r
-        p4.setSpaceAfter(300); // 3 lines after the paragraph\r
+        p4.setSpaceBefore(-20d); // 20 pt from the previous paragraph\r
+        p4.setSpaceAfter(300d); // 3 lines after the paragraph\r
         XSLFTextRun r4 = p4.addNewTextRun();\r
         r4.setFontSize(16);\r
         r4.setText(\r
index a80f23cad7a264f0aebb47ac202e8952c1ff5de0..95252d72f563dd2fce9d2211213168d7b542e9d5 100644 (file)
@@ -45,9 +45,9 @@ public class Tutorial7 {
 \r
         XSLFTextParagraph p2 = shape.addNewTextParagraph();\r
         // indentation before text\r
-        p2.setLeftMargin(60);\r
+        p2.setLeftMargin(60d);\r
         // the bullet is set 40 pt before the text\r
-        p2.setIndent(-40);\r
+        p2.setIndent(-40d);\r
         p2.setBullet(true);\r
         // customize bullets\r
         p2.setBulletFontColor(Color.red);\r
index bc643fd97bd5d10071af703ba485c6a285384cca..107a9b583ef135d9c7afc6a46518a4f92f1ae869 100644 (file)
@@ -16,6 +16,8 @@
 ==================================================================== */\r
 package org.apache.poi.util;\r
 \r
+import org.apache.poi.hslf.usermodel.HSLFShape;\r
+\r
 /**\r
  * @author Yegor Kozlov\r
  */\r
@@ -23,6 +25,22 @@ public class Units {
     public static final int EMU_PER_PIXEL = 9525;\r
     public static final int EMU_PER_POINT = 12700;\r
 \r
+    /**\r
+     * Master DPI (576 pixels per inch).\r
+     * Used by the reference coordinate system in PowerPoint (HSLF)\r
+     */\r
+    public static final int MASTER_DPI = 576;    \r
+\r
+    /**\r
+     * Pixels DPI (96 pixels per inch)\r
+     */\r
+    public static final int PIXEL_DPI = 96;\r
+\r
+    /**\r
+     * Points DPI (72 pixels per inch)\r
+     */\r
+    public static final int POINT_DPI = 72;    \r
+\r
     /**\r
      * Converts points to EMUs\r
      * @param points points\r
@@ -70,4 +88,17 @@ public class Units {
         int fixedPoint = (i << 16) | (f & 0xFFFF);\r
         return fixedPoint;\r
     }\r
+\r
+    public static double masterToPoints(int masterDPI) {\r
+        double points = masterDPI;\r
+        points *= HSLFShape.POINT_DPI;\r
+        points /= HSLFShape.MASTER_DPI;\r
+        return points;\r
+    }\r
+    \r
+    public static int pointsToMaster(double points) {\r
+        points *= HSLFShape.MASTER_DPI;\r
+        points /= HSLFShape.POINT_DPI;\r
+        return (int)points;\r
+    }\r
 }\r
index aef03197dd9c0c6ec18e80bdecf9fa4470c07ffa..f0cf3a78a98cd3d6c895125d042f896981591bd8 100644 (file)
@@ -137,8 +137,10 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
     /**\r
      * Returns the alignment that is applied to the paragraph.\r
      *\r
-     * If this attribute is omitted, then a value of left is implied.\r
-     * @return ??? alignment that is applied to the paragraph\r
+     * If this attribute is omitted, then null is returned.\r
+     * User code can imply the value {@link TextAlign#LEFT} then.\r
+     *\r
+     * @return alignment that is applied to the paragraph\r
      */\r
     public TextAlign getTextAlign(){\r
         ParagraphPropertyFetcher<TextAlign> fetcher = new ParagraphPropertyFetcher<TextAlign>(getLevel()){\r
@@ -152,7 +154,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
             }\r
         };\r
         fetchParagraphProperty(fetcher);\r
-        return fetcher.getValue() == null ? TextAlign.LEFT : fetcher.getValue();\r
+        return fetcher.getValue();\r
     }\r
 \r
     /**\r
@@ -184,7 +186,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
             }\r
         };\r
         fetchParagraphProperty(fetcher);\r
-        return fetcher.getValue() == null ? FontAlign.AUTO : fetcher.getValue();\r
+        return fetcher.getValue();\r
     }\r
 \r
     /**\r
@@ -294,7 +296,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
      *\r
      * @return the bullet size\r
      */\r
-    public double getBulletFontSize(){\r
+    public Double getBulletFontSize(){\r
         ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){\r
             public boolean fetch(CTTextParagraphProperties props){\r
                 if(props.isSetBuSzPct()){\r
@@ -309,7 +311,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
             }\r
         };\r
         fetchParagraphProperty(fetcher);\r
-        return fetcher.getValue() == null ? 100 : fetcher.getValue();\r
+        return fetcher.getValue();\r
     }\r
 \r
     /**\r
@@ -334,27 +336,19 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
         }\r
    }\r
 \r
-    /**\r
-     * Specifies the indent size that will be applied to the first line of text in the paragraph.\r
-     *\r
-     * @param value the indent in points. \r
-     */\r
     @Override\r
-    public void setIndent(double value){\r
+    public void setIndent(Double indent){\r
+        if (indent == null && !_p.isSetPPr()) return;\r
         CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
-        if(value == -1) {\r
+        if(indent == -1) {\r
             if(pr.isSetIndent()) pr.unsetIndent();\r
         } else {\r
-            pr.setIndent(Units.toEMU(value));\r
+            pr.setIndent(Units.toEMU(indent));\r
         }\r
     }\r
 \r
-    /**\r
-     *\r
-     * @return the indent applied to the first line of text in the paragraph.\r
-     */\r
     @Override\r
-    public double getIndent(){\r
+    public Double getIndent() {\r
 \r
         ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){\r
             public boolean fetch(CTTextParagraphProperties props){\r
@@ -367,32 +361,26 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
         };\r
         fetchParagraphProperty(fetcher);\r
 \r
-        return fetcher.getValue() == null ? 0 : fetcher.getValue();\r
+        return fetcher.getValue();\r
     }\r
 \r
-    /**\r
-     * Specifies the left margin of the paragraph. This is specified in addition to the text body\r
-     * inset and applies only to this text paragraph. That is the text body Inset and the LeftMargin\r
-     * attributes are additive with respect to the text position.\r
-     *\r
-     * @param value the left margin (in points) of the paragraph\r
-     */\r
     @Override\r
-    public void setLeftMargin(double value){\r
+    public void setLeftMargin(Double leftMargin){\r
+        if (leftMargin == null && !_p.isSetPPr()) return;\r
         CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
-        if(value == -1) {\r
+        if (leftMargin == null) {\r
             if(pr.isSetMarL()) pr.unsetMarL();\r
         } else {\r
-            pr.setMarL(Units.toEMU(value));\r
+            pr.setMarL(Units.toEMU(leftMargin));\r
         }\r
 \r
     }\r
 \r
     /**\r
-     * @return the left margin (in points) of the paragraph\r
+     * @return the left margin (in points) of the paragraph, null if unset\r
      */\r
     @Override\r
-    public double getLeftMargin(){\r
+    public Double getLeftMargin(){\r
         ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){\r
             public boolean fetch(CTTextParagraphProperties props){\r
                 if(props.isSetMarL()){\r
@@ -405,32 +393,26 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
         };\r
         fetchParagraphProperty(fetcher);\r
         // if the marL attribute is omitted, then a value of 347663 is implied\r
-        return fetcher.getValue() == null ? 0 : fetcher.getValue();\r
+        return fetcher.getValue();\r
     }\r
 \r
-    /**\r
-     * Specifies the right margin of the paragraph. This is specified in addition to the text body\r
-     * inset and applies only to this text paragraph. That is the text body Inset and the RightMargin\r
-     * attributes are additive with respect to the text position.\r
-     *\r
-     * @param value the right margin (in points) of the paragraph\r
-     */\r
     @Override\r
-    public void setRightMargin(double value){\r
+    public void setRightMargin(Double rightMargin){\r
+        if (rightMargin == null && !_p.isSetPPr()) return;\r
         CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
-        if(value == -1) {\r
+        if(rightMargin == -1) {\r
             if(pr.isSetMarR()) pr.unsetMarR();\r
         } else {\r
-            pr.setMarR(Units.toEMU(value));\r
+            pr.setMarR(Units.toEMU(rightMargin));\r
         }\r
     }\r
 \r
     /**\r
      *\r
-     * @return the right margin of the paragraph\r
+     * @return the right margin of the paragraph, null if unset\r
      */\r
     @Override\r
-    public double getRightMargin(){\r
+    public Double getRightMargin(){\r
         ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){\r
             public boolean fetch(CTTextParagraphProperties props){\r
                 if(props.isSetMarR()){\r
@@ -443,14 +425,11 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
         };\r
         fetchParagraphProperty(fetcher);\r
         // if the marL attribute is omitted, then a value of 347663 is implied\r
-        return fetcher.getValue() == null ? 0 : fetcher.getValue();\r
+        return fetcher.getValue();\r
     }\r
 \r
-    /**\r
-     *\r
-     * @return the default size for a tab character within this paragraph in points\r
-     */\r
-    public double getDefaultTabSize(){\r
+    @Override\r
+    public Double getDefaultTabSize(){\r
         ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){\r
             public boolean fetch(CTTextParagraphProperties props){\r
                 if(props.isSetDefTabSz()){\r
@@ -462,7 +441,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
             }\r
         };\r
         fetchParagraphProperty(fetcher);\r
-        return fetcher.getValue() == null ? 0 : fetcher.getValue();\r
+        return fetcher.getValue();\r
     }\r
 \r
     public double getTabStop(final int idx){\r
@@ -491,16 +470,25 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
     }\r
 \r
     @Override\r
-    public void setLineSpacing(double linespacing){\r
+    public void setLineSpacing(Double lineSpacing){\r
+        if (lineSpacing == null && !_p.isSetPPr()) return;\r
         CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
-        CTTextSpacing spc = CTTextSpacing.Factory.newInstance();\r
-        if(linespacing >= 0) spc.addNewSpcPct().setVal((int)(linespacing*1000));\r
-        else spc.addNewSpcPts().setVal((int)(-linespacing*100));\r
-        pr.setLnSpc(spc);\r
+        if(lineSpacing == null) {\r
+            if (pr.isSetLnSpc()) pr.unsetLnSpc();\r
+        } else {\r
+            CTTextSpacing spc = (pr.isSetLnSpc()) ? pr.getLnSpc() : pr.addNewLnSpc();\r
+            if (lineSpacing >= 0) {\r
+                (spc.isSetSpcPct() ? spc.getSpcPct() : spc.addNewSpcPct()).setVal((int)(lineSpacing*1000));\r
+                if (spc.isSetSpcPts()) spc.unsetSpcPts();\r
+            } else {\r
+                (spc.isSetSpcPts() ? spc.getSpcPts() : spc.addNewSpcPts()).setVal((int)(-lineSpacing*100));\r
+                if (spc.isSetSpcPct()) spc.unsetSpcPct();\r
+            }\r
+        }\r
     }\r
 \r
     @Override\r
-    public double getLineSpacing(){\r
+    public Double getLineSpacing(){\r
         ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){\r
             public boolean fetch(CTTextParagraphProperties props){\r
                 if(props.isSetLnSpc()){\r
@@ -515,8 +503,8 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
         };\r
         fetchParagraphProperty(fetcher);\r
 \r
-        double lnSpc = fetcher.getValue() == null ? 100 : fetcher.getValue();\r
-        if(lnSpc > 0) {\r
+        Double lnSpc = fetcher.getValue();\r
+        if (lnSpc != null && lnSpc > 0) {\r
             // check if the percentage value is scaled\r
             CTTextNormalAutofit normAutofit = getParentShape().getTextBodyPr().getNormAutofit();\r
             if(normAutofit != null) {\r
@@ -528,26 +516,9 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
         return lnSpc;\r
     }\r
 \r
-    /**\r
-     * Set the amount of vertical white space that will be present before the paragraph.\r
-     * This space is specified in either percentage or points:\r
-     * <p>\r
-     * If spaceBefore >= 0, then space is a percentage of normal line height.\r
-     * If spaceBefore < 0, the absolute value of linespacing is the spacing in points\r
-     * </p>\r
-     * Examples:\r
-     * <pre><code>\r
-     *      // The paragraph will be formatted to have a spacing before the paragraph text.\r
-     *      // The spacing will be 200% of the size of the largest text on each line\r
-     *      paragraph.setSpaceBefore(200);\r
-     *\r
-     *      // The spacing will be a size of 48 points\r
-     *      paragraph.setSpaceBefore(-48.0);\r
-     * </code></pre>\r
-     *\r
-     * @param spaceBefore the vertical white space before the paragraph.\r
-     */\r
-    public void setSpaceBefore(double spaceBefore){\r
+    @Override\r
+    public void setSpaceBefore(Double spaceBefore){\r
+        if (spaceBefore == null && !_p.isSetPPr()) return;\r
         CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
         CTTextSpacing spc = CTTextSpacing.Factory.newInstance();\r
         if(spaceBefore >= 0) spc.addNewSpcPct().setVal((int)(spaceBefore*1000));\r
@@ -555,17 +526,8 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
         pr.setSpcBef(spc);\r
     }\r
 \r
-    /**\r
-     * The amount of vertical white space before the paragraph\r
-     * This may be specified in two different ways, percentage spacing and font point spacing:\r
-     * <p>\r
-     * If spaceBefore >= 0, then space is a percentage of normal line height.\r
-     * If spaceBefore < 0, the absolute value of linespacing is the spacing in points\r
-     * </p>\r
-     *\r
-     * @return the vertical white space before the paragraph\r
-     */\r
-    public double getSpaceBefore(){\r
+    @Override\r
+    public Double getSpaceBefore(){\r
         ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){\r
             public boolean fetch(CTTextParagraphProperties props){\r
                 if(props.isSetSpcBef()){\r
@@ -580,30 +542,11 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
         };\r
         fetchParagraphProperty(fetcher);\r
 \r
-        double spcBef = fetcher.getValue() == null ? 0 : fetcher.getValue();\r
-        return spcBef;\r
+        return fetcher.getValue();\r
     }\r
 \r
-    /**\r
-     * Set the amount of vertical white space that will be present after the paragraph.\r
-     * This space is specified in either percentage or points:\r
-     * <p>\r
-     * If spaceAfter >= 0, then space is a percentage of normal line height.\r
-     * If spaceAfter < 0, the absolute value of linespacing is the spacing in points\r
-     * </p>\r
-     * Examples:\r
-     * <pre><code>\r
-     *      // The paragraph will be formatted to have a spacing after the paragraph text.\r
-     *      // The spacing will be 200% of the size of the largest text on each line\r
-     *      paragraph.setSpaceAfter(200);\r
-     *\r
-     *      // The spacing will be a size of 48 points\r
-     *      paragraph.setSpaceAfter(-48.0);\r
-     * </code></pre>\r
-     *\r
-     * @param spaceAfter the vertical white space after the paragraph.\r
-     */\r
-    public void setSpaceAfter(double spaceAfter){\r
+    @Override\r
+    public void setSpaceAfter(Double spaceAfter){\r
         CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
         CTTextSpacing spc = CTTextSpacing.Factory.newInstance();\r
         if(spaceAfter >= 0) spc.addNewSpcPct().setVal((int)(spaceAfter*1000));\r
@@ -611,17 +554,8 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
         pr.setSpcAft(spc);\r
     }\r
 \r
-    /**\r
-     * The amount of vertical white space after the paragraph\r
-     * This may be specified in two different ways, percentage spacing and font point spacing:\r
-     * <p>\r
-     * If spaceBefore >= 0, then space is a percentage of normal line height.\r
-     * If spaceBefore < 0, the absolute value of linespacing is the spacing in points\r
-     * </p>\r
-     *\r
-     * @return the vertical white space after the paragraph\r
-     */\r
-    public double getSpaceAfter(){\r
+    @Override\r
+    public Double getSpaceAfter(){\r
         ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){\r
             public boolean fetch(CTTextParagraphProperties props){\r
                 if(props.isSetSpcAft()){\r
@@ -635,7 +569,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
             }\r
         };\r
         fetchParagraphProperty(fetcher);\r
-        return fetcher.getValue() == null ? 0 : fetcher.getValue();\r
+        return fetcher.getValue();\r
     }\r
 \r
     /**\r
@@ -647,7 +581,6 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
      */\r
     public void setLevel(int level){\r
         CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
-\r
         pr.setLvl(level);\r
     }\r
 \r
@@ -657,10 +590,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
      */\r
     public int getLevel(){\r
         CTTextParagraphProperties pr = _p.getPPr();\r
-        if(pr == null) return 0;\r
-\r
-        return pr.getLvl();\r
-\r
+        return (pr == null || !pr.isSetLvl()) ? 0 : pr.getLvl();\r
     }\r
 \r
     /**\r
@@ -721,53 +651,70 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
     }\r
 \r
 \r
-    CTTextParagraphProperties getDefaultMasterStyle(){\r
+    /* package */ CTTextParagraphProperties getDefaultMasterStyle(){\r
         CTPlaceholder ph = _shape.getCTPlaceholder();\r
-        String defaultStyleSelector;\r
-        if(ph == null) defaultStyleSelector = "otherStyle";   // no placeholder means plain text box\r
-        else {\r
-            switch(ph.getType().intValue()){\r
-                case STPlaceholderType.INT_TITLE:\r
-                case STPlaceholderType.INT_CTR_TITLE:\r
-                    defaultStyleSelector = "titleStyle";\r
-                    break;\r
-                case STPlaceholderType.INT_FTR:\r
-                case STPlaceholderType.INT_SLD_NUM:\r
-                case STPlaceholderType.INT_DT:\r
-                    defaultStyleSelector = "otherStyle";\r
-                    break;\r
-                default:\r
-                    defaultStyleSelector = "bodyStyle";\r
-                    break;\r
-            }\r
+        String defaultStyleSelector;   \r
+        switch(ph == null ? -1 : ph.getType().intValue()) {\r
+            case STPlaceholderType.INT_TITLE:\r
+            case STPlaceholderType.INT_CTR_TITLE:\r
+                defaultStyleSelector = "titleStyle";\r
+                break;\r
+            case -1: // no placeholder means plain text box\r
+            case STPlaceholderType.INT_FTR:\r
+            case STPlaceholderType.INT_SLD_NUM:\r
+            case STPlaceholderType.INT_DT:\r
+                defaultStyleSelector = "otherStyle";\r
+                break;\r
+            default:\r
+                defaultStyleSelector = "bodyStyle";\r
+                break;\r
         }\r
         int level = getLevel();\r
 \r
         // wind up and find the root master sheet which must be slide master\r
         XSLFSheet masterSheet = _shape.getSheet();\r
-        while (masterSheet.getMasterSheet() != null){\r
-            masterSheet = (XSLFSheet)masterSheet.getMasterSheet();\r
+        for (XSLFSheet m = masterSheet; m != null; m = (XSLFSheet)m.getMasterSheet()) {\r
+            masterSheet = m;\r
         }\r
 \r
-        XmlObject[] o = masterSheet.getXmlObject().selectPath(\r
-                "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +\r
-                "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +\r
-                ".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr");\r
-        if (o.length == 1){\r
-            return (CTTextParagraphProperties)o[0];\r
-        } else {\r
-                o = masterSheet.getXmlObject().selectPath(\r
-                "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +\r
-                "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " +\r
-                ".//p:notesStyle/a:lvl" +(level+1)+ "pPr");\r
-                \r
-            if (o.length == 1){\r
+        String nsDecl =\r
+            "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +\r
+            "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' ";\r
+        String xpaths[] = {\r
+            nsDecl+".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr",\r
+            nsDecl+".//p:notesStyle/a:lvl" +(level+1)+ "pPr"\r
+        };\r
+        XmlObject xo = masterSheet.getXmlObject();\r
+        for (String xpath : xpaths) {\r
+            XmlObject[] o = xo.selectPath(xpath);\r
+            if (o.length == 1) {\r
                 return (CTTextParagraphProperties)o[0];\r
             }\r
-            \r
-            throw new IllegalArgumentException("Failed to fetch default style for " +\r
-                    defaultStyleSelector + " and level=" + level);\r
         }\r
+        \r
+//        for (CTTextBody txBody : (CTTextBody[])xo.selectPath(nsDecl+".//p:txBody")) {\r
+//            CTTextParagraphProperties defaultPr = null, lastPr = null;\r
+//            boolean hasLvl = false;\r
+//            for (CTTextParagraph p : txBody.getPArray()) {\r
+//                CTTextParagraphProperties pr = p.getPPr();\r
+//                if (pr.isSetLvl()) {\r
+//                    hasLvl |= true;\r
+//                    lastPr = pr;\r
+//                    if (pr.getLvl() == level) return pr;\r
+//                } else {\r
+//                    defaultPr = pr;\r
+//                }\r
+//            }\r
+//            if (!hasLvl) continue;\r
+//            if (level == 0 && defaultPr != null) return defaultPr;\r
+//            if (lastPr != null) return lastPr;\r
+//            break;\r
+//        }\r
+//           \r
+//        String err = "Failed to fetch default style for " + defaultStyleSelector + " and level=" + level;\r
+//        throw new IllegalArgumentException(err);\r
+        \r
+        return null;\r
     }\r
 \r
     private <T> boolean fetchParagraphProperty(ParagraphPropertyFetcher<T> visitor){\r
@@ -860,9 +807,9 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
     }\r
 \r
     @Override\r
-    public double getDefaultFontSize() {\r
+    public Double getDefaultFontSize() {\r
         CTTextCharacterProperties endPr = _p.getEndParaRPr();\r
-        return (endPr == null || !endPr.isSetSz()) ? 12 : (endPr.getSz() / 100);\r
+        return (endPr == null || !endPr.isSetSz()) ? 12 : (endPr.getSz() / 100.);\r
     }\r
 \r
     @Override\r
@@ -871,6 +818,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
     }\r
 \r
     public BulletStyle getBulletStyle() {\r
+        if (!isBullet()) return null;\r
         return new BulletStyle(){\r
             public String getBulletCharacter() {\r
                 return XSLFTextParagraph.this.getBulletCharacter();\r
@@ -880,7 +828,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFTextRun> {
                 return XSLFTextParagraph.this.getBulletFont();\r
             }\r
 \r
-            public double getBulletFontSize() {\r
+            public Double getBulletFontSize() {\r
                 return XSLFTextParagraph.this.getBulletFontSize();\r
             }\r
 \r
index 2b63a5809997cc3df8019b392f496413fc72a96b..d99df7e79a0a3f6b5926c1bc8624179a6468019d 100644 (file)
 package org.apache.poi.xslf.usermodel;\r
 \r
 import java.awt.Color;\r
-import java.awt.font.FontRenderContext;\r
-import java.awt.font.TextAttribute;\r
-import java.awt.font.TextLayout;\r
-import java.text.AttributedString;\r
 \r
 import org.apache.poi.sl.usermodel.TextRun;\r
 import org.apache.poi.util.Beta;\r
 import org.apache.poi.xslf.model.CharacterPropertyFetcher;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.STTextStrikeType;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.*;\r
 import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;\r
 \r
 /**\r
@@ -89,28 +74,6 @@ public class XSLFTextRun implements TextRun {
         return buf.toString();\r
     }\r
 \r
-    /**\r
-     * Replace a tab with the effective number of white spaces.\r
-     */\r
-    private String tab2space(){\r
-        AttributedString string = new AttributedString(" ");\r
-        // user can pass an object to convert fonts via a rendering hint\r
-        string.addAttribute(TextAttribute.FAMILY, getFontFamily());\r
-\r
-        string.addAttribute(TextAttribute.SIZE, (float)getFontSize());\r
-        TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true));\r
-        double wspace = l.getAdvance();\r
-\r
-        double tabSz = _p.getDefaultTabSize();\r
-\r
-        int numSpaces = (int)Math.ceil(tabSz / wspace);\r
-        StringBuffer buf = new StringBuffer();\r
-        for(int i = 0; i < numSpaces; i++) {\r
-            buf.append(' ');\r
-        }\r
-        return buf.toString();\r
-    }\r
-    \r
     public void setText(String text){\r
         _r.setT(text);\r
     }\r
@@ -175,9 +138,10 @@ public class XSLFTextRun implements TextRun {
     }\r
 \r
     /**\r
-     * @return font size in points or -1 if font size is not set.\r
+     * @return font size in points or null if font size is not set.\r
      */\r
-    public double getFontSize(){\r
+    @Override\r
+    public Double getFontSize(){\r
         double scale = 1;\r
         CTTextNormalAutofit afit = getParentParagraph().getParentShape().getTextBodyPr().getNormAutofit();\r
         if(afit != null) scale = (double)afit.getFontScale() / 100000;\r
@@ -192,7 +156,7 @@ public class XSLFTextRun implements TextRun {
             }\r
         };\r
         fetchCharacterProperty(fetcher);\r
-        return fetcher.getValue() == null ? -1 : fetcher.getValue()*scale;\r
+        return fetcher.getValue() == null ? null : fetcher.getValue()*scale;\r
     }\r
 \r
     /**\r
@@ -514,7 +478,7 @@ public class XSLFTextRun implements TextRun {
         return new XSLFHyperlink(_r.getRPr().getHlinkClick(), this);\r
     }\r
 \r
-    private boolean fetchCharacterProperty(CharacterPropertyFetcher fetcher){\r
+    private boolean fetchCharacterProperty(CharacterPropertyFetcher<?> fetcher){\r
         boolean ok = false;\r
 \r
         if(_r.isSetRPr()) ok = fetcher.fetch(getRPr());\r
index 6fc5690cca2b775b8fe4ff7e73920c4d33e6089f..b3c44f84a774fa018d9849be137abe87381903cb 100644 (file)
@@ -125,7 +125,7 @@ public class TestXSLFAutoShape {
         p.setIndent(2.0);\r
         assertEquals(2.0, p.getIndent(), 0);\r
         assertTrue(p.getXmlObject().getPPr().isSetIndent());\r
-        p.setIndent(-1);\r
+        p.setIndent(-1d);\r
         assertEquals(0.0, p.getIndent(), 0);\r
         assertFalse(p.getXmlObject().getPPr().isSetIndent());\r
         p.setIndent(10.0);\r
@@ -149,44 +149,44 @@ public class TestXSLFAutoShape {
 \r
 \r
         assertFalse(p.getXmlObject().getPPr().isSetSpcAft());\r
-        p.setSpaceAfter(200);\r
+        p.setSpaceAfter(200d);\r
         assertEquals(200000, p.getXmlObject().getPPr().getSpcAft().getSpcPct().getVal());\r
         assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPts());\r
-        p.setSpaceAfter(100);\r
+        p.setSpaceAfter(100d);\r
         assertEquals(100000, p.getXmlObject().getPPr().getSpcAft().getSpcPct().getVal());\r
         assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPts());\r
-        p.setSpaceAfter(-20);\r
+        p.setSpaceAfter(-20d);\r
         assertEquals(2000, p.getXmlObject().getPPr().getSpcAft().getSpcPts().getVal());\r
         assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPct());\r
-        p.setSpaceAfter(-10);\r
+        p.setSpaceAfter(-10d);\r
         assertEquals(1000, p.getXmlObject().getPPr().getSpcAft().getSpcPts().getVal());\r
         assertFalse(p.getXmlObject().getPPr().getSpcAft().isSetSpcPct());\r
 \r
         assertFalse(p.getXmlObject().getPPr().isSetSpcBef());\r
-        p.setSpaceBefore(200);\r
+        p.setSpaceBefore(200d);\r
         assertEquals(200000, p.getXmlObject().getPPr().getSpcBef().getSpcPct().getVal());\r
         assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPts());\r
-        p.setSpaceBefore(100);\r
+        p.setSpaceBefore(100d);\r
         assertEquals(100000, p.getXmlObject().getPPr().getSpcBef().getSpcPct().getVal());\r
         assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPts());\r
-        p.setSpaceBefore(-20);\r
+        p.setSpaceBefore(-20d);\r
         assertEquals(2000, p.getXmlObject().getPPr().getSpcBef().getSpcPts().getVal());\r
         assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPct());\r
-        p.setSpaceBefore(-10);\r
+        p.setSpaceBefore(-10d);\r
         assertEquals(1000, p.getXmlObject().getPPr().getSpcBef().getSpcPts().getVal());\r
         assertFalse(p.getXmlObject().getPPr().getSpcBef().isSetSpcPct());\r
 \r
         assertFalse(p.getXmlObject().getPPr().isSetLnSpc());\r
-        p.setLineSpacing(200);\r
+        p.setLineSpacing(200d);\r
         assertEquals(200000, p.getXmlObject().getPPr().getLnSpc().getSpcPct().getVal());\r
         assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPts());\r
-        p.setLineSpacing(100);\r
+        p.setLineSpacing(100d);\r
         assertEquals(100000, p.getXmlObject().getPPr().getLnSpc().getSpcPct().getVal());\r
         assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPts());\r
-        p.setLineSpacing(-20);\r
+        p.setLineSpacing(-20d);\r
         assertEquals(2000, p.getXmlObject().getPPr().getLnSpc().getSpcPts().getVal());\r
         assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPct());\r
-        p.setLineSpacing(-10);\r
+        p.setLineSpacing(-10d);\r
         assertEquals(1000, p.getXmlObject().getPPr().getLnSpc().getSpcPts().getVal());\r
         assertFalse(p.getXmlObject().getPPr().getLnSpc().isSetSpcPct());\r
 \r
index 0998ee8e7290e056420ef3ded4d0a3ecac67f092..37a01a73ae3ce2eda197fbb500abe7d0606464e1 100644 (file)
@@ -92,7 +92,7 @@ public class TestXSLFTextParagraph {
         assertEquals(expectedWidth, dtp.getWrappingWidth(true, null), 0);\r
         assertEquals(expectedWidth, dtp.getWrappingWidth(false, null), 0);\r
 \r
-        p.setLeftMargin(36); // 0.5"\r
+        p.setLeftMargin(36d); // 0.5"\r
         leftMargin = p.getLeftMargin();\r
         assertEquals(36.0, leftMargin, 0);\r
         expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;\r
index c9c911ccf9b627bcbe4a1f84629e2473ef766fa0..904feeedf8ded5475815627bbcc6b35f0be3a14f 100644 (file)
@@ -21,12 +21,13 @@ package org.apache.poi.hslf.model.textproperties;
  * Definition for the font alignment property.
  */
 public class FontAlignmentProp extends TextProp {
+    public static final String NAME = "fontAlign";
        public static final int BASELINE = 0;
        public static final int TOP = 1;
        public static final int CENTER = 2;
        public static final int BOTTOM = 3;
 
        public FontAlignmentProp() {
-               super(2, 0x10000, "fontAlign");
+               super(2, 0x10000, NAME);
        }
 }
\ No newline at end of file
index 17d0c1d2c5ce46ccf42517876bc0faba7415df58..84cfe9940fc35c012354ef3548d634ef98e9d568 100644 (file)
@@ -31,47 +31,10 @@ import org.apache.poi.util.LittleEndian;
  *  properties, and the indent level if required.
  */
 public class TextPropCollection {
-    /*
-    private static TextProp paragraphSpecialPropTypes[] = {
-        new ParagraphFlagsTextProp(),
-        new TextProp(2, 0x80, "bullet.char"),
-        new TextProp(2, 0x10, "bullet.font"),
-        new TextProp(2, 0x40, "bullet.size"),
-        new TextProp(4, 0x20, "bullet.color"),
-        new TextProp(2, 0xD00, "alignment"),
-        new TextProp(2, 0x1000, "linespacing"),
-        new TextProp(2, 0x2000, "spacebefore"),
-        new TextProp(2, 0x4000, "spaceafter"),
-        new TextProp(2, 0x8000, "text.offset"),
-        new TextProp(2, 0x10000, "bullet.offset"),
-        new TextProp(2, 0x20000, "defaulttab"),
-        new TextProp(2, 0x40000, "para_unknown_2"),
-        new TextProp(2, 0x80000, "para_unknown_3"),
-        new TextProp(2, 0x100000, "para_unknown_4"),
-        new TextProp(2, 0x200000, "para_unknown_5")
-    };
-
-    private static TextProp characterSpecialPropTypes[] = {
-        new CharFlagsTextProp(),
-        new TextProp(2, 0x10000, "font.index"),
-        new TextProp(2, 0x20000, "char_unknown_1"),
-        new TextProp(4, 0x40000, "char_unknown_2"),
-        new TextProp(2, 0x80000, "font.size"),
-        new TextProp(2, 0x100000, "char_unknown_3"),
-        new TextProp(4, 0x200000, "font.color"),
-        new TextProp(2, 0x800000, "char_unknown_4")
-    };
-*/    
-
-    
     /** All the different kinds of paragraph properties we might handle */
     public static final TextProp[] paragraphTextPropTypes = {
         // TextProp order is according to 2.9.20 TextPFException,
         // bitmask order can be different
-//        new TextProp(0, 0x1, "hasBullet"),
-//        new TextProp(0, 0x2, "hasBulletFont"),
-//        new TextProp(0, 0x4, "hasBulletColor"),
-//        new TextProp(0, 0x8, "hasBulletSize"),
         new ParagraphFlagsTextProp(),
         new TextProp(2, 0x80, "bullet.char"),
         new TextProp(2, 0x10, "bullet.font"),
@@ -95,24 +58,9 @@ public class TextPropCollection {
         new TextProp(0, 0x2000000, "hasBulletScheme"), // TODO: check size
         // 0xFC000000 MUST be zero and MUST be ignored
     };
+    
     /** All the different kinds of character properties we might handle */
     public static final TextProp[] characterTextPropTypes = new TextProp[] {
-//        new TextProp(0, 0x1, "bold"),
-//        new TextProp(0, 0x2, "italic"),
-//        new TextProp(0, 0x4, "underline"),
-//        new TextProp(0, 0x8, "unused1"),
-//        new TextProp(0, 0x10, "shadow"),
-//        new TextProp(0, 0x20, "fehint"),
-//        new TextProp(0, 0x40, "unused2"),
-//        new TextProp(0, 0x80, "kumi"),
-//        new TextProp(0, 0x100, "strikethrough"),
-//        new TextProp(0, 0x200, "emboss"),
-//        new TextProp(0, 0x400, "nibble1"),
-//        new TextProp(0, 0x800, "nibble2"),
-//        new TextProp(0, 0x1000, "nibble3"),
-//        new TextProp(0, 0x2000, "nibble4"),
-//        new TextProp(0, 0x4000, "unused4"),
-//        new TextProp(0, 0x8000, "unused5"),
         new TextProp(0, 0x100000, "pp10ext"),
         new TextProp(0, 0x1000000, "newAsian.font.index"), // A bit that specifies whether the newEAFontRef field of the TextCFException10 structure that contains this CFMasks exists.
         new TextProp(0, 0x2000000, "cs.font.index"), // A bit that specifies whether the csFontRef field of the TextCFException10 structure that contains this CFMasks exists.
@@ -166,6 +114,19 @@ public class TextPropCollection {
                }
                return null;
        }
+
+       public TextProp removeByName(String name) {
+           Iterator<TextProp> iter = textPropList.iterator();
+           TextProp tp = null;
+           while (iter.hasNext()) {
+               tp = iter.next();
+               if (tp.getName().equals(name)){
+                   iter.remove();
+                   break;
+               }
+           }
+           return tp;
+       }
        
        /** Add the TextProp with this name to the list */
        public TextProp addWithName(String name) {
@@ -192,6 +153,10 @@ public class TextPropCollection {
                return textProp;
        }
 
+       public TextPropType getTextPropType() {
+           return textPropType;
+       }
+       
        private TextProp[] getPotentialProperties() {
            return (textPropType == TextPropType.paragraph) ? paragraphTextPropTypes : characterTextPropTypes;
        }
index 0f7f05148b1a034725d8dd5ba5352643b5703078..b90c698dd66116ddd89917890f55765d55e98bf7 100644 (file)
@@ -17,6 +17,7 @@
 
 package org.apache.poi.hslf.record;
 
+import org.apache.poi.hslf.model.PPFont;
 import org.apache.poi.util.POILogger;
 
 import java.io.*;
@@ -75,9 +76,9 @@ public final class FontCollection extends RecordContainer {
      */
     public int addFont(String name) {
         int idx = getFontIndex(name);
-        if(idx != -1) return idx;
+        if (idx != -1) return idx;
 
-        return addFont(name, 0, 0, 4, 34);
+        return addFont(name, 0, 0, 4, PPFont.FF_SWISS | PPFont.VARIABLE_PITCH);
     }
 
     public int addFont(String name, int charset, int flags, int type, int pitch) {
index a6dfedf073849d5faa3734894651647968e3d2a4..346dab74502231e9bcf2a38f577788ff44e90a49 100644 (file)
@@ -76,45 +76,31 @@ public final class HSLFSlideMaster extends HSLFMasterSheet {
      * This is the "workhorse" which returns the default style attrubutes.
      */
     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 = level; i >= 0; i--) {
-            List<TextPropCollection> styles =
-                    isCharacter ? _txmaster[txtype].getCharacterStyles() : _txmaster[txtype].getParagraphStyles();
-            if (i < styles.size()) prop = styles.get(i).findByName(name);
-            if (prop != null) break;
+        for (int i = Math.min(level, styles.size()-1); prop == null && i >= 0; i--) {
+            prop = styles.get(i).findByName(name);
         }
-        if (prop == null) {
-            if(isCharacter) {
-                switch (txtype) {
-                    case TextHeaderAtom.CENTRE_BODY_TYPE:
-                    case TextHeaderAtom.HALF_BODY_TYPE:
-                    case TextHeaderAtom.QUARTER_BODY_TYPE:
-                        txtype = TextHeaderAtom.BODY_TYPE;
-                        break;
-                    case TextHeaderAtom.CENTER_TITLE_TYPE:
-                        txtype = TextHeaderAtom.TITLE_TYPE;
-                        break;
-                    default:
-                        return null;
-                }
-            } else {
-                switch (txtype) {
-                    case TextHeaderAtom.CENTRE_BODY_TYPE:
-                    case TextHeaderAtom.HALF_BODY_TYPE:
-                    case TextHeaderAtom.QUARTER_BODY_TYPE:
-                        txtype = TextHeaderAtom.BODY_TYPE;
-                        break;
-                    case TextHeaderAtom.CENTER_TITLE_TYPE:
-                        txtype = TextHeaderAtom.TITLE_TYPE;
-                        break;
-                    default:
-                        return null;
-                }
-            }
-            prop = getStyleAttribute(txtype, level, name, isCharacter);
+
+        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;
+            case TextHeaderAtom.CENTER_TITLE_TYPE:
+                txtype = TextHeaderAtom.TITLE_TYPE;
+                break;
+            default:
+                return null;
         }
-        return prop;
+
+        return getStyleAttribute(txtype, level, name, isCharacter);
     }
 
     /**
index c0d381e912b21f69fb5a57c48e07fbd397fd87d2..ee99745b5a452d9fbfbaf3c9dee90cbfba295474 100644 (file)
@@ -34,7 +34,7 @@ import org.apache.poi.util.*;
  * This class represents a run of text in a powerpoint document. That\r
  *  run could be text on a sheet, or text in a note.\r
  *  It is only a very basic class for now\r
- *\r
+ * \r
  * @author Nick Burch\r
  */\r
 \r
@@ -44,50 +44,49 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
     /**\r
      * How to align the text\r
      */\r
-    /* package */ static final int AlignLeft = 0;\r
-    /* package */ static final int AlignCenter = 1;\r
-    /* package */ static final int AlignRight = 2;\r
-    /* package */ static final int AlignJustify = 3;\r
-\r
+    /* package */static final int AlignLeft = 0;\r
+    /* package */static final int AlignCenter = 1;\r
+    /* package */static final int AlignRight = 2;\r
+    /* package */static final int AlignJustify = 3;\r
 \r
     // Note: These fields are protected to help with unit testing\r
-       //   Other classes shouldn't really go playing with them!\r
-       private final TextHeaderAtom _headerAtom;\r
-       private TextBytesAtom  _byteAtom;\r
-       private TextCharsAtom  _charAtom;\r
-       private final TextPropCollection _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph);\r
+    // Other classes shouldn't really go playing with them!\r
+    private final TextHeaderAtom _headerAtom;\r
+    private TextBytesAtom _byteAtom;\r
+    private TextCharsAtom _charAtom;\r
+    private final TextPropCollection _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph);\r
 \r
     protected TextRulerAtom _ruler;\r
-       protected List<HSLFTextRun> _runs = new ArrayList<HSLFTextRun>();\r
-       protected HSLFTextShape _parentShape;\r
+    protected final List<HSLFTextRun> _runs = new ArrayList<HSLFTextRun>();\r
+    protected HSLFTextShape _parentShape;\r
     private HSLFSheet _sheet;\r
     private int shapeId;\r
 \r
-       // private StyleTextPropAtom styleTextPropAtom;\r
-       private StyleTextProp9Atom styleTextProp9Atom;\r
+    // private StyleTextPropAtom styleTextPropAtom;\r
+    private StyleTextProp9Atom styleTextProp9Atom;\r
 \r
-       /**\r
+    /**\r
     * Constructs a Text Run from a Unicode text block.\r
     * Either a {@link TextCharsAtom} or a {@link TextBytesAtom} needs to be provided.\r
-    *\r
+     * \r
     * @param tha the TextHeaderAtom that defines what's what\r
     * @param tba the TextBytesAtom containing the text or null if {@link TextCharsAtom} is provided\r
     * @param tca the TextCharsAtom containing the text or null if {@link TextBytesAtom} is provided\r
-        */\r
+     */\r
        /* package */ HSLFTextParagraph(\r
         TextHeaderAtom tha,\r
         TextBytesAtom tba,\r
         TextCharsAtom tca\r
     ) {\r
-           if (tha == null)  {\r
-               throw new IllegalArgumentException("TextHeaderAtom must be set.");\r
-           }\r
-               _headerAtom = tha;\r
+        if (tha == null) {\r
+            throw new IllegalArgumentException("TextHeaderAtom must be set.");\r
+        }\r
+        _headerAtom = tha;\r
         _byteAtom = tba;\r
         _charAtom = tca;\r
-       }\r
+    }\r
 \r
-    /* package */ HSLFTextParagraph(HSLFTextParagraph other) {\r
+    /* package */HSLFTextParagraph(HSLFTextParagraph other) {\r
         _headerAtom = other._headerAtom;\r
         _byteAtom = other._byteAtom;\r
         _charAtom = other._charAtom;\r
@@ -98,67 +97,67 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
         _paragraphStyle.copy(other._paragraphStyle);\r
     }\r
 \r
-       public void addTextRun(HSLFTextRun run) {\r
-           _runs.add(run);\r
-       }\r
+    public void addTextRun(HSLFTextRun run) {\r
+        _runs.add(run);\r
+    }\r
 \r
-       /**\r
+    /**\r
         * Fetch the rich text runs (runs of text with the same styling) that\r
         *  are contained within this block of text\r
-        */\r
-       public List<HSLFTextRun> getTextRuns() {\r
-               return  _runs;\r
-       }\r
+     */\r
+    public List<HSLFTextRun> getTextRuns() {\r
+        return _runs;\r
+    }\r
 \r
-       public TextPropCollection getParagraphStyle() {\r
-           return _paragraphStyle;\r
-       }\r
+    public TextPropCollection getParagraphStyle() {\r
+        return _paragraphStyle;\r
+    }\r
 \r
-       public void setParagraphStyle(TextPropCollection paragraphStyle) {\r
-           _paragraphStyle.copy(paragraphStyle);\r
-       }\r
+    public void setParagraphStyle(TextPropCollection paragraphStyle) {\r
+        _paragraphStyle.copy(paragraphStyle);\r
+    }\r
 \r
-       /**\r
+    /**\r
      * Supply the Sheet we belong to, which might have an assigned SlideShow\r
      * Also passes it on to our child RichTextRuns\r
      */\r
-       public void supplySheet(HSLFSheet sheet){\r
+    public void supplySheet(HSLFSheet sheet) {\r
         this._sheet = sheet;\r
 \r
         if (_runs == null) return;\r
-        for(HSLFTextRun rt : _runs) {\r
+        for (HSLFTextRun rt : _runs) {\r
             rt.updateSheet();\r
         }\r
-       }\r
+    }\r
 \r
-    public HSLFSheet getSheet(){\r
+    public HSLFSheet getSheet() {\r
         return this._sheet;\r
     }\r
 \r
     /**\r
-     * @return  Shape ID\r
+     * @return Shape ID\r
      */\r
-    protected int getShapeId(){\r
+    protected int getShapeId() {\r
         return shapeId;\r
     }\r
 \r
     /**\r
-     *  @param id Shape ID\r
+     * @param id Shape ID\r
      */\r
-    protected void setShapeId(int id){\r
+    protected void setShapeId(int id) {\r
         shapeId = id;\r
     }\r
 \r
     /**\r
-     * @return  0-based index of the text run in the SLWT container\r
+     * @return 0-based index of the text run in the SLWT container\r
      */\r
-    protected int getIndex(){\r
+    protected int getIndex() {\r
         return (_headerAtom != null) ? _headerAtom.getIndex() : -1;\r
     }\r
 \r
     /**\r
      * Sets the index of the paragraph in the SLWT container\r
-     *\r
+     * \r
      * @param index\r
      */\r
     protected void setIndex(int index) {\r
@@ -166,10 +165,10 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
     }\r
 \r
     /**\r
-    * Returns the type of the text, from the TextHeaderAtom.\r
-    * Possible values can be seen from TextHeaderAtom\r
-    * @see org.apache.poi.hslf.record.TextHeaderAtom\r
-    */\r
+     * Returns the type of the text, from the TextHeaderAtom.\r
+     * Possible values can be seen from TextHeaderAtom\r
+     * @see org.apache.poi.hslf.record.TextHeaderAtom\r
+     */\r
     public int getRunType() {\r
         return (_headerAtom != null) ? _headerAtom.getTextType() : -1;\r
     }\r
@@ -177,8 +176,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
     public void setRunType(int runType) {\r
         if (_headerAtom != null) _headerAtom.setTextType(runType);\r
     }\r
-    \r
-    \r
+\r
     /**\r
      * Is this Text Run one from a {@link PPDrawing}, or is it\r
      *  one from the {@link SlideListWithText}?\r
@@ -187,12 +185,12 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
         return (getIndex() == -1);\r
     }\r
 \r
-    public TextRulerAtom getTextRuler(){\r
+    public TextRulerAtom getTextRuler() {\r
         return _ruler;\r
 \r
     }\r
 \r
-    public TextRulerAtom createTextRuler(){\r
+    public TextRulerAtom createTextRuler() {\r
         _ruler = getTextRuler();\r
         if (_ruler == null) {\r
             _ruler = TextRulerAtom.getParagraphInstance();\r
@@ -207,19 +205,19 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
     /**\r
      * Returns records that make up the list of text paragraphs\r
      * (there can be misc InteractiveInfo, TxInteractiveInfo and other records)\r
-     *\r
+     * \r
      * @return text run records\r
      */\r
-    public Record[] getRecords(){\r
+    public Record[] getRecords() {\r
         Record r[] = _headerAtom.getParentRecord().getChildRecords();\r
-        return getRecords(r, new int[]{0}, _headerAtom);\r
+        return getRecords(r, new int[] { 0 }, _headerAtom);\r
     }\r
 \r
     private static Record[] getRecords(Record[] records, int[] startIdx, TextHeaderAtom headerAtom) {\r
         if (records == null) {\r
             throw new NullPointerException("records need to be set.");\r
         }\r
-        \r
+\r
         for (; startIdx[0] < records.length; startIdx[0]++) {\r
             Record r = records[startIdx[0]];\r
             if (r instanceof TextHeaderAtom && (headerAtom == null || r == headerAtom)) break;\r
@@ -229,135 +227,113 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
             logger.log(POILogger.INFO, "header atom wasn't found - container might contain only an OutlineTextRefAtom");\r
             return new Record[0];\r
         }\r
-        \r
+\r
         int length;\r
-        for (length = 1; startIdx[0]+length < records.length; length++) {\r
+        for (length = 1; startIdx[0] + length < records.length; length++) {\r
             if (records[startIdx[0]+length] instanceof TextHeaderAtom) break;\r
         }\r
-        \r
+\r
         Record result[] = new Record[length];\r
         System.arraycopy(records, startIdx[0], result, 0, length);\r
         startIdx[0] += length;\r
-        \r
+\r
         return result;\r
     }\r
-    \r
-    /** Numbered List info */\r
-       public void setStyleTextProp9Atom(final StyleTextProp9Atom styleTextProp9Atom) {\r
-               this.styleTextProp9Atom = styleTextProp9Atom;\r
-       }\r
 \r
     /** Numbered List info */\r
-       public StyleTextProp9Atom getStyleTextProp9Atom() {\r
-               return this.styleTextProp9Atom;\r
-       }\r
-\r
-       /**\r
-     * Fetch the value of the given Paragraph related TextProp.\r
-     * Returns -1 if that TextProp isn't present.\r
-     * If the TextProp isn't present, the value from the appropriate\r
-     *  Master Sheet will apply.\r
-     */\r
-    private int getParaTextPropVal(String propName) {\r
-        TextProp prop = _paragraphStyle.findByName(propName);\r
-        BitMaskTextProp maskProp = (BitMaskTextProp)_paragraphStyle.findByName(ParagraphFlagsTextProp.NAME);\r
-        boolean hardAttribute = (maskProp != null && maskProp.getValue() == 0);\r
-        if (prop == null && !hardAttribute){\r
-            HSLFSheet sheet = getSheet();\r
-            int txtype = getRunType();\r
-            HSLFMasterSheet master = sheet.getMasterSheet();\r
-            if (master != null)\r
-                prop = master.getStyleAttribute(txtype, getIndentLevel(), propName, false);\r
-        }\r
-\r
-        return prop == null ? -1 : prop.getValue();\r
+       public void setStyleTextProp9Atom(final StyleTextProp9Atom styleTextProp9Atom) {\r
+        this.styleTextProp9Atom = styleTextProp9Atom;\r
     }\r
 \r
-    /**\r
-     * Sets the value of the given Character TextProp, add if required\r
-     * @param propName The name of the Character TextProp\r
-     * @param val The value to set for the TextProp\r
-     */\r
-    public void setParaTextPropVal(String propName, int val) {\r
-        // Ensure we have the StyleTextProp atom we're going to need\r
-        assert(_paragraphStyle!=null);\r
-        TextProp tp = fetchOrAddTextProp(_paragraphStyle, propName);\r
-        tp.setValue(val);\r
+    /** Numbered List info */\r
+    public StyleTextProp9Atom getStyleTextProp9Atom() {\r
+        return this.styleTextProp9Atom;\r
     }\r
-\r
+    \r
     @Override\r
     public Iterator<HSLFTextRun> iterator() {\r
         return _runs.iterator();\r
     }\r
 \r
     @Override\r
-    public double getLeftMargin() {\r
-        int val = getParaTextPropVal("text.offset");\r
-        return val*HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI);\r
+    public Double getLeftMargin() {\r
+        TextProp val = getPropVal(_paragraphStyle, "text.offset", this);\r
+        return (val == null) ? null : Units.masterToPoints(val.getValue());\r
     }\r
 \r
     @Override\r
-    public void setLeftMargin(double leftMargin) {\r
-        int val = (int)(leftMargin*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI);\r
-        setParaTextPropVal("text.offset", val);\r
+    public void setLeftMargin(Double leftMargin) {\r
+        Integer val = (leftMargin == null) ? null : Units.pointsToMaster(leftMargin);\r
+        setPropVal(_paragraphStyle, "text.offset", val);\r
     }\r
 \r
     @Override\r
-    public double getRightMargin() {\r
+    public Double getRightMargin() {\r
         // TODO: find out, how to determine this value\r
-        return 0;\r
+        return null;\r
     }\r
 \r
     @Override\r
-    public void setRightMargin(double rightMargin) {\r
+    public void setRightMargin(Double rightMargin) {\r
         // TODO: find out, how to set this value\r
     }\r
 \r
     @Override\r
-    public double getIndent() {\r
-        int val = getParaTextPropVal("bullet.offset");\r
-        return val*HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI);\r
+    public Double getIndent() {\r
+        TextProp val = getPropVal(_paragraphStyle, "bullet.offset", this);\r
+        return (val == null) ? null : Units.masterToPoints(val.getValue());\r
     }\r
 \r
     @Override\r
-    public void setIndent(double intent) {\r
-        int val = (int)(intent*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI);\r
-        setParaTextPropVal("bullet.offset", val);\r
+    public void setIndent(Double indent) {\r
+        Integer val = (indent == null) ? null : Units.pointsToMaster(indent);\r
+        setPropVal(_paragraphStyle, "bullet.offset", val);\r
     }\r
 \r
     @Override\r
     public String getDefaultFontFamily() {\r
-        return (_runs.isEmpty() ? "Arial" : _runs.get(0).getFontFamily());\r
+        String typeface = null;\r
+        if (!_runs.isEmpty()) {\r
+            typeface = _runs.get(0).getFontFamily();\r
+        }\r
+        return (typeface != null) ? typeface : "Arial";\r
     }\r
 \r
     @Override\r
-    public double getDefaultFontSize() {\r
-        return (_runs.isEmpty() ? 12 : _runs.get(0).getFontSize());\r
+    public Double getDefaultFontSize() {\r
+        Double d = null;\r
+        if (!_runs.isEmpty()) {\r
+            d = _runs.get(0).getFontSize();\r
+        }\r
+        \r
+        return (d != null) ? d : 12d;\r
     }\r
 \r
     /**\r
      * Sets the type of horizontal alignment for the paragraph.\r
-     *\r
+     * \r
      * @param align - the type of alignment\r
      */\r
     public void setAlignment(org.apache.poi.sl.usermodel.TextParagraph.TextAlign align) {\r
-        int alignInt;\r
-        switch (align) {\r
-        default:\r
-        case LEFT: alignInt = TextAlignmentProp.LEFT; break;\r
-        case CENTER: alignInt = TextAlignmentProp.CENTER; break;\r
-        case RIGHT: alignInt = TextAlignmentProp.RIGHT; break;\r
-        case DIST: alignInt = TextAlignmentProp.DISTRIBUTED; break;\r
-        case JUSTIFY: alignInt = TextAlignmentProp.JUSTIFY; break;\r
-        case JUSTIFY_LOW: alignInt = TextAlignmentProp.JUSTIFYLOW; break;\r
-        case THAI_DIST: alignInt = TextAlignmentProp.THAIDISTRIBUTED; break;\r
+        Integer alignInt = null;\r
+        if (align != null) switch (align) {\r
+            default:\r
+            case LEFT: alignInt = TextAlignmentProp.LEFT;break;\r
+            case CENTER: alignInt = TextAlignmentProp.CENTER; break;\r
+            case RIGHT: alignInt = TextAlignmentProp.RIGHT; break;\r
+            case DIST: alignInt = TextAlignmentProp.DISTRIBUTED; break;\r
+            case JUSTIFY: alignInt = TextAlignmentProp.JUSTIFY; break;\r
+            case JUSTIFY_LOW: alignInt = TextAlignmentProp.JUSTIFYLOW; break;\r
+            case THAI_DIST: alignInt = TextAlignmentProp.THAIDISTRIBUTED; break;\r
         }\r
-        setParaTextPropVal("alignment", alignInt);\r
+        setPropVal(_paragraphStyle, "alignment", alignInt);\r
     }\r
 \r
     @Override\r
     public org.apache.poi.sl.usermodel.TextParagraph.TextAlign getTextAlign() {\r
-        switch (getParaTextPropVal("alignment")) {\r
+        TextProp tp = getPropVal(_paragraphStyle, "alignment", this);\r
+        if (tp == null) return null;\r
+        switch (tp.getValue()) {\r
             default:\r
             case TextAlignmentProp.LEFT: return TextAlign.LEFT;\r
             case TextAlignmentProp.CENTER: return TextAlign.CENTER;\r
@@ -371,34 +347,33 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
 \r
     @Override\r
     public FontAlign getFontAlign() {\r
-        switch(getParaTextPropVal("fontAlign")) {\r
-            default:\r
-            case -1: return FontAlign.AUTO;\r
+        TextProp tp = getPropVal(_paragraphStyle, FontAlignmentProp.NAME, this);\r
+        if (tp == null) return null;\r
+        \r
+        switch (tp.getValue()) {\r
             case FontAlignmentProp.BASELINE: return FontAlign.BASELINE;\r
             case FontAlignmentProp.TOP: return FontAlign.TOP;\r
             case FontAlignmentProp.CENTER: return FontAlign.CENTER;\r
             case FontAlignmentProp.BOTTOM: return FontAlign.BOTTOM;\r
+            default: return FontAlign.AUTO;\r
         }\r
     }\r
 \r
     @Override\r
     public BulletStyle getBulletStyle() {\r
-        if (getBulletChar() == 0) return null;\r
+        if (!isBullet()) return null;\r
 \r
         return new BulletStyle() {\r
             public String getBulletCharacter() {\r
-                char chr =  HSLFTextParagraph.this.getBulletChar();\r
-                return (chr == 0 ? "" : ""+chr);\r
+                Character chr = HSLFTextParagraph.this.getBulletChar();\r
+                return (chr == null || chr == 0) ? "" : "" + chr;\r
             }\r
 \r
             public String getBulletFont() {\r
-                int fontIdx = HSLFTextParagraph.this.getBulletFont();\r
-                if (fontIdx == -1) return getDefaultFontFamily();\r
-                PPFont ppFont = getSheet().getSlideShow().getFont(fontIdx);\r
-                return ppFont.getFontName();\r
+                return HSLFTextParagraph.this.getBulletFont();\r
             }\r
 \r
-            public double getBulletFontSize() {\r
+            public Double getBulletFontSize() {\r
                 return HSLFTextParagraph.this.getBulletSize();\r
             }\r
 \r
@@ -417,666 +392,669 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
         _parentShape = parentShape;\r
     }\r
 \r
+    /**\r
+     * \r
+     * @return indentation level\r
+     */\r
+    public int getIndentLevel() {\r
+        return _paragraphStyle == null ? 0 : _paragraphStyle.getIndentLevel();\r
+    }\r
 \r
     /**\r
-    *\r
-    * @return indentation level\r
-    */\r
-   public int getIndentLevel() {\r
-       return _paragraphStyle == null ? 0 : _paragraphStyle.getIndentLevel();\r
-   }\r
-\r
-   /**\r
-    * Sets indentation level\r
-    *\r
+     * Sets indentation level\r
+     * \r
     * @param level indentation level. Must be in the range [0, 4]\r
-    */\r
-   public void setIndentLevel(int level) {\r
+     */\r
+    public void setIndentLevel(int level) {\r
        if( _paragraphStyle != null ) _paragraphStyle.setIndentLevel((short)level);\r
-   }\r
-\r
-   /**\r
-    * Sets whether this rich text run has bullets\r
-    */\r
-   public void setBullet(boolean flag) {\r
-       setFlag(ParagraphFlagsTextProp.BULLET_IDX, flag);\r
-   }\r
-\r
-   /**\r
-    * Returns whether this rich text run has bullets\r
-    */\r
-   public boolean isBullet() {\r
-       return getFlag(ParagraphFlagsTextProp.BULLET_IDX);\r
-   }\r
-\r
-   /**\r
-    * Returns whether this rich text run has bullets\r
-    */\r
-   public boolean isBulletHard() {\r
-       return getFlag(ParagraphFlagsTextProp.BULLET_IDX);\r
-   }\r
-\r
-   /**\r
-    * Sets the bullet character\r
-    */\r
-   public void setBulletChar(char c) {\r
-       setParaTextPropVal("bullet.char", c);\r
-   }\r
-\r
-   /**\r
-    * Returns the bullet character\r
-    */\r
-   public char getBulletChar() {\r
-       int val = getParaTextPropVal("bullet.char");\r
-       return (char)(val == -1 ? 0 : val);\r
-   }\r
-\r
-   /**\r
-    * Sets the bullet size\r
-    */\r
-   public void setBulletSize(int size) {\r
-       setParaTextPropVal("bullet.size", size);\r
-   }\r
-\r
-   /**\r
-    * Returns the bullet size\r
-    */\r
-   public int getBulletSize() {\r
-       return getParaTextPropVal("bullet.size");\r
-   }\r
-\r
-   /**\r
-    * Sets the bullet color\r
-    */\r
-   public void setBulletColor(Color color) {\r
-       int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 254).getRGB();\r
-       setParaTextPropVal("bullet.color", rgb);\r
-   }\r
-\r
-   /**\r
-    * Returns the bullet color\r
-    */\r
-   public Color getBulletColor() {\r
-       int rgb = getParaTextPropVal("bullet.color");\r
-       if (rgb == -1) {\r
-           // if bullet color is undefined, return color of first run\r
-           if (_runs.isEmpty()) return null;\r
-           return _runs.get(0).getFontColor();\r
-       }\r
-\r
-       int cidx = rgb >> 24;\r
-       if (rgb % 0x1000000 == 0){\r
-           if (_sheet == null) return null;\r
-           ColorSchemeAtom ca = _sheet.getColorScheme();\r
-           if(cidx >= 0 && cidx <= 7) rgb = ca.getColor(cidx);\r
-       }\r
-       Color tmp = new Color(rgb, true);\r
-       return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed());\r
-   }\r
-\r
-   /**\r
-    * Sets the bullet font\r
-    */\r
-   public void setBulletFont(int idx) {\r
-       setParaTextPropVal("bullet.font", idx);\r
-       setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true);\r
-   }\r
-\r
-   /**\r
-    * Returns the bullet font\r
-    */\r
-   public int getBulletFont() {\r
-       return getParaTextPropVal("bullet.font");\r
-   }\r
-\r
-   @Override\r
-   public void setLineSpacing(double lineSpacing) {\r
-       // if lineSpacing < 0, we need to convert points (common interface) to master units (hslf)\r
-       if (lineSpacing < 0) {\r
-           lineSpacing = (lineSpacing*HSLFShape.MASTER_DPI/HSLFShape.POINT_DPI);\r
-       }\r
-       setParaTextPropVal("linespacing", (int)lineSpacing);\r
-   }\r
-\r
-   @Override\r
-   public double getLineSpacing() {\r
-       double val = getParaTextPropVal("linespacing");\r
-       // if lineSpacing < 0, we need to convert master units (hslf) to points (common interface)\r
-       if (val == -1) return 0;\r
-       if (val < -1) val *= HSLFShape.POINT_DPI/((double)HSLFShape.MASTER_DPI);\r
-       return val;\r
-   }\r
-\r
-   /**\r
-    * Sets spacing before a paragraph.\r
-    * <p>\r
-    * If spacebefore >= 0, then spacebefore is a percentage of normal line height.\r
-    * If spacebefore < 0, the absolute value of spacebefore is the spacing in master coordinates.\r
-    * </p>\r
-    */\r
-   public void setSpaceBefore(int val) {\r
-       setParaTextPropVal("spacebefore", val);\r
-   }\r
-\r
-   /**\r
-    * Returns spacing before a paragraph\r
-    * <p>\r
-    * If spacebefore >= 0, then spacebefore is a percentage of normal line height.\r
-    * If spacebefore < 0, the absolute value of spacebefore is the spacing in master coordinates.\r
-    * </p>\r
-    *\r
-    * @return the spacing before a paragraph\r
-    */\r
-   @Override\r
-   public double getSpaceBefore() {\r
-       int val = getParaTextPropVal("spacebefore");\r
-       return val == -1 ? 0 : val;\r
-   }\r
-\r
-   /**\r
-    * Sets spacing after a paragraph.\r
-    * <p>\r
-    * If spaceafter >= 0, then spaceafter is a percentage of normal line height.\r
-    * If spaceafter < 0, the absolute value of spaceafter is the spacing in master coordinates.\r
-    * </p>\r
-    */\r
-   public void setSpaceAfter(int val) {\r
-       setParaTextPropVal("spaceafter", val);\r
-   }\r
-\r
-   /**\r
-    * Returns spacing after a paragraph\r
-    * <p>\r
-    * If spaceafter >= 0, then spaceafter is a percentage of normal line height.\r
-    * If spaceafter < 0, the absolute value of spaceafter is the spacing in master coordinates.\r
-    * </p>\r
-    *\r
-    * @return the spacing before a paragraph\r
-    */\r
-   @Override\r
-   public double getSpaceAfter() {\r
-       int val = getParaTextPropVal("spaceafter");\r
-       return val == -1 ? 0 : val;\r
-   }\r
-\r
-   /**\r
-    * Returns the named TextProp, either by fetching it (if it exists) or adding it\r
-    *  (if it didn't)\r
-    * @param textPropCol The TextPropCollection to fetch from / add into\r
-    * @param textPropName The name of the TextProp to fetch/add\r
-    */\r
-    protected static TextProp fetchOrAddTextProp(TextPropCollection textPropCol, String textPropName) {\r
+    }\r
+\r
+    /**\r
+     * Sets whether this rich text run has bullets\r
+     */\r
+    public void setBullet(boolean flag) {\r
+        setFlag(ParagraphFlagsTextProp.BULLET_IDX, flag);\r
+    }\r
+\r
+    /**\r
+     * Returns whether this rich text run has bullets\r
+     */\r
+    public boolean isBullet() {\r
+        return getFlag(ParagraphFlagsTextProp.BULLET_IDX);\r
+    }\r
+\r
+    /**\r
+     * Sets the bullet character\r
+     */\r
+    public void setBulletChar(Character c) {\r
+        Integer val = (c == null) ? null : (int)c.charValue();\r
+        setPropVal(_paragraphStyle, "bullet.char", val);\r
+    }\r
+\r
+    /**\r
+     * Returns the bullet character\r
+     */\r
+    public Character getBulletChar() {\r
+        TextProp tp = getPropVal(_paragraphStyle, "bullet.char", this);\r
+        return (tp == null) ? null : (char)tp.getValue();\r
+    }\r
+\r
+    /**\r
+     * Sets the bullet size\r
+     */\r
+    public void setBulletSize(Double size) {\r
+        setPctOrPoints("bullet.size", size);\r
+    }\r
+\r
+    /**\r
+     * Returns the bullet size, null if unset\r
+     */\r
+    public Double getBulletSize() {\r
+        return getPctOrPoints("bullet.size");\r
+    }\r
+\r
+    /**\r
+     * Sets the bullet color\r
+     */\r
+    public void setBulletColor(Color color) {\r
+        Integer val = (color == null) ? null : new Color(color.getBlue(), color.getGreen(), color.getRed(), 254).getRGB();\r
+        setPropVal(_paragraphStyle, "bullet.color", val);\r
+    }\r
+\r
+    /**\r
+     * Returns the bullet color\r
+     */\r
+    public Color getBulletColor() {\r
+        TextProp tp = getPropVal(_paragraphStyle, "bullet.color", this);\r
+        if (tp == null) {\r
+            // if bullet color is undefined, return color of first run\r
+            return (_runs.isEmpty()) ? null : _runs.get(0).getFontColor();\r
+        }\r
+\r
+        int rgb = tp.getValue();\r
+        int cidx = rgb >> 24;\r
+        if (rgb % 0x1000000 == 0) {\r
+            if (_sheet == null)\r
+                return null;\r
+            ColorSchemeAtom ca = _sheet.getColorScheme();\r
+            if (cidx >= 0 && cidx <= 7)\r
+                rgb = ca.getColor(cidx);\r
+        }\r
+        Color tmp = new Color(rgb, true);\r
+        return new Color(tmp.getBlue(), tmp.getGreen(), tmp.getRed());\r
+    }\r
+\r
+    /**\r
+     * Sets the bullet font\r
+     */\r
+    public void setBulletFont(String typeface) {\r
+        if (typeface == null) {\r
+            setPropVal(_paragraphStyle, "bullet.font", null);\r
+            setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, false);\r
+        }\r
+\r
+        FontCollection fc = getSheet().getSlideShow().getFontCollection();\r
+        int idx = fc.addFont(typeface);\r
+\r
+        setPropVal(_paragraphStyle, "bullet.font", idx);\r
+        setFlag(ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true);\r
+    }\r
+\r
+    /**\r
+     * Returns the bullet font\r
+     */\r
+    public String getBulletFont() {\r
+        TextProp tp = getPropVal(_paragraphStyle, "bullet.font", this);\r
+        if (tp == null) return getDefaultFontFamily();\r
+        PPFont ppFont = getSheet().getSlideShow().getFont(tp.getValue());\r
+        assert(ppFont != null);\r
+        return ppFont.getFontName();\r
+    }\r
+\r
+    @Override\r
+    public void setLineSpacing(Double lineSpacing) {\r
+        setPctOrPoints("linespacing", lineSpacing);\r
+    }\r
+\r
+    @Override\r
+    public Double getLineSpacing() {\r
+        return getPctOrPoints("linespacing");\r
+    }\r
+\r
+    @Override\r
+    public void setSpaceBefore(Double spaceBefore) {\r
+        setPctOrPoints("spacebefore", spaceBefore);\r
+    }\r
+\r
+    @Override\r
+    public Double getSpaceBefore() {\r
+        return getPctOrPoints("spacebefore");\r
+    }\r
+\r
+    @Override\r
+    public void setSpaceAfter(Double spaceAfter) {\r
+        setPctOrPoints("spaceafter", spaceAfter);\r
+    }\r
+\r
+    @Override\r
+    public Double getSpaceAfter() {\r
+        return getPctOrPoints("spaceafter");\r
+    }\r
+\r
+    @Override\r
+    public Double getDefaultTabSize() {\r
+        // TODO: implement\r
+        return null;\r
+    }\r
+    \r
+    private Double getPctOrPoints(String propName) {\r
+        TextProp tp = getPropVal(_paragraphStyle, propName, this);\r
+        if (tp == null) return null;\r
+        int val = tp.getValue();\r
+        return (val < 0) ? Units.masterToPoints(val) : val;\r
+    }\r
+\r
+    private void setPctOrPoints(String propName, Double dval) {\r
+        Integer ival = null;\r
+        if (dval != null) {\r
+            ival = (dval < 0) ? Units.pointsToMaster(dval) : dval.intValue();\r
+        }\r
+        setPropVal(_paragraphStyle, propName, ival);\r
+    }\r
+    \r
+    private boolean getFlag(int index) {\r
+        BitMaskTextProp tp = (BitMaskTextProp)getPropVal(_paragraphStyle, ParagraphFlagsTextProp.NAME, this);\r
+        return (tp == null) ? false : tp.getSubValue(index);\r
+    }\r
+\r
+    private void setFlag(int index, boolean value) {\r
+        BitMaskTextProp tp = (BitMaskTextProp)_paragraphStyle.addWithName(ParagraphFlagsTextProp.NAME);\r
+        tp.setSubValue(value, index);\r
+    }\r
+\r
+    /**\r
+     * Fetch the value of the given Paragraph related TextProp. Returns null if\r
+     * that TextProp isn't present. If the TextProp isn't present, the value\r
+     * from the appropriate Master Sheet will apply.\r
+     */\r
+    protected static TextProp getPropVal(TextPropCollection props, String propName, HSLFTextParagraph paragraph) {\r
+        TextProp prop = props.findByName(propName);\r
+        if (prop != null) return prop;\r
+\r
+        BitMaskTextProp maskProp = (BitMaskTextProp) props.findByName(ParagraphFlagsTextProp.NAME);\r
+        boolean hardAttribute = (maskProp != null && maskProp.getValue() == 0);\r
+        if (hardAttribute) return null;\r
+\r
+        HSLFSheet sheet = paragraph.getSheet();\r
+        int txtype = paragraph.getRunType();\r
+        HSLFMasterSheet master = sheet.getMasterSheet();\r
+        if (master == null) {\r
+            logger.log(POILogger.WARN, "MasterSheet is not available");\r
+            return null;\r
+        }\r
+\r
+        boolean isChar = props.getTextPropType() == TextPropType.character;\r
+        return master.getStyleAttribute(txtype, paragraph.getIndentLevel(), propName, isChar);\r
+    }\r
+\r
+    /**\r
+     * Returns the named TextProp, either by fetching it (if it exists) or\r
+     * adding it (if it didn't)\r
+     * \r
+     * @param props the TextPropCollection to fetch from / add into\r
+     * @param name the name of the TextProp to fetch/add\r
+     * @param val the value, null if unset\r
+     */\r
+    protected static void setPropVal(TextPropCollection props, String name, Integer val) {\r
+        if (val == null) {\r
+            props.removeByName(name);\r
+            return;\r
+        }\r
+        \r
         // Fetch / Add the TextProp\r
-        return textPropCol.addWithName(textPropName);\r
+        TextProp tp = props.addWithName(name);\r
+        tp.setValue(val);\r
+    }\r
+    \r
+    /**\r
+     * Check and add linebreaks to text runs leading other paragraphs\r
+     * \r
+     * @param paragraphs\r
+     */\r
+    protected static void fixLineEndings(List<HSLFTextParagraph> paragraphs) {\r
+        HSLFTextRun lastRun = null;\r
+        for (HSLFTextParagraph p : paragraphs) {\r
+            if (lastRun != null && !lastRun.getRawText().endsWith("\r")) {\r
+                lastRun.setText(lastRun.getRawText() + "\r");\r
+            }\r
+            List<HSLFTextRun> ltr = p.getTextRuns();\r
+            if (ltr.isEmpty()) {\r
+                throw new RuntimeException("paragraph without textruns found");\r
+            }\r
+            lastRun = ltr.get(ltr.size() - 1);\r
+            assert (lastRun.getRawText() != null);\r
+        }\r
     }\r
 \r
-    protected boolean getFlag(int index) {\r
-        if (_paragraphStyle == null) return false;\r
+    /**\r
+     * Search for a StyleTextPropAtom is for this text header (list of paragraphs)\r
+     * \r
+     * @param header the header\r
+     * @param textLen the length of the rawtext, or -1 if the length is not known\r
+     */\r
+    private static StyleTextPropAtom findStyleAtomPresent(TextHeaderAtom header, int textLen) {\r
+        boolean afterHeader = false;\r
+        StyleTextPropAtom style = null;\r
+        for (Record record : header.getParentRecord().getChildRecords()) {\r
+            long rt = record.getRecordType();\r
+            if (afterHeader && rt == RecordTypes.TextHeaderAtom.typeID) {\r
+                // already on the next header, quit searching\r
+                break;\r
+            }\r
+            afterHeader |= (header == record);\r
+            if (afterHeader && rt == RecordTypes.StyleTextPropAtom.typeID) {\r
+                // found it\r
+                style = (StyleTextPropAtom) record;\r
+            }\r
+        }\r
+\r
+        if (style == null) {\r
+            logger.log(POILogger.INFO, "styles atom doesn't exist. Creating dummy record for later saving.");\r
+            style = new StyleTextPropAtom((textLen < 0) ? 1 : textLen);\r
+        } else {\r
+            if (textLen >= 0) {\r
+                style.setParentTextSize(textLen);\r
+            }\r
+        }\r
 \r
-        BitMaskTextProp prop = (BitMaskTextProp) _paragraphStyle.findByName(ParagraphFlagsTextProp.NAME);\r
+        return style;\r
+    }\r
 \r
-        if (prop == null) {\r
-            if (_sheet != null) {\r
-                int txtype = getRunType();\r
-                HSLFMasterSheet master = _sheet.getMasterSheet();\r
-                if (master != null) {\r
-                    prop = (BitMaskTextProp) master.getStyleAttribute(txtype, getIndentLevel(), ParagraphFlagsTextProp.NAME, false);\r
-                }\r
+    /**\r
+     * Saves the modified paragraphs/textrun to the records.\r
+     * Also updates the styles to the correct text length.\r
+     */\r
+    protected static void storeText(List<HSLFTextParagraph> paragraphs) {\r
+        fixLineEndings(paragraphs);\r
+\r
+        String rawText = toInternalString(getRawText(paragraphs));\r
+\r
+        // Will it fit in a 8 bit atom?\r
+        boolean isUnicode = StringUtil.hasMultibyte(rawText);\r
+        // isUnicode = true;\r
+\r
+        TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;\r
+        TextBytesAtom byteAtom = paragraphs.get(0)._byteAtom;\r
+        TextCharsAtom charAtom = paragraphs.get(0)._charAtom;\r
+        StyleTextPropAtom styleAtom = findStyleAtomPresent(headerAtom, rawText.length());\r
+\r
+        // Store in the appropriate record\r
+        Record oldRecord = null, newRecord = null;\r
+        if (isUnicode) {\r
+            if (byteAtom != null || charAtom == null) {\r
+                oldRecord = byteAtom;\r
+                charAtom = new TextCharsAtom();\r
+            }\r
+            newRecord = charAtom;\r
+            charAtom.setText(rawText);\r
+        } else {\r
+            if (charAtom != null || byteAtom == null) {\r
+                oldRecord = charAtom;\r
+                byteAtom = new TextBytesAtom();\r
+            }\r
+            newRecord = byteAtom;\r
+            byte[] byteText = new byte[rawText.length()];\r
+            StringUtil.putCompressedUnicode(rawText, byteText, 0);\r
+            byteAtom.setText(byteText);\r
+        }\r
+        assert (newRecord != null);\r
+\r
+        RecordContainer _txtbox = headerAtom.getParentRecord();\r
+        Record[] cr = _txtbox.getChildRecords();\r
+        int headerIdx = -1, textIdx = -1, styleIdx = -1;\r
+        for (int i = 0; i < cr.length; i++) {\r
+            Record r = cr[i];\r
+            if (r == headerAtom) headerIdx = i;\r
+            else if (r == oldRecord || r == newRecord) textIdx = i;\r
+            else if (r == styleAtom) styleIdx = i;\r
+        }\r
+\r
+        if (textIdx == -1) {\r
+            // the old record was never registered, ignore it\r
+            _txtbox.addChildAfter(newRecord, headerAtom);\r
+            textIdx = headerIdx + 1;\r
+        } else {\r
+            // swap not appropriated records - noop if unchanged\r
+            cr[textIdx] = newRecord;\r
+        }\r
+\r
+        if (styleIdx == -1) {\r
+            // Add the new StyleTextPropAtom after the TextCharsAtom / TextBytesAtom\r
+            _txtbox.addChildAfter(styleAtom, newRecord);\r
+        }\r
+\r
+        for (HSLFTextParagraph p : paragraphs) {\r
+            if (newRecord == byteAtom) {\r
+                p._byteAtom = byteAtom;\r
+                p._charAtom = null;\r
             } else {\r
-                logger.log(POILogger.WARN, "MasterSheet is not available");\r
+                p._byteAtom = null;\r
+                p._charAtom = charAtom;\r
+            }\r
+        }\r
+\r
+        // Update the text length for its Paragraph and Character stylings\r
+        // * reset the length, to the new string's length\r
+        // * add on +1 if the last block\r
+\r
+        styleAtom.clearStyles();\r
+\r
+        TextPropCollection lastPTPC = null, lastRTPC = null, ptpc = null, rtpc = null;\r
+        for (HSLFTextParagraph para : paragraphs) {\r
+            ptpc = para.getParagraphStyle();\r
+            ptpc.updateTextSize(0);\r
+            if (!ptpc.equals(lastPTPC)) {\r
+                lastPTPC = styleAtom.addParagraphTextPropCollection(0);\r
+                lastPTPC.copy(ptpc);\r
+            }\r
+            for (HSLFTextRun tr : para.getTextRuns()) {\r
+                rtpc = tr.getCharacterStyle();\r
+                rtpc.updateTextSize(0);\r
+                if (!rtpc.equals(lastRTPC)) {\r
+                    lastRTPC = styleAtom.addCharacterTextPropCollection(0);\r
+                    lastRTPC.copy(rtpc);\r
+                }\r
+                int len = tr.getLength();\r
+                ptpc.updateTextSize(ptpc.getCharactersCovered() + len);\r
+                rtpc.updateTextSize(len);\r
+                lastPTPC.updateTextSize(lastPTPC.getCharactersCovered() + len);\r
+                lastRTPC.updateTextSize(lastRTPC.getCharactersCovered() + len);\r
+            }\r
+        }\r
+\r
+        assert (lastPTPC != null && lastRTPC != null && ptpc != null && rtpc != null);\r
+        ptpc.updateTextSize(ptpc.getCharactersCovered() + 1);\r
+        rtpc.updateTextSize(rtpc.getCharactersCovered() + 1);\r
+        lastPTPC.updateTextSize(lastPTPC.getCharactersCovered() + 1);\r
+        lastRTPC.updateTextSize(lastRTPC.getCharactersCovered() + 1);\r
+\r
+        /**\r
+         * If TextSpecInfoAtom is present, we must update the text size in it,\r
+         * otherwise the ppt will be corrupted\r
+         */\r
+        for (Record r : paragraphs.get(0).getRecords()) {\r
+            if (r instanceof TextSpecInfoAtom) {\r
+                ((TextSpecInfoAtom) r).setParentSize(rawText.length() + 1);\r
+                break;\r
+            }\r
+        }\r
+\r
+        if (_txtbox instanceof EscherTextboxWrapper) {\r
+            try {\r
+                ((EscherTextboxWrapper) _txtbox).writeOut(null);\r
+            } catch (IOException e) {\r
+                throw new RuntimeException("failed dummy write", e);\r
+            }\r
+        }\r
+    }\r
+\r
+    /**\r
+     * Adds the supplied text onto the end of the TextParagraphs,\r
+     * creating a new RichTextRun for it to sit in.\r
+     * \r
+     * @param text the text string used by this object.\r
+     */\r
+    protected static HSLFTextRun appendText(List<HSLFTextParagraph> paragraphs, String text, boolean newParagraph) {\r
+        text = toInternalString(text);\r
+\r
+        // check paragraphs\r
+        assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());\r
+\r
+        HSLFTextParagraph htp = paragraphs.get(paragraphs.size() - 1);\r
+        HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size() - 1);\r
+\r
+        boolean isFirst = !newParagraph;\r
+        for (String rawText : text.split("(?<=\r)")) {\r
+            if (!isFirst) {\r
+                TextPropCollection tpc = htp.getParagraphStyle();\r
+                HSLFTextParagraph prevHtp = htp;\r
+                htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom);\r
+                htp.getParagraphStyle().copy(tpc);\r
+                htp.setParentShape(prevHtp.getParentShape());\r
+                htp.setShapeId(prevHtp.getShapeId());\r
+                htp.supplySheet(prevHtp.getSheet());\r
+                paragraphs.add(htp);\r
+            }\r
+            isFirst = false;\r
+\r
+            TextPropCollection tpc = htr.getCharacterStyle();\r
+            // special case, last text run is empty, we will reuse it\r
+            if (htr.getLength() > 0) {\r
+                htr = new HSLFTextRun(htp);\r
+                htr.getCharacterStyle().copy(tpc);\r
+                htp.addTextRun(htr);\r
             }\r
+            htr.setText(rawText);\r
         }\r
 \r
-        return prop == null ? false : prop.getSubValue(index);\r
-    }\r
-\r
-   protected void setFlag(int index, boolean value) {\r
-       // Ensure we have the StyleTextProp atom we're going to need\r
-       assert(_paragraphStyle!=null);\r
-       BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(_paragraphStyle, ParagraphFlagsTextProp.NAME);\r
-       prop.setSubValue(value,index);\r
-   }\r
-\r
-   /**\r
-    * Check and add linebreaks to text runs leading other paragraphs\r
-    *\r
-    * @param paragraphs\r
-    */\r
-   protected static void fixLineEndings(List<HSLFTextParagraph> paragraphs) {\r
-       HSLFTextRun lastRun = null;\r
-       for (HSLFTextParagraph p : paragraphs) {\r
-           if (lastRun != null && !lastRun.getRawText().endsWith("\r")) {\r
-               lastRun.setText(lastRun.getRawText()+"\r");\r
-           }\r
-           List<HSLFTextRun> ltr = p.getTextRuns();\r
-           if (ltr.isEmpty()) {\r
-               throw new RuntimeException("paragraph without textruns found");\r
-           }\r
-           lastRun = ltr.get(ltr.size()-1);\r
-           assert(lastRun.getRawText() != null);\r
-       }\r
-   }\r
-\r
-   /**\r
-    * Search for a StyleTextPropAtom is for this text header (list of paragraphs)\r
-    * \r
-    * @param header the header\r
-    * @param textLen the length of the rawtext, or -1 if the length is not known\r
-    */\r
-   private static StyleTextPropAtom findStyleAtomPresent(TextHeaderAtom header, int textLen) {\r
-       boolean afterHeader = false;\r
-       StyleTextPropAtom style = null;\r
-       for (Record record : header.getParentRecord().getChildRecords()) {\r
-           long rt = record.getRecordType();\r
-           if (afterHeader && rt == RecordTypes.TextHeaderAtom.typeID) {\r
-               // already on the next header, quit searching\r
-               break;\r
-           }\r
-           afterHeader |= (header == record);\r
-           if (afterHeader && rt == RecordTypes.StyleTextPropAtom.typeID) {\r
-               // found it\r
-               style = (StyleTextPropAtom)record;\r
-           }\r
-       }\r
-\r
-       if (style == null) {\r
-           logger.log(POILogger.INFO, "styles atom doesn't exist. Creating dummy record for later saving.");\r
-           style = new StyleTextPropAtom((textLen < 0) ? 1 : textLen);\r
-       } else {\r
-           if (textLen >= 0) {\r
-               style.setParentTextSize(textLen);\r
-           }\r
-       }\r
-       \r
-       return style;\r
-   }\r
-\r
-\r
-   /**\r
-    * Saves the modified paragraphs/textrun to the records.\r
-    * Also updates the styles to the correct text length.\r
-    */\r
-   protected static void storeText(List<HSLFTextParagraph> paragraphs) {\r
-       fixLineEndings(paragraphs);\r
-\r
-       String rawText = toInternalString(getRawText(paragraphs));\r
-\r
-       // Will it fit in a 8 bit atom?\r
-       boolean isUnicode = StringUtil.hasMultibyte(rawText);\r
-       // isUnicode = true;\r
-\r
-       TextHeaderAtom headerAtom = paragraphs.get(0)._headerAtom;\r
-       TextBytesAtom byteAtom = paragraphs.get(0)._byteAtom;\r
-       TextCharsAtom charAtom = paragraphs.get(0)._charAtom;\r
-       StyleTextPropAtom styleAtom = findStyleAtomPresent(headerAtom, rawText.length());\r
-\r
-       // Store in the appropriate record\r
-       Record oldRecord = null, newRecord = null;\r
-       if (isUnicode) {\r
-           if (byteAtom != null || charAtom == null) {\r
-               oldRecord = byteAtom;\r
-               charAtom = new TextCharsAtom();\r
-           }\r
-           newRecord = charAtom;\r
-           charAtom.setText(rawText);\r
-       } else {\r
-           if (charAtom != null || byteAtom == null) {\r
-               oldRecord = charAtom;\r
-               byteAtom = new TextBytesAtom();\r
-           }\r
-           newRecord = byteAtom;\r
-           byte[] byteText = new byte[rawText.length()];\r
-           StringUtil.putCompressedUnicode(rawText,byteText,0);\r
-           byteAtom.setText(byteText);\r
-       }\r
-       assert(newRecord != null);\r
-       \r
-       RecordContainer _txtbox = headerAtom.getParentRecord();\r
-       Record[] cr = _txtbox.getChildRecords();\r
-       int headerIdx = -1, textIdx = -1, styleIdx = -1;\r
-       for (int i=0; i<cr.length; i++) {\r
-           Record r = cr[i];\r
-           if (r == headerAtom) headerIdx = i;\r
-           else if (r == oldRecord || r == newRecord) textIdx = i;\r
-           else if (r == styleAtom) styleIdx = i;\r
-       }\r
-\r
-       if (textIdx == -1) {\r
-           // the old record was never registered, ignore it\r
-           _txtbox.addChildAfter(newRecord, headerAtom);\r
-           textIdx = headerIdx+1;\r
-       } else {\r
-           // swap not appropriated records - noop if unchanged\r
-           cr[textIdx] = newRecord;\r
-       }\r
-\r
-       if (styleIdx == -1) {\r
-           // Add the new StyleTextPropAtom after the TextCharsAtom / TextBytesAtom\r
-           _txtbox.addChildAfter(styleAtom, newRecord);\r
-       }\r
-       \r
-       for (HSLFTextParagraph p : paragraphs) {\r
-           if (newRecord == byteAtom) {\r
-               p._byteAtom = byteAtom;\r
-               p._charAtom = null;\r
-           } else {\r
-               p._byteAtom = null;\r
-               p._charAtom = charAtom;\r
-           }\r
-       }\r
-       \r
-       // Update the text length for its Paragraph and Character stylings\r
-       //   * reset the length, to the new string's length\r
-       //   * add on +1 if the last block\r
-\r
-       styleAtom.clearStyles();\r
-\r
-       TextPropCollection lastPTPC = null, lastRTPC = null, ptpc = null, rtpc = null;\r
-       for (HSLFTextParagraph para : paragraphs) {\r
-           ptpc = para.getParagraphStyle();\r
-           ptpc.updateTextSize(0);\r
-           if (!ptpc.equals(lastPTPC)) {\r
-               lastPTPC = styleAtom.addParagraphTextPropCollection(0);\r
-               lastPTPC.copy(ptpc);\r
-           }\r
-           for (HSLFTextRun tr : para.getTextRuns()) {\r
-               rtpc = tr.getCharacterStyle();\r
-               rtpc.updateTextSize(0);\r
-               if (!rtpc.equals(lastRTPC)) {\r
-                   lastRTPC = styleAtom.addCharacterTextPropCollection(0);\r
-                   lastRTPC.copy(rtpc);\r
-               }\r
-               int len = tr.getLength();\r
-               ptpc.updateTextSize(ptpc.getCharactersCovered()+len);\r
-               rtpc.updateTextSize(len);\r
-               lastPTPC.updateTextSize(lastPTPC.getCharactersCovered()+len);\r
-               lastRTPC.updateTextSize(lastRTPC.getCharactersCovered()+len);\r
-           }\r
-       }\r
-\r
-       assert(lastPTPC != null && lastRTPC != null && ptpc != null && rtpc != null);\r
-       ptpc.updateTextSize(ptpc.getCharactersCovered()+1);\r
-       rtpc.updateTextSize(rtpc.getCharactersCovered()+1);\r
-       lastPTPC.updateTextSize(lastPTPC.getCharactersCovered()+1);\r
-       lastRTPC.updateTextSize(lastRTPC.getCharactersCovered()+1);\r
-\r
-       /**\r
-        * If TextSpecInfoAtom is present, we must update the text size in it,\r
-        * otherwise the ppt will be corrupted\r
-        */\r
-       for (Record r : paragraphs.get(0).getRecords()) {\r
-           if (r instanceof TextSpecInfoAtom) {\r
-               ((TextSpecInfoAtom)r).setParentSize(rawText.length()+1);\r
-               break;\r
-           }\r
-       }\r
-       \r
-       if (_txtbox instanceof EscherTextboxWrapper) {\r
-           try {\r
-               ((EscherTextboxWrapper)_txtbox).writeOut(null);\r
-           } catch (IOException e) {\r
-               throw new RuntimeException("failed dummy write", e);\r
-           }\r
-       }\r
-   }\r
-\r
-   /**\r
-    * Adds the supplied text onto the end of the TextParagraphs,\r
-    * creating a new RichTextRun for it to sit in.\r
-    *\r
-    * @param text the text string used by this object.\r
-    */\r
-   protected static HSLFTextRun appendText(List<HSLFTextParagraph> paragraphs, String text, boolean newParagraph) {\r
-       text = toInternalString(text);\r
-\r
-       // check paragraphs\r
-       assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());\r
-\r
-       HSLFTextParagraph htp = paragraphs.get(paragraphs.size()-1);\r
-       HSLFTextRun htr = htp.getTextRuns().get(htp.getTextRuns().size()-1);\r
-\r
-       boolean isFirst = !newParagraph;\r
-       for (String rawText : text.split("(?<=\r)")) {\r
-           if (!isFirst) {\r
-               TextPropCollection tpc = htp.getParagraphStyle();\r
-               HSLFTextParagraph prevHtp = htp;\r
-               htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom);\r
-               htp.getParagraphStyle().copy(tpc);\r
-               htp.setParentShape(prevHtp.getParentShape());\r
-               htp.setShapeId(prevHtp.getShapeId());\r
-               htp.supplySheet(prevHtp.getSheet());\r
-               paragraphs.add(htp);\r
-           }\r
-           isFirst = false;\r
-           \r
-           TextPropCollection tpc = htr.getCharacterStyle();\r
-           // special case, last text run is empty, we will reuse it\r
-           if (htr.getLength() > 0) {\r
-               htr = new HSLFTextRun(htp);\r
-               htr.getCharacterStyle().copy(tpc);\r
-               htp.addTextRun(htr);\r
-           }\r
-           htr.setText(rawText);\r
-       }\r
-       \r
-       storeText(paragraphs);\r
-\r
-       return htr;\r
-   }\r
-\r
-   /**\r
-    * Sets (overwrites) the current text.\r
-    * Uses the properties of the first paragraph / textrun\r
-    *\r
-    * @param text the text string used by this object.\r
-    */\r
-   public static HSLFTextRun setText(List<HSLFTextParagraph> paragraphs, String text) {\r
-       // check paragraphs\r
-       assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());\r
-\r
-       Iterator<HSLFTextParagraph> paraIter = paragraphs.iterator();\r
-       HSLFTextParagraph htp = paraIter.next(); // keep first\r
-       assert(htp != null);\r
-       while (paraIter.hasNext()) {\r
-           paraIter.next();\r
-           paraIter.remove();\r
-       }\r
-\r
-       Iterator<HSLFTextRun> runIter = htp.getTextRuns().iterator();\r
-       HSLFTextRun htr = runIter.next();\r
-       htr.setText("");\r
-       assert(htr != null);\r
-       while (runIter.hasNext()) {\r
-           runIter.next();\r
-           runIter.remove();\r
-       }\r
-\r
-       return appendText(paragraphs, text, false);\r
-   }\r
-\r
-   public static String getText(List<HSLFTextParagraph> paragraphs) {\r
-       assert(!paragraphs.isEmpty());\r
-       String rawText = getRawText(paragraphs);\r
-       return toExternalString(rawText, paragraphs.get(0).getRunType());\r
-   }\r
-   \r
-   public static String getRawText(List<HSLFTextParagraph> paragraphs) {\r
-       StringBuilder sb = new StringBuilder();\r
-       for (HSLFTextParagraph p : paragraphs) {\r
-           for (HSLFTextRun r : p.getTextRuns()) {\r
-               sb.append(r.getRawText());\r
-           }\r
-       }\r
-       return sb.toString();\r
-   }\r
-\r
-   /**\r
-    * Returns a new string with line breaks converted into internal ppt\r
-    * representation\r
-    */\r
-   protected static String toInternalString(String s) {\r
-       String ns = s.replaceAll("\\r?\\n", "\r");\r
-       return ns;\r
-   }\r
-\r
-   /**\r
-    * Converts raw text from the text paragraphs to a formatted string,\r
-    * i.e. it converts certain control characters used in the raw txt\r
-    *\r
-    * @param rawText the raw text\r
-    * @param runType the run type of the shape, paragraph or headerAtom.\r
-    *        use -1 if unknown\r
-    * @return the formatted string\r
-    */\r
-   public static String toExternalString(String rawText, int runType) {\r
-       // PowerPoint seems to store files with \r as the line break\r
-       // The messes things up on everything but a Mac, so translate\r
-       // them to \n\r
-       String text = rawText.replace('\r', '\n');\r
-\r
-       switch (runType) {\r
-           // 0xB acts like cariage return in page titles and like blank in the\r
-           // others\r
-           case -1:\r
-           case org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE:\r
-           case org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE:\r
-               text = text.replace((char) 0x0B, '\n');\r
-               break;\r
-           default:\r
-               text = text.replace((char) 0x0B, ' ');\r
-               break;\r
-       }\r
-\r
-       return text;\r
-   }\r
-\r
-   /**\r
-    * For a given PPDrawing, grab all the TextRuns\r
-    */\r
+        storeText(paragraphs);\r
+\r
+        return htr;\r
+    }\r
+\r
+    /**\r
+     * Sets (overwrites) the current text.\r
+     * Uses the properties of the first paragraph / textrun\r
+     * \r
+     * @param text the text string used by this object.\r
+     */\r
+    public static HSLFTextRun setText(List<HSLFTextParagraph> paragraphs, String text) {\r
+        // check paragraphs\r
+        assert(!paragraphs.isEmpty() && !paragraphs.get(0).getTextRuns().isEmpty());\r
+\r
+        Iterator<HSLFTextParagraph> paraIter = paragraphs.iterator();\r
+        HSLFTextParagraph htp = paraIter.next(); // keep first\r
+        assert (htp != null);\r
+        while (paraIter.hasNext()) {\r
+            paraIter.next();\r
+            paraIter.remove();\r
+        }\r
+\r
+        Iterator<HSLFTextRun> runIter = htp.getTextRuns().iterator();\r
+        HSLFTextRun htr = runIter.next();\r
+        htr.setText("");\r
+        assert (htr != null);\r
+        while (runIter.hasNext()) {\r
+            runIter.next();\r
+            runIter.remove();\r
+        }\r
+\r
+        return appendText(paragraphs, text, false);\r
+    }\r
+\r
+    public static String getText(List<HSLFTextParagraph> paragraphs) {\r
+        assert (!paragraphs.isEmpty());\r
+        String rawText = getRawText(paragraphs);\r
+        return toExternalString(rawText, paragraphs.get(0).getRunType());\r
+    }\r
+\r
+    public static String getRawText(List<HSLFTextParagraph> paragraphs) {\r
+        StringBuilder sb = new StringBuilder();\r
+        for (HSLFTextParagraph p : paragraphs) {\r
+            for (HSLFTextRun r : p.getTextRuns()) {\r
+                sb.append(r.getRawText());\r
+            }\r
+        }\r
+        return sb.toString();\r
+    }\r
+\r
+    /**\r
+     * Returns a new string with line breaks converted into internal ppt\r
+     * representation\r
+     */\r
+    protected static String toInternalString(String s) {\r
+        String ns = s.replaceAll("\\r?\\n", "\r");\r
+        return ns;\r
+    }\r
+\r
+    /**\r
+     * Converts raw text from the text paragraphs to a formatted string,\r
+     * i.e. it converts certain control characters used in the raw txt\r
+     * \r
+     * @param rawText the raw text\r
+     * @param runType the run type of the shape, paragraph or headerAtom.\r
+     *        use -1 if unknown\r
+     * @return the formatted string\r
+     */\r
+    public static String toExternalString(String rawText, int runType) {\r
+        // PowerPoint seems to store files with \r as the line break\r
+        // The messes things up on everything but a Mac, so translate\r
+        // them to \n\r
+        String text = rawText.replace('\r', '\n');\r
+\r
+        switch (runType) {\r
+        // 0xB acts like cariage return in page titles and like blank in the\r
+        // others\r
+        case -1:\r
+        case org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE:\r
+        case org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE:\r
+            text = text.replace((char) 0x0B, '\n');\r
+            break;\r
+        default:\r
+            text = text.replace((char) 0x0B, ' ');\r
+            break;\r
+        }\r
+\r
+        return text;\r
+    }\r
+\r
+    /**\r
+     * For a given PPDrawing, grab all the TextRuns\r
+     */\r
    public static List<List<HSLFTextParagraph>> findTextParagraphs(PPDrawing ppdrawing, HSLFSheet sheet) {\r
-       List<List<HSLFTextParagraph>> runsV = new ArrayList<List<HSLFTextParagraph>>();\r
-       for (EscherTextboxWrapper wrapper : ppdrawing.getTextboxWrappers()) {\r
-           runsV.add(findTextParagraphs(wrapper, sheet));\r
-       }\r
-       return runsV;\r
-   }\r
-\r
-   /**\r
-    * Scans through the supplied record array, looking for\r
-    * a TextHeaderAtom followed by one of a TextBytesAtom or\r
-    * a TextCharsAtom. Builds up TextRuns from these\r
-    *\r
-    * @param wrapper an EscherTextboxWrapper\r
-    */\r
-   protected static List<HSLFTextParagraph> findTextParagraphs(EscherTextboxWrapper wrapper, HSLFSheet sheet) {\r
-       // propagate parents to parent-aware records\r
-       RecordContainer.handleParentAwareRecords(wrapper);\r
-       int shapeId = wrapper.getShapeId();\r
-       List<HSLFTextParagraph> rv = null;\r
-       \r
-       OutlineTextRefAtom ota = (OutlineTextRefAtom)wrapper.findFirstOfType(OutlineTextRefAtom.typeID);\r
-       if (ota != null) {\r
-           // if we are based on an outline, there are no further records to be parsed from the wrapper\r
-           if (sheet == null) {\r
-               throw new RuntimeException("Outline atom reference can't be solved without a sheet record");\r
-           }\r
-           \r
-           List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs();\r
-           assert(sheetRuns != null);\r
-           \r
-           int idx = ota.getTextIndex();\r
-           for (List<HSLFTextParagraph> r : sheetRuns) {\r
-               if (r.isEmpty()) continue;\r
-               int ridx = r.get(0).getIndex();\r
-               if (ridx > idx) break;\r
-               if (ridx == idx) {\r
-                   if (rv == null) {\r
-                       rv = r;\r
-                   } else {\r
-                       // create a new container\r
-                       // TODO: ... is this case really happening?\r
-                       rv = new ArrayList<HSLFTextParagraph>(rv);\r
-                       rv.addAll(r);\r
-                   }\r
-               }\r
-           }\r
-           if(rv == null || rv.isEmpty()) {\r
-               logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);\r
-           }\r
-       } else {\r
-           if (sheet != null) {\r
-               // check sheet runs first, so we get exactly the same paragraph list\r
-               List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs();\r
-               assert(sheetRuns != null);\r
-\r
-               for (List<HSLFTextParagraph> paras : sheetRuns) {\r
+        List<List<HSLFTextParagraph>> runsV = new ArrayList<List<HSLFTextParagraph>>();\r
+        for (EscherTextboxWrapper wrapper : ppdrawing.getTextboxWrappers()) {\r
+            runsV.add(findTextParagraphs(wrapper, sheet));\r
+        }\r
+        return runsV;\r
+    }\r
+\r
+    /**\r
+     * Scans through the supplied record array, looking for\r
+     * a TextHeaderAtom followed by one of a TextBytesAtom or\r
+     * a TextCharsAtom. Builds up TextRuns from these\r
+     * \r
+     * @param wrapper an EscherTextboxWrapper\r
+     */\r
+    protected static List<HSLFTextParagraph> findTextParagraphs(EscherTextboxWrapper wrapper, HSLFSheet sheet) {\r
+        // propagate parents to parent-aware records\r
+        RecordContainer.handleParentAwareRecords(wrapper);\r
+        int shapeId = wrapper.getShapeId();\r
+        List<HSLFTextParagraph> rv = null;\r
+\r
+        OutlineTextRefAtom ota = (OutlineTextRefAtom)wrapper.findFirstOfType(OutlineTextRefAtom.typeID);\r
+        if (ota != null) {\r
+            // if we are based on an outline, there are no further records to be parsed from the wrapper\r
+            if (sheet == null) {\r
+                throw new RuntimeException("Outline atom reference can't be solved without a sheet record");\r
+            }\r
+\r
+            List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs();\r
+            assert (sheetRuns != null);\r
+\r
+            int idx = ota.getTextIndex();\r
+            for (List<HSLFTextParagraph> r : sheetRuns) {\r
+                if (r.isEmpty()) continue;\r
+                int ridx = r.get(0).getIndex();\r
+                if (ridx > idx) break;\r
+                if (ridx == idx) {\r
+                    if (rv == null) {\r
+                        rv = r;\r
+                    } else {\r
+                        // create a new container\r
+                        // TODO: ... is this case really happening?\r
+                        rv = new ArrayList<HSLFTextParagraph>(rv);\r
+                        rv.addAll(r);\r
+                    }\r
+                }\r
+            }\r
+            if (rv == null || rv.isEmpty()) {\r
+                logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);\r
+            }\r
+        } else {\r
+            if (sheet != null) {\r
+                // check sheet runs first, so we get exactly the same paragraph list\r
+                List<List<HSLFTextParagraph>> sheetRuns = sheet.getTextParagraphs();\r
+                assert (sheetRuns != null);\r
+\r
+                for (List<HSLFTextParagraph> paras : sheetRuns) {\r
                    if (!paras.isEmpty() && paras.get(0)._headerAtom.getParentRecord() == wrapper) {\r
-                       rv = paras;\r
-                       break;\r
-                   }\r
-               }\r
-           }\r
-           \r
-           if (rv == null) {\r
-               // if we haven't found the wrapper in the sheet runs, create a new paragraph list from its record\r
-               List<List<HSLFTextParagraph>> rvl = findTextParagraphs(wrapper.getChildRecords());\r
-               switch (rvl.size()) {\r
-               case 0: break; // nothing found\r
-               case 1: rv = rvl.get(0); break; // normal case\r
-               default:\r
-                   throw new RuntimeException("TextBox contains more than one list of paragraphs.");\r
-               }\r
-           }\r
-       }\r
-       \r
-       if (rv !=  null) {\r
-           StyleTextProp9Atom styleTextProp9Atom = wrapper.getStyleTextProp9Atom();\r
-           \r
-           for (HSLFTextParagraph htp : rv) {\r
-               htp.setShapeId(shapeId);\r
-               htp.setStyleTextProp9Atom(styleTextProp9Atom);\r
-           }\r
-       }\r
-       return rv;\r
-   }\r
-\r
-   /**\r
-    * Scans through the supplied record array, looking for\r
-    * a TextHeaderAtom followed by one of a TextBytesAtom or\r
-    * a TextCharsAtom. Builds up TextRuns from these\r
-    *\r
-    * @param records the records to build from\r
-    */\r
+                        rv = paras;\r
+                        break;\r
+                    }\r
+                }\r
+            }\r
+\r
+            if (rv == null) {\r
+                // if we haven't found the wrapper in the sheet runs, create a new paragraph list from its record\r
+                List<List<HSLFTextParagraph>> rvl = findTextParagraphs(wrapper.getChildRecords());\r
+                switch (rvl.size()) {\r
+                case 0: break; // nothing found\r
+                case 1: rv = rvl.get(0); break; // normal case\r
+                default:\r
+                    throw new RuntimeException("TextBox contains more than one list of paragraphs.");\r
+                }\r
+            }\r
+        }\r
+\r
+        if (rv != null) {\r
+            StyleTextProp9Atom styleTextProp9Atom = wrapper.getStyleTextProp9Atom();\r
+\r
+            for (HSLFTextParagraph htp : rv) {\r
+                htp.setShapeId(shapeId);\r
+                htp.setStyleTextProp9Atom(styleTextProp9Atom);\r
+            }\r
+        }\r
+        return rv;\r
+    }\r
+\r
+    /**\r
+     * Scans through the supplied record array, looking for\r
+     * a TextHeaderAtom followed by one of a TextBytesAtom or\r
+     * a TextCharsAtom. Builds up TextRuns from these\r
+     * \r
+     * @param records the records to build from\r
+     */\r
     protected static List<List<HSLFTextParagraph>> findTextParagraphs(Record[] records) {\r
         List<List<HSLFTextParagraph>> paragraphCollection = new ArrayList<List<HSLFTextParagraph>>();\r
 \r
-        int[] recordIdx = {0};\r
-        \r
+        int[] recordIdx = { 0 };\r
+\r
         for (int slwtIndex = 0; recordIdx[0] < records.length; slwtIndex++) {\r
-            TextHeaderAtom    header = null;\r
-            TextBytesAtom     tbytes = null;\r
-            TextCharsAtom     tchars = null;\r
-            TextRulerAtom     ruler  = null;\r
+            TextHeaderAtom header = null;\r
+            TextBytesAtom tbytes = null;\r
+            TextCharsAtom tchars = null;\r
+            TextRulerAtom ruler = null;\r
             MasterTextPropAtom indents = null;\r
 \r
             for (Record r : getRecords(records, recordIdx, null)) {\r
                 long rt = r.getRecordType();\r
                 if (RecordTypes.TextHeaderAtom.typeID == rt) {\r
-                    header = (TextHeaderAtom)r;\r
+                    header = (TextHeaderAtom) r;\r
                 } else if (RecordTypes.TextBytesAtom.typeID == rt) {\r
-                    tbytes = (TextBytesAtom)r;\r
+                    tbytes = (TextBytesAtom) r;\r
                 } else if (RecordTypes.TextCharsAtom.typeID == rt) {\r
-                    tchars = (TextCharsAtom)r;\r
+                    tchars = (TextCharsAtom) r;\r
                 } else if (RecordTypes.TextRulerAtom.typeID == rt) {\r
-                    ruler = (TextRulerAtom)r;\r
+                    ruler = (TextRulerAtom) r;\r
                 } else if (RecordTypes.MasterTextPropAtom.typeID == rt) {\r
-                    indents = (MasterTextPropAtom)r;\r
+                    indents = (MasterTextPropAtom) r;\r
                 }\r
                 // don't search for RecordTypes.StyleTextPropAtom.typeID here ... see findStyleAtomPresent below\r
             }\r
 \r
             if (header == null) break;\r
-            \r
+\r
             if (header.getParentRecord() instanceof SlideListWithText) {\r
                 // runs found in PPDrawing are not linked with SlideListWithTexts\r
                 header.setIndex(slwtIndex);\r
@@ -1093,7 +1071,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
 \r
             List<HSLFTextParagraph> paragraphs = new ArrayList<HSLFTextParagraph>();\r
             paragraphCollection.add(paragraphs);\r
-            \r
+\r
             // split, but keep delimiter\r
             for (String para : rawText.split("(?<=\r)")) {\r
                 HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars);\r
@@ -1116,7 +1094,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
         if (paragraphCollection.isEmpty()) {\r
             logger.log(POILogger.DEBUG, "No text records found.");\r
         }\r
-        \r
+\r
         return paragraphCollection;\r
     }\r
 \r
@@ -1124,25 +1102,25 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
         int paraIdx = 0, runIdx = 0;\r
         HSLFTextRun trun;\r
 \r
-        for (int csIdx=0; csIdx<charStyles.size(); csIdx++) {\r
+        for (int csIdx = 0; csIdx < charStyles.size(); csIdx++) {\r
             TextPropCollection p = charStyles.get(csIdx);\r
-            for (int ccRun = 0, ccStyle = p.getCharactersCovered(); ccRun < ccStyle; ) {\r
+            for (int ccRun = 0, ccStyle = p.getCharactersCovered(); ccRun < ccStyle;) {\r
                 HSLFTextParagraph para = paragraphs.get(paraIdx);\r
                 List<HSLFTextRun> runs = para.getTextRuns();\r
                 trun = runs.get(runIdx);\r
                 int len = trun.getLength();\r
 \r
-                if (ccRun+len <= ccStyle) {\r
+                if (ccRun + len <= ccStyle) {\r
                     ccRun += len;\r
                 } else {\r
                     String text = trun.getRawText();\r
-                    trun.setText(text.substring(0,ccStyle-ccRun));\r
+                    trun.setText(text.substring(0, ccStyle - ccRun));\r
 \r
                     HSLFTextRun nextRun = new HSLFTextRun(para);\r
-                    nextRun.setText(text.substring(ccStyle-ccRun));\r
-                    runs.add(runIdx+1, nextRun);\r
+                    nextRun.setText(text.substring(ccStyle - ccRun));\r
+                    runs.add(runIdx + 1, nextRun);\r
 \r
-                    ccRun += ccStyle-ccRun;\r
+                    ccRun += ccStyle - ccRun;\r
                 }\r
 \r
                 TextPropCollection pCopy = new TextPropCollection(0, TextPropType.character);\r
@@ -1151,7 +1129,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
 \r
                 len = trun.getLength();\r
                 if (paraIdx == paragraphs.size()-1 && runIdx == runs.size()-1) {\r
-                    if (csIdx < charStyles.size()-1) {\r
+                    if (csIdx < charStyles.size() - 1) {\r
                         // special case, empty trailing text run\r
                         HSLFTextRun nextRun = new HSLFTextRun(para);\r
                         nextRun.setText("");\r
@@ -1204,7 +1182,7 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
                     len += trun.getLength();\r
                 }\r
                 para.setIndentLevel(p.getIndentLevel());\r
-                ccPara += len+1;\r
+                ccPara += len + 1;\r
             }\r
         }\r
     }\r
@@ -1237,6 +1215,6 @@ public final class HSLFTextParagraph implements TextParagraph<HSLFTextRun> {
     }\r
 \r
     public EscherTextboxWrapper getTextboxWrapper() {\r
-        return (EscherTextboxWrapper)_headerAtom.getParentRecord();\r
+        return (EscherTextboxWrapper) _headerAtom.getParentRecord();\r
     }\r
 }\r
index 1e1dca888fd7d14d3852bfdaa576129acd55ec1b..bb4f14041589ef3afb79d692f4f84f8636e7153a 100644 (file)
@@ -17,7 +17,8 @@
 
 package org.apache.poi.hslf.usermodel;
 
-import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.fetchOrAddTextProp;
+import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.setPropVal;
+import static org.apache.poi.hslf.usermodel.HSLFTextParagraph.getPropVal;
 
 import java.awt.Color;
 
@@ -132,39 +133,17 @@ public final class HSLFTextRun implements TextRun {
         *  it if required.
         */
        private void setCharFlagsTextPropVal(int index, boolean value) {
-               if(getFlag(index) != value) setFlag(index, value);
+           // TODO: check if paragraph/chars can be handled the same ...
+               if (getFlag(index) != value) setFlag(index, value);
        }
 
-       /**
-        * Fetch the value of the given Character related TextProp.
-        * Returns -1 if that TextProp isn't present.
-        * If the TextProp isn't present, the value from the appropriate
-        *  Master Sheet will apply.
-        */
-       private int getCharTextPropVal(String propName) {
-               TextProp prop = null;
-               if (characterStyle != null){
-                       prop = characterStyle.findByName(propName);
-               }
-
-               if (prop == null){
-                       HSLFSheet sheet = parentParagraph.getSheet();
-                       int txtype = parentParagraph.getRunType();
-                       HSLFMasterSheet master = sheet.getMasterSheet();
-                       if (master != null)
-                               prop = master.getStyleAttribute(txtype, parentParagraph.getIndentLevel(), propName, true);
-               }
-               return prop == null ? -1 : prop.getValue();
-       }
-       
        /**
         * Sets the value of the given Paragraph TextProp, add if required
         * @param propName The name of the Paragraph TextProp
         * @param val The value to set for the TextProp
         */
        public void setCharTextPropVal(String propName, int val) {
-               TextProp tp = fetchOrAddTextProp(characterStyle, propName);
-               tp.setValue(val);
+           setPropVal(characterStyle, propName, val);
        }
 
 
@@ -260,8 +239,8 @@ public final class HSLFTextRun implements TextRun {
         * @return the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
         */
        public int getSuperscript() {
-               int val = getCharTextPropVal("superscript");
-               return val == -1 ? 0 : val;
+               TextProp tp = getPropVal(characterStyle, "superscript", parentParagraph);
+               return tp == null ? 0 : tp.getValue();
        }
 
        /**
@@ -270,14 +249,15 @@ public final class HSLFTextRun implements TextRun {
         * @param val the percentage of the font size. If the value is positive, it is superscript, otherwise it is subscript
         */
        public void setSuperscript(int val) {
-               setCharTextPropVal("superscript", val);
+           setPropVal(characterStyle, "superscript", val);
        }
 
        /**
         * Gets the font size
         */
-       public double getFontSize() {
-               return getCharTextPropVal("font.size");
+       public Double getFontSize() {
+        TextProp tp = getPropVal(characterStyle, "font.size", parentParagraph);
+        return tp == null ? null : (double)tp.getValue();
        }
 
 
@@ -292,7 +272,8 @@ public final class HSLFTextRun implements TextRun {
         * Gets the font index
         */
        public int getFontIndex() {
-               return getCharTextPropVal("font.index");
+        TextProp tp = getPropVal(characterStyle, "font.index", parentParagraph);
+        return tp == null ? -1 : tp.getValue();
        }
 
        /**
@@ -329,9 +310,9 @@ public final class HSLFTextRun implements TextRun {
                if (sheet == null || slideShow == null) {
                        return _fontFamily;
                }
-               int fontIdx = getCharTextPropVal("font.index");
-               if(fontIdx == -1) { return null; }
-               return slideShow.getFontCollection().getFontWithId(fontIdx);
+        TextProp tp = getPropVal(characterStyle, "font.index", parentParagraph);
+        if (tp == null) { return null; }
+               return slideShow.getFontCollection().getFontWithId(tp.getValue());
        }
 
        /**
@@ -339,7 +320,9 @@ public final class HSLFTextRun implements TextRun {
         * @see java.awt.Color
         */
        public Color getFontColor() {
-               int rgb = getCharTextPropVal("font.color");
+               TextProp tp = getPropVal(characterStyle, "font.color", parentParagraph);
+               if (tp == null) return null;
+               int rgb = tp.getValue();
 
                int cidx = rgb >> 24;
                if (rgb % 0x1000000 == 0){
@@ -370,7 +353,7 @@ public final class HSLFTextRun implements TextRun {
        }
 
     protected void setFlag(int index, boolean value) {
-        BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(characterStyle, CharFlagsTextProp.NAME);
+        BitMaskTextProp prop = (BitMaskTextProp)characterStyle.addWithName(CharFlagsTextProp.NAME);
         prop.setSubValue(value, index);
     }
 
index 3db3c6f6d023f5ec28ffdbc5844c84efb288ec6d..d3c606a62dc36a537418e81c647f01c499afffde 100644 (file)
@@ -55,12 +55,21 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
         double rightInset = insets.right;\r
         double penY = y;\r
 \r
-        double leftMargin = paragraph.getLeftMargin();\r
         boolean firstLine = true;\r
-        double indent = paragraph.getIndent();\r
+        Double leftMargin = paragraph.getLeftMargin();\r
+        Double indent = paragraph.getIndent();\r
+        if (leftMargin == null) {\r
+            leftMargin = (indent != null) ? -indent : 0;\r
+        }\r
+        if (indent == null) {\r
+            indent = (leftMargin != null) ? -leftMargin : 0;\r
+        }\r
+\r
 \r
         //The vertical line spacing\r
-        double spacing = paragraph.getLineSpacing();\r
+        Double spacing = paragraph.getLineSpacing();\r
+        if (spacing == null) spacing = 100d;\r
+        \r
         for(DrawTextFragment line : lines){\r
             double penX = x + leftMargin;\r
 \r
@@ -77,7 +86,7 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
                         bullet.setPosition(penX + indent, penY);\r
                     } else if(indent > 0){\r
                         // a positive value means the "First Line" indentation:\r
-                        // the first line is indented and other lines start at the bullet ofset\r
+                        // the first line is indented and other lines start at the bullet offset\r
                         bullet.setPosition(penX, penY);\r
                         penX += indent;\r
                     } else {\r
@@ -96,7 +105,9 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
 \r
             Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape());\r
 \r
-            switch (paragraph.getTextAlign()) {\r
+            TextAlign ta = paragraph.getTextAlign();\r
+            if (ta == null) ta = TextAlign.LEFT;\r
+            switch (ta) {\r
                 case CENTER:\r
                     penX += (anchor.getWidth() - leftMargin - line.getWidth() - leftInset - rightInset) / 2;\r
                     break;\r
@@ -219,9 +230,10 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
         if (buColor == null) buColor = (Color)firstLineAttr.getAttribute(TextAttribute.FOREGROUND);\r
 \r
         float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE);\r
-        float buSz = (float)bulletStyle.getBulletFontSize();\r
-        if(buSz > 0) fontSize *= buSz* 0.01;\r
-        else fontSize = -buSz;\r
+        Double buSz = bulletStyle.getBulletFontSize();\r
+        if (buSz == null) buSz = 100d; \r
+        if (buSz > 0) fontSize *= buSz* 0.01;\r
+        else fontSize = (float)-buSz;\r
 \r
         \r
         AttributedString str = new AttributedString(buCharacter);\r
@@ -237,10 +249,13 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
     protected String getRenderableText(TextRun tr) {\r
         StringBuilder buf = new StringBuilder();\r
         TextCap cap = tr.getTextCap();\r
+        String tabs = null;\r
         for (char c : tr.getRawText().toCharArray()) {\r
             if(c == '\t') {\r
-                // TODO: finish support for tabs\r
-                buf.append("  ");\r
+                if (tabs == null) {\r
+                    tabs = tab2space(tr);\r
+                }\r
+                buf.append(tabs);\r
                 continue;\r
             }\r
 \r
@@ -255,6 +270,34 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
 \r
         return buf.toString();\r
     }\r
+    \r
+    /**\r
+     * Replace a tab with the effective number of white spaces.\r
+     */\r
+    private String tab2space(TextRun tr) {\r
+        AttributedString string = new AttributedString(" ");\r
+        String typeFace = tr.getFontFamily();\r
+        if (typeFace == null) typeFace = "Lucida Sans";\r
+        string.addAttribute(TextAttribute.FAMILY, typeFace);\r
+\r
+        Double fs = tr.getFontSize();\r
+        if (fs == null) fs = 12d;\r
+        string.addAttribute(TextAttribute.SIZE, fs.floatValue());\r
+        \r
+        TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true));\r
+        double wspace = l.getAdvance();\r
+\r
+        Double tabSz = paragraph.getDefaultTabSize();\r
+        if (tabSz == null) tabSz = wspace*4;\r
+\r
+        int numSpaces = (int)Math.ceil(tabSz / wspace);\r
+        StringBuilder buf = new StringBuilder();\r
+        for(int i = 0; i < numSpaces; i++) {\r
+            buf.append(' ');\r
+        }\r
+        return buf.toString();\r
+    }\r
+    \r
 \r
     /**\r
      * Returns wrapping width to break lines in this paragraph\r
@@ -271,8 +314,14 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
 \r
         Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape());\r
 \r
-        double leftMargin = paragraph.getLeftMargin();\r
-        double indent = paragraph.getIndent();\r
+        Double leftMargin = paragraph.getLeftMargin();\r
+        Double indent = paragraph.getIndent();\r
+        if (leftMargin == null) {\r
+            leftMargin = (indent != null) ? -indent : 0;\r
+        }\r
+        if (indent == null) {\r
+            indent = (leftMargin != null) ? -leftMargin : 0;\r
+        }\r
 \r
         double width;\r
         TextShape<? extends TextParagraph<T>> ts = paragraph.getParentShape();\r
@@ -323,7 +372,9 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
             text.append(runText);\r
             int endIndex = text.length();\r
 \r
-            attList.add(new AttributedStringData(TextAttribute.FOREGROUND, run.getFontColor(), beginIndex, endIndex));\r
+            Color fgColor = run.getFontColor();\r
+            if (fgColor == null) fgColor = Color.BLACK;\r
+            attList.add(new AttributedStringData(TextAttribute.FOREGROUND, fgColor, beginIndex, endIndex));\r
 \r
             // user can pass an custom object to convert fonts\r
             String fontFamily = run.getFontFamily();\r
@@ -335,10 +386,14 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
             if(fontHandler != null) {\r
                 fontFamily = fontHandler.getRendererableFont(fontFamily, run.getPitchAndFamily());\r
             }\r
+            if (fontFamily == null) {\r
+                fontFamily = paragraph.getDefaultFontFamily();\r
+            }\r
             attList.add(new AttributedStringData(TextAttribute.FAMILY, fontFamily, beginIndex, endIndex));\r
 \r
-            float fontSz = (float)run.getFontSize();\r
-            attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz, beginIndex, endIndex));\r
+            Double fontSz = run.getFontSize();\r
+            if (fontSz == null) fontSz = paragraph.getDefaultFontSize();\r
+            attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), beginIndex, endIndex));\r
 \r
             if(run.isBold()) {\r
                 attList.add(new AttributedStringData(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD, beginIndex, endIndex));\r
@@ -364,9 +419,9 @@ public class DrawTextParagraph<T extends TextRun> implements Drawable {
         // ensure that the paragraph contains at least one character\r
         // We need this trick to correctly measure text\r
         if (text.length() == 0) {\r
-            float fontSz = (float)paragraph.getDefaultFontSize();\r
+            Double fontSz = paragraph.getDefaultFontSize();\r
             text.append(" ");\r
-            attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz, 0, 1));\r
+            attList.add(new AttributedStringData(TextAttribute.SIZE, fontSz.floatValue(), 0, 1));\r
         }\r
 \r
         AttributedString string = new AttributedString(text.toString());\r
index 247c7c1845b2454eb3f1399fc265cd788503f867..bb6c6dd29f68aa56010977fc1794a00a2ff19d45 100644 (file)
@@ -95,7 +95,8 @@ public class DrawTextShape<T extends TextShape<? extends TextParagraph<? extends
 \r
             if (!isFirstLine) {\r
                 // the amount of vertical white space before the paragraph\r
-                double spaceBefore = p.getSpaceBefore();\r
+                Double spaceBefore = p.getSpaceBefore();\r
+                if (spaceBefore == null) spaceBefore = 0d;\r
                 if(spaceBefore > 0) {\r
                     // positive value means percentage spacing of the height of the first line, e.g.\r
                     // the higher the first line, the bigger the space before the paragraph\r
@@ -112,7 +113,8 @@ public class DrawTextShape<T extends TextShape<? extends TextParagraph<? extends
             y += dp.getY();\r
 \r
             if (paragraphs.hasNext()) {\r
-                double spaceAfter = p.getSpaceAfter();\r
+                Double spaceAfter = p.getSpaceAfter();\r
+                if (spaceAfter == null) spaceAfter = 0d;\r
                 if(spaceAfter > 0) {\r
                     // positive value means percentage spacing of the height of the last line, e.g.\r
                     // the higher the last line, the bigger the space after the paragraph\r
index 14eb03c842d0a06c32b3b851430db84e79fb64bb..e85eee140b0323cc3ca31fc3895163df3b47e73e 100644 (file)
@@ -104,10 +104,18 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
     public interface BulletStyle {\r
         String getBulletCharacter();\r
         String getBulletFont();\r
-        double getBulletFontSize();\r
+        \r
+        /**\r
+         * The bullet point font size\r
+         * If bulletFontSize >= 0, then space is a percentage of normal line height.\r
+         * If bulletFontSize < 0, the absolute value in points\r
+         *\r
+         * @return the bullet point font size\r
+         */\r
+        Double getBulletFontSize();\r
         Color getBulletFontColor();\r
     }\r
-    \r
+\r
     /**\r
      * The amount of vertical white space before the paragraph\r
      * This may be specified in two different ways, percentage spacing and font point spacing:\r
@@ -116,9 +124,30 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
      * If spaceBefore < 0, the absolute value in points\r
      * </p>\r
      *\r
-     * @return the vertical white space before the paragraph\r
+     * @return the vertical white space before the paragraph, or null if unset\r
      */\r
-    double getSpaceBefore();\r
+    Double getSpaceBefore();\r
+\r
+    /**\r
+     * Set the amount of vertical white space that will be present before the paragraph.\r
+     * This space is specified in either percentage or points:\r
+     * <p>\r
+     * If spaceBefore >= 0, then space is a percentage of normal line height.\r
+     * If spaceBefore < 0, the absolute value of linespacing is the spacing in points\r
+     * </p>\r
+     * Examples:\r
+     * <pre><code>\r
+     *      // The paragraph will be formatted to have a spacing before the paragraph text.\r
+     *      // The spacing will be 200% of the size of the largest text on each line\r
+     *      paragraph.setSpaceBefore(200);\r
+     *\r
+     *      // The spacing will be a size of 48 points\r
+     *      paragraph.setSpaceBefore(-48.0);\r
+     * </code></pre>\r
+     *\r
+     * @param spaceBefore the vertical white space before the paragraph, null to unset\r
+     */\r
+    void setSpaceBefore(Double spaceBefore);    \r
     \r
     /**\r
      * The amount of vertical white space after the paragraph\r
@@ -128,40 +157,74 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
      * If spaceBefore < 0, the absolute value of linespacing is the spacing in points\r
      * </p>\r
      *\r
-     * @return the vertical white space after the paragraph\r
+     * @return the vertical white space after the paragraph or null, if unset\r
      */\r
-    double getSpaceAfter();    \r
+    Double getSpaceAfter();    \r
 \r
     /**\r
-     * @return the left margin (in points) of the paragraph\r
+     * Set the amount of vertical white space that will be present after the paragraph.\r
+     * This space is specified in either percentage or points:\r
+     * <p>\r
+     * If spaceAfter >= 0, then space is a percentage of normal line height.\r
+     * If spaceAfter < 0, the absolute value of linespacing is the spacing in points\r
+     * </p>\r
+     * Examples:\r
+     * <pre><code>\r
+     *      // The paragraph will be formatted to have a spacing after the paragraph text.\r
+     *      // The spacing will be 200% of the size of the largest text on each line\r
+     *      paragraph.setSpaceAfter(200);\r
+     *\r
+     *      // The spacing will be a size of 48 points\r
+     *      paragraph.setSpaceAfter(-48.0);\r
+     * </code></pre>\r
+     *\r
+     * @param spaceAfter the vertical white space after the paragraph, null to unset\r
      */\r
-    double getLeftMargin();\r
+    public void setSpaceAfter(Double spaceAfter);\r
+    \r
+    /**\r
+     * @return the left margin (in points) of the paragraph or null, if unset\r
+     */\r
+    Double getLeftMargin();\r
 \r
     /**\r
-     * @param leftMargin the left margin (in points) \r
+     * Specifies the left margin of the paragraph. This is specified in addition to the text body\r
+     * inset and applies only to this text paragraph. That is the text body Inset and the LeftMargin\r
+     * attributes are additive with respect to the text position.\r
+     * \r
+     * @param leftMargin the left margin (in points) or null to unset\r
      */\r
-    void setLeftMargin(double leftMargin);\r
+    void setLeftMargin(Double leftMargin);\r
     \r
     \r
     /**\r
-     * @return the right margin (in points) of the paragraph\r
+     * Specifies the right margin of the paragraph. This is specified in addition to the text body\r
+     * inset and applies only to this text paragraph. That is the text body Inset and the RightMargin\r
+     * attributes are additive with respect to the text position.\r
+     * \r
+     * The right margin is not support and therefore ignored by the HSLF implementation.\r
+     * \r
+     * @return the right margin (in points) of the paragraph or null, if unset\r
      */\r
-    double getRightMargin();\r
+    Double getRightMargin();\r
 \r
     /**\r
      * @param rightMargin the right margin (in points) of the paragraph\r
      */\r
-    void setRightMargin(double rightMargin);\r
+    void setRightMargin(Double rightMargin);\r
     \r
     /**\r
      * @return the indent (in points) applied to the first line of text in the paragraph.\r
+     *  or null, if unset\r
      */\r
-    double getIndent();\r
+    Double getIndent();\r
 \r
     /**\r
+     * Specifies the indent size that will be applied to the first line of text in the paragraph.   \r
+     * \r
      * @param indent the indent (in points) applied to the first line of text in the paragraph\r
      */\r
-    void setIndent(double indent);\r
+    void setIndent(Double indent);\r
     \r
     /**\r
      * Returns the vertical line spacing that is to be used within a paragraph.\r
@@ -171,9 +234,9 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
      * If linespacing < 0, the absolute value of linespacing is the spacing in points\r
      * </p>\r
      *\r
-     * @return the vertical line spacing.\r
+     * @return the vertical line spacing or null, if unset\r
      */\r
-    double getLineSpacing();\r
+    Double getLineSpacing();\r
     \r
     /**\r
      * This element specifies the vertical line spacing that is to be used within a paragraph.\r
@@ -196,14 +259,14 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
      * \r
      * @param linespacing the vertical line spacing\r
      */\r
-    void setLineSpacing(double lineSpacing);\r
+    void setLineSpacing(Double lineSpacing);\r
 \r
     String getDefaultFontFamily();\r
     \r
     /**\r
-     * @return the default font size, in case its not set in the textrun\r
+     * @return the default font size, in case its not set in the textrun or null, if unset\r
      */\r
-    double getDefaultFontSize();\r
+    Double getDefaultFontSize();\r
     \r
     /**\r
      * Returns the alignment that is applied to the paragraph.\r
@@ -217,8 +280,10 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
     /**\r
      * Returns the font alignment that is applied to the paragraph.\r
      *\r
-     * If this attribute is omitted, then a value of auto (~ left) is implied.\r
-     * @return ??? alignment that is applied to the paragraph\r
+     * If this attribute is omitted, then null is return,\r
+     * user code can imply the a value of {@link FontAlign#AUTO}\r
+     * \r
+     * @return alignment that is applied to the paragraph\r
      */\r
     FontAlign getFontAlign();\r
     \r
@@ -227,5 +292,11 @@ public interface TextParagraph<T extends TextRun> extends Iterable<T> {
      */\r
     BulletStyle getBulletStyle();\r
     \r
+    /**\r
+     * @return the default size for a tab character within this paragraph in points, null if unset\r
+     */\r
+    Double getDefaultTabSize();\r
+\r
+    \r
     TextShape<? extends TextParagraph<T>> getParentShape();\r
 }\r
index 8ac40c18923ac662d640ec3f284b444283085c25..bc1652afe5c50f2a37485a075c88e95047f856d5 100644 (file)
@@ -21,8 +21,6 @@ import java.awt.Color;
 
 /**
  * Some text.
- *
- * TODO - decide on how we do rich text stuff
  */
 public interface TextRun {
     enum TextCap {
@@ -31,13 +29,13 @@ public interface TextRun {
         ALL
     }
     
-    public String getRawText();
-       public void setText(String text);
+    String getRawText();
+       void setText(String text);
 
        TextCap getTextCap();
        
        Color getFontColor();
-       double getFontSize();
+       Double getFontSize();
        String getFontFamily();
        
        boolean isBold();
index 8e5652fb81158aafac79a4d34a2d885b916cd273..4733999d492dc4c0db8efd1d6b8c54047a5821fe 100644 (file)
@@ -22,8 +22,7 @@ import static org.junit.Assert.*;
 import java.awt.*;
 import java.awt.image.BufferedImage;
 import java.io.*;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
 
 import javax.imageio.ImageIO;
 
@@ -35,7 +34,6 @@ import org.apache.poi.sl.usermodel.Slide;
 import org.apache.poi.sl.usermodel.SlideShow;
 import org.apache.poi.util.JvmBugs;
 import org.apache.poi.xslf.usermodel.XMLSlideShow;
-import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -142,29 +140,43 @@ public final class TestPicture {
     @Test
     // @Ignore("Just for visual validation - antialiasing is different on various systems")
     public void bug54541() throws Exception {
-        String file = new String[]{
-            "54542_cropped_bitmap.pptx",
-            "54541_cropped_bitmap.ppt",
-            "54541_cropped_bitmap2.ppt",
-            "sample_pptx_grouping_issues.pptx"
-        }[3];
-        InputStream is = _slTests.openResourceAsStream(file);
-        SlideShow ss = file.endsWith("pptx") ? new XMLSlideShow(is) : new HSLFSlideShow(is);
-        is.close();
+        String files[] = {
+//            "sample_pptx_grouping_issues.pptx",
+//            "54542_cropped_bitmap.pptx",
+//            "54541_cropped_bitmap.ppt",
+//            "54541_cropped_bitmap2.ppt",
+//            "alterman_security.ppt",
+            "alterman_security2.pptx",
+        };
+        
+        BitSet pages = new BitSet();
+        pages.set(2);
         
-        boolean debugOut = false;
-        Dimension pg = ss.getPageSize();
-        int i=1;
-        for(Slide<?,?,?> slide : ss.getSlides()) {
-            if (debugOut) {
-                DummyGraphics2d graphics = new DummyGraphics2d();
-                slide.draw(graphics);
-            } else {
-                BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_RGB);
-                Graphics2D graphics = img.createGraphics();
-                fixFonts(graphics);
-                slide.draw(graphics);
-                ImageIO.write(img, "PNG", new File("test"+(i++)+"hslf.png"));
+        for (String file : files) {
+            InputStream is = _slTests.openResourceAsStream(file);
+            SlideShow ss = file.endsWith("pptx") ? new XMLSlideShow(is) : new HSLFSlideShow(is);
+            is.close();
+            
+            boolean debugOut = false;
+            Dimension pg = ss.getPageSize();
+            for (Slide<?,?,?> slide : ss.getSlides()) {
+                int slideNo = slide.getSlideNumber();
+                if (!pages.get(slideNo-1)) {
+                    if (pages.nextSetBit(slideNo-1) == -1) break; else continue;
+                }
+                if (debugOut) {
+                    DummyGraphics2d graphics = new DummyGraphics2d();
+                    slide.draw(graphics);
+                } else {
+                    BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_ARGB);
+                    Graphics2D graphics = img.createGraphics();
+                    fixFonts(graphics);
+                    slide.draw(graphics);
+                    graphics.setColor(Color.BLACK);
+                    graphics.setStroke(new BasicStroke(1));
+                    graphics.drawRect(0, 0, (int)pg.getWidth()-1, (int)pg.getHeight()-1);
+                    ImageIO.write(img, "PNG", new File(file.replaceFirst(".pptx?", "-")+slideNo+".png"));
+                }
             }
         }
     }
index 0468955eeb03465c42a1c9bcb1e67e975e9be354..b57e9f503cdbb839b225064c76d7d4a4bf456481 100644 (file)
@@ -470,7 +470,7 @@ public final class TestRichTextRun {
                assertEquals(expected, HSLFTextParagraph.getRawText(txt.get(1)));
                assertEquals(4, txt.get(1).size());
                rt = txt.get(1).get(0);
-               assertEquals('\u2022', rt.getBulletChar());
+               assertEquals('\u2022', (char)rt.getBulletChar());
                assertTrue(rt.isBullet());
 
 
@@ -486,7 +486,7 @@ public final class TestRichTextRun {
                assertEquals(4, txt.get(0).size());
                rt = txt.get(0).get(0);
                assertTrue(rt.isBullet());
-               assertEquals('\u2022', rt.getBulletChar());
+               assertEquals('\u2022', (char)rt.getBulletChar());
 
                expected =
             "I\u2019m a text box with user-defined\r" +
@@ -495,7 +495,7 @@ public final class TestRichTextRun {
                assertEquals(2, txt.get(1).size());
                rt = txt.get(1).get(0);
                assertTrue(rt.isBullet());
-               assertEquals('\u263A', rt.getBulletChar());
+               assertEquals('\u263A', (char)rt.getBulletChar());
        }
 
     @Test
@@ -513,8 +513,8 @@ public final class TestRichTextRun {
         HSLFTextRun tr = rt.getTextRuns().get(0);
                tr.setFontSize(42);
                rt.setBullet(true);
-               rt.setLeftMargin(50);
-               rt.setIndent(0);
+               rt.setLeftMargin(50d);
+               rt.setIndent(0d);
                rt.setBulletChar('\u263A');
                slide.addShape(shape);
 
@@ -522,7 +522,7 @@ public final class TestRichTextRun {
                assertEquals(true, rt.isBullet());
                assertEquals(50.0, rt.getLeftMargin(), 0);
                assertEquals(0, rt.getIndent(), 0);
-               assertEquals('\u263A', rt.getBulletChar());
+               assertEquals('\u263A', (char)rt.getBulletChar());
 
                shape.setAnchor(new java.awt.Rectangle(50, 50, 500, 300));
                slide.addShape(shape);
@@ -541,7 +541,7 @@ public final class TestRichTextRun {
                assertEquals(true, rt.isBullet());
                assertEquals(50.0, rt.getLeftMargin(), 0);
                assertEquals(0, rt.getIndent(), 0);
-               assertEquals('\u263A', rt.getBulletChar());
+               assertEquals('\u263A', (char)rt.getBulletChar());
        }
 
     @Test
index 9dda9af244cbabdc4f9c87b334b2a212bd879dd7..c2f7ac9d08f805acfd7ef423ebe82889f17b0817 100644 (file)
@@ -556,7 +556,7 @@ public final class TestTextRun {
         int i=0;
         for (List<HSLFTextParagraph> textParas : slide.getTextParagraphs()) {
             assertEquals("Arial", textParas.get(0).getTextRuns().get(0).getFontFamily());
-            assertEquals(sizes[i++], (int)textParas.get(0).getTextRuns().get(0).getFontSize());
+            assertEquals(sizes[i++], textParas.get(0).getTextRuns().get(0).getFontSize().intValue());
         }
     }
 
diff --git a/test-data/slideshow/alterman_security2.pptx b/test-data/slideshow/alterman_security2.pptx
new file mode 100644 (file)
index 0000000..1329228
Binary files /dev/null and b/test-data/slideshow/alterman_security2.pptx differ