Bug 53176

Summary: [PATCH] Auto shapes render problem in pptx files
Product: POI Reporter: mdbhaskar <mdbhaskar>
Component: XSLFAssignee: POI Developers List <dev>
Status: RESOLVED FIXED    
Severity: critical CC: mdbhaskar
Priority: P1    
Version: 3.8-FINAL   
Target Milestone: ---   
Hardware: PC   
OS: Windows XP   
Bug Depends on:    
Bug Blocks: 62381    
Attachments: The attachment have the auto shape that is not coming properly after rending using the pptx2png tool
Sample pptx file
[patch] Bug 53176 - scale to bounding box after/in XSLFShape.applyTransform
[patch] Bug 53176 - scale to bounding box after/in XSLFShape.applyTransform II

Description mdbhaskar 2012-05-02 10:52:40 UTC
Created attachment 28707 [details]
The attachment have the auto shape that is not coming properly after rending using the pptx2png tool

I am converting the few PPTX slides to images. few of the slides having the multiple auto shapes (some are custom drawing shapes). all the shapes are not rendering properly.
I have attached one image for understanding the problem.
http://apache-poi.1045710.n5.nabble.com/file/n5679512/problem.png

Is there any limitation on auto shapes rendering. 
Based on the feed back I am raising the bug, please review and let me know what could be the issue?

I am using the poi-3.8-beta5 version jar files.
Comment 1 Yegor Kozlov 2012-05-02 11:10:45 UTC
Please attach the .pptx file.
Comment 2 mdbhaskar 2012-05-03 14:17:35 UTC
Created attachment 28716 [details]
Sample pptx file

Please find attached problematic pptx file. Please let me know what could be the issue?
Comment 3 mdbhaskar 2012-05-07 04:06:53 UTC
I have attached requested sample problematic file. It is one of the severe blocker in my current development project. Could you please provide us the solution, it is very much helpful to development?
Comment 4 Nick Burch 2012-05-09 17:40:19 UTC
(In reply to comment #3)
> It is one of the severe blocker in my current development project. Could you please provide us the solution, it is very much helpful to development?

Apache POI is, like all Apache projects, a volunteer project. We do try to fix all the bugs we can do, but the priorities are set by the people willing to put the time in to do the work!

If this bug is important to you, there are two main options. 

The first is to work on it yourself! If you want to go down this round, please join the dev list, and people will do their best to guide you and give advice on the general area. The second option is pay someone (either via a support contract, or some consultancy) to work on the problem urgently. Otherwise, it will be fixed when volunteer time permits, which depending on the complexity of the issue and how it affects those who are willing to work on it, may be some time...
Comment 5 Andreas Beeker 2013-10-02 22:01:33 UTC
Created attachment 30902 [details]
[patch] Bug 53176 - scale to bounding box after/in XSLFShape.applyTransform

When shapes are rotated their bounding box wasn't resized to the specified anchor.
I have to admit, that it seems to be a bit clumsy to use the graphics-transform to calculate the scaling, but at least it eventually worked ...
I haven't added a test-case, as I don't know how to check for that certain geometrical feature.
Comment 6 Andreas Beeker 2013-10-06 23:26:40 UTC
Comment on attachment 30902 [details]
[patch] Bug 53176 - scale to bounding box after/in XSLFShape.applyTransform

This patch doesn't work as intended. The transformation is applied twice and it only works for the bug example, because its rotated 90°. When you use other values for the rotation, the rendering differs between libre office and office viewer. In LO its somehow skewed whereas in office viewer, the scaling depends on the quadrant of the circle, i.e. for degrees 0-45° it won't be scaled, for deg. 46-135° the height of the bounding box will be scaled to the width and vice versa ...
I hopefully supply a different patch soon ...
Comment 7 Andreas Beeker 2013-10-10 20:15:28 UTC
Comment on attachment 30902 [details]
[patch] Bug 53176 - scale to bounding box after/in XSLFShape.applyTransform

Index: src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java
===================================================================
--- src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java	(revision 1529677)
+++ src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java	(working copy)
@@ -141,11 +141,42 @@
         double rotation = getRotation();
         if (rotation != 0.) {
             // PowerPoint rotates shapes relative to the geometric center
-            double centerX = anchor.getX() + anchor.getWidth() / 2;
-            double centerY = anchor.getY() + anchor.getHeight() / 2;
+            double centerX = anchor.getCenterX();
+            double centerY = anchor.getCenterY();
 
+            // normalize rotation
+            rotation = (360.+(rotation%360.))%360.;
+            int quadrant = (((int)rotation+45)/90)%4;
+            double scaleX = 1.0, scaleY = 1.0;
+
+            // scale to bounding box (bug #53176)
+            if (quadrant == 1 || quadrant == 3) {
+                // In quadrant 1 and 3, which is basically a shape in a more or less portrait orientation (45°-135° and 225°-315°),
+                // we need to first rotate the shape by a multiple of 90° and then resize the bounding box  
+                // to its original bbox. After that we can rotate the shape to the exact rotation amount.
+                // It's strange that you'll need to rotate the shape back and forth again, but you can
+                // think of it, as if you paint the shape on a canvas. First you rotate the canvas, which might
+                // be already (differently) scaled, so you can paint the shape in its default orientation
+                // and later on, turn it around again to compare it with its original size ...
+                AffineTransform txg = new AffineTransform(); // graphics coordinate space
+                AffineTransform txs = new AffineTransform(tx); // shape coordinate space
+                txg.translate(centerX, centerY);
+                txg.rotate(Math.toRadians(quadrant*90));
+                txg.translate(-centerX, -centerY);
+                txs.translate(centerX, centerY);
+                txs.rotate(Math.toRadians(-quadrant*90));
+                txs.translate(-centerX, -centerY);
+                txg.concatenate(txs);
+                Rectangle2D anchor2 = txg.createTransformedShape(getAnchor()).getBounds2D();
+                scaleX = anchor.getWidth() == 0. ? 1.0 : anchor.getWidth() / anchor2.getWidth();
+                scaleY = anchor.getHeight() == 0. ? 1.0 : anchor.getHeight() / anchor2.getHeight();
+            }
+
+            // transformation is applied reversed ...
             graphics.translate(centerX, centerY);
-            graphics.rotate(Math.toRadians(rotation));
+            graphics.rotate(Math.toRadians(rotation-(double)(quadrant*90)));
+            graphics.scale(scaleX, scaleY);
+            graphics.rotate(Math.toRadians(quadrant*90));
             graphics.translate(-centerX, -centerY);
         }
Comment 8 Andreas Beeker 2013-10-10 20:19:16 UTC
Created attachment 30919 [details]
[patch] Bug 53176 - scale to bounding box after/in XSLFShape.applyTransform II

fixed version ... sorry for the double posting (see above), but for a moment it looked like I could edit the existing attachment ..
Comment 9 Yegor Kozlov 2013-11-09 11:46:08 UTC
A very good patch, Andreas! Applied in r1540290. 

I suspect there may be similar rendering issues in the future when we will need to tweak the transformation matrix. When I was writing the pptx rendering engine I tried to cover *most* of the base cases such as rotation and flipping but you never know it for sure with graphics :) 

Regards,
Yegor