]> source.dussan.org Git - poi.git/commitdiff
Bug 53191 - Problems with line style when converting ppt to png
authorAndreas Beeker <kiwiwings@apache.org>
Sun, 16 Oct 2016 22:48:25 +0000 (22:48 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Sun, 16 Oct 2016 22:48:25 +0000 (22:48 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1765196 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/sl/draw/DrawFreeformShape.java
src/java/org/apache/poi/sl/draw/DrawShape.java
src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFreeformShape.java

index ee3bebb1c2c2f2b95d6b492e3030533cbf44354a..d8f986881d3ae4f09359054d5d4af46e659bae07 100644 (file)
@@ -18,6 +18,8 @@
 package org.apache.poi.sl.draw;\r
 \r
 import java.awt.Graphics2D;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Path2D;\r
 import java.util.ArrayList;\r
 import java.util.Collection;\r
 import java.util.List;\r
@@ -32,14 +34,23 @@ public class DrawFreeformShape extends DrawAutoShape {
     public DrawFreeformShape(FreeformShape<?,?> shape) {\r
         super(shape);\r
     }\r
-    \r
+\r
     protected Collection<Outline> computeOutlines(Graphics2D graphics) {\r
         List<Outline> lst = new ArrayList<Outline>();\r
-        java.awt.Shape sh = getShape().getPath();\r
-        FillStyle fs = getShape().getFillStyle();\r
-        StrokeStyle ss = getShape().getStrokeStyle();\r
+        FreeformShape<?,?> fsh = getShape();\r
+        Path2D sh = fsh.getPath();\r
+\r
+        AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);\r
+        if (tx == null) {\r
+            tx = new AffineTransform();\r
+        }\r
+\r
+        java.awt.Shape canvasShape = tx.createTransformedShape(sh);\r
+\r
+        FillStyle fs = fsh.getFillStyle();\r
+        StrokeStyle ss = fsh.getStrokeStyle();\r
         Path path = new Path(fs != null, ss != null);\r
-        lst.add(new Outline(sh, path));\r
+        lst.add(new Outline(canvasShape, path));\r
         return lst;\r
     }\r
 \r
index f4f53b52d20aecc707bb7ffdb2afdd16250d6fe8..ff226be906cd5d09dec6767a61f6dafa2c4f3fbd 100644 (file)
@@ -47,85 +47,95 @@ public class DrawShape implements Drawable {
         if (!(shape instanceof PlaceableShape)) return;\r
 \r
         PlaceableShape<?,?> ps = (PlaceableShape<?,?>)shape;\r
+        final boolean isHSLF = ps.getClass().getCanonicalName().toLowerCase(Locale.ROOT).contains("hslf");\r
         AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM);\r
         if (tx == null) tx = new AffineTransform();\r
         final Rectangle2D anchor = tx.createTransformedShape(ps.getAnchor()).getBounds2D();\r
 \r
-        // rotation\r
-        double rotation = ps.getRotation();\r
-        if (rotation != 0.) {\r
-            // PowerPoint rotates shapes relative to the geometric center\r
-            double centerX = anchor.getCenterX();\r
-            double centerY = anchor.getCenterY();\r
-\r
-            // normalize rotation\r
-            rotation %= 360.;\r
-            if (rotation < 0) rotation += 360.;\r
-\r
-            int quadrant = (((int)rotation+45)/90)%4;\r
-            double scaleX = 1.0, scaleY = 1.0;\r
-\r
-            // scale to bounding box (bug #53176)\r
-            if (quadrant == 1 || quadrant == 3) {\r
-                // In quadrant 1 and 3, which is basically a shape in a more or less portrait orientation\r
-                // (45-135 degrees and 225-315 degrees), we need to first rotate the shape by a multiple\r
-                // of 90 degrees and then resize the bounding box to its original bbox. After that we can\r
-                // rotate the shape to the exact rotation amount.\r
-                // It's strange that you'll need to rotate the shape back and forth again, but you can\r
-                // think of it, as if you paint the shape on a canvas. First you rotate the canvas, which might\r
-                // be already (differently) scaled, so you can paint the shape in its default orientation\r
-                // and later on, turn it around again to compare it with its original size ...\r
-\r
-                AffineTransform txs;\r
-                if (ps.getClass().getCanonicalName().toLowerCase(Locale.ROOT).contains("hslf")) {\r
-                    txs = new AffineTransform(tx);\r
-                } else {\r
-                    // this handling is only based on try and error ... not sure why xslf is handled differently.\r
-                    txs = new AffineTransform();\r
-                    txs.translate(centerX, centerY);\r
-                    txs.rotate(Math.PI/2.); // actually doesn't matter if +/- 90 degrees\r
-                    txs.translate(-centerX, -centerY);\r
-                    txs.concatenate(tx);\r
+        char cmds[] = isHSLF ? new char[]{ 'h','v','r' } : new char[]{ 'r','h','v' };\r
+        for (char ch : cmds) {\r
+            switch (ch) {\r
+            case 'h':\r
+                //flip horizontal\r
+                if (ps.getFlipHorizontal()) {\r
+                    graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());\r
+                    graphics.scale(-1, 1);\r
+                    graphics.translate(-anchor.getX(), -anchor.getY());\r
                 }\r
-\r
-                txs.translate(centerX, centerY);\r
-                txs.rotate(Math.PI/2.);\r
-                txs.translate(-centerX, -centerY);\r
-\r
-                Rectangle2D anchor2 = txs.createTransformedShape(ps.getAnchor()).getBounds2D();\r
-\r
-                scaleX = safeScale(anchor.getWidth(), anchor2.getWidth());\r
-                scaleY = safeScale(anchor.getHeight(), anchor2.getHeight());\r
-            } else {\r
-                quadrant = 0;\r
-            }\r
-\r
-            // transformation is applied reversed ...\r
-            graphics.translate(centerX, centerY);\r
-            double rot = Math.toRadians(rotation-quadrant*90.);\r
-            if (rot != 0) {\r
-                graphics.rotate(rot);\r
-            }\r
-            graphics.scale(scaleX, scaleY);\r
-            rot = Math.toRadians(quadrant*90);\r
-            if (rot != 0) {\r
-                graphics.rotate(rot);\r
+                break;\r
+            case 'v':\r
+                //flip vertical\r
+                if (ps.getFlipVertical()) {\r
+                    graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());\r
+                    graphics.scale(1, -1);\r
+                    graphics.translate(-anchor.getX(), -anchor.getY());\r
+                }\r
+                break;\r
+            case 'r':\r
+                // rotation\r
+                double rotation = ps.getRotation();\r
+                if (rotation != 0.) {\r
+                    // PowerPoint rotates shapes relative to the geometric center\r
+                    double centerX = anchor.getCenterX();\r
+                    double centerY = anchor.getCenterY();\r
+\r
+                    // normalize rotation\r
+                    rotation %= 360.;\r
+                    if (rotation < 0) rotation += 360.;\r
+\r
+                    int quadrant = (((int)rotation+45)/90)%4;\r
+                    double scaleX = 1.0, scaleY = 1.0;\r
+\r
+                    // scale to bounding box (bug #53176)\r
+                    if (quadrant == 1 || quadrant == 3) {\r
+                        // In quadrant 1 and 3, which is basically a shape in a more or less portrait orientation\r
+                        // (45-135 degrees and 225-315 degrees), we need to first rotate the shape by a multiple\r
+                        // of 90 degrees and then resize the bounding box to its original bbox. After that we can\r
+                        // rotate the shape to the exact rotation amount.\r
+                        // It's strange that you'll need to rotate the shape back and forth again, but you can\r
+                        // think of it, as if you paint the shape on a canvas. First you rotate the canvas, which might\r
+                        // be already (differently) scaled, so you can paint the shape in its default orientation\r
+                        // and later on, turn it around again to compare it with its original size ...\r
+\r
+                        AffineTransform txs;\r
+                        if (isHSLF) {\r
+                            txs = new AffineTransform(tx);\r
+                        } else {\r
+                            // this handling is only based on try and error ... not sure why xslf is handled differently.\r
+                            txs = new AffineTransform();\r
+                            txs.translate(centerX, centerY);\r
+                            txs.rotate(Math.PI/2.); // actually doesn't matter if +/- 90 degrees\r
+                            txs.translate(-centerX, -centerY);\r
+                            txs.concatenate(tx);\r
+                        }\r
+\r
+                        txs.translate(centerX, centerY);\r
+                        txs.rotate(Math.PI/2.);\r
+                        txs.translate(-centerX, -centerY);\r
+\r
+                        Rectangle2D anchor2 = txs.createTransformedShape(ps.getAnchor()).getBounds2D();\r
+\r
+                        scaleX = safeScale(anchor.getWidth(), anchor2.getWidth());\r
+                        scaleY = safeScale(anchor.getHeight(), anchor2.getHeight());\r
+                    } else {\r
+                        quadrant = 0;\r
+                    }\r
+\r
+                    // transformation is applied reversed ...\r
+                    graphics.translate(centerX, centerY);\r
+                    double rot = Math.toRadians(rotation-quadrant*90.);\r
+                    if (rot != 0) {\r
+                        graphics.rotate(rot);\r
+                    }\r
+                    graphics.scale(scaleX, scaleY);\r
+                    rot = Math.toRadians(quadrant*90);\r
+                    if (rot != 0) {\r
+                        graphics.rotate(rot);\r
+                    }\r
+                    graphics.translate(-centerX, -centerY);\r
+                }\r
+                break;\r
             }\r
-            graphics.translate(-centerX, -centerY);\r
-        }\r
-\r
-        //flip horizontal\r
-        if (ps.getFlipHorizontal()) {\r
-            graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());\r
-            graphics.scale(-1, 1);\r
-            graphics.translate(-anchor.getX(), -anchor.getY());\r
-        }\r
-\r
-        //flip vertical\r
-        if (ps.getFlipVertical()) {\r
-            graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());\r
-            graphics.scale(1, -1);\r
-            graphics.translate(-anchor.getX(), -anchor.getY());\r
         }\r
     }\r
 \r
index 226b2f8bbf461ac1943ff2be51ecd83c8d24d79c..5e045f526ae4da8de4b50820c249930cd6d7276d 100644 (file)
@@ -230,12 +230,12 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
         EscherArrayProperty verticesProp = new EscherArrayProperty((short)(EscherProperties.GEOMETRY__VERTICES + 0x4000), false, null);
         verticesProp.setNumberOfElementsInArray(pntInfo.size());
         verticesProp.setNumberOfElementsInMemory(pntInfo.size());
-        verticesProp.setSizeOfElements(0xFFF0);
+        verticesProp.setSizeOfElements(8);
         for (int i = 0; i < pntInfo.size(); i++) {
             Point2D.Double pnt = pntInfo.get(i);
-            byte[] data = new byte[4];
-            LittleEndian.putShort(data, 0, (short)Units.pointsToMaster(pnt.getX() - bounds.getX()));
-            LittleEndian.putShort(data, 2, (short)Units.pointsToMaster(pnt.getY() - bounds.getY()));
+            byte[] data = new byte[8];
+            LittleEndian.putInt(data, 0, Units.pointsToMaster(pnt.getX() - bounds.getX()));
+            LittleEndian.putInt(data, 4, Units.pointsToMaster(pnt.getY() - bounds.getY()));
             verticesProp.setElement(i, data);
         }
         opt.addEscherProperty(verticesProp);
@@ -282,6 +282,7 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
 
         Iterator<byte[]> vertIter = verticesProp.iterator();
         Iterator<byte[]> segIter = segmentsProp.iterator();
+        double xyPoints[] = new double[2];
         
         while (vertIter.hasNext() && segIter.hasNext()) {
             byte[] segElem = segIter.next();
@@ -292,30 +293,30 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
                     break;
                 }
                 case moveTo: {
-                    byte[] p = vertIter.next();
-                    double x = Units.masterToPoints(LittleEndian.getShort(p, 0));
-                    double y = Units.masterToPoints(LittleEndian.getShort(p, 2));
+                    fillPoint(vertIter.next(), xyPoints);
+                    double x = xyPoints[0];
+                    double y = xyPoints[1];
                     path.moveTo(x,y);
                     break;
                 }
                 case curveTo: {
-                    byte[] p1 = vertIter.next();
-                    double x1 = Units.masterToPoints(LittleEndian.getShort(p1, 0));
-                    double y1 = Units.masterToPoints(LittleEndian.getShort(p1, 2));
-                    byte[] p2 = vertIter.next();
-                    double x2 = Units.masterToPoints(LittleEndian.getShort(p2, 0));
-                    double y2 = Units.masterToPoints(LittleEndian.getShort(p2, 2));
-                    byte[] p3 = vertIter.next();
-                    double x3 = Units.masterToPoints(LittleEndian.getShort(p3, 0));
-                    double y3 = Units.masterToPoints(LittleEndian.getShort(p3, 2));
+                    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()) {
-                        byte[] p = vertIter.next();
-                        double x = Units.masterToPoints(LittleEndian.getShort(p, 0));
-                        double y = Units.masterToPoints(LittleEndian.getShort(p, 2));
+                        fillPoint(vertIter.next(), xyPoints);
+                        double x = xyPoints[0];
+                        double y = xyPoints[1];
                         path.lineTo(x,y);
                     }
                     break;
@@ -344,6 +345,25 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
         return new Path2D.Double(at.createTransformedShape(path));
     }
     
+    private void fillPoint(byte xyMaster[], double xyPoints[]) {
+        if (xyMaster == null || xyPoints == null || (xyMaster.length != 4 && xyMaster.length != 8) || xyPoints.length != 2) {
+            logger.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) {