    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     * This file is part of the LibreOffice project.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
    #include <com/sun/star/beans/XPropertySet.hpp>
    #include <com/sun/star/beans/XPropertyContainer.hpp>
    #include <com/sun/star/document/XDocumentProperties.hpp>
    #include "DomainMapper_Impl.hxx"
    #include "ConversionHelper.hxx"
    #include "SdtHelper.hxx"
    #include "DomainMapperTableHandler.hxx"
    #include <com/sun/star/uno/XComponentContext.hpp>
    #include <com/sun/star/graphic/XGraphic.hpp>
    #include <com/sun/star/beans/XPropertyState.hpp>
    #include <com/sun/star/container/XNamed.hpp>
    #include <com/sun/star/document/PrinterIndependentLayout.hpp>
    #include <com/sun/star/document/IndexedPropertyValues.hpp>
    #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
    #include <com/sun/star/embed/XEmbeddedObject.hpp>
    #include <com/sun/star/lang/XServiceInfo.hpp>
    #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
    #include <com/sun/star/style/LineNumberPosition.hpp>
    #include <com/sun/star/style/LineSpacing.hpp>
    #include <com/sun/star/style/LineSpacingMode.hpp>
    #include <com/sun/star/text/ChapterFormat.hpp>
    #include <com/sun/star/text/FilenameDisplayFormat.hpp>
    #include <com/sun/star/text/SetVariableType.hpp>
    #include <com/sun/star/text/XFootnote.hpp>
    #include <com/sun/star/text/XLineNumberingProperties.hpp>
    #include <com/sun/star/style/XStyle.hpp>
    #include <com/sun/star/text/PageNumberType.hpp>
    #include <com/sun/star/text/HoriOrientation.hpp>
    #include <com/sun/star/text/VertOrientation.hpp>
    #include <com/sun/star/text/ReferenceFieldPart.hpp>
    #include <com/sun/star/text/RelOrientation.hpp>
    #include <com/sun/star/text/ReferenceFieldSource.hpp>
    #include <com/sun/star/text/SizeType.hpp>
    #include <com/sun/star/text/TextContentAnchorType.hpp>
    #include <com/sun/star/text/WrapTextMode.hpp>
    #include <com/sun/star/text/XDependentTextField.hpp>
    #include <com/sun/star/text/XParagraphCursor.hpp>
    #include <com/sun/star/text/XRedline.hpp>
    #include <com/sun/star/text/XTextFieldsSupplier.hpp>
    #include <com/sun/star/text/RubyPosition.hpp>
    #include <com/sun/star/style/DropCapFormat.hpp>
    #include <com/sun/star/util/NumberFormatter.hpp>
    #include <com/sun/star/util/XNumberFormatsSupplier.hpp>
    #include <com/sun/star/util/XNumberFormatter.hpp>
    #include <com/sun/star/document/XViewDataSupplier.hpp>
    #include <com/sun/star/container/XIndexContainer.hpp>
    #include <com/sun/star/awt/XControlModel.hpp>
    #include <com/sun/star/drawing/XControlShape.hpp>
    #include <com/sun/star/text/ControlCharacter.hpp>
    #include <com/sun/star/text/XTextColumns.hpp>
    #include <com/sun/star/awt/CharSet.hpp>
    #include <com/sun/star/lang/XMultiServiceFactory.hpp>
    #include <o3tl/temporary.hxx>
    #include <oox/mathml/import.hxx>
    #include <rtl/uri.hxx>
    #include "GraphicHelpers.hxx"
    #include <dmapper/GraphicZOrderHelper.hxx>

    #include <oox/token/tokens.hxx>

    #include <cmath>
    #include <map>
    #include <tuple>
    #include <unordered_map>

    #include <vcl/svapp.hxx>
    #include <vcl/outdev.hxx>
    #include <officecfg/Office/Common.hxx>
    #include <filter/msfilter/util.hxx>
    #include <comphelper/sequence.hxx>
    #include <comphelper/propertyvalue.hxx>
    #include <comphelper/propertysequence.hxx>
    #include <unotools/configmgr.hxx>
    #include <unotools/mediadescriptor.hxx>
    #include <tools/diagnose_ex.h>
    #include <sal/log.hxx>

    using namespace ::com::sun::star;
    using namespace oox;
    namespace writerfilter {
    namespace dmapper{

    //line numbering for header/footer
    static void lcl_linenumberingHeaderFooter( const uno::Reference<container::XNameContainer>& xStyles, const OUString& rname, DomainMapper_Impl* dmapper )
    {
        const StyleSheetEntryPtr pEntry = dmapper->GetStyleSheetTable()->FindStyleSheetByISTD( rname );
        if (!pEntry)
            return;
        const StyleSheetPropertyMap* pStyleSheetProperties = dynamic_cast<const StyleSheetPropertyMap*>( pEntry->pProperties.get() );
        if ( !pStyleSheetProperties )
            return;
        sal_Int32 nListId = pStyleSheetProperties->GetListId();
        if( xStyles.is() )
        {
            if( xStyles->hasByName( rname ) )
            {
                uno::Reference< style::XStyle > xStyle;
                xStyles->getByName( rname ) >>= xStyle;
                if( !xStyle.is() )
                    return;
                uno::Reference<beans::XPropertySet> xPropertySet( xStyle, uno::UNO_QUERY );
                xPropertySet->setPropertyValue( getPropertyName( PROP_PARA_LINE_NUMBER_COUNT ), uno::makeAny( nListId >= 0 ) );
            }
        }
    }

    // Populate Dropdown Field properties from FFData structure
    static void lcl_handleDropdownField( const uno::Reference< beans::XPropertySet >& rxFieldProps, const FFDataHandler::Pointer_t& pFFDataHandler )
    {
        if ( rxFieldProps.is() )
        {
            if ( !pFFDataHandler->getName().isEmpty() )
                rxFieldProps->setPropertyValue( "Name", uno::makeAny( pFFDataHandler->getName() ) );

            const FFDataHandler::DropDownEntries_t& rEntries = pFFDataHandler->getDropDownEntries();
            uno::Sequence< OUString > sItems( rEntries.size() );
            ::std::copy( rEntries.begin(), rEntries.end(), sItems.begin());
            if ( sItems.getLength() )
                rxFieldProps->setPropertyValue( "Items", uno::makeAny( sItems ) );

            sal_Int32 nResult = pFFDataHandler->getDropDownResult().toInt32();
            if ( nResult )
                rxFieldProps->setPropertyValue( "SelectedItem", uno::makeAny( sItems[ nResult ] ) );
            if ( !pFFDataHandler->getHelpText().isEmpty() )
                 rxFieldProps->setPropertyValue( "Help", uno::makeAny( pFFDataHandler->getHelpText() ) );
        }
    }

    static void lcl_handleTextField( const uno::Reference< beans::XPropertySet >& rxFieldProps, const FFDataHandler::Pointer_t& pFFDataHandler )
    {
        if ( rxFieldProps.is() && pFFDataHandler )
        {
            rxFieldProps->setPropertyValue
                (getPropertyName(PROP_HINT),
                uno::makeAny(pFFDataHandler->getStatusText()));
            rxFieldProps->setPropertyValue
                (getPropertyName(PROP_HELP),
                uno::makeAny(pFFDataHandler->getHelpText()));
            rxFieldProps->setPropertyValue
                (getPropertyName(PROP_CONTENT),
                uno::makeAny(pFFDataHandler->getTextDefault()));
        }
    }

    struct FieldConversion
    {
        const sal_Char*     cFieldServiceName;
        FieldId             eFieldId;
    };

    typedef std::unordered_map<OUString, FieldConversion> FieldConversionMap_t;

    uno::Any FloatingTableInfo::getPropertyValue(const OUString &propertyName)
    {
        for( beans::PropertyValue const & propVal : m_aFrameProperties )
            if( propVal.Name == propertyName )
                return propVal.Value ;
        return uno::Any() ;
    }

    DomainMapper_Impl::DomainMapper_Impl(
                DomainMapper& rDMapper,
                uno::Reference<uno::XComponentContext> const& xContext,
                uno::Reference<lang::XComponent> const& xModel,
                SourceDocumentType eDocumentType,
                utl::MediaDescriptor const & rMediaDesc) :
            m_eDocumentType( eDocumentType ),
            m_rDMapper( rDMapper ),
            m_xTextDocument( xModel, uno::UNO_QUERY ),
            m_xTextFactory( xModel, uno::UNO_QUERY ),
            m_xComponentContext( xContext ),
            m_bSetUserFieldContent( false ),
            m_bSetCitation( false ),
            m_bSetDateValue( false ),
            m_bIsFirstSection( true ),
            m_bIsColumnBreakDeferred( false ),
            m_bIsPageBreakDeferred( false ),
            m_bSdtEndDeferred(false),
            m_bParaSdtEndDeferred(false),
            m_bStartTOC(false),
            m_bStartTOCHeaderFooter(false),
            m_bStartedTOC(false),
            m_bStartIndex(false),
            m_bStartBibliography(false),
            m_bStartGenericField(false),
            m_bTextInserted(false),
            m_sCurrentPermId(0),
            m_pLastSectionContext( ),
            m_pLastCharacterContext(),
            m_sCurrentParaStyleName(),
            m_sDefaultParaStyleName(),
            m_bInStyleSheetImport( false ),
            m_bInAnyTableImport( false ),
            m_bInHeaderFooterImport( false ),
            m_bDiscardHeaderFooter( false ),
            m_bInFootOrEndnote(false),
            m_bSeenFootOrEndnoteSeparator(false),
            m_bLineNumberingSet( false ),
            m_bIsInFootnoteProperties( false ),
            m_bIsCustomFtnMark( false ),
            m_bIsParaMarkerChange( false ),
            m_bParaChanged( false ),
            m_bIsFirstParaInSection( true ),
            m_bDummyParaAddedForTableInSection( false ),
            m_bTextFrameInserted(false),
            m_bIsPreviousParagraphFramed( false ),
            m_bIsLastParaInSection( false ),
            m_bIsLastSectionGroup( false ),
            m_bIsInComments( false ),
            m_bParaSectpr( false ),
            m_bUsingEnhancedFields( false ),
            m_bSdt(false),
            m_bIsFirstRun(false),
            m_bIsOutsideAParagraph(true),
            m_xAnnotationField(),
            m_nAnnotationId( -1 ),
            m_aAnnotationPositions(),
            m_aSmartTagHandler(m_xComponentContext, m_xTextDocument),
            m_xInsertTextRange(rMediaDesc.getUnpackedValueOrDefault("TextInsertModeRange", uno::Reference<text::XTextRange>())),
            m_bIsNewDoc(!rMediaDesc.getUnpackedValueOrDefault("InsertMode", false)),
            m_bInTableStyleRunProps(false),
            m_nTableDepth(0),
            m_nTableCellDepth(0),
            m_nLastTableCellParagraphDepth(0),
            m_bHasFtn(false),
            m_bHasFtnSep(false),
            m_bIgnoreNextPara(false),
            m_bCheckFirstFootnoteTab(false),
            m_bIgnoreNextTab(false),
            m_bFrameBtLr(false),
            m_bIsSplitPara(false),
            m_vTextFramesForChaining(),
            m_bParaHadField(false),
            m_bParaAutoBefore(false),
            m_bFirstParagraphInCell(true),
            m_bSaveFirstParagraphInCell(false)

    {
        m_aBaseUrl = rMediaDesc.getUnpackedValueOrDefault(
            utl::MediaDescriptor::PROP_DOCUMENTBASEURL(), OUString());
        if (m_aBaseUrl.isEmpty()) {
            m_aBaseUrl = rMediaDesc.getUnpackedValueOrDefault(
                utl::MediaDescriptor::PROP_URL(), OUString());
        }

        appendTableManager( );
        GetBodyText();
        uno::Reference< text::XTextAppend > xBodyTextAppend( m_xBodyText, uno::UNO_QUERY );
        m_aTextAppendStack.push(TextAppendContext(xBodyTextAppend,
                    m_bIsNewDoc ? uno::Reference<text::XTextCursor>() : m_xBodyText->createTextCursorByRange(m_xInsertTextRange)));

        //todo: does it make sense to set the body text as static text interface?
        uno::Reference< text::XTextAppendAndConvert > xBodyTextAppendAndConvert( m_xBodyText, uno::UNO_QUERY );
        m_pTableHandler = new DomainMapperTableHandler(xBodyTextAppendAndConvert, *this);
        getTableManager( ).setHandler(m_pTableHandler);

        getTableManager( ).startLevel();
        m_bUsingEnhancedFields = !utl::ConfigManager::IsFuzzing() && officecfg::Office::Common::Filter::Microsoft::Import::ImportWWFieldsAsEnhancedFields::get(m_xComponentContext);

        m_pSdtHelper = new SdtHelper(*this);

        m_aRedlines.push(std::vector<RedlineParamsPtr>());
    }

    DomainMapper_Impl::~DomainMapper_Impl()
    {
        ChainTextFrames();
        // Don't remove last paragraph when pasting, sw expects that empty paragraph.
        if (m_bIsNewDoc)
            RemoveLastParagraph();
        if (hasTableManager())
        {
            getTableManager().endLevel();
            popTableManager();
        }
    }

    uno::Reference< container::XNameContainer > const &  DomainMapper_Impl::GetPageStyles()
    {
        if(!m_xPageStyles.is())
        {
            uno::Reference< style::XStyleFamiliesSupplier > xSupplier( m_xTextDocument, uno::UNO_QUERY );
            if (xSupplier.is())
                xSupplier->getStyleFamilies()->getByName("PageStyles") >>= m_xPageStyles;
        }
        return m_xPageStyles;
    }

    uno::Reference< text::XText > const & DomainMapper_Impl::GetBodyText()
    {
        if(!m_xBodyText.is())
        {
            if (m_xInsertTextRange.is())
                m_xBodyText = m_xInsertTextRange->getText();
            else if (m_xTextDocument.is())
                m_xBodyText = m_xTextDocument->getText();
        }
        return m_xBodyText;
    }

    uno::Reference< beans::XPropertySet > const & DomainMapper_Impl::GetDocumentSettings()
    {
        if( !m_xDocumentSettings.is() && m_xTextFactory.is())
        {
            m_xDocumentSettings.set( m_xTextFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY );
        }
        return m_xDocumentSettings;
    }

    void DomainMapper_Impl::SetDocumentSettingsProperty( const OUString& rPropName, const uno::Any& rValue )
    {
        uno::Reference< beans::XPropertySet > xSettings = GetDocumentSettings();
        if( xSettings.is() )
        {
            try
            {
                xSettings->setPropertyValue( rPropName, rValue );
            }
            catch( const uno::Exception& )
            {
            }
        }
    }
    void DomainMapper_Impl::RemoveDummyParaForTableInSection()
    {
        SetIsDummyParaAddedForTableInSection(false);
        PropertyMapPtr pContext = GetTopContextOfType(CONTEXT_SECTION);
        SectionPropertyMap* pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
        if (!pSectionContext)
            return;

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

        uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursorByRange(pSectionContext->GetStartingRange());

        // Remove the extra NumPicBullets from the document,
        // which get attached to the first paragraph in the
        // document
        ListsManager::Pointer pListTable = GetListTable();
        pListTable->DisposeNumPicBullets();

        uno::Reference<container::XEnumerationAccess> xEnumerationAccess(xCursor, uno::UNO_QUERY);
        if (xEnumerationAccess.is() && m_aTextAppendStack.size() == 1 )
        {
            uno::Reference<container::XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
            uno::Reference<lang::XComponent> xParagraph(xEnumeration->nextElement(), uno::UNO_QUERY);
            xParagraph->dispose();
        }
    }
    void DomainMapper_Impl::AddDummyParaForTableInSection()
    {
        // Shapes can't have sections.
        if (IsInShape())
            return;

        if (!m_aTextAppendStack.empty())
        {
            uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
            uno::Reference< text::XTextCursor > xCrsr = xTextAppend->getText()->createTextCursor();
            uno::Reference< text::XText > xText = xTextAppend->getText();
            if(xCrsr.is() && xText.is())
            {
                xTextAppend->finishParagraph(  uno::Sequence< beans::PropertyValue >() );
                SetIsDummyParaAddedForTableInSection(true);
            }
        }
    }

    void DomainMapper_Impl::RemoveLastParagraph( )
    {
        if (m_bDiscardHeaderFooter)
            return;

        if (m_aTextAppendStack.empty())
            return;
        uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
        if (!xTextAppend.is())
            return;
        try
        {
            uno::Reference< text::XTextCursor > xCursor;
            if (m_bIsNewDoc)
            {
                xCursor = xTextAppend->createTextCursor();
                xCursor->gotoEnd(false);
            }
            else
                xCursor.set(m_aTextAppendStack.top().xCursor, uno::UNO_QUERY);
            uno::Reference<container::XEnumerationAccess> xEnumerationAccess(xCursor, uno::UNO_QUERY);
            // Keep the character properties of the last but one paragraph, even if
            // it's empty. This works for headers/footers, and maybe in other cases
            // as well, but surely not in textboxes.
            // fdo#58327: also do this at the end of the document: when pasting,
            // a table before the cursor position would be deleted
            // (but only for paste/insert, not load; otherwise it can happen that
            // flys anchored at the disposed paragraph are deleted (fdo47036.rtf))
            bool const bEndOfDocument(m_aTextAppendStack.size() == 1);
            if ((m_bInHeaderFooterImport || (bEndOfDocument && !m_bIsNewDoc))
                && xEnumerationAccess.is())
            {
                uno::Reference<container::XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
                uno::Reference<lang::XComponent> xParagraph(xEnumeration->nextElement(), uno::UNO_QUERY);
                xParagraph->dispose();
            }
            else if (xCursor.is())
            {
                xCursor->goLeft( 1, true );
                // If this is a text on a shape, possibly the text has the trailing
                // newline removed already.
                if (xCursor->getString() == SAL_NEWLINE_STRING)
                {
                    uno::Reference<beans::XPropertySet> xDocProps(GetTextDocument(), uno::UNO_QUERY);
                    const OUString aRecordChanges("RecordChanges");
                    uno::Any aPreviousValue(xDocProps->getPropertyValue(aRecordChanges));

                    // disable redlining for this operation, otherwise we might
                    // end up with an unwanted recorded deletion
                    xDocProps->setPropertyValue(aRecordChanges, uno::Any(false));

                    // delete
                    xCursor->setString(OUString());

                    // restore again
                    xDocProps->setPropertyValue(aRecordChanges, aPreviousValue);
                }
            }
        }
        catch( const uno::Exception& )
        {
        }
    }

    void DomainMapper_Impl::SetIsLastSectionGroup( bool bIsLast )
    {
        m_bIsLastSectionGroup = bIsLast;
    }

    void DomainMapper_Impl::SetIsLastParagraphInSection( bool bIsLast )
    {
        m_bIsLastParaInSection = bIsLast;
    }

    void DomainMapper_Impl::SetIsFirstParagraphInSection( bool bIsFirst )
    {
        m_bIsFirstParaInSection = bIsFirst;
    }

    bool DomainMapper_Impl::GetIsFirstParagraphInSection()
    {
        // Anchored objects may include multiple paragraphs,
        // and none of them should be considered the first para in section.
        return m_bIsFirstParaInSection
                    && !IsInShape()
                    && !m_bIsInComments
                    && !m_bInFootOrEndnote;
    }

    void DomainMapper_Impl::SetIsFirstParagraphInShape(bool bIsFirst)
    {
        m_bIsFirstParaInShape = bIsFirst;
    }

    void DomainMapper_Impl::SetIsDummyParaAddedForTableInSection( bool bIsAdded )
    {
        m_bDummyParaAddedForTableInSection = bIsAdded;
    }

    void DomainMapper_Impl::SetIsTextFrameInserted( bool bIsInserted )
    {
        m_bTextFrameInserted  = bIsInserted;
    }

    void DomainMapper_Impl::SetParaSectpr(bool bParaSectpr)
    {
        m_bParaSectpr = bParaSectpr;
    }

    void DomainMapper_Impl::SetSdt(bool bSdt)
    {
        m_bSdt = bSdt;
    }

    void    DomainMapper_Impl::PushProperties(ContextType eId)
    {
        PropertyMapPtr pInsert(eId == CONTEXT_SECTION ?
            (new SectionPropertyMap( m_bIsFirstSection )) :
            eId == CONTEXT_PARAGRAPH ? new ParagraphPropertyMap :  new PropertyMap);
        if(eId == CONTEXT_SECTION)
        {
            if( m_bIsFirstSection )
                m_bIsFirstSection = false;
            // beginning with the second section group a section has to be inserted
            // into the document
            SectionPropertyMap* pSectionContext_ = dynamic_cast< SectionPropertyMap* >( pInsert.get() );
            if (!m_aTextAppendStack.empty())
            {
                uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
                if (xTextAppend.is() && pSectionContext_)
                    pSectionContext_->SetStart( xTextAppend->getEnd() );
            }
        }
        if(eId == CONTEXT_PARAGRAPH && m_bIsSplitPara)
        {
            m_aPropertyStacks[eId].push( GetTopContextOfType(eId));
            m_bIsSplitPara = false;
        }
        else
        {
            m_aPropertyStacks[eId].push( pInsert );
        }
        m_aContextStack.push(eId);

        m_pTopContext = m_aPropertyStacks[eId].top();
    }

    void DomainMapper_Impl::PushStyleProperties( const PropertyMapPtr& pStyleProperties )
    {
        m_aPropertyStacks[CONTEXT_STYLESHEET].push( pStyleProperties );
        m_aContextStack.push(CONTEXT_STYLESHEET);

        m_pTopContext = m_aPropertyStacks[CONTEXT_STYLESHEET].top();
    }

    void DomainMapper_Impl::PushListProperties(const PropertyMapPtr& pListProperties)
    {
        m_aPropertyStacks[CONTEXT_LIST].push( pListProperties );
        m_aContextStack.push(CONTEXT_LIST);
        m_pTopContext = m_aPropertyStacks[CONTEXT_LIST].top();
    }

    void    DomainMapper_Impl::PopProperties(ContextType eId)
    {
        OSL_ENSURE(!m_aPropertyStacks[eId].empty(), "section stack already empty");
        if ( m_aPropertyStacks[eId].empty() )
            return;

        if ( eId == CONTEXT_SECTION )
        {
            m_pLastSectionContext = m_aPropertyStacks[eId].top( );
        }
        else if (eId == CONTEXT_CHARACTER)
        {
            m_pLastCharacterContext = m_aPropertyStacks[eId].top();
            // Sadly an assert about deferredCharacterProperties being empty is not possible
            // here, because appendTextPortion() may not be called for every character section.
            deferredCharacterProperties.clear();
        }

        m_aPropertyStacks[eId].pop();
        m_aContextStack.pop();
        if(!m_aContextStack.empty() && !m_aPropertyStacks[m_aContextStack.top()].empty())

                m_pTopContext = m_aPropertyStacks[m_aContextStack.top()].top();
        else
        {
            // OSL_ENSURE(eId == CONTEXT_SECTION, "this should happen at a section context end");
            m_pTopContext.clear();
        }
    }

    PropertyMapPtr DomainMapper_Impl::GetTopContextOfType(ContextType eId)
    {
        PropertyMapPtr pRet;
        if(!m_aPropertyStacks[eId].empty())
            pRet = m_aPropertyStacks[eId].top();
        return pRet;
    }

    uno::Reference< text::XTextAppend > const &  DomainMapper_Impl::GetTopTextAppend()
    {
        OSL_ENSURE(!m_aTextAppendStack.empty(), "text append stack is empty" );
        return m_aTextAppendStack.top().xTextAppend;
    }

    FieldContextPtr const &  DomainMapper_Impl::GetTopFieldContext()
    {
        SAL_WARN_IF(m_aFieldStack.empty(), "writerfilter.dmapper", "Field stack is empty");
        return m_aFieldStack.top();
    }

    void DomainMapper_Impl::InitTabStopFromStyle( const uno::Sequence< style::TabStop >& rInitTabStops )
    {
        OSL_ENSURE(m_aCurrentTabStops.empty(), "tab stops already initialized");
        for( sal_Int32 nTab = 0; nTab < rInitTabStops.getLength(); ++nTab)
        {
            m_aCurrentTabStops.emplace_back(rInitTabStops[nTab] );
        }
    }

    void DomainMapper_Impl::IncorporateTabStop( const DeletableTabStop &  rTabStop )
    {
        ::std::vector<DeletableTabStop>::iterator aIt = m_aCurrentTabStops.begin();
        ::std::vector<DeletableTabStop>::iterator aEndIt = m_aCurrentTabStops.end();
        sal_Int32 nConverted = rTabStop.Position;
        bool bFound = false;
        for( ; aIt != aEndIt; ++aIt)
        {
            if( aIt->Position == nConverted )
            {
                bFound = true;
                if( rTabStop.bDeleted )
                    m_aCurrentTabStops.erase( aIt );
                else
                    *aIt = rTabStop;
                break;
            }
        }
        if( !bFound )
            m_aCurrentTabStops.push_back( rTabStop );
    }

    uno::Sequence< style::TabStop > DomainMapper_Impl::GetCurrentTabStopAndClear()
    {
        std::vector<style::TabStop> aRet;
        for (DeletableTabStop& rStop : m_aCurrentTabStops)
        {
            if (!rStop.bDeleted)
                aRet.push_back(rStop);
        }
        m_aCurrentTabStops.clear();
        return comphelper::containerToSequence(aRet);
    }

    const OUString DomainMapper_Impl::GetCurrentParaStyleName()
    {
        // use saved currParaStyleName as a fallback, in case no particular para style name applied.
        OUString sName = m_sCurrentParaStyleName;
        PropertyMapPtr pParaContext = GetTopContextOfType(CONTEXT_PARAGRAPH);
        if ( pParaContext && pParaContext->isSet(PROP_PARA_STYLE_NAME) )
            pParaContext->getProperty(PROP_PARA_STYLE_NAME)->second >>= sName;

        // In rare situations the name might still be blank, so use the default style,
        // despite documentation that states, "If this attribute is not specified for any style,
        // then no properties shall be applied to objects of the specified type."
        // Word, however, assigns "Normal" style even in these situations.
        if ( !m_bInStyleSheetImport && sName.isEmpty() )
            sName = GetDefaultParaStyleName();

        return sName;
    }

    const OUString DomainMapper_Impl::GetDefaultParaStyleName()
    {
        // After import the default style won't change and is frequently requested: cache the LO style name.
        // TODO assert !InStyleSheetImport? This function really only makes sense once import is finished anyway.
        if ( m_sDefaultParaStyleName.isEmpty() )
        {
            const StyleSheetEntryPtr pEntry = GetStyleSheetTable()->FindDefaultParaStyle();
            if ( pEntry && !pEntry->sConvertedStyleName.isEmpty() )
            {
                if ( !m_bInStyleSheetImport )
                    m_sDefaultParaStyleName = pEntry->sConvertedStyleName;
                return pEntry->sConvertedStyleName;
            }
            else
                return OUString( "Standard");
        }
        return m_sDefaultParaStyleName;
    }

    /*-------------------------------------------------------------------------
        returns the value from the current paragraph style - if available
      -----------------------------------------------------------------------*/
    uno::Any DomainMapper_Impl::GetPropertyFromStyleSheet(PropertyIds eId)
    {
        StyleSheetEntryPtr pEntry;
        if( m_bInStyleSheetImport )
            pEntry = GetStyleSheetTable()->GetCurrentEntry();
        else
            pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(GetCurrentParaStyleName());
        while(pEntry.get( ) )
        {
            if(pEntry->pProperties)
            {
                boost::optional<PropertyMap::Property> aProperty =
                        pEntry->pProperties->getProperty(eId);
                if( aProperty )
                {
                    return aProperty->second;
                }
            }
            //search until the property is set or no parent is available
            StyleSheetEntryPtr pNewEntry;
            if ( !pEntry->sBaseStyleIdentifier.isEmpty() )
                pNewEntry = GetStyleSheetTable()->FindStyleSheetByISTD(pEntry->sBaseStyleIdentifier);

            SAL_WARN_IF( pEntry == pNewEntry, "writerfilter.dmapper", "circular loop in style hierarchy?");

            if (pEntry == pNewEntry) //fdo#49587
                break;

            pEntry = pNewEntry;
        }
        // not found in style, try the document's DocDefault properties
        {
            const PropertyMapPtr& pDefaultParaProps = GetStyleSheetTable()->GetDefaultParaProps();
            if ( pDefaultParaProps )
            {
                boost::optional<PropertyMap::Property> aProperty = pDefaultParaProps->getProperty(eId);
                if ( aProperty )
                    return aProperty->second;
            }
            const PropertyMapPtr& pDefaultCharProps = GetStyleSheetTable()->GetDefaultCharProps();
            if ( pDefaultCharProps )
            {
                boost::optional<PropertyMap::Property> aProperty = pDefaultCharProps->getProperty(eId);
                if ( aProperty )
                    return aProperty->second;
            }
        }
        return uno::Any();
    }

    uno::Any DomainMapper_Impl::GetAnyProperty(PropertyIds eId, const PropertyMapPtr& rContext)
    {
        if ( rContext )
        {
            boost::optional<PropertyMap::Property> aProperty = rContext->getProperty(eId);
            if ( aProperty )
                return aProperty->second;
        }
        return GetPropertyFromStyleSheet(eId);
    }

    ListsManager::Pointer const & DomainMapper_Impl::GetListTable()
    {
        if(!m_pListTable)
            m_pListTable =
                new ListsManager( m_rDMapper, m_xTextFactory );
        return m_pListTable;
    }

    void DomainMapper_Impl::deferBreak( BreakType deferredBreakType)
    {
        switch (deferredBreakType)
        {
        case COLUMN_BREAK:
                m_bIsColumnBreakDeferred = true;
            break;
        case PAGE_BREAK:
                // See SwWW8ImplReader::HandlePageBreakChar(), page break should be
                // ignored inside tables.
                if (m_nTableDepth > 0)
                    return;

                m_bIsPageBreakDeferred = true;
            break;
        default:
            return;
        }
    }

    bool DomainMapper_Impl::isBreakDeferred( BreakType deferredBreakType )
    {
        switch (deferredBreakType)
        {
        case COLUMN_BREAK:
            return m_bIsColumnBreakDeferred;
        case PAGE_BREAK:
            return m_bIsPageBreakDeferred;
        default:
            return false;
        }
    }

    void DomainMapper_Impl::clearDeferredBreak(BreakType deferredBreakType)
    {
        switch (deferredBreakType)
        {
        case COLUMN_BREAK:
            m_bIsColumnBreakDeferred = false;
            break;
        case PAGE_BREAK:
            m_bIsPageBreakDeferred = false;
            break;
        default:
            break;
        }
    }

    void DomainMapper_Impl::clearDeferredBreaks()
    {
        m_bIsColumnBreakDeferred = false;
        m_bIsPageBreakDeferred = false;
    }

    void DomainMapper_Impl::setSdtEndDeferred(bool bSdtEndDeferred)
    {
        m_bSdtEndDeferred = bSdtEndDeferred;
    }

    bool DomainMapper_Impl::isSdtEndDeferred()
    {
        return m_bSdtEndDeferred;
    }

    void DomainMapper_Impl::setParaSdtEndDeferred(bool bParaSdtEndDeferred)
    {
        m_bParaSdtEndDeferred = bParaSdtEndDeferred;
    }

    bool DomainMapper_Impl::isParaSdtEndDeferred()
    {
        return m_bParaSdtEndDeferred;
    }

    static void lcl_MoveBorderPropertiesToFrame(std::vector<beans::PropertyValue>& rFrameProperties,
        uno::Reference<text::XTextRange> const& xStartTextRange,
        uno::Reference<text::XTextRange> const& xEndTextRange )
    {
        try
        {
            if (!xStartTextRange.is())   //rhbz#1077780
                return;
            uno::Reference<text::XTextCursor> xRangeCursor = xStartTextRange->getText()->createTextCursorByRange( xStartTextRange );
            xRangeCursor->gotoRange( xEndTextRange, true );

            uno::Reference<beans::XPropertySet> xTextRangeProperties(xRangeCursor, uno::UNO_QUERY);
            if(!xTextRangeProperties.is())
                return ;

            static PropertyIds const aBorderProperties[] =
            {
                PROP_LEFT_BORDER,
                PROP_RIGHT_BORDER,
                PROP_TOP_BORDER,
                PROP_BOTTOM_BORDER,
                PROP_LEFT_BORDER_DISTANCE,
                PROP_RIGHT_BORDER_DISTANCE,
                PROP_TOP_BORDER_DISTANCE,
                PROP_BOTTOM_BORDER_DISTANCE
            };

            for( sal_uInt32 nProperty = 0; nProperty < SAL_N_ELEMENTS( aBorderProperties ); ++nProperty)
            {
                OUString sPropertyName = getPropertyName(aBorderProperties[nProperty]);
                beans::PropertyValue aValue;
                aValue.Name = sPropertyName;
                aValue.Value = xTextRangeProperties->getPropertyValue(sPropertyName);
                rFrameProperties.push_back(aValue);
                if( nProperty < 4 )
                    xTextRangeProperties->setPropertyValue( sPropertyName, uno::makeAny(table::BorderLine2()));
            }
        }
        catch( const uno::Exception& )
        {
        }
    }

    static void lcl_AddRangeAndStyle(
        ParagraphPropertiesPtr const & pToBeSavedProperties,
        uno::Reference< text::XTextAppend > const& xTextAppend,
        const PropertyMapPtr& pPropertyMap,
        TextAppendContext const & rAppendContext)
    {
        uno::Reference<text::XParagraphCursor> xParaCursor(
            xTextAppend->createTextCursorByRange( rAppendContext.xInsertPosition.is() ? rAppendContext.xInsertPosition : xTextAppend->getEnd()), uno::UNO_QUERY_THROW );
        pToBeSavedProperties->SetEndingRange(xParaCursor->getStart());
        xParaCursor->gotoStartOfParagraph( false );

        pToBeSavedProperties->SetStartingRange(xParaCursor->getStart());
        if(pPropertyMap)
        {
            boost::optional<PropertyMap::Property> aParaStyle = pPropertyMap->getProperty(PROP_PARA_STYLE_NAME);
            if( aParaStyle )
            {
                OUString sName;
                aParaStyle->second >>= sName;
                pToBeSavedProperties->SetParaStyleName(sName);
            }
        }
    }

    //define some default frame width - 0cm ATM: this allow the frame to be wrapped around the text
    #define DEFAULT_FRAME_MIN_WIDTH 0
    #define DEFAULT_FRAME_MIN_HEIGHT 0
    #define DEFAULT_VALUE 0

    void DomainMapper_Impl::CheckUnregisteredFrameConversion( )
    {
        if (m_aTextAppendStack.empty())
            return;
        TextAppendContext& rAppendContext = m_aTextAppendStack.top();
        // n#779642: ignore fly frame inside table as it could lead to messy situations
        if (!rAppendContext.pLastParagraphProperties.get())
            return;
        if (!rAppendContext.pLastParagraphProperties->IsFrameMode())
            return;
        if (!hasTableManager())
            return;
        if (getTableManager().isInTable())
            return;
        try
        {
            StyleSheetEntryPtr pParaStyle =
                GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(rAppendContext.pLastParagraphProperties->GetParaStyleName());

            std::vector<beans::PropertyValue> aFrameProperties;

            if ( pParaStyle.get( ) )
            {
                const ParagraphProperties* pStyleProperties = dynamic_cast<const ParagraphProperties*>( pParaStyle->pProperties.get() );
                if (!pStyleProperties)
                    return;
                sal_Int32 nWidth =
                    rAppendContext.pLastParagraphProperties->Getw() > 0 ?
                        rAppendContext.pLastParagraphProperties->Getw() :
                        pStyleProperties->Getw();
                bool bAutoWidth = nWidth < 1;
                if( bAutoWidth )
                    nWidth = DEFAULT_FRAME_MIN_WIDTH;
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH), nWidth));

                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HEIGHT),
                    rAppendContext.pLastParagraphProperties->Geth() > 0 ?
                        rAppendContext.pLastParagraphProperties->Geth() :
                        pStyleProperties->Geth() > 0 ? pStyleProperties->Geth() : DEFAULT_FRAME_MIN_HEIGHT));

                sal_Int16 nhRule = sal_Int16(
                    rAppendContext.pLastParagraphProperties->GethRule() >= 0 ?
                        rAppendContext.pLastParagraphProperties->GethRule() :
                        pStyleProperties->GethRule());
                if ( nhRule < 0 )
                {
                    if ( rAppendContext.pLastParagraphProperties->Geth() >= 0 ||
                        pStyleProperties->GethRule() >= 0 )
                    {
                        // [MS-OE376] Word uses a default value of "atLeast" for
                        // this attribute when the value of the h attribute is not 0.
                        nhRule = text::SizeType::MIN;
                    }
                    else
                    {
                        nhRule = text::SizeType::VARIABLE;
                    }
                }
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE), nhRule));

                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE), bAutoWidth ?  text::SizeType::MIN : text::SizeType::FIX));

                sal_Int16 nHoriOrient = sal_Int16(
                    rAppendContext.pLastParagraphProperties->GetxAlign() >= 0 ?
                        rAppendContext.pLastParagraphProperties->GetxAlign() :
                        pStyleProperties->GetxAlign() >= 0 ? pStyleProperties->GetxAlign() : text::HoriOrientation::NONE );
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT), nHoriOrient));

                //set a non negative default value
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_POSITION),
                    rAppendContext.pLastParagraphProperties->IsxValid() ?
                        rAppendContext.pLastParagraphProperties->Getx() :
                        pStyleProperties->IsxValid() ? pStyleProperties->Getx() : DEFAULT_VALUE));

                //Default the anchor in case FramePr_hAnchor is missing ECMA 17.3.1.11
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_RELATION), sal_Int16(
                    rAppendContext.pLastParagraphProperties->GethAnchor() >= 0 ?
                        rAppendContext.pLastParagraphProperties->GethAnchor() :
                    pStyleProperties->GethAnchor() >=0 ? pStyleProperties->GethAnchor() : text::RelOrientation::FRAME )));

                sal_Int16 nVertOrient = sal_Int16(
                    rAppendContext.pLastParagraphProperties->GetyAlign() >= 0 ?
                        rAppendContext.pLastParagraphProperties->GetyAlign() :
                        pStyleProperties->GetyAlign() >= 0 ? pStyleProperties->GetyAlign() : text::VertOrientation::NONE );
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT), nVertOrient));

                //set a non negative default value
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_POSITION),
                    rAppendContext.pLastParagraphProperties->IsyValid() ?
                        rAppendContext.pLastParagraphProperties->Gety() :
                        pStyleProperties->IsyValid() ? pStyleProperties->Gety() : DEFAULT_VALUE));

                //Default the anchor in case FramePr_vAnchor is missing ECMA 17.3.1.11
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_RELATION), sal_Int16(
                    rAppendContext.pLastParagraphProperties->GetvAnchor() >= 0 ?
                        rAppendContext.pLastParagraphProperties->GetvAnchor() :
                        pStyleProperties->GetvAnchor() >= 0 ? pStyleProperties->GetvAnchor() : text::RelOrientation::FRAME )));

                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SURROUND),
                    rAppendContext.pLastParagraphProperties->GetWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
                    ? rAppendContext.pLastParagraphProperties->GetWrap()
                    : pStyleProperties->GetWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE
                      ? pStyleProperties->GetWrap()
                      : text::WrapTextMode_NONE ));

                /** FDO#73546 : distL & distR should be unsigned integers <Ecma 20.4.3.6>
                    Swapped the array elements 11,12 & 13,14 since 11 & 12 are
                    LEFT & RIGHT margins and 13,14 are TOP and BOTTOM margins respectively.
                */
                sal_Int32 nRightDist;
                sal_Int32 nLeftDist = nRightDist =
                    rAppendContext.pLastParagraphProperties->GethSpace() >= 0 ?
                    rAppendContext.pLastParagraphProperties->GethSpace() :
                    pStyleProperties->GethSpace() >= 0 ? pStyleProperties->GethSpace() : 0;

                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_LEFT_MARGIN), nHoriOrient == text::HoriOrientation::LEFT ? 0 : nLeftDist));
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_RIGHT_MARGIN), nHoriOrient == text::HoriOrientation::RIGHT ? 0 : nRightDist));

                sal_Int32 nBottomDist;
                sal_Int32 nTopDist = nBottomDist =
                    rAppendContext.pLastParagraphProperties->GetvSpace() >= 0 ?
                    rAppendContext.pLastParagraphProperties->GetvSpace() :
                    pStyleProperties->GetvSpace() >= 0 ? pStyleProperties->GetvSpace() : 0;

                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_TOP_MARGIN), nVertOrient == text::VertOrientation::TOP ? 0 : nTopDist));
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_BOTTOM_MARGIN), nVertOrient == text::VertOrientation::BOTTOM ? 0 : nBottomDist));
                // If there is no fill, the Word default is 100% transparency.
                // Otherwise CellColorHandler has priority, and this setting
                // will be ignored.
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_BACK_COLOR_TRANSPARENCY), sal_Int32(100)));

                uno::Sequence<beans::PropertyValue> aGrabBag( comphelper::InitPropertySequence({
                        { "ParaFrameProperties", uno::Any(rAppendContext.pLastParagraphProperties->IsFrameMode()) }
                }));
                aFrameProperties.push_back(comphelper::makePropertyValue("FrameInteropGrabBag", aGrabBag));

                lcl_MoveBorderPropertiesToFrame(aFrameProperties,
                    rAppendContext.pLastParagraphProperties->GetStartingRange(),
                    rAppendContext.pLastParagraphProperties->GetEndingRange());
            }
            else
            {
                sal_Int32 nWidth = rAppendContext.pLastParagraphProperties->Getw();
                bool bAutoWidth = nWidth < 1;
                if( bAutoWidth )
                    nWidth = DEFAULT_FRAME_MIN_WIDTH;
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH), nWidth));

                sal_Int16 nhRule = sal_Int16(rAppendContext.pLastParagraphProperties->GethRule());
                if ( nhRule < 0 )
                {
                    if ( rAppendContext.pLastParagraphProperties->Geth() >= 0 )
                    {
                        // [MS-OE376] Word uses a default value of atLeast for
                        // this attribute when the value of the h attribute is not 0.
                        nhRule = text::SizeType::MIN;
                    }
                    else
                    {
                        nhRule = text::SizeType::VARIABLE;
                    }
                }
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_SIZE_TYPE), nhRule));

                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_WIDTH_TYPE), bAutoWidth ?  text::SizeType::MIN : text::SizeType::FIX));

                sal_Int16 nHoriOrient = sal_Int16(
                    rAppendContext.pLastParagraphProperties->GetxAlign() >= 0 ?
                        rAppendContext.pLastParagraphProperties->GetxAlign() :
                        text::HoriOrientation::NONE );
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT), nHoriOrient));

                sal_Int16 nVertOrient = sal_Int16(
                    rAppendContext.pLastParagraphProperties->GetyAlign() >= 0 ?
                        rAppendContext.pLastParagraphProperties->GetyAlign() :
                        text::VertOrientation::NONE );
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT), nVertOrient));

                sal_Int32 nVertDist = rAppendContext.pLastParagraphProperties->GethSpace();
                if( nVertDist < 0 )
                    nVertDist = 0;
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_LEFT_MARGIN), nVertOrient == text::VertOrientation::TOP ? 0 : nVertDist));
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_RIGHT_MARGIN), nVertOrient == text::VertOrientation::BOTTOM ? 0 : nVertDist));

                sal_Int32 nHoriDist = rAppendContext.pLastParagraphProperties->GetvSpace();
                if( nHoriDist < 0 )
                    nHoriDist = 0;
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_TOP_MARGIN), nHoriOrient == text::HoriOrientation::LEFT ? 0 : nHoriDist));
                aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_BOTTOM_MARGIN), nHoriOrient == text::HoriOrientation::RIGHT ? 0 : nHoriDist));

                if( rAppendContext.pLastParagraphProperties->Geth() > 0 )
                    aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HEIGHT), rAppendContext.pLastParagraphProperties->Geth()));

                if( rAppendContext.pLastParagraphProperties->IsxValid() )
                    aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_HORI_ORIENT_POSITION), rAppendContext.pLastParagraphProperties->Getx()));

                if( rAppendContext.pLastParagraphProperties->GethAnchor() >= 0 )
                    aFrameProperties.push_back(comphelper::makePropertyValue("HoriOrientRelation", sal_Int16(rAppendContext.pLastParagraphProperties->GethAnchor())));

                if( rAppendContext.pLastParagraphProperties->IsyValid() )
                    aFrameProperties.push_back(comphelper::makePropertyValue(getPropertyName(PROP_VERT_ORIENT_POSITION), rAppendContext.pLastParagraphProperties->Gety()));

                if( rAppendContext.pLastParagraphProperties->GetvAnchor() >= 0 )
                    aFrameProperties.push_back(comphelper::makePropertyValue("VertOrientRelation", sal_Int16(rAppendContext.pLastParagraphProperties->GetvAnchor())));

                if( rAppendContext.pLastParagraphProperties->GetWrap() >= text::WrapTextMode_NONE )
                    aFrameProperties.push_back(comphelper::makePropertyValue("Surround", rAppendContext.pLastParagraphProperties->GetWrap()));

                lcl_MoveBorderPropertiesToFrame(aFrameProperties,
                    rAppendContext.pLastParagraphProperties->GetStartingRange(),
                    rAppendContext.pLastParagraphProperties->GetEndingRange());
            }

            //frame conversion has to be executed after table conversion
            RegisterFrameConversion(
                rAppendContext.pLastParagraphProperties->GetStartingRange(),
                rAppendContext.pLastParagraphProperties->GetEndingRange(),
                aFrameProperties );
        }
        catch( const uno::Exception& )
        {
        }
    }

    /// Check if the style or its parent has a list id, recursively.
    static sal_Int32 lcl_getListId(const StyleSheetEntryPtr& rEntry, const StyleSheetTablePtr& rStyleTable)
    {
        const StyleSheetPropertyMap* pEntryProperties = dynamic_cast<const StyleSheetPropertyMap*>(rEntry->pProperties.get());
        if (!pEntryProperties)
            return -1;

        sal_Int32 nListId = pEntryProperties->GetListId();
        // The style itself has a list id.
        if (nListId >= 0)
            return nListId;

        // The style has no parent.
        if (rEntry->sBaseStyleIdentifier.isEmpty())
            return -1;

        const StyleSheetEntryPtr pParent = rStyleTable->FindStyleSheetByISTD(rEntry->sBaseStyleIdentifier);
        // No such parent style or loop in the style hierarchy.
        if (!pParent || pParent == rEntry)
            return -1;

        return lcl_getListId(pParent, rStyleTable);
    }

    void DomainMapper_Impl::finishParagraph( const PropertyMapPtr& pPropertyMap, const bool bRemove )
    {
        if (m_bDiscardHeaderFooter)
            return;
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement("finishParagraph");
    #endif

        m_nLastTableCellParagraphDepth = m_nTableCellDepth;
        ParagraphPropertyMap* pParaContext = dynamic_cast< ParagraphPropertyMap* >( pPropertyMap.get() );
        if (m_aTextAppendStack.empty())
            return;
        TextAppendContext& rAppendContext = m_aTextAppendStack.top();
        uno::Reference< text::XTextAppend > xTextAppend(rAppendContext.xTextAppend);
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().attribute("isTextAppend", sal_uInt32(xTextAppend.is()));
    #endif

        const StyleSheetEntryPtr pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( GetCurrentParaStyleName() );
        OSL_ENSURE( pEntry.get(), "no style sheet found" );
        const StyleSheetPropertyMap* pStyleSheetProperties = dynamic_cast<const StyleSheetPropertyMap*>(pEntry ? pEntry->pProperties.get() : nullptr);
        //apply numbering to paragraph if it was set at the style, but only if the paragraph itself
        //does not specify the numbering
        if ( !bRemove && pStyleSheetProperties && pParaContext && !pParaContext->isSet(PROP_NUMBERING_RULES) )
        {

            sal_Int32 nListId = pEntry ? lcl_getListId(pEntry, GetStyleSheetTable()) : -1;
            if ( nListId >= 0 )
            {
                pParaContext->Insert( PROP_NUMBERING_STYLE_NAME, uno::makeAny( ListDef::GetStyleName( nListId ) ), false);

                // Indent properties from the paragraph style have priority
                // over the ones from the numbering styles in Word
                // but in Writer numbering styles have priority,
                // so insert directly into the paragraph properties to compensate.
                boost::optional<PropertyMap::Property> oProperty;
                if ( (oProperty = pStyleSheetProperties->getProperty(PROP_PARA_FIRST_LINE_INDENT)) )
                    pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, oProperty->second, /*bOverwrite=*/false);
                if ( (oProperty = pStyleSheetProperties->getProperty(PROP_PARA_LEFT_MARGIN)) )
                    pParaContext->Insert(PROP_PARA_LEFT_MARGIN, oProperty->second, /*bOverwrite=*/false);

                // We're inheriting properties from a numbering style. Make sure a possible right margin is inherited from the base style.
                sal_Int32 nParaRightMargin = 0;
                if (!pEntry->sBaseStyleIdentifier.isEmpty())
                {
                    const StyleSheetEntryPtr pParent = GetStyleSheetTable()->FindStyleSheetByISTD(pEntry->sBaseStyleIdentifier);
                    const StyleSheetPropertyMap* pParentProperties = dynamic_cast<const StyleSheetPropertyMap*>(pParent ? pParent->pProperties.get() : nullptr);
                    boost::optional<PropertyMap::Property> pPropMargin;
                    if (pParentProperties && (pPropMargin = pParentProperties->getProperty(PROP_PARA_RIGHT_MARGIN)) )
                        nParaRightMargin = pPropMargin->second.get<sal_Int32>();
                }
                if (nParaRightMargin != 0)
                {
                    // If we're setting the right margin, we should set the first / left margin as well from the numbering style.
                    const sal_Int32 nFirstLineIndent = getNumberingProperty(nListId, pStyleSheetProperties->GetListLevel(), "FirstLineIndent");
                    const sal_Int32 nParaLeftMargin  = getNumberingProperty(nListId, pStyleSheetProperties->GetListLevel(), "IndentAt");
                    if (nFirstLineIndent != 0)
                        pParaContext->Insert(PROP_PARA_FIRST_LINE_INDENT, uno::makeAny(nFirstLineIndent), /*bOverwrite=*/false);
                    if (nParaLeftMargin != 0)
                        pParaContext->Insert(PROP_PARA_LEFT_MARGIN, uno::makeAny(nParaLeftMargin), /*bOverwrite=*/false);

                    pParaContext->Insert(PROP_PARA_RIGHT_MARGIN, uno::makeAny(nParaRightMargin), /*bOverwrite=*/false);
                }
            }

            if ( pStyleSheetProperties->GetListLevel() >= 0 )
                pParaContext->Insert( PROP_NUMBERING_LEVEL, uno::makeAny(pStyleSheetProperties->GetListLevel()), false);
        }

        // apply AutoSpacing: it has priority over all other margin settings
        // (note that numbering with autoSpacing is handled separately later on)
        const bool bAllowAdjustments = !GetSettingsTable()->GetDoNotUseHTMLParagraphAutoSpacing();
        sal_Int32 nBeforeAutospacing = -1;
        bool bIsAutoSet = pParaContext && pParaContext->isSet(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING);
        // apply INHERITED autospacing only if top margin is not set
        if ( bIsAutoSet || (pParaContext && !pParaContext->isSet(PROP_PARA_TOP_MARGIN)) )
            GetAnyProperty(PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING, pPropertyMap) >>= nBeforeAutospacing;
        if ( nBeforeAutospacing > -1 && pParaContext )
        {
            if ( bAllowAdjustments )
            {
                if ( GetIsFirstParagraphInShape() ||
                     (GetIsFirstParagraphInSection() && GetSectionContext() && GetSectionContext()->IsFirstSection()) ||
                     (m_bFirstParagraphInCell && m_nTableDepth > 0 && m_nTableDepth == m_nTableCellDepth) )
                {
                    nBeforeAutospacing = 0;
                    // export requires grabbag to match top_margin, so keep them in sync
                    if ( bIsAutoSet )
                        pParaContext->Insert( PROP_PARA_TOP_MARGIN_BEFORE_AUTO_SPACING, uno::makeAny( sal_Int32(0) ),true, PARA_GRAB_BAG );
                }
            }
            pParaContext->Insert(PROP_PARA_TOP_MARGIN, uno::makeAny(nBeforeAutospacing));
        }

        sal_Int32 nAfterAutospacing = -1;
        bIsAutoSet = pParaContext && pParaContext->isSet(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING);
        bool bApplyAutospacing = bIsAutoSet || (pParaContext && !pParaContext->isSet(PROP_PARA_BOTTOM_MARGIN));
        if ( bApplyAutospacing )
            GetAnyProperty(PROP_PARA_BOTTOM_MARGIN_AFTER_AUTO_SPACING, pPropertyMap) >>= nAfterAutospacing;
        if ( nAfterAutospacing > -1 && pParaContext )
        {
            pParaContext->Insert(PROP_PARA_BOTTOM_MARGIN, uno::makeAny(nAfterAutospacing));
            bApplyAutospacing = bAllowAdjustments;
        }
        else
            bApplyAutospacing = false;

        // tell TableManager to reset the bottom margin if it determines that this is the cell's last paragraph.
        if ( hasTableManager() && getTableManager().isInCell() )
            getTableManager().setCellLastParaAfterAutospacing( bApplyAutospacing );

        if (xTextAppend.is() && pParaContext && hasTableManager() && !getTableManager().isIgnore())
        {
            try
            {
                /*the following combinations of previous and current frame settings can occur:
                    (1) - no old frame and no current frame     -> no special action
                    (2) - no old frame and current DropCap      -> save DropCap for later use, don't call finishParagraph
                                                                remove character properties of the DropCap?
                    (3) - no old frame and current Frame        -> save Frame for later use
                    (4) - old DropCap and no current frame      -> add DropCap to the properties of the finished paragraph, delete previous setting
                    (5) - old DropCap and current frame         -> add DropCap to the properties of the finished paragraph, save current frame settings
                    (6) - old Frame and new DropCap             -> add old Frame, save DropCap for later use
                    (7) - old Frame and new same Frame          -> continue
                    (8) - old Frame and new different Frame     -> add old Frame, save new Frame for later use
                    (9) - old Frame and no current frame        -> add old Frame, delete previous settings

                  old _and_ new DropCap must not occur
                 */

                bool bIsDropCap =
                    pParaContext->IsFrameMode() &&
                    sal::static_int_cast<Id>(pParaContext->GetDropCap()) != NS_ooxml::LN_Value_doc_ST_DropCap_none;

                style::DropCapFormat aDrop;
                ParagraphPropertiesPtr pToBeSavedProperties;
                bool bKeepLastParagraphProperties = false;
                if( bIsDropCap )
                {
                    uno::Reference<text::XParagraphCursor> xParaCursor(
                        xTextAppend->createTextCursorByRange(xTextAppend->getEnd()), uno::UNO_QUERY_THROW);
                    //select paragraph
                    xParaCursor->gotoStartOfParagraph( true );
                    uno::Reference< beans::XPropertyState > xParaProperties( xParaCursor, uno::UNO_QUERY_THROW );
                    xParaProperties->setPropertyToDefault(getPropertyName(PROP_CHAR_ESCAPEMENT));
                    xParaProperties->setPropertyToDefault(getPropertyName(PROP_CHAR_HEIGHT));
                    //handles (2) and part of (6)
                    pToBeSavedProperties = new ParagraphProperties(*pParaContext);
                    sal_Int32 nCount = xParaCursor->getString().getLength();
                    pToBeSavedProperties->SetDropCapLength(nCount > 0 && nCount < 255 ? static_cast<sal_Int8>(nCount) : 1);
                }
                if( rAppendContext.pLastParagraphProperties.get() )
                {
                    if( sal::static_int_cast<Id>(rAppendContext.pLastParagraphProperties->GetDropCap()) != NS_ooxml::LN_Value_doc_ST_DropCap_none)
                    {
                        //handles (4) and part of (5)
                        //create a DropCap property, add it to the property sequence of finishParagraph
                        sal_Int32 nLines = rAppendContext.pLastParagraphProperties->GetLines();
                        aDrop.Lines = nLines > 0 && nLines < SAL_MAX_INT8 ? static_cast<sal_Int8>(nLines) : 2;
                        aDrop.Count = rAppendContext.pLastParagraphProperties->GetDropCapLength();
                        sal_Int32 nHSpace = rAppendContext.pLastParagraphProperties->GethSpace();
                        aDrop.Distance  = nHSpace > 0 && nHSpace < SAL_MAX_INT16 ? static_cast<sal_Int16>(nHSpace) : 0;
                        //completes (5)
                        if( pParaContext->IsFrameMode() )
                            pToBeSavedProperties = new ParagraphProperties(*pParaContext);
                    }
                    else if(*rAppendContext.pLastParagraphProperties == *pParaContext )
                    {
                        //handles (7)
                        rAppendContext.pLastParagraphProperties->SetEndingRange(rAppendContext.xInsertPosition.is() ? rAppendContext.xInsertPosition : xTextAppend->getEnd());
                        bKeepLastParagraphProperties = true;
                    }
                    else
                    {
                        //handles (8)(9) and completes (6)
                        CheckUnregisteredFrameConversion( );

                        // If different frame properties are set on this paragraph, keep them.
                        if ( !bIsDropCap && pParaContext->IsFrameMode() )
                        {
                            pToBeSavedProperties = new ParagraphProperties(*pParaContext);
                            lcl_AddRangeAndStyle(pToBeSavedProperties, xTextAppend, pPropertyMap, rAppendContext);
                        }
                    }
                }
                else
                {
                    // (1) doesn't need handling

                    if( !bIsDropCap && pParaContext->IsFrameMode() )
                    {
                        pToBeSavedProperties = new ParagraphProperties(*pParaContext);
                        lcl_AddRangeAndStyle(pToBeSavedProperties, xTextAppend, pPropertyMap, rAppendContext);
                    }
                }
                std::vector<beans::PropertyValue> aProperties;
                if (pPropertyMap.get())
                    aProperties = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(pPropertyMap->GetPropertyValues());
                if( !bIsDropCap )
                {
                    if( aDrop.Lines > 1 )
                    {
                        beans::PropertyValue aValue;
                        aValue.Name = getPropertyName(PROP_DROP_CAP_FORMAT);
                        aValue.Value <<= aDrop;
                        aProperties.push_back(aValue);
                    }
                    uno::Reference< text::XTextRange > xTextRange;
                    if (rAppendContext.xInsertPosition.is())
                    {
                        xTextRange = xTextAppend->finishParagraphInsert( comphelper::containerToSequence(aProperties), rAppendContext.xInsertPosition );
                        rAppendContext.xCursor->gotoNextParagraph(false);
                        if (rAppendContext.pLastParagraphProperties.get())
                            rAppendContext.pLastParagraphProperties->SetEndingRange(xTextRange->getEnd());
                    }
                    else
                    {
                        uno::Reference<text::XTextCursor> xCursor;
                        if (m_bParaHadField && !m_bIsInComments && !xTOCMarkerCursor.is())
                        {
                            // Workaround to make sure char props of the field are not lost.
                            // Not relevant for editeng-based comments.
                            // Not relevant for fields inside a TOC field.
                            OUString const sMarker("X");
                            xCursor = xTextAppend->getText()->createTextCursor();
                            if (xCursor.is())
                                xCursor->gotoEnd(false);
                            PropertyMapPtr pEmpty(new PropertyMap());
                            appendTextPortion(sMarker, pEmpty);
                        }

                        // Check if top / bottom margin has to be updated, now that we know the numbering status of both the previous and
                        // the current text node.
                        auto itNumberingRules = std::find_if(aProperties.begin(), aProperties.end(), [](const beans::PropertyValue& rValue)
                        {
                            return rValue.Name == "NumberingRules";
                        });
                        if (itNumberingRules != aProperties.end())
                        {
                            // This textnode has numbering. Look up the numbering style name of the current and previous paragraph.
                            OUString aCurrentNumberingRuleName;
                            uno::Reference<container::XNamed> xCurrentNumberingRules(itNumberingRules->Value, uno::UNO_QUERY);
                            if (xCurrentNumberingRules.is())
                                aCurrentNumberingRuleName = xCurrentNumberingRules->getName();
                            OUString aPreviousNumberingRuleName;
                            if (m_xPreviousParagraph.is())
                            {
                                uno::Reference<container::XNamed> xPreviousNumberingRules(m_xPreviousParagraph->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
                                if (xPreviousNumberingRules.is())
                                    aPreviousNumberingRuleName = xPreviousNumberingRules->getName();
                            }

                            if (!aPreviousNumberingRuleName.isEmpty() && aCurrentNumberingRuleName == aPreviousNumberingRuleName)
                            {
                                // There was a previous textnode and it had the same numbering.
                                if (m_bParaAutoBefore)
                                {
                                    // This before spacing is set to auto, set before space to 0.
                                    auto itParaTopMargin = std::find_if(aProperties.begin(), aProperties.end(), [](const beans::PropertyValue& rValue)
                                    {
                                        return rValue.Name == "ParaTopMargin";
                                    });
                                    if (itParaTopMargin != aProperties.end())
                                        itParaTopMargin->Value <<= static_cast<sal_Int32>(0);
                                    else
                                        aProperties.push_back(comphelper::makePropertyValue("ParaTopMargin", static_cast<sal_Int32>(0)));
                                }
                                uno::Sequence<beans::PropertyValue> aPrevPropertiesSeq;
                                m_xPreviousParagraph->getPropertyValue("ParaInteropGrabBag") >>= aPrevPropertiesSeq;
                                auto aPrevProperties = comphelper::sequenceToContainer< std::vector<beans::PropertyValue> >(aPrevPropertiesSeq);
                                bool bPrevParaAutoAfter = std::any_of(aPrevProperties.begin(), aPrevProperties.end(), [](const beans::PropertyValue& rValue)
                                {
                                    return rValue.Name == "ParaBottomMarginAfterAutoSpacing";
                                });
                                if (bPrevParaAutoAfter)
                                {
                                    // Previous after spacing is set to auto, set previous after space to 0.
                                    m_xPreviousParagraph->setPropertyValue("ParaBottomMargin", uno::makeAny(static_cast<sal_Int32>(0)));
                                }
                            }
                        }

                        xTextRange = xTextAppend->finishParagraph( comphelper::containerToSequence(aProperties) );
                        m_xPreviousParagraph.set(xTextRange, uno::UNO_QUERY);

                        if (!rAppendContext.m_aAnchoredObjects.empty() && !IsInHeaderFooter())
                        {
                            // Remember what objects are anchored to this paragraph.
                            // That list is only used for Word compat purposes, and
                            // it is only relevant for body text.
                            AnchoredObjectInfo aInfo;
                            aInfo.m_xParagraph = xTextRange;
                            aInfo.m_aAnchoredObjects = rAppendContext.m_aAnchoredObjects;
                            m_aAnchoredObjectAnchors.push_back(aInfo);
                            rAppendContext.m_aAnchoredObjects.clear();
                        }

                        // We're no longer right after a table conversion.
                        m_bConvertedTable = false;

                        if (xCursor.is())
                        {
                            xCursor->goLeft(1, true);
                            xCursor->setString(OUString());
                        }
                    }
                    getTableManager( ).handle(xTextRange);
                    m_aSmartTagHandler.handle(xTextRange);

                    if (xTextRange.is())
                    {
                        // Get the end of paragraph character inserted
                        uno::Reference< text::XTextCursor > xCur = xTextRange->getText( )->createTextCursor( );
                        if (rAppendContext.xInsertPosition.is())
                            xCur->gotoRange( rAppendContext.xInsertPosition, false );
                        else
                            xCur->gotoEnd( false );
                        xCur->goLeft( 1 , true );
                        uno::Reference< text::XTextRange > xParaEnd( xCur, uno::UNO_QUERY );
                        CheckParaMarkerRedline( xParaEnd );
                    }

                    // tdf#118521 set paragraph top or bottom margin based on the paragraph style
                    // if we already set the other margin with direct formatting
                    if ( pParaContext && m_xPreviousParagraph.is() )
                    {
                        const bool bTopSet = pParaContext->isSet(PROP_PARA_TOP_MARGIN);
                        const bool bBottomSet = pParaContext->isSet(PROP_PARA_BOTTOM_MARGIN);
                        const bool bContextSet = pParaContext->isSet(PROP_PARA_CONTEXT_MARGIN);
                        if ( !(bTopSet == bBottomSet && bBottomSet == bContextSet) )
                        {
                            if ( !bTopSet )
                            {
                                uno::Any aMargin = GetPropertyFromStyleSheet(PROP_PARA_TOP_MARGIN);
                                if ( aMargin != uno::Any() )
                                    m_xPreviousParagraph->setPropertyValue("ParaTopMargin", aMargin);
                            }
                            if ( !bBottomSet )
                            {
                                uno::Any aMargin = GetPropertyFromStyleSheet(PROP_PARA_BOTTOM_MARGIN);
                                if ( aMargin != uno::Any() )
                                    m_xPreviousParagraph->setPropertyValue("ParaBottomMargin", aMargin);
                            }
                            if ( !bContextSet )
                            {
                                uno::Any aMargin = GetPropertyFromStyleSheet(PROP_PARA_CONTEXT_MARGIN);
                                if ( aMargin != uno::Any() )
                                    m_xPreviousParagraph->setPropertyValue("ParaContextMargin", aMargin);
                            }
                        }
                    }

                    // Left, Right, and Hanging settings are also grouped. Ensure that all or none are set.
                    // m_xPreviousParagraph was set earlier, so really it still is the current paragraph...
                    if ( pParaContext && m_xPreviousParagraph.is() )
                    {
                        const bool bLeftSet  = pParaContext->isSet(PROP_PARA_LEFT_MARGIN);
                        const bool bRightSet = pParaContext->isSet(PROP_PARA_RIGHT_MARGIN);
                        const bool bFirstSet = pParaContext->isSet(PROP_PARA_FIRST_LINE_INDENT);
                        if ( !(bLeftSet == bRightSet && bRightSet == bFirstSet) )
                        {
                            if ( !bLeftSet )
                            {
                                uno::Any aMargin = GetPropertyFromStyleSheet(PROP_PARA_LEFT_MARGIN);
                                if ( aMargin != uno::Any() )
                                    m_xPreviousParagraph->setPropertyValue("ParaLeftMargin", aMargin);
                            }
                            if ( !bRightSet )
                            {
                                uno::Any aMargin = GetPropertyFromStyleSheet(PROP_PARA_RIGHT_MARGIN);
                                if ( aMargin != uno::Any() )
                                    m_xPreviousParagraph->setPropertyValue("ParaRightMargin", aMargin);
                            }
                            if ( !bFirstSet )
                            {
                                uno::Any aMargin = GetPropertyFromStyleSheet(PROP_PARA_FIRST_LINE_INDENT);
                                if ( aMargin != uno::Any() )
                                    m_xPreviousParagraph->setPropertyValue("ParaFirstLineIndent", aMargin);
                            }
                        }
                    }
                }
                if( !bKeepLastParagraphProperties )
                    rAppendContext.pLastParagraphProperties = pToBeSavedProperties;
            }
            catch(const lang::IllegalArgumentException&)
            {
                OSL_FAIL( "IllegalArgumentException in DomainMapper_Impl::finishParagraph" );
            }
            catch(const uno::Exception& e)
            {
                SAL_WARN( "writerfilter.dmapper", "finishParagraph() " << e );
            }

        }

        bool bIgnoreFrameState = IsInHeaderFooter();
        if( (!bIgnoreFrameState && pParaContext && pParaContext->IsFrameMode()) || (bIgnoreFrameState && GetIsPreviousParagraphFramed()) )
            SetIsPreviousParagraphFramed(true);
        else
            SetIsPreviousParagraphFramed(false);

        m_bParaChanged = false;
        if( !IsInHeaderFooter() && !IsInShape() && (!pParaContext || !pParaContext->IsFrameMode()) )
        { // If the paragraph is in a frame, shape or header/footer, it's not a paragraph of the section itself.
            SetIsFirstParagraphInSection(false);
            SetIsLastParagraphInSection(false);
        }

        if (m_bIsFirstParaInShape)
            m_bIsFirstParaInShape = false;

        if (pParaContext)
        {
            // Reset the frame properties for the next paragraph
            pParaContext->ResetFrameProperties();
        }

        SetIsOutsideAParagraph(true);
        m_bParaHadField = false;

        // don't overwrite m_bFirstParagraphInCell in table separator nodes
        if (m_nTableDepth > 0 && m_nTableDepth == m_nTableCellDepth)
            m_bFirstParagraphInCell = false;

        m_bParaAutoBefore = false;

    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().endElement();
    #endif

    }

    void DomainMapper_Impl::appendTextPortion( const OUString& rString, const PropertyMapPtr& pPropertyMap )
    {
        if (m_bDiscardHeaderFooter)
            return;

        if (m_aTextAppendStack.empty())
            return;
        // Before placing call to processDeferredCharacterProperties(), TopContextType should be CONTEXT_CHARACTER
        // processDeferredCharacterProperties() invokes only if character inserted
        if( pPropertyMap == m_pTopContext && !deferredCharacterProperties.empty() && (GetTopContextType() == CONTEXT_CHARACTER) )
            processDeferredCharacterProperties();
        uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
        if (xTextAppend.is() && hasTableManager() && !getTableManager().isIgnore())
        {
            try
            {
                // If we are in comments, then disable CharGrabBag, comment text doesn't support that.
                uno::Sequence< beans::PropertyValue > aValues = pPropertyMap->GetPropertyValues(/*bCharGrabBag=*/!m_bIsInComments);
                sal_Int32 len = aValues.getLength();

                if (m_bStartTOC || m_bStartIndex || m_bStartBibliography)
                    for( int i =0; i < len; ++i )
                    {
                        if (aValues[i].Name == "CharHidden")
                            aValues[i].Value <<= false;
                    }

                uno::Reference< text::XTextRange > xTextRange;
                if (m_aTextAppendStack.top().xInsertPosition.is())
                {
                    xTextRange = xTextAppend->insertTextPortion(rString, aValues, m_aTextAppendStack.top().xInsertPosition);
                    m_aTextAppendStack.top().xCursor->gotoRange(xTextRange->getEnd(), true);
                }
                else
                {
                    if (m_bStartTOC || m_bStartIndex || m_bStartBibliography || m_bStartGenericField)
                    {
                        if(m_bInHeaderFooterImport && !m_bStartTOCHeaderFooter)
                        {
                            xTextRange = xTextAppend->appendTextPortion(rString, aValues);
                        }
                        else
                        {
                            m_bStartedTOC = true;
                            uno::Reference< text::XTextCursor > xTOCTextCursor;
                            xTOCTextCursor = xTextAppend->getEnd()->getText( )->createTextCursor( );
                            xTOCTextCursor->gotoEnd(false);
                            if (xTOCTextCursor.is())
                            {
                                if (m_bStartIndex || m_bStartBibliography || m_bStartGenericField)
                                    xTOCTextCursor->goLeft(1, false);
                                xTextRange = xTextAppend->insertTextPortion(rString, aValues, xTOCTextCursor);
                                SAL_WARN_IF(!xTextRange.is(), "writerfilter.dmapper", "insertTextPortion failed");
                                if (!xTextRange.is())
                                    throw uno::Exception("insertTextPortion failed", nullptr);
                                m_bTextInserted = true;
                                xTOCTextCursor->gotoRange(xTextRange->getEnd(), true);
                                mxTOCTextCursor = xTOCTextCursor;
                            }
                            else
                            {
                                xTextRange = xTextAppend->appendTextPortion(rString, aValues);
                                xTOCTextCursor = xTextAppend->createTextCursor();
                                xTOCTextCursor->gotoRange(xTextRange->getEnd(), false);
                            }
                            m_aTextAppendStack.push(TextAppendContext(xTextAppend, xTOCTextCursor));
                        }
                    }
                    else
                    {
                        if (IsOpenField() && GetTopFieldContext()->GetFieldId() == FIELD_HYPERLINK)
                        {
                            // It is content of hyperlink field. We need to create and remember
                            // character style for later applying to hyperlink
                            PropertyValueVector_t aProps = comphelper::sequenceToContainer< PropertyValueVector_t >(GetTopContext()->GetPropertyValues());
                            OUString sHyperlinkStyleName = GetStyleSheetTable()->getOrCreateCharStyle(aProps, /*bAlwaysCreate=*/false);
                            GetTopFieldContext()->SetHyperlinkStyle(sHyperlinkStyleName);
                        }

                        xTextRange = xTextAppend->appendTextPortion(rString, aValues);
                    }
                }

                CheckRedline( xTextRange );
                m_bParaChanged = true;

                //getTableManager( ).handle(xTextRange);
            }
            catch(const lang::IllegalArgumentException&)
            {
                OSL_FAIL( "IllegalArgumentException in DomainMapper_Impl::appendTextPortion" );
            }
            catch(const uno::Exception&)
            {
                OSL_FAIL( "Exception in DomainMapper_Impl::appendTextPortion" );
            }
        }
    }

    void DomainMapper_Impl::appendTextContent(
        const uno::Reference< text::XTextContent >& xContent,
        const uno::Sequence< beans::PropertyValue >& xPropertyValues
        )
    {
        SAL_WARN_IF(m_aTextAppendStack.empty(), "writerfilter.dmapper", "no text append stack");
        if (m_aTextAppendStack.empty())
            return;
        uno::Reference< text::XTextAppendAndConvert >  xTextAppendAndConvert( m_aTextAppendStack.top().xTextAppend, uno::UNO_QUERY );
        OSL_ENSURE( xTextAppendAndConvert.is(), "trying to append a text content without XTextAppendAndConvert" );
        if (xTextAppendAndConvert.is() && hasTableManager() && !getTableManager().isIgnore())
        {
            try
            {
                if (m_aTextAppendStack.top().xInsertPosition.is())
                    xTextAppendAndConvert->insertTextContentWithProperties( xContent, xPropertyValues, m_aTextAppendStack.top().xInsertPosition );
                else
                    xTextAppendAndConvert->appendTextContent( xContent, xPropertyValues );
            }
            catch(const lang::IllegalArgumentException&)
            {
            }
            catch(const uno::Exception&)
            {
            }
        }
    }

    void DomainMapper_Impl::appendOLE( const OUString& rStreamName, const std::shared_ptr<OLEHandler>& pOLEHandler )
    {
        try
        {
            uno::Reference< text::XTextContent > xOLE( m_xTextFactory->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW );
            uno::Reference< beans::XPropertySet > xOLEProperties(xOLE, uno::UNO_QUERY_THROW);

            OUString aCLSID = pOLEHandler->getCLSID(m_xComponentContext);
            if (aCLSID.isEmpty())
                xOLEProperties->setPropertyValue(getPropertyName( PROP_STREAM_NAME ),
                                uno::makeAny( rStreamName ));
            else
                xOLEProperties->setPropertyValue("CLSID", uno::makeAny(aCLSID));

            OUString aDrawAspect = pOLEHandler->GetDrawAspect();
            if(!aDrawAspect.isEmpty())
                xOLEProperties->setPropertyValue("DrawAspect", uno::makeAny(aDrawAspect));

            awt::Size aSize = pOLEHandler->getSize();
            if( !aSize.Width )
                aSize.Width = 1000;
            if( !aSize.Height )
                aSize.Height = 1000;
            xOLEProperties->setPropertyValue(getPropertyName( PROP_WIDTH ),
                            uno::makeAny(aSize.Width));
            xOLEProperties->setPropertyValue(getPropertyName( PROP_HEIGHT ),
                            uno::makeAny(aSize.Height));

            uno::Reference< graphic::XGraphic > xGraphic = pOLEHandler->getReplacement();
            xOLEProperties->setPropertyValue(getPropertyName( PROP_GRAPHIC ),
                            uno::makeAny(xGraphic));
            uno::Reference<beans::XPropertySet> xReplacementProperties(pOLEHandler->getShape(), uno::UNO_QUERY);
            if (xReplacementProperties.is())
            {
                OUString pProperties[] = {
                    OUString("AnchorType"),
                    OUString("Surround"),
                    OUString("HoriOrient"),
                    OUString("HoriOrientPosition"),
                    OUString("VertOrient"),
                    OUString("VertOrientPosition")
                };
                for (const OUString & s : pProperties)
                    xOLEProperties->setPropertyValue(s, xReplacementProperties->getPropertyValue(s));
            }
            else
                // mimic the treatment of graphics here.. it seems anchoring as character
                // gives a better ( visually ) result
                xOLEProperties->setPropertyValue(getPropertyName( PROP_ANCHOR_TYPE ),  uno::makeAny( text::TextContentAnchorType_AS_CHARACTER ) );
            // remove ( if valid ) associated shape ( used for graphic replacement )
            SAL_WARN_IF(m_aAnchoredStack.empty(), "writerfilter.dmapper", "no anchor stack");
            if (!m_aAnchoredStack.empty())
                m_aAnchoredStack.top( ).bToRemove = true;
            RemoveLastParagraph();
            SAL_WARN_IF(m_aTextAppendStack.empty(), "writerfilter.dmapper", "no text stack");
            if (!m_aTextAppendStack.empty())
                m_aTextAppendStack.pop();

            appendTextContent( xOLE, uno::Sequence< beans::PropertyValue >() );

            if (!aCLSID.isEmpty())
                pOLEHandler->importStream(m_xComponentContext, GetTextDocument(), xOLE);

        }
        catch( const uno::Exception& )
        {
            OSL_FAIL( "Exception in creation of OLE object" );
        }

    }

    void DomainMapper_Impl::appendStarMath( const Value& val )
    {
        uno::Reference< embed::XEmbeddedObject > formula;
        val.getAny() >>= formula;
        if( formula.is() )
        {
            try
            {
                uno::Reference< text::XTextContent > xStarMath( m_xTextFactory->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW );
                uno::Reference< beans::XPropertySet > xStarMathProperties(xStarMath, uno::UNO_QUERY_THROW);

                xStarMathProperties->setPropertyValue(getPropertyName( PROP_EMBEDDED_OBJECT ),
                    val.getAny());
                // tdf#66405: set zero margins for embedded object
                xStarMathProperties->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ),
                    uno::makeAny(sal_Int32(0)));
                xStarMathProperties->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN ),
                    uno::makeAny(sal_Int32(0)));
                xStarMathProperties->setPropertyValue(getPropertyName( PROP_TOP_MARGIN ),
                    uno::makeAny(sal_Int32(0)));
                xStarMathProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ),
                    uno::makeAny(sal_Int32(0)));

                uno::Reference< uno::XInterface > xInterface( formula->getComponent(), uno::UNO_QUERY );
                // set zero margins for object's component
                uno::Reference< beans::XPropertySet > xComponentProperties( xInterface, uno::UNO_QUERY_THROW );
                xComponentProperties->setPropertyValue(getPropertyName( PROP_LEFT_MARGIN ),
                    uno::makeAny(sal_Int32(0)));
                xComponentProperties->setPropertyValue(getPropertyName( PROP_RIGHT_MARGIN ),
                    uno::makeAny(sal_Int32(0)));
                xComponentProperties->setPropertyValue(getPropertyName( PROP_TOP_MARGIN ),
                    uno::makeAny(sal_Int32(0)));
                xComponentProperties->setPropertyValue(getPropertyName( PROP_BOTTOM_MARGIN ),
                    uno::makeAny(sal_Int32(0)));
                Size size( 1000, 1000 );
                if( oox::FormulaImportBase* formulaimport = dynamic_cast< oox::FormulaImportBase* >( xInterface.get()))
                    size = formulaimport->getFormulaSize();
                xStarMathProperties->setPropertyValue(getPropertyName( PROP_WIDTH ),
                    uno::makeAny( sal_Int32(size.Width())));
                xStarMathProperties->setPropertyValue(getPropertyName( PROP_HEIGHT ),
                    uno::makeAny( sal_Int32(size.Height())));
                // mimic the treatment of graphics here.. it seems anchoring as character
                // gives a better ( visually ) result
                xStarMathProperties->setPropertyValue(getPropertyName( PROP_ANCHOR_TYPE ),
                    uno::makeAny( text::TextContentAnchorType_AS_CHARACTER ) );
                appendTextContent( xStarMath, uno::Sequence< beans::PropertyValue >() );
            }
            catch( const uno::Exception& )
            {
                OSL_FAIL( "Exception in creation of StarMath object" );
            }
        }
    }

    uno::Reference< beans::XPropertySet > DomainMapper_Impl::appendTextSectionAfter(
                                        uno::Reference< text::XTextRange > const & xBefore )
    {
        uno::Reference< beans::XPropertySet > xRet;
        if (m_aTextAppendStack.empty())
            return xRet;
        uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
        if(xTextAppend.is())
        {
            try
            {
                uno::Reference< text::XParagraphCursor > xCursor(
                    xTextAppend->createTextCursorByRange( xBefore ), uno::UNO_QUERY_THROW);
                //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);
                uno::Reference< text::XTextContent > xSection( m_xTextFactory->createInstance("com.sun.star.text.TextSection"), 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::appendGlossaryEntry()
    {
        appendTextSectionAfter(m_xGlossaryEntryStart);
    }

    void DomainMapper_Impl::PushPageHeaderFooter(bool bHeader, SectionPropertyMap::PageType eType)
    {
        m_aHeaderFooterStack.push(HeaderFooterContext(m_bTextInserted));
        m_bTextInserted = false;

        const PropertyIds ePropIsOn = bHeader? PROP_HEADER_IS_ON: PROP_FOOTER_IS_ON;
        const PropertyIds ePropShared = bHeader? PROP_HEADER_IS_SHARED: PROP_FOOTER_IS_SHARED;
        const PropertyIds ePropTextLeft = bHeader? PROP_HEADER_TEXT_LEFT: PROP_FOOTER_TEXT_LEFT;
        const PropertyIds ePropText = bHeader? PROP_HEADER_TEXT: PROP_FOOTER_TEXT;

        m_bInHeaderFooterImport = true;

        //get the section context
        PropertyMapPtr pContext = DomainMapper_Impl::GetTopContextOfType(CONTEXT_SECTION);
        //ask for the header/footer name of the given type
        SectionPropertyMap* pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
        if(pSectionContext)
        {
            // clear the "Link To Previous" flag so that the header/footer
            // content is not copied from the previous section
            pSectionContext->ClearHeaderFooterLinkToPrevious(bHeader, eType);

            uno::Reference< beans::XPropertySet > xPageStyle =
                pSectionContext->GetPageStyle(
                    GetPageStyles(),
                    m_xTextFactory,
                    eType == SectionPropertyMap::PAGE_FIRST );
            if (!xPageStyle.is())
                return;
            try
            {
                bool bLeft = eType == SectionPropertyMap::PAGE_LEFT;
                bool bFirst = eType == SectionPropertyMap::PAGE_FIRST;
                if ((!bLeft && !GetSettingsTable()->GetEvenAndOddHeaders()) || (GetSettingsTable()->GetEvenAndOddHeaders()))
                {
                    //switch on header/footer use
                    xPageStyle->setPropertyValue(
                            getPropertyName(ePropIsOn),
                            uno::makeAny(true));

                    // If the 'Different Even & Odd Pages' flag is turned on - do not ignore it
                    // Even if the 'Even' header/footer is blank - the flag should be imported (so it would look in LO like in Word)
                    if (!bFirst && GetSettingsTable()->GetEvenAndOddHeaders())
                        xPageStyle->setPropertyValue(getPropertyName(ePropShared), uno::makeAny(false));

                    //set the interface
                    uno::Reference< text::XText > xText;
                    xPageStyle->getPropertyValue(getPropertyName(bLeft? ePropTextLeft: ePropText)) >>= xText;

                    m_aTextAppendStack.push(TextAppendContext(uno::Reference< text::XTextAppend >(xText, uno::UNO_QUERY_THROW),
                                m_bIsNewDoc? uno::Reference<text::XTextCursor>(): m_xBodyText->createTextCursorByRange(xText->getStart())));
                }
                else
                {
                    m_bDiscardHeaderFooter = true;
                }
            }
            catch( const uno::Exception& )
            {
            }
        }
    }

    void DomainMapper_Impl::PushPageHeader(SectionPropertyMap::PageType eType)
    {
        PushPageHeaderFooter(/* bHeader = */ true, eType);
    }

    void DomainMapper_Impl::PushPageFooter(SectionPropertyMap::PageType eType)
    {
        PushPageHeaderFooter(/* bHeader = */ false, eType);
    }

    void DomainMapper_Impl::PopPageHeaderFooter()
    {
        //header and footer always have an empty paragraph at the end
        //this has to be removed
        RemoveLastParagraph( );

        if (!m_aTextAppendStack.empty())
        {
            if (!m_bDiscardHeaderFooter)
            {
                m_aTextAppendStack.pop();
            }
            m_bDiscardHeaderFooter = false;
        }
        m_bInHeaderFooterImport = false;

        if (!m_aHeaderFooterStack.empty())
        {
            m_bTextInserted = m_aHeaderFooterStack.top().getTextInserted();
            m_aHeaderFooterStack.pop();
        }
    }

    void DomainMapper_Impl::PushFootOrEndnote( bool bIsFootnote )
    {
        m_bInFootOrEndnote = true;
        m_bCheckFirstFootnoteTab = true;
        m_bSaveFirstParagraphInCell = m_bFirstParagraphInCell;
        try
        {
            // Redlines outside the footnote should not affect footnote content
            m_aRedlines.push(std::vector< RedlineParamsPtr >());

            PropertyMapPtr pTopContext = GetTopContext();
            uno::Reference< text::XText > xFootnoteText;
            if (GetTextFactory().is())
                xFootnoteText.set( GetTextFactory()->createInstance(
                bIsFootnote ?
                    OUString( "com.sun.star.text.Footnote" ) : OUString( "com.sun.star.text.Endnote" )),
                uno::UNO_QUERY_THROW );
            uno::Reference< text::XFootnote > xFootnote( xFootnoteText, uno::UNO_QUERY_THROW );
            pTopContext->SetFootnote( xFootnote );
            uno::Sequence< beans::PropertyValue > aFontProperties = pTopContext->GetPropertyValues();
            appendTextContent( uno::Reference< text::XTextContent >( xFootnoteText, uno::UNO_QUERY_THROW ), aFontProperties );
            m_aTextAppendStack.push(TextAppendContext(uno::Reference< text::XTextAppend >( xFootnoteText, uno::UNO_QUERY_THROW ),
                        xFootnoteText->createTextCursorByRange(xFootnoteText->getStart())));

            // Redlines for the footnote anchor
            CheckRedline( xFootnote->getAnchor( ) );

        }
        catch( const uno::Exception& e )
        {
            SAL_WARN("writerfilter.dmapper", "exception in PushFootOrEndnote: " << e);
        }
    }

    void DomainMapper_Impl::CreateRedline(uno::Reference<text::XTextRange> const& xRange,
            const RedlineParamsPtr& pRedline)
    {
        if ( pRedline.get( ) )
        {
            try
            {
                OUString sType;
                switch ( pRedline->m_nToken & 0xffff )
                {
                case XML_mod:
                    sType = getPropertyName( PROP_FORMAT );
                    break;
                case XML_moveTo:
                case XML_ins:
                    sType = getPropertyName( PROP_INSERT );
                    break;
                case XML_moveFrom:
                case XML_del:
                    sType = getPropertyName( PROP_DELETE );
                    break;
                case XML_ParagraphFormat:
                    sType = getPropertyName( PROP_PARAGRAPH_FORMAT );
                    break;
                default:
                    throw lang::IllegalArgumentException("illegal redline token type", nullptr, 0);
                }
                uno::Reference < text::XRedline > xRedline( xRange, uno::UNO_QUERY_THROW );
                beans::PropertyValues aRedlineProperties( 3 );
                beans::PropertyValue * pRedlineProperties = aRedlineProperties.getArray(  );
                pRedlineProperties[0].Name = getPropertyName( PROP_REDLINE_AUTHOR );
                pRedlineProperties[0].Value <<= pRedline->m_sAuthor;
                pRedlineProperties[1].Name = getPropertyName( PROP_REDLINE_DATE_TIME );
                pRedlineProperties[1].Value <<= ConversionHelper::ConvertDateStringToDateTime( pRedline->m_sDate );
                pRedlineProperties[2].Name = getPropertyName( PROP_REDLINE_REVERT_PROPERTIES );
                pRedlineProperties[2].Value <<= pRedline->m_aRevertProperties;
                xRedline->makeRedline( sType, aRedlineProperties );
            }
            catch( const uno::Exception & )
            {
                OSL_FAIL( "Exception in makeRedline" );
            }
        }
    }

    void DomainMapper_Impl::CheckParaMarkerRedline( uno::Reference< text::XTextRange > const& xRange )
    {
        if ( m_pParaMarkerRedline.get( ) )
        {
            CreateRedline( xRange, m_pParaMarkerRedline );
            if ( m_pParaMarkerRedline.get( ) )
            {
                m_pParaMarkerRedline.clear();
                m_currentRedline.clear();
            }
        }
    }

    void DomainMapper_Impl::CheckRedline( uno::Reference< text::XTextRange > const& xRange )
    {
        // Writer core "officially" does not like overlapping redlines, and its UNO interface is stupid enough
        // to not prevent that. However, in practice in fact everything appears to work fine (except for the debug warnings
        // about redline table corruption, which may possibly be harmless in reality). So leave this as it is, since this
        // is a better representation of how the changes happened. If this will ever become a problem, overlapping redlines
        // will need to be merged into one, just like doing the changes in the UI does, which will lose some information
        // (and so if that happens, it may be better to fix Writer).
        // Create the redlines here from lowest (formats) to highest (inserts/removals) priority, since the last one is
        // what Writer presents graphically, so this will show deletes as deleted text and not as just formatted text being there.
        if( GetTopContextOfType(CONTEXT_PARAGRAPH) )
        {
            std::vector<RedlineParamsPtr>& avRedLines = GetTopContextOfType(CONTEXT_PARAGRAPH)->Redlines();
            for( std::vector<RedlineParamsPtr>::const_iterator it = avRedLines.begin();
                 it != avRedLines.end(); ++it )
                CreateRedline( xRange, *it );
        }
        if( GetTopContextOfType(CONTEXT_CHARACTER) )
        {
            std::vector<RedlineParamsPtr>& avRedLines = GetTopContextOfType(CONTEXT_CHARACTER)->Redlines();
            for( std::vector<RedlineParamsPtr>::const_iterator it = avRedLines.begin();
                 it != avRedLines.end(); ++it )
                CreateRedline( xRange, *it );
        }
        std::vector<RedlineParamsPtr>::iterator pIt = m_aRedlines.top().begin( );
        for (; pIt != m_aRedlines.top().end( ); ++pIt )
            CreateRedline( xRange, *pIt );
    }

    void DomainMapper_Impl::StartParaMarkerChange( )
    {
        m_bIsParaMarkerChange = true;
    }

    void DomainMapper_Impl::EndParaMarkerChange( )
    {
        m_bIsParaMarkerChange = false;
        m_currentRedline.clear();
    }

    void DomainMapper_Impl::PushAnnotation()
    {
        try
        {
            m_bIsInComments = true;
            if (!GetTextFactory().is())
                return;
            m_xAnnotationField.set( GetTextFactory()->createInstance( "com.sun.star.text.TextField.Annotation" ),
                                    uno::UNO_QUERY_THROW );
            uno::Reference< text::XText > xAnnotationText;
            m_xAnnotationField->getPropertyValue("TextRange") >>= xAnnotationText;
            m_aTextAppendStack.push(TextAppendContext(uno::Reference< text::XTextAppend >( xAnnotationText, uno::UNO_QUERY_THROW ),
                        m_bIsNewDoc ? uno::Reference<text::XTextCursor>() : xAnnotationText->createTextCursorByRange(xAnnotationText->getStart())));
        }
        catch( const uno::Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
        }
    }

    void DomainMapper_Impl::PopFootOrEndnote()
    {
        if (!IsRTFImport())
            RemoveLastParagraph();

        // In case the foot or endnote did not contain a tab.
        m_bIgnoreNextTab = false;

        if (!m_aTextAppendStack.empty())
            m_aTextAppendStack.pop();

        if (m_aRedlines.size() == 1)
        {
            SAL_WARN("writerfilter.dmapper", "PopFootOrEndnote() is called without PushFootOrEndnote()?");
            return;
        }
        m_aRedlines.pop();
        m_bSeenFootOrEndnoteSeparator = false;
        m_bInFootOrEndnote = false;
        m_bFirstParagraphInCell = m_bSaveFirstParagraphInCell;
    }

    void DomainMapper_Impl::SeenFootOrEndnoteSeparator()
    {
        if (!m_bSeenFootOrEndnoteSeparator)
        {
            m_bSeenFootOrEndnoteSeparator = true;
            m_bIgnoreNextPara = true;
        }
    }

    void DomainMapper_Impl::PopAnnotation()
    {
        RemoveLastParagraph();

        m_bIsInComments = false;
        m_aTextAppendStack.pop();

        try
        {
            // See if the annotation will be a single position or a range.
            if (m_nAnnotationId == -1 || !m_aAnnotationPositions[m_nAnnotationId].m_xStart.is() || !m_aAnnotationPositions[m_nAnnotationId].m_xEnd.is())
            {
                uno::Sequence< beans::PropertyValue > aEmptyProperties;
                appendTextContent( uno::Reference< text::XTextContent >( m_xAnnotationField, uno::UNO_QUERY_THROW ), aEmptyProperties );
            }
            else
            {
                AnnotationPosition& aAnnotationPosition = m_aAnnotationPositions[m_nAnnotationId];
                // Create a range that points to the annotation start/end.
                uno::Reference<text::XText> const xText = aAnnotationPosition.m_xStart->getText();
                uno::Reference<text::XTextCursor> const xCursor = xText->createTextCursorByRange(aAnnotationPosition.m_xStart);
                xCursor->gotoRange(aAnnotationPosition.m_xEnd, true);
                uno::Reference<text::XTextRange> const xTextRange(xCursor, uno::UNO_QUERY_THROW);

                // Attach the annotation to the range.
                uno::Reference<text::XTextAppend> const xTextAppend = m_aTextAppendStack.top().xTextAppend;
                xTextAppend->insertTextContent(xTextRange, uno::Reference<text::XTextContent>(m_xAnnotationField, uno::UNO_QUERY_THROW), !xCursor->isCollapsed());
            }
            m_aAnnotationPositions.erase( m_nAnnotationId );
        }
        catch (uno::Exception const& e)
        {
            SAL_WARN("writerfilter.dmapper",
                    "Cannot insert annotation field: " << e);
        }

        m_xAnnotationField.clear();
        m_nAnnotationId = -1;
    }

    void DomainMapper_Impl::PushPendingShape( const uno::Reference< drawing::XShape > & xShape )
    {
        m_aPendingShapes.push_back(xShape);
    }

    uno::Reference<drawing::XShape> DomainMapper_Impl::PopPendingShape()
    {
        uno::Reference<drawing::XShape> xRet;
        if (!m_aPendingShapes.empty())
        {
            xRet = m_aPendingShapes.front();
            m_aPendingShapes.pop_front();
        }
        return xRet;
    }

    void DomainMapper_Impl::PushShapeContext( const uno::Reference< drawing::XShape > & xShape )
    {
        // Append these early, so the context and the table manager stack will be
        // in sync, even if the text append stack is empty.
        appendTableManager();
        appendTableHandler();
        getTableManager().startLevel();

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

        try
        {
            uno::Reference< lang::XServiceInfo > xSInfo( xShape, uno::UNO_QUERY_THROW );
            if (xSInfo->supportsService("com.sun.star.drawing.GroupShape"))
            {
                // A GroupShape doesn't implement text::XTextRange, but appending
                // an empty reference to the stacks still makes sense, because this
                // way bToRemove can be set, and we won't end up with duplicated
                // shapes for OLE objects.
                m_aTextAppendStack.push(TextAppendContext(uno::Reference<text::XTextAppend>(xShape, uno::UNO_QUERY), uno::Reference<text::XTextCursor>()));
                uno::Reference<text::XTextContent> xTxtContent(xShape, uno::UNO_QUERY);
                m_aAnchoredStack.push(AnchoredContext(xTxtContent));
            }
            else if (xSInfo->supportsService("com.sun.star.drawing.OLE2Shape"))
            {
                // OLE2Shape from oox should be converted to a TextEmbeddedObject for sw.
                m_aTextAppendStack.push(TextAppendContext(uno::Reference<text::XTextAppend>(xShape, uno::UNO_QUERY), uno::Reference<text::XTextCursor>()));
                uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY);
                m_aAnchoredStack.push(AnchoredContext(xTextContent));
                uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);

                m_xEmbedded.set(m_xTextFactory->createInstance("com.sun.star.text.TextEmbeddedObject"), uno::UNO_QUERY_THROW);
                uno::Reference<beans::XPropertySet> xEmbeddedProperties(m_xEmbedded, uno::UNO_QUERY_THROW);
                xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT), xShapePropertySet->getPropertyValue(getPropertyName(PROP_EMBEDDED_OBJECT)));
                xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_ANCHOR_TYPE), uno::makeAny(text::TextContentAnchorType_AS_CHARACTER));
                // So that the original bitmap-only shape will be replaced by the embedded object.
                m_aAnchoredStack.top().bToRemove = true;
                m_aTextAppendStack.pop();
                   appendTextContent(m_xEmbedded, uno::Sequence<beans::PropertyValue>());
            }
            else
            {
                uno::Reference< text::XTextRange > xShapeText( xShape, uno::UNO_QUERY_THROW);
                // Add the shape to the text append stack
                m_aTextAppendStack.push( TextAppendContext(uno::Reference< text::XTextAppend >( xShape, uno::UNO_QUERY_THROW ),
                            m_bIsNewDoc ? uno::Reference<text::XTextCursor>() : m_xBodyText->createTextCursorByRange(xShapeText->getStart() )));

                // Add the shape to the anchored objects stack
                uno::Reference< text::XTextContent > xTxtContent( xShape, uno::UNO_QUERY_THROW );
                m_aAnchoredStack.push( AnchoredContext(xTxtContent) );

                uno::Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY_THROW );
    #ifdef DEBUG_WRITERFILTER
                TagLogger::getInstance().unoPropertySet(xProps);
    #endif
                text::TextContentAnchorType nAnchorType(text::TextContentAnchorType_AT_PARAGRAPH);
                xProps->getPropertyValue(getPropertyName( PROP_ANCHOR_TYPE )) >>= nAnchorType;
                bool checkZOrderStatus = false;
                if (xSInfo->supportsService("com.sun.star.text.TextFrame"))
                {
                    SetIsTextFrameInserted(true);
                    // Extract the special "btLr text frame" mode, requested by oox, if needed.
                    // Extract vml ZOrder from FrameInteropGrabBag
                    uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
                    uno::Sequence<beans::PropertyValue> aGrabBag;
                    xShapePropertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
                    bool checkBtLrStatus = false;

                    for (int i = 0; i < aGrabBag.getLength(); ++i)
                    {
                        if (aGrabBag[i].Name == "mso-layout-flow-alt")
                        {
                            m_bFrameBtLr = aGrabBag[i].Value.get<OUString>() == "bottom-to-top";
                            checkBtLrStatus = true;
                        }
                        if (aGrabBag[i].Name == "VML-Z-ORDER")
                        {
                            GraphicZOrderHelper* pZOrderHelper = m_rDMapper.graphicZOrderHelper();
                            sal_Int32 zOrder(0);
                            aGrabBag[i].Value >>= zOrder;
                            xShapePropertySet->setPropertyValue( "ZOrder", uno::makeAny(pZOrderHelper->findZOrder(zOrder)));
                            pZOrderHelper->addItem(xShapePropertySet, zOrder);
                            xShapePropertySet->setPropertyValue(getPropertyName( PROP_OPAQUE ), uno::makeAny( zOrder >= 0 ) );
                            checkZOrderStatus = true;
                        }
                        if(checkBtLrStatus && checkZOrderStatus)
                            break;

                        if ( aGrabBag[i].Name == "TxbxHasLink" )
                        {
                            //Chaining of textboxes will happen in ~DomainMapper_Impl
                            //i.e when all the textboxes are read and all its attributes
                            //have been set ( basically the Name/LinkedDisplayName )
                            //which is set in Graphic Import.
                            m_vTextFramesForChaining.push_back(xShape);
                        }
                    }

                    uno::Reference<text::XTextContent> xTextContent(xShape, uno::UNO_QUERY_THROW);
                    uno::Reference<text::XTextRange> xTextRange(xTextAppend->createTextCursorByRange(xTextAppend->getEnd()), uno::UNO_QUERY_THROW);
                    xTextAppend->insertTextContent(xTextRange, xTextContent, false);

                    uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
                    // we need to re-set this value to xTextContent, then only values are preserved.
                    xPropertySet->setPropertyValue("FrameInteropGrabBag",uno::makeAny(aGrabBag));
                }
                else if (nAnchorType == text::TextContentAnchorType_AS_CHARACTER)
                {
                    // Fix spacing for as-character objects. If the paragraph has CT_Spacing_after set,
                    // it needs to be set on the object too, as that's what object placement code uses.
                    PropertyMapPtr paragraphContext = GetTopContextOfType( CONTEXT_PARAGRAPH );
                    boost::optional<PropertyMap::Property> aPropMargin = paragraphContext->getProperty(PROP_PARA_BOTTOM_MARGIN);
                    if(aPropMargin)
                        xProps->setPropertyValue( getPropertyName( PROP_BOTTOM_MARGIN ), aPropMargin->second );
                }
                else
                {
                    uno::Reference<beans::XPropertySet> xShapePropertySet(xShape, uno::UNO_QUERY);
                    uno::Sequence<beans::PropertyValue> aGrabBag;
                    xShapePropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
                    for (int i = 0; i < aGrabBag.getLength(); ++i)
                    {
                        if (aGrabBag[i].Name == "VML-Z-ORDER")
                        {
                            GraphicZOrderHelper* pZOrderHelper = m_rDMapper.graphicZOrderHelper();
                            sal_Int32 zOrder(0);
                            aGrabBag[i].Value >>= zOrder;
                            xShapePropertySet->setPropertyValue( "ZOrder", uno::makeAny(pZOrderHelper->findZOrder(zOrder)));
                            pZOrderHelper->addItem(xShapePropertySet, zOrder);
                            xShapePropertySet->setPropertyValue(getPropertyName( PROP_OPAQUE ), uno::makeAny( zOrder >= 0 ) );
                            checkZOrderStatus = true;
                        }
                        else if ( aGrabBag[i].Name == "TxbxHasLink" )
                        {
                            //Chaining of textboxes will happen in ~DomainMapper_Impl
                            //i.e when all the textboxes are read and all its attributes
                            //have been set ( basically the Name/LinkedDisplayName )
                            //which is set in Graphic Import.
                            m_vTextFramesForChaining.push_back(xShape);
                        }
                    }

                    if(IsSdtEndBefore())
                    {
                        uno::Reference< beans::XPropertySetInfo > xPropSetInfo;
                        if(xShapePropertySet.is())
                        {
                            xPropSetInfo = xShapePropertySet->getPropertySetInfo();
                            if (xPropSetInfo.is() && xPropSetInfo->hasPropertyByName("InteropGrabBag"))
                            {
                                uno::Sequence<beans::PropertyValue> aShapeGrabBag( comphelper::InitPropertySequence({
                                    { "SdtEndBefore", uno::Any(true) }
                                }));
                                xShapePropertySet->setPropertyValue("InteropGrabBag",uno::makeAny(aShapeGrabBag));
                            }
                        }
                    }
                }
                if (!m_bInHeaderFooterImport && !checkZOrderStatus)
                    xProps->setPropertyValue(
                            getPropertyName( PROP_OPAQUE ),
                            uno::makeAny( true ) );
            }
            m_bParaChanged = true;
            getTableManager().setIsInShape(true);
        }
        catch ( const uno::Exception& e )
        {
            SAL_WARN("writerfilter.dmapper", "Exception when adding shape: " << e);
        }
    }
    /*
     * Updating chart height and width after reading the actual values from wp:extent
    */
    void DomainMapper_Impl::UpdateEmbeddedShapeProps(const uno::Reference< drawing::XShape > & xShape)
    {
        if (!xShape.is())
            return;

        uno::Reference<beans::XPropertySet> xEmbeddedProperties(m_xEmbedded, uno::UNO_QUERY_THROW);
        awt::Size aSize = xShape->getSize( );
        xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_WIDTH), uno::makeAny(sal_Int32(aSize.Width)));
        xEmbeddedProperties->setPropertyValue(getPropertyName(PROP_HEIGHT), uno::makeAny(sal_Int32(aSize.Height)));
    }

    void DomainMapper_Impl::PopShapeContext()
    {
        if (hasTableManager())
        {
            getTableManager().endLevel();
            popTableManager();
        }
        if ( m_aAnchoredStack.size() > 0 )
        {
            // For OLE object replacement shape, the text append context was already removed
            // or the OLE object couldn't be inserted.
            if ( !m_aAnchoredStack.top().bToRemove )
            {
                RemoveLastParagraph();
                if (!m_aTextAppendStack.empty())
                    m_aTextAppendStack.pop();
            }

            uno::Reference< text::XTextContent > xObj = m_aAnchoredStack.top( ).xTextContent;
            try
            {
                appendTextContent( xObj, uno::Sequence< beans::PropertyValue >() );
            }
            catch ( const uno::RuntimeException& )
            {
                // this is normal: the shape is already attached
            }

            const uno::Reference<drawing::XShape> xShape( xObj, uno::UNO_QUERY_THROW );
            // Remove the shape if required (most likely replacement shape for OLE object)
            // or anchored to a discarded header or footer
            if ( m_aAnchoredStack.top().bToRemove || m_bDiscardHeaderFooter )
            {
                try
                {
                    uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(m_xTextDocument, uno::UNO_QUERY_THROW);
                    uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
                    if ( xDrawPage.is() )
                        xDrawPage->remove( xShape );
                }
                catch( const uno::Exception& )
                {
                }
            }

            // Relative width calculations deferred until section's margins are defined.
            // Being cautious: only deferring undefined/minimum-width shapes in order to avoid causing potential regressions
            if( xShape->getSize().Width <= 2 )
            {
                const uno::Reference<beans::XPropertySet> xShapePropertySet( xShape, uno::UNO_QUERY );
                SectionPropertyMap* pSectionContext = GetSectionContext();
                if ( pSectionContext && (!hasTableManager() || !getTableManager().isInTable()) &&
                     xShapePropertySet->getPropertySetInfo()->hasPropertyByName(getPropertyName(PROP_RELATIVE_WIDTH)) )
                {
                    pSectionContext->addRelativeWidthShape(xShape);
                }
            }

            m_aAnchoredStack.pop();
        }
        m_bFrameBtLr = false;
    }

    bool DomainMapper_Impl::IsSdtEndBefore()
    {
        bool bIsSdtEndBefore = false;
        PropertyMapPtr pContext = GetTopContextOfType(CONTEXT_CHARACTER);
        if(pContext)
        {
            uno::Sequence< beans::PropertyValue > currentCharProps = pContext->GetPropertyValues();
            for (int i =0; i< currentCharProps.getLength(); i++)
            {
                if (currentCharProps[i].Name == "CharInteropGrabBag")
                {
                    uno::Sequence<beans::PropertyValue> aCharGrabBag;
                    currentCharProps[i].Value >>= aCharGrabBag;
                    for (int j=0; j < aCharGrabBag.getLength();j++)
                    {
                        if(aCharGrabBag[j].Name == "SdtEndBefore")
                        {
                            aCharGrabBag[j].Value >>= bIsSdtEndBefore;
                        }
                    }
                }
            }
        }
        return bIsSdtEndBefore;
    }

    bool DomainMapper_Impl::IsDiscardHeaderFooter()
    {
        return m_bDiscardHeaderFooter;
    }

    // called from TableManager::closeCell()
    void DomainMapper_Impl::ClearPreviousParagraph()
    {
        // in table cells, set bottom auto margin of last paragraph to 0, except in paragraphs with numbering
        if ((m_nTableDepth == (m_nTableCellDepth + 1))
            && m_xPreviousParagraph.is()
            && hasTableManager() && getTableManager().isCellLastParaAfterAutospacing())
        {
            uno::Reference<container::XNamed> xPreviousNumberingRules(m_xPreviousParagraph->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
            if ( !xPreviousNumberingRules.is() || xPreviousNumberingRules->getName().isEmpty() )
                m_xPreviousParagraph->setPropertyValue("ParaBottomMargin", uno::makeAny(static_cast<sal_Int32>(0)));
        }

        m_xPreviousParagraph.clear();

        // next table paragraph will be first paragraph in a cell
        m_bFirstParagraphInCell = true;
    }

    static sal_Int16 lcl_ParseNumberingType( const OUString& rCommand )
    {
        sal_Int16 nRet = style::NumberingType::PAGE_DESCRIPTOR;

        //  The command looks like: " PAGE \* Arabic "
        OUString sNumber = msfilter::util::findQuotedText(rCommand, "\\* ", ' ');

        if( !sNumber.isEmpty() )
        {
            //todo: might make sense to hash this list, too
            struct NumberingPairs
            {
                const sal_Char* cWordName;
                sal_Int16       nType;
            };
            static const NumberingPairs aNumberingPairs[] =
            {
                {"Arabic", style::NumberingType::ARABIC}
                ,{"ROMAN", style::NumberingType::ROMAN_UPPER}
                ,{"roman", style::NumberingType::ROMAN_LOWER}
                ,{"ALPHABETIC", style::NumberingType::CHARS_UPPER_LETTER}
                ,{"alphabetic", style::NumberingType::CHARS_LOWER_LETTER}
                ,{"CircleNum", style::NumberingType::CIRCLE_NUMBER}
                ,{"ThaiArabic", style::NumberingType::CHARS_THAI}
                ,{"ThaiCardText", style::NumberingType::CHARS_THAI}
                ,{"ThaiLetter", style::NumberingType::CHARS_THAI}
    //            ,{"SBCHAR", style::NumberingType::}
    //            ,{"DBCHAR", style::NumberingType::}
    //            ,{"DBNUM1", style::NumberingType::}
    //            ,{"DBNUM2", style::NumberingType::}
    //            ,{"DBNUM3", style::NumberingType::}
    //            ,{"DBNUM4", style::NumberingType::}
                ,{"Aiueo", style::NumberingType::AIU_FULLWIDTH_JA}
                ,{"Iroha", style::NumberingType::IROHA_FULLWIDTH_JA}
    //            ,{"ZODIAC1", style::NumberingType::}
    //            ,{"ZODIAC2", style::NumberingType::}
    //            ,{"ZODIAC3", style::NumberingType::}
    //            ,{"CHINESENUM1", style::NumberingType::}
    //            ,{"CHINESENUM2", style::NumberingType::}
    //            ,{"CHINESENUM3", style::NumberingType::}
                ,{"ArabicAlpha", style::NumberingType::CHARS_ARABIC}
                ,{"ArabicAbjad", style::NumberingType::FULLWIDTH_ARABIC}
    /* possible values:
    style::NumberingType::

        CHARS_UPPER_LETTER_N
        CHARS_LOWER_LETTER_N
        TRANSLITERATION
        NATIVE_NUMBERING
        CIRCLE_NUMBER
        NUMBER_LOWER_ZH
        NUMBER_UPPER_ZH
        NUMBER_UPPER_ZH_TW
        TIAN_GAN_ZH
        DI_ZI_ZH
        NUMBER_TRADITIONAL_JA
        AIU_HALFWIDTH_JA
        IROHA_HALFWIDTH_JA
        NUMBER_UPPER_KO
        NUMBER_HANGUL_KO
        HANGUL_JAMO_KO
        HANGUL_SYLLABLE_KO
        HANGUL_CIRCLED_JAMO_KO
        HANGUL_CIRCLED_SYLLABLE_KO
        CHARS_HEBREW
        CHARS_NEPALI
        CHARS_KHMER
        CHARS_LAO
        CHARS_TIBETAN
        CHARS_CYRILLIC_UPPER_LETTER_BG
        CHARS_CYRILLIC_LOWER_LETTER_BG
        CHARS_CYRILLIC_UPPER_LETTER_N_BG
        CHARS_CYRILLIC_LOWER_LETTER_N_BG
        CHARS_CYRILLIC_UPPER_LETTER_RU
        CHARS_CYRILLIC_LOWER_LETTER_RU
        CHARS_CYRILLIC_UPPER_LETTER_N_RU
        CHARS_CYRILLIC_LOWER_LETTER_N_RU
        CHARS_CYRILLIC_UPPER_LETTER_SR
        CHARS_CYRILLIC_LOWER_LETTER_SR
        CHARS_CYRILLIC_UPPER_LETTER_N_SR
        CHARS_CYRILLIC_LOWER_LETTER_N_SR*/

            };
            for(const NumberingPairs& rNumberingPair : aNumberingPairs)
            {
                if( /*sCommand*/sNumber.equalsAscii(rNumberingPair.cWordName ))
                {
                    nRet = rNumberingPair.nType;
                    break;
                }
            }

        }
        return nRet;
    }

    static OUString lcl_ParseFormat( const OUString& rCommand )
    {
        //  The command looks like: " DATE \@"dd MMMM yyyy" or "09/02/2014"
        //  Remove whitespace permitted by standard between \@ and "
        OUString command;
        sal_Int32 delimPos = rCommand.indexOf("\\@");
        if (delimPos != -1)
        {
            sal_Int32 wsChars = rCommand.indexOf('\"') - delimPos - 2;
            command = rCommand.replaceAt(delimPos+2, wsChars, "");
        }
        else
            command = rCommand;

        return msfilter::util::findQuotedText(command, "\\@\"", '\"');
    }
    /*-------------------------------------------------------------------------
    extract a parameter (with or without quotes) between the command and the following backslash
      -----------------------------------------------------------------------*/
    static OUString lcl_ExtractToken(OUString const& rCommand,
            sal_Int32 & rIndex, bool & rHaveToken, bool & rIsSwitch)
    {
        rHaveToken = false;
        rIsSwitch = false;

        OUStringBuffer token;
        bool bQuoted(false);
        for (; rIndex < rCommand.getLength(); ++rIndex)
        {
            sal_Unicode const currentChar(rCommand[rIndex]);
            switch (currentChar)
            {
                case '\\':
                {
                    if (rIndex == rCommand.getLength() - 1)
                    {
                        SAL_INFO("writerfilter.dmapper", "field: trailing escape");
                        ++rIndex;
                        return OUString();
                    }
                    sal_Unicode const nextChar(rCommand[rIndex+1]);
                    if (bQuoted || '\\' == nextChar)
                    {
                        ++rIndex; // read 2 chars
                        token.append(nextChar);
                    }
                    else // field switch (case insensitive)
                    {
                        rHaveToken = true;
                        if (token.isEmpty())
                        {
                            rIsSwitch = true;
                            rIndex += 2; // read 2 chars
                            return rCommand.copy(rIndex - 2, 2).toAsciiUpperCase();
                        }
                        else
                        {   // leave rIndex, read it again next time
                            return token.makeStringAndClear();
                        }
                    }
                }
                break;
                case '\"':
                    if (bQuoted || !token.isEmpty())
                    {
                        rHaveToken = true;
                        if (bQuoted)
                        {
                            ++rIndex;
                        }
                        return token.makeStringAndClear();
                    }
                    else
                    {
                        bQuoted = true;
                    }
                break;
                case ' ':
                    if (bQuoted)
                    {
                        token.append(' ');
                    }
                    else
                    {
                        if (!token.isEmpty())
                        {
                            rHaveToken = true;
                            ++rIndex;
                            return token.makeStringAndClear();
                        }
                    }
                break;
                case '=':
                    if (token.isEmpty())
                    {
                        rHaveToken = true;
                        ++rIndex;
                        return OUString("FORMULA");
                    }
                break;
                default:
                    token.append(currentChar);
                break;
            }
        }
        assert(rIndex == rCommand.getLength());
        if (bQuoted)
        {
            // MS Word allows this, so just emit a debug message
            SAL_INFO("writerfilter.dmapper",
                        "field argument with unterminated quote");
        }
        rHaveToken = !token.isEmpty();
        return token.makeStringAndClear();
    }

    std::tuple<OUString, std::vector<OUString>, std::vector<OUString> > splitFieldCommand(const OUString& rCommand)
    {
        OUString sType;
        std::vector<OUString> arguments;
        std::vector<OUString> switches;
        sal_Int32 nStartIndex(0);
        // tdf#54584: Field may be prepended by a backslash
        // This is not an escapement, but already escaped literal "\"
        // MS Word allows this, so just skip it
        if ((rCommand.getLength() >= nStartIndex + 2) &&
            (rCommand[nStartIndex] == L'\\') &&
            (rCommand[nStartIndex + 1] != L'\\') &&
            (rCommand[nStartIndex + 1] != L' '))
        {
            ++nStartIndex;
        }

        do
        {
            bool bHaveToken;
            bool bIsSwitch;
            OUString const token =
                lcl_ExtractToken(rCommand, nStartIndex, bHaveToken, bIsSwitch);
            assert(nStartIndex <= rCommand.getLength());
            if (bHaveToken)
            {
                if (sType.isEmpty())
                {
                    sType = token.toAsciiUpperCase();
                }
                else if (bIsSwitch || !switches.empty())
                {
                    switches.push_back(token);
                }
                else
                {
                    arguments.push_back(token);
                }
            }
        } while (nStartIndex < rCommand.getLength());

        return std::make_tuple(sType, arguments, switches);
    }

    static OUString lcl_ExctractVariableAndHint( const OUString& rCommand, OUString& rHint )
    {
        // the first word after "ASK " is the variable
        // the text after the variable and before a '\' is the hint
        // if no hint is set the variable is used as hint
        // the quotes of the hint have to be removed
        sal_Int32 nIndex = rCommand.indexOf( ' ', 2); //find last space after 'ASK'
        if (nIndex == -1)
            return OUString();
        while(rCommand[nIndex] == ' ')
            ++nIndex;
        OUString sShortCommand( rCommand.copy( nIndex ) ); //cut off the " ASK "

        nIndex = 0;
        sShortCommand = sShortCommand.getToken( 0, '\\', nIndex);
        nIndex = 0;
        OUString sRet = sShortCommand.getToken( 0, ' ', nIndex);
        if( nIndex > 0)
            rHint = sShortCommand.copy( nIndex );
        if( rHint.isEmpty() )
            rHint = sRet;
        return sRet;
    }

    static bool lcl_FindInCommand(
        const OUString& rCommand,
        sal_Unicode cSwitch,
        OUString& rValue )
    {
        bool bRet = false;
        OUString sSearch = "\\" + OUStringLiteral1( cSwitch );
        sal_Int32 nIndex = rCommand.indexOf( sSearch  );
        if( nIndex >= 0 )
        {
            bRet = true;
            //find next '\' or end of string
            sal_Int32 nEndIndex = rCommand.indexOf( '\\', nIndex + 1);
            if( nEndIndex < 0 )
                nEndIndex = rCommand.getLength() ;
            if( nEndIndex - nIndex > 3 )
                rValue = rCommand.copy( nIndex + 3, nEndIndex - nIndex - 3);
        }
        return bRet;
    }

    void DomainMapper_Impl::GetCurrentLocale(lang::Locale& rLocale)
    {
        PropertyMapPtr pTopContext = GetTopContext();
        boost::optional<PropertyMap::Property> pLocale = pTopContext->getProperty(PROP_CHAR_LOCALE);
        if( pLocale )
            pLocale->second >>= rLocale;
        else
        {
            PropertyMapPtr pParaContext = GetTopContextOfType(CONTEXT_PARAGRAPH);
            pLocale = pParaContext->getProperty(PROP_CHAR_LOCALE);
            if( pLocale )
            {
                pLocale->second >>= rLocale;
            }
        }
    }

    /*-------------------------------------------------------------------------
        extract the number format from the command and apply the resulting number
        format to the XPropertySet
      -----------------------------------------------------------------------*/
    void DomainMapper_Impl::SetNumberFormat( const OUString& rCommand,
            uno::Reference< beans::XPropertySet > const& xPropertySet,
            bool const bDetectFormat)
    {
        OUString sFormatString = lcl_ParseFormat( rCommand );
        // find \h - hijri/luna calendar todo: what about saka/era calendar?
        bool bHijri = 0 < rCommand.indexOf("\\h ");
        lang::Locale aUSLocale;
        aUSLocale.Language = "en";
        aUSLocale.Country = "US";

        //determine current locale - todo: is it necessary to initialize this locale?
        lang::Locale aCurrentLocale = aUSLocale;
        GetCurrentLocale( aCurrentLocale );
        OUString sFormat = ConversionHelper::ConvertMSFormatStringToSO( sFormatString, aCurrentLocale, bHijri);
        //get the number formatter and convert the string to a format value
        try
        {
            sal_Int32 nKey = 0;
            uno::Reference< util::XNumberFormatsSupplier > xNumberSupplier( m_xTextDocument, uno::UNO_QUERY_THROW );
            if( bDetectFormat )
            {
                uno::Reference< util::XNumberFormatter> xFormatter(util::NumberFormatter::create(m_xComponentContext), uno::UNO_QUERY_THROW);
                xFormatter->attachNumberFormatsSupplier( xNumberSupplier );
                nKey = xFormatter->detectNumberFormat( 0, rCommand );
             }
            else
            {
                nKey = xNumberSupplier->getNumberFormats()->addNewConverted( sFormat, aUSLocale, aCurrentLocale );
            }
            xPropertySet->setPropertyValue(
                getPropertyName(PROP_NUMBER_FORMAT),
                uno::makeAny( nKey ));
        }
        catch(const uno::Exception&)
        {
        }
    }

    static uno::Any lcl_getGrabBagValue( const uno::Sequence<beans::PropertyValue>& grabBag, OUString const & name )
    {
        for (int i = 0; i < grabBag.getLength(); ++i)
        {
            if (grabBag[i].Name == name )
                return grabBag[i].Value ;
        }
        return uno::Any();
    }

    //Link the text frames.
    void DomainMapper_Impl::ChainTextFrames()
    {
        //can't link textboxes if there are not even two of them...
        if( 2 > m_vTextFramesForChaining.size() )
            return ;

        struct TextFramesForChaining {
            css::uno::Reference< css::drawing::XShape > xShape;
            sal_Int32 nId;
            sal_Int32 nSeq;
            OUString s_mso_next_textbox;
            bool bShapeNameSet;
            TextFramesForChaining(): nId(0), nSeq(0), bShapeNameSet(false) {}
        } ;
        typedef std::map <OUString, TextFramesForChaining> ChainMap;

        try
        {
            ChainMap aTextFramesForChainingHelper;
            OUString sChainNextName("ChainNextName");

            //learn about ALL of the textboxes and their chaining values first - because frames are processed in no specific order.
            std::vector<uno::Reference< drawing::XShape > >::iterator iter;
            for( iter = m_vTextFramesForChaining.begin(); iter != m_vTextFramesForChaining.end(); ++iter )
            {
                uno::Reference<text::XTextContent>  xTextContent(*iter, uno::UNO_QUERY_THROW);
                uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
                uno::Reference<beans::XPropertySetInfo> xPropertySetInfo;
                if( xPropertySet.is() )
                    xPropertySetInfo = xPropertySet->getPropertySetInfo();
                uno::Sequence<beans::PropertyValue> aGrabBag;
                uno::Reference<lang::XServiceInfo> xServiceInfo(xPropertySet, uno::UNO_QUERY);

                TextFramesForChaining aChainStruct = TextFramesForChaining();
                OUString sShapeName;
                OUString sLinkChainName;

                //The chaining name and the shape name CAN be different in .docx.
                //MUST use LinkDisplayName/ChainName as the shape name for establishing links.
                if ( xServiceInfo->supportsService("com.sun.star.text.TextFrame") )
                {
                    xPropertySet->getPropertyValue("FrameInteropGrabBag") >>= aGrabBag;
                    xPropertySet->getPropertyValue("LinkDisplayName") >>= sShapeName;
                }
                else
                {
                    xPropertySet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
                    xPropertySet->getPropertyValue("ChainName") >>= sShapeName;
                }

                lcl_getGrabBagValue( aGrabBag, "Txbx-Id")  >>= aChainStruct.nId;
                lcl_getGrabBagValue( aGrabBag, "Txbx-Seq") >>= aChainStruct.nSeq;
                lcl_getGrabBagValue( aGrabBag, "LinkChainName") >>= sLinkChainName;
                lcl_getGrabBagValue( aGrabBag, "mso-next-textbox") >>= aChainStruct.s_mso_next_textbox;

                //Sometimes the shape names have not been imported.  If not, we may have a fallback name.
                //Set name later, only if required for linking.
                if( sShapeName.isEmpty() )
                    aChainStruct.bShapeNameSet = false;
                else
                {
                    aChainStruct.bShapeNameSet = true;
                    sLinkChainName = sShapeName;
                }

                if( !sLinkChainName.isEmpty() )
                {
                    aChainStruct.xShape = *iter;
                    aTextFramesForChainingHelper[sLinkChainName] = aChainStruct;
                }
            }

            //if mso-next-textbox tags are provided, create those vml-style links first. Afterwards we will make dml-style id/seq links.
            for (ChainMap::iterator msoIter= aTextFramesForChainingHelper.begin(); msoIter != aTextFramesForChainingHelper.end(); ++msoIter)
            {
                //if no mso-next-textbox, we are done.
                //if it points to itself, we are done.
                if( !msoIter->second.s_mso_next_textbox.isEmpty()
                    && msoIter->second.s_mso_next_textbox != msoIter->first )
                {
                    ChainMap::iterator nextFinder=aTextFramesForChainingHelper.find(msoIter->second.s_mso_next_textbox);
                    if( nextFinder != aTextFramesForChainingHelper.end() )
                    {
                        //if the frames have no name yet, then set them.  LinkDisplayName / ChainName are read-only.
                        if( !msoIter->second.bShapeNameSet )
                        {
                            uno::Reference< container::XNamed > xNamed( msoIter->second.xShape, uno::UNO_QUERY );
                            if ( xNamed.is() )
                            {
                                xNamed->setName( msoIter->first );
                                msoIter->second.bShapeNameSet = true;
                            }
                        }
                        if( !nextFinder->second.bShapeNameSet )
                        {
                            uno::Reference< container::XNamed > xNamed( nextFinder->second.xShape, uno::UNO_QUERY );
                            if ( xNamed.is() )
                            {
                                xNamed->setName( nextFinder->first );
                                nextFinder->second.bShapeNameSet = true;
                            }
                        }

                        uno::Reference<text::XTextContent>  xTextContent(msoIter->second.xShape, uno::UNO_QUERY_THROW);
                        uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);

                        //The reverse chaining happens automatically, so only one direction needs to be set
                        xPropertySet->setPropertyValue(sChainNextName, uno::makeAny(nextFinder->first));

                        //the last item in an mso-next-textbox chain is indistinguishable from id/seq items.  Now that it is handled, remove it.
                        if( nextFinder->second.s_mso_next_textbox.isEmpty() )
                            aTextFramesForChainingHelper.erase(nextFinder->first);
                    }
                }
            }

            //TODO: Perhaps allow reverse sequences when mso-layout-flow-alt = "bottom-to-top"
            const sal_Int32 nDirection = 1;

            //Finally - go through and attach the chains based on matching ID and incremented sequence number (dml-style).
            for (ChainMap::iterator outer_iter= aTextFramesForChainingHelper.begin(); outer_iter != aTextFramesForChainingHelper.end(); ++outer_iter)
            {
                if( outer_iter->second.s_mso_next_textbox.isEmpty() )  //non-empty ones already handled earlier - so skipping them now.
                {
                    for (ChainMap::iterator inner_iter=aTextFramesForChainingHelper.begin(); inner_iter != aTextFramesForChainingHelper.end(); ++inner_iter)
                    {
                        if ( inner_iter->second.nId == outer_iter->second.nId )
                        {
                            if (  inner_iter->second.nSeq == ( outer_iter->second.nSeq + nDirection ) )
                            {
                                uno::Reference<text::XTextContent>  xTextContent(outer_iter->second.xShape, uno::UNO_QUERY_THROW);
                                uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);

                                //The reverse chaining happens automatically, so only one direction needs to be set
                                xPropertySet->setPropertyValue(sChainNextName, uno::makeAny(inner_iter->first));
                                break ; //there cannot be more than one next frame
                            }
                        }
                    }
                }
            }
            m_vTextFramesForChaining.clear(); //clear the vector
        }
        catch (const uno::Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper");
        }
    }

    uno::Reference<beans::XPropertySet> DomainMapper_Impl::FindOrCreateFieldMaster(const sal_Char* pFieldMasterService, const OUString& rFieldMasterName)
    {
        // query master, create if not available
        uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY_THROW );
        uno::Reference< container::XNameAccess > xFieldMasterAccess = xFieldsSupplier->getTextFieldMasters();
        uno::Reference< beans::XPropertySet > xMaster;
        OUString sFieldMasterService( OUString::createFromAscii(pFieldMasterService) );
        OUStringBuffer aFieldMasterName;
        aFieldMasterName.appendAscii( pFieldMasterService );
        aFieldMasterName.append('.');
        aFieldMasterName.append(rFieldMasterName);
        OUString sFieldMasterName = aFieldMasterName.makeStringAndClear();
        if(xFieldMasterAccess->hasByName(sFieldMasterName))
        {
            //get the master
            xMaster.set(xFieldMasterAccess->getByName(sFieldMasterName), uno::UNO_QUERY_THROW);
        }
        else if( m_xTextFactory.is() )
        {
            //create the master
            xMaster.set( m_xTextFactory->createInstance(sFieldMasterService), uno::UNO_QUERY_THROW);
            //set the master's name
            xMaster->setPropertyValue(
                        getPropertyName(PROP_NAME),
                        uno::makeAny(rFieldMasterName));
        }
        return xMaster;
    }

    void DomainMapper_Impl::PushFieldContext()
    {
        m_bParaHadField = true;
        if(m_bDiscardHeaderFooter)
            return;
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().element("pushFieldContext");
    #endif

        uno::Reference<text::XTextCursor> xCrsr;
        if (!m_aTextAppendStack.empty())
        {
            uno::Reference<text::XTextAppend> xTextAppend = m_aTextAppendStack.top().xTextAppend;
            if (xTextAppend.is())
                xCrsr = xTextAppend->createTextCursorByRange(xTextAppend->getEnd());
        }

        uno::Reference< text::XTextRange > xStart;
        if (xCrsr.is())
            xStart = xCrsr->getStart();
        m_aFieldStack.push( new FieldContext( xStart ) );
    }
    /*-------------------------------------------------------------------------
    //the current field context waits for the completion of the command
      -----------------------------------------------------------------------*/
    bool DomainMapper_Impl::IsOpenFieldCommand() const
    {
        return !m_aFieldStack.empty() && !m_aFieldStack.top()->IsCommandCompleted();
    }
    /*-------------------------------------------------------------------------
    //the current field context waits for the completion of the command
      -----------------------------------------------------------------------*/
    bool DomainMapper_Impl::IsOpenField() const
    {
        return !m_aFieldStack.empty();
    }

    // Mark top field context as containing a fixed field
    void DomainMapper_Impl::SetFieldLocked()
    {
        if (IsOpenField())
            m_aFieldStack.top()->SetFieldLocked();
    }

    HeaderFooterContext::HeaderFooterContext(bool bTextInserted)
        : m_bTextInserted(bTextInserted)
    {
    }

    bool HeaderFooterContext::getTextInserted()
    {
        return m_bTextInserted;
    }

    FieldContext::FieldContext(uno::Reference< text::XTextRange > const& xStart)
        : m_bFieldCommandCompleted(false)
        , m_xStartRange( xStart )
        , m_bFieldLocked( false )
    {
        m_pProperties = new PropertyMap();
    }

    FieldContext::~FieldContext()
    {
    }

    void FieldContext::AppendCommand(const OUString& rPart)
    {
        m_sCommand += rPart;
    }

    ::std::vector<OUString> FieldContext::GetCommandParts() const
    {
        ::std::vector<OUString> aResult;
        sal_Int32 nIndex = 0;
        bool bInString = false;
        OUString sPart;
        while (nIndex != -1)
        {
            OUString sToken = GetCommand().getToken(0, ' ', nIndex);
            bool bInStringNext = bInString;

            if (sToken.isEmpty())
                continue;

            if (sToken[0] == '"')
            {
                bInStringNext = true;
                sToken = sToken.copy(1);
            }
            if (sToken.endsWith("\""))
            {
                bInStringNext = false;
                sToken = sToken.copy(0, sToken.getLength() - 1);
            }

            if (bInString)
            {
                sPart += " " + sToken;
                if (!bInStringNext)
                {
                    aResult.push_back(sPart);
                }
            }
            else
            {
                if (bInStringNext)
                {
                    sPart = sToken;
                }
                else
                {
                    aResult.push_back(sToken);
                }
            }

            bInString = bInStringNext;
        }

        return aResult;
    }

    /*-------------------------------------------------------------------------
    //collect the pieces of the command
      -----------------------------------------------------------------------*/
    void DomainMapper_Impl::AppendFieldCommand(OUString const & rPartOfCommand)
    {
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement("appendFieldCommand");
        TagLogger::getInstance().chars(rPartOfCommand);
        TagLogger::getInstance().endElement();
    #endif

        FieldContextPtr pContext = m_aFieldStack.top();
        OSL_ENSURE( pContext.get(), "no field context available");
        if( pContext.get() )
        {
            pContext->AppendCommand( rPartOfCommand );
        }
    }

    typedef std::multimap < sal_Int32, OUString > TOCStyleMap;

    static const FieldConversionMap_t & lcl_GetFieldConversion()
    {
        static const FieldConversionMap_t aFieldConversionMap
        {
    //      {"ADDRESSBLOCK",    {"",                        FIELD_ADDRESSBLOCK  }},
    //      {"ADVANCE",         {"",                        FIELD_ADVANCE       }},
            {"ASK",             {"SetExpression",           FIELD_ASK           }},
            {"AUTONUM",         {"SetExpression",           FIELD_AUTONUM       }},
            {"AUTONUMLGL",      {"SetExpression",           FIELD_AUTONUMLGL    }},
            {"AUTONUMOUT",      {"SetExpression",           FIELD_AUTONUMOUT    }},
            {"AUTHOR",          {"DocInfo.CreateAuthor",    FIELD_AUTHOR        }},
            {"DATE",            {"DateTime",                FIELD_DATE          }},
            {"COMMENTS",        {"DocInfo.Description",     FIELD_COMMENTS      }},
            {"CREATEDATE",      {"DocInfo.CreateDateTime",  FIELD_CREATEDATE    }},
            {"DOCPROPERTY",     {"",                        FIELD_DOCPROPERTY   }},
            {"DOCVARIABLE",     {"User",                    FIELD_DOCVARIABLE   }},
            {"EDITTIME",        {"DocInfo.EditTime",        FIELD_EDITTIME      }},
            {"EQ",              {"",                        FIELD_EQ            }},
            {"FILLIN",          {"Input",                   FIELD_FILLIN        }},
            {"FILENAME",        {"FileName",                FIELD_FILENAME      }},
    //      {"FILESIZE",        {"",                        FIELD_FILESIZE      }},
            {"FORMULA",         {"TableFormula",            FIELD_FORMULA       }},
            {"FORMCHECKBOX",    {"",                        FIELD_FORMCHECKBOX  }},
            {"FORMDROPDOWN",    {"DropDown",                FIELD_FORMDROPDOWN  }},
            {"FORMTEXT",        {"Input",                   FIELD_FORMTEXT      }},
    //      {"GOTOBUTTON",      {"",                        FIELD_GOTOBUTTON    }},
            {"HYPERLINK",       {"",                        FIELD_HYPERLINK     }},
            {"IF",              {"ConditionalText",         FIELD_IF            }},
    //      {"INFO",            {"",                        FIELD_INFO          }},
            {"INCLUDEPICTURE",  {"",                        FIELD_INCLUDEPICTURE}},
            {"KEYWORDS",        {"DocInfo.KeyWords",        FIELD_KEYWORDS      }},
            {"LASTSAVEDBY",     {"DocInfo.ChangeAuthor",    FIELD_LASTSAVEDBY   }},
            {"MACROBUTTON",     {"Macro",                   FIELD_MACROBUTTON   }},
            {"MERGEFIELD",      {"Database",                FIELD_MERGEFIELD    }},
            {"MERGEREC",        {"DatabaseNumberOfSet",     FIELD_MERGEREC      }},
    //      {"MERGESEQ",        {"",                        FIELD_MERGESEQ      }},
            {"NEXT",            {"DatabaseNextSet",         FIELD_NEXT          }},
            {"NEXTIF",          {"DatabaseNextSet",         FIELD_NEXTIF        }},
            {"PAGE",            {"PageNumber",              FIELD_PAGE          }},
            {"PAGEREF",         {"GetReference",            FIELD_PAGEREF       }},
            {"REF",             {"GetReference",            FIELD_REF           }},
            {"REVNUM",          {"DocInfo.Revision",        FIELD_REVNUM        }},
            {"SAVEDATE",        {"DocInfo.Change",          FIELD_SAVEDATE      }},
    //      {"SECTION",         {"",                        FIELD_SECTION       }},
    //      {"SECTIONPAGES",    {"",                        FIELD_SECTIONPAGES  }},
            {"SEQ",             {"SetExpression",           FIELD_SEQ           }},
            {"SET",             {"SetExpression",           FIELD_SET           }},
    //      {"SKIPIF",          {"",                        FIELD_SKIPIF        }},
    //      {"STYLEREF",        {"",                        FIELD_STYLEREF      }},
            {"SUBJECT",         {"DocInfo.Subject",         FIELD_SUBJECT       }},
            {"SYMBOL",          {"",                        FIELD_SYMBOL        }},
            {"TEMPLATE",        {"TemplateName",            FIELD_TEMPLATE      }},
            {"TIME",            {"DateTime",                FIELD_TIME          }},
            {"TITLE",           {"DocInfo.Title",           FIELD_TITLE         }},
            {"USERINITIALS",    {"Author",                  FIELD_USERINITIALS  }},
    //      {"USERADDRESS",     {"",                        FIELD_USERADDRESS   }},
            {"USERNAME",        {"Author",                  FIELD_USERNAME      }},

            {"TOC",         {"com.sun.star.text.ContentIndex",          FIELD_TOC           }},
            {"TC",          {"com.sun.star.text.ContentIndexMark",      FIELD_TC            }},
            {"NUMCHARS",    {"CharacterCount",                          FIELD_NUMCHARS      }},
            {"NUMWORDS",    {"WordCount",                               FIELD_NUMWORDS      }},
            {"NUMPAGES",    {"PageCount",                               FIELD_NUMPAGES      }},
            {"INDEX",       {"com.sun.star.text.DocumentIndex",         FIELD_INDEX         }},
            {"XE",          {"com.sun.star.text.DocumentIndexMark",     FIELD_XE            }},
            {"BIBLIOGRAPHY",{"com.sun.star.text.Bibliography",          FIELD_BIBLIOGRAPHY  }},
            {"CITATION",    {"com.sun.star.text.TextField.Bibliography",FIELD_CITATION      }},
        };

        return aFieldConversionMap;
    }

    static const FieldConversionMap_t & lcl_GetEnhancedFieldConversion()
    {
        static const FieldConversionMap_t aEnhancedFieldConversionMap =
        {
            {"FORMCHECKBOX", {"FormFieldmark", FIELD_FORMCHECKBOX}},
            {"FORMDROPDOWN", {"FormFieldmark", FIELD_FORMDROPDOWN}},
            {"FORMTEXT",     {"Fieldmark",     FIELD_FORMTEXT}},
        };

        return aEnhancedFieldConversionMap;
    }

    void DomainMapper_Impl::handleFieldSet
        (const FieldContextPtr& pContext,
         uno::Reference< uno::XInterface > const & xFieldInterface,
         uno::Reference< beans::XPropertySet > const& xFieldProperties)
    {
        OUString sVariable, sHint;

        sVariable = lcl_ExctractVariableAndHint(pContext->GetCommand(), sHint);

        // remove surrounding "" if exists
        if( sHint.getLength() >= 2 && sHint.startsWith("\"") )
        {
            sHint = sHint.trim().copy(1, sHint.getLength() - 2);
        }

        // determine field master name
        uno::Reference< beans::XPropertySet > xMaster =
            FindOrCreateFieldMaster
            ("com.sun.star.text.FieldMaster.SetExpression", sVariable );

        // a set field is a string
        xMaster->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::makeAny(text::SetVariableType::STRING));

        // attach the master to the field
        uno::Reference< text::XDependentTextField > xDependentField
            ( xFieldInterface, uno::UNO_QUERY_THROW );
        xDependentField->attachTextFieldMaster( xMaster );

        xFieldProperties->setPropertyValue(getPropertyName(PROP_HINT), uno::makeAny( sHint ));
        xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), uno::makeAny( sHint ));
        xFieldProperties->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::makeAny(text::SetVariableType::STRING));

        // Mimic MS Word behavior (hide the SET)
        xFieldProperties->setPropertyValue(getPropertyName(PROP_IS_VISIBLE), uno::makeAny(false));
    }

    void DomainMapper_Impl::handleFieldAsk
        (const FieldContextPtr& pContext,
         uno::Reference< uno::XInterface > & xFieldInterface,
         uno::Reference< beans::XPropertySet > const& xFieldProperties)
    {
        //doesn the command contain a variable name?
        OUString sVariable, sHint;

        sVariable = lcl_ExctractVariableAndHint( pContext->GetCommand(),
            sHint );
        if(!sVariable.isEmpty())
        {
            // determine field master name
            uno::Reference< beans::XPropertySet > xMaster =
                FindOrCreateFieldMaster
                ("com.sun.star.text.FieldMaster.SetExpression", sVariable );
            // An ASK field is always a string of characters
            xMaster->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::makeAny(text::SetVariableType::STRING));

            // attach the master to the field
            uno::Reference< text::XDependentTextField > xDependentField
                ( xFieldInterface, uno::UNO_QUERY_THROW );
            xDependentField->attachTextFieldMaster( xMaster );

            // set input flag at the field
            xFieldProperties->setPropertyValue(
                getPropertyName(PROP_IS_INPUT), uno::makeAny( true ));
            // set the prompt
            xFieldProperties->setPropertyValue(
                getPropertyName(PROP_HINT),
                uno::makeAny( sHint ));
            xFieldProperties->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::makeAny(text::SetVariableType::STRING));
            // The ASK has no field value to display
            xFieldProperties->setPropertyValue(getPropertyName(PROP_IS_VISIBLE), uno::makeAny(false));
        }
        else
        {
            //don't insert the field
            //todo: maybe import a 'normal' input field here?
            xFieldInterface = nullptr;
        }
    }

    void DomainMapper_Impl::handleFieldFormula
        (const FieldContextPtr& pContext,
         uno::Reference< beans::XPropertySet > const& xFieldProperties)
    {
        OUString command = pContext->GetCommand().trim();
        // command must contains = and at least another char
        if (command.getLength() < 2)
            return;

        // we don't copy the = symbol from the command
        OUString formula = command.copy(1);

        xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), uno::makeAny(formula));
        xFieldProperties->setPropertyValue(getPropertyName(PROP_NUMBER_FORMAT), uno::makeAny(sal_Int32(0)));
        xFieldProperties->setPropertyValue("IsShowFormula", uno::makeAny(false));
    }

    void  DomainMapper_Impl::handleRubyEQField( const FieldContextPtr& pContext)
    {
        const OUString & rCommand(pContext->GetCommand());
        sal_Int32 nIndex = 0, nEnd = 0;
        RubyInfo aInfo ;
        nIndex = rCommand.indexOf("\\* jc" );
        if (nIndex != -1)
        {
            nIndex += 5;
            sal_uInt32  nJc = rCommand.getToken(0, ' ',nIndex).toInt32();
            const   sal_Int32   aRubyAlignValues[] =
            {
                NS_ooxml::LN_Value_ST_RubyAlign_center,
                NS_ooxml::LN_Value_ST_RubyAlign_distributeLetter,
                NS_ooxml::LN_Value_ST_RubyAlign_distributeSpace,
                NS_ooxml::LN_Value_ST_RubyAlign_left,
                NS_ooxml::LN_Value_ST_RubyAlign_right,
                NS_ooxml::LN_Value_ST_RubyAlign_rightVertical,
            };
            aInfo.nRubyAlign = aRubyAlignValues[(nJc<SAL_N_ELEMENTS(aRubyAlignValues))?nJc:0];
        }

        // we don't parse or use the font field in rCommand

        nIndex = rCommand.indexOf("\\* hps" );
        if (nIndex != -1)
        {
            nIndex += 6;
            aInfo.nHps = rCommand.getToken(0, ' ',nIndex).toInt32();
        }

        nIndex = rCommand.indexOf("\\o");
        if (nIndex == -1 || (nIndex = rCommand.indexOf('(', nIndex)) == -1 || (nEnd = rCommand.lastIndexOf(')'))==-1 || nEnd <= nIndex)
            return;

        OUString sRubyParts = rCommand.copy(nIndex+1,nEnd-nIndex-1);
        nIndex = 0;
        OUString sPart1 = sRubyParts.getToken(0, ',', nIndex);
        OUString sPart2 = sRubyParts.getToken(0, ',', nIndex);
        if ((nIndex = sPart1.indexOf('(')) != -1 && (nEnd = sPart1.lastIndexOf(')'))!=-1  && nEnd > nIndex)
        {
            aInfo.sRubyText = sPart1.copy(nIndex+1,nEnd-nIndex-1);
        }

        PropertyMapPtr pRubyContext(new PropertyMap());
        pRubyContext->InsertProps(GetTopContext());
        if (aInfo.nHps > 0)
        {
            double fVal = double(aInfo.nHps) / 2.;
            uno::Any aVal = uno::makeAny( fVal );

            pRubyContext->Insert(PROP_CHAR_HEIGHT, aVal);
            pRubyContext->Insert(PROP_CHAR_HEIGHT_ASIAN, aVal);
        }
        PropertyValueVector_t aProps = comphelper::sequenceToContainer< PropertyValueVector_t >(pRubyContext->GetPropertyValues());
        aInfo.sRubyStyle = m_rDMapper.getOrCreateCharStyle(aProps, /*bAlwaysCreate=*/false);
        PropertyMapPtr pCharContext(new PropertyMap());
        if (m_pLastCharacterContext.get())
            pCharContext->InsertProps(m_pLastCharacterContext);
        pCharContext->InsertProps(pContext->getProperties());
        pCharContext->Insert(PROP_RUBY_TEXT, uno::makeAny( aInfo.sRubyText ) );
        pCharContext->Insert(PROP_RUBY_ADJUST, uno::makeAny(static_cast<sal_Int16>(ConversionHelper::convertRubyAlign(aInfo.nRubyAlign))));
        if ( aInfo.nRubyAlign == NS_ooxml::LN_Value_ST_RubyAlign_rightVertical )
            pCharContext->Insert(PROP_RUBY_POSITION, uno::makeAny(css::text::RubyPosition::INTER_CHARACTER));
        pCharContext->Insert(PROP_RUBY_STYLE, uno::makeAny(aInfo.sRubyStyle));
        appendTextPortion(sPart2, pCharContext);

    }

    void DomainMapper_Impl::handleAutoNum
        (const FieldContextPtr& pContext,
        uno::Reference< uno::XInterface > const & xFieldInterface,
        uno::Reference< beans::XPropertySet > const& xFieldProperties)
    {
        //create a sequence field master "AutoNr"
        uno::Reference< beans::XPropertySet > xMaster =
        FindOrCreateFieldMaster
            ("com.sun.star.text.FieldMaster.SetExpression",
            "AutoNr");

        xMaster->setPropertyValue( getPropertyName(PROP_SUB_TYPE),
            uno::makeAny(text::SetVariableType::SEQUENCE));

        //apply the numbering type
        xFieldProperties->setPropertyValue(
            getPropertyName(PROP_NUMBERING_TYPE),
            uno::makeAny( lcl_ParseNumberingType(pContext->GetCommand()) ));
            // attach the master to the field
        uno::Reference< text::XDependentTextField > xDependentField
            ( xFieldInterface, uno::UNO_QUERY_THROW );
        xDependentField->attachTextFieldMaster( xMaster );
    }

    void DomainMapper_Impl::handleAuthor
        (OUString const& rFirstParam,
         uno::Reference< beans::XPropertySet > const& xFieldProperties,
         FieldId  eFieldId )
    {
        if ( eFieldId != FIELD_USERINITIALS )
            xFieldProperties->setPropertyValue
                ( getPropertyName(PROP_FULL_NAME), uno::makeAny( true ));

        if (!rFirstParam.isEmpty())
        {
            xFieldProperties->setPropertyValue(
                    getPropertyName( PROP_IS_FIXED ),
                    uno::makeAny( true ));
            //PROP_CURRENT_PRESENTATION is set later anyway
        }
    }

        void DomainMapper_Impl::handleDocProperty
            (const FieldContextPtr& pContext,
            OUString const& rFirstParam,
            uno::Reference< uno::XInterface > & xFieldInterface)
    {
        //some docproperties should be imported as document statistic fields, some as DocInfo fields
        //others should be user fields
        if (rFirstParam.isEmpty())
            return;

        #define SET_ARABIC      0x01
        #define SET_DATE        0x04
        struct DocPropertyMap
        {
            const sal_Char* pDocPropertyName;
            const sal_Char* pServiceName;
            sal_uInt8       nFlags;
        };
        static const DocPropertyMap aDocProperties[] =
        {
            {"CreateTime",       "DocInfo.CreateDateTime",  SET_DATE},
            {"Characters",       "CharacterCount",          SET_ARABIC},
            {"Comments",         "DocInfo.Description",     0},
            {"Keywords",         "DocInfo.KeyWords",        0},
            {"LastPrinted",      "DocInfo.PrintDateTime",   0},
            {"LastSavedBy",      "DocInfo.ChangeAuthor",    0},
            {"LastSavedTime",    "DocInfo.ChangeDateTime",  SET_DATE},
            {"Paragraphs",       "ParagraphCount",          SET_ARABIC},
            {"RevisionNumber",   "DocInfo.Revision",        0},
            {"Subject",          "DocInfo.Subject",         0},
            {"Template",         "TemplateName",            0},
            {"Title",            "DocInfo.Title",           0},
            {"TotalEditingTime", "DocInfo.EditTime",        0},
            {"Words",            "WordCount",               SET_ARABIC}

            //other available DocProperties:
            //Bytes, Category, CharactersWithSpaces, Company
            //HyperlinkBase,
            //Lines, Manager, NameofApplication, ODMADocId, Pages,
            //Security,
        };
        uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(m_xTextDocument, uno::UNO_QUERY);
        uno::Reference<document::XDocumentProperties> xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties();
        uno::Reference<beans::XPropertySet>  xUserDefinedProps(xDocumentProperties->getUserDefinedProperties(), uno::UNO_QUERY_THROW);
        uno::Reference<beans::XPropertySetInfo> xPropertySetInfo =  xUserDefinedProps->getPropertySetInfo();
        //search for a field mapping
        OUString sFieldServiceName;
        sal_uInt16 nMap = 0;
        for( ; nMap < SAL_N_ELEMENTS(aDocProperties); ++nMap )
        {
            if ((rFirstParam.equalsAscii(aDocProperties[nMap].pDocPropertyName)) && (!xPropertySetInfo->hasPropertyByName(rFirstParam)))
            {
                sFieldServiceName =
                OUString::createFromAscii
                (aDocProperties[nMap].pServiceName);
                break;
            }
        }
        OUString sServiceName("com.sun.star.text.TextField.");
        bool bIsCustomField = false;
        if(sFieldServiceName.isEmpty())
        {
            //create a custom property field
            sServiceName += "DocInfo.Custom";
            bIsCustomField = true;
        }
        else
        {
            sServiceName += sFieldServiceName;
        }
        if (m_xTextFactory.is())
            xFieldInterface = m_xTextFactory->createInstance(sServiceName);
        uno::Reference<beans::XPropertySet> xFieldProperties =
            uno::Reference< beans::XPropertySet >( xFieldInterface,
                uno::UNO_QUERY_THROW);
        if( bIsCustomField )
        {
            xFieldProperties->setPropertyValue(
                getPropertyName(PROP_NAME), uno::makeAny(rFirstParam));
            pContext->SetCustomField( xFieldProperties );
        }
        else
        {
            if(0 != (aDocProperties[nMap].nFlags & SET_ARABIC))
                xFieldProperties->setPropertyValue(
                    getPropertyName(PROP_NUMBERING_TYPE),
                    uno::makeAny( style::NumberingType::ARABIC ));
            else if(0 != (aDocProperties[nMap].nFlags & SET_DATE))
            {
                xFieldProperties->setPropertyValue(
                    getPropertyName(PROP_IS_DATE),
                        uno::makeAny( true ));
                SetNumberFormat( pContext->GetCommand(), xFieldProperties );
            }
        }

    #undef SET_ARABIC
    #undef SET_DATE
    }

    static uno::Sequence< beans::PropertyValues > lcl_createTOXLevelHyperlinks( bool bHyperlinks, const OUString& sChapterNoSeparator,
                                       const uno::Sequence< beans::PropertyValues >& aLevel )
    {
        //create a copy of the level and add two new entries - hyperlink start and end
        bool bChapterNoSeparator  = !sChapterNoSeparator.isEmpty();
        sal_Int32 nAdd = (bHyperlinks && bChapterNoSeparator) ? 4 : 2;
        uno::Sequence< beans::PropertyValues > aNewLevel( aLevel.getLength() + nAdd);
        beans::PropertyValues* pNewLevel = aNewLevel.getArray();
        if( bHyperlinks )
        {
            beans::PropertyValues aHyperlink(1);
            aHyperlink[0].Name = getPropertyName( PROP_TOKEN_TYPE );
            aHyperlink[0].Value <<= getPropertyName( PROP_TOKEN_HYPERLINK_START );
            pNewLevel[0] = aHyperlink;
            aHyperlink[0].Value <<= getPropertyName( PROP_TOKEN_HYPERLINK_END );
            pNewLevel[aNewLevel.getLength() -1] = aHyperlink;
        }
        if( bChapterNoSeparator )
        {
            beans::PropertyValues aChapterNo(2);
            aChapterNo[0].Name = getPropertyName( PROP_TOKEN_TYPE );
            aChapterNo[0].Value <<= getPropertyName( PROP_TOKEN_CHAPTER_INFO );
            aChapterNo[1].Name = getPropertyName( PROP_CHAPTER_FORMAT );
            //todo: is ChapterFormat::Number correct?
            aChapterNo[1].Value <<= sal_Int16(text::ChapterFormat::NUMBER);
            pNewLevel[aNewLevel.getLength() - (bHyperlinks ? 4 : 2) ] = aChapterNo;

            beans::PropertyValues aChapterSeparator(2);
            aChapterSeparator[0].Name = getPropertyName( PROP_TOKEN_TYPE );
            aChapterSeparator[0].Value <<= getPropertyName( PROP_TOKEN_TEXT );
            aChapterSeparator[1].Name = getPropertyName( PROP_TEXT );
            aChapterSeparator[1].Value <<= sChapterNoSeparator;
            pNewLevel[aNewLevel.getLength() - (bHyperlinks ? 3 : 1)] = aChapterSeparator;
        }
        //copy the 'old' entries except the last (page no)
        for( sal_Int32 nToken = 0; nToken < aLevel.getLength() - 1; ++nToken)
        {
            pNewLevel[nToken + 1] = aLevel[nToken];
        }
        //copy page no entry (last or last but one depending on bHyperlinks
        sal_Int32 nPageNo = aNewLevel.getLength() - (bHyperlinks ? 2 : 3);
        pNewLevel[nPageNo] = aLevel[aLevel.getLength() - 1];

        return aNewLevel;
    }

    void DomainMapper_Impl::handleToc
        (const FieldContextPtr& pContext,
        const OUString & sTOCServiceName)
    {
        OUString sValue;
        m_bStartTOC = true;
        if(m_bInHeaderFooterImport)
            m_bStartTOCHeaderFooter = true;
        bool bTableOfFigures = false;
        bool bHyperlinks = false;
        bool bFromOutline = false;
        bool bFromEntries = false;
        bool bHideTabLeaderPageNumbers = false ;
        bool bIsTabEntry = false ;
        bool bNewLine = false ;
        bool bParagraphOutlineLevel = false;

        sal_Int16 nMaxLevel = 10;
        OUString sTemplate;
        OUString sChapterNoSeparator;
        OUString sFigureSequence;
        uno::Reference< beans::XPropertySet > xTOC;
        OUString aBookmarkName;

    //                  \a Builds a table of figures but does not include the captions's label and number
        if( lcl_FindInCommand( pContext->GetCommand(), 'a', sValue ))
        { //make it a table of figures
            bTableOfFigures = true;
        }
    //                  \b Uses a bookmark to specify area of document from which to build table of contents
        if( lcl_FindInCommand( pContext->GetCommand(), 'b', sValue ))
        {
            aBookmarkName = sValue.trim().replaceAll("\"","");
        }
        if( lcl_FindInCommand( pContext->GetCommand(), 'c', sValue ))
    //                  \c Builds a table of figures of the given label
        {
                            //todo: sValue contains the label's name
            bTableOfFigures = true;
            sFigureSequence = sValue.trim();
            sFigureSequence = sFigureSequence.replaceAll("\"", "").replaceAll("'","");
        }
    //                  \d Defines the separator between sequence and page numbers
        if( lcl_FindInCommand( pContext->GetCommand(), 'd', sValue ))
        {
                            //todo: insert the chapter number into each level and insert the separator additionally
            sChapterNoSeparator = sValue;
        }
    //                  \f Builds a table of contents using TC entries instead of outline levels
        if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
        {
                            //todo: sValue can contain a TOC entry identifier - use unclear
            bFromEntries = true;
        }
    //                  \h Hyperlinks the entries and page numbers within the table of contents
        if( lcl_FindInCommand( pContext->GetCommand(), 'h', sValue ))
        {
                            //todo: make all entries to hyperlinks
            bHyperlinks = true;
        }
    //                  \l Defines the TC entries field level used to build a table of contents
    //                    if( lcl_FindInCommand( pContext->GetCommand(), 'l', sValue ))
    //                    {
                                //todo: entries can only be included completely
    //                    }
    //                  \n Builds a table of contents or a range of entries, such as 1-9 in a table of contents without page numbers
    //                    if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
    //                    {
                            //todo: what does the description mean?
    //                    }
    //                  \o  Builds a table of contents by using outline levels instead of TC entries
        if( lcl_FindInCommand( pContext->GetCommand(), 'o', sValue ))
        {
            bFromOutline = true;
            if (sValue.isEmpty())
                nMaxLevel = WW_OUTLINE_MAX;
            else
            {
                sal_Int32 nIndex = 0;
                sValue.getToken( 0, '-', nIndex );
                nMaxLevel = static_cast<sal_Int16>(nIndex != -1 ? sValue.copy(nIndex).toInt32() : 0);
            }
        }
    //                  \p Defines the separator between the table entry and its page number
    //                  \s  Builds a table of contents by using a sequence type
    //                  \t  Builds a table of contents by using style names other than the standard outline styles
        if( lcl_FindInCommand( pContext->GetCommand(), 't', sValue ))
        {
            sal_Int32 nPos = 0;
            OUString sToken = sValue.getToken( 1, '"', nPos);
            sTemplate = sToken.isEmpty() ? sValue : sToken;
        }
    //                  \u  Builds a table of contents by using the applied paragraph outline level
        if( lcl_FindInCommand( pContext->GetCommand(), 'u', sValue ))
        {
            bFromOutline = true;
            bParagraphOutlineLevel = true;
                            //todo: what doesn 'the applied paragraph outline level' refer to?
        }
    //    \w Preserve tab characters within table entries
        if( lcl_FindInCommand( pContext->GetCommand(), 'w', sValue ))
        {
            bIsTabEntry = true ;
        }
    //                  \x Preserve newline characters within table entries
        if( lcl_FindInCommand( pContext->GetCommand(), 'x', sValue ))
        {
            bNewLine = true ;
        }
    //                  \z Hides page numbers within the table of contents when shown in Web Layout View
                        if( lcl_FindInCommand( pContext->GetCommand(), 'z', sValue ))
                        {
                            bHideTabLeaderPageNumbers = true ;
                        }

                        //if there's no option then it should be created from outline
        if( !bFromOutline && !bFromEntries && sTemplate.isEmpty()  )
            bFromOutline = true;

        if (m_xTextFactory.is())
            xTOC.set(
                    m_xTextFactory->createInstance
                    ( bTableOfFigures ?
                      "com.sun.star.text.IllustrationsIndex"
                      : sTOCServiceName),
                    uno::UNO_QUERY_THROW);
        if (xTOC.is())
            xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::makeAny(OUString()));
        if (!aBookmarkName.isEmpty())
            xTOC->setPropertyValue(getPropertyName(PROP_TOC_BOOKMARK), uno::makeAny(aBookmarkName));
        if( !bTableOfFigures && xTOC.is() )
        {
            xTOC->setPropertyValue( getPropertyName( PROP_LEVEL ), uno::makeAny( nMaxLevel ) );
            xTOC->setPropertyValue( getPropertyName( PROP_CREATE_FROM_OUTLINE ), uno::makeAny( bFromOutline ));
            xTOC->setPropertyValue( getPropertyName( PROP_CREATE_FROM_MARKS ), uno::makeAny( bFromEntries ));
            xTOC->setPropertyValue( getPropertyName( PROP_HIDE_TAB_LEADER_AND_PAGE_NUMBERS ), uno::makeAny( bHideTabLeaderPageNumbers ));
            xTOC->setPropertyValue( getPropertyName( PROP_TAB_IN_TOC ), uno::makeAny( bIsTabEntry ));
            xTOC->setPropertyValue( getPropertyName( PROP_TOC_NEW_LINE ), uno::makeAny( bNewLine ));
            xTOC->setPropertyValue( getPropertyName( PROP_TOC_PARAGRAPH_OUTLINE_LEVEL ), uno::makeAny( bParagraphOutlineLevel ));
            if( !sTemplate.isEmpty() )
            {
                                //the string contains comma separated the names and related levels
                                //like: "Heading 1,1,Heading 2,2"
                TOCStyleMap aMap;
                sal_Int32 nLevel;
                sal_Int32 nPosition = 0;
                while( nPosition >= 0)
                {
                    OUString sStyleName = sTemplate.getToken( 0, ',', nPosition );
                                    //empty tokens should be skipped
                    while( sStyleName.isEmpty() && nPosition > 0 )
                        sStyleName = sTemplate.getToken( 0, ',', nPosition );
                    nLevel = sTemplate.getToken( 0, ',', nPosition ).toInt32();
                    if( !nLevel )
                        nLevel = 1;
                    if( !sStyleName.isEmpty() )
                        aMap.emplace(nLevel, sStyleName);
                }
                uno::Reference< container::XIndexReplace> xParaStyles;
                xTOC->getPropertyValue(getPropertyName(PROP_LEVEL_PARAGRAPH_STYLES)) >>= xParaStyles;
                for( nLevel = 1; nLevel < 10; ++nLevel)
                {
                    sal_Int32 nLevelCount = aMap.count( nLevel );
                    if( nLevelCount  )
                    {
                        TOCStyleMap::iterator aTOCStyleIter = aMap.find( nLevel );

                        uno::Sequence< OUString> aStyles( nLevelCount );
                        for ( sal_Int32 nStyle = 0; nStyle < nLevelCount; ++nStyle, ++aTOCStyleIter )
                        {
                            aStyles[nStyle] = aTOCStyleIter->second;
                        }
                        xParaStyles->replaceByIndex(nLevel - 1, uno::makeAny(aStyles));
                    }
                }
                xTOC->setPropertyValue(getPropertyName(PROP_CREATE_FROM_LEVEL_PARAGRAPH_STYLES), uno::makeAny( true ));

            }
            if(bHyperlinks  || !sChapterNoSeparator.isEmpty())
            {
                uno::Reference< container::XIndexReplace> xLevelFormats;
                xTOC->getPropertyValue(getPropertyName(PROP_LEVEL_FORMAT)) >>= xLevelFormats;
                sal_Int32 nLevelCount = xLevelFormats->getCount();
                                //start with level 1, 0 is the header level
                for( sal_Int32 nLevel = 1; nLevel < nLevelCount; ++nLevel)
                {
                    uno::Sequence< beans::PropertyValues > aLevel;
                    xLevelFormats->getByIndex( nLevel ) >>= aLevel;

                    uno::Sequence< beans::PropertyValues > aNewLevel = lcl_createTOXLevelHyperlinks(
                                                        bHyperlinks, sChapterNoSeparator,
                                                        aLevel );
                    xLevelFormats->replaceByIndex( nLevel, uno::makeAny( aNewLevel ) );
                }
            }
        }
        else if (bTableOfFigures && xTOC.is())
        {
            if (!sFigureSequence.isEmpty())
                xTOC->setPropertyValue(getPropertyName(PROP_LABEL_CATEGORY),
                                       uno::makeAny(sFigureSequence));

            if ( bHyperlinks )
            {
                uno::Reference< container::XIndexReplace> xLevelFormats;
                xTOC->getPropertyValue(getPropertyName(PROP_LEVEL_FORMAT)) >>= xLevelFormats;
                uno::Sequence< beans::PropertyValues > aLevel;
                xLevelFormats->getByIndex( 1 ) >>= aLevel;

                uno::Sequence< beans::PropertyValues > aNewLevel = lcl_createTOXLevelHyperlinks(
                                                    bHyperlinks, sChapterNoSeparator,
                                                    aLevel );
                xLevelFormats->replaceByIndex( 1, uno::makeAny( aNewLevel ) );
            }
        }
        pContext->SetTOC( xTOC );
        m_bParaHadField = false;

        if (m_aTextAppendStack.empty())
            return;

        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;
            }
        }
    }

    void DomainMapper_Impl::handleBibliography
        (const FieldContextPtr& pContext,
        const OUString & sTOCServiceName)
    {
        uno::Reference< beans::XPropertySet > xTOC;
        m_bStartTOC = true;
        m_bStartBibliography = true;
        if (m_xTextFactory.is())
            xTOC.set(
                    m_xTextFactory->createInstance(
                    sTOCServiceName),
                    uno::UNO_QUERY_THROW);
        if (xTOC.is())
            xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::makeAny(OUString()));

        pContext->SetTOC( xTOC );
        m_bParaHadField = false;

        uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY );
        appendTextContent(xToInsert, uno::Sequence< beans::PropertyValue >() );
    }

    void DomainMapper_Impl::handleIndex
        (const FieldContextPtr& pContext,
        const OUString & sTOCServiceName)
    {
        uno::Reference< beans::XPropertySet > xTOC;
        m_bStartTOC = true;
        m_bStartIndex = true;
        OUString sValue;
        OUString sIndexEntryType = "I"; // Default value for field flag '\f' is 'I'.

        if (m_xTextFactory.is())
            xTOC.set(
                    m_xTextFactory->createInstance(
                    sTOCServiceName),
                    uno::UNO_QUERY_THROW);
        if (xTOC.is())
        {
            xTOC->setPropertyValue(getPropertyName( PROP_TITLE ), uno::makeAny(OUString()));

            if( lcl_FindInCommand( pContext->GetCommand(), 'r', sValue ))
            {
                xTOC->setPropertyValue("IsCommaSeparated", uno::makeAny(true));
            }
            if( lcl_FindInCommand( pContext->GetCommand(), 'h', sValue ))
            {
                xTOC->setPropertyValue("UseAlphabeticalSeparators", uno::makeAny(true));
            }
            if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
            {
                if(!sValue.isEmpty())
                    sIndexEntryType = sValue ;
                xTOC->setPropertyValue(getPropertyName( PROP_INDEX_ENTRY_TYPE ), uno::makeAny(sIndexEntryType));
            }
        }
        pContext->SetTOC( xTOC );
        m_bParaHadField = false;

        uno::Reference< text::XTextContent > xToInsert( xTOC, uno::UNO_QUERY );
        appendTextContent(xToInsert, uno::Sequence< beans::PropertyValue >() );

        if( lcl_FindInCommand( pContext->GetCommand(), 'c', sValue ))
        {
            sValue = sValue.replaceAll("\"", "");
            uno::Reference<text::XTextColumns> xTextColumns;
            xTOC->getPropertyValue(getPropertyName( PROP_TEXT_COLUMNS )) >>= xTextColumns;
            if (xTextColumns.is())
            {
                xTextColumns->setColumnCount( sValue.toInt32() );
                xTOC->setPropertyValue( getPropertyName( PROP_TEXT_COLUMNS ), uno::makeAny( xTextColumns ) );
            }
        }
    }

    void DomainMapper_Impl::CloseFieldCommand()
    {
        if(m_bDiscardHeaderFooter)
            return;
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().element("closeFieldCommand");
    #endif

        FieldContextPtr pContext;
        if(!m_aFieldStack.empty())
            pContext = m_aFieldStack.top();
        OSL_ENSURE( pContext.get(), "no field context available");
        if( pContext.get() )
        {
            m_bSetUserFieldContent = false;
            m_bSetCitation = false;
            m_bSetDateValue = false;
            const FieldConversionMap_t& aFieldConversionMap = lcl_GetFieldConversion();

            try
            {
                uno::Reference< uno::XInterface > xFieldInterface;

                std::tuple<OUString, std::vector<OUString>, std::vector<OUString> > const
                    field(splitFieldCommand(pContext->GetCommand()));
                OUString const sFirstParam(std::get<1>(field).empty()
                        ? OUString() : std::get<1>(field).front());

                FieldConversionMap_t::const_iterator const aIt =
                    aFieldConversionMap.find(std::get<0>(field));
                if(aIt != aFieldConversionMap.end())
                {
                    pContext->SetFieldId(aIt->second.eFieldId);

                    bool bCreateEnhancedField = false;
                    uno::Reference< beans::XPropertySet > xFieldProperties;
                    bool bCreateField = true;
                    switch (aIt->second.eFieldId)
                    {
                    case FIELD_HYPERLINK:
                    case FIELD_DOCPROPERTY:
                    case FIELD_TOC:
                    case FIELD_INDEX:
                    case FIELD_XE:
                    case FIELD_BIBLIOGRAPHY:
                    case FIELD_CITATION:
                    case FIELD_TC:
                    case FIELD_EQ:
                    case FIELD_INCLUDEPICTURE:
                    case FIELD_SYMBOL:
                            bCreateField = false;
                            break;
                    case FIELD_FORMCHECKBOX :
                    case FIELD_FORMTEXT :
                    case FIELD_FORMDROPDOWN :
                    {
                        // If we use 'enhanced' fields then FIELD_FORMCHECKBOX,
                        // FIELD_FORMTEXT & FIELD_FORMDROPDOWN are treated specially
                        if ( m_bUsingEnhancedFields  )
                        {
                            bCreateField = false;
                            bCreateEnhancedField = true;
                        }
                        // for non enhanced fields checkboxes are displayed
                        // as an awt control not a field
                        else if ( aIt->second.eFieldId == FIELD_FORMCHECKBOX )
                            bCreateField = false;
                        break;
                    }
                    default:
                        break;
                    }
                    if (m_bStartTOC && (aIt->second.eFieldId == FIELD_PAGEREF) )
                    {
                        bCreateField = false;
                    }

                    if( bCreateField || bCreateEnhancedField )
                    {
                        //add the service prefix
                        OUString sServiceName("com.sun.star.text.");
                        if ( bCreateEnhancedField )
                        {
                            const FieldConversionMap_t& aEnhancedFieldConversionMap = lcl_GetEnhancedFieldConversion();
                            FieldConversionMap_t::const_iterator aEnhancedIt =
                                aEnhancedFieldConversionMap.find(std::get<0>(field));
                            if ( aEnhancedIt != aEnhancedFieldConversionMap.end())
                                sServiceName += OUString::createFromAscii(aEnhancedIt->second.cFieldServiceName );
                        }
                        else
                        {
                            sServiceName += "TextField.";
                            sServiceName += OUString::createFromAscii(aIt->second.cFieldServiceName );
                        }

    #ifdef DEBUG_WRITERFILTER
                        TagLogger::getInstance().startElement("fieldService");
                        TagLogger::getInstance().chars(sServiceName);
                        TagLogger::getInstance().endElement();
    #endif

                        if (m_xTextFactory.is())
                        {
                            xFieldInterface = m_xTextFactory->createInstance(sServiceName);
                            xFieldProperties.set( xFieldInterface, uno::UNO_QUERY_THROW);
                        }
                    }
                    switch( aIt->second.eFieldId )
                    {
                        case FIELD_ADDRESSBLOCK: break;
                        case FIELD_ADVANCE     : break;
                        case FIELD_ASK         :
                            handleFieldAsk(pContext, xFieldInterface, xFieldProperties);
                        break;
                        case FIELD_AUTONUM    :
                        case FIELD_AUTONUMLGL :
                        case FIELD_AUTONUMOUT :
                            handleAutoNum(pContext, xFieldInterface, xFieldProperties);
                        break;
                        case FIELD_AUTHOR       :
                        case FIELD_USERNAME     :
                        case FIELD_USERINITIALS :
                            handleAuthor(sFirstParam,
                                xFieldProperties,
                                aIt->second.eFieldId);
                        break;
                        case FIELD_DATE:
                        if (xFieldProperties.is())
                        {
                            // Get field fixed property from the context handler
                            if (pContext->IsFieldLocked())
                            {
                                xFieldProperties->setPropertyValue(
                                    getPropertyName(PROP_IS_FIXED),
                                    uno::makeAny( true ));
                                m_bSetDateValue = true;
                            }
                            else
                                xFieldProperties->setPropertyValue(
                                    getPropertyName(PROP_IS_FIXED),
                                    uno::makeAny( false ));

                            xFieldProperties->setPropertyValue(
                                getPropertyName(PROP_IS_DATE),
                                uno::makeAny( true ));
                            SetNumberFormat( pContext->GetCommand(), xFieldProperties );
                        }
                        break;
                        case FIELD_COMMENTS     :
                        {
                            // OUString sParam = lcl_ExtractParameter(pContext->GetCommand(), sizeof(" COMMENTS") );
                            // A parameter with COMMENTS shouldn't set fixed
                            // ( or at least the binary filter doesn't )
                            // If we set fixed then we won't export a field cmd.
                            // Additionally the para in COMMENTS is more like an
                            // instruction to set the document property comments
                            // with the param ( e.g. each COMMENT with a param will
                            // overwrite the Comments document property
                            // #TODO implement the above too
                            xFieldProperties->setPropertyValue(
                                getPropertyName( PROP_IS_FIXED ), uno::makeAny( false ));
                                //PROP_CURRENT_PRESENTATION is set later anyway
                        }
                        break;
                        case FIELD_CREATEDATE  :
                        {
                            xFieldProperties->setPropertyValue(
                                getPropertyName( PROP_IS_DATE ), uno::makeAny( true ));
                            SetNumberFormat( pContext->GetCommand(), xFieldProperties );
                        }
                        break;
                        case FIELD_DOCPROPERTY :
                            handleDocProperty(pContext, sFirstParam,
                                    xFieldInterface);
                        break;
                        case FIELD_DOCVARIABLE  :
                        {
                            //create a user field and type
                            uno::Reference< beans::XPropertySet > xMaster =
                                FindOrCreateFieldMaster("com.sun.star.text.FieldMaster.User", sFirstParam);
                            uno::Reference< text::XDependentTextField > xDependentField( xFieldInterface, uno::UNO_QUERY_THROW );
                            xDependentField->attachTextFieldMaster( xMaster );
                            m_bSetUserFieldContent = true;
                        }
                        break;
                        case FIELD_EDITTIME     :
                            //it's a numbering type, no number format! SetNumberFormat( pContext->GetCommand(), xFieldProperties );
                        break;
                        case FIELD_EQ:
                        {
                            OUString aCommand = pContext->GetCommand().trim();

                            msfilter::util::EquationResult aResult(msfilter::util::ParseCombinedChars(aCommand));
                            if (!aResult.sType.isEmpty() && m_xTextFactory.is())
                            {
                                xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.TextField." + aResult.sType);
                                xFieldProperties =
                                    uno::Reference< beans::XPropertySet >( xFieldInterface,
                                        uno::UNO_QUERY_THROW);
                                xFieldProperties->setPropertyValue(getPropertyName(PROP_CONTENT), uno::makeAny(aResult.sResult));
                            }
                            else
                            {
                                //merge Read_SubF_Ruby into filter/.../util.cxx and reuse that ?
                                sal_Int32 nSpaceIndex = aCommand.indexOf(' ');
                                if(nSpaceIndex > 0)
                                    aCommand = aCommand.copy(nSpaceIndex).trim();
                                if (aCommand.startsWith("\\s"))
                                {
                                    aCommand = aCommand.copy(2);
                                    if (aCommand.startsWith("\\do"))
                                    {
                                        aCommand = aCommand.copy(3);
                                        sal_Int32 nStartIndex = aCommand.indexOf('(');
                                        sal_Int32 nEndIndex = aCommand.indexOf(')');
                                        if (nStartIndex > 0 && nEndIndex > 0)
                                        {
                                            // nDown is the requested "lower by" value in points.
                                            sal_Int32 nDown = aCommand.copy(0, nStartIndex).toInt32();
                                            OUString aContent = aCommand.copy(nStartIndex + 1, nEndIndex - nStartIndex - 1);
                                            PropertyMapPtr pCharContext = GetTopContext();
                                            // dHeight is the font size of the current style.
                                            double dHeight = 0;
                                            if ((GetPropertyFromStyleSheet(PROP_CHAR_HEIGHT) >>= dHeight) && dHeight != 0)
                                                // Character escapement should be given in negative percents for subscripts.
                                                pCharContext->Insert(PROP_CHAR_ESCAPEMENT, uno::makeAny( sal_Int16(- 100 * nDown / dHeight) ) );
                                            appendTextPortion(aContent, pCharContext);
                                        }
                                    }
                                }
                                else if (aCommand.startsWith("\\* jc"))
                                {
                                    handleRubyEQField(pContext);
                                }
                            }
                        }
                        break;
                        case FIELD_FILLIN       :
                        {
                            sal_Int32 nIndex = 0;
                            if (xFieldProperties.is())
                                xFieldProperties->setPropertyValue(
                                        getPropertyName(PROP_HINT), uno::makeAny( pContext->GetCommand().getToken( 1, '\"', nIndex)));
                        }
                        break;
                        case FIELD_FILENAME:
                        {
                            sal_Int32 nNumberingTypeIndex = pContext->GetCommand().indexOf("\\p");
                            if (xFieldProperties.is())
                                xFieldProperties->setPropertyValue(
                                        getPropertyName(PROP_FILE_FORMAT),
                                        uno::makeAny( nNumberingTypeIndex > 0 ? text::FilenameDisplayFormat::FULL : text::FilenameDisplayFormat::NAME_AND_EXT ));
                        }
                        break;
                        case FIELD_FILESIZE     : break;
                        case FIELD_FORMULA :
                            handleFieldFormula(pContext, xFieldProperties);
                        break;
                        case FIELD_FORMCHECKBOX :
                        case FIELD_FORMDROPDOWN :
                        case FIELD_FORMTEXT :
                            {
                                uno::Reference< text::XTextField > xTextField( xFieldInterface, uno::UNO_QUERY );
                                if ( !xTextField.is() )
                                {
                                    FFDataHandler::Pointer_t
                                    pFFDataHandler(pContext->getFFDataHandler());
                                    FormControlHelper::Pointer_t
                                        pFormControlHelper(new FormControlHelper
                                                           (m_bUsingEnhancedFields ? aIt->second.eFieldId : FIELD_FORMCHECKBOX,

                                                            m_xTextDocument, pFFDataHandler));
                                    pContext->setFormControlHelper(pFormControlHelper);
                                    uno::Reference< text::XFormField > xFormField( xFieldInterface, uno::UNO_QUERY );
                                    uno::Reference< container::XNamed > xNamed( xFormField, uno::UNO_QUERY );
                                    if ( xNamed.is() )
                                    {
                                        if ( pFFDataHandler && !pFFDataHandler->getName().isEmpty() )
                                            xNamed->setName(  pFFDataHandler->getName() );
                                        pContext->SetFormField( xFormField );
                                    }
                                }
                                else
                                {
                                    if ( aIt->second.eFieldId == FIELD_FORMDROPDOWN )
                                        lcl_handleDropdownField( xFieldProperties, pContext->getFFDataHandler() );
                                    else
                                        lcl_handleTextField( xFieldProperties, pContext->getFFDataHandler() );
                                }
                            }
                            break;
                        case FIELD_GOTOBUTTON   : break;
                        case FIELD_HYPERLINK:
                        {
                            ::std::vector<OUString> aParts = pContext->GetCommandParts();

                            // Syntax is either:
                            // HYPERLINK "" \l "link"
                            // or
                            // HYPERLINK \l "link"
                            // Make sure "HYPERLINK" doesn't end up as part of link in the second case.
                            if (!aParts.empty() && aParts[0] == "HYPERLINK")
                                aParts.erase(aParts.begin());

                            ::std::vector<OUString>::const_iterator aItEnd = aParts.end();
                            ::std::vector<OUString>::const_iterator aPartIt = aParts.begin();

                            OUString sURL;
                            OUString sTarget;

                            while (aPartIt != aItEnd)
                            {
                                if ( *aPartIt == "\\l" )
                                {
                                    ++aPartIt;

                                    if (aPartIt == aItEnd)
                                        break;

                                    sURL += "#" + *aPartIt;
                                }
                                else if (*aPartIt == "\\m" || *aPartIt == "\\n" || *aPartIt == "\\h")
                                {
                                }
                                else if ( *aPartIt == "\\o" || *aPartIt == "\\t" )
                                {
                                    ++aPartIt;

                                    if (aPartIt == aItEnd)
                                        break;

                                    sTarget = *aPartIt;
                                }
                                else
                                {
                                    sURL = *aPartIt;
                                }

                                ++aPartIt;
                            }

                            if (!sURL.isEmpty())
                            {
                                // Try to make absolute any relative URLs, except
                                // for relative same-document URLs that only contain
                                // a fragment part:
                                if (!sURL.startsWith("#") && !m_aSaveOpt.IsSaveRelFSys()) {
                                    try {
                                        sURL = rtl::Uri::convertRelToAbs(
                                            m_aBaseUrl, sURL);
                                    } catch (rtl::MalformedUriException & e) {
                                        SAL_WARN(
                                            "writerfilter.dmapper",
                                            "MalformedUriException "
                                                << e.getMessage());
                                    }
                                }
                                pContext->SetHyperlinkURL(sURL);
                            }

                            if (!sTarget.isEmpty())
                                pContext->SetHyperlinkTarget(sTarget);
                        }
                        break;
                        case FIELD_IF           : break;
                        case FIELD_INFO         : break;
                        case FIELD_INCLUDEPICTURE: break;
                        case FIELD_KEYWORDS     :
                        {
                            if (!sFirstParam.isEmpty())
                            {
                                xFieldProperties->setPropertyValue(
                                        getPropertyName( PROP_IS_FIXED ), uno::makeAny( true ));
                                //PROP_CURRENT_PRESENTATION is set later anyway
                            }
                        }
                        break;
                        case FIELD_LASTSAVEDBY  : break;
                        case FIELD_MACROBUTTON:
                        {
                            //extract macro name
                            sal_Int32 nIndex = sizeof(" MACROBUTTON ");
                            OUString sMacro = pContext->GetCommand().getToken( 0, ' ', nIndex);
                            if (xFieldProperties.is())
                                xFieldProperties->setPropertyValue(
                                        getPropertyName(PROP_MACRO_NAME), uno::makeAny( sMacro ));

                            //extract quick help text
                            if(xFieldProperties.is() && pContext->GetCommand().getLength() > nIndex + 1)
                            {
                                xFieldProperties->setPropertyValue(
                                    getPropertyName(PROP_HINT),
                                    uno::makeAny( pContext->GetCommand().copy( nIndex )));
                            }
                        }
                        break;
                        case FIELD_MERGEFIELD  :
                        {
                            //todo: create a database field and fieldmaster pointing to a column, only
                            //create a user field and type
                            uno::Reference< beans::XPropertySet > xMaster =
                                FindOrCreateFieldMaster("com.sun.star.text.FieldMaster.Database", sFirstParam);

        //                    xFieldProperties->setPropertyValue(
        //                             "FieldCode",
        //                             uno::makeAny( pContext->GetCommand().copy( nIndex + 1 )));
                            uno::Reference< text::XDependentTextField > xDependentField( xFieldInterface, uno::UNO_QUERY_THROW );
                            xDependentField->attachTextFieldMaster( xMaster );
                        }
                        break;
                        case FIELD_MERGEREC     : break;
                        case FIELD_MERGESEQ     : break;
                        case FIELD_NEXT         : break;
                        case FIELD_NEXTIF       : break;
                        case FIELD_PAGE        :
                            if (xFieldProperties.is())
                            {
                                xFieldProperties->setPropertyValue(
                                        getPropertyName(PROP_NUMBERING_TYPE),
                                        uno::makeAny( lcl_ParseNumberingType(pContext->GetCommand()) ));
                                xFieldProperties->setPropertyValue(
                                        getPropertyName(PROP_SUB_TYPE),
                                        uno::makeAny( text::PageNumberType_CURRENT ));
                            }

                        break;
                        case FIELD_PAGEREF:
                        case FIELD_REF:
                        if (xFieldProperties.is() && !m_bStartTOC)
                        {
                            bool bPageRef = aIt->second.eFieldId == FIELD_PAGEREF;

                            // Do we need a GetReference (default) or a GetExpression field?
                            uno::Reference< text::XTextFieldsSupplier > xFieldsSupplier( GetTextDocument(), uno::UNO_QUERY );
                            uno::Reference< container::XNameAccess > xFieldMasterAccess = xFieldsSupplier->getTextFieldMasters();

                            if (!xFieldMasterAccess->hasByName(
                                    "com.sun.star.text.FieldMaster.SetExpression."
                                    + sFirstParam))
                            {
                            xFieldProperties->setPropertyValue(
                                getPropertyName(PROP_REFERENCE_FIELD_SOURCE),
                                uno::makeAny( sal_Int16(text::ReferenceFieldSource::BOOKMARK)) );
                            xFieldProperties->setPropertyValue(
                                getPropertyName(PROP_SOURCE_NAME),
                                uno::makeAny(sFirstParam) );
                            sal_Int16 nFieldPart = (bPageRef ? text::ReferenceFieldPart::PAGE : text::ReferenceFieldPart::TEXT);
                            OUString sValue;
                            if( lcl_FindInCommand( pContext->GetCommand(), 'p', sValue ))
                            {
                                //above-below
                                nFieldPart = text::ReferenceFieldPart::UP_DOWN;
                            }
                            else if( lcl_FindInCommand( pContext->GetCommand(), 'r', sValue ))
                            {
                                //number
                                nFieldPart = text::ReferenceFieldPart::NUMBER;
                            }
                            else if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
                            {
                                //number-no-context
                                nFieldPart = text::ReferenceFieldPart::NUMBER_NO_CONTEXT;
                            }
                            else if( lcl_FindInCommand( pContext->GetCommand(), 'w', sValue ))
                            {
                                //number-full-context
                                nFieldPart = text::ReferenceFieldPart::NUMBER_FULL_CONTEXT;
                            }
                            xFieldProperties->setPropertyValue(
                                    getPropertyName( PROP_REFERENCE_FIELD_PART ), uno::makeAny( nFieldPart ));
                            }
                            else if( m_xTextFactory.is() )
                            {
                                xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.TextField.GetExpression");
                                xFieldProperties.set(xFieldInterface, uno::UNO_QUERY);
                                xFieldProperties->setPropertyValue(
                                    getPropertyName(PROP_CONTENT),
                                    uno::makeAny(sFirstParam));
                                xFieldProperties->setPropertyValue(getPropertyName(PROP_SUB_TYPE), uno::makeAny(text::SetVariableType::STRING));
                            }
                        }
                        break;
                        case FIELD_REVNUM       : break;
                        case FIELD_SAVEDATE     :
                            SetNumberFormat( pContext->GetCommand(), xFieldProperties );
                        break;
                        case FIELD_SECTION      : break;
                        case FIELD_SECTIONPAGES : break;
                        case FIELD_SEQ          :
                        {
                            // command looks like: " SEQ Table \* ARABIC "
                            OUString sCmd(pContext->GetCommand());
                            // find the sequence name, e.g. "SEQ"
                            OUString sSeqName = msfilter::util::findQuotedText(sCmd, "SEQ ", '\\');
                            sSeqName = sSeqName.trim();

                            // create a sequence field master using the sequence name
                            uno::Reference< beans::XPropertySet > xMaster = FindOrCreateFieldMaster(
                                        "com.sun.star.text.FieldMaster.SetExpression",
                                        sSeqName);

                            xMaster->setPropertyValue(
                                getPropertyName(PROP_SUB_TYPE),
                                uno::makeAny(text::SetVariableType::SEQUENCE));

                            // apply the numbering type
                            xFieldProperties->setPropertyValue(
                                getPropertyName(PROP_NUMBERING_TYPE),
                                uno::makeAny( lcl_ParseNumberingType(pContext->GetCommand()) ));

                            // attach the master to the field
                            uno::Reference< text::XDependentTextField > xDependentField( xFieldInterface, uno::UNO_QUERY_THROW );
                            xDependentField->attachTextFieldMaster( xMaster );

                            rtl::OUString sFormula = sSeqName + "+1";
                            rtl::OUString sValue;
                            if( lcl_FindInCommand( pContext->GetCommand(), 'c', sValue ))
                            {
                                sFormula = sSeqName;
                            }
                            else if( lcl_FindInCommand( pContext->GetCommand(), 'r', sValue ))
                            {
                                sFormula = sValue;
                            }
                            // TODO \s isn't handled, but the spec isn't easy to understand without
                            // an example for this one.
                            xFieldProperties->setPropertyValue(
                                    getPropertyName(PROP_CONTENT),
                                    uno::makeAny(sFormula));

                            // Take care of the numeric formatting definition, default is Arabic
                            sal_Int16 nNumberingType = lcl_ParseNumberingType(pContext->GetCommand());
                            if (nNumberingType == style::NumberingType::PAGE_DESCRIPTOR)
                                nNumberingType = style::NumberingType::ARABIC;
                            xFieldProperties->setPropertyValue(
                                    getPropertyName(PROP_NUMBERING_TYPE),
                                    uno::makeAny(nNumberingType));
                        }
                        break;
                        case FIELD_SET          :
                            handleFieldSet(pContext, xFieldInterface, xFieldProperties);
                        break;
                        case FIELD_SKIPIF       : break;
                        case FIELD_STYLEREF     : break;
                        case FIELD_SUBJECT      :
                        {
                            if (!sFirstParam.isEmpty())
                            {
                                xFieldProperties->setPropertyValue(
                                        getPropertyName( PROP_IS_FIXED ), uno::makeAny( true ));
                                //PROP_CURRENT_PRESENTATION is set later anyway
                            }
                        }
                        break;
                        case FIELD_SYMBOL:
                        {
                            uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
                            OUString sSymbol( sal_Unicode( sFirstParam.startsWithIgnoreAsciiCase("0x") ?  sFirstParam.copy(2).toUInt32(16) : sFirstParam.toUInt32() ) );
                            OUString sFont;
                            bool bHasFont = lcl_FindInCommand( pContext->GetCommand(), 'f', sFont);
                            if ( bHasFont )
                            {
                                sFont = sFont.trim();
                                if (sFont.startsWith("\""))
                                    sFont = sFont.copy(1);
                                if (sFont.endsWith("\""))
                                    sFont = sFont.copy(0,sFont.getLength()-1);
                            }

                            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, sSymbol, true);
                                    uno::Reference< beans::XPropertySet > xProp( xCrsr, uno::UNO_QUERY );
                                    xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_CHAR_SET), uno::makeAny(awt::CharSet::SYMBOL));
                                    if(bHasFont)
                                    {
                                        uno::Any    aVal = uno::makeAny( sFont );
                                        xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME), aVal);
                                        xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_ASIAN), aVal);
                                        xProp->setPropertyValue(getPropertyName(PROP_CHAR_FONT_NAME_COMPLEX), aVal);

                                    }
                                }
                            }
                        }
                        break;
                        case FIELD_TEMPLATE: break;
                        case FIELD_TIME         :
                        {
                            if (pContext->IsFieldLocked())
                            {
                                xFieldProperties->setPropertyValue(
                                    getPropertyName(PROP_IS_FIXED),
                                    uno::makeAny( true ));
                                m_bSetDateValue = true;
                            }
                            SetNumberFormat( pContext->GetCommand(), xFieldProperties );
                        }
                        break;
                        case FIELD_TITLE        :
                        {
                            if (!sFirstParam.isEmpty())
                            {
                                xFieldProperties->setPropertyValue(
                                        getPropertyName( PROP_IS_FIXED ), uno::makeAny( true ));
                                //PROP_CURRENT_PRESENTATION is set later anyway
                            }
                        }
                        break;
                        case FIELD_USERADDRESS  : //todo: user address collects street, city ...
                        break;
                        case FIELD_INDEX:
                            handleIndex(pContext,
                                      OUString::createFromAscii(aIt->second.cFieldServiceName));
                            break;
                        case FIELD_BIBLIOGRAPHY:
                            handleBibliography(pContext,
                                      OUString::createFromAscii(aIt->second.cFieldServiceName));
                            break;
                        case FIELD_TOC:
                            handleToc(pContext,
                                      OUString::createFromAscii(aIt->second.cFieldServiceName));
                        break;
                        case FIELD_XE:
                        {
                            if( !m_xTextFactory.is() )
                                break;

                            uno::Reference< beans::XPropertySet > xTC(
                                    m_xTextFactory->createInstance(
                                            OUString::createFromAscii(aIt->second.cFieldServiceName)),
                                            uno::UNO_QUERY_THROW);
                            if (!sFirstParam.isEmpty())
                            {
                                xTC->setPropertyValue("PrimaryKey",
                                        uno::makeAny(sFirstParam));
                            }
                            uno::Reference< text::XTextContent > xToInsert( xTC, 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->insertTextContent(uno::Reference< text::XTextRange >( xCrsr, uno::UNO_QUERY_THROW ), xToInsert, false);
                                }
                            }
                        }
                            break;
                        case FIELD_CITATION:
                        {
                            if( !m_xTextFactory.is() )
                                break;

                            xFieldInterface = m_xTextFactory->createInstance(
                                      OUString::createFromAscii(aIt->second.cFieldServiceName));
                                      uno::Reference< beans::XPropertySet > xTC(xFieldInterface,
                                      uno::UNO_QUERY_THROW);
                            OUString sCmd(pContext->GetCommand());//sCmd is the entire instrText inclusing the index e.g. CITATION Kra06 \l 1033
                            if( !sCmd.isEmpty()){
                                uno::Sequence<beans::PropertyValue> aValues( comphelper::InitPropertySequence({
                                    { "Identifier", uno::Any(sCmd) }
                                }));
                                xTC->setPropertyValue("Fields", uno::makeAny(aValues));
                            }
                            uno::Reference< text::XTextContent > xToInsert( xTC, uno::UNO_QUERY );

                            uno::Sequence<beans::PropertyValue> aValues = m_aFieldStack.top()->getProperties()->GetPropertyValues();
                            appendTextContent(xToInsert, aValues);
                            m_bSetCitation = true;
                        }
                        break;

                        case FIELD_TC :
                        {
                            if( !m_xTextFactory.is() )
                                break;

                            uno::Reference< beans::XPropertySet > xTC(
                                m_xTextFactory->createInstance(
                                    OUString::createFromAscii(aIt->second.cFieldServiceName)),
                                    uno::UNO_QUERY_THROW);
                            if (!sFirstParam.isEmpty())
                            {
                                xTC->setPropertyValue(getPropertyName(PROP_ALTERNATIVE_TEXT),
                                    uno::makeAny(sFirstParam));
                            }
                            OUString sValue;
                            // \f TC entry in doc with multiple tables
        //                    if( lcl_FindInCommand( pContext->GetCommand(), 'f', sValue ))
        //                    {
                                // todo: unsupported
        //                    }
                            if( lcl_FindInCommand( pContext->GetCommand(), 'l', sValue ))
                            // \l Outline Level
                            {
                                sal_Int32 nLevel = sValue.toInt32();
                                if( !sValue.isEmpty() && nLevel >= 0 && nLevel <= 10 )
                                    xTC->setPropertyValue(getPropertyName(PROP_LEVEL), uno::makeAny( static_cast<sal_Int16>(nLevel) ));
                            }
        //                    if( lcl_FindInCommand( pContext->GetCommand(), 'n', sValue ))
        //                    \n Suppress page numbers
        //                    {
                                //todo: unsupported feature
        //                    }
                            pContext->SetTC( xTC );
                        }
                        break;
                        case  FIELD_NUMCHARS:
                        case  FIELD_NUMWORDS:
                        case  FIELD_NUMPAGES:
                        if (xFieldProperties.is())
                            xFieldProperties->setPropertyValue(
                                getPropertyName(PROP_NUMBERING_TYPE),
                                uno::makeAny( lcl_ParseNumberingType(pContext->GetCommand()) ));
                        break;
                    }
                }
                else
                {
                    /* Unsupported fields will be handled here for docx file.
                     * To handle unsupported fields used fieldmark API.
                     */
                    OUString aCode( pContext->GetCommand().trim() );
                    // Don't waste resources on wrapping shapes inside a fieldmark.
                    if (aCode != "SHAPE" && m_xTextFactory.is() && !m_aTextAppendStack.empty())
                    {
                        xFieldInterface = m_xTextFactory->createInstance("com.sun.star.text.Fieldmark");
                        const uno::Reference<text::XTextContent> xTextContent(xFieldInterface, uno::UNO_QUERY_THROW);
                        uno::Reference< text::XTextAppend >  xTextAppend;
                        xTextAppend = m_aTextAppendStack.top().xTextAppend;
                        uno::Reference< text::XTextCursor > xCrsr = xTextAppend->createTextCursorByRange(pContext->GetStartRange());
                        if (xTextContent.is())
                        {
                            xTextAppend->insertTextContent(xCrsr,xTextContent, true);
                        }
                        uno::Reference<uno::XInterface> xContent(xTextContent);
                        uno::Reference< text::XFormField> xFormField(xContent, uno::UNO_QUERY);
                        xFormField->setFieldType(aCode);
                        m_bStartGenericField = true;
                        pContext->SetFormField( xFormField );
                    }
                    else
                        m_bParaHadField = false;
                }
                //set the text field if there is any
                pContext->SetTextField( uno::Reference< text::XTextField >( xFieldInterface, uno::UNO_QUERY ) );
            }
            catch( const uno::Exception& e )
            {
                SAL_WARN( "writerfilter.dmapper", "Exception in CloseFieldCommand(): " << e );
            }
            pContext->SetCommandCompleted();
        }
    }
    /*-------------------------------------------------------------------------
    //the _current_ fields require a string type result while TOCs accept richt results
      -----------------------------------------------------------------------*/
    bool DomainMapper_Impl::IsFieldResultAsString()
    {
        bool bRet = false;
        OSL_ENSURE( !m_aFieldStack.empty(), "field stack empty?");
        FieldContextPtr pContext = m_aFieldStack.top();
        OSL_ENSURE( pContext.get(), "no field context available");
        if( pContext.get() )
        {
            bRet = pContext->GetTextField().is();
        }
        return bRet;
    }

    void DomainMapper_Impl::AppendFieldResult(OUString const& rString)
    {
        assert(!m_aFieldStack.empty());
        FieldContextPtr pContext = m_aFieldStack.top();
        SAL_WARN_IF(!pContext.get(), "writerfilter.dmapper", "no field context");
        if (pContext.get())
        {
            pContext->AppendResult(rString);
        }
    }

    // Calculates css::DateTime based on ddddd.sssss since 1900-1-0
    static util::DateTime lcl_dateTimeFromSerial(const double& dSerial)
    {
        const sal_uInt32 secondsPerDay = 86400;
        const sal_uInt16 secondsPerHour = 3600;

        DateTime d(Date(30, 12, 1899));
        d.AddDays( static_cast<sal_Int32>(dSerial) );

        double frac = std::modf(dSerial, &o3tl::temporary(double()));
        sal_uInt32 seconds = frac * secondsPerDay;

        util::DateTime date;
        date.Year = d.GetYear();
        date.Month = d.GetMonth();
        date.Day = d.GetDay();
        date.Hours = seconds / secondsPerHour;
        date.Minutes = (seconds % secondsPerHour) / 60;
        date.Seconds = seconds % 60;

        return date;
    }

    void DomainMapper_Impl::SetFieldResult(OUString const& rResult)
    {
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement("setFieldResult");
        TagLogger::getInstance().chars(rResult);
    #endif

        FieldContextPtr pContext = m_aFieldStack.top();
        OSL_ENSURE( pContext.get(), "no field context available");
        if( pContext.get() )
        {
            uno::Reference<text::XTextField> xTextField = pContext->GetTextField();
            try
            {
                OSL_ENSURE( xTextField.is()
                //||m_xTOC.is() ||m_xTC.is()
                //||m_sHyperlinkURL.getLength()
                , "DomainMapper_Impl::SetFieldResult: field not created" );
                if(xTextField.is())
                {
                    try
                    {
                        if( m_bSetUserFieldContent )
                        {
                            // user field content has to be set at the field master
                            uno::Reference< text::XDependentTextField > xDependentField( xTextField, uno::UNO_QUERY_THROW );
                            xDependentField->getTextFieldMaster()->setPropertyValue(
                                    getPropertyName(PROP_CONTENT),
                                 uno::makeAny( rResult ));
                        }
                        else if ( m_bSetCitation )
                        {

                            uno::Reference< beans::XPropertySet > xFieldProperties( xTextField, uno::UNO_QUERY_THROW);
                            // In case of SetExpression, the field result contains the content of the variable.
                            uno::Reference<lang::XServiceInfo> xServiceInfo(xTextField, uno::UNO_QUERY);

                            bool bIsSetbiblio = xServiceInfo->supportsService("com.sun.star.text.TextField.Bibliography");
                            if( bIsSetbiblio )
                            {
                                uno::Any aProperty  = xFieldProperties->getPropertyValue("Fields");
                                uno::Sequence<beans::PropertyValue> aValues ;
                                aProperty >>= aValues;
                                beans::PropertyValue propertyVal;
                                sal_Int32 nTitleFoundIndex = -1;
                                for (sal_Int32 i = 0; i < aValues.getLength(); ++i)
                                {
                                    propertyVal = aValues[i];
                                    if (propertyVal.Name == "Title")
                                    {
                                        nTitleFoundIndex = i;
                                        break;
                                    }
                                }
                                if (nTitleFoundIndex != -1)
                                {
                                    OUString titleStr;
                                    uno::Any aValue(propertyVal.Value);
                                    aValue >>= titleStr;
                                    titleStr = titleStr + rResult;
                                    propertyVal.Value <<= titleStr;
                                    aValues[nTitleFoundIndex] = propertyVal;
                                }
                                else
                                {
                                    aValues.realloc(aValues.getLength() + 1);
                                    propertyVal.Name = "Title";
                                    propertyVal.Value <<= rResult;
                                    aValues[aValues.getLength() - 1] = propertyVal;
                                }
                                xFieldProperties->setPropertyValue("Fields",
                                        uno::makeAny(aValues));
                            }
                        }
                        else if ( m_bSetDateValue )
                        {
                            uno::Reference< util::XNumberFormatsSupplier > xNumberSupplier( m_xTextDocument, uno::UNO_QUERY_THROW );

                            uno::Reference<util::XNumberFormatter> xFormatter(util::NumberFormatter::create(m_xComponentContext), uno::UNO_QUERY_THROW);
                            xFormatter->attachNumberFormatsSupplier( xNumberSupplier );
                            sal_Int32 nKey = 0;

                            uno::Reference< beans::XPropertySet > xFieldProperties( xTextField, uno::UNO_QUERY_THROW);

                            xFieldProperties->getPropertyValue( "NumberFormat" ) >>= nKey;
                            xFieldProperties->setPropertyValue(
                                "DateTimeValue",
                                uno::makeAny( lcl_dateTimeFromSerial( xFormatter->convertStringToNumber( nKey, rResult ) ) ) );
                        }
                        else
                        {
                            uno::Reference< beans::XPropertySet > xFieldProperties( xTextField, uno::UNO_QUERY_THROW);
                            // In case of SetExpression, the field result contains the content of the variable.
                            uno::Reference<lang::XServiceInfo> xServiceInfo(xTextField, uno::UNO_QUERY);
                            bool bIsSetExpression = xServiceInfo->supportsService("com.sun.star.text.TextField.SetExpression");
                            // If we already have content set, then use the current presentation
                            rtl::OUString sValue;
                            if (bIsSetExpression)
                            {   // this will throw for field types without Content
                                uno::Any aValue(xFieldProperties->getPropertyValue(
                                        getPropertyName(PROP_CONTENT)));
                                aValue >>= sValue;
                            }
                            xFieldProperties->setPropertyValue(
                                    getPropertyName(bIsSetExpression && sValue.isEmpty()? PROP_CONTENT : PROP_CURRENT_PRESENTATION),
                                 uno::makeAny( rResult ));
                        }
                    }
                    catch( const beans::UnknownPropertyException& )
                    {
                        //some fields don't have a CurrentPresentation (DateTime)
                    }
                }
            }
            catch (const uno::Exception& e)
            {
                SAL_WARN("writerfilter.dmapper",
                    "DomainMapper_Impl::SetFieldResult: " << e);
            }
        }
    }

    void DomainMapper_Impl::SetFieldFFData(const FFDataHandler::Pointer_t& pFFDataHandler)
    {
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement("setFieldFFData");
    #endif

        if (m_aFieldStack.size())
        {
            FieldContextPtr pContext = m_aFieldStack.top();
            if (pContext.get())
            {
                pContext->setFFDataHandler(pFFDataHandler);
            }
        }

    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().endElement();
    #endif
    }

    void DomainMapper_Impl::PopFieldContext()
    {
        if(m_bDiscardHeaderFooter)
            return;
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().element("popFieldContext");
    #endif

        if (m_aFieldStack.empty())
            return;

        FieldContextPtr pContext = m_aFieldStack.top();
        OSL_ENSURE( pContext.get(), "no field context available");
        if( pContext.get() )
        {
            if( !pContext->IsCommandCompleted() )
                CloseFieldCommand();

            if (!pContext->GetResult().isEmpty())
            {
               uno::Reference< beans::XPropertySet > xFieldProperties = pContext->GetCustomField();
               if(xFieldProperties.is())
                  SetNumberFormat( pContext->GetResult(), xFieldProperties, true );
               SetFieldResult( pContext->GetResult() );
             }

            //insert the field, TC or TOC
            uno::Reference< text::XTextAppend >  xTextAppend;
            if (!m_aTextAppendStack.empty())
                xTextAppend = m_aTextAppendStack.top().xTextAppend;
            if(xTextAppend.is())
            {
                try
                {
                    uno::Reference< text::XTextCursor > xCrsr = xTextAppend->createTextCursorByRange(pContext->GetStartRange());
                    uno::Reference< text::XTextContent > xToInsert( pContext->GetTOC(), uno::UNO_QUERY );
                    if( xToInsert.is() )
                    {
                        if(xTOCMarkerCursor.is() || m_bStartIndex || m_bStartBibliography)
                        {
                            if (m_bStartIndex || m_bStartBibliography)
                            {
                                if (mxTOCTextCursor.is())
                                {
                                    mxTOCTextCursor->goLeft(1,true);
                                    mxTOCTextCursor->setString(OUString());
                                }
                                xTextAppend->finishParagraph(  uno::Sequence< beans::PropertyValue >() );
                            }
                            else
                            {
                                xTOCMarkerCursor->goLeft(1,true);
                                xTOCMarkerCursor->setString(OUString());
                                xTOCMarkerCursor->goLeft(1,true);
                                xTOCMarkerCursor->setString(OUString());
                            }
                        }
                        if (m_bStartedTOC || m_bStartIndex || m_bStartBibliography)
                        {
                            m_bStartedTOC = false;
                            m_aTextAppendStack.pop();
                            m_bTextInserted = false;
                        }
                        m_bStartTOC = false;
                        m_bStartIndex = false;
                        m_bStartBibliography = false;
                        if(m_bInHeaderFooterImport && m_bStartTOCHeaderFooter)
                            m_bStartTOCHeaderFooter = false;
                    }
                    else
                    {
                        xToInsert.set(pContext->GetTC(), uno::UNO_QUERY);
                        if( !xToInsert.is() && !m_bStartTOC && !m_bStartIndex && !m_bStartBibliography )
                            xToInsert.set( pContext->GetTextField(), uno::UNO_QUERY);
                        if( xToInsert.is() && !m_bStartTOC && !m_bStartIndex && !m_bStartBibliography)
                        {
                            PropertyMap aMap;
                            // Character properties of the field show up here the
                            // last (always empty) run. Inherit character
                            // properties from there.
                            // Also merge in the properties from the field context,
                            // e.g. SdtEndBefore.
                            if (m_pLastCharacterContext.get())
                                aMap.InsertProps(m_pLastCharacterContext);
                            aMap.InsertProps(m_aFieldStack.top()->getProperties());
                            appendTextContent(xToInsert, aMap.GetPropertyValues());
                        }
                        else
                        {
                            FormControlHelper::Pointer_t pFormControlHelper(pContext->getFormControlHelper());
                            if (pFormControlHelper.get() != nullptr && pFormControlHelper->hasFFDataHandler() && xCrsr.is())
                            {
                                uno::Reference< text::XFormField > xFormField( pContext->GetFormField() );
                                xToInsert.set(xFormField, uno::UNO_QUERY);
                                if ( xFormField.is() && xToInsert.is() )
                                {
                                    xCrsr->gotoEnd( true );
                                    xToInsert->attach( uno::Reference< text::XTextRange >( xCrsr, uno::UNO_QUERY_THROW ));
                                    pFormControlHelper->processField( xFormField );
                                }
                                else
                                {
                                    uno::Reference<text::XTextRange> xTxtRange(xCrsr, uno::UNO_QUERY);
                                    pFormControlHelper->insertControl(xTxtRange);
                                }
                            }
                            else if (!pContext->GetHyperlinkURL().isEmpty() && xCrsr.is())
                            {
                                xCrsr->gotoEnd( true );

                                uno::Reference< beans::XPropertySet > xCrsrProperties( xCrsr, uno::UNO_QUERY_THROW );
                                xCrsrProperties->setPropertyValue(getPropertyName(PROP_HYPER_LINK_U_R_L), uno::
                                                                  makeAny(pContext->GetHyperlinkURL()));

                                if (!pContext->GetHyperlinkTarget().isEmpty())
                                    xCrsrProperties->setPropertyValue("HyperLinkTarget", uno::makeAny(pContext->GetHyperlinkTarget()));

                                if (m_bStartTOC) {
                                    OUString sDisplayName("Index Link");
                                    xCrsrProperties->setPropertyValue("VisitedCharStyleName",uno::makeAny(sDisplayName));
                                    xCrsrProperties->setPropertyValue("UnvisitedCharStyleName",uno::makeAny(sDisplayName));
                                }
                                else
                                {
                                    if (!pContext->GetHyperlinkStyle().isEmpty())
                                    {
                                        xCrsrProperties->setPropertyValue("VisitedCharStyleName", uno::makeAny(pContext->GetHyperlinkStyle()));
                                        xCrsrProperties->setPropertyValue("UnvisitedCharStyleName", uno::makeAny(pContext->GetHyperlinkStyle()));
                                    }
                                }
                            }
                            else if(m_bStartGenericField)
                            {
                                m_bStartGenericField = false;
                                if(m_bTextInserted)
                                {
                                    m_aTextAppendStack.pop();
                                    m_bTextInserted = false;
                                }
                            }
                        }
                    }
                }
                catch(const lang::IllegalArgumentException&)
                {
                    OSL_FAIL( "IllegalArgumentException in PopFieldContext()" );
                }
                catch(const uno::Exception&)
                {
                    OSL_FAIL( "exception in PopFieldContext()" );
                }
            }

            //TOCs have to include all the imported content

        }
        //remove the field context
        m_aFieldStack.pop();
    }

    void DomainMapper_Impl::SetBookmarkName( const OUString& rBookmarkName )
    {
        BookmarkMap_t::iterator aBookmarkIter = m_aBookmarkMap.find( m_sCurrentBkmkId );
        if( aBookmarkIter != m_aBookmarkMap.end() )
            aBookmarkIter->second.m_sBookmarkName = rBookmarkName;
        else
            m_sCurrentBkmkName = rBookmarkName;
    }

    // This method was used as-is for DomainMapper_Impl::startOrEndPermissionRange() implementation.
    void DomainMapper_Impl::StartOrEndBookmark( const OUString& rId )
    {
        /*
         * Add the dummy paragraph to handle section properties
         * iff the first element in the section is a table. If the dummy para is not added yet, then add it;
         * So bookmark is not attached to the wrong paragraph.
         */
        if(hasTableManager() && getTableManager().isInCell() && m_nTableDepth == 0 && GetIsFirstParagraphInSection()
                        && !GetIsDummyParaAddedForTableInSection() &&!GetIsTextFrameInserted())
        {
            AddDummyParaForTableInSection();
        }

        bool bIsAfterDummyPara = GetIsDummyParaAddedForTableInSection() && GetIsFirstParagraphInSection();
        if (m_aTextAppendStack.empty())
            return;
        uno::Reference< text::XTextAppend >  xTextAppend = m_aTextAppendStack.top().xTextAppend;
        BookmarkMap_t::iterator aBookmarkIter = m_aBookmarkMap.find( rId );
        //is the bookmark name already registered?
        try
        {
            if( aBookmarkIter != m_aBookmarkMap.end() )
            {
                if (m_xTextFactory.is())
                {
                    uno::Reference< text::XTextContent > xBookmark( m_xTextFactory->createInstance( "com.sun.star.text.Bookmark" ), uno::UNO_QUERY_THROW );
                    uno::Reference< text::XTextCursor > xCursor;
                    uno::Reference< text::XText > xText = aBookmarkIter->second.m_xTextRange->getText();
                    if( aBookmarkIter->second.m_bIsStartOfText && !bIsAfterDummyPara)
                    {
                        xCursor = xText->createTextCursorByRange( xText->getStart() );
                    }
                    else
                    {
                        xCursor = xText->createTextCursorByRange( aBookmarkIter->second.m_xTextRange );
                        xCursor->goRight( 1, false );
                    }

                    xCursor->gotoRange( xTextAppend->getEnd(), true );
                    // A Paragraph was recently finished, and a new Paragraph has not been started as yet
                    // then  move the bookmark-End to the earlier paragraph
                    if (IsOutsideAParagraph())
                    {
                        xCursor->goLeft( 1, false );
                    }
                    uno::Reference< container::XNamed > xBkmNamed( xBookmark, uno::UNO_QUERY_THROW );
                    assert(!aBookmarkIter->second.m_sBookmarkName.isEmpty());
                    //todo: make sure the name is not used already!
                    xBkmNamed->setName( aBookmarkIter->second.m_sBookmarkName );
                    xTextAppend->insertTextContent( uno::Reference< text::XTextRange >( xCursor, uno::UNO_QUERY_THROW), xBookmark, !xCursor->isCollapsed() );
                }
                m_aBookmarkMap.erase( aBookmarkIter );
                m_sCurrentBkmkId.clear();
            }
            else
            {
                //otherwise insert a text range as marker
                bool bIsStart = true;
                uno::Reference< text::XTextRange > xCurrent;
                if (xTextAppend.is())
                {
                    uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursorByRange( xTextAppend->getEnd() );

                    if (!xCursor)
                        return;

                    if (!bIsAfterDummyPara)
                        bIsStart = !xCursor->goLeft(1, false);
                    xCurrent = xCursor->getStart();
                }
                m_sCurrentBkmkId = rId;
                m_aBookmarkMap.emplace( rId, BookmarkInsertPosition( bIsStart, m_sCurrentBkmkName, xCurrent ) );
                m_sCurrentBkmkName.clear();
            }
        }
        catch( const uno::Exception& )
        {
            //TODO: What happens to bookmarks where start and end are at different XText objects?
        }
    }

    void DomainMapper_Impl::setPermissionRangeEd(const OUString& user)
    {
        PermMap_t::iterator aPremIter = m_aPermMap.find(m_sCurrentPermId);
        if (aPremIter != m_aPermMap.end())
            aPremIter->second.m_Ed = user;
        else
            m_sCurrentPermEd = user;
    }

    void DomainMapper_Impl::setPermissionRangeEdGrp(const OUString& group)
    {
        PermMap_t::iterator aPremIter = m_aPermMap.find(m_sCurrentPermId);
        if (aPremIter != m_aPermMap.end())
            aPremIter->second.m_EdGrp = group;
        else
            m_sCurrentPermEdGrp = group;
    }

    // This method is based on implementation from DomainMapper_Impl::StartOrEndBookmark()
    void DomainMapper_Impl::startOrEndPermissionRange(sal_Int32 permissinId)
    {
        /*
        * Add the dummy paragraph to handle section properties
        * if the first element in the section is a table. If the dummy para is not added yet, then add it;
        * So permission is not attached to the wrong paragraph.
        */
        if (getTableManager().isInCell() && m_nTableDepth == 0 && GetIsFirstParagraphInSection()
            && !GetIsDummyParaAddedForTableInSection() && !GetIsTextFrameInserted())
        {
            AddDummyParaForTableInSection();
        }

        if (m_aTextAppendStack.empty())
            return;

        const bool bIsAfterDummyPara = GetIsDummyParaAddedForTableInSection() && GetIsFirstParagraphInSection();

        uno::Reference< text::XTextAppend > xTextAppend = m_aTextAppendStack.top().xTextAppend;
        PermMap_t::iterator aPermIter = m_aPermMap.find(permissinId);

        //is the bookmark name already registered?
        try
        {
            if (aPermIter == m_aPermMap.end())
            {
                //otherwise insert a text range as marker
                bool bIsStart = true;
                uno::Reference< text::XTextRange > xCurrent;
                if (xTextAppend.is())
                {
                    uno::Reference< text::XTextCursor > xCursor = xTextAppend->createTextCursorByRange(xTextAppend->getEnd());

                    if (!bIsAfterDummyPara)
                        bIsStart = !xCursor->goLeft(1, false);
                    xCurrent = xCursor->getStart();
                }

                // register the start of the new permission
                m_sCurrentPermId = permissinId;
                m_aPermMap.emplace(permissinId, PermInsertPosition(bIsStart, permissinId, m_sCurrentPermEd, m_sCurrentPermEdGrp, xCurrent));

                // clean up
                m_sCurrentPermEd.clear();
                m_sCurrentPermEdGrp.clear();
            }
            else
            {
                if (m_xTextFactory.is())
                {
                    uno::Reference< text::XTextCursor > xCursor;
                    uno::Reference< text::XText > xText = aPermIter->second.m_xTextRange->getText();
                    if (aPermIter->second.m_bIsStartOfText && !bIsAfterDummyPara)
                    {
                        xCursor = xText->createTextCursorByRange(xText->getStart());
                    }
                    else
                    {
                        xCursor = xText->createTextCursorByRange(aPermIter->second.m_xTextRange);
                        xCursor->goRight(1, false);
                    }

                    xCursor->gotoRange(xTextAppend->getEnd(), true);
                    // A Paragraph was recently finished, and a new Paragraph has not been started as yet
                    // then  move the bookmark-End to the earlier paragraph
                    if (IsOutsideAParagraph())
                    {
                        xCursor->goLeft(1, false);
                    }

                    // create a new bookmark using specific bookmark name pattern for permissions
                    uno::Reference< text::XTextContent > xPerm(m_xTextFactory->createInstance("com.sun.star.text.Bookmark"), uno::UNO_QUERY_THROW);
                    uno::Reference< container::XNamed > xPermNamed(xPerm, uno::UNO_QUERY_THROW);
                    xPermNamed->setName(aPermIter->second.createBookmarkName());

                    // add new bookmark
                    const bool bAbsorb = !xCursor->isCollapsed();
                    uno::Reference< text::XTextRange > xCurrent = uno::Reference< text::XTextRange >(xCursor, uno::UNO_QUERY_THROW);
                    xTextAppend->insertTextContent(xCurrent, xPerm, bAbsorb);
                }

                // remove processed permission
                m_aPermMap.erase(aPermIter);

                // clean up
                m_sCurrentPermId = 0;
                m_sCurrentPermEd.clear();
                m_sCurrentPermEdGrp.clear();
            }
        }
        catch (const uno::Exception&)
        {
            //TODO: What happens to bookmarks where start and end are at different XText objects?
        }
    }

    void DomainMapper_Impl::AddAnnotationPosition(
        const bool bStart,
        const sal_Int32 nAnnotationId)
    {
        if (m_aTextAppendStack.empty())
            return;

        // Create a cursor, pointing to the current position.
        uno::Reference<text::XTextAppend>  xTextAppend = m_aTextAppendStack.top().xTextAppend;
        uno::Reference<text::XTextRange> xCurrent;
        if (xTextAppend.is())
        {
            uno::Reference<text::XTextCursor> xCursor;
            if (m_bIsNewDoc)
                xCursor = xTextAppend->createTextCursorByRange(xTextAppend->getEnd());
            else
                xCursor = m_aTextAppendStack.top().xCursor;
            if (xCursor.is())
                xCurrent = xCursor->getStart();
        }

        // And save it, to be used by PopAnnotation() later.
        AnnotationPosition& aAnnotationPosition = m_aAnnotationPositions[ nAnnotationId ];
        if (bStart)
        {
            aAnnotationPosition.m_xStart = xCurrent;
        }
        else
        {
            aAnnotationPosition.m_xEnd = xCurrent;
        }
        m_aAnnotationPositions[ nAnnotationId ] = aAnnotationPosition;
    }

    GraphicImportPtr const & DomainMapper_Impl::GetGraphicImport(GraphicImportType eGraphicImportType)
    {
        if(!m_pGraphicImport)
            m_pGraphicImport = new GraphicImport( m_xComponentContext, m_xTextFactory, m_rDMapper, eGraphicImportType, m_aPositionOffsets, m_aAligns, m_aPositivePercentages );
        return m_pGraphicImport;
    }
    /*-------------------------------------------------------------------------
        reset graphic import if the last import resulted in a shape, not a graphic
      -----------------------------------------------------------------------*/
    void DomainMapper_Impl::ResetGraphicImport()
    {
        m_pGraphicImport.clear();
    }

    void  DomainMapper_Impl::ImportGraphic(const writerfilter::Reference< Properties >::Pointer_t& ref, GraphicImportType eGraphicImportType)
    {
        GetGraphicImport(eGraphicImportType);
        if( eGraphicImportType != IMPORT_AS_DETECTED_INLINE && eGraphicImportType != IMPORT_AS_DETECTED_ANCHOR )
        {
            //create the graphic
            ref->resolve( *m_pGraphicImport );
        }

        //insert it into the document at the current cursor position

        uno::Reference<text::XTextContent> xTextContent
            (m_pGraphicImport->GetGraphicObject());

        // In case the SDT starts with the text portion of the graphic, then set the SDT properties here.
        bool bHasGrabBag = false;
        uno::Reference<beans::XPropertySet> xPropertySet(xTextContent, uno::UNO_QUERY);
        if (xPropertySet.is())
        {
            uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
            bHasGrabBag = xPropertySetInfo->hasPropertyByName("FrameInteropGrabBag");
            // In case we're outside a paragraph, then the SDT properties are stored in the paragraph grab-bag, not the frame one.
            if (!m_pSdtHelper->isInteropGrabBagEmpty() && bHasGrabBag && !m_pSdtHelper->isOutsideAParagraph())
            {
                comphelper::SequenceAsHashMap aFrameGrabBag(xPropertySet->getPropertyValue("FrameInteropGrabBag"));
                aFrameGrabBag["SdtPr"] <<= m_pSdtHelper->getInteropGrabBagAndClear();
                xPropertySet->setPropertyValue("FrameInteropGrabBag", uno::makeAny(aFrameGrabBag.getAsConstPropertyValueList()));
            }
        }

        /* Set "SdtEndBefore" property on Drawing.
         * It is required in a case when Drawing appears immediately after first run i.e.
         * there is no text/space/tab in between two runs.
         * In this case "SdtEndBefore" property needs to be set on Drawing.
         */
        if(IsSdtEndBefore())
        {
            if(xPropertySet.is())
            {
                if (bHasGrabBag)
                {
                    uno::Sequence<beans::PropertyValue> aFrameGrabBag( comphelper::InitPropertySequence({
                        { "SdtEndBefore", uno::Any(true) }
                    }));
                    xPropertySet->setPropertyValue("FrameInteropGrabBag",uno::makeAny(aFrameGrabBag));
                }
            }
        }

        // Update the shape properties if it is embedded object.
        if(m_xEmbedded.is()){
            uno::Reference<drawing::XShape> xShape = m_pGraphicImport->GetXShapeObject();
            UpdateEmbeddedShapeProps(xShape);
            if (eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR)
            {
                uno::Reference<beans::XPropertySet> xEmbeddedProps(m_xEmbedded, uno::UNO_QUERY);
                xEmbeddedProps->setPropertyValue("AnchorType", uno::makeAny(text::TextContentAnchorType_AT_CHARACTER));
                uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
                xEmbeddedProps->setPropertyValue("HoriOrient", xShapeProps->getPropertyValue("HoriOrient"));
                xEmbeddedProps->setPropertyValue("HoriOrientPosition", xShapeProps->getPropertyValue("HoriOrientPosition"));
                xEmbeddedProps->setPropertyValue("HoriOrientRelation", xShapeProps->getPropertyValue("HoriOrientRelation"));
                xEmbeddedProps->setPropertyValue("VertOrient", xShapeProps->getPropertyValue("VertOrient"));
                xEmbeddedProps->setPropertyValue("VertOrientPosition", xShapeProps->getPropertyValue("VertOrientPosition"));
                xEmbeddedProps->setPropertyValue("VertOrientRelation", xShapeProps->getPropertyValue("VertOrientRelation"));
            }
        }
        //insert it into the document at the current cursor position
        OSL_ENSURE( xTextContent.is(), "DomainMapper_Impl::ImportGraphic");
        if( xTextContent.is())
        {
            appendTextContent( xTextContent, uno::Sequence< beans::PropertyValue >() );

            if (eGraphicImportType == IMPORT_AS_DETECTED_ANCHOR && !m_aTextAppendStack.empty())
                // Remember this object is anchored to the current paragraph.
                m_aTextAppendStack.top().m_aAnchoredObjects.push_back(xTextContent);
        }

        // Clear the reference, so in case the embedded object is inside a
        // TextFrame, we won't try to resize it (to match the size of the
        // TextFrame) here.
        m_xEmbedded.clear();
        m_pGraphicImport.clear();
    }

    void DomainMapper_Impl::SetLineNumbering( sal_Int32 nLnnMod, sal_uInt32 nLnc, sal_Int32 ndxaLnn )
    {
        if( !m_bLineNumberingSet )
        {
            try
            {
                uno::Reference< text::XLineNumberingProperties > xLineProperties( m_xTextDocument, uno::UNO_QUERY_THROW );
                uno::Reference< beans::XPropertySet > xProperties = xLineProperties->getLineNumberingProperties();
                uno::Any aTrue( uno::makeAny( true ));
                xProperties->setPropertyValue( getPropertyName( PROP_IS_ON                  ), aTrue);
                xProperties->setPropertyValue( getPropertyName( PROP_COUNT_EMPTY_LINES      ), aTrue );
                xProperties->setPropertyValue( getPropertyName( PROP_COUNT_LINES_IN_FRAMES  ), uno::makeAny( false ) );
                xProperties->setPropertyValue( getPropertyName( PROP_INTERVAL               ), uno::makeAny( static_cast< sal_Int16 >( nLnnMod )));
                xProperties->setPropertyValue( getPropertyName( PROP_DISTANCE               ), uno::makeAny( ConversionHelper::convertTwipToMM100(ndxaLnn) ));
                xProperties->setPropertyValue( getPropertyName( PROP_NUMBER_POSITION        ), uno::makeAny( style::LineNumberPosition::LEFT));
                xProperties->setPropertyValue( getPropertyName( PROP_NUMBERING_TYPE         ), uno::makeAny( style::NumberingType::ARABIC));
                xProperties->setPropertyValue( getPropertyName( PROP_RESTART_AT_EACH_PAGE   ), uno::makeAny( nLnc == NS_ooxml::LN_Value_ST_LineNumberRestart_newPage ));
            }
            catch( const uno::Exception& )
            {}
        }
        m_bLineNumberingSet = true;
        uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier( GetTextDocument(), uno::UNO_QUERY_THROW );
        uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
        uno::Reference<container::XNameContainer> xStyles;
        xStyleFamilies->getByName(getPropertyName( PROP_PARAGRAPH_STYLES )) >>= xStyles;
        lcl_linenumberingHeaderFooter( xStyles, "Header", this );
        lcl_linenumberingHeaderFooter( xStyles, "Footer", this );
    }

    void DomainMapper_Impl::SetPageMarginTwip( PageMarElement eElement, sal_Int32 nValue )
    {
        nValue = ConversionHelper::convertTwipToMM100(nValue);
        switch(eElement)
        {
            case PAGE_MAR_TOP    : m_aPageMargins.top     = nValue; break;
            case PAGE_MAR_RIGHT  : m_aPageMargins.right   = nValue; break;
            case PAGE_MAR_BOTTOM : m_aPageMargins.bottom  = nValue; break;
            case PAGE_MAR_LEFT   : m_aPageMargins.left    = nValue; break;
            case PAGE_MAR_HEADER : m_aPageMargins.header  = nValue; break;
            case PAGE_MAR_FOOTER : m_aPageMargins.footer  = nValue; break;
            case PAGE_MAR_GUTTER : break;
        }
    }

    PageMar::PageMar()
        : top(ConversionHelper::convertTwipToMM100( sal_Int32(1440)))
        // This is strange, the RTF spec says it's 1800, but it's clearly 1440 in Word
        // OOXML seems not to specify a default value
        , right(ConversionHelper::convertTwipToMM100( sal_Int32(1440)))
        , bottom(top)
        , left(right)
        , header(ConversionHelper::convertTwipToMM100(sal_Int32(720)))
        , footer(header)
    {
    }

    void DomainMapper_Impl::RegisterFrameConversion(
            uno::Reference< text::XTextRange > const&    xFrameStartRange,
            uno::Reference< text::XTextRange > const&    xFrameEndRange,
            const std::vector<beans::PropertyValue>& rFrameProperties
            )
    {
        OSL_ENSURE(
            m_aFrameProperties.empty() && !m_xFrameStartRange.is() && !m_xFrameEndRange.is(),
            "frame properties not removed");
        m_aFrameProperties = rFrameProperties;
        m_xFrameStartRange = xFrameStartRange;
        m_xFrameEndRange   = xFrameEndRange;
    }

    void DomainMapper_Impl::ExecuteFrameConversion()
    {
        if( m_xFrameStartRange.is() && m_xFrameEndRange.is() && !m_bDiscardHeaderFooter )
        {
            try
            {
                uno::Reference< text::XTextAppendAndConvert > xTextAppendAndConvert( GetTopTextAppend(), uno::UNO_QUERY_THROW );
                xTextAppendAndConvert->convertToTextFrame(
                    m_xFrameStartRange,
                    m_xFrameEndRange,
                    comphelper::containerToSequence(m_aFrameProperties) );
            }
            catch( const uno::Exception&)
            {
                DBG_UNHANDLED_EXCEPTION( "writerfilter.dmapper", "Exception caught when converting to frame");
            }
        }
        m_xFrameStartRange = nullptr;
        m_xFrameEndRange = nullptr;
        m_aFrameProperties.clear();
    }

    void DomainMapper_Impl::AddNewRedline( sal_uInt32 sprmId )
    {
        RedlineParamsPtr pNew( new RedlineParams );
        pNew->m_nToken = XML_mod;
        if ( !m_bIsParaMarkerChange )
        {
            // <w:rPrChange> applies to the whole <w:r>, <w:pPrChange> applies to the whole <w:p>,
            // so keep those two in CONTEXT_CHARACTERS and CONTEXT_PARAGRAPH, which will take
            // care of their scope (i.e. when they should be used and discarded).
            // Let's keep the rest the same way they used to be handled (explicitly dropped
            // from a global stack by endtrackchange), but quite possibly they should not be handled
            // that way either (I don't know).
            if( sprmId == NS_ooxml::LN_EG_RPrContent_rPrChange )
                GetTopContextOfType( CONTEXT_CHARACTER )->Redlines().push_back( pNew );
            else if( sprmId == NS_ooxml::LN_CT_PPr_pPrChange )
                GetTopContextOfType( CONTEXT_PARAGRAPH )->Redlines().push_back( pNew );
            else
                m_aRedlines.top().push_back( pNew );
        }
        else
        {
            m_pParaMarkerRedline = pNew;
        }
        // Newly read data will go into this redline.
        m_currentRedline = pNew;
    }

    void DomainMapper_Impl::SetCurrentRedlineIsRead()
    {
        m_currentRedline.clear();
    }

    sal_Int32 DomainMapper_Impl::GetCurrentRedlineToken(  )
    {
        sal_Int32 nToken = 0;
        assert( m_currentRedline.get());
        nToken = m_currentRedline->m_nToken;
        return nToken;
    }

    void DomainMapper_Impl::SetCurrentRedlineAuthor( const OUString& sAuthor )
    {
        if (!m_xAnnotationField.is())
        {
            if (m_currentRedline.get())
                m_currentRedline->m_sAuthor = sAuthor;
            else
                SAL_INFO("writerfilter.dmapper", "numberingChange not implemented");
        }
        else
            m_xAnnotationField->setPropertyValue("Author", uno::makeAny(sAuthor));
    }

    void DomainMapper_Impl::SetCurrentRedlineInitials( const OUString& sInitials )
    {
        if (m_xAnnotationField.is())
            m_xAnnotationField->setPropertyValue("Initials", uno::makeAny(sInitials));
    }

    void DomainMapper_Impl::SetCurrentRedlineDate( const OUString& sDate )
    {
        if (!m_xAnnotationField.is())
        {
            if (m_currentRedline.get())
                m_currentRedline->m_sDate = sDate;
            else
                SAL_INFO("writerfilter.dmapper", "numberingChange not implemented");
        }
        else
            m_xAnnotationField->setPropertyValue("DateTimeValue", uno::makeAny(ConversionHelper::ConvertDateStringToDateTime(sDate)));
    }

    void DomainMapper_Impl::SetCurrentRedlineId( sal_Int32 sId )
    {
        if (m_xAnnotationField.is())
        {
            m_nAnnotationId = sId;
        }
        else
        {
            // This should be an assert, but somebody had the smart idea to reuse this function also for comments and whatnot,
            // and in some cases the id is actually not handled, which may be in fact a bug.
            if( !m_currentRedline.get())
                SAL_INFO("writerfilter.dmapper", "no current redline");
        }
    }

    void DomainMapper_Impl::SetCurrentRedlineToken( sal_Int32 nToken )
    {
        assert( m_currentRedline.get());
        m_currentRedline->m_nToken = nToken;
    }

    void DomainMapper_Impl::SetCurrentRedlineRevertProperties( const uno::Sequence<beans::PropertyValue>& aProperties )
    {
        assert( m_currentRedline.get());
        m_currentRedline->m_aRevertProperties = aProperties;
    }

    // This removes only the last redline stored here, those stored in contexts are automatically removed when
    // the context is destroyed.
    void DomainMapper_Impl::RemoveTopRedline( )
    {
        assert( m_aRedlines.top().size( ) > 0 );
        m_aRedlines.top().pop_back( );
        m_currentRedline.clear();
    }

    void DomainMapper_Impl::ApplySettingsTable()
    {
        if (m_pSettingsTable && m_xTextFactory.is())
        {
            try
            {
                uno::Reference< beans::XPropertySet > xTextDefaults(m_xTextFactory->createInstance("com.sun.star.text.Defaults"), uno::UNO_QUERY_THROW );
                sal_Int32 nDefTab = m_pSettingsTable->GetDefaultTabStop();
                xTextDefaults->setPropertyValue( getPropertyName( PROP_TAB_STOP_DISTANCE ), uno::makeAny(nDefTab) );
                if (m_pSettingsTable->GetLinkStyles())
                {
                    // If linked styles are enabled, set paragraph defaults from Word's default template
                    xTextDefaults->setPropertyValue(getPropertyName(PROP_PARA_BOTTOM_MARGIN), uno::makeAny(ConversionHelper::convertTwipToMM100(200)));
                    style::LineSpacing aSpacing;
                    aSpacing.Mode = style::LineSpacingMode::PROP;
                    aSpacing.Height = sal_Int16(115);
                    xTextDefaults->setPropertyValue(getPropertyName(PROP_PARA_LINE_SPACING), uno::makeAny(aSpacing));
                }

                if (m_pSettingsTable->GetZoomFactor() || m_pSettingsTable->GetView())
                {
                    std::vector<beans::PropertyValue> aViewProps;
                    if (m_pSettingsTable->GetZoomFactor())
                    {
                        aViewProps.emplace_back("ZoomFactor", -1, uno::makeAny(m_pSettingsTable->GetZoomFactor()), beans::PropertyState_DIRECT_VALUE);
                        aViewProps.emplace_back("VisibleBottom", -1, uno::makeAny(sal_Int32(0)), beans::PropertyState_DIRECT_VALUE);
                        aViewProps.emplace_back("ZoomType", -1, uno::makeAny(sal_Int16(0)), beans::PropertyState_DIRECT_VALUE);
                    }
                    uno::Reference<container::XIndexContainer> xBox = document::IndexedPropertyValues::create(m_xComponentContext);
                    xBox->insertByIndex(sal_Int32(0), uno::makeAny(comphelper::containerToSequence(aViewProps)));
                    uno::Reference<container::XIndexAccess> xIndexAccess(xBox, uno::UNO_QUERY);
                    uno::Reference<document::XViewDataSupplier> xViewDataSupplier(m_xTextDocument, uno::UNO_QUERY);
                    xViewDataSupplier->setViewData(xIndexAccess);
                }

                uno::Reference< beans::XPropertySet > xSettings(m_xTextFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY);

                if (m_pSettingsTable->GetDoNotExpandShiftReturn())
                    xSettings->setPropertyValue( "DoNotJustifyLinesWithManualBreak", uno::makeAny(true) );
                if (m_pSettingsTable->GetUsePrinterMetrics())
                    xSettings->setPropertyValue("PrinterIndependentLayout", uno::makeAny(document::PrinterIndependentLayout::DISABLED));
                if( m_pSettingsTable->GetEmbedTrueTypeFonts())
                    xSettings->setPropertyValue( getPropertyName( PROP_EMBED_FONTS ), uno::makeAny(true) );
                if( m_pSettingsTable->GetEmbedSystemFonts())
                    xSettings->setPropertyValue( getPropertyName( PROP_EMBED_SYSTEM_FONTS ), uno::makeAny(true) );
                xSettings->setPropertyValue("AddParaTableSpacing", uno::makeAny(m_pSettingsTable->GetDoNotUseHTMLParagraphAutoSpacing()));
                if( m_pSettingsTable->GetProtectForm() )
                    xSettings->setPropertyValue("ProtectForm", uno::makeAny( true ));
            }
            catch(const uno::Exception&)
            {
            }
        }
    }

    uno::Reference<container::XIndexAccess> DomainMapper_Impl::GetCurrentNumberingRules(sal_Int32* pListLevel)
    {
        uno::Reference<container::XIndexAccess> xRet;
        try
        {
            OUString aStyle = GetCurrentParaStyleName();
            if (aStyle.isEmpty())
                return xRet;
            const StyleSheetEntryPtr pEntry = GetStyleSheetTable()->FindStyleSheetByConvertedStyleName(aStyle);
            if (!pEntry)
                return xRet;
            const StyleSheetPropertyMap* pStyleSheetProperties = dynamic_cast<const StyleSheetPropertyMap*>(pEntry->pProperties.get());
            if (!pStyleSheetProperties)
                return xRet;
            sal_Int32 nListId = pStyleSheetProperties->GetListId();
            if (nListId < 0)
                return xRet;
            if (pListLevel)
                *pListLevel = pStyleSheetProperties->GetListLevel();

            // So we are in a paragraph style and it has numbering. Look up the relevant numbering rules.
            OUString aListName = ListDef::GetStyleName(nListId);
            uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier(GetTextDocument(), uno::UNO_QUERY_THROW);
            uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
            uno::Reference<container::XNameAccess> xNumberingStyles;
            xStyleFamilies->getByName("NumberingStyles") >>= xNumberingStyles;
            uno::Reference<beans::XPropertySet> xStyle(xNumberingStyles->getByName(aListName), uno::UNO_QUERY);
            xRet.set(xStyle->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
        }
        catch (const uno::Exception& e)
        {
            SAL_WARN("writerfilter.dmapper",
                    "GetCurrentNumberingRules: exception caught: " << e);
        }
        return xRet;
    }

    uno::Reference<beans::XPropertySet> DomainMapper_Impl::GetCurrentNumberingCharStyle()
    {
        uno::Reference<beans::XPropertySet> xRet;
        try
        {
            sal_Int32 nListLevel = -1;
            uno::Reference<container::XIndexAccess> xLevels;
            if ( GetTopContextType() == CONTEXT_PARAGRAPH )
                xLevels = GetCurrentNumberingRules(&nListLevel);
            if (!xLevels.is())
            {
                PropertyMapPtr pContext = m_pTopContext;
                if (IsRTFImport() && !IsOpenField())
                {
                    // Looking up the paragraph context explicitly (and not just taking
                    // the top context) is necessary for RTF, where formatting of a run
                    // and of the paragraph mark is not separated.
                    // We know that the formatting inside a field won't affect the
                    // paragraph marker formatting, though.
                    pContext = GetTopContextOfType(CONTEXT_PARAGRAPH);
                    if (!pContext)
                        return xRet;
                }

                // In case numbering rules is not found via a style, try the direct formatting instead.
                boost::optional<PropertyMap::Property> oProp = pContext->getProperty(PROP_NUMBERING_RULES);
                if (oProp)
                {
                    xLevels.set(oProp->second, uno::UNO_QUERY);
                    // Found the rules, then also try to look up our numbering level.
                    oProp = pContext->getProperty(PROP_NUMBERING_LEVEL);
                    if (oProp)
                        oProp->second >>= nListLevel;
                    else
                        nListLevel = 0;
                }

                if (!xLevels.is())
                    return xRet;
            }
            uno::Sequence<beans::PropertyValue> aProps;
            xLevels->getByIndex(nListLevel) >>= aProps;
            for (int i = 0; i < aProps.getLength(); ++i)
            {
                const beans::PropertyValue& rProp = aProps[i];

                if (rProp.Name == "CharStyleName")
                {
                    OUString aCharStyle;
                    rProp.Value >>= aCharStyle;
                    uno::Reference<container::XNameAccess> xCharacterStyles;
                    uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier(GetTextDocument(), uno::UNO_QUERY);
                    uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
                    xStyleFamilies->getByName("CharacterStyles") >>= xCharacterStyles;
                    xRet.set(xCharacterStyles->getByName(aCharStyle), uno::UNO_QUERY_THROW);
                    break;
                }
            }
        }
        catch( const uno::Exception& )
        {
        }
        return xRet;
    }

    SectionPropertyMap * DomainMapper_Impl::GetSectionContext()
    {
        SectionPropertyMap* pSectionContext = nullptr;
        //the section context is not available before the first call of startSectionGroup()
        if( !IsAnyTableImport() )
        {
            PropertyMapPtr pContext = GetTopContextOfType(CONTEXT_SECTION);
            pSectionContext = dynamic_cast< SectionPropertyMap* >( pContext.get() );
        }

        return pSectionContext;
    }

    void DomainMapper_Impl::deferCharacterProperty(sal_Int32 id, const css::uno::Any& value)
    {
        deferredCharacterProperties[ id ] = value;
    }

    void DomainMapper_Impl::processDeferredCharacterProperties()
    {
        // ACtually process in DomainMapper, so that it's the same source file like normal processing.
        if( !deferredCharacterProperties.empty())
        {
            m_rDMapper.processDeferredCharacterProperties( deferredCharacterProperties );
            deferredCharacterProperties.clear();
        }
    }

    sal_Int32 DomainMapper_Impl::getNumberingProperty(const sal_Int32 nListId, sal_Int32 nNumberingLevel, const OUString& aProp)
    {
        sal_Int32 nRet = 0;
        if ( nListId < 0 )
            return nRet;

        try
        {
            if (nNumberingLevel < 0) // It seems it's valid to omit numbering level, and in that case it means zero.
                nNumberingLevel = 0;

            const OUString aListName = ListDef::GetStyleName(nListId);
            const uno::Reference< style::XStyleFamiliesSupplier > xStylesSupplier(GetTextDocument(), uno::UNO_QUERY_THROW);
            const uno::Reference< container::XNameAccess > xStyleFamilies = xStylesSupplier->getStyleFamilies();
            uno::Reference<container::XNameAccess> xNumberingStyles;
            xStyleFamilies->getByName("NumberingStyles") >>= xNumberingStyles;
            const uno::Reference<beans::XPropertySet> xStyle(xNumberingStyles->getByName(aListName), uno::UNO_QUERY);
            const uno::Reference<container::XIndexAccess> xNumberingRules(xStyle->getPropertyValue("NumberingRules"), uno::UNO_QUERY);
            if (xNumberingRules.is())
            {
                uno::Sequence<beans::PropertyValue> aProps;
                xNumberingRules->getByIndex(nNumberingLevel) >>= aProps;
                for (int i = 0; i < aProps.getLength(); ++i)
                {
                    const beans::PropertyValue& rProp = aProps[i];

                    if (rProp.Name == aProp)
                    {
                        rProp.Value >>= nRet;
                        break;
                    }
                }
            }
        }
        catch( const uno::Exception& )
        {
            // This can happen when the doc contains some hand-crafted invalid list level.
        }

        return nRet;
    }

    sal_Int32 DomainMapper_Impl::getCurrentNumberingProperty(const OUString& aProp)
    {
        sal_Int32 nRet = 0;

        boost::optional<PropertyMap::Property> pProp = m_pTopContext->getProperty(PROP_NUMBERING_RULES);
        uno::Reference<container::XIndexAccess> xNumberingRules;
        if (pProp)
            xNumberingRules.set(pProp->second, uno::UNO_QUERY);
        pProp = m_pTopContext->getProperty(PROP_NUMBERING_LEVEL);
        // Default numbering level is the first one.
        sal_Int32 nNumberingLevel = 0;
        if (pProp)
            pProp->second >>= nNumberingLevel;
        if (xNumberingRules.is())
        {
            uno::Sequence<beans::PropertyValue> aProps;
            xNumberingRules->getByIndex(nNumberingLevel) >>= aProps;
            for (int i = 0; i < aProps.getLength(); ++i)
            {
                const beans::PropertyValue& rProp = aProps[i];

                if (rProp.Name == aProp)
                {
                    rProp.Value >>= nRet;
                    break;
                }
            }
        }

        return nRet;
    }

    void DomainMapper_Impl::enableInteropGrabBag(const OUString& aName)
    {
        m_aInteropGrabBagName = aName;
    }

    void DomainMapper_Impl::disableInteropGrabBag()
    {
        m_aInteropGrabBagName.clear();
        m_aInteropGrabBag.clear();
        m_aSubInteropGrabBag.clear();
    }

    bool DomainMapper_Impl::isInteropGrabBagEnabled()
    {
        return !(m_aInteropGrabBagName.isEmpty());
    }

    void DomainMapper_Impl::appendGrabBag(std::vector<beans::PropertyValue>& rInteropGrabBag, const OUString& aKey, const OUString& aValue)
    {
        if (m_aInteropGrabBagName.isEmpty())
            return;
        beans::PropertyValue aProperty;
        aProperty.Name = aKey;
        aProperty.Value <<= aValue;
        rInteropGrabBag.push_back(aProperty);
    }

    void DomainMapper_Impl::appendGrabBag(std::vector<beans::PropertyValue>& rInteropGrabBag, const OUString& aKey, std::vector<beans::PropertyValue>& rValue)
    {
        if (m_aInteropGrabBagName.isEmpty())
            return;
        beans::PropertyValue aProperty;
        aProperty.Name = aKey;
        aProperty.Value <<= comphelper::containerToSequence(rValue);
        rValue.clear();
        rInteropGrabBag.push_back(aProperty);
    }

    void DomainMapper_Impl::substream(Id rName,
            ::writerfilter::Reference<Stream>::Pointer_t const& ref)
    {
    #ifndef NDEBUG
        size_t contextSize(m_aContextStack.size());
        size_t propSize[NUMBER_OF_CONTEXTS];
        for (int i = 0; i < NUMBER_OF_CONTEXTS; ++i) {
            propSize[i] = m_aPropertyStacks[i].size();
        }
    #endif

        // Save "has footnote" state, which is specific to a section in the body
        // text, so state from substreams is not relevant.
        bool bHasFtn = m_bHasFtn;

        //finalize any waiting frames before starting alternate streams
        CheckUnregisteredFrameConversion();
        ExecuteFrameConversion();

        appendTableManager();
        // Appending a TableManager resets its TableHandler, so we need to append
        // that as well, or tables won't be imported properly in headers/footers.
        appendTableHandler();
        getTableManager().startLevel();

        //import of page header/footer
        //Ensure that only one header/footer per section is pushed

        switch( rName )
        {
        case NS_ooxml::LN_headerl:
                PushPageHeader(SectionPropertyMap::PAGE_LEFT);
            break;
        case NS_ooxml::LN_headerr:
                PushPageHeader(SectionPropertyMap::PAGE_RIGHT);
            break;
        case NS_ooxml::LN_headerf:
                PushPageHeader(SectionPropertyMap::PAGE_FIRST);
            break;
        case NS_ooxml::LN_footerl:
                PushPageFooter(SectionPropertyMap::PAGE_LEFT);
            break;
        case NS_ooxml::LN_footerr:
                PushPageFooter(SectionPropertyMap::PAGE_RIGHT);
            break;
        case NS_ooxml::LN_footerf:
                PushPageFooter(SectionPropertyMap::PAGE_FIRST);
            break;
        case NS_ooxml::LN_footnote:
        case NS_ooxml::LN_endnote:
            PushFootOrEndnote( NS_ooxml::LN_footnote == rName );
        break;
        case NS_ooxml::LN_annotation :
            PushAnnotation();
        break;
        }
        ref->resolve(m_rDMapper);

        switch( rName )
        {
        case NS_ooxml::LN_headerl:
        case NS_ooxml::LN_headerr:
        case NS_ooxml::LN_headerf:
        case NS_ooxml::LN_footerl:
        case NS_ooxml::LN_footerr:
        case NS_ooxml::LN_footerf:
            PopPageHeaderFooter();
        break;
        case NS_ooxml::LN_footnote:
        case NS_ooxml::LN_endnote:
            PopFootOrEndnote();
        break;
        case NS_ooxml::LN_annotation :
            PopAnnotation();
        break;
        }

        getTableManager().endLevel();
        popTableManager();
        m_bHasFtn = bHasFtn;

        switch(rName)
        {
        case NS_ooxml::LN_footnote:
        case NS_ooxml::LN_endnote:
            m_pTableHandler->setHadFootOrEndnote(true);
            m_bHasFtn = true;
            break;
        }

        // check that stacks are the same as before substream
        assert(m_aContextStack.size() == contextSize);
        for (int i = 0; i < NUMBER_OF_CONTEXTS; ++i) {
            assert(m_aPropertyStacks[i].size() == propSize[i]);
        }
    }
    }}

    /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

↑ V560 A part of conditional expression is always false.

↑ V560 A part of conditional expression is always true: pParaContext.

↑ V699 Consider inspecting the 'foo = bar = baz ? .... : ....' expression. It is possible that 'foo = bar == baz ? .... : ....' should be used here instead.

↑ V699 Consider inspecting the 'foo = bar = baz ? .... : ....' expression. It is possible that 'foo = bar == baz ? .... : ....' should be used here instead.

↑ V560 A part of conditional expression is always true: pParaContext.