    /* -*- 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 "PropertyMap.hxx"
    #include <ooxml/resourceids.hxx>
    #include "DomainMapper_Impl.hxx"
    #include "ConversionHelper.hxx"
    #include <editeng/boxitem.hxx>
    #include <i18nutil/paper.hxx>
    #include <osl/diagnose.h>
    #include <rtl/ustring.hxx>
    #include <sal/log.hxx>
    #include <com/sun/star/beans/PropertyAttribute.hpp>
    #include <com/sun/star/beans/PropertyValue.hpp>
    #include <com/sun/star/beans/XMultiPropertySet.hpp>
    #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
    #include <com/sun/star/lang/XMultiServiceFactory.hpp>
    #include <com/sun/star/table/BorderLine2.hpp>
    #include <com/sun/star/container/XEnumeration.hpp>
    #include <com/sun/star/container/XEnumerationAccess.hpp>
    #include <com/sun/star/container/XNameContainer.hpp>
    #include <com/sun/star/style/BreakType.hpp>
    #include <com/sun/star/style/PageStyleLayout.hpp>
    #include <com/sun/star/style/XStyle.hpp>
    #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
    #include <com/sun/star/table/ShadowFormat.hpp>
    #include <com/sun/star/text/RelOrientation.hpp>
    #include <com/sun/star/text/HoriOrientation.hpp>
    #include <com/sun/star/text/HorizontalAdjust.hpp>
    #include <com/sun/star/text/SizeType.hpp>
    #include <com/sun/star/text/VertOrientation.hpp>
    #include <com/sun/star/text/WritingMode.hpp>
    #include <com/sun/star/text/WritingMode2.hpp>
    #include <com/sun/star/text/XTextColumns.hpp>
    #include <com/sun/star/text/XText.hpp>
    #include <com/sun/star/text/TextGridMode.hpp>
    #include <com/sun/star/text/XTextCopy.hpp>
    #include <com/sun/star/style/VerticalAlignment.hpp>
    #include <comphelper/sequence.hxx>
    #include <comphelper/propertyvalue.hxx>
    #include <tools/diagnose_ex.h>
    #include "PropertyMapHelper.hxx"
    #include <set>

    using namespace com::sun::star;

    namespace writerfilter {
    namespace dmapper {

    uno::Sequence< beans::PropertyValue > PropertyMap::GetPropertyValues( bool bCharGrabBag )
    {
        using comphelper::makePropertyValue;

        if ( m_aValues.empty() && !m_vMap.empty() )
        {
            size_t nCharGrabBag = 0;
            size_t nParaGrabBag = 0;
            size_t nCellGrabBag = 0;
            size_t nRowGrabBag  = 0;

            const PropValue* pParaStyleProp = nullptr;
            const PropValue* pCharStyleProp = nullptr;
            const PropValue* pNumRuleProp   = nullptr;

            for ( const auto& rPropPair : m_vMap )
            {
                if ( rPropPair.second.getGrabBagType() == CHAR_GRAB_BAG )
                    nCharGrabBag++;
                else if ( rPropPair.second.getGrabBagType() == PARA_GRAB_BAG )
                    nParaGrabBag++;
                else if ( rPropPair.second.getGrabBagType() == CELL_GRAB_BAG )
                    nCellGrabBag++;
                else if ( rPropPair.first == PROP_CELL_INTEROP_GRAB_BAG )
                {
                    uno::Sequence< beans::PropertyValue > aSeq;
                    rPropPair.second.getValue() >>= aSeq;
                    nCellGrabBag += aSeq.getLength();
                }
                else if ( rPropPair.second.getGrabBagType() == ROW_GRAB_BAG )
                    nRowGrabBag++;

                if ( rPropPair.first == PROP_PARA_STYLE_NAME ) pParaStyleProp = &rPropPair.second;
                if ( rPropPair.first == PROP_CHAR_STYLE_NAME ) pCharStyleProp = &rPropPair.second;
                if ( rPropPair.first == PROP_NUMBERING_RULES ) pNumRuleProp   = &rPropPair.second;
            }

            // Style names have to be the first elements within the property sequence
            // otherwise they will overwrite 'hard' attributes
            if ( pParaStyleProp != nullptr )
                m_aValues.push_back( makePropertyValue( getPropertyName( PROP_PARA_STYLE_NAME ), pParaStyleProp->getValue() ) );
            if ( pCharStyleProp != nullptr )
                m_aValues.push_back( makePropertyValue( getPropertyName( PROP_CHAR_STYLE_NAME ), pCharStyleProp->getValue() ) );
            if ( pNumRuleProp != nullptr )
                m_aValues.push_back( makePropertyValue(getPropertyName( PROP_NUMBERING_RULES ), pNumRuleProp->getValue() ) );

            // If there are any grab bag properties, we need one slot for them.
            uno::Sequence< beans::PropertyValue > aCharGrabBagValues( nCharGrabBag );
            uno::Sequence< beans::PropertyValue > aParaGrabBagValues( nParaGrabBag );
            uno::Sequence< beans::PropertyValue > aCellGrabBagValues( nCellGrabBag );
            uno::Sequence< beans::PropertyValue > aRowGrabBagValues ( nRowGrabBag );
            beans::PropertyValue* pCharGrabBagValues = aCharGrabBagValues.getArray();
            beans::PropertyValue* pParaGrabBagValues = aParaGrabBagValues.getArray();
            beans::PropertyValue* pCellGrabBagValues = aCellGrabBagValues.getArray();
            beans::PropertyValue* pRowGrabBagValues  = aRowGrabBagValues.getArray();
            // Record index for the next property to be added in each grab bag.
            sal_Int32 nRowGrabBagValue  = 0;
            sal_Int32 nCellGrabBagValue = 0;
            sal_Int32 nParaGrabBagValue = 0;
            sal_Int32 nCharGrabBagValue = 0;

            for ( const auto& rPropPair : m_vMap )
            {
                if ( rPropPair.first != PROP_PARA_STYLE_NAME &&
                     rPropPair.first != PROP_CHAR_STYLE_NAME &&
                     rPropPair.first != PROP_NUMBERING_RULES )
                {
                    if ( rPropPair.second.getGrabBagType() == CHAR_GRAB_BAG )
                    {
                        if ( bCharGrabBag )
                        {
                            pCharGrabBagValues[nCharGrabBagValue].Name  = getPropertyName( rPropPair.first );
                            pCharGrabBagValues[nCharGrabBagValue].Value = rPropPair.second.getValue();
                            ++nCharGrabBagValue;
                        }
                    }
                    else if ( rPropPair.second.getGrabBagType() == PARA_GRAB_BAG )
                    {
                        pParaGrabBagValues[nParaGrabBagValue].Name  = getPropertyName( rPropPair.first );
                        pParaGrabBagValues[nParaGrabBagValue].Value = rPropPair.second.getValue();
                        ++nParaGrabBagValue;
                    }
                    else if ( rPropPair.second.getGrabBagType() == CELL_GRAB_BAG )
                    {
                        pCellGrabBagValues[nCellGrabBagValue].Name  = getPropertyName( rPropPair.first );
                        pCellGrabBagValues[nCellGrabBagValue].Value = rPropPair.second.getValue();
                        ++nCellGrabBagValue;
                    }
                    else if ( rPropPair.second.getGrabBagType() == ROW_GRAB_BAG )
                    {
                        pRowGrabBagValues[nRowGrabBagValue].Name  = getPropertyName( rPropPair.first );
                        pRowGrabBagValues[nRowGrabBagValue].Value = rPropPair.second.getValue();
                        ++nRowGrabBagValue;
                    }
                    else if ( rPropPair.first == PROP_CELL_INTEROP_GRAB_BAG )
                    {
                        uno::Sequence< beans::PropertyValue > aSeq;
                        rPropPair.second.getValue() >>= aSeq;
                        for ( sal_Int32 i = 0; i < aSeq.getLength(); ++i )
                        {
                            pCellGrabBagValues[nCellGrabBagValue] = aSeq[i];
                            ++nCellGrabBagValue;
                        }
                    }
                    else
                    {
                        m_aValues.push_back( makePropertyValue( getPropertyName( rPropPair.first ), rPropPair.second.getValue() ) );
                    }
                }
            }

            if ( nCharGrabBag && bCharGrabBag )
                m_aValues.push_back( makePropertyValue( "CharInteropGrabBag", uno::makeAny( aCharGrabBagValues ) ) );

            if ( nParaGrabBag )
                m_aValues.push_back( makePropertyValue( "ParaInteropGrabBag", uno::makeAny( aParaGrabBagValues ) ) );

            if ( nCellGrabBag )
                m_aValues.push_back( makePropertyValue( "CellInteropGrabBag", uno::makeAny( aCellGrabBagValues ) ) );

            if ( nRowGrabBag )
                m_aValues.push_back( makePropertyValue( "RowInteropGrabBag", uno::makeAny( aRowGrabBagValues ) ) );
        }

        return comphelper::containerToSequence( m_aValues );
    }

    #ifdef DEBUG_WRITERFILTER
    static void lcl_AnyToTag( const uno::Any& rAny )
    {
        try {
            sal_Int32 aInt = 0;
            if ( rAny >>= aInt )
            {
                TagLogger::getInstance().attribute( "value", rAny );
            }
            else
            {
                TagLogger::getInstance().attribute( "unsignedValue", 0 );
            }

            sal_uInt32 auInt = 0;
            rAny >>= auInt;
            TagLogger::getInstance().attribute( "unsignedValue", auInt );

            float aFloat = 0.0f;
            if ( rAny >>= aFloat )
            {
                TagLogger::getInstance().attribute( "floatValue", rAny );
            }
            else
            {
                TagLogger::getInstance().attribute( "unsignedValue", 0 );
            }

            OUString aStr;
            rAny >>= aStr;
            TagLogger::getInstance().attribute( "stringValue", aStr );
        }
        catch ( ... )
        {
        }
    }
    #endif

    void PropertyMap::Insert( PropertyIds eId, const uno::Any& rAny, bool bOverwrite, GrabBagType i_GrabBagType )
    {
    #ifdef DEBUG_WRITERFILTER
        const OUString& rInsert = getPropertyName(eId);

        TagLogger::getInstance().startElement("propertyMap.insert");
        TagLogger::getInstance().attribute("name", rInsert);
        lcl_AnyToTag(rAny);
        TagLogger::getInstance().endElement();
    #endif

        if ( !bOverwrite )
            m_vMap.insert(std::make_pair(eId, PropValue(rAny, i_GrabBagType)));
        else
            m_vMap[eId] = PropValue(rAny, i_GrabBagType);

        Invalidate();
    }

    void PropertyMap::Erase( PropertyIds eId )
    {
        // Safe call to erase, it throws no exceptions, even if eId is not in m_vMap
        m_vMap.erase(eId);

        Invalidate();
    }

    boost::optional< PropertyMap::Property > PropertyMap::getProperty( PropertyIds eId ) const
    {
        std::map< PropertyIds, PropValue >::const_iterator aIter = m_vMap.find( eId );
        if ( aIter == m_vMap.end() )
            return boost::optional<Property>();
        else
            return std::make_pair( eId, aIter->second.getValue() );
    }

    bool PropertyMap::isSet( PropertyIds eId) const
    {
        return m_vMap.find( eId ) != m_vMap.end();
    }

    #ifdef DEBUG_WRITERFILTER
    void PropertyMap::dumpXml() const
    {
        TagLogger::getInstance().startElement( "PropertyMap" );

        for ( const auto& rPropPair : m_vMap )
        {
            TagLogger::getInstance().startElement( "property" );

            TagLogger::getInstance().attribute( "name", getPropertyName( rPropPair.first ) );

            switch ( rPropPair.first )
            {
                case PROP_TABLE_COLUMN_SEPARATORS:
                    lcl_DumpTableColumnSeparators( rPropPair.second.getValue() );
                    break;
                default:
                {
                    try
                    {
                        sal_Int32 aInt = 0;
                        rPropPair.second.getValue() >>= aInt;
                        TagLogger::getInstance().attribute( "value", aInt );

                        sal_uInt32 auInt = 0;
                        rPropPair.second.getValue() >>= auInt;
                        TagLogger::getInstance().attribute( "unsignedValue", auInt );

                        float aFloat = 0.0;
                        rPropPair.second.getValue() >>= aFloat;
                        TagLogger::getInstance().attribute( "floatValue", aFloat );

                        rPropPair.second.getValue() >>= auInt;
                        TagLogger::getInstance().attribute( "stringValue", OUString() );
                    }
                    catch ( ... )
                    {
                    }
                }
                break;
            }

            TagLogger::getInstance().endElement();
        }

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

    void PropertyMap::InsertProps( const PropertyMapPtr& rMap, const bool bOverwrite )
    {
        if ( rMap )
        {
            for ( const auto& rPropPair : rMap->m_vMap )
            {
                if ( bOverwrite || !m_vMap.count(rPropPair.first) )
                    m_vMap[rPropPair.first] = rPropPair.second;
            }

            insertTableProperties( rMap.get(), bOverwrite );

            Invalidate();
        }
    }

    void PropertyMap::insertTableProperties( const PropertyMap*, const bool )
    {
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().element( "PropertyMap.insertTableProperties" );
    #endif
    }

    void PropertyMap::printProperties()
    {
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement( "properties" );

        for ( const auto& rPropPair : m_vMap )
        {
            SAL_INFO( "writerfilter", getPropertyName( rPropPair.first ) );

            table::BorderLine2 aLine;
            sal_Int32 nColor;
            if ( rPropPair.second.getValue() >>= aLine )
            {
                TagLogger::getInstance().startElement( "borderline" );
                TagLogger::getInstance().attribute( "color", aLine.Color );
                TagLogger::getInstance().attribute( "inner", aLine.InnerLineWidth );
                TagLogger::getInstance().attribute( "outer", aLine.OuterLineWidth );
                TagLogger::getInstance().endElement();
            }
            else if ( rPropPair.second.getValue() >>= nColor )
            {
                TagLogger::getInstance().startElement( "color" );
                TagLogger::getInstance().attribute( "number", nColor );
                TagLogger::getInstance().endElement();
            }
        }

        TagLogger::getInstance().endElement();
    #else
        (void) this; // avoid loplugin:staticmethods
    #endif
    }

    SectionPropertyMap::SectionPropertyMap( bool bIsFirstSection )
        : m_bIsFirstSection( bIsFirstSection )
        , m_eBorderApply( BorderApply::ToAllInSection )
        , m_eBorderOffsetFrom( BorderOffsetFrom::Text )
        , m_bTitlePage( false )
        , m_nColumnCount( 0 )
        , m_nColumnDistance( 1249 )
        , m_bSeparatorLineIsOn( false )
        , m_bEvenlySpaced( false )
        , m_nPageNumber( -1 )
        , m_nPageNumberType( -1 )
        , m_nBreakType( -1 )
        , m_nPaperBin( -1 )
        , m_nFirstPaperBin( -1 )
        , m_nLeftMargin( 3175 )  // page left margin,  default 0x708 (1800) twip -> 3175 1/100 mm
        , m_nRightMargin( 3175 ) // page right margin,  default 0x708 (1800) twip -> 3175 1/100 mm
        , m_nTopMargin( 2540 )
        , m_nBottomMargin( 2540 )
        , m_nHeaderTop( 1270 )    // 720 twip
        , m_nHeaderBottom( 1270 ) // 720 twip
        , m_nGridType( 0 )
        , m_nGridLinePitch( 1 )
        , m_nDxtCharSpace( 0 )
        , m_bGridSnapToChars( true )
        , m_nLnnMod( 0 )
        , m_nLnc( 0 )
        , m_ndxaLnn( 0 )
        , m_nLnnMin( 0 )
        , m_bDefaultHeaderLinkToPrevious( true )
        , m_bEvenPageHeaderLinkToPrevious( true )
        , m_bFirstPageHeaderLinkToPrevious( true )
        , m_bDefaultFooterLinkToPrevious( true )
        , m_bEvenPageFooterLinkToPrevious( true )
        , m_bFirstPageFooterLinkToPrevious( true )
    {
    #ifdef DEBUG_WRITERFILTER
        static sal_Int32 nNumber = 0;
        m_nDebugSectionNumber = nNumber++;
    #endif

        for ( sal_Int32 nBorder = 0; nBorder < 4; ++nBorder )
        {
            m_nBorderDistances[nBorder] = -1;
            m_bBorderShadows[nBorder] = false;
        }
        // todo: set defaults in ApplyPropertiesToPageStyles
        // initialize defaults
        PaperInfo aLetter( PAPER_LETTER );
        // page height, 1/100mm
        Insert( PROP_HEIGHT, uno::makeAny( static_cast<sal_Int32>(aLetter.getHeight()) ) );
        // page width, 1/100mm
        Insert( PROP_WIDTH, uno::makeAny( static_cast<sal_Int32>(aLetter.getWidth()) ) );
        // page left margin, default 0x708 (1800) twip -> 3175 1/100 mm
        Insert( PROP_LEFT_MARGIN, uno::makeAny( sal_Int32(3175) ) );
        // page right margin, default 0x708 (1800) twip -> 3175 1/100 mm
        Insert( PROP_RIGHT_MARGIN, uno::makeAny( sal_Int32(3175) ) );
        // page top margin, default 0x5a0 (1440) twip -> 2540 1/100 mm
        Insert( PROP_TOP_MARGIN, uno::makeAny( sal_Int32(2540) ) );
        // page bottom margin, default 0x5a0 (1440) twip -> 2540 1/100 mm
        Insert( PROP_BOTTOM_MARGIN, uno::makeAny( sal_Int32(2540) ) );
        // page style layout
        Insert( PROP_PAGE_STYLE_LAYOUT, uno::makeAny( style::PageStyleLayout_ALL ) );
        uno::Any aFalse( uno::makeAny( false ) );
        Insert( PROP_GRID_DISPLAY, aFalse );
        Insert( PROP_GRID_PRINT, aFalse );
        Insert( PROP_GRID_MODE, uno::makeAny( text::TextGridMode::NONE ) );

        if ( m_bIsFirstSection )
        {
            m_sFirstPageStyleName = getPropertyName( PROP_FIRST_PAGE );
            m_sFollowPageStyleName = getPropertyName( PROP_STANDARD );
        }
    }

    OUString lcl_FindUnusedPageStyleName( const uno::Sequence< OUString >& rPageStyleNames )
    {
        static const char DEFAULT_STYLE[] = "Converted";
        sal_Int32         nMaxIndex       = 0;
        // find the highest number x in each style with the name "DEFAULT_STYLE+x" and
        // return an incremented name

        const OUString* pStyleNames = rPageStyleNames.getConstArray();
        for ( sal_Int32 nStyle = 0; nStyle < rPageStyleNames.getLength(); ++nStyle )
        {
            if ( pStyleNames[nStyle].startsWith( DEFAULT_STYLE ) )
            {
                sal_Int32 nIndex = pStyleNames[nStyle].copy( strlen( DEFAULT_STYLE ) ).toInt32();
                if ( nIndex > nMaxIndex )
                    nMaxIndex = nIndex;
            }
        }

        return DEFAULT_STYLE + OUString::number( nMaxIndex + 1 );
    }

    uno::Reference< beans::XPropertySet > SectionPropertyMap::GetPageStyle( const uno::Reference< container::XNameContainer >& xPageStyles,
                                                                            const uno::Reference < lang::XMultiServiceFactory >& xTextFactory,
                                                                            bool bFirst )
    {
        uno::Reference< beans::XPropertySet > xRet;
        try
        {
            if ( bFirst )
            {
                if ( m_sFirstPageStyleName.isEmpty() && xPageStyles.is() )
                {
                    uno::Sequence< OUString > aPageStyleNames = xPageStyles->getElementNames();
                    m_sFirstPageStyleName = lcl_FindUnusedPageStyleName( aPageStyleNames );
                    m_aFirstPageStyle.set( xTextFactory->createInstance( "com.sun.star.style.PageStyle" ),
                        uno::UNO_QUERY );

                    // Call insertByName() before GetPageStyle(), otherwise the
                    // first and the follow page style will have the same name, and
                    // insertByName() will fail.
                    if ( xPageStyles.is() )
                        xPageStyles->insertByName( m_sFirstPageStyleName, uno::makeAny( m_aFirstPageStyle ) );

                    // Ensure that m_aFollowPageStyle has been created
                    GetPageStyle( xPageStyles, xTextFactory, false );
                    // Chain m_aFollowPageStyle to be after m_aFirstPageStyle
                    m_aFirstPageStyle->setPropertyValue( "FollowStyle",
                        uno::makeAny( m_sFollowPageStyleName ) );
                }
                else if ( !m_aFirstPageStyle.is() && xPageStyles.is() )
                {
                    xPageStyles->getByName( m_sFirstPageStyleName ) >>= m_aFirstPageStyle;
                }
                xRet = m_aFirstPageStyle;
            }
            else
            {
                if ( m_sFollowPageStyleName.isEmpty() && xPageStyles.is() )
                {
                    uno::Sequence< OUString > aPageStyleNames = xPageStyles->getElementNames();
                    m_sFollowPageStyleName = lcl_FindUnusedPageStyleName( aPageStyleNames );
                    m_aFollowPageStyle.set( xTextFactory->createInstance( "com.sun.star.style.PageStyle" ),
                        uno::UNO_QUERY );
                    xPageStyles->insertByName( m_sFollowPageStyleName, uno::makeAny( m_aFollowPageStyle ) );
                }
                else if ( !m_aFollowPageStyle.is() && xPageStyles.is() )
                {
                    xPageStyles->getByName( m_sFollowPageStyleName ) >>= m_aFollowPageStyle;
                }
                xRet = m_aFollowPageStyle;
            }

        }
        catch ( const uno::Exception& )
        {
            DBG_UNHANDLED_EXCEPTION( "writerfilter" );
        }

        return xRet;
    }

    void SectionPropertyMap::SetBorder( BorderPosition ePos, sal_Int32 nLineDistance, const table::BorderLine2& rBorderLine, bool bShadow )
    {
        m_oBorderLines[ePos]     = rBorderLine;
        m_nBorderDistances[ePos] = nLineDistance;
        m_bBorderShadows[ePos]   = bShadow;
    }

    void SectionPropertyMap::ApplyBorderToPageStyles( const uno::Reference< container::XNameContainer >& xPageStyles,
                                                      const uno::Reference < lang::XMultiServiceFactory >& xTextFactory,
                                                      BorderApply eBorderApply, BorderOffsetFrom eOffsetFrom )
    {
        /*
        page border applies to:
        nIntValue & 0x07 ->
        0 all pages in this section
        1 first page in this section
        2 all pages in this section but first
        3 whole document (all sections)
        nIntValue & 0x18 -> page border depth 0 - in front 1- in back
        nIntValue & 0xe0 ->
        page border offset from:
        0 offset from text
        1 offset from edge of page
        */
        uno::Reference< beans::XPropertySet > xFirst;
        uno::Reference< beans::XPropertySet > xSecond;
        // todo: negative spacing (from ww8par6.cxx)
        switch ( eBorderApply )
        {
            case BorderApply::ToAllInSection: // all styles
                if ( !m_sFollowPageStyleName.isEmpty() )
                    xFirst = GetPageStyle( xPageStyles, xTextFactory, false );
                if ( !m_sFirstPageStyleName.isEmpty() )
                    xSecond = GetPageStyle( xPageStyles, xTextFactory, true );
                break;
            case BorderApply::ToFirstPageInSection: // first page
                if ( !m_sFirstPageStyleName.isEmpty() )
                    xFirst = GetPageStyle( xPageStyles, xTextFactory, true );
                break;
            case BorderApply::ToAllButFirstInSection: // left and right
                if ( !m_sFollowPageStyleName.isEmpty() )
                    xFirst = GetPageStyle( xPageStyles, xTextFactory, false );
                break;
            default:
                return;
        }

        // has to be sorted like enum BorderPosition: l-r-t-b
        const PropertyIds aBorderIds[4] =
        {
            PROP_LEFT_BORDER,
            PROP_RIGHT_BORDER,
            PROP_TOP_BORDER,
            PROP_BOTTOM_BORDER
        };

        const PropertyIds aBorderDistanceIds[4] =
        {
            PROP_LEFT_BORDER_DISTANCE,
            PROP_RIGHT_BORDER_DISTANCE,
            PROP_TOP_BORDER_DISTANCE,
            PROP_BOTTOM_BORDER_DISTANCE
        };

        const PropertyIds aMarginIds[4] =
        {
            PROP_LEFT_MARGIN,
            PROP_RIGHT_MARGIN,
            PROP_TOP_MARGIN,
            PROP_BOTTOM_MARGIN
        };

        for ( sal_Int32 nBorder = 0; nBorder < 4; ++nBorder )
        {
            if ( m_oBorderLines[nBorder] )
            {
                const OUString sBorderName = getPropertyName( aBorderIds[nBorder] );
                if ( xFirst.is() )
                    xFirst->setPropertyValue( sBorderName, uno::makeAny( *m_oBorderLines[nBorder] ) );
                if ( xSecond.is() )
                    xSecond->setPropertyValue( sBorderName, uno::makeAny( *m_oBorderLines[nBorder] ) );
            }
            if ( m_nBorderDistances[nBorder] >= 0 )
            {
                sal_uInt32 nLineWidth = 0;
                if ( m_oBorderLines[nBorder] )
                    nLineWidth = m_oBorderLines[nBorder]->LineWidth;
                if ( xFirst.is() )
                    SetBorderDistance( xFirst, aMarginIds[nBorder], aBorderDistanceIds[nBorder],
                        m_nBorderDistances[nBorder], eOffsetFrom, nLineWidth );
                if ( xSecond.is() )
                    SetBorderDistance( xSecond, aMarginIds[nBorder], aBorderDistanceIds[nBorder],
                        m_nBorderDistances[nBorder], eOffsetFrom, nLineWidth );
            }
        }

        if ( m_bBorderShadows[BORDER_RIGHT] )
        {
            table::ShadowFormat aFormat = getShadowFromBorder( *m_oBorderLines[BORDER_RIGHT] );
            if ( xFirst.is() )
                xFirst->setPropertyValue( getPropertyName( PROP_SHADOW_FORMAT ), uno::makeAny( aFormat ) );
            if ( xSecond.is() )
                xSecond->setPropertyValue( getPropertyName( PROP_SHADOW_FORMAT ), uno::makeAny( aFormat ) );
        }
    }

    table::ShadowFormat PropertyMap::getShadowFromBorder( const table::BorderLine2& rBorder )
    {
        // In Word UI, shadow is a boolean property, in OOXML, it's a boolean
        // property of each 4 border type, finally in Writer the border is a
        // property of the page style, with shadow location, distance and
        // color. See SwWW8ImplReader::SetShadow().
        table::ShadowFormat aFormat;
        aFormat.Color       = sal_Int32(COL_BLACK);
        aFormat.Location    = table::ShadowLocation_BOTTOM_RIGHT;
        aFormat.ShadowWidth = rBorder.LineWidth;
        return aFormat;
    }

    void SectionPropertyMap::SetBorderDistance( const uno::Reference< beans::XPropertySet >& xStyle,
                                                PropertyIds eMarginId,
                                                PropertyIds eDistId,
                                                sal_Int32 nDistance,
                                                BorderOffsetFrom eOffsetFrom,
                                                sal_uInt32 nLineWidth )
    {
        if (!xStyle.is())
            return;
        const OUString sMarginName = getPropertyName( eMarginId );
        const OUString sBorderDistanceName = getPropertyName( eDistId );
        uno::Any aMargin = xStyle->getPropertyValue( sMarginName );
        sal_Int32 nMargin = 0;
        aMargin >>= nMargin;
        editeng::BorderDistanceFromWord(eOffsetFrom == BorderOffsetFrom::Edge, nMargin, nDistance,
                                        nLineWidth);

        // Change the margins with the border distance
        uno::Reference< beans::XMultiPropertySet > xMultiSet( xStyle, uno::UNO_QUERY_THROW );
        uno::Sequence<OUString> aProperties { sMarginName, sBorderDistanceName };
        uno::Sequence<uno::Any> aValues { uno::makeAny( nMargin ), uno::makeAny( nDistance ) };
        xMultiSet->setPropertyValues( aProperties, aValues );
    }

    void SectionPropertyMap::DontBalanceTextColumns()
    {
        try
        {
            if ( m_xColumnContainer.is() )
                m_xColumnContainer->setPropertyValue( "DontBalanceTextColumns", uno::makeAny( true ) );
        }
        catch ( const uno::Exception& )
        {
            OSL_FAIL( "Exception in SectionPropertyMap::DontBalanceTextColumns" );
        }
    }

    uno::Reference< text::XTextColumns > SectionPropertyMap::ApplyColumnProperties( const uno::Reference< beans::XPropertySet >& xColumnContainer,
                                                                                    DomainMapper_Impl& rDM_Impl )
    {
        uno::Reference< text::XTextColumns > xColumns;
        try
        {
            const OUString sTextColumns = getPropertyName( PROP_TEXT_COLUMNS );
            if ( xColumnContainer.is() )
                xColumnContainer->getPropertyValue( sTextColumns ) >>= xColumns;
            uno::Reference< beans::XPropertySet > xColumnPropSet( xColumns, uno::UNO_QUERY_THROW );
            if ( !m_bEvenlySpaced &&
                 ( sal_Int32(m_aColWidth.size()) == (m_nColumnCount + 1) ) &&
                 ( (sal_Int32(m_aColDistance.size()) == m_nColumnCount) || (sal_Int32(m_aColDistance.size()) == m_nColumnCount + 1) ) )
            {
                // the column width in word is an absolute value, in OOo it's relative
                // the distances are both absolute
                sal_Int32 nColSum = 0;
                for ( sal_Int32 nCol = 0; nCol <= m_nColumnCount; ++nCol )
                {
                    nColSum += m_aColWidth[nCol];
                    if ( nCol )
                        nColSum += m_aColDistance[nCol - 1];
                }

                sal_Int32 nRefValue = xColumns->getReferenceValue();
                double fRel = nColSum ? double( nRefValue ) / double( nColSum ) : 0.0;
                uno::Sequence< text::TextColumn > aColumns( m_nColumnCount + 1 );
                text::TextColumn* pColumn = aColumns.getArray();

                nColSum = 0;
                for ( sal_Int32 nCol = 0; nCol <= m_nColumnCount; ++nCol )
                {
                    pColumn[nCol].LeftMargin = nCol ? m_aColDistance[nCol - 1] / 2 : 0;
                    pColumn[nCol].RightMargin = nCol == m_nColumnCount ? 0 : m_aColDistance[nCol] / 2;
                    pColumn[nCol].Width = sal_Int32( (double( m_aColWidth[nCol] + pColumn[nCol].RightMargin + pColumn[nCol].LeftMargin ) + 0.5) * fRel );
                    nColSum += pColumn[nCol].Width;
                }
                if ( nColSum != nRefValue )
                    pColumn[m_nColumnCount].Width -= (nColSum - nRefValue);
                xColumns->setColumns( aColumns );
            }
            else
            {
                xColumns->setColumnCount( m_nColumnCount + 1 );
                xColumnPropSet->setPropertyValue( getPropertyName( PROP_AUTOMATIC_DISTANCE ), uno::makeAny( m_nColumnDistance ) );
            }

            if ( m_bSeparatorLineIsOn )
            {
                xColumnPropSet->setPropertyValue( "SeparatorLineIsOn", uno::makeAny( true ) );
                xColumnPropSet->setPropertyValue( "SeparatorLineVerticalAlignment", uno::makeAny( style::VerticalAlignment_TOP ) );
                xColumnPropSet->setPropertyValue( "SeparatorLineRelativeHeight", uno::makeAny( static_cast<sal_Int8>(100) ) );
                xColumnPropSet->setPropertyValue( "SeparatorLineColor", uno::makeAny( static_cast<sal_Int32>(COL_BLACK) ) );
                // 1 twip -> 2 mm100.
                xColumnPropSet->setPropertyValue( "SeparatorLineWidth", uno::makeAny( static_cast<sal_Int32>(2) ) );
            }
            xColumnContainer->setPropertyValue( sTextColumns, uno::makeAny( xColumns ) );
            // Set the columns to be unbalanced if that compatibility option is set or this is the last section.
            m_xColumnContainer = xColumnContainer;
            if ( rDM_Impl.GetSettingsTable()->GetNoColumnBalance() || rDM_Impl.GetIsLastSectionGroup() )
                DontBalanceTextColumns();
        }
        catch ( const uno::Exception& )
        {
            OSL_FAIL( "Exception in SectionPropertyMap::ApplyColumnProperties" );
        }
        return xColumns;
    }

    bool SectionPropertyMap::HasHeader( bool bFirstPage ) const
    {
        bool bRet = false;
        if ( (bFirstPage && m_aFirstPageStyle.is()) || (!bFirstPage && m_aFollowPageStyle.is()) )
        {
            if ( bFirstPage )
                m_aFirstPageStyle->getPropertyValue(
                    getPropertyName( PROP_HEADER_IS_ON ) ) >>= bRet;
            else
                m_aFollowPageStyle->getPropertyValue(
                    getPropertyName( PROP_HEADER_IS_ON ) ) >>= bRet;
        }
        return bRet;
    }

    bool SectionPropertyMap::HasFooter( bool bFirstPage ) const
    {
        bool bRet = false;
        if ( (bFirstPage && m_aFirstPageStyle.is()) || (!bFirstPage && m_aFollowPageStyle.is()) )
        {
            if ( bFirstPage )
                m_aFirstPageStyle->getPropertyValue( getPropertyName( PROP_FOOTER_IS_ON ) ) >>= bRet;
            else
                m_aFollowPageStyle->getPropertyValue( getPropertyName( PROP_FOOTER_IS_ON ) ) >>= bRet;
        }
        return bRet;
    }

    #define MIN_HEAD_FOOT_HEIGHT 100 // minimum header/footer height

    void SectionPropertyMap::CopyHeaderFooterTextProperty( const uno::Reference< beans::XPropertySet >& xPrevStyle,
                                                           const uno::Reference< beans::XPropertySet >& xStyle,
                                                           PropertyIds ePropId )
    {
        try {
            OUString sName = getPropertyName( ePropId );

            SAL_INFO( "writerfilter", "Copying " << sName );
            uno::Reference< text::XTextCopy > xTxt;
            if ( xStyle.is() )
                xTxt.set( xStyle->getPropertyValue( sName ), uno::UNO_QUERY_THROW );

            uno::Reference< text::XTextCopy > xPrevTxt;
            if ( xPrevStyle.is() )
                xPrevTxt.set( xPrevStyle->getPropertyValue( sName ), uno::UNO_QUERY_THROW );

            xTxt->copyText( xPrevTxt );
        }
        catch ( const uno::Exception& e )
        {
            SAL_INFO( "writerfilter", "An exception occurred in SectionPropertyMap::CopyHeaderFooterTextProperty( ) - " << e );
        }
    }

    // Copy headers and footers from the previous page style.
    void SectionPropertyMap::CopyHeaderFooter( const uno::Reference< beans::XPropertySet >& xPrevStyle,
                                               const uno::Reference< beans::XPropertySet >& xStyle,
                                               bool bOmitRightHeader,
                                               bool bOmitLeftHeader,
                                               bool bOmitRightFooter,
                                               bool bOmitLeftFooter )
    {
        bool bHasPrevHeader = false;
        bool bHeaderIsShared = true;
        OUString sHeaderIsOn = getPropertyName( PROP_HEADER_IS_ON );
        OUString sHeaderIsShared = getPropertyName( PROP_HEADER_IS_SHARED );
        if ( xPrevStyle.is() )
        {
            xPrevStyle->getPropertyValue( sHeaderIsOn ) >>= bHasPrevHeader;
            xPrevStyle->getPropertyValue( sHeaderIsShared ) >>= bHeaderIsShared;
        }

        if ( bHasPrevHeader )
        {
            uno::Reference< beans::XMultiPropertySet > xMultiSet( xStyle, uno::UNO_QUERY_THROW );
            uno::Sequence<OUString> aProperties { sHeaderIsOn, sHeaderIsShared };
            uno::Sequence<uno::Any> aValues { uno::makeAny( true ), uno::makeAny( bHeaderIsShared ) };
            xMultiSet->setPropertyValues( aProperties, aValues );
            if ( !bOmitRightHeader )
            {
                CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
                    PROP_HEADER_TEXT );
            }
            if ( !bHeaderIsShared && !bOmitLeftHeader )
            {
                CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
                    PROP_HEADER_TEXT_LEFT );
            }
        }

        bool bHasPrevFooter = false;
        bool bFooterIsShared = true;
        OUString sFooterIsOn = getPropertyName( PROP_FOOTER_IS_ON );
        OUString sFooterIsShared = getPropertyName( PROP_FOOTER_IS_SHARED );
        if ( xPrevStyle.is() )
        {
            xPrevStyle->getPropertyValue( sFooterIsOn ) >>= bHasPrevFooter;
            xPrevStyle->getPropertyValue( sFooterIsShared ) >>= bFooterIsShared;
        }

        if ( bHasPrevFooter )
        {
            uno::Reference< beans::XMultiPropertySet > xMultiSet( xStyle, uno::UNO_QUERY_THROW );
            uno::Sequence<OUString> aProperties { sFooterIsOn, sFooterIsShared };
            uno::Sequence<uno::Any> aValues { uno::makeAny( true ), uno::makeAny( bFooterIsShared ) };
            xMultiSet->setPropertyValues( aProperties, aValues );
            if ( !bOmitRightFooter )
            {
                CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
                    PROP_FOOTER_TEXT );
            }
            if ( !bFooterIsShared && !bOmitLeftFooter )
            {
                CopyHeaderFooterTextProperty( xPrevStyle, xStyle,
                    PROP_FOOTER_TEXT_LEFT );
            }
        }
    }

    // Copy header and footer content from the previous docx section as needed.
    //
    // Any headers and footers which were not defined in this docx section
    // should be "linked" with the corresponding header or footer from the
    // previous section.  LO does not support linking of header/footer content
    // across page styles so we just copy the content from the previous section.
    void SectionPropertyMap::CopyLastHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl )
    {
        SAL_INFO( "writerfilter", "START>>> SectionPropertyMap::CopyLastHeaderFooter()" );
        SectionPropertyMap* pLastContext = rDM_Impl.GetLastSectionContext();
        if ( pLastContext )
        {
            uno::Reference< beans::XPropertySet > xPrevStyle = pLastContext->GetPageStyle( rDM_Impl.GetPageStyles(),
                rDM_Impl.GetTextFactory(),
                bFirstPage );
            uno::Reference< beans::XPropertySet > xStyle = GetPageStyle( rDM_Impl.GetPageStyles(),
                rDM_Impl.GetTextFactory(),
                bFirstPage );

            if ( bFirstPage )
            {
                CopyHeaderFooter( xPrevStyle, xStyle,
                    !m_bFirstPageHeaderLinkToPrevious, true,
                    !m_bFirstPageFooterLinkToPrevious, true );
            }
            else
            {
                CopyHeaderFooter( xPrevStyle, xStyle,
                    !m_bDefaultHeaderLinkToPrevious,
                    !m_bEvenPageHeaderLinkToPrevious,
                    !m_bDefaultFooterLinkToPrevious,
                    !m_bEvenPageFooterLinkToPrevious );
            }
        }
        SAL_INFO( "writerfilter", "END>>> SectionPropertyMap::CopyLastHeaderFooter()" );
    }

    void SectionPropertyMap::PrepareHeaderFooterProperties( bool bFirstPage )
    {
        bool bCopyFirstToFollow = bFirstPage && m_bTitlePage && m_aFollowPageStyle.is();
        if (bCopyFirstToFollow)
        {
            // This is a first page and has a follow style, then enable the
            // header/footer there as well to be consistent.
            if (HasHeader(/*bFirstPage=*/true))
                m_aFollowPageStyle->setPropertyValue("HeaderIsOn", uno::makeAny(true));
            if (HasFooter(/*bFirstPage=*/true))
                m_aFollowPageStyle->setPropertyValue("FooterIsOn", uno::makeAny(true));
        }

        sal_Int32 nTopMargin = m_nTopMargin;
        sal_Int32 nHeaderTop = m_nHeaderTop;
        if ( HasHeader( bFirstPage ) )
        {
            nTopMargin = nHeaderTop;
            if ( m_nTopMargin > 0 && m_nTopMargin > nHeaderTop )
                nHeaderTop = m_nTopMargin - nHeaderTop;
            else
                nHeaderTop = 0;

            // minimum header height 1mm
            if ( nHeaderTop < MIN_HEAD_FOOT_HEIGHT )
                nHeaderTop = MIN_HEAD_FOOT_HEIGHT;
        }

        if ( m_nTopMargin >= 0 ) //fixed height header -> see WW8Par6.hxx
        {
            Insert( PROP_HEADER_IS_DYNAMIC_HEIGHT, uno::makeAny( true ) );
            Insert( PROP_HEADER_DYNAMIC_SPACING, uno::makeAny( true ) );
            Insert( PROP_HEADER_BODY_DISTANCE, uno::makeAny( nHeaderTop - MIN_HEAD_FOOT_HEIGHT ) );// ULSpace.Top()
            Insert( PROP_HEADER_HEIGHT, uno::makeAny( nHeaderTop ) );

            if (bCopyFirstToFollow && HasHeader(/*bFirstPage=*/true))
            {
                m_aFollowPageStyle->setPropertyValue("HeaderDynamicSpacing",
                                                     getProperty(PROP_HEADER_DYNAMIC_SPACING)->second);
                m_aFollowPageStyle->setPropertyValue("HeaderHeight",
                                                     getProperty(PROP_HEADER_HEIGHT)->second);
            }
        }
        else
        {
            //todo: old filter fakes a frame into the header/footer to support overlapping
            //current setting is completely wrong!
            Insert( PROP_HEADER_HEIGHT, uno::makeAny( nHeaderTop ) );
            Insert( PROP_HEADER_BODY_DISTANCE, uno::makeAny( m_nTopMargin - nHeaderTop ) );
            Insert( PROP_HEADER_IS_DYNAMIC_HEIGHT, uno::makeAny( false ) );
            Insert( PROP_HEADER_DYNAMIC_SPACING, uno::makeAny( false ) );
        }

        sal_Int32 nBottomMargin = m_nBottomMargin;
        sal_Int32 nHeaderBottom = m_nHeaderBottom;
        if ( HasFooter( bFirstPage ) )
        {
            nBottomMargin = nHeaderBottom;
            if ( m_nBottomMargin > 0 && m_nBottomMargin > nHeaderBottom )
                nHeaderBottom = m_nBottomMargin - nHeaderBottom;
            else
                nHeaderBottom = 0;
            if ( nHeaderBottom < MIN_HEAD_FOOT_HEIGHT )
                nHeaderBottom = MIN_HEAD_FOOT_HEIGHT;
        }

        if ( m_nBottomMargin >= 0 ) //fixed height footer -> see WW8Par6.hxx
        {
            Insert( PROP_FOOTER_IS_DYNAMIC_HEIGHT, uno::makeAny( true ) );
            Insert( PROP_FOOTER_DYNAMIC_SPACING, uno::makeAny( true ) );
            Insert( PROP_FOOTER_BODY_DISTANCE, uno::makeAny( nHeaderBottom - MIN_HEAD_FOOT_HEIGHT ) );
            Insert( PROP_FOOTER_HEIGHT, uno::makeAny( nHeaderBottom ) );

            if (bCopyFirstToFollow && HasFooter(/*bFirstPage=*/true))
            {
                m_aFollowPageStyle->setPropertyValue("FooterDynamicSpacing",
                                                     getProperty(PROP_FOOTER_DYNAMIC_SPACING)->second);
                m_aFollowPageStyle->setPropertyValue("FooterHeight",
                                                     getProperty(PROP_FOOTER_HEIGHT)->second);
            }
        }
        else
        {
            //todo: old filter fakes a frame into the header/footer to support overlapping
            //current setting is completely wrong!
            Insert( PROP_FOOTER_IS_DYNAMIC_HEIGHT, uno::makeAny( false ) );
            Insert( PROP_FOOTER_DYNAMIC_SPACING, uno::makeAny( false ) );
            Insert( PROP_FOOTER_HEIGHT, uno::makeAny( m_nBottomMargin - nHeaderBottom ) );
            Insert( PROP_FOOTER_BODY_DISTANCE, uno::makeAny( nHeaderBottom ) );
        }

        //now set the top/bottom margin for the follow page style
        Insert( PROP_TOP_MARGIN, uno::makeAny( std::max<sal_Int32>(nTopMargin, 0) ) );
        Insert( PROP_BOTTOM_MARGIN, uno::makeAny( std::max<sal_Int32>(nBottomMargin, 0) ) );

        if (bCopyFirstToFollow)
        {
            if (HasHeader(/*bFirstPage=*/true))
                m_aFollowPageStyle->setPropertyValue("TopMargin", getProperty(PROP_TOP_MARGIN)->second);
            if (HasFooter(/*bFirstPage=*/true))
                m_aFollowPageStyle->setPropertyValue("BottomMargin",
                                                     getProperty(PROP_BOTTOM_MARGIN)->second);
        }
    }

    uno::Reference< beans::XPropertySet > lcl_GetRangeProperties( bool bIsFirstSection,
                                                                  DomainMapper_Impl& rDM_Impl,
                                                                  const uno::Reference< text::XTextRange >& xStartingRange )
    {
        uno::Reference< beans::XPropertySet > xRangeProperties;
        if ( bIsFirstSection && rDM_Impl.GetBodyText().is() )
        {
            uno::Reference< container::XEnumerationAccess > xEnumAccess( rDM_Impl.GetBodyText(), uno::UNO_QUERY_THROW );
            uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
            xRangeProperties.set( xEnum->nextElement(), uno::UNO_QUERY_THROW );
            if ( rDM_Impl.GetIsDummyParaAddedForTableInSection() && xEnum->hasMoreElements() )
                xRangeProperties.set( xEnum->nextElement(), uno::UNO_QUERY_THROW );
        }
        else if ( xStartingRange.is() )
            xRangeProperties.set( xStartingRange, uno::UNO_QUERY_THROW );
        return xRangeProperties;
    }

    void SectionPropertyMap::HandleMarginsHeaderFooter( bool bFirstPage, DomainMapper_Impl& rDM_Impl )
    {
        Insert( PROP_LEFT_MARGIN, uno::makeAny( m_nLeftMargin ) );
        Insert( PROP_RIGHT_MARGIN, uno::makeAny( m_nRightMargin ) );

        if ( rDM_Impl.m_oBackgroundColor )
            Insert( PROP_BACK_COLOR, uno::makeAny( *rDM_Impl.m_oBackgroundColor ) );

        // Check for missing footnote separator only in case there is at least
        // one footnote.
        if (rDM_Impl.m_bHasFtn && !rDM_Impl.m_bHasFtnSep)
        {
            // Set footnote line width to zero, document has no footnote separator.
            Insert(PROP_FOOTNOTE_LINE_RELATIVE_WIDTH, uno::makeAny(sal_Int32(0)));
        }
        if ( rDM_Impl.m_bHasFtnSep )
        {
            //If default paragraph style is RTL, footnote separator should be right aligned
            //and for RTL locales, LTR default paragraph style should present a left aligned footnote separator
            try
            {
                uno::Reference<style::XStyleFamiliesSupplier> xStylesSupplier(rDM_Impl.GetTextDocument(), uno::UNO_QUERY);
                if ( xStylesSupplier.is() )
                {
                    uno::Reference<container::XNameAccess> xStyleFamilies = xStylesSupplier->getStyleFamilies();
                    uno::Reference<container::XNameAccess> xParagraphStyles;
                    if ( xStyleFamilies.is() )
                        xStyleFamilies->getByName("ParagraphStyles") >>= xParagraphStyles;
                    uno::Reference<beans::XPropertySet> xStandard;
                    if ( xParagraphStyles.is() )
                        xParagraphStyles->getByName("Standard") >>= xStandard;
                    if ( xStandard.is() )
                    {
                        sal_Int16 aWritingMode(0);
                        xStandard->getPropertyValue( getPropertyName(PROP_WRITING_MODE) ) >>= aWritingMode;
                        if( aWritingMode == text::WritingMode2::RL_TB )
                            Insert( PROP_FOOTNOTE_LINE_ADJUST, uno::makeAny( sal_Int16(text::HorizontalAdjust_RIGHT) ), false );
                        else
                            Insert( PROP_FOOTNOTE_LINE_ADJUST, uno::makeAny( sal_Int16(text::HorizontalAdjust_LEFT) ), false );
                    }
                }
            }
            catch ( const uno::Exception& ) {}
        }

        /*** if headers/footers are available then the top/bottom margins of the
        header/footer are copied to the top/bottom margin of the page
        */
        CopyLastHeaderFooter( bFirstPage, rDM_Impl );
        PrepareHeaderFooterProperties( bFirstPage );
    }

    bool SectionPropertyMap::FloatingTableConversion( DomainMapper_Impl& rDM_Impl, FloatingTableInfo& rInfo )
    {
        // This is OOXML version of the code deciding if the table needs to be
        // in a floating frame.
        // For ww8 code, see SwWW8ImplReader::FloatingTableConversion in
        // sw/source/filter/ww8/ww8par.cxx
        // The two should do the same, so if you make changes here, please check
        // that the other is in sync.

        // Note that this is just a list of heuristics till sw core can have a
        // table that is floating and can span over multiple pages at the same
        // time.

        // If there is an explicit section break right after a table, then there
        // will be no wrapping anyway.
        if (rDM_Impl.m_bConvertedTable && !rDM_Impl.GetIsLastSectionGroup() && rInfo.m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_nextPage)
            return false;

        sal_Int32 nPageWidth = GetPageWidth();
        sal_Int32 nTextAreaWidth = nPageWidth - GetLeftMargin() - GetRightMargin();
        // Count the layout width of the table.
        sal_Int32 nTableWidth = rInfo.m_nTableWidth;
        if (rInfo.m_nTableWidthType == text::SizeType::VARIABLE)
        {
            nTableWidth *= nTextAreaWidth / 100.0;
        }
        sal_Int32 nLeftMargin = 0;
        if ( rInfo.getPropertyValue( "LeftMargin" ) >>= nLeftMargin )
            nTableWidth += nLeftMargin;
        sal_Int32 nRightMargin = 0;
        if ( rInfo.getPropertyValue( "RightMargin" ) >>= nRightMargin )
            nTableWidth += nRightMargin;

        sal_Int16 nHoriOrientRelation = rInfo.getPropertyValue( "HoriOrientRelation" ).get<sal_Int16>();
        sal_Int16 nVertOrientRelation = rInfo.getPropertyValue( "VertOrientRelation" ).get<sal_Int16>();
        if ( nHoriOrientRelation == text::RelOrientation::PAGE_FRAME && nVertOrientRelation == text::RelOrientation::PAGE_FRAME )
        {
            sal_Int16 nHoriOrient = rInfo.getPropertyValue( "HoriOrient" ).get<sal_Int16>();
            sal_Int16 nVertOrient = rInfo.getPropertyValue( "VertOrient" ).get<sal_Int16>();
            if ( nHoriOrient == text::HoriOrientation::NONE && nVertOrient == text::VertOrientation::NONE )
            {
                // Anchor position is relative to the page horizontally and vertically as well and is an absolute position.
                // The more close we are to the left edge, the less likely there will be any wrapping.
                // The more close we are to the bottom, the more likely the table will span over to the next page
                // So if we're in the bottom left quarter, don't do any conversion.
                sal_Int32 nHoriOrientPosition = rInfo.getPropertyValue( "HoriOrientPosition" ).get<sal_Int32>();
                sal_Int32 nVertOrientPosition = rInfo.getPropertyValue( "VertOrientPosition" ).get<sal_Int32>();
                sal_Int32 nPageHeight = getProperty( PROP_HEIGHT )->second.get<sal_Int32>();
                if ( nHoriOrientPosition < (nPageWidth / 2) && nVertOrientPosition >( nPageHeight / 2 ) )
                    return false;
            }
        }

        // It seems Word has a limit here, so that in case the table width is quite
        // close to the text area width, then it won't perform a wrapping, even in
        // case the content (e.g. an empty paragraph) would fit. The magic constant
        // here represents this limit.
        const sal_Int32 nMagicNumber = 469;

        // If the table's with is smaller than the text area width, text might
        // be next to the table and so it should behave as a floating table.
        if ( (nTableWidth + nMagicNumber) < nTextAreaWidth )
            return true;

        // If the position is relative to the edge of the page, then we need to check the whole
        // page width to see whether text can fit next to the table.
        if ( nHoriOrientRelation == text::RelOrientation::PAGE_FRAME )
        {
            // If the table is wide enough so that no text fits next to it, then don't create a fly
            // for the table: no wrapping will be performed anyway, but multi-page
            // tables will be broken.
            if ((nTableWidth + nMagicNumber) < (nPageWidth - std::min(GetLeftMargin(), GetRightMargin())))
                return true;
        }

        // If there are columns, always create the fly, otherwise the columns would
        // restrict geometry of the table.
        if ( ColumnCount() + 1 >= 2 )
            return true;

        return false;
    }

    void SectionPropertyMap::InheritOrFinalizePageStyles( DomainMapper_Impl& rDM_Impl )
    {
        const uno::Reference< container::XNameContainer >& xPageStyles = rDM_Impl.GetPageStyles();
        const uno::Reference < lang::XMultiServiceFactory >& xTextFactory = rDM_Impl.GetTextFactory();

        // if no new styles have been created for this section, inherit from the previous section,
        // otherwise apply this section's settings to the new style.
        // Ensure that FollowPage is inherited first - otherwise GetPageStyle may auto-create a follow when checking FirstPage.
        SectionPropertyMap* pLastContext = rDM_Impl.GetLastSectionContext();
        if ( pLastContext && m_sFollowPageStyleName.isEmpty() )
            m_sFollowPageStyleName = pLastContext->GetPageStyleName();
        else
        {
            HandleMarginsHeaderFooter( /*bFirst=*/false, rDM_Impl );
            GetPageStyle( xPageStyles, xTextFactory, /*bFirst=*/false );
            if ( rDM_Impl.IsNewDoc() && m_aFollowPageStyle.is() )
                ApplyProperties_( m_aFollowPageStyle );
        }

        // FirstPageStyle may only be inherited if it will not be used or re-linked to a different follow
        if ( !m_bTitlePage && pLastContext && m_sFirstPageStyleName.isEmpty() )
            m_sFirstPageStyleName = pLastContext->GetPageStyleName( /*bFirst=*/true );
        else
        {
            HandleMarginsHeaderFooter( /*bFirst=*/true, rDM_Impl );
            GetPageStyle( xPageStyles, xTextFactory, /*bFirst=*/true );
            if ( rDM_Impl.IsNewDoc() && m_aFirstPageStyle.is() )
                ApplyProperties_( m_aFirstPageStyle );

            // Chain m_aFollowPageStyle to be after m_aFirstPageStyle
            m_aFirstPageStyle->setPropertyValue( "FollowStyle", uno::makeAny( m_sFollowPageStyleName ) );
        }
    }

    void SectionPropertyMap::HandleIncreasedAnchoredObjectSpacing(DomainMapper_Impl& rDM_Impl)
    {
        // Ignore Word 2010 and older.
        if (rDM_Impl.GetSettingsTable()->GetWordCompatibilityMode() < 15)
            return;

        sal_Int32 nPageWidth = GetPageWidth();
        sal_Int32 nTextAreaWidth = nPageWidth - GetLeftMargin() - GetRightMargin();

        std::vector<AnchoredObjectInfo>& rAnchoredObjectAnchors = rDM_Impl.m_aAnchoredObjectAnchors;
        for (auto& rAnchor : rAnchoredObjectAnchors)
        {
            // Ignore this paragraph when there is a single shape only.
            if (rAnchor.m_aAnchoredObjects.size() < 2)
                continue;

            // Analyze the anchored objects of this paragraph, now that we know the
            // page width.
            sal_Int32 nShapesWidth = 0;
            for (const auto& rAnchored : rAnchor.m_aAnchoredObjects)
            {
                uno::Reference<drawing::XShape> xShape(rAnchored, uno::UNO_QUERY);
                if (!xShape.is())
                    continue;

                uno::Reference<beans::XPropertySet> xPropertySet(rAnchored, uno::UNO_QUERY);
                if (!xPropertySet.is())
                    continue;

                // Ignore objects with no wrapping.
                text::WrapTextMode eWrap = text::WrapTextMode_THROUGH;
                xPropertySet->getPropertyValue("Surround") >>= eWrap;
                if (eWrap == text::WrapTextMode_THROUGH)
                    continue;

                sal_Int32 nLeftMargin = 0;
                xPropertySet->getPropertyValue("LeftMargin") >>= nLeftMargin;
                sal_Int32 nRightMargin = 0;
                xPropertySet->getPropertyValue("RightMargin") >>= nRightMargin;
                nShapesWidth += xShape->getSize().Width + nLeftMargin + nRightMargin;
            }

            // Ignore cases when we have enough horizontal space for the shapes.
            if (nTextAreaWidth > nShapesWidth)
                continue;

            sal_Int32 nHeight = 0;
            for (const auto& rAnchored : rAnchor.m_aAnchoredObjects)
            {
                uno::Reference<drawing::XShape> xShape(rAnchored, uno::UNO_QUERY);
                if (!xShape.is())
                    continue;

                nHeight += xShape->getSize().Height;
            }

            uno::Reference<beans::XPropertySet> xParagraph(rAnchor.m_xParagraph, uno::UNO_QUERY);
            if (xParagraph.is())
            {
                sal_Int32 nTopMargin = 0;
                xParagraph->getPropertyValue("ParaTopMargin") >>= nTopMargin;
                // Increase top spacing of the paragraph to match Word layout
                // behavior.
                nTopMargin = std::max(nTopMargin, nHeight);
                xParagraph->setPropertyValue("ParaTopMargin", uno::makeAny(nTopMargin));
            }
        }
        rAnchoredObjectAnchors.clear();
    }

    void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl )
    {
        // The default section type is nextPage.
        if ( m_nBreakType == -1 )
            m_nBreakType = NS_ooxml::LN_Value_ST_SectionMark_nextPage;

        // Text area width is known at the end of a section: decide if tables should be converted or not.
        std::vector<FloatingTableInfo>& rPendingFloatingTables = rDM_Impl.m_aPendingFloatingTables;
        uno::Reference<text::XTextAppendAndConvert> xBodyText( rDM_Impl.GetBodyText(), uno::UNO_QUERY );
        for ( FloatingTableInfo & rInfo : rPendingFloatingTables )
        {
            rInfo.m_nBreakType = m_nBreakType;
            if ( FloatingTableConversion( rDM_Impl, rInfo ) )
                xBodyText->convertToTextFrame( rInfo.m_xStart, rInfo.m_xEnd, rInfo.m_aFrameProperties );
        }
        rPendingFloatingTables.clear();

        try
        {
            HandleIncreasedAnchoredObjectSpacing(rDM_Impl);
        }
        catch (const uno::Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("writerfilter", "HandleIncreasedAnchoredObjectSpacing() failed");
        }

        if ( m_nLnnMod )
        {
            bool bFirst = rDM_Impl.IsLineNumberingSet();
            rDM_Impl.SetLineNumbering( m_nLnnMod, m_nLnc, m_ndxaLnn );
            if ( m_nLnnMin > 0 || (bFirst && m_nLnc == NS_ooxml::LN_Value_ST_LineNumberRestart_newSection) )
            {
                //set the starting value at the beginning of the section
                try
                {
                    uno::Reference< beans::XPropertySet > xRangeProperties;
                    if ( m_xStartingRange.is() )
                    {
                        xRangeProperties.set( m_xStartingRange, uno::UNO_QUERY_THROW );
                    }
                    else
                    {
                        //set the start value at the beginning of the document
                        xRangeProperties.set( rDM_Impl.GetTextDocument()->getText()->getStart(), uno::UNO_QUERY_THROW );
                    }
                    xRangeProperties->setPropertyValue( getPropertyName( PROP_PARA_LINE_NUMBER_START_VALUE ), uno::makeAny( m_nLnnMin ) );
                }
                catch ( const uno::Exception& )
                {
                    OSL_FAIL( "Exception in SectionPropertyMap::CloseSectionGroup" );
                }
            }
        }

        // depending on the break type no page styles should be created
        // Continuous sections usually create only a section, and not a new page style
        const bool bTreatAsContinuous = m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_nextPage
            && m_nColumnCount > 0
            && (m_bIsFirstSection || (!HasHeader( m_bTitlePage ) && !HasFooter( m_bTitlePage )) )
            && (m_bIsFirstSection || m_sFollowPageStyleName.isEmpty() || (m_sFirstPageStyleName.isEmpty() && m_bTitlePage));
        if ( m_nBreakType == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_continuous) || bTreatAsContinuous )
        {
            //todo: insert a section or access the already inserted section
            uno::Reference< beans::XPropertySet > xSection =
                rDM_Impl.appendTextSectionAfter( m_xStartingRange );
            if ( m_nColumnCount > 0 && xSection.is() )
                ApplyColumnProperties( xSection, rDM_Impl );

            try
            {
                InheritOrFinalizePageStyles( rDM_Impl );
                OUString aName = m_bTitlePage ? m_sFirstPageStyleName : m_sFollowPageStyleName;
                uno::Reference< beans::XPropertySet > xRangeProperties( lcl_GetRangeProperties( m_bIsFirstSection, rDM_Impl, m_xStartingRange ) );
                if ( m_bIsFirstSection && !aName.isEmpty() && xRangeProperties.is() )
                    xRangeProperties->setPropertyValue( getPropertyName( PROP_PAGE_DESC_NAME ), uno::makeAny( aName ) );
            }
            catch ( const uno::Exception& )
            {
                SAL_WARN( "writerfilter", "failed to set PageDescName!" );
            }
        }
        // If the section is of type "New column" (0x01), then simply insert a column break.
        // But only if there actually are columns on the page, otherwise a column break
        // seems to be handled like a page break by MSO.
        else if ( m_nBreakType == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_nextColumn) && m_nColumnCount > 0 )
        {
            try
            {
                InheritOrFinalizePageStyles( rDM_Impl );
                uno::Reference< beans::XPropertySet > xRangeProperties;
                if ( m_xStartingRange.is() )
                {
                    xRangeProperties.set( m_xStartingRange, uno::UNO_QUERY_THROW );
                }
                else
                {
                    //set the start value at the beginning of the document
                    xRangeProperties.set( rDM_Impl.GetTextDocument()->getText()->getStart(), uno::UNO_QUERY_THROW );
                }
                xRangeProperties->setPropertyValue( getPropertyName( PROP_BREAK_TYPE ), uno::makeAny( style::BreakType_COLUMN_BEFORE ) );
            }
            catch ( const uno::Exception& ) {}
        }
        else
        {
            //get the properties and create appropriate page styles
            uno::Reference< beans::XPropertySet > xFollowPageStyle = GetPageStyle( rDM_Impl.GetPageStyles(), rDM_Impl.GetTextFactory(), false );

            HandleMarginsHeaderFooter(/*bFirstPage=*/false, rDM_Impl );

            const OUString sTrayIndex = getPropertyName( PROP_PRINTER_PAPER_TRAY_INDEX );
            if ( m_nPaperBin >= 0 )
                xFollowPageStyle->setPropertyValue( sTrayIndex, uno::makeAny( m_nPaperBin ) );
            if ( rDM_Impl.GetSettingsTable()->GetMirrorMarginSettings() )
            {
                Insert( PROP_PAGE_STYLE_LAYOUT, uno::makeAny( style::PageStyleLayout_MIRRORED ) );
            }
            uno::Reference< text::XTextColumns > xColumns;
            if ( m_nColumnCount > 0 )
                xColumns = ApplyColumnProperties( xFollowPageStyle, rDM_Impl );

            // these BreakTypes are effectively page-breaks: don't evenly distribute text in columns before a page break;
            SectionPropertyMap* pLastContext = rDM_Impl.GetLastSectionContext();
            if ( pLastContext && pLastContext->ColumnCount() )
                pLastContext->DontBalanceTextColumns();

            //prepare text grid properties
            sal_Int32 nHeight = 1;
            boost::optional< PropertyMap::Property > pProp = getProperty( PROP_HEIGHT );
            if ( pProp )
                pProp->second >>= nHeight;

            sal_Int32 nWidth = 1;
            pProp = getProperty( PROP_WIDTH );
            if ( pProp )
                pProp->second >>= nWidth;

            text::WritingMode eWritingMode = text::WritingMode_LR_TB;
            pProp = getProperty( PROP_WRITING_MODE );
            if ( pProp )
                pProp->second >>= eWritingMode;

            sal_Int32 nTextAreaHeight = eWritingMode == text::WritingMode_LR_TB ?
                nHeight - m_nTopMargin - m_nBottomMargin :
                nWidth - m_nLeftMargin - m_nRightMargin;

            sal_Int32 nGridLinePitch = m_nGridLinePitch;
            //sep.dyaLinePitch
            if ( nGridLinePitch < 1 || nGridLinePitch > 31680 )
            {
                SAL_WARN( "writerfilter", "sep.dyaLinePitch outside legal range: " << nGridLinePitch );
                nGridLinePitch = 1;
            }

            const sal_Int16 nGridLines = nTextAreaHeight / nGridLinePitch;
            if ( nGridLines >= 0 )
                Insert( PROP_GRID_LINES, uno::makeAny( nGridLines ) );

            // PROP_GRID_MODE
            Insert( PROP_GRID_MODE, uno::makeAny( static_cast<sal_Int16> (m_nGridType) ) );
            if ( m_nGridType == text::TextGridMode::LINES_AND_CHARS )
            {
                Insert( PROP_GRID_SNAP_TO_CHARS, uno::makeAny( m_bGridSnapToChars ) );
            }

            sal_Int32 nCharWidth = 423; //240 twip/ 12 pt
            const StyleSheetEntryPtr pEntry = rDM_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( "Standard" );
            if ( pEntry.get() )
            {
                boost::optional< PropertyMap::Property > pPropHeight = pEntry->pProperties->getProperty( PROP_CHAR_HEIGHT_ASIAN );
                if ( pPropHeight )
                {
                    double fHeight = 0;
                    if ( pPropHeight->second >>= fHeight )
                        nCharWidth = ConversionHelper::convertTwipToMM100( static_cast<long>(fHeight * 20.0 + 0.5) );
                }
            }

            //dxtCharSpace
            if ( m_nDxtCharSpace )
            {
                sal_Int32 nCharSpace = m_nDxtCharSpace;
                //main lives in top 20 bits, and is signed.
                sal_Int32 nMain = (nCharSpace & 0xFFFFF000);
                nMain /= 0x1000;
                nCharWidth += ConversionHelper::convertTwipToMM100( nMain * 20 );

                sal_Int32 nFraction = (nCharSpace & 0x00000FFF);
                nFraction = (nFraction * 20) / 0xFFF;
                nCharWidth += ConversionHelper::convertTwipToMM100( nFraction );
            }

            if ( m_nPageNumberType >= 0 )
                Insert( PROP_NUMBERING_TYPE, uno::makeAny( m_nPageNumberType ) );

            // #i119558#, force to set document as standard page mode,
            // refer to ww8 import process function "SwWW8ImplReader::SetDocumentGrid"
            try
            {
                uno::Reference< beans::XPropertySet > xDocProperties;
                xDocProperties.set( rDM_Impl.GetTextDocument(), uno::UNO_QUERY_THROW );
                bool bSquaredPageMode = false;
                Insert( PROP_GRID_STANDARD_MODE, uno::makeAny( !bSquaredPageMode ) );
                xDocProperties->setPropertyValue( "DefaultPageMode", uno::makeAny( bSquaredPageMode ) );
            }
            catch ( const uno::Exception& )
            {
                OSL_ENSURE( false, "Exception in SectionPropertyMap::CloseSectionGroup" );
            }

            Insert( PROP_GRID_BASE_HEIGHT, uno::makeAny( nGridLinePitch ) );
            Insert( PROP_GRID_BASE_WIDTH, uno::makeAny( nCharWidth ) );
            Insert( PROP_GRID_RUBY_HEIGHT, uno::makeAny( sal_Int32( 0 ) ) );

            if ( rDM_Impl.IsNewDoc() )
                ApplyProperties_( xFollowPageStyle );

            //todo: creating a "First Page" style depends on HasTitlePage and _fFacingPage_
            if ( m_bTitlePage )
            {
                CopyLastHeaderFooter( true, rDM_Impl );
                PrepareHeaderFooterProperties( true );
                uno::Reference< beans::XPropertySet > xFirstPageStyle = GetPageStyle(
                    rDM_Impl.GetPageStyles(), rDM_Impl.GetTextFactory(), true );
                if ( rDM_Impl.IsNewDoc() )
                    ApplyProperties_( xFirstPageStyle );

                sal_Int32 nPaperBin = m_nFirstPaperBin >= 0 ? m_nFirstPaperBin : m_nPaperBin >= 0 ? m_nPaperBin : 0;
                if ( nPaperBin )
                    xFirstPageStyle->setPropertyValue( sTrayIndex, uno::makeAny( nPaperBin ) );
                if ( xColumns.is() )
                    xFirstPageStyle->setPropertyValue(
                        getPropertyName( PROP_TEXT_COLUMNS ), uno::makeAny( xColumns ) );
            }

            ApplyBorderToPageStyles( rDM_Impl.GetPageStyles(), rDM_Impl.GetTextFactory(), m_eBorderApply, m_eBorderOffsetFrom );

            try
            {
                //now apply this break at the first paragraph of this section
                uno::Reference< beans::XPropertySet > xRangeProperties( lcl_GetRangeProperties( m_bIsFirstSection, rDM_Impl, m_xStartingRange ) );

                // Handle page breaks with odd/even page numbering. We need to use an extra page style for setting the page style
                // to left/right, because if we set it to the normal style, we'd set it to "First Page"/"Default Style", which would
                // break them (all default pages would be only left or right).
                if ( m_nBreakType == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_evenPage) || m_nBreakType == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_oddPage) )
                {
                    OUString* pageStyle = m_bTitlePage ? &m_sFirstPageStyleName : &m_sFollowPageStyleName;
                    OUString evenOddStyleName = lcl_FindUnusedPageStyleName( rDM_Impl.GetPageStyles()->getElementNames() );
                    uno::Reference< beans::XPropertySet > evenOddStyle(
                        rDM_Impl.GetTextFactory()->createInstance( "com.sun.star.style.PageStyle" ),
                        uno::UNO_QUERY );
                    // Unfortunately using setParent() does not work for page styles, so make a deep copy of the page style.
                    uno::Reference< beans::XPropertySet > pageProperties( m_bTitlePage ? m_aFirstPageStyle : m_aFollowPageStyle );
                    uno::Reference< beans::XPropertySetInfo > pagePropertiesInfo( pageProperties->getPropertySetInfo() );
                    uno::Sequence< beans::Property > propertyList( pagePropertiesInfo->getProperties() );
                    // Ignore write-only properties.
                    static const std::set<OUString> aBlacklist
                        = { "FooterBackGraphicURL", "BackGraphicURL", "HeaderBackGraphicURL" };
                    for ( int i = 0; i < propertyList.getLength(); ++i )
                    {
                        if ( (propertyList[i].Attributes & beans::PropertyAttribute::READONLY) == 0 )
                        {
                            if (aBlacklist.find(propertyList[i].Name) == aBlacklist.end())
                                evenOddStyle->setPropertyValue(
                                    propertyList[i].Name,
                                    pageProperties->getPropertyValue(propertyList[i].Name));
                        }
                    }
                    evenOddStyle->setPropertyValue( "FollowStyle", uno::makeAny( *pageStyle ) );
                    rDM_Impl.GetPageStyles()->insertByName( evenOddStyleName, uno::makeAny( evenOddStyle ) );
                    evenOddStyle->setPropertyValue( "HeaderIsOn", uno::makeAny( false ) );
                    evenOddStyle->setPropertyValue( "FooterIsOn", uno::makeAny( false ) );
                    CopyHeaderFooter( pageProperties, evenOddStyle );
                    *pageStyle = evenOddStyleName; // And use it instead of the original one (which is set as follow of this one).
                    if ( m_nBreakType == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_evenPage) )
                        evenOddStyle->setPropertyValue( getPropertyName( PROP_PAGE_STYLE_LAYOUT ), uno::makeAny( style::PageStyleLayout_LEFT ) );
                    else if ( m_nBreakType == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_oddPage) )
                        evenOddStyle->setPropertyValue( getPropertyName( PROP_PAGE_STYLE_LAYOUT ), uno::makeAny( style::PageStyleLayout_RIGHT ) );
                }

                if ( xRangeProperties.is() && rDM_Impl.IsNewDoc() )
                {
                    xRangeProperties->setPropertyValue(
                        getPropertyName( PROP_PAGE_DESC_NAME ),
                        uno::makeAny( m_bTitlePage ? m_sFirstPageStyleName
                            : m_sFollowPageStyleName ) );

                    if (0 <= m_nPageNumber)
                    {
                        sal_Int16 nPageNumber = m_nPageNumber >= 0 ? static_cast< sal_Int16 >(m_nPageNumber) : 1;
                        xRangeProperties->setPropertyValue(getPropertyName(PROP_PAGE_NUMBER_OFFSET),
                            uno::makeAny(nPageNumber));
                    }
                }
            }
            catch ( const uno::Exception& )
            {
                OSL_FAIL( "Exception in SectionPropertyMap::CloseSectionGroup" );
            }
        }

        // Now that the margins are known, resize relative width shapes because some shapes in LO do not support percentage-sizes
        sal_Int32 nParagraphWidth = GetPageWidth() - m_nLeftMargin - m_nRightMargin;
        if ( m_nColumnCount > 0 )
        {
            // skip custom-width columns since we don't know what column the shape is in.
            if ( m_aColWidth.size() )
                nParagraphWidth = 0;
            else
                nParagraphWidth = (nParagraphWidth - (m_nColumnDistance * m_nColumnCount)) / (m_nColumnCount + 1);
        }
        if ( nParagraphWidth > 0 )
        {
            const OUString sPropRelativeWidth = getPropertyName(PROP_RELATIVE_WIDTH);
            for ( const auto& xShape : m_xRelativeWidthShapes )
            {
                const uno::Reference<beans::XPropertySet> xShapePropertySet( xShape, uno::UNO_QUERY );
                if ( xShapePropertySet->getPropertySetInfo()->hasPropertyByName(sPropRelativeWidth) )
                {
                    sal_uInt16 nPercent = 0;
                    xShapePropertySet->getPropertyValue( sPropRelativeWidth ) >>= nPercent;
                    if ( nPercent )
                    {
                        const sal_Int32 nWidth = nParagraphWidth * nPercent / 100;
                        xShape->setSize( awt::Size( nWidth, xShape->getSize().Height ) );
                    }
                }
            }
        }
        m_xRelativeWidthShapes.clear();

        rDM_Impl.SetIsLastSectionGroup( false );
        rDM_Impl.SetIsFirstParagraphInSection( true );

        if ( !rDM_Impl.IsInFootOrEndnote() )
        {
            rDM_Impl.m_bHasFtn = false;
            rDM_Impl.m_bHasFtnSep = false;
        }
    }

    // Clear the flag that says we should take the header/footer content from
    // the previous section.  This should be called when we encounter a header
    // or footer definition for this section.
    void SectionPropertyMap::ClearHeaderFooterLinkToPrevious( bool bHeader, PageType eType )
    {
        if ( bHeader )
        {
            switch ( eType )
            {
                case PAGE_FIRST: m_bFirstPageHeaderLinkToPrevious = false; break;
                case PAGE_LEFT:  m_bEvenPageHeaderLinkToPrevious = false; break;
                case PAGE_RIGHT: m_bDefaultHeaderLinkToPrevious = false; break;
                    // no default case as all enumeration values have been covered
            }
        }
        else
        {
            switch ( eType )
            {
                case PAGE_FIRST: m_bFirstPageFooterLinkToPrevious = false; break;
                case PAGE_LEFT:  m_bEvenPageFooterLinkToPrevious = false; break;
                case PAGE_RIGHT: m_bDefaultFooterLinkToPrevious = false; break;
            }
        }
    }

    class NamedPropertyValue
    {
    private:
        OUString m_aName;

    public:
        explicit NamedPropertyValue( const OUString& i_aStr )
            : m_aName( i_aStr )
        {
        }

        bool operator() ( beans::PropertyValue const & aVal )
        {
            return aVal.Name == m_aName;
        }
    };

    void SectionPropertyMap::ApplyProperties_( const uno::Reference< beans::XPropertySet >& xStyle )
    {
        uno::Reference< beans::XMultiPropertySet > const xMultiSet( xStyle, uno::UNO_QUERY );

        std::vector< OUString > vNames;
        std::vector< uno::Any > vValues;
        {
            // Convert GetPropertyValues() value into something useful
            uno::Sequence< beans::PropertyValue > vPropVals = GetPropertyValues();

            //Temporarily store the items that are in grab bags
            uno::Sequence< beans::PropertyValue > vCharVals;
            uno::Sequence< beans::PropertyValue > vParaVals;
            beans::PropertyValue* pCharGrabBag = std::find_if( vPropVals.begin(), vPropVals.end(), NamedPropertyValue( "CharInteropGrabBag" ) );
            if ( pCharGrabBag != vPropVals.end() )
                (pCharGrabBag->Value) >>= vCharVals;
            beans::PropertyValue* pParaGrabBag = std::find_if( vPropVals.begin(), vPropVals.end(), NamedPropertyValue( "ParaInteropGrabBag" ) );
            if ( pParaGrabBag != vPropVals.end() )
                (pParaGrabBag->Value) >>= vParaVals;

            for ( beans::PropertyValue* pIter = vPropVals.begin(); pIter != vPropVals.end(); ++pIter )
            {
                if ( pIter != pCharGrabBag && pIter != pParaGrabBag )
                {
                    vNames.push_back( pIter->Name );
                    vValues.push_back( pIter->Value );
                }
            }
            for ( beans::PropertyValue & v : vCharVals )
            {
                vNames.push_back( v.Name );
                vValues.push_back( v.Value );
            }
            for ( beans::PropertyValue & v : vParaVals )
            {
                vNames.push_back( v.Name );
                vValues.push_back( v.Value );
            }
        }
        if ( xMultiSet.is() )
        {
            try
            {
                xMultiSet->setPropertyValues( comphelper::containerToSequence( vNames ), comphelper::containerToSequence( vValues ) );
                return;
            }
            catch ( const uno::Exception& )
            {
                OSL_FAIL( "Exception in SectionPropertyMap::ApplyProperties_" );
            }
        }
        for ( size_t i = 0; i < vNames.size(); ++i )
        {
            try
            {
                if ( xStyle.is() )
                    xStyle->setPropertyValue( vNames[i], vValues[i] );
            }
            catch ( const uno::Exception& )
            {
                OSL_FAIL( "Exception in SectionPropertyMap::ApplyProperties_" );
            }
        }
    }

    sal_Int32 SectionPropertyMap::GetPageWidth()
    {
        return getProperty( PROP_WIDTH )->second.get<sal_Int32>();
    }

    StyleSheetPropertyMap::StyleSheetPropertyMap()
        : mnListId( -1 )
        , mnListLevel( -1 )
        , mnOutlineLevel( -1 )
        , mnNumId( -1 )
    {
    }

    ParagraphProperties::ParagraphProperties()
        : m_bFrameMode( false )
        , m_nDropCap( NS_ooxml::LN_Value_doc_ST_DropCap_none )
        , m_nLines( 0 )
        , m_w( -1 )
        , m_h( -1 )
        , m_nWrap( text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE )
        , m_hAnchor( -1 )
        , m_vAnchor( -1 )
        , m_x( -1 )
        , m_bxValid( false )
        , m_y( -1 )
        , m_byValid( false )
        , m_hSpace( -1 )
        , m_vSpace( -1 )
        , m_hRule( -1 )
        , m_xAlign( -1 )
        , m_yAlign( -1 )
        , m_bAnchorLock( false )
        , m_nDropCapLength( 0 )
    {
    }

    bool ParagraphProperties::operator==( const ParagraphProperties& rCompare )
    {
        return ( m_bFrameMode  == rCompare.m_bFrameMode &&
                 m_nDropCap    == rCompare.m_nDropCap &&
                 m_nLines      == rCompare.m_nLines &&
                 m_w           == rCompare.m_w &&
                 m_h           == rCompare.m_h &&
                 m_nWrap       == rCompare.m_nWrap &&
                 m_hAnchor     == rCompare.m_hAnchor &&
                 m_vAnchor     == rCompare.m_vAnchor &&
                 m_x           == rCompare.m_x &&
                 m_bxValid     == rCompare.m_bxValid &&
                 m_y           == rCompare.m_y &&
                 m_byValid     == rCompare.m_byValid &&
                 m_hSpace      == rCompare.m_hSpace &&
                 m_vSpace      == rCompare.m_vSpace &&
                 m_hRule       == rCompare.m_hRule &&
                 m_xAlign      == rCompare.m_xAlign &&
                 m_yAlign      == rCompare.m_yAlign &&
                 m_bAnchorLock == rCompare.m_bAnchorLock );
    }

    void ParagraphProperties::ResetFrameProperties()
    {
        m_bFrameMode     = false;
        m_nDropCap       = NS_ooxml::LN_Value_doc_ST_DropCap_none;
        m_nLines         = 0;
        m_w              = -1;
        m_h              = -1;
        m_nWrap          = text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE;
        m_hAnchor        = -1;
        m_vAnchor        = -1;
        m_x              = -1;
        m_bxValid        = false;
        m_y              = -1;
        m_byValid        = false;
        m_hSpace         = -1;
        m_vSpace         = -1;
        m_hRule          = -1;
        m_xAlign         = -1;
        m_yAlign         = -1;
        m_bAnchorLock    = false;
        m_nDropCapLength = 0;
    }

    bool TablePropertyMap::getValue( TablePropertyMapTarget eWhich, sal_Int32& nFill )
    {
        if ( eWhich < TablePropertyMapTarget_MAX )
        {
            if ( m_aValidValues[eWhich].bValid )
                nFill = m_aValidValues[eWhich].nValue;
            return m_aValidValues[eWhich].bValid;
        }
        else
        {
            OSL_FAIL( "invalid TablePropertyMapTarget" );
            return false;
        }
    }

    void TablePropertyMap::setValue( TablePropertyMapTarget eWhich, sal_Int32 nSet )
    {
        if ( eWhich < TablePropertyMapTarget_MAX )
        {
            m_aValidValues[eWhich].bValid = true;
            m_aValidValues[eWhich].nValue = nSet;
        }
        else
            OSL_FAIL( "invalid TablePropertyMapTarget" );
    }

    void TablePropertyMap::insertTableProperties( const PropertyMap* pMap, const bool bOverwrite )
    {
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement( "TablePropertyMap.insertTableProperties" );
        pMap->dumpXml();
    #endif

        const TablePropertyMap* pSource = dynamic_cast< const TablePropertyMap* >(pMap);
        if ( pSource )
        {
            for ( sal_Int32 eTarget = TablePropertyMapTarget_START;
                eTarget < TablePropertyMapTarget_MAX; ++eTarget )
            {
                if ( pSource->m_aValidValues[eTarget].bValid && (bOverwrite || !m_aValidValues[eTarget].bValid) )
                {
                    m_aValidValues[eTarget].bValid = true;
                    m_aValidValues[eTarget].nValue = pSource->m_aValidValues[eTarget].nValue;
                }
            }
        }

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

    } // namespace dmapper
    } // namespace writerfilter

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

↑ V547 Expression 'eWritingMode == text::WritingMode_LR_TB' is always true.

↑ V547 Expression '!bSquaredPageMode' is always true.

↑ V547 Expression 'm_nPageNumber >= 0' is always true.