oox/source/drawingml/transform2dcontext.cxx               |   88 +++++++++++---
 sd/qa/unit/data/pptx/tdf132302_SmartArt_rightArrow.pptx   |binary
 sd/qa/unit/data/pptx/tdf135953_SmartArt_textposition.pptx |binary
 sd/qa/unit/import-tests-smartart.cxx                      |   58 +++++++++
 4 files changed, 129 insertions(+), 17 deletions(-)

New commits:
commit 5df1bb4b1b222be00d25097660c4ee33542896ea
Author:     Regina Henschel <rb.hensc...@t-online.de>
AuthorDate: Sun Jul 24 01:23:32 2022 +0200
Commit:     Regina Henschel <rb.hensc...@t-online.de>
CommitDate: Mon Jul 25 23:07:28 2022 +0200

    tdf#135953 move txXfrm to compensate TextRotateAngle
    
    The text rectangle specified by the txXfrm element rotates around its
    center. We not directly use this rectangle but we use the text area
    rectangle of the preset geometry and crop it by setting distances so,
    that the result equals the txXfrm rectangle. But the text area
    rectangle still rotates at its original center. So we shift the txXfrm
    rectangle so, that it would be at it original place, when rotated with
    TextRotateAngle. We then can use the shifted one to calculate the
    needed distance values.
    
    This patch contains the calculation of the text area rectangle for
    type 'rightArrow' in addition. That fixes tdf#132302.
    
    Change-Id: I59d9aecaaa7ce2ef4502651088ff9576b90873f7
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137396
    Tested-by: Jenkins
    Reviewed-by: Regina Henschel <rb.hensc...@t-online.de>

diff --git a/oox/source/drawingml/transform2dcontext.cxx 
b/oox/source/drawingml/transform2dcontext.cxx
index 4c98db7592b5..da4ad10a168b 100644
--- a/oox/source/drawingml/transform2dcontext.cxx
+++ b/oox/source/drawingml/transform2dcontext.cxx
@@ -20,10 +20,14 @@
 #include <cmath>
 
 #include <drawingml/transform2dcontext.hxx>
-#include <oox/helper/attributelist.hxx>
-#include <oox/drawingml/shape.hxx>
+
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
 #include <drawingml/customshapeproperties.hxx>
 #include <drawingml/textbody.hxx>
+#include <oox/drawingml/shape.hxx>
+#include <oox/helper/attributelist.hxx>
 #include <oox/token/namespaces.hxx>
 
 #include <com/sun/star/awt/Rectangle.hpp>
@@ -190,7 +194,7 @@ bool ConstructPresetTextRectangle(Shape& rShape, 
awt::Rectangle& rRect)
                 return false;
             double fMaxAdj = 50000.0 * nWidth / std::min(nWidth, nHeight);
             fAdj = std::clamp<double>(fAdj, 0, fMaxAdj);
-            double fFactor = fAdj/fMaxAdj/6.0 + 1.0/12.0;
+            double fFactor = fAdj / fMaxAdj / 6.0 + 1.0 / 12.0;
             sal_Int32 nTextLeft = nWidth * fFactor;
             sal_Int32 nTextTop = nHeight * fFactor;
             rRect.X = rShape.getPosition().X + nTextLeft;
@@ -215,15 +219,51 @@ bool ConstructPresetTextRectangle(Shape& rShape, 
awt::Rectangle& rRect)
             rRect.Height = nHeight;
             return true;
         }
+        case XML_rightArrow:
+        {
+            // The identifiers here reflect the guides name value in 
presetShapeDefinitions.xml
+            sal_Int32 nWidth = rShape.getSize().Width;
+            sal_Int32 nHeight = rShape.getSize().Height;
+            if (nWidth == 0 || nHeight == 0)
+                return false;
+            double a1(50000.0);
+            double a2(50000.0);
+            auto aAdjGdList = 
rShape.getCustomShapeProperties()->getAdjustmentGuideList();
+            if (aAdjGdList.size() == 2)
+            {
+                a1 = aAdjGdList[0].maFormula.toDouble();
+                a2 = aAdjGdList[1].maFormula.toDouble();
+                a1 = std::clamp<double>(a1, 0, 100000);
+            }
+            double maxAdj2 = 100000.0 * nWidth / std::min(nWidth, nHeight);
+            a2 = std::clamp<double>(a2, 0, maxAdj2);
+            double dx1 = std::min(nWidth, nHeight) * a2 / 100000.0;
+            double x1 = nWidth - dx1;
+            double dy1 = nHeight * a1 / 200000.0;
+            double y1 = nHeight / 2.0 - dy1; // top
+            double y2 = nHeight / 2.0 + dy1; // bottom
+            double dx2 = y1 * dx1 / (nHeight / 2.0);
+            double x2 = x1 + dx2; // right
+            rRect.X = rShape.getPosition().X; // left = 0
+            rRect.Y = rShape.getPosition().Y + y1;
+            rRect.Width = x2;
+            rRect.Height = y2 - y1;
+            return true;
+        }
         default:
             return false;
     }
 }
+
+basegfx::B2DPoint getCenter(const awt::Rectangle& rRect)
+{
+    return basegfx::B2DPoint(rRect.X + rRect.Width / 2.0, rRect.Y + 
rRect.Height / 2.0);
 }
+} // end namespace
 
 ContextHandlerRef Transform2DContext::onCreateContext( sal_Int32 
aElementToken, const AttributeList& rAttribs )
 {
-    if( mbtxXfrm )
+    if (mbtxXfrm)
     {
         // The child elements <a:off> and <a:ext> of a <dsp:txXfrm> element 
describe the position and
         // size of the text area rectangle. We cannot change the text area 
rectangle directly, because
@@ -232,7 +272,7 @@ ContextHandlerRef Transform2DContext::onCreateContext( 
sal_Int32 aElementToken,
         // and used in TextBodyProperties::pushTextDistances().
         awt::Rectangle aPresetTextRectangle;
         if (!ConstructPresetTextRectangle(mrShape, aPresetTextRectangle))
-            return nullptr; // faulty shape or corrections from txXfrm not 
needed.
+            return nullptr; // faulty shape or text area calculation not 
implemented
 
         switch (aElementToken)
         {
@@ -254,26 +294,40 @@ ContextHandlerRef Transform2DContext::onCreateContext( 
sal_Int32 aElementToken,
                 awt::Rectangle aUnrotatedTxXfrm = aPresetTextRectangle; // 
dummy initialize
                 const OUString sCXValue = rAttribs.getStringDefaulted(XML_cx);
                 const OUString sCYValue = rAttribs.getStringDefaulted(XML_cy);
-                if (!sCXValue.isEmpty() && !sCYValue.isEmpty() && 
mno_txXfrmOffX.has_value()
-                    && mno_txXfrmOffY.has_value())
+                if (!sCXValue.isEmpty() && !sCYValue.isEmpty())
                 {
-                    sal_Int32 ntxXfrmWidth = sCXValue.toInt32();
-                    sal_Int32 ntxXfrmHeight = sCYValue.toInt32();
-                    aUnrotatedTxXfrm.X = mno_txXfrmOffX.value();
-                    aUnrotatedTxXfrm.Y = mno_txXfrmOffY.value();
-                    aUnrotatedTxXfrm.Width = ntxXfrmWidth;
-                    aUnrotatedTxXfrm.Height = ntxXfrmHeight;
+                    aUnrotatedTxXfrm.Width = sCXValue.toInt32();
+                    aUnrotatedTxXfrm.Height = sCYValue.toInt32();
                 }
-                else if (mno_txXfrmOffX.has_value() && 
mno_txXfrmOffY.has_value()) // can it happen?
+                if (mno_txXfrmOffX.has_value() && mno_txXfrmOffY.has_value())
                 {
                     aUnrotatedTxXfrm.X = mno_txXfrmOffX.value();
                     aUnrotatedTxXfrm.Y = mno_txXfrmOffY.value();
                 }
-                else if (!sCXValue.isEmpty() && !sCYValue.isEmpty()) // can it 
happen?
+
+                // Has the txXfrm an own rotation beyond compensation of the 
shape rotation?
+                // Happens e.g. in diagram type 'Detailed Process'.
+                sal_Int32 nAngleDiff
+                    = (mrShape.getRotation() + mno_txXfrmRot.value_or(0)) % 
21600000;
+                if (nAngleDiff != 0)
                 {
-                    aUnrotatedTxXfrm.Width = sCXValue.toInt32();
-                    aUnrotatedTxXfrm.Height = sCYValue.toInt32();
+                    // Rectangle aUnrotatedTxXfrm rotates around its center 
not around text area
+                    // center from preset. We shift aUnrotatedTxXfrm so that 
it is at the original
+                    // position after rotation of text area rectangle from 
preset.
+                    basegfx::B2DPoint aXfrmCenter(getCenter(aUnrotatedTxXfrm));
+                    basegfx::B2DPoint 
aPresetCenter(getCenter(aPresetTextRectangle));
+
+                    if (!aXfrmCenter.equal(aPresetCenter))
+                    {
+                        double fAngleRad = basegfx::deg2rad(nAngleDiff / 
60000.0);
+                        basegfx::B2DHomMatrix aRotMatrix(
+                            
basegfx::utils::createRotateAroundPoint(aPresetCenter, -fAngleRad));
+                        basegfx::B2DPoint aNewCenter(aRotMatrix * aXfrmCenter);
+                        aUnrotatedTxXfrm.X += aNewCenter.getX() - 
aXfrmCenter.getX();
+                        aUnrotatedTxXfrm.Y += aNewCenter.getY() - 
aXfrmCenter.getY();
+                    }
                 }
+
                 // Calculate indent offsets
                 sal_Int32 nOffsetLeft = aUnrotatedTxXfrm.X - 
aPresetTextRectangle.X;
                 sal_Int32 nOffsetTop = aUnrotatedTxXfrm.Y - 
aPresetTextRectangle.Y;
diff --git a/sd/qa/unit/data/pptx/tdf132302_SmartArt_rightArrow.pptx 
b/sd/qa/unit/data/pptx/tdf132302_SmartArt_rightArrow.pptx
new file mode 100644
index 000000000000..7e50ff4005ba
Binary files /dev/null and 
b/sd/qa/unit/data/pptx/tdf132302_SmartArt_rightArrow.pptx differ
diff --git a/sd/qa/unit/data/pptx/tdf135953_SmartArt_textposition.pptx 
b/sd/qa/unit/data/pptx/tdf135953_SmartArt_textposition.pptx
new file mode 100644
index 000000000000..c76e4c7db0cf
Binary files /dev/null and 
b/sd/qa/unit/data/pptx/tdf135953_SmartArt_textposition.pptx differ
diff --git a/sd/qa/unit/import-tests-smartart.cxx 
b/sd/qa/unit/import-tests-smartart.cxx
index 1d2728b5bee4..9dda19f31a65 100644
--- a/sd/qa/unit/import-tests-smartart.cxx
+++ b/sd/qa/unit/import-tests-smartart.cxx
@@ -125,6 +125,8 @@ public:
     void testTdf149551Venn();
     void testTdf149551Gear();
     void testTdf145528Matrix();
+    void testTdf135953TextPosition();
+    void testTdf132302RightArrow();
 
     CPPUNIT_TEST_SUITE(SdImportTestSmartArt);
 
@@ -185,6 +187,8 @@ public:
     CPPUNIT_TEST(testTdf149551Venn);
     CPPUNIT_TEST(testTdf149551Gear);
     CPPUNIT_TEST(testTdf145528Matrix);
+    CPPUNIT_TEST(testTdf135953TextPosition);
+    CPPUNIT_TEST(testTdf132302RightArrow);
 
     CPPUNIT_TEST_SUITE_END();
 };
@@ -1986,6 +1990,60 @@ void SdImportTestSmartArt::testTdf145528Matrix()
     xDocShRef->DoClose();
 }
 
+void SdImportTestSmartArt::testTdf135953TextPosition()
+{
+    // The file contains a diagram of type "Detailed Process". There the text 
area rectangle
+    // is at the left edge of the shape and rotated there.
+    sd::DrawDocShellRef xDocShRef = loadURL(
+        
m_directories.getURLFromSrc(u"/sd/qa/unit/data/pptx/tdf135953_SmartArt_textposition.pptx"),
+        PPTX);
+    uno::Reference<drawing::XShape> xGroup(getShapeFromPage(0, 0, xDocShRef), 
uno::UNO_QUERY);
+    // shape at index 0 is the background shape
+    uno::Reference<drawing::XShape> xShape = getChildShape(xGroup, 1);
+    CPPUNIT_ASSERT(xShape.is());
+    auto pCustomShape = 
dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape));
+    CPPUNIT_ASSERT(pCustomShape);
+    tools::Rectangle aTextRect;
+    pCustomShape->TakeTextAnchorRect(aTextRect);
+    // without fix the text area rectangle had LT[-2213,2336] and 
RB[4123,4575]. It was not
+    // considered that the txXfrm and the preset text area rectangle have 
different centers in this
+    // case and thus the text was far off.
+    // The used tolerance is estimated.
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(3223), sal_Int32(aTextRect.Left()), 
4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(-1661), sal_Int32(aTextRect.Top()), 
4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(9559), 
sal_Int32(aTextRect.Right()), 4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(578), 
sal_Int32(aTextRect.Bottom()), 4);
+
+    xDocShRef->DoClose();
+}
+
+void SdImportTestSmartArt::testTdf132302RightArrow()
+{
+    // The file contains a diagram of type "Process Arrows". It uses shapes of 
type "rightArrow".
+    // The text starts not at the left edge but in the middle to have space 
for a circle. Error was
+    // that the text starts left and so was hidden by the circle.
+    sd::DrawDocShellRef xDocShRef = loadURL(
+        
m_directories.getURLFromSrc(u"/sd/qa/unit/data/pptx/tdf132302_SmartArt_rightArrow.pptx"),
+        PPTX);
+    uno::Reference<drawing::XShape> xGroup(getShapeFromPage(0, 0, xDocShRef), 
uno::UNO_QUERY);
+    // shape at index 0 is the background shape
+    uno::Reference<drawing::XShape> xShape = getChildShape(xGroup, 1);
+    CPPUNIT_ASSERT(xShape.is());
+    auto pCustomShape = 
dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape));
+    CPPUNIT_ASSERT(pCustomShape);
+    tools::Rectangle aTextRect;
+    pCustomShape->TakeTextAnchorRect(aTextRect);
+    // without fix the text area rectangle had LT[2790,59] and RB[11591,7940]. 
Position and size
+    // given in txXfrm in drawing.xml were not considered.
+    // The used tolerance is estimated.
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(5078), sal_Int32(aTextRect.Left()), 
4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(58), sal_Int32(aTextRect.Top()), 4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(11989), 
sal_Int32(aTextRect.Right()), 4);
+    CPPUNIT_ASSERT_DOUBLES_EQUAL(sal_Int32(7940), 
sal_Int32(aTextRect.Bottom()), 4);
+
+    xDocShRef->DoClose();
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(SdImportTestSmartArt);
 
 CPPUNIT_PLUGIN_IMPLEMENT();

Reply via email to