oox/inc/drawingml/textbodyproperties.hxx       |   17 -
 oox/source/drawingml/shape.cxx                 |    9 
 oox/source/drawingml/textbodyproperties.cxx    |  129 +++++++-
 oox/source/export/drawingml.cxx                |   48 ++-
 sd/CppunitTest_sd_shape_import_export_tests.mk |   74 ++++
 sd/Module_sd.mk                                |    1 
 sd/qa/unit/ShapeImportExportTest.cxx           |  381 +++++++++++++++++++++++++
 sd/qa/unit/data/TextDistancesInsets1.pptx      |binary
 sd/qa/unit/data/TextDistancesInsets2.pptx      |binary
 sd/qa/unit/data/TextDistancesInsets3.pptx      |binary
 10 files changed, 619 insertions(+), 40 deletions(-)

New commits:
commit e216988657e20a1e52986f742ab60464697bcb41
Author:     Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk>
AuthorDate: Mon Jun 6 22:53:23 2022 +0200
Commit:     Tomaž Vajngerl <qui...@gmail.com>
CommitDate: Wed Jun 8 11:36:12 2022 +0200

    tdf148321: convert OOXML inset values to text distance values
    
    Inset values for top, bottom are calcualted differently in OOXML
    and need to be coverted on import to the text distance LO values,
    that place the text relative to the shape correctly.
    
    At export, the values can be converted back to the OOXML inset
    compatible values, but the values are not always converted back to
    the same values as the conversion is not bijective, however they
    do render the same.
    
    This also adds the test for the conversion when importing and
    checks that the exported values are expected.
    
    Change-Id: Ic64eec1a2a80ddad997f916da3e87dc30aaa12be
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135463
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <qui...@gmail.com>

diff --git a/oox/inc/drawingml/textbodyproperties.hxx 
b/oox/inc/drawingml/textbodyproperties.hxx
index 41fbb832a5d8..7cc1b9d8041c 100644
--- a/oox/inc/drawingml/textbodyproperties.hxx
+++ b/oox/inc/drawingml/textbodyproperties.hxx
@@ -21,12 +21,15 @@
 #define INCLUDED_OOX_DRAWINGML_TEXTBODYPROPERTIES_HXX
 
 #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
 #include <oox/helper/helper.hxx>
 #include <oox/helper/propertymap.hxx>
 #include <optional>
+#include <array>
 
-namespace oox::drawingml {
+class Size;
 
+namespace oox::drawingml {
 
 struct TextBodyProperties
 {
@@ -35,7 +38,7 @@ struct TextBodyProperties
     bool                                            mbAnchorCtr;
     OptValue< sal_Int32 >                           moVert;
     bool                                            moUpright = false;
-    std::optional< sal_Int32 >                    moInsets[4];
+    std::array<std::optional<sal_Int32>, 4> moInsets;
     std::optional< sal_Int32 >                    moTextOffUpper;
     std::optional< sal_Int32 >                    moTextOffLeft;
     std::optional< sal_Int32 >                    moTextOffLower;
@@ -47,10 +50,14 @@ struct TextBodyProperties
     OUString msHorzOverflow;
     OUString msVertOverflow;
 
-    explicit            TextBodyProperties();
+    std::array<std::optional<sal_Int32>, 4> maTextDistanceValues;
+
+    explicit TextBodyProperties();
+
+    void pushTextDistances(Size const& rShapeSize);
+    void readjustTextDistances(css::uno::Reference<css::drawing::XShape> 
const& xShape);
+    void pushVertSimulation();
 
-    void                pushRotationAdjustments();
-    void                pushVertSimulation();
 };
 
 
diff --git a/oox/source/drawingml/shape.cxx b/oox/source/drawingml/shape.cxx
index 8739d4d8989d..8ef86298fd8b 100644
--- a/oox/source/drawingml/shape.cxx
+++ b/oox/source/drawingml/shape.cxx
@@ -1237,7 +1237,7 @@ Reference< XShape > const & Shape::createAndInsert(
         // add properties from textbody to shape properties
         if( mpTextBody )
         {
-            mpTextBody->getTextProperties().pushRotationAdjustments();
+            
mpTextBody->getTextProperties().pushTextDistances(Size(aShapeRectHmm.Width, 
aShapeRectHmm.Height));
             aShapeProps.assignUsed( 
mpTextBody->getTextProperties().maPropertyMap );
             // Push char properties as well - specifically useful when this is 
a placeholder
             if( mpMasterTextListStyle &&  
mpMasterTextListStyle->getListStyle()[0].getTextCharacterProperties().moHeight.has()
 )
@@ -1827,9 +1827,14 @@ Reference< XShape > const & Shape::createAndInsert(
         }
     }
 
-    if( mxShape.is() )
+    if (mxShape.is())
+    {
         finalizeXShape( rFilterBase, rxShapes );
 
+        if (mpTextBody)
+            mpTextBody->getTextProperties().readjustTextDistances(mxShape);
+    }
+
     return mxShape;
 }
 
diff --git a/oox/source/drawingml/textbodyproperties.cxx 
b/oox/source/drawingml/textbodyproperties.cxx
index 2ffa7de1085f..e44a103e3865 100644
--- a/oox/source/drawingml/textbodyproperties.cxx
+++ b/oox/source/drawingml/textbodyproperties.cxx
@@ -22,9 +22,17 @@
 #include <drawingml/textbodyproperties.hxx>
 #include <oox/token/properties.hxx>
 #include <oox/token/tokens.hxx>
+#include <tools/gen.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/sdtditm.hxx>
+
+#include <array>
 
 using namespace ::com::sun::star::drawing;
 using namespace ::com::sun::star::text;
+using namespace css;
 
 namespace oox::drawingml {
 
@@ -57,14 +65,21 @@ void TextBodyProperties::pushVertSimulation()
         maPropertyMap.setProperty( PROP_TextHorizontalAdjust, 
TextHorizontalAdjust_CENTER);
 }
 
-/* Push adjusted values, taking into consideration Shape Rotation */
-void TextBodyProperties::pushRotationAdjustments()
+/* Push text distances / insets, taking into consideration Shape Rotation */
+void TextBodyProperties::pushTextDistances(Size const& rTextAreaSize)
 {
-    sal_Int32 nOff      = 0;
-    static sal_Int32 const aProps[] { PROP_TextLeftDistance, 
PROP_TextUpperDistance, PROP_TextRightDistance, PROP_TextLowerDistance };
-    sal_Int32 n         = SAL_N_ELEMENTS( aProps );
+    for (auto & rValue : maTextDistanceValues)
+        rValue.reset();
+
+    sal_Int32 nOff = 0;
+    static constexpr const std::array<sal_Int32, 4> aProps {
+        PROP_TextLeftDistance,
+        PROP_TextUpperDistance,
+        PROP_TextRightDistance,
+        PROP_TextLowerDistance
+    };
 
-    switch( moRotation.get(0) )
+    switch (moRotation.get(0))
     {
         case 90*1*60000: nOff = 3; break;
         case 90*2*60000: nOff = 2; break;
@@ -72,28 +87,104 @@ void TextBodyProperties::pushRotationAdjustments()
         default: break;
     }
 
-    for( sal_Int32 i = 0; i < n; i++ )
+    for (size_t i = 0; i < aProps.size(); i++)
     {
         sal_Int32 nVal = 0;
 
         // Hack for n#760986
         // TODO: Preferred method would be to have a textbox on top
         // of the shape and the place it according to the (off,ext)
-        if( nOff == 0 && moTextOffLeft  ) nVal = *moTextOffLeft;
-        if( nOff == 1 && moTextOffUpper ) nVal = *moTextOffUpper;
-        if( nOff == 2 && moTextOffRight ) nVal = *moTextOffRight;
-        if( nOff == 3 && moTextOffLower ) nVal = *moTextOffLower;
-        if( nVal < 0 ) nVal = 0;
-
-        if( moInsets[i] )
-            maPropertyMap.setProperty( aProps[ nOff ], static_cast< sal_Int32 
>( *moInsets[i] + nVal ));
-        else if( nVal )
-            maPropertyMap.setProperty( aProps[ nOff ], nVal );
-
-        nOff = (nOff+1) % n;
+        if (nOff == 0 && moTextOffLeft)
+            nVal = *moTextOffLeft;
+
+        if (nOff == 1 && moTextOffUpper)
+            nVal = *moTextOffUpper;
+
+
+        if (nOff == 2 && moTextOffRight)
+            nVal = *moTextOffRight;
+
+        if (nOff == 3 && moTextOffLower)
+            nVal = *moTextOffLower;
+
+
+        if( nVal < 0 )
+            nVal = 0;
+
+        sal_Int32 nTextOffsetValue = nVal;
+
+        if (moInsets[i])
+        {
+            nTextOffsetValue = *moInsets[i] + nVal;
+        }
+
+        // if inset is set, then always set the value
+        // this prevents the default to be set (0 is a valid value)
+        if (moInsets[i] || nTextOffsetValue)
+        {
+            maTextDistanceValues[nOff] = nTextOffsetValue;
+        }
+
+        nOff = (nOff + 1) % aProps.size();
+    }
+
+    // Check if bottom and top are set
+    if (maTextDistanceValues[1] && maTextDistanceValues[3])
+    {
+        double nHeight = rTextAreaSize.getHeight();
+
+        double nTop = *maTextDistanceValues[1];
+        double nBottom = *maTextDistanceValues[3];
+
+        // Check if top + bottom is more than text area height.
+        // If yes, we need to adjust the values as defined in OOXML.
+        if (nTop + nBottom >= nHeight)
+        {
+            double diffFactor = (nTop + nBottom - nHeight) / 2.0;
+
+            maTextDistanceValues[1] = nTop - diffFactor;
+            maTextDistanceValues[3] = nBottom - diffFactor;
+        }
+    }
+
+    for (size_t i = 0; i < aProps.size(); i++)
+    {
+        if (maTextDistanceValues[i])
+            maPropertyMap.setProperty(aProps[i], *maTextDistanceValues[i]);
     }
 }
 
+/* Readjust the text distances / insets if necessary to take
+   the text area into account, not just the shape area*/
+void TextBodyProperties::readjustTextDistances(uno::Reference<drawing::XShape> 
const& xShape)
+{
+    // Only for custom shapes (for now)
+    auto* pCustomShape = 
dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape));
+    if (pCustomShape)
+    {
+        sal_Int32 nLower = pCustomShape->GetTextLowerDistance();
+        sal_Int32 nUpper = pCustomShape->GetTextUpperDistance();
+
+        pCustomShape->SetMergedItem(makeSdrTextUpperDistItem(0));
+        pCustomShape->SetMergedItem(makeSdrTextLowerDistItem(0));
+
+        tools::Rectangle aAnchorRect;
+        pCustomShape->TakeTextAnchorRect(aAnchorRect);
+        Size aAnchorSize = aAnchorRect.GetSize();
+
+        pushTextDistances(aAnchorSize);
+        if (maTextDistanceValues[1] && maTextDistanceValues[3])
+        {
+            nLower = *maTextDistanceValues[3];
+            nUpper = *maTextDistanceValues[1];
+        }
+
+        pCustomShape->SetMergedItem(makeSdrTextLowerDistItem(nLower));
+        pCustomShape->SetMergedItem(makeSdrTextUpperDistItem(nUpper));
+    }
+}
+
+
 } // namespace oox::drawingml
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/oox/source/export/drawingml.cxx b/oox/source/export/drawingml.cxx
index eea8394aef36..dad1ce7a5c44 100644
--- a/oox/source/export/drawingml.cxx
+++ b/oox/source/export/drawingml.cxx
@@ -3265,20 +3265,22 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
                           sal_Int32 nXmlNamespace, bool 
bWritePropertiesAsLstStyles)
 {
     // ToDo: Fontwork in DOCX
-    Reference< XText > xXText( rXIface, UNO_QUERY );
+    uno::Reference<XText> xXText(rXIface, UNO_QUERY);
     if( !xXText.is() )
         return;
 
-    Reference< XPropertySet > rXPropSet( rXIface, UNO_QUERY );
+    uno::Reference<drawing::XShape> xShape(rXIface, UNO_QUERY);
+    uno::Reference<XPropertySet> rXPropSet(rXIface, UNO_QUERY);
 
     sal_Int32 nTextPreRotateAngle = 0;
     double nTextRotateAngle = 0;
 
-#define DEFLRINS 254
-#define DEFTBINS 127
-    sal_Int32 nLeft, nRight, nTop, nBottom;
-    nLeft = nRight = DEFLRINS;
-    nTop = nBottom = DEFTBINS;
+    constexpr const sal_Int32 constDefaultLeftRightInset = 254;
+    constexpr const sal_Int32 constDefaultTopBottomInset = 127;
+    sal_Int32 nLeft = constDefaultLeftRightInset;
+    sal_Int32 nRight = constDefaultLeftRightInset;
+    sal_Int32 nTop = constDefaultTopBottomInset;
+    sal_Int32 nBottom = constDefaultTopBottomInset;
 
     // top inset looks a bit different compared to ppt export
     // check if something related doesn't work as expected
@@ -3291,6 +3293,27 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
     if (GetProperty(rXPropSet, "TextLowerDistance"))
         mAny >>= nBottom;
 
+    // Transform the text distance values so they are compatible with OOXML 
insets
+    if (xShape.is())
+    {
+        sal_Int32 nTextHeight = xShape->getSize().Width;
+
+        auto* pCustomShape = 
dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape));
+        if (pCustomShape)
+        {
+            tools::Rectangle aAnchorRect;
+            pCustomShape->TakeTextAnchorRect(aAnchorRect);
+            nTextHeight = aAnchorRect.GetSize().getHeight();
+        }
+
+        if (nTop + nBottom >= nTextHeight)
+        {
+            sal_Int32 nDiff = std::abs(std::min(nTop, nBottom));
+            nTop += nDiff;
+            nBottom += nDiff;
+        }
+    }
+
     TextVerticalAdjust eVerticalAlignment( TextVerticalAdjust_TOP );
     const char* sVerticalAlignment = nullptr;
     if (GetProperty(rXPropSet, "TextVerticalAdjust"))
@@ -3362,7 +3385,6 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
     {
         if (mpTextExport)
         {
-            uno::Reference<drawing::XShape> xShape(rXIface, uno::UNO_QUERY);
             if (xShape)
             {
                 auto xTextFrame = mpTextExport->GetUnoTextFrame(xShape);
@@ -3510,10 +3532,10 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
                                XML_horzOverflow, sHorzOverflow,
                                XML_vertOverflow, sVertOverflow,
                                XML_fromWordArt, sax_fastparser::UseIf("1", 
bFromWordArt),
-                               XML_lIns, 
sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeft)), 
nLeft != DEFLRINS),
-                               XML_rIns, 
sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRight)), 
nRight != DEFLRINS),
-                               XML_tIns, 
sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nTop)), 
nTop != DEFTBINS),
-                               XML_bIns, 
sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nBottom)),
 nBottom != DEFTBINS),
+                               XML_lIns, 
sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeft)), 
nLeft != constDefaultLeftRightInset),
+                               XML_rIns, 
sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRight)), 
nRight != constDefaultLeftRightInset),
+                               XML_tIns, 
sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nTop)), 
nTop != constDefaultTopBottomInset),
+                               XML_bIns, 
sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nBottom)),
 nBottom != constDefaultTopBottomInset),
                                XML_anchor, sVerticalAlignment,
                                XML_anchorCtr, sax_fastparser::UseIf("1", 
bHorizontalCenter),
                                XML_vert, sWritingMode,
@@ -3598,7 +3620,6 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
         {
             // tdf#112312: only custom shapes obey the TextAutoGrowHeight 
option
             bool bTextAutoGrowHeight = false;
-            uno::Reference<drawing::XShape> xShape(rXIface, uno::UNO_QUERY);
             auto pSdrObjCustomShape = xShape.is() ? 
dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape)) : 
nullptr;
             if (pSdrObjCustomShape && GetProperty(rXPropSet, 
"TextAutoGrowHeight"))
             {
@@ -3650,7 +3671,6 @@ void DrawingML::WriteText(const Reference<XInterface>& 
rXIface, bool bBodyPr, bo
     if( !enumeration.is() )
         return;
 
-    uno::Reference<drawing::XShape> xShape(rXIface, uno::UNO_QUERY);
     SdrObject* pSdrObject = xShape.is() ? 
SdrObject::getSdrObjectFromXShape(xShape) : nullptr;
     const SdrTextObj* pTxtObj = dynamic_cast<SdrTextObj*>( pSdrObject );
     if (pTxtObj && mpTextExport)
diff --git a/sd/CppunitTest_sd_shape_import_export_tests.mk 
b/sd/CppunitTest_sd_shape_import_export_tests.mk
new file mode 100644
index 000000000000..89d1f96ef382
--- /dev/null
+++ b/sd/CppunitTest_sd_shape_import_export_tests.mk
@@ -0,0 +1,74 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#*************************************************************************
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+#*************************************************************************
+
+$(eval $(call gb_CppunitTest_CppunitTest,sd_shape_import_export_tests))
+
+$(eval $(call gb_CppunitTest_use_externals,sd_shape_import_export_tests,\
+       boost_headers \
+    libxml2 \
+))
+
+$(eval $(call 
gb_CppunitTest_add_exception_objects,sd_shape_import_export_tests, \
+    sd/qa/unit/ShapeImportExportTest \
+))
+
+$(eval $(call gb_CppunitTest_set_include,sd_shape_import_export_tests,\
+    -I$(SRCDIR)/sd/inc \
+    -I$(SRCDIR)/sd/source/ui/inc \
+    -I$(SRCDIR)/sd/source/ui/slidesorter/inc \
+    -I$(SRCDIR)/svx/source/inc \
+    -I$(SRCDIR)/svx/inc \
+    $$(INCLUDE) \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,sd_shape_import_export_tests, \
+    $(call gb_Helper_optional,AVMEDIA,avmedia) \
+    basegfx \
+    canvastools \
+    comphelper \
+    cppcanvas \
+    cppu \
+    cppuhelper \
+    drawinglayer \
+    editeng \
+    for \
+    forui \
+    i18nlangtag \
+    i18nutil \
+    msfilter \
+    oox \
+    sal \
+    salhelper \
+    sax \
+    sb \
+    sd \
+    sfx \
+    sot \
+    svl \
+    svt \
+    svx \
+    svxcore \
+    test \
+    tl \
+    tk \
+    ucbhelper \
+    unotest \
+    utl \
+    vcl \
+    xo \
+))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,sd_shape_import_export_tests))
+$(eval $(call gb_CppunitTest_use_ure,sd_shape_import_export_tests))
+$(eval $(call gb_CppunitTest_use_vcl,sd_shape_import_export_tests))
+$(eval $(call gb_CppunitTest_use_rdb,sd_shape_import_export_tests,services))
+$(eval $(call gb_CppunitTest_use_configuration,sd_shape_import_export_tests))
+# vim: set noet sw=4 ts=4:
diff --git a/sd/Module_sd.mk b/sd/Module_sd.mk
index 65d56523f30d..0ac7b5763607 100644
--- a/sd/Module_sd.mk
+++ b/sd/Module_sd.mk
@@ -48,6 +48,7 @@ $(eval $(call gb_Module_add_slowcheck_targets,sd,\
     CppunitTest_sd_pdf_import_test \
     CppunitTest_sd_png_export_tests \
     CppunitTest_sd_filter_eppt \
+    CppunitTest_sd_shape_import_export_tests \
 ))
 endif
 
diff --git a/sd/qa/unit/ShapeImportExportTest.cxx 
b/sd/qa/unit/ShapeImportExportTest.cxx
new file mode 100644
index 000000000000..071fdc58c74e
--- /dev/null
+++ b/sd/qa/unit/ShapeImportExportTest.cxx
@@ -0,0 +1,381 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "sdmodeltestbase.hxx"
+
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <com/sun/star/drawing/XShapes.hpp>
+
+#include <svx/svdpage.hxx>
+#include <svx/svdotext.hxx>
+
+using namespace css;
+
+/// Shape / SdrObject import and export tests
+class ShapeImportExportTest : public SdModelTestBaseXML
+{
+public:
+    void testTextDistancesOOXML();
+    void testTextDistancesOOXML_LargerThanTextAreaSpecialCase();
+    void testTextDistancesOOXML_Export();
+
+    CPPUNIT_TEST_SUITE(ShapeImportExportTest);
+    CPPUNIT_TEST(testTextDistancesOOXML);
+    CPPUNIT_TEST(testTextDistancesOOXML_LargerThanTextAreaSpecialCase);
+    CPPUNIT_TEST(testTextDistancesOOXML_Export);
+    CPPUNIT_TEST_SUITE_END();
+
+    virtual void registerNamespaces(xmlXPathContextPtr& pXmlXPathCtx) override
+    {
+        XmlTestTools::registerODFNamespaces(pXmlXPathCtx);
+        XmlTestTools::registerOOXMLNamespaces(pXmlXPathCtx);
+    }
+};
+
+namespace
+{
+SdrObject* searchObject(SdrPage const* pPage, std::u16string_view rName)
+{
+    for (size_t i = 0; i < pPage->GetObjCount(); ++i)
+    {
+        SdrObject* pCurrent = pPage->GetObj(i);
+        if (pCurrent->GetName() == rName)
+            return pCurrent;
+    }
+    return nullptr;
+}
+}
+
+/* Test text distances (insets) */
+void ShapeImportExportTest::testTextDistancesOOXML()
+{
+    ::sd::DrawDocShellRef xDocShell
+        = 
loadURL(m_directories.getURLFromSrc(u"sd/qa/unit/data/TextDistancesInsets1.pptx"),
 PPTX);
+
+    SdrPage const* pPage = GetPage(1, xDocShell);
+    // Bottom Margin = 4cm
+    {
+        std::array<std::u16string_view, 3> aObjectDesc = {
+            u"T, BM - 4cm",
+            u"M, BM - 4cm",
+            u"B, BM - 4cm",
+        };
+
+        for (auto const& rString : aObjectDesc)
+        {
+            auto* pTextObj = dynamic_cast<SdrTextObj*>(searchObject(pPage, 
rString));
+            CPPUNIT_ASSERT(pTextObj);
+            CPPUNIT_ASSERT_EQUAL(tools::Long(-1292), 
pTextObj->GetTextUpperDistance());
+            CPPUNIT_ASSERT_EQUAL(tools::Long(2708), 
pTextObj->GetTextLowerDistance());
+        }
+    }
+
+    // Bottom Margin = 1cm
+    {
+        std::array<std::u16string_view, 3> aObjectDesc = {
+            u"T, BM - 1cm",
+            u"M, BM - 1cm",
+            u"B, BM - 1cm",
+        };
+
+        for (auto const& rString : aObjectDesc)
+        {
+            auto* pTextObj = dynamic_cast<SdrTextObj*>(searchObject(pPage, 
rString));
+            CPPUNIT_ASSERT(pTextObj);
+            CPPUNIT_ASSERT_EQUAL(tools::Long(0), 
pTextObj->GetTextUpperDistance());
+            CPPUNIT_ASSERT_EQUAL(tools::Long(1000), 
pTextObj->GetTextLowerDistance());
+        }
+    }
+
+    // Top + Bottom Margin = 1cm
+    {
+        std::array<std::u16string_view, 3> aObjectDesc = {
+            u"T, TM+BM - 1cm",
+            u"M, TM+BM - 1cm",
+            u"B, TM+BM - 1cm",
+        };
+
+        for (auto const& rString : aObjectDesc)
+        {
+            auto* pTextObj = dynamic_cast<SdrTextObj*>(searchObject(pPage, 
rString));
+            CPPUNIT_ASSERT(pTextObj);
+            CPPUNIT_ASSERT_EQUAL(tools::Long(708), 
pTextObj->GetTextUpperDistance());
+            CPPUNIT_ASSERT_EQUAL(tools::Long(708), 
pTextObj->GetTextLowerDistance());
+        }
+    }
+
+    // No margin - Top + Bottom = 0cm
+    {
+        std::array<std::u16string_view, 3> aObjectDesc = {
+            u"T",
+            u"M",
+            u"B",
+        };
+
+        for (auto const& rString : aObjectDesc)
+        {
+            auto* pTextObj = dynamic_cast<SdrTextObj*>(searchObject(pPage, 
rString));
+            CPPUNIT_ASSERT(pTextObj);
+            CPPUNIT_ASSERT_EQUAL(tools::Long(0), 
pTextObj->GetTextUpperDistance());
+            CPPUNIT_ASSERT_EQUAL(tools::Long(0), 
pTextObj->GetTextLowerDistance());
+        }
+    }
+
+    // Top Margin = 1cm
+    {
+        std::array<std::u16string_view, 3> aObjectDesc = {
+            u"T, TM - 1cm",
+            u"M, TM - 1cm",
+            u"B, TM - 1cm",
+        };
+
+        for (auto const& rString : aObjectDesc)
+        {
+            auto* pTextObj = dynamic_cast<SdrTextObj*>(searchObject(pPage, 
rString));
+            CPPUNIT_ASSERT(pTextObj);
+            CPPUNIT_ASSERT_EQUAL(tools::Long(1000), 
pTextObj->GetTextUpperDistance());
+            CPPUNIT_ASSERT_EQUAL(tools::Long(0), 
pTextObj->GetTextLowerDistance());
+        }
+    }
+
+    // Top Margin = 4cm
+    {
+        std::array<std::u16string_view, 3> aObjectDesc = {
+            u"T, TM - 4cm",
+            u"M, TM - 4cm",
+            u"B, TM - 4cm",
+        };
+
+        for (auto const& rString : aObjectDesc)
+        {
+            auto* pTextObj = dynamic_cast<SdrTextObj*>(searchObject(pPage, 
rString));
+            CPPUNIT_ASSERT(pTextObj);
+            CPPUNIT_ASSERT_EQUAL(tools::Long(2708), 
pTextObj->GetTextUpperDistance());
+            CPPUNIT_ASSERT_EQUAL(tools::Long(-1292), 
pTextObj->GetTextLowerDistance());
+        }
+    }
+
+    xDocShell->DoClose();
+}
+
+/* Test text distances (insets) variants where top+bottom margin > text area*/
+void 
ShapeImportExportTest::testTextDistancesOOXML_LargerThanTextAreaSpecialCase()
+{
+    ::sd::DrawDocShellRef xDocShell
+        = 
loadURL(m_directories.getURLFromSrc(u"sd/qa/unit/data/TextDistancesInsets2.pptx"),
 PPTX);
+
+    SdrPage const* pPage = GetPage(1, xDocShell);
+
+    // Top/Bottom 0cm/3cm, 1cm/4cm, 4cm/7cm - all should be converted to the 
same value in LO
+    {
+        std::array<std::u16string_view, 9> aObjectNames = {
+            u"T_0_3", u"M_0_3", u"B_0_3", u"T_1_4", u"M_1_4",
+            u"B_1_4", u"T_4_7", u"M_4_7", u"B_4_7",
+        };
+
+        for (auto const& rName : aObjectNames)
+        {
+            auto* pTextObj = dynamic_cast<SdrTextObj*>(searchObject(pPage, 
rName));
+            CPPUNIT_ASSERT(pTextObj);
+            CPPUNIT_ASSERT_EQUAL(tools::Long(-792), 
pTextObj->GetTextUpperDistance());
+            CPPUNIT_ASSERT_EQUAL(tools::Long(2208), 
pTextObj->GetTextLowerDistance());
+        }
+    }
+
+    // Top/Bottom 0cm/2cm, 1cm/3cm, 4cm/6cm - all should be converted to the 
same value in LO
+    {
+        std::array<std::u16string_view, 9> aObjectNames = {
+            u"T_0_2", u"M_0_2", u"B_0_2", u"T_1_3", u"M_1_3",
+            u"B_1_3", u"T_4_6", u"M_4_6", u"B_4_6",
+        };
+
+        for (auto const& rName : aObjectNames)
+        {
+            auto* pTextObj = dynamic_cast<SdrTextObj*>(searchObject(pPage, 
rName));
+            CPPUNIT_ASSERT(pTextObj);
+            CPPUNIT_ASSERT_EQUAL(tools::Long(-292), 
pTextObj->GetTextUpperDistance());
+            CPPUNIT_ASSERT_EQUAL(tools::Long(1708), 
pTextObj->GetTextLowerDistance());
+        }
+    }
+
+    // Top/Bottom 2cm/2cm, 3cm/3cm, 4cm/4cm - all should be converted to the 
same value in LO
+    {
+        std::array<std::u16string_view, 9> aObjectNames = {
+            u"T_2_2", u"M_2_2", u"B_2_2", u"T_3_3", u"M_3_3",
+            u"B_3_3", u"T_4_4", u"M_4_4", u"B_4_4",
+        };
+
+        for (auto const& rName : aObjectNames)
+        {
+            auto* pTextObj = dynamic_cast<SdrTextObj*>(searchObject(pPage, 
rName));
+            CPPUNIT_ASSERT(pTextObj);
+            CPPUNIT_ASSERT_EQUAL(tools::Long(708), 
pTextObj->GetTextUpperDistance());
+            CPPUNIT_ASSERT_EQUAL(tools::Long(708), 
pTextObj->GetTextLowerDistance());
+        }
+    }
+
+    // Top/Bottom 2cm/0cm, 3cm/1cm, 6cm/4cm - all should be converted to the 
same value in LO
+    {
+        std::array<std::u16string_view, 9> aObjectNames = {
+            u"T_2_0", u"M_2_0", u"B_2_0", u"T_3_1", u"M_3_1",
+            u"B_3_1", u"T_6_4", u"M_6_4", u"B_6_4",
+        };
+
+        for (auto const& rName : aObjectNames)
+        {
+            auto* pTextObj = dynamic_cast<SdrTextObj*>(searchObject(pPage, 
rName));
+            CPPUNIT_ASSERT(pTextObj);
+            CPPUNIT_ASSERT_EQUAL(tools::Long(1708), 
pTextObj->GetTextUpperDistance());
+            CPPUNIT_ASSERT_EQUAL(tools::Long(-292), 
pTextObj->GetTextLowerDistance());
+        }
+    }
+
+    // Top/Bottom 3cm/0cm, 4cm/1cm, 7cm/4cm - all should be converted to the 
same value in LO
+    {
+        std::array<std::u16string_view, 9> aObjectNames = {
+            u"T_3_0", u"M_3_0", u"B_3_0", u"T_4_1", u"M_4_1",
+            u"B_4_1", u"T_7_4", u"M_7_4", u"B_7_4",
+        };
+
+        for (auto const& rName : aObjectNames)
+        {
+            auto* pTextObj = dynamic_cast<SdrTextObj*>(searchObject(pPage, 
rName));
+            CPPUNIT_ASSERT(pTextObj);
+            CPPUNIT_ASSERT_EQUAL(tools::Long(2208), 
pTextObj->GetTextUpperDistance());
+            CPPUNIT_ASSERT_EQUAL(tools::Long(-792), 
pTextObj->GetTextLowerDistance());
+        }
+    }
+
+    xDocShell->DoClose();
+}
+
+/* Test export of text distances (insets) - conversion back of special case */
+void ShapeImportExportTest::testTextDistancesOOXML_Export()
+{
+    ::sd::DrawDocShellRef xDocShell
+        = 
loadURL(m_directories.getURLFromSrc(u"sd/qa/unit/data/TextDistancesInsets3.pptx"),
 PPTX);
+
+    utl::TempFile aTempFile;
+    xDocShell = saveAndReload(xDocShell.get(), PPTX, &aTempFile);
+    xDocShell->DoClose();
+
+    xmlDocUniquePtr pXmlDoc = parseExport(aTempFile, "ppt/slides/slide1.xml");
+    CPPUNIT_ASSERT(pXmlDoc);
+
+    //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" } });
+
+    //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" } });
+
+    //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" } });
+
+    //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" } });
+
+    //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" } });
+
+    //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");
+    assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[6]/p:txBody/a:bodyPr",
+                     { { "tIns", "0" }, { "bIns", "720000" } });
+
+    //Check shape Top/Bottom - 0cm, 1.5cm
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[7]/p:nvSpPr/p:cNvPr", 
"name",
+                "Text_TB_0_1.5");
+    assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[7]/p:txBody/a:bodyPr",
+                     { { "tIns", "0" }, { "bIns", "540000" } });
+
+    //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" } });
+
+    //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" } });
+
+    //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");
+    assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[10]/p:txBody/a:bodyPr",
+                     { { "tIns", "720000" }, { "bIns", "0" } });
+
+    //Check shape Top/Bottom - 1.5cm, 0cm
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[11]/p:nvSpPr/p:cNvPr", 
"name",
+                "Text_TB_1.5_0");
+    assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[11]/p:txBody/a:bodyPr",
+                     { { "tIns", "540000" }, { "bIns", "0" } });
+
+    //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" } });
+
+    //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" } });
+
+    //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" } });
+
+    //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" } });
+
+    //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" } });
+
+    //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" } });
+
+    //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" } });
+
+    //Check shape Top/Bottom - 0.5cm, 0.5cm
+    assertXPath(pXmlDoc, "/p:sld/p:cSld/p:spTree/p:sp[19]/p:nvSpPr/p:cNvPr", 
"name",
+                "Text_TB_0.5_0.5");
+    assertXPathAttrs(pXmlDoc, 
"/p:sld/p:cSld/p:spTree/p:sp[19]/p:txBody/a:bodyPr",
+                     { { "tIns", "180000" }, { "bIns", "180000" } });
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ShapeImportExportTest);
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sd/qa/unit/data/TextDistancesInsets1.pptx 
b/sd/qa/unit/data/TextDistancesInsets1.pptx
new file mode 100644
index 000000000000..1d1020626c72
Binary files /dev/null and b/sd/qa/unit/data/TextDistancesInsets1.pptx differ
diff --git a/sd/qa/unit/data/TextDistancesInsets2.pptx 
b/sd/qa/unit/data/TextDistancesInsets2.pptx
new file mode 100644
index 000000000000..136d15c9b2d0
Binary files /dev/null and b/sd/qa/unit/data/TextDistancesInsets2.pptx differ
diff --git a/sd/qa/unit/data/TextDistancesInsets3.pptx 
b/sd/qa/unit/data/TextDistancesInsets3.pptx
new file mode 100644
index 000000000000..e977f459c2fb
Binary files /dev/null and b/sd/qa/unit/data/TextDistancesInsets3.pptx differ

Reply via email to