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");