Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/oox/source/export/chartexport.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <oox/token/namespaces.hxx>
21
#include <oox/token/properties.hxx>
22
#include <oox/token/tokens.hxx>
23
#include <oox/core/xmlfilterbase.hxx>
24
#include <oox/export/chartexport.hxx>
25
#include <oox/export/shapes.hxx>
26
#include <oox/token/relationship.hxx>
27
#include <oox/export/utils.hxx>
28
#include <drawingml/chart/typegroupconverter.hxx>
29
#include <basegfx/utils/gradienttools.hxx>
30
#include <docmodel/uno/UnoGradientTools.hxx>
31
32
#include <cstdio>
33
#include <limits>
34
35
#include <tools/UnitConversion.hxx>
36
37
#include <com/sun/star/awt/Gradient2.hpp>
38
#include <com/sun/star/chart/XChartDocument.hpp>
39
#include <com/sun/star/chart/ChartLegendPosition.hpp>
40
#include <com/sun/star/chart/XTwoAxisXSupplier.hpp>
41
#include <com/sun/star/chart/XTwoAxisYSupplier.hpp>
42
#include <com/sun/star/chart/XAxisZSupplier.hpp>
43
#include <com/sun/star/chart/ChartDataRowSource.hpp>
44
#include <com/sun/star/chart/X3DDisplay.hpp>
45
#include <com/sun/star/chart/XStatisticDisplay.hpp>
46
#include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp>
47
#include <com/sun/star/chart/ChartSymbolType.hpp>
48
#include <com/sun/star/chart/ChartAxisMarks.hpp>
49
#include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
50
#include <com/sun/star/chart/ChartAxisPosition.hpp>
51
#include <com/sun/star/chart/ChartSolidType.hpp>
52
#include <com/sun/star/chart/DataLabelPlacement.hpp>
53
#include <com/sun/star/chart/ErrorBarStyle.hpp>
54
#include <com/sun/star/chart/MissingValueTreatment.hpp>
55
#include <com/sun/star/chart/XDiagramPositioning.hpp>
56
#include <com/sun/star/chart/TimeIncrement.hpp>
57
#include <com/sun/star/chart/TimeInterval.hpp>
58
#include <com/sun/star/chart/TimeUnit.hpp>
59
60
#include <com/sun/star/chart2/RelativePosition.hpp>
61
#include <com/sun/star/chart2/RelativeSize.hpp>
62
#include <com/sun/star/chart2/XChartDocument.hpp>
63
#include <com/sun/star/chart2/XDiagram.hpp>
64
#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
65
#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
66
#include <com/sun/star/chart2/XChartTypeContainer.hpp>
67
#include <com/sun/star/chart2/XDataSeriesContainer.hpp>
68
#include <com/sun/star/chart2/DataPointLabel.hpp>
69
#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
70
#include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
71
#include <com/sun/star/chart2/PieChartSubType.hpp>
72
#include <com/sun/star/chart2/Symbol.hpp>
73
#include <com/sun/star/chart2/data/XDataSource.hpp>
74
#include <com/sun/star/chart2/data/XDataProvider.hpp>
75
#include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
76
#include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
77
#include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
78
#include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
79
#include <com/sun/star/chart2/AxisType.hpp>
80
81
#include <com/sun/star/beans/XPropertySet.hpp>
82
#include <com/sun/star/container/XNameAccess.hpp>
83
#include <com/sun/star/drawing/XShape.hpp>
84
#include <com/sun/star/drawing/XShapes.hpp>
85
#include <com/sun/star/drawing/FillStyle.hpp>
86
#include <com/sun/star/drawing/LineStyle.hpp>
87
#include <com/sun/star/awt/XBitmap.hpp>
88
#include <com/sun/star/io/XSeekable.hpp>
89
#include <com/sun/star/io/XStreamListener.hpp>
90
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
91
#include <com/sun/star/lang/XServiceName.hpp>
92
93
#include <com/sun/star/table/CellAddress.hpp>
94
#include <com/sun/star/sheet/XFormulaParser.hpp>
95
#include <com/sun/star/sheet/FormulaToken.hpp>
96
#include <com/sun/star/sheet/AddressConvention.hpp>
97
98
#include <com/sun/star/container/XNamed.hpp>
99
#include <com/sun/star/embed/XVisualObject.hpp>
100
#include <com/sun/star/embed/Aspects.hpp>
101
102
#include <comphelper/processfactory.hxx>
103
#include <comphelper/random.hxx>
104
#include <utility>
105
#include <xmloff/SchXMLSeriesHelper.hxx>
106
#include "ColorPropertySet.hxx"
107
108
#include <svl/numformat.hxx>
109
#include <svl/numuno.hxx>
110
#include <comphelper/diagnose_ex.hxx>
111
#include <sal/log.hxx>
112
113
#include <set>
114
#include <unordered_set>
115
116
#include <frozen/bits/defines.h>
117
#include <frozen/bits/elsa_std.h>
118
#include <frozen/unordered_map.h>
119
120
#include <o3tl/temporary.hxx>
121
#include <o3tl/sorted_vector.hxx>
122
#include <o3tl/enumrange.hxx>
123
124
#include <docmodel/styles/ChartStyle.hxx>
125
#include <docmodel/uno/UnoChartStyle.hxx>
126
#include <docmodel/styles/ChartColorStyle.hxx>
127
#include <docmodel/uno/UnoChartColorStyle.hxx>
128
129
#include <oox/export/ThemeExport.hxx>
130
131
using namespace css;
132
using namespace css::uno;
133
using namespace css::drawing;
134
using namespace ::oox::core;
135
using css::beans::PropertyValue;
136
using css::beans::XPropertySet;
137
using css::container::XNamed;
138
using css::table::CellAddress;
139
using css::sheet::XFormulaParser;
140
using ::oox::core::XmlFilterBase;
141
using ::sax_fastparser::FSHelperPtr;
142
143
namespace cssc = css::chart;
144
145
namespace oox::drawingml {
146
147
namespace {
148
149
bool isPrimaryAxes(sal_Int32 nIndex)
150
0
{
151
0
    assert(nIndex == 0 || nIndex == 1);
152
0
    return nIndex != 1;
153
0
}
154
155
class lcl_MatchesRole
156
{
157
public:
158
    explicit lcl_MatchesRole( OUString aRole ) :
159
0
            m_aRole(std::move( aRole ))
160
0
    {}
161
162
    bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const
163
0
    {
164
0
        if( !xSeq.is() )
165
0
            return  false;
166
0
        Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY );
167
0
        OUString aRole;
168
169
0
        return ( xProp.is() &&
170
0
                 (xProp->getPropertyValue( u"Role"_ustr ) >>= aRole ) &&
171
0
                 m_aRole == aRole );
172
0
    }
173
174
private:
175
    OUString m_aRole;
176
};
177
178
std::vector<Sequence<Reference<chart2::XDataSeries> > > splitDataSeriesByAxis(const Reference< chart2::XChartType >& xChartType)
179
0
{
180
0
    std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitSeries;
181
0
    std::map<sal_Int32, size_t> aMapAxisToIndex;
182
183
0
    Reference< chart2::XDataSeriesContainer > xDSCnt(xChartType, uno::UNO_QUERY);
184
0
    if (xDSCnt.is())
185
0
    {
186
0
        sal_Int32 nAxisIndexOfFirstSeries = -1;
187
0
        const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq(xDSCnt->getDataSeries());
188
0
        for (const uno::Reference<chart2::XDataSeries>& xSeries : aSeriesSeq)
189
0
        {
190
0
            Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY);
191
0
            if (!xPropSet.is())
192
0
                continue;
193
194
0
            sal_Int32 nAxisIndex = -1;
195
0
            uno::Any aAny = xPropSet->getPropertyValue(u"AttachedAxisIndex"_ustr);
196
0
            aAny >>= nAxisIndex;
197
0
            size_t nVectorPos = 0;
198
0
            if (nAxisIndexOfFirstSeries == -1)
199
0
            {
200
0
                nAxisIndexOfFirstSeries = nAxisIndex;
201
0
            }
202
203
0
            auto it = aMapAxisToIndex.find(nAxisIndex);
204
0
            if (it == aMapAxisToIndex.end())
205
0
            {
206
0
                aSplitSeries.emplace_back();
207
0
                nVectorPos = aSplitSeries.size() - 1;
208
0
                aMapAxisToIndex.insert(std::pair<sal_Int32, size_t>(nAxisIndex, nVectorPos));
209
0
            }
210
0
            else
211
0
            {
212
0
                nVectorPos = it->second;
213
0
            }
214
215
0
            uno::Sequence<Reference<chart2::XDataSeries> >& rAxisSeriesSeq = aSplitSeries[nVectorPos];
216
0
            sal_Int32 nLength = rAxisSeriesSeq.getLength();
217
0
            rAxisSeriesSeq.realloc(nLength + 1);
218
0
            rAxisSeriesSeq.getArray()[nLength] = xSeries;
219
0
        }
220
        // if the first series attached to secondary axis, then export those series first, which are attached to primary axis
221
        // also the MS Office export every time in this order
222
0
        if (aSplitSeries.size() > 1 && nAxisIndexOfFirstSeries == 1)
223
0
        {
224
0
            std::swap(aSplitSeries[0], aSplitSeries[1]);
225
0
        }
226
0
    }
227
228
0
    return aSplitSeries;
229
0
}
230
231
}   // unnamed namespace
232
233
static Reference< chart2::data::XLabeledDataSequence > lcl_getCategories(
234
        const Reference< chart2::XDiagram > & xDiagram, bool *pbHasDateCategories )
235
0
{
236
0
    *pbHasDateCategories = false;
237
0
    Reference< chart2::data::XLabeledDataSequence >  xResult;
238
0
    try
239
0
    {
240
0
        Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
241
0
            xDiagram, uno::UNO_QUERY_THROW );
242
0
        const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
243
0
            xCooSysCnt->getCoordinateSystems());
244
0
        for( const auto& xCooSys : aCooSysSeq )
245
0
        {
246
0
            OSL_ASSERT( xCooSys.is());
247
0
            for( sal_Int32 nN = xCooSys->getDimension(); nN--; )
248
0
            {
249
0
                const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nN);
250
0
                for(sal_Int32 nI=0; nI<=nMaxAxisIndex; ++nI)
251
0
                {
252
0
                    Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( nN, nI );
253
0
                    OSL_ASSERT( xAxis.is());
254
0
                    if( xAxis.is())
255
0
                    {
256
0
                        chart2::ScaleData aScaleData = xAxis->getScaleData();
257
0
                        if( aScaleData.Categories.is())
258
0
                        {
259
0
                            *pbHasDateCategories = aScaleData.AxisType == chart2::AxisType::DATE;
260
0
                            xResult.set( aScaleData.Categories );
261
0
                            break;
262
0
                        }
263
0
                    }
264
0
                }
265
0
            }
266
0
        }
267
0
    }
268
0
    catch( const uno::Exception & )
269
0
    {
270
0
        DBG_UNHANDLED_EXCEPTION("oox");
271
0
    }
272
273
0
    return xResult;
274
0
}
275
276
static Reference< chart2::data::XLabeledDataSequence >
277
    lcl_getDataSequenceByRole(
278
        const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aLabeledSeq,
279
        const OUString & rRole )
280
0
{
281
0
    auto* pMatch = std::find_if(aLabeledSeq.begin(), aLabeledSeq.end(), lcl_MatchesRole(rRole));
282
0
    if (pMatch != aLabeledSeq.end())
283
0
        return *pMatch;
284
285
0
    return {};
286
0
}
287
288
static bool lcl_hasCategoryLabels( const Reference< chart2::XChartDocument >& xChartDoc )
289
0
{
290
    //categories are always the first sequence
291
0
    Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram());
292
0
    bool bDateCategories;
293
0
    Reference< chart2::data::XLabeledDataSequence > xCategories(
294
0
            lcl_getCategories( xDiagram, &bDateCategories ) );
295
0
    return xCategories.is();
296
0
}
297
298
static bool lcl_isCategoryAxisShifted( const Reference< chart2::XDiagram >& xDiagram )
299
0
{
300
0
    bool bCategoryPositionShifted = false;
301
0
    try
302
0
    {
303
0
        Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
304
0
            xDiagram, uno::UNO_QUERY_THROW);
305
0
        const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
306
0
            xCooSysCnt->getCoordinateSystems());
307
0
        for (const auto& xCooSys : aCooSysSeq)
308
0
        {
309
0
            OSL_ASSERT(xCooSys.is());
310
0
            if( 0 < xCooSys->getDimension() && 0 <= xCooSys->getMaximumAxisIndexByDimension(0) )
311
0
            {
312
0
                Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(0, 0);
313
0
                OSL_ASSERT(xAxis.is());
314
0
                if (xAxis.is())
315
0
                {
316
0
                    chart2::ScaleData aScaleData = xAxis->getScaleData();
317
0
                    bCategoryPositionShifted = aScaleData.ShiftedCategoryPosition;
318
0
                    break;
319
0
                }
320
0
            }
321
0
        }
322
0
    }
323
0
    catch (const uno::Exception&)
324
0
    {
325
0
        DBG_UNHANDLED_EXCEPTION("oox");
326
0
    }
327
328
0
    return bCategoryPositionShifted;
329
0
}
330
331
static sal_Int32 lcl_getCategoryAxisType( const Reference< chart2::XDiagram >& xDiagram, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
332
0
{
333
0
    sal_Int32 nAxisType = -1;
334
0
    try
335
0
    {
336
0
        Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
337
0
            xDiagram, uno::UNO_QUERY_THROW);
338
0
        const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
339
0
            xCooSysCnt->getCoordinateSystems());
340
0
        for( const auto& xCooSys : aCooSysSeq )
341
0
        {
342
0
            OSL_ASSERT(xCooSys.is());
343
0
            if( nDimensionIndex < xCooSys->getDimension() && nAxisIndex <= xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex) )
344
0
            {
345
0
                Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(nDimensionIndex, nAxisIndex);
346
0
                OSL_ASSERT(xAxis.is());
347
0
                if( xAxis.is() )
348
0
                {
349
0
                    chart2::ScaleData aScaleData = xAxis->getScaleData();
350
0
                    nAxisType = aScaleData.AxisType;
351
0
                    break;
352
0
                }
353
0
            }
354
0
        }
355
0
    }
356
0
    catch (const uno::Exception&)
357
0
    {
358
0
        DBG_UNHANDLED_EXCEPTION("oox");
359
0
    }
360
361
0
    return nAxisType;
362
0
}
363
364
static OUString lclGetTimeUnitToken( sal_Int32 nTimeUnit )
365
0
{
366
0
    switch( nTimeUnit )
367
0
    {
368
0
        case cssc::TimeUnit::DAY:      return u"days"_ustr;
369
0
        case cssc::TimeUnit::MONTH:    return u"months"_ustr;
370
0
        case cssc::TimeUnit::YEAR:     return u"years"_ustr;
371
0
        default:                       OSL_ENSURE(false, "lclGetTimeUnitToken - unexpected time unit");
372
0
    }
373
0
    return u"days"_ustr;
374
0
}
375
376
static cssc::TimeIncrement lcl_getDateTimeIncrement( const Reference< chart2::XDiagram >& xDiagram, sal_Int32 nAxisIndex )
377
0
{
378
0
    cssc::TimeIncrement aTimeIncrement;
379
0
    try
380
0
    {
381
0
        Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
382
0
            xDiagram, uno::UNO_QUERY_THROW);
383
0
        const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
384
0
            xCooSysCnt->getCoordinateSystems());
385
0
        for( const auto& xCooSys : aCooSysSeq )
386
0
        {
387
0
            OSL_ASSERT(xCooSys.is());
388
0
            if( 0 < xCooSys->getDimension() && nAxisIndex <= xCooSys->getMaximumAxisIndexByDimension(0) )
389
0
            {
390
0
                Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(0, nAxisIndex);
391
0
                OSL_ASSERT(xAxis.is());
392
0
                if( xAxis.is() )
393
0
                {
394
0
                    chart2::ScaleData aScaleData = xAxis->getScaleData();
395
0
                    aTimeIncrement = aScaleData.TimeIncrement;
396
0
                    break;
397
0
                }
398
0
            }
399
0
        }
400
0
    }
401
0
    catch (const uno::Exception&)
402
0
    {
403
0
        DBG_UNHANDLED_EXCEPTION("oox");
404
0
    }
405
406
0
    return aTimeIncrement;
407
0
}
408
409
static bool lcl_isSeriesAttachedToFirstAxis(
410
    const Reference< chart2::XDataSeries > & xDataSeries )
411
0
{
412
0
    bool bResult=true;
413
414
0
    try
415
0
    {
416
0
        sal_Int32 nAxisIndex = 0;
417
0
        Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW );
418
0
        xProp->getPropertyValue(u"AttachedAxisIndex"_ustr) >>= nAxisIndex;
419
0
        bResult = (0==nAxisIndex);
420
0
    }
421
0
    catch( const uno::Exception & )
422
0
    {
423
0
        DBG_UNHANDLED_EXCEPTION("oox");
424
0
    }
425
426
0
    return bResult;
427
0
}
428
429
static OUString lcl_flattenStringSequence( const Sequence< OUString > & rSequence )
430
0
{
431
0
    OUStringBuffer aResult;
432
0
    bool bPrecedeWithSpace = false;
433
0
    for( const auto& rString : rSequence )
434
0
    {
435
0
        if( !rString.isEmpty())
436
0
        {
437
0
            if( bPrecedeWithSpace )
438
0
                aResult.append( ' ' );
439
0
            aResult.append( rString );
440
0
            bPrecedeWithSpace = true;
441
0
        }
442
0
    }
443
0
    return aResult.makeStringAndClear();
444
0
}
445
446
static void lcl_writeChartexString(const FSHelperPtr& pFS, std::u16string_view sOut)
447
0
{
448
0
    pFS->startElement(FSNS(XML_cx, XML_tx));
449
    // cell range doesn't seem to be supported in chartex?
450
    // TODO: also handle <cx:rich>
451
0
    pFS->startElement(FSNS(XML_cx, XML_txData));
452
    // TODO: also handle <cx:f> <cx:v>
453
0
    pFS->startElement(FSNS(XML_cx, XML_v));
454
0
    pFS->writeEscaped(sOut);
455
0
    pFS->endElement( FSNS( XML_cx, XML_v ) );
456
0
    pFS->endElement( FSNS( XML_cx, XML_txData ) );
457
0
    pFS->endElement( FSNS( XML_cx, XML_tx ) );
458
0
}
459
460
static Sequence< OUString > lcl_getLabelSequence( const Reference< chart2::data::XDataSequence > & xLabelSeq )
461
0
{
462
0
    Sequence< OUString > aLabels;
463
464
0
    uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xLabelSeq, uno::UNO_QUERY );
465
0
    if( xTextualDataSequence.is())
466
0
    {
467
0
        aLabels = xTextualDataSequence->getTextualData();
468
0
    }
469
0
    else if( xLabelSeq.is())
470
0
    {
471
0
        const Sequence< uno::Any > aAnies( xLabelSeq->getData());
472
0
        aLabels.realloc( aAnies.getLength());
473
0
        auto pLabels = aLabels.getArray();
474
0
        for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
475
0
            aAnies[i] >>= pLabels[i];
476
0
    }
477
478
0
    return aLabels;
479
0
}
480
481
static void lcl_fillCategoriesIntoStringVector(
482
    const Reference< chart2::data::XDataSequence > & xCategories,
483
    ::std::vector< OUString > & rOutCategories )
484
0
{
485
0
    OSL_ASSERT( xCategories.is());
486
0
    if( !xCategories.is())
487
0
        return;
488
0
    Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xCategories, uno::UNO_QUERY );
489
0
    if( xTextualDataSequence.is())
490
0
    {
491
0
        rOutCategories.clear();
492
0
        const Sequence< OUString > aTextData( xTextualDataSequence->getTextualData());
493
0
        rOutCategories.insert( rOutCategories.end(), aTextData.begin(), aTextData.end() );
494
0
    }
495
0
    else
496
0
    {
497
0
        Sequence< uno::Any > aAnies( xCategories->getData());
498
0
        rOutCategories.resize( aAnies.getLength());
499
0
        for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
500
0
            aAnies[i] >>= rOutCategories[i];
501
0
    }
502
0
}
503
504
static ::std::vector< double > lcl_getAllValuesFromSequence( const Reference< chart2::data::XDataSequence > & xSeq )
505
0
{
506
0
    ::std::vector< double > aResult;
507
508
0
    Reference< chart2::data::XNumericalDataSequence > xNumSeq( xSeq, uno::UNO_QUERY );
509
0
    if( xNumSeq.is())
510
0
    {
511
0
        const Sequence< double > aValues( xNumSeq->getNumericalData());
512
0
        aResult.insert( aResult.end(), aValues.begin(), aValues.end() );
513
0
    }
514
0
    else if( xSeq.is())
515
0
    {
516
0
        Sequence< uno::Any > aAnies( xSeq->getData());
517
0
        aResult.resize( aAnies.getLength(), std::numeric_limits<double>::quiet_NaN() );
518
0
        for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
519
0
            aAnies[i] >>= aResult[i];
520
0
    }
521
0
    return aResult;
522
0
}
523
524
namespace
525
{
526
527
constexpr auto constChartTypeMap = frozen::make_unordered_map<std::u16string_view, chart::TypeId>(
528
{
529
    { u"com.sun.star.chart.BarDiagram", chart::TYPEID_BAR },
530
    { u"com.sun.star.chart2.ColumnChartType",  chart::TYPEID_BAR },
531
    { u"com.sun.star.chart.AreaDiagram",  chart::TYPEID_AREA },
532
    { u"com.sun.star.chart2.AreaChartType",  chart::TYPEID_AREA },
533
    { u"com.sun.star.chart.LineDiagram",  chart::TYPEID_LINE },
534
    { u"com.sun.star.chart2.LineChartType",  chart::TYPEID_LINE },
535
    { u"com.sun.star.chart.PieDiagram",  chart::TYPEID_PIE },
536
    { u"com.sun.star.chart2.PieChartType",  chart::TYPEID_PIE },
537
    { u"com.sun.star.chart.DonutDiagram",  chart::TYPEID_DOUGHNUT },
538
    { u"com.sun.star.chart2.DonutChartType",  chart::TYPEID_DOUGHNUT },
539
    { u"com.sun.star.chart.XYDiagram",  chart::TYPEID_SCATTER },
540
    { u"com.sun.star.chart2.ScatterChartType",  chart::TYPEID_SCATTER },
541
    { u"com.sun.star.chart.NetDiagram",  chart::TYPEID_RADARLINE },
542
    { u"com.sun.star.chart2.NetChartType",  chart::TYPEID_RADARLINE },
543
    { u"com.sun.star.chart.FilledNetDiagram",  chart::TYPEID_RADARAREA },
544
    { u"com.sun.star.chart2.FilledNetChartType",  chart::TYPEID_RADARAREA },
545
    { u"com.sun.star.chart.StockDiagram",  chart::TYPEID_STOCK },
546
    { u"com.sun.star.chart2.CandleStickChartType",  chart::TYPEID_STOCK },
547
    { u"com.sun.star.chart.BubbleDiagram",  chart::TYPEID_BUBBLE },
548
    { u"com.sun.star.chart2.BubbleChartType",  chart::TYPEID_BUBBLE },
549
    { u"com.sun.star.chart2.BoxWhiskerDiagram",  chart::TYPEID_BOXWHISKER },
550
    { u"com.sun.star.chart2.BoxWhiskerChartType",  chart::TYPEID_BOXWHISKER },
551
    { u"com.sun.star.chart2.ClusteredColumnDiagram", chart::TYPEID_CLUSTEREDCOLUMN },
552
    { u"com.sun.star.chart2.ClusteredColumnChartType", chart::TYPEID_CLUSTEREDCOLUMN },
553
    { u"com.sun.star.chart.FunnelDiagram",  chart::TYPEID_FUNNEL },
554
    { u"com.sun.star.chart2.FunnelChartType",  chart::TYPEID_FUNNEL },
555
    { u"com.sun.star.chart2.ParetoLineDiagram",  chart::TYPEID_PARETOLINE },
556
    { u"com.sun.star.chart2.ParetoLineChartType",  chart::TYPEID_PARETOLINE },
557
    { u"com.sun.star.chart2.RegionMapDiagram",  chart::TYPEID_REGIONMAP },
558
    { u"com.sun.star.chart2.RegionMapChartType",  chart::TYPEID_REGIONMAP },
559
    { u"com.sun.star.chart2.SunburstDiagram",  chart::TYPEID_SUNBURST },
560
    { u"com.sun.star.chart2.SunburstChartType",  chart::TYPEID_SUNBURST },
561
    { u"com.sun.star.chart2.TreemapDiagram",  chart::TYPEID_TREEMAP },
562
    { u"com.sun.star.chart2.TreemapChartType",  chart::TYPEID_TREEMAP },
563
    { u"com.sun.star.chart2.WaterfallDiagram",  chart::TYPEID_WATERFALL },
564
    { u"com.sun.star.chart2.WaterfallChartType",  chart::TYPEID_WATERFALL },
565
});
566
567
} // end anonymous namespace
568
569
static sal_Int32 lcl_getChartType(std::u16string_view sChartType)
570
0
{
571
0
    auto aIterator = constChartTypeMap.find(sChartType);
572
0
    if (aIterator == constChartTypeMap.end())
573
0
        return chart::TYPEID_UNKNOWN;
574
0
    return aIterator->second;
575
0
}
576
577
static sal_Int32 lcl_generateRandomValue()
578
0
{
579
0
    return comphelper::rng::uniform_int_distribution(0, 100000000-1);
580
0
}
581
582
bool DataLabelsRange::empty() const
583
0
{
584
0
    return maLabels.empty();
585
0
}
586
587
size_t DataLabelsRange::count() const
588
0
{
589
0
    return maLabels.size();
590
0
}
591
592
bool DataLabelsRange::hasLabel(sal_Int32 nIndex) const
593
0
{
594
0
    return maLabels.find(nIndex) != maLabels.end();
595
0
}
596
597
const OUString & DataLabelsRange::getRange() const
598
0
{
599
0
    return maRange;
600
0
}
601
602
void DataLabelsRange::setRange(const OUString& rRange)
603
0
{
604
0
    maRange = rRange;
605
0
}
606
607
void DataLabelsRange::setLabel(sal_Int32 nIndex, const OUString& rText)
608
0
{
609
0
    maLabels.emplace(nIndex, rText);
610
0
}
611
612
DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::begin() const
613
0
{
614
0
    return maLabels.begin();
615
0
}
616
617
DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::end() const
618
0
{
619
0
    return maLabels.end();
620
0
}
621
622
ChartExport::ChartExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, Reference< frame::XModel > const & xModel, XmlFilterBase* pFB, DocumentType eDocumentType )
623
0
    : DrawingML( std::move(pFS), pFB, eDocumentType )
624
0
    , mnXmlNamespace( nXmlNamespace )
625
0
    , mnSeriesCount(0)
626
0
    , mxChartModel( xModel )
627
0
    , mpURLTransformer(std::make_shared<URLTransformer>())
628
0
    , mbHasCategoryLabels( false )
629
0
    , mbHasZAxis( false )
630
0
    , mbIs3DChart( false )
631
0
    , mbStacked(false)
632
0
    , mbPercent(false)
633
0
    , mbHasDateCategories(false)
634
0
{
635
0
}
636
637
void ChartExport::SetURLTranslator(const std::shared_ptr<URLTransformer>& pTransformer)
638
0
{
639
0
    mpURLTransformer = pTransformer;
640
0
}
641
642
sal_Int32 ChartExport::getChartType( )
643
0
{
644
0
    OUString sChartType = mxDiagram->getDiagramType();
645
0
    return lcl_getChartType( sChartType );
646
0
}
647
648
namespace {
649
650
uno::Sequence< beans::PropertyValue > createArguments(
651
    const OUString & rRangeRepresentation, bool bUseColumns)
652
0
{
653
0
    css::chart::ChartDataRowSource eRowSource = css::chart::ChartDataRowSource_ROWS;
654
0
    if (bUseColumns)
655
0
        eRowSource = css::chart::ChartDataRowSource_COLUMNS;
656
657
0
    uno::Sequence<beans::PropertyValue> aArguments{
658
0
        { u"DataRowSource"_ustr, -1, uno::Any(eRowSource), beans::PropertyState_DIRECT_VALUE },
659
0
        { u"FirstCellAsLabel"_ustr, -1, uno::Any(false), beans::PropertyState_DIRECT_VALUE },
660
0
        { u"HasCategories"_ustr, -1, uno::Any(false), beans::PropertyState_DIRECT_VALUE },
661
0
        { u"CellRangeRepresentation"_ustr, -1, uno::Any(rRangeRepresentation),
662
0
          beans::PropertyState_DIRECT_VALUE }
663
0
    };
664
665
0
    return aArguments;
666
0
}
667
668
Reference<chart2::XDataSeries> getPrimaryDataSeries(const Reference<chart2::XChartType>& xChartType)
669
0
{
670
0
    Reference< chart2::XDataSeriesContainer > xDSCnt(xChartType, uno::UNO_QUERY_THROW);
671
672
    // export dataseries for current chart-type
673
0
    const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq(xDSCnt->getDataSeries());
674
0
    for (const auto& rSeries : aSeriesSeq)
675
0
    {
676
0
        Reference<chart2::XDataSeries> xSource(rSeries, uno::UNO_QUERY);
677
0
        if (xSource.is())
678
0
            return xSource;
679
0
    }
680
681
0
    return Reference<chart2::XDataSeries>();
682
0
}
683
684
}
685
686
Sequence< Sequence< OUString > > ChartExport::getSplitCategoriesList( const OUString& rRange )
687
0
{
688
0
    Reference< chart2::XChartDocument > xChartDoc(getModel(), uno::UNO_QUERY);
689
0
    OSL_ASSERT(xChartDoc.is());
690
0
    if (xChartDoc.is())
691
0
    {
692
0
        Reference< chart2::data::XDataProvider > xDataProvider(xChartDoc->getDataProvider());
693
0
        OSL_ENSURE(xDataProvider.is(), "No DataProvider");
694
0
        if (xDataProvider.is())
695
0
        {
696
            //detect whether the first series is a row or a column
697
0
            bool bSeriesUsesColumns = true;
698
0
            Reference< chart2::XDiagram > xDiagram(xChartDoc->getFirstDiagram());
699
0
            try
700
0
            {
701
0
                Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(xDiagram, uno::UNO_QUERY_THROW);
702
0
                const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(xCooSysCnt->getCoordinateSystems());
703
0
                for (const auto& rCooSys : aCooSysSeq)
704
0
                {
705
0
                    const Reference< chart2::XChartTypeContainer > xCTCnt(rCooSys, uno::UNO_QUERY_THROW);
706
0
                    const Sequence< Reference< chart2::XChartType > > aChartTypeSeq(xCTCnt->getChartTypes());
707
0
                    for (const auto& rChartType : aChartTypeSeq)
708
0
                    {
709
0
                        Reference< chart2::XDataSeries > xDataSeries = getPrimaryDataSeries(rChartType);
710
0
                        if (xDataSeries.is())
711
0
                        {
712
0
                            uno::Reference< chart2::data::XDataSource > xSeriesSource(xDataSeries, uno::UNO_QUERY);
713
0
                            const uno::Sequence< beans::PropertyValue > rArguments = xDataProvider->detectArguments(xSeriesSource);
714
0
                            for (const beans::PropertyValue& rProperty : rArguments)
715
0
                            {
716
0
                                if (rProperty.Name == "DataRowSource")
717
0
                                {
718
0
                                    css::chart::ChartDataRowSource eRowSource;
719
0
                                    if (rProperty.Value >>= eRowSource)
720
0
                                    {
721
0
                                        bSeriesUsesColumns = (eRowSource == css::chart::ChartDataRowSource_COLUMNS);
722
0
                                        break;
723
0
                                    }
724
0
                                }
725
0
                            }
726
0
                        }
727
0
                    }
728
0
                }
729
0
            }
730
0
            catch (const uno::Exception &)
731
0
            {
732
0
                DBG_UNHANDLED_EXCEPTION("chart2");
733
0
            }
734
            // detect we have an inner data table or not
735
0
            if (xChartDoc->hasInternalDataProvider() && rRange == "categories")
736
0
            {
737
0
                try
738
0
                {
739
0
                    css::uno::Reference< css::chart2::XAnyDescriptionAccess > xDataAccess(xChartDoc->getDataProvider(), uno::UNO_QUERY);
740
0
                    const Sequence< Sequence< uno::Any > >aAnyCategories(bSeriesUsesColumns ? xDataAccess->getAnyRowDescriptions() : xDataAccess->getAnyColumnDescriptions());
741
0
                    auto pMax = std::max_element(aAnyCategories.begin(), aAnyCategories.end(),
742
0
                        [](const Sequence<uno::Any>& a, const Sequence<uno::Any>& b) {
743
0
                            return a.getLength() < b.getLength(); });
744
745
                    //minimum is 1!
746
0
                    if (pMax != aAnyCategories.end() && pMax->getLength() > 1)
747
0
                    {
748
0
                        sal_Int32 nLevelCount = pMax->getLength();
749
                        //we have complex categories
750
                        //sort the categories name
751
0
                        Sequence<Sequence<OUString>>aFinalSplitSource(nLevelCount);
752
0
                        auto pFinalSplitSource = aFinalSplitSource.getArray();
753
0
                        for (sal_Int32 i = 0; i < nLevelCount; i++)
754
0
                        {
755
0
                            sal_Int32 nElemLabel = 0;
756
0
                            pFinalSplitSource[nLevelCount - i - 1].realloc(aAnyCategories.getLength());
757
0
                            auto pSeq = pFinalSplitSource[nLevelCount - i - 1].getArray();
758
0
                            for (auto const& elemLabel : aAnyCategories)
759
0
                            {
760
                                // make sure elemLabel[i] exists!
761
0
                                if (elemLabel.getLength() > i)
762
0
                                {
763
0
                                    pSeq[nElemLabel] = elemLabel[i].get<OUString>();
764
0
                                    nElemLabel++;
765
0
                                }
766
0
                            }
767
0
                        }
768
0
                        return aFinalSplitSource;
769
0
                    }
770
0
                }
771
0
                catch (const uno::Exception &)
772
0
                {
773
0
                    DBG_UNHANDLED_EXCEPTION("oox");
774
0
                }
775
0
            }
776
0
            else
777
0
            {
778
0
                try
779
0
                {
780
0
                    uno::Reference< chart2::data::XDataSource > xCategoriesSource(xDataProvider->createDataSource(
781
0
                        createArguments(rRange, bSeriesUsesColumns)));
782
783
0
                    if (xCategoriesSource.is())
784
0
                    {
785
0
                        const Sequence< Reference< chart2::data::XLabeledDataSequence >> aCategories = xCategoriesSource->getDataSequences();
786
0
                        if (aCategories.getLength() > 1)
787
0
                        {
788
                            //we have complex categories
789
                            //sort the categories name
790
0
                            Sequence<Sequence<OUString>> aFinalSplitSource(aCategories.getLength());
791
0
                            std::transform(aCategories.begin(), aCategories.end(),
792
0
                                std::reverse_iterator(asNonConstRange(aFinalSplitSource).end()),
793
0
                                [](const Reference<chart2::data::XLabeledDataSequence>& xCat) {
794
0
                                    return lcl_getLabelSequence(xCat->getValues()); });
795
0
                            return aFinalSplitSource;
796
0
                        }
797
0
                    }
798
0
                }
799
0
                catch (const uno::Exception &)
800
0
                {
801
0
                    DBG_UNHANDLED_EXCEPTION("oox");
802
0
                }
803
0
            }
804
0
        }
805
0
    }
806
807
0
    return Sequence< Sequence< OUString>>(0);
808
0
}
809
810
OUString ChartExport::parseFormula( const OUString& rRange )
811
0
{
812
0
    OUString aResult;
813
0
    Reference< XFormulaParser > xParser;
814
0
    uno::Reference< lang::XMultiServiceFactory > xSF = GetFB()->getModelFactory();
815
0
    if( xSF.is() )
816
0
    {
817
0
        try
818
0
        {
819
0
            xParser.set( xSF->createInstance(u"com.sun.star.sheet.FormulaParser"_ustr), UNO_QUERY );
820
0
        }
821
0
        catch( Exception& )
822
0
        {
823
0
        }
824
0
    }
825
826
0
    SAL_WARN_IF(!xParser.is(), "oox", "creating formula parser failed");
827
828
0
    if( xParser.is() )
829
0
    {
830
0
        Reference< XPropertySet > xParserProps( xParser, uno::UNO_QUERY );
831
        // rRange is the result of a
832
        // css::chart2::data::XDataSequence::getSourceRangeRepresentation()
833
        // call that returns the range in the document's current UI notation.
834
        // Creating a FormulaParser defaults to the same notation, for
835
        // parseFormula() do not attempt to override the FormulaConvention
836
        // property with css::sheet::AddressConvention::OOO or some such.
837
        /* TODO: it would be much better to introduce a
838
         * getSourceRangeRepresentation(css::sheet::AddressConvention) to
839
         * return the ranges in a specific convention than converting them with
840
         * the overhead of creating an XFormulaParser for each... */
841
0
        uno::Sequence<sheet::FormulaToken> aTokens = xParser->parseFormula( rRange, CellAddress( 0, 0, 0 ) );
842
0
        if( xParserProps.is() )
843
0
        {
844
0
            xParserProps->setPropertyValue(u"FormulaConvention"_ustr, uno::Any(css::sheet::AddressConvention::XL_OOX) );
845
            // For referencing named ranges correctly with special excel chart syntax.
846
0
            xParserProps->setPropertyValue(u"RefConventionChartOOXML"_ustr, uno::Any(true) );
847
0
        }
848
0
        aResult = xParser->printFormula( aTokens, CellAddress( 0, 0, 0 ) );
849
0
    }
850
0
    else
851
0
    {
852
        //FIXME: currently just using simple converter, e.g $Sheet1.$A$1:$C$1 -> Sheet1!$A$1:$C$1
853
0
        OUString aRange( rRange );
854
0
        if( aRange.startsWith("$") )
855
0
            aRange = aRange.copy(1);
856
0
        aRange = aRange.replaceAll(".$", "!$" );
857
0
        aResult = aRange;
858
0
    }
859
860
0
    return aResult;
861
0
}
862
863
// Output the chartex AlternateContent fallback path
864
static void writeChartexAlternateContent(FSHelperPtr pFS)
865
0
{
866
0
    pFS->startElementNS(XML_mc, XML_Fallback);
867
0
    pFS->startElementNS(XML_xdr, XML_sp, XML_macro, "", XML_textlink, "");
868
0
    pFS->startElementNS(XML_xdr, XML_nvSpPr);
869
0
    pFS->singleElementNS(XML_xdr, XML_cNvPr, XML_id, "0", XML_name, "");
870
0
    pFS->startElementNS(XML_xdr, XML_cNvSpPr);
871
0
    pFS->singleElementNS(XML_a, XML_spLocks, XML_noTextEdit, "1");
872
0
    pFS->endElementNS(XML_xdr, XML_cNvSpPr);
873
0
    pFS->endElementNS(XML_xdr, XML_nvSpPr);
874
0
    pFS->startElementNS(XML_xdr, XML_spPr);
875
0
    pFS->startElementNS(XML_a, XML_xfrm);
876
0
    pFS->singleElementNS(XML_a, XML_off, XML_x, "6600825", XML_y, "2533650");
877
0
    pFS->singleElementNS(XML_a, XML_ext, XML_cx, "4572000", XML_cy, "2743200");
878
0
    pFS->endElementNS(XML_a, XML_xfrm);
879
0
    pFS->startElementNS(XML_a, XML_prstGeom, XML_prst, "rect");
880
0
    pFS->singleElementNS(XML_a, XML_avLst);
881
0
    pFS->endElementNS(XML_a, XML_prstGeom);
882
0
    pFS->startElementNS(XML_a, XML_solidFill);
883
0
    pFS->singleElementNS(XML_a, XML_prstClr, XML_val, "white");
884
0
    pFS->endElementNS(XML_a, XML_solidFill);
885
0
    pFS->startElementNS(XML_a, XML_ln, XML_w, "1");
886
0
    pFS->startElementNS(XML_a, XML_solidFill);
887
0
    pFS->singleElementNS(XML_a, XML_prstClr, XML_val, "green");
888
0
    pFS->endElementNS(XML_a, XML_solidFill);
889
0
    pFS->endElementNS(XML_a, XML_ln);
890
0
    pFS->endElementNS(XML_xdr, XML_spPr);
891
0
    pFS->startElementNS(XML_xdr, XML_txBody);
892
0
    pFS->singleElementNS(XML_a, XML_bodyPr, XML_vertOverflow, "clip", XML_horzOverflow, "clip");
893
0
    pFS->singleElementNS(XML_a, XML_lstStyle);
894
0
    pFS->startElementNS(XML_a, XML_p);
895
0
    pFS->startElementNS(XML_a, XML_r);
896
0
    pFS->singleElementNS(XML_a, XML_rPr, XML_sz, "1100");
897
0
    pFS->startElementNS(XML_a, XML_t);
898
899
0
    const std::string_view sErrTxt("This chart isn't available in your version of Excel.\n\n"
900
0
        "Editing this shape or saving this workbook into a different file format will permanently break the chart.");
901
0
    pFS->writeEscaped( sErrTxt );
902
903
0
    pFS->endElementNS(XML_a, XML_t);
904
0
    pFS->endElementNS(XML_a, XML_r);
905
0
    pFS->endElementNS(XML_a, XML_p);
906
0
    pFS->endElementNS(XML_xdr, XML_txBody);
907
0
    pFS->endElementNS(XML_xdr, XML_sp);
908
0
}
909
910
namespace {
911
912
// Map enumerated style entry types to corresponding XML tags
913
struct styleTag {
914
    model::StyleSet::StyleEntryType meType;
915
    sal_Int32             mnTag;
916
};
917
918
std::array<styleTag, static_cast<int>(model::StyleSet::StyleEntryType::END)> styleTagMap {{
919
    { model::StyleSet::StyleEntryType::AXISTITLE, XML_axisTitle },
920
    { model::StyleSet::StyleEntryType::CATEGORYAXIS, XML_categoryAxis },
921
    { model::StyleSet::StyleEntryType::CHARTAREA, XML_chartArea },
922
    { model::StyleSet::StyleEntryType::DATALABEL, XML_dataLabel },
923
    { model::StyleSet::StyleEntryType::DATALABELCALLOUT, XML_dataLabelCallout },
924
    { model::StyleSet::StyleEntryType::DATAPOINT, XML_dataPoint },
925
    { model::StyleSet::StyleEntryType::DATAPOINT3D, XML_dataPoint3D },
926
    { model::StyleSet::StyleEntryType::DATAPOINTLINE, XML_dataPointLine },
927
    { model::StyleSet::StyleEntryType::DATAPOINTMARKER, XML_dataPointMarker },
928
    { model::StyleSet::StyleEntryType::DATAPOINTMARKERLAYOUT, XML_dataPointMarkerLayout },
929
    { model::StyleSet::StyleEntryType::DATAPOINTWIREFRAME, XML_dataPointWireframe },
930
    { model::StyleSet::StyleEntryType::DATATABLE, XML_dataTable },
931
    { model::StyleSet::StyleEntryType::DOWNBAR, XML_downBar },
932
    { model::StyleSet::StyleEntryType::DROPLINE, XML_dropLine },
933
    { model::StyleSet::StyleEntryType::ERRORBAR, XML_errorBar },
934
    { model::StyleSet::StyleEntryType::FLOOR, XML_floor },
935
    { model::StyleSet::StyleEntryType::GRIDLINEMAJOR, XML_gridlineMajor },
936
    { model::StyleSet::StyleEntryType::GRIDLINEMINOR, XML_gridlineMinor },
937
    { model::StyleSet::StyleEntryType::HILOLINE, XML_hiLoLine },
938
    { model::StyleSet::StyleEntryType::LEADERLINE, XML_leaderLine },
939
    { model::StyleSet::StyleEntryType::LEGEND, XML_legend },
940
    { model::StyleSet::StyleEntryType::PLOTAREA, XML_plotArea },
941
    { model::StyleSet::StyleEntryType::PLOTAREA3D, XML_plotArea3D },
942
    { model::StyleSet::StyleEntryType::SERIESAXIS, XML_seriesAxis },
943
    { model::StyleSet::StyleEntryType::SERIESLINE, XML_seriesLine },
944
    { model::StyleSet::StyleEntryType::TITLE, XML_title },
945
    { model::StyleSet::StyleEntryType::TRENDLINE, XML_trendline },
946
    { model::StyleSet::StyleEntryType::TRENDLINELABEL, XML_trendlineLabel },
947
    { model::StyleSet::StyleEntryType::UPBAR, XML_upBar },
948
    { model::StyleSet::StyleEntryType::VALUEAXIS, XML_valueAxis },
949
    { model::StyleSet::StyleEntryType::WALL, XML_wall }
950
}};
951
952
// The following functions are intended to duplicate what appear to be the MS
953
// Office defaults for various style entries. These are not documented anywhere,
954
// so far as I can tell. The values here are just derived by looking at some
955
// files Office creates. It's very possible that things are not as simple as the
956
// hard-coded values here.
957
958
// All style entries other than the special cases below
959
void outputDefaultStyleEntry(FSHelperPtr pFS, sal_Int32 nElTokenId)
960
0
{
961
0
    pFS->startElement(FSNS(XML_cs, nElTokenId));
962
0
    pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0");
963
0
    pFS->singleElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0");
964
0
    pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0");
965
0
    pFS->singleElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor");
966
0
    pFS->endElement(FSNS(XML_cs, nElTokenId));
967
0
}
968
969
// chartArea entry
970
void outputDefaultChartAreaStyleEntry(FSHelperPtr pFS)
971
0
{
972
0
    pFS->startElement(FSNS(XML_cs, XML_chartArea), XML_mods, "allowNoFillOverride allowNoLineOverride");
973
0
    pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0");
974
0
    pFS->singleElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0");
975
0
    pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0");
976
977
0
    pFS->startElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor");
978
0
    pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "tx1");
979
0
    pFS->endElement(FSNS(XML_cs, XML_fontRef));
980
981
0
    pFS->startElement(FSNS(XML_cs, XML_spPr));
982
983
0
    pFS->startElement(FSNS(XML_a, XML_solidFill));
984
0
    pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "bg1");
985
0
    pFS->endElement(FSNS(XML_a, XML_solidFill));
986
987
0
    pFS->startElement(FSNS(XML_a, XML_ln), XML_w, "9525", XML_cap, "flat",
988
0
            XML_cmpd, "sng", XML_algn, "ctr");
989
0
    pFS->startElement(FSNS(XML_a, XML_solidFill));
990
0
    pFS->startElement(FSNS(XML_a, XML_schemeClr), XML_val, "tx1");
991
0
    pFS->singleElement(FSNS(XML_a, XML_lumMod), XML_val, "15000");
992
0
    pFS->singleElement(FSNS(XML_a, XML_lumOff), XML_val, "85000");
993
0
    pFS->endElement(FSNS(XML_a, XML_schemeClr));
994
0
    pFS->endElement(FSNS(XML_a, XML_solidFill));
995
0
    pFS->singleElement(FSNS(XML_a, XML_round));
996
0
    pFS->endElement(FSNS(XML_a, XML_ln));
997
998
0
    pFS->endElement(FSNS(XML_cs, XML_spPr));
999
1000
0
    pFS->endElement(FSNS(XML_cs, XML_chartArea));
1001
0
}
1002
1003
// dataPoint entry
1004
void outputDefaultDataPointStyleEntry(FSHelperPtr pFS)
1005
0
{
1006
0
    pFS->startElement(FSNS(XML_cs, XML_dataPoint));
1007
0
    pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0");
1008
1009
0
    pFS->startElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0");
1010
0
    pFS->singleElement(FSNS(XML_cs, XML_styleClr), XML_val, "auto");
1011
0
    pFS->endElement(FSNS(XML_cs, XML_fillRef));
1012
1013
0
    pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0");
1014
1015
0
    pFS->startElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor");
1016
0
    pFS->singleElement(FSNS(XML_cs, XML_schemeClr), XML_val, "tx1");
1017
0
    pFS->endElement(FSNS(XML_cs, XML_fontRef));
1018
1019
0
    pFS->startElement(FSNS(XML_cs, XML_spPr));
1020
0
    pFS->startElement(FSNS(XML_a, XML_solidFill));
1021
0
    pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "phClr");
1022
0
    pFS->endElement(FSNS(XML_a, XML_solidFill));
1023
0
    pFS->endElement(FSNS(XML_cs, XML_spPr));
1024
1025
0
    pFS->endElement(FSNS(XML_cs, XML_dataPoint));
1026
0
}
1027
1028
} // unnamed namespace
1029
1030
1031
1032
void ChartExport::WriteChartObj( const Reference< XShape >& xShape, sal_Int32 nID, sal_Int32 nChartCount )
1033
0
{
1034
0
    FSHelperPtr pFS = GetFS();
1035
1036
0
    Reference< chart2::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
1037
0
    OSL_ASSERT( xChartDoc.is() );
1038
0
    if( !xChartDoc.is() )
1039
0
        return;
1040
1041
    // At minimum, a PlotArea is needed or else MS Word complains about an invalid file
1042
0
    uno::Reference<chart2::XCoordinateSystemContainer>
1043
0
        xBCooSysCnt(xChartDoc->getFirstDiagram(), uno::UNO_QUERY);
1044
0
    if (!xBCooSysCnt.is())
1045
0
        return;
1046
1047
    // We need to get the new diagram here so we can know if this is a chartex
1048
    // chart.
1049
0
    mxNewDiagram.set( xChartDoc->getFirstDiagram());
1050
1051
0
    NamespaceAbbrev eNS = NamespaceAbbrev::NONE;
1052
0
    const bool bIsChartex = isChartexNotChartNS(&eNS);
1053
1054
0
    const bool bIsInGroupShape = GetDocumentType() == DOCUMENT_DOCX && mnXmlNamespace == XML_wps
1055
0
                                 && IsGroupShape(m_xParent);
1056
0
    if (bIsInGroupShape)
1057
0
        mnXmlNamespace = XML_wpg;
1058
1059
0
    if (bIsChartex) {
1060
        // Do the AlternateContent header
1061
0
        mpFS->startElementNS(XML_mc, XML_AlternateContent, FSNS(XML_xmlns, XML_mc),
1062
0
                "http://schemas.openxmlformats.org/markup-compatibility/2006");
1063
0
        switch (eNS) {
1064
0
            case NamespaceAbbrev::CX1:
1065
0
                mpFS->startElementNS(XML_mc, XML_Choice,
1066
0
                        FSNS(XML_xmlns, XML_cx1), "http://schemas.microsoft.com/office/drawing/2015/9/8/chartex",
1067
0
                        XML_Requires, "cx1");
1068
0
                break;
1069
0
            case NamespaceAbbrev::CX2:
1070
0
                mpFS->startElementNS(XML_mc, XML_Choice,
1071
0
                        FSNS(XML_xmlns, XML_cx2), "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
1072
0
                        XML_Requires, "cx2");
1073
0
                break;
1074
0
            case NamespaceAbbrev::CX4:
1075
0
                mpFS->startElementNS(XML_mc, XML_Choice,
1076
0
                        FSNS(XML_xmlns, XML_cx4), "http://schemas.microsoft.com/office/drawing/2016/5/10/chartex",
1077
0
                        XML_Requires, "cx4");
1078
0
                break;
1079
0
            case NamespaceAbbrev::NONE:
1080
0
                assert(false);
1081
0
        }
1082
0
    }
1083
1084
0
    Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
1085
1086
0
    pFS->startElementNS(mnXmlNamespace, XML_graphicFrame);
1087
0
    if (GetDocumentType() != DOCUMENT_DOCX)
1088
0
        pFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr);
1089
1090
    // TODO: get the correct chart name chart id
1091
0
    OUString sName = u"Object 1"_ustr;
1092
0
    Reference< XNamed > xNamed( xShape, UNO_QUERY );
1093
0
    if (xNamed.is())
1094
0
        sName = xNamed->getName();
1095
1096
0
    pFS->startElementNS( mnXmlNamespace, XML_cNvPr,
1097
0
                          XML_id,     OString::number(nID),
1098
0
                          XML_name,   sName);
1099
1100
0
    OUString sURL;
1101
0
    if ( GetProperty( xShapeProps, u"URL"_ustr ) )
1102
0
        mAny >>= sURL;
1103
0
    if( !sURL.isEmpty() )
1104
0
    {
1105
0
        OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
1106
0
                oox::getRelationship(Relationship::HYPERLINK),
1107
0
                mpURLTransformer->getTransformedString(sURL),
1108
0
                mpURLTransformer->isExternalURL(sURL));
1109
1110
0
        mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
1111
0
    }
1112
1113
0
    if (bIsChartex) {
1114
0
        pFS->startElement(FSNS(XML_a, XML_extLst));
1115
0
        pFS->startElement(FSNS(XML_a, XML_ext), XML_uri,
1116
0
            "{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}");
1117
0
        pFS->singleElement(FSNS(XML_a16, XML_creationId),
1118
0
                FSNS(XML_xmlns, XML_a16), "http://schemas.microsoft.com/office/drawing/2014/main",
1119
0
                XML_id, "{393D7C90-AF84-3958-641C-0FEC03FE8894}");
1120
1121
0
        pFS->endElement(FSNS(XML_a, XML_ext));
1122
0
        pFS->endElement(FSNS(XML_a, XML_extLst));
1123
0
    }
1124
1125
0
    pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
1126
1127
0
    if (GetDocumentType() == DOCUMENT_DOCX)
1128
0
    {
1129
0
        if (bIsInGroupShape)
1130
0
            pFS->singleElementNS(mnXmlNamespace, XML_cNvFrPr);
1131
0
        else
1132
0
            pFS->singleElementNS(mnXmlNamespace, XML_cNvSpPr);
1133
0
    }
1134
0
    else
1135
0
        pFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr);
1136
1137
0
    if( GetDocumentType() == DOCUMENT_PPTX )
1138
0
        pFS->singleElementNS(mnXmlNamespace, XML_nvPr);
1139
1140
0
    if (GetDocumentType() != DOCUMENT_DOCX)
1141
0
        pFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
1142
1143
    // visual chart properties
1144
0
    WriteShapeTransformation( xShape, mnXmlNamespace );
1145
1146
0
    const char *sSchemaURL = bIsChartex?
1147
0
        "http://schemas.microsoft.com/office/drawing/2014/chartex" :
1148
0
        "http://schemas.openxmlformats.org/drawingml/2006/chart";
1149
1150
    // writer chart object
1151
0
    pFS->startElement(FSNS(XML_a, XML_graphic));
1152
0
    pFS->startElement( FSNS( XML_a, XML_graphicData ), XML_uri, sSchemaURL );
1153
0
    OUString sId;
1154
0
    const char* sFullPath = nullptr;
1155
0
    const char* sRelativePath = nullptr;
1156
0
    const char *sChartFnamePrefix = bIsChartex? "chartEx" : "chart";
1157
0
    switch( GetDocumentType() )
1158
0
    {
1159
0
        case DOCUMENT_DOCX:
1160
0
        {
1161
0
            sFullPath = "word/charts/";
1162
0
            sRelativePath = "charts/";
1163
0
            break;
1164
0
        }
1165
0
        case DOCUMENT_PPTX:
1166
0
        {
1167
0
            sFullPath = "ppt/charts/";
1168
0
            sRelativePath = "../charts/";
1169
0
            break;
1170
0
        }
1171
0
        case DOCUMENT_XLSX:
1172
0
        {
1173
0
            sFullPath = "xl/charts/";
1174
0
            sRelativePath = "../charts/";
1175
0
            break;
1176
0
        }
1177
0
        default:
1178
0
        {
1179
0
            sFullPath = "charts/";
1180
0
            sRelativePath = "charts/";
1181
0
            break;
1182
0
        }
1183
0
    }
1184
0
    OUString sFullStream = OUStringBuffer()
1185
0
                            .appendAscii(sFullPath)
1186
0
                            .appendAscii(sChartFnamePrefix)
1187
0
                            .append(OUString::number(nChartCount) + ".xml")
1188
0
                            .makeStringAndClear();
1189
0
    OUString sRelativeStream = OUStringBuffer()
1190
0
                            .appendAscii(sRelativePath)
1191
0
                            .appendAscii(sChartFnamePrefix)
1192
0
                            .append(OUString::number(nChartCount) + ".xml" )
1193
0
                            .makeStringAndClear();
1194
1195
0
    const OUString sAppURL = bIsChartex?
1196
0
        u"application/vnd.ms-office.chartex+xml"_ustr :
1197
0
        u"application/vnd.openxmlformats-officedocument.drawingml.chart+xml"_ustr;
1198
1199
0
    const Relationship eChartRel = bIsChartex ?
1200
0
        Relationship::CHARTEX :
1201
0
        Relationship::CHART;
1202
1203
0
    FSHelperPtr pChart = CreateOutputStream(
1204
0
            sFullStream,
1205
0
            sRelativeStream,
1206
0
            pFS->getOutputStream(),
1207
0
            sAppURL,
1208
0
            oox::getRelationship(eChartRel),
1209
0
            &sId );
1210
1211
0
    XmlFilterBase* pFB = GetFB();
1212
1213
0
    if (bIsChartex) {
1214
        // Use chartex namespace
1215
0
        pFS->singleElement(  FSNS( XML_cx, XML_chart ),
1216
0
                FSNS(XML_xmlns, XML_cx), pFB->getNamespaceURL(OOX_NS(cx)),
1217
0
                FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)),
1218
0
                FSNS(XML_r, XML_id), sId );
1219
0
    } else {
1220
0
        pFS->singleElement(  FSNS( XML_c, XML_chart ),
1221
0
                FSNS(XML_xmlns, XML_c), pFB->getNamespaceURL(OOX_NS(dmlChart)),
1222
0
                FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)),
1223
0
                FSNS(XML_r, XML_id), sId );
1224
0
    }
1225
1226
0
    pFS->endElement( FSNS( XML_a, XML_graphicData ) );
1227
0
    pFS->endElement( FSNS( XML_a, XML_graphic ) );
1228
0
    pFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
1229
1230
0
    if (bIsChartex) {
1231
0
        pFS->endElementNS(XML_mc, XML_Choice);
1232
1233
0
        writeChartexAlternateContent(pFS);
1234
1235
0
        pFS->endElementNS(XML_mc, XML_Fallback);
1236
0
        pFS->endElementNS(XML_mc, XML_AlternateContent);
1237
0
    }
1238
1239
0
    SetFS( pChart );
1240
0
    ExportContent();
1241
1242
    // output style and colorstyle files
1243
1244
0
    SetFS( pChart );
1245
0
    sRelativePath ="";
1246
1247
0
    FSHelperPtr pChartFS = GetFS();
1248
1249
    // first style
1250
0
    static constexpr char sStyleFnamePrefix[] = "style";
1251
0
    OUStringBuffer sFullStreamBuf;
1252
0
    sFullStreamBuf.appendAscii(sFullPath);
1253
0
    sFullStreamBuf = sFullStreamBuf + sStyleFnamePrefix + OUString::number(nChartCount) + ".xml";
1254
0
    sFullStream = sFullStreamBuf.makeStringAndClear();
1255
0
    OUStringBuffer sRelativeStreamBuf;
1256
0
    sRelativeStreamBuf.appendAscii(sRelativePath);
1257
0
    sRelativeStreamBuf = sRelativeStreamBuf + sStyleFnamePrefix + OUString::number(nChartCount) + ".xml";
1258
0
    sRelativeStream = sRelativeStreamBuf.makeStringAndClear();
1259
1260
0
    FSHelperPtr pStyle = CreateOutputStream(
1261
0
            sFullStream,
1262
0
            sRelativeStream,
1263
0
            pChartFS->getOutputStream(),
1264
0
            u"application/vnd.ms-office.chartstyle+xml"_ustr,
1265
0
            oox::getRelationship(Relationship::CHARTSTYLE),
1266
0
            &sId,
1267
0
            true /* for some reason this doesn't have a header line */);
1268
1269
0
    SetFS( pStyle );
1270
0
    pFS = GetFS();
1271
1272
0
    pFS->startElement(FSNS(XML_cs, XML_chartStyle),
1273
0
            FSNS( XML_xmlns, XML_cs ), pFB->getNamespaceURL(OOX_NS(cs)),
1274
0
            FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
1275
0
            XML_id, "419" /* no idea what this number is supposed to be */);
1276
1277
0
    Reference<com::sun::star::chart2::XChartStyle> xStyle = xChartDoc->getStyles();
1278
0
    model::StyleSet* aSS = model::style::getFromXChartStyle(xStyle);
1279
1280
0
    for (enum model::StyleSet::StyleEntryType eEType = model::StyleSet::StyleEntryType::BEGIN;
1281
0
            eEType != model::StyleSet::StyleEntryType::END;
1282
0
            eEType = static_cast<model::StyleSet::StyleEntryType>(static_cast<int>(eEType) + 1)) {
1283
0
        auto entryIt = aSS->maEntryMap.find(eEType);
1284
1285
0
        if (entryIt == aSS->maEntryMap.end()) {
1286
            // We haven't stored any style information for this entry type, so
1287
            // just output a default value
1288
0
            switch (eEType) {
1289
0
                case model::StyleSet::StyleEntryType::CHARTAREA:
1290
0
                    outputDefaultChartAreaStyleEntry(pFS);
1291
0
                    break;
1292
0
                case model::StyleSet::StyleEntryType::DATAPOINT:
1293
0
                    outputDefaultDataPointStyleEntry(pFS);
1294
0
                    break;
1295
0
                case model::StyleSet::StyleEntryType::DATALABELCALLOUT:
1296
                    // no entry required?
1297
0
                    break;
1298
0
                default:
1299
0
                    if (styleTagMap[static_cast<int>(eEType)].meType != eEType) {
1300
0
                        assert(false);
1301
0
                    }
1302
0
                    outputDefaultStyleEntry(pFS, styleTagMap[static_cast<int>(eEType)].mnTag);
1303
0
                    break;
1304
0
            }
1305
0
        } else {
1306
0
            outputStyleEntry(pFS, styleTagMap[static_cast<int>(eEType)].mnTag,
1307
0
                    entryIt->second);
1308
0
        }
1309
0
    }
1310
1311
0
    pFS->endElement(FSNS(XML_cs, XML_chartStyle));
1312
1313
0
    pStyle->endDocument();
1314
1315
    // now colorstyle
1316
0
    static constexpr char sColorFnamePrefix[] = "colors";
1317
0
    sFullStreamBuf = OUStringBuffer();
1318
0
    sFullStreamBuf.appendAscii(sFullPath);
1319
0
    sFullStreamBuf = sFullStreamBuf + sColorFnamePrefix + OUString::number(nChartCount) + ".xml";
1320
0
    sFullStream = sFullStreamBuf.makeStringAndClear();
1321
0
    sRelativeStreamBuf = OUStringBuffer();
1322
0
    sRelativeStreamBuf.appendAscii(sRelativePath);
1323
0
    sRelativeStreamBuf = sRelativeStreamBuf + sColorFnamePrefix + OUString::number(nChartCount) + ".xml";
1324
0
    sRelativeStream = sRelativeStreamBuf.makeStringAndClear();
1325
1326
0
    FSHelperPtr pColorStyle = CreateOutputStream(
1327
0
            sFullStream,
1328
0
            sRelativeStream,
1329
0
            pChartFS->getOutputStream(),
1330
0
            u"application/vnd.ms-office.chartcolorstyle+xml"_ustr,
1331
0
            oox::getRelationship(Relationship::CHARTCOLORSTYLE),
1332
0
            &sId,
1333
0
            true /* also no header line */);
1334
1335
0
    SetFS( pColorStyle );
1336
0
    pFS = GetFS();
1337
1338
0
    Reference<com::sun::star::chart2::XChartColorStyle> xColorStyle = xChartDoc->getColorStyles();
1339
0
    model::ColorStyleSet* aCSS = model::style::getFromXChartColorStyle(xColorStyle);
1340
1341
0
    if (!aCSS->maEntryList.empty()) {
1342
        // use the stored data in the document model
1343
1344
0
        static const std::map<model::ColorStyleMethod, std::string> aMethMap {
1345
0
                { model::ColorStyleMethod::CYCLE, "cycle" },
1346
0
                { model::ColorStyleMethod::WITHIN_LINEAR, "withinLinear" },
1347
0
                { model::ColorStyleMethod::ACROSS_LINEAR, "acrossLinear" },
1348
0
                { model::ColorStyleMethod::WITHIN_LINEAR_REVERSED, "withinLinearReversed" },
1349
0
                { model::ColorStyleMethod::ACROSS_LINEAR_REVERSED, "acrossLinearReversed" }
1350
0
        };
1351
1352
0
        for (const model::ColorStyleEntry& rCStyleEntry : aCSS->maEntryList) {
1353
0
            pFS->startElement(FSNS(XML_cs, XML_colorStyle),
1354
0
                    FSNS( XML_xmlns, XML_cs ), pFB->getNamespaceURL(OOX_NS(cs)),
1355
0
                    FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
1356
0
                    XML_meth, aMethMap.at(rCStyleEntry.meMethod).c_str(),
1357
0
                    XML_id, OUString::number(rCStyleEntry.mnId));
1358
1359
0
            ThemeExport aTE(mpFB, GetDocumentType(), pFS);
1360
0
            for (const model::ComplexColor& rColor : rCStyleEntry.maComplexColors) {
1361
0
                aTE.writeComplexColor(rColor);
1362
0
            }
1363
1364
0
            pFS->endElement(FSNS(XML_cs, XML_colorStyle));
1365
0
        }
1366
0
    } else {
1367
        // output a default value to make MS Office happy
1368
0
        pFS->startElement(FSNS(XML_cs, XML_colorStyle),
1369
0
                FSNS( XML_xmlns, XML_cs ), pFB->getNamespaceURL(OOX_NS(cs)),
1370
0
                FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
1371
0
                XML_meth, "cycle",
1372
0
                XML_id, "10" /* no idea what this number is supposed to be */);
1373
1374
0
        pFS->singleElement(FSNS(XML_a, XML_schemeClr),
1375
0
                XML_val, "accent1");
1376
1377
0
        pFS->endElement(FSNS(XML_cs, XML_colorStyle));
1378
0
    }
1379
1380
0
    pColorStyle->endDocument();
1381
1382
0
    pChart->endDocument();
1383
0
}
1384
1385
void ChartExport::InitRangeSegmentationProperties( const Reference< chart2::XChartDocument > & xChartDoc )
1386
0
{
1387
0
    if( !xChartDoc.is())
1388
0
        return;
1389
1390
0
    try
1391
0
    {
1392
0
        Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
1393
0
        OSL_ENSURE( xDataProvider.is(), "No DataProvider" );
1394
0
        if( xDataProvider.is())
1395
0
        {
1396
0
            mbHasCategoryLabels = lcl_hasCategoryLabels( xChartDoc );
1397
0
        }
1398
0
    }
1399
0
    catch( const uno::Exception & )
1400
0
    {
1401
0
        DBG_UNHANDLED_EXCEPTION("oox");
1402
0
    }
1403
0
}
1404
1405
void ChartExport::ExportContent()
1406
0
{
1407
0
    Reference< chart2::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
1408
0
    OSL_ASSERT( xChartDoc.is() );
1409
0
    if( !xChartDoc.is() )
1410
0
        return;
1411
0
    InitRangeSegmentationProperties( xChartDoc );
1412
1413
0
    const bool bIsChartex = isChartexNotChartNS(nullptr);
1414
0
    ExportContent_( bIsChartex );
1415
0
}
1416
1417
void ChartExport::ExportContent_( bool bIsChartex )
1418
0
{
1419
0
    Reference< css::chart::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
1420
0
    if( xChartDoc.is())
1421
0
    {
1422
        // determine if data comes from the outside
1423
0
        bool bIncludeTable = true;
1424
1425
0
        Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
1426
0
        if( xNewDoc.is())
1427
0
        {
1428
            // check if we have own data.  If so we must not export the complete
1429
            // range string, as this is our only indicator for having own or
1430
            // external data. @todo: fix this in the file format!
1431
0
            Reference< lang::XServiceInfo > xDPServiceInfo( xNewDoc->getDataProvider(), uno::UNO_QUERY );
1432
0
            if( ! (xDPServiceInfo.is() && xDPServiceInfo->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" ))
1433
0
            {
1434
0
                bIncludeTable = false;
1435
0
            }
1436
0
        }
1437
0
        exportChartSpace( xChartDoc, bIncludeTable, bIsChartex );
1438
0
    }
1439
0
    else
1440
0
    {
1441
0
        OSL_FAIL( "Couldn't export chart due to wrong XModel" );
1442
0
    }
1443
0
}
1444
1445
void ChartExport::exportChartSpace( const Reference< css::chart::XChartDocument >& xChartDoc,
1446
                                    bool bIncludeTable,
1447
                                    bool bIsChartex)
1448
0
{
1449
0
    FSHelperPtr pFS = GetFS();
1450
0
    XmlFilterBase* pFB = GetFB();
1451
1452
0
    const sal_Int32 nChartNS = bIsChartex ? XML_cx : XML_c;
1453
1454
0
    if (bIsChartex) {
1455
0
        pFS->startElement( FSNS( nChartNS, XML_chartSpace ),
1456
0
                FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
1457
0
                FSNS( XML_xmlns, XML_r ), pFB->getNamespaceURL(OOX_NS(officeRel)),
1458
0
                FSNS( XML_xmlns, XML_cx ), pFB->getNamespaceURL(OOX_NS(cx)));
1459
0
    } else {
1460
0
        pFS->startElement( FSNS( nChartNS, XML_chartSpace ),
1461
0
                FSNS( XML_xmlns, XML_c ), pFB->getNamespaceURL(OOX_NS(dmlChart)),
1462
0
                FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
1463
0
                FSNS( XML_xmlns, XML_r ), pFB->getNamespaceURL(OOX_NS(officeRel)));
1464
0
    }
1465
1466
0
    if( !bIncludeTable )
1467
0
    {
1468
        // TODO:external data
1469
0
    }
1470
0
    else
1471
0
    {
1472
0
        Reference< XPropertySet > xPropSet(xChartDoc, UNO_QUERY);
1473
0
        Any aNullDate = xPropSet->getPropertyValue("NullDate");
1474
0
        util::DateTime aDate;
1475
0
        if ((aNullDate >>= aDate) && (aDate.Year == 1904 && aDate.Month == 1 && aDate.Day == 1))
1476
0
        {
1477
0
            pFS->singleElement(FSNS(XML_c, XML_date1904), XML_val, "1");
1478
0
        }
1479
0
        else
1480
0
        {
1481
0
            pFS->singleElement(FSNS(XML_c, XML_date1904), XML_val, "0");
1482
0
        }
1483
0
    }
1484
1485
    // TODO: get the correct editing language
1486
0
    if (bIsChartex) {
1487
        // chartData
1488
0
        pFS->startElement(FSNS(XML_cx, XML_chartData));
1489
1490
0
        exportExternalData(true);
1491
0
        exportData_chartex(xChartDoc);
1492
1493
0
        pFS->endElement(FSNS(XML_cx, XML_chartData));
1494
0
    } else {
1495
0
        pFS->singleElement(FSNS(XML_c, XML_lang), XML_val, "en-US");
1496
1497
0
        pFS->singleElement(FSNS(XML_c, XML_roundedCorners), XML_val, "0");
1498
0
    }
1499
1500
    // style
1501
0
    if (!bIsChartex) {
1502
0
        mxDiagram.set( xChartDoc->getDiagram() );
1503
0
        Reference< XPropertySet > xPropSet(mxDiagram, uno::UNO_QUERY);
1504
0
        if (GetProperty(xPropSet, u"StyleIndex"_ustr)) {
1505
0
            sal_Int32 nStyleIdx = -1;
1506
0
            mAny >>= nStyleIdx;
1507
0
            assert(nStyleIdx >= 0);
1508
0
            pFS->singleElement(FSNS(XML_c, XML_style), XML_val,
1509
0
                    OUString::number(nStyleIdx));
1510
0
        }
1511
0
    }
1512
1513
    //XML_chart
1514
0
    exportChart(xChartDoc, bIsChartex);
1515
1516
    // TODO: printSettings
1517
    // TODO: text properties
1518
0
    Reference< XPropertySet > xPropSet = xChartDoc->getArea();
1519
0
    if( xPropSet.is() )
1520
0
        exportShapeProps( xPropSet, nChartNS );
1521
1522
    // TODO for chartex
1523
0
    if (!bIsChartex) {
1524
        //XML_externalData
1525
0
        exportExternalData(false);
1526
0
    }
1527
1528
    // export additional shapes in chart
1529
0
    if (!bIsChartex) {
1530
0
        exportAdditionalShapes(xChartDoc);
1531
0
    }
1532
1533
0
    pFS->endElement( FSNS( nChartNS, XML_chartSpace ) );
1534
0
}
1535
1536
void ChartExport::exportData_chartex( [[maybe_unused]] const Reference< css::chart::XChartDocument >& xChartDoc)
1537
0
{
1538
0
    Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( mxNewDiagram, uno::UNO_QUERY );
1539
0
    if( ! xBCooSysCnt.is()) return;
1540
0
    const Sequence< Reference< chart2::XCoordinateSystem > >
1541
0
        aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
1542
1543
0
    if (!aCooSysSeq.hasElements()) return;
1544
1545
    // There's a possibility that multiple sub-charts in the same overall chart
1546
    // could reference the same data. In that case we don't want to output it
1547
    // multiple times. So we use this list to keep track of the ids we've
1548
    // already exported.
1549
0
    std::unordered_set<sal_Int32> aExportedIds;
1550
1551
0
    for( const auto& rCS : aCooSysSeq ) {
1552
0
        Reference< chart2::XChartTypeContainer > xCTCnt( rCS, uno::UNO_QUERY );
1553
0
        if( ! xCTCnt.is())
1554
0
            continue;
1555
0
        const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
1556
1557
0
        for( const auto& rCT : aCTSeq ) {
1558
0
            Reference< chart2::XDataSeriesContainer > xDSCnt( rCT, uno::UNO_QUERY );
1559
0
            if( ! xDSCnt.is())
1560
0
                return;
1561
0
            Reference< chart2::XChartType > xChartType( rCT, uno::UNO_QUERY );
1562
0
            if( ! xChartType.is())
1563
0
                continue;
1564
            // note: if xDSCnt.is() then also aCTSeq[nCTIdx]
1565
0
            OUString aChartType( xChartType->getChartType());
1566
0
            sal_Int32 eChartType = lcl_getChartType( aChartType );
1567
1568
0
            OUString aLabelRole = xChartType->getRoleOfSequenceForSeriesLabel();
1569
1570
0
            const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
1571
1572
0
            for (const auto& splitDataSeries : aSplitDataSeries) {
1573
0
                sal_Int32 nSeriesIndex = 0;
1574
0
                for( const auto& rSeries : splitDataSeries )
1575
0
                {
1576
                    // export series
1577
0
                    Reference< chart2::data::XDataSource > xSource( rSeries, uno::UNO_QUERY );
1578
0
                    if( !xSource.is()) continue;
1579
1580
0
                    Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
1581
0
                        xSource->getDataSequences());
1582
1583
                    // search for main sequence and create a series element
1584
0
                    sal_Int32 nMainSequenceIndex = -1;
1585
0
                    Reference< chart2::data::XDataSequence > xValueSeq;
1586
0
                    Reference< chart2::data::XDataSequence > xLabelSeq;
1587
0
                    sal_Int32 nSeqIdx=0;
1588
0
                    for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx )
1589
0
                    {
1590
0
                        Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() );
1591
0
                        if( nMainSequenceIndex==-1 )
1592
0
                        {
1593
0
                            Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY );
1594
0
                            OUString aRole;
1595
0
                            if( xSeqProp.is())
1596
0
                                xSeqProp->getPropertyValue(u"Role"_ustr) >>= aRole;
1597
                            // "main" sequence
1598
0
                            if( aRole == aLabelRole )
1599
0
                            {
1600
0
                                xValueSeq.set( xTempValueSeq );
1601
0
                                xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel());
1602
0
                                nMainSequenceIndex = nSeqIdx;
1603
0
                            }
1604
0
                        }
1605
0
                    }
1606
1607
0
                    if (aExportedIds.contains(nSeriesIndex)) {
1608
0
                        continue;
1609
0
                    } else {
1610
0
                        aExportedIds.insert(nSeriesIndex);
1611
0
                    }
1612
1613
0
                    FSHelperPtr pFS = GetFS();
1614
1615
                    // The data id needs to agree with the id in exportSeries(). See DATA_ID_COMMENT
1616
0
                    pFS->startElement(FSNS(XML_cx, XML_data), XML_id, OUString::number(nSeriesIndex));
1617
1618
                    // .xlsx chartex files seem to have this magical "_xlchart..." string,
1619
                    // and no explicit data, while .docx and .pptx contain the literal data,
1620
                    // as well as a ../embeddings file (which LO doesn't seem to produce).
1621
                    // But there's probably a smarter way to determine which pathway to take
1622
                    // than based on document type.
1623
0
                    if (GetDocumentType() == DOCUMENT_XLSX) {
1624
                        // Just hard-coding this for now
1625
1626
0
                        sal_Int32 nSuffixVal = nSeriesIndex;
1627
1628
                        // Output category data formula for some chart types.
1629
                        // (This is completely hacky)
1630
0
                        if (eChartType ==  chart::TYPEID_SUNBURST ||
1631
0
                                eChartType == chart::TYPEID_TREEMAP) {
1632
0
                            pFS->startElement(FSNS(XML_cx, XML_strDim), XML_type, "cat");
1633
0
                            pFS->startElement(FSNS(XML_cx, XML_f));
1634
1635
0
                            std::string sFormulaId = "_xlchart.v1.";
1636
0
                            sFormulaId.append(std::to_string(nSuffixVal));
1637
1638
0
                            pFS->writeEscaped(sFormulaId);
1639
1640
0
                            pFS->endElement(FSNS(XML_cx, XML_f));
1641
0
                            pFS->endElement(FSNS(XML_cx, XML_strDim));
1642
1643
0
                            ++nSuffixVal;
1644
0
                        }
1645
1646
                        // Set the ST_NumericDimensionType. For some (stupid?)
1647
                        // reason, MSO requires the value data for sunburst and
1648
                        // treemap to be type "size", while for most other chart
1649
                        // types it's of type "val".
1650
0
                        std::string sNumDimType;
1651
0
                        if (eChartType ==  chart::TYPEID_SUNBURST ||
1652
0
                                eChartType == chart::TYPEID_TREEMAP) {
1653
0
                            sNumDimType = "size";
1654
0
                        } else {
1655
0
                            sNumDimType = "val";
1656
0
                        }
1657
1658
                        // Now output value data formula
1659
0
                        pFS->startElement(FSNS(XML_cx, XML_numDim), XML_type,
1660
0
                                sNumDimType.c_str());
1661
0
                        pFS->startElement(FSNS(XML_cx, XML_f));
1662
1663
                        // Set the formula value based on the chart type. This
1664
                        // is just a hack. It obviously should be based on the
1665
                        // identifier for the actual data (either as imported or
1666
                        // created within LO). TODO
1667
0
                        std::string sFormulaId;
1668
0
                        switch( eChartType )
1669
0
                        {
1670
0
                            case chart::TYPEID_BOXWHISKER:
1671
0
                            case chart::TYPEID_CLUSTEREDCOLUMN:
1672
0
                            case chart::TYPEID_PARETOLINE:
1673
0
                            case chart::TYPEID_SUNBURST:
1674
0
                            case chart::TYPEID_TREEMAP:
1675
0
                            case chart::TYPEID_WATERFALL:
1676
0
                                sFormulaId = "_xlchart.v1.";
1677
0
                                break;
1678
0
                            case chart::TYPEID_FUNNEL:
1679
0
                                sFormulaId = "_xlchart.v2.";
1680
0
                                break;
1681
0
                            case chart::TYPEID_REGIONMAP:
1682
0
                                sFormulaId = "_xlchart.v5.";
1683
0
                                break;
1684
0
                            default:
1685
0
                                assert(false);
1686
0
                                break;
1687
0
                        }
1688
                        // Append the id value, which seems (?) to be what
1689
                        // follows the period in the string. E.g., for id=2 we
1690
                        // might end up with "_xlchart.v1.2"
1691
0
                        sFormulaId.append(std::to_string(nSuffixVal));
1692
1693
0
                        pFS->writeEscaped(sFormulaId);
1694
1695
0
                        pFS->endElement(FSNS(XML_cx, XML_f));
1696
0
                        pFS->endElement(FSNS(XML_cx, XML_numDim));
1697
0
                    } else {    // PPTX, DOCX
1698
0
                        OUString aCellRange = mxCategoriesValues.is() ? mxCategoriesValues->getSourceRangeRepresentation() : OUString();
1699
0
#undef OUTPUT_SPLIT_CATEGORIES  // do we need this or not? TODO
1700
#ifdef OUTPUT_SPLIT_CATEGORIES
1701
                        const Sequence< Sequence< OUString >> aFinalSplitSource = getSplitCategoriesList(aCellRange);
1702
#endif
1703
0
                        aCellRange = parseFormula( aCellRange );
1704
1705
#ifdef OUTPUT_SPLIT_CATEGORIES
1706
                        if (aFinalSplitSource.getLength() > 1) {
1707
1708
                            // export multi level category axis labels
1709
                            pFS->startElement(FSNS(XML_cx, XML_strDim), XML_type, "cat");
1710
1711
                            pFS->startElement(FSNS(XML_cx, XML_f));
1712
                            pFS->writeEscaped(aCellRange);
1713
                            pFS->endElement(FSNS(XML_cx, XML_f));
1714
1715
                            for (const auto& rSeq : aFinalSplitSource) {
1716
                                pFS->startElement(FSNS(XML_cx, XML_lvl),
1717
                                        XML_ptCount, OString::number(aFinalSplitSource[0].getLength()));
1718
1719
                                for (sal_Int32 j = 0; j < rSeq.getLength(); j++) {
1720
                                    if(!rSeq[j].isEmpty()) {
1721
                                        pFS->startElement(FSNS(XML_cx, XML_pt), XML_idx, OString::number(j));
1722
                                        pFS->writeEscaped(rSeq[j]);
1723
                                        pFS->endElement(FSNS(XML_cx, XML_pt));
1724
                                    }
1725
                                }
1726
                                pFS->endElement(FSNS(XML_cx, XML_lvl));
1727
                            }
1728
1729
                            pFS->endElement(FSNS(XML_cx, XML_strDim));
1730
                        }
1731
                        else
1732
#endif
1733
0
                        {
1734
                            // export single category axis labels
1735
                            // TODO: seems like this should consider mbHasCategoryLabels
1736
0
                            bool bWriteDateCategories = mbHasDateCategories;
1737
0
                            OUString aNumberFormatString;
1738
0
                            if (bWriteDateCategories)
1739
0
                            {
1740
0
                                Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY );
1741
0
                                if( xAxisXSupp.is())
1742
0
                                {
1743
0
                                    Reference< XPropertySet > xAxisProp = xAxisXSupp->getXAxis();
1744
0
                                    if (GetProperty(xAxisProp, u"NumberFormat"_ustr))
1745
0
                                    {
1746
0
                                        sal_Int32 nKey = 0;
1747
0
                                        mAny >>= nKey;
1748
0
                                        aNumberFormatString = getNumberFormatCode(nKey);
1749
0
                                    }
1750
0
                                }
1751
0
                                if (aNumberFormatString.isEmpty()) bWriteDateCategories = false;
1752
0
                            }
1753
1754
                            // === Output the categories
1755
0
                            if (bWriteDateCategories)
1756
0
                            {
1757
0
                                std::vector<double> aDateCategories = lcl_getAllValuesFromSequence(xValueSeq);
1758
0
                                const sal_Int32 ptCount = aDateCategories.size();
1759
1760
0
                                pFS->startElement(FSNS(XML_cx, XML_numDim), XML_type, "x"); // is "x" right?
1761
                                // TODO: check this
1762
1763
0
                                pFS->startElement(FSNS(XML_cx, XML_f));
1764
0
                                pFS->writeEscaped(aCellRange);
1765
0
                                pFS->endElement(FSNS(XML_cx, XML_f));
1766
1767
0
                                pFS->startElement(FSNS(XML_cx, XML_lvl),
1768
0
                                        XML_ptCount, OString::number(ptCount),
1769
0
                                        XML_formatCode, aNumberFormatString);
1770
1771
0
                                for (sal_Int32 i = 0; i < ptCount; i++) {
1772
0
                                    if (!std::isnan(aDateCategories[i])) {
1773
0
                                        pFS->startElement(FSNS(XML_cx, XML_pt), XML_idx, OString::number(i));
1774
0
                                        pFS->write(OString::number(aDateCategories[i]));
1775
0
                                        pFS->endElement(FSNS(XML_cx, XML_pt));
1776
0
                                    }
1777
0
                                }
1778
1779
0
                                pFS->endElement(FSNS(XML_cx, XML_lvl));
1780
0
                                pFS->endElement(FSNS(XML_cx, XML_numDim));
1781
0
                            }
1782
0
                            else
1783
0
                            {
1784
0
                                std::vector<OUString> aCategories;
1785
0
                                lcl_fillCategoriesIntoStringVector(xValueSeq, aCategories);
1786
0
                                const sal_Int32 ptCount = aCategories.size();
1787
1788
                                // TODO: shouldn't have "cat" hard-coded here:
1789
                                // other options are colorStr, entityId
1790
0
                                pFS->startElement(FSNS(XML_cx, XML_strDim), XML_type, "cat");
1791
1792
0
                                pFS->startElement(FSNS(XML_cx, XML_f));
1793
0
                                pFS->writeEscaped(aCellRange);
1794
0
                                pFS->endElement(FSNS(XML_cx, XML_f));
1795
1796
0
                                pFS->startElement(FSNS(XML_cx, XML_lvl), XML_ptCount, OString::number(ptCount));
1797
1798
0
                                for (sal_Int32 i = 0; i < ptCount; i++) {
1799
0
                                    pFS->startElement(FSNS(XML_cx, XML_pt), XML_idx, OString::number(i));
1800
0
                                    pFS->writeEscaped(aCategories[i]);
1801
0
                                    pFS->endElement(FSNS(XML_cx, XML_pt));
1802
0
                                }
1803
1804
0
                                pFS->endElement(FSNS(XML_cx, XML_lvl));
1805
0
                                pFS->endElement(FSNS(XML_cx, XML_strDim));
1806
0
                            }
1807
1808
                            // === Output the values
1809
0
                            pFS->startElement(FSNS(XML_cx, XML_numDim), XML_type, "val");
1810
1811
0
                            aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString();
1812
0
                            aCellRange = parseFormula( aCellRange );
1813
                            // TODO: need to handle XML_multiLvlStrRef according to aCellRange
1814
1815
0
                            pFS->startElement(FSNS(XML_cx, XML_f));
1816
0
                            pFS->writeEscaped( aCellRange );
1817
0
                            pFS->endElement( FSNS( XML_cx, XML_f ) );
1818
1819
0
                            ::std::vector< double > aValues = lcl_getAllValuesFromSequence( xValueSeq );
1820
0
                            sal_Int32 ptCount = aValues.size();
1821
0
                            OUString sNumberFormatString(u"General"_ustr);
1822
0
                            const sal_Int32 nKey = xValueSeq.is() ? xValueSeq->getNumberFormatKeyByIndex(-1) : 0;
1823
0
                            if (nKey > 0) {
1824
0
                                sNumberFormatString = getNumberFormatCode(nKey);
1825
0
                            }
1826
0
                            pFS->startElement(FSNS(XML_cx, XML_lvl),
1827
0
                                    XML_ptCount, OString::number(ptCount),
1828
0
                                    XML_formatCode,  sNumberFormatString);
1829
1830
0
                            for( sal_Int32 i = 0; i < ptCount; i++ ) {
1831
1832
0
                                pFS->startElement(FSNS(XML_cx, XML_pt), XML_idx, OString::number(i));
1833
0
                                pFS->write(std::isnan(aValues[i]) ?  0 : aValues[i]);
1834
0
                                pFS->endElement(FSNS(XML_cx, XML_pt));
1835
0
                            }
1836
1837
0
                            pFS->endElement(FSNS(XML_cx, XML_lvl));
1838
0
                            pFS->endElement(FSNS(XML_cx, XML_numDim));
1839
0
                        }
1840
0
                    }
1841
0
                    pFS->endElement(FSNS(XML_cx, XML_data));
1842
1843
0
                    ++nSeriesIndex;
1844
0
                }
1845
0
            }
1846
0
        }
1847
0
    }
1848
0
}
1849
1850
OUString ChartExport::GetExternalDataPath() const
1851
0
{
1852
0
    OUString sRet;
1853
1854
0
    const Reference<css::chart::XChartDocument> xChartDoc(getModel(), uno::UNO_QUERY);
1855
0
    if (!xChartDoc.is())
1856
0
        return sRet;
1857
1858
0
    const Reference<beans::XPropertySet> xDocPropSet(xChartDoc->getDiagram(), uno::UNO_QUERY);
1859
0
    if (!xDocPropSet.is())
1860
0
        return sRet;
1861
1862
0
    try
1863
0
    {
1864
0
        Any aAny(xDocPropSet->getPropertyValue(u"ExternalData"_ustr));
1865
0
        aAny >>= sRet;
1866
0
    }
1867
0
    catch(beans::UnknownPropertyException&)
1868
0
    {
1869
0
    }
1870
1871
0
    return sRet;
1872
0
}
1873
1874
void ChartExport::exportExternalData(bool bIsChartex)
1875
0
{
1876
0
    if (bIsChartex) return; // TODO!!
1877
    // Embedded external data is grab bagged for docx file hence adding export part of
1878
    // external data for docx files only.
1879
0
    if(!mbLinkToExternalData || GetDocumentType() != DOCUMENT_DOCX)
1880
0
        return;
1881
1882
0
    const OUString externalDataPath = GetExternalDataPath();
1883
0
    if(externalDataPath.isEmpty())
1884
0
        return;
1885
1886
    // Here adding external data entry to relationship.
1887
0
    OUString relationPath = externalDataPath;
1888
    // Converting absolute path to relative path.
1889
0
    if( externalDataPath[ 0 ] != '.' && externalDataPath[ 1 ] != '.')
1890
0
    {
1891
0
        sal_Int32 nSepPos = externalDataPath.indexOf( '/', 0 );
1892
0
        if( nSepPos > 0)
1893
0
        {
1894
0
            relationPath = relationPath.copy( nSepPos,  ::std::max< sal_Int32 >( externalDataPath.getLength(), 0 ) -  nSepPos );
1895
0
            relationPath = ".." + relationPath;
1896
0
        }
1897
0
    }
1898
0
    FSHelperPtr pFS = GetFS();
1899
0
    OUString type = oox::getRelationship(Relationship::PACKAGE);
1900
0
    if (relationPath.endsWith(".bin"))
1901
0
        type = oox::getRelationship(Relationship::OLEOBJECT);
1902
1903
0
    OUString sRelId = GetFB()->addRelation(pFS->getOutputStream(),
1904
0
                    type,
1905
0
                    relationPath);
1906
0
    pFS->singleElementNS(XML_c, XML_externalData, FSNS(XML_r, XML_id), sRelId);
1907
0
}
1908
1909
void ChartExport::exportAdditionalShapes( const Reference< css::chart::XChartDocument >& xChartDoc )
1910
0
{
1911
    // Not used in chartex
1912
1913
0
    Reference< beans::XPropertySet > xDocPropSet(xChartDoc, uno::UNO_QUERY);
1914
0
    if (!xDocPropSet.is())
1915
0
        return;
1916
1917
0
    css::uno::Reference< css::drawing::XShapes > mxAdditionalShapes;
1918
    // get a sequence of non-chart shapes
1919
0
    try
1920
0
    {
1921
0
        Any aShapesAny = xDocPropSet->getPropertyValue(u"AdditionalShapes"_ustr);
1922
0
        if( (aShapesAny >>= mxAdditionalShapes) && mxAdditionalShapes.is() )
1923
0
        {
1924
0
            OUString sId;
1925
0
            const char* sFullPath = nullptr;
1926
0
            const char* sRelativePath = nullptr;
1927
0
            sal_Int32 nDrawing = GetFB()->getNewDrawingUniqueId();
1928
1929
0
            switch (GetDocumentType())
1930
0
            {
1931
0
                case DOCUMENT_DOCX:
1932
0
                {
1933
0
                    sFullPath = "word/drawings/drawing";
1934
0
                    sRelativePath = "../drawings/drawing";
1935
0
                    break;
1936
0
                }
1937
0
                case DOCUMENT_PPTX:
1938
0
                {
1939
0
                    sFullPath = "ppt/drawings/drawing";
1940
0
                    sRelativePath = "../drawings/drawing";
1941
0
                    break;
1942
0
                }
1943
0
                case DOCUMENT_XLSX:
1944
0
                {
1945
0
                    sFullPath = "xl/drawings/drawing";
1946
0
                    sRelativePath = "../drawings/drawing";
1947
0
                    break;
1948
0
                }
1949
0
                default:
1950
0
                {
1951
0
                    sFullPath = "drawings/drawing";
1952
0
                    sRelativePath = "drawings/drawing";
1953
0
                    break;
1954
0
                }
1955
0
            }
1956
0
            OUString sFullStream = OUStringBuffer()
1957
0
                .appendAscii(sFullPath)
1958
0
                .append(OUString::number(nDrawing) + ".xml")
1959
0
                .makeStringAndClear();
1960
0
            OUString sRelativeStream = OUStringBuffer()
1961
0
                .appendAscii(sRelativePath)
1962
0
                .append(OUString::number(nDrawing) + ".xml")
1963
0
                .makeStringAndClear();
1964
1965
0
            sax_fastparser::FSHelperPtr pDrawing = CreateOutputStream(
1966
0
                sFullStream,
1967
0
                sRelativeStream,
1968
0
                GetFS()->getOutputStream(),
1969
0
                u"application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml"_ustr,
1970
0
                oox::getRelationship(Relationship::CHARTUSERSHAPES),
1971
0
                &sId);
1972
1973
0
            GetFS()->singleElementNS(XML_c, XML_userShapes, FSNS(XML_r, XML_id), sId);
1974
1975
0
            XmlFilterBase* pFB = GetFB();
1976
0
            pDrawing->startElement(FSNS(XML_c, XML_userShapes),
1977
0
                FSNS(XML_xmlns, XML_cdr), pFB->getNamespaceURL(OOX_NS(dmlChartDr)),
1978
0
                FSNS(XML_xmlns, XML_a), pFB->getNamespaceURL(OOX_NS(dml)),
1979
0
                FSNS(XML_xmlns, XML_c), pFB->getNamespaceURL(OOX_NS(dmlChart)),
1980
0
                FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)));
1981
1982
0
            const sal_Int32 nShapeCount(mxAdditionalShapes->getCount());
1983
0
            for (sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++)
1984
0
            {
1985
0
                Reference< drawing::XShape > xShape;
1986
0
                mxAdditionalShapes->getByIndex(nShapeId) >>= xShape;
1987
0
                SAL_WARN_IF(!xShape.is(), "xmloff.chart", "Shape without an XShape?");
1988
0
                if (!xShape.is())
1989
0
                    continue;
1990
1991
                // TODO: absSizeAnchor: we import both (absSizeAnchor and relSizeAnchor), but there is no essential difference between them.
1992
0
                pDrawing->startElement(FSNS(XML_cdr, XML_relSizeAnchor));
1993
0
                uno::Reference< beans::XPropertySet > xShapeProperties(xShape, uno::UNO_QUERY);
1994
0
                if( xShapeProperties.is() )
1995
0
                {
1996
0
                    Reference<embed::XVisualObject> xVisObject(mxChartModel, uno::UNO_QUERY);
1997
0
                    awt::Size aPageSize = xVisObject->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT);
1998
0
                    WriteFromTo( xShape, aPageSize, pDrawing );
1999
2000
0
                    ShapeExport aExport(XML_cdr, pDrawing, nullptr, GetFB(), GetDocumentType(), nullptr, true);
2001
0
                    aExport.WriteShape(xShape);
2002
0
                }
2003
0
                pDrawing->endElement(FSNS(XML_cdr, XML_relSizeAnchor));
2004
0
            }
2005
0
            pDrawing->endElement(FSNS(XML_c, XML_userShapes));
2006
0
            pDrawing->endDocument();
2007
0
        }
2008
0
    }
2009
0
    catch (const uno::Exception&)
2010
0
    {
2011
0
        TOOLS_INFO_EXCEPTION("xmloff.chart", "AdditionalShapes not found");
2012
0
    }
2013
0
}
2014
2015
void ChartExport::exportChart( const Reference< css::chart::XChartDocument >& xChartDoc,
2016
        bool bIsChartex)
2017
0
{
2018
0
    Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
2019
0
    mxDiagram.set( xChartDoc->getDiagram() );
2020
0
    if( xNewDoc.is()) {
2021
0
        mxNewDiagram.set( xNewDoc->getFirstDiagram());
2022
0
    }
2023
2024
    // get Properties of ChartDocument
2025
0
    bool bHasMainTitle = false;
2026
0
    bool bHasLegend = false;
2027
0
    Reference< beans::XPropertySet > xDocPropSet( xChartDoc, uno::UNO_QUERY );
2028
0
    if( xDocPropSet.is())
2029
0
    {
2030
0
        try
2031
0
        {
2032
0
            Any aAny( xDocPropSet->getPropertyValue(u"HasMainTitle"_ustr));
2033
0
            aAny >>= bHasMainTitle;
2034
0
            aAny = xDocPropSet->getPropertyValue(u"HasLegend"_ustr);
2035
0
            aAny >>= bHasLegend;
2036
0
        }
2037
0
        catch( beans::UnknownPropertyException & )
2038
0
        {
2039
0
            SAL_WARN("oox", "Required property not found in ChartDocument");
2040
0
        }
2041
0
    } // if( xDocPropSet.is())
2042
2043
0
    Sequence< uno::Reference< chart2::XFormattedString > > xFormattedSubTitle;
2044
0
    Reference< beans::XPropertySet > xPropSubTitle( xChartDoc->getSubTitle(), UNO_QUERY );
2045
0
    if( xPropSubTitle.is())
2046
0
    {
2047
0
        OUString aSubTitle;
2048
0
        if ((xPropSubTitle->getPropertyValue(u"String"_ustr) >>= aSubTitle) && !aSubTitle.isEmpty())
2049
0
            xPropSubTitle->getPropertyValue(u"FormattedStrings"_ustr) >>= xFormattedSubTitle;
2050
0
    }
2051
2052
    // chart element
2053
0
    FSHelperPtr pFS = GetFS();
2054
2055
0
    const sal_Int32 nChartNS = bIsChartex ? XML_cx : XML_c;
2056
0
    pFS->startElement(FSNS(nChartNS, XML_chart));
2057
2058
    // titles
2059
0
    if( bHasMainTitle )
2060
0
    {
2061
0
        exportTitle( xChartDoc->getTitle(), bIsChartex, xFormattedSubTitle);
2062
0
        if (!bIsChartex) {
2063
0
            pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
2064
0
        }
2065
0
    }
2066
0
    else if( xFormattedSubTitle.hasElements() )
2067
0
    {
2068
0
        exportTitle( xChartDoc->getSubTitle(), bIsChartex );
2069
0
        if (!bIsChartex) {
2070
0
            pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
2071
0
        }
2072
0
    }
2073
0
    else if (!bIsChartex) {
2074
0
        pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "1");
2075
0
    }
2076
2077
0
    InitPlotArea( );
2078
0
    if( mbIs3DChart )
2079
0
    {
2080
0
        if (!bIsChartex) {
2081
0
            exportView3D();
2082
2083
            // floor
2084
0
            Reference< beans::XPropertySet > xFloor = mxNewDiagram->getFloor();
2085
0
            if( xFloor.is() )
2086
0
            {
2087
0
                pFS->startElement(FSNS(XML_c, XML_floor));
2088
0
                exportShapeProps( xFloor, XML_c );
2089
0
                pFS->endElement( FSNS( XML_c, XML_floor ) );
2090
0
            }
2091
2092
            // LibreOffice doesn't distinguish between sideWall and backWall (both are using the same color).
2093
            // It is controlled by the same Wall property.
2094
0
            Reference< beans::XPropertySet > xWall = mxNewDiagram->getWall();
2095
0
            if( xWall.is() )
2096
0
            {
2097
                // sideWall
2098
0
                pFS->startElement(FSNS(XML_c, XML_sideWall));
2099
0
                exportShapeProps( xWall, XML_c );
2100
0
                pFS->endElement( FSNS( XML_c, XML_sideWall ) );
2101
2102
                // backWall
2103
0
                pFS->startElement(FSNS(XML_c, XML_backWall));
2104
0
                exportShapeProps( xWall, XML_c );
2105
0
                pFS->endElement( FSNS( XML_c, XML_backWall ) );
2106
0
            }
2107
0
        }
2108
0
    }
2109
    // plot area
2110
0
    exportPlotArea( xChartDoc, bIsChartex );
2111
    // legend
2112
0
    if( bHasLegend ) {
2113
0
        exportLegend( xChartDoc, bIsChartex );
2114
0
    }
2115
2116
0
    if (!bIsChartex) {
2117
0
        uno::Reference<beans::XPropertySet> xDiagramPropSet(xChartDoc->getDiagram(), uno::UNO_QUERY);
2118
0
        uno::Any aPlotVisOnly = xDiagramPropSet->getPropertyValue(u"IncludeHiddenCells"_ustr);
2119
0
        bool bIncludeHiddenCells = false;
2120
0
        aPlotVisOnly >>= bIncludeHiddenCells;
2121
0
        pFS->singleElement(FSNS(XML_c, XML_plotVisOnly), XML_val, ToPsz10(!bIncludeHiddenCells));
2122
2123
0
        exportMissingValueTreatment(Reference<beans::XPropertySet>(mxDiagram, uno::UNO_QUERY));
2124
0
    }
2125
2126
0
    pFS->endElement( FSNS( nChartNS, XML_chart ) );
2127
0
}
2128
2129
void ChartExport::exportMissingValueTreatment(const uno::Reference<beans::XPropertySet>& xPropSet)
2130
0
{
2131
0
    if (!xPropSet.is())
2132
0
        return;
2133
2134
0
    sal_Int32 nVal = 0;
2135
0
    uno::Any aAny = xPropSet->getPropertyValue(u"MissingValueTreatment"_ustr);
2136
0
    if (!(aAny >>= nVal))
2137
0
        return;
2138
2139
0
    const char* pVal = nullptr;
2140
0
    switch (nVal)
2141
0
    {
2142
0
        case cssc::MissingValueTreatment::LEAVE_GAP:
2143
0
            pVal = "gap";
2144
0
        break;
2145
0
        case cssc::MissingValueTreatment::USE_ZERO:
2146
0
            pVal = "zero";
2147
0
        break;
2148
0
        case cssc::MissingValueTreatment::CONTINUE:
2149
0
            pVal = "span";
2150
0
        break;
2151
0
        default:
2152
0
            SAL_WARN("oox", "unknown MissingValueTreatment value");
2153
0
        break;
2154
0
    }
2155
2156
0
    FSHelperPtr pFS = GetFS();
2157
0
    pFS->singleElement(FSNS(XML_c, XML_dispBlanksAs), XML_val, pVal);
2158
0
}
2159
2160
void ChartExport::exportLegend( const Reference< css::chart::XChartDocument >& xChartDoc,
2161
        bool bIsChartex)
2162
0
{
2163
0
    FSHelperPtr pFS = GetFS();
2164
2165
0
    Reference< beans::XPropertySet > xProp( xChartDoc->getLegend(), uno::UNO_QUERY );
2166
0
    if( xProp.is() )
2167
0
    {
2168
0
        if (!bIsChartex) {
2169
0
            pFS->startElement(FSNS(XML_c, XML_legend));
2170
0
        }
2171
2172
        // position
2173
0
        css::chart::ChartLegendPosition aLegendPos = css::chart::ChartLegendPosition_NONE;
2174
0
        try
2175
0
        {
2176
0
            Any aAny( xProp->getPropertyValue( u"Alignment"_ustr ));
2177
0
            aAny >>= aLegendPos;
2178
0
        }
2179
0
        catch( beans::UnknownPropertyException & )
2180
0
        {
2181
0
            SAL_WARN("oox", "Property Align not found in ChartLegend");
2182
0
        }
2183
2184
0
        const char* strPos = nullptr;
2185
0
        switch( aLegendPos )
2186
0
        {
2187
0
            case css::chart::ChartLegendPosition_LEFT:
2188
0
                strPos = "l";
2189
0
                break;
2190
0
            case css::chart::ChartLegendPosition_RIGHT:
2191
0
                strPos = "r";
2192
0
                break;
2193
0
            case css::chart::ChartLegendPosition_TOP:
2194
0
                strPos = "t";
2195
0
                break;
2196
0
            case css::chart::ChartLegendPosition_BOTTOM:
2197
0
                strPos = "b";
2198
0
                break;
2199
0
            case css::chart::ChartLegendPosition_NONE:
2200
0
            case css::chart::ChartLegendPosition::ChartLegendPosition_MAKE_FIXED_SIZE:
2201
                // nothing
2202
0
                break;
2203
0
        }
2204
2205
0
        if (!bIsChartex) {
2206
0
            if( strPos != nullptr )
2207
0
            {
2208
0
                pFS->singleElement(FSNS(XML_c, XML_legendPos), XML_val, strPos);
2209
0
            }
2210
2211
            // legendEntry
2212
0
            Reference<chart2::XCoordinateSystemContainer> xCooSysContainer(mxNewDiagram, UNO_QUERY_THROW);
2213
0
            const Sequence<Reference<chart2::XCoordinateSystem>> xCooSysSequence(xCooSysContainer->getCoordinateSystems());
2214
2215
0
            sal_Int32 nIndex = 0;
2216
0
            bool bShowLegendEntry;
2217
0
            for (const auto& rCooSys : xCooSysSequence)
2218
0
            {
2219
0
                PropertySet aCooSysProp(rCooSys);
2220
0
                bool bSwapXAndY = aCooSysProp.getBoolProperty(PROP_SwapXAndYAxis);
2221
2222
0
                Reference<chart2::XChartTypeContainer> xChartTypeContainer(rCooSys, UNO_QUERY_THROW);
2223
0
                const Sequence<Reference<chart2::XChartType>> xChartTypeSequence(xChartTypeContainer->getChartTypes());
2224
0
                if (!xChartTypeSequence.hasElements())
2225
0
                    continue;
2226
2227
0
                for (const auto& rCT : xChartTypeSequence)
2228
0
                {
2229
0
                    Reference<chart2::XDataSeriesContainer> xDSCont(rCT, UNO_QUERY);
2230
0
                    if (!xDSCont.is())
2231
0
                        continue;
2232
2233
0
                    OUString aChartType(rCT->getChartType());
2234
0
                    bool bIsPie = lcl_getChartType(aChartType) == chart::TYPEID_PIE;
2235
0
                    if (bIsPie)
2236
0
                    {
2237
0
                        PropertySet xChartTypeProp(rCT);
2238
0
                        bIsPie = !xChartTypeProp.getBoolProperty(PROP_UseRings);
2239
0
                    }
2240
0
                    const Sequence<Reference<chart2::XDataSeries>> aDataSeriesSeq = xDSCont->getDataSeries();
2241
0
                    if (bSwapXAndY)
2242
0
                        nIndex += aDataSeriesSeq.getLength() - 1;
2243
0
                    for (const auto& rDataSeries : aDataSeriesSeq)
2244
0
                    {
2245
0
                        PropertySet aSeriesProp(rDataSeries);
2246
0
                        bool bVaryColorsByPoint = aSeriesProp.getBoolProperty(PROP_VaryColorsByPoint);
2247
0
                        if (bVaryColorsByPoint || bIsPie)
2248
0
                        {
2249
0
                            Sequence<sal_Int32> deletedLegendEntriesSeq;
2250
0
                            aSeriesProp.getProperty(deletedLegendEntriesSeq, PROP_DeletedLegendEntries);
2251
0
                            for (const auto& deletedLegendEntry : std::as_const(deletedLegendEntriesSeq))
2252
0
                            {
2253
0
                                pFS->startElement(FSNS(XML_c, XML_legendEntry));
2254
0
                                pFS->singleElement(FSNS(XML_c, XML_idx), XML_val,
2255
0
                                                   OString::number(nIndex + deletedLegendEntry));
2256
0
                                pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, "1");
2257
0
                                pFS->endElement(FSNS(XML_c, XML_legendEntry));
2258
0
                            }
2259
0
                            Reference<chart2::data::XDataSource> xDSrc(rDataSeries, UNO_QUERY);
2260
0
                            if (!xDSrc.is())
2261
0
                                continue;
2262
2263
0
                            const Sequence<Reference<chart2::data::XLabeledDataSequence>> aDataSeqs = xDSrc->getDataSequences();
2264
0
                            for (const auto& rDataSeq : aDataSeqs)
2265
0
                            {
2266
0
                                Reference<chart2::data::XDataSequence> xValues = rDataSeq->getValues();
2267
0
                                if (!xValues.is())
2268
0
                                    continue;
2269
2270
0
                                sal_Int32 nDataSeqSize = xValues->getData().getLength();
2271
0
                                nIndex += nDataSeqSize;
2272
0
                            }
2273
0
                        }
2274
0
                        else
2275
0
                        {
2276
0
                            bShowLegendEntry = aSeriesProp.getBoolProperty(PROP_ShowLegendEntry);
2277
0
                            if (!bShowLegendEntry)
2278
0
                            {
2279
0
                                pFS->startElement(FSNS(XML_c, XML_legendEntry));
2280
0
                                pFS->singleElement(FSNS(XML_c, XML_idx), XML_val,
2281
0
                                                   OString::number(nIndex));
2282
0
                                pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, "1");
2283
0
                                pFS->endElement(FSNS(XML_c, XML_legendEntry));
2284
0
                            }
2285
0
                            bSwapXAndY ? nIndex-- : nIndex++;
2286
0
                        }
2287
0
                    }
2288
0
                    if (bSwapXAndY)
2289
0
                        nIndex += aDataSeriesSeq.getLength() + 1;
2290
0
                }
2291
0
            }
2292
2293
0
            uno::Any aRelativePos = xProp->getPropertyValue(u"RelativePosition"_ustr);
2294
0
            if (aRelativePos.hasValue())
2295
0
            {
2296
0
                pFS->startElement(FSNS(XML_c, XML_layout));
2297
0
                pFS->startElement(FSNS(XML_c, XML_manualLayout));
2298
2299
0
                pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
2300
0
                pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
2301
0
                chart2::RelativePosition aPos = aRelativePos.get<chart2::RelativePosition>();
2302
2303
0
                const double x = aPos.Primary;
2304
0
                const double y = aPos.Secondary;
2305
2306
0
                pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
2307
0
                pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
2308
2309
0
                uno::Any aRelativeSize = xProp->getPropertyValue(u"RelativeSize"_ustr);
2310
0
                if (aRelativeSize.hasValue())
2311
0
                {
2312
0
                    chart2::RelativeSize aSize = aRelativeSize.get<chart2::RelativeSize>();
2313
2314
0
                    const double w = aSize.Primary;
2315
0
                    const double h = aSize.Secondary;
2316
2317
0
                    pFS->singleElement(FSNS(XML_c, XML_w), XML_val, OString::number(w));
2318
2319
0
                    pFS->singleElement(FSNS(XML_c, XML_h), XML_val, OString::number(h));
2320
0
                }
2321
2322
0
                SAL_WARN_IF(aPos.Anchor != css::drawing::Alignment_TOP_LEFT, "oox", "unsupported anchor position");
2323
2324
0
                pFS->endElement(FSNS(XML_c, XML_manualLayout));
2325
0
                pFS->endElement(FSNS(XML_c, XML_layout));
2326
0
            }
2327
0
        }
2328
2329
0
        const char *sOverlay = nullptr;
2330
0
        if (strPos != nullptr)
2331
0
        {
2332
0
            uno::Any aOverlay = xProp->getPropertyValue(u"Overlay"_ustr);
2333
0
            if(aOverlay.get<bool>())
2334
0
                sOverlay = "1";
2335
0
            else
2336
0
                sOverlay = "0";
2337
0
        }
2338
2339
0
        if (bIsChartex) {
2340
0
            pFS->startElement(FSNS(XML_cx, XML_legend),
2341
0
                    XML_pos, strPos ? strPos : "r",
2342
0
                    XML_align, "ctr",   // is this supported?
2343
0
                    XML_overlay, sOverlay ? sOverlay : "0");
2344
0
        } else {
2345
0
            pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, sOverlay);
2346
0
        }
2347
2348
        // shape properties
2349
0
        exportShapeProps( xProp, bIsChartex ? XML_cx : XML_c );
2350
2351
        // draw-chart:txPr text properties
2352
0
        exportTextProps( xProp, bIsChartex );
2353
2354
0
        if (bIsChartex) {
2355
0
            pFS->endElement( FSNS( XML_cx, XML_legend ) );
2356
0
        } else {
2357
0
            pFS->endElement( FSNS( XML_c, XML_legend ) );
2358
0
        }
2359
0
    }
2360
0
}
2361
2362
void ChartExport::exportTitle( const Reference< XShape >& xShape, bool bIsChartex,
2363
    const css::uno::Sequence< uno::Reference< css::chart2::XFormattedString > >& xFormattedSubTitle )
2364
0
{
2365
0
    Sequence< uno::Reference< chart2::XFormattedString > > xFormattedTitle;
2366
0
    Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
2367
0
    if( xPropSet.is())
2368
0
    {
2369
0
        OUString aTitle;
2370
0
        if ((xPropSet->getPropertyValue(u"String"_ustr) >>= aTitle) && !aTitle.isEmpty())
2371
0
            xPropSet->getPropertyValue(u"FormattedStrings"_ustr) >>= xFormattedTitle;
2372
0
    }
2373
2374
    // tdf#101322: add subtitle to title
2375
0
    if (xFormattedSubTitle.hasElements())
2376
0
    {
2377
0
        if (!xFormattedTitle.hasElements())
2378
0
        {
2379
0
            xFormattedTitle = xFormattedSubTitle;
2380
0
        }
2381
0
        else
2382
0
        {
2383
0
            sal_uInt32 nLength = xFormattedTitle.size();
2384
0
            const OUString aLastString = xFormattedTitle.getArray()[nLength - 1]->getString();
2385
0
            xFormattedTitle.getArray()[nLength - 1]->setString(aLastString + OUStringChar('\n'));
2386
0
            for (const uno::Reference<chart2::XFormattedString>& rxFS : xFormattedSubTitle)
2387
0
            {
2388
0
                if (!rxFS->getString().isEmpty())
2389
0
                {
2390
0
                    xFormattedTitle.realloc(nLength + 1);
2391
0
                    xFormattedTitle.getArray()[nLength++] = rxFS;
2392
0
                }
2393
0
            }
2394
0
        }
2395
0
    }
2396
2397
0
    if (!xFormattedTitle.hasElements())
2398
0
        return;
2399
2400
0
    FSHelperPtr pFS = GetFS();
2401
2402
0
    if (bIsChartex) {
2403
0
        pFS->startElement(FSNS(XML_cx, XML_title));
2404
0
        lcl_writeChartexString(pFS, xFormattedTitle[0]->getString());
2405
0
    } else {
2406
0
        pFS->startElement(FSNS(XML_c, XML_title));
2407
0
        pFS->startElement(FSNS(XML_c, XML_tx));
2408
0
        pFS->startElement(FSNS(XML_c, XML_rich));
2409
0
    }
2410
2411
0
    if (bIsChartex) {
2412
        // shape properties
2413
0
        if( xPropSet.is() )
2414
0
        {
2415
0
            exportShapeProps(xPropSet, XML_cx);
2416
0
        }
2417
2418
0
        pFS->startElement(FSNS(XML_cx, XML_txPr));
2419
0
    }
2420
2421
    // TODO: bodyPr
2422
0
    const char* sWritingMode = nullptr;
2423
0
    bool bVertical = false;
2424
0
    xPropSet->getPropertyValue(u"StackedText"_ustr) >>= bVertical;
2425
0
    if( bVertical )
2426
0
        sWritingMode = "wordArtVert";
2427
2428
0
    sal_Int32 nRotation = 0;
2429
0
    xPropSet->getPropertyValue(u"TextRotation"_ustr) >>= nRotation;
2430
2431
0
    pFS->singleElement( FSNS( XML_a, XML_bodyPr ),
2432
0
            XML_vert, sWritingMode,
2433
0
            XML_rot, oox::drawingml::calcRotationValue(nRotation) );
2434
    // TODO: lstStyle
2435
0
    pFS->singleElement(FSNS(XML_a, XML_lstStyle));
2436
0
    pFS->startElement(FSNS(XML_a, XML_p));
2437
2438
0
    pFS->startElement(FSNS(XML_a, XML_pPr));
2439
2440
0
    {
2441
0
        WriteRunInput aInput;
2442
0
        aInput.bCheckDirect = true;
2443
0
        aInput.bUseTextSchemeColors = true;
2444
0
        WriteRunProperties(xPropSet, XML_defRPr, aInput);
2445
0
    }
2446
2447
0
    pFS->endElement( FSNS( XML_a, XML_pPr ) );
2448
2449
0
    for (const uno::Reference<chart2::XFormattedString>& rxFS : xFormattedTitle)
2450
0
    {
2451
0
        pFS->startElement(FSNS(XML_a, XML_r));
2452
0
        Reference< beans::XPropertySet > xRunPropSet(rxFS, uno::UNO_QUERY);
2453
0
        {
2454
0
            WriteRunInput aInput;
2455
0
            aInput.bCheckDirect = true;
2456
0
            aInput.bUseTextSchemeColors = true;
2457
0
            WriteRunProperties(xRunPropSet, XML_rPr, aInput);
2458
0
        }
2459
0
        pFS->startElement(FSNS(XML_a, XML_t));
2460
2461
        // the linebreak should always be at the end of the XFormattedString text
2462
0
        bool bNextPara = rxFS->getString().endsWith(u"\n");
2463
0
        if (!bNextPara)
2464
0
            pFS->writeEscaped(rxFS->getString());
2465
0
        else
2466
0
        {
2467
0
            sal_Int32 nEnd = rxFS->getString().lastIndexOf('\n');
2468
0
            pFS->writeEscaped(rxFS->getString().replaceAt(nEnd, 1, u""));
2469
0
        }
2470
0
        pFS->endElement(FSNS(XML_a, XML_t));
2471
0
        pFS->endElement(FSNS(XML_a, XML_r));
2472
2473
0
        if (bNextPara)
2474
0
        {
2475
0
            pFS->endElement(FSNS(XML_a, XML_p));
2476
2477
0
            pFS->startElement(FSNS(XML_a, XML_p));
2478
0
            pFS->startElement(FSNS(XML_a, XML_pPr));
2479
2480
0
            WriteRunInput aInput;
2481
0
            aInput.bCheckDirect = true;
2482
0
            aInput.bUseTextSchemeColors = true;
2483
0
            WriteRunProperties(xPropSet, XML_defRPr, aInput);
2484
2485
0
            pFS->endElement(FSNS(XML_a, XML_pPr));
2486
0
        }
2487
0
    }
2488
2489
0
    pFS->endElement( FSNS( XML_a, XML_p ) );
2490
2491
0
    if (bIsChartex) {
2492
0
        pFS->endElement( FSNS( XML_cx, XML_txPr ) );
2493
0
    } else {
2494
0
        pFS->endElement( FSNS( XML_c, XML_rich ) );
2495
0
        pFS->endElement( FSNS( XML_c, XML_tx ) );
2496
0
    }
2497
2498
0
    uno::Any aManualLayout = xPropSet->getPropertyValue(u"RelativePosition"_ustr);
2499
0
    if (aManualLayout.hasValue())
2500
0
    {
2501
0
        if (bIsChartex) {
2502
            // TODO. Chartex doesn't have a manualLayout tag, but does have
2503
            // "pos" and "align" attributes. Not sure how these correspond.
2504
0
        } else {
2505
0
            pFS->startElement(FSNS(XML_c, XML_layout));
2506
0
            pFS->startElement(FSNS(XML_c, XML_manualLayout));
2507
0
            pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
2508
0
            pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
2509
2510
0
            Reference<embed::XVisualObject> xVisObject(mxChartModel, uno::UNO_QUERY);
2511
0
            awt::Size aPageSize = xVisObject->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT);
2512
2513
0
            awt::Size aSize = xShape->getSize();
2514
0
            awt::Point aPos2 = xShape->getPosition();
2515
            // rotated shapes need special handling...
2516
0
            double fSin = fabs(sin(basegfx::deg2rad<100>(nRotation)));
2517
            // remove part of height from X direction, if title is rotated down
2518
0
            if( nRotation*0.01 > 180.0 )
2519
0
                aPos2.X -= static_cast<sal_Int32>(fSin * aSize.Height + 0.5);
2520
            // remove part of width from Y direction, if title is rotated up
2521
0
            else if( nRotation*0.01 > 0.0 )
2522
0
                aPos2.Y -= static_cast<sal_Int32>(fSin * aSize.Width + 0.5);
2523
2524
0
            double x = static_cast<double>(aPos2.X) / static_cast<double>(aPageSize.Width);
2525
0
            double y = static_cast<double>(aPos2.Y) / static_cast<double>(aPageSize.Height);
2526
            /*
2527
            pFS->singleElement(FSNS(XML_c, XML_wMode), XML_val, "edge");
2528
            pFS->singleElement(FSNS(XML_c, XML_hMode), XML_val, "edge");
2529
                    */
2530
0
            pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
2531
0
            pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
2532
            /*
2533
            pFS->singleElement(FSNS(XML_c, XML_w), XML_val, "");
2534
            pFS->singleElement(FSNS(XML_c, XML_h), XML_val, "");
2535
                    */
2536
0
            pFS->endElement(FSNS(XML_c, XML_manualLayout));
2537
0
            pFS->endElement(FSNS(XML_c, XML_layout));
2538
0
        }
2539
0
    }
2540
2541
0
    if (!bIsChartex) {
2542
0
        pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, "0");
2543
0
    }
2544
2545
0
    if (!bIsChartex) {
2546
        // shape properties
2547
0
        if( xPropSet.is() )
2548
0
        {
2549
0
            exportShapeProps( xPropSet, XML_c );
2550
0
        }
2551
0
    }
2552
2553
0
    if (bIsChartex) {
2554
0
        pFS->endElement( FSNS( XML_cx, XML_title ) );
2555
0
    } else {
2556
0
        pFS->endElement( FSNS( XML_c, XML_title ) );
2557
0
    }
2558
0
}
2559
2560
void ChartExport::exportPlotArea(const Reference< css::chart::XChartDocument >& xChartDoc,
2561
        bool bIsChartex)
2562
0
{
2563
0
    Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( mxNewDiagram, uno::UNO_QUERY );
2564
    // MS Word considers a chart corrupt if it doesn't have a c:plotArea
2565
0
    assert(xBCooSysCnt.is());
2566
0
    if (!xBCooSysCnt.is())
2567
0
        return;
2568
2569
    // plot-area element
2570
2571
0
    FSHelperPtr pFS = GetFS();
2572
2573
0
    if (bIsChartex) {
2574
0
        pFS->startElement(FSNS(XML_cx, XML_plotArea));
2575
0
        pFS->startElement(FSNS(XML_cx, XML_plotAreaRegion));
2576
0
    } else {
2577
0
        pFS->startElement(FSNS(XML_c, XML_plotArea));
2578
2579
0
        Reference<beans::XPropertySet> xWall(mxNewDiagram, uno::UNO_QUERY);
2580
0
        if( xWall.is() )
2581
0
        {
2582
0
            uno::Any aAny = xWall->getPropertyValue(u"RelativePosition"_ustr);
2583
0
            if (aAny.hasValue())
2584
0
            {
2585
0
                chart2::RelativePosition aPos = aAny.get<chart2::RelativePosition>();
2586
0
                aAny = xWall->getPropertyValue(u"RelativeSize"_ustr);
2587
0
                chart2::RelativeSize aSize = aAny.get<chart2::RelativeSize>();
2588
0
                uno::Reference< css::chart::XDiagramPositioning > xDiagramPositioning( xChartDoc->getDiagram(), uno::UNO_QUERY );
2589
0
                exportManualLayout(aPos, aSize, xDiagramPositioning->isExcludingDiagramPositioning() );
2590
0
            }
2591
0
        }
2592
0
    }
2593
2594
    // chart type
2595
0
    const Sequence< Reference< chart2::XCoordinateSystem > >
2596
0
        aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
2597
2598
    // tdf#123647 Save empty chart as empty bar chart.
2599
0
    if (!aCooSysSeq.hasElements())
2600
0
    {
2601
0
        assert(!bIsChartex);
2602
2603
0
        pFS->startElement(FSNS(XML_c, XML_barChart));
2604
0
        pFS->singleElement(FSNS(XML_c, XML_barDir), XML_val, "col");
2605
0
        pFS->singleElement(FSNS(XML_c, XML_grouping), XML_val, "clustered");
2606
0
        pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, "0");
2607
0
        createAxes(true, false, false);
2608
0
        pFS->endElement(FSNS(XML_c, XML_barChart));
2609
0
    }
2610
2611
0
    for( const auto& rCS : aCooSysSeq )
2612
0
    {
2613
0
        Reference< chart2::XChartTypeContainer > xCTCnt( rCS, uno::UNO_QUERY );
2614
0
        if( ! xCTCnt.is())
2615
0
            continue;
2616
0
        mnSeriesCount=0;
2617
0
        const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
2618
0
        for( const auto& rCT : aCTSeq )
2619
0
        {
2620
0
            Reference< chart2::XDataSeriesContainer > xDSCnt( rCT, uno::UNO_QUERY );
2621
0
            if( ! xDSCnt.is())
2622
0
                return;
2623
0
            Reference< chart2::XChartType > xChartType( rCT, uno::UNO_QUERY );
2624
0
            if( ! xChartType.is())
2625
0
                continue;
2626
            // note: if xDSCnt.is() then also aCTSeq[nCTIdx]
2627
0
            OUString aChartType( xChartType->getChartType());
2628
0
            sal_Int32 eChartType = lcl_getChartType( aChartType );
2629
0
            switch( eChartType )
2630
0
            {
2631
0
                case chart::TYPEID_BAR:
2632
0
                    {
2633
0
                        exportBarChart( xChartType );
2634
0
                        break;
2635
0
                    }
2636
0
                case chart::TYPEID_AREA:
2637
0
                    {
2638
0
                        exportAreaChart( xChartType );
2639
0
                        break;
2640
0
                    }
2641
0
                case chart::TYPEID_LINE:
2642
0
                    {
2643
0
                        exportLineChart( xChartType );
2644
0
                        break;
2645
0
                    }
2646
0
                case chart::TYPEID_BUBBLE:
2647
0
                    {
2648
0
                        exportBubbleChart( xChartType );
2649
0
                        break;
2650
0
                    }
2651
0
                case chart::TYPEID_BOXWHISKER:
2652
0
                    {
2653
0
                        exportChartex( xChartType, "boxWhisker" );
2654
0
                        break;
2655
0
                    }
2656
0
                case chart::TYPEID_CLUSTEREDCOLUMN:
2657
0
                    {
2658
0
                        exportChartex( xChartType, "clusteredColumn" );
2659
0
                        break;
2660
0
                    }
2661
0
                case chart::TYPEID_FUNNEL:
2662
0
                    {
2663
0
                        exportChartex( xChartType, "funnel" );
2664
0
                        break;
2665
0
                    }
2666
0
                case chart::TYPEID_PARETOLINE:
2667
0
                    {
2668
                        // MSO pareto charts actually include both a
2669
                        // clusteredColumn series and a paretoLine series. TODO
2670
0
                        exportChartex( xChartType, "paretoLine" );
2671
0
                        break;
2672
0
                    }
2673
0
                case chart::TYPEID_REGIONMAP:
2674
0
                    {
2675
0
                        exportRegionMapChart( xChartType);
2676
0
                        break;
2677
0
                    }
2678
0
                case chart::TYPEID_SUNBURST:
2679
0
                    {
2680
0
                        exportChartex( xChartType, "sunburst" );
2681
0
                        break;
2682
0
                    }
2683
0
                case chart::TYPEID_TREEMAP:
2684
0
                    {
2685
0
                        exportChartex( xChartType, "treemap" );
2686
0
                        break;
2687
0
                    }
2688
0
                case chart::TYPEID_WATERFALL:
2689
0
                    {
2690
0
                        exportChartex( xChartType, "waterfall" );
2691
0
                        break;
2692
0
                    }
2693
0
                case chart::TYPEID_DOUGHNUT: // doesn't currently happen
2694
0
                case chart::TYPEID_OFPIE:    // doesn't currently happen
2695
0
                case chart::TYPEID_PIE:
2696
0
                    {
2697
0
                        sal_Int32 eCT = getChartType( );
2698
0
                        if(eCT == chart::TYPEID_DOUGHNUT)
2699
0
                        {
2700
0
                            exportDoughnutChart( xChartType );
2701
0
                        }
2702
0
                        else
2703
0
                        {
2704
2705
0
                            PropertySet xChartTypeProp(rCT);
2706
0
                            chart2::PieChartSubType subtype(chart2::PieChartSubType_NONE);
2707
0
                            if (!xChartTypeProp.getProperty(subtype, PROP_SubPieType))
2708
0
                            {
2709
0
                                subtype = chart2::PieChartSubType_NONE;
2710
0
                            }
2711
0
                            if (subtype != chart2::PieChartSubType_NONE)
2712
0
                            {
2713
0
                                const char* sSubType = "pie";   // default
2714
0
                                switch (subtype) {
2715
0
                                    case chart2::PieChartSubType_PIE:
2716
0
                                        sSubType = "pie";
2717
0
                                        break;
2718
0
                                    case chart2::PieChartSubType_BAR:
2719
0
                                        sSubType = "bar";
2720
0
                                        break;
2721
0
                                    default:
2722
0
                                        assert(false);
2723
0
                                }
2724
0
                                double fSplitPos;
2725
0
                                if (!xChartTypeProp.getProperty(fSplitPos,
2726
0
                                            PROP_SplitPos)) {
2727
0
                                    fSplitPos = 2;
2728
0
                                }
2729
2730
0
                                exportOfPieChart(xChartType, sSubType, fSplitPos);
2731
0
                            } else {
2732
0
                                exportPieChart( xChartType );
2733
0
                            }
2734
0
                        }
2735
0
                        break;
2736
0
                    }
2737
0
                case chart::TYPEID_RADARLINE:
2738
0
                case chart::TYPEID_RADARAREA:
2739
0
                    {
2740
0
                        exportRadarChart( xChartType );
2741
0
                        break;
2742
0
                    }
2743
0
                case chart::TYPEID_SCATTER:
2744
0
                    {
2745
0
                        exportScatterChart( xChartType );
2746
0
                        break;
2747
0
                    }
2748
0
                case chart::TYPEID_STOCK:
2749
0
                    {
2750
0
                        exportStockChart( xChartType );
2751
0
                        break;
2752
0
                    }
2753
0
                case chart::TYPEID_SURFACE:
2754
0
                    {
2755
0
                        exportSurfaceChart( xChartType );
2756
0
                        break;
2757
0
                    }
2758
0
                default:
2759
0
                    {
2760
0
                        SAL_WARN("oox", "ChartExport::exportPlotArea -- not support chart type");
2761
0
                        break;
2762
0
                    }
2763
0
            }
2764
2765
0
        }
2766
0
    }
2767
2768
0
    if (bIsChartex) {
2769
0
        pFS->endElement( FSNS( XML_cx, XML_plotAreaRegion ) );
2770
0
    }
2771
2772
    //Axis Data
2773
0
    exportAxes(bIsChartex);
2774
2775
0
    if (!bIsChartex) {
2776
        // Data Table
2777
        // not supported in chartex?
2778
0
        exportDataTable();
2779
0
    }
2780
2781
    // shape properties
2782
    /*
2783
     * Export the Plot area Shape Properties
2784
     * eg: Fill and Outline
2785
     */
2786
0
    Reference< css::chart::X3DDisplay > xWallFloorSupplier( mxDiagram, uno::UNO_QUERY );
2787
    // tdf#114139 For 2D charts Plot Area equivalent is Chart Wall.
2788
    // Unfortunately LibreOffice doesn't have Plot Area equivalent for 3D charts.
2789
    // It means that Plot Area couldn't be displayed and changed for 3D chars in LibreOffice.
2790
    // We cannot write Wall attributes into Plot Area for 3D charts, because Wall us used as background wall.
2791
0
    if( !mbIs3DChart && xWallFloorSupplier.is() )
2792
0
    {
2793
0
        Reference< beans::XPropertySet > xWallPropSet = xWallFloorSupplier->getWall();
2794
0
        if( xWallPropSet.is() )
2795
0
        {
2796
0
            uno::Any aAny = xWallPropSet->getPropertyValue(u"LineStyle"_ustr);
2797
0
            sal_Int32 eChartType = getChartType( );
2798
            // Export LineStyle_NONE instead of default linestyle of PlotArea border, because LibreOffice
2799
            // make invisible the Wall shape properties, in case of these charts. Or in the future set
2800
            // the default LineStyle of these charts to LineStyle_NONE.
2801
0
            bool noSupportWallProp = ( (eChartType == chart::TYPEID_PIE) || (eChartType == chart::TYPEID_RADARLINE) || (eChartType == chart::TYPEID_RADARAREA) );
2802
0
            if ( noSupportWallProp && (aAny != drawing::LineStyle_NONE) )
2803
0
            {
2804
0
                xWallPropSet->setPropertyValue( u"LineStyle"_ustr, uno::Any(drawing::LineStyle_NONE) );
2805
0
            }
2806
0
            exportShapeProps( xWallPropSet, bIsChartex ? XML_cx : XML_c );
2807
0
        }
2808
0
    }
2809
2810
0
    if (bIsChartex) {
2811
0
        pFS->endElement( FSNS( XML_cx, XML_plotArea ) );
2812
0
    } else {
2813
0
        pFS->endElement( FSNS( XML_c, XML_plotArea ) );
2814
0
    }
2815
2816
0
}
2817
2818
void ChartExport::exportManualLayout(const css::chart2::RelativePosition& rPos,
2819
                                     const css::chart2::RelativeSize& rSize,
2820
                                     const bool bIsExcludingDiagramPositioning)
2821
0
{
2822
    // 2006 chart schema only
2823
0
    FSHelperPtr pFS = GetFS();
2824
0
    pFS->startElement(FSNS(XML_c, XML_layout));
2825
0
    pFS->startElement(FSNS(XML_c, XML_manualLayout));
2826
2827
    // By default layoutTarget is set to "outer" and we shouldn't save it in that case
2828
0
    if ( bIsExcludingDiagramPositioning )
2829
0
    {
2830
0
        pFS->singleElement(FSNS(XML_c, XML_layoutTarget), XML_val, "inner");
2831
0
    }
2832
0
    pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
2833
0
    pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
2834
2835
0
    double x = rPos.Primary;
2836
0
    double y = rPos.Secondary;
2837
0
    const double w = rSize.Primary;
2838
0
    const double h = rSize.Secondary;
2839
0
    switch (rPos.Anchor)
2840
0
    {
2841
0
        case drawing::Alignment_LEFT:
2842
0
            y -= (h/2);
2843
0
        break;
2844
0
        case drawing::Alignment_TOP_LEFT:
2845
0
        break;
2846
0
        case drawing::Alignment_BOTTOM_LEFT:
2847
0
            y -= h;
2848
0
        break;
2849
0
        case drawing::Alignment_TOP:
2850
0
            x -= (w/2);
2851
0
        break;
2852
0
        case drawing::Alignment_CENTER:
2853
0
            x -= (w/2);
2854
0
            y -= (h/2);
2855
0
        break;
2856
0
        case drawing::Alignment_BOTTOM:
2857
0
            x -= (w/2);
2858
0
            y -= h;
2859
0
        break;
2860
0
        case drawing::Alignment_TOP_RIGHT:
2861
0
            x -= w;
2862
0
        break;
2863
0
        case drawing::Alignment_BOTTOM_RIGHT:
2864
0
            x -= w;
2865
0
            y -= h;
2866
0
        break;
2867
0
        case drawing::Alignment_RIGHT:
2868
0
            y -= (h/2);
2869
0
            x -= w;
2870
0
        break;
2871
0
        default:
2872
0
            SAL_WARN("oox", "unhandled alignment case for manual layout export " << static_cast<sal_uInt16>(rPos.Anchor));
2873
0
    }
2874
2875
0
    pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
2876
2877
0
    pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
2878
2879
0
    pFS->singleElement(FSNS(XML_c, XML_w), XML_val, OString::number(w));
2880
2881
0
    pFS->singleElement(FSNS(XML_c, XML_h), XML_val, OString::number(h));
2882
2883
0
    pFS->endElement(FSNS(XML_c, XML_manualLayout));
2884
0
    pFS->endElement(FSNS(XML_c, XML_layout));
2885
0
}
2886
2887
void ChartExport::exportFill( const Reference< XPropertySet >& xPropSet )
2888
0
{
2889
    // Similar to DrawingML::WriteFill, but gradient access via name
2890
0
    if (!GetProperty( xPropSet, u"FillStyle"_ustr ))
2891
0
        return;
2892
0
    FillStyle aFillStyle(FillStyle_NONE);
2893
0
    mAny >>= aFillStyle;
2894
2895
    // map full transparent background to no fill
2896
0
    if (aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, u"FillTransparence"_ustr ))
2897
0
    {
2898
0
        sal_Int16 nVal = 0;
2899
0
        mAny >>= nVal;
2900
0
        if ( nVal == 100 )
2901
0
            aFillStyle = FillStyle_NONE;
2902
0
    }
2903
0
    OUString sFillTransparenceGradientName;
2904
0
    if (aFillStyle == FillStyle_SOLID
2905
0
        && GetProperty(xPropSet, u"FillTransparenceGradientName"_ustr) && (mAny >>= sFillTransparenceGradientName)
2906
0
        && !sFillTransparenceGradientName.isEmpty())
2907
0
    {
2908
0
        awt::Gradient aTransparenceGradient;
2909
0
        uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
2910
0
        uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance(u"com.sun.star.drawing.TransparencyGradientTable"_ustr), uno::UNO_QUERY);
2911
0
        uno::Any rTransparenceValue = xTransparenceGradient->getByName(sFillTransparenceGradientName);
2912
0
        rTransparenceValue >>= aTransparenceGradient;
2913
0
        if (aTransparenceGradient.StartColor == 0xffffff && aTransparenceGradient.EndColor == 0xffffff)
2914
0
            aFillStyle = FillStyle_NONE;
2915
0
    }
2916
0
    switch( aFillStyle )
2917
0
    {
2918
0
        case FillStyle_SOLID:
2919
0
            exportSolidFill(xPropSet);
2920
0
            break;
2921
0
        case FillStyle_GRADIENT :
2922
0
            exportGradientFill( xPropSet );
2923
0
            break;
2924
0
        case FillStyle_BITMAP :
2925
0
            exportBitmapFill( xPropSet );
2926
0
            break;
2927
0
        case FillStyle_HATCH:
2928
0
            exportHatch(xPropSet);
2929
0
            break;
2930
0
        case FillStyle_NONE:
2931
0
            mpFS->singleElementNS(XML_a, XML_noFill);
2932
0
            break;
2933
0
        default:
2934
0
            ;
2935
0
    }
2936
0
}
2937
2938
void ChartExport::exportSolidFill(const Reference< XPropertySet >& xPropSet)
2939
0
{
2940
    // Similar to DrawingML::WriteSolidFill, but gradient access via name
2941
    // and currently no InteropGrabBag
2942
    // get fill color
2943
0
    sal_uInt32 nFillColor = 0;
2944
0
    if (!GetProperty(xPropSet, u"FillColor"_ustr) || !(mAny >>= nFillColor))
2945
0
        return;
2946
2947
0
    sal_Int32 nAlpha = MAX_PERCENT;
2948
0
    if (GetProperty( xPropSet, u"FillTransparence"_ustr ))
2949
0
    {
2950
0
        sal_Int32 nTransparency = 0;
2951
0
        mAny >>= nTransparency;
2952
        // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
2953
0
        nAlpha = (MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
2954
0
    }
2955
    // OOXML has no separate transparence gradient but uses transparency in the gradient stops.
2956
    // So we merge transparency and color and use gradient fill in such case.
2957
0
    basegfx::BGradient aTransparenceGradient;
2958
0
    bool bNeedGradientFill(false);
2959
0
    OUString sFillTransparenceGradientName;
2960
2961
0
    if (GetProperty(xPropSet, u"FillTransparenceGradientName"_ustr)
2962
0
        && (mAny >>= sFillTransparenceGradientName)
2963
0
        && !sFillTransparenceGradientName.isEmpty())
2964
0
    {
2965
0
        uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
2966
0
        uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance(u"com.sun.star.drawing.TransparencyGradientTable"_ustr), uno::UNO_QUERY);
2967
0
        const uno::Any rTransparenceAny = xTransparenceGradient->getByName(sFillTransparenceGradientName);
2968
2969
0
        aTransparenceGradient = model::gradient::getFromAny(rTransparenceAny);
2970
0
        basegfx::BColor aSingleColor;
2971
0
        bNeedGradientFill = !aTransparenceGradient.GetColorStops().isSingleColor(aSingleColor);
2972
2973
0
        if (!bNeedGradientFill)
2974
0
        {
2975
            // Our alpha is a single gray color value.
2976
0
            const sal_uInt8 nRed(aSingleColor.getRed() * 255.0);
2977
2978
            // drawingML alpha is a percentage on a 0..100000 scale.
2979
0
            nAlpha = (255 - nRed) * oox::drawingml::MAX_PERCENT / 255;
2980
0
        }
2981
0
    }
2982
    // write XML
2983
0
    if (bNeedGradientFill)
2984
0
    {
2985
        // no longer create copy/PseudoColorGradient, use new API of
2986
        // WriteGradientFill to express fix fill color
2987
0
        mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
2988
0
        WriteGradientFill(nullptr, nFillColor, &aTransparenceGradient);
2989
0
        mpFS->endElementNS(XML_a, XML_gradFill);
2990
0
    }
2991
0
    else
2992
0
        WriteSolidFill(::Color(ColorTransparency, nFillColor & 0xffffff), nAlpha);
2993
0
}
2994
2995
void ChartExport::exportHatch( const Reference< XPropertySet >& xPropSet )
2996
0
{
2997
0
    if (!xPropSet.is())
2998
0
        return;
2999
3000
0
    if (GetProperty(xPropSet, u"FillHatchName"_ustr))
3001
0
    {
3002
0
        OUString aHatchName;
3003
0
        mAny >>= aHatchName;
3004
0
        uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
3005
0
        uno::Reference< container::XNameAccess > xHatchTable( xFact->createInstance(u"com.sun.star.drawing.HatchTable"_ustr), uno::UNO_QUERY );
3006
0
        uno::Any rValue = xHatchTable->getByName(aHatchName);
3007
0
        css::drawing::Hatch aHatch;
3008
0
        rValue >>= aHatch;
3009
0
        WritePattFill(xPropSet, aHatch);
3010
0
    }
3011
3012
0
}
3013
3014
void ChartExport::exportBitmapFill( const Reference< XPropertySet >& xPropSet )
3015
0
{
3016
0
    if( !xPropSet.is() )
3017
0
        return;
3018
3019
0
    OUString sFillBitmapName;
3020
0
    xPropSet->getPropertyValue(u"FillBitmapName"_ustr) >>= sFillBitmapName;
3021
3022
0
    uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
3023
0
    try
3024
0
    {
3025
0
        uno::Reference< container::XNameAccess > xBitmapTable( xFact->createInstance(u"com.sun.star.drawing.BitmapTable"_ustr), uno::UNO_QUERY );
3026
0
        uno::Any rValue = xBitmapTable->getByName( sFillBitmapName );
3027
0
        if (rValue.has<uno::Reference<awt::XBitmap>>())
3028
0
        {
3029
0
            uno::Reference<awt::XBitmap> xBitmap = rValue.get<uno::Reference<awt::XBitmap>>();
3030
0
            uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY);
3031
0
            if (xGraphic.is())
3032
0
            {
3033
0
                WriteXGraphicBlipFill(xPropSet, xGraphic, XML_a, true, true);
3034
0
            }
3035
0
        }
3036
0
    }
3037
0
    catch (const uno::Exception &)
3038
0
    {
3039
0
        TOOLS_WARN_EXCEPTION("oox", "ChartExport::exportBitmapFill");
3040
0
    }
3041
0
}
3042
3043
void ChartExport::exportGradientFill( const Reference< XPropertySet >& xPropSet )
3044
0
{
3045
0
    if( !xPropSet.is() )
3046
0
        return;
3047
3048
0
    OUString sFillGradientName;
3049
0
    xPropSet->getPropertyValue(u"FillGradientName"_ustr) >>= sFillGradientName;
3050
3051
0
    uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
3052
0
    try
3053
0
    {
3054
0
        uno::Reference< container::XNameAccess > xGradient( xFact->createInstance(u"com.sun.star.drawing.GradientTable"_ustr), uno::UNO_QUERY );
3055
0
        const uno::Any rGradientAny(xGradient->getByName( sFillGradientName ));
3056
0
        const basegfx::BGradient aGradient = model::gradient::getFromAny(rGradientAny);
3057
0
        basegfx::BColor aSingleColor;
3058
3059
0
        if (!aGradient.GetColorStops().isSingleColor(aSingleColor))
3060
0
        {
3061
0
            basegfx::BGradient aTransparenceGradient;
3062
0
            mpFS->startElementNS(XML_a, XML_gradFill);
3063
0
            OUString sFillTransparenceGradientName;
3064
3065
0
            if( (xPropSet->getPropertyValue(u"FillTransparenceGradientName"_ustr) >>= sFillTransparenceGradientName) && !sFillTransparenceGradientName.isEmpty())
3066
0
            {
3067
0
                uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance(u"com.sun.star.drawing.TransparencyGradientTable"_ustr), uno::UNO_QUERY);
3068
0
                const uno::Any rTransparenceAny(xTransparenceGradient->getByName(sFillTransparenceGradientName));
3069
3070
0
                aTransparenceGradient = model::gradient::getFromAny(rTransparenceAny);
3071
3072
0
                WriteGradientFill(&aGradient, 0, &aTransparenceGradient);
3073
0
            }
3074
0
            else if (GetProperty(xPropSet, u"FillTransparence"_ustr) )
3075
0
            {
3076
                // no longer create PseudoTransparencyGradient, use new API of
3077
                // WriteGradientFill to express fix transparency
3078
0
                sal_Int32 nTransparency = 0;
3079
0
                mAny >>= nTransparency;
3080
                // nTransparency is [0..100]%
3081
0
                WriteGradientFill(&aGradient, 0, nullptr, nTransparency * 0.01);
3082
0
            }
3083
0
            else
3084
0
            {
3085
0
                WriteGradientFill(&aGradient, 0, nullptr);
3086
0
            }
3087
3088
0
            mpFS->endElementNS(XML_a, XML_gradFill);
3089
0
        }
3090
0
    }
3091
0
    catch (const uno::Exception &)
3092
0
    {
3093
0
        TOOLS_INFO_EXCEPTION("oox", "ChartExport::exportGradientFill");
3094
0
    }
3095
0
}
3096
3097
void ChartExport::exportDataTable( )
3098
0
{
3099
    // Not supported in chartex 2014 schema
3100
0
    auto xDataTable = mxNewDiagram->getDataTable();
3101
0
    if (!xDataTable.is())
3102
0
        return;
3103
3104
0
    FSHelperPtr pFS = GetFS();
3105
0
    uno::Reference<beans::XPropertySet> aPropSet(xDataTable, uno::UNO_QUERY);
3106
3107
0
    bool bShowVBorder = false;
3108
0
    bool bShowHBorder = false;
3109
0
    bool bShowOutline = false;
3110
0
    bool bShowKeys = false;
3111
3112
0
    if (GetProperty(aPropSet, u"HBorder"_ustr))
3113
0
        mAny >>= bShowHBorder;
3114
0
    if (GetProperty(aPropSet, u"VBorder"_ustr))
3115
0
        mAny >>= bShowVBorder;
3116
0
    if (GetProperty(aPropSet, u"Outline"_ustr))
3117
0
        mAny >>= bShowOutline;
3118
0
    if (GetProperty(aPropSet, u"Keys"_ustr))
3119
0
        mAny >>= bShowKeys;
3120
3121
0
    pFS->startElement(FSNS(XML_c, XML_dTable));
3122
3123
0
    if (bShowHBorder)
3124
0
        pFS->singleElement(FSNS(XML_c, XML_showHorzBorder), XML_val, "1" );
3125
0
    if (bShowVBorder)
3126
0
        pFS->singleElement(FSNS(XML_c, XML_showVertBorder), XML_val, "1");
3127
0
    if (bShowOutline)
3128
0
        pFS->singleElement(FSNS(XML_c, XML_showOutline), XML_val, "1");
3129
0
    if (bShowKeys)
3130
0
        pFS->singleElement(FSNS(XML_c, XML_showKeys), XML_val, "1");
3131
3132
0
    exportShapeProps(aPropSet, XML_c);
3133
0
    exportTextProps(aPropSet, false);
3134
3135
0
    pFS->endElement(FSNS(XML_c, XML_dTable));
3136
0
}
3137
3138
void ChartExport::exportAreaChart( const Reference< chart2::XChartType >& xChartType )
3139
0
{
3140
0
    FSHelperPtr pFS = GetFS();
3141
0
    std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
3142
0
    if (aSplitDataSeries.empty())
3143
0
    {
3144
        // Use a dummy data series to output needed basic chart-related XML even in case of empty charts
3145
0
        aSplitDataSeries.push_back({});
3146
0
    }
3147
0
    for (const auto& splitDataSeries : aSplitDataSeries)
3148
0
    {
3149
0
        sal_Int32 nTypeId = XML_areaChart;
3150
0
        if (mbIs3DChart)
3151
0
            nTypeId = XML_area3DChart;
3152
0
        pFS->startElement(FSNS(XML_c, nTypeId));
3153
3154
0
        exportGrouping();
3155
0
        bool bPrimaryAxes = true;
3156
0
        if (splitDataSeries.hasElements())
3157
0
            exportSeries_chart(xChartType, splitDataSeries, bPrimaryAxes);
3158
3159
0
        createAxes(bPrimaryAxes, false, false);
3160
        //exportAxesId(bPrimaryAxes);
3161
3162
0
        pFS->endElement(FSNS(XML_c, nTypeId));
3163
0
    }
3164
0
}
3165
3166
void ChartExport::exportBarChart(const Reference< chart2::XChartType >& xChartType)
3167
0
{
3168
0
    sal_Int32 nTypeId = XML_barChart;
3169
0
    if (mbIs3DChart)
3170
0
        nTypeId = XML_bar3DChart;
3171
0
    FSHelperPtr pFS = GetFS();
3172
3173
0
    std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
3174
0
    if (aSplitDataSeries.empty())
3175
0
    {
3176
        // Use a dummy data series to output needed basic chart-related XML even in case of empty charts
3177
0
        aSplitDataSeries.push_back({});
3178
0
    }
3179
0
    for (const auto& splitDataSeries : aSplitDataSeries)
3180
0
    {
3181
0
        pFS->startElement(FSNS(XML_c, nTypeId));
3182
        // bar direction
3183
0
        bool bVertical = false;
3184
0
        Reference< XPropertySet > xPropSet(mxDiagram, uno::UNO_QUERY);
3185
0
        if (GetProperty(xPropSet, u"Vertical"_ustr))
3186
0
            mAny >>= bVertical;
3187
3188
0
        const char* bardir = bVertical ? "bar" : "col";
3189
0
        pFS->singleElement(FSNS(XML_c, XML_barDir), XML_val, bardir);
3190
3191
0
        exportGrouping(true);
3192
3193
0
        exportVaryColors(xChartType);
3194
3195
0
        bool bPrimaryAxes = true;
3196
0
        if (splitDataSeries.hasElements())
3197
0
            exportSeries_chart(xChartType, splitDataSeries, bPrimaryAxes);
3198
3199
0
        Reference< XPropertySet > xTypeProp(xChartType, uno::UNO_QUERY);
3200
3201
0
        if (xTypeProp.is() && GetProperty(xTypeProp, u"GapwidthSequence"_ustr))
3202
0
        {
3203
0
            uno::Sequence< sal_Int32 > aBarPositionSequence;
3204
0
            mAny >>= aBarPositionSequence;
3205
0
            if (aBarPositionSequence.hasElements())
3206
0
            {
3207
0
                sal_Int32 nGapWidth = aBarPositionSequence[0];
3208
0
                pFS->singleElement(FSNS(XML_c, XML_gapWidth), XML_val, OString::number(nGapWidth));
3209
0
            }
3210
0
        }
3211
3212
0
        if (mbIs3DChart)
3213
0
        {
3214
            // Shape
3215
0
            namespace cssc = css::chart;
3216
0
            sal_Int32 nGeom3d = cssc::ChartSolidType::RECTANGULAR_SOLID;
3217
0
            if (xPropSet.is() && GetProperty(xPropSet, u"SolidType"_ustr))
3218
0
                mAny >>= nGeom3d;
3219
0
            const char* sShapeType = nullptr;
3220
0
            switch (nGeom3d)
3221
0
            {
3222
0
            case cssc::ChartSolidType::RECTANGULAR_SOLID:
3223
0
                sShapeType = "box";
3224
0
                break;
3225
0
            case cssc::ChartSolidType::CONE:
3226
0
                sShapeType = "cone";
3227
0
                break;
3228
0
            case cssc::ChartSolidType::CYLINDER:
3229
0
                sShapeType = "cylinder";
3230
0
                break;
3231
0
            case cssc::ChartSolidType::PYRAMID:
3232
0
                sShapeType = "pyramid";
3233
0
                break;
3234
0
            }
3235
0
            pFS->singleElement(FSNS(XML_c, XML_shape), XML_val, sShapeType);
3236
0
        }
3237
3238
        //overlap
3239
0
        if (!mbIs3DChart && xTypeProp.is() && GetProperty(xTypeProp, u"OverlapSequence"_ustr))
3240
0
        {
3241
0
            uno::Sequence< sal_Int32 > aBarPositionSequence;
3242
0
            mAny >>= aBarPositionSequence;
3243
0
            if (aBarPositionSequence.hasElements())
3244
0
            {
3245
0
                sal_Int32 nOverlap = aBarPositionSequence[0];
3246
                // Stacked/Percent Bar/Column chart Overlap-workaround
3247
                // Export the Overlap value with 100% for stacked charts,
3248
                // because the default overlap value of the Bar/Column chart is 0% and
3249
                // LibreOffice do nothing with the overlap value in Stacked charts case,
3250
                // unlike the MS Office, which is interpreted differently.
3251
0
                if ((mbStacked || mbPercent) && nOverlap != 100)
3252
0
                {
3253
0
                    nOverlap = 100;
3254
0
                    pFS->singleElement(FSNS(XML_c, XML_overlap), XML_val, OString::number(nOverlap));
3255
0
                }
3256
0
                else // Normal bar chart
3257
0
                {
3258
0
                    pFS->singleElement(FSNS(XML_c, XML_overlap), XML_val, OString::number(nOverlap));
3259
0
                }
3260
0
            }
3261
0
        }
3262
3263
0
        createAxes(bPrimaryAxes, false, false);
3264
3265
0
        pFS->endElement(FSNS(XML_c, nTypeId));
3266
0
    }
3267
0
}
3268
3269
void ChartExport::exportBubbleChart( const Reference< chart2::XChartType >& xChartType )
3270
0
{
3271
0
    FSHelperPtr pFS = GetFS();
3272
0
    std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
3273
0
    if (aSplitDataSeries.empty())
3274
0
    {
3275
        // Use a dummy data series to output needed basic chart-related XML even in case of empty charts
3276
0
        aSplitDataSeries.push_back({});
3277
0
    }
3278
0
    for (const auto& splitDataSeries : aSplitDataSeries)
3279
0
    {
3280
0
        pFS->startElement(FSNS(XML_c, XML_bubbleChart));
3281
3282
0
        exportVaryColors(xChartType);
3283
3284
0
        bool bPrimaryAxes = true;
3285
0
        if (splitDataSeries.hasElements())
3286
0
            exportSeries_chart(xChartType, splitDataSeries, bPrimaryAxes);
3287
3288
0
        createAxes(bPrimaryAxes, false, false);
3289
3290
0
        pFS->endElement(FSNS(XML_c, XML_bubbleChart));
3291
0
    }
3292
0
}
3293
3294
// Output a chartex chart. *sTypeName should be the string used in the layoutId
3295
// attribute of <cx:series>
3296
void ChartExport::exportChartex( const Reference< chart2::XChartType >& xChartType,
3297
        const char* sTypeName)
3298
0
{
3299
0
    FSHelperPtr pFS = GetFS();
3300
0
    const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
3301
0
    for (const auto& splitDataSeries : aSplitDataSeries)
3302
0
    {
3303
0
        if (!splitDataSeries.hasElements())
3304
0
            continue;
3305
3306
0
        createAxes(true, false, true);
3307
3308
        //exportVaryColors(xChartType);
3309
3310
0
        exportSeries_chartex(xChartType, splitDataSeries, sTypeName);
3311
0
    }
3312
0
}
3313
3314
void ChartExport::exportRegionMapChart( const Reference< chart2::XChartType >& xChartType)
3315
0
{
3316
0
    FSHelperPtr pFS = GetFS();
3317
0
    const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
3318
0
    for (const auto& splitDataSeries : aSplitDataSeries)
3319
0
    {
3320
0
        if (!splitDataSeries.hasElements())
3321
0
            continue;
3322
3323
        //exportVaryColors(xChartType);
3324
3325
        // Handle all the regionMap-specific stuff under
3326
        // <cx:series> <cx:layoutPr>
3327
3328
0
        exportSeries_chartex(xChartType, splitDataSeries, "regionMap");
3329
0
    }
3330
0
}
3331
3332
void ChartExport::exportDoughnutChart( const Reference< chart2::XChartType >& xChartType )
3333
0
{
3334
0
    FSHelperPtr pFS = GetFS();
3335
0
    pFS->startElement(FSNS(XML_c, XML_doughnutChart));
3336
3337
0
    exportVaryColors(xChartType);
3338
3339
0
    bool bPrimaryAxes = true;
3340
0
    exportAllSeries(xChartType, bPrimaryAxes);
3341
    // firstSliceAng
3342
0
    exportFirstSliceAng( );
3343
    //FIXME: holeSize
3344
0
    pFS->singleElement(FSNS(XML_c, XML_holeSize), XML_val, OString::number(50));
3345
3346
0
    pFS->endElement( FSNS( XML_c, XML_doughnutChart ) );
3347
0
}
3348
3349
void ChartExport::exportOfPieChart(
3350
        const Reference< chart2::XChartType >& xChartType,
3351
        const char* sSubType,
3352
        double fSplitPos)
3353
0
{
3354
0
    FSHelperPtr pFS = GetFS();
3355
0
    pFS->startElement(FSNS(XML_c, XML_ofPieChart));
3356
3357
0
    pFS->singleElement(FSNS(XML_c, XML_ofPieType), XML_val, sSubType);
3358
3359
0
    exportVaryColors(xChartType);
3360
3361
0
    bool bPrimaryAxes = true;
3362
0
    exportAllSeries(xChartType, bPrimaryAxes);
3363
3364
0
    pFS->singleElement(FSNS(XML_c, XML_splitType), XML_val, "pos");
3365
0
    pFS->singleElement(FSNS(XML_c, XML_splitPos), XML_val, OString::number(fSplitPos));
3366
3367
0
    pFS->endElement( FSNS( XML_c, XML_ofPieChart ) );
3368
0
}
3369
3370
namespace {
3371
3372
void writeDataLabelsRange(const FSHelperPtr& pFS, const XmlFilterBase* pFB, DataLabelsRange& rDLblsRange)
3373
0
{
3374
0
    if (rDLblsRange.empty())
3375
0
        return;
3376
3377
0
    pFS->startElement(FSNS(XML_c, XML_extLst));
3378
0
    pFS->startElement(FSNS(XML_c, XML_ext), XML_uri, "{02D57815-91ED-43cb-92C2-25804820EDAC}", FSNS(XML_xmlns, XML_c15), pFB->getNamespaceURL(OOX_NS(c15)));
3379
0
    pFS->startElement(FSNS(XML_c15, XML_datalabelsRange));
3380
3381
    // Write cell range.
3382
0
    pFS->startElement(FSNS(XML_c15, XML_f));
3383
0
    pFS->writeEscaped(rDLblsRange.getRange());
3384
0
    pFS->endElement(FSNS(XML_c15, XML_f));
3385
3386
    // Write all labels.
3387
0
    pFS->startElement(FSNS(XML_c15, XML_dlblRangeCache));
3388
0
    pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(rDLblsRange.count()));
3389
0
    for (const auto& rLabelKV: rDLblsRange)
3390
0
    {
3391
0
        pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(rLabelKV.first));
3392
0
        pFS->startElement(FSNS(XML_c, XML_v));
3393
0
        pFS->writeEscaped(rLabelKV.second);
3394
0
        pFS->endElement(FSNS( XML_c, XML_v ));
3395
0
        pFS->endElement(FSNS(XML_c, XML_pt));
3396
0
    }
3397
3398
0
    pFS->endElement(FSNS(XML_c15, XML_dlblRangeCache));
3399
3400
0
    pFS->endElement(FSNS(XML_c15, XML_datalabelsRange));
3401
0
    pFS->endElement(FSNS(XML_c, XML_ext));
3402
0
    pFS->endElement(FSNS(XML_c, XML_extLst));
3403
0
}
3404
3405
}
3406
3407
void ChartExport::exportLineChart( const Reference< chart2::XChartType >& xChartType )
3408
0
{
3409
0
    FSHelperPtr pFS = GetFS();
3410
0
    std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
3411
0
    if (aSplitDataSeries.empty())
3412
0
    {
3413
        // Use a dummy data series to output needed basic chart-related XML even in case of empty charts
3414
0
        aSplitDataSeries.push_back({});
3415
0
    }
3416
0
    for (const auto& splitDataSeries : aSplitDataSeries)
3417
0
    {
3418
0
        sal_Int32 nTypeId = XML_lineChart;
3419
0
        if( mbIs3DChart )
3420
0
            nTypeId = XML_line3DChart;
3421
0
        pFS->startElement(FSNS(XML_c, nTypeId));
3422
3423
0
        exportGrouping( );
3424
3425
0
        exportVaryColors(xChartType);
3426
        // TODO: show marker symbol in series?
3427
0
        bool bPrimaryAxes = true;
3428
0
        if (splitDataSeries.hasElements())
3429
0
            exportSeries_chart(xChartType, splitDataSeries, bPrimaryAxes);
3430
3431
        // show marker?
3432
0
        sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE;
3433
0
        Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
3434
0
        if( GetProperty( xPropSet, u"SymbolType"_ustr ) )
3435
0
            mAny >>= nSymbolType;
3436
3437
0
        if( !mbIs3DChart )
3438
0
        {
3439
0
            exportHiLowLines();
3440
0
            exportUpDownBars(xChartType);
3441
0
            const char* marker = nSymbolType == css::chart::ChartSymbolType::NONE? "0":"1";
3442
0
            pFS->singleElement(FSNS(XML_c, XML_marker), XML_val, marker);
3443
0
        }
3444
3445
0
        createAxes(bPrimaryAxes, true, false);
3446
3447
0
        pFS->endElement( FSNS( XML_c, nTypeId ) );
3448
0
    }
3449
0
}
3450
3451
void ChartExport::exportPieChart( const Reference< chart2::XChartType >& xChartType )
3452
0
{
3453
0
    FSHelperPtr pFS = GetFS();
3454
0
    sal_Int32 nTypeId = XML_pieChart;
3455
0
    if( mbIs3DChart )
3456
0
        nTypeId = XML_pie3DChart;
3457
0
    pFS->startElement(FSNS(XML_c, nTypeId));
3458
3459
0
    exportVaryColors(xChartType);
3460
3461
0
    bool bPrimaryAxes = true;
3462
0
    exportAllSeries(xChartType, bPrimaryAxes);
3463
3464
0
    if( !mbIs3DChart )
3465
0
    {
3466
        // firstSliceAng
3467
0
        exportFirstSliceAng( );
3468
0
    }
3469
3470
0
    pFS->endElement( FSNS( XML_c, nTypeId ) );
3471
0
}
3472
3473
void ChartExport::exportRadarChart( const Reference< chart2::XChartType >& xChartType)
3474
0
{
3475
0
    FSHelperPtr pFS = GetFS();
3476
0
    pFS->startElement(FSNS(XML_c, XML_radarChart));
3477
3478
    // radarStyle
3479
0
    sal_Int32 eChartType = getChartType( );
3480
0
    const char* radarStyle = nullptr;
3481
0
    if( eChartType == chart::TYPEID_RADARAREA )
3482
0
        radarStyle = "filled";
3483
0
    else
3484
0
        radarStyle = "marker";
3485
0
    pFS->singleElement(FSNS(XML_c, XML_radarStyle), XML_val, radarStyle);
3486
3487
0
    exportVaryColors(xChartType);
3488
0
    bool bPrimaryAxes = true;
3489
0
    exportAllSeries(xChartType, bPrimaryAxes);
3490
0
    createAxes(bPrimaryAxes, false, false);
3491
3492
0
    pFS->endElement( FSNS( XML_c, XML_radarChart ) );
3493
0
}
3494
3495
void ChartExport::exportScatterChartSeries( const Reference< chart2::XChartType >& xChartType,
3496
        const css::uno::Sequence<css::uno::Reference<chart2::XDataSeries>>* pSeries)
3497
0
{
3498
0
    FSHelperPtr pFS = GetFS();
3499
0
    pFS->startElement(FSNS(XML_c, XML_scatterChart));
3500
    // TODO:scatterStyle
3501
3502
0
    sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE;
3503
0
    Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
3504
0
    if( GetProperty( xPropSet, u"SymbolType"_ustr ) )
3505
0
        mAny >>= nSymbolType;
3506
3507
0
    const char* scatterStyle = "lineMarker";
3508
0
    if (nSymbolType == css::chart::ChartSymbolType::NONE)
3509
0
    {
3510
0
        scatterStyle = "line";
3511
0
    }
3512
3513
0
    pFS->singleElement(FSNS(XML_c, XML_scatterStyle), XML_val, scatterStyle);
3514
3515
0
    exportVaryColors(xChartType);
3516
    // FIXME: should export xVal and yVal
3517
0
    bool bPrimaryAxes = true;
3518
0
    if (pSeries)
3519
0
        exportSeries_chart(xChartType, *pSeries, bPrimaryAxes);
3520
0
    createAxes(bPrimaryAxes, false, false);
3521
    //exportAxesId(bPrimaryAxes);
3522
3523
0
    pFS->endElement( FSNS( XML_c, XML_scatterChart ) );
3524
0
}
3525
3526
void ChartExport::exportScatterChart( const Reference< chart2::XChartType >& xChartType )
3527
0
{
3528
0
    const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
3529
0
    bool bExported = false;
3530
0
    for (const auto& splitDataSeries : aSplitDataSeries)
3531
0
    {
3532
0
        if (!splitDataSeries.hasElements())
3533
0
            continue;
3534
3535
0
        bExported = true;
3536
0
        exportScatterChartSeries(xChartType, &splitDataSeries);
3537
0
    }
3538
0
    if (!bExported)
3539
0
        exportScatterChartSeries(xChartType, nullptr);
3540
0
}
3541
3542
void ChartExport::exportStockChart( const Reference< chart2::XChartType >& xChartType )
3543
0
{
3544
0
    FSHelperPtr pFS = GetFS();
3545
0
    std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
3546
0
    if (aSplitDataSeries.empty())
3547
0
    {
3548
        // Use a dummy data series to output needed basic chart-related XML even in case of empty charts
3549
0
        aSplitDataSeries.push_back({});
3550
0
    }
3551
3552
0
    sal_uInt32 nIdx = 0;
3553
0
    for (const auto& splitDataSeries : aSplitDataSeries)
3554
0
    {
3555
0
        pFS->startElement(FSNS(XML_c, XML_stockChart));
3556
3557
0
        bool bPrimaryAxes = true;
3558
0
        if (splitDataSeries.hasElements())
3559
0
            exportCandleStickSeries(splitDataSeries, bPrimaryAxes, nIdx);
3560
3561
        // export stock properties
3562
0
        Reference< css::chart::XStatisticDisplay > xStockPropProvider(mxDiagram, uno::UNO_QUERY);
3563
0
        if (xStockPropProvider.is())
3564
0
        {
3565
0
            exportHiLowLines();
3566
0
            exportUpDownBars(xChartType);
3567
0
        }
3568
3569
0
        createAxes(bPrimaryAxes, false, false);
3570
3571
0
        pFS->endElement(FSNS(XML_c, XML_stockChart));
3572
0
    }
3573
0
}
3574
3575
void ChartExport::exportHiLowLines()
3576
0
{
3577
0
    FSHelperPtr pFS = GetFS();
3578
    // export the chart property
3579
0
    Reference< css::chart::XStatisticDisplay > xChartPropProvider( mxDiagram, uno::UNO_QUERY );
3580
3581
0
    if (!xChartPropProvider.is())
3582
0
        return;
3583
3584
0
    Reference< beans::XPropertySet > xStockPropSet = xChartPropProvider->getMinMaxLine();
3585
0
    if( !xStockPropSet.is() )
3586
0
        return;
3587
3588
0
    pFS->startElement(FSNS(XML_c, XML_hiLowLines));
3589
0
    exportShapeProps( xStockPropSet, XML_c );
3590
0
    pFS->endElement( FSNS( XML_c, XML_hiLowLines ) );
3591
0
}
3592
3593
void ChartExport::exportUpDownBars( const Reference< chart2::XChartType >& xChartType)
3594
0
{
3595
0
    if(xChartType->getChartType() != "com.sun.star.chart2.CandleStickChartType")
3596
0
        return;
3597
3598
0
    FSHelperPtr pFS = GetFS();
3599
    // export the chart property
3600
0
    Reference< css::chart::XStatisticDisplay > xChartPropProvider( mxDiagram, uno::UNO_QUERY );
3601
0
    if(!xChartPropProvider.is())
3602
0
        return;
3603
3604
    //  updownbar
3605
0
    pFS->startElement(FSNS(XML_c, XML_upDownBars));
3606
    // TODO: gapWidth
3607
0
    pFS->singleElement(FSNS(XML_c, XML_gapWidth), XML_val, OString::number(150));
3608
3609
0
    Reference< beans::XPropertySet > xChartPropSet = xChartPropProvider->getUpBar();
3610
0
    if( xChartPropSet.is() )
3611
0
    {
3612
0
        pFS->startElement(FSNS(XML_c, XML_upBars));
3613
        // For Linechart with UpDownBars, spPr is not getting imported
3614
        // so no need to call the exportShapeProps() for LineChart
3615
0
        if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType")
3616
0
        {
3617
0
            exportShapeProps(xChartPropSet, XML_c);
3618
0
        }
3619
0
        pFS->endElement( FSNS( XML_c, XML_upBars ) );
3620
0
    }
3621
0
    xChartPropSet = xChartPropProvider->getDownBar();
3622
0
    if( xChartPropSet.is() )
3623
0
    {
3624
0
        pFS->startElement(FSNS(XML_c, XML_downBars));
3625
0
        if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType")
3626
0
        {
3627
0
            exportShapeProps(xChartPropSet, XML_c);
3628
0
        }
3629
0
        pFS->endElement( FSNS( XML_c, XML_downBars ) );
3630
0
    }
3631
0
    pFS->endElement( FSNS( XML_c, XML_upDownBars ) );
3632
0
}
3633
3634
void ChartExport::exportSurfaceChart( const Reference< chart2::XChartType >& xChartType )
3635
0
{
3636
0
    FSHelperPtr pFS = GetFS();
3637
0
    sal_Int32 nTypeId = XML_surfaceChart;
3638
0
    if( mbIs3DChart )
3639
0
        nTypeId = XML_surface3DChart;
3640
0
    pFS->startElement(FSNS(XML_c, nTypeId));
3641
0
    exportVaryColors(xChartType);
3642
0
    bool bPrimaryAxes = true;
3643
0
    exportAllSeries(xChartType, bPrimaryAxes);
3644
0
    createAxes(bPrimaryAxes, false, false);
3645
3646
0
    pFS->endElement( FSNS( XML_c, nTypeId ) );
3647
0
}
3648
3649
void ChartExport::exportAllSeries(const Reference<chart2::XChartType>& xChartType, bool& rPrimaryAxes)
3650
0
{
3651
0
    Reference< chart2::XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY );
3652
0
    if( ! xDSCnt.is())
3653
0
        return;
3654
3655
    // export dataseries for current chart-type
3656
0
    Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries());
3657
0
    exportSeries_chart(xChartType, aSeriesSeq, rPrimaryAxes);
3658
0
}
3659
3660
void ChartExport::exportVaryColors(const Reference<chart2::XChartType>& xChartType)
3661
0
{
3662
0
    FSHelperPtr pFS = GetFS();
3663
0
    try
3664
0
    {
3665
0
        Reference<chart2::XDataSeries> xDataSeries = getPrimaryDataSeries(xChartType);
3666
0
        Reference<beans::XPropertySet> xDataSeriesProps(xDataSeries, uno::UNO_QUERY_THROW);
3667
0
        Any aAnyVaryColors = xDataSeriesProps->getPropertyValue(u"VaryColorsByPoint"_ustr);
3668
0
        bool bVaryColors = false;
3669
0
        aAnyVaryColors >>= bVaryColors;
3670
0
        pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, ToPsz10(bVaryColors));
3671
0
    }
3672
0
    catch (...)
3673
0
    {
3674
0
        pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, "0");
3675
0
    }
3676
0
}
3677
3678
void ChartExport::exportSeries_chart( const Reference<chart2::XChartType>& xChartType,
3679
        const Sequence<Reference<chart2::XDataSeries> >& rSeriesSeq,
3680
        bool& rPrimaryAxes)
3681
0
{
3682
0
    OUString aLabelRole = xChartType->getRoleOfSequenceForSeriesLabel();
3683
0
    OUString aChartType( xChartType->getChartType());
3684
0
    sal_Int32 eChartType = lcl_getChartType( aChartType );
3685
3686
0
    for( const auto& rSeries : rSeriesSeq )
3687
0
    {
3688
        // export series
3689
0
        Reference< chart2::data::XDataSource > xSource( rSeries, uno::UNO_QUERY );
3690
0
        if( xSource.is())
3691
0
        {
3692
0
            Reference< chart2::XDataSeries > xDataSeries( xSource, uno::UNO_QUERY );
3693
0
            Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
3694
0
                xSource->getDataSequences());
3695
            // search for main sequence and create a series element
3696
0
            {
3697
0
                sal_Int32 nMainSequenceIndex = -1;
3698
0
                sal_Int32 nSeriesLength = 0;
3699
0
                Reference< chart2::data::XDataSequence > xValuesSeq;
3700
0
                Reference< chart2::data::XDataSequence > xLabelSeq;
3701
0
                sal_Int32 nSeqIdx=0;
3702
0
                for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx )
3703
0
                {
3704
0
                    Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() );
3705
0
                    if( nMainSequenceIndex==-1 )
3706
0
                    {
3707
0
                        Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY );
3708
0
                        OUString aRole;
3709
0
                        if( xSeqProp.is())
3710
0
                            xSeqProp->getPropertyValue(u"Role"_ustr) >>= aRole;
3711
                        // "main" sequence
3712
0
                        if( aRole == aLabelRole )
3713
0
                        {
3714
0
                            xValuesSeq.set( xTempValueSeq );
3715
0
                            xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel());
3716
0
                            nMainSequenceIndex = nSeqIdx;
3717
0
                        }
3718
0
                    }
3719
0
                    sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0));
3720
0
                    if( nSeriesLength < nSequenceLength )
3721
0
                        nSeriesLength = nSequenceLength;
3722
0
                }
3723
3724
                // have found the main sequence, then xValuesSeq and
3725
                // xLabelSeq contain those.  Otherwise both are empty
3726
0
                FSHelperPtr pFS = GetFS();
3727
3728
0
                pFS->startElement(FSNS(XML_c, XML_ser));
3729
3730
                // TODO: idx and order
3731
0
                pFS->singleElement( FSNS( XML_c, XML_idx ),
3732
0
                    XML_val, OString::number(mnSeriesCount) );
3733
0
                pFS->singleElement( FSNS( XML_c, XML_order ),
3734
0
                    XML_val, OString::number(mnSeriesCount++) );
3735
3736
                // export label
3737
0
                if( xLabelSeq.is() )
3738
0
                    exportSeriesText( xLabelSeq, false );
3739
3740
0
                Reference<XPropertySet> xPropSet(xDataSeries, UNO_QUERY_THROW);
3741
0
                if( GetProperty( xPropSet, u"AttachedAxisIndex"_ustr) )
3742
0
                {
3743
0
                    sal_Int32 nLocalAttachedAxis = 0;
3744
0
                    mAny >>= nLocalAttachedAxis;
3745
0
                    rPrimaryAxes = isPrimaryAxes(nLocalAttachedAxis);
3746
0
                }
3747
3748
                // export shape properties
3749
0
                Reference< XPropertySet > xOldPropSet = SchXMLSeriesHelper::createOldAPISeriesPropertySet(
3750
0
                    rSeries, getModel() );
3751
0
                if( xOldPropSet.is() )
3752
0
                {
3753
0
                    exportShapeProps( xOldPropSet, XML_c );
3754
0
                }
3755
3756
0
                switch( eChartType )
3757
0
                {
3758
0
                    case chart::TYPEID_BUBBLE:
3759
0
                    case chart::TYPEID_HORBAR:
3760
0
                    case chart::TYPEID_BAR:
3761
0
                    {
3762
0
                        pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0");
3763
0
                    }
3764
0
                    break;
3765
0
                    case chart::TYPEID_LINE:
3766
0
                    {
3767
0
                        exportMarker(xOldPropSet);
3768
0
                        break;
3769
0
                    }
3770
0
                    case chart::TYPEID_PIE:
3771
0
                    case chart::TYPEID_DOUGHNUT:
3772
0
                    {
3773
0
                        if( xOldPropSet.is() && GetProperty( xOldPropSet, u"SegmentOffset"_ustr) )
3774
0
                        {
3775
0
                            sal_Int32 nOffset = 0;
3776
0
                            mAny >>= nOffset;
3777
0
                            pFS->singleElement( FSNS( XML_c, XML_explosion ),
3778
0
                                XML_val, OString::number( nOffset ) );
3779
0
                        }
3780
0
                        break;
3781
0
                    }
3782
0
                    case chart::TYPEID_SCATTER:
3783
0
                    {
3784
0
                        exportMarker(xOldPropSet);
3785
0
                        break;
3786
0
                    }
3787
0
                    case chart::TYPEID_RADARLINE:
3788
0
                    {
3789
0
                        exportMarker(xOldPropSet);
3790
0
                        break;
3791
0
                    }
3792
0
                }
3793
3794
                // export data points
3795
0
                exportDataPoints( uno::Reference< beans::XPropertySet >( rSeries, uno::UNO_QUERY ), nSeriesLength, eChartType );
3796
3797
0
                DataLabelsRange aDLblsRange;
3798
                // export data labels
3799
0
                exportDataLabels(rSeries, nSeriesLength, eChartType, aDLblsRange, false);
3800
3801
0
                exportTrendlines( rSeries );
3802
3803
0
                if( eChartType != chart::TYPEID_PIE &&
3804
0
                        eChartType != chart::TYPEID_RADARLINE )
3805
0
                {
3806
                    //export error bars here
3807
0
                    Reference< XPropertySet > xSeriesPropSet( xSource, uno::UNO_QUERY );
3808
0
                    Reference< XPropertySet > xErrorBarYProps;
3809
0
                    xSeriesPropSet->getPropertyValue(u"ErrorBarY"_ustr) >>= xErrorBarYProps;
3810
0
                    if(xErrorBarYProps.is())
3811
0
                        exportErrorBar(xErrorBarYProps, true);
3812
0
                    if (eChartType != chart::TYPEID_BAR &&
3813
0
                            eChartType != chart::TYPEID_HORBAR)
3814
0
                    {
3815
0
                        Reference< XPropertySet > xErrorBarXProps;
3816
0
                        xSeriesPropSet->getPropertyValue(u"ErrorBarX"_ustr) >>= xErrorBarXProps;
3817
0
                        if(xErrorBarXProps.is())
3818
0
                            exportErrorBar(xErrorBarXProps, false);
3819
0
                    }
3820
0
                }
3821
3822
                // export categories
3823
0
                if( eChartType != chart::TYPEID_SCATTER && eChartType != chart::TYPEID_BUBBLE && mxCategoriesValues.is() )
3824
0
                    exportSeriesCategory( mxCategoriesValues );
3825
3826
0
                if( (eChartType == chart::TYPEID_SCATTER)
3827
0
                    || (eChartType == chart::TYPEID_BUBBLE) )
3828
0
                {
3829
                    // export xVal
3830
0
                    Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, u"values-x"_ustr ) );
3831
0
                    if( xSequence.is() )
3832
0
                    {
3833
0
                        Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
3834
0
                        if( xValues.is() )
3835
0
                            exportSeriesValues( xValues, XML_xVal );
3836
0
                    }
3837
0
                    else if( mxCategoriesValues.is() )
3838
0
                        exportSeriesCategory( mxCategoriesValues, XML_xVal );
3839
0
                }
3840
3841
0
                if( eChartType == chart::TYPEID_BUBBLE )
3842
0
                {
3843
                    // export yVal
3844
0
                    Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, u"values-y"_ustr ) );
3845
0
                    if( xSequence.is() )
3846
0
                    {
3847
0
                        Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
3848
0
                        if( xValues.is() )
3849
0
                            exportSeriesValues( xValues, XML_yVal );
3850
0
                    }
3851
0
                }
3852
3853
                // export values
3854
0
                if( xValuesSeq.is() )
3855
0
                {
3856
0
                    sal_Int32 nYValueType = XML_val;
3857
0
                    if( eChartType == chart::TYPEID_SCATTER )
3858
0
                        nYValueType = XML_yVal;
3859
0
                    else if( eChartType == chart::TYPEID_BUBBLE )
3860
0
                        nYValueType = XML_bubbleSize;
3861
0
                    exportSeriesValues( xValuesSeq, nYValueType );
3862
0
                }
3863
3864
0
                if( eChartType == chart::TYPEID_SCATTER
3865
0
                        || eChartType == chart::TYPEID_LINE )
3866
0
                    exportSmooth();
3867
3868
                // tdf103988: "corrupted" files with Bubble chart opening in MSO
3869
0
                if( eChartType == chart::TYPEID_BUBBLE )
3870
0
                    pFS->singleElement(FSNS(XML_c, XML_bubble3D), XML_val, "0");
3871
3872
0
                if (!aDLblsRange.empty())
3873
0
                    writeDataLabelsRange(pFS, GetFB(), aDLblsRange);
3874
3875
0
                pFS->endElement( FSNS( XML_c, XML_ser ) );
3876
0
            }
3877
0
        }
3878
0
    }
3879
0
}
3880
3881
void ChartExport::exportSeries_chartex( const Reference<chart2::XChartType>& xChartType,
3882
        const Sequence<Reference<chart2::XDataSeries> >& rSeriesSeq,
3883
        const char* sTypeName)
3884
0
{
3885
0
    OUString aLabelRole = xChartType->getRoleOfSequenceForSeriesLabel();
3886
0
    OUString aChartType( xChartType->getChartType());
3887
0
    sal_Int32 eChartType = lcl_getChartType( aChartType );
3888
3889
0
    sal_Int32 nSeriesCnt = 0;
3890
0
    for( const auto& rSeries : rSeriesSeq )
3891
0
    {
3892
        // export series
3893
0
        Reference< chart2::data::XDataSource > xSource( rSeries, uno::UNO_QUERY );
3894
0
        if( xSource.is())
3895
0
        {
3896
0
            FSHelperPtr pFS = GetFS();
3897
0
            pFS->startElement(FSNS(XML_cx, XML_series), XML_layoutId, sTypeName);
3898
3899
0
            Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
3900
0
                xSource->getDataSequences());
3901
3902
            // search for main sequence and create a series element
3903
0
            sal_Int32 nMainSequenceIndex = -1;
3904
0
            sal_Int32 nSeriesLength = 0;
3905
0
            Reference< chart2::data::XDataSequence > xLabelSeq;
3906
0
            sal_Int32 nSeqIdx=0;
3907
0
            for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx )
3908
0
            {
3909
0
                Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() );
3910
0
                if( nMainSequenceIndex==-1 )
3911
0
                {
3912
0
                    Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY );
3913
0
                    OUString aRole;
3914
0
                    if( xSeqProp.is())
3915
0
                        xSeqProp->getPropertyValue(u"Role"_ustr) >>= aRole;
3916
                    // "main" sequence
3917
0
                    if( aRole == aLabelRole )
3918
0
                    {
3919
0
                        xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel());
3920
0
                        nMainSequenceIndex = nSeqIdx;
3921
0
                    }
3922
0
                }
3923
0
                sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0));
3924
0
                if( nSeriesLength < nSequenceLength )
3925
0
                    nSeriesLength = nSequenceLength;
3926
0
            }
3927
3928
            // export label
3929
0
            if( xLabelSeq.is() )
3930
0
                exportSeriesText( xLabelSeq, true );
3931
3932
            // export shape properties
3933
0
            Reference< XPropertySet > xOldPropSet = SchXMLSeriesHelper::createOldAPISeriesPropertySet(
3934
0
                rSeries, getModel() );
3935
0
            if( xOldPropSet.is() )
3936
0
            {
3937
0
                exportShapeProps( xOldPropSet, XML_cx );
3938
0
            }
3939
3940
0
            DataLabelsRange aDLblsRange;
3941
            // export data labels
3942
0
            exportDataLabels(rSeries, nSeriesLength, eChartType, aDLblsRange, true);
3943
3944
            // dataId links to the correct data set in the <cx:chartData>. See
3945
            // DATA_ID_COMMENT
3946
0
            pFS->singleElement(FSNS(XML_cx, XML_dataId), XML_val,
3947
0
                    OString::number(nSeriesCnt++));
3948
3949
            // layoutPr
3950
0
            PropertySet aSeriesProp(rSeries);
3951
0
            sal_uInt32 nIntervalClosedChar = '\0';
3952
0
            if (aSeriesProp.getProperty(nIntervalClosedChar, PROP_IntervalClosed)) {
3953
0
                pFS->startElement(FSNS(XML_cx, XML_layoutPr));
3954
3955
0
                switch (nIntervalClosedChar) {
3956
0
                    case 'l' :
3957
0
                        pFS->singleElement(FSNS(XML_cx, XML_binning), XML_intervalClosed, "l");
3958
0
                        break;
3959
0
                    case 'r' :
3960
0
                        pFS->singleElement(FSNS(XML_cx, XML_binning), XML_intervalClosed, "r");
3961
0
                        break;
3962
0
                    default:
3963
0
                        assert(false);
3964
0
                        break;
3965
0
                }
3966
3967
0
                pFS->endElement(FSNS(XML_cx, XML_layoutPr));
3968
0
            }
3969
3970
            // axisId
3971
3972
            // extLst
3973
3974
0
            pFS->endElement(FSNS(XML_cx, XML_series));
3975
0
        }
3976
0
    }
3977
0
}
3978
3979
void ChartExport::exportCandleStickSeries(
3980
    const Sequence<Reference<chart2::XDataSeries>>& aSeriesSeq, bool& rPrimaryAxes, sal_uInt32& nIdx)
3981
0
{
3982
0
    for( const Reference< chart2::XDataSeries >& xSeries : aSeriesSeq )
3983
0
    {
3984
0
        rPrimaryAxes = lcl_isSeriesAttachedToFirstAxis(xSeries);
3985
3986
0
        Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
3987
0
        if( xSource.is())
3988
0
        {
3989
            // export series in correct order (as we don't store roles)
3990
            // with japanese candlesticks: open, low, high, close
3991
            // otherwise: low, high, close
3992
0
            Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
3993
0
                xSource->getDataSequences());
3994
3995
0
            const char* sSeries[] = {"values-first","values-max","values-min","values-last"};
3996
3997
0
            for (const char* series : sSeries)
3998
0
            {
3999
0
                Reference<chart2::data::XLabeledDataSequence> xLabeledSeq(
4000
0
                    lcl_getDataSequenceByRole(aSeqCnt, OUString::createFromAscii(series)));
4001
0
                if (xLabeledSeq.is())
4002
0
                {
4003
0
                    Reference< chart2::data::XDataSequence > xLabelSeq( xLabeledSeq->getLabel());
4004
0
                    Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues());
4005
0
                    {
4006
0
                        FSHelperPtr pFS = GetFS();
4007
0
                        pFS->startElement(FSNS(XML_c, XML_ser));
4008
4009
0
                        pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(++nIdx));
4010
0
                        pFS->singleElement(FSNS(XML_c, XML_order), XML_val, OString::number(nIdx));
4011
4012
                        // export label
4013
0
                        if( xLabelSeq.is() )
4014
0
                            exportSeriesText( xLabelSeq, false );
4015
4016
                        // TODO:export shape properties
4017
4018
                        // export categories
4019
0
                        if( mxCategoriesValues.is() )
4020
0
                            exportSeriesCategory( mxCategoriesValues );
4021
4022
                        // export values
4023
0
                        if( xValueSeq.is() )
4024
0
                            exportSeriesValues( xValueSeq );
4025
4026
0
                        pFS->endElement( FSNS( XML_c, XML_ser ) );
4027
0
                    }
4028
0
                }
4029
0
            }
4030
0
        }
4031
0
    }
4032
0
}
4033
4034
void ChartExport::exportSeriesText( const Reference< chart2::data::XDataSequence > & xValueSeq,
4035
        bool bIsChartex)
4036
0
{
4037
0
    FSHelperPtr pFS = GetFS();
4038
4039
0
    OUString aLabelString = lcl_flattenStringSequence(lcl_getLabelSequence(xValueSeq));
4040
4041
0
    if (bIsChartex) {
4042
0
        lcl_writeChartexString(pFS, aLabelString);
4043
0
    } else {
4044
0
        pFS->startElement(FSNS(XML_c, XML_tx));
4045
4046
0
        OUString aCellRange =  xValueSeq->getSourceRangeRepresentation();
4047
0
        aCellRange = parseFormula( aCellRange );
4048
0
        pFS->startElement(FSNS(XML_c, XML_strRef));
4049
4050
0
        pFS->startElement(FSNS(XML_c, XML_f));
4051
0
        pFS->writeEscaped( aCellRange );
4052
0
        pFS->endElement( FSNS( XML_c, XML_f ) );
4053
4054
0
        pFS->startElement(FSNS(XML_c, XML_strCache));
4055
0
        pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, "1");
4056
0
        pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, "0");
4057
0
        pFS->startElement(FSNS(XML_c, XML_v));
4058
0
        pFS->writeEscaped( aLabelString );
4059
0
        pFS->endElement( FSNS( XML_c, XML_v ) );
4060
0
        pFS->endElement( FSNS( XML_c, XML_pt ) );
4061
0
        pFS->endElement( FSNS( XML_c, XML_strCache ) );
4062
0
        pFS->endElement( FSNS( XML_c, XML_strRef ) );
4063
0
        pFS->endElement( FSNS( XML_c, XML_tx ) );
4064
0
    }
4065
0
}
4066
4067
void ChartExport::exportSeriesCategory( const Reference< chart2::data::XDataSequence > & xValueSeq, sal_Int32 nValueType )
4068
0
{
4069
0
    FSHelperPtr pFS = GetFS();
4070
0
    pFS->startElement(FSNS(XML_c, nValueType));
4071
4072
0
    OUString aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString();
4073
0
    const Sequence< Sequence< OUString >> aFinalSplitSource = (nValueType == XML_cat) ? getSplitCategoriesList(aCellRange) : Sequence< Sequence< OUString>>(0);
4074
0
    aCellRange = parseFormula( aCellRange );
4075
4076
0
    if(aFinalSplitSource.getLength() > 1)
4077
0
    {
4078
        // export multi level category axis labels
4079
0
        pFS->startElement(FSNS(XML_c, XML_multiLvlStrRef));
4080
4081
0
        pFS->startElement(FSNS(XML_c, XML_f));
4082
0
        pFS->writeEscaped(aCellRange);
4083
0
        pFS->endElement(FSNS(XML_c, XML_f));
4084
4085
0
        pFS->startElement(FSNS(XML_c, XML_multiLvlStrCache));
4086
0
        pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(aFinalSplitSource[0].getLength()));
4087
0
        for(const auto& rSeq : aFinalSplitSource)
4088
0
        {
4089
0
            pFS->startElement(FSNS(XML_c, XML_lvl));
4090
0
            for(sal_Int32 j = 0; j < rSeq.getLength(); j++)
4091
0
            {
4092
0
                if(!rSeq[j].isEmpty())
4093
0
                {
4094
0
                    pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(j));
4095
0
                    pFS->startElement(FSNS(XML_c, XML_v));
4096
0
                    pFS->writeEscaped(rSeq[j]);
4097
0
                    pFS->endElement(FSNS(XML_c, XML_v));
4098
0
                    pFS->endElement(FSNS(XML_c, XML_pt));
4099
0
                }
4100
0
            }
4101
0
            pFS->endElement(FSNS(XML_c, XML_lvl));
4102
0
        }
4103
4104
0
        pFS->endElement(FSNS(XML_c, XML_multiLvlStrCache));
4105
0
        pFS->endElement(FSNS(XML_c, XML_multiLvlStrRef));
4106
0
    }
4107
0
    else
4108
0
    {
4109
        // export single category axis labels
4110
0
        bool bWriteDateCategories = mbHasDateCategories && (nValueType == XML_cat);
4111
0
        OUString aNumberFormatString;
4112
0
        if (bWriteDateCategories)
4113
0
        {
4114
0
            Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY );
4115
0
            if( xAxisXSupp.is())
4116
0
            {
4117
0
                Reference< XPropertySet > xAxisProp = xAxisXSupp->getXAxis();
4118
0
                if (GetProperty(xAxisProp, u"NumberFormat"_ustr))
4119
0
                {
4120
0
                    sal_Int32 nKey = 0;
4121
0
                    mAny >>= nKey;
4122
0
                    aNumberFormatString = getNumberFormatCode(nKey);
4123
0
                }
4124
0
            }
4125
0
            if (aNumberFormatString.isEmpty())
4126
0
                bWriteDateCategories = false;
4127
0
        }
4128
4129
0
        pFS->startElement(FSNS(XML_c, bWriteDateCategories ? XML_numRef : XML_strRef));
4130
4131
0
        pFS->startElement(FSNS(XML_c, XML_f));
4132
0
        pFS->writeEscaped(aCellRange);
4133
0
        pFS->endElement(FSNS(XML_c, XML_f));
4134
4135
0
        pFS->startElement(FSNS(XML_c, bWriteDateCategories ? XML_numCache : XML_strCache));
4136
0
        if (bWriteDateCategories)
4137
0
        {
4138
0
            pFS->startElement(FSNS(XML_c, XML_formatCode));
4139
0
            pFS->writeEscaped(aNumberFormatString);
4140
0
            pFS->endElement(FSNS(XML_c, XML_formatCode));
4141
4142
0
            std::vector<double> aDateCategories = lcl_getAllValuesFromSequence(xValueSeq);
4143
0
            const sal_Int32 ptCount = aDateCategories.size();
4144
0
            pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount));
4145
0
            for (sal_Int32 i = 0; i < ptCount; i++)
4146
0
            {
4147
0
                if (!std::isnan(aDateCategories[i]))
4148
0
                {
4149
0
                    pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i));
4150
0
                    pFS->startElement(FSNS(XML_c, XML_v));
4151
0
                    pFS->write(OString::number(aDateCategories[i]));
4152
0
                    pFS->endElement(FSNS(XML_c, XML_v));
4153
0
                    pFS->endElement(FSNS(XML_c, XML_pt));
4154
0
                }
4155
0
            }
4156
0
        }
4157
0
        else
4158
0
        {
4159
0
            std::vector<OUString> aCategories;
4160
0
            lcl_fillCategoriesIntoStringVector(xValueSeq, aCategories);
4161
0
            const sal_Int32 ptCount = aCategories.size();
4162
0
            pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount));
4163
0
            for (sal_Int32 i = 0; i < ptCount; i++)
4164
0
            {
4165
0
                pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i));
4166
0
                pFS->startElement(FSNS(XML_c, XML_v));
4167
0
                pFS->writeEscaped(aCategories[i]);
4168
0
                pFS->endElement(FSNS(XML_c, XML_v));
4169
0
                pFS->endElement(FSNS(XML_c, XML_pt));
4170
0
            }
4171
0
        }
4172
4173
0
        pFS->endElement(FSNS(XML_c, bWriteDateCategories ? XML_numCache : XML_strCache));
4174
0
        pFS->endElement(FSNS(XML_c, bWriteDateCategories ? XML_numRef : XML_strRef));
4175
0
    }
4176
4177
0
    pFS->endElement( FSNS( XML_c, nValueType ) );
4178
0
}
4179
4180
void ChartExport::exportSeriesValues( const Reference< chart2::data::XDataSequence > & xValueSeq, sal_Int32 nValueType )
4181
0
{
4182
0
    FSHelperPtr pFS = GetFS();
4183
0
    pFS->startElement(FSNS(XML_c, nValueType));
4184
4185
0
    OUString aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString();
4186
0
    aCellRange = parseFormula( aCellRange );
4187
    // TODO: need to handle XML_multiLvlStrRef according to aCellRange
4188
0
    pFS->startElement(FSNS(XML_c, XML_numRef));
4189
4190
0
    pFS->startElement(FSNS(XML_c, XML_f));
4191
0
    pFS->writeEscaped( aCellRange );
4192
0
    pFS->endElement( FSNS( XML_c, XML_f ) );
4193
4194
0
    ::std::vector< double > aValues = lcl_getAllValuesFromSequence( xValueSeq );
4195
0
    sal_Int32 ptCount = aValues.size();
4196
0
    pFS->startElement(FSNS(XML_c, XML_numCache));
4197
0
    pFS->startElement(FSNS(XML_c, XML_formatCode));
4198
0
    OUString sNumberFormatString(u"General"_ustr);
4199
0
    const sal_Int32 nKey = xValueSeq.is() ? xValueSeq->getNumberFormatKeyByIndex(-1) : 0;
4200
0
    if (nKey > 0)
4201
0
        sNumberFormatString = getNumberFormatCode(nKey);
4202
0
    pFS->writeEscaped(sNumberFormatString);
4203
0
    pFS->endElement( FSNS( XML_c, XML_formatCode ) );
4204
0
    pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount));
4205
4206
0
    for( sal_Int32 i = 0; i < ptCount; i++ )
4207
0
    {
4208
0
        if (!std::isnan(aValues[i]))
4209
0
        {
4210
0
            pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i));
4211
0
            pFS->startElement(FSNS(XML_c, XML_v));
4212
0
            pFS->write(aValues[i]);
4213
0
            pFS->endElement(FSNS(XML_c, XML_v));
4214
0
            pFS->endElement(FSNS(XML_c, XML_pt));
4215
0
        }
4216
0
    }
4217
4218
0
    pFS->endElement( FSNS( XML_c, XML_numCache ) );
4219
0
    pFS->endElement( FSNS( XML_c, XML_numRef ) );
4220
0
    pFS->endElement( FSNS( XML_c, nValueType ) );
4221
0
}
4222
4223
void ChartExport::exportShapeProps( const Reference< XPropertySet >& xPropSet,
4224
        sal_Int32 nNS)
4225
0
{
4226
0
    FSHelperPtr pFS = GetFS();
4227
0
    pFS->startElement(FSNS(nNS, XML_spPr));
4228
4229
0
    exportFill( xPropSet );
4230
0
    WriteOutline( xPropSet, getModel() );
4231
4232
0
    pFS->endElement( FSNS( nNS, XML_spPr ) );
4233
0
}
4234
4235
void ChartExport::exportTextProps(const Reference<XPropertySet>& xPropSet,
4236
        bool bIsChartex)
4237
0
{
4238
0
    FSHelperPtr pFS = GetFS();
4239
4240
0
    const sal_Int32 nChartNS = bIsChartex ? XML_cx : XML_c;
4241
0
    pFS->startElement(FSNS(nChartNS, XML_txPr));
4242
4243
0
    sal_Int32 nRotation = 0;
4244
0
    const char* textWordWrap = nullptr;
4245
4246
0
    if (auto xServiceInfo = uno::Reference<lang::XServiceInfo>(xPropSet, uno::UNO_QUERY))
4247
0
    {
4248
0
        double fMultiplier = 0.0;
4249
        // We have at least two possible units of returned value: degrees (e.g., for data labels),
4250
        // and 100ths of degree (e.g., for axes labels). The latter is returned as an Any wrapping
4251
        // a sal_Int32 value (see WrappedTextRotationProperty::convertInnerToOuterValue), while
4252
        // the former is double. So we could test the contained type to decide which multiplier to
4253
        // use. But testing the service info should be more robust.
4254
0
        if (xServiceInfo->supportsService(u"com.sun.star.chart.ChartAxis"_ustr))
4255
0
            fMultiplier = -600.0;
4256
0
        else if (xServiceInfo->supportsService(u"com.sun.star.chart2.DataSeries"_ustr) || xServiceInfo->supportsService(u"com.sun.star.chart2.DataPointProperties"_ustr))
4257
0
        {
4258
0
            fMultiplier = -60000.0;
4259
0
            bool bTextWordWrap = false;
4260
0
            if ((xPropSet->getPropertyValue(u"TextWordWrap"_ustr) >>= bTextWordWrap) && bTextWordWrap)
4261
0
                textWordWrap = "square";
4262
0
            else
4263
0
                textWordWrap = "none";
4264
0
        }
4265
4266
0
        if (fMultiplier)
4267
0
        {
4268
0
            double fTextRotation = 0.0;
4269
0
            uno::Any aAny = xPropSet->getPropertyValue(u"TextRotation"_ustr);
4270
0
            if (aAny.hasValue() && (aAny >>= fTextRotation))
4271
0
            {
4272
0
                fTextRotation *= fMultiplier;
4273
                // The MS Office UI allows values only in range of [-90,90].
4274
0
                if (fTextRotation < -5400000.0 && fTextRotation > -16200000.0)
4275
0
                {
4276
                    // Reflect the angle if the value is between 90° and 270°
4277
0
                    fTextRotation += 10800000.0;
4278
0
                }
4279
0
                else if (fTextRotation <= -16200000.0)
4280
0
                {
4281
0
                    fTextRotation += 21600000.0;
4282
0
                }
4283
0
                nRotation = std::round(fTextRotation);
4284
0
            }
4285
0
        }
4286
0
    }
4287
4288
0
    if (nRotation)
4289
0
        pFS->singleElement(FSNS(XML_a, XML_bodyPr), XML_rot, OString::number(nRotation), XML_wrap, textWordWrap);
4290
0
    else
4291
0
        pFS->singleElement(FSNS(XML_a, XML_bodyPr), XML_wrap, textWordWrap);
4292
4293
0
    pFS->singleElement(FSNS(XML_a, XML_lstStyle));
4294
4295
0
    pFS->startElement(FSNS(XML_a, XML_p));
4296
0
    pFS->startElement(FSNS(XML_a, XML_pPr));
4297
4298
0
    WriteRunInput aInput;
4299
0
    aInput.bCheckDirect = true;
4300
0
    aInput.bUseTextSchemeColors = true;
4301
0
    WriteRunProperties(xPropSet, XML_defRPr, aInput);
4302
4303
0
    pFS->endElement(FSNS(XML_a, XML_pPr));
4304
0
    pFS->endElement(FSNS(XML_a, XML_p));
4305
0
    pFS->endElement(FSNS(nChartNS, XML_txPr));
4306
0
}
4307
4308
void ChartExport::InitPlotArea( )
4309
0
{
4310
0
    Reference< XPropertySet > xDiagramProperties (mxDiagram, uno::UNO_QUERY);
4311
4312
    //    Check for supported services and then the properties provided by this service.
4313
0
    Reference<lang::XServiceInfo> xServiceInfo (mxDiagram, uno::UNO_QUERY);
4314
0
    if (xServiceInfo.is())
4315
0
    {
4316
0
        if (xServiceInfo->supportsService(u"com.sun.star.chart.ChartAxisZSupplier"_ustr))
4317
0
        {
4318
0
            xDiagramProperties->getPropertyValue(u"HasZAxis"_ustr) >>= mbHasZAxis;
4319
0
        }
4320
0
    }
4321
4322
0
    xDiagramProperties->getPropertyValue(u"Dim3D"_ustr) >>=  mbIs3DChart;
4323
4324
0
    if( mbHasCategoryLabels && mxNewDiagram.is())
4325
0
    {
4326
0
        Reference< chart2::data::XLabeledDataSequence > xCategories(
4327
0
                lcl_getCategories( mxNewDiagram, &mbHasDateCategories ) );
4328
0
        if( xCategories.is() )
4329
0
        {
4330
0
            mxCategoriesValues.set( xCategories->getValues() );
4331
0
        }
4332
0
    }
4333
0
}
4334
4335
void ChartExport::exportAxes( bool bIsChartex )
4336
0
{
4337
0
    sal_Int32 nSize = maAxes.size();
4338
    // let's export the axis types in the right order
4339
0
    for ( sal_Int32 nSortIdx = AXIS_PRIMARY_X; nSortIdx <= AXIS_SECONDARY_Y; nSortIdx++ )
4340
0
    {
4341
0
        for ( sal_Int32 nIdx = 0; nIdx < nSize; nIdx++ )
4342
0
        {
4343
0
            if (nSortIdx == maAxes[nIdx].nAxisType)
4344
0
                exportAxis( maAxes[nIdx], bIsChartex );
4345
0
        }
4346
0
    }
4347
0
}
4348
4349
namespace {
4350
4351
sal_Int32 getXAxisTypeByChartType(sal_Int32 eChartType)
4352
0
{
4353
0
    if( (eChartType == chart::TYPEID_SCATTER)
4354
0
            || (eChartType == chart::TYPEID_BUBBLE) )
4355
0
        return  XML_valAx;
4356
0
    else if( eChartType == chart::TYPEID_STOCK )
4357
0
        return  XML_dateAx;
4358
4359
0
    return XML_catAx;
4360
0
}
4361
4362
sal_Int32 getRealXAxisType(sal_Int32 nAxisType)
4363
0
{
4364
0
    if( nAxisType == chart2::AxisType::CATEGORY )
4365
0
        return XML_catAx;
4366
0
    else if( nAxisType == chart2::AxisType::DATE )
4367
0
        return XML_dateAx;
4368
0
    else if( nAxisType == chart2::AxisType::SERIES )
4369
0
        return XML_serAx;
4370
4371
0
    return XML_valAx;
4372
0
}
4373
4374
}
4375
4376
void ChartExport::exportAxis(const AxisIdPair& rAxisIdPair, bool bIsChartex)
4377
0
{
4378
    // get some properties from document first
4379
0
    bool bHasXAxisTitle = false,
4380
0
         bHasYAxisTitle = false,
4381
0
         bHasZAxisTitle = false,
4382
0
         bHasSecondaryXAxisTitle = false,
4383
0
         bHasSecondaryYAxisTitle = false;
4384
0
    bool bHasXAxisMajorGrid = false,
4385
0
         bHasXAxisMinorGrid = false,
4386
0
         bHasYAxisMajorGrid = false,
4387
0
         bHasYAxisMinorGrid = false,
4388
0
         bHasZAxisMajorGrid = false,
4389
0
         bHasZAxisMinorGrid = false;
4390
4391
0
    Reference< XPropertySet > xDiagramProperties (mxDiagram, uno::UNO_QUERY);
4392
4393
0
    xDiagramProperties->getPropertyValue(u"HasXAxisTitle"_ustr) >>= bHasXAxisTitle;
4394
0
    xDiagramProperties->getPropertyValue(u"HasYAxisTitle"_ustr) >>= bHasYAxisTitle;
4395
0
    xDiagramProperties->getPropertyValue(u"HasZAxisTitle"_ustr) >>= bHasZAxisTitle;
4396
0
    xDiagramProperties->getPropertyValue(u"HasSecondaryXAxisTitle"_ustr) >>=  bHasSecondaryXAxisTitle;
4397
0
    xDiagramProperties->getPropertyValue(u"HasSecondaryYAxisTitle"_ustr) >>=  bHasSecondaryYAxisTitle;
4398
4399
0
    xDiagramProperties->getPropertyValue(u"HasXAxisGrid"_ustr) >>=  bHasXAxisMajorGrid;
4400
0
    xDiagramProperties->getPropertyValue(u"HasYAxisGrid"_ustr) >>=  bHasYAxisMajorGrid;
4401
0
    xDiagramProperties->getPropertyValue(u"HasZAxisGrid"_ustr) >>=  bHasZAxisMajorGrid;
4402
4403
0
    xDiagramProperties->getPropertyValue(u"HasXAxisHelpGrid"_ustr) >>=  bHasXAxisMinorGrid;
4404
0
    xDiagramProperties->getPropertyValue(u"HasYAxisHelpGrid"_ustr) >>=  bHasYAxisMinorGrid;
4405
0
    xDiagramProperties->getPropertyValue(u"HasZAxisHelpGrid"_ustr) >>=  bHasZAxisMinorGrid;
4406
4407
0
    Reference< XPropertySet > xAxisProp;
4408
0
    Reference< drawing::XShape > xAxisTitle;
4409
0
    Reference< beans::XPropertySet > xMajorGrid;
4410
0
    Reference< beans::XPropertySet > xMinorGrid;
4411
0
    sal_Int32 nAxisType = XML_catAx;
4412
0
    const char* sAxPos = nullptr;
4413
4414
0
    switch( rAxisIdPair.nAxisType )
4415
0
    {
4416
0
        case AXIS_PRIMARY_X:
4417
0
        {
4418
0
            Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY );
4419
0
            if( xAxisXSupp.is())
4420
0
                xAxisProp = xAxisXSupp->getXAxis();
4421
0
            if( bHasXAxisTitle )
4422
0
                xAxisTitle = xAxisXSupp->getXAxisTitle();
4423
0
            if( bHasXAxisMajorGrid )
4424
0
                xMajorGrid = xAxisXSupp->getXMainGrid();
4425
0
            if( bHasXAxisMinorGrid )
4426
0
                xMinorGrid = xAxisXSupp->getXHelpGrid();
4427
4428
0
            nAxisType = lcl_getCategoryAxisType(mxNewDiagram, 0, 0);
4429
0
            if( nAxisType != -1 )
4430
0
                nAxisType = getRealXAxisType(nAxisType);
4431
0
            else
4432
0
                nAxisType = getXAxisTypeByChartType( getChartType() );
4433
            // FIXME: axPos, need to check axis direction
4434
0
            sAxPos = "b";
4435
0
            break;
4436
0
        }
4437
0
        case AXIS_PRIMARY_Y:
4438
0
        {
4439
0
            Reference< css::chart::XAxisYSupplier > xAxisYSupp( mxDiagram, uno::UNO_QUERY );
4440
0
            if( xAxisYSupp.is())
4441
0
                xAxisProp = xAxisYSupp->getYAxis();
4442
0
            if( bHasYAxisTitle )
4443
0
                xAxisTitle = xAxisYSupp->getYAxisTitle();
4444
0
            if( bHasYAxisMajorGrid )
4445
0
                xMajorGrid = xAxisYSupp->getYMainGrid();
4446
0
            if( bHasYAxisMinorGrid )
4447
0
                xMinorGrid = xAxisYSupp->getYHelpGrid();
4448
4449
0
            nAxisType = XML_valAx;
4450
            // FIXME: axPos, need to check axis direction
4451
0
            sAxPos = "l";
4452
0
            break;
4453
0
        }
4454
0
        case AXIS_PRIMARY_Z:
4455
0
        {
4456
0
            Reference< css::chart::XAxisZSupplier > xAxisZSupp( mxDiagram, uno::UNO_QUERY );
4457
0
            if( xAxisZSupp.is())
4458
0
                xAxisProp = xAxisZSupp->getZAxis();
4459
0
            if( bHasZAxisTitle )
4460
0
                xAxisTitle = xAxisZSupp->getZAxisTitle();
4461
0
            if( bHasZAxisMajorGrid )
4462
0
                xMajorGrid = xAxisZSupp->getZMainGrid();
4463
0
            if( bHasZAxisMinorGrid )
4464
0
                xMinorGrid = xAxisZSupp->getZHelpGrid();
4465
4466
0
            sal_Int32 eChartType = getChartType( );
4467
0
            if( (eChartType == chart::TYPEID_SCATTER)
4468
0
                || (eChartType == chart::TYPEID_BUBBLE) )
4469
0
                nAxisType = XML_valAx;
4470
0
            else if( eChartType == chart::TYPEID_STOCK )
4471
0
                nAxisType = XML_dateAx;
4472
0
            else if( eChartType == chart::TYPEID_BAR || eChartType == chart::TYPEID_AREA )
4473
0
                nAxisType = XML_serAx;
4474
            // FIXME: axPos, need to check axis direction
4475
0
            sAxPos = "b";
4476
0
            break;
4477
0
        }
4478
0
        case AXIS_SECONDARY_X:
4479
0
        {
4480
0
            Reference< css::chart::XTwoAxisXSupplier > xAxisTwoXSupp( mxDiagram, uno::UNO_QUERY );
4481
0
            if( xAxisTwoXSupp.is())
4482
0
                xAxisProp = xAxisTwoXSupp->getSecondaryXAxis();
4483
0
            if( bHasSecondaryXAxisTitle )
4484
0
            {
4485
0
                Reference< css::chart::XSecondAxisTitleSupplier > xAxisSupp( mxDiagram, uno::UNO_QUERY );
4486
0
                xAxisTitle = xAxisSupp->getSecondXAxisTitle();
4487
0
            }
4488
4489
0
            nAxisType = lcl_getCategoryAxisType(mxNewDiagram, 0, 1);
4490
0
            if( nAxisType != -1 )
4491
0
                nAxisType = getRealXAxisType(nAxisType);
4492
0
            else
4493
0
                nAxisType = getXAxisTypeByChartType( getChartType() );
4494
            // FIXME: axPos, need to check axis direction
4495
0
            sAxPos = "t";
4496
0
            break;
4497
0
        }
4498
0
        case AXIS_SECONDARY_Y:
4499
0
        {
4500
0
            Reference< css::chart::XTwoAxisYSupplier > xAxisTwoYSupp( mxDiagram, uno::UNO_QUERY );
4501
0
            if( xAxisTwoYSupp.is())
4502
0
                xAxisProp = xAxisTwoYSupp->getSecondaryYAxis();
4503
0
            if( bHasSecondaryYAxisTitle )
4504
0
            {
4505
0
                Reference< css::chart::XSecondAxisTitleSupplier > xAxisSupp( mxDiagram, uno::UNO_QUERY );
4506
0
                xAxisTitle = xAxisSupp->getSecondYAxisTitle();
4507
0
            }
4508
4509
0
            nAxisType = XML_valAx;
4510
            // FIXME: axPos, need to check axis direction
4511
0
            sAxPos = "r";
4512
0
            break;
4513
0
        }
4514
0
    }
4515
4516
0
    if (bIsChartex) {
4517
0
        exportOneAxis_chartex(xAxisProp, xAxisTitle, xMajorGrid, xMinorGrid, nAxisType,
4518
0
                rAxisIdPair);
4519
0
    } else {
4520
0
        exportOneAxis_chart(xAxisProp, xAxisTitle, xMajorGrid, xMinorGrid, nAxisType,
4521
0
                sAxPos, rAxisIdPair);
4522
0
    }
4523
0
}
4524
4525
static const char *getTickMarkLocStr(sal_Int32 nValue)
4526
0
{
4527
0
    const bool bInner = nValue & css::chart::ChartAxisMarks::INNER;
4528
0
    const bool bOuter = nValue & css::chart::ChartAxisMarks::OUTER;
4529
0
    if( bInner && bOuter ) {
4530
0
        return "cross";
4531
0
    } else if( bInner ) {
4532
0
        return "in";
4533
0
    } else if( bOuter ) {
4534
0
        return "out";
4535
0
    } else {
4536
0
        return "none";
4537
0
    }
4538
0
}
4539
4540
void ChartExport::exportOneAxis_chart(
4541
    const Reference< XPropertySet >& xAxisProp,
4542
    const Reference< drawing::XShape >& xAxisTitle,
4543
    const Reference< XPropertySet >& xMajorGrid,
4544
    const Reference< XPropertySet >& xMinorGrid,
4545
    sal_Int32 nAxisType,
4546
    const char* sAxisPos,
4547
    const AxisIdPair& rAxisIdPair)
4548
0
{
4549
0
    FSHelperPtr pFS = GetFS();
4550
0
    pFS->startElement(FSNS(XML_c, nAxisType));
4551
0
    pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(rAxisIdPair.nAxisId));
4552
4553
0
    pFS->startElement(FSNS(XML_c, XML_scaling));
4554
4555
    // logBase, min, max
4556
0
    if(GetProperty( xAxisProp, u"Logarithmic"_ustr ) )
4557
0
    {
4558
0
        bool bLogarithmic = false;
4559
0
        mAny >>= bLogarithmic;
4560
0
        if( bLogarithmic )
4561
0
        {
4562
            // default value is 10?
4563
0
            pFS->singleElement(FSNS(XML_c, XML_logBase), XML_val, OString::number(10));
4564
0
        }
4565
0
    }
4566
4567
    // orientation: minMax, maxMin
4568
0
    bool bReverseDirection = false;
4569
0
    if(GetProperty( xAxisProp, u"ReverseDirection"_ustr ) )
4570
0
        mAny >>= bReverseDirection;
4571
4572
0
    const char* orientation = bReverseDirection ? "maxMin":"minMax";
4573
0
    pFS->singleElement(FSNS(XML_c, XML_orientation), XML_val, orientation);
4574
4575
0
    bool bAutoMax = false;
4576
0
    if(GetProperty( xAxisProp, u"AutoMax"_ustr ) )
4577
0
        mAny >>= bAutoMax;
4578
4579
0
    if( !bAutoMax && (GetProperty( xAxisProp, u"Max"_ustr ) ) )
4580
0
    {
4581
0
        double dMax = 0;
4582
0
        mAny >>= dMax;
4583
0
        pFS->singleElement(FSNS(XML_c, XML_max), XML_val, OString::number(dMax));
4584
0
    }
4585
4586
0
    bool bAutoMin = false;
4587
0
    if(GetProperty( xAxisProp, u"AutoMin"_ustr ) )
4588
0
        mAny >>= bAutoMin;
4589
4590
0
    if( !bAutoMin && (GetProperty( xAxisProp, u"Min"_ustr ) ) )
4591
0
    {
4592
0
        double dMin = 0;
4593
0
        mAny >>= dMin;
4594
0
        pFS->singleElement(FSNS(XML_c, XML_min), XML_val, OString::number(dMin));
4595
0
    }
4596
4597
0
    pFS->endElement( FSNS( XML_c, XML_scaling ) );
4598
4599
0
    bool bVisible = true;
4600
0
    if( xAxisProp.is() )
4601
0
    {
4602
0
        xAxisProp->getPropertyValue(u"Visible"_ustr) >>=  bVisible;
4603
0
    }
4604
4605
    // only export each axis only once non-deleted
4606
0
    auto aItInsertedPair = maExportedAxis.insert(rAxisIdPair.nAxisType);
4607
0
    bool bDeleted = !aItInsertedPair.second;
4608
4609
0
    pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, !bDeleted && bVisible ? "0" : "1");
4610
4611
    // FIXME: axPos, need to check the property "ReverseDirection"
4612
0
    pFS->singleElement(FSNS(XML_c, XML_axPos), XML_val, sAxisPos);
4613
    // major grid line
4614
0
    if( xMajorGrid.is())
4615
0
    {
4616
0
        pFS->startElement(FSNS(XML_c, XML_majorGridlines));
4617
0
        exportShapeProps( xMajorGrid, XML_c );
4618
0
        pFS->endElement( FSNS( XML_c, XML_majorGridlines ) );
4619
0
    }
4620
4621
    // minor grid line
4622
0
    if( xMinorGrid.is())
4623
0
    {
4624
0
        pFS->startElement(FSNS(XML_c, XML_minorGridlines));
4625
0
        exportShapeProps( xMinorGrid, XML_c );
4626
0
        pFS->endElement( FSNS( XML_c, XML_minorGridlines ) );
4627
0
    }
4628
4629
    // title
4630
0
    if( xAxisTitle.is() )
4631
0
        exportTitle( xAxisTitle, false );
4632
4633
0
    bool bLinkedNumFmt = true;
4634
0
    if (GetProperty(xAxisProp, u"LinkNumberFormatToSource"_ustr))
4635
0
        mAny >>= bLinkedNumFmt;
4636
4637
0
    OUString aNumberFormatString(u"General"_ustr);
4638
0
    if (GetProperty(xAxisProp, u"NumberFormat"_ustr))
4639
0
    {
4640
0
        sal_Int32 nKey = 0;
4641
0
        mAny >>= nKey;
4642
0
        aNumberFormatString = getNumberFormatCode(nKey);
4643
0
    }
4644
4645
0
    pFS->singleElement(FSNS(XML_c, XML_numFmt),
4646
0
            XML_formatCode, aNumberFormatString,
4647
0
            XML_sourceLinked, bLinkedNumFmt ? "1" : "0");
4648
4649
    // majorTickMark
4650
0
    sal_Int32 nValue = 0;
4651
0
    if(GetProperty( xAxisProp, u"Marks"_ustr ) )
4652
0
    {
4653
0
        mAny >>= nValue;
4654
0
        pFS->singleElement(FSNS(XML_c, XML_majorTickMark), XML_val,
4655
0
                getTickMarkLocStr(nValue));
4656
0
    }
4657
    // minorTickMark
4658
0
    if(GetProperty( xAxisProp, u"HelpMarks"_ustr ) )
4659
0
    {
4660
0
        mAny >>= nValue;
4661
0
        pFS->singleElement(FSNS(XML_c, XML_minorTickMark), XML_val,
4662
0
                getTickMarkLocStr(nValue));
4663
0
    }
4664
    // tickLblPos
4665
0
    const char* sTickLblPos = nullptr;
4666
0
    bool bDisplayLabel = true;
4667
0
    if(GetProperty( xAxisProp, u"DisplayLabels"_ustr ) )
4668
0
        mAny >>= bDisplayLabel;
4669
0
    if( bDisplayLabel && (GetProperty( xAxisProp, u"LabelPosition"_ustr ) ) )
4670
0
    {
4671
0
        css::chart::ChartAxisLabelPosition eLabelPosition = css::chart::ChartAxisLabelPosition_NEAR_AXIS;
4672
0
        mAny >>= eLabelPosition;
4673
0
        switch( eLabelPosition )
4674
0
        {
4675
0
            case css::chart::ChartAxisLabelPosition_NEAR_AXIS:
4676
0
            case css::chart::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE:
4677
0
                sTickLblPos = "nextTo";
4678
0
                break;
4679
0
            case css::chart::ChartAxisLabelPosition_OUTSIDE_START:
4680
0
                sTickLblPos = "low";
4681
0
                break;
4682
0
            case css::chart::ChartAxisLabelPosition_OUTSIDE_END:
4683
0
                sTickLblPos = "high";
4684
0
                break;
4685
0
            default:
4686
0
                sTickLblPos = "nextTo";
4687
0
                break;
4688
0
        }
4689
0
    }
4690
0
    else
4691
0
    {
4692
0
        sTickLblPos = "none";
4693
0
    }
4694
0
    pFS->singleElement(FSNS(XML_c, XML_tickLblPos), XML_val, sTickLblPos);
4695
4696
    // shape properties
4697
0
    exportShapeProps( xAxisProp, XML_c );
4698
4699
0
    exportTextProps(xAxisProp, false);
4700
4701
0
    pFS->singleElement(FSNS(XML_c, XML_crossAx), XML_val, OString::number(rAxisIdPair.nCrossAx));
4702
4703
    // crosses & crossesAt
4704
0
    bool bCrossesValue = false;
4705
0
    const char* sCrosses = nullptr;
4706
    // do not export the CrossoverPosition/CrossoverValue, if the axis is deleted and not visible
4707
0
    if( GetProperty( xAxisProp, u"CrossoverPosition"_ustr ) && !bDeleted && bVisible )
4708
0
    {
4709
0
        css::chart::ChartAxisPosition ePosition( css::chart::ChartAxisPosition_ZERO );
4710
0
        mAny >>= ePosition;
4711
0
        switch( ePosition )
4712
0
        {
4713
0
            case css::chart::ChartAxisPosition_START:
4714
0
                sCrosses = "min";
4715
0
                break;
4716
0
            case css::chart::ChartAxisPosition_END:
4717
0
                sCrosses = "max";
4718
0
                break;
4719
0
            case css::chart::ChartAxisPosition_ZERO:
4720
0
                sCrosses = "autoZero";
4721
0
                break;
4722
0
            default:
4723
0
                bCrossesValue = true;
4724
0
                break;
4725
0
        }
4726
0
    }
4727
4728
0
    if( bCrossesValue && GetProperty( xAxisProp, u"CrossoverValue"_ustr ) )
4729
0
    {
4730
0
        double dValue = 0;
4731
0
        mAny >>= dValue;
4732
0
        pFS->singleElement(FSNS(XML_c, XML_crossesAt), XML_val, OString::number(dValue));
4733
0
    }
4734
0
    else
4735
0
    {
4736
0
        if(sCrosses)
4737
0
        {
4738
0
            pFS->singleElement(FSNS(XML_c, XML_crosses), XML_val, sCrosses);
4739
0
        }
4740
0
    }
4741
4742
0
    if( ( nAxisType == XML_catAx )
4743
0
        || ( nAxisType == XML_dateAx ) )
4744
0
    {
4745
        // FIXME: seems not support? use default value,
4746
0
        const char* const isAuto = "1";
4747
0
        pFS->singleElement(FSNS(XML_c, XML_auto), XML_val, isAuto);
4748
4749
0
        if( nAxisType == XML_catAx )
4750
0
        {
4751
            // FIXME: seems not support? lblAlgn
4752
0
            const char* const sLblAlgn = "ctr";
4753
0
            pFS->singleElement(FSNS(XML_c, XML_lblAlgn), XML_val, sLblAlgn);
4754
0
        }
4755
4756
        // FIXME: seems not support? lblOffset
4757
0
        pFS->singleElement(FSNS(XML_c, XML_lblOffset), XML_val, OString::number(100));
4758
4759
        // export baseTimeUnit, majorTimeUnit, minorTimeUnit of Date axis
4760
0
        if( nAxisType == XML_dateAx )
4761
0
        {
4762
0
            sal_Int32 nAxisIndex = -1;
4763
0
            if( rAxisIdPair.nAxisType == AXIS_PRIMARY_X )
4764
0
                nAxisIndex = 0;
4765
0
            else if( rAxisIdPair.nAxisType == AXIS_SECONDARY_X )
4766
0
                nAxisIndex = 1;
4767
4768
0
            cssc::TimeIncrement aTimeIncrement = lcl_getDateTimeIncrement( mxNewDiagram, nAxisIndex );
4769
0
            sal_Int32 nTimeResolution = css::chart::TimeUnit::DAY;
4770
0
            if( aTimeIncrement.TimeResolution >>= nTimeResolution )
4771
0
                pFS->singleElement(FSNS(XML_c, XML_baseTimeUnit), XML_val, lclGetTimeUnitToken(nTimeResolution));
4772
4773
0
            cssc::TimeInterval aInterval;
4774
0
            if( aTimeIncrement.MajorTimeInterval >>= aInterval )
4775
0
            {
4776
0
                pFS->singleElement(FSNS(XML_c, XML_majorUnit), XML_val, OString::number(aInterval.Number));
4777
0
                pFS->singleElement(FSNS(XML_c, XML_majorTimeUnit), XML_val, lclGetTimeUnitToken(aInterval.TimeUnit));
4778
0
            }
4779
0
            if( aTimeIncrement.MinorTimeInterval >>= aInterval )
4780
0
            {
4781
0
                pFS->singleElement(FSNS(XML_c, XML_minorUnit), XML_val, OString::number(aInterval.Number));
4782
0
                pFS->singleElement(FSNS(XML_c, XML_minorTimeUnit), XML_val, lclGetTimeUnitToken(aInterval.TimeUnit));
4783
0
            }
4784
0
        }
4785
4786
        // FIXME: seems not support? noMultiLvlLbl
4787
0
        if( nAxisType == XML_catAx )
4788
0
            pFS->singleElement(FSNS(XML_c, XML_noMultiLvlLbl), XML_val, OString::number(0));
4789
0
    }
4790
4791
    // crossBetween
4792
0
    if( nAxisType == XML_valAx )
4793
0
    {
4794
0
        if( lcl_isCategoryAxisShifted( mxNewDiagram ))
4795
0
            pFS->singleElement(FSNS(XML_c, XML_crossBetween), XML_val, "between");
4796
0
        else
4797
0
            pFS->singleElement(FSNS(XML_c, XML_crossBetween), XML_val, "midCat");
4798
0
    }
4799
4800
    // majorUnit
4801
0
    bool bAutoStepMain = false;
4802
0
    if(GetProperty( xAxisProp, u"AutoStepMain"_ustr ) )
4803
0
        mAny >>= bAutoStepMain;
4804
4805
0
    if( !bAutoStepMain && (GetProperty( xAxisProp, u"StepMain"_ustr ) ) )
4806
0
    {
4807
0
        double dMajorUnit = 0;
4808
0
        mAny >>= dMajorUnit;
4809
0
        pFS->singleElement(FSNS(XML_c, XML_majorUnit), XML_val, OString::number(dMajorUnit));
4810
0
    }
4811
    // minorUnit
4812
0
    bool bAutoStepHelp = false;
4813
0
    if(GetProperty( xAxisProp, u"AutoStepHelp"_ustr ) )
4814
0
        mAny >>= bAutoStepHelp;
4815
4816
0
    if( !bAutoStepHelp && (GetProperty( xAxisProp, u"StepHelp"_ustr ) ) )
4817
0
    {
4818
0
        double dMinorUnit = 0;
4819
0
        mAny >>= dMinorUnit;
4820
0
        if( GetProperty( xAxisProp, u"StepHelpCount"_ustr ) )
4821
0
        {
4822
0
            sal_Int32 dMinorUnitCount = 0;
4823
0
            mAny >>= dMinorUnitCount;
4824
            // tdf#114168 Don't save minor unit if number of step help count is 5 (which is default for MS Excel),
4825
            // to allow proper .xlsx import. If minorUnit is set and majorUnit not, then it is impossible
4826
            // to calculate StepHelpCount.
4827
0
            if( dMinorUnitCount != 5 )
4828
0
            {
4829
0
                pFS->singleElement( FSNS( XML_c, XML_minorUnit ),
4830
0
                    XML_val, OString::number( dMinorUnit ) );
4831
0
            }
4832
0
        }
4833
0
    }
4834
4835
0
    if( nAxisType == XML_valAx && GetProperty( xAxisProp, u"DisplayUnits"_ustr ) )
4836
0
    {
4837
0
        bool bDisplayUnits = false;
4838
0
        mAny >>= bDisplayUnits;
4839
0
        if(bDisplayUnits)
4840
0
        {
4841
0
            if(GetProperty( xAxisProp, u"BuiltInUnit"_ustr ))
4842
0
            {
4843
0
                OUString aVal;
4844
0
                mAny >>= aVal;
4845
0
                if(!aVal.isEmpty())
4846
0
                {
4847
0
                    pFS->startElement(FSNS(XML_c, XML_dispUnits));
4848
4849
0
                    pFS->singleElement(FSNS(XML_c, XML_builtInUnit), XML_val, aVal);
4850
4851
0
                    pFS->singleElement(FSNS( XML_c, XML_dispUnitsLbl ));
4852
0
                    pFS->endElement( FSNS( XML_c, XML_dispUnits ) );
4853
0
                }
4854
0
            }
4855
0
        }
4856
0
    }
4857
4858
0
    pFS->endElement( FSNS( XML_c, nAxisType ) );
4859
0
}
4860
4861
void ChartExport::exportOneAxis_chartex(
4862
    const Reference< XPropertySet >& xAxisProp,
4863
    const Reference< drawing::XShape >& xAxisTitle,
4864
    const Reference< XPropertySet >& xMajorGrid,
4865
    const Reference< XPropertySet >& xMinorGrid,
4866
    sal_Int32 nAxisType,
4867
    const AxisIdPair& rAxisIdPair)
4868
0
{
4869
0
    FSHelperPtr pFS = GetFS();
4870
0
    pFS->startElement(FSNS(XML_cx, XML_axis), XML_id, OString::number(rAxisIdPair.nAxisId));
4871
4872
    // The following is in the 2010 chart code above:
4873
    //    bool bVisible = true;
4874
    //    if( xAxisProp.is() )
4875
    //    {
4876
    //        xAxisProp->getPropertyValue(u"Visible"_ustr) >>=  bVisible;
4877
    //    }
4878
    //    // only export each axis only once non-deleted
4879
    //    auto aItInsertedPair = maExportedAxis.insert(rAxisIdPair.nAxisType);
4880
    //    bool bDeleted = !aItInsertedPair.second;
4881
    //
4882
    //    pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, !bDeleted && bVisible ? "0" : "1");
4883
    //
4884
    // Is chartex attribute "hidden" the same as !bVisible? And what to do if
4885
    // the axis is deleted, per above?
4886
4887
    // ==== catScaling/valScaling
4888
0
    switch (nAxisType) {
4889
0
        case XML_catAx:
4890
0
            pFS->singleElement(FSNS(XML_cx, XML_catScaling) /* TODO: handle gapWidth */);
4891
0
            break;
4892
0
        case XML_valAx:
4893
0
            {
4894
0
                bool bAutoMax = false;
4895
0
                double dMax = 0; // Make VS happy
4896
0
                bool bMaxSpecified = false;
4897
0
                if(GetProperty( xAxisProp, u"AutoMax"_ustr ) )
4898
0
                    mAny >>= bAutoMax;
4899
4900
0
                if( !bAutoMax && (GetProperty( xAxisProp, u"Max"_ustr ) ) )
4901
0
                {
4902
0
                    mAny >>= dMax;
4903
0
                    bMaxSpecified = true;
4904
0
                }
4905
4906
0
                bool bAutoMin = false;
4907
0
                double dMin = 0; // Make VS happy
4908
0
                bool bMinSpecified = false;
4909
0
                if(GetProperty( xAxisProp, u"AutoMin"_ustr ) )
4910
0
                    mAny >>= bAutoMin;
4911
4912
0
                if( !bAutoMin && (GetProperty( xAxisProp, u"Min"_ustr ) ) )
4913
0
                {
4914
0
                    mAny >>= dMin;
4915
0
                    bMinSpecified = true;
4916
0
                }
4917
4918
                // TODO: handle majorUnit/minorUnit in the following
4919
0
                if (bMaxSpecified && bMinSpecified) {
4920
0
                    pFS->singleElement(FSNS(XML_cx, XML_valScaling),
4921
0
                            XML_max, OString::number(dMax),
4922
0
                            XML_min, OString::number(dMin));
4923
0
                } else if (!bMaxSpecified && bMinSpecified) {
4924
0
                    pFS->singleElement(FSNS(XML_cx, XML_valScaling),
4925
0
                            XML_min, OString::number(dMin));
4926
0
                } else if (bMaxSpecified && !bMinSpecified) {
4927
0
                    pFS->singleElement(FSNS(XML_cx, XML_valScaling),
4928
0
                            XML_max, OString::number(dMax));
4929
0
                } else {
4930
0
                    pFS->singleElement(FSNS(XML_cx, XML_valScaling));
4931
0
                }
4932
4933
0
            }
4934
0
            break;
4935
0
        default:
4936
            // shouldn't happen
4937
0
            assert(false);
4938
0
    }
4939
4940
    // ==== title
4941
0
    if( xAxisTitle.is() ) {
4942
0
        exportTitle( xAxisTitle, true );
4943
0
    }
4944
4945
    // ==== units
4946
0
    if (GetProperty( xAxisProp, u"DisplayUnits"_ustr ) )
4947
0
    {
4948
0
        bool bDisplayUnits = false;
4949
0
        mAny >>= bDisplayUnits;
4950
0
        if (bDisplayUnits)
4951
0
        {
4952
0
            if (GetProperty( xAxisProp, u"BuiltInUnit"_ustr ))
4953
0
            {
4954
0
                OUString aVal;
4955
0
                mAny >>= aVal;
4956
0
                if(!aVal.isEmpty())
4957
0
                {
4958
0
                    pFS->startElement(FSNS(XML_cx, XML_units));
4959
4960
0
                    pFS->startElement(FSNS(XML_cx, XML_unitsLabel));
4961
4962
0
                    lcl_writeChartexString(pFS, aVal);
4963
4964
0
                    pFS->endElement(FSNS(XML_cx, XML_unitsLabel));
4965
4966
0
                    pFS->endElement( FSNS( XML_cx, XML_units ) );
4967
0
                }
4968
0
            }
4969
0
        }
4970
0
    }
4971
4972
    // ==== majorGridlines
4973
0
    if( xMajorGrid.is())
4974
0
    {
4975
0
        pFS->startElement(FSNS(XML_cx, XML_majorGridlines));
4976
0
        exportShapeProps( xMajorGrid, XML_cx );
4977
0
        pFS->endElement( FSNS( XML_cx, XML_majorGridlines ) );
4978
0
    }
4979
4980
    // ==== minorGridlines
4981
0
    if( xMinorGrid.is())
4982
0
    {
4983
0
        pFS->startElement(FSNS(XML_cx, XML_minorGridlines));
4984
0
        exportShapeProps( xMinorGrid, XML_cx );
4985
0
        pFS->endElement( FSNS( XML_cx, XML_minorGridlines ) );
4986
0
    }
4987
4988
    // ==== majorTickMarks
4989
0
    if (GetProperty( xAxisProp, u"Marks"_ustr ) )
4990
0
    {
4991
0
        sal_Int32 nValue = 0;
4992
0
        mAny >>= nValue;
4993
0
        pFS->singleElement(FSNS(XML_cx, XML_majorTickMarks), XML_type,
4994
0
                getTickMarkLocStr(nValue));
4995
0
    }
4996
4997
    // ==== minorTickMarks
4998
0
    if (GetProperty( xAxisProp, u"HelpMarks"_ustr ) )
4999
0
    {
5000
0
        sal_Int32 nValue = 0;
5001
0
        mAny >>= nValue;
5002
0
        pFS->singleElement(FSNS(XML_cx, XML_minorTickMarks), XML_type,
5003
0
                getTickMarkLocStr(nValue));
5004
0
    }
5005
5006
    // ==== tickLabels
5007
0
    bool bDisplayLabel = false;
5008
0
    if (GetProperty( xAxisProp, u"DisplayLabels"_ustr ) )
5009
0
    {
5010
0
        mAny >>= bDisplayLabel;
5011
5012
0
        if( bDisplayLabel )
5013
0
        {
5014
0
            pFS->singleElement(FSNS(XML_cx, XML_tickLabels));
5015
0
        }
5016
0
    }
5017
    // There's also an extLst but not sure what to do with it
5018
5019
    // ==== numFmt
5020
0
    bool bLinkedNumFmt = true;
5021
0
    if (GetProperty(xAxisProp, u"LinkNumberFormatToSource"_ustr))
5022
0
        mAny >>= bLinkedNumFmt;
5023
5024
0
    OUString aNumberFormatString(u"General"_ustr);
5025
0
    if (GetProperty(xAxisProp, u"NumberFormat"_ustr))
5026
0
    {
5027
0
        sal_Int32 nKey = 0;
5028
0
        mAny >>= nKey;
5029
0
        aNumberFormatString = getNumberFormatCode(nKey);
5030
0
    }
5031
5032
    // We're always outputting this, which presumably isn't necessary, but it's
5033
    // not clear what the defaults are for determining if an explicit element is
5034
    // needed
5035
0
    pFS->singleElement(FSNS(XML_cx, XML_numFmt),
5036
0
            XML_formatCode, aNumberFormatString,
5037
0
            XML_sourceLinked, bLinkedNumFmt ? "1" : "0");
5038
5039
    // ==== spPr
5040
0
    exportShapeProps( xAxisProp, XML_cx );
5041
5042
    // ==== txPr
5043
0
    exportTextProps(xAxisProp, true);
5044
5045
0
    pFS->endElement( FSNS( XML_cx, XML_axis ) );
5046
0
}
5047
5048
struct LabelPlacementParam
5049
{
5050
    bool mbExport;
5051
    sal_Int32 meDefault;
5052
5053
    std::unordered_set<sal_Int32> maAllowedValues;
5054
5055
    LabelPlacementParam(bool bExport, sal_Int32 nDefault) :
5056
0
        mbExport(bExport),
5057
0
        meDefault(nDefault),
5058
0
        maAllowedValues(
5059
0
          {
5060
0
           css::chart::DataLabelPlacement::OUTSIDE,
5061
0
           css::chart::DataLabelPlacement::INSIDE,
5062
0
           css::chart::DataLabelPlacement::CENTER,
5063
0
           css::chart::DataLabelPlacement::NEAR_ORIGIN,
5064
0
           css::chart::DataLabelPlacement::TOP,
5065
0
           css::chart::DataLabelPlacement::BOTTOM,
5066
0
           css::chart::DataLabelPlacement::LEFT,
5067
0
           css::chart::DataLabelPlacement::RIGHT,
5068
0
           css::chart::DataLabelPlacement::AVOID_OVERLAP
5069
0
          }
5070
0
        )
5071
0
    {}
5072
};
5073
5074
namespace {
5075
5076
const char* toOOXMLPlacement( sal_Int32 nPlacement )
5077
0
{
5078
0
    switch (nPlacement)
5079
0
    {
5080
0
        case css::chart::DataLabelPlacement::OUTSIDE:       return "outEnd";
5081
0
        case css::chart::DataLabelPlacement::INSIDE:        return "inEnd";
5082
0
        case css::chart::DataLabelPlacement::CENTER:        return "ctr";
5083
0
        case css::chart::DataLabelPlacement::NEAR_ORIGIN:   return "inBase";
5084
0
        case css::chart::DataLabelPlacement::TOP:           return "t";
5085
0
        case css::chart::DataLabelPlacement::BOTTOM:        return "b";
5086
0
        case css::chart::DataLabelPlacement::LEFT:          return "l";
5087
0
        case css::chart::DataLabelPlacement::RIGHT:         return "r";
5088
0
        case css::chart::DataLabelPlacement::CUSTOM:
5089
0
        case css::chart::DataLabelPlacement::AVOID_OVERLAP: return "bestFit";
5090
0
        default:
5091
0
            ;
5092
0
    }
5093
5094
0
    return "outEnd";
5095
0
}
5096
5097
OUString getFieldTypeString( const chart2::DataPointCustomLabelFieldType aType )
5098
0
{
5099
0
    switch (aType)
5100
0
    {
5101
0
    case chart2::DataPointCustomLabelFieldType_CATEGORYNAME:
5102
0
        return u"CATEGORYNAME"_ustr;
5103
5104
0
    case chart2::DataPointCustomLabelFieldType_SERIESNAME:
5105
0
        return u"SERIESNAME"_ustr;
5106
5107
0
    case chart2::DataPointCustomLabelFieldType_VALUE:
5108
0
        return u"VALUE"_ustr;
5109
5110
0
    case chart2::DataPointCustomLabelFieldType_CELLREF:
5111
0
        return u"CELLREF"_ustr;
5112
5113
0
    case chart2::DataPointCustomLabelFieldType_CELLRANGE:
5114
0
        return u"CELLRANGE"_ustr;
5115
5116
0
    default:
5117
0
        break;
5118
0
    }
5119
0
    return OUString();
5120
0
}
5121
5122
void writeRunProperties( ChartExport* pChartExport, Reference<XPropertySet> const & xPropertySet )
5123
0
{
5124
0
    WriteRunInput aInput;
5125
0
    aInput.bCheckDirect = true;
5126
0
    aInput.bUseTextSchemeColors = true;
5127
0
    pChartExport->WriteRunProperties(xPropertySet, XML_rPr, aInput);
5128
0
}
5129
5130
void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport,
5131
                       const Sequence<Reference<chart2::XDataPointCustomLabelField>>& rCustomLabelFields,
5132
                       sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange )
5133
0
{
5134
0
    pFS->startElement(FSNS(XML_c, XML_tx));
5135
0
    pFS->startElement(FSNS(XML_c, XML_rich));
5136
5137
    // TODO: body properties?
5138
0
    pFS->singleElement(FSNS(XML_a, XML_bodyPr));
5139
5140
0
    OUString sFieldType;
5141
0
    OUString sContent;
5142
0
    pFS->startElement(FSNS(XML_a, XML_p));
5143
5144
0
    for (auto& rField : rCustomLabelFields)
5145
0
    {
5146
0
        Reference<XPropertySet> xPropertySet(rField, UNO_QUERY);
5147
0
        chart2::DataPointCustomLabelFieldType aType = rField->getFieldType();
5148
0
        sFieldType.clear();
5149
0
        sContent.clear();
5150
0
        bool bNewParagraph = false;
5151
5152
0
        if (aType == chart2::DataPointCustomLabelFieldType_CELLRANGE &&
5153
0
            rField->getDataLabelsRange())
5154
0
        {
5155
0
            if (rDLblsRange.getRange().isEmpty())
5156
0
                rDLblsRange.setRange(rField->getCellRange());
5157
5158
0
            if (!rDLblsRange.hasLabel(nLabelIndex))
5159
0
                rDLblsRange.setLabel(nLabelIndex, rField->getString());
5160
5161
0
            sContent = "[CELLRANGE]";
5162
0
        }
5163
0
        else
5164
0
        {
5165
0
            sContent = rField->getString();
5166
0
        }
5167
5168
0
        if (aType == chart2::DataPointCustomLabelFieldType_NEWLINE)
5169
0
            bNewParagraph = true;
5170
0
        else if (aType != chart2::DataPointCustomLabelFieldType_TEXT)
5171
0
            sFieldType = getFieldTypeString(aType);
5172
5173
0
        if (bNewParagraph)
5174
0
        {
5175
0
            pFS->endElement(FSNS(XML_a, XML_p));
5176
0
            pFS->startElement(FSNS(XML_a, XML_p));
5177
0
            continue;
5178
0
        }
5179
5180
0
        if (sFieldType.isEmpty())
5181
0
        {
5182
            // Normal text run
5183
0
            pFS->startElement(FSNS(XML_a, XML_r));
5184
0
            writeRunProperties(pChartExport, xPropertySet);
5185
5186
0
            pFS->startElement(FSNS(XML_a, XML_t));
5187
0
            pFS->writeEscaped(sContent);
5188
0
            pFS->endElement(FSNS(XML_a, XML_t));
5189
5190
0
            pFS->endElement(FSNS(XML_a, XML_r));
5191
0
        }
5192
0
        else
5193
0
        {
5194
            // Field
5195
0
            pFS->startElement(FSNS(XML_a, XML_fld), XML_id, rField->getGuid(), XML_type,
5196
0
                              sFieldType);
5197
0
            writeRunProperties(pChartExport, xPropertySet);
5198
5199
0
            pFS->startElement(FSNS(XML_a, XML_t));
5200
0
            pFS->writeEscaped(sContent);
5201
0
            pFS->endElement(FSNS(XML_a, XML_t));
5202
5203
0
            pFS->endElement(FSNS(XML_a, XML_fld));
5204
0
        }
5205
0
    }
5206
5207
0
    pFS->endElement(FSNS(XML_a, XML_p));
5208
0
    pFS->endElement(FSNS(XML_c, XML_rich));
5209
0
    pFS->endElement(FSNS(XML_c, XML_tx));
5210
0
}
5211
5212
}
5213
5214
void ChartExport::writeLabelProperties(
5215
    const uno::Reference<beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam,
5216
    sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange,
5217
    bool bIsChartex)
5218
0
{
5219
0
    if (!xPropSet.is())
5220
0
        return;
5221
5222
0
    FSHelperPtr pFS = GetFS();
5223
5224
0
    const sal_Int32 nChartNS = bIsChartex ? XML_cx : XML_c;
5225
5226
0
    chart2::DataPointLabel aLabel;
5227
0
    Sequence<Reference<chart2::XDataPointCustomLabelField>> aCustomLabelFields;
5228
0
    sal_Int32 nLabelBorderWidth = 0;
5229
0
    sal_Int32 nLabelBorderColor = 0x00FFFFFF;
5230
0
    sal_Int32 nLabelFillColor = -1;
5231
5232
0
    xPropSet->getPropertyValue(u"CustomLabelFields"_ustr) >>= aCustomLabelFields;
5233
0
    xPropSet->getPropertyValue(u"LabelBorderWidth"_ustr) >>= nLabelBorderWidth;
5234
0
    xPropSet->getPropertyValue(u"LabelBorderColor"_ustr) >>= nLabelBorderColor;
5235
0
    xPropSet->getPropertyValue(u"LabelFillColor"_ustr) >>= nLabelFillColor;
5236
5237
0
    if (aCustomLabelFields.hasElements())
5238
0
        writeCustomLabel(pFS, this, aCustomLabelFields, nLabelIndex, rDLblsRange); // c:tx
5239
5240
0
    bool bLinkedNumFmt = false;
5241
0
    if( GetProperty(xPropSet, u"LinkNumberFormatToSource"_ustr) )
5242
0
        mAny >>= bLinkedNumFmt;
5243
5244
0
    bool bLabelIsNumberFormat = true;
5245
0
    if( xPropSet->getPropertyValue(u"Label"_ustr) >>= aLabel )
5246
0
        bLabelIsNumberFormat = aLabel.ShowNumber;
5247
5248
0
    if (GetProperty(xPropSet, bLabelIsNumberFormat ? u"NumberFormat"_ustr : u"PercentageNumberFormat"_ustr))
5249
0
    {
5250
0
        sal_Int32 nKey = 0;
5251
0
        mAny >>= nKey;
5252
5253
0
        OUString aNumberFormatString = getNumberFormatCode(nKey);
5254
5255
0
        if (bIsChartex) {
5256
0
            pFS->singleElement(FSNS(XML_cx, XML_numFmt), XML_formatCode, aNumberFormatString,
5257
0
                               XML_sourceLinked, ToPsz10(bLinkedNumFmt));
5258
0
        } else {
5259
0
            pFS->singleElement(FSNS(XML_c, XML_numFmt), XML_formatCode, aNumberFormatString,
5260
0
                               XML_sourceLinked, ToPsz10(bLinkedNumFmt));
5261
0
        }
5262
0
    }
5263
5264
0
    if (nLabelBorderWidth > 0 || nLabelFillColor != -1)
5265
0
    {
5266
0
        pFS->startElement(FSNS(nChartNS, XML_spPr));
5267
5268
0
        if (nLabelFillColor != -1)
5269
0
        {
5270
0
            ::Color nColor(ColorTransparency, nLabelFillColor);
5271
0
            if (nColor.IsTransparent())
5272
0
                WriteSolidFill(nColor, nColor.GetAlpha());
5273
0
            else
5274
0
                WriteSolidFill(nColor);
5275
0
        }
5276
5277
0
        if (nLabelBorderWidth > 0)
5278
0
        {
5279
0
            pFS->startElement(FSNS(XML_a, XML_ln), XML_w,
5280
0
                              OString::number(convertHmmToEmu(nLabelBorderWidth)));
5281
5282
0
            if (nLabelBorderColor != -1)
5283
0
            {
5284
0
                ::Color nColor(ColorTransparency, nLabelBorderColor);
5285
0
                if (nColor.IsTransparent())
5286
0
                    WriteSolidFill(nColor, nColor.GetAlpha());
5287
0
                else
5288
0
                    WriteSolidFill(nColor);
5289
0
            }
5290
5291
0
            pFS->endElement(FSNS(XML_a, XML_ln));
5292
0
        }
5293
5294
0
        pFS->endElement(FSNS(nChartNS, XML_spPr));
5295
0
    }
5296
5297
0
    exportTextProps(xPropSet, bIsChartex); // c:txPr
5298
5299
0
    if (!bIsChartex) {
5300
        // In chartex label position is an attribute of cx:dataLabel
5301
0
        if (rLabelParam.mbExport)
5302
0
        {
5303
0
            sal_Int32 nLabelPlacement = rLabelParam.meDefault;
5304
0
            if (xPropSet->getPropertyValue(u"LabelPlacement"_ustr) >>= nLabelPlacement)
5305
0
            {
5306
0
                if (!rLabelParam.maAllowedValues.count(nLabelPlacement))
5307
0
                {
5308
0
                    SAL_WARN("oox", "this label placement not allowed in OOXML for current chart type: "
5309
0
                                        + OString::number(nLabelPlacement));
5310
0
                    nLabelPlacement = rLabelParam.meDefault;
5311
0
                }
5312
0
                pFS->singleElement(FSNS(XML_c, XML_dLblPos), XML_val, toOOXMLPlacement(nLabelPlacement));
5313
0
            }
5314
0
        }
5315
5316
0
        pFS->singleElement(FSNS(XML_c, XML_showLegendKey), XML_val, ToPsz10(aLabel.ShowLegendSymbol));
5317
0
        pFS->singleElement(FSNS(XML_c, XML_showVal), XML_val, ToPsz10(aLabel.ShowNumber));
5318
0
        pFS->singleElement(FSNS(XML_c, XML_showCatName), XML_val, ToPsz10(aLabel.ShowCategoryName));
5319
0
        pFS->singleElement(FSNS(XML_c, XML_showSerName), XML_val, ToPsz10(aLabel.ShowSeriesName));
5320
0
        pFS->singleElement(FSNS(XML_c, XML_showPercent), XML_val, ToPsz10(aLabel.ShowNumberInPercent));
5321
0
    }
5322
5323
    // Export the text "separator" if exists
5324
0
    uno::Any aAny = xPropSet->getPropertyValue(u"LabelSeparator"_ustr);
5325
0
    if( aAny.hasValue() )
5326
0
    {
5327
0
        OUString nLabelSeparator;
5328
0
        aAny >>= nLabelSeparator;
5329
0
        pFS->startElement(FSNS(nChartNS, XML_separator));
5330
0
        pFS->writeEscaped( nLabelSeparator );
5331
0
        pFS->endElement( FSNS(nChartNS, XML_separator ) );
5332
0
    }
5333
5334
0
    if (rDLblsRange.hasLabel(nLabelIndex))
5335
0
    {
5336
0
        pFS->startElement(FSNS(nChartNS, XML_extLst));
5337
        // TODO: is the following correct for chartex?
5338
0
        pFS->startElement(FSNS(nChartNS, XML_ext), XML_uri,
5339
0
            "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}", FSNS(XML_xmlns, XML_c15),
5340
0
            GetFB()->getNamespaceURL(OOX_NS(c15)));
5341
5342
0
        pFS->singleElement(FSNS(XML_c15, XML_showDataLabelsRange), XML_val, "1");
5343
5344
0
        pFS->endElement(FSNS(nChartNS, XML_ext));
5345
0
        pFS->endElement(FSNS(nChartNS, XML_extLst));
5346
0
    }
5347
0
}
5348
5349
void ChartExport::exportDataLabels(
5350
    const uno::Reference<chart2::XDataSeries> & xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType,
5351
    DataLabelsRange& rDLblsRange,
5352
    bool bIsChartex)
5353
0
{
5354
0
    if (!xSeries.is() || nSeriesLength <= 0)
5355
0
        return;
5356
5357
0
    uno::Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY);
5358
0
    if (!xPropSet.is())
5359
0
        return;
5360
5361
0
    FSHelperPtr pFS = GetFS();
5362
5363
0
    if (bIsChartex) {
5364
0
        pFS->startElement(FSNS(XML_cx, XML_dataLabels));
5365
0
    } else {
5366
0
        pFS->startElement(FSNS(XML_c, XML_dLbls));
5367
0
    }
5368
5369
0
    uno::Sequence<sal_Int32> aAttrLabelIndices;
5370
0
    xPropSet->getPropertyValue(u"AttributedDataPoints"_ustr) >>= aAttrLabelIndices;
5371
5372
    // We must not export label placement property when the chart type doesn't
5373
    // support this option in MS Office, else MS Office would think the file
5374
    // is corrupt & refuse to open it.
5375
    // For allowed values see 2.1.1456 Part 1 Section 21.2.2.48,
5376
    // dLblPos (Data Label Position) in [MS-OI29500].
5377
5378
0
    const chart::TypeGroupInfo& rInfo = chart::GetTypeGroupInfo(static_cast<chart::TypeId>(eChartType));
5379
0
    LabelPlacementParam aParam(!mbIs3DChart, rInfo.mnDefLabelPos);
5380
0
    switch (eChartType) // diagram chart type
5381
0
    {
5382
0
        case chart::TYPEID_PIE:
5383
0
            if(getChartType() == chart::TYPEID_DOUGHNUT)
5384
0
                aParam.mbExport = false;
5385
0
            else
5386
0
            {
5387
                // All pie charts support label placement.
5388
0
                aParam.mbExport = true;
5389
0
                aParam.maAllowedValues.clear();
5390
0
                aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::OUTSIDE);
5391
0
                aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE);
5392
0
                aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER);
5393
0
                aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::AVOID_OVERLAP);
5394
0
                aParam.meDefault = css::chart::DataLabelPlacement::AVOID_OVERLAP;
5395
0
            }
5396
0
        break;
5397
0
        case chart::TYPEID_AREA:
5398
0
        case chart::TYPEID_RADARLINE:
5399
0
        case chart::TYPEID_RADARAREA:
5400
            // These chart types don't support label placement.
5401
0
            aParam.mbExport = false;
5402
0
        break;
5403
0
        case chart::TYPEID_BAR:
5404
0
            if (mbStacked || mbPercent)
5405
0
            {
5406
0
                aParam.maAllowedValues.clear();
5407
0
                aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER);
5408
0
                aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE);
5409
0
                aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN);
5410
0
                aParam.meDefault = css::chart::DataLabelPlacement::CENTER;
5411
0
            }
5412
0
            else  // Clustered bar chart
5413
0
            {
5414
0
                aParam.maAllowedValues.clear();
5415
0
                aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER);
5416
0
                aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE);
5417
0
                aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::OUTSIDE);
5418
0
                aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN);
5419
0
                aParam.meDefault = css::chart::DataLabelPlacement::OUTSIDE;
5420
0
            }
5421
0
        break;
5422
        // TODO: How do chartex charts handle this?
5423
0
        default:
5424
0
            ;
5425
0
    }
5426
5427
0
    for (const sal_Int32 nIdx : aAttrLabelIndices)
5428
0
    {
5429
0
        uno::Reference<beans::XPropertySet> xLabelPropSet = xSeries->getDataPointByIndex(nIdx);
5430
5431
0
        if (!xLabelPropSet.is())
5432
0
            continue;
5433
5434
0
        if (bIsChartex) {
5435
0
            if (aParam.mbExport)
5436
0
            {
5437
0
                sal_Int32 nLabelPlacement = aParam.meDefault;
5438
0
                if (xPropSet->getPropertyValue(u"LabelPlacement"_ustr) >>= nLabelPlacement)
5439
0
                {
5440
0
                    if (!aParam.maAllowedValues.count(nLabelPlacement))
5441
0
                        nLabelPlacement = aParam.meDefault;
5442
0
                    pFS->startElement(FSNS(XML_cx, XML_dataLabel),
5443
0
                            XML_idx, OString::number(nIdx),
5444
0
                            XML_pos, toOOXMLPlacement(nLabelPlacement));
5445
0
                }
5446
0
            } else {
5447
0
                pFS->startElement(FSNS(XML_cx, XML_dataLabel), XML_idx, OString::number(nIdx));
5448
0
            }
5449
0
        } else {
5450
0
            pFS->startElement(FSNS(XML_c, XML_dLbl));
5451
0
            pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nIdx));
5452
5453
            // As far as i know there can be issues with the Positions,
5454
            // if a piechart label use AVOID_OVERLAP placement (== BestFit)
5455
            // because LO and MS may calculate the bestFit positions differently.
5456
0
            bool bWritePosition = true;
5457
0
            if (eChartType == chart::TYPEID_PIE)
5458
0
            {
5459
0
                sal_Int32 nLabelPlacement = aParam.meDefault;
5460
0
                xLabelPropSet->getPropertyValue(u"LabelPlacement"_ustr) >>= nLabelPlacement;
5461
0
                if (nLabelPlacement == css::chart::DataLabelPlacement::AVOID_OVERLAP)
5462
0
                    bWritePosition = false;
5463
0
            }
5464
5465
            // export custom position of data label
5466
0
            if (bWritePosition)
5467
0
            {
5468
0
                chart2::RelativePosition aCustomLabelPosition;
5469
0
                if( xLabelPropSet->getPropertyValue(u"CustomLabelPosition"_ustr) >>= aCustomLabelPosition )
5470
0
                {
5471
0
                    pFS->startElement(FSNS(XML_c, XML_layout));
5472
0
                    pFS->startElement(FSNS(XML_c, XML_manualLayout));
5473
5474
0
                    pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(aCustomLabelPosition.Primary));
5475
0
                    pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(aCustomLabelPosition.Secondary));
5476
5477
0
                    SAL_WARN_IF(aCustomLabelPosition.Anchor != css::drawing::Alignment_TOP_LEFT, "oox", "unsupported anchor position");
5478
5479
0
                    pFS->endElement(FSNS(XML_c, XML_manualLayout));
5480
0
                    pFS->endElement(FSNS(XML_c, XML_layout));
5481
0
                }
5482
0
            }
5483
0
        }
5484
5485
        // Individual label property that overwrites the baseline.
5486
0
        writeLabelProperties(xLabelPropSet, aParam, nIdx, rDLblsRange, bIsChartex);
5487
0
        pFS->endElement(FSNS(XML_c, XML_dLbl));
5488
0
    }
5489
5490
    // Baseline label properties for all labels.
5491
0
    writeLabelProperties(xPropSet, aParam, -1, rDLblsRange, bIsChartex);
5492
5493
0
    if (!bIsChartex) {
5494
0
        bool bShowLeaderLines = false;
5495
0
        xPropSet->getPropertyValue(u"ShowCustomLeaderLines"_ustr) >>= bShowLeaderLines;
5496
0
        pFS->singleElement(FSNS(XML_c, XML_showLeaderLines), XML_val, ToPsz10(bShowLeaderLines));
5497
5498
        // Export LeaderLine properties
5499
        // TODO: import all kind of LeaderLine props (not just LineColor/LineWidth)
5500
0
        if (bShowLeaderLines)
5501
0
        {
5502
0
            pFS->startElement(FSNS(XML_c, XML_leaderLines));
5503
0
            pFS->startElement(FSNS(XML_c, XML_spPr));
5504
0
            WriteOutline(xPropSet, getModel());
5505
0
            pFS->endElement(FSNS(XML_c, XML_spPr));
5506
0
            pFS->endElement(FSNS(XML_c, XML_leaderLines));
5507
0
        }
5508
5509
        // Export leader line
5510
0
        if( eChartType != chart::TYPEID_PIE )
5511
0
        {
5512
0
            pFS->startElement(FSNS(XML_c, XML_extLst));
5513
0
            pFS->startElement(FSNS(XML_c, XML_ext), XML_uri, "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}", FSNS(XML_xmlns, XML_c15), GetFB()->getNamespaceURL(OOX_NS(c15)));
5514
0
            pFS->singleElement(FSNS(XML_c15, XML_showLeaderLines), XML_val, ToPsz10(bShowLeaderLines));
5515
0
            pFS->endElement(FSNS(XML_c, XML_ext));
5516
0
            pFS->endElement(FSNS(XML_c, XML_extLst));
5517
0
        }
5518
0
    }
5519
5520
0
    if (bIsChartex) {
5521
0
        pFS->endElement(FSNS(XML_cx, XML_dataLabels));
5522
0
    } else {
5523
0
        pFS->endElement(FSNS(XML_c, XML_dLbls));
5524
0
    }
5525
0
}
5526
5527
void ChartExport::exportDataPoints(
5528
    const uno::Reference< beans::XPropertySet > & xSeriesProperties,
5529
    sal_Int32 nSeriesLength, sal_Int32 eChartType )
5530
0
{
5531
0
    uno::Reference< chart2::XDataSeries > xSeries( xSeriesProperties, uno::UNO_QUERY );
5532
0
    bool bVaryColorsByPoint = false;
5533
0
    Sequence< sal_Int32 > aDataPointSeq;
5534
0
    if( xSeriesProperties.is())
5535
0
    {
5536
0
        Any aAny = xSeriesProperties->getPropertyValue( u"AttributedDataPoints"_ustr );
5537
0
        aAny >>= aDataPointSeq;
5538
0
        xSeriesProperties->getPropertyValue( u"VaryColorsByPoint"_ustr ) >>= bVaryColorsByPoint;
5539
0
    }
5540
5541
0
    sal_Int32 nElement;
5542
0
    Reference< chart2::XColorScheme > xColorScheme;
5543
0
    if( mxNewDiagram.is())
5544
0
        xColorScheme.set( mxNewDiagram->getDefaultColorScheme());
5545
5546
0
    if( bVaryColorsByPoint && xColorScheme.is() )
5547
0
    {
5548
0
        o3tl::sorted_vector< sal_Int32 > aAttrPointSet;
5549
0
        aAttrPointSet.reserve(aDataPointSeq.getLength());
5550
0
        for (sal_Int32 point : aDataPointSeq)
5551
0
            aAttrPointSet.insert(point);
5552
0
        const auto aEndIt = aAttrPointSet.end();
5553
0
        for( nElement = 0; nElement < nSeriesLength; ++nElement )
5554
0
        {
5555
0
            uno::Reference< beans::XPropertySet > xPropSet;
5556
0
            if( aAttrPointSet.find( nElement ) != aEndIt )
5557
0
            {
5558
0
                try
5559
0
                {
5560
0
                    xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
5561
0
                            xSeries, nElement, getModel() );
5562
0
                }
5563
0
                catch( const uno::Exception & )
5564
0
                {
5565
0
                    DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
5566
0
                }
5567
0
            }
5568
0
            else
5569
0
            {
5570
                // property set only containing the color
5571
0
                xPropSet.set( new ColorPropertySet( ColorTransparency, xColorScheme->getColorByIndex( nElement )));
5572
0
            }
5573
5574
0
            if( xPropSet.is() )
5575
0
            {
5576
0
                FSHelperPtr pFS = GetFS();
5577
0
                pFS->startElement(FSNS(XML_c, XML_dPt));
5578
0
                pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nElement));
5579
5580
0
                switch (eChartType)
5581
0
                {
5582
0
                    case chart::TYPEID_PIE:
5583
0
                    case chart::TYPEID_DOUGHNUT:
5584
0
                    {
5585
0
                        if( xPropSet.is() && GetProperty( xPropSet, u"SegmentOffset"_ustr) )
5586
0
                        {
5587
0
                            sal_Int32 nOffset = 0;
5588
0
                            mAny >>= nOffset;
5589
0
                            if (nOffset)
5590
0
                                pFS->singleElement( FSNS( XML_c, XML_explosion ),
5591
0
                                        XML_val, OString::number( nOffset ) );
5592
0
                        }
5593
0
                        break;
5594
0
                    }
5595
0
                    default:
5596
0
                        break;
5597
0
                }
5598
0
                exportShapeProps( xPropSet, XML_c );
5599
5600
0
                pFS->endElement( FSNS( XML_c, XML_dPt ) );
5601
0
            }
5602
0
        }
5603
0
    }
5604
5605
    // Export Data Point Property in Charts even if the VaryColors is false
5606
0
    if( bVaryColorsByPoint )
5607
0
        return;
5608
5609
0
    o3tl::sorted_vector< sal_Int32 > aAttrPointSet;
5610
0
    aAttrPointSet.reserve(aDataPointSeq.getLength());
5611
0
    for (sal_Int32 point : aDataPointSeq)
5612
0
        aAttrPointSet.insert(point);
5613
0
    const auto aEndIt = aAttrPointSet.end();
5614
0
    for( nElement = 0; nElement < nSeriesLength; ++nElement )
5615
0
    {
5616
0
        uno::Reference< beans::XPropertySet > xPropSet;
5617
0
        if( aAttrPointSet.find( nElement ) != aEndIt )
5618
0
        {
5619
0
            try
5620
0
            {
5621
0
                xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
5622
0
                        xSeries, nElement, getModel() );
5623
0
            }
5624
0
            catch( const uno::Exception & )
5625
0
            {
5626
0
                DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
5627
0
            }
5628
0
        }
5629
5630
0
        if( xPropSet.is() )
5631
0
        {
5632
0
            FSHelperPtr pFS = GetFS();
5633
0
            pFS->startElement(FSNS(XML_c, XML_dPt));
5634
0
            pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nElement));
5635
5636
0
            switch( eChartType )
5637
0
            {
5638
0
                case chart::TYPEID_BUBBLE:
5639
0
                case chart::TYPEID_HORBAR:
5640
0
                case chart::TYPEID_BAR:
5641
0
                    pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0");
5642
0
                    exportShapeProps(xPropSet, XML_c);
5643
0
                    break;
5644
5645
0
                case chart::TYPEID_LINE:
5646
0
                case chart::TYPEID_SCATTER:
5647
0
                case chart::TYPEID_RADARLINE:
5648
0
                    exportMarker(xPropSet);
5649
0
                    break;
5650
5651
0
                default:
5652
0
                    exportShapeProps(xPropSet, XML_c);
5653
0
                    break;
5654
0
            }
5655
5656
0
            pFS->endElement( FSNS( XML_c, XML_dPt ) );
5657
0
        }
5658
0
    }
5659
0
}
5660
5661
// Generalized axis output
5662
void ChartExport::createAxes(bool bPrimaryAxes, bool bCheckCombinedAxes, bool bIsChartex)
5663
0
{
5664
0
    sal_Int32 nAxisIdx, nAxisIdy;
5665
0
    bool bPrimaryAxisExists = false;
5666
0
    bool bSecondaryAxisExists = false;
5667
    // let's check which axis already exists and which axis is attached to the actual dataseries
5668
0
    if (maAxes.size() >= 2)
5669
0
    {
5670
0
        bPrimaryAxisExists = bPrimaryAxes && maAxes[1].nAxisType == AXIS_PRIMARY_Y;
5671
0
        bSecondaryAxisExists = !bPrimaryAxes && maAxes[1].nAxisType == AXIS_SECONDARY_Y;
5672
0
    }
5673
    // tdf#114181 keep axes of combined charts
5674
0
    if ( bCheckCombinedAxes && ( bPrimaryAxisExists || bSecondaryAxisExists ) )
5675
0
    {
5676
0
        nAxisIdx = maAxes[0].nAxisId;
5677
0
        nAxisIdy = maAxes[1].nAxisId;
5678
0
    }
5679
0
    else
5680
0
    {
5681
0
        nAxisIdx = lcl_generateRandomValue();
5682
0
        nAxisIdy = lcl_generateRandomValue();
5683
0
        AxesType eXAxis = bPrimaryAxes ? AXIS_PRIMARY_X : AXIS_SECONDARY_X;
5684
0
        AxesType eYAxis = bPrimaryAxes ? AXIS_PRIMARY_Y : AXIS_SECONDARY_Y;
5685
0
        maAxes.emplace_back( eXAxis, nAxisIdx, nAxisIdy );
5686
0
        maAxes.emplace_back( eYAxis, nAxisIdy, nAxisIdx );
5687
0
    }
5688
5689
0
    if (!bIsChartex) {
5690
        // Export IDs
5691
0
        FSHelperPtr pFS = GetFS();
5692
0
        pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdx));
5693
0
        pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdy));
5694
0
        if (mbHasZAxis)
5695
0
        {
5696
0
            sal_Int32 nAxisIdz = 0;
5697
0
            if( isDeep3dChart() )
5698
0
            {
5699
0
                nAxisIdz = lcl_generateRandomValue();
5700
0
                maAxes.emplace_back( AXIS_PRIMARY_Z, nAxisIdz, nAxisIdy );
5701
0
            }
5702
0
            pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdz));
5703
0
        }
5704
0
    }
5705
0
}
5706
5707
void ChartExport::exportGrouping( bool isBar )
5708
0
{
5709
0
    FSHelperPtr pFS = GetFS();
5710
0
    Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
5711
    // grouping
5712
0
    if( GetProperty( xPropSet, u"Stacked"_ustr ) )
5713
0
        mAny >>= mbStacked;
5714
0
    if( GetProperty( xPropSet, u"Percent"_ustr ) )
5715
0
        mAny >>= mbPercent;
5716
5717
0
    const char* grouping = nullptr;
5718
0
    if (mbStacked)
5719
0
        grouping = "stacked";
5720
0
    else if (mbPercent)
5721
0
        grouping = "percentStacked";
5722
0
    else
5723
0
    {
5724
0
        if( isBar && !isDeep3dChart() )
5725
0
        {
5726
0
            grouping = "clustered";
5727
0
        }
5728
0
        else
5729
0
            grouping = "standard";
5730
0
    }
5731
0
    pFS->singleElement(FSNS(XML_c, XML_grouping), XML_val, grouping);
5732
0
}
5733
5734
void ChartExport::exportTrendlines( const Reference< chart2::XDataSeries >& xSeries )
5735
0
{
5736
0
    FSHelperPtr pFS = GetFS();
5737
0
    Reference< chart2::XRegressionCurveContainer > xRegressionCurveContainer( xSeries, UNO_QUERY );
5738
0
    if( !xRegressionCurveContainer.is() )
5739
0
        return;
5740
5741
0
    const Sequence< Reference< chart2::XRegressionCurve > > aRegCurveSeq = xRegressionCurveContainer->getRegressionCurves();
5742
0
    for( const Reference< chart2::XRegressionCurve >& xRegCurve : aRegCurveSeq )
5743
0
    {
5744
0
        if (!xRegCurve.is())
5745
0
            continue;
5746
5747
0
        Reference< XPropertySet > xProperties( xRegCurve , uno::UNO_QUERY );
5748
5749
0
        OUString aService;
5750
0
        Reference< lang::XServiceName > xServiceName( xProperties, UNO_QUERY );
5751
0
        if( !xServiceName.is() )
5752
0
            continue;
5753
5754
0
        aService = xServiceName->getServiceName();
5755
5756
0
        if(aService != "com.sun.star.chart2.LinearRegressionCurve" &&
5757
0
                aService != "com.sun.star.chart2.ExponentialRegressionCurve" &&
5758
0
                aService != "com.sun.star.chart2.LogarithmicRegressionCurve" &&
5759
0
                aService != "com.sun.star.chart2.PotentialRegressionCurve" &&
5760
0
                aService != "com.sun.star.chart2.PolynomialRegressionCurve" &&
5761
0
                aService != "com.sun.star.chart2.MovingAverageRegressionCurve")
5762
0
            continue;
5763
5764
0
        pFS->startElement(FSNS(XML_c, XML_trendline));
5765
5766
0
        OUString aName;
5767
0
        xProperties->getPropertyValue(u"CurveName"_ustr) >>= aName;
5768
0
        if(!aName.isEmpty())
5769
0
        {
5770
0
            pFS->startElement(FSNS(XML_c, XML_name));
5771
0
            pFS->writeEscaped(aName);
5772
0
            pFS->endElement( FSNS( XML_c, XML_name) );
5773
0
        }
5774
5775
0
        exportShapeProps( xProperties, XML_c );
5776
5777
0
        if( aService == "com.sun.star.chart2.LinearRegressionCurve" )
5778
0
        {
5779
0
            pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "linear");
5780
0
        }
5781
0
        else if( aService == "com.sun.star.chart2.ExponentialRegressionCurve" )
5782
0
        {
5783
0
            pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "exp");
5784
0
        }
5785
0
        else if( aService == "com.sun.star.chart2.LogarithmicRegressionCurve" )
5786
0
        {
5787
0
            pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "log");
5788
0
        }
5789
0
        else if( aService == "com.sun.star.chart2.PotentialRegressionCurve" )
5790
0
        {
5791
0
            pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "power");
5792
0
        }
5793
0
        else if( aService == "com.sun.star.chart2.PolynomialRegressionCurve" )
5794
0
        {
5795
0
            pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "poly");
5796
5797
0
            sal_Int32 aDegree = 2;
5798
0
            xProperties->getPropertyValue( u"PolynomialDegree"_ustr) >>= aDegree;
5799
0
            pFS->singleElement(FSNS(XML_c, XML_order), XML_val, OString::number(aDegree));
5800
0
        }
5801
0
        else if( aService == "com.sun.star.chart2.MovingAverageRegressionCurve" )
5802
0
        {
5803
0
            pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "movingAvg");
5804
5805
0
            sal_Int32 aPeriod = 2;
5806
0
            xProperties->getPropertyValue( u"MovingAveragePeriod"_ustr) >>= aPeriod;
5807
5808
0
            pFS->singleElement(FSNS(XML_c, XML_period), XML_val, OString::number(aPeriod));
5809
0
        }
5810
0
        else
5811
0
        {
5812
            // should never happen
5813
            // This would produce invalid OOXML files so we check earlier for the type
5814
0
            assert(false);
5815
0
        }
5816
5817
0
        double fExtrapolateForward = 0.0;
5818
0
        double fExtrapolateBackward = 0.0;
5819
5820
0
        xProperties->getPropertyValue(u"ExtrapolateForward"_ustr) >>= fExtrapolateForward;
5821
0
        xProperties->getPropertyValue(u"ExtrapolateBackward"_ustr) >>= fExtrapolateBackward;
5822
5823
0
        pFS->singleElement( FSNS( XML_c, XML_forward ),
5824
0
                XML_val, OString::number(fExtrapolateForward) );
5825
5826
0
        pFS->singleElement( FSNS( XML_c, XML_backward ),
5827
0
                XML_val, OString::number(fExtrapolateBackward) );
5828
5829
0
        bool bForceIntercept = false;
5830
0
        xProperties->getPropertyValue(u"ForceIntercept"_ustr) >>= bForceIntercept;
5831
5832
0
        if (bForceIntercept)
5833
0
        {
5834
0
            double fInterceptValue = 0.0;
5835
0
            xProperties->getPropertyValue(u"InterceptValue"_ustr) >>= fInterceptValue;
5836
5837
0
            pFS->singleElement( FSNS( XML_c, XML_intercept ),
5838
0
                XML_val, OString::number(fInterceptValue) );
5839
0
        }
5840
5841
        // Equation properties
5842
0
        Reference< XPropertySet > xEquationProperties( xRegCurve->getEquationProperties() );
5843
5844
        // Show Equation
5845
0
        bool bShowEquation = false;
5846
0
        xEquationProperties->getPropertyValue(u"ShowEquation"_ustr) >>= bShowEquation;
5847
5848
        // Show R^2
5849
0
        bool bShowCorrelationCoefficient = false;
5850
0
        xEquationProperties->getPropertyValue(u"ShowCorrelationCoefficient"_ustr) >>= bShowCorrelationCoefficient;
5851
5852
0
        pFS->singleElement( FSNS( XML_c, XML_dispRSqr ),
5853
0
                XML_val, ToPsz10(bShowCorrelationCoefficient) );
5854
5855
0
        pFS->singleElement(FSNS(XML_c, XML_dispEq), XML_val, ToPsz10(bShowEquation));
5856
5857
0
        pFS->endElement( FSNS( XML_c, XML_trendline ) );
5858
0
    }
5859
0
}
5860
5861
void ChartExport::exportMarker(const Reference< XPropertySet >& xPropSet)
5862
0
{
5863
0
    chart2::Symbol aSymbol;
5864
0
    if( GetProperty( xPropSet, u"Symbol"_ustr ) )
5865
0
        mAny >>= aSymbol;
5866
5867
0
    if(aSymbol.Style != chart2::SymbolStyle_STANDARD && aSymbol.Style != chart2::SymbolStyle_NONE)
5868
0
        return;
5869
5870
0
    FSHelperPtr pFS = GetFS();
5871
0
    pFS->startElement(FSNS(XML_c, XML_marker));
5872
5873
0
    sal_Int32 nSymbol = aSymbol.StandardSymbol;
5874
    // TODO: more properties support for marker
5875
0
    const char* pSymbolType; // no initialization here, to let compiler warn if we have a code path
5876
                             // where it stays uninitialized
5877
0
    switch( nSymbol )
5878
0
    {
5879
0
        case 0:
5880
0
            pSymbolType = "square";
5881
0
            break;
5882
0
        case 1:
5883
0
            pSymbolType = "diamond";
5884
0
            break;
5885
0
        case 2:
5886
0
        case 3:
5887
0
        case 4:
5888
0
        case 5:
5889
0
            pSymbolType = "triangle";
5890
0
            break;
5891
0
        case 8:
5892
0
            pSymbolType = "circle";
5893
0
            break;
5894
0
        case 9:
5895
0
            pSymbolType = "star";
5896
0
            break;
5897
0
        case 10:
5898
0
            pSymbolType = "x"; // in MS office 2010 built in symbol marker 'X' is represented as 'x'
5899
0
            break;
5900
0
        case 11:
5901
0
            pSymbolType = "plus";
5902
0
            break;
5903
0
        case 13:
5904
0
            pSymbolType = "dash";
5905
0
            break;
5906
0
        default:
5907
0
            pSymbolType = "square";
5908
0
            break;
5909
0
    }
5910
5911
0
    bool bSkipFormatting = false;
5912
0
    if (aSymbol.Style == chart2::SymbolStyle_NONE)
5913
0
    {
5914
0
        bSkipFormatting = true;
5915
0
        pSymbolType = "none";
5916
0
    }
5917
5918
0
    pFS->singleElement(FSNS(XML_c, XML_symbol), XML_val, pSymbolType);
5919
5920
0
    if (!bSkipFormatting)
5921
0
    {
5922
0
        awt::Size aSymbolSize = aSymbol.Size;
5923
0
        sal_Int32 nSize = std::max( aSymbolSize.Width, aSymbolSize.Height );
5924
5925
0
        nSize = nSize/250.0*7.0 + 1; // just guessed based on some test cases,
5926
        //the value is always 1 less than the actual value.
5927
0
        nSize = std::clamp( int(nSize), 2, 72 );
5928
0
        pFS->singleElement(FSNS(XML_c, XML_size), XML_val, OString::number(nSize));
5929
5930
0
        pFS->startElement(FSNS(XML_c, XML_spPr));
5931
5932
0
        util::Color aColor = aSymbol.FillColor;
5933
0
        if (GetProperty(xPropSet, u"Color"_ustr))
5934
0
            mAny >>= aColor;
5935
5936
0
        if (aColor == -1)
5937
0
        {
5938
0
            pFS->singleElement(FSNS(XML_a, XML_noFill));
5939
0
        }
5940
0
        else
5941
0
            WriteSolidFill(::Color(ColorTransparency, aColor));
5942
5943
0
        pFS->endElement( FSNS( XML_c, XML_spPr ) );
5944
0
    }
5945
5946
0
    pFS->endElement( FSNS( XML_c, XML_marker ) );
5947
0
}
5948
5949
void ChartExport::exportSmooth()
5950
0
{
5951
0
    FSHelperPtr pFS = GetFS();
5952
0
    Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY );
5953
0
    sal_Int32 nSplineType = 0;
5954
0
    if( GetProperty( xPropSet, u"SplineType"_ustr ) )
5955
0
        mAny >>= nSplineType;
5956
0
    const char* pVal = nSplineType != 0 ? "1" : "0";
5957
0
    pFS->singleElement(FSNS(XML_c, XML_smooth), XML_val, pVal);
5958
0
}
5959
5960
void ChartExport::exportFirstSliceAng( )
5961
0
{
5962
0
    FSHelperPtr pFS = GetFS();
5963
0
    sal_Int32 nStartingAngle = 0;
5964
0
    Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
5965
0
    if( GetProperty( xPropSet, u"StartingAngle"_ustr ) )
5966
0
        mAny >>= nStartingAngle;
5967
5968
    // convert to ooxml angle
5969
0
    nStartingAngle = (450 - nStartingAngle ) % 360;
5970
0
    pFS->singleElement(FSNS(XML_c, XML_firstSliceAng), XML_val, OString::number(nStartingAngle));
5971
0
}
5972
5973
namespace {
5974
5975
const char* getErrorBarStyle(sal_Int32 nErrorBarStyle)
5976
0
{
5977
0
    switch(nErrorBarStyle)
5978
0
    {
5979
0
        case cssc::ErrorBarStyle::NONE:
5980
0
            return nullptr;
5981
0
        case cssc::ErrorBarStyle::VARIANCE:
5982
0
            break;
5983
0
        case cssc::ErrorBarStyle::STANDARD_DEVIATION:
5984
0
            return "stdDev";
5985
0
        case cssc::ErrorBarStyle::ABSOLUTE:
5986
0
            return "fixedVal";
5987
0
        case cssc::ErrorBarStyle::RELATIVE:
5988
0
            return "percentage";
5989
0
        case cssc::ErrorBarStyle::ERROR_MARGIN:
5990
0
            break;
5991
0
        case cssc::ErrorBarStyle::STANDARD_ERROR:
5992
0
            return "stdErr";
5993
0
        case cssc::ErrorBarStyle::FROM_DATA:
5994
0
            return "cust";
5995
0
        default:
5996
0
            assert(false && "can't happen");
5997
0
    }
5998
0
    return nullptr;
5999
0
}
6000
6001
Reference< chart2::data::XDataSequence>  getLabeledSequence(
6002
        const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > >& aSequences,
6003
        bool bPositive )
6004
0
{
6005
0
    OUString aDirection;
6006
0
    if(bPositive)
6007
0
        aDirection = "positive";
6008
0
    else
6009
0
        aDirection = "negative";
6010
6011
0
    for( const auto& rSequence : aSequences )
6012
0
    {
6013
0
        if( rSequence.is())
6014
0
        {
6015
0
            uno::Reference< chart2::data::XDataSequence > xSequence( rSequence->getValues());
6016
0
            uno::Reference< beans::XPropertySet > xSeqProp( xSequence, uno::UNO_QUERY_THROW );
6017
0
            OUString aRole;
6018
0
            if( ( xSeqProp->getPropertyValue( u"Role"_ustr ) >>= aRole ) &&
6019
0
                    aRole.match( "error-bars" ) && aRole.indexOf(aDirection) >= 0 )
6020
0
            {
6021
0
                return xSequence;
6022
0
            }
6023
0
        }
6024
0
    }
6025
6026
0
    return Reference< chart2::data::XDataSequence > ();
6027
0
}
6028
6029
}
6030
6031
void ChartExport::exportErrorBar(const Reference< XPropertySet>& xErrorBarProps, bool bYError)
6032
0
{
6033
0
    sal_Int32 nErrorBarStyle = cssc::ErrorBarStyle::NONE;
6034
0
    xErrorBarProps->getPropertyValue(u"ErrorBarStyle"_ustr) >>= nErrorBarStyle;
6035
0
    const char* pErrorBarStyle = getErrorBarStyle(nErrorBarStyle);
6036
0
    if(!pErrorBarStyle)
6037
0
        return;
6038
6039
0
    FSHelperPtr pFS = GetFS();
6040
0
    pFS->startElement(FSNS(XML_c, XML_errBars));
6041
0
    pFS->singleElement(FSNS(XML_c, XML_errDir), XML_val, bYError ? "y" : "x");
6042
0
    bool bPositive = false, bNegative = false;
6043
0
    xErrorBarProps->getPropertyValue(u"ShowPositiveError"_ustr) >>= bPositive;
6044
0
    xErrorBarProps->getPropertyValue(u"ShowNegativeError"_ustr) >>= bNegative;
6045
0
    const char* pErrBarType;
6046
0
    if(bPositive && bNegative)
6047
0
        pErrBarType = "both";
6048
0
    else if(bPositive)
6049
0
        pErrBarType = "plus";
6050
0
    else if(bNegative)
6051
0
        pErrBarType = "minus";
6052
0
    else
6053
0
    {
6054
        // what the hell should we do now?
6055
        // at least this makes the file valid
6056
0
        pErrBarType = "both";
6057
0
    }
6058
0
    pFS->singleElement(FSNS(XML_c, XML_errBarType), XML_val, pErrBarType);
6059
0
    pFS->singleElement(FSNS(XML_c, XML_errValType), XML_val, pErrorBarStyle);
6060
0
    pFS->singleElement(FSNS(XML_c, XML_noEndCap), XML_val, "0");
6061
0
    if(nErrorBarStyle == cssc::ErrorBarStyle::FROM_DATA)
6062
0
    {
6063
0
        uno::Reference< chart2::data::XDataSource > xDataSource(xErrorBarProps, uno::UNO_QUERY);
6064
0
        Sequence< Reference < chart2::data::XLabeledDataSequence > > aSequences =
6065
0
            xDataSource->getDataSequences();
6066
6067
0
        if(bPositive)
6068
0
        {
6069
0
            exportSeriesValues(getLabeledSequence(aSequences, true), XML_plus);
6070
0
        }
6071
6072
0
        if(bNegative)
6073
0
        {
6074
0
            exportSeriesValues(getLabeledSequence(aSequences, false), XML_minus);
6075
0
        }
6076
0
    }
6077
0
    else
6078
0
    {
6079
0
        double nVal = 0.0;
6080
0
        if(nErrorBarStyle == cssc::ErrorBarStyle::STANDARD_DEVIATION)
6081
0
        {
6082
0
            xErrorBarProps->getPropertyValue(u"Weight"_ustr) >>= nVal;
6083
0
        }
6084
0
        else
6085
0
        {
6086
0
            if(bPositive)
6087
0
                xErrorBarProps->getPropertyValue(u"PositiveError"_ustr) >>= nVal;
6088
0
            else
6089
0
                xErrorBarProps->getPropertyValue(u"NegativeError"_ustr) >>= nVal;
6090
0
        }
6091
6092
0
        pFS->singleElement(FSNS(XML_c, XML_val), XML_val, OString::number(nVal));
6093
0
    }
6094
6095
0
    exportShapeProps( xErrorBarProps, XML_c );
6096
6097
0
    pFS->endElement( FSNS( XML_c, XML_errBars) );
6098
0
}
6099
6100
void ChartExport::exportView3D()
6101
0
{
6102
0
    Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
6103
0
    if( !xPropSet.is() )
6104
0
        return;
6105
0
    FSHelperPtr pFS = GetFS();
6106
0
    pFS->startElement(FSNS(XML_c, XML_view3D));
6107
0
    sal_Int32 eChartType = getChartType( );
6108
    // rotX
6109
0
    if( GetProperty( xPropSet, u"RotationHorizontal"_ustr ) )
6110
0
    {
6111
0
        sal_Int32 nRotationX = 0;
6112
0
        mAny >>= nRotationX;
6113
0
        if(eChartType == chart::TYPEID_PIE)
6114
0
        {
6115
            /* In OOXML we get value in 0..90 range for pie chart X rotation , whereas we expect it to be in -90..90 range,
6116
               so we convert that during import. It is modified in View3DConverter::convertFromModel()
6117
               here we convert it back to 0..90 as we received in import */
6118
0
            if( nRotationX < 0 )
6119
0
                nRotationX += 90;  // X rotation (map Chart2 [-179,180] to OOXML [0..90])
6120
0
        }
6121
0
        else
6122
0
        {
6123
0
            assert(nRotationX >= -180 && nRotationX <= 180);
6124
            // X rotation (map Chart2 [-179,180] to OOXML [-90..90])
6125
            // This is not ideal, we are losing information, but that is unavoidable since OOXML does not
6126
            // allow upside down 3d charts.
6127
0
            if( nRotationX < -90 )
6128
0
                nRotationX = -90;
6129
0
            else if( nRotationX > 90 )
6130
0
                nRotationX = 90;
6131
0
        }
6132
0
        pFS->singleElement(FSNS(XML_c, XML_rotX), XML_val, OString::number(nRotationX));
6133
0
    }
6134
    // rotY
6135
0
    if( GetProperty( xPropSet, u"RotationVertical"_ustr ) )
6136
0
    {
6137
        // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
6138
0
        if( eChartType == chart::TYPEID_PIE && GetProperty( xPropSet, u"StartingAngle"_ustr ) )
6139
0
        {
6140
         // Y rotation used as 'first pie slice angle' in 3D pie charts
6141
0
            sal_Int32 nStartingAngle=0;
6142
0
            mAny >>= nStartingAngle;
6143
            // convert to ooxml angle
6144
0
            nStartingAngle = (450 - nStartingAngle ) % 360;
6145
0
            pFS->singleElement(FSNS(XML_c, XML_rotY), XML_val, OString::number(nStartingAngle));
6146
0
        }
6147
0
        else
6148
0
        {
6149
0
            sal_Int32 nRotationY = 0;
6150
0
            mAny >>= nRotationY;
6151
            // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
6152
0
            if( nRotationY < 0 )
6153
0
                nRotationY += 360;
6154
0
            pFS->singleElement(FSNS(XML_c, XML_rotY), XML_val, OString::number(nRotationY));
6155
0
        }
6156
0
    }
6157
    // rAngAx
6158
0
    if( GetProperty( xPropSet, u"RightAngledAxes"_ustr ) )
6159
0
    {
6160
0
        bool bRightAngled = false;
6161
0
        mAny >>= bRightAngled;
6162
0
        const char* sRightAngled = bRightAngled ? "1":"0";
6163
0
        pFS->singleElement(FSNS(XML_c, XML_rAngAx), XML_val, sRightAngled);
6164
0
    }
6165
    // perspective
6166
0
    if( GetProperty( xPropSet, u"Perspective"_ustr ) )
6167
0
    {
6168
0
        sal_Int32 nPerspective = 0;
6169
0
        mAny >>= nPerspective;
6170
        // map Chart2 [0,100] to OOXML [0..200]
6171
0
        nPerspective *= 2;
6172
0
        pFS->singleElement(FSNS(XML_c, XML_perspective), XML_val, OString::number(nPerspective));
6173
0
    }
6174
0
    pFS->endElement( FSNS( XML_c, XML_view3D ) );
6175
0
}
6176
6177
bool ChartExport::isDeep3dChart()
6178
0
{
6179
0
    bool isDeep = false;
6180
0
    if( mbIs3DChart )
6181
0
    {
6182
0
        Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
6183
0
        if( GetProperty( xPropSet, u"Deep"_ustr ) )
6184
0
            mAny >>= isDeep;
6185
0
    }
6186
0
    return isDeep;
6187
0
}
6188
6189
// Determine if the chart is a chartex type or pre-2015 ("classic chart") type.
6190
// If *peNS is non-null, also set it to the namespace abbreviation (e.g., "cx1",
6191
// "cx2", etc.)
6192
bool ChartExport::isChartexNotChartNS(NamespaceAbbrev *peNS) const
6193
0
{
6194
0
    Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( mxNewDiagram, uno::UNO_QUERY );
6195
0
    if( ! xBCooSysCnt.is()) return false;
6196
6197
    // chart type
6198
0
    const Sequence< Reference< chart2::XCoordinateSystem > >
6199
0
        aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
6200
6201
0
    for( const auto& rCS : aCooSysSeq ) {
6202
0
        Reference< chart2::XChartTypeContainer > xCTCnt( rCS, uno::UNO_QUERY );
6203
0
        if( ! xCTCnt.is())
6204
0
            continue;
6205
0
        const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
6206
0
        for( const auto& rCT : aCTSeq ) {
6207
0
            Reference< chart2::XDataSeriesContainer > xDSCnt( rCT, uno::UNO_QUERY );
6208
0
            if( ! xDSCnt.is())
6209
0
                return false;
6210
0
            Reference< chart2::XChartType > xChartType( rCT, uno::UNO_QUERY );
6211
0
            if( ! xChartType.is())
6212
0
                continue;
6213
            // note: if xDSCnt.is() then also aCTSeq[nCTIdx]
6214
0
            OUString aChartType( xChartType->getChartType());
6215
0
            sal_Int32 eChartType = lcl_getChartType( aChartType );
6216
0
            switch( eChartType )
6217
0
            {
6218
0
                case chart::TYPEID_BAR:
6219
0
                case chart::TYPEID_AREA:
6220
0
                case chart::TYPEID_LINE:
6221
0
                case chart::TYPEID_BUBBLE:
6222
0
                case chart::TYPEID_OFPIE:
6223
0
                case chart::TYPEID_DOUGHNUT:
6224
0
                case chart::TYPEID_PIE:
6225
0
                case chart::TYPEID_RADARLINE:
6226
0
                case chart::TYPEID_RADARAREA:
6227
0
                case chart::TYPEID_SCATTER:
6228
0
                case chart::TYPEID_STOCK:
6229
0
                case chart::TYPEID_SURFACE:
6230
0
                    break;
6231
0
                case chart::TYPEID_BOXWHISKER:
6232
0
                case chart::TYPEID_CLUSTEREDCOLUMN:
6233
0
                case chart::TYPEID_PARETOLINE:
6234
0
                case chart::TYPEID_SUNBURST:
6235
0
                case chart::TYPEID_TREEMAP:
6236
0
                case chart::TYPEID_WATERFALL:
6237
0
                    if (peNS) *peNS = NamespaceAbbrev::CX1;
6238
0
                    return true;
6239
0
                case chart::TYPEID_FUNNEL:
6240
0
                    if (peNS) *peNS = NamespaceAbbrev::CX2;
6241
0
                    return true;
6242
0
                case chart::TYPEID_REGIONMAP:
6243
0
                    if (peNS) *peNS = NamespaceAbbrev::CX4;
6244
0
                    return true;
6245
0
                default:
6246
0
                    assert(false);
6247
0
                    break;
6248
0
            }
6249
0
        }
6250
0
    }
6251
0
    if (peNS) *peNS = NamespaceAbbrev::NONE;
6252
0
    return false;
6253
0
}
6254
6255
OUString ChartExport::getNumberFormatCode(sal_Int32 nKey) const
6256
0
{
6257
    /* XXX if this was called more than one or two times per export the two
6258
     * SvNumberFormatter instances and NfKeywordTable should be member
6259
     * variables and initialized only once. */
6260
6261
0
    OUString aCode(u"General"_ustr);  // init with fallback
6262
0
    uno::Reference<util::XNumberFormatsSupplier> xNumberFormatsSupplier(mxChartModel, uno::UNO_QUERY_THROW);
6263
0
    SvNumberFormatsSupplierObj* pSupplierObj = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( xNumberFormatsSupplier);
6264
0
    if (!pSupplierObj)
6265
0
        return aCode;
6266
6267
0
    SvNumberFormatter* pNumberFormatter = pSupplierObj->GetNumberFormatter();
6268
0
    if (!pNumberFormatter)
6269
0
        return aCode;
6270
6271
0
    SvNumberFormatter aTempFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US);
6272
0
    NfKeywordTable aKeywords;
6273
0
    aTempFormatter.FillKeywordTableForExcel( aKeywords);
6274
0
    aCode = pNumberFormatter->GetFormatStringForExcel( nKey, aKeywords, aTempFormatter);
6275
6276
0
    return aCode;
6277
0
}
6278
6279
// Create cs:CT_StyleReference
6280
void ChartExport::outputStyleRef(FSHelperPtr pFS, sal_Int32 nElTokenId,
6281
        const model::StyleRef& aColor)
6282
0
{
6283
0
    pFS->startElement(FSNS(XML_cs, nElTokenId), XML_idx,
6284
0
            OUString::number(aColor.mnIdx));
6285
6286
0
    ThemeExport aTE(mpFB, GetDocumentType(), pFS);
6287
0
    aTE.writeComplexColor(aColor.maComplexColor);
6288
6289
    // Get the string for the StyleColorVal
6290
0
    OUString sV = aColor.getColorValStr();
6291
0
    if (!sV.isEmpty()) {
6292
0
        pFS->singleElement(FSNS(XML_cs, XML_styleClr), XML_val, sV);
6293
0
    }
6294
6295
0
    pFS->endElement(FSNS(XML_cs, nElTokenId));
6296
0
}
6297
6298
// Create cs:CT_FontReference
6299
void ChartExport::outputFontRef(FSHelperPtr pFS, sal_Int32 nElTokenId,
6300
        const model::FontRef& aColor)
6301
0
{
6302
0
    pFS->startElement(FSNS(XML_cs, nElTokenId), XML_idx, aColor.getFontCollectionStr());
6303
6304
0
    ThemeExport aTE(mpFB, GetDocumentType(), pFS);
6305
0
    aTE.writeComplexColor(aColor.maComplexColor);
6306
6307
    // Get the string for the StyleColorVal
6308
0
    OUString sV = aColor.getColorValStr();
6309
0
    if (!sV.isEmpty()) {
6310
0
        pFS->singleElement(FSNS(XML_cs, XML_styleClr), XML_val, sV);
6311
0
    }
6312
6313
0
    pFS->endElement(FSNS(XML_cs, nElTokenId));
6314
0
}
6315
6316
// Create cs:CT_StyleEntry
6317
void ChartExport::outputStyleEntry(FSHelperPtr pFS, sal_Int32 nElTokenId, model::StyleEntry& aEntry)
6318
0
{
6319
    // Just default values for now
6320
0
    pFS->startElement(FSNS(XML_cs, nElTokenId));
6321
6322
0
    outputStyleRef(pFS, FSNS(XML_cs, XML_lnRef), *(aEntry.mxLnClr));
6323
6324
0
    pFS->startElement(FSNS(XML_cs, XML_lineWidthScale));
6325
0
    pFS->write(aEntry.mfLineWidthScale);
6326
0
    pFS->endElement(FSNS(XML_cs, XML_lineWidthScale));
6327
6328
0
    outputStyleRef(pFS, FSNS(XML_cs, XML_fillRef), *(aEntry.mxFillClr));
6329
0
    outputStyleRef(pFS, FSNS(XML_cs, XML_effectRef), *(aEntry.mxEffectClr));
6330
0
    outputFontRef(pFS, FSNS(XML_cs, XML_fontRef), *(aEntry.mxFontClr));
6331
6332
0
    if (aEntry.mxShapePr) {
6333
0
        exportShapeProps(aEntry.mxShapePr->getShapeProperties().makePropertySet(), XML_cs);
6334
0
    }
6335
6336
0
    if (aEntry.mrTextCharacterPr) {
6337
0
        PropertyMap *pPM = aEntry.mrTextCharacterPr.get();
6338
0
        Reference< XPropertySet > rPS = pPM->makePropertySet();
6339
6340
0
        WriteRunInput aInput;
6341
0
        aInput.bUseTextSchemeColors = true;
6342
0
        WriteRunProperties(rPS, XML_defRPr, aInput);
6343
0
    }
6344
6345
0
    if (aEntry.mxTextBodyPr) {
6346
0
        PropertyMap *pPM = aEntry.mxTextBodyPr.get();
6347
0
        aEntry.mxTextBodyPr.reset();
6348
0
        Reference< XPropertySet > rPS = pPM->makePropertySet();
6349
0
        WriteBodyProps(rPS, XML_cs, false, 0, 0, 0, 0);
6350
0
    }
6351
6352
0
    pFS->endElement(FSNS(XML_cs, nElTokenId));
6353
0
}
6354
6355
}// oox
6356
6357
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */