#62953 - Rendering of FreeformShapes with formula fails

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1848492 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/java/org/apache/poi/ddf/EscherProperties.java b/src/java/org/apache/poi/ddf/EscherProperties.java
index acff624..482bce9 100644
--- a/src/java/org/apache/poi/ddf/EscherProperties.java
+++ b/src/java/org/apache/poi/ddf/EscherProperties.java
@@ -26,6 +26,7 @@
  *
  * @author Glen Stampoultzis (glens at apache.org)
  */
+@SuppressWarnings("WeakerAccess")
 public final class EscherProperties {
 
 	// Property constants
@@ -117,6 +118,15 @@
 	public static final short GEOMETRY__ADJUST8VALUE = 334;
 	public static final short GEOMETRY__ADJUST9VALUE = 335;
 	public static final short GEOMETRY__ADJUST10VALUE = 336;
+	public static final short GEOMETRY__PCONNECTIONSITES = 337;
+	public static final short GEOMETRY__PCONNECTIONSITESDIR = 338;
+	public static final short GEOMETRY__XLIMO = 339;
+	public static final short GEOMETRY__YLIMO = 340;
+	public static final short GEOMETRY__PADJUSTHANDLES = 341;
+	public static final short GEOMETRY__PGUIDES = 342;
+	public static final short GEOMETRY__PINSCRIBE = 343;
+	public static final short GEOMETRY__CXK = 344;
+	public static final short GEOMETRY__PFRAGMENTS = 345;
 	public static final short GEOMETRY__SHADOWok = 378;
 	public static final short GEOMETRY__3DOK = 379;
 	public static final short GEOMETRY__LINEOK = 380;
@@ -333,6 +343,9 @@
 
 	private static final Map<Short, EscherPropertyMetaData> properties = initProps();
 
+	private EscherProperties() {
+	}
+
 	private static Map<Short, EscherPropertyMetaData> initProps() {
 		Map<Short, EscherPropertyMetaData> m = new HashMap<>();
 		addProp(m, TRANSFORM__ROTATION, "transform.rotation");
@@ -423,6 +436,15 @@
 		addProp(m, GEOMETRY__ADJUST8VALUE, "geometry.adjust8value");
 		addProp(m, GEOMETRY__ADJUST9VALUE, "geometry.adjust9value");
 		addProp(m, GEOMETRY__ADJUST10VALUE, "geometry.adjust10value");
+		addProp(m, GEOMETRY__PCONNECTIONSITES, "geometry.pConnectionSites");
+		addProp(m, GEOMETRY__PCONNECTIONSITESDIR, "geometry.pConnectionSitesDir");
+		addProp(m, GEOMETRY__XLIMO, "geometry.xLimo");
+		addProp(m, GEOMETRY__YLIMO, "geometry.yLimo");
+		addProp(m, GEOMETRY__PADJUSTHANDLES, "geometry.pAdjustHandles");
+		addProp(m, GEOMETRY__PGUIDES, "geometry.pGuides");
+		addProp(m, GEOMETRY__PINSCRIBE, "geometry.pInscribe");
+		addProp(m, GEOMETRY__CXK, "geometry.cxk");
+		addProp(m, GEOMETRY__PFRAGMENTS, "geometry.pFragments");
 		addProp(m, GEOMETRY__SHADOWok, "geometry.shadowOK");
 		addProp(m, GEOMETRY__3DOK, "geometry.3dok");
 		addProp(m, GEOMETRY__LINEOK, "geometry.lineok");
@@ -641,20 +663,20 @@
 	}
 
 	private static void addProp(Map<Short, EscherPropertyMetaData> m, int s, String propName) {
-		m.put(Short.valueOf((short) s), new EscherPropertyMetaData(propName));
+		m.put((short) s, new EscherPropertyMetaData(propName));
 	}
 
 	private static void addProp(Map<Short, EscherPropertyMetaData> m, int s, String propName, byte type) {
-		m.put(Short.valueOf((short) s), new EscherPropertyMetaData(propName, type));
+		m.put((short) s, new EscherPropertyMetaData(propName, type));
 	}
 
 	public static String getPropertyName(short propertyId) {
-		EscherPropertyMetaData o = properties.get(Short.valueOf(propertyId));
+		EscherPropertyMetaData o = properties.get(propertyId);
 		return o == null ? "unknown" : o.getDescription();
 	}
 
 	public static byte getPropertyType(short propertyId) {
-		EscherPropertyMetaData escherPropertyMetaData = properties.get(Short.valueOf(propertyId));
+		EscherPropertyMetaData escherPropertyMetaData = properties.get(propertyId);
 		return escherPropertyMetaData == null ? 0 : escherPropertyMetaData.getType();
 	}
 }
diff --git a/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java b/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java
index 6eb60df..be18f03 100644
--- a/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawFreeformShape.java
@@ -17,43 +17,11 @@
 
 package org.apache.poi.sl.draw;
 
-import java.awt.Graphics2D;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Path2D;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import org.apache.poi.sl.usermodel.FreeformShape;
 
-import org.apache.poi.sl.draw.geom.Outline;
-import org.apache.poi.sl.draw.geom.Path;
-import org.apache.poi.sl.usermodel.*;
-
+@SuppressWarnings("WeakerAccess")
 public class DrawFreeformShape extends DrawAutoShape {
     public DrawFreeformShape(FreeformShape<?,?> shape) {
         super(shape);
     }
-
-    protected Collection<Outline> computeOutlines(Graphics2D graphics) {
-        List<Outline> lst = new ArrayList<>();
-        FreeformShape<?,?> fsh = (FreeformShape<?, ?>) getShape();
-        Path2D sh = fsh.getPath();
-
-        AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);
-        if (tx == null) {
-            tx = new AffineTransform();
-        }
-
-        java.awt.Shape canvasShape = tx.createTransformedShape(sh);
-
-        FillStyle fs = fsh.getFillStyle();
-        StrokeStyle ss = fsh.getStrokeStyle();
-        Path path = new Path(fs != null, ss != null);
-        lst.add(new Outline(canvasShape, path));
-        return lst;
-    }
-
-    @Override
-    protected TextShape<?,? extends TextParagraph<?,?,? extends TextRun>> getShape() {
-        return (TextShape<?,? extends TextParagraph<?,?,? extends TextRun>>)shape;
-    }
 }
diff --git a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
index 7ed1d35..35c3aa1 100644
--- a/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
+++ b/src/java/org/apache/poi/sl/draw/DrawSimpleShape.java
@@ -410,14 +410,20 @@
         }
         for (Path p : geom) {
 
-            double w = p.getW(), h = p.getH(), scaleX = Units.toPoints(1), scaleY = scaleX;
+            double w = p.getW(), h = p.getH(), scaleX, scaleY;
             if (w == -1) {
                 w = Units.toEMU(anchor.getWidth());
+                scaleX = Units.toPoints(1);
+            } else if (anchor.getWidth() == 0) {
+                scaleX = 1;
             } else {
                 scaleX = anchor.getWidth() / w;
             }
             if (h == -1) {
                 h = Units.toEMU(anchor.getHeight());
+                scaleY = Units.toPoints(1);
+            } else if (anchor.getHeight() == 0) {
+                scaleY = 1;
             } else {
                 scaleY = anchor.getHeight() / h;
             }
diff --git a/src/java/org/apache/poi/sl/draw/geom/Context.java b/src/java/org/apache/poi/sl/draw/geom/Context.java
index 31847ce..3ed84b3 100644
--- a/src/java/org/apache/poi/sl/draw/geom/Context.java
+++ b/src/java/org/apache/poi/sl/draw/geom/Context.java
@@ -22,11 +22,18 @@
 import java.awt.geom.Rectangle2D;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.regex.Pattern;
 
 public class Context {
-    final Map<String, Double> _ctx = new HashMap<>();
-    final IAdjustableShape _props;
-    final Rectangle2D _anchor;
+    private static final Pattern DOUBLE_PATTERN = Pattern.compile(
+        "[\\x00-\\x20]*[+-]?(NaN|Infinity|((((\\p{Digit}+)(\\.)?((\\p{Digit}+)?)" +
+        "([eE][+-]?(\\p{Digit}+))?)|(\\.(\\p{Digit}+)([eE][+-]?(\\p{Digit}+))?)|" +
+        "(((0[xX](\\p{XDigit}+)(\\.)?)|(0[xX](\\p{XDigit}+)?(\\.)(\\p{XDigit}+)))" +
+        "[pP][+-]?(\\p{Digit}+)))[fFdD]?))[\\x00-\\x20]*");
+
+    private final Map<String, Double> _ctx = new HashMap<>();
+    private final IAdjustableShape _props;
+    private final Rectangle2D _anchor;
     
     public Context(CustomGeometry geom, Rectangle2D anchor, IAdjustableShape props){
         _props = props;
@@ -39,23 +46,22 @@
         }
     }
 
-    public Rectangle2D getShapeAnchor(){
+    Rectangle2D getShapeAnchor(){
         return _anchor;
     }
 
-    public Guide getAdjustValue(String name){
+    Guide getAdjustValue(String name){
         // ignore HSLF props for now ... the results with default value are usually better - see #59004
         return (_props.getClass().getName().contains("hslf")) ? null : _props.getAdjustValue(name);
     }
 
     public double getValue(String key){
-        if(key.matches("(\\+|-)?\\d+")){
+        if(DOUBLE_PATTERN.matcher(key).matches()){
             return Double.parseDouble(key);
         }
 
-        Double val = _ctx.get(key);
         // BuiltInGuide throws IllegalArgumentException if key is not defined
-        return (val != null) ? val : evaluate(BuiltInGuide.valueOf("_"+key));
+        return _ctx.containsKey(key) ? _ctx.get(key) : evaluate(BuiltInGuide.valueOf("_"+key));
     }
 
     public double evaluate(Formula fmla){
diff --git a/src/java/org/apache/poi/sl/usermodel/FreeformShape.java b/src/java/org/apache/poi/sl/usermodel/FreeformShape.java
index 2a1580a..42ae10a 100644
--- a/src/java/org/apache/poi/sl/usermodel/FreeformShape.java
+++ b/src/java/org/apache/poi/sl/usermodel/FreeformShape.java
@@ -24,16 +24,15 @@
     P extends TextParagraph<S,P,? extends TextRun>
 > extends AutoShape<S,P> {
     /**
-     * Gets the shape path.
-     * <p>
-     *     The path is translated in the shape's coordinate system, i.e.
-     *     freeform.getPath().getBounds2D() equals to freeform.getAnchor()
-     *     (small discrepancies are possible due to rounding errors)
-     * </p>
+     * Gets the shape path.<p>
+     *
+     * The path is translated in the shape's coordinate system, i.e.
+     * freeform.getPath2D().getBounds2D() equals to freeform.getAnchor()
+     * (small discrepancies are possible due to rounding errors)
      *
      * @return the path
      */
-    Path2D.Double getPath();
+    Path2D getPath();
 
     /**
      * Set the shape path
@@ -41,5 +40,5 @@
      * @param path  shape outline
      * @return the number of points written
      */
-    int setPath(Path2D.Double path);
+    int setPath(Path2D path);
 }
diff --git a/src/java/org/apache/poi/util/Units.java b/src/java/org/apache/poi/util/Units.java
index da0a877..709e3f1 100644
--- a/src/java/org/apache/poi/util/Units.java
+++ b/src/java/org/apache/poi/util/Units.java
@@ -127,7 +127,7 @@
         points /= MASTER_DPI;
         return points;
     }
-    
+
     public static int pointsToMaster(double points) {
         points *= MASTER_DPI;
         points /= POINT_DPI;
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java
index 54acd2b..8c41fe1 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFFreeformShape.java
@@ -24,6 +24,12 @@
 import java.awt.geom.PathIterator;
 import java.awt.geom.Rectangle2D;
 
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+
+import org.apache.poi.ooxml.POIXMLTypeLoader;
+import org.apache.poi.sl.draw.geom.CustomGeometry;
+import org.apache.poi.sl.draw.geom.PresetGeometries;
 import org.apache.poi.sl.usermodel.FreeformShape;
 import org.apache.poi.util.Beta;
 import org.apache.poi.util.POILogFactory;
@@ -31,6 +37,7 @@
 import org.apache.poi.util.Units;
 import org.apache.xmlbeans.XmlCursor;
 import org.apache.xmlbeans.XmlObject;
+import org.apache.xmlbeans.XmlOptions;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTAdjPoint2D;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTCustomGeometry2D;
 import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomRect;
@@ -61,7 +68,7 @@
     }
 
     @Override
-    public int setPath(final Path2D.Double path) {
+    public int setPath(final Path2D path) {
         final CTPath2D ctPath = CTPath2D.Factory.newInstance();
 
         final Rectangle2D bounds = path.getBounds2D();
@@ -117,6 +124,30 @@
         return numPoints;
     }
 
+    /**
+     * @return definition of the shape geometry
+     */
+    @Override
+    public CustomGeometry getGeometry() {
+        final XmlObject xo = getShapeProperties();
+        if (!(xo instanceof CTShapeProperties)) {
+            return null;
+        }
+
+        XmlOptions xop = new XmlOptions(POIXMLTypeLoader.DEFAULT_XML_OPTIONS);
+        xop.setSaveOuter();
+
+        XMLStreamReader staxReader = ((CTShapeProperties)xo).getCustGeom().newXMLStreamReader(xop);
+        CustomGeometry custGeo = PresetGeometries.convertCustomGeometry(staxReader);
+        try {
+            staxReader.close();
+        } catch (XMLStreamException e) {
+            LOG.log(POILogger.WARN,
+                    "An error occurred while closing a Custom Geometry XML Stream Reader: " + e.getMessage());
+        }
+
+        return custGeo;
+    }
 
     @Override
     public Path2D.Double getPath() {
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java
index 4e54712..89f3123 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java
@@ -716,7 +716,6 @@
     }
 
     /**
-     *
      * @return definition of the shape geometry
      */
     @Override
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java
index 199a9f6..9a206a4 100644
--- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java
+++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestPPTX2PNG.java
@@ -47,7 +47,9 @@
     private static final POIDataSamples samples = POIDataSamples.getSlideShowInstance();
     private static final File basedir = null;
     private static final String files =
-        "53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt, keyframes.pptx";
+        "53446.ppt, alterman_security.ppt, alterman_security.pptx, KEY02.pptx, themes.pptx, " +
+        "backgrounds.pptx, layouts.pptx, sample.pptx, shapes.pptx, 54880_chinese.ppt, keyframes.pptx," +
+        "customGeo.pptx, customGeo.ppt";
 
         
     
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFAutoShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFAutoShape.java
index 910c8a0..3a3fa7a 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFAutoShape.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFAutoShape.java
@@ -17,21 +17,122 @@
 
 package org.apache.poi.hslf.usermodel;
 
+import java.awt.geom.Arc2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.poi.ddf.AbstractEscherOptRecord;
+import org.apache.poi.ddf.EscherArrayProperty;
 import org.apache.poi.ddf.EscherContainerRecord;
 import org.apache.poi.ddf.EscherProperties;
-import org.apache.poi.sl.usermodel.*;
+import org.apache.poi.ddf.EscherProperty;
+import org.apache.poi.ddf.EscherSimpleProperty;
+import org.apache.poi.sl.draw.binding.CTAdjPoint2D;
+import org.apache.poi.sl.draw.binding.CTCustomGeometry2D;
+import org.apache.poi.sl.draw.binding.CTPath2D;
+import org.apache.poi.sl.draw.binding.CTPath2DArcTo;
+import org.apache.poi.sl.draw.binding.CTPath2DCubicBezierTo;
+import org.apache.poi.sl.draw.binding.CTPath2DLineTo;
+import org.apache.poi.sl.draw.binding.CTPath2DList;
+import org.apache.poi.sl.draw.binding.CTPath2DMoveTo;
+import org.apache.poi.sl.draw.binding.ObjectFactory;
+import org.apache.poi.sl.draw.geom.CustomGeometry;
+import org.apache.poi.sl.usermodel.AutoShape;
+import org.apache.poi.sl.usermodel.ShapeContainer;
+import org.apache.poi.sl.usermodel.ShapeType;
+import org.apache.poi.sl.usermodel.VerticalAlignment;
 import org.apache.poi.ss.usermodel.ShapeTypes;
+import org.apache.poi.util.BitField;
+import org.apache.poi.util.BitFieldFactory;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
 
 /**
- * Represents an AutoShape.
- * <p>
+ * Represents an AutoShape.<p>
+ *
  * AutoShapes are drawing objects with a particular shape that may be customized through smart resizing and adjustments.
  * See {@link ShapeTypes}
- * </p>
- *
- *  @author Yegor Kozlov
  */
 public class HSLFAutoShape extends HSLFTextShape implements AutoShape<HSLFShape,HSLFTextParagraph> {
+    private static final POILogger LOG = POILogFactory.getLogger(HSLFAutoShape.class);
+
+    static final byte[] SEGMENTINFO_MOVETO   = new byte[]{0x00, 0x40};
+    static final byte[] SEGMENTINFO_LINETO   = new byte[]{0x00, (byte)0xAC};
+    static final byte[] SEGMENTINFO_ESCAPE   = new byte[]{0x01, 0x00};
+    static final byte[] SEGMENTINFO_ESCAPE2  = new byte[]{0x01, 0x20};
+    static final byte[] SEGMENTINFO_CUBICTO  = new byte[]{0x00, (byte)0xAD};
+    // OpenOffice inserts 0xB3 instead of 0xAD.
+    // protected static final byte[] SEGMENTINFO_CUBICTO2 = new byte[]{0x00, (byte)0xB3};
+    static final byte[] SEGMENTINFO_CLOSE    = new byte[]{0x01, (byte)0x60};
+    static final byte[] SEGMENTINFO_END      = new byte[]{0x00, (byte)0x80};
+
+    private static final BitField PATH_INFO = BitFieldFactory.getInstance(0xE000);
+    private static final BitField ESCAPE_INFO = BitFieldFactory.getInstance(0x1F00);
+
+    enum PathInfo {
+        lineTo(0),curveTo(1),moveTo(2),close(3),end(4),escape(5),clientEscape(6);
+        private final int flag;
+        PathInfo(int flag) {
+            this.flag = flag;
+        }
+        public int getFlag() {
+            return flag;
+        }
+        static PathInfo valueOf(int flag) {
+            for (PathInfo v : values()) {
+                if (v.flag == flag) {
+                    return v;
+                }
+            }
+            return null;
+        }
+    }
+
+    enum EscapeInfo {
+        EXTENSION(0x0000),
+        ANGLE_ELLIPSE_TO(0x0001),
+        ANGLE_ELLIPSE(0x0002),
+        ARC_TO(0x0003),
+        ARC(0x0004),
+        CLOCKWISE_ARC_TO(0x0005),
+        CLOCKWISE_ARC(0x0006),
+        ELLIPTICAL_QUADRANT_X(0x0007),
+        ELLIPTICAL_QUADRANT_Y(0x0008),
+        QUADRATIC_BEZIER(0x0009),
+        NO_FILL(0X000A),
+        NO_LINE(0X000B),
+        AUTO_LINE(0X000C),
+        AUTO_CURVE(0X000D),
+        CORNER_LINE(0X000E),
+        CORNER_CURVE(0X000F),
+        SMOOTH_LINE(0X0010),
+        SMOOTH_CURVE(0X0011),
+        SYMMETRIC_LINE(0X0012),
+        SYMMETRIC_CURVE(0X0013),
+        FREEFORM(0X0014),
+        FILL_COLOR(0X0015),
+        LINE_COLOR(0X0016);
+
+        private final int flag;
+        EscapeInfo(int flag) {
+            this.flag = flag;
+        }
+        public int getFlag() {
+            return flag;
+        }
+        static EscapeInfo valueOf(int flag) {
+            for (EscapeInfo v : values()) {
+                if (v.flag == flag) {
+                    return v;
+                }
+            }
+            return null;
+        }
+    }
 
     protected HSLFAutoShape(EscherContainerRecord escherRecord, ShapeContainer<HSLFShape,HSLFTextParagraph> parent){
         super(escherRecord, parent);
@@ -72,13 +173,11 @@
     }
 
     /**
-     * Gets adjust value which controls smart resizing of the auto-shape.
+     * Gets adjust value which controls smart resizing of the auto-shape.<p>
      *
-     * <p>
      * The adjustment values are given in shape coordinates:
      * the origin is at the top-left, positive-x is to the right, positive-y is down.
      * The region from (0,0) to (S,S) maps to the geometry box of the shape (S=21600 is a constant).
-     * </p>
      *
      * @param idx the adjust index in the [0, 9] range
      * @return the adjustment value
@@ -90,13 +189,11 @@
     }
 
     /**
-     * Sets adjust value which controls smart resizing of the auto-shape.
+     * Sets adjust value which controls smart resizing of the auto-shape.<p>
      *
-     * <p>
      * The adjustment values are given in shape coordinates:
      * the origin is at the top-left, positive-x is to the right, positive-y is down.
      * The region from (0,0) to (S,S) maps to the geometry box of the shape (S=21600 is a constant).
-     * </p>
      *
      * @param idx the adjust index in the [0, 9] range
      * @param val the adjustment value
@@ -106,4 +203,278 @@
 
         setEscherProperty((short)(EscherProperties.GEOMETRY__ADJUSTVALUE + idx), val);
     }
+
+    @Override
+    public CustomGeometry getGeometry() {
+        return getGeometry(new Path2D.Double());
+    }
+
+    CustomGeometry getGeometry(Path2D path2D) {
+        final ObjectFactory of = new ObjectFactory();
+        final CTCustomGeometry2D cusGeo = of.createCTCustomGeometry2D();
+        cusGeo.setAvLst(of.createCTGeomGuideList());
+        cusGeo.setGdLst(of.createCTGeomGuideList());
+        cusGeo.setAhLst(of.createCTAdjustHandleList());
+        cusGeo.setCxnLst(of.createCTConnectionSiteList());
+
+        final AbstractEscherOptRecord opt = getEscherOptRecord();
+
+        EscherArrayProperty verticesProp = getShapeProp(opt, EscherProperties.GEOMETRY__VERTICES);
+        EscherArrayProperty segmentsProp = getShapeProp(opt, EscherProperties.GEOMETRY__SEGMENTINFO);
+
+        // return empty path if either GEOMETRY__VERTICES or GEOMETRY__SEGMENTINFO is missing, see Bugzilla 54188
+
+        //sanity check
+        if(verticesProp == null) {
+            LOG.log(POILogger.WARN, "Freeform is missing GEOMETRY__VERTICES ");
+            return super.getGeometry();
+        }
+        if(segmentsProp == null) {
+            LOG.log(POILogger.WARN, "Freeform is missing GEOMETRY__SEGMENTINFO ");
+            return super.getGeometry();
+        }
+
+        final Iterator<byte[]> vertIter = verticesProp.iterator();
+        final Iterator<byte[]> segIter = segmentsProp.iterator();
+        final int[] xyPoints = new int[2];
+        boolean isClosed = false;
+
+        final CTPath2DList pathLst = of.createCTPath2DList();
+        final CTPath2D pathCT = of.createCTPath2D();
+        final List<Object> moveLst = pathCT.getCloseOrMoveToOrLnTo();
+        pathLst.getPath().add(pathCT);
+        cusGeo.setPathLst(pathLst);
+
+        while (segIter.hasNext()) {
+            byte[] segElem = segIter.next();
+            HSLFFreeformShape.PathInfo pi = getPathInfo(segElem);
+            if (pi == null) {
+                continue;
+            }
+            switch (pi) {
+                case escape: {
+                    handleEscapeInfo(pathCT, path2D, segElem, vertIter);
+                    break;
+                }
+                case moveTo:
+                    if (vertIter.hasNext()) {
+                        final CTPath2DMoveTo m = of.createCTPath2DMoveTo();
+                        m.setPt(fillPoint(vertIter.next(), xyPoints));
+                        moveLst.add(m);
+                        path2D.moveTo(xyPoints[0], xyPoints[1]);
+                    }
+                    break;
+                case lineTo:
+                    if (vertIter.hasNext()) {
+                        final CTPath2DLineTo m = of.createCTPath2DLineTo();
+                        m.setPt(fillPoint(vertIter.next(), xyPoints));
+                        moveLst.add(m);
+                        path2D.lineTo(xyPoints[0], xyPoints[1]);
+                    }
+                    break;
+                case curveTo: {
+                    final CTPath2DCubicBezierTo m = of.createCTPath2DCubicBezierTo();
+                    List<CTAdjPoint2D> mLst = m.getPt();
+
+                    int[] pts = new int[6];
+
+                    for (int i=0; vertIter.hasNext() && i<3; i++) {
+                        mLst.add(fillPoint(vertIter.next(), xyPoints));
+                        pts[i*2] = xyPoints[0];
+                        pts[i*2+1] = xyPoints[1];
+                        if (i == 2) {
+                            moveLst.add(m);
+                            path2D.curveTo(pts[0], pts[1], pts[2], pts[3], pts[4], pts[5]);
+                        }
+                    }
+                    break;
+                }
+                case close:
+                    moveLst.add(of.createCTPath2DClose());
+                    path2D.closePath();
+                    isClosed = true;
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        EscherSimpleProperty shapePath = getShapeProp(opt, EscherProperties.GEOMETRY__SHAPEPATH);
+        HSLFFreeformShape.ShapePath sp = HSLFFreeformShape.ShapePath.valueOf(shapePath == null ? 1 : shapePath.getPropertyValue());
+        if ((sp == HSLFFreeformShape.ShapePath.LINES_CLOSED || sp == HSLFFreeformShape.ShapePath.CURVES_CLOSED) && !isClosed) {
+            moveLst.add(of.createCTPath2DClose());
+            path2D.closePath();
+        }
+
+        EscherSimpleProperty geoLeft = getShapeProp(opt, EscherProperties.GEOMETRY__LEFT);
+        EscherSimpleProperty geoRight = getShapeProp(opt, EscherProperties.GEOMETRY__RIGHT);
+        EscherSimpleProperty geoTop = getShapeProp(opt, EscherProperties.GEOMETRY__TOP);
+        EscherSimpleProperty geoBottom = getShapeProp(opt, EscherProperties.GEOMETRY__BOTTOM);
+
+        final Rectangle2D bounds;
+        if (geoLeft != null && geoRight != null && geoTop != null && geoBottom != null) {
+            bounds = new Rectangle2D.Double();
+            bounds.setFrameFromDiagonal(
+                new Point2D.Double(geoLeft.getPropertyValue(), geoTop.getPropertyValue()),
+                new Point2D.Double(geoRight.getPropertyValue(), geoBottom.getPropertyValue())
+            );
+        } else {
+            bounds = path2D.getBounds2D();
+        }
+
+        pathCT.setW((int)Math.rint(bounds.getWidth()));
+        pathCT.setH((int)Math.rint(bounds.getHeight()));
+
+        return new CustomGeometry(cusGeo);
+    }
+
+    private void handleEscapeInfo(CTPath2D pathCT, Path2D path2D, byte[] segElem, Iterator<byte[]> vertIter) {
+        final ObjectFactory of = new ObjectFactory();
+        HSLFFreeformShape.EscapeInfo ei = getEscapeInfo(segElem);
+        switch (ei) {
+            case EXTENSION:
+                break;
+            case ANGLE_ELLIPSE_TO:
+                break;
+            case ANGLE_ELLIPSE:
+                break;
+            case ARC_TO: {
+                // The first two POINT values specify the bounding rectangle of the ellipse.
+                // The second two POINT values specify the radial vectors for the ellipse.
+                // The radial vectors are cast from the center of the bounding rectangle.
+                // The path starts at the POINT where the first radial vector intersects the
+                // bounding rectangle and goes to the POINT where the second radial vector
+                // intersects the bounding rectangle. The drawing direction is always counterclockwise.
+                // If the path has already been started, a line is drawn from the last POINT to
+                // the starting POINT of the arc; otherwise, a new path is started.
+                // The number of arc segments drawn equals the number of segments divided by four.
+
+                int[] r1 = new int[2], r2 = new int[2], start = new int[2], end = new int[2];
+                fillPoint(vertIter.next(), r1);
+                fillPoint(vertIter.next(), r2);
+                fillPoint(vertIter.next(), start);
+                fillPoint(vertIter.next(), end);
+
+                Arc2D arc2D = new Arc2D.Double();
+                Rectangle2D.Double bounds = new Rectangle2D.Double();
+                bounds.setFrameFromDiagonal(xy2p(r1), xy2p(r2));
+                arc2D.setFrame(bounds);
+                arc2D.setAngles(xy2p(start), xy2p(end));
+                path2D.append(arc2D, true);
+
+
+                CTPath2DArcTo arcTo = of.createCTPath2DArcTo();
+                arcTo.setHR(d2s(bounds.getHeight()/2.0));
+                arcTo.setWR(d2s(bounds.getWidth()/2.0));
+
+                arcTo.setStAng(d2s(-arc2D.getAngleStart()*60000.));
+                arcTo.setSwAng(d2s(-arc2D.getAngleExtent()*60000.));
+
+                pathCT.getCloseOrMoveToOrLnTo().add(arcTo);
+
+                break;
+            }
+            case ARC:
+                break;
+            case CLOCKWISE_ARC_TO:
+                break;
+            case CLOCKWISE_ARC:
+                break;
+            case ELLIPTICAL_QUADRANT_X:
+                break;
+            case ELLIPTICAL_QUADRANT_Y:
+                break;
+            case QUADRATIC_BEZIER:
+                break;
+            case NO_FILL:
+                break;
+            case NO_LINE:
+                break;
+            case AUTO_LINE:
+                break;
+            case AUTO_CURVE:
+                break;
+            case CORNER_LINE:
+                break;
+            case CORNER_CURVE:
+                break;
+            case SMOOTH_LINE:
+                break;
+            case SMOOTH_CURVE:
+                break;
+            case SYMMETRIC_LINE:
+                break;
+            case SYMMETRIC_CURVE:
+                break;
+            case FREEFORM:
+                break;
+            case FILL_COLOR:
+                break;
+            case LINE_COLOR:
+                break;
+            default:
+                break;
+        }
+    }
+
+    private static String d2s(double d) {
+        return Integer.toString((int)Math.rint(d));
+    }
+
+    private static Point2D xy2p(int[] xyPoints) {
+        return new Point2D.Double(xyPoints[0],xyPoints[1]);
+    }
+
+    private static HSLFFreeformShape.PathInfo getPathInfo(byte[] elem) {
+        int elemUS = LittleEndian.getUShort(elem, 0);
+        int pathInfo = PATH_INFO.getValue(elemUS);
+        return HSLFFreeformShape.PathInfo.valueOf(pathInfo);
+    }
+
+    private static HSLFFreeformShape.EscapeInfo getEscapeInfo(byte[] elem) {
+        int elemUS = LittleEndian.getUShort(elem, 0);
+        int escInfo = ESCAPE_INFO.getValue(elemUS);
+        return HSLFFreeformShape.EscapeInfo.valueOf(escInfo);
+    }
+
+
+    private static <T extends EscherProperty> T getShapeProp(AbstractEscherOptRecord opt, int propId) {
+        T prop = getEscherProperty(opt, (short)(propId + 0x4000));
+        if (prop == null) {
+            prop = getEscherProperty(opt, propId);
+        }
+        return prop;
+    }
+
+    private CTAdjPoint2D fillPoint(byte[] xyMaster, int[] xyPoints) {
+        if (xyMaster == null || xyPoints == null) {
+            LOG.log(POILogger.WARN, "Master bytes or points not set - ignore point");
+            return null;
+        }
+        if ((xyMaster.length != 4 && xyMaster.length != 8) || xyPoints.length != 2) {
+            LOG.log(POILogger.WARN, "Invalid number of master bytes for a single point - ignore point");
+            return null;
+        }
+
+        int x, y;
+        if (xyMaster.length == 4) {
+            x = LittleEndian.getShort(xyMaster, 0);
+            y = LittleEndian.getShort(xyMaster, 2);
+        } else {
+            x = LittleEndian.getInt(xyMaster, 0);
+            y = LittleEndian.getInt(xyMaster, 4);
+        }
+
+        xyPoints[0] = x;
+        xyPoints[1] = y;
+
+        return toPoint(xyPoints);
+    }
+
+    private static CTAdjPoint2D toPoint(int[] xyPoints) {
+        CTAdjPoint2D pt = new CTAdjPoint2D();
+        pt.setX(Integer.toString(xyPoints[0]));
+        pt.setY(Integer.toString(xyPoints[1]));
+        return pt;
+    }
 }
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java
index ee0a2d0..63885af 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java
@@ -49,60 +49,61 @@
 /**
  * Represents functionality provided by the 'Fill Effects' dialog in PowerPoint.
  */
+@SuppressWarnings("WeakerAccess")
 public final class HSLFFill {
     private static final POILogger LOG = POILogFactory.getLogger(HSLFFill.class);
 
     /**
      *  Fill with a solid color
      */
-    public static final int FILL_SOLID = 0;
+    static final int FILL_SOLID = 0;
 
     /**
      *  Fill with a pattern (bitmap)
      */
-    public static final int FILL_PATTERN = 1;
+    static final int FILL_PATTERN = 1;
 
     /**
      *  A texture (pattern with its own color map)
      */
-    public static final int FILL_TEXTURE = 2;
+    static final int FILL_TEXTURE = 2;
 
     /**
      *  Center a picture in the shape
      */
-    public static final int FILL_PICTURE = 3;
+    static final int FILL_PICTURE = 3;
 
     /**
      *  Shade from start to end points
      */
-    public static final int FILL_SHADE = 4;
+    static final int FILL_SHADE = 4;
 
     /**
      *  Shade from bounding rectangle to end point
      */
-    public static final int FILL_SHADE_CENTER = 5;
+    static final int FILL_SHADE_CENTER = 5;
 
     /**
      *  Shade from shape outline to end point
      */
-    public static final int FILL_SHADE_SHAPE = 6;
+    static final int FILL_SHADE_SHAPE = 6;
 
     /**
      *  Similar to FILL_SHADE, but the fill angle
      *  is additionally scaled by the aspect ratio of
      *  the shape. If shape is square, it is the same as FILL_SHADE
      */
-    public static final int FILL_SHADE_SCALE = 7;
+    static final int FILL_SHADE_SCALE = 7;
 
     /**
      *  shade to title
      */
-    public static final int FILL_SHADE_TITLE = 8;
+    static final int FILL_SHADE_TITLE = 8;
 
     /**
      *  Use the background fill color/pattern
      */
-    public static final int FILL_BACKGROUND = 9;
+    static final int FILL_BACKGROUND = 9;
 
     /**
      * A bit that specifies whether the RecolorFillAsPicture bit is set.
@@ -214,7 +215,7 @@
     private HSLFShape shape;
 
     /**
-     * Construct a <code>Fill</code> object for a shape.
+     * Construct a {@code Fill} object for a shape.
      * Fill information will be read from shape's escher properties.
      *
      * @param shape the shape this background applies to
@@ -279,7 +280,7 @@
             
             @Override
             public ColorStyle[] getGradientColors() {
-                ColorStyle cs[];
+                ColorStyle[] cs;
                 if (colorCnt == 0) {
                     cs = new ColorStyle[2];
                     cs[0] = wrapColor(getBackgroundColor());
@@ -288,7 +289,7 @@
                     cs = new ColorStyle[colorCnt];
                     int idx = 0;
                     // TODO: handle palette colors and alpha(?) value 
-                    for (byte data[] : ep) {
+                    for (byte[] data : ep) {
                         EscherColorRef ecr = new EscherColorRef(data, 0, 4);
                         cs[idx++] = wrapColor(shape.getColor(ecr));
                     }
@@ -302,13 +303,13 @@
             
             @Override
             public float[] getGradientFractions() {
-                float frc[];
+                float[] frc;
                 if (colorCnt == 0) {
                     frc = new float[]{0, 1};
                 } else {
                     frc = new float[colorCnt];
                     int idx = 0;
-                    for (byte data[] : ep) {
+                    for (byte[] data : ep) {
                         double pos = Units.fixedPointToDouble(LittleEndian.getInt(data, 4));
                         frc[idx++] = (float)pos;
                     }
@@ -354,7 +355,7 @@
 
     /**
      * Returns fill type.
-     * Must be one of the <code>FILL_*</code> constants defined in this class.
+     * Must be one of the {@code FILL_*} constants defined in this class.
      *
      * @return type of fill
      */
@@ -364,9 +365,7 @@
         return prop == null ? FILL_SOLID : prop.getPropertyValue();
     }
 
-    /**
-     */
-    protected void afterInsert(HSLFSheet sh){
+    void afterInsert(HSLFSheet sh){
         AbstractEscherOptRecord opt = shape.getEscherOptRecord();
         EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__PATTERNTEXTURE);
         if(p != null) {
@@ -379,7 +378,7 @@
     }
 
     @SuppressWarnings("resource")
-    protected EscherBSERecord getEscherBSERecord(int idx){
+    EscherBSERecord getEscherBSERecord(int idx){
         HSLFSheet sheet = shape.getSheet();
         if(sheet == null) {
             LOG.log(POILogger.DEBUG, "Fill has not yet been assigned to a sheet");
@@ -399,7 +398,7 @@
 
     /**
      * Sets fill type.
-     * Must be one of the <code>FILL_*</code> constants defined in this class.
+     * Must be one of the {@code FILL_*} constants defined in this class.
      *
      * @param type type of the fill
      */
@@ -415,10 +414,10 @@
         AbstractEscherOptRecord opt = shape.getEscherOptRecord();
         EscherSimpleProperty p = HSLFShape.getEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST);
         int propVal = (p == null) ? 0 : p.getPropertyValue();
-        
+
         return (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal))
             ? null
-            : shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY, -1);
+            : shape.getColor(EscherProperties.FILL__FILLCOLOR, EscherProperties.FILL__FILLOPACITY);
     }
 
     /**
@@ -462,7 +461,7 @@
 
         return (FILL_USE_FILLED.isSet(propVal) && !FILL_FILLED.isSet(propVal))
             ? null
-            : shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY, -1);
+            : shape.getColor(EscherProperties.FILL__FILLBACKCOLOR, EscherProperties.FILL__FILLOPACITY);
     }
 
     /**
@@ -480,7 +479,7 @@
     }
 
     /**
-     * <code>PictureData</code> object used in a texture, pattern of picture fill.
+     * {@code PictureData} object used in a texture, pattern of picture fill.
      */
     @SuppressWarnings("resource")
     public HSLFPictureData getPictureData(){
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java
index 0b2d322..10eeb7b 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java
@@ -23,20 +23,16 @@
 import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 
 import org.apache.poi.ddf.AbstractEscherOptRecord;
 import org.apache.poi.ddf.EscherArrayProperty;
 import org.apache.poi.ddf.EscherContainerRecord;
 import org.apache.poi.ddf.EscherProperties;
-import org.apache.poi.ddf.EscherProperty;
 import org.apache.poi.ddf.EscherSimpleProperty;
 import org.apache.poi.sl.usermodel.FreeformShape;
 import org.apache.poi.sl.usermodel.ShapeContainer;
 import org.apache.poi.sl.usermodel.ShapeType;
-import org.apache.poi.util.BitField;
-import org.apache.poi.util.BitFieldFactory;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
@@ -53,79 +49,6 @@
 public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformShape<HSLFShape,HSLFTextParagraph> {
     private static final POILogger LOG = POILogFactory.getLogger(HSLFFreeformShape.class);
 
-    private static final byte[] SEGMENTINFO_MOVETO   = new byte[]{0x00, 0x40};
-    private static final byte[] SEGMENTINFO_LINETO   = new byte[]{0x00, (byte)0xAC};
-    private static final byte[] SEGMENTINFO_ESCAPE   = new byte[]{0x01, 0x00};
-    private static final byte[] SEGMENTINFO_ESCAPE2  = new byte[]{0x01, 0x20};
-    private static final byte[] SEGMENTINFO_CUBICTO  = new byte[]{0x00, (byte)0xAD};
-    // OpenOffice inserts 0xB3 instead of 0xAD.
-    // private static final byte[] SEGMENTINFO_CUBICTO2 = new byte[]{0x00, (byte)0xB3};
-    private static final byte[] SEGMENTINFO_CLOSE    = new byte[]{0x01, (byte)0x60};
-    private static final byte[] SEGMENTINFO_END      = new byte[]{0x00, (byte)0x80};
-
-    private static final BitField PATH_INFO = BitFieldFactory.getInstance(0xE000);
-    // private static final BitField ESCAPE_INFO = BitFieldFactory.getInstance(0x1F00);
-
-    enum PathInfo {
-        lineTo(0),curveTo(1),moveTo(2),close(3),end(4),escape(5),clientEscape(6);
-        private final int flag;
-        PathInfo(int flag) {
-            this.flag = flag;
-        }
-        public int getFlag() {
-            return flag;
-        }
-        static PathInfo valueOf(int flag) {
-            for (PathInfo v : values()) {
-                if (v.flag == flag) {
-                    return v;
-                }
-            }
-            return null;
-        }
-    }
-
-    enum EscapeInfo {
-        EXTENSION(0x0000),
-        ANGLE_ELLIPSE_TO(0x0001),
-        ANGLE_ELLIPSE(0x0002),
-        ARC_TO(0x0003),
-        ARC(0x0004),
-        CLOCKWISE_ARC_TO(0x0005),
-        CLOCKWISE_ARC(0x0006),
-        ELLIPTICAL_QUADRANT_X(0x0007),
-        ELLIPTICAL_QUADRANT_Y(0x0008),
-        QUADRATIC_BEZIER(0x0009),
-        NO_FILL(0X000A),
-        NO_LINE(0X000B),
-        AUTO_LINE(0X000C),
-        AUTO_CURVE(0X000D),
-        CORNER_LINE(0X000E),
-        CORNER_CURVE(0X000F),
-        SMOOTH_LINE(0X0010),
-        SMOOTH_CURVE(0X0011),
-        SYMMETRIC_LINE(0X0012),
-        SYMMETRIC_CURVE(0X0013),
-        FREEFORM(0X0014),
-        FILL_COLOR(0X0015),
-        LINE_COLOR(0X0016);
-
-        private final int flag;
-        EscapeInfo(int flag) {
-            this.flag = flag;
-        }
-        public int getFlag() {
-            return flag;
-        }
-        static EscapeInfo valueOf(int flag) {
-            for (EscapeInfo v : values()) {
-                if (v.flag == flag) {
-                    return v;
-                }
-            }
-            return null;
-        }
-    }
 
     enum ShapePath {
         LINES(0),
@@ -182,9 +105,9 @@
     }
 
     @Override
-    public int setPath(Path2D.Double path) {
+    public int setPath(Path2D path) {
         Rectangle2D bounds = path.getBounds2D();
-        PathIterator it = path.getPathIterator(new AffineTransform());
+        PathIterator it = path.getPathIterator(null);
 
         List<byte[]> segInfo = new ArrayList<>();
         List<Point2D.Double> pntInfo = new ArrayList<>();
@@ -275,187 +198,24 @@
     }
 
     @Override
-    public Path2D.Double getPath(){
-        AbstractEscherOptRecord opt = getEscherOptRecord();
+    public Path2D getPath(){
+        Path2D path2D = new Path2D.Double();
+        getGeometry(path2D);
 
-        EscherArrayProperty verticesProp = getShapeProp(opt, EscherProperties.GEOMETRY__VERTICES);
-        EscherArrayProperty segmentsProp = getShapeProp(opt, EscherProperties.GEOMETRY__SEGMENTINFO);
-
-        // return empty path if either GEOMETRY__VERTICES or GEOMETRY__SEGMENTINFO is missing, see Bugzilla 54188
-        Path2D.Double path = new Path2D.Double();
-
-        //sanity check
-        if(verticesProp == null) {
-            LOG.log(POILogger.WARN, "Freeform is missing GEOMETRY__VERTICES ");
-            return path;
-        }
-        if(segmentsProp == null) {
-            LOG.log(POILogger.WARN, "Freeform is missing GEOMETRY__SEGMENTINFO ");
-            return path;
-        }
-
-        Iterator<byte[]> vertIter = verticesProp.iterator();
-        Iterator<byte[]> segIter = segmentsProp.iterator();
-        double xyPoints[] = new double[2];
-        
-        while (vertIter.hasNext() && segIter.hasNext()) {
-            byte[] segElem = segIter.next();
-            PathInfo pi = getPathInfo(segElem);
-            if (pi != null) {
-                switch (pi) {
-                    case escape: {
-                        // handleEscapeInfo(path, segElem, vertIter);
-                        break;
-                    }
-                    case moveTo: {
-                        fillPoint(vertIter.next(), xyPoints);
-                        double x = xyPoints[0];
-                        double y = xyPoints[1];
-                        path.moveTo(x, y);
-                        break;
-                    }
-                    case curveTo: {
-                        fillPoint(vertIter.next(), xyPoints);
-                        double x1 = xyPoints[0];
-                        double y1 = xyPoints[1];
-                        fillPoint(vertIter.next(), xyPoints);
-                        double x2 = xyPoints[0];
-                        double y2 = xyPoints[1];
-                        fillPoint(vertIter.next(), xyPoints);
-                        double x3 = xyPoints[0];
-                        double y3 = xyPoints[1];
-                        path.curveTo(x1, y1, x2, y2, x3, y3);
-                        break;
-                    }
-                    case lineTo:
-                        if (vertIter.hasNext()) {
-                            fillPoint(vertIter.next(), xyPoints);
-                            double x = xyPoints[0];
-                            double y = xyPoints[1];
-                            path.lineTo(x, y);
-                        }
-                        break;
-                    case close:
-                        path.closePath();
-                        break;
-                    default:
-                        break;
-                }
-            }
-        }
-
-        EscherSimpleProperty shapePath = getShapeProp(opt, EscherProperties.GEOMETRY__SHAPEPATH);
-        ShapePath sp = ShapePath.valueOf(shapePath == null ? 1 : shapePath.getPropertyValue());
-        if (sp == ShapePath.LINES_CLOSED || sp == ShapePath.CURVES_CLOSED) {
-            path.closePath();
-        }
-        
+        Rectangle2D bounds = path2D.getBounds2D();
         Rectangle2D anchor = getAnchor();
-        Rectangle2D bounds = path.getBounds2D();
         AffineTransform at = new AffineTransform();
         at.translate(anchor.getX(), anchor.getY());
         at.scale(
                 anchor.getWidth()/bounds.getWidth(),
                 anchor.getHeight()/bounds.getHeight()
         );
-        return new Path2D.Double(at.createTransformedShape(path));
-    }
-    
-    private void fillPoint(byte xyMaster[], double xyPoints[]) {
-        if (xyMaster == null || xyPoints == null) {
-            LOG.log(POILogger.WARN, "Master bytes or points not set - ignore point");
-            return;
-        }
-        if ((xyMaster.length != 4 && xyMaster.length != 8) || xyPoints.length != 2) {
-            LOG.log(POILogger.WARN, "Invalid number of master bytes for a single point - ignore point");
-            return;
-        }
-        
-        int x, y;
-        if (xyMaster.length == 4) {
-            x = LittleEndian.getShort(xyMaster, 0);
-            y = LittleEndian.getShort(xyMaster, 2);
-        } else {
-            x = LittleEndian.getInt(xyMaster, 0);
-            y = LittleEndian.getInt(xyMaster, 4);
-        }
-        
-        xyPoints[0] = Units.masterToPoints(x);
-        xyPoints[1] = Units.masterToPoints(y);
-    }
-    
-    private static <T extends EscherProperty> T getShapeProp(AbstractEscherOptRecord opt, int propId) {
-        T prop = getEscherProperty(opt, (short)(propId + 0x4000));
-        if (prop == null) {
-            prop = getEscherProperty(opt, propId);
-        }
-        return prop;
-    }
-    
-//    private void handleEscapeInfo(Path2D path, byte segElem[], Iterator<byte[]> vertIter) {
-//        EscapeInfo ei = getEscapeInfo(segElem);
-//        switch (ei) {
-//            case EXTENSION:
-//                break;
-//            case ANGLE_ELLIPSE_TO:
-//                break;
-//            case ANGLE_ELLIPSE:
-//                break;
-//            case ARC_TO:
-//                break;
-//            case ARC:
-//                break;
-//            case CLOCKWISE_ARC_TO:
-//                break;
-//            case CLOCKWISE_ARC:
-//                break;
-//            case ELLIPTICAL_QUADRANT_X:
-//                break;
-//            case ELLIPTICAL_QUADRANT_Y:
-//                break;
-//            case QUADRATIC_BEZIER:
-//                break;
-//            case NO_FILL:
-//                break;
-//            case NO_LINE:
-//                break;
-//            case AUTO_LINE:
-//                break;
-//            case AUTO_CURVE:
-//                break;
-//            case CORNER_LINE:
-//                break;
-//            case CORNER_CURVE:
-//                break;
-//            case SMOOTH_LINE:
-//                break;
-//            case SMOOTH_CURVE:
-//                break;
-//            case SYMMETRIC_LINE:
-//                break;
-//            case SYMMETRIC_CURVE:
-//                break;
-//            case FREEFORM:
-//                break;
-//            case FILL_COLOR:
-//                break;
-//            case LINE_COLOR:
-//                break;
-//            default:
-//                break;
-//        }
-//    }
-    
 
-    private static PathInfo getPathInfo(byte elem[]) {
-        int elemUS = LittleEndian.getUShort(elem, 0);
-        int pathInfo = PATH_INFO.getValue(elemUS);
-        return PathInfo.valueOf(pathInfo);
+        path2D.transform(at);
+
+
+        return path2D;
     }
-    
-//    private static EscapeInfo getEscapeInfo(byte elem[]) {
-//        int elemUS = LittleEndian.getUShort(elem, 0);
-//        int escInfo = ESCAPE_INFO.getValue(elemUS);
-//        return EscapeInfo.valueOf(escInfo);
-//    }
+
+
 }
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java
index 085d617..f1f2c22 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java
@@ -358,17 +358,18 @@
         _sheet = sheet;
     }
 
-    Color getColor(short colorProperty, short opacityProperty, int defaultColor){
-        AbstractEscherOptRecord opt = getEscherOptRecord();
-        EscherSimpleProperty p = getEscherProperty(opt, colorProperty);
-        if(p == null && defaultColor == -1) return null;
-
-        int val = (p == null) ? defaultColor : p.getPropertyValue();
-
-        EscherColorRef ecr = new EscherColorRef(val);
-        Color col = getColor(ecr);
-        if (col == null) {
-            return null;
+    Color getColor(short colorProperty, short opacityProperty){
+        final AbstractEscherOptRecord opt = getEscherOptRecord();
+        final EscherSimpleProperty colProp = getEscherProperty(opt, colorProperty);
+        final Color col;
+        if (colProp == null) {
+            col = Color.WHITE;
+        } else {
+            EscherColorRef ecr = new EscherColorRef(colProp.getPropertyValue());
+            col = getColor(ecr);
+            if (col == null) {
+                return null;
+            }
         }
 
         double alpha = getAlpha(opacityProperty);
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java
index f9cf74c..acabc68 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java
@@ -164,7 +164,7 @@
             return null;
         }
 
-        Color clr = getColor(EscherProperties.LINESTYLE__COLOR, EscherProperties.LINESTYLE__OPACITY, -1);
+        Color clr = getColor(EscherProperties.LINESTYLE__COLOR, EscherProperties.LINESTYLE__OPACITY);
         return clr == null ? null : clr;
     }
 
@@ -179,7 +179,7 @@
             return null;
         }
 
-        Color clr = getColor(EscherProperties.LINESTYLE__BACKCOLOR, EscherProperties.LINESTYLE__OPACITY, -1);
+        Color clr = getColor(EscherProperties.LINESTYLE__BACKCOLOR, EscherProperties.LINESTYLE__OPACITY);
         return clr == null ? null : clr;
     }
 
@@ -385,7 +385,7 @@
      * @return color of the line. If color is not set returns <code>java.awt.Color.black</code>
      */
     public Color getShadowColor(){
-        Color clr = getColor(EscherProperties.SHADOWSTYLE__COLOR, EscherProperties.SHADOWSTYLE__OPACITY, -1);
+        Color clr = getColor(EscherProperties.SHADOWSTYLE__COLOR, EscherProperties.SHADOWSTYLE__OPACITY);
         return clr == null ? Color.black : clr;
     }
 
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java
index 14301af..26b30ea 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java
@@ -25,7 +25,6 @@
  */
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
-    TestBackground.class,
     TestFreeform.class,
     TestHeadersFooters.class,
     TestHyperlink.class,
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java
index 91d2366..1e3c52d 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java
@@ -87,7 +87,7 @@
     public void test54188() {
 
         HSLFFreeformShape p = new HSLFFreeformShape();
-        Path2D.Double path = p.getPath();
+        Path2D path = p.getPath();
         Path2D.Double emptyPath = new Path2D.Double();
         assertEquals(emptyPath.getBounds2D(), path.getBounds2D());
     }
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java
index 0354d9b..cf8dc76 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/AllHSLFUserModelTests.java
@@ -26,6 +26,7 @@
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
     TestAddingSlides.class,
+    TestBackground.class,
     TestBugs.class,
     TestCounts.class,
     TestMostRecentRecords.class,
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestBackground.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBackground.java
similarity index 99%
rename from src/scratchpad/testcases/org/apache/poi/hslf/model/TestBackground.java
rename to src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBackground.java
index faf61fb..2732d16 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestBackground.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBackground.java
@@ -15,7 +15,7 @@
    limitations under the License.
 ==================================================================== */
 
-package org.apache.poi.hslf.model;
+package org.apache.poi.hslf.usermodel;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
diff --git a/test-data/slideshow/customGeo.ppt b/test-data/slideshow/customGeo.ppt
new file mode 100644
index 0000000..fdb80ed
--- /dev/null
+++ b/test-data/slideshow/customGeo.ppt
Binary files differ
diff --git a/test-data/slideshow/customGeo.pptx b/test-data/slideshow/customGeo.pptx
new file mode 100644
index 0000000..c30e331
--- /dev/null
+++ b/test-data/slideshow/customGeo.pptx
Binary files differ