Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / oox / source / export / chartexport.cxx
blobc9db6f8cc431f00937ab94f085cb12afa0fc5148
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <oox/token/namespaces.hxx>
21 #include <oox/token/properties.hxx>
22 #include <oox/token/tokens.hxx>
23 #include <oox/core/xmlfilterbase.hxx>
24 #include <oox/export/chartexport.hxx>
25 #include <oox/token/relationship.hxx>
26 #include <oox/export/utils.hxx>
27 #include <drawingml/chart/typegroupconverter.hxx>
28 #include <basegfx/utils/gradienttools.hxx>
29 #include <docmodel/uno/UnoGradientTools.hxx>
31 #include <cstdio>
32 #include <limits>
34 #include <com/sun/star/awt/Gradient2.hpp>
35 #include <com/sun/star/chart/XChartDocument.hpp>
36 #include <com/sun/star/chart/ChartLegendPosition.hpp>
37 #include <com/sun/star/chart/XTwoAxisXSupplier.hpp>
38 #include <com/sun/star/chart/XTwoAxisYSupplier.hpp>
39 #include <com/sun/star/chart/XAxisZSupplier.hpp>
40 #include <com/sun/star/chart/ChartDataRowSource.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 #include <com/sun/star/chart/TimeIncrement.hpp>
54 #include <com/sun/star/chart/TimeInterval.hpp>
55 #include <com/sun/star/chart/TimeUnit.hpp>
57 #include <com/sun/star/chart2/RelativePosition.hpp>
58 #include <com/sun/star/chart2/RelativeSize.hpp>
59 #include <com/sun/star/chart2/XChartDocument.hpp>
60 #include <com/sun/star/chart2/XDiagram.hpp>
61 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
62 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
63 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
64 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
65 #include <com/sun/star/chart2/DataPointLabel.hpp>
66 #include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
67 #include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
68 #include <com/sun/star/chart2/Symbol.hpp>
69 #include <com/sun/star/chart2/data/XDataSource.hpp>
70 #include <com/sun/star/chart2/data/XDataProvider.hpp>
71 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
72 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
73 #include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
74 #include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
75 #include <com/sun/star/chart2/AxisType.hpp>
77 #include <com/sun/star/beans/XPropertySet.hpp>
78 #include <com/sun/star/container/XNameAccess.hpp>
79 #include <com/sun/star/drawing/XShape.hpp>
80 #include <com/sun/star/drawing/XShapes.hpp>
81 #include <com/sun/star/drawing/FillStyle.hpp>
82 #include <com/sun/star/drawing/LineStyle.hpp>
83 #include <com/sun/star/awt/XBitmap.hpp>
84 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
85 #include <com/sun/star/lang/XServiceName.hpp>
87 #include <com/sun/star/table/CellAddress.hpp>
88 #include <com/sun/star/sheet/XFormulaParser.hpp>
89 #include <com/sun/star/sheet/FormulaToken.hpp>
90 #include <com/sun/star/sheet/AddressConvention.hpp>
92 #include <com/sun/star/container/XNamed.hpp>
93 #include <com/sun/star/embed/XVisualObject.hpp>
94 #include <com/sun/star/embed/Aspects.hpp>
96 #include <comphelper/processfactory.hxx>
97 #include <comphelper/random.hxx>
98 #include <utility>
99 #include <xmloff/SchXMLSeriesHelper.hxx>
100 #include "ColorPropertySet.hxx"
102 #include <svl/numformat.hxx>
103 #include <svl/numuno.hxx>
104 #include <comphelper/diagnose_ex.hxx>
105 #include <sal/log.hxx>
107 #include <set>
108 #include <unordered_set>
110 #include <o3tl/temporary.hxx>
111 #include <o3tl/sorted_vector.hxx>
113 using namespace css;
114 using namespace css::uno;
115 using namespace css::drawing;
116 using namespace ::oox::core;
117 using css::beans::PropertyValue;
118 using css::beans::XPropertySet;
119 using css::container::XNamed;
120 using css::table::CellAddress;
121 using css::sheet::XFormulaParser;
122 using ::oox::core::XmlFilterBase;
123 using ::sax_fastparser::FSHelperPtr;
125 namespace cssc = css::chart;
127 namespace oox::drawingml {
129 namespace {
131 bool isPrimaryAxes(sal_Int32 nIndex)
133 assert(nIndex == 0 || nIndex == 1);
134 return nIndex != 1;
137 class lcl_MatchesRole
139 public:
140 explicit lcl_MatchesRole( OUString aRole ) :
141 m_aRole(std::move( aRole ))
144 bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const
146 if( !xSeq.is() )
147 return false;
148 Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY );
149 OUString aRole;
151 return ( xProp.is() &&
152 (xProp->getPropertyValue( "Role" ) >>= aRole ) &&
153 m_aRole == aRole );
156 private:
157 OUString m_aRole;
162 static Reference< chart2::data::XLabeledDataSequence > lcl_getCategories( const Reference< chart2::XDiagram > & xDiagram, bool& bHasDateCategories )
164 bHasDateCategories = false;
165 Reference< chart2::data::XLabeledDataSequence > xResult;
168 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
169 xDiagram, uno::UNO_QUERY_THROW );
170 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
171 xCooSysCnt->getCoordinateSystems());
172 for( const auto& xCooSys : aCooSysSeq )
174 OSL_ASSERT( xCooSys.is());
175 for( sal_Int32 nN = xCooSys->getDimension(); nN--; )
177 const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nN);
178 for(sal_Int32 nI=0; nI<=nMaxAxisIndex; ++nI)
180 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( nN, nI );
181 OSL_ASSERT( xAxis.is());
182 if( xAxis.is())
184 chart2::ScaleData aScaleData = xAxis->getScaleData();
185 if( aScaleData.Categories.is())
187 bHasDateCategories = aScaleData.AxisType == chart2::AxisType::DATE;
188 xResult.set( aScaleData.Categories );
189 break;
196 catch( const uno::Exception & )
198 DBG_UNHANDLED_EXCEPTION("oox");
201 return xResult;
204 static Reference< chart2::data::XLabeledDataSequence >
205 lcl_getDataSequenceByRole(
206 const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aLabeledSeq,
207 const OUString & rRole )
209 Reference< chart2::data::XLabeledDataSequence > aNoResult;
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 ));
216 if( pMatch != pEnd )
217 return *pMatch;
219 return aNoResult;
222 static bool lcl_hasCategoryLabels( const Reference< chart2::XChartDocument >& xChartDoc )
224 //categories are always the first sequence
225 Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram());
226 bool bDateCategories;
227 Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xDiagram, bDateCategories ) );
228 return xCategories.is();
231 static bool lcl_isCategoryAxisShifted( const Reference< chart2::XDiagram >& xDiagram )
233 bool bCategoryPositionShifted = false;
236 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
237 xDiagram, uno::UNO_QUERY_THROW);
238 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
239 xCooSysCnt->getCoordinateSystems());
240 for (const auto& xCooSys : aCooSysSeq)
242 OSL_ASSERT(xCooSys.is());
243 if( 0 < xCooSys->getDimension() && 0 <= xCooSys->getMaximumAxisIndexByDimension(0) )
245 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(0, 0);
246 OSL_ASSERT(xAxis.is());
247 if (xAxis.is())
249 chart2::ScaleData aScaleData = xAxis->getScaleData();
250 bCategoryPositionShifted = aScaleData.ShiftedCategoryPosition;
251 break;
256 catch (const uno::Exception&)
258 DBG_UNHANDLED_EXCEPTION("oox");
261 return bCategoryPositionShifted;
264 static sal_Int32 lcl_getCategoryAxisType( const Reference< chart2::XDiagram >& xDiagram, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
266 sal_Int32 nAxisType = -1;
269 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
270 xDiagram, uno::UNO_QUERY_THROW);
271 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
272 xCooSysCnt->getCoordinateSystems());
273 for( const auto& xCooSys : aCooSysSeq )
275 OSL_ASSERT(xCooSys.is());
276 if( nDimensionIndex < xCooSys->getDimension() && nAxisIndex <= xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex) )
278 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(nDimensionIndex, nAxisIndex);
279 OSL_ASSERT(xAxis.is());
280 if( xAxis.is() )
282 chart2::ScaleData aScaleData = xAxis->getScaleData();
283 nAxisType = aScaleData.AxisType;
284 break;
289 catch (const uno::Exception&)
291 DBG_UNHANDLED_EXCEPTION("oox");
294 return nAxisType;
297 static OUString lclGetTimeUnitToken( sal_Int32 nTimeUnit )
299 switch( nTimeUnit )
301 case cssc::TimeUnit::DAY: return "days";
302 case cssc::TimeUnit::MONTH: return "months";
303 case cssc::TimeUnit::YEAR: return "years";
304 default: OSL_ENSURE(false, "lclGetTimeUnitToken - unexpected time unit");
306 return "days";
309 static cssc::TimeIncrement lcl_getDateTimeIncrement( const Reference< chart2::XDiagram >& xDiagram, sal_Int32 nAxisIndex )
311 cssc::TimeIncrement aTimeIncrement;
314 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
315 xDiagram, uno::UNO_QUERY_THROW);
316 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
317 xCooSysCnt->getCoordinateSystems());
318 for( const auto& xCooSys : aCooSysSeq )
320 OSL_ASSERT(xCooSys.is());
321 if( 0 < xCooSys->getDimension() && nAxisIndex <= xCooSys->getMaximumAxisIndexByDimension(0) )
323 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(0, nAxisIndex);
324 OSL_ASSERT(xAxis.is());
325 if( xAxis.is() )
327 chart2::ScaleData aScaleData = xAxis->getScaleData();
328 aTimeIncrement = aScaleData.TimeIncrement;
329 break;
334 catch (const uno::Exception&)
336 DBG_UNHANDLED_EXCEPTION("oox");
339 return aTimeIncrement;
342 static bool lcl_isSeriesAttachedToFirstAxis(
343 const Reference< chart2::XDataSeries > & xDataSeries )
345 bool bResult=true;
349 sal_Int32 nAxisIndex = 0;
350 Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW );
351 xProp->getPropertyValue("AttachedAxisIndex") >>= nAxisIndex;
352 bResult = (0==nAxisIndex);
354 catch( const uno::Exception & )
356 DBG_UNHANDLED_EXCEPTION("oox");
359 return bResult;
362 static OUString lcl_flattenStringSequence( const Sequence< OUString > & rSequence )
364 OUStringBuffer aResult;
365 bool bPrecedeWithSpace = false;
366 for( const auto& rString : rSequence )
368 if( !rString.isEmpty())
370 if( bPrecedeWithSpace )
371 aResult.append( ' ' );
372 aResult.append( rString );
373 bPrecedeWithSpace = true;
376 return aResult.makeStringAndClear();
379 static Sequence< OUString > lcl_getLabelSequence( const Reference< chart2::data::XDataSequence > & xLabelSeq )
381 Sequence< OUString > aLabels;
383 uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xLabelSeq, uno::UNO_QUERY );
384 if( xTextualDataSequence.is())
386 aLabels = xTextualDataSequence->getTextualData();
388 else if( xLabelSeq.is())
390 const Sequence< uno::Any > aAnies( xLabelSeq->getData());
391 aLabels.realloc( aAnies.getLength());
392 auto pLabels = aLabels.getArray();
393 for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
394 aAnies[i] >>= pLabels[i];
397 return aLabels;
400 static void lcl_fillCategoriesIntoStringVector(
401 const Reference< chart2::data::XDataSequence > & xCategories,
402 ::std::vector< OUString > & rOutCategories )
404 OSL_ASSERT( xCategories.is());
405 if( !xCategories.is())
406 return;
407 Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xCategories, uno::UNO_QUERY );
408 if( xTextualDataSequence.is())
410 rOutCategories.clear();
411 const Sequence< OUString > aTextData( xTextualDataSequence->getTextualData());
412 rOutCategories.insert( rOutCategories.end(), aTextData.begin(), aTextData.end() );
414 else
416 Sequence< uno::Any > aAnies( xCategories->getData());
417 rOutCategories.resize( aAnies.getLength());
418 for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
419 aAnies[i] >>= rOutCategories[i];
423 static ::std::vector< double > lcl_getAllValuesFromSequence( const Reference< chart2::data::XDataSequence > & xSeq )
425 ::std::vector< double > aResult;
427 Reference< chart2::data::XNumericalDataSequence > xNumSeq( xSeq, uno::UNO_QUERY );
428 if( xNumSeq.is())
430 const Sequence< double > aValues( xNumSeq->getNumericalData());
431 aResult.insert( aResult.end(), aValues.begin(), aValues.end() );
433 else if( xSeq.is())
435 Sequence< uno::Any > aAnies( xSeq->getData());
436 aResult.resize( aAnies.getLength(), std::numeric_limits<double>::quiet_NaN() );
437 for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
438 aAnies[i] >>= aResult[i];
440 return aResult;
443 static sal_Int32 lcl_getChartType( std::u16string_view sChartType )
445 chart::TypeId eChartTypeId = chart::TYPEID_UNKNOWN;
446 if( sChartType == u"com.sun.star.chart.BarDiagram"
447 || sChartType == u"com.sun.star.chart2.ColumnChartType" )
448 eChartTypeId = chart::TYPEID_BAR;
449 else if( sChartType == u"com.sun.star.chart.AreaDiagram"
450 || sChartType == u"com.sun.star.chart2.AreaChartType" )
451 eChartTypeId = chart::TYPEID_AREA;
452 else if( sChartType == u"com.sun.star.chart.LineDiagram"
453 || sChartType == u"com.sun.star.chart2.LineChartType" )
454 eChartTypeId = chart::TYPEID_LINE;
455 else if( sChartType == u"com.sun.star.chart.PieDiagram"
456 || sChartType == u"com.sun.star.chart2.PieChartType" )
457 eChartTypeId = chart::TYPEID_PIE;
458 else if( sChartType == u"com.sun.star.chart.DonutDiagram"
459 || sChartType == u"com.sun.star.chart2.DonutChartType" )
460 eChartTypeId = chart::TYPEID_DOUGHNUT;
461 else if( sChartType == u"com.sun.star.chart.XYDiagram"
462 || sChartType == u"com.sun.star.chart2.ScatterChartType" )
463 eChartTypeId = chart::TYPEID_SCATTER;
464 else if( sChartType == u"com.sun.star.chart.NetDiagram"
465 || sChartType == u"com.sun.star.chart2.NetChartType" )
466 eChartTypeId = chart::TYPEID_RADARLINE;
467 else if( sChartType == u"com.sun.star.chart.FilledNetDiagram"
468 || sChartType == u"com.sun.star.chart2.FilledNetChartType" )
469 eChartTypeId = chart::TYPEID_RADARAREA;
470 else if( sChartType == u"com.sun.star.chart.StockDiagram"
471 || sChartType == u"com.sun.star.chart2.CandleStickChartType" )
472 eChartTypeId = chart::TYPEID_STOCK;
473 else if( sChartType == u"com.sun.star.chart.BubbleDiagram"
474 || sChartType == u"com.sun.star.chart2.BubbleChartType" )
475 eChartTypeId = chart::TYPEID_BUBBLE;
477 return eChartTypeId;
480 static sal_Int32 lcl_generateRandomValue()
482 return comphelper::rng::uniform_int_distribution(0, 100000000-1);
485 bool DataLabelsRange::empty() const
487 return maLabels.empty();
490 size_t DataLabelsRange::count() const
492 return maLabels.size();
495 bool DataLabelsRange::hasLabel(sal_Int32 nIndex) const
497 return maLabels.find(nIndex) != maLabels.end();
500 const OUString & DataLabelsRange::getRange() const
502 return maRange;
505 void DataLabelsRange::setRange(const OUString& rRange)
507 maRange = rRange;
510 void DataLabelsRange::setLabel(sal_Int32 nIndex, const OUString& rText)
512 maLabels.emplace(nIndex, rText);
515 DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::begin() const
517 return maLabels.begin();
520 DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::end() const
522 return maLabels.end();
525 ChartExport::ChartExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, Reference< frame::XModel > const & xModel, XmlFilterBase* pFB, DocumentType eDocumentType )
526 : DrawingML( std::move(pFS), pFB, eDocumentType )
527 , mnXmlNamespace( nXmlNamespace )
528 , mnSeriesCount(0)
529 , mxChartModel( xModel )
530 , mpURLTransformer(std::make_shared<URLTransformer>())
531 , mbHasCategoryLabels( false )
532 , mbHasZAxis( false )
533 , mbIs3DChart( false )
534 , mbStacked(false)
535 , mbPercent(false)
536 , mbHasDateCategories(false)
540 void ChartExport::SetURLTranslator(const std::shared_ptr<URLTransformer>& pTransformer)
542 mpURLTransformer = pTransformer;
545 sal_Int32 ChartExport::getChartType( )
547 OUString sChartType = mxDiagram->getDiagramType();
548 return lcl_getChartType( sChartType );
551 namespace {
553 uno::Sequence< beans::PropertyValue > createArguments(
554 const OUString & rRangeRepresentation, bool bUseColumns)
556 css::chart::ChartDataRowSource eRowSource = css::chart::ChartDataRowSource_ROWS;
557 if (bUseColumns)
558 eRowSource = css::chart::ChartDataRowSource_COLUMNS;
560 uno::Sequence<beans::PropertyValue> aArguments{
561 { "DataRowSource", -1, uno::Any(eRowSource), beans::PropertyState_DIRECT_VALUE },
562 { "FirstCellAsLabel", -1, uno::Any(false), beans::PropertyState_DIRECT_VALUE },
563 { "HasCategories", -1, uno::Any(false), beans::PropertyState_DIRECT_VALUE },
564 { "CellRangeRepresentation", -1, uno::Any(rRangeRepresentation),
565 beans::PropertyState_DIRECT_VALUE }
568 return aArguments;
571 Reference<chart2::XDataSeries> getPrimaryDataSeries(const Reference<chart2::XChartType>& xChartType)
573 Reference< chart2::XDataSeriesContainer > xDSCnt(xChartType, uno::UNO_QUERY_THROW);
575 // export dataseries for current chart-type
576 const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq(xDSCnt->getDataSeries());
577 for (const auto& rSeries : aSeriesSeq)
579 Reference<chart2::XDataSeries> xSource(rSeries, uno::UNO_QUERY);
580 if (xSource.is())
581 return xSource;
584 return Reference<chart2::XDataSeries>();
589 Sequence< Sequence< OUString > > ChartExport::getSplitCategoriesList( const OUString& rRange )
591 Reference< chart2::XChartDocument > xChartDoc(getModel(), uno::UNO_QUERY);
592 OSL_ASSERT(xChartDoc.is());
593 if (xChartDoc.is())
595 Reference< chart2::data::XDataProvider > xDataProvider(xChartDoc->getDataProvider());
596 OSL_ENSURE(xDataProvider.is(), "No DataProvider");
597 if (xDataProvider.is())
599 //detect whether the first series is a row or a column
600 bool bSeriesUsesColumns = true;
601 Reference< chart2::XDiagram > xDiagram(xChartDoc->getFirstDiagram());
604 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(xDiagram, uno::UNO_QUERY_THROW);
605 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(xCooSysCnt->getCoordinateSystems());
606 for (const auto& rCooSys : aCooSysSeq)
608 const Reference< chart2::XChartTypeContainer > xCTCnt(rCooSys, uno::UNO_QUERY_THROW);
609 const Sequence< Reference< chart2::XChartType > > aChartTypeSeq(xCTCnt->getChartTypes());
610 for (const auto& rChartType : aChartTypeSeq)
612 Reference< chart2::XDataSeries > xDataSeries = getPrimaryDataSeries(rChartType);
613 if (xDataSeries.is())
615 uno::Reference< chart2::data::XDataSource > xSeriesSource(xDataSeries, uno::UNO_QUERY);
616 const uno::Sequence< beans::PropertyValue > rArguments = xDataProvider->detectArguments(xSeriesSource);
617 for (const beans::PropertyValue& rProperty : rArguments)
619 if (rProperty.Name == "DataRowSource")
621 css::chart::ChartDataRowSource eRowSource;
622 if (rProperty.Value >>= eRowSource)
624 bSeriesUsesColumns = (eRowSource == css::chart::ChartDataRowSource_COLUMNS);
625 break;
633 catch (const uno::Exception &)
635 DBG_UNHANDLED_EXCEPTION("chart2");
637 // detect we have an inner data table or not
638 if (xChartDoc->hasInternalDataProvider() && rRange == "categories")
642 css::uno::Reference< css::chart2::XAnyDescriptionAccess > xDataAccess(xChartDoc->getDataProvider(), uno::UNO_QUERY);
643 const Sequence< Sequence< uno::Any > >aAnyCategories(bSeriesUsesColumns ? xDataAccess->getAnyRowDescriptions() : xDataAccess->getAnyColumnDescriptions());
644 auto pMax = std::max_element(aAnyCategories.begin(), aAnyCategories.end(),
645 [](const Sequence<uno::Any>& a, const Sequence<uno::Any>& b) {
646 return a.getLength() < b.getLength(); });
648 //minimum is 1!
649 if (pMax != aAnyCategories.end() && pMax->getLength() > 1)
651 sal_Int32 nLevelCount = pMax->getLength();
652 //we have complex categories
653 //sort the categories name
654 Sequence<Sequence<OUString>>aFinalSplitSource(nLevelCount);
655 auto pFinalSplitSource = aFinalSplitSource.getArray();
656 for (sal_Int32 i = 0; i < nLevelCount; i++)
658 sal_Int32 nElemLabel = 0;
659 pFinalSplitSource[nLevelCount - i - 1].realloc(aAnyCategories.getLength());
660 auto pSeq = pFinalSplitSource[nLevelCount - i - 1].getArray();
661 for (auto const& elemLabel : aAnyCategories)
663 // make sure elemLabel[i] exists!
664 if (elemLabel.getLength() > i)
666 pSeq[nElemLabel] = elemLabel[i].get<OUString>();
667 nElemLabel++;
671 return aFinalSplitSource;
674 catch (const uno::Exception &)
676 DBG_UNHANDLED_EXCEPTION("oox");
679 else
683 uno::Reference< chart2::data::XDataSource > xCategoriesSource(xDataProvider->createDataSource(
684 createArguments(rRange, bSeriesUsesColumns)));
686 if (xCategoriesSource.is())
688 const Sequence< Reference< chart2::data::XLabeledDataSequence >> aCategories = xCategoriesSource->getDataSequences();
689 if (aCategories.getLength() > 1)
691 //we have complex categories
692 //sort the categories name
693 Sequence<Sequence<OUString>> aFinalSplitSource(aCategories.getLength());
694 std::transform(aCategories.begin(), aCategories.end(),
695 std::reverse_iterator(asNonConstRange(aFinalSplitSource).end()),
696 [](const Reference<chart2::data::XLabeledDataSequence>& xCat) {
697 return lcl_getLabelSequence(xCat->getValues()); });
698 return aFinalSplitSource;
702 catch (const uno::Exception &)
704 DBG_UNHANDLED_EXCEPTION("oox");
710 return Sequence< Sequence< OUString>>(0);
713 OUString ChartExport::parseFormula( const OUString& rRange )
715 OUString aResult;
716 Reference< XFormulaParser > xParser;
717 uno::Reference< lang::XMultiServiceFactory > xSF = GetFB()->getModelFactory();
718 if( xSF.is() )
722 xParser.set( xSF->createInstance("com.sun.star.sheet.FormulaParser"), UNO_QUERY );
724 catch( Exception& )
729 SAL_WARN_IF(!xParser.is(), "oox", "creating formula parser failed");
731 if( xParser.is() )
733 Reference< XPropertySet > xParserProps( xParser, uno::UNO_QUERY );
734 // rRange is the result of a
735 // css::chart2::data::XDataSequence::getSourceRangeRepresentation()
736 // call that returns the range in the document's current UI notation.
737 // Creating a FormulaParser defaults to the same notation, for
738 // parseFormula() do not attempt to override the FormulaConvention
739 // property with css::sheet::AddressConvention::OOO or some such.
740 /* TODO: it would be much better to introduce a
741 * getSourceRangeRepresentation(css::sheet::AddressConvention) to
742 * return the ranges in a specific convention than converting them with
743 * the overhead of creating an XFormulaParser for each... */
744 uno::Sequence<sheet::FormulaToken> aTokens = xParser->parseFormula( rRange, CellAddress( 0, 0, 0 ) );
745 if( xParserProps.is() )
747 xParserProps->setPropertyValue("FormulaConvention", uno::Any(css::sheet::AddressConvention::XL_OOX) );
748 // For referencing named ranges correctly with special excel chart syntax.
749 xParserProps->setPropertyValue("RefConventionChartOOXML", uno::Any(true) );
751 aResult = xParser->printFormula( aTokens, CellAddress( 0, 0, 0 ) );
753 else
755 //FIXME: currently just using simple converter, e.g $Sheet1.$A$1:$C$1 -> Sheet1!$A$1:$C$1
756 OUString aRange( rRange );
757 if( aRange.startsWith("$") )
758 aRange = aRange.copy(1);
759 aRange = aRange.replaceAll(".$", "!$" );
760 aResult = aRange;
763 return aResult;
766 void ChartExport::WriteChartObj( const Reference< XShape >& xShape, sal_Int32 nID, sal_Int32 nChartCount )
768 FSHelperPtr pFS = GetFS();
770 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
772 pFS->startElementNS(mnXmlNamespace, XML_graphicFrame);
774 pFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr);
776 // TODO: get the correct chart name chart id
777 OUString sName = "Object 1";
778 Reference< XNamed > xNamed( xShape, UNO_QUERY );
779 if (xNamed.is())
780 sName = xNamed->getName();
782 pFS->startElementNS( mnXmlNamespace, XML_cNvPr,
783 XML_id, OString::number(nID),
784 XML_name, sName);
786 OUString sURL;
787 if ( GetProperty( xShapeProps, "URL" ) )
788 mAny >>= sURL;
789 if( !sURL.isEmpty() )
791 OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
792 oox::getRelationship(Relationship::HYPERLINK),
793 mpURLTransformer->getTransformedString(sURL),
794 mpURLTransformer->isExternalURL(sURL));
796 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
798 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
800 pFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr);
802 if( GetDocumentType() == DOCUMENT_PPTX )
803 pFS->singleElementNS(mnXmlNamespace, XML_nvPr);
804 pFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
806 // visual chart properties
807 WriteShapeTransformation( xShape, mnXmlNamespace );
809 // writer chart object
810 pFS->startElement(FSNS(XML_a, XML_graphic));
811 pFS->startElement( FSNS( XML_a, XML_graphicData ),
812 XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/chart" );
813 OUString sId;
814 const char* sFullPath = nullptr;
815 const char* sRelativePath = nullptr;
816 switch( GetDocumentType() )
818 case DOCUMENT_DOCX:
820 sFullPath = "word/charts/chart";
821 sRelativePath = "charts/chart";
822 break;
824 case DOCUMENT_PPTX:
826 sFullPath = "ppt/charts/chart";
827 sRelativePath = "../charts/chart";
828 break;
830 case DOCUMENT_XLSX:
832 sFullPath = "xl/charts/chart";
833 sRelativePath = "../charts/chart";
834 break;
836 default:
838 sFullPath = "charts/chart";
839 sRelativePath = "charts/chart";
840 break;
843 OUString sFullStream = OUStringBuffer()
844 .appendAscii(sFullPath)
845 .append(OUString::number(nChartCount) + ".xml")
846 .makeStringAndClear();
847 OUString sRelativeStream = OUStringBuffer()
848 .appendAscii(sRelativePath)
849 .append(OUString::number(nChartCount) + ".xml" )
850 .makeStringAndClear();
851 FSHelperPtr pChart = CreateOutputStream(
852 sFullStream,
853 sRelativeStream,
854 pFS->getOutputStream(),
855 "application/vnd.openxmlformats-officedocument.drawingml.chart+xml",
856 oox::getRelationship(Relationship::CHART),
857 &sId );
859 XmlFilterBase* pFB = GetFB();
860 pFS->singleElement( FSNS( XML_c, XML_chart ),
861 FSNS(XML_xmlns, XML_c), pFB->getNamespaceURL(OOX_NS(dmlChart)),
862 FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)),
863 FSNS(XML_r, XML_id), sId );
865 pFS->endElement( FSNS( XML_a, XML_graphicData ) );
866 pFS->endElement( FSNS( XML_a, XML_graphic ) );
867 pFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
869 SetFS( pChart );
870 ExportContent();
871 SetFS( pFS );
872 pChart->endDocument();
875 void ChartExport::InitRangeSegmentationProperties( const Reference< chart2::XChartDocument > & xChartDoc )
877 if( !xChartDoc.is())
878 return;
882 Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
883 OSL_ENSURE( xDataProvider.is(), "No DataProvider" );
884 if( xDataProvider.is())
886 mbHasCategoryLabels = lcl_hasCategoryLabels( xChartDoc );
889 catch( const uno::Exception & )
891 DBG_UNHANDLED_EXCEPTION("oox");
895 void ChartExport::ExportContent()
897 Reference< chart2::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
898 OSL_ASSERT( xChartDoc.is() );
899 if( !xChartDoc.is() )
900 return;
901 InitRangeSegmentationProperties( xChartDoc );
902 // TODO: export chart
903 ExportContent_( );
906 void ChartExport::ExportContent_()
908 Reference< css::chart::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
909 if( xChartDoc.is())
911 // determine if data comes from the outside
912 bool bIncludeTable = true;
914 Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
915 if( xNewDoc.is())
917 // check if we have own data. If so we must not export the complete
918 // range string, as this is our only indicator for having own or
919 // external data. @todo: fix this in the file format!
920 Reference< lang::XServiceInfo > xDPServiceInfo( xNewDoc->getDataProvider(), uno::UNO_QUERY );
921 if( ! (xDPServiceInfo.is() && xDPServiceInfo->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" ))
923 bIncludeTable = false;
926 exportChartSpace( xChartDoc, bIncludeTable );
928 else
930 OSL_FAIL( "Couldn't export chart due to wrong XModel" );
934 void ChartExport::exportChartSpace( const Reference< css::chart::XChartDocument >& xChartDoc,
935 bool bIncludeTable )
937 FSHelperPtr pFS = GetFS();
938 XmlFilterBase* pFB = GetFB();
939 pFS->startElement( FSNS( XML_c, XML_chartSpace ),
940 FSNS( XML_xmlns, XML_c ), pFB->getNamespaceURL(OOX_NS(dmlChart)),
941 FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
942 FSNS( XML_xmlns, XML_r ), pFB->getNamespaceURL(OOX_NS(officeRel)));
943 // TODO: get the correct editing language
944 pFS->singleElement(FSNS(XML_c, XML_lang), XML_val, "en-US");
946 pFS->singleElement(FSNS(XML_c, XML_roundedCorners), XML_val, "0");
948 if( !bIncludeTable )
950 // TODO:external data
952 //XML_chart
953 exportChart(xChartDoc);
955 // TODO: printSettings
956 // TODO: style
957 // TODO: text properties
958 // TODO: shape properties
959 Reference< XPropertySet > xPropSet = xChartDoc->getArea();
960 if( xPropSet.is() )
961 exportShapeProps( xPropSet );
963 //XML_externalData
964 exportExternalData(xChartDoc);
966 // export additional shapes in chart
967 exportAdditionalShapes(xChartDoc);
969 pFS->endElement( FSNS( XML_c, XML_chartSpace ) );
972 void ChartExport::exportExternalData( const Reference< css::chart::XChartDocument >& xChartDoc )
974 // Embedded external data is grab bagged for docx file hence adding export part of
975 // external data for docx files only.
976 if(GetDocumentType() != DOCUMENT_DOCX)
977 return;
979 OUString externalDataPath;
980 Reference< beans::XPropertySet > xDocPropSet( xChartDoc->getDiagram(), uno::UNO_QUERY );
981 if( xDocPropSet.is())
985 Any aAny( xDocPropSet->getPropertyValue( "ExternalData" ));
986 aAny >>= externalDataPath;
988 catch( beans::UnknownPropertyException & )
990 SAL_WARN("oox", "Required property not found in ChartDocument");
993 if(externalDataPath.isEmpty())
994 return;
996 // Here adding external data entry to relationship.
997 OUString relationPath = externalDataPath;
998 // Converting absolute path to relative path.
999 if( externalDataPath[ 0 ] != '.' && externalDataPath[ 1 ] != '.')
1001 sal_Int32 nSepPos = externalDataPath.indexOf( '/', 0 );
1002 if( nSepPos > 0)
1004 relationPath = relationPath.copy( nSepPos, ::std::max< sal_Int32 >( externalDataPath.getLength(), 0 ) - nSepPos );
1005 relationPath = ".." + relationPath;
1008 FSHelperPtr pFS = GetFS();
1009 OUString type = oox::getRelationship(Relationship::PACKAGE);
1010 if (relationPath.endsWith(".bin"))
1011 type = oox::getRelationship(Relationship::OLEOBJECT);
1013 OUString sRelId = GetFB()->addRelation(pFS->getOutputStream(),
1014 type,
1015 relationPath);
1016 pFS->singleElementNS(XML_c, XML_externalData, FSNS(XML_r, XML_id), sRelId);
1019 void ChartExport::exportAdditionalShapes( const Reference< css::chart::XChartDocument >& xChartDoc )
1021 Reference< beans::XPropertySet > xDocPropSet(xChartDoc, uno::UNO_QUERY);
1022 if (!xDocPropSet.is())
1023 return;
1025 css::uno::Reference< css::drawing::XShapes > mxAdditionalShapes;
1026 // get a sequence of non-chart shapes
1029 Any aShapesAny = xDocPropSet->getPropertyValue("AdditionalShapes");
1030 if( (aShapesAny >>= mxAdditionalShapes) && mxAdditionalShapes.is() )
1032 OUString sId;
1033 const char* sFullPath = nullptr;
1034 const char* sRelativePath = nullptr;
1035 sal_Int32 nDrawing = getNewDrawingUniqueId();
1037 switch (GetDocumentType())
1039 case DOCUMENT_DOCX:
1041 sFullPath = "word/drawings/drawing";
1042 sRelativePath = "../drawings/drawing";
1043 break;
1045 case DOCUMENT_PPTX:
1047 sFullPath = "ppt/drawings/drawing";
1048 sRelativePath = "../drawings/drawing";
1049 break;
1051 case DOCUMENT_XLSX:
1053 sFullPath = "xl/drawings/drawing";
1054 sRelativePath = "../drawings/drawing";
1055 break;
1057 default:
1059 sFullPath = "drawings/drawing";
1060 sRelativePath = "drawings/drawing";
1061 break;
1064 OUString sFullStream = OUStringBuffer()
1065 .appendAscii(sFullPath)
1066 .append(OUString::number(nDrawing) + ".xml")
1067 .makeStringAndClear();
1068 OUString sRelativeStream = OUStringBuffer()
1069 .appendAscii(sRelativePath)
1070 .append(OUString::number(nDrawing) + ".xml")
1071 .makeStringAndClear();
1073 sax_fastparser::FSHelperPtr pDrawing = CreateOutputStream(
1074 sFullStream,
1075 sRelativeStream,
1076 GetFS()->getOutputStream(),
1077 "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml",
1078 oox::getRelationship(Relationship::CHARTUSERSHAPES),
1079 &sId);
1081 GetFS()->singleElementNS(XML_c, XML_userShapes, FSNS(XML_r, XML_id), sId);
1083 XmlFilterBase* pFB = GetFB();
1084 pDrawing->startElement(FSNS(XML_c, XML_userShapes),
1085 FSNS(XML_xmlns, XML_cdr), pFB->getNamespaceURL(OOX_NS(dmlChartDr)),
1086 FSNS(XML_xmlns, XML_a), pFB->getNamespaceURL(OOX_NS(dml)),
1087 FSNS(XML_xmlns, XML_c), pFB->getNamespaceURL(OOX_NS(dmlChart)),
1088 FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)));
1090 const sal_Int32 nShapeCount(mxAdditionalShapes->getCount());
1091 for (sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++)
1093 Reference< drawing::XShape > xShape;
1094 mxAdditionalShapes->getByIndex(nShapeId) >>= xShape;
1095 SAL_WARN_IF(!xShape.is(), "xmloff.chart", "Shape without an XShape?");
1096 if (!xShape.is())
1097 continue;
1099 // TODO: absSizeAnchor: we import both (absSizeAnchor and relSizeAnchor), but there is no essential difference between them.
1100 pDrawing->startElement(FSNS(XML_cdr, XML_relSizeAnchor));
1101 uno::Reference< beans::XPropertySet > xShapeProperties(xShape, uno::UNO_QUERY);
1102 if( xShapeProperties.is() )
1104 Reference<embed::XVisualObject> xVisObject(mxChartModel, uno::UNO_QUERY);
1105 awt::Size aPageSize = xVisObject->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT);
1106 WriteFromTo( xShape, aPageSize, pDrawing );
1108 ShapeExport aExport(XML_cdr, pDrawing, nullptr, GetFB(), GetDocumentType(), nullptr, true);
1109 aExport.WriteShape(xShape);
1111 pDrawing->endElement(FSNS(XML_cdr, XML_relSizeAnchor));
1113 pDrawing->endElement(FSNS(XML_c, XML_userShapes));
1114 pDrawing->endDocument();
1117 catch (const uno::Exception&)
1119 TOOLS_INFO_EXCEPTION("xmloff.chart", "AdditionalShapes not found");
1123 void ChartExport::exportChart( const Reference< css::chart::XChartDocument >& xChartDoc )
1125 Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
1126 mxDiagram.set( xChartDoc->getDiagram() );
1127 if( xNewDoc.is())
1128 mxNewDiagram.set( xNewDoc->getFirstDiagram());
1130 // get Properties of ChartDocument
1131 bool bHasMainTitle = false;
1132 OUString aSubTitle;
1133 bool bHasLegend = false;
1134 Reference< beans::XPropertySet > xDocPropSet( xChartDoc, uno::UNO_QUERY );
1135 if( xDocPropSet.is())
1139 Any aAny( xDocPropSet->getPropertyValue("HasMainTitle"));
1140 aAny >>= bHasMainTitle;
1141 aAny = xDocPropSet->getPropertyValue("HasLegend");
1142 aAny >>= bHasLegend;
1144 catch( beans::UnknownPropertyException & )
1146 SAL_WARN("oox", "Required property not found in ChartDocument");
1148 } // if( xDocPropSet.is())
1150 Reference< beans::XPropertySet > xPropSubTitle( xChartDoc->getSubTitle(), UNO_QUERY );
1151 if( xPropSubTitle.is())
1155 xPropSubTitle->getPropertyValue("String") >>= aSubTitle;
1157 catch( beans::UnknownPropertyException & )
1162 // chart element
1163 FSHelperPtr pFS = GetFS();
1164 pFS->startElement(FSNS(XML_c, XML_chart));
1166 // titles
1167 if( bHasMainTitle )
1169 exportTitle( xChartDoc->getTitle(), !aSubTitle.isEmpty() ? &aSubTitle : nullptr );
1170 pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
1172 else if( !aSubTitle.isEmpty() )
1174 exportTitle( xChartDoc->getSubTitle(), nullptr );
1175 pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
1177 else
1179 pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "1");
1182 InitPlotArea( );
1183 if( mbIs3DChart )
1185 exportView3D();
1187 // floor
1188 Reference< beans::XPropertySet > xFloor = mxNewDiagram->getFloor();
1189 if( xFloor.is() )
1191 pFS->startElement(FSNS(XML_c, XML_floor));
1192 exportShapeProps( xFloor );
1193 pFS->endElement( FSNS( XML_c, XML_floor ) );
1196 // LibreOffice doesn't distinguish between sideWall and backWall (both are using the same color).
1197 // It is controlled by the same Wall property.
1198 Reference< beans::XPropertySet > xWall = mxNewDiagram->getWall();
1199 if( xWall.is() )
1201 // sideWall
1202 pFS->startElement(FSNS(XML_c, XML_sideWall));
1203 exportShapeProps( xWall );
1204 pFS->endElement( FSNS( XML_c, XML_sideWall ) );
1206 // backWall
1207 pFS->startElement(FSNS(XML_c, XML_backWall));
1208 exportShapeProps( xWall );
1209 pFS->endElement( FSNS( XML_c, XML_backWall ) );
1213 // plot area
1214 exportPlotArea( xChartDoc );
1215 // legend
1216 if( bHasLegend )
1217 exportLegend( xChartDoc );
1219 uno::Reference<beans::XPropertySet> xDiagramPropSet(xChartDoc->getDiagram(), uno::UNO_QUERY);
1220 uno::Any aPlotVisOnly = xDiagramPropSet->getPropertyValue("IncludeHiddenCells");
1221 bool bIncludeHiddenCells = false;
1222 aPlotVisOnly >>= bIncludeHiddenCells;
1223 pFS->singleElement(FSNS(XML_c, XML_plotVisOnly), XML_val, ToPsz10(!bIncludeHiddenCells));
1225 exportMissingValueTreatment(Reference<beans::XPropertySet>(mxDiagram, uno::UNO_QUERY));
1227 pFS->endElement( FSNS( XML_c, XML_chart ) );
1230 void ChartExport::exportMissingValueTreatment(const uno::Reference<beans::XPropertySet>& xPropSet)
1232 if (!xPropSet.is())
1233 return;
1235 sal_Int32 nVal = 0;
1236 uno::Any aAny = xPropSet->getPropertyValue("MissingValueTreatment");
1237 if (!(aAny >>= nVal))
1238 return;
1240 const char* pVal = nullptr;
1241 switch (nVal)
1243 case cssc::MissingValueTreatment::LEAVE_GAP:
1244 pVal = "gap";
1245 break;
1246 case cssc::MissingValueTreatment::USE_ZERO:
1247 pVal = "zero";
1248 break;
1249 case cssc::MissingValueTreatment::CONTINUE:
1250 pVal = "span";
1251 break;
1252 default:
1253 SAL_WARN("oox", "unknown MissingValueTreatment value");
1254 break;
1257 FSHelperPtr pFS = GetFS();
1258 pFS->singleElement(FSNS(XML_c, XML_dispBlanksAs), XML_val, pVal);
1261 void ChartExport::exportLegend( const Reference< css::chart::XChartDocument >& xChartDoc )
1263 FSHelperPtr pFS = GetFS();
1264 pFS->startElement(FSNS(XML_c, XML_legend));
1266 Reference< beans::XPropertySet > xProp( xChartDoc->getLegend(), uno::UNO_QUERY );
1267 if( xProp.is() )
1269 // position
1270 css::chart::ChartLegendPosition aLegendPos = css::chart::ChartLegendPosition_NONE;
1273 Any aAny( xProp->getPropertyValue( "Alignment" ));
1274 aAny >>= aLegendPos;
1276 catch( beans::UnknownPropertyException & )
1278 SAL_WARN("oox", "Property Align not found in ChartLegend");
1281 const char* strPos = nullptr;
1282 switch( aLegendPos )
1284 case css::chart::ChartLegendPosition_LEFT:
1285 strPos = "l";
1286 break;
1287 case css::chart::ChartLegendPosition_RIGHT:
1288 strPos = "r";
1289 break;
1290 case css::chart::ChartLegendPosition_TOP:
1291 strPos = "t";
1292 break;
1293 case css::chart::ChartLegendPosition_BOTTOM:
1294 strPos = "b";
1295 break;
1296 case css::chart::ChartLegendPosition_NONE:
1297 case css::chart::ChartLegendPosition::ChartLegendPosition_MAKE_FIXED_SIZE:
1298 // nothing
1299 break;
1302 if( strPos != nullptr )
1304 pFS->singleElement(FSNS(XML_c, XML_legendPos), XML_val, strPos);
1307 // legendEntry
1308 Reference<chart2::XCoordinateSystemContainer> xCooSysContainer(mxNewDiagram, UNO_QUERY_THROW);
1309 const Sequence<Reference<chart2::XCoordinateSystem>> xCooSysSequence(xCooSysContainer->getCoordinateSystems());
1311 sal_Int32 nIndex = 0;
1312 bool bShowLegendEntry;
1313 for (const auto& rCooSys : xCooSysSequence)
1315 PropertySet aCooSysProp(rCooSys);
1316 bool bSwapXAndY = aCooSysProp.getBoolProperty(PROP_SwapXAndYAxis);
1318 Reference<chart2::XChartTypeContainer> xChartTypeContainer(rCooSys, UNO_QUERY_THROW);
1319 const Sequence<Reference<chart2::XChartType>> xChartTypeSequence(xChartTypeContainer->getChartTypes());
1320 if (!xChartTypeSequence.hasElements())
1321 continue;
1323 for (const auto& rCT : xChartTypeSequence)
1325 Reference<chart2::XDataSeriesContainer> xDSCont(rCT, UNO_QUERY);
1326 if (!xDSCont.is())
1327 continue;
1329 OUString aChartType(rCT->getChartType());
1330 bool bIsPie = lcl_getChartType(aChartType) == chart::TYPEID_PIE;
1331 if (bIsPie)
1333 PropertySet xChartTypeProp(rCT);
1334 bIsPie = !xChartTypeProp.getBoolProperty(PROP_UseRings);
1336 const Sequence<Reference<chart2::XDataSeries>> aDataSeriesSeq = xDSCont->getDataSeries();
1337 if (bSwapXAndY)
1338 nIndex += aDataSeriesSeq.getLength() - 1;
1339 for (const auto& rDataSeries : aDataSeriesSeq)
1341 PropertySet aSeriesProp(rDataSeries);
1342 bool bVaryColorsByPoint = aSeriesProp.getBoolProperty(PROP_VaryColorsByPoint);
1343 if (bVaryColorsByPoint || bIsPie)
1345 Sequence<sal_Int32> deletedLegendEntriesSeq;
1346 aSeriesProp.getProperty(deletedLegendEntriesSeq, PROP_DeletedLegendEntries);
1347 for (const auto& deletedLegendEntry : std::as_const(deletedLegendEntriesSeq))
1349 pFS->startElement(FSNS(XML_c, XML_legendEntry));
1350 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val,
1351 OString::number(nIndex + deletedLegendEntry));
1352 pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, "1");
1353 pFS->endElement(FSNS(XML_c, XML_legendEntry));
1355 Reference<chart2::data::XDataSource> xDSrc(rDataSeries, UNO_QUERY);
1356 if (!xDSrc.is())
1357 continue;
1359 const Sequence<Reference<chart2::data::XLabeledDataSequence>> aDataSeqs = xDSrc->getDataSequences();
1360 for (const auto& rDataSeq : aDataSeqs)
1362 Reference<chart2::data::XDataSequence> xValues = rDataSeq->getValues();
1363 if (!xValues.is())
1364 continue;
1366 sal_Int32 nDataSeqSize = xValues->getData().getLength();
1367 nIndex += nDataSeqSize;
1370 else
1372 bShowLegendEntry = aSeriesProp.getBoolProperty(PROP_ShowLegendEntry);
1373 if (!bShowLegendEntry)
1375 pFS->startElement(FSNS(XML_c, XML_legendEntry));
1376 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val,
1377 OString::number(nIndex));
1378 pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, "1");
1379 pFS->endElement(FSNS(XML_c, XML_legendEntry));
1381 bSwapXAndY ? nIndex-- : nIndex++;
1384 if (bSwapXAndY)
1385 nIndex += aDataSeriesSeq.getLength() + 1;
1389 uno::Any aRelativePos = xProp->getPropertyValue("RelativePosition");
1390 if (aRelativePos.hasValue())
1392 pFS->startElement(FSNS(XML_c, XML_layout));
1393 pFS->startElement(FSNS(XML_c, XML_manualLayout));
1395 pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
1396 pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
1397 chart2::RelativePosition aPos = aRelativePos.get<chart2::RelativePosition>();
1399 const double x = aPos.Primary;
1400 const double y = aPos.Secondary;
1402 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
1403 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
1405 uno::Any aRelativeSize = xProp->getPropertyValue("RelativeSize");
1406 if (aRelativeSize.hasValue())
1408 chart2::RelativeSize aSize = aRelativeSize.get<chart2::RelativeSize>();
1410 const double w = aSize.Primary;
1411 const double h = aSize.Secondary;
1413 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, OString::number(w));
1415 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, OString::number(h));
1418 SAL_WARN_IF(aPos.Anchor != css::drawing::Alignment_TOP_LEFT, "oox", "unsupported anchor position");
1420 pFS->endElement(FSNS(XML_c, XML_manualLayout));
1421 pFS->endElement(FSNS(XML_c, XML_layout));
1424 if (strPos != nullptr)
1426 uno::Any aOverlay = xProp->getPropertyValue("Overlay");
1427 if(aOverlay.get<bool>())
1428 pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, "1");
1429 else
1430 pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, "0");
1433 // shape properties
1434 exportShapeProps( xProp );
1436 // draw-chart:txPr text properties
1437 exportTextProps( xProp );
1440 pFS->endElement( FSNS( XML_c, XML_legend ) );
1443 void ChartExport::exportTitle( const Reference< XShape >& xShape, const OUString* pSubText)
1445 OUString sText;
1446 Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
1447 if( xPropSet.is())
1449 xPropSet->getPropertyValue("String") >>= sText;
1452 // tdf#101322: add subtitle to title
1453 if( pSubText )
1454 sText = sText.isEmpty() ? *pSubText : sText + "\n" + *pSubText;
1456 if( sText.isEmpty() )
1457 return;
1459 FSHelperPtr pFS = GetFS();
1460 pFS->startElement(FSNS(XML_c, XML_title));
1462 pFS->startElement(FSNS(XML_c, XML_tx));
1463 pFS->startElement(FSNS(XML_c, XML_rich));
1465 // TODO: bodyPr
1466 const char* sWritingMode = nullptr;
1467 bool bVertical = false;
1468 xPropSet->getPropertyValue("StackedText") >>= bVertical;
1469 if( bVertical )
1470 sWritingMode = "wordArtVert";
1472 sal_Int32 nRotation = 0;
1473 xPropSet->getPropertyValue("TextRotation") >>= nRotation;
1475 pFS->singleElement( FSNS( XML_a, XML_bodyPr ),
1476 XML_vert, sWritingMode,
1477 XML_rot, oox::drawingml::calcRotationValue(nRotation) );
1478 // TODO: lstStyle
1479 pFS->singleElement(FSNS(XML_a, XML_lstStyle));
1480 // FIXME: handle multiple paragraphs to parse aText
1481 pFS->startElement(FSNS(XML_a, XML_p));
1483 pFS->startElement(FSNS(XML_a, XML_pPr));
1485 bool bDummy = false;
1486 sal_Int32 nDummy;
1487 WriteRunProperties(xPropSet, false, XML_defRPr, true, bDummy, nDummy );
1489 pFS->endElement( FSNS( XML_a, XML_pPr ) );
1491 pFS->startElement(FSNS(XML_a, XML_r));
1492 bDummy = false;
1493 WriteRunProperties( xPropSet, false, XML_rPr, true, bDummy, nDummy );
1494 pFS->startElement(FSNS(XML_a, XML_t));
1495 pFS->writeEscaped( sText );
1496 pFS->endElement( FSNS( XML_a, XML_t ) );
1497 pFS->endElement( FSNS( XML_a, XML_r ) );
1499 pFS->endElement( FSNS( XML_a, XML_p ) );
1501 pFS->endElement( FSNS( XML_c, XML_rich ) );
1502 pFS->endElement( FSNS( XML_c, XML_tx ) );
1504 uno::Any aManualLayout = xPropSet->getPropertyValue("RelativePosition");
1505 if (aManualLayout.hasValue())
1507 pFS->startElement(FSNS(XML_c, XML_layout));
1508 pFS->startElement(FSNS(XML_c, XML_manualLayout));
1509 pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
1510 pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
1512 Reference<embed::XVisualObject> xVisObject(mxChartModel, uno::UNO_QUERY);
1513 awt::Size aPageSize = xVisObject->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT);
1515 awt::Size aSize = xShape->getSize();
1516 awt::Point aPos2 = xShape->getPosition();
1517 // rotated shapes need special handling...
1518 double fSin = fabs(sin(basegfx::deg2rad<100>(nRotation)));
1519 // remove part of height from X direction, if title is rotated down
1520 if( nRotation*0.01 > 180.0 )
1521 aPos2.X -= static_cast<sal_Int32>(fSin * aSize.Height + 0.5);
1522 // remove part of width from Y direction, if title is rotated up
1523 else if( nRotation*0.01 > 0.0 )
1524 aPos2.Y -= static_cast<sal_Int32>(fSin * aSize.Width + 0.5);
1526 double x = static_cast<double>(aPos2.X) / static_cast<double>(aPageSize.Width);
1527 double y = static_cast<double>(aPos2.Y) / static_cast<double>(aPageSize.Height);
1529 pFS->singleElement(FSNS(XML_c, XML_wMode), XML_val, "edge");
1530 pFS->singleElement(FSNS(XML_c, XML_hMode), XML_val, "edge");
1532 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
1533 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
1535 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, "");
1536 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, "");
1538 pFS->endElement(FSNS(XML_c, XML_manualLayout));
1539 pFS->endElement(FSNS(XML_c, XML_layout));
1542 pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, "0");
1544 // shape properties
1545 if( xPropSet.is() )
1547 exportShapeProps( xPropSet );
1550 pFS->endElement( FSNS( XML_c, XML_title ) );
1553 namespace {
1555 std::vector<Sequence<Reference<chart2::XDataSeries> > > splitDataSeriesByAxis(const Reference< chart2::XChartType >& xChartType)
1557 std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitSeries;
1558 std::map<sal_Int32, size_t> aMapAxisToIndex;
1560 Reference< chart2::XDataSeriesContainer > xDSCnt(xChartType, uno::UNO_QUERY);
1561 if (xDSCnt.is())
1563 sal_Int32 nAxisIndexOfFirstSeries = -1;
1564 const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq(xDSCnt->getDataSeries());
1565 for (const uno::Reference<chart2::XDataSeries>& xSeries : aSeriesSeq)
1567 Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY);
1568 if (!xPropSet.is())
1569 continue;
1571 sal_Int32 nAxisIndex = -1;
1572 uno::Any aAny = xPropSet->getPropertyValue("AttachedAxisIndex");
1573 aAny >>= nAxisIndex;
1574 size_t nVectorPos = 0;
1575 if (nAxisIndexOfFirstSeries == -1)
1577 nAxisIndexOfFirstSeries = nAxisIndex;
1580 auto it = aMapAxisToIndex.find(nAxisIndex);
1581 if (it == aMapAxisToIndex.end())
1583 aSplitSeries.emplace_back();
1584 nVectorPos = aSplitSeries.size() - 1;
1585 aMapAxisToIndex.insert(std::pair<sal_Int32, size_t>(nAxisIndex, nVectorPos));
1587 else
1589 nVectorPos = it->second;
1592 uno::Sequence<Reference<chart2::XDataSeries> >& rAxisSeriesSeq = aSplitSeries[nVectorPos];
1593 sal_Int32 nLength = rAxisSeriesSeq.getLength();
1594 rAxisSeriesSeq.realloc(nLength + 1);
1595 rAxisSeriesSeq.getArray()[nLength] = xSeries;
1597 // if the first series attached to secondary axis, then export those series first, which are attached to primary axis
1598 // also the MS Office export every time in this order
1599 if (aSplitSeries.size() > 1 && nAxisIndexOfFirstSeries == 1)
1601 std::swap(aSplitSeries[0], aSplitSeries[1]);
1605 return aSplitSeries;
1610 void ChartExport::exportPlotArea(const Reference< css::chart::XChartDocument >& xChartDoc)
1612 Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( mxNewDiagram, uno::UNO_QUERY );
1613 if( ! xBCooSysCnt.is())
1614 return;
1616 // plot-area element
1618 FSHelperPtr pFS = GetFS();
1619 pFS->startElement(FSNS(XML_c, XML_plotArea));
1621 Reference<beans::XPropertySet> xWall(mxNewDiagram, uno::UNO_QUERY);
1622 if( xWall.is() )
1624 uno::Any aAny = xWall->getPropertyValue("RelativePosition");
1625 if (aAny.hasValue())
1627 chart2::RelativePosition aPos = aAny.get<chart2::RelativePosition>();
1628 aAny = xWall->getPropertyValue("RelativeSize");
1629 chart2::RelativeSize aSize = aAny.get<chart2::RelativeSize>();
1630 uno::Reference< css::chart::XDiagramPositioning > xDiagramPositioning( xChartDoc->getDiagram(), uno::UNO_QUERY );
1631 exportManualLayout(aPos, aSize, xDiagramPositioning->isExcludingDiagramPositioning() );
1635 // chart type
1636 const Sequence< Reference< chart2::XCoordinateSystem > >
1637 aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
1639 // tdf#123647 Save empty chart as empty bar chart.
1640 if (!aCooSysSeq.hasElements())
1642 pFS->startElement(FSNS(XML_c, XML_barChart));
1643 pFS->singleElement(FSNS(XML_c, XML_barDir), XML_val, "col");
1644 pFS->singleElement(FSNS(XML_c, XML_grouping), XML_val, "clustered");
1645 pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, "0");
1646 exportAxesId(true);
1647 pFS->endElement(FSNS(XML_c, XML_barChart));
1650 for( const auto& rCS : aCooSysSeq )
1652 Reference< chart2::XChartTypeContainer > xCTCnt( rCS, uno::UNO_QUERY );
1653 if( ! xCTCnt.is())
1654 continue;
1655 mnSeriesCount=0;
1656 const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
1657 for( const auto& rCT : aCTSeq )
1659 Reference< chart2::XDataSeriesContainer > xDSCnt( rCT, uno::UNO_QUERY );
1660 if( ! xDSCnt.is())
1661 return;
1662 Reference< chart2::XChartType > xChartType( rCT, uno::UNO_QUERY );
1663 if( ! xChartType.is())
1664 continue;
1665 // note: if xDSCnt.is() then also aCTSeq[nCTIdx]
1666 OUString aChartType( xChartType->getChartType());
1667 sal_Int32 eChartType = lcl_getChartType( aChartType );
1668 switch( eChartType )
1670 case chart::TYPEID_BAR:
1672 exportBarChart( xChartType );
1673 break;
1675 case chart::TYPEID_AREA:
1677 exportAreaChart( xChartType );
1678 break;
1680 case chart::TYPEID_LINE:
1682 exportLineChart( xChartType );
1683 break;
1685 case chart::TYPEID_BUBBLE:
1687 exportBubbleChart( xChartType );
1688 break;
1690 case chart::TYPEID_OFPIE:
1692 break;
1694 case chart::TYPEID_DOUGHNUT:
1695 case chart::TYPEID_PIE:
1697 exportPieChart( xChartType );
1698 break;
1700 case chart::TYPEID_RADARLINE:
1701 case chart::TYPEID_RADARAREA:
1703 exportRadarChart( xChartType );
1704 break;
1706 case chart::TYPEID_SCATTER:
1708 exportScatterChart( xChartType );
1709 break;
1711 case chart::TYPEID_STOCK:
1713 exportStockChart( xChartType );
1714 break;
1716 case chart::TYPEID_SURFACE:
1718 exportSurfaceChart( xChartType );
1719 break;
1721 default:
1723 SAL_WARN("oox", "ChartExport::exportPlotArea -- not support chart type");
1724 break;
1730 //Axis Data
1731 exportAxes( );
1733 // Data Table
1734 exportDataTable();
1736 // shape properties
1738 * Export the Plot area Shape Properties
1739 * eg: Fill and Outline
1741 Reference< css::chart::X3DDisplay > xWallFloorSupplier( mxDiagram, uno::UNO_QUERY );
1742 // tdf#114139 For 2D charts Plot Area equivalent is Chart Wall.
1743 // Unfortunately LibreOffice doesn't have Plot Area equivalent for 3D charts.
1744 // It means that Plot Area couldn't be displayed and changed for 3D chars in LibreOffice.
1745 // We cannot write Wall attributes into Plot Area for 3D charts, because Wall us used as background wall.
1746 if( !mbIs3DChart && xWallFloorSupplier.is() )
1748 Reference< beans::XPropertySet > xWallPropSet = xWallFloorSupplier->getWall();
1749 if( xWallPropSet.is() )
1751 uno::Any aAny = xWallPropSet->getPropertyValue("LineStyle");
1752 sal_Int32 eChartType = getChartType( );
1753 // Export LineStyle_NONE instead of default linestyle of PlotArea border, because LibreOffice
1754 // make invisible the Wall shape properties, in case of these charts. Or in the future set
1755 // the default LineStyle of these charts to LineStyle_NONE.
1756 bool noSupportWallProp = ( (eChartType == chart::TYPEID_PIE) || (eChartType == chart::TYPEID_RADARLINE) || (eChartType == chart::TYPEID_RADARAREA) );
1757 if ( noSupportWallProp && (aAny != drawing::LineStyle_NONE) )
1759 xWallPropSet->setPropertyValue( "LineStyle", uno::Any(drawing::LineStyle_NONE) );
1761 exportShapeProps( xWallPropSet );
1765 pFS->endElement( FSNS( XML_c, XML_plotArea ) );
1769 void ChartExport::exportManualLayout(const css::chart2::RelativePosition& rPos,
1770 const css::chart2::RelativeSize& rSize,
1771 const bool bIsExcludingDiagramPositioning)
1773 FSHelperPtr pFS = GetFS();
1774 pFS->startElement(FSNS(XML_c, XML_layout));
1775 pFS->startElement(FSNS(XML_c, XML_manualLayout));
1777 // By default layoutTarget is set to "outer" and we shouldn't save it in that case
1778 if ( bIsExcludingDiagramPositioning )
1780 pFS->singleElement(FSNS(XML_c, XML_layoutTarget), XML_val, "inner");
1782 pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
1783 pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
1785 double x = rPos.Primary;
1786 double y = rPos.Secondary;
1787 const double w = rSize.Primary;
1788 const double h = rSize.Secondary;
1789 switch (rPos.Anchor)
1791 case drawing::Alignment_LEFT:
1792 y -= (h/2);
1793 break;
1794 case drawing::Alignment_TOP_LEFT:
1795 break;
1796 case drawing::Alignment_BOTTOM_LEFT:
1797 y -= h;
1798 break;
1799 case drawing::Alignment_TOP:
1800 x -= (w/2);
1801 break;
1802 case drawing::Alignment_CENTER:
1803 x -= (w/2);
1804 y -= (h/2);
1805 break;
1806 case drawing::Alignment_BOTTOM:
1807 x -= (w/2);
1808 y -= h;
1809 break;
1810 case drawing::Alignment_TOP_RIGHT:
1811 x -= w;
1812 break;
1813 case drawing::Alignment_BOTTOM_RIGHT:
1814 x -= w;
1815 y -= h;
1816 break;
1817 case drawing::Alignment_RIGHT:
1818 y -= (h/2);
1819 x -= w;
1820 break;
1821 default:
1822 SAL_WARN("oox", "unhandled alignment case for manual layout export " << static_cast<sal_uInt16>(rPos.Anchor));
1825 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
1827 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
1829 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, OString::number(w));
1831 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, OString::number(h));
1833 pFS->endElement(FSNS(XML_c, XML_manualLayout));
1834 pFS->endElement(FSNS(XML_c, XML_layout));
1837 void ChartExport::exportFill( const Reference< XPropertySet >& xPropSet )
1839 // Similar to DrawingML::WriteFill, but gradient access via name
1840 if (!GetProperty( xPropSet, "FillStyle" ))
1841 return;
1842 FillStyle aFillStyle(FillStyle_NONE);
1843 mAny >>= aFillStyle;
1845 // map full transparent background to no fill
1846 if (aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, "FillTransparence" ))
1848 sal_Int16 nVal = 0;
1849 mAny >>= nVal;
1850 if ( nVal == 100 )
1851 aFillStyle = FillStyle_NONE;
1853 OUString sFillTransparenceGradientName;
1854 if (aFillStyle == FillStyle_SOLID
1855 && GetProperty(xPropSet, "FillTransparenceGradientName") && (mAny >>= sFillTransparenceGradientName)
1856 && !sFillTransparenceGradientName.isEmpty())
1858 awt::Gradient aTransparenceGradient;
1859 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
1860 uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY);
1861 uno::Any rTransparenceValue = xTransparenceGradient->getByName(sFillTransparenceGradientName);
1862 rTransparenceValue >>= aTransparenceGradient;
1863 if (aTransparenceGradient.StartColor == 0xffffff && aTransparenceGradient.EndColor == 0xffffff)
1864 aFillStyle = FillStyle_NONE;
1866 switch( aFillStyle )
1868 case FillStyle_SOLID:
1869 exportSolidFill(xPropSet);
1870 break;
1871 case FillStyle_GRADIENT :
1872 exportGradientFill( xPropSet );
1873 break;
1874 case FillStyle_BITMAP :
1875 exportBitmapFill( xPropSet );
1876 break;
1877 case FillStyle_HATCH:
1878 exportHatch(xPropSet);
1879 break;
1880 case FillStyle_NONE:
1881 mpFS->singleElementNS(XML_a, XML_noFill);
1882 break;
1883 default:
1888 void ChartExport::exportSolidFill(const Reference< XPropertySet >& xPropSet)
1890 // Similar to DrawingML::WriteSolidFill, but gradient access via name
1891 // and currently no InteropGrabBag
1892 // get fill color
1893 sal_uInt32 nFillColor = 0;
1894 if (!GetProperty(xPropSet, "FillColor") || !(mAny >>= nFillColor))
1895 return;
1897 sal_Int32 nAlpha = MAX_PERCENT;
1898 if (GetProperty( xPropSet, "FillTransparence" ))
1900 sal_Int32 nTransparency = 0;
1901 mAny >>= nTransparency;
1902 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
1903 nAlpha = (MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
1905 // OOXML has no separate transparence gradient but uses transparency in the gradient stops.
1906 // So we merge transparency and color and use gradient fill in such case.
1907 basegfx::BGradient aTransparenceGradient;
1908 bool bNeedGradientFill(false);
1909 OUString sFillTransparenceGradientName;
1911 if (GetProperty(xPropSet, "FillTransparenceGradientName")
1912 && (mAny >>= sFillTransparenceGradientName)
1913 && !sFillTransparenceGradientName.isEmpty())
1915 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
1916 uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY);
1917 const uno::Any rTransparenceAny = xTransparenceGradient->getByName(sFillTransparenceGradientName);
1919 aTransparenceGradient = model::gradient::getFromAny(rTransparenceAny);
1920 basegfx::BColor aSingleColor;
1921 bNeedGradientFill = !aTransparenceGradient.GetColorStops().isSingleColor(aSingleColor);
1923 if (!bNeedGradientFill)
1925 // Our alpha is a single gray color value.
1926 const sal_uInt8 nRed(aSingleColor.getRed() * 255.0);
1928 // drawingML alpha is a percentage on a 0..100000 scale.
1929 nAlpha = (255 - nRed) * oox::drawingml::MAX_PERCENT / 255;
1932 // write XML
1933 if (bNeedGradientFill)
1935 // no longer create copy/PseudoColorGradient, use new API of
1936 // WriteGradientFill to express fix fill color
1937 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
1938 WriteGradientFill(nullptr, nFillColor, &aTransparenceGradient);
1939 mpFS->endElementNS(XML_a, XML_gradFill);
1941 else
1942 WriteSolidFill(::Color(ColorTransparency, nFillColor & 0xffffff), nAlpha);
1945 void ChartExport::exportHatch( const Reference< XPropertySet >& xPropSet )
1947 if (!xPropSet.is())
1948 return;
1950 if (GetProperty(xPropSet, "FillHatchName"))
1952 OUString aHatchName;
1953 mAny >>= aHatchName;
1954 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
1955 uno::Reference< container::XNameAccess > xHatchTable( xFact->createInstance("com.sun.star.drawing.HatchTable"), uno::UNO_QUERY );
1956 uno::Any rValue = xHatchTable->getByName(aHatchName);
1957 css::drawing::Hatch aHatch;
1958 rValue >>= aHatch;
1959 WritePattFill(xPropSet, aHatch);
1964 void ChartExport::exportBitmapFill( const Reference< XPropertySet >& xPropSet )
1966 if( !xPropSet.is() )
1967 return;
1969 OUString sFillBitmapName;
1970 xPropSet->getPropertyValue("FillBitmapName") >>= sFillBitmapName;
1972 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
1975 uno::Reference< container::XNameAccess > xBitmapTable( xFact->createInstance("com.sun.star.drawing.BitmapTable"), uno::UNO_QUERY );
1976 uno::Any rValue = xBitmapTable->getByName( sFillBitmapName );
1977 if (rValue.has<uno::Reference<awt::XBitmap>>())
1979 uno::Reference<awt::XBitmap> xBitmap = rValue.get<uno::Reference<awt::XBitmap>>();
1980 uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY);
1981 if (xGraphic.is())
1983 WriteXGraphicBlipFill(xPropSet, xGraphic, XML_a, true, true);
1987 catch (const uno::Exception &)
1989 TOOLS_WARN_EXCEPTION("oox", "ChartExport::exportBitmapFill");
1993 void ChartExport::exportGradientFill( const Reference< XPropertySet >& xPropSet )
1995 if( !xPropSet.is() )
1996 return;
1998 OUString sFillGradientName;
1999 xPropSet->getPropertyValue("FillGradientName") >>= sFillGradientName;
2001 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
2004 uno::Reference< container::XNameAccess > xGradient( xFact->createInstance("com.sun.star.drawing.GradientTable"), uno::UNO_QUERY );
2005 const uno::Any rGradientAny(xGradient->getByName( sFillGradientName ));
2006 const basegfx::BGradient aGradient = model::gradient::getFromAny(rGradientAny);
2007 basegfx::BColor aSingleColor;
2009 if (!aGradient.GetColorStops().isSingleColor(aSingleColor))
2011 basegfx::BGradient aTransparenceGradient;
2012 mpFS->startElementNS(XML_a, XML_gradFill);
2013 OUString sFillTransparenceGradientName;
2015 if( (xPropSet->getPropertyValue("FillTransparenceGradientName") >>= sFillTransparenceGradientName) && !sFillTransparenceGradientName.isEmpty())
2017 uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY);
2018 const uno::Any rTransparenceAny(xTransparenceGradient->getByName(sFillTransparenceGradientName));
2020 aTransparenceGradient = model::gradient::getFromAny(rTransparenceAny);
2022 WriteGradientFill(&aGradient, 0, &aTransparenceGradient);
2024 else if (GetProperty(xPropSet, "FillTransparence") )
2026 // no longer create PseudoTransparencyGradient, use new API of
2027 // WriteGradientFill to express fix transparency
2028 sal_Int32 nTransparency = 0;
2029 mAny >>= nTransparency;
2030 // nTransparency is [0..100]%
2031 WriteGradientFill(&aGradient, 0, nullptr, nTransparency * 0.01);
2033 else
2035 WriteGradientFill(&aGradient, 0, nullptr);
2038 mpFS->endElementNS(XML_a, XML_gradFill);
2041 catch (const uno::Exception &)
2043 TOOLS_INFO_EXCEPTION("oox", "ChartExport::exportGradientFill");
2047 void ChartExport::exportDataTable( )
2049 auto xDataTable = mxNewDiagram->getDataTable();
2050 if (!xDataTable.is())
2051 return;
2053 FSHelperPtr pFS = GetFS();
2054 uno::Reference<beans::XPropertySet> aPropSet(xDataTable, uno::UNO_QUERY);
2056 bool bShowVBorder = false;
2057 bool bShowHBorder = false;
2058 bool bShowOutline = false;
2059 bool bShowKeys = false;
2061 if (GetProperty(aPropSet, "HBorder"))
2062 mAny >>= bShowHBorder;
2063 if (GetProperty(aPropSet, "VBorder"))
2064 mAny >>= bShowVBorder;
2065 if (GetProperty(aPropSet, "Outline"))
2066 mAny >>= bShowOutline;
2067 if (GetProperty(aPropSet, "Keys"))
2068 mAny >>= bShowKeys;
2070 pFS->startElement(FSNS(XML_c, XML_dTable));
2072 if (bShowHBorder)
2073 pFS->singleElement(FSNS(XML_c, XML_showHorzBorder), XML_val, "1" );
2074 if (bShowVBorder)
2075 pFS->singleElement(FSNS(XML_c, XML_showVertBorder), XML_val, "1");
2076 if (bShowOutline)
2077 pFS->singleElement(FSNS(XML_c, XML_showOutline), XML_val, "1");
2078 if (bShowKeys)
2079 pFS->singleElement(FSNS(XML_c, XML_showKeys), XML_val, "1");
2081 exportShapeProps(aPropSet);
2082 exportTextProps(aPropSet);
2084 pFS->endElement(FSNS(XML_c, XML_dTable));
2087 void ChartExport::exportAreaChart( const Reference< chart2::XChartType >& xChartType )
2089 FSHelperPtr pFS = GetFS();
2090 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2091 for (const auto& splitDataSeries : aSplitDataSeries)
2093 if (!splitDataSeries.hasElements())
2094 continue;
2096 sal_Int32 nTypeId = XML_areaChart;
2097 if (mbIs3DChart)
2098 nTypeId = XML_area3DChart;
2099 pFS->startElement(FSNS(XML_c, nTypeId));
2101 exportGrouping();
2102 bool bPrimaryAxes = true;
2103 exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
2104 exportAxesId(bPrimaryAxes);
2106 pFS->endElement(FSNS(XML_c, nTypeId));
2110 void ChartExport::exportBarChart(const Reference< chart2::XChartType >& xChartType)
2112 sal_Int32 nTypeId = XML_barChart;
2113 if (mbIs3DChart)
2114 nTypeId = XML_bar3DChart;
2115 FSHelperPtr pFS = GetFS();
2117 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2118 for (const auto& splitDataSeries : aSplitDataSeries)
2120 if (!splitDataSeries.hasElements())
2121 continue;
2123 pFS->startElement(FSNS(XML_c, nTypeId));
2124 // bar direction
2125 bool bVertical = false;
2126 Reference< XPropertySet > xPropSet(mxDiagram, uno::UNO_QUERY);
2127 if (GetProperty(xPropSet, "Vertical"))
2128 mAny >>= bVertical;
2130 const char* bardir = bVertical ? "bar" : "col";
2131 pFS->singleElement(FSNS(XML_c, XML_barDir), XML_val, bardir);
2133 exportGrouping(true);
2135 exportVaryColors(xChartType);
2137 bool bPrimaryAxes = true;
2138 exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
2140 Reference< XPropertySet > xTypeProp(xChartType, uno::UNO_QUERY);
2142 if (xTypeProp.is() && GetProperty(xTypeProp, "GapwidthSequence"))
2144 uno::Sequence< sal_Int32 > aBarPositionSequence;
2145 mAny >>= aBarPositionSequence;
2146 if (aBarPositionSequence.hasElements())
2148 sal_Int32 nGapWidth = aBarPositionSequence[0];
2149 pFS->singleElement(FSNS(XML_c, XML_gapWidth), XML_val, OString::number(nGapWidth));
2153 if (mbIs3DChart)
2155 // Shape
2156 namespace cssc = css::chart;
2157 sal_Int32 nGeom3d = cssc::ChartSolidType::RECTANGULAR_SOLID;
2158 if (xPropSet.is() && GetProperty(xPropSet, "SolidType"))
2159 mAny >>= nGeom3d;
2160 const char* sShapeType = nullptr;
2161 switch (nGeom3d)
2163 case cssc::ChartSolidType::RECTANGULAR_SOLID:
2164 sShapeType = "box";
2165 break;
2166 case cssc::ChartSolidType::CONE:
2167 sShapeType = "cone";
2168 break;
2169 case cssc::ChartSolidType::CYLINDER:
2170 sShapeType = "cylinder";
2171 break;
2172 case cssc::ChartSolidType::PYRAMID:
2173 sShapeType = "pyramid";
2174 break;
2176 pFS->singleElement(FSNS(XML_c, XML_shape), XML_val, sShapeType);
2179 //overlap
2180 if (!mbIs3DChart && xTypeProp.is() && GetProperty(xTypeProp, "OverlapSequence"))
2182 uno::Sequence< sal_Int32 > aBarPositionSequence;
2183 mAny >>= aBarPositionSequence;
2184 if (aBarPositionSequence.hasElements())
2186 sal_Int32 nOverlap = aBarPositionSequence[0];
2187 // Stacked/Percent Bar/Column chart Overlap-workaround
2188 // Export the Overlap value with 100% for stacked charts,
2189 // because the default overlap value of the Bar/Column chart is 0% and
2190 // LibreOffice do nothing with the overlap value in Stacked charts case,
2191 // unlike the MS Office, which is interpreted differently.
2192 if ((mbStacked || mbPercent) && nOverlap != 100)
2194 nOverlap = 100;
2195 pFS->singleElement(FSNS(XML_c, XML_overlap), XML_val, OString::number(nOverlap));
2197 else // Normal bar chart
2199 pFS->singleElement(FSNS(XML_c, XML_overlap), XML_val, OString::number(nOverlap));
2204 exportAxesId(bPrimaryAxes);
2206 pFS->endElement(FSNS(XML_c, nTypeId));
2210 void ChartExport::exportBubbleChart( const Reference< chart2::XChartType >& xChartType )
2212 FSHelperPtr pFS = GetFS();
2213 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2214 for (const auto& splitDataSeries : aSplitDataSeries)
2216 if (!splitDataSeries.hasElements())
2217 continue;
2219 pFS->startElement(FSNS(XML_c, XML_bubbleChart));
2221 exportVaryColors(xChartType);
2223 bool bPrimaryAxes = true;
2224 exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
2226 exportAxesId(bPrimaryAxes);
2228 pFS->endElement(FSNS(XML_c, XML_bubbleChart));
2232 void ChartExport::exportDoughnutChart( const Reference< chart2::XChartType >& xChartType )
2234 FSHelperPtr pFS = GetFS();
2235 pFS->startElement(FSNS(XML_c, XML_doughnutChart));
2237 exportVaryColors(xChartType);
2239 bool bPrimaryAxes = true;
2240 exportAllSeries(xChartType, bPrimaryAxes);
2241 // firstSliceAng
2242 exportFirstSliceAng( );
2243 //FIXME: holeSize
2244 pFS->singleElement(FSNS(XML_c, XML_holeSize), XML_val, OString::number(50));
2246 pFS->endElement( FSNS( XML_c, XML_doughnutChart ) );
2249 namespace {
2251 void writeDataLabelsRange(const FSHelperPtr& pFS, const XmlFilterBase* pFB, DataLabelsRange& rDLblsRange)
2253 if (rDLblsRange.empty())
2254 return;
2256 pFS->startElement(FSNS(XML_c, XML_extLst));
2257 pFS->startElement(FSNS(XML_c, XML_ext), XML_uri, "{02D57815-91ED-43cb-92C2-25804820EDAC}", FSNS(XML_xmlns, XML_c15), pFB->getNamespaceURL(OOX_NS(c15)));
2258 pFS->startElement(FSNS(XML_c15, XML_datalabelsRange));
2260 // Write cell range.
2261 pFS->startElement(FSNS(XML_c15, XML_f));
2262 pFS->writeEscaped(rDLblsRange.getRange());
2263 pFS->endElement(FSNS(XML_c15, XML_f));
2265 // Write all labels.
2266 pFS->startElement(FSNS(XML_c15, XML_dlblRangeCache));
2267 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(rDLblsRange.count()));
2268 for (const auto& rLabelKV: rDLblsRange)
2270 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(rLabelKV.first));
2271 pFS->startElement(FSNS(XML_c, XML_v));
2272 pFS->writeEscaped(rLabelKV.second);
2273 pFS->endElement(FSNS( XML_c, XML_v ));
2274 pFS->endElement(FSNS(XML_c, XML_pt));
2277 pFS->endElement(FSNS(XML_c15, XML_dlblRangeCache));
2279 pFS->endElement(FSNS(XML_c15, XML_datalabelsRange));
2280 pFS->endElement(FSNS(XML_c, XML_ext));
2281 pFS->endElement(FSNS(XML_c, XML_extLst));
2286 void ChartExport::exportLineChart( const Reference< chart2::XChartType >& xChartType )
2288 FSHelperPtr pFS = GetFS();
2289 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2290 for (const auto& splitDataSeries : aSplitDataSeries)
2292 if (!splitDataSeries.hasElements())
2293 continue;
2295 sal_Int32 nTypeId = XML_lineChart;
2296 if( mbIs3DChart )
2297 nTypeId = XML_line3DChart;
2298 pFS->startElement(FSNS(XML_c, nTypeId));
2300 exportGrouping( );
2302 exportVaryColors(xChartType);
2303 // TODO: show marker symbol in series?
2304 bool bPrimaryAxes = true;
2305 exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
2307 // show marker?
2308 sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE;
2309 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
2310 if( GetProperty( xPropSet, "SymbolType" ) )
2311 mAny >>= nSymbolType;
2313 if( !mbIs3DChart )
2315 exportHiLowLines();
2316 exportUpDownBars(xChartType);
2317 const char* marker = nSymbolType == css::chart::ChartSymbolType::NONE? "0":"1";
2318 pFS->singleElement(FSNS(XML_c, XML_marker), XML_val, marker);
2321 exportAxesId(bPrimaryAxes, true);
2323 pFS->endElement( FSNS( XML_c, nTypeId ) );
2327 void ChartExport::exportPieChart( const Reference< chart2::XChartType >& xChartType )
2329 sal_Int32 eChartType = getChartType( );
2330 if(eChartType == chart::TYPEID_DOUGHNUT)
2332 exportDoughnutChart( xChartType );
2333 return;
2335 FSHelperPtr pFS = GetFS();
2336 sal_Int32 nTypeId = XML_pieChart;
2337 if( mbIs3DChart )
2338 nTypeId = XML_pie3DChart;
2339 pFS->startElement(FSNS(XML_c, nTypeId));
2341 exportVaryColors(xChartType);
2343 bool bPrimaryAxes = true;
2344 exportAllSeries(xChartType, bPrimaryAxes);
2346 if( !mbIs3DChart )
2348 // firstSliceAng
2349 exportFirstSliceAng( );
2352 pFS->endElement( FSNS( XML_c, nTypeId ) );
2355 void ChartExport::exportRadarChart( const Reference< chart2::XChartType >& xChartType)
2357 FSHelperPtr pFS = GetFS();
2358 pFS->startElement(FSNS(XML_c, XML_radarChart));
2360 // radarStyle
2361 sal_Int32 eChartType = getChartType( );
2362 const char* radarStyle = nullptr;
2363 if( eChartType == chart::TYPEID_RADARAREA )
2364 radarStyle = "filled";
2365 else
2366 radarStyle = "marker";
2367 pFS->singleElement(FSNS(XML_c, XML_radarStyle), XML_val, radarStyle);
2369 exportVaryColors(xChartType);
2370 bool bPrimaryAxes = true;
2371 exportAllSeries(xChartType, bPrimaryAxes);
2372 exportAxesId(bPrimaryAxes);
2374 pFS->endElement( FSNS( XML_c, XML_radarChart ) );
2377 void ChartExport::exportScatterChartSeries( const Reference< chart2::XChartType >& xChartType,
2378 const css::uno::Sequence<css::uno::Reference<chart2::XDataSeries>>* pSeries)
2380 FSHelperPtr pFS = GetFS();
2381 pFS->startElement(FSNS(XML_c, XML_scatterChart));
2382 // TODO:scatterStyle
2384 sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE;
2385 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
2386 if( GetProperty( xPropSet, "SymbolType" ) )
2387 mAny >>= nSymbolType;
2389 const char* scatterStyle = "lineMarker";
2390 if (nSymbolType == css::chart::ChartSymbolType::NONE)
2392 scatterStyle = "line";
2395 pFS->singleElement(FSNS(XML_c, XML_scatterStyle), XML_val, scatterStyle);
2397 exportVaryColors(xChartType);
2398 // FIXME: should export xVal and yVal
2399 bool bPrimaryAxes = true;
2400 if (pSeries)
2401 exportSeries(xChartType, *pSeries, bPrimaryAxes);
2402 exportAxesId(bPrimaryAxes);
2404 pFS->endElement( FSNS( XML_c, XML_scatterChart ) );
2407 void ChartExport::exportScatterChart( const Reference< chart2::XChartType >& xChartType )
2409 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2410 bool bExported = false;
2411 for (const auto& splitDataSeries : aSplitDataSeries)
2413 if (!splitDataSeries.hasElements())
2414 continue;
2416 bExported = true;
2417 exportScatterChartSeries(xChartType, &splitDataSeries);
2419 if (!bExported)
2420 exportScatterChartSeries(xChartType, nullptr);
2423 void ChartExport::exportStockChart( const Reference< chart2::XChartType >& xChartType )
2425 FSHelperPtr pFS = GetFS();
2426 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2427 for (const auto& splitDataSeries : aSplitDataSeries)
2429 if (!splitDataSeries.hasElements())
2430 continue;
2432 pFS->startElement(FSNS(XML_c, XML_stockChart));
2434 bool bPrimaryAxes = true;
2435 exportCandleStickSeries(splitDataSeries, bPrimaryAxes);
2437 // export stock properties
2438 Reference< css::chart::XStatisticDisplay > xStockPropProvider(mxDiagram, uno::UNO_QUERY);
2439 if (xStockPropProvider.is())
2441 exportHiLowLines();
2442 exportUpDownBars(xChartType);
2445 exportAxesId(bPrimaryAxes);
2447 pFS->endElement(FSNS(XML_c, XML_stockChart));
2451 void ChartExport::exportHiLowLines()
2453 FSHelperPtr pFS = GetFS();
2454 // export the chart property
2455 Reference< css::chart::XStatisticDisplay > xChartPropProvider( mxDiagram, uno::UNO_QUERY );
2457 if (!xChartPropProvider.is())
2458 return;
2460 Reference< beans::XPropertySet > xStockPropSet = xChartPropProvider->getMinMaxLine();
2461 if( !xStockPropSet.is() )
2462 return;
2464 pFS->startElement(FSNS(XML_c, XML_hiLowLines));
2465 exportShapeProps( xStockPropSet );
2466 pFS->endElement( FSNS( XML_c, XML_hiLowLines ) );
2469 void ChartExport::exportUpDownBars( const Reference< chart2::XChartType >& xChartType)
2471 if(xChartType->getChartType() != "com.sun.star.chart2.CandleStickChartType")
2472 return;
2474 FSHelperPtr pFS = GetFS();
2475 // export the chart property
2476 Reference< css::chart::XStatisticDisplay > xChartPropProvider( mxDiagram, uno::UNO_QUERY );
2477 if(!xChartPropProvider.is())
2478 return;
2480 // updownbar
2481 pFS->startElement(FSNS(XML_c, XML_upDownBars));
2482 // TODO: gapWidth
2483 pFS->singleElement(FSNS(XML_c, XML_gapWidth), XML_val, OString::number(150));
2485 Reference< beans::XPropertySet > xChartPropSet = xChartPropProvider->getUpBar();
2486 if( xChartPropSet.is() )
2488 pFS->startElement(FSNS(XML_c, XML_upBars));
2489 // For Linechart with UpDownBars, spPr is not getting imported
2490 // so no need to call the exportShapeProps() for LineChart
2491 if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType")
2493 exportShapeProps(xChartPropSet);
2495 pFS->endElement( FSNS( XML_c, XML_upBars ) );
2497 xChartPropSet = xChartPropProvider->getDownBar();
2498 if( xChartPropSet.is() )
2500 pFS->startElement(FSNS(XML_c, XML_downBars));
2501 if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType")
2503 exportShapeProps(xChartPropSet);
2505 pFS->endElement( FSNS( XML_c, XML_downBars ) );
2507 pFS->endElement( FSNS( XML_c, XML_upDownBars ) );
2510 void ChartExport::exportSurfaceChart( const Reference< chart2::XChartType >& xChartType )
2512 FSHelperPtr pFS = GetFS();
2513 sal_Int32 nTypeId = XML_surfaceChart;
2514 if( mbIs3DChart )
2515 nTypeId = XML_surface3DChart;
2516 pFS->startElement(FSNS(XML_c, nTypeId));
2517 exportVaryColors(xChartType);
2518 bool bPrimaryAxes = true;
2519 exportAllSeries(xChartType, bPrimaryAxes);
2520 exportAxesId(bPrimaryAxes);
2522 pFS->endElement( FSNS( XML_c, nTypeId ) );
2525 void ChartExport::exportAllSeries(const Reference<chart2::XChartType>& xChartType, bool& rPrimaryAxes)
2527 Reference< chart2::XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY );
2528 if( ! xDSCnt.is())
2529 return;
2531 // export dataseries for current chart-type
2532 Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries());
2533 exportSeries(xChartType, aSeriesSeq, rPrimaryAxes);
2536 void ChartExport::exportVaryColors(const Reference<chart2::XChartType>& xChartType)
2538 FSHelperPtr pFS = GetFS();
2541 Reference<chart2::XDataSeries> xDataSeries = getPrimaryDataSeries(xChartType);
2542 Reference<beans::XPropertySet> xDataSeriesProps(xDataSeries, uno::UNO_QUERY_THROW);
2543 Any aAnyVaryColors = xDataSeriesProps->getPropertyValue("VaryColorsByPoint");
2544 bool bVaryColors = false;
2545 aAnyVaryColors >>= bVaryColors;
2546 pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, ToPsz10(bVaryColors));
2548 catch (...)
2550 pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, "0");
2554 void ChartExport::exportSeries( const Reference<chart2::XChartType>& xChartType,
2555 const Sequence<Reference<chart2::XDataSeries> >& rSeriesSeq, bool& rPrimaryAxes )
2557 OUString aLabelRole = xChartType->getRoleOfSequenceForSeriesLabel();
2558 OUString aChartType( xChartType->getChartType());
2559 sal_Int32 eChartType = lcl_getChartType( aChartType );
2561 for( const auto& rSeries : rSeriesSeq )
2563 // export series
2564 Reference< chart2::data::XDataSource > xSource( rSeries, uno::UNO_QUERY );
2565 if( xSource.is())
2567 Reference< chart2::XDataSeries > xDataSeries( xSource, uno::UNO_QUERY );
2568 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
2569 xSource->getDataSequences());
2570 // search for main sequence and create a series element
2572 sal_Int32 nMainSequenceIndex = -1;
2573 sal_Int32 nSeriesLength = 0;
2574 Reference< chart2::data::XDataSequence > xValuesSeq;
2575 Reference< chart2::data::XDataSequence > xLabelSeq;
2576 sal_Int32 nSeqIdx=0;
2577 for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx )
2579 Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() );
2580 if( nMainSequenceIndex==-1 )
2582 Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY );
2583 OUString aRole;
2584 if( xSeqProp.is())
2585 xSeqProp->getPropertyValue("Role") >>= aRole;
2586 // "main" sequence
2587 if( aRole == aLabelRole )
2589 xValuesSeq.set( xTempValueSeq );
2590 xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel());
2591 nMainSequenceIndex = nSeqIdx;
2594 sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0));
2595 if( nSeriesLength < nSequenceLength )
2596 nSeriesLength = nSequenceLength;
2599 // have found the main sequence, then xValuesSeq and
2600 // xLabelSeq contain those. Otherwise both are empty
2602 FSHelperPtr pFS = GetFS();
2604 pFS->startElement(FSNS(XML_c, XML_ser));
2606 // TODO: idx and order
2607 pFS->singleElement( FSNS( XML_c, XML_idx ),
2608 XML_val, OString::number(mnSeriesCount) );
2609 pFS->singleElement( FSNS( XML_c, XML_order ),
2610 XML_val, OString::number(mnSeriesCount++) );
2612 // export label
2613 if( xLabelSeq.is() )
2614 exportSeriesText( xLabelSeq );
2616 Reference<XPropertySet> xPropSet(xDataSeries, UNO_QUERY_THROW);
2617 if( GetProperty( xPropSet, "AttachedAxisIndex") )
2619 sal_Int32 nLocalAttachedAxis = 0;
2620 mAny >>= nLocalAttachedAxis;
2621 rPrimaryAxes = isPrimaryAxes(nLocalAttachedAxis);
2624 // export shape properties
2625 Reference< XPropertySet > xOldPropSet = SchXMLSeriesHelper::createOldAPISeriesPropertySet(
2626 rSeries, getModel() );
2627 if( xOldPropSet.is() )
2629 exportShapeProps( xOldPropSet );
2632 switch( eChartType )
2634 case chart::TYPEID_BUBBLE:
2635 case chart::TYPEID_HORBAR:
2636 case chart::TYPEID_BAR:
2638 pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0");
2640 break;
2641 case chart::TYPEID_LINE:
2643 exportMarker(xOldPropSet);
2644 break;
2646 case chart::TYPEID_PIE:
2647 case chart::TYPEID_DOUGHNUT:
2649 if( xOldPropSet.is() && GetProperty( xOldPropSet, "SegmentOffset") )
2651 sal_Int32 nOffset = 0;
2652 mAny >>= nOffset;
2653 pFS->singleElement( FSNS( XML_c, XML_explosion ),
2654 XML_val, OString::number( nOffset ) );
2656 break;
2658 case chart::TYPEID_SCATTER:
2660 exportMarker(xOldPropSet);
2661 break;
2663 case chart::TYPEID_RADARLINE:
2665 exportMarker(xOldPropSet);
2666 break;
2670 // export data points
2671 exportDataPoints( uno::Reference< beans::XPropertySet >( rSeries, uno::UNO_QUERY ), nSeriesLength, eChartType );
2673 DataLabelsRange aDLblsRange;
2674 // export data labels
2675 exportDataLabels(rSeries, nSeriesLength, eChartType, aDLblsRange);
2677 exportTrendlines( rSeries );
2679 if( eChartType != chart::TYPEID_PIE &&
2680 eChartType != chart::TYPEID_RADARLINE )
2682 //export error bars here
2683 Reference< XPropertySet > xSeriesPropSet( xSource, uno::UNO_QUERY );
2684 Reference< XPropertySet > xErrorBarYProps;
2685 xSeriesPropSet->getPropertyValue("ErrorBarY") >>= xErrorBarYProps;
2686 if(xErrorBarYProps.is())
2687 exportErrorBar(xErrorBarYProps, true);
2688 if (eChartType != chart::TYPEID_BAR &&
2689 eChartType != chart::TYPEID_HORBAR)
2691 Reference< XPropertySet > xErrorBarXProps;
2692 xSeriesPropSet->getPropertyValue("ErrorBarX") >>= xErrorBarXProps;
2693 if(xErrorBarXProps.is())
2694 exportErrorBar(xErrorBarXProps, false);
2698 // export categories
2699 if( eChartType != chart::TYPEID_SCATTER && eChartType != chart::TYPEID_BUBBLE && mxCategoriesValues.is() )
2700 exportSeriesCategory( mxCategoriesValues );
2702 if( (eChartType == chart::TYPEID_SCATTER)
2703 || (eChartType == chart::TYPEID_BUBBLE) )
2705 // export xVal
2706 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, "values-x" ) );
2707 if( xSequence.is() )
2709 Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
2710 if( xValues.is() )
2711 exportSeriesValues( xValues, XML_xVal );
2713 else if( mxCategoriesValues.is() )
2714 exportSeriesCategory( mxCategoriesValues, XML_xVal );
2717 if( eChartType == chart::TYPEID_BUBBLE )
2719 // export yVal
2720 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, "values-y" ) );
2721 if( xSequence.is() )
2723 Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
2724 if( xValues.is() )
2725 exportSeriesValues( xValues, XML_yVal );
2729 // export values
2730 if( xValuesSeq.is() )
2732 sal_Int32 nYValueType = XML_val;
2733 if( eChartType == chart::TYPEID_SCATTER )
2734 nYValueType = XML_yVal;
2735 else if( eChartType == chart::TYPEID_BUBBLE )
2736 nYValueType = XML_bubbleSize;
2737 exportSeriesValues( xValuesSeq, nYValueType );
2740 if( eChartType == chart::TYPEID_SCATTER
2741 || eChartType == chart::TYPEID_LINE )
2742 exportSmooth();
2744 // tdf103988: "corrupted" files with Bubble chart opening in MSO
2745 if( eChartType == chart::TYPEID_BUBBLE )
2746 pFS->singleElement(FSNS(XML_c, XML_bubble3D), XML_val, "0");
2748 if (!aDLblsRange.empty())
2749 writeDataLabelsRange(pFS, GetFB(), aDLblsRange);
2751 pFS->endElement( FSNS( XML_c, XML_ser ) );
2758 void ChartExport::exportCandleStickSeries(
2759 const Sequence< Reference< chart2::XDataSeries > > & aSeriesSeq,
2760 bool& rPrimaryAxes)
2762 for( const Reference< chart2::XDataSeries >& xSeries : aSeriesSeq )
2764 rPrimaryAxes = lcl_isSeriesAttachedToFirstAxis(xSeries);
2766 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
2767 if( xSource.is())
2769 // export series in correct order (as we don't store roles)
2770 // with japanese candlesticks: open, low, high, close
2771 // otherwise: low, high, close
2772 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
2773 xSource->getDataSequences());
2775 const char* sSeries[] = {"values-first","values-max","values-min","values-last",nullptr};
2777 for( sal_Int32 idx = 0; sSeries[idx] != nullptr ; idx++ )
2779 Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( lcl_getDataSequenceByRole( aSeqCnt, OUString::createFromAscii(sSeries[idx]) ) );
2780 if( xLabeledSeq.is())
2782 Reference< chart2::data::XDataSequence > xLabelSeq( xLabeledSeq->getLabel());
2783 Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues());
2785 FSHelperPtr pFS = GetFS();
2786 pFS->startElement(FSNS(XML_c, XML_ser));
2788 // TODO: idx and order
2789 // idx attribute should start from 1 and not from 0.
2790 pFS->singleElement( FSNS( XML_c, XML_idx ),
2791 XML_val, OString::number(idx+1) );
2792 pFS->singleElement( FSNS( XML_c, XML_order ),
2793 XML_val, OString::number(idx+1) );
2795 // export label
2796 if( xLabelSeq.is() )
2797 exportSeriesText( xLabelSeq );
2799 // TODO:export shape properties
2801 // export categories
2802 if( mxCategoriesValues.is() )
2803 exportSeriesCategory( mxCategoriesValues );
2805 // export values
2806 if( xValueSeq.is() )
2807 exportSeriesValues( xValueSeq );
2809 pFS->endElement( FSNS( XML_c, XML_ser ) );
2817 void ChartExport::exportSeriesText( const Reference< chart2::data::XDataSequence > & xValueSeq )
2819 FSHelperPtr pFS = GetFS();
2820 pFS->startElement(FSNS(XML_c, XML_tx));
2822 OUString aCellRange = xValueSeq->getSourceRangeRepresentation();
2823 aCellRange = parseFormula( aCellRange );
2824 pFS->startElement(FSNS(XML_c, XML_strRef));
2826 pFS->startElement(FSNS(XML_c, XML_f));
2827 pFS->writeEscaped( aCellRange );
2828 pFS->endElement( FSNS( XML_c, XML_f ) );
2830 OUString aLabelString = lcl_flattenStringSequence(lcl_getLabelSequence(xValueSeq));
2831 pFS->startElement(FSNS(XML_c, XML_strCache));
2832 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, "1");
2833 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, "0");
2834 pFS->startElement(FSNS(XML_c, XML_v));
2835 pFS->writeEscaped( aLabelString );
2836 pFS->endElement( FSNS( XML_c, XML_v ) );
2837 pFS->endElement( FSNS( XML_c, XML_pt ) );
2838 pFS->endElement( FSNS( XML_c, XML_strCache ) );
2839 pFS->endElement( FSNS( XML_c, XML_strRef ) );
2840 pFS->endElement( FSNS( XML_c, XML_tx ) );
2843 void ChartExport::exportSeriesCategory( const Reference< chart2::data::XDataSequence > & xValueSeq, sal_Int32 nValueType )
2845 FSHelperPtr pFS = GetFS();
2846 pFS->startElement(FSNS(XML_c, nValueType));
2848 OUString aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString();
2849 const Sequence< Sequence< OUString >> aFinalSplitSource = (nValueType == XML_cat) ? getSplitCategoriesList(aCellRange) : Sequence< Sequence< OUString>>(0);
2850 aCellRange = parseFormula( aCellRange );
2852 if(aFinalSplitSource.getLength() > 1)
2854 // export multi level category axis labels
2855 pFS->startElement(FSNS(XML_c, XML_multiLvlStrRef));
2857 pFS->startElement(FSNS(XML_c, XML_f));
2858 pFS->writeEscaped(aCellRange);
2859 pFS->endElement(FSNS(XML_c, XML_f));
2861 pFS->startElement(FSNS(XML_c, XML_multiLvlStrCache));
2862 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(aFinalSplitSource[0].getLength()));
2863 for(const auto& rSeq : aFinalSplitSource)
2865 pFS->startElement(FSNS(XML_c, XML_lvl));
2866 for(sal_Int32 j = 0; j < rSeq.getLength(); j++)
2868 if(!rSeq[j].isEmpty())
2870 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(j));
2871 pFS->startElement(FSNS(XML_c, XML_v));
2872 pFS->writeEscaped(rSeq[j]);
2873 pFS->endElement(FSNS(XML_c, XML_v));
2874 pFS->endElement(FSNS(XML_c, XML_pt));
2877 pFS->endElement(FSNS(XML_c, XML_lvl));
2880 pFS->endElement(FSNS(XML_c, XML_multiLvlStrCache));
2881 pFS->endElement(FSNS(XML_c, XML_multiLvlStrRef));
2883 else
2885 // export single category axis labels
2886 bool bWriteDateCategories = mbHasDateCategories && (nValueType == XML_cat);
2887 OUString aNumberFormatString;
2888 if (bWriteDateCategories)
2890 Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY );
2891 if( xAxisXSupp.is())
2893 Reference< XPropertySet > xAxisProp = xAxisXSupp->getXAxis();
2894 if (GetProperty(xAxisProp, "NumberFormat"))
2896 sal_Int32 nKey = 0;
2897 mAny >>= nKey;
2898 aNumberFormatString = getNumberFormatCode(nKey);
2901 if (aNumberFormatString.isEmpty())
2902 bWriteDateCategories = false;
2905 pFS->startElement(FSNS(XML_c, bWriteDateCategories ? XML_numRef : XML_strRef));
2907 pFS->startElement(FSNS(XML_c, XML_f));
2908 pFS->writeEscaped(aCellRange);
2909 pFS->endElement(FSNS(XML_c, XML_f));
2911 ::std::vector< OUString > aCategories;
2912 lcl_fillCategoriesIntoStringVector(xValueSeq, aCategories);
2913 sal_Int32 ptCount = aCategories.size();
2914 pFS->startElement(FSNS(XML_c, bWriteDateCategories ? XML_numCache : XML_strCache));
2915 if (bWriteDateCategories)
2917 pFS->startElement(FSNS(XML_c, XML_formatCode));
2918 pFS->writeEscaped(aNumberFormatString);
2919 pFS->endElement(FSNS(XML_c, XML_formatCode));
2922 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount));
2923 for (sal_Int32 i = 0; i < ptCount; i++)
2925 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i));
2926 pFS->startElement(FSNS(XML_c, XML_v));
2927 pFS->writeEscaped(aCategories[i]);
2928 pFS->endElement(FSNS(XML_c, XML_v));
2929 pFS->endElement(FSNS(XML_c, XML_pt));
2932 pFS->endElement(FSNS(XML_c, bWriteDateCategories ? XML_numCache : XML_strCache));
2933 pFS->endElement(FSNS(XML_c, bWriteDateCategories ? XML_numRef : XML_strRef));
2936 pFS->endElement( FSNS( XML_c, nValueType ) );
2939 void ChartExport::exportSeriesValues( const Reference< chart2::data::XDataSequence > & xValueSeq, sal_Int32 nValueType )
2941 FSHelperPtr pFS = GetFS();
2942 pFS->startElement(FSNS(XML_c, nValueType));
2944 OUString aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString();
2945 aCellRange = parseFormula( aCellRange );
2946 // TODO: need to handle XML_multiLvlStrRef according to aCellRange
2947 pFS->startElement(FSNS(XML_c, XML_numRef));
2949 pFS->startElement(FSNS(XML_c, XML_f));
2950 pFS->writeEscaped( aCellRange );
2951 pFS->endElement( FSNS( XML_c, XML_f ) );
2953 ::std::vector< double > aValues = lcl_getAllValuesFromSequence( xValueSeq );
2954 sal_Int32 ptCount = aValues.size();
2955 pFS->startElement(FSNS(XML_c, XML_numCache));
2956 pFS->startElement(FSNS(XML_c, XML_formatCode));
2957 // TODO: what format code?
2958 pFS->writeEscaped( "General" );
2959 pFS->endElement( FSNS( XML_c, XML_formatCode ) );
2960 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount));
2962 for( sal_Int32 i = 0; i < ptCount; i++ )
2964 if (!std::isnan(aValues[i]))
2966 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i));
2967 pFS->startElement(FSNS(XML_c, XML_v));
2968 pFS->write(aValues[i]);
2969 pFS->endElement(FSNS(XML_c, XML_v));
2970 pFS->endElement(FSNS(XML_c, XML_pt));
2974 pFS->endElement( FSNS( XML_c, XML_numCache ) );
2975 pFS->endElement( FSNS( XML_c, XML_numRef ) );
2976 pFS->endElement( FSNS( XML_c, nValueType ) );
2979 void ChartExport::exportShapeProps( const Reference< XPropertySet >& xPropSet )
2981 FSHelperPtr pFS = GetFS();
2982 pFS->startElement(FSNS(XML_c, XML_spPr));
2984 exportFill( xPropSet );
2985 WriteOutline( xPropSet, getModel() );
2987 pFS->endElement( FSNS( XML_c, XML_spPr ) );
2990 void ChartExport::exportTextProps(const Reference<XPropertySet>& xPropSet)
2992 FSHelperPtr pFS = GetFS();
2993 pFS->startElement(FSNS(XML_c, XML_txPr));
2995 sal_Int32 nRotation = 0;
2996 const char* textWordWrap = nullptr;
2998 if (auto xServiceInfo = uno::Reference<lang::XServiceInfo>(xPropSet, uno::UNO_QUERY))
3000 double fMultiplier = 0.0;
3001 // We have at least two possible units of returned value: degrees (e.g., for data labels),
3002 // and 100ths of degree (e.g., for axes labels). The latter is returned as an Any wrapping
3003 // a sal_Int32 value (see WrappedTextRotationProperty::convertInnerToOuterValue), while
3004 // the former is double. So we could test the contained type to decide which multiplier to
3005 // use. But testing the service info should be more robust.
3006 if (xServiceInfo->supportsService("com.sun.star.chart.ChartAxis"))
3007 fMultiplier = -600.0;
3008 else if (xServiceInfo->supportsService("com.sun.star.chart2.DataSeries") || xServiceInfo->supportsService("com.sun.star.chart2.DataPointProperties"))
3010 fMultiplier = -60000.0;
3011 bool bTextWordWrap = false;
3012 if ((xPropSet->getPropertyValue("TextWordWrap") >>= bTextWordWrap) && bTextWordWrap)
3013 textWordWrap = "square";
3014 else
3015 textWordWrap = "none";
3018 if (fMultiplier)
3020 double fTextRotation = 0.0;
3021 uno::Any aAny = xPropSet->getPropertyValue("TextRotation");
3022 if (aAny.hasValue() && (aAny >>= fTextRotation))
3024 fTextRotation *= fMultiplier;
3025 // The MS Office UI allows values only in range of [-90,90].
3026 if (fTextRotation < -5400000.0 && fTextRotation > -16200000.0)
3028 // Reflect the angle if the value is between 90° and 270°
3029 fTextRotation += 10800000.0;
3031 else if (fTextRotation <= -16200000.0)
3033 fTextRotation += 21600000.0;
3035 nRotation = std::round(fTextRotation);
3040 if (nRotation)
3041 pFS->singleElement(FSNS(XML_a, XML_bodyPr), XML_rot, OString::number(nRotation), XML_wrap, textWordWrap);
3042 else
3043 pFS->singleElement(FSNS(XML_a, XML_bodyPr), XML_wrap, textWordWrap);
3045 pFS->singleElement(FSNS(XML_a, XML_lstStyle));
3047 pFS->startElement(FSNS(XML_a, XML_p));
3048 pFS->startElement(FSNS(XML_a, XML_pPr));
3050 WriteRunProperties(xPropSet, false, XML_defRPr, true, o3tl::temporary(false),
3051 o3tl::temporary(sal_Int32()));
3053 pFS->endElement(FSNS(XML_a, XML_pPr));
3054 pFS->endElement(FSNS(XML_a, XML_p));
3055 pFS->endElement(FSNS(XML_c, XML_txPr));
3058 void ChartExport::InitPlotArea( )
3060 Reference< XPropertySet > xDiagramProperties (mxDiagram, uno::UNO_QUERY);
3062 // Check for supported services and then the properties provided by this service.
3063 Reference<lang::XServiceInfo> xServiceInfo (mxDiagram, uno::UNO_QUERY);
3064 if (xServiceInfo.is())
3066 if (xServiceInfo->supportsService("com.sun.star.chart.ChartAxisZSupplier"))
3068 xDiagramProperties->getPropertyValue("HasZAxis") >>= mbHasZAxis;
3072 xDiagramProperties->getPropertyValue("Dim3D") >>= mbIs3DChart;
3074 if( mbHasCategoryLabels && mxNewDiagram.is())
3076 Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( mxNewDiagram, mbHasDateCategories ) );
3077 if( xCategories.is() )
3079 mxCategoriesValues.set( xCategories->getValues() );
3084 void ChartExport::exportAxes( )
3086 sal_Int32 nSize = maAxes.size();
3087 // let's export the axis types in the right order
3088 for ( sal_Int32 nSortIdx = AXIS_PRIMARY_X; nSortIdx <= AXIS_SECONDARY_Y; nSortIdx++ )
3090 for ( sal_Int32 nIdx = 0; nIdx < nSize; nIdx++ )
3092 if (nSortIdx == maAxes[nIdx].nAxisType)
3093 exportAxis( maAxes[nIdx] );
3098 namespace {
3100 sal_Int32 getXAxisTypeByChartType(sal_Int32 eChartType)
3102 if( (eChartType == chart::TYPEID_SCATTER)
3103 || (eChartType == chart::TYPEID_BUBBLE) )
3104 return XML_valAx;
3105 else if( eChartType == chart::TYPEID_STOCK )
3106 return XML_dateAx;
3108 return XML_catAx;
3111 sal_Int32 getRealXAxisType(sal_Int32 nAxisType)
3113 if( nAxisType == chart2::AxisType::CATEGORY )
3114 return XML_catAx;
3115 else if( nAxisType == chart2::AxisType::DATE )
3116 return XML_dateAx;
3117 else if( nAxisType == chart2::AxisType::SERIES )
3118 return XML_serAx;
3120 return XML_valAx;
3125 void ChartExport::exportAxis(const AxisIdPair& rAxisIdPair)
3127 // get some properties from document first
3128 bool bHasXAxisTitle = false,
3129 bHasYAxisTitle = false,
3130 bHasZAxisTitle = false,
3131 bHasSecondaryXAxisTitle = false,
3132 bHasSecondaryYAxisTitle = false;
3133 bool bHasXAxisMajorGrid = false,
3134 bHasXAxisMinorGrid = false,
3135 bHasYAxisMajorGrid = false,
3136 bHasYAxisMinorGrid = false,
3137 bHasZAxisMajorGrid = false,
3138 bHasZAxisMinorGrid = false;
3140 Reference< XPropertySet > xDiagramProperties (mxDiagram, uno::UNO_QUERY);
3142 xDiagramProperties->getPropertyValue("HasXAxisTitle") >>= bHasXAxisTitle;
3143 xDiagramProperties->getPropertyValue("HasYAxisTitle") >>= bHasYAxisTitle;
3144 xDiagramProperties->getPropertyValue("HasZAxisTitle") >>= bHasZAxisTitle;
3145 xDiagramProperties->getPropertyValue("HasSecondaryXAxisTitle") >>= bHasSecondaryXAxisTitle;
3146 xDiagramProperties->getPropertyValue("HasSecondaryYAxisTitle") >>= bHasSecondaryYAxisTitle;
3148 xDiagramProperties->getPropertyValue("HasXAxisGrid") >>= bHasXAxisMajorGrid;
3149 xDiagramProperties->getPropertyValue("HasYAxisGrid") >>= bHasYAxisMajorGrid;
3150 xDiagramProperties->getPropertyValue("HasZAxisGrid") >>= bHasZAxisMajorGrid;
3152 xDiagramProperties->getPropertyValue("HasXAxisHelpGrid") >>= bHasXAxisMinorGrid;
3153 xDiagramProperties->getPropertyValue("HasYAxisHelpGrid") >>= bHasYAxisMinorGrid;
3154 xDiagramProperties->getPropertyValue("HasZAxisHelpGrid") >>= bHasZAxisMinorGrid;
3156 Reference< XPropertySet > xAxisProp;
3157 Reference< drawing::XShape > xAxisTitle;
3158 Reference< beans::XPropertySet > xMajorGrid;
3159 Reference< beans::XPropertySet > xMinorGrid;
3160 sal_Int32 nAxisType = XML_catAx;
3161 const char* sAxPos = nullptr;
3163 switch( rAxisIdPair.nAxisType )
3165 case AXIS_PRIMARY_X:
3167 Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY );
3168 if( xAxisXSupp.is())
3169 xAxisProp = xAxisXSupp->getXAxis();
3170 if( bHasXAxisTitle )
3171 xAxisTitle = xAxisXSupp->getXAxisTitle();
3172 if( bHasXAxisMajorGrid )
3173 xMajorGrid = xAxisXSupp->getXMainGrid();
3174 if( bHasXAxisMinorGrid )
3175 xMinorGrid = xAxisXSupp->getXHelpGrid();
3177 nAxisType = lcl_getCategoryAxisType(mxNewDiagram, 0, 0);
3178 if( nAxisType != -1 )
3179 nAxisType = getRealXAxisType(nAxisType);
3180 else
3181 nAxisType = getXAxisTypeByChartType( getChartType() );
3182 // FIXME: axPos, need to check axis direction
3183 sAxPos = "b";
3184 break;
3186 case AXIS_PRIMARY_Y:
3188 Reference< css::chart::XAxisYSupplier > xAxisYSupp( mxDiagram, uno::UNO_QUERY );
3189 if( xAxisYSupp.is())
3190 xAxisProp = xAxisYSupp->getYAxis();
3191 if( bHasYAxisTitle )
3192 xAxisTitle = xAxisYSupp->getYAxisTitle();
3193 if( bHasYAxisMajorGrid )
3194 xMajorGrid = xAxisYSupp->getYMainGrid();
3195 if( bHasYAxisMinorGrid )
3196 xMinorGrid = xAxisYSupp->getYHelpGrid();
3198 nAxisType = XML_valAx;
3199 // FIXME: axPos, need to check axis direction
3200 sAxPos = "l";
3201 break;
3203 case AXIS_PRIMARY_Z:
3205 Reference< css::chart::XAxisZSupplier > xAxisZSupp( mxDiagram, uno::UNO_QUERY );
3206 if( xAxisZSupp.is())
3207 xAxisProp = xAxisZSupp->getZAxis();
3208 if( bHasZAxisTitle )
3209 xAxisTitle = xAxisZSupp->getZAxisTitle();
3210 if( bHasZAxisMajorGrid )
3211 xMajorGrid = xAxisZSupp->getZMainGrid();
3212 if( bHasZAxisMinorGrid )
3213 xMinorGrid = xAxisZSupp->getZHelpGrid();
3215 sal_Int32 eChartType = getChartType( );
3216 if( (eChartType == chart::TYPEID_SCATTER)
3217 || (eChartType == chart::TYPEID_BUBBLE) )
3218 nAxisType = XML_valAx;
3219 else if( eChartType == chart::TYPEID_STOCK )
3220 nAxisType = XML_dateAx;
3221 else if( eChartType == chart::TYPEID_BAR || eChartType == chart::TYPEID_AREA )
3222 nAxisType = XML_serAx;
3223 // FIXME: axPos, need to check axis direction
3224 sAxPos = "b";
3225 break;
3227 case AXIS_SECONDARY_X:
3229 Reference< css::chart::XTwoAxisXSupplier > xAxisTwoXSupp( mxDiagram, uno::UNO_QUERY );
3230 if( xAxisTwoXSupp.is())
3231 xAxisProp = xAxisTwoXSupp->getSecondaryXAxis();
3232 if( bHasSecondaryXAxisTitle )
3234 Reference< css::chart::XSecondAxisTitleSupplier > xAxisSupp( mxDiagram, uno::UNO_QUERY );
3235 xAxisTitle = xAxisSupp->getSecondXAxisTitle();
3238 nAxisType = lcl_getCategoryAxisType(mxNewDiagram, 0, 1);
3239 if( nAxisType != -1 )
3240 nAxisType = getRealXAxisType(nAxisType);
3241 else
3242 nAxisType = getXAxisTypeByChartType( getChartType() );
3243 // FIXME: axPos, need to check axis direction
3244 sAxPos = "t";
3245 break;
3247 case AXIS_SECONDARY_Y:
3249 Reference< css::chart::XTwoAxisYSupplier > xAxisTwoYSupp( mxDiagram, uno::UNO_QUERY );
3250 if( xAxisTwoYSupp.is())
3251 xAxisProp = xAxisTwoYSupp->getSecondaryYAxis();
3252 if( bHasSecondaryYAxisTitle )
3254 Reference< css::chart::XSecondAxisTitleSupplier > xAxisSupp( mxDiagram, uno::UNO_QUERY );
3255 xAxisTitle = xAxisSupp->getSecondYAxisTitle();
3258 nAxisType = XML_valAx;
3259 // FIXME: axPos, need to check axis direction
3260 sAxPos = "r";
3261 break;
3265 _exportAxis(xAxisProp, xAxisTitle, xMajorGrid, xMinorGrid, nAxisType, sAxPos, rAxisIdPair);
3268 void ChartExport::_exportAxis(
3269 const Reference< XPropertySet >& xAxisProp,
3270 const Reference< drawing::XShape >& xAxisTitle,
3271 const Reference< XPropertySet >& xMajorGrid,
3272 const Reference< XPropertySet >& xMinorGrid,
3273 sal_Int32 nAxisType,
3274 const char* sAxisPos,
3275 const AxisIdPair& rAxisIdPair )
3277 FSHelperPtr pFS = GetFS();
3278 pFS->startElement(FSNS(XML_c, nAxisType));
3279 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(rAxisIdPair.nAxisId));
3281 pFS->startElement(FSNS(XML_c, XML_scaling));
3283 // logBase, min, max
3284 if(GetProperty( xAxisProp, "Logarithmic" ) )
3286 bool bLogarithmic = false;
3287 mAny >>= bLogarithmic;
3288 if( bLogarithmic )
3290 // default value is 10?
3291 pFS->singleElement(FSNS(XML_c, XML_logBase), XML_val, OString::number(10));
3295 // orientation: minMax, maxMin
3296 bool bReverseDirection = false;
3297 if(GetProperty( xAxisProp, "ReverseDirection" ) )
3298 mAny >>= bReverseDirection;
3300 const char* orientation = bReverseDirection ? "maxMin":"minMax";
3301 pFS->singleElement(FSNS(XML_c, XML_orientation), XML_val, orientation);
3303 bool bAutoMax = false;
3304 if(GetProperty( xAxisProp, "AutoMax" ) )
3305 mAny >>= bAutoMax;
3307 if( !bAutoMax && (GetProperty( xAxisProp, "Max" ) ) )
3309 double dMax = 0;
3310 mAny >>= dMax;
3311 pFS->singleElement(FSNS(XML_c, XML_max), XML_val, OString::number(dMax));
3314 bool bAutoMin = false;
3315 if(GetProperty( xAxisProp, "AutoMin" ) )
3316 mAny >>= bAutoMin;
3318 if( !bAutoMin && (GetProperty( xAxisProp, "Min" ) ) )
3320 double dMin = 0;
3321 mAny >>= dMin;
3322 pFS->singleElement(FSNS(XML_c, XML_min), XML_val, OString::number(dMin));
3325 pFS->endElement( FSNS( XML_c, XML_scaling ) );
3327 bool bVisible = true;
3328 if( xAxisProp.is() )
3330 xAxisProp->getPropertyValue("Visible") >>= bVisible;
3333 // only export each axis only once non-deleted
3334 auto aItInsertedPair = maExportedAxis.insert(rAxisIdPair.nAxisType);
3335 bool bDeleted = !aItInsertedPair.second;
3337 pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, !bDeleted && bVisible ? "0" : "1");
3339 // FIXME: axPos, need to check the property "ReverseDirection"
3340 pFS->singleElement(FSNS(XML_c, XML_axPos), XML_val, sAxisPos);
3341 // major grid line
3342 if( xMajorGrid.is())
3344 pFS->startElement(FSNS(XML_c, XML_majorGridlines));
3345 exportShapeProps( xMajorGrid );
3346 pFS->endElement( FSNS( XML_c, XML_majorGridlines ) );
3349 // minor grid line
3350 if( xMinorGrid.is())
3352 pFS->startElement(FSNS(XML_c, XML_minorGridlines));
3353 exportShapeProps( xMinorGrid );
3354 pFS->endElement( FSNS( XML_c, XML_minorGridlines ) );
3357 // title
3358 if( xAxisTitle.is() )
3359 exportTitle( xAxisTitle );
3361 bool bLinkedNumFmt = true;
3362 if (GetProperty(xAxisProp, "LinkNumberFormatToSource"))
3363 mAny >>= bLinkedNumFmt;
3365 OUString aNumberFormatString("General");
3366 if (GetProperty(xAxisProp, "NumberFormat"))
3368 sal_Int32 nKey = 0;
3369 mAny >>= nKey;
3370 aNumberFormatString = getNumberFormatCode(nKey);
3373 pFS->singleElement(FSNS(XML_c, XML_numFmt),
3374 XML_formatCode, aNumberFormatString,
3375 XML_sourceLinked, bLinkedNumFmt ? "1" : "0");
3377 // majorTickMark
3378 sal_Int32 nValue = 0;
3379 if(GetProperty( xAxisProp, "Marks" ) )
3381 mAny >>= nValue;
3382 bool bInner = nValue & css::chart::ChartAxisMarks::INNER;
3383 bool bOuter = nValue & css::chart::ChartAxisMarks::OUTER;
3384 const char* majorTickMark = nullptr;
3385 if( bInner && bOuter )
3386 majorTickMark = "cross";
3387 else if( bInner )
3388 majorTickMark = "in";
3389 else if( bOuter )
3390 majorTickMark = "out";
3391 else
3392 majorTickMark = "none";
3393 pFS->singleElement(FSNS(XML_c, XML_majorTickMark), XML_val, majorTickMark);
3395 // minorTickMark
3396 if(GetProperty( xAxisProp, "HelpMarks" ) )
3398 mAny >>= nValue;
3399 bool bInner = nValue & css::chart::ChartAxisMarks::INNER;
3400 bool bOuter = nValue & css::chart::ChartAxisMarks::OUTER;
3401 const char* minorTickMark = nullptr;
3402 if( bInner && bOuter )
3403 minorTickMark = "cross";
3404 else if( bInner )
3405 minorTickMark = "in";
3406 else if( bOuter )
3407 minorTickMark = "out";
3408 else
3409 minorTickMark = "none";
3410 pFS->singleElement(FSNS(XML_c, XML_minorTickMark), XML_val, minorTickMark);
3412 // tickLblPos
3413 const char* sTickLblPos = nullptr;
3414 bool bDisplayLabel = true;
3415 if(GetProperty( xAxisProp, "DisplayLabels" ) )
3416 mAny >>= bDisplayLabel;
3417 if( bDisplayLabel && (GetProperty( xAxisProp, "LabelPosition" ) ) )
3419 css::chart::ChartAxisLabelPosition eLabelPosition = css::chart::ChartAxisLabelPosition_NEAR_AXIS;
3420 mAny >>= eLabelPosition;
3421 switch( eLabelPosition )
3423 case css::chart::ChartAxisLabelPosition_NEAR_AXIS:
3424 case css::chart::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE:
3425 sTickLblPos = "nextTo";
3426 break;
3427 case css::chart::ChartAxisLabelPosition_OUTSIDE_START:
3428 sTickLblPos = "low";
3429 break;
3430 case css::chart::ChartAxisLabelPosition_OUTSIDE_END:
3431 sTickLblPos = "high";
3432 break;
3433 default:
3434 sTickLblPos = "nextTo";
3435 break;
3438 else
3440 sTickLblPos = "none";
3442 pFS->singleElement(FSNS(XML_c, XML_tickLblPos), XML_val, sTickLblPos);
3444 // shape properties
3445 exportShapeProps( xAxisProp );
3447 exportTextProps(xAxisProp);
3449 pFS->singleElement(FSNS(XML_c, XML_crossAx), XML_val, OString::number(rAxisIdPair.nCrossAx));
3451 // crosses & crossesAt
3452 bool bCrossesValue = false;
3453 const char* sCrosses = nullptr;
3454 // do not export the CrossoverPosition/CrossoverValue, if the axis is deleted and not visible
3455 if( GetProperty( xAxisProp, "CrossoverPosition" ) && !bDeleted && bVisible )
3457 css::chart::ChartAxisPosition ePosition( css::chart::ChartAxisPosition_ZERO );
3458 mAny >>= ePosition;
3459 switch( ePosition )
3461 case css::chart::ChartAxisPosition_START:
3462 sCrosses = "min";
3463 break;
3464 case css::chart::ChartAxisPosition_END:
3465 sCrosses = "max";
3466 break;
3467 case css::chart::ChartAxisPosition_ZERO:
3468 sCrosses = "autoZero";
3469 break;
3470 default:
3471 bCrossesValue = true;
3472 break;
3476 if( bCrossesValue && GetProperty( xAxisProp, "CrossoverValue" ) )
3478 double dValue = 0;
3479 mAny >>= dValue;
3480 pFS->singleElement(FSNS(XML_c, XML_crossesAt), XML_val, OString::number(dValue));
3482 else
3484 if(sCrosses)
3486 pFS->singleElement(FSNS(XML_c, XML_crosses), XML_val, sCrosses);
3490 if( ( nAxisType == XML_catAx )
3491 || ( nAxisType == XML_dateAx ) )
3493 // FIXME: seems not support? use default value,
3494 const char* const isAuto = "1";
3495 pFS->singleElement(FSNS(XML_c, XML_auto), XML_val, isAuto);
3497 if( nAxisType == XML_catAx )
3499 // FIXME: seems not support? lblAlgn
3500 const char* const sLblAlgn = "ctr";
3501 pFS->singleElement(FSNS(XML_c, XML_lblAlgn), XML_val, sLblAlgn);
3504 // FIXME: seems not support? lblOffset
3505 pFS->singleElement(FSNS(XML_c, XML_lblOffset), XML_val, OString::number(100));
3507 // export baseTimeUnit, majorTimeUnit, minorTimeUnit of Date axis
3508 if( nAxisType == XML_dateAx )
3510 sal_Int32 nAxisIndex = -1;
3511 if( rAxisIdPair.nAxisType == AXIS_PRIMARY_X )
3512 nAxisIndex = 0;
3513 else if( rAxisIdPair.nAxisType == AXIS_SECONDARY_X )
3514 nAxisIndex = 1;
3516 cssc::TimeIncrement aTimeIncrement = lcl_getDateTimeIncrement( mxNewDiagram, nAxisIndex );
3517 sal_Int32 nTimeResolution = css::chart::TimeUnit::DAY;
3518 if( aTimeIncrement.TimeResolution >>= nTimeResolution )
3519 pFS->singleElement(FSNS(XML_c, XML_baseTimeUnit), XML_val, lclGetTimeUnitToken(nTimeResolution));
3521 cssc::TimeInterval aInterval;
3522 if( aTimeIncrement.MajorTimeInterval >>= aInterval )
3524 pFS->singleElement(FSNS(XML_c, XML_majorUnit), XML_val, OString::number(aInterval.Number));
3525 pFS->singleElement(FSNS(XML_c, XML_majorTimeUnit), XML_val, lclGetTimeUnitToken(aInterval.TimeUnit));
3527 if( aTimeIncrement.MinorTimeInterval >>= aInterval )
3529 pFS->singleElement(FSNS(XML_c, XML_minorUnit), XML_val, OString::number(aInterval.Number));
3530 pFS->singleElement(FSNS(XML_c, XML_minorTimeUnit), XML_val, lclGetTimeUnitToken(aInterval.TimeUnit));
3534 // FIXME: seems not support? noMultiLvlLbl
3535 pFS->singleElement(FSNS(XML_c, XML_noMultiLvlLbl), XML_val, OString::number(0));
3538 // crossBetween
3539 if( nAxisType == XML_valAx )
3541 if( lcl_isCategoryAxisShifted( mxNewDiagram ))
3542 pFS->singleElement(FSNS(XML_c, XML_crossBetween), XML_val, "between");
3543 else
3544 pFS->singleElement(FSNS(XML_c, XML_crossBetween), XML_val, "midCat");
3547 // majorUnit
3548 bool bAutoStepMain = false;
3549 if(GetProperty( xAxisProp, "AutoStepMain" ) )
3550 mAny >>= bAutoStepMain;
3552 if( !bAutoStepMain && (GetProperty( xAxisProp, "StepMain" ) ) )
3554 double dMajorUnit = 0;
3555 mAny >>= dMajorUnit;
3556 pFS->singleElement(FSNS(XML_c, XML_majorUnit), XML_val, OString::number(dMajorUnit));
3558 // minorUnit
3559 bool bAutoStepHelp = false;
3560 if(GetProperty( xAxisProp, "AutoStepHelp" ) )
3561 mAny >>= bAutoStepHelp;
3563 if( !bAutoStepHelp && (GetProperty( xAxisProp, "StepHelp" ) ) )
3565 double dMinorUnit = 0;
3566 mAny >>= dMinorUnit;
3567 if( GetProperty( xAxisProp, "StepHelpCount" ) )
3569 sal_Int32 dMinorUnitCount = 0;
3570 mAny >>= dMinorUnitCount;
3571 // tdf#114168 Don't save minor unit if number of step help count is 5 (which is default for MS Excel),
3572 // to allow proper .xlsx import. If minorUnit is set and majorUnit not, then it is impossible
3573 // to calculate StepHelpCount.
3574 if( dMinorUnitCount != 5 )
3576 pFS->singleElement( FSNS( XML_c, XML_minorUnit ),
3577 XML_val, OString::number( dMinorUnit ) );
3582 if( nAxisType == XML_valAx && GetProperty( xAxisProp, "DisplayUnits" ) )
3584 bool bDisplayUnits = false;
3585 mAny >>= bDisplayUnits;
3586 if(bDisplayUnits)
3588 if(GetProperty( xAxisProp, "BuiltInUnit" ))
3590 OUString aVal;
3591 mAny >>= aVal;
3592 if(!aVal.isEmpty())
3594 pFS->startElement(FSNS(XML_c, XML_dispUnits));
3596 pFS->singleElement(FSNS(XML_c, XML_builtInUnit), XML_val, aVal);
3598 pFS->singleElement(FSNS( XML_c, XML_dispUnitsLbl ));
3599 pFS->endElement( FSNS( XML_c, XML_dispUnits ) );
3605 pFS->endElement( FSNS( XML_c, nAxisType ) );
3608 namespace {
3610 struct LabelPlacementParam
3612 bool mbExport;
3613 sal_Int32 meDefault;
3615 std::unordered_set<sal_Int32> maAllowedValues;
3617 LabelPlacementParam(bool bExport, sal_Int32 nDefault) :
3618 mbExport(bExport),
3619 meDefault(nDefault),
3620 maAllowedValues(
3622 css::chart::DataLabelPlacement::OUTSIDE,
3623 css::chart::DataLabelPlacement::INSIDE,
3624 css::chart::DataLabelPlacement::CENTER,
3625 css::chart::DataLabelPlacement::NEAR_ORIGIN,
3626 css::chart::DataLabelPlacement::TOP,
3627 css::chart::DataLabelPlacement::BOTTOM,
3628 css::chart::DataLabelPlacement::LEFT,
3629 css::chart::DataLabelPlacement::RIGHT,
3630 css::chart::DataLabelPlacement::AVOID_OVERLAP
3636 const char* toOOXMLPlacement( sal_Int32 nPlacement )
3638 switch (nPlacement)
3640 case css::chart::DataLabelPlacement::OUTSIDE: return "outEnd";
3641 case css::chart::DataLabelPlacement::INSIDE: return "inEnd";
3642 case css::chart::DataLabelPlacement::CENTER: return "ctr";
3643 case css::chart::DataLabelPlacement::NEAR_ORIGIN: return "inBase";
3644 case css::chart::DataLabelPlacement::TOP: return "t";
3645 case css::chart::DataLabelPlacement::BOTTOM: return "b";
3646 case css::chart::DataLabelPlacement::LEFT: return "l";
3647 case css::chart::DataLabelPlacement::RIGHT: return "r";
3648 case css::chart::DataLabelPlacement::CUSTOM:
3649 case css::chart::DataLabelPlacement::AVOID_OVERLAP: return "bestFit";
3650 default:
3654 return "outEnd";
3657 OUString getFieldTypeString( const chart2::DataPointCustomLabelFieldType aType )
3659 switch (aType)
3661 case chart2::DataPointCustomLabelFieldType_CATEGORYNAME:
3662 return "CATEGORYNAME";
3664 case chart2::DataPointCustomLabelFieldType_SERIESNAME:
3665 return "SERIESNAME";
3667 case chart2::DataPointCustomLabelFieldType_VALUE:
3668 return "VALUE";
3670 case chart2::DataPointCustomLabelFieldType_CELLREF:
3671 return "CELLREF";
3673 case chart2::DataPointCustomLabelFieldType_CELLRANGE:
3674 return "CELLRANGE";
3676 default:
3677 break;
3679 return OUString();
3682 void writeRunProperties( ChartExport* pChartExport, Reference<XPropertySet> const & xPropertySet )
3684 bool bDummy = false;
3685 sal_Int32 nDummy;
3686 pChartExport->WriteRunProperties(xPropertySet, false, XML_rPr, true, bDummy, nDummy);
3689 void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport,
3690 const Sequence<Reference<chart2::XDataPointCustomLabelField>>& rCustomLabelFields,
3691 sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange )
3693 pFS->startElement(FSNS(XML_c, XML_tx));
3694 pFS->startElement(FSNS(XML_c, XML_rich));
3696 // TODO: body properties?
3697 pFS->singleElement(FSNS(XML_a, XML_bodyPr));
3699 OUString sFieldType;
3700 OUString sContent;
3701 pFS->startElement(FSNS(XML_a, XML_p));
3703 for (auto& rField : rCustomLabelFields)
3705 Reference<XPropertySet> xPropertySet(rField, UNO_QUERY);
3706 chart2::DataPointCustomLabelFieldType aType = rField->getFieldType();
3707 sFieldType.clear();
3708 sContent.clear();
3709 bool bNewParagraph = false;
3711 if (aType == chart2::DataPointCustomLabelFieldType_CELLRANGE &&
3712 rField->getDataLabelsRange())
3714 if (rDLblsRange.getRange().isEmpty())
3715 rDLblsRange.setRange(rField->getCellRange());
3717 if (!rDLblsRange.hasLabel(nLabelIndex))
3718 rDLblsRange.setLabel(nLabelIndex, rField->getString());
3720 sContent = "[CELLRANGE]";
3722 else
3724 sContent = rField->getString();
3727 if (aType == chart2::DataPointCustomLabelFieldType_NEWLINE)
3728 bNewParagraph = true;
3729 else if (aType != chart2::DataPointCustomLabelFieldType_TEXT)
3730 sFieldType = getFieldTypeString(aType);
3732 if (bNewParagraph)
3734 pFS->endElement(FSNS(XML_a, XML_p));
3735 pFS->startElement(FSNS(XML_a, XML_p));
3736 continue;
3739 if (sFieldType.isEmpty())
3741 // Normal text run
3742 pFS->startElement(FSNS(XML_a, XML_r));
3743 writeRunProperties(pChartExport, xPropertySet);
3745 pFS->startElement(FSNS(XML_a, XML_t));
3746 pFS->writeEscaped(sContent);
3747 pFS->endElement(FSNS(XML_a, XML_t));
3749 pFS->endElement(FSNS(XML_a, XML_r));
3751 else
3753 // Field
3754 pFS->startElement(FSNS(XML_a, XML_fld), XML_id, rField->getGuid(), XML_type,
3755 sFieldType);
3756 writeRunProperties(pChartExport, xPropertySet);
3758 pFS->startElement(FSNS(XML_a, XML_t));
3759 pFS->writeEscaped(sContent);
3760 pFS->endElement(FSNS(XML_a, XML_t));
3762 pFS->endElement(FSNS(XML_a, XML_fld));
3766 pFS->endElement(FSNS(XML_a, XML_p));
3767 pFS->endElement(FSNS(XML_c, XML_rich));
3768 pFS->endElement(FSNS(XML_c, XML_tx));
3771 void writeLabelProperties( const FSHelperPtr& pFS, ChartExport* pChartExport,
3772 const uno::Reference<beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam,
3773 sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange )
3775 if (!xPropSet.is())
3776 return;
3778 chart2::DataPointLabel aLabel;
3779 Sequence<Reference<chart2::XDataPointCustomLabelField>> aCustomLabelFields;
3780 sal_Int32 nLabelBorderWidth = 0;
3781 sal_Int32 nLabelBorderColor = 0x00FFFFFF;
3782 sal_Int32 nLabelFillColor = -1;
3784 xPropSet->getPropertyValue("Label") >>= aLabel;
3785 xPropSet->getPropertyValue("CustomLabelFields") >>= aCustomLabelFields;
3786 xPropSet->getPropertyValue("LabelBorderWidth") >>= nLabelBorderWidth;
3787 xPropSet->getPropertyValue("LabelBorderColor") >>= nLabelBorderColor;
3788 xPropSet->getPropertyValue("LabelFillColor") >>= nLabelFillColor;
3790 if (nLabelBorderWidth > 0 || nLabelFillColor != -1)
3792 pFS->startElement(FSNS(XML_c, XML_spPr));
3794 if (nLabelFillColor != -1)
3796 pFS->startElement(FSNS(XML_a, XML_solidFill));
3798 OString aStr = OString::number(nLabelFillColor, 16).toAsciiUpperCase();
3799 pFS->singleElement(FSNS(XML_a, XML_srgbClr), XML_val, aStr);
3801 pFS->endElement(FSNS(XML_a, XML_solidFill));
3804 if (nLabelBorderWidth > 0)
3806 pFS->startElement(FSNS(XML_a, XML_ln), XML_w,
3807 OString::number(convertHmmToEmu(nLabelBorderWidth)));
3809 if (nLabelBorderColor != -1)
3811 pFS->startElement(FSNS(XML_a, XML_solidFill));
3813 OString aStr = OString::number(nLabelBorderColor, 16).toAsciiUpperCase();
3814 pFS->singleElement(FSNS(XML_a, XML_srgbClr), XML_val, aStr);
3816 pFS->endElement(FSNS(XML_a, XML_solidFill));
3819 pFS->endElement(FSNS(XML_a, XML_ln));
3822 pFS->endElement(FSNS(XML_c, XML_spPr));
3825 pChartExport->exportTextProps(xPropSet);
3827 if (aCustomLabelFields.hasElements())
3828 writeCustomLabel(pFS, pChartExport, aCustomLabelFields, nLabelIndex, rDLblsRange);
3830 if (rLabelParam.mbExport)
3832 sal_Int32 nLabelPlacement = rLabelParam.meDefault;
3833 if (xPropSet->getPropertyValue("LabelPlacement") >>= nLabelPlacement)
3835 if (!rLabelParam.maAllowedValues.count(nLabelPlacement))
3836 nLabelPlacement = rLabelParam.meDefault;
3837 pFS->singleElement(FSNS(XML_c, XML_dLblPos), XML_val, toOOXMLPlacement(nLabelPlacement));
3841 pFS->singleElement(FSNS(XML_c, XML_showLegendKey), XML_val, ToPsz10(aLabel.ShowLegendSymbol));
3842 pFS->singleElement(FSNS(XML_c, XML_showVal), XML_val, ToPsz10(aLabel.ShowNumber));
3843 pFS->singleElement(FSNS(XML_c, XML_showCatName), XML_val, ToPsz10(aLabel.ShowCategoryName));
3844 pFS->singleElement(FSNS(XML_c, XML_showSerName), XML_val, ToPsz10(aLabel.ShowSeriesName));
3845 pFS->singleElement(FSNS(XML_c, XML_showPercent), XML_val, ToPsz10(aLabel.ShowNumberInPercent));
3847 // Export the text "separator" if exists
3848 uno::Any aAny = xPropSet->getPropertyValue("LabelSeparator");
3849 if( aAny.hasValue() )
3851 OUString nLabelSeparator;
3852 aAny >>= nLabelSeparator;
3853 pFS->startElement(FSNS(XML_c, XML_separator));
3854 pFS->writeEscaped( nLabelSeparator );
3855 pFS->endElement( FSNS( XML_c, XML_separator ) );
3858 if (rDLblsRange.hasLabel(nLabelIndex))
3860 pFS->startElement(FSNS(XML_c, XML_extLst));
3861 pFS->startElement(FSNS(XML_c, XML_ext), XML_uri,
3862 "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}", FSNS(XML_xmlns, XML_c15),
3863 pChartExport->GetFB()->getNamespaceURL(OOX_NS(c15)));
3865 pFS->singleElement(FSNS(XML_c15, XML_showDataLabelsRange), XML_val, "1");
3867 pFS->endElement(FSNS(XML_c, XML_ext));
3868 pFS->endElement(FSNS(XML_c, XML_extLst));
3874 void ChartExport::exportDataLabels(
3875 const uno::Reference<chart2::XDataSeries> & xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType,
3876 DataLabelsRange& rDLblsRange)
3878 if (!xSeries.is() || nSeriesLength <= 0)
3879 return;
3881 uno::Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY);
3882 if (!xPropSet.is())
3883 return;
3885 FSHelperPtr pFS = GetFS();
3886 pFS->startElement(FSNS(XML_c, XML_dLbls));
3888 bool bLinkedNumFmt = true;
3889 if (GetProperty(xPropSet, "LinkNumberFormatToSource"))
3890 mAny >>= bLinkedNumFmt;
3892 chart2::DataPointLabel aLabel;
3893 bool bLabelIsNumberFormat = true;
3894 if( xPropSet->getPropertyValue("Label") >>= aLabel )
3895 bLabelIsNumberFormat = aLabel.ShowNumber;
3897 if (GetProperty(xPropSet, bLabelIsNumberFormat ? OUString("NumberFormat") : OUString("PercentageNumberFormat")))
3899 sal_Int32 nKey = 0;
3900 mAny >>= nKey;
3902 OUString aNumberFormatString = getNumberFormatCode(nKey);
3904 pFS->singleElement(FSNS(XML_c, XML_numFmt),
3905 XML_formatCode, aNumberFormatString,
3906 XML_sourceLinked, ToPsz10(bLinkedNumFmt));
3909 uno::Sequence<sal_Int32> aAttrLabelIndices;
3910 xPropSet->getPropertyValue("AttributedDataPoints") >>= aAttrLabelIndices;
3912 // We must not export label placement property when the chart type doesn't
3913 // support this option in MS Office, else MS Office would think the file
3914 // is corrupt & refuse to open it.
3916 const chart::TypeGroupInfo& rInfo = chart::GetTypeGroupInfo(static_cast<chart::TypeId>(eChartType));
3917 LabelPlacementParam aParam(!mbIs3DChart, rInfo.mnDefLabelPos);
3918 switch (eChartType) // diagram chart type
3920 case chart::TYPEID_PIE:
3921 if(getChartType() == chart::TYPEID_DOUGHNUT)
3922 aParam.mbExport = false;
3923 else
3924 // All pie charts support label placement.
3925 aParam.mbExport = true;
3926 break;
3927 case chart::TYPEID_AREA:
3928 case chart::TYPEID_RADARLINE:
3929 case chart::TYPEID_RADARAREA:
3930 // These chart types don't support label placement.
3931 aParam.mbExport = false;
3932 break;
3933 case chart::TYPEID_BAR:
3934 if (mbStacked || mbPercent)
3936 aParam.maAllowedValues.clear();
3937 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER);
3938 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE);
3939 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN);
3940 aParam.meDefault = css::chart::DataLabelPlacement::CENTER;
3942 else // Clustered bar chart
3944 aParam.maAllowedValues.clear();
3945 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER);
3946 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE);
3947 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::OUTSIDE);
3948 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN);
3949 aParam.meDefault = css::chart::DataLabelPlacement::OUTSIDE;
3951 break;
3952 default:
3956 for (const sal_Int32 nIdx : std::as_const(aAttrLabelIndices))
3958 uno::Reference<beans::XPropertySet> xLabelPropSet = xSeries->getDataPointByIndex(nIdx);
3960 if (!xLabelPropSet.is())
3961 continue;
3963 pFS->startElement(FSNS(XML_c, XML_dLbl));
3964 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nIdx));
3966 // export custom position of data label
3967 if( eChartType != chart::TYPEID_PIE )
3969 chart2::RelativePosition aCustomLabelPosition;
3970 if( xLabelPropSet->getPropertyValue("CustomLabelPosition") >>= aCustomLabelPosition )
3972 pFS->startElement(FSNS(XML_c, XML_layout));
3973 pFS->startElement(FSNS(XML_c, XML_manualLayout));
3975 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(aCustomLabelPosition.Primary));
3976 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(aCustomLabelPosition.Secondary));
3978 SAL_WARN_IF(aCustomLabelPosition.Anchor != css::drawing::Alignment_TOP_LEFT, "oox", "unsupported anchor position");
3980 pFS->endElement(FSNS(XML_c, XML_manualLayout));
3981 pFS->endElement(FSNS(XML_c, XML_layout));
3985 if( GetProperty(xLabelPropSet, "LinkNumberFormatToSource") )
3986 mAny >>= bLinkedNumFmt;
3988 if( xLabelPropSet->getPropertyValue("Label") >>= aLabel )
3989 bLabelIsNumberFormat = aLabel.ShowNumber;
3990 else
3991 bLabelIsNumberFormat = true;
3993 if (GetProperty(xLabelPropSet, bLabelIsNumberFormat ? OUString("NumberFormat") : OUString("PercentageNumberFormat")))
3995 sal_Int32 nKey = 0;
3996 mAny >>= nKey;
3998 OUString aNumberFormatString = getNumberFormatCode(nKey);
4000 pFS->singleElement(FSNS(XML_c, XML_numFmt), XML_formatCode, aNumberFormatString,
4001 XML_sourceLinked, ToPsz10(bLinkedNumFmt));
4004 // Individual label property that overwrites the baseline.
4005 writeLabelProperties(pFS, this, xLabelPropSet, aParam, nIdx, rDLblsRange);
4006 pFS->endElement(FSNS(XML_c, XML_dLbl));
4009 // Baseline label properties for all labels.
4010 writeLabelProperties(pFS, this, xPropSet, aParam, -1, rDLblsRange);
4012 bool bShowLeaderLines = false;
4013 xPropSet->getPropertyValue("ShowCustomLeaderLines") >>= bShowLeaderLines;
4014 pFS->singleElement(FSNS(XML_c, XML_showLeaderLines), XML_val, ToPsz10(bShowLeaderLines));
4016 // Export leader line
4017 if( eChartType != chart::TYPEID_PIE )
4019 pFS->startElement(FSNS(XML_c, XML_extLst));
4020 pFS->startElement(FSNS(XML_c, XML_ext), XML_uri, "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}", FSNS(XML_xmlns, XML_c15), GetFB()->getNamespaceURL(OOX_NS(c15)));
4021 pFS->singleElement(FSNS(XML_c15, XML_showLeaderLines), XML_val, ToPsz10(bShowLeaderLines));
4022 pFS->endElement(FSNS(XML_c, XML_ext));
4023 pFS->endElement(FSNS(XML_c, XML_extLst));
4025 pFS->endElement(FSNS(XML_c, XML_dLbls));
4028 void ChartExport::exportDataPoints(
4029 const uno::Reference< beans::XPropertySet > & xSeriesProperties,
4030 sal_Int32 nSeriesLength, sal_Int32 eChartType )
4032 uno::Reference< chart2::XDataSeries > xSeries( xSeriesProperties, uno::UNO_QUERY );
4033 bool bVaryColorsByPoint = false;
4034 Sequence< sal_Int32 > aDataPointSeq;
4035 if( xSeriesProperties.is())
4037 Any aAny = xSeriesProperties->getPropertyValue( "AttributedDataPoints" );
4038 aAny >>= aDataPointSeq;
4039 xSeriesProperties->getPropertyValue( "VaryColorsByPoint" ) >>= bVaryColorsByPoint;
4042 const sal_Int32 * pPoints = aDataPointSeq.getConstArray();
4043 sal_Int32 nElement;
4044 Reference< chart2::XColorScheme > xColorScheme;
4045 if( mxNewDiagram.is())
4046 xColorScheme.set( mxNewDiagram->getDefaultColorScheme());
4048 if( bVaryColorsByPoint && xColorScheme.is() )
4050 o3tl::sorted_vector< sal_Int32 > aAttrPointSet;
4051 aAttrPointSet.reserve(aDataPointSeq.getLength());
4052 for (auto p = pPoints; p < pPoints + aDataPointSeq.getLength(); ++p)
4053 aAttrPointSet.insert(*p);
4054 const auto aEndIt = aAttrPointSet.end();
4055 for( nElement = 0; nElement < nSeriesLength; ++nElement )
4057 uno::Reference< beans::XPropertySet > xPropSet;
4058 if( aAttrPointSet.find( nElement ) != aEndIt )
4062 xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
4063 xSeries, nElement, getModel() );
4065 catch( const uno::Exception & )
4067 DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
4070 else
4072 // property set only containing the color
4073 xPropSet.set( new ColorPropertySet( ColorTransparency, xColorScheme->getColorByIndex( nElement )));
4076 if( xPropSet.is() )
4078 FSHelperPtr pFS = GetFS();
4079 pFS->startElement(FSNS(XML_c, XML_dPt));
4080 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nElement));
4082 switch (eChartType)
4084 case chart::TYPEID_PIE:
4085 case chart::TYPEID_DOUGHNUT:
4087 if( xPropSet.is() && GetProperty( xPropSet, "SegmentOffset") )
4089 sal_Int32 nOffset = 0;
4090 mAny >>= nOffset;
4091 if (nOffset)
4092 pFS->singleElement( FSNS( XML_c, XML_explosion ),
4093 XML_val, OString::number( nOffset ) );
4095 break;
4097 default:
4098 break;
4100 exportShapeProps( xPropSet );
4102 pFS->endElement( FSNS( XML_c, XML_dPt ) );
4107 // Export Data Point Property in Charts even if the VaryColors is false
4108 if( bVaryColorsByPoint )
4109 return;
4111 o3tl::sorted_vector< sal_Int32 > aAttrPointSet;
4112 aAttrPointSet.reserve(aDataPointSeq.getLength());
4113 for (auto p = pPoints; p < pPoints + aDataPointSeq.getLength(); ++p)
4114 aAttrPointSet.insert(*p);
4115 const auto aEndIt = aAttrPointSet.end();
4116 for( nElement = 0; nElement < nSeriesLength; ++nElement )
4118 uno::Reference< beans::XPropertySet > xPropSet;
4119 if( aAttrPointSet.find( nElement ) != aEndIt )
4123 xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
4124 xSeries, nElement, getModel() );
4126 catch( const uno::Exception & )
4128 DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
4132 if( xPropSet.is() )
4134 FSHelperPtr pFS = GetFS();
4135 pFS->startElement(FSNS(XML_c, XML_dPt));
4136 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nElement));
4138 switch( eChartType )
4140 case chart::TYPEID_BUBBLE:
4141 case chart::TYPEID_HORBAR:
4142 case chart::TYPEID_BAR:
4143 pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0");
4144 exportShapeProps(xPropSet);
4145 break;
4147 case chart::TYPEID_LINE:
4148 case chart::TYPEID_SCATTER:
4149 case chart::TYPEID_RADARLINE:
4150 exportMarker(xPropSet);
4151 break;
4153 default:
4154 exportShapeProps(xPropSet);
4155 break;
4158 pFS->endElement( FSNS( XML_c, XML_dPt ) );
4163 void ChartExport::exportAxesId(bool bPrimaryAxes, bool bCheckCombinedAxes)
4165 sal_Int32 nAxisIdx, nAxisIdy;
4166 bool bPrimaryAxisExists = false;
4167 bool bSecondaryAxisExists = false;
4168 // let's check which axis already exists and which axis is attached to the actual dataseries
4169 if (maAxes.size() >= 2)
4171 bPrimaryAxisExists = bPrimaryAxes && maAxes[1].nAxisType == AXIS_PRIMARY_Y;
4172 bSecondaryAxisExists = !bPrimaryAxes && maAxes[1].nAxisType == AXIS_SECONDARY_Y;
4174 // tdf#114181 keep axes of combined charts
4175 if ( bCheckCombinedAxes && ( bPrimaryAxisExists || bSecondaryAxisExists ) )
4177 nAxisIdx = maAxes[0].nAxisId;
4178 nAxisIdy = maAxes[1].nAxisId;
4180 else
4182 nAxisIdx = lcl_generateRandomValue();
4183 nAxisIdy = lcl_generateRandomValue();
4184 AxesType eXAxis = bPrimaryAxes ? AXIS_PRIMARY_X : AXIS_SECONDARY_X;
4185 AxesType eYAxis = bPrimaryAxes ? AXIS_PRIMARY_Y : AXIS_SECONDARY_Y;
4186 maAxes.emplace_back( eXAxis, nAxisIdx, nAxisIdy );
4187 maAxes.emplace_back( eYAxis, nAxisIdy, nAxisIdx );
4189 FSHelperPtr pFS = GetFS();
4190 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdx));
4191 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdy));
4192 if (mbHasZAxis)
4194 sal_Int32 nAxisIdz = 0;
4195 if( isDeep3dChart() )
4197 nAxisIdz = lcl_generateRandomValue();
4198 maAxes.emplace_back( AXIS_PRIMARY_Z, nAxisIdz, nAxisIdy );
4200 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdz));
4204 void ChartExport::exportGrouping( bool isBar )
4206 FSHelperPtr pFS = GetFS();
4207 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
4208 // grouping
4209 if( GetProperty( xPropSet, "Stacked" ) )
4210 mAny >>= mbStacked;
4211 if( GetProperty( xPropSet, "Percent" ) )
4212 mAny >>= mbPercent;
4214 const char* grouping = nullptr;
4215 if (mbStacked)
4216 grouping = "stacked";
4217 else if (mbPercent)
4218 grouping = "percentStacked";
4219 else
4221 if( isBar && !isDeep3dChart() )
4223 grouping = "clustered";
4225 else
4226 grouping = "standard";
4228 pFS->singleElement(FSNS(XML_c, XML_grouping), XML_val, grouping);
4231 void ChartExport::exportTrendlines( const Reference< chart2::XDataSeries >& xSeries )
4233 FSHelperPtr pFS = GetFS();
4234 Reference< chart2::XRegressionCurveContainer > xRegressionCurveContainer( xSeries, UNO_QUERY );
4235 if( !xRegressionCurveContainer.is() )
4236 return;
4238 const Sequence< Reference< chart2::XRegressionCurve > > aRegCurveSeq = xRegressionCurveContainer->getRegressionCurves();
4239 for( const Reference< chart2::XRegressionCurve >& xRegCurve : aRegCurveSeq )
4241 if (!xRegCurve.is())
4242 continue;
4244 Reference< XPropertySet > xProperties( xRegCurve , uno::UNO_QUERY );
4246 OUString aService;
4247 Reference< lang::XServiceName > xServiceName( xProperties, UNO_QUERY );
4248 if( !xServiceName.is() )
4249 continue;
4251 aService = xServiceName->getServiceName();
4253 if(aService != "com.sun.star.chart2.LinearRegressionCurve" &&
4254 aService != "com.sun.star.chart2.ExponentialRegressionCurve" &&
4255 aService != "com.sun.star.chart2.LogarithmicRegressionCurve" &&
4256 aService != "com.sun.star.chart2.PotentialRegressionCurve" &&
4257 aService != "com.sun.star.chart2.PolynomialRegressionCurve" &&
4258 aService != "com.sun.star.chart2.MovingAverageRegressionCurve")
4259 continue;
4261 pFS->startElement(FSNS(XML_c, XML_trendline));
4263 OUString aName;
4264 xProperties->getPropertyValue("CurveName") >>= aName;
4265 if(!aName.isEmpty())
4267 pFS->startElement(FSNS(XML_c, XML_name));
4268 pFS->writeEscaped(aName);
4269 pFS->endElement( FSNS( XML_c, XML_name) );
4272 exportShapeProps( xProperties );
4274 if( aService == "com.sun.star.chart2.LinearRegressionCurve" )
4276 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "linear");
4278 else if( aService == "com.sun.star.chart2.ExponentialRegressionCurve" )
4280 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "exp");
4282 else if( aService == "com.sun.star.chart2.LogarithmicRegressionCurve" )
4284 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "log");
4286 else if( aService == "com.sun.star.chart2.PotentialRegressionCurve" )
4288 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "power");
4290 else if( aService == "com.sun.star.chart2.PolynomialRegressionCurve" )
4292 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "poly");
4294 sal_Int32 aDegree = 2;
4295 xProperties->getPropertyValue( "PolynomialDegree") >>= aDegree;
4296 pFS->singleElement(FSNS(XML_c, XML_order), XML_val, OString::number(aDegree));
4298 else if( aService == "com.sun.star.chart2.MovingAverageRegressionCurve" )
4300 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "movingAvg");
4302 sal_Int32 aPeriod = 2;
4303 xProperties->getPropertyValue( "MovingAveragePeriod") >>= aPeriod;
4305 pFS->singleElement(FSNS(XML_c, XML_period), XML_val, OString::number(aPeriod));
4307 else
4309 // should never happen
4310 // This would produce invalid OOXML files so we check earlier for the type
4311 assert(false);
4314 double fExtrapolateForward = 0.0;
4315 double fExtrapolateBackward = 0.0;
4317 xProperties->getPropertyValue("ExtrapolateForward") >>= fExtrapolateForward;
4318 xProperties->getPropertyValue("ExtrapolateBackward") >>= fExtrapolateBackward;
4320 pFS->singleElement( FSNS( XML_c, XML_forward ),
4321 XML_val, OString::number(fExtrapolateForward) );
4323 pFS->singleElement( FSNS( XML_c, XML_backward ),
4324 XML_val, OString::number(fExtrapolateBackward) );
4326 bool bForceIntercept = false;
4327 xProperties->getPropertyValue("ForceIntercept") >>= bForceIntercept;
4329 if (bForceIntercept)
4331 double fInterceptValue = 0.0;
4332 xProperties->getPropertyValue("InterceptValue") >>= fInterceptValue;
4334 pFS->singleElement( FSNS( XML_c, XML_intercept ),
4335 XML_val, OString::number(fInterceptValue) );
4338 // Equation properties
4339 Reference< XPropertySet > xEquationProperties( xRegCurve->getEquationProperties() );
4341 // Show Equation
4342 bool bShowEquation = false;
4343 xEquationProperties->getPropertyValue("ShowEquation") >>= bShowEquation;
4345 // Show R^2
4346 bool bShowCorrelationCoefficient = false;
4347 xEquationProperties->getPropertyValue("ShowCorrelationCoefficient") >>= bShowCorrelationCoefficient;
4349 pFS->singleElement( FSNS( XML_c, XML_dispRSqr ),
4350 XML_val, ToPsz10(bShowCorrelationCoefficient) );
4352 pFS->singleElement(FSNS(XML_c, XML_dispEq), XML_val, ToPsz10(bShowEquation));
4354 pFS->endElement( FSNS( XML_c, XML_trendline ) );
4358 void ChartExport::exportMarker(const Reference< XPropertySet >& xPropSet)
4360 chart2::Symbol aSymbol;
4361 if( GetProperty( xPropSet, "Symbol" ) )
4362 mAny >>= aSymbol;
4364 if(aSymbol.Style != chart2::SymbolStyle_STANDARD && aSymbol.Style != chart2::SymbolStyle_NONE)
4365 return;
4367 FSHelperPtr pFS = GetFS();
4368 pFS->startElement(FSNS(XML_c, XML_marker));
4370 sal_Int32 nSymbol = aSymbol.StandardSymbol;
4371 // TODO: more properties support for marker
4372 const char* pSymbolType; // no initialization here, to let compiler warn if we have a code path
4373 // where it stays uninitialized
4374 switch( nSymbol )
4376 case 0:
4377 pSymbolType = "square";
4378 break;
4379 case 1:
4380 pSymbolType = "diamond";
4381 break;
4382 case 2:
4383 case 3:
4384 case 4:
4385 case 5:
4386 pSymbolType = "triangle";
4387 break;
4388 case 8:
4389 pSymbolType = "circle";
4390 break;
4391 case 9:
4392 pSymbolType = "star";
4393 break;
4394 case 10:
4395 pSymbolType = "x"; // in MS office 2010 built in symbol marker 'X' is represented as 'x'
4396 break;
4397 case 11:
4398 pSymbolType = "plus";
4399 break;
4400 case 13:
4401 pSymbolType = "dash";
4402 break;
4403 default:
4404 pSymbolType = "square";
4405 break;
4408 bool bSkipFormatting = false;
4409 if (aSymbol.Style == chart2::SymbolStyle_NONE)
4411 bSkipFormatting = true;
4412 pSymbolType = "none";
4415 pFS->singleElement(FSNS(XML_c, XML_symbol), XML_val, pSymbolType);
4417 if (!bSkipFormatting)
4419 awt::Size aSymbolSize = aSymbol.Size;
4420 sal_Int32 nSize = std::max( aSymbolSize.Width, aSymbolSize.Height );
4422 nSize = nSize/250.0*7.0 + 1; // just guessed based on some test cases,
4423 //the value is always 1 less than the actual value.
4424 nSize = std::clamp( int(nSize), 2, 72 );
4425 pFS->singleElement(FSNS(XML_c, XML_size), XML_val, OString::number(nSize));
4427 pFS->startElement(FSNS(XML_c, XML_spPr));
4429 util::Color aColor = aSymbol.FillColor;
4430 if (GetProperty(xPropSet, "Color"))
4431 mAny >>= aColor;
4433 if (aColor == -1)
4435 pFS->singleElement(FSNS(XML_a, XML_noFill));
4437 else
4438 WriteSolidFill(::Color(ColorTransparency, aColor));
4440 pFS->endElement( FSNS( XML_c, XML_spPr ) );
4443 pFS->endElement( FSNS( XML_c, XML_marker ) );
4446 void ChartExport::exportSmooth()
4448 FSHelperPtr pFS = GetFS();
4449 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY );
4450 sal_Int32 nSplineType = 0;
4451 if( GetProperty( xPropSet, "SplineType" ) )
4452 mAny >>= nSplineType;
4453 const char* pVal = nSplineType != 0 ? "1" : "0";
4454 pFS->singleElement(FSNS(XML_c, XML_smooth), XML_val, pVal);
4457 void ChartExport::exportFirstSliceAng( )
4459 FSHelperPtr pFS = GetFS();
4460 sal_Int32 nStartingAngle = 0;
4461 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
4462 if( GetProperty( xPropSet, "StartingAngle" ) )
4463 mAny >>= nStartingAngle;
4465 // convert to ooxml angle
4466 nStartingAngle = (450 - nStartingAngle ) % 360;
4467 pFS->singleElement(FSNS(XML_c, XML_firstSliceAng), XML_val, OString::number(nStartingAngle));
4470 namespace {
4472 const char* getErrorBarStyle(sal_Int32 nErrorBarStyle)
4474 switch(nErrorBarStyle)
4476 case cssc::ErrorBarStyle::NONE:
4477 return nullptr;
4478 case cssc::ErrorBarStyle::VARIANCE:
4479 break;
4480 case cssc::ErrorBarStyle::STANDARD_DEVIATION:
4481 return "stdDev";
4482 case cssc::ErrorBarStyle::ABSOLUTE:
4483 return "fixedVal";
4484 case cssc::ErrorBarStyle::RELATIVE:
4485 return "percentage";
4486 case cssc::ErrorBarStyle::ERROR_MARGIN:
4487 break;
4488 case cssc::ErrorBarStyle::STANDARD_ERROR:
4489 return "stdErr";
4490 case cssc::ErrorBarStyle::FROM_DATA:
4491 return "cust";
4492 default:
4493 assert(false && "can't happen");
4495 return nullptr;
4498 Reference< chart2::data::XDataSequence> getLabeledSequence(
4499 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > >& aSequences,
4500 bool bPositive )
4502 OUString aDirection;
4503 if(bPositive)
4504 aDirection = "positive";
4505 else
4506 aDirection = "negative";
4508 for( const auto& rSequence : aSequences )
4510 if( rSequence.is())
4512 uno::Reference< chart2::data::XDataSequence > xSequence( rSequence->getValues());
4513 uno::Reference< beans::XPropertySet > xSeqProp( xSequence, uno::UNO_QUERY_THROW );
4514 OUString aRole;
4515 if( ( xSeqProp->getPropertyValue( "Role" ) >>= aRole ) &&
4516 aRole.match( "error-bars" ) && aRole.indexOf(aDirection) >= 0 )
4518 return xSequence;
4523 return Reference< chart2::data::XDataSequence > ();
4528 void ChartExport::exportErrorBar(const Reference< XPropertySet>& xErrorBarProps, bool bYError)
4530 sal_Int32 nErrorBarStyle = cssc::ErrorBarStyle::NONE;
4531 xErrorBarProps->getPropertyValue("ErrorBarStyle") >>= nErrorBarStyle;
4532 const char* pErrorBarStyle = getErrorBarStyle(nErrorBarStyle);
4533 if(!pErrorBarStyle)
4534 return;
4536 FSHelperPtr pFS = GetFS();
4537 pFS->startElement(FSNS(XML_c, XML_errBars));
4538 pFS->singleElement(FSNS(XML_c, XML_errDir), XML_val, bYError ? "y" : "x");
4539 bool bPositive = false, bNegative = false;
4540 xErrorBarProps->getPropertyValue("ShowPositiveError") >>= bPositive;
4541 xErrorBarProps->getPropertyValue("ShowNegativeError") >>= bNegative;
4542 const char* pErrBarType;
4543 if(bPositive && bNegative)
4544 pErrBarType = "both";
4545 else if(bPositive)
4546 pErrBarType = "plus";
4547 else if(bNegative)
4548 pErrBarType = "minus";
4549 else
4551 // what the hell should we do now?
4552 // at least this makes the file valid
4553 pErrBarType = "both";
4555 pFS->singleElement(FSNS(XML_c, XML_errBarType), XML_val, pErrBarType);
4556 pFS->singleElement(FSNS(XML_c, XML_errValType), XML_val, pErrorBarStyle);
4557 pFS->singleElement(FSNS(XML_c, XML_noEndCap), XML_val, "0");
4558 if(nErrorBarStyle == cssc::ErrorBarStyle::FROM_DATA)
4560 uno::Reference< chart2::data::XDataSource > xDataSource(xErrorBarProps, uno::UNO_QUERY);
4561 Sequence< Reference < chart2::data::XLabeledDataSequence > > aSequences =
4562 xDataSource->getDataSequences();
4564 if(bPositive)
4566 exportSeriesValues(getLabeledSequence(aSequences, true), XML_plus);
4569 if(bNegative)
4571 exportSeriesValues(getLabeledSequence(aSequences, false), XML_minus);
4574 else
4576 double nVal = 0.0;
4577 if(nErrorBarStyle == cssc::ErrorBarStyle::STANDARD_DEVIATION)
4579 xErrorBarProps->getPropertyValue("Weight") >>= nVal;
4581 else
4583 if(bPositive)
4584 xErrorBarProps->getPropertyValue("PositiveError") >>= nVal;
4585 else
4586 xErrorBarProps->getPropertyValue("NegativeError") >>= nVal;
4589 pFS->singleElement(FSNS(XML_c, XML_val), XML_val, OString::number(nVal));
4592 exportShapeProps( xErrorBarProps );
4594 pFS->endElement( FSNS( XML_c, XML_errBars) );
4597 void ChartExport::exportView3D()
4599 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
4600 if( !xPropSet.is() )
4601 return;
4602 FSHelperPtr pFS = GetFS();
4603 pFS->startElement(FSNS(XML_c, XML_view3D));
4604 sal_Int32 eChartType = getChartType( );
4605 // rotX
4606 if( GetProperty( xPropSet, "RotationHorizontal" ) )
4608 sal_Int32 nRotationX = 0;
4609 mAny >>= nRotationX;
4610 if( nRotationX < 0 )
4612 if(eChartType == chart::TYPEID_PIE)
4614 /* In OOXML we get value in 0..90 range for pie chart X rotation , whereas we expect it to be in -90..90 range,
4615 so we convert that during import. It is modified in View3DConverter::convertFromModel()
4616 here we convert it back to 0..90 as we received in import */
4617 nRotationX += 90; // X rotation (map Chart2 [-179,180] to OOXML [0..90])
4619 else
4620 nRotationX += 360; // X rotation (map Chart2 [-179,180] to OOXML [-90..90])
4622 pFS->singleElement(FSNS(XML_c, XML_rotX), XML_val, OString::number(nRotationX));
4624 // rotY
4625 if( GetProperty( xPropSet, "RotationVertical" ) )
4627 // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
4628 if( eChartType == chart::TYPEID_PIE && GetProperty( xPropSet, "StartingAngle" ) )
4630 // Y rotation used as 'first pie slice angle' in 3D pie charts
4631 sal_Int32 nStartingAngle=0;
4632 mAny >>= nStartingAngle;
4633 // convert to ooxml angle
4634 nStartingAngle = (450 - nStartingAngle ) % 360;
4635 pFS->singleElement(FSNS(XML_c, XML_rotY), XML_val, OString::number(nStartingAngle));
4637 else
4639 sal_Int32 nRotationY = 0;
4640 mAny >>= nRotationY;
4641 // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
4642 if( nRotationY < 0 )
4643 nRotationY += 360;
4644 pFS->singleElement(FSNS(XML_c, XML_rotY), XML_val, OString::number(nRotationY));
4647 // rAngAx
4648 if( GetProperty( xPropSet, "RightAngledAxes" ) )
4650 bool bRightAngled = false;
4651 mAny >>= bRightAngled;
4652 const char* sRightAngled = bRightAngled ? "1":"0";
4653 pFS->singleElement(FSNS(XML_c, XML_rAngAx), XML_val, sRightAngled);
4655 // perspective
4656 if( GetProperty( xPropSet, "Perspective" ) )
4658 sal_Int32 nPerspective = 0;
4659 mAny >>= nPerspective;
4660 // map Chart2 [0,100] to OOXML [0..200]
4661 nPerspective *= 2;
4662 pFS->singleElement(FSNS(XML_c, XML_perspective), XML_val, OString::number(nPerspective));
4664 pFS->endElement( FSNS( XML_c, XML_view3D ) );
4667 bool ChartExport::isDeep3dChart()
4669 bool isDeep = false;
4670 if( mbIs3DChart )
4672 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
4673 if( GetProperty( xPropSet, "Deep" ) )
4674 mAny >>= isDeep;
4676 return isDeep;
4679 OUString ChartExport::getNumberFormatCode(sal_Int32 nKey) const
4681 /* XXX if this was called more than one or two times per export the two
4682 * SvNumberFormatter instances and NfKeywordTable should be member
4683 * variables and initialized only once. */
4685 OUString aCode("General"); // init with fallback
4686 uno::Reference<util::XNumberFormatsSupplier> xNumberFormatsSupplier(mxChartModel, uno::UNO_QUERY_THROW);
4687 SvNumberFormatsSupplierObj* pSupplierObj = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( xNumberFormatsSupplier);
4688 if (!pSupplierObj)
4689 return aCode;
4691 SvNumberFormatter* pNumberFormatter = pSupplierObj->GetNumberFormatter();
4692 if (!pNumberFormatter)
4693 return aCode;
4695 SvNumberFormatter aTempFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US);
4696 NfKeywordTable aKeywords;
4697 aTempFormatter.FillKeywordTableForExcel( aKeywords);
4698 aCode = pNumberFormatter->GetFormatStringForExcel( nKey, aKeywords, aTempFormatter);
4700 return aCode;
4703 }// oox
4705 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */