tdf#81705 doc export: write sprmPOutLvl

but only if it is not the default value,
and it isn't the same as what it inherits.

Also tweaked DOCX, so that it can write out
the default in order to cancel a style's outline level.

RTF does nothing with ParaOutlineLevel.

I am slightly confused by ::OutlineNumbering
which also writes out Outline Level.
This appears to be a duplication
(and it also seems to duplicate ParaNumRule)
which DOCX avoids by just doing nothing.

Change-Id: I32dd1bf9f35dc5f7f500e65d517fad0304bf452d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/113426
Tested-by: Jenkins
Reviewed-by: Justin Luth <justin_luth@sil.org>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
diff --git a/sw/qa/extras/ww8export/data/tdf81705_outlineLevel.doc b/sw/qa/extras/ww8export/data/tdf81705_outlineLevel.doc
new file mode 100755
index 0000000..55d1883
--- /dev/null
+++ b/sw/qa/extras/ww8export/data/tdf81705_outlineLevel.doc
Binary files differ
diff --git a/sw/qa/extras/ww8export/ww8export3.cxx b/sw/qa/extras/ww8export/ww8export3.cxx
index 6364762..508636e 100644
--- a/sw/qa/extras/ww8export/ww8export3.cxx
+++ b/sw/qa/extras/ww8export/ww8export3.cxx
@@ -579,6 +579,14 @@ DECLARE_WW8EXPORT_TEST(testTdf129522_removeShadowStyle, "tdf129522_removeShadowS
    CPPUNIT_ASSERT_EQUAL(table::ShadowLocation_NONE, aShadow.Location);
}

DECLARE_WW8EXPORT_TEST(testTdf81705_outlineLevel, "tdf81705_outlineLevel.doc")
{
    // direct formatting resets outline level to body text (0)
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Paragraph C", sal_uInt16(0), getProperty<sal_uInt16>(getParagraph(3), "OutlineLevel"));
    // myStyle sets outline level to 1.
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Paragraph D", sal_uInt16(1), getProperty<sal_uInt16>(getParagraph(4), "OutlineLevel"));
}

DECLARE_WW8EXPORT_TEST(testBtlrCell, "btlr-cell.doc")
{
    // Without the accompanying fix in place, this test would have failed, as
diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx b/sw/source/filter/ww8/attributeoutputbase.hxx
index eceeb4f..df84fa3 100644
--- a/sw/source/filter/ww8/attributeoutputbase.hxx
+++ b/sw/source/filter/ww8/attributeoutputbase.hxx
@@ -620,6 +620,7 @@ protected:
    virtual void CharGrabBag( const SfxGrabBagItem& ) = 0;

    /// Sfx item RES_PARATR_OUTLINELEVEL
    void ParaOutlineLevelBase( const SfxUInt16Item& rItem );
    virtual void ParaOutlineLevel( const SfxUInt16Item& ) = 0;

    /// Write the expanded field
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 4149e3e..1416dd4 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -6515,20 +6515,6 @@ void DocxAttributeOutput::EndStyleProperties( bool bParProp )
    }
}

namespace
{

void lcl_OutlineLevel(sax_fastparser::FSHelperPtr const & pSerializer, sal_uInt16 nLevel)
{
    if (nLevel >= WW8ListManager::nMaxLevel)
        nLevel = WW8ListManager::nMaxLevel - 1;

    pSerializer->singleElementNS(XML_w, XML_outlineLvl,
            FSNS(XML_w, XML_val), OString::number(nLevel));
}

}

void DocxAttributeOutput::OutlineNumbering(sal_uInt8 const /*nLvl*/)
{
    // Handled by ParaOutlineLevel() instead.
@@ -6536,8 +6522,10 @@ void DocxAttributeOutput::OutlineNumbering(sal_uInt8 const /*nLvl*/)

void DocxAttributeOutput::ParaOutlineLevel(const SfxUInt16Item& rItem)
{
    if (rItem.GetValue() > 0)
        lcl_OutlineLevel(m_pSerializer, rItem.GetValue() - 1);
    sal_uInt16 nOutLvl = std::min(rItem.GetValue(), sal_uInt16(WW8ListManager::nMaxLevel));
    // Outline Level: in LO Body Text = 0, in MS Body Text = 9
    nOutLvl = nOutLvl ? nOutLvl - 1 : 9;
    m_pSerializer->singleElementNS(XML_w, XML_outlineLvl, FSNS(XML_w, XML_val), OString::number(nOutLvl));
}

void DocxAttributeOutput::PageBreakBefore( bool bBreak )
diff --git a/sw/source/filter/ww8/ww8atr.cxx b/sw/source/filter/ww8/ww8atr.cxx
index 4e0451e..de65515 100644
--- a/sw/source/filter/ww8/ww8atr.cxx
+++ b/sw/source/filter/ww8/ww8atr.cxx
@@ -758,9 +758,8 @@ void WW8AttributeOutput::OutlineNumbering(sal_uInt8 nLvl)
    if ( nLvl >= WW8ListManager::nMaxLevel )
        nLvl = WW8ListManager::nMaxLevel-1;

    // write sprmPOutLvl sprmPIlvl and sprmPIlfo
    SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::POutLvl::val );
    m_rWW8Export.pO->push_back( nLvl );
    // write sprmPIlvl and sprmPIlfo
    // (sprmPOutLvl now handled by ParaOutlineLevel)
    SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::PIlvl::val );
    m_rWW8Export.pO->push_back( nLvl );
    SwWW8Writer::InsUInt16( *m_rWW8Export.pO, NS_sprm::PIlfo::val );
@@ -3593,6 +3592,23 @@ void WW8AttributeOutput::CharTwoLines( const SvxTwoLinesItem& rTwoLines )
    m_rWW8Export.pO->insert( m_rWW8Export.pO->end(), aZeroArr, aZeroArr+3);
}

void AttributeOutputBase::ParaOutlineLevelBase( const SfxUInt16Item& rItem )
{
    sal_uInt16 nOutLvl = rItem.GetValue();

    // Do not write out default level (Body Text) if there is no inheritance, or if the level matches the inherited value
    const SfxUInt16Item* pInherited = nullptr;
    if (auto pNd = dynamic_cast<const SwContentNode*>(GetExport().m_pOutFormatNode)) //paragraph
        pInherited = static_cast<SwTextFormatColl&>(pNd->GetAnyFormatColl()).GetAttrSet().GetItem<SfxUInt16Item>(RES_PARATR_OUTLINELEVEL);
    else if (GetExport().m_bStyDef && GetExport().m_pCurrentStyle && GetExport().m_pCurrentStyle->DerivedFrom()) //style
        pInherited = GetExport().m_pCurrentStyle->DerivedFrom()->GetAttrSet().GetItem<SfxUInt16Item>(RES_PARATR_OUTLINELEVEL);
    if ((pInherited && pInherited->GetValue() == nOutLvl)
        || (!pInherited && !nOutLvl))
        return;

    ParaOutlineLevel(rItem);
}

void AttributeOutputBase::ParaNumRule( const SwNumRuleItem& rNumRule )
{
    const SwTextNode* pTextNd = nullptr;
@@ -4952,8 +4968,13 @@ void WW8AttributeOutput::CharGrabBag(const SfxGrabBagItem& /*rItem*/)
{
}

void WW8AttributeOutput::ParaOutlineLevel(const SfxUInt16Item& /*rItem*/)
void WW8AttributeOutput::ParaOutlineLevel(const SfxUInt16Item& rItem)
{
    sal_uInt16 nOutLvl = std::min(rItem.GetValue(), sal_uInt16(WW8ListManager::nMaxLevel));
    // Outline Level: in LO Body Text = 0, in MS Body Text = 9
    nOutLvl = nOutLvl ? nOutLvl - 1 : 9;
    m_rWW8Export.InsUInt16( NS_sprm::POutLvl::val );
    m_rWW8Export.pO->push_back( nOutLvl );
}

// "Separate paragraphs"
@@ -5510,7 +5531,7 @@ void AttributeOutputBase::OutputItem( const SfxPoolItem& rHt )
            ParaGrabBag(static_cast<const SfxGrabBagItem&>(rHt));
            break;
        case RES_PARATR_OUTLINELEVEL:
            ParaOutlineLevel(static_cast<const SfxUInt16Item&>(rHt));
            ParaOutlineLevelBase(static_cast<const SfxUInt16Item&>(rHt));
            break;
        case RES_CHRATR_GRABBAG:
            CharGrabBag(static_cast<const SfxGrabBagItem&>(rHt));