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 <jluth@mail.com>
Reviewed-by: Justin Luth <jluth@mail.com>
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 0000000..6c57bcf
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf153964_topMarginAfterBreak14.docx
Binary files 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 0000000..e4d5c45
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf153964_topMarginAfterBreak15.docx
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
index 67c1568..1e87210 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx
@@ -202,6 +202,96 @@ DECLARE_OOXMLEXPORT_TEST(testTdf153613_sdtAfterPgBreak, "tdf153613_sdtAfterPgBre
    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 d816aad..e712007 100644
--- a/writerfilter/source/dmapper/DomainMapper.cxx
+++ b/writerfilter/source/dmapper/DomainMapper.cxx
@@ -1107,6 +1107,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();
                    }
@@ -1119,6 +1127,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->GetTopContext())
        {
            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;
        }
        m_pImpl->clearDeferredBreaks();
@@ -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))
@@ -4213,6 +4266,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();