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/tokens.hxx>
22 #include <oox/core/xmlfilterbase.hxx>
23 #include <oox/export/chartexport.hxx>
24 #include <oox/token/relationship.hxx>
25 #include <oox/export/utils.hxx>
26 #include <drawingml/chart/typegroupconverter.hxx>
31 #include <com/sun/star/awt/Gradient.hpp>
32 #include <com/sun/star/chart/XChartDocument.hpp>
33 #include <com/sun/star/chart/ChartLegendPosition.hpp>
34 #include <com/sun/star/chart/XTwoAxisXSupplier.hpp>
35 #include <com/sun/star/chart/XTwoAxisYSupplier.hpp>
36 #include <com/sun/star/chart/XAxisZSupplier.hpp>
37 #include <com/sun/star/chart/XChartDataArray.hpp>
38 #include <com/sun/star/chart/ChartDataRowSource.hpp>
39 #include <com/sun/star/chart/ChartAxisAssign.hpp>
40 #include <com/sun/star/chart/ChartSeriesAddress.hpp>
41 #include <com/sun/star/chart/X3DDisplay.hpp>
42 #include <com/sun/star/chart/XStatisticDisplay.hpp>
43 #include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp>
44 #include <com/sun/star/chart/ChartSymbolType.hpp>
45 #include <com/sun/star/chart/ChartAxisMarks.hpp>
46 #include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
47 #include <com/sun/star/chart/ChartAxisPosition.hpp>
48 #include <com/sun/star/chart/ChartSolidType.hpp>
49 #include <com/sun/star/chart/DataLabelPlacement.hpp>
50 #include <com/sun/star/chart/ErrorBarStyle.hpp>
51 #include <com/sun/star/chart/MissingValueTreatment.hpp>
52 #include <com/sun/star/chart/XDiagramPositioning.hpp>
54 #include <com/sun/star/chart2/RelativePosition.hpp>
55 #include <com/sun/star/chart2/RelativeSize.hpp>
56 #include <com/sun/star/chart2/XChartDocument.hpp>
57 #include <com/sun/star/chart2/XDiagram.hpp>
58 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
59 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
60 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
61 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
62 #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
63 #include <com/sun/star/chart2/DataPointLabel.hpp>
64 #include <com/sun/star/chart2/DataPointCustomLabelField.hpp>
65 #include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
66 #include <com/sun/star/chart2/Symbol.hpp>
67 #include <com/sun/star/chart2/data/XDataSource.hpp>
68 #include <com/sun/star/chart2/data/XDataSink.hpp>
69 #include <com/sun/star/chart2/data/XDataReceiver.hpp>
70 #include <com/sun/star/chart2/data/XDataProvider.hpp>
71 #include <com/sun/star/chart2/XInternalDataProvider.hpp>
72 #include <com/sun/star/chart2/data/XDatabaseDataProvider.hpp>
73 #include <com/sun/star/chart2/data/XRangeXMLConversion.hpp>
74 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
75 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
76 #include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
77 #include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
79 #include <com/sun/star/beans/XPropertySet.hpp>
80 #include <com/sun/star/drawing/XShape.hpp>
81 #include <com/sun/star/drawing/FillStyle.hpp>
82 #include <com/sun/star/drawing/LineStyle.hpp>
83 #include <com/sun/star/drawing/BitmapMode.hpp>
84 #include <com/sun/star/awt/XBitmap.hpp>
85 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
86 #include <com/sun/star/lang/XServiceName.hpp>
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/text/WritingMode.hpp>
94 #include <com/sun/star/container/XNamed.hpp>
95 #include <com/sun/star/embed/XVisualObject.hpp>
96 #include <com/sun/star/embed/Aspects.hpp>
98 #include <comphelper/processfactory.hxx>
99 #include <comphelper/random.hxx>
101 #include <xmloff/SchXMLSeriesHelper.hxx>
102 #include "ColorPropertySet.hxx"
104 #include <svl/zforlist.hxx>
105 #include <svl/numuno.hxx>
106 #include <tools/diagnose_ex.h>
107 #include <sal/log.hxx>
110 #include <unordered_set>
112 #include <rtl/math.hxx>
113 #include <o3tl/temporary.hxx>
116 using namespace css::uno
;
117 using namespace css::drawing
;
118 using namespace ::oox::core
;
119 using css::beans::PropertyValue
;
120 using css::beans::XPropertySet
;
121 using css::container::XNamed
;
122 using css::table::CellAddress
;
123 using css::sheet::XFormulaParser
;
124 using ::oox::core::XmlFilterBase
;
125 using ::sax_fastparser::FSHelperPtr
;
127 namespace cssc
= css::chart
;
129 namespace oox
{ namespace drawingml
{
133 bool isPrimaryAxes(sal_Int32 nIndex
)
135 assert(nIndex
== 0 || nIndex
== 1);
141 class lcl_MatchesRole
144 explicit lcl_MatchesRole( const OUString
& aRole
) :
148 bool operator () ( const Reference
< chart2::data::XLabeledDataSequence
> & xSeq
) const
152 Reference
< beans::XPropertySet
> xProp( xSeq
->getValues(), uno::UNO_QUERY
);
155 return ( xProp
.is() &&
156 (xProp
->getPropertyValue( "Role" ) >>= aRole
) &&
161 OUString
const m_aRole
;
164 static Reference
< chart2::data::XLabeledDataSequence
> lcl_getCategories( const Reference
< chart2::XDiagram
> & xDiagram
)
166 Reference
< chart2::data::XLabeledDataSequence
> xResult
;
169 Reference
< chart2::XCoordinateSystemContainer
> xCooSysCnt(
170 xDiagram
, uno::UNO_QUERY_THROW
);
171 const Sequence
< Reference
< chart2::XCoordinateSystem
> > aCooSysSeq(
172 xCooSysCnt
->getCoordinateSystems());
173 for( const auto& xCooSys
: aCooSysSeq
)
175 OSL_ASSERT( xCooSys
.is());
176 for( sal_Int32 nN
= xCooSys
->getDimension(); nN
--; )
178 const sal_Int32 nMaxAxisIndex
= xCooSys
->getMaximumAxisIndexByDimension(nN
);
179 for(sal_Int32 nI
=0; nI
<=nMaxAxisIndex
; ++nI
)
181 Reference
< chart2::XAxis
> xAxis
= xCooSys
->getAxisByDimension( nN
, nI
);
182 OSL_ASSERT( xAxis
.is());
185 chart2::ScaleData aScaleData
= xAxis
->getScaleData();
186 if( aScaleData
.Categories
.is())
188 xResult
.set( aScaleData
.Categories
);
196 catch( const uno::Exception
& )
198 DBG_UNHANDLED_EXCEPTION("oox");
204 static Reference
< chart2::data::XLabeledDataSequence
>
205 lcl_getDataSequenceByRole(
206 const Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > & aLabeledSeq
,
207 const OUString
& rRole
)
209 Reference
< chart2::data::XLabeledDataSequence
> aNoResult
;
211 const Reference
< chart2::data::XLabeledDataSequence
> * pBegin
= aLabeledSeq
.getConstArray();
212 const Reference
< chart2::data::XLabeledDataSequence
> * pEnd
= pBegin
+ aLabeledSeq
.getLength();
213 const Reference
< chart2::data::XLabeledDataSequence
> * pMatch
=
214 ::std::find_if( pBegin
, pEnd
, lcl_MatchesRole( rRole
));
222 static bool lcl_hasCategoryLabels( const Reference
< chart2::XChartDocument
>& xChartDoc
)
224 //categories are always the first sequence
225 Reference
< chart2::XDiagram
> xDiagram( xChartDoc
->getFirstDiagram());
226 Reference
< chart2::data::XLabeledDataSequence
> xCategories( lcl_getCategories( xDiagram
) );
227 return xCategories
.is();
230 static bool lcl_isCategoryAxisShifted(const Reference
< chart2::XChartDocument
>& xChartDoc
)
232 Reference
< chart2::XDiagram
> xDiagram(xChartDoc
->getFirstDiagram());
233 bool isCategoryPositionShifted
= false;
237 Reference
< chart2::XCoordinateSystemContainer
> xCooSysCnt(
238 xDiagram
, uno::UNO_QUERY_THROW
);
239 const Sequence
< Reference
< chart2::XCoordinateSystem
> > aCooSysSeq(
240 xCooSysCnt
->getCoordinateSystems());
241 for( const auto& xCooSys
: aCooSysSeq
)
243 OSL_ASSERT(xCooSys
.is());
244 for( sal_Int32 nN
= xCooSys
->getDimension(); nN
--; )
246 const sal_Int32 nMaxAxisIndex
= xCooSys
->getMaximumAxisIndexByDimension(nN
);
247 for( sal_Int32 nI
= 0; nI
<= nMaxAxisIndex
; ++nI
)
249 Reference
< chart2::XAxis
> xAxis
= xCooSys
->getAxisByDimension(nN
, nI
);
250 OSL_ASSERT(xAxis
.is());
253 chart2::ScaleData aScaleData
= xAxis
->getScaleData();
254 if( aScaleData
.AxisType
== AXIS_PRIMARY_Y
)
256 isCategoryPositionShifted
= aScaleData
.ShiftedCategoryPosition
;
264 catch (const uno::Exception
&)
266 DBG_UNHANDLED_EXCEPTION("oox");
269 return isCategoryPositionShifted
;
272 static bool lcl_isSeriesAttachedToFirstAxis(
273 const Reference
< chart2::XDataSeries
> & xDataSeries
)
279 sal_Int32 nAxisIndex
= 0;
280 Reference
< beans::XPropertySet
> xProp( xDataSeries
, uno::UNO_QUERY_THROW
);
281 xProp
->getPropertyValue("AttachedAxisIndex") >>= nAxisIndex
;
282 bResult
= (0==nAxisIndex
);
284 catch( const uno::Exception
& )
286 DBG_UNHANDLED_EXCEPTION("oox");
292 static OUString
lcl_flattenStringSequence( const Sequence
< OUString
> & rSequence
)
294 OUStringBuffer aResult
;
295 bool bPrecedeWithSpace
= false;
296 for( const auto& rString
: rSequence
)
298 if( !rString
.isEmpty())
300 if( bPrecedeWithSpace
)
301 aResult
.append( ' ' );
302 aResult
.append( rString
);
303 bPrecedeWithSpace
= true;
306 return aResult
.makeStringAndClear();
309 static Sequence
< OUString
> lcl_getLabelSequence( const Reference
< chart2::data::XDataSequence
> & xLabelSeq
)
311 Sequence
< OUString
> aLabels
;
313 uno::Reference
< chart2::data::XTextualDataSequence
> xTextualDataSequence( xLabelSeq
, uno::UNO_QUERY
);
314 if( xTextualDataSequence
.is())
316 aLabels
= xTextualDataSequence
->getTextualData();
318 else if( xLabelSeq
.is())
320 const Sequence
< uno::Any
> aAnies( xLabelSeq
->getData());
321 aLabels
.realloc( aAnies
.getLength());
322 for( sal_Int32 i
=0; i
<aAnies
.getLength(); ++i
)
323 aAnies
[i
] >>= aLabels
[i
];
329 static void lcl_fillCategoriesIntoStringVector(
330 const Reference
< chart2::data::XDataSequence
> & xCategories
,
331 ::std::vector
< OUString
> & rOutCategories
)
333 OSL_ASSERT( xCategories
.is());
334 if( !xCategories
.is())
336 Reference
< chart2::data::XTextualDataSequence
> xTextualDataSequence( xCategories
, uno::UNO_QUERY
);
337 if( xTextualDataSequence
.is())
339 rOutCategories
.clear();
340 Sequence
< OUString
> aTextData( xTextualDataSequence
->getTextualData());
341 ::std::copy( aTextData
.begin(), aTextData
.end(),
342 ::std::back_inserter( rOutCategories
));
346 Sequence
< uno::Any
> aAnies( xCategories
->getData());
347 rOutCategories
.resize( aAnies
.getLength());
348 for( sal_Int32 i
=0; i
<aAnies
.getLength(); ++i
)
349 aAnies
[i
] >>= rOutCategories
[i
];
353 static ::std::vector
< double > lcl_getAllValuesFromSequence( const Reference
< chart2::data::XDataSequence
> & xSeq
)
356 ::rtl::math::setNan( &fNan
);
357 ::std::vector
< double > aResult
;
359 Reference
< chart2::data::XNumericalDataSequence
> xNumSeq( xSeq
, uno::UNO_QUERY
);
362 Sequence
< double > aValues( xNumSeq
->getNumericalData());
363 ::std::copy( aValues
.begin(), aValues
.end(),
364 ::std::back_inserter( aResult
));
368 Sequence
< uno::Any
> aAnies( xSeq
->getData());
369 aResult
.resize( aAnies
.getLength(), fNan
);
370 for( sal_Int32 i
=0; i
<aAnies
.getLength(); ++i
)
371 aAnies
[i
] >>= aResult
[i
];
376 static sal_Int32
lcl_getChartType( const OUString
& sChartType
)
378 chart::TypeId eChartTypeId
= chart::TYPEID_UNKNOWN
;
379 if( sChartType
== "com.sun.star.chart.BarDiagram"
380 || sChartType
== "com.sun.star.chart2.ColumnChartType" )
381 eChartTypeId
= chart::TYPEID_BAR
;
382 else if( sChartType
== "com.sun.star.chart.AreaDiagram"
383 || sChartType
== "com.sun.star.chart2.AreaChartType" )
384 eChartTypeId
= chart::TYPEID_AREA
;
385 else if( sChartType
== "com.sun.star.chart.LineDiagram"
386 || sChartType
== "com.sun.star.chart2.LineChartType" )
387 eChartTypeId
= chart::TYPEID_LINE
;
388 else if( sChartType
== "com.sun.star.chart.PieDiagram"
389 || sChartType
== "com.sun.star.chart2.PieChartType" )
390 eChartTypeId
= chart::TYPEID_PIE
;
391 else if( sChartType
== "com.sun.star.chart.DonutDiagram"
392 || sChartType
== "com.sun.star.chart2.DonutChartType" )
393 eChartTypeId
= chart::TYPEID_DOUGHNUT
;
394 else if( sChartType
== "com.sun.star.chart.XYDiagram"
395 || sChartType
== "com.sun.star.chart2.ScatterChartType" )
396 eChartTypeId
= chart::TYPEID_SCATTER
;
397 else if( sChartType
== "com.sun.star.chart.NetDiagram"
398 || sChartType
== "com.sun.star.chart2.NetChartType" )
399 eChartTypeId
= chart::TYPEID_RADARLINE
;
400 else if( sChartType
== "com.sun.star.chart.FilledNetDiagram"
401 || sChartType
== "com.sun.star.chart2.FilledNetChartType" )
402 eChartTypeId
= chart::TYPEID_RADARAREA
;
403 else if( sChartType
== "com.sun.star.chart.StockDiagram"
404 || sChartType
== "com.sun.star.chart2.CandleStickChartType" )
405 eChartTypeId
= chart::TYPEID_STOCK
;
406 else if( sChartType
== "com.sun.star.chart.BubbleDiagram"
407 || sChartType
== "com.sun.star.chart2.BubbleChartType" )
408 eChartTypeId
= chart::TYPEID_BUBBLE
;
413 static sal_Int32
lcl_generateRandomValue()
415 return comphelper::rng::uniform_int_distribution(0, 100000000-1);
418 ChartExport::ChartExport( sal_Int32 nXmlNamespace
, FSHelperPtr pFS
, Reference
< frame::XModel
> const & xModel
, XmlFilterBase
* pFB
, DocumentType eDocumentType
)
419 : DrawingML( std::move(pFS
), pFB
, eDocumentType
)
420 , mnXmlNamespace( nXmlNamespace
)
422 , mxChartModel( xModel
)
423 , mpURLTransformer(new URLTransformer
)
424 , mbHasCategoryLabels( false )
425 , mbIsCategoryPositionShifted( false )
426 , mbHasZAxis( false )
427 , mbIs3DChart( false )
433 void ChartExport::SetURLTranslator(const std::shared_ptr
<URLTransformer
>& pTransformer
)
435 mpURLTransformer
= pTransformer
;
438 sal_Int32
ChartExport::getChartType( )
440 OUString sChartType
= mxDiagram
->getDiagramType();
441 return lcl_getChartType( sChartType
);
446 uno::Sequence
< beans::PropertyValue
> createArguments(
447 const OUString
& rRangeRepresentation
, bool bUseColumns
)
449 css::chart::ChartDataRowSource eRowSource
= css::chart::ChartDataRowSource_ROWS
;
451 eRowSource
= css::chart::ChartDataRowSource_COLUMNS
;
453 uno::Sequence
< beans::PropertyValue
> aArguments(4);
454 aArguments
[0] = beans::PropertyValue("DataRowSource"
455 , -1, uno::Any(eRowSource
)
456 , beans::PropertyState_DIRECT_VALUE
);
457 aArguments
[1] = beans::PropertyValue("FirstCellAsLabel"
458 , -1, uno::Any(false)
459 , beans::PropertyState_DIRECT_VALUE
);
460 aArguments
[2] = beans::PropertyValue("HasCategories"
461 , -1, uno::Any(false)
462 , beans::PropertyState_DIRECT_VALUE
);
463 aArguments
[3] = beans::PropertyValue("CellRangeRepresentation"
464 , -1, uno::Any(rRangeRepresentation
)
465 , beans::PropertyState_DIRECT_VALUE
);
470 Reference
<chart2::XDataSeries
> getPrimaryDataSeries(const Reference
<chart2::XChartType
>& xChartType
)
472 Reference
< chart2::XDataSeriesContainer
> xDSCnt(xChartType
, uno::UNO_QUERY_THROW
);
474 // export dataseries for current chart-type
475 const Sequence
< Reference
< chart2::XDataSeries
> > aSeriesSeq(xDSCnt
->getDataSeries());
476 for (const auto& rSeries
: aSeriesSeq
)
478 Reference
<chart2::XDataSeries
> xSource(rSeries
, uno::UNO_QUERY
);
483 return Reference
<chart2::XDataSeries
>();
488 Sequence
< Sequence
< OUString
> > ChartExport::getSplitCategoriesList( const OUString
& rRange
)
490 Reference
< chart2::XChartDocument
> xChartDoc(getModel(), uno::UNO_QUERY
);
491 OSL_ASSERT(xChartDoc
.is());
494 Reference
< chart2::data::XDataProvider
> xDataProvider(xChartDoc
->getDataProvider());
495 OSL_ENSURE(xDataProvider
.is(), "No DataProvider");
496 if (xDataProvider
.is())
498 //detect whether the first series is a row or a column
499 bool bSeriesUsesColumns
= true;
500 Reference
< chart2::XDiagram
> xDiagram(xChartDoc
->getFirstDiagram());
503 Reference
< chart2::XCoordinateSystemContainer
> xCooSysCnt(xDiagram
, uno::UNO_QUERY_THROW
);
504 const Sequence
< Reference
< chart2::XCoordinateSystem
> > aCooSysSeq(xCooSysCnt
->getCoordinateSystems());
505 for (const auto& rCooSys
: aCooSysSeq
)
507 const Reference
< chart2::XChartTypeContainer
> xCTCnt(rCooSys
, uno::UNO_QUERY_THROW
);
508 const Sequence
< Reference
< chart2::XChartType
> > aChartTypeSeq(xCTCnt
->getChartTypes());
509 for (const auto& rChartType
: aChartTypeSeq
)
511 Reference
< chart2::XDataSeries
> xDataSeries
= getPrimaryDataSeries(rChartType
);
512 if (xDataSeries
.is())
514 uno::Reference
< chart2::data::XDataSource
> xSeriesSource(xDataSeries
, uno::UNO_QUERY
);
515 const uno::Sequence
< beans::PropertyValue
> rArguments
= xDataProvider
->detectArguments(xSeriesSource
);
516 for (const beans::PropertyValue
& rProperty
: rArguments
)
518 if (rProperty
.Name
== "DataRowSource")
520 css::chart::ChartDataRowSource eRowSource
;
521 if (rProperty
.Value
>>= eRowSource
)
523 bSeriesUsesColumns
= (eRowSource
== css::chart::ChartDataRowSource_COLUMNS
);
532 catch (const uno::Exception
&)
534 DBG_UNHANDLED_EXCEPTION("chart2");
536 // detect we have an inner data table or not
537 if (xChartDoc
->hasInternalDataProvider() && rRange
== "categories")
541 css::uno::Reference
< css::chart2::XAnyDescriptionAccess
> xDataAccess(xChartDoc
->getDataProvider(), uno::UNO_QUERY
);
542 const Sequence
< Sequence
< uno::Any
> >aAnyCategories(bSeriesUsesColumns
? xDataAccess
->getAnyRowDescriptions() : xDataAccess
->getAnyColumnDescriptions());
543 auto pMax
= std::max_element(aAnyCategories
.begin(), aAnyCategories
.end(),
544 [](const Sequence
<uno::Any
>& a
, const Sequence
<uno::Any
>& b
) {
545 return a
.getLength() < b
.getLength(); });
548 if (pMax
!= aAnyCategories
.end() && pMax
->getLength() > 1)
550 sal_Int32 nLevelCount
= pMax
->getLength();
551 //we have complex categories
552 //sort the categories name
553 Sequence
<Sequence
<OUString
>>aFinalSplitSource(nLevelCount
);
554 for (sal_Int32 i
= 0; i
< nLevelCount
; i
++)
556 sal_Int32 nElemLabel
= 0;
557 aFinalSplitSource
[nLevelCount
- i
- 1].realloc(aAnyCategories
.getLength());
558 for (auto const& elemLabel
: aAnyCategories
)
560 // make sure elemLabel[i] exists!
561 if (elemLabel
.getLength() > i
)
563 aFinalSplitSource
[nLevelCount
- i
- 1][nElemLabel
] = elemLabel
[i
].get
<OUString
>();
568 return aFinalSplitSource
;
571 catch (const uno::Exception
&)
573 DBG_UNHANDLED_EXCEPTION("oox");
580 uno::Reference
< chart2::data::XDataSource
> xCategoriesSource(xDataProvider
->createDataSource(
581 createArguments(rRange
, bSeriesUsesColumns
)));
583 if (xCategoriesSource
.is())
585 const Sequence
< Reference
< chart2::data::XLabeledDataSequence
>> aCategories
= xCategoriesSource
->getDataSequences();
586 if (aCategories
.getLength() > 1)
588 //we have complex categories
589 //sort the categories name
590 Sequence
<Sequence
<OUString
>> aFinalSplitSource(aCategories
.getLength());
591 std::transform(aCategories
.begin(), aCategories
.end(),
592 std::reverse_iterator(aFinalSplitSource
.end()),
593 [](const Reference
<chart2::data::XLabeledDataSequence
>& xCat
) {
594 return lcl_getLabelSequence(xCat
->getValues()); });
595 return aFinalSplitSource
;
599 catch (const uno::Exception
&)
601 DBG_UNHANDLED_EXCEPTION("oox");
607 return Sequence
< Sequence
< OUString
>>(0);
610 OUString
ChartExport::parseFormula( const OUString
& rRange
)
613 Reference
< XFormulaParser
> xParser
;
614 uno::Reference
< lang::XMultiServiceFactory
> xSF
= GetFB()->getModelFactory();
619 xParser
.set( xSF
->createInstance("com.sun.star.sheet.FormulaParser"), UNO_QUERY
);
626 SAL_WARN_IF(!xParser
.is(), "oox", "creating formula parser failed");
630 Reference
< XPropertySet
> xParserProps( xParser
, uno::UNO_QUERY
);
631 // rRange is the result of a
632 // css::chart2::data::XDataSequence::getSourceRangeRepresentation()
633 // call that returns the range in the document's current UI notation.
634 // Creating a FormulaParser defaults to the same notation, for
635 // parseFormula() do not attempt to override the FormulaConvention
636 // property with css::sheet::AddressConvention::OOO or some such.
637 /* TODO: it would be much better to introduce a
638 * getSourceRangeRepresentation(css::sheet::AddressConvention) to
639 * return the ranges in a specific convention than converting them with
640 * the overhead of creating an XFormulaParser for each... */
641 uno::Sequence
<sheet::FormulaToken
> aTokens
= xParser
->parseFormula( rRange
, CellAddress( 0, 0, 0 ) );
642 if( xParserProps
.is() )
644 xParserProps
->setPropertyValue("FormulaConvention", uno::makeAny(css::sheet::AddressConvention::XL_OOX
) );
646 aResult
= xParser
->printFormula( aTokens
, CellAddress( 0, 0, 0 ) );
650 //FIXME: currently just using simple converter, e.g $Sheet1.$A$1:$C$1 -> Sheet1!$A$1:$C$1
651 OUString
aRange( rRange
);
652 if( aRange
.startsWith("$") )
653 aRange
= aRange
.copy(1);
654 aRange
= aRange
.replaceAll(".$", "!$" );
661 void ChartExport::WriteChartObj( const Reference
< XShape
>& xShape
, sal_Int32 nID
, sal_Int32 nChartCount
)
663 FSHelperPtr pFS
= GetFS();
665 Reference
< XPropertySet
> xShapeProps( xShape
, UNO_QUERY
);
667 pFS
->startElementNS(mnXmlNamespace
, XML_graphicFrame
);
669 pFS
->startElementNS(mnXmlNamespace
, XML_nvGraphicFramePr
);
671 // TODO: get the correct chart name chart id
672 OUString sName
= "Object 1";
673 Reference
< XNamed
> xNamed( xShape
, UNO_QUERY
);
675 sName
= xNamed
->getName();
677 pFS
->startElementNS( mnXmlNamespace
, XML_cNvPr
,
678 XML_id
, OString::number(nID
),
679 XML_name
, sName
.toUtf8());
682 if ( GetProperty( xShapeProps
, "URL" ) )
684 if( !sURL
.isEmpty() )
686 OUString sRelId
= mpFB
->addRelation( mpFS
->getOutputStream(),
687 oox::getRelationship(Relationship::HYPERLINK
),
688 mpURLTransformer
->getTransformedString(sURL
),
689 mpURLTransformer
->isExternalURL(sURL
));
691 mpFS
->singleElementNS( XML_a
, XML_hlinkClick
,
692 FSNS( XML_r
,XML_id
), sRelId
.toUtf8() );
694 pFS
->endElementNS(mnXmlNamespace
, XML_cNvPr
);
696 pFS
->singleElementNS(mnXmlNamespace
, XML_cNvGraphicFramePr
);
698 if( GetDocumentType() == DOCUMENT_PPTX
)
699 pFS
->singleElementNS(mnXmlNamespace
, XML_nvPr
);
700 pFS
->endElementNS( mnXmlNamespace
, XML_nvGraphicFramePr
);
702 // visual chart properties
703 WriteShapeTransformation( xShape
, mnXmlNamespace
);
705 // writer chart object
706 pFS
->startElement(FSNS(XML_a
, XML_graphic
));
707 pFS
->startElement( FSNS( XML_a
, XML_graphicData
),
708 XML_uri
, "http://schemas.openxmlformats.org/drawingml/2006/chart" );
710 const char* sFullPath
= nullptr;
711 const char* sRelativePath
= nullptr;
712 switch( GetDocumentType() )
716 sFullPath
= "word/charts/chart";
717 sRelativePath
= "charts/chart";
722 sFullPath
= "ppt/charts/chart";
723 sRelativePath
= "../charts/chart";
728 sFullPath
= "xl/charts/chart";
729 sRelativePath
= "../charts/chart";
734 sFullPath
= "charts/chart";
735 sRelativePath
= "charts/chart";
739 OUString sFullStream
= OUStringBuffer()
740 .appendAscii(sFullPath
)
743 .makeStringAndClear();
744 OUString sRelativeStream
= OUStringBuffer()
745 .appendAscii(sRelativePath
)
748 .makeStringAndClear();
749 FSHelperPtr pChart
= CreateOutputStream(
752 pFS
->getOutputStream(),
753 "application/vnd.openxmlformats-officedocument.drawingml.chart+xml",
754 OUStringToOString(oox::getRelationship(Relationship::CHART
), RTL_TEXTENCODING_UTF8
).getStr(),
757 XmlFilterBase
* pFB
= GetFB();
758 pFS
->singleElement( FSNS( XML_c
, XML_chart
),
759 FSNS(XML_xmlns
, XML_c
), pFB
->getNamespaceURL(OOX_NS(dmlChart
)).toUtf8(),
760 FSNS(XML_xmlns
, XML_r
), pFB
->getNamespaceURL(OOX_NS(officeRel
)).toUtf8(),
761 FSNS(XML_r
, XML_id
), sId
.toUtf8() );
763 pFS
->endElement( FSNS( XML_a
, XML_graphicData
) );
764 pFS
->endElement( FSNS( XML_a
, XML_graphic
) );
765 pFS
->endElementNS( mnXmlNamespace
, XML_graphicFrame
);
771 void ChartExport::InitRangeSegmentationProperties( const Reference
< chart2::XChartDocument
> & xChartDoc
)
776 Reference
< chart2::data::XDataProvider
> xDataProvider( xChartDoc
->getDataProvider() );
777 OSL_ENSURE( xDataProvider
.is(), "No DataProvider" );
778 if( xDataProvider
.is())
780 mbHasCategoryLabels
= lcl_hasCategoryLabels( xChartDoc
);
781 mbIsCategoryPositionShifted
= lcl_isCategoryAxisShifted( xChartDoc
);
784 catch( const uno::Exception
& )
786 DBG_UNHANDLED_EXCEPTION("oox");
790 void ChartExport::ExportContent()
792 Reference
< chart2::XChartDocument
> xChartDoc( getModel(), uno::UNO_QUERY
);
793 OSL_ASSERT( xChartDoc
.is() );
794 if( !xChartDoc
.is() )
796 InitRangeSegmentationProperties( xChartDoc
);
797 // TODO: export chart
801 void ChartExport::ExportContent_()
803 Reference
< css::chart::XChartDocument
> xChartDoc( getModel(), uno::UNO_QUERY
);
806 // determine if data comes from the outside
807 bool bIncludeTable
= true;
809 Reference
< chart2::XChartDocument
> xNewDoc( xChartDoc
, uno::UNO_QUERY
);
812 // check if we have own data. If so we must not export the complete
813 // range string, as this is our only indicator for having own or
814 // external data. @todo: fix this in the file format!
815 Reference
< lang::XServiceInfo
> xDPServiceInfo( xNewDoc
->getDataProvider(), uno::UNO_QUERY
);
816 if( ! (xDPServiceInfo
.is() && xDPServiceInfo
->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" ))
818 bIncludeTable
= false;
821 exportChartSpace( xChartDoc
, bIncludeTable
);
825 OSL_FAIL( "Couldn't export chart due to wrong XModel" );
829 void ChartExport::exportChartSpace( const Reference
< css::chart::XChartDocument
>& xChartDoc
,
832 FSHelperPtr pFS
= GetFS();
833 XmlFilterBase
* pFB
= GetFB();
834 pFS
->startElement( FSNS( XML_c
, XML_chartSpace
),
835 FSNS( XML_xmlns
, XML_c
), pFB
->getNamespaceURL(OOX_NS(dmlChart
)).toUtf8(),
836 FSNS( XML_xmlns
, XML_a
), pFB
->getNamespaceURL(OOX_NS(dml
)).toUtf8(),
837 FSNS( XML_xmlns
, XML_r
), pFB
->getNamespaceURL(OOX_NS(officeRel
)).toUtf8());
838 // TODO: get the correct editing language
839 pFS
->singleElement(FSNS(XML_c
, XML_lang
), XML_val
, "en-US");
841 pFS
->singleElement(FSNS(XML_c
, XML_roundedCorners
), XML_val
, "0");
845 // TODO:external data
848 exportChart(xChartDoc
);
850 // TODO: printSettings
852 // TODO: text properties
853 // TODO: shape properties
854 Reference
< XPropertySet
> xPropSet
= xChartDoc
->getArea();
856 exportShapeProps( xPropSet
);
859 exportExternalData(xChartDoc
);
861 pFS
->endElement( FSNS( XML_c
, XML_chartSpace
) );
864 void ChartExport::exportExternalData( const Reference
< css::chart::XChartDocument
>& xChartDoc
)
866 // Embedded external data is grab bagged for docx file hence adding export part of
867 // external data for docx files only.
868 if(GetDocumentType() != DOCUMENT_DOCX
)
871 OUString externalDataPath
;
872 Reference
< beans::XPropertySet
> xDocPropSet( xChartDoc
->getDiagram(), uno::UNO_QUERY
);
873 if( xDocPropSet
.is())
877 Any
aAny( xDocPropSet
->getPropertyValue( "ExternalData" ));
878 aAny
>>= externalDataPath
;
880 catch( beans::UnknownPropertyException
& )
882 SAL_WARN("oox", "Required property not found in ChartDocument");
885 if(!externalDataPath
.isEmpty())
887 // Here adding external data entry to relationship.
888 OUString relationPath
= externalDataPath
;
889 // Converting absolute path to relative path.
890 if( externalDataPath
[ 0 ] != '.' && externalDataPath
[ 1 ] != '.')
892 sal_Int32 nSepPos
= externalDataPath
.indexOf( '/', 0 );
895 relationPath
= relationPath
.copy( nSepPos
, ::std::max
< sal_Int32
>( externalDataPath
.getLength(), 0 ) - nSepPos
);
896 relationPath
= ".." + relationPath
;
899 FSHelperPtr pFS
= GetFS();
900 OUString type
= oox::getRelationship(Relationship::PACKAGE
);
901 if (relationPath
.endsWith(".bin"))
902 type
= oox::getRelationship(Relationship::OLEOBJECT
);
904 OUString sRelId
= GetFB()->addRelation(pFS
->getOutputStream(),
907 pFS
->singleElementNS(XML_c
, XML_externalData
, FSNS(XML_r
, XML_id
), sRelId
.toUtf8());
911 void ChartExport::exportChart( const Reference
< css::chart::XChartDocument
>& xChartDoc
)
913 Reference
< chart2::XChartDocument
> xNewDoc( xChartDoc
, uno::UNO_QUERY
);
914 mxDiagram
.set( xChartDoc
->getDiagram() );
916 mxNewDiagram
.set( xNewDoc
->getFirstDiagram());
918 // get Properties of ChartDocument
919 bool bHasMainTitle
= false;
921 bool bHasLegend
= false;
922 Reference
< beans::XPropertySet
> xDocPropSet( xChartDoc
, uno::UNO_QUERY
);
923 if( xDocPropSet
.is())
927 Any
aAny( xDocPropSet
->getPropertyValue("HasMainTitle"));
928 aAny
>>= bHasMainTitle
;
929 aAny
= xDocPropSet
->getPropertyValue("HasLegend");
932 catch( beans::UnknownPropertyException
& )
934 SAL_WARN("oox", "Required property not found in ChartDocument");
936 } // if( xDocPropSet.is())
938 Reference
< beans::XPropertySet
> xPropSubTitle( xChartDoc
->getSubTitle(), UNO_QUERY
);
939 if( xPropSubTitle
.is())
943 xPropSubTitle
->getPropertyValue("String") >>= aSubTitle
;
945 catch( beans::UnknownPropertyException
& )
951 FSHelperPtr pFS
= GetFS();
952 pFS
->startElement(FSNS(XML_c
, XML_chart
));
957 exportTitle( xChartDoc
->getTitle(), !aSubTitle
.isEmpty() ? &aSubTitle
: nullptr );
958 pFS
->singleElement(FSNS(XML_c
, XML_autoTitleDeleted
), XML_val
, "0");
960 else if( !aSubTitle
.isEmpty() )
962 exportTitle( xChartDoc
->getSubTitle(), nullptr );
963 pFS
->singleElement(FSNS(XML_c
, XML_autoTitleDeleted
), XML_val
, "0");
967 pFS
->singleElement(FSNS(XML_c
, XML_autoTitleDeleted
), XML_val
, "1");
976 Reference
< beans::XPropertySet
> xFloor
= mxNewDiagram
->getFloor();
979 pFS
->startElement(FSNS(XML_c
, XML_floor
));
980 exportShapeProps( xFloor
);
981 pFS
->endElement( FSNS( XML_c
, XML_floor
) );
984 // LibreOffice doesn't distinguish between sideWall and backWall (both are using the same color).
985 // It is controlled by the same Wall property.
986 Reference
< beans::XPropertySet
> xWall
= mxNewDiagram
->getWall();
990 pFS
->startElement(FSNS(XML_c
, XML_sideWall
));
991 exportShapeProps( xWall
);
992 pFS
->endElement( FSNS( XML_c
, XML_sideWall
) );
995 pFS
->startElement(FSNS(XML_c
, XML_backWall
));
996 exportShapeProps( xWall
);
997 pFS
->endElement( FSNS( XML_c
, XML_backWall
) );
1002 exportPlotArea( xChartDoc
);
1005 exportLegend( xChartDoc
);
1007 uno::Reference
<beans::XPropertySet
> xDiagramPropSet(xChartDoc
->getDiagram(), uno::UNO_QUERY
);
1008 uno::Any aPlotVisOnly
= xDiagramPropSet
->getPropertyValue("IncludeHiddenCells");
1009 bool bIncludeHiddenCells
= false;
1010 aPlotVisOnly
>>= bIncludeHiddenCells
;
1011 pFS
->singleElement(FSNS(XML_c
, XML_plotVisOnly
), XML_val
, ToPsz10(!bIncludeHiddenCells
));
1013 exportMissingValueTreatment(Reference
<beans::XPropertySet
>(mxDiagram
, uno::UNO_QUERY
));
1015 pFS
->endElement( FSNS( XML_c
, XML_chart
) );
1018 void ChartExport::exportMissingValueTreatment(const uno::Reference
<beans::XPropertySet
>& xPropSet
)
1024 uno::Any aAny
= xPropSet
->getPropertyValue("MissingValueTreatment");
1025 if (!(aAny
>>= nVal
))
1028 const char* pVal
= nullptr;
1031 case cssc::MissingValueTreatment::LEAVE_GAP
:
1034 case cssc::MissingValueTreatment::USE_ZERO
:
1037 case cssc::MissingValueTreatment::CONTINUE
:
1041 SAL_WARN("oox", "unknown MissingValueTreatment value");
1045 FSHelperPtr pFS
= GetFS();
1046 pFS
->singleElement(FSNS(XML_c
, XML_dispBlanksAs
), XML_val
, pVal
);
1049 void ChartExport::exportLegend( const Reference
< css::chart::XChartDocument
>& xChartDoc
)
1051 FSHelperPtr pFS
= GetFS();
1052 pFS
->startElement(FSNS(XML_c
, XML_legend
));
1054 Reference
< beans::XPropertySet
> xProp( xChartDoc
->getLegend(), uno::UNO_QUERY
);
1058 css::chart::ChartLegendPosition aLegendPos
= css::chart::ChartLegendPosition_NONE
;
1061 Any
aAny( xProp
->getPropertyValue( "Alignment" ));
1062 aAny
>>= aLegendPos
;
1064 catch( beans::UnknownPropertyException
& )
1066 SAL_WARN("oox", "Property Align not found in ChartLegend");
1069 const char* strPos
= nullptr;
1070 switch( aLegendPos
)
1072 case css::chart::ChartLegendPosition_LEFT
:
1075 case css::chart::ChartLegendPosition_RIGHT
:
1078 case css::chart::ChartLegendPosition_TOP
:
1081 case css::chart::ChartLegendPosition_BOTTOM
:
1084 case css::chart::ChartLegendPosition_NONE
:
1085 case css::chart::ChartLegendPosition::ChartLegendPosition_MAKE_FIXED_SIZE
:
1090 if( strPos
!= nullptr )
1092 pFS
->singleElement(FSNS(XML_c
, XML_legendPos
), XML_val
, strPos
);
1095 uno::Any aRelativePos
= xProp
->getPropertyValue("RelativePosition");
1096 if (aRelativePos
.hasValue())
1098 pFS
->startElement(FSNS(XML_c
, XML_layout
));
1099 pFS
->startElement(FSNS(XML_c
, XML_manualLayout
));
1101 pFS
->singleElement(FSNS(XML_c
, XML_xMode
), XML_val
, "edge");
1102 pFS
->singleElement(FSNS(XML_c
, XML_yMode
), XML_val
, "edge");
1103 chart2::RelativePosition aPos
= aRelativePos
.get
<chart2::RelativePosition
>();
1105 const double x
= aPos
.Primary
;
1106 const double y
= aPos
.Secondary
;
1108 pFS
->singleElement(FSNS(XML_c
, XML_x
), XML_val
, OString::number(x
));
1109 pFS
->singleElement(FSNS(XML_c
, XML_y
), XML_val
, OString::number(y
));
1111 uno::Any aRelativeSize
= xProp
->getPropertyValue("RelativeSize");
1112 if (aRelativeSize
.hasValue())
1114 chart2::RelativeSize aSize
= aRelativeSize
.get
<chart2::RelativeSize
>();
1116 const double w
= aSize
.Primary
;
1117 const double h
= aSize
.Secondary
;
1119 pFS
->singleElement(FSNS(XML_c
, XML_w
), XML_val
, OString::number(w
));
1121 pFS
->singleElement(FSNS(XML_c
, XML_h
), XML_val
, OString::number(h
));
1124 SAL_WARN_IF(aPos
.Anchor
!= css::drawing::Alignment_TOP_LEFT
, "oox", "unsupported anchor position");
1126 pFS
->endElement(FSNS(XML_c
, XML_manualLayout
));
1127 pFS
->endElement(FSNS(XML_c
, XML_layout
));
1130 if (strPos
!= nullptr)
1132 pFS
->singleElement(FSNS(XML_c
, XML_overlay
), XML_val
, "0");
1136 exportShapeProps( xProp
);
1138 // draw-chart:txPr text properties
1139 exportTextProps( xProp
);
1144 pFS
->endElement( FSNS( XML_c
, XML_legend
) );
1147 void ChartExport::exportTitle( const Reference
< XShape
>& xShape
, const OUString
* pSubText
)
1150 Reference
< beans::XPropertySet
> xPropSet( xShape
, uno::UNO_QUERY
);
1153 xPropSet
->getPropertyValue("String") >>= sText
;
1156 // tdf#101322: add subtitle to title
1158 sText
= sText
.isEmpty() ? *pSubText
: sText
+ "\n" + *pSubText
;
1160 if( sText
.isEmpty() )
1163 FSHelperPtr pFS
= GetFS();
1164 pFS
->startElement(FSNS(XML_c
, XML_title
));
1166 pFS
->startElement(FSNS(XML_c
, XML_tx
));
1167 pFS
->startElement(FSNS(XML_c
, XML_rich
));
1170 const char* sWritingMode
= nullptr;
1171 bool bVertical
= false;
1172 xPropSet
->getPropertyValue("StackedText") >>= bVertical
;
1174 sWritingMode
= "wordArtVert";
1176 sal_Int32 nRotation
= 0;
1177 xPropSet
->getPropertyValue("TextRotation") >>= nRotation
;
1179 pFS
->singleElement( FSNS( XML_a
, XML_bodyPr
),
1180 XML_vert
, sWritingMode
,
1181 XML_rot
, oox::drawingml::calcRotationValue(nRotation
) );
1183 pFS
->singleElement(FSNS(XML_a
, XML_lstStyle
));
1184 // FIXME: handle multiple paragraphs to parse aText
1185 pFS
->startElement(FSNS(XML_a
, XML_p
));
1187 pFS
->startElement(FSNS(XML_a
, XML_pPr
));
1189 bool bDummy
= false;
1191 WriteRunProperties(xPropSet
, false, XML_defRPr
, true, bDummy
, nDummy
);
1193 pFS
->endElement( FSNS( XML_a
, XML_pPr
) );
1195 pFS
->startElement(FSNS(XML_a
, XML_r
));
1197 WriteRunProperties( xPropSet
, false, XML_rPr
, true, bDummy
, nDummy
);
1198 pFS
->startElement(FSNS(XML_a
, XML_t
));
1199 pFS
->writeEscaped( sText
);
1200 pFS
->endElement( FSNS( XML_a
, XML_t
) );
1201 pFS
->endElement( FSNS( XML_a
, XML_r
) );
1203 pFS
->endElement( FSNS( XML_a
, XML_p
) );
1205 pFS
->endElement( FSNS( XML_c
, XML_rich
) );
1206 pFS
->endElement( FSNS( XML_c
, XML_tx
) );
1208 uno::Any aManualLayout
= xPropSet
->getPropertyValue("RelativePosition");
1209 if (aManualLayout
.hasValue())
1211 pFS
->startElement(FSNS(XML_c
, XML_layout
));
1212 pFS
->startElement(FSNS(XML_c
, XML_manualLayout
));
1213 pFS
->singleElement(FSNS(XML_c
, XML_xMode
), XML_val
, "edge");
1214 pFS
->singleElement(FSNS(XML_c
, XML_yMode
), XML_val
, "edge");
1216 Reference
<embed::XVisualObject
> xVisObject(mxChartModel
, uno::UNO_QUERY
);
1217 awt::Size aPageSize
= xVisObject
->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT
);
1219 awt::Size aSize
= xShape
->getSize();
1220 awt::Point aPos2
= xShape
->getPosition();
1221 // rotated shapes need special handling...
1222 double fSin
= fabs(sin(basegfx::deg2rad(nRotation
*0.01)));
1223 // remove part of height from X direction, if title is rotated down
1224 if( nRotation
*0.01 > 180.0 )
1225 aPos2
.X
-= static_cast<sal_Int32
>(fSin
* aSize
.Height
+ 0.5);
1226 // remove part of width from Y direction, if title is rotated up
1227 else if( nRotation
*0.01 > 0.0 )
1228 aPos2
.Y
-= static_cast<sal_Int32
>(fSin
* aSize
.Width
+ 0.5);
1230 double x
= static_cast<double>(aPos2
.X
) / static_cast<double>(aPageSize
.Width
);
1231 double y
= static_cast<double>(aPos2
.Y
) / static_cast<double>(aPageSize
.Height
);
1233 pFS->singleElement(FSNS(XML_c, XML_wMode), XML_val, "edge");
1234 pFS->singleElement(FSNS(XML_c, XML_hMode), XML_val, "edge");
1236 pFS
->singleElement(FSNS(XML_c
, XML_x
), XML_val
, OString::number(x
));
1237 pFS
->singleElement(FSNS(XML_c
, XML_y
), XML_val
, OString::number(y
));
1239 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, "");
1240 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, "");
1242 pFS
->endElement(FSNS(XML_c
, XML_manualLayout
));
1243 pFS
->endElement(FSNS(XML_c
, XML_layout
));
1246 pFS
->singleElement(FSNS(XML_c
, XML_overlay
), XML_val
, "0");
1251 exportShapeProps( xPropSet
);
1254 pFS
->endElement( FSNS( XML_c
, XML_title
) );
1257 void ChartExport::exportPlotArea( const Reference
< css::chart::XChartDocument
>& xChartDoc
)
1259 Reference
< chart2::XCoordinateSystemContainer
> xBCooSysCnt( mxNewDiagram
, uno::UNO_QUERY
);
1260 if( ! xBCooSysCnt
.is())
1263 // plot-area element
1265 FSHelperPtr pFS
= GetFS();
1266 pFS
->startElement(FSNS(XML_c
, XML_plotArea
));
1268 Reference
<beans::XPropertySet
> xWall(mxNewDiagram
, uno::UNO_QUERY
);
1271 uno::Any aAny
= xWall
->getPropertyValue("RelativePosition");
1272 if (aAny
.hasValue())
1274 chart2::RelativePosition aPos
= aAny
.get
<chart2::RelativePosition
>();
1275 aAny
= xWall
->getPropertyValue("RelativeSize");
1276 chart2::RelativeSize aSize
= aAny
.get
<chart2::RelativeSize
>();
1277 uno::Reference
< css::chart::XDiagramPositioning
> xDiagramPositioning( xChartDoc
->getDiagram(), uno::UNO_QUERY
);
1278 exportManualLayout(aPos
, aSize
, xDiagramPositioning
->isExcludingDiagramPositioning() );
1283 const Sequence
< Reference
< chart2::XCoordinateSystem
> >
1284 aCooSysSeq( xBCooSysCnt
->getCoordinateSystems());
1285 for( const auto& rCS
: aCooSysSeq
)
1287 Reference
< chart2::XChartTypeContainer
> xCTCnt( rCS
, uno::UNO_QUERY
);
1291 const Sequence
< Reference
< chart2::XChartType
> > aCTSeq( xCTCnt
->getChartTypes());
1292 for( const auto& rCT
: aCTSeq
)
1294 Reference
< chart2::XDataSeriesContainer
> xDSCnt( rCT
, uno::UNO_QUERY
);
1297 Reference
< chart2::XChartType
> xChartType( rCT
, uno::UNO_QUERY
);
1298 if( ! xChartType
.is())
1300 // note: if xDSCnt.is() then also aCTSeq[nCTIdx]
1301 OUString
aChartType( xChartType
->getChartType());
1302 sal_Int32 eChartType
= lcl_getChartType( aChartType
);
1303 switch( eChartType
)
1305 case chart::TYPEID_BAR
:
1307 exportBarChart( xChartType
);
1310 case chart::TYPEID_AREA
:
1312 exportAreaChart( xChartType
);
1315 case chart::TYPEID_LINE
:
1317 exportLineChart( xChartType
);
1320 case chart::TYPEID_BUBBLE
:
1322 exportBubbleChart( xChartType
);
1325 case chart::TYPEID_OFPIE
:
1329 case chart::TYPEID_DOUGHNUT
:
1330 case chart::TYPEID_PIE
:
1332 exportPieChart( xChartType
);
1335 case chart::TYPEID_RADARLINE
:
1336 case chart::TYPEID_RADARAREA
:
1338 exportRadarChart( xChartType
);
1341 case chart::TYPEID_SCATTER
:
1343 exportScatterChart( xChartType
);
1346 case chart::TYPEID_STOCK
:
1348 exportStockChart( xChartType
);
1351 case chart::TYPEID_SURFACE
:
1353 exportSurfaceChart( xChartType
);
1358 SAL_WARN("oox", "ChartExport::exportPlotArea -- not support chart type");
1372 * Export the Plot area Shape Properties
1373 * eg: Fill and Outline
1375 Reference
< css::chart::X3DDisplay
> xWallFloorSupplier( mxDiagram
, uno::UNO_QUERY
);
1376 // tdf#114139 For 2D charts Plot Area equivalent is Chart Wall.
1377 // Unfortunately LibreOffice doesn't have Plot Area equivalent for 3D charts.
1378 // It means that Plot Area couldn't be displayed and changed for 3D chars in LibreOffice.
1379 // We cannot write Wall attributes into Plot Area for 3D charts, because Wall us used as background wall.
1380 if( !mbIs3DChart
&& xWallFloorSupplier
.is() )
1382 Reference
< beans::XPropertySet
> xWallPropSet
= xWallFloorSupplier
->getWall();
1383 if( xWallPropSet
.is() )
1385 uno::Any aAny
= xWallPropSet
->getPropertyValue("LineStyle");
1386 sal_Int32 eChartType
= getChartType( );
1387 // Export LineStyle_NONE instead of default linestyle of PlotArea border, because LibreOffice
1388 // make invisible the Wall shape properties, in case of these charts. Or in the future set
1389 // the default LineStyle of these charts to LineStyle_NONE.
1390 bool noSupportWallProp
= ( (eChartType
== chart::TYPEID_PIE
) || (eChartType
== chart::TYPEID_RADARLINE
) || (eChartType
== chart::TYPEID_RADARAREA
) );
1391 if ( noSupportWallProp
&& (aAny
!= drawing::LineStyle_NONE
) )
1393 xWallPropSet
->setPropertyValue( "LineStyle", uno::Any(drawing::LineStyle_NONE
) );
1395 exportShapeProps( xWallPropSet
);
1399 pFS
->endElement( FSNS( XML_c
, XML_plotArea
) );
1403 void ChartExport::exportManualLayout(const css::chart2::RelativePosition
& rPos
,
1404 const css::chart2::RelativeSize
& rSize
,
1405 const bool bIsExcludingDiagramPositioning
)
1407 FSHelperPtr pFS
= GetFS();
1408 pFS
->startElement(FSNS(XML_c
, XML_layout
));
1409 pFS
->startElement(FSNS(XML_c
, XML_manualLayout
));
1411 // By default layoutTarget is set to "outer" and we shouldn't save it in that case
1412 if ( bIsExcludingDiagramPositioning
)
1414 pFS
->singleElement(FSNS(XML_c
, XML_layoutTarget
), XML_val
, "inner");
1416 pFS
->singleElement(FSNS(XML_c
, XML_xMode
), XML_val
, "edge");
1417 pFS
->singleElement(FSNS(XML_c
, XML_yMode
), XML_val
, "edge");
1419 double x
= rPos
.Primary
;
1420 double y
= rPos
.Secondary
;
1421 const double w
= rSize
.Primary
;
1422 const double h
= rSize
.Secondary
;
1423 switch (rPos
.Anchor
)
1425 case drawing::Alignment_LEFT
:
1428 case drawing::Alignment_TOP_LEFT
:
1430 case drawing::Alignment_BOTTOM_LEFT
:
1433 case drawing::Alignment_TOP
:
1436 case drawing::Alignment_CENTER
:
1440 case drawing::Alignment_BOTTOM
:
1444 case drawing::Alignment_TOP_RIGHT
:
1447 case drawing::Alignment_BOTTOM_RIGHT
:
1451 case drawing::Alignment_RIGHT
:
1456 SAL_WARN("oox", "unhandled alignment case for manual layout export " << static_cast<sal_uInt16
>(rPos
.Anchor
));
1459 pFS
->singleElement(FSNS(XML_c
, XML_x
), XML_val
, OString::number(x
));
1461 pFS
->singleElement(FSNS(XML_c
, XML_y
), XML_val
, OString::number(y
));
1463 pFS
->singleElement(FSNS(XML_c
, XML_w
), XML_val
, OString::number(w
));
1465 pFS
->singleElement(FSNS(XML_c
, XML_h
), XML_val
, OString::number(h
));
1467 pFS
->endElement(FSNS(XML_c
, XML_manualLayout
));
1468 pFS
->endElement(FSNS(XML_c
, XML_layout
));
1471 void ChartExport::exportFill( const Reference
< XPropertySet
>& xPropSet
)
1473 if ( !GetProperty( xPropSet
, "FillStyle" ) )
1475 FillStyle
aFillStyle( FillStyle_NONE
);
1476 xPropSet
->getPropertyValue( "FillStyle" ) >>= aFillStyle
;
1477 switch( aFillStyle
)
1479 case FillStyle_GRADIENT
:
1480 exportGradientFill( xPropSet
);
1482 case FillStyle_BITMAP
:
1483 exportBitmapFill( xPropSet
);
1485 case FillStyle_HATCH
:
1486 exportHatch(xPropSet
);
1489 WriteFill( xPropSet
);
1493 void ChartExport::exportHatch( const Reference
< XPropertySet
>& xPropSet
)
1498 if (GetProperty(xPropSet
, "FillHatchName"))
1500 OUString aHatchName
;
1501 mAny
>>= aHatchName
;
1502 uno::Reference
< lang::XMultiServiceFactory
> xFact( getModel(), uno::UNO_QUERY
);
1503 uno::Reference
< container::XNameAccess
> xHatchTable( xFact
->createInstance("com.sun.star.drawing.HatchTable"), uno::UNO_QUERY
);
1504 uno::Any rValue
= xHatchTable
->getByName(aHatchName
);
1505 css::drawing::Hatch aHatch
;
1507 WritePattFill(xPropSet
, aHatch
);
1512 void ChartExport::exportBitmapFill( const Reference
< XPropertySet
>& xPropSet
)
1516 OUString sFillBitmapName
;
1517 xPropSet
->getPropertyValue("FillBitmapName") >>= sFillBitmapName
;
1519 uno::Reference
< lang::XMultiServiceFactory
> xFact( getModel(), uno::UNO_QUERY
);
1522 uno::Reference
< container::XNameAccess
> xBitmapTable( xFact
->createInstance("com.sun.star.drawing.BitmapTable"), uno::UNO_QUERY
);
1523 uno::Any rValue
= xBitmapTable
->getByName( sFillBitmapName
);
1524 if (rValue
.has
<uno::Reference
<awt::XBitmap
>>())
1526 uno::Reference
<awt::XBitmap
> xBitmap
= rValue
.get
<uno::Reference
<awt::XBitmap
>>();
1527 uno::Reference
<graphic::XGraphic
> xGraphic(xBitmap
, uno::UNO_QUERY
);
1530 WriteXGraphicBlipFill(xPropSet
, xGraphic
, XML_a
, true, true);
1534 catch (const uno::Exception
&)
1536 TOOLS_WARN_EXCEPTION("oox", "ChartExport::exportBitmapFill");
1541 void ChartExport::exportGradientFill( const Reference
< XPropertySet
>& xPropSet
)
1545 OUString sFillGradientName
;
1546 xPropSet
->getPropertyValue("FillGradientName") >>= sFillGradientName
;
1548 awt::Gradient aGradient
;
1549 awt::Gradient aTransparenceGradient
;
1550 uno::Reference
< lang::XMultiServiceFactory
> xFact( getModel(), uno::UNO_QUERY
);
1553 uno::Reference
< container::XNameAccess
> xGradient( xFact
->createInstance("com.sun.star.drawing.GradientTable"), uno::UNO_QUERY
);
1554 uno::Any rGradientValue
= xGradient
->getByName( sFillGradientName
);
1555 if( rGradientValue
>>= aGradient
)
1557 mpFS
->startElementNS(XML_a
, XML_gradFill
);
1558 OUString sFillTransparenceGradientName
;
1559 if( (xPropSet
->getPropertyValue("FillTransparenceGradientName") >>= sFillTransparenceGradientName
) && !sFillTransparenceGradientName
.isEmpty())
1561 uno::Reference
< container::XNameAccess
> xTransparenceGradient(xFact
->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY
);
1562 uno::Any rTransparenceValue
= xTransparenceGradient
->getByName(sFillTransparenceGradientName
);
1563 rTransparenceValue
>>= aTransparenceGradient
;;
1564 WriteGradientFill(aGradient
, aTransparenceGradient
);
1568 WriteGradientFill(aGradient
, aTransparenceGradient
, xPropSet
);
1570 mpFS
->endElementNS(XML_a
, XML_gradFill
);
1573 catch (const uno::Exception
&)
1575 TOOLS_INFO_EXCEPTION("oox", "ChartExport::exportGradientFill");
1581 void ChartExport::exportDataTable( )
1583 FSHelperPtr pFS
= GetFS();
1584 Reference
< beans::XPropertySet
> aPropSet( mxDiagram
, uno::UNO_QUERY
);
1586 bool bShowVBorder
= false;
1587 bool bShowHBorder
= false;
1588 bool bShowOutline
= false;
1590 if (GetProperty( aPropSet
, "DataTableHBorder"))
1591 mAny
>>= bShowHBorder
;
1592 if (GetProperty( aPropSet
, "DataTableVBorder"))
1593 mAny
>>= bShowVBorder
;
1594 if (GetProperty( aPropSet
, "DataTableOutline"))
1595 mAny
>>= bShowOutline
;
1597 if (bShowVBorder
|| bShowHBorder
|| bShowOutline
)
1599 pFS
->startElement(FSNS(XML_c
, XML_dTable
));
1601 pFS
->singleElement( FSNS( XML_c
, XML_showHorzBorder
),
1604 pFS
->singleElement(FSNS(XML_c
, XML_showVertBorder
), XML_val
, "1");
1606 pFS
->singleElement(FSNS(XML_c
, XML_showOutline
), XML_val
, "1");
1608 pFS
->endElement( FSNS( XML_c
, XML_dTable
));
1612 void ChartExport::exportAreaChart( const Reference
< chart2::XChartType
>& xChartType
)
1614 FSHelperPtr pFS
= GetFS();
1615 sal_Int32 nTypeId
= XML_areaChart
;
1617 nTypeId
= XML_area3DChart
;
1618 pFS
->startElement(FSNS(XML_c
, nTypeId
));
1621 bool bPrimaryAxes
= true;
1622 exportAllSeries(xChartType
, bPrimaryAxes
);
1623 exportAxesId(bPrimaryAxes
);
1625 pFS
->endElement( FSNS( XML_c
, nTypeId
) );
1628 void ChartExport::exportBarChart( const Reference
< chart2::XChartType
>& xChartType
)
1630 sal_Int32 nTypeId
= XML_barChart
;
1632 nTypeId
= XML_bar3DChart
;
1633 FSHelperPtr pFS
= GetFS();
1634 pFS
->startElement(FSNS(XML_c
, nTypeId
));
1636 bool bVertical
= false;
1637 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
1638 if( GetProperty( xPropSet
, "Vertical" ) )
1641 const char* bardir
= bVertical
? "bar":"col";
1642 pFS
->singleElement(FSNS(XML_c
, XML_barDir
), XML_val
, bardir
);
1644 exportGrouping( true );
1646 exportVaryColors(xChartType
);
1648 bool bPrimaryAxes
= true;
1649 exportAllSeries(xChartType
, bPrimaryAxes
);
1651 Reference
< XPropertySet
> xTypeProp( xChartType
, uno::UNO_QUERY
);
1653 if( xTypeProp
.is() && GetProperty( xTypeProp
, "GapwidthSequence") )
1655 uno::Sequence
< sal_Int32
> aBarPositionSequence
;
1656 mAny
>>= aBarPositionSequence
;
1657 if( aBarPositionSequence
.hasElements() )
1659 sal_Int32 nGapWidth
= aBarPositionSequence
[0];
1660 pFS
->singleElement(FSNS(XML_c
, XML_gapWidth
), XML_val
, OString::number(nGapWidth
));
1667 namespace cssc
= css::chart
;
1668 sal_Int32 nGeom3d
= cssc::ChartSolidType::RECTANGULAR_SOLID
;
1669 if( xPropSet
.is() && GetProperty( xPropSet
, "SolidType") )
1671 const char* sShapeType
= nullptr;
1674 case cssc::ChartSolidType::RECTANGULAR_SOLID
:
1677 case cssc::ChartSolidType::CONE
:
1678 sShapeType
= "cone";
1680 case cssc::ChartSolidType::CYLINDER
:
1681 sShapeType
= "cylinder";
1683 case cssc::ChartSolidType::PYRAMID
:
1684 sShapeType
= "pyramid";
1687 pFS
->singleElement(FSNS(XML_c
, XML_shape
), XML_val
, sShapeType
);
1691 if( !mbIs3DChart
&& xTypeProp
.is() && GetProperty( xTypeProp
, "OverlapSequence") )
1693 uno::Sequence
< sal_Int32
> aBarPositionSequence
;
1694 mAny
>>= aBarPositionSequence
;
1695 if( aBarPositionSequence
.hasElements() )
1697 sal_Int32 nOverlap
= aBarPositionSequence
[0];
1698 // Stacked/Percent Bar/Column chart Overlap-workaround
1699 // Export the Overlap value with 100% for stacked charts,
1700 // because the default overlap value of the Bar/Column chart is 0% and
1701 // LibreOffice do nothing with the overlap value in Stacked charts case,
1702 // unlike the MS Office, which is interpreted differently.
1703 if( ( mbStacked
|| mbPercent
) && nOverlap
!= 100 )
1706 pFS
->singleElement(FSNS(XML_c
, XML_overlap
), XML_val
, OString::number(nOverlap
));
1708 else // Normal bar chart
1710 pFS
->singleElement(FSNS(XML_c
, XML_overlap
), XML_val
, OString::number(nOverlap
));
1715 exportAxesId(bPrimaryAxes
);
1717 pFS
->endElement( FSNS( XML_c
, nTypeId
) );
1720 void ChartExport::exportBubbleChart( const Reference
< chart2::XChartType
>& xChartType
)
1722 FSHelperPtr pFS
= GetFS();
1723 pFS
->startElement(FSNS(XML_c
, XML_bubbleChart
));
1725 exportVaryColors(xChartType
);
1727 bool bPrimaryAxes
= true;
1728 exportAllSeries(xChartType
, bPrimaryAxes
);
1730 exportAxesId(bPrimaryAxes
);
1732 pFS
->endElement( FSNS( XML_c
, XML_bubbleChart
) );
1735 void ChartExport::exportDoughnutChart( const Reference
< chart2::XChartType
>& xChartType
)
1737 FSHelperPtr pFS
= GetFS();
1738 pFS
->startElement(FSNS(XML_c
, XML_doughnutChart
));
1740 exportVaryColors(xChartType
);
1742 bool bPrimaryAxes
= true;
1743 exportAllSeries(xChartType
, bPrimaryAxes
);
1745 exportFirstSliceAng( );
1747 pFS
->singleElement(FSNS(XML_c
, XML_holeSize
), XML_val
, OString::number(50));
1749 pFS
->endElement( FSNS( XML_c
, XML_doughnutChart
) );
1754 std::vector
<Sequence
<Reference
<chart2::XDataSeries
> > > splitDataSeriesByAxis(const Reference
< chart2::XChartType
>& xChartType
)
1756 std::vector
<Sequence
<Reference
<chart2::XDataSeries
> > > aSplitSeries
;
1757 std::map
<sal_Int32
, size_t> aMapAxisToIndex
;
1759 Reference
< chart2::XDataSeriesContainer
> xDSCnt( xChartType
, uno::UNO_QUERY
);
1762 sal_Int32 nAxisIndexOfFirstSeries
= -1;
1763 const Sequence
< Reference
< chart2::XDataSeries
> > aSeriesSeq( xDSCnt
->getDataSeries());
1764 for (const uno::Reference
<chart2::XDataSeries
>& xSeries
: aSeriesSeq
)
1766 Reference
<beans::XPropertySet
> xPropSet(xSeries
, uno::UNO_QUERY
);
1770 sal_Int32 nAxisIndex
= -1;
1771 uno::Any aAny
= xPropSet
->getPropertyValue("AttachedAxisIndex");
1772 aAny
>>= nAxisIndex
;
1773 size_t nVectorPos
= 0;
1774 if (nAxisIndexOfFirstSeries
== -1)
1776 nAxisIndexOfFirstSeries
= nAxisIndex
;
1779 auto it
= aMapAxisToIndex
.find(nAxisIndex
);
1780 if (it
== aMapAxisToIndex
.end())
1782 aSplitSeries
.emplace_back();
1783 nVectorPos
= aSplitSeries
.size() - 1;
1784 aMapAxisToIndex
.insert(std::pair
<sal_Int32
, size_t>(nAxisIndex
, nVectorPos
));
1788 nVectorPos
= it
->second
;
1791 uno::Sequence
<Reference
<chart2::XDataSeries
> >& rAxisSeriesSeq
= aSplitSeries
[nVectorPos
];
1792 sal_Int32 nLength
= rAxisSeriesSeq
.getLength();
1793 rAxisSeriesSeq
.realloc(nLength
+ 1);
1794 rAxisSeriesSeq
[nLength
] = xSeries
;
1796 // if the first series attached to secondary axis, then export those series first, which are attached to primary axis
1797 // also the MS Office export every time in this order
1798 if ( aSplitSeries
.size() > 1 && nAxisIndexOfFirstSeries
== 1 )
1800 std::swap( aSplitSeries
[0], aSplitSeries
[1] );
1804 return aSplitSeries
;
1809 void ChartExport::exportLineChart( const Reference
< chart2::XChartType
>& xChartType
)
1811 FSHelperPtr pFS
= GetFS();
1812 std::vector
<Sequence
<Reference
<chart2::XDataSeries
> > > aSplitDataSeries
= splitDataSeriesByAxis(xChartType
);
1813 for (auto & splitDataSeries
: aSplitDataSeries
)
1815 if (!splitDataSeries
.hasElements())
1818 sal_Int32 nTypeId
= XML_lineChart
;
1820 nTypeId
= XML_line3DChart
;
1821 pFS
->startElement(FSNS(XML_c
, nTypeId
));
1825 exportVaryColors(xChartType
);
1826 // TODO: show marker symbol in series?
1827 bool bPrimaryAxes
= true;
1828 exportSeries(xChartType
, splitDataSeries
, bPrimaryAxes
);
1831 sal_Int32 nSymbolType
= css::chart::ChartSymbolType::NONE
;
1832 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
1833 if( GetProperty( xPropSet
, "SymbolType" ) )
1834 mAny
>>= nSymbolType
;
1839 exportUpDownBars(xChartType
);
1840 const char* marker
= nSymbolType
== css::chart::ChartSymbolType::NONE
? "0":"1";
1841 pFS
->singleElement(FSNS(XML_c
, XML_marker
), XML_val
, marker
);
1844 exportAxesId(bPrimaryAxes
, true);
1846 pFS
->endElement( FSNS( XML_c
, nTypeId
) );
1850 void ChartExport::exportPieChart( const Reference
< chart2::XChartType
>& xChartType
)
1852 sal_Int32 eChartType
= getChartType( );
1853 if(eChartType
== chart::TYPEID_DOUGHNUT
)
1855 exportDoughnutChart( xChartType
);
1858 FSHelperPtr pFS
= GetFS();
1859 sal_Int32 nTypeId
= XML_pieChart
;
1861 nTypeId
= XML_pie3DChart
;
1862 pFS
->startElement(FSNS(XML_c
, nTypeId
));
1864 exportVaryColors(xChartType
);
1866 bool bPrimaryAxes
= true;
1867 exportAllSeries(xChartType
, bPrimaryAxes
);
1872 exportFirstSliceAng( );
1875 pFS
->endElement( FSNS( XML_c
, nTypeId
) );
1878 void ChartExport::exportRadarChart( const Reference
< chart2::XChartType
>& xChartType
)
1880 FSHelperPtr pFS
= GetFS();
1881 pFS
->startElement(FSNS(XML_c
, XML_radarChart
));
1884 sal_Int32 eChartType
= getChartType( );
1885 const char* radarStyle
= nullptr;
1886 if( eChartType
== chart::TYPEID_RADARAREA
)
1887 radarStyle
= "filled";
1889 radarStyle
= "marker";
1890 pFS
->singleElement(FSNS(XML_c
, XML_radarStyle
), XML_val
, radarStyle
);
1892 exportVaryColors(xChartType
);
1893 bool bPrimaryAxes
= true;
1894 exportAllSeries(xChartType
, bPrimaryAxes
);
1895 exportAxesId(bPrimaryAxes
);
1897 pFS
->endElement( FSNS( XML_c
, XML_radarChart
) );
1900 void ChartExport::exportScatterChartSeries( const Reference
< chart2::XChartType
>& xChartType
,
1901 css::uno::Sequence
<css::uno::Reference
<chart2::XDataSeries
>>* pSeries
)
1903 FSHelperPtr pFS
= GetFS();
1904 pFS
->startElement(FSNS(XML_c
, XML_scatterChart
));
1905 // TODO:scatterStyle
1907 sal_Int32 nSymbolType
= css::chart::ChartSymbolType::NONE
;
1908 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
1909 if( GetProperty( xPropSet
, "SymbolType" ) )
1910 mAny
>>= nSymbolType
;
1912 const char* scatterStyle
= "lineMarker";
1913 if (nSymbolType
== css::chart::ChartSymbolType::NONE
)
1915 scatterStyle
= "line";
1918 pFS
->singleElement(FSNS(XML_c
, XML_scatterStyle
), XML_val
, scatterStyle
);
1920 exportVaryColors(xChartType
);
1921 // FIXME: should export xVal and yVal
1922 bool bPrimaryAxes
= true;
1924 exportSeries(xChartType
, *pSeries
, bPrimaryAxes
);
1925 exportAxesId(bPrimaryAxes
);
1927 pFS
->endElement( FSNS( XML_c
, XML_scatterChart
) );
1930 void ChartExport::exportScatterChart( const Reference
< chart2::XChartType
>& xChartType
)
1932 FSHelperPtr pFS
= GetFS();
1933 std::vector
<Sequence
<Reference
<chart2::XDataSeries
> > > aSplitDataSeries
= splitDataSeriesByAxis(xChartType
);
1934 bool bExported
= false;
1935 for (auto & splitDataSeries
: aSplitDataSeries
)
1937 if (!splitDataSeries
.hasElements())
1941 exportScatterChartSeries(xChartType
, &splitDataSeries
);
1944 exportScatterChartSeries(xChartType
, nullptr);
1947 void ChartExport::exportStockChart( const Reference
< chart2::XChartType
>& xChartType
)
1949 FSHelperPtr pFS
= GetFS();
1950 pFS
->startElement(FSNS(XML_c
, XML_stockChart
));
1952 bool bPrimaryAxes
= true;
1953 Reference
< chart2::XDataSeriesContainer
> xDSCnt( xChartType
, uno::UNO_QUERY
);
1955 exportCandleStickSeries( xDSCnt
->getDataSeries(), bPrimaryAxes
);
1957 // export stock properties
1958 Reference
< css::chart::XStatisticDisplay
> xStockPropProvider( mxDiagram
, uno::UNO_QUERY
);
1959 if( xStockPropProvider
.is())
1962 exportUpDownBars(xChartType
);
1965 exportAxesId(bPrimaryAxes
);
1967 pFS
->endElement( FSNS( XML_c
, XML_stockChart
) );
1970 void ChartExport::exportHiLowLines()
1972 FSHelperPtr pFS
= GetFS();
1973 // export the chart property
1974 Reference
< css::chart::XStatisticDisplay
> xChartPropProvider( mxDiagram
, uno::UNO_QUERY
);
1976 if (!xChartPropProvider
.is())
1979 Reference
< beans::XPropertySet
> xStockPropSet
= xChartPropProvider
->getMinMaxLine();
1980 if( !xStockPropSet
.is() )
1983 pFS
->startElement(FSNS(XML_c
, XML_hiLowLines
));
1984 exportShapeProps( xStockPropSet
);
1985 pFS
->endElement( FSNS( XML_c
, XML_hiLowLines
) );
1988 void ChartExport::exportUpDownBars( const Reference
< chart2::XChartType
>& xChartType
)
1990 if(xChartType
->getChartType() != "com.sun.star.chart2.CandleStickChartType")
1993 FSHelperPtr pFS
= GetFS();
1994 // export the chart property
1995 Reference
< css::chart::XStatisticDisplay
> xChartPropProvider( mxDiagram
, uno::UNO_QUERY
);
1996 if(xChartPropProvider
.is())
1999 pFS
->startElement(FSNS(XML_c
, XML_upDownBars
));
2001 pFS
->singleElement(FSNS(XML_c
, XML_gapWidth
), XML_val
, OString::number(150));
2003 Reference
< beans::XPropertySet
> xChartPropSet
= xChartPropProvider
->getUpBar();
2004 if( xChartPropSet
.is() )
2006 pFS
->startElement(FSNS(XML_c
, XML_upBars
));
2007 // For Linechart with UpDownBars, spPr is not getting imported
2008 // so no need to call the exportShapeProps() for LineChart
2009 if(xChartType
->getChartType() == "com.sun.star.chart2.CandleStickChartType")
2011 exportShapeProps(xChartPropSet
);
2013 pFS
->endElement( FSNS( XML_c
, XML_upBars
) );
2015 xChartPropSet
= xChartPropProvider
->getDownBar();
2016 if( xChartPropSet
.is() )
2018 pFS
->startElement(FSNS(XML_c
, XML_downBars
));
2019 if(xChartType
->getChartType() == "com.sun.star.chart2.CandleStickChartType")
2021 exportShapeProps(xChartPropSet
);
2023 pFS
->endElement( FSNS( XML_c
, XML_downBars
) );
2025 pFS
->endElement( FSNS( XML_c
, XML_upDownBars
) );
2029 void ChartExport::exportSurfaceChart( const Reference
< chart2::XChartType
>& xChartType
)
2031 FSHelperPtr pFS
= GetFS();
2032 sal_Int32 nTypeId
= XML_surfaceChart
;
2034 nTypeId
= XML_surface3DChart
;
2035 pFS
->startElement(FSNS(XML_c
, nTypeId
));
2036 exportVaryColors(xChartType
);
2037 bool bPrimaryAxes
= true;
2038 exportAllSeries(xChartType
, bPrimaryAxes
);
2039 exportAxesId(bPrimaryAxes
);
2041 pFS
->endElement( FSNS( XML_c
, nTypeId
) );
2044 void ChartExport::exportAllSeries(const Reference
<chart2::XChartType
>& xChartType
, bool& rPrimaryAxes
)
2046 Reference
< chart2::XDataSeriesContainer
> xDSCnt( xChartType
, uno::UNO_QUERY
);
2050 // export dataseries for current chart-type
2051 Sequence
< Reference
< chart2::XDataSeries
> > aSeriesSeq( xDSCnt
->getDataSeries());
2052 exportSeries(xChartType
, aSeriesSeq
, rPrimaryAxes
);
2055 void ChartExport::exportVaryColors(const Reference
<chart2::XChartType
>& xChartType
)
2057 FSHelperPtr pFS
= GetFS();
2060 Reference
<chart2::XDataSeries
> xDataSeries
= getPrimaryDataSeries(xChartType
);
2061 Reference
<beans::XPropertySet
> xDataSeriesProps(xDataSeries
, uno::UNO_QUERY_THROW
);
2062 Any aAnyVaryColors
= xDataSeriesProps
->getPropertyValue("VaryColorsByPoint");
2063 bool bVaryColors
= false;
2064 aAnyVaryColors
>>= bVaryColors
;
2065 pFS
->singleElement(FSNS(XML_c
, XML_varyColors
), XML_val
, ToPsz10(bVaryColors
));
2069 pFS
->singleElement(FSNS(XML_c
, XML_varyColors
), XML_val
, "0");
2073 void ChartExport::exportSeries( const Reference
<chart2::XChartType
>& xChartType
,
2074 Sequence
<Reference
<chart2::XDataSeries
> >& rSeriesSeq
, bool& rPrimaryAxes
)
2076 OUString aLabelRole
= xChartType
->getRoleOfSequenceForSeriesLabel();
2077 OUString
aChartType( xChartType
->getChartType());
2078 sal_Int32 eChartType
= lcl_getChartType( aChartType
);
2080 for( const auto& rSeries
: std::as_const(rSeriesSeq
) )
2083 Reference
< chart2::data::XDataSource
> xSource( rSeries
, uno::UNO_QUERY
);
2086 Reference
< chart2::XDataSeries
> xDataSeries( xSource
, uno::UNO_QUERY
);
2087 Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aSeqCnt(
2088 xSource
->getDataSequences());
2089 // search for main sequence and create a series element
2091 sal_Int32 nMainSequenceIndex
= -1;
2092 sal_Int32 nSeriesLength
= 0;
2093 Reference
< chart2::data::XDataSequence
> xValuesSeq
;
2094 Reference
< chart2::data::XDataSequence
> xLabelSeq
;
2095 sal_Int32 nSeqIdx
=0;
2096 for( ; nSeqIdx
<aSeqCnt
.getLength(); ++nSeqIdx
)
2099 Reference
< chart2::data::XDataSequence
> xTempValueSeq( aSeqCnt
[nSeqIdx
]->getValues() );
2100 if( nMainSequenceIndex
==-1 )
2102 Reference
< beans::XPropertySet
> xSeqProp( xTempValueSeq
, uno::UNO_QUERY
);
2104 xSeqProp
->getPropertyValue("Role") >>= aRole
;
2106 if( aRole
== aLabelRole
)
2108 xValuesSeq
.set( xTempValueSeq
);
2109 xLabelSeq
.set( aSeqCnt
[nSeqIdx
]->getLabel());
2110 nMainSequenceIndex
= nSeqIdx
;
2113 sal_Int32 nSequenceLength
= (xTempValueSeq
.is()? xTempValueSeq
->getData().getLength() : sal_Int32(0));
2114 if( nSeriesLength
< nSequenceLength
)
2115 nSeriesLength
= nSequenceLength
;
2118 // have found the main sequence, then xValuesSeq and
2119 // xLabelSeq contain those. Otherwise both are empty
2121 FSHelperPtr pFS
= GetFS();
2123 pFS
->startElement(FSNS(XML_c
, XML_ser
));
2125 // TODO: idx and order
2126 pFS
->singleElement( FSNS( XML_c
, XML_idx
),
2127 XML_val
, OString::number(mnSeriesCount
) );
2128 pFS
->singleElement( FSNS( XML_c
, XML_order
),
2129 XML_val
, OString::number(mnSeriesCount
++) );
2132 if( xLabelSeq
.is() )
2133 exportSeriesText( xLabelSeq
);
2135 Reference
<XPropertySet
> xPropSet(xDataSeries
, UNO_QUERY_THROW
);
2136 if( GetProperty( xPropSet
, "AttachedAxisIndex") )
2138 sal_Int32 nLocalAttachedAxis
= 0;
2139 mAny
>>= nLocalAttachedAxis
;
2140 rPrimaryAxes
= isPrimaryAxes(nLocalAttachedAxis
);
2143 // export shape properties
2144 Reference
< XPropertySet
> xOldPropSet
= SchXMLSeriesHelper::createOldAPISeriesPropertySet(
2145 rSeries
, getModel() );
2146 if( xOldPropSet
.is() )
2148 exportShapeProps( xOldPropSet
);
2151 switch( eChartType
)
2153 case chart::TYPEID_BUBBLE
:
2154 case chart::TYPEID_HORBAR
:
2155 case chart::TYPEID_BAR
:
2157 pFS
->singleElement(FSNS(XML_c
, XML_invertIfNegative
), XML_val
, "0");
2160 case chart::TYPEID_LINE
:
2162 exportMarker(xOldPropSet
);
2165 case chart::TYPEID_PIE
:
2166 case chart::TYPEID_DOUGHNUT
:
2168 if( xOldPropSet
.is() && GetProperty( xOldPropSet
, "SegmentOffset") )
2170 sal_Int32 nOffset
= 0;
2172 pFS
->singleElement( FSNS( XML_c
, XML_explosion
),
2173 XML_val
, OString::number( nOffset
) );
2177 case chart::TYPEID_SCATTER
:
2179 exportMarker(xOldPropSet
);
2182 case chart::TYPEID_RADARLINE
:
2184 exportMarker(xOldPropSet
);
2189 // export data points
2190 exportDataPoints( uno::Reference
< beans::XPropertySet
>( rSeries
, uno::UNO_QUERY
), nSeriesLength
, eChartType
);
2192 // export data labels
2193 exportDataLabels(rSeries
, nSeriesLength
, eChartType
);
2195 exportTrendlines( rSeries
);
2197 if( eChartType
!= chart::TYPEID_PIE
&&
2198 eChartType
!= chart::TYPEID_RADARLINE
)
2200 //export error bars here
2201 Reference
< XPropertySet
> xSeriesPropSet( xSource
, uno::UNO_QUERY
);
2202 Reference
< XPropertySet
> xErrorBarYProps
;
2203 xSeriesPropSet
->getPropertyValue("ErrorBarY") >>= xErrorBarYProps
;
2204 if(xErrorBarYProps
.is())
2205 exportErrorBar(xErrorBarYProps
, true);
2206 if (eChartType
!= chart::TYPEID_BAR
&&
2207 eChartType
!= chart::TYPEID_HORBAR
)
2209 Reference
< XPropertySet
> xErrorBarXProps
;
2210 xSeriesPropSet
->getPropertyValue("ErrorBarX") >>= xErrorBarXProps
;
2211 if(xErrorBarXProps
.is())
2212 exportErrorBar(xErrorBarXProps
, false);
2216 // export categories
2217 if( eChartType
!= chart::TYPEID_SCATTER
&& eChartType
!= chart::TYPEID_BUBBLE
&& mxCategoriesValues
.is() )
2218 exportSeriesCategory( mxCategoriesValues
);
2220 if( (eChartType
== chart::TYPEID_SCATTER
)
2221 || (eChartType
== chart::TYPEID_BUBBLE
) )
2224 Reference
< chart2::data::XLabeledDataSequence
> xSequence( lcl_getDataSequenceByRole( aSeqCnt
, "values-x" ) );
2225 if( xSequence
.is() )
2227 Reference
< chart2::data::XDataSequence
> xValues( xSequence
->getValues() );
2229 exportSeriesValues( xValues
, XML_xVal
);
2233 if( eChartType
== chart::TYPEID_BUBBLE
)
2236 Reference
< chart2::data::XLabeledDataSequence
> xSequence( lcl_getDataSequenceByRole( aSeqCnt
, "values-y" ) );
2237 if( xSequence
.is() )
2239 Reference
< chart2::data::XDataSequence
> xValues( xSequence
->getValues() );
2241 exportSeriesValues( xValues
, XML_yVal
);
2246 if( xValuesSeq
.is() )
2248 sal_Int32 nYValueType
= XML_val
;
2249 if( eChartType
== chart::TYPEID_SCATTER
)
2250 nYValueType
= XML_yVal
;
2251 else if( eChartType
== chart::TYPEID_BUBBLE
)
2252 nYValueType
= XML_bubbleSize
;
2253 exportSeriesValues( xValuesSeq
, nYValueType
);
2256 if( eChartType
== chart::TYPEID_SCATTER
2257 || eChartType
== chart::TYPEID_LINE
)
2260 // tdf103988: "corrupted" files with Bubble chart opening in MSO
2261 if( eChartType
== chart::TYPEID_BUBBLE
)
2262 pFS
->singleElement(FSNS(XML_c
, XML_bubble3D
), XML_val
, "0");
2264 pFS
->endElement( FSNS( XML_c
, XML_ser
) );
2271 void ChartExport::exportCandleStickSeries(
2272 const Sequence
< Reference
< chart2::XDataSeries
> > & aSeriesSeq
,
2275 for( const Reference
< chart2::XDataSeries
>& xSeries
: aSeriesSeq
)
2277 rPrimaryAxes
= lcl_isSeriesAttachedToFirstAxis(xSeries
);
2279 Reference
< chart2::data::XDataSource
> xSource( xSeries
, uno::UNO_QUERY
);
2282 // export series in correct order (as we don't store roles)
2283 // with japanese candlesticks: open, low, high, close
2284 // otherwise: low, high, close
2285 Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aSeqCnt(
2286 xSource
->getDataSequences());
2288 const char* sSeries
[] = {"values-first","values-max","values-min","values-last",nullptr};
2290 for( sal_Int32 idx
= 0; sSeries
[idx
] != nullptr ; idx
++ )
2292 Reference
< chart2::data::XLabeledDataSequence
> xLabeledSeq( lcl_getDataSequenceByRole( aSeqCnt
, OUString::createFromAscii(sSeries
[idx
]) ) );
2293 if( xLabeledSeq
.is())
2295 Reference
< chart2::data::XDataSequence
> xLabelSeq( xLabeledSeq
->getLabel());
2296 Reference
< chart2::data::XDataSequence
> xValueSeq( xLabeledSeq
->getValues());
2298 FSHelperPtr pFS
= GetFS();
2299 pFS
->startElement(FSNS(XML_c
, XML_ser
));
2301 // TODO: idx and order
2302 // idx attribute should start from 1 and not from 0.
2303 pFS
->singleElement( FSNS( XML_c
, XML_idx
),
2304 XML_val
, OString::number(idx
+1) );
2305 pFS
->singleElement( FSNS( XML_c
, XML_order
),
2306 XML_val
, OString::number(idx
+1) );
2309 if( xLabelSeq
.is() )
2310 exportSeriesText( xLabelSeq
);
2312 // TODO:export shape properties
2314 // export categories
2315 if( mxCategoriesValues
.is() )
2316 exportSeriesCategory( mxCategoriesValues
);
2319 if( xValueSeq
.is() )
2320 exportSeriesValues( xValueSeq
);
2322 pFS
->endElement( FSNS( XML_c
, XML_ser
) );
2330 void ChartExport::exportSeriesText( const Reference
< chart2::data::XDataSequence
> & xValueSeq
)
2332 FSHelperPtr pFS
= GetFS();
2333 pFS
->startElement(FSNS(XML_c
, XML_tx
));
2335 OUString aCellRange
= xValueSeq
->getSourceRangeRepresentation();
2336 aCellRange
= parseFormula( aCellRange
);
2337 pFS
->startElement(FSNS(XML_c
, XML_strRef
));
2339 pFS
->startElement(FSNS(XML_c
, XML_f
));
2340 pFS
->writeEscaped( aCellRange
);
2341 pFS
->endElement( FSNS( XML_c
, XML_f
) );
2343 OUString aLabelString
= lcl_flattenStringSequence(lcl_getLabelSequence(xValueSeq
));
2344 pFS
->startElement(FSNS(XML_c
, XML_strCache
));
2345 pFS
->singleElement(FSNS(XML_c
, XML_ptCount
), XML_val
, "1");
2346 pFS
->startElement(FSNS(XML_c
, XML_pt
), XML_idx
, "0");
2347 pFS
->startElement(FSNS(XML_c
, XML_v
));
2348 pFS
->writeEscaped( aLabelString
);
2349 pFS
->endElement( FSNS( XML_c
, XML_v
) );
2350 pFS
->endElement( FSNS( XML_c
, XML_pt
) );
2351 pFS
->endElement( FSNS( XML_c
, XML_strCache
) );
2352 pFS
->endElement( FSNS( XML_c
, XML_strRef
) );
2353 pFS
->endElement( FSNS( XML_c
, XML_tx
) );
2356 void ChartExport::exportSeriesCategory( const Reference
< chart2::data::XDataSequence
> & xValueSeq
)
2358 FSHelperPtr pFS
= GetFS();
2359 pFS
->startElement(FSNS(XML_c
, XML_cat
));
2361 OUString aCellRange
= xValueSeq
.is() ? xValueSeq
->getSourceRangeRepresentation() : OUString();
2362 const Sequence
< Sequence
< OUString
>> aFinalSplitSource
= getSplitCategoriesList(aCellRange
);
2363 aCellRange
= parseFormula( aCellRange
);
2365 if(aFinalSplitSource
.getLength() > 1)
2367 // export multi level category axis labels
2368 pFS
->startElement(FSNS(XML_c
, XML_multiLvlStrRef
));
2370 pFS
->startElement(FSNS(XML_c
, XML_f
));
2371 pFS
->writeEscaped(aCellRange
);
2372 pFS
->endElement(FSNS(XML_c
, XML_f
));
2374 pFS
->startElement(FSNS(XML_c
, XML_multiLvlStrCache
));
2375 pFS
->singleElement(FSNS(XML_c
, XML_ptCount
), XML_val
, OString::number(aFinalSplitSource
[0].getLength()));
2376 for(const auto& rSeq
: aFinalSplitSource
)
2378 pFS
->startElement(FSNS(XML_c
, XML_lvl
));
2379 for(sal_Int32 j
= 0; j
< rSeq
.getLength(); j
++)
2381 if(!rSeq
[j
].isEmpty())
2383 pFS
->startElement(FSNS(XML_c
, XML_pt
), XML_idx
, OString::number(j
));
2384 pFS
->startElement(FSNS(XML_c
, XML_v
));
2385 pFS
->writeEscaped(rSeq
[j
]);
2386 pFS
->endElement(FSNS(XML_c
, XML_v
));
2387 pFS
->endElement(FSNS(XML_c
, XML_pt
));
2390 pFS
->endElement(FSNS(XML_c
, XML_lvl
));
2393 pFS
->endElement(FSNS(XML_c
, XML_multiLvlStrCache
));
2394 pFS
->endElement(FSNS(XML_c
, XML_multiLvlStrRef
));
2398 // export single category axis labels
2399 pFS
->startElement(FSNS(XML_c
, XML_strRef
));
2401 pFS
->startElement(FSNS(XML_c
, XML_f
));
2402 pFS
->writeEscaped(aCellRange
);
2403 pFS
->endElement(FSNS(XML_c
, XML_f
));
2405 ::std::vector
< OUString
> aCategories
;
2406 lcl_fillCategoriesIntoStringVector(xValueSeq
, aCategories
);
2407 sal_Int32 ptCount
= aCategories
.size();
2408 pFS
->startElement(FSNS(XML_c
, XML_strCache
));
2409 pFS
->singleElement(FSNS(XML_c
, XML_ptCount
), XML_val
, OString::number(ptCount
));
2410 for (sal_Int32 i
= 0; i
< ptCount
; i
++)
2412 pFS
->startElement(FSNS(XML_c
, XML_pt
), XML_idx
, OString::number(i
));
2413 pFS
->startElement(FSNS(XML_c
, XML_v
));
2414 pFS
->writeEscaped(aCategories
[i
]);
2415 pFS
->endElement(FSNS(XML_c
, XML_v
));
2416 pFS
->endElement(FSNS(XML_c
, XML_pt
));
2419 pFS
->endElement(FSNS(XML_c
, XML_strCache
));
2420 pFS
->endElement(FSNS(XML_c
, XML_strRef
));
2423 pFS
->endElement( FSNS( XML_c
, XML_cat
) );
2426 void ChartExport::exportSeriesValues( const Reference
< chart2::data::XDataSequence
> & xValueSeq
, sal_Int32 nValueType
)
2428 FSHelperPtr pFS
= GetFS();
2429 pFS
->startElement(FSNS(XML_c
, nValueType
));
2431 OUString aCellRange
= xValueSeq
.is() ? xValueSeq
->getSourceRangeRepresentation() : OUString();
2432 aCellRange
= parseFormula( aCellRange
);
2433 // TODO: need to handle XML_multiLvlStrRef according to aCellRange
2434 pFS
->startElement(FSNS(XML_c
, XML_numRef
));
2436 pFS
->startElement(FSNS(XML_c
, XML_f
));
2437 pFS
->writeEscaped( aCellRange
);
2438 pFS
->endElement( FSNS( XML_c
, XML_f
) );
2440 ::std::vector
< double > aValues
= lcl_getAllValuesFromSequence( xValueSeq
);
2441 sal_Int32 ptCount
= aValues
.size();
2442 pFS
->startElement(FSNS(XML_c
, XML_numCache
));
2443 pFS
->startElement(FSNS(XML_c
, XML_formatCode
));
2444 // TODO: what format code?
2445 pFS
->writeEscaped( "General" );
2446 pFS
->endElement( FSNS( XML_c
, XML_formatCode
) );
2447 pFS
->singleElement(FSNS(XML_c
, XML_ptCount
), XML_val
, OString::number(ptCount
));
2449 for( sal_Int32 i
= 0; i
< ptCount
; i
++ )
2451 if (!rtl::math::isNan(aValues
[i
]))
2453 pFS
->startElement(FSNS(XML_c
, XML_pt
), XML_idx
, OString::number(i
));
2454 pFS
->startElement(FSNS(XML_c
, XML_v
));
2455 pFS
->write(aValues
[i
]);
2456 pFS
->endElement(FSNS(XML_c
, XML_v
));
2457 pFS
->endElement(FSNS(XML_c
, XML_pt
));
2461 pFS
->endElement( FSNS( XML_c
, XML_numCache
) );
2462 pFS
->endElement( FSNS( XML_c
, XML_numRef
) );
2463 pFS
->endElement( FSNS( XML_c
, nValueType
) );
2466 void ChartExport::exportShapeProps( const Reference
< XPropertySet
>& xPropSet
)
2468 FSHelperPtr pFS
= GetFS();
2469 pFS
->startElement(FSNS(XML_c
, XML_spPr
));
2471 exportFill( xPropSet
);
2472 WriteOutline( xPropSet
, getModel() );
2474 pFS
->endElement( FSNS( XML_c
, XML_spPr
) );
2477 void ChartExport::exportTextProps(const Reference
<XPropertySet
>& xPropSet
)
2479 FSHelperPtr pFS
= GetFS();
2480 pFS
->startElement(FSNS(XML_c
, XML_txPr
));
2482 sal_Int32 nRotation
= 0;
2483 if (auto xServiceInfo
= uno::Reference
<lang::XServiceInfo
>(xPropSet
, uno::UNO_QUERY
))
2485 double fMultiplier
= 0;
2486 // We have at least two possible units of returned value: degrees (e.g., for data labels),
2487 // and 100ths of degree (e.g., for axes labels). The latter is returned as an Any wrapping
2488 // a sal_Int32 value (see WrappedTextRotationProperty::convertInnerToOuterValue), while
2489 // the former is double. So we could test the contained type to decide which multiplier to
2490 // use. But testing the service info should be more robust.
2491 if (xServiceInfo
->supportsService("com.sun.star.chart.ChartAxis"))
2492 fMultiplier
= -600.0;
2493 else if (xServiceInfo
->supportsService("com.sun.star.chart2.DataSeries"))
2494 fMultiplier
= -60000.0;
2498 double fTextRotation
= 0.0;
2499 uno::Any aAny
= xPropSet
->getPropertyValue("TextRotation");
2500 if (aAny
.hasValue() && (aAny
>>= fTextRotation
))
2502 // The MS Office UI allows values only in range of [-90,90].
2503 if (fTextRotation
> 9000.0 && fTextRotation
< 27000.0)
2505 // Reflect the angle if the value is between 90° and 270°
2506 fTextRotation
-= 18000.0;
2508 else if (fTextRotation
>=27000.0)
2510 fTextRotation
-= 36000.0;
2512 nRotation
= std::round(fTextRotation
* fMultiplier
);
2518 pFS
->singleElement(FSNS(XML_a
, XML_bodyPr
), XML_rot
, OString::number(nRotation
));
2520 pFS
->singleElement(FSNS(XML_a
, XML_bodyPr
));
2522 pFS
->singleElement(FSNS(XML_a
, XML_lstStyle
));
2524 pFS
->startElement(FSNS(XML_a
, XML_p
));
2525 pFS
->startElement(FSNS(XML_a
, XML_pPr
));
2527 WriteRunProperties(xPropSet
, false, XML_defRPr
, true, o3tl::temporary(false),
2528 o3tl::temporary(sal_Int32()));
2530 pFS
->endElement(FSNS(XML_a
, XML_pPr
));
2531 pFS
->endElement(FSNS(XML_a
, XML_p
));
2532 pFS
->endElement(FSNS(XML_c
, XML_txPr
));
2535 void ChartExport::InitPlotArea( )
2537 Reference
< XPropertySet
> xDiagramProperties (mxDiagram
, uno::UNO_QUERY
);
2539 // Check for supported services and then the properties provided by this service.
2540 Reference
<lang::XServiceInfo
> xServiceInfo (mxDiagram
, uno::UNO_QUERY
);
2541 if (xServiceInfo
.is())
2543 if (xServiceInfo
->supportsService("com.sun.star.chart.ChartAxisZSupplier"))
2545 xDiagramProperties
->getPropertyValue("HasZAxis") >>= mbHasZAxis
;
2549 xDiagramProperties
->getPropertyValue("Dim3D") >>= mbIs3DChart
;
2551 if( mbHasCategoryLabels
&& mxNewDiagram
.is())
2553 Reference
< chart2::data::XLabeledDataSequence
> xCategories( lcl_getCategories( mxNewDiagram
) );
2554 if( xCategories
.is() )
2556 mxCategoriesValues
.set( xCategories
->getValues() );
2561 void ChartExport::exportAxes( )
2563 sal_Int32 nSize
= maAxes
.size();
2564 // let's export the axis types in the right order
2565 for ( sal_Int32 nSortIdx
= AXIS_PRIMARY_X
; nSortIdx
<= AXIS_SECONDARY_Y
; nSortIdx
++ )
2567 for ( sal_Int32 nIdx
= 0; nIdx
< nSize
; nIdx
++ )
2569 if (nSortIdx
== maAxes
[nIdx
].nAxisType
)
2570 exportAxis( maAxes
[nIdx
] );
2577 sal_Int32
getXAxisType(sal_Int32 eChartType
)
2579 if( (eChartType
== chart::TYPEID_SCATTER
)
2580 || (eChartType
== chart::TYPEID_BUBBLE
) )
2582 else if( eChartType
== chart::TYPEID_STOCK
)
2590 void ChartExport::exportAxis(const AxisIdPair
& rAxisIdPair
)
2592 // get some properties from document first
2593 bool bHasXAxisTitle
= false,
2594 bHasYAxisTitle
= false,
2595 bHasZAxisTitle
= false,
2596 bHasSecondaryXAxisTitle
= false,
2597 bHasSecondaryYAxisTitle
= false;
2598 bool bHasXAxisMajorGrid
= false,
2599 bHasXAxisMinorGrid
= false,
2600 bHasYAxisMajorGrid
= false,
2601 bHasYAxisMinorGrid
= false,
2602 bHasZAxisMajorGrid
= false,
2603 bHasZAxisMinorGrid
= false;
2605 Reference
< XPropertySet
> xDiagramProperties (mxDiagram
, uno::UNO_QUERY
);
2607 xDiagramProperties
->getPropertyValue("HasXAxisTitle") >>= bHasXAxisTitle
;
2608 xDiagramProperties
->getPropertyValue("HasYAxisTitle") >>= bHasYAxisTitle
;
2609 xDiagramProperties
->getPropertyValue("HasZAxisTitle") >>= bHasZAxisTitle
;
2610 xDiagramProperties
->getPropertyValue("HasSecondaryXAxisTitle") >>= bHasSecondaryXAxisTitle
;
2611 xDiagramProperties
->getPropertyValue("HasSecondaryYAxisTitle") >>= bHasSecondaryYAxisTitle
;
2613 xDiagramProperties
->getPropertyValue("HasXAxisGrid") >>= bHasXAxisMajorGrid
;
2614 xDiagramProperties
->getPropertyValue("HasYAxisGrid") >>= bHasYAxisMajorGrid
;
2615 xDiagramProperties
->getPropertyValue("HasZAxisGrid") >>= bHasZAxisMajorGrid
;
2617 xDiagramProperties
->getPropertyValue("HasXAxisHelpGrid") >>= bHasXAxisMinorGrid
;
2618 xDiagramProperties
->getPropertyValue("HasYAxisHelpGrid") >>= bHasYAxisMinorGrid
;
2619 xDiagramProperties
->getPropertyValue("HasZAxisHelpGrid") >>= bHasZAxisMinorGrid
;
2621 Reference
< XPropertySet
> xAxisProp
;
2622 Reference
< drawing::XShape
> xAxisTitle
;
2623 Reference
< beans::XPropertySet
> xMajorGrid
;
2624 Reference
< beans::XPropertySet
> xMinorGrid
;
2625 sal_Int32 nAxisType
= XML_catAx
;
2626 const char* sAxPos
= nullptr;
2628 switch( rAxisIdPair
.nAxisType
)
2630 case AXIS_PRIMARY_X
:
2632 Reference
< css::chart::XAxisXSupplier
> xAxisXSupp( mxDiagram
, uno::UNO_QUERY
);
2633 if( xAxisXSupp
.is())
2634 xAxisProp
= xAxisXSupp
->getXAxis();
2635 if( bHasXAxisTitle
)
2636 xAxisTitle
= xAxisXSupp
->getXAxisTitle();
2637 if( bHasXAxisMajorGrid
)
2638 xMajorGrid
= xAxisXSupp
->getXMainGrid();
2639 if( bHasXAxisMinorGrid
)
2640 xMinorGrid
= xAxisXSupp
->getXHelpGrid();
2642 sal_Int32 eChartType
= getChartType();
2643 nAxisType
= getXAxisType(eChartType
);
2644 // FIXME: axPos, need to check axis direction
2648 case AXIS_PRIMARY_Y
:
2650 Reference
< css::chart::XAxisYSupplier
> xAxisYSupp( mxDiagram
, uno::UNO_QUERY
);
2651 if( xAxisYSupp
.is())
2652 xAxisProp
= xAxisYSupp
->getYAxis();
2653 if( bHasYAxisTitle
)
2654 xAxisTitle
= xAxisYSupp
->getYAxisTitle();
2655 if( bHasYAxisMajorGrid
)
2656 xMajorGrid
= xAxisYSupp
->getYMainGrid();
2657 if( bHasYAxisMinorGrid
)
2658 xMinorGrid
= xAxisYSupp
->getYHelpGrid();
2660 nAxisType
= XML_valAx
;
2661 // FIXME: axPos, need to check axis direction
2665 case AXIS_PRIMARY_Z
:
2667 Reference
< css::chart::XAxisZSupplier
> xAxisZSupp( mxDiagram
, uno::UNO_QUERY
);
2668 if( xAxisZSupp
.is())
2669 xAxisProp
= xAxisZSupp
->getZAxis();
2670 if( bHasZAxisTitle
)
2671 xAxisTitle
= xAxisZSupp
->getZAxisTitle();
2672 if( bHasZAxisMajorGrid
)
2673 xMajorGrid
= xAxisZSupp
->getZMainGrid();
2674 if( bHasZAxisMinorGrid
)
2675 xMinorGrid
= xAxisZSupp
->getZHelpGrid();
2677 sal_Int32 eChartType
= getChartType( );
2678 if( (eChartType
== chart::TYPEID_SCATTER
)
2679 || (eChartType
== chart::TYPEID_BUBBLE
) )
2680 nAxisType
= XML_valAx
;
2681 else if( eChartType
== chart::TYPEID_STOCK
)
2682 nAxisType
= XML_dateAx
;
2683 else if( eChartType
== chart::TYPEID_BAR
)
2684 nAxisType
= XML_serAx
;
2685 // FIXME: axPos, need to check axis direction
2689 case AXIS_SECONDARY_X
:
2691 Reference
< css::chart::XTwoAxisXSupplier
> xAxisTwoXSupp( mxDiagram
, uno::UNO_QUERY
);
2692 if( xAxisTwoXSupp
.is())
2693 xAxisProp
= xAxisTwoXSupp
->getSecondaryXAxis();
2694 if( bHasSecondaryXAxisTitle
)
2696 Reference
< css::chart::XSecondAxisTitleSupplier
> xAxisSupp( mxDiagram
, uno::UNO_QUERY
);
2697 xAxisTitle
= xAxisSupp
->getSecondXAxisTitle();
2700 sal_Int32 eChartType
= getChartType();
2701 nAxisType
= getXAxisType(eChartType
);
2702 // FIXME: axPos, need to check axis direction
2706 case AXIS_SECONDARY_Y
:
2708 Reference
< css::chart::XTwoAxisYSupplier
> xAxisTwoYSupp( mxDiagram
, uno::UNO_QUERY
);
2709 if( xAxisTwoYSupp
.is())
2710 xAxisProp
= xAxisTwoYSupp
->getSecondaryYAxis();
2711 if( bHasSecondaryYAxisTitle
)
2713 Reference
< css::chart::XSecondAxisTitleSupplier
> xAxisSupp( mxDiagram
, uno::UNO_QUERY
);
2714 xAxisTitle
= xAxisSupp
->getSecondYAxisTitle();
2717 nAxisType
= XML_valAx
;
2718 // FIXME: axPos, need to check axis direction
2724 _exportAxis(xAxisProp
, xAxisTitle
, xMajorGrid
, xMinorGrid
, nAxisType
, sAxPos
, rAxisIdPair
);
2727 void ChartExport::_exportAxis(
2728 const Reference
< XPropertySet
>& xAxisProp
,
2729 const Reference
< drawing::XShape
>& xAxisTitle
,
2730 const Reference
< XPropertySet
>& xMajorGrid
,
2731 const Reference
< XPropertySet
>& xMinorGrid
,
2732 sal_Int32 nAxisType
,
2733 const char* sAxisPos
,
2734 const AxisIdPair
& rAxisIdPair
)
2736 FSHelperPtr pFS
= GetFS();
2737 pFS
->startElement(FSNS(XML_c
, nAxisType
));
2738 pFS
->singleElement(FSNS(XML_c
, XML_axId
), XML_val
, OString::number(rAxisIdPair
.nAxisId
));
2740 pFS
->startElement(FSNS(XML_c
, XML_scaling
));
2742 // logBase, min, max
2743 if(GetProperty( xAxisProp
, "Logarithmic" ) )
2745 bool bLogarithmic
= false;
2746 mAny
>>= bLogarithmic
;
2749 // default value is 10?
2750 pFS
->singleElement(FSNS(XML_c
, XML_logBase
), XML_val
, OString::number(10));
2754 // orientation: minMax, maxMin
2755 bool bReverseDirection
= false;
2756 if(GetProperty( xAxisProp
, "ReverseDirection" ) )
2757 mAny
>>= bReverseDirection
;
2759 const char* orientation
= bReverseDirection
? "maxMin":"minMax";
2760 pFS
->singleElement(FSNS(XML_c
, XML_orientation
), XML_val
, orientation
);
2762 bool bAutoMax
= false;
2763 if(GetProperty( xAxisProp
, "AutoMax" ) )
2766 if( !bAutoMax
&& (GetProperty( xAxisProp
, "Max" ) ) )
2770 pFS
->singleElement(FSNS(XML_c
, XML_max
), XML_val
, OString::number(dMax
));
2773 bool bAutoMin
= false;
2774 if(GetProperty( xAxisProp
, "AutoMin" ) )
2777 if( !bAutoMin
&& (GetProperty( xAxisProp
, "Min" ) ) )
2781 pFS
->singleElement(FSNS(XML_c
, XML_min
), XML_val
, OString::number(dMin
));
2784 pFS
->endElement( FSNS( XML_c
, XML_scaling
) );
2786 bool bVisible
= true;
2787 if( xAxisProp
.is() )
2789 xAxisProp
->getPropertyValue("Visible") >>= bVisible
;
2792 // only export each axis only once non-deleted
2793 bool bDeleted
= maExportedAxis
.find(rAxisIdPair
.nAxisType
) != maExportedAxis
.end();
2796 maExportedAxis
.insert(rAxisIdPair
.nAxisType
);
2798 pFS
->singleElement(FSNS(XML_c
, XML_delete
), XML_val
, !bDeleted
&& bVisible
? "0" : "1");
2800 // FIXME: axPos, need to check the property "ReverseDirection"
2801 pFS
->singleElement(FSNS(XML_c
, XML_axPos
), XML_val
, sAxisPos
);
2803 if( xMajorGrid
.is())
2805 pFS
->startElement(FSNS(XML_c
, XML_majorGridlines
));
2806 exportShapeProps( xMajorGrid
);
2807 pFS
->endElement( FSNS( XML_c
, XML_majorGridlines
) );
2811 if( xMinorGrid
.is())
2813 pFS
->startElement(FSNS(XML_c
, XML_minorGridlines
));
2814 exportShapeProps( xMinorGrid
);
2815 pFS
->endElement( FSNS( XML_c
, XML_minorGridlines
) );
2819 if( xAxisTitle
.is() )
2820 exportTitle( xAxisTitle
);
2822 bool bLinkedNumFmt
= true;
2823 if (GetProperty(xAxisProp
, "LinkNumberFormatToSource"))
2824 mAny
>>= bLinkedNumFmt
;
2826 OUString
aNumberFormatString("General");
2827 if (GetProperty(xAxisProp
, "NumberFormat"))
2831 aNumberFormatString
= getNumberFormatCode(nKey
);
2834 OString sNumberFormatString
= OUStringToOString(aNumberFormatString
, RTL_TEXTENCODING_UTF8
);
2835 pFS
->singleElement(FSNS(XML_c
, XML_numFmt
),
2836 XML_formatCode
, sNumberFormatString
.getStr(),
2837 XML_sourceLinked
, bLinkedNumFmt
? "1" : "0");
2840 sal_Int32 nValue
= 0;
2841 if(GetProperty( xAxisProp
, "Marks" ) )
2844 bool bInner
= nValue
& css::chart::ChartAxisMarks::INNER
;
2845 bool bOuter
= nValue
& css::chart::ChartAxisMarks::OUTER
;
2846 const char* majorTickMark
= nullptr;
2847 if( bInner
&& bOuter
)
2848 majorTickMark
= "cross";
2850 majorTickMark
= "in";
2852 majorTickMark
= "out";
2854 majorTickMark
= "none";
2855 pFS
->singleElement(FSNS(XML_c
, XML_majorTickMark
), XML_val
, majorTickMark
);
2858 if(GetProperty( xAxisProp
, "HelpMarks" ) )
2861 bool bInner
= nValue
& css::chart::ChartAxisMarks::INNER
;
2862 bool bOuter
= nValue
& css::chart::ChartAxisMarks::OUTER
;
2863 const char* minorTickMark
= nullptr;
2864 if( bInner
&& bOuter
)
2865 minorTickMark
= "cross";
2867 minorTickMark
= "in";
2869 minorTickMark
= "out";
2871 minorTickMark
= "none";
2872 pFS
->singleElement(FSNS(XML_c
, XML_minorTickMark
), XML_val
, minorTickMark
);
2875 const char* sTickLblPos
= nullptr;
2876 bool bDisplayLabel
= true;
2877 if(GetProperty( xAxisProp
, "DisplayLabels" ) )
2878 mAny
>>= bDisplayLabel
;
2879 if( bDisplayLabel
&& (GetProperty( xAxisProp
, "LabelPosition" ) ) )
2881 css::chart::ChartAxisLabelPosition eLabelPosition
= css::chart::ChartAxisLabelPosition_NEAR_AXIS
;
2882 mAny
>>= eLabelPosition
;
2883 switch( eLabelPosition
)
2885 case css::chart::ChartAxisLabelPosition_NEAR_AXIS
:
2886 case css::chart::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE
:
2887 sTickLblPos
= "nextTo";
2889 case css::chart::ChartAxisLabelPosition_OUTSIDE_START
:
2890 sTickLblPos
= "low";
2892 case css::chart::ChartAxisLabelPosition_OUTSIDE_END
:
2893 sTickLblPos
= "high";
2896 sTickLblPos
= "nextTo";
2902 sTickLblPos
= "none";
2904 pFS
->singleElement(FSNS(XML_c
, XML_tickLblPos
), XML_val
, sTickLblPos
);
2907 exportShapeProps( xAxisProp
);
2909 exportTextProps(xAxisProp
);
2911 pFS
->singleElement(FSNS(XML_c
, XML_crossAx
), XML_val
, OString::number(rAxisIdPair
.nCrossAx
));
2913 // crosses & crossesAt
2914 bool bCrossesValue
= false;
2915 const char* sCrosses
= nullptr;
2916 // do not export the CrossoverPosition/CrossoverValue, if the axis is deleted and not visible
2917 if( GetProperty( xAxisProp
, "CrossoverPosition" ) && !bDeleted
&& bVisible
)
2919 css::chart::ChartAxisPosition
ePosition( css::chart::ChartAxisPosition_ZERO
);
2923 case css::chart::ChartAxisPosition_START
:
2926 case css::chart::ChartAxisPosition_END
:
2929 case css::chart::ChartAxisPosition_ZERO
:
2930 sCrosses
= "autoZero";
2933 bCrossesValue
= true;
2938 if( bCrossesValue
&& GetProperty( xAxisProp
, "CrossoverValue" ) )
2942 pFS
->singleElement(FSNS(XML_c
, XML_crossesAt
), XML_val
, OString::number(dValue
));
2948 pFS
->singleElement(FSNS(XML_c
, XML_crosses
), XML_val
, sCrosses
);
2952 if( ( nAxisType
== XML_catAx
)
2953 || ( nAxisType
== XML_dateAx
) )
2955 // FIXME: seems not support? use default value,
2956 const char* const isAuto
= "1";
2957 pFS
->singleElement(FSNS(XML_c
, XML_auto
), XML_val
, isAuto
);
2959 if( nAxisType
== XML_catAx
)
2961 // FIXME: seems not support? lblAlgn
2962 const char* const sLblAlgn
= "ctr";
2963 pFS
->singleElement(FSNS(XML_c
, XML_lblAlgn
), XML_val
, sLblAlgn
);
2966 // FIXME: seems not support? lblOffset
2967 pFS
->singleElement(FSNS(XML_c
, XML_lblOffset
), XML_val
, OString::number(100));
2969 // FIXME: seems not support? noMultiLvlLbl
2970 pFS
->singleElement(FSNS(XML_c
, XML_noMultiLvlLbl
), XML_val
, OString::number(0));
2974 if( nAxisType
== XML_valAx
)
2976 if( mbIsCategoryPositionShifted
)
2977 pFS
->singleElement(FSNS(XML_c
, XML_crossBetween
), XML_val
, "between");
2979 pFS
->singleElement(FSNS(XML_c
, XML_crossBetween
), XML_val
, "midCat");
2983 bool bAutoStepMain
= false;
2984 if(GetProperty( xAxisProp
, "AutoStepMain" ) )
2985 mAny
>>= bAutoStepMain
;
2987 if( !bAutoStepMain
&& (GetProperty( xAxisProp
, "StepMain" ) ) )
2989 double dMajorUnit
= 0;
2990 mAny
>>= dMajorUnit
;
2991 pFS
->singleElement(FSNS(XML_c
, XML_majorUnit
), XML_val
, OString::number(dMajorUnit
));
2994 bool bAutoStepHelp
= false;
2995 if(GetProperty( xAxisProp
, "AutoStepHelp" ) )
2996 mAny
>>= bAutoStepHelp
;
2998 if( !bAutoStepHelp
&& (GetProperty( xAxisProp
, "StepHelp" ) ) )
3000 double dMinorUnit
= 0;
3001 mAny
>>= dMinorUnit
;
3002 if( GetProperty( xAxisProp
, "StepHelpCount" ) )
3004 sal_Int32 dMinorUnitCount
= 0;
3005 mAny
>>= dMinorUnitCount
;
3006 // tdf#114168 Don't save minor unit if number of step help count is 5 (which is default for MS Excel),
3007 // to allow proper .xlsx import. If minorUnit is set and majorUnit not, then it is impossible
3008 // to calculate StepHelpCount.
3009 if( dMinorUnitCount
!= 5 )
3011 pFS
->singleElement( FSNS( XML_c
, XML_minorUnit
),
3012 XML_val
, OString::number( dMinorUnit
) );
3017 if( nAxisType
== XML_valAx
&& GetProperty( xAxisProp
, "DisplayUnits" ) )
3019 bool bDisplayUnits
= false;
3020 mAny
>>= bDisplayUnits
;
3024 if(GetProperty( xAxisProp
, "BuiltInUnit" ))
3029 pFS
->startElement(FSNS(XML_c
, XML_dispUnits
));
3031 pFS
->singleElement(FSNS(XML_c
, XML_builtInUnit
), XML_val
, aVal
.toUtf8());
3033 pFS
->singleElement(FSNS( XML_c
, XML_dispUnitsLbl
));
3034 pFS
->endElement( FSNS( XML_c
, XML_dispUnits
) );
3040 pFS
->endElement( FSNS( XML_c
, nAxisType
) );
3045 struct LabelPlacementParam
3048 sal_Int32 meDefault
;
3050 std::unordered_set
<sal_Int32
> maAllowedValues
;
3052 LabelPlacementParam() :
3054 meDefault(css::chart::DataLabelPlacement::OUTSIDE
) {}
3058 maAllowedValues
.insert(css::chart::DataLabelPlacement::OUTSIDE
);
3059 maAllowedValues
.insert(css::chart::DataLabelPlacement::INSIDE
);
3060 maAllowedValues
.insert(css::chart::DataLabelPlacement::CENTER
);
3061 maAllowedValues
.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN
);
3062 maAllowedValues
.insert(css::chart::DataLabelPlacement::TOP
);
3063 maAllowedValues
.insert(css::chart::DataLabelPlacement::BOTTOM
);
3064 maAllowedValues
.insert(css::chart::DataLabelPlacement::LEFT
);
3065 maAllowedValues
.insert(css::chart::DataLabelPlacement::RIGHT
);
3066 maAllowedValues
.insert(css::chart::DataLabelPlacement::AVOID_OVERLAP
);
3070 const char* toOOXMLPlacement( sal_Int32 nPlacement
)
3074 case css::chart::DataLabelPlacement::OUTSIDE
: return "outEnd";
3075 case css::chart::DataLabelPlacement::INSIDE
: return "inEnd";
3076 case css::chart::DataLabelPlacement::CENTER
: return "ctr";
3077 case css::chart::DataLabelPlacement::NEAR_ORIGIN
: return "inBase";
3078 case css::chart::DataLabelPlacement::TOP
: return "t";
3079 case css::chart::DataLabelPlacement::BOTTOM
: return "b";
3080 case css::chart::DataLabelPlacement::LEFT
: return "l";
3081 case css::chart::DataLabelPlacement::RIGHT
: return "r";
3082 case css::chart::DataLabelPlacement::AVOID_OVERLAP
: return "bestFit";
3090 OUString
getFieldTypeString( const chart2::DataPointCustomLabelFieldType aType
)
3094 case chart2::DataPointCustomLabelFieldType_CATEGORYNAME
:
3095 return "CATEGORYNAME";
3097 case chart2::DataPointCustomLabelFieldType_SERIESNAME
:
3098 return "SERIESNAME";
3100 case chart2::DataPointCustomLabelFieldType_VALUE
:
3103 case chart2::DataPointCustomLabelFieldType_CELLREF
:
3112 void writeRunProperties( ChartExport
* pChartExport
, Reference
<XPropertySet
> const & xPropertySet
)
3114 bool bDummy
= false;
3116 pChartExport
->WriteRunProperties(xPropertySet
, false, XML_rPr
, true, bDummy
, nDummy
);
3119 void writeCustomLabel( const FSHelperPtr
& pFS
, ChartExport
* pChartExport
,
3120 const Sequence
<Reference
<chart2::XDataPointCustomLabelField
>>& rCustomLabelFields
)
3122 pFS
->startElement(FSNS(XML_c
, XML_tx
));
3123 pFS
->startElement(FSNS(XML_c
, XML_rich
));
3125 // TODO: body properties?
3126 pFS
->singleElement(FSNS(XML_a
, XML_bodyPr
));
3128 OUString sFieldType
;
3129 pFS
->startElement(FSNS(XML_a
, XML_p
));
3131 for (auto& rField
: rCustomLabelFields
)
3133 Reference
<XPropertySet
> xPropertySet(rField
, UNO_QUERY
);
3134 chart2::DataPointCustomLabelFieldType aType
= rField
->getFieldType();
3136 bool bNewParagraph
= false;
3138 if (aType
== chart2::DataPointCustomLabelFieldType_NEWLINE
)
3139 bNewParagraph
= true;
3140 else if (aType
!= chart2::DataPointCustomLabelFieldType_TEXT
)
3141 sFieldType
= getFieldTypeString(aType
);
3145 pFS
->endElement(FSNS(XML_a
, XML_p
));
3146 pFS
->startElement(FSNS(XML_a
, XML_p
));
3150 if (sFieldType
.isEmpty())
3153 pFS
->startElement(FSNS(XML_a
, XML_r
));
3154 writeRunProperties(pChartExport
, xPropertySet
);
3156 pFS
->startElement(FSNS(XML_a
, XML_t
));
3157 pFS
->writeEscaped(rField
->getString());
3158 pFS
->endElement(FSNS(XML_a
, XML_t
));
3160 pFS
->endElement(FSNS(XML_a
, XML_r
));
3165 pFS
->startElement(FSNS(XML_a
, XML_fld
), XML_id
, rField
->getGuid().toUtf8(), XML_type
,
3166 sFieldType
.toUtf8());
3167 writeRunProperties(pChartExport
, xPropertySet
);
3169 pFS
->startElement(FSNS(XML_a
, XML_t
));
3170 pFS
->writeEscaped(rField
->getString());
3171 pFS
->endElement(FSNS(XML_a
, XML_t
));
3173 pFS
->endElement(FSNS(XML_a
, XML_fld
));
3177 pFS
->endElement(FSNS(XML_a
, XML_p
));
3178 pFS
->endElement(FSNS(XML_c
, XML_rich
));
3179 pFS
->endElement(FSNS(XML_c
, XML_tx
));
3182 void writeLabelProperties( const FSHelperPtr
& pFS
, ChartExport
* pChartExport
,
3183 const uno::Reference
<beans::XPropertySet
>& xPropSet
, const LabelPlacementParam
& rLabelParam
)
3188 chart2::DataPointLabel aLabel
;
3189 Sequence
<Reference
<chart2::XDataPointCustomLabelField
>> aCustomLabelFields
;
3190 sal_Int32 nLabelBorderWidth
= 0;
3191 sal_Int32 nLabelBorderColor
= 0x00FFFFFF;
3193 xPropSet
->getPropertyValue("Label") >>= aLabel
;
3194 xPropSet
->getPropertyValue("CustomLabelFields") >>= aCustomLabelFields
;
3195 xPropSet
->getPropertyValue("LabelBorderWidth") >>= nLabelBorderWidth
;
3196 xPropSet
->getPropertyValue("LabelBorderColor") >>= nLabelBorderColor
;
3198 if (nLabelBorderWidth
> 0)
3200 pFS
->startElement(FSNS(XML_c
, XML_spPr
));
3201 pFS
->startElement(FSNS(XML_a
, XML_ln
), XML_w
,
3202 OString::number(convertHmmToEmu(nLabelBorderWidth
)));
3203 if (nLabelBorderColor
!= -1)
3205 pFS
->startElement(FSNS(XML_a
, XML_solidFill
));
3207 OString aStr
= OString::number(nLabelBorderColor
, 16).toAsciiUpperCase();
3208 pFS
->singleElement(FSNS(XML_a
, XML_srgbClr
), XML_val
, aStr
);
3210 pFS
->endElement(FSNS(XML_a
, XML_solidFill
));
3212 pFS
->endElement(FSNS(XML_a
, XML_ln
));
3213 pFS
->endElement(FSNS(XML_c
, XML_spPr
));
3216 if (aCustomLabelFields
.hasElements())
3217 writeCustomLabel(pFS
, pChartExport
, aCustomLabelFields
);
3219 if (rLabelParam
.mbExport
)
3221 sal_Int32 nLabelPlacement
= rLabelParam
.meDefault
;
3222 if (xPropSet
->getPropertyValue("LabelPlacement") >>= nLabelPlacement
)
3224 if (!rLabelParam
.maAllowedValues
.count(nLabelPlacement
))
3225 nLabelPlacement
= rLabelParam
.meDefault
;
3226 pFS
->singleElement(FSNS(XML_c
, XML_dLblPos
), XML_val
, toOOXMLPlacement(nLabelPlacement
));
3230 pFS
->singleElement(FSNS(XML_c
, XML_showLegendKey
), XML_val
, ToPsz10(aLabel
.ShowLegendSymbol
));
3231 pFS
->singleElement(FSNS(XML_c
, XML_showVal
), XML_val
, ToPsz10(aLabel
.ShowNumber
));
3232 pFS
->singleElement(FSNS(XML_c
, XML_showCatName
), XML_val
, ToPsz10(aLabel
.ShowCategoryName
));
3233 pFS
->singleElement(FSNS(XML_c
, XML_showSerName
), XML_val
, ToPsz10(false));
3234 pFS
->singleElement(FSNS(XML_c
, XML_showPercent
), XML_val
, ToPsz10(aLabel
.ShowNumberInPercent
));
3236 // Export the text "separator" if exists
3237 uno::Any aAny
= xPropSet
->getPropertyValue("LabelSeparator");
3238 if( aAny
.hasValue() )
3240 OUString nLabelSeparator
;
3241 aAny
>>= nLabelSeparator
;
3242 pFS
->startElement(FSNS(XML_c
, XML_separator
));
3243 pFS
->writeEscaped( nLabelSeparator
);
3244 pFS
->endElement( FSNS( XML_c
, XML_separator
) );
3250 void ChartExport::exportDataLabels(
3251 const uno::Reference
<chart2::XDataSeries
> & xSeries
, sal_Int32 nSeriesLength
, sal_Int32 eChartType
)
3253 if (!xSeries
.is() || nSeriesLength
<= 0)
3256 uno::Reference
<beans::XPropertySet
> xPropSet(xSeries
, uno::UNO_QUERY
);
3260 FSHelperPtr pFS
= GetFS();
3261 pFS
->startElement(FSNS(XML_c
, XML_dLbls
));
3263 bool bLinkedNumFmt
= true;
3264 if (GetProperty(xPropSet
, "LinkNumberFormatToSource"))
3265 mAny
>>= bLinkedNumFmt
;
3267 if (GetProperty(xPropSet
, "NumberFormat") || GetProperty(xPropSet
, "PercentageNumberFormat"))
3272 OUString aNumberFormatString
= getNumberFormatCode(nKey
);
3273 OString sNumberFormatString
= OUStringToOString(aNumberFormatString
, RTL_TEXTENCODING_UTF8
);
3275 pFS
->singleElement(FSNS(XML_c
, XML_numFmt
),
3276 XML_formatCode
, sNumberFormatString
,
3277 XML_sourceLinked
, ToPsz10(bLinkedNumFmt
));
3280 uno::Sequence
<sal_Int32
> aAttrLabelIndices
;
3281 xPropSet
->getPropertyValue("AttributedDataPoints") >>= aAttrLabelIndices
;
3283 // We must not export label placement property when the chart type doesn't
3284 // support this option in MS Office, else MS Office would think the file
3285 // is corrupt & refuse to open it.
3287 const chart::TypeGroupInfo
& rInfo
= chart::GetTypeGroupInfo(static_cast<chart::TypeId
>(eChartType
));
3288 LabelPlacementParam aParam
;
3289 aParam
.mbExport
= !mbIs3DChart
;
3290 aParam
.meDefault
= rInfo
.mnDefLabelPos
;
3292 switch (eChartType
) // diagram chart type
3294 case chart::TYPEID_PIE
:
3295 if(getChartType() == chart::TYPEID_DOUGHNUT
)
3296 aParam
.mbExport
= false;
3298 // All pie charts support label placement.
3299 aParam
.mbExport
= true;
3301 case chart::TYPEID_AREA
:
3302 case chart::TYPEID_RADARLINE
:
3303 case chart::TYPEID_RADARAREA
:
3304 // These chart types don't support label placement.
3305 aParam
.mbExport
= false;
3307 case chart::TYPEID_BAR
:
3308 if (mbStacked
|| mbPercent
)
3310 aParam
.maAllowedValues
.clear();
3311 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::CENTER
);
3312 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::INSIDE
);
3313 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN
);
3314 aParam
.meDefault
= css::chart::DataLabelPlacement::CENTER
;
3316 else // Clustered bar chart
3318 aParam
.maAllowedValues
.clear();
3319 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::CENTER
);
3320 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::INSIDE
);
3321 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::OUTSIDE
);
3322 aParam
.maAllowedValues
.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN
);
3323 aParam
.meDefault
= css::chart::DataLabelPlacement::OUTSIDE
;
3330 for (const sal_Int32 nIdx
: std::as_const(aAttrLabelIndices
))
3332 uno::Reference
<beans::XPropertySet
> xLabelPropSet
= xSeries
->getDataPointByIndex(nIdx
);
3334 if (!xLabelPropSet
.is())
3337 pFS
->startElement(FSNS(XML_c
, XML_dLbl
));
3338 pFS
->singleElement(FSNS(XML_c
, XML_idx
), XML_val
, OString::number(nIdx
));
3340 if (GetProperty(xLabelPropSet
, "NumberFormat") || GetProperty(xLabelPropSet
, "PercentageNumberFormat"))
3345 OUString aNumberFormatString
= getNumberFormatCode(nKey
);
3346 OString sNumberFormatString
= OUStringToOString(aNumberFormatString
, RTL_TEXTENCODING_UTF8
);
3348 pFS
->singleElement(FSNS(XML_c
, XML_numFmt
), XML_formatCode
, sNumberFormatString
.getStr(),
3349 XML_sourceLinked
, ToPsz10(bLinkedNumFmt
));
3352 // Individual label property that overwrites the baseline.
3353 exportTextProps( xLabelPropSet
);
3354 writeLabelProperties(pFS
, this, xLabelPropSet
, aParam
);
3355 pFS
->endElement(FSNS(XML_c
, XML_dLbl
));
3358 exportTextProps( xPropSet
);
3359 // Baseline label properties for all labels.
3360 writeLabelProperties(pFS
, this, xPropSet
, aParam
);
3362 pFS
->singleElement(FSNS(XML_c
, XML_showLeaderLines
), XML_val
, "0");
3364 pFS
->endElement(FSNS(XML_c
, XML_dLbls
));
3367 void ChartExport::exportDataPoints(
3368 const uno::Reference
< beans::XPropertySet
> & xSeriesProperties
,
3369 sal_Int32 nSeriesLength
, sal_Int32 eChartType
)
3371 uno::Reference
< chart2::XDataSeries
> xSeries( xSeriesProperties
, uno::UNO_QUERY
);
3372 bool bVaryColorsByPoint
= false;
3373 Sequence
< sal_Int32
> aDataPointSeq
;
3374 if( xSeriesProperties
.is())
3376 Any aAny
= xSeriesProperties
->getPropertyValue( "AttributedDataPoints" );
3377 aAny
>>= aDataPointSeq
;
3378 xSeriesProperties
->getPropertyValue( "VaryColorsByPoint" ) >>= bVaryColorsByPoint
;
3381 const sal_Int32
* pPoints
= aDataPointSeq
.getConstArray();
3383 Reference
< chart2::XColorScheme
> xColorScheme
;
3384 if( mxNewDiagram
.is())
3385 xColorScheme
.set( mxNewDiagram
->getDefaultColorScheme());
3387 if( bVaryColorsByPoint
&& xColorScheme
.is() )
3389 ::std::set
< sal_Int32
> aAttrPointSet
;
3390 ::std::copy( pPoints
, pPoints
+ aDataPointSeq
.getLength(),
3391 ::std::inserter( aAttrPointSet
, aAttrPointSet
.begin()));
3392 const ::std::set
< sal_Int32
>::const_iterator
aEndIt( aAttrPointSet
.end());
3393 for( nElement
= 0; nElement
< nSeriesLength
; ++nElement
)
3395 uno::Reference
< beans::XPropertySet
> xPropSet
;
3396 if( aAttrPointSet
.find( nElement
) != aEndIt
)
3400 xPropSet
= SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
3401 xSeries
, nElement
, getModel() );
3403 catch( const uno::Exception
& )
3405 DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
3410 // property set only containing the color
3411 xPropSet
.set( new ColorPropertySet( xColorScheme
->getColorByIndex( nElement
)));
3416 FSHelperPtr pFS
= GetFS();
3417 pFS
->startElement(FSNS(XML_c
, XML_dPt
));
3418 pFS
->singleElement(FSNS(XML_c
, XML_idx
), XML_val
, OString::number(nElement
));
3422 case chart::TYPEID_PIE
:
3423 case chart::TYPEID_DOUGHNUT
:
3425 if( xPropSet
.is() && GetProperty( xPropSet
, "SegmentOffset") )
3427 sal_Int32 nOffset
= 0;
3430 pFS
->singleElement( FSNS( XML_c
, XML_explosion
),
3431 XML_val
, OString::number( nOffset
) );
3438 exportShapeProps( xPropSet
);
3440 pFS
->endElement( FSNS( XML_c
, XML_dPt
) );
3445 // Export Data Point Property in Charts even if the VaryColors is false
3446 if( !bVaryColorsByPoint
)
3448 ::std::set
< sal_Int32
> aAttrPointSet
;
3449 ::std::copy( pPoints
, pPoints
+ aDataPointSeq
.getLength(),
3450 ::std::inserter( aAttrPointSet
, aAttrPointSet
.begin()));
3451 const ::std::set
< sal_Int32
>::const_iterator
aEndIt( aAttrPointSet
.end());
3452 for( nElement
= 0; nElement
< nSeriesLength
; ++nElement
)
3454 uno::Reference
< beans::XPropertySet
> xPropSet
;
3455 if( aAttrPointSet
.find( nElement
) != aEndIt
)
3459 xPropSet
= SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
3460 xSeries
, nElement
, getModel() );
3462 catch( const uno::Exception
& )
3464 DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
3470 FSHelperPtr pFS
= GetFS();
3471 pFS
->startElement(FSNS(XML_c
, XML_dPt
));
3472 pFS
->singleElement(FSNS(XML_c
, XML_idx
), XML_val
, OString::number(nElement
));
3474 switch( eChartType
)
3476 case chart::TYPEID_BUBBLE
:
3477 case chart::TYPEID_HORBAR
:
3478 case chart::TYPEID_BAR
:
3479 pFS
->singleElement(FSNS(XML_c
, XML_invertIfNegative
), XML_val
, "0");
3480 exportShapeProps(xPropSet
);
3483 case chart::TYPEID_LINE
:
3484 case chart::TYPEID_SCATTER
:
3485 case chart::TYPEID_RADARLINE
:
3486 exportMarker(xPropSet
);
3490 exportShapeProps(xPropSet
);
3494 pFS
->endElement( FSNS( XML_c
, XML_dPt
) );
3500 void ChartExport::exportAxesId(bool bPrimaryAxes
, bool bCheckCombinedAxes
)
3502 sal_Int32 nAxisIdx
, nAxisIdy
;
3503 bool bPrimaryAxisExists
= false;
3504 bool bSecondaryAxisExists
= false;
3505 // let's check which axis already exists and which axis is attached to the actual dataseries
3506 if (maAxes
.size() >= 2)
3508 bPrimaryAxisExists
= bPrimaryAxes
&& maAxes
[1].nAxisType
== AXIS_PRIMARY_Y
;
3509 bSecondaryAxisExists
= !bPrimaryAxes
&& maAxes
[1].nAxisType
== AXIS_SECONDARY_Y
;
3511 // tdf#114181 keep axes of combined charts
3512 if ( bCheckCombinedAxes
&& ( bPrimaryAxisExists
|| bSecondaryAxisExists
) )
3514 nAxisIdx
= maAxes
[0].nAxisId
;
3515 nAxisIdy
= maAxes
[1].nAxisId
;
3519 nAxisIdx
= lcl_generateRandomValue();
3520 nAxisIdy
= lcl_generateRandomValue();
3521 AxesType eXAxis
= bPrimaryAxes
? AXIS_PRIMARY_X
: AXIS_SECONDARY_X
;
3522 AxesType eYAxis
= bPrimaryAxes
? AXIS_PRIMARY_Y
: AXIS_SECONDARY_Y
;
3523 maAxes
.emplace_back( eXAxis
, nAxisIdx
, nAxisIdy
);
3524 maAxes
.emplace_back( eYAxis
, nAxisIdy
, nAxisIdx
);
3526 FSHelperPtr pFS
= GetFS();
3527 pFS
->singleElement(FSNS(XML_c
, XML_axId
), XML_val
, OString::number(nAxisIdx
));
3528 pFS
->singleElement(FSNS(XML_c
, XML_axId
), XML_val
, OString::number(nAxisIdy
));
3531 sal_Int32 nAxisIdz
= 0;
3532 if( isDeep3dChart() )
3534 nAxisIdz
= lcl_generateRandomValue();
3535 maAxes
.emplace_back( AXIS_PRIMARY_Z
, nAxisIdz
, nAxisIdy
);
3537 pFS
->singleElement(FSNS(XML_c
, XML_axId
), XML_val
, OString::number(nAxisIdz
));
3541 void ChartExport::exportGrouping( bool isBar
)
3543 FSHelperPtr pFS
= GetFS();
3544 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
3546 if( GetProperty( xPropSet
, "Stacked" ) )
3548 if( GetProperty( xPropSet
, "Percent" ) )
3551 const char* grouping
= nullptr;
3553 grouping
= "stacked";
3555 grouping
= "percentStacked";
3558 if( isBar
&& !isDeep3dChart() )
3560 grouping
= "clustered";
3563 grouping
= "standard";
3565 pFS
->singleElement(FSNS(XML_c
, XML_grouping
), XML_val
, grouping
);
3568 void ChartExport::exportTrendlines( const Reference
< chart2::XDataSeries
>& xSeries
)
3570 FSHelperPtr pFS
= GetFS();
3571 Reference
< chart2::XRegressionCurveContainer
> xRegressionCurveContainer( xSeries
, UNO_QUERY
);
3572 if( xRegressionCurveContainer
.is() )
3574 const Sequence
< Reference
< chart2::XRegressionCurve
> > aRegCurveSeq
= xRegressionCurveContainer
->getRegressionCurves();
3575 for( const Reference
< chart2::XRegressionCurve
>& xRegCurve
: aRegCurveSeq
)
3577 if (!xRegCurve
.is())
3580 Reference
< XPropertySet
> xProperties( xRegCurve
, uno::UNO_QUERY
);
3583 Reference
< lang::XServiceName
> xServiceName( xProperties
, UNO_QUERY
);
3584 if( !xServiceName
.is() )
3587 aService
= xServiceName
->getServiceName();
3589 if(aService
!= "com.sun.star.chart2.LinearRegressionCurve" &&
3590 aService
!= "com.sun.star.chart2.ExponentialRegressionCurve" &&
3591 aService
!= "com.sun.star.chart2.LogarithmicRegressionCurve" &&
3592 aService
!= "com.sun.star.chart2.PotentialRegressionCurve" &&
3593 aService
!= "com.sun.star.chart2.PolynomialRegressionCurve" &&
3594 aService
!= "com.sun.star.chart2.MovingAverageRegressionCurve")
3597 pFS
->startElement(FSNS(XML_c
, XML_trendline
));
3600 xProperties
->getPropertyValue("CurveName") >>= aName
;
3601 if(!aName
.isEmpty())
3603 pFS
->startElement(FSNS(XML_c
, XML_name
));
3604 pFS
->writeEscaped(aName
);
3605 pFS
->endElement( FSNS( XML_c
, XML_name
) );
3608 exportShapeProps( xProperties
);
3610 if( aService
== "com.sun.star.chart2.LinearRegressionCurve" )
3612 pFS
->singleElement(FSNS(XML_c
, XML_trendlineType
), XML_val
, "linear");
3614 else if( aService
== "com.sun.star.chart2.ExponentialRegressionCurve" )
3616 pFS
->singleElement(FSNS(XML_c
, XML_trendlineType
), XML_val
, "exp");
3618 else if( aService
== "com.sun.star.chart2.LogarithmicRegressionCurve" )
3620 pFS
->singleElement(FSNS(XML_c
, XML_trendlineType
), XML_val
, "log");
3622 else if( aService
== "com.sun.star.chart2.PotentialRegressionCurve" )
3624 pFS
->singleElement(FSNS(XML_c
, XML_trendlineType
), XML_val
, "power");
3626 else if( aService
== "com.sun.star.chart2.PolynomialRegressionCurve" )
3628 pFS
->singleElement(FSNS(XML_c
, XML_trendlineType
), XML_val
, "poly");
3630 sal_Int32 aDegree
= 2;
3631 xProperties
->getPropertyValue( "PolynomialDegree") >>= aDegree
;
3632 pFS
->singleElement(FSNS(XML_c
, XML_order
), XML_val
, OString::number(aDegree
));
3634 else if( aService
== "com.sun.star.chart2.MovingAverageRegressionCurve" )
3636 pFS
->singleElement(FSNS(XML_c
, XML_trendlineType
), XML_val
, "movingAvg");
3638 sal_Int32 aPeriod
= 2;
3639 xProperties
->getPropertyValue( "MovingAveragePeriod") >>= aPeriod
;
3641 pFS
->singleElement(FSNS(XML_c
, XML_period
), XML_val
, OString::number(aPeriod
));
3645 // should never happen
3646 // This would produce invalid OOXML files so we check earlier for the type
3650 double fExtrapolateForward
= 0.0;
3651 double fExtrapolateBackward
= 0.0;
3653 xProperties
->getPropertyValue("ExtrapolateForward") >>= fExtrapolateForward
;
3654 xProperties
->getPropertyValue("ExtrapolateBackward") >>= fExtrapolateBackward
;
3656 pFS
->singleElement( FSNS( XML_c
, XML_forward
),
3657 XML_val
, OString::number(fExtrapolateForward
) );
3659 pFS
->singleElement( FSNS( XML_c
, XML_backward
),
3660 XML_val
, OString::number(fExtrapolateBackward
) );
3662 bool bForceIntercept
= false;
3663 xProperties
->getPropertyValue("ForceIntercept") >>= bForceIntercept
;
3665 if (bForceIntercept
)
3667 double fInterceptValue
= 0.0;
3668 xProperties
->getPropertyValue("InterceptValue") >>= fInterceptValue
;
3670 pFS
->singleElement( FSNS( XML_c
, XML_intercept
),
3671 XML_val
, OString::number(fInterceptValue
) );
3674 // Equation properties
3675 Reference
< XPropertySet
> xEquationProperties( xRegCurve
->getEquationProperties() );
3678 bool bShowEquation
= false;
3679 xEquationProperties
->getPropertyValue("ShowEquation") >>= bShowEquation
;
3682 bool bShowCorrelationCoefficient
= false;
3683 xEquationProperties
->getPropertyValue("ShowCorrelationCoefficient") >>= bShowCorrelationCoefficient
;
3685 pFS
->singleElement( FSNS( XML_c
, XML_dispRSqr
),
3686 XML_val
, ToPsz10(bShowCorrelationCoefficient
) );
3688 pFS
->singleElement(FSNS(XML_c
, XML_dispEq
), XML_val
, ToPsz10(bShowEquation
));
3690 pFS
->endElement( FSNS( XML_c
, XML_trendline
) );
3695 void ChartExport::exportMarker(const Reference
< XPropertySet
>& xPropSet
)
3697 chart2::Symbol aSymbol
;
3698 if( GetProperty( xPropSet
, "Symbol" ) )
3701 if(aSymbol
.Style
!= chart2::SymbolStyle_STANDARD
&& aSymbol
.Style
!= chart2::SymbolStyle_AUTO
&& aSymbol
.Style
!= chart2::SymbolStyle_NONE
)
3704 FSHelperPtr pFS
= GetFS();
3705 pFS
->startElement(FSNS(XML_c
, XML_marker
));
3707 sal_Int32 nSymbol
= aSymbol
.StandardSymbol
;
3708 // TODO: more properties support for marker
3709 const char* pSymbolType
; // no initialization here, to let compiler warn if we have a code path
3710 // where it stays uninitialized
3714 pSymbolType
= "square";
3717 pSymbolType
= "diamond";
3723 pSymbolType
= "triangle";
3726 pSymbolType
= "circle";
3729 pSymbolType
= "star";
3732 pSymbolType
= "x"; // in MS office 2010 built in symbol marker 'X' is represented as 'x'
3735 pSymbolType
= "plus";
3738 pSymbolType
= "dash";
3741 pSymbolType
= "square";
3745 bool bSkipFormatting
= false;
3746 if (aSymbol
.Style
== chart2::SymbolStyle_NONE
)
3748 bSkipFormatting
= true;
3749 pSymbolType
= "none";
3752 pFS
->singleElement(FSNS(XML_c
, XML_symbol
), XML_val
, pSymbolType
);
3754 if (!bSkipFormatting
)
3756 awt::Size aSymbolSize
= aSymbol
.Size
;
3757 sal_Int32 nSize
= std::max( aSymbolSize
.Width
, aSymbolSize
.Height
);
3759 nSize
= nSize
/250.0*7.0 + 1; // just guessed based on some test cases,
3760 //the value is always 1 less than the actual value.
3761 nSize
= std::min
<sal_Int32
>( 72, std::max
<sal_Int32
>( 2, nSize
) );
3762 pFS
->singleElement(FSNS(XML_c
, XML_size
), XML_val
, OString::number(nSize
));
3764 pFS
->startElement(FSNS(XML_c
, XML_spPr
));
3766 util::Color aColor
= aSymbol
.FillColor
;
3767 if (GetProperty(xPropSet
, "Color"))
3772 pFS
->singleElement(FSNS(XML_a
, XML_noFill
));
3775 WriteSolidFill(::Color(aColor
));
3777 pFS
->endElement( FSNS( XML_c
, XML_spPr
) );
3780 pFS
->endElement( FSNS( XML_c
, XML_marker
) );
3783 void ChartExport::exportSmooth()
3785 FSHelperPtr pFS
= GetFS();
3786 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
3787 sal_Int32 nSplineType
= 0;
3788 if( GetProperty( xPropSet
, "SplineType" ) )
3789 mAny
>>= nSplineType
;
3790 const char* pVal
= nSplineType
!= 0 ? "1" : "0";
3791 pFS
->singleElement(FSNS(XML_c
, XML_smooth
), XML_val
, pVal
);
3794 void ChartExport::exportFirstSliceAng( )
3796 FSHelperPtr pFS
= GetFS();
3797 sal_Int32 nStartingAngle
= 0;
3798 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
3799 if( GetProperty( xPropSet
, "StartingAngle" ) )
3800 mAny
>>= nStartingAngle
;
3802 // convert to ooxml angle
3803 nStartingAngle
= (450 - nStartingAngle
) % 360;
3804 pFS
->singleElement(FSNS(XML_c
, XML_firstSliceAng
), XML_val
, OString::number(nStartingAngle
));
3809 const char* getErrorBarStyle(sal_Int32 nErrorBarStyle
)
3811 switch(nErrorBarStyle
)
3813 case cssc::ErrorBarStyle::NONE
:
3815 case cssc::ErrorBarStyle::VARIANCE
:
3817 case cssc::ErrorBarStyle::STANDARD_DEVIATION
:
3819 case cssc::ErrorBarStyle::ABSOLUTE
:
3821 case cssc::ErrorBarStyle::RELATIVE
:
3822 return "percentage";
3823 case cssc::ErrorBarStyle::ERROR_MARGIN
:
3825 case cssc::ErrorBarStyle::STANDARD_ERROR
:
3827 case cssc::ErrorBarStyle::FROM_DATA
:
3830 assert(false && "can't happen");
3835 Reference
< chart2::data::XDataSequence
> getLabeledSequence(
3836 const uno::Sequence
< uno::Reference
< chart2::data::XLabeledDataSequence
> >& aSequences
,
3839 OUString aDirection
;
3841 aDirection
= "positive";
3843 aDirection
= "negative";
3845 for( const auto& rSequence
: aSequences
)
3849 uno::Reference
< chart2::data::XDataSequence
> xSequence( rSequence
->getValues());
3850 uno::Reference
< beans::XPropertySet
> xSeqProp( xSequence
, uno::UNO_QUERY_THROW
);
3852 if( ( xSeqProp
->getPropertyValue( "Role" ) >>= aRole
) &&
3853 aRole
.match( "error-bars" ) && aRole
.indexOf(aDirection
) >= 0 )
3860 return Reference
< chart2::data::XDataSequence
> ();
3865 void ChartExport::exportErrorBar(const Reference
< XPropertySet
>& xErrorBarProps
, bool bYError
)
3867 sal_Int32 nErrorBarStyle
= cssc::ErrorBarStyle::NONE
;
3868 xErrorBarProps
->getPropertyValue("ErrorBarStyle") >>= nErrorBarStyle
;
3869 const char* pErrorBarStyle
= getErrorBarStyle(nErrorBarStyle
);
3873 FSHelperPtr pFS
= GetFS();
3874 pFS
->startElement(FSNS(XML_c
, XML_errBars
));
3875 pFS
->singleElement(FSNS(XML_c
, XML_errDir
), XML_val
, bYError
? "y" : "x");
3876 bool bPositive
= false, bNegative
= false;
3877 xErrorBarProps
->getPropertyValue("ShowPositiveError") >>= bPositive
;
3878 xErrorBarProps
->getPropertyValue("ShowNegativeError") >>= bNegative
;
3879 const char* pErrBarType
;
3880 if(bPositive
&& bNegative
)
3881 pErrBarType
= "both";
3883 pErrBarType
= "plus";
3885 pErrBarType
= "minus";
3888 // what the hell should we do now?
3889 // at least this makes the file valid
3890 pErrBarType
= "both";
3892 pFS
->singleElement(FSNS(XML_c
, XML_errBarType
), XML_val
, pErrBarType
);
3893 pFS
->singleElement(FSNS(XML_c
, XML_errValType
), XML_val
, pErrorBarStyle
);
3894 pFS
->singleElement(FSNS(XML_c
, XML_noEndCap
), XML_val
, "0");
3895 if(nErrorBarStyle
== cssc::ErrorBarStyle::FROM_DATA
)
3897 uno::Reference
< chart2::data::XDataSource
> xDataSource(xErrorBarProps
, uno::UNO_QUERY
);
3898 Sequence
< Reference
< chart2::data::XLabeledDataSequence
> > aSequences
=
3899 xDataSource
->getDataSequences();
3903 exportSeriesValues(getLabeledSequence(aSequences
, true), XML_plus
);
3908 exportSeriesValues(getLabeledSequence(aSequences
, false), XML_minus
);
3914 if(nErrorBarStyle
== cssc::ErrorBarStyle::STANDARD_DEVIATION
)
3916 xErrorBarProps
->getPropertyValue("Weight") >>= nVal
;
3921 xErrorBarProps
->getPropertyValue("PositiveError") >>= nVal
;
3923 xErrorBarProps
->getPropertyValue("NegativeError") >>= nVal
;
3926 pFS
->singleElement(FSNS(XML_c
, XML_val
), XML_val
, OString::number(nVal
));
3929 exportShapeProps( xErrorBarProps
);
3931 pFS
->endElement( FSNS( XML_c
, XML_errBars
) );
3934 void ChartExport::exportView3D()
3936 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
3937 if( !xPropSet
.is() )
3939 FSHelperPtr pFS
= GetFS();
3940 pFS
->startElement(FSNS(XML_c
, XML_view3D
));
3941 sal_Int32 eChartType
= getChartType( );
3943 if( GetProperty( xPropSet
, "RotationHorizontal" ) )
3945 sal_Int32 nRotationX
= 0;
3946 mAny
>>= nRotationX
;
3947 if( nRotationX
< 0 )
3949 if(eChartType
== chart::TYPEID_PIE
)
3951 /* In OOXML we get value in 0..90 range for pie chart X rotation , whereas we expect it to be in -90..90 range,
3952 so we convert that during import. It is modified in View3DConverter::convertFromModel()
3953 here we convert it back to 0..90 as we received in import */
3954 nRotationX
+= 90; // X rotation (map Chart2 [-179,180] to OOXML [0..90])
3957 nRotationX
+= 360; // X rotation (map Chart2 [-179,180] to OOXML [-90..90])
3959 pFS
->singleElement(FSNS(XML_c
, XML_rotX
), XML_val
, OString::number(nRotationX
));
3962 if( GetProperty( xPropSet
, "RotationVertical" ) )
3964 // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
3965 if( eChartType
== chart::TYPEID_PIE
&& GetProperty( xPropSet
, "StartingAngle" ) )
3967 // Y rotation used as 'first pie slice angle' in 3D pie charts
3968 sal_Int32 nStartingAngle
=0;
3969 mAny
>>= nStartingAngle
;
3970 // convert to ooxml angle
3971 nStartingAngle
= (450 - nStartingAngle
) % 360;
3972 pFS
->singleElement(FSNS(XML_c
, XML_rotY
), XML_val
, OString::number(nStartingAngle
));
3976 sal_Int32 nRotationY
= 0;
3977 mAny
>>= nRotationY
;
3978 // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
3979 if( nRotationY
< 0 )
3981 pFS
->singleElement(FSNS(XML_c
, XML_rotY
), XML_val
, OString::number(nRotationY
));
3985 if( GetProperty( xPropSet
, "RightAngledAxes" ) )
3987 bool bRightAngled
= false;
3988 mAny
>>= bRightAngled
;
3989 const char* sRightAngled
= bRightAngled
? "1":"0";
3990 pFS
->singleElement(FSNS(XML_c
, XML_rAngAx
), XML_val
, sRightAngled
);
3993 if( GetProperty( xPropSet
, "Perspective" ) )
3995 sal_Int32 nPerspective
= 0;
3996 mAny
>>= nPerspective
;
3997 // map Chart2 [0,100] to OOXML [0..200]
3999 pFS
->singleElement(FSNS(XML_c
, XML_perspective
), XML_val
, OString::number(nPerspective
));
4001 pFS
->endElement( FSNS( XML_c
, XML_view3D
) );
4004 bool ChartExport::isDeep3dChart()
4006 bool isDeep
= false;
4009 Reference
< XPropertySet
> xPropSet( mxDiagram
, uno::UNO_QUERY
);
4010 if( GetProperty( xPropSet
, "Deep" ) )
4016 OUString
ChartExport::getNumberFormatCode(sal_Int32 nKey
) const
4018 /* XXX if this was called more than one or two times per export the two
4019 * SvNumberFormatter instances and NfKeywordTable should be member
4020 * variables and initialized only once. */
4022 OUString
aCode("General"); // init with fallback
4023 uno::Reference
<util::XNumberFormatsSupplier
> xNumberFormatsSupplier(mxChartModel
, uno::UNO_QUERY_THROW
);
4024 SvNumberFormatsSupplierObj
* pSupplierObj
= comphelper::getUnoTunnelImplementation
<SvNumberFormatsSupplierObj
>( xNumberFormatsSupplier
);
4028 SvNumberFormatter
* pNumberFormatter
= pSupplierObj
->GetNumberFormatter();
4029 if (!pNumberFormatter
)
4032 SvNumberFormatter
aTempFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US
);
4033 NfKeywordTable aKeywords
;
4034 aTempFormatter
.FillKeywordTableForExcel( aKeywords
);
4035 aCode
= pNumberFormatter
->GetFormatStringForExcel( nKey
, aKeywords
, aTempFormatter
);
4043 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */