Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/oox/source/export/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 207 kB image not shown  

Quelle  chartexport.cxx   Sprache: C

 
/* -*- 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 <oox/token/namespaces.hxx>
#include <oox/token/properties.hxx>
#include <oox/token/tokens.hxx>
#include <oox/core/xmlfilterbase.hxx>
#include <oox/export/chartexport.hxx>
#include <oox/token/relationship.hxx>
#include <oox/export/utils.hxx>
#include <drawingml/chart/typegroupconverter.hxx>
#include <basegfx/utils/gradienttools.hxx>
#include <docmodel/uno/UnoGradientTools.hxx>

#include <cstdio>
#include <limits>

#include <com/sun/star/awt/Gradient2.hpp>
#include <com/sun/star/chart/XChartDocument.hpp>
#include <com/sun/star/chart/ChartLegendPosition.hpp>
#include <com/sun/star/chart/XTwoAxisXSupplier.hpp>
#include <com/sun/star/chart/XTwoAxisYSupplier.hpp>
#include <com/sun/star/chart/XAxisZSupplier.hpp>
#include <com/sun/star/chart/ChartDataRowSource.hpp>
#include <com/sun/star/chart/X3DDisplay.hpp>
#include <com/sun/star/chart/XStatisticDisplay.hpp>
#include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp>
#include <com/sun/star/chart/ChartSymbolType.hpp>
#include <com/sun/star/chart/ChartAxisMarks.hpp>
#include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
#include <com/sun/star/chart/ChartAxisPosition.hpp>
#include <com/sun/star/chart/ChartSolidType.hpp>
#include <com/sun/star/chart/DataLabelPlacement.hpp>
#include <com/sun/star/chart/ErrorBarStyle.hpp>
#include <com/sun/star/chart/MissingValueTreatment.hpp>
#include <com/sun/star/chart/XDiagramPositioning.hpp>
#include <com/sun/star/chart/TimeIncrement.hpp>
#include <com/sun/star/chart/TimeInterval.hpp>
#include <com/sun/star/chart/TimeUnit.hpp>

#include <com/sun/star/chart2/RelativePosition.hpp>
#include <com/sun/star/chart2/RelativeSize.hpp>
#include <com/sun/star/chart2/XChartDocument.hpp>
#include <com/sun/star/chart2/XDiagram.hpp>
#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
#include <com/sun/star/chart2/XChartTypeContainer.hpp>
#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
#include <com/sun/star/chart2/DataPointLabel.hpp>
#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
#include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
#include <com/sun/star/chart2/PieChartSubType.hpp>
#include <com/sun/star/chart2/Symbol.hpp>
#include <com/sun/star/chart2/data/XDataSource.hpp>
#include <com/sun/star/chart2/data/XDataProvider.hpp>
#include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
#include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
#include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
#include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
#include <com/sun/star/chart2/AxisType.hpp>

#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/drawing/XShape.hpp>
#include <com/sun/star/drawing/XShapes.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <com/sun/star/drawing/LineStyle.hpp>
#include <com/sun/star/awt/XBitmap.hpp>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/lang/XServiceName.hpp>

#include <com/sun/star/table/CellAddress.hpp>
#include <com/sun/star/sheet/XFormulaParser.hpp>
#include <com/sun/star/sheet/FormulaToken.hpp>
#include <com/sun/star/sheet/AddressConvention.hpp>

#include <com/sun/star/container/XNamed.hpp>
#include <com/sun/star/embed/XVisualObject.hpp>
#include <com/sun/star/embed/Aspects.hpp>

#include <comphelper/processfactory.hxx>
#include <comphelper/random.hxx>
#include <utility>
#include <xmloff/SchXMLSeriesHelper.hxx>
#include "ColorPropertySet.hxx"

#include <svl/numformat.hxx>
#include <svl/numuno.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <sal/log.hxx>

#include <set>
#include <unordered_set>

#include <frozen/bits/defines.h>
#include <frozen/bits/elsa_std.h>
#include <frozen/unordered_map.h>

#include <o3tl/temporary.hxx>
#include <o3tl/sorted_vector.hxx>

using namespace css;
using namespace css::uno;
using namespace css::drawing;
using namespace ::oox::core;
using css::beans::PropertyValue;
using css::beans::XPropertySet;
using css::container::XNamed;
using css::table::CellAddress;
using css::sheet::XFormulaParser;
using ::oox::core::XmlFilterBase;
using ::sax_fastparser::FSHelperPtr;

namespace cssc = css::chart;

namespace oox::drawingml {

namespace {

bool isPrimaryAxes(sal_Int32 nIndex)
{
    assert(nIndex == 0 || nIndex == 1);
    return nIndex != 1;
}

class lcl_MatchesRole
{
public:
    explicit lcl_MatchesRole( OUString aRole ) :
            m_aRole(std::move( aRole ))
    {}

    bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const
    {
        if( !xSeq.is() )
            return  false;
        Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY );
        OUString aRole;

        return ( xProp.is() &&
                 (xProp->getPropertyValue( u"Role"_ustr ) >>= aRole ) &&
                 m_aRole == aRole );
    }

private:
    OUString m_aRole;
};

void outputStyleEntry(FSHelperPtr pFS, sal_Int32 nElTokenId)
{
    // Just default values for now
    pFS->startElement(FSNS(XML_cs, nElTokenId));
    pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0");
    pFS->singleElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0");
    pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0");
    pFS->singleElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor");
    pFS->endElement(FSNS(XML_cs, nElTokenId));
}

void outputChartAreaStyleEntry(FSHelperPtr pFS)
{
    // Just default values for now
    pFS->startElement(FSNS(XML_cs, XML_chartArea), XML_mods, "allowNoFillOverride allowNoLineOverride");
    pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0");
    pFS->singleElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0");
    pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0");

    pFS->startElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor");
    pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "tx1");
    pFS->endElement(FSNS(XML_cs, XML_fontRef));

    pFS->startElement(FSNS(XML_cs, XML_spPr));

    pFS->startElement(FSNS(XML_a, XML_solidFill));
    pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "bg1");
    pFS->endElement(FSNS(XML_a, XML_solidFill));

    pFS->startElement(FSNS(XML_a, XML_ln), XML_w, "9525", XML_cap, "flat",
            XML_cmpd, "sng", XML_algn, "ctr");
    pFS->startElement(FSNS(XML_a, XML_solidFill));
    pFS->startElement(FSNS(XML_a, XML_schemeClr), XML_val, "tx1");
    pFS->singleElement(FSNS(XML_a, XML_lumMod), XML_val, "15000");
    pFS->singleElement(FSNS(XML_a, XML_lumOff), XML_val, "85000");
    pFS->endElement(FSNS(XML_a, XML_schemeClr));
    pFS->endElement(FSNS(XML_a, XML_solidFill));
    pFS->singleElement(FSNS(XML_a, XML_round));
    pFS->endElement(FSNS(XML_a, XML_ln));

    pFS->endElement(FSNS(XML_cs, XML_spPr));

    pFS->endElement(FSNS(XML_cs, XML_chartArea));
}

void outputDataPointStyleEntry(FSHelperPtr pFS)
{
    pFS->startElement(FSNS(XML_cs, XML_dataPoint));
    pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0");

    pFS->startElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0");
    pFS->singleElement(FSNS(XML_cs, XML_styleClr), XML_val, "auto");
    pFS->endElement(FSNS(XML_cs, XML_fillRef));

    pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0");

    pFS->startElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor");
    pFS->singleElement(FSNS(XML_cs, XML_schemeClr), XML_val, "tx1");
    pFS->endElement(FSNS(XML_cs, XML_fontRef));

    pFS->startElement(FSNS(XML_cs, XML_spPr));
    pFS->startElement(FSNS(XML_a, XML_solidFill));
    pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "phClr");
    pFS->endElement(FSNS(XML_a, XML_solidFill));
    pFS->endElement(FSNS(XML_cs, XML_spPr));

    pFS->endElement(FSNS(XML_cs, XML_dataPoint));
}


}   // unnamed namespace

static Reference< chart2::data::XLabeledDataSequence > lcl_getCategories( const Reference< chart2::XDiagram > & xDiagram, bool& bHasDateCategories )
{
    bHasDateCategories = false;
    Reference< chart2::data::XLabeledDataSequence >  xResult;
    try
    {
        Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
            xDiagram, uno::UNO_QUERY_THROW );
        const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
            xCooSysCnt->getCoordinateSystems());
        forconst auto& xCooSys : aCooSysSeq )
        {
            OSL_ASSERT( xCooSys.is());
            for( sal_Int32 nN = xCooSys->getDimension(); nN--; )
            {
                const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nN);
                for(sal_Int32 nI=0; nI<=nMaxAxisIndex; ++nI)
                {
                    Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( nN, nI );
                    OSL_ASSERT( xAxis.is());
                    if( xAxis.is())
                    {
                        chart2::ScaleData aScaleData = xAxis->getScaleData();
                        if( aScaleData.Categories.is())
                        {
                            bHasDateCategories = aScaleData.AxisType == chart2::AxisType::DATE;
                            xResult.set( aScaleData.Categories );
                            break;
                        }
                    }
                }
            }
        }
    }
    catchconst uno::Exception & )
    {
        DBG_UNHANDLED_EXCEPTION("oox");
    }

    return xResult;
}

static Reference< chart2::data::XLabeledDataSequence >
    lcl_getDataSequenceByRole(
        const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aLabeledSeq,
        const OUString & rRole )
{
    Reference< chart2::data::XLabeledDataSequence > aNoResult;

    const Reference< chart2::data::XLabeledDataSequence > * pBegin = aLabeledSeq.getConstArray();
    const Reference< chart2::data::XLabeledDataSequence > * pEnd = pBegin + aLabeledSeq.getLength();
    const Reference< chart2::data::XLabeledDataSequence > * pMatch =
        ::std::find_if( pBegin, pEnd, lcl_MatchesRole( rRole ));

    if( pMatch != pEnd )
        return *pMatch;

    return aNoResult;
}

static bool lcl_hasCategoryLabels( const Reference< chart2::XChartDocument >& xChartDoc )
{
    //categories are always the first sequence
    Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram());
    bool bDateCategories;
    Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xDiagram, bDateCategories ) );
    return xCategories.is();
}

static bool lcl_isCategoryAxisShifted( const Reference< chart2::XDiagram >& xDiagram )
{
    bool bCategoryPositionShifted = false;
    try
    {
        Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
            xDiagram, uno::UNO_QUERY_THROW);
        const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
            xCooSysCnt->getCoordinateSystems());
        for (const auto& xCooSys : aCooSysSeq)
        {
            OSL_ASSERT(xCooSys.is());
            if( 0 < xCooSys->getDimension() && 0 <= xCooSys->getMaximumAxisIndexByDimension(0) )
            {
                Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(0, 0);
                OSL_ASSERT(xAxis.is());
                if (xAxis.is())
                {
                    chart2::ScaleData aScaleData = xAxis->getScaleData();
                    bCategoryPositionShifted = aScaleData.ShiftedCategoryPosition;
                    break;
                }
            }
        }
    }
    catch (const uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("oox");
    }

    return bCategoryPositionShifted;
}

static sal_Int32 lcl_getCategoryAxisType( const Reference< chart2::XDiagram >& xDiagram, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
{
    sal_Int32 nAxisType = -1;
    try
    {
        Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
            xDiagram, uno::UNO_QUERY_THROW);
        const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
            xCooSysCnt->getCoordinateSystems());
        forconst auto& xCooSys : aCooSysSeq )
        {
            OSL_ASSERT(xCooSys.is());
            if( nDimensionIndex < xCooSys->getDimension() && nAxisIndex <= xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex) )
            {
                Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(nDimensionIndex, nAxisIndex);
                OSL_ASSERT(xAxis.is());
                if( xAxis.is() )
                {
                    chart2::ScaleData aScaleData = xAxis->getScaleData();
                    nAxisType = aScaleData.AxisType;
                    break;
                }
            }
        }
    }
    catch (const uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("oox");
    }

    return nAxisType;
}

static OUString lclGetTimeUnitToken( sal_Int32 nTimeUnit )
{
    switch( nTimeUnit )
    {
        case cssc::TimeUnit::DAY:      return u"days"_ustr;
        case cssc::TimeUnit::MONTH:    return u"months"_ustr;
        case cssc::TimeUnit::YEAR:     return u"years"_ustr;
        default:                       OSL_ENSURE(false"lclGetTimeUnitToken - unexpected time unit");
    }
    return u"days"_ustr;
}

static cssc::TimeIncrement lcl_getDateTimeIncrement( const Reference< chart2::XDiagram >&&nbsp;xDiagram, sal_Int32 nAxisIndex )
{
    cssc::TimeIncrement aTimeIncrement;
    try
    {
        Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
            xDiagram, uno::UNO_QUERY_THROW);
        const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
            xCooSysCnt->getCoordinateSystems());
        forconst auto& xCooSys : aCooSysSeq )
        {
            OSL_ASSERT(xCooSys.is());
            if( 0 < xCooSys->getDimension() && nAxisIndex <= xCooSys->getMaximumAxisIndexByDimension(0) )
            {
                Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(0, nAxisIndex);
                OSL_ASSERT(xAxis.is());
                if( xAxis.is() )
                {
                    chart2::ScaleData aScaleData = xAxis->getScaleData();
                    aTimeIncrement = aScaleData.TimeIncrement;
                    break;
                }
            }
        }
    }
    catch (const uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION("oox");
    }

    return aTimeIncrement;
}

static bool lcl_isSeriesAttachedToFirstAxis(
    const Reference< chart2::XDataSeries > & xDataSeries )
{
    bool bResult=true;

    try
    {
        sal_Int32 nAxisIndex = 0;
        Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW );
        xProp->getPropertyValue(u"AttachedAxisIndex"_ustr) >>= nAxisIndex;
        bResult = (0==nAxisIndex);
    }
    catchconst uno::Exception & )
    {
        DBG_UNHANDLED_EXCEPTION("oox");
    }

    return bResult;
}

static OUString lcl_flattenStringSequence( const Sequence< OUString > & rSequence )
{
    OUStringBuffer aResult;
    bool bPrecedeWithSpace = false;
    forconst auto& rString : rSequence )
    {
        if( !rString.isEmpty())
        {
            if( bPrecedeWithSpace )
                aResult.append( ' ' );
            aResult.append( rString );
            bPrecedeWithSpace = true;
        }
    }
    return aResult.makeStringAndClear();
}

static void lcl_writeChartexString(FSHelperPtr pFS, std::u16string_view sOut)
{
    pFS->startElement(FSNS(XML_cx, XML_tx));
    // cell range doesn't seem to be supported in chartex?
    // TODO: also handle <cx:rich>
    pFS->startElement(FSNS(XML_cx, XML_txData));
    // TODO: also handle <cx:f> <cx:v>
    pFS->startElement(FSNS(XML_cx, XML_v));
    pFS->writeEscaped(sOut);
    pFS->endElement( FSNS( XML_cx, XML_v ) );
    pFS->endElement( FSNS( XML_cx, XML_txData ) );
    pFS->endElement( FSNS( XML_cx, XML_tx ) );
}

static Sequence< OUString > lcl_getLabelSequence( const Reference< chart2::data::XDataSequence > & xLabelSeq )
{
    Sequence< OUString > aLabels;

    uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xLabelSeq, uno::UNO_QUERY );
    if( xTextualDataSequence.is())
    {
        aLabels = xTextualDataSequence->getTextualData();
    }
    else if( xLabelSeq.is())
    {
        const Sequence< uno::Any > aAnies( xLabelSeq->getData());
        aLabels.realloc( aAnies.getLength());
        auto pLabels = aLabels.getArray();
        for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
            aAnies[i] >>= pLabels[i];
    }

    return aLabels;
}

static void lcl_fillCategoriesIntoStringVector(
    const Reference< chart2::data::XDataSequence > & xCategories,
    ::std::vector< OUString > & rOutCategories )
{
    OSL_ASSERT( xCategories.is());
    if( !xCategories.is())
        return;
    Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xCategories, uno::UNO_QUERY );
    if( xTextualDataSequence.is())
    {
        rOutCategories.clear();
        const Sequence< OUString > aTextData( xTextualDataSequence->getTextualData());
        rOutCategories.insert( rOutCategories.end(), aTextData.begin(), aTextData.end() );
    }
    else
    {
        Sequence< uno::Any > aAnies( xCategories->getData());
        rOutCategories.resize( aAnies.getLength());
        for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
            aAnies[i] >>= rOutCategories[i];
    }
}

static ::std::vector< double > lcl_getAllValuesFromSequence( const Reference< chart2::data::XDataSequence > & xSeq )
{
    ::std::vector< double > aResult;

    Reference< chart2::data::XNumericalDataSequence > xNumSeq( xSeq, uno::UNO_QUERY );
    if( xNumSeq.is())
    {
        const Sequence< double > aValues( xNumSeq->getNumericalData());
        aResult.insert( aResult.end(), aValues.begin(), aValues.end() );
    }
    else if( xSeq.is())
    {
        Sequence< uno::Any > aAnies( xSeq->getData());
        aResult.resize( aAnies.getLength(), std::numeric_limits<double>::quiet_NaN() );
        for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
            aAnies[i] >>= aResult[i];
    }
    return aResult;
}

namespace
{

constexpr auto constChartTypeMap = frozen::make_unordered_map<std::u16string_view, chart::TypeId>(
{
    { u"com.sun.star.chart.BarDiagram", chart::TYPEID_BAR },
    { u"com.sun.star.chart2.ColumnChartType",  chart::TYPEID_BAR },
    { u"com.sun.star.chart.AreaDiagram",  chart::TYPEID_AREA },
    { u"com.sun.star.chart2.AreaChartType",  chart::TYPEID_AREA },
    { u"com.sun.star.chart.LineDiagram",  chart::TYPEID_LINE },
    { u"com.sun.star.chart2.LineChartType",  chart::TYPEID_LINE },
    { u"com.sun.star.chart.PieDiagram",  chart::TYPEID_PIE },
    { u"com.sun.star.chart2.PieChartType",  chart::TYPEID_PIE },
    { u"com.sun.star.chart.DonutDiagram",  chart::TYPEID_DOUGHNUT },
    { u"com.sun.star.chart2.DonutChartType",  chart::TYPEID_DOUGHNUT },
    { u"com.sun.star.chart.XYDiagram",  chart::TYPEID_SCATTER },
    { u"com.sun.star.chart2.ScatterChartType",  chart::TYPEID_SCATTER },
    { u"com.sun.star.chart.NetDiagram",  chart::TYPEID_RADARLINE },
    { u"com.sun.star.chart2.NetChartType",  chart::TYPEID_RADARLINE },
    { u"com.sun.star.chart.FilledNetDiagram",  chart::TYPEID_RADARAREA },
    { u"com.sun.star.chart2.FilledNetChartType",  chart::TYPEID_RADARAREA },
    { u"com.sun.star.chart.StockDiagram",  chart::TYPEID_STOCK },
    { u"com.sun.star.chart2.CandleStickChartType",  chart::TYPEID_STOCK },
    { u"com.sun.star.chart.BubbleDiagram",  chart::TYPEID_BUBBLE },
    { u"com.sun.star.chart2.BubbleChartType",  chart::TYPEID_BUBBLE },
    { u"com.sun.star.chart.FunnelDiagram",  chart::TYPEID_FUNNEL },
    { u"com.sun.star.chart2.FunnelChartType",  chart::TYPEID_FUNNEL },
});

// end anonymous namespace

static sal_Int32 lcl_getChartType(std::u16string_view sChartType)
{
    auto aIterator = constChartTypeMap.find(sChartType);
    if (aIterator == constChartTypeMap.end())
        return chart::TYPEID_UNKNOWN;
    return aIterator->second;
}

static sal_Int32 lcl_generateRandomValue()
{
    return comphelper::rng::uniform_int_distribution(0, 100000000-1);
}

bool DataLabelsRange::empty() const
{
    return maLabels.empty();
}

size_t DataLabelsRange::count() const
{
    return maLabels.size();
}

bool DataLabelsRange::hasLabel(sal_Int32 nIndex) const
{
    return maLabels.find(nIndex) != maLabels.end();
}

const OUString & DataLabelsRange::getRange() const
{
    return maRange;
}

void DataLabelsRange::setRange(const OUString& rRange)
{
    maRange = rRange;
}

void DataLabelsRange::setLabel(sal_Int32 nIndex, const OUString& rText)
{
    maLabels.emplace(nIndex, rText);
}

DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::begin() const
{
    return maLabels.begin();
}

DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::end() const
{
    return maLabels.end();
}

ChartExport::ChartExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, Reference< frame::XModel > const & xModel, XmlFilterBase* pFB, DocumentType eDocumentType )
    : DrawingML( std::move(pFS), pFB, eDocumentType )
    , mnXmlNamespace( nXmlNamespace )
    , mnSeriesCount(0)
    , mxChartModel( xModel )
    , mpURLTransformer(std::make_shared<URLTransformer>())
    , mbHasCategoryLabels( false )
    , mbHasZAxis( false )
    , mbIs3DChart( false )
    , mbStacked(false)
    , mbPercent(false)
    , mbHasDateCategories(false)
{
}

void ChartExport::SetURLTranslator(const std::shared_ptr<URLTransformer>& pTransformer)
{
    mpURLTransformer = pTransformer;
}

sal_Int32 ChartExport::getChartType( )
{
    OUString sChartType = mxDiagram->getDiagramType();
    return lcl_getChartType( sChartType );
}

namespace {

uno::Sequence< beans::PropertyValue > createArguments(
    const OUString & rRangeRepresentation, bool bUseColumns)
{
    css::chart::ChartDataRowSource eRowSource = css::chart::ChartDataRowSource_ROWS;
    if (bUseColumns)
        eRowSource = css::chart::ChartDataRowSource_COLUMNS;

    uno::Sequence<beans::PropertyValue> aArguments{
        { u"DataRowSource"_ustr, -1, uno::Any(eRowSource), beans::PropertyState_DIRECT_VALUE },
        { u"FirstCellAsLabel"_ustr, -1, uno::Any(false), beans::PropertyState_DIRECT_VALUE },
        { u"HasCategories"_ustr, -1, uno::Any(false), beans::PropertyState_DIRECT_VALUE },
        { u"CellRangeRepresentation"_ustr, -1, uno::Any(rRangeRepresentation),
          beans::PropertyState_DIRECT_VALUE }
    };

    return aArguments;
}

Reference<chart2::XDataSeries> getPrimaryDataSeries(const Reference<chart2::XChartType>& xChartType)
{
    Reference< chart2::XDataSeriesContainer > xDSCnt(xChartType, uno::UNO_QUERY_THROW);

    // export dataseries for current chart-type
    const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq(xDSCnt->getDataSeries());
    for (const auto& rSeries : aSeriesSeq)
    {
        Reference<chart2::XDataSeries> xSource(rSeries, uno::UNO_QUERY);
        if (xSource.is())
            return xSource;
    }

    return Reference<chart2::XDataSeries>();
}

}

Sequence< Sequence< OUString > > ChartExport::getSplitCategoriesList( const OUString& rRange )
{
    Reference< chart2::XChartDocument > xChartDoc(getModel(), uno::UNO_QUERY);
    OSL_ASSERT(xChartDoc.is());
    if (xChartDoc.is())
    {
        Reference< chart2::data::XDataProvider > xDataProvider(xChartDoc->getDataProvider());
        OSL_ENSURE(xDataProvider.is(), "No DataProvider");
        if (xDataProvider.is())
        {
            //detect whether the first series is a row or a column
            bool bSeriesUsesColumns = true;
            Reference< chart2::XDiagram > xDiagram(xChartDoc->getFirstDiagram());
            try
            {
                Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(xDiagram, uno::UNO_QUERY_THROW);
                const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(xCooSysCnt->getCoordinateSystems());
                for (const auto& rCooSys : aCooSysSeq)
                {
                    const Reference< chart2::XChartTypeContainer > xCTCnt(rCooSys, uno::UNO_QUERY_THROW);
                    const Sequence< Reference< chart2::XChartType > > aChartTypeSeq(xCTCnt->getChartTypes());
                    for (const auto& rChartType : aChartTypeSeq)
                    {
                        Reference< chart2::XDataSeries > xDataSeries = getPrimaryDataSeries(rChartType);
                        if (xDataSeries.is())
                        {
                            uno::Reference< chart2::data::XDataSource > xSeriesSource(xDataSeries, uno::UNO_QUERY);
                            const uno::Sequence< beans::PropertyValue > rArguments = xDataProvider->detectArguments(xSeriesSource);
                            for (const beans::PropertyValue& rProperty : rArguments)
                            {
                                if (rProperty.Name == "DataRowSource")
                                {
                                    css::chart::ChartDataRowSource eRowSource;
                                    if (rProperty.Value >>= eRowSource)
                                    {
                                        bSeriesUsesColumns = (eRowSource == css::chart::ChartDataRowSource_COLUMNS);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (const uno::Exception &)
            {
                DBG_UNHANDLED_EXCEPTION("chart2");
            }
            // detect we have an inner data table or not
            if (xChartDoc->hasInternalDataProvider() && rRange == "categories")
            {
                try
                {
                    css::uno::Reference< css::chart2::XAnyDescriptionAccess > xDataAccess(xChartDoc->getDataProvider(), uno::UNO_QUERY);
                    const Sequence< Sequence< uno::Any > >aAnyCategories(bSeriesUsesColumns ? xDataAccess->getAnyRowDescriptions() : xDataAccess->getAnyColumnDescriptions());
                    auto pMax = std::max_element(aAnyCategories.begin(), aAnyCategories.end(),
                        [](const Sequence<uno::Any>& a, const Sequence<uno::Any>& b) {
                            return a.getLength() < b.getLength(); });

                    //minimum is 1!
                    if (pMax != aAnyCategories.end() && pMax->getLength() > 1)
                    {
                        sal_Int32 nLevelCount = pMax->getLength();
                        //we have complex categories
                        //sort the categories name
                        Sequence<Sequence<OUString>>aFinalSplitSource(nLevelCount);
                        auto pFinalSplitSource = aFinalSplitSource.getArray();
                        for (sal_Int32 i = 0; i < nLevelCount; i++)
                        {
                            sal_Int32 nElemLabel = 0;
                            pFinalSplitSource[nLevelCount - i - 1].realloc(aAnyCategories.getLength());
                            auto pSeq = pFinalSplitSource[nLevelCount - i - 1].getArray();
                            for (auto const& elemLabel : aAnyCategories)
                            {
                                // make sure elemLabel[i] exists!
                                if (elemLabel.getLength() > i)
                                {
                                    pSeq[nElemLabel] = elemLabel[i].get<OUString>();
                                    nElemLabel++;
                                }
                            }
                        }
                        return aFinalSplitSource;
                    }
                }
                catch (const uno::Exception &)
                {
                    DBG_UNHANDLED_EXCEPTION("oox");
                }
            }
            else
            {
                try
                {
                    uno::Reference< chart2::data::XDataSource > xCategoriesSource(xDataProvider->createDataSource(
                        createArguments(rRange, bSeriesUsesColumns)));

                    if (xCategoriesSource.is())
                    {
                        const Sequence< Reference< chart2::data::XLabeledDataSequence >> aCategories = xCategoriesSource->getDataSequences();
                        if (aCategories.getLength() > 1)
                        {
                            //we have complex categories
                            //sort the categories name
                            Sequence<Sequence<OUString>> aFinalSplitSource(aCategories.getLength());
                            std::transform(aCategories.begin(), aCategories.end(),
                                std::reverse_iterator(asNonConstRange(aFinalSplitSource).end()),
                                [](const Reference<chart2::data::XLabeledDataSequence>& xCat) {
                                    return lcl_getLabelSequence(xCat->getValues()); });
                            return aFinalSplitSource;
                        }
                    }
                }
                catch (const uno::Exception &)
                {
                    DBG_UNHANDLED_EXCEPTION("oox");
                }
            }
        }
    }

    return Sequence< Sequence< OUString>>(0);
}

OUString ChartExport::parseFormula( const OUString& rRange )
{
    OUString aResult;
    Reference< XFormulaParser > xParser;
    uno::Reference< lang::XMultiServiceFactory > xSF = GetFB()->getModelFactory();
    if( xSF.is() )
    {
        try
        {
            xParser.set( xSF->createInstance(u"com.sun.star.sheet.FormulaParser"_ustr), UNO_QUERY );
        }
        catch( Exception& )
        {
        }
    }

    SAL_WARN_IF(!xParser.is(), "oox""creating formula parser failed");

    if( xParser.is() )
    {
        Reference< XPropertySet > xParserProps( xParser, uno::UNO_QUERY );
        // rRange is the result of a
        // css::chart2::data::XDataSequence::getSourceRangeRepresentation()
        // call that returns the range in the document's current UI notation.
        // Creating a FormulaParser defaults to the same notation, for
        // parseFormula() do not attempt to override the FormulaConvention
        // property with css::sheet::AddressConvention::OOO or some such.
        /* TODO: it would be much better to introduce a
         * getSourceRangeRepresentation(css::sheet::AddressConvention) to
         * return the ranges in a specific convention than converting them with
         * the overhead of creating an XFormulaParser for each... */

        uno::Sequence<sheet::FormulaToken> aTokens = xParser->parseFormula( rRange, CellAddress( 0, 0, 0 ) );
        if( xParserProps.is() )
        {
            xParserProps->setPropertyValue(u"FormulaConvention"_ustr, uno::Any(css::sheet::AddressConvention::XL_OOX) );
            // For referencing named ranges correctly with special excel chart syntax.
            xParserProps->setPropertyValue(u"RefConventionChartOOXML"_ustr, uno::Any(true) );
        }
        aResult = xParser->printFormula( aTokens, CellAddress( 0, 0, 0 ) );
    }
    else
    {
        //FIXME: currently just using simple converter, e.g $Sheet1.$A$1:$C$1 -> Sheet1!$A$1:$C$1
        OUString aRange( rRange );
        if( aRange.startsWith("$") )
            aRange = aRange.copy(1);
        aRange = aRange.replaceAll(".$""!$" );
        aResult = aRange;
    }

    return aResult;
}

void ChartExport::WriteChartObj( const Reference< XShape >& xShape, sal_Int32 nID, sal_Int32 nChartCount )
{
    FSHelperPtr pFS = GetFS();

    Reference< chart2::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
    OSL_ASSERT( xChartDoc.is() );
    if( !xChartDoc.is() )
        return;

    // We need to get the new diagram here so we can know if this is a chartex
    // chart.
    mxNewDiagram.set( xChartDoc->getFirstDiagram());

    const bool bIsChartex = isChartexNotChartNS();

    if (bIsChartex) {
        // Do the AlternateContent header
        mpFS->startElementNS(XML_mc, XML_AlternateContent, FSNS(XML_xmlns, XML_mc),
                "http://schemas.openxmlformats.org/markup-compatibility/2006");
        mpFS->startElementNS(XML_mc, XML_Choice,
                FSNS(XML_xmlns, XML_cx2), "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
                XML_Requires, "cx2");
    }

    Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );

    pFS->startElementNS(mnXmlNamespace, XML_graphicFrame);

    pFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr);

    // TODO: get the correct chart name chart id
    OUString sName = u"Object 1"_ustr;
    Reference< XNamed > xNamed( xShape, UNO_QUERY );
    if (xNamed.is())
        sName = xNamed->getName();

    pFS->startElementNS( mnXmlNamespace, XML_cNvPr,
                          XML_id,     OString::number(nID),
                          XML_name,   sName);

    OUString sURL;
    if ( GetProperty( xShapeProps, u"URL"_ustr ) )
        mAny >>= sURL;
    if( !sURL.isEmpty() )
    {
        OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
                oox::getRelationship(Relationship::HYPERLINK),
                mpURLTransformer->getTransformedString(sURL),
                mpURLTransformer->isExternalURL(sURL));

        mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
    }

    if (bIsChartex) {
        pFS->startElement(FSNS(XML_a, XML_extLst));
        pFS->startElement(FSNS(XML_a, XML_ext), XML_uri,
            "{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}");
        pFS->singleElement(FSNS(XML_a16, XML_creationId),
                FSNS(XML_xmlns, XML_a16), "http://schemas.microsoft.com/office/drawing/2014/main",
                XML_id, "{393D7C90-AF84-3958-641C-0FEC03FE8894}");

        pFS->endElement(FSNS(XML_a, XML_ext));
        pFS->endElement(FSNS(XML_a, XML_extLst));
    }

    pFS->endElementNS(mnXmlNamespace, XML_cNvPr);

    pFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr);

    if( GetDocumentType() == DOCUMENT_PPTX )
        pFS->singleElementNS(mnXmlNamespace, XML_nvPr);
    pFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );

    // visual chart properties
    WriteShapeTransformation( xShape, mnXmlNamespace );

    const char *sSchemaURL = bIsChartex?
        "http://schemas.microsoft.com/office/drawing/2014/chartex" :
        "http://schemas.openxmlformats.org/drawingml/2006/chart";

    // writer chart object
    pFS->startElement(FSNS(XML_a, XML_graphic));
    pFS->startElement( FSNS( XML_a, XML_graphicData ), XML_uri, sSchemaURL );
    OUString sId;
    const char* sFullPath = nullptr;
    const char* sRelativePath = nullptr;
    const char *sChartFnamePrefix = bIsChartex? "chartEx" : "chart";
    switch( GetDocumentType() )
    {
        case DOCUMENT_DOCX:
        {
            sFullPath = "word/charts/";
            sRelativePath = "charts/";
            break;
        }
        case DOCUMENT_PPTX:
        {
            sFullPath = "ppt/charts/";
            sRelativePath = "../charts/";
            break;
        }
        case DOCUMENT_XLSX:
        {
            sFullPath = "xl/charts/";
            sRelativePath = "../charts/";
            break;
        }
        default:
        {
            sFullPath = "charts/";
            sRelativePath = "charts/";
            break;
        }
    }
    OUString sFullStream = OUStringBuffer()
                            .appendAscii(sFullPath)
                            .appendAscii(sChartFnamePrefix)
                            .append(OUString::number(nChartCount) + ".xml")
                            .makeStringAndClear();
    OUString sRelativeStream = OUStringBuffer()
                            .appendAscii(sRelativePath)
                            .appendAscii(sChartFnamePrefix)
                            .append(OUString::number(nChartCount) + ".xml" )
                            .makeStringAndClear();

    const OUString sAppURL = bIsChartex?
        u"application/vnd.ms-office.chartex+xml"_ustr :
        u"application/vnd.openxmlformats-officedocument.drawingml.chart+xml"_ustr;

    const Relationship eChartRel = bIsChartex ?
        Relationship::CHARTEX :
        Relationship::CHART;

    FSHelperPtr pChart = CreateOutputStream(
            sFullStream,
            sRelativeStream,
            pFS->getOutputStream(),
            sAppURL,
            oox::getRelationship(eChartRel),
            &sId );

    XmlFilterBase* pFB = GetFB();

    if (bIsChartex) {
        // Use chartex namespace
        pFS->singleElement(  FSNS( XML_cx, XML_chart ),
                FSNS(XML_xmlns, XML_cx), pFB->getNamespaceURL(OOX_NS(cx)),
                FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)),
                FSNS(XML_r, XML_id), sId );
    } else {
        pFS->singleElement(  FSNS( XML_c, XML_chart ),
                FSNS(XML_xmlns, XML_c), pFB->getNamespaceURL(OOX_NS(dmlChart)),
                FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)),
                FSNS(XML_r, XML_id), sId );
    }

    pFS->endElement( FSNS( XML_a, XML_graphicData ) );
    pFS->endElement( FSNS( XML_a, XML_graphic ) );
    pFS->endElementNS( mnXmlNamespace, XML_graphicFrame );

    if (bIsChartex) {
        // Do the AlternateContent fallback path
        pFS->endElementNS(XML_mc, XML_Choice);
        pFS->startElementNS(XML_mc, XML_Fallback);
        pFS->startElementNS(XML_xdr, XML_sp, XML_macro, "", XML_textlink, "");
        pFS->startElementNS(XML_xdr, XML_nvSpPr);
        pFS->singleElementNS(XML_xdr, XML_cNvPr, XML_id, "0", XML_name, "");
        pFS->startElementNS(XML_xdr, XML_cNvSpPr);
        pFS->singleElementNS(XML_a, XML_spLocks, XML_noTextEdit, "1");
        pFS->endElementNS(XML_xdr, XML_cNvSpPr);
        pFS->endElementNS(XML_xdr, XML_nvSpPr);
        pFS->startElementNS(XML_xdr, XML_spPr);
        pFS->startElementNS(XML_a, XML_xfrm);
        pFS->singleElementNS(XML_a, XML_off, XML_x, "6600825", XML_y, "2533650");
        pFS->singleElementNS(XML_a, XML_ext, XML_cx, "4572000", XML_cy, "2743200");
        pFS->endElementNS(XML_a, XML_xfrm);
        pFS->startElementNS(XML_a, XML_prstGeom, XML_prst, "rect");
        pFS->singleElementNS(XML_a, XML_avLst);
        pFS->endElementNS(XML_a, XML_prstGeom);
        pFS->startElementNS(XML_a, XML_solidFill);
        pFS->singleElementNS(XML_a, XML_prstClr, XML_val, "white");
        pFS->endElementNS(XML_a, XML_solidFill);
        pFS->startElementNS(XML_a, XML_ln, XML_w, "1");
        pFS->startElementNS(XML_a, XML_solidFill);
        pFS->singleElementNS(XML_a, XML_prstClr, XML_val, "green");
        pFS->endElementNS(XML_a, XML_solidFill);
        pFS->endElementNS(XML_a, XML_ln);
        pFS->endElementNS(XML_xdr, XML_spPr);
        pFS->startElementNS(XML_xdr, XML_txBody);
        pFS->singleElementNS(XML_a, XML_bodyPr, XML_vertOverflow, "clip", XML_horzOverflow, "clip");
        pFS->singleElementNS(XML_a, XML_lstStyle);
        pFS->startElementNS(XML_a, XML_p);
        pFS->startElementNS(XML_a, XML_r);
        pFS->singleElementNS(XML_a, XML_rPr, XML_sz, "1100");
        pFS->startElementNS(XML_a, XML_t);

        const std::string_view sErrTxt("This chart isn't available in your version of Excel.\n\n"
            "Editing this shape or saving this workbook into a different file format will permanently break the chart.");
        pFS->writeEscaped( sErrTxt );

        pFS->endElementNS(XML_a, XML_t);
        pFS->endElementNS(XML_a, XML_r);
        pFS->endElementNS(XML_a, XML_p);
        pFS->endElementNS(XML_xdr, XML_txBody);
        pFS->endElementNS(XML_xdr, XML_sp);

        pFS->endElementNS(XML_mc, XML_Fallback);
        pFS->endElementNS(XML_mc, XML_AlternateContent);
    }

    SetFS( pChart );
    ExportContent();

    if (bIsChartex) {
        SetFS( pChart );
        sRelativePath ="";

        FSHelperPtr pChartFS = GetFS();

        // output style and colorstyle files

        // first style
        static constexpr char sStyleFnamePrefix[] = "style";
        OUStringBuffer sFullStreamBuf;
        sFullStreamBuf.appendAscii(sFullPath);
        sFullStreamBuf = sFullStreamBuf + sStyleFnamePrefix + OUString::number(nChartCount) + ".xml";
        sFullStream = sFullStreamBuf.makeStringAndClear();
        OUStringBuffer sRelativeStreamBuf;
        sRelativeStreamBuf.appendAscii(sRelativePath);
        sRelativeStreamBuf = sRelativeStreamBuf + sStyleFnamePrefix + OUString::number(nChartCount) + ".xml";
        sRelativeStream = sRelativeStreamBuf.makeStringAndClear();

        FSHelperPtr pStyle = CreateOutputStream(
                sFullStream,
                sRelativeStream,
                pChartFS->getOutputStream(),
                u"application/vnd.ms-office.chartstyle+xml"_ustr,
                oox::getRelationship(Relationship::CHARTSTYLE),
                &sId,
                true /* for some reason this doesn't have a header line */);

        SetFS( pStyle );
        pFS = GetFS();

        pFS->startElement(FSNS(XML_cs, XML_chartStyle),
                FSNS( XML_xmlns, XML_cs ), pFB->getNamespaceURL(OOX_NS(cs)),
                FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
                XML_id, "419" /* no idea what this number is supposed to be */);

        outputStyleEntry(pFS, XML_axisTitle);;
        outputStyleEntry(pFS, XML_categoryAxis);
        outputChartAreaStyleEntry(pFS);
        outputStyleEntry(pFS, XML_dataLabel);
        outputDataPointStyleEntry(pFS);
        outputStyleEntry(pFS, XML_dataPoint3D);
        outputStyleEntry(pFS, XML_dataPointLine);
        outputStyleEntry(pFS, XML_dataPointMarker);
        outputStyleEntry(pFS, XML_dataPointWireframe);
        outputStyleEntry(pFS, XML_dataTable);
        outputStyleEntry(pFS, XML_downBar);
        outputStyleEntry(pFS, XML_dropLine);
        outputStyleEntry(pFS, XML_errorBar);
        outputStyleEntry(pFS, XML_floor);
        outputStyleEntry(pFS, XML_gridlineMajor);
        outputStyleEntry(pFS, XML_gridlineMinor);
        outputStyleEntry(pFS, XML_hiLoLine);
        outputStyleEntry(pFS, XML_leaderLine);
        outputStyleEntry(pFS, XML_legend);
        outputStyleEntry(pFS, XML_plotArea);
        outputStyleEntry(pFS, XML_plotArea3D);
        outputStyleEntry(pFS, XML_seriesAxis);
        outputStyleEntry(pFS, XML_seriesLine);
        outputStyleEntry(pFS, XML_title);
        outputStyleEntry(pFS, XML_trendline);
        outputStyleEntry(pFS, XML_trendlineLabel);
        outputStyleEntry(pFS, XML_upBar);
        outputStyleEntry(pFS, XML_valueAxis);
        outputStyleEntry(pFS, XML_wall);

        pFS->endElement(FSNS(XML_cs, XML_chartStyle));

        pStyle->endDocument();

        // now colorstyle
        static constexpr char sColorFnamePrefix[] = "colors";
        sFullStreamBuf = OUStringBuffer();
        sFullStreamBuf.appendAscii(sFullPath);
        sFullStreamBuf = sFullStreamBuf + sColorFnamePrefix + OUString::number(nChartCount) + ".xml";
        sFullStream = sFullStreamBuf.makeStringAndClear();
        sRelativeStreamBuf = OUStringBuffer();
        sRelativeStreamBuf.appendAscii(sRelativePath);
        sRelativeStreamBuf = sRelativeStreamBuf + sColorFnamePrefix + OUString::number(nChartCount) + ".xml";
        sRelativeStream = sRelativeStreamBuf.makeStringAndClear();

        FSHelperPtr pColorStyle = CreateOutputStream(
                sFullStream,
                sRelativeStream,
                pChartFS->getOutputStream(),
                u"application/vnd.ms-office.chartcolorstyle+xml"_ustr,
                oox::getRelationship(Relationship::CHARTCOLORSTYLE),
                &sId,
                true /* also no header line */);

        SetFS( pColorStyle );
        pFS = GetFS();

        pFS->startElement(FSNS(XML_cs, XML_colorStyle),
                FSNS( XML_xmlns, XML_cs ), pFB->getNamespaceURL(OOX_NS(cs)),
                FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
                XML_meth, "cycle",
                XML_id, "10" /* no idea what this number is supposed to be */);

        pFS->singleElement(FSNS(XML_a, XML_schemeClr),
                XML_val, "accent1");

        pFS->endElement(FSNS(XML_cs, XML_colorStyle));

        pColorStyle->endDocument();
    }

    pChart->endDocument();
}

void ChartExport::InitRangeSegmentationProperties( const Reference< chart2::XChartDocument > & xChartDoc )
{
    if( !xChartDoc.is())
        return;

    try
    {
        Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
        OSL_ENSURE( xDataProvider.is(), "No DataProvider" );
        if( xDataProvider.is())
        {
            mbHasCategoryLabels = lcl_hasCategoryLabels( xChartDoc );
        }
    }
    catchconst uno::Exception & )
    {
        DBG_UNHANDLED_EXCEPTION("oox");
    }
}

void ChartExport::ExportContent()
{
    Reference< chart2::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
    OSL_ASSERT( xChartDoc.is() );
    if( !xChartDoc.is() )
        return;
    InitRangeSegmentationProperties( xChartDoc );

    const bool bIsChartex = isChartexNotChartNS();
    ExportContent_( bIsChartex );
}

void ChartExport::ExportContent_( bool bIsChartex )
{
    Reference< css::chart::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
    if( xChartDoc.is())
    {
        // determine if data comes from the outside
        bool bIncludeTable = true;

        Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
        if( xNewDoc.is())
        {
            // check if we have own data.  If so we must not export the complete
            // range string, as this is our only indicator for having own or
            // external data. @todo: fix this in the file format!
            Reference< lang::XServiceInfo > xDPServiceInfo( xNewDoc->getDataProvider(), uno::UNO_QUERY );
            if( ! (xDPServiceInfo.is() && xDPServiceInfo->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" ))
            {
                bIncludeTable = false;
            }
        }
        exportChartSpace( xChartDoc, bIncludeTable, bIsChartex );
    }
    else
    {
        OSL_FAIL( "Couldn't export chart due to wrong XModel" );
    }
}

void ChartExport::exportChartSpace( const Reference< css::chart::XChartDocument >& xChartDoc,
                                    bool bIncludeTable,
                                    bool bIsChartex)
{
    FSHelperPtr pFS = GetFS();
    XmlFilterBase* pFB = GetFB();

    const sal_Int32 nChartNS = bIsChartex ? XML_cx : XML_c;

    if (bIsChartex) {
        pFS->startElement( FSNS( nChartNS, XML_chartSpace ),
                FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
                FSNS( XML_xmlns, XML_r ), pFB->getNamespaceURL(OOX_NS(officeRel)),
                FSNS( XML_xmlns, XML_cx ), pFB->getNamespaceURL(OOX_NS(cx)));
    } else {
        pFS->startElement( FSNS( nChartNS, XML_chartSpace ),
                FSNS( XML_xmlns, XML_c ), pFB->getNamespaceURL(OOX_NS(dmlChart)),
                FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
                FSNS( XML_xmlns, XML_r ), pFB->getNamespaceURL(OOX_NS(officeRel)));
    }

    if( !bIncludeTable )
    {
        // TODO:external data
    }
    else
    {
        Reference< XPropertySet > xPropSet(xChartDoc, UNO_QUERY);
        Any aNullDate = xPropSet->getPropertyValue("NullDate");
        util::DateTime aDate;
        if ((aNullDate >>= aDate) && (aDate.Year == 1904 && aDate.Month == 1 && aDate.Day == 1))
        {
            pFS->singleElement(FSNS(XML_c, XML_date1904), XML_val, "1");
        }
        else
        {
            pFS->singleElement(FSNS(XML_c, XML_date1904), XML_val, "0");
        }
    }

    // TODO: get the correct editing language
    if (bIsChartex) {
        // chartData
        pFS->startElement(FSNS(XML_cx, XML_chartData));

        exportExternalData(xChartDoc, true);
        exportData(xChartDoc, true);

        pFS->endElement(FSNS(XML_cx, XML_chartData));
    } else {
        pFS->singleElement(FSNS(XML_c, XML_lang), XML_val, "en-US");

        pFS->singleElement(FSNS(XML_c, XML_roundedCorners), XML_val, "0");
    }

    // style
    if (!bIsChartex) {
        mxDiagram.set( xChartDoc->getDiagram() );
        Reference< XPropertySet > xPropSet(mxDiagram, uno::UNO_QUERY);
        if (GetProperty(xPropSet, u"StyleIndex"_ustr)) {
            sal_Int32 nStyleIdx = -1;
            mAny >>= nStyleIdx;
            assert(nStyleIdx >= 0);
            pFS->singleElement(FSNS(XML_c, XML_style), XML_val,
                    OUString::number(nStyleIdx));
        }
    }

    //XML_chart
    exportChart(xChartDoc, bIsChartex);

    // TODO: printSettings
    // TODO: text properties
    Reference< XPropertySet > xPropSet = xChartDoc->getArea();
    if( xPropSet.is() )
        exportShapeProps( xPropSet, bIsChartex );

    // TODO for chartex
    if (!bIsChartex) {
        //XML_externalData
        exportExternalData(xChartDoc, false);
    }

    // export additional shapes in chart
    if (!bIsChartex) {
        exportAdditionalShapes(xChartDoc);
    }

    pFS->endElement( FSNS( nChartNS, XML_chartSpace ) );
}

void ChartExport::exportData( [[maybe_unused]] const Reference< css::chart::XChartDocument >& xChartDoc,
        bool bIsChartex)
{
    if (bIsChartex) {
        FSHelperPtr pFS = GetFS();

        // Not sure if the data id is always 0. However, it seems it may need to
        // agree with the id in exportSeries(). See DATA_ID_COMMENT
        pFS->startElement(FSNS(XML_cx, XML_data), XML_id, "0");
        // Just hard-coding this for now
        pFS->startElement(FSNS(XML_cx, XML_numDim), XML_type, "val");
        pFS->startElement(FSNS(XML_cx, XML_f));
        pFS->writeEscaped("_xlchart.v2.0");    // I have no idea what this
                                                // means or what it should be in
                                                // general
        pFS->endElement(FSNS(XML_cx, XML_f));
        pFS->endElement(FSNS(XML_cx, XML_numDim));
        pFS->endElement(FSNS(XML_cx, XML_data));
    }
}

void ChartExport::exportExternalData( const Reference< css::chart::XChartDocument >& ;xChartDoc,
        bool bIsChartex)
{
    if (bIsChartex) return// TODO!!
    // Embedded external data is grab bagged for docx file hence adding export part of
    // external data for docx files only.
    if(GetDocumentType() != DOCUMENT_DOCX)
        return;

    OUString externalDataPath;
    Reference< beans::XPropertySet > xDocPropSet( xChartDoc->getDiagram(), uno::UNO_QUERY );
    if( xDocPropSet.is())
    {
        try
        {
            Any aAny( xDocPropSet->getPropertyValue( u"ExternalData"_ustr ));
            aAny >>= externalDataPath;
        }
        catch( beans::UnknownPropertyException & )
        {
            SAL_WARN("oox""Required property not found in ChartDocument");
        }
    }
    if(externalDataPath.isEmpty())
        return;

    // Here adding external data entry to relationship.
    OUString relationPath = externalDataPath;
    // Converting absolute path to relative path.
    if( externalDataPath[ 0 ] != '.' && externalDataPath[ 1 ] != '.')
    {
        sal_Int32 nSepPos = externalDataPath.indexOf( '/', 0 );
        if( nSepPos > 0)
        {
            relationPath = relationPath.copy( nSepPos,  ::std::max< sal_Int32 >( externalDataPath.getLength(), 0 ) -  nSepPos );
            relationPath = ".." + relationPath;
        }
    }
    FSHelperPtr pFS = GetFS();
    OUString type = oox::getRelationship(Relationship::PACKAGE);
    if (relationPath.endsWith(".bin"))
        type = oox::getRelationship(Relationship::OLEOBJECT);

    OUString sRelId = GetFB()->addRelation(pFS->getOutputStream(),
                    type,
                    relationPath);
    pFS->singleElementNS(XML_c, XML_externalData, FSNS(XML_r, XML_id), sRelId);
}

void ChartExport::exportAdditionalShapes( const Reference< css::chart::XChartDocument >&&nbsp;xChartDoc )
{
    // Not used in chartex

    Reference< beans::XPropertySet > xDocPropSet(xChartDoc, uno::UNO_QUERY);
    if (!xDocPropSet.is())
        return;

    css::uno::Reference< css::drawing::XShapes > mxAdditionalShapes;
    // get a sequence of non-chart shapes
    try
    {
        Any aShapesAny = xDocPropSet->getPropertyValue(u"AdditionalShapes"_ustr);
        if( (aShapesAny >>= mxAdditionalShapes) && mxAdditionalShapes.is() )
        {
            OUString sId;
            const char* sFullPath = nullptr;
            const char* sRelativePath = nullptr;
            sal_Int32 nDrawing = getNewDrawingUniqueId();

            switch (GetDocumentType())
            {
                case DOCUMENT_DOCX:
                {
                    sFullPath = "word/drawings/drawing";
                    sRelativePath = "../drawings/drawing";
                    break;
                }
                case DOCUMENT_PPTX:
                {
                    sFullPath = "ppt/drawings/drawing";
                    sRelativePath = "../drawings/drawing";
                    break;
                }
                case DOCUMENT_XLSX:
                {
                    sFullPath = "xl/drawings/drawing";
                    sRelativePath = "../drawings/drawing";
                    break;
                }
                default:
                {
                    sFullPath = "drawings/drawing";
                    sRelativePath = "drawings/drawing";
                    break;
                }
            }
            OUString sFullStream = OUStringBuffer()
                .appendAscii(sFullPath)
                .append(OUString::number(nDrawing) + ".xml")
                .makeStringAndClear();
            OUString sRelativeStream = OUStringBuffer()
                .appendAscii(sRelativePath)
                .append(OUString::number(nDrawing) + ".xml")
                .makeStringAndClear();

            sax_fastparser::FSHelperPtr pDrawing = CreateOutputStream(
                sFullStream,
                sRelativeStream,
                GetFS()->getOutputStream(),
                u"application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml"_ustr,
                oox::getRelationship(Relationship::CHARTUSERSHAPES),
                &sId);

            GetFS()->singleElementNS(XML_c, XML_userShapes, FSNS(XML_r, XML_id), sId);

            XmlFilterBase* pFB = GetFB();
            pDrawing->startElement(FSNS(XML_c, XML_userShapes),
                FSNS(XML_xmlns, XML_cdr), pFB->getNamespaceURL(OOX_NS(dmlChartDr)),
                FSNS(XML_xmlns, XML_a), pFB->getNamespaceURL(OOX_NS(dml)),
                FSNS(XML_xmlns, XML_c), pFB->getNamespaceURL(OOX_NS(dmlChart)),
                FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)));

            const sal_Int32 nShapeCount(mxAdditionalShapes->getCount());
            for (sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++)
            {
                Reference< drawing::XShape > xShape;
                mxAdditionalShapes->getByIndex(nShapeId) >>= xShape;
                SAL_WARN_IF(!xShape.is(), "xmloff.chart""Shape without an XShape?");
                if (!xShape.is())
                    continue;

                // TODO: absSizeAnchor: we import both (absSizeAnchor and relSizeAnchor), but there is no essential difference between them.
                pDrawing->startElement(FSNS(XML_cdr, XML_relSizeAnchor));
                uno::Reference< beans::XPropertySet > xShapeProperties(xShape, uno::UNO_QUERY);
                if( xShapeProperties.is() )
                {
                    Reference<embed::XVisualObject> xVisObject(mxChartModel, uno::UNO_QUERY);
                    awt::Size aPageSize = xVisObject->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT);
                    WriteFromTo( xShape, aPageSize, pDrawing );

                    ShapeExport aExport(XML_cdr, pDrawing, nullptr, GetFB(), GetDocumentType(), nullptr, true);
                    aExport.WriteShape(xShape);
                }
                pDrawing->endElement(FSNS(XML_cdr, XML_relSizeAnchor));
            }
            pDrawing->endElement(FSNS(XML_c, XML_userShapes));
            pDrawing->endDocument();
        }
    }
    catch (const uno::Exception&)
    {
        TOOLS_INFO_EXCEPTION("xmloff.chart""AdditionalShapes not found");
    }
}

void ChartExport::exportChart( const Reference< css::chart::XChartDocument >& xChartDoc,
        bool bIsChartex)
{
    Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
    mxDiagram.set( xChartDoc->getDiagram() );
    if( xNewDoc.is()) {
        mxNewDiagram.set( xNewDoc->getFirstDiagram());
    }

    // get Properties of ChartDocument
    bool bHasMainTitle = false;
    bool bHasLegend = false;
    Reference< beans::XPropertySet > xDocPropSet( xChartDoc, uno::UNO_QUERY );
    if( xDocPropSet.is())
    {
        try
        {
            Any aAny( xDocPropSet->getPropertyValue(u"HasMainTitle"_ustr));
            aAny >>= bHasMainTitle;
            aAny = xDocPropSet->getPropertyValue(u"HasLegend"_ustr);
            aAny >>= bHasLegend;
        }
        catch( beans::UnknownPropertyException & )
        {
            SAL_WARN("oox""Required property not found in ChartDocument");
        }
    } // if( xDocPropSet.is())

    Sequence< uno::Reference< chart2::XFormattedString > > xFormattedSubTitle;
    Reference< beans::XPropertySet > xPropSubTitle( xChartDoc->getSubTitle(), UNO_QUERY );
    if( xPropSubTitle.is())
    {
        OUString aSubTitle;
        if ((xPropSubTitle->getPropertyValue(u"String"_ustr) >>= aSubTitle) && !aSubTitle.isEmpty())
            xPropSubTitle->getPropertyValue(u"FormattedStrings"_ustr) >>= xFormattedSubTitle;
    }

    // chart element
    FSHelperPtr pFS = GetFS();

    const sal_Int32 nChartNS = bIsChartex ? XML_cx : XML_c;
    pFS->startElement(FSNS(nChartNS, XML_chart));

    // titles
    if( bHasMainTitle )
    {
        exportTitle( xChartDoc->getTitle(), bIsChartex, xFormattedSubTitle);
        if (!bIsChartex) {
            pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
        }
    }
    else if( xFormattedSubTitle.hasElements() )
    {
        exportTitle( xChartDoc->getSubTitle(), bIsChartex );
        if (!bIsChartex) {
            pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
        }
    }
    else if (!bIsChartex) {
        pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "1");
    }

    InitPlotArea( );
    if( mbIs3DChart )
    {
        if (!bIsChartex) {
            exportView3D();

            // floor
            Reference< beans::XPropertySet > xFloor = mxNewDiagram->getFloor();
            if( xFloor.is() )
            {
                pFS->startElement(FSNS(XML_c, XML_floor));
                exportShapeProps( xFloor, false );
                pFS->endElement( FSNS( XML_c, XML_floor ) );
            }

            // LibreOffice doesn't distinguish between sideWall and backWall (both are using the same color).
            // It is controlled by the same Wall property.
            Reference< beans::XPropertySet > xWall = mxNewDiagram->getWall();
            if( xWall.is() )
            {
                // sideWall
                pFS->startElement(FSNS(XML_c, XML_sideWall));
                exportShapeProps( xWall, false );
                pFS->endElement( FSNS( XML_c, XML_sideWall ) );

                // backWall
                pFS->startElement(FSNS(XML_c, XML_backWall));
                exportShapeProps( xWall, false );
                pFS->endElement( FSNS( XML_c, XML_backWall ) );
            }
        }
    }
    // plot area
    exportPlotArea( xChartDoc, bIsChartex );
    // legend
    if( bHasLegend ) {
        exportLegend( xChartDoc, bIsChartex );
    }

    if (!bIsChartex) {
        uno::Reference<beans::XPropertySet> xDiagramPropSet(xChartDoc->getDiagram(), uno::UNO_QUERY);
        uno::Any aPlotVisOnly = xDiagramPropSet->getPropertyValue(u"IncludeHiddenCells"_ustr);
        bool bIncludeHiddenCells = false;
        aPlotVisOnly >>= bIncludeHiddenCells;
        pFS->singleElement(FSNS(XML_c, XML_plotVisOnly), XML_val, ToPsz10(!bIncludeHiddenCells));

        exportMissingValueTreatment(Reference<beans::XPropertySet>(mxDiagram, uno::UNO_QUERY));
    }

    pFS->endElement( FSNS( nChartNS, XML_chart ) );
}

void ChartExport::exportMissingValueTreatment(const uno::Reference<beans::XPropertySet>& xPropSet)
{
    if (!xPropSet.is())
        return;

    sal_Int32 nVal = 0;
    uno::Any aAny = xPropSet->getPropertyValue(u"MissingValueTreatment"_ustr);
    if (!(aAny >>= nVal))
        return;

    const char* pVal = nullptr;
    switch (nVal)
    {
        case cssc::MissingValueTreatment::LEAVE_GAP:
            pVal = "gap";
        break;
        case cssc::MissingValueTreatment::USE_ZERO:
            pVal = "zero";
        break;
        case cssc::MissingValueTreatment::CONTINUE:
            pVal = "span";
        break;
        default:
            SAL_WARN("oox""unknown MissingValueTreatment value");
        break;
    }

    FSHelperPtr pFS = GetFS();
    pFS->singleElement(FSNS(XML_c, XML_dispBlanksAs), XML_val, pVal);
}

void ChartExport::exportLegend( const Reference< css::chart::XChartDocument >& xChartDoc,
        bool bIsChartex)
{
    FSHelperPtr pFS = GetFS();

    Reference< beans::XPropertySet > xProp( xChartDoc->getLegend(), uno::UNO_QUERY );
    if( xProp.is() )
    {
        if (!bIsChartex) {
            pFS->startElement(FSNS(XML_c, XML_legend));
        }

        // position
        css::chart::ChartLegendPosition aLegendPos = css::chart::ChartLegendPosition_NONE;
        try
        {
            Any aAny( xProp->getPropertyValue( u"Alignment"_ustr ));
            aAny >>= aLegendPos;
        }
        catch( beans::UnknownPropertyException & )
        {
            SAL_WARN("oox""Property Align not found in ChartLegend");
        }

        const char* strPos = nullptr;
        switch( aLegendPos )
        {
            case css::chart::ChartLegendPosition_LEFT:
                strPos = "l";
                break;
            case css::chart::ChartLegendPosition_RIGHT:
                strPos = "r";
                break;
            case css::chart::ChartLegendPosition_TOP:
                strPos = "t";
                break;
            case css::chart::ChartLegendPosition_BOTTOM:
                strPos = "b";
                break;
            case css::chart::ChartLegendPosition_NONE:
            case css::chart::ChartLegendPosition::ChartLegendPosition_MAKE_FIXED_SIZE:
                // nothing
                break;
        }

        if (!bIsChartex) {
            if( strPos != nullptr )
            {
                pFS->singleElement(FSNS(XML_c, XML_legendPos), XML_val, strPos);
            }

            // legendEntry
            Reference<chart2::XCoordinateSystemContainer> xCooSysContainer(mxNewDiagram, UNO_QUERY_THROW);
            const Sequence<Reference<chart2::XCoordinateSystem>> xCooSysSequence(xCooSysContainer->getCoordinateSystems());

            sal_Int32 nIndex = 0;
            bool bShowLegendEntry;
            for (const auto& rCooSys : xCooSysSequence)
            {
                PropertySet aCooSysProp(rCooSys);
                bool bSwapXAndY = aCooSysProp.getBoolProperty(PROP_SwapXAndYAxis);

                Reference<chart2::XChartTypeContainer> xChartTypeContainer(rCooSys, UNO_QUERY_THROW);
                const Sequence<Reference<chart2::XChartType>> xChartTypeSequence(xChartTypeContainer->getChartTypes());
                if (!xChartTypeSequence.hasElements())
                    continue;

                for (const auto& rCT : xChartTypeSequence)
                {
                    Reference<chart2::XDataSeriesContainer> xDSCont(rCT, UNO_QUERY);
                    if (!xDSCont.is())
                        continue;

                    OUString aChartType(rCT->getChartType());
                    bool bIsPie = lcl_getChartType(aChartType) == chart::TYPEID_PIE;
                    if (bIsPie)
                    {
                        PropertySet xChartTypeProp(rCT);
                        bIsPie = !xChartTypeProp.getBoolProperty(PROP_UseRings);
                    }
                    const Sequence<Reference<chart2::XDataSeries>> aDataSeriesSeq = xDSCont->getDataSeries();
                    if (bSwapXAndY)
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=97 G=96

¤ Dauer der Verarbeitung: 0.18 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.