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