tdf#92521 DOCX export: handle section break right after a table

DocxAttributeOutput::SectionBreaks() previously only handled the
text-text and text-table node transitions; implement support for
table-text to avoid loosing a page break on export for the bugdoc.

(View this commit with whitespace ignored to filter out the noise about
SectionBreaks() now accepting non-text nodes, too.)

Change-Id: Ie8a1575374a207399351635bda8c0c076ce7268d
diff --git a/sw/qa/extras/ooxmlexport/data/tdf92521.odt b/sw/qa/extras/ooxmlexport/data/tdf92521.odt
new file mode 100644
index 0000000..8148e49
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf92521.odt
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
index 37c19c2..1359241 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport4.cxx
@@ -812,6 +812,13 @@ DECLARE_OOXMLEXPORT_TEST(testTdf83227, "tdf83227.docx")
    CPPUNIT_ASSERT_EQUAL(false, bool(xNameAccess->hasByName("word/media/image2.png")));
}

DECLARE_OOXMLEXPORT_TEST(testTdf92521, "tdf92521.odt")
{
    if (xmlDocPtr pXmlDoc = parseExport("word/document.xml"))
        // There should be a section break that's in the middle of the document: right after the table.
        assertXPath(pXmlDoc, "/w:document/w:body/w:p/w:pPr/w:sectPr", 1);
}

CPPUNIT_PLUGIN_IMPLEMENT();

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx b/sw/source/filter/ww8/attributeoutputbase.hxx
index 457839c..a180564 100644
--- a/sw/source/filter/ww8/attributeoutputbase.hxx
+++ b/sw/source/filter/ww8/attributeoutputbase.hxx
@@ -156,7 +156,7 @@ public:
    virtual void EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) = 0;

    /// Called in order to output section breaks.
    virtual void SectionBreaks(const SwTextNode& rNode) = 0;
    virtual void SectionBreaks(const SwNode& rNode) = 0;

    /// Called before we start outputting the attributes.
    virtual void StartParagraphProperties() = 0;
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 58ae8c6..f28b729 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -794,7 +794,7 @@ void DocxAttributeOutput::EmptyParagraph()
    m_pSerializer->singleElementNS( XML_w, XML_p, FSEND );
}

void DocxAttributeOutput::SectionBreaks(const SwTextNode& rNode)
void DocxAttributeOutput::SectionBreaks(const SwNode& rNode)
{
    // output page/section breaks
    // Writer can have them at the beginning of a paragraph, or at the end, but
@@ -802,16 +802,31 @@ void DocxAttributeOutput::SectionBreaks(const SwTextNode& rNode)
    // paragraph in a section.  To get it right, we have to switch to the next
    // paragraph, and detect the section breaks there.
    SwNodeIndex aNextIndex( rNode, 1 );
    if ( aNextIndex.GetNode().IsTextNode() )

    if (rNode.IsTextNode())
    {
        const SwTextNode* pTextNode = static_cast< SwTextNode* >( &aNextIndex.GetNode() );
        m_rExport.OutputSectionBreaks( pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen, pTextNode->GetText().isEmpty() );
        if (aNextIndex.GetNode().IsTextNode())
        {
            const SwTextNode* pTextNode = static_cast<SwTextNode*>(&aNextIndex.GetNode());
            m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen, pTextNode->GetText().isEmpty());
        }
        else if (aNextIndex.GetNode().IsTableNode())
        {
            const SwTableNode* pTableNode = static_cast<SwTableNode*>(&aNextIndex.GetNode());
            const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat();
            m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
        }
    }
    else if ( aNextIndex.GetNode().IsTableNode() )
    else if (rNode.IsEndNode())
    {
        const SwTableNode* pTableNode = static_cast< SwTableNode* >( &aNextIndex.GetNode() );
        const SwFrameFormat *pFormat = pTableNode->GetTable().GetFrameFormat();
        m_rExport.OutputSectionBreaks( &(pFormat->GetAttrSet()), *pTableNode );
        // End of something: make sure that it's the end of a table.
        assert(rNode.StartOfSectionNode()->IsTableNode());
        if (aNextIndex.GetNode().IsTextNode())
        {
            // Handle section break between a table and a text node following it.
            const SwTextNode* pTextNode = aNextIndex.GetNode().GetTextNode();
            m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode, m_tableReference->m_bTableCellOpen, pTextNode->GetText().isEmpty());
        }
    }
}

diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index cc68dcb..0bc904d 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -154,7 +154,7 @@ public:
    virtual void EmptyParagraph() SAL_OVERRIDE;

    /// Called in order to output section breaks.
    virtual void SectionBreaks(const SwTextNode& rNode) SAL_OVERRIDE;
    virtual void SectionBreaks(const SwNode& rNode) SAL_OVERRIDE;

    /// Called before we start outputting the attributes.
    virtual void StartParagraphProperties() SAL_OVERRIDE;
diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx
index 8fe0344..e7fa769 100644
--- a/sw/source/filter/ww8/docxexport.cxx
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -499,6 +499,9 @@ void DocxExport::OutputEndNode( const SwEndNode& rEndNode )
            m_pSections->AppendSection( m_pAktPageDesc, pParentFormat, nRstLnNum );
        }
    }
    else if (TXT_MAINTEXT == m_nTextTyp && rEndNode.StartOfSectionNode()->IsTableNode())
        // End node of a table: see if a section break should be written after the table.
        AttrOutput().SectionBreaks(rEndNode);
}

void DocxExport::OutputGrfNode( const SwGrfNode& )
diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx b/sw/source/filter/ww8/rtfattributeoutput.cxx
index c1ac77ee..83219e9 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.cxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.cxx
@@ -314,33 +314,36 @@ void RtfAttributeOutput::EmptyParagraph()
    m_rExport.Strm().WriteCharPtr(SAL_NEWLINE_STRING).WriteCharPtr(OOO_STRING_SVTOOLS_RTF_PAR).WriteChar(' ');
}

void RtfAttributeOutput::SectionBreaks(const SwTextNode& rNode)
void RtfAttributeOutput::SectionBreaks(const SwNode& rNode)
{
    OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");

    // output page/section breaks
    SwNodeIndex aNextIndex(rNode, 1);
    m_rExport.Strm().WriteCharPtr(m_aSectionBreaks.makeStringAndClear().getStr());
    m_bBufferSectionBreaks = true;

    // output section headers / footers
    if (!m_bBufferSectionHeaders)
        m_rExport.Strm().WriteCharPtr(m_aSectionHeaders.makeStringAndClear().getStr());

    if (aNextIndex.GetNode().IsTextNode())
    if (rNode.IsTextNode())
    {
        const SwTextNode* pTextNode = static_cast< SwTextNode* >(&aNextIndex.GetNode());
        m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
        // Save the current page description for now, so later we will be able to access the previous one.
        m_pPrevPageDesc = pTextNode->FindPageDesc(false);
        OSL_ENSURE(m_aStyles.getLength() == 0, "m_aStyles is not empty");

        // output page/section breaks
        SwNodeIndex aNextIndex(rNode, 1);
        m_rExport.Strm().WriteCharPtr(m_aSectionBreaks.makeStringAndClear().getStr());
        m_bBufferSectionBreaks = true;

        // output section headers / footers
        if (!m_bBufferSectionHeaders)
            m_rExport.Strm().WriteCharPtr(m_aSectionHeaders.makeStringAndClear().getStr());

        if (aNextIndex.GetNode().IsTextNode())
        {
            const SwTextNode* pTextNode = static_cast< SwTextNode* >(&aNextIndex.GetNode());
            m_rExport.OutputSectionBreaks(pTextNode->GetpSwAttrSet(), *pTextNode);
            // Save the current page description for now, so later we will be able to access the previous one.
            m_pPrevPageDesc = pTextNode->FindPageDesc(false);
        }
        else if (aNextIndex.GetNode().IsTableNode())
        {
            const SwTableNode* pTableNode = static_cast< SwTableNode* >(&aNextIndex.GetNode());
            const SwFrameFormat* pFormat = pTableNode->GetTable().GetFrameFormat();
            m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
        }
        m_bBufferSectionBreaks = false;
    }
    else if (aNextIndex.GetNode().IsTableNode())
    {
        const SwTableNode* pTableNode = static_cast< SwTableNode* >(&aNextIndex.GetNode());
        const SwFrameFormat* pFormat = pTableNode->GetTable().GetFrameFormat();
        m_rExport.OutputSectionBreaks(&(pFormat->GetAttrSet()), *pTableNode);
    }
    m_bBufferSectionBreaks = false;
}

void RtfAttributeOutput::StartParagraphProperties()
diff --git a/sw/source/filter/ww8/rtfattributeoutput.hxx b/sw/source/filter/ww8/rtfattributeoutput.hxx
index 9407fef..abc8e2d 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.hxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.hxx
@@ -53,7 +53,7 @@ public:
    virtual void EmptyParagraph() SAL_OVERRIDE;

    /// Called in order to output section breaks.
    virtual void SectionBreaks(const SwTextNode& rNode) SAL_OVERRIDE;
    virtual void SectionBreaks(const SwNode& rNode) SAL_OVERRIDE;

    /// Called before we start outputting the attributes.
    virtual void StartParagraphProperties() SAL_OVERRIDE;
diff --git a/sw/source/filter/ww8/ww8attributeoutput.hxx b/sw/source/filter/ww8/ww8attributeoutput.hxx
index 0b8765e..25e3308 100644
--- a/sw/source/filter/ww8/ww8attributeoutput.hxx
+++ b/sw/source/filter/ww8/ww8attributeoutput.hxx
@@ -36,7 +36,7 @@ public:
    virtual void EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) SAL_OVERRIDE;

    /// Called in order to output section breaks.
    virtual void SectionBreaks(const SwTextNode& /*rNode*/) SAL_OVERRIDE {}
    virtual void SectionBreaks(const SwNode& /*rNode*/) SAL_OVERRIDE {}

    /// Called before we start outputting the attributes.
    virtual void StartParagraphProperties() SAL_OVERRIDE {}