tdf#129631 writerfilter,sw: RTF import of invalid border...

... and export of border on paragraph that clears the border from the
paragraph style.

The import problem is that a lone \brdrcf17 without any preceding
keyword about which border it belongs to (as required by the EBNF in the
RTF spec) is apparently handled by Word as overriding/clearing all
paragraph borders that may be inherited from the paragraph style.

The export problem is that a null SvxBorderLine isn't exported at all,
but there needs to be a \brdrnone to override the definition from the
style.

There was also an API problem that the null SvxBorderLine is wrongly
converted to a table::BorderLine2 with LineStyle SOLID.

Change-Id: I5fa3857de2ef843f5194e12dd0c3e57bcf1ca52b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/138089
Tested-by: Jenkins
Reviewed-by: Michael Stahl <michael.stahl@allotropia.de>
(cherry picked from commit 967a03eb8760fb498c5ea6c32d03d1eea486bbe2)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/138120
Reviewed-by: Thorsten Behrens <thorsten.behrens@allotropia.de>
diff --git a/editeng/source/items/frmitems.cxx b/editeng/source/items/frmitems.cxx
index c4c79dc..35e1be7 100644
--- a/editeng/source/items/frmitems.cxx
+++ b/editeng/source/items/frmitems.cxx
@@ -1387,7 +1387,10 @@ table::BorderLine2 SvxBoxItem::SvxLineToLine(const SvxBorderLine* pLine, bool bC
        aLine.LineWidth      = sal_uInt32( bConvert ? convertTwipToMm100( pLine->GetWidth( ) ) : pLine->GetWidth( ) );
    }
    else
    {
        aLine.Color          = aLine.InnerLineWidth = aLine.OuterLineWidth = aLine.LineDistance  = 0;
        aLine.LineStyle = table::BorderLineStyle::NONE; // 0 is SOLID!
    }
    return aLine;
}

diff --git a/sw/qa/extras/odfexport/odfexport.cxx b/sw/qa/extras/odfexport/odfexport.cxx
index 3fb14b2..73b9bcf 100644
--- a/sw/qa/extras/odfexport/odfexport.cxx
+++ b/sw/qa/extras/odfexport/odfexport.cxx
@@ -1111,7 +1111,7 @@ DECLARE_ODFEXPORT_TEST(testCharacterBorder, "charborder.odt")
            table::BorderLine2(0xFF3333,0,37,0,14,37),     // Top (fine dashed line)
            table::BorderLine2(0x99FF66,26,26,53,11,106),  // Bottom
            table::BorderLine2(0x6666FF,9,26,9,12,71),     // Left
            table::BorderLine2(0,0,0,0,0,0)                // Right
            table::BorderLine2(0,0,0,0,table::BorderLineStyle::NONE,0) // Right
        };

        sal_Int32 aDistances[4] = { 400 /*Top*/, 300 /*Bottom*/, 250 /*Left*/, 0 /*Right*/ };
diff --git a/sw/qa/extras/rtfexport/data/tdf129631_lostBorders3.rtf b/sw/qa/extras/rtfexport/data/tdf129631_lostBorders3.rtf
new file mode 100644
index 0000000..dba7db7
--- /dev/null
+++ b/sw/qa/extras/rtfexport/data/tdf129631_lostBorders3.rtf
@@ -0,0 +1,27 @@
{\rtf1\ansi\deff4\adeflang1025
{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\froman\fprq2\fcharset0 Liberation Serif{\*\falt Times New Roman};}{\f4\froman\fprq0\fcharset0 Times New Roman;}{\f5\fnil\fprq2\fcharset0 DejaVu Sans;}}
{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red6\green154\blue46;}
{\stylesheet{\s0\snext0\dbch\af5\langfe2052\dbch\af4\afs24\alang1025\ql\widctlpar\ltrpar\hyphpar0\aspalpha\cf0\loch\f3\fs24\lang255\kerning1 Normal;}
{\*\cs15\snext15 CharShadow;}
{\*\cs16\sbasedon15\snext16 CharShadow-removed;}
{\s17\sbasedon0\snext18\dbch\af5\langfe2052\dbch\af4\afs28\alang1025\ql\widctlpar\sb240\sa120\keepn\ltrpar\cf0\loch\f4\fs28\lang255\kerning1 Heading;}
{\s18\sbasedon0\snext18\dbch\af5\langfe2052\dbch\af4\afs24\alang1025\sl276\slmult1\ql\widctlpar\sb0\sa140\ltrpar\cf0\loch\f3\fs24\lang255\kerning1 Text Body;}
{\s19\sbasedon18\snext19\dbch\af5\langfe2052\dbch\af4\afs24\alang1025\sl276\slmult1\ql\widctlpar\sb0\sa140\ltrpar\cf0\loch\f4\fs24\lang255\kerning1 List;}
{\s20\sbasedon0\snext20\dbch\af5\langfe2052\dbch\af4\afs24\alang1025\ai\ql\widctlpar\sb120\sa120\noline\ltrpar\cf0\loch\f4\fs24\lang255\i\kerning1 Caption;}
{\s21\sbasedon0\snext21\dbch\af5\langfe2052\dbch\af4\afs24\alang1025\ql\widctlpar\noline\ltrpar\cf0\loch\f4\fs24\lang255\kerning1 Index;}
{\s22\sbasedon0\snext22\dbch\af5\langfe2052\dbch\af4\afs24\alang1025\ql\widctlpar\brdrt\brdrs\brdrw50\brdrcf1\brsp0\brdrl\brdrs\brdrw50\brdrcf1\brsp0\brdrb\brdrs\brdrw50\brdrcf1\brsp20\brdrr\brdrs\brdrw50\brdrcf1\brsp20\ltrpar\cf0\loch\f3\fs48\lang255\i\b\kerning1 Border;}
{\s23\sbasedon0\snext23\dbch\af5\langfe2052\dbch\af4\afs24\alang1025\ql\widctlpar\sb0\sa60\noline\ltrpar\cf0\loch\f3\fs24\lang255\kerning1 Sender;}
}{\*\generator LibreOfficeDev/6.5.0.0.alpha0$Linux_X86_64 LibreOffice_project/c71d886120998884fdd16a862826f59883d9a114}{\info{\creatim\yr2019\mo12\dy21\hr9\min10}{\revtim\yr2019\mo12\dy26\hr11\min50}{\printim\yr0\mo0\dy0\hr0\min0}}{\*\userprops}\deftab709
\viewscale140
{\*\pgdsctbl
{\pgdsc0\pgdscuse451\lndscpsxn\pgwsxn8391\pghsxn5953\marglsxn1134\margrsxn1134\margtsxn992\margbsxn992\pgdscnxt0 Default Style;}}
\formshade{\*\pgdscno0}\landscape\paperh5953\paperw8391\margl1134\margr1134\margt992\margb992\sectd\sbknone\sectunlocked1\lndscpsxn\pgndec\pgwsxn8391\pghsxn5953\marglsxn1134\margrsxn1134\margtsxn992\margbsxn992\ftnbj\ftnstart1\ftnrstcont\ftnnar\aenddoc\aftnrstcont\aftnstart1\aftnnrlc\htmautsp
{\*\ftnsep\chftnsep}\pgndec\pard\plain \s0\dbch\af5\langfe2052\dbch\af4\afs24\alang1025\ql\widctlpar\ltrpar\hyphpar0\aspalpha\cf0\loch\f3\fs24\lang255\kerning1{\loch
Paragraph border are NOT inherited from styles if partially re-defined directly.}
\par \pard\plain \s22\langfe2052\dbch\af4\afs24\alang1025\ql\widctlpar\cf0\loch\f3\fs48\lang255\i\b\kerning1\brdrcf17{\loch
Here colored green by direct formating, but no borders defined.}
\par \pard\plain \s0\dbch\af5\langfe2052\dbch\af4\afs24\alang1025\ql\widctlpar\ltrpar\hyphpar0\aspalpha\cf0\loch\f3\fs24\lang255\kerning1\loch

\par \pard\plain \s22\dbch\af5\langfe2052\dbch\af4\afs24\alang1025\ql\widctlpar\brdrt\brdrs\brdrw50\brdrcf1\brsp0\brdrl\brdrs\brdrw50\brdrcf1\brsp0\brdrb\brdrs\brdrw50\brdrcf1\brsp20\brdrr\brdrs\brdrw50\brdrcf1\brsp20\ltrpar\cf0\loch\f3\fs48\lang255\i\b\kerning1{\loch
End of test}
\par }
diff --git a/sw/qa/extras/rtfexport/rtfexport4.cxx b/sw/qa/extras/rtfexport/rtfexport4.cxx
index 27335cf..dbcbeae 100644
--- a/sw/qa/extras/rtfexport/rtfexport4.cxx
+++ b/sw/qa/extras/rtfexport/rtfexport4.cxx
@@ -123,6 +123,57 @@ DECLARE_RTFEXPORT_TEST(test150269, "hidden-linebreaks.rtf")
    CPPUNIT_ASSERT_EQUAL(true, getProperty<bool>(xRun, "CharHidden"));
}

DECLARE_RTFEXPORT_TEST(test129758, "tdf129631_lostBorders3.rtf")
{
    uno::Reference<container::XNameAccess> xStyles(getStyles("ParagraphStyles"));
    uno::Reference<beans::XPropertySet> xStyle(xStyles->getByName("Border"), uno::UNO_QUERY);
    // style has borders
    table::BorderLine2 border;
    border = getProperty<table::BorderLine2>(xStyle, "RightBorder");
    CPPUNIT_ASSERT_EQUAL(table::BorderLineStyle::SOLID, border.LineStyle);
    CPPUNIT_ASSERT_EQUAL(sal_uInt32(88), border.LineWidth);
    border = getProperty<table::BorderLine2>(xStyle, "LeftBorder");
    CPPUNIT_ASSERT_EQUAL(table::BorderLineStyle::SOLID, border.LineStyle);
    CPPUNIT_ASSERT_EQUAL(sal_uInt32(88), border.LineWidth);
    border = getProperty<table::BorderLine2>(xStyle, "TopBorder");
    CPPUNIT_ASSERT_EQUAL(table::BorderLineStyle::SOLID, border.LineStyle);
    CPPUNIT_ASSERT_EQUAL(sal_uInt32(88), border.LineWidth);
    border = getProperty<table::BorderLine2>(xStyle, "BottomBorder");
    CPPUNIT_ASSERT_EQUAL(table::BorderLineStyle::SOLID, border.LineStyle);
    CPPUNIT_ASSERT_EQUAL(sal_uInt32(88), border.LineWidth);
    // style applied
    uno::Reference<beans::XPropertySet> xPara2(getParagraph(2), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(OUString("Border"), getProperty<OUString>(xPara2, "ParaStyleName"));
    // but no borders
    border = getProperty<table::BorderLine2>(xPara2, "RightBorder");
    CPPUNIT_ASSERT_EQUAL(table::BorderLineStyle::NONE, border.LineStyle);
    CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), border.LineWidth);
    border = getProperty<table::BorderLine2>(xPara2, "LeftBorder");
    CPPUNIT_ASSERT_EQUAL(table::BorderLineStyle::NONE, border.LineStyle);
    CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), border.LineWidth);
    border = getProperty<table::BorderLine2>(xPara2, "TopBorder");
    CPPUNIT_ASSERT_EQUAL(table::BorderLineStyle::NONE, border.LineStyle);
    CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), border.LineWidth);
    border = getProperty<table::BorderLine2>(xPara2, "BottomBorder");
    CPPUNIT_ASSERT_EQUAL(table::BorderLineStyle::NONE, border.LineStyle);
    CPPUNIT_ASSERT_EQUAL(sal_uInt32(0), border.LineWidth);
    // last paragraph: style applied, no override
    uno::Reference<beans::XPropertySet> xPara4(getParagraph(4), uno::UNO_QUERY);
    CPPUNIT_ASSERT_EQUAL(OUString("Border"), getProperty<OUString>(xPara4, "ParaStyleName"));
    border = getProperty<table::BorderLine2>(xPara4, "RightBorder");
    CPPUNIT_ASSERT_EQUAL(table::BorderLineStyle::SOLID, border.LineStyle);
    CPPUNIT_ASSERT_EQUAL(sal_uInt32(88), border.LineWidth);
    border = getProperty<table::BorderLine2>(xPara4, "LeftBorder");
    CPPUNIT_ASSERT_EQUAL(table::BorderLineStyle::SOLID, border.LineStyle);
    CPPUNIT_ASSERT_EQUAL(sal_uInt32(88), border.LineWidth);
    border = getProperty<table::BorderLine2>(xPara4, "TopBorder");
    CPPUNIT_ASSERT_EQUAL(table::BorderLineStyle::SOLID, border.LineStyle);
    CPPUNIT_ASSERT_EQUAL(sal_uInt32(88), border.LineWidth);
    border = getProperty<table::BorderLine2>(xPara4, "BottomBorder");
    CPPUNIT_ASSERT_EQUAL(table::BorderLineStyle::SOLID, border.LineStyle);
    CPPUNIT_ASSERT_EQUAL(sal_uInt32(88), border.LineWidth);
}

DECLARE_RTFEXPORT_TEST(testAnchoredAtSamePosition, "anchor.fodt")
{
    SwXTextDocument* const pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get());
diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx b/sw/source/filter/ww8/rtfattributeoutput.cxx
index 9d634e7..6fa088f 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.cxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.cxx
@@ -188,6 +188,11 @@ static OString OutTBLBorderLine(RtfExport const& rExport, const editeng::SvxBord
        aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRCF);
        aRet.append(static_cast<sal_Int32>(rExport.GetColor(pLine->GetColor())));
    }
    else // tdf#129758 "no border" may be needed to override style
    {
        aRet.append(pStr);
        aRet.append(OOO_STRING_SVTOOLS_RTF_BRDRNONE);
    }
    return aRet.makeStringAndClear();
}

@@ -197,8 +202,11 @@ static OString OutBorderLine(RtfExport const& rExport, const editeng::SvxBorderL
{
    OStringBuffer aRet;
    aRet.append(OutTBLBorderLine(rExport, pLine, pStr));
    aRet.append(OOO_STRING_SVTOOLS_RTF_BRSP);
    aRet.append(static_cast<sal_Int32>(nDist));
    if (pLine)
    {
        aRet.append(OOO_STRING_SVTOOLS_RTF_BRSP);
        aRet.append(static_cast<sal_Int32>(nDist));
    }
    if (eShadowLocation == SvxShadowLocation::BottomRight)
        aRet.append(LO_STRING_SVTOOLS_RTF_BRDRSH);
    return aRet.makeStringAndClear();
@@ -3644,11 +3652,9 @@ void RtfAttributeOutput::FormatBox(const SvxBoxItem& rBox)
        const char** pBrdNms = aBorderNames;
        for (int i = 0; i < 4; ++i, ++pBrd, ++pBrdNms)
        {
            if (const editeng::SvxBorderLine* pLn = rBox.GetLine(*pBrd))
            {
                m_aSectionBreaks.append(OutBorderLine(m_rExport, pLn, *pBrdNms,
                                                      rBox.GetDistance(*pBrd), eShadowLocation));
            }
            editeng::SvxBorderLine const* const pLn = rBox.GetLine(*pBrd);
            m_aSectionBreaks.append(
                OutBorderLine(m_rExport, pLn, *pBrdNms, rBox.GetDistance(*pBrd), eShadowLocation));
        }
    }

diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx
index cbe36e5..076bd5e 100644
--- a/writerfilter/source/rtftok/rtfdocumentimpl.cxx
+++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx
@@ -186,6 +186,21 @@ void putBorderProperty(RTFStack& aStates, Id nId, const RTFValue::Pointer_t& pVa
    else if (aStates.top().getBorderState() == RTFBorderState::PAGE)
        pAttributes = &getLastAttributes(aStates.top().getSectionSprms(),
                                         NS_ooxml::LN_EG_SectPrContents_pgBorders);
    else if (aStates.top().getBorderState() == RTFBorderState::NONE)
    {
        // this is invalid, but Word apparently clears or overrides all paragraph borders now
        for (int i = 0; i < 4; ++i)
        {
            auto const nBorder = getParagraphBorder(i);
            RTFSprms aAttributes;
            RTFSprms aSprms;
            aAttributes.set(NS_ooxml::LN_CT_Border_val,
                            new RTFValue(NS_ooxml::LN_Value_ST_Border_none));
            putNestedSprm(aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PrBase_pBdr, nBorder,
                          new RTFValue(aAttributes, aSprms), RTFOverwrite::YES);
        }
    }

    if (pAttributes)
        pAttributes->set(nId, pValue);
}