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/tokens.hxx>
22 #include <oox/core/xmlfilterbase.hxx>
23 #include <oox/export/chartexport.hxx>
24 #include <oox/token/relationship.hxx>
25 #include <oox/export/utils.hxx>
26 #include <drawingml/chart/typegroupconverter.hxx>
27
28 #include <cstdio>
29 #include <iterator>
30
31 #include <com/sun/star/awt/Gradient.hpp>
32 #include <com/sun/star/chart/XChartDocument.hpp>
33 #include <com/sun/star/chart/ChartLegendPosition.hpp>
34 #include <com/sun/star/chart/XTwoAxisXSupplier.hpp>
35 #include <com/sun/star/chart/XTwoAxisYSupplier.hpp>
36 #include <com/sun/star/chart/XAxisZSupplier.hpp>
37 #include <com/sun/star/chart/XChartDataArray.hpp>
38 #include <com/sun/star/chart/ChartDataRowSource.hpp>
39 #include <com/sun/star/chart/ChartAxisAssign.hpp>
40 #include <com/sun/star/chart/ChartSeriesAddress.hpp>
41 #include <com/sun/star/chart/X3DDisplay.hpp>
42 #include <com/sun/star/chart/XStatisticDisplay.hpp>
43 #include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp>
44 #include <com/sun/star/chart/ChartSymbolType.hpp>
45 #include <com/sun/star/chart/ChartAxisMarks.hpp>
46 #include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
47 #include <com/sun/star/chart/ChartAxisPosition.hpp>
48 #include <com/sun/star/chart/ChartSolidType.hpp>
49 #include <com/sun/star/chart/DataLabelPlacement.hpp>
50 #include <com/sun/star/chart/ErrorBarStyle.hpp>
51 #include <com/sun/star/chart/MissingValueTreatment.hpp>
52 #include <com/sun/star/chart/XDiagramPositioning.hpp>
53
54 #include <com/sun/star/chart2/RelativePosition.hpp>
55 #include <com/sun/star/chart2/RelativeSize.hpp>
56 #include <com/sun/star/chart2/XChartDocument.hpp>
57 #include <com/sun/star/chart2/XDiagram.hpp>
58 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
59 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
60 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
61 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
62 #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
63 #include <com/sun/star/chart2/DataPointLabel.hpp>
64 #include <com/sun/star/chart2/DataPointCustomLabelField.hpp>
65 #include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
66 #include <com/sun/star/chart2/Symbol.hpp>
67 #include <com/sun/star/chart2/data/XDataSource.hpp>
68 #include <com/sun/star/chart2/data/XDataSink.hpp>
69 #include <com/sun/star/chart2/data/XDataReceiver.hpp>
70 #include <com/sun/star/chart2/data/XDataProvider.hpp>
71 #include <com/sun/star/chart2/XInternalDataProvider.hpp>
72 #include <com/sun/star/chart2/data/XDatabaseDataProvider.hpp>
73 #include <com/sun/star/chart2/data/XRangeXMLConversion.hpp>
74 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
75 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
76 #include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
77 #include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
78
79 #include <com/sun/star/beans/XPropertySet.hpp>
80 #include <com/sun/star/drawing/XShape.hpp>
81 #include <com/sun/star/drawing/FillStyle.hpp>
82 #include <com/sun/star/drawing/LineStyle.hpp>
83 #include <com/sun/star/drawing/BitmapMode.hpp>
84 #include <com/sun/star/awt/XBitmap.hpp>
85 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
86 #include <com/sun/star/lang/XServiceName.hpp>
87
88 #include <com/sun/star/table/CellAddress.hpp>
89 #include <com/sun/star/sheet/XFormulaParser.hpp>
90 #include <com/sun/star/sheet/FormulaToken.hpp>
91 #include <com/sun/star/sheet/AddressConvention.hpp>
92
93 #include <com/sun/star/text/WritingMode.hpp>
94 #include <com/sun/star/container/XNamed.hpp>
95 #include <com/sun/star/embed/XVisualObject.hpp>
96 #include <com/sun/star/embed/Aspects.hpp>
97
98 #include <comphelper/processfactory.hxx>
99 #include <comphelper/random.hxx>
100 #include <utility>
101 #include <xmloff/SchXMLSeriesHelper.hxx>
102 #include "ColorPropertySet.hxx"
103
104 #include <svl/zforlist.hxx>
105 #include <svl/numuno.hxx>
106 #include <tools/diagnose_ex.h>
107 #include <sal/log.hxx>
108
109 #include <set>
110 #include <unordered_set>
111
112 #include <rtl/math.hxx>
113 #include <o3tl/temporary.hxx>
114
115 using namespace css;
116 using namespace css::uno;
117 using namespace css::drawing;
118 using namespace ::oox::core;
119 using css::beans::PropertyValue;
120 using css::beans::XPropertySet;
121 using css::container::XNamed;
122 using css::table::CellAddress;
123 using css::sheet::XFormulaParser;
124 using ::oox::core::XmlFilterBase;
125 using ::sax_fastparser::FSHelperPtr;
126
127 namespace cssc = css::chart;
128
129 namespace oox { namespace drawingml {
130
131 namespace {
132
isPrimaryAxes(sal_Int32 nIndex)133 bool isPrimaryAxes(sal_Int32 nIndex)
134 {
135 assert(nIndex == 0 || nIndex == 1);
136 return nIndex != 1;
137 }
138
139 }
140
141 class lcl_MatchesRole
142 {
143 public:
lcl_MatchesRole(const OUString & aRole)144 explicit lcl_MatchesRole( const OUString & aRole ) :
145 m_aRole( aRole )
146 {}
147
operator ()(const Reference<chart2::data::XLabeledDataSequence> & xSeq) const148 bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const
149 {
150 if( !xSeq.is() )
151 return false;
152 Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY );
153 OUString aRole;
154
155 return ( xProp.is() &&
156 (xProp->getPropertyValue( "Role" ) >>= aRole ) &&
157 m_aRole == aRole );
158 }
159
160 private:
161 OUString const m_aRole;
162 };
163
lcl_getCategories(const Reference<chart2::XDiagram> & xDiagram)164 static Reference< chart2::data::XLabeledDataSequence > lcl_getCategories( const Reference< chart2::XDiagram > & xDiagram )
165 {
166 Reference< chart2::data::XLabeledDataSequence > xResult;
167 try
168 {
169 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
170 xDiagram, uno::UNO_QUERY_THROW );
171 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
172 xCooSysCnt->getCoordinateSystems());
173 for( const auto& xCooSys : aCooSysSeq )
174 {
175 OSL_ASSERT( xCooSys.is());
176 for( sal_Int32 nN = xCooSys->getDimension(); nN--; )
177 {
178 const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nN);
179 for(sal_Int32 nI=0; nI<=nMaxAxisIndex; ++nI)
180 {
181 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( nN, nI );
182 OSL_ASSERT( xAxis.is());
183 if( xAxis.is())
184 {
185 chart2::ScaleData aScaleData = xAxis->getScaleData();
186 if( aScaleData.Categories.is())
187 {
188 xResult.set( aScaleData.Categories );
189 break;
190 }
191 }
192 }
193 }
194 }
195 }
196 catch( const uno::Exception & )
197 {
198 DBG_UNHANDLED_EXCEPTION("oox");
199 }
200
201 return xResult;
202 }
203
204 static Reference< chart2::data::XLabeledDataSequence >
lcl_getDataSequenceByRole(const Sequence<Reference<chart2::data::XLabeledDataSequence>> & aLabeledSeq,const OUString & rRole)205 lcl_getDataSequenceByRole(
206 const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aLabeledSeq,
207 const OUString & rRole )
208 {
209 Reference< chart2::data::XLabeledDataSequence > aNoResult;
210
211 const Reference< chart2::data::XLabeledDataSequence > * pBegin = aLabeledSeq.getConstArray();
212 const Reference< chart2::data::XLabeledDataSequence > * pEnd = pBegin + aLabeledSeq.getLength();
213 const Reference< chart2::data::XLabeledDataSequence > * pMatch =
214 ::std::find_if( pBegin, pEnd, lcl_MatchesRole( rRole ));
215
216 if( pMatch != pEnd )
217 return *pMatch;
218
219 return aNoResult;
220 }
221
lcl_hasCategoryLabels(const Reference<chart2::XChartDocument> & xChartDoc)222 static bool lcl_hasCategoryLabels( const Reference< chart2::XChartDocument >& xChartDoc )
223 {
224 //categories are always the first sequence
225 Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram());
226 Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xDiagram ) );
227 return xCategories.is();
228 }
229
lcl_isCategoryAxisShifted(const Reference<chart2::XChartDocument> & xChartDoc)230 static bool lcl_isCategoryAxisShifted(const Reference< chart2::XChartDocument >& xChartDoc)
231 {
232 Reference< chart2::XDiagram > xDiagram(xChartDoc->getFirstDiagram());
233 bool isCategoryPositionShifted = false;
234
235 try
236 {
237 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
238 xDiagram, uno::UNO_QUERY_THROW);
239 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
240 xCooSysCnt->getCoordinateSystems());
241 for( const auto& xCooSys : aCooSysSeq )
242 {
243 OSL_ASSERT(xCooSys.is());
244 for( sal_Int32 nN = xCooSys->getDimension(); nN--; )
245 {
246 const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nN);
247 for( sal_Int32 nI = 0; nI <= nMaxAxisIndex; ++nI )
248 {
249 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(nN, nI);
250 OSL_ASSERT(xAxis.is());
251 if( xAxis.is())
252 {
253 chart2::ScaleData aScaleData = xAxis->getScaleData();
254 if( aScaleData.AxisType == AXIS_PRIMARY_Y )
255 {
256 isCategoryPositionShifted = aScaleData.ShiftedCategoryPosition;
257 break;
258 }
259 }
260 }
261 }
262 }
263 }
264 catch (const uno::Exception &)
265 {
266 DBG_UNHANDLED_EXCEPTION("oox");
267 }
268
269 return isCategoryPositionShifted;
270 }
271
lcl_isSeriesAttachedToFirstAxis(const Reference<chart2::XDataSeries> & xDataSeries)272 static bool lcl_isSeriesAttachedToFirstAxis(
273 const Reference< chart2::XDataSeries > & xDataSeries )
274 {
275 bool bResult=true;
276
277 try
278 {
279 sal_Int32 nAxisIndex = 0;
280 Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW );
281 xProp->getPropertyValue("AttachedAxisIndex") >>= nAxisIndex;
282 bResult = (0==nAxisIndex);
283 }
284 catch( const uno::Exception & )
285 {
286 DBG_UNHANDLED_EXCEPTION("oox");
287 }
288
289 return bResult;
290 }
291
lcl_flattenStringSequence(const Sequence<OUString> & rSequence)292 static OUString lcl_flattenStringSequence( const Sequence< OUString > & rSequence )
293 {
294 OUStringBuffer aResult;
295 bool bPrecedeWithSpace = false;
296 for( const auto& rString : rSequence )
297 {
298 if( !rString.isEmpty())
299 {
300 if( bPrecedeWithSpace )
301 aResult.append( ' ' );
302 aResult.append( rString );
303 bPrecedeWithSpace = true;
304 }
305 }
306 return aResult.makeStringAndClear();
307 }
308
lcl_getLabelSequence(const Reference<chart2::data::XDataSequence> & xLabelSeq)309 static Sequence< OUString > lcl_getLabelSequence( const Reference< chart2::data::XDataSequence > & xLabelSeq )
310 {
311 Sequence< OUString > aLabels;
312
313 uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xLabelSeq, uno::UNO_QUERY );
314 if( xTextualDataSequence.is())
315 {
316 aLabels = xTextualDataSequence->getTextualData();
317 }
318 else if( xLabelSeq.is())
319 {
320 const Sequence< uno::Any > aAnies( xLabelSeq->getData());
321 aLabels.realloc( aAnies.getLength());
322 for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
323 aAnies[i] >>= aLabels[i];
324 }
325
326 return aLabels;
327 }
328
lcl_fillCategoriesIntoStringVector(const Reference<chart2::data::XDataSequence> & xCategories,::std::vector<OUString> & rOutCategories)329 static void lcl_fillCategoriesIntoStringVector(
330 const Reference< chart2::data::XDataSequence > & xCategories,
331 ::std::vector< OUString > & rOutCategories )
332 {
333 OSL_ASSERT( xCategories.is());
334 if( !xCategories.is())
335 return;
336 Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xCategories, uno::UNO_QUERY );
337 if( xTextualDataSequence.is())
338 {
339 rOutCategories.clear();
340 Sequence< OUString > aTextData( xTextualDataSequence->getTextualData());
341 ::std::copy( aTextData.begin(), aTextData.end(),
342 ::std::back_inserter( rOutCategories ));
343 }
344 else
345 {
346 Sequence< uno::Any > aAnies( xCategories->getData());
347 rOutCategories.resize( aAnies.getLength());
348 for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
349 aAnies[i] >>= rOutCategories[i];
350 }
351 }
352
lcl_getAllValuesFromSequence(const Reference<chart2::data::XDataSequence> & xSeq)353 static ::std::vector< double > lcl_getAllValuesFromSequence( const Reference< chart2::data::XDataSequence > & xSeq )
354 {
355 double fNan = 0.0;
356 ::rtl::math::setNan( &fNan );
357 ::std::vector< double > aResult;
358
359 Reference< chart2::data::XNumericalDataSequence > xNumSeq( xSeq, uno::UNO_QUERY );
360 if( xNumSeq.is())
361 {
362 Sequence< double > aValues( xNumSeq->getNumericalData());
363 ::std::copy( aValues.begin(), aValues.end(),
364 ::std::back_inserter( aResult ));
365 }
366 else if( xSeq.is())
367 {
368 Sequence< uno::Any > aAnies( xSeq->getData());
369 aResult.resize( aAnies.getLength(), fNan );
370 for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
371 aAnies[i] >>= aResult[i];
372 }
373 return aResult;
374 }
375
lcl_getChartType(const OUString & sChartType)376 static sal_Int32 lcl_getChartType( const OUString& sChartType )
377 {
378 chart::TypeId eChartTypeId = chart::TYPEID_UNKNOWN;
379 if( sChartType == "com.sun.star.chart.BarDiagram"
380 || sChartType == "com.sun.star.chart2.ColumnChartType" )
381 eChartTypeId = chart::TYPEID_BAR;
382 else if( sChartType == "com.sun.star.chart.AreaDiagram"
383 || sChartType == "com.sun.star.chart2.AreaChartType" )
384 eChartTypeId = chart::TYPEID_AREA;
385 else if( sChartType == "com.sun.star.chart.LineDiagram"
386 || sChartType == "com.sun.star.chart2.LineChartType" )
387 eChartTypeId = chart::TYPEID_LINE;
388 else if( sChartType == "com.sun.star.chart.PieDiagram"
389 || sChartType == "com.sun.star.chart2.PieChartType" )
390 eChartTypeId = chart::TYPEID_PIE;
391 else if( sChartType == "com.sun.star.chart.DonutDiagram"
392 || sChartType == "com.sun.star.chart2.DonutChartType" )
393 eChartTypeId = chart::TYPEID_DOUGHNUT;
394 else if( sChartType == "com.sun.star.chart.XYDiagram"
395 || sChartType == "com.sun.star.chart2.ScatterChartType" )
396 eChartTypeId = chart::TYPEID_SCATTER;
397 else if( sChartType == "com.sun.star.chart.NetDiagram"
398 || sChartType == "com.sun.star.chart2.NetChartType" )
399 eChartTypeId = chart::TYPEID_RADARLINE;
400 else if( sChartType == "com.sun.star.chart.FilledNetDiagram"
401 || sChartType == "com.sun.star.chart2.FilledNetChartType" )
402 eChartTypeId = chart::TYPEID_RADARAREA;
403 else if( sChartType == "com.sun.star.chart.StockDiagram"
404 || sChartType == "com.sun.star.chart2.CandleStickChartType" )
405 eChartTypeId = chart::TYPEID_STOCK;
406 else if( sChartType == "com.sun.star.chart.BubbleDiagram"
407 || sChartType == "com.sun.star.chart2.BubbleChartType" )
408 eChartTypeId = chart::TYPEID_BUBBLE;
409
410 return eChartTypeId;
411 }
412
lcl_generateRandomValue()413 static sal_Int32 lcl_generateRandomValue()
414 {
415 return comphelper::rng::uniform_int_distribution(0, 100000000-1);
416 }
417
ChartExport(sal_Int32 nXmlNamespace,FSHelperPtr pFS,Reference<frame::XModel> const & xModel,XmlFilterBase * pFB,DocumentType eDocumentType)418 ChartExport::ChartExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, Reference< frame::XModel > const & xModel, XmlFilterBase* pFB, DocumentType eDocumentType )
419 : DrawingML( std::move(pFS), pFB, eDocumentType )
420 , mnXmlNamespace( nXmlNamespace )
421 , mnSeriesCount(0)
422 , mxChartModel( xModel )
423 , mpURLTransformer(new URLTransformer)
424 , mbHasCategoryLabels( false )
425 , mbIsCategoryPositionShifted( false )
426 , mbHasZAxis( false )
427 , mbIs3DChart( false )
428 , mbStacked(false)
429 , mbPercent(false)
430 {
431 }
432
SetURLTranslator(const std::shared_ptr<URLTransformer> & pTransformer)433 void ChartExport::SetURLTranslator(const std::shared_ptr<URLTransformer>& pTransformer)
434 {
435 mpURLTransformer = pTransformer;
436 }
437
getChartType()438 sal_Int32 ChartExport::getChartType( )
439 {
440 OUString sChartType = mxDiagram->getDiagramType();
441 return lcl_getChartType( sChartType );
442 }
443
444 namespace {
445
createArguments(const OUString & rRangeRepresentation,bool bUseColumns)446 uno::Sequence< beans::PropertyValue > createArguments(
447 const OUString & rRangeRepresentation, bool bUseColumns)
448 {
449 css::chart::ChartDataRowSource eRowSource = css::chart::ChartDataRowSource_ROWS;
450 if (bUseColumns)
451 eRowSource = css::chart::ChartDataRowSource_COLUMNS;
452
453 uno::Sequence< beans::PropertyValue > aArguments(4);
454 aArguments[0] = beans::PropertyValue("DataRowSource"
455 , -1, uno::Any(eRowSource)
456 , beans::PropertyState_DIRECT_VALUE);
457 aArguments[1] = beans::PropertyValue("FirstCellAsLabel"
458 , -1, uno::Any(false)
459 , beans::PropertyState_DIRECT_VALUE);
460 aArguments[2] = beans::PropertyValue("HasCategories"
461 , -1, uno::Any(false)
462 , beans::PropertyState_DIRECT_VALUE);
463 aArguments[3] = beans::PropertyValue("CellRangeRepresentation"
464 , -1, uno::Any(rRangeRepresentation)
465 , beans::PropertyState_DIRECT_VALUE);
466
467 return aArguments;
468 }
469
getPrimaryDataSeries(const Reference<chart2::XChartType> & xChartType)470 Reference<chart2::XDataSeries> getPrimaryDataSeries(const Reference<chart2::XChartType>& xChartType)
471 {
472 Reference< chart2::XDataSeriesContainer > xDSCnt(xChartType, uno::UNO_QUERY_THROW);
473
474 // export dataseries for current chart-type
475 const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq(xDSCnt->getDataSeries());
476 for (const auto& rSeries : aSeriesSeq)
477 {
478 Reference<chart2::XDataSeries> xSource(rSeries, uno::UNO_QUERY);
479 if (xSource.is())
480 return xSource;
481 }
482
483 return Reference<chart2::XDataSeries>();
484 }
485
486 }
487
getSplitCategoriesList(const OUString & rRange)488 Sequence< Sequence< OUString > > ChartExport::getSplitCategoriesList( const OUString& rRange )
489 {
490 Reference< chart2::XChartDocument > xChartDoc(getModel(), uno::UNO_QUERY);
491 OSL_ASSERT(xChartDoc.is());
492 if (xChartDoc.is())
493 {
494 Reference< chart2::data::XDataProvider > xDataProvider(xChartDoc->getDataProvider());
495 OSL_ENSURE(xDataProvider.is(), "No DataProvider");
496 if (xDataProvider.is())
497 {
498 //detect whether the first series is a row or a column
499 bool bSeriesUsesColumns = true;
500 Reference< chart2::XDiagram > xDiagram(xChartDoc->getFirstDiagram());
501 try
502 {
503 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(xDiagram, uno::UNO_QUERY_THROW);
504 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(xCooSysCnt->getCoordinateSystems());
505 for (const auto& rCooSys : aCooSysSeq)
506 {
507 const Reference< chart2::XChartTypeContainer > xCTCnt(rCooSys, uno::UNO_QUERY_THROW);
508 const Sequence< Reference< chart2::XChartType > > aChartTypeSeq(xCTCnt->getChartTypes());
509 for (const auto& rChartType : aChartTypeSeq)
510 {
511 Reference< chart2::XDataSeries > xDataSeries = getPrimaryDataSeries(rChartType);
512 if (xDataSeries.is())
513 {
514 uno::Reference< chart2::data::XDataSource > xSeriesSource(xDataSeries, uno::UNO_QUERY);
515 const uno::Sequence< beans::PropertyValue > rArguments = xDataProvider->detectArguments(xSeriesSource);
516 for (const beans::PropertyValue& rProperty : rArguments)
517 {
518 if (rProperty.Name == "DataRowSource")
519 {
520 css::chart::ChartDataRowSource eRowSource;
521 if (rProperty.Value >>= eRowSource)
522 {
523 bSeriesUsesColumns = (eRowSource == css::chart::ChartDataRowSource_COLUMNS);
524 break;
525 }
526 }
527 }
528 }
529 }
530 }
531 }
532 catch (const uno::Exception &)
533 {
534 DBG_UNHANDLED_EXCEPTION("chart2");
535 }
536 // detect we have an inner data table or not
537 if (xChartDoc->hasInternalDataProvider() && rRange == "categories")
538 {
539 try
540 {
541 css::uno::Reference< css::chart2::XAnyDescriptionAccess > xDataAccess(xChartDoc->getDataProvider(), uno::UNO_QUERY);
542 const Sequence< Sequence< uno::Any > >aAnyCategories(bSeriesUsesColumns ? xDataAccess->getAnyRowDescriptions() : xDataAccess->getAnyColumnDescriptions());
543 auto pMax = std::max_element(aAnyCategories.begin(), aAnyCategories.end(),
544 [](const Sequence<uno::Any>& a, const Sequence<uno::Any>& b) {
545 return a.getLength() < b.getLength(); });
546
547 //minimum is 1!
548 if (pMax != aAnyCategories.end() && pMax->getLength() > 1)
549 {
550 sal_Int32 nLevelCount = pMax->getLength();
551 //we have complex categories
552 //sort the categories name
553 Sequence<Sequence<OUString>>aFinalSplitSource(nLevelCount);
554 for (sal_Int32 i = 0; i < nLevelCount; i++)
555 {
556 sal_Int32 nElemLabel = 0;
557 aFinalSplitSource[nLevelCount - i - 1].realloc(aAnyCategories.getLength());
558 for (auto const& elemLabel : aAnyCategories)
559 {
560 // make sure elemLabel[i] exists!
561 if (elemLabel.getLength() > i)
562 {
563 aFinalSplitSource[nLevelCount - i - 1][nElemLabel] = elemLabel[i].get<OUString>();
564 nElemLabel++;
565 }
566 }
567 }
568 return aFinalSplitSource;
569 }
570 }
571 catch (const uno::Exception &)
572 {
573 DBG_UNHANDLED_EXCEPTION("oox");
574 }
575 }
576 else
577 {
578 try
579 {
580 uno::Reference< chart2::data::XDataSource > xCategoriesSource(xDataProvider->createDataSource(
581 createArguments(rRange, bSeriesUsesColumns)));
582
583 if (xCategoriesSource.is())
584 {
585 const Sequence< Reference< chart2::data::XLabeledDataSequence >> aCategories = xCategoriesSource->getDataSequences();
586 if (aCategories.getLength() > 1)
587 {
588 //we have complex categories
589 //sort the categories name
590 Sequence<Sequence<OUString>> aFinalSplitSource(aCategories.getLength());
591 std::transform(aCategories.begin(), aCategories.end(),
592 std::reverse_iterator(aFinalSplitSource.end()),
593 [](const Reference<chart2::data::XLabeledDataSequence>& xCat) {
594 return lcl_getLabelSequence(xCat->getValues()); });
595 return aFinalSplitSource;
596 }
597 }
598 }
599 catch (const uno::Exception &)
600 {
601 DBG_UNHANDLED_EXCEPTION("oox");
602 }
603 }
604 }
605 }
606
607 return Sequence< Sequence< OUString>>(0);
608 }
609
parseFormula(const OUString & rRange)610 OUString ChartExport::parseFormula( const OUString& rRange )
611 {
612 OUString aResult;
613 Reference< XFormulaParser > xParser;
614 uno::Reference< lang::XMultiServiceFactory > xSF = GetFB()->getModelFactory();
615 if( xSF.is() )
616 {
617 try
618 {
619 xParser.set( xSF->createInstance("com.sun.star.sheet.FormulaParser"), UNO_QUERY );
620 }
621 catch( Exception& )
622 {
623 }
624 }
625
626 SAL_WARN_IF(!xParser.is(), "oox", "creating formula parser failed");
627
628 if( xParser.is() )
629 {
630 Reference< XPropertySet > xParserProps( xParser, uno::UNO_QUERY );
631 // rRange is the result of a
632 // css::chart2::data::XDataSequence::getSourceRangeRepresentation()
633 // call that returns the range in the document's current UI notation.
634 // Creating a FormulaParser defaults to the same notation, for
635 // parseFormula() do not attempt to override the FormulaConvention
636 // property with css::sheet::AddressConvention::OOO or some such.
637 /* TODO: it would be much better to introduce a
638 * getSourceRangeRepresentation(css::sheet::AddressConvention) to
639 * return the ranges in a specific convention than converting them with
640 * the overhead of creating an XFormulaParser for each... */
641 uno::Sequence<sheet::FormulaToken> aTokens = xParser->parseFormula( rRange, CellAddress( 0, 0, 0 ) );
642 if( xParserProps.is() )
643 {
644 xParserProps->setPropertyValue("FormulaConvention", uno::makeAny(css::sheet::AddressConvention::XL_OOX) );
645 }
646 aResult = xParser->printFormula( aTokens, CellAddress( 0, 0, 0 ) );
647 }
648 else
649 {
650 //FIXME: currently just using simple converter, e.g $Sheet1.$A$1:$C$1 -> Sheet1!$A$1:$C$1
651 OUString aRange( rRange );
652 if( aRange.startsWith("$") )
653 aRange = aRange.copy(1);
654 aRange = aRange.replaceAll(".$", "!$" );
655 aResult = aRange;
656 }
657
658 return aResult;
659 }
660
WriteChartObj(const Reference<XShape> & xShape,sal_Int32 nID,sal_Int32 nChartCount)661 void ChartExport::WriteChartObj( const Reference< XShape >& xShape, sal_Int32 nID, sal_Int32 nChartCount )
662 {
663 FSHelperPtr pFS = GetFS();
664
665 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
666
667 pFS->startElementNS(mnXmlNamespace, XML_graphicFrame);
668
669 pFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr);
670
671 // TODO: get the correct chart name chart id
672 OUString sName = "Object 1";
673 Reference< XNamed > xNamed( xShape, UNO_QUERY );
674 if (xNamed.is())
675 sName = xNamed->getName();
676
677 pFS->startElementNS( mnXmlNamespace, XML_cNvPr,
678 XML_id, OString::number(nID),
679 XML_name, sName.toUtf8());
680
681 OUString sURL;
682 if ( GetProperty( xShapeProps, "URL" ) )
683 mAny >>= sURL;
684 if( !sURL.isEmpty() )
685 {
686 OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
687 oox::getRelationship(Relationship::HYPERLINK),
688 mpURLTransformer->getTransformedString(sURL),
689 mpURLTransformer->isExternalURL(sURL));
690
691 mpFS->singleElementNS( XML_a, XML_hlinkClick,
692 FSNS( XML_r,XML_id ), sRelId.toUtf8() );
693 }
694 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
695
696 pFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr);
697
698 if( GetDocumentType() == DOCUMENT_PPTX )
699 pFS->singleElementNS(mnXmlNamespace, XML_nvPr);
700 pFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
701
702 // visual chart properties
703 WriteShapeTransformation( xShape, mnXmlNamespace );
704
705 // writer chart object
706 pFS->startElement(FSNS(XML_a, XML_graphic));
707 pFS->startElement( FSNS( XML_a, XML_graphicData ),
708 XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/chart" );
709 OUString sId;
710 const char* sFullPath = nullptr;
711 const char* sRelativePath = nullptr;
712 switch( GetDocumentType() )
713 {
714 case DOCUMENT_DOCX:
715 {
716 sFullPath = "word/charts/chart";
717 sRelativePath = "charts/chart";
718 break;
719 }
720 case DOCUMENT_PPTX:
721 {
722 sFullPath = "ppt/charts/chart";
723 sRelativePath = "../charts/chart";
724 break;
725 }
726 case DOCUMENT_XLSX:
727 {
728 sFullPath = "xl/charts/chart";
729 sRelativePath = "../charts/chart";
730 break;
731 }
732 default:
733 {
734 sFullPath = "charts/chart";
735 sRelativePath = "charts/chart";
736 break;
737 }
738 }
739 OUString sFullStream = OUStringBuffer()
740 .appendAscii(sFullPath)
741 .append(nChartCount)
742 .append( ".xml" )
743 .makeStringAndClear();
744 OUString sRelativeStream = OUStringBuffer()
745 .appendAscii(sRelativePath)
746 .append(nChartCount)
747 .append( ".xml" )
748 .makeStringAndClear();
749 FSHelperPtr pChart = CreateOutputStream(
750 sFullStream,
751 sRelativeStream,
752 pFS->getOutputStream(),
753 "application/vnd.openxmlformats-officedocument.drawingml.chart+xml",
754 OUStringToOString(oox::getRelationship(Relationship::CHART), RTL_TEXTENCODING_UTF8).getStr(),
755 &sId );
756
757 XmlFilterBase* pFB = GetFB();
758 pFS->singleElement( FSNS( XML_c, XML_chart ),
759 FSNS(XML_xmlns, XML_c), pFB->getNamespaceURL(OOX_NS(dmlChart)).toUtf8(),
760 FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
761 FSNS(XML_r, XML_id), sId.toUtf8() );
762
763 pFS->endElement( FSNS( XML_a, XML_graphicData ) );
764 pFS->endElement( FSNS( XML_a, XML_graphic ) );
765 pFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
766
767 SetFS( pChart );
768 ExportContent();
769 }
770
InitRangeSegmentationProperties(const Reference<chart2::XChartDocument> & xChartDoc)771 void ChartExport::InitRangeSegmentationProperties( const Reference< chart2::XChartDocument > & xChartDoc )
772 {
773 if( xChartDoc.is())
774 try
775 {
776 Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
777 OSL_ENSURE( xDataProvider.is(), "No DataProvider" );
778 if( xDataProvider.is())
779 {
780 mbHasCategoryLabels = lcl_hasCategoryLabels( xChartDoc );
781 mbIsCategoryPositionShifted = lcl_isCategoryAxisShifted( xChartDoc );
782 }
783 }
784 catch( const uno::Exception & )
785 {
786 DBG_UNHANDLED_EXCEPTION("oox");
787 }
788 }
789
ExportContent()790 void ChartExport::ExportContent()
791 {
792 Reference< chart2::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
793 OSL_ASSERT( xChartDoc.is() );
794 if( !xChartDoc.is() )
795 return;
796 InitRangeSegmentationProperties( xChartDoc );
797 // TODO: export chart
798 ExportContent_( );
799 }
800
ExportContent_()801 void ChartExport::ExportContent_()
802 {
803 Reference< css::chart::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
804 if( xChartDoc.is())
805 {
806 // determine if data comes from the outside
807 bool bIncludeTable = true;
808
809 Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
810 if( xNewDoc.is())
811 {
812 // check if we have own data. If so we must not export the complete
813 // range string, as this is our only indicator for having own or
814 // external data. @todo: fix this in the file format!
815 Reference< lang::XServiceInfo > xDPServiceInfo( xNewDoc->getDataProvider(), uno::UNO_QUERY );
816 if( ! (xDPServiceInfo.is() && xDPServiceInfo->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" ))
817 {
818 bIncludeTable = false;
819 }
820 }
821 exportChartSpace( xChartDoc, bIncludeTable );
822 }
823 else
824 {
825 OSL_FAIL( "Couldn't export chart due to wrong XModel" );
826 }
827 }
828
exportChartSpace(const Reference<css::chart::XChartDocument> & xChartDoc,bool bIncludeTable)829 void ChartExport::exportChartSpace( const Reference< css::chart::XChartDocument >& xChartDoc,
830 bool bIncludeTable )
831 {
832 FSHelperPtr pFS = GetFS();
833 XmlFilterBase* pFB = GetFB();
834 pFS->startElement( FSNS( XML_c, XML_chartSpace ),
835 FSNS( XML_xmlns, XML_c ), pFB->getNamespaceURL(OOX_NS(dmlChart)).toUtf8(),
836 FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)).toUtf8(),
837 FSNS( XML_xmlns, XML_r ), pFB->getNamespaceURL(OOX_NS(officeRel)).toUtf8());
838 // TODO: get the correct editing language
839 pFS->singleElement(FSNS(XML_c, XML_lang), XML_val, "en-US");
840
841 pFS->singleElement(FSNS(XML_c, XML_roundedCorners), XML_val, "0");
842
843 if( !bIncludeTable )
844 {
845 // TODO:external data
846 }
847 //XML_chart
848 exportChart(xChartDoc);
849
850 // TODO: printSettings
851 // TODO: style
852 // TODO: text properties
853 // TODO: shape properties
854 Reference< XPropertySet > xPropSet = xChartDoc->getArea();
855 if( xPropSet.is() )
856 exportShapeProps( xPropSet );
857
858 //XML_externalData
859 exportExternalData(xChartDoc);
860
861 pFS->endElement( FSNS( XML_c, XML_chartSpace ) );
862 }
863
exportExternalData(const Reference<css::chart::XChartDocument> & xChartDoc)864 void ChartExport::exportExternalData( const Reference< css::chart::XChartDocument >& xChartDoc )
865 {
866 // Embedded external data is grab bagged for docx file hence adding export part of
867 // external data for docx files only.
868 if(GetDocumentType() != DOCUMENT_DOCX)
869 return;
870
871 OUString externalDataPath;
872 Reference< beans::XPropertySet > xDocPropSet( xChartDoc->getDiagram(), uno::UNO_QUERY );
873 if( xDocPropSet.is())
874 {
875 try
876 {
877 Any aAny( xDocPropSet->getPropertyValue( "ExternalData" ));
878 aAny >>= externalDataPath;
879 }
880 catch( beans::UnknownPropertyException & )
881 {
882 SAL_WARN("oox", "Required property not found in ChartDocument");
883 }
884 }
885 if(!externalDataPath.isEmpty())
886 {
887 // Here adding external data entry to relationship.
888 OUString relationPath = externalDataPath;
889 // Converting absolute path to relative path.
890 if( externalDataPath[ 0 ] != '.' && externalDataPath[ 1 ] != '.')
891 {
892 sal_Int32 nSepPos = externalDataPath.indexOf( '/', 0 );
893 if( nSepPos > 0)
894 {
895 relationPath = relationPath.copy( nSepPos, ::std::max< sal_Int32 >( externalDataPath.getLength(), 0 ) - nSepPos );
896 relationPath = ".." + relationPath;
897 }
898 }
899 FSHelperPtr pFS = GetFS();
900 OUString type = oox::getRelationship(Relationship::PACKAGE);
901 if (relationPath.endsWith(".bin"))
902 type = oox::getRelationship(Relationship::OLEOBJECT);
903
904 OUString sRelId = GetFB()->addRelation(pFS->getOutputStream(),
905 type,
906 relationPath);
907 pFS->singleElementNS(XML_c, XML_externalData, FSNS(XML_r, XML_id), sRelId.toUtf8());
908 }
909 }
910
exportChart(const Reference<css::chart::XChartDocument> & xChartDoc)911 void ChartExport::exportChart( const Reference< css::chart::XChartDocument >& xChartDoc )
912 {
913 Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
914 mxDiagram.set( xChartDoc->getDiagram() );
915 if( xNewDoc.is())
916 mxNewDiagram.set( xNewDoc->getFirstDiagram());
917
918 // get Properties of ChartDocument
919 bool bHasMainTitle = false;
920 OUString aSubTitle;
921 bool bHasLegend = false;
922 Reference< beans::XPropertySet > xDocPropSet( xChartDoc, uno::UNO_QUERY );
923 if( xDocPropSet.is())
924 {
925 try
926 {
927 Any aAny( xDocPropSet->getPropertyValue("HasMainTitle"));
928 aAny >>= bHasMainTitle;
929 aAny = xDocPropSet->getPropertyValue("HasLegend");
930 aAny >>= bHasLegend;
931 }
932 catch( beans::UnknownPropertyException & )
933 {
934 SAL_WARN("oox", "Required property not found in ChartDocument");
935 }
936 } // if( xDocPropSet.is())
937
938 Reference< beans::XPropertySet > xPropSubTitle( xChartDoc->getSubTitle(), UNO_QUERY );
939 if( xPropSubTitle.is())
940 {
941 try
942 {
943 xPropSubTitle->getPropertyValue("String") >>= aSubTitle;
944 }
945 catch( beans::UnknownPropertyException & )
946 {
947 }
948 }
949
950 // chart element
951 FSHelperPtr pFS = GetFS();
952 pFS->startElement(FSNS(XML_c, XML_chart));
953
954 // titles
955 if( bHasMainTitle )
956 {
957 exportTitle( xChartDoc->getTitle(), !aSubTitle.isEmpty() ? &aSubTitle : nullptr );
958 pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
959 }
960 else if( !aSubTitle.isEmpty() )
961 {
962 exportTitle( xChartDoc->getSubTitle(), nullptr );
963 pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
964 }
965 else
966 {
967 pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "1");
968 }
969
970 InitPlotArea( );
971 if( mbIs3DChart )
972 {
973 exportView3D();
974
975 // floor
976 Reference< beans::XPropertySet > xFloor = mxNewDiagram->getFloor();
977 if( xFloor.is() )
978 {
979 pFS->startElement(FSNS(XML_c, XML_floor));
980 exportShapeProps( xFloor );
981 pFS->endElement( FSNS( XML_c, XML_floor ) );
982 }
983
984 // LibreOffice doesn't distinguish between sideWall and backWall (both are using the same color).
985 // It is controlled by the same Wall property.
986 Reference< beans::XPropertySet > xWall = mxNewDiagram->getWall();
987 if( xWall.is() )
988 {
989 // sideWall
990 pFS->startElement(FSNS(XML_c, XML_sideWall));
991 exportShapeProps( xWall );
992 pFS->endElement( FSNS( XML_c, XML_sideWall ) );
993
994 // backWall
995 pFS->startElement(FSNS(XML_c, XML_backWall));
996 exportShapeProps( xWall );
997 pFS->endElement( FSNS( XML_c, XML_backWall ) );
998 }
999
1000 }
1001 // plot area
1002 exportPlotArea( xChartDoc );
1003 // legend
1004 if( bHasLegend )
1005 exportLegend( xChartDoc );
1006
1007 uno::Reference<beans::XPropertySet> xDiagramPropSet(xChartDoc->getDiagram(), uno::UNO_QUERY);
1008 uno::Any aPlotVisOnly = xDiagramPropSet->getPropertyValue("IncludeHiddenCells");
1009 bool bIncludeHiddenCells = false;
1010 aPlotVisOnly >>= bIncludeHiddenCells;
1011 pFS->singleElement(FSNS(XML_c, XML_plotVisOnly), XML_val, ToPsz10(!bIncludeHiddenCells));
1012
1013 exportMissingValueTreatment(Reference<beans::XPropertySet>(mxDiagram, uno::UNO_QUERY));
1014
1015 pFS->endElement( FSNS( XML_c, XML_chart ) );
1016 }
1017
exportMissingValueTreatment(const uno::Reference<beans::XPropertySet> & xPropSet)1018 void ChartExport::exportMissingValueTreatment(const uno::Reference<beans::XPropertySet>& xPropSet)
1019 {
1020 if (!xPropSet.is())
1021 return;
1022
1023 sal_Int32 nVal = 0;
1024 uno::Any aAny = xPropSet->getPropertyValue("MissingValueTreatment");
1025 if (!(aAny >>= nVal))
1026 return;
1027
1028 const char* pVal = nullptr;
1029 switch (nVal)
1030 {
1031 case cssc::MissingValueTreatment::LEAVE_GAP:
1032 pVal = "gap";
1033 break;
1034 case cssc::MissingValueTreatment::USE_ZERO:
1035 pVal = "zero";
1036 break;
1037 case cssc::MissingValueTreatment::CONTINUE:
1038 pVal = "span";
1039 break;
1040 default:
1041 SAL_WARN("oox", "unknown MissingValueTreatment value");
1042 break;
1043 }
1044
1045 FSHelperPtr pFS = GetFS();
1046 pFS->singleElement(FSNS(XML_c, XML_dispBlanksAs), XML_val, pVal);
1047 }
1048
exportLegend(const Reference<css::chart::XChartDocument> & xChartDoc)1049 void ChartExport::exportLegend( const Reference< css::chart::XChartDocument >& xChartDoc )
1050 {
1051 FSHelperPtr pFS = GetFS();
1052 pFS->startElement(FSNS(XML_c, XML_legend));
1053
1054 Reference< beans::XPropertySet > xProp( xChartDoc->getLegend(), uno::UNO_QUERY );
1055 if( xProp.is() )
1056 {
1057 // position
1058 css::chart::ChartLegendPosition aLegendPos = css::chart::ChartLegendPosition_NONE;
1059 try
1060 {
1061 Any aAny( xProp->getPropertyValue( "Alignment" ));
1062 aAny >>= aLegendPos;
1063 }
1064 catch( beans::UnknownPropertyException & )
1065 {
1066 SAL_WARN("oox", "Property Align not found in ChartLegend");
1067 }
1068
1069 const char* strPos = nullptr;
1070 switch( aLegendPos )
1071 {
1072 case css::chart::ChartLegendPosition_LEFT:
1073 strPos = "l";
1074 break;
1075 case css::chart::ChartLegendPosition_RIGHT:
1076 strPos = "r";
1077 break;
1078 case css::chart::ChartLegendPosition_TOP:
1079 strPos = "t";
1080 break;
1081 case css::chart::ChartLegendPosition_BOTTOM:
1082 strPos = "b";
1083 break;
1084 case css::chart::ChartLegendPosition_NONE:
1085 case css::chart::ChartLegendPosition::ChartLegendPosition_MAKE_FIXED_SIZE:
1086 // nothing
1087 break;
1088 }
1089
1090 if( strPos != nullptr )
1091 {
1092 pFS->singleElement(FSNS(XML_c, XML_legendPos), XML_val, strPos);
1093 }
1094
1095 uno::Any aRelativePos = xProp->getPropertyValue("RelativePosition");
1096 if (aRelativePos.hasValue())
1097 {
1098 pFS->startElement(FSNS(XML_c, XML_layout));
1099 pFS->startElement(FSNS(XML_c, XML_manualLayout));
1100
1101 pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
1102 pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
1103 chart2::RelativePosition aPos = aRelativePos.get<chart2::RelativePosition>();
1104
1105 const double x = aPos.Primary;
1106 const double y = aPos.Secondary;
1107
1108 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
1109 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
1110
1111 uno::Any aRelativeSize = xProp->getPropertyValue("RelativeSize");
1112 if (aRelativeSize.hasValue())
1113 {
1114 chart2::RelativeSize aSize = aRelativeSize.get<chart2::RelativeSize>();
1115
1116 const double w = aSize.Primary;
1117 const double h = aSize.Secondary;
1118
1119 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, OString::number(w));
1120
1121 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, OString::number(h));
1122 }
1123
1124 SAL_WARN_IF(aPos.Anchor != css::drawing::Alignment_TOP_LEFT, "oox", "unsupported anchor position");
1125
1126 pFS->endElement(FSNS(XML_c, XML_manualLayout));
1127 pFS->endElement(FSNS(XML_c, XML_layout));
1128 }
1129
1130 if (strPos != nullptr)
1131 {
1132 pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, "0");
1133 }
1134
1135 // shape properties
1136 exportShapeProps( xProp );
1137
1138 // draw-chart:txPr text properties
1139 exportTextProps( xProp );
1140 }
1141
1142 // legendEntry
1143
1144 pFS->endElement( FSNS( XML_c, XML_legend ) );
1145 }
1146
exportTitle(const Reference<XShape> & xShape,const OUString * pSubText)1147 void ChartExport::exportTitle( const Reference< XShape >& xShape, const OUString* pSubText)
1148 {
1149 OUString sText;
1150 Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
1151 if( xPropSet.is())
1152 {
1153 xPropSet->getPropertyValue("String") >>= sText;
1154 }
1155
1156 // tdf#101322: add subtitle to title
1157 if( pSubText )
1158 sText = sText.isEmpty() ? *pSubText : sText + "\n" + *pSubText;
1159
1160 if( sText.isEmpty() )
1161 return;
1162
1163 FSHelperPtr pFS = GetFS();
1164 pFS->startElement(FSNS(XML_c, XML_title));
1165
1166 pFS->startElement(FSNS(XML_c, XML_tx));
1167 pFS->startElement(FSNS(XML_c, XML_rich));
1168
1169 // TODO: bodyPr
1170 const char* sWritingMode = nullptr;
1171 bool bVertical = false;
1172 xPropSet->getPropertyValue("StackedText") >>= bVertical;
1173 if( bVertical )
1174 sWritingMode = "wordArtVert";
1175
1176 sal_Int32 nRotation = 0;
1177 xPropSet->getPropertyValue("TextRotation") >>= nRotation;
1178
1179 pFS->singleElement( FSNS( XML_a, XML_bodyPr ),
1180 XML_vert, sWritingMode,
1181 XML_rot, oox::drawingml::calcRotationValue(nRotation) );
1182 // TODO: lstStyle
1183 pFS->singleElement(FSNS(XML_a, XML_lstStyle));
1184 // FIXME: handle multiple paragraphs to parse aText
1185 pFS->startElement(FSNS(XML_a, XML_p));
1186
1187 pFS->startElement(FSNS(XML_a, XML_pPr));
1188
1189 bool bDummy = false;
1190 sal_Int32 nDummy;
1191 WriteRunProperties(xPropSet, false, XML_defRPr, true, bDummy, nDummy );
1192
1193 pFS->endElement( FSNS( XML_a, XML_pPr ) );
1194
1195 pFS->startElement(FSNS(XML_a, XML_r));
1196 bDummy = false;
1197 WriteRunProperties( xPropSet, false, XML_rPr, true, bDummy, nDummy );
1198 pFS->startElement(FSNS(XML_a, XML_t));
1199 pFS->writeEscaped( sText );
1200 pFS->endElement( FSNS( XML_a, XML_t ) );
1201 pFS->endElement( FSNS( XML_a, XML_r ) );
1202
1203 pFS->endElement( FSNS( XML_a, XML_p ) );
1204
1205 pFS->endElement( FSNS( XML_c, XML_rich ) );
1206 pFS->endElement( FSNS( XML_c, XML_tx ) );
1207
1208 uno::Any aManualLayout = xPropSet->getPropertyValue("RelativePosition");
1209 if (aManualLayout.hasValue())
1210 {
1211 pFS->startElement(FSNS(XML_c, XML_layout));
1212 pFS->startElement(FSNS(XML_c, XML_manualLayout));
1213 pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
1214 pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
1215
1216 Reference<embed::XVisualObject> xVisObject(mxChartModel, uno::UNO_QUERY);
1217 awt::Size aPageSize = xVisObject->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT);
1218
1219 awt::Size aSize = xShape->getSize();
1220 awt::Point aPos2 = xShape->getPosition();
1221 // rotated shapes need special handling...
1222 double fSin = fabs(sin(basegfx::deg2rad(nRotation*0.01)));
1223 // remove part of height from X direction, if title is rotated down
1224 if( nRotation*0.01 > 180.0 )
1225 aPos2.X -= static_cast<sal_Int32>(fSin * aSize.Height + 0.5);
1226 // remove part of width from Y direction, if title is rotated up
1227 else if( nRotation*0.01 > 0.0 )
1228 aPos2.Y -= static_cast<sal_Int32>(fSin * aSize.Width + 0.5);
1229
1230 double x = static_cast<double>(aPos2.X) / static_cast<double>(aPageSize.Width);
1231 double y = static_cast<double>(aPos2.Y) / static_cast<double>(aPageSize.Height);
1232 /*
1233 pFS->singleElement(FSNS(XML_c, XML_wMode), XML_val, "edge");
1234 pFS->singleElement(FSNS(XML_c, XML_hMode), XML_val, "edge");
1235 */
1236 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
1237 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
1238 /*
1239 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, "");
1240 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, "");
1241 */
1242 pFS->endElement(FSNS(XML_c, XML_manualLayout));
1243 pFS->endElement(FSNS(XML_c, XML_layout));
1244 }
1245
1246 pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, "0");
1247
1248 // shape properties
1249 if( xPropSet.is() )
1250 {
1251 exportShapeProps( xPropSet );
1252 }
1253
1254 pFS->endElement( FSNS( XML_c, XML_title ) );
1255 }
1256
exportPlotArea(const Reference<css::chart::XChartDocument> & xChartDoc)1257 void ChartExport::exportPlotArea( const Reference< css::chart::XChartDocument >& xChartDoc )
1258 {
1259 Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( mxNewDiagram, uno::UNO_QUERY );
1260 if( ! xBCooSysCnt.is())
1261 return;
1262
1263 // plot-area element
1264
1265 FSHelperPtr pFS = GetFS();
1266 pFS->startElement(FSNS(XML_c, XML_plotArea));
1267
1268 Reference<beans::XPropertySet> xWall(mxNewDiagram, uno::UNO_QUERY);
1269 if( xWall.is() )
1270 {
1271 uno::Any aAny = xWall->getPropertyValue("RelativePosition");
1272 if (aAny.hasValue())
1273 {
1274 chart2::RelativePosition aPos = aAny.get<chart2::RelativePosition>();
1275 aAny = xWall->getPropertyValue("RelativeSize");
1276 chart2::RelativeSize aSize = aAny.get<chart2::RelativeSize>();
1277 uno::Reference< css::chart::XDiagramPositioning > xDiagramPositioning( xChartDoc->getDiagram(), uno::UNO_QUERY );
1278 exportManualLayout(aPos, aSize, xDiagramPositioning->isExcludingDiagramPositioning() );
1279 }
1280 }
1281
1282 // chart type
1283 const Sequence< Reference< chart2::XCoordinateSystem > >
1284 aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
1285 for( const auto& rCS : aCooSysSeq )
1286 {
1287 Reference< chart2::XChartTypeContainer > xCTCnt( rCS, uno::UNO_QUERY );
1288 if( ! xCTCnt.is())
1289 continue;
1290 mnSeriesCount=0;
1291 const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
1292 for( const auto& rCT : aCTSeq )
1293 {
1294 Reference< chart2::XDataSeriesContainer > xDSCnt( rCT, uno::UNO_QUERY );
1295 if( ! xDSCnt.is())
1296 return;
1297 Reference< chart2::XChartType > xChartType( rCT, uno::UNO_QUERY );
1298 if( ! xChartType.is())
1299 continue;
1300 // note: if xDSCnt.is() then also aCTSeq[nCTIdx]
1301 OUString aChartType( xChartType->getChartType());
1302 sal_Int32 eChartType = lcl_getChartType( aChartType );
1303 switch( eChartType )
1304 {
1305 case chart::TYPEID_BAR:
1306 {
1307 exportBarChart( xChartType );
1308 break;
1309 }
1310 case chart::TYPEID_AREA:
1311 {
1312 exportAreaChart( xChartType );
1313 break;
1314 }
1315 case chart::TYPEID_LINE:
1316 {
1317 exportLineChart( xChartType );
1318 break;
1319 }
1320 case chart::TYPEID_BUBBLE:
1321 {
1322 exportBubbleChart( xChartType );
1323 break;
1324 }
1325 case chart::TYPEID_OFPIE:
1326 {
1327 break;
1328 }
1329 case chart::TYPEID_DOUGHNUT:
1330 case chart::TYPEID_PIE:
1331 {
1332 exportPieChart( xChartType );
1333 break;
1334 }
1335 case chart::TYPEID_RADARLINE:
1336 case chart::TYPEID_RADARAREA:
1337 {
1338 exportRadarChart( xChartType );
1339 break;
1340 }
1341 case chart::TYPEID_SCATTER:
1342 {
1343 exportScatterChart( xChartType );
1344 break;
1345 }
1346 case chart::TYPEID_STOCK:
1347 {
1348 exportStockChart( xChartType );
1349 break;
1350 }
1351 case chart::TYPEID_SURFACE:
1352 {
1353 exportSurfaceChart( xChartType );
1354 break;
1355 }
1356 default:
1357 {
1358 SAL_WARN("oox", "ChartExport::exportPlotArea -- not support chart type");
1359 break;
1360 }
1361 }
1362
1363 }
1364 }
1365 //Axis Data
1366 exportAxes( );
1367 // Data Table
1368 exportDataTable();
1369
1370 // shape properties
1371 /*
1372 * Export the Plot area Shape Properties
1373 * eg: Fill and Outline
1374 */
1375 Reference< css::chart::X3DDisplay > xWallFloorSupplier( mxDiagram, uno::UNO_QUERY );
1376 // tdf#114139 For 2D charts Plot Area equivalent is Chart Wall.
1377 // Unfortunately LibreOffice doesn't have Plot Area equivalent for 3D charts.
1378 // It means that Plot Area couldn't be displayed and changed for 3D chars in LibreOffice.
1379 // We cannot write Wall attributes into Plot Area for 3D charts, because Wall us used as background wall.
1380 if( !mbIs3DChart && xWallFloorSupplier.is() )
1381 {
1382 Reference< beans::XPropertySet > xWallPropSet = xWallFloorSupplier->getWall();
1383 if( xWallPropSet.is() )
1384 {
1385 uno::Any aAny = xWallPropSet->getPropertyValue("LineStyle");
1386 sal_Int32 eChartType = getChartType( );
1387 // Export LineStyle_NONE instead of default linestyle of PlotArea border, because LibreOffice
1388 // make invisible the Wall shape properties, in case of these charts. Or in the future set
1389 // the default LineStyle of these charts to LineStyle_NONE.
1390 bool noSupportWallProp = ( (eChartType == chart::TYPEID_PIE) || (eChartType == chart::TYPEID_RADARLINE) || (eChartType == chart::TYPEID_RADARAREA) );
1391 if ( noSupportWallProp && (aAny != drawing::LineStyle_NONE) )
1392 {
1393 xWallPropSet->setPropertyValue( "LineStyle", uno::Any(drawing::LineStyle_NONE) );
1394 }
1395 exportShapeProps( xWallPropSet );
1396 }
1397 }
1398
1399 pFS->endElement( FSNS( XML_c, XML_plotArea ) );
1400
1401 }
1402
exportManualLayout(const css::chart2::RelativePosition & rPos,const css::chart2::RelativeSize & rSize,const bool bIsExcludingDiagramPositioning)1403 void ChartExport::exportManualLayout(const css::chart2::RelativePosition& rPos,
1404 const css::chart2::RelativeSize& rSize,
1405 const bool bIsExcludingDiagramPositioning)
1406 {
1407 FSHelperPtr pFS = GetFS();
1408 pFS->startElement(FSNS(XML_c, XML_layout));
1409 pFS->startElement(FSNS(XML_c, XML_manualLayout));
1410
1411 // By default layoutTarget is set to "outer" and we shouldn't save it in that case
1412 if ( bIsExcludingDiagramPositioning )
1413 {
1414 pFS->singleElement(FSNS(XML_c, XML_layoutTarget), XML_val, "inner");
1415 }
1416 pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
1417 pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
1418
1419 double x = rPos.Primary;
1420 double y = rPos.Secondary;
1421 const double w = rSize.Primary;
1422 const double h = rSize.Secondary;
1423 switch (rPos.Anchor)
1424 {
1425 case drawing::Alignment_LEFT:
1426 y -= (h/2);
1427 break;
1428 case drawing::Alignment_TOP_LEFT:
1429 break;
1430 case drawing::Alignment_BOTTOM_LEFT:
1431 y -= h;
1432 break;
1433 case drawing::Alignment_TOP:
1434 x -= (w/2);
1435 break;
1436 case drawing::Alignment_CENTER:
1437 x -= (w/2);
1438 y -= (h/2);
1439 break;
1440 case drawing::Alignment_BOTTOM:
1441 x -= (w/2);
1442 y -= h;
1443 break;
1444 case drawing::Alignment_TOP_RIGHT:
1445 x -= w;
1446 break;
1447 case drawing::Alignment_BOTTOM_RIGHT:
1448 x -= w;
1449 y -= h;
1450 break;
1451 case drawing::Alignment_RIGHT:
1452 y -= (h/2);
1453 x -= w;
1454 break;
1455 default:
1456 SAL_WARN("oox", "unhandled alignment case for manual layout export " << static_cast<sal_uInt16>(rPos.Anchor));
1457 }
1458
1459 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
1460
1461 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
1462
1463 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, OString::number(w));
1464
1465 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, OString::number(h));
1466
1467 pFS->endElement(FSNS(XML_c, XML_manualLayout));
1468 pFS->endElement(FSNS(XML_c, XML_layout));
1469 }
1470
exportFill(const Reference<XPropertySet> & xPropSet)1471 void ChartExport::exportFill( const Reference< XPropertySet >& xPropSet )
1472 {
1473 if ( !GetProperty( xPropSet, "FillStyle" ) )
1474 return;
1475 FillStyle aFillStyle( FillStyle_NONE );
1476 xPropSet->getPropertyValue( "FillStyle" ) >>= aFillStyle;
1477 switch( aFillStyle )
1478 {
1479 case FillStyle_GRADIENT :
1480 exportGradientFill( xPropSet );
1481 break;
1482 case FillStyle_BITMAP :
1483 exportBitmapFill( xPropSet );
1484 break;
1485 case FillStyle_HATCH:
1486 exportHatch(xPropSet);
1487 break;
1488 default:
1489 WriteFill( xPropSet );
1490 }
1491 }
1492
exportHatch(const Reference<XPropertySet> & xPropSet)1493 void ChartExport::exportHatch( const Reference< XPropertySet >& xPropSet )
1494 {
1495 if (!xPropSet.is())
1496 return;
1497
1498 if (GetProperty(xPropSet, "FillHatchName"))
1499 {
1500 OUString aHatchName;
1501 mAny >>= aHatchName;
1502 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
1503 uno::Reference< container::XNameAccess > xHatchTable( xFact->createInstance("com.sun.star.drawing.HatchTable"), uno::UNO_QUERY );
1504 uno::Any rValue = xHatchTable->getByName(aHatchName);
1505 css::drawing::Hatch aHatch;
1506 rValue >>= aHatch;
1507 WritePattFill(xPropSet, aHatch);
1508 }
1509
1510 }
1511
exportBitmapFill(const Reference<XPropertySet> & xPropSet)1512 void ChartExport::exportBitmapFill( const Reference< XPropertySet >& xPropSet )
1513 {
1514 if( xPropSet.is() )
1515 {
1516 OUString sFillBitmapName;
1517 xPropSet->getPropertyValue("FillBitmapName") >>= sFillBitmapName;
1518
1519 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
1520 try
1521 {
1522 uno::Reference< container::XNameAccess > xBitmapTable( xFact->createInstance("com.sun.star.drawing.BitmapTable"), uno::UNO_QUERY );
1523 uno::Any rValue = xBitmapTable->getByName( sFillBitmapName );
1524 if (rValue.has<uno::Reference<awt::XBitmap>>())
1525 {
1526 uno::Reference<awt::XBitmap> xBitmap = rValue.get<uno::Reference<awt::XBitmap>>();
1527 uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY);
1528 if (xGraphic.is())
1529 {
1530 WriteXGraphicBlipFill(xPropSet, xGraphic, XML_a, true, true);
1531 }
1532 }
1533 }
1534 catch (const uno::Exception &)
1535 {
1536 TOOLS_WARN_EXCEPTION("oox", "ChartExport::exportBitmapFill");
1537 }
1538 }
1539 }
1540
exportGradientFill(const Reference<XPropertySet> & xPropSet)1541 void ChartExport::exportGradientFill( const Reference< XPropertySet >& xPropSet )
1542 {
1543 if( xPropSet.is() )
1544 {
1545 OUString sFillGradientName;
1546 xPropSet->getPropertyValue("FillGradientName") >>= sFillGradientName;
1547
1548 awt::Gradient aGradient;
1549 awt::Gradient aTransparenceGradient;
1550 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
1551 try
1552 {
1553 uno::Reference< container::XNameAccess > xGradient( xFact->createInstance("com.sun.star.drawing.GradientTable"), uno::UNO_QUERY );
1554 uno::Any rGradientValue = xGradient->getByName( sFillGradientName );
1555 if( rGradientValue >>= aGradient )
1556 {
1557 mpFS->startElementNS(XML_a, XML_gradFill);
1558 OUString sFillTransparenceGradientName;
1559 if( (xPropSet->getPropertyValue("FillTransparenceGradientName") >>= sFillTransparenceGradientName) && !sFillTransparenceGradientName.isEmpty())
1560 {
1561 uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY);
1562 uno::Any rTransparenceValue = xTransparenceGradient->getByName(sFillTransparenceGradientName);
1563 rTransparenceValue >>= aTransparenceGradient;;
1564 WriteGradientFill(aGradient, aTransparenceGradient);
1565 }
1566 else
1567 {
1568 WriteGradientFill(aGradient, aTransparenceGradient, xPropSet);
1569 }
1570 mpFS->endElementNS(XML_a, XML_gradFill);
1571 }
1572 }
1573 catch (const uno::Exception &)
1574 {
1575 TOOLS_INFO_EXCEPTION("oox", "ChartExport::exportGradientFill");
1576 }
1577
1578 }
1579 }
1580
exportDataTable()1581 void ChartExport::exportDataTable( )
1582 {
1583 FSHelperPtr pFS = GetFS();
1584 Reference< beans::XPropertySet > aPropSet( mxDiagram, uno::UNO_QUERY );
1585
1586 bool bShowVBorder = false;
1587 bool bShowHBorder = false;
1588 bool bShowOutline = false;
1589
1590 if (GetProperty( aPropSet, "DataTableHBorder"))
1591 mAny >>= bShowHBorder;
1592 if (GetProperty( aPropSet, "DataTableVBorder"))
1593 mAny >>= bShowVBorder;
1594 if (GetProperty( aPropSet, "DataTableOutline"))
1595 mAny >>= bShowOutline;
1596
1597 if (bShowVBorder || bShowHBorder || bShowOutline)
1598 {
1599 pFS->startElement(FSNS(XML_c, XML_dTable));
1600 if (bShowHBorder)
1601 pFS->singleElement( FSNS( XML_c, XML_showHorzBorder ),
1602 XML_val, "1" );
1603 if (bShowVBorder)
1604 pFS->singleElement(FSNS(XML_c, XML_showVertBorder), XML_val, "1");
1605 if (bShowOutline)
1606 pFS->singleElement(FSNS(XML_c, XML_showOutline), XML_val, "1");
1607
1608 pFS->endElement( FSNS( XML_c, XML_dTable));
1609 }
1610
1611 }
exportAreaChart(const Reference<chart2::XChartType> & xChartType)1612 void ChartExport::exportAreaChart( const Reference< chart2::XChartType >& xChartType )
1613 {
1614 FSHelperPtr pFS = GetFS();
1615 sal_Int32 nTypeId = XML_areaChart;
1616 if( mbIs3DChart )
1617 nTypeId = XML_area3DChart;
1618 pFS->startElement(FSNS(XML_c, nTypeId));
1619
1620 exportGrouping( );
1621 bool bPrimaryAxes = true;
1622 exportAllSeries(xChartType, bPrimaryAxes);
1623 exportAxesId(bPrimaryAxes);
1624
1625 pFS->endElement( FSNS( XML_c, nTypeId ) );
1626 }
1627
exportBarChart(const Reference<chart2::XChartType> & xChartType)1628 void ChartExport::exportBarChart( const Reference< chart2::XChartType >& xChartType )
1629 {
1630 sal_Int32 nTypeId = XML_barChart;
1631 if( mbIs3DChart )
1632 nTypeId = XML_bar3DChart;
1633 FSHelperPtr pFS = GetFS();
1634 pFS->startElement(FSNS(XML_c, nTypeId));
1635 // bar direction
1636 bool bVertical = false;
1637 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
1638 if( GetProperty( xPropSet, "Vertical" ) )
1639 mAny >>= bVertical;
1640
1641 const char* bardir = bVertical? "bar":"col";
1642 pFS->singleElement(FSNS(XML_c, XML_barDir), XML_val, bardir);
1643
1644 exportGrouping( true );
1645
1646 exportVaryColors(xChartType);
1647
1648 bool bPrimaryAxes = true;
1649 exportAllSeries(xChartType, bPrimaryAxes);
1650
1651 Reference< XPropertySet > xTypeProp( xChartType, uno::UNO_QUERY );
1652
1653 if( xTypeProp.is() && GetProperty( xTypeProp, "GapwidthSequence") )
1654 {
1655 uno::Sequence< sal_Int32 > aBarPositionSequence;
1656 mAny >>= aBarPositionSequence;
1657 if( aBarPositionSequence.hasElements() )
1658 {
1659 sal_Int32 nGapWidth = aBarPositionSequence[0];
1660 pFS->singleElement(FSNS(XML_c, XML_gapWidth), XML_val, OString::number(nGapWidth));
1661 }
1662 }
1663
1664 if( mbIs3DChart )
1665 {
1666 // Shape
1667 namespace cssc = css::chart;
1668 sal_Int32 nGeom3d = cssc::ChartSolidType::RECTANGULAR_SOLID;
1669 if( xPropSet.is() && GetProperty( xPropSet, "SolidType") )
1670 mAny >>= nGeom3d;
1671 const char* sShapeType = nullptr;
1672 switch( nGeom3d )
1673 {
1674 case cssc::ChartSolidType::RECTANGULAR_SOLID:
1675 sShapeType = "box";
1676 break;
1677 case cssc::ChartSolidType::CONE:
1678 sShapeType = "cone";
1679 break;
1680 case cssc::ChartSolidType::CYLINDER:
1681 sShapeType = "cylinder";
1682 break;
1683 case cssc::ChartSolidType::PYRAMID:
1684 sShapeType = "pyramid";
1685 break;
1686 }
1687 pFS->singleElement(FSNS(XML_c, XML_shape), XML_val, sShapeType);
1688 }
1689
1690 //overlap
1691 if( !mbIs3DChart && xTypeProp.is() && GetProperty( xTypeProp, "OverlapSequence") )
1692 {
1693 uno::Sequence< sal_Int32 > aBarPositionSequence;
1694 mAny >>= aBarPositionSequence;
1695 if( aBarPositionSequence.hasElements() )
1696 {
1697 sal_Int32 nOverlap = aBarPositionSequence[0];
1698 // Stacked/Percent Bar/Column chart Overlap-workaround
1699 // Export the Overlap value with 100% for stacked charts,
1700 // because the default overlap value of the Bar/Column chart is 0% and
1701 // LibreOffice do nothing with the overlap value in Stacked charts case,
1702 // unlike the MS Office, which is interpreted differently.
1703 if( ( mbStacked || mbPercent ) && nOverlap != 100 )
1704 {
1705 nOverlap = 100;
1706 pFS->singleElement(FSNS(XML_c, XML_overlap), XML_val, OString::number(nOverlap));
1707 }
1708 else // Normal bar chart
1709 {
1710 pFS->singleElement(FSNS(XML_c, XML_overlap), XML_val, OString::number(nOverlap));
1711 }
1712 }
1713 }
1714
1715 exportAxesId(bPrimaryAxes);
1716
1717 pFS->endElement( FSNS( XML_c, nTypeId ) );
1718 }
1719
exportBubbleChart(const Reference<chart2::XChartType> & xChartType)1720 void ChartExport::exportBubbleChart( const Reference< chart2::XChartType >& xChartType )
1721 {
1722 FSHelperPtr pFS = GetFS();
1723 pFS->startElement(FSNS(XML_c, XML_bubbleChart));
1724
1725 exportVaryColors(xChartType);
1726
1727 bool bPrimaryAxes = true;
1728 exportAllSeries(xChartType, bPrimaryAxes);
1729
1730 exportAxesId(bPrimaryAxes);
1731
1732 pFS->endElement( FSNS( XML_c, XML_bubbleChart ) );
1733 }
1734
exportDoughnutChart(const Reference<chart2::XChartType> & xChartType)1735 void ChartExport::exportDoughnutChart( const Reference< chart2::XChartType >& xChartType )
1736 {
1737 FSHelperPtr pFS = GetFS();
1738 pFS->startElement(FSNS(XML_c, XML_doughnutChart));
1739
1740 exportVaryColors(xChartType);
1741
1742 bool bPrimaryAxes = true;
1743 exportAllSeries(xChartType, bPrimaryAxes);
1744 // firstSliceAng
1745 exportFirstSliceAng( );
1746 //FIXME: holeSize
1747 pFS->singleElement(FSNS(XML_c, XML_holeSize), XML_val, OString::number(50));
1748
1749 pFS->endElement( FSNS( XML_c, XML_doughnutChart ) );
1750 }
1751
1752 namespace {
1753
splitDataSeriesByAxis(const Reference<chart2::XChartType> & xChartType)1754 std::vector<Sequence<Reference<chart2::XDataSeries> > > splitDataSeriesByAxis(const Reference< chart2::XChartType >& xChartType)
1755 {
1756 std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitSeries;
1757 std::map<sal_Int32, size_t> aMapAxisToIndex;
1758
1759 Reference< chart2::XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY );
1760 if(xDSCnt.is())
1761 {
1762 sal_Int32 nAxisIndexOfFirstSeries = -1;
1763 const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries());
1764 for (const uno::Reference<chart2::XDataSeries>& xSeries : aSeriesSeq)
1765 {
1766 Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY);
1767 if (!xPropSet.is())
1768 continue;
1769
1770 sal_Int32 nAxisIndex = -1;
1771 uno::Any aAny = xPropSet->getPropertyValue("AttachedAxisIndex");
1772 aAny >>= nAxisIndex;
1773 size_t nVectorPos = 0;
1774 if (nAxisIndexOfFirstSeries == -1)
1775 {
1776 nAxisIndexOfFirstSeries = nAxisIndex;
1777 }
1778
1779 auto it = aMapAxisToIndex.find(nAxisIndex);
1780 if (it == aMapAxisToIndex.end())
1781 {
1782 aSplitSeries.emplace_back();
1783 nVectorPos = aSplitSeries.size() - 1;
1784 aMapAxisToIndex.insert(std::pair<sal_Int32, size_t>(nAxisIndex, nVectorPos));
1785 }
1786 else
1787 {
1788 nVectorPos = it->second;
1789 }
1790
1791 uno::Sequence<Reference<chart2::XDataSeries> >& rAxisSeriesSeq = aSplitSeries[nVectorPos];
1792 sal_Int32 nLength = rAxisSeriesSeq.getLength();
1793 rAxisSeriesSeq.realloc(nLength + 1);
1794 rAxisSeriesSeq[nLength] = xSeries;
1795 }
1796 // if the first series attached to secondary axis, then export those series first, which are attached to primary axis
1797 // also the MS Office export every time in this order
1798 if ( aSplitSeries.size() > 1 && nAxisIndexOfFirstSeries == 1 )
1799 {
1800 std::swap( aSplitSeries[0], aSplitSeries[1] );
1801 }
1802 }
1803
1804 return aSplitSeries;
1805 }
1806
1807 }
1808
exportLineChart(const Reference<chart2::XChartType> & xChartType)1809 void ChartExport::exportLineChart( const Reference< chart2::XChartType >& xChartType )
1810 {
1811 FSHelperPtr pFS = GetFS();
1812 std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
1813 for (auto & splitDataSeries : aSplitDataSeries)
1814 {
1815 if (!splitDataSeries.hasElements())
1816 continue;
1817
1818 sal_Int32 nTypeId = XML_lineChart;
1819 if( mbIs3DChart )
1820 nTypeId = XML_line3DChart;
1821 pFS->startElement(FSNS(XML_c, nTypeId));
1822
1823 exportGrouping( );
1824
1825 exportVaryColors(xChartType);
1826 // TODO: show marker symbol in series?
1827 bool bPrimaryAxes = true;
1828 exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
1829
1830 // show marker?
1831 sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE;
1832 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
1833 if( GetProperty( xPropSet, "SymbolType" ) )
1834 mAny >>= nSymbolType;
1835
1836 if( !mbIs3DChart )
1837 {
1838 exportHiLowLines();
1839 exportUpDownBars(xChartType);
1840 const char* marker = nSymbolType == css::chart::ChartSymbolType::NONE? "0":"1";
1841 pFS->singleElement(FSNS(XML_c, XML_marker), XML_val, marker);
1842 }
1843
1844 exportAxesId(bPrimaryAxes, true);
1845
1846 pFS->endElement( FSNS( XML_c, nTypeId ) );
1847 }
1848 }
1849
exportPieChart(const Reference<chart2::XChartType> & xChartType)1850 void ChartExport::exportPieChart( const Reference< chart2::XChartType >& xChartType )
1851 {
1852 sal_Int32 eChartType = getChartType( );
1853 if(eChartType == chart::TYPEID_DOUGHNUT)
1854 {
1855 exportDoughnutChart( xChartType );
1856 return;
1857 }
1858 FSHelperPtr pFS = GetFS();
1859 sal_Int32 nTypeId = XML_pieChart;
1860 if( mbIs3DChart )
1861 nTypeId = XML_pie3DChart;
1862 pFS->startElement(FSNS(XML_c, nTypeId));
1863
1864 exportVaryColors(xChartType);
1865
1866 bool bPrimaryAxes = true;
1867 exportAllSeries(xChartType, bPrimaryAxes);
1868
1869 if( !mbIs3DChart )
1870 {
1871 // firstSliceAng
1872 exportFirstSliceAng( );
1873 }
1874
1875 pFS->endElement( FSNS( XML_c, nTypeId ) );
1876 }
1877
exportRadarChart(const Reference<chart2::XChartType> & xChartType)1878 void ChartExport::exportRadarChart( const Reference< chart2::XChartType >& xChartType)
1879 {
1880 FSHelperPtr pFS = GetFS();
1881 pFS->startElement(FSNS(XML_c, XML_radarChart));
1882
1883 // radarStyle
1884 sal_Int32 eChartType = getChartType( );
1885 const char* radarStyle = nullptr;
1886 if( eChartType == chart::TYPEID_RADARAREA )
1887 radarStyle = "filled";
1888 else
1889 radarStyle = "marker";
1890 pFS->singleElement(FSNS(XML_c, XML_radarStyle), XML_val, radarStyle);
1891
1892 exportVaryColors(xChartType);
1893 bool bPrimaryAxes = true;
1894 exportAllSeries(xChartType, bPrimaryAxes);
1895 exportAxesId(bPrimaryAxes);
1896
1897 pFS->endElement( FSNS( XML_c, XML_radarChart ) );
1898 }
1899
exportScatterChartSeries(const Reference<chart2::XChartType> & xChartType,css::uno::Sequence<css::uno::Reference<chart2::XDataSeries>> * pSeries)1900 void ChartExport::exportScatterChartSeries( const Reference< chart2::XChartType >& xChartType,
1901 css::uno::Sequence<css::uno::Reference<chart2::XDataSeries>>* pSeries)
1902 {
1903 FSHelperPtr pFS = GetFS();
1904 pFS->startElement(FSNS(XML_c, XML_scatterChart));
1905 // TODO:scatterStyle
1906
1907 sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE;
1908 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
1909 if( GetProperty( xPropSet, "SymbolType" ) )
1910 mAny >>= nSymbolType;
1911
1912 const char* scatterStyle = "lineMarker";
1913 if (nSymbolType == css::chart::ChartSymbolType::NONE)
1914 {
1915 scatterStyle = "line";
1916 }
1917
1918 pFS->singleElement(FSNS(XML_c, XML_scatterStyle), XML_val, scatterStyle);
1919
1920 exportVaryColors(xChartType);
1921 // FIXME: should export xVal and yVal
1922 bool bPrimaryAxes = true;
1923 if (pSeries)
1924 exportSeries(xChartType, *pSeries, bPrimaryAxes);
1925 exportAxesId(bPrimaryAxes);
1926
1927 pFS->endElement( FSNS( XML_c, XML_scatterChart ) );
1928 }
1929
exportScatterChart(const Reference<chart2::XChartType> & xChartType)1930 void ChartExport::exportScatterChart( const Reference< chart2::XChartType >& xChartType )
1931 {
1932 FSHelperPtr pFS = GetFS();
1933 std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
1934 bool bExported = false;
1935 for (auto & splitDataSeries : aSplitDataSeries)
1936 {
1937 if (!splitDataSeries.hasElements())
1938 continue;
1939
1940 bExported = true;
1941 exportScatterChartSeries(xChartType, &splitDataSeries);
1942 }
1943 if (!bExported)
1944 exportScatterChartSeries(xChartType, nullptr);
1945 }
1946
exportStockChart(const Reference<chart2::XChartType> & xChartType)1947 void ChartExport::exportStockChart( const Reference< chart2::XChartType >& xChartType )
1948 {
1949 FSHelperPtr pFS = GetFS();
1950 pFS->startElement(FSNS(XML_c, XML_stockChart));
1951
1952 bool bPrimaryAxes = true;
1953 Reference< chart2::XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY );
1954 if(xDSCnt.is())
1955 exportCandleStickSeries( xDSCnt->getDataSeries(), bPrimaryAxes );
1956
1957 // export stock properties
1958 Reference< css::chart::XStatisticDisplay > xStockPropProvider( mxDiagram, uno::UNO_QUERY );
1959 if( xStockPropProvider.is())
1960 {
1961 exportHiLowLines();
1962 exportUpDownBars(xChartType);
1963 }
1964
1965 exportAxesId(bPrimaryAxes);
1966
1967 pFS->endElement( FSNS( XML_c, XML_stockChart ) );
1968 }
1969
exportHiLowLines()1970 void ChartExport::exportHiLowLines()
1971 {
1972 FSHelperPtr pFS = GetFS();
1973 // export the chart property
1974 Reference< css::chart::XStatisticDisplay > xChartPropProvider( mxDiagram, uno::UNO_QUERY );
1975
1976 if (!xChartPropProvider.is())
1977 return;
1978
1979 Reference< beans::XPropertySet > xStockPropSet = xChartPropProvider->getMinMaxLine();
1980 if( !xStockPropSet.is() )
1981 return;
1982
1983 pFS->startElement(FSNS(XML_c, XML_hiLowLines));
1984 exportShapeProps( xStockPropSet );
1985 pFS->endElement( FSNS( XML_c, XML_hiLowLines ) );
1986 }
1987
exportUpDownBars(const Reference<chart2::XChartType> & xChartType)1988 void ChartExport::exportUpDownBars( const Reference< chart2::XChartType >& xChartType)
1989 {
1990 if(xChartType->getChartType() != "com.sun.star.chart2.CandleStickChartType")
1991 return;
1992
1993 FSHelperPtr pFS = GetFS();
1994 // export the chart property
1995 Reference< css::chart::XStatisticDisplay > xChartPropProvider( mxDiagram, uno::UNO_QUERY );
1996 if(xChartPropProvider.is())
1997 {
1998 // updownbar
1999 pFS->startElement(FSNS(XML_c, XML_upDownBars));
2000 // TODO: gapWidth
2001 pFS->singleElement(FSNS(XML_c, XML_gapWidth), XML_val, OString::number(150));
2002
2003 Reference< beans::XPropertySet > xChartPropSet = xChartPropProvider->getUpBar();
2004 if( xChartPropSet.is() )
2005 {
2006 pFS->startElement(FSNS(XML_c, XML_upBars));
2007 // For Linechart with UpDownBars, spPr is not getting imported
2008 // so no need to call the exportShapeProps() for LineChart
2009 if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType")
2010 {
2011 exportShapeProps(xChartPropSet);
2012 }
2013 pFS->endElement( FSNS( XML_c, XML_upBars ) );
2014 }
2015 xChartPropSet = xChartPropProvider->getDownBar();
2016 if( xChartPropSet.is() )
2017 {
2018 pFS->startElement(FSNS(XML_c, XML_downBars));
2019 if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType")
2020 {
2021 exportShapeProps(xChartPropSet);
2022 }
2023 pFS->endElement( FSNS( XML_c, XML_downBars ) );
2024 }
2025 pFS->endElement( FSNS( XML_c, XML_upDownBars ) );
2026 }
2027 }
2028
exportSurfaceChart(const Reference<chart2::XChartType> & xChartType)2029 void ChartExport::exportSurfaceChart( const Reference< chart2::XChartType >& xChartType )
2030 {
2031 FSHelperPtr pFS = GetFS();
2032 sal_Int32 nTypeId = XML_surfaceChart;
2033 if( mbIs3DChart )
2034 nTypeId = XML_surface3DChart;
2035 pFS->startElement(FSNS(XML_c, nTypeId));
2036 exportVaryColors(xChartType);
2037 bool bPrimaryAxes = true;
2038 exportAllSeries(xChartType, bPrimaryAxes);
2039 exportAxesId(bPrimaryAxes);
2040
2041 pFS->endElement( FSNS( XML_c, nTypeId ) );
2042 }
2043
exportAllSeries(const Reference<chart2::XChartType> & xChartType,bool & rPrimaryAxes)2044 void ChartExport::exportAllSeries(const Reference<chart2::XChartType>& xChartType, bool& rPrimaryAxes)
2045 {
2046 Reference< chart2::XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY );
2047 if( ! xDSCnt.is())
2048 return;
2049
2050 // export dataseries for current chart-type
2051 Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries());
2052 exportSeries(xChartType, aSeriesSeq, rPrimaryAxes);
2053 }
2054
exportVaryColors(const Reference<chart2::XChartType> & xChartType)2055 void ChartExport::exportVaryColors(const Reference<chart2::XChartType>& xChartType)
2056 {
2057 FSHelperPtr pFS = GetFS();
2058 try
2059 {
2060 Reference<chart2::XDataSeries> xDataSeries = getPrimaryDataSeries(xChartType);
2061 Reference<beans::XPropertySet> xDataSeriesProps(xDataSeries, uno::UNO_QUERY_THROW);
2062 Any aAnyVaryColors = xDataSeriesProps->getPropertyValue("VaryColorsByPoint");
2063 bool bVaryColors = false;
2064 aAnyVaryColors >>= bVaryColors;
2065 pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, ToPsz10(bVaryColors));
2066 }
2067 catch (...)
2068 {
2069 pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, "0");
2070 }
2071 }
2072
exportSeries(const Reference<chart2::XChartType> & xChartType,Sequence<Reference<chart2::XDataSeries>> & rSeriesSeq,bool & rPrimaryAxes)2073 void ChartExport::exportSeries( const Reference<chart2::XChartType>& xChartType,
2074 Sequence<Reference<chart2::XDataSeries> >& rSeriesSeq, bool& rPrimaryAxes )
2075 {
2076 OUString aLabelRole = xChartType->getRoleOfSequenceForSeriesLabel();
2077 OUString aChartType( xChartType->getChartType());
2078 sal_Int32 eChartType = lcl_getChartType( aChartType );
2079
2080 for( const auto& rSeries : std::as_const(rSeriesSeq) )
2081 {
2082 // export series
2083 Reference< chart2::data::XDataSource > xSource( rSeries, uno::UNO_QUERY );
2084 if( xSource.is())
2085 {
2086 Reference< chart2::XDataSeries > xDataSeries( xSource, uno::UNO_QUERY );
2087 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
2088 xSource->getDataSequences());
2089 // search for main sequence and create a series element
2090 {
2091 sal_Int32 nMainSequenceIndex = -1;
2092 sal_Int32 nSeriesLength = 0;
2093 Reference< chart2::data::XDataSequence > xValuesSeq;
2094 Reference< chart2::data::XDataSequence > xLabelSeq;
2095 sal_Int32 nSeqIdx=0;
2096 for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx )
2097 {
2098 OUString aRole;
2099 Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() );
2100 if( nMainSequenceIndex==-1 )
2101 {
2102 Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY );
2103 if( xSeqProp.is())
2104 xSeqProp->getPropertyValue("Role") >>= aRole;
2105 // "main" sequence
2106 if( aRole == aLabelRole )
2107 {
2108 xValuesSeq.set( xTempValueSeq );
2109 xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel());
2110 nMainSequenceIndex = nSeqIdx;
2111 }
2112 }
2113 sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0));
2114 if( nSeriesLength < nSequenceLength )
2115 nSeriesLength = nSequenceLength;
2116 }
2117
2118 // have found the main sequence, then xValuesSeq and
2119 // xLabelSeq contain those. Otherwise both are empty
2120 {
2121 FSHelperPtr pFS = GetFS();
2122
2123 pFS->startElement(FSNS(XML_c, XML_ser));
2124
2125 // TODO: idx and order
2126 pFS->singleElement( FSNS( XML_c, XML_idx ),
2127 XML_val, OString::number(mnSeriesCount) );
2128 pFS->singleElement( FSNS( XML_c, XML_order ),
2129 XML_val, OString::number(mnSeriesCount++) );
2130
2131 // export label
2132 if( xLabelSeq.is() )
2133 exportSeriesText( xLabelSeq );
2134
2135 Reference<XPropertySet> xPropSet(xDataSeries, UNO_QUERY_THROW);
2136 if( GetProperty( xPropSet, "AttachedAxisIndex") )
2137 {
2138 sal_Int32 nLocalAttachedAxis = 0;
2139 mAny >>= nLocalAttachedAxis;
2140 rPrimaryAxes = isPrimaryAxes(nLocalAttachedAxis);
2141 }
2142
2143 // export shape properties
2144 Reference< XPropertySet > xOldPropSet = SchXMLSeriesHelper::createOldAPISeriesPropertySet(
2145 rSeries, getModel() );
2146 if( xOldPropSet.is() )
2147 {
2148 exportShapeProps( xOldPropSet );
2149 }
2150
2151 switch( eChartType )
2152 {
2153 case chart::TYPEID_BUBBLE:
2154 case chart::TYPEID_HORBAR:
2155 case chart::TYPEID_BAR:
2156 {
2157 pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0");
2158 }
2159 break;
2160 case chart::TYPEID_LINE:
2161 {
2162 exportMarker(xOldPropSet);
2163 break;
2164 }
2165 case chart::TYPEID_PIE:
2166 case chart::TYPEID_DOUGHNUT:
2167 {
2168 if( xOldPropSet.is() && GetProperty( xOldPropSet, "SegmentOffset") )
2169 {
2170 sal_Int32 nOffset = 0;
2171 mAny >>= nOffset;
2172 pFS->singleElement( FSNS( XML_c, XML_explosion ),
2173 XML_val, OString::number( nOffset ) );
2174 }
2175 break;
2176 }
2177 case chart::TYPEID_SCATTER:
2178 {
2179 exportMarker(xOldPropSet);
2180 break;
2181 }
2182 case chart::TYPEID_RADARLINE:
2183 {
2184 exportMarker(xOldPropSet);
2185 break;
2186 }
2187 }
2188
2189 // export data points
2190 exportDataPoints( uno::Reference< beans::XPropertySet >( rSeries, uno::UNO_QUERY ), nSeriesLength, eChartType );
2191
2192 // export data labels
2193 exportDataLabels(rSeries, nSeriesLength, eChartType);
2194
2195 exportTrendlines( rSeries );
2196
2197 if( eChartType != chart::TYPEID_PIE &&
2198 eChartType != chart::TYPEID_RADARLINE )
2199 {
2200 //export error bars here
2201 Reference< XPropertySet > xSeriesPropSet( xSource, uno::UNO_QUERY );
2202 Reference< XPropertySet > xErrorBarYProps;
2203 xSeriesPropSet->getPropertyValue("ErrorBarY") >>= xErrorBarYProps;
2204 if(xErrorBarYProps.is())
2205 exportErrorBar(xErrorBarYProps, true);
2206 if (eChartType != chart::TYPEID_BAR &&
2207 eChartType != chart::TYPEID_HORBAR)
2208 {
2209 Reference< XPropertySet > xErrorBarXProps;
2210 xSeriesPropSet->getPropertyValue("ErrorBarX") >>= xErrorBarXProps;
2211 if(xErrorBarXProps.is())
2212 exportErrorBar(xErrorBarXProps, false);
2213 }
2214 }
2215
2216 // export categories
2217 if( eChartType != chart::TYPEID_SCATTER && eChartType != chart::TYPEID_BUBBLE && mxCategoriesValues.is() )
2218 exportSeriesCategory( mxCategoriesValues );
2219
2220 if( (eChartType == chart::TYPEID_SCATTER)
2221 || (eChartType == chart::TYPEID_BUBBLE) )
2222 {
2223 // export xVal
2224 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, "values-x" ) );
2225 if( xSequence.is() )
2226 {
2227 Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
2228 if( xValues.is() )
2229 exportSeriesValues( xValues, XML_xVal );
2230 }
2231 }
2232
2233 if( eChartType == chart::TYPEID_BUBBLE )
2234 {
2235 // export yVal
2236 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, "values-y" ) );
2237 if( xSequence.is() )
2238 {
2239 Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
2240 if( xValues.is() )
2241 exportSeriesValues( xValues, XML_yVal );
2242 }
2243 }
2244
2245 // export values
2246 if( xValuesSeq.is() )
2247 {
2248 sal_Int32 nYValueType = XML_val;
2249 if( eChartType == chart::TYPEID_SCATTER )
2250 nYValueType = XML_yVal;
2251 else if( eChartType == chart::TYPEID_BUBBLE )
2252 nYValueType = XML_bubbleSize;
2253 exportSeriesValues( xValuesSeq, nYValueType );
2254 }
2255
2256 if( eChartType == chart::TYPEID_SCATTER
2257 || eChartType == chart::TYPEID_LINE )
2258 exportSmooth();
2259
2260 // tdf103988: "corrupted" files with Bubble chart opening in MSO
2261 if( eChartType == chart::TYPEID_BUBBLE )
2262 pFS->singleElement(FSNS(XML_c, XML_bubble3D), XML_val, "0");
2263
2264 pFS->endElement( FSNS( XML_c, XML_ser ) );
2265 }
2266 }
2267 }
2268 }
2269 }
2270
exportCandleStickSeries(const Sequence<Reference<chart2::XDataSeries>> & aSeriesSeq,bool & rPrimaryAxes)2271 void ChartExport::exportCandleStickSeries(
2272 const Sequence< Reference< chart2::XDataSeries > > & aSeriesSeq,
2273 bool& rPrimaryAxes)
2274 {
2275 for( const Reference< chart2::XDataSeries >& xSeries : aSeriesSeq )
2276 {
2277 rPrimaryAxes = lcl_isSeriesAttachedToFirstAxis(xSeries);
2278
2279 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
2280 if( xSource.is())
2281 {
2282 // export series in correct order (as we don't store roles)
2283 // with japanese candlesticks: open, low, high, close
2284 // otherwise: low, high, close
2285 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
2286 xSource->getDataSequences());
2287
2288 const char* sSeries[] = {"values-first","values-max","values-min","values-last",nullptr};
2289
2290 for( sal_Int32 idx = 0; sSeries[idx] != nullptr ; idx++ )
2291 {
2292 Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( lcl_getDataSequenceByRole( aSeqCnt, OUString::createFromAscii(sSeries[idx]) ) );
2293 if( xLabeledSeq.is())
2294 {
2295 Reference< chart2::data::XDataSequence > xLabelSeq( xLabeledSeq->getLabel());
2296 Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues());
2297 {
2298 FSHelperPtr pFS = GetFS();
2299 pFS->startElement(FSNS(XML_c, XML_ser));
2300
2301 // TODO: idx and order
2302 // idx attribute should start from 1 and not from 0.
2303 pFS->singleElement( FSNS( XML_c, XML_idx ),
2304 XML_val, OString::number(idx+1) );
2305 pFS->singleElement( FSNS( XML_c, XML_order ),
2306 XML_val, OString::number(idx+1) );
2307
2308 // export label
2309 if( xLabelSeq.is() )
2310 exportSeriesText( xLabelSeq );
2311
2312 // TODO:export shape properties
2313
2314 // export categories
2315 if( mxCategoriesValues.is() )
2316 exportSeriesCategory( mxCategoriesValues );
2317
2318 // export values
2319 if( xValueSeq.is() )
2320 exportSeriesValues( xValueSeq );
2321
2322 pFS->endElement( FSNS( XML_c, XML_ser ) );
2323 }
2324 }
2325 }
2326 }
2327 }
2328 }
2329
exportSeriesText(const Reference<chart2::data::XDataSequence> & xValueSeq)2330 void ChartExport::exportSeriesText( const Reference< chart2::data::XDataSequence > & xValueSeq )
2331 {
2332 FSHelperPtr pFS = GetFS();
2333 pFS->startElement(FSNS(XML_c, XML_tx));
2334
2335 OUString aCellRange = xValueSeq->getSourceRangeRepresentation();
2336 aCellRange = parseFormula( aCellRange );
2337 pFS->startElement(FSNS(XML_c, XML_strRef));
2338
2339 pFS->startElement(FSNS(XML_c, XML_f));
2340 pFS->writeEscaped( aCellRange );
2341 pFS->endElement( FSNS( XML_c, XML_f ) );
2342
2343 OUString aLabelString = lcl_flattenStringSequence(lcl_getLabelSequence(xValueSeq));
2344 pFS->startElement(FSNS(XML_c, XML_strCache));
2345 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, "1");
2346 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, "0");
2347 pFS->startElement(FSNS(XML_c, XML_v));
2348 pFS->writeEscaped( aLabelString );
2349 pFS->endElement( FSNS( XML_c, XML_v ) );
2350 pFS->endElement( FSNS( XML_c, XML_pt ) );
2351 pFS->endElement( FSNS( XML_c, XML_strCache ) );
2352 pFS->endElement( FSNS( XML_c, XML_strRef ) );
2353 pFS->endElement( FSNS( XML_c, XML_tx ) );
2354 }
2355
exportSeriesCategory(const Reference<chart2::data::XDataSequence> & xValueSeq)2356 void ChartExport::exportSeriesCategory( const Reference< chart2::data::XDataSequence > & xValueSeq )
2357 {
2358 FSHelperPtr pFS = GetFS();
2359 pFS->startElement(FSNS(XML_c, XML_cat));
2360
2361 OUString aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString();
2362 const Sequence< Sequence< OUString >> aFinalSplitSource = getSplitCategoriesList(aCellRange);
2363 aCellRange = parseFormula( aCellRange );
2364
2365 if(aFinalSplitSource.getLength() > 1)
2366 {
2367 // export multi level category axis labels
2368 pFS->startElement(FSNS(XML_c, XML_multiLvlStrRef));
2369
2370 pFS->startElement(FSNS(XML_c, XML_f));
2371 pFS->writeEscaped(aCellRange);
2372 pFS->endElement(FSNS(XML_c, XML_f));
2373
2374 pFS->startElement(FSNS(XML_c, XML_multiLvlStrCache));
2375 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(aFinalSplitSource[0].getLength()));
2376 for(const auto& rSeq : aFinalSplitSource)
2377 {
2378 pFS->startElement(FSNS(XML_c, XML_lvl));
2379 for(sal_Int32 j = 0; j < rSeq.getLength(); j++)
2380 {
2381 if(!rSeq[j].isEmpty())
2382 {
2383 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(j));
2384 pFS->startElement(FSNS(XML_c, XML_v));
2385 pFS->writeEscaped(rSeq[j]);
2386 pFS->endElement(FSNS(XML_c, XML_v));
2387 pFS->endElement(FSNS(XML_c, XML_pt));
2388 }
2389 }
2390 pFS->endElement(FSNS(XML_c, XML_lvl));
2391 }
2392
2393 pFS->endElement(FSNS(XML_c, XML_multiLvlStrCache));
2394 pFS->endElement(FSNS(XML_c, XML_multiLvlStrRef));
2395 }
2396 else
2397 {
2398 // export single category axis labels
2399 pFS->startElement(FSNS(XML_c, XML_strRef));
2400
2401 pFS->startElement(FSNS(XML_c, XML_f));
2402 pFS->writeEscaped(aCellRange);
2403 pFS->endElement(FSNS(XML_c, XML_f));
2404
2405 ::std::vector< OUString > aCategories;
2406 lcl_fillCategoriesIntoStringVector(xValueSeq, aCategories);
2407 sal_Int32 ptCount = aCategories.size();
2408 pFS->startElement(FSNS(XML_c, XML_strCache));
2409 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount));
2410 for (sal_Int32 i = 0; i < ptCount; i++)
2411 {
2412 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i));
2413 pFS->startElement(FSNS(XML_c, XML_v));
2414 pFS->writeEscaped(aCategories[i]);
2415 pFS->endElement(FSNS(XML_c, XML_v));
2416 pFS->endElement(FSNS(XML_c, XML_pt));
2417 }
2418
2419 pFS->endElement(FSNS(XML_c, XML_strCache));
2420 pFS->endElement(FSNS(XML_c, XML_strRef));
2421 }
2422
2423 pFS->endElement( FSNS( XML_c, XML_cat ) );
2424 }
2425
exportSeriesValues(const Reference<chart2::data::XDataSequence> & xValueSeq,sal_Int32 nValueType)2426 void ChartExport::exportSeriesValues( const Reference< chart2::data::XDataSequence > & xValueSeq, sal_Int32 nValueType )
2427 {
2428 FSHelperPtr pFS = GetFS();
2429 pFS->startElement(FSNS(XML_c, nValueType));
2430
2431 OUString aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString();
2432 aCellRange = parseFormula( aCellRange );
2433 // TODO: need to handle XML_multiLvlStrRef according to aCellRange
2434 pFS->startElement(FSNS(XML_c, XML_numRef));
2435
2436 pFS->startElement(FSNS(XML_c, XML_f));
2437 pFS->writeEscaped( aCellRange );
2438 pFS->endElement( FSNS( XML_c, XML_f ) );
2439
2440 ::std::vector< double > aValues = lcl_getAllValuesFromSequence( xValueSeq );
2441 sal_Int32 ptCount = aValues.size();
2442 pFS->startElement(FSNS(XML_c, XML_numCache));
2443 pFS->startElement(FSNS(XML_c, XML_formatCode));
2444 // TODO: what format code?
2445 pFS->writeEscaped( "General" );
2446 pFS->endElement( FSNS( XML_c, XML_formatCode ) );
2447 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount));
2448
2449 for( sal_Int32 i = 0; i < ptCount; i++ )
2450 {
2451 if (!rtl::math::isNan(aValues[i]))
2452 {
2453 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i));
2454 pFS->startElement(FSNS(XML_c, XML_v));
2455 pFS->write(aValues[i]);
2456 pFS->endElement(FSNS(XML_c, XML_v));
2457 pFS->endElement(FSNS(XML_c, XML_pt));
2458 }
2459 }
2460
2461 pFS->endElement( FSNS( XML_c, XML_numCache ) );
2462 pFS->endElement( FSNS( XML_c, XML_numRef ) );
2463 pFS->endElement( FSNS( XML_c, nValueType ) );
2464 }
2465
exportShapeProps(const Reference<XPropertySet> & xPropSet)2466 void ChartExport::exportShapeProps( const Reference< XPropertySet >& xPropSet )
2467 {
2468 FSHelperPtr pFS = GetFS();
2469 pFS->startElement(FSNS(XML_c, XML_spPr));
2470
2471 exportFill( xPropSet );
2472 WriteOutline( xPropSet, getModel() );
2473
2474 pFS->endElement( FSNS( XML_c, XML_spPr ) );
2475 }
2476
exportTextProps(const Reference<XPropertySet> & xPropSet)2477 void ChartExport::exportTextProps(const Reference<XPropertySet>& xPropSet)
2478 {
2479 FSHelperPtr pFS = GetFS();
2480 pFS->startElement(FSNS(XML_c, XML_txPr));
2481
2482 sal_Int32 nRotation = 0;
2483 if (auto xServiceInfo = uno::Reference<lang::XServiceInfo>(xPropSet, uno::UNO_QUERY))
2484 {
2485 double fMultiplier = 0;
2486 // We have at least two possible units of returned value: degrees (e.g., for data labels),
2487 // and 100ths of degree (e.g., for axes labels). The latter is returned as an Any wrapping
2488 // a sal_Int32 value (see WrappedTextRotationProperty::convertInnerToOuterValue), while
2489 // the former is double. So we could test the contained type to decide which multiplier to
2490 // use. But testing the service info should be more robust.
2491 if (xServiceInfo->supportsService("com.sun.star.chart.ChartAxis"))
2492 fMultiplier = -600.0;
2493 else if (xServiceInfo->supportsService("com.sun.star.chart2.DataSeries"))
2494 fMultiplier = -60000.0;
2495
2496 if (fMultiplier)
2497 {
2498 double fTextRotation = 0.0;
2499 uno::Any aAny = xPropSet->getPropertyValue("TextRotation");
2500 if (aAny.hasValue() && (aAny >>= fTextRotation))
2501 {
2502 // The MS Office UI allows values only in range of [-90,90].
2503 if (fTextRotation > 9000.0 && fTextRotation < 27000.0)
2504 {
2505 // Reflect the angle if the value is between 90° and 270°
2506 fTextRotation -= 18000.0;
2507 }
2508 else if (fTextRotation >=27000.0)
2509 {
2510 fTextRotation -= 36000.0;
2511 }
2512 nRotation = std::round(fTextRotation * fMultiplier);
2513 }
2514 }
2515 }
2516
2517 if (nRotation)
2518 pFS->singleElement(FSNS(XML_a, XML_bodyPr), XML_rot, OString::number(nRotation));
2519 else
2520 pFS->singleElement(FSNS(XML_a, XML_bodyPr));
2521
2522 pFS->singleElement(FSNS(XML_a, XML_lstStyle));
2523
2524 pFS->startElement(FSNS(XML_a, XML_p));
2525 pFS->startElement(FSNS(XML_a, XML_pPr));
2526
2527 WriteRunProperties(xPropSet, false, XML_defRPr, true, o3tl::temporary(false),
2528 o3tl::temporary(sal_Int32()));
2529
2530 pFS->endElement(FSNS(XML_a, XML_pPr));
2531 pFS->endElement(FSNS(XML_a, XML_p));
2532 pFS->endElement(FSNS(XML_c, XML_txPr));
2533 }
2534
InitPlotArea()2535 void ChartExport::InitPlotArea( )
2536 {
2537 Reference< XPropertySet > xDiagramProperties (mxDiagram, uno::UNO_QUERY);
2538
2539 // Check for supported services and then the properties provided by this service.
2540 Reference<lang::XServiceInfo> xServiceInfo (mxDiagram, uno::UNO_QUERY);
2541 if (xServiceInfo.is())
2542 {
2543 if (xServiceInfo->supportsService("com.sun.star.chart.ChartAxisZSupplier"))
2544 {
2545 xDiagramProperties->getPropertyValue("HasZAxis") >>= mbHasZAxis;
2546 }
2547 }
2548
2549 xDiagramProperties->getPropertyValue("Dim3D") >>= mbIs3DChart;
2550
2551 if( mbHasCategoryLabels && mxNewDiagram.is())
2552 {
2553 Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( mxNewDiagram ) );
2554 if( xCategories.is() )
2555 {
2556 mxCategoriesValues.set( xCategories->getValues() );
2557 }
2558 }
2559 }
2560
exportAxes()2561 void ChartExport::exportAxes( )
2562 {
2563 sal_Int32 nSize = maAxes.size();
2564 // let's export the axis types in the right order
2565 for ( sal_Int32 nSortIdx = AXIS_PRIMARY_X; nSortIdx <= AXIS_SECONDARY_Y; nSortIdx++ )
2566 {
2567 for ( sal_Int32 nIdx = 0; nIdx < nSize; nIdx++ )
2568 {
2569 if (nSortIdx == maAxes[nIdx].nAxisType)
2570 exportAxis( maAxes[nIdx] );
2571 }
2572 }
2573 }
2574
2575 namespace {
2576
getXAxisType(sal_Int32 eChartType)2577 sal_Int32 getXAxisType(sal_Int32 eChartType)
2578 {
2579 if( (eChartType == chart::TYPEID_SCATTER)
2580 || (eChartType == chart::TYPEID_BUBBLE) )
2581 return XML_valAx;
2582 else if( eChartType == chart::TYPEID_STOCK )
2583 return XML_dateAx;
2584
2585 return XML_catAx;
2586 }
2587
2588 }
2589
exportAxis(const AxisIdPair & rAxisIdPair)2590 void ChartExport::exportAxis(const AxisIdPair& rAxisIdPair)
2591 {
2592 // get some properties from document first
2593 bool bHasXAxisTitle = false,
2594 bHasYAxisTitle = false,
2595 bHasZAxisTitle = false,
2596 bHasSecondaryXAxisTitle = false,
2597 bHasSecondaryYAxisTitle = false;
2598 bool bHasXAxisMajorGrid = false,
2599 bHasXAxisMinorGrid = false,
2600 bHasYAxisMajorGrid = false,
2601 bHasYAxisMinorGrid = false,
2602 bHasZAxisMajorGrid = false,
2603 bHasZAxisMinorGrid = false;
2604
2605 Reference< XPropertySet > xDiagramProperties (mxDiagram, uno::UNO_QUERY);
2606
2607 xDiagramProperties->getPropertyValue("HasXAxisTitle") >>= bHasXAxisTitle;
2608 xDiagramProperties->getPropertyValue("HasYAxisTitle") >>= bHasYAxisTitle;
2609 xDiagramProperties->getPropertyValue("HasZAxisTitle") >>= bHasZAxisTitle;
2610 xDiagramProperties->getPropertyValue("HasSecondaryXAxisTitle") >>= bHasSecondaryXAxisTitle;
2611 xDiagramProperties->getPropertyValue("HasSecondaryYAxisTitle") >>= bHasSecondaryYAxisTitle;
2612
2613 xDiagramProperties->getPropertyValue("HasXAxisGrid") >>= bHasXAxisMajorGrid;
2614 xDiagramProperties->getPropertyValue("HasYAxisGrid") >>= bHasYAxisMajorGrid;
2615 xDiagramProperties->getPropertyValue("HasZAxisGrid") >>= bHasZAxisMajorGrid;
2616
2617 xDiagramProperties->getPropertyValue("HasXAxisHelpGrid") >>= bHasXAxisMinorGrid;
2618 xDiagramProperties->getPropertyValue("HasYAxisHelpGrid") >>= bHasYAxisMinorGrid;
2619 xDiagramProperties->getPropertyValue("HasZAxisHelpGrid") >>= bHasZAxisMinorGrid;
2620
2621 Reference< XPropertySet > xAxisProp;
2622 Reference< drawing::XShape > xAxisTitle;
2623 Reference< beans::XPropertySet > xMajorGrid;
2624 Reference< beans::XPropertySet > xMinorGrid;
2625 sal_Int32 nAxisType = XML_catAx;
2626 const char* sAxPos = nullptr;
2627
2628 switch( rAxisIdPair.nAxisType )
2629 {
2630 case AXIS_PRIMARY_X:
2631 {
2632 Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY );
2633 if( xAxisXSupp.is())
2634 xAxisProp = xAxisXSupp->getXAxis();
2635 if( bHasXAxisTitle )
2636 xAxisTitle = xAxisXSupp->getXAxisTitle();
2637 if( bHasXAxisMajorGrid )
2638 xMajorGrid = xAxisXSupp->getXMainGrid();
2639 if( bHasXAxisMinorGrid )
2640 xMinorGrid = xAxisXSupp->getXHelpGrid();
2641
2642 sal_Int32 eChartType = getChartType();
2643 nAxisType = getXAxisType(eChartType);
2644 // FIXME: axPos, need to check axis direction
2645 sAxPos = "b";
2646 break;
2647 }
2648 case AXIS_PRIMARY_Y:
2649 {
2650 Reference< css::chart::XAxisYSupplier > xAxisYSupp( mxDiagram, uno::UNO_QUERY );
2651 if( xAxisYSupp.is())
2652 xAxisProp = xAxisYSupp->getYAxis();
2653 if( bHasYAxisTitle )
2654 xAxisTitle = xAxisYSupp->getYAxisTitle();
2655 if( bHasYAxisMajorGrid )
2656 xMajorGrid = xAxisYSupp->getYMainGrid();
2657 if( bHasYAxisMinorGrid )
2658 xMinorGrid = xAxisYSupp->getYHelpGrid();
2659
2660 nAxisType = XML_valAx;
2661 // FIXME: axPos, need to check axis direction
2662 sAxPos = "l";
2663 break;
2664 }
2665 case AXIS_PRIMARY_Z:
2666 {
2667 Reference< css::chart::XAxisZSupplier > xAxisZSupp( mxDiagram, uno::UNO_QUERY );
2668 if( xAxisZSupp.is())
2669 xAxisProp = xAxisZSupp->getZAxis();
2670 if( bHasZAxisTitle )
2671 xAxisTitle = xAxisZSupp->getZAxisTitle();
2672 if( bHasZAxisMajorGrid )
2673 xMajorGrid = xAxisZSupp->getZMainGrid();
2674 if( bHasZAxisMinorGrid )
2675 xMinorGrid = xAxisZSupp->getZHelpGrid();
2676
2677 sal_Int32 eChartType = getChartType( );
2678 if( (eChartType == chart::TYPEID_SCATTER)
2679 || (eChartType == chart::TYPEID_BUBBLE) )
2680 nAxisType = XML_valAx;
2681 else if( eChartType == chart::TYPEID_STOCK )
2682 nAxisType = XML_dateAx;
2683 else if( eChartType == chart::TYPEID_BAR )
2684 nAxisType = XML_serAx;
2685 // FIXME: axPos, need to check axis direction
2686 sAxPos = "b";
2687 break;
2688 }
2689 case AXIS_SECONDARY_X:
2690 {
2691 Reference< css::chart::XTwoAxisXSupplier > xAxisTwoXSupp( mxDiagram, uno::UNO_QUERY );
2692 if( xAxisTwoXSupp.is())
2693 xAxisProp = xAxisTwoXSupp->getSecondaryXAxis();
2694 if( bHasSecondaryXAxisTitle )
2695 {
2696 Reference< css::chart::XSecondAxisTitleSupplier > xAxisSupp( mxDiagram, uno::UNO_QUERY );
2697 xAxisTitle = xAxisSupp->getSecondXAxisTitle();
2698 }
2699
2700 sal_Int32 eChartType = getChartType();
2701 nAxisType = getXAxisType(eChartType);
2702 // FIXME: axPos, need to check axis direction
2703 sAxPos = "t";
2704 break;
2705 }
2706 case AXIS_SECONDARY_Y:
2707 {
2708 Reference< css::chart::XTwoAxisYSupplier > xAxisTwoYSupp( mxDiagram, uno::UNO_QUERY );
2709 if( xAxisTwoYSupp.is())
2710 xAxisProp = xAxisTwoYSupp->getSecondaryYAxis();
2711 if( bHasSecondaryYAxisTitle )
2712 {
2713 Reference< css::chart::XSecondAxisTitleSupplier > xAxisSupp( mxDiagram, uno::UNO_QUERY );
2714 xAxisTitle = xAxisSupp->getSecondYAxisTitle();
2715 }
2716
2717 nAxisType = XML_valAx;
2718 // FIXME: axPos, need to check axis direction
2719 sAxPos = "r";
2720 break;
2721 }
2722 }
2723
2724 _exportAxis(xAxisProp, xAxisTitle, xMajorGrid, xMinorGrid, nAxisType, sAxPos, rAxisIdPair);
2725 }
2726
_exportAxis(const Reference<XPropertySet> & xAxisProp,const Reference<drawing::XShape> & xAxisTitle,const Reference<XPropertySet> & xMajorGrid,const Reference<XPropertySet> & xMinorGrid,sal_Int32 nAxisType,const char * sAxisPos,const AxisIdPair & rAxisIdPair)2727 void ChartExport::_exportAxis(
2728 const Reference< XPropertySet >& xAxisProp,
2729 const Reference< drawing::XShape >& xAxisTitle,
2730 const Reference< XPropertySet >& xMajorGrid,
2731 const Reference< XPropertySet >& xMinorGrid,
2732 sal_Int32 nAxisType,
2733 const char* sAxisPos,
2734 const AxisIdPair& rAxisIdPair )
2735 {
2736 FSHelperPtr pFS = GetFS();
2737 pFS->startElement(FSNS(XML_c, nAxisType));
2738 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(rAxisIdPair.nAxisId));
2739
2740 pFS->startElement(FSNS(XML_c, XML_scaling));
2741
2742 // logBase, min, max
2743 if(GetProperty( xAxisProp, "Logarithmic" ) )
2744 {
2745 bool bLogarithmic = false;
2746 mAny >>= bLogarithmic;
2747 if( bLogarithmic )
2748 {
2749 // default value is 10?
2750 pFS->singleElement(FSNS(XML_c, XML_logBase), XML_val, OString::number(10));
2751 }
2752 }
2753
2754 // orientation: minMax, maxMin
2755 bool bReverseDirection = false;
2756 if(GetProperty( xAxisProp, "ReverseDirection" ) )
2757 mAny >>= bReverseDirection;
2758
2759 const char* orientation = bReverseDirection ? "maxMin":"minMax";
2760 pFS->singleElement(FSNS(XML_c, XML_orientation), XML_val, orientation);
2761
2762 bool bAutoMax = false;
2763 if(GetProperty( xAxisProp, "AutoMax" ) )
2764 mAny >>= bAutoMax;
2765
2766 if( !bAutoMax && (GetProperty( xAxisProp, "Max" ) ) )
2767 {
2768 double dMax = 0;
2769 mAny >>= dMax;
2770 pFS->singleElement(FSNS(XML_c, XML_max), XML_val, OString::number(dMax));
2771 }
2772
2773 bool bAutoMin = false;
2774 if(GetProperty( xAxisProp, "AutoMin" ) )
2775 mAny >>= bAutoMin;
2776
2777 if( !bAutoMin && (GetProperty( xAxisProp, "Min" ) ) )
2778 {
2779 double dMin = 0;
2780 mAny >>= dMin;
2781 pFS->singleElement(FSNS(XML_c, XML_min), XML_val, OString::number(dMin));
2782 }
2783
2784 pFS->endElement( FSNS( XML_c, XML_scaling ) );
2785
2786 bool bVisible = true;
2787 if( xAxisProp.is() )
2788 {
2789 xAxisProp->getPropertyValue("Visible") >>= bVisible;
2790 }
2791
2792 // only export each axis only once non-deleted
2793 bool bDeleted = maExportedAxis.find(rAxisIdPair.nAxisType) != maExportedAxis.end();
2794
2795 if (!bDeleted)
2796 maExportedAxis.insert(rAxisIdPair.nAxisType);
2797
2798 pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, !bDeleted && bVisible ? "0" : "1");
2799
2800 // FIXME: axPos, need to check the property "ReverseDirection"
2801 pFS->singleElement(FSNS(XML_c, XML_axPos), XML_val, sAxisPos);
2802 // major grid line
2803 if( xMajorGrid.is())
2804 {
2805 pFS->startElement(FSNS(XML_c, XML_majorGridlines));
2806 exportShapeProps( xMajorGrid );
2807 pFS->endElement( FSNS( XML_c, XML_majorGridlines ) );
2808 }
2809
2810 // minor grid line
2811 if( xMinorGrid.is())
2812 {
2813 pFS->startElement(FSNS(XML_c, XML_minorGridlines));
2814 exportShapeProps( xMinorGrid );
2815 pFS->endElement( FSNS( XML_c, XML_minorGridlines ) );
2816 }
2817
2818 // title
2819 if( xAxisTitle.is() )
2820 exportTitle( xAxisTitle );
2821
2822 bool bLinkedNumFmt = true;
2823 if (GetProperty(xAxisProp, "LinkNumberFormatToSource"))
2824 mAny >>= bLinkedNumFmt;
2825
2826 OUString aNumberFormatString("General");
2827 if (GetProperty(xAxisProp, "NumberFormat"))
2828 {
2829 sal_Int32 nKey = 0;
2830 mAny >>= nKey;
2831 aNumberFormatString = getNumberFormatCode(nKey);
2832 }
2833
2834 OString sNumberFormatString = OUStringToOString(aNumberFormatString, RTL_TEXTENCODING_UTF8);
2835 pFS->singleElement(FSNS(XML_c, XML_numFmt),
2836 XML_formatCode, sNumberFormatString.getStr(),
2837 XML_sourceLinked, bLinkedNumFmt ? "1" : "0");
2838
2839 // majorTickMark
2840 sal_Int32 nValue = 0;
2841 if(GetProperty( xAxisProp, "Marks" ) )
2842 {
2843 mAny >>= nValue;
2844 bool bInner = nValue & css::chart::ChartAxisMarks::INNER;
2845 bool bOuter = nValue & css::chart::ChartAxisMarks::OUTER;
2846 const char* majorTickMark = nullptr;
2847 if( bInner && bOuter )
2848 majorTickMark = "cross";
2849 else if( bInner )
2850 majorTickMark = "in";
2851 else if( bOuter )
2852 majorTickMark = "out";
2853 else
2854 majorTickMark = "none";
2855 pFS->singleElement(FSNS(XML_c, XML_majorTickMark), XML_val, majorTickMark);
2856 }
2857 // minorTickMark
2858 if(GetProperty( xAxisProp, "HelpMarks" ) )
2859 {
2860 mAny >>= nValue;
2861 bool bInner = nValue & css::chart::ChartAxisMarks::INNER;
2862 bool bOuter = nValue & css::chart::ChartAxisMarks::OUTER;
2863 const char* minorTickMark = nullptr;
2864 if( bInner && bOuter )
2865 minorTickMark = "cross";
2866 else if( bInner )
2867 minorTickMark = "in";
2868 else if( bOuter )
2869 minorTickMark = "out";
2870 else
2871 minorTickMark = "none";
2872 pFS->singleElement(FSNS(XML_c, XML_minorTickMark), XML_val, minorTickMark);
2873 }
2874 // tickLblPos
2875 const char* sTickLblPos = nullptr;
2876 bool bDisplayLabel = true;
2877 if(GetProperty( xAxisProp, "DisplayLabels" ) )
2878 mAny >>= bDisplayLabel;
2879 if( bDisplayLabel && (GetProperty( xAxisProp, "LabelPosition" ) ) )
2880 {
2881 css::chart::ChartAxisLabelPosition eLabelPosition = css::chart::ChartAxisLabelPosition_NEAR_AXIS;
2882 mAny >>= eLabelPosition;
2883 switch( eLabelPosition )
2884 {
2885 case css::chart::ChartAxisLabelPosition_NEAR_AXIS:
2886 case css::chart::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE:
2887 sTickLblPos = "nextTo";
2888 break;
2889 case css::chart::ChartAxisLabelPosition_OUTSIDE_START:
2890 sTickLblPos = "low";
2891 break;
2892 case css::chart::ChartAxisLabelPosition_OUTSIDE_END:
2893 sTickLblPos = "high";
2894 break;
2895 default:
2896 sTickLblPos = "nextTo";
2897 break;
2898 }
2899 }
2900 else
2901 {
2902 sTickLblPos = "none";
2903 }
2904 pFS->singleElement(FSNS(XML_c, XML_tickLblPos), XML_val, sTickLblPos);
2905
2906 // shape properties
2907 exportShapeProps( xAxisProp );
2908
2909 exportTextProps(xAxisProp);
2910
2911 pFS->singleElement(FSNS(XML_c, XML_crossAx), XML_val, OString::number(rAxisIdPair.nCrossAx));
2912
2913 // crosses & crossesAt
2914 bool bCrossesValue = false;
2915 const char* sCrosses = nullptr;
2916 // do not export the CrossoverPosition/CrossoverValue, if the axis is deleted and not visible
2917 if( GetProperty( xAxisProp, "CrossoverPosition" ) && !bDeleted && bVisible )
2918 {
2919 css::chart::ChartAxisPosition ePosition( css::chart::ChartAxisPosition_ZERO );
2920 mAny >>= ePosition;
2921 switch( ePosition )
2922 {
2923 case css::chart::ChartAxisPosition_START:
2924 sCrosses = "min";
2925 break;
2926 case css::chart::ChartAxisPosition_END:
2927 sCrosses = "max";
2928 break;
2929 case css::chart::ChartAxisPosition_ZERO:
2930 sCrosses = "autoZero";
2931 break;
2932 default:
2933 bCrossesValue = true;
2934 break;
2935 }
2936 }
2937
2938 if( bCrossesValue && GetProperty( xAxisProp, "CrossoverValue" ) )
2939 {
2940 double dValue = 0;
2941 mAny >>= dValue;
2942 pFS->singleElement(FSNS(XML_c, XML_crossesAt), XML_val, OString::number(dValue));
2943 }
2944 else
2945 {
2946 if(sCrosses)
2947 {
2948 pFS->singleElement(FSNS(XML_c, XML_crosses), XML_val, sCrosses);
2949 }
2950 }
2951
2952 if( ( nAxisType == XML_catAx )
2953 || ( nAxisType == XML_dateAx ) )
2954 {
2955 // FIXME: seems not support? use default value,
2956 const char* const isAuto = "1";
2957 pFS->singleElement(FSNS(XML_c, XML_auto), XML_val, isAuto);
2958
2959 if( nAxisType == XML_catAx )
2960 {
2961 // FIXME: seems not support? lblAlgn
2962 const char* const sLblAlgn = "ctr";
2963 pFS->singleElement(FSNS(XML_c, XML_lblAlgn), XML_val, sLblAlgn);
2964 }
2965
2966 // FIXME: seems not support? lblOffset
2967 pFS->singleElement(FSNS(XML_c, XML_lblOffset), XML_val, OString::number(100));
2968
2969 // FIXME: seems not support? noMultiLvlLbl
2970 pFS->singleElement(FSNS(XML_c, XML_noMultiLvlLbl), XML_val, OString::number(0));
2971 }
2972
2973 // crossBetween
2974 if( nAxisType == XML_valAx )
2975 {
2976 if( mbIsCategoryPositionShifted )
2977 pFS->singleElement(FSNS(XML_c, XML_crossBetween), XML_val, "between");
2978 else
2979 pFS->singleElement(FSNS(XML_c, XML_crossBetween), XML_val, "midCat");
2980 }
2981
2982 // majorUnit
2983 bool bAutoStepMain = false;
2984 if(GetProperty( xAxisProp, "AutoStepMain" ) )
2985 mAny >>= bAutoStepMain;
2986
2987 if( !bAutoStepMain && (GetProperty( xAxisProp, "StepMain" ) ) )
2988 {
2989 double dMajorUnit = 0;
2990 mAny >>= dMajorUnit;
2991 pFS->singleElement(FSNS(XML_c, XML_majorUnit), XML_val, OString::number(dMajorUnit));
2992 }
2993 // minorUnit
2994 bool bAutoStepHelp = false;
2995 if(GetProperty( xAxisProp, "AutoStepHelp" ) )
2996 mAny >>= bAutoStepHelp;
2997
2998 if( !bAutoStepHelp && (GetProperty( xAxisProp, "StepHelp" ) ) )
2999 {
3000 double dMinorUnit = 0;
3001 mAny >>= dMinorUnit;
3002 if( GetProperty( xAxisProp, "StepHelpCount" ) )
3003 {
3004 sal_Int32 dMinorUnitCount = 0;
3005 mAny >>= dMinorUnitCount;
3006 // tdf#114168 Don't save minor unit if number of step help count is 5 (which is default for MS Excel),
3007 // to allow proper .xlsx import. If minorUnit is set and majorUnit not, then it is impossible
3008 // to calculate StepHelpCount.
3009 if( dMinorUnitCount != 5 )
3010 {
3011 pFS->singleElement( FSNS( XML_c, XML_minorUnit ),
3012 XML_val, OString::number( dMinorUnit ) );
3013 }
3014 }
3015 }
3016
3017 if( nAxisType == XML_valAx && GetProperty( xAxisProp, "DisplayUnits" ) )
3018 {
3019 bool bDisplayUnits = false;
3020 mAny >>= bDisplayUnits;
3021 if(bDisplayUnits)
3022 {
3023 OUString aVal;
3024 if(GetProperty( xAxisProp, "BuiltInUnit" ))
3025 {
3026 mAny >>= aVal;
3027 if(!aVal.isEmpty())
3028 {
3029 pFS->startElement(FSNS(XML_c, XML_dispUnits));
3030
3031 pFS->singleElement(FSNS(XML_c, XML_builtInUnit), XML_val, aVal.toUtf8());
3032
3033 pFS->singleElement(FSNS( XML_c, XML_dispUnitsLbl ));
3034 pFS->endElement( FSNS( XML_c, XML_dispUnits ) );
3035 }
3036 }
3037 }
3038 }
3039
3040 pFS->endElement( FSNS( XML_c, nAxisType ) );
3041 }
3042
3043 namespace {
3044
3045 struct LabelPlacementParam
3046 {
3047 bool mbExport;
3048 sal_Int32 meDefault;
3049
3050 std::unordered_set<sal_Int32> maAllowedValues;
3051
LabelPlacementParamoox::drawingml::__anona986c9d00711::LabelPlacementParam3052 LabelPlacementParam() :
3053 mbExport(true),
3054 meDefault(css::chart::DataLabelPlacement::OUTSIDE) {}
3055
allowAlloox::drawingml::__anona986c9d00711::LabelPlacementParam3056 void allowAll()
3057 {
3058 maAllowedValues.insert(css::chart::DataLabelPlacement::OUTSIDE);
3059 maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE);
3060 maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER);
3061 maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN);
3062 maAllowedValues.insert(css::chart::DataLabelPlacement::TOP);
3063 maAllowedValues.insert(css::chart::DataLabelPlacement::BOTTOM);
3064 maAllowedValues.insert(css::chart::DataLabelPlacement::LEFT);
3065 maAllowedValues.insert(css::chart::DataLabelPlacement::RIGHT);
3066 maAllowedValues.insert(css::chart::DataLabelPlacement::AVOID_OVERLAP);
3067 }
3068 };
3069
toOOXMLPlacement(sal_Int32 nPlacement)3070 const char* toOOXMLPlacement( sal_Int32 nPlacement )
3071 {
3072 switch (nPlacement)
3073 {
3074 case css::chart::DataLabelPlacement::OUTSIDE: return "outEnd";
3075 case css::chart::DataLabelPlacement::INSIDE: return "inEnd";
3076 case css::chart::DataLabelPlacement::CENTER: return "ctr";
3077 case css::chart::DataLabelPlacement::NEAR_ORIGIN: return "inBase";
3078 case css::chart::DataLabelPlacement::TOP: return "t";
3079 case css::chart::DataLabelPlacement::BOTTOM: return "b";
3080 case css::chart::DataLabelPlacement::LEFT: return "l";
3081 case css::chart::DataLabelPlacement::RIGHT: return "r";
3082 case css::chart::DataLabelPlacement::AVOID_OVERLAP: return "bestFit";
3083 default:
3084 ;
3085 }
3086
3087 return "outEnd";
3088 }
3089
getFieldTypeString(const chart2::DataPointCustomLabelFieldType aType)3090 OUString getFieldTypeString( const chart2::DataPointCustomLabelFieldType aType )
3091 {
3092 switch (aType)
3093 {
3094 case chart2::DataPointCustomLabelFieldType_CATEGORYNAME:
3095 return "CATEGORYNAME";
3096
3097 case chart2::DataPointCustomLabelFieldType_SERIESNAME:
3098 return "SERIESNAME";
3099
3100 case chart2::DataPointCustomLabelFieldType_VALUE:
3101 return "VALUE";
3102
3103 case chart2::DataPointCustomLabelFieldType_CELLREF:
3104 return "CELLREF";
3105
3106 default:
3107 break;
3108 }
3109 return OUString();
3110 }
3111
writeRunProperties(ChartExport * pChartExport,Reference<XPropertySet> const & xPropertySet)3112 void writeRunProperties( ChartExport* pChartExport, Reference<XPropertySet> const & xPropertySet )
3113 {
3114 bool bDummy = false;
3115 sal_Int32 nDummy;
3116 pChartExport->WriteRunProperties(xPropertySet, false, XML_rPr, true, bDummy, nDummy);
3117 }
3118
writeCustomLabel(const FSHelperPtr & pFS,ChartExport * pChartExport,const Sequence<Reference<chart2::XDataPointCustomLabelField>> & rCustomLabelFields)3119 void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport,
3120 const Sequence<Reference<chart2::XDataPointCustomLabelField>>& rCustomLabelFields )
3121 {
3122 pFS->startElement(FSNS(XML_c, XML_tx));
3123 pFS->startElement(FSNS(XML_c, XML_rich));
3124
3125 // TODO: body properties?
3126 pFS->singleElement(FSNS(XML_a, XML_bodyPr));
3127
3128 OUString sFieldType;
3129 pFS->startElement(FSNS(XML_a, XML_p));
3130
3131 for (auto& rField : rCustomLabelFields)
3132 {
3133 Reference<XPropertySet> xPropertySet(rField, UNO_QUERY);
3134 chart2::DataPointCustomLabelFieldType aType = rField->getFieldType();
3135 sFieldType.clear();
3136 bool bNewParagraph = false;
3137
3138 if (aType == chart2::DataPointCustomLabelFieldType_NEWLINE)
3139 bNewParagraph = true;
3140 else if (aType != chart2::DataPointCustomLabelFieldType_TEXT)
3141 sFieldType = getFieldTypeString(aType);
3142
3143 if (bNewParagraph)
3144 {
3145 pFS->endElement(FSNS(XML_a, XML_p));
3146 pFS->startElement(FSNS(XML_a, XML_p));
3147 continue;
3148 }
3149
3150 if (sFieldType.isEmpty())
3151 {
3152 // Normal text run
3153 pFS->startElement(FSNS(XML_a, XML_r));
3154 writeRunProperties(pChartExport, xPropertySet);
3155
3156 pFS->startElement(FSNS(XML_a, XML_t));
3157 pFS->writeEscaped(rField->getString());
3158 pFS->endElement(FSNS(XML_a, XML_t));
3159
3160 pFS->endElement(FSNS(XML_a, XML_r));
3161 }
3162 else
3163 {
3164 // Field
3165 pFS->startElement(FSNS(XML_a, XML_fld), XML_id, rField->getGuid().toUtf8(), XML_type,
3166 sFieldType.toUtf8());
3167 writeRunProperties(pChartExport, xPropertySet);
3168
3169 pFS->startElement(FSNS(XML_a, XML_t));
3170 pFS->writeEscaped(rField->getString());
3171 pFS->endElement(FSNS(XML_a, XML_t));
3172
3173 pFS->endElement(FSNS(XML_a, XML_fld));
3174 }
3175 }
3176
3177 pFS->endElement(FSNS(XML_a, XML_p));
3178 pFS->endElement(FSNS(XML_c, XML_rich));
3179 pFS->endElement(FSNS(XML_c, XML_tx));
3180 }
3181
writeLabelProperties(const FSHelperPtr & pFS,ChartExport * pChartExport,const uno::Reference<beans::XPropertySet> & xPropSet,const LabelPlacementParam & rLabelParam)3182 void writeLabelProperties( const FSHelperPtr& pFS, ChartExport* pChartExport,
3183 const uno::Reference<beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam )
3184 {
3185 if (!xPropSet.is())
3186 return;
3187
3188 chart2::DataPointLabel aLabel;
3189 Sequence<Reference<chart2::XDataPointCustomLabelField>> aCustomLabelFields;
3190 sal_Int32 nLabelBorderWidth = 0;
3191 sal_Int32 nLabelBorderColor = 0x00FFFFFF;
3192
3193 xPropSet->getPropertyValue("Label") >>= aLabel;
3194 xPropSet->getPropertyValue("CustomLabelFields") >>= aCustomLabelFields;
3195 xPropSet->getPropertyValue("LabelBorderWidth") >>= nLabelBorderWidth;
3196 xPropSet->getPropertyValue("LabelBorderColor") >>= nLabelBorderColor;
3197
3198 if (nLabelBorderWidth > 0)
3199 {
3200 pFS->startElement(FSNS(XML_c, XML_spPr));
3201 pFS->startElement(FSNS(XML_a, XML_ln), XML_w,
3202 OString::number(convertHmmToEmu(nLabelBorderWidth)));
3203 if (nLabelBorderColor != -1)
3204 {
3205 pFS->startElement(FSNS(XML_a, XML_solidFill));
3206
3207 OString aStr = OString::number(nLabelBorderColor, 16).toAsciiUpperCase();
3208 pFS->singleElement(FSNS(XML_a, XML_srgbClr), XML_val, aStr);
3209
3210 pFS->endElement(FSNS(XML_a, XML_solidFill));
3211 }
3212 pFS->endElement(FSNS(XML_a, XML_ln));
3213 pFS->endElement(FSNS(XML_c, XML_spPr));
3214 }
3215
3216 if (aCustomLabelFields.hasElements())
3217 writeCustomLabel(pFS, pChartExport, aCustomLabelFields);
3218
3219 if (rLabelParam.mbExport)
3220 {
3221 sal_Int32 nLabelPlacement = rLabelParam.meDefault;
3222 if (xPropSet->getPropertyValue("LabelPlacement") >>= nLabelPlacement)
3223 {
3224 if (!rLabelParam.maAllowedValues.count(nLabelPlacement))
3225 nLabelPlacement = rLabelParam.meDefault;
3226 pFS->singleElement(FSNS(XML_c, XML_dLblPos), XML_val, toOOXMLPlacement(nLabelPlacement));
3227 }
3228 }
3229
3230 pFS->singleElement(FSNS(XML_c, XML_showLegendKey), XML_val, ToPsz10(aLabel.ShowLegendSymbol));
3231 pFS->singleElement(FSNS(XML_c, XML_showVal), XML_val, ToPsz10(aLabel.ShowNumber));
3232 pFS->singleElement(FSNS(XML_c, XML_showCatName), XML_val, ToPsz10(aLabel.ShowCategoryName));
3233 pFS->singleElement(FSNS(XML_c, XML_showSerName), XML_val, ToPsz10(false));
3234 pFS->singleElement(FSNS(XML_c, XML_showPercent), XML_val, ToPsz10(aLabel.ShowNumberInPercent));
3235
3236 // Export the text "separator" if exists
3237 uno::Any aAny = xPropSet->getPropertyValue("LabelSeparator");
3238 if( aAny.hasValue() )
3239 {
3240 OUString nLabelSeparator;
3241 aAny >>= nLabelSeparator;
3242 pFS->startElement(FSNS(XML_c, XML_separator));
3243 pFS->writeEscaped( nLabelSeparator );
3244 pFS->endElement( FSNS( XML_c, XML_separator ) );
3245 }
3246 }
3247
3248 }
3249
exportDataLabels(const uno::Reference<chart2::XDataSeries> & xSeries,sal_Int32 nSeriesLength,sal_Int32 eChartType)3250 void ChartExport::exportDataLabels(
3251 const uno::Reference<chart2::XDataSeries> & xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType )
3252 {
3253 if (!xSeries.is() || nSeriesLength <= 0)
3254 return;
3255
3256 uno::Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY);
3257 if (!xPropSet.is())
3258 return;
3259
3260 FSHelperPtr pFS = GetFS();
3261 pFS->startElement(FSNS(XML_c, XML_dLbls));
3262
3263 bool bLinkedNumFmt = true;
3264 if (GetProperty(xPropSet, "LinkNumberFormatToSource"))
3265 mAny >>= bLinkedNumFmt;
3266
3267 chart2::DataPointLabel aLabel;
3268 bool bLabelIsNumberFormat = true;
3269 if( xPropSet->getPropertyValue("Label") >>= aLabel )
3270 bLabelIsNumberFormat = aLabel.ShowNumber;
3271
3272 if (GetProperty(xPropSet, bLabelIsNumberFormat ? OUString("NumberFormat") : OUString("PercentageNumberFormat")))
3273 {
3274 sal_Int32 nKey = 0;
3275 mAny >>= nKey;
3276
3277 OUString aNumberFormatString = getNumberFormatCode(nKey);
3278 OString sNumberFormatString = OUStringToOString(aNumberFormatString, RTL_TEXTENCODING_UTF8);
3279
3280 pFS->singleElement(FSNS(XML_c, XML_numFmt),
3281 XML_formatCode, sNumberFormatString,
3282 XML_sourceLinked, ToPsz10(bLinkedNumFmt));
3283 }
3284
3285 uno::Sequence<sal_Int32> aAttrLabelIndices;
3286 xPropSet->getPropertyValue("AttributedDataPoints") >>= aAttrLabelIndices;
3287
3288 // We must not export label placement property when the chart type doesn't
3289 // support this option in MS Office, else MS Office would think the file
3290 // is corrupt & refuse to open it.
3291
3292 const chart::TypeGroupInfo& rInfo = chart::GetTypeGroupInfo(static_cast<chart::TypeId>(eChartType));
3293 LabelPlacementParam aParam;
3294 aParam.mbExport = !mbIs3DChart;
3295 aParam.meDefault = rInfo.mnDefLabelPos;
3296 aParam.allowAll();
3297 switch (eChartType) // diagram chart type
3298 {
3299 case chart::TYPEID_PIE:
3300 if(getChartType() == chart::TYPEID_DOUGHNUT)
3301 aParam.mbExport = false;
3302 else
3303 // All pie charts support label placement.
3304 aParam.mbExport = true;
3305 break;
3306 case chart::TYPEID_AREA:
3307 case chart::TYPEID_RADARLINE:
3308 case chart::TYPEID_RADARAREA:
3309 // These chart types don't support label placement.
3310 aParam.mbExport = false;
3311 break;
3312 case chart::TYPEID_BAR:
3313 if (mbStacked || mbPercent)
3314 {
3315 aParam.maAllowedValues.clear();
3316 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER);
3317 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE);
3318 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN);
3319 aParam.meDefault = css::chart::DataLabelPlacement::CENTER;
3320 }
3321 else // Clustered bar chart
3322 {
3323 aParam.maAllowedValues.clear();
3324 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER);
3325 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE);
3326 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::OUTSIDE);
3327 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN);
3328 aParam.meDefault = css::chart::DataLabelPlacement::OUTSIDE;
3329 }
3330 break;
3331 default:
3332 ;
3333 }
3334
3335 for (const sal_Int32 nIdx : std::as_const(aAttrLabelIndices))
3336 {
3337 uno::Reference<beans::XPropertySet> xLabelPropSet = xSeries->getDataPointByIndex(nIdx);
3338
3339 if (!xLabelPropSet.is())
3340 continue;
3341
3342 pFS->startElement(FSNS(XML_c, XML_dLbl));
3343 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nIdx));
3344
3345 if( GetProperty(xLabelPropSet, "LinkNumberFormatToSource") )
3346 mAny >>= bLinkedNumFmt;
3347
3348 if( xLabelPropSet->getPropertyValue("Label") >>= aLabel )
3349 bLabelIsNumberFormat = aLabel.ShowNumber;
3350 else
3351 bLabelIsNumberFormat = true;
3352
3353 if (GetProperty(xLabelPropSet, bLabelIsNumberFormat ? OUString("NumberFormat") : OUString("PercentageNumberFormat")))
3354 {
3355 sal_Int32 nKey = 0;
3356 mAny >>= nKey;
3357
3358 OUString aNumberFormatString = getNumberFormatCode(nKey);
3359 OString sNumberFormatString = OUStringToOString(aNumberFormatString, RTL_TEXTENCODING_UTF8);
3360
3361 pFS->singleElement(FSNS(XML_c, XML_numFmt), XML_formatCode, sNumberFormatString.getStr(),
3362 XML_sourceLinked, ToPsz10(bLinkedNumFmt));
3363 }
3364
3365 // Individual label property that overwrites the baseline.
3366 exportTextProps( xLabelPropSet );
3367 writeLabelProperties(pFS, this, xLabelPropSet, aParam);
3368 pFS->endElement(FSNS(XML_c, XML_dLbl));
3369 }
3370
3371 exportTextProps( xPropSet );
3372 // Baseline label properties for all labels.
3373 writeLabelProperties(pFS, this, xPropSet, aParam);
3374
3375 pFS->singleElement(FSNS(XML_c, XML_showLeaderLines), XML_val, "0");
3376
3377 pFS->endElement(FSNS(XML_c, XML_dLbls));
3378 }
3379
exportDataPoints(const uno::Reference<beans::XPropertySet> & xSeriesProperties,sal_Int32 nSeriesLength,sal_Int32 eChartType)3380 void ChartExport::exportDataPoints(
3381 const uno::Reference< beans::XPropertySet > & xSeriesProperties,
3382 sal_Int32 nSeriesLength, sal_Int32 eChartType )
3383 {
3384 uno::Reference< chart2::XDataSeries > xSeries( xSeriesProperties, uno::UNO_QUERY );
3385 bool bVaryColorsByPoint = false;
3386 Sequence< sal_Int32 > aDataPointSeq;
3387 if( xSeriesProperties.is())
3388 {
3389 Any aAny = xSeriesProperties->getPropertyValue( "AttributedDataPoints" );
3390 aAny >>= aDataPointSeq;
3391 xSeriesProperties->getPropertyValue( "VaryColorsByPoint" ) >>= bVaryColorsByPoint;
3392 }
3393
3394 const sal_Int32 * pPoints = aDataPointSeq.getConstArray();
3395 sal_Int32 nElement;
3396 Reference< chart2::XColorScheme > xColorScheme;
3397 if( mxNewDiagram.is())
3398 xColorScheme.set( mxNewDiagram->getDefaultColorScheme());
3399
3400 if( bVaryColorsByPoint && xColorScheme.is() )
3401 {
3402 ::std::set< sal_Int32 > aAttrPointSet;
3403 ::std::copy( pPoints, pPoints + aDataPointSeq.getLength(),
3404 ::std::inserter( aAttrPointSet, aAttrPointSet.begin()));
3405 const ::std::set< sal_Int32 >::const_iterator aEndIt( aAttrPointSet.end());
3406 for( nElement = 0; nElement < nSeriesLength; ++nElement )
3407 {
3408 uno::Reference< beans::XPropertySet > xPropSet;
3409 if( aAttrPointSet.find( nElement ) != aEndIt )
3410 {
3411 try
3412 {
3413 xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
3414 xSeries, nElement, getModel() );
3415 }
3416 catch( const uno::Exception & )
3417 {
3418 DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
3419 }
3420 }
3421 else
3422 {
3423 // property set only containing the color
3424 xPropSet.set( new ColorPropertySet( xColorScheme->getColorByIndex( nElement )));
3425 }
3426
3427 if( xPropSet.is() )
3428 {
3429 FSHelperPtr pFS = GetFS();
3430 pFS->startElement(FSNS(XML_c, XML_dPt));
3431 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nElement));
3432
3433 switch (eChartType)
3434 {
3435 case chart::TYPEID_PIE:
3436 case chart::TYPEID_DOUGHNUT:
3437 {
3438 if( xPropSet.is() && GetProperty( xPropSet, "SegmentOffset") )
3439 {
3440 sal_Int32 nOffset = 0;
3441 mAny >>= nOffset;
3442 if (nOffset)
3443 pFS->singleElement( FSNS( XML_c, XML_explosion ),
3444 XML_val, OString::number( nOffset ) );
3445 }
3446 break;
3447 }
3448 default:
3449 break;
3450 }
3451 exportShapeProps( xPropSet );
3452
3453 pFS->endElement( FSNS( XML_c, XML_dPt ) );
3454 }
3455 }
3456 }
3457
3458 // Export Data Point Property in Charts even if the VaryColors is false
3459 if( !bVaryColorsByPoint )
3460 {
3461 ::std::set< sal_Int32 > aAttrPointSet;
3462 ::std::copy( pPoints, pPoints + aDataPointSeq.getLength(),
3463 ::std::inserter( aAttrPointSet, aAttrPointSet.begin()));
3464 const ::std::set< sal_Int32 >::const_iterator aEndIt( aAttrPointSet.end());
3465 for( nElement = 0; nElement < nSeriesLength; ++nElement )
3466 {
3467 uno::Reference< beans::XPropertySet > xPropSet;
3468 if( aAttrPointSet.find( nElement ) != aEndIt )
3469 {
3470 try
3471 {
3472 xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
3473 xSeries, nElement, getModel() );
3474 }
3475 catch( const uno::Exception & )
3476 {
3477 DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
3478 }
3479 }
3480
3481 if( xPropSet.is() )
3482 {
3483 FSHelperPtr pFS = GetFS();
3484 pFS->startElement(FSNS(XML_c, XML_dPt));
3485 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nElement));
3486
3487 switch( eChartType )
3488 {
3489 case chart::TYPEID_BUBBLE:
3490 case chart::TYPEID_HORBAR:
3491 case chart::TYPEID_BAR:
3492 pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0");
3493 exportShapeProps(xPropSet);
3494 break;
3495
3496 case chart::TYPEID_LINE:
3497 case chart::TYPEID_SCATTER:
3498 case chart::TYPEID_RADARLINE:
3499 exportMarker(xPropSet);
3500 break;
3501
3502 default:
3503 exportShapeProps(xPropSet);
3504 break;
3505 }
3506
3507 pFS->endElement( FSNS( XML_c, XML_dPt ) );
3508 }
3509 }
3510 }
3511 }
3512
exportAxesId(bool bPrimaryAxes,bool bCheckCombinedAxes)3513 void ChartExport::exportAxesId(bool bPrimaryAxes, bool bCheckCombinedAxes)
3514 {
3515 sal_Int32 nAxisIdx, nAxisIdy;
3516 bool bPrimaryAxisExists = false;
3517 bool bSecondaryAxisExists = false;
3518 // let's check which axis already exists and which axis is attached to the actual dataseries
3519 if (maAxes.size() >= 2)
3520 {
3521 bPrimaryAxisExists = bPrimaryAxes && maAxes[1].nAxisType == AXIS_PRIMARY_Y;
3522 bSecondaryAxisExists = !bPrimaryAxes && maAxes[1].nAxisType == AXIS_SECONDARY_Y;
3523 }
3524 // tdf#114181 keep axes of combined charts
3525 if ( bCheckCombinedAxes && ( bPrimaryAxisExists || bSecondaryAxisExists ) )
3526 {
3527 nAxisIdx = maAxes[0].nAxisId;
3528 nAxisIdy = maAxes[1].nAxisId;
3529 }
3530 else
3531 {
3532 nAxisIdx = lcl_generateRandomValue();
3533 nAxisIdy = lcl_generateRandomValue();
3534 AxesType eXAxis = bPrimaryAxes ? AXIS_PRIMARY_X : AXIS_SECONDARY_X;
3535 AxesType eYAxis = bPrimaryAxes ? AXIS_PRIMARY_Y : AXIS_SECONDARY_Y;
3536 maAxes.emplace_back( eXAxis, nAxisIdx, nAxisIdy );
3537 maAxes.emplace_back( eYAxis, nAxisIdy, nAxisIdx );
3538 }
3539 FSHelperPtr pFS = GetFS();
3540 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdx));
3541 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdy));
3542 if (mbHasZAxis)
3543 {
3544 sal_Int32 nAxisIdz = 0;
3545 if( isDeep3dChart() )
3546 {
3547 nAxisIdz = lcl_generateRandomValue();
3548 maAxes.emplace_back( AXIS_PRIMARY_Z, nAxisIdz, nAxisIdy );
3549 }
3550 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdz));
3551 }
3552 }
3553
exportGrouping(bool isBar)3554 void ChartExport::exportGrouping( bool isBar )
3555 {
3556 FSHelperPtr pFS = GetFS();
3557 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
3558 // grouping
3559 if( GetProperty( xPropSet, "Stacked" ) )
3560 mAny >>= mbStacked;
3561 if( GetProperty( xPropSet, "Percent" ) )
3562 mAny >>= mbPercent;
3563
3564 const char* grouping = nullptr;
3565 if (mbStacked)
3566 grouping = "stacked";
3567 else if (mbPercent)
3568 grouping = "percentStacked";
3569 else
3570 {
3571 if( isBar && !isDeep3dChart() )
3572 {
3573 grouping = "clustered";
3574 }
3575 else
3576 grouping = "standard";
3577 }
3578 pFS->singleElement(FSNS(XML_c, XML_grouping), XML_val, grouping);
3579 }
3580
exportTrendlines(const Reference<chart2::XDataSeries> & xSeries)3581 void ChartExport::exportTrendlines( const Reference< chart2::XDataSeries >& xSeries )
3582 {
3583 FSHelperPtr pFS = GetFS();
3584 Reference< chart2::XRegressionCurveContainer > xRegressionCurveContainer( xSeries, UNO_QUERY );
3585 if( xRegressionCurveContainer.is() )
3586 {
3587 const Sequence< Reference< chart2::XRegressionCurve > > aRegCurveSeq = xRegressionCurveContainer->getRegressionCurves();
3588 for( const Reference< chart2::XRegressionCurve >& xRegCurve : aRegCurveSeq )
3589 {
3590 if (!xRegCurve.is())
3591 continue;
3592
3593 Reference< XPropertySet > xProperties( xRegCurve , uno::UNO_QUERY );
3594
3595 OUString aService;
3596 Reference< lang::XServiceName > xServiceName( xProperties, UNO_QUERY );
3597 if( !xServiceName.is() )
3598 continue;
3599
3600 aService = xServiceName->getServiceName();
3601
3602 if(aService != "com.sun.star.chart2.LinearRegressionCurve" &&
3603 aService != "com.sun.star.chart2.ExponentialRegressionCurve" &&
3604 aService != "com.sun.star.chart2.LogarithmicRegressionCurve" &&
3605 aService != "com.sun.star.chart2.PotentialRegressionCurve" &&
3606 aService != "com.sun.star.chart2.PolynomialRegressionCurve" &&
3607 aService != "com.sun.star.chart2.MovingAverageRegressionCurve")
3608 continue;
3609
3610 pFS->startElement(FSNS(XML_c, XML_trendline));
3611
3612 OUString aName;
3613 xProperties->getPropertyValue("CurveName") >>= aName;
3614 if(!aName.isEmpty())
3615 {
3616 pFS->startElement(FSNS(XML_c, XML_name));
3617 pFS->writeEscaped(aName);
3618 pFS->endElement( FSNS( XML_c, XML_name) );
3619 }
3620
3621 exportShapeProps( xProperties );
3622
3623 if( aService == "com.sun.star.chart2.LinearRegressionCurve" )
3624 {
3625 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "linear");
3626 }
3627 else if( aService == "com.sun.star.chart2.ExponentialRegressionCurve" )
3628 {
3629 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "exp");
3630 }
3631 else if( aService == "com.sun.star.chart2.LogarithmicRegressionCurve" )
3632 {
3633 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "log");
3634 }
3635 else if( aService == "com.sun.star.chart2.PotentialRegressionCurve" )
3636 {
3637 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "power");
3638 }
3639 else if( aService == "com.sun.star.chart2.PolynomialRegressionCurve" )
3640 {
3641 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "poly");
3642
3643 sal_Int32 aDegree = 2;
3644 xProperties->getPropertyValue( "PolynomialDegree") >>= aDegree;
3645 pFS->singleElement(FSNS(XML_c, XML_order), XML_val, OString::number(aDegree));
3646 }
3647 else if( aService == "com.sun.star.chart2.MovingAverageRegressionCurve" )
3648 {
3649 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "movingAvg");
3650
3651 sal_Int32 aPeriod = 2;
3652 xProperties->getPropertyValue( "MovingAveragePeriod") >>= aPeriod;
3653
3654 pFS->singleElement(FSNS(XML_c, XML_period), XML_val, OString::number(aPeriod));
3655 }
3656 else
3657 {
3658 // should never happen
3659 // This would produce invalid OOXML files so we check earlier for the type
3660 assert(false);
3661 }
3662
3663 double fExtrapolateForward = 0.0;
3664 double fExtrapolateBackward = 0.0;
3665
3666 xProperties->getPropertyValue("ExtrapolateForward") >>= fExtrapolateForward;
3667 xProperties->getPropertyValue("ExtrapolateBackward") >>= fExtrapolateBackward;
3668
3669 pFS->singleElement( FSNS( XML_c, XML_forward ),
3670 XML_val, OString::number(fExtrapolateForward) );
3671
3672 pFS->singleElement( FSNS( XML_c, XML_backward ),
3673 XML_val, OString::number(fExtrapolateBackward) );
3674
3675 bool bForceIntercept = false;
3676 xProperties->getPropertyValue("ForceIntercept") >>= bForceIntercept;
3677
3678 if (bForceIntercept)
3679 {
3680 double fInterceptValue = 0.0;
3681 xProperties->getPropertyValue("InterceptValue") >>= fInterceptValue;
3682
3683 pFS->singleElement( FSNS( XML_c, XML_intercept ),
3684 XML_val, OString::number(fInterceptValue) );
3685 }
3686
3687 // Equation properties
3688 Reference< XPropertySet > xEquationProperties( xRegCurve->getEquationProperties() );
3689
3690 // Show Equation
3691 bool bShowEquation = false;
3692 xEquationProperties->getPropertyValue("ShowEquation") >>= bShowEquation;
3693
3694 // Show R^2
3695 bool bShowCorrelationCoefficient = false;
3696 xEquationProperties->getPropertyValue("ShowCorrelationCoefficient") >>= bShowCorrelationCoefficient;
3697
3698 pFS->singleElement( FSNS( XML_c, XML_dispRSqr ),
3699 XML_val, ToPsz10(bShowCorrelationCoefficient) );
3700
3701 pFS->singleElement(FSNS(XML_c, XML_dispEq), XML_val, ToPsz10(bShowEquation));
3702
3703 pFS->endElement( FSNS( XML_c, XML_trendline ) );
3704 }
3705 }
3706 }
3707
exportMarker(const Reference<XPropertySet> & xPropSet)3708 void ChartExport::exportMarker(const Reference< XPropertySet >& xPropSet)
3709 {
3710 chart2::Symbol aSymbol;
3711 if( GetProperty( xPropSet, "Symbol" ) )
3712 mAny >>= aSymbol;
3713
3714 if(aSymbol.Style != chart2::SymbolStyle_STANDARD && aSymbol.Style != chart2::SymbolStyle_AUTO && aSymbol.Style != chart2::SymbolStyle_NONE)
3715 return;
3716
3717 FSHelperPtr pFS = GetFS();
3718 pFS->startElement(FSNS(XML_c, XML_marker));
3719
3720 sal_Int32 nSymbol = aSymbol.StandardSymbol;
3721 // TODO: more properties support for marker
3722 const char* pSymbolType; // no initialization here, to let compiler warn if we have a code path
3723 // where it stays uninitialized
3724 switch( nSymbol )
3725 {
3726 case 0:
3727 pSymbolType = "square";
3728 break;
3729 case 1:
3730 pSymbolType = "diamond";
3731 break;
3732 case 2:
3733 case 3:
3734 case 4:
3735 case 5:
3736 pSymbolType = "triangle";
3737 break;
3738 case 8:
3739 pSymbolType = "circle";
3740 break;
3741 case 9:
3742 pSymbolType = "star";
3743 break;
3744 case 10:
3745 pSymbolType = "x"; // in MS office 2010 built in symbol marker 'X' is represented as 'x'
3746 break;
3747 case 11:
3748 pSymbolType = "plus";
3749 break;
3750 case 13:
3751 pSymbolType = "dash";
3752 break;
3753 default:
3754 pSymbolType = "square";
3755 break;
3756 }
3757
3758 bool bSkipFormatting = false;
3759 if (aSymbol.Style == chart2::SymbolStyle_NONE)
3760 {
3761 bSkipFormatting = true;
3762 pSymbolType = "none";
3763 }
3764
3765 pFS->singleElement(FSNS(XML_c, XML_symbol), XML_val, pSymbolType);
3766
3767 if (!bSkipFormatting)
3768 {
3769 awt::Size aSymbolSize = aSymbol.Size;
3770 sal_Int32 nSize = std::max( aSymbolSize.Width, aSymbolSize.Height );
3771
3772 nSize = nSize/250.0*7.0 + 1; // just guessed based on some test cases,
3773 //the value is always 1 less than the actual value.
3774 nSize = std::min<sal_Int32>( 72, std::max<sal_Int32>( 2, nSize ) );
3775 pFS->singleElement(FSNS(XML_c, XML_size), XML_val, OString::number(nSize));
3776
3777 pFS->startElement(FSNS(XML_c, XML_spPr));
3778
3779 util::Color aColor = aSymbol.FillColor;
3780 if (GetProperty(xPropSet, "Color"))
3781 mAny >>= aColor;
3782
3783 if (aColor == -1)
3784 {
3785 pFS->singleElement(FSNS(XML_a, XML_noFill));
3786 }
3787 else
3788 WriteSolidFill(::Color(aColor));
3789
3790 pFS->endElement( FSNS( XML_c, XML_spPr ) );
3791 }
3792
3793 pFS->endElement( FSNS( XML_c, XML_marker ) );
3794 }
3795
exportSmooth()3796 void ChartExport::exportSmooth()
3797 {
3798 FSHelperPtr pFS = GetFS();
3799 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY );
3800 sal_Int32 nSplineType = 0;
3801 if( GetProperty( xPropSet, "SplineType" ) )
3802 mAny >>= nSplineType;
3803 const char* pVal = nSplineType != 0 ? "1" : "0";
3804 pFS->singleElement(FSNS(XML_c, XML_smooth), XML_val, pVal);
3805 }
3806
exportFirstSliceAng()3807 void ChartExport::exportFirstSliceAng( )
3808 {
3809 FSHelperPtr pFS = GetFS();
3810 sal_Int32 nStartingAngle = 0;
3811 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
3812 if( GetProperty( xPropSet, "StartingAngle" ) )
3813 mAny >>= nStartingAngle;
3814
3815 // convert to ooxml angle
3816 nStartingAngle = (450 - nStartingAngle ) % 360;
3817 pFS->singleElement(FSNS(XML_c, XML_firstSliceAng), XML_val, OString::number(nStartingAngle));
3818 }
3819
3820 namespace {
3821
getErrorBarStyle(sal_Int32 nErrorBarStyle)3822 const char* getErrorBarStyle(sal_Int32 nErrorBarStyle)
3823 {
3824 switch(nErrorBarStyle)
3825 {
3826 case cssc::ErrorBarStyle::NONE:
3827 return nullptr;
3828 case cssc::ErrorBarStyle::VARIANCE:
3829 break;
3830 case cssc::ErrorBarStyle::STANDARD_DEVIATION:
3831 return "stdDev";
3832 case cssc::ErrorBarStyle::ABSOLUTE:
3833 return "fixedVal";
3834 case cssc::ErrorBarStyle::RELATIVE:
3835 return "percentage";
3836 case cssc::ErrorBarStyle::ERROR_MARGIN:
3837 break;
3838 case cssc::ErrorBarStyle::STANDARD_ERROR:
3839 return "stdErr";
3840 case cssc::ErrorBarStyle::FROM_DATA:
3841 return "cust";
3842 default:
3843 assert(false && "can't happen");
3844 }
3845 return nullptr;
3846 }
3847
getLabeledSequence(const uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> & aSequences,bool bPositive)3848 Reference< chart2::data::XDataSequence> getLabeledSequence(
3849 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > >& aSequences,
3850 bool bPositive )
3851 {
3852 OUString aDirection;
3853 if(bPositive)
3854 aDirection = "positive";
3855 else
3856 aDirection = "negative";
3857
3858 for( const auto& rSequence : aSequences )
3859 {
3860 if( rSequence.is())
3861 {
3862 uno::Reference< chart2::data::XDataSequence > xSequence( rSequence->getValues());
3863 uno::Reference< beans::XPropertySet > xSeqProp( xSequence, uno::UNO_QUERY_THROW );
3864 OUString aRole;
3865 if( ( xSeqProp->getPropertyValue( "Role" ) >>= aRole ) &&
3866 aRole.match( "error-bars" ) && aRole.indexOf(aDirection) >= 0 )
3867 {
3868 return xSequence;
3869 }
3870 }
3871 }
3872
3873 return Reference< chart2::data::XDataSequence > ();
3874 }
3875
3876 }
3877
exportErrorBar(const Reference<XPropertySet> & xErrorBarProps,bool bYError)3878 void ChartExport::exportErrorBar(const Reference< XPropertySet>& xErrorBarProps, bool bYError)
3879 {
3880 sal_Int32 nErrorBarStyle = cssc::ErrorBarStyle::NONE;
3881 xErrorBarProps->getPropertyValue("ErrorBarStyle") >>= nErrorBarStyle;
3882 const char* pErrorBarStyle = getErrorBarStyle(nErrorBarStyle);
3883 if(!pErrorBarStyle)
3884 return;
3885
3886 FSHelperPtr pFS = GetFS();
3887 pFS->startElement(FSNS(XML_c, XML_errBars));
3888 pFS->singleElement(FSNS(XML_c, XML_errDir), XML_val, bYError ? "y" : "x");
3889 bool bPositive = false, bNegative = false;
3890 xErrorBarProps->getPropertyValue("ShowPositiveError") >>= bPositive;
3891 xErrorBarProps->getPropertyValue("ShowNegativeError") >>= bNegative;
3892 const char* pErrBarType;
3893 if(bPositive && bNegative)
3894 pErrBarType = "both";
3895 else if(bPositive)
3896 pErrBarType = "plus";
3897 else if(bNegative)
3898 pErrBarType = "minus";
3899 else
3900 {
3901 // what the hell should we do now?
3902 // at least this makes the file valid
3903 pErrBarType = "both";
3904 }
3905 pFS->singleElement(FSNS(XML_c, XML_errBarType), XML_val, pErrBarType);
3906 pFS->singleElement(FSNS(XML_c, XML_errValType), XML_val, pErrorBarStyle);
3907 pFS->singleElement(FSNS(XML_c, XML_noEndCap), XML_val, "0");
3908 if(nErrorBarStyle == cssc::ErrorBarStyle::FROM_DATA)
3909 {
3910 uno::Reference< chart2::data::XDataSource > xDataSource(xErrorBarProps, uno::UNO_QUERY);
3911 Sequence< Reference < chart2::data::XLabeledDataSequence > > aSequences =
3912 xDataSource->getDataSequences();
3913
3914 if(bPositive)
3915 {
3916 exportSeriesValues(getLabeledSequence(aSequences, true), XML_plus);
3917 }
3918
3919 if(bNegative)
3920 {
3921 exportSeriesValues(getLabeledSequence(aSequences, false), XML_minus);
3922 }
3923 }
3924 else
3925 {
3926 double nVal = 0.0;
3927 if(nErrorBarStyle == cssc::ErrorBarStyle::STANDARD_DEVIATION)
3928 {
3929 xErrorBarProps->getPropertyValue("Weight") >>= nVal;
3930 }
3931 else
3932 {
3933 if(bPositive)
3934 xErrorBarProps->getPropertyValue("PositiveError") >>= nVal;
3935 else
3936 xErrorBarProps->getPropertyValue("NegativeError") >>= nVal;
3937 }
3938
3939 pFS->singleElement(FSNS(XML_c, XML_val), XML_val, OString::number(nVal));
3940 }
3941
3942 exportShapeProps( xErrorBarProps );
3943
3944 pFS->endElement( FSNS( XML_c, XML_errBars) );
3945 }
3946
exportView3D()3947 void ChartExport::exportView3D()
3948 {
3949 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
3950 if( !xPropSet.is() )
3951 return;
3952 FSHelperPtr pFS = GetFS();
3953 pFS->startElement(FSNS(XML_c, XML_view3D));
3954 sal_Int32 eChartType = getChartType( );
3955 // rotX
3956 if( GetProperty( xPropSet, "RotationHorizontal" ) )
3957 {
3958 sal_Int32 nRotationX = 0;
3959 mAny >>= nRotationX;
3960 if( nRotationX < 0 )
3961 {
3962 if(eChartType == chart::TYPEID_PIE)
3963 {
3964 /* In OOXML we get value in 0..90 range for pie chart X rotation , whereas we expect it to be in -90..90 range,
3965 so we convert that during import. It is modified in View3DConverter::convertFromModel()
3966 here we convert it back to 0..90 as we received in import */
3967 nRotationX += 90; // X rotation (map Chart2 [-179,180] to OOXML [0..90])
3968 }
3969 else
3970 nRotationX += 360; // X rotation (map Chart2 [-179,180] to OOXML [-90..90])
3971 }
3972 pFS->singleElement(FSNS(XML_c, XML_rotX), XML_val, OString::number(nRotationX));
3973 }
3974 // rotY
3975 if( GetProperty( xPropSet, "RotationVertical" ) )
3976 {
3977 // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
3978 if( eChartType == chart::TYPEID_PIE && GetProperty( xPropSet, "StartingAngle" ) )
3979 {
3980 // Y rotation used as 'first pie slice angle' in 3D pie charts
3981 sal_Int32 nStartingAngle=0;
3982 mAny >>= nStartingAngle;
3983 // convert to ooxml angle
3984 nStartingAngle = (450 - nStartingAngle ) % 360;
3985 pFS->singleElement(FSNS(XML_c, XML_rotY), XML_val, OString::number(nStartingAngle));
3986 }
3987 else
3988 {
3989 sal_Int32 nRotationY = 0;
3990 mAny >>= nRotationY;
3991 // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
3992 if( nRotationY < 0 )
3993 nRotationY += 360;
3994 pFS->singleElement(FSNS(XML_c, XML_rotY), XML_val, OString::number(nRotationY));
3995 }
3996 }
3997 // rAngAx
3998 if( GetProperty( xPropSet, "RightAngledAxes" ) )
3999 {
4000 bool bRightAngled = false;
4001 mAny >>= bRightAngled;
4002 const char* sRightAngled = bRightAngled ? "1":"0";
4003 pFS->singleElement(FSNS(XML_c, XML_rAngAx), XML_val, sRightAngled);
4004 }
4005 // perspective
4006 if( GetProperty( xPropSet, "Perspective" ) )
4007 {
4008 sal_Int32 nPerspective = 0;
4009 mAny >>= nPerspective;
4010 // map Chart2 [0,100] to OOXML [0..200]
4011 nPerspective *= 2;
4012 pFS->singleElement(FSNS(XML_c, XML_perspective), XML_val, OString::number(nPerspective));
4013 }
4014 pFS->endElement( FSNS( XML_c, XML_view3D ) );
4015 }
4016
isDeep3dChart()4017 bool ChartExport::isDeep3dChart()
4018 {
4019 bool isDeep = false;
4020 if( mbIs3DChart )
4021 {
4022 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
4023 if( GetProperty( xPropSet, "Deep" ) )
4024 mAny >>= isDeep;
4025 }
4026 return isDeep;
4027 }
4028
getNumberFormatCode(sal_Int32 nKey) const4029 OUString ChartExport::getNumberFormatCode(sal_Int32 nKey) const
4030 {
4031 /* XXX if this was called more than one or two times per export the two
4032 * SvNumberFormatter instances and NfKeywordTable should be member
4033 * variables and initialized only once. */
4034
4035 OUString aCode("General"); // init with fallback
4036 uno::Reference<util::XNumberFormatsSupplier> xNumberFormatsSupplier(mxChartModel, uno::UNO_QUERY_THROW);
4037 SvNumberFormatsSupplierObj* pSupplierObj = comphelper::getUnoTunnelImplementation<SvNumberFormatsSupplierObj>( xNumberFormatsSupplier);
4038 if (!pSupplierObj)
4039 return aCode;
4040
4041 SvNumberFormatter* pNumberFormatter = pSupplierObj->GetNumberFormatter();
4042 if (!pNumberFormatter)
4043 return aCode;
4044
4045 SvNumberFormatter aTempFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US);
4046 NfKeywordTable aKeywords;
4047 aTempFormatter.FillKeywordTableForExcel( aKeywords);
4048 aCode = pNumberFormatter->GetFormatStringForExcel( nKey, aKeywords, aTempFormatter);
4049
4050 return aCode;
4051 }
4052
4053 }// drawingml
4054 }// oox
4055
4056 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4057