sw/qa/core/layout/calcmove.cxx                                     |    6 -
 sw/qa/extras/ooxmlexport/data/tdf165047_consolidatedTopMargin.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport22.cxx                         |   18 +++++
 sw/source/core/layout/flowfrm.cxx                                  |   33 
++++++++++
 4 files changed, 54 insertions(+), 3 deletions(-)

New commits:
commit 395c4f07a61bf607f3283d7a75be93c95a3571d7
Author:     Justin Luth <justin.l...@collabora.com>
AuthorDate: Wed Apr 2 13:14:16 2025 -0400
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Thu Apr 3 09:00:10 2025 +0200

    tdf#165047 sw mso-compat layout: always consolidate top margin
    
    This improves 25.8 commit ae7900dd42a65aaf60df6b21b9ad511496b209d9
        tdf#164095 sw: fix missing top margin on paragraph
                       after changing page style
    which was backported to 24.8.5.
    
    The problem is that you can't just simply apply the top margin.
    Even though it may be across a page break,
    or even in a different section,
    the already-applied, previous paragraph's bottom margin
    needs to be "removed" from this paragraph's top margin.
    
    make CppunitTest_sw_ooxmlexport22 \
        CPPUNIT_TEST_NAME=testTdf165047_consolidatedTopMargin
    
    Change-Id: I1ca5a6173cc5ae5a06adaf38a3292f556a81a48d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183619
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <jl...@mail.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183649
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/sw/qa/core/layout/calcmove.cxx b/sw/qa/core/layout/calcmove.cxx
index 893d3fd89915..3606c70eb73e 100644
--- a/sw/qa/core/layout/calcmove.cxx
+++ b/sw/qa/core/layout/calcmove.cxx
@@ -96,10 +96,10 @@ CPPUNIT_TEST_FIXTURE(Test, 
testIgnoreTopMarginPageStyleChange)
     sal_Int32 nParaTopMargin
         = getXPath(pXmlDoc, "/root/page[3]/body/txt/infos/prtBounds", 
"top").toInt32();
     // Without the accompanying fix in place, this test would have failed with:
-    // - Expected: 2000
+    // - Expected: 1840
     // - Actual  : 0
-    // i.e. the top margin was ignored, which is incorrect.
-    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(2000), nParaTopMargin);
+    // i.e. the top margin (minus the previous bottom margin) was ignored, 
which is incorrect.
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(1840), nParaTopMargin);
 }
 
 CPPUNIT_TEST_FIXTURE(Test, testHideWhitespaceGrowingLastPage)
diff --git a/sw/qa/extras/ooxmlexport/data/tdf165047_consolidatedTopMargin.docx 
b/sw/qa/extras/ooxmlexport/data/tdf165047_consolidatedTopMargin.docx
new file mode 100644
index 000000000000..7a44a2d14f5f
Binary files /dev/null and 
b/sw/qa/extras/ooxmlexport/data/tdf165047_consolidatedTopMargin.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx
index 34408dcc958b..e01e03f07028 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx
@@ -43,6 +43,24 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf165642_glossaryFootnote)
     parseExport(u"word/glossary/footnotes.xml"_ustr);
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf165047_consolidatedTopMargin)
+{
+    // Given a two page document with a section page break
+    // which is preceded by a paragraph with a lot of lower spacing
+    // and followed by a paragraph with even more upper spacing...
+    loadAndSave("tdf165047_consolidatedTopMargin.docx");
+
+    // the upper spacing is mostly "absorbed" by the preceding lower spacing, 
and is barely noticed
+    CPPUNIT_ASSERT_EQUAL(2, getPages());
+
+    // When laying out that document:
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+    // the effective top margin should be 60pt - 50pt = 10pt (0.36cm) after 
the page break
+    SwTwips nParaTopMargin
+        = getXPath(pXmlDoc, "/root/page[2]/body/section/infos/prtBounds", 
"top").toInt32();
+    CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips>(200), nParaTopMargin);
+}
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/sw/source/core/layout/flowfrm.cxx 
b/sw/source/core/layout/flowfrm.cxx
index a60e51962541..1a82925021ac 100644
--- a/sw/source/core/layout/flowfrm.cxx
+++ b/sw/source/core/layout/flowfrm.cxx
@@ -1536,6 +1536,37 @@ static bool lcl_getContextualSpacing(const SwFrame* 
pPrevFrame)
     return bRet;
 }
 
+/// Implement top-of-the-page layout anomalies needed to match MS Word
+static void lcl_PartiallyCollapseUpper(const SwFrame& rFrame, SwTwips& rUpper)
+{
+    const SwTextFrame* pTextFrame = rFrame.DynCastTextFrame();
+    if (!pTextFrame || !rFrame.IsInDocBody() || rFrame.IsInTab() || 
rFrame.IsInFly())
+        return;
+
+    // re-used existing compat values to identify whether MSO-compatible 
layout is needed
+    const IDocumentSettingAccess& rIDSA = 
pTextFrame->GetDoc().getIDocumentSettingAccess();
+    const bool bCompat15 = rIDSA.get(DocumentSettingId::TAB_OVER_SPACING); // 
MSO 2013+
+    const bool bCompat14 = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN); // 
<= MSO 2010
+    if (!bCompat15 && !bCompat14)
+        return;
+
+    const SwContentFrame* pPrevPara = pTextFrame->FindPrevCnt();
+    while (pPrevPara && pPrevPara->IsHiddenNow())
+        pPrevPara = pPrevPara->FindPrevCnt();
+
+    // Anything related to tables is skipped simply to avoid potential disaster
+    if (!pPrevPara || pPrevPara->IsInTab())
+        return;
+
+    {
+        // MSO is also hyper-consistent about consolidating
+        // the lower-space from the previous paragraph
+        // with the upper spacing of this paragraph
+        const SwTwips nPrevLowerSpace
+            = pPrevPara->GetAttrSet()->GetULSpace().GetLower();
+        rUpper = std::max<SwTwips>(rUpper - nPrevLowerSpace, 0);
+    }
+}
 
 SwTwips SwFlowFrame::CalcUpperSpace( const SwBorderAttrs *pAttrs,
                                    const SwFrame* pPr,
@@ -1709,6 +1740,8 @@ SwTwips SwFlowFrame::CalcUpperSpace( const SwBorderAttrs 
*pAttrs,
             {
                 nUpper = 0;
             }
+            else
+                lcl_PartiallyCollapseUpper(*pOwn, nUpper); // possibly 
modifies nUpper
         }
     }
 

Reply via email to