sw/qa/extras/ooxmlexport/data/tdf153964_topMarginAfterBreak14.docx |binary
 sw/qa/extras/ooxmlexport/data/tdf153964_topMarginAfterBreak15.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport18.cxx                         |  101 
+++++++++-
 writerfilter/source/dmapper/DomainMapper.cxx                       |   62 
++++++
 4 files changed, 156 insertions(+), 7 deletions(-)

New commits:
commit e099d21f540a0018687064953832163815549b2e
Author:     Justin Luth <justin.l...@collabora.com>
AuthorDate: Tue Mar 7 07:43:14 2023 -0500
Commit:     Justin Luth <jl...@mail.com>
CommitDate: Wed Mar 8 23:34:15 2023 +0000

    tdf#153964 writerfilter compat15: top margin after break split
    
    The problem was that the top margin was being applied
    to the new paragraph after the split.
    
    This is absolutely attrocious in MS Word before 2013.
    If I am going to be in error, I want to error on the side
    of being compliant with compat15, since all other versions
    of MS Word are now unsupported.
    
    I think I have the logic of it mostly figured out.
    In compat15, the top margin never applies after the break.
    
    In compat14, if the paragraph properties are not applied
    to a run before the break, then they can be applied after the break.
    
    make CppunitTest_sw_ooxmlexport18 \
    CPPUNIT_TEST_NAME=testTdf153964_topMarginAfterBreak14
    
    make CppunitTest_sw_ooxmlexport18 \
    CPPUNIT_TEST_NAME=testTdf153964_topMarginAfterBreak15
    
    Change-Id: I8816b391e898cfea58c2e0dbf01c881f87bbc4c4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148451
    Tested-by: Justin Luth <jl...@mail.com>
    Reviewed-by: Justin Luth <jl...@mail.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148476

diff --git a/sw/qa/extras/ooxmlexport/data/tdf153964_topMarginAfterBreak14.docx 
b/sw/qa/extras/ooxmlexport/data/tdf153964_topMarginAfterBreak14.docx
new file mode 100644
index 000000000000..6c57bcfdc8ef
Binary files /dev/null and 
b/sw/qa/extras/ooxmlexport/data/tdf153964_topMarginAfterBreak14.docx differ
diff --git a/sw/qa/extras/ooxmlexport/data/tdf153964_topMarginAfterBreak15.docx 
b/sw/qa/extras/ooxmlexport/data/tdf153964_topMarginAfterBreak15.docx
new file mode 100644
index 000000000000..e4d5c45ed034
Binary files /dev/null and 
b/sw/qa/extras/ooxmlexport/data/tdf153964_topMarginAfterBreak15.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
index 02d229f08f11..fc3ca2f99bf8 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
@@ -87,12 +87,6 @@ DECLARE_OOXMLEXPORT_TEST(testTdf153526_commentInNumbering, 
"tdf153526_commentInN
     CPPUNIT_ASSERT_EQUAL(13, getParagraphs());
 }
 
-DECLARE_OOXMLEXPORT_TEST(testTdf153613_sdtAfterPgBreak, 
"tdf153613_sdtAfterPgBreak.docx")
-{
-    // the page break was missing. Before the fix this was 1 page
-    CPPUNIT_ASSERT_EQUAL(2, getPages());
-}
-
 CPPUNIT_TEST_FIXTURE(Test, testTdf153592_columnBreaks)
 {
     loadAndSave("tdf153592_columnBreaks.docx");
@@ -159,6 +153,101 @@ 
DECLARE_OOXMLEXPORT_TEST(testTdf153613_textboxAfterPgBreak2, "tdf153613_textboxA
     assertXPathContent(pLayout, "//page[2]/body/txt", "There should be no 
prior carriage return.");
 }
 
+DECLARE_OOXMLEXPORT_TEST(testTdf153613_sdtAfterPgBreak, 
"tdf153613_sdtAfterPgBreak.docx")
+{
+    CPPUNIT_ASSERT_EQUAL(2, getPages());
+}
+
+DECLARE_OOXMLEXPORT_TEST(testTdf153964_topMarginAfterBreak14, 
"tdf153964_topMarginAfterBreak14.docx")
+{
+    //The top margin should only apply once in a split paragraph.
+    //In this compat14 (Windows 2010) version, it applies after the break if 
no prior visible run.
+    uno::Reference<beans::XPropertySet> xPara(getParagraph(2, "a w:br at the 
start of the document. Does it use 60 point top margin?"), uno::UNO_QUERY);
+    //CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+
+    xPara.set(getParagraph(3, u"60 pt spacing before"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+    CPPUNIT_ASSERT_EQUAL(Color(0xfbe4d5), getProperty<Color>(xPara, 
"ParaBackColor"));
+
+    // The top margin was applied to paragraph 3, so it shouldn't apply here
+    xPara.set(getParagraph(4, u"column break1"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+    //CPPUNIT_ASSERT_EQUAL(Color(0xfbe4d5), getProperty<Color>(xPara, 
"ParaBackColor"));
+
+    xPara.set(getParagraph(5, u"60 pt followed by page break"), 
uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+    CPPUNIT_ASSERT_EQUAL(Color(0xdeeaf6), getProperty<Color>(xPara, 
"ParaBackColor"));
+
+    // The top margin was applied to paragraph 5, so it shouldn't apply here
+    xPara.set(getParagraph(6, u"page break1"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+    CPPUNIT_ASSERT_EQUAL(Color(0xdeeaf6), getProperty<Color>(xPara, 
"ParaBackColor"));
+
+    // The top margin was not applied yet, so with compat14 it should apply 
here.
+    xPara.set(getParagraph(7, u"column break2"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+    CPPUNIT_ASSERT_EQUAL(Color(0xe2efd9), getProperty<Color>(xPara, 
"ParaBackColor"));
+
+    // In an odd twist, the w:br was actually at the end of the previous w:p, 
so in that case
+    // we ignore the top margin definition this time.
+    xPara.set(getParagraph(9, u"page break2"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+
+    // The top margin was not applied before the column break, so with 
compat14 it should apply here
+    xPara.set(getParagraph(10, u""), uno::UNO_QUERY); // after column break
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+    //CPPUNIT_ASSERT_EQUAL(Color(0xfff2cc), getProperty<Color>(xPara, 
"ParaBackColor"));
+
+    // In an odd twist, the w:br was actually at the end of the previous w:p, 
so in that case
+    // we ignore the top margin definition this time.
+    xPara.set(getParagraph(12, u""), uno::UNO_QUERY); // after page break
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+}
+
+DECLARE_OOXMLEXPORT_TEST(testTdf153964_topMarginAfterBreak15, 
"tdf153964_topMarginAfterBreak15.docx")
+{
+    //The top margin should only apply once (at most) in a split paragraph.
+    //In this compat15 (Windows 2013) version, it never applies after the 
break.
+    uno::Reference<beans::XPropertySet> xPara(getParagraph(2, "a w:br at the 
start of the document. Does it use 60 point top margin?"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+
+    xPara.set(getParagraph(3, u"60 pt spacing before"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+    CPPUNIT_ASSERT_EQUAL(Color(0xfbe4d5), getProperty<Color>(xPara, 
"ParaBackColor"));
+
+    // The top margin was applied to paragraph 3, so it shouldn't apply here
+    xPara.set(getParagraph(4, u"column break1"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+    //CPPUNIT_ASSERT_EQUAL(Color(0xfbe4d5), getProperty<Color>(xPara, 
"ParaBackColor"));
+
+    xPara.set(getParagraph(5, u"60 pt followed by page break"), 
uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(2117), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+    CPPUNIT_ASSERT_EQUAL(Color(0xdeeaf6), getProperty<Color>(xPara, 
"ParaBackColor"));
+
+    // The top margin was applied to paragraph 5, so it shouldn't apply here
+    xPara.set(getParagraph(6, u"page break1"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+    CPPUNIT_ASSERT_EQUAL(Color(0xdeeaf6), getProperty<Color>(xPara, 
"ParaBackColor"));
+
+    // The top margin was not applied to paragraph 6, and with compat15 it 
shouldn't apply here.
+    xPara.set(getParagraph(7, u"column break2"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+    CPPUNIT_ASSERT_EQUAL(Color(0xe2efd9), getProperty<Color>(xPara, 
"ParaBackColor"));
+
+    // The top margin not was applied to paragraph 8, and with compat15 it 
shouldn't apply here.
+    xPara.set(getParagraph(9, u"page break2"), uno::UNO_QUERY);
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+
+    // The top margin was not applied to paragraph 9, and with compat15 it 
shouldn't apply here.
+    xPara.set(getParagraph(10, u""), uno::UNO_QUERY); // after column break
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+    //CPPUNIT_ASSERT_EQUAL(Color(0xfff2cc), getProperty<Color>(xPara, 
"ParaBackColor"));
+
+    // The top margin was not applied to paragraph 11, and with compat15 it 
shouldn't apply here.
+    xPara.set(getParagraph(12, u""), uno::UNO_QUERY); // after page break
+    CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(xPara, 
"ParaTopMargin"));
+}
+
 CPPUNIT_TEST_FIXTURE(Test, testTdf149551_mongolianVert)
 {
     // Given a docx document with a shape with vert="mongolianVert".
diff --git a/writerfilter/source/dmapper/DomainMapper.cxx 
b/writerfilter/source/dmapper/DomainMapper.cxx
index 799d1f5d2a68..2ef39d8117d0 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -1106,6 +1106,14 @@ void DomainMapper::lcl_attribute(Id nName, Value & val)
                             finishParagraph();
                             lcl_startParagraphGroup();
                         }
+                        else // IsFirstRun
+                        {
+                            if (GetSettingsTable()->GetWordCompatibilityMode() 
> 14)
+                            {
+                                pContext->Insert(PROP_PARA_TOP_MARGIN, 
uno::Any(sal_uInt32(0)));
+                            }
+                        }
+
                         pContext->Insert(PROP_BREAK_TYPE, 
uno::Any(style::BreakType_PAGE_BEFORE));
                         m_pImpl->clearDeferredBreaks();
                     }
@@ -1118,6 +1126,14 @@ void DomainMapper::lcl_attribute(Id nName, Value & val)
                         finishParagraph();
                         lcl_startParagraphGroup();
                     }
+                    else // IsFirstRun
+                    {
+                        if (GetSettingsTable()->GetWordCompatibilityMode() > 
14)
+                        {
+                            pContext->Insert(PROP_PARA_TOP_MARGIN, 
uno::Any(sal_uInt32(0)));
+                        }
+                    }
+
                     pContext->Insert(PROP_BREAK_TYPE, 
uno::Any(style::BreakType_COLUMN_BEFORE));
                     m_pImpl->clearDeferredBreaks();
                 }
@@ -3541,13 +3557,29 @@ void DomainMapper::lcl_startParagraphGroup()
         if (!m_pImpl->IsInShape() && !m_pImpl->IsInComments())
         {
             const OUString& sDefaultParaStyle = 
m_pImpl->GetDefaultParaStyleName();
+            auto pContext = 
static_cast<ParagraphPropertyMap*>(m_pImpl->GetTopContext().get());
             m_pImpl->GetTopContext()->Insert( PROP_PARA_STYLE_NAME, uno::Any( 
sDefaultParaStyle ) );
             m_pImpl->SetCurrentParaStyleName( sDefaultParaStyle );
 
             if (m_pImpl->isBreakDeferred(PAGE_BREAK))
+            {
                 m_pImpl->GetTopContext()->Insert(PROP_BREAK_TYPE, 
uno::Any(style::BreakType_PAGE_BEFORE));
+
+                // With a w:br at the start of a paragraph, compat14 
apparently doesn't apply.
+                // It works to insert a zero margin before importing paragraph 
properties because
+                // TopMargin typically imports without overwrite any existing 
value. Very handy.
+                pContext->Insert(PROP_PARA_TOP_MARGIN, 
uno::Any(sal_uInt32(0)));
+            }
             else if (m_pImpl->isBreakDeferred(COLUMN_BREAK))
+            {
                 m_pImpl->GetTopContext()->Insert(PROP_BREAK_TYPE, 
uno::Any(style::BreakType_COLUMN_BEFORE));
+
+                if (GetSettingsTable()->GetWordCompatibilityMode() > 14)
+                {
+                    pContext->Insert(PROP_PARA_TOP_MARGIN, 
uno::Any(sal_uInt32(0)));
+                }
+            }
+
             mbWasShapeInPara = false;
         }
 
@@ -4115,7 +4147,13 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, 
size_t len)
                             finishParagraph();
                             lcl_startParagraphGroup();
                         }
-
+                        else // IsFirstRun
+                        {
+                            if (GetSettingsTable()->GetWordCompatibilityMode() 
> 14)
+                            {
+                                pContext->Insert(PROP_PARA_TOP_MARGIN, 
uno::Any(sal_uInt32(0)));
+                            }
+                        }
 
                         pContext->Insert(PROP_BREAK_TYPE, 
uno::Any(style::BreakType_PAGE_BEFORE));
                         m_pImpl->clearDeferredBreaks();
@@ -4129,6 +4167,13 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, 
size_t len)
                         finishParagraph();
                         lcl_startParagraphGroup();
                     }
+                    else // IsFirstRun
+                    {
+                        if (GetSettingsTable()->GetWordCompatibilityMode() > 
14)
+                        {
+                            pContext->Insert(PROP_PARA_TOP_MARGIN, 
uno::Any(sal_uInt32(0)));
+                        }
+                    }
 
                     pContext->Insert(PROP_BREAK_TYPE, 
uno::Any(style::BreakType_COLUMN_BEFORE));
                     m_pImpl->clearDeferredBreaks();
@@ -4191,6 +4236,7 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, 
size_t len)
             PropertyMapPtr pContext = m_pImpl->GetTopContext();
             if (!m_pImpl->GetFootnoteContext() && !m_pImpl->IsInShape() && 
!m_pImpl->IsInComments())
             {
+                auto pPara = 
static_cast<ParagraphPropertyMap*>(m_pImpl->GetTopContextOfType(CONTEXT_PARAGRAPH).get());
                 if (m_pImpl->isBreakDeferred(PAGE_BREAK))
                 {
                     /* If PAGEBREAK appears in first paragraph of the section 
or
@@ -4203,6 +4249,13 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, 
size_t len)
                         finishParagraph();
                         lcl_startParagraphGroup();
                     }
+                    else // IsFirstRun
+                    {
+                        if (GetSettingsTable()->GetWordCompatibilityMode() > 
14)
+                        {
+                            pPara->Insert(PROP_PARA_TOP_MARGIN, 
uno::Any(sal_uInt32(0)), true);
+                        }
+                    }
                     m_pImpl->GetTopContext()->Insert(PROP_BREAK_TYPE, 
uno::Any(style::BreakType_PAGE_BEFORE));
                 }
                 else if (m_pImpl->isBreakDeferred(COLUMN_BREAK))
@@ -4214,6 +4267,13 @@ void DomainMapper::lcl_utext(const sal_uInt8 * data_, 
size_t len)
                         finishParagraph();
                         lcl_startParagraphGroup();
                     }
+                    else // IsFirstRun
+                    {
+                        if (GetSettingsTable()->GetWordCompatibilityMode() > 
14)
+                        {
+                            pPara->Insert(PROP_PARA_TOP_MARGIN, 
uno::Any(sal_uInt32(0)));
+                        }
+                    }
                     m_pImpl->GetTopContext()->Insert(PROP_BREAK_TYPE, 
uno::Any(style::BreakType_COLUMN_BEFORE));
                 }
                 m_pImpl->clearDeferredBreaks();

Reply via email to