/* -*- 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 <sal/config.h>
 
#include <string_view>
 
#include "PropertyMap.hxx"
#include "TagLogger.hxx"
#include <ooxml/resourceids.hxx>
#include "DomainMapper_Impl.hxx"
#include "ConversionHelper.hxx"
#include <editeng/boxitem.hxx>
#include <i18nutil/paper.hxx>
#include <osl/diagnose.h>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/XMultiPropertySet.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/table/BorderLine2.hpp>
#include <com/sun/star/container/XEnumeration.hpp>
#include <com/sun/star/container/XEnumerationAccess.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/style/BreakType.hpp>
#include <com/sun/star/style/PageStyleLayout.hpp>
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/table/ShadowFormat.hpp>
#include <com/sun/star/text/RelOrientation.hpp>
#include <com/sun/star/text/HoriOrientation.hpp>
#include <com/sun/star/text/HorizontalAdjust.hpp>
#include <com/sun/star/text/SizeType.hpp>
#include <com/sun/star/text/VertOrientation.hpp>
#include <com/sun/star/text/WritingMode.hpp>
#include <com/sun/star/text/WritingMode2.hpp>
#include <com/sun/star/text/XRedline.hpp>
#include <com/sun/star/text/XTextColumns.hpp>
#include <com/sun/star/text/XText.hpp>
#include <com/sun/star/text/XTextFrame.hpp>
#include <com/sun/star/text/XTextTablesSupplier.hpp>
#include <com/sun/star/text/TextGridMode.hpp>
#include <com/sun/star/text/XTextCopy.hpp>
#include <com/sun/star/style/VerticalAlignment.hpp>
#include <comphelper/sequence.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/diagnose_ex.hxx>
#include "PropertyMapHelper.hxx"
#include <o3tl/sorted_vector.hxx>
#include <o3tl/unit_conversion.hxx>
#include <unosection.hxx>
#include <unotxdoc.hxx>
#include <unoxstyle.hxx>
#include <unostyle.hxx>
#include <unotext.hxx>
#include <unotextcursor.hxx>
#include <unotbl.hxx>
#include <utility>
 
#include <frozen/bits/defines.h>
#include <frozen/bits/elsa_std.h>
#include <frozen/unordered_set.h>
 
#include <unoprnms.hxx>
 
using namespace com::sun::star;
 
namespace writerfilter::dmapper {
 
uno::Sequence< beans::PropertyValue > PropertyMap::GetPropertyValues( bool bCharGrabBag )
{
    using comphelper::makePropertyValue;
 
    if ( !m_aValues.empty() || m_vMap.empty() )
        return comphelper::containerToSequence( m_aValues );
 
    size_t nCharGrabBag = 0;
    size_t nParaGrabBag = 0;
    size_t nCellGrabBag = 0;
    size_t nRowGrabBag  = 0;
 
    const PropValue* pParaStyleProp = nullptr;
    const PropValue* pCharStyleProp = nullptr;
    const PropValue* pNumRuleProp   = nullptr;
 
    m_aValues.reserve( m_vMap.size() );
    for ( const auto& rPropPair : m_vMap )
    {
        if ( rPropPair.second.getGrabBagType() == CHAR_GRAB_BAG )
            nCharGrabBag++;
        else if ( rPropPair.second.getGrabBagType() == PARA_GRAB_BAG )
            nParaGrabBag++;
        else if ( rPropPair.second.getGrabBagType() == CELL_GRAB_BAG )
            nCellGrabBag++;
        else if ( rPropPair.first == PROP_CELL_INTEROP_GRAB_BAG )
        {
            uno::Sequence< beans::PropertyValue > aSeq;
            rPropPair.second.getValue() >>= aSeq;
            nCellGrabBag += aSeq.getLength();
        }
        else if ( rPropPair.second.getGrabBagType() == ROW_GRAB_BAG )
            nRowGrabBag++;
 
        if ( rPropPair.first == PROP_PARA_STYLE_NAME ) pParaStyleProp = &rPropPair.second;
        if ( rPropPair.first == PROP_CHAR_STYLE_NAME ) pCharStyleProp = &rPropPair.second;
        if ( rPropPair.first == PROP_NUMBERING_RULES ) pNumRuleProp   = &rPropPair.second;
    }
 
    // Style names have to be the first elements within the property sequence
    // otherwise they will overwrite 'hard' attributes
    if ( pParaStyleProp != nullptr )
        m_aValues.push_back( makePropertyValue( getPropertyName( PROP_PARA_STYLE_NAME ), pParaStyleProp->getValue() ) );
    if ( pCharStyleProp != nullptr )
        m_aValues.push_back( makePropertyValue( getPropertyName( PROP_CHAR_STYLE_NAME ), pCharStyleProp->getValue() ) );
    if ( pNumRuleProp != nullptr )
        m_aValues.push_back( makePropertyValue(getPropertyName( PROP_NUMBERING_RULES ), pNumRuleProp->getValue() ) );
 
    // If there are any grab bag properties, we need one slot for them.
    uno::Sequence< beans::PropertyValue > aCharGrabBagValues( nCharGrabBag );
    uno::Sequence< beans::PropertyValue > aParaGrabBagValues( nParaGrabBag );
    uno::Sequence< beans::PropertyValue > aCellGrabBagValues( nCellGrabBag );
    uno::Sequence< beans::PropertyValue > aRowGrabBagValues ( nRowGrabBag );
    beans::PropertyValue* pCharGrabBagValues = aCharGrabBagValues.getArray();
    beans::PropertyValue* pParaGrabBagValues = aParaGrabBagValues.getArray();
    beans::PropertyValue* pCellGrabBagValues = aCellGrabBagValues.getArray();
    beans::PropertyValue* pRowGrabBagValues  = aRowGrabBagValues.getArray();
    // Record index for the next property to be added in each grab bag.
    sal_Int32 nRowGrabBagValue  = 0;
    sal_Int32 nCellGrabBagValue = 0;
    sal_Int32 nParaGrabBagValue = 0;
    sal_Int32 nCharGrabBagValue = 0;
 
    for ( const auto& rPropPair : m_vMap )
    {
        if ( rPropPair.first != PROP_PARA_STYLE_NAME &&
             rPropPair.first != PROP_CHAR_STYLE_NAME &&
             rPropPair.first != PROP_NUMBERING_RULES )
        {
            if ( rPropPair.second.getGrabBagType() == CHAR_GRAB_BAG )
            {
                if ( bCharGrabBag )
                {
                    pCharGrabBagValues[nCharGrabBagValue].Name  = getPropertyName( rPropPair.first );
                    pCharGrabBagValues[nCharGrabBagValue].Value = rPropPair.second.getValue();
                    ++nCharGrabBagValue;
                }
            }
            else if ( rPropPair.second.getGrabBagType() == PARA_GRAB_BAG )
            {
                pParaGrabBagValues[nParaGrabBagValue].Name  = getPropertyName( rPropPair.first );
                pParaGrabBagValues[nParaGrabBagValue].Value = rPropPair.second.getValue();
                ++nParaGrabBagValue;
            }
            else if ( rPropPair.second.getGrabBagType() == CELL_GRAB_BAG )
            {
                pCellGrabBagValues[nCellGrabBagValue].Name  = getPropertyName( rPropPair.first );
                pCellGrabBagValues[nCellGrabBagValue].Value = rPropPair.second.getValue();
                ++nCellGrabBagValue;
            }
            else if ( rPropPair.second.getGrabBagType() == ROW_GRAB_BAG )
            {
                pRowGrabBagValues[nRowGrabBagValue].Name  = getPropertyName( rPropPair.first );
                pRowGrabBagValues[nRowGrabBagValue].Value = rPropPair.second.getValue();
                ++nRowGrabBagValue;
            }
            else if ( rPropPair.first == PROP_CELL_INTEROP_GRAB_BAG )
            {
                uno::Sequence< beans::PropertyValue > aSeq;
                rPropPair.second.getValue() >>= aSeq;
                std::copy(std::cbegin(aSeq), std::cend(aSeq), pCellGrabBagValues + nCellGrabBagValue);
                nCellGrabBagValue += aSeq.getLength();
            }
            else
            {
                m_aValues.push_back( makePropertyValue( getPropertyName( rPropPair.first ), rPropPair.second.getValue() ) );
            }
        }
    }
 
    if ( nCharGrabBag && bCharGrabBag )
        m_aValues.push_back( makePropertyValue( u"CharInteropGrabBag"_ustr, uno::Any( aCharGrabBagValues ) ) );
 
    if ( nParaGrabBag )
        m_aValues.push_back( makePropertyValue( u"ParaInteropGrabBag"_ustr, uno::Any( aParaGrabBagValues ) ) );
 
    if ( nCellGrabBag )
        m_aValues.push_back( makePropertyValue( u"CellInteropGrabBag"_ustr, uno::Any( aCellGrabBagValues ) ) );
 
    if ( nRowGrabBag )
        m_aValues.push_back( makePropertyValue( u"RowInteropGrabBag"_ustr, uno::Any( aRowGrabBagValues ) ) );
 
    return comphelper::containerToSequence( m_aValues );
}
 
std::vector< PropertyIds > PropertyMap::GetPropertyIds()
{
    std::vector< PropertyIds > aRet;
    for ( const auto& rPropPair : m_vMap )
        aRet.push_back( rPropPair.first );
    return aRet;
}
 
#ifdef DBG_UTIL
static void lcl_AnyToTag( const uno::Any& rAny )
{
    try {
        sal_Int32 aInt = 0;
        if ( rAny >>= aInt )
        {
            TagLogger::getInstance().attribute( "value", rAny );
        }
        else
        {
            TagLogger::getInstance().attribute( "unsignedValue", 0 );
        }
 
        sal_uInt32 auInt = 0;
        rAny >>= auInt;
        TagLogger::getInstance().attribute( "unsignedValue", auInt );
 
        float aFloat = 0.0f;
        if ( rAny >>= aFloat )
        {
            TagLogger::getInstance().attribute( "floatValue", rAny );
        }
        else
        {
            TagLogger::getInstance().attribute( "unsignedValue", 0 );
        }
 
        OUString aStr;
        rAny >>= aStr;
        TagLogger::getInstance().attribute( "stringValue", aStr );
    }
    catch ( ... )
    {
    }
}
#endif
 
void PropertyMap::Insert( PropertyIds eId, const uno::Any& rAny, bool bOverwrite, GrabBagType i_GrabBagType, bool bDocDefault )
{
#ifdef DBG_UTIL
    const OUString& rInsert = getPropertyName(eId);
 
    TagLogger::getInstance().startElement("propertyMap.insert");
    TagLogger::getInstance().attribute("name", rInsert);
    lcl_AnyToTag(rAny);
    TagLogger::getInstance().endElement();
#endif
 
    if ( !bOverwrite )
        m_vMap.insert(std::make_pair(eId, PropValue(rAny, i_GrabBagType, bDocDefault)));
    else
        m_vMap[eId] = PropValue(rAny, i_GrabBagType);
 
    Invalidate();
}
 
void PropertyMap::Erase( PropertyIds eId )
{
    // Safe call to erase, it throws no exceptions, even if eId is not in m_vMap
    m_vMap.erase(eId);
 
    Invalidate();
}
 
std::optional< PropertyMap::Property > PropertyMap::getProperty( PropertyIds eId ) const
{
    std::map< PropertyIds, PropValue >::const_iterator aIter = m_vMap.find( eId );
    if ( aIter == m_vMap.end() )
        return std::optional<Property>();
    else
        return std::make_pair( eId, aIter->second.getValue() );
}
 
bool PropertyMap::isSet( PropertyIds eId) const
{
    return m_vMap.contains( eId );
}
 
bool PropertyMap::isDocDefault( PropertyIds eId ) const
{
    std::map< PropertyIds, PropValue >::const_iterator aIter = m_vMap.find( eId );
    if ( aIter == m_vMap.end() )
        return false;
    else
        return aIter->second.getIsDocDefault();
}
 
#ifdef DBG_UTIL
void PropertyMap::dumpXml() const
{
    TagLogger::getInstance().startElement( "PropertyMap" );
 
    for ( const auto& rPropPair : m_vMap )
    {
        TagLogger::getInstance().startElement( "property" );
 
        TagLogger::getInstance().attribute( "name", getPropertyName( rPropPair.first ) );
 
        switch ( rPropPair.first )
        {
            case PROP_TABLE_COLUMN_SEPARATORS:
                lcl_DumpTableColumnSeparators( rPropPair.second.getValue() );
                break;
            default:
            {
                try
                {
                    sal_Int32 aInt = 0;
                    rPropPair.second.getValue() >>= aInt;
                    TagLogger::getInstance().attribute( "value", aInt );
 
                    sal_uInt32 auInt = 0;
                    rPropPair.second.getValue() >>= auInt;
                    TagLogger::getInstance().attribute( "unsignedValue", auInt );
 
                    float aFloat = 0.0;
                    rPropPair.second.getValue() >>= aFloat;
                    TagLogger::getInstance().attribute( "floatValue", aFloat );
 
                    rPropPair.second.getValue() >>= auInt;
                    TagLogger::getInstance().attribute( "stringValue", std::u16string_view() );
                }
                catch ( ... )
                {
                }
            }
            break;
        }
 
        TagLogger::getInstance().endElement();
    }
 
    TagLogger::getInstance().endElement();
}
#endif
 
void PropertyMap::InsertProps( const PropertyMapPtr& rMap, const bool bOverwrite )
{
    if ( !rMap )
        return;
 
    for ( const auto& rPropPair : rMap->m_vMap )
    {
        if ( bOverwrite || !m_vMap.count(rPropPair.first) )
        {
            if ( !bOverwrite && !rPropPair.second.getIsDocDefault() )
                m_vMap.insert(std::make_pair(rPropPair.first, PropValue(rPropPair.second.getValue(), rPropPair.second.getGrabBagType(), true)));
            else
                m_vMap[rPropPair.first] = rPropPair.second;
        }
    }
 
    insertTableProperties( rMap.get(), bOverwrite );
 
    Invalidate();
}
 
void PropertyMap::insertTableProperties( const PropertyMap*, const bool )
{
#ifdef DBG_UTIL
    TagLogger::getInstance().element( "PropertyMap.insertTableProperties" );
#endif
}
 
void PropertyMap::printProperties()
{
#ifdef DBG_UTIL
    TagLogger::getInstance().startElement( "properties" );
 
    for ( const auto& rPropPair : m_vMap )
    {
        SAL_INFO( "writerfilter", getPropertyName( rPropPair.first ) );
 
        table::BorderLine2 aLine;
        sal_Int32 nColor;
        if ( rPropPair.second.getValue() >>= aLine )
        {
            TagLogger::getInstance().startElement( "borderline" );
            TagLogger::getInstance().attribute( "color", aLine.Color );
            TagLogger::getInstance().attribute( "inner", aLine.InnerLineWidth );
            TagLogger::getInstance().attribute( "outer", aLine.OuterLineWidth );
            TagLogger::getInstance().endElement();
        }
        else if ( rPropPair.second.getValue() >>= nColor )
        {
            TagLogger::getInstance().startElement( "color" );
            TagLogger::getInstance().attribute( "number", nColor );
            TagLogger::getInstance().endElement();
        }
    }
 
    TagLogger::getInstance().endElement();
#else
    (void) this; // avoid loplugin:staticmethods
#endif
}
 
SectionPropertyMap::SectionPropertyMap( bool bIsFirstSection )
    : m_bIsFirstSection( bIsFirstSection )
    , m_eBorderApply( BorderApply::ToAllInSection )
    , m_eBorderOffsetFrom( BorderOffsetFrom::Text )
    , m_bTitlePage( false )
    , m_nColumnCount( 0 )
    , m_nColumnDistance( 1249 )
    , m_bSeparatorLineIsOn( false )
    , m_bEvenlySpaced( false )
    , m_nPageNumber( -1 )
    , m_nPageNumberType( -1 )
    , m_nBreakType( -1 )
    , m_nLeftMargin( o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100) )
    , m_nRightMargin( o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100) )
    , m_nGutterMargin(0)
    , m_nTopMargin( o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100) )
    , m_nBottomMargin( o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100) )
    , m_nHeaderTop( o3tl::convert(0.5, o3tl::Length::in, o3tl::Length::mm100) )
    , m_nHeaderBottom( o3tl::convert(0.5, o3tl::Length::in, o3tl::Length::mm100) )
    , m_nGridType( 0 )
    , m_nGridLinePitch( 1 )
    , m_nDxtCharSpace( 0 )
    , m_bGridSnapToChars( true )
    , m_nLnnMod( 0 )
    , m_nLnc(NS_ooxml::LN_Value_ST_LineNumberRestart_newPage)
    , m_ndxaLnn( 0 )
    , m_nLnnMin( 0 )
    , m_nPaperSourceFirst( 0 )
    , m_nPaperSourceOther( 0 )
    , m_bDynamicHeightTop( true )
    , m_bDynamicHeightBottom( true )
{
#ifdef DBG_UTIL
    static sal_Int32 nNumber = 0;
    m_nDebugSectionNumber = nNumber++;
#endif
 
    for ( sal_Int32 nBorder = 0; nBorder < 4; ++nBorder )
    {
        m_nBorderDistances[nBorder] = -1;
        m_bBorderShadows[nBorder] = false;
    }
    // todo: set defaults in ApplyPropertiesToPageStyles
    // initialize defaults
    PaperInfo aLetter( PAPER_LETTER );
    // page height, 1/100mm
    Insert( PROP_HEIGHT, uno::Any( static_cast<sal_Int32>(aLetter.getHeight()) ) );
    // page width, 1/100mm
    Insert( PROP_WIDTH, uno::Any( static_cast<sal_Int32>(aLetter.getWidth()) ) );
    // page left margin, 1/100 mm
    Insert( PROP_LEFT_MARGIN, uno::Any( sal_Int32(o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100)) ) );
    // page right margin, 1/100 mm
    Insert( PROP_RIGHT_MARGIN, uno::Any( sal_Int32(o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100)) ) );
    // page top margin, 1/100 mm
    Insert( PROP_TOP_MARGIN, uno::Any( sal_Int32(o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100)) ) );
    // page bottom margin, 1/100 mm
    Insert( PROP_BOTTOM_MARGIN, uno::Any( sal_Int32(o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100)) ) );
    // page style layout
    Insert( PROP_PAGE_STYLE_LAYOUT, uno::Any( style::PageStyleLayout_ALL ) );
    uno::Any aFalse( uno::Any( false ) );
    Insert( PROP_GRID_DISPLAY, aFalse );
    Insert( PROP_GRID_PRINT, aFalse );
    Insert( PROP_GRID_MODE, uno::Any( text::TextGridMode::NONE ) );
 
    if ( m_bIsFirstSection )
    {
        m_sPageStyleName = getPropertyName(PROP_STANDARD);
    }
}
 
rtl::Reference<SwXPageStyle> SectionPropertyMap::GetPageStyle(DomainMapper_Impl& rDM_Impl)
{
    const rtl::Reference< SwXStyleFamily >& xPageStyles = rDM_Impl.GetPageStyles();
    rtl::Reference<SwXPageStyle> xReturnPageStyle;
    try
    {
        if (m_sPageStyleName.isEmpty() && xPageStyles.is())
        {
            assert( !rDM_Impl.IsInFootOrEndnote() && "Don't create useless page styles" );
 
            m_sPageStyleName = rDM_Impl.GetUnusedPageStyleName();
 
            m_aPageStyle = rDM_Impl.GetTextDocument()->createPageStyle();
            xPageStyles->insertStyleByName(m_sPageStyleName, m_aPageStyle);
        }
        else if (!m_aPageStyle.is() && xPageStyles.is())
        {
            m_aPageStyle = xPageStyles->getPageStyleByName(m_sPageStyleName);
        }
        xReturnPageStyle = m_aPageStyle;
    }
    catch (const uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION( "writerfilter" );
    }
 
    return xReturnPageStyle;
}
 
// removes the content - all the paragraphs of an input XText
void SectionPropertyMap::removeXTextContent(uno::Reference<text::XText> const& rxText)
{
    if (!rxText.is())
        return;
    rxText->setString(OUString());
    uno::Reference<text::XParagraphAppend> const xAppend(rxText, uno::UNO_QUERY_THROW);
    uno::Reference<lang::XComponent> const xParagraph(xAppend->finishParagraph(uno::Sequence<beans::PropertyValue>()), uno::UNO_QUERY_THROW);
    xParagraph->dispose();
}
 
/** Set the header/footer sharing as defined by titlePage and evenAndOdd flags
 *  in the document and clear the content of anything not written during the import.
 */
void SectionPropertyMap::setHeaderFooterProperties(DomainMapper_Impl& rDM_Impl)
{
    // do not alter header/footer during copy/paste
    if (!m_aPageStyle.is() || !rDM_Impl.IsNewDoc())
        return;
 
    bool bHasHeader = false;
    bool bHasFooter = false;
    bool bHeaderIsShared = false;
    bool bFooterIsShared = false;
    bool bFirstIsShared = false;
 
    const OUString& sHeaderIsOn = getPropertyName(PROP_HEADER_IS_ON);
    const OUString& sFooterIsOn = getPropertyName(PROP_FOOTER_IS_ON);
    const OUString& sHeaderIsShared = getPropertyName(PROP_HEADER_IS_SHARED);
    const OUString& sFooterIsShared = getPropertyName(PROP_FOOTER_IS_SHARED);
    const OUString& sFirstIsShared = getPropertyName(PROP_FIRST_IS_SHARED);
 
    m_aPageStyle->getPropertyValue(sHeaderIsOn) >>= bHasHeader;
    m_aPageStyle->getPropertyValue(sFooterIsOn) >>= bHasFooter;
    m_aPageStyle->getPropertyValue(sHeaderIsShared) >>= bHeaderIsShared;
    m_aPageStyle->getPropertyValue(sFooterIsShared) >>= bFooterIsShared;
    m_aPageStyle->getPropertyValue(sFirstIsShared) >>= bFirstIsShared;
 
    bool bEvenAndOdd = rDM_Impl.GetSettingsTable()->GetEvenAndOddHeaders();
 
    if (bHasHeader && !m_bLeftHeader && !bEvenAndOdd)
    {
        auto aAny = m_aPageStyle->getPropertyValue(getPropertyName(PROP_HEADER_TEXT_LEFT));
        uno::Reference<text::XText> xText(aAny, uno::UNO_QUERY);
        if (xText.is())
            SectionPropertyMap::removeXTextContent(xText);
    }
 
    if (bHasFooter && !m_bLeftFooter && !bEvenAndOdd)
    {
        auto aAny = m_aPageStyle->getPropertyValue(getPropertyName(PROP_FOOTER_TEXT_LEFT));
        uno::Reference<text::XText> xText(aAny, uno::UNO_QUERY);
        if (xText.is())
            SectionPropertyMap::removeXTextContent(xText);
    }
 
    if (bHasHeader && !m_bFirstHeader && !m_bTitlePage)
    {
        auto aAny = m_aPageStyle->getPropertyValue(getPropertyName(PROP_HEADER_TEXT_FIRST));
        uno::Reference<text::XText> xText(aAny, uno::UNO_QUERY);
        if (xText.is())
            SectionPropertyMap::removeXTextContent(xText);
    }
 
    if (bHasFooter && !m_bFirstFooter && !m_bTitlePage)
    {
        auto aAny = m_aPageStyle->getPropertyValue(getPropertyName(PROP_FOOTER_TEXT_FIRST));
        uno::Reference<text::XText> xText(aAny, uno::UNO_QUERY);
        if (xText.is())
            SectionPropertyMap::removeXTextContent(xText);
    }
 
    if ( bHeaderIsShared != !bEvenAndOdd )
        m_aPageStyle->setPropertyValue(sHeaderIsShared, uno::Any(!bEvenAndOdd));
    if ( bFooterIsShared != !bEvenAndOdd )
        m_aPageStyle->setPropertyValue(sFooterIsShared, uno::Any(!bEvenAndOdd));
    if ( bFirstIsShared != !m_bTitlePage )
        m_aPageStyle->setPropertyValue(sFirstIsShared, uno::Any(!m_bTitlePage));
 
    bool bHadFirstHeader = m_bHadFirstHeader && m_bTitlePage;
    if (bHasHeader && !bHadFirstHeader && !m_bHadLeftHeader && !m_bHadRightHeader)
    {
        m_aPageStyle->setPropertyValue(sHeaderIsOn, uno::Any(false));
    }
}
 
void SectionPropertyMap::SetBorder( BorderPosition ePos, sal_Int32 nLineDistance, const table::BorderLine2& rBorderLine, bool bShadow )
{
    m_oBorderLines[ePos]     = rBorderLine;
    m_nBorderDistances[ePos] = nLineDistance;
    m_bBorderShadows[ePos]   = bShadow;
}
 
void SectionPropertyMap::ApplyPaperSource(DomainMapper_Impl& rDM_Impl)
{
    // todo: negative spacing (from ww8par6.cxx)
    if (!m_sPageStyleName.isEmpty())
    {
        rtl::Reference<SwXPageStyle> xFirst = GetPageStyle(rDM_Impl);
        if ( xFirst.is() )
            try
            {
                //TODO: which of the two tray values needs to be set? first/other - the interfaces requires the name of the tray!
                xFirst->setPropertyValue(getPropertyName(PROP_PAPER_TRAY),
                                         uno::Any(m_nPaperSourceFirst));
            }
            catch (const uno::Exception&)
            {
                TOOLS_WARN_EXCEPTION("writerfilter", "Paper source not found");
            }
    }
}
 
void SectionPropertyMap::ApplyBorderToPageStyles( DomainMapper_Impl& rDM_Impl,
                                                  BorderApply /*eBorderApply*/, BorderOffsetFrom eOffsetFrom )
{
    /*
    page border applies to:
    nIntValue & 0x07 ->
    0 all pages in this section
    1 first page in this section
    2 all pages in this section but first
    3 whole document (all sections)
    nIntValue & 0x18 -> page border depth 0 - in front 1- in back
    nIntValue & 0xe0 ->
    page border offset from:
    0 offset from text
    1 offset from edge of page
    */
 
    rtl::Reference<SwXPageStyle> xFirst;
    // todo: negative spacing (from ww8par6.cxx)
    if (!m_sPageStyleName.isEmpty())
        xFirst = GetPageStyle(rDM_Impl);
 
    // has to be sorted like enum BorderPosition: l-r-t-b
    const PropertyIds aBorderIds[4] =
    {
        PROP_LEFT_BORDER,
        PROP_RIGHT_BORDER,
        PROP_TOP_BORDER,
        PROP_BOTTOM_BORDER
    };
 
    const PropertyIds aBorderDistanceIds[4] =
    {
        PROP_LEFT_BORDER_DISTANCE,
        PROP_RIGHT_BORDER_DISTANCE,
        PROP_TOP_BORDER_DISTANCE,
        PROP_BOTTOM_BORDER_DISTANCE
    };
 
    const PropertyIds aMarginIds[4] =
    {
        PROP_LEFT_MARGIN,
        PROP_RIGHT_MARGIN,
        PROP_TOP_MARGIN,
        PROP_BOTTOM_MARGIN
    };
 
    for ( sal_Int32 nBorder = 0; nBorder < 4; ++nBorder )
    {
        if ( m_oBorderLines[nBorder] )
        {
            const OUString & sBorderName = getPropertyName( aBorderIds[nBorder] );
            if ( xFirst.is() )
                xFirst->setPropertyValue( sBorderName, uno::Any( *m_oBorderLines[nBorder] ) );
        }
        if ( m_nBorderDistances[nBorder] >= 0 )
        {
            sal_uInt32 nLineWidth = 0;
            if ( m_oBorderLines[nBorder] )
                nLineWidth = m_oBorderLines[nBorder]->LineWidth;
            if ( xFirst.is() )
                SetBorderDistance( xFirst, aMarginIds[nBorder], aBorderDistanceIds[nBorder],
                    m_nBorderDistances[nBorder], eOffsetFrom, nLineWidth, rDM_Impl );
        }
    }
 
    if ( m_bBorderShadows[BORDER_RIGHT] )
    {
        table::ShadowFormat aFormat = getShadowFromBorder( *m_oBorderLines[BORDER_RIGHT] );
        if ( xFirst.is() )
            xFirst->setPropertyValue( getPropertyName( PROP_SHADOW_FORMAT ), uno::Any( aFormat ) );
    }
}
 
table::ShadowFormat PropertyMap::getShadowFromBorder( const table::BorderLine2& rBorder )
{
    // In Word UI, shadow is a boolean property, in OOXML, it's a boolean
    // property of each 4 border type, finally in Writer the border is a
    // property of the page style, with shadow location, distance and
    // color. See SwWW8ImplReader::SetShadow().
    table::ShadowFormat aFormat;
    aFormat.Color       = sal_Int32(COL_BLACK);
    aFormat.Location    = table::ShadowLocation_BOTTOM_RIGHT;
    aFormat.ShadowWidth = rBorder.LineWidth;
    return aFormat;
}
 
void SectionPropertyMap::SetBorderDistance( const rtl::Reference<SwXPageStyle>& xStyle,
                                            PropertyIds eMarginId,
                                            PropertyIds eDistId,
                                            sal_Int32 nDistance,
                                            BorderOffsetFrom eOffsetFrom,
                                            sal_uInt32 nLineWidth,
                                            DomainMapper_Impl& rDM_Impl )
{
    if (!xStyle.is())
        return;
    const OUString & sMarginName = getPropertyName( eMarginId );
    const OUString & sBorderDistanceName = getPropertyName( eDistId );
    uno::Any aMargin = xStyle->getPropertyValue( sMarginName );
    sal_Int32 nMargin = 0;
    aMargin >>= nMargin;
    editeng::BorderDistanceFromWord(eOffsetFrom == BorderOffsetFrom::Edge, nMargin, nDistance,
                                    nLineWidth);
 
    if (eOffsetFrom == BorderOffsetFrom::Edge)
    {
        uno::Any aGutterMargin = xStyle->getPropertyValue( u"GutterMargin"_ustr );
        sal_Int32 nGutterMargin = 0;
        aGutterMargin >>= nGutterMargin;
 
        if (eMarginId == PROP_LEFT_MARGIN && !rDM_Impl.GetSettingsTable()->GetGutterAtTop())
        {
            nMargin -= nGutterMargin;
            nDistance += nGutterMargin;
        }
 
        if (eMarginId == PROP_TOP_MARGIN && rDM_Impl.GetSettingsTable()->GetGutterAtTop())
        {
            nMargin -= nGutterMargin;
            nDistance += nGutterMargin;
        }
    }
 
    // Change the margins with the border distance
    uno::Sequence<OUString> aProperties { sMarginName, sBorderDistanceName };
    uno::Sequence<uno::Any> aValues { uno::Any( nMargin ), uno::Any( nDistance ) };
    xStyle->setPropertyValues( aProperties, aValues );
}
 
void SectionPropertyMap::DontBalanceTextColumns()
{
    try
    {
        if ( m_xColumnContainer.is() )
            m_xColumnContainer->setPropertyValue( u"DontBalanceTextColumns"_ustr, uno::Any( true ) );
    }
    catch ( const uno::Exception& )
    {
        TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::DontBalanceTextColumns" );
    }
}
 
void SectionPropertyMap::ApplySectionProperties( const rtl::Reference< SwXTextSection >& xSection, DomainMapper_Impl& rDM_Impl )
{
    try
    {
        if ( xSection.is() )
        {
            std::optional< PropertyMap::Property > pProp = getProperty( PROP_WRITING_MODE );
            if ( pProp )
                xSection->setPropertyValue( u"WritingMode"_ustr, pProp->second );
 
            if (rDM_Impl.GetSettingsTable()->GetEndnoteIsCollectAtSectionEnd())
            {
                xSection->setPropertyValue(UNO_NAME_ENDNOTE_IS_COLLECT_AT_TEXT_END, uno::Any(true));
            }
        }
    }
    catch ( uno::Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("writerfilter", "Exception in SectionPropertyMap::ApplySectionProperties");
    }
}
 
void SectionPropertyMap::ApplyProtectionProperties( rtl::Reference< SwXTextSection >& xSection, DomainMapper_Impl& rDM_Impl )
{
    try
    {
        // Word implements section protection differently than LO.
        // PROP_IS_PROTECTED only applies if global setting GetProtectForm is enabled.
        bool bIsProtected = rDM_Impl.GetSettingsTable()->GetProtectForm();
        if ( bIsProtected )
        {
            // If form protection is enabled then section protection is enabled, unless explicitly disabled
            if ( isSet(PROP_IS_PROTECTED) )
                getProperty(PROP_IS_PROTECTED)->second >>= bIsProtected;
            if ( !xSection.is() )
                xSection = rDM_Impl.appendTextSectionAfter( m_xStartingRange );
            if ( xSection.is() )
                xSection->setPropertyValue( getPropertyName(PROP_IS_PROTECTED), uno::Any(bIsProtected) );
        }
    }
    catch ( uno::Exception& )
    {
        DBG_UNHANDLED_EXCEPTION("writerfilter", "ApplyProtectionProperties failed setting PROP_IS_PROTECTED");
    }
}
 
uno::Reference< text::XTextColumns > SectionPropertyMap::ApplyColumnProperties( const uno::Reference< beans::XPropertySet >& xColumnContainer,
                                                                                DomainMapper_Impl& rDM_Impl )
{
    uno::Reference< text::XTextColumns > xColumns;
    assert( m_nColumnCount > 1 && "ApplyColumnProperties called without any columns" );
    try
    {
        const OUString & sTextColumns = getPropertyName( PROP_TEXT_COLUMNS );
        if ( xColumnContainer.is() )
            xColumnContainer->getPropertyValue( sTextColumns ) >>= xColumns;
        uno::Reference< beans::XPropertySet > xColumnPropSet( xColumns, uno::UNO_QUERY_THROW );
        if ( !m_bEvenlySpaced &&
             ( sal_Int32(m_aColWidth.size()) == m_nColumnCount ) &&
             ( (sal_Int32(m_aColDistance.size()) == m_nColumnCount - 1) || (sal_Int32(m_aColDistance.size()) == m_nColumnCount) ) )
        {
            // the column width in word is an absolute value, in OOo it's relative
            // the distances are both absolute
            sal_Int32 nColSum = 0;
            for ( sal_Int32 nCol = 0; nCol < m_nColumnCount; ++nCol )
            {
                nColSum += m_aColWidth[nCol];
                if ( nCol )
                    nColSum += m_aColDistance[nCol - 1];
            }
 
            sal_Int32 nRefValue = xColumns->getReferenceValue();
            double fRel = nColSum ? double( nRefValue ) / double( nColSum ) : 0.0;
            uno::Sequence< text::TextColumn > aColumns( m_nColumnCount );
            text::TextColumn* pColumn = aColumns.getArray();
 
            nColSum = 0;
            for ( sal_Int32 nCol = 0; nCol < m_nColumnCount; ++nCol )
            {
                const double fLeft = nCol ? m_aColDistance[nCol - 1] / 2 : 0;
                pColumn[nCol].LeftMargin = fLeft;
                const double fRight = (nCol == m_nColumnCount - 1) ? 0 : m_aColDistance[nCol] / 2;
                pColumn[nCol].RightMargin = fRight;
                const double fWidth = m_aColWidth[nCol];
                pColumn[nCol].Width = (fWidth + fLeft + fRight) * fRel;
                nColSum += pColumn[nCol].Width;
            }
            if ( nColSum != nRefValue )
                pColumn[m_nColumnCount - 1].Width += (nRefValue - nColSum);
            assert( pColumn[m_nColumnCount - 1].Width >= 0 );
 
            xColumns->setColumns( aColumns );
        }
        else
        {
            xColumns->setColumnCount( m_nColumnCount );
            xColumnPropSet->setPropertyValue( getPropertyName( PROP_AUTOMATIC_DISTANCE ), uno::Any( m_nColumnDistance ) );
        }
 
        if ( m_bSeparatorLineIsOn )
        {
            xColumnPropSet->setPropertyValue( u"SeparatorLineIsOn"_ustr, uno::Any( true ) );
            xColumnPropSet->setPropertyValue( u"SeparatorLineVerticalAlignment"_ustr, uno::Any( style::VerticalAlignment_TOP ) );
            xColumnPropSet->setPropertyValue( u"SeparatorLineRelativeHeight"_ustr, uno::Any( static_cast<sal_Int8>(100) ) );
            xColumnPropSet->setPropertyValue( u"SeparatorLineColor"_ustr, uno::Any( static_cast<sal_Int32>(COL_BLACK) ) );
            // 1 twip -> 2 mm100.
            xColumnPropSet->setPropertyValue( u"SeparatorLineWidth"_ustr, uno::Any( static_cast<sal_Int32>(2) ) );
        }
        xColumnContainer->setPropertyValue( sTextColumns, uno::Any( xColumns ) );
        // Set the columns to be unbalanced if that compatibility option is set or this is the last section.
        m_xColumnContainer = xColumnContainer;
        if ( rDM_Impl.GetSettingsTable()->GetNoColumnBalance() || rDM_Impl.GetIsLastSectionGroup() )
            DontBalanceTextColumns();
    }
    catch ( const uno::Exception& )
    {
        TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::ApplyColumnProperties" );
    }
    return xColumns;
}
 
bool SectionPropertyMap::HasHeader() const
{
    bool bRet = false;
    if (m_aPageStyle.is())
        m_aPageStyle->getPropertyValue(getPropertyName(PROP_HEADER_IS_ON)) >>= bRet;
    return bRet;
}
 
bool SectionPropertyMap::HasFooter() const
{
    bool bRet = false;
    if (m_aPageStyle.is())
        m_aPageStyle->getPropertyValue(getPropertyName(PROP_FOOTER_IS_ON)) >>= bRet;
    return bRet;
}
 
#define MIN_HEAD_FOOT_HEIGHT 100 // minimum header/footer height
 
namespace
{
 
// Copy the content of the header/footer property to the target style
void copyHeaderFooterTextProperty(const rtl::Reference<SwXPageStyle>& xSource,
                                  const rtl::Reference<SwXPageStyle>& xTarget,
                                  PropertyIds ePropId)
{
    if (!xSource.is() || !xTarget.is())
        return;
 
    try {
        const OUString& sName = getPropertyName(ePropId);
        SAL_INFO( "writerfilter", "Copying " << sName );
        uno::Reference<text::XText> xTextTarget(xTarget->getPropertyValue(sName), uno::UNO_QUERY_THROW);
        // remove any content already present or else it can become a mess
        SectionPropertyMap::removeXTextContent(xTextTarget);
        uno::Reference<text::XTextCopy> xTextCopyTarget(xTextTarget, uno::UNO_QUERY_THROW);
        if (!xTextCopyTarget.is())
            return;
        uno::Reference<text::XTextCopy> xTextCopySource(xSource->getPropertyValue(sName), uno::UNO_QUERY_THROW);
        if (!xTextCopySource.is())
            return;
        xTextCopyTarget->copyText(xTextCopySource);
    }
    catch (const uno::Exception&)
    {
        TOOLS_INFO_EXCEPTION( "writerfilter", "An exception occurred in SectionPropertyMap::CopyHeaderFooterTextProperty( )" );
    }
}
 
// Copies all the header and footer content and relevant flags from the source style to the target.
void completeCopyHeaderFooter(const rtl::Reference<SwXPageStyle>& xSourceStyle,
        const rtl::Reference<SwXPageStyle>& xTargetStyle,
        bool const bMissingHeader, bool const bMissingFooter)
{
    if (!xSourceStyle.is() || !xTargetStyle.is())
        return;
 
    bool bSourceHasHeader = false;
    bool bSourceHasFooter = false;
    bool bSourceHeaderIsShared = true;
    bool bSourceFooterIsShared = true;
    bool bSourceFirstIsShared = true;
 
    const OUString& sHeaderIsOn = getPropertyName(PROP_HEADER_IS_ON);
    const OUString& sFooterIsOn = getPropertyName(PROP_FOOTER_IS_ON);
    const OUString& sHeaderIsShared = getPropertyName(PROP_HEADER_IS_SHARED);
    const OUString& sFooterIsShared = getPropertyName(PROP_FOOTER_IS_SHARED);
    const OUString& sFirstIsShared = getPropertyName(PROP_FIRST_IS_SHARED);
 
    xSourceStyle->getPropertyValue(sHeaderIsOn) >>= bSourceHasHeader;
    xSourceStyle->getPropertyValue(sFooterIsOn) >>= bSourceHasFooter;
    xSourceStyle->getPropertyValue(sHeaderIsShared) >>= bSourceHeaderIsShared;
    xSourceStyle->getPropertyValue(sFooterIsShared) >>= bSourceFooterIsShared;
    xSourceStyle->getPropertyValue(sFirstIsShared) >>= bSourceFirstIsShared;
 
    xTargetStyle->setPropertyValue(sHeaderIsOn, uno::Any(bSourceHasHeader));
    xTargetStyle->setPropertyValue(sFooterIsOn, uno::Any(bSourceHasFooter));
    xTargetStyle->setPropertyValue(sHeaderIsShared, uno::Any(bSourceHeaderIsShared));
    xTargetStyle->setPropertyValue(sFooterIsShared, uno::Any(bSourceFooterIsShared));
    xTargetStyle->setPropertyValue(sFirstIsShared, uno::Any(bSourceFirstIsShared));
 
    if (bSourceHasHeader)
    {
        if (!bSourceFirstIsShared)
            copyHeaderFooterTextProperty(xSourceStyle, xTargetStyle, PROP_HEADER_TEXT_FIRST);
        if (!bSourceHeaderIsShared)
            copyHeaderFooterTextProperty(xSourceStyle, xTargetStyle, PROP_HEADER_TEXT_LEFT);
        copyHeaderFooterTextProperty(xSourceStyle, xTargetStyle, PROP_HEADER_TEXT);
    }
 
    if (bSourceHasFooter)
    {
        if (!bSourceFirstIsShared)
            copyHeaderFooterTextProperty(xSourceStyle, xTargetStyle, PROP_FOOTER_TEXT_FIRST);
        if (!bSourceFooterIsShared)
            copyHeaderFooterTextProperty(xSourceStyle, xTargetStyle, PROP_FOOTER_TEXT_LEFT);
        copyHeaderFooterTextProperty(xSourceStyle, xTargetStyle, PROP_FOOTER_TEXT);
    }
    // tdf#153196 the copy is used for the first page, the source will be used
    // on subsequent pages, so clear source's first page header/footer
    if (!bSourceFirstIsShared)
    {
        xSourceStyle->setPropertyValue(sFirstIsShared, uno::Any(true));
    }
    // if there is *only* a first-footer, and no previous section from which
    // to inherit a footer, the import process has created an empty footer
    // that didn't exist in the file; remove it
    if (bSourceHasHeader && bMissingHeader)
    {
        xSourceStyle->setPropertyValue(sHeaderIsOn, uno::Any(false));
    }
    if (bSourceHasFooter && bMissingFooter)
    {
        // setting "FooterIsShared" to true here does nothing, because it causes
        // left footer to be stashed, which means it will be exported anyway
        xSourceStyle->setPropertyValue(sFooterIsOn, uno::Any(false));
    }
}
 
// Copy headers and footers from the previous page style.
void copyHeaderFooter(const DomainMapper_Impl& rDM_Impl,
                                          const rtl::Reference< SwXPageStyle >& xPreviousStyle,
                                          const rtl::Reference< SwXPageStyle >& xStyle,
                                          bool bCopyRightHeader, bool bCopyLeftHeader, bool bCopyFirstHeader,
                                          bool bCopyRightFooter, bool bCopyLeftFooter, bool bCopyFirstFooter,
                                          bool bEvenAndOdd, bool bTitlePage)
{
    if (!rDM_Impl.IsNewDoc())
    {   // see also DomainMapper_Impl::PushPageHeaderFooter()
        return; // tdf#139737 SwUndoInserts cannot deal with new header/footer
    }
 
    if (!xPreviousStyle.is())
        return;
 
    bool bCopyHeader = bCopyRightHeader || bCopyLeftHeader || bCopyFirstHeader;
    bool bCopyFooter = bCopyRightFooter || bCopyLeftFooter || bCopyFirstFooter;
 
    if (!bCopyHeader && !bCopyFooter)
        return;
 
    bool bPreviousHasHeader = false;
    bool bPreviousHasFooter = false;
 
    bool bHasHeader = false;
    bool bHasFooter = false;
 
    const OUString& sHeaderIsOn = getPropertyName(PROP_HEADER_IS_ON);
    const OUString& sFooterIsOn = getPropertyName(PROP_FOOTER_IS_ON);
    const OUString& sHeaderIsShared = getPropertyName(PROP_HEADER_IS_SHARED);
    const OUString& sFooterIsShared = getPropertyName(PROP_FOOTER_IS_SHARED);
    const OUString& sFirstIsShared = getPropertyName(PROP_FIRST_IS_SHARED);
 
    xPreviousStyle->getPropertyValue(sHeaderIsOn) >>= bPreviousHasHeader;
    xPreviousStyle->getPropertyValue(sFooterIsOn) >>= bPreviousHasFooter;
 
    xStyle->getPropertyValue(sHeaderIsOn) >>= bHasHeader;
    xStyle->getPropertyValue(sFooterIsOn) >>= bHasFooter;
 
    // Set all properties at once before the copy, to avoid needless SwPageDesc copying.
    uno::Sequence<OUString> aNames = {
        sHeaderIsOn,
        sFooterIsOn,
        sHeaderIsShared,
        sFooterIsShared,
        sFirstIsShared,
    };
    uno::Sequence<uno::Any> aValues = {
        uno::Any(bPreviousHasHeader || bHasHeader),
        uno::Any(bPreviousHasFooter || bHasFooter),
        uno::Any(!bEvenAndOdd),
        uno::Any(!bEvenAndOdd),
        uno::Any(!bTitlePage),
    };
    xStyle->setPropertyValues(aNames, aValues);
 
    if (bPreviousHasHeader && bCopyHeader)
    {
        if (bCopyRightHeader)
            copyHeaderFooterTextProperty(xPreviousStyle, xStyle, PROP_HEADER_TEXT);
        if (bCopyLeftHeader && bEvenAndOdd)
            copyHeaderFooterTextProperty(xPreviousStyle, xStyle, PROP_HEADER_TEXT_LEFT);
        if (bCopyFirstHeader && bTitlePage)
            copyHeaderFooterTextProperty(xPreviousStyle, xStyle, PROP_HEADER_TEXT_FIRST);
    }
 
    if (bPreviousHasFooter && bCopyFooter)
    {
        if (bCopyRightFooter)
            copyHeaderFooterTextProperty(xPreviousStyle, xStyle, PROP_FOOTER_TEXT);
        if (bCopyLeftFooter && bEvenAndOdd)
            copyHeaderFooterTextProperty(xPreviousStyle, xStyle, PROP_FOOTER_TEXT_LEFT);
        if (bCopyFirstFooter && bTitlePage)
            copyHeaderFooterTextProperty(xPreviousStyle, xStyle, PROP_FOOTER_TEXT_FIRST);
    }
}
 
} // end anonymous namespace
 
 
// Copy header and footer content from the previous docx section as needed.
//
// Any headers and footers which were not defined in this docx section
// should be "linked" with the corresponding header or footer from the
// previous section.  LO does not support linking of header/footer content
// across page styles so we just copy the content from the previous section.
void SectionPropertyMap::CopyLastHeaderFooter(DomainMapper_Impl& rDM_Impl )
{
    SAL_INFO( "writerfilter", "START>>> SectionPropertyMap::CopyLastHeaderFooter()" );
    SectionPropertyMap* pLastContext = rDM_Impl.GetLastSectionContext();
    if (pLastContext)
    {
        rtl::Reference<SwXPageStyle> xPreviousStyle = pLastContext->GetPageStyle(rDM_Impl);
        rtl::Reference<SwXPageStyle> xStyle = GetPageStyle(rDM_Impl);
 
        bool bEvenAndOdd = rDM_Impl.GetSettingsTable()->GetEvenAndOddHeaders();
 
        copyHeaderFooter(rDM_Impl, xPreviousStyle, xStyle,
                         m_bDefaultHeaderLinkToPrevious,
                         m_bEvenPageHeaderLinkToPrevious,
                         m_bFirstPageHeaderLinkToPrevious,
                         m_bDefaultFooterLinkToPrevious,
                         m_bEvenPageFooterLinkToPrevious,
                         m_bFirstPageFooterLinkToPrevious,
                         bEvenAndOdd, m_bTitlePage);
    }
    SAL_INFO( "writerfilter", "END>>> SectionPropertyMap::CopyLastHeaderFooter()" );
}
 
void SectionPropertyMap::PrepareHeaderFooterProperties()
{
    sal_Int32 nTopMargin = m_nTopMargin;
    sal_Int32 nHeaderHeight = m_nHeaderTop;
    if (HasHeader())
    {
        nTopMargin = m_nHeaderTop;
        nHeaderHeight = m_nTopMargin - m_nHeaderTop;
 
        // minimum header height 1mm
        if ( nHeaderHeight < MIN_HEAD_FOOT_HEIGHT )
            nHeaderHeight = MIN_HEAD_FOOT_HEIGHT;
    }
 
    Insert(PROP_HEADER_IS_DYNAMIC_HEIGHT, uno::Any(m_bDynamicHeightTop));
    Insert(PROP_HEADER_DYNAMIC_SPACING, uno::Any(m_bDynamicHeightTop));
    Insert(PROP_HEADER_BODY_DISTANCE, uno::Any(nHeaderHeight - MIN_HEAD_FOOT_HEIGHT));
    Insert(PROP_HEADER_HEIGHT, uno::Any(nHeaderHeight));
    // looks like PROP_HEADER_HEIGHT = height of the header + space between the header, and the body
 
    sal_Int32 nBottomMargin = m_nBottomMargin;
    sal_Int32 nFooterHeight = m_nHeaderBottom;
    if (HasFooter())
    {
        nBottomMargin = m_nHeaderBottom;
        nFooterHeight = m_nBottomMargin - m_nHeaderBottom;
 
        // minimum footer height 1mm
        if ( nFooterHeight < MIN_HEAD_FOOT_HEIGHT )
            nFooterHeight = MIN_HEAD_FOOT_HEIGHT;
    }
 
    Insert(PROP_FOOTER_IS_DYNAMIC_HEIGHT, uno::Any(m_bDynamicHeightBottom));
    Insert(PROP_FOOTER_DYNAMIC_SPACING, uno::Any(m_bDynamicHeightBottom));
    Insert(PROP_FOOTER_BODY_DISTANCE, uno::Any(nFooterHeight - MIN_HEAD_FOOT_HEIGHT));
    Insert(PROP_FOOTER_HEIGHT, uno::Any(nFooterHeight));
 
    //now set the top/bottom margin for the follow page style
    Insert( PROP_TOP_MARGIN, uno::Any( std::max<sal_Int32>(nTopMargin, 0) ) );
    Insert( PROP_BOTTOM_MARGIN, uno::Any( std::max<sal_Int32>(nBottomMargin, 0) ) );
}
 
static uno::Reference< beans::XPropertySet > lcl_GetRangeProperties( bool bIsFirstSection,
                                                              DomainMapper_Impl& rDM_Impl,
                                                              const uno::Reference< text::XTextRange >& xStartingRange )
{
    uno::Reference< beans::XPropertySet > xRangeProperties;
    if ( bIsFirstSection && rDM_Impl.GetBodyText().is() )
    {
        uno::Reference< container::XEnumerationAccess > xEnumAccess( dynamic_cast<container::XEnumerationAccess*>(rDM_Impl.GetBodyText().get()), uno::UNO_QUERY_THROW );
        uno::Reference< container::XEnumeration > xEnum = xEnumAccess->createEnumeration();
        xRangeProperties.set( xEnum->nextElement(), uno::UNO_QUERY_THROW );
        if ( rDM_Impl.GetIsDummyParaAddedForTableInSection() && xEnum->hasMoreElements() )
            xRangeProperties.set( xEnum->nextElement(), uno::UNO_QUERY_THROW );
    }
    else if ( xStartingRange.is() )
        xRangeProperties.set( xStartingRange, uno::UNO_QUERY_THROW );
    return xRangeProperties;
}
 
void SectionPropertyMap::HandleMarginsHeaderFooter(DomainMapper_Impl& rDM_Impl)
{
    Insert( PROP_LEFT_MARGIN, uno::Any( m_nLeftMargin ) );
    Insert( PROP_RIGHT_MARGIN, uno::Any( m_nRightMargin ) );
    Insert(PROP_GUTTER_MARGIN, uno::Any(m_nGutterMargin));
 
    // w:background is applied to every page in the document
    if (!rDM_Impl.m_oBackgroundColor.has_value() && !rDM_Impl.IsRTFImport())
    {
        // DOCX has an interesting quirk, where if the fallback background color is not defined,
        // then the fill is not applied either.
 
        // Disable the imported fill from the default style
        if (rDM_Impl.m_bCopyStandardPageStyleFill && m_sPageStyleName == "Standard")
        {
            rDM_Impl.m_bCopyStandardPageStyleFill = false;
            m_aPageStyle->setPropertyValue(u"FillStyle"_ustr, uno::Any(drawing::FillStyle_NONE));
        }
    }
    else if (rDM_Impl.m_bCopyStandardPageStyleFill) // complex fill: graphics/gradients/patterns
    {
        rtl::Reference<SwXBaseStyle> xDefaultPageStyle =
                    rDM_Impl.GetPageStyles()->getStyleByName(u"Standard"_ustr);
        for (const beans::Property& rProp : m_aPageStyle->getPropertySetInfo()->getProperties())
        {
            if (!rProp.Name.startsWith("Fill")) // only copy XATTR_FILL properties
                continue;
            try
            {
                const uno::Any aFillValue = xDefaultPageStyle->getPropertyValue(rProp.Name);
                m_aPageStyle->setPropertyValue(rProp.Name, aFillValue);
            }
            catch (uno::Exception&)
            {
                DBG_UNHANDLED_EXCEPTION("writerfilter", "Exception setting page background fill");
            }
        }
    }
    else if (rDM_Impl.m_oBackgroundColor) // simple, solid color
        Insert( PROP_BACK_COLOR, uno::Any( *rDM_Impl.m_oBackgroundColor ) );
 
    // Check for missing footnote separator only in case there is at least
    // one footnote.
    if (rDM_Impl.m_StreamStateStack.top().bHasFtn && !rDM_Impl.m_bHasFtnSep)
    {
        // Set footnote line width to zero, document has no footnote separator.
        Insert(PROP_FOOTNOTE_LINE_RELATIVE_WIDTH, uno::Any(sal_Int32(0)));
    }
    if ( rDM_Impl.m_bHasFtnSep && rDM_Impl.GetTextDocument() )
    {
        //If default paragraph style is RTL, footnote separator should be right aligned
        //and for RTL locales, LTR default paragraph style should present a left aligned footnote separator
        try
        {
            rtl::Reference<SwXStyleFamilies> xStyleFamilies = rDM_Impl.GetTextDocument()->getSwStyleFamilies();
            rtl::Reference<SwXStyleFamily> xParagraphStyles;
            if ( xStyleFamilies.is() )
                xParagraphStyles = xStyleFamilies->GetParagraphStyles();
            rtl::Reference<SwXBaseStyle> xStandard;
            if ( xParagraphStyles.is() )
                xStandard = xParagraphStyles->getStyleByName(u"Standard"_ustr) ;
            if ( xStandard.is() )
            {
                sal_Int16 aWritingMode(0);
                xStandard->getPropertyValue( getPropertyName(PROP_WRITING_MODE) ) >>= aWritingMode;
                if( aWritingMode == text::WritingMode2::RL_TB )
                    Insert( PROP_FOOTNOTE_LINE_ADJUST, uno::Any( sal_Int16(text::HorizontalAdjust_RIGHT) ), false );
                else
                    Insert( PROP_FOOTNOTE_LINE_ADJUST, uno::Any( sal_Int16(text::HorizontalAdjust_LEFT) ), false );
            }
        }
        catch ( const uno::Exception& ) {}
    }
 
    /*** if headers/footers are available then the top/bottom margins of the
    header/footer are copied to the top/bottom margin of the page
    */
    CopyLastHeaderFooter(rDM_Impl);
    PrepareHeaderFooterProperties();
 
    // tdf#119952: If top/bottom margin was negative during docx import,
    // then the header/footer and the body could be on top of each other
    // writer is unable to display both of them in the same position, but can be simulated
    // by moving the header/footer text into a flyframe anchored to the header/footer,
    // leaving an empty dummy header/footer.
    rDM_Impl.ConvertHeaderFooterToTextFrame(m_bDynamicHeightTop, m_bDynamicHeightBottom);
}
 
void SectionPropertyMap::InheritOrFinalizePageStyles(DomainMapper_Impl& rDM_Impl)
{
    // if no new styles have been created for this section, inherit from the previous section,
    // otherwise apply this section's settings to the new style.
    SectionPropertyMap* pLastContext = rDM_Impl.GetLastSectionContext();
    //tdf124637 TODO: identify and skip special sections (like footnotes/endnotes)
    if (pLastContext && m_sPageStyleName.isEmpty())
        m_sPageStyleName = pLastContext->GetPageStyleName();
    else
    {
        HandleMarginsHeaderFooter(rDM_Impl);
        GetPageStyle(rDM_Impl);
        if (rDM_Impl.IsNewDoc() && m_aPageStyle.is())
            ApplyProperties_(m_aPageStyle);
    }
}
 
void SectionPropertyMap::HandleIncreasedAnchoredObjectSpacing(DomainMapper_Impl& rDM_Impl)
{
    // Ignore Word 2010 and older.
    if (rDM_Impl.GetSettingsTable()->GetWordCompatibilityMode() < 15)
        return;
 
    sal_Int32 nPageWidth = GetPageWidth();
    sal_Int32 nTextAreaWidth = nPageWidth - GetLeftMargin() - GetRightMargin();
 
    std::vector<AnchoredObjectsInfo>& rAnchoredObjectAnchors = rDM_Impl.m_aAnchoredObjectAnchors;
    for (const auto& rAnchor : rAnchoredObjectAnchors)
    {
        // Ignore this paragraph when there are not enough shapes to trigger the Word bug we
        // emulate.
        if (rAnchor.m_aAnchoredObjects.size() < 4)
            continue;
 
        // Ignore this paragraph if none of the objects are wrapped in the background.
        sal_Int32 nOpaqueCount = 0;
        for (const auto& rAnchored : rAnchor.m_aAnchoredObjects)
        {
            // Ignore inline objects stored only for redlining.
            if (rAnchored.m_xRedlineForInline)
                continue;
 
            uno::Reference<beans::XPropertySet> xShape(rAnchored.m_xAnchoredObject, uno::UNO_QUERY);
            if (!xShape.is())
            {
                continue;
            }
 
            bool bOpaque = true;
            xShape->getPropertyValue(u"Opaque"_ustr) >>= bOpaque;
            if (!bOpaque)
            {
                ++nOpaqueCount;
            }
        }
        if (nOpaqueCount < 1)
        {
            continue;
        }
 
        // Analyze the anchored objects of this paragraph, now that we know the
        // page width.
        sal_Int32 nShapesWidth = 0;
        for (const auto& rAnchored : rAnchor.m_aAnchoredObjects)
        {
            uno::Reference<drawing::XShape> xShape(rAnchored.m_xAnchoredObject, uno::UNO_QUERY);
            if (!xShape.is())
                continue;
 
            uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
            if (!xPropertySet.is())
                continue;
 
            // Ignore objects with no wrapping.
            text::WrapTextMode eWrap = text::WrapTextMode_THROUGH;
            xPropertySet->getPropertyValue(u"Surround"_ustr) >>= eWrap;
            if (eWrap == text::WrapTextMode_THROUGH)
                continue;
 
            // Use the original left margin, in case GraphicImport::lcl_sprm() reduced the doc model
            // one to 0.
            sal_Int32 nLeftMargin = rAnchored.m_nLeftMargin;
            sal_Int32 nRightMargin = 0;
            xPropertySet->getPropertyValue(u"RightMargin"_ustr) >>= nRightMargin;
            nShapesWidth += xShape->getSize().Width + nLeftMargin + nRightMargin;
        }
 
        // Ignore cases when we have enough horizontal space for the shapes.
        if (nTextAreaWidth > nShapesWidth)
            continue;
 
        sal_Int32 nHeight = 0;
        for (const auto& rAnchored : rAnchor.m_aAnchoredObjects)
        {
            uno::Reference<drawing::XShape> xShape(rAnchored.m_xAnchoredObject, uno::UNO_QUERY);
            if (!xShape.is())
                continue;
 
            nHeight += xShape->getSize().Height;
        }
 
        uno::Reference<beans::XPropertySet> xParagraph(rAnchor.m_xParagraph, uno::UNO_QUERY);
        if (xParagraph.is())
        {
            sal_Int32 nTopMargin = 0;
            xParagraph->getPropertyValue(u"ParaTopMargin"_ustr) >>= nTopMargin;
            // Increase top spacing of the paragraph to match Word layout
            // behavior.
            nTopMargin = std::max(nTopMargin, nHeight);
            xParagraph->setPropertyValue(u"ParaTopMargin"_ustr, uno::Any(nTopMargin));
        }
    }
    rAnchoredObjectAnchors.clear();
}
 
void BeforeConvertToTextFrame(const std::deque<StoredRedline>& rFramedRedlines, std::vector<sal_Int32>& redPos, std::vector<sal_Int32>& redLen, std::vector<OUString>& redCell, std::vector<OUString>& redTable)
{
    // convert redline ranges to cursor movement and character length
    for( size_t i = 0; i < rFramedRedlines.size(); i++)
    {
        uno::Reference<text::XText> xCell;
        uno::Reference< text::XTextRange > xRange = rFramedRedlines[i].mxRange;
        uno::Reference< beans::XPropertySet > xRangeProperties;
        if ( xRange.is() )
        {
            OUString sTableName;
            OUString sCellName;
            xRangeProperties.set( xRange, uno::UNO_QUERY_THROW );
            if (xRangeProperties->getPropertySetInfo()->hasPropertyByName(u"TextTable"_ustr))
            {
                uno::Any aTable = xRangeProperties->getPropertyValue(u"TextTable"_ustr);
                if ( aTable != uno::Any() )
                {
                    uno::Reference<text::XTextTable> xTable;
                    aTable >>= xTable;
                    uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY);
                    xTableProperties->getPropertyValue(u"TableName"_ustr) >>= sTableName;
                }
                if (xRangeProperties->getPropertySetInfo()->hasPropertyByName(u"Cell"_ustr))
                {
                    uno::Any aCell = xRangeProperties->getPropertyValue(u"Cell"_ustr);
                    if ( aCell != uno::Any() )
                    {
                        aCell >>= xCell;
                        uno::Reference<beans::XPropertySet> xCellProperties(xCell, uno::UNO_QUERY);
                        xCellProperties->getPropertyValue(u"CellName"_ustr) >>= sCellName;
                    }
                }
            }
            redTable.push_back(sTableName);
            redCell.push_back(sCellName);
            bool bOk = false;
            if (!sTableName.isEmpty() && !sCellName.isEmpty())
            {
                uno::Reference<text::XTextCursor> xRangeCursor = xCell->createTextCursorByRange( xRange );
                if ( xRangeCursor.is() )
                {
                    bOk = true;
                    sal_Int32 nLen = xRange->getString().getLength();
                    redLen.push_back(nLen);
                    xRangeCursor->gotoStart(true);
                    redPos.push_back(xRangeCursor->getString().getLength() - nLen);
                }
            }
            if (!bOk)
            {
                // missing cell or failed createTextCursorByRange()
                redLen.push_back(-1);
                redPos.push_back(-1);
            }
        }
    }
}
 
void AfterConvertToTextFrame(DomainMapper_Impl& rDM_Impl, const std::deque<StoredRedline>& rFramedRedlines, std::vector<sal_Int32>& redPos, std::vector<sal_Int32>& redLen, std::vector<OUString>& redCell, std::vector<OUString>& redTable)
{
    const rtl::Reference<SwXTextDocument>& xTextDocument(rDM_Impl.GetTextDocument());
    rtl::Reference<SwXTextTables> xTables = xTextDocument->getSwTextTables();
    for( size_t i = 0; i < rFramedRedlines.size(); i++)
    {
        // skip failed createTextCursorByRange()
        if (redPos[i] == -1)
            continue;
        rtl::Reference<SwXTextTable> xTable(xTables->getTextTableByName(redTable[i]));
        rtl::Reference<SwXCell> xCell(xTable->getSwCellByName(redCell[i]));
        rtl::Reference<SwXTextCursor> xCrsr = xCell->createXTextCursor();
        xCrsr->goRight(redPos[i], false);
        xCrsr->goRight(redLen[i], true);
        try
        {
            xCrsr->makeRedline( rFramedRedlines[i].msType, rFramedRedlines[i].maRedlineProperties );
        }
        catch (const uno::Exception&)
        {
            DBG_UNHANDLED_EXCEPTION("writerfilter", "makeRedline() failed");
        }
    }
}
 
void SectionPropertyMap::CreateEvenOddPageStyleCopy(DomainMapper_Impl& rDM_Impl, PageBreakType eBreakType)
{
    OUString evenOddStyleName = rDM_Impl.GetUnusedPageStyleName();
    rtl::Reference<SwXPageStyle> evenOddStyle = rDM_Impl.GetTextDocument()->createPageStyle();
    // Unfortunately using setParent() does not work for page styles, so make a deep copy of the page style.
    rtl::Reference<SwXPageStyle> pageProperties(m_aPageStyle);
    uno::Reference<beans::XPropertySetInfo> pagePropertiesInfo(pageProperties->getPropertySetInfo());
    const uno::Sequence<beans::Property> propertyList(pagePropertiesInfo->getProperties());
 
    // Ignore write-only properties.
    static constexpr auto staticDenylist = frozen::make_unordered_set<std::u16string_view>({
        u"FooterBackGraphicURL", u"BackGraphicURL", u"HeaderBackGraphicURL",
        u"HeaderIsOn", u"FooterIsOn",
        u"HeaderIsShared", u"FooterIsShared", u"FirstIsShared",
        u"HeaderText", u"HeaderTextLeft", u"HeaderTextFirst",
        u"FooterText", u"FooterTextLeft", u"FooterTextFirst"
    });
 
    bool isMirrorMargins = PageBreakType::Even == eBreakType && rDM_Impl.GetSettingsTable()->GetMirrorMarginSettings();
    for (const auto& rProperty : propertyList)
    {
        if ((rProperty.Attributes & beans::PropertyAttribute::READONLY) == 0)
        {
            if (staticDenylist.find(rProperty.Name) == staticDenylist.end())
            {
                OUString sSetName = rProperty.Name;
                if (isMirrorMargins)
                {
                    if (rProperty.Name == u"LeftMargin"_ustr)
                        sSetName = u"RightMargin"_ustr;
                    else if (rProperty.Name == u"RightMargin"_ustr)
                        sSetName = u"LeftMargin"_ustr;
                }
                evenOddStyle->setPropertyValue(
                    sSetName,
                    pageProperties->getPropertyValue(rProperty.Name));
            }
        }
    }
    evenOddStyle->setPropertyValue(u"FollowStyle"_ustr, uno::Any(m_sPageStyleName));
 
    rDM_Impl.GetPageStyles()->insertStyleByName(evenOddStyleName, evenOddStyle);
 
    if (rDM_Impl.IsNewDoc())
    {
        bool const bEvenAndOdd(rDM_Impl.GetSettingsTable()->GetEvenAndOddHeaders());
        completeCopyHeaderFooter(pageProperties, evenOddStyle,
            !rDM_Impl.SeenHeaderFooter(PagePartType::Header, PageType::RIGHT)
                && (!bEvenAndOdd || !rDM_Impl.SeenHeaderFooter(PagePartType::Header, PageType::LEFT)),
            !rDM_Impl.SeenHeaderFooter(PagePartType::Footer, PageType::RIGHT)
                && (!bEvenAndOdd || !rDM_Impl.SeenHeaderFooter(PagePartType::Footer, PageType::LEFT)));
    }
 
    if (eBreakType == PageBreakType::Even)
        evenOddStyle->setPropertyValue(getPropertyName(PROP_PAGE_STYLE_LAYOUT), uno::Any(style::PageStyleLayout_LEFT));
    else if (eBreakType == PageBreakType::Odd)
        evenOddStyle->setPropertyValue(getPropertyName(PROP_PAGE_STYLE_LAYOUT), uno::Any(style::PageStyleLayout_RIGHT));
 
    m_sPageStyleName = evenOddStyleName; // And use it instead of the original one (which is set as follow of this one).
}
 
void SectionPropertyMap::CloseSectionGroup( DomainMapper_Impl& rDM_Impl )
{
    SectionPropertyMap* pPrevSection = rDM_Impl.GetLastSectionContext();
 
    // The default section type is nextPage.
    if ( m_nBreakType == -1 )
        m_nBreakType = NS_ooxml::LN_Value_ST_SectionMark_nextPage;
    else if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_nextColumn )
    {
        // Word 2013+ seems to treat a section column break as a page break all the time.
        // It always acts like a page break if there are no columns, or a different number of columns.
        // Also, if this is the first section, the break type is basically irrelevant - works best as nextPage.
        if ( rDM_Impl.GetSettingsTable()->GetWordCompatibilityMode() > 14
             || !pPrevSection
             || m_nColumnCount < 2
             || m_nColumnCount != pPrevSection->ColumnCount()
           )
        {
            m_nBreakType = NS_ooxml::LN_Value_ST_SectionMark_nextPage;
        }
    }
    else if ( m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_continuous )
    {
        // if page orientation differs from previous section, it can't be treated as continuous
        if ( pPrevSection )
        {
            bool bIsLandscape = false;
            std::optional< PropertyMap::Property > pProp = getProperty( PROP_IS_LANDSCAPE );
            if ( pProp )
                pProp->second >>= bIsLandscape;
 
            bool bPrevIsLandscape = false;
            pProp = pPrevSection->getProperty( PROP_IS_LANDSCAPE );
            if ( pProp )
                pProp->second >>= bPrevIsLandscape;
 
            if ( bIsLandscape != bPrevIsLandscape )
                m_nBreakType = NS_ooxml::LN_Value_ST_SectionMark_nextPage;
        }
    }
 
    try
    {
        HandleIncreasedAnchoredObjectSpacing(rDM_Impl);
    }
    catch (const uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("writerfilter", "HandleIncreasedAnchoredObjectSpacing() failed");
    }
 
    if ( m_nLnnMod )
    {
        bool bFirst = rDM_Impl.IsLineNumberingSet();
        rDM_Impl.SetLineNumbering( m_nLnnMod, m_nLnc, m_ndxaLnn );
        if ( m_nLnnMin > 0 || (bFirst && m_nLnc == NS_ooxml::LN_Value_ST_LineNumberRestart_newSection) )
        {
            //set the starting value at the beginning of the section
            try
            {
                uno::Reference< beans::XPropertySet > xRangeProperties;
                if ( m_xStartingRange.is() )
                {
                    xRangeProperties.set( m_xStartingRange, uno::UNO_QUERY_THROW );
                }
                else
                {
                    //set the start value at the beginning of the document
                    xRangeProperties.set( rDM_Impl.GetTextDocument()->getText()->getStart(), uno::UNO_QUERY_THROW );
                }
                // Writer is 1-based, Word is 0-based.
                xRangeProperties->setPropertyValue(
                    getPropertyName(PROP_PARA_LINE_NUMBER_START_VALUE),
                    uno::Any(m_nLnnMin + 1));
            }
            catch ( const uno::Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper", "Exception in SectionPropertyMap::CloseSectionGroup");
            }
        }
    }
 
    if (m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_continuous
        && !rDM_Impl.IsInComments())
    {
        //todo: insert a section or access the already inserted section
        rtl::Reference< SwXTextSection > xSection =
            rDM_Impl.appendTextSectionAfter( m_xStartingRange );
        if ( xSection.is() )
        {
            if ( m_nColumnCount > 1 )
                ApplyColumnProperties( xSection, rDM_Impl );
 
            ApplyProtectionProperties( xSection, rDM_Impl );
        }
 
        try
        {
            setHeaderFooterProperties(rDM_Impl);
            InheritOrFinalizePageStyles( rDM_Impl );
            ApplySectionProperties( xSection, rDM_Impl );  //depends on InheritOrFinalizePageStyles
            uno::Reference< beans::XPropertySet > xRangeProperties( lcl_GetRangeProperties( m_bIsFirstSection, rDM_Impl, m_xStartingRange ) );
            if ( m_bIsFirstSection && !m_sPageStyleName.isEmpty() && xRangeProperties.is() )
            {
                xRangeProperties->setPropertyValue(getPropertyName(PROP_PAGE_DESC_NAME), uno::Any(m_sPageStyleName));
            }
            else if ((!m_bFirstPageHeaderLinkToPrevious ||
                      !m_bFirstPageFooterLinkToPrevious ||
                      !m_bDefaultHeaderLinkToPrevious ||
                      !m_bDefaultFooterLinkToPrevious ||
                      !m_bEvenPageHeaderLinkToPrevious ||
                      !m_bEvenPageFooterLinkToPrevious)
                    && rDM_Impl.GetCurrentXText())
            {
                // find a node in the section that has a page break and change
                // it to apply the page style; see "nightmare scenario" in
                // wwSectionManager::InsertSegments()
                auto xTextAppend = rDM_Impl.GetCurrentXText();
                uno::Reference<container::XEnumerationAccess> const xCursor(
                    xTextAppend->createTextCursorByRange(
                        uno::Reference<text::XTextContent>(static_cast<cppu::OWeakObject*>(xSection.get()), uno::UNO_QUERY_THROW)->getAnchor()),
                    uno::UNO_QUERY_THROW);
                uno::Reference<container::XEnumeration> const xEnum(
                        xCursor->createEnumeration());
                bool isFound = false;
                while (xEnum->hasMoreElements())
                {
                    uno::Reference<beans::XPropertySet> xElem;
                    xEnum->nextElement() >>= xElem;
                    if (xElem->getPropertySetInfo()->hasPropertyByName(u"BreakType"_ustr))
                    {
                        style::BreakType eBreakType;
                        if ((xElem->getPropertyValue(u"BreakType"_ustr) >>= eBreakType) && eBreakType == style::BreakType_PAGE_BEFORE)
                        {
                            // tdf#112201: do *not* use m_sFirstPageStyleName here!
                            xElem->setPropertyValue(getPropertyName(PROP_PAGE_DESC_NAME), uno::Any(m_sPageStyleName));
                            m_aPageStyle->setPropertyValue(getPropertyName(PROP_FIRST_IS_SHARED), uno::Any(true));
                            isFound = true;
                            break;
                        }
                    }
                }
                uno::Reference<text::XParagraphCursor> const xPCursor(xCursor, uno::UNO_QUERY_THROW);
                float fCharHeight = 0;
                if (!isFound)
                {   // HACK: try the last paragraph of the previous section
                    xPCursor->gotoPreviousParagraph(false);
                    uno::Reference<beans::XPropertySet> const xPSCursor(xCursor, uno::UNO_QUERY_THROW);
                    style::BreakType eBreakType;
                    if ((xPSCursor->getPropertyValue(u"BreakType"_ustr) >>= eBreakType) && eBreakType == style::BreakType_PAGE_BEFORE)
                    {
                        xPSCursor->setPropertyValue(getPropertyName(PROP_PAGE_DESC_NAME), uno::Any(m_sPageStyleName));
                        m_aPageStyle->setPropertyValue(getPropertyName(PROP_FIRST_IS_SHARED), uno::Any(true));
                        isFound = true;
                    }
                    else
                    {
                        xPSCursor->getPropertyValue(u"CharHeight"_ustr) >>= fCharHeight;
                    }
                }
                if (!isFound && fCharHeight <= 1.0)
                {
                    // If still not found, see if the last paragraph is ~invisible, and work with
                    // the last-in-practice paragraph.
                    xPCursor->gotoPreviousParagraph(false);
                    uno::Reference<beans::XPropertySet> xPropertySet(xCursor, uno::UNO_QUERY_THROW);
                    OUString aPageDescName;
                    if ((xPropertySet->getPropertyValue(u"PageDescName"_ustr) >>= aPageDescName) && !aPageDescName.isEmpty())
                    {
                        rtl::Reference<SwXBaseStyle> xPageStyle(rDM_Impl.GetPageStyles()->getStyleByName(aPageDescName));
                        xPageStyle->setPropertyValue(u"FollowStyle"_ustr, uno::Any(m_sPageStyleName));
                        m_aPageStyle->setPropertyValue(getPropertyName(PROP_FIRST_IS_SHARED), uno::Any(true));
                    }
                }
            }
        }
        catch ( const uno::Exception& )
        {
            SAL_WARN( "writerfilter", "failed to set PageDescName!" );
        }
    }
    // If the section is of type "New column" (0x01), then simply insert a column break.
    // But only if there actually are columns on the page, otherwise a column break
    // seems to be handled like a page break by MSO.
    else if (m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_nextColumn
            && m_nColumnCount > 1 && !rDM_Impl.IsInComments())
    {
        try
        {
            setHeaderFooterProperties(rDM_Impl);
            InheritOrFinalizePageStyles( rDM_Impl );
            /*TODO tdf#135343: Just inserting a column break sounds like the right idea, but the implementation is wrong.
             * Somehow, the previous column section needs to be extended to cover this new text.
             * Currently, it is completely broken, producing a no-column section that starts on a new page.
             */
            uno::Reference< beans::XPropertySet > xRangeProperties;
            if ( m_xStartingRange.is() )
            {
                xRangeProperties.set( m_xStartingRange, uno::UNO_QUERY );
            }
            else
            {
                //set the start value at the beginning of the document
                xRangeProperties.set( rDM_Impl.GetTextDocument()->getText()->getStart(), uno::UNO_QUERY );
            }
            if (xRangeProperties)
                xRangeProperties->setPropertyValue( getPropertyName( PROP_BREAK_TYPE ), uno::Any( style::BreakType_COLUMN_BEFORE ) );
        }
        catch ( const uno::Exception& ) {}
    }
    else if (!rDM_Impl.IsInComments())
    {
        rtl::Reference< SwXTextSection > xSection;
        ApplyProtectionProperties( xSection, rDM_Impl );
 
        //get the properties and create appropriate page styles
        rtl::Reference<SwXPageStyle> xPageStyle;
        //This part certainly is not needed for footnotes, so don't create unused page styles.
        if ( !rDM_Impl.IsInFootOrEndnote() )
        {
            xPageStyle = GetPageStyle(rDM_Impl);
            setHeaderFooterProperties(rDM_Impl);
            HandleMarginsHeaderFooter(rDM_Impl);
        }
 
        if ( rDM_Impl.GetSettingsTable()->GetMirrorMarginSettings() )
        {
            Insert( PROP_PAGE_STYLE_LAYOUT, uno::Any( style::PageStyleLayout_MIRRORED ) );
        }
        uno::Reference< text::XTextColumns > xColumns;
        if ( m_nColumnCount > 1 )
        {
            // prefer setting column properties into a section, not a page style if at all possible.
            if ( !xSection.is() )
                xSection = rDM_Impl.appendTextSectionAfter( m_xStartingRange );
            if ( xSection.is() )
                ApplyColumnProperties(xSection, rDM_Impl);
            else if (xPageStyle.is())
                xColumns = ApplyColumnProperties(xPageStyle, rDM_Impl);
        }
 
        // these BreakTypes are effectively page-breaks: don't evenly distribute text in columns before a page break;
        if ( pPrevSection && pPrevSection->ColumnCount() )
            pPrevSection->DontBalanceTextColumns();
 
        //prepare text grid properties
        sal_Int32 nHeight = 1;
        std::optional< PropertyMap::Property > pProp = getProperty( PROP_HEIGHT );
        if ( pProp )
            pProp->second >>= nHeight;
 
        sal_Int32 nWidth = 1;
        pProp = getProperty( PROP_WIDTH );
        if ( pProp )
            pProp->second >>= nWidth;
 
        sal_Int16 nWritingMode = text::WritingMode2::LR_TB;
        pProp = getProperty( PROP_WRITING_MODE );
        if ( pProp )
            pProp->second >>= nWritingMode;
 
        sal_Int32 nTextAreaHeight = nWritingMode == text::WritingMode2::LR_TB ?
            nHeight - m_nTopMargin - m_nBottomMargin :
            nWidth - m_nLeftMargin - m_nRightMargin;
 
        sal_Int32 nGridLinePitch = m_nGridLinePitch;
        //sep.dyaLinePitch
        if ( nGridLinePitch < 1 || nGridLinePitch > 31680 )
        {
            SAL_WARN( "writerfilter", "sep.dyaLinePitch outside legal range: " << nGridLinePitch );
            nGridLinePitch = 1;
        }
 
        const sal_Int32 nGridLines = nTextAreaHeight / nGridLinePitch;
        sal_Int16 nGridType = m_nGridType;
        if ( nGridLines >= 0 && nGridLines <= SAL_MAX_INT16 )
            Insert( PROP_GRID_LINES, uno::Any( sal_Int16(nGridLines) ) );
        else
            nGridType = text::TextGridMode::NONE;
 
        // PROP_GRID_MODE
        if ( nGridType == text::TextGridMode::LINES_AND_CHARS )
        {
            if (!m_nDxtCharSpace)
                nGridType = text::TextGridMode::LINES;
            else
                Insert( PROP_GRID_SNAP_TO_CHARS, uno::Any( m_bGridSnapToChars ) );
        }
 
        Insert( PROP_GRID_MODE, uno::Any( nGridType ) );
 
        sal_Int32 nCharWidth = 423; //240 twip/ 12 pt
        const StyleSheetEntryPtr pEntry = rDM_Impl.GetStyleSheetTable()->FindStyleSheetByConvertedStyleName( u"Standard" );
        if ( pEntry )
        {
            std::optional< PropertyMap::Property > pPropHeight = pEntry->m_pProperties->getProperty( PROP_CHAR_HEIGHT_ASIAN );
            if ( pPropHeight )
            {
                double fHeight = 0;
                if ( pPropHeight->second >>= fHeight )
                    nCharWidth = ConversionHelper::convertTwipToMm100_Limited( static_cast<sal_Int32>(fHeight * 20.0 + 0.5) );
            }
        }
 
        //dxtCharSpace
        if ( m_nDxtCharSpace )
        {
            sal_Int32 nCharSpace = m_nDxtCharSpace;
            //main lives in top 20 bits, and is signed.
            sal_Int32 nMain = (nCharSpace & 0xFFFFF000);
            nMain /= 0x1000;
            nCharWidth += ConversionHelper::convertTwipToMm100_Limited(nMain * 20);
 
            sal_Int32 nFraction = (nCharSpace & 0x00000FFF);
            nFraction = (nFraction * 20) / 0xFFF;
            nCharWidth += ConversionHelper::convertTwipToMm100_Limited(nFraction);
        }
 
        if ( m_nPageNumberType >= 0 )
            Insert( PROP_NUMBERING_TYPE, uno::Any( m_nPageNumberType ) );
 
        // #i119558#, force to set document as standard page mode,
        // refer to ww8 import process function "SwWW8ImplReader::SetDocumentGrid"
        try
        {
            if (rDM_Impl.GetTextDocument())
            {
                Insert(PROP_GRID_STANDARD_MODE, uno::Any(true));
                rDM_Impl.GetTextDocument()->setPropertyValue(u"DefaultPageMode"_ustr, uno::Any(false));
            }
        }
        catch ( const uno::Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("writerfilter.dmapper", "Exception in SectionPropertyMap::CloseSectionGroup");
        }
 
        Insert( PROP_GRID_BASE_HEIGHT, uno::Any( nGridLinePitch ) );
        Insert( PROP_GRID_BASE_WIDTH, uno::Any( nCharWidth ) );
        Insert( PROP_GRID_RUBY_HEIGHT, uno::Any( sal_Int32( 0 ) ) );
 
        if (rDM_Impl.IsNewDoc() && xPageStyle.is())
            ApplyProperties_(xPageStyle);
 
        ApplyBorderToPageStyles( rDM_Impl, m_eBorderApply, m_eBorderOffsetFrom );
        ApplyPaperSource(rDM_Impl);
 
        try
        {
            //now apply this break at the first paragraph of this section
            uno::Reference< beans::XPropertySet > xRangeProperties( lcl_GetRangeProperties( m_bIsFirstSection, rDM_Impl, m_xStartingRange ) );
 
            // Handle page breaks with odd/even page numbering. We need to use an extra page style for setting the page style
            // to left/right, because if we set it to the normal style, we'd set it to "First Page"/"Default Style", which would
            // break them (all default pages would be only left or right).
            if (m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_evenPage)
            {
                CreateEvenOddPageStyleCopy(rDM_Impl, PageBreakType::Even);
            }
            else if (m_nBreakType == NS_ooxml::LN_Value_ST_SectionMark_oddPage)
            {
                CreateEvenOddPageStyleCopy(rDM_Impl, PageBreakType::Odd);
            }
 
            if (rDM_Impl.m_xAltChunkStartingRange.is())
            {
                xRangeProperties.set(rDM_Impl.m_xAltChunkStartingRange, uno::UNO_QUERY);
            }
            if (xRangeProperties.is() && (rDM_Impl.IsNewDoc() || rDM_Impl.IsAltChunk()))
            {
                // Avoid setting page style in case of autotext: so inserting the autotext at the
                // end of the document does not introduce an unwanted page break.
                // Also avoid setting the page style at the very beginning if it still is the default page style.
                const OUString sPageStyle = m_sPageStyleName;
                if (!rDM_Impl.IsReadGlossaries()
                    && !rDM_Impl.IsInFootOrEndnote()
                    && !(m_bIsFirstSection && sPageStyle == getPropertyName( PROP_STANDARD ) && m_nPageNumber < 0)
                   )
                {
                    xRangeProperties->setPropertyValue(
                        getPropertyName( PROP_PAGE_DESC_NAME ),
                        uno::Any(sPageStyle) );
                }
 
                if (0 <= m_nPageNumber)
                {
                    sal_Int16 nPageNumber = static_cast< sal_Int16 >(m_nPageNumber);
                    xRangeProperties->setPropertyValue(getPropertyName(PROP_PAGE_NUMBER_OFFSET),
                        uno::Any(nPageNumber));
                }
            }
        }
        catch ( const uno::Exception& )
        {
            TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::CloseSectionGroup" );
        }
    }
 
    // Now that the margins are known, resize relative width shapes because some shapes in LO do not support percentage-sizes
    sal_Int32 nParagraphWidth = GetPageWidth() - m_nLeftMargin - m_nRightMargin;
    if ( m_nColumnCount > 1 )
    {
        // skip custom-width columns since we don't know what column the shape is in.
        if ( !m_aColWidth.empty() )
            nParagraphWidth = 0;
        else
            nParagraphWidth = (nParagraphWidth - (m_nColumnDistance * (m_nColumnCount - 1))) / m_nColumnCount;
    }
    if ( nParagraphWidth > 0 )
    {
        const OUString & sPropRelativeWidth = getPropertyName(PROP_RELATIVE_WIDTH);
        for ( const auto& xShape : m_xRelativeWidthShapes )
        {
            const uno::Reference<beans::XPropertySet> xShapePropertySet( xShape, uno::UNO_QUERY );
            if ( xShapePropertySet->getPropertySetInfo()->hasPropertyByName(sPropRelativeWidth) )
            {
                sal_uInt16 nPercent = 0;
                try
                {
                    xShapePropertySet->getPropertyValue(sPropRelativeWidth) >>= nPercent;
                }
                catch (const css::uno::RuntimeException& e)
                {
                    // May happen e.g. when text frame has no frame format
                    // See sw/qa/extras/ooxmlimport/data/n779627.docx
                    SAL_WARN("writerfilter", "Getting relative width failed. " << e.Message);
                }
                if ( nPercent )
                {
                    const sal_Int32 nWidth = nParagraphWidth * nPercent / 100;
                    xShape->setSize( awt::Size( nWidth, xShape->getSize().Height ) );
                }
            }
        }
    }
    m_xRelativeWidthShapes.clear();
 
    rDM_Impl.SetIsLastSectionGroup( false );
    rDM_Impl.SetIsFirstParagraphInSection( true );
 
    if ( !rDM_Impl.IsInFootOrEndnote() && !rDM_Impl.IsInComments() )
    {
        rDM_Impl.m_StreamStateStack.top().bHasFtn = false;
        rDM_Impl.m_bHasFtnSep = false;
    }
}
 
// Clear the flag that says we should take the header/footer content from
// the previous section.  This should be called when we encounter a header
// or footer definition for this section.
void SectionPropertyMap::clearHeaderFooterLinkToPrevious(PagePartType ePartType, PageType eType)
{
    if (ePartType == PagePartType::Header)
    {
        switch (eType)
        {
            case PageType::FIRST: m_bFirstPageHeaderLinkToPrevious = false; break;
            case PageType::LEFT:  m_bEvenPageHeaderLinkToPrevious = false; break;
            case PageType::RIGHT: m_bDefaultHeaderLinkToPrevious = false; break;
        }
    }
    else if (ePartType == PagePartType::Footer)
    {
        switch (eType)
        {
            case PageType::FIRST: m_bFirstPageFooterLinkToPrevious = false; break;
            case PageType::LEFT:  m_bEvenPageFooterLinkToPrevious = false; break;
            case PageType::RIGHT: m_bDefaultFooterLinkToPrevious = false; break;
        }
    }
}
 
namespace {
 
class NamedPropertyValue
{
private:
    OUString m_aName;
 
public:
    explicit NamedPropertyValue( OUString i_aStr )
        : m_aName(std::move( i_aStr ))
    {
    }
 
    bool operator() ( beans::PropertyValue const & aVal )
    {
        return aVal.Name == m_aName;
    }
};
 
}
 
void SectionPropertyMap::ApplyProperties_( const rtl::Reference<SwXPageStyle>& xStyle )
{
    if ( !xStyle.is() )
        return;
    std::vector< OUString > vNames;
    std::vector< uno::Any > vValues;
    {
        // Convert GetPropertyValues() value into something useful
        const uno::Sequence< beans::PropertyValue > vPropVals = GetPropertyValues();
 
        //Temporarily store the items that are in grab bags
        uno::Sequence< beans::PropertyValue > vCharVals;
        uno::Sequence< beans::PropertyValue > vParaVals;
        const beans::PropertyValue* pCharGrabBag = std::find_if( vPropVals.begin(), vPropVals.end(), NamedPropertyValue( u"CharInteropGrabBag"_ustr ) );
        if ( pCharGrabBag != vPropVals.end() )
            (pCharGrabBag->Value) >>= vCharVals;
        const beans::PropertyValue* pParaGrabBag = std::find_if( vPropVals.begin(), vPropVals.end(), NamedPropertyValue( u"ParaInteropGrabBag"_ustr ) );
        if ( pParaGrabBag != vPropVals.end() )
            (pParaGrabBag->Value) >>= vParaVals;
 
        for ( const beans::PropertyValue* pIter = vPropVals.begin(); pIter != vPropVals.end(); ++pIter )
        {
            if ( pIter != pCharGrabBag && pIter != pParaGrabBag
                 && pIter->Name != "IsProtected" //section-only property
               )
            {
                vNames.push_back( pIter->Name );
                vValues.push_back( pIter->Value );
            }
        }
        for (const beans::PropertyValue& v : vCharVals)
        {
            vNames.push_back( v.Name );
            vValues.push_back( v.Value );
        }
        for (const beans::PropertyValue& v : vParaVals)
        {
            vNames.push_back( v.Name );
            vValues.push_back( v.Value );
        }
    }
    try
    {
        xStyle->setPropertyValues( comphelper::containerToSequence( vNames ), comphelper::containerToSequence( vValues ) );
        return;
    }
    catch ( const uno::Exception& )
    {
        TOOLS_WARN_EXCEPTION( "writerfilter", "SectionPropertyMap::ApplyProperties_" );
    }
}
 
sal_Int32 SectionPropertyMap::GetPageWidth() const
{
    return getProperty( PROP_WIDTH )->second.get<sal_Int32>();
}
 
StyleSheetPropertyMap::StyleSheetPropertyMap()
    : mnListLevel( -1 )
    , mnOutlineLevel( -1 )
{
}
 
ParagraphProperties::ParagraphProperties()
    : m_bFrameMode( false )
    , m_nDropCap( NS_ooxml::LN_Value_doc_ST_DropCap_none )
    , m_nLines( 0 )
    , m_w( -1 )
    , m_h( -1 )
    , m_nWrap( text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE )
    , m_hAnchor( -1 )
    , m_vAnchor( -1 )
    , m_x( -1 )
    , m_bxValid( false )
    , m_y( -1 )
    , m_byValid( false )
    , m_hSpace( -1 )
    , m_vSpace( -1 )
    , m_hRule( -1 )
    , m_xAlign( -1 )
    , m_yAlign( -1 )
    , m_nDropCapLength( 0 )
{
}
 
void ParagraphProperties::ResetFrameProperties()
{
    m_bFrameMode     = false;
    m_nDropCap       = NS_ooxml::LN_Value_doc_ST_DropCap_none;
    m_nLines         = 0;
    m_w              = -1;
    m_h              = -1;
    m_nWrap          = text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE;
    m_hAnchor        = -1;
    m_vAnchor        = -1;
    m_x              = -1;
    m_bxValid        = false;
    m_y              = -1;
    m_byValid        = false;
    m_hSpace         = -1;
    m_vSpace         = -1;
    m_hRule          = -1;
    m_xAlign         = -1;
    m_yAlign         = -1;
    m_nDropCapLength = 0;
}
 
bool TablePropertyMap::getValue( TablePropertyMapTarget eWhich, sal_Int32& nFill )
{
    if ( eWhich < TablePropertyMapTarget_MAX )
    {
        if ( m_aValidValues[eWhich].bValid )
            nFill = m_aValidValues[eWhich].nValue;
        return m_aValidValues[eWhich].bValid;
    }
    else
    {
        OSL_FAIL( "invalid TablePropertyMapTarget" );
        return false;
    }
}
 
void TablePropertyMap::setValue( TablePropertyMapTarget eWhich, sal_Int32 nSet )
{
    if ( eWhich < TablePropertyMapTarget_MAX )
    {
        m_aValidValues[eWhich].bValid = true;
        m_aValidValues[eWhich].nValue = nSet;
    }
    else
        OSL_FAIL( "invalid TablePropertyMapTarget" );
}
 
void TablePropertyMap::insertTableProperties( const PropertyMap* pMap, const bool bOverwrite )
{
#ifdef DBG_UTIL
    TagLogger::getInstance().startElement( "TablePropertyMap.insertTableProperties" );
    pMap->dumpXml();
#endif
 
    const TablePropertyMap* pSource = dynamic_cast< const TablePropertyMap* >(pMap);
    if ( pSource )
    {
        for ( sal_Int32 eTarget = TablePropertyMapTarget_START;
            eTarget < TablePropertyMapTarget_MAX; ++eTarget )
        {
            if ( pSource->m_aValidValues[eTarget].bValid && (bOverwrite || !m_aValidValues[eTarget].bValid) )
            {
                m_aValidValues[eTarget].bValid = true;
                m_aValidValues[eTarget].nValue = pSource->m_aValidValues[eTarget].nValue;
            }
        }
    }
 
#ifdef DBG_UTIL
    dumpXml();
    TagLogger::getInstance().endElement();
#endif
}
 
} // namespace writerfilter
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V547 Expression is always false.

V547 Expression 'eWrap == text::WrapTextMode_THROUGH' is always true.

V547 Expression 'bIsLandscape != bPrevIsLandscape' is always false.

V547 Expression is always true.

V614 Uninitialized variable 'eBreakType' used.

V614 Uninitialized variable 'eBreakType' used.

V547 Expression '!bEvenAndOdd' is always true.

V547 Expression '!bEvenAndOdd' is always true.

V547 Expression '!m_bTitlePage' is always true.

V547 Expression 'bSourceHasHeader' is always false.

V547 Expression 'bSourceHasFooter' is always false.

V547 Expression '!bSourceFirstIsShared' is always false.

V547 Expression '!bOpaque' is always false.

V547 Expression 'nPercent' is always false.

V601 The 'm_nLnnMin + 1' value is implicitly cast to the bool type. Inspect the first argument.

V636 The 'm_aColDistance[nCol - 1] / 2' expression was implicitly cast from 'int' type to 'double' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;.

V636 The 'm_aColDistance[nCol] / 2' expression was implicitly cast from 'int' type to 'double' type. Consider utilizing an explicit type cast to avoid the loss of a fractional part. An example: double A = (double)(X) / Y;.