oox/source/export/drawingml.cxx     |  191 +++++++++++++++++++-----------------
 sd/qa/unit/data/pptx/tdf165670.pptx |binary
 sd/qa/unit/export-tests-ooxml3.cxx  |   25 ++++
 3 files changed, 130 insertions(+), 86 deletions(-)

New commits:
commit 95128d92bce952e0df3f3be6f9a4d126a05426df
Author:     Tibor Nagy <tibor.nagy.ext...@allotropia.de>
AuthorDate: Thu Mar 20 11:47:57 2025 +0100
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Fri Mar 21 14:50:07 2025 +0100

    tdf#165670 PPTX export: fix glue points export regression
    
    In some cases, we exported non-integer values for the glue points,
    and their positions were also incorrect.
    
    this regression is
    caused by commit Id4a127e7ef462611bf63da791717f8260ec51be0
    (tdf165262 PPTX export: fix shape export regression).
    
    Change-Id: I978fa180ef181391651abad5c2b748e0b606b320
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183165
    Reviewed-by: Nagy Tibor <tibor.nagy.ext...@allotropia.de>
    Tested-by: Jenkins
    (cherry picked from commit 612891865a74777950ab7355a4d794fa5577c5fc)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183177
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index 561facf42deb..dd2cab050495 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -4691,52 +4691,53 @@ void prepareTextArea(const EnhancedCustomShape2d& 
rEnhancedCustomShape2d,
     return;
 }
 
-void prepareGluePoints(const EnhancedCustomShape2d& rEnhancedCustomShape2d,
-                       std::vector<Guide>& rGuideList,
-                       const 
uno::Sequence<drawing::EnhancedCustomShapeParameterPair>& rGluePoints,
-                       const 
uno::Sequence<drawing::EnhancedCustomShapeTextFrame>& aTextFrames)
+OUString GetFormula(const OUString& sEquation, const OUString& sReplace, const 
OUString& sNewStr)
 {
-    if (rGluePoints.hasElements())
+    OUString sFormula = sEquation;
+    size_t nPos = sFormula.indexOf(sReplace);
+    if (nPos != std::string::npos)
     {
-        OString sWidth, sHeight;
-        if (aTextFrames.hasElements())
-        {
-            double fTop = 0.0, fLeft = 0.0, fBottom = 0.0, fRight = 0.0;
-
-            rEnhancedCustomShape2d.GetParameter(fTop, 
aTextFrames[0].TopLeft.First, true, false);
-            rEnhancedCustomShape2d.GetParameter(fLeft, 
aTextFrames[0].TopLeft.Second, true, false);
-            rEnhancedCustomShape2d.GetParameter(fBottom, 
aTextFrames[0].BottomRight.First, false,
-                                                true);
-            rEnhancedCustomShape2d.GetParameter(fRight, 
aTextFrames[0].BottomRight.Second, false,
-                                                true);
+        OUString sModifiedEquation = sFormula.replaceAt(nPos, 
sReplace.getLength(), sNewStr);
+        sFormula = "*/ " + sModifiedEquation;
+    }
 
-            sWidth = OString::number(fLeft + fRight);
-            sHeight = OString::number(fTop + fBottom);
-        }
-        else
-        {
-            tools::Rectangle 
aLogicRectLO(rEnhancedCustomShape2d.GetLogicRect());
-            sal_Int32 nWidth = aLogicRectLO.Right() - aLogicRectLO.Left();
-            sal_Int32 nHeight = aLogicRectLO.Bottom() - aLogicRectLO.Top();
-            sWidth = OString::number(oox::drawingml::convertHmmToEmu(nWidth));
-            sHeight = 
OString::number(oox::drawingml::convertHmmToEmu(nHeight));
-        }
+    return sFormula;
+}
 
+void prepareGluePoints(std::vector<Guide>& rGuideList,
+                       const css::uno::Sequence<OUString>& aEquations,
+                       const 
uno::Sequence<drawing::EnhancedCustomShapeParameterPair>& rGluePoints,
+                       const bool bIsOOXML, const sal_Int32 nWidth, const 
sal_Int32 nHeight)
+{
+    if (rGluePoints.hasElements())
+    {
         sal_Int32 nIndex = 1;
         for (auto const& rGluePoint : rGluePoints)
         {
-            Guide aGuide;
-            double fRetValueX;
-            rEnhancedCustomShape2d.GetParameter(fRetValueX, rGluePoint.First, 
false, false);
-            aGuide.sName = "GluePoint"_ostr + OString::number(nIndex) + "X";
-            aGuide.sFormula = "*/ " + OString::number(fRetValueX) + " w " + 
sWidth;
-            rGuideList.push_back(aGuide);
-
-            double fRetValueY;
-            rEnhancedCustomShape2d.GetParameter(fRetValueY, rGluePoint.Second, 
false, false);
-            aGuide.sName = "GluePoint"_ostr + OString::number(nIndex) + "Y";
-            aGuide.sFormula = "*/ " + OString::number(fRetValueY) + " h " + 
sHeight;
-            rGuideList.push_back(aGuide);
+            sal_Int32 nIdx1 = -1;
+            sal_Int32 nIdx2 = -1;
+            rGluePoint.First.Value >>= nIdx1;
+            rGluePoint.Second.Value >>= nIdx2;
+
+            if (nIdx1 != -1 && nIdx2 != -1)
+            {
+                Guide aGuideX;
+                aGuideX.sName = "GluePoint"_ostr + OString::number(nIndex) + 
"X";
+                aGuideX.sFormula
+                    = (bIsOOXML && aEquations.hasElements())
+                          ? GetFormula(aEquations[nIdx1], "*logwidth/", " w 
").toUtf8()
+                          : "*/ " + OString::number(nIdx1) + " w " + 
OString::number(nWidth);
+                rGuideList.push_back(aGuideX);
+
+                Guide aGuideY;
+                aGuideY.sName = "GluePoint"_ostr + OString::number(nIndex) + 
"Y";
+                aGuideY.sFormula
+                    = (bIsOOXML && aEquations.hasElements())
+                          ? GetFormula(aEquations[nIdx2], "*logheight/", " h 
").toUtf8()
+                          : "*/ " + OString::number(nIdx2) + " h " + 
OString::number(nHeight);
+                rGuideList.push_back(aGuideY);
+            }
+
             nIndex++;
         }
     }
@@ -4776,8 +4777,28 @@ bool DrawingML::WriteCustomGeometry(
     uno::Sequence<beans::PropertyValue> aPathProp;
     pPathProp->Value >>= aPathProp;
 
+    auto pShapeType = std::find_if(std::cbegin(*pGeometrySeq), 
std::cend(*pGeometrySeq),
+                                   [](const PropertyValue& rProp) { return 
rProp.Name == "Type"; });
+    bool bOOXML = false;
+    if (pShapeType != std::cend(*pGeometrySeq))
+    {
+        OUString sShapeType;
+        pShapeType->Value >>= sShapeType;
+        if (sShapeType.startsWith("ooxml"))
+            bOOXML = true;
+    }
+
+    auto pEquationsProp
+        = std::find_if(std::cbegin(*pGeometrySeq), std::cend(*pGeometrySeq),
+                       [](const PropertyValue& rProp) { return rProp.Name == 
"Equations"; });
+
+    css::uno::Sequence<OUString> aEquationSeq;
+    if (pEquationsProp != std::cend(*pGeometrySeq))
+    {
+        pEquationsProp->Value >>= aEquationSeq;
+    }
+
     uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aGluePoints;
-    uno::Sequence<drawing::EnhancedCustomShapeTextFrame> aTextFrames;
     uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aPairs;
     uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments;
     uno::Sequence<awt::Size> aPathSize;
@@ -4791,8 +4812,6 @@ bool DrawingML::WriteCustomGeometry(
             rPathProp.Value >>= aSegments;
         else if (rPathProp.Name == "GluePoints")
             rPathProp.Value >>= aGluePoints;
-        else if (rPathProp.Name == "TextFrames")
-            rPathProp.Value >>= aTextFrames;
         else if (rPathProp.Name == "SubViewSize")
             rPathProp.Value >>= aPathSize;
         else if (rPathProp.Name == "StretchX")
@@ -4829,50 +4848,6 @@ bool DrawingML::WriteCustomGeometry(
     // entire method.
     const EnhancedCustomShape2d 
aCustomShape2d(const_cast<SdrObjCustomShape&>(rSdrObjCustomShape));
 
-    TextAreaRect aTextAreaRect;
-    std::vector<Guide> aGuideList; // for now only for <a:rect>
-    prepareTextArea(aCustomShape2d, aGuideList, aTextAreaRect);
-    prepareGluePoints(aCustomShape2d, aGuideList, aGluePoints, aTextFrames);
-    mpFS->startElementNS(XML_a, XML_custGeom);
-    mpFS->singleElementNS(XML_a, XML_avLst);
-    if (aGuideList.empty())
-    {
-        mpFS->singleElementNS(XML_a, XML_gdLst);
-    }
-    else
-    {
-        mpFS->startElementNS(XML_a, XML_gdLst);
-        for (auto const& elem : aGuideList)
-        {
-            mpFS->singleElementNS(XML_a, XML_gd, XML_name, elem.sName, 
XML_fmla, elem.sFormula);
-        }
-        mpFS->endElementNS(XML_a, XML_gdLst);
-    }
-    mpFS->singleElementNS(XML_a, XML_ahLst);
-
-    if (!aGuideList.empty())
-    {
-        mpFS->startElementNS(XML_a, XML_cxnLst);
-        for (auto it = aGuideList.begin(); it != aGuideList.end(); ++it)
-        {
-            auto aNextIt = std::next(it);
-            if (aNextIt != aGuideList.end() && 
it->sName.startsWith("GluePoint")
-                && aNextIt->sName.startsWith("GluePoint"))
-            {
-                mpFS->startElementNS(XML_a, XML_cxn, XML_ang, "0");
-                mpFS->singleElementNS(XML_a, XML_pos, XML_x, it->sName, XML_y, 
aNextIt->sName);
-                mpFS->endElementNS(XML_a, XML_cxn);
-
-                ++it;
-            }
-        }
-        mpFS->endElementNS(XML_a, XML_cxnLst);
-    }
-
-    mpFS->singleElementNS(XML_a, XML_rect, XML_l, aTextAreaRect.left, XML_t, 
aTextAreaRect.top,
-                          XML_r, aTextAreaRect.right, XML_b, 
aTextAreaRect.bottom);
-    mpFS->startElementNS(XML_a, XML_pathLst);
-
     // Prepare width and height for <a:path>
     bool bUseGlobalViewBox(false);
 
@@ -4942,6 +4917,50 @@ bool DrawingML::WriteCustomGeometry(
         // shift of the resulting path coordinates.
     }
 
+    TextAreaRect aTextAreaRect;
+    std::vector<Guide> aGuideList; // for now only for <a:rect>
+    prepareTextArea(aCustomShape2d, aGuideList, aTextAreaRect);
+    prepareGluePoints(aGuideList, aEquationSeq, aGluePoints, bOOXML, 
nViewBoxWidth, nViewBoxHeight);
+    mpFS->startElementNS(XML_a, XML_custGeom);
+    mpFS->singleElementNS(XML_a, XML_avLst);
+    if (aGuideList.empty())
+    {
+        mpFS->singleElementNS(XML_a, XML_gdLst);
+    }
+    else
+    {
+        mpFS->startElementNS(XML_a, XML_gdLst);
+        for (auto const& elem : aGuideList)
+        {
+            mpFS->singleElementNS(XML_a, XML_gd, XML_name, elem.sName, 
XML_fmla, elem.sFormula);
+        }
+        mpFS->endElementNS(XML_a, XML_gdLst);
+    }
+    mpFS->singleElementNS(XML_a, XML_ahLst);
+
+    if (!aGuideList.empty())
+    {
+        mpFS->startElementNS(XML_a, XML_cxnLst);
+        for (auto it = aGuideList.begin(); it != aGuideList.end(); ++it)
+        {
+            auto aNextIt = std::next(it);
+            if (aNextIt != aGuideList.end() && 
it->sName.startsWith("GluePoint")
+                && aNextIt->sName.startsWith("GluePoint"))
+            {
+                mpFS->startElementNS(XML_a, XML_cxn, XML_ang, "0");
+                mpFS->singleElementNS(XML_a, XML_pos, XML_x, it->sName, XML_y, 
aNextIt->sName);
+                mpFS->endElementNS(XML_a, XML_cxn);
+
+                ++it;
+            }
+        }
+        mpFS->endElementNS(XML_a, XML_cxnLst);
+    }
+
+    mpFS->singleElementNS(XML_a, XML_rect, XML_l, aTextAreaRect.left, XML_t, 
aTextAreaRect.top,
+                          XML_r, aTextAreaRect.right, XML_b, 
aTextAreaRect.bottom);
+    mpFS->startElementNS(XML_a, XML_pathLst);
+
     // Iterate over subpaths
     sal_Int32 nPairIndex = 0; // index over "Coordinates"
     sal_Int32 nPathSizeIndex = 0; // index over "SubViewSize"
diff --git a/sd/qa/unit/data/pptx/tdf165670.pptx 
b/sd/qa/unit/data/pptx/tdf165670.pptx
new file mode 100644
index 000000000000..66e7c54041f5
Binary files /dev/null and b/sd/qa/unit/data/pptx/tdf165670.pptx differ
diff --git a/sd/qa/unit/export-tests-ooxml3.cxx 
b/sd/qa/unit/export-tests-ooxml3.cxx
index 7d06160b4ce2..639a64555566 100644
--- a/sd/qa/unit/export-tests-ooxml3.cxx
+++ b/sd/qa/unit/export-tests-ooxml3.cxx
@@ -27,6 +27,31 @@ public:
     int testTdf115005_FallBack_Images(bool bAddReplacementImages);
 };
 
+CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest3, testTdf165670)
+{
+    createSdImpressDoc("pptx/tdf165670.pptx");
+    save(u"Impress Office Open XML"_ustr);
+
+    xmlDocUniquePtr pXmlDoc1 = parseExport(u"ppt/slides/slide1.xml"_ustr);
+
+    // glue points export
+    // without the fix in place, this test would have failed with
+    // - Expected: "*/ 690465 w 2407298"
+    // - Actual  : "*/ 1917.97586131837 w 5236"
+    assertXPath(pXmlDoc1, 
"/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:custGeom/a:gdLst/a:gd[5]", "name",
+                u"GluePoint1X");
+    assertXPath(pXmlDoc1, 
"/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:custGeom/a:gdLst/a:gd[5]", "fmla",
+                u"*/ 690465 w 2407298");
+
+    // without the fix in place, this test would have failed with
+    // - Expected: "*/ 802433 h 1884784"
+    // - Actual  : "*/ 2229.18869642357 h 6687"
+    assertXPath(pXmlDoc1, 
"/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:custGeom/a:gdLst/a:gd[6]", "name",
+                u"GluePoint1Y");
+    assertXPath(pXmlDoc1, 
"/p:sld/p:cSld/p:spTree/p:sp/p:spPr/a:custGeom/a:gdLst/a:gd[6]", "fmla",
+                u"*/ 802433 h 1884784");
+}
+
 CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest3, testTdf165262)
 {
     createSdImpressDoc("ppt/tdf165262.ppt");

Reply via email to