[Libreoffice-commits] core.git: sw/qa writerfilter/source

Attila Szűcs (via logerrit) logerrit at kemper.freedesktop.org
Fri Jul 2 13:09:01 UTC 2021


 sw/qa/extras/ooxmlexport/data/tdf119952_negativeMargins.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport11.cxx                   |   83 +++++++++++
 writerfilter/source/dmapper/DomainMapperTableHandler.cxx     |   38 -----
 writerfilter/source/dmapper/DomainMapper_Impl.cxx            |   63 ++++++++
 writerfilter/source/dmapper/DomainMapper_Impl.hxx            |    3 
 writerfilter/source/dmapper/PropertyMap.cxx                  |   78 ++++------
 writerfilter/source/dmapper/PropertyMap.hxx                  |    7 
 7 files changed, 190 insertions(+), 82 deletions(-)

New commits:
commit d656191ec308d4280b93c7169372e543a255d108
Author:     Attila Szűcs <szucs.attila3 at nisz.hu>
AuthorDate: Fri Jun 25 10:37:47 2021 +0200
Commit:     László Németh <nemeth at numbertext.org>
CommitDate: Fri Jul 2 15:08:24 2021 +0200

    tdf#119952 DOCX import: fix negative page margins
    
    DOCX body text can overlap with header/footer, if top/bottom
    page margin is negative. To support this, convert header/footer
    text content to textbox anchored to header/footer, if needed.
    
    Note: possible improvements:
    
    1) Skip this hack, if the header is small enough to not overlap with
    the body, calculate only the height of the header at the import time.
    
    2) This hack does not fix the case when the top of the header
    is under the top of the body. (A problem in DOC import, too.)
    This could be achieved by repositioning the dummy header to the top,
    and lower the textbox by the same amount. (This would still not
    resolve the extreme situation, when the body start from 0 mm
    (in LibreOffice, header must be at least 1 mm).
    
    3) Import of VertOrientation::BOTTOM property seems to be bad,
    or at least the footer loses this property after a DOCX round-trip,
    resulting bad footer position.
    
    4) after a round-trip, the 1 mm height of the dummy header
    increases to 1 line height. Also the "Autofit height" and
    "Use dynamic spacing" settings are changed, likely related
    to their missing DOCX export.
    
    Co-authored-by: Tibor Nagy (NISZ)
    
    Change-Id: I8319c93c6c5a980878ee9956c8ab2953da60409e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/117842
    Tested-by: László Németh <nemeth at numbertext.org>
    Reviewed-by: László Németh <nemeth at numbertext.org>

diff --git a/sw/qa/extras/ooxmlexport/data/tdf119952_negativeMargins.docx b/sw/qa/extras/ooxmlexport/data/tdf119952_negativeMargins.docx
new file mode 100644
index 000000000000..9b5a98d3f1aa
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf119952_negativeMargins.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
index d999827899a4..9cfaf5aa6e6c 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
@@ -1458,6 +1458,89 @@ DECLARE_OOXMLEXPORT_TEST(testTdf124678_case2, "tdf124678_with_leading_paragraph.
     CPPUNIT_ASSERT_EQUAL_MESSAGE("Second page header text", OUString("HEADER"), parseDump("/root/page[2]/header/txt"));
 }
 
+static bool lcl_nearEqual(const sal_Int32 nNumber1, const sal_Int32 nNumber2, sal_Int32 nMaxDiff = 5)
+{
+    return std::abs(nNumber1 - nNumber2) < nMaxDiff;
+}
+
+DECLARE_OOXMLEXPORT_TEST(testTdf119952_negativeMargins, "tdf119952_negativeMargins.docx")
+{
+    // With negative margins (in MS Word) one can set up header (or footer) that overlaps with the body.
+    // LibreOffice unable to display that, so when importing negative margins,
+    // the header (or footer) converted to a flyframe, anchored to the header..
+    // that can overlap with the body, and will appear like in Word.
+    // This conversion modifies the document [i.e. replacing header text with a textbox...]
+    // but its DOCX export looks the same, as the original document in Word, too.
+    xmlDocUniquePtr pDump = parseLayoutDump();
+
+    //Check layout positions / sizes
+    sal_Int32 nLeftHead = getXPath(pDump, "//page[1]/header/infos/bounds", "left").toInt32();
+    sal_Int32 nLeftBody = getXPath(pDump, "//page[1]/body/infos/bounds", "left").toInt32();
+    sal_Int32 nLeftFoot = getXPath(pDump, "//page[1]/footer/infos/bounds", "left").toInt32();
+    sal_Int32 nLeftHFly = getXPath(pDump, "//page[1]/header/txt/anchored/fly/infos/bounds", "left").toInt32();
+    sal_Int32 nLeftFFly = getXPath(pDump, "//page[1]/footer/txt/anchored/fly/infos/bounds", "left").toInt32();
+
+    sal_Int32 nTopHead = getXPath(pDump, "//page[1]/header/infos/bounds", "top").toInt32();
+    sal_Int32 nTopBody = getXPath(pDump, "//page[1]/body/infos/bounds", "top").toInt32();
+    sal_Int32 nTopFoot = getXPath(pDump, "//page[1]/footer/infos/bounds", "top").toInt32();
+    sal_Int32 nTopHFly = getXPath(pDump, "//page[1]/header/txt/anchored/fly/infos/bounds", "top").toInt32();
+    sal_Int32 nTopFFly = getXPath(pDump, "//page[1]/footer/txt/anchored/fly/infos/bounds", "top").toInt32();
+
+    sal_Int32 nHeightHead = getXPath(pDump, "//page[1]/header/infos/bounds", "height").toInt32();
+    sal_Int32 nHeightBody = getXPath(pDump, "//page[1]/body/infos/bounds", "height").toInt32();
+    sal_Int32 nHeightFoot = getXPath(pDump, "//page[1]/footer/infos/bounds", "height").toInt32();
+    sal_Int32 nHeightHFly = getXPath(pDump, "//page[1]/header/txt/anchored/fly/infos/bounds", "height").toInt32();
+    sal_Int32 nHeightFFly = getXPath(pDump, "//page[1]/footer/txt/anchored/fly/infos/bounds", "height").toInt32();
+    sal_Int32 nHeightHFlyBound = getXPath(pDump, "//page[1]/header/infos/prtBounds", "height").toInt32();
+    sal_Int32 nHeightFFlyBound = getXPath(pDump, "//page[1]/footer/infos/prtBounds", "height").toInt32();
+
+    CPPUNIT_ASSERT(lcl_nearEqual(nLeftHead, nLeftBody));
+    CPPUNIT_ASSERT(lcl_nearEqual(nLeftHead, nLeftFoot));
+    CPPUNIT_ASSERT(lcl_nearEqual(nLeftHead, nLeftHFly));
+    CPPUNIT_ASSERT(lcl_nearEqual(nLeftHead, nLeftFFly));
+
+    CPPUNIT_ASSERT(lcl_nearEqual(nTopHead, 851));
+    CPPUNIT_ASSERT(lcl_nearEqual(nTopBody, 1418));
+    CPPUNIT_ASSERT(lcl_nearEqual(nTopFoot, 15875));
+    CPPUNIT_ASSERT(lcl_nearEqual(nTopHFly, 851));
+
+    // this seems to be an import bug
+    if (!mbExported)
+        CPPUNIT_ASSERT(lcl_nearEqual(nTopFFly, 14403));
+
+    CPPUNIT_ASSERT(lcl_nearEqual(nHeightHead, 567));
+    CPPUNIT_ASSERT(lcl_nearEqual(nHeightBody, 14457));
+    CPPUNIT_ASSERT(lcl_nearEqual(nHeightFoot, 680));
+    CPPUNIT_ASSERT(lcl_nearEqual(nHeightHFly, 2152));
+    CPPUNIT_ASSERT(lcl_nearEqual(nHeightFFly, 2152));
+
+    // after export these heights increase to like 567..
+    // not sure if it is an other import, or export bug... or just the result of the modified document
+    if (!mbExported)
+    {
+        CPPUNIT_ASSERT(lcl_nearEqual(nHeightHFlyBound, 57));
+        CPPUNIT_ASSERT(lcl_nearEqual(nHeightFFlyBound, 57));
+    }
+
+    //Check text of header/ footer
+    CPPUNIT_ASSERT_EQUAL(OUString("f1"), getXPath(pDump, "//page[1]/header/txt/anchored/fly/txt[1]/Text", "Portion"));
+    CPPUNIT_ASSERT_EQUAL(OUString("                f8"), getXPath(pDump, "//page[1]/header/txt/anchored/fly/txt[8]/Text", "Portion"));
+    CPPUNIT_ASSERT_EQUAL(OUString("                f8"), getXPath(pDump, "//page[1]/footer/txt/anchored/fly/txt[1]/Text", "Portion"));
+    CPPUNIT_ASSERT_EQUAL(OUString("f1"), getXPath(pDump, "//page[1]/footer/txt/anchored/fly/txt[8]/Text", "Portion"));
+
+    CPPUNIT_ASSERT_EQUAL(OUString("p1"), getXPath(pDump, "//page[2]/header/txt/anchored/fly/txt[1]/Text", "Portion"));
+    CPPUNIT_ASSERT_EQUAL(OUString("p1"), getXPath(pDump, "//page[2]/footer/txt/anchored/fly/txt[1]/Text", "Portion"));
+
+    CPPUNIT_ASSERT_EQUAL(OUString("  aaaa"), getXPath(pDump, "//page[3]/header/txt/anchored/fly/txt[1]/Text", "Portion"));
+    CPPUNIT_ASSERT_EQUAL(OUString("      eeee"), getXPath(pDump, "//page[3]/header/txt/anchored/fly/txt[5]/Text", "Portion"));
+
+    CPPUNIT_ASSERT_EQUAL(OUString("f1    f2      f3        f4          f5            f6              f7                f8"), parseDump("/root/page[1]/header/txt/anchored/fly"));
+    CPPUNIT_ASSERT_EQUAL(OUString("                f8              f7            f6          f5        f4      f3    f2f1"), parseDump("/root/page[1]/footer/txt/anchored/fly"));
+    CPPUNIT_ASSERT_EQUAL(OUString("p1"), parseDump("/root/page[2]/header/txt/anchored/fly"));
+    CPPUNIT_ASSERT_EQUAL(OUString("p1"), parseDump("/root/page[2]/footer/txt/anchored/fly"));
+    CPPUNIT_ASSERT_EQUAL(OUString("  aaaa   bbbb    cccc     dddd      eeee"), parseDump("/root/page[3]/header/txt/anchored/fly"));
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
index 55c48740f0ec..884a195df75a 100644
--- a/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
+++ b/writerfilter/source/dmapper/DomainMapperTableHandler.cxx
@@ -340,38 +340,6 @@ void lcl_adjustBorderDistance(TableInfo& rInfo, const table::BorderLine2& rLeftB
     rInfo.nRightBorderDistance = nActualR;
 }
 
-void lcl_fillEmptyFrameProperties(std::vector<beans::PropertyValue>& rFrameProperties)
-{
-    // fill empty frame properties to create an invisible frame around the table:
-    // hide frame borders and zero inner and outer frame margins
-    beans::PropertyValue aValue;
-    aValue.Name = getPropertyName( PROP_ANCHOR_TYPE );
-    aValue.Value <<= text::TextContentAnchorType_AS_CHARACTER;
-    rFrameProperties.push_back(aValue);
-
-    table::BorderLine2 aEmptyBorder;
-    static const std::vector<std::u16string_view> aBorderNames
-        = { u"TopBorder", u"LeftBorder", u"BottomBorder", u"RightBorder" };
-    for (size_t i = 0; i < aBorderNames.size(); ++i)
-    {
-        beans::PropertyValue aBorderValue;
-        aBorderValue.Name = aBorderNames[i];
-        aBorderValue.Value <<= aEmptyBorder;
-        rFrameProperties.push_back(aBorderValue);
-    }
-    static const std::vector<std::u16string_view> aMarginNames
-        = { u"TopBorderDistance", u"LeftBorderDistance",
-            u"BottomBorderDistance", u"RightBorderDistance",
-            u"TopMargin", u"LeftMargin", u"BottomMargin", u"RightMargin" };
-    for (size_t i = 0; i < aMarginNames.size(); ++i)
-    {
-        beans::PropertyValue aMarginValue;
-        aMarginValue.Name = aMarginNames[i];
-        aMarginValue.Value <<= sal_Int32(10);
-        rFrameProperties.push_back(aMarginValue);
-    }
-}
-
 }
 
 TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo,
@@ -1440,8 +1408,10 @@ void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTab
         uno::Reference<text::XTextRange> xStart;
         uno::Reference<text::XTextRange> xEnd;
 
-        if ( bConvertToFloating )
-            lcl_fillEmptyFrameProperties(aFrameProperties);
+        // fill empty frame properties to create an invisible frame around the table:
+        // hide frame borders and zero inner and outer frame margins
+        if (bConvertToFloating)
+            DomainMapper_Impl::fillEmptyFrameProperties(aFrameProperties, true);
 
         // OOXML table style may contain paragraph properties, apply these on cell paragraphs
         if ( m_aTableRanges[0].hasElements() && m_aTableRanges[0][0].hasElements() )
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index e5823f6b43e5..75812194a12d 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -2600,6 +2600,64 @@ void DomainMapper_Impl::appendGlossaryEntry()
     appendTextSectionAfter(m_xGlossaryEntryStart);
 }
 
+void DomainMapper_Impl::fillEmptyFrameProperties(std::vector<beans::PropertyValue>& rFrameProperties, bool bSetAnchorToChar)
+{
+    if (bSetAnchorToChar)
+        rFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_ANCHOR_TYPE), text::TextContentAnchorType_AS_CHARACTER));
+
+    uno::Any aEmptyBorder = uno::makeAny(table::BorderLine2());
+    static const std::vector<PropertyIds> aBorderIds
+        = { PROP_BOTTOM_BORDER, PROP_LEFT_BORDER, PROP_RIGHT_BORDER, PROP_TOP_BORDER };
+    for (size_t i = 0; i < aBorderIds.size(); ++i)
+        rFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(aBorderIds[i]), aEmptyBorder));
+
+    static const std::vector<PropertyIds> aMarginIds
+        = { PROP_BOTTOM_MARGIN, PROP_BOTTOM_BORDER_DISTANCE,
+            PROP_LEFT_MARGIN,   PROP_LEFT_BORDER_DISTANCE,
+            PROP_RIGHT_MARGIN,  PROP_RIGHT_BORDER_DISTANCE,
+            PROP_TOP_MARGIN,    PROP_TOP_BORDER_DISTANCE };
+    for (size_t i = 0; i < aMarginIds.size(); ++i)
+        rFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(aMarginIds[i]), static_cast<sal_Int32>(0)));
+}
+
+void DomainMapper_Impl::ConvertHeaderFooterToTextFrame(bool bDynamicHeightTop, bool bDynamicHeightBottom)
+{
+    while (!m_aHeaderFooterTextAppendStack.empty())
+    {
+        auto aFooterHeader = m_aHeaderFooterTextAppendStack.top();
+        if ((aFooterHeader.second && !bDynamicHeightTop) || (!aFooterHeader.second && !bDynamicHeightBottom))
+        {
+            uno::Reference< text::XTextAppend > xTextAppend = aFooterHeader.first.xTextAppend;
+            uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursor();
+            uno::Reference< text::XTextRange > xRangeStart, xRangeEnd;
+
+            xRangeStart = xCursor->getStart();
+            xCursor->gotoEnd(false);
+            xRangeEnd = xCursor->getStart();
+
+            std::vector<beans::PropertyValue> aFrameProperties;
+
+            aFrameProperties.push_back(comphelper::makePropertyValue("TextWrap", css::text::WrapTextMode_THROUGH));
+            aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT), text::HoriOrientation::LEFT));
+            aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_OPAQUE), false));
+            aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE), text::SizeType::MIN));
+            aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE), text::SizeType::MIN));
+
+            fillEmptyFrameProperties(aFrameProperties, false);
+
+            // If it is a footer, then orient the frame to the bottom
+            if (!aFooterHeader.second)
+                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT), text::VertOrientation::BOTTOM));
+
+            uno::Reference<text::XTextAppendAndConvert> xBodyText(
+                xRangeStart->getText(), uno::UNO_QUERY);
+            xBodyText->convertToTextFrame(xRangeStart, xRangeEnd,
+                comphelper::containerToSequence(aFrameProperties));
+        }
+        m_aHeaderFooterTextAppendStack.pop();
+    }
+}
+
 void DomainMapper_Impl::PushPageHeaderFooter(bool bHeader, SectionPropertyMap::PageType eType)
 {
     m_bSaveParaHadField = m_bParaHadField;
@@ -2662,6 +2720,11 @@ void DomainMapper_Impl::PushPageHeaderFooter(bool bHeader, SectionPropertyMap::P
                 m_bIsNewDoc
                     ? uno::Reference<text::XTextCursor>()
                     : xText->createTextCursorByRange(xText->getStart())));
+            m_aHeaderFooterTextAppendStack.push(std::make_pair(TextAppendContext(uno::Reference< text::XTextAppend >(xText, uno::UNO_QUERY_THROW),
+                m_bIsNewDoc
+                    ? uno::Reference<text::XTextCursor>()
+                    : xText->createTextCursorByRange(xText->getStart())),
+                bHeader));
             m_bDiscardHeaderFooter = false; // set only on success!
         }
         // If we have *hidden* header footer
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index 5212653dcd47..845e902a9ee3 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -467,6 +467,7 @@ private:
     std::stack<TextAppendContext>                                                   m_aTextAppendStack;
     std::stack<AnchoredContext>                                                     m_aAnchoredStack;
     std::stack<HeaderFooterContext>                                                 m_aHeaderFooterStack;
+    std::stack<std::pair<TextAppendContext, bool>>                                  m_aHeaderFooterTextAppendStack;
     std::deque<FieldContextPtr> m_aFieldStack;
     bool m_bForceGenericFields;
     bool                                                                            m_bSetUserFieldContent;
@@ -820,6 +821,8 @@ public:
 
     void PopPageHeaderFooter();
     bool IsInHeaderFooter() const { return m_eInHeaderFooterImport != HeaderFooterImportState::none; }
+    void ConvertHeaderFooterToTextFrame(bool, bool);
+    static void fillEmptyFrameProperties(std::vector<css::beans::PropertyValue>& rFrameProperties, bool bSetAnchorToChar);
 
     bool IsInTOC() const { return m_bStartTOC; }
 
diff --git a/writerfilter/source/dmapper/PropertyMap.cxx b/writerfilter/source/dmapper/PropertyMap.cxx
index 9b4c819e35cb..cacadef477d4 100644
--- a/writerfilter/source/dmapper/PropertyMap.cxx
+++ b/writerfilter/source/dmapper/PropertyMap.cxx
@@ -425,6 +425,8 @@ SectionPropertyMap::SectionPropertyMap( bool bIsFirstSection )
     , m_nLnc(NS_ooxml::LN_Value_ST_LineNumberRestart_newPage)
     , m_ndxaLnn( 0 )
     , m_nLnnMin( 0 )
+    , m_bDynamicHeightTop( true )
+    , m_bDynamicHeightBottom( true )
     , m_bDefaultHeaderLinkToPrevious( true )
     , m_bEvenPageHeaderLinkToPrevious( true )
     , m_bFirstPageHeaderLinkToPrevious( true )
@@ -982,28 +984,25 @@ void SectionPropertyMap::PrepareHeaderFooterProperties( bool bFirstPage )
     bool bCopyFirstToFollow = bFirstPage && m_bTitlePage && m_aFollowPageStyle.is();
 
     sal_Int32 nTopMargin = m_nTopMargin;
-    sal_Int32 nHeaderTop = m_nHeaderTop;
+    sal_Int32 nHeaderHeight = m_nHeaderTop;
     if ( HasHeader( bFirstPage ) )
     {
-        nTopMargin = nHeaderTop;
-        if ( m_nTopMargin > 0 && m_nTopMargin > nHeaderTop )
-            nHeaderTop = m_nTopMargin - nHeaderTop;
-        else
-            nHeaderTop = 0;
+        nTopMargin = m_nHeaderTop;
+        nHeaderHeight = m_nTopMargin - m_nHeaderTop;
 
         // minimum header height 1mm
-        if ( nHeaderTop < MIN_HEAD_FOOT_HEIGHT )
-            nHeaderTop = MIN_HEAD_FOOT_HEIGHT;
+        if ( nHeaderHeight < MIN_HEAD_FOOT_HEIGHT )
+            nHeaderHeight = MIN_HEAD_FOOT_HEIGHT;
     }
 
+    Insert(PROP_HEADER_IS_DYNAMIC_HEIGHT, uno::makeAny(m_bDynamicHeightTop));
+    Insert(PROP_HEADER_DYNAMIC_SPACING, uno::makeAny(m_bDynamicHeightTop));
+    Insert(PROP_HEADER_BODY_DISTANCE, uno::makeAny(nHeaderHeight - MIN_HEAD_FOOT_HEIGHT));
+    Insert(PROP_HEADER_HEIGHT, uno::makeAny(nHeaderHeight));
+    // looks like PROP_HEADER_HEIGHT = height of the header + space between the header, and the body
 
-    if ( m_nTopMargin >= 0 ) //fixed height header -> see WW8Par6.hxx
+    if ( m_bDynamicHeightTop ) //fixed height header -> see WW8Par6.hxx
     {
-        Insert( PROP_HEADER_IS_DYNAMIC_HEIGHT, uno::makeAny( true ) );
-        Insert( PROP_HEADER_DYNAMIC_SPACING, uno::makeAny( true ) );
-        Insert( PROP_HEADER_BODY_DISTANCE, uno::makeAny( nHeaderTop - MIN_HEAD_FOOT_HEIGHT ) );// ULSpace.Top()
-        Insert( PROP_HEADER_HEIGHT, uno::makeAny( nHeaderTop ) );
-
         if (bCopyFirstToFollow && HasHeader(/*bFirstPage=*/true))
         {
             m_aFollowPageStyle->setPropertyValue("HeaderDynamicSpacing",
@@ -1012,36 +1011,25 @@ void SectionPropertyMap::PrepareHeaderFooterProperties( bool bFirstPage )
                                                  getProperty(PROP_HEADER_HEIGHT)->second);
         }
     }
-    else
-    {
-        //todo: old filter fakes a frame into the header/footer to support overlapping
-        //current setting is completely wrong!
-        Insert( PROP_HEADER_HEIGHT, uno::makeAny( nHeaderTop ) );
-        Insert( PROP_HEADER_BODY_DISTANCE, uno::makeAny( m_nTopMargin - nHeaderTop ) );
-        Insert( PROP_HEADER_IS_DYNAMIC_HEIGHT, uno::makeAny( false ) );
-        Insert( PROP_HEADER_DYNAMIC_SPACING, uno::makeAny( false ) );
-    }
 
     sal_Int32 nBottomMargin = m_nBottomMargin;
-    sal_Int32 nHeaderBottom = m_nHeaderBottom;
+    sal_Int32 nFooterHeight = m_nHeaderBottom;
     if ( HasFooter( bFirstPage ) )
     {
-        nBottomMargin = nHeaderBottom;
-        if ( m_nBottomMargin > 0 && m_nBottomMargin > nHeaderBottom )
-            nHeaderBottom = m_nBottomMargin - nHeaderBottom;
-        else
-            nHeaderBottom = 0;
-        if ( nHeaderBottom < MIN_HEAD_FOOT_HEIGHT )
-            nHeaderBottom = MIN_HEAD_FOOT_HEIGHT;
+        nBottomMargin = m_nHeaderBottom;
+        nFooterHeight = m_nBottomMargin - m_nHeaderBottom;
+
+        // minimum footer height 1mm
+        if ( nFooterHeight < MIN_HEAD_FOOT_HEIGHT )
+            nFooterHeight = MIN_HEAD_FOOT_HEIGHT;
     }
 
-    if ( m_nBottomMargin >= 0 ) //fixed height footer -> see WW8Par6.hxx
+    Insert(PROP_FOOTER_IS_DYNAMIC_HEIGHT, uno::makeAny(m_bDynamicHeightBottom));
+    Insert(PROP_FOOTER_DYNAMIC_SPACING, uno::makeAny(m_bDynamicHeightBottom));
+    Insert(PROP_FOOTER_BODY_DISTANCE, uno::makeAny(nFooterHeight - MIN_HEAD_FOOT_HEIGHT));
+    Insert(PROP_FOOTER_HEIGHT, uno::makeAny(nFooterHeight));
+    if (m_bDynamicHeightBottom) //fixed height footer -> see WW8Par6.hxx
     {
-        Insert( PROP_FOOTER_IS_DYNAMIC_HEIGHT, uno::makeAny( true ) );
-        Insert( PROP_FOOTER_DYNAMIC_SPACING, uno::makeAny( true ) );
-        Insert( PROP_FOOTER_BODY_DISTANCE, uno::makeAny( nHeaderBottom - MIN_HEAD_FOOT_HEIGHT ) );
-        Insert( PROP_FOOTER_HEIGHT, uno::makeAny( nHeaderBottom ) );
-
         if (bCopyFirstToFollow && HasFooter(/*bFirstPage=*/true))
         {
             m_aFollowPageStyle->setPropertyValue("FooterDynamicSpacing",
@@ -1050,15 +1038,6 @@ void SectionPropertyMap::PrepareHeaderFooterProperties( bool bFirstPage )
                                                  getProperty(PROP_FOOTER_HEIGHT)->second);
         }
     }
-    else
-    {
-        //todo: old filter fakes a frame into the header/footer to support overlapping
-        //current setting is completely wrong!
-        Insert( PROP_FOOTER_IS_DYNAMIC_HEIGHT, uno::makeAny( false ) );
-        Insert( PROP_FOOTER_DYNAMIC_SPACING, uno::makeAny( false ) );
-        Insert( PROP_FOOTER_HEIGHT, uno::makeAny( m_nBottomMargin - nHeaderBottom ) );
-        Insert( PROP_FOOTER_BODY_DISTANCE, uno::makeAny( nHeaderBottom ) );
-    }
 
     //now set the top/bottom margin for the follow page style
     Insert( PROP_TOP_MARGIN, uno::makeAny( std::max<sal_Int32>(nTopMargin, 0) ) );
@@ -1134,6 +1113,13 @@ void SectionPropertyMap::HandleMarginsHeaderFooter( bool bFirstPage, DomainMappe
     */
     CopyLastHeaderFooter( bFirstPage, rDM_Impl );
     PrepareHeaderFooterProperties( bFirstPage );
+
+    // tdf#119952: If top/bottom margin was negative during docx import,
+    // then the header/footer and the body could be on top of each other
+    // writer is unable to display both of them in the same position, but can be simulated
+    // by moving the header/footer text into a flyframe anchored to the header/footer,
+    // leaving an empty dummy header/footer.
+    rDM_Impl.ConvertHeaderFooterToTextFrame(m_bDynamicHeightTop, m_bDynamicHeightBottom);
 }
 
 bool SectionPropertyMap::FloatingTableConversion( const DomainMapper_Impl& rDM_Impl, FloatingTableInfo& rInfo )
diff --git a/writerfilter/source/dmapper/PropertyMap.hxx b/writerfilter/source/dmapper/PropertyMap.hxx
index d4f981c5db91..917849957ce0 100644
--- a/writerfilter/source/dmapper/PropertyMap.hxx
+++ b/writerfilter/source/dmapper/PropertyMap.hxx
@@ -270,6 +270,9 @@ private:
     sal_Int32                                       m_ndxaLnn;
     sal_Int32                                       m_nLnnMin;
 
+    bool                                            m_bDynamicHeightTop;
+    bool                                            m_bDynamicHeightBottom;
+
     std::vector<css::uno::Reference<css::drawing::XShape>>    m_xRelativeWidthShapes;
 
     // The "Link To Previous" flag indicates whether the header/footer
@@ -378,8 +381,8 @@ public:
     sal_Int32 GetLeftMargin() const        { return m_nLeftMargin; }
     void SetRightMargin( sal_Int32 nSet )  { m_nRightMargin = nSet; }
     sal_Int32 GetRightMargin() const       { return m_nRightMargin; }
-    void SetTopMargin( sal_Int32 nSet )    { m_nTopMargin = nSet; }
-    void SetBottomMargin( sal_Int32 nSet ) { m_nBottomMargin = nSet; }
+    void SetTopMargin(sal_Int32 nSet)      { m_bDynamicHeightTop = nSet >= 0; m_nTopMargin = std::abs(nSet); }
+    void SetBottomMargin( sal_Int32 nSet ) { m_bDynamicHeightBottom = nSet >= 0; m_nBottomMargin = std::abs(nSet); }
     void SetHeaderTop( sal_Int32 nSet )    { m_nHeaderTop = nSet; }
     void SetHeaderBottom( sal_Int32 nSet ) { m_nHeaderBottom = nSet; }
     void SetGutterMargin( sal_Int32 nGutterMargin ) { m_nGutterMargin = nGutterMargin; }


More information about the Libreoffice-commits mailing list