oox/source/export/shapes.cxx       |  306 +++++++++++++++++++++++++++++++++----
 sd/qa/unit/data/odp/tdf147919.odp  |binary
 sd/qa/unit/export-tests-ooxml1.cxx |   62 +++++++
 3 files changed, 340 insertions(+), 28 deletions(-)

New commits:
commit 4cc3a144ef2ecc019da42bb9b115267a18e9559f
Author:     Tibor Nagy <nagy.tib...@nisz.hu>
AuthorDate: Fri Apr 29 14:08:17 2022 +0200
Commit:     László Németh <nem...@numbertext.org>
CommitDate: Tue May 10 16:51:54 2022 +0200

    tdf#147919 PPTX export: fix curved and bent connector shape
    
    by calculating and exporting adjustment values to avoid
    of connector lines with bad turns overlapping the
    connected shapes.
    
    Change-Id: I0441508f4fe98b3482e2955df5f41f2cfc2ffb4e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/134109
    Tested-by: Jenkins
    Tested-by: László Németh <nem...@numbertext.org>
    Reviewed-by: László Németh <nem...@numbertext.org>

diff --git a/oox/source/export/shapes.cxx b/oox/source/export/shapes.cxx
index 3308d5ab4492..ed129784ba51 100644
--- a/oox/source/export/shapes.cxx
+++ b/oox/source/export/shapes.cxx
@@ -1411,49 +1411,276 @@ void ShapeExport::WriteGraphicObjectShapePart( const 
Reference< XShape >& xShape
     pFS->endElementNS( mnXmlNamespace, XML_pic );
 }
 
+static void lcl_Rotate(sal_Int32 nAngle, Point center, awt::Point& pt)
+{
+    sal_Int16 nCos, nSin;
+    switch (nAngle)
+    {
+        case 90:
+            nCos = 0;
+            nSin = 1;
+            break;
+        case 180:
+            nCos = -1;
+            nSin = 0;
+            break;
+        case 270:
+            nCos = 0;
+            nSin = -1;
+            break;
+        default:
+            return;
+    }
+    sal_Int32 x = pt.X - center.X();
+    sal_Int32 y = pt.Y - center.Y();
+    pt.X = center.X() + x * nCos - y * nSin;
+    pt.Y = center.Y() + y * nCos + x * nSin;
+}
+
+static void lcl_FlipHFlipV(tools::Polygon aPoly, sal_Int32 nAngle, bool& 
rFlipH, bool& rFlipV)
+{
+    Point aStart = aPoly[0];
+    Point aEnd = aPoly[aPoly.GetSize() - 1];
+
+    if (aStart.X() > aEnd.X() && aStart.Y() > aEnd.Y())
+    {
+        if (nAngle)
+        {
+            if (nAngle == 90)
+                rFlipH = true;
+            if (nAngle == 270)
+                rFlipV = true;
+        }
+        else // 0°
+        {
+            rFlipH = true;
+            rFlipV = true;
+        }
+    }
+
+    if (aStart.X() < aEnd.X() && aStart.Y() < aEnd.Y())
+    {
+        if (nAngle)
+        {
+            if (nAngle != 270)
+            {
+                rFlipH = true;
+                rFlipV = true;
+            }
+            else
+                rFlipH = true;
+        }
+    }
+
+    if (aStart.Y() < aEnd.Y() && aStart.X() > aEnd.X())
+    {
+        if (nAngle)
+        {
+            if (nAngle == 180)
+                rFlipV = true;
+            if (nAngle == 270)
+            {
+                rFlipV = true;
+                rFlipH = true;
+            }
+        }
+        else // 0°
+        {
+            rFlipH = true;
+        }
+    }
+
+    if (aStart.Y() > aEnd.Y() && aStart.X() < aEnd.X())
+    {
+        if (nAngle)
+        {
+            if (nAngle == 90)
+            {
+                rFlipH = true;
+                rFlipV = true;
+            }
+            if (nAngle == 180)
+                rFlipH = true;
+        }
+        else // 0°
+            rFlipV = true;
+    }
+}
+
+static sal_Int32 lcl_GetAngle(tools::Polygon aPoly)
+{
+    sal_Int32 nAngle;
+    Point aStartPoint = aPoly[0];
+    Point aEndPoint = aPoly[aPoly.GetSize() - 1];
+    if (aStartPoint.X() == aPoly[1].X())
+    {
+        if ((aStartPoint.X() < aEndPoint.X() && aStartPoint.Y() > 
aEndPoint.Y())
+            || (aStartPoint.X() > aEndPoint.X() && aStartPoint.Y() < 
aEndPoint.Y()))
+        {
+            nAngle = 90;
+        }
+        else
+            nAngle = 270;
+    }
+    else
+    {
+        if (aStartPoint.X() > aPoly[1].X())
+            nAngle = 180;
+        else
+            nAngle = 0;
+    }
+
+    return nAngle;
+}
+
+// Adjust value decide the position, where the connector should turn.
+static void lcl_GetConnectorAdjustValue(const Reference<XShape>& xShape, 
tools::Polygon aPoly,
+                                        ConnectorType eConnectorType,
+                                        std::vector<std::pair<sal_Int32, 
sal_Int32>>& rAvList)
+{
+    sal_Int32 nAdjCount = 0;
+    if (eConnectorType == ConnectorType_CURVE)
+    {
+        if (aPoly.GetSize() == 4)
+        {
+            if ((aPoly[0].X() == aPoly[1].X() && aPoly[2].X() == aPoly[3].X())
+                || (aPoly[0].Y() == aPoly[1].Y() && aPoly[2].Y() == 
aPoly[3].Y()))
+            {
+                nAdjCount = 1; // curvedConnector3
+            }
+            else
+                nAdjCount = 0; // curvedConnector2
+        }
+        else if (aPoly.GetSize() > 4)
+        {
+            if ((aPoly[2].X() == aPoly[3].X() && aPoly[3].X() == aPoly[4].X())
+                || (aPoly[2].Y() == aPoly[3].Y() && aPoly[3].Y() == 
aPoly[4].Y()))
+            {
+                nAdjCount = 3; // curvedConnector5
+            }
+            else
+                nAdjCount = 2; // curvedConnector4
+        }
+    }
+    else
+    {
+        switch (aPoly.GetSize())
+        {
+            case 3:
+                nAdjCount = 0; // bentConnector2
+                break;
+            case 4:
+                nAdjCount = 1; // bentConnector3
+                break;
+            case 5:
+                nAdjCount = 2; // bentConnector4
+                break;
+            case 6:
+                nAdjCount = 3; // bentConnector5
+                break;
+        }
+    }
+
+    if (nAdjCount)
+    {
+        sal_Int32 nAdjustValue;
+        Point aStart = aPoly[0];
+        Point aEnd = aPoly[aPoly.GetSize() - 1];
+
+        for (sal_Int32 i = 1; i <= nAdjCount; ++i)
+        {
+            Point aPt = aPoly[i];
+
+            if (aEnd.Y() == aStart.Y())
+                aEnd.setY(aStart.Y() + 1);
+            if (aEnd.X() == aStart.X())
+                aEnd.setX(aStart.X() + 1);
+
+            bool bVertical = aPoly[1].X() - aStart.X() != 0 ? true : false;
+            // vertical and horizon alternate
+            if (i % 2 == 1)
+                bVertical = !bVertical;
+
+            if (eConnectorType == ConnectorType_CURVE)
+            {
+                awt::Size aSize = xShape->getSize();
+                awt::Point aShapePosition = xShape->getPosition();
+                tools::Rectangle aBoundRect = aPoly.GetBoundRect();
+
+                if (bVertical)
+                {
+                    if ((aBoundRect.GetSize().Height() - aSize.Height) == 1)
+                        aPt.setY(aPoly[i + 1].Y());
+                    else if (aStart.Y() > aPt.Y())
+                        aPt.setY(aShapePosition.Y);
+                    else
+                        aPt.setY(aShapePosition.Y + aSize.Height);
+                }
+                else
+                {
+                    if ((aBoundRect.GetSize().Width() - aSize.Width) == 1)
+                        aPt.setX(aPoly[i + 1].X());
+                    else if (aStart.X() > aPt.X())
+                        aPt.setX(aShapePosition.X);
+                    else
+                        aPt.setX(aShapePosition.X + aSize.Width);
+                }
+            }
+
+            if (bVertical)
+                nAdjustValue = ((aPt.Y() - aStart.Y()) * 100000) / (aEnd.Y() - 
aStart.Y());
+            else
+                nAdjustValue = ((aPt.X() - aStart.X()) * 100000) / (aEnd.X() - 
aStart.X());
+
+            rAvList.emplace_back(i, nAdjustValue);
+        }
+    }
+}
+
 ShapeExport& ShapeExport::WriteConnectorShape( const Reference< XShape >& 
xShape )
 {
     bool bFlipH = false;
     bool bFlipV = false;
+    sal_Int32 nAngle = 0;
 
     SAL_INFO("oox.shape", "write connector shape");
 
     FSHelperPtr pFS = GetFS();
 
-    const char* sGeometry = "line";
+    OUString sGeometry;
+    std::vector<std::pair<sal_Int32, sal_Int32>> aAdjustValueList;
     Reference< XPropertySet > rXPropSet( xShape, UNO_QUERY );
     Reference< XPropertyState > rXPropState( xShape, UNO_QUERY );
     awt::Point aStartPoint, aEndPoint;
     Reference< XShape > rXShapeA;
     Reference< XShape > rXShapeB;
     PropertyState eState;
-    ConnectorType eConnectorType;
-    if( GETAD( EdgeKind ) ) {
-        mAny >>= eConnectorType;
+    ConnectorType eConnectorType = ConnectorType_STANDARD;
+    GET(eConnectorType, EdgeKind);
 
-        switch( eConnectorType ) {
-            case ConnectorType_CURVE:
-                sGeometry = "curvedConnector3";
-                break;
-            case ConnectorType_STANDARD:
-                sGeometry = "bentConnector3";
-                break;
-            default:
-            case ConnectorType_LINE:
-            case ConnectorType_LINES:
-                sGeometry = "straightConnector1";
-                break;
-        }
+    switch( eConnectorType ) {
+        case ConnectorType_CURVE:
+            sGeometry = "curvedConnector";
+            break;
+        case ConnectorType_STANDARD:
+            sGeometry = "bentConnector";
+            break;
+        default:
+        case ConnectorType_LINE:
+        case ConnectorType_LINES:
+            sGeometry = "straightConnector1";
+            break;
+    }
 
-        if( GETAD( EdgeStartPoint ) ) {
-            mAny >>= aStartPoint;
-            if( GETAD( EdgeEndPoint ) ) {
-                mAny >>= aEndPoint;
-            }
+    if( GETAD( EdgeStartPoint ) ) {
+        mAny >>= aStartPoint;
+        if( GETAD( EdgeEndPoint ) ) {
+            mAny >>= aEndPoint;
         }
-        GET( rXShapeA, EdgeStartConnection );
-        GET( rXShapeB, EdgeEndConnection );
     }
+    GET( rXShapeA, EdgeStartConnection );
+    GET( rXShapeB, EdgeEndConnection );
+
     // Position is relative to group in Word, but relative to anchor of group 
in API.
     if (GetDocumentType() == DOCUMENT_DOCX && !mbUserShapes && m_xParent.is())
     {
@@ -1464,17 +1691,40 @@ ShapeExport& ShapeExport::WriteConnectorShape( const 
Reference< XShape >& xShape
         aEndPoint.Y -= aParentPos.Y;
     }
     EscherConnectorListEntry aConnectorEntry( xShape, aStartPoint, rXShapeA, 
aEndPoint, rXShapeB );
+
+    if (eConnectorType == ConnectorType_CURVE || eConnectorType == 
ConnectorType_STANDARD)
+    {
+        tools::PolyPolygon aPolyPolygon = 
EscherPropertyContainer::GetPolyPolygon(xShape);
+        if (aPolyPolygon.Count() > 0)
+        {
+            tools::Polygon aPoly = aPolyPolygon.GetObject(0);
+            lcl_GetConnectorAdjustValue(xShape, aPoly, eConnectorType, 
aAdjustValueList);
+            nAngle = lcl_GetAngle(aPoly);
+            lcl_FlipHFlipV(aPoly, nAngle, bFlipH, bFlipV);
+            if (nAngle)
+            {
+                Point center((aEndPoint.X + aStartPoint.X) / 2, (aEndPoint.Y + 
aStartPoint.Y) / 2);
+                lcl_Rotate(nAngle, center, aStartPoint);
+                lcl_Rotate(nAngle, center, aEndPoint);
+                nAngle *= 60000;
+            }
+            sGeometry = sGeometry + OUString::number(aAdjustValueList.size() + 
2);
+        }
+    }
+
     tools::Rectangle aRect( Point( aStartPoint.X, aStartPoint.Y ), Point( 
aEndPoint.X, aEndPoint.Y ) );
     if( aRect.getWidth() < 0 ) {
-        bFlipH = true;
         aRect.SetLeft(aEndPoint.X);
         aRect.setWidth( aStartPoint.X - aEndPoint.X );
+        if (eConnectorType == ConnectorType_LINE)
+            bFlipH = true;
     }
 
     if( aRect.getHeight() < 0 ) {
-        bFlipV = true;
         aRect.SetTop(aEndPoint.Y);
         aRect.setHeight( aStartPoint.Y - aEndPoint.Y );
+        if (eConnectorType == ConnectorType_LINE)
+            bFlipV = true;
     }
 
     // tdf#99810 connector shape (cxnSp) is not valid with namespace 'wps'
@@ -1504,9 +1754,9 @@ ShapeExport& ShapeExport::WriteConnectorShape( const 
Reference< XShape >& xShape
 
     // visual shape properties
     pFS->startElementNS(mnXmlNamespace, XML_spPr);
-    WriteTransformation( xShape, aRect, XML_a, bFlipH, bFlipV );
+    WriteTransformation( xShape, aRect, XML_a, bFlipH, bFlipV, nAngle );
     // TODO: write adjustments (ppt export doesn't work well there either)
-    WritePresetShape( sGeometry );
+    WritePresetShape( sGeometry.toUtf8(), aAdjustValueList);
     Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
     if( xShapeProps.is() )
         WriteOutline( xShapeProps );
diff --git a/sd/qa/unit/data/odp/tdf147919.odp 
b/sd/qa/unit/data/odp/tdf147919.odp
new file mode 100644
index 000000000000..2676b0e1c08f
Binary files /dev/null and b/sd/qa/unit/data/odp/tdf147919.odp differ
diff --git a/sd/qa/unit/export-tests-ooxml1.cxx 
b/sd/qa/unit/export-tests-ooxml1.cxx
index db33355b9bc6..a1dac4fbf63c 100644
--- a/sd/qa/unit/export-tests-ooxml1.cxx
+++ b/sd/qa/unit/export-tests-ooxml1.cxx
@@ -48,6 +48,7 @@ using namespace css;
 class SdOOXMLExportTest1 : public SdModelTestBaseXML
 {
 public:
+    void testTdf147919();
     void testTdf130165();
     void testTdf124781();
     void testTdf144914();
@@ -117,6 +118,7 @@ public:
 
     CPPUNIT_TEST_SUITE(SdOOXMLExportTest1);
 
+    CPPUNIT_TEST(testTdf147919);
     CPPUNIT_TEST(testTdf130165);
     CPPUNIT_TEST(testTdf124781);
     CPPUNIT_TEST(testTdf144914);
@@ -213,6 +215,66 @@ void checkFontAttributes( const SdrTextObj* pObj, 
ItemValue nVal, sal_uInt32 nId
 
 }
 
+void SdOOXMLExportTest1::testTdf147919()
+{
+    sd::DrawDocShellRef xDocShRef
+        = 
loadURL(m_directories.getURLFromSrc(u"/sd/qa/unit/data/odp/tdf147919.odp"), 
ODP);
+    utl::TempFile tempFile;
+    xDocShRef = saveAndReload(xDocShRef.get(), PPTX, &tempFile);
+    xDocShRef->DoClose();
+
+    xmlDocUniquePtr pXmlDoc = parseExport(tempFile, "ppt/slides/slide1.xml");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[1]/p:spPr/a:prstGeom", "prst",
+                "bentConnector2");
+
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[2]/p:spPr/a:prstGeom/a:avLst/a:gd", "name", 
"adj1");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[2]/p:spPr/a:prstGeom", "prst",
+                "bentConnector3");
+
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[3]/p:spPr/a:xfrm", 
"flipH", "1");
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[3]/p:spPr/a:xfrm", 
"rot", "16200000");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[3]/p:spPr/a:prstGeom/a:avLst/a:gd[1]", "name", 
"adj1");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[3]/p:spPr/a:prstGeom/a:avLst/a:gd[2]", "name", 
"adj2");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[3]/p:spPr/a:prstGeom", "prst",
+                "bentConnector4");
+
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:xfrm", 
"flipH", "1");
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:xfrm", 
"flipV", "1");
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:xfrm", 
"rot", "10800000");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:prstGeom/a:avLst/a:gd[1]", "name", 
"adj1");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:prstGeom/a:avLst/a:gd[2]", "name", 
"adj2");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:prstGeom/a:avLst/a:gd[3]", "name", 
"adj3");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[4]/p:spPr/a:prstGeom", "prst",
+                "bentConnector5");
+
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[5]/p:spPr/a:xfrm", 
"flipH", "1");
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[5]/p:spPr/a:xfrm", 
"rot", "16200000");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[5]/p:spPr/a:prstGeom", "prst",
+                "curvedConnector2");
+
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[6]/p:spPr/a:xfrm", 
"flipH", "1");
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[6]/p:spPr/a:xfrm", 
"rot", "16200000");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[6]/p:spPr/a:prstGeom/a:avLst/a:gd", "name", 
"adj1");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[6]/p:spPr/a:prstGeom", "prst",
+                "curvedConnector3");
+
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[7]/p:spPr/a:xfrm", 
"flipH", "1");
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[7]/p:spPr/a:xfrm", 
"flipV", "1");
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[7]/p:spPr/a:xfrm", 
"rot", "10800000");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[7]/p:spPr/a:prstGeom/a:avLst/a:gd[1]", "name", 
"adj1");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[7]/p:spPr/a:prstGeom/a:avLst/a:gd[2]", "name", 
"adj2");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[7]/p:spPr/a:prstGeom", "prst",
+                "curvedConnector4");
+
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[8]/p:spPr/a:xfrm", 
"flipV", "1");
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:cxnSp[8]/p:spPr/a:xfrm", 
"rot", "16200000");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[8]/p:spPr/a:prstGeom/a:avLst/a:gd[1]", "name", 
"adj1");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[8]/p:spPr/a:prstGeom/a:avLst/a:gd[2]", "name", 
"adj2");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[8]/p:spPr/a:prstGeom/a:avLst/a:gd[3]", "name", 
"adj3");
+    assertXPath(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:cxnSp[8]/p:spPr/a:prstGeom", "prst",
+                "curvedConnector5");
+}
+
 void SdOOXMLExportTest1::testTdf130165()
 {
     sd::DrawDocShellRef xDocShRef

Reply via email to