/* -*- 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 "xmlexpit.hxx"

#include <osl/diagnose.h>
#include <rtl/ustrbuf.hxx>
#include <sax/tools/converter.hxx>
#include <svl/poolitem.hxx>
#include <svl/itemset.hxx>
#include <utility>
#include <xmloff/xmluconv.hxx>
#include <xmloff/namespacemap.hxx>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/prhdlfac.hxx>
#include <xmloff/xmltypes.hxx>
#include <editeng/xmlcnitm.hxx>
#include <xmloff/xmlexp.hxx>
#include <xmloff/xmlprhdl.hxx>
#include <editeng/memberids.h>
#include <hintids.hxx>
#include <unomid.h>
#include <svx/unomid.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/shaditem.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/formatbreakitem.hxx>
#include <editeng/keepitem.hxx>
#include <editeng/brushitem.hxx>
#include <editeng/frmdiritem.hxx>
#include <editeng/prntitem.hxx>
#include <fmtpdsc.hxx>
#include <fmtornt.hxx>
#include <fmtfsize.hxx>

#include "xmlithlp.hxx"

#include <fmtrowsplt.hxx>

using ::editeng::SvxBorderLine;
using namespace ::com::sun::star;
using namespace ::xmloff::token;
using uno::Any;

// fills the given attribute list with the items in the given set
void SvXMLExportItemMapper::exportXML( const SvXMLExport& rExport,
                                comphelper::AttributeList& rAttrList,
                                const SfxItemSet& rSet,
                                const SvXMLUnitConverter& rUnitConverter,
                                const SvXMLNamespaceMap& rNamespaceMap,
                                std::vector<sal_uInt16> *pIndexArray ) const
{
    const sal_uInt16 nCount = mrMapEntries->getCount();
    sal_uInt16 nIndex = 0;

    while( nIndex < nCount )
    {
        SvXMLItemMapEntry const & rEntry = mrMapEntries->getByIndex( nIndex );

        // we have a valid map entry here, so let's use it...
        if( 0 == (rEntry.nMemberId & MID_SW_FLAG_NO_ITEM_EXPORT) )
        {
            const SfxPoolItem* pItem = GetItem( rSet, rEntry.nWhichId );
            // do we have an item?
            if(pItem)
            {
                if( 0 != (rEntry.nMemberId & MID_SW_FLAG_ELEMENT_ITEM_EXPORT) )
                {
                    // element items do not add any properties,
                    // we export it later
                    if( pIndexArray )
                        pIndexArray->push_back( nIndex );

                }
                else
                {
                    exportXML( rExport, rAttrList, *pItem, rEntry, rUnitConverter,
                                  rNamespaceMap, &rSet );
                }
            }
        }
        else
        {
            OSL_FAIL( "no item not handled in xml export" );
        }
        nIndex++;
    }
}

void SvXMLExportItemMapper::exportXML(const SvXMLExport&,
                                 comphelper::AttributeList& rAttrList,
                                 const SfxPoolItem& rItem,
                                 const SvXMLItemMapEntry& rEntry,
                                 const SvXMLUnitConverter& rUnitConverter,
                                 const SvXMLNamespaceMap& rNamespaceMap,
                                 const SfxItemSet *pSet ) const
{
    if( 0 != (rEntry.nMemberId & MID_SW_FLAG_SPECIAL_ITEM_EXPORT) )
    {
        if( dynamic_cast<const SwFormatRowSplit*>( &rItem) !=  nullptr )
        {
            OUString aValue;
            bool bAddAttribute = true;
            if( rEntry.nNameSpace == XML_NAMESPACE_STYLE )
            {
                bAddAttribute = false;
            }
            else
            {
                OUStringBuffer aOut;
                const SfxBoolItem* pSplit = dynamic_cast<const SfxBoolItem*>( &rItem );
                assert(pSplit && "Wrong Which-ID");
                const sal_uInt16 eEnum = (pSplit && pSplit->GetValue()) ? 1 : 0;
                SvXMLUnitConverter::convertEnum( aOut, eEnum, aXML_KeepTogetherType );
                aValue = aOut.makeStringAndClear();
            }
            if( bAddAttribute )
            {
                const OUString sName( rNamespaceMap.GetQNameByKey( rEntry.nNameSpace,
                                GetXMLToken(rEntry.eLocalName) ) );
                rAttrList.AddAttribute( sName, aValue );
            }
        }

        if (const SvXMLAttrContainerItem *pUnknown = dynamic_cast<const SvXMLAttrContainerItem*>(&rItem))
        {
            std::unique_ptr<SvXMLNamespaceMap> pNewNamespaceMap;
            const SvXMLNamespaceMap *pNamespaceMap = &rNamespaceMap;

            const sal_uInt16 nCount = pUnknown->GetAttrCount();
            for( sal_uInt16 i=0; i < nCount; i++ )
            {
                const OUString sPrefix( pUnknown->GetAttrPrefix( i ) );
                if( !sPrefix.isEmpty() )
                {
                    const OUString sNamespace( pUnknown->GetAttrNamespace( i ) );

                    // if the prefix isn't defined yet or has another meaning,
                    // we have to redefine it now.
                    const sal_uInt16 nIdx = pNamespaceMap->GetIndexByPrefix( sPrefix );
                    if( USHRT_MAX == nIdx ||
                        pNamespaceMap->GetNameByIndex( nIdx ) != sNamespace )
                    {
                        if( !pNewNamespaceMap )
                        {
                            pNewNamespaceMap.reset(
                                        new SvXMLNamespaceMap( rNamespaceMap ));
                            pNamespaceMap = pNewNamespaceMap.get();
                        }
                        pNewNamespaceMap->Add( sPrefix, sNamespace );

                        rAttrList.AddAttribute( GetXMLToken(XML_XMLNS) + ":" + sPrefix,
                                                sNamespace );
                    }

                    rAttrList.AddAttribute( sPrefix + ":" + pUnknown->GetAttrLName(i),
                                            pUnknown->GetAttrValue(i) );
                }
                else
                {
                    rAttrList.AddAttribute( pUnknown->GetAttrLName(i),
                                            pUnknown->GetAttrValue(i) );
                }
            }
        }
        else
        {
            handleSpecialItem( rAttrList, rEntry, rItem, rUnitConverter,
                                  rNamespaceMap, pSet );
        }
    }
    else if( 0 == (rEntry.nMemberId & MID_SW_FLAG_ELEMENT_ITEM_EXPORT) )
    {
        bool bDone = false;
        switch (rItem.Which())
        {
            case RES_FRAMEDIR:
            {
                // Write bt-lr and tb-rl90 to the extension namespace, handle other values
                // below.
                auto pDirection = static_cast<const SvxFrameDirectionItem*>(&rItem);
                if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT
                    && pDirection->GetValue() == SvxFrameDirection::Vertical_LR_BT)
                {
                    const OUString sName(rNamespaceMap.GetQNameByKey(
                        XML_NAMESPACE_LO_EXT, GetXMLToken(XML_WRITING_MODE)));
                    rAttrList.AddAttribute(sName, GetXMLToken(XML_BT_LR));
                }
                if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT
                    || pDirection->GetValue() == SvxFrameDirection::Vertical_LR_BT)
                    bDone = true;

                if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT
                    && pDirection->GetValue() == SvxFrameDirection::Vertical_RL_TB90)
                {
                    const OUString sName(rNamespaceMap.GetQNameByKey(
                        XML_NAMESPACE_LO_EXT, GetXMLToken(XML_WRITING_MODE)));
                    rAttrList.AddAttribute(sName, GetXMLToken(XML_TB_RL90));
                }
                if (rEntry.nNameSpace == XML_NAMESPACE_LO_EXT
                    || pDirection->GetValue() == SvxFrameDirection::Vertical_RL_TB90)
                    bDone = true;
                break;
            }
        }

        if (!bDone)
        {
            OUString aValue;
            if( QueryXMLValue(rItem, aValue,
                              static_cast< sal_uInt16 >(
                                              rEntry.nMemberId & MID_SW_FLAG_MASK ),
                                 rUnitConverter ) )
            {
                const OUString sName(
                    rNamespaceMap.GetQNameByKey( rEntry.nNameSpace,
                                                 GetXMLToken(rEntry.eLocalName)));
                rAttrList.AddAttribute( sName, aValue );
            }
        }
    }
}

void SvXMLExportItemMapper::exportElementItems(
                          SvXMLExport& rExport,
                          const SfxItemSet &rSet,
                          const std::vector<sal_uInt16> &rIndexArray ) const
{
    const size_t nCount = rIndexArray.size();

    bool bItemsExported = false;
    for( size_t nIndex = 0; nIndex < nCount; ++nIndex )
    {
        const sal_uInt16 nElement = rIndexArray[ nIndex ];
        SvXMLItemMapEntry const & rEntry = mrMapEntries->getByIndex( nElement );
        OSL_ENSURE( 0 != (rEntry.nMemberId & MID_SW_FLAG_ELEMENT_ITEM_EXPORT),
                    "wrong mid flag!" );

        const SfxPoolItem* pItem = GetItem( rSet, rEntry.nWhichId );
        // do we have an item?
        if(pItem)
        {
            rExport.IgnorableWhitespace();
            handleElementItem( rEntry, *pItem );
            bItemsExported = true;
        }
    }

    if( bItemsExported )
        rExport.IgnorableWhitespace();
}

/** returns the item with the given WhichId from the given ItemSet if it's
    set
*/
const SfxPoolItem* SvXMLExportItemMapper::GetItem( const SfxItemSet& rSet,
                                                   sal_uInt16 nWhichId)
{
    // first get item from itemset
    const SfxPoolItem* pItem;
    SfxItemState eState = rSet.GetItemState( nWhichId, false, &pItem );

    if( SfxItemState::SET == eState )
    {
        return pItem;
    }
    else
    {
        return nullptr;
    }
}

SvXMLExportItemMapper::SvXMLExportItemMapper( SvXMLItemMapEntriesRef rMapEntries )
   : mrMapEntries(std::move(rMapEntries))
{
}

SvXMLExportItemMapper::~SvXMLExportItemMapper()
{
}

void SvXMLExportItemMapper::exportXML( SvXMLExport& rExport,
                    const SfxItemSet& rSet,
                    const SvXMLUnitConverter& rUnitConverter,
                    XMLTokenEnum ePropToken ) const
{
    std::vector<sal_uInt16> aIndexArray;

    exportXML( rExport, rExport.GetAttrList(), rSet, rUnitConverter,
               rExport.GetNamespaceMap(), &aIndexArray );

    if( rExport.GetAttrList().getLength() > 0 || !aIndexArray.empty() )
    {
        rExport.IgnorableWhitespace();

        SvXMLElementExport aElem( rExport, XML_NAMESPACE_STYLE, ePropToken,
                                  false, false );
        exportElementItems( rExport, rSet, aIndexArray );
    }
}

/** this method is called for every item that has the
    MID_SW_FLAG_SPECIAL_ITEM_EXPORT flag set */
void SvXMLExportItemMapper::handleSpecialItem( comphelper::AttributeList& /*rAttrList*/,
                                    const SvXMLItemMapEntry& /*rEntry*/,
                                    const SfxPoolItem& /*rItem*/,
                                    const SvXMLUnitConverter& /*rUnitConverter*/,
                                    const SvXMLNamespaceMap& /*rNamespaceMap*/,
                                    const SfxItemSet* /*pSet*/ /* = NULL */ ) const
{
    OSL_FAIL( "special item not handled in xml export" );
}

/** this method is called for every item that has the
    MID_SW_FLAG_ELEMENT_EXPORT flag set */
void SvXMLExportItemMapper::handleElementItem(
                        const SvXMLItemMapEntry& /*rEntry*/,
                        const SfxPoolItem& /*rItem*/ ) const
{
    OSL_FAIL( "element item not handled in xml export" );
}

static bool lcl_isOdfDoubleLine( const SvxBorderLine* pLine )
{
    bool bIsOdfDouble = false;
    switch (pLine->GetBorderLineStyle())
    {
        case SvxBorderLineStyle::DOUBLE:
        case SvxBorderLineStyle::THINTHICK_SMALLGAP:
        case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
        case SvxBorderLineStyle::THINTHICK_LARGEGAP:
        case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
        case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
        case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
            bIsOdfDouble = true;
            break;
        default:
            break;
    }
    return bIsOdfDouble;
}

bool SvXMLExportItemMapper::QueryXMLValue(
    const SfxPoolItem& rItem,
    OUString& rValue,
    sal_uInt16 nMemberId,
    const SvXMLUnitConverter& rUnitConverter )
{
    bool bOk = false;
    OUStringBuffer aOut;

    switch ( rItem.Which() )
    {
        case RES_MARGIN_FIRSTLINE:
        case RES_MARGIN_TEXTLEFT:
        case RES_MARGIN_RIGHT:
            assert(false); // is only called for frame formats?
            break;

        case RES_LR_SPACE:
        {
            const SvxLRSpaceItem& rLRSpace = dynamic_cast<const SvxLRSpaceItem&>(rItem);

            bOk = true;
            switch( nMemberId )
            {
                case  MID_L_MARGIN:
                    if (rLRSpace.GetPropLeft() != 100)
                    {
                        ::sax::Converter::convertPercent(
                                aOut, rLRSpace.GetPropLeft() );
                    }
                    else if (rLRSpace.GetLeft().m_nUnit != css::util::MeasureUnit::TWIP)
                    {
                        ::sax::Converter::convertMeasureUnit(aOut, rLRSpace.GetLeft().m_dValue,
                                                             rLRSpace.GetLeft().m_nUnit);
                    }
                    else
                    {
                        rUnitConverter.convertMeasureToXML(aOut, rLRSpace.GetLeft().m_dValue);
                    }
                    break;

                case  MID_R_MARGIN:
                    if (rLRSpace.GetPropRight() != 100)
                    {
                        ::sax::Converter::convertPercent(
                                aOut, rLRSpace.GetPropRight() );
                    }
                    else if (rLRSpace.GetRight().m_nUnit != css::util::MeasureUnit::TWIP)
                    {
                        ::sax::Converter::convertMeasureUnit(aOut, rLRSpace.GetRight().m_dValue,
                                                             rLRSpace.GetRight().m_nUnit);
                    }
                    else
                    {
                        rUnitConverter.convertMeasureToXML(aOut, rLRSpace.GetRight().m_dValue);
                    }
                    break;

                case MID_FIRST_AUTO:
                    if (rLRSpace.IsAutoFirst())
                    {
                        ::sax::Converter::convertBool(
                                aOut, rLRSpace.IsAutoFirst() );
                    }
                    else
                        bOk = false;
                    break;

                case  MID_FIRST_LINE_INDENT:
                    if (!rLRSpace.IsAutoFirst())
                    {
                        if (rLRSpace.GetPropTextFirstLineOffset() != 100)
                        {
                            ::sax::Converter::convertPercent(
                                aOut, rLRSpace.GetPropTextFirstLineOffset() );
                        }
                        else if (rLRSpace.GetTextFirstLineOffset().m_nUnit
                                 != css::util::MeasureUnit::TWIP)
                        {
                            ::sax::Converter::convertMeasureUnit(
                                aOut, rLRSpace.GetTextFirstLineOffset().m_dValue,
                                rLRSpace.GetTextFirstLineOffset().m_nUnit);
                        }
                        else
                        {
                            rUnitConverter.convertMeasureToXML(
                                aOut, rLRSpace.GetTextFirstLineOffset().m_dValue);
                        }
                    }
                    else
                        bOk = false;
                    break;

                default:
                    OSL_FAIL( "unknown member id!");
                    bOk = false;
                    break;
            }
        }
        break;

        case RES_UL_SPACE:
        {
            const SvxULSpaceItem& rULSpace = dynamic_cast<const SvxULSpaceItem&>(rItem);

            switch( nMemberId )
            {
                case MID_UP_MARGIN:
                    if (rULSpace.GetPropUpper() != 100)
                    {
                        ::sax::Converter::convertPercent(
                                aOut, rULSpace.GetPropUpper() );
                    }
                    else
                    {
                        rUnitConverter.convertMeasureToXML(
                                aOut, rULSpace.GetUpper() );
                    }
                    break;

                case MID_LO_MARGIN:
                    if (rULSpace.GetPropLower() != 100)
                    {
                        ::sax::Converter::convertPercent(
                                aOut, rULSpace.GetPropLower() );
                    }
                    else
                    {
                        rUnitConverter.convertMeasureToXML(
                                aOut, rULSpace.GetLower() );
                    }
                    break;

                default:
                    OSL_FAIL("unknown MemberId");
            };

            bOk = true;
        }
        break;

        case RES_SHADOW:
        {
            const SvxShadowItem* pShadow = dynamic_cast<const SvxShadowItem*>( &rItem );
            assert(pShadow && "Wrong Which-ID");
            if (pShadow)
            {
                sal_Int32 nX = 1, nY = 1;
                switch( pShadow->GetLocation() )
                {
                    case SvxShadowLocation::TopLeft:
                        nX = -1;
                        nY = -1;
                        break;
                    case SvxShadowLocation::TopRight:
                        nY = -1;
                        break;
                    case SvxShadowLocation::BottomLeft:
                        nX = -1;
                        break;
                    case SvxShadowLocation::BottomRight:
                        break;
                    case SvxShadowLocation::NONE:
                    default:
                        rValue = GetXMLToken(XML_NONE);
                        return true;
                }

                nX *= pShadow->GetWidth();
                nY *= pShadow->GetWidth();

                ::sax::Converter::convertColor(aOut, pShadow->GetColor());
                aOut.append( ' ' );
                rUnitConverter.convertMeasureToXML( aOut, nX );
                aOut.append( ' ' );
                rUnitConverter.convertMeasureToXML( aOut, nY );

                bOk = true;
            }
        }
        break;

        case RES_BOX:
        {
            const SvxBoxItem* pBox = dynamic_cast<const SvxBoxItem*>( &rItem );
            assert(pBox && "Wrong Which-ID");
            if (pBox)
            {
                /**
                   xml -> MemberId

                   border-padding           ALL_BORDER_PADDING
                   border-padding-before    LEFT_BORDER_PADDING
                   border-padding-after RIGHT_BORDER_PADDING
                   border-padding-start TOP_BORDER_PADDING
                   border-padding-end       BOTTOM_BORDER_PADDING

                   border                   ALL_BORDER
                   border-before            LEFT_BORDER
                   border-after         RIGHT_BORDER
                   border-start         TOP_BORDER
                   border-end               BOTTOM_BORDER

                   border-line-width            ALL_BORDER_LINE_WIDTH
                   border-line-width-before LEFT_BORDER_LINE_WIDTH
                   border-line-width-after      RIGHT_BORDER_LINE_WIDTH
                   border-line-width-start      TOP_BORDER_LINE_WIDTH
                   border-line-width-end        BOTTOM_BORDER_LINE_WIDTH
                */

                const SvxBorderLine* pLeft    = pBox->GetLeft();
                const SvxBorderLine* pRight   = pBox->GetRight();
                const SvxBorderLine* pTop     = pBox->GetTop();
                const SvxBorderLine* pBottom  = pBox->GetBottom();
                const sal_uInt16 nTopDist     = pBox->GetDistance( SvxBoxItemLine::TOP );
                const sal_uInt16 nBottomDist  = pBox->GetDistance( SvxBoxItemLine::BOTTOM );
                const sal_uInt16 nLeftDist    = pBox->GetDistance( SvxBoxItemLine::LEFT );
                const sal_uInt16 nRightDist   = pBox->GetDistance( SvxBoxItemLine::RIGHT );

                // check if we need to export it
                switch( nMemberId )
                {
                    case ALL_BORDER_PADDING:
                    case LEFT_BORDER_PADDING:
                    case RIGHT_BORDER_PADDING:
                    case TOP_BORDER_PADDING:
                    case BOTTOM_BORDER_PADDING:
                    {
                        bool bEqual = nLeftDist == nRightDist &&
                                          nLeftDist == nTopDist &&
                                          nLeftDist == nBottomDist;
                        // don't export individual paddings if all paddings are equal and
                        // don't export all padding if some paddings are not equal
                        if( (bEqual && ALL_BORDER_PADDING != nMemberId) ||
                            (!bEqual && ALL_BORDER_PADDING == nMemberId) )
                            return false;
                    }
                    break;
                    case ALL_BORDER:
                    case LEFT_BORDER:
                    case RIGHT_BORDER:
                    case TOP_BORDER:
                    case BOTTOM_BORDER:
                    {
                        bool bEqual = ( nullptr == pTop && nullptr == pBottom &&
                                            nullptr == pLeft && nullptr == pRight ) ||
                                          ( pTop && pBottom && pLeft && pRight &&
                                           *pTop == *pBottom  && *pTop == *pLeft &&
                                            *pTop == *pRight );

                        // don't export individual borders if all are the same and
                        // don't export all borders if some are not equal
                        if( (bEqual && ALL_BORDER != nMemberId) ||
                            (!bEqual && ALL_BORDER == nMemberId) )
                            return false;
                    }
                    break;
                    case ALL_BORDER_LINE_WIDTH:
                    case LEFT_BORDER_LINE_WIDTH:
                    case RIGHT_BORDER_LINE_WIDTH:
                    case TOP_BORDER_LINE_WIDTH:
                    case BOTTOM_BORDER_LINE_WIDTH:
                    {
                        // if no line is set, there is nothing to export
                        if( !pTop && !pBottom && !pLeft && !pRight )
                            return false;

                        bool bEqual = nullptr != pTop &&
                                          nullptr != pBottom &&
                                          nullptr != pLeft &&
                                          nullptr != pRight;

                        if( bEqual )
                        {
                            const sal_uInt16 nDistance = pTop->GetDistance();
                            const sal_uInt16 nInWidth  = pTop->GetInWidth();
                            const sal_uInt16 nOutWidth = pTop->GetOutWidth();
                            const tools::Long nWidth = pTop->GetWidth();

                            bEqual = nDistance == pLeft->GetDistance() &&
                                     nInWidth  == pLeft->GetInWidth()  &&
                                     nOutWidth == pLeft->GetOutWidth() &&
                                     nWidth == pLeft->GetWidth() &&
                                     nDistance == pRight->GetDistance()  &&
                                     nInWidth  == pRight->GetInWidth()   &&
                                     nOutWidth == pRight->GetOutWidth()  &&
                                     nWidth == pRight->GetWidth()  &&
                                     nDistance == pBottom->GetDistance()  &&
                                     nInWidth  == pBottom->GetInWidth()   &&
                                     nOutWidth == pBottom->GetOutWidth() &&
                                     nWidth == pBottom->GetWidth();
                        }

                        switch( nMemberId )
                        {
                            case ALL_BORDER_LINE_WIDTH:
                                if( !bEqual || pTop->GetDistance() == 0 ||
                                    !lcl_isOdfDoubleLine( pTop ) )
                                    return false;
                                break;
                            case LEFT_BORDER_LINE_WIDTH:
                                if( bEqual || nullptr == pLeft ||
                                    0 == pLeft->GetDistance() ||
                                    !lcl_isOdfDoubleLine( pLeft ) )
                                    return false;
                                break;
                            case RIGHT_BORDER_LINE_WIDTH:
                                if( bEqual || nullptr == pRight ||
                                    0 == pRight->GetDistance() ||
                                    !lcl_isOdfDoubleLine( pRight ) )
                                    return false;
                                break;
                            case TOP_BORDER_LINE_WIDTH:
                                if( bEqual || nullptr == pTop ||
                                    0 == pTop->GetDistance() ||
                                    !lcl_isOdfDoubleLine( pTop ) )
                                    return false;
                                break;
                            case BOTTOM_BORDER_LINE_WIDTH:
                                if( bEqual || nullptr == pBottom ||
                                    0 == pBottom->GetDistance() ||
                                    !lcl_isOdfDoubleLine( pBottom ) )
                                    return false;
                                break;
                        }
                    }
                    break;
                }

                // now export it export
                switch( nMemberId )
                {
                    // padding
                    case ALL_BORDER_PADDING:
                    case LEFT_BORDER_PADDING:
                        rUnitConverter.convertMeasureToXML( aOut, nLeftDist );
                        break;
                    case RIGHT_BORDER_PADDING:
                        rUnitConverter.convertMeasureToXML( aOut, nRightDist );
                        break;
                    case TOP_BORDER_PADDING:
                        rUnitConverter.convertMeasureToXML( aOut, nTopDist );
                        break;
                    case BOTTOM_BORDER_PADDING:
                        rUnitConverter.convertMeasureToXML( aOut, nBottomDist );
                        break;

                        // border
                    case ALL_BORDER:
                    case LEFT_BORDER:
                    case RIGHT_BORDER:
                    case TOP_BORDER:
                    case BOTTOM_BORDER:
                    {
                        const SvxBorderLine* pLine;
                        switch( nMemberId )
                        {
                        case ALL_BORDER:
                        case LEFT_BORDER:
                            pLine = pLeft;
                            break;
                        case RIGHT_BORDER:
                            pLine = pRight;
                            break;
                        case TOP_BORDER:
                            pLine = pTop;
                            break;
                        case BOTTOM_BORDER:
                            pLine = pBottom;
                            break;
                        default:
                            pLine = nullptr;
                            break;
                        }

                        if( nullptr != pLine )
                        {
                            sal_Int32 nWidth = pLine->GetWidth();

                            enum XMLTokenEnum eStyle = XML_SOLID;
                            bool bNoBorder = false;
                            switch (pLine->GetBorderLineStyle())
                            {
                                case SvxBorderLineStyle::SOLID:
                                    eStyle = XML_SOLID;
                                    break;
                                case SvxBorderLineStyle::DOTTED:
                                    eStyle = XML_DOTTED;
                                    break;
                                case SvxBorderLineStyle::DASHED:
                                    eStyle = XML_DASHED;
                                    break;
                                case SvxBorderLineStyle::FINE_DASHED:
                                    eStyle = XML_FINE_DASHED;
                                    break;
                                case SvxBorderLineStyle::DASH_DOT:
                                    eStyle = XML_DASH_DOT;
                                    break;
                                case SvxBorderLineStyle::DASH_DOT_DOT:
                                    eStyle = XML_DASH_DOT_DOT;
                                    break;
                                case SvxBorderLineStyle::DOUBLE_THIN:
                                    eStyle = XML_DOUBLE_THIN;
                                    break;
                                case SvxBorderLineStyle::DOUBLE:
                                case SvxBorderLineStyle::THINTHICK_SMALLGAP:
                                case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
                                case SvxBorderLineStyle::THINTHICK_LARGEGAP:
                                case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
                                case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
                                case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
                                    eStyle = XML_DOUBLE;
                                    break;
                                case SvxBorderLineStyle::EMBOSSED:
                                    eStyle = XML_RIDGE;
                                    break;
                                case SvxBorderLineStyle::ENGRAVED:
                                    eStyle = XML_GROOVE;
                                    break;
                                case SvxBorderLineStyle::INSET:
                                    eStyle = XML_INSET;
                                    break;
                                case SvxBorderLineStyle::OUTSET:
                                    eStyle = XML_OUTSET;
                                    break;
                                default:
                                    bNoBorder = true;
                            }

                            if ( !bNoBorder )
                            {
                                ::sax::Converter::convertMeasure(aOut, nWidth,
                                       util::MeasureUnit::TWIP,
                                       util::MeasureUnit::POINT);
                                aOut.append( " "
                                    + GetXMLToken( eStyle )
                                    + " " );
                                ::sax::Converter::convertColor(aOut,
                                        pLine->GetColor());
                            }
                        }
                        else
                        {
                            aOut.append( GetXMLToken(XML_NONE) );
                        }
                    }
                    break;

                    // width
                    case ALL_BORDER_LINE_WIDTH:
                    case LEFT_BORDER_LINE_WIDTH:
                    case RIGHT_BORDER_LINE_WIDTH:
                    case TOP_BORDER_LINE_WIDTH:
                    case BOTTOM_BORDER_LINE_WIDTH:
                        const SvxBorderLine* pLine;
                        switch( nMemberId )
                        {
                        case ALL_BORDER_LINE_WIDTH:
                        case LEFT_BORDER_LINE_WIDTH:
                            pLine = pLeft;
                            break;
                        case RIGHT_BORDER_LINE_WIDTH:
                            pLine = pRight;
                            break;
                        case TOP_BORDER_LINE_WIDTH:
                            pLine = pTop;
                            break;
                        case BOTTOM_BORDER_LINE_WIDTH:
                            pLine = pBottom;
                            break;
                        default:
                            return false;
                        }
                        rUnitConverter.convertMeasureToXML( aOut, pLine->GetInWidth() );
                        aOut.append( ' ' );
                        rUnitConverter.convertMeasureToXML( aOut, pLine->GetDistance() );
                        aOut.append( ' ' );
                        rUnitConverter.convertMeasureToXML( aOut, pLine->GetOutWidth() );
                        break;
                }
                bOk = true;
            }
        }
        break;

        case RES_BREAK:
        {
            const SvxFormatBreakItem& rFormatBreak = dynamic_cast<const SvxFormatBreakItem&>(rItem);

            sal_uInt16 eEnum = 0;

            switch( nMemberId )
            {
            case MID_BREAK_BEFORE:
                switch (rFormatBreak.GetBreak())
                {
                    case SvxBreak::ColumnBefore:
                        eEnum = 1;
                        break;
                    case SvxBreak::PageBefore:
                        eEnum = 2;
                        break;
                    case SvxBreak::NONE:
                        eEnum = 0;
                        break;
                    default:
                        return false;
                }
                break;
            case MID_BREAK_AFTER:
                switch (rFormatBreak.GetBreak())
                {
                    case SvxBreak::ColumnAfter:
                        eEnum = 1;
                        break;
                    case SvxBreak::PageAfter:
                        eEnum = 2;
                        break;
                    case SvxBreak::NONE:
                        eEnum = 0;
                        break;
                    default:
                        return false;
                }
                break;
            }

            bOk = SvXMLUnitConverter::convertEnum( aOut, eEnum, psXML_BreakType );
        }
        break;

        case RES_KEEP:
        {
            const SvxFormatKeepItem* pFormatKeep = dynamic_cast<const SvxFormatKeepItem*>( &rItem );
            assert(pFormatKeep && "Wrong Which-ID");
            if (pFormatKeep)
            {
                aOut.append( pFormatKeep->GetValue()
                             ? GetXMLToken( XML_ALWAYS )
                             : GetXMLToken( XML_AUTO ) );
                bOk = true;
            }
        }
        break;

        case RES_PRINT:
        {
            const SvxPrintItem* pHasTextChangesOnly = dynamic_cast<const SvxPrintItem*>( &rItem );
            if (pHasTextChangesOnly && !pHasTextChangesOnly->GetValue())
            {
                aOut.append( "false" );
                bOk = true;
            }
        }
        break;

        case RES_BACKGROUND:
        {
            const SvxBrushItem& rBrush = dynamic_cast<const SvxBrushItem&>(rItem);

            // note: the graphic is only exported if nMemberId equals
            //       MID_GRAPHIC...
            //       If not, only the color or transparency is exported

            switch( nMemberId )
            {
                case MID_BACK_COLOR:
                    if ( rBrush.GetColor().IsTransparent() )
                        aOut.append( GetXMLToken(XML_TRANSPARENT) );
                    else
                    {
                        ::sax::Converter::convertColor(aOut,
                                rBrush.GetColor());
                    }
                    bOk = true;
                    break;

                case MID_GRAPHIC_POSITION:
                    switch (rBrush.GetGraphicPos())
                    {
                    case GPOS_LT:
                    case GPOS_MT:
                    case GPOS_RT:
                        aOut.append( GetXMLToken(XML_TOP) );
                        bOk = true;
                        break;
                    case GPOS_LM:
                    case GPOS_MM:
                    case GPOS_RM:
                        aOut.append( GetXMLToken(XML_CENTER) );
                        bOk = true;
                        break;
                    case GPOS_LB:
                    case GPOS_MB:
                    case GPOS_RB:
                        aOut.append( GetXMLToken(XML_BOTTOM) );
                        bOk = true;
                        break;
                    default:
                        ;
                    }

                    if( bOk )
                    {
                        aOut.append( ' ' );

                        switch (rBrush.GetGraphicPos())
                        {
                        case GPOS_LT:
                        case GPOS_LB:
                        case GPOS_LM:
                            aOut.append( GetXMLToken(XML_LEFT) );
                            break;
                        case GPOS_MT:
                        case GPOS_MM:
                        case GPOS_MB:
                            aOut.append( GetXMLToken(XML_CENTER) );
                            break;
                        case GPOS_RM:
                        case GPOS_RT:
                        case GPOS_RB:
                            aOut.append( GetXMLToken(XML_RIGHT) );
                            break;
                        default:
                            ;
                        }
                    }
                    break;

                case MID_GRAPHIC_REPEAT:
                {
                    SvxGraphicPosition eGraphicPos = rBrush.GetGraphicPos();
                    if( GPOS_AREA == eGraphicPos )
                    {
                        aOut.append( GetXMLToken(XML_STRETCH)  );
                        bOk = true;
                    }
                    else if( GPOS_NONE != eGraphicPos && GPOS_TILED != eGraphicPos  )
                    {
                        aOut.append( GetXMLToken(XML_BACKGROUND_NO_REPEAT) );
                        bOk = true;
                    }
                }
                break;

                case MID_GRAPHIC_FILTER:
                    if (rBrush.GetGraphicPos() != GPOS_NONE &&
                        !rBrush.GetGraphicFilter().isEmpty())
                    {
                        aOut.append(rBrush.GetGraphicFilter());
                        bOk = true;
                    }
                    break;
            }
        }
        break;

        case RES_PAGEDESC:
        {
            const SwFormatPageDesc& rPageDesc = dynamic_cast<const SwFormatPageDesc&>(rItem);

            if( MID_PAGEDESC_PAGENUMOFFSET==nMemberId )
            {
                ::std::optional<sal_uInt16> oNumOffset = rPageDesc.GetNumOffset();
                if (oNumOffset && *oNumOffset > 0)
                {
                    // #i114163# positiveInteger only!
                    sal_Int32 const number(*oNumOffset);
                    aOut.append(number);
                }
                else
                {
                    aOut.append(GetXMLToken(XML_AUTO));
                }
                bOk = true;
            }
        }
        break;

        case RES_LAYOUT_SPLIT:
        case RES_ROW_SPLIT:
        {
            const SfxBoolItem* pSplit = dynamic_cast<const SfxBoolItem*>( &rItem );
            assert(pSplit && "Wrong Which-ID");
            if (pSplit)
            {
                ::sax::Converter::convertBool( aOut, pSplit->GetValue() );
                bOk = true;
            }
        }
        break;

        case RES_HORI_ORIENT:
        {
            const SwFormatHoriOrient* pHoriOrient = dynamic_cast<const SwFormatHoriOrient*>( &rItem );
            assert(pHoriOrient && "Wrong Which-ID");
            if (pHoriOrient)
            {
                SvXMLUnitConverter::convertEnum( aOut, pHoriOrient->GetHoriOrient(),
                                            aXMLTableAlignMap );
                bOk = true;
            }
        }
        break;

        case RES_VERT_ORIENT:
        {
            const SwFormatVertOrient* pVertOrient = dynamic_cast<const SwFormatVertOrient*>( &rItem );
            assert(pVertOrient && "Wrong Which-ID");

            SvXMLUnitConverter::convertEnum( aOut, pVertOrient->GetVertOrient(),
                                        aXMLTableVAlignMap );
            bOk = true;
        }
        break;

        case RES_FRM_SIZE:
        {
            const SwFormatFrameSize& rFrameSize = dynamic_cast<const SwFormatFrameSize&>(rItem);

            bool bOutHeight = false;
            switch( nMemberId )
            {
                case MID_FRMSIZE_REL_WIDTH:
                    if (rFrameSize.GetWidthPercent())
                    {
                        ::sax::Converter::convertPercent(
                                aOut, rFrameSize.GetWidthPercent() );
                        bOk = true;
                    }
                    break;
                case MID_FRMSIZE_MIN_HEIGHT:
                    if( SwFrameSize::Minimum == rFrameSize.GetHeightSizeType() )
                        bOutHeight = true;
                    break;
                case MID_FRMSIZE_FIX_HEIGHT:
                    if( SwFrameSize::Fixed == rFrameSize.GetHeightSizeType() )
                        bOutHeight = true;
                    break;
            }

            if( bOutHeight )
            {
                rUnitConverter.convertMeasureToXML(aOut, rFrameSize.GetHeight());
                bOk = true;
            }
        }
        break;

        case RES_FRAMEDIR:
        {
            Any aAny;
            bOk = rItem.QueryValue( aAny );
            if( bOk )
            {
                std::unique_ptr<XMLPropertyHandler> pWritingModeHandler =
                    XMLPropertyHandlerFactory::CreatePropertyHandler(
                        XML_TYPE_TEXT_WRITING_MODE_WITH_DEFAULT );
                OUString sValue;
                bOk = pWritingModeHandler->exportXML( sValue, aAny,
                                                      rUnitConverter );
                if( bOk )
                    aOut.append( sValue );
            }
        }
        break;

        case RES_COLLAPSING_BORDERS:
        {
            const SfxBoolItem* pBorders = dynamic_cast<const SfxBoolItem*>( &rItem );
            assert(pBorders && "Wrong RES-ID");
            if (pBorders)
            {
                aOut.append( pBorders->GetValue()
                             ? GetXMLToken( XML_COLLAPSING )
                             : GetXMLToken( XML_SEPARATING ) );
                bOk = true;
            }
        }
        break;

        default:
            OSL_FAIL("GetXMLValue not implemented for this item.");
            break;
    }

    if ( bOk )
        rValue = aOut.makeStringAndClear();

    return bOk;
}

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