oox/source/export/drawingml.cxx                           |   23 ++++--
 sd/qa/unit/ShapeImportExportTest.cxx                      |   48 +++++++++-----
 sd/qa/unit/data/odp/tdf150966_hugeInset.odp               |binary
 sw/qa/extras/ooxmlexport/data/tdf150966_regularInset.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport18.cxx                |   18 +++++
 5 files changed, 67 insertions(+), 22 deletions(-)

New commits:
commit 59b44e72f46021c070095a75a0d7e0ae12c43399
Author:     Regina Henschel <rb.hensc...@t-online.de>
AuthorDate: Wed Nov 2 22:34:16 2022 +0100
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Thu Nov 10 11:22:09 2022 +0100

    tdf#150966 oox export avoid bottom above top for text area
    
    If bottom and top insets set so that the bottom edge of the resulting
    text area is above the top edge, then LO and MS Office behave different
    in how this is rendered. With commit e2169886 insets are converted on
    import to make rendering in LO similar to MS Office, but the
    implemented export has some problems, see analysis in bug report.
    
    LibreOffice normalizes the resulting text area in case bottom edge is
    above top edge. So this patch exports the insets so, that MS Office
    gets a normalized resulting text area and will not apply its special
    rules.
    
    A roundtrip starting with pptx will not regenerate the old values but
    will produce inset values, which give same rendering in MS Office than
    in LO. Because the method is different now, the inset values have
    changed and test testTextDistancesOOXML_Export is adapted. When you
    compare the result with the screenshot on slide 2, you see that the
    new method works as well.
    
    The old method did not work for exporting an odp file. That is covered
    by the new unit test. The docx unit test file covers the case, that
    the export tweak was erroneously triggered.
    
    Change-Id: I0091f284d9bdd635dd87ddb9e9b0e415cc0cc51e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142185
    Tested-by: Jenkins
    Reviewed-by: Regina Henschel <rb.hensc...@t-online.de>
    (cherry picked from commit 933768ffcd8617942f45481de77e656ded9dcfe2)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142265
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index 5ee48ff6e338..5df185983ea8 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -110,6 +110,7 @@
 #include <o3tl/safeint.hxx>
 #include <o3tl/string_view.hxx>
 #include <tools/stream.hxx>
+#include <tools/UnitConversion.hxx>
 #include <unotools/fontdefs.hxx>
 #include <vcl/cvtgrf.hxx>
 #include <vcl/svapp.hxx>
@@ -3337,21 +3338,27 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
     // Transform the text distance values so they are compatible with OOXML 
insets
     if (xShape.is())
     {
-        sal_Int32 nTextHeight = xShape->getSize().Width;
+        sal_Int32 nTextHeight = xShape->getSize().Height; // Hmm, default
 
-        auto* pCustomShape = 
dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape));
+        // CustomShape can have text area different from shape rectangle
+        auto* pCustomShape
+            = 
dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape));
         if (pCustomShape)
         {
-            tools::Rectangle aAnchorRect;
-            pCustomShape->TakeTextAnchorRect(aAnchorRect);
-            nTextHeight = aAnchorRect.GetSize().getHeight();
+            const EnhancedCustomShape2d aCustomShape2d(*pCustomShape);
+            nTextHeight = aCustomShape2d.GetTextRect().getHeight();
+            if (DOCUMENT_DOCX == meDocumentType)
+                nTextHeight = convertTwipToMm100(nTextHeight);
         }
 
         if (nTop + nBottom >= nTextHeight)
         {
-            sal_Int32 nDiff = std::abs(std::min(nTop, nBottom));
-            nTop += nDiff;
-            nBottom += nDiff;
+            // Effective bottom would be above effective top of text area. LO 
normalizes the
+            // effective text area in such case implicitely for rendering. MS 
needs indents so that
+            // the result is the normalized effective text area.
+            std::swap(nTop, nBottom);
+            nTop = nTextHeight - nTop;
+            nBottom = nTextHeight - nBottom;
         }
     }
 
diff --git a/sd/qa/unit/ShapeImportExportTest.cxx 
b/sd/qa/unit/ShapeImportExportTest.cxx
index 071fdc58c74e..0f1609df2e41 100644
--- a/sd/qa/unit/ShapeImportExportTest.cxx
+++ b/sd/qa/unit/ShapeImportExportTest.cxx
@@ -25,11 +25,13 @@ public:
     void testTextDistancesOOXML();
     void testTextDistancesOOXML_LargerThanTextAreaSpecialCase();
     void testTextDistancesOOXML_Export();
+    void testTextDistancesODP_OOXML_Export();
 
     CPPUNIT_TEST_SUITE(ShapeImportExportTest);
     CPPUNIT_TEST(testTextDistancesOOXML);
     CPPUNIT_TEST(testTextDistancesOOXML_LargerThanTextAreaSpecialCase);
     CPPUNIT_TEST(testTextDistancesOOXML_Export);
+    CPPUNIT_TEST(testTextDistancesODP_OOXML_Export);
     CPPUNIT_TEST_SUITE_END();
 
     virtual void registerNamespaces(xmlXPathContextPtr& pXmlXPathCtx) override
@@ -272,28 +274,28 @@ void 
ShapeImportExportTest::testTextDistancesOOXML_Export()
     //Check shape Top/Bottom - 0cm, 4cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:cNvPr", 
"name", "Text_TB_0_4");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[1]/p:txBody/a:bodyPr",
-                     { { "tIns", "0" }, { "bIns", "1439640" } });
+                     { { "tIns", "-360000" }, { "bIns", "1079640" } });
 
     //Check shape Top/Bottom - 4cm, 0cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:cNvPr", 
"name", "Text_TB_4_0");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[2]/p:txBody/a:bodyPr",
-                     { { "tIns", "1439640" }, { "bIns", "0" } });
+                     { { "tIns", "1079640" }, { "bIns", "-360000" } });
 
     //Check shape Top/Bottom - 0cm, 3cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[3]/p:nvSpPr/p:cNvPr", 
"name", "Text_TB_0_3");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[3]/p:txBody/a:bodyPr",
-                     { { "tIns", "0" }, { "bIns", "1079640" } });
+                     { { "tIns", "-180000" }, { "bIns", "899640" } });
 
     //Check shape Top/Bottom - 2cm, 1cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[4]/p:nvSpPr/p:cNvPr", 
"name", "Text_TB_2_1");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[4]/p:txBody/a:bodyPr",
-                     { { "tIns", "720000" }, { "bIns", "360000" } });
+                     { { "tIns", "540000" }, { "bIns", "180000" } });
 
     //Check shape Top/Bottom - 0cm, 2.5cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[5]/p:nvSpPr/p:cNvPr", 
"name",
                 "Text_TB_0_2.5");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[5]/p:txBody/a:bodyPr",
-                     { { "tIns", "0" }, { "bIns", "899640" } });
+                     { { "tIns", "-90000" }, { "bIns", "809640" } });
 
     //Check shape Top/Bottom - 0cm, 2cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[6]/p:nvSpPr/p:cNvPr", 
"name", "Text_TB_0_2");
@@ -309,13 +311,13 @@ void 
ShapeImportExportTest::testTextDistancesOOXML_Export()
     //Check shape Top/Bottom - 3cm, 0cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[8]/p:nvSpPr/p:cNvPr", 
"name", "Text_TB_3_0");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[8]/p:txBody/a:bodyPr",
-                     { { "tIns", "1079640" }, { "bIns", "0" } });
+                     { { "tIns", "899640" }, { "bIns", "-180000" } });
 
     //Check shape Top/Bottom - 2.5cm, 0cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[9]/p:nvSpPr/p:cNvPr", 
"name",
                 "Text_TB_2.5_0");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[9]/p:txBody/a:bodyPr",
-                     { { "tIns", "899640" }, { "bIns", "0" } });
+                     { { "tIns", "809640" }, { "bIns", "-90000" } });
 
     //Check shape Top/Bottom - 2cm, 0cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[10]/p:nvSpPr/p:cNvPr", 
"name", "Text_TB_2_0");
@@ -331,41 +333,41 @@ void 
ShapeImportExportTest::testTextDistancesOOXML_Export()
     //Check shape Top/Bottom - 1cm, 2cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[12]/p:nvSpPr/p:cNvPr", 
"name", "Text_TB_1_2");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[12]/p:txBody/a:bodyPr",
-                     { { "tIns", "360000" }, { "bIns", "720000" } });
+                     { { "tIns", "180000" }, { "bIns", "540000" } });
 
     //Check shape Top/Bottom - 2cm, 1.5cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[13]/p:nvSpPr/p:cNvPr", 
"name",
                 "Text_TB_2_1.5");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[13]/p:txBody/a:bodyPr",
-                     { { "tIns", "720000" }, { "bIns", "540000" } });
+                     { { "tIns", "450000" }, { "bIns", "270000" } });
 
     //Check shape Top/Bottom - 1.5cm, 2cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[14]/p:nvSpPr/p:cNvPr", 
"name",
                 "Text_TB_1.5_2");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[14]/p:txBody/a:bodyPr",
-                     { { "tIns", "540000" }, { "bIns", "720000" } });
+                     { { "tIns", "270000" }, { "bIns", "450000" } });
 
     //Check shape Top/Bottom - 2cm, 1.75cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[15]/p:nvSpPr/p:cNvPr", 
"name",
                 "Text_TB_2_1.75");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[15]/p:txBody/a:bodyPr",
-                     { { "tIns", "720000" }, { "bIns", "630000" } });
+                     { { "tIns", "405000" }, { "bIns", "315000" } });
 
     //Check shape Top/Bottom - 1.75cm, 2cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[16]/p:nvSpPr/p:cNvPr", 
"name",
                 "Text_TB_1.75_2");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[16]/p:txBody/a:bodyPr",
-                     { { "tIns", "630000" }, { "bIns", "720000" } });
+                     { { "tIns", "315000" }, { "bIns", "405000" } });
 
     //Check shape Top/Bottom - 2cm, 2cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[17]/p:nvSpPr/p:cNvPr", 
"name", "Text_TB_2_2");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[17]/p:txBody/a:bodyPr",
-                     { { "tIns", "720000" }, { "bIns", "720000" } });
+                     { { "tIns", "360000" }, { "bIns", "360000" } });
 
     //Check shape Top/Bottom - 1cm, 1cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[18]/p:nvSpPr/p:cNvPr", 
"name", "Text_TB_1_1");
     assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[18]/p:txBody/a:bodyPr",
-                     { { "tIns", "720000" }, { "bIns", "720000" } });
+                     { { "tIns", "360000" }, { "bIns", "360000" } });
 
     //Check shape Top/Bottom - 0.5cm, 0.5cm
     assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[19]/p:nvSpPr/p:cNvPr", 
"name",
@@ -374,6 +376,24 @@ void ShapeImportExportTest::testTextDistancesOOXML_Export()
                      { { "tIns", "180000" }, { "bIns", "180000" } });
 }
 
+void ShapeImportExportTest::testTextDistancesODP_OOXML_Export()
+{
+    ::sd::DrawDocShellRef xDocShell
+        = 
loadURL(m_directories.getURLFromSrc(u"sd/qa/unit/data/odp/tdf150966_hugeInset.odp"),
 ODP);
+
+    utl::TempFile aTempFile;
+    xDocShell = saveAndReload(xDocShell.get(), PPTX, &aTempFile);
+    xDocShell->DoClose();
+    xmlDocUniquePtr pXmlDoc = parseExport(aTempFile, "ppt/slides/slide1.xml");
+    CPPUNIT_ASSERT(pXmlDoc);
+
+    // The text ends 5cm below the top edge of the shape.
+    // Without the fix we exported tIns="3600000" and bIns="5400000".
+    // The text had ended about 3.3cm below the top edge in PowerPoint.
+    assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[1]/p:txBody/a:bodyPr",
+                     { { "tIns", "720000" }, { "bIns", "2520000" } });
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(ShapeImportExportTest);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sd/qa/unit/data/odp/tdf150966_hugeInset.odp 
b/sd/qa/unit/data/odp/tdf150966_hugeInset.odp
new file mode 100644
index 000000000000..9dcc88bef9cb
Binary files /dev/null and b/sd/qa/unit/data/odp/tdf150966_hugeInset.odp differ
diff --git a/sw/qa/extras/ooxmlexport/data/tdf150966_regularInset.docx 
b/sw/qa/extras/ooxmlexport/data/tdf150966_regularInset.docx
new file mode 100644
index 000000000000..0d07a5453e35
Binary files /dev/null and 
b/sw/qa/extras/ooxmlexport/data/tdf150966_regularInset.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
index 8a7009805f13..e1e515f99e57 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
@@ -83,6 +83,24 @@ DECLARE_OOXMLEXPORT_TEST(testTdf147724, "tdf147724.docx")
     CPPUNIT_ASSERT(sFieldResult == "Placeholder -> *HERUNTERLADEN*" || 
sFieldResult == "Placeholder -> *ABC*");
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf150966_regularInset)
+{
+    // Given a docx document with a rectangular shape with height cy="900000" 
(EMU), tIns="180000"
+    // and bIns="360000", resulting in 360000EMU text area height.
+    load(DATA_DIRECTORY, "tdf150966_regularInset.docx");
+
+    // The shape is imported as custom shape with attached frame.
+    // The insets are currently imported as margin top="4.99mm" and 
bottom="10mm".
+    // That should result in tIns="179640" and bIns="360000" on export.
+
+    // Without fix the insets were tIns="359280" and bIns="539640". The text 
area had 1080Emu height
+    // and Word displayes no text at all.
+    save("Office Open XML Text", maTempFile);
+    mbExported = true;
+    xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
+    assertXPathAttrs(pXmlDoc, "//wps:bodyPr", { { "tIns", "179640" }, { 
"bIns", "360000" } });
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to