oox/qa/unit/data/tdf100391_TextAreaRect.odp |binary oox/qa/unit/export.cxx | 21 ++++++ oox/source/export/drawingml.cxx | 98 +++++++++++++++++++++++++++- 3 files changed, 116 insertions(+), 3 deletions(-)
New commits: commit b88449732d1b97ad53c09425831d48362c3a9bf5 Author: Regina Henschel <rb.hensc...@t-online.de> AuthorDate: Mon Apr 4 01:55:29 2022 +0200 Commit: Andras Timar <andras.ti...@collabora.com> CommitDate: Tue Jan 24 06:38:56 2023 +0000 tdf#100391 calculate true textarea rect for custGeom Without the fix the attributes for <a:rect> were set to 'l', 't', 'r' and 'b'. That means that the textarea rectangle equals the shape rectangle. That is the default and works for many shapes. But 'Puzzle' has a smaller textarea rectangle, for example. Because the values in draw:text-areas are relative to the internal coordinate system given by draw:viewBox in ODF, but the values in <a:rect> are relative to the shape coordinate system in OOXML, we cannot simple write the current absolute values but need to calculate them depending on actual width and height. For that we need guides. The patch introduces a guide list. Currently the list contains only the guides for the textarea rectangle, but it can be extended when export of handles will be implemented one day. Change-Id: I1050627ef6459ab5f8fafa939d7905122870c903 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/132489 Tested-by: Jenkins Reviewed-by: Regina Henschel <rb.hensc...@t-online.de> (cherry picked from commit 1ad58c77352e418124387b804b18da2aeea22c8b) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145986 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Andras Timar <andras.ti...@collabora.com> diff --git a/oox/qa/unit/data/tdf100391_TextAreaRect.odp b/oox/qa/unit/data/tdf100391_TextAreaRect.odp new file mode 100755 index 000000000000..b9b9e5b39e4a Binary files /dev/null and b/oox/qa/unit/data/tdf100391_TextAreaRect.odp differ diff --git a/oox/qa/unit/export.cxx b/oox/qa/unit/export.cxx index 69dc10860f4e..a6cf4e7342eb 100644 --- a/oox/qa/unit/export.cxx +++ b/oox/qa/unit/export.cxx @@ -530,6 +530,27 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf147978_subpath) assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "h", "80"); } +CPPUNIT_TEST_FIXTURE(Test, testTdf100391TextAreaRect) +{ + // The document has a custom shape of type "non-primitive" to trigger the custGeom export + OUString aURL = m_directories.getURLFromSrc(DATA_DIRECTORY) + "tdf100391_TextAreaRect.odp"; + // When saving to PPTX the textarea rect was set to default instead of using the actual area + loadAndSave(aURL, "Impress Office Open XML"); + + // Verify the markup. Without fix the values were l="l", t="t", r="r", b="b" + std::unique_ptr<SvStream> pStream = parseExportStream(getTempFile(), "ppt/slides/slide1.xml"); + xmlDocUniquePtr pXmlDoc = parseXmlStream(pStream.get()); + assertXPath(pXmlDoc, "//a:custGeom/a:rect", "l", "textAreaLeft"); + assertXPath(pXmlDoc, "//a:custGeom/a:rect", "t", "textAreaTop"); + assertXPath(pXmlDoc, "//a:custGeom/a:rect", "r", "textAreaRight"); + assertXPath(pXmlDoc, "//a:custGeom/a:rect", "b", "textAreaBottom"); + // The values are calculated in guides, for example + assertXPath(pXmlDoc, "//a:custGeom/a:gdLst/a:gd[1]", "name", "textAreaLeft"); + assertXPath(pXmlDoc, "//a:custGeom/a:gdLst/a:gd[1]", "fmla", "*/ 1440000 w 2880000"); + // The test reflects the state of Apr 2022. It needs to be adapted when export of handles and + // guides is implemented. +} + CPPUNIT_TEST_FIXTURE(Test, testTdf109169_OctagonBevel) { // The odp file contains an "Octagon Bevel" shape. Such has shading not in commands H,I,J,K diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx index da43043bd9c5..b8e265882a65 100644 --- a/oox/source/export/drawingml.cxx +++ b/oox/source/export/drawingml.cxx @@ -3865,6 +3865,83 @@ sal_Int32 GetCustomGeometryPointValue(const css::drawing::EnhancedCustomShapePar return nValue; } + +struct TextAreaRect +{ + OString left; + OString top; + OString right; + OString bottom; +}; + +struct Guide +{ + OString sName; + OString sFormula; +}; + +void prepareTextArea(const EnhancedCustomShape2d& rEnhancedCustomShape2d, + std::vector<Guide>& rGuideList, TextAreaRect& rTextAreaRect) +{ + tools::Rectangle aTextAreaLO(rEnhancedCustomShape2d.GetTextRect()); + tools::Rectangle aLogicRectLO(rEnhancedCustomShape2d.GetLogicRect()); + if (aTextAreaLO == aLogicRectLO) + { + rTextAreaRect.left = "l"; + rTextAreaRect.top = "t"; + rTextAreaRect.right = "r"; + rTextAreaRect.bottom = "b"; + return; + } + // Flip aTextAreaLO if shape is flipped + if (rEnhancedCustomShape2d.IsFlipHorz()) + aTextAreaLO.Move((aLogicRectLO.Center().X() - aTextAreaLO.Center().X()) * 2, 0); + if (rEnhancedCustomShape2d.IsFlipVert()) + aTextAreaLO.Move(0, (aLogicRectLO.Center().Y() - aTextAreaLO.Center().Y()) * 2); + + Guide aGuide; + // horizontal + const sal_Int32 nWidth = aLogicRectLO.Right() - aLogicRectLO.Left(); + const OString sWidth = OString::number(oox::drawingml::convertHmmToEmu(nWidth)); + + // left + aGuide.sName = "textAreaLeft"; + sal_Int32 nHelp = aTextAreaLO.Left() - aLogicRectLO.Left(); + const OString sLeft = OString::number(oox::drawingml::convertHmmToEmu(nHelp)); + aGuide.sFormula = "*/ " + sLeft + " w " + sWidth; + rTextAreaRect.left = aGuide.sName; + rGuideList.push_back(aGuide); + + // right + aGuide.sName = "textAreaRight"; + nHelp = aTextAreaLO.Right() - aLogicRectLO.Left(); + const OString sRight = OString::number(oox::drawingml::convertHmmToEmu(nHelp)); + aGuide.sFormula = "*/ " + sRight + " w " + sWidth; + rTextAreaRect.right = aGuide.sName; + rGuideList.push_back(aGuide); + + // vertical + const sal_Int32 nHeight = aLogicRectLO.Bottom() - aLogicRectLO.Top(); + const OString sHeight = OString::number(oox::drawingml::convertHmmToEmu(nHeight)); + + // top + aGuide.sName = "textAreaTop"; + nHelp = aTextAreaLO.Top() - aLogicRectLO.Top(); + const OString sTop = OString::number(oox::drawingml::convertHmmToEmu(nHelp)); + aGuide.sFormula = "*/ " + sTop + " h " + sHeight; + rTextAreaRect.top = aGuide.sName; + rGuideList.push_back(aGuide); + + // bottom + aGuide.sName = "textAreaBottom"; + nHelp = aTextAreaLO.Bottom() - aLogicRectLO.Top(); + const OString sBottom = OString::number(oox::drawingml::convertHmmToEmu(nHelp)); + aGuide.sFormula = "*/ " + sBottom + " h " + sHeight; + rTextAreaRect.bottom = aGuide.sName; + rGuideList.push_back(aGuide); + + return; +} } bool DrawingML::WriteCustomGeometry( @@ -3941,12 +4018,27 @@ 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); mpFS->startElementNS(XML_a, XML_custGeom); mpFS->singleElementNS(XML_a, XML_avLst); - mpFS->singleElementNS(XML_a, XML_gdLst); + 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); - // ToDO: use draw:TextAreas for <a:rect> - mpFS->singleElementNS(XML_a, XML_rect, XML_l, "l", XML_t, "t", XML_r, "r", XML_b, "b"); + 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>