]> source.dussan.org Git - poi.git/commitdiff
Misc improvements in Freeform shape
authorYegor Kozlov <yegor@apache.org>
Wed, 16 Apr 2008 08:44:07 +0000 (08:44 +0000)
committerYegor Kozlov <yegor@apache.org>
Wed, 16 Apr 2008 08:44:07 +0000 (08:44 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@648624 13f79535-47bb-0310-9956-ffa450edef68

src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java
src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java [new file with mode: 0755]

index e34d651b3da67bcd37d10271b404a13ac73f506d..28e41f8b0991cfbbd2a0ae1db643f99081820a6c 100755 (executable)
@@ -19,9 +19,11 @@ package org.apache.poi.hslf.model;
 import org.apache.poi.ddf.*;\r
 import org.apache.poi.util.LittleEndian;\r
 import org.apache.poi.util.POILogger;\r
+import org.apache.poi.util.HexDump;\r
 \r
 import java.awt.geom.*;\r
 import java.util.ArrayList;\r
+import java.util.Arrays;\r
 \r
 /**\r
  * A "Freeform" shape.\r
@@ -33,6 +35,16 @@ import java.util.ArrayList;
  * @author Yegor Kozlov\r
  */\r
 public class Freeform extends AutoShape {\r
+\r
+    public static final byte[] SEGMENTINFO_MOVETO   = new byte[]{0x00, 0x40};\r
+    public static final byte[] SEGMENTINFO_LINETO   = new byte[]{0x00, (byte)0xAC};\r
+    public static final byte[] SEGMENTINFO_ESCAPE   = new byte[]{0x01, 0x00};\r
+    public static final byte[] SEGMENTINFO_ESCAPE2  = new byte[]{0x01, 0x20};\r
+    public static final byte[] SEGMENTINFO_CUBICTO  = new byte[]{0x00, (byte)0xAD};\r
+    public static final byte[] SEGMENTINFO_CUBICTO2 = new byte[]{0x00, (byte)0xB3}; //OpenOffice inserts 0xB3 instead of 0xAD.\r
+    public static final byte[] SEGMENTINFO_CLOSE    = new byte[]{0x01, (byte)0x60};\r
+    public static final byte[] SEGMENTINFO_END      = new byte[]{0x00, (byte)0x80};\r
+\r
     /**\r
      * Create a Freeform object and initialize it from the supplied Record container.\r
      *\r
@@ -82,36 +94,37 @@ public class Freeform extends AutoShape {
             switch (type) {\r
                 case PathIterator.SEG_MOVETO:\r
                     pntInfo.add(new Point2D.Double(vals[0], vals[1]));\r
-                    segInfo.add(new byte[]{0x00, 0x40});\r
+                    segInfo.add(SEGMENTINFO_MOVETO);\r
                     break;\r
                 case PathIterator.SEG_LINETO:\r
                     pntInfo.add(new Point2D.Double(vals[0], vals[1]));\r
-                    segInfo.add(new byte[]{0x00, (byte)0xAC});\r
-                    segInfo.add(new byte[]{0x01, 0x00 });\r
+                    segInfo.add(SEGMENTINFO_LINETO);\r
+                    segInfo.add(SEGMENTINFO_ESCAPE);\r
                     break;\r
                 case PathIterator.SEG_CUBICTO:\r
                     pntInfo.add(new Point2D.Double(vals[0], vals[1]));\r
                     pntInfo.add(new Point2D.Double(vals[2], vals[3]));\r
                     pntInfo.add(new Point2D.Double(vals[4], vals[5]));\r
-                    segInfo.add(new byte[]{0x00, (byte)0xAD});\r
-                    segInfo.add(new byte[]{0x01, 0x20 });\r
+                    segInfo.add(SEGMENTINFO_CUBICTO);\r
+                    segInfo.add(SEGMENTINFO_ESCAPE2);\r
                     break;\r
                 case PathIterator.SEG_QUADTO:\r
+                    //TODO: figure out how to convert SEG_QUADTO into SEG_CUBICTO  \r
                     logger.log(POILogger.WARN, "SEG_QUADTO is not supported");\r
                     break;\r
                 case PathIterator.SEG_CLOSE:\r
                     pntInfo.add(pntInfo.get(0));\r
-                    segInfo.add(new byte[]{0x00, (byte)0xAC});\r
-                    segInfo.add(new byte[]{0x01, 0x00 });\r
-                    segInfo.add(new byte[]{0x00, (byte)0xAC});\r
-                    segInfo.add(new byte[]{0x01, (byte)0x60});\r
+                    segInfo.add(SEGMENTINFO_LINETO);\r
+                    segInfo.add(SEGMENTINFO_ESCAPE);\r
+                    segInfo.add(SEGMENTINFO_LINETO);\r
+                    segInfo.add(SEGMENTINFO_CLOSE);\r
                     isClosed = true;\r
                     break;\r
             }\r
 \r
             it.next();\r
         }\r
-        if(!isClosed) segInfo.add(new byte[]{0x00, (byte)0xAC});\r
+        if(!isClosed) segInfo.add(SEGMENTINFO_LINETO);\r
         segInfo.add(new byte[]{0x00, (byte)0x80});\r
 \r
         EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
@@ -147,4 +160,81 @@ public class Freeform extends AutoShape {
 \r
         setAnchor(bounds);\r
     }\r
+\r
+    /**\r
+     * Gets the freeform path\r
+     *\r
+     * @return the freeform path\r
+     */\r
+     public GeneralPath getPath(){\r
+        EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID);\r
+        opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 0x4));\r
+\r
+        EscherArrayProperty verticesProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES + 0x4000));\r
+        if(verticesProp == null) verticesProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__VERTICES));\r
+\r
+        EscherArrayProperty segmentsProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO + 0x4000));\r
+        if(segmentsProp == null) segmentsProp = (EscherArrayProperty)getEscherProperty(opt, (short)(EscherProperties.GEOMETRY__SEGMENTINFO));\r
+\r
+        //sanity check\r
+        if(verticesProp == null) {\r
+            logger.log(POILogger.WARN, "Freeform is missing GEOMETRY__VERTICES ");\r
+            return null;\r
+        }\r
+        if(segmentsProp == null) {\r
+            logger.log(POILogger.WARN, "Freeform is missing GEOMETRY__SEGMENTINFO ");\r
+            return null;\r
+        }\r
+\r
+        Rectangle2D bounds = getAnchor2D();\r
+        float right = (float)bounds.getX();\r
+        float bottom = (float)bounds.getY();\r
+\r
+        GeneralPath path = new GeneralPath();\r
+        int numPoints = verticesProp.getNumberOfElementsInArray();\r
+        int numSegments = segmentsProp.getNumberOfElementsInArray();\r
+        for (int i = 0, j = 0; i < numSegments && j < numPoints; i++) {\r
+            byte[] elem = segmentsProp.getElement(i);\r
+            if(Arrays.equals(elem, SEGMENTINFO_MOVETO)){\r
+                byte[] p = verticesProp.getElement(j++);\r
+                short x = LittleEndian.getShort(p, 0);\r
+                short y = LittleEndian.getShort(p, 2);\r
+                path.moveTo(\r
+                        ((float)x*POINT_DPI/MASTER_DPI + right),\r
+                        ((float)y*POINT_DPI/MASTER_DPI + bottom));\r
+            } else if (Arrays.equals(elem, SEGMENTINFO_CUBICTO) || Arrays.equals(elem, SEGMENTINFO_CUBICTO2)){\r
+                i++;\r
+                byte[] p1 = verticesProp.getElement(j++);\r
+                short x1 = LittleEndian.getShort(p1, 0);\r
+                short y1 = LittleEndian.getShort(p1, 2);\r
+                byte[] p2 = verticesProp.getElement(j++);\r
+                short x2 = LittleEndian.getShort(p2, 0);\r
+                short y2 = LittleEndian.getShort(p2, 2);\r
+                byte[] p3 = verticesProp.getElement(j++);\r
+                short x3 = LittleEndian.getShort(p3, 0);\r
+                short y3 = LittleEndian.getShort(p3, 2);\r
+                path.curveTo(\r
+                        ((float)x1*POINT_DPI/MASTER_DPI + right), ((float)y1*POINT_DPI/MASTER_DPI + bottom),\r
+                        ((float)x2*POINT_DPI/MASTER_DPI + right), ((float)y2*POINT_DPI/MASTER_DPI + bottom),\r
+                        ((float)x3*POINT_DPI/MASTER_DPI + right), ((float)y3*POINT_DPI/MASTER_DPI + bottom));\r
+\r
+            } else if (Arrays.equals(elem, SEGMENTINFO_LINETO)){\r
+                i++;\r
+                byte[] pnext = segmentsProp.getElement(i);\r
+                if(Arrays.equals(pnext, SEGMENTINFO_ESCAPE)){\r
+                    if(j + 1 < numPoints){\r
+                        byte[] p = verticesProp.getElement(j++);\r
+                        short x = LittleEndian.getShort(p, 0);\r
+                        short y = LittleEndian.getShort(p, 2);\r
+                        path.lineTo(\r
+                                ((float)x*POINT_DPI/MASTER_DPI + right), ((float)y*POINT_DPI/MASTER_DPI + bottom));\r
+                    }\r
+                } else if (Arrays.equals(pnext, SEGMENTINFO_CLOSE)){\r
+                    path.closePath();\r
+                }\r
+            }\r
+        }\r
+\r
+        return path;\r
+    }\r
 }\r
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestFreeform.java
new file mode 100755 (executable)
index 0000000..5e7e7cc
--- /dev/null
@@ -0,0 +1,80 @@
+/* ====================================================================\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to You under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+       http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+==================================================================== */\r
+package org.apache.poi.hslf.model;\r
+\r
+import junit.framework.TestCase;\r
+import org.apache.poi.hslf.usermodel.SlideShow;\r
+import org.apache.poi.hslf.usermodel.RichTextRun;\r
+import org.apache.poi.hslf.HSLFSlideShow;\r
+\r
+import java.awt.*;\r
+import java.awt.Rectangle;\r
+import java.awt.geom.*;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.ByteArrayInputStream;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * Test Freeform object.\r
+ * The Freeform shape is constructed from java.awt.GeneralPath.\r
+ * Check that the get/set path accessors are consistent.\r
+ * (TODO: verification of Bezier curves is more difficult due to rounding error.  Figure out a test approach for that)\r
+ *\r
+ * @author Yegor Kozlov\r
+ */\r
+public class TestFreeform extends TestCase {\r
+\r
+    public void testClosedPath() throws Exception {\r
+\r
+        GeneralPath path1 = new GeneralPath();\r
+        path1.moveTo(100, 100);\r
+        path1.lineTo(200, 100);\r
+        path1.lineTo(200, 200);\r
+        path1.lineTo(100, 200);\r
+        path1.closePath();\r
+\r
+        Freeform p = new Freeform();\r
+        p.setPath(path1);\r
+\r
+        GeneralPath path2 = p.getPath();\r
+        assertTrue(new Area(path1).equals(new Area(path2)));\r
+    }\r
+\r
+    public void testLine() throws Exception {\r
+\r
+        GeneralPath path1 = new GeneralPath(new Line2D.Double(100, 100, 200, 100));\r
+\r
+        Freeform p = new Freeform();\r
+        p.setPath(path1);\r
+\r
+        GeneralPath path2 = p.getPath();\r
+        assertTrue(new Area(path1).equals(new Area(path2)));\r
+    }\r
+\r
+    public void testRectangle() throws Exception {\r
+\r
+        GeneralPath path1 = new GeneralPath(new Rectangle2D.Double(100, 100, 200, 50));\r
+\r
+        Freeform p = new Freeform();\r
+        p.setPath(path1);\r
+\r
+        GeneralPath path2 = p.getPath();\r
+        assertTrue(new Area(path1).equals(new Area(path2)));\r
+    }\r
+}\r