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/PieChartSubType.hpp>
69 #include <com/sun/star/chart2/Symbol.hpp>
70 #include <com/sun/star/chart2/data/XDataSource.hpp>
71 #include <com/sun/star/chart2/data/XDataProvider.hpp>
72 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
73 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
74 #include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
75 #include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
76 #include <com/sun/star/chart2/AxisType.hpp>
78 #include <com/sun/star/beans/XPropertySet.hpp>
79 #include <com/sun/star/container/XNameAccess.hpp>
80 #include <com/sun/star/drawing/XShape.hpp>
81 #include <com/sun/star/drawing/XShapes.hpp>
82 #include <com/sun/star/drawing/FillStyle.hpp>
83 #include <com/sun/star/drawing/LineStyle.hpp>
84 #include <com/sun/star/awt/XBitmap.hpp>
85 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
86 #include <com/sun/star/lang/XServiceName.hpp>
88 #include <com/sun/star/table/CellAddress.hpp>
89 #include <com/sun/star/sheet/XFormulaParser.hpp>
90 #include <com/sun/star/sheet/FormulaToken.hpp>
91 #include <com/sun/star/sheet/AddressConvention.hpp>
93 #include <com/sun/star/container/XNamed.hpp>
94 #include <com/sun/star/embed/XVisualObject.hpp>
95 #include <com/sun/star/embed/Aspects.hpp>
97 #include <comphelper/processfactory.hxx>
98 #include <comphelper/random.hxx>
100 #include <xmloff/SchXMLSeriesHelper.hxx>
101 #include "ColorPropertySet.hxx"
103 #include <svl/numformat.hxx>
104 #include <svl/numuno.hxx>
105 #include <comphelper/diagnose_ex.hxx>
106 #include <sal/log.hxx>
109 #include <unordered_set>
111 #include <frozen/bits/defines.h>
112 #include <frozen/bits/elsa_std.h>
113 #include <frozen/unordered_map.h>
115 #include <o3tl/temporary.hxx>
116 #include <o3tl/sorted_vector.hxx>
119 using namespace css::uno
;
120 using namespace css::drawing
;
121 using namespace ::oox::core
;
122 using css::beans::PropertyValue
;
123 using css::beans::XPropertySet
;
124 using css::container::XNamed
;
125 using css::table::CellAddress
;
126 using css::sheet::XFormulaParser
;
127 using ::oox::core::XmlFilterBase
;
128 using ::sax_fastparser::FSHelperPtr
;
130 namespace cssc
= css::chart
;
132 namespace oox::drawingml
{
136 bool isPrimaryAxes(sal_Int32 nIndex
)
138 assert(nIndex
== 0 || nIndex
== 1);
142 class lcl_MatchesRole
145 explicit lcl_MatchesRole( OUString aRole
) :
146 m_aRole(std::move( aRole
))
149 bool operator () ( const Reference
< chart2::data::XLabeledDataSequence
> & xSeq
) const
153 Reference
< beans::XPropertySet
> xProp( xSeq
->getValues(), uno::UNO_QUERY
);
156 return ( xProp
.is() &&
157 (xProp
->getPropertyValue( "Role" ) >>= aRole
) &&
167 static Reference
< chart2::data::XLabeledDataSequence
> lcl_getCategories( const Reference
< chart2::XDiagram
> & xDiagram
, bool& bHasDateCategories
)
169 bHasDateCategories
= false;
170 Reference
< chart2::data::XLabeledDataSequence
> xResult
;
173 Reference
< chart2::XCoordinateSystemContainer
> xCooSysCnt(
174 xDiagram
, uno::UNO_QUERY_THROW
);
175 const Sequence
< Reference
< chart2::XCoordinateSystem
> > aCooSysSeq(
176 xCooSysCnt
->getCoordinateSystems());
177 for( const auto& xCooSys
: aCooSysSeq
)
179 OSL_ASSERT( xCooSys
.is());
180 for( sal_Int32 nN
= xCooSys
->getDimension(); nN
--; )
182 const sal_Int32 nMaxAxisIndex
= xCooSys
->getMaximumAxisIndexByDimension(nN
);
183 for(sal_Int32 nI
=0; nI
<=nMaxAxisIndex
; ++nI
)
185 Reference
< chart2::XAxis
> xAxis
= xCooSys
->getAxisByDimension( nN
, nI
);
186 OSL_ASSERT( xAxis
.is());
189 chart2::ScaleData aScaleData
= xAxis
->getScaleData();
190 if( aScaleData
.Categories
.is())
192 bHasDateCategories
= aScaleData
.AxisType
== chart2::AxisType::DATE
;
193 xResult
.set( aScaleData
.Categories
);
201 catch( const uno::Exception
& )
203 DBG_UNHANDLED_EXCEPTION("oox");
209 static Reference
< chart2::data::XLabeledDataSequence
>
210 lcl_getDataSequenceByRole(
211 const Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > & aLabeledSeq
,
212 const OUString
& rRole
)
214 Reference
< chart2::data::XLabeledDataSequence
> aNoResult
;
216 const Reference
< chart2::data::XLabeledDataSequence
> * pBegin
= aLabeledSeq
.getConstArray();
217 const Reference
< chart2::data::XLabeledDataSequence
> * pEnd
= pBegin
+ aLabeledSeq
.getLength();
218 const Reference
< chart2::data::XLabeledDataSequence
> * pMatch
=
219 ::std::find_if( pBegin
, pEnd
, lcl_MatchesRole( rRole
));
227 static bool lcl_hasCategoryLabels( const Reference
< chart2::XChartDocument
>& xChartDoc
)
229 //categories are always the first sequence
230 Reference
< chart2::XDiagram
> xDiagram( xChartDoc
->getFirstDiagram());
231 bool bDateCategories
;
232 Reference
< chart2::data::XLabeledDataSequence
> xCategories( lcl_getCategories( xDiagram
, bDateCategories
) );
233 return xCategories
.is();
236 static bool lcl_isCategoryAxisShifted( const Reference
< chart2::XDiagram
>& xDiagram
)
238 bool bCategoryPositionShifted
= false;
241 Reference
< chart2::XCoordinateSystemContainer
> xCooSysCnt(
242 xDiagram
, uno::UNO_QUERY_THROW
);
243 const Sequence
< Reference
< chart2::XCoordinateSystem
> > aCooSysSeq(
244 xCooSysCnt
->getCoordinateSystems());
245 for (const auto& xCooSys
: aCooSysSeq
)
247 OSL_ASSERT(xCooSys
.is());
248 if( 0 < xCooSys
->getDimension() && 0 <= xCooSys
->getMaximumAxisIndexByDimension(0) )
250 Reference
< chart2::XAxis
> xAxis
= xCooSys
->getAxisByDimension(0, 0);
251 OSL_ASSERT(xAxis
.is());
254 chart2::ScaleData aScaleData
= xAxis
->getScaleData();
255 bCategoryPositionShifted
= aScaleData
.ShiftedCategoryPosition
;
261 catch (const uno::Exception
&)
263 DBG_UNHANDLED_EXCEPTION("oox");
266 return bCategoryPositionShifted
;
269 static sal_Int32
lcl_getCategoryAxisType( const Reference
< chart2::XDiagram
>& xDiagram
, sal_Int32 nDimensionIndex
, sal_Int32 nAxisIndex
)
271 sal_Int32 nAxisType
= -1;
274 Reference
< chart2::XCoordinateSystemContainer
> xCooSysCnt(
275 xDiagram
, uno::UNO_QUERY_THROW
);
276 const Sequence
< Reference
< chart2::XCoordinateSystem
> > aCooSysSeq(
277 xCooSysCnt
->getCoordinateSystems());
278 for( const auto& xCooSys
: aCooSysSeq
)
280 OSL_ASSERT(xCooSys
.is());
281 if( nDimensionIndex
< xCooSys
->getDimension() && nAxisIndex
<= xCooSys
->getMaximumAxisIndexByDimension(nDimensionIndex
) )
283 Reference
< chart2::XAxis
> xAxis
= xCooSys
->getAxisByDimension(nDimensionIndex
, nAxisIndex
);
284 OSL_ASSERT(xAxis
.is());
287 chart2::ScaleData aScaleData
= xAxis
->getScaleData();
288 nAxisType
= aScaleData
.AxisType
;
294 catch (const uno::Exception
&)
296 DBG_UNHANDLED_EXCEPTION("oox");
302 static OUString
lclGetTimeUnitToken( sal_Int32 nTimeUnit
)
306 case cssc::TimeUnit::DAY
: return "days";
307 case cssc::TimeUnit::MONTH
: return "months";
308 case cssc::TimeUnit::YEAR
: return "years";
309 default: OSL_ENSURE(false, "lclGetTimeUnitToken - unexpected time unit");
314 static cssc::TimeIncrement
lcl_getDateTimeIncrement( const Reference
< chart2::XDiagram
>& xDiagram
, sal_Int32 nAxisIndex
)
316 cssc::TimeIncrement aTimeIncrement
;
319 Reference
< chart2::XCoordinateSystemContainer
> xCooSysCnt(
320 xDiagram
, uno::UNO_QUERY_THROW
);
321 const Sequence
< Reference
< chart2::XCoordinateSystem
> > aCooSysSeq(
322 xCooSysCnt
->getCoordinateSystems());
323 for( const auto& xCooSys
: aCooSysSeq
)
325 OSL_ASSERT(xCooSys
.is());
326 if( 0 < xCooSys
->getDimension() && nAxisIndex
<= xCooSys
->getMaximumAxisIndexByDimension(0) )
328 Reference
< chart2::XAxis
> xAxis
= xCooSys
->getAxisByDimension(0, nAxisIndex
);
329 OSL_ASSERT(xAxis
.is());
332 chart2::ScaleData aScaleData
= xAxis
->getScaleData();
333 aTimeIncrement
= aScaleData
.TimeIncrement
;
339 catch (const uno::Exception
&)
341 DBG_UNHANDLED_EXCEPTION("oox");
344 return aTimeIncrement
;
347 static bool lcl_isSeriesAttachedToFirstAxis(
348 const Reference
< chart2::XDataSeries
> & xDataSeries
)
354 sal_Int32 nAxisIndex
= 0;
355 Reference
< beans::XPropertySet
> xProp( xDataSeries
, uno::UNO_QUERY_THROW
);
356 xProp
->getPropertyValue("AttachedAxisIndex") >>= nAxisIndex
;
357 bResult
= (0==nAxisIndex
);
359 catch( const uno::Exception
& )
361 DBG_UNHANDLED_EXCEPTION("oox");
367 static OUString
lcl_flattenStringSequence( const Sequence
< OUString
> & rSequence
)
369 OUStringBuffer aResult
;
370 bool bPrecedeWithSpace
= false;
371 for( const auto& rString
: rSequence
)
373 if( !rString
.isEmpty())
375 if( bPrecedeWithSpace
)
376 aResult
.append( ' ' );
377 aResult
.append( rString
);
378 bPrecedeWithSpace
= true;
381 return aResult
.makeStringAndClear();
384 static Sequence
< OUString
> lcl_getLabelSequence( const Reference
< chart2::data::XDataSequence
> & xLabelSeq
)
386 Sequence
< OUString
> aLabels
;
388 uno::Reference
< chart2::data::XTextualDataSequence
> xTextualDataSequence( xLabelSeq
, uno::UNO_QUERY
);
389 if( xTextualDataSequence
.is())
391 aLabels
= xTextualDataSequence
->getTextualData();
393 else if( xLabelSeq
.is())
395 const Sequence
< uno::Any
> aAnies( xLabelSeq
->getData());
396 aLabels
.realloc( aAnies
.getLength());
397 auto pLabels
= aLabels
.getArray();
398 for( sal_Int32 i
=0; i
<aAnies
.getLength(); ++i
)
399 aAnies
[i
] >>= pLabels
[i
];
405 static void lcl_fillCategoriesIntoStringVector(
406 const Reference
< chart2::data::XDataSequence
> & xCategories
,
407 ::std::vector
< OUString
> & rOutCategories
)
409 OSL_ASSERT( xCategories
.is());
410 if( !xCategories
.is())
412 Reference
< chart2::data::XTextualDataSequence
> xTextualDataSequence( xCategories
, uno::UNO_QUERY
);
413 if( xTextualDataSequence
.is())
415 rOutCategories
.clear();
416 const Sequence
< OUString
> aTextData( xTextualDataSequence
->getTextualData());
417 rOutCategories
.insert( rOutCategories
.end(), aTextData
.begin(), aTextData
.end() );
421 Sequence
< uno::Any
> aAnies( xCategories
->getData());
422 rOutCategories
.resize( aAnies
.getLength());
423 for( sal_Int32 i
=0; i
<aAnies
.getLength(); ++i
)
424 aAnies
[i
] >>= rOutCategories
[i
];
428 static ::std::vector
< double > lcl_getAllValuesFromSequence( const Reference
< chart2::data::XDataSequence
> & xSeq
)
430 ::std::vector
< double > aResult
;
432 Reference
< chart2::data::XNumericalDataSequence
> xNumSeq( xSeq
, uno::UNO_QUERY
);
435 const Sequence
< double > aValues( xNumSeq
->getNumericalData());
436 aResult
.insert( aResult
.end(), aValues
.begin(), aValues
.end() );
440 Sequence
< uno::Any
> aAnies( xSeq
->getData());
441 aResult
.resize( aAnies
.getLength(), std::numeric_limits
<double>::quiet_NaN() );
442 for( sal_Int32 i
=0; i
<aAnies
.getLength(); ++i
)
443 aAnies
[i
] >>= aResult
[i
];
451 constexpr auto constChartTypeMap
= frozen::make_unordered_map
<std::u16string_view
, chart::TypeId
>(
453 { u
"com.sun.star.chart.BarDiagram", chart::TYPEID_BAR
},
454 { u
"com.sun.star.chart2.ColumnChartType", chart::TYPEID_BAR
},
455 { u
"com.sun.star.chart.AreaDiagram", chart::TYPEID_AREA
},
456 { u
"com.sun.star.chart2.AreaChartType", chart::TYPEID_AREA
},
457 { u
"com.sun.star.chart.LineDiagram", chart::TYPEID_LINE
},
458 { u
"com.sun.star.chart2.LineChartType", chart::TYPEID_LINE
},
459 { u
"com.sun.star.chart.PieDiagram", chart::TYPEID_PIE
},
460 { u
"com.sun.star.chart2.PieChartType", chart::TYPEID_PIE
},
461 { u
"com.sun.star.chart.DonutDiagram", chart::TYPEID_DOUGHNUT
},
462 { u
"com.sun.star.chart2.DonutChartType", chart::TYPEID_DOUGHNUT
},
463 { u
"com.sun.star.chart.XYDiagram", chart::TYPEID_SCATTER
},
464 { u
"com.sun.star.chart2.ScatterChartType", chart::TYPEID_SCATTER
},
465 { u
"com.sun.star.chart.NetDiagram", chart::TYPEID_RADARLINE
},
466 { u
"com.sun.star.chart2.NetChartType", chart::TYPEID_RADARLINE
},
467 { u
"com.sun.star.chart.FilledNetDiagram", chart::TYPEID_RADARAREA
},
468 { u
"com.sun.star.chart2.FilledNetChartType", chart::TYPEID_RADARAREA
},
469 { u
"com.sun.star.chart.StockDiagram", chart::TYPEID_STOCK
},
470 { u
"com.sun.star.chart2.CandleStickChartType", chart::TYPEID_STOCK
},
471 { u
"com.sun.star.chart.BubbleDiagram", chart::TYPEID_BUBBLE
},
472 { u
"com.sun.star.chart2.BubbleChartType", chart::TYPEID_BUBBLE
},
475 } // end anonymous namespace
477 static sal_Int32
lcl_getChartType(std::u16string_view sChartType
)
479 auto aIterator
= constChartTypeMap
.find(sChartType
);
480 if (aIterator
== constChartTypeMap
.end())
481 return chart::TYPEID_UNKNOWN
;
482 return aIterator
->second
;
485 static sal_Int32
lcl_generateRandomValue()
487 return comphelper::rng::uniform_int_distribution(0, 100000000-1);
490 bool DataLabelsRange::empty() const
492 return maLabels
.empty();
495 size_t DataLabelsRange::count() const
497 return maLabels
.size();
500 bool DataLabelsRange::hasLabel(sal_Int32 nIndex
) const
502 return maLabels
.find(nIndex
) != maLabels
.end();
505 const OUString
& DataLabelsRange::getRange() const
510 void DataLabelsRange::setRange(const OUString
& rRange
)
515 void DataLabelsRange::setLabel(sal_Int32 nIndex
, const OUString
& rText
)
517 maLabels
.emplace(nIndex
, rText
);
520 DataLabelsRange::LabelsRangeMap::const_iterator
DataLabelsRange::begin() const
522 return maLabels
.begin();
525 DataLabelsRange::LabelsRangeMap::const_iterator
DataLabelsRange::end() const
527 return maLabels
.end();
530 ChartExport::ChartExport( sal_Int32 nXmlNamespace
, FSHelperPtr pFS
, Reference
< frame::XModel
> const & xModel
, XmlFilterBase
* pFB
, DocumentType eDocumentType
)
531 : DrawingML( std::move(pFS
), pFB
, eDocumentType
)
532 , mnXmlNamespace( nXmlNamespace
)
534 , mxChartModel( xModel
)
535 , mpURLTransformer(std::make_shared
<URLTransformer
>())
536 , mbHasCategoryLabels( false )
537 , mbHasZAxis( false )
538 , mbIs3DChart( false )
541 , mbHasDateCategories(false)
545 void ChartExport::SetURLTranslator(const std::shared_ptr
<URLTransformer
>& pTransformer
)
547 mpURLTransformer
= pTransformer
;
550 sal_Int32
ChartExport::getChartType( )
552 OUString sChartType
= mxDiagram
->getDiagramType();
553 return lcl_getChartType( sChartType
);
558 uno::Sequence
< beans::PropertyValue
> createArguments(
559 const OUString
& rRangeRepresentation
, bool bUseColumns
)
561 css::chart::ChartDataRowSource eRowSource
= css::chart::ChartDataRowSource_ROWS
;
563 eRowSource
= css::chart::ChartDataRowSource_COLUMNS
;
565 uno::Sequence
<beans::PropertyValue
> aArguments
{
566 { "DataRowSource", -1, uno::Any(eRowSource
), beans::PropertyState_DIRECT_VALUE
},
567 { "FirstCellAsLabel", -1, uno::Any(false), beans::PropertyState_DIRECT_VALUE
},
568 { "HasCategories", -1, uno::Any(false), beans::PropertyState_DIRECT_VALUE
},
569 { "CellRangeRepresentation", -1, uno::Any(rRangeRepresentation
),
570 beans::PropertyState_DIRECT_VALUE
}
576 Reference
<chart2::XDataSeries
> getPrimaryDataSeries(const Reference
<chart2::XChartType
>& xChartType
)
578 Reference
< chart2::XDataSeriesContainer
> xDSCnt(xChartType
, uno::UNO_QUERY_THROW
);
580 // export dataseries for current chart-type
581 const Sequence
< Reference
< chart2::XDataSeries
> > aSeriesSeq(xDSCnt
->getDataSeries());
582 for (const auto& rSeries
: aSeriesSeq
)
584 Reference
<chart2::XDataSeries
> xSource(rSeries
, uno::UNO_QUERY
);
589 return Reference
<chart2::XDataSeries
>();
594 Sequence
< Sequence
< OUString
> > ChartExport::getSplitCategoriesList( const OUString
& rRange
)
596 Reference
< chart2::XChartDocument
> xChartDoc(getModel(), uno::UNO_QUERY
);
597 OSL_ASSERT(xChartDoc
.is());
600 Reference
< chart2::data::XDataProvider
> xDataProvider(xChartDoc
->getDataProvider());
601 OSL_ENSURE(xDataProvider
.is(), "No DataProvider");
602 if (xDataProvider
.is())
604 //detect whether the first series is a row or a column
605 bool bSeriesUsesColumns
= true;
606 Reference
< chart2::XDiagram
> xDiagram(xChartDoc
->getFirstDiagram());
609 Reference
< chart2::XCoordinateSystemContainer
> xCooSysCnt(xDiagram
, uno::UNO_QUERY_THROW
);
610 const Sequence
< Reference
< chart2::XCoordinateSystem
> > aCooSysSeq(xCooSysCnt
->getCoordinateSystems());
611 for (const auto& rCooSys
: aCooSysSeq
)
613 const Reference
< chart2::XChartTypeContainer
> xCTCnt(rCooSys
, uno::UNO_QUERY_THROW
);
614 const Sequence
< Reference
< chart2::XChartType
> > aChartTypeSeq(xCTCnt
->getChartTypes());
615 for (const auto& rChartType
: aChartTypeSeq
)
617 Reference
< chart2::XDataSeries
> xDataSeries
= getPrimaryDataSeries(rChartType
);
618 if (xDataSeries
.is())
620 uno::Reference
< chart2::data::XDataSource
> xSeriesSource(xDataSeries
, uno::UNO_QUERY
);
621 const uno::Sequence
< beans::PropertyValue
> rArguments
= xDataProvider
->detectArguments(xSeriesSource
);
622 for (const beans::PropertyValue
& rProperty
: rArguments
)
624 if (rProperty
.Name
== "DataRowSource")
626 css::chart::ChartDataRowSource eRowSource
;
627 if (rProperty
.Value
>>= eRowSource
)
629 bSeriesUsesColumns
= (eRowSource
== css::chart::ChartDataRowSource_COLUMNS
);
638 catch (const uno::Exception
&)
640 DBG_UNHANDLED_EXCEPTION("chart2");
642 // detect we have an inner data table or not
643 if (xChartDoc
->hasInternalDataProvider() && rRange
== "categories")
647 css::uno::Reference
< css::chart2::XAnyDescriptionAccess
> xDataAccess(xChartDoc
->getDataProvider(), uno::UNO_QUERY
);
648 const Sequence
< Sequence
< uno::Any
> >aAnyCategories(bSeriesUsesColumns
? xDataAccess
->getAnyRowDescriptions() : xDataAccess
->getAnyColumnDescriptions());
649 auto pMax
= std::max_element(aAnyCategories
.begin(), aAnyCategories
.end(),
650 [](const Sequence
<uno::Any
>& a
, const Sequence
<uno::Any
>& b
) {
651 return a
.getLength() < b
.getLength(); });
654 if (pMax
!= aAnyCategories
.end() && pMax
->getLength() > 1)
656 sal_Int32 nLevelCount
= pMax
->getLength();
657 //we have complex categories
658 //sort the categories name
659 Sequence
<Sequence
<OUString
>>aFinalSplitSource(nLevelCount
);
660 auto pFinalSplitSource
= aFinalSplitSource
.getArray();
661 for (sal_Int32 i
= 0; i
< nLevelCount
; i
++)
663 sal_Int32 nElemLabel
= 0;
664 pFinalSplitSource
[nLevelCount
- i
- 1].realloc(aAnyCategories
.getLength());
665 auto pSeq
= pFinalSplitSource
[nLevelCount
- i
- 1].getArray();
666 for (auto const& elemLabel
: aAnyCategories
)
668 // make sure elemLabel[i] exists!
669 if (elemLabel
.getLength() > i
)
671 pSeq
[nElemLabel
] = elemLabel
[i
].get
<OUString
>();
676 return aFinalSplitSource
;
679 catch (const uno::Exception
&)
681 DBG_UNHANDLED_EXCEPTION("oox");
688 uno::Reference
< chart2::data::XDataSource
> xCategoriesSource(xDataProvider
->createDataSource(
689 createArguments(rRange
, bSeriesUsesColumns
)));
691 if (xCategoriesSource
.is())
693 const Sequence
< Reference
< chart2::data::XLabeledDataSequence
>> aCategories
= xCategoriesSource
->getDataSequences();
694 if (aCategories
.getLength() > 1)
696 //we have complex categories
697 //sort the categories name
698 Sequence
<Sequence
<OUString
>> aFinalSplitSource(aCategories
.getLength());
699 std::transform(aCategories
.begin(), aCategories
.end(),
700 std::reverse_iterator(asNonConstRange(aFinalSplitSource
).end()),
701 [](const Reference
<chart2::data::XLabeledDataSequence
>& xCat
) {
702 return lcl_getLabelSequence(xCat
->getValues()); });
703 return aFinalSplitSource
;
707 catch (const uno::Exception
&)
709 DBG_UNHANDLED_EXCEPTION("oox");
715 return Sequence
< Sequence
< OUString
>>(0);
718 OUString
ChartExport::parseFormula( const OUString
& rRange
)
721 Reference
< XFormulaParser
> xParser
;
722 uno::Reference
< lang::XMultiServiceFactory
> xSF
= GetFB()->getModelFactory();
727 xParser
.set( xSF
->createInstance("com.sun.star.sheet.FormulaParser"), UNO_QUERY
);
734 SAL_WARN_IF(!xParser
.is(), "oox", "creating formula parser failed");
738 Reference
< XPropertySet
> xParserProps( xParser
, uno::UNO_QUERY
);
739 // rRange is the result of a
740 // css::chart2::data::XDataSequence::getSourceRangeRepresentation()
741 // call that returns the range in the document's current UI notation.
742 // Creating a FormulaParser defaults to the same notation, for
743 // parseFormula() do not attempt to override the FormulaConvention
744 // property with css::sheet::AddressConvention::OOO or some such.
745 /* TODO: it would be much better to introduce a
746 * getSourceRangeRepresentation(css::sheet::AddressConvention) to
747 * return the ranges in a specific convention than converting them with
748 * the overhead of creating an XFormulaParser for each... */
749 uno::Sequence
<sheet::FormulaToken
> aTokens
= xParser
->parseFormula( rRange
, CellAddress( 0, 0, 0 ) );
750 if( xParserProps
.is() )
752 xParserProps
->setPropertyValue("FormulaConvention", uno::Any(css::sheet::AddressConvention::XL_OOX
) );
753 // For referencing named ranges correctly with special excel chart syntax.
754 xParserProps
->setPropertyValue("RefConventionChartOOXML", uno::Any(true) );
756 aResult
= xParser
->printFormula( aTokens
, CellAddress( 0, 0, 0 ) );
760 //FIXME: currently just using simple converter, e.g $Sheet1.$A$1:$C$1 -> Sheet1!$A$1:$C$1
761 OUString
aRange( rRange
);
762 if( aRange
.startsWith("$") )
763 aRange
= aRange
.copy(1);
764 aRange
= aRange
.replaceAll(".$", "!$" );
771 void ChartExport::WriteChartObj( const Reference
< XShape
>& xShape
, sal_Int32 nID
, sal_Int32 nChartCount
)
773 FSHelperPtr pFS
= GetFS();
775 Reference
< XPropertySet
> xShapeProps( xShape
, UNO_QUERY
);
777 pFS
->startElementNS(mnXmlNamespace
, XML_graphicFrame
);
779 pFS
->startElementNS(mnXmlNamespace
, XML_nvGraphicFramePr
);
781 // TODO: get the correct chart name chart id
782 OUString sName
= "Object 1";
783 Reference
< XNamed
> xNamed( xShape
, UNO_QUERY
);
785 sName
= xNamed
->getName();
787 pFS
->startElementNS( mnXmlNamespace
, XML_cNvPr
,
788 XML_id
, OString::number(nID
),
792 if ( GetProperty( xShapeProps
, "URL" ) )
794 if( !sURL
.isEmpty() )
796 OUString sRelId
= mpFB
->addRelation( mpFS
->getOutputStream(),
797 oox::getRelationship(Relationship::HYPERLINK
),
798 mpURLTransformer
->getTransformedString(sURL
),
799 mpURLTransformer
->isExternalURL(sURL
));
801 mpFS
->singleElementNS(XML_a
, XML_hlinkClick
, FSNS(XML_r
, XML_id
), sRelId
);
803 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
805 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvGraphicFramePr
);
807 if( GetDocumentType() == DOCUMENT_PPTX
)
808 pFS
->singleElementNS(mnXmlNamespace
, XML_nvPr
);
809 pFS
->endElementNS( mnXmlNamespace
, XML_nvGraphicFramePr
);
811 // visual chart properties
812 WriteShapeTransformation( xShape
, mnXmlNamespace
);
814 // writer chart object
815 pFS
->startElement(FSNS(XML_a
, XML_graphic
));
816 pFS
->startElement( FSNS( XML_a
, XML_graphicData
),
817 XML_uri
, "http://schemas.openxmlformats.org/drawingml/2006/chart" );
819 const char* sFullPath
= nullptr;
820 const char* sRelativePath
= nullptr;
821 switch( GetDocumentType() )
825 sFullPath
= "word/charts/chart";
826 sRelativePath
= "charts/chart";
831 sFullPath
= "ppt/charts/chart";
832 sRelativePath
= "../charts/chart";
837 sFullPath
= "xl/charts/chart";
838 sRelativePath
= "../charts/chart";
843 sFullPath
= "charts/chart";
844 sRelativePath
= "charts/chart";
848 OUString sFullStream
= OUStringBuffer()
849 .appendAscii(sFullPath
)
850 .append(OUString::number(nChartCount
) + ".xml")
851 .makeStringAndClear();
852 OUString sRelativeStream
= OUStringBuffer()
853 .appendAscii(sRelativePath
)
854 .append(OUString::number(nChartCount
) + ".xml" )
855 .makeStringAndClear();
856 FSHelperPtr pChart
= CreateOutputStream(
859 pFS
->getOutputStream(),
860 "application/vnd.openxmlformats-officedocument.drawingml.chart+xml",
861 oox::getRelationship(Relationship::CHART
),
864 XmlFilterBase
* pFB
= GetFB();
865 pFS
->singleElement( FSNS( XML_c
, XML_chart
),
866 FSNS(XML_xmlns
, XML_c
), pFB
->getNamespaceURL(OOX_NS(dmlChart
)),
867 FSNS(XML_xmlns
, XML_r
), pFB
->getNamespaceURL(OOX_NS(officeRel
)),
868 FSNS(XML_r
, XML_id
), sId
);
870 pFS
->endElement( FSNS( XML_a
, XML_graphicData
) );
871 pFS
->endElement( FSNS( XML_a
, XML_graphic
) );
872 pFS
->endElementNS( mnXmlNamespace
, XML_graphicFrame
);
877 pChart
->endDocument();
880 void ChartExport::InitRangeSegmentationProperties( const Reference
< chart2::XChartDocument
> & xChartDoc
)
887 Reference
< chart2::data::XDataProvider
> xDataProvider( xChartDoc
->getDataProvider() );
888 OSL_ENSURE( xDataProvider
.is(), "No DataProvider" );
889 if( xDataProvider
.is())
891 mbHasCategoryLabels
= lcl_hasCategoryLabels( xChartDoc
);
894 catch( const uno::Exception
& )
896 DBG_UNHANDLED_EXCEPTION("oox");
900 void ChartExport::ExportContent()
902 Reference
< chart2::XChartDocument
> xChartDoc( getModel(), uno::UNO_QUERY
);
903 OSL_ASSERT( xChartDoc
.is() );
904 if( !xChartDoc
.is() )
906 InitRangeSegmentationProperties( xChartDoc
);
907 // TODO: export chart
911 void ChartExport::ExportContent_()
913 Reference
< css::chart::XChartDocument
> xChartDoc( getModel(), uno::UNO_QUERY
);
916 // determine if data comes from the outside
917 bool bIncludeTable
= true;
919 Reference
< chart2::XChartDocument
> xNewDoc( xChartDoc
, uno::UNO_QUERY
);
922 // check if we have own data. If so we must not export the complete
923 // range string, as this is our only indicator for having own or
924 // external data. @todo: fix this in the file format!
925 Reference
< lang::XServiceInfo
> xDPServiceInfo( xNewDoc
->getDataProvider(), uno::UNO_QUERY
);
926 if( ! (xDPServiceInfo
.is() && xDPServiceInfo
->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" ))
928 bIncludeTable
= false;
931 exportChartSpace( xChartDoc
, bIncludeTable
);
935 OSL_FAIL( "Couldn't export chart due to wrong XModel" );
939 void ChartExport::exportChartSpace( const Reference
< css::chart::XChartDocument
>& xChartDoc
,
942 FSHelperPtr pFS
= GetFS();
943 XmlFilterBase
* pFB
= GetFB();
944 pFS
->startElement( FSNS( XML_c
, XML_chartSpace
),
945 FSNS( XML_xmlns
, XML_c
), pFB
->getNamespaceURL(OOX_NS(dmlChart
)),
946 FSNS( XML_xmlns
, XML_a
), pFB
->getNamespaceURL(OOX_NS(dml
)),
947 FSNS( XML_xmlns
, XML_r
), pFB
->getNamespaceURL(OOX_NS(officeRel
)));
948 // TODO: get the correct editing language
949 pFS
->singleElement(FSNS(XML_c
, XML_lang
), XML_val
, "en-US");
951 pFS
->singleElement(FSNS(XML_c
, XML_roundedCorners
), XML_val
, "0");
955 // TODO:external data
958 exportChart(xChartDoc
);
960 // TODO: printSettings
962 // TODO: text properties
963 // TODO: shape properties
964 Reference
< XPropertySet
> xPropSet
= xChartDoc
->getArea();
966 exportShapeProps( xPropSet
);
969 exportExternalData(xChartDoc
);
971 // export additional shapes in chart
972 exportAdditionalShapes(xChartDoc
);
974 pFS
->endElement( FSNS( XML_c
, XML_chartSpace
) );
977 void ChartExport::exportExternalData( const Reference
< css::chart::XChartDocument
>& xChartDoc
)
979 // Embedded external data is grab bagged for docx file hence adding export part of
980 // external data for docx files only.
981 if(GetDocumentType() != DOCUMENT_DOCX
)
984 OUString externalDataPath
;
985 Reference
< beans::XPropertySet
> xDocPropSet( xChartDoc
->getDiagram(), uno::UNO_QUERY
);
986 if( xDocPropSet
.is())
990 Any
aAny( xDocPropSet
->getPropertyValue( "ExternalData" ));
991 aAny
>>= externalDataPath
;
993 catch( beans::UnknownPropertyException
& )
995 SAL_WARN("oox", "Required property not found in ChartDocument");
998 if(externalDataPath
.isEmpty())
1001 // Here adding external data entry to relationship.
1002 OUString relationPath
= externalDataPath
;
1003 // Converting absolute path to relative path.
1004 if( externalDataPath
[ 0 ] != '.' && externalDataPath
[ 1 ] != '.')
1006 sal_Int32 nSepPos
= externalDataPath
.indexOf( '/', 0 );
1009 relationPath
= relationPath
.copy( nSepPos
, ::std::max
< sal_Int32
>( externalDataPath
.getLength(), 0 ) - nSepPos
);
1010 relationPath
= ".." + relationPath
;
1013 FSHelperPtr pFS
= GetFS();
1014 OUString type
= oox::getRelationship(Relationship::PACKAGE
);
1015 if (relationPath
.endsWith(".bin"))
1016 type
= oox::getRelationship(Relationship::OLEOBJECT
);
1018 OUString sRelId
= GetFB()->addRelation(pFS
->getOutputStream(),
1021 pFS
->singleElementNS(XML_c
, XML_externalData
, FSNS(XML_r
, XML_id
), sRelId
);
1024 void ChartExport::exportAdditionalShapes( const Reference
< css::chart::XChartDocument
>& xChartDoc
)
1026 Reference
< beans::XPropertySet
> xDocPropSet(xChartDoc
, uno::UNO_QUERY
);
1027 if (!xDocPropSet
.is())
1030 css::uno::Reference
< css::drawing::XShapes
> mxAdditionalShapes
;
1031 // get a sequence of non-chart shapes
1034 Any aShapesAny
= xDocPropSet
->getPropertyValue("AdditionalShapes");
1035 if( (aShapesAny
>>= mxAdditionalShapes
) && mxAdditionalShapes
.is() )
1038 const char* sFullPath
= nullptr;
1039 const char* sRelativePath
= nullptr;
1040 sal_Int32 nDrawing
= getNewDrawingUniqueId();
1042 switch (GetDocumentType())
1046 sFullPath
= "word/drawings/drawing";
1047 sRelativePath
= "../drawings/drawing";
1052 sFullPath
= "ppt/drawings/drawing";
1053 sRelativePath
= "../drawings/drawing";
1058 sFullPath
= "xl/drawings/drawing";
1059 sRelativePath
= "../drawings/drawing";
1064 sFullPath
= "drawings/drawing";
1065 sRelativePath
= "drawings/drawing";
1069 OUString sFullStream
= OUStringBuffer()
1070 .appendAscii(sFullPath
)
1071 .append(OUString::number(nDrawing
) + ".xml")
1072 .makeStringAndClear();
1073 OUString sRelativeStream
= OUStringBuffer()
1074 .appendAscii(sRelativePath
)
1075 .append(OUString::number(nDrawing
) + ".xml")
1076 .makeStringAndClear();
1078 sax_fastparser::FSHelperPtr pDrawing
= CreateOutputStream(
1081 GetFS()->getOutputStream(),
1082 "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml",
1083 oox::getRelationship(Relationship::CHARTUSERSHAPES
),
1086 GetFS()->singleElementNS(XML_c
, XML_userShapes
, FSNS(XML_r
, XML_id
), sId
);
1088 XmlFilterBase
* pFB
= GetFB();
1089 pDrawing
->startElement(FSNS(XML_c
, XML_userShapes
),
1090 FSNS(XML_xmlns
, XML_cdr
), pFB
->getNamespaceURL(OOX_NS(dmlChartDr
)),
1091 FSNS(XML_xmlns
, XML_a
), pFB
->getNamespaceURL(OOX_NS(dml
)),
1092 FSNS(XML_xmlns
, XML_c
), pFB
->getNamespaceURL(OOX_NS(dmlChart
)),
1093 FSNS(XML_xmlns
, XML_r
), pFB
->getNamespaceURL(OOX_NS(officeRel
)));
1095 const sal_Int32
nShapeCount(mxAdditionalShapes
->getCount());
1096 for (sal_Int32 nShapeId
= 0; nShapeId
< nShapeCount
; nShapeId
++)
1098 Reference
< drawing::XShape
> xShape
;
1099 mxAdditionalShapes
->getByIndex(nShapeId
) >>= xShape
;
1100 SAL_WARN_IF(!xShape
.is(), "xmloff.chart", "Shape without an XShape?");
1104 // TODO: absSizeAnchor: we import both (absSizeAnchor and relSizeAnchor), but there is no essential difference between them.
1105 pDrawing
->startElement(FSNS(XML_cdr
, XML_relSizeAnchor
));
1106 uno::Reference
< beans::XPropertySet
> xShapeProperties(xShape
, uno::UNO_QUERY
);
1107 if( xShapeProperties
.is() )
1109 Reference
<embed::XVisualObject
> xVisObject(mxChartModel
, uno::UNO_QUERY
);
1110 awt::Size aPageSize
= xVisObject
->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT
);
1111 WriteFromTo( xShape
, aPageSize
, pDrawing
);
1113 ShapeExport
aExport(XML_cdr
, pDrawing
, nullptr, GetFB(), GetDocumentType(), nullptr, true);
1114 aExport
.WriteShape(xShape
);
1116 pDrawing
->endElement(FSNS(XML_cdr
, XML_relSizeAnchor
));
1118 pDrawing
->endElement(FSNS(XML_c
, XML_userShapes
));
1119 pDrawing
->endDocument();
1122 catch (const uno::Exception
&)
1124 TOOLS_INFO_EXCEPTION("xmloff.chart", "AdditionalShapes not found");
1128 void ChartExport::exportChart( const Reference
< css::chart::XChartDocument
>& xChartDoc
)
1130 Reference
< chart2::XChartDocument
> xNewDoc( xChartDoc
, uno::UNO_QUERY
);
1131 mxDiagram
.set( xChartDoc
->getDiagram() );
1133 mxNewDiagram
.set( xNewDoc
->getFirstDiagram());
1135 // get Properties of ChartDocument
1136 bool bHasMainTitle
= false;
1138 bool bHasLegend
= false;
1139 Reference
< beans::XPropertySet
> xDocPropSet( xChartDoc
, uno::UNO_QUERY
);
1140 if( xDocPropSet
.is())
1144 Any
aAny( xDocPropSet
->getPropertyValue("HasMainTitle"));
1145 aAny
>>= bHasMainTitle
;
1146 aAny
= xDocPropSet
->getPropertyValue("HasLegend");
1147 aAny
>>= bHasLegend
;
1149 catch( beans::UnknownPropertyException
& )
1151 SAL_WARN("oox", "Required property not found in ChartDocument");
1153 } // if( xDocPropSet.is())
1155 Reference
< beans::XPropertySet
> xPropSubTitle( xChartDoc
->getSubTitle(), UNO_QUERY
);
1156 if( xPropSubTitle
.is())
1160 xPropSubTitle
->getPropertyValue("String") >>= aSubTitle
;
1162 catch( beans::UnknownPropertyException
& )
1168 FSHelperPtr pFS
= GetFS();
1169 pFS
->startElement(FSNS(XML_c
, XML_chart
));
1174 exportTitle( xChartDoc
->getTitle(), !aSubTitle
.isEmpty() ? &aSubTitle
: nullptr );
1175 pFS
->singleElement(FSNS(XML_c
, XML_autoTitleDeleted
), XML_val
, "0");
1177 else if( !aSubTitle
.isEmpty() )
1179 exportTitle( xChartDoc
->getSubTitle(), nullptr );
1180 pFS
->singleElement(FSNS(XML_c
, XML_autoTitleDeleted
), XML_val
, "0");
1184 pFS
->singleElement(FSNS(XML_c
, XML_autoTitleDeleted
), XML_val
, "1");
1193 Reference
< beans::XPropertySet
> xFloor
= mxNewDiagram
->getFloor();
1196 pFS
->startElement(FSNS(XML_c
, XML_floor
));
1197 exportShapeProps( xFloor
);
1198 pFS
->endElement( FSNS( XML_c
, XML_floor
) );
1201 // LibreOffice doesn't distinguish between sideWall and backWall (both are using the same color).
1202 // It is controlled by the same Wall property.
1203 Reference
< beans::XPropertySet
> xWall
= mxNewDiagram
->getWall();
1207 pFS
->startElement(FSNS(XML_c
, XML_sideWall
));
1208 exportShapeProps( xWall
);
1209 pFS
->endElement( FSNS( XML_c
, XML_sideWall
) );
1212 pFS
->startElement(FSNS(XML_c
, XML_backWall
));
1213 exportShapeProps( xWall
);
1214 pFS
->endElement( FSNS( XML_c
, XML_backWall
) );
1219 exportPlotArea( xChartDoc
);
1222 exportLegend( xChartDoc
);
1224 uno::Reference
<beans::XPropertySet
> xDiagramPropSet(xChartDoc
->getDiagram(), uno::UNO_QUERY
);
1225 uno::Any aPlotVisOnly
= xDiagramPropSet
->getPropertyValue("IncludeHiddenCells");
1226 bool bIncludeHiddenCells
= false;
1227 aPlotVisOnly
>>= bIncludeHiddenCells
;
1228 pFS
->singleElement(FSNS(XML_c
, XML_plotVisOnly
), XML_val
, ToPsz10(!bIncludeHiddenCells
));
1230 exportMissingValueTreatment(Reference
<beans::XPropertySet
>(mxDiagram
, uno::UNO_QUERY
));
1232 pFS
->endElement( FSNS( XML_c
, XML_chart
) );
1235 void ChartExport::exportMissingValueTreatment(const uno::Reference
<beans::XPropertySet
>& xPropSet
)
1241 uno::Any aAny
= xPropSet
->getPropertyValue("MissingValueTreatment");
1242 if (!(aAny
>>= nVal
))
1245 const char* pVal
= nullptr;
1248 case cssc::MissingValueTreatment::LEAVE_GAP
:
1251 case cssc::MissingValueTreatment::USE_ZERO
:
1254 case cssc::MissingValueTreatment::CONTINUE
:
1258 SAL_WARN("oox", "unknown MissingValueTreatment value");
1262 FSHelperPtr pFS
= GetFS();
1263 pFS
->singleElement(FSNS(XML_c
, XML_dispBlanksAs
), XML_val
, pVal
);
1266 void ChartExport::exportLegend( const Reference
< css::chart::XChartDocument
>& xChartDoc
)
1268 FSHelperPtr pFS
= GetFS();
1269 pFS
->startElement(FSNS(XML_c
, XML_legend
));
1271 Reference
< beans::XPropertySet
> xProp( xChartDoc
->getLegend(), uno::UNO_QUERY
);
1275 css::chart::ChartLegendPosition aLegendPos
= css::chart::ChartLegendPosition_NONE
;
1278 Any
aAny( xProp
->getPropertyValue( "Alignment" ));
1279 aAny
>>= aLegendPos
;
1281 catch( beans::UnknownPropertyException
& )
1283 SAL_WARN("oox", "Property Align not found in ChartLegend");
1286 const char* strPos
= nullptr;
1287 switch( aLegendPos
)
1289 case css::chart::ChartLegendPosition_LEFT
:
1292 case css::chart::ChartLegendPosition_RIGHT
:
1295 case css::chart::ChartLegendPosition_TOP
:
1298 case css::chart::ChartLegendPosition_BOTTOM
:
1301 case css::chart::ChartLegendPosition_NONE
:
1302 case css::chart::ChartLegendPosition::ChartLegendPosition_MAKE_FIXED_SIZE
:
1307 if( strPos
!= nullptr )
1309 pFS
->singleElement(FSNS(XML_c
, XML_legendPos
), XML_val
, strPos
);
1313 Reference
<chart2::XCoordinateSystemContainer
> xCooSysContainer(mxNewDiagram
, UNO_QUERY_THROW
);
1314 const Sequence
<Reference
<chart2::XCoordinateSystem
>> xCooSysSequence(xCooSysContainer
->getCoordinateSystems());
1316 sal_Int32 nIndex
= 0;
1317 bool bShowLegendEntry
;
1318 for (const auto& rCooSys
: xCooSysSequence
)
1320 PropertySet
aCooSysProp(rCooSys
);
1321 bool bSwapXAndY
= aCooSysProp
.getBoolProperty(PROP_SwapXAndYAxis
);
1323 Reference
<chart2::XChartTypeContainer
> xChartTypeContainer(rCooSys
, UNO_QUERY_THROW
);
1324 const Sequence
<Reference
<chart2::XChartType
>> xChartTypeSequence(xChartTypeContainer
->getChartTypes());
1325 if (!xChartTypeSequence
.hasElements())
1328 for (const auto& rCT
: xChartTypeSequence
)
1330 Reference
<chart2::XDataSeriesContainer
> xDSCont(rCT
, UNO_QUERY
);
1334 OUString
aChartType(rCT
->getChartType());
1335 bool bIsPie
= lcl_getChartType(aChartType
) == chart::TYPEID_PIE
;
1338 PropertySet
xChartTypeProp(rCT
);
1339 bIsPie
= !xChartTypeProp
.getBoolProperty(PROP_UseRings
);
1341 const Sequence
<Reference
<chart2::XDataSeries
>> aDataSeriesSeq
= xDSCont
->getDataSeries();
1343 nIndex
+= aDataSeriesSeq
.getLength() - 1;
1344 for (const auto& rDataSeries
: aDataSeriesSeq
)
1346 PropertySet
aSeriesProp(rDataSeries
);
1347 bool bVaryColorsByPoint
= aSeriesProp
.getBoolProperty(PROP_VaryColorsByPoint
);
1348 if (bVaryColorsByPoint
|| bIsPie
)
1350 Sequence
<sal_Int32
> deletedLegendEntriesSeq
;
1351 aSeriesProp
.getProperty(deletedLegendEntriesSeq
, PROP_DeletedLegendEntries
);
1352 for (const auto& deletedLegendEntry
: deletedLegendEntriesSeq
)
1354 pFS
->startElement(FSNS(XML_c
, XML_legendEntry
));
1355 pFS
->singleElement(FSNS(XML_c
, XML_idx
), XML_val
,
1356 OString::number(nIndex
+ deletedLegendEntry
));
1357 pFS
->singleElement(FSNS(XML_c
, XML_delete
), XML_val
, "1");
1358 pFS
->endElement(FSNS(XML_c
, XML_legendEntry
));
1360 Reference
<chart2::data::XDataSource
> xDSrc(rDataSeries
, UNO_QUERY
);
1364 const Sequence
<Reference
<chart2::data::XLabeledDataSequence
>> aDataSeqs
= xDSrc
->getDataSequences();
1365 for (const auto& rDataSeq
: aDataSeqs
)
1367 Reference
<chart2::data::XDataSequence
> xValues
= rDataSeq
->getValues();
1371 sal_Int32 nDataSeqSize
= xValues
->getData().getLength();
1372 nIndex
+= nDataSeqSize
;
1377 bShowLegendEntry
= aSeriesProp
.getBoolProperty(PROP_ShowLegendEntry
);
1378 if (!bShowLegendEntry
)
1380 pFS
->startElement(FSNS(XML_c
, XML_legendEntry
));
1381 pFS
->singleElement(FSNS(XML_c
, XML_idx
), XML_val
,
1382 OString::number(nIndex
));
1383 pFS
->singleElement(FSNS(XML_c
, XML_delete
), XML_val
, "1");
1384 pFS
->endElement(FSNS(XML_c
, XML_legendEntry
));
1386 bSwapXAndY
? nIndex
-- : nIndex
++;
1390 nIndex
+= aDataSeriesSeq
.getLength() + 1;
1394 uno::Any aRelativePos
= xProp
->getPropertyValue("RelativePosition");
1395 if (aRelativePos
.hasValue())
1397 pFS
->startElement(FSNS(XML_c
, XML_layout
));
1398 pFS
->startElement(FSNS(XML_c
, XML_manualLayout
));
1400 pFS
->singleElement(FSNS(XML_c
, XML_xMode
), XML_val
, "edge");
1401 pFS
->singleElement(FSNS(XML_c
, XML_yMode
), XML_val
, "edge");
1402 chart2::RelativePosition aPos
= aRelativePos
.get
<chart2::RelativePosition
>();
1404 const double x
= aPos
.Primary
;
1405 const double y
= aPos
.Secondary
;
1407 pFS
->singleElement(FSNS(XML_c
, XML_x
), XML_val
, OString::number(x
));
1408 pFS
->singleElement(FSNS(XML_c
, XML_y
), XML_val
, OString::number(y
));
1410 uno::Any aRelativeSize
= xProp
->getPropertyValue("RelativeSize");
1411 if (aRelativeSize
.hasValue())
1413 chart2::RelativeSize aSize
= aRelativeSize
.get
<chart2::RelativeSize
>();
1415 const double w
= aSize
.Primary
;
1416 const double h
= aSize
.Secondary
;
1418 pFS
->singleElement(FSNS(XML_c
, XML_w
), XML_val
, OString::number(w
));
1420 pFS
->singleElement(FSNS(XML_c
, XML_h
), XML_val
, OString::number(h
));
1423 SAL_WARN_IF(aPos
.Anchor
!= css::drawing::Alignment_TOP_LEFT
, "oox", "unsupported anchor position");
1425 pFS
->endElement(FSNS(XML_c
, XML_manualLayout
));
1426 pFS
->endElement(FSNS(XML_c
, XML_layout
));
1429 if (strPos
!= nullptr)
1431 uno::Any aOverlay
= xProp
->getPropertyValue("Overlay");
1432 if(aOverlay
.get
<bool>())
1433 pFS
->singleElement(FSNS(XML_c
, XML_overlay
), XML_val
, "1");
1435 pFS
->singleElement(FSNS(XML_c
, XML_overlay
), XML_val
, "0");
1439 exportShapeProps( xProp
);
1441 // draw-chart:txPr text properties
1442 exportTextProps( xProp
);
1445 pFS
->endElement( FSNS( XML_c
, XML_legend
) );
1448 void ChartExport::exportTitle( const Reference
< XShape
>& xShape
, const OUString
* pSubText
)
1451 Reference
< beans::XPropertySet
> xPropSet( xShape
, uno::UNO_QUERY
);
1454 xPropSet
->getPropertyValue("String") >>= sText
;
1457 // tdf#101322: add subtitle to title
1459 sText
= sText
.isEmpty() ? *pSubText
: sText
+ "\n" + *pSubText
;
1461 if( sText
.isEmpty() )
1464 FSHelperPtr pFS
= GetFS();
1465 pFS
->startElement(FSNS(XML_c
, XML_title
));
1467 pFS
->startElement(FSNS(XML_c
, XML_tx
));
1468 pFS
->startElement(FSNS(XML_c
, XML_rich
));
1471 const char* sWritingMode
= nullptr;
1472 bool bVertical
= false;
1473 xPropSet
->getPropertyValue("StackedText") >>= bVertical
;
1475 sWritingMode
= "wordArtVert";
1477 sal_Int32 nRotation
= 0;
1478 xPropSet
->getPropertyValue("TextRotation") >>= nRotation
;
1480 pFS
->singleElement( FSNS( XML_a
, XML_bodyPr
),
1481 XML_vert
, sWritingMode
,
1482 XML_rot
, oox::drawingml::calcRotationValue(nRotation
) );
1484 pFS
->singleElement(FSNS(XML_a
, XML_lstStyle
));
1485 // FIXME: handle multiple paragraphs to parse aText
1486 pFS
->startElement(FSNS(XML_a
, XML_p
));
1488 pFS
->startElement(FSNS(XML_a
, XML_pPr
));
1490 bool bDummy
= false;
1492 WriteRunProperties(xPropSet
, false, XML_defRPr
, true, bDummy
, nDummy
);
1494 pFS
->endElement( FSNS( XML_a
, XML_pPr
) );
1496 pFS
->startElement(FSNS(XML_a
, XML_r
));
1498 WriteRunProperties( xPropSet
, false, XML_rPr
, true, bDummy
, nDummy
);
1499 pFS
->startElement(FSNS(XML_a
, XML_t
));
1500 pFS
->writeEscaped( sText
);
1501 pFS
->endElement( FSNS( XML_a
, XML_t
) );
1502 pFS
->endElement( FSNS( XML_a
, XML_r
) );
1504 pFS
->endElement( FSNS( XML_a
, XML_p
) );
1506 pFS
->endElement( FSNS( XML_c
, XML_rich
) );
1507 pFS
->endElement( FSNS( XML_c
, XML_tx
) );
1509 uno::Any aManualLayout
= xPropSet
->getPropertyValue("RelativePosition");
1510 if (aManualLayout
.hasValue())
1512 pFS
->startElement(FSNS(XML_c
, XML_layout
));
1513 pFS
->startElement(FSNS(XML_c
, XML_manualLayout
));
1514 pFS
->singleElement(FSNS(XML_c
, XML_xMode
), XML_val
, "edge");
1515 pFS
->singleElement(FSNS(XML_c
, XML_yMode
), XML_val
, "edge");
1517 Reference
<embed::XVisualObject
> xVisObject(mxChartModel
, uno::UNO_QUERY
);
1518 awt::Size aPageSize
= xVisObject
->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT
);
1520 awt::Size aSize
= xShape
->getSize();
1521 awt::Point aPos2
= xShape
->getPosition();
1522 // rotated shapes need special handling...
1523 double fSin
= fabs(sin(basegfx::deg2rad
<100>(nRotation
)));
1524 // remove part of height from X direction, if title is rotated down
1525 if( nRotation
*0.01 > 180.0 )
1526 aPos2
.X
-= static_cast<sal_Int32
>(fSin
* aSize
.Height
+ 0.5);
1527 // remove part of width from Y direction, if title is rotated up
1528 else if( nRotation
*0.01 > 0.0 )
1529 aPos2
.Y
-= static_cast<sal_Int32
>(fSin
* aSize
.Width
+ 0.5);
1531 double x
= static_cast<double>(aPos2
.X
) / static_cast<double>(aPageSize
.Width
);
1532 double y
= static_cast<double>(aPos2
.Y
) / static_cast<double>(aPageSize
.Height
);
1534 pFS->singleElement(FSNS(XML_c, XML_wMode), XML_val, "edge");
1535 pFS->singleElement(FSNS(XML_c, XML_hMode), XML_val, "edge");
1537 pFS
->singleElement(FSNS(XML_c
, XML_x
), XML_val
, OString::number(x
));
1538 pFS
->singleElement(FSNS(XML_c
, XML_y
), XML_val
, OString::number(y
));
1540 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, "");
1541 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, "");
1543 pFS
->endElement(FSNS(XML_c
, XML_manualLayout
));
1544 pFS
->endElement(FSNS(XML_c
, XML_layout
));
1547 pFS
->singleElement(FSNS(XML_c
, XML_overlay
), XML_val
, "0");
1552 exportShapeProps( xPropSet
);
1555 pFS
->endElement( FSNS( XML_c
, XML_title
) );
1560 std::vector
<Sequence
<Reference
<chart2::XDataSeries
> > > splitDataSeriesByAxis(const Reference
< chart2::XChartType
>& xChartType
)
1562 std::vector
<Sequence
<Reference
<chart2::XDataSeries
> > > aSplitSeries
;
1563 std::map
<sal_Int32
, size_t> aMapAxisToIndex
;
1565 Reference
< chart2::XDataSeriesContainer
> xDSCnt(xChartType
, uno::UNO_QUERY
);
1568 sal_Int32 nAxisIndexOfFirstSeries
= -1;
1569 const Sequence
< Reference
< chart2::XDataSeries
> > aSeriesSeq(xDSCnt
->getDataSeries());
1570 for (const uno::Reference
<chart2::XDataSeries
>& xSeries
: aSeriesSeq
)
1572 Reference
<beans::XPropertySet
> xPropSet(xSeries
, uno::UNO_QUERY
);
1576 sal_Int32 nAxisIndex
= -1;
1577 uno::Any aAny
= xPropSet
->getPropertyValue("AttachedAxisIndex");
1578 aAny
>>= nAxisIndex
;
1579 size_t nVectorPos
= 0;
1580 if (nAxisIndexOfFirstSeries
== -1)
1582 nAxisIndexOfFirstSeries
= nAxisIndex
;
1585 auto it
= aMapAxisToIndex
.find(nAxisIndex
);
1586 if (it
== aMapAxisToIndex
.end())
1588 aSplitSeries
.emplace_back();
1589 nVectorPos
= aSplitSeries
.size() - 1;
1590 aMapAxisToIndex
.insert(std::pair
<sal_Int32
, size_t>(nAxisIndex
, nVectorPos
));
1594 nVectorPos
= it
->second
;
1597 uno::Sequence
<Reference
<chart2::XDataSeries
> >& rAxisSeriesSeq
= aSplitSeries
[nVectorPos
];
1598 sal_Int32 nLength
= rAxisSeriesSeq
.getLength();
1599 rAxisSeriesSeq
.realloc(nLength
+ 1);
1600 rAxisSeriesSeq
.getArray()[nLength
] = xSeries
;
1602 // if the first series attached to secondary axis, then export those series first, which are attached to primary axis
1603 // also the MS Office export every time in this order
1604 if (aSplitSeries
.size() > 1 && nAxisIndexOfFirstSeries
== 1)
1606 std::swap(aSplitSeries
[0], aSplitSeries
[1]);
1610 return aSplitSeries
;
1615 void ChartExport::exportPlotArea(const Reference
< css::chart::XChartDocument
>& xChartDoc
)
1617 Reference
< chart2::XCoordinateSystemContainer
> xBCooSysCnt( mxNewDiagram
, uno::UNO_QUERY
);
1618 if( ! xBCooSysCnt
.is())
1621 // plot-area element
1623 FSHelperPtr pFS
= GetFS();
1624 pFS
->startElement(FSNS(XML_c
, XML_plotArea
));
1626 Reference
<beans::XPropertySet
> xWall(mxNewDiagram
, uno::UNO_QUERY
);
1629 uno::Any aAny
= xWall
->getPropertyValue("RelativePosition");
1630 if (aAny
.hasValue())
1632 chart2::RelativePosition aPos
= aAny
.get
<chart2::RelativePosition
>();
1633 aAny
= xWall
->getPropertyValue("RelativeSize");
1634 chart2::RelativeSize aSize
= aAny
.get
<chart2::RelativeSize
>();
1635 uno::Reference
< css::chart::XDiagramPositioning
> xDiagramPositioning( xChartDoc
->getDiagram(), uno::UNO_QUERY
);
1636 exportManualLayout(aPos
, aSize
, xDiagramPositioning
->isExcludingDiagramPositioning() );
1641 const Sequence
< Reference
< chart2::XCoordinateSystem
> >
1642 aCooSysSeq( xBCooSysCnt
->getCoordinateSystems());
1644 // tdf#123647 Save empty chart as empty bar chart.
1645 if (!aCooSysSeq
.hasElements())
1647 pFS
->startElement(FSNS(XML_c
, XML_barChart
));
1648 pFS
->singleElement(FSNS(XML_c
, XML_barDir
), XML_val
, "col");
1649 pFS
->singleElement(FSNS(XML_c
, XML_grouping
), XML_val
, "clustered");
1650 pFS
->singleElement(FSNS(XML_c
, XML_varyColors
), XML_val
, "0");
1652 pFS
->endElement(FSNS(XML_c
, XML_barChart
));
1655 for( const auto& rCS
: aCooSysSeq
)
1657 Reference
< chart2::XChartTypeContainer
> xCTCnt( rCS
, uno::UNO_QUERY
);
1661 const Sequence
< Reference
< chart2::XChartType
> > aCTSeq( xCTCnt
->getChartTypes());
1662 for( const auto& rCT
: aCTSeq
)
1664 Reference
< chart2::XDataSeriesContainer
> xDSCnt( rCT
, uno::UNO_QUERY
);
1667 Reference
< chart2::XChartType
> xChartType( rCT
, uno::UNO_QUERY
);
1668 if( ! xChartType
.is())
1670 // note: if xDSCnt.is() then also aCTSeq[nCTIdx]
1671 OUString
aChartType( xChartType
->getChartType());
1672 sal_Int32 eChartType
= lcl_getChartType( aChartType
);
1673 switch( eChartType
)
1675 case chart::TYPEID_BAR
:
1677 exportBarChart( xChartType
);
1680 case chart::TYPEID_AREA
:
1682 exportAreaChart( xChartType
);
1685 case chart::TYPEID_LINE
:
1687 exportLineChart( xChartType
);
1690 case chart::TYPEID_BUBBLE
:
1692 exportBubbleChart( xChartType
);
1695 case chart::TYPEID_DOUGHNUT
: // doesn't currently happen
1696 case chart::TYPEID_OFPIE
: // doesn't currently happen
1697 case chart::TYPEID_PIE
:
1699 sal_Int32 eCT
= getChartType( );
1700 if(eCT
== chart::TYPEID_DOUGHNUT
)
1702 exportDoughnutChart( xChartType
);
1707 PropertySet
xChartTypeProp(rCT
);
1708 chart2::PieChartSubType
subtype(chart2::PieChartSubType_NONE
);
1709 if (!xChartTypeProp
.getProperty(subtype
, PROP_SubPieType
))
1711 subtype
= chart2::PieChartSubType_NONE
;
1713 if (subtype
!= chart2::PieChartSubType_NONE
)
1715 const char* sSubType
= "pie"; // default
1717 case chart2::PieChartSubType_PIE
:
1720 case chart2::PieChartSubType_BAR
:
1727 exportOfPieChart(xChartType
, sSubType
);
1729 exportPieChart( xChartType
);
1734 case chart::TYPEID_RADARLINE
:
1735 case chart::TYPEID_RADARAREA
:
1737 exportRadarChart( xChartType
);
1740 case chart::TYPEID_SCATTER
:
1742 exportScatterChart( xChartType
);
1745 case chart::TYPEID_STOCK
:
1747 exportStockChart( xChartType
);
1750 case chart::TYPEID_SURFACE
:
1752 exportSurfaceChart( xChartType
);
1757 SAL_WARN("oox", "ChartExport::exportPlotArea -- not support chart type");
1772 * Export the Plot area Shape Properties
1773 * eg: Fill and Outline
1775 Reference
< css::chart::X3DDisplay
> xWallFloorSupplier( mxDiagram
, uno::UNO_QUERY
);
1776 // tdf#114139 For 2D charts Plot Area equivalent is Chart Wall.
1777 // Unfortunately LibreOffice doesn't have Plot Area equivalent for 3D charts.
1778 // It means that Plot Area couldn't be displayed and changed for 3D chars in LibreOffice.
1779 // We cannot write Wall attributes into Plot Area for 3D charts, because Wall us used as background wall.
1780 if( !mbIs3DChart
&& xWallFloorSupplier
.is() )
1782 Reference
< beans::XPropertySet
> xWallPropSet
= xWallFloorSupplier
->getWall();
1783 if( xWallPropSet
.is() )
1785 uno::Any aAny
= xWallPropSet
->getPropertyValue("LineStyle");
1786 sal_Int32 eChartType
= getChartType( );
1787 // Export LineStyle_NONE instead of default linestyle of PlotArea border, because LibreOffice
1788 // make invisible the Wall shape properties, in case of these charts. Or in the future set
1789 // the default LineStyle of these charts to LineStyle_NONE.
1790 bool noSupportWallProp
= ( (eChartType
== chart::TYPEID_PIE
) || (eChartType
== chart::TYPEID_RADARLINE
) || (eChartType
== chart::TYPEID_RADARAREA
) );
1791 if ( noSupportWallProp
&& (aAny
!= drawing::LineStyle_NONE
) )
1793 xWallPropSet
->setPropertyValue( "LineStyle", uno::Any(drawing::LineStyle_NONE
) );
1795 exportShapeProps( xWallPropSet
);
1799 pFS
->endElement( FSNS( XML_c
, XML_plotArea
) );
1803 void ChartExport::exportManualLayout(const css::chart2::RelativePosition
& rPos
,
1804 const css::chart2::RelativeSize
& rSize
,
1805 const bool bIsExcludingDiagramPositioning
)
1807 FSHelperPtr pFS
= GetFS();
1808 pFS
->startElement(FSNS(XML_c
, XML_layout
));
1809 pFS
->startElement(FSNS(XML_c
, XML_manualLayout
));
1811 // By default layoutTarget is set to "outer" and we shouldn't save it in that case
1812 if ( bIsExcludingDiagramPositioning
)
1814 pFS
->singleElement(FSNS(XML_c
, XML_layoutTarget
), XML_val
, "inner");
1816 pFS
->singleElement(FSNS(XML_c
, XML_xMode
), XML_val
, "edge");
1817 pFS
->singleElement(FSNS(XML_c
, XML_yMode
), XML_val
, "edge");
1819 double x
= rPos
.Primary
;
1820 double y
= rPos
.Secondary
;
1821 const double w
= rSize
.Primary
;
1822 const double h
= rSize
.Secondary
;
1823 switch (rPos
.Anchor
)
1825 case drawing::Alignment_LEFT
:
1828 case drawing::Alignment_TOP_LEFT
:
1830 case drawing::Alignment_BOTTOM_LEFT
:
1833 case drawing::Alignment_TOP
:
1836 case drawing::Alignment_CENTER
:
1840 case drawing::Alignment_BOTTOM
:
1844 case drawing::Alignment_TOP_RIGHT
:
1847 case drawing::Alignment_BOTTOM_RIGHT
:
1851 case drawing::Alignment_RIGHT
:
1856 SAL_WARN("oox", "unhandled alignment case for manual layout export " << static_cast<sal_uInt16
>(rPos
.Anchor
));
1859 pFS
->singleElement(FSNS(XML_c
, XML_x
), XML_val
, OString::number(x
));
1861 pFS
->singleElement(FSNS(XML_c
, XML_y
), XML_val
, OString::number(y
));
1863 pFS
->singleElement(FSNS(XML_c
, XML_w
), XML_val
, OString::number(w
));
1865 pFS
->singleElement(FSNS(XML_c
, XML_h
), XML_val
, OString::number(h
));
1867 pFS
->endElement(FSNS(XML_c
, XML_manualLayout
));
1868 pFS
->endElement(FSNS(XML_c
, XML_layout
));
1871 void ChartExport::exportFill( const Reference
< XPropertySet
>& xPropSet
)
1873 // Similar to DrawingML::WriteFill, but gradient access via name
1874 if (!GetProperty( xPropSet
, "FillStyle" ))
1876 FillStyle
aFillStyle(FillStyle_NONE
);
1877 mAny
>>= aFillStyle
;
1879 // map full transparent background to no fill
1880 if (aFillStyle
== FillStyle_SOLID
&& GetProperty( xPropSet
, "FillTransparence" ))
1885 aFillStyle
= FillStyle_NONE
;
1887 OUString sFillTransparenceGradientName
;
1888 if (aFillStyle
== FillStyle_SOLID
1889 && GetProperty(xPropSet
, "FillTransparenceGradientName") && (mAny
>>= sFillTransparenceGradientName
)
1890 && !sFillTransparenceGradientName
.isEmpty())
1892 awt::Gradient aTransparenceGradient
;
1893 uno::Reference
< lang::XMultiServiceFactory
> xFact( getModel(), uno::UNO_QUERY
);
1894 uno::Reference
< container::XNameAccess
> xTransparenceGradient(xFact
->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY
);
1895 uno::Any rTransparenceValue
= xTransparenceGradient
->getByName(sFillTransparenceGradientName
);
1896 rTransparenceValue
>>= aTransparenceGradient
;
1897 if (aTransparenceGradient
.StartColor
== 0xffffff && aTransparenceGradient
.EndColor
== 0xffffff)
1898 aFillStyle
= FillStyle_NONE
;
1900 switch( aFillStyle
)
1902 case FillStyle_SOLID
:
1903 exportSolidFill(xPropSet
);
1905 case FillStyle_GRADIENT
:
1906 exportGradientFill( xPropSet
);
1908 case FillStyle_BITMAP
:
1909 exportBitmapFill( xPropSet
);
1911 case FillStyle_HATCH
:
1912 exportHatch(xPropSet
);
1914 case FillStyle_NONE
:
1915 mpFS
->singleElementNS(XML_a
, XML_noFill
);
1922 void ChartExport::exportSolidFill(const Reference
< XPropertySet
>& xPropSet
)
1924 // Similar to DrawingML::WriteSolidFill, but gradient access via name
1925 // and currently no InteropGrabBag
1927 sal_uInt32 nFillColor
= 0;
1928 if (!GetProperty(xPropSet
, "FillColor") || !(mAny
>>= nFillColor
))
1931 sal_Int32 nAlpha
= MAX_PERCENT
;
1932 if (GetProperty( xPropSet
, "FillTransparence" ))
1934 sal_Int32 nTransparency
= 0;
1935 mAny
>>= nTransparency
;
1936 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
1937 nAlpha
= (MAX_PERCENT
- ( PER_PERCENT
* nTransparency
) );
1939 // OOXML has no separate transparence gradient but uses transparency in the gradient stops.
1940 // So we merge transparency and color and use gradient fill in such case.
1941 basegfx::BGradient aTransparenceGradient
;
1942 bool bNeedGradientFill(false);
1943 OUString sFillTransparenceGradientName
;
1945 if (GetProperty(xPropSet
, "FillTransparenceGradientName")
1946 && (mAny
>>= sFillTransparenceGradientName
)
1947 && !sFillTransparenceGradientName
.isEmpty())
1949 uno::Reference
< lang::XMultiServiceFactory
> xFact( getModel(), uno::UNO_QUERY
);
1950 uno::Reference
< container::XNameAccess
> xTransparenceGradient(xFact
->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY
);
1951 const uno::Any rTransparenceAny
= xTransparenceGradient
->getByName(sFillTransparenceGradientName
);
1953 aTransparenceGradient
= model::gradient::getFromAny(rTransparenceAny
);
1954 basegfx::BColor aSingleColor
;
1955 bNeedGradientFill
= !aTransparenceGradient
.GetColorStops().isSingleColor(aSingleColor
);
1957 if (!bNeedGradientFill
)
1959 // Our alpha is a single gray color value.
1960 const sal_uInt8
nRed(aSingleColor
.getRed() * 255.0);
1962 // drawingML alpha is a percentage on a 0..100000 scale.
1963 nAlpha
= (255 - nRed
) * oox::drawingml::MAX_PERCENT
/ 255;
1967 if (bNeedGradientFill
)
1969 // no longer create copy/PseudoColorGradient, use new API of
1970 // WriteGradientFill to express fix fill color
1971 mpFS
->startElementNS(XML_a
, XML_gradFill
, XML_rotWithShape
, "0");
1972 WriteGradientFill(nullptr, nFillColor
, &aTransparenceGradient
);
1973 mpFS
->endElementNS(XML_a
, XML_gradFill
);
1976 WriteSolidFill(::Color(ColorTransparency
, nFillColor
& 0xffffff), nAlpha
);
1979 void ChartExport::exportHatch( const Reference
< XPropertySet
>& xPropSet
)
1984 if (GetProperty(xPropSet
, "FillHatchName"))
1986 OUString aHatchName
;
1987 mAny
>>= aHatchName
;
1988 uno::Reference
< lang::XMultiServiceFactory
> xFact( getModel(), uno::UNO_QUERY
);
1989 uno::Reference
< container::XNameAccess
> xHatchTable( xFact
->createInstance("com.sun.star.drawing.HatchTable"), uno::UNO_QUERY
);
1990 uno::Any rValue
= xHatchTable
->getByName(aHatchName
);
1991 css::drawing::Hatch aHatch
;
1993 WritePattFill(xPropSet
, aHatch
);
1998 void ChartExport::exportBitmapFill( const Reference
< XPropertySet
>& xPropSet
)
2000 if( !xPropSet
.is() )
2003 OUString sFillBitmapName
;
2004 xPropSet
->getPropertyValue("FillBitmapName") >>= sFillBitmapName
;
2006 uno::Reference
< lang::XMultiServiceFactory
> xFact( getModel(), uno::UNO_QUERY
);
2009 uno::Reference
< container::XNameAccess
> xBitmapTable( xFact
->createInstance("com.sun.star.drawing.BitmapTable"), uno::UNO_QUERY
);
2010 uno::Any rValue
= xBitmapTable
->getByName( sFillBitmapName
);
2011 if (rValue
.has
<uno::Reference
<awt::XBitmap
>>())
2013 uno::Reference
<awt::XBitmap
> xBitmap
= rValue
.get
<uno::Reference
<awt::XBitmap
>>();
2014 uno::Reference
<graphic::XGraphic
> xGraphic(xBitmap
, uno::UNO_QUERY
);
2017 WriteXGraphicBlipFill(xPropSet
, xGraphic
, XML_a
, true, true);
2021 catch (const uno::Exception
&)
2023 TOOLS_WARN_EXCEPTION("oox", "ChartExport::exportBitmapFill");
2027 void ChartExport::exportGradientFill( const Reference
< XPropertySet
>& xPropSet
)
2029 if( !xPropSet
.is() )
2032 OUString sFillGradientName
;
2033 xPropSet
->getPropertyValue("FillGradientName") >>= sFillGradientName
;
2035 uno::Reference
< lang::XMultiServiceFactory
> xFact( getModel(), uno::UNO_QUERY
);
2038 uno::Reference
< container::XNameAccess
> xGradient( xFact
->createInstance("com.sun.star.drawing.GradientTable"), uno::UNO_QUERY
);
2039 const uno::Any
rGradientAny(xGradient
->getByName( sFillGradientName
));
2040 const basegfx::BGradient aGradient
= model::gradient::getFromAny(rGradientAny
);
2041 basegfx::BColor aSingleColor
;
2043 if (!aGradient
.GetColorStops().isSingleColor(aSingleColor
))
2045 basegfx::BGradient aTransparenceGradient
;
2046 mpFS
->startElementNS(XML_a
, XML_gradFill
);
2047 OUString sFillTransparenceGradientName
;
2049 if( (xPropSet
->getPropertyValue("FillTransparenceGradientName") >>= sFillTransparenceGradientName
) && !sFillTransparenceGradientName
.isEmpty())
2051 uno::Reference
< container::XNameAccess
> xTransparenceGradient(xFact
->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY
);
2052 const uno::Any
rTransparenceAny(xTransparenceGradient
->getByName(sFillTransparenceGradientName
));
2054 aTransparenceGradient
= model::gradient::getFromAny(rTransparenceAny
);
2056 WriteGradientFill(&aGradient
, 0, &aTransparenceGradient
);
2058 else if (GetProperty(xPropSet
, "FillTransparence") )
2060 // no longer create PseudoTransparencyGradient, use new API of
2061 // WriteGradientFill to express fix transparency
2062 sal_Int32 nTransparency
= 0;
2063 mAny
>>= nTransparency
;
2064 // nTransparency is [0..100]%
2065 WriteGradientFill(&aGradient
, 0, nullptr, nTransparency
* 0.01);
2069 WriteGradientFill(&aGradient
, 0, nullptr);
2072 mpFS
->endElementNS(XML_a
, XML_gradFill
);
2075 catch (const uno::Exception
&)
2077 TOOLS_INFO_EXCEPTION("oox", "ChartExport::exportGradientFill");
2081 void ChartExport::exportDataTable( )
2083 auto xDataTable
= mxNewDiagram
->getDataTable();
2084 if (!xDataTable
.is())
2087 FSHelperPtr pFS
= GetFS();
2088 uno::Reference
<beans::XPropertySet
> aPropSet(xDataTable
, uno::UNO_QUERY
);
2090 bool bShowVBorder
= false;
2091 bool bShowHBorder
= false;
2092 bool bShowOutline
= false;
2093 bool bShowKeys
= false;
2095 if (GetProperty(aPropSet
, "HBorder"))
2096 mAny
>>= bShowHBorder
;
2097 if (GetProperty(aPropSet
, "VBorder"))
2098 mAny
>>= bShowVBorder
;
2099 if (GetProperty(aPropSet
, "Outline"))
2100 mAny
>>= bShowOutline
;
2101 if (GetProperty(aPropSet
, "Keys"))
2104 pFS
->startElement(FSNS(XML_c
, XML_dTable
));
2107 pFS
->singleElement(FSNS(XML_c
, XML_showHorzBorder
), XML_val
, "1" );
2109 pFS
->singleElement(FSNS(XML_c
, XML_showVertBorder
), XML_val
, "1");
2111 pFS
->singleElement(FSNS(XML_c
, XML_showOutline
), XML_val
, "1");
2113 pFS
->singleElement(FSNS(XML_c
, XML_showKeys
), XML_val
, "1");
2115 exportShapeProps(aPropSet
);
2116 exportTextProps(aPropSet
);
2118 pFS
->endElement(FSNS(XML_c
, XML_dTable
));
2121 void ChartExport::exportAreaChart( const Reference
< chart2::XChartType
>& xChartType
)
2123 FSHelperPtr pFS
= GetFS();
2124 const std::vector
<Sequence
<Reference
<chart2::XDataSeries
> > > aSplitDataSeries
= splitDataSeriesByAxis(xChartType
);
2125 for (const auto& splitDataSeries
: aSplitDataSeries
)
2127 if (!splitDataSeries
.hasElements())
2130 sal_Int32 nTypeId
= XML_areaChart
;
2132 nTypeId
= XML_area3DChart
;
2133 pFS
->startElement(FSNS(XML_c
, nTypeId
));
2136 bool bPrimaryAxes
= true;
2137 exportSeries(xChartType
, splitDataSeries
, bPrimaryAxes
);
2138 exportAxesId(bPrimaryAxes
);
2140 pFS
->endElement(FSNS(XML_c
, nTypeId
));
2144 void ChartExport::exportBarChart(const Reference
< chart2::XChartType
>& xChartType
)
2146 sal_Int32 nTypeId
= XML_barChart
;
2148 nTypeId
= XML_bar3DChart
;
2149 FSHelperPtr pFS
= GetFS();
2151 const std::vector
<Sequence
<Reference
<chart2::XDataSeries
> > > aSplitDataSeries
= splitDataSeriesByAxis(xChartType
);
2152 for (const auto& splitDataSeries
: aSplitDataSeries
)
2154 if (!splitDataSeries
.hasElements())
2157 pFS
->startElement(FSNS(XML_c
, nTypeId
));
2159 bool bVertical
= false;
2160 Reference
< XPropertySet
> xPropSet(mxDiagram
, uno::UNO_QUERY
);
2161 if (GetProperty(xPropSet
, "Vertical"))
2164 const char* bardir
= bVertical
? "bar" : "col";
2165 pFS
->singleElement(FSNS(XML_c
, XML_barDir
), XML_val
, bardir
);
2167 exportGrouping(true);
2169 exportVaryColors(xChartType
);
2171 bool bPrimaryAxes
= true;
2172 exportSeries(xChartType
, splitDataSeries
, bPrimaryAxes
);
2174 Reference
< XPropertySet
> xTypeProp(xChartType
, uno::UNO_QUERY
);
2176 if (xTypeProp
.is() && GetProperty(xTypeProp
, "GapwidthSequence"))
2178 uno::Sequence
< sal_Int32
> aBarPositionSequence
;
2179 mAny
>>= aBarPositionSequence
;
2180 if (aBarPositionSequence
.hasElements())
2182 sal_Int32 nGapWidth
= aBarPositionSequence
[0];
2183 pFS
->singleElement(FSNS(XML_c
, XML_gapWidth
), XML_val
, OString::number(nGapWidth
));
2190 namespace cssc
= css::chart
;
2191 sal_Int32 nGeom3d
= cssc::ChartSolidType::RECTANGULAR_SOLID
;
2192 if (xPropSet
.is() && GetProperty(xPropSet
, "SolidType"))
2194 const char* sShapeType
= nullptr;
2197 case cssc::ChartSolidType::RECTANGULAR_SOLID
:
2200 case cssc::ChartSolidType::CONE
:
2201 sShapeType
= "cone";
2203 case cssc::ChartSolidType::CYLINDER
:
2204 sShapeType
= "cylinder";
2206 case cssc::ChartSolidType::PYRAMID
:
2207 sShapeType
= "pyramid";
2210 pFS
->singleElement(FSNS(XML_c
, XML_shape
), XML_val
, sShapeType
);
2214 if (!mbIs3DChart
&& xTypeProp
.is() && GetProperty(xTypeProp
, "OverlapSequence"))
2216 uno::Sequence
< sal_Int32
> aBarPositionSequence
;
2217 mAny
>>= aBarPositionSequence
;
2218 if (aBarPositionSequence
.hasElements())
2220 sal_Int32 nOverlap
= aBarPositionSequence
[0];
2221 // Stacked/Percent Bar/Column chart Overlap-workaround
2222 // Export the Overlap value with 100% for stacked charts,
2223 // because the default overlap value of the Bar/Column chart is 0% and
2224 // LibreOffice do nothing with the overlap value in Stacked charts case,
2225 // unlike the MS Office, which is interpreted differently.
2226 if ((mbStacked
|| mbPercent
) && nOverlap
!= 100)
2229 pFS
->singleElement(FSNS(XML_c
, XML_overlap
), XML_val
, OString::number(nOverlap
));
2231 else // Normal bar chart
2233 pFS
->singleElement(FSNS(XML_c
, XML_overlap
), XML_val
, OString::number(nOverlap
));
2238 exportAxesId(bPrimaryAxes
);
2240 pFS
->endElement(FSNS(XML_c
, nTypeId
));
2244 void ChartExport::exportBubbleChart( const Reference
< chart2::XChartType
>& xChartType
)
2246 FSHelperPtr pFS
= GetFS();
2247 const std::vector
<Sequence
<Reference
<chart2::XDataSeries
> > > aSplitDataSeries
= splitDataSeriesByAxis(xChartType
);
2248 for (const auto& splitDataSeries
: aSplitDataSeries
)
2250 if (!splitDataSeries
.hasElements())
2253 pFS
->startElement(FSNS(XML_c
, XML_bubbleChart
));
2255 exportVaryColors(xChartType
);
2257 bool bPrimaryAxes
= true;
2258 exportSeries(xChartType
, splitDataSeries
, bPrimaryAxes
);
2260 exportAxesId(bPrimaryAxes
);
2262 pFS
->endElement(FSNS(XML_c
, XML_bubbleChart
));
2266 void ChartExport::exportDoughnutChart( const Reference
< chart2::XChartType
>& xChartType
)
2268 FSHelperPtr pFS
= GetFS();
2269 pFS
->startElement(FSNS(XML_c
, XML_doughnutChart
));
2271 exportVaryColors(xChartType
);
2273 bool bPrimaryAxes
= true;
2274 exportAllSeries(xChartType
, bPrimaryAxes
);
2276 exportFirstSliceAng( );
2278 pFS
->singleElement(FSNS(XML_c
, XML_holeSize
), XML_val
, OString::number(50));
2280 pFS
->endElement( FSNS( XML_c
, XML_doughnutChart
) );
2283 void ChartExport::exportOfPieChart(
2284 const Reference
< chart2::XChartType
>& xChartType
,
2285 const char* sSubType
)
2287 FSHelperPtr pFS
= GetFS();
2288 pFS
->startElement(FSNS(XML_c
, XML_ofPieChart
));
2290 pFS
->singleElement(FSNS(XML_c
, XML_ofPieType
), XML_val
, sSubType
);
2292 exportVaryColors(xChartType
);
2294 bool bPrimaryAxes
= true;
2295 exportAllSeries(xChartType
, bPrimaryAxes
);
2297 pFS
->endElement( FSNS( XML_c
, XML_ofPieChart
) );
2302 void writeDataLabelsRange(const FSHelperPtr
& pFS
, const XmlFilterBase
* pFB
, DataLabelsRange
& rDLblsRange
)
2304 if (rDLblsRange
.empty())
2307 pFS
->startElement(FSNS(XML_c
, XML_extLst
));
2308 pFS
->startElement(FSNS(XML_c
, XML_ext
), XML_uri
, "{02D57815-91ED-43cb-92C2-25804820EDAC}", FSNS(XML_xmlns
, XML_c15
), pFB
->getNamespaceURL(OOX_NS(c15
)));
2309 pFS
->startElement(FSNS(XML_c15
, XML_datalabelsRange
));
2311 // Write cell range.
2312 pFS
->startElement(FSNS(XML_c15
, XML_f
));
2313 pFS
->writeEscaped(rDLblsRange
.getRange());
2314 pFS
->endElement(FSNS(XML_c15
, XML_f
));
2316 // Write all labels.
2317 pFS
->startElement(FSNS(XML_c15
, XML_dlblRangeCache
));
2318 pFS
->singleElement(FSNS(XML_c
, XML_ptCount
), XML_val
, OString::number(rDLblsRange
.count()));
2319 for (const auto& rLabelKV
: rDLblsRange
)
2321 pFS
->startElement(FSNS(XML_c
, XML_pt
), XML_idx
, OString::number(rLabelKV
.first
));
2322 pFS
->startElement(FSNS(XML_c
, XML_v
));
2323 pFS
->writeEscaped(rLabelKV
.second
);
2324 pFS
->endElement(FSNS( XML_c
, XML_v
));
2325 pFS
->endElement(FSNS(XML_c
, XML_pt
));
2328 pFS
->endElement(FSNS(XML_c15
, XML_dlblRangeCache
));
2330 pFS
->endElement(FSNS(XML_c15
, XML_datalabelsRange
));
2331 pFS
->endElement(FSNS(XML_c
, XML_ext
));
2332 pFS
->endElement(FSNS(XML_c
, XML_extLst
));
2337 void ChartExport::exportLineChart( const Reference
< chart2::XChartType
>& xChartType
)
2339 FSHelperPtr pFS
= GetFS();
2340 const std::vector
<Sequence
<Reference
<chart2::XDataSeries
> > > aSplitDataSeries
= splitDataSeriesByAxis(xChartType
);
2341 for (const auto& splitDataSeries
: aSplitDataSeries
)
2343 if (!splitDataSeries
.hasElements())
2346 sal_Int32 nTypeId
= XML_lineChart
;
2348 nTypeId
= XML_line3DChart
;
2349 pFS
->startElement(FSNS(XML_c
, nTypeId
));
2353 exportVaryColors(xChartType
);
2354 // TODO: show marker symbol in series?
2355 bool bPrimaryAxes
= true;
2356 exportSeries(xChartType
, splitDataSeries
, bPrimaryAxes
);
2359 sal_Int32 nSymbolType
= css::chart::ChartSymbolType::NONE
;
2360 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
2361 if( GetProperty( xPropSet
, "SymbolType" ) )
2362 mAny
>>= nSymbolType
;
2367 exportUpDownBars(xChartType
);
2368 const char* marker
= nSymbolType
== css::chart::ChartSymbolType::NONE
? "0":"1";
2369 pFS
->singleElement(FSNS(XML_c
, XML_marker
), XML_val
, marker
);
2372 exportAxesId(bPrimaryAxes
, true);
2374 pFS
->endElement( FSNS( XML_c
, nTypeId
) );
2378 void ChartExport::exportPieChart( const Reference
< chart2::XChartType
>& xChartType
)
2380 FSHelperPtr pFS
= GetFS();
2381 sal_Int32 nTypeId
= XML_pieChart
;
2383 nTypeId
= XML_pie3DChart
;
2384 pFS
->startElement(FSNS(XML_c
, nTypeId
));
2386 exportVaryColors(xChartType
);
2388 bool bPrimaryAxes
= true;
2389 exportAllSeries(xChartType
, bPrimaryAxes
);
2394 exportFirstSliceAng( );
2397 pFS
->endElement( FSNS( XML_c
, nTypeId
) );
2400 void ChartExport::exportRadarChart( const Reference
< chart2::XChartType
>& xChartType
)
2402 FSHelperPtr pFS
= GetFS();
2403 pFS
->startElement(FSNS(XML_c
, XML_radarChart
));
2406 sal_Int32 eChartType
= getChartType( );
2407 const char* radarStyle
= nullptr;
2408 if( eChartType
== chart::TYPEID_RADARAREA
)
2409 radarStyle
= "filled";
2411 radarStyle
= "marker";
2412 pFS
->singleElement(FSNS(XML_c
, XML_radarStyle
), XML_val
, radarStyle
);
2414 exportVaryColors(xChartType
);
2415 bool bPrimaryAxes
= true;
2416 exportAllSeries(xChartType
, bPrimaryAxes
);
2417 exportAxesId(bPrimaryAxes
);
2419 pFS
->endElement( FSNS( XML_c
, XML_radarChart
) );
2422 void ChartExport::exportScatterChartSeries( const Reference
< chart2::XChartType
>& xChartType
,
2423 const css::uno::Sequence
<css::uno::Reference
<chart2::XDataSeries
>>* pSeries
)
2425 FSHelperPtr pFS
= GetFS();
2426 pFS
->startElement(FSNS(XML_c
, XML_scatterChart
));
2427 // TODO:scatterStyle
2429 sal_Int32 nSymbolType
= css::chart::ChartSymbolType::NONE
;
2430 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
2431 if( GetProperty( xPropSet
, "SymbolType" ) )
2432 mAny
>>= nSymbolType
;
2434 const char* scatterStyle
= "lineMarker";
2435 if (nSymbolType
== css::chart::ChartSymbolType::NONE
)
2437 scatterStyle
= "line";
2440 pFS
->singleElement(FSNS(XML_c
, XML_scatterStyle
), XML_val
, scatterStyle
);
2442 exportVaryColors(xChartType
);
2443 // FIXME: should export xVal and yVal
2444 bool bPrimaryAxes
= true;
2446 exportSeries(xChartType
, *pSeries
, bPrimaryAxes
);
2447 exportAxesId(bPrimaryAxes
);
2449 pFS
->endElement( FSNS( XML_c
, XML_scatterChart
) );
2452 void ChartExport::exportScatterChart( const Reference
< chart2::XChartType
>& xChartType
)
2454 const std::vector
<Sequence
<Reference
<chart2::XDataSeries
> > > aSplitDataSeries
= splitDataSeriesByAxis(xChartType
);
2455 bool bExported
= false;
2456 for (const auto& splitDataSeries
: aSplitDataSeries
)
2458 if (!splitDataSeries
.hasElements())
2462 exportScatterChartSeries(xChartType
, &splitDataSeries
);
2465 exportScatterChartSeries(xChartType
, nullptr);
2468 void ChartExport::exportStockChart( const Reference
< chart2::XChartType
>& xChartType
)
2470 FSHelperPtr pFS
= GetFS();
2471 const std::vector
<Sequence
<Reference
<chart2::XDataSeries
> > > aSplitDataSeries
= splitDataSeriesByAxis(xChartType
);
2472 for (const auto& splitDataSeries
: aSplitDataSeries
)
2474 if (!splitDataSeries
.hasElements())
2477 pFS
->startElement(FSNS(XML_c
, XML_stockChart
));
2479 bool bPrimaryAxes
= true;
2480 exportCandleStickSeries(splitDataSeries
, bPrimaryAxes
);
2482 // export stock properties
2483 Reference
< css::chart::XStatisticDisplay
> xStockPropProvider(mxDiagram
, uno::UNO_QUERY
);
2484 if (xStockPropProvider
.is())
2487 exportUpDownBars(xChartType
);
2490 exportAxesId(bPrimaryAxes
);
2492 pFS
->endElement(FSNS(XML_c
, XML_stockChart
));
2496 void ChartExport::exportHiLowLines()
2498 FSHelperPtr pFS
= GetFS();
2499 // export the chart property
2500 Reference
< css::chart::XStatisticDisplay
> xChartPropProvider( mxDiagram
, uno::UNO_QUERY
);
2502 if (!xChartPropProvider
.is())
2505 Reference
< beans::XPropertySet
> xStockPropSet
= xChartPropProvider
->getMinMaxLine();
2506 if( !xStockPropSet
.is() )
2509 pFS
->startElement(FSNS(XML_c
, XML_hiLowLines
));
2510 exportShapeProps( xStockPropSet
);
2511 pFS
->endElement( FSNS( XML_c
, XML_hiLowLines
) );
2514 void ChartExport::exportUpDownBars( const Reference
< chart2::XChartType
>& xChartType
)
2516 if(xChartType
->getChartType() != "com.sun.star.chart2.CandleStickChartType")
2519 FSHelperPtr pFS
= GetFS();
2520 // export the chart property
2521 Reference
< css::chart::XStatisticDisplay
> xChartPropProvider( mxDiagram
, uno::UNO_QUERY
);
2522 if(!xChartPropProvider
.is())
2526 pFS
->startElement(FSNS(XML_c
, XML_upDownBars
));
2528 pFS
->singleElement(FSNS(XML_c
, XML_gapWidth
), XML_val
, OString::number(150));
2530 Reference
< beans::XPropertySet
> xChartPropSet
= xChartPropProvider
->getUpBar();
2531 if( xChartPropSet
.is() )
2533 pFS
->startElement(FSNS(XML_c
, XML_upBars
));
2534 // For Linechart with UpDownBars, spPr is not getting imported
2535 // so no need to call the exportShapeProps() for LineChart
2536 if(xChartType
->getChartType() == "com.sun.star.chart2.CandleStickChartType")
2538 exportShapeProps(xChartPropSet
);
2540 pFS
->endElement( FSNS( XML_c
, XML_upBars
) );
2542 xChartPropSet
= xChartPropProvider
->getDownBar();
2543 if( xChartPropSet
.is() )
2545 pFS
->startElement(FSNS(XML_c
, XML_downBars
));
2546 if(xChartType
->getChartType() == "com.sun.star.chart2.CandleStickChartType")
2548 exportShapeProps(xChartPropSet
);
2550 pFS
->endElement( FSNS( XML_c
, XML_downBars
) );
2552 pFS
->endElement( FSNS( XML_c
, XML_upDownBars
) );
2555 void ChartExport::exportSurfaceChart( const Reference
< chart2::XChartType
>& xChartType
)
2557 FSHelperPtr pFS
= GetFS();
2558 sal_Int32 nTypeId
= XML_surfaceChart
;
2560 nTypeId
= XML_surface3DChart
;
2561 pFS
->startElement(FSNS(XML_c
, nTypeId
));
2562 exportVaryColors(xChartType
);
2563 bool bPrimaryAxes
= true;
2564 exportAllSeries(xChartType
, bPrimaryAxes
);
2565 exportAxesId(bPrimaryAxes
);
2567 pFS
->endElement( FSNS( XML_c
, nTypeId
) );
2570 void ChartExport::exportAllSeries(const Reference
<chart2::XChartType
>& xChartType
, bool& rPrimaryAxes
)
2572 Reference
< chart2::XDataSeriesContainer
> xDSCnt( xChartType
, uno::UNO_QUERY
);
2576 // export dataseries for current chart-type
2577 Sequence
< Reference
< chart2::XDataSeries
> > aSeriesSeq( xDSCnt
->getDataSeries());
2578 exportSeries(xChartType
, aSeriesSeq
, rPrimaryAxes
);
2581 void ChartExport::exportVaryColors(const Reference
<chart2::XChartType
>& xChartType
)
2583 FSHelperPtr pFS
= GetFS();
2586 Reference
<chart2::XDataSeries
> xDataSeries
= getPrimaryDataSeries(xChartType
);
2587 Reference
<beans::XPropertySet
> xDataSeriesProps(xDataSeries
, uno::UNO_QUERY_THROW
);
2588 Any aAnyVaryColors
= xDataSeriesProps
->getPropertyValue("VaryColorsByPoint");
2589 bool bVaryColors
= false;
2590 aAnyVaryColors
>>= bVaryColors
;
2591 pFS
->singleElement(FSNS(XML_c
, XML_varyColors
), XML_val
, ToPsz10(bVaryColors
));
2595 pFS
->singleElement(FSNS(XML_c
, XML_varyColors
), XML_val
, "0");
2599 void ChartExport::exportSeries( const Reference
<chart2::XChartType
>& xChartType
,
2600 const Sequence
<Reference
<chart2::XDataSeries
> >& rSeriesSeq
, bool& rPrimaryAxes
)
2602 OUString aLabelRole
= xChartType
->getRoleOfSequenceForSeriesLabel();
2603 OUString
aChartType( xChartType
->getChartType());
2604 sal_Int32 eChartType
= lcl_getChartType( aChartType
);
2606 for( const auto& rSeries
: rSeriesSeq
)
2609 Reference
< chart2::data::XDataSource
> xSource( rSeries
, uno::UNO_QUERY
);
2612 Reference
< chart2::XDataSeries
> xDataSeries( xSource
, uno::UNO_QUERY
);
2613 Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aSeqCnt(
2614 xSource
->getDataSequences());
2615 // search for main sequence and create a series element
2617 sal_Int32 nMainSequenceIndex
= -1;
2618 sal_Int32 nSeriesLength
= 0;
2619 Reference
< chart2::data::XDataSequence
> xValuesSeq
;
2620 Reference
< chart2::data::XDataSequence
> xLabelSeq
;
2621 sal_Int32 nSeqIdx
=0;
2622 for( ; nSeqIdx
<aSeqCnt
.getLength(); ++nSeqIdx
)
2624 Reference
< chart2::data::XDataSequence
> xTempValueSeq( aSeqCnt
[nSeqIdx
]->getValues() );
2625 if( nMainSequenceIndex
==-1 )
2627 Reference
< beans::XPropertySet
> xSeqProp( xTempValueSeq
, uno::UNO_QUERY
);
2630 xSeqProp
->getPropertyValue("Role") >>= aRole
;
2632 if( aRole
== aLabelRole
)
2634 xValuesSeq
.set( xTempValueSeq
);
2635 xLabelSeq
.set( aSeqCnt
[nSeqIdx
]->getLabel());
2636 nMainSequenceIndex
= nSeqIdx
;
2639 sal_Int32 nSequenceLength
= (xTempValueSeq
.is()? xTempValueSeq
->getData().getLength() : sal_Int32(0));
2640 if( nSeriesLength
< nSequenceLength
)
2641 nSeriesLength
= nSequenceLength
;
2644 // have found the main sequence, then xValuesSeq and
2645 // xLabelSeq contain those. Otherwise both are empty
2647 FSHelperPtr pFS
= GetFS();
2649 pFS
->startElement(FSNS(XML_c
, XML_ser
));
2651 // TODO: idx and order
2652 pFS
->singleElement( FSNS( XML_c
, XML_idx
),
2653 XML_val
, OString::number(mnSeriesCount
) );
2654 pFS
->singleElement( FSNS( XML_c
, XML_order
),
2655 XML_val
, OString::number(mnSeriesCount
++) );
2658 if( xLabelSeq
.is() )
2659 exportSeriesText( xLabelSeq
);
2661 Reference
<XPropertySet
> xPropSet(xDataSeries
, UNO_QUERY_THROW
);
2662 if( GetProperty( xPropSet
, "AttachedAxisIndex") )
2664 sal_Int32 nLocalAttachedAxis
= 0;
2665 mAny
>>= nLocalAttachedAxis
;
2666 rPrimaryAxes
= isPrimaryAxes(nLocalAttachedAxis
);
2669 // export shape properties
2670 Reference
< XPropertySet
> xOldPropSet
= SchXMLSeriesHelper::createOldAPISeriesPropertySet(
2671 rSeries
, getModel() );
2672 if( xOldPropSet
.is() )
2674 exportShapeProps( xOldPropSet
);
2677 switch( eChartType
)
2679 case chart::TYPEID_BUBBLE
:
2680 case chart::TYPEID_HORBAR
:
2681 case chart::TYPEID_BAR
:
2683 pFS
->singleElement(FSNS(XML_c
, XML_invertIfNegative
), XML_val
, "0");
2686 case chart::TYPEID_LINE
:
2688 exportMarker(xOldPropSet
);
2691 case chart::TYPEID_PIE
:
2692 case chart::TYPEID_DOUGHNUT
:
2694 if( xOldPropSet
.is() && GetProperty( xOldPropSet
, "SegmentOffset") )
2696 sal_Int32 nOffset
= 0;
2698 pFS
->singleElement( FSNS( XML_c
, XML_explosion
),
2699 XML_val
, OString::number( nOffset
) );
2703 case chart::TYPEID_SCATTER
:
2705 exportMarker(xOldPropSet
);
2708 case chart::TYPEID_RADARLINE
:
2710 exportMarker(xOldPropSet
);
2715 // export data points
2716 exportDataPoints( uno::Reference
< beans::XPropertySet
>( rSeries
, uno::UNO_QUERY
), nSeriesLength
, eChartType
);
2718 DataLabelsRange aDLblsRange
;
2719 // export data labels
2720 exportDataLabels(rSeries
, nSeriesLength
, eChartType
, aDLblsRange
);
2722 exportTrendlines( rSeries
);
2724 if( eChartType
!= chart::TYPEID_PIE
&&
2725 eChartType
!= chart::TYPEID_RADARLINE
)
2727 //export error bars here
2728 Reference
< XPropertySet
> xSeriesPropSet( xSource
, uno::UNO_QUERY
);
2729 Reference
< XPropertySet
> xErrorBarYProps
;
2730 xSeriesPropSet
->getPropertyValue("ErrorBarY") >>= xErrorBarYProps
;
2731 if(xErrorBarYProps
.is())
2732 exportErrorBar(xErrorBarYProps
, true);
2733 if (eChartType
!= chart::TYPEID_BAR
&&
2734 eChartType
!= chart::TYPEID_HORBAR
)
2736 Reference
< XPropertySet
> xErrorBarXProps
;
2737 xSeriesPropSet
->getPropertyValue("ErrorBarX") >>= xErrorBarXProps
;
2738 if(xErrorBarXProps
.is())
2739 exportErrorBar(xErrorBarXProps
, false);
2743 // export categories
2744 if( eChartType
!= chart::TYPEID_SCATTER
&& eChartType
!= chart::TYPEID_BUBBLE
&& mxCategoriesValues
.is() )
2745 exportSeriesCategory( mxCategoriesValues
);
2747 if( (eChartType
== chart::TYPEID_SCATTER
)
2748 || (eChartType
== chart::TYPEID_BUBBLE
) )
2751 Reference
< chart2::data::XLabeledDataSequence
> xSequence( lcl_getDataSequenceByRole( aSeqCnt
, "values-x" ) );
2752 if( xSequence
.is() )
2754 Reference
< chart2::data::XDataSequence
> xValues( xSequence
->getValues() );
2756 exportSeriesValues( xValues
, XML_xVal
);
2758 else if( mxCategoriesValues
.is() )
2759 exportSeriesCategory( mxCategoriesValues
, XML_xVal
);
2762 if( eChartType
== chart::TYPEID_BUBBLE
)
2765 Reference
< chart2::data::XLabeledDataSequence
> xSequence( lcl_getDataSequenceByRole( aSeqCnt
, "values-y" ) );
2766 if( xSequence
.is() )
2768 Reference
< chart2::data::XDataSequence
> xValues( xSequence
->getValues() );
2770 exportSeriesValues( xValues
, XML_yVal
);
2775 if( xValuesSeq
.is() )
2777 sal_Int32 nYValueType
= XML_val
;
2778 if( eChartType
== chart::TYPEID_SCATTER
)
2779 nYValueType
= XML_yVal
;
2780 else if( eChartType
== chart::TYPEID_BUBBLE
)
2781 nYValueType
= XML_bubbleSize
;
2782 exportSeriesValues( xValuesSeq
, nYValueType
);
2785 if( eChartType
== chart::TYPEID_SCATTER
2786 || eChartType
== chart::TYPEID_LINE
)
2789 // tdf103988: "corrupted" files with Bubble chart opening in MSO
2790 if( eChartType
== chart::TYPEID_BUBBLE
)
2791 pFS
->singleElement(FSNS(XML_c
, XML_bubble3D
), XML_val
, "0");
2793 if (!aDLblsRange
.empty())
2794 writeDataLabelsRange(pFS
, GetFB(), aDLblsRange
);
2796 pFS
->endElement( FSNS( XML_c
, XML_ser
) );
2803 void ChartExport::exportCandleStickSeries(
2804 const Sequence
< Reference
< chart2::XDataSeries
> > & aSeriesSeq
,
2807 for( const Reference
< chart2::XDataSeries
>& xSeries
: aSeriesSeq
)
2809 rPrimaryAxes
= lcl_isSeriesAttachedToFirstAxis(xSeries
);
2811 Reference
< chart2::data::XDataSource
> xSource( xSeries
, uno::UNO_QUERY
);
2814 // export series in correct order (as we don't store roles)
2815 // with japanese candlesticks: open, low, high, close
2816 // otherwise: low, high, close
2817 Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aSeqCnt(
2818 xSource
->getDataSequences());
2820 const char* sSeries
[] = {"values-first","values-max","values-min","values-last",nullptr};
2822 for( sal_Int32 idx
= 0; sSeries
[idx
] != nullptr ; idx
++ )
2824 Reference
< chart2::data::XLabeledDataSequence
> xLabeledSeq( lcl_getDataSequenceByRole( aSeqCnt
, OUString::createFromAscii(sSeries
[idx
]) ) );
2825 if( xLabeledSeq
.is())
2827 Reference
< chart2::data::XDataSequence
> xLabelSeq( xLabeledSeq
->getLabel());
2828 Reference
< chart2::data::XDataSequence
> xValueSeq( xLabeledSeq
->getValues());
2830 FSHelperPtr pFS
= GetFS();
2831 pFS
->startElement(FSNS(XML_c
, XML_ser
));
2833 // TODO: idx and order
2834 // idx attribute should start from 1 and not from 0.
2835 pFS
->singleElement( FSNS( XML_c
, XML_idx
),
2836 XML_val
, OString::number(idx
+1) );
2837 pFS
->singleElement( FSNS( XML_c
, XML_order
),
2838 XML_val
, OString::number(idx
+1) );
2841 if( xLabelSeq
.is() )
2842 exportSeriesText( xLabelSeq
);
2844 // TODO:export shape properties
2846 // export categories
2847 if( mxCategoriesValues
.is() )
2848 exportSeriesCategory( mxCategoriesValues
);
2851 if( xValueSeq
.is() )
2852 exportSeriesValues( xValueSeq
);
2854 pFS
->endElement( FSNS( XML_c
, XML_ser
) );
2862 void ChartExport::exportSeriesText( const Reference
< chart2::data::XDataSequence
> & xValueSeq
)
2864 FSHelperPtr pFS
= GetFS();
2865 pFS
->startElement(FSNS(XML_c
, XML_tx
));
2867 OUString aCellRange
= xValueSeq
->getSourceRangeRepresentation();
2868 aCellRange
= parseFormula( aCellRange
);
2869 pFS
->startElement(FSNS(XML_c
, XML_strRef
));
2871 pFS
->startElement(FSNS(XML_c
, XML_f
));
2872 pFS
->writeEscaped( aCellRange
);
2873 pFS
->endElement( FSNS( XML_c
, XML_f
) );
2875 OUString aLabelString
= lcl_flattenStringSequence(lcl_getLabelSequence(xValueSeq
));
2876 pFS
->startElement(FSNS(XML_c
, XML_strCache
));
2877 pFS
->singleElement(FSNS(XML_c
, XML_ptCount
), XML_val
, "1");
2878 pFS
->startElement(FSNS(XML_c
, XML_pt
), XML_idx
, "0");
2879 pFS
->startElement(FSNS(XML_c
, XML_v
));
2880 pFS
->writeEscaped( aLabelString
);
2881 pFS
->endElement( FSNS( XML_c
, XML_v
) );
2882 pFS
->endElement( FSNS( XML_c
, XML_pt
) );
2883 pFS
->endElement( FSNS( XML_c
, XML_strCache
) );
2884 pFS
->endElement( FSNS( XML_c
, XML_strRef
) );
2885 pFS
->endElement( FSNS( XML_c
, XML_tx
) );
2888 void ChartExport::exportSeriesCategory( const Reference
< chart2::data::XDataSequence
> & xValueSeq
, sal_Int32 nValueType
)
2890 FSHelperPtr pFS
= GetFS();
2891 pFS
->startElement(FSNS(XML_c
, nValueType
));
2893 OUString aCellRange
= xValueSeq
.is() ? xValueSeq
->getSourceRangeRepresentation() : OUString();
2894 const Sequence
< Sequence
< OUString
>> aFinalSplitSource
= (nValueType
== XML_cat
) ? getSplitCategoriesList(aCellRange
) : Sequence
< Sequence
< OUString
>>(0);
2895 aCellRange
= parseFormula( aCellRange
);
2897 if(aFinalSplitSource
.getLength() > 1)
2899 // export multi level category axis labels
2900 pFS
->startElement(FSNS(XML_c
, XML_multiLvlStrRef
));
2902 pFS
->startElement(FSNS(XML_c
, XML_f
));
2903 pFS
->writeEscaped(aCellRange
);
2904 pFS
->endElement(FSNS(XML_c
, XML_f
));
2906 pFS
->startElement(FSNS(XML_c
, XML_multiLvlStrCache
));
2907 pFS
->singleElement(FSNS(XML_c
, XML_ptCount
), XML_val
, OString::number(aFinalSplitSource
[0].getLength()));
2908 for(const auto& rSeq
: aFinalSplitSource
)
2910 pFS
->startElement(FSNS(XML_c
, XML_lvl
));
2911 for(sal_Int32 j
= 0; j
< rSeq
.getLength(); j
++)
2913 if(!rSeq
[j
].isEmpty())
2915 pFS
->startElement(FSNS(XML_c
, XML_pt
), XML_idx
, OString::number(j
));
2916 pFS
->startElement(FSNS(XML_c
, XML_v
));
2917 pFS
->writeEscaped(rSeq
[j
]);
2918 pFS
->endElement(FSNS(XML_c
, XML_v
));
2919 pFS
->endElement(FSNS(XML_c
, XML_pt
));
2922 pFS
->endElement(FSNS(XML_c
, XML_lvl
));
2925 pFS
->endElement(FSNS(XML_c
, XML_multiLvlStrCache
));
2926 pFS
->endElement(FSNS(XML_c
, XML_multiLvlStrRef
));
2930 // export single category axis labels
2931 bool bWriteDateCategories
= mbHasDateCategories
&& (nValueType
== XML_cat
);
2932 OUString aNumberFormatString
;
2933 if (bWriteDateCategories
)
2935 Reference
< css::chart::XAxisXSupplier
> xAxisXSupp( mxDiagram
, uno::UNO_QUERY
);
2936 if( xAxisXSupp
.is())
2938 Reference
< XPropertySet
> xAxisProp
= xAxisXSupp
->getXAxis();
2939 if (GetProperty(xAxisProp
, "NumberFormat"))
2943 aNumberFormatString
= getNumberFormatCode(nKey
);
2946 if (aNumberFormatString
.isEmpty())
2947 bWriteDateCategories
= false;
2950 pFS
->startElement(FSNS(XML_c
, bWriteDateCategories
? XML_numRef
: XML_strRef
));
2952 pFS
->startElement(FSNS(XML_c
, XML_f
));
2953 pFS
->writeEscaped(aCellRange
);
2954 pFS
->endElement(FSNS(XML_c
, XML_f
));
2956 ::std::vector
< OUString
> aCategories
;
2957 lcl_fillCategoriesIntoStringVector(xValueSeq
, aCategories
);
2958 sal_Int32 ptCount
= aCategories
.size();
2959 pFS
->startElement(FSNS(XML_c
, bWriteDateCategories
? XML_numCache
: XML_strCache
));
2960 if (bWriteDateCategories
)
2962 pFS
->startElement(FSNS(XML_c
, XML_formatCode
));
2963 pFS
->writeEscaped(aNumberFormatString
);
2964 pFS
->endElement(FSNS(XML_c
, XML_formatCode
));
2967 pFS
->singleElement(FSNS(XML_c
, XML_ptCount
), XML_val
, OString::number(ptCount
));
2968 for (sal_Int32 i
= 0; i
< ptCount
; i
++)
2970 pFS
->startElement(FSNS(XML_c
, XML_pt
), XML_idx
, OString::number(i
));
2971 pFS
->startElement(FSNS(XML_c
, XML_v
));
2972 pFS
->writeEscaped(aCategories
[i
]);
2973 pFS
->endElement(FSNS(XML_c
, XML_v
));
2974 pFS
->endElement(FSNS(XML_c
, XML_pt
));
2977 pFS
->endElement(FSNS(XML_c
, bWriteDateCategories
? XML_numCache
: XML_strCache
));
2978 pFS
->endElement(FSNS(XML_c
, bWriteDateCategories
? XML_numRef
: XML_strRef
));
2981 pFS
->endElement( FSNS( XML_c
, nValueType
) );
2984 void ChartExport::exportSeriesValues( const Reference
< chart2::data::XDataSequence
> & xValueSeq
, sal_Int32 nValueType
)
2986 FSHelperPtr pFS
= GetFS();
2987 pFS
->startElement(FSNS(XML_c
, nValueType
));
2989 OUString aCellRange
= xValueSeq
.is() ? xValueSeq
->getSourceRangeRepresentation() : OUString();
2990 aCellRange
= parseFormula( aCellRange
);
2991 // TODO: need to handle XML_multiLvlStrRef according to aCellRange
2992 pFS
->startElement(FSNS(XML_c
, XML_numRef
));
2994 pFS
->startElement(FSNS(XML_c
, XML_f
));
2995 pFS
->writeEscaped( aCellRange
);
2996 pFS
->endElement( FSNS( XML_c
, XML_f
) );
2998 ::std::vector
< double > aValues
= lcl_getAllValuesFromSequence( xValueSeq
);
2999 sal_Int32 ptCount
= aValues
.size();
3000 pFS
->startElement(FSNS(XML_c
, XML_numCache
));
3001 pFS
->startElement(FSNS(XML_c
, XML_formatCode
));
3002 OUString
sNumberFormatString("General");
3003 const sal_Int32 nKey
= xValueSeq
.is() ? xValueSeq
->getNumberFormatKeyByIndex(-1) : 0;
3005 sNumberFormatString
= getNumberFormatCode(nKey
);
3006 pFS
->writeEscaped(sNumberFormatString
);
3007 pFS
->endElement( FSNS( XML_c
, XML_formatCode
) );
3008 pFS
->singleElement(FSNS(XML_c
, XML_ptCount
), XML_val
, OString::number(ptCount
));
3010 for( sal_Int32 i
= 0; i
< ptCount
; i
++ )
3012 if (!std::isnan(aValues
[i
]))
3014 pFS
->startElement(FSNS(XML_c
, XML_pt
), XML_idx
, OString::number(i
));
3015 pFS
->startElement(FSNS(XML_c
, XML_v
));
3016 pFS
->write(aValues
[i
]);
3017 pFS
->endElement(FSNS(XML_c
, XML_v
));
3018 pFS
->endElement(FSNS(XML_c
, XML_pt
));
3022 pFS
->endElement( FSNS( XML_c
, XML_numCache
) );
3023 pFS
->endElement( FSNS( XML_c
, XML_numRef
) );
3024 pFS
->endElement( FSNS( XML_c
, nValueType
) );
3027 void ChartExport::exportShapeProps( const Reference
< XPropertySet
>& xPropSet
)
3029 FSHelperPtr pFS
= GetFS();
3030 pFS
->startElement(FSNS(XML_c
, XML_spPr
));
3032 exportFill( xPropSet
);
3033 WriteOutline( xPropSet
, getModel() );
3035 pFS
->endElement( FSNS( XML_c
, XML_spPr
) );
3038 void ChartExport::exportTextProps(const Reference
<XPropertySet
>& xPropSet
)
3040 FSHelperPtr pFS
= GetFS();
3041 pFS
->startElement(FSNS(XML_c
, XML_txPr
));
3043 sal_Int32 nRotation
= 0;
3044 const char* textWordWrap
= nullptr;
3046 if (auto xServiceInfo
= uno::Reference
<lang::XServiceInfo
>(xPropSet
, uno::UNO_QUERY
))
3048 double fMultiplier
= 0.0;
3049 // We have at least two possible units of returned value: degrees (e.g., for data labels),
3050 // and 100ths of degree (e.g., for axes labels). The latter is returned as an Any wrapping
3051 // a sal_Int32 value (see WrappedTextRotationProperty::convertInnerToOuterValue), while
3052 // the former is double. So we could test the contained type to decide which multiplier to
3053 // use. But testing the service info should be more robust.
3054 if (xServiceInfo
->supportsService("com.sun.star.chart.ChartAxis"))
3055 fMultiplier
= -600.0;
3056 else if (xServiceInfo
->supportsService("com.sun.star.chart2.DataSeries") || xServiceInfo
->supportsService("com.sun.star.chart2.DataPointProperties"))
3058 fMultiplier
= -60000.0;
3059 bool bTextWordWrap
= false;
3060 if ((xPropSet
->getPropertyValue("TextWordWrap") >>= bTextWordWrap
) && bTextWordWrap
)
3061 textWordWrap
= "square";
3063 textWordWrap
= "none";
3068 double fTextRotation
= 0.0;
3069 uno::Any aAny
= xPropSet
->getPropertyValue("TextRotation");
3070 if (aAny
.hasValue() && (aAny
>>= fTextRotation
))
3072 fTextRotation
*= fMultiplier
;
3073 // The MS Office UI allows values only in range of [-90,90].
3074 if (fTextRotation
< -5400000.0 && fTextRotation
> -16200000.0)
3076 // Reflect the angle if the value is between 90° and 270°
3077 fTextRotation
+= 10800000.0;
3079 else if (fTextRotation
<= -16200000.0)
3081 fTextRotation
+= 21600000.0;
3083 nRotation
= std::round(fTextRotation
);
3089 pFS
->singleElement(FSNS(XML_a
, XML_bodyPr
), XML_rot
, OString::number(nRotation
), XML_wrap
, textWordWrap
);
3091 pFS
->singleElement(FSNS(XML_a
, XML_bodyPr
), XML_wrap
, textWordWrap
);
3093 pFS
->singleElement(FSNS(XML_a
, XML_lstStyle
));
3095 pFS
->startElement(FSNS(XML_a
, XML_p
));
3096 pFS
->startElement(FSNS(XML_a
, XML_pPr
));
3098 WriteRunProperties(xPropSet
, false, XML_defRPr
, true, o3tl::temporary(false),
3099 o3tl::temporary(sal_Int32()));
3101 pFS
->endElement(FSNS(XML_a
, XML_pPr
));
3102 pFS
->endElement(FSNS(XML_a
, XML_p
));
3103 pFS
->endElement(FSNS(XML_c
, XML_txPr
));
3106 void ChartExport::InitPlotArea( )
3108 Reference
< XPropertySet
> xDiagramProperties (mxDiagram
, uno::UNO_QUERY
);
3110 // Check for supported services and then the properties provided by this service.
3111 Reference
<lang::XServiceInfo
> xServiceInfo (mxDiagram
, uno::UNO_QUERY
);
3112 if (xServiceInfo
.is())
3114 if (xServiceInfo
->supportsService("com.sun.star.chart.ChartAxisZSupplier"))
3116 xDiagramProperties
->getPropertyValue("HasZAxis") >>= mbHasZAxis
;
3120 xDiagramProperties
->getPropertyValue("Dim3D") >>= mbIs3DChart
;
3122 if( mbHasCategoryLabels
&& mxNewDiagram
.is())
3124 Reference
< chart2::data::XLabeledDataSequence
> xCategories( lcl_getCategories( mxNewDiagram
, mbHasDateCategories
) );
3125 if( xCategories
.is() )
3127 mxCategoriesValues
.set( xCategories
->getValues() );
3132 void ChartExport::exportAxes( )
3134 sal_Int32 nSize
= maAxes
.size();
3135 // let's export the axis types in the right order
3136 for ( sal_Int32 nSortIdx
= AXIS_PRIMARY_X
; nSortIdx
<= AXIS_SECONDARY_Y
; nSortIdx
++ )
3138 for ( sal_Int32 nIdx
= 0; nIdx
< nSize
; nIdx
++ )
3140 if (nSortIdx
== maAxes
[nIdx
].nAxisType
)
3141 exportAxis( maAxes
[nIdx
] );
3148 sal_Int32
getXAxisTypeByChartType(sal_Int32 eChartType
)
3150 if( (eChartType
== chart::TYPEID_SCATTER
)
3151 || (eChartType
== chart::TYPEID_BUBBLE
) )
3153 else if( eChartType
== chart::TYPEID_STOCK
)
3159 sal_Int32
getRealXAxisType(sal_Int32 nAxisType
)
3161 if( nAxisType
== chart2::AxisType::CATEGORY
)
3163 else if( nAxisType
== chart2::AxisType::DATE
)
3165 else if( nAxisType
== chart2::AxisType::SERIES
)
3173 void ChartExport::exportAxis(const AxisIdPair
& rAxisIdPair
)
3175 // get some properties from document first
3176 bool bHasXAxisTitle
= false,
3177 bHasYAxisTitle
= false,
3178 bHasZAxisTitle
= false,
3179 bHasSecondaryXAxisTitle
= false,
3180 bHasSecondaryYAxisTitle
= false;
3181 bool bHasXAxisMajorGrid
= false,
3182 bHasXAxisMinorGrid
= false,
3183 bHasYAxisMajorGrid
= false,
3184 bHasYAxisMinorGrid
= false,
3185 bHasZAxisMajorGrid
= false,
3186 bHasZAxisMinorGrid
= false;
3188 Reference
< XPropertySet
> xDiagramProperties (mxDiagram
, uno::UNO_QUERY
);
3190 xDiagramProperties
->getPropertyValue("HasXAxisTitle") >>= bHasXAxisTitle
;
3191 xDiagramProperties
->getPropertyValue("HasYAxisTitle") >>= bHasYAxisTitle
;
3192 xDiagramProperties
->getPropertyValue("HasZAxisTitle") >>= bHasZAxisTitle
;
3193 xDiagramProperties
->getPropertyValue("HasSecondaryXAxisTitle") >>= bHasSecondaryXAxisTitle
;
3194 xDiagramProperties
->getPropertyValue("HasSecondaryYAxisTitle") >>= bHasSecondaryYAxisTitle
;
3196 xDiagramProperties
->getPropertyValue("HasXAxisGrid") >>= bHasXAxisMajorGrid
;
3197 xDiagramProperties
->getPropertyValue("HasYAxisGrid") >>= bHasYAxisMajorGrid
;
3198 xDiagramProperties
->getPropertyValue("HasZAxisGrid") >>= bHasZAxisMajorGrid
;
3200 xDiagramProperties
->getPropertyValue("HasXAxisHelpGrid") >>= bHasXAxisMinorGrid
;
3201 xDiagramProperties
->getPropertyValue("HasYAxisHelpGrid") >>= bHasYAxisMinorGrid
;
3202 xDiagramProperties
->getPropertyValue("HasZAxisHelpGrid") >>= bHasZAxisMinorGrid
;
3204 Reference
< XPropertySet
> xAxisProp
;
3205 Reference
< drawing::XShape
> xAxisTitle
;
3206 Reference
< beans::XPropertySet
> xMajorGrid
;
3207 Reference
< beans::XPropertySet
> xMinorGrid
;
3208 sal_Int32 nAxisType
= XML_catAx
;
3209 const char* sAxPos
= nullptr;
3211 switch( rAxisIdPair
.nAxisType
)
3213 case AXIS_PRIMARY_X
:
3215 Reference
< css::chart::XAxisXSupplier
> xAxisXSupp( mxDiagram
, uno::UNO_QUERY
);
3216 if( xAxisXSupp
.is())
3217 xAxisProp
= xAxisXSupp
->getXAxis();
3218 if( bHasXAxisTitle
)
3219 xAxisTitle
= xAxisXSupp
->getXAxisTitle();
3220 if( bHasXAxisMajorGrid
)
3221 xMajorGrid
= xAxisXSupp
->getXMainGrid();
3222 if( bHasXAxisMinorGrid
)
3223 xMinorGrid
= xAxisXSupp
->getXHelpGrid();
3225 nAxisType
= lcl_getCategoryAxisType(mxNewDiagram
, 0, 0);
3226 if( nAxisType
!= -1 )
3227 nAxisType
= getRealXAxisType(nAxisType
);
3229 nAxisType
= getXAxisTypeByChartType( getChartType() );
3230 // FIXME: axPos, need to check axis direction
3234 case AXIS_PRIMARY_Y
:
3236 Reference
< css::chart::XAxisYSupplier
> xAxisYSupp( mxDiagram
, uno::UNO_QUERY
);
3237 if( xAxisYSupp
.is())
3238 xAxisProp
= xAxisYSupp
->getYAxis();
3239 if( bHasYAxisTitle
)
3240 xAxisTitle
= xAxisYSupp
->getYAxisTitle();
3241 if( bHasYAxisMajorGrid
)
3242 xMajorGrid
= xAxisYSupp
->getYMainGrid();
3243 if( bHasYAxisMinorGrid
)
3244 xMinorGrid
= xAxisYSupp
->getYHelpGrid();
3246 nAxisType
= XML_valAx
;
3247 // FIXME: axPos, need to check axis direction
3251 case AXIS_PRIMARY_Z
:
3253 Reference
< css::chart::XAxisZSupplier
> xAxisZSupp( mxDiagram
, uno::UNO_QUERY
);
3254 if( xAxisZSupp
.is())
3255 xAxisProp
= xAxisZSupp
->getZAxis();
3256 if( bHasZAxisTitle
)
3257 xAxisTitle
= xAxisZSupp
->getZAxisTitle();
3258 if( bHasZAxisMajorGrid
)
3259 xMajorGrid
= xAxisZSupp
->getZMainGrid();
3260 if( bHasZAxisMinorGrid
)
3261 xMinorGrid
= xAxisZSupp
->getZHelpGrid();
3263 sal_Int32 eChartType
= getChartType( );
3264 if( (eChartType
== chart::TYPEID_SCATTER
)
3265 || (eChartType
== chart::TYPEID_BUBBLE
) )
3266 nAxisType
= XML_valAx
;
3267 else if( eChartType
== chart::TYPEID_STOCK
)
3268 nAxisType
= XML_dateAx
;
3269 else if( eChartType
== chart::TYPEID_BAR
|| eChartType
== chart::TYPEID_AREA
)
3270 nAxisType
= XML_serAx
;
3271 // FIXME: axPos, need to check axis direction
3275 case AXIS_SECONDARY_X
:
3277 Reference
< css::chart::XTwoAxisXSupplier
> xAxisTwoXSupp( mxDiagram
, uno::UNO_QUERY
);
3278 if( xAxisTwoXSupp
.is())
3279 xAxisProp
= xAxisTwoXSupp
->getSecondaryXAxis();
3280 if( bHasSecondaryXAxisTitle
)
3282 Reference
< css::chart::XSecondAxisTitleSupplier
> xAxisSupp( mxDiagram
, uno::UNO_QUERY
);
3283 xAxisTitle
= xAxisSupp
->getSecondXAxisTitle();
3286 nAxisType
= lcl_getCategoryAxisType(mxNewDiagram
, 0, 1);
3287 if( nAxisType
!= -1 )
3288 nAxisType
= getRealXAxisType(nAxisType
);
3290 nAxisType
= getXAxisTypeByChartType( getChartType() );
3291 // FIXME: axPos, need to check axis direction
3295 case AXIS_SECONDARY_Y
:
3297 Reference
< css::chart::XTwoAxisYSupplier
> xAxisTwoYSupp( mxDiagram
, uno::UNO_QUERY
);
3298 if( xAxisTwoYSupp
.is())
3299 xAxisProp
= xAxisTwoYSupp
->getSecondaryYAxis();
3300 if( bHasSecondaryYAxisTitle
)
3302 Reference
< css::chart::XSecondAxisTitleSupplier
> xAxisSupp( mxDiagram
, uno::UNO_QUERY
);
3303 xAxisTitle
= xAxisSupp
->getSecondYAxisTitle();
3306 nAxisType
= XML_valAx
;
3307 // FIXME: axPos, need to check axis direction
3313 _exportAxis(xAxisProp
, xAxisTitle
, xMajorGrid
, xMinorGrid
, nAxisType
, sAxPos
, rAxisIdPair
);
3316 void ChartExport::_exportAxis(
3317 const Reference
< XPropertySet
>& xAxisProp
,
3318 const Reference
< drawing::XShape
>& xAxisTitle
,
3319 const Reference
< XPropertySet
>& xMajorGrid
,
3320 const Reference
< XPropertySet
>& xMinorGrid
,
3321 sal_Int32 nAxisType
,
3322 const char* sAxisPos
,
3323 const AxisIdPair
& rAxisIdPair
)
3325 FSHelperPtr pFS
= GetFS();
3326 pFS
->startElement(FSNS(XML_c
, nAxisType
));
3327 pFS
->singleElement(FSNS(XML_c
, XML_axId
), XML_val
, OString::number(rAxisIdPair
.nAxisId
));
3329 pFS
->startElement(FSNS(XML_c
, XML_scaling
));
3331 // logBase, min, max
3332 if(GetProperty( xAxisProp
, "Logarithmic" ) )
3334 bool bLogarithmic
= false;
3335 mAny
>>= bLogarithmic
;
3338 // default value is 10?
3339 pFS
->singleElement(FSNS(XML_c
, XML_logBase
), XML_val
, OString::number(10));
3343 // orientation: minMax, maxMin
3344 bool bReverseDirection
= false;
3345 if(GetProperty( xAxisProp
, "ReverseDirection" ) )
3346 mAny
>>= bReverseDirection
;
3348 const char* orientation
= bReverseDirection
? "maxMin":"minMax";
3349 pFS
->singleElement(FSNS(XML_c
, XML_orientation
), XML_val
, orientation
);
3351 bool bAutoMax
= false;
3352 if(GetProperty( xAxisProp
, "AutoMax" ) )
3355 if( !bAutoMax
&& (GetProperty( xAxisProp
, "Max" ) ) )
3359 pFS
->singleElement(FSNS(XML_c
, XML_max
), XML_val
, OString::number(dMax
));
3362 bool bAutoMin
= false;
3363 if(GetProperty( xAxisProp
, "AutoMin" ) )
3366 if( !bAutoMin
&& (GetProperty( xAxisProp
, "Min" ) ) )
3370 pFS
->singleElement(FSNS(XML_c
, XML_min
), XML_val
, OString::number(dMin
));
3373 pFS
->endElement( FSNS( XML_c
, XML_scaling
) );
3375 bool bVisible
= true;
3376 if( xAxisProp
.is() )
3378 xAxisProp
->getPropertyValue("Visible") >>= bVisible
;
3381 // only export each axis only once non-deleted
3382 auto aItInsertedPair
= maExportedAxis
.insert(rAxisIdPair
.nAxisType
);
3383 bool bDeleted
= !aItInsertedPair
.second
;
3385 pFS
->singleElement(FSNS(XML_c
, XML_delete
), XML_val
, !bDeleted
&& bVisible
? "0" : "1");
3387 // FIXME: axPos, need to check the property "ReverseDirection"
3388 pFS
->singleElement(FSNS(XML_c
, XML_axPos
), XML_val
, sAxisPos
);
3390 if( xMajorGrid
.is())
3392 pFS
->startElement(FSNS(XML_c
, XML_majorGridlines
));
3393 exportShapeProps( xMajorGrid
);
3394 pFS
->endElement( FSNS( XML_c
, XML_majorGridlines
) );
3398 if( xMinorGrid
.is())
3400 pFS
->startElement(FSNS(XML_c
, XML_minorGridlines
));
3401 exportShapeProps( xMinorGrid
);
3402 pFS
->endElement( FSNS( XML_c
, XML_minorGridlines
) );
3406 if( xAxisTitle
.is() )
3407 exportTitle( xAxisTitle
);
3409 bool bLinkedNumFmt
= true;
3410 if (GetProperty(xAxisProp
, "LinkNumberFormatToSource"))
3411 mAny
>>= bLinkedNumFmt
;
3413 OUString
aNumberFormatString("General");
3414 if (GetProperty(xAxisProp
, "NumberFormat"))
3418 aNumberFormatString
= getNumberFormatCode(nKey
);
3421 pFS
->singleElement(FSNS(XML_c
, XML_numFmt
),
3422 XML_formatCode
, aNumberFormatString
,
3423 XML_sourceLinked
, bLinkedNumFmt
? "1" : "0");
3426 sal_Int32 nValue
= 0;
3427 if(GetProperty( xAxisProp
, "Marks" ) )
3430 bool bInner
= nValue
& css::chart::ChartAxisMarks::INNER
;
3431 bool bOuter
= nValue
& css::chart::ChartAxisMarks::OUTER
;
3432 const char* majorTickMark
= nullptr;
3433 if( bInner
&& bOuter
)
3434 majorTickMark
= "cross";
3436 majorTickMark
= "in";
3438 majorTickMark
= "out";
3440 majorTickMark
= "none";
3441 pFS
->singleElement(FSNS(XML_c
, XML_majorTickMark
), XML_val
, majorTickMark
);
3444 if(GetProperty( xAxisProp
, "HelpMarks" ) )
3447 bool bInner
= nValue
& css::chart::ChartAxisMarks::INNER
;
3448 bool bOuter
= nValue
& css::chart::ChartAxisMarks::OUTER
;
3449 const char* minorTickMark
= nullptr;
3450 if( bInner
&& bOuter
)
3451 minorTickMark
= "cross";
3453 minorTickMark
= "in";
3455 minorTickMark
= "out";
3457 minorTickMark
= "none";
3458 pFS
->singleElement(FSNS(XML_c
, XML_minorTickMark
), XML_val
, minorTickMark
);
3461 const char* sTickLblPos
= nullptr;
3462 bool bDisplayLabel
= true;
3463 if(GetProperty( xAxisProp
, "DisplayLabels" ) )
3464 mAny
>>= bDisplayLabel
;
3465 if( bDisplayLabel
&& (GetProperty( xAxisProp
, "LabelPosition" ) ) )
3467 css::chart::ChartAxisLabelPosition eLabelPosition
= css::chart::ChartAxisLabelPosition_NEAR_AXIS
;
3468 mAny
>>= eLabelPosition
;
3469 switch( eLabelPosition
)
3471 case css::chart::ChartAxisLabelPosition_NEAR_AXIS
:
3472 case css::chart::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE
:
3473 sTickLblPos
= "nextTo";
3475 case css::chart::ChartAxisLabelPosition_OUTSIDE_START
:
3476 sTickLblPos
= "low";
3478 case css::chart::ChartAxisLabelPosition_OUTSIDE_END
:
3479 sTickLblPos
= "high";
3482 sTickLblPos
= "nextTo";
3488 sTickLblPos
= "none";
3490 pFS
->singleElement(FSNS(XML_c
, XML_tickLblPos
), XML_val
, sTickLblPos
);
3493 exportShapeProps( xAxisProp
);
3495 exportTextProps(xAxisProp
);
3497 pFS
->singleElement(FSNS(XML_c
, XML_crossAx
), XML_val
, OString::number(rAxisIdPair
.nCrossAx
));
3499 // crosses & crossesAt
3500 bool bCrossesValue
= false;
3501 const char* sCrosses
= nullptr;
3502 // do not export the CrossoverPosition/CrossoverValue, if the axis is deleted and not visible
3503 if( GetProperty( xAxisProp
, "CrossoverPosition" ) && !bDeleted
&& bVisible
)
3505 css::chart::ChartAxisPosition
ePosition( css::chart::ChartAxisPosition_ZERO
);
3509 case css::chart::ChartAxisPosition_START
:
3512 case css::chart::ChartAxisPosition_END
:
3515 case css::chart::ChartAxisPosition_ZERO
:
3516 sCrosses
= "autoZero";
3519 bCrossesValue
= true;
3524 if( bCrossesValue
&& GetProperty( xAxisProp
, "CrossoverValue" ) )
3528 pFS
->singleElement(FSNS(XML_c
, XML_crossesAt
), XML_val
, OString::number(dValue
));
3534 pFS
->singleElement(FSNS(XML_c
, XML_crosses
), XML_val
, sCrosses
);
3538 if( ( nAxisType
== XML_catAx
)
3539 || ( nAxisType
== XML_dateAx
) )
3541 // FIXME: seems not support? use default value,
3542 const char* const isAuto
= "1";
3543 pFS
->singleElement(FSNS(XML_c
, XML_auto
), XML_val
, isAuto
);
3545 if( nAxisType
== XML_catAx
)
3547 // FIXME: seems not support? lblAlgn
3548 const char* const sLblAlgn
= "ctr";
3549 pFS
->singleElement(FSNS(XML_c
, XML_lblAlgn
), XML_val
, sLblAlgn
);
3552 // FIXME: seems not support? lblOffset
3553 pFS
->singleElement(FSNS(XML_c
, XML_lblOffset
), XML_val
, OString::number(100));
3555 // export baseTimeUnit, majorTimeUnit, minorTimeUnit of Date axis
3556 if( nAxisType
== XML_dateAx
)
3558 sal_Int32 nAxisIndex
= -1;
3559 if( rAxisIdPair
.nAxisType
== AXIS_PRIMARY_X
)
3561 else if( rAxisIdPair
.nAxisType
== AXIS_SECONDARY_X
)
3564 cssc::TimeIncrement aTimeIncrement
= lcl_getDateTimeIncrement( mxNewDiagram
, nAxisIndex
);
3565 sal_Int32 nTimeResolution
= css::chart::TimeUnit::DAY
;
3566 if( aTimeIncrement
.TimeResolution
>>= nTimeResolution
)
3567 pFS
->singleElement(FSNS(XML_c
, XML_baseTimeUnit
), XML_val
, lclGetTimeUnitToken(nTimeResolution
));
3569 cssc::TimeInterval aInterval
;
3570 if( aTimeIncrement
.MajorTimeInterval
>>= aInterval
)
3572 pFS
->singleElement(FSNS(XML_c
, XML_majorUnit
), XML_val
, OString::number(aInterval
.Number
));
3573 pFS
->singleElement(FSNS(XML_c
, XML_majorTimeUnit
), XML_val
, lclGetTimeUnitToken(aInterval
.TimeUnit
));
3575 if( aTimeIncrement
.MinorTimeInterval
>>= aInterval
)
3577 pFS
->singleElement(FSNS(XML_c
, XML_minorUnit
), XML_val
, OString::number(aInterval
.Number
));
3578 pFS
->singleElement(FSNS(XML_c
, XML_minorTimeUnit
), XML_val
, lclGetTimeUnitToken(aInterval
.TimeUnit
));
3582 // FIXME: seems not support? noMultiLvlLbl
3583 pFS
->singleElement(FSNS(XML_c
, XML_noMultiLvlLbl
), XML_val
, OString::number(0));
3587 if( nAxisType
== XML_valAx
)
3589 if( lcl_isCategoryAxisShifted( mxNewDiagram
))
3590 pFS
->singleElement(FSNS(XML_c
, XML_crossBetween
), XML_val
, "between");
3592 pFS
->singleElement(FSNS(XML_c
, XML_crossBetween
), XML_val
, "midCat");
3596 bool bAutoStepMain
= false;
3597 if(GetProperty( xAxisProp
, "AutoStepMain" ) )
3598 mAny
>>= bAutoStepMain
;
3600 if( !bAutoStepMain
&& (GetProperty( xAxisProp
, "StepMain" ) ) )
3602 double dMajorUnit
= 0;
3603 mAny
>>= dMajorUnit
;
3604 pFS
->singleElement(FSNS(XML_c
, XML_majorUnit
), XML_val
, OString::number(dMajorUnit
));
3607 bool bAutoStepHelp
= false;
3608 if(GetProperty( xAxisProp
, "AutoStepHelp" ) )
3609 mAny
>>= bAutoStepHelp
;
3611 if( !bAutoStepHelp
&& (GetProperty( xAxisProp
, "StepHelp" ) ) )
3613 double dMinorUnit
= 0;
3614 mAny
>>= dMinorUnit
;
3615 if( GetProperty( xAxisProp
, "StepHelpCount" ) )
3617 sal_Int32 dMinorUnitCount
= 0;
3618 mAny
>>= dMinorUnitCount
;
3619 // tdf#114168 Don't save minor unit if number of step help count is 5 (which is default for MS Excel),
3620 // to allow proper .xlsx import. If minorUnit is set and majorUnit not, then it is impossible
3621 // to calculate StepHelpCount.
3622 if( dMinorUnitCount
!= 5 )
3624 pFS
->singleElement( FSNS( XML_c
, XML_minorUnit
),
3625 XML_val
, OString::number( dMinorUnit
) );
3630 if( nAxisType
== XML_valAx
&& GetProperty( xAxisProp
, "DisplayUnits" ) )
3632 bool bDisplayUnits
= false;
3633 mAny
>>= bDisplayUnits
;
3636 if(GetProperty( xAxisProp
, "BuiltInUnit" ))
3642 pFS
->startElement(FSNS(XML_c
, XML_dispUnits
));
3644 pFS
->singleElement(FSNS(XML_c
, XML_builtInUnit
), XML_val
, aVal
);
3646 pFS
->singleElement(FSNS( XML_c
, XML_dispUnitsLbl
));
3647 pFS
->endElement( FSNS( XML_c
, XML_dispUnits
) );
3653 pFS
->endElement( FSNS( XML_c
, nAxisType
) );
3658 struct LabelPlacementParam
3661 sal_Int32 meDefault
;
3663 std::unordered_set
<sal_Int32
> maAllowedValues
;
3665 LabelPlacementParam(bool bExport
, sal_Int32 nDefault
) :
3667 meDefault(nDefault
),
3670 css::chart::DataLabelPlacement::OUTSIDE
,
3671 css::chart::DataLabelPlacement::INSIDE
,
3672 css::chart::DataLabelPlacement::CENTER
,
3673 css::chart::DataLabelPlacement::NEAR_ORIGIN
,
3674 css::chart::DataLabelPlacement::TOP
,
3675 css::chart::DataLabelPlacement::BOTTOM
,
3676 css::chart::DataLabelPlacement::LEFT
,
3677 css::chart::DataLabelPlacement::RIGHT
,
3678 css::chart::DataLabelPlacement::AVOID_OVERLAP
3684 const char* toOOXMLPlacement( sal_Int32 nPlacement
)
3688 case css::chart::DataLabelPlacement::OUTSIDE
: return "outEnd";
3689 case css::chart::DataLabelPlacement::INSIDE
: return "inEnd";
3690 case css::chart::DataLabelPlacement::CENTER
: return "ctr";
3691 case css::chart::DataLabelPlacement::NEAR_ORIGIN
: return "inBase";
3692 case css::chart::DataLabelPlacement::TOP
: return "t";
3693 case css::chart::DataLabelPlacement::BOTTOM
: return "b";
3694 case css::chart::DataLabelPlacement::LEFT
: return "l";
3695 case css::chart::DataLabelPlacement::RIGHT
: return "r";
3696 case css::chart::DataLabelPlacement::CUSTOM
:
3697 case css::chart::DataLabelPlacement::AVOID_OVERLAP
: return "bestFit";
3705 OUString
getFieldTypeString( const chart2::DataPointCustomLabelFieldType aType
)
3709 case chart2::DataPointCustomLabelFieldType_CATEGORYNAME
:
3710 return "CATEGORYNAME";
3712 case chart2::DataPointCustomLabelFieldType_SERIESNAME
:
3713 return "SERIESNAME";
3715 case chart2::DataPointCustomLabelFieldType_VALUE
:
3718 case chart2::DataPointCustomLabelFieldType_CELLREF
:
3721 case chart2::DataPointCustomLabelFieldType_CELLRANGE
:
3730 void writeRunProperties( ChartExport
* pChartExport
, Reference
<XPropertySet
> const & xPropertySet
)
3732 bool bDummy
= false;
3734 pChartExport
->WriteRunProperties(xPropertySet
, false, XML_rPr
, true, bDummy
, nDummy
);
3737 void writeCustomLabel( const FSHelperPtr
& pFS
, ChartExport
* pChartExport
,
3738 const Sequence
<Reference
<chart2::XDataPointCustomLabelField
>>& rCustomLabelFields
,
3739 sal_Int32 nLabelIndex
, DataLabelsRange
& rDLblsRange
)
3741 pFS
->startElement(FSNS(XML_c
, XML_tx
));
3742 pFS
->startElement(FSNS(XML_c
, XML_rich
));
3744 // TODO: body properties?
3745 pFS
->singleElement(FSNS(XML_a
, XML_bodyPr
));
3747 OUString sFieldType
;
3749 pFS
->startElement(FSNS(XML_a
, XML_p
));
3751 for (auto& rField
: rCustomLabelFields
)
3753 Reference
<XPropertySet
> xPropertySet(rField
, UNO_QUERY
);
3754 chart2::DataPointCustomLabelFieldType aType
= rField
->getFieldType();
3757 bool bNewParagraph
= false;
3759 if (aType
== chart2::DataPointCustomLabelFieldType_CELLRANGE
&&
3760 rField
->getDataLabelsRange())
3762 if (rDLblsRange
.getRange().isEmpty())
3763 rDLblsRange
.setRange(rField
->getCellRange());
3765 if (!rDLblsRange
.hasLabel(nLabelIndex
))
3766 rDLblsRange
.setLabel(nLabelIndex
, rField
->getString());
3768 sContent
= "[CELLRANGE]";
3772 sContent
= rField
->getString();
3775 if (aType
== chart2::DataPointCustomLabelFieldType_NEWLINE
)
3776 bNewParagraph
= true;
3777 else if (aType
!= chart2::DataPointCustomLabelFieldType_TEXT
)
3778 sFieldType
= getFieldTypeString(aType
);
3782 pFS
->endElement(FSNS(XML_a
, XML_p
));
3783 pFS
->startElement(FSNS(XML_a
, XML_p
));
3787 if (sFieldType
.isEmpty())
3790 pFS
->startElement(FSNS(XML_a
, XML_r
));
3791 writeRunProperties(pChartExport
, xPropertySet
);
3793 pFS
->startElement(FSNS(XML_a
, XML_t
));
3794 pFS
->writeEscaped(sContent
);
3795 pFS
->endElement(FSNS(XML_a
, XML_t
));
3797 pFS
->endElement(FSNS(XML_a
, XML_r
));
3802 pFS
->startElement(FSNS(XML_a
, XML_fld
), XML_id
, rField
->getGuid(), XML_type
,
3804 writeRunProperties(pChartExport
, xPropertySet
);
3806 pFS
->startElement(FSNS(XML_a
, XML_t
));
3807 pFS
->writeEscaped(sContent
);
3808 pFS
->endElement(FSNS(XML_a
, XML_t
));
3810 pFS
->endElement(FSNS(XML_a
, XML_fld
));
3814 pFS
->endElement(FSNS(XML_a
, XML_p
));
3815 pFS
->endElement(FSNS(XML_c
, XML_rich
));
3816 pFS
->endElement(FSNS(XML_c
, XML_tx
));
3819 void writeLabelProperties( const FSHelperPtr
& pFS
, ChartExport
* pChartExport
,
3820 const uno::Reference
<beans::XPropertySet
>& xPropSet
, const LabelPlacementParam
& rLabelParam
,
3821 sal_Int32 nLabelIndex
, DataLabelsRange
& rDLblsRange
)
3826 chart2::DataPointLabel aLabel
;
3827 Sequence
<Reference
<chart2::XDataPointCustomLabelField
>> aCustomLabelFields
;
3828 sal_Int32 nLabelBorderWidth
= 0;
3829 sal_Int32 nLabelBorderColor
= 0x00FFFFFF;
3830 sal_Int32 nLabelFillColor
= -1;
3832 xPropSet
->getPropertyValue("Label") >>= aLabel
;
3833 xPropSet
->getPropertyValue("CustomLabelFields") >>= aCustomLabelFields
;
3834 xPropSet
->getPropertyValue("LabelBorderWidth") >>= nLabelBorderWidth
;
3835 xPropSet
->getPropertyValue("LabelBorderColor") >>= nLabelBorderColor
;
3836 xPropSet
->getPropertyValue("LabelFillColor") >>= nLabelFillColor
;
3838 if (nLabelBorderWidth
> 0 || nLabelFillColor
!= -1)
3840 pFS
->startElement(FSNS(XML_c
, XML_spPr
));
3842 if (nLabelFillColor
!= -1)
3844 pFS
->startElement(FSNS(XML_a
, XML_solidFill
));
3846 OString aStr
= OString::number(nLabelFillColor
, 16).toAsciiUpperCase();
3847 pFS
->singleElement(FSNS(XML_a
, XML_srgbClr
), XML_val
, aStr
);
3849 pFS
->endElement(FSNS(XML_a
, XML_solidFill
));
3852 if (nLabelBorderWidth
> 0)
3854 pFS
->startElement(FSNS(XML_a
, XML_ln
), XML_w
,
3855 OString::number(convertHmmToEmu(nLabelBorderWidth
)));
3857 if (nLabelBorderColor
!= -1)
3859 pFS
->startElement(FSNS(XML_a
, XML_solidFill
));
3861 OString aStr
= OString::number(nLabelBorderColor
, 16).toAsciiUpperCase();
3862 pFS
->singleElement(FSNS(XML_a
, XML_srgbClr
), XML_val
, aStr
);
3864 pFS
->endElement(FSNS(XML_a
, XML_solidFill
));
3867 pFS
->endElement(FSNS(XML_a
, XML_ln
));
3870 pFS
->endElement(FSNS(XML_c
, XML_spPr
));
3873 pChartExport
->exportTextProps(xPropSet
);
3875 if (aCustomLabelFields
.hasElements())
3876 writeCustomLabel(pFS
, pChartExport
, aCustomLabelFields
, nLabelIndex
, rDLblsRange
);
3878 if (rLabelParam
.mbExport
)
3880 sal_Int32 nLabelPlacement
= rLabelParam
.meDefault
;
3881 if (xPropSet
->getPropertyValue("LabelPlacement") >>= nLabelPlacement
)
3883 if (!rLabelParam
.maAllowedValues
.count(nLabelPlacement
))
3884 nLabelPlacement
= rLabelParam
.meDefault
;
3885 pFS
->singleElement(FSNS(XML_c
, XML_dLblPos
), XML_val
, toOOXMLPlacement(nLabelPlacement
));
3889 pFS
->singleElement(FSNS(XML_c
, XML_showLegendKey
), XML_val
, ToPsz10(aLabel
.ShowLegendSymbol
));
3890 pFS
->singleElement(FSNS(XML_c
, XML_showVal
), XML_val
, ToPsz10(aLabel
.ShowNumber
));
3891 pFS
->singleElement(FSNS(XML_c
, XML_showCatName
), XML_val
, ToPsz10(aLabel
.ShowCategoryName
));
3892 pFS
->singleElement(FSNS(XML_c
, XML_showSerName
), XML_val
, ToPsz10(aLabel
.ShowSeriesName
));
3893 pFS
->singleElement(FSNS(XML_c
, XML_showPercent
), XML_val
, ToPsz10(aLabel
.ShowNumberInPercent
));
3895 // Export the text "separator" if exists
3896 uno::Any aAny
= xPropSet
->getPropertyValue("LabelSeparator");
3897 if( aAny
.hasValue() )
3899 OUString nLabelSeparator
;
3900 aAny
>>= nLabelSeparator
;
3901 pFS
->startElement(FSNS(XML_c
, XML_separator
));
3902 pFS
->writeEscaped( nLabelSeparator
);
3903 pFS
->endElement( FSNS( XML_c
, XML_separator
) );
3906 if (rDLblsRange
.hasLabel(nLabelIndex
))
3908 pFS
->startElement(FSNS(XML_c
, XML_extLst
));
3909 pFS
->startElement(FSNS(XML_c
, XML_ext
), XML_uri
,
3910 "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}", FSNS(XML_xmlns
, XML_c15
),
3911 pChartExport
->GetFB()->getNamespaceURL(OOX_NS(c15
)));
3913 pFS
->singleElement(FSNS(XML_c15
, XML_showDataLabelsRange
), XML_val
, "1");
3915 pFS
->endElement(FSNS(XML_c
, XML_ext
));
3916 pFS
->endElement(FSNS(XML_c
, XML_extLst
));
3922 void ChartExport::exportDataLabels(
3923 const uno::Reference
<chart2::XDataSeries
> & xSeries
, sal_Int32 nSeriesLength
, sal_Int32 eChartType
,
3924 DataLabelsRange
& rDLblsRange
)
3926 if (!xSeries
.is() || nSeriesLength
<= 0)
3929 uno::Reference
<beans::XPropertySet
> xPropSet(xSeries
, uno::UNO_QUERY
);
3933 FSHelperPtr pFS
= GetFS();
3934 pFS
->startElement(FSNS(XML_c
, XML_dLbls
));
3936 bool bLinkedNumFmt
= true;
3937 if (GetProperty(xPropSet
, "LinkNumberFormatToSource"))
3938 mAny
>>= bLinkedNumFmt
;
3940 chart2::DataPointLabel aLabel
;
3941 bool bLabelIsNumberFormat
= true;
3942 if( xPropSet
->getPropertyValue("Label") >>= aLabel
)
3943 bLabelIsNumberFormat
= aLabel
.ShowNumber
;
3945 if (GetProperty(xPropSet
, bLabelIsNumberFormat
? OUString("NumberFormat") : OUString("PercentageNumberFormat")))
3950 OUString aNumberFormatString
= getNumberFormatCode(nKey
);
3952 pFS
->singleElement(FSNS(XML_c
, XML_numFmt
),
3953 XML_formatCode
, aNumberFormatString
,
3954 XML_sourceLinked
, ToPsz10(bLinkedNumFmt
));
3957 uno::Sequence
<sal_Int32
> aAttrLabelIndices
;
3958 xPropSet
->getPropertyValue("AttributedDataPoints") >>= aAttrLabelIndices
;
3960 // We must not export label placement property when the chart type doesn't
3961 // support this option in MS Office, else MS Office would think the file
3962 // is corrupt & refuse to open it.
3964 const chart::TypeGroupInfo
& rInfo
= chart::GetTypeGroupInfo(static_cast<chart::TypeId
>(eChartType
));
3965 LabelPlacementParam
aParam(!mbIs3DChart
, rInfo
.mnDefLabelPos
);
3966 switch (eChartType
) // diagram chart type
3968 case chart::TYPEID_PIE
:
3969 if(getChartType() == chart::TYPEID_DOUGHNUT
)
3970 aParam
.mbExport
= false;
3972 // All pie charts support label placement.
3973 aParam
.mbExport
= true;
3975 case chart::TYPEID_AREA
:
3976 case chart::TYPEID_RADARLINE
:
3977 case chart::TYPEID_RADARAREA
:
3978 // These chart types don't support label placement.
3979 aParam
.mbExport
= false;
3981 case chart::TYPEID_BAR
:
3982 if (mbStacked
|| mbPercent
)
3984 aParam
.maAllowedValues
.clear();
3985 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::CENTER
);
3986 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::INSIDE
);
3987 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN
);
3988 aParam
.meDefault
= css::chart::DataLabelPlacement::CENTER
;
3990 else // Clustered bar chart
3992 aParam
.maAllowedValues
.clear();
3993 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::CENTER
);
3994 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::INSIDE
);
3995 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::OUTSIDE
);
3996 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN
);
3997 aParam
.meDefault
= css::chart::DataLabelPlacement::OUTSIDE
;
4004 for (const sal_Int32 nIdx
: aAttrLabelIndices
)
4006 uno::Reference
<beans::XPropertySet
> xLabelPropSet
= xSeries
->getDataPointByIndex(nIdx
);
4008 if (!xLabelPropSet
.is())
4011 pFS
->startElement(FSNS(XML_c
, XML_dLbl
));
4012 pFS
->singleElement(FSNS(XML_c
, XML_idx
), XML_val
, OString::number(nIdx
));
4014 // export custom position of data label
4015 if( eChartType
!= chart::TYPEID_PIE
)
4017 chart2::RelativePosition aCustomLabelPosition
;
4018 if( xLabelPropSet
->getPropertyValue("CustomLabelPosition") >>= aCustomLabelPosition
)
4020 pFS
->startElement(FSNS(XML_c
, XML_layout
));
4021 pFS
->startElement(FSNS(XML_c
, XML_manualLayout
));
4023 pFS
->singleElement(FSNS(XML_c
, XML_x
), XML_val
, OString::number(aCustomLabelPosition
.Primary
));
4024 pFS
->singleElement(FSNS(XML_c
, XML_y
), XML_val
, OString::number(aCustomLabelPosition
.Secondary
));
4026 SAL_WARN_IF(aCustomLabelPosition
.Anchor
!= css::drawing::Alignment_TOP_LEFT
, "oox", "unsupported anchor position");
4028 pFS
->endElement(FSNS(XML_c
, XML_manualLayout
));
4029 pFS
->endElement(FSNS(XML_c
, XML_layout
));
4033 if( GetProperty(xLabelPropSet
, "LinkNumberFormatToSource") )
4034 mAny
>>= bLinkedNumFmt
;
4036 if( xLabelPropSet
->getPropertyValue("Label") >>= aLabel
)
4037 bLabelIsNumberFormat
= aLabel
.ShowNumber
;
4039 bLabelIsNumberFormat
= true;
4041 if (GetProperty(xLabelPropSet
, bLabelIsNumberFormat
? OUString("NumberFormat") : OUString("PercentageNumberFormat")))
4046 OUString aNumberFormatString
= getNumberFormatCode(nKey
);
4048 pFS
->singleElement(FSNS(XML_c
, XML_numFmt
), XML_formatCode
, aNumberFormatString
,
4049 XML_sourceLinked
, ToPsz10(bLinkedNumFmt
));
4052 // Individual label property that overwrites the baseline.
4053 writeLabelProperties(pFS
, this, xLabelPropSet
, aParam
, nIdx
, rDLblsRange
);
4054 pFS
->endElement(FSNS(XML_c
, XML_dLbl
));
4057 // Baseline label properties for all labels.
4058 writeLabelProperties(pFS
, this, xPropSet
, aParam
, -1, rDLblsRange
);
4060 bool bShowLeaderLines
= false;
4061 xPropSet
->getPropertyValue("ShowCustomLeaderLines") >>= bShowLeaderLines
;
4062 pFS
->singleElement(FSNS(XML_c
, XML_showLeaderLines
), XML_val
, ToPsz10(bShowLeaderLines
));
4064 // Export leader line
4065 if( eChartType
!= chart::TYPEID_PIE
)
4067 pFS
->startElement(FSNS(XML_c
, XML_extLst
));
4068 pFS
->startElement(FSNS(XML_c
, XML_ext
), XML_uri
, "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}", FSNS(XML_xmlns
, XML_c15
), GetFB()->getNamespaceURL(OOX_NS(c15
)));
4069 pFS
->singleElement(FSNS(XML_c15
, XML_showLeaderLines
), XML_val
, ToPsz10(bShowLeaderLines
));
4070 pFS
->endElement(FSNS(XML_c
, XML_ext
));
4071 pFS
->endElement(FSNS(XML_c
, XML_extLst
));
4073 pFS
->endElement(FSNS(XML_c
, XML_dLbls
));
4076 void ChartExport::exportDataPoints(
4077 const uno::Reference
< beans::XPropertySet
> & xSeriesProperties
,
4078 sal_Int32 nSeriesLength
, sal_Int32 eChartType
)
4080 uno::Reference
< chart2::XDataSeries
> xSeries( xSeriesProperties
, uno::UNO_QUERY
);
4081 bool bVaryColorsByPoint
= false;
4082 Sequence
< sal_Int32
> aDataPointSeq
;
4083 if( xSeriesProperties
.is())
4085 Any aAny
= xSeriesProperties
->getPropertyValue( "AttributedDataPoints" );
4086 aAny
>>= aDataPointSeq
;
4087 xSeriesProperties
->getPropertyValue( "VaryColorsByPoint" ) >>= bVaryColorsByPoint
;
4090 const sal_Int32
* pPoints
= aDataPointSeq
.getConstArray();
4092 Reference
< chart2::XColorScheme
> xColorScheme
;
4093 if( mxNewDiagram
.is())
4094 xColorScheme
.set( mxNewDiagram
->getDefaultColorScheme());
4096 if( bVaryColorsByPoint
&& xColorScheme
.is() )
4098 o3tl::sorted_vector
< sal_Int32
> aAttrPointSet
;
4099 aAttrPointSet
.reserve(aDataPointSeq
.getLength());
4100 for (auto p
= pPoints
; p
< pPoints
+ aDataPointSeq
.getLength(); ++p
)
4101 aAttrPointSet
.insert(*p
);
4102 const auto aEndIt
= aAttrPointSet
.end();
4103 for( nElement
= 0; nElement
< nSeriesLength
; ++nElement
)
4105 uno::Reference
< beans::XPropertySet
> xPropSet
;
4106 if( aAttrPointSet
.find( nElement
) != aEndIt
)
4110 xPropSet
= SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
4111 xSeries
, nElement
, getModel() );
4113 catch( const uno::Exception
& )
4115 DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
4120 // property set only containing the color
4121 xPropSet
.set( new ColorPropertySet( ColorTransparency
, xColorScheme
->getColorByIndex( nElement
)));
4126 FSHelperPtr pFS
= GetFS();
4127 pFS
->startElement(FSNS(XML_c
, XML_dPt
));
4128 pFS
->singleElement(FSNS(XML_c
, XML_idx
), XML_val
, OString::number(nElement
));
4132 case chart::TYPEID_PIE
:
4133 case chart::TYPEID_DOUGHNUT
:
4135 if( xPropSet
.is() && GetProperty( xPropSet
, "SegmentOffset") )
4137 sal_Int32 nOffset
= 0;
4140 pFS
->singleElement( FSNS( XML_c
, XML_explosion
),
4141 XML_val
, OString::number( nOffset
) );
4148 exportShapeProps( xPropSet
);
4150 pFS
->endElement( FSNS( XML_c
, XML_dPt
) );
4155 // Export Data Point Property in Charts even if the VaryColors is false
4156 if( bVaryColorsByPoint
)
4159 o3tl::sorted_vector
< sal_Int32
> aAttrPointSet
;
4160 aAttrPointSet
.reserve(aDataPointSeq
.getLength());
4161 for (auto p
= pPoints
; p
< pPoints
+ aDataPointSeq
.getLength(); ++p
)
4162 aAttrPointSet
.insert(*p
);
4163 const auto aEndIt
= aAttrPointSet
.end();
4164 for( nElement
= 0; nElement
< nSeriesLength
; ++nElement
)
4166 uno::Reference
< beans::XPropertySet
> xPropSet
;
4167 if( aAttrPointSet
.find( nElement
) != aEndIt
)
4171 xPropSet
= SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
4172 xSeries
, nElement
, getModel() );
4174 catch( const uno::Exception
& )
4176 DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
4182 FSHelperPtr pFS
= GetFS();
4183 pFS
->startElement(FSNS(XML_c
, XML_dPt
));
4184 pFS
->singleElement(FSNS(XML_c
, XML_idx
), XML_val
, OString::number(nElement
));
4186 switch( eChartType
)
4188 case chart::TYPEID_BUBBLE
:
4189 case chart::TYPEID_HORBAR
:
4190 case chart::TYPEID_BAR
:
4191 pFS
->singleElement(FSNS(XML_c
, XML_invertIfNegative
), XML_val
, "0");
4192 exportShapeProps(xPropSet
);
4195 case chart::TYPEID_LINE
:
4196 case chart::TYPEID_SCATTER
:
4197 case chart::TYPEID_RADARLINE
:
4198 exportMarker(xPropSet
);
4202 exportShapeProps(xPropSet
);
4206 pFS
->endElement( FSNS( XML_c
, XML_dPt
) );
4211 void ChartExport::exportAxesId(bool bPrimaryAxes
, bool bCheckCombinedAxes
)
4213 sal_Int32 nAxisIdx
, nAxisIdy
;
4214 bool bPrimaryAxisExists
= false;
4215 bool bSecondaryAxisExists
= false;
4216 // let's check which axis already exists and which axis is attached to the actual dataseries
4217 if (maAxes
.size() >= 2)
4219 bPrimaryAxisExists
= bPrimaryAxes
&& maAxes
[1].nAxisType
== AXIS_PRIMARY_Y
;
4220 bSecondaryAxisExists
= !bPrimaryAxes
&& maAxes
[1].nAxisType
== AXIS_SECONDARY_Y
;
4222 // tdf#114181 keep axes of combined charts
4223 if ( bCheckCombinedAxes
&& ( bPrimaryAxisExists
|| bSecondaryAxisExists
) )
4225 nAxisIdx
= maAxes
[0].nAxisId
;
4226 nAxisIdy
= maAxes
[1].nAxisId
;
4230 nAxisIdx
= lcl_generateRandomValue();
4231 nAxisIdy
= lcl_generateRandomValue();
4232 AxesType eXAxis
= bPrimaryAxes
? AXIS_PRIMARY_X
: AXIS_SECONDARY_X
;
4233 AxesType eYAxis
= bPrimaryAxes
? AXIS_PRIMARY_Y
: AXIS_SECONDARY_Y
;
4234 maAxes
.emplace_back( eXAxis
, nAxisIdx
, nAxisIdy
);
4235 maAxes
.emplace_back( eYAxis
, nAxisIdy
, nAxisIdx
);
4237 FSHelperPtr pFS
= GetFS();
4238 pFS
->singleElement(FSNS(XML_c
, XML_axId
), XML_val
, OString::number(nAxisIdx
));
4239 pFS
->singleElement(FSNS(XML_c
, XML_axId
), XML_val
, OString::number(nAxisIdy
));
4242 sal_Int32 nAxisIdz
= 0;
4243 if( isDeep3dChart() )
4245 nAxisIdz
= lcl_generateRandomValue();
4246 maAxes
.emplace_back( AXIS_PRIMARY_Z
, nAxisIdz
, nAxisIdy
);
4248 pFS
->singleElement(FSNS(XML_c
, XML_axId
), XML_val
, OString::number(nAxisIdz
));
4252 void ChartExport::exportGrouping( bool isBar
)
4254 FSHelperPtr pFS
= GetFS();
4255 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
4257 if( GetProperty( xPropSet
, "Stacked" ) )
4259 if( GetProperty( xPropSet
, "Percent" ) )
4262 const char* grouping
= nullptr;
4264 grouping
= "stacked";
4266 grouping
= "percentStacked";
4269 if( isBar
&& !isDeep3dChart() )
4271 grouping
= "clustered";
4274 grouping
= "standard";
4276 pFS
->singleElement(FSNS(XML_c
, XML_grouping
), XML_val
, grouping
);
4279 void ChartExport::exportTrendlines( const Reference
< chart2::XDataSeries
>& xSeries
)
4281 FSHelperPtr pFS
= GetFS();
4282 Reference
< chart2::XRegressionCurveContainer
> xRegressionCurveContainer( xSeries
, UNO_QUERY
);
4283 if( !xRegressionCurveContainer
.is() )
4286 const Sequence
< Reference
< chart2::XRegressionCurve
> > aRegCurveSeq
= xRegressionCurveContainer
->getRegressionCurves();
4287 for( const Reference
< chart2::XRegressionCurve
>& xRegCurve
: aRegCurveSeq
)
4289 if (!xRegCurve
.is())
4292 Reference
< XPropertySet
> xProperties( xRegCurve
, uno::UNO_QUERY
);
4295 Reference
< lang::XServiceName
> xServiceName( xProperties
, UNO_QUERY
);
4296 if( !xServiceName
.is() )
4299 aService
= xServiceName
->getServiceName();
4301 if(aService
!= "com.sun.star.chart2.LinearRegressionCurve" &&
4302 aService
!= "com.sun.star.chart2.ExponentialRegressionCurve" &&
4303 aService
!= "com.sun.star.chart2.LogarithmicRegressionCurve" &&
4304 aService
!= "com.sun.star.chart2.PotentialRegressionCurve" &&
4305 aService
!= "com.sun.star.chart2.PolynomialRegressionCurve" &&
4306 aService
!= "com.sun.star.chart2.MovingAverageRegressionCurve")
4309 pFS
->startElement(FSNS(XML_c
, XML_trendline
));
4312 xProperties
->getPropertyValue("CurveName") >>= aName
;
4313 if(!aName
.isEmpty())
4315 pFS
->startElement(FSNS(XML_c
, XML_name
));
4316 pFS
->writeEscaped(aName
);
4317 pFS
->endElement( FSNS( XML_c
, XML_name
) );
4320 exportShapeProps( xProperties
);
4322 if( aService
== "com.sun.star.chart2.LinearRegressionCurve" )
4324 pFS
->singleElement(FSNS(XML_c
, XML_trendlineType
), XML_val
, "linear");
4326 else if( aService
== "com.sun.star.chart2.ExponentialRegressionCurve" )
4328 pFS
->singleElement(FSNS(XML_c
, XML_trendlineType
), XML_val
, "exp");
4330 else if( aService
== "com.sun.star.chart2.LogarithmicRegressionCurve" )
4332 pFS
->singleElement(FSNS(XML_c
, XML_trendlineType
), XML_val
, "log");
4334 else if( aService
== "com.sun.star.chart2.PotentialRegressionCurve" )
4336 pFS
->singleElement(FSNS(XML_c
, XML_trendlineType
), XML_val
, "power");
4338 else if( aService
== "com.sun.star.chart2.PolynomialRegressionCurve" )
4340 pFS
->singleElement(FSNS(XML_c
, XML_trendlineType
), XML_val
, "poly");
4342 sal_Int32 aDegree
= 2;
4343 xProperties
->getPropertyValue( "PolynomialDegree") >>= aDegree
;
4344 pFS
->singleElement(FSNS(XML_c
, XML_order
), XML_val
, OString::number(aDegree
));
4346 else if( aService
== "com.sun.star.chart2.MovingAverageRegressionCurve" )
4348 pFS
->singleElement(FSNS(XML_c
, XML_trendlineType
), XML_val
, "movingAvg");
4350 sal_Int32 aPeriod
= 2;
4351 xProperties
->getPropertyValue( "MovingAveragePeriod") >>= aPeriod
;
4353 pFS
->singleElement(FSNS(XML_c
, XML_period
), XML_val
, OString::number(aPeriod
));
4357 // should never happen
4358 // This would produce invalid OOXML files so we check earlier for the type
4362 double fExtrapolateForward
= 0.0;
4363 double fExtrapolateBackward
= 0.0;
4365 xProperties
->getPropertyValue("ExtrapolateForward") >>= fExtrapolateForward
;
4366 xProperties
->getPropertyValue("ExtrapolateBackward") >>= fExtrapolateBackward
;
4368 pFS
->singleElement( FSNS( XML_c
, XML_forward
),
4369 XML_val
, OString::number(fExtrapolateForward
) );
4371 pFS
->singleElement( FSNS( XML_c
, XML_backward
),
4372 XML_val
, OString::number(fExtrapolateBackward
) );
4374 bool bForceIntercept
= false;
4375 xProperties
->getPropertyValue("ForceIntercept") >>= bForceIntercept
;
4377 if (bForceIntercept
)
4379 double fInterceptValue
= 0.0;
4380 xProperties
->getPropertyValue("InterceptValue") >>= fInterceptValue
;
4382 pFS
->singleElement( FSNS( XML_c
, XML_intercept
),
4383 XML_val
, OString::number(fInterceptValue
) );
4386 // Equation properties
4387 Reference
< XPropertySet
> xEquationProperties( xRegCurve
->getEquationProperties() );
4390 bool bShowEquation
= false;
4391 xEquationProperties
->getPropertyValue("ShowEquation") >>= bShowEquation
;
4394 bool bShowCorrelationCoefficient
= false;
4395 xEquationProperties
->getPropertyValue("ShowCorrelationCoefficient") >>= bShowCorrelationCoefficient
;
4397 pFS
->singleElement( FSNS( XML_c
, XML_dispRSqr
),
4398 XML_val
, ToPsz10(bShowCorrelationCoefficient
) );
4400 pFS
->singleElement(FSNS(XML_c
, XML_dispEq
), XML_val
, ToPsz10(bShowEquation
));
4402 pFS
->endElement( FSNS( XML_c
, XML_trendline
) );
4406 void ChartExport::exportMarker(const Reference
< XPropertySet
>& xPropSet
)
4408 chart2::Symbol aSymbol
;
4409 if( GetProperty( xPropSet
, "Symbol" ) )
4412 if(aSymbol
.Style
!= chart2::SymbolStyle_STANDARD
&& aSymbol
.Style
!= chart2::SymbolStyle_NONE
)
4415 FSHelperPtr pFS
= GetFS();
4416 pFS
->startElement(FSNS(XML_c
, XML_marker
));
4418 sal_Int32 nSymbol
= aSymbol
.StandardSymbol
;
4419 // TODO: more properties support for marker
4420 const char* pSymbolType
; // no initialization here, to let compiler warn if we have a code path
4421 // where it stays uninitialized
4425 pSymbolType
= "square";
4428 pSymbolType
= "diamond";
4434 pSymbolType
= "triangle";
4437 pSymbolType
= "circle";
4440 pSymbolType
= "star";
4443 pSymbolType
= "x"; // in MS office 2010 built in symbol marker 'X' is represented as 'x'
4446 pSymbolType
= "plus";
4449 pSymbolType
= "dash";
4452 pSymbolType
= "square";
4456 bool bSkipFormatting
= false;
4457 if (aSymbol
.Style
== chart2::SymbolStyle_NONE
)
4459 bSkipFormatting
= true;
4460 pSymbolType
= "none";
4463 pFS
->singleElement(FSNS(XML_c
, XML_symbol
), XML_val
, pSymbolType
);
4465 if (!bSkipFormatting
)
4467 awt::Size aSymbolSize
= aSymbol
.Size
;
4468 sal_Int32 nSize
= std::max( aSymbolSize
.Width
, aSymbolSize
.Height
);
4470 nSize
= nSize
/250.0*7.0 + 1; // just guessed based on some test cases,
4471 //the value is always 1 less than the actual value.
4472 nSize
= std::clamp( int(nSize
), 2, 72 );
4473 pFS
->singleElement(FSNS(XML_c
, XML_size
), XML_val
, OString::number(nSize
));
4475 pFS
->startElement(FSNS(XML_c
, XML_spPr
));
4477 util::Color aColor
= aSymbol
.FillColor
;
4478 if (GetProperty(xPropSet
, "Color"))
4483 pFS
->singleElement(FSNS(XML_a
, XML_noFill
));
4486 WriteSolidFill(::Color(ColorTransparency
, aColor
));
4488 pFS
->endElement( FSNS( XML_c
, XML_spPr
) );
4491 pFS
->endElement( FSNS( XML_c
, XML_marker
) );
4494 void ChartExport::exportSmooth()
4496 FSHelperPtr pFS
= GetFS();
4497 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
4498 sal_Int32 nSplineType
= 0;
4499 if( GetProperty( xPropSet
, "SplineType" ) )
4500 mAny
>>= nSplineType
;
4501 const char* pVal
= nSplineType
!= 0 ? "1" : "0";
4502 pFS
->singleElement(FSNS(XML_c
, XML_smooth
), XML_val
, pVal
);
4505 void ChartExport::exportFirstSliceAng( )
4507 FSHelperPtr pFS
= GetFS();
4508 sal_Int32 nStartingAngle
= 0;
4509 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
4510 if( GetProperty( xPropSet
, "StartingAngle" ) )
4511 mAny
>>= nStartingAngle
;
4513 // convert to ooxml angle
4514 nStartingAngle
= (450 - nStartingAngle
) % 360;
4515 pFS
->singleElement(FSNS(XML_c
, XML_firstSliceAng
), XML_val
, OString::number(nStartingAngle
));
4520 const char* getErrorBarStyle(sal_Int32 nErrorBarStyle
)
4522 switch(nErrorBarStyle
)
4524 case cssc::ErrorBarStyle::NONE
:
4526 case cssc::ErrorBarStyle::VARIANCE
:
4528 case cssc::ErrorBarStyle::STANDARD_DEVIATION
:
4530 case cssc::ErrorBarStyle::ABSOLUTE
:
4532 case cssc::ErrorBarStyle::RELATIVE
:
4533 return "percentage";
4534 case cssc::ErrorBarStyle::ERROR_MARGIN
:
4536 case cssc::ErrorBarStyle::STANDARD_ERROR
:
4538 case cssc::ErrorBarStyle::FROM_DATA
:
4541 assert(false && "can't happen");
4546 Reference
< chart2::data::XDataSequence
> getLabeledSequence(
4547 const uno::Sequence
< uno::Reference
< chart2::data::XLabeledDataSequence
> >& aSequences
,
4550 OUString aDirection
;
4552 aDirection
= "positive";
4554 aDirection
= "negative";
4556 for( const auto& rSequence
: aSequences
)
4560 uno::Reference
< chart2::data::XDataSequence
> xSequence( rSequence
->getValues());
4561 uno::Reference
< beans::XPropertySet
> xSeqProp( xSequence
, uno::UNO_QUERY_THROW
);
4563 if( ( xSeqProp
->getPropertyValue( "Role" ) >>= aRole
) &&
4564 aRole
.match( "error-bars" ) && aRole
.indexOf(aDirection
) >= 0 )
4571 return Reference
< chart2::data::XDataSequence
> ();
4576 void ChartExport::exportErrorBar(const Reference
< XPropertySet
>& xErrorBarProps
, bool bYError
)
4578 sal_Int32 nErrorBarStyle
= cssc::ErrorBarStyle::NONE
;
4579 xErrorBarProps
->getPropertyValue("ErrorBarStyle") >>= nErrorBarStyle
;
4580 const char* pErrorBarStyle
= getErrorBarStyle(nErrorBarStyle
);
4584 FSHelperPtr pFS
= GetFS();
4585 pFS
->startElement(FSNS(XML_c
, XML_errBars
));
4586 pFS
->singleElement(FSNS(XML_c
, XML_errDir
), XML_val
, bYError
? "y" : "x");
4587 bool bPositive
= false, bNegative
= false;
4588 xErrorBarProps
->getPropertyValue("ShowPositiveError") >>= bPositive
;
4589 xErrorBarProps
->getPropertyValue("ShowNegativeError") >>= bNegative
;
4590 const char* pErrBarType
;
4591 if(bPositive
&& bNegative
)
4592 pErrBarType
= "both";
4594 pErrBarType
= "plus";
4596 pErrBarType
= "minus";
4599 // what the hell should we do now?
4600 // at least this makes the file valid
4601 pErrBarType
= "both";
4603 pFS
->singleElement(FSNS(XML_c
, XML_errBarType
), XML_val
, pErrBarType
);
4604 pFS
->singleElement(FSNS(XML_c
, XML_errValType
), XML_val
, pErrorBarStyle
);
4605 pFS
->singleElement(FSNS(XML_c
, XML_noEndCap
), XML_val
, "0");
4606 if(nErrorBarStyle
== cssc::ErrorBarStyle::FROM_DATA
)
4608 uno::Reference
< chart2::data::XDataSource
> xDataSource(xErrorBarProps
, uno::UNO_QUERY
);
4609 Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aSequences
=
4610 xDataSource
->getDataSequences();
4614 exportSeriesValues(getLabeledSequence(aSequences
, true), XML_plus
);
4619 exportSeriesValues(getLabeledSequence(aSequences
, false), XML_minus
);
4625 if(nErrorBarStyle
== cssc::ErrorBarStyle::STANDARD_DEVIATION
)
4627 xErrorBarProps
->getPropertyValue("Weight") >>= nVal
;
4632 xErrorBarProps
->getPropertyValue("PositiveError") >>= nVal
;
4634 xErrorBarProps
->getPropertyValue("NegativeError") >>= nVal
;
4637 pFS
->singleElement(FSNS(XML_c
, XML_val
), XML_val
, OString::number(nVal
));
4640 exportShapeProps( xErrorBarProps
);
4642 pFS
->endElement( FSNS( XML_c
, XML_errBars
) );
4645 void ChartExport::exportView3D()
4647 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
4648 if( !xPropSet
.is() )
4650 FSHelperPtr pFS
= GetFS();
4651 pFS
->startElement(FSNS(XML_c
, XML_view3D
));
4652 sal_Int32 eChartType
= getChartType( );
4654 if( GetProperty( xPropSet
, "RotationHorizontal" ) )
4656 sal_Int32 nRotationX
= 0;
4657 mAny
>>= nRotationX
;
4658 if( nRotationX
< 0 )
4660 if(eChartType
== chart::TYPEID_PIE
)
4662 /* In OOXML we get value in 0..90 range for pie chart X rotation , whereas we expect it to be in -90..90 range,
4663 so we convert that during import. It is modified in View3DConverter::convertFromModel()
4664 here we convert it back to 0..90 as we received in import */
4665 nRotationX
+= 90; // X rotation (map Chart2 [-179,180] to OOXML [0..90])
4668 nRotationX
+= 360; // X rotation (map Chart2 [-179,180] to OOXML [-90..90])
4670 pFS
->singleElement(FSNS(XML_c
, XML_rotX
), XML_val
, OString::number(nRotationX
));
4673 if( GetProperty( xPropSet
, "RotationVertical" ) )
4675 // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
4676 if( eChartType
== chart::TYPEID_PIE
&& GetProperty( xPropSet
, "StartingAngle" ) )
4678 // Y rotation used as 'first pie slice angle' in 3D pie charts
4679 sal_Int32 nStartingAngle
=0;
4680 mAny
>>= nStartingAngle
;
4681 // convert to ooxml angle
4682 nStartingAngle
= (450 - nStartingAngle
) % 360;
4683 pFS
->singleElement(FSNS(XML_c
, XML_rotY
), XML_val
, OString::number(nStartingAngle
));
4687 sal_Int32 nRotationY
= 0;
4688 mAny
>>= nRotationY
;
4689 // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
4690 if( nRotationY
< 0 )
4692 pFS
->singleElement(FSNS(XML_c
, XML_rotY
), XML_val
, OString::number(nRotationY
));
4696 if( GetProperty( xPropSet
, "RightAngledAxes" ) )
4698 bool bRightAngled
= false;
4699 mAny
>>= bRightAngled
;
4700 const char* sRightAngled
= bRightAngled
? "1":"0";
4701 pFS
->singleElement(FSNS(XML_c
, XML_rAngAx
), XML_val
, sRightAngled
);
4704 if( GetProperty( xPropSet
, "Perspective" ) )
4706 sal_Int32 nPerspective
= 0;
4707 mAny
>>= nPerspective
;
4708 // map Chart2 [0,100] to OOXML [0..200]
4710 pFS
->singleElement(FSNS(XML_c
, XML_perspective
), XML_val
, OString::number(nPerspective
));
4712 pFS
->endElement( FSNS( XML_c
, XML_view3D
) );
4715 bool ChartExport::isDeep3dChart()
4717 bool isDeep
= false;
4720 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
4721 if( GetProperty( xPropSet
, "Deep" ) )
4727 OUString
ChartExport::getNumberFormatCode(sal_Int32 nKey
) const
4729 /* XXX if this was called more than one or two times per export the two
4730 * SvNumberFormatter instances and NfKeywordTable should be member
4731 * variables and initialized only once. */
4733 OUString
aCode("General"); // init with fallback
4734 uno::Reference
<util::XNumberFormatsSupplier
> xNumberFormatsSupplier(mxChartModel
, uno::UNO_QUERY_THROW
);
4735 SvNumberFormatsSupplierObj
* pSupplierObj
= comphelper::getFromUnoTunnel
<SvNumberFormatsSupplierObj
>( xNumberFormatsSupplier
);
4739 SvNumberFormatter
* pNumberFormatter
= pSupplierObj
->GetNumberFormatter();
4740 if (!pNumberFormatter
)
4743 SvNumberFormatter
aTempFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US
);
4744 NfKeywordTable aKeywords
;
4745 aTempFormatter
.FillKeywordTableForExcel( aKeywords
);
4746 aCode
= pNumberFormatter
->GetFormatStringForExcel( nKey
, aKeywords
, aTempFormatter
);
4753 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */