/* -*- 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 <sal/log.hxx>
 
#include "xmlexprt.hxx"
#include "XMLConverter.hxx"
#include "xmlstyle.hxx"
#include <unonames.hxx>
#include <document.hxx>
#include <olinetab.hxx>
#include <formulacell.hxx>
#include <rangenam.hxx>
#include "XMLTableMasterPageExport.hxx"
#include <drwlayer.hxx>
#include "XMLExportDataPilot.hxx"
#include "XMLExportDatabaseRanges.hxx"
#include "XMLExportDDELinks.hxx"
#include "XMLExportIterator.hxx"
#include "XMLColumnRowGroupExport.hxx"
#include "XMLStylesExportHelper.hxx"
#include "XMLChangeTrackingExportHelper.hxx"
#include <sheetdata.hxx>
#include <docoptio.hxx>
#include "XMLExportSharedData.hxx"
#include <chgviset.hxx>
#include <docuno.hxx>
#include <textuno.hxx>
#include <chartlis.hxx>
#include <scitems.hxx>
#include <docpool.hxx>
#include <userdat.hxx>
#include <chgtrack.hxx>
#include <rangeutl.hxx>
#include <postit.hxx>
#include <externalrefmgr.hxx>
#include <editutil.hxx>
#include <tabprotection.hxx>
#include "cachedattraccess.hxx"
#include <colorscale.hxx>
#include <conditio.hxx>
#include <cellvalue.hxx>
#include <stylehelper.hxx>
#include <edittextiterator.hxx>
#include "editattributemap.hxx"
#include <arealink.hxx>
#include <datastream.hxx>
#include <documentlinkmgr.hxx>
#include <tokenstringcontext.hxx>
#include <cellform.hxx>
#include <datamapper.hxx>
#include <datatransformation.hxx>
#include "SparklineGroupsExport.hxx"
#include <SparklineList.hxx>
 
#include <xmloff/xmltoken.hxx>
#include <xmloff/xmlnamespace.hxx>
#include <xmloff/xmluconv.hxx>
#include <xmloff/namespacemap.hxx>
#include <xmloff/families.hxx>
#include <xmloff/numehelp.hxx>
#include <xmloff/txtparae.hxx>
#include <editeng/autokernitem.hxx>
#include <editeng/charreliefitem.hxx>
#include <editeng/charscaleitem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/contouritem.hxx>
#include <editeng/crossedoutitem.hxx>
#include <editeng/emphasismarkitem.hxx>
#include <editeng/escapementitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/kernitem.hxx>
#include <editeng/langitem.hxx>
#include <editeng/postitem.hxx>
#include <editeng/section.hxx>
#include <editeng/shdditem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/wghtitem.hxx>
#include <editeng/wrlmitem.hxx>
#include <editeng/xmlcnitm.hxx>
#include <editeng/flditem.hxx>
#include <editeng/eeitem.hxx>
#include <formula/errorcodes.hxx>
#include <xmloff/xmlerror.hxx>
#include <xmloff/XMLEventExport.hxx>
#include <xmloff/xmlprmap.hxx>
#include <xmloff/ProgressBarHelper.hxx>
#include <xmloff/table/XMLTableExport.hxx>
 
#include <sax/tools/converter.hxx>
#include <tools/fldunit.hxx>
 
#include <rtl/ustring.hxx>
 
#include <tools/color.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <rtl/math.hxx>
#include <svl/numformat.hxx>
#include <svl/zforlist.hxx>
#include <comphelper/base64.hxx>
#include <comphelper/extract.hxx>
#include <svx/svdoashp.hxx>
#include <svx/svdobj.hxx>
#include <svx/svdocapt.hxx>
#include <svx/svdomeas.hxx>
#include <svx/svdmodel.hxx>
#include <vcl/svapp.hxx>
#include <docmodel/theme/Theme.hxx>
 
#include <comphelper/processfactory.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XNamed.hpp>
#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
#include <com/sun/star/form/XFormsSupplier2.hpp>
#include <com/sun/star/io/XActiveDataSource.hpp>
#include <com/sun/star/io/XSeekable.hpp>
#include <com/sun/star/sheet/XUsedAreaCursor.hpp>
#include <com/sun/star/sheet/XCellRangeAddressable.hpp>
#include <com/sun/star/sheet/XPrintAreas.hpp>
#include <com/sun/star/sheet/XUniqueCellFormatRangesSupplier.hpp>
#include <com/sun/star/sheet/XLabelRange.hpp>
#include <com/sun/star/sheet/NamedRangeFlag.hpp>
#include <com/sun/star/sheet/XSheetCellCursor.hpp>
#include <com/sun/star/sheet/XSheetCellRanges.hpp>
#include <com/sun/star/sheet/XSheetLinkable.hpp>
#include <com/sun/star/sheet/GlobalSheetSettings.hpp>
#include <com/sun/star/table/XColumnRowRange.hpp>
#include <com/sun/star/util/XProtectable.hpp>
#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
#include <com/sun/star/chart2/XChartDocument.hpp>
#include <com/sun/star/chart2/data/XRangeXMLConversion.hpp>
#include <com/sun/star/chart2/data/XDataReceiver.hpp>
 
#include <com/sun/star/document/XDocumentProperties.hpp>
#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
 
#include "XMLCodeNameProvider.hxx"
 
#include <sfx2/linkmgr.hxx>
#include <sfx2/objsh.hxx>
 
#include <memory>
#include <vector>
#include <vbahelper/vbaaccesshelper.hxx>
#include <officecfg/Office/Common.hxx>
 
namespace com::sun::star::uno { class XComponentContext; }
 
 
 
//! not found in unonames.hxx
constexpr OUString SC_LAYERID = u"LayerID"_ustr;
 
#define SC_VIEWCHANGES_COUNT                        13
#define SC_SHOW_CHANGES                             0
#define SC_SHOW_ACCEPTED_CHANGES                    1
#define SC_SHOW_REJECTED_CHANGES                    2
#define SC_SHOW_CHANGES_BY_DATETIME                 3
#define SC_SHOW_CHANGES_BY_DATETIME_MODE            4
#define SC_SHOW_CHANGES_BY_DATETIME_FIRST_DATETIME  5
#define SC_SHOW_CHANGES_BY_DATETIME_SECOND_DATETIME 6
#define SC_SHOW_CHANGES_BY_AUTHOR                   7
#define SC_SHOW_CHANGES_BY_AUTHOR_NAME              8
#define SC_SHOW_CHANGES_BY_COMMENT                  9
#define SC_SHOW_CHANGES_BY_COMMENT_TEXT             10
#define SC_SHOW_CHANGES_BY_RANGES                   11
#define SC_SHOW_CHANGES_BY_RANGES_LIST              12
 
using namespace formula;
using namespace com::sun::star;
using namespace xmloff::token;
using ::std::vector;
using ::com::sun::star::uno::UNO_QUERY;
 
namespace
{
OUString lcl_RangeSequenceToString(
    const uno::Sequence< OUString > & rRanges,
    const uno::Reference< chart2::data::XRangeXMLConversion > & xFormatConverter )
{
    OUStringBuffer aResult;
    const sal_Int32 nMaxIndex( rRanges.getLength() - 1 );
    const sal_Unicode cSep(' ');
    for( sal_Int32 i=0; i<=nMaxIndex; ++i )
    {
        OUString aRange( rRanges[i] );
        if( xFormatConverter.is())
            aRange = xFormatConverter->convertRangeToXML( aRange );
        aResult.append( aRange );
        if( i < nMaxIndex )
            aResult.append( cSep );
    }
    return aResult.makeStringAndClear();
}
 
OUString lcl_GetFormattedString(ScDocument* pDoc, const ScRefCellValue& rCell, const ScAddress& rAddr)
{
    // return text/edit cell string content, with line feeds in edit cells
 
    if (!pDoc)
        return OUString();
 
    switch (rCell.getType())
    {
        case CELLTYPE_STRING:
        {
            const Color* pColor;
            sal_uInt32 nFormat = pDoc->GetNumberFormat(ScRange(rAddr));
            return ScCellFormat::GetString(rCell, nFormat, &pColor, nullptr, *pDoc);
        }
        case CELLTYPE_EDIT:
        {
            const EditTextObject* pData = rCell.getEditText();
            if (!pData)
                return OUString();
 
            EditEngine& rEngine = pDoc->GetEditEngine();
            rEngine.SetText(*pData);
            return rEngine.GetText();
        }
        break;
        default:
            ;
    }
 
    return OUString();
}
 
} // anonymous namespace
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
Calc_XMLExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new ScXMLExport(context, u"com.sun.star.comp.Calc.XMLExporter"_ustr, SvXMLExportFlags::ALL));
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
Calc_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new ScXMLExport(context, u"com.sun.star.comp.Calc.XMLMetaExporter"_ustr, SvXMLExportFlags::META));
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
Calc_XMLStylesExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new ScXMLExport(context, u"com.sun.star.comp.Calc.XMLStylesExporter"_ustr, SvXMLExportFlags::STYLES|SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::FONTDECLS));
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
Calc_XMLContentExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new ScXMLExport(context, u"com.sun.star.comp.Calc.XMLContentExporter"_ustr, SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::CONTENT|SvXMLExportFlags::SCRIPTS|SvXMLExportFlags::FONTDECLS));
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
Calc_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new ScXMLExport(context, u"com.sun.star.comp.Calc.XMLSettingsExporter"_ustr, SvXMLExportFlags::SETTINGS));
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
Calc_XMLOasisExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new ScXMLExport(context, u"com.sun.star.comp.Calc.XMLOasisExporter"_ustr, SvXMLExportFlags::ALL|SvXMLExportFlags::OASIS));
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
Calc_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new ScXMLExport(context, u"com.sun.star.comp.Calc.XMLOasisMetaExporter"_ustr, SvXMLExportFlags::META|SvXMLExportFlags::OASIS));
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
Calc_XMLOasisStylesExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new ScXMLExport(context, u"com.sun.star.comp.Calc.XMLOasisStylesExporter"_ustr, SvXMLExportFlags::STYLES|SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::FONTDECLS|SvXMLExportFlags::OASIS));
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
Calc_XMLOasisContentExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new ScXMLExport(context, u"com.sun.star.comp.Calc.XMLOasisContentExporter"_ustr, SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::CONTENT|SvXMLExportFlags::SCRIPTS|SvXMLExportFlags::FONTDECLS|SvXMLExportFlags::OASIS));
}
 
extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
Calc_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const &)
{
    return cppu::acquire(new ScXMLExport(context, u"com.sun.star.comp.Calc.XMLOasisSettingsExporter"_ustr, SvXMLExportFlags::SETTINGS|SvXMLExportFlags::OASIS));
}
 
namespace {
 
class ScXMLShapeExport : public XMLShapeExport
{
public:
    explicit ScXMLShapeExport(SvXMLExport& rExp)
        : XMLShapeExport(rExp,
                         // chain text attributes
                         XMLTextParagraphExport::CreateParaExtPropMapper(rExp))
    {
    }
 
    /** is called before a shape element for the given XShape is exported */
    virtual void onExport( const uno::Reference < drawing::XShape >& xShape ) override;
};
 
}
 
void ScXMLShapeExport::onExport( const uno::Reference < drawing::XShape >& xShape )
{
    uno::Reference< beans::XPropertySet > xShapeProp( xShape, uno::UNO_QUERY );
    if( xShapeProp.is() )
    {
        sal_Int16 nLayerID = 0;
        if( (xShapeProp->getPropertyValue( SC_LAYERID ) >>= nLayerID) && (SdrLayerID(nLayerID) == SC_LAYER_BACK) )
            GetExport().AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE_BACKGROUND, XML_TRUE);
    }
}
 
sal_Int16 ScXMLExport::GetMeasureUnit()
{
    css::uno::Reference<css::sheet::XGlobalSheetSettings> xProperties =
                css::sheet::GlobalSheetSettings::create( comphelper::getProcessComponentContext() );
    const FieldUnit eFieldUnit = static_cast<FieldUnit>(xProperties->getMetric());
    return SvXMLUnitConverter::GetMeasureUnit(eFieldUnit);
}
 
ScXMLExport::ScXMLExport(
    const css::uno::Reference< css::uno::XComponentContext >& rContext,
    OUString const & implementationName, SvXMLExportFlags nExportFlag)
:   SvXMLExport(
        rContext, implementationName, GetMeasureUnit(), XML_SPREADSHEET, nExportFlag ),
    pDoc(nullptr),
    nSourceStreamPos(0),
    pCurrentCell(nullptr),
    nOpenRow(-1),
    nProgressCount(0),
    nCurrentTable(0),
    bHasRowHeader(false),
    bRowHeaderOpen(false)
{
    if (getExportFlags() & SvXMLExportFlags::CONTENT)
    {
        pGroupColumns.reset( new ScMyOpenCloseColumnRowGroup(*this, XML_TABLE_COLUMN_GROUP) );
        pGroupRows.reset( new ScMyOpenCloseColumnRowGroup(*this, XML_TABLE_ROW_GROUP) );
        pColumnStyles.reset( new ScColumnStyles() );
        pRowStyles.reset( new ScRowStyles() );
        pRowFormatRanges.reset( new ScRowFormatRanges() );
        pMergedRangesContainer.reset( new ScMyMergedRangesContainer() );
        pValidationsContainer.reset( new ScMyValidationsContainer() );
        mpCellsItr.reset(new ScMyNotEmptyCellsIterator(*this));
        pDefaults.reset( new ScMyDefaultStyles );
    }
    pCellStyles.reset( new ScFormatRangeStyles() );
 
    // document is not set here - create ScChangeTrackingExportHelper later
 
    xScPropHdlFactory = new XMLScPropHdlFactory;
    xCellStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScCellStylesProperties, xScPropHdlFactory, true);
    xColumnStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScColumnStylesProperties, xScPropHdlFactory, true);
    xRowStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScRowStylesProperties, xScPropHdlFactory, true);
    xTableStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScTableStylesProperties, xScPropHdlFactory, true);
    xCellStylesExportPropertySetMapper = new ScXMLCellExportPropertyMapper(xCellStylesPropertySetMapper);
    xCellStylesExportPropertySetMapper->ChainExportMapper(XMLTextParagraphExport::CreateParaExtPropMapper(*this));
    xColumnStylesExportPropertySetMapper = new ScXMLColumnExportPropertyMapper(xColumnStylesPropertySetMapper);
    xRowStylesExportPropertySetMapper = new ScXMLRowExportPropertyMapper(xRowStylesPropertySetMapper);
    xTableStylesExportPropertySetMapper = new ScXMLTableExportPropertyMapper(xTableStylesPropertySetMapper);
 
    GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_CELL, XML_STYLE_FAMILY_TABLE_CELL_STYLES_NAME,
        xCellStylesExportPropertySetMapper, XML_STYLE_FAMILY_TABLE_CELL_STYLES_PREFIX);
    GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_COLUMN, XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_NAME,
        xColumnStylesExportPropertySetMapper, XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_PREFIX);
    GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_ROW, XML_STYLE_FAMILY_TABLE_ROW_STYLES_NAME,
        xRowStylesExportPropertySetMapper, XML_STYLE_FAMILY_TABLE_ROW_STYLES_PREFIX);
    GetAutoStylePool()->AddFamily(XmlStyleFamily::TABLE_TABLE, XML_STYLE_FAMILY_TABLE_TABLE_STYLES_NAME,
        xTableStylesExportPropertySetMapper, XML_STYLE_FAMILY_TABLE_TABLE_STYLES_PREFIX);
 
    GetShapeExport(); // make sure the graphics styles family is added
 
    if( !(getExportFlags() & (SvXMLExportFlags::STYLES|SvXMLExportFlags::AUTOSTYLES|SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT)) )
        return;
 
    // This name is reserved for the external ref cache tables.  This
    // should not conflict with user-defined styles since this name is
    // used for a table style which is not available in the UI.
    sExternalRefTabStyleName = "ta_extref";
    GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_TABLE, sExternalRefTabStyleName);
 
    sAttrName = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_NAME));
    sAttrStyleName = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_STYLE_NAME));
    sAttrColumnsRepeated = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_NUMBER_COLUMNS_REPEATED));
    sAttrFormula = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_FORMULA));
    sAttrStringValue = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_OFFICE, GetXMLToken(XML_STRING_VALUE));
    sAttrValueType = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_OFFICE, GetXMLToken(XML_VALUE_TYPE));
    sElemCell = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_TABLE_CELL));
    sElemCoveredCell = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_COVERED_TABLE_CELL));
    sElemCol = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_TABLE_COLUMN));
    sElemRow = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_TABLE_ROW));
    sElemTab = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TABLE, GetXMLToken(XML_TABLE));
    sElemP = GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_TEXT, GetXMLToken(XML_P));
}
 
ScXMLExport::~ScXMLExport()
{
    pGroupColumns.reset();
    pGroupRows.reset();
    pColumnStyles.reset();
    pRowStyles.reset();
    pCellStyles.reset();
    pRowFormatRanges.reset();
    pMergedRangesContainer.reset();
    pValidationsContainer.reset();
    pChangeTrackingExportHelper.reset();
    pDefaults.reset();
    pNumberFormatAttributesExportHelper.reset();
}
 
void ScXMLExport::SetSourceStream( const uno::Reference<io::XInputStream>& xNewStream )
{
    xSourceStream = xNewStream;
 
    if ( !xSourceStream.is() )
        return;
 
    // make sure it's a plain UTF-8 stream as written by OOo itself
 
    const char pXmlHeader[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
    sal_Int32 nLen = strlen(pXmlHeader);
 
    uno::Sequence<sal_Int8> aFileStart(nLen);
    sal_Int32 nRead = xSourceStream->readBytes( aFileStart, nLen );
 
    if ( nRead != nLen || memcmp( aFileStart.getConstArray(), pXmlHeader, nLen ) != 0 )
    {
        // invalid - ignore stream, save normally
        xSourceStream.clear();
    }
    else
    {
        // keep track of the bytes already read
        nSourceStreamPos = nRead;
 
        const ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(GetModel())->GetSheetSaveData();
        if (pSheetData)
        {
            // add the loaded namespaces to the name space map
 
            if ( !pSheetData->AddLoadedNamespaces( GetNamespaceMap_() ) )
            {
                // conflicts in the namespaces - ignore the stream, save normally
                xSourceStream.clear();
            }
        }
    }
}
 
sal_Int32 ScXMLExport::GetNumberFormatStyleIndex(sal_Int32 nNumFmt) const
{
    NumberFormatIndexMap::const_iterator itr = aNumFmtIndexMap.find(nNumFmt);
    if (itr == aNumFmtIndexMap.end())
        return -1;
 
    return itr->second;
}
 
void ScXMLExport::CollectSharedData(SCTAB& nTableCount, sal_Int32& nShapesCount)
{
    if (!GetModel().is())
        return;
 
    uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc(GetModel(), uno::UNO_QUERY);
    if (!xSpreadDoc.is())
        return;
 
    uno::Reference<container::XIndexAccess> xIndex(xSpreadDoc->getSheets(), uno::UNO_QUERY);
    if (!xIndex.is())
        return;
 
    nTableCount = xIndex->getCount();
    if (!pSharedData)
        pSharedData.reset(new ScMySharedData(nTableCount));
 
    for (SCTAB nTable = 0; nTable < nTableCount; ++nTable)
    {
        nCurrentTable = sal::static_int_cast<sal_uInt16>(nTable);
        uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(xIndex->getByIndex(nTable), uno::UNO_QUERY);
        if (!xDrawPageSupplier.is())
            continue;
 
        uno::Reference<drawing::XDrawPage> xDrawPage(xDrawPageSupplier->getDrawPage());
        ScMyDrawPage aDrawPage;
        aDrawPage.bHasForms = false;
        aDrawPage.xDrawPage.set(xDrawPage);
        pSharedData->AddDrawPage(aDrawPage, nTable);
        if (!xDrawPage.is())
            continue;
 
        sal_Int32 nShapes = xDrawPage->getCount();
        for (sal_Int32 nShape = 0; nShape < nShapes; ++nShape)
        {
            uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(nShape), uno::UNO_QUERY);
            if (!xShape.is())
                continue;
 
            uno::Reference<beans::XPropertySet> xShapeProp(xShape, uno::UNO_QUERY);
            if (!xShapeProp.is())
                continue;
 
            sal_Int16 nLayerID = 0;
            bool bExtracted = xShapeProp->getPropertyValue(SC_LAYERID) >>= nLayerID;
            if (!bExtracted)
                continue;
 
            if ((SdrLayerID(nLayerID) == SC_LAYER_INTERN) || (SdrLayerID(nLayerID) == SC_LAYER_HIDDEN))
            {
                CollectInternalShape(xShape);
                continue;
            }
 
            ++nShapesCount;
 
            SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(xShape);
            if (!pSdrObj)
                continue;
 
            if (ScDrawObjData *pAnchor = ScDrawLayer::GetNonRotatedObjData(pSdrObj))
            {
                ScMyShape aMyShape;
                aMyShape.aAddress = pAnchor->maStart;
                SAL_WARN_IF(aMyShape.aAddress.Tab() != nTable, "sc", "not anchored to current sheet!");
                aMyShape.aAddress.SetTab(nTable);
                aMyShape.aEndAddress = pAnchor->maEnd;
                aMyShape.aEndAddress.SetTab( nTable );
                aMyShape.nEndX = pAnchor->maEndOffset.X();
                aMyShape.nEndY = pAnchor->maEndOffset.Y();
                aMyShape.xShape = std::move(xShape);
                pSharedData->AddNewShape(aMyShape);
                pSharedData->SetLastColumn(nTable, pAnchor->maStart.Col());
                pSharedData->SetLastRow(nTable, pAnchor->maStart.Row());
            }
            else
                pSharedData->AddTableShape(nTable, xShape);
        }
    }
}
 
void ScXMLExport::CollectShapesAutoStyles(SCTAB nTableCount)
{
    // #i84077# To avoid compiler warnings about uninitialized aShapeItr,
    // it's initialized using this dummy list. The iterator contains shapes
    // from all sheets, so it can't be declared inside the nTable loop where
    // it is used.
    ScMyShapeList aDummyInitList;
 
    pSharedData->SortShapesContainer();
    pSharedData->SortNoteShapes();
    const ScMyShapeList* pShapeList(nullptr);
    ScMyShapeList::const_iterator aShapeItr = aDummyInitList.end();
    if (pSharedData->GetShapesContainer())
    {
        pShapeList = &pSharedData->GetShapesContainer()->GetShapes();
        aShapeItr = pShapeList->begin();
    }
    if (pSharedData->HasDrawPage())
    {
        for (SCTAB nTable = 0; nTable < nTableCount; ++nTable)
        {
            uno::Reference<drawing::XDrawPage> xDrawPage(pSharedData->GetDrawPage(nTable));
 
            if (xDrawPage.is())
            {
                GetShapeExport()->seekShapes(xDrawPage);
                uno::Reference< form::XFormsSupplier2 > xFormsSupplier( xDrawPage, uno::UNO_QUERY );
                if( xFormsSupplier.is() && xFormsSupplier->hasForms() )
                {
                    GetFormExport()->examineForms(xDrawPage);
                    pSharedData->SetDrawPageHasForms(nTable, true);
                }
                ScMyTableShapes* pTableShapes(pSharedData->GetTableShapes());
                if (pTableShapes)
                {
                    for (const auto& rxShape : (*pTableShapes)[nTable])
                    {
                        GetShapeExport()->collectShapeAutoStyles(rxShape);
                        IncrementProgressBar(false);
                    }
                }
                if (pShapeList)
                {
                    ScMyShapeList::const_iterator aEndItr(pShapeList->end());
                    while ( aShapeItr != aEndItr && ( aShapeItr->aAddress.Tab() == nTable ) )
                    {
                        GetShapeExport()->collectShapeAutoStyles(aShapeItr->xShape);
                        IncrementProgressBar(false);
                        ++aShapeItr;
                    }
                }
                if (pSharedData->GetNoteShapes())
                {
                    const ScMyNoteShapeList& rNoteShapes = pSharedData->GetNoteShapes()->GetNotes();
                    for (const auto& rNoteShape : rNoteShapes)
                    {
                        if ( rNoteShape.aPos.Tab() == nTable )
                            GetShapeExport()->collectShapeAutoStyles(rNoteShape.xShape);
                    }
                }
            }
        }
    }
    pSharedData->SortNoteShapes(); // sort twice, because some more shapes are added
}
 
void ScXMLExport::ExportMeta_()
{
    sal_Int32 nCellCount(pDoc ? pDoc->GetCellCount() : 0);
    SCTAB nTableCount(0);
    sal_Int32 nShapesCount(0);
    GetAutoStylePool()->ClearEntries();
    CollectSharedData(nTableCount, nShapesCount);
 
    uno::Sequence<beans::NamedValue> stats
    {
        { u"TableCount"_ustr,  uno::Any(static_cast<sal_Int32>(nTableCount)) },
        { u"CellCount"_ustr,   uno::Any(nCellCount) },
        { u"ObjectCount"_ustr, uno::Any(nShapesCount) }
    };
 
    // update document statistics at the model
    uno::Reference<document::XDocumentPropertiesSupplier> xPropSup(GetModel(),
        uno::UNO_QUERY_THROW);
    uno::Reference<document::XDocumentProperties> xDocProps(
        xPropSup->getDocumentProperties());
    if (xDocProps.is()) {
        xDocProps->setDocumentStatistics(stats);
    }
 
    // export document properties
    SvXMLExport::ExportMeta_();
}
 
void ScXMLExport::ExportFontDecls_()
{
    GetFontAutoStylePool(); // make sure the pool is created
    SvXMLExport::ExportFontDecls_();
}
 
table::CellRangeAddress ScXMLExport::GetEndAddress(const uno::Reference<sheet::XSpreadsheet>& xTable)
{
    table::CellRangeAddress aCellAddress;
    uno::Reference<sheet::XSheetCellCursor> xCursor(xTable->createCursor());
    uno::Reference<sheet::XUsedAreaCursor> xUsedArea (xCursor, uno::UNO_QUERY);
    uno::Reference<sheet::XCellRangeAddressable> xCellAddress (xCursor, uno::UNO_QUERY);
    if (xUsedArea.is() && xCellAddress.is())
    {
        xUsedArea->gotoEndOfUsedArea(true);
        aCellAddress = xCellAddress->getRangeAddress();
    }
    return aCellAddress;
}
 
void ScXMLExport::GetAreaLinks( ScMyAreaLinksContainer& rAreaLinks )
{
    if (pDoc->GetLinkManager())
    {
        const sfx2::SvBaseLinks& rLinks = pDoc->GetLinkManager()->GetLinks();
        for (const auto & rLink : rLinks)
        {
            ScAreaLink *pLink = dynamic_cast<ScAreaLink*>(rLink.get());
            if (pLink)
            {
                ScMyAreaLink aAreaLink;
                aAreaLink.aDestRange = pLink->GetDestArea();
                aAreaLink.sSourceStr = pLink->GetSource();
                aAreaLink.sFilter = pLink->GetFilter();
                aAreaLink.sFilterOptions = pLink->GetOptions();
                aAreaLink.sURL = pLink->GetFile();
                aAreaLink.nRefreshDelaySeconds = pLink->GetRefreshDelaySeconds();
                rAreaLinks.AddNewAreaLink( aAreaLink );
            }
        }
    }
    rAreaLinks.Sort();
}
 
// core implementation
void ScXMLExport::GetDetectiveOpList( ScMyDetectiveOpContainer& rDetOp )
{
    if (!pDoc)
        return;
 
    ScDetOpList* pOpList(pDoc->GetDetOpList());
    if( !pOpList )
        return;
 
    size_t nCount = pOpList->Count();
    for (size_t nIndex = 0; nIndex < nCount; ++nIndex )
    {
        const ScDetOpData& rDetData = pOpList->GetObject( nIndex);
        const ScAddress& rDetPos = rDetData.GetPos();
        SCTAB nTab = rDetPos.Tab();
        if ( nTab < pDoc->GetTableCount() )
        {
            rDetOp.AddOperation( rDetData.GetOperation(), rDetPos, static_cast<sal_uInt32>( nIndex) );
 
            // cells with detective operations are written even if empty
            pSharedData->SetLastColumn( nTab, rDetPos.Col() );
            pSharedData->SetLastRow( nTab, rDetPos.Row() );
        }
    }
    rDetOp.Sort();
}
 
void ScXMLExport::WriteSingleColumn(const sal_Int32 nRepeatColumns, const sal_Int32 nStyleIndex,
    const sal_Int32 nIndex, const bool bIsAutoStyle, const bool bIsVisible)
{
    CheckAttrList();
    // tdf#138466
    if (nStyleIndex != -1)
        AddAttribute(sAttrStyleName, pColumnStyles->GetStyleNameByIndex(nStyleIndex));
    if (!bIsVisible)
        AddAttribute(XML_NAMESPACE_TABLE, XML_VISIBILITY, XML_COLLAPSE);
    if (nRepeatColumns > 1)
    {
        OUString sOUEndCol(OUString::number(nRepeatColumns));
        AddAttribute(sAttrColumnsRepeated, sOUEndCol);
    }
    if (nIndex != -1)
        AddAttribute(XML_NAMESPACE_TABLE, XML_DEFAULT_CELL_STYLE_NAME, pCellStyles->GetStyleNameByIndex(nIndex, bIsAutoStyle));
    SvXMLElementExport aElemC(*this, sElemCol, true, true);
}
 
void ScXMLExport::WriteColumn(const sal_Int32 nColumn, const sal_Int32 nRepeatColumns,
    const sal_Int32 nStyleIndex, const bool bIsVisible)
{
    sal_Int32 nRepeat(1);
    sal_Int32 nPrevIndex(pDefaults->GetColDefaults()[nColumn].nIndex);
    bool bPrevAutoStyle(pDefaults->GetColDefaults()[nColumn].bIsAutoStyle);
    for (sal_Int32 i = nColumn + 1; i < nColumn + nRepeatColumns; ++i)
    {
        if ((pDefaults->GetColDefaults()[i].nIndex != nPrevIndex) ||
            (pDefaults->GetColDefaults()[i].bIsAutoStyle != bPrevAutoStyle))
        {
            WriteSingleColumn(nRepeat, nStyleIndex, nPrevIndex, bPrevAutoStyle, bIsVisible);
            nPrevIndex = pDefaults->GetColDefaults()[i].nIndex;
            bPrevAutoStyle = pDefaults->GetColDefaults()[i].bIsAutoStyle;
            nRepeat = 1;
        }
        else
            ++nRepeat;
    }
    WriteSingleColumn(nRepeat, nStyleIndex, nPrevIndex, bPrevAutoStyle, bIsVisible);
}
 
void ScXMLExport::OpenHeaderColumn()
{
    StartElement( XML_NAMESPACE_TABLE, XML_TABLE_HEADER_COLUMNS, true );
}
 
void ScXMLExport::CloseHeaderColumn()
{
    EndElement(XML_NAMESPACE_TABLE, XML_TABLE_HEADER_COLUMNS, true);
}
 
void ScXMLExport::ExportColumns(const sal_Int32 nTable, const ScRange& aColumnHeaderRange, const bool bHasColumnHeader)
{
    sal_Int32 nColsRepeated (1);
    sal_Int32 nIndex;
    sal_Int32 nPrevColumn(0);
    bool bPrevIsVisible (true);
    bool bWasHeader (false);
    bool bIsClosed (true);
    sal_Int32 nPrevIndex (-1);
    sal_Int32 nColumn;
    for (nColumn = 0; nColumn <= pSharedData->GetLastColumn(nTable); ++nColumn)
    {
        CheckAttrList();
        bool bIsVisible(true);
        nIndex = pColumnStyles->GetStyleNameIndex(nTable, nColumn, bIsVisible);
 
        const bool bIsHeader = bHasColumnHeader && (aColumnHeaderRange.aStart.Col() <= nColumn) && (nColumn <= aColumnHeaderRange.aEnd.Col());
        if (bIsHeader != bWasHeader)
        {
            if (bIsHeader)
            {
                if (nColumn > 0)
                {
                    WriteColumn(nPrevColumn, nColsRepeated, nPrevIndex, bPrevIsVisible);
                    if (pGroupColumns->IsGroupEnd(nColumn - 1))
                        pGroupColumns->CloseGroups(nColumn - 1);
                }
                bPrevIsVisible = bIsVisible;
                nPrevIndex = nIndex;
                nPrevColumn = nColumn;
                nColsRepeated = 1;
                if(pGroupColumns->IsGroupStart(nColumn))
                    pGroupColumns->OpenGroups(nColumn);
                OpenHeaderColumn();
                bWasHeader = true;
                bIsClosed = false;
            }
            else
            {
                WriteColumn(nPrevColumn, nColsRepeated, nPrevIndex, bPrevIsVisible);
                CloseHeaderColumn();
                if (pGroupColumns->IsGroupEnd(nColumn - 1))
                    pGroupColumns->CloseGroups(nColumn - 1);
                if(pGroupColumns->IsGroupStart(nColumn))
                    pGroupColumns->OpenGroups(nColumn);
                bPrevIsVisible = bIsVisible;
                nPrevIndex = nIndex;
                nPrevColumn = nColumn;
                nColsRepeated = 1;
                bWasHeader = false;
                bIsClosed = true;
            }
        }
        else if (nColumn == 0)
        {
            if (pGroupColumns->IsGroupStart(nColumn))
                pGroupColumns->OpenGroups(nColumn);
            bPrevIsVisible = bIsVisible;
            nPrevIndex = nIndex;
        }
        else if ((bIsVisible == bPrevIsVisible) && (nIndex == nPrevIndex) &&
            !pGroupColumns->IsGroupStart(nColumn) && !pGroupColumns->IsGroupEnd(nColumn - 1))
            ++nColsRepeated;
        else
        {
            WriteColumn(nPrevColumn, nColsRepeated, nPrevIndex, bPrevIsVisible);
            if (pGroupColumns->IsGroupEnd(nColumn - 1))
            {
                if (bIsHeader)
                    CloseHeaderColumn();
                pGroupColumns->CloseGroups(nColumn - 1);
                if (bIsHeader)
                    OpenHeaderColumn();
            }
            if (pGroupColumns->IsGroupStart(nColumn))
            {
                if (bIsHeader)
                    CloseHeaderColumn();
                pGroupColumns->OpenGroups(nColumn);
                if (bIsHeader)
                    OpenHeaderColumn();
            }
            bPrevIsVisible = bIsVisible;
            nPrevIndex = nIndex;
            nPrevColumn = nColumn;
            nColsRepeated = 1;
        }
    }
    WriteColumn(nPrevColumn, nColsRepeated, nPrevIndex, bPrevIsVisible);
    if (!bIsClosed)
        CloseHeaderColumn();
    if (pGroupColumns->IsGroupEnd(nColumn - 1))
        pGroupColumns->CloseGroups(nColumn - 1);
}
 
void ScXMLExport::ExportExternalRefCacheStyles()
{
    sal_Int32 nEntryIndex = GetCellStylesPropertySetMapper()->FindEntryIndex(
        "NumberFormat", XML_NAMESPACE_STYLE, u"data-style-name");
 
    if (nEntryIndex < 0)
        // No entry index for the number format is found.
        return;
 
    ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
    if (!pRefMgr->hasExternalData())
        // No external reference data cached.
        return;
 
    // Export each unique number format used in the external ref cache.
    vector<sal_uInt32> aNumFmts;
    pRefMgr->getAllCachedNumberFormats(aNumFmts);
    static constexpr OUString aDefaultStyle(u"Default"_ustr);
    for (const auto& rNumFmt : aNumFmts)
    {
        sal_Int32 nNumFmt = static_cast<sal_Int32>(rNumFmt);
 
        addDataStyle(nNumFmt);
 
        uno::Any aVal;
        aVal <<= nNumFmt;
        vector<XMLPropertyState> aProps;
        aVal <<= aDefaultStyle;
        aProps.emplace_back(nEntryIndex, aVal);
 
        OUString aName;
        sal_Int32 nIndex;
        if (GetAutoStylePool()->Add(aName, XmlStyleFamily::TABLE_CELL, aDefaultStyle, std::move(aProps)))
        {
            pCellStyles->AddStyleName(aName, nIndex);
        }
        else
        {
            bool bIsAuto;
            nIndex = pCellStyles->GetIndexOfStyleName(
                aName, XML_STYLE_FAMILY_TABLE_CELL_STYLES_PREFIX, bIsAuto);
        }
 
        // store the number format to index mapping for later use.
        aNumFmtIndexMap.emplace(nNumFmt, nIndex);
    }
}
 
namespace {
 
void handleFont(
    SvXMLExport & rExport,
    std::vector<XMLPropertyState>& rPropStates,
    const SfxPoolItem* p, const rtl::Reference<XMLPropertySetMapper>& xMapper, std::u16string_view rXMLName )
{
    sal_Int32 nEntryCount = xMapper->GetEntryCount();
 
    // Apparently font info needs special handling.
    const SvxFontItem* pItem = static_cast<const SvxFontItem*>(p);
 
    sal_Int32 nIndexFontName = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, rXMLName, 0);
 
    if (nIndexFontName == -1 || nIndexFontName >= nEntryCount)
        return;
 
    OUString const sFamilyName(pItem->GetFamilyName());
    OUString const sStyleName(pItem->GetStyleName());
    auto const nFamily(pItem->GetFamily());
    auto const nPitch(pItem->GetPitch());
    auto const eEnc(pItem->GetCharSet());
    OUString const sName(rExport.GetFontAutoStylePool()->Find(
                            sFamilyName, sStyleName, nFamily, nPitch, eEnc));
    if (sName.isEmpty())
    {
        assert(false); // fallback to fo:font-family etc. probably not needed
    }
 
    rPropStates.emplace_back(nIndexFontName, uno::Any(sName));
}
 
const SvxFieldData* toXMLPropertyStates(
    SvXMLExport & rExport,
    std::vector<XMLPropertyState>& rPropStates, const std::vector<const SfxPoolItem*>& rSecAttrs,
    const rtl::Reference<XMLPropertySetMapper>& xMapper, const ScXMLEditAttributeMap& rAttrMap )
{
    const SvxFieldData* pField = nullptr;
    sal_Int32 nEntryCount = xMapper->GetEntryCount();
    rPropStates.reserve(rSecAttrs.size());
    for (const SfxPoolItem* p : rSecAttrs)
    {
        if (p->Which() == EE_FEATURE_FIELD)
        {
            pField = static_cast<const SvxFieldItem*>(p)->GetField();
            continue;
        }
 
        const ScXMLEditAttributeMap::Entry* pEntry = rAttrMap.getEntryByItemID(p->Which());
        if (!pEntry)
            continue;
 
        sal_Int32 nIndex = xMapper->GetEntryIndex(pEntry->nmXMLNS, pEntry->maXMLName, 0);
 
        if (nIndex == -1 || nIndex >= nEntryCount)
            continue;
 
        uno::Any aAny;
        switch (p->Which())
        {
            case EE_CHAR_FONTINFO:
                handleFont(rExport, rPropStates, p, xMapper, u"font-name");
            break;
            case EE_CHAR_FONTINFO_CJK:
                handleFont(rExport, rPropStates, p, xMapper, u"font-name-asian");
            break;
            case EE_CHAR_FONTINFO_CTL:
                handleFont(rExport, rPropStates, p, xMapper, u"font-name-complex");
            break;
            case EE_CHAR_WEIGHT:
            case EE_CHAR_WEIGHT_CJK:
            case EE_CHAR_WEIGHT_CTL:
            {
                if (!static_cast<const SvxWeightItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                rPropStates.emplace_back(nIndex, aAny);
            }
            break;
            case EE_CHAR_FONTHEIGHT:
            case EE_CHAR_FONTHEIGHT_CJK:
            case EE_CHAR_FONTHEIGHT_CTL:
            {
                if (!static_cast<const SvxFontHeightItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                rPropStates.emplace_back(nIndex, aAny);
            }
            break;
            case EE_CHAR_ITALIC:
            case EE_CHAR_ITALIC_CJK:
            case EE_CHAR_ITALIC_CTL:
            {
                if (!static_cast<const SvxPostureItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                rPropStates.emplace_back(nIndex, aAny);
            }
            break;
            case EE_CHAR_UNDERLINE:
            {
                // Underline attribute needs to export multiple entries.
                sal_Int32 nIndexStyle = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-underline-style", 0);
                if (nIndexStyle == -1 || nIndexStyle > nEntryCount)
                    break;
 
                sal_Int32 nIndexWidth = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-underline-width", 0);
                if (nIndexWidth == -1 || nIndexWidth > nEntryCount)
                    break;
 
                sal_Int32 nIndexType = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-underline-type", 0);
                if (nIndexType == -1 || nIndexType > nEntryCount)
                    break;
 
                sal_Int32 nIndexColor = xMapper->FindEntryIndex("CharUnderlineColor", XML_NAMESPACE_STYLE, u"text-underline-color");
                if (nIndexColor == -1 || nIndexColor > nEntryCount)
                    break;
 
                sal_Int32 nIndexHasColor = xMapper->FindEntryIndex("CharUnderlineHasColor", XML_NAMESPACE_STYLE, u"text-underline-color");
                if (nIndexHasColor == -1 || nIndexHasColor > nEntryCount)
                    break;
 
                const SvxUnderlineItem* pUL = static_cast<const SvxUnderlineItem*>(p);
                pUL->QueryValue(aAny, MID_TL_STYLE);
                rPropStates.emplace_back(nIndexStyle, aAny);
                rPropStates.emplace_back(nIndexType,  aAny);
                rPropStates.emplace_back(nIndexWidth, aAny);
 
                pUL->QueryValue(aAny, MID_TL_COLOR);
                rPropStates.emplace_back(nIndexColor, aAny);
 
                pUL->QueryValue(aAny, MID_TL_HASCOLOR);
                rPropStates.emplace_back(nIndexHasColor, aAny);
            }
            break;
            case EE_CHAR_OVERLINE:
            {
                // Same with overline.  Do just as we do with underline attributes.
                sal_Int32 nIndexStyle = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-overline-style", 0);
                if (nIndexStyle == -1 || nIndexStyle > nEntryCount)
                    break;
 
                sal_Int32 nIndexWidth = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-overline-width", 0);
                if (nIndexWidth == -1 || nIndexWidth > nEntryCount)
                    break;
 
                sal_Int32 nIndexType = xMapper->GetEntryIndex(XML_NAMESPACE_STYLE, u"text-overline-type", 0);
                if (nIndexType == -1 || nIndexType > nEntryCount)
                    break;
 
                sal_Int32 nIndexColor = xMapper->FindEntryIndex("CharOverlineColor", XML_NAMESPACE_STYLE, u"text-overline-color");
                if (nIndexColor == -1 || nIndexColor > nEntryCount)
                    break;
 
                sal_Int32 nIndexHasColor = xMapper->FindEntryIndex("CharOverlineHasColor", XML_NAMESPACE_STYLE, u"text-overline-color");
                if (nIndexHasColor == -1 || nIndexHasColor > nEntryCount)
                    break;
 
                const SvxOverlineItem* pOL = static_cast<const SvxOverlineItem*>(p);
                pOL->QueryValue(aAny, MID_TL_STYLE);
                rPropStates.emplace_back(nIndexStyle, aAny);
                rPropStates.emplace_back(nIndexType,  aAny);
                rPropStates.emplace_back(nIndexWidth, aAny);
 
                pOL->QueryValue(aAny, MID_TL_COLOR);
                rPropStates.emplace_back(nIndexColor, aAny);
 
                pOL->QueryValue(aAny, MID_TL_HASCOLOR);
                rPropStates.emplace_back(nIndexHasColor, aAny);
            }
            break;
            case EE_CHAR_COLOR:
            {
                if (!static_cast<const SvxColorItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                ::Color nColor;
                if ( aAny >>= nColor )
                {
                    sal_Int32 nIndexColor = ( nColor == COL_AUTO ) ? xMapper->GetEntryIndex(
                        XML_NAMESPACE_STYLE, GetXMLToken( XML_USE_WINDOW_FONT_COLOR ), 0 ) : nIndex;
                    rPropStates.emplace_back( nIndexColor, aAny );
                }
            }
            break;
            case EE_CHAR_WLM:
            {
                if (!static_cast<const SvxWordLineModeItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                rPropStates.emplace_back(nIndex, aAny);
            }
            break;
            case EE_CHAR_STRIKEOUT:
            {
                if (!static_cast<const SvxCrossedOutItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                rPropStates.emplace_back(nIndex, aAny);
            }
            break;
            case EE_CHAR_RELIEF:
            {
                if (!static_cast<const SvxCharReliefItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                rPropStates.emplace_back(nIndex, aAny);
            }
            break;
            case EE_CHAR_OUTLINE:
            {
                if (!static_cast<const SvxContourItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                rPropStates.emplace_back(nIndex, aAny);
            }
            break;
            case EE_CHAR_SHADOW:
            {
                if (!static_cast<const SvxShadowedItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                rPropStates.emplace_back(nIndex, aAny);
            }
            break;
            case EE_CHAR_KERNING:
            {
                if (!static_cast<const SvxKerningItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                rPropStates.emplace_back(nIndex, aAny);
            }
            break;
            case EE_CHAR_PAIRKERNING:
            {
                if (!static_cast<const SvxAutoKernItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                rPropStates.emplace_back(nIndex, aAny);
            }
            break;
            case EE_CHAR_FONTWIDTH:
            {
                if (!static_cast<const SvxCharScaleWidthItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                rPropStates.emplace_back(nIndex, aAny);
            }
            break;
            case EE_CHAR_ESCAPEMENT:
            {
                sal_Int32 nIndexEsc = xMapper->FindEntryIndex("CharEscapement", XML_NAMESPACE_STYLE, u"text-position");
                if (nIndexEsc == -1 || nIndexEsc > nEntryCount)
                    break;
 
                sal_Int32 nIndexEscHeight = xMapper->FindEntryIndex("CharEscapementHeight", XML_NAMESPACE_STYLE, u"text-position");
                if (nIndexEscHeight == -1 || nIndexEscHeight > nEntryCount)
                    break;
 
                const SvxEscapementItem* pEsc = static_cast<const SvxEscapementItem*>(p);
 
                pEsc->QueryValue(aAny);
                rPropStates.emplace_back(nIndexEsc, aAny);
 
                pEsc->QueryValue(aAny, MID_ESC_HEIGHT);
                rPropStates.emplace_back(nIndexEscHeight, aAny);
 
            }
            break;
            case EE_CHAR_EMPHASISMARK:
            {
                if (!static_cast<const SvxEmphasisMarkItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                rPropStates.emplace_back(nIndex, aAny);
            }
            break;
            case EE_CHAR_LANGUAGE:
            case EE_CHAR_LANGUAGE_CJK:
            case EE_CHAR_LANGUAGE_CTL:
            {
                if (!static_cast<const SvxLanguageItem*>(p)->QueryValue(aAny, pEntry->mnFlag))
                    continue;
 
                // Export multiple entries.
                sal_Int32 nIndexLanguage, nIndexCountry, nIndexScript, nIndexTag;
                switch (p->Which())
                {
                    case EE_CHAR_LANGUAGE:
                        nIndexLanguage = xMapper->GetEntryIndex( XML_NAMESPACE_FO, u"language", 0);
                        nIndexCountry = xMapper->GetEntryIndex( XML_NAMESPACE_FO, u"country", 0);
                        nIndexScript = xMapper->GetEntryIndex( XML_NAMESPACE_FO, u"script", 0);
                        nIndexTag = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"rfc-language-tag", 0);
                    break;
                    case EE_CHAR_LANGUAGE_CJK:
                        nIndexLanguage = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"language-asian", 0);
                        nIndexCountry = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"country-asian", 0);
                        nIndexScript = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"script-asian", 0);
                        nIndexTag = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"rfc-language-tag-asian", 0);
                    break;
                    case EE_CHAR_LANGUAGE_CTL:
                        nIndexLanguage = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"language-complex", 0);
                        nIndexCountry = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"country-complex", 0);
                        nIndexScript = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"script-complex", 0);
                        nIndexTag = xMapper->GetEntryIndex( XML_NAMESPACE_STYLE, u"rfc-language-tag-complex", 0);
                    break;
                    default:
                        nIndexLanguage = nIndexCountry = nIndexScript = nIndexTag = -1;
                }
                assert( nIndexLanguage >= 0 && nIndexCountry >= 0 && nIndexScript >= 0 && nIndexTag >= 0);
                rPropStates.emplace_back( nIndexLanguage, aAny);
                rPropStates.emplace_back( nIndexCountry, aAny);
                rPropStates.emplace_back( nIndexScript, aAny);
                rPropStates.emplace_back( nIndexTag, aAny);
            }
            break;
            default:
                continue;
        }
    }
 
    return pField;
}
 
}
 
void ScXMLExport::ExportCellTextAutoStyles(sal_Int32 nTable)
{
    if (!ValidTab(nTable))
        return;
 
    rtl::Reference<XMLPropertySetMapper> xMapper = GetTextParagraphExport()->GetTextPropMapper()->getPropertySetMapper();
    rtl::Reference<SvXMLAutoStylePoolP> xStylePool = GetAutoStylePool();
    const ScXMLEditAttributeMap& rAttrMap = GetEditAttributeMap();
 
    sc::EditTextIterator aIter(*pDoc, nTable);
    sal_Int32 nCellCount = 0;
    for (const EditTextObject* pEdit = aIter.first(); pEdit; pEdit = aIter.next(), ++nCellCount)
    {
        std::vector<editeng::Section> aAttrs;
        pEdit->GetAllSections(aAttrs);
        if (aAttrs.empty())
            continue;
 
        for (const auto& rSec : aAttrs)
        {
            const std::vector<const SfxPoolItem*>& rSecAttrs = rSec.maAttributes;
            if (rSecAttrs.empty())
                // No formats applied to this section. Skip it.
                continue;
 
            std::vector<XMLPropertyState> aPropStates;
            toXMLPropertyStates(*this, aPropStates, rSecAttrs, xMapper, rAttrMap);
            if (!aPropStates.empty())
                xStylePool->Add(XmlStyleFamily::TEXT_TEXT, OUString(), std::move(aPropStates));
        }
    }
 
    GetProgressBarHelper()->ChangeReference(GetProgressBarHelper()->GetReference() + nCellCount);
}
 
void ScXMLExport::WriteRowContent()
{
    ScMyRowFormatRange aRange;
    sal_Int32 nIndex(-1);
#if OSL_DEBUG_LEVEL > 0
    sal_Int32 nPrevCol(0);
#endif
    sal_Int32 nCols(0);
    sal_Int32 nPrevValidationIndex(-1);
    bool bIsAutoStyle(true);
    bool bIsFirst(true);
    while (pRowFormatRanges->GetNext(aRange))
    {
#if OSL_DEBUG_LEVEL > 0
        OSL_ENSURE(bIsFirst || (!bIsFirst && (nPrevCol + nCols == aRange.nStartColumn)), "here are some columns missing");
#endif
        if (bIsFirst)
        {
            nIndex = aRange.nIndex;
            nPrevValidationIndex = aRange.nValidationIndex;
            bIsAutoStyle = aRange.bIsAutoStyle;
            nCols = aRange.nRepeatColumns;
            bIsFirst = false;
#if OSL_DEBUG_LEVEL > 0
            nPrevCol = aRange.nStartColumn;
#endif
        }
        else
        {
            if (((aRange.nIndex == nIndex && aRange.bIsAutoStyle == bIsAutoStyle) ||
                (aRange.nIndex == nIndex && nIndex == -1)) &&
                nPrevValidationIndex == aRange.nValidationIndex)
                nCols += aRange.nRepeatColumns;
            else
            {
                if (nIndex != -1)
                    AddAttribute(sAttrStyleName, pCellStyles->GetStyleNameByIndex(nIndex, bIsAutoStyle));
                if (nPrevValidationIndex > -1)
                    AddAttribute(XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION_NAME, pValidationsContainer->GetValidationName(nPrevValidationIndex));
                if (nCols > 1)
                {
                    AddAttribute(sAttrColumnsRepeated, OUString::number(nCols));
                }
                SvXMLElementExport aElemC(*this, sElemCell, true, true);
                nIndex = aRange.nIndex;
                bIsAutoStyle = aRange.bIsAutoStyle;
                nCols = aRange.nRepeatColumns;
                nPrevValidationIndex = aRange.nValidationIndex;
#if OSL_DEBUG_LEVEL > 0
                nPrevCol = aRange.nStartColumn;
#endif
            }
        }
    }
    if (!bIsFirst)
    {
        if (nIndex != -1)
            AddAttribute(sAttrStyleName, pCellStyles->GetStyleNameByIndex(nIndex, bIsAutoStyle));
        if (nPrevValidationIndex > -1)
            AddAttribute(XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION_NAME, pValidationsContainer->GetValidationName(nPrevValidationIndex));
        if (nCols > 1)
        {
            AddAttribute(sAttrColumnsRepeated, OUString::number(nCols));
        }
        SvXMLElementExport aElemC(*this, sElemCell, true, true);
    }
}
 
void ScXMLExport::WriteRowStartTag(
    const sal_Int32 nIndex, const sal_Int32 nEqualRows,
    bool bHidden, bool bFiltered)
{
    // tdf#143940
    if (nIndex != -1)
        AddAttribute(sAttrStyleName, pRowStyles->GetStyleNameByIndex(nIndex));
    if (bHidden)
    {
        if (bFiltered)
            AddAttribute(XML_NAMESPACE_TABLE, XML_VISIBILITY, XML_FILTER);
        else
            AddAttribute(XML_NAMESPACE_TABLE, XML_VISIBILITY, XML_COLLAPSE);
    }
    if (nEqualRows > 1)
    {
        AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_REPEATED, OUString::number(nEqualRows));
    }
 
    StartElement( sElemRow, true);
}
 
void ScXMLExport::OpenHeaderRows()
{
    StartElement( XML_NAMESPACE_TABLE, XML_TABLE_HEADER_ROWS, true);
    bRowHeaderOpen = true;
}
 
void ScXMLExport::CloseHeaderRows()
{
    EndElement(XML_NAMESPACE_TABLE, XML_TABLE_HEADER_ROWS, true);
}
 
void ScXMLExport::OpenNewRow(
    const sal_Int32 nIndex, const sal_Int32 nStartRow, const sal_Int32 nEqualRows,
    bool bHidden, bool bFiltered)
{
    nOpenRow = nStartRow;
    if (pGroupRows->IsGroupStart(nStartRow))
    {
        if (bHasRowHeader && bRowHeaderOpen)
            CloseHeaderRows();
        pGroupRows->OpenGroups(nStartRow);
        if (bHasRowHeader && bRowHeaderOpen)
            OpenHeaderRows();
    }
    if (bHasRowHeader && !bRowHeaderOpen && nStartRow >= aRowHeaderRange.aStart.Row() && nStartRow <= aRowHeaderRange.aEnd.Row())
    {
        if (nStartRow == aRowHeaderRange.aStart.Row())
            OpenHeaderRows();
        sal_Int32 nEquals;
        if (aRowHeaderRange.aEnd.Row() < nStartRow + nEqualRows - 1)
            nEquals = aRowHeaderRange.aEnd.Row() - nStartRow + 1;
        else
            nEquals = nEqualRows;
        WriteRowStartTag(nIndex, nEquals, bHidden, bFiltered);
        nOpenRow = nStartRow + nEquals - 1;
        if (nEquals < nEqualRows)
        {
            CloseRow(nStartRow + nEquals - 1);
            WriteRowStartTag(nIndex, nEqualRows - nEquals, bHidden, bFiltered);
            nOpenRow = nStartRow + nEqualRows - 1;
        }
    }
    else
        WriteRowStartTag(nIndex, nEqualRows, bHidden, bFiltered);
}
 
void ScXMLExport::OpenAndCloseRow(
    const sal_Int32 nIndex, const sal_Int32 nStartRow, const sal_Int32 nEqualRows,
    bool bHidden, bool bFiltered)
{
    OpenNewRow(nIndex, nStartRow, nEqualRows, bHidden, bFiltered);
    WriteRowContent();
    CloseRow(nStartRow + nEqualRows - 1);
    pRowFormatRanges->Clear();
}
 
void ScXMLExport::OpenRow(const sal_Int32 nTable, const sal_Int32 nStartRow, const sal_Int32 nRepeatRow, ScXMLCachedRowAttrAccess& rRowAttr)
{
    if (nRepeatRow > 1)
    {
        sal_Int32 nPrevIndex(0), nIndex;
        bool bPrevHidden = false;
        bool bPrevFiltered = false;
        bool bHidden = false;
        bool bFiltered = false;
        sal_Int32 nEqualRows(1);
        sal_Int32 nEndRow(nStartRow + nRepeatRow);
        sal_Int32 nEndRowHidden = nStartRow - 1;
        sal_Int32 nEndRowFiltered = nStartRow - 1;
        sal_Int32 nRow;
        for (nRow = nStartRow; nRow < nEndRow; ++nRow)
        {
            if (nRow == nStartRow)
            {
                nPrevIndex = pRowStyles->GetStyleNameIndex(nTable, nRow);
                if (pDoc)
                {
                    if (nRow > nEndRowHidden)
                    {
                        bPrevHidden = rRowAttr.rowHidden(nTable, nRow, nEndRowHidden);
                        bHidden = bPrevHidden;
                    }
                    if (nRow > nEndRowFiltered)
                    {
                        bPrevFiltered = rRowAttr.rowFiltered(nTable, nRow, nEndRowFiltered);
                        bFiltered = bPrevFiltered;
                    }
                }
 
            }
            else
            {
                nIndex = pRowStyles->GetStyleNameIndex(nTable, nRow);
                if (pDoc)
                {
                    if (nRow > nEndRowHidden)
                        bHidden = rRowAttr.rowHidden(nTable, nRow, nEndRowHidden);
                    if (nRow > nEndRowFiltered)
                        bFiltered = rRowAttr.rowFiltered(nTable, nRow, nEndRowFiltered);
                }
                if (nIndex == nPrevIndex && bHidden == bPrevHidden && bFiltered == bPrevFiltered &&
                    !(bHasRowHeader && ((nRow == aRowHeaderRange.aStart.Row()) || (nRow - 1 == aRowHeaderRange.aEnd.Row()))) &&
                    !(pGroupRows->IsGroupStart(nRow)) &&
                    !(pGroupRows->IsGroupEnd(nRow - 1)))
                    ++nEqualRows;
                else
                {
                    assert(nPrevIndex >= 0 && "coverity#1438402");
                    ScRowFormatRanges* pTempRowFormatRanges = new ScRowFormatRanges(pRowFormatRanges.get());
                    OpenAndCloseRow(nPrevIndex, nRow - nEqualRows, nEqualRows, bPrevHidden, bPrevFiltered);
                    pRowFormatRanges.reset(pTempRowFormatRanges);
                    nEqualRows = 1;
                    nPrevIndex = nIndex;
                    bPrevHidden = bHidden;
                    bPrevFiltered = bFiltered;
                }
            }
        }
        assert(nPrevIndex >= 0 && "coverity#1438402");
        OpenNewRow(nPrevIndex, nRow - nEqualRows, nEqualRows, bPrevHidden, bPrevFiltered);
    }
    else
    {
        sal_Int32 nIndex = pRowStyles->GetStyleNameIndex(nTable, nStartRow);
        bool bHidden = false;
        bool bFiltered = false;
        if (pDoc)
        {
            sal_Int32 nEndRowHidden;
            sal_Int32 nEndRowFiltered;
            bHidden = rRowAttr.rowHidden(nTable, nStartRow, nEndRowHidden);
            bFiltered = rRowAttr.rowFiltered(nTable, nStartRow, nEndRowFiltered);
        }
        assert(nIndex >= 0 && "coverity#1438402");
        OpenNewRow(nIndex, nStartRow, 1, bHidden, bFiltered);
    }
    nOpenRow = nStartRow + nRepeatRow - 1;
}
 
void ScXMLExport::CloseRow(const sal_Int32 nRow)
{
    if (nOpenRow > -1)
    {
        EndElement(sElemRow, true);
        if (bHasRowHeader && nRow == aRowHeaderRange.aEnd.Row())
        {
            CloseHeaderRows();
            bRowHeaderOpen = false;
        }
        if (pGroupRows->IsGroupEnd(nRow))
        {
            if (bHasRowHeader && bRowHeaderOpen)
                CloseHeaderRows();
            pGroupRows->CloseGroups(nRow);
            if (bHasRowHeader && bRowHeaderOpen)
                OpenHeaderRows();
        }
    }
    nOpenRow = -1;
}
 
void ScXMLExport::ExportFormatRanges(const sal_Int32 nStartCol, const sal_Int32 nStartRow,
    const sal_Int32 nEndCol, const sal_Int32 nEndRow, const sal_Int32 nSheet)
{
    pRowFormatRanges->Clear();
    ScXMLCachedRowAttrAccess aRowAttr(pDoc);
    if (nStartRow == nEndRow)
    {
        pCellStyles->GetFormatRanges(nStartCol, nEndCol, nStartRow, nSheet, pRowFormatRanges.get());
        if (nOpenRow == - 1)
            OpenRow(nSheet, nStartRow, 1, aRowAttr);
        WriteRowContent();
        pRowFormatRanges->Clear();
    }
    else
    {
        if (nOpenRow > -1)
        {
            pCellStyles->GetFormatRanges(nStartCol, pSharedData->GetLastColumn(nSheet), nStartRow, nSheet, pRowFormatRanges.get());
            WriteRowContent();
            CloseRow(nStartRow);
            sal_Int32 nRows(1);
            sal_Int32 nTotalRows(nEndRow - nStartRow + 1 - 1);
            while (nRows < nTotalRows)
            {
                pRowFormatRanges->Clear();
                pCellStyles->GetFormatRanges(0, pSharedData->GetLastColumn(nSheet), nStartRow + nRows, nSheet, pRowFormatRanges.get());
                sal_Int32 nMaxRows = pRowFormatRanges->GetMaxRows();
                assert(nMaxRows && "ScXMLExport::ExportFormatRanges cannot make progress with zero rows, something went wrong");
                if (!nMaxRows)
                {
                    uno::Sequence<OUString> aEmptySeq;
                    SetError(XMLERROR_CANCEL|XMLERROR_FLAG_SEVERE, aEmptySeq);
                    break;
                }
                if (nMaxRows >= nTotalRows - nRows)
                {
                    OpenRow(nSheet, nStartRow + nRows, nTotalRows - nRows, aRowAttr);
                    nRows += nTotalRows - nRows;
                }
                else
                {
                    OpenRow(nSheet, nStartRow + nRows, nMaxRows, aRowAttr);
                    nRows += nMaxRows;
                }
                if (!pRowFormatRanges->GetSize())
                    pCellStyles->GetFormatRanges(0, pSharedData->GetLastColumn(nSheet), nStartRow + nRows, nSheet, pRowFormatRanges.get());
                WriteRowContent();
                CloseRow(nStartRow + nRows - 1);
            }
            if (nTotalRows == 1)
                CloseRow(nStartRow);
            OpenRow(nSheet, nEndRow, 1, aRowAttr);
            pRowFormatRanges->Clear();
            pCellStyles->GetFormatRanges(0, nEndCol, nEndRow, nSheet, pRowFormatRanges.get());
            WriteRowContent();
        }
        else
        {
            sal_Int32 nRows(0);
            sal_Int32 nTotalRows(nEndRow - nStartRow + 1 - 1);
            while (nRows < nTotalRows)
            {
                pCellStyles->GetFormatRanges(0, pSharedData->GetLastColumn(nSheet), nStartRow + nRows, nSheet, pRowFormatRanges.get());
                sal_Int32 nMaxRows = pRowFormatRanges->GetMaxRows();
                OSL_ENSURE(nMaxRows, "something went wrong");
                if (nMaxRows >= nTotalRows - nRows)
                {
                    OpenRow(nSheet, nStartRow + nRows, nTotalRows - nRows, aRowAttr);
                    nRows += nTotalRows - nRows;
                }
                else
                {
                    OpenRow(nSheet, nStartRow + nRows, nMaxRows, aRowAttr);
                    nRows += nMaxRows;
                }
                if (!pRowFormatRanges->GetSize())
                    pCellStyles->GetFormatRanges(0, pSharedData->GetLastColumn(nSheet), nStartRow + nRows, nSheet, pRowFormatRanges.get());
                WriteRowContent();
                CloseRow(nStartRow + nRows - 1);
            }
            OpenRow(nSheet, nEndRow, 1, aRowAttr);
            pRowFormatRanges->Clear();
            pCellStyles->GetFormatRanges(0, nEndCol, nEndRow, nSheet, pRowFormatRanges.get());
            WriteRowContent();
        }
    }
}
 
void ScXMLExport::GetColumnRowHeader(bool& rHasColumnHeader, ScRange& rColumnHeaderRange,
                                     bool& rHasRowHeader, ScRange& rRowHeaderRange,
                                     OUString& rPrintRanges) const
{
    uno::Reference <sheet::XPrintAreas> xPrintAreas (xCurrentTable, uno::UNO_QUERY);
    if (!xPrintAreas.is())
        return;
 
    rHasRowHeader = xPrintAreas->getPrintTitleRows();
    rHasColumnHeader = xPrintAreas->getPrintTitleColumns();
    table::CellRangeAddress rTempRowHeaderRange = xPrintAreas->getTitleRows();
    rRowHeaderRange = ScRange(rTempRowHeaderRange.StartColumn,
                              rTempRowHeaderRange.StartRow,
                              rTempRowHeaderRange.Sheet,
                              rTempRowHeaderRange.EndColumn,
                              rTempRowHeaderRange.EndRow,
                              rTempRowHeaderRange.Sheet);
    table::CellRangeAddress rTempColumnHeaderRange = xPrintAreas->getTitleColumns();
    rColumnHeaderRange = ScRange(rTempColumnHeaderRange.StartColumn,
                              rTempColumnHeaderRange.StartRow,
                              rTempColumnHeaderRange.Sheet,
                              rTempColumnHeaderRange.EndColumn,
                              rTempColumnHeaderRange.EndRow,
                              rTempColumnHeaderRange.Sheet);
    uno::Sequence< table::CellRangeAddress > aRangeList( xPrintAreas->getPrintAreas() );
    ScRangeStringConverter::GetStringFromRangeList( rPrintRanges, aRangeList, pDoc, FormulaGrammar::CONV_OOO );
}
 
void ScXMLExport::FillFieldGroup(ScOutlineArray* pFields, ScMyOpenCloseColumnRowGroup* pGroups)
{
    size_t nDepth = pFields->GetDepth();
    for (size_t i = 0; i < nDepth; ++i)
    {
        size_t nFields = pFields->GetCount(i);
        for (size_t j = 0; j < nFields; ++j)
        {
            ScMyColumnRowGroup aGroup;
            const ScOutlineEntry* pEntry = pFields->GetEntry(i, j);
            aGroup.nField = pEntry->GetStart();
            aGroup.nLevel = static_cast<sal_Int16>(i);
            aGroup.bDisplay = !(pEntry->IsHidden());
            pGroups->AddGroup(aGroup, pEntry->GetEnd());
        }
    }
    if (nDepth)
        pGroups->Sort();
}
 
void ScXMLExport::FillColumnRowGroups()
{
    if (!pDoc)
        return;
 
    ScOutlineTable* pOutlineTable = pDoc->GetOutlineTable( static_cast<SCTAB>(nCurrentTable) );
    if(pOutlineTable)
    {
        ScOutlineArray& rCols(pOutlineTable->GetColArray());
        ScOutlineArray& rRows(pOutlineTable->GetRowArray());
        FillFieldGroup(&rCols, pGroupColumns.get());
        FillFieldGroup(&rRows, pGroupRows.get());
        pSharedData->SetLastColumn(nCurrentTable, pGroupColumns->GetLast());
        pSharedData->SetLastRow(nCurrentTable, pGroupRows->GetLast());
    }
}
 
void ScXMLExport::SetBodyAttributes()
{
    if (!(pDoc && pDoc->IsDocProtected()))
        return;
 
    AddAttribute(XML_NAMESPACE_TABLE, XML_STRUCTURE_PROTECTED, XML_TRUE);
    OUStringBuffer aBuffer;
    uno::Sequence<sal_Int8> aPassHash;
    ScPasswordHash eHashUsed = PASSHASH_UNSPECIFIED;
    const ScDocProtection* p = pDoc->GetDocProtection();
    if (p)
    {
        if (p->hasPasswordHash(PASSHASH_SHA1))
        {
            aPassHash = p->getPasswordHash(PASSHASH_SHA1);
            eHashUsed = PASSHASH_SHA1;
        }
        else if (p->hasPasswordHash(PASSHASH_SHA256))
        {
            aPassHash = p->getPasswordHash(PASSHASH_SHA256);
            eHashUsed = PASSHASH_SHA256;
        }
        else if (p->hasPasswordHash(PASSHASH_XL, PASSHASH_SHA1))
        {
            aPassHash = p->getPasswordHash(PASSHASH_XL, PASSHASH_SHA1);
            eHashUsed = PASSHASH_XL;
        }
    }
    ::comphelper::Base64::encode(aBuffer, aPassHash);
    if (aBuffer.isEmpty())
        return;
 
    AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY, aBuffer.makeStringAndClear());
    if (getSaneDefaultVersion() < SvtSaveOptions::ODFSVER_012)
        return;
 
    if (eHashUsed == PASSHASH_XL)
    {
        AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
                     ScPassHashHelper::getHashURI(PASSHASH_XL));
        if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
            AddAttribute(XML_NAMESPACE_LO_EXT, XML_PROTECTION_KEY_DIGEST_ALGORITHM_2,
                    ScPassHashHelper::getHashURI(PASSHASH_SHA1));
    }
    else if (eHashUsed == PASSHASH_SHA1)
    {
        AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
                     ScPassHashHelper::getHashURI(PASSHASH_SHA1));
    }
    else if (eHashUsed == PASSHASH_SHA256)
    {
        AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
                     ScPassHashHelper::getHashURI(PASSHASH_SHA256));
    }
}
 
static bool lcl_CopyStreamElement( const uno::Reference< io::XInputStream >& xInput,
                            const uno::Reference< io::XOutputStream >& xOutput,
                            sal_Int64 nCount )
{
    const sal_Int32 nBufSize = 16*1024;
    uno::Sequence<sal_Int8> aSequence(nBufSize);
 
    sal_Int64 nRemaining = nCount;
    bool bFirst = true;
 
    while ( nRemaining > 0 )
    {
        sal_Int32 nRead = xInput->readBytes( aSequence, std::min( nRemaining, static_cast<sal_Int64>(nBufSize) ) );
        if (bFirst)
        {
            // safety check: Make sure the copied part actually points to the start of an element
            if ( nRead < 1 || aSequence[0] != static_cast<sal_Int8>('<') )
            {
                return false;   // abort and set an error
            }
            bFirst = false;
        }
        if (nRead == nRemaining)
        {
            // safety check: Make sure the copied part also ends at the end of an element
            if ( aSequence[nRead-1] != static_cast<sal_Int8>('>') )
            {
                return false;   // abort and set an error
            }
        }
 
        if ( nRead == nBufSize )
        {
            xOutput->writeBytes( aSequence );
            nRemaining -= nRead;
        }
        else
        {
            if ( nRead > 0 )
            {
                uno::Sequence<sal_Int8> aTempBuf( aSequence.getConstArray(), nRead );
                xOutput->writeBytes( aTempBuf );
            }
            nRemaining = 0;
        }
    }
    return true;    // successful
}
 
static void lcl_SkipBytesInBlocks( const uno::Reference< io::XInputStream >& xInput, sal_Int64 nBytesToSkip )
{
    // skipBytes in zip stream is implemented as reading.
    // For now, split into several calls to avoid allocating a large buffer.
    // Later, skipBytes should be changed.
 
    const sal_Int64 nMaxSize = 32*1024;
 
    if ( nBytesToSkip > 0 )
    {
        sal_Int64 nRemaining = nBytesToSkip;
        while ( nRemaining > 0 )
        {
            sal_Int32 nSkip = std::min( nRemaining, nMaxSize );
            xInput->skipBytes( nSkip );
            nRemaining -= nSkip;
        }
    }
}
 
void ScXMLExport::CopySourceStream( sal_Int64 nStartOffset, sal_Int64 nEndOffset, sal_Int64& rNewStart, sal_Int64& rNewEnd )
{
    uno::Reference<xml::sax::XDocumentHandler> xHandler = GetDocHandler();
    uno::Reference<io::XActiveDataSource> xDestSource( xHandler, uno::UNO_QUERY );
    if ( !xDestSource.is() )
        return;
 
    uno::Reference<io::XOutputStream> xDestStream = xDestSource->getOutputStream();
    uno::Reference<io::XSeekable> xDestSeek( xDestStream, uno::UNO_QUERY );
    if ( !xDestSeek.is() )
        return;
 
    // temporary: set same stream again to clear buffer
    xDestSource->setOutputStream( xDestStream );
 
    if ( getExportFlags() & SvXMLExportFlags::PRETTY )
    {
        static constexpr OString aOutStr("\n   "_ostr);
        uno::Sequence<sal_Int8> aOutSeq( reinterpret_cast<sal_Int8 const *>(aOutStr.getStr()), aOutStr.getLength() );
        xDestStream->writeBytes( aOutSeq );
    }
 
    rNewStart = xDestSeek->getPosition();
 
    if ( nStartOffset > nSourceStreamPos )
        lcl_SkipBytesInBlocks( xSourceStream, nStartOffset - nSourceStreamPos );
 
    if ( !lcl_CopyStreamElement( xSourceStream, xDestStream, nEndOffset - nStartOffset ) )
    {
        // If copying went wrong, set an error.
        // ScXMLImportWrapper then resets all stream flags, so the next save attempt will use normal saving.
 
        uno::Sequence<OUString> aEmptySeq;
        SetError(XMLERROR_CANCEL|XMLERROR_FLAG_SEVERE, aEmptySeq);
    }
    nSourceStreamPos = nEndOffset;
 
    rNewEnd = xDestSeek->getPosition();
}
 
const ScXMLEditAttributeMap& ScXMLExport::GetEditAttributeMap() const
{
    if (!mpEditAttrMap)
        mpEditAttrMap.reset(new ScXMLEditAttributeMap);
    return *mpEditAttrMap;
}
 
void ScXMLExport::RegisterDefinedStyleNames( const uno::Reference< css::sheet::XSpreadsheetDocument > & xSpreadDoc )
{
    ScFormatSaveData* pFormatData = comphelper::getFromUnoTunnel<ScModelObj>(xSpreadDoc)->GetFormatSaveData();
    auto xAutoStylePool = GetAutoStylePool();
    for (const auto& rFormatInfo : pFormatData->maIDToName)
    {
        xAutoStylePool->RegisterDefinedName(XmlStyleFamily::TABLE_CELL, rFormatInfo.second);
    }
}
 
void ScXMLExport::ExportContent_()
{
    nCurrentTable = 0;
    if (!pSharedData)
    {
        SCTAB nTableCount(0);
        sal_Int32 nShapesCount(0);
        CollectSharedData(nTableCount, nShapesCount);
        OSL_FAIL("no shared data set");
        if (!pSharedData)
            return;
    }
    ScXMLExportDatabaseRanges aExportDatabaseRanges(*this);
    if (!GetModel().is())
        return;
 
    uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
    if ( !xSpreadDoc.is() )
        return;
 
    ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(xSpreadDoc)->GetSheetSaveData();
    if (pSheetData)
        pSheetData->ResetSaveEntries();
 
    uno::Reference<container::XIndexAccess> xIndex( xSpreadDoc->getSheets(), uno::UNO_QUERY );
    if ( xIndex.is() )
    {
        //_GetNamespaceMap().ClearQNamesCache();
        pChangeTrackingExportHelper->CollectAndWriteChanges();
        WriteCalculationSettings(xSpreadDoc);
        sal_Int32 nTableCount(xIndex->getCount());
        ScMyAreaLinksContainer aAreaLinks;
        GetAreaLinks( aAreaLinks );
        ScMyEmptyDatabaseRangesContainer aEmptyRanges(aExportDatabaseRanges.GetEmptyDatabaseRanges());
        ScMyDetectiveOpContainer aDetectiveOpContainer;
        GetDetectiveOpList( aDetectiveOpContainer );
 
        pMergedRangesContainer->Sort();
        pSharedData->GetDetectiveObjContainer()->Sort();
 
        mpCellsItr->Clear();
        mpCellsItr->SetShapes( pSharedData->GetShapesContainer() );
        mpCellsItr->SetNoteShapes( pSharedData->GetNoteShapes() );
        mpCellsItr->SetMergedRanges( pMergedRangesContainer.get() );
        mpCellsItr->SetAreaLinks( &aAreaLinks );
        mpCellsItr->SetEmptyDatabaseRanges( &aEmptyRanges );
        mpCellsItr->SetDetectiveObj( pSharedData->GetDetectiveObjContainer() );
        mpCellsItr->SetDetectiveOp( &aDetectiveOpContainer );
 
        if (nTableCount > 0)
            pValidationsContainer->WriteValidations(*this);
        WriteTheLabelRanges( xSpreadDoc );
        for (sal_Int32 nTable = 0; nTable < nTableCount; ++nTable)
        {
            sal_Int64 nStartOffset = -1;
            sal_Int64 nEndOffset = -1;
            if (pSheetData && pDoc && pDoc->IsStreamValid(static_cast<SCTAB>(nTable)) && !pDoc->GetChangeTrack())
                pSheetData->GetStreamPos( nTable, nStartOffset, nEndOffset );
 
            if ( nStartOffset >= 0 && nEndOffset >= 0 && xSourceStream.is() )
            {
                sal_Int64 nNewStart = -1;
                sal_Int64 nNewEnd = -1;
                CopySourceStream( nStartOffset, nEndOffset, nNewStart, nNewEnd );
 
                // store position of copied sheet in output
                pSheetData->AddSavePos( nTable, nNewStart, nNewEnd );
 
                // skip iterator entries for this sheet
                mpCellsItr->SkipTable(static_cast<SCTAB>(nTable));
            }
            else
            {
                uno::Reference<sheet::XSpreadsheet> xTable(xIndex->getByIndex(nTable), uno::UNO_QUERY);
                WriteTable(nTable, xTable);
            }
            IncrementProgressBar(false);
        }
    }
    WriteExternalRefCaches();
    WriteNamedExpressions();
    WriteDataStream();
    aExportDatabaseRanges.WriteDatabaseRanges();
    WriteExternalDataMapping();
    ScXMLExportDataPilot aExportDataPilot(*this);
    aExportDataPilot.WriteDataPilots();
    WriteConsolidation();
    ScXMLExportDDELinks aExportDDELinks(*this);
    aExportDDELinks.WriteDDELinks(xSpreadDoc);
    IncrementProgressBar(true, 0);
    GetProgressBarHelper()->SetValue(GetProgressBarHelper()->GetReference());
}
 
void ScXMLExport::ExportStyles_( bool bUsed )
{
    uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
    if (xSpreadDoc.is())
        RegisterDefinedStyleNames( xSpreadDoc);
 
    if (!pSharedData)
    {
        SCTAB nTableCount(0);
        sal_Int32 nShapesCount(0);
        CollectSharedData(nTableCount, nShapesCount);
    }
    rtl::Reference<XMLCellStyleExport> aStylesExp(new XMLCellStyleExport(*this, GetAutoStylePool().get()));
    if (GetModel().is())
    {
        uno::Reference <lang::XMultiServiceFactory> xMultiServiceFactory(GetModel(), uno::UNO_QUERY);
        if (xMultiServiceFactory.is())
        {
            uno::Reference <beans::XPropertySet> xProperties(xMultiServiceFactory->createInstance(u"com.sun.star.sheet.Defaults"_ustr), uno::UNO_QUERY);
            if (xProperties.is())
                aStylesExp->exportDefaultStyle(xProperties, XML_STYLE_FAMILY_TABLE_CELL_STYLES_NAME, xCellStylesExportPropertySetMapper);
            GetShapeExport()->ExportGraphicDefaults();
        }
        collectDataStyles(false);
    }
    exportDataStyles();
 
    aStylesExp->exportStyleFamily(u"CellStyles"_ustr,
        XML_STYLE_FAMILY_TABLE_CELL_STYLES_NAME, xCellStylesExportPropertySetMapper, false, XmlStyleFamily::TABLE_CELL);
 
    SvXMLExport::ExportStyles_(bUsed);
 
    exportTheme();
}
 
void ScXMLExport::exportTheme()
{
    if ((getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0)
        return;
 
    SdrModel* pModel = GetDocument()->GetDrawLayer();
 
    if (!pModel)
        return;
 
    auto const& pTheme = pModel->getTheme();
    if (!pTheme)
        return;
 
    ExportThemeElement(pTheme);
}
 
void ScXMLExport::AddStyleFromCells(const uno::Reference<beans::XPropertySet>& xProperties,
                                    const uno::Reference<sheet::XSpreadsheet>& xTable,
                                    sal_Int32 nTable, const OUString* pOldName)
{
    css::uno::Any aAny = xProperties->getPropertyValue(u"FormatID"_ustr);
    sal_uInt64 nKey = 0;
    aAny >>= nKey;
 
    //! pass xCellRanges instead
    uno::Reference<sheet::XSheetCellRanges> xCellRanges( xProperties, uno::UNO_QUERY );
 
    OUString sStyleName;
    sal_Int32 nNumberFormat(-1);
    sal_Int32 nValidationIndex(-1);
    std::vector<XMLPropertyState> aPropStates(xCellStylesExportPropertySetMapper->Filter(*this, xProperties));
    std::vector< XMLPropertyState >::iterator aItr(aPropStates.begin());
    std::vector< XMLPropertyState >::iterator aEndItr(aPropStates.end());
    sal_Int32 nCount(0);
    while (aItr != aEndItr)
    {
        if (aItr->mnIndex != -1)
        {
            switch (xCellStylesPropertySetMapper->GetEntryContextId(aItr->mnIndex))
            {
                case CTF_SC_VALIDATION :
                {
                    pValidationsContainer->AddValidation(aItr->maValue, nValidationIndex);
                    // this is not very slow, because it is most the last property or
                    // if it is not the last property it is the property before the last property,
                    // so in the worst case only one property has to be copied, but in the best case no
                    // property has to be copied
                    aItr = aPropStates.erase(aItr);
                    aEndItr = aPropStates.end();    // old aEndItr is invalidated!
                }
                break;
                case CTF_SC_CELLSTYLE :
                {
                    aItr->maValue >>= sStyleName;
                    aItr->mnIndex = -1;
                    ++aItr;
                    ++nCount;
                }
                break;
                case CTF_SC_NUMBERFORMAT :
                {
                    if (aItr->maValue >>= nNumberFormat)
                        addDataStyle(nNumberFormat);
                    ++aItr;
                    ++nCount;
                }
                break;
                default:
                {
                    ++aItr;
                    ++nCount;
                }
                break;
            }
        }
        else
        {
            ++aItr;
            ++nCount;
        }
    }
    if (nCount == 1) // this is the CellStyle and should be removed if alone
        aPropStates.clear();
    if (nNumberFormat == -1)
        xProperties->getPropertyValue(SC_UNONAME_NUMFMT) >>= nNumberFormat;
    if (sStyleName.isEmpty())
        return;
 
    if (!aPropStates.empty())
    {
        sal_Int32 nIndex;
        if (pOldName)
        {
            if (GetAutoStylePool()->AddNamed(*pOldName, XmlStyleFamily::TABLE_CELL, sStyleName, std::move(aPropStates)))
            {
                GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_CELL, *pOldName);
                // add to pCellStyles, so the name is found for normal sheets
                pCellStyles->AddStyleName(*pOldName, nIndex);
            }
        }
        else
        {
            OUString sName;
            bool bAdded = false;
            if (nKey)
            {
                uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
                ScFormatSaveData* pFormatData = comphelper::getFromUnoTunnel<ScModelObj>(xSpreadDoc)->GetFormatSaveData();
                auto itr = pFormatData->maIDToName.find(nKey);
                if (itr != pFormatData->maIDToName.end())
                {
                    sName = itr->second;
                    bAdded = GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TABLE_CELL, sStyleName, aPropStates);
                    if (bAdded)
                        GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_CELL, sName);
                }
            }
            bool bIsAutoStyle(true);
            if (bAdded || GetAutoStylePool()->Add(sName, XmlStyleFamily::TABLE_CELL, sStyleName, std::move(aPropStates)))
            {
                pCellStyles->AddStyleName(sName, nIndex);
            }
            else
                nIndex = pCellStyles->GetIndexOfStyleName(sName, XML_STYLE_FAMILY_TABLE_CELL_STYLES_PREFIX, bIsAutoStyle);
 
            const uno::Sequence<table::CellRangeAddress> aAddresses(xCellRanges->getRangeAddresses());
            bool bGetMerge(true);
            for (table::CellRangeAddress const & address : aAddresses)
            {
                pSharedData->SetLastColumn(nTable, address.EndColumn);
                pSharedData->SetLastRow(nTable, address.EndRow);
                pCellStyles->AddRangeStyleName(address, nIndex, bIsAutoStyle, nValidationIndex, nNumberFormat);
                if (bGetMerge)
                    bGetMerge = GetMerged(&address, xTable);
            }
        }
    }
    else
    {
        OUString sEncodedStyleName(EncodeStyleName(sStyleName));
        sal_Int32 nIndex(0);
        pCellStyles->AddStyleName(sEncodedStyleName, nIndex, false);
        if ( !pOldName )
        {
            const uno::Sequence<table::CellRangeAddress> aAddresses(xCellRanges->getRangeAddresses());
            bool bGetMerge(true);
            for (table::CellRangeAddress const & address : aAddresses)
            {
                if (bGetMerge)
                    bGetMerge = GetMerged(&address, xTable);
                pCellStyles->AddRangeStyleName(address, nIndex, false, nValidationIndex, nNumberFormat);
                if( sStyleName != "Default" || nValidationIndex != -1 )
                {
                    pSharedData->SetLastColumn(nTable, address.EndColumn);
                    pSharedData->SetLastRow(nTable, address.EndRow);
                }
            }
        }
    }
}
 
void ScXMLExport::AddStyleFromColumn(const uno::Reference<beans::XPropertySet>& xColumnProperties,
                                     const OUString* pOldName, sal_Int32& rIndex, bool& rIsVisible)
{
    std::vector<XMLPropertyState> aPropStates(xColumnStylesExportPropertySetMapper->Filter(*this, xColumnProperties));
    if(aPropStates.empty())
        return;
 
    auto aItr = std::find_if(aPropStates.begin(), aPropStates.end(),
        [this](const XMLPropertyState& rPropState) {
            return xColumnStylesPropertySetMapper->GetEntryContextId(rPropState.mnIndex) == CTF_SC_ISVISIBLE; });
    if (aItr != aPropStates.end())
    {
        aItr->maValue >>= rIsVisible;
    }
 
    const OUString sParent;
    if (pOldName)
    {
        if (GetAutoStylePool()->AddNamed(*pOldName, XmlStyleFamily::TABLE_COLUMN, sParent, std::move(aPropStates)))
        {
            GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_COLUMN, *pOldName);
            // add to pColumnStyles, so the name is found for normal sheets
            rIndex = pColumnStyles->AddStyleName(*pOldName);
        }
    }
    else
    {
        OUString sName;
        if (GetAutoStylePool()->Add(sName, XmlStyleFamily::TABLE_COLUMN, sParent, std::move(aPropStates)))
        {
            rIndex = pColumnStyles->AddStyleName(sName);
        }
        else
            rIndex = pColumnStyles->GetIndexOfStyleName(sName, XML_STYLE_FAMILY_TABLE_COLUMN_STYLES_PREFIX);
    }
}
 
void ScXMLExport::AddStyleFromRow(const uno::Reference<beans::XPropertySet>& xRowProperties,
                                  const OUString* pOldName, sal_Int32& rIndex)
{
    std::vector<XMLPropertyState> aPropStates(xRowStylesExportPropertySetMapper->Filter(*this, xRowProperties));
    if(aPropStates.empty())
        return;
 
    const OUString sParent;
    if (pOldName)
    {
        if (GetAutoStylePool()->AddNamed(*pOldName, XmlStyleFamily::TABLE_ROW, sParent, std::move(aPropStates)))
        {
            GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_ROW, *pOldName);
            // add to pRowStyles, so the name is found for normal sheets
            rIndex = pRowStyles->AddStyleName(*pOldName);
        }
    }
    else
    {
        OUString sName;
        if (GetAutoStylePool()->Add(sName, XmlStyleFamily::TABLE_ROW, sParent, std::move(aPropStates)))
        {
            rIndex = pRowStyles->AddStyleName(sName);
        }
        else
            rIndex = pRowStyles->GetIndexOfStyleName(sName, XML_STYLE_FAMILY_TABLE_ROW_STYLES_PREFIX);
    }
}
 
static uno::Any lcl_GetEnumerated( uno::Reference<container::XEnumerationAccess> const & xEnumAccess, sal_Int32 nIndex )
{
    uno::Any aRet;
    uno::Reference<container::XEnumeration> xEnum( xEnumAccess->createEnumeration() );
    try
    {
        sal_Int32 nSkip = nIndex;
        while ( nSkip > 0 )
        {
            (void) xEnum->nextElement();
            --nSkip;
        }
        aRet = xEnum->nextElement();
    }
    catch (container::NoSuchElementException&)
    {
        // leave aRet empty
    }
    return aRet;
}
 
void ScXMLExport::collectAutoStyles()
{
    SvXMLExport::collectAutoStyles();
 
    if (mbAutoStylesCollected)
        return;
 
    if (!GetModel().is())
        return;
 
    uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
    if (!xSpreadDoc.is())
        return;
 
    uno::Reference<container::XIndexAccess> xIndex( xSpreadDoc->getSheets(), uno::UNO_QUERY );
    if (!xIndex.is())
        return;
 
    if (getExportFlags() & SvXMLExportFlags::CONTENT)
    {
        // Reserve the loaded cell style names.
        RegisterDefinedStyleNames( xSpreadDoc);
 
        //  re-create automatic styles with old names from stored data
        ScSheetSaveData* pSheetData = comphelper::getFromUnoTunnel<ScModelObj>(xSpreadDoc)->GetSheetSaveData();
        if (pSheetData && pDoc)
        {
            // formulas have to be calculated now, to detect changed results
            // (during normal save, they will be calculated anyway)
            SCTAB nTabCount = pDoc->GetTableCount();
            for (SCTAB nTab=0; nTab<nTabCount; ++nTab)
                if (pDoc->IsStreamValid(nTab))
                    pDoc->InterpretDirtyCells(ScRange(0, 0, nTab, pDoc->MaxCol(), pDoc->MaxRow(), nTab));
 
            // stored cell styles
            const std::vector<ScCellStyleEntry>& rCellEntries = pSheetData->GetCellStyles();
            for (const auto& rCellEntry : rCellEntries)
            {
                ScAddress aPos = rCellEntry.maCellPos;
                sal_Int32 nTable = aPos.Tab();
                bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
                if (bCopySheet)
                {
                    uno::Reference <sheet::XSpreadsheet> xTable(xIndex->getByIndex(nTable), uno::UNO_QUERY);
                    uno::Reference <beans::XPropertySet> xProperties(
                        xTable->getCellByPosition( aPos.Col(), aPos.Row() ), uno::UNO_QUERY );
 
                    AddStyleFromCells(xProperties, xTable, nTable, &rCellEntry.maName);
                }
            }
 
            // stored column styles
            const std::vector<ScCellStyleEntry>& rColumnEntries = pSheetData->GetColumnStyles();
            for (const auto& rColumnEntry : rColumnEntries)
            {
                ScAddress aPos = rColumnEntry.maCellPos;
                sal_Int32 nTable = aPos.Tab();
                bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
                if (bCopySheet)
                {
                    uno::Reference<table::XColumnRowRange> xColumnRowRange(xIndex->getByIndex(nTable), uno::UNO_QUERY);
                    uno::Reference<table::XTableColumns> xTableColumns(xColumnRowRange->getColumns());
                    uno::Reference<beans::XPropertySet> xColumnProperties(xTableColumns->getByIndex( aPos.Col() ), uno::UNO_QUERY);
 
                    sal_Int32 nIndex(-1);
                    bool bIsVisible(true);
                    AddStyleFromColumn( xColumnProperties, &rColumnEntry.maName, nIndex, bIsVisible );
                }
            }
 
            // stored row styles
            const std::vector<ScCellStyleEntry>& rRowEntries = pSheetData->GetRowStyles();
            for (const auto& rRowEntry : rRowEntries)
            {
                ScAddress aPos = rRowEntry.maCellPos;
                sal_Int32 nTable = aPos.Tab();
                bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
                if (bCopySheet)
                {
                    uno::Reference<table::XColumnRowRange> xColumnRowRange(xIndex->getByIndex(nTable), uno::UNO_QUERY);
                    uno::Reference<table::XTableRows> xTableRows(xColumnRowRange->getRows());
                    uno::Reference<beans::XPropertySet> xRowProperties(xTableRows->getByIndex( aPos.Row() ), uno::UNO_QUERY);
 
                    sal_Int32 nIndex(-1);
                    AddStyleFromRow( xRowProperties, &rRowEntry.maName, nIndex );
                }
            }
 
            // stored table styles
            const std::vector<ScCellStyleEntry>& rTableEntries = pSheetData->GetTableStyles();
            for (const auto& rTableEntry : rTableEntries)
            {
                ScAddress aPos = rTableEntry.maCellPos;
                sal_Int32 nTable = aPos.Tab();
                bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
                if (bCopySheet)
                {
                    //! separate method AddStyleFromTable needed?
                    uno::Reference<beans::XPropertySet> xTableProperties(xIndex->getByIndex(nTable), uno::UNO_QUERY);
                    if (xTableProperties.is())
                    {
                        std::vector<XMLPropertyState> aPropStates(xTableStylesExportPropertySetMapper->Filter(*this, xTableProperties));
                        OUString sName( rTableEntry.maName );
                        GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TABLE_TABLE, OUString(), std::move(aPropStates));
                        GetAutoStylePool()->RegisterName(XmlStyleFamily::TABLE_TABLE, sName);
                    }
                }
            }
 
            // stored styles for notes
 
            rtl::Reference<SvXMLExportPropertyMapper> xShapeMapper = XMLShapeExport::CreateShapePropMapper( *this );
 
            const std::vector<ScNoteStyleEntry>& rNoteEntries = pSheetData->GetNoteStyles();
            for (const auto& rNoteEntry : rNoteEntries)
            {
                ScAddress aPos = rNoteEntry.maCellPos;
                SCTAB nTable = aPos.Tab();
                bool bCopySheet = pDoc->IsStreamValid( nTable );
                if (bCopySheet)
                {
                    //! separate method AddStyleFromNote needed?
 
                    ScPostIt* pNote = pDoc->GetNote(aPos);
                    OSL_ENSURE( pNote, "note not found" );
                    if (pNote)
                    {
                        SdrCaptionObj* pDrawObj = pNote->GetOrCreateCaption( aPos );
                        // all uno shapes are created anyway in CollectSharedData
                        uno::Reference<beans::XPropertySet> xShapeProperties( pDrawObj->getUnoShape(), uno::UNO_QUERY );
                        if (xShapeProperties.is())
                        {
                            if ( !rNoteEntry.maStyleName.isEmpty() )
                            {
                                std::vector<XMLPropertyState> aPropStates(xShapeMapper->Filter(*this, xShapeProperties));
                                OUString sName( rNoteEntry.maStyleName );
                                GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::SD_GRAPHICS_ID, OUString(), std::move(aPropStates));
                                GetAutoStylePool()->RegisterName(XmlStyleFamily::SD_GRAPHICS_ID, sName);
                            }
                            if ( !rNoteEntry.maTextStyle.isEmpty() )
                            {
                                std::vector<XMLPropertyState> aPropStates(
                                    GetTextParagraphExport()->GetParagraphPropertyMapper()->Filter(*this, xShapeProperties));
                                OUString sName( rNoteEntry.maTextStyle );
                                GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TEXT_PARAGRAPH, OUString(), std::move(aPropStates));
                                GetAutoStylePool()->RegisterName(XmlStyleFamily::TEXT_PARAGRAPH, sName);
                            }
                        }
                    }
                }
            }
 
            // note paragraph styles
 
            rtl::Reference<SvXMLExportPropertyMapper> xParaPropMapper = GetTextParagraphExport()->GetParagraphPropertyMapper();
 
            const std::vector<ScTextStyleEntry>& rNoteParaEntries = pSheetData->GetNoteParaStyles();
            for (const auto& rNoteParaEntry : rNoteParaEntries)
            {
                ScAddress aPos = rNoteParaEntry.maCellPos;
                SCTAB nTable = aPos.Tab();
                bool bCopySheet = pDoc->IsStreamValid( nTable );
                if (bCopySheet)
                {
                    ScPostIt* pNote = pDoc->GetNote( aPos );
                    OSL_ENSURE( pNote, "note not found" );
                    if (pNote)
                    {
                        SdrCaptionObj* pDrawObj = pNote->GetOrCreateCaption( aPos );
                        uno::Reference<container::XEnumerationAccess> xCellText(pDrawObj->getUnoShape(), uno::UNO_QUERY);
                        uno::Reference<beans::XPropertySet> xParaProp(
                            lcl_GetEnumerated( xCellText, rNoteParaEntry.maSelection.nStartPara ), uno::UNO_QUERY );
                        if ( xParaProp.is() )
                        {
                            std::vector<XMLPropertyState> aPropStates(xParaPropMapper->Filter(*this, xParaProp));
                            OUString sName( rNoteParaEntry.maName );
                            GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TEXT_PARAGRAPH, OUString(), std::move(aPropStates));
                            GetAutoStylePool()->RegisterName(XmlStyleFamily::TEXT_PARAGRAPH, sName);
                        }
                    }
                }
            }
 
            // note text styles
 
            rtl::Reference<SvXMLExportPropertyMapper> xTextPropMapper = XMLTextParagraphExport::CreateCharExtPropMapper( *this );
 
            const std::vector<ScTextStyleEntry>& rNoteTextEntries = pSheetData->GetNoteTextStyles();
            for (const auto& rNoteTextEntry : rNoteTextEntries)
            {
                ScAddress aPos = rNoteTextEntry.maCellPos;
                SCTAB nTable = aPos.Tab();
                bool bCopySheet = pDoc->IsStreamValid( nTable );
                if (bCopySheet)
                {
                    ScPostIt* pNote = pDoc->GetNote( aPos );
                    OSL_ENSURE( pNote, "note not found" );
                    if (pNote)
                    {
                        SdrCaptionObj* pDrawObj = pNote->GetOrCreateCaption( aPos );
                        uno::Reference<text::XSimpleText> xCellText(pDrawObj->getUnoShape(), uno::UNO_QUERY);
                        uno::Reference<beans::XPropertySet> xCursorProp(xCellText->createTextCursor(), uno::UNO_QUERY);
                        ScDrawTextCursor* pCursor = comphelper::getFromUnoTunnel<ScDrawTextCursor>( xCursorProp );
                        if (pCursor)
                        {
                            pCursor->SetSelection( rNoteTextEntry.maSelection );
 
                            std::vector<XMLPropertyState> aPropStates(xTextPropMapper->Filter(*this, xCursorProp));
                            OUString sName( rNoteTextEntry.maName );
                            GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TEXT_TEXT, OUString(), std::move(aPropStates));
                            GetAutoStylePool()->RegisterName(XmlStyleFamily::TEXT_TEXT, sName);
                        }
                    }
                }
            }
 
            // stored text styles
 
            // Calling createTextCursor fires up editeng, which is very slow, and often subsequent style entries
            // refer to the same cell, so cache it.
            ScAddress aPrevPos;
            uno::Reference<beans::XPropertySet> xPrevCursorProp;
            const std::vector<ScTextStyleEntry>& rTextEntries = pSheetData->GetTextStyles();
            for (const auto& rTextEntry : rTextEntries)
            {
                ScAddress aPos = rTextEntry.maCellPos;
                sal_Int32 nTable = aPos.Tab();
                bool bCopySheet = pDoc->IsStreamValid( static_cast<SCTAB>(nTable) );
                if (!bCopySheet)
                    continue;
 
                //! separate method AddStyleFromText needed?
                //! cache sheet object
 
                uno::Reference<beans::XPropertySet> xCursorProp;
                if (xPrevCursorProp && aPrevPos == aPos)
                    xCursorProp = xPrevCursorProp;
                else
                {
                    uno::Reference<table::XCellRange> xCellRange(xIndex->getByIndex(nTable), uno::UNO_QUERY);
                    uno::Reference<text::XSimpleText> xCellText(xCellRange->getCellByPosition(aPos.Col(), aPos.Row()), uno::UNO_QUERY);
                    xCursorProp.set(xCellText->createTextCursor(), uno::UNO_QUERY);
                }
                ScCellTextCursor* pCursor = comphelper::getFromUnoTunnel<ScCellTextCursor>( xCursorProp );
                if (!pCursor)
                    continue;
                pCursor->SetSelection( rTextEntry.maSelection );
 
                std::vector<XMLPropertyState> aPropStates(xTextPropMapper->Filter(*this, xCursorProp));
                OUString sName( rTextEntry.maName );
                GetAutoStylePool()->AddNamed(sName, XmlStyleFamily::TEXT_TEXT, OUString(), std::move(aPropStates));
                GetAutoStylePool()->RegisterName(XmlStyleFamily::TEXT_TEXT, sName);
                xPrevCursorProp = std::move(xCursorProp);
                aPrevPos = aPos;
            }
        }
 
        ExportExternalRefCacheStyles();
 
        if (!pSharedData)
        {
            SCTAB nTableCount(0);
            sal_Int32 nShapesCount(0);
            CollectSharedData(nTableCount, nShapesCount);
        }
        sal_Int32 nTableCount(xIndex->getCount());
        CollectShapesAutoStyles(nTableCount);
        for (sal_Int32 nTable = 0; nTable < nTableCount; ++nTable, IncrementProgressBar(false))
        {
            uno::Reference <sheet::XSpreadsheet> xTable(xIndex->getByIndex(nTable), uno::UNO_QUERY);
            if (!xTable.is())
                continue;
 
            // table styles array must be complete, including copied tables - Add should find the stored style
            uno::Reference<beans::XPropertySet> xTableProperties(xTable, uno::UNO_QUERY);
            if (xTableProperties.is())
            {
                std::vector<XMLPropertyState> aPropStates(xTableStylesExportPropertySetMapper->Filter(*this, xTableProperties));
                if(!aPropStates.empty())
                {
                    OUString sName;
                    GetAutoStylePool()->Add(sName, XmlStyleFamily::TABLE_TABLE, OUString(), std::move(aPropStates));
                    aTableStyles.push_back(sName);
                }
            }
 
            // collect other auto-styles only for non-copied sheets
            uno::Reference<sheet::XUniqueCellFormatRangesSupplier> xCellFormatRanges ( xTable, uno::UNO_QUERY );
            if ( xCellFormatRanges.is() )
            {
                uno::Reference<container::XIndexAccess> xFormatRangesIndex(xCellFormatRanges->getUniqueCellFormatRanges());
                if (xFormatRangesIndex.is())
                {
                    sal_Int32 nFormatRangesCount(xFormatRangesIndex->getCount());
                    GetProgressBarHelper()->ChangeReference(GetProgressBarHelper()->GetReference() + nFormatRangesCount);
                    for (sal_Int32 nFormatRange = 0; nFormatRange < nFormatRangesCount; ++nFormatRange)
                    {
                        uno::Reference< sheet::XSheetCellRanges> xCellRanges(xFormatRangesIndex->getByIndex(nFormatRange), uno::UNO_QUERY);
                        if (xCellRanges.is())
                        {
                            uno::Reference <beans::XPropertySet> xProperties (xCellRanges, uno::UNO_QUERY);
                            if (xProperties.is())
                            {
                                AddStyleFromCells(xProperties, xTable, nTable, nullptr);
                                IncrementProgressBar(false);
                            }
                        }
                    }
                }
            }
            uno::Reference<table::XColumnRowRange> xColumnRowRange (xTable, uno::UNO_QUERY);
            if (xColumnRowRange.is() && pDoc)
            {
                pDoc->SyncColRowFlags();
                uno::Reference<table::XTableColumns> xTableColumns(xColumnRowRange->getColumns());
                if (xTableColumns.is())
                {
                    sal_Int32 nColumns(pDoc->GetLastChangedColFlagsWidth(sal::static_int_cast<SCTAB>(nTable)));
                    pSharedData->SetLastColumn(nTable, nColumns);
                    table::CellRangeAddress aCellAddress(GetEndAddress(xTable));
                    if (aCellAddress.EndColumn > nColumns)
                    {
                        ++nColumns;
                        pColumnStyles->AddNewTable(nTable, aCellAddress.EndColumn);
                    }
                    else
                        pColumnStyles->AddNewTable(nTable, nColumns);
                    sal_Int32 nColumn = 0;
                    while (nColumn <= pDoc->MaxCol())
                    {
                        sal_Int32 nIndex(-1);
                        bool bIsVisible(true);
                        uno::Reference <beans::XPropertySet> xColumnProperties(xTableColumns->getByIndex(nColumn), uno::UNO_QUERY);
                        if (xColumnProperties.is())
                        {
                            AddStyleFromColumn( xColumnProperties, nullptr, nIndex, bIsVisible );
                            pColumnStyles->AddFieldStyleName(nTable, nColumn, nIndex, bIsVisible);
                        }
                        sal_Int32 nOld(nColumn);
                        nColumn = pDoc->GetNextDifferentChangedColFlagsWidth(sal::static_int_cast<SCTAB>(nTable), static_cast<SCCOL>(nColumn));
                        for (sal_Int32 i = nOld + 1; i < nColumn; ++i)
                            pColumnStyles->AddFieldStyleName(nTable, i, nIndex, bIsVisible);
                    }
                    if (aCellAddress.EndColumn > nColumns)
                    {
                        bool bIsVisible(true);
                        sal_Int32 nIndex(pColumnStyles->GetStyleNameIndex(nTable, nColumns, bIsVisible));
                        for (sal_Int32 i = nColumns + 1; i <= aCellAddress.EndColumn; ++i)
                            pColumnStyles->AddFieldStyleName(nTable, i, nIndex, bIsVisible);
                    }
                }
                uno::Reference<table::XTableRows> xTableRows(xColumnRowRange->getRows());
                if (xTableRows.is())
                {
                    sal_Int32 nRows(pDoc->GetLastChangedRowFlagsWidth(sal::static_int_cast<SCTAB>(nTable)));
                    pSharedData->SetLastRow(nTable, nRows);
 
                    pRowStyles->AddNewTable(nTable, pDoc->MaxRow());
                    sal_Int32 nRow = 0;
                    while (nRow <= pDoc->MaxRow())
                    {
                        sal_Int32 nIndex = 0;
                        uno::Reference <beans::XPropertySet> xRowProperties(xTableRows->getByIndex(nRow), uno::UNO_QUERY);
                        if(xRowProperties.is())
                        {
                            AddStyleFromRow( xRowProperties, nullptr, nIndex );
                            pRowStyles->AddFieldStyleName(nTable, nRow, nIndex);
                        }
                        sal_Int32 nOld(nRow);
                        nRow = pDoc->GetNextDifferentChangedRowFlagsWidth(sal::static_int_cast<SCTAB>(nTable), static_cast<SCROW>(nRow));
                        if (nRow > nOld + 1)
                            pRowStyles->AddFieldStyleName(nTable, nOld + 1, nIndex, nRow - 1);
                    }
                }
            }
            ExportCellTextAutoStyles(nTable);
        }
 
        pChangeTrackingExportHelper->CollectAutoStyles();
    }
 
    if (getExportFlags() & SvXMLExportFlags::MASTERSTYLES)
        // tdf#154445 - export all page styles even if they are not in use
        GetPageExport()->collectAutoStyles(false);
 
    mbAutoStylesCollected = true;
}
 
void ScXMLExport::ExportAutoStyles_()
{
    if (!GetModel().is())
        return;
 
    uno::Reference <sheet::XSpreadsheetDocument> xSpreadDoc( GetModel(), uno::UNO_QUERY );
    if (!xSpreadDoc.is())
        return;
 
    uno::Reference<container::XIndexAccess> xIndex( xSpreadDoc->getSheets(), uno::UNO_QUERY );
    if (!xIndex.is())
        return;
 
    collectAutoStyles();
 
    if (getExportFlags() & SvXMLExportFlags::CONTENT)
    {
        GetAutoStylePool()->exportXML(XmlStyleFamily::TABLE_COLUMN);
        GetAutoStylePool()->exportXML(XmlStyleFamily::TABLE_ROW);
        GetAutoStylePool()->exportXML(XmlStyleFamily::TABLE_TABLE);
        exportAutoDataStyles();
        GetAutoStylePool()->exportXML(XmlStyleFamily::TABLE_CELL);
 
        GetShapeExport()->exportAutoStyles();
        GetFormExport()->exportAutoStyles( );
 
        if (pDoc)
        {
            ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
            // #i100879# write the table style for cached tables only if there are cached tables
            // (same logic as in ExportExternalRefCacheStyles)
            if (pRefMgr->hasExternalData())
            {
                // Special table style for the external ref cache tables.
                AddAttribute(XML_NAMESPACE_STYLE, XML_NAME, sExternalRefTabStyleName);
                AddAttribute(XML_NAMESPACE_STYLE, XML_FAMILY, XML_TABLE);
                SvXMLElementExport aElemStyle(*this, XML_NAMESPACE_STYLE, XML_STYLE, true, true);
                AddAttribute(XML_NAMESPACE_TABLE,  XML_DISPLAY, XML_FALSE);
                SvXMLElementExport aElemStyleTabProps(*this, XML_NAMESPACE_STYLE, XML_TABLE_PROPERTIES, true, true);
            }
        }
    }
 
    if (getExportFlags() & SvXMLExportFlags::MASTERSTYLES)
    {
        exportAutoDataStyles();
        GetPageExport()->exportAutoStyles();
    }
 
    // #i30251#; only write Text Styles once
 
    if ((getExportFlags() & SvXMLExportFlags::CONTENT) || (getExportFlags() & SvXMLExportFlags::MASTERSTYLES))
        GetTextParagraphExport()->exportTextAutoStyles();
}
 
void ScXMLExport::ExportMasterStyles_()
{
    // tdf#154445 - export all page styles even if they are not in use
    GetPageExport()->exportMasterStyles( false );
}
 
void ScXMLExport::CollectInternalShape( uno::Reference< drawing::XShape > const & xShape )
{
    // detective objects and notes
    SdrObject* pObject = SdrObject::getSdrObjectFromXShape( xShape );
    if( !pObject )
        return;
 
    // collect note caption objects from all layers (internal or hidden)
    if( ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( pObject, static_cast< SCTAB >( nCurrentTable ) ) )
    {
        if(pDoc->GetNote(pCaptData->maStart))
        {
            pSharedData->AddNoteObj( xShape, pCaptData->maStart );
 
            // #i60851# When the file is saved while editing a new note,
            // the cell is still empty -> last column/row must be updated
            OSL_ENSURE( pCaptData->maStart.Tab() == nCurrentTable, "invalid table in object data" );
            pSharedData->SetLastColumn( nCurrentTable, pCaptData->maStart.Col() );
            pSharedData->SetLastRow( nCurrentTable, pCaptData->maStart.Row() );
        }
    }
    // other objects from internal layer only (detective)
    else if( pObject->GetLayer() == SC_LAYER_INTERN )
    {
        ScDetectiveFunc aDetFunc( *pDoc, static_cast<SCTAB>(nCurrentTable) );
        ScAddress       aPosition;
        ScRange         aSourceRange;
        bool            bRedLine;
        ScDetectiveObjType eObjType = aDetFunc.GetDetectiveObjectType(
            pObject, nCurrentTable, aPosition, aSourceRange, bRedLine );
        pSharedData->GetDetectiveObjContainer()->AddObject( eObjType, static_cast<SCTAB>(nCurrentTable), aPosition, aSourceRange, bRedLine );
    }
}
 
bool ScXMLExport::GetMerged (const table::CellRangeAddress* pCellAddress,
                            const uno::Reference <sheet::XSpreadsheet>& xTable)
{
    bool bReady(false);
    sal_Int32 nRow(pCellAddress->StartRow);
    sal_Int32 nCol(pCellAddress->StartColumn);
    sal_Int32 nEndRow(pCellAddress->EndRow);
    sal_Int32 nEndCol(pCellAddress->EndColumn);
    bool bRowInc(nEndRow > nRow);
    while(!bReady && nRow <= nEndRow && nCol <= nEndCol)
    {
        uno::Reference<sheet::XSheetCellRange> xSheetCellRange(xTable->getCellRangeByPosition(nCol, nRow, nCol, nRow), uno::UNO_QUERY);
        if (xSheetCellRange.is())
        {
            uno::Reference<sheet::XSheetCellCursor> xCursor(xTable->createCursorByRange(xSheetCellRange));
            if(xCursor.is())
            {
                uno::Reference<sheet::XCellRangeAddressable> xCellAddress (xCursor, uno::UNO_QUERY);
                xCursor->collapseToMergedArea();
                table::CellRangeAddress aCellAddress2(xCellAddress->getRangeAddress());
                ScRange aScRange( aCellAddress2.StartColumn, aCellAddress2.StartRow, aCellAddress2.Sheet,
                                  aCellAddress2.EndColumn, aCellAddress2.EndRow, aCellAddress2.Sheet );
 
                if ((aScRange.aEnd.Row() > nRow ||
                    aScRange.aEnd.Col() > nCol) &&
                    aScRange.aStart.Row() == nRow &&
                    aScRange.aStart.Col() == nCol)
                {
                    pMergedRangesContainer->AddRange(aScRange);
                    pSharedData->SetLastColumn(aScRange.aEnd.Tab(), aScRange.aEnd.Col());
                    pSharedData->SetLastRow(aScRange.aEnd.Tab(), aScRange.aEnd.Row());
                }
                else
                    bReady = true;
            }
        }
        if (!bReady)
        {
            if (bRowInc)
                ++nRow;
            else
                ++nCol;
        }
    }
    OSL_ENSURE(!(!bReady && nEndRow > nRow && nEndCol > nCol), "should not be possible");
    return !bReady;
}
 
bool ScXMLExport::IsMatrix (const ScAddress& aCell,
                            ScRange& aCellAddress, bool& bIsFirst) const
{
    bIsFirst = false;
 
    ScRange aMatrixRange;
 
    if (pDoc && pDoc->GetMatrixFormulaRange(aCell, aMatrixRange))
    {
        aCellAddress = aMatrixRange;
        if ((aCellAddress.aStart.Col() == aCell.Col() && aCellAddress.aStart.Row() == aCell.Row()) &&
            (aCellAddress.aEnd.Col() > aCell.Col() || aCellAddress.aEnd.Row() > aCell.Row()))
        {
            bIsFirst = true;
            return true;
        }
        else if (aCellAddress.aStart.Col() != aCell.Col() || aCellAddress.aStart.Row() != aCell.Row() ||
            aCellAddress.aEnd.Col() != aCell.Col() || aCellAddress.aEnd.Row()!= aCell.Row())
            return true;
        else
        {
            bIsFirst = true;
            return true;
        }
    }
 
    return false;
}
 
void ScXMLExport::WriteTable(sal_Int32 nTable, const uno::Reference<sheet::XSpreadsheet>& xTable)
{
    if (!xTable.is())
        return;
 
    xCurrentTable.set(xTable);
    uno::Reference<container::XNamed> xName (xTable, uno::UNO_QUERY );
    if (!xName.is())
        return;
 
    nCurrentTable = sal::static_int_cast<sal_uInt16>( nTable );
    OUString sOUTableName(xName->getName());
    AddAttribute(sAttrName, sOUTableName);
    AddAttribute(sAttrStyleName, aTableStyles[nTable]);
 
    uno::Reference<util::XProtectable> xProtectable (xTable, uno::UNO_QUERY);
    const ScTableProtection* pProtect = nullptr;
    if (xProtectable.is() && xProtectable->isProtected())
    {
        AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTED, XML_TRUE);
        if (pDoc)
        {
            pProtect = pDoc->GetTabProtection(nTable);
            if (pProtect)
            {
                OUStringBuffer aBuffer;
                ScPasswordHash eHashUsed = PASSHASH_UNSPECIFIED;
                if (pProtect->hasPasswordHash(PASSHASH_SHA1))
                {
                    ::comphelper::Base64::encode(aBuffer,
                        pProtect->getPasswordHash(PASSHASH_SHA1));
                    eHashUsed = PASSHASH_SHA1;
                }
                else if (pProtect->hasPasswordHash(PASSHASH_SHA256))
                {
                    ::comphelper::Base64::encode(aBuffer,
                        pProtect->getPasswordHash(PASSHASH_SHA256));
                    eHashUsed = PASSHASH_SHA256;
                }
                else if (pProtect->hasPasswordHash(PASSHASH_XL, PASSHASH_SHA1))
                {
                    // Double-hash this by SHA1 on top of the legacy xls hash.
                    uno::Sequence<sal_Int8> aHash = pProtect->getPasswordHash(PASSHASH_XL, PASSHASH_SHA1);
                    ::comphelper::Base64::encode(aBuffer, aHash);
                    eHashUsed = PASSHASH_XL;
                }
                if (!aBuffer.isEmpty())
                {
                    AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY, aBuffer.makeStringAndClear());
                    if (getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012)
                    {
                        if (eHashUsed == PASSHASH_XL)
                        {
                            AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
                                         ScPassHashHelper::getHashURI(PASSHASH_XL));
                            if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
                                AddAttribute(XML_NAMESPACE_LO_EXT, XML_PROTECTION_KEY_DIGEST_ALGORITHM_2,
                                        ScPassHashHelper::getHashURI(PASSHASH_SHA1));
                        }
                        else if (eHashUsed == PASSHASH_SHA1)
                        {
                            AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
                                         ScPassHashHelper::getHashURI(PASSHASH_SHA1));
                        }
                        else if (eHashUsed == PASSHASH_SHA256)
                        {
                            AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTION_KEY_DIGEST_ALGORITHM,
                                         ScPassHashHelper::getHashURI(PASSHASH_SHA256));
                        }
                    }
                }
            }
        }
    }
    OUString sPrintRanges;
    ScRange aColumnHeaderRange;
    bool bHasColumnHeader;
    GetColumnRowHeader(bHasColumnHeader, aColumnHeaderRange, bHasRowHeader, aRowHeaderRange, sPrintRanges);
    if( !sPrintRanges.isEmpty() )
        AddAttribute( XML_NAMESPACE_TABLE, XML_PRINT_RANGES, sPrintRanges );
    else if (pDoc && !pDoc->IsPrintEntireSheet(static_cast<SCTAB>(nTable)))
        AddAttribute( XML_NAMESPACE_TABLE, XML_PRINT, XML_FALSE);
    SvXMLElementExport aElemT(*this, sElemTab, true, true);
 
    if (pProtect && pProtect->isProtected() && getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
    {
        if (pProtect->isOptionEnabled(ScTableProtection::SELECT_LOCKED_CELLS))
            AddAttribute(XML_NAMESPACE_LO_EXT, XML_SELECT_PROTECTED_CELLS, XML_TRUE);
        if (pProtect->isOptionEnabled(ScTableProtection::SELECT_UNLOCKED_CELLS))
            AddAttribute(XML_NAMESPACE_LO_EXT, XML_SELECT_UNPROTECTED_CELLS, XML_TRUE);
 
        if (pProtect->isOptionEnabled(ScTableProtection::INSERT_COLUMNS))
            AddAttribute(XML_NAMESPACE_LO_EXT, XML_INSERT_COLUMNS, XML_TRUE);
        if (pProtect->isOptionEnabled(ScTableProtection::INSERT_ROWS))
            AddAttribute(XML_NAMESPACE_LO_EXT, XML_INSERT_ROWS, XML_TRUE);
 
        if (pProtect->isOptionEnabled(ScTableProtection::DELETE_COLUMNS))
            AddAttribute(XML_NAMESPACE_LO_EXT, XML_DELETE_COLUMNS, XML_TRUE);
        if (pProtect->isOptionEnabled(ScTableProtection::DELETE_ROWS))
            AddAttribute(XML_NAMESPACE_LO_EXT, XML_DELETE_ROWS, XML_TRUE);
 
        OUString aElemName = GetNamespaceMap().GetQNameByKey(
            XML_NAMESPACE_LO_EXT, GetXMLToken(XML_TABLE_PROTECTION));
 
        SvXMLElementExport aElemProtected(*this, aElemName, true, true);
    }
 
    CheckAttrList();
 
    if ( pDoc && pDoc->GetSheetEvents( static_cast<SCTAB>(nTable) ) &&
        getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012)
    {
        // store sheet events
        uno::Reference<document::XEventsSupplier> xSupplier(xTable, uno::UNO_QUERY);
        uno::Reference<container::XNameAccess> xEvents = xSupplier->getEvents();
        GetEventExport().ExportExt( xEvents );
    }
 
    WriteTableSource();
    WriteScenario();
    uno::Reference<drawing::XDrawPage> xDrawPage;
    if (pSharedData->HasForm(nTable, xDrawPage) && xDrawPage.is())
    {
        ::xmloff::OOfficeFormsExport aForms(*this);
        GetFormExport()->exportForms( xDrawPage );
        bool bRet(GetFormExport()->seekPage( xDrawPage ));
        OSL_ENSURE( bRet, "OFormLayerXMLExport::seekPage failed!" );
    }
    if (pSharedData->HasDrawPage())
    {
        GetShapeExport()->seekShapes(pSharedData->GetDrawPage(nTable));
        WriteTableShapes();
    }
    table::CellRangeAddress aRange(GetEndAddress(xTable));
    pSharedData->SetLastColumn(nTable, aRange.EndColumn);
    pSharedData->SetLastRow(nTable, aRange.EndRow);
    mpCellsItr->SetCurrentTable(static_cast<SCTAB>(nTable), xCurrentTable);
    pGroupColumns->NewTable();
    pGroupRows->NewTable();
    FillColumnRowGroups();
    if (bHasColumnHeader)
        pSharedData->SetLastColumn(nTable, aColumnHeaderRange.aEnd.Col());
    bRowHeaderOpen = false;
    if (bHasRowHeader)
        pSharedData->SetLastRow(nTable, aRowHeaderRange.aEnd.Row());
    pDefaults->FillDefaultStyles(nTable, pSharedData->GetLastRow(nTable),
        pSharedData->GetLastColumn(nTable), pCellStyles.get(), pDoc);
    pRowFormatRanges->SetColDefaults(&pDefaults->GetColDefaults());
    pCellStyles->SetColDefaults(&pDefaults->GetColDefaults());
    ExportColumns(nTable, aColumnHeaderRange, bHasColumnHeader);
    bool bIsFirst(true);
    sal_Int32 nEqualCells(0);
    ScMyCell aCell;
    ScMyCell aPrevCell;
    while (mpCellsItr->GetNext(aCell, pCellStyles.get()))
    {
        if (bIsFirst)
        {
            ExportFormatRanges(0, 0, aCell.maCellAddress.Col()-1, aCell.maCellAddress.Row(), nTable);
            aPrevCell = aCell;
            bIsFirst = false;
        }
        else
        {
            if ((aPrevCell.maCellAddress.Row() == aCell.maCellAddress.Row()) &&
                (aPrevCell.maCellAddress.Col() + nEqualCells + 1 == aCell.maCellAddress.Col()))
            {
                if(IsCellEqual(aPrevCell, aCell))
                    ++nEqualCells;
                else
                {
                    WriteCell(aPrevCell, nEqualCells);
                    nEqualCells = 0;
                    aPrevCell = aCell;
                }
            }
            else
            {
                WriteCell(aPrevCell, nEqualCells);
                ExportFormatRanges(aPrevCell.maCellAddress.Col() + nEqualCells + 1, aPrevCell.maCellAddress.Row(),
                    aCell.maCellAddress.Col()-1, aCell.maCellAddress.Row(), nTable);
                nEqualCells = 0;
                aPrevCell = aCell;
            }
        }
    }
    if (!bIsFirst)
    {
        WriteCell(aPrevCell, nEqualCells);
        ExportFormatRanges(aPrevCell.maCellAddress.Col() + nEqualCells + 1, aPrevCell.maCellAddress.Row(),
            pSharedData->GetLastColumn(nTable), pSharedData->GetLastRow(nTable), nTable);
    }
    else
        ExportFormatRanges(0, 0, pSharedData->GetLastColumn(nTable), pSharedData->GetLastRow(nTable), nTable);
 
    CloseRow(pSharedData->GetLastRow(nTable));
 
    if (!pDoc)
        return;
 
    // Export sheet-local named ranges.
    ScRangeName* pRangeName = pDoc->GetRangeName(nTable);
    if (pRangeName && !pRangeName->empty())
    {
        WriteNamedRange(pRangeName);
    }
 
    if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
    {
        //export new conditional format information
        ExportConditionalFormat(nTable);
        exportSparklineGroups(nTable);
    }
}
 
namespace {
 
void writeContent(
    ScXMLExport& rExport, const OUString& rStyleName, const OUString& rContent, const SvxFieldData* pField )
{
    std::unique_ptr<SvXMLElementExport> pElem;
    if (!rStyleName.isEmpty())
    {
        // Formatted section with automatic style.
        rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_STYLE_NAME, rStyleName);
        OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
            XML_NAMESPACE_TEXT, GetXMLToken(XML_SPAN));
        pElem.reset(new SvXMLElementExport(rExport, aElemName, false, false));
    }
 
    if (pField)
    {
        // Write a field item.
        OUString aFieldVal = ScEditUtil::GetCellFieldValue(*pField, rExport.GetDocument(), nullptr, nullptr);
        switch (pField->GetClassId())
        {
            case text::textfield::Type::URL:
            {
                // <text:a xlink:href="url" xlink:type="simple">value</text:a>
 
                const SvxURLField* pURLField = static_cast<const SvxURLField*>(pField);
                const OUString& aURL = pURLField->GetURL();
                rExport.AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, rExport.GetRelativeReference(aURL));
                rExport.AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, u"simple"_ustr);
                const OUString& aTargetFrame = pURLField->GetTargetFrame();
                if (!aTargetFrame.isEmpty())
                    rExport.AddAttribute(XML_NAMESPACE_OFFICE, XML_TARGET_FRAME_NAME, aTargetFrame);
 
                OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
                    XML_NAMESPACE_TEXT, GetXMLToken(XML_A));
                SvXMLElementExport aElem(rExport, aElemName, false, false);
                rExport.Characters(aFieldVal);
            }
            break;
            case text::textfield::Type::DATE:
            {
                // <text:date style:data-style-name="N2" text:date-value="YYYY-MM-DD">value</text:date>
 
                Date aDate(Date::SYSTEM);
                OUStringBuffer aBuf;
                sal_Int32 nVal = aDate.GetYear();
                aBuf.append(OUString::number(nVal) + "-");
                nVal = aDate.GetMonth();
                if (nVal < 10)
                    aBuf.append('0');
                aBuf.append(OUString::number(nVal) + "-");
                nVal = aDate.GetDay();
                if (nVal < 10)
                    aBuf.append('0');
                aBuf.append(nVal);
                rExport.AddAttribute(XML_NAMESPACE_STYLE, XML_DATA_STYLE_NAME, u"N2"_ustr);
                rExport.AddAttribute(XML_NAMESPACE_TEXT, XML_DATE_VALUE, aBuf.makeStringAndClear());
 
                OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
                    XML_NAMESPACE_TEXT, GetXMLToken(XML_DATE));
                SvXMLElementExport aElem(rExport, aElemName, false, false);
                rExport.Characters(aFieldVal);
            }
            break;
            case text::textfield::Type::DOCINFO_TITLE:
            {
                // <text:title>value</text:title>
 
                OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
                    XML_NAMESPACE_TEXT, GetXMLToken(XML_TITLE));
                SvXMLElementExport aElem(rExport, aElemName, false, false);
                rExport.Characters(aFieldVal);
            }
            break;
            case text::textfield::Type::TABLE:
            {
                // <text:sheet-name>value</text:sheet-name>
 
                OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
                    XML_NAMESPACE_TEXT, GetXMLToken(XML_SHEET_NAME));
                SvXMLElementExport aElem(rExport, aElemName, false, false);
                rExport.Characters(aFieldVal);
            }
            break;
            default:
                rExport.Characters(aFieldVal);
        }
    }
    else
        rExport.Characters(rContent);
}
 
void flushParagraph(
    ScXMLExport& rExport, std::u16string_view rParaText,
    rtl::Reference<XMLPropertySetMapper> const & xMapper, rtl::Reference<SvXMLAutoStylePoolP> const & xStylePool,
    const ScXMLEditAttributeMap& rAttrMap,
    std::vector<editeng::Section>::const_iterator it, std::vector<editeng::Section>::const_iterator const & itEnd )
{
    OUString aElemName = rExport.GetNamespaceMap().GetQNameByKey(
        XML_NAMESPACE_TEXT, GetXMLToken(XML_P));
    SvXMLElementExport aElemP(rExport, aElemName, false, false);
 
    for (; it != itEnd; ++it)
    {
        const editeng::Section& rSec = *it;
 
        OUString aContent(rParaText.substr(rSec.mnStart, rSec.mnEnd - rSec.mnStart));
 
        std::vector<XMLPropertyState> aPropStates;
        const SvxFieldData* pField = toXMLPropertyStates(rExport, aPropStates, rSec.maAttributes, xMapper, rAttrMap);
        OUString aStyleName = xStylePool->Find(XmlStyleFamily::TEXT_TEXT, OUString(), aPropStates);
        if (aContent == "\x001" && !pField)
        {
            for (const SfxPoolItem* p : rSec.maAttributes)
            {
                if (p->Which() == EE_FEATURE_TAB)
                {
                    SvXMLElementExport Tab(rExport, XML_NAMESPACE_TEXT, XML_TAB, false, false);
                    break;
                }
                else if (p->Which() == EE_FEATURE_LINEBR)
                {
                    SvXMLElementExport L(rExport, XML_NAMESPACE_TEXT, XML_LINE_BREAK, false, false);
                    break;
                }
            }
        }
        else
            writeContent(rExport, aStyleName, aContent, pField);
    }
}
 
}
 
void ScXMLExport::WriteCell(ScMyCell& aCell, sal_Int32 nEqualCellCount)
{
    // nEqualCellCount is the number of additional cells
    SetRepeatAttribute(nEqualCellCount, (aCell.nType != table::CellContentType_EMPTY));
 
    if (aCell.nStyleIndex != -1)
        AddAttribute(sAttrStyleName, pCellStyles->GetStyleNameByIndex(aCell.nStyleIndex, aCell.bIsAutoStyle));
    if (aCell.nValidationIndex > -1)
        AddAttribute(XML_NAMESPACE_TABLE, XML_CONTENT_VALIDATION_NAME, pValidationsContainer->GetValidationName(aCell.nValidationIndex));
    const bool bIsFirstMatrixCell(aCell.bIsMatrixBase);
    if (bIsFirstMatrixCell)
    {
        SCCOL nColumns( aCell.aMatrixRange.aEnd.Col() - aCell.aMatrixRange.aStart.Col() + 1 );
        SCROW nRows( aCell.aMatrixRange.aEnd.Row() - aCell.aMatrixRange.aStart.Row() + 1 );
        AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_MATRIX_COLUMNS_SPANNED, OUString::number(nColumns));
        AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_MATRIX_ROWS_SPANNED, OUString::number(nRows));
    }
    bool bIsEmpty(false);
    switch (aCell.nType)
    {
        case table::CellContentType_EMPTY :
            {
                bIsEmpty = true;
            }
            break;
        case table::CellContentType_VALUE :
            {
                GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
                    aCell.nNumberFormat, aCell.maBaseCell.getDouble());
                if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
                    GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
                            aCell.nNumberFormat, aCell.maBaseCell.getDouble(), false, XML_NAMESPACE_CALC_EXT, false);
            }
            break;
        case table::CellContentType_TEXT :
            {
                OUString sFormattedString(lcl_GetFormattedString(pDoc, aCell.maBaseCell, aCell.maCellAddress));
                OUString sCellString = aCell.maBaseCell.getString(pDoc);
                bool bExportValue = sCellString.indexOf('\x001') == -1;
                GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
                        sCellString, sFormattedString, bExportValue);
                if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
                    GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
                            sCellString, sFormattedString, false, XML_NAMESPACE_CALC_EXT);
            }
            break;
        case table::CellContentType_FORMULA :
            {
                if (aCell.maBaseCell.getType() == CELLTYPE_FORMULA)
                {
                    const bool bIsMatrix(bIsFirstMatrixCell || aCell.bIsMatrixCovered);
                    ScFormulaCell* pFormulaCell = aCell.maBaseCell.getFormula();
                    if (!bIsMatrix || bIsFirstMatrixCell)
                    {
                        if (!mpCompileFormulaCxt)
                        {
                            const formula::FormulaGrammar::Grammar eGrammar = pDoc->GetStorageGrammar();
                            mpCompileFormulaCxt.reset(new sc::CompileFormulaContext(*pDoc, eGrammar));
                        }
                        mpCompileFormulaCxt->setODFSavingVersion(getSaneDefaultVersion());
                        OUString aFormula = pFormulaCell->GetFormula(*mpCompileFormulaCxt);
                        sal_uInt16 nNamespacePrefix =
                            (mpCompileFormulaCxt->getGrammar() == formula::FormulaGrammar::GRAM_ODFF ? XML_NAMESPACE_OF : XML_NAMESPACE_OOOC);
 
                        if (!bIsMatrix)
                        {
                            AddAttribute(sAttrFormula, GetNamespaceMap().GetQNameByKey(nNamespacePrefix, aFormula, false));
                        }
                        else
                        {
                            AddAttribute(sAttrFormula, GetNamespaceMap().GetQNameByKey(nNamespacePrefix, aFormula.copy(1, aFormula.getLength()-2), false));
                        }
                    }
                    if (pFormulaCell->GetErrCode() != FormulaError::NONE)
                    {
                        AddAttribute(sAttrValueType, XML_STRING);
                        AddAttribute(sAttrStringValue, aCell.maBaseCell.getString(pDoc));
                        if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
                        {
                            //export calcext:value-type="error"
                            AddAttribute(XML_NAMESPACE_CALC_EXT,XML_VALUE_TYPE, u"error"_ustr);
                        }
                    }
                    else if (pFormulaCell->IsValue())
                    {
                        bool bIsStandard;
                        OUString sCurrency;
                        GetNumberFormatAttributesExportHelper()->GetCellType(aCell.nNumberFormat, sCurrency, bIsStandard);
                        if (pDoc)
                        {
                            GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
                                    aCell.nNumberFormat, pDoc->GetValue(aCell.maCellAddress));
                            if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
                            {
                                GetNumberFormatAttributesExportHelper()->SetNumberFormatAttributes(
                                        aCell.nNumberFormat, pDoc->GetValue(aCell.maCellAddress), false, XML_NAMESPACE_CALC_EXT, false );
                            }
                        }
                    }
                    else
                    {
                        if (!aCell.maBaseCell.getString(pDoc).isEmpty())
                        {
                            AddAttribute(sAttrValueType, XML_STRING);
                            AddAttribute(sAttrStringValue, aCell.maBaseCell.getString(pDoc));
                            if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
                            {
                                AddAttribute(XML_NAMESPACE_CALC_EXT,XML_VALUE_TYPE, XML_STRING);
                            }
                        }
                    }
                }
            }
            break;
        default:
            break;
    }
    OUString* pCellString(&sElemCell);
    if (aCell.bIsCovered)
    {
        pCellString = &sElemCoveredCell;
    }
    else
    {
        if (aCell.bIsMergedBase)
        {
            SCCOL nColumns( aCell.aMergeRange.aEnd.Col() - aCell.aMergeRange.aStart.Col() + 1 );
            SCROW nRows( aCell.aMergeRange.aEnd.Row() - aCell.aMergeRange.aStart.Row() + 1 );
            AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_SPANNED, OUString::number(nColumns));
            AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_SPANNED, OUString::number(nRows));
        }
    }
    SvXMLElementExport aElemC(*this, *pCellString, true, true);
    CheckAttrList();
    WriteAreaLink(aCell);
    WriteAnnotation(aCell);
    WriteDetective(aCell);
 
    if (!bIsEmpty)
    {
        if (aCell.maBaseCell.getType() == CELLTYPE_EDIT)
        {
            WriteEditCell(aCell.maBaseCell.getEditText());
        }
        else if (aCell.maBaseCell.getType() == CELLTYPE_FORMULA && aCell.maBaseCell.getFormula()->IsMultilineResult())
        {
            WriteMultiLineFormulaResult(aCell.maBaseCell.getFormula());
        }
        else
        {
            SvXMLElementExport aElemP(*this, sElemP, true, false);
 
            assert(pDoc);
 
            OUString aParaStr =
                ScCellFormat::GetOutputString(*pDoc, aCell.maCellAddress, aCell.maBaseCell);
 
            bool bPrevCharWasSpace = true;
            GetTextParagraphExport()->exportCharacterData(aParaStr, bPrevCharWasSpace);
        }
    }
    WriteShapes(aCell);
    if (!bIsEmpty)
        IncrementProgressBar(false);
}
 
void ScXMLExport::WriteEditCell(const EditTextObject* pText)
{
    rtl::Reference<XMLPropertySetMapper> xMapper = GetTextParagraphExport()->GetTextPropMapper()->getPropertySetMapper();
    rtl::Reference<SvXMLAutoStylePoolP> xStylePool = GetAutoStylePool();
    const ScXMLEditAttributeMap& rAttrMap = GetEditAttributeMap();
 
    // Get raw paragraph texts first.
    std::vector<OUString> aParaTexts;
    sal_Int32 nParaCount = pText->GetParagraphCount();
    aParaTexts.reserve(nParaCount);
    for (sal_Int32 i = 0; i < nParaCount; ++i)
        aParaTexts.push_back(pText->GetText(i));
 
    // Get all section data and iterate through them.
    std::vector<editeng::Section> aAttrs;
    pText->GetAllSections(aAttrs);
    std::vector<editeng::Section>::const_iterator itSec = aAttrs.begin(), itSecEnd = aAttrs.end();
    std::vector<editeng::Section>::const_iterator itPara = itSec;
    sal_Int32 nCurPara = 0; // current paragraph
    for (; itSec != itSecEnd; ++itSec)
    {
        const editeng::Section& rSec = *itSec;
        if (nCurPara == rSec.mnParagraph)
            // Still in the same paragraph.
            continue;
 
        // Start of a new paragraph. Flush the old paragraph.
        flushParagraph(*this, aParaTexts[nCurPara], xMapper, xStylePool, rAttrMap, itPara, itSec);
        nCurPara = rSec.mnParagraph;
        itPara = itSec;
    }
 
    flushParagraph(*this, aParaTexts[nCurPara], xMapper, xStylePool, rAttrMap, itPara, itSecEnd);
}
 
void ScXMLExport::WriteMultiLineFormulaResult(const ScFormulaCell* pCell)
{
    OUString aElemName = GetNamespaceMap().GetQNameByKey(XML_NAMESPACE_TEXT, GetXMLToken(XML_P));
 
    OUString aResStr = pCell->GetResultString().getString();
    const sal_Unicode* p = aResStr.getStr();
    const sal_Unicode* pEnd = p + static_cast<size_t>(aResStr.getLength());
    const sal_Unicode* pPara = p; // paragraph head.
    for (; p != pEnd; ++p)
    {
        if (*p != '\n')
            continue;
 
        // flush the paragraph.
        OUString aContent;
        if (*pPara == '\n')
            ++pPara;
        if (p > pPara)
            aContent = OUString(pPara, p-pPara);
 
        SvXMLElementExport aElem(*this, aElemName, false, false);
        Characters(aContent);
 
        pPara = p;
    }
 
    OUString aContent;
    if (*pPara == '\n')
        ++pPara;
    if (pEnd > pPara)
        aContent = OUString(pPara, pEnd-pPara);
 
    SvXMLElementExport aElem(*this, aElemName, false, false);
    Characters(aContent);
}
 
void ScXMLExport::ExportShape(const uno::Reference < drawing::XShape >& xShape, awt::Point* pPoint)
{
    uno::Reference < beans::XPropertySet > xShapeProps ( xShape, uno::UNO_QUERY );
    bool bIsChart( false );
    if (xShapeProps.is())
    {
        sal_Int32 nZOrder = 0;
        if (xShapeProps->getPropertyValue(u"ZOrder"_ustr) >>= nZOrder)
        {
            AddAttribute(XML_NAMESPACE_DRAW, XML_ZINDEX, OUString::number(nZOrder));
        }
        uno::Reference< beans::XPropertySetInfo > xPropSetInfo = xShapeProps->getPropertySetInfo();
        OUString sPropCLSID (u"CLSID"_ustr);
        if( xPropSetInfo->hasPropertyByName( sPropCLSID ) )
        {
            OUString sCLSID;
            if (xShapeProps->getPropertyValue( sPropCLSID ) >>= sCLSID)
            {
                if ( sCLSID.equalsIgnoreAsciiCase(GetChartExport()->getChartCLSID()) )
                {
                    // we have a chart
                    OUString sRanges;
                    if ( pDoc )
                    {
                        OUString aChartName;
                        xShapeProps->getPropertyValue( u"PersistName"_ustr ) >>= aChartName;
                        ScChartListenerCollection* pCollection = pDoc->GetChartListenerCollection();
                        if (pCollection)
                        {
                            ScChartListener* pListener = pCollection->findByName(aChartName);
                            if (pListener)
                            {
                                const ScRangeListRef xRangeList = pListener->GetRangeList();
                                if ( xRangeList.is() )
                                {
                                    ScRangeStringConverter::GetStringFromRangeList( sRanges, xRangeList.get(), pDoc, FormulaGrammar::CONV_OOO );
                                    if ( !sRanges.isEmpty() )
                                    {
                                        bIsChart = true;
                                        rtl::Reference<comphelper::AttributeList> pAttrList = new comphelper::AttributeList();
                                        pAttrList->AddAttribute(
                                            GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_DRAW, GetXMLToken( XML_NOTIFY_ON_UPDATE_OF_RANGES ) ), sRanges );
                                        GetShapeExport()->exportShape( xShape, SEF_DEFAULT, pPoint, pAttrList.get() );
                                    }
                                }
                            }
                        }
                    }
 
                    if ( sRanges.isEmpty() )
                    {
                        uno::Reference< frame::XModel > xChartModel;
                        if( ( xShapeProps->getPropertyValue( u"Model"_ustr ) >>= xChartModel ) &&
                            xChartModel.is())
                        {
                            uno::Reference< chart2::XChartDocument > xChartDoc( xChartModel, uno::UNO_QUERY );
                            uno::Reference< chart2::data::XDataReceiver > xReceiver( xChartModel, uno::UNO_QUERY );
                            if( xChartDoc.is() && xReceiver.is() &&
                                ! xChartDoc->hasInternalDataProvider())
                            {
                                // we have a chart that gets its data from Calc
                                bIsChart = true;
                                uno::Sequence< OUString > aRepresentations(
                                    xReceiver->getUsedRangeRepresentations());
                                rtl::Reference<comphelper::AttributeList> pAttrList;
                                try
                                {
                                    if (aRepresentations.hasElements())
                                    {
                                        // add the ranges used by the chart to the shape
                                        // element to be able to start listening after
                                        // load (when the chart is not yet loaded)
                                        uno::Reference< chart2::data::XRangeXMLConversion > xRangeConverter( xChartDoc->getDataProvider(), uno::UNO_QUERY );
                                        sRanges = lcl_RangeSequenceToString( aRepresentations, xRangeConverter );
                                        pAttrList = new comphelper::AttributeList();
                                        pAttrList->AddAttribute(
                                            GetNamespaceMap().GetQNameByKey( XML_NAMESPACE_DRAW, GetXMLToken(XML_NOTIFY_ON_UPDATE_OF_RANGES) ), sRanges );
                                    }
                                }
                                catch (const lang::IllegalArgumentException&)
                                {
                                    TOOLS_WARN_EXCEPTION("sc", "Exception in lcl_RangeSequenceToString - invalid range?");
                                }
                                GetShapeExport()->exportShape(xShape, SEF_DEFAULT, pPoint, pAttrList.get());
                            }
                        }
                    }
                }
            }
        }
    }
    if (!bIsChart)
        GetShapeExport()->exportShape(xShape, SEF_DEFAULT, pPoint);
 
    IncrementProgressBar(false);
}
 
void ScXMLExport::WriteShapes(const ScMyCell& rMyCell)
{
    if( !(rMyCell.bHasShape && !rMyCell.aShapeList.empty() && pDoc) )
        return;
 
    // Reference point to turn absolute coordinates in reference point + offset. That happens in most
    // cases in XMLShapeExport::ImpExportNewTrans_DecomposeAndRefPoint, which gets the absolute
    // coordinates as translation from matrix in property "Transformation". For cell anchored shapes
    // the reference point is left-top (in LTR mode) of that cell, which contains the shape.
    tools::Rectangle aCellRectFull = pDoc->GetMMRect(
        rMyCell.maCellAddress.Col(), rMyCell.maCellAddress.Row(), rMyCell.maCellAddress.Col(),
        rMyCell.maCellAddress.Row(), rMyCell.maCellAddress.Tab(), false /*bHiddenAsZero*/);
    awt::Point aPoint;
    bool bNegativePage = pDoc->IsNegativePage(rMyCell.maCellAddress.Tab());
    if (bNegativePage)
        aPoint.X = aCellRectFull.Right();
    else
        aPoint.X = aCellRectFull.Left();
    aPoint.Y = aCellRectFull.Top();
 
    for (const auto& rShape : rMyCell.aShapeList)
    {
        // Skip the shape if requirements are not met. The tests should not fail, but allow
        // shorter conditions in main part below.
        if (!rShape.xShape.is())
            continue;
        SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rShape.xShape);
        if (!pObj)
            continue;
        ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObj);
        if (!pObjData)
            continue;
        ScAddress aSnapStartAddress = pObjData->maStart;
        if (!aSnapStartAddress.IsValid())
            continue;
 
        // The current object geometry is based on bHiddenAsZero=true, but ODF file format
        // needs it as if there were no hidden rows or columns. We determine a fictive snap
        // rectangle from the anchor as if all column/rows are shown. Then we move and resize
        // (in case of "resize with cell") the object to meet this snap rectangle. We need to
        // manipulate the object itself, because the used methods in xmloff do not evaluate the
        // ObjData. We remember the transformations and restore them later.
        Point aMoveBy(0, 0);
        bool bNeedsRestorePosition = false;
        Fraction aScaleWidth(1, 1);
        Fraction aScaleHeight(1, 1);
        bool bNeedsRestoreSize = false;
 
        // Determine top point of fictive snap rectangle ('Full' rectangle).
        SCTAB aTab(aSnapStartAddress.Tab());
        SCCOL aCol(aSnapStartAddress.Col());
        SCROW aRow(aSnapStartAddress.Row());
        tools::Rectangle aFullStartCellRect
            = pDoc->GetMMRect(aCol, aRow, aCol, aRow, aTab, false /*bHiddenAsZero*/);
        // The reference corner for the offset is top-left in case of LTR and top-right for RTL.
        Point aFullTopPoint;
        if (bNegativePage)
            aFullTopPoint.setX(aFullStartCellRect.Right() - pObjData->maStartOffset.X());
        else
            aFullTopPoint.setX(aFullStartCellRect.Left() + pObjData->maStartOffset.X());
        aFullTopPoint.setY(aFullStartCellRect.Top() + pObjData->maStartOffset.Y());
 
        // Compare actual top point and full top point and move object accordingly.
        tools::Rectangle aOrigSnapRect(pObj->GetSnapRect());
        Point aActualTopPoint = bNegativePage ? aOrigSnapRect.TopRight() : aOrigSnapRect.TopLeft();
        if (aFullTopPoint != aActualTopPoint)
        {
            bNeedsRestorePosition = true;
            aMoveBy = aFullTopPoint - aActualTopPoint;
            pObj->NbcMove(Size(aMoveBy.X(), aMoveBy.Y()));
        }
 
        ScAddress aSnapEndAddress = pObjData->maEnd;
        // tdf#154005: We treat the combination of "To cell (resize with cell)" with 'size protected'
        // as being "To cell".
        if (pObjData->mbResizeWithCell && aSnapEndAddress.IsValid() && !pObj->IsResizeProtect())
        {
            // Object is anchored "To cell (resize with cell)". Compare size of actual snap rectangle
            // and fictive full one. Resize object accordingly.
            tools::Rectangle aActualSnapRect(pObj->GetSnapRect());
 
            Point aSnapEndOffset(pObjData->maEndOffset);
            aCol = aSnapEndAddress.Col();
            aRow = aSnapEndAddress.Row();
            tools::Rectangle aFullEndCellRect
                = pDoc->GetMMRect(aCol, aRow, aCol, aRow, aTab, false /*bHiddenAsZero*/);
            Point aFullBottomPoint;
            if (bNegativePage)
                aFullBottomPoint.setX(aFullEndCellRect.Right() - aSnapEndOffset.X());
            else
                aFullBottomPoint.setX(aFullEndCellRect.Left() + aSnapEndOffset.X());
            aFullBottomPoint.setY(aFullEndCellRect.Top() + aSnapEndOffset.Y());
            tools::Rectangle aFullSnapRect(aFullTopPoint, aFullBottomPoint);
            aFullSnapRect.Normalize();
 
            if (aFullSnapRect != aActualSnapRect)
            {
                bNeedsRestoreSize = true;
                aScaleWidth
                    = Fraction(aFullSnapRect.getOpenWidth(), aActualSnapRect.getOpenWidth());
                if (!aScaleWidth.IsValid())
                    aScaleWidth = Fraction(1, 1);
                aScaleHeight
                    = Fraction(aFullSnapRect.getOpenHeight(), aActualSnapRect.getOpenHeight());
                if (!aScaleHeight.IsValid())
                    aScaleHeight = Fraction(1, 1);
                pObj->NbcResize(aFullTopPoint, aScaleWidth, aScaleHeight);
            }
        }
 
        // The existence of an end address is equivalent to anchor mode "To Cell (resize with cell)".
        // XML needs end address in regard of untransformed shape. Those are contained in rShape but
        // could be received from NonRotatedObjData as well.
        // tdf#154005: We treat the combination of "To Cell (resize with cell)" anchor with 'size
        // protected' property as being "To cell" anchor.
        if (pObjData->mbResizeWithCell && !pObj->IsResizeProtect())
        {
            OUString sEndAddress;
            ScRangeStringConverter::GetStringFromAddress(sEndAddress, rShape.aEndAddress, pDoc,
                                                         FormulaGrammar::CONV_OOO);
            AddAttribute(XML_NAMESPACE_TABLE, XML_END_CELL_ADDRESS, sEndAddress);
            OUStringBuffer sBuffer;
            GetMM100UnitConverter().convertMeasureToXML(sBuffer, rShape.nEndX);
            AddAttribute(XML_NAMESPACE_TABLE, XML_END_X, sBuffer.makeStringAndClear());
            GetMM100UnitConverter().convertMeasureToXML(sBuffer, rShape.nEndY);
            AddAttribute(XML_NAMESPACE_TABLE, XML_END_Y, sBuffer.makeStringAndClear());
        }
 
        // Correct above calculated reference point for these cases:
        // a) For a RTL-sheet translate from matrix is not suitable, because the shape
        // from xml (which is always LTR) is not mirrored to negative page but shifted.
        // b) In case of horizontal mirrored, 'resize with cell' anchored custom shape, translate from
        // matrix has wrong values. FixMe: Why is translate wrong?
        if (bNegativePage
            || (pObj->GetObjIdentifier() == SdrObjKind::CustomShape
                && static_cast<SdrObjCustomShape*>(pObj)->IsMirroredX()
                && pObjData->mbResizeWithCell))
        {
            // In these cases we set reference point so that the offset calculation in XML export
            // (=  matrix translate - reference point) results in maStartOffset.
            ScDrawObjData* pNRObjData = ScDrawLayer::GetNonRotatedObjData(pObj);
            if (pNRObjData)
            {
                awt::Point aMatrixTranslate = rShape.xShape->getPosition();
                aPoint.X = aMatrixTranslate.X - pNRObjData->maStartOffset.X();
                aPoint.Y = aMatrixTranslate.Y - pNRObjData->maStartOffset.Y();
            }
        }
 
        ExportShape(rShape.xShape, &aPoint);
 
        if (bNeedsRestoreSize)
        {
            Fraction aScaleWidthInvers(aScaleWidth.GetDenominator(), aScaleWidth.GetNumerator());
            if (!aScaleWidthInvers.IsValid())
                aScaleWidthInvers = Fraction(1, 1);
            Fraction aScaleHeightInvers(aScaleHeight.GetDenominator(), aScaleHeight.GetNumerator());
            if (!aScaleHeightInvers.IsValid())
                aScaleHeightInvers = Fraction(1, 1);
            pObj->NbcResize(aFullTopPoint, aScaleWidthInvers, aScaleHeightInvers);
        }
        if (bNeedsRestorePosition)
        {
            pObj->NbcMove(Size(-aMoveBy.X(), -aMoveBy.Y()));
        }
    }
}
 
void ScXMLExport::WriteTableShapes()
{
    ScMyTableShapes* pTableShapes(pSharedData->GetTableShapes());
    if (!pTableShapes || (*pTableShapes)[nCurrentTable].empty())
        return;
 
    OSL_ENSURE(pTableShapes->size() > static_cast<size_t>(nCurrentTable), "wrong Table");
    SvXMLElementExport aShapesElem(*this, XML_NAMESPACE_TABLE, XML_SHAPES, true, false);
    for (const auto& rxShape : (*pTableShapes)[nCurrentTable])
    {
        if (rxShape.is())
        {
            if (pDoc->IsNegativePage(static_cast<SCTAB>(nCurrentTable)))
            {
                // RTL-mirroring refers to snap rectangle, not to logic rectangle, therefore cannot use
                // getPosition() and getSize(), but need property "FrameRect" from rxShape or
                // GetSnapRect() from associated SdrObject.
                uno::Reference<beans::XPropertySet> xShapeProp(rxShape, uno::UNO_QUERY);
                awt::Rectangle aFrameRect;
                if (xShapeProp.is() && (xShapeProp->getPropertyValue(u"FrameRect"_ustr) >>= aFrameRect))
                {
                    // file format uses shape in LTR mode. newLeft = - oldRight = - (oldLeft + width).
                    // newTranslate = oldTranslate - refPoint, oldTranslate from transformation matrix,
                    // calculated in XMLShapeExport::exportShape common for all modules.
                    // oldTranslate.X = oldLeft ==> refPoint.X = 2 * oldLeft + width
                    awt::Point aRefPoint;
                    aRefPoint.X = 2 * aFrameRect.X + aFrameRect.Width - 1;
                    aRefPoint.Y = 0;
                    ExportShape(rxShape, &aRefPoint);
                }
                // else should not happen
            }
            else
                ExportShape(rxShape, nullptr);
        }
    }
    (*pTableShapes)[nCurrentTable].clear();
}
 
void ScXMLExport::WriteAreaLink( const ScMyCell& rMyCell )
{
    if( !rMyCell.bHasAreaLink )
        return;
 
    const ScMyAreaLink& rAreaLink = rMyCell.aAreaLink;
    AddAttribute( XML_NAMESPACE_TABLE, XML_NAME, rAreaLink.sSourceStr );
    AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE );
    AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, GetRelativeReference(rAreaLink.sURL) );
    AddAttribute( XML_NAMESPACE_TABLE, XML_FILTER_NAME, rAreaLink.sFilter );
    if( !rAreaLink.sFilterOptions.isEmpty() )
        AddAttribute( XML_NAMESPACE_TABLE, XML_FILTER_OPTIONS, rAreaLink.sFilterOptions );
    AddAttribute( XML_NAMESPACE_TABLE, XML_LAST_COLUMN_SPANNED, OUString::number(rAreaLink.GetColCount()) );
    AddAttribute( XML_NAMESPACE_TABLE, XML_LAST_ROW_SPANNED, OUString::number(rAreaLink.GetRowCount()) );
    if( rAreaLink.nRefreshDelaySeconds )
    {
        OUStringBuffer sValue;
        ::sax::Converter::convertDuration( sValue,
                static_cast<double>(rAreaLink.nRefreshDelaySeconds) / 86400 );
        AddAttribute( XML_NAMESPACE_TABLE, XML_REFRESH_DELAY, sValue.makeStringAndClear() );
    }
    SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, XML_CELL_RANGE_SOURCE, true, true );
}
 
void ScXMLExport::exportAnnotationMeta( const uno::Reference < drawing::XShape >& xShape)
{
    ScPostIt* pNote = pCurrentCell->pNote;
 
    if (!pNote)
        return;
 
    // TODO : notes
    //is it still useful, as this call back is only called from ScXMLExport::WriteAnnotation
    // and should be in sync with pCurrentCell
    SdrCaptionObj* pNoteCaption = pNote->GetOrCreateCaption(pCurrentCell->maCellAddress);
    uno::Reference<drawing::XShape> xCurrentShape( pNoteCaption->getUnoShape(), uno::UNO_QUERY );
    if (xCurrentShape.get()!=xShape.get())
        return;
 
    bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
        SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo) && !SvtSecurityOptions::IsOptionSet(
            SvtSecurityOptions::EOption::DocWarnKeepNoteAuthorDateInfo);
 
    const OUString& sAuthor(pNote->GetAuthor());
    if (!sAuthor.isEmpty())
    {
        SvXMLElementExport aCreatorElem( *this, XML_NAMESPACE_DC,
                                            XML_CREATOR, true,
                                            false );
        Characters( bRemovePersonalInfo
            ? "Author" + OUString::number(SvXMLExport::GetInfoID(sAuthor))
            : sAuthor );
    }
 
    const OUString aDate(bRemovePersonalInfo ? u"1970-01-01"_ustr : pNote->GetDate()); // Epoch time
    if (pDoc)
    {
        SvNumberFormatter* pNumForm = pDoc->GetFormatTable();
        double fDate;
        sal_uInt32 nfIndex = pNumForm->GetFormatIndex(NF_DATE_SYS_DDMMYYYY, LANGUAGE_SYSTEM);
        if (pNumForm->IsNumberFormat(aDate, nfIndex, fDate))
        {
            OUStringBuffer sBuf;
            GetMM100UnitConverter().convertDateTime(sBuf, fDate,true);
            SvXMLElementExport aDateElem( *this, XML_NAMESPACE_DC,
                                            XML_DATE, true,
                                            false );
            Characters(sBuf.makeStringAndClear());
        }
        else
        {
            SvXMLElementExport aDateElem( *this, XML_NAMESPACE_META,
                                            XML_DATE_STRING, true,
                                            false );
            Characters(aDate);
        }
    }
    else
    {
        SvXMLElementExport aDateElem( *this, XML_NAMESPACE_META,
                                        XML_DATE_STRING, true,
                                        false );
        Characters(aDate);
    }
}
 
void ScXMLExport::WriteAnnotation(const ScMyCell& rMyCell)
{
    ScPostIt* pNote = pDoc->GetNote(rMyCell.maCellAddress);
    if (!pNote)
        return;
 
    if (pNote->IsCaptionShown())
        AddAttribute(XML_NAMESPACE_OFFICE, XML_DISPLAY, XML_TRUE);
 
    pCurrentCell = &rMyCell;
 
    SdrCaptionObj* pNoteCaption = pNote->GetOrCreateCaption(rMyCell.maCellAddress);
    if (pNoteCaption)
    {
        uno::Reference<drawing::XShape> xShape( pNoteCaption->getUnoShape(), uno::UNO_QUERY );
        if (xShape.is())
            GetShapeExport()->exportShape(xShape, SEF_DEFAULT|XMLShapeExportFlags::ANNOTATION);
    }
 
    pCurrentCell = nullptr;
}
 
void ScXMLExport::WriteDetective( const ScMyCell& rMyCell )
{
    if( !(rMyCell.bHasDetectiveObj || rMyCell.bHasDetectiveOp) )
        return;
 
    const ScMyDetectiveObjVec& rObjVec = rMyCell.aDetectiveObjVec;
    const ScMyDetectiveOpVec& rOpVec = rMyCell.aDetectiveOpVec;
    sal_Int32 nObjCount(rObjVec.size());
    sal_Int32 nOpCount(rOpVec.size());
    if( !(nObjCount || nOpCount) )
        return;
 
    SvXMLElementExport aDetElem( *this, XML_NAMESPACE_TABLE, XML_DETECTIVE, true, true );
    OUString sString;
    for(const auto& rObj : rObjVec)
    {
        if (rObj.eObjType != SC_DETOBJ_CIRCLE)
        {
            if( (rObj.eObjType == SC_DETOBJ_ARROW) || (rObj.eObjType == SC_DETOBJ_TOOTHERTAB))
            {
                ScRangeStringConverter::GetStringFromRange( sString, rObj.aSourceRange, pDoc, FormulaGrammar::CONV_OOO );
                AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, sString );
            }
            sString = ScXMLConverter::GetStringFromDetObjType( rObj.eObjType );
            AddAttribute( XML_NAMESPACE_TABLE, XML_DIRECTION, sString );
            if( rObj.bHasError )
                AddAttribute( XML_NAMESPACE_TABLE, XML_CONTAINS_ERROR, XML_TRUE );
        }
        else
            AddAttribute( XML_NAMESPACE_TABLE, XML_MARKED_INVALID, XML_TRUE );
        SvXMLElementExport aRangeElem( *this, XML_NAMESPACE_TABLE, XML_HIGHLIGHTED_RANGE, true, true );
    }
    for(const auto& rOp : rOpVec)
    {
        OUString sOpString = ScXMLConverter::GetStringFromDetOpType( rOp.eOpType );
        AddAttribute( XML_NAMESPACE_TABLE, XML_NAME, sOpString );
        AddAttribute( XML_NAMESPACE_TABLE, XML_INDEX, OUString::number(rOp.nIndex) );
        SvXMLElementExport aRangeElem( *this, XML_NAMESPACE_TABLE, XML_OPERATION, true, true );
    }
}
 
void ScXMLExport::SetRepeatAttribute(sal_Int32 nEqualCellCount, bool bIncProgress)
{
    // nEqualCellCount is additional cells, so the attribute value is nEqualCellCount+1
    if (nEqualCellCount > 0)
    {
        sal_Int32 nTemp(nEqualCellCount + 1);
        OUString sOUEqualCellCount(OUString::number(nTemp));
        AddAttribute(sAttrColumnsRepeated, sOUEqualCellCount);
        if (bIncProgress)
            IncrementProgressBar(false, nEqualCellCount);
    }
}
 
bool ScXMLExport::IsEditCell(const ScMyCell& rCell)
{
    return rCell.maBaseCell.getType() == CELLTYPE_EDIT;
}
 
bool ScXMLExport::IsCellEqual (const ScMyCell& aCell1, const ScMyCell& aCell2)
{
    bool bIsEqual = false;
    if( !aCell1.bIsMergedBase && !aCell2.bIsMergedBase &&
        aCell1.bIsCovered == aCell2.bIsCovered &&
        !aCell1.bIsMatrixBase && !aCell2.bIsMatrixBase &&
        aCell1.bIsMatrixCovered == aCell2.bIsMatrixCovered &&
        aCell1.bHasAnnotation == aCell2.bHasAnnotation &&
        !aCell1.bHasShape && !aCell2.bHasShape &&
        aCell1.bHasAreaLink == aCell2.bHasAreaLink &&
        !aCell1.bHasDetectiveObj && !aCell2.bHasDetectiveObj)
    {
        if( (aCell1.bHasAreaLink &&
            (aCell1.aAreaLink.GetColCount() == 1) &&
            (aCell2.aAreaLink.GetColCount() == 1) &&
            aCell1.aAreaLink.Compare( aCell2.aAreaLink ) ) ||
            !aCell1.bHasAreaLink )
        {
            if (!aCell1.bHasAnnotation)
            {
                if ((((aCell1.nStyleIndex == aCell2.nStyleIndex) && (aCell1.bIsAutoStyle == aCell2.bIsAutoStyle)) ||
                     ((aCell1.nStyleIndex == aCell2.nStyleIndex) && (aCell1.nStyleIndex == -1))) &&
                    aCell1.nValidationIndex == aCell2.nValidationIndex &&
                    aCell1.nType == aCell2.nType)
                {
                    switch ( aCell1.nType )
                    {
                    case table::CellContentType_EMPTY :
                        {
                            bIsEqual = true;
                        }
                        break;
                    case table::CellContentType_VALUE :
                        {
                            // #i29101# number format may be different from column default styles,
                            // but can lead to different value types, so it must also be compared
                            bIsEqual = (aCell1.nNumberFormat == aCell2.nNumberFormat) &&
                                       (aCell1.maBaseCell.getDouble() == aCell2.maBaseCell.getDouble());
                        }
                        break;
                    case table::CellContentType_TEXT :
                        {
                            if (IsEditCell(aCell1) || IsEditCell(aCell2))
                                bIsEqual = false;
                            else
                            {
                                bIsEqual = (aCell1.maBaseCell.getString(pDoc) == aCell2.maBaseCell.getString(pDoc));
                            }
                        }
                        break;
                    case table::CellContentType_FORMULA :
                        {
                            bIsEqual = false;
                        }
                        break;
                    default :
                        {
                            bIsEqual = false;
                        }
                        break;
                    }
                }
            }
        }
    }
    return bIsEqual;
}
 
void ScXMLExport::WriteCalculationSettings(const uno::Reference <sheet::XSpreadsheetDocument>& xSpreadDoc)
{
    uno::Reference<beans::XPropertySet> xPropertySet(xSpreadDoc, uno::UNO_QUERY);
    if (!xPropertySet.is())
        return;
 
    bool bCalcAsShown (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_CALCASSHOWN) ));
    bool bIgnoreCase (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_IGNORECASE) ));
    bool bLookUpLabels (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_LOOKUPLABELS) ));
    bool bMatchWholeCell (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_MATCHWHOLE) ));
    bool bUseRegularExpressions (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_REGEXENABLED) ));
    bool bUseWildcards (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_WILDCARDSENABLED) ));
    if (bUseWildcards && bUseRegularExpressions)
        bUseRegularExpressions = false;     // mutually exclusive, wildcards take precedence
    bool bIsIterationEnabled (::cppu::any2bool( xPropertySet->getPropertyValue(SC_UNO_ITERENABLED) ));
    sal_uInt16 nYear2000 (pDoc ? pDoc->GetDocOptions().GetYear2000() : 0);
    sal_Int32 nIterationCount(100);
    xPropertySet->getPropertyValue( SC_UNO_ITERCOUNT ) >>= nIterationCount;
    double fIterationEpsilon = 0;
    xPropertySet->getPropertyValue( SC_UNO_ITEREPSILON ) >>= fIterationEpsilon;
    util::Date aNullDate;
    xPropertySet->getPropertyValue( SC_UNO_NULLDATE ) >>= aNullDate;
    if (!(bCalcAsShown || bIgnoreCase || !bLookUpLabels || !bMatchWholeCell || !bUseRegularExpressions ||
            bUseWildcards ||
            bIsIterationEnabled || nIterationCount != 100 || !::rtl::math::approxEqual(fIterationEpsilon, 0.001) ||
            aNullDate.Day != 30 || aNullDate.Month != 12 || aNullDate.Year != 1899 || nYear2000 != 1930))
        return;
 
    if (bIgnoreCase)
        AddAttribute(XML_NAMESPACE_TABLE, XML_CASE_SENSITIVE, XML_FALSE);
    if (bCalcAsShown)
        AddAttribute(XML_NAMESPACE_TABLE, XML_PRECISION_AS_SHOWN, XML_TRUE);
    if (!bMatchWholeCell)
        AddAttribute(XML_NAMESPACE_TABLE, XML_SEARCH_CRITERIA_MUST_APPLY_TO_WHOLE_CELL, XML_FALSE);
    if (!bLookUpLabels)
        AddAttribute(XML_NAMESPACE_TABLE, XML_AUTOMATIC_FIND_LABELS, XML_FALSE);
    if (!bUseRegularExpressions)
        AddAttribute(XML_NAMESPACE_TABLE, XML_USE_REGULAR_EXPRESSIONS, XML_FALSE);
    if (bUseWildcards)
        AddAttribute(XML_NAMESPACE_TABLE, XML_USE_WILDCARDS, XML_TRUE);
    if (nYear2000 != 1930)
    {
        AddAttribute(XML_NAMESPACE_TABLE, XML_NULL_YEAR, OUString::number(nYear2000));
    }
    SvXMLElementExport aCalcSettings(*this, XML_NAMESPACE_TABLE, XML_CALCULATION_SETTINGS, true, true);
    {
        if (aNullDate.Day != 30 || aNullDate.Month != 12 || aNullDate.Year != 1899)
        {
            OUStringBuffer sDate;
            SvXMLUnitConverter::convertDateTime(sDate, 0.0, aNullDate);
            AddAttribute(XML_NAMESPACE_TABLE, XML_DATE_VALUE, sDate.makeStringAndClear());
            SvXMLElementExport aElemNullDate(*this, XML_NAMESPACE_TABLE, XML_NULL_DATE, true, true);
        }
        if (bIsIterationEnabled || nIterationCount != 100 || !::rtl::math::approxEqual(fIterationEpsilon, 0.001))
        {
            if (bIsIterationEnabled)
                AddAttribute(XML_NAMESPACE_TABLE, XML_STATUS, XML_ENABLE);
            if (nIterationCount != 100)
            {
                AddAttribute(XML_NAMESPACE_TABLE, XML_STEPS, OUString::number(nIterationCount));
            }
            if (!::rtl::math::approxEqual(fIterationEpsilon, 0.001))
            {
                OUStringBuffer sBuffer;
                ::sax::Converter::convertDouble(sBuffer,
                        fIterationEpsilon);
                AddAttribute(XML_NAMESPACE_TABLE, XML_MAXIMUM_DIFFERENCE, sBuffer.makeStringAndClear());
            }
            SvXMLElementExport aElemIteration(*this, XML_NAMESPACE_TABLE, XML_ITERATION, true, true);
        }
    }
}
 
void ScXMLExport::WriteTableSource()
{
    uno::Reference <sheet::XSheetLinkable> xLinkable (xCurrentTable, uno::UNO_QUERY);
    if (!(xLinkable.is() && GetModel().is()))
        return;
 
    sheet::SheetLinkMode nMode (xLinkable->getLinkMode());
    if (nMode == sheet::SheetLinkMode_NONE)
        return;
 
    OUString sLink (xLinkable->getLinkUrl());
    uno::Reference <beans::XPropertySet> xProps (GetModel(), uno::UNO_QUERY);
    if (!xProps.is())
        return;
 
    uno::Reference <container::XIndexAccess> xIndex(xProps->getPropertyValue(SC_UNO_SHEETLINKS), uno::UNO_QUERY);
    if (!xIndex.is())
        return;
 
    sal_Int32 nCount(xIndex->getCount());
    if (!nCount)
        return;
 
    bool bFound(false);
    uno::Reference <beans::XPropertySet> xLinkProps;
    for (sal_Int32 i = 0; (i < nCount) && !bFound; ++i)
    {
        xLinkProps.set(xIndex->getByIndex(i), uno::UNO_QUERY);
        if (xLinkProps.is())
        {
            OUString sNewLink;
            if (xLinkProps->getPropertyValue(SC_UNONAME_LINKURL) >>= sNewLink)
                bFound = sLink == sNewLink;
        }
    }
    if (!(bFound && xLinkProps.is()))
        return;
 
    OUString sFilter;
    OUString sFilterOptions;
    OUString sTableName (xLinkable->getLinkSheetName());
    sal_Int32 nRefresh(0);
    xLinkProps->getPropertyValue(SC_UNONAME_FILTER) >>= sFilter;
    xLinkProps->getPropertyValue(SC_UNONAME_FILTOPT) >>= sFilterOptions;
    xLinkProps->getPropertyValue(SC_UNONAME_REFDELAY) >>= nRefresh;
    if (sLink.isEmpty())
        return;
 
    AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE);
    AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, GetRelativeReference(sLink));
    if (!sTableName.isEmpty())
        AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE_NAME, sTableName);
    if (!sFilter.isEmpty())
        AddAttribute(XML_NAMESPACE_TABLE, XML_FILTER_NAME, sFilter);
    if (!sFilterOptions.isEmpty())
        AddAttribute(XML_NAMESPACE_TABLE, XML_FILTER_OPTIONS, sFilterOptions);
    if (nMode != sheet::SheetLinkMode_NORMAL)
        AddAttribute(XML_NAMESPACE_TABLE, XML_MODE, XML_COPY_RESULTS_ONLY);
    if( nRefresh )
    {
        OUStringBuffer sBuffer;
        ::sax::Converter::convertDuration( sBuffer,
                static_cast<double>(nRefresh) / 86400 );
        AddAttribute( XML_NAMESPACE_TABLE, XML_REFRESH_DELAY, sBuffer.makeStringAndClear() );
    }
    SvXMLElementExport aSourceElem(*this, XML_NAMESPACE_TABLE, XML_TABLE_SOURCE, true, true);
}
 
// core implementation
void ScXMLExport::WriteScenario()
{
    if (!(pDoc && pDoc->IsScenario(static_cast<SCTAB>(nCurrentTable))))
        return;
 
    OUString sComment;
    Color       aColor;
    ScScenarioFlags nFlags;
    pDoc->GetScenarioData(static_cast<SCTAB>(nCurrentTable), sComment, aColor, nFlags);
    if (!(nFlags & ScScenarioFlags::ShowFrame))
        AddAttribute(XML_NAMESPACE_TABLE, XML_DISPLAY_BORDER, XML_FALSE);
    OUStringBuffer aBuffer;
    ::sax::Converter::convertColor(aBuffer, aColor);
    AddAttribute(XML_NAMESPACE_TABLE, XML_BORDER_COLOR, aBuffer.makeStringAndClear());
    if (!(nFlags & ScScenarioFlags::TwoWay))
        AddAttribute(XML_NAMESPACE_TABLE, XML_COPY_BACK, XML_FALSE);
    if (!(nFlags & ScScenarioFlags::Attrib))
        AddAttribute(XML_NAMESPACE_TABLE, XML_COPY_STYLES, XML_FALSE);
    if (nFlags & ScScenarioFlags::Value)
        AddAttribute(XML_NAMESPACE_TABLE, XML_COPY_FORMULAS, XML_FALSE);
    if (nFlags & ScScenarioFlags::Protected)
        AddAttribute(XML_NAMESPACE_TABLE, XML_PROTECTED, XML_TRUE);
    ::sax::Converter::convertBool(aBuffer,
            pDoc->IsActiveScenario(static_cast<SCTAB>(nCurrentTable)));
    AddAttribute(XML_NAMESPACE_TABLE, XML_IS_ACTIVE, aBuffer.makeStringAndClear());
    const ScRangeList* pRangeList = pDoc->GetScenarioRanges(static_cast<SCTAB>(nCurrentTable));
    OUString sRangeListStr;
    ScRangeStringConverter::GetStringFromRangeList( sRangeListStr, pRangeList, pDoc, FormulaGrammar::CONV_OOO );
    AddAttribute(XML_NAMESPACE_TABLE, XML_SCENARIO_RANGES, sRangeListStr);
    if (!sComment.isEmpty())
        AddAttribute(XML_NAMESPACE_TABLE, XML_COMMENT, sComment);
    SvXMLElementExport aElem(*this, XML_NAMESPACE_TABLE, XML_SCENARIO, true, true);
}
 
void ScXMLExport::WriteTheLabelRanges( const uno::Reference< sheet::XSpreadsheetDocument >& xSpreadDoc )
{
    uno::Reference< beans::XPropertySet > xDocProp( xSpreadDoc, uno::UNO_QUERY );
    if( !xDocProp.is() ) return;
 
    sal_Int32 nCount(0);
    uno::Reference< container::XIndexAccess > xColRangesIAccess(xDocProp->getPropertyValue( SC_UNO_COLLABELRNG ), uno::UNO_QUERY);
    if( xColRangesIAccess.is() )
        nCount += xColRangesIAccess->getCount();
 
    uno::Reference< container::XIndexAccess > xRowRangesIAccess(xDocProp->getPropertyValue( SC_UNO_ROWLABELRNG ), uno::UNO_QUERY);
    if( xRowRangesIAccess.is() )
        nCount += xRowRangesIAccess->getCount();
 
    if( nCount )
    {
        SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, XML_LABEL_RANGES, true, true );
        WriteLabelRanges( xColRangesIAccess, true );
        WriteLabelRanges( xRowRangesIAccess, false );
    }
}
 
void ScXMLExport::WriteLabelRanges( const uno::Reference< container::XIndexAccess >& xRangesIAccess, bool bColumn )
{
    if( !xRangesIAccess.is() ) return;
 
    sal_Int32 nCount(xRangesIAccess->getCount());
    for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex )
    {
        uno::Reference< sheet::XLabelRange > xRange(xRangesIAccess->getByIndex( nIndex ), uno::UNO_QUERY);
        if( xRange.is() )
        {
            OUString sRangeStr;
            table::CellRangeAddress aCellRange( xRange->getLabelArea() );
            ScRangeStringConverter::GetStringFromRange( sRangeStr, aCellRange, pDoc, FormulaGrammar::CONV_OOO );
            AddAttribute( XML_NAMESPACE_TABLE, XML_LABEL_CELL_RANGE_ADDRESS, sRangeStr );
            aCellRange = xRange->getDataArea();
            ScRangeStringConverter::GetStringFromRange( sRangeStr, aCellRange, pDoc, FormulaGrammar::CONV_OOO );
            AddAttribute( XML_NAMESPACE_TABLE, XML_DATA_CELL_RANGE_ADDRESS, sRangeStr );
            AddAttribute( XML_NAMESPACE_TABLE, XML_ORIENTATION, bColumn ? XML_COLUMN : XML_ROW );
            SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, XML_LABEL_RANGE, true, true );
        }
    }
}
 
void ScXMLExport::WriteNamedExpressions()
{
    if (!pDoc)
        return;
    ScRangeName* pNamedRanges = pDoc->GetRangeName();
    WriteNamedRange(pNamedRanges);
}
 
void ScXMLExport::WriteExternalDataMapping()
{
    if (!pDoc)
        return;
 
    if ((getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0)
        // Export this only for 1.2 extended and above.
        return;
 
    sc::ExternalDataMapper& rDataMapper = pDoc->GetExternalDataMapper();
    auto& rDataSources = rDataMapper.getDataSources();
 
    if (rDataSources.empty())
        return;
 
    SvXMLElementExport aMappings(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_MAPPINGS, true, true);
    for (const auto& itr : rDataSources)
    {
        AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, itr.getURL());
        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_PROVIDER, itr.getProvider());
        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_DATA_FREQUENCY, OUString::number(sc::ExternalDataSource::getUpdateFrequency()));
        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_ID, itr.getID());
        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_DATABASE_NAME, itr.getDBName());
 
        SvXMLElementExport aMapping(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_MAPPING, true, true);
        // Add the data transformations
        WriteExternalDataTransformations(itr.getDataTransformation());
    }
}
 
void ScXMLExport::WriteExternalDataTransformations(const std::vector<std::shared_ptr<sc::DataTransformation>>& aDataTransformations)
{
    SvXMLElementExport aTransformations(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_TRANSFORMATIONS, true, true);
    for (auto& itr : aDataTransformations)
    {
        sc::TransformationType aTransformationType = itr->getTransformationType();
 
        switch(aTransformationType)
        {
            case sc::TransformationType::DELETE_TRANSFORMATION:
            {
                // Delete Columns Transformation
                std::shared_ptr<sc::ColumnRemoveTransformation> aDeleteTransformation = std::dynamic_pointer_cast<sc::ColumnRemoveTransformation>(itr);
                std::set<SCCOL> aColumns = aDeleteTransformation->getColumns();
                SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_REMOVE_TRANSFORMATION, true, true);
                for(auto& col : aColumns)
                {
                    // Add Columns
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
                    SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
                }
            }
            break;
            case sc::TransformationType::SPLIT_TRANSFORMATION:
            {
                std::shared_ptr<sc::SplitColumnTransformation> aSplitTransformation = std::dynamic_pointer_cast<sc::SplitColumnTransformation>(itr);
 
                AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(aSplitTransformation->getColumn()));
                AddAttribute(XML_NAMESPACE_CALC_EXT, XML_SEPARATOR, OUString::number(aSplitTransformation->getSeparator()));
                SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_SPLIT_TRANSFORMATION, true, true);
            }
            break;
            case sc::TransformationType::MERGE_TRANSFORMATION:
            {
                // Merge Transformation
                std::shared_ptr<sc::MergeColumnTransformation> aMergeTransformation = std::dynamic_pointer_cast<sc::MergeColumnTransformation>(itr);
                std::set<SCCOL> aColumns = aMergeTransformation->getColumns();
 
                AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MERGE_STRING, aMergeTransformation->getMergeString());
                SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_MERGE_TRANSFORMATION, true, true);
 
                for(auto& col : aColumns)
                {
                    // Columns
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
                    SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
                }
            }
            break;
            case sc::TransformationType::SORT_TRANSFORMATION:
            {
                // Sort Transformation
                std::shared_ptr<sc::SortTransformation> aSortTransformation = std::dynamic_pointer_cast<sc::SortTransformation>(itr);
                ScSortParam aSortParam = aSortTransformation->getSortParam();
                const sc::DocumentLinkManager& rMgr = pDoc->GetDocLinkManager();
                const sc::DataStream* pStrm = rMgr.getDataStream();
                if (!pStrm)
                    // No data stream.
                    return;
 
                // Streamed range
                ScRange aRange = pStrm->GetRange();
 
                SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_SORT_TRANSFORMATION, true, true);
 
                writeSort(*this, aSortParam, aRange, pDoc);
            }
            break;
            case sc::TransformationType::TEXT_TRANSFORMATION:
            {
                // Text Transformation
                std::shared_ptr<sc::TextTransformation> aTextTransformation = std::dynamic_pointer_cast<sc::TextTransformation>(itr);
 
                sc::TEXT_TRANSFORM_TYPE aTextTransformType = aTextTransformation->getTextTransformationType();
 
                switch ( aTextTransformType )
                {
                    case sc::TEXT_TRANSFORM_TYPE::TO_LOWER:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_CASEMAP_LOWERCASE);
                    break;
                    case sc::TEXT_TRANSFORM_TYPE::TO_UPPER:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_CASEMAP_UPPERCASE);
                    break;
                    case sc::TEXT_TRANSFORM_TYPE::CAPITALIZE:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_CASEMAP_CAPITALIZE);
                    break;
                    case sc::TEXT_TRANSFORM_TYPE::TRIM:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_TRIM);
                    break;
                }
 
                std::set<SCCOL> aColumns = aTextTransformation->getColumns();
 
                SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_TEXT_TRANSFORMATION, true, true);
 
                for(auto& col : aColumns)
                {
                    // Columns
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
                    SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
                }
            }
            break;
            case sc::TransformationType::AGGREGATE_FUNCTION:
            {
                // Aggregate Transformation
                std::shared_ptr<sc::AggregateFunction> aAggregateFunction = std::dynamic_pointer_cast<sc::AggregateFunction>(itr);
                std::set<SCCOL> aColumns = aAggregateFunction->getColumns();
 
                sc::AGGREGATE_FUNCTION aAggregateType = aAggregateFunction->getAggregateType();
 
                switch (aAggregateType)
                {
                    case sc::AGGREGATE_FUNCTION::SUM:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SUM);
                    break;
                    case sc::AGGREGATE_FUNCTION::AVERAGE:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_AVERAGE);
                    break;
                    case sc::AGGREGATE_FUNCTION::MIN:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MIN);
                    break;
                    case sc::AGGREGATE_FUNCTION::MAX:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MAX);
                    break;
                }
 
                SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT,XML_COLUMN_AGGREGATE_TRANSFORMATION, true, true);
 
                for(auto& col : aColumns)
                {
                    // Columns
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
                    SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
                }
            }
            break;
            case sc::TransformationType::NUMBER_TRANSFORMATION:
            {
                // Number Transformation
                std::shared_ptr<sc::NumberTransformation> aNumberTransformation = std::dynamic_pointer_cast<sc::NumberTransformation>(itr);
 
                sc::NUMBER_TRANSFORM_TYPE aNumberTransformType = aNumberTransformation->getNumberTransformationType();
 
                switch ( aNumberTransformType )
                {
                    case sc::NUMBER_TRANSFORM_TYPE::ROUND:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ROUND);
                    break;
                    case sc::NUMBER_TRANSFORM_TYPE::ROUND_UP:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ROUND_UP);
                    break;
                    case sc::NUMBER_TRANSFORM_TYPE::ROUND_DOWN:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ROUND_DOWN);
                    break;
                    case sc::NUMBER_TRANSFORM_TYPE::ABSOLUTE:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ABS);
                    break;
                    case sc::NUMBER_TRANSFORM_TYPE::LOG_E:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_LOG);
                    break;
                    case sc::NUMBER_TRANSFORM_TYPE::LOG_10:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_LOG_10);
                    break;
                    case sc::NUMBER_TRANSFORM_TYPE::CUBE:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_CUBE);
                    break;
                    case sc::NUMBER_TRANSFORM_TYPE::SQUARE:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SQUARE);
                    break;
                    case sc::NUMBER_TRANSFORM_TYPE::SQUARE_ROOT:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SQUARE_ROOT);
                    break;
                    case sc::NUMBER_TRANSFORM_TYPE::EXPONENT:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_EXPONENTIAL);
                    break;
                    case sc::NUMBER_TRANSFORM_TYPE::IS_EVEN:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_EVEN);
                    break;
                    case sc::NUMBER_TRANSFORM_TYPE::IS_ODD:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_ODD);
                    break;
                    case sc::NUMBER_TRANSFORM_TYPE::SIGN:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SIGN);
                    break;
                }
 
                AddAttribute(XML_NAMESPACE_CALC_EXT, XML_PRECISION, OUString::number(aNumberTransformation->getPrecision()));
                SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_NUMBER_TRANSFORMATION, true, true);
 
                std::set<SCCOL> aColumns = aNumberTransformation->getColumn();
                for(auto& col : aColumns)
                {
                    // Columns
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
                    SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
                }
            }
            break;
            case sc::TransformationType::REMOVE_NULL_TRANSFORMATION:
            {
                // Replace Null Transformation
                std::shared_ptr<sc::ReplaceNullTransformation> aReplaceNullTransformation = std::dynamic_pointer_cast<sc::ReplaceNullTransformation>(itr);
                std::set<SCCOL> aColumns = aReplaceNullTransformation->getColumn();
 
                AddAttribute(XML_NAMESPACE_CALC_EXT, XML_REPLACE_STRING, aReplaceNullTransformation->getReplaceString());
                SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_REPLACENULL_TRANSFORMATION, true, true);
 
                for(auto& col : aColumns)
                {
                    // Columns
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
                    SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
                }
            }
            break;
            case sc::TransformationType::DATETIME_TRANSFORMATION:
            {
                // Number Transformation
                std::shared_ptr<sc::DateTimeTransformation> aDateTimeTransformation = std::dynamic_pointer_cast<sc::DateTimeTransformation>(itr);
 
                sc::DATETIME_TRANSFORMATION_TYPE aDateTimeTransformationType = aDateTimeTransformation->getDateTimeTransformationType();
 
                switch ( aDateTimeTransformationType )
                {
                    case sc::DATETIME_TRANSFORMATION_TYPE::DATE_STRING:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_DATE_STRING);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::YEAR:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_YEAR);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::START_OF_YEAR:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_START_OF_YEAR);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::END_OF_YEAR:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_END_OF_YEAR);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::MONTH:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MONTH);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::MONTH_NAME:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MONTH_NAME);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::START_OF_MONTH:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_START_OF_MONTH);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::END_OF_MONTH:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_END_OF_MONTH);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::DAY:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_DAY);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_WEEK:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_DAY_OF_WEEK);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::DAY_OF_YEAR:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_DAY_OF_YEAR);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::QUARTER:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_QUARTER);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::START_OF_QUARTER:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_START_OF_QUARTER);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::END_OF_QUARTER:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_END_OF_QUARTER);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::TIME:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_TIME);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::HOUR:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_HOUR);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::MINUTE:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_MINUTE);
                    break;
                    case sc::DATETIME_TRANSFORMATION_TYPE::SECOND:
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, XML_SECONDS);
                    break;
                }
 
                SvXMLElementExport aTransformation(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN_DATETIME_TRANSFORMATION, true, true);
 
                std::set<SCCOL> aColumns = aDateTimeTransformation->getColumn();
                for(auto& col : aColumns)
                {
                    // Columns
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLUMN, OUString::number(col));
                    SvXMLElementExport aCol(*this, XML_NAMESPACE_CALC_EXT, XML_COLUMN, true, true);
                }
            }
            break;
            default:
            break;
        }
    }
}
 
void ScXMLExport::WriteDataStream()
{
    if (!pDoc)
        return;
 
    if (!officecfg::Office::Common::Misc::ExperimentalMode::get())
        // Export this only in experimental mode.
        return;
 
    if ((getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) == 0)
        // Export this only for 1.2 extended and above.
        return;
 
    const sc::DocumentLinkManager& rMgr = pDoc->GetDocLinkManager();
    const sc::DataStream* pStrm = rMgr.getDataStream();
    if (!pStrm)
        // No data stream.
        return;
 
    // Source URL
    AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, GetRelativeReference(pStrm->GetURL()));
 
    // Streamed range
    ScRange aRange = pStrm->GetRange();
    OUString aRangeStr;
    ScRangeStringConverter::GetStringFromRange(
        aRangeStr, aRange, pDoc, formula::FormulaGrammar::CONV_OOO);
    AddAttribute(XML_NAMESPACE_TABLE, XML_TARGET_RANGE_ADDRESS, aRangeStr);
 
    // Empty line refresh option.
    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_EMPTY_LINE_REFRESH, pStrm->IsRefreshOnEmptyLine() ? XML_TRUE : XML_FALSE);
 
    // New data insertion position. Either top of bottom. Default to bottom.
    xmloff::token::XMLTokenEnum eInsertPosition = XML_BOTTOM;
    if (pStrm->GetMove() == sc::DataStream::MOVE_DOWN)
        eInsertPosition = XML_TOP;
 
    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_INSERTION_POSITION, eInsertPosition);
 
    SvXMLElementExport aElem(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_STREAM_SOURCE, true, true);
}
 
void ScXMLExport::WriteNamedRange(ScRangeName* pRangeName)
{
    //write a global or local ScRangeName
    SvXMLElementExport aElemNEs(*this, XML_NAMESPACE_TABLE, XML_NAMED_EXPRESSIONS, true, true);
    for (const auto& rxEntry : *pRangeName)
    {
        AddAttribute(sAttrName, rxEntry.second->GetName());
 
        OUString sBaseCellAddress;
        rxEntry.second->ValidateTabRefs();
        ScRangeStringConverter::GetStringFromAddress( sBaseCellAddress, rxEntry.second->GetPos(), pDoc,
                            FormulaGrammar::CONV_OOO, ' ', false, ScRefFlags::ADDR_ABS_3D);
        assert(!sBaseCellAddress.isEmpty());
        AddAttribute(XML_NAMESPACE_TABLE, XML_BASE_CELL_ADDRESS, sBaseCellAddress);
 
        OUString sTempSymbol(rxEntry.second->GetSymbol(pDoc->GetStorageGrammar()));
        ScRange aRange;
        if (rxEntry.second->IsReference(aRange))
        {
 
            OUString sContent(sTempSymbol.copy(1, sTempSymbol.getLength() -2 ));
            AddAttribute(XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, sContent);
 
            sal_Int32 nRangeType = rxEntry.second->GetUnoType();
            OUStringBuffer sBufferRangeType;
            if ((nRangeType & sheet::NamedRangeFlag::COLUMN_HEADER) == sheet::NamedRangeFlag::COLUMN_HEADER)
                sBufferRangeType.append(GetXMLToken(XML_REPEAT_COLUMN));
            if ((nRangeType & sheet::NamedRangeFlag::ROW_HEADER) == sheet::NamedRangeFlag::ROW_HEADER)
            {
                if (!sBufferRangeType.isEmpty())
                    sBufferRangeType.append(" ");
                sBufferRangeType.append(GetXMLToken(XML_REPEAT_ROW));
            }
            if ((nRangeType & sheet::NamedRangeFlag::FILTER_CRITERIA) == sheet::NamedRangeFlag::FILTER_CRITERIA)
            {
                if (!sBufferRangeType.isEmpty())
                    sBufferRangeType.append(" ");
                sBufferRangeType.append(GetXMLToken(XML_FILTER));
            }
            if ((nRangeType & sheet::NamedRangeFlag::PRINT_AREA) == sheet::NamedRangeFlag::PRINT_AREA)
            {
                if (!sBufferRangeType.isEmpty())
                    sBufferRangeType.append(" ");
                sBufferRangeType.append(GetXMLToken(XML_PRINT_RANGE));
            }
            if ((nRangeType & sheet::NamedRangeFlag::HIDDEN) == sheet::NamedRangeFlag::HIDDEN)
            {
                if (!sBufferRangeType.isEmpty())
                    sBufferRangeType.append(" ");
                sBufferRangeType.append(GetXMLToken(XML_HIDDEN));
            }
            OUString sRangeType = sBufferRangeType.makeStringAndClear();
            if (!sRangeType.isEmpty())
                AddAttribute(XML_NAMESPACE_TABLE, XML_RANGE_USABLE_AS, sRangeType);
            SvXMLElementExport aElemNR(*this, XML_NAMESPACE_TABLE, XML_NAMED_RANGE, true, true);
 
        }
        else
        {
            AddAttribute(XML_NAMESPACE_TABLE, XML_EXPRESSION, sTempSymbol);
            // Check if it is a hidden named expression
            sal_Int32 nRangeType = rxEntry.second->GetUnoType();
            if ((nRangeType & sheet::NamedRangeFlag::HIDDEN) == sheet::NamedRangeFlag::HIDDEN)
                AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDDEN, XML_TRUE);
            SvXMLElementExport aElemNE(*this, XML_NAMESPACE_TABLE, XML_NAMED_EXPRESSION, true, true);
        }
    }
}
 
void ScXMLExport::exportSparklineGroups(SCTAB nTable)
{
    sc::SparklineGroupsExport aSparklineGroupExport(*this, nTable);
    aSparklineGroupExport.write();
}
 
namespace {
 
OUString getCondFormatEntryType(const ScColorScaleEntry& rEntry, bool bFirst = true)
{
    switch(rEntry.GetType())
    {
        case COLORSCALE_MIN:
            return u"minimum"_ustr;
        case COLORSCALE_MAX:
            return u"maximum"_ustr;
        case COLORSCALE_PERCENT:
            return u"percent"_ustr;
        case COLORSCALE_PERCENTILE:
            return u"percentile"_ustr;
        case COLORSCALE_FORMULA:
            return u"formula"_ustr;
        case COLORSCALE_VALUE:
            return u"number"_ustr;
        case COLORSCALE_AUTO:
            // only important for data bars
            if(bFirst)
                return u"auto-minimum"_ustr;
            else
                return u"auto-maximum"_ustr;
    }
    return OUString();
}
 
OUString getDateStringForType(condformat::ScCondFormatDateType eType)
{
    switch(eType)
    {
        case condformat::TODAY:
            return u"today"_ustr;
        case condformat::YESTERDAY:
            return u"yesterday"_ustr;
        case condformat::TOMORROW:
            return u"tomorrow"_ustr;
        case condformat::LAST7DAYS:
            return u"last-7-days"_ustr;
        case condformat::THISWEEK:
            return u"this-week"_ustr;
        case condformat::LASTWEEK:
            return u"last-week"_ustr;
        case condformat::NEXTWEEK:
            return u"next-week"_ustr;
        case condformat::THISMONTH:
            return u"this-month"_ustr;
        case condformat::LASTMONTH:
            return u"last-month"_ustr;
        case condformat::NEXTMONTH:
            return u"next-month"_ustr;
        case condformat::THISYEAR:
            return u"this-year"_ustr;
        case condformat::LASTYEAR:
            return u"last-year"_ustr;
        case condformat::NEXTYEAR:
            return u"next-year"_ustr;
    }
 
    return OUString();
}
 
}
 
void ScXMLExport::ExportConditionalFormat(SCTAB nTab)
{
    ScConditionalFormatList* pCondFormatList = pDoc->GetCondFormList(nTab);
    if(!pCondFormatList)
        return;
 
    if (pCondFormatList->empty())
        return;
 
    SvXMLElementExport aElementCondFormats(*this, XML_NAMESPACE_CALC_EXT, XML_CONDITIONAL_FORMATS, true, true);
 
    for(const auto& rxCondFormat : *pCondFormatList)
    {
        OUString sRanges;
        const ScRangeList& rRangeList = rxCondFormat->GetRange();
        ScRangeStringConverter::GetStringFromRangeList( sRanges, &rRangeList, pDoc, formula::FormulaGrammar::CONV_OOO );
        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TARGET_RANGE_ADDRESS, sRanges);
        SvXMLElementExport aElementCondFormat(*this, XML_NAMESPACE_CALC_EXT, XML_CONDITIONAL_FORMAT, true, true);
        size_t nEntries = rxCondFormat->size();
        for(size_t i = 0; i < nEntries; ++i)
        {
            const ScFormatEntry* pFormatEntry = rxCondFormat->GetEntry(i);
            if(pFormatEntry->GetType()==ScFormatEntry::Type::Condition)
            {
                const ScCondFormatEntry* pEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry);
                OUStringBuffer aCond;
                ScAddress aPos = pEntry->GetSrcPos();
                switch(pEntry->GetOperation())
                {
                    case ScConditionMode::Equal:
                        aCond.append("=" + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
                        break;
                    case ScConditionMode::Less:
                        aCond.append("<" + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
                        break;
                    case ScConditionMode::Greater:
                        aCond.append(">" + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
                        break;
                    case ScConditionMode::EqLess:
                        aCond.append("<=" + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
                        break;
                    case ScConditionMode::EqGreater:
                        aCond.append(">=" + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
                        break;
                    case ScConditionMode::NotEqual:
                        aCond.append("!=" + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF));
                        break;
                    case ScConditionMode::Between:
                        aCond.append("between("
                            + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ","
                            + pEntry->GetExpression(aPos, 1, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ")");
                        break;
                    case ScConditionMode::NotBetween:
                        aCond.append("not-between("
                            + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ","
                            + pEntry->GetExpression(aPos, 1, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ")");
                        break;
                    case ScConditionMode::Duplicate:
                        aCond.append("duplicate");
                        break;
                    case ScConditionMode::NotDuplicate:
                        aCond.append("unique");
                        break;
                    case ScConditionMode::Direct:
                        aCond.append("formula-is("
                            + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ")");
                        break;
                    case ScConditionMode::Top10:
                        aCond.append("top-elements("
                            + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ")");
                        break;
                    case ScConditionMode::Bottom10:
                        aCond.append("bottom-elements("
                            + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ")");
                        break;
                    case ScConditionMode::TopPercent:
                        aCond.append("top-percent("
                            + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ")");
                        break;
                    case ScConditionMode::BottomPercent:
                        aCond.append("bottom-percent("
                            + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ")");
                        break;
                    case ScConditionMode::AboveAverage:
                        aCond.append("above-average");
                        break;
                    case ScConditionMode::BelowAverage:
                        aCond.append("below-average");
                        break;
                    case ScConditionMode::AboveEqualAverage:
                        aCond.append("above-equal-average");
                        break;
                    case ScConditionMode::BelowEqualAverage:
                        aCond.append("below-equal-average");
                        break;
                    case ScConditionMode::Error:
                        aCond.append("is-error");
                        break;
                    case ScConditionMode::NoError:
                        aCond.append("is-no-error");
                        break;
                    case ScConditionMode::BeginsWith:
                        aCond.append("begins-with("
                            + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ")");
                        break;
                    case ScConditionMode::EndsWith:
                        aCond.append("ends-with("
                            + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ")");
                        break;
                    case ScConditionMode::ContainsText:
                        aCond.append("contains-text("
                            + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ")");
                        break;
                    case ScConditionMode::NotContainsText:
                        aCond.append("not-contains-text("
                            + pEntry->GetExpression(aPos, 0, 0, formula::FormulaGrammar::GRAM_ODFF)
                            + ")");
                        break;
                    case ScConditionMode::NONE:
                        continue;
                    default:
                        SAL_WARN("sc", "unimplemented conditional format export");
                }
                OUString sStyle = ScStyleNameConversion::DisplayToProgrammaticName(pEntry->GetStyle(), SfxStyleFamily::Para);
                AddAttribute(XML_NAMESPACE_CALC_EXT, XML_APPLY_STYLE_NAME, sStyle);
                AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, aCond.makeStringAndClear());
 
                OUString sBaseAddress;
                ScRangeStringConverter::GetStringFromAddress( sBaseAddress, aPos, pDoc,formula::FormulaGrammar::CONV_ODF );
                AddAttribute(XML_NAMESPACE_CALC_EXT, XML_BASE_CELL_ADDRESS, sBaseAddress);
                SvXMLElementExport aElementCondEntry(*this, XML_NAMESPACE_CALC_EXT, XML_CONDITION, true, true);
            }
            else if(pFormatEntry->GetType() == ScFormatEntry::Type::Colorscale)
            {
                SvXMLElementExport aElementColorScale(*this, XML_NAMESPACE_CALC_EXT, XML_COLOR_SCALE, true, true);
                const ScColorScaleFormat& rColorScale = static_cast<const ScColorScaleFormat&>(*pFormatEntry);
                for(const auto& rxItem : rColorScale)
                {
                    if(rxItem->GetType() == COLORSCALE_FORMULA)
                    {
                        OUString sFormula = rxItem->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, sFormula);
                    }
                    else
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, OUString::number(rxItem->GetValue()));
 
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, getCondFormatEntryType(*rxItem));
                    OUStringBuffer aBuffer;
                    ::sax::Converter::convertColor(aBuffer, rxItem->GetColor());
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_COLOR, aBuffer.makeStringAndClear());
                    SvXMLElementExport aElementColorScaleEntry(*this, XML_NAMESPACE_CALC_EXT, XML_COLOR_SCALE_ENTRY, true, true);
                }
            }
            else if(pFormatEntry->GetType() == ScFormatEntry::Type::Databar)
            {
                const ScDataBarFormatData* pFormatData = static_cast<const ScDataBarFormat&>(*pFormatEntry).GetDataBarData();
                if(!pFormatData->mbGradient)
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_GRADIENT, XML_FALSE);
                if(pFormatData->mbOnlyBar)
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_SHOW_VALUE, XML_FALSE);
 
                if (pFormatData->mnMinLength != 0.0)
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MIN_LENGTH, OUString::number(pFormatData->mnMinLength));
 
                if (pFormatData->mnMaxLength != 0.0)
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_MAX_LENGTH, OUString::number(pFormatData->mnMaxLength));
 
                if(pFormatData->mbNeg)
                {
                    if(pFormatData->mxNegativeColor)
                    {
                        OUStringBuffer aBuffer;
                        ::sax::Converter::convertColor(aBuffer, *pFormatData->mxNegativeColor);
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_NEGATIVE_COLOR, aBuffer.makeStringAndClear());
                    }
                    else
                    {
                        OUStringBuffer aBuffer;
                        ::sax::Converter::convertColor(aBuffer, COL_LIGHTRED);
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_NEGATIVE_COLOR, aBuffer.makeStringAndClear());
                    }
                }
 
                if(pFormatData->meAxisPosition != databar::AUTOMATIC)
                {
                    if(pFormatData->meAxisPosition == databar::NONE)
                    {
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_AXIS_POSITION, u"none"_ustr);
                    }
                    else
                    {
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_AXIS_POSITION, u"middle"_ustr);
                    }
                }
 
                OUStringBuffer aBuffer;
                ::sax::Converter::convertColor(aBuffer, pFormatData->maPositiveColor);
                AddAttribute(XML_NAMESPACE_CALC_EXT, XML_POSITIVE_COLOR, aBuffer.makeStringAndClear());
 
                aBuffer.truncate();
                ::sax::Converter::convertColor(aBuffer, pFormatData->maAxisColor);
                AddAttribute(XML_NAMESPACE_CALC_EXT, XML_AXIS_COLOR, aBuffer.makeStringAndClear());
                SvXMLElementExport aElementDataBar(*this, XML_NAMESPACE_CALC_EXT, XML_DATA_BAR, true, true);
 
                {
                    if(pFormatData->mpLowerLimit->GetType() == COLORSCALE_FORMULA)
                    {
                        OUString sFormula = pFormatData->mpLowerLimit->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, sFormula);
                    }
                    else
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, OUString::number(pFormatData->mpLowerLimit->GetValue()));
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, getCondFormatEntryType(*pFormatData->mpLowerLimit));
                    SvXMLElementExport aElementDataBarEntryLower(*this, XML_NAMESPACE_CALC_EXT, XML_FORMATTING_ENTRY, true, true);
                }
 
                {
                    if(pFormatData->mpUpperLimit->GetType() == COLORSCALE_FORMULA)
                    {
                        OUString sFormula = pFormatData->mpUpperLimit->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, sFormula);
                    }
                    else
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, OUString::number(pFormatData->mpUpperLimit->GetValue()));
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, getCondFormatEntryType(*pFormatData->mpUpperLimit, false));
                    SvXMLElementExport aElementDataBarEntryUpper(*this, XML_NAMESPACE_CALC_EXT, XML_FORMATTING_ENTRY, true, true);
                }
            }
            else if(pFormatEntry->GetType() == ScFormatEntry::Type::Iconset)
            {
                const ScIconSetFormat& rIconSet = static_cast<const ScIconSetFormat&>(*pFormatEntry);
                OUString aIconSetName = ScIconSetFormat::getIconSetName(rIconSet.GetIconSetData()->eIconSetType);
                AddAttribute( XML_NAMESPACE_CALC_EXT, XML_ICON_SET_TYPE, aIconSetName );
                if (rIconSet.GetIconSetData()->mbCustom)
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_CUSTOM, OUString::boolean(true));
 
                SvXMLElementExport aElementColorScale(*this, XML_NAMESPACE_CALC_EXT, XML_ICON_SET, true, true);
 
                if (rIconSet.GetIconSetData()->mbCustom)
                {
                    for (const auto& [rType, rIndex] : rIconSet.GetIconSetData()->maCustomVector)
                    {
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_CUSTOM_ICONSET_NAME, ScIconSetFormat::getIconSetName(rType));
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_CUSTOM_ICONSET_INDEX, OUString::number(rIndex));
                        SvXMLElementExport aCustomIcon(*this, XML_NAMESPACE_CALC_EXT, XML_CUSTOM_ICONSET, true, true);
                    }
 
                }
 
                if(!rIconSet.GetIconSetData()->mbShowValue)
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_SHOW_VALUE, XML_FALSE);
                for (auto const& it : rIconSet)
                {
                    if(it->GetType() == COLORSCALE_FORMULA)
                    {
                        OUString sFormula = it->GetFormula(formula::FormulaGrammar::GRAM_ODFF);
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, sFormula);
                    }
                    else
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_VALUE, OUString::number(it->GetValue()));
 
                    if (!it->GetGreaterThanOrEqual())
                        AddAttribute(XML_NAMESPACE_CALC_EXT, XML_GREATER_EQUAL, OUString::boolean(false));
 
                    AddAttribute(XML_NAMESPACE_CALC_EXT, XML_TYPE, getCondFormatEntryType(*it));
                    SvXMLElementExport aElementColorScaleEntry(*this, XML_NAMESPACE_CALC_EXT, XML_FORMATTING_ENTRY, true, true);
                }
            }
            else if(pFormatEntry->GetType() == ScFormatEntry::Type::Date)
            {
                const ScCondDateFormatEntry& rDateFormat = static_cast<const ScCondDateFormatEntry&>(*pFormatEntry);
                OUString aDateType = getDateStringForType(rDateFormat.GetDateType());
                OUString aStyleName = ScStyleNameConversion::DisplayToProgrammaticName(rDateFormat.GetStyleName(), SfxStyleFamily::Para );
                AddAttribute( XML_NAMESPACE_CALC_EXT, XML_STYLE, aStyleName);
                AddAttribute( XML_NAMESPACE_CALC_EXT, XML_DATE, aDateType);
                SvXMLElementExport aElementDateFormat(*this, XML_NAMESPACE_CALC_EXT, XML_DATE_IS, true, true);
            }
        }
    }
}
 
void ScXMLExport::WriteExternalRefCaches()
{
    if (!pDoc)
        return;
 
    ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
    pRefMgr->resetSrcFileData(GetOrigFileName());
    sal_uInt16 nCount = pRefMgr->getExternalFileCount();
    for (sal_uInt16 nFileId = 0; nFileId < nCount; ++nFileId)
    {
        const OUString* pUrl = pRefMgr->getExternalFileName(nFileId);
        if (!pUrl)
            continue;
 
        vector<OUString> aTabNames;
        pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
        if (aTabNames.empty())
            continue;
 
        for (const auto& rTabName : aTabNames)
        {
            ScExternalRefCache::TableTypeRef pTable = pRefMgr->getCacheTable(nFileId, rTabName, false);
            if (!pTable || !pTable->isReferenced())
                continue;
 
            AddAttribute(XML_NAMESPACE_TABLE, XML_NAME, "'" + *pUrl + "'#" + rTabName);
            AddAttribute(XML_NAMESPACE_TABLE, XML_PRINT, GetXMLToken(XML_FALSE));
            AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, sExternalRefTabStyleName);
            SvXMLElementExport aElemTable(*this, XML_NAMESPACE_TABLE, XML_TABLE, true, true);
            {
                const ScExternalRefManager::SrcFileData* pExtFileData = pRefMgr->getExternalFileData(nFileId);
                if (pExtFileData)
                {
                    OUString aRelUrl;
                    if (!pExtFileData->maRelativeName.isEmpty())
                        aRelUrl = pExtFileData->maRelativeName;
                    else
                        aRelUrl = GetRelativeReference(pExtFileData->maRelativeName);
                    AddAttribute(XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE);
                    AddAttribute(XML_NAMESPACE_XLINK, XML_HREF, aRelUrl);
                    AddAttribute(XML_NAMESPACE_TABLE, XML_TABLE_NAME, rTabName);
                    if (!pExtFileData->maFilterName.isEmpty())
                        AddAttribute(XML_NAMESPACE_TABLE, XML_FILTER_NAME, pExtFileData->maFilterName);
                    if (!pExtFileData->maFilterOptions.isEmpty())
                        AddAttribute(XML_NAMESPACE_TABLE, XML_FILTER_OPTIONS, pExtFileData->maFilterOptions);
                    AddAttribute(XML_NAMESPACE_TABLE, XML_MODE, XML_COPY_RESULTS_ONLY);
                }
                SvXMLElementExport aElemTableSource(*this, XML_NAMESPACE_TABLE, XML_TABLE_SOURCE, true, true);
            }
 
            // Determine maximum column count of used area, for repeated cells.
            SCCOL nMaxColsUsed = 1;     // assume that there is at least one cell somewhere...
            vector<SCROW> aRows;
            pTable->getAllRows(aRows);
            for (SCROW nRow : aRows)
            {
                vector<SCCOL> aCols;
                pTable->getAllCols(nRow, aCols);
                if (!aCols.empty())
                {
                    SCCOL nCol = aCols.back();
                    if (nMaxColsUsed <= nCol)
                        nMaxColsUsed = nCol + 1;
                }
            }
 
            // Column definitions have to be present to make a valid file
            {
                if (nMaxColsUsed > 1)
                    AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
                                    OUString::number(nMaxColsUsed));
                SvXMLElementExport aElemColumn(*this, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true);
            }
 
            // Write cache content for this table.
            SCROW nLastRow = 0;
            bool bFirstRow = true;
            for (SCROW nRow : aRows)
            {
                if (bFirstRow)
                {
                    if (nRow > 0)
                    {
                        if (nRow > 1)
                        {
                            OUString aVal = OUString::number(nRow);
                            AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_REPEATED, aVal);
                        }
                        SvXMLElementExport aElemRow(*this, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true);
                        OUString aVal = OUString::number(static_cast<sal_Int32>(nMaxColsUsed));
                        AddAttribute(XML_NAMESPACE_TABLE,  XML_NUMBER_COLUMNS_REPEATED, aVal);
                        SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
                    }
                }
                else
                {
                    SCROW nRowGap = nRow - nLastRow;
                    if (nRowGap > 1)
                    {
                        if (nRowGap > 2)
                        {
                            OUString aVal = OUString::number(static_cast<sal_Int32>(nRowGap-1));
                            AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_ROWS_REPEATED, aVal);
                        }
                        SvXMLElementExport aElemRow(*this, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true);
                        OUString aVal = OUString::number(static_cast<sal_Int32>(nMaxColsUsed));
                        AddAttribute(XML_NAMESPACE_TABLE,  XML_NUMBER_COLUMNS_REPEATED, aVal);
                        SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
                    }
                }
                SvXMLElementExport aElemRow(*this, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true);
 
                vector<SCCOL> aCols;
                pTable->getAllCols(nRow, aCols);
                SCCOL nLastCol = 0;
                bool bFirstCol = true;
                for (SCCOL nCol : aCols)
                {
                    if (bFirstCol)
                    {
                        if (nCol > 0)
                        {
                            if (nCol > 1)
                            {
                                OUString aVal = OUString::number(static_cast<sal_Int32>(nCol));
                                AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, aVal);
                            }
                            SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
                        }
                    }
                    else
                    {
                        SCCOL nColGap = nCol - nLastCol;
                        if (nColGap > 1)
                        {
                            if (nColGap > 2)
                            {
                                OUString aVal = OUString::number(static_cast<sal_Int32>(nColGap-1));
                                AddAttribute(XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED, aVal);
                            }
                            SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
                        }
                    }
 
                    // Write out this cell.
                    sal_uInt32 nNumFmt = 0;
                    ScExternalRefCache::TokenRef pToken = pTable->getCell(nCol, nRow, &nNumFmt);
                    OUString aStrVal;
                    if (pToken)
                    {
                        sal_Int32 nIndex = GetNumberFormatStyleIndex(nNumFmt);
                        if (nIndex >= 0)
                        {
                            const OUString & aStyleName = pCellStyles->GetStyleNameByIndex(nIndex, true);
                            AddAttribute(XML_NAMESPACE_TABLE, XML_STYLE_NAME, aStyleName);
                        }
 
                        switch(pToken->GetType())
                        {
                            case svDouble:
                            {
                                AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT);
                                aStrVal = OUString::number(pToken->GetDouble());
                                AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE, aStrVal);
                            }
                            break;
                            case svString:
                            {
                                AddAttribute(XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING);
                                aStrVal = pToken->GetString().getString();
                            }
                            break;
                            default:
                                ;
                        }
                    }
                    SvXMLElementExport aElemCell(*this, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true);
                    SvXMLElementExport aElemText(*this, XML_NAMESPACE_TEXT, XML_P, true, false);
                    Characters(aStrVal);
 
                    nLastCol = nCol;
                    bFirstCol = false;
                }
                nLastRow = nRow;
                bFirstRow = false;
            }
        }
    }
}
 
// core implementation
void ScXMLExport::WriteConsolidation()
{
    if (!pDoc)
        return;
 
    const ScConsolidateParam* pCons(pDoc->GetConsolidateDlgData());
    if( !pCons )
        return;
 
    OUString sStrData = ScXMLConverter::GetStringFromFunction( pCons->eFunction );
    AddAttribute( XML_NAMESPACE_TABLE, XML_FUNCTION, sStrData );
 
    sStrData.clear();
    for( sal_Int32 nIndex = 0; nIndex < pCons->nDataAreaCount; ++nIndex )
        ScRangeStringConverter::GetStringFromArea( sStrData, pCons->pDataAreas[ nIndex ], pDoc, FormulaGrammar::CONV_OOO, ' ', true );
    AddAttribute( XML_NAMESPACE_TABLE, XML_SOURCE_CELL_RANGE_ADDRESSES, sStrData );
 
    ScRangeStringConverter::GetStringFromAddress( sStrData, ScAddress( pCons->nCol, pCons->nRow, pCons->nTab ), pDoc, FormulaGrammar::CONV_OOO );
    AddAttribute( XML_NAMESPACE_TABLE, XML_TARGET_CELL_ADDRESS, sStrData );
 
    if( pCons->bByCol && !pCons->bByRow )
        AddAttribute( XML_NAMESPACE_TABLE, XML_USE_LABEL, XML_COLUMN );
    else if( !pCons->bByCol && pCons->bByRow )
        AddAttribute( XML_NAMESPACE_TABLE, XML_USE_LABEL, XML_ROW );
    else if( pCons->bByCol && pCons->bByRow )
        AddAttribute( XML_NAMESPACE_TABLE, XML_USE_LABEL, XML_BOTH );
 
    if( pCons->bReferenceData )
        AddAttribute( XML_NAMESPACE_TABLE, XML_LINK_TO_SOURCE_DATA, XML_TRUE );
 
    SvXMLElementExport aElem( *this, XML_NAMESPACE_TABLE, XML_CONSOLIDATION, true, true );
}
 
SvXMLAutoStylePoolP* ScXMLExport::CreateAutoStylePool()
{
    return new ScXMLAutoStylePoolP(*this);
}
 
XMLPageExport* ScXMLExport::CreatePageExport()
{
    return new XMLTableMasterPageExport( *this );
}
 
void ScXMLExport::GetChangeTrackViewSettings(uno::Sequence<beans::PropertyValue>& rProps)
{
    ScChangeViewSettings* pViewSettings(GetDocument() ? GetDocument()->GetChangeViewSettings() : nullptr);
    if (!pViewSettings)
        return;
 
    sal_Int32 nChangePos(rProps.getLength());
    rProps.realloc(nChangePos + 1);
    beans::PropertyValue* pProps(rProps.getArray());
 
    uno::Sequence<beans::PropertyValue> aChangeProps(SC_VIEWCHANGES_COUNT);
    beans::PropertyValue* pChangeProps(aChangeProps.getArray());
    pChangeProps[SC_SHOW_CHANGES].Name = "ShowChanges";
    pChangeProps[SC_SHOW_CHANGES].Value <<= pViewSettings->ShowChanges();
    pChangeProps[SC_SHOW_ACCEPTED_CHANGES].Name = "ShowAcceptedChanges";
    pChangeProps[SC_SHOW_ACCEPTED_CHANGES].Value <<= pViewSettings->IsShowAccepted();
    pChangeProps[SC_SHOW_REJECTED_CHANGES].Name = "ShowRejectedChanges";
    pChangeProps[SC_SHOW_REJECTED_CHANGES].Value <<= pViewSettings->IsShowRejected();
    pChangeProps[SC_SHOW_CHANGES_BY_DATETIME].Name = "ShowChangesByDatetime";
    pChangeProps[SC_SHOW_CHANGES_BY_DATETIME].Value <<= pViewSettings->HasDate();
    pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_MODE].Name = "ShowChangesByDatetimeMode";
    pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_MODE].Value <<= static_cast<sal_Int16>(pViewSettings->GetTheDateMode());
    pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_FIRST_DATETIME].Name = "ShowChangesByDatetimeFirstDatetime";
    pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_FIRST_DATETIME].Value <<= pViewSettings->GetTheFirstDateTime().GetUNODateTime();
    pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_SECOND_DATETIME].Name = "ShowChangesByDatetimeSecondDatetime";
    pChangeProps[SC_SHOW_CHANGES_BY_DATETIME_SECOND_DATETIME].Value <<= pViewSettings->GetTheLastDateTime().GetUNODateTime();
    pChangeProps[SC_SHOW_CHANGES_BY_AUTHOR].Name = "ShowChangesByAuthor";
    pChangeProps[SC_SHOW_CHANGES_BY_AUTHOR].Value <<= pViewSettings->HasAuthor();
    pChangeProps[SC_SHOW_CHANGES_BY_AUTHOR_NAME].Name = "ShowChangesByAuthorName";
    pChangeProps[SC_SHOW_CHANGES_BY_AUTHOR_NAME].Value <<= pViewSettings->GetTheAuthorToShow();
    pChangeProps[SC_SHOW_CHANGES_BY_COMMENT].Name = "ShowChangesByComment";
    pChangeProps[SC_SHOW_CHANGES_BY_COMMENT].Value <<= pViewSettings->HasComment();
    pChangeProps[SC_SHOW_CHANGES_BY_COMMENT_TEXT].Name = "ShowChangesByCommentText";
    pChangeProps[SC_SHOW_CHANGES_BY_COMMENT_TEXT].Value <<= pViewSettings->GetTheComment();
    pChangeProps[SC_SHOW_CHANGES_BY_RANGES].Name = "ShowChangesByRanges";
    pChangeProps[SC_SHOW_CHANGES_BY_RANGES].Value <<= pViewSettings->HasRange();
    OUString sRangeList;
    ScRangeStringConverter::GetStringFromRangeList(sRangeList, &(pViewSettings->GetTheRangeList()), GetDocument(), FormulaGrammar::CONV_OOO);
    pChangeProps[SC_SHOW_CHANGES_BY_RANGES_LIST].Name = "ShowChangesByRangesList";
    pChangeProps[SC_SHOW_CHANGES_BY_RANGES_LIST].Value <<= sRangeList;
 
    pProps[nChangePos].Name = "TrackedChangesViewSettings";
    pProps[nChangePos].Value <<= aChangeProps;
}
 
void ScXMLExport::GetViewSettings(uno::Sequence<beans::PropertyValue>& rProps)
{
    if (GetModel().is())
    {
        rProps.realloc(4);
        beans::PropertyValue* pProps(rProps.getArray());
        ScModelObj* pDocObj(comphelper::getFromUnoTunnel<ScModelObj>( GetModel() ));
        if (pDocObj)
        {
            SfxObjectShell* pEmbeddedObj = pDocObj->GetEmbeddedObject();
            if (pEmbeddedObj)
            {
                tools::Rectangle aRect(pEmbeddedObj->GetVisArea());
                sal_uInt16 i(0);
                pProps[i].Name = "VisibleAreaTop";
                pProps[i].Value <<= static_cast<sal_Int32>(aRect.Top());
                pProps[++i].Name = "VisibleAreaLeft";
                pProps[i].Value <<= static_cast<sal_Int32>(aRect.Left());
                pProps[++i].Name = "VisibleAreaWidth";
                pProps[i].Value <<= static_cast<sal_Int32>(aRect.getOpenWidth());
                pProps[++i].Name = "VisibleAreaHeight";
                pProps[i].Value <<= static_cast<sal_Int32>(aRect.getOpenHeight());
            }
        }
    }
    GetChangeTrackViewSettings(rProps);
}
 
void ScXMLExport::GetConfigurationSettings(uno::Sequence<beans::PropertyValue>& rProps)
{
    if (!GetModel().is())
        return;
 
    uno::Reference <lang::XMultiServiceFactory> xMultiServiceFactory(GetModel(), uno::UNO_QUERY);
    if (!xMultiServiceFactory.is())
        return;
 
    uno::Reference <beans::XPropertySet> xProperties(xMultiServiceFactory->createInstance(u"com.sun.star.comp.SpreadsheetSettings"_ustr), uno::UNO_QUERY);
    if (xProperties.is())
        SvXMLUnitConverter::convertPropertySet(rProps, xProperties);
 
    sal_Int32 nPropsToAdd = 0;
    OUStringBuffer aTrackedChangesKey;
    if (GetDocument() && GetDocument()->GetChangeTrack() && GetDocument()->GetChangeTrack()->IsProtected())
    {
        ::comphelper::Base64::encode(aTrackedChangesKey,
                GetDocument()->GetChangeTrack()->GetProtection());
        if (!aTrackedChangesKey.isEmpty())
            ++nPropsToAdd;
    }
 
    bool bVBACompat = false;
    uno::Reference <container::XNameAccess> xCodeNameAccess;
    OSL_ENSURE( pDoc, "ScXMLExport::GetConfigurationSettings - no ScDocument!" );
    // tdf#71271 - add code names regardless of VBA compatibility mode
    if (pDoc)
    {
        // VBA compatibility mode
        if (bVBACompat = pDoc->IsInVBAMode(); bVBACompat)
            ++nPropsToAdd;
 
        // code names
        xCodeNameAccess = new XMLCodeNameProvider( pDoc );
        if( xCodeNameAccess->hasElements() )
            ++nPropsToAdd;
        else
            xCodeNameAccess.clear();
    }
 
    if( nPropsToAdd <= 0 )
        return;
 
    sal_Int32 nCount(rProps.getLength());
    rProps.realloc(nCount + nPropsToAdd);
    auto pProps = rProps.getArray();
    if (!aTrackedChangesKey.isEmpty())
    {
        pProps[nCount].Name = "TrackedChangesProtectionKey";
        pProps[nCount].Value <<= aTrackedChangesKey.makeStringAndClear();
        ++nCount;
    }
    if( bVBACompat )
    {
        pProps[nCount].Name = "VBACompatibilityMode";
        pProps[nCount].Value <<= bVBACompat;
        ++nCount;
    }
    if( xCodeNameAccess.is() )
    {
        pProps[nCount].Name = "ScriptConfiguration";
        pProps[nCount].Value <<= xCodeNameAccess;
        ++nCount;
    }
}
 
XMLShapeExport* ScXMLExport::CreateShapeExport()
{
    return new ScXMLShapeExport(*this);
}
 
XMLNumberFormatAttributesExportHelper* ScXMLExport::GetNumberFormatAttributesExportHelper()
{
    if (!pNumberFormatAttributesExportHelper)
        pNumberFormatAttributesExportHelper.reset(new XMLNumberFormatAttributesExportHelper(GetNumberFormatsSupplier(), *this ));
    return pNumberFormatAttributesExportHelper.get();
}
 
void ScXMLExport::CollectUserDefinedNamespaces(const SfxItemPool* pPool, sal_uInt16 nAttrib)
{
    ItemSurrogates aSurrogates;
    pPool->GetItemSurrogates(aSurrogates, nAttrib);
    for (const SfxPoolItem* pItem : aSurrogates)
    {
        const SvXMLAttrContainerItem *pUnknown(static_cast<const SvXMLAttrContainerItem *>(pItem));
        if( pUnknown->GetAttrCount() > 0 )
        {
            sal_uInt16 nIdx(pUnknown->GetFirstNamespaceIndex());
            while( USHRT_MAX != nIdx )
            {
                if( (XML_NAMESPACE_UNKNOWN_FLAG & nIdx) != 0 )
                {
                    const OUString& rPrefix = pUnknown->GetPrefix( nIdx );
                    // Add namespace declaration for unknown attributes if
                    // there aren't existing ones for the prefix used by the
                    // attributes
                    GetNamespaceMap_().Add( rPrefix,
                                            pUnknown->GetNamespace( nIdx ) );
                }
                nIdx = pUnknown->GetNextNamespaceIndex( nIdx );
            }
        }
    }
 
    // #i66550# needed for 'presentation:event-listener' element for URLs in shapes
    GetNamespaceMap_().Add(
        GetXMLToken( XML_NP_PRESENTATION ),
        GetXMLToken( XML_N_PRESENTATION ),
        XML_NAMESPACE_PRESENTATION );
}
 
void ScXMLExport::IncrementProgressBar(bool bFlush, sal_Int32 nInc)
{
    nProgressCount += nInc;
    if (bFlush || nProgressCount > 100)
    {
        GetProgressBarHelper()->Increment(nProgressCount);
        nProgressCount = 0;
    }
}
 
ErrCode ScXMLExport::exportDoc( enum XMLTokenEnum eClass )
{
    if( getExportFlags() & (SvXMLExportFlags::FONTDECLS|SvXMLExportFlags::STYLES|
                             SvXMLExportFlags::MASTERSTYLES|SvXMLExportFlags::CONTENT) )
    {
        if (GetDocument())
        {
            // if source doc was Excel then
            uno::Reference< frame::XModel > xModel = GetModel();
            if ( xModel.is() )
            {
                auto pFoundShell = comphelper::getFromUnoTunnel<SfxObjectShell>(xModel);
                if ( pFoundShell && ooo::vba::isAlienExcelDoc( *pFoundShell ) )
                {
                    xRowStylesPropertySetMapper = new XMLPropertySetMapper(aXMLScFromXLSRowStylesProperties, xScPropHdlFactory, true);
                    xRowStylesExportPropertySetMapper = new ScXMLRowExportPropertyMapper(xRowStylesPropertySetMapper);
                    GetAutoStylePool()->SetFamilyPropSetMapper( XmlStyleFamily::TABLE_ROW,
                        xRowStylesExportPropertySetMapper );
                }
            }
            CollectUserDefinedNamespaces(GetDocument()->GetPool(), ATTR_USERDEF);
            CollectUserDefinedNamespaces(GetDocument()->GetEditPool(), EE_PARA_XMLATTRIBS);
            CollectUserDefinedNamespaces(GetDocument()->GetEditPool(), EE_CHAR_XMLATTRIBS);
            ScDrawLayer* pDrawLayer = GetDocument()->GetDrawLayer();
            if (pDrawLayer)
            {
                CollectUserDefinedNamespaces(&pDrawLayer->GetItemPool(), EE_PARA_XMLATTRIBS);
                CollectUserDefinedNamespaces(&pDrawLayer->GetItemPool(), EE_CHAR_XMLATTRIBS);
                CollectUserDefinedNamespaces(&pDrawLayer->GetItemPool(), SDRATTR_XMLATTRIBUTES);
            }
 
            // sheet events use officeooo namespace
            if( (getExportFlags() & SvXMLExportFlags::CONTENT) &&
                getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012)
            {
                bool bAnySheetEvents = false;
                SCTAB nTabCount = pDoc->GetTableCount();
                for (SCTAB nTab=0; nTab<nTabCount; ++nTab)
                    if (pDoc->GetSheetEvents(nTab))
                        bAnySheetEvents = true;
                if (bAnySheetEvents)
                    GetNamespaceMap_().Add(
                        GetXMLToken( XML_NP_OFFICE_EXT ),
                        GetXMLToken( XML_N_OFFICE_EXT ),
                        XML_NAMESPACE_OFFICE_EXT );
            }
        }
    }
    return SvXMLExport::exportDoc( eClass );
}
 
// XExporter
void SAL_CALL ScXMLExport::setSourceDocument( const uno::Reference<lang::XComponent>& xComponent )
{
    SolarMutexGuard aGuard;
    SvXMLExport::setSourceDocument( xComponent );
 
    pDoc = ScXMLConverter::GetScDocument( GetModel() );
    OSL_ENSURE( pDoc, "ScXMLExport::setSourceDocument - no ScDocument!" );
    if (!pDoc)
        throw lang::IllegalArgumentException();
 
    // create ScChangeTrackingExportHelper after document is known
    pChangeTrackingExportHelper.reset(new ScChangeTrackingExportHelper(*this));
 
    // Set the document's storage grammar corresponding to the ODF version that
    // is to be written.
    SvtSaveOptions::ODFSaneDefaultVersion meODFDefaultVersion = getSaneDefaultVersion();
    switch (meODFDefaultVersion)
    {
        // ODF 1.0 and 1.1 use GRAM_PODF, everything later or unspecified GRAM_ODFF
        case SvtSaveOptions::ODFSVER_010:
        case SvtSaveOptions::ODFSVER_011:
            pDoc->SetStorageGrammar( formula::FormulaGrammar::GRAM_PODF);
            break;
        default:
            pDoc->SetStorageGrammar( formula::FormulaGrammar::GRAM_ODFF);
    }
}
 
// XFilter
sal_Bool SAL_CALL ScXMLExport::filter( const css::uno::Sequence< css::beans::PropertyValue >& aDescriptor )
{
    SolarMutexGuard aGuard;
    if (pDoc)
        pDoc->EnableIdle(false);
    bool bReturn(SvXMLExport::filter(aDescriptor));
    if (pDoc)
        pDoc->EnableIdle(true);
    return bReturn;
}
 
void SAL_CALL ScXMLExport::cancel()
{
    SolarMutexGuard aGuard;
    if (pDoc)
        pDoc->EnableIdle(true);
    SvXMLExport::cancel();
}
 
// XInitialization
void SAL_CALL ScXMLExport::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
{
    SolarMutexGuard aGuard;
    SvXMLExport::initialize(aArguments);
}
 
void ScXMLExport::DisposingModel()
{
    SvXMLExport::DisposingModel();
    pDoc = nullptr;
    xCurrentTable = nullptr;
}
 
void ScXMLExport::SetSharedData(std::unique_ptr<ScMySharedData> pTemp) { pSharedData = std::move(pTemp); }
 
std::unique_ptr<ScMySharedData> ScXMLExport::ReleaseSharedData() { return std::move(pSharedData); }
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'Add' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'append' is required to be utilized.

V530 The return value of function 'truncate' is required to be utilized.

V547 Expression 'nNumberFormat == - 1' is always true.

V547 Expression 'nIterationCount != 100' is always false.

V1053 Calling the 'CreateAutoStylePool' virtual function indirectly in the constructor may lead to unexpected result at runtime. Check lines: 'xmlexprt.cxx:387', 'xmlexp.hxx:579', 'xmlexprt.hxx:228'.

V547 Expression 'nRefresh' is always false.

V560 A part of conditional expression is always false: nIterationCount != 100.

V560 A part of conditional expression is always false: nIterationCount != 100.

V728 An excessive check can be simplified. The '||' operator is surrounded by opposite expressions 'bIsFirst' and '!bIsFirst'.

V1020 The function exited without calling the 'CloseHeaderColumn' function. Check lines: 873, 815.

V1020 The function exited without calling the 'CloseHeaderColumn' function. Check lines: 873, 854.

V1020 The function exited without calling the 'CloseHeaderColumn' function. Check lines: 873, 862.

V1020 The function exited without calling the 'CloseHeaderColumn' function. Check lines: 874, 815.

V1020 The function exited without calling the 'CloseHeaderColumn' function. Check lines: 874, 854.

V1020 The function exited without calling the 'CloseHeaderColumn' function. Check lines: 874, 862.