You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2015/11/14 03:44:07 UTC

svn commit: r1714290 - in /poi: site/src/documentation/content/xdocs/ trunk/src/java/org/apache/poi/sl/draw/ trunk/src/java/org/apache/poi/sl/usermodel/ trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/ trunk/src/scratchpad/src/org/apache/poi/hslf/us...

Author: kiwiwings
Date: Sat Nov 14 02:44:07 2015
New Revision: 1714290

URL: http://svn.apache.org/viewvc?rev=1714290&view=rev
Log:
#54210 When saving PPT to PNG, some text is rendered backwards
#53189 Shapes drawn wrongly when ppt file converted to image
fix line decoration (HSLF)

Modified:
    poi/site/src/documentation/content/xdocs/status.xml
    poi/trunk/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
    poi/trunk/src/java/org/apache/poi/sl/draw/DrawTextShape.java
    poi/trunk/src/java/org/apache/poi/sl/usermodel/LineDecoration.java
    poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java

Modified: poi/site/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/status.xml?rev=1714290&r1=1714289&r2=1714290&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/status.xml (original)
+++ poi/site/src/documentation/content/xdocs/status.xml Sat Nov 14 02:44:07 2015
@@ -40,6 +40,8 @@
     </devs>
 
     <release version="3.14-beta1" date="2015-11-??">
+        <action dev="PD" type="fix" fixes-bug="54210">When saving PPT to PNG, some text is rendered backwards</action>
+        <action dev="PD" type="fix" fixes-bug="53189">Shapes drawn wrongly when ppt file converted to image</action>
         <action dev="PD" type="add">Removed most reflection calls on private methods/fields from production code; others are wrapped by AccessController.doPrivileged().</action>
         <action dev="PD" type="fix" fixes-bug="58597">XWPFDocument causes SecurityException under SecurityManager</action>
         <action dev="PD" type="fix" fixes-bug="53192">Images in ppt file have wrong width when convering ppt to png</action>

Modified: poi/trunk/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java?rev=1714290&r1=1714289&r2=1714290&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java Sat Nov 14 02:44:07 2015
@@ -48,6 +48,7 @@ import org.apache.poi.sl.draw.geom.Custo
 import org.apache.poi.sl.draw.geom.Outline;
 import org.apache.poi.sl.draw.geom.Path;
 import org.apache.poi.sl.usermodel.LineDecoration;
+import org.apache.poi.sl.usermodel.LineDecoration.DecorationShape;
 import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize;
 import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
 import org.apache.poi.sl.usermodel.Shadow;
@@ -59,6 +60,8 @@ import org.apache.poi.util.Units;
 
 
 public class DrawSimpleShape extends DrawShape {
+    
+    private static final double DECO_SIZE_POW = 1.5d;
 
     public DrawSimpleShape(SimpleShape<?,?> shape) {
         super(shape);
@@ -73,10 +76,10 @@ public class DrawSimpleShape extends Dra
         graphics.setStroke(stroke);
 
         Collection<Outline> elems = computeOutlines(graphics);
-        
+
         // first paint the shadow
         drawShadow(graphics, elems, fill, line);
-        
+
         // then fill the shape interior
         if (fill != null) {
             graphics.setPaint(fill);
@@ -85,10 +88,10 @@ public class DrawSimpleShape extends Dra
                     java.awt.Shape s = o.getOutline();
                     graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
                     graphics.fill(s);
-                }                
+                }
             }
         }
-        
+
         // then draw any content within this shape (text, image, etc.)
         drawContent(graphics);
 
@@ -103,7 +106,7 @@ public class DrawSimpleShape extends Dra
                 }
             }
         }
-        
+
         // draw line decorations
         drawDecoration(graphics, line, stroke);
     }
@@ -111,44 +114,60 @@ public class DrawSimpleShape extends Dra
     protected void drawDecoration(Graphics2D graphics, Paint line, BasicStroke stroke) {
         if(line == null) return;
         graphics.setPaint(line);
-        
+
         List<Outline> lst = new ArrayList<Outline>();
         LineDecoration deco = getShape().getLineDecoration();
         Outline head = getHeadDecoration(graphics, deco, stroke);
         if (head != null) lst.add(head);
         Outline tail = getTailDecoration(graphics, deco, stroke);
         if (tail != null) lst.add(tail);
-        
-        
+
+
         for(Outline o : lst){
             java.awt.Shape s = o.getOutline();
             Path p = o.getPath();
             graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
-            
+
             if(p.isFilled()) graphics.fill(s);
             if(p.isStroked()) graphics.draw(s);
         }
     }
 
     protected Outline getTailDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {
+        if (deco == null || stroke == null) {
+            return null;
+        }
         DecorationSize tailLength = deco.getTailLength();
+        if (tailLength == null) {
+            tailLength = DecorationSize.MEDIUM;
+        }
         DecorationSize tailWidth = deco.getTailWidth();
-    
+        if (tailWidth == null) {
+            tailWidth = DecorationSize.MEDIUM;
+        }
+
         double lineWidth = Math.max(2.5, stroke.getLineWidth());
-    
+
         Rectangle2D anchor = getAnchor(graphics, getShape());
         double x2 = anchor.getX() + anchor.getWidth(),
                y2 = anchor.getY() + anchor.getHeight();
-    
+
         double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());
-    
+
         AffineTransform at = new AffineTransform();
         java.awt.Shape tailShape = null;
         Path p = null;
         Rectangle2D bounds;
-        final double scaleY = Math.pow(2, tailWidth.ordinal()+1);
-        final double scaleX = Math.pow(2, tailLength.ordinal()+1);
-        switch (deco.getTailShape()) {
+        final double scaleY = Math.pow(DECO_SIZE_POW, tailWidth.ordinal()+1);
+        final double scaleX = Math.pow(DECO_SIZE_POW, tailLength.ordinal()+1);
+
+        DecorationShape tailShapeEnum = deco.getTailShape();
+
+        if (tailShapeEnum == null) {
+            return null;
+        }
+
+        switch (tailShapeEnum) {
             case OVAL:
                 p = new Path();
                 tailShape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
@@ -181,32 +200,47 @@ public class DrawSimpleShape extends Dra
             default:
                 break;
         }
-    
+
         if (tailShape != null) {
             tailShape = at.createTransformedShape(tailShape);
         }
         return tailShape == null ? null : new Outline(tailShape, p);
     }
-    
+
     protected Outline getHeadDecoration(Graphics2D graphics, LineDecoration deco, BasicStroke stroke) {
+        if (deco == null || stroke == null) {
+            return null;
+        }
         DecorationSize headLength = deco.getHeadLength();
+        if (headLength == null) {
+            headLength = DecorationSize.MEDIUM;
+        }
         DecorationSize headWidth = deco.getHeadWidth();
-    
+        if (headWidth == null) {
+            headWidth = DecorationSize.MEDIUM;
+        }
+
         double lineWidth = Math.max(2.5, stroke.getLineWidth());
-    
+
         Rectangle2D anchor = getAnchor(graphics, getShape());
         double x1 = anchor.getX(),
                 y1 = anchor.getY();
-    
+
         double alpha = Math.atan(anchor.getHeight() / anchor.getWidth());
-    
+
         AffineTransform at = new AffineTransform();
         java.awt.Shape headShape = null;
         Path p = null;
         Rectangle2D bounds;
-        final double scaleY = Math.pow(2, headWidth.ordinal()+1);
-        final double scaleX = Math.pow(2, headLength.ordinal()+1);
-        switch (deco.getHeadShape()) {
+        final double scaleY = Math.pow(DECO_SIZE_POW, headWidth.ordinal()+1);
+        final double scaleX = Math.pow(DECO_SIZE_POW, headLength.ordinal()+1);
+        DecorationShape headShapeEnum = deco.getHeadShape();
+
+        if (headShapeEnum == null) {
+            return null;
+        }
+
+        switch (headShapeEnum) {
             case OVAL:
                 p = new Path();
                 headShape = new Ellipse2D.Double(0, 0, lineWidth * scaleX, lineWidth * scaleY);
@@ -239,16 +273,16 @@ public class DrawSimpleShape extends Dra
             default:
                 break;
         }
-    
+
         if (headShape != null) {
             headShape = at.createTransformedShape(headShape);
         }
         return headShape == null ? null : new Outline(headShape, p);
     }
-    
+
     public BasicStroke getStroke() {
         StrokeStyle strokeStyle = getShape().getStrokeStyle();
-        
+
         float lineWidth = (float) strokeStyle.getLineWidth();
         if (lineWidth == 0.0f) lineWidth = 0.25f; // Both PowerPoint and OOo draw zero-length lines as 0.25pt
 
@@ -299,7 +333,7 @@ public class DrawSimpleShape extends Dra
 
           SolidPaint shadowPaint = shadow.getFillStyle();
           Color shadowColor = DrawPaint.applyColorTransform(shadowPaint.getSolidColor());
-          
+
           double shapeRotation = getShape().getRotation();
           if(getShape().getFlipVertical()) {
               shapeRotation += 180;
@@ -308,15 +342,15 @@ public class DrawSimpleShape extends Dra
           double dist = shadow.getDistance();
           double dx = dist * Math.cos(Math.toRadians(angle));
           double dy = dist * Math.sin(Math.toRadians(angle));
-          
+
           graphics.translate(dx, dy);
-          
+
           for(Outline o : outlines){
               java.awt.Shape s = o.getOutline();
               Path p = o.getPath();
               graphics.setRenderingHint(Drawable.GRADIENT_SHAPE, s);
               graphics.setPaint(shadowColor);
-              
+
               if(fill != null && p.isFilled()){
                   graphics.fill(s);
               } else if (line != null && p.isStroked()) {
@@ -326,26 +360,26 @@ public class DrawSimpleShape extends Dra
 
           graphics.translate(-dx, -dy);
       }
-      
+
     protected static CustomGeometry getCustomGeometry(String name) {
         return getCustomGeometry(name, null);
     }
-    
+
     protected static CustomGeometry getCustomGeometry(String name, Graphics2D graphics) {
         @SuppressWarnings("unchecked")
         Map<String, CustomGeometry> presets = (graphics == null)
             ? null
             : (Map<String, CustomGeometry>)graphics.getRenderingHint(Drawable.PRESET_GEOMETRY_CACHE);
-        
+
         if (presets == null) {
             presets = new HashMap<String,CustomGeometry>();
             if (graphics != null) {
                 graphics.setRenderingHint(Drawable.PRESET_GEOMETRY_CACHE, presets);
             }
-            
+
             String packageName = "org.apache.poi.sl.draw.binding";
             InputStream presetIS = Drawable.class.getResourceAsStream("presetShapeDefinitions.xml");
-    
+
             // StAX:
             EventFilter startElementFilter = new EventFilter() {
                 @Override
@@ -353,7 +387,7 @@ public class DrawSimpleShape extends Dra
                     return event.isStartElement();
                 }
             };
-            
+
             try {
                 XMLInputFactory staxFactory = XMLInputFactory.newInstance();
                 XMLEventReader staxReader = staxFactory.createXMLEventReader(presetIS);
@@ -363,14 +397,14 @@ public class DrawSimpleShape extends Dra
                 // JAXB:
                 JAXBContext jaxbContext = JAXBContext.newInstance(packageName);
                 Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
-        
+
                 while (staxFiltRd.peek() != null) {
                     StartElement evRoot = (StartElement)staxFiltRd.peek();
                     String cusName = evRoot.getName().getLocalPart();
                     // XMLEvent ev = staxReader.nextEvent();
                     JAXBElement<org.apache.poi.sl.draw.binding.CTCustomGeometry2D> el = unmarshaller.unmarshal(staxReader, CTCustomGeometry2D.class);
                     CTCustomGeometry2D cusGeom = el.getValue();
-                    
+
                     presets.put(cusName, new CustomGeometry(cusGeom));
                 }
             } catch (Exception e) {
@@ -383,10 +417,10 @@ public class DrawSimpleShape extends Dra
                 }
             }
         }
-        
+
         return presets.get(name);
     }
-    
+
     protected Collection<Outline> computeOutlines(Graphics2D graphics) {
 
         List<Outline> lst = new ArrayList<Outline>();

Modified: poi/trunk/src/java/org/apache/poi/sl/draw/DrawTextShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/draw/DrawTextShape.java?rev=1714290&r1=1714289&r2=1714290&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/draw/DrawTextShape.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/draw/DrawTextShape.java Sat Nov 14 02:44:07 2015
@@ -37,8 +37,10 @@ public class DrawTextShape extends DrawS
     public void drawContent(Graphics2D graphics) {
         fixFonts(graphics);
         
-        Rectangle2D anchor = DrawShape.getAnchor(graphics, getShape());
-        Insets2D insets = getShape().getInsets();
+        TextShape<?,?> s = getShape();
+        
+        Rectangle2D anchor = DrawShape.getAnchor(graphics, s);
+        Insets2D insets = s.getInsets();
         double x = anchor.getX() + insets.left;
         double y = anchor.getY();
 
@@ -50,39 +52,42 @@ public class DrawTextShape extends DrawS
         // (see DrawShape#applyTransform ), but we need to restore it to avoid painting "upside down".
         // See Bugzilla 54210.
 
-        if(getShape().getFlipVertical()){
-            graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
-            graphics.scale(1, -1);
-            graphics.translate(-anchor.getX(), -anchor.getY());
-
-            // text in vertically flipped shapes is rotated by 180 degrees
-            double centerX = anchor.getX() + anchor.getWidth()/2;
-            double centerY = anchor.getY() + anchor.getHeight()/2;
-            graphics.translate(centerX, centerY);
-            graphics.rotate(Math.toRadians(180));
-            graphics.translate(-centerX, -centerY);
-        }
-
+        boolean vertFlip = s.getFlipVertical();
+        boolean horzFlip = s.getFlipHorizontal();
+        ShapeContainer<?,?> sc = s.getParent();
+        while (sc instanceof PlaceableShape) {
+            PlaceableShape<?,?> ps = (PlaceableShape<?,?>)sc;
+            vertFlip ^= ps.getFlipVertical();
+            horzFlip ^= ps.getFlipHorizontal();
+            sc = ps.getParent();
+        };
+        
         // Horizontal flipping applies only to shape outline and not to the text in the shape.
         // Applying flip second time restores the original not-flipped transform
-        if(getShape().getFlipHorizontal()){
+        if (horzFlip ^ vertFlip) {
             graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
             graphics.scale(-1, 1);
-            graphics.translate(-anchor.getX() , -anchor.getY());
+            graphics.translate(-anchor.getX(), -anchor.getY());
+        }
+        
+        Double textRot = s.getTextRotation();
+        if (textRot != null) {
+            graphics.translate(anchor.getCenterX(), anchor.getCenterY());
+            graphics.rotate(Math.toRadians(textRot));
+            graphics.translate(-anchor.getCenterX(), -anchor.getCenterY());
         }
-
 
         // first dry-run to calculate the total height of the text
-        double textHeight = getShape().getTextHeight();
+        double textHeight = s.getTextHeight();
 
-        switch (getShape().getVerticalAlignment()){
+        switch (s.getVerticalAlignment()){
+            default:
             case TOP:
                 y += insets.top;
                 break;
             case BOTTOM:
                 y += anchor.getHeight() - textHeight - insets.bottom;
                 break;
-            default:
             case MIDDLE:
                 double delta = anchor.getHeight() - textHeight - insets.top - insets.bottom;
                 y += insets.top + delta/2;

Modified: poi/trunk/src/java/org/apache/poi/sl/usermodel/LineDecoration.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/usermodel/LineDecoration.java?rev=1714290&r1=1714289&r2=1714290&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/usermodel/LineDecoration.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/usermodel/LineDecoration.java Sat Nov 14 02:44:07 2015
@@ -22,19 +22,28 @@ public interface LineDecoration {
      *  Represents the shape decoration that appears at the ends of lines.
      */
     enum DecorationShape {
-        NONE(1),
-        TRIANGLE(2),
-        STEALTH(3),
-        DIAMOND(4),
-        OVAL(5),
-        ARROW(6);
-        
+        NONE(0,1),
+        TRIANGLE(1,2),
+        STEALTH(2,3),
+        DIAMOND(3,4),
+        OVAL(4,5),
+        ARROW(5,6);
+
+        public final int nativeId;
         public final int ooxmlId;
-        
-        DecorationShape(int ooxmlId) {
+
+        DecorationShape(int nativeId, int ooxmlId) {
+            this.nativeId = nativeId;
             this.ooxmlId = ooxmlId;
         }
-    
+
+        public static DecorationShape fromNativeId(int nativeId) {
+            for (DecorationShape ld : values()) {
+                if (ld.nativeId == nativeId) return ld;
+            }
+            return null;
+        }
+
         public static DecorationShape fromOoxmlId(int ooxmlId) {
             for (DecorationShape ds : values()) {
                 if (ds.ooxmlId == ooxmlId) return ds;
@@ -42,18 +51,27 @@ public interface LineDecoration {
             return null;
         }
     }
-    
+
     enum DecorationSize {
-        SMALL(1),
-        MEDIUM(2),
-        LARGE(3);
-        
+        SMALL(0, 1),
+        MEDIUM(1, 2),
+        LARGE(2, 3);
+
+        public final int nativeId;
         public final int ooxmlId;
-        
-        DecorationSize(int ooxmlId) {
+
+        DecorationSize(int nativeId, int ooxmlId) {
+            this.nativeId = nativeId;
             this.ooxmlId = ooxmlId;
         }
-        
+
+        public static DecorationSize fromNativeId(int nativeId) {
+            for (DecorationSize ld : values()) {
+                if (ld.nativeId == nativeId) return ld;
+            }
+            return null;
+        }
+
         public static DecorationSize fromOoxmlId(int ooxmlId) {
             for (DecorationSize ds : values()) {
                 if (ds.ooxmlId == ooxmlId) return ds;
@@ -61,32 +79,32 @@ public interface LineDecoration {
             return null;
         }
     }
-    
+
     /**
      * @return the line start shape
      */
     DecorationShape getHeadShape();
-    
+
     /**
      * @return the width of the start shape
      */
     DecorationSize getHeadWidth();
-    
+
     /**
      * @return the length of the start shape
      */
     DecorationSize getHeadLength();
-    
+
     /**
      * @return the line end shape
      */
     DecorationShape getTailShape();
-    
+
     /**
      * @return the width of the end shape
      */
     DecorationSize getTailWidth();
-    
+
     /**
      * @return the length of the end shape
      */

Modified: poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java?rev=1714290&r1=1714289&r2=1714290&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/usermodel/TextShape.java Sat Nov 14 02:44:07 2015
@@ -199,6 +199,28 @@ public interface TextShape<
      * @return vertical orientation of the text
      */
     TextDirection getTextDirection();
+
+    /**
+     * sets the vertical orientation
+     * @param orientation vertical orientation of the text
+     */
+    void setTextDirection(TextDirection orientation);
+
+    /**
+     * The text rotation can be independent specified from the shape rotation.
+     * For XSLF this can be an arbitrary degree, for HSLF the degree is given in steps of 90 degrees
+     * 
+     * @return text rotation in degrees, returns null if no rotation is given
+     */
+    Double getTextRotation();
+    
+    /**
+     * Sets the text rotation.
+     * For XSLF this can ben an arbitrary degree, for HSLF the rotation is rounded to next 90 degree step
+     * 
+     * @param rotation the text rotation, or null to unset the rotation
+     */
+    void setTextRotation(Double rotation);
     
     /**
      * Sets the text placeholder

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java?rev=1714290&r1=1714289&r2=1714290&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java Sat Nov 14 02:44:07 2015
@@ -161,7 +161,7 @@ public abstract class XSLFTextShape exte
 
     @Override
     public void setVerticalAlignment(VerticalAlignment anchor){
-        CTTextBodyProperties bodyPr = getTextBodyPr();
+        CTTextBodyProperties bodyPr = getTextBodyPr(true);
         if (bodyPr != null) {
              if(anchor == null) {
                 if(bodyPr.isSetAnchor()) bodyPr.unsetAnchor();
@@ -189,7 +189,7 @@ public abstract class XSLFTextShape exte
 
     @Override
     public void setHorizontalCentered(Boolean isCentered){
-        CTTextBodyProperties bodyPr = getTextBodyPr();
+        CTTextBodyProperties bodyPr = getTextBodyPr(true);
         if (bodyPr != null) {
              if (isCentered == null) {
                 if (bodyPr.isSetAnchorCtr()) bodyPr.unsetAnchorCtr();
@@ -214,12 +214,9 @@ public abstract class XSLFTextShape exte
         return fetcher.getValue() == null ? false : fetcher.getValue();
     }
     
-    /**
-     *
-     * @param orientation vertical orientation of the text
-     */
+    @Override
     public void setTextDirection(TextDirection orientation){
-        CTTextBodyProperties bodyPr = getTextBodyPr();
+        CTTextBodyProperties bodyPr = getTextBodyPr(true);
         if (bodyPr != null) {
             if(orientation == null) {
                 if(bodyPr.isSetVert()) bodyPr.unsetVert();
@@ -229,9 +226,7 @@ public abstract class XSLFTextShape exte
         }
     }
 
-    /**
-     * @return vertical orientation of the text
-     */
+    @Override
     public TextDirection getTextDirection(){
         CTTextBodyProperties bodyPr = getTextBodyPr();
         if (bodyPr != null) {
@@ -243,7 +238,24 @@ public abstract class XSLFTextShape exte
         return TextDirection.HORIZONTAL;
     }
 
-
+    @Override
+    public Double getTextRotation() {
+        CTTextBodyProperties bodyPr = getTextBodyPr();
+        if (bodyPr != null && bodyPr.isSetRot()) {
+            return bodyPr.getRot() / 60000.;
+        }
+        return null;        
+    }
+    
+    @Override
+    public void setTextRotation(Double rotation) {
+        CTTextBodyProperties bodyPr = getTextBodyPr(true);
+        if (bodyPr != null) {
+            bodyPr.setRot((int)(rotation * 60000.));
+        }
+    }
+    
+    
     /**
      * Returns the distance (in points) between the bottom of the text frame
      * and the bottom of the inscribed rectangle of the shape that contains the text.
@@ -341,7 +353,7 @@ public abstract class XSLFTextShape exte
      * @param margin    the bottom margin
      */
     public void setBottomInset(double margin){
-        CTTextBodyProperties bodyPr = getTextBodyPr();
+        CTTextBodyProperties bodyPr = getTextBodyPr(true);
         if (bodyPr != null) {
             if(margin == -1) bodyPr.unsetBIns();
             else bodyPr.setBIns(Units.toEMU(margin));
@@ -355,7 +367,7 @@ public abstract class XSLFTextShape exte
      * @param margin    the left margin
      */
     public void setLeftInset(double margin){
-        CTTextBodyProperties bodyPr = getTextBodyPr();
+        CTTextBodyProperties bodyPr = getTextBodyPr(true);
         if (bodyPr != null) {
             if(margin == -1) bodyPr.unsetLIns();
             else bodyPr.setLIns(Units.toEMU(margin));
@@ -369,7 +381,7 @@ public abstract class XSLFTextShape exte
      * @param margin    the right margin
      */
     public void setRightInset(double margin){
-        CTTextBodyProperties bodyPr = getTextBodyPr();
+        CTTextBodyProperties bodyPr = getTextBodyPr(true);
         if (bodyPr != null) {
             if(margin == -1) bodyPr.unsetRIns();
             else bodyPr.setRIns(Units.toEMU(margin));
@@ -383,7 +395,7 @@ public abstract class XSLFTextShape exte
      * @param margin    the top margin
      */
     public void setTopInset(double margin){
-        CTTextBodyProperties bodyPr = getTextBodyPr();
+        CTTextBodyProperties bodyPr = getTextBodyPr(true);
         if (bodyPr != null) {
             if(margin == -1) bodyPr.unsetTIns();
             else bodyPr.setTIns(Units.toEMU(margin));
@@ -421,7 +433,7 @@ public abstract class XSLFTextShape exte
 
     @Override
     public void setWordWrap(boolean wrap){
-        CTTextBodyProperties bodyPr = getTextBodyPr();
+        CTTextBodyProperties bodyPr = getTextBodyPr(true);
         if (bodyPr != null) {
             bodyPr.setWrap(wrap ? STTextWrappingType.SQUARE : STTextWrappingType.NONE);
         }
@@ -435,7 +447,7 @@ public abstract class XSLFTextShape exte
      * @param value type of autofit
      */
     public void setTextAutofit(TextAutofit value){
-        CTTextBodyProperties bodyPr = getTextBodyPr();
+        CTTextBodyProperties bodyPr = getTextBodyPr(true);
         if (bodyPr != null) {
             if(bodyPr.isSetSpAutoFit()) bodyPr.unsetSpAutoFit();
             if(bodyPr.isSetNoAutofit()) bodyPr.unsetNoAutofit();
@@ -464,10 +476,21 @@ public abstract class XSLFTextShape exte
     }
 
     protected CTTextBodyProperties getTextBodyPr(){
-        CTTextBody textBody = getTextBody(false);
-        return textBody == null ? null : textBody.getBodyPr();
+        return getTextBodyPr(false);
     }
 
+    protected CTTextBodyProperties getTextBodyPr(boolean create) {
+        CTTextBody textBody = getTextBody(create);
+        if (textBody == null) {
+            return null;
+        }
+        CTTextBodyProperties textBodyPr = textBody.getBodyPr();
+        if (textBodyPr == null && create) {
+            textBodyPr = textBody.addNewBodyPr();
+        }
+        return textBodyPr;
+    }
+    
     protected abstract CTTextBody getTextBody(boolean create);
 
     @Override

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java?rev=1714290&r1=1714289&r2=1714290&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java Sat Nov 14 02:44:07 2015
@@ -26,6 +26,7 @@ import org.apache.poi.hslf.record.*;
 import org.apache.poi.sl.draw.DrawPaint;
 import org.apache.poi.sl.draw.geom.*;
 import org.apache.poi.sl.usermodel.*;
+import org.apache.poi.sl.usermodel.LineDecoration.*;
 import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
 import org.apache.poi.sl.usermodel.StrokeStyle.LineCap;
 import org.apache.poi.sl.usermodel.StrokeStyle.LineCompound;
@@ -462,32 +463,100 @@ public abstract class HSLFSimpleShape ex
 
         };
     }
+    
+    public DecorationShape getLineHeadDecoration(){
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWHEAD);
+        return (prop == null) ? null : DecorationShape.fromNativeId(prop.getPropertyValue());
+    }
+
+    public void setLineHeadDecoration(DecorationShape decoShape){
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWHEAD, decoShape == null ? -1 : decoShape.nativeId);
+    }
+    
+    public DecorationSize getLineHeadWidth(){
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWWIDTH);
+        return (prop == null) ? null : DecorationSize.fromNativeId(prop.getPropertyValue());
+    }
+
+    public void setLineHeadWidth(DecorationSize decoSize){
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWWIDTH, decoSize == null ? -1 : decoSize.nativeId);
+    }
+    
+    public DecorationSize getLineHeadLength(){
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWLENGTH);
+        return (prop == null) ? null : DecorationSize.fromNativeId(prop.getPropertyValue());
+    }
+
+    public void setLineHeadLength(DecorationSize decoSize){
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        setEscherProperty(opt, EscherProperties.LINESTYLE__LINESTARTARROWLENGTH, decoSize == null ? -1 : decoSize.nativeId);
+    }
+    
+    public DecorationShape getLineTailDecoration(){
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWHEAD);
+        return (prop == null) ? null : DecorationShape.fromNativeId(prop.getPropertyValue());
+    }
+
+    public void setLineTailDecoration(DecorationShape decoShape){
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWHEAD, decoShape == null ? -1 : decoShape.nativeId);
+    }
+    
+    public DecorationSize getLineTailWidth(){
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWWIDTH);
+        return (prop == null) ? null : DecorationSize.fromNativeId(prop.getPropertyValue());
+    }
 
+    public void setLineTailWidth(DecorationSize decoSize){
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWWIDTH, decoSize == null ? -1 : decoSize.nativeId);
+    }
+    
+    public DecorationSize getLineTailLength(){
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWLENGTH);
+        return (prop == null) ? null : DecorationSize.fromNativeId(prop.getPropertyValue());
+    }
+
+    public void setLineTailLength(DecorationSize decoSize){
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        setEscherProperty(opt, EscherProperties.LINESTYLE__LINEENDARROWLENGTH, decoSize == null ? -1 : decoSize.nativeId);
+    }
+
+    
+    
     public LineDecoration getLineDecoration() {
         return new LineDecoration() {
 
             public DecorationShape getHeadShape() {
-                return DecorationShape.NONE;
+                return HSLFSimpleShape.this.getLineHeadDecoration();
             }
 
             public DecorationSize getHeadWidth() {
-                return DecorationSize.MEDIUM;
+                return HSLFSimpleShape.this.getLineHeadWidth();
             }
 
             public DecorationSize getHeadLength() {
-                return DecorationSize.MEDIUM;
+                return HSLFSimpleShape.this.getLineHeadLength();
             }
 
             public DecorationShape getTailShape() {
-                return DecorationShape.NONE;
+                return HSLFSimpleShape.this.getLineTailDecoration();
             }
 
             public DecorationSize getTailWidth() {
-                return DecorationSize.MEDIUM;
+                return HSLFSimpleShape.this.getLineTailWidth();
             }
 
             public DecorationSize getTailLength() {
-                return DecorationSize.MEDIUM;
+                return HSLFSimpleShape.this.getLineTailLength();
             }
         };
     }

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java?rev=1714290&r1=1714289&r2=1714290&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java Sat Nov 14 02:44:07 2015
@@ -54,8 +54,6 @@ import org.apache.poi.util.Units;
 
 /**
  * A common superclass of all shapes that can hold text.
- *
- * @author Yegor Kozlov
  */
 public abstract class HSLFTextShape extends HSLFSimpleShape
 implements TextShape<HSLFShape,HSLFTextParagraph> {
@@ -63,16 +61,37 @@ implements TextShape<HSLFShape,HSLFTextP
     /**
      * How to anchor the text
      */
-    /* package */ static final int AnchorTop = 0;
-    /* package */ static final int AnchorMiddle = 1;
-    /* package */ static final int AnchorBottom = 2;
-    /* package */ static final int AnchorTopCentered = 3;
-    /* package */ static final int AnchorMiddleCentered = 4;
-    /* package */ static final int AnchorBottomCentered = 5;
-    /* package */ static final int AnchorTopBaseline = 6;
-    /* package */ static final int AnchorBottomBaseline = 7;
-    /* package */ static final int AnchorTopCenteredBaseline = 8;
-    /* package */ static final int AnchorBottomCenteredBaseline = 9;
+    private enum HSLFTextAnchor {
+        TOP                   (0, VerticalAlignment.TOP,    false, false),
+        MIDDLE                (1, VerticalAlignment.MIDDLE, false, false),
+        BOTTOM                (2, VerticalAlignment.BOTTOM, false, false),
+        TOP_CENTER            (3, VerticalAlignment.TOP,    true,  false),
+        MIDDLE_CENTER         (4, VerticalAlignment.MIDDLE, true,  null),
+        BOTTOM_CENTER         (5, VerticalAlignment.BOTTOM, true,  false),
+        TOP_BASELINE          (6, VerticalAlignment.TOP,    false, true),
+        BOTTOM_BASELINE       (7, VerticalAlignment.BOTTOM, false, true),
+        TOP_CENTER_BASELINE   (8, VerticalAlignment.TOP,    true,  true),
+        BOTTOM_CENTER_BASELINE(9, VerticalAlignment.BOTTOM, true,  true);
+        
+        public final int nativeId;
+        public final VerticalAlignment vAlign;
+        public final boolean centered;
+        public final Boolean baseline;
+        
+        HSLFTextAnchor(int nativeId, VerticalAlignment vAlign, boolean centered, Boolean baseline) {
+            this.nativeId = nativeId;
+            this.vAlign = vAlign;
+            this.centered = centered;
+            this.baseline = baseline;
+        }
+
+        static HSLFTextAnchor fromNativeId(int nativeId) {
+            for (HSLFTextAnchor ta : values()) {
+                if (ta.nativeId == nativeId) return ta;
+            }
+            return null;
+        }
+    }
 
     /**
      * Specifies that a line of text will continue on subsequent lines instead
@@ -120,7 +139,7 @@ implements TextShape<HSLFShape,HSLFTextP
      *
      * @see <a href=""></a>
      */
-    boolean alignToBaseline = false;
+//    boolean alignToBaseline = false;
 
     /**
      * Used to calculate text bounds
@@ -279,38 +298,40 @@ implements TextShape<HSLFShape,HSLFTextP
      *
      * @return the type of alignment
      */
-    /* package */ int getAlignment(){
+    /* package */ HSLFTextAnchor getAlignment(){
         AbstractEscherOptRecord opt = getEscherOptRecord();
         EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT);
-        int align = HSLFTextShape.AnchorTop;
+        HSLFTextAnchor align = HSLFTextAnchor.TOP;
         if (prop == null){
             /**
              * If vertical alignment was not found in the shape properties then try to
              * fetch the master shape and search for the align property there.
              */
             int type = getRunType();
-            if(getSheet() != null && getSheet().getMasterSheet() != null){
-                HSLFMasterSheet master = getSheet().getMasterSheet();
-                HSLFTextShape masterShape = master.getPlaceholderByTextType(type);
-                if(masterShape != null) align = masterShape.getAlignment();
+            HSLFSheet sh = getSheet();
+            HSLFMasterSheet master = (sh != null) ? sh.getMasterSheet() : null;
+            HSLFTextShape masterShape = (master != null) ? master.getPlaceholderByTextType(type) : null;
+            if (masterShape != null && type != TextHeaderAtom.OTHER_TYPE) {
+                align = masterShape.getAlignment();
             } else {
                 //not found in the master sheet. Use the hardcoded defaults.
                 switch (type){
-                     case org.apache.poi.hslf.record.TextHeaderAtom.TITLE_TYPE:
-                     case org.apache.poi.hslf.record.TextHeaderAtom.CENTER_TITLE_TYPE:
-                         align = HSLFTextShape.AnchorMiddle;
+                     case TextHeaderAtom.TITLE_TYPE:
+                     case TextHeaderAtom.CENTER_TITLE_TYPE:
+                         align = HSLFTextAnchor.MIDDLE;
                          break;
                      default:
-                         align = HSLFTextShape.AnchorTop;
+                         align = HSLFTextAnchor.TOP;
                          break;
                  }
             }
         } else {
-            align = prop.getPropertyValue();
+            align = HSLFTextAnchor.fromNativeId(prop.getPropertyValue());
         }
 
-        alignToBaseline = (align == AnchorBottomBaseline || align == AnchorBottomCenteredBaseline
-            || align == AnchorTopBaseline || align == AnchorTopCenteredBaseline);
+        if (align == null) {
+            align = HSLFTextAnchor.TOP;
+        }
 
         return align;
     }
@@ -319,26 +340,21 @@ implements TextShape<HSLFShape,HSLFTextP
      * Sets the type of alignment for the text.
      * One of the <code>Anchor*</code> constants defined in this class.
      *
-     * @param align - the type of alignment
-     */
-    /* package */ void setAlignment(Boolean isCentered, VerticalAlignment vAlign) {
-        int align[];
-        switch (vAlign) {
-        case TOP:
-            align = new int[]{AnchorTop, AnchorTopCentered, AnchorTopBaseline, AnchorTopCenteredBaseline};
-            break;
-        default:
-        case MIDDLE:
-            align = new int[]{AnchorMiddle, AnchorMiddleCentered, AnchorMiddle, AnchorMiddleCentered};
-            break;
-        case BOTTOM:
-            align = new int[]{AnchorBottom, AnchorBottomCentered, AnchorBottomBaseline, AnchorBottomCenteredBaseline};
-            break;
+     * @param isCentered horizontal centered?
+     * @param vAlign vertical alignment
+     * @param baseline aligned to baseline?
+     */
+    /* package */ void setAlignment(Boolean isCentered, VerticalAlignment vAlign, boolean baseline) {
+        for (HSLFTextAnchor hta : HSLFTextAnchor.values()) {
+            if (
+                (hta.centered == (isCentered != null && isCentered)) &&
+                (hta.vAlign == vAlign) &&
+                (hta.baseline == null || hta.baseline == baseline)
+            ) {
+                setEscherProperty(EscherProperties.TEXT__ANCHORTEXT, hta.nativeId);
+                break;
+            }
         }
-
-        int align2 = align[(isCentered ? 1 : 0)+(alignToBaseline ? 2 : 0)];
-
-        setEscherProperty(EscherProperties.TEXT__ANCHORTEXT, align2);
     }
 
     /**
@@ -346,8 +362,7 @@ implements TextShape<HSLFShape,HSLFTextP
      * this is only used for older versions less equals Office 2003
      */
     public boolean isAlignToBaseline() {
-        getAlignment();
-        return alignToBaseline;
+        return getAlignment().baseline;
     }
 
     /**
@@ -356,51 +371,27 @@ implements TextShape<HSLFShape,HSLFTextP
      * @param alignToBaseline if true, vertical alignment is relative to baseline
      */
     public void setAlignToBaseline(boolean alignToBaseline) {
-        this.alignToBaseline = alignToBaseline;
-        setAlignment(isHorizontalCentered(), getVerticalAlignment());
+        setAlignment(isHorizontalCentered(), getVerticalAlignment(), alignToBaseline);
     }
 
     @Override
     public boolean isHorizontalCentered() {
-        int va = getAlignment();
-        switch (va) {
-        case AnchorTopCentered:
-        case AnchorTopCenteredBaseline:
-        case AnchorBottomCentered:
-        case AnchorBottomCenteredBaseline:
-        case AnchorMiddleCentered:
-            return true;
-        default:
-            return false;
-        }
+        return getAlignment().centered;
     }
 
     @Override
     public void setHorizontalCentered(Boolean isCentered) {
-        setAlignment(isCentered, getVerticalAlignment());
+        setAlignment(isCentered, getVerticalAlignment(), getAlignment().baseline);
     }
 
     @Override
     public VerticalAlignment getVerticalAlignment() {
-        int va = getAlignment();
-        switch (va) {
-        case AnchorTop:
-        case AnchorTopCentered:
-        case AnchorTopBaseline:
-        case AnchorTopCenteredBaseline: return VerticalAlignment.TOP;
-        case AnchorBottom:
-        case AnchorBottomCentered:
-        case AnchorBottomBaseline:
-        case AnchorBottomCenteredBaseline: return VerticalAlignment.BOTTOM;
-        default:
-        case AnchorMiddle:
-        case AnchorMiddleCentered: return VerticalAlignment.MIDDLE;
-        }
+        return getAlignment().vAlign;
     }
 
     @Override
     public void setVerticalAlignment(VerticalAlignment vAlign) {
-        setAlignment(isHorizontalCentered(), vAlign);
+        setAlignment(isHorizontalCentered(), vAlign, getAlignment().baseline);
     }
 
     /**
@@ -583,13 +574,6 @@ implements TextShape<HSLFShape,HSLFTextP
             if (_paragraphs.isEmpty()) {
                 logger.log(POILogger.WARN, "TextRecord didn't contained any text lines");
             }
-//            initParagraphsFromSheetRecords();
-//            if (_paragraphs.isEmpty()) {
-//                List<List<HSLFTextParagraph>> llhtp = HSLFTextParagraph.findTextParagraphs(_txtbox);
-//                if (!llhtp.isEmpty()) {
-//                    _paragraphs.addAll(llhtp.get(0));
-//                }
-//            }
         }
 
         for (HSLFTextParagraph p : _paragraphs) {
@@ -615,68 +599,6 @@ implements TextShape<HSLFShape,HSLFTextP
         }
     }
 
-//    protected void initParagraphsFromSheetRecords(){
-//        EscherTextboxWrapper txtbox = getEscherTextboxWrapper();
-//        HSLFSheet sheet = getSheet();
-//
-//        if (sheet == null || txtbox == null) return;
-//        List<List<HSLFTextParagraph>> sheetRuns = _sheet.getTextParagraphs();
-//        if (sheetRuns == null) return;
-//
-//        _paragraphs.clear();
-//        OutlineTextRefAtom ota = (OutlineTextRefAtom)txtbox.findFirstOfType(OutlineTextRefAtom.typeID);
-//
-//        if (ota != null) {
-//            int idx = ota.getTextIndex();
-//            for (List<HSLFTextParagraph> r : sheetRuns) {
-//                if (r.isEmpty()) continue;
-//                int ridx = r.get(0).getIndex();
-//                if (ridx > idx) break;
-//                if (ridx == idx) _paragraphs.addAll(r);
-//            }
-//            if(_paragraphs.isEmpty()) {
-//                logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx);
-//            }
-//        } else {
-//            int shapeId = getShapeId();
-//            for (List<HSLFTextParagraph> r : sheetRuns) {
-//                if (r.isEmpty()) continue;
-//                if (r.get(0).getShapeId() == shapeId) _paragraphs.addAll(r);
-//            }
-//        }
-//
-//        // ensure the same references child records of TextRun - see #48916
-////        if(_txtrun != null) {
-////            for (int i = 0; i < child.length; i++) {
-////                for (Record r : _txtrun.getRecords()) {
-////                    if (child[i].getRecordType() == r.getRecordType()) {
-////                        child[i] = r;
-////                    }
-////                }
-////            }
-////        }
-//    }
-
-    /*
-        // 0xB acts like cariage return in page titles and like blank in the others
-        char replChr;
-        switch(tha == null ? -1 : tha.getTextType()) {
-            case -1:
-            case TextHeaderAtom.TITLE_TYPE:
-            case TextHeaderAtom.CENTER_TITLE_TYPE:
-                replChr = '\n';
-                break;
-            default:
-                replChr = ' ';
-                break;
-        }
-
-        // PowerPoint seems to store files with \r as the line break
-        // The messes things up on everything but a Mac, so translate
-        //  them to \n
-        String text = rawText.replace('\r','\n').replace('\u000b', replChr);
-     */
-
     /**
      * Return <code>OEPlaceholderAtom</code>, the atom that describes a placeholder.
      *
@@ -755,9 +677,35 @@ implements TextShape<HSLFShape,HSLFTextP
     @Override
     public TextDirection getTextDirection() {
         // TODO: determine vertical text setting
+        // see 2.3.22.10 Geometry Text Boolean Properties
         return TextDirection.HORIZONTAL;
     }
 
+    @Override
+    public void setTextDirection(TextDirection orientation) {
+        // TODO: determine vertical text setting
+        // see 2.3.22.10 Geometry Text Boolean Properties / gtextFVertical [MS-ODRAW]
+    }
+    
+    @Override
+    public Double getTextRotation() {
+        // see 2.4.6 MSOCDIR
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__FONTROTATION);
+        return (prop == null) ? null : (90. * prop.getPropertyValue());
+    }
+    
+    @Override
+    public void setTextRotation(Double rotation) {
+        AbstractEscherOptRecord opt = getEscherOptRecord();
+        if (rotation == null) {
+            opt.removeEscherProperty(EscherProperties.TEXT__FONTROTATION);
+        } else {
+            int rot = (int)(Math.round(rotation / 90.) % 4L);
+            setEscherProperty(EscherProperties.TEXT__FONTROTATION, rot);
+        }
+    }
+    
     /**
      * Returns the raw text content of the shape. This hasn't had any
      * changes applied to it, and so is probably unlikely to print




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