1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
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>
108 #include <unordered_set>
110 #include <o3tl/temporary.hxx>
111 #include <o3tl/sorted_vector.hxx>
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
{
131 bool isPrimaryAxes(sal_Int32 nIndex
)
133 assert(nIndex
== 0 || nIndex
== 1);
137 class lcl_MatchesRole
140 explicit lcl_MatchesRole( OUString aRole
) :
141 m_aRole(std::move( aRole
))
144 bool operator () ( const Reference
< chart2::data::XLabeledDataSequence
> & xSeq
) const
148 Reference
< beans::XPropertySet
> xProp( xSeq
->getValues(), uno::UNO_QUERY
);
151 return ( xProp
.is() &&
152 (xProp
->getPropertyValue( "Role" ) >>= 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());
184 chart2::ScaleData aScaleData
= xAxis
->getScaleData();
185 if( aScaleData
.Categories
.is())
187 bHasDateCategories
= aScaleData
.AxisType
== chart2::AxisType::DATE
;
188 xResult
.set( aScaleData
.Categories
);
196 catch( const uno::Exception
& )
198 DBG_UNHANDLED_EXCEPTION("oox");
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
));
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());
249 chart2::ScaleData aScaleData
= xAxis
->getScaleData();
250 bCategoryPositionShifted
= aScaleData
.ShiftedCategoryPosition
;
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());
282 chart2::ScaleData aScaleData
= xAxis
->getScaleData();
283 nAxisType
= aScaleData
.AxisType
;
289 catch (const uno::Exception
&)
291 DBG_UNHANDLED_EXCEPTION("oox");
297 static OUString
lclGetTimeUnitToken( sal_Int32 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");
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());
327 chart2::ScaleData aScaleData
= xAxis
->getScaleData();
328 aTimeIncrement
= aScaleData
.TimeIncrement
;
334 catch (const uno::Exception
&)
336 DBG_UNHANDLED_EXCEPTION("oox");
339 return aTimeIncrement
;
342 static bool lcl_isSeriesAttachedToFirstAxis(
343 const Reference
< chart2::XDataSeries
> & xDataSeries
)
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");
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
];
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())
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() );
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
);
430 const Sequence
< double > aValues( xNumSeq
->getNumericalData());
431 aResult
.insert( aResult
.end(), aValues
.begin(), aValues
.end() );
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
];
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
;
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
505 void DataLabelsRange::setRange(const OUString
& 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
)
529 , mxChartModel( xModel
)
530 , mpURLTransformer(std::make_shared
<URLTransformer
>())
531 , mbHasCategoryLabels( false )
532 , mbHasZAxis( false )
533 , mbIs3DChart( 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
);
553 uno::Sequence
< beans::PropertyValue
> createArguments(
554 const OUString
& rRangeRepresentation
, bool bUseColumns
)
556 css::chart::ChartDataRowSource eRowSource
= css::chart::ChartDataRowSource_ROWS
;
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
}
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
);
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());
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
);
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(); });
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
>();
671 return aFinalSplitSource
;
674 catch (const uno::Exception
&)
676 DBG_UNHANDLED_EXCEPTION("oox");
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
)
716 Reference
< XFormulaParser
> xParser
;
717 uno::Reference
< lang::XMultiServiceFactory
> xSF
= GetFB()->getModelFactory();
722 xParser
.set( xSF
->createInstance("com.sun.star.sheet.FormulaParser"), UNO_QUERY
);
729 SAL_WARN_IF(!xParser
.is(), "oox", "creating formula parser failed");
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 ) );
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(".$", "!$" );
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
);
780 sName
= xNamed
->getName();
782 pFS
->startElementNS( mnXmlNamespace
, XML_cNvPr
,
783 XML_id
, OString::number(nID
),
787 if ( GetProperty( xShapeProps
, "URL" ) )
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" );
814 const char* sFullPath
= nullptr;
815 const char* sRelativePath
= nullptr;
816 switch( GetDocumentType() )
820 sFullPath
= "word/charts/chart";
821 sRelativePath
= "charts/chart";
826 sFullPath
= "ppt/charts/chart";
827 sRelativePath
= "../charts/chart";
832 sFullPath
= "xl/charts/chart";
833 sRelativePath
= "../charts/chart";
838 sFullPath
= "charts/chart";
839 sRelativePath
= "charts/chart";
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(
854 pFS
->getOutputStream(),
855 "application/vnd.openxmlformats-officedocument.drawingml.chart+xml",
856 oox::getRelationship(Relationship::CHART
),
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
);
872 pChart
->endDocument();
875 void ChartExport::InitRangeSegmentationProperties( const Reference
< chart2::XChartDocument
> & xChartDoc
)
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() )
901 InitRangeSegmentationProperties( xChartDoc
);
902 // TODO: export chart
906 void ChartExport::ExportContent_()
908 Reference
< css::chart::XChartDocument
> xChartDoc( getModel(), uno::UNO_QUERY
);
911 // determine if data comes from the outside
912 bool bIncludeTable
= true;
914 Reference
< chart2::XChartDocument
> xNewDoc( xChartDoc
, uno::UNO_QUERY
);
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
);
930 OSL_FAIL( "Couldn't export chart due to wrong XModel" );
934 void ChartExport::exportChartSpace( const Reference
< css::chart::XChartDocument
>& xChartDoc
,
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");
950 // TODO:external data
953 exportChart(xChartDoc
);
955 // TODO: printSettings
957 // TODO: text properties
958 // TODO: shape properties
959 Reference
< XPropertySet
> xPropSet
= xChartDoc
->getArea();
961 exportShapeProps( xPropSet
);
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
)
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())
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 );
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(),
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())
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() )
1033 const char* sFullPath
= nullptr;
1034 const char* sRelativePath
= nullptr;
1035 sal_Int32 nDrawing
= getNewDrawingUniqueId();
1037 switch (GetDocumentType())
1041 sFullPath
= "word/drawings/drawing";
1042 sRelativePath
= "../drawings/drawing";
1047 sFullPath
= "ppt/drawings/drawing";
1048 sRelativePath
= "../drawings/drawing";
1053 sFullPath
= "xl/drawings/drawing";
1054 sRelativePath
= "../drawings/drawing";
1059 sFullPath
= "drawings/drawing";
1060 sRelativePath
= "drawings/drawing";
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(
1076 GetFS()->getOutputStream(),
1077 "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml",
1078 oox::getRelationship(Relationship::CHARTUSERSHAPES
),
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?");
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() );
1128 mxNewDiagram
.set( xNewDoc
->getFirstDiagram());
1130 // get Properties of ChartDocument
1131 bool bHasMainTitle
= false;
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
& )
1163 FSHelperPtr pFS
= GetFS();
1164 pFS
->startElement(FSNS(XML_c
, XML_chart
));
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");
1179 pFS
->singleElement(FSNS(XML_c
, XML_autoTitleDeleted
), XML_val
, "1");
1188 Reference
< beans::XPropertySet
> xFloor
= mxNewDiagram
->getFloor();
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();
1202 pFS
->startElement(FSNS(XML_c
, XML_sideWall
));
1203 exportShapeProps( xWall
);
1204 pFS
->endElement( FSNS( XML_c
, XML_sideWall
) );
1207 pFS
->startElement(FSNS(XML_c
, XML_backWall
));
1208 exportShapeProps( xWall
);
1209 pFS
->endElement( FSNS( XML_c
, XML_backWall
) );
1214 exportPlotArea( xChartDoc
);
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
)
1236 uno::Any aAny
= xPropSet
->getPropertyValue("MissingValueTreatment");
1237 if (!(aAny
>>= nVal
))
1240 const char* pVal
= nullptr;
1243 case cssc::MissingValueTreatment::LEAVE_GAP
:
1246 case cssc::MissingValueTreatment::USE_ZERO
:
1249 case cssc::MissingValueTreatment::CONTINUE
:
1253 SAL_WARN("oox", "unknown MissingValueTreatment value");
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
);
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
:
1287 case css::chart::ChartLegendPosition_RIGHT
:
1290 case css::chart::ChartLegendPosition_TOP
:
1293 case css::chart::ChartLegendPosition_BOTTOM
:
1296 case css::chart::ChartLegendPosition_NONE
:
1297 case css::chart::ChartLegendPosition::ChartLegendPosition_MAKE_FIXED_SIZE
:
1302 if( strPos
!= nullptr )
1304 pFS
->singleElement(FSNS(XML_c
, XML_legendPos
), XML_val
, strPos
);
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())
1323 for (const auto& rCT
: xChartTypeSequence
)
1325 Reference
<chart2::XDataSeriesContainer
> xDSCont(rCT
, UNO_QUERY
);
1329 OUString
aChartType(rCT
->getChartType());
1330 bool bIsPie
= lcl_getChartType(aChartType
) == chart::TYPEID_PIE
;
1333 PropertySet
xChartTypeProp(rCT
);
1334 bIsPie
= !xChartTypeProp
.getBoolProperty(PROP_UseRings
);
1336 const Sequence
<Reference
<chart2::XDataSeries
>> aDataSeriesSeq
= xDSCont
->getDataSeries();
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
);
1359 const Sequence
<Reference
<chart2::data::XLabeledDataSequence
>> aDataSeqs
= xDSrc
->getDataSequences();
1360 for (const auto& rDataSeq
: aDataSeqs
)
1362 Reference
<chart2::data::XDataSequence
> xValues
= rDataSeq
->getValues();
1366 sal_Int32 nDataSeqSize
= xValues
->getData().getLength();
1367 nIndex
+= nDataSeqSize
;
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
++;
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");
1430 pFS
->singleElement(FSNS(XML_c
, XML_overlay
), XML_val
, "0");
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
)
1446 Reference
< beans::XPropertySet
> xPropSet( xShape
, uno::UNO_QUERY
);
1449 xPropSet
->getPropertyValue("String") >>= sText
;
1452 // tdf#101322: add subtitle to title
1454 sText
= sText
.isEmpty() ? *pSubText
: sText
+ "\n" + *pSubText
;
1456 if( sText
.isEmpty() )
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
));
1466 const char* sWritingMode
= nullptr;
1467 bool bVertical
= false;
1468 xPropSet
->getPropertyValue("StackedText") >>= 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
) );
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;
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
));
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");
1547 exportShapeProps( xPropSet
);
1550 pFS
->endElement( FSNS( XML_c
, XML_title
) );
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
);
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
);
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
));
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())
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
);
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() );
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");
1647 pFS
->endElement(FSNS(XML_c
, XML_barChart
));
1650 for( const auto& rCS
: aCooSysSeq
)
1652 Reference
< chart2::XChartTypeContainer
> xCTCnt( rCS
, uno::UNO_QUERY
);
1656 const Sequence
< Reference
< chart2::XChartType
> > aCTSeq( xCTCnt
->getChartTypes());
1657 for( const auto& rCT
: aCTSeq
)
1659 Reference
< chart2::XDataSeriesContainer
> xDSCnt( rCT
, uno::UNO_QUERY
);
1662 Reference
< chart2::XChartType
> xChartType( rCT
, uno::UNO_QUERY
);
1663 if( ! xChartType
.is())
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
);
1675 case chart::TYPEID_AREA
:
1677 exportAreaChart( xChartType
);
1680 case chart::TYPEID_LINE
:
1682 exportLineChart( xChartType
);
1685 case chart::TYPEID_BUBBLE
:
1687 exportBubbleChart( xChartType
);
1690 case chart::TYPEID_OFPIE
:
1694 case chart::TYPEID_DOUGHNUT
:
1695 case chart::TYPEID_PIE
:
1697 exportPieChart( xChartType
);
1700 case chart::TYPEID_RADARLINE
:
1701 case chart::TYPEID_RADARAREA
:
1703 exportRadarChart( xChartType
);
1706 case chart::TYPEID_SCATTER
:
1708 exportScatterChart( xChartType
);
1711 case chart::TYPEID_STOCK
:
1713 exportStockChart( xChartType
);
1716 case chart::TYPEID_SURFACE
:
1718 exportSurfaceChart( xChartType
);
1723 SAL_WARN("oox", "ChartExport::exportPlotArea -- not support chart type");
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
:
1794 case drawing::Alignment_TOP_LEFT
:
1796 case drawing::Alignment_BOTTOM_LEFT
:
1799 case drawing::Alignment_TOP
:
1802 case drawing::Alignment_CENTER
:
1806 case drawing::Alignment_BOTTOM
:
1810 case drawing::Alignment_TOP_RIGHT
:
1813 case drawing::Alignment_BOTTOM_RIGHT
:
1817 case drawing::Alignment_RIGHT
:
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" ))
1842 FillStyle
aFillStyle(FillStyle_NONE
);
1843 mAny
>>= aFillStyle
;
1845 // map full transparent background to no fill
1846 if (aFillStyle
== FillStyle_SOLID
&& GetProperty( xPropSet
, "FillTransparence" ))
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
);
1871 case FillStyle_GRADIENT
:
1872 exportGradientFill( xPropSet
);
1874 case FillStyle_BITMAP
:
1875 exportBitmapFill( xPropSet
);
1877 case FillStyle_HATCH
:
1878 exportHatch(xPropSet
);
1880 case FillStyle_NONE
:
1881 mpFS
->singleElementNS(XML_a
, XML_noFill
);
1888 void ChartExport::exportSolidFill(const Reference
< XPropertySet
>& xPropSet
)
1890 // Similar to DrawingML::WriteSolidFill, but gradient access via name
1891 // and currently no InteropGrabBag
1893 sal_uInt32 nFillColor
= 0;
1894 if (!GetProperty(xPropSet
, "FillColor") || !(mAny
>>= nFillColor
))
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;
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
);
1942 WriteSolidFill(::Color(ColorTransparency
, nFillColor
& 0xffffff), nAlpha
);
1945 void ChartExport::exportHatch( const Reference
< XPropertySet
>& xPropSet
)
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
;
1959 WritePattFill(xPropSet
, aHatch
);
1964 void ChartExport::exportBitmapFill( const Reference
< XPropertySet
>& xPropSet
)
1966 if( !xPropSet
.is() )
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
);
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() )
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);
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())
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"))
2070 pFS
->startElement(FSNS(XML_c
, XML_dTable
));
2073 pFS
->singleElement(FSNS(XML_c
, XML_showHorzBorder
), XML_val
, "1" );
2075 pFS
->singleElement(FSNS(XML_c
, XML_showVertBorder
), XML_val
, "1");
2077 pFS
->singleElement(FSNS(XML_c
, XML_showOutline
), XML_val
, "1");
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())
2096 sal_Int32 nTypeId
= XML_areaChart
;
2098 nTypeId
= XML_area3DChart
;
2099 pFS
->startElement(FSNS(XML_c
, nTypeId
));
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
;
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())
2123 pFS
->startElement(FSNS(XML_c
, nTypeId
));
2125 bool bVertical
= false;
2126 Reference
< XPropertySet
> xPropSet(mxDiagram
, uno::UNO_QUERY
);
2127 if (GetProperty(xPropSet
, "Vertical"))
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
));
2156 namespace cssc
= css::chart
;
2157 sal_Int32 nGeom3d
= cssc::ChartSolidType::RECTANGULAR_SOLID
;
2158 if (xPropSet
.is() && GetProperty(xPropSet
, "SolidType"))
2160 const char* sShapeType
= nullptr;
2163 case cssc::ChartSolidType::RECTANGULAR_SOLID
:
2166 case cssc::ChartSolidType::CONE
:
2167 sShapeType
= "cone";
2169 case cssc::ChartSolidType::CYLINDER
:
2170 sShapeType
= "cylinder";
2172 case cssc::ChartSolidType::PYRAMID
:
2173 sShapeType
= "pyramid";
2176 pFS
->singleElement(FSNS(XML_c
, XML_shape
), XML_val
, sShapeType
);
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)
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())
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
);
2242 exportFirstSliceAng( );
2244 pFS
->singleElement(FSNS(XML_c
, XML_holeSize
), XML_val
, OString::number(50));
2246 pFS
->endElement( FSNS( XML_c
, XML_doughnutChart
) );
2251 void writeDataLabelsRange(const FSHelperPtr
& pFS
, const XmlFilterBase
* pFB
, DataLabelsRange
& rDLblsRange
)
2253 if (rDLblsRange
.empty())
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())
2295 sal_Int32 nTypeId
= XML_lineChart
;
2297 nTypeId
= XML_line3DChart
;
2298 pFS
->startElement(FSNS(XML_c
, nTypeId
));
2302 exportVaryColors(xChartType
);
2303 // TODO: show marker symbol in series?
2304 bool bPrimaryAxes
= true;
2305 exportSeries(xChartType
, splitDataSeries
, bPrimaryAxes
);
2308 sal_Int32 nSymbolType
= css::chart::ChartSymbolType::NONE
;
2309 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
2310 if( GetProperty( xPropSet
, "SymbolType" ) )
2311 mAny
>>= nSymbolType
;
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
);
2335 FSHelperPtr pFS
= GetFS();
2336 sal_Int32 nTypeId
= XML_pieChart
;
2338 nTypeId
= XML_pie3DChart
;
2339 pFS
->startElement(FSNS(XML_c
, nTypeId
));
2341 exportVaryColors(xChartType
);
2343 bool bPrimaryAxes
= true;
2344 exportAllSeries(xChartType
, bPrimaryAxes
);
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
));
2361 sal_Int32 eChartType
= getChartType( );
2362 const char* radarStyle
= nullptr;
2363 if( eChartType
== chart::TYPEID_RADARAREA
)
2364 radarStyle
= "filled";
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;
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())
2417 exportScatterChartSeries(xChartType
, &splitDataSeries
);
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())
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())
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())
2460 Reference
< beans::XPropertySet
> xStockPropSet
= xChartPropProvider
->getMinMaxLine();
2461 if( !xStockPropSet
.is() )
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")
2474 FSHelperPtr pFS
= GetFS();
2475 // export the chart property
2476 Reference
< css::chart::XStatisticDisplay
> xChartPropProvider( mxDiagram
, uno::UNO_QUERY
);
2477 if(!xChartPropProvider
.is())
2481 pFS
->startElement(FSNS(XML_c
, XML_upDownBars
));
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
;
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
);
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
));
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
)
2564 Reference
< chart2::data::XDataSource
> xSource( rSeries
, uno::UNO_QUERY
);
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
);
2585 xSeqProp
->getPropertyValue("Role") >>= aRole
;
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
++) );
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");
2641 case chart::TYPEID_LINE
:
2643 exportMarker(xOldPropSet
);
2646 case chart::TYPEID_PIE
:
2647 case chart::TYPEID_DOUGHNUT
:
2649 if( xOldPropSet
.is() && GetProperty( xOldPropSet
, "SegmentOffset") )
2651 sal_Int32 nOffset
= 0;
2653 pFS
->singleElement( FSNS( XML_c
, XML_explosion
),
2654 XML_val
, OString::number( nOffset
) );
2658 case chart::TYPEID_SCATTER
:
2660 exportMarker(xOldPropSet
);
2663 case chart::TYPEID_RADARLINE
:
2665 exportMarker(xOldPropSet
);
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
) )
2706 Reference
< chart2::data::XLabeledDataSequence
> xSequence( lcl_getDataSequenceByRole( aSeqCnt
, "values-x" ) );
2707 if( xSequence
.is() )
2709 Reference
< chart2::data::XDataSequence
> xValues( xSequence
->getValues() );
2711 exportSeriesValues( xValues
, XML_xVal
);
2713 else if( mxCategoriesValues
.is() )
2714 exportSeriesCategory( mxCategoriesValues
, XML_xVal
);
2717 if( eChartType
== chart::TYPEID_BUBBLE
)
2720 Reference
< chart2::data::XLabeledDataSequence
> xSequence( lcl_getDataSequenceByRole( aSeqCnt
, "values-y" ) );
2721 if( xSequence
.is() )
2723 Reference
< chart2::data::XDataSequence
> xValues( xSequence
->getValues() );
2725 exportSeriesValues( xValues
, XML_yVal
);
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
)
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
,
2762 for( const Reference
< chart2::XDataSeries
>& xSeries
: aSeriesSeq
)
2764 rPrimaryAxes
= lcl_isSeriesAttachedToFirstAxis(xSeries
);
2766 Reference
< chart2::data::XDataSource
> xSource( xSeries
, uno::UNO_QUERY
);
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) );
2796 if( xLabelSeq
.is() )
2797 exportSeriesText( xLabelSeq
);
2799 // TODO:export shape properties
2801 // export categories
2802 if( mxCategoriesValues
.is() )
2803 exportSeriesCategory( mxCategoriesValues
);
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
));
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"))
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";
3015 textWordWrap
= "none";
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
);
3041 pFS
->singleElement(FSNS(XML_a
, XML_bodyPr
), XML_rot
, OString::number(nRotation
), XML_wrap
, textWordWrap
);
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
] );
3100 sal_Int32
getXAxisTypeByChartType(sal_Int32 eChartType
)
3102 if( (eChartType
== chart::TYPEID_SCATTER
)
3103 || (eChartType
== chart::TYPEID_BUBBLE
) )
3105 else if( eChartType
== chart::TYPEID_STOCK
)
3111 sal_Int32
getRealXAxisType(sal_Int32 nAxisType
)
3113 if( nAxisType
== chart2::AxisType::CATEGORY
)
3115 else if( nAxisType
== chart2::AxisType::DATE
)
3117 else if( nAxisType
== chart2::AxisType::SERIES
)
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
);
3181 nAxisType
= getXAxisTypeByChartType( getChartType() );
3182 // FIXME: axPos, need to check axis direction
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
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
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
);
3242 nAxisType
= getXAxisTypeByChartType( getChartType() );
3243 // FIXME: axPos, need to check axis direction
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
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
;
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" ) )
3307 if( !bAutoMax
&& (GetProperty( xAxisProp
, "Max" ) ) )
3311 pFS
->singleElement(FSNS(XML_c
, XML_max
), XML_val
, OString::number(dMax
));
3314 bool bAutoMin
= false;
3315 if(GetProperty( xAxisProp
, "AutoMin" ) )
3318 if( !bAutoMin
&& (GetProperty( xAxisProp
, "Min" ) ) )
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
);
3342 if( xMajorGrid
.is())
3344 pFS
->startElement(FSNS(XML_c
, XML_majorGridlines
));
3345 exportShapeProps( xMajorGrid
);
3346 pFS
->endElement( FSNS( XML_c
, XML_majorGridlines
) );
3350 if( xMinorGrid
.is())
3352 pFS
->startElement(FSNS(XML_c
, XML_minorGridlines
));
3353 exportShapeProps( xMinorGrid
);
3354 pFS
->endElement( FSNS( XML_c
, XML_minorGridlines
) );
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"))
3370 aNumberFormatString
= getNumberFormatCode(nKey
);
3373 pFS
->singleElement(FSNS(XML_c
, XML_numFmt
),
3374 XML_formatCode
, aNumberFormatString
,
3375 XML_sourceLinked
, bLinkedNumFmt
? "1" : "0");
3378 sal_Int32 nValue
= 0;
3379 if(GetProperty( xAxisProp
, "Marks" ) )
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";
3388 majorTickMark
= "in";
3390 majorTickMark
= "out";
3392 majorTickMark
= "none";
3393 pFS
->singleElement(FSNS(XML_c
, XML_majorTickMark
), XML_val
, majorTickMark
);
3396 if(GetProperty( xAxisProp
, "HelpMarks" ) )
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";
3405 minorTickMark
= "in";
3407 minorTickMark
= "out";
3409 minorTickMark
= "none";
3410 pFS
->singleElement(FSNS(XML_c
, XML_minorTickMark
), XML_val
, minorTickMark
);
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";
3427 case css::chart::ChartAxisLabelPosition_OUTSIDE_START
:
3428 sTickLblPos
= "low";
3430 case css::chart::ChartAxisLabelPosition_OUTSIDE_END
:
3431 sTickLblPos
= "high";
3434 sTickLblPos
= "nextTo";
3440 sTickLblPos
= "none";
3442 pFS
->singleElement(FSNS(XML_c
, XML_tickLblPos
), XML_val
, sTickLblPos
);
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
);
3461 case css::chart::ChartAxisPosition_START
:
3464 case css::chart::ChartAxisPosition_END
:
3467 case css::chart::ChartAxisPosition_ZERO
:
3468 sCrosses
= "autoZero";
3471 bCrossesValue
= true;
3476 if( bCrossesValue
&& GetProperty( xAxisProp
, "CrossoverValue" ) )
3480 pFS
->singleElement(FSNS(XML_c
, XML_crossesAt
), XML_val
, OString::number(dValue
));
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
)
3513 else if( rAxisIdPair
.nAxisType
== AXIS_SECONDARY_X
)
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));
3539 if( nAxisType
== XML_valAx
)
3541 if( lcl_isCategoryAxisShifted( mxNewDiagram
))
3542 pFS
->singleElement(FSNS(XML_c
, XML_crossBetween
), XML_val
, "between");
3544 pFS
->singleElement(FSNS(XML_c
, XML_crossBetween
), XML_val
, "midCat");
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
));
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
;
3588 if(GetProperty( xAxisProp
, "BuiltInUnit" ))
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
) );
3610 struct LabelPlacementParam
3613 sal_Int32 meDefault
;
3615 std::unordered_set
<sal_Int32
> maAllowedValues
;
3617 LabelPlacementParam(bool bExport
, sal_Int32 nDefault
) :
3619 meDefault(nDefault
),
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
)
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";
3657 OUString
getFieldTypeString( const chart2::DataPointCustomLabelFieldType aType
)
3661 case chart2::DataPointCustomLabelFieldType_CATEGORYNAME
:
3662 return "CATEGORYNAME";
3664 case chart2::DataPointCustomLabelFieldType_SERIESNAME
:
3665 return "SERIESNAME";
3667 case chart2::DataPointCustomLabelFieldType_VALUE
:
3670 case chart2::DataPointCustomLabelFieldType_CELLREF
:
3673 case chart2::DataPointCustomLabelFieldType_CELLRANGE
:
3682 void writeRunProperties( ChartExport
* pChartExport
, Reference
<XPropertySet
> const & xPropertySet
)
3684 bool bDummy
= false;
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
;
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();
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]";
3724 sContent
= rField
->getString();
3727 if (aType
== chart2::DataPointCustomLabelFieldType_NEWLINE
)
3728 bNewParagraph
= true;
3729 else if (aType
!= chart2::DataPointCustomLabelFieldType_TEXT
)
3730 sFieldType
= getFieldTypeString(aType
);
3734 pFS
->endElement(FSNS(XML_a
, XML_p
));
3735 pFS
->startElement(FSNS(XML_a
, XML_p
));
3739 if (sFieldType
.isEmpty())
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
));
3754 pFS
->startElement(FSNS(XML_a
, XML_fld
), XML_id
, rField
->getGuid(), XML_type
,
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
)
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)
3881 uno::Reference
<beans::XPropertySet
> xPropSet(xSeries
, uno::UNO_QUERY
);
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")))
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;
3924 // All pie charts support label placement.
3925 aParam
.mbExport
= true;
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;
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
;
3956 for (const sal_Int32 nIdx
: std::as_const(aAttrLabelIndices
))
3958 uno::Reference
<beans::XPropertySet
> xLabelPropSet
= xSeries
->getDataPointByIndex(nIdx
);
3960 if (!xLabelPropSet
.is())
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
;
3991 bLabelIsNumberFormat
= true;
3993 if (GetProperty(xLabelPropSet
, bLabelIsNumberFormat
? OUString("NumberFormat") : OUString("PercentageNumberFormat")))
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();
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" );
4072 // property set only containing the color
4073 xPropSet
.set( new ColorPropertySet( ColorTransparency
, xColorScheme
->getColorByIndex( nElement
)));
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
));
4084 case chart::TYPEID_PIE
:
4085 case chart::TYPEID_DOUGHNUT
:
4087 if( xPropSet
.is() && GetProperty( xPropSet
, "SegmentOffset") )
4089 sal_Int32 nOffset
= 0;
4092 pFS
->singleElement( FSNS( XML_c
, XML_explosion
),
4093 XML_val
, OString::number( nOffset
) );
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
)
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" );
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
);
4147 case chart::TYPEID_LINE
:
4148 case chart::TYPEID_SCATTER
:
4149 case chart::TYPEID_RADARLINE
:
4150 exportMarker(xPropSet
);
4154 exportShapeProps(xPropSet
);
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
;
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
));
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
);
4209 if( GetProperty( xPropSet
, "Stacked" ) )
4211 if( GetProperty( xPropSet
, "Percent" ) )
4214 const char* grouping
= nullptr;
4216 grouping
= "stacked";
4218 grouping
= "percentStacked";
4221 if( isBar
&& !isDeep3dChart() )
4223 grouping
= "clustered";
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() )
4238 const Sequence
< Reference
< chart2::XRegressionCurve
> > aRegCurveSeq
= xRegressionCurveContainer
->getRegressionCurves();
4239 for( const Reference
< chart2::XRegressionCurve
>& xRegCurve
: aRegCurveSeq
)
4241 if (!xRegCurve
.is())
4244 Reference
< XPropertySet
> xProperties( xRegCurve
, uno::UNO_QUERY
);
4247 Reference
< lang::XServiceName
> xServiceName( xProperties
, UNO_QUERY
);
4248 if( !xServiceName
.is() )
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")
4261 pFS
->startElement(FSNS(XML_c
, XML_trendline
));
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
));
4309 // should never happen
4310 // This would produce invalid OOXML files so we check earlier for the type
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() );
4342 bool bShowEquation
= false;
4343 xEquationProperties
->getPropertyValue("ShowEquation") >>= bShowEquation
;
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" ) )
4364 if(aSymbol
.Style
!= chart2::SymbolStyle_STANDARD
&& aSymbol
.Style
!= chart2::SymbolStyle_NONE
)
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
4377 pSymbolType
= "square";
4380 pSymbolType
= "diamond";
4386 pSymbolType
= "triangle";
4389 pSymbolType
= "circle";
4392 pSymbolType
= "star";
4395 pSymbolType
= "x"; // in MS office 2010 built in symbol marker 'X' is represented as 'x'
4398 pSymbolType
= "plus";
4401 pSymbolType
= "dash";
4404 pSymbolType
= "square";
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"))
4435 pFS
->singleElement(FSNS(XML_a
, XML_noFill
));
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
));
4472 const char* getErrorBarStyle(sal_Int32 nErrorBarStyle
)
4474 switch(nErrorBarStyle
)
4476 case cssc::ErrorBarStyle::NONE
:
4478 case cssc::ErrorBarStyle::VARIANCE
:
4480 case cssc::ErrorBarStyle::STANDARD_DEVIATION
:
4482 case cssc::ErrorBarStyle::ABSOLUTE
:
4484 case cssc::ErrorBarStyle::RELATIVE
:
4485 return "percentage";
4486 case cssc::ErrorBarStyle::ERROR_MARGIN
:
4488 case cssc::ErrorBarStyle::STANDARD_ERROR
:
4490 case cssc::ErrorBarStyle::FROM_DATA
:
4493 assert(false && "can't happen");
4498 Reference
< chart2::data::XDataSequence
> getLabeledSequence(
4499 const uno::Sequence
< uno::Reference
< chart2::data::XLabeledDataSequence
> >& aSequences
,
4502 OUString aDirection
;
4504 aDirection
= "positive";
4506 aDirection
= "negative";
4508 for( const auto& rSequence
: aSequences
)
4512 uno::Reference
< chart2::data::XDataSequence
> xSequence( rSequence
->getValues());
4513 uno::Reference
< beans::XPropertySet
> xSeqProp( xSequence
, uno::UNO_QUERY_THROW
);
4515 if( ( xSeqProp
->getPropertyValue( "Role" ) >>= aRole
) &&
4516 aRole
.match( "error-bars" ) && aRole
.indexOf(aDirection
) >= 0 )
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
);
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";
4546 pErrBarType
= "plus";
4548 pErrBarType
= "minus";
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();
4566 exportSeriesValues(getLabeledSequence(aSequences
, true), XML_plus
);
4571 exportSeriesValues(getLabeledSequence(aSequences
, false), XML_minus
);
4577 if(nErrorBarStyle
== cssc::ErrorBarStyle::STANDARD_DEVIATION
)
4579 xErrorBarProps
->getPropertyValue("Weight") >>= nVal
;
4584 xErrorBarProps
->getPropertyValue("PositiveError") >>= nVal
;
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() )
4602 FSHelperPtr pFS
= GetFS();
4603 pFS
->startElement(FSNS(XML_c
, XML_view3D
));
4604 sal_Int32 eChartType
= getChartType( );
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])
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
));
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
));
4639 sal_Int32 nRotationY
= 0;
4640 mAny
>>= nRotationY
;
4641 // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
4642 if( nRotationY
< 0 )
4644 pFS
->singleElement(FSNS(XML_c
, XML_rotY
), XML_val
, OString::number(nRotationY
));
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
);
4656 if( GetProperty( xPropSet
, "Perspective" ) )
4658 sal_Int32 nPerspective
= 0;
4659 mAny
>>= nPerspective
;
4660 // map Chart2 [0,100] to OOXML [0..200]
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;
4672 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
4673 if( GetProperty( xPropSet
, "Deep" ) )
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
);
4691 SvNumberFormatter
* pNumberFormatter
= pSupplierObj
->GetNumberFormatter();
4692 if (!pNumberFormatter
)
4695 SvNumberFormatter
aTempFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US
);
4696 NfKeywordTable aKeywords
;
4697 aTempFormatter
.FillKeywordTableForExcel( aKeywords
);
4698 aCode
= pNumberFormatter
->GetFormatStringForExcel( nKey
, aKeywords
, aTempFormatter
);
4705 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */