sw/qa/extras/ooxmlexport/data/tdf166510_sectPr_bottomSpacing.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport25.cxx                        |   11 ++++
 sw/source/writerfilter/dmapper/DomainMapper.cxx                   |    3 +
 sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx              |   23 
++++++++++
 sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx              |    3 +
 5 files changed, 40 insertions(+)

New commits:
commit 23e28e35d18bf9ee0d9a9f72117480971ca437ce
Author:     Justin Luth <jl...@mail.com>
AuthorDate: Fri May 16 11:43:06 2025 -0400
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon May 19 08:49:23 2025 +0200

    tdf#166510 writerfilter: move empty sectPr bottom margin to prev para
    
    This is follow-up to my 25.2.3 commit for tdf#165047:
        sw mso-compat layout: always consolidate top margin
    
    The problem here was not the consolidation itself,
    but the fact that the paragraph after the section page break
    needs to consolidate based on the below spacing of the sectPr paragraph
    and we simply deleted that info along with the empty sectPr.
    The result was that consolidation was working with the wrong number.
    
    Since the bottom margin of the previous paragraph
    has no impact on the empty sectPr,
    we can simply transfer the sectPr's spacing to the previous paragraph,
    which of course now becomes the last paragraph before the page break.
    
    Slightly unexpected were some examples where no spacing was identified
    for the sectPr context. The unit test includes this complication...
    
    make CppunitTest_sw_ooxmlexport25 \
        CPPUNIT_TEST_NAME=testTdf166510_sectPr_bottomSpacing
    
    Change-Id: I9bcae239480035b02653ec93a37f6bf05d135d8f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185424
    Reviewed-by: Justin Luth <jl...@mail.com>
    Tested-by: Jenkins
    (cherry picked from commit 5ff1fd13f1879105ecc0541c55f30cea32ae54f4)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185430
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/sw/qa/extras/ooxmlexport/data/tdf166510_sectPr_bottomSpacing.docx 
b/sw/qa/extras/ooxmlexport/data/tdf166510_sectPr_bottomSpacing.docx
new file mode 100644
index 000000000000..620335bde7d5
Binary files /dev/null and 
b/sw/qa/extras/ooxmlexport/data/tdf166510_sectPr_bottomSpacing.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index cf48755a7765..2b2976aefac5 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -43,6 +43,17 @@ DECLARE_OOXMLEXPORT_TEST(testTdf166544_noTopMargin_fields, 
"tdf166544_noTopMargi
     CPPUNIT_ASSERT_EQUAL(sal_Int32(269), nHeight);
 }
 
+DECLARE_OOXMLEXPORT_TEST(testTdf166510_sectPr_bottomSpacing, 
"tdf166510_sectPr_bottomSpacing.docx")
+{
+    // given with a sectPr with different bottom spacing (undefined in this 
case - i.e. zero)
+    auto pXmlDoc = parseLayoutDump();
+
+    // The last paragraph (sectPr) has 0 below spacing, so no reduction of 
page 2's 200pt top margin
+    sal_Int32 nHeight = getXPath(pXmlDoc, "//page[2]//body/txt/infos/bounds", 
"height").toInt32();
+    // Without the fix, the text height (showing no top margin at all) was 253
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(4253), nHeight);
+}
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/sw/source/writerfilter/dmapper/DomainMapper.cxx 
b/sw/source/writerfilter/dmapper/DomainMapper.cxx
index b2c4eeec8dc6..01415e209783 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper.cxx
@@ -4592,6 +4592,9 @@ void DomainMapper::lcl_utext(const sal_Unicode *const 
data_, size_t len)
                             && !m_pImpl->HasTopAnchoredObjects()
                             && !m_pImpl->IsParaWithInlineObject());
 
+            if (bRemove && !m_pImpl->GetRemoveThisPara())
+                m_pImpl->handleSectPrBeforeRemoval();
+
             const bool bNoNumbering = bRemove || (!m_pImpl->GetParaChanged() 
&& m_pImpl->GetParaSectpr() && bSingleParagraph);
             PropertyMapPtr xContext = bNoNumbering ? 
m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH) : PropertyMapPtr();
             if (xContext)
diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx 
b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
index 743b9bdcf8a5..0eba41c7c43b 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
@@ -10181,6 +10181,29 @@ bool 
DomainMapper_Impl::handlePreviousParagraphBorderInBetween() const
     return true;
 }
 
+void DomainMapper_Impl::handleSectPrBeforeRemoval()
+{
+    PropertyMapPtr pSectPrContext = GetTopContextOfType(CONTEXT_PARAGRAPH);
+    if (!pSectPrContext)
+        return;
+
+    // transfer below-spacing to previous paragraph - needed to layout 
above-spacing after pageBreak
+    if (!m_StreamStateStack.top().xPreviousParagraph.is())
+        return;
+
+    uno::Any aBelowSpacing = GetAnyProperty(PROP_PARA_BOTTOM_MARGIN, 
pSectPrContext);
+    if (!aBelowSpacing.hasValue())
+        aBelowSpacing <<= sal_Int32(0);
+
+    std::optional<uno::Any> oPrevBelowSpacing;
+    OUString sProp(getPropertyName(PROP_PARA_BOTTOM_MARGIN));
+    if 
(m_StreamStateStack.top().xPreviousParagraph->getPropertySetInfo()->hasPropertyByName(sProp))
+        oPrevBelowSpacing = 
m_StreamStateStack.top().xPreviousParagraph->getPropertyValue(sProp);
+
+    if (!oPrevBelowSpacing.has_value() || aBelowSpacing != *oPrevBelowSpacing)
+        m_StreamStateStack.top().xPreviousParagraph->setPropertyValue(sProp, 
aBelowSpacing);
+}
+
 OUString DomainMapper_Impl::getFontNameForTheme(const Id id)
 {
     auto const& pHandler = getThemeHandler();
diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx 
b/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx
index 9d5274d7ed62..96a840b1a414 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.hxx
@@ -1247,6 +1247,9 @@ public:
     /// Check if previous paragraph has borders in between and do the border 
magic to it if so
     bool handlePreviousParagraphBorderInBetween() const;
 
+    /// An empty SectPr paragraph is not kept in LO. Do some adjustments 
before it gets deleted.
+    void handleSectPrBeforeRemoval();
+
     /// Handle redline text portions in a frame, footnotes and redlines:
     /// store their data, and create them after frame creation or 
footnote/endnote copying
     bool m_bIsActualParagraphFramed;

Reply via email to