sw: DOCX: recognize TOC title during import

Change-Id: Ifa4fb59858d61580f76e3d104aa4caa6b5902d1b
Reviewed-on: https://gerrit.libreoffice.org/64735
Tested-by: Jenkins
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>

tdf#121561: sw: DOCX: add std/stdPr/stdContent around TOC

During export into DOCX from ODT we need to do it
because in this case the TOC title will be recognized
inside MS Word as part of the TOC.

Later we could add support of these keywords in LO import
in order to detect TOC title from DOCX input.

Added unit test for export.

Change-Id: I7135e91dc04d4c0501e6074a046fc473e041f014
Reviewed-on: https://gerrit.libreoffice.org/63786
Tested-by: Jenkins
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
Reviewed-on: https://gerrit.libreoffice.org/66307
Reviewed-by: Michael Stahl <Michael.Stahl@cib.de>
diff --git a/sw/inc/doc.hxx b/sw/inc/doc.hxx
index f92fbc7..1413def 100644
--- a/sw/inc/doc.hxx
+++ b/sw/inc/doc.hxx
@@ -911,6 +911,10 @@ public:
                                            const SwTOXBase& rTOX,
                                            const SfxItemSet* pSet = nullptr,
                                            bool bExpand = false );
    SwTOXBaseSection* InsertTableOf( const SwPaM& aPam,
                                            const SwTOXBase& rTOX,
                                            const SfxItemSet* pSet = nullptr,
                                            bool bExpand = false );
    void              InsertTableOf( sal_uLong nSttNd, sal_uLong nEndNd,
                                            const SwTOXBase& rTOX,
                                            const SfxItemSet* pSet );
diff --git a/sw/qa/extras/ooxmlexport/data/tdf121456_tabsOffset.odt b/sw/qa/extras/ooxmlexport/data/tdf121456_tabsOffset.odt
new file mode 100755
index 0000000..ff8d540
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf121456_tabsOffset.odt
Binary files differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
index c7dc952..41194b0 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
@@ -23,6 +23,7 @@
#include <com/sun/star/graphic/XGraphic.hpp>
#include <com/sun/star/text/RubyAdjust.hpp>
#include <com/sun/star/text/RubyPosition.hpp>
#include <com/sun/star/text/XDocumentIndex.hpp>


#include <sfx2/docfile.hxx>
@@ -79,6 +80,32 @@ DECLARE_OOXMLEXPORT_TEST(testTdf63561_clearTabs2, "tdf63561_clearTabs2.docx")
    CPPUNIT_ASSERT_EQUAL(sal_Int32(4), getProperty< uno::Sequence<style::TabStop> >(getParagraph(4), "ParaTabStops").getLength());
}

// tdf#121561: make sure w:sdt/w:sdtContent around TOC is written during ODT->DOCX conversion
DECLARE_OOXMLEXPORT_TEST(testTdf121561_tocTitle, "tdf121456_tabsOffset.odt")
{
    xmlDocPtr pXmlDoc = parseExport();
    if (!pXmlDoc)
        return;

    assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p/w:r/w:t", "Inhaltsverzeichnis");
    assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p/w:r/w:instrText", " TOC \\f \\o \"1-9\" \\h");
    assertXPath(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtPr/w:docPartObj/w:docPartGallery", "val", "Table of Contents");
    assertXPath(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtPr/w:docPartObj/w:docPartUnique", 1);
}

DECLARE_OOXMLEXPORT_TEST(testTdf121561_tocTitleDocx, "tdf121456_tabsOffset.odt")
{
    xmlDocPtr pXmlDoc = parseExport();
    if (!pXmlDoc)
        return;

    uno::Reference<text::XDocumentIndexesSupplier> xIndexSupplier(mxComponent, uno::UNO_QUERY);
    uno::Reference<container::XIndexAccess> xIndexes(xIndexSupplier->getDocumentIndexes( ), uno::UNO_QUERY);
    uno::Reference<text::XDocumentIndex> xTOCIndex(xIndexes->getByIndex(0), uno::UNO_QUERY);

    CPPUNIT_ASSERT_EQUAL(OUString("Inhaltsverzeichnis"), getProperty<OUString>(xTOCIndex, "Title"));
}

DECLARE_OOXMLEXPORT_TEST(testTdf82065_Ind_start_strict, "tdf82065_Ind_start_strict.docx")
{
    uno::Reference<beans::XPropertySet> xPropertySet(getStyles("NumberingStyles")->getByName("WWNum1"), uno::UNO_QUERY);
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx
index 0f8e562..54bb469 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport5.cxx
@@ -722,7 +722,7 @@ DECLARE_OOXMLEXPORT_TEST(testFdo77129, "fdo77129.docx")
       return;

    // Data was lost from this paragraph.
    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[5]/w:r[1]/w:t", "Abstract");
    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[4]/w:r[1]/w:t", "Abstract");
}

DECLARE_OOXMLEXPORT_TEST(testfdo79969_xlsm, "fdo79969_xlsm.docx")
diff --git a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
index fb41c03..789a6bc 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
@@ -144,7 +144,7 @@ DECLARE_OOXMLEXPORT_TEST(testFieldFlagO,"TOC_field_f.docx")

    // FIXME "p[2]" will have to be "p[1]", once the TOC import code is fixed
    // not to insert an empty paragraph before TOC.
    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[2]/w:r[2]/w:instrText", " TOC \\z \\f \\o \"1-3\" \\u \\h");
    assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p[1]/w:r[2]/w:instrText", " TOC \\z \\f \\o \"1-3\" \\u \\h");
}

DECLARE_OOXMLEXPORT_TEST(testTOCFlag_f, "toc_doc.docx")
@@ -161,7 +161,7 @@ DECLARE_OOXMLEXPORT_TEST(testTOCFlag_f, "toc_doc.docx")

    // FIXME "p[2]" will have to be "p[1]", once the TOC import code is fixed
    // not to insert an empty paragraph before TOC.
    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[2]/w:r[2]/w:instrText", " TOC \\z \\o \"1-3\" \\u \\h");
    assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p[1]/w:r[2]/w:instrText", " TOC \\z \\o \"1-3\" \\u \\h");
}

DECLARE_OOXMLEXPORT_TEST(testPreserveZfield,"preserve_Z_field_TOC.docx")
@@ -191,7 +191,7 @@ DECLARE_OOXMLEXPORT_TEST(testFieldFlagB,"TOC_field_b.docx")

    // FIXME "p[2]" will have to be "p[1]", once the TOC import code is fixed
    // not to insert an empty paragraph before TOC.
    assertXPathContent(pXmlDoc, "/w:document/w:body/w:p[2]/w:r[2]/w:instrText", " TOC \\b \"bookmark111\" \\o \"1-9\" \\h");
    assertXPathContent(pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p[1]/w:r[2]/w:instrText", " TOC \\b \"bookmark111\" \\o \"1-9\" \\h");
}

DECLARE_OOXMLEXPORT_TEST(testPreserveXfieldTOC, "PreserveXfieldTOC.docx")
@@ -449,7 +449,7 @@ DECLARE_OOXMLEXPORT_TEST(testFDO78654 , "fdo78654.docx")
        return;
    // In case of two "Hyperlink" tags in one paragraph and one of them
    // contains "PAGEREF" field then field end tag was missing from hyperlink.
    assertXPath ( pXmlDoc, "/w:document/w:body/w:sdt/w:sdtContent/w:p[2]/w:hyperlink[3]/w:r[5]/w:fldChar", "fldCharType", "end" );
    assertXPath ( pXmlDoc, "/w:document/w:body/w:p[2]/w:hyperlink[3]/w:r[5]/w:fldChar", "fldCharType", "end" );
}


diff --git a/sw/source/core/doc/doctxm.cxx b/sw/source/core/doc/doctxm.cxx
index 84d57cf..1305dfe 100644
--- a/sw/source/core/doc/doctxm.cxx
+++ b/sw/source/core/doc/doctxm.cxx
@@ -333,14 +333,22 @@ const SwTOXMark& SwDoc::GotoTOXMark( const SwTOXMark& rCurTOXMark,
}

SwTOXBaseSection* SwDoc::InsertTableOf( const SwPosition& rPos,
                                                const SwTOXBase& rTOX,
                                                const SfxItemSet* pSet,
                                                bool bExpand )
                                        const SwTOXBase& rTOX,
                                        const SfxItemSet* pSet,
                                        bool bExpand)
{
    SwPaM aPam( rPos );
    return InsertTableOf( aPam, rTOX, pSet, bExpand );
}

SwTOXBaseSection* SwDoc::InsertTableOf( const SwPaM& aPam,
                                        const SwTOXBase& rTOX,
                                        const SfxItemSet* pSet,
                                        bool bExpand )
{
    GetIDocumentUndoRedo().StartUndo( SwUndoId::INSTOX, nullptr );

    OUString sSectNm = GetUniqueTOXBaseName( *rTOX.GetTOXType(), rTOX.GetTOXName() );
    SwPaM aPam( rPos );
    SwSectionData aSectionData( TOX_CONTENT_SECTION, sSectNm );
    SwTOXBaseSection *const pNewSection = dynamic_cast<SwTOXBaseSection *>(
        InsertSwSection( aPam, aSectionData, & rTOX, pSet, false ));
diff --git a/sw/source/core/unocore/unoidx.cxx b/sw/source/core/unocore/unoidx.cxx
index 76f4faf..8bb22a0 100644
--- a/sw/source/core/unocore/unoidx.cxx
+++ b/sw/source/core/unocore/unoidx.cxx
@@ -1339,10 +1339,6 @@ SwXDocumentIndex::attach(const uno::Reference< text::XTextRange > & xTextRange)
    }

    UnoActionContext aAction(pDoc);
    if (aPam.HasMark())
    {
        pDoc->getIDocumentContentOperations().DeleteAndJoin(aPam);
    }

    SwTOXBase & rTOXBase = m_pImpl->m_pProps->GetTOXBase();
    SwTOXType const*const pTOXType = rTOXBase.GetTOXType();
@@ -1353,7 +1349,7 @@ SwXDocumentIndex::attach(const uno::Reference< text::XTextRange > & xTextRange)
    }
    //TODO: apply Section attributes (columns and background)
    SwTOXBaseSection *const pTOX =
        pDoc->InsertTableOf( *aPam.GetPoint(), rTOXBase );
        pDoc->InsertTableOf( aPam, rTOXBase );

    pDoc->SetTOXBaseName(*pTOX, m_pImpl->m_pProps->GetTOXBase().GetTOXName());

diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx
index c1d94ed..f147adc 100644
--- a/sw/source/filter/ww8/wrtw8nds.cxx
+++ b/sw/source/filter/ww8/wrtw8nds.cxx
@@ -91,6 +91,8 @@
#include <com/sun/star/text/RubyPosition.hpp>
#include <oox/export/vmlexport.hxx>
#include <sfx2/docfile.hxx>
#include <sal/log.hxx>
#include <comphelper/propertysequence.hxx>

#include "sprmids.hxx"

@@ -3083,7 +3085,101 @@ void MSWordExportBase::OutputSectionNode( const SwSectionNode& rSectionNode )
        }
    }
    if ( TOX_CONTENT_SECTION == rSection.GetType() )
    {
        m_bStartTOX = true;
        UpdateTocSectionNodeProperties(rSectionNode);
    }
}

// tdf#121561: During export of the ODT file with TOC inside into DOCX format,
// the TOC title is being exported as regular paragraph. We should surround it
// with <w:sdt><w:sdtPr><w:sdtContent> to make it (TOC title) recognizable
// by MS Word as part of the TOC.
void MSWordExportBase::UpdateTocSectionNodeProperties(const SwSectionNode& rSectionNode)
{
    // check section type
    {
        const SwSection& rSection = rSectionNode.GetSection();
        if (TOX_CONTENT_SECTION != rSection.GetType())
            return;

        const SwTOXBase* pTOX = rSection.GetTOXBase();
        if (pTOX)
        {
            TOXTypes type = pTOX->GetType();
            if (type != TOXTypes::TOX_CONTENT)
                return;
        }
    }

    // get section node, skip toc-header node
    const SwSectionNode* pSectNd = &rSectionNode;
    {
        SwNodeIndex aIdxNext( *pSectNd, 1 );
        const SwNode& rNdNext = aIdxNext.GetNode();

        if (rNdNext.IsSectionNode())
        {
            const SwSectionNode* pSectNdNext = static_cast<const SwSectionNode*>(&rNdNext);
            if (TOX_HEADER_SECTION == pSectNdNext->GetSection().GetType() &&
                pSectNdNext->StartOfSectionNode()->IsSectionNode())
            {
                pSectNd = pSectNdNext;
            }
        }
    }

    // get node of the first paragraph inside TOC
    SwNodeIndex aIdxNext( *pSectNd, 1 );
    const SwNode& rNdTocPara = aIdxNext.GetNode();
    const SwContentNode* pNode = rNdTocPara.GetContentNode();
    if (!pNode)
        return;

    // put required flags into grab bag of the first node in TOC
    {
        uno::Sequence<beans::PropertyValue> aDocPropertyValues(comphelper::InitPropertySequence(
        {
            {"ooxml:CT_SdtDocPart_docPartGallery", uno::makeAny(OUString("Table of Contents"))},
            {"ooxml:CT_SdtDocPart_docPartUnique",  uno::makeAny(OUString("true"))},
        }));

        uno::Sequence<beans::PropertyValue> aSdtPrPropertyValues(comphelper::InitPropertySequence(
        {
            {"ooxml:CT_SdtPr_docPartObj", uno::makeAny(aDocPropertyValues)},
        }));

        SfxGrabBagItem aGrabBag(RES_PARATR_GRABBAG);
        aGrabBag.GetGrabBag()["SdtPr"] <<= aSdtPrPropertyValues;

        // create temp attr set
        SwAttrSet aSet(pNode->GetSwAttrSet());
        aSet.Put(aGrabBag);

        // set new attr to node
        const_cast<SwContentNode*>(pNode)->SetAttr(aSet);
    }

    // set flag for the next node after TOC
    // in order to indicate that std area has been finished
    // see, DomainMapper::lcl_startParagraphGroup() for the same functionality during load
    {
        SwNodeIndex aEndTocNext( *rSectionNode.EndOfSectionNode(), 1 );
        const SwNode& rEndTocNextNode = aEndTocNext.GetNode();
        const SwContentNode* pNodeAfterToc = rEndTocNextNode.GetContentNode();
        if (pNodeAfterToc)
        {
            SfxGrabBagItem aGrabBag(RES_PARATR_GRABBAG);
            aGrabBag.GetGrabBag()["ParaSdtEndBefore"] <<= true;

            // create temp attr set
            SwAttrSet aSet(pNodeAfterToc->GetSwAttrSet());
            aSet.Put(aGrabBag);

            // set new attr to node
            const_cast<SwContentNode*>(pNodeAfterToc)->SetAttr(aSet);
        }
    }
}

void WW8Export::AppendSection( const SwPageDesc *pPageDesc, const SwSectionFormat* pFormat, sal_uLong nLnNum )
diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx
index 8a911bd..ec31a1b 100644
--- a/sw/source/filter/ww8/wrtww8.hxx
+++ b/sw/source/filter/ww8/wrtww8.hxx
@@ -847,6 +847,7 @@ protected:

    /// Output SwSectionNode
    void OutputSectionNode( const SwSectionNode& );
    static void UpdateTocSectionNodeProperties(const SwSectionNode& rSectionNode);

    virtual void AppendSection( const SwPageDesc *pPageDesc, const SwSectionFormat* pFormat, sal_uLong nLnNum ) = 0;

diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index bfdcad2..8114600 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -500,6 +500,15 @@ void DomainMapper_Impl::SetParaSectpr(bool bParaSectpr)
void DomainMapper_Impl::SetSdt(bool bSdt)
{
    m_bSdt = bSdt;

    if (m_bSdt)
    {
        m_xStdEntryStart = GetTopTextAppend()->getEnd();
    }
    else
    {
        m_xStdEntryStart = uno::Reference< text::XTextRange >();
    }
}


@@ -3478,6 +3487,42 @@ static uno::Sequence< beans::PropertyValues > lcl_createTOXLevelHyperlinks( bool
    return aNewLevel;
}

/// Returns title of the TOC placed in paragraph(s) before TOC field inside STD-frame
OUString DomainMapper_Impl::extractTocTitle()
{
    if (!m_xStdEntryStart.is())
        return OUString();

    uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
    if(!xTextAppend.is())
        return OUString();

    // try-catch was added in the same way as inside appendTextSectionAfter()
    try
    {
        uno::Reference< text::XParagraphCursor > xCursor( xTextAppend->createTextCursorByRange( m_xStdEntryStart ), uno::UNO_QUERY_THROW);
        if (!xCursor.is())
            return OUString();

        //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
        xCursor->gotoStartOfParagraph( false );
        if (m_aTextAppendStack.top().xInsertPosition.is())
            xCursor->gotoRange( m_aTextAppendStack.top().xInsertPosition, true );
        else
            xCursor->gotoEnd( true );

        //the paragraph after this new section is already inserted
        xCursor->goLeft(1, true);

        return xCursor->getString();
    }
    catch(const uno::Exception&)
    {
    }

    return OUString();
}

void DomainMapper_Impl::handleToc
    (const FieldContextPtr& pContext,
    const OUString & sTOCServiceName)
@@ -3597,16 +3642,57 @@ void DomainMapper_Impl::handleToc
    if( !bFromOutline && !bFromEntries && sTemplate.isEmpty()  )
        bFromOutline = true;

    const OUString aTocTitle = extractTocTitle();

    if (m_xTextFactory.is())
        xTOC.set(
                m_xTextFactory->createInstance
                ( bTableOfFigures ?
                  "com.sun.star.text.IllustrationsIndex"
                  : sTOCServiceName),
                uno::UNO_QUERY_THROW);
    if (m_xTextFactory.is() && ! m_aTextAppendStack.empty())
    {
        if (aTocTitle.isEmpty() || bTableOfFigures)
        {
            xTOC.set(
                    m_xTextFactory->createInstance
                    ( bTableOfFigures ?
                      "com.sun.star.text.IllustrationsIndex"
                      : sTOCServiceName),
                    uno::UNO_QUERY_THROW);

            OUString const sMarker("Y");
            //insert index
            uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY );
            uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
            if (xTextAppend.is())
            {
                uno::Reference< text::XTextCursor > xCrsr = xTextAppend->getText()->createTextCursor();
                uno::Reference< text::XText > xText = xTextAppend->getText();
                if(xCrsr.is() && xText.is())
                {
                    xCrsr->gotoEnd(false);
                    xText->insertString(xCrsr, sMarker, false);
                    xText->insertTextContent(uno::Reference< text::XTextRange >( xCrsr, uno::UNO_QUERY_THROW ), xToInsert, false);
                    xTOCMarkerCursor = xCrsr;
                }
            }
        }
        else
        {
            // create TOC section
            css::uno::Reference<css::text::XTextRange> xTextRangeEndOfTocHeader = GetTopTextAppend()->getEnd();
            xTOC = createSectionForRange(m_xStdEntryStart, xTextRangeEndOfTocHeader, sTOCServiceName, false);

            // init [xTOCMarkerCursor]
            uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
            uno::Reference< text::XText > xText = xTextAppend->getText();
            uno::Reference< text::XTextCursor > xCrsr = xText->createTextCursor();
            xTOCMarkerCursor = xCrsr;

            // create header of the TOC with the TOC title inside
            const OUString aObjectType("com.sun.star.text.IndexHeaderSection");
            uno::Reference<beans::XPropertySet> xIfc = createSectionForRange(m_xStdEntryStart, xTextRangeEndOfTocHeader, aObjectType, true);
        }
    }

    if (xTOC.is())
        xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::makeAny(OUString()));
        xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::makeAny(aTocTitle));

    if (!aBookmarkName.isEmpty())
        xTOC->setPropertyValue(getPropertyName(PROP_TOC_BOOKMARK), uno::makeAny(aBookmarkName));
    if( !bTableOfFigures && xTOC.is() )
@@ -3696,27 +3782,45 @@ void DomainMapper_Impl::handleToc
    }
    pContext->SetTOC( xTOC );
    m_bParaHadField = false;
}

uno::Reference<beans::XPropertySet> DomainMapper_Impl::createSectionForRange(
    uno::Reference< css::text::XTextRange > xStart,
    uno::Reference< css::text::XTextRange > xEnd,
    const OUString & sObjectType,
    bool stepLeft)
{
    if (!xStart.is())
        return uno::Reference<beans::XPropertySet>();
    if (!xEnd.is())
        return uno::Reference<beans::XPropertySet>();

    uno::Reference< beans::XPropertySet > xRet;
    if (m_aTextAppendStack.empty())
        return;

    OUString const sMarker("Y");
    //insert index
    uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY );
        return xRet;
    uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
    if (xTextAppend.is())
    if(xTextAppend.is())
    {
        uno::Reference< text::XTextCursor > xCrsr = xTextAppend->getText()->createTextCursor();

        uno::Reference< text::XText > xText = xTextAppend->getText();
        if(xCrsr.is() && xText.is())
        try
        {
            xCrsr->gotoEnd(false);
            xText->insertString(xCrsr, sMarker, false);
            xText->insertTextContent(uno::Reference< text::XTextRange >( xCrsr, uno::UNO_QUERY_THROW ), xToInsert, false);
            xTOCMarkerCursor = xCrsr;
            uno::Reference< text::XParagraphCursor > xCursor(
                xTextAppend->createTextCursorByRange( xStart ), uno::UNO_QUERY_THROW);
            //the cursor has been moved to the end of the paragraph because of the appendTextPortion() calls
            xCursor->gotoStartOfParagraph( false );
            xCursor->gotoRange( xEnd, true );
            //the paragraph after this new section is already inserted
            if (stepLeft)
                xCursor->goLeft(1, true);
            uno::Reference< text::XTextContent > xSection( m_xTextFactory->createInstance(sObjectType), uno::UNO_QUERY_THROW );
            xSection->attach( uno::Reference< text::XTextRange >( xCursor, uno::UNO_QUERY_THROW) );
            xRet.set(xSection, uno::UNO_QUERY );
        }
        catch(const uno::Exception&)
        {
        }
    }

    return xRet;
}

void DomainMapper_Impl::handleBibliography
@@ -4803,10 +4907,13 @@ void DomainMapper_Impl::PopFieldContext()
                        }
                        else
                        {
                            xTOCMarkerCursor->goLeft(1,true);
                            xTOCMarkerCursor->setString(OUString());
                            xTOCMarkerCursor->goLeft(1,true);
                            xTOCMarkerCursor->setString(OUString());
                            if (!m_xStdEntryStart.is())
                            {
                                xTOCMarkerCursor->goLeft(1,true);
                                xTOCMarkerCursor->setString(OUString());
                                xTOCMarkerCursor->goLeft(1,true);
                                xTOCMarkerCursor->setString(OUString());
                            }
                        }
                    }
                    if (m_bStartedTOC || m_bStartIndex || m_bStartBibliography)
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index cc014db..984f0e4 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -541,6 +541,7 @@ private:
    SmartTagHandler m_aSmartTagHandler;

    css::uno::Reference<css::text::XTextRange> m_xGlossaryEntryStart;
    css::uno::Reference<css::text::XTextRange> m_xStdEntryStart;

public:
    css::uno::Reference<css::text::XTextRange> m_xInsertTextRange;
@@ -788,6 +789,10 @@ public:
    /// The end of field is reached (cFieldEnd appeared) - the command might still be open.
    void PopFieldContext();

    /// Returns title of the TOC placed in paragraph(s) before TOC field inside STD-frame
    OUString extractTocTitle();
    css::uno::Reference<css::beans::XPropertySet> createSectionForRange(css::uno::Reference< css::text::XTextRange > xStart, css::uno::Reference< css::text::XTextRange > xEnd, const OUString & sObjectType, bool stepLeft);

    void SetBookmarkName( const OUString& rBookmarkName );
    void StartOrEndBookmark( const OUString& rId );