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 <drawingml/chart/typegroupconverter.hxx>
21
22 #include <com/sun/star/chart/DataLabelPlacement.hpp>
23 #include <com/sun/star/chart2/CartesianCoordinateSystem2d.hpp>
24 #include <com/sun/star/chart2/CartesianCoordinateSystem3d.hpp>
25 #include <com/sun/star/chart2/PolarCoordinateSystem2d.hpp>
26 #include <com/sun/star/chart2/PolarCoordinateSystem3d.hpp>
27 #include <com/sun/star/chart2/CurveStyle.hpp>
28 #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
29 #include <com/sun/star/chart2/PieChartSubType.hpp>
30 #include <com/sun/star/chart2/StackingDirection.hpp>
31 #include <com/sun/star/chart2/Symbol.hpp>
32 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
33 #include <com/sun/star/chart2/XCoordinateSystem.hpp>
34 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
35 #include <com/sun/star/chart2/XDiagram.hpp>
36 #include <com/sun/star/chart2/data/XDataSink.hpp>
37 #include <com/sun/star/drawing/LineStyle.hpp>
38
39 #include <comphelper/sequence.hxx>
40 #include <osl/diagnose.h>
41 #include <drawingml/lineproperties.hxx>
42 #include <drawingml/chart/seriesmodel.hxx>
43 #include <drawingml/chart/seriesconverter.hxx>
44 #include <drawingml/chart/typegroupmodel.hxx>
45 #include <oox/core/xmlfilterbase.hxx>
46 #include <oox/token/namespaces.hxx>
47 #include <oox/token/properties.hxx>
48 #include <oox/token/tokens.hxx>
49 #include <tools/UnitConversion.hxx>
50
51 namespace oox::drawingml::chart {
52
53 using namespace ::com::sun::star::beans;
54 using namespace ::com::sun::star::chart2;
55 using namespace ::com::sun::star::chart2::data;
56 using namespace ::com::sun::star::uno;
57
58 namespace {
59
60 // chart type service names
61 const char SERVICE_CHART2_AREA[] = "com.sun.star.chart2.AreaChartType";
62 const char SERVICE_CHART2_CANDLE[] = "com.sun.star.chart2.CandleStickChartType";
63 const char SERVICE_CHART2_COLUMN[] = "com.sun.star.chart2.ColumnChartType";
64 const char SERVICE_CHART2_LINE[] = "com.sun.star.chart2.LineChartType";
65 const char SERVICE_CHART2_NET[] = "com.sun.star.chart2.NetChartType";
66 const char SERVICE_CHART2_FILLEDNET[] = "com.sun.star.chart2.FilledNetChartType";
67 const char SERVICE_CHART2_PIE[] = "com.sun.star.chart2.PieChartType";
68 const char SERVICE_CHART2_SCATTER[] = "com.sun.star.chart2.ScatterChartType";
69 const char SERVICE_CHART2_BUBBLE[] = "com.sun.star.chart2.BubbleChartType";
70 const char SERVICE_CHART2_SURFACE[] = "com.sun.star.chart2.ColumnChartType"; // Todo
71 const char SERVICE_CHART2_FUNNEL[] = "com.sun.star.chart2.FunnelChartType";
72 const char SERVICE_CHART2_HISTO[] = "com.sun.star.chart2.HistogramChartType";
73
74 namespace csscd = css::chart::DataLabelPlacement;
75
76 const TypeGroupInfo spTypeInfos[] =
77 {
78 // type-id type-category service varied-point-color default label pos polar area2d 1stvis xcateg swap stack picopt
79 { TYPEID_BAR, TYPECATEGORY_BAR, SERVICE_CHART2_COLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, true, true },
80 { TYPEID_HORBAR, TYPECATEGORY_BAR, SERVICE_CHART2_COLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, true, true, true },
81 { TYPEID_HISTO, TYPECATEGORY_HISTO, SERVICE_CHART2_HISTO, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, true, true, true },
82 { TYPEID_LINE, TYPECATEGORY_LINE, SERVICE_CHART2_LINE, VARPOINTMODE_SINGLE, csscd::RIGHT, false, false, false, true, false, true, false },
83 { TYPEID_AREA, TYPECATEGORY_LINE, SERVICE_CHART2_AREA, VARPOINTMODE_NONE, csscd::CENTER, false, true, false, true, false, true, false },
84 { TYPEID_STOCK, TYPECATEGORY_LINE, SERVICE_CHART2_CANDLE, VARPOINTMODE_NONE, csscd::RIGHT, false, false, false, true, false, true, false },
85 { TYPEID_RADARLINE, TYPECATEGORY_RADAR, SERVICE_CHART2_NET, VARPOINTMODE_SINGLE, csscd::OUTSIDE, true, false, false, true, false, false, false },
86 { TYPEID_RADARAREA, TYPECATEGORY_RADAR, SERVICE_CHART2_FILLEDNET, VARPOINTMODE_NONE, csscd::OUTSIDE, true, true, false, true, false, false, false },
87 { TYPEID_PIE, TYPECATEGORY_PIE, SERVICE_CHART2_PIE, VARPOINTMODE_MULTI, csscd::AVOID_OVERLAP, true, true, true, true, false, false, false },
88 { TYPEID_DOUGHNUT, TYPECATEGORY_PIE, SERVICE_CHART2_PIE, VARPOINTMODE_MULTI, csscd::AVOID_OVERLAP, true, true, false, true, false, false, false },
89 { TYPEID_OFPIE, TYPECATEGORY_PIE, SERVICE_CHART2_PIE, VARPOINTMODE_MULTI, csscd::AVOID_OVERLAP, true, true, true, true, false, false, false },
90 { TYPEID_SCATTER, TYPECATEGORY_SCATTER, SERVICE_CHART2_SCATTER, VARPOINTMODE_SINGLE, csscd::RIGHT, false, false, false, false, false, false, false },
91 { TYPEID_BUBBLE, TYPECATEGORY_SCATTER, SERVICE_CHART2_BUBBLE, VARPOINTMODE_SINGLE, csscd::RIGHT, false, true, false, false, false, false, false },
92 { TYPEID_SURFACE, TYPECATEGORY_SURFACE, SERVICE_CHART2_SURFACE, VARPOINTMODE_NONE, csscd::RIGHT, false, true, false, true, false, false, false },
93 { TYPEID_FUNNEL, TYPECATEGORY_FUNNEL, SERVICE_CHART2_FUNNEL, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, false, false }
94 };
95
96 const TypeGroupInfo saUnknownTypeInfo =
97 { TYPEID_UNKNOWN, TYPECATEGORY_BAR, SERVICE_CHART2_COLUMN, VARPOINTMODE_SINGLE, csscd::OUTSIDE, false, true, false, true, false, true, true };
98
lclGetTypeInfoFromTypeId(TypeId eTypeId)99 const TypeGroupInfo& lclGetTypeInfoFromTypeId( TypeId eTypeId )
100 {
101 for( auto const &rIt : spTypeInfos)
102 {
103 if( rIt.meTypeId == eTypeId )
104 return rIt;
105 }
106 OSL_ENSURE( eTypeId == TYPEID_UNKNOWN, "lclGetTypeInfoFromTypeId - unexpected chart type identifier" );
107 return saUnknownTypeInfo;
108 }
109
110 } // namespace
111
GetTypeGroupInfo(TypeId eType)112 const TypeGroupInfo& GetTypeGroupInfo( TypeId eType )
113 {
114 return lclGetTypeInfoFromTypeId(eType);
115 }
116
UpDownBarsConverter(const ConverterRoot & rParent,UpDownBarsModel & rModel)117 UpDownBarsConverter::UpDownBarsConverter( const ConverterRoot& rParent, UpDownBarsModel& rModel ) :
118 ConverterBase< UpDownBarsModel >( rParent, rModel )
119 {
120 }
121
~UpDownBarsConverter()122 UpDownBarsConverter::~UpDownBarsConverter()
123 {
124 }
125
convertFromModel(const Reference<XChartType> & rxChartType)126 void UpDownBarsConverter::convertFromModel( const Reference< XChartType >& rxChartType )
127 {
128 PropertySet aTypeProp( rxChartType );
129
130 // upbar format
131 Reference< XPropertySet > xWhitePropSet;
132 if( aTypeProp.getProperty( xWhitePropSet, PROP_WhiteDay ) )
133 {
134 PropertySet aPropSet( xWhitePropSet );
135 getFormatter().convertFrameFormatting( aPropSet, mrModel.mxUpBars, OBJECTTYPE_UPBAR );
136 }
137
138 // downbar format
139 Reference< XPropertySet > xBlackPropSet;
140 if( aTypeProp.getProperty( xBlackPropSet, PROP_BlackDay ) )
141 {
142 PropertySet aPropSet( xBlackPropSet );
143 getFormatter().convertFrameFormatting( aPropSet, mrModel.mxDownBars, OBJECTTYPE_DOWNBAR );
144 }
145 }
146
TypeGroupConverter(const ConverterRoot & rParent,TypeGroupModel & rModel)147 TypeGroupConverter::TypeGroupConverter( const ConverterRoot& rParent, TypeGroupModel& rModel ) :
148 ConverterBase< TypeGroupModel >( rParent, rModel ),
149 mb3dChart( false )
150 {
151 TypeId eTypeId = TYPEID_UNKNOWN;
152 switch( mrModel.mnTypeId )
153 {
154 #define ENSURE_AXESCOUNT( min, max ) OSL_ENSURE( (min <= static_cast<int>(mrModel.maAxisIds.size())) && (static_cast<int>(mrModel.maAxisIds.size()) <= max), "TypeGroupConverter::TypeGroupConverter - invalid axes count" )
155 case C_TOKEN( area3DChart ): ENSURE_AXESCOUNT( 2, 3 ); eTypeId = TYPEID_AREA; mb3dChart = true; break;
156 case C_TOKEN( areaChart ): ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_AREA; mb3dChart = false; break;
157 case C_TOKEN( bar3DChart ): ENSURE_AXESCOUNT( 2, 3 ); eTypeId = TYPEID_BAR; mb3dChart = true; break;
158 case C_TOKEN( barChart ): ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_BAR; mb3dChart = false; break;
159 case C_TOKEN( bubbleChart ): ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_BUBBLE; mb3dChart = false; break;
160 case C_TOKEN( doughnutChart ): ENSURE_AXESCOUNT( 0, 0 ); eTypeId = TYPEID_DOUGHNUT; mb3dChart = false; break;
161 case CX_TOKEN( funnel ): ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_FUNNEL; mb3dChart = false; break;
162 case C_TOKEN( line3DChart ): ENSURE_AXESCOUNT( 3, 3 ); eTypeId = TYPEID_LINE; mb3dChart = true; break;
163 case C_TOKEN( lineChart ): ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_LINE; mb3dChart = false; break;
164 case C_TOKEN( ofPieChart ): ENSURE_AXESCOUNT( 0, 0 ); eTypeId = TYPEID_OFPIE; mb3dChart = false; break;
165 case C_TOKEN( pie3DChart ): ENSURE_AXESCOUNT( 0, 0 ); eTypeId = TYPEID_PIE; mb3dChart = true; break;
166 case C_TOKEN( pieChart ): ENSURE_AXESCOUNT( 0, 0 ); eTypeId = TYPEID_PIE; mb3dChart = false; break;
167 case C_TOKEN( radarChart ): ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_RADARLINE; mb3dChart = false; break;
168 case C_TOKEN( scatterChart ): ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_SCATTER; mb3dChart = false; break;
169 case C_TOKEN( stockChart ): ENSURE_AXESCOUNT( 2, 2 ); eTypeId = TYPEID_STOCK; mb3dChart = false; break;
170 case C_TOKEN( surface3DChart ): ENSURE_AXESCOUNT( 3, 3 ); eTypeId = TYPEID_SURFACE; mb3dChart = true; break;
171 case C_TOKEN( surfaceChart ): ENSURE_AXESCOUNT( 2, 3 ); eTypeId = TYPEID_SURFACE; mb3dChart = true; break; // 3D bar chart from all surface charts
172 default: OSL_FAIL( "TypeGroupConverter::TypeGroupConverter - unknown chart type" );
173 #undef ENSURE_AXESCOUNT
174 }
175
176 // special handling for some chart types
177 switch( eTypeId )
178 {
179 case TYPEID_BAR:
180 if( mrModel.mnBarDir == XML_bar )
181 eTypeId = TYPEID_HORBAR;
182 break;
183 case TYPEID_RADARLINE:
184 if( mrModel.mnRadarStyle == XML_filled )
185 eTypeId = TYPEID_RADARAREA;
186 break;
187 case TYPEID_SURFACE:
188 // create a deep 3D bar chart from surface charts
189 mrModel.mnGrouping = XML_standard;
190 break;
191 default:;
192 }
193
194 // set the chart type info struct for the current chart type
195 maTypeInfo = lclGetTypeInfoFromTypeId( eTypeId );
196 }
197
~TypeGroupConverter()198 TypeGroupConverter::~TypeGroupConverter()
199 {
200 }
201
isStacked() const202 bool TypeGroupConverter::isStacked() const
203 {
204 return maTypeInfo.mbSupportsStacking && (mrModel.mnGrouping == XML_stacked);
205 }
206
isPercent() const207 bool TypeGroupConverter::isPercent() const
208 {
209 return maTypeInfo.mbSupportsStacking && (mrModel.mnGrouping == XML_percentStacked);
210 }
211
isWall3dChart() const212 bool TypeGroupConverter::isWall3dChart() const
213 {
214 return mb3dChart && (maTypeInfo.meTypeCategory != TYPECATEGORY_PIE);
215 }
216
isDeep3dChart() const217 bool TypeGroupConverter::isDeep3dChart() const
218 {
219 return isWall3dChart() && (mrModel.mnGrouping == XML_standard);
220 }
221
isSeriesFrameFormat() const222 bool TypeGroupConverter::isSeriesFrameFormat() const
223 {
224 return mb3dChart || maTypeInfo.mbSeriesIsFrame2d;
225 }
226
getSeriesObjectType() const227 ObjectType TypeGroupConverter::getSeriesObjectType() const
228 {
229 return mb3dChart ? OBJECTTYPE_FILLEDSERIES3D :
230 (maTypeInfo.mbSeriesIsFrame2d ? OBJECTTYPE_FILLEDSERIES2D : OBJECTTYPE_LINEARSERIES2D);
231 }
232
getSingleSeriesTitle() const233 OUString TypeGroupConverter::getSingleSeriesTitle() const
234 {
235 OUString aSeriesTitle;
236 if( !mrModel.maSeries.empty() && (maTypeInfo.mbSingleSeriesVis || (mrModel.maSeries.size() == 1)) )
237 if( const TextModel* pText = mrModel.maSeries.front()->mxText.get() )
238 if( const DataSequenceModel* pDataSeq = pText->mxDataSeq.get() )
239 if( !pDataSeq->maData.empty() )
240 pDataSeq->maData.begin()->second >>= aSeriesTitle;
241 return aSeriesTitle;
242 }
243
isSingleSeriesTitle() const244 bool TypeGroupConverter::isSingleSeriesTitle() const
245 {
246 if (!mrModel.maSeries.empty() && (maTypeInfo.mbSingleSeriesVis || (mrModel.maSeries.size() == 1)) &&
247 mrModel.maSeries.front()->mxText.is())
248 return true;
249
250 return false;
251 }
252
createCoordinateSystem()253 Reference< XCoordinateSystem > TypeGroupConverter::createCoordinateSystem()
254 {
255 // create the coordinate system object
256 Reference< css::uno::XComponentContext > xContext = getComponentContext();
257 Reference< XCoordinateSystem > xCoordSystem;
258 if( maTypeInfo.mbPolarCoordSystem )
259 {
260 if( mb3dChart )
261 xCoordSystem = css::chart2::PolarCoordinateSystem3d::create(xContext);
262 else
263 xCoordSystem = css::chart2::PolarCoordinateSystem2d::create(xContext);
264 }
265 else
266 {
267 if( mb3dChart )
268 xCoordSystem = css::chart2::CartesianCoordinateSystem3d::create(xContext);
269 else
270 xCoordSystem = css::chart2::CartesianCoordinateSystem2d::create(xContext);
271 }
272
273 // swap X and Y axis
274 if( maTypeInfo.mbSwappedAxesSet )
275 {
276 PropertySet aPropSet( xCoordSystem );
277 aPropSet.setProperty( PROP_SwapXAndYAxis, true );
278 }
279
280 return xCoordSystem;
281 }
282
createCategorySequence()283 Reference< XLabeledDataSequence > TypeGroupConverter::createCategorySequence()
284 {
285 sal_Int32 nMaxValues = 0;
286 Reference< XLabeledDataSequence > xLabeledSeq;
287 /* Find first existing category sequence. The behaviour of Excel 2007 is
288 different to Excel 2003, which always used the category sequence of the
289 first series, even if it was empty. */
290 for (auto const& elem : mrModel.maSeries)
291 {
292 if( elem->maSources.has( DataSourceType::CATEGORIES ) )
293 {
294 SeriesConverter aSeriesConv(*this, *elem);
295 xLabeledSeq = aSeriesConv.createCategorySequence( u"categories"_ustr );
296 if (xLabeledSeq.is())
297 break;
298 }
299 else if( nMaxValues <= 0 && elem->maSources.has( DataSourceType::VALUES ) )
300 {
301 DataSourceModel *pValues = elem->maSources.get( DataSourceType::VALUES ).get();
302 if( pValues->mxDataSeq.is() )
303 nMaxValues = pValues->mxDataSeq->maData.size();
304 }
305 }
306 /* n#839727 Create Category Sequence when none are found */
307 if( !xLabeledSeq.is() && !mrModel.maSeries.empty() ) {
308 if( nMaxValues < 0 )
309 nMaxValues = 2;
310 typedef RefVector<SeriesModel> SeriesModelVector;
311 SeriesModelVector::value_type aModel = mrModel.maSeries.get(0);
312 if (!aModel->maSources.has(DataSourceType::CATEGORIES))
313 {
314 DataSourceModel &aSrc = aModel->maSources.create( DataSourceType::CATEGORIES );
315 DataSequenceModel &aSeq = aSrc.mxDataSeq.create();
316 aSeq.mnPointCount = nMaxValues;
317 for( sal_Int32 i = 0; i < nMaxValues; i++ )
318 aSeq.maData[ i ] <<= OUString::number( i + 1 );
319 }
320 SeriesConverter aSeriesConv( *this, *aModel );
321 xLabeledSeq = aSeriesConv.createCategorySequence( u"categories"_ustr );
322 }
323 return xLabeledSeq;
324 }
325
convertFromModel(const Reference<XDiagram> & rxDiagram,const Reference<XCoordinateSystem> & rxCoordSystem,sal_Int32 nAxesSetIdx,bool bSupportsVaryColorsByPoint)326 void TypeGroupConverter::convertFromModel( const Reference< XDiagram >& rxDiagram,
327 const Reference< XCoordinateSystem >& rxCoordSystem,
328 sal_Int32 nAxesSetIdx,
329 bool bSupportsVaryColorsByPoint )
330 {
331 try
332 {
333 // create the chart type object
334 OUString aService = OUString::createFromAscii( maTypeInfo.mpcServiceName );
335 Reference< XChartType > xChartType( createInstance( aService ), UNO_QUERY_THROW );
336
337 Reference< XChartTypeContainer > xChartTypeContOld( rxCoordSystem, UNO_QUERY_THROW );
338 Sequence< Reference< XChartType > > xOldChartTypes( xChartTypeContOld->getChartTypes() );
339 sal_Int32 nOldChartTypeIdx = -1;
340
341 // additional properties
342 PropertySet aDiaProp( rxDiagram );
343 PropertySet aTypeProp( xChartType );
344 switch( maTypeInfo.meTypeCategory )
345 {
346 case TYPECATEGORY_BAR:
347 {
348 Sequence< sal_Int32 > aInt32Seq{ mrModel.mnOverlap, mrModel.mnOverlap };
349 aTypeProp.setProperty( PROP_OverlapSequence, aInt32Seq );
350 aInt32Seq = { mrModel.mnGapWidth, mrModel.mnGapWidth };
351 aTypeProp.setProperty( PROP_GapwidthSequence, aInt32Seq );
352 }
353 break;
354 case TYPECATEGORY_PIE:
355 {
356 aTypeProp.setProperty( PROP_UseRings, maTypeInfo.meTypeId == TYPEID_DOUGHNUT );
357 /* #i85166# starting angle of first pie slice. 3D pie charts
358 use Y rotation setting in view3D element. Of-pie charts do
359 not support pie rotation. */
360 if( !is3dChart() && (maTypeInfo.meTypeId != TYPEID_OFPIE) )
361 convertPieRotation( aDiaProp, mrModel.mnFirstAngle );
362
363 if (maTypeInfo.meTypeId == TYPEID_OFPIE) {
364 aDiaProp.setProperty(PROP_SubPieType,
365 convertOfPieType(mrModel.mnOfPieType));
366 if (mrModel.mnSplitType == XML_auto ||
367 mrModel.mnSplitType == XML_pos) {
368 aDiaProp.setProperty(PROP_SplitPos, mrModel.mfSplitPos);
369 }
370 } else {
371 aDiaProp.setProperty(PROP_SubPieType, PieChartSubType_NONE);
372 }
373 }
374 break;
375 default:;
376 }
377
378 // create converter objects for all series models
379 typedef RefVector< SeriesConverter > SeriesConvVector;
380 SeriesConvVector aSeries;
381 for (auto const& elemSeries : mrModel.maSeries)
382 aSeries.push_back( std::make_shared<SeriesConverter>(*this, *elemSeries) );
383
384 // decide whether to use varying colors for each data point
385 bool bVaryColorsByPoint = bSupportsVaryColorsByPoint && mrModel.mbVaryColors;
386 switch( maTypeInfo.meVarPointMode )
387 {
388 case VARPOINTMODE_NONE: bVaryColorsByPoint = false; break;
389 case VARPOINTMODE_SINGLE: bVaryColorsByPoint &= (mrModel.maSeries.size() == 1); break;
390 case VARPOINTMODE_MULTI: break;
391 }
392
393 /* Stock chart needs special processing. Create one 'big' series with
394 data sequences of different roles. */
395 if( maTypeInfo.meTypeId == TYPEID_STOCK )
396 {
397 // create the data series object
398 Reference< XDataSeries > xDataSeries( createInstance( u"com.sun.star.chart2.DataSeries"_ustr ), UNO_QUERY );
399 Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
400 if( xDataSink.is() )
401 {
402 // create a list of data sequences from all series
403 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
404 OSL_ENSURE( aSeries.size() >= 3, "TypeGroupConverter::convertFromModel - too few stock chart series" );
405 int nRoleIdx = (aSeries.size() == 3) ? 1 : 0;
406 for( auto& rxSeriesConv : aSeries )
407 {
408 // create a data sequence with a specific role
409 OUString aRole;
410 switch( nRoleIdx )
411 {
412 case 0: aRole = "values-first"; break;
413 case 1: aRole = "values-max"; break;
414 case 2: aRole = "values-min"; break;
415 case 3: aRole = "values-last"; break;
416 }
417 Reference< XLabeledDataSequence > xDataSeq = rxSeriesConv->createValueSequence( aRole );
418 if( xDataSeq.is() )
419 aLabeledSeqVec.push_back( xDataSeq );
420
421 ++nRoleIdx;
422 if (nRoleIdx >= 4)
423 break;
424 }
425
426 // attach labeled data sequences to series and insert series into chart type
427 xDataSink->setData( comphelper::containerToSequence( aLabeledSeqVec ) );
428
429 // formatting of high/low lines
430 aTypeProp.setProperty( PROP_ShowHighLow, true );
431 PropertySet aSeriesProp( xDataSeries );
432 if( mrModel.mxHiLowLines.is() )
433 getFormatter().convertFrameFormatting( aSeriesProp, mrModel.mxHiLowLines, OBJECTTYPE_HILOLINE );
434 else
435 // hi/low-lines cannot be switched off via "ShowHighLow" property (?)
436 aSeriesProp.setProperty( PROP_LineStyle, css::drawing::LineStyle_NONE );
437
438 // formatting of up/down bars
439 bool bUpDownBars = mrModel.mxUpDownBars.is();
440 aTypeProp.setProperty( PROP_Japanese, bUpDownBars );
441 aTypeProp.setProperty( PROP_ShowFirst, bUpDownBars );
442 if( bUpDownBars )
443 {
444 UpDownBarsConverter aUpDownConv( *this, *mrModel.mxUpDownBars );
445 aUpDownConv.convertFromModel( xChartType );
446 }
447
448 // insert the series into the chart type object
449 insertDataSeries( xChartType, xDataSeries, nAxesSetIdx );
450 }
451 }
452 else
453 {
454 for( sal_Int32 nCTIdx=0; nCTIdx<xOldChartTypes.getLength(); ++nCTIdx )
455 {
456 if ( xChartType->getChartType() == xOldChartTypes[nCTIdx]->getChartType() )
457 {
458 nOldChartTypeIdx = nCTIdx;
459 }
460 }
461
462 for (auto const& elem : aSeries)
463 {
464 SeriesConverter& rSeriesConv = *elem;
465 Reference< XDataSeries > xDataSeries = rSeriesConv.createDataSeries( *this, bVaryColorsByPoint );
466 insertDataSeries( nOldChartTypeIdx == -1 ? xChartType : xOldChartTypes[nOldChartTypeIdx], xDataSeries, nAxesSetIdx );
467
468 /* Excel does not use the value of the c:smooth element of the
469 chart type to set a default line smoothing for the data
470 series. Line smoothing is always controlled by the c:smooth
471 element of the respective data series. If the element in the
472 data series is missing, line smoothing is off, regardless of
473 the c:smooth element of the chart type. */
474 #if !OOX_CHART_SMOOTHED_PER_SERIES
475 if( rSeriesConv.getModel().mbSmooth )
476 convertLineSmooth( aTypeProp, true );
477 #endif
478 }
479 }
480
481 // add chart type object to coordinate system
482 Reference< XChartTypeContainer > xChartTypeCont( rxCoordSystem, UNO_QUERY_THROW );
483 if (nOldChartTypeIdx == -1)
484 {
485 xChartTypeCont->addChartType(xChartType);
486 }
487
488 // set existence of bar connector lines at diagram (only in stacked 2D bar charts)
489 if( mrModel.mxSerLines.is() && !mb3dChart && (maTypeInfo.meTypeCategory == TYPECATEGORY_BAR) && (isStacked() || isPercent()) )
490 aDiaProp.setProperty( PROP_ConnectBars, true );
491 }
492 catch( Exception& )
493 {
494 OSL_FAIL( "TypeGroupConverter::convertFromModel - cannot add chart type" );
495 }
496 }
497
convertMarker(PropertySet & rPropSet,sal_Int32 nOoxSymbol,sal_Int32 nOoxSize,const ModelRef<Shape> & xShapeProps) const498 void TypeGroupConverter::convertMarker( PropertySet& rPropSet, sal_Int32 nOoxSymbol, sal_Int32 nOoxSize,
499 const ModelRef< Shape >& xShapeProps ) const
500 {
501 if( isSeriesFrameFormat() )
502 return;
503
504 namespace cssc = css::chart2;
505
506 // symbol style
507 cssc::Symbol aSymbol;
508 aSymbol.Style = cssc::SymbolStyle_STANDARD;
509 switch( nOoxSymbol ) // compare with XclChPropSetHelper::WriteMarkerProperties in xlchart.cxx
510 {
511 case XML_auto: aSymbol.Style = cssc::SymbolStyle_AUTO; break;
512 case XML_none: aSymbol.Style = cssc::SymbolStyle_NONE; break;
513 case XML_square: aSymbol.StandardSymbol = 0; break; // square
514 case XML_diamond: aSymbol.StandardSymbol = 1; break; // diamond
515 case XML_triangle: aSymbol.StandardSymbol = 3; break; // arrow up
516 case XML_x: aSymbol.StandardSymbol = 10; break; // X, legacy bow tie
517 case XML_star: aSymbol.StandardSymbol = 12; break; // asterisk, legacy sand glass
518 case XML_dot: aSymbol.StandardSymbol = 4; break; // arrow right
519 case XML_dash: aSymbol.StandardSymbol = 13; break; // horizontal bar, legacy arrow down
520 case XML_circle: aSymbol.StandardSymbol = 8; break; // circle, legacy arrow right
521 case XML_plus: aSymbol.StandardSymbol = 11; break; // plus, legacy arrow left
522 }
523
524 // symbol size (points in OOXML, 1/100 mm in Chart2)
525 sal_Int32 nSize = convertPointToMm100(nOoxSize);
526 aSymbol.Size.Width = aSymbol.Size.Height = nSize;
527
528 if(xShapeProps.is())
529 {
530 Color aFillColor = xShapeProps->getFillProperties().maFillColor;
531 aSymbol.FillColor = sal_Int32(aFillColor.getColor(getFilter().getGraphicHelper()));
532 // tdf#124817: if there is no fill color, use line color of the symbol
533 if( aSymbol.FillColor < 0 )
534 {
535 Color aLineColor = xShapeProps->getLineProperties().maLineFill.maFillColor;
536 aSymbol.BorderColor = sal_Int32(aLineColor.getColor(getFilter().getGraphicHelper()));
537 rPropSet.setProperty(PROP_Color, aSymbol.BorderColor);
538 }
539 else
540 rPropSet.setProperty(PROP_Color, aSymbol.FillColor);
541 }
542
543 // set the property
544 rPropSet.setProperty( PROP_Symbol, aSymbol );
545 }
546
convertLineSmooth(PropertySet & rPropSet,bool bOoxSmooth) const547 void TypeGroupConverter::convertLineSmooth( PropertySet& rPropSet, bool bOoxSmooth ) const
548 {
549 if( !isSeriesFrameFormat() && (maTypeInfo.meTypeCategory != TYPECATEGORY_RADAR) )
550 {
551 namespace cssc = css::chart2;
552 cssc::CurveStyle eCurveStyle = bOoxSmooth ? cssc::CurveStyle_CUBIC_SPLINES : cssc::CurveStyle_LINES;
553 rPropSet.setProperty( PROP_CurveStyle, eCurveStyle );
554 }
555 }
556
convertBarGeometry(PropertySet & rPropSet,sal_Int32 nOoxShape) const557 void TypeGroupConverter::convertBarGeometry( PropertySet& rPropSet, sal_Int32 nOoxShape ) const
558 {
559 if( !(mb3dChart && (maTypeInfo.meTypeCategory == TYPECATEGORY_BAR)) )
560 return;
561
562 namespace cssc = css::chart2;
563
564 sal_Int32 nGeom3d = cssc::DataPointGeometry3D::CUBOID;
565 switch( nOoxShape )
566 {
567 case XML_box: nGeom3d = cssc::DataPointGeometry3D::CUBOID; break;
568 case XML_cone: nGeom3d = cssc::DataPointGeometry3D::CONE; break;
569 case XML_coneToMax: nGeom3d = cssc::DataPointGeometry3D::CONE; break;
570 case XML_cylinder: nGeom3d = cssc::DataPointGeometry3D::CYLINDER; break;
571 case XML_pyramid: nGeom3d = cssc::DataPointGeometry3D::PYRAMID; break;
572 case XML_pyramidToMax: nGeom3d = cssc::DataPointGeometry3D::PYRAMID; break;
573 default: OSL_FAIL( "TypeGroupConverter::convertBarGeometry - unknown 3D bar shape type" );
574 }
575 rPropSet.setProperty( PROP_Geometry3D, nGeom3d );
576 }
577
convertPieRotation(PropertySet & rPropSet,sal_Int32 nOoxAngle) const578 void TypeGroupConverter::convertPieRotation( PropertySet& rPropSet, sal_Int32 nOoxAngle ) const
579 {
580 if( maTypeInfo.meTypeCategory == TYPECATEGORY_PIE )
581 {
582 // map OOXML [0,360] clockwise (0deg top) to Chart2 counterclockwise (0deg left)
583 sal_Int32 nAngle = (450 - nOoxAngle) % 360;
584 rPropSet.setProperty( PROP_StartingAngle, nAngle );
585 }
586 }
587
convertPieExplosion(PropertySet & rPropSet,sal_Int32 nOoxExplosion) const588 void TypeGroupConverter::convertPieExplosion( PropertySet& rPropSet, sal_Int32 nOoxExplosion ) const
589 {
590 if( maTypeInfo.meTypeCategory == TYPECATEGORY_PIE )
591 {
592 // pie explosion restricted to 100% in Chart2, set as double in range [0,1]
593 double fOffset = getLimitedValue< double >( nOoxExplosion / 100.0, 0.0, 1.0 );
594 rPropSet.setProperty( PROP_Offset, fOffset );
595 }
596 }
597
convertOfPieType(sal_Int32 nOoxOfPieType) const598 PieChartSubType TypeGroupConverter::convertOfPieType(sal_Int32 nOoxOfPieType ) const
599 {
600 if( maTypeInfo.meTypeCategory == TYPECATEGORY_PIE ) {
601 switch (nOoxOfPieType) {
602 case XML_pie:
603 return PieChartSubType_PIE;
604 break;
605 case XML_bar:
606 return PieChartSubType_BAR;
607 break;
608 default:
609 OSL_FAIL( "TypeGroupConverter::convertOfPieType - unknown of-pie type" );
610 return PieChartSubType_NONE;
611 }
612 } else {
613 return PieChartSubType_NONE;
614 }
615 }
616
moveDataToSeries(DataSourceCxModel::DataMap & raDataMap)617 void TypeGroupConverter::moveDataToSeries(DataSourceCxModel::DataMap& raDataMap)
618 {
619 // For chartex, move data from rDataMap to the appropriate series. In
620 // chartex the data is given outside the series, in a child element of
621 // <cx:chartSpace>. Pre-2016 charts have the data inside the series, and
622 // SeriesModel and subsequent handling reflects this. So here we get the
623 // data to the right place for processing.
624 if (!raDataMap.empty()) {
625 // should only happen for chartex
626 for (auto const& elem : mrModel.maSeries) {
627 // This ID must be present in the map
628 assert(raDataMap.find(elem->mnDataId) != raDataMap.end());
629 elem->maSources = *(raDataMap[elem->mnDataId]);
630 }
631 }
632 }
633
634 // private --------------------------------------------------------------------
635
insertDataSeries(const Reference<XChartType> & rxChartType,const Reference<XDataSeries> & rxSeries,sal_Int32 nAxesSetIdx)636 void TypeGroupConverter::insertDataSeries( const Reference< XChartType >& rxChartType, const Reference< XDataSeries >& rxSeries, sal_Int32 nAxesSetIdx )
637 {
638 if( !rxSeries.is() )
639 return;
640
641 PropertySet aSeriesProp( rxSeries );
642
643 // series stacking mode
644 namespace cssc = css::chart2;
645 cssc::StackingDirection eStacking = cssc::StackingDirection_NO_STACKING;
646 // stacked overrides deep-3d
647 if( isStacked() || isPercent() )
648 eStacking = cssc::StackingDirection_Y_STACKING;
649 else if( isDeep3dChart() )
650 eStacking = cssc::StackingDirection_Z_STACKING;
651 aSeriesProp.setProperty( PROP_StackingDirection, eStacking );
652
653 // additional series properties
654 aSeriesProp.setProperty( PROP_AttachedAxisIndex, nAxesSetIdx );
655
656 // insert series into container
657 try
658 {
659 Reference< XDataSeriesContainer > xSeriesCont( rxChartType, UNO_QUERY_THROW );
660 xSeriesCont->addDataSeries( rxSeries );
661 }
662 catch( Exception& )
663 {
664 OSL_FAIL( "TypeGroupConverter::insertDataSeries - cannot add data series" );
665 }
666 }
667
668 } // namespace oox
669
670 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
671