    /* -*- 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 "DomainMapperTableHandler.hxx"
    #include "DomainMapper_Impl.hxx"
    #include "StyleSheetTable.hxx"
    #include <com/sun/star/beans/XPropertyState.hpp>
    #include <com/sun/star/container/XEnumerationAccess.hpp>
    #include <com/sun/star/table/TableBorderDistances.hpp>
    #include <com/sun/star/table/TableBorder.hpp>
    #include <com/sun/star/table/BorderLine2.hpp>
    #include <com/sun/star/table/BorderLineStyle.hpp>
    #include <com/sun/star/table/XCellRange.hpp>
    #include <com/sun/star/text/HoriOrientation.hpp>
    #include <com/sun/star/text/RelOrientation.hpp>
    #include <com/sun/star/text/SizeType.hpp>
    #include <com/sun/star/text/VertOrientation.hpp>
    #include <com/sun/star/text/XTextRangeCompare.hpp>
    #include <com/sun/star/style/ParagraphAdjust.hpp>
    #include "TablePositionHandler.hxx"
    #include "ConversionHelper.hxx"
    #include "util.hxx"
    #include <osl/diagnose.h>
    #include <sal/log.hxx>
    #include <comphelper/sequence.hxx>

    #ifdef DEBUG_WRITERFILTER
    #include "PropertyMapHelper.hxx"
    #include <rtl/ustring.hxx>
    #endif

    namespace writerfilter {
    namespace dmapper {

    using namespace ::com::sun::star;
    using namespace ::std;

    #define DEF_BORDER_DIST 190  //0,19cm

    DomainMapperTableHandler::DomainMapperTableHandler(
                css::uno::Reference<css::text::XTextAppendAndConvert> const& xText,
                DomainMapper_Impl& rDMapper_Impl)
        : m_xText(xText),
            m_rDMapper_Impl( rDMapper_Impl ),
            m_bHadFootOrEndnote(false)
    {
    }

    DomainMapperTableHandler::~DomainMapperTableHandler()
    {
    }

    void DomainMapperTableHandler::startTable(const TablePropertyMapPtr& pProps)
    {
        m_aTableProperties = pProps;
        m_aTableRanges.clear();

    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement("tablehandler.table");

        if (pProps.get() != nullptr)
            pProps->dumpXml();
    #endif
    }

    void lcl_mergeBorder( PropertyIds nId, const PropertyMapPtr& pOrig, const PropertyMapPtr& pDest )
    {
        boost::optional<PropertyMap::Property> pOrigVal = pOrig->getProperty(nId);

        if ( pOrigVal )
        {
            pDest->Insert( nId, pOrigVal->second, false );
        }
    }

    void lcl_computeCellBorders( const PropertyMapPtr& pTableBorders, const PropertyMapPtr& pCellProps,
            sal_Int32 nCell, sal_Int32 nRow, bool bIsEndCol, bool bIsEndRow )
    {
        boost::optional<PropertyMap::Property> pVerticalVal = pCellProps->getProperty(META_PROP_VERTICAL_BORDER);
        boost::optional<PropertyMap::Property> pHorizontalVal = pCellProps->getProperty(META_PROP_HORIZONTAL_BORDER);

        // Handle the vertical and horizontal borders
        uno::Any aVertProp;
        if ( !pVerticalVal)
        {
            pVerticalVal = pTableBorders->getProperty(META_PROP_VERTICAL_BORDER);
            if ( pVerticalVal )
                aVertProp = pVerticalVal->second;
        }
        else
        {
            aVertProp = pVerticalVal->second;
            pCellProps->Erase( pVerticalVal->first );
        }

        uno::Any aHorizProp;
        if ( !pHorizontalVal )
        {
            pHorizontalVal = pTableBorders->getProperty(META_PROP_HORIZONTAL_BORDER);
            if ( pHorizontalVal )
                aHorizProp = pHorizontalVal->second;
        }
        else
        {
            aHorizProp = pHorizontalVal->second;
            pCellProps->Erase( pHorizontalVal->first );
        }

        if ( nCell == 0 )
        {
            lcl_mergeBorder( PROP_LEFT_BORDER, pTableBorders, pCellProps );
            if ( pVerticalVal )
                pCellProps->Insert( PROP_RIGHT_BORDER, aVertProp, false );
        }

        if ( bIsEndCol )
        {
            lcl_mergeBorder( PROP_RIGHT_BORDER, pTableBorders, pCellProps );
            if ( pVerticalVal )
                pCellProps->Insert( PROP_LEFT_BORDER, aVertProp, false );
        }

        if ( nCell > 0 && !bIsEndCol )
        {
            if ( pVerticalVal )
            {
                pCellProps->Insert( PROP_RIGHT_BORDER, aVertProp, false );
                pCellProps->Insert( PROP_LEFT_BORDER, aVertProp, false );
            }
        }

        if ( nRow == 0 )
        {
            lcl_mergeBorder( PROP_TOP_BORDER, pTableBorders, pCellProps );
            if ( pHorizontalVal )
                pCellProps->Insert( PROP_BOTTOM_BORDER, aHorizProp, false );
        }

        if ( bIsEndRow )
        {
            lcl_mergeBorder( PROP_BOTTOM_BORDER, pTableBorders, pCellProps );
            if ( pHorizontalVal )
                pCellProps->Insert( PROP_TOP_BORDER, aHorizProp, false );
        }

        if ( nRow > 0 && !bIsEndRow )
        {
            if ( pHorizontalVal )
            {
                pCellProps->Insert( PROP_TOP_BORDER, aHorizProp, false );
                pCellProps->Insert( PROP_BOTTOM_BORDER, aHorizProp, false );
            }
        }
    }

    #ifdef DEBUG_WRITERFILTER

    void lcl_debug_BorderLine(table::BorderLine const & rLine)
    {
        TagLogger::getInstance().startElement("BorderLine");
        TagLogger::getInstance().attribute("Color", rLine.Color);
        TagLogger::getInstance().attribute("InnerLineWidth", rLine.InnerLineWidth);
        TagLogger::getInstance().attribute("OuterLineWidth", rLine.OuterLineWidth);
        TagLogger::getInstance().attribute("LineDistance", rLine.LineDistance);
        TagLogger::getInstance().endElement();
    }

    void lcl_debug_TableBorder(table::TableBorder const & rBorder)
    {
        TagLogger::getInstance().startElement("TableBorder");
        lcl_debug_BorderLine(rBorder.TopLine);
        TagLogger::getInstance().attribute("IsTopLineValid", sal_uInt32(rBorder.IsTopLineValid));
        lcl_debug_BorderLine(rBorder.BottomLine);
        TagLogger::getInstance().attribute("IsBottomLineValid", sal_uInt32(rBorder.IsBottomLineValid));
        lcl_debug_BorderLine(rBorder.LeftLine);
        TagLogger::getInstance().attribute("IsLeftLineValid", sal_uInt32(rBorder.IsLeftLineValid));
        lcl_debug_BorderLine(rBorder.RightLine);
        TagLogger::getInstance().attribute("IsRightLineValid", sal_uInt32(rBorder.IsRightLineValid));
        lcl_debug_BorderLine(rBorder.VerticalLine);
        TagLogger::getInstance().attribute("IsVerticalLineValid", sal_uInt32(rBorder.IsVerticalLineValid));
        lcl_debug_BorderLine(rBorder.HorizontalLine);
        TagLogger::getInstance().attribute("IsHorizontalLineValid", sal_uInt32(rBorder.IsHorizontalLineValid));
        TagLogger::getInstance().attribute("Distance", rBorder.Distance);
        TagLogger::getInstance().attribute("IsDistanceValid", sal_uInt32(rBorder.IsDistanceValid));
        TagLogger::getInstance().endElement();
    }
    #endif

    struct TableInfo
    {
        sal_Int32 nLeftBorderDistance;
        sal_Int32 nRightBorderDistance;
        sal_Int32 nTopBorderDistance;
        sal_Int32 nBottomBorderDistance;
        sal_Int32 nTblLook;
        sal_Int32 nNestLevel;
        PropertyMapPtr pTableDefaults;
        PropertyMapPtr pTableBorders;
        TableStyleSheetEntry* pTableStyle;
        css::beans::PropertyValues aTableProperties;

        TableInfo()
        : nLeftBorderDistance(DEF_BORDER_DIST)
        , nRightBorderDistance(DEF_BORDER_DIST)
        , nTopBorderDistance(0)
        , nBottomBorderDistance(0)
        , nTblLook(0x4a0)
        , nNestLevel(0)
        , pTableDefaults(new PropertyMap)
        , pTableBorders(new PropertyMap)
        , pTableStyle(nullptr)
        {
        }

    };

    namespace
    {

    bool lcl_extractTableBorderProperty(const PropertyMapPtr& pTableProperties, const PropertyIds nId, TableInfo const & rInfo, table::BorderLine2& rLine)
    {
        if (!pTableProperties)
            return false;

        const boost::optional<PropertyMap::Property> aTblBorder = pTableProperties->getProperty(nId);
        if( aTblBorder )
        {
            OSL_VERIFY(aTblBorder->second >>= rLine);

            rInfo.pTableBorders->Insert( nId, uno::makeAny( rLine ) );
            rInfo.pTableDefaults->Erase( nId );

            return true;
        }

        return false;
    }

    void lcl_extractHoriOrient(std::vector<beans::PropertyValue>& rFrameProperties, sal_Int32& nHoriOrient)
    {
        // Shifts the frame left by the given value.
        for (beans::PropertyValue & rFrameProperty : rFrameProperties)
        {
            if (rFrameProperty.Name == "HoriOrient")
            {
                sal_Int32 nValue = rFrameProperty.Value.get<sal_Int32>();
                if (nValue != text::HoriOrientation::NONE)
                    nHoriOrient = nValue;
                return;
            }
        }
    }

    }

    void lcl_DecrementHoriOrientPosition(std::vector<beans::PropertyValue>& rFrameProperties, sal_Int32 nAmount)
    {
        // Shifts the frame left by the given value.
        for (beans::PropertyValue & rPropertyValue : rFrameProperties)
        {
            if (rPropertyValue.Name == "HoriOrientPosition")
            {
                sal_Int32 nValue = rPropertyValue.Value.get<sal_Int32>();
                nValue -= nAmount;
                rPropertyValue.Value <<= nValue;
                return;
            }
        }
    }

    TableStyleSheetEntry * DomainMapperTableHandler::endTableGetTableStyle(TableInfo & rInfo, std::vector<beans::PropertyValue>& rFrameProperties)
    {
        // will receive the table style if any
        TableStyleSheetEntry* pTableStyle = nullptr;

        if( m_aTableProperties.get() )
        {
            //create properties from the table attributes
            //...pPropMap->Insert( PROP_LEFT_MARGIN, uno::makeAny( m_nLeftMargin - m_nGapHalf ));
            //pPropMap->Insert( PROP_HORI_ORIENT, uno::makeAny( text::HoriOrientation::RIGHT ));
            sal_Int32 nGapHalf = 0;
            sal_Int32 nLeftMargin = 0;

            comphelper::SequenceAsHashMap aGrabBag;

            if (nullptr != m_rDMapper_Impl.getTableManager().getCurrentTableRealPosition())
            {
                TablePositionHandler *pTablePositions = m_rDMapper_Impl.getTableManager().getCurrentTableRealPosition();

                uno::Sequence< beans::PropertyValue  > aGrabBagTS( 10 );

                aGrabBagTS[0].Name = "bottomFromText";
                aGrabBagTS[0].Value <<= pTablePositions->getBottomFromText();

                aGrabBagTS[1].Name = "horzAnchor";
                aGrabBagTS[1].Value <<= pTablePositions->getHorzAnchor();

                aGrabBagTS[2].Name = "leftFromText";
                aGrabBagTS[2].Value <<= pTablePositions->getLeftFromText();

                aGrabBagTS[3].Name = "rightFromText";
                aGrabBagTS[3].Value <<= pTablePositions->getRightFromText();

                aGrabBagTS[4].Name = "tblpX";
                aGrabBagTS[4].Value <<= pTablePositions->getX();

                aGrabBagTS[5].Name = "tblpXSpec";
                aGrabBagTS[5].Value <<= pTablePositions->getXSpec();

                aGrabBagTS[6].Name = "tblpY";
                aGrabBagTS[6].Value <<= pTablePositions->getY();

                aGrabBagTS[7].Name = "tblpYSpec";
                aGrabBagTS[7].Value <<= pTablePositions->getYSpec();

                aGrabBagTS[8].Name = "topFromText";
                aGrabBagTS[8].Value <<= pTablePositions->getTopFromText();

                aGrabBagTS[9].Name = "vertAnchor";
                aGrabBagTS[9].Value <<= pTablePositions->getVertAnchor();

                aGrabBag["TablePosition"] <<= aGrabBagTS;
            }

            boost::optional<PropertyMap::Property> aTableStyleVal = m_aTableProperties->getProperty(META_PROP_TABLE_STYLE_NAME);
            if(aTableStyleVal)
            {
                // Apply table style properties recursively
                OUString sTableStyleName;
                aTableStyleVal->second >>= sTableStyleName;
                StyleSheetTablePtr pStyleSheetTable = m_rDMapper_Impl.GetStyleSheetTable();
                const StyleSheetEntryPtr pStyleSheet = pStyleSheetTable->FindStyleSheetByISTD( sTableStyleName );
                pTableStyle = dynamic_cast<TableStyleSheetEntry*>( pStyleSheet.get( ) );
                m_aTableProperties->Erase( aTableStyleVal->first );

                aGrabBag["TableStyleName"] <<= sTableStyleName;

                if( pStyleSheet )
                {
                    // First get the style properties, then the table ones
                    PropertyMapPtr pTableProps( m_aTableProperties.get() );
                    TablePropertyMapPtr pEmptyProps( new TablePropertyMap );

                    m_aTableProperties = pEmptyProps;

                    PropertyMapPtr pMergedProperties = pStyleSheet->GetMergedInheritedProperties(pStyleSheetTable);

                    table::BorderLine2 aBorderLine;
                    TableInfo rStyleInfo;
                    if (lcl_extractTableBorderProperty(pMergedProperties, PROP_TOP_BORDER, rStyleInfo, aBorderLine))
                    {
                        aGrabBag["TableStyleTopBorder"] <<= aBorderLine;
                    }
                    if (lcl_extractTableBorderProperty(pMergedProperties, PROP_BOTTOM_BORDER, rStyleInfo, aBorderLine))
                    {
                        aGrabBag["TableStyleBottomBorder"] <<= aBorderLine;
                    }
                    if (lcl_extractTableBorderProperty(pMergedProperties, PROP_LEFT_BORDER, rStyleInfo, aBorderLine))
                    {
                        aGrabBag["TableStyleLeftBorder"] <<= aBorderLine;
                    }
                    if (lcl_extractTableBorderProperty(pMergedProperties, PROP_RIGHT_BORDER, rStyleInfo, aBorderLine))
                    {
                        aGrabBag["TableStyleRightBorder"] <<= aBorderLine;
                    }

    #ifdef DEBUG_WRITERFILTER
                    TagLogger::getInstance().startElement("mergedProps");
                    if (pMergedProperties)
                        pMergedProperties->dumpXml();
                    TagLogger::getInstance().endElement();
    #endif

                    m_aTableProperties->InsertProps(pMergedProperties);
                    m_aTableProperties->InsertProps(pTableProps);

    #ifdef DEBUG_WRITERFILTER
                    TagLogger::getInstance().startElement("TableProperties");
                    m_aTableProperties->dumpXml();
                    TagLogger::getInstance().endElement();
    #endif
                }
            }

            // This is the one preserving just all the table look attributes.
            boost::optional<PropertyMap::Property> oTableLook = m_aTableProperties->getProperty(META_PROP_TABLE_LOOK);
            if (oTableLook)
            {
                aGrabBag["TableStyleLook"] = oTableLook->second;
                m_aTableProperties->Erase(oTableLook->first);
            }

            // This is just the "val" attribute's numeric value.
            const boost::optional<PropertyMap::Property> aTblLook = m_aTableProperties->getProperty(PROP_TBL_LOOK);
            if(aTblLook)
            {
                aTblLook->second >>= rInfo.nTblLook;
                m_aTableProperties->Erase( aTblLook->first );
            }

            // Set the table default attributes for the cells
            rInfo.pTableDefaults->InsertProps(m_aTableProperties.get());

    #ifdef DEBUG_WRITERFILTER
            TagLogger::getInstance().startElement("TableDefaults");
            rInfo.pTableDefaults->dumpXml();
            TagLogger::getInstance().endElement();
    #endif

            if (!aGrabBag.empty())
            {
                m_aTableProperties->Insert( PROP_TABLE_INTEROP_GRAB_BAG, uno::makeAny( aGrabBag.getAsConstPropertyValueList() ) );
            }

            m_aTableProperties->getValue( TablePropertyMap::GAP_HALF, nGapHalf );
            m_aTableProperties->getValue( TablePropertyMap::LEFT_MARGIN, nLeftMargin );

            m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_LEFT,
                                         rInfo.nLeftBorderDistance );
            m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_RIGHT,
                                         rInfo.nRightBorderDistance );
            m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_TOP,
                                         rInfo.nTopBorderDistance );
            m_aTableProperties->getValue( TablePropertyMap::CELL_MAR_BOTTOM,
                                         rInfo.nBottomBorderDistance );

            table::TableBorderDistances aDistances;
            aDistances.IsTopDistanceValid =
            aDistances.IsBottomDistanceValid =
            aDistances.IsLeftDistanceValid =
            aDistances.IsRightDistanceValid = true;
            aDistances.TopDistance = static_cast<sal_Int16>( rInfo.nTopBorderDistance );
            aDistances.BottomDistance = static_cast<sal_Int16>( rInfo.nBottomBorderDistance );
            aDistances.LeftDistance = static_cast<sal_Int16>( rInfo.nLeftBorderDistance );
            aDistances.RightDistance = static_cast<sal_Int16>( rInfo.nRightBorderDistance );

            m_aTableProperties->Insert( PROP_TABLE_BORDER_DISTANCES, uno::makeAny( aDistances ) );

            if (!rFrameProperties.empty())
                lcl_DecrementHoriOrientPosition(rFrameProperties, rInfo.nLeftBorderDistance);

            // Set table above/bottom spacing to 0.
            m_aTableProperties->Insert( PROP_TOP_MARGIN, uno::makeAny( sal_Int32( 0 ) ) );
            m_aTableProperties->Insert( PROP_BOTTOM_MARGIN, uno::makeAny( sal_Int32( 0 ) ) );

            //table border settings
            table::TableBorder aTableBorder;
            table::BorderLine2 aBorderLine, aLeftBorder;

            if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_TOP_BORDER, rInfo, aBorderLine))
            {
                aTableBorder.TopLine = aBorderLine;
                aTableBorder.IsTopLineValid = true;
            }
            if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_BOTTOM_BORDER, rInfo, aBorderLine))
            {
                aTableBorder.BottomLine = aBorderLine;
                aTableBorder.IsBottomLineValid = true;
            }
            if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_LEFT_BORDER, rInfo, aLeftBorder))
            {
                aTableBorder.LeftLine = aLeftBorder;
                aTableBorder.IsLeftLineValid = true;
                // Only top level table position depends on border width
                if (rInfo.nNestLevel == 1)
                {
                    if (rFrameProperties.empty())
                        rInfo.nLeftBorderDistance += aLeftBorder.LineWidth * 0.5;
                    else
                        lcl_DecrementHoriOrientPosition(rFrameProperties, aLeftBorder.LineWidth * 0.5);
                }
            }
            if (lcl_extractTableBorderProperty(m_aTableProperties.get(), PROP_RIGHT_BORDER, rInfo, aBorderLine))
            {
                aTableBorder.RightLine = aBorderLine;
                aTableBorder.IsRightLineValid = true;
            }
            if (lcl_extractTableBorderProperty(m_aTableProperties.get(), META_PROP_HORIZONTAL_BORDER, rInfo, aBorderLine))
            {
                aTableBorder.HorizontalLine = aBorderLine;
                aTableBorder.IsHorizontalLineValid = true;
            }
            if (lcl_extractTableBorderProperty(m_aTableProperties.get(), META_PROP_VERTICAL_BORDER, rInfo, aBorderLine))
            {
                aTableBorder.VerticalLine = aBorderLine;
                aTableBorder.IsVerticalLineValid = true;
            }

            aTableBorder.Distance = 0;
            aTableBorder.IsDistanceValid = false;

            m_aTableProperties->Insert( PROP_TABLE_BORDER, uno::makeAny( aTableBorder ) );

    #ifdef DEBUG_WRITERFILTER
            lcl_debug_TableBorder(aTableBorder);
    #endif

            // Table position in Office is computed in 2 different ways :
            // - top level tables: the goal is to have in-cell text starting at table indent pos (tblInd),
            //   so table's position depends on table's cells margin
            // - nested tables: the goal is to have left-most border starting at table_indent pos

            // tdf#106742: since MS Word 2013 (compatibilityMode >= 15), top-level tables are handled the same as nested tables;
            // this is also the default behavior in LO when DOCX doesn't define "compatibilityMode" option
            sal_Int32 nMode = m_rDMapper_Impl.GetSettingsTable()->GetWordCompatibilityMode();

            if ( nMode > 0 && nMode <= 14 && rInfo.nNestLevel == 1 )
            {
                m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::makeAny( nLeftMargin - nGapHalf - rInfo.nLeftBorderDistance ) );
            }
            else
            {
                m_aTableProperties->Insert( PROP_LEFT_MARGIN, uno::makeAny( nLeftMargin - nGapHalf ) );
            }

            sal_Int32 nTableWidth = 0;
            sal_Int32 nTableWidthType = text::SizeType::FIX;
            m_aTableProperties->getValue( TablePropertyMap::TABLE_WIDTH, nTableWidth );
            m_aTableProperties->getValue( TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType );
            if( nTableWidthType == text::SizeType::FIX )
            {
                if( nTableWidth > 0 )
                    m_aTableProperties->Insert( PROP_WIDTH, uno::makeAny( nTableWidth ));
                else
                {
                    // tdf#109524: If there is no width for the table, make it simply 100% by default.
                    // TODO: use cell contents to evaluate width (according to ECMA-376-1:2016 17.18.87)
                    nTableWidth = 100;
                    nTableWidthType = text::SizeType::VARIABLE;
                }
            }
            if (nTableWidthType != text::SizeType::FIX)
            {
                m_aTableProperties->Insert( PROP_RELATIVE_WIDTH, uno::makeAny( sal_Int16( nTableWidth ) ) );
                m_aTableProperties->Insert( PROP_IS_WIDTH_RELATIVE, uno::makeAny( true ) );
            }

            sal_Int32 nHoriOrient = text::HoriOrientation::LEFT_AND_WIDTH;
            // Fetch Horizontal Orientation in rFrameProperties if not set in m_aTableProperties
            if ( !m_aTableProperties->getValue( TablePropertyMap::HORI_ORIENT, nHoriOrient ) )
                lcl_extractHoriOrient( rFrameProperties, nHoriOrient );
            m_aTableProperties->Insert( PROP_HORI_ORIENT, uno::makeAny( sal_Int16(nHoriOrient) ) );
            //fill default value - if not available
            m_aTableProperties->Insert( PROP_HEADER_ROW_COUNT, uno::makeAny( sal_Int32(0)), false);

            // if table is only a single row, and row is set as don't split, set the same value for the whole table.
            if( m_aRowProperties.size() == 1 && m_aRowProperties[0].get() )
            {
                boost::optional<PropertyMap::Property> oSplitAllowed = m_aRowProperties[0]->getProperty(PROP_IS_SPLIT_ALLOWED);
                if( oSplitAllowed )
                {
                    bool bRowCanSplit = true;
                    oSplitAllowed->second >>= bRowCanSplit;
                    if( !bRowCanSplit )
                        m_aTableProperties->Insert( PROP_SPLIT, uno::makeAny(bRowCanSplit) );
                }
            }

            rInfo.aTableProperties = m_aTableProperties->GetPropertyValues();

    #ifdef DEBUG_WRITERFILTER
            TagLogger::getInstance().startElement("debug.tableprops");
            m_aTableProperties->dumpXml();
            TagLogger::getInstance().endElement();
    #endif

        }

        return pTableStyle;
    }

    #define CNF_FIRST_ROW               0x800
    #define CNF_LAST_ROW                0x400
    #define CNF_FIRST_COLUMN            0x200
    #define CNF_LAST_COLUMN             0x100
    #define CNF_ODD_VBAND               0x080
    #define CNF_EVEN_VBAND              0x040
    #define CNF_ODD_HBAND               0x020
    #define CNF_EVEN_HBAND              0x010
    #define CNF_FIRST_ROW_LAST_COLUMN   0x008
    #define CNF_FIRST_ROW_FIRST_COLUMN  0x004
    #define CNF_LAST_ROW_LAST_COLUMN    0x002
    #define CNF_LAST_ROW_FIRST_COLUMN   0x001

    CellPropertyValuesSeq_t DomainMapperTableHandler::endTableGetCellProperties(TableInfo & rInfo, std::vector<HorizontallyMergedCell>& rMerges)
    {
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement("getCellProperties");
    #endif

        CellPropertyValuesSeq_t aCellProperties( m_aCellProperties.size() );

        if ( m_aCellProperties.empty() )
        {
            #ifdef DEBUG_WRITERFILTER
            TagLogger::getInstance().endElement();
            #endif
            return aCellProperties;
        }
        // std::vector< std::vector<PropertyMapPtr> > m_aCellProperties
        PropertyMapVector2::const_iterator aRowOfCellsIterator = m_aCellProperties.begin();
        PropertyMapVector2::const_iterator aRowOfCellsIteratorEnd = m_aCellProperties.end();
        PropertyMapVector2::const_iterator aLastRowIterator = m_aCellProperties.end() - 1;
        sal_Int32 nRow = 0;

        css::uno::Sequence<css::beans::PropertyValues>* pCellProperties = aCellProperties.getArray();
        PropertyMapVector1::const_iterator aRowIter = m_aRowProperties.begin();
        while( aRowOfCellsIterator != aRowOfCellsIteratorEnd )
        {
            //aRowOfCellsIterator points to a vector of PropertyMapPtr
            PropertyMapVector1::const_iterator aCellIterator = aRowOfCellsIterator->begin();
            PropertyMapVector1::const_iterator aCellIteratorEnd = aRowOfCellsIterator->end();

            sal_Int32 nRowStyleMask = 0;

            if (aRowOfCellsIterator==m_aCellProperties.begin())
            {
                if(rInfo.nTblLook&0x20)
                    nRowStyleMask |= CNF_FIRST_ROW;     // first row style used
            }
            else if (aRowOfCellsIterator==aLastRowIterator)
            {
                if(rInfo.nTblLook&0x40)
                    nRowStyleMask |= CNF_LAST_ROW;      // last row style used
            }
            else if (*aRowIter && (*aRowIter)->isSet(PROP_TBL_HEADER))
                nRowStyleMask |= CNF_FIRST_ROW; // table header implies first row
            if(!nRowStyleMask)                          // if no row style used yet
            {
                // banding used only if not first and or last row style used
                if(!(rInfo.nTblLook&0x200))
                {   // hbanding used
                    int n = nRow + 1;
                    if(rInfo.nTblLook&0x20)
                        n++;
                    if(n & 1)
                        nRowStyleMask = CNF_ODD_HBAND;
                    else
                        nRowStyleMask = CNF_EVEN_HBAND;
                }
            }

            sal_Int32 nCell = 0;
            pCellProperties[nRow].realloc( aRowOfCellsIterator->size() );
            beans::PropertyValues* pSingleCellProperties = pCellProperties[nRow].getArray();
            while( aCellIterator != aCellIteratorEnd )
            {
                PropertyMapPtr pAllCellProps( new PropertyMap );

                PropertyMapVector1::const_iterator aLastCellIterator = aRowOfCellsIterator->end() - 1;
                bool bIsEndCol = aCellIterator == aLastCellIterator;
                bool bIsEndRow = aRowOfCellsIterator == aLastRowIterator;

                //aCellIterator points to a PropertyMapPtr;
                if( *aCellIterator )
                {
                    // remove directly applied insideV/H borders since they are meaningless without a context (tdf#82177)
                    (*aCellIterator)->Erase(META_PROP_VERTICAL_BORDER);
                    (*aCellIterator)->Erase(META_PROP_HORIZONTAL_BORDER);

                    pAllCellProps->InsertProps(rInfo.pTableDefaults);

                    sal_Int32 nCellStyleMask = 0;
                    if (aCellIterator==aRowOfCellsIterator->begin())
                    {
                        if(rInfo.nTblLook&0x80)
                            nCellStyleMask = CNF_FIRST_COLUMN;      // first col style used
                    }
                    else if (bIsEndCol)
                    {
                        if(rInfo.nTblLook&0x100)
                            nCellStyleMask = CNF_LAST_COLUMN;       // last col style used
                    }
                    if(!nCellStyleMask)                 // if no cell style is used yet
                    {
                        if(!(rInfo.nTblLook&0x400))
                        {   // vbanding used
                            int n = nCell + 1;
                            if(rInfo.nTblLook&0x80)
                                n++;
                            if(n & 1)
                                nCellStyleMask = CNF_ODD_VBAND;
                            else
                                nCellStyleMask = CNF_EVEN_VBAND;
                        }
                    }
                    sal_Int32 nCnfStyleMask = nCellStyleMask + nRowStyleMask;
                    if(nCnfStyleMask == CNF_FIRST_COLUMN + CNF_FIRST_ROW)
                        nCnfStyleMask |= CNF_FIRST_ROW_FIRST_COLUMN;
                    else if(nCnfStyleMask == CNF_FIRST_COLUMN + CNF_LAST_ROW)
                        nCnfStyleMask |= CNF_LAST_ROW_FIRST_COLUMN;
                    else if(nCnfStyleMask == CNF_LAST_COLUMN + CNF_FIRST_ROW)
                        nCnfStyleMask |= CNF_FIRST_ROW_LAST_COLUMN;
                    else if(nCnfStyleMask == CNF_LAST_COLUMN + CNF_LAST_ROW)
                        nCnfStyleMask |= CNF_LAST_ROW_LAST_COLUMN;

                    if ( rInfo.pTableStyle )
                    {
                        PropertyMapPtr pStyleProps = rInfo.pTableStyle->GetProperties( nCnfStyleMask );

                        // Check if we need to clean up some empty border definitions to match what Word does.
                        static const PropertyIds pBorders[] =
                        {
                            PROP_TOP_BORDER, PROP_LEFT_BORDER, PROP_BOTTOM_BORDER, PROP_RIGHT_BORDER
                        };
                        for (const PropertyIds& rBorder : pBorders)
                        {
                            boost::optional<PropertyMap::Property> oStyleCellBorder = pStyleProps->getProperty(rBorder);
                            boost::optional<PropertyMap::Property> oDirectCellBorder = (*aCellIterator)->getProperty(rBorder);
                            if (oStyleCellBorder && oDirectCellBorder)
                            {
                                // We have a cell border from the table style and as direct formatting as well.
                                table::BorderLine2 aStyleCellBorder = oStyleCellBorder->second.get<table::BorderLine2>();
                                table::BorderLine2 aDirectCellBorder = oDirectCellBorder->second.get<table::BorderLine2>();
                                if (aStyleCellBorder.LineStyle != table::BorderLineStyle::NONE && aDirectCellBorder.LineStyle == table::BorderLineStyle::NONE)
                                {
                                    // The style one would be visible, but then cleared away as direct formatting.
                                    // Delete both, so that table formatting can become visible.
                                    pStyleProps->Erase(rBorder);
                                    (*aCellIterator)->Erase(rBorder);
                                }
                                else
                                {
                                    boost::optional<PropertyMap::Property> oTableBorder = rInfo.pTableBorders->getProperty(rBorder);
                                    if (oTableBorder)
                                    {
                                        table::BorderLine2 aTableBorder = oTableBorder->second.get<table::BorderLine2>();
                                        // Both style and direct formatting says that the cell has no border.
                                        bool bNoCellBorder = aStyleCellBorder.LineStyle == table::BorderLineStyle::NONE && aDirectCellBorder.LineStyle == table::BorderLineStyle::NONE;
                                        if (aTableBorder.LineStyle != table::BorderLineStyle::NONE && bNoCellBorder)
                                        {
                                            // But at a table-level, there is a border, then again delete both cell properties.
                                            pStyleProps->Erase(rBorder);
                                            (*aCellIterator)->Erase(rBorder);
                                        }
                                    }
                                }
                            }
                        }

                        pAllCellProps->InsertProps( pStyleProps );
                    }

                    // Remove properties from style/row that aren't allowed in cells
                    pAllCellProps->Erase( PROP_HEADER_ROW_COUNT );
                    pAllCellProps->Erase( PROP_TBL_HEADER );

                    // Then add the cell properties
                    pAllCellProps->InsertProps(*aCellIterator);
                    std::swap(*(*aCellIterator), *pAllCellProps );

    #ifdef DEBUG_WRITERFILTER
                    TagLogger::getInstance().startElement("cell");
                    TagLogger::getInstance().attribute("cell", nCell);
                    TagLogger::getInstance().attribute("row", nRow);
    #endif

                    lcl_computeCellBorders( rInfo.pTableBorders, *aCellIterator, nCell, nRow, bIsEndCol, bIsEndRow );

                    //now set the default left+right border distance TODO: there's an sprm containing the default distance!
                    aCellIterator->get()->Insert( PROP_LEFT_BORDER_DISTANCE,
                                                     uno::makeAny(rInfo.nLeftBorderDistance ), false);
                    aCellIterator->get()->Insert( PROP_RIGHT_BORDER_DISTANCE,
                                                     uno::makeAny(rInfo.nRightBorderDistance ), false);
                    aCellIterator->get()->Insert( PROP_TOP_BORDER_DISTANCE,
                                                     uno::makeAny(rInfo.nTopBorderDistance ), false);
                    aCellIterator->get()->Insert( PROP_BOTTOM_BORDER_DISTANCE,
                                                     uno::makeAny(rInfo.nBottomBorderDistance ), false);

                    // Horizontal merge is not an UNO property, extract that info here to rMerges, and then remove it from the map.
                    const boost::optional<PropertyMap::Property> aHorizontalMergeVal = (*aCellIterator)->getProperty(PROP_HORIZONTAL_MERGE);
                    if (aHorizontalMergeVal)
                    {
                        if (aHorizontalMergeVal->second.get<bool>())
                        {
                            // first cell in a merge
                            HorizontallyMergedCell aMerge(nRow, nCell);
                            rMerges.push_back(aMerge);
                        }
                        else if (!rMerges.empty())
                        {
                            // resuming an earlier merge
                            HorizontallyMergedCell& rMerge = rMerges.back();
                            rMerge.m_nLastRow = nRow;
                            rMerge.m_nLastCol = nCell;
                        }
                        (*aCellIterator)->Erase(PROP_HORIZONTAL_MERGE);
                    }

                    // Cell direction is not an UNO Property, either.
                    const boost::optional<PropertyMap::Property> aCellDirectionVal = (*aCellIterator)->getProperty(PROP_CELL_DIRECTION);
                    if (aCellDirectionVal)
                    {
                        if (aCellDirectionVal->second.get<sal_Int32>() == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_TextDirection_btLr))
                        {
                            // btLr, so map ParagraphAdjust_CENTER to VertOrientation::CENTER.
                            uno::Reference<beans::XPropertySet> xPropertySet(m_aTableRanges[nRow][nCell][0], uno::UNO_QUERY);
                            if (xPropertySet.is() && xPropertySet->getPropertyValue("ParaAdjust").get<sal_Int16>() == sal_Int16(style::ParagraphAdjust_CENTER))
                                (*aCellIterator)->Insert(PROP_VERT_ORIENT, uno::makeAny(text::VertOrientation::CENTER));
                        }
                        (*aCellIterator)->Erase(PROP_CELL_DIRECTION);
                    }

                    pSingleCellProperties[nCell] = (*aCellIterator)->GetPropertyValues();
    #ifdef DEBUG_WRITERFILTER
                    TagLogger::getInstance().endElement();
    #endif
                }
                ++nCell;
                ++aCellIterator;
            }
            ++nRow;
            ++aRowOfCellsIterator;
            ++aRowIter;
        }

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

        return aCellProperties;
    }

    /// Do all cells in this row have a CellHideMark property?
    bool lcl_hideMarks(PropertyMapVector1& rCellProperties)
    {
        for (PropertyMapPtr & p : rCellProperties)
        {
            // if anything is vertically merged, the row must not be set to fixed
            // as Writer's layout doesn't handle that well
            if (!p->isSet(PROP_CELL_HIDE_MARK) || p->isSet(PROP_VERTICAL_MERGE))
                return false;
        }
        return true;
    }

    /// Are all cells in this row empty?
    bool lcl_emptyRow(std::vector<RowSequence_t>& rTableRanges, sal_Int32 nRow)
    {
        if (nRow >= static_cast<sal_Int32>(rTableRanges.size()))
        {
            SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?");
            return false;
        }

        RowSequence_t rRowSeq = rTableRanges[nRow];
        if (rRowSeq.getLength() == 0)
        {
            SAL_WARN("writerfilter.dmapper", "m_aCellProperties not in sync with rTableRanges?");
            return false;
        }

        if (!rRowSeq[0][0].is())
        {
            // This can happen when we can't import the table, e.g. we're inside a
            // comment.
            SAL_WARN("writerfilter.dmapper", "rRowSeq[0][0] is an empty reference");
            return false;
        }

        uno::Reference<text::XTextRangeCompare> xTextRangeCompare(rRowSeq[0][0]->getText(), uno::UNO_QUERY);
        try
        {
            for (sal_Int32 nCell = 0; nCell < rRowSeq.getLength(); ++nCell)
                // See SwXText::Impl::ConvertCell(), we need to compare the start of
                // the start and the end of the end. However for our text ranges, only
                // the starts are set, so compareRegionStarts() does what we need.
                if (xTextRangeCompare->compareRegionStarts(rRowSeq[nCell][0], rRowSeq[nCell][1]) != 0)
                    return false;
        }
        catch (const lang::IllegalArgumentException& e)
        {
            SAL_WARN( "writerfilter.dmapper", "compareRegionStarts() failed: " << e);
            return false;
        }
        return true;
    }

    css::uno::Sequence<css::beans::PropertyValues> DomainMapperTableHandler::endTableGetRowProperties()
    {
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement("getRowProperties");
    #endif

        css::uno::Sequence<css::beans::PropertyValues> aRowProperties( m_aRowProperties.size() );
        PropertyMapVector1::const_iterator aRowIter = m_aRowProperties.begin();
        PropertyMapVector1::const_iterator aRowIterEnd = m_aRowProperties.end();
        sal_Int32 nRow = 0;
        while( aRowIter != aRowIterEnd )
        {
    #ifdef DEBUG_WRITERFILTER
            TagLogger::getInstance().startElement("rowProps.row");
    #endif
            if( aRowIter->get() )
            {
                //set default to 'break across pages"
                (*aRowIter)->Insert( PROP_IS_SPLIT_ALLOWED, uno::makeAny(true ), false );
                // tblHeader is only our property, remove before the property map hits UNO
                (*aRowIter)->Erase(PROP_TBL_HEADER);

                if (lcl_hideMarks(m_aCellProperties[nRow]) && lcl_emptyRow(m_aTableRanges, nRow))
                {
                    // We have CellHideMark on all cells, and also all cells are empty:
                    // Force the row height to be exactly as specified, and not just as the minimum suggestion.
                    (*aRowIter)->Insert(PROP_SIZE_TYPE, uno::makeAny(text::SizeType::FIX));
                }

                aRowProperties[nRow] = (*aRowIter)->GetPropertyValues();
    #ifdef DEBUG_WRITERFILTER
                (*aRowIter)->dumpXml();
                lcl_DumpPropertyValues(aRowProperties[nRow]);
    #endif
            }
            ++nRow;
            ++aRowIter;
    #ifdef DEBUG_WRITERFILTER
            TagLogger::getInstance().endElement();
    #endif
        }

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

        return aRowProperties;
    }

    // Apply paragraph property to each paragraph within a cell.
    static void lcl_ApplyCellParaProps(uno::Reference<table::XCell> const& xCell,
            const uno::Any& rBottomMargin)
    {
        uno::Reference<container::XEnumerationAccess> xEnumerationAccess(xCell, uno::UNO_QUERY);
        uno::Reference<container::XEnumeration> xEnumeration = xEnumerationAccess->createEnumeration();
        while (xEnumeration->hasMoreElements())
        {
            uno::Reference<beans::XPropertySet> xParagraph(xEnumeration->nextElement(), uno::UNO_QUERY);
            uno::Reference<beans::XPropertyState> xPropertyState(xParagraph, uno::UNO_QUERY);
            // Don't apply in case direct formatting is already present.
            // TODO: probably paragraph style has priority over table style here.
            if (xPropertyState.is() && xPropertyState->getPropertyState("ParaBottomMargin") == beans::PropertyState_DEFAULT_VALUE)
                xParagraph->setPropertyValue("ParaBottomMargin", rBottomMargin);
        }
    }

    void DomainMapperTableHandler::endTable(unsigned int nestedTableLevel, bool bTableStartsAtCellStart)
    {
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement("tablehandler.endTable");
    #endif

        // If we want to make this table a floating one.
        std::vector<beans::PropertyValue> aFrameProperties = comphelper::sequenceToContainer<std::vector<beans::PropertyValue> >
                (m_rDMapper_Impl.getTableManager().getCurrentTablePosition());
        TableInfo aTableInfo;
        aTableInfo.nNestLevel = nestedTableLevel;
        aTableInfo.pTableStyle = endTableGetTableStyle(aTableInfo, aFrameProperties);
        //  expands to uno::Sequence< Sequence< beans::PropertyValues > >

        std::vector<HorizontallyMergedCell> aMerges;
        CellPropertyValuesSeq_t aCellProperties = endTableGetCellProperties(aTableInfo, aMerges);

        css::uno::Sequence<css::beans::PropertyValues> aRowProperties = endTableGetRowProperties();

    #ifdef DEBUG_WRITERFILTER
        lcl_DumpPropertyValueSeq(aRowProperties);
    #endif

        if (!m_aTableRanges.empty())
        {
            uno::Reference<text::XTextRange> xStart;
            uno::Reference<text::XTextRange> xEnd;

            bool bFloating = !aFrameProperties.empty();
            // Additional checks: if we can do this.
            if (bFloating && m_aTableRanges[0].getLength() > 0 && m_aTableRanges[0][0].getLength() > 0)
            {
                xStart = m_aTableRanges[0][0][0];
                uno::Sequence< uno::Sequence< uno::Reference<text::XTextRange> > >& rLastRow = m_aTableRanges[m_aTableRanges.size() - 1];
                if (rLastRow.getLength())
                {
                    uno::Sequence< uno::Reference<text::XTextRange> >& rLastCell = rLastRow[rLastRow.getLength() - 1];
                    xEnd = rLastCell[1];
                }
            }
            uno::Reference<text::XTextTable> xTable;
            try
            {
                if (m_xText.is())
                {
                    xTable = m_xText->convertToTable(comphelper::containerToSequence(m_aTableRanges), aCellProperties, aRowProperties, aTableInfo.aTableProperties);

                    if (xTable.is())
                    {
                        if (!aMerges.empty())
                        {
                            static const std::vector<OUStringLiteral> aBorderNames
                                = { "TopBorder", "LeftBorder", "BottomBorder", "RightBorder" };

                            // Perform horizontal merges in reverse order, so the fact that merging changes the position of cells won't cause a problem for us.
                            for (std::vector<HorizontallyMergedCell>::reverse_iterator it = aMerges.rbegin(); it != aMerges.rend(); ++it)
                            {
                                uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY_THROW);
                                uno::Reference<beans::XPropertySet> xFirstCell(
                                    xCellRange->getCellByPosition(it->m_nFirstCol, it->m_nFirstRow),
                                    uno::UNO_QUERY_THROW);
                                OUString aFirst
                                    = xFirstCell->getPropertyValue("CellName").get<OUString>();
                                // tdf#105852: Only try to merge if m_nLastCol is set (i.e. there were some merge continuation cells)
                                if (it->m_nLastCol != -1)
                                {
                                    // Save border properties of the first cell
                                    // before merge.
                                    table::BorderLine2 aBorderValues[4];
                                    for (size_t i = 0; i < aBorderNames.size(); ++i)
                                        xFirstCell->getPropertyValue(aBorderNames[i])
                                            >>= aBorderValues[i];

                                    uno::Reference<beans::XPropertySet> xLastCell(
                                        xCellRange->getCellByPosition(it->m_nLastCol, it->m_nLastRow),
                                        uno::UNO_QUERY_THROW);
                                    OUString aLast
                                        = xLastCell->getPropertyValue("CellName").get<OUString>();

                                    uno::Reference<text::XTextTableCursor> xCursor = xTable->createCursorByCellName(aFirst);
                                    xCursor->gotoCellByName(aLast, true);

                                    xCursor->mergeRange();

                                    // Handle conflicting properties: mergeRange()
                                    // takes the last cell, Word takes the first
                                    // cell.
                                    for (size_t i = 0; i < aBorderNames.size(); ++i)
                                    {
                                        if (aBorderValues[i].LineStyle != table::BorderLineStyle::NONE)
                                            xFirstCell->setPropertyValue(
                                                aBorderNames[i], uno::makeAny(aBorderValues[i]));
                                    }
                                }
                            }
                        }
                    }

                    // OOXML table style may container paragraph properties, apply these now.
                    for (int i = 0; i < aTableInfo.aTableProperties.getLength(); ++i)
                    {
                        if (aTableInfo.aTableProperties[i].Name == "ParaBottomMargin")
                        {
                            uno::Reference<table::XCellRange> xCellRange(xTable, uno::UNO_QUERY);
                            uno::Any aBottomMargin = aTableInfo.aTableProperties[i].Value;
                            sal_Int32 nRows = aCellProperties.getLength();
                            for (sal_Int32 nRow = 0; nRow < nRows; ++nRow)
                            {
                                const uno::Sequence< beans::PropertyValues > aCurrentRow = aCellProperties[nRow];
                                sal_Int32 nCells = aCurrentRow.getLength();
                                for (sal_Int32 nCell = 0; nCell < nCells; ++nCell)
                                    lcl_ApplyCellParaProps(xCellRange->getCellByPosition(nCell, nRow), aBottomMargin);
                            }
                            break;
                        }
                    }
                }
            }
            catch ( const lang::IllegalArgumentException &e )
            {
                SAL_INFO("writerfilter.dmapper",
                        "Conversion to table error: " << e);
    #ifdef DEBUG_WRITERFILTER
                TagLogger::getInstance().chars(std::string("failed to import table!"));
    #endif
            }
            catch ( const uno::Exception &e )
            {
                SAL_INFO("writerfilter.dmapper",
                        "Exception during table creation: " << e);
            }

            // If we have a table with a start and an end position, we should make it a floating one.
            // Unless the table had a foot or endnote, as Writer doesn't support those in TextFrames.
            if (xTable.is() && xStart.is() && xEnd.is() && !m_bHadFootOrEndnote)
            {
                uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY);
                bool bIsRelative = false;
                xTableProperties->getPropertyValue("IsWidthRelative") >>= bIsRelative;
                if (!bIsRelative)
                {
                    beans::PropertyValue aValue;
                    aValue.Name = "Width";
                    aValue.Value = xTableProperties->getPropertyValue("Width");
                    aFrameProperties.push_back(aValue);
                }
                else
                {
                    beans::PropertyValue aValue;
                    aValue.Name = "FrameWidthPercent";
                    aValue.Value = xTableProperties->getPropertyValue("RelativeWidth");
                    aFrameProperties.push_back(aValue);

                    // Applying the relative width to the frame, needs to have the table width to be 100% of the frame width
                    xTableProperties->setPropertyValue("RelativeWidth", uno::makeAny(sal_Int16(100)));
                }

                // A non-zero left margin would move the table out of the frame, move the frame itself instead.
                xTableProperties->setPropertyValue("LeftMargin", uno::makeAny(sal_Int32(0)));

                // In case the document ends with a table, we're called after
                // SectionPropertyMap::CloseSectionGroup(), so we'll have no idea
                // about the text area width, nor can fix this by delaying the text
                // frame conversion: just do it here.
                // Also, when the anchor is within a table, then do it here as well,
                // as xStart/xEnd would not point to the start/end at conversion
                // time anyway.
                // Next exception: it's pointless to delay the conversion if the
                // table is not in the body text.
                sal_Int32 nTableWidth = 0;
                m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH, nTableWidth);
                sal_Int32 nTableWidthType = text::SizeType::FIX;
                m_aTableProperties->getValue(TablePropertyMap::TABLE_WIDTH_TYPE, nTableWidthType);
                if (m_rDMapper_Impl.GetSectionContext() && nestedTableLevel <= 1 && !m_rDMapper_Impl.IsInHeaderFooter())
                    m_rDMapper_Impl.m_aPendingFloatingTables.emplace_back(xStart, xEnd, comphelper::containerToSequence(aFrameProperties), nTableWidth, nTableWidthType);
                else
                {
                    // m_xText points to the body text, get the current xText from m_rDMapper_Impl, in case e.g. we would be in a header.
                    uno::Reference<text::XTextAppendAndConvert> xTextAppendAndConvert(m_rDMapper_Impl.GetTopTextAppend(), uno::UNO_QUERY);
                    // Only execute the conversion if the table is not anchored at
                    // the start of an outer table cell, that's not yet
                    // implemented.
                    if (xTextAppendAndConvert.is() && !bTableStartsAtCellStart)
                        xTextAppendAndConvert->convertToTextFrame(xStart, xEnd, comphelper::containerToSequence(aFrameProperties));
                }
            }

            // We're right after a table conversion.
            m_rDMapper_Impl.m_bConvertedTable = true;
        }

        m_aTableProperties.clear();
        m_aCellProperties.clear();
        m_aRowProperties.clear();
        m_bHadFootOrEndnote = false;

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

    void DomainMapperTableHandler::startRow(const TablePropertyMapPtr& pProps)
    {
        m_aRowProperties.push_back( pProps.get() );
        m_aCellProperties.emplace_back( );

    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement("table.row");
        if (pProps != nullptr)
            pProps->dumpXml();
    #endif

        m_aRowRanges.clear();
    }

    void DomainMapperTableHandler::endRow()
    {
        m_aTableRanges.push_back(comphelper::containerToSequence(m_aRowRanges));
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().endElement();
    #endif
    }

    void DomainMapperTableHandler::startCell(const css::uno::Reference< css::text::XTextRange > & start,
                                             const TablePropertyMapPtr& pProps )
    {
        sal_uInt32 nRow = m_aRowProperties.size();
        if ( pProps.get( ) )
            m_aCellProperties[nRow - 1].push_back( pProps.get() );
        else
        {
            // Adding an empty cell properties map to be able to get
            // the table defaults properties
            TablePropertyMapPtr pEmptyProps( new TablePropertyMap( ) );
            m_aCellProperties[nRow - 1].push_back( pEmptyProps.get() );
        }

    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement("table.cell");
        TagLogger::getInstance().startElement("table.cell.start");
        TagLogger::getInstance().chars(XTextRangeToString(start));
        TagLogger::getInstance().endElement();
        if (pProps.get())
            pProps->printProperties();
    #endif

        //add a new 'row' of properties
        m_aCellRange.clear();
        uno::Reference<text::XTextRange> xStart;
        if (start.get())
            xStart = start->getStart();
        m_aCellRange.push_back(xStart);
    }

    void DomainMapperTableHandler::endCell(const css::uno::Reference< css::text::XTextRange > & end)
    {
    #ifdef DEBUG_WRITERFILTER
        TagLogger::getInstance().startElement("table.cell.end");
        TagLogger::getInstance().chars(XTextRangeToString(end));
        TagLogger::getInstance().endElement();
        TagLogger::getInstance().endElement();
    #endif

        uno::Reference<text::XTextRange> xEnd;
        if (end.get())
            xEnd = end->getEnd();
        m_aCellRange.push_back(xEnd);
        m_aRowRanges.push_back(comphelper::containerToSequence(m_aCellRange));
    }

    void DomainMapperTableHandler::setHadFootOrEndnote(bool bHadFootOrEndnote)
    {
        m_bHadFootOrEndnote = bHadFootOrEndnote;
    }

    DomainMapper_Impl& DomainMapperTableHandler::getDomainMapperImpl()
    {
        return m_rDMapper_Impl;
    }

    }}

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

↑ V547 Expression '!bIsRelative' is always true.

↑ V547 Expression '!bRowCanSplit' is always false.