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 1ad58c77352e418124387b804b18da2aeea22c8b
Author:     Regina Henschel <rb.hensc...@t-online.de>
AuthorDate: Mon Apr 4 01:55:29 2022 +0200
Commit:     Regina Henschel <rb.hensc...@t-online.de>
CommitDate: Mon Apr 4 20:21:14 2022 +0200

    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>

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 60e20e2d933a..91b8d7608c24 100644
--- a/oox/qa/unit/export.cxx
+++ b/oox/qa/unit/export.cxx
@@ -590,6 +590,27 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf147978_subpath)
     assertXPath(pXmlDoc, "//a:pathLst/a:path[4]", "w", "80");
     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_PLUGIN_IMPLEMENT();
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index 54f94278d782..1d2d601a40f0 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -3857,6 +3857,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(
@@ -3933,12 +4010,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>

Reply via email to