LibreOffice Module oox (master)  1
chartexport.cxx
Go to the documentation of this file.
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/properties.hxx>
22 #include <oox/token/tokens.hxx>
26 #include <oox/export/utils.hxx>
28 
29 #include <cstdio>
30 #include <limits>
31 
32 #include <com/sun/star/awt/Gradient.hpp>
33 #include <com/sun/star/chart/XChartDocument.hpp>
34 #include <com/sun/star/chart/ChartLegendPosition.hpp>
35 #include <com/sun/star/chart/XTwoAxisXSupplier.hpp>
36 #include <com/sun/star/chart/XTwoAxisYSupplier.hpp>
37 #include <com/sun/star/chart/XAxisZSupplier.hpp>
38 #include <com/sun/star/chart/ChartDataRowSource.hpp>
39 #include <com/sun/star/chart/X3DDisplay.hpp>
40 #include <com/sun/star/chart/XStatisticDisplay.hpp>
41 #include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp>
42 #include <com/sun/star/chart/ChartSymbolType.hpp>
43 #include <com/sun/star/chart/ChartAxisMarks.hpp>
44 #include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
45 #include <com/sun/star/chart/ChartAxisPosition.hpp>
46 #include <com/sun/star/chart/ChartSolidType.hpp>
47 #include <com/sun/star/chart/DataLabelPlacement.hpp>
48 #include <com/sun/star/chart/ErrorBarStyle.hpp>
49 #include <com/sun/star/chart/MissingValueTreatment.hpp>
50 #include <com/sun/star/chart/XDiagramPositioning.hpp>
51 #include <com/sun/star/chart/TimeIncrement.hpp>
52 #include <com/sun/star/chart/TimeInterval.hpp>
53 #include <com/sun/star/chart/TimeUnit.hpp>
54 
55 #include <com/sun/star/chart2/RelativePosition.hpp>
56 #include <com/sun/star/chart2/RelativeSize.hpp>
57 #include <com/sun/star/chart2/XChartDocument.hpp>
58 #include <com/sun/star/chart2/XDiagram.hpp>
59 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
60 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
61 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
62 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
63 #include <com/sun/star/chart2/DataPointLabel.hpp>
64 #include <com/sun/star/chart2/XDataPointCustomLabelField.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/XDataProvider.hpp>
69 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
70 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
71 #include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
72 #include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
73 #include <com/sun/star/chart2/AxisType.hpp>
74 
75 #include <com/sun/star/beans/XPropertySet.hpp>
76 #include <com/sun/star/container/XNameAccess.hpp>
77 #include <com/sun/star/drawing/XShape.hpp>
78 #include <com/sun/star/drawing/XShapes.hpp>
79 #include <com/sun/star/drawing/FillStyle.hpp>
80 #include <com/sun/star/drawing/LineStyle.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/container/XNamed.hpp>
91 #include <com/sun/star/embed/XVisualObject.hpp>
92 #include <com/sun/star/embed/Aspects.hpp>
93 
95 #include <comphelper/random.hxx>
96 #include <utility>
98 #include "ColorPropertySet.hxx"
99 
100 #include <svl/numformat.hxx>
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 <o3tl/temporary.hxx>
110 #include <o3tl/sorted_vector.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::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 class lcl_MatchesRole
137 {
138 public:
139  explicit lcl_MatchesRole( const OUString & aRole ) :
140  m_aRole( aRole )
141  {}
142 
143  bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const
144  {
145  if( !xSeq.is() )
146  return false;
147  Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY );
148  OUString aRole;
149 
150  return ( xProp.is() &&
151  (xProp->getPropertyValue( "Role" ) >>= aRole ) &&
152  m_aRole == aRole );
153  }
154 
155 private:
156  OUString m_aRole;
157 };
158 
159 }
160 
162 {
163  bHasDateCategories = false;
165  try
166  {
168  xDiagram, uno::UNO_QUERY_THROW );
170  xCooSysCnt->getCoordinateSystems());
171  for( const auto& xCooSys : aCooSysSeq )
172  {
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  bHasDateCategories = aScaleData.AxisType == chart2::AxisType::DATE;
187  xResult.set( aScaleData.Categories );
188  break;
189  }
190  }
191  }
192  }
193  }
194  }
195  catch( const uno::Exception & )
196  {
198  }
199 
200  return xResult;
201 }
202 
206  const OUString & rRole )
207 {
209 
210  const Reference< chart2::data::XLabeledDataSequence > * pBegin = aLabeledSeq.getConstArray();
211  const Reference< chart2::data::XLabeledDataSequence > * pEnd = pBegin + aLabeledSeq.getLength();
213  ::std::find_if( pBegin, pEnd, lcl_MatchesRole( rRole ));
214 
215  if( pMatch != pEnd )
216  return *pMatch;
217 
218  return aNoResult;
219 }
220 
222 {
223  //categories are always the first sequence
224  Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram());
225  bool bDateCategories;
226  Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xDiagram, bDateCategories ) );
227  return xCategories.is();
228 }
229 
231 {
232  bool bCategoryPositionShifted = false;
233  try
234  {
236  xDiagram, uno::UNO_QUERY_THROW);
238  xCooSysCnt->getCoordinateSystems());
239  for (const auto& xCooSys : aCooSysSeq)
240  {
241  OSL_ASSERT(xCooSys.is());
242  if( 0 < xCooSys->getDimension() && 0 <= xCooSys->getMaximumAxisIndexByDimension(0) )
243  {
244  Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(0, 0);
245  OSL_ASSERT(xAxis.is());
246  if (xAxis.is())
247  {
248  chart2::ScaleData aScaleData = xAxis->getScaleData();
249  bCategoryPositionShifted = aScaleData.ShiftedCategoryPosition;
250  break;
251  }
252  }
253  }
254  }
255  catch (const uno::Exception&)
256  {
258  }
259 
260  return bCategoryPositionShifted;
261 }
262 
263 static sal_Int32 lcl_getCategoryAxisType( const Reference< chart2::XDiagram >& xDiagram, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
264 {
265  sal_Int32 nAxisType = -1;
266  try
267  {
269  xDiagram, uno::UNO_QUERY_THROW);
271  xCooSysCnt->getCoordinateSystems());
272  for( const auto& xCooSys : aCooSysSeq )
273  {
274  OSL_ASSERT(xCooSys.is());
275  if( nDimensionIndex < xCooSys->getDimension() && nAxisIndex <= xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex) )
276  {
277  Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(nDimensionIndex, nAxisIndex);
278  OSL_ASSERT(xAxis.is());
279  if( xAxis.is() )
280  {
281  chart2::ScaleData aScaleData = xAxis->getScaleData();
282  nAxisType = aScaleData.AxisType;
283  break;
284  }
285  }
286  }
287  }
288  catch (const uno::Exception&)
289  {
291  }
292 
293  return nAxisType;
294 }
295 
296 static OUString lclGetTimeUnitToken( sal_Int32 nTimeUnit )
297 {
298  switch( nTimeUnit )
299  {
300  case cssc::TimeUnit::DAY: return "days";
301  case cssc::TimeUnit::MONTH: return "months";
302  case cssc::TimeUnit::YEAR: return "years";
303  default: OSL_ENSURE(false, "lclGetTimeUnitToken - unexpected time unit");
304  }
305  return "days";
306 }
307 
308 static cssc::TimeIncrement lcl_getDateTimeIncrement( const Reference< chart2::XDiagram >& xDiagram, sal_Int32 nAxisIndex )
309 {
310  cssc::TimeIncrement aTimeIncrement;
311  try
312  {
314  xDiagram, uno::UNO_QUERY_THROW);
316  xCooSysCnt->getCoordinateSystems());
317  for( const auto& xCooSys : aCooSysSeq )
318  {
319  OSL_ASSERT(xCooSys.is());
320  if( 0 < xCooSys->getDimension() && nAxisIndex <= xCooSys->getMaximumAxisIndexByDimension(0) )
321  {
322  Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(0, nAxisIndex);
323  OSL_ASSERT(xAxis.is());
324  if( xAxis.is() )
325  {
326  chart2::ScaleData aScaleData = xAxis->getScaleData();
327  aTimeIncrement = aScaleData.TimeIncrement;
328  break;
329  }
330  }
331  }
332  }
333  catch (const uno::Exception&)
334  {
336  }
337 
338  return aTimeIncrement;
339 }
340 
342  const Reference< chart2::XDataSeries > & xDataSeries )
343 {
344  bool bResult=true;
345 
346  try
347  {
348  sal_Int32 nAxisIndex = 0;
349  Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW );
350  xProp->getPropertyValue("AttachedAxisIndex") >>= nAxisIndex;
351  bResult = (0==nAxisIndex);
352  }
353  catch( const uno::Exception & )
354  {
356  }
357 
358  return bResult;
359 }
360 
361 static OUString lcl_flattenStringSequence( const Sequence< OUString > & rSequence )
362 {
363  OUStringBuffer aResult;
364  bool bPrecedeWithSpace = false;
365  for( const auto& rString : rSequence )
366  {
367  if( !rString.isEmpty())
368  {
369  if( bPrecedeWithSpace )
370  aResult.append( ' ' );
371  aResult.append( rString );
372  bPrecedeWithSpace = true;
373  }
374  }
375  return aResult.makeStringAndClear();
376 }
377 
379 {
380  Sequence< OUString > aLabels;
381 
382  uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xLabelSeq, uno::UNO_QUERY );
383  if( xTextualDataSequence.is())
384  {
385  aLabels = xTextualDataSequence->getTextualData();
386  }
387  else if( xLabelSeq.is())
388  {
389  const Sequence< uno::Any > aAnies( xLabelSeq->getData());
390  aLabels.realloc( aAnies.getLength());
391  auto pLabels = aLabels.getArray();
392  for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
393  aAnies[i] >>= pLabels[i];
394  }
395 
396  return aLabels;
397 }
398 
400  const Reference< chart2::data::XDataSequence > & xCategories,
401  ::std::vector< OUString > & rOutCategories )
402 {
403  OSL_ASSERT( xCategories.is());
404  if( !xCategories.is())
405  return;
406  Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xCategories, uno::UNO_QUERY );
407  if( xTextualDataSequence.is())
408  {
409  rOutCategories.clear();
410  const Sequence< OUString > aTextData( xTextualDataSequence->getTextualData());
411  rOutCategories.insert( rOutCategories.end(), aTextData.begin(), aTextData.end() );
412  }
413  else
414  {
415  Sequence< uno::Any > aAnies( xCategories->getData());
416  rOutCategories.resize( aAnies.getLength());
417  for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
418  aAnies[i] >>= rOutCategories[i];
419  }
420 }
421 
422 static ::std::vector< double > lcl_getAllValuesFromSequence( const Reference< chart2::data::XDataSequence > & xSeq )
423 {
424  ::std::vector< double > aResult;
425 
426  Reference< chart2::data::XNumericalDataSequence > xNumSeq( xSeq, uno::UNO_QUERY );
427  if( xNumSeq.is())
428  {
429  const Sequence< double > aValues( xNumSeq->getNumericalData());
430  aResult.insert( aResult.end(), aValues.begin(), aValues.end() );
431  }
432  else if( xSeq.is())
433  {
434  Sequence< uno::Any > aAnies( xSeq->getData());
435  aResult.resize( aAnies.getLength(), std::numeric_limits<double>::quiet_NaN() );
436  for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
437  aAnies[i] >>= aResult[i];
438  }
439  return aResult;
440 }
441 
442 static sal_Int32 lcl_getChartType( std::u16string_view sChartType )
443 {
444  chart::TypeId eChartTypeId = chart::TYPEID_UNKNOWN;
445  if( sChartType == u"com.sun.star.chart.BarDiagram"
446  || sChartType == u"com.sun.star.chart2.ColumnChartType" )
447  eChartTypeId = chart::TYPEID_BAR;
448  else if( sChartType == u"com.sun.star.chart.AreaDiagram"
449  || sChartType == u"com.sun.star.chart2.AreaChartType" )
450  eChartTypeId = chart::TYPEID_AREA;
451  else if( sChartType == u"com.sun.star.chart.LineDiagram"
452  || sChartType == u"com.sun.star.chart2.LineChartType" )
453  eChartTypeId = chart::TYPEID_LINE;
454  else if( sChartType == u"com.sun.star.chart.PieDiagram"
455  || sChartType == u"com.sun.star.chart2.PieChartType" )
456  eChartTypeId = chart::TYPEID_PIE;
457  else if( sChartType == u"com.sun.star.chart.DonutDiagram"
458  || sChartType == u"com.sun.star.chart2.DonutChartType" )
459  eChartTypeId = chart::TYPEID_DOUGHNUT;
460  else if( sChartType == u"com.sun.star.chart.XYDiagram"
461  || sChartType == u"com.sun.star.chart2.ScatterChartType" )
462  eChartTypeId = chart::TYPEID_SCATTER;
463  else if( sChartType == u"com.sun.star.chart.NetDiagram"
464  || sChartType == u"com.sun.star.chart2.NetChartType" )
465  eChartTypeId = chart::TYPEID_RADARLINE;
466  else if( sChartType == u"com.sun.star.chart.FilledNetDiagram"
467  || sChartType == u"com.sun.star.chart2.FilledNetChartType" )
468  eChartTypeId = chart::TYPEID_RADARAREA;
469  else if( sChartType == u"com.sun.star.chart.StockDiagram"
470  || sChartType == u"com.sun.star.chart2.CandleStickChartType" )
471  eChartTypeId = chart::TYPEID_STOCK;
472  else if( sChartType == u"com.sun.star.chart.BubbleDiagram"
473  || sChartType == u"com.sun.star.chart2.BubbleChartType" )
474  eChartTypeId = chart::TYPEID_BUBBLE;
475 
476  return eChartTypeId;
477 }
478 
479 static sal_Int32 lcl_generateRandomValue()
480 {
481  return comphelper::rng::uniform_int_distribution(0, 100000000-1);
482 }
483 
484 static sal_Int32 lcl_getAlphaFromTransparenceGradient(const awt::Gradient& rGradient, bool bStart)
485 {
486  // Our alpha is a gray color value.
487  sal_uInt8 nRed = ::Color(ColorTransparency, bStart ? rGradient.StartColor : rGradient.EndColor).GetRed();
488  // drawingML alpha is a percentage on a 0..100000 scale.
489  return (255 - nRed) * oox::drawingml::MAX_PERCENT / 255;
490 }
491 
492 bool DataLabelsRange::empty() const
493 {
494  return maLabels.empty();
495 }
496 
497 size_t DataLabelsRange::count() const
498 {
499  return maLabels.size();
500 }
501 
502 bool DataLabelsRange::hasLabel(sal_Int32 nIndex) const
503 {
504  return maLabels.find(nIndex) != maLabels.end();
505 }
506 
507 OUString DataLabelsRange::getRange() const
508 {
509  return maRange;
510 }
511 
512 void DataLabelsRange::setRange(const OUString& rRange)
513 {
514  maRange = rRange;
515 }
516 
517 void DataLabelsRange::setLabel(sal_Int32 nIndex, const OUString& rText)
518 {
519  maLabels.emplace(nIndex, rText);
520 }
521 
522 DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::begin() const
523 {
524  return maLabels.begin();
525 }
526 
527 DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::end() const
528 {
529  return maLabels.end();
530 }
531 
532 ChartExport::ChartExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, Reference< frame::XModel > const & xModel, XmlFilterBase* pFB, DocumentType eDocumentType )
533  : DrawingML( std::move(pFS), pFB, eDocumentType )
534  , mnXmlNamespace( nXmlNamespace )
535  , mnSeriesCount(0)
536  , mxChartModel( xModel )
537  , mpURLTransformer(std::make_shared<URLTransformer>())
538  , mbHasCategoryLabels( false )
539  , mbHasZAxis( false )
540  , mbIs3DChart( false )
541  , mbStacked(false)
542  , mbPercent(false)
543  , mbHasDateCategories(false)
544 {
545 }
546 
547 void ChartExport::SetURLTranslator(const std::shared_ptr<URLTransformer>& pTransformer)
548 {
549  mpURLTransformer = pTransformer;
550 }
551 
553 {
554  OUString sChartType = mxDiagram->getDiagramType();
555  return lcl_getChartType( sChartType );
556 }
557 
558 namespace {
559 
560 uno::Sequence< beans::PropertyValue > createArguments(
561  const OUString & rRangeRepresentation, bool bUseColumns)
562 {
563  css::chart::ChartDataRowSource eRowSource = css::chart::ChartDataRowSource_ROWS;
564  if (bUseColumns)
565  eRowSource = css::chart::ChartDataRowSource_COLUMNS;
566 
567  uno::Sequence<beans::PropertyValue> aArguments{
568  { "DataRowSource", -1, uno::Any(eRowSource), beans::PropertyState_DIRECT_VALUE },
569  { "FirstCellAsLabel", -1, uno::Any(false), beans::PropertyState_DIRECT_VALUE },
570  { "HasCategories", -1, uno::Any(false), beans::PropertyState_DIRECT_VALUE },
571  { "CellRangeRepresentation", -1, uno::Any(rRangeRepresentation),
572  beans::PropertyState_DIRECT_VALUE }
573  };
574 
575  return aArguments;
576 }
577 
578 Reference<chart2::XDataSeries> getPrimaryDataSeries(const Reference<chart2::XChartType>& xChartType)
579 {
580  Reference< chart2::XDataSeriesContainer > xDSCnt(xChartType, uno::UNO_QUERY_THROW);
581 
582  // export dataseries for current chart-type
583  const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq(xDSCnt->getDataSeries());
584  for (const auto& rSeries : aSeriesSeq)
585  {
586  Reference<chart2::XDataSeries> xSource(rSeries, uno::UNO_QUERY);
587  if (xSource.is())
588  return xSource;
589  }
590 
592 }
593 
594 }
595 
597 {
598  Reference< chart2::XChartDocument > xChartDoc(getModel(), uno::UNO_QUERY);
599  OSL_ASSERT(xChartDoc.is());
600  if (xChartDoc.is())
601  {
602  Reference< chart2::data::XDataProvider > xDataProvider(xChartDoc->getDataProvider());
603  OSL_ENSURE(xDataProvider.is(), "No DataProvider");
604  if (xDataProvider.is())
605  {
606  //detect whether the first series is a row or a column
607  bool bSeriesUsesColumns = true;
608  Reference< chart2::XDiagram > xDiagram(xChartDoc->getFirstDiagram());
609  try
610  {
611  Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(xDiagram, uno::UNO_QUERY_THROW);
612  const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(xCooSysCnt->getCoordinateSystems());
613  for (const auto& rCooSys : aCooSysSeq)
614  {
615  const Reference< chart2::XChartTypeContainer > xCTCnt(rCooSys, uno::UNO_QUERY_THROW);
616  const Sequence< Reference< chart2::XChartType > > aChartTypeSeq(xCTCnt->getChartTypes());
617  for (const auto& rChartType : aChartTypeSeq)
618  {
619  Reference< chart2::XDataSeries > xDataSeries = getPrimaryDataSeries(rChartType);
620  if (xDataSeries.is())
621  {
622  uno::Reference< chart2::data::XDataSource > xSeriesSource(xDataSeries, uno::UNO_QUERY);
623  const uno::Sequence< beans::PropertyValue > rArguments = xDataProvider->detectArguments(xSeriesSource);
624  for (const beans::PropertyValue& rProperty : rArguments)
625  {
626  if (rProperty.Name == "DataRowSource")
627  {
628  css::chart::ChartDataRowSource eRowSource;
629  if (rProperty.Value >>= eRowSource)
630  {
631  bSeriesUsesColumns = (eRowSource == css::chart::ChartDataRowSource_COLUMNS);
632  break;
633  }
634  }
635  }
636  }
637  }
638  }
639  }
640  catch (const uno::Exception &)
641  {
642  DBG_UNHANDLED_EXCEPTION("chart2");
643  }
644  // detect we have an inner data table or not
645  if (xChartDoc->hasInternalDataProvider() && rRange == "categories")
646  {
647  try
648  {
649  css::uno::Reference< css::chart2::XAnyDescriptionAccess > xDataAccess(xChartDoc->getDataProvider(), uno::UNO_QUERY);
650  const Sequence< Sequence< uno::Any > >aAnyCategories(bSeriesUsesColumns ? xDataAccess->getAnyRowDescriptions() : xDataAccess->getAnyColumnDescriptions());
651  auto pMax = std::max_element(aAnyCategories.begin(), aAnyCategories.end(),
652  [](const Sequence<uno::Any>& a, const Sequence<uno::Any>& b) {
653  return a.getLength() < b.getLength(); });
654 
655  //minimum is 1!
656  if (pMax != aAnyCategories.end() && pMax->getLength() > 1)
657  {
658  sal_Int32 nLevelCount = pMax->getLength();
659  //we have complex categories
660  //sort the categories name
661  Sequence<Sequence<OUString>>aFinalSplitSource(nLevelCount);
662  auto pFinalSplitSource = aFinalSplitSource.getArray();
663  for (sal_Int32 i = 0; i < nLevelCount; i++)
664  {
665  sal_Int32 nElemLabel = 0;
666  pFinalSplitSource[nLevelCount - i - 1].realloc(aAnyCategories.getLength());
667  auto pSeq = pFinalSplitSource[nLevelCount - i - 1].getArray();
668  for (auto const& elemLabel : aAnyCategories)
669  {
670  // make sure elemLabel[i] exists!
671  if (elemLabel.getLength() > i)
672  {
673  pSeq[nElemLabel] = elemLabel[i].get<OUString>();
674  nElemLabel++;
675  }
676  }
677  }
678  return aFinalSplitSource;
679  }
680  }
681  catch (const uno::Exception &)
682  {
684  }
685  }
686  else
687  {
688  try
689  {
690  uno::Reference< chart2::data::XDataSource > xCategoriesSource(xDataProvider->createDataSource(
691  createArguments(rRange, bSeriesUsesColumns)));
692 
693  if (xCategoriesSource.is())
694  {
695  const Sequence< Reference< chart2::data::XLabeledDataSequence >> aCategories = xCategoriesSource->getDataSequences();
696  if (aCategories.getLength() > 1)
697  {
698  //we have complex categories
699  //sort the categories name
700  Sequence<Sequence<OUString>> aFinalSplitSource(aCategories.getLength());
701  std::transform(aCategories.begin(), aCategories.end(),
702  std::reverse_iterator(asNonConstRange(aFinalSplitSource).end()),
704  return lcl_getLabelSequence(xCat->getValues()); });
705  return aFinalSplitSource;
706  }
707  }
708  }
709  catch (const uno::Exception &)
710  {
712  }
713  }
714  }
715  }
716 
718 }
719 
720 OUString ChartExport::parseFormula( const OUString& rRange )
721 {
722  OUString aResult;
724  uno::Reference< lang::XMultiServiceFactory > xSF = GetFB()->getModelFactory();
725  if( xSF.is() )
726  {
727  try
728  {
729  xParser.set( xSF->createInstance("com.sun.star.sheet.FormulaParser"), UNO_QUERY );
730  }
731  catch( Exception& )
732  {
733  }
734  }
735 
736  SAL_WARN_IF(!xParser.is(), "oox", "creating formula parser failed");
737 
738  if( xParser.is() )
739  {
740  Reference< XPropertySet > xParserProps( xParser, uno::UNO_QUERY );
741  // rRange is the result of a
742  // css::chart2::data::XDataSequence::getSourceRangeRepresentation()
743  // call that returns the range in the document's current UI notation.
744  // Creating a FormulaParser defaults to the same notation, for
745  // parseFormula() do not attempt to override the FormulaConvention
746  // property with css::sheet::AddressConvention::OOO or some such.
747  /* TODO: it would be much better to introduce a
748  * getSourceRangeRepresentation(css::sheet::AddressConvention) to
749  * return the ranges in a specific convention than converting them with
750  * the overhead of creating an XFormulaParser for each... */
751  uno::Sequence<sheet::FormulaToken> aTokens = xParser->parseFormula( rRange, CellAddress( 0, 0, 0 ) );
752  if( xParserProps.is() )
753  {
754  xParserProps->setPropertyValue("FormulaConvention", uno::makeAny(css::sheet::AddressConvention::XL_OOX) );
755  // For referencing named ranges correctly with special excel chart syntax.
756  xParserProps->setPropertyValue("RefConventionChartOOXML", uno::makeAny(true) );
757  }
758  aResult = xParser->printFormula( aTokens, CellAddress( 0, 0, 0 ) );
759  }
760  else
761  {
762  //FIXME: currently just using simple converter, e.g $Sheet1.$A$1:$C$1 -> Sheet1!$A$1:$C$1
763  OUString aRange( rRange );
764  if( aRange.startsWith("$") )
765  aRange = aRange.copy(1);
766  aRange = aRange.replaceAll(".$", "!$" );
767  aResult = aRange;
768  }
769 
770  return aResult;
771 }
772 
773 void ChartExport::WriteChartObj( const Reference< XShape >& xShape, sal_Int32 nID, sal_Int32 nChartCount )
774 {
775  FSHelperPtr pFS = GetFS();
776 
777  Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
778 
779  pFS->startElementNS(mnXmlNamespace, XML_graphicFrame);
780 
781  pFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr);
782 
783  // TODO: get the correct chart name chart id
784  OUString sName = "Object 1";
785  Reference< XNamed > xNamed( xShape, UNO_QUERY );
786  if (xNamed.is())
787  sName = xNamed->getName();
788 
789  pFS->startElementNS( mnXmlNamespace, XML_cNvPr,
790  XML_id, OString::number(nID),
791  XML_name, sName);
792 
793  OUString sURL;
794  if ( GetProperty( xShapeProps, "URL" ) )
795  mAny >>= sURL;
796  if( !sURL.isEmpty() )
797  {
798  OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
800  mpURLTransformer->getTransformedString(sURL),
801  mpURLTransformer->isExternalURL(sURL));
802 
803  mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
804  }
805  pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
806 
807  pFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr);
808 
809  if( GetDocumentType() == DOCUMENT_PPTX )
810  pFS->singleElementNS(mnXmlNamespace, XML_nvPr);
811  pFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
812 
813  // visual chart properties
815 
816  // writer chart object
817  pFS->startElement(FSNS(XML_a, XML_graphic));
818  pFS->startElement( FSNS( XML_a, XML_graphicData ),
819  XML_uri, "http://schemas.openxmlformats.org/drawingml/2006/chart" );
820  OUString sId;
821  const char* sFullPath = nullptr;
822  const char* sRelativePath = nullptr;
823  switch( GetDocumentType() )
824  {
825  case DOCUMENT_DOCX:
826  {
827  sFullPath = "word/charts/chart";
828  sRelativePath = "charts/chart";
829  break;
830  }
831  case DOCUMENT_PPTX:
832  {
833  sFullPath = "ppt/charts/chart";
834  sRelativePath = "../charts/chart";
835  break;
836  }
837  case DOCUMENT_XLSX:
838  {
839  sFullPath = "xl/charts/chart";
840  sRelativePath = "../charts/chart";
841  break;
842  }
843  default:
844  {
845  sFullPath = "charts/chart";
846  sRelativePath = "charts/chart";
847  break;
848  }
849  }
850  OUString sFullStream = OUStringBuffer()
851  .appendAscii(sFullPath)
852  .append(OUString::number(nChartCount) + ".xml")
853  .makeStringAndClear();
854  OUString sRelativeStream = OUStringBuffer()
855  .appendAscii(sRelativePath)
856  .append(OUString::number(nChartCount) + ".xml" )
857  .makeStringAndClear();
859  sFullStream,
860  sRelativeStream,
861  pFS->getOutputStream(),
862  "application/vnd.openxmlformats-officedocument.drawingml.chart+xml",
863  OUStringToOString(oox::getRelationship(Relationship::CHART), RTL_TEXTENCODING_UTF8).getStr(),
864  &sId );
865 
866  XmlFilterBase* pFB = GetFB();
867  pFS->singleElement( FSNS( XML_c, XML_chart ),
868  FSNS(XML_xmlns, XML_c), pFB->getNamespaceURL(OOX_NS(dmlChart)),
869  FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)),
870  FSNS(XML_r, XML_id), sId );
871 
872  pFS->endElement( FSNS( XML_a, XML_graphicData ) );
873  pFS->endElement( FSNS( XML_a, XML_graphic ) );
874  pFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
875 
876  SetFS( pChart );
877  ExportContent();
878 }
879 
881 {
882  if( !xChartDoc.is())
883  return;
884 
885  try
886  {
887  Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
888  OSL_ENSURE( xDataProvider.is(), "No DataProvider" );
889  if( xDataProvider.is())
890  {
892  }
893  }
894  catch( const uno::Exception & )
895  {
897  }
898 }
899 
901 {
902  Reference< chart2::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
903  OSL_ASSERT( xChartDoc.is() );
904  if( !xChartDoc.is() )
905  return;
906  InitRangeSegmentationProperties( xChartDoc );
907  // TODO: export chart
908  ExportContent_( );
909 }
910 
912 {
913  Reference< css::chart::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
914  if( xChartDoc.is())
915  {
916  // determine if data comes from the outside
917  bool bIncludeTable = true;
918 
919  Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
920  if( xNewDoc.is())
921  {
922  // check if we have own data. If so we must not export the complete
923  // range string, as this is our only indicator for having own or
924  // external data. @todo: fix this in the file format!
925  Reference< lang::XServiceInfo > xDPServiceInfo( xNewDoc->getDataProvider(), uno::UNO_QUERY );
926  if( ! (xDPServiceInfo.is() && xDPServiceInfo->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" ))
927  {
928  bIncludeTable = false;
929  }
930  }
931  exportChartSpace( xChartDoc, bIncludeTable );
932  }
933  else
934  {
935  OSL_FAIL( "Couldn't export chart due to wrong XModel" );
936  }
937 }
938 
940  bool bIncludeTable )
941 {
942  FSHelperPtr pFS = GetFS();
943  XmlFilterBase* pFB = GetFB();
944  pFS->startElement( FSNS( XML_c, XML_chartSpace ),
945  FSNS( XML_xmlns, XML_c ), pFB->getNamespaceURL(OOX_NS(dmlChart)),
946  FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
947  FSNS( XML_xmlns, XML_r ), pFB->getNamespaceURL(OOX_NS(officeRel)));
948  // TODO: get the correct editing language
949  pFS->singleElement(FSNS(XML_c, XML_lang), XML_val, "en-US");
950 
951  pFS->singleElement(FSNS(XML_c, XML_roundedCorners), XML_val, "0");
952 
953  if( !bIncludeTable )
954  {
955  // TODO:external data
956  }
957  //XML_chart
958  exportChart(xChartDoc);
959 
960  // TODO: printSettings
961  // TODO: style
962  // TODO: text properties
963  // TODO: shape properties
964  Reference< XPropertySet > xPropSet = xChartDoc->getArea();
965  if( xPropSet.is() )
966  exportShapeProps( xPropSet );
967 
968  //XML_externalData
969  exportExternalData(xChartDoc);
970 
971  // export additional shapes in chart
972  exportAdditionalShapes(xChartDoc);
973 
974  pFS->endElement( FSNS( XML_c, XML_chartSpace ) );
975 }
976 
978 {
979  // Embedded external data is grab bagged for docx file hence adding export part of
980  // external data for docx files only.
982  return;
983 
984  OUString externalDataPath;
985  Reference< beans::XPropertySet > xDocPropSet( xChartDoc->getDiagram(), uno::UNO_QUERY );
986  if( xDocPropSet.is())
987  {
988  try
989  {
990  Any aAny( xDocPropSet->getPropertyValue( "ExternalData" ));
991  aAny >>= externalDataPath;
992  }
993  catch( beans::UnknownPropertyException & )
994  {
995  SAL_WARN("oox", "Required property not found in ChartDocument");
996  }
997  }
998  if(externalDataPath.isEmpty())
999  return;
1000 
1001  // Here adding external data entry to relationship.
1002  OUString relationPath = externalDataPath;
1003  // Converting absolute path to relative path.
1004  if( externalDataPath[ 0 ] != '.' && externalDataPath[ 1 ] != '.')
1005  {
1006  sal_Int32 nSepPos = externalDataPath.indexOf( '/', 0 );
1007  if( nSepPos > 0)
1008  {
1009  relationPath = relationPath.copy( nSepPos, ::std::max< sal_Int32 >( externalDataPath.getLength(), 0 ) - nSepPos );
1010  relationPath = ".." + relationPath;
1011  }
1012  }
1013  FSHelperPtr pFS = GetFS();
1015  if (relationPath.endsWith(".bin"))
1017 
1018  OUString sRelId = GetFB()->addRelation(pFS->getOutputStream(),
1019  type,
1020  relationPath);
1021  pFS->singleElementNS(XML_c, XML_externalData, FSNS(XML_r, XML_id), sRelId);
1022 }
1023 
1025 {
1026  Reference< beans::XPropertySet > xDocPropSet(xChartDoc, uno::UNO_QUERY);
1027  if (!xDocPropSet.is())
1028  return;
1029 
1030  css::uno::Reference< css::drawing::XShapes > mxAdditionalShapes;
1031  // get a sequence of non-chart shapes
1032  try
1033  {
1034  Any aShapesAny = xDocPropSet->getPropertyValue("AdditionalShapes");
1035  if( (aShapesAny >>= mxAdditionalShapes) && mxAdditionalShapes.is() )
1036  {
1037  OUString sId;
1038  const char* sFullPath = nullptr;
1039  const char* sRelativePath = nullptr;
1040  sal_Int32 nDrawing = getNewDrawingUniqueId();
1041 
1042  switch (GetDocumentType())
1043  {
1044  case DOCUMENT_DOCX:
1045  {
1046  sFullPath = "word/drawings/drawing";
1047  sRelativePath = "../drawings/drawing";
1048  break;
1049  }
1050  case DOCUMENT_PPTX:
1051  {
1052  sFullPath = "ppt/drawings/drawing";
1053  sRelativePath = "../drawings/drawing";
1054  break;
1055  }
1056  case DOCUMENT_XLSX:
1057  {
1058  sFullPath = "xl/drawings/drawing";
1059  sRelativePath = "../drawings/drawing";
1060  break;
1061  }
1062  default:
1063  {
1064  sFullPath = "drawings/drawing";
1065  sRelativePath = "drawings/drawing";
1066  break;
1067  }
1068  }
1069  OUString sFullStream = OUStringBuffer()
1070  .appendAscii(sFullPath)
1071  .append(OUString::number(nDrawing) + ".xml")
1072  .makeStringAndClear();
1073  OUString sRelativeStream = OUStringBuffer()
1074  .appendAscii(sRelativePath)
1075  .append(OUString::number(nDrawing) + ".xml")
1076  .makeStringAndClear();
1077 
1079  sFullStream,
1080  sRelativeStream,
1081  GetFS()->getOutputStream(),
1082  "application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml",
1083  OUStringToOString(oox::getRelationship(Relationship::CHARTUSERSHAPES), RTL_TEXTENCODING_UTF8).getStr(),
1084  &sId);
1085 
1086  GetFS()->singleElementNS(XML_c, XML_userShapes, FSNS(XML_r, XML_id), sId);
1087 
1088  XmlFilterBase* pFB = GetFB();
1089  pDrawing->startElement(FSNS(XML_c, XML_userShapes),
1090  FSNS(XML_xmlns, XML_cdr), pFB->getNamespaceURL(OOX_NS(dmlChartDr)),
1091  FSNS(XML_xmlns, XML_a), pFB->getNamespaceURL(OOX_NS(dml)),
1092  FSNS(XML_xmlns, XML_c), pFB->getNamespaceURL(OOX_NS(dmlChart)),
1093  FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)));
1094 
1095  const sal_Int32 nShapeCount(mxAdditionalShapes->getCount());
1096  for (sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++)
1097  {
1099  mxAdditionalShapes->getByIndex(nShapeId) >>= xShape;
1100  SAL_WARN_IF(!xShape.is(), "xmloff.chart", "Shape without an XShape?");
1101  if (!xShape.is())
1102  continue;
1103 
1104  // TODO: absSizeAnchor: we import both (absSizeAnchor and relSizeAnchor), but there is no essential difference between them.
1105  pDrawing->startElement(FSNS(XML_cdr, XML_relSizeAnchor));
1106  uno::Reference< beans::XPropertySet > xShapeProperties(xShape, uno::UNO_QUERY);
1107  if( xShapeProperties.is() )
1108  {
1109  Reference<embed::XVisualObject> xVisObject(mxChartModel, uno::UNO_QUERY);
1110  awt::Size aPageSize = xVisObject->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT);
1111  WriteFromTo( xShape, aPageSize, pDrawing );
1112 
1113  ShapeExport aExport(XML_cdr, pDrawing, nullptr, GetFB(), GetDocumentType(), nullptr, true);
1114  aExport.WriteShape(xShape);
1115  }
1116  pDrawing->endElement(FSNS(XML_cdr, XML_relSizeAnchor));
1117  }
1118  pDrawing->endElement(FSNS(XML_c, XML_userShapes));
1119  }
1120  }
1121  catch (const uno::Exception&)
1122  {
1123  TOOLS_INFO_EXCEPTION("xmloff.chart", "AdditionalShapes not found");
1124  }
1125 }
1126 
1128 {
1129  Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
1130  mxDiagram.set( xChartDoc->getDiagram() );
1131  if( xNewDoc.is())
1132  mxNewDiagram.set( xNewDoc->getFirstDiagram());
1133 
1134  // get Properties of ChartDocument
1135  bool bHasMainTitle = false;
1136  OUString aSubTitle;
1137  bool bHasLegend = false;
1138  Reference< beans::XPropertySet > xDocPropSet( xChartDoc, uno::UNO_QUERY );
1139  if( xDocPropSet.is())
1140  {
1141  try
1142  {
1143  Any aAny( xDocPropSet->getPropertyValue("HasMainTitle"));
1144  aAny >>= bHasMainTitle;
1145  aAny = xDocPropSet->getPropertyValue("HasLegend");
1146  aAny >>= bHasLegend;
1147  }
1148  catch( beans::UnknownPropertyException & )
1149  {
1150  SAL_WARN("oox", "Required property not found in ChartDocument");
1151  }
1152  } // if( xDocPropSet.is())
1153 
1154  Reference< beans::XPropertySet > xPropSubTitle( xChartDoc->getSubTitle(), UNO_QUERY );
1155  if( xPropSubTitle.is())
1156  {
1157  try
1158  {
1159  xPropSubTitle->getPropertyValue("String") >>= aSubTitle;
1160  }
1161  catch( beans::UnknownPropertyException & )
1162  {
1163  }
1164  }
1165 
1166  // chart element
1167  FSHelperPtr pFS = GetFS();
1168  pFS->startElement(FSNS(XML_c, XML_chart));
1169 
1170  // titles
1171  if( bHasMainTitle )
1172  {
1173  exportTitle( xChartDoc->getTitle(), !aSubTitle.isEmpty() ? &aSubTitle : nullptr );
1174  pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
1175  }
1176  else if( !aSubTitle.isEmpty() )
1177  {
1178  exportTitle( xChartDoc->getSubTitle(), nullptr );
1179  pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
1180  }
1181  else
1182  {
1183  pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "1");
1184  }
1185 
1186  InitPlotArea( );
1187  if( mbIs3DChart )
1188  {
1189  exportView3D();
1190 
1191  // floor
1192  Reference< beans::XPropertySet > xFloor = mxNewDiagram->getFloor();
1193  if( xFloor.is() )
1194  {
1195  pFS->startElement(FSNS(XML_c, XML_floor));
1196  exportShapeProps( xFloor );
1197  pFS->endElement( FSNS( XML_c, XML_floor ) );
1198  }
1199 
1200  // LibreOffice doesn't distinguish between sideWall and backWall (both are using the same color).
1201  // It is controlled by the same Wall property.
1203  if( xWall.is() )
1204  {
1205  // sideWall
1206  pFS->startElement(FSNS(XML_c, XML_sideWall));
1207  exportShapeProps( xWall );
1208  pFS->endElement( FSNS( XML_c, XML_sideWall ) );
1209 
1210  // backWall
1211  pFS->startElement(FSNS(XML_c, XML_backWall));
1212  exportShapeProps( xWall );
1213  pFS->endElement( FSNS( XML_c, XML_backWall ) );
1214  }
1215 
1216  }
1217  // plot area
1218  exportPlotArea( xChartDoc );
1219  // legend
1220  if( bHasLegend )
1221  exportLegend( xChartDoc );
1222 
1223  uno::Reference<beans::XPropertySet> xDiagramPropSet(xChartDoc->getDiagram(), uno::UNO_QUERY);
1224  uno::Any aPlotVisOnly = xDiagramPropSet->getPropertyValue("IncludeHiddenCells");
1225  bool bIncludeHiddenCells = false;
1226  aPlotVisOnly >>= bIncludeHiddenCells;
1227  pFS->singleElement(FSNS(XML_c, XML_plotVisOnly), XML_val, ToPsz10(!bIncludeHiddenCells));
1228 
1230 
1231  pFS->endElement( FSNS( XML_c, XML_chart ) );
1232 }
1233 
1234 void ChartExport::exportMissingValueTreatment(const uno::Reference<beans::XPropertySet>& xPropSet)
1235 {
1236  if (!xPropSet.is())
1237  return;
1238 
1239  sal_Int32 nVal = 0;
1240  uno::Any aAny = xPropSet->getPropertyValue("MissingValueTreatment");
1241  if (!(aAny >>= nVal))
1242  return;
1243 
1244  const char* pVal = nullptr;
1245  switch (nVal)
1246  {
1247  case cssc::MissingValueTreatment::LEAVE_GAP:
1248  pVal = "gap";
1249  break;
1250  case cssc::MissingValueTreatment::USE_ZERO:
1251  pVal = "zero";
1252  break;
1253  case cssc::MissingValueTreatment::CONTINUE:
1254  pVal = "span";
1255  break;
1256  default:
1257  SAL_WARN("oox", "unknown MissingValueTreatment value");
1258  break;
1259  }
1260 
1261  FSHelperPtr pFS = GetFS();
1262  pFS->singleElement(FSNS(XML_c, XML_dispBlanksAs), XML_val, pVal);
1263 }
1264 
1266 {
1267  FSHelperPtr pFS = GetFS();
1268  pFS->startElement(FSNS(XML_c, XML_legend));
1269 
1270  Reference< beans::XPropertySet > xProp( xChartDoc->getLegend(), uno::UNO_QUERY );
1271  if( xProp.is() )
1272  {
1273  // position
1274  css::chart::ChartLegendPosition aLegendPos = css::chart::ChartLegendPosition_NONE;
1275  try
1276  {
1277  Any aAny( xProp->getPropertyValue( "Alignment" ));
1278  aAny >>= aLegendPos;
1279  }
1280  catch( beans::UnknownPropertyException & )
1281  {
1282  SAL_WARN("oox", "Property Align not found in ChartLegend");
1283  }
1284 
1285  const char* strPos = nullptr;
1286  switch( aLegendPos )
1287  {
1288  case css::chart::ChartLegendPosition_LEFT:
1289  strPos = "l";
1290  break;
1291  case css::chart::ChartLegendPosition_RIGHT:
1292  strPos = "r";
1293  break;
1294  case css::chart::ChartLegendPosition_TOP:
1295  strPos = "t";
1296  break;
1297  case css::chart::ChartLegendPosition_BOTTOM:
1298  strPos = "b";
1299  break;
1300  case css::chart::ChartLegendPosition_NONE:
1301  case css::chart::ChartLegendPosition::ChartLegendPosition_MAKE_FIXED_SIZE:
1302  // nothing
1303  break;
1304  }
1305 
1306  if( strPos != nullptr )
1307  {
1308  pFS->singleElement(FSNS(XML_c, XML_legendPos), XML_val, strPos);
1309  }
1310 
1311  // legendEntry
1312  Reference<chart2::XCoordinateSystemContainer> xCooSysContainer(mxNewDiagram, UNO_QUERY_THROW);
1313  const Sequence<Reference<chart2::XCoordinateSystem>> xCooSysSequence(xCooSysContainer->getCoordinateSystems());
1314 
1315  sal_Int32 nIndex = 0;
1316  bool bShowLegendEntry;
1317  for (const auto& rCooSys : xCooSysSequence)
1318  {
1319  PropertySet aCooSysProp(rCooSys);
1320  bool bSwapXAndY = aCooSysProp.getBoolProperty(PROP_SwapXAndYAxis);
1321 
1322  Reference<chart2::XChartTypeContainer> xChartTypeContainer(rCooSys, UNO_QUERY_THROW);
1323  const Sequence<Reference<chart2::XChartType>> xChartTypeSequence(xChartTypeContainer->getChartTypes());
1324  if (!xChartTypeSequence.hasElements())
1325  continue;
1326 
1327  for (const auto& rCT : xChartTypeSequence)
1328  {
1329  Reference<chart2::XDataSeriesContainer> xDSCont(rCT, UNO_QUERY);
1330  if (!xDSCont.is())
1331  continue;
1332 
1333  OUString aChartType(rCT->getChartType());
1334  bool bIsPie = lcl_getChartType(aChartType) == chart::TYPEID_PIE;
1335  if (bIsPie)
1336  {
1337  PropertySet xChartTypeProp(rCT);
1338  bIsPie = !xChartTypeProp.getBoolProperty(PROP_UseRings);
1339  }
1340  const Sequence<Reference<chart2::XDataSeries>> aDataSeriesSeq = xDSCont->getDataSeries();
1341  if (bSwapXAndY)
1342  nIndex += aDataSeriesSeq.getLength() - 1;
1343  for (const auto& rDataSeries : aDataSeriesSeq)
1344  {
1345  PropertySet aSeriesProp(rDataSeries);
1346  bool bVaryColorsByPoint = aSeriesProp.getBoolProperty(PROP_VaryColorsByPoint);
1347  if (bVaryColorsByPoint || bIsPie)
1348  {
1349  Sequence<sal_Int32> deletedLegendEntriesSeq;
1350  aSeriesProp.getProperty(deletedLegendEntriesSeq, PROP_DeletedLegendEntries);
1351  for (const auto& deletedLegendEntry : std::as_const(deletedLegendEntriesSeq))
1352  {
1353  pFS->startElement(FSNS(XML_c, XML_legendEntry));
1354  pFS->singleElement(FSNS(XML_c, XML_idx), XML_val,
1355  OString::number(nIndex + deletedLegendEntry));
1356  pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, "1");
1357  pFS->endElement(FSNS(XML_c, XML_legendEntry));
1358  }
1359  Reference<chart2::data::XDataSource> xDSrc(rDataSeries, UNO_QUERY);
1360  if (!xDSrc.is())
1361  continue;
1362 
1363  const Sequence<Reference<chart2::data::XLabeledDataSequence>> aDataSeqs = xDSrc->getDataSequences();
1364  for (const auto& rDataSeq : aDataSeqs)
1365  {
1366  Reference<chart2::data::XDataSequence> xValues = rDataSeq->getValues();
1367  if (!xValues.is())
1368  continue;
1369 
1370  sal_Int32 nDataSeqSize = xValues->getData().getLength();
1371  nIndex += nDataSeqSize;
1372  }
1373  }
1374  else
1375  {
1376  bShowLegendEntry = aSeriesProp.getBoolProperty(PROP_ShowLegendEntry);
1377  if (!bShowLegendEntry)
1378  {
1379  pFS->startElement(FSNS(XML_c, XML_legendEntry));
1380  pFS->singleElement(FSNS(XML_c, XML_idx), XML_val,
1381  OString::number(nIndex));
1382  pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, "1");
1383  pFS->endElement(FSNS(XML_c, XML_legendEntry));
1384  }
1385  bSwapXAndY ? nIndex-- : nIndex++;
1386  }
1387  }
1388  if (bSwapXAndY)
1389  nIndex += aDataSeriesSeq.getLength() + 1;
1390  }
1391  }
1392 
1393  uno::Any aRelativePos = xProp->getPropertyValue("RelativePosition");
1394  if (aRelativePos.hasValue())
1395  {
1396  pFS->startElement(FSNS(XML_c, XML_layout));
1397  pFS->startElement(FSNS(XML_c, XML_manualLayout));
1398 
1399  pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
1400  pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
1401  chart2::RelativePosition aPos = aRelativePos.get<chart2::RelativePosition>();
1402 
1403  const double x = aPos.Primary;
1404  const double y = aPos.Secondary;
1405 
1406  pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
1407  pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
1408 
1409  uno::Any aRelativeSize = xProp->getPropertyValue("RelativeSize");
1410  if (aRelativeSize.hasValue())
1411  {
1412  chart2::RelativeSize aSize = aRelativeSize.get<chart2::RelativeSize>();
1413 
1414  const double w = aSize.Primary;
1415  const double h = aSize.Secondary;
1416 
1417  pFS->singleElement(FSNS(XML_c, XML_w), XML_val, OString::number(w));
1418 
1419  pFS->singleElement(FSNS(XML_c, XML_h), XML_val, OString::number(h));
1420  }
1421 
1422  SAL_WARN_IF(aPos.Anchor != css::drawing::Alignment_TOP_LEFT, "oox", "unsupported anchor position");
1423 
1424  pFS->endElement(FSNS(XML_c, XML_manualLayout));
1425  pFS->endElement(FSNS(XML_c, XML_layout));
1426  }
1427 
1428  if (strPos != nullptr)
1429  {
1430  uno::Any aOverlay = xProp->getPropertyValue("Overlay");
1431  if(aOverlay.get<bool>())
1432  pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, "1");
1433  else
1434  pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, "0");
1435  }
1436 
1437  // shape properties
1438  exportShapeProps( xProp );
1439 
1440  // draw-chart:txPr text properties
1441  exportTextProps( xProp );
1442  }
1443 
1444  pFS->endElement( FSNS( XML_c, XML_legend ) );
1445 }
1446 
1447 void ChartExport::exportTitle( const Reference< XShape >& xShape, const OUString* pSubText)
1448 {
1449  OUString sText;
1450  Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
1451  if( xPropSet.is())
1452  {
1453  xPropSet->getPropertyValue("String") >>= sText;
1454  }
1455 
1456  // tdf#101322: add subtitle to title
1457  if( pSubText )
1458  sText = sText.isEmpty() ? *pSubText : sText + "\n" + *pSubText;
1459 
1460  if( sText.isEmpty() )
1461  return;
1462 
1463  FSHelperPtr pFS = GetFS();
1464  pFS->startElement(FSNS(XML_c, XML_title));
1465 
1466  pFS->startElement(FSNS(XML_c, XML_tx));
1467  pFS->startElement(FSNS(XML_c, XML_rich));
1468 
1469  // TODO: bodyPr
1470  const char* sWritingMode = nullptr;
1471  bool bVertical = false;
1472  xPropSet->getPropertyValue("StackedText") >>= bVertical;
1473  if( bVertical )
1474  sWritingMode = "wordArtVert";
1475 
1476  sal_Int32 nRotation = 0;
1477  xPropSet->getPropertyValue("TextRotation") >>= nRotation;
1478 
1479  pFS->singleElement( FSNS( XML_a, XML_bodyPr ),
1480  XML_vert, sWritingMode,
1481  XML_rot, oox::drawingml::calcRotationValue(nRotation) );
1482  // TODO: lstStyle
1483  pFS->singleElement(FSNS(XML_a, XML_lstStyle));
1484  // FIXME: handle multiple paragraphs to parse aText
1485  pFS->startElement(FSNS(XML_a, XML_p));
1486 
1487  pFS->startElement(FSNS(XML_a, XML_pPr));
1488 
1489  bool bDummy = false;
1490  sal_Int32 nDummy;
1491  WriteRunProperties(xPropSet, false, XML_defRPr, true, bDummy, nDummy );
1492 
1493  pFS->endElement( FSNS( XML_a, XML_pPr ) );
1494 
1495  pFS->startElement(FSNS(XML_a, XML_r));
1496  bDummy = false;
1497  WriteRunProperties( xPropSet, false, XML_rPr, true, bDummy, nDummy );
1498  pFS->startElement(FSNS(XML_a, XML_t));
1499  pFS->writeEscaped( sText );
1500  pFS->endElement( FSNS( XML_a, XML_t ) );
1501  pFS->endElement( FSNS( XML_a, XML_r ) );
1502 
1503  pFS->endElement( FSNS( XML_a, XML_p ) );
1504 
1505  pFS->endElement( FSNS( XML_c, XML_rich ) );
1506  pFS->endElement( FSNS( XML_c, XML_tx ) );
1507 
1508  uno::Any aManualLayout = xPropSet->getPropertyValue("RelativePosition");
1509  if (aManualLayout.hasValue())
1510  {
1511  pFS->startElement(FSNS(XML_c, XML_layout));
1512  pFS->startElement(FSNS(XML_c, XML_manualLayout));
1513  pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
1514  pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
1515 
1516  Reference<embed::XVisualObject> xVisObject(mxChartModel, uno::UNO_QUERY);
1517  awt::Size aPageSize = xVisObject->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT);
1518 
1519  awt::Size aSize = xShape->getSize();
1520  awt::Point aPos2 = xShape->getPosition();
1521  // rotated shapes need special handling...
1522  double fSin = fabs(sin(basegfx::deg2rad<100>(nRotation)));
1523  // remove part of height from X direction, if title is rotated down
1524  if( nRotation*0.01 > 180.0 )
1525  aPos2.X -= static_cast<sal_Int32>(fSin * aSize.Height + 0.5);
1526  // remove part of width from Y direction, if title is rotated up
1527  else if( nRotation*0.01 > 0.0 )
1528  aPos2.Y -= static_cast<sal_Int32>(fSin * aSize.Width + 0.5);
1529 
1530  double x = static_cast<double>(aPos2.X) / static_cast<double>(aPageSize.Width);
1531  double y = static_cast<double>(aPos2.Y) / static_cast<double>(aPageSize.Height);
1532  /*
1533  pFS->singleElement(FSNS(XML_c, XML_wMode), XML_val, "edge");
1534  pFS->singleElement(FSNS(XML_c, XML_hMode), XML_val, "edge");
1535  */
1536  pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
1537  pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
1538  /*
1539  pFS->singleElement(FSNS(XML_c, XML_w), XML_val, "");
1540  pFS->singleElement(FSNS(XML_c, XML_h), XML_val, "");
1541  */
1542  pFS->endElement(FSNS(XML_c, XML_manualLayout));
1543  pFS->endElement(FSNS(XML_c, XML_layout));
1544  }
1545 
1546  pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, "0");
1547 
1548  // shape properties
1549  if( xPropSet.is() )
1550  {
1551  exportShapeProps( xPropSet );
1552  }
1553 
1554  pFS->endElement( FSNS( XML_c, XML_title ) );
1555 }
1556 
1557 namespace {
1558 
1559  std::vector<Sequence<Reference<chart2::XDataSeries> > > splitDataSeriesByAxis(const Reference< chart2::XChartType >& xChartType)
1560  {
1561  std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitSeries;
1562  std::map<sal_Int32, size_t> aMapAxisToIndex;
1563 
1564  Reference< chart2::XDataSeriesContainer > xDSCnt(xChartType, uno::UNO_QUERY);
1565  if (xDSCnt.is())
1566  {
1567  sal_Int32 nAxisIndexOfFirstSeries = -1;
1568  const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq(xDSCnt->getDataSeries());
1569  for (const uno::Reference<chart2::XDataSeries>& xSeries : aSeriesSeq)
1570  {
1571  Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY);
1572  if (!xPropSet.is())
1573  continue;
1574 
1575  sal_Int32 nAxisIndex = -1;
1576  uno::Any aAny = xPropSet->getPropertyValue("AttachedAxisIndex");
1577  aAny >>= nAxisIndex;
1578  size_t nVectorPos = 0;
1579  if (nAxisIndexOfFirstSeries == -1)
1580  {
1581  nAxisIndexOfFirstSeries = nAxisIndex;
1582  }
1583 
1584  auto it = aMapAxisToIndex.find(nAxisIndex);
1585  if (it == aMapAxisToIndex.end())
1586  {
1587  aSplitSeries.emplace_back();
1588  nVectorPos = aSplitSeries.size() - 1;
1589  aMapAxisToIndex.insert(std::pair<sal_Int32, size_t>(nAxisIndex, nVectorPos));
1590  }
1591  else
1592  {
1593  nVectorPos = it->second;
1594  }
1595 
1596  uno::Sequence<Reference<chart2::XDataSeries> >& rAxisSeriesSeq = aSplitSeries[nVectorPos];
1597  sal_Int32 nLength = rAxisSeriesSeq.getLength();
1598  rAxisSeriesSeq.realloc(nLength + 1);
1599  rAxisSeriesSeq.getArray()[nLength] = xSeries;
1600  }
1601  // if the first series attached to secondary axis, then export those series first, which are attached to primary axis
1602  // also the MS Office export every time in this order
1603  if (aSplitSeries.size() > 1 && nAxisIndexOfFirstSeries == 1)
1604  {
1605  std::swap(aSplitSeries[0], aSplitSeries[1]);
1606  }
1607  }
1608 
1609  return aSplitSeries;
1610  }
1611 
1612 }
1613 
1615 {
1616  Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( mxNewDiagram, uno::UNO_QUERY );
1617  if( ! xBCooSysCnt.is())
1618  return;
1619 
1620  // plot-area element
1621 
1622  FSHelperPtr pFS = GetFS();
1623  pFS->startElement(FSNS(XML_c, XML_plotArea));
1624 
1625  Reference<beans::XPropertySet> xWall(mxNewDiagram, uno::UNO_QUERY);
1626  if( xWall.is() )
1627  {
1628  uno::Any aAny = xWall->getPropertyValue("RelativePosition");
1629  if (aAny.hasValue())
1630  {
1631  chart2::RelativePosition aPos = aAny.get<chart2::RelativePosition>();
1632  aAny = xWall->getPropertyValue("RelativeSize");
1633  chart2::RelativeSize aSize = aAny.get<chart2::RelativeSize>();
1634  uno::Reference< css::chart::XDiagramPositioning > xDiagramPositioning( xChartDoc->getDiagram(), uno::UNO_QUERY );
1635  exportManualLayout(aPos, aSize, xDiagramPositioning->isExcludingDiagramPositioning() );
1636  }
1637  }
1638 
1639  // chart type
1641  aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
1642 
1643  // tdf#123647 Save empty chart as empty bar chart.
1644  if (!aCooSysSeq.hasElements())
1645  {
1646  pFS->startElement(FSNS(XML_c, XML_barChart));
1647  pFS->singleElement(FSNS(XML_c, XML_barDir), XML_val, "col");
1648  pFS->singleElement(FSNS(XML_c, XML_grouping), XML_val, "clustered");
1649  pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, "0");
1650  exportAxesId(true);
1651  pFS->endElement(FSNS(XML_c, XML_barChart));
1652  }
1653 
1654  for( const auto& rCS : aCooSysSeq )
1655  {
1656  Reference< chart2::XChartTypeContainer > xCTCnt( rCS, uno::UNO_QUERY );
1657  if( ! xCTCnt.is())
1658  continue;
1659  mnSeriesCount=0;
1660  const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
1661  for( const auto& rCT : aCTSeq )
1662  {
1663  Reference< chart2::XDataSeriesContainer > xDSCnt( rCT, uno::UNO_QUERY );
1664  if( ! xDSCnt.is())
1665  return;
1666  Reference< chart2::XChartType > xChartType( rCT, uno::UNO_QUERY );
1667  if( ! xChartType.is())
1668  continue;
1669  // note: if xDSCnt.is() then also aCTSeq[nCTIdx]
1670  OUString aChartType( xChartType->getChartType());
1671  sal_Int32 eChartType = lcl_getChartType( aChartType );
1672  switch( eChartType )
1673  {
1674  case chart::TYPEID_BAR:
1675  {
1676  exportBarChart( xChartType );
1677  break;
1678  }
1679  case chart::TYPEID_AREA:
1680  {
1681  exportAreaChart( xChartType );
1682  break;
1683  }
1684  case chart::TYPEID_LINE:
1685  {
1686  exportLineChart( xChartType );
1687  break;
1688  }
1689  case chart::TYPEID_BUBBLE:
1690  {
1691  exportBubbleChart( xChartType );
1692  break;
1693  }
1694  case chart::TYPEID_OFPIE:
1695  {
1696  break;
1697  }
1699  case chart::TYPEID_PIE:
1700  {
1701  exportPieChart( xChartType );
1702  break;
1703  }
1706  {
1707  exportRadarChart( xChartType );
1708  break;
1709  }
1710  case chart::TYPEID_SCATTER:
1711  {
1712  exportScatterChart( xChartType );
1713  break;
1714  }
1715  case chart::TYPEID_STOCK:
1716  {
1717  exportStockChart( xChartType );
1718  break;
1719  }
1720  case chart::TYPEID_SURFACE:
1721  {
1722  exportSurfaceChart( xChartType );
1723  break;
1724  }
1725  default:
1726  {
1727  SAL_WARN("oox", "ChartExport::exportPlotArea -- not support chart type");
1728  break;
1729  }
1730  }
1731 
1732  }
1733  }
1734  //Axis Data
1735  exportAxes( );
1736  // Data Table
1737  exportDataTable();
1738 
1739  // shape properties
1740  /*
1741  * Export the Plot area Shape Properties
1742  * eg: Fill and Outline
1743  */
1744  Reference< css::chart::X3DDisplay > xWallFloorSupplier( mxDiagram, uno::UNO_QUERY );
1745  // tdf#114139 For 2D charts Plot Area equivalent is Chart Wall.
1746  // Unfortunately LibreOffice doesn't have Plot Area equivalent for 3D charts.
1747  // It means that Plot Area couldn't be displayed and changed for 3D chars in LibreOffice.
1748  // We cannot write Wall attributes into Plot Area for 3D charts, because Wall us used as background wall.
1749  if( !mbIs3DChart && xWallFloorSupplier.is() )
1750  {
1751  Reference< beans::XPropertySet > xWallPropSet = xWallFloorSupplier->getWall();
1752  if( xWallPropSet.is() )
1753  {
1754  uno::Any aAny = xWallPropSet->getPropertyValue("LineStyle");
1755  sal_Int32 eChartType = getChartType( );
1756  // Export LineStyle_NONE instead of default linestyle of PlotArea border, because LibreOffice
1757  // make invisible the Wall shape properties, in case of these charts. Or in the future set
1758  // the default LineStyle of these charts to LineStyle_NONE.
1759  bool noSupportWallProp = ( (eChartType == chart::TYPEID_PIE) || (eChartType == chart::TYPEID_RADARLINE) || (eChartType == chart::TYPEID_RADARAREA) );
1760  if ( noSupportWallProp && (aAny != drawing::LineStyle_NONE) )
1761  {
1762  xWallPropSet->setPropertyValue( "LineStyle", uno::Any(drawing::LineStyle_NONE) );
1763  }
1764  exportShapeProps( xWallPropSet );
1765  }
1766  }
1767 
1768  pFS->endElement( FSNS( XML_c, XML_plotArea ) );
1769 
1770 }
1771 
1772 void ChartExport::exportManualLayout(const css::chart2::RelativePosition& rPos,
1773  const css::chart2::RelativeSize& rSize,
1774  const bool bIsExcludingDiagramPositioning)
1775 {
1776  FSHelperPtr pFS = GetFS();
1777  pFS->startElement(FSNS(XML_c, XML_layout));
1778  pFS->startElement(FSNS(XML_c, XML_manualLayout));
1779 
1780  // By default layoutTarget is set to "outer" and we shouldn't save it in that case
1781  if ( bIsExcludingDiagramPositioning )
1782  {
1783  pFS->singleElement(FSNS(XML_c, XML_layoutTarget), XML_val, "inner");
1784  }
1785  pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
1786  pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
1787 
1788  double x = rPos.Primary;
1789  double y = rPos.Secondary;
1790  const double w = rSize.Primary;
1791  const double h = rSize.Secondary;
1792  switch (rPos.Anchor)
1793  {
1794  case drawing::Alignment_LEFT:
1795  y -= (h/2);
1796  break;
1797  case drawing::Alignment_TOP_LEFT:
1798  break;
1799  case drawing::Alignment_BOTTOM_LEFT:
1800  y -= h;
1801  break;
1802  case drawing::Alignment_TOP:
1803  x -= (w/2);
1804  break;
1805  case drawing::Alignment_CENTER:
1806  x -= (w/2);
1807  y -= (h/2);
1808  break;
1809  case drawing::Alignment_BOTTOM:
1810  x -= (w/2);
1811  y -= h;
1812  break;
1813  case drawing::Alignment_TOP_RIGHT:
1814  x -= w;
1815  break;
1816  case drawing::Alignment_BOTTOM_RIGHT:
1817  x -= w;
1818  y -= h;
1819  break;
1820  case drawing::Alignment_RIGHT:
1821  y -= (h/2);
1822  x -= w;
1823  break;
1824  default:
1825  SAL_WARN("oox", "unhandled alignment case for manual layout export " << static_cast<sal_uInt16>(rPos.Anchor));
1826  }
1827 
1828  pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
1829 
1830  pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
1831 
1832  pFS->singleElement(FSNS(XML_c, XML_w), XML_val, OString::number(w));
1833 
1834  pFS->singleElement(FSNS(XML_c, XML_h), XML_val, OString::number(h));
1835 
1836  pFS->endElement(FSNS(XML_c, XML_manualLayout));
1837  pFS->endElement(FSNS(XML_c, XML_layout));
1838 }
1839 
1841 {
1842  // Similar to DrawingML::WriteFill, but gradient access via name
1843  if (!GetProperty( xPropSet, "FillStyle" ))
1844  return;
1845  FillStyle aFillStyle(FillStyle_NONE);
1846  mAny >>= aFillStyle;
1847 
1848  // map full transparent background to no fill
1849  if (aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, "FillTransparence" ))
1850  {
1851  sal_Int16 nVal = 0;
1852  mAny >>= nVal;
1853  if ( nVal == 100 )
1854  aFillStyle = FillStyle_NONE;
1855  }
1856  OUString sFillTransparenceGradientName;
1857  if (aFillStyle == FillStyle_SOLID
1858  && GetProperty(xPropSet, "FillTransparenceGradientName") && (mAny >>= sFillTransparenceGradientName)
1859  && !sFillTransparenceGradientName.isEmpty())
1860  {
1861  awt::Gradient aTransparenceGradient;
1862  uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
1863  uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY);
1864  uno::Any rTransparenceValue = xTransparenceGradient->getByName(sFillTransparenceGradientName);
1865  rTransparenceValue >>= aTransparenceGradient;
1866  if (aTransparenceGradient.StartColor == 0xffffff && aTransparenceGradient.EndColor == 0xffffff)
1867  aFillStyle = FillStyle_NONE;
1868  }
1869  switch( aFillStyle )
1870  {
1871  case FillStyle_SOLID:
1872  exportSolidFill(xPropSet);
1873  break;
1874  case FillStyle_GRADIENT :
1875  exportGradientFill( xPropSet );
1876  break;
1877  case FillStyle_BITMAP :
1878  exportBitmapFill( xPropSet );
1879  break;
1880  case FillStyle_HATCH:
1881  exportHatch(xPropSet);
1882  break;
1883  case FillStyle_NONE:
1884  mpFS->singleElementNS(XML_a, XML_noFill);
1885  break;
1886  default:
1887  ;
1888  }
1889 }
1890 
1892 {
1893  // Similar to DrawingML::WriteSolidFill, but gradient access via name
1894  // and currently no InteropGrabBag
1895  // get fill color
1896  if (!GetProperty( xPropSet, "FillColor" ))
1897  return;
1898  sal_uInt32 nFillColor = mAny.get<sal_uInt32>();
1899 
1900  sal_Int32 nAlpha = MAX_PERCENT;
1901  if (GetProperty( xPropSet, "FillTransparence" ))
1902  {
1903  sal_Int32 nTransparency = 0;
1904  mAny >>= nTransparency;
1905  // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
1906  nAlpha = (MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
1907  }
1908  // OOXML has no separate transparence gradient but uses transparency in the gradient stops.
1909  // So we merge transparency and color and use gradient fill in such case.
1910  awt::Gradient aTransparenceGradient;
1911  bool bNeedGradientFill(false);
1912  OUString sFillTransparenceGradientName;
1913  if (GetProperty(xPropSet, "FillTransparenceGradientName")
1914  && (mAny >>= sFillTransparenceGradientName)
1915  && !sFillTransparenceGradientName.isEmpty())
1916  {
1917  uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
1918  uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY);
1919  uno::Any rTransparenceValue = xTransparenceGradient->getByName(sFillTransparenceGradientName);
1920  rTransparenceValue >>= aTransparenceGradient;
1921  if (aTransparenceGradient.StartColor != aTransparenceGradient.EndColor)
1922  bNeedGradientFill = true;
1923  else if (aTransparenceGradient.StartColor != 0)
1924  nAlpha = lcl_getAlphaFromTransparenceGradient(aTransparenceGradient, true);
1925  }
1926  // write XML
1927  if (bNeedGradientFill)
1928  {
1929  awt::Gradient aPseudoColorGradient;
1930  aPseudoColorGradient.XOffset = aTransparenceGradient.XOffset;
1931  aPseudoColorGradient.YOffset = aTransparenceGradient.YOffset;
1932  aPseudoColorGradient.StartIntensity = 100;
1933  aPseudoColorGradient.EndIntensity = 100;
1934  aPseudoColorGradient.Angle = aTransparenceGradient.Angle;
1935  aPseudoColorGradient.Border = aTransparenceGradient.Border;
1936  aPseudoColorGradient.Style = aTransparenceGradient.Style;
1937  aPseudoColorGradient.StartColor = nFillColor;
1938  aPseudoColorGradient.EndColor = nFillColor;
1939  aPseudoColorGradient.StepCount = aTransparenceGradient.StepCount;
1940  mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
1941  WriteGradientFill(aPseudoColorGradient, aTransparenceGradient);
1942  mpFS->endElementNS(XML_a, XML_gradFill);
1943  }
1944  else
1945  WriteSolidFill(::Color(ColorTransparency, nFillColor & 0xffffff), nAlpha);
1946 }
1947 
1949 {
1950  if (!xPropSet.is())
1951  return;
1952 
1953  if (GetProperty(xPropSet, "FillHatchName"))
1954  {
1955  OUString aHatchName;
1956  mAny >>= aHatchName;
1957  uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
1958  uno::Reference< container::XNameAccess > xHatchTable( xFact->createInstance("com.sun.star.drawing.HatchTable"), uno::UNO_QUERY );
1959  uno::Any rValue = xHatchTable->getByName(aHatchName);
1960  css::drawing::Hatch aHatch;
1961  rValue >>= aHatch;
1962  WritePattFill(xPropSet, aHatch);
1963  }
1964 
1965 }
1966 
1968 {
1969  if( !xPropSet.is() )
1970  return;
1971 
1972  OUString sFillBitmapName;
1973  xPropSet->getPropertyValue("FillBitmapName") >>= sFillBitmapName;
1974 
1975  uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
1976  try
1977  {
1978  uno::Reference< container::XNameAccess > xBitmapTable( xFact->createInstance("com.sun.star.drawing.BitmapTable"), uno::UNO_QUERY );
1979  uno::Any rValue = xBitmapTable->getByName( sFillBitmapName );
1980  if (rValue.has<uno::Reference<awt::XBitmap>>())
1981  {
1982  uno::Reference<awt::XBitmap> xBitmap = rValue.get<uno::Reference<awt::XBitmap>>();
1983  uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY);
1984  if (xGraphic.is())
1985  {
1986  WriteXGraphicBlipFill(xPropSet, xGraphic, XML_a, true, true);
1987  }
1988  }
1989  }
1990  catch (const uno::Exception &)
1991  {
1992  TOOLS_WARN_EXCEPTION("oox", "ChartExport::exportBitmapFill");
1993  }
1994 }
1995 
1997 {
1998  if( !xPropSet.is() )
1999  return;
2000 
2001  OUString sFillGradientName;
2002  xPropSet->getPropertyValue("FillGradientName") >>= sFillGradientName;
2003 
2004  uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
2005  try
2006  {
2007  uno::Reference< container::XNameAccess > xGradient( xFact->createInstance("com.sun.star.drawing.GradientTable"), uno::UNO_QUERY );
2008  uno::Any rGradientValue = xGradient->getByName( sFillGradientName );
2009  awt::Gradient aGradient;
2010  if( rGradientValue >>= aGradient )
2011  {
2012  awt::Gradient aTransparenceGradient;
2013  mpFS->startElementNS(XML_a, XML_gradFill);
2014  OUString sFillTransparenceGradientName;
2015  if( (xPropSet->getPropertyValue("FillTransparenceGradientName") >>= sFillTransparenceGradientName) && !sFillTransparenceGradientName.isEmpty())
2016  {
2017  uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance("com.sun.star.drawing.TransparencyGradientTable"), uno::UNO_QUERY);
2018  uno::Any rTransparenceValue = xTransparenceGradient->getByName(sFillTransparenceGradientName);
2019  rTransparenceValue >>= aTransparenceGradient;
2020  WriteGradientFill(aGradient, aTransparenceGradient);
2021  }
2022  else
2023  {
2024  WriteGradientFill(aGradient, aTransparenceGradient, xPropSet);
2025  }
2026  mpFS->endElementNS(XML_a, XML_gradFill);
2027  }
2028  }
2029  catch (const uno::Exception &)
2030  {
2031  TOOLS_INFO_EXCEPTION("oox", "ChartExport::exportGradientFill");
2032  }
2033 }
2034 
2036 {
2037  FSHelperPtr pFS = GetFS();
2038  Reference< beans::XPropertySet > aPropSet( mxDiagram, uno::UNO_QUERY );
2039 
2040  bool bShowVBorder = false;
2041  bool bShowHBorder = false;
2042  bool bShowOutline = false;
2043 
2044  if (GetProperty( aPropSet, "DataTableHBorder"))
2045  mAny >>= bShowHBorder;
2046  if (GetProperty( aPropSet, "DataTableVBorder"))
2047  mAny >>= bShowVBorder;
2048  if (GetProperty( aPropSet, "DataTableOutline"))
2049  mAny >>= bShowOutline;
2050 
2051  if (!(bShowVBorder || bShowHBorder || bShowOutline))
2052  return;
2053 
2054  pFS->startElement(FSNS(XML_c, XML_dTable));
2055  if (bShowHBorder)
2056  pFS->singleElement( FSNS( XML_c, XML_showHorzBorder ),
2057  XML_val, "1" );
2058  if (bShowVBorder)
2059  pFS->singleElement(FSNS(XML_c, XML_showVertBorder), XML_val, "1");
2060  if (bShowOutline)
2061  pFS->singleElement(FSNS(XML_c, XML_showOutline), XML_val, "1");
2062 
2063  pFS->endElement( FSNS( XML_c, XML_dTable));
2064 
2065 }
2067 {
2068  FSHelperPtr pFS = GetFS();
2069  const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2070  for (const auto& splitDataSeries : aSplitDataSeries)
2071  {
2072  if (!splitDataSeries.hasElements())
2073  continue;
2074 
2075  sal_Int32 nTypeId = XML_areaChart;
2076  if (mbIs3DChart)
2077  nTypeId = XML_area3DChart;
2078  pFS->startElement(FSNS(XML_c, nTypeId));
2079 
2080  exportGrouping();
2081  bool bPrimaryAxes = true;
2082  exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
2083  exportAxesId(bPrimaryAxes);
2084 
2085  pFS->endElement(FSNS(XML_c, nTypeId));
2086  }
2087 }
2088 
2090 {
2091  sal_Int32 nTypeId = XML_barChart;
2092  if (mbIs3DChart)
2093  nTypeId = XML_bar3DChart;
2094  FSHelperPtr pFS = GetFS();
2095 
2096  const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2097  for (const auto& splitDataSeries : aSplitDataSeries)
2098  {
2099  if (!splitDataSeries.hasElements())
2100  continue;
2101 
2102  pFS->startElement(FSNS(XML_c, nTypeId));
2103  // bar direction
2104  bool bVertical = false;
2105  Reference< XPropertySet > xPropSet(mxDiagram, uno::UNO_QUERY);
2106  if (GetProperty(xPropSet, "Vertical"))
2107  mAny >>= bVertical;
2108 
2109  const char* bardir = bVertical ? "bar" : "col";
2110  pFS->singleElement(FSNS(XML_c, XML_barDir), XML_val, bardir);
2111 
2112  exportGrouping(true);
2113 
2114  exportVaryColors(xChartType);
2115 
2116  bool bPrimaryAxes = true;
2117  exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
2118 
2119  Reference< XPropertySet > xTypeProp(xChartType, uno::UNO_QUERY);
2120 
2121  if (xTypeProp.is() && GetProperty(xTypeProp, "GapwidthSequence"))
2122  {
2123  uno::Sequence< sal_Int32 > aBarPositionSequence;
2124  mAny >>= aBarPositionSequence;
2125  if (aBarPositionSequence.hasElements())
2126  {
2127  sal_Int32 nGapWidth = aBarPositionSequence[0];
2128  pFS->singleElement(FSNS(XML_c, XML_gapWidth), XML_val, OString::number(nGapWidth));
2129  }
2130  }
2131 
2132  if (mbIs3DChart)
2133  {
2134  // Shape
2135  namespace cssc = css::chart;
2136  sal_Int32 nGeom3d = cssc::ChartSolidType::RECTANGULAR_SOLID;
2137  if (xPropSet.is() && GetProperty(xPropSet, "SolidType"))
2138  mAny >>= nGeom3d;
2139  const char* sShapeType = nullptr;
2140  switch (nGeom3d)
2141  {
2142  case cssc::ChartSolidType::RECTANGULAR_SOLID:
2143  sShapeType = "box";
2144  break;
2145  case cssc::ChartSolidType::CONE:
2146  sShapeType = "cone";
2147  break;
2148  case cssc::ChartSolidType::CYLINDER:
2149  sShapeType = "cylinder";
2150  break;
2151  case cssc::ChartSolidType::PYRAMID:
2152  sShapeType = "pyramid";
2153  break;
2154  }
2155  pFS->singleElement(FSNS(XML_c, XML_shape), XML_val, sShapeType);
2156  }
2157 
2158  //overlap
2159  if (!mbIs3DChart && xTypeProp.is() && GetProperty(xTypeProp, "OverlapSequence"))
2160  {
2161  uno::Sequence< sal_Int32 > aBarPositionSequence;
2162  mAny >>= aBarPositionSequence;
2163  if (aBarPositionSequence.hasElements())
2164  {
2165  sal_Int32 nOverlap = aBarPositionSequence[0];
2166  // Stacked/Percent Bar/Column chart Overlap-workaround
2167  // Export the Overlap value with 100% for stacked charts,
2168  // because the default overlap value of the Bar/Column chart is 0% and
2169  // LibreOffice do nothing with the overlap value in Stacked charts case,
2170  // unlike the MS Office, which is interpreted differently.
2171  if ((mbStacked || mbPercent) && nOverlap != 100)
2172  {
2173  nOverlap = 100;
2174  pFS->singleElement(FSNS(XML_c, XML_overlap), XML_val, OString::number(nOverlap));
2175  }
2176  else // Normal bar chart
2177  {
2178  pFS->singleElement(FSNS(XML_c, XML_overlap), XML_val, OString::number(nOverlap));
2179  }
2180  }
2181  }
2182 
2183  exportAxesId(bPrimaryAxes);
2184 
2185  pFS->endElement(FSNS(XML_c, nTypeId));
2186  }
2187 }
2188 
2190 {
2191  FSHelperPtr pFS = GetFS();
2192  const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2193  for (const auto& splitDataSeries : aSplitDataSeries)
2194  {
2195  if (!splitDataSeries.hasElements())
2196  continue;
2197 
2198  pFS->startElement(FSNS(XML_c, XML_bubbleChart));
2199 
2200  exportVaryColors(xChartType);
2201 
2202  bool bPrimaryAxes = true;
2203  exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
2204 
2205  exportAxesId(bPrimaryAxes);
2206 
2207  pFS->endElement(FSNS(XML_c, XML_bubbleChart));
2208  }
2209 }
2210 
2212 {
2213  FSHelperPtr pFS = GetFS();
2214  pFS->startElement(FSNS(XML_c, XML_doughnutChart));
2215 
2216  exportVaryColors(xChartType);
2217 
2218  bool bPrimaryAxes = true;
2219  exportAllSeries(xChartType, bPrimaryAxes);
2220  // firstSliceAng
2222  //FIXME: holeSize
2223  pFS->singleElement(FSNS(XML_c, XML_holeSize), XML_val, OString::number(50));
2224 
2225  pFS->endElement( FSNS( XML_c, XML_doughnutChart ) );
2226 }
2227 
2228 namespace {
2229 
2230 void writeDataLabelsRange(const FSHelperPtr& pFS, const XmlFilterBase* pFB, DataLabelsRange& rDLblsRange)
2231 {
2232  if (rDLblsRange.empty())
2233  return;
2234 
2235  pFS->startElement(FSNS(XML_c, XML_extLst));
2236  pFS->startElement(FSNS(XML_c, XML_ext), XML_uri, "{02D57815-91ED-43cb-92C2-25804820EDAC}", FSNS(XML_xmlns, XML_c15), pFB->getNamespaceURL(OOX_NS(c15)));
2237  pFS->startElement(FSNS(XML_c15, XML_datalabelsRange));
2238 
2239  // Write cell range.
2240  pFS->startElement(FSNS(XML_c15, XML_f));
2241  pFS->writeEscaped(rDLblsRange.getRange());
2242  pFS->endElement(FSNS(XML_c15, XML_f));
2243 
2244  // Write all labels.
2245  pFS->startElement(FSNS(XML_c15, XML_dlblRangeCache));
2246  pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(rDLblsRange.count()));
2247  for (const auto& rLabelKV: rDLblsRange)
2248  {
2249  pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(rLabelKV.first));
2250  pFS->startElement(FSNS(XML_c, XML_v));
2251  pFS->writeEscaped(rLabelKV.second);
2252  pFS->endElement(FSNS( XML_c, XML_v ));
2253  pFS->endElement(FSNS(XML_c, XML_pt));
2254  }
2255 
2256  pFS->endElement(FSNS(XML_c15, XML_dlblRangeCache));
2257 
2258  pFS->endElement(FSNS(XML_c15, XML_datalabelsRange));
2259  pFS->endElement(FSNS(XML_c, XML_ext));
2260  pFS->endElement(FSNS(XML_c, XML_extLst));
2261 }
2262 
2263 }
2264 
2266 {
2267  FSHelperPtr pFS = GetFS();
2268  const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2269  for (const auto& splitDataSeries : aSplitDataSeries)
2270  {
2271  if (!splitDataSeries.hasElements())
2272  continue;
2273 
2274  sal_Int32 nTypeId = XML_lineChart;
2275  if( mbIs3DChart )
2276  nTypeId = XML_line3DChart;
2277  pFS->startElement(FSNS(XML_c, nTypeId));
2278 
2279  exportGrouping( );
2280 
2281  exportVaryColors(xChartType);
2282  // TODO: show marker symbol in series?
2283  bool bPrimaryAxes = true;
2284  exportSeries(xChartType, splitDataSeries, bPrimaryAxes);
2285 
2286  // show marker?
2287  sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE;
2288  Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
2289  if( GetProperty( xPropSet, "SymbolType" ) )
2290  mAny >>= nSymbolType;
2291 
2292  if( !mbIs3DChart )
2293  {
2294  exportHiLowLines();
2295  exportUpDownBars(xChartType);
2296  const char* marker = nSymbolType == css::chart::ChartSymbolType::NONE? "0":"1";
2297  pFS->singleElement(FSNS(XML_c, XML_marker), XML_val, marker);
2298  }
2299 
2300  exportAxesId(bPrimaryAxes, true);
2301 
2302  pFS->endElement( FSNS( XML_c, nTypeId ) );
2303  }
2304 }
2305 
2307 {
2308  sal_Int32 eChartType = getChartType( );
2309  if(eChartType == chart::TYPEID_DOUGHNUT)
2310  {
2311  exportDoughnutChart( xChartType );
2312  return;
2313  }
2314  FSHelperPtr pFS = GetFS();
2315  sal_Int32 nTypeId = XML_pieChart;
2316  if( mbIs3DChart )
2317  nTypeId = XML_pie3DChart;
2318  pFS->startElement(FSNS(XML_c, nTypeId));
2319 
2320  exportVaryColors(xChartType);
2321 
2322  bool bPrimaryAxes = true;
2323  exportAllSeries(xChartType, bPrimaryAxes);
2324 
2325  if( !mbIs3DChart )
2326  {
2327  // firstSliceAng
2329  }
2330 
2331  pFS->endElement( FSNS( XML_c, nTypeId ) );
2332 }
2333 
2335 {
2336  FSHelperPtr pFS = GetFS();
2337  pFS->startElement(FSNS(XML_c, XML_radarChart));
2338 
2339  // radarStyle
2340  sal_Int32 eChartType = getChartType( );
2341  const char* radarStyle = nullptr;
2342  if( eChartType == chart::TYPEID_RADARAREA )
2343  radarStyle = "filled";
2344  else
2345  radarStyle = "marker";
2346  pFS->singleElement(FSNS(XML_c, XML_radarStyle), XML_val, radarStyle);
2347 
2348  exportVaryColors(xChartType);
2349  bool bPrimaryAxes = true;
2350  exportAllSeries(xChartType, bPrimaryAxes);
2351  exportAxesId(bPrimaryAxes);
2352 
2353  pFS->endElement( FSNS( XML_c, XML_radarChart ) );
2354 }
2355 
2357  const css::uno::Sequence<css::uno::Reference<chart2::XDataSeries>>* pSeries)
2358 {
2359  FSHelperPtr pFS = GetFS();
2360  pFS->startElement(FSNS(XML_c, XML_scatterChart));
2361  // TODO:scatterStyle
2362 
2363  sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE;
2364  Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
2365  if( GetProperty( xPropSet, "SymbolType" ) )
2366  mAny >>= nSymbolType;
2367 
2368  const char* scatterStyle = "lineMarker";
2369  if (nSymbolType == css::chart::ChartSymbolType::NONE)
2370  {
2371  scatterStyle = "line";
2372  }
2373 
2374  pFS->singleElement(FSNS(XML_c, XML_scatterStyle), XML_val, scatterStyle);
2375 
2376  exportVaryColors(xChartType);
2377  // FIXME: should export xVal and yVal
2378  bool bPrimaryAxes = true;
2379  if (pSeries)
2380  exportSeries(xChartType, *pSeries, bPrimaryAxes);
2381  exportAxesId(bPrimaryAxes);
2382 
2383  pFS->endElement( FSNS( XML_c, XML_scatterChart ) );
2384 }
2385 
2387 {
2388  const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2389  bool bExported = false;
2390  for (const auto& splitDataSeries : aSplitDataSeries)
2391  {
2392  if (!splitDataSeries.hasElements())
2393  continue;
2394 
2395  bExported = true;
2396  exportScatterChartSeries(xChartType, &splitDataSeries);
2397  }
2398  if (!bExported)
2399  exportScatterChartSeries(xChartType, nullptr);
2400 }
2401 
2403 {
2404  FSHelperPtr pFS = GetFS();
2405  const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2406  for (const auto& splitDataSeries : aSplitDataSeries)
2407  {
2408  if (!splitDataSeries.hasElements())
2409  continue;
2410 
2411  pFS->startElement(FSNS(XML_c, XML_stockChart));
2412 
2413  bool bPrimaryAxes = true;
2414  exportCandleStickSeries(splitDataSeries, bPrimaryAxes);
2415 
2416  // export stock properties
2417  Reference< css::chart::XStatisticDisplay > xStockPropProvider(mxDiagram, uno::UNO_QUERY);
2418  if (xStockPropProvider.is())
2419  {
2420  exportHiLowLines();
2421  exportUpDownBars(xChartType);
2422  }
2423 
2424  exportAxesId(bPrimaryAxes);
2425 
2426  pFS->endElement(FSNS(XML_c, XML_stockChart));
2427  }
2428 }
2429 
2431 {
2432  FSHelperPtr pFS = GetFS();
2433  // export the chart property
2434  Reference< css::chart::XStatisticDisplay > xChartPropProvider( mxDiagram, uno::UNO_QUERY );
2435 
2436  if (!xChartPropProvider.is())
2437  return;
2438 
2439  Reference< beans::XPropertySet > xStockPropSet = xChartPropProvider->getMinMaxLine();
2440  if( !xStockPropSet.is() )
2441  return;
2442 
2443  pFS->startElement(FSNS(XML_c, XML_hiLowLines));
2444  exportShapeProps( xStockPropSet );
2445  pFS->endElement( FSNS( XML_c, XML_hiLowLines ) );
2446 }
2447 
2449 {
2450  if(xChartType->getChartType() != "com.sun.star.chart2.CandleStickChartType")
2451  return;
2452 
2453  FSHelperPtr pFS = GetFS();
2454  // export the chart property
2455  Reference< css::chart::XStatisticDisplay > xChartPropProvider( mxDiagram, uno::UNO_QUERY );
2456  if(!xChartPropProvider.is())
2457  return;
2458 
2459  // updownbar
2460  pFS->startElement(FSNS(XML_c, XML_upDownBars));
2461  // TODO: gapWidth
2462  pFS->singleElement(FSNS(XML_c, XML_gapWidth), XML_val, OString::number(150));
2463 
2464  Reference< beans::XPropertySet > xChartPropSet = xChartPropProvider->getUpBar();
2465  if( xChartPropSet.is() )
2466  {
2467  pFS->startElement(FSNS(XML_c, XML_upBars));
2468  // For Linechart with UpDownBars, spPr is not getting imported
2469  // so no need to call the exportShapeProps() for LineChart
2470  if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType")
2471  {
2472  exportShapeProps(xChartPropSet);
2473  }
2474  pFS->endElement( FSNS( XML_c, XML_upBars ) );
2475  }
2476  xChartPropSet = xChartPropProvider->getDownBar();
2477  if( xChartPropSet.is() )
2478  {
2479  pFS->startElement(FSNS(XML_c, XML_downBars));
2480  if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType")
2481  {
2482  exportShapeProps(xChartPropSet);
2483  }
2484  pFS->endElement( FSNS( XML_c, XML_downBars ) );
2485  }
2486  pFS->endElement( FSNS( XML_c, XML_upDownBars ) );
2487 }
2488 
2490 {
2491  FSHelperPtr pFS = GetFS();
2492  sal_Int32 nTypeId = XML_surfaceChart;
2493  if( mbIs3DChart )
2494  nTypeId = XML_surface3DChart;
2495  pFS->startElement(FSNS(XML_c, nTypeId));
2496  exportVaryColors(xChartType);
2497  bool bPrimaryAxes = true;
2498  exportAllSeries(xChartType, bPrimaryAxes);
2499  exportAxesId(bPrimaryAxes);
2500 
2501  pFS->endElement( FSNS( XML_c, nTypeId ) );
2502 }
2503 
2504 void ChartExport::exportAllSeries(const Reference<chart2::XChartType>& xChartType, bool& rPrimaryAxes)
2505 {
2506  Reference< chart2::XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY );
2507  if( ! xDSCnt.is())
2508  return;
2509 
2510  // export dataseries for current chart-type
2511  Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries());
2512  exportSeries(xChartType, aSeriesSeq, rPrimaryAxes);
2513 }
2514 
2516 {
2517  FSHelperPtr pFS = GetFS();
2518  try
2519  {
2520  Reference<chart2::XDataSeries> xDataSeries = getPrimaryDataSeries(xChartType);
2521  Reference<beans::XPropertySet> xDataSeriesProps(xDataSeries, uno::UNO_QUERY_THROW);
2522  Any aAnyVaryColors = xDataSeriesProps->getPropertyValue("VaryColorsByPoint");
2523  bool bVaryColors = false;
2524  aAnyVaryColors >>= bVaryColors;
2525  pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, ToPsz10(bVaryColors));
2526  }
2527  catch (...)
2528  {
2529  pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, "0");
2530  }
2531 }
2532 
2534  const Sequence<Reference<chart2::XDataSeries> >& rSeriesSeq, bool& rPrimaryAxes )
2535 {
2536  OUString aLabelRole = xChartType->getRoleOfSequenceForSeriesLabel();
2537  OUString aChartType( xChartType->getChartType());
2538  sal_Int32 eChartType = lcl_getChartType( aChartType );
2539 
2540  for( const auto& rSeries : rSeriesSeq )
2541  {
2542  // export series
2543  Reference< chart2::data::XDataSource > xSource( rSeries, uno::UNO_QUERY );
2544  if( xSource.is())
2545  {
2546  Reference< chart2::XDataSeries > xDataSeries( xSource, uno::UNO_QUERY );
2548  xSource->getDataSequences());
2549  // search for main sequence and create a series element
2550  {
2551  sal_Int32 nMainSequenceIndex = -1;
2552  sal_Int32 nSeriesLength = 0;
2555  sal_Int32 nSeqIdx=0;
2556  for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx )
2557  {
2558  Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() );
2559  if( nMainSequenceIndex==-1 )
2560  {
2561  Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY );
2562  OUString aRole;
2563  if( xSeqProp.is())
2564  xSeqProp->getPropertyValue("Role") >>= aRole;
2565  // "main" sequence
2566  if( aRole == aLabelRole )
2567  {
2568  xValuesSeq.set( xTempValueSeq );
2569  xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel());
2570  nMainSequenceIndex = nSeqIdx;
2571  }
2572  }
2573  sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0));
2574  if( nSeriesLength < nSequenceLength )
2575  nSeriesLength = nSequenceLength;
2576  }
2577 
2578  // have found the main sequence, then xValuesSeq and
2579  // xLabelSeq contain those. Otherwise both are empty
2580  {
2581  FSHelperPtr pFS = GetFS();
2582 
2583  pFS->startElement(FSNS(XML_c, XML_ser));
2584 
2585  // TODO: idx and order
2586  pFS->singleElement( FSNS( XML_c, XML_idx ),
2587  XML_val, OString::number(mnSeriesCount) );
2588  pFS->singleElement( FSNS( XML_c, XML_order ),
2589  XML_val, OString::number(mnSeriesCount++) );
2590 
2591  // export label
2592  if( xLabelSeq.is() )
2593  exportSeriesText( xLabelSeq );
2594 
2595  Reference<XPropertySet> xPropSet(xDataSeries, UNO_QUERY_THROW);
2596  if( GetProperty( xPropSet, "AttachedAxisIndex") )
2597  {
2598  sal_Int32 nLocalAttachedAxis = 0;
2599  mAny >>= nLocalAttachedAxis;
2600  rPrimaryAxes = isPrimaryAxes(nLocalAttachedAxis);
2601  }
2602 
2603  // export shape properties
2605  rSeries, getModel() );
2606  if( xOldPropSet.is() )
2607  {
2608  exportShapeProps( xOldPropSet );
2609  }
2610 
2611  switch( eChartType )
2612  {
2613  case chart::TYPEID_BUBBLE:
2614  case chart::TYPEID_HORBAR:
2615  case chart::TYPEID_BAR:
2616  {
2617  pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0");
2618  }
2619  break;
2620  case chart::TYPEID_LINE:
2621  {
2622  exportMarker(xOldPropSet);
2623  break;
2624  }
2625  case chart::TYPEID_PIE:
2627  {
2628  if( xOldPropSet.is() && GetProperty( xOldPropSet, "SegmentOffset") )
2629  {
2630  sal_Int32 nOffset = 0;
2631  mAny >>= nOffset;
2632  pFS->singleElement( FSNS( XML_c, XML_explosion ),
2633  XML_val, OString::number( nOffset ) );
2634  }
2635  break;
2636  }
2637  case chart::TYPEID_SCATTER:
2638  {
2639  exportMarker(xOldPropSet);
2640  break;
2641  }
2643  {
2644  exportMarker(xOldPropSet);
2645  break;
2646  }
2647  }
2648 
2649  // export data points
2650  exportDataPoints( uno::Reference< beans::XPropertySet >( rSeries, uno::UNO_QUERY ), nSeriesLength, eChartType );
2651 
2652  DataLabelsRange aDLblsRange;
2653  // export data labels
2654  exportDataLabels(rSeries, nSeriesLength, eChartType, aDLblsRange);
2655 
2656  exportTrendlines( rSeries );
2657 
2658  if( eChartType != chart::TYPEID_PIE &&
2659  eChartType != chart::TYPEID_RADARLINE )
2660  {
2661  //export error bars here
2662  Reference< XPropertySet > xSeriesPropSet( xSource, uno::UNO_QUERY );
2663  Reference< XPropertySet > xErrorBarYProps;
2664  xSeriesPropSet->getPropertyValue("ErrorBarY") >>= xErrorBarYProps;
2665  if(xErrorBarYProps.is())
2666  exportErrorBar(xErrorBarYProps, true);
2667  if (eChartType != chart::TYPEID_BAR &&
2668  eChartType != chart::TYPEID_HORBAR)
2669  {
2670  Reference< XPropertySet > xErrorBarXProps;
2671  xSeriesPropSet->getPropertyValue("ErrorBarX") >>= xErrorBarXProps;
2672  if(xErrorBarXProps.is())
2673  exportErrorBar(xErrorBarXProps, false);
2674  }
2675  }
2676 
2677  // export categories
2678  if( eChartType != chart::TYPEID_SCATTER && eChartType != chart::TYPEID_BUBBLE && mxCategoriesValues.is() )
2680 
2681  if( (eChartType == chart::TYPEID_SCATTER)
2682  || (eChartType == chart::TYPEID_BUBBLE) )
2683  {
2684  // export xVal
2686  if( xSequence.is() )
2687  {
2688  Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
2689  if( xValues.is() )
2690  exportSeriesValues( xValues, XML_xVal );
2691  }
2692  else if( mxCategoriesValues.is() )
2694  }
2695 
2696  if( eChartType == chart::TYPEID_BUBBLE )
2697  {
2698  // export yVal
2700  if( xSequence.is() )
2701  {
2702  Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
2703  if( xValues.is() )
2704  exportSeriesValues( xValues, XML_yVal );
2705  }
2706  }
2707 
2708  // export values
2709  if( xValuesSeq.is() )
2710  {
2711  sal_Int32 nYValueType = XML_val;
2712  if( eChartType == chart::TYPEID_SCATTER )
2713  nYValueType = XML_yVal;
2714  else if( eChartType == chart::TYPEID_BUBBLE )
2715  nYValueType = XML_bubbleSize;
2716  exportSeriesValues( xValuesSeq, nYValueType );
2717  }
2718 
2719  if( eChartType == chart::TYPEID_SCATTER
2720  || eChartType == chart::TYPEID_LINE )
2721  exportSmooth();
2722 
2723  // tdf103988: "corrupted" files with Bubble chart opening in MSO
2724  if( eChartType == chart::TYPEID_BUBBLE )
2725  pFS->singleElement(FSNS(XML_c, XML_bubble3D), XML_val, "0");
2726 
2727  if (!aDLblsRange.empty())
2728  writeDataLabelsRange(pFS, GetFB(), aDLblsRange);
2729 
2730  pFS->endElement( FSNS( XML_c, XML_ser ) );
2731  }
2732  }
2733  }
2734  }
2735 }
2736 
2738  const Sequence< Reference< chart2::XDataSeries > > & aSeriesSeq,
2739  bool& rPrimaryAxes)
2740 {
2741  for( const Reference< chart2::XDataSeries >& xSeries : aSeriesSeq )
2742  {
2743  rPrimaryAxes = lcl_isSeriesAttachedToFirstAxis(xSeries);
2744 
2745  Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
2746  if( xSource.is())
2747  {
2748  // export series in correct order (as we don't store roles)
2749  // with japanese candlesticks: open, low, high, close
2750  // otherwise: low, high, close
2752  xSource->getDataSequences());
2753 
2754  const char* sSeries[] = {"values-first","values-max","values-min","values-last",nullptr};
2755 
2756  for( sal_Int32 idx = 0; sSeries[idx] != nullptr ; idx++ )
2757  {
2758  Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( lcl_getDataSequenceByRole( aSeqCnt, OUString::createFromAscii(sSeries[idx]) ) );
2759  if( xLabeledSeq.is())
2760  {
2761  Reference< chart2::data::XDataSequence > xLabelSeq( xLabeledSeq->getLabel());
2762  Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues());
2763  {
2764  FSHelperPtr pFS = GetFS();
2765  pFS->startElement(FSNS(XML_c, XML_ser));
2766 
2767  // TODO: idx and order
2768  // idx attribute should start from 1 and not from 0.
2769  pFS->singleElement( FSNS( XML_c, XML_idx ),
2770  XML_val, OString::number(idx+1) );
2771  pFS->singleElement( FSNS( XML_c, XML_order ),
2772  XML_val, OString::number(idx+1) );
2773 
2774  // export label
2775  if( xLabelSeq.is() )
2776  exportSeriesText( xLabelSeq );
2777 
2778  // TODO:export shape properties
2779 
2780  // export categories
2781  if( mxCategoriesValues.is() )
2783 
2784  // export values
2785  if( xValueSeq.is() )
2786  exportSeriesValues( xValueSeq );
2787 
2788  pFS->endElement( FSNS( XML_c, XML_ser ) );
2789  }
2790  }
2791  }
2792  }
2793  }
2794 }
2795 
2797 {
2798  FSHelperPtr pFS = GetFS();
2799  pFS->startElement(FSNS(XML_c, XML_tx));
2800 
2801  OUString aCellRange = xValueSeq->getSourceRangeRepresentation();
2802  aCellRange = parseFormula( aCellRange );
2803  pFS->startElement(FSNS(XML_c, XML_strRef));
2804 
2805  pFS->startElement(FSNS(XML_c, XML_f));
2806  pFS->writeEscaped( aCellRange );
2807  pFS->endElement( FSNS( XML_c, XML_f ) );
2808 
2809  OUString aLabelString = lcl_flattenStringSequence(lcl_getLabelSequence(xValueSeq));
2810  pFS->startElement(FSNS(XML_c, XML_strCache));
2811  pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, "1");
2812  pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, "0");
2813  pFS->startElement(FSNS(XML_c, XML_v));
2814  pFS->writeEscaped( aLabelString );
2815  pFS->endElement( FSNS( XML_c, XML_v ) );
2816  pFS->endElement( FSNS( XML_c, XML_pt ) );
2817  pFS->endElement( FSNS( XML_c, XML_strCache ) );
2818  pFS->endElement( FSNS( XML_c, XML_strRef ) );
2819  pFS->endElement( FSNS( XML_c, XML_tx ) );
2820 }
2821 
2823 {
2824  FSHelperPtr pFS = GetFS();
2825  pFS->startElement(FSNS(XML_c, nValueType));
2826 
2827  OUString aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString();
2828  const Sequence< Sequence< OUString >> aFinalSplitSource = (nValueType == XML_cat) ? getSplitCategoriesList(aCellRange) : Sequence< Sequence< OUString>>(0);
2829  aCellRange = parseFormula( aCellRange );
2830 
2831  if(aFinalSplitSource.getLength() > 1)
2832  {
2833  // export multi level category axis labels
2834  pFS->startElement(FSNS(XML_c, XML_multiLvlStrRef));
2835 
2836  pFS->startElement(FSNS(XML_c, XML_f));
2837  pFS->writeEscaped(aCellRange);
2838  pFS->endElement(FSNS(XML_c, XML_f));
2839 
2840  pFS->startElement(FSNS(XML_c, XML_multiLvlStrCache));
2841  pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(aFinalSplitSource[0].getLength()));
2842  for(const auto& rSeq : aFinalSplitSource)
2843  {
2844  pFS->startElement(FSNS(XML_c, XML_lvl));
2845  for(sal_Int32 j = 0; j < rSeq.getLength(); j++)
2846  {
2847  if(!rSeq[j].isEmpty())
2848  {
2849  pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(j));
2850  pFS->startElement(FSNS(XML_c, XML_v));
2851  pFS->writeEscaped(rSeq[j]);
2852  pFS->endElement(FSNS(XML_c, XML_v));
2853  pFS->endElement(FSNS(XML_c, XML_pt));
2854  }
2855  }
2856  pFS->endElement(FSNS(XML_c, XML_lvl));
2857  }
2858 
2859  pFS->endElement(FSNS(XML_c, XML_multiLvlStrCache));
2860  pFS->endElement(FSNS(XML_c, XML_multiLvlStrRef));
2861  }
2862  else
2863  {
2864  // export single category axis labels
2865  bool bWriteDateCategories = mbHasDateCategories && (nValueType == XML_cat);
2866  OUString aNumberFormatString;
2867  if (bWriteDateCategories)
2868  {
2869  Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY );
2870  if( xAxisXSupp.is())
2871  {
2872  Reference< XPropertySet > xAxisProp = xAxisXSupp->getXAxis();
2873  if (GetProperty(xAxisProp, "NumberFormat"))
2874  {
2875  sal_Int32 nKey = 0;
2876  mAny >>= nKey;
2877  aNumberFormatString = getNumberFormatCode(nKey);
2878  }
2879  }
2880  if (aNumberFormatString.isEmpty())
2881  bWriteDateCategories = false;
2882  }
2883 
2884  pFS->startElement(FSNS(XML_c, bWriteDateCategories ? XML_numRef : XML_strRef));
2885 
2886  pFS->startElement(FSNS(XML_c, XML_f));
2887  pFS->writeEscaped(aCellRange);
2888  pFS->endElement(FSNS(XML_c, XML_f));
2889 
2890  ::std::vector< OUString > aCategories;
2891  lcl_fillCategoriesIntoStringVector(xValueSeq, aCategories);
2892  sal_Int32 ptCount = aCategories.size();
2893  pFS->startElement(FSNS(XML_c, bWriteDateCategories ? XML_numCache : XML_strCache));
2894  if (bWriteDateCategories)
2895  {
2896  pFS->startElement(FSNS(XML_c, XML_formatCode));
2897  pFS->writeEscaped(aNumberFormatString);
2898  pFS->endElement(FSNS(XML_c, XML_formatCode));
2899  }
2900 
2901  pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount));
2902  for (sal_Int32 i = 0; i < ptCount; i++)
2903  {
2904  pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i));
2905  pFS->startElement(FSNS(XML_c, XML_v));
2906  pFS->writeEscaped(aCategories[i]);
2907  pFS->endElement(FSNS(XML_c, XML_v));
2908  pFS->endElement(FSNS(XML_c, XML_pt));
2909  }
2910 
2911  pFS->endElement(FSNS(XML_c, bWriteDateCategories ? XML_numCache : XML_strCache));
2912  pFS->endElement(FSNS(XML_c, bWriteDateCategories ? XML_numRef : XML_strRef));
2913  }
2914 
2915  pFS->endElement( FSNS( XML_c, nValueType ) );
2916 }
2917 
2918 void ChartExport::exportSeriesValues( const Reference< chart2::data::XDataSequence > & xValueSeq, sal_Int32 nValueType )
2919 {
2920  FSHelperPtr pFS = GetFS();
2921  pFS->startElement(FSNS(XML_c, nValueType));
2922 
2923  OUString aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString();
2924  aCellRange = parseFormula( aCellRange );
2925  // TODO: need to handle XML_multiLvlStrRef according to aCellRange
2926  pFS->startElement(FSNS(XML_c, XML_numRef));
2927 
2928  pFS->startElement(FSNS(XML_c, XML_f));
2929  pFS->writeEscaped( aCellRange );
2930  pFS->endElement( FSNS( XML_c, XML_f ) );
2931 
2932  ::std::vector< double > aValues = lcl_getAllValuesFromSequence( xValueSeq );
2933  sal_Int32 ptCount = aValues.size();
2934  pFS->startElement(FSNS(XML_c, XML_numCache));
2935  pFS->startElement(FSNS(XML_c, XML_formatCode));
2936  // TODO: what format code?
2937  pFS->writeEscaped( "General" );
2938  pFS->endElement( FSNS( XML_c, XML_formatCode ) );
2939  pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount));
2940 
2941  for( sal_Int32 i = 0; i < ptCount; i++ )
2942  {
2943  if (!std::isnan(aValues[i]))
2944  {
2945  pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i));
2946  pFS->startElement(FSNS(XML_c, XML_v));
2947  pFS->write(aValues[i]);
2948  pFS->endElement(FSNS(XML_c, XML_v));
2949  pFS->endElement(FSNS(XML_c, XML_pt));
2950  }
2951  }
2952 
2953  pFS->endElement( FSNS( XML_c, XML_numCache ) );
2954  pFS->endElement( FSNS( XML_c, XML_numRef ) );
2955  pFS->endElement( FSNS( XML_c, nValueType ) );
2956 }
2957 
2959 {
2960  FSHelperPtr pFS = GetFS();
2961  pFS->startElement(FSNS(XML_c, XML_spPr));
2962 
2963  exportFill( xPropSet );
2964  WriteOutline( xPropSet, getModel() );
2965 
2966  pFS->endElement( FSNS( XML_c, XML_spPr ) );
2967 }
2968 
2970 {
2971  FSHelperPtr pFS = GetFS();
2972  pFS->startElement(FSNS(XML_c, XML_txPr));
2973 
2974  sal_Int32 nRotation = 0;
2975  const char* textWordWrap = nullptr;
2976 
2977  if (auto xServiceInfo = uno::Reference<lang::XServiceInfo>(xPropSet, uno::UNO_QUERY))
2978  {
2979  double fMultiplier = 0.0;
2980  // We have at least two possible units of returned value: degrees (e.g., for data labels),
2981  // and 100ths of degree (e.g., for axes labels). The latter is returned as an Any wrapping
2982  // a sal_Int32 value (see WrappedTextRotationProperty::convertInnerToOuterValue), while
2983  // the former is double. So we could test the contained type to decide which multiplier to
2984  // use. But testing the service info should be more robust.
2985  if (xServiceInfo->supportsService("com.sun.star.chart.ChartAxis"))
2986  fMultiplier = -600.0;
2987  else if (xServiceInfo->supportsService("com.sun.star.chart2.DataSeries") || xServiceInfo->supportsService("com.sun.star.chart2.DataPointProperties"))
2988  {
2989  fMultiplier = -60000.0;
2990  bool bTextWordWrap = false;
2991  if ((xPropSet->getPropertyValue("TextWordWrap") >>= bTextWordWrap) && bTextWordWrap)
2992  textWordWrap = "square";
2993  else
2994  textWordWrap = "none";
2995  }
2996 
2997  if (fMultiplier)
2998  {
2999  double fTextRotation = 0.0;
3000  uno::Any aAny = xPropSet->getPropertyValue("TextRotation");
3001  if (aAny.hasValue() && (aAny >>= fTextRotation))
3002  {
3003  fTextRotation *= fMultiplier;
3004  // The MS Office UI allows values only in range of [-90,90].
3005  if (fTextRotation < -5400000.0 && fTextRotation > -16200000.0)
3006  {
3007  // Reflect the angle if the value is between 90° and 270°
3008  fTextRotation += 10800000.0;
3009  }
3010  else if (fTextRotation <= -16200000.0)
3011  {
3012  fTextRotation += 21600000.0;
3013  }
3014  nRotation = std::round(fTextRotation);
3015  }
3016  }
3017  }
3018 
3019  if (nRotation)
3020  pFS->singleElement(FSNS(XML_a, XML_bodyPr), XML_rot, OString::number(nRotation), XML_wrap, textWordWrap);
3021  else
3022  pFS->singleElement(FSNS(XML_a, XML_bodyPr), XML_wrap, textWordWrap);
3023 
3024  pFS->singleElement(FSNS(XML_a, XML_lstStyle));
3025 
3026  pFS->startElement(FSNS(XML_a, XML_p));
3027  pFS->startElement(FSNS(XML_a, XML_pPr));
3028 
3029  WriteRunProperties(xPropSet, false, XML_defRPr, true, o3tl::temporary(false),
3030  o3tl::temporary(sal_Int32()));
3031 
3032  pFS->endElement(FSNS(XML_a, XML_pPr));
3033  pFS->endElement(FSNS(XML_a, XML_p));
3034  pFS->endElement(FSNS(XML_c, XML_txPr));
3035 }
3036 
3038 {
3039  Reference< XPropertySet > xDiagramProperties (mxDiagram, uno::UNO_QUERY);
3040 
3041  // Check for supported services and then the properties provided by this service.
3042  Reference<lang::XServiceInfo> xServiceInfo (mxDiagram, uno::UNO_QUERY);
3043  if (xServiceInfo.is())
3044  {
3045  if (xServiceInfo->supportsService("com.sun.star.chart.ChartAxisZSupplier"))
3046  {
3047  xDiagramProperties->getPropertyValue("HasZAxis") >>= mbHasZAxis;
3048  }
3049  }
3050 
3051  xDiagramProperties->getPropertyValue("Dim3D") >>= mbIs3DChart;
3052 
3053  if( mbHasCategoryLabels && mxNewDiagram.is())
3054  {
3056  if( xCategories.is() )
3057  {
3058  mxCategoriesValues.set( xCategories->getValues() );
3059  }
3060  }
3061 }
3062 
3064 {
3065  sal_Int32 nSize = maAxes.size();
3066  // let's export the axis types in the right order
3067  for ( sal_Int32 nSortIdx = AXIS_PRIMARY_X; nSortIdx <= AXIS_SECONDARY_Y; nSortIdx++ )
3068  {
3069  for ( sal_Int32 nIdx = 0; nIdx < nSize; nIdx++ )
3070  {
3071  if (nSortIdx == maAxes[nIdx].nAxisType)
3072  exportAxis( maAxes[nIdx] );
3073  }
3074  }
3075 }
3076 
3077 namespace {
3078 
3079 sal_Int32 getXAxisTypeByChartType(sal_Int32 eChartType)
3080 {
3081  if( (eChartType == chart::TYPEID_SCATTER)
3082  || (eChartType == chart::TYPEID_BUBBLE) )
3083  return XML_valAx;
3084  else if( eChartType == chart::TYPEID_STOCK )
3085  return XML_dateAx;
3086 
3087  return XML_catAx;
3088 }
3089 
3090 sal_Int32 getRealXAxisType(sal_Int32 nAxisType)
3091 {
3092  if( nAxisType == chart2::AxisType::CATEGORY )
3093  return XML_catAx;
3094  else if( nAxisType == chart2::AxisType::DATE )
3095  return XML_dateAx;
3096  else if( nAxisType == chart2::AxisType::SERIES )
3097  return XML_serAx;
3098 
3099  return XML_valAx;
3100 }
3101 
3102 }
3103 
3104 void ChartExport::exportAxis(const AxisIdPair& rAxisIdPair)
3105 {
3106  // get some properties from document first
3107  bool bHasXAxisTitle = false,
3108  bHasYAxisTitle = false,
3109  bHasZAxisTitle = false,
3110  bHasSecondaryXAxisTitle = false,
3111  bHasSecondaryYAxisTitle = false;
3112  bool bHasXAxisMajorGrid = false,
3113  bHasXAxisMinorGrid = false,
3114  bHasYAxisMajorGrid = false,
3115  bHasYAxisMinorGrid = false,
3116  bHasZAxisMajorGrid = false,
3117  bHasZAxisMinorGrid = false;
3118 
3119  Reference< XPropertySet > xDiagramProperties (mxDiagram, uno::UNO_QUERY);
3120 
3121  xDiagramProperties->getPropertyValue("HasXAxisTitle") >>= bHasXAxisTitle;
3122  xDiagramProperties->getPropertyValue("HasYAxisTitle") >>= bHasYAxisTitle;
3123  xDiagramProperties->getPropertyValue("HasZAxisTitle") >>= bHasZAxisTitle;
3124  xDiagramProperties->getPropertyValue("HasSecondaryXAxisTitle") >>= bHasSecondaryXAxisTitle;
3125  xDiagramProperties->getPropertyValue("HasSecondaryYAxisTitle") >>= bHasSecondaryYAxisTitle;
3126 
3127  xDiagramProperties->getPropertyValue("HasXAxisGrid") >>= bHasXAxisMajorGrid;
3128  xDiagramProperties->getPropertyValue("HasYAxisGrid") >>= bHasYAxisMajorGrid;
3129  xDiagramProperties->getPropertyValue("HasZAxisGrid") >>= bHasZAxisMajorGrid;
3130 
3131  xDiagramProperties->getPropertyValue("HasXAxisHelpGrid") >>= bHasXAxisMinorGrid;
3132  xDiagramProperties->getPropertyValue("HasYAxisHelpGrid") >>= bHasYAxisMinorGrid;
3133  xDiagramProperties->getPropertyValue("HasZAxisHelpGrid") >>= bHasZAxisMinorGrid;
3134 
3135  Reference< XPropertySet > xAxisProp;
3136  Reference< drawing::XShape > xAxisTitle;
3139  sal_Int32 nAxisType = XML_catAx;
3140  const char* sAxPos = nullptr;
3141 
3142  switch( rAxisIdPair.nAxisType )
3143  {
3144  case AXIS_PRIMARY_X:
3145  {
3146  Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY );
3147  if( xAxisXSupp.is())
3148  xAxisProp = xAxisXSupp->getXAxis();
3149  if( bHasXAxisTitle )
3150  xAxisTitle = xAxisXSupp->getXAxisTitle();
3151  if( bHasXAxisMajorGrid )
3152  xMajorGrid = xAxisXSupp->getXMainGrid();
3153  if( bHasXAxisMinorGrid )
3154  xMinorGrid = xAxisXSupp->getXHelpGrid();
3155 
3156  nAxisType = lcl_getCategoryAxisType(mxNewDiagram, 0, 0);
3157  if( nAxisType != -1 )
3158  nAxisType = getRealXAxisType(nAxisType);
3159  else
3160  nAxisType = getXAxisTypeByChartType( getChartType() );
3161  // FIXME: axPos, need to check axis direction
3162  sAxPos = "b";
3163  break;
3164  }
3165  case AXIS_PRIMARY_Y:
3166  {
3167  Reference< css::chart::XAxisYSupplier > xAxisYSupp( mxDiagram, uno::UNO_QUERY );
3168  if( xAxisYSupp.is())
3169  xAxisProp = xAxisYSupp->getYAxis();
3170  if( bHasYAxisTitle )
3171  xAxisTitle = xAxisYSupp->getYAxisTitle();
3172  if( bHasYAxisMajorGrid )
3173  xMajorGrid = xAxisYSupp->getYMainGrid();
3174  if( bHasYAxisMinorGrid )
3175  xMinorGrid = xAxisYSupp->getYHelpGrid();
3176 
3177  nAxisType = XML_valAx;
3178  // FIXME: axPos, need to check axis direction
3179  sAxPos = "l";
3180  break;
3181  }
3182  case AXIS_PRIMARY_Z:
3183  {
3184  Reference< css::chart::XAxisZSupplier > xAxisZSupp( mxDiagram, uno::UNO_QUERY );
3185  if( xAxisZSupp.is())
3186  xAxisProp = xAxisZSupp->getZAxis();
3187  if( bHasZAxisTitle )
3188  xAxisTitle = xAxisZSupp->getZAxisTitle();
3189  if( bHasZAxisMajorGrid )
3190  xMajorGrid = xAxisZSupp->getZMainGrid();
3191  if( bHasZAxisMinorGrid )
3192  xMinorGrid = xAxisZSupp->getZHelpGrid();
3193 
3194  sal_Int32 eChartType = getChartType( );
3195  if( (eChartType == chart::TYPEID_SCATTER)
3196  || (eChartType == chart::TYPEID_BUBBLE) )
3197  nAxisType = XML_valAx;
3198  else if( eChartType == chart::TYPEID_STOCK )
3199  nAxisType = XML_dateAx;
3200  else if( eChartType == chart::TYPEID_BAR || eChartType == chart::TYPEID_AREA )
3201  nAxisType = XML_serAx;
3202  // FIXME: axPos, need to check axis direction
3203  sAxPos = "b";
3204  break;
3205  }
3206  case AXIS_SECONDARY_X:
3207  {
3208  Reference< css::chart::XTwoAxisXSupplier > xAxisTwoXSupp( mxDiagram, uno::UNO_QUERY );
3209  if( xAxisTwoXSupp.is())
3210  xAxisProp = xAxisTwoXSupp->getSecondaryXAxis();
3211  if( bHasSecondaryXAxisTitle )
3212  {
3213  Reference< css::chart::XSecondAxisTitleSupplier > xAxisSupp( mxDiagram, uno::UNO_QUERY );
3214  xAxisTitle = xAxisSupp->getSecondXAxisTitle();
3215  }
3216 
3217  nAxisType = lcl_getCategoryAxisType(mxNewDiagram, 0, 1);
3218  if( nAxisType != -1 )
3219  nAxisType = getRealXAxisType(nAxisType);
3220  else
3221  nAxisType = getXAxisTypeByChartType( getChartType() );
3222  // FIXME: axPos, need to check axis direction
3223  sAxPos = "t";
3224  break;
3225  }
3226  case AXIS_SECONDARY_Y:
3227  {
3228  Reference< css::chart::XTwoAxisYSupplier > xAxisTwoYSupp( mxDiagram, uno::UNO_QUERY );
3229  if( xAxisTwoYSupp.is())
3230  xAxisProp = xAxisTwoYSupp->getSecondaryYAxis();
3231  if( bHasSecondaryYAxisTitle )
3232  {
3233  Reference< css::chart::XSecondAxisTitleSupplier > xAxisSupp( mxDiagram, uno::UNO_QUERY );
3234  xAxisTitle = xAxisSupp->getSecondYAxisTitle();
3235  }
3236 
3237  nAxisType = XML_valAx;
3238  // FIXME: axPos, need to check axis direction
3239  sAxPos = "r";
3240  break;
3241  }
3242  }
3243 
3244  _exportAxis(xAxisProp, xAxisTitle, xMajorGrid, xMinorGrid, nAxisType, sAxPos, rAxisIdPair);
3245 }
3246 
3248  const Reference< XPropertySet >& xAxisProp,
3249  const Reference< drawing::XShape >& xAxisTitle,
3250  const Reference< XPropertySet >& xMajorGrid,
3251  const Reference< XPropertySet >& xMinorGrid,
3252  sal_Int32 nAxisType,
3253  const char* sAxisPos,
3254  const AxisIdPair& rAxisIdPair )
3255 {
3256  FSHelperPtr pFS = GetFS();
3257  pFS->startElement(FSNS(XML_c, nAxisType));
3258  pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(rAxisIdPair.nAxisId));
3259 
3260  pFS->startElement(FSNS(XML_c, XML_scaling));
3261 
3262  // logBase, min, max
3263  if(GetProperty( xAxisProp, "Logarithmic" ) )
3264  {
3265  bool bLogarithmic = false;
3266  mAny >>= bLogarithmic;
3267  if( bLogarithmic )
3268  {
3269  // default value is 10?
3270  pFS->singleElement(FSNS(XML_c, XML_logBase), XML_val, OString::number(10));
3271  }
3272  }
3273 
3274  // orientation: minMax, maxMin
3275  bool bReverseDirection = false;
3276  if(GetProperty( xAxisProp, "ReverseDirection" ) )
3277  mAny >>= bReverseDirection;
3278 
3279  const char* orientation = bReverseDirection ? "maxMin":"minMax";
3280  pFS->singleElement(FSNS(XML_c, XML_orientation), XML_val, orientation);
3281 
3282  bool bAutoMax = false;
3283  if(GetProperty( xAxisProp, "AutoMax" ) )
3284  mAny >>= bAutoMax;
3285 
3286  if( !bAutoMax && (GetProperty( xAxisProp, "Max" ) ) )
3287  {
3288  double dMax = 0;
3289  mAny >>= dMax;
3290  pFS->singleElement(FSNS(XML_c, XML_max), XML_val, OString::number(dMax));
3291  }
3292 
3293  bool bAutoMin = false;
3294  if(GetProperty( xAxisProp, "AutoMin" ) )
3295  mAny >>= bAutoMin;
3296 
3297  if( !bAutoMin && (GetProperty( xAxisProp, "Min" ) ) )
3298  {
3299  double dMin = 0;
3300  mAny >>= dMin;
3301  pFS->singleElement(FSNS(XML_c, XML_min), XML_val, OString::number(dMin));
3302  }
3303 
3304  pFS->endElement( FSNS( XML_c, XML_scaling ) );
3305 
3306  bool bVisible = true;
3307  if( xAxisProp.is() )
3308  {
3309  xAxisProp->getPropertyValue("Visible") >>= bVisible;
3310  }
3311 
3312  // only export each axis only once non-deleted
3313  bool bDeleted = maExportedAxis.find(rAxisIdPair.nAxisType) != maExportedAxis.end();
3314 
3315  if (!bDeleted)
3316  maExportedAxis.insert(rAxisIdPair.nAxisType);
3317 
3318  pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, !bDeleted && bVisible ? "0" : "1");
3319 
3320  // FIXME: axPos, need to check the property "ReverseDirection"
3321  pFS->singleElement(FSNS(XML_c, XML_axPos), XML_val, sAxisPos);
3322  // major grid line
3323  if( xMajorGrid.is())
3324  {
3325  pFS->startElement(FSNS(XML_c, XML_majorGridlines));
3326  exportShapeProps( xMajorGrid );
3327  pFS->endElement( FSNS( XML_c, XML_majorGridlines ) );
3328  }
3329 
3330  // minor grid line
3331  if( xMinorGrid.is())
3332  {
3333  pFS->startElement(FSNS(XML_c, XML_minorGridlines));
3334  exportShapeProps( xMinorGrid );
3335  pFS->endElement( FSNS( XML_c, XML_minorGridlines ) );
3336  }
3337 
3338  // title
3339  if( xAxisTitle.is() )
3340  exportTitle( xAxisTitle );
3341 
3342  bool bLinkedNumFmt = true;
3343  if (GetProperty(xAxisProp, "LinkNumberFormatToSource"))
3344  mAny >>= bLinkedNumFmt;
3345 
3346  OUString aNumberFormatString("General");
3347  if (GetProperty(xAxisProp, "NumberFormat"))
3348  {
3349  sal_Int32 nKey = 0;
3350  mAny >>= nKey;
3351  aNumberFormatString = getNumberFormatCode(nKey);
3352  }
3353 
3354  OString sNumberFormatString = OUStringToOString(aNumberFormatString, RTL_TEXTENCODING_UTF8);
3355  pFS->singleElement(FSNS(XML_c, XML_numFmt),
3356  XML_formatCode, sNumberFormatString.getStr(),
3357  XML_sourceLinked, bLinkedNumFmt ? "1" : "0");
3358 
3359  // majorTickMark
3360  sal_Int32 nValue = 0;
3361  if(GetProperty( xAxisProp, "Marks" ) )
3362  {
3363  mAny >>= nValue;
3364  bool bInner = nValue & css::chart::ChartAxisMarks::INNER;
3365  bool bOuter = nValue & css::chart::ChartAxisMarks::OUTER;
3366  const char* majorTickMark = nullptr;
3367  if( bInner && bOuter )
3368  majorTickMark = "cross";
3369  else if( bInner )
3370  majorTickMark = "in";
3371  else if( bOuter )
3372  majorTickMark = "out";
3373  else
3374  majorTickMark = "none";
3375  pFS->singleElement(FSNS(XML_c, XML_majorTickMark), XML_val, majorTickMark);
3376  }
3377  // minorTickMark
3378  if(GetProperty( xAxisProp, "HelpMarks" ) )
3379  {
3380  mAny >>= nValue;
3381  bool bInner = nValue & css::chart::ChartAxisMarks::INNER;
3382  bool bOuter = nValue & css::chart::ChartAxisMarks::OUTER;
3383  const char* minorTickMark = nullptr;
3384  if( bInner && bOuter )
3385  minorTickMark = "cross";
3386  else if( bInner )
3387  minorTickMark = "in";
3388  else if( bOuter )
3389  minorTickMark = "out";
3390  else
3391  minorTickMark = "none";
3392  pFS->singleElement(FSNS(XML_c, XML_minorTickMark), XML_val, minorTickMark);
3393  }
3394  // tickLblPos
3395  const char* sTickLblPos = nullptr;
3396  bool bDisplayLabel = true;
3397  if(GetProperty( xAxisProp, "DisplayLabels" ) )
3398  mAny >>= bDisplayLabel;
3399  if( bDisplayLabel && (GetProperty( xAxisProp, "LabelPosition" ) ) )
3400  {
3401  css::chart::ChartAxisLabelPosition eLabelPosition = css::chart::ChartAxisLabelPosition_NEAR_AXIS;
3402  mAny >>= eLabelPosition;
3403  switch( eLabelPosition )
3404  {
3405  case css::chart::ChartAxisLabelPosition_NEAR_AXIS:
3406  case css::chart::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE:
3407  sTickLblPos = "nextTo";
3408  break;
3409  case css::chart::ChartAxisLabelPosition_OUTSIDE_START:
3410  sTickLblPos = "low";
3411  break;
3412  case css::chart::ChartAxisLabelPosition_OUTSIDE_END:
3413  sTickLblPos = "high";
3414  break;
3415  default:
3416  sTickLblPos = "nextTo";
3417  break;
3418  }
3419  }
3420  else
3421  {
3422  sTickLblPos = "none";
3423  }
3424  pFS->singleElement(FSNS(XML_c, XML_tickLblPos), XML_val, sTickLblPos);
3425 
3426  // shape properties
3427  exportShapeProps( xAxisProp );
3428 
3429  exportTextProps(xAxisProp);
3430 
3431  pFS->singleElement(FSNS(XML_c, XML_crossAx), XML_val, OString::number(rAxisIdPair.nCrossAx));
3432 
3433  // crosses & crossesAt
3434  bool bCrossesValue = false;
3435  const char* sCrosses = nullptr;
3436  // do not export the CrossoverPosition/CrossoverValue, if the axis is deleted and not visible
3437  if( GetProperty( xAxisProp, "CrossoverPosition" ) && !bDeleted && bVisible )
3438  {
3439  css::chart::ChartAxisPosition ePosition( css::chart::ChartAxisPosition_ZERO );
3440  mAny >>= ePosition;
3441  switch( ePosition )
3442  {
3443  case css::chart::ChartAxisPosition_START:
3444  sCrosses = "min";
3445  break;
3446  case css::chart::ChartAxisPosition_END:
3447  sCrosses = "max";
3448  break;
3449  case css::chart::ChartAxisPosition_ZERO:
3450  sCrosses = "autoZero";
3451  break;
3452  default:
3453  bCrossesValue = true;
3454  break;
3455  }
3456  }
3457 
3458  if( bCrossesValue && GetProperty( xAxisProp, "CrossoverValue" ) )
3459  {
3460  double dValue = 0;
3461  mAny >>= dValue;
3462  pFS->singleElement(FSNS(XML_c, XML_crossesAt), XML_val, OString::number(dValue));
3463  }
3464  else
3465  {
3466  if(sCrosses)
3467  {
3468  pFS->singleElement(FSNS(XML_c, XML_crosses), XML_val, sCrosses);
3469  }
3470  }
3471 
3472  if( ( nAxisType == XML_catAx )
3473  || ( nAxisType == XML_dateAx ) )
3474  {
3475  // FIXME: seems not support? use default value,
3476  const char* const isAuto = "1";
3477  pFS->singleElement(FSNS(XML_c, XML_auto), XML_val, isAuto);
3478 
3479  if( nAxisType == XML_catAx )
3480  {
3481  // FIXME: seems not support? lblAlgn
3482  const char* const sLblAlgn = "ctr";
3483  pFS->singleElement(FSNS(XML_c, XML_lblAlgn), XML_val, sLblAlgn);
3484  }
3485 
3486  // FIXME: seems not support? lblOffset
3487  pFS->singleElement(FSNS(XML_c, XML_lblOffset), XML_val, OString::number(100));
3488 
3489  // export baseTimeUnit, majorTimeUnit, minorTimeUnit of Date axis
3490  if( nAxisType == XML_dateAx )
3491  {
3492  sal_Int32 nAxisIndex = -1;
3493  if( rAxisIdPair.nAxisType == AXIS_PRIMARY_X )
3494  nAxisIndex = 0;
3495  else if( rAxisIdPair.nAxisType == AXIS_SECONDARY_X )
3496  nAxisIndex = 1;
3497 
3498  cssc::TimeIncrement aTimeIncrement = lcl_getDateTimeIncrement( mxNewDiagram, nAxisIndex );
3499  sal_Int32 nTimeResolution = css::chart::TimeUnit::DAY;
3500  if( aTimeIncrement.TimeResolution >>= nTimeResolution )
3501  pFS->singleElement(FSNS(XML_c, XML_baseTimeUnit), XML_val, lclGetTimeUnitToken(nTimeResolution));
3502 
3503  cssc::TimeInterval aInterval;
3504  if( aTimeIncrement.MajorTimeInterval >>= aInterval )
3505  {
3506  pFS->singleElement(FSNS(XML_c, XML_majorUnit), XML_val, OString::number(aInterval.Number));
3507  pFS->singleElement(FSNS(XML_c, XML_majorTimeUnit), XML_val, lclGetTimeUnitToken(aInterval.TimeUnit));
3508  }
3509  if( aTimeIncrement.MinorTimeInterval >>= aInterval )
3510  {
3511  pFS->singleElement(FSNS(XML_c, XML_minorUnit), XML_val, OString::number(aInterval.Number));
3512  pFS->singleElement(FSNS(XML_c, XML_minorTimeUnit), XML_val, lclGetTimeUnitToken(aInterval.TimeUnit));
3513  }
3514  }
3515 
3516  // FIXME: seems not support? noMultiLvlLbl
3517  pFS->singleElement(FSNS(XML_c, XML_noMultiLvlLbl), XML_val, OString::number(0));
3518  }
3519 
3520  // crossBetween
3521  if( nAxisType == XML_valAx )
3522  {
3524  pFS->singleElement(FSNS(XML_c, XML_crossBetween), XML_val, "between");
3525  else
3526  pFS->singleElement(FSNS(XML_c, XML_crossBetween), XML_val, "midCat");
3527  }
3528 
3529  // majorUnit
3530  bool bAutoStepMain = false;
3531  if(GetProperty( xAxisProp, "AutoStepMain" ) )
3532  mAny >>= bAutoStepMain;
3533 
3534  if( !bAutoStepMain && (GetProperty( xAxisProp, "StepMain" ) ) )
3535  {
3536  double dMajorUnit = 0;
3537  mAny >>= dMajorUnit;
3538  pFS->singleElement(FSNS(XML_c, XML_majorUnit), XML_val, OString::number(dMajorUnit));
3539  }
3540  // minorUnit
3541  bool bAutoStepHelp = false;
3542  if(GetProperty( xAxisProp, "AutoStepHelp" ) )
3543  mAny >>= bAutoStepHelp;
3544 
3545  if( !bAutoStepHelp && (GetProperty( xAxisProp, "StepHelp" ) ) )
3546  {
3547  double dMinorUnit = 0;
3548  mAny >>= dMinorUnit;
3549  if( GetProperty( xAxisProp, "StepHelpCount" ) )
3550  {
3551  sal_Int32 dMinorUnitCount = 0;
3552  mAny >>= dMinorUnitCount;
3553  // tdf#114168 Don't save minor unit if number of step help count is 5 (which is default for MS Excel),
3554  // to allow proper .xlsx import. If minorUnit is set and majorUnit not, then it is impossible
3555  // to calculate StepHelpCount.
3556  if( dMinorUnitCount != 5 )
3557  {
3558  pFS->singleElement( FSNS( XML_c, XML_minorUnit ),
3559  XML_val, OString::number( dMinorUnit ) );
3560  }
3561  }
3562  }
3563 
3564  if( nAxisType == XML_valAx && GetProperty( xAxisProp, "DisplayUnits" ) )
3565  {
3566  bool bDisplayUnits = false;
3567  mAny >>= bDisplayUnits;
3568  if(bDisplayUnits)
3569  {
3570  if(GetProperty( xAxisProp, "BuiltInUnit" ))
3571  {
3572  OUString aVal;
3573  mAny >>= aVal;
3574  if(!aVal.isEmpty())
3575  {
3576  pFS->startElement(FSNS(XML_c, XML_dispUnits));
3577 
3578  pFS->singleElement(FSNS(XML_c, XML_builtInUnit), XML_val, aVal);
3579 
3580  pFS->singleElement(FSNS( XML_c, XML_dispUnitsLbl ));
3581  pFS->endElement( FSNS( XML_c, XML_dispUnits ) );
3582  }
3583  }
3584  }
3585  }
3586 
3587  pFS->endElement( FSNS( XML_c, nAxisType ) );
3588 }
3589 
3590 namespace {
3591 
3592 struct LabelPlacementParam
3593 {
3594  bool mbExport;
3595  sal_Int32 meDefault;
3596 
3597  std::unordered_set<sal_Int32> maAllowedValues;
3598 
3599  LabelPlacementParam(bool bExport, sal_Int32 nDefault) :
3600  mbExport(bExport),
3601  meDefault(nDefault),
3602  maAllowedValues(
3603  {
3604  css::chart::DataLabelPlacement::OUTSIDE,
3605  css::chart::DataLabelPlacement::INSIDE,
3606  css::chart::DataLabelPlacement::CENTER,
3607  css::chart::DataLabelPlacement::NEAR_ORIGIN,
3608  css::chart::DataLabelPlacement::TOP,
3609  css::chart::DataLabelPlacement::BOTTOM,
3610  css::chart::DataLabelPlacement::LEFT,
3611  css::chart::DataLabelPlacement::RIGHT,
3612  css::chart::DataLabelPlacement::AVOID_OVERLAP
3613  }
3614  )
3615  {}
3616 };
3617 
3618 const char* toOOXMLPlacement( sal_Int32 nPlacement )
3619 {
3620  switch (nPlacement)
3621  {
3622  case css::chart::DataLabelPlacement::OUTSIDE: return "outEnd";
3623  case css::chart::DataLabelPlacement::INSIDE: return "inEnd";
3624  case css::chart::DataLabelPlacement::CENTER: return "ctr";
3625  case css::chart::DataLabelPlacement::NEAR_ORIGIN: return "inBase";
3626  case css::chart::DataLabelPlacement::TOP: return "t";
3627  case css::chart::DataLabelPlacement::BOTTOM: return "b";
3628  case css::chart::DataLabelPlacement::LEFT: return "l";
3629  case css::chart::DataLabelPlacement::RIGHT: return "r";
3630  case css::chart::DataLabelPlacement::CUSTOM:
3631  case css::chart::DataLabelPlacement::AVOID_OVERLAP: return "bestFit";
3632  default:
3633  ;
3634  }
3635 
3636  return "outEnd";
3637 }
3638 
3639 OUString getFieldTypeString( const chart2::DataPointCustomLabelFieldType aType )
3640 {
3641  switch (aType)
3642  {
3643  case chart2::DataPointCustomLabelFieldType_CATEGORYNAME:
3644  return "CATEGORYNAME";
3645 
3646  case chart2::DataPointCustomLabelFieldType_SERIESNAME:
3647  return "SERIESNAME";
3648 
3649  case chart2::DataPointCustomLabelFieldType_VALUE:
3650  return "VALUE";
3651 
3652  case chart2::DataPointCustomLabelFieldType_CELLREF:
3653  return "CELLREF";
3654 
3655  case chart2::DataPointCustomLabelFieldType_CELLRANGE:
3656  return "CELLRANGE";
3657 
3658  default:
3659  break;
3660  }
3661  return OUString();
3662 }
3663 
3664 void writeRunProperties( ChartExport* pChartExport, Reference<XPropertySet> const & xPropertySet )
3665 {
3666  bool bDummy = false;
3667  sal_Int32 nDummy;
3668  pChartExport->WriteRunProperties(xPropertySet, false, XML_rPr, true, bDummy, nDummy);
3669 }
3670 
3671 void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport,
3672  const Sequence<Reference<chart2::XDataPointCustomLabelField>>& rCustomLabelFields,
3673  sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange )
3674 {
3675  pFS->startElement(FSNS(XML_c, XML_tx));
3676  pFS->startElement(FSNS(XML_c, XML_rich));
3677 
3678  // TODO: body properties?
3679  pFS->singleElement(FSNS(XML_a, XML_bodyPr));
3680 
3681  OUString sFieldType;
3682  OUString sContent;
3683  pFS->startElement(FSNS(XML_a, XML_p));
3684 
3685  for (auto& rField : rCustomLabelFields)
3686  {
3687  Reference<XPropertySet> xPropertySet(rField, UNO_QUERY);
3688  chart2::DataPointCustomLabelFieldType aType = rField->getFieldType();
3689  sFieldType.clear();
3690  sContent.clear();
3691  bool bNewParagraph = false;
3692 
3693  if (aType == chart2::DataPointCustomLabelFieldType_CELLRANGE &&
3694  rField->getDataLabelsRange())
3695  {
3696  if (rDLblsRange.getRange().isEmpty())
3697  rDLblsRange.setRange(rField->getCellRange());
3698 
3699  if (!rDLblsRange.hasLabel(nLabelIndex))
3700  rDLblsRange.setLabel(nLabelIndex, rField->getString());
3701 
3702  sContent = "[CELLRANGE]";
3703  }
3704  else
3705  {
3706  sContent = rField->getString();
3707  }
3708 
3709  if (aType == chart2::DataPointCustomLabelFieldType_NEWLINE)
3710  bNewParagraph = true;
3711  else if (aType != chart2::DataPointCustomLabelFieldType_TEXT)
3712  sFieldType = getFieldTypeString(aType);
3713 
3714  if (bNewParagraph)
3715  {
3716  pFS->endElement(FSNS(XML_a, XML_p));
3717  pFS->startElement(FSNS(XML_a, XML_p));
3718  continue;
3719  }
3720 
3721  if (sFieldType.isEmpty())
3722  {
3723  // Normal text run
3724  pFS->startElement(FSNS(XML_a, XML_r));
3725  writeRunProperties(pChartExport, xPropertySet);
3726 
3727  pFS->startElement(FSNS(XML_a, XML_t));
3728  pFS->writeEscaped(sContent);
3729  pFS->endElement(FSNS(XML_a, XML_t));
3730 
3731  pFS->endElement(FSNS(XML_a, XML_r));
3732  }
3733  else
3734  {
3735  // Field
3736  pFS->startElement(FSNS(XML_a, XML_fld), XML_id, rField->getGuid(), XML_type,
3737  sFieldType);
3738  writeRunProperties(pChartExport, xPropertySet);
3739 
3740  pFS->startElement(FSNS(XML_a, XML_t));
3741  pFS->writeEscaped(sContent);
3742  pFS->endElement(FSNS(XML_a, XML_t));
3743 
3744  pFS->endElement(FSNS(XML_a, XML_fld));
3745  }
3746  }
3747 
3748  pFS->endElement(FSNS(XML_a, XML_p));
3749  pFS->endElement(FSNS(XML_c, XML_rich));
3750  pFS->endElement(FSNS(XML_c, XML_tx));
3751 }
3752 
3753 void writeLabelProperties( const FSHelperPtr& pFS, ChartExport* pChartExport,
3754  const uno::Reference<beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam,
3755  sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange )
3756 {
3757  if (!xPropSet.is())
3758  return;
3759 
3760  chart2::DataPointLabel aLabel;
3761  Sequence<Reference<chart2::XDataPointCustomLabelField>> aCustomLabelFields;
3762  sal_Int32 nLabelBorderWidth = 0;
3763  sal_Int32 nLabelBorderColor = 0x00FFFFFF;
3764  sal_Int32 nLabelFillColor = -1;
3765 
3766  xPropSet->getPropertyValue("Label") >>= aLabel;
3767  xPropSet->getPropertyValue("CustomLabelFields") >>= aCustomLabelFields;
3768  xPropSet->getPropertyValue("LabelBorderWidth") >>= nLabelBorderWidth;
3769  xPropSet->getPropertyValue("LabelBorderColor") >>= nLabelBorderColor;
3770  xPropSet->getPropertyValue("LabelFillColor") >>= nLabelFillColor;
3771 
3772  if (nLabelBorderWidth > 0 || nLabelFillColor != -1)
3773  {
3774  pFS->startElement(FSNS(XML_c, XML_spPr));
3775 
3776  if (nLabelFillColor != -1)
3777  {
3778  pFS->startElement(FSNS(XML_a, XML_solidFill));
3779 
3780  OString aStr = OString::number(nLabelFillColor, 16).toAsciiUpperCase();
3781  pFS->singleElement(FSNS(XML_a, XML_srgbClr), XML_val, aStr);
3782 
3783  pFS->endElement(FSNS(XML_a, XML_solidFill));
3784  }
3785 
3786  if (nLabelBorderWidth > 0)
3787  {
3788  pFS->startElement(FSNS(XML_a, XML_ln), XML_w,
3789  OString::number(convertHmmToEmu(nLabelBorderWidth)));
3790 
3791  if (nLabelBorderColor != -1)
3792  {
3793  pFS->startElement(FSNS(XML_a, XML_solidFill));
3794 
3795  OString aStr = OString::number(nLabelBorderColor, 16).toAsciiUpperCase();
3796  pFS->singleElement(FSNS(XML_a, XML_srgbClr), XML_val, aStr);
3797 
3798  pFS->endElement(FSNS(XML_a, XML_solidFill));
3799  }
3800 
3801  pFS->endElement(FSNS(XML_a, XML_ln));
3802  }
3803 
3804  pFS->endElement(FSNS(XML_c, XML_spPr));
3805  }
3806 
3807  pChartExport->exportTextProps(xPropSet);
3808 
3809  if (aCustomLabelFields.hasElements())
3810  writeCustomLabel(pFS, pChartExport, aCustomLabelFields, nLabelIndex, rDLblsRange);
3811 
3812  if (rLabelParam.mbExport)
3813  {
3814  sal_Int32 nLabelPlacement = rLabelParam.meDefault;
3815  if (xPropSet->getPropertyValue("LabelPlacement") >>= nLabelPlacement)
3816  {
3817  if (!rLabelParam.maAllowedValues.count(nLabelPlacement))
3818  nLabelPlacement = rLabelParam.meDefault;
3819  pFS->singleElement(FSNS(XML_c, XML_dLblPos), XML_val, toOOXMLPlacement(nLabelPlacement));
3820  }
3821  }
3822 
3823  pFS->singleElement(FSNS(XML_c, XML_showLegendKey), XML_val, ToPsz10(aLabel.ShowLegendSymbol));
3824  pFS->singleElement(FSNS(XML_c, XML_showVal), XML_val, ToPsz10(aLabel.ShowNumber));
3825  pFS->singleElement(FSNS(XML_c, XML_showCatName), XML_val, ToPsz10(aLabel.ShowCategoryName));
3826  pFS->singleElement(FSNS(XML_c, XML_showSerName), XML_val, ToPsz10(aLabel.ShowSeriesName));
3827  pFS->singleElement(FSNS(XML_c, XML_showPercent), XML_val, ToPsz10(aLabel.ShowNumberInPercent));
3828 
3829  // Export the text "separator" if exists
3830  uno::Any aAny = xPropSet->getPropertyValue("LabelSeparator");
3831  if( aAny.hasValue() )
3832  {
3833  OUString nLabelSeparator;
3834  aAny >>= nLabelSeparator;
3835  pFS->startElement(FSNS(XML_c, XML_separator));
3836  pFS->writeEscaped( nLabelSeparator );
3837  pFS->endElement( FSNS( XML_c, XML_separator ) );
3838  }
3839 
3840  if (rDLblsRange.hasLabel(nLabelIndex))
3841  {
3842  pFS->startElement(FSNS(XML_c, XML_extLst));
3843  pFS->startElement(FSNS(XML_c, XML_ext), XML_uri,
3844  "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}", FSNS(XML_xmlns, XML_c15),
3845  pChartExport->GetFB()->getNamespaceURL(OOX_NS(c15)));
3846 
3847  pFS->singleElement(FSNS(XML_c15, XML_showDataLabelsRange), XML_val, "1");
3848 
3849  pFS->endElement(FSNS(XML_c, XML_ext));
3850  pFS->endElement(FSNS(XML_c, XML_extLst));
3851  }
3852 }
3853 
3854 }
3855 
3857  const uno::Reference<chart2::XDataSeries> & xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType,
3858  DataLabelsRange& rDLblsRange)
3859 {
3860  if (!xSeries.is() || nSeriesLength <= 0)
3861  return;
3862 
3863  uno::Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY);
3864  if (!xPropSet.is())
3865  return;
3866 
3867  FSHelperPtr pFS = GetFS();
3868  pFS->startElement(FSNS(XML_c, XML_dLbls));
3869 
3870  bool bLinkedNumFmt = true;
3871  if (GetProperty(xPropSet, "LinkNumberFormatToSource"))
3872  mAny >>= bLinkedNumFmt;
3873 
3874  chart2::DataPointLabel aLabel;
3875  bool bLabelIsNumberFormat = true;
3876  if( xPropSet->getPropertyValue("Label") >>= aLabel )
3877  bLabelIsNumberFormat = aLabel.ShowNumber;
3878 
3879  if (GetProperty(xPropSet, bLabelIsNumberFormat ? OUString("NumberFormat") : OUString("PercentageNumberFormat")))
3880  {
3881  sal_Int32 nKey = 0;
3882  mAny >>= nKey;
3883 
3884  OUString aNumberFormatString = getNumberFormatCode(nKey);
3885  OString sNumberFormatString = OUStringToOString(aNumberFormatString, RTL_TEXTENCODING_UTF8);
3886 
3887  pFS->singleElement(FSNS(XML_c, XML_numFmt),
3888  XML_formatCode, sNumberFormatString,
3889  XML_sourceLinked, ToPsz10(bLinkedNumFmt));
3890  }
3891 
3892  uno::Sequence<sal_Int32> aAttrLabelIndices;
3893  xPropSet->getPropertyValue("AttributedDataPoints") >>= aAttrLabelIndices;
3894 
3895  // We must not export label placement property when the chart type doesn't
3896  // support this option in MS Office, else MS Office would think the file
3897  // is corrupt & refuse to open it.
3898 
3899  const chart::TypeGroupInfo& rInfo = chart::GetTypeGroupInfo(static_cast<chart::TypeId>(eChartType));
3900  LabelPlacementParam aParam(!mbIs3DChart, rInfo.mnDefLabelPos);
3901  switch (eChartType) // diagram chart type
3902  {
3903  case chart::TYPEID_PIE:
3905  aParam.mbExport = false;
3906  else
3907  // All pie charts support label placement.
3908  aParam.mbExport = true;
3909  break;
3910  case chart::TYPEID_AREA:
3913  // These chart types don't support label placement.
3914  aParam.mbExport = false;
3915  break;
3916  case chart::TYPEID_BAR:
3917  if (mbStacked || mbPercent)
3918  {
3919  aParam.maAllowedValues.clear();
3920  aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER);
3921  aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE);
3922  aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN);
3923  aParam.meDefault = css::chart::DataLabelPlacement::CENTER;
3924  }
3925  else // Clustered bar chart
3926  {
3927  aParam.maAllowedValues.clear();
3928  aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER);
3929  aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE);
3930  aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::OUTSIDE);
3931  aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN);
3932  aParam.meDefault = css::chart::DataLabelPlacement::OUTSIDE;
3933  }
3934  break;
3935  default:
3936  ;
3937  }
3938 
3939  for (const sal_Int32 nIdx : std::as_const(aAttrLabelIndices))
3940  {
3941  uno::Reference<beans::XPropertySet> xLabelPropSet = xSeries->getDataPointByIndex(nIdx);
3942 
3943  if (!xLabelPropSet.is())
3944  continue;
3945 
3946  pFS->startElement(FSNS(XML_c, XML_dLbl));
3947  pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nIdx));
3948 
3949  // export custom position of data label
3950  if( eChartType != chart::TYPEID_PIE )
3951  {
3952  chart2::RelativePosition aCustomLabelPosition;
3953  if( xLabelPropSet->getPropertyValue("CustomLabelPosition") >>= aCustomLabelPosition )
3954  {
3955  pFS->startElement(FSNS(XML_c, XML_layout));
3956  pFS->startElement(FSNS(XML_c, XML_manualLayout));
3957 
3958  pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(aCustomLabelPosition.Primary));
3959  pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(aCustomLabelPosition.Secondary));
3960 
3961  SAL_WARN_IF(aCustomLabelPosition.Anchor != css::drawing::Alignment_TOP_LEFT, "oox", "unsupported anchor position");
3962 
3963  pFS->endElement(FSNS(XML_c, XML_manualLayout));
3964  pFS->endElement(FSNS(XML_c, XML_layout));
3965  }
3966  }
3967 
3968  if( GetProperty(xLabelPropSet, "LinkNumberFormatToSource") )
3969  mAny >>= bLinkedNumFmt;
3970 
3971  if( xLabelPropSet->getPropertyValue("Label") >>= aLabel )
3972  bLabelIsNumberFormat = aLabel.ShowNumber;
3973  else
3974  bLabelIsNumberFormat = true;
3975 
3976  if (GetProperty(xLabelPropSet, bLabelIsNumberFormat ? OUString("NumberFormat") : OUString("PercentageNumberFormat")))
3977  {
3978  sal_Int32 nKey = 0;
3979  mAny >>= nKey;
3980 
3981  OUString aNumberFormatString = getNumberFormatCode(nKey);
3982  OString sNumberFormatString = OUStringToOString(aNumberFormatString, RTL_TEXTENCODING_UTF8);
3983 
3984  pFS->singleElement(FSNS(XML_c, XML_numFmt), XML_formatCode, sNumberFormatString.getStr(),
3985  XML_sourceLinked, ToPsz10(bLinkedNumFmt));
3986  }
3987 
3988  // Individual label property that overwrites the baseline.
3989  writeLabelProperties(pFS, this, xLabelPropSet, aParam, nIdx, rDLblsRange);
3990  pFS->endElement(FSNS(XML_c, XML_dLbl));
3991  }
3992 
3993  // Baseline label properties for all labels.
3994  writeLabelProperties(pFS, this, xPropSet, aParam, -1, rDLblsRange);
3995 
3996  bool bShowLeaderLines = false;
3997  xPropSet->getPropertyValue("ShowCustomLeaderLines") >>= bShowLeaderLines;
3998  pFS->singleElement(FSNS(XML_c, XML_showLeaderLines), XML_val, ToPsz10(bShowLeaderLines));
3999 
4000  // Export leader line
4001  if( eChartType != chart::TYPEID_PIE )
4002  {
4003  pFS->startElement(FSNS(XML_c, XML_extLst));
4004  pFS->startElement(FSNS(XML_c, XML_ext), XML_uri, "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}", FSNS(XML_xmlns, XML_c15), GetFB()->getNamespaceURL(OOX_NS(c15)));
4005  pFS->singleElement(FSNS(XML_c15, XML_showLeaderLines), XML_val, ToPsz10(bShowLeaderLines));
4006  pFS->endElement(FSNS(XML_c, XML_ext));
4007  pFS->endElement(FSNS(XML_c, XML_extLst));
4008  }
4009  pFS->endElement(FSNS(XML_c, XML_dLbls));
4010 }
4011 
4013  const uno::Reference< beans::XPropertySet > & xSeriesProperties,
4014  sal_Int32 nSeriesLength, sal_Int32 eChartType )
4015 {
4016  uno::Reference< chart2::XDataSeries > xSeries( xSeriesProperties, uno::UNO_QUERY );
4017  bool bVaryColorsByPoint = false;
4018  Sequence< sal_Int32 > aDataPointSeq;
4019  if( xSeriesProperties.is())
4020  {
4021  Any aAny = xSeriesProperties->getPropertyValue( "AttributedDataPoints" );
4022  aAny >>= aDataPointSeq;
4023  xSeriesProperties->getPropertyValue( "VaryColorsByPoint" ) >>= bVaryColorsByPoint;
4024  }
4025 
4026  const sal_Int32 * pPoints = aDataPointSeq.getConstArray();
4027  sal_Int32 nElement;
4028  Reference< chart2::XColorScheme > xColorScheme;
4029  if( mxNewDiagram.is())
4030  xColorScheme.set( mxNewDiagram->getDefaultColorScheme());
4031 
4032  if( bVaryColorsByPoint && xColorScheme.is() )
4033  {
4034  o3tl::sorted_vector< sal_Int32 > aAttrPointSet;
4035  aAttrPointSet.reserve(aDataPointSeq.getLength());
4036  for (auto p = pPoints; p < pPoints + aDataPointSeq.getLength(); ++p)
4037  aAttrPointSet.insert(*p);
4038  const auto aEndIt = aAttrPointSet.end();
4039  for( nElement = 0; nElement < nSeriesLength; ++nElement )
4040  {
4041  uno::Reference< beans::XPropertySet > xPropSet;
4042  if( aAttrPointSet.find( nElement ) != aEndIt )
4043  {
4044  try
4045  {
4047  xSeries, nElement, getModel() );
4048  }
4049  catch( const uno::Exception & )
4050  {
4051  DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
4052  }
4053  }
4054  else
4055  {
4056  // property set only containing the color
4057  xPropSet.set( new ColorPropertySet( ColorTransparency, xColorScheme->getColorByIndex( nElement )));
4058  }
4059 
4060  if( xPropSet.is() )
4061  {
4062  FSHelperPtr pFS = GetFS();
4063  pFS->startElement(FSNS(XML_c, XML_dPt));
4064  pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nElement));
4065 
4066  switch (eChartType)
4067  {
4068  case chart::TYPEID_PIE:
4070  {
4071  if( xPropSet.is() && GetProperty( xPropSet, "SegmentOffset") )
4072  {
4073  sal_Int32 nOffset = 0;
4074  mAny >>= nOffset;
4075  if (nOffset)
4076  pFS->singleElement( FSNS( XML_c, XML_explosion ),
4077  XML_val, OString::number( nOffset ) );
4078  }
4079  break;
4080  }
4081  default:
4082  break;
4083  }
4084  exportShapeProps( xPropSet );
4085 
4086  pFS->endElement( FSNS( XML_c, XML_dPt ) );
4087  }
4088  }
4089  }
4090 
4091  // Export Data Point Property in Charts even if the VaryColors is false
4092  if( bVaryColorsByPoint )
4093  return;
4094 
4095  o3tl::sorted_vector< sal_Int32 > aAttrPointSet;
4096  aAttrPointSet.reserve(aDataPointSeq.getLength());
4097  for (auto p = pPoints; p < pPoints + aDataPointSeq.getLength(); ++p)
4098  aAttrPointSet.insert(*p);
4099  const auto aEndIt = aAttrPointSet.end();
4100  for( nElement = 0; nElement < nSeriesLength; ++nElement )
4101  {
4102  uno::Reference< beans::XPropertySet > xPropSet;
4103  if( aAttrPointSet.find( nElement ) != aEndIt )
4104  {
4105  try
4106  {
4108  xSeries, nElement, getModel() );
4109  }
4110  catch( const uno::Exception & )
4111  {
4112  DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
4113  }
4114  }
4115 
4116  if( xPropSet.is() )
4117  {
4118  FSHelperPtr pFS = GetFS();
4119  pFS->startElement(FSNS(XML_c, XML_dPt));
4120  pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nElement));
4121 
4122  switch( eChartType )
4123  {
4124  case chart::TYPEID_BUBBLE:
4125  case chart::TYPEID_HORBAR:
4126  case chart::TYPEID_BAR:
4127  pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0");
4128  exportShapeProps(xPropSet);
4129  break;
4130 
4131  case chart::TYPEID_LINE:
4132  case chart::TYPEID_SCATTER:
4134  exportMarker(xPropSet);
4135  break;
4136 
4137  default:
4138  exportShapeProps(xPropSet);
4139  break;
4140  }
4141 
4142  pFS->endElement( FSNS( XML_c, XML_dPt ) );
4143  }
4144  }
4145 }
4146 
4147 void ChartExport::exportAxesId(bool bPrimaryAxes, bool bCheckCombinedAxes)
4148 {
4149  sal_Int32 nAxisIdx, nAxisIdy;
4150  bool bPrimaryAxisExists = false;
4151  bool bSecondaryAxisExists = false;
4152  // let's check which axis already exists and which axis is attached to the actual dataseries
4153  if (maAxes.size() >= 2)
4154  {
4155  bPrimaryAxisExists = bPrimaryAxes && maAxes[1].nAxisType == AXIS_PRIMARY_Y;
4156  bSecondaryAxisExists = !bPrimaryAxes && maAxes[1].nAxisType == AXIS_SECONDARY_Y;
4157  }
4158  // tdf#114181 keep axes of combined charts
4159  if ( bCheckCombinedAxes && ( bPrimaryAxisExists || bSecondaryAxisExists ) )
4160  {
4161  nAxisIdx = maAxes[0].nAxisId;
4162  nAxisIdy = maAxes[1].nAxisId;
4163  }
4164  else
4165  {
4166  nAxisIdx = lcl_generateRandomValue();
4167  nAxisIdy = lcl_generateRandomValue();
4168  AxesType eXAxis = bPrimaryAxes ? AXIS_PRIMARY_X : AXIS_SECONDARY_X;
4169  AxesType eYAxis = bPrimaryAxes ? AXIS_PRIMARY_Y : AXIS_SECONDARY_Y;
4170  maAxes.emplace_back( eXAxis, nAxisIdx, nAxisIdy );
4171  maAxes.emplace_back( eYAxis, nAxisIdy, nAxisIdx );
4172  }
4173  FSHelperPtr pFS = GetFS();
4174  pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdx));
4175  pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdy));
4176  if (mbHasZAxis)
4177  {
4178  sal_Int32 nAxisIdz = 0;
4179  if( isDeep3dChart() )
4180  {
4181  nAxisIdz = lcl_generateRandomValue();
4182  maAxes.emplace_back( AXIS_PRIMARY_Z, nAxisIdz, nAxisIdy );
4183  }
4184  pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdz));
4185  }
4186 }
4187 
4189 {
4190  FSHelperPtr pFS = GetFS();
4191  Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
4192  // grouping
4193  if( GetProperty( xPropSet, "Stacked" ) )
4194  mAny >>= mbStacked;
4195  if( GetProperty( xPropSet, "Percent" ) )
4196  mAny >>= mbPercent;
4197 
4198  const char* grouping = nullptr;
4199  if (mbStacked)
4200  grouping = "stacked";
4201  else if (mbPercent)
4202  grouping = "percentStacked";
4203  else
4204  {
4205  if( isBar && !isDeep3dChart() )
4206  {
4207  grouping = "clustered";
4208  }
4209  else
4210  grouping = "standard";
4211  }
4212  pFS->singleElement(FSNS(XML_c, XML_grouping), XML_val, grouping);
4213 }
4214 
4216 {
4217  FSHelperPtr pFS = GetFS();
4218  Reference< chart2::XRegressionCurveContainer > xRegressionCurveContainer( xSeries, UNO_QUERY );
4219  if( !xRegressionCurveContainer.is() )
4220  return;
4221 
4222  const Sequence< Reference< chart2::XRegressionCurve > > aRegCurveSeq = xRegressionCurveContainer->getRegressionCurves();
4223  for( const Reference< chart2::XRegressionCurve >& xRegCurve : aRegCurveSeq )
4224  {
4225  if (!xRegCurve.is())
4226  continue;
4227 
4228  Reference< XPropertySet > xProperties( xRegCurve , uno::UNO_QUERY );
4229 
4230  OUString aService;
4231  Reference< lang::XServiceName > xServiceName( xProperties, UNO_QUERY );
4232  if( !xServiceName.is() )
4233  continue;
4234 
4235  aService = xServiceName->getServiceName();
4236 
4237  if(aService != "com.sun.star.chart2.LinearRegressionCurve" &&
4238  aService != "com.sun.star.chart2.ExponentialRegressionCurve" &&
4239  aService != "com.sun.star.chart2.LogarithmicRegressionCurve" &&
4240  aService != "com.sun.star.chart2.PotentialRegressionCurve" &&
4241  aService != "com.sun.star.chart2.PolynomialRegressionCurve" &&
4242  aService != "com.sun.star.chart2.MovingAverageRegressionCurve")
4243  continue;
4244 
4245  pFS->startElement(FSNS(XML_c, XML_trendline));
4246 
4247  OUString aName;
4248  xProperties->getPropertyValue("CurveName") >>= aName;
4249  if(!aName.isEmpty())
4250  {
4251  pFS->startElement(FSNS(XML_c, XML_name));
4252  pFS->writeEscaped(aName);
4253  pFS->endElement( FSNS( XML_c, XML_name) );
4254  }
4255 
4256  exportShapeProps( xProperties );
4257 
4258  if( aService == "com.sun.star.chart2.LinearRegressionCurve" )
4259  {
4260  pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "linear");
4261  }
4262  else if( aService == "com.sun.star.chart2.ExponentialRegressionCurve" )
4263  {
4264  pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "exp");
4265  }
4266  else if( aService == "com.sun.star.chart2.LogarithmicRegressionCurve" )
4267  {
4268  pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "log");
4269  }
4270  else if( aService == "com.sun.star.chart2.PotentialRegressionCurve" )
4271  {
4272  pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "power");
4273  }
4274  else if( aService == "com.sun.star.chart2.PolynomialRegressionCurve" )
4275  {
4276  pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "poly");
4277 
4278  sal_Int32 aDegree = 2;
4279  xProperties->getPropertyValue( "PolynomialDegree") >>= aDegree;
4280  pFS->singleElement(FSNS(XML_c, XML_order), XML_val, OString::number(aDegree));
4281  }
4282  else if( aService == "com.sun.star.chart2.MovingAverageRegressionCurve" )
4283  {
4284  pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "movingAvg");
4285 
4286  sal_Int32 aPeriod = 2;
4287  xProperties->getPropertyValue( "MovingAveragePeriod") >>= aPeriod;
4288 
4289  pFS->singleElement(FSNS(XML_c, XML_period), XML_val, OString::number(aPeriod));
4290  }
4291  else
4292  {
4293  // should never happen
4294  // This would produce invalid OOXML files so we check earlier for the type
4295  assert(false);
4296  }
4297 
4298  double fExtrapolateForward = 0.0;
4299  double fExtrapolateBackward = 0.0;
4300 
4301  xProperties->getPropertyValue("ExtrapolateForward") >>= fExtrapolateForward;
4302  xProperties->getPropertyValue("ExtrapolateBackward") >>= fExtrapolateBackward;
4303 
4304  pFS->singleElement( FSNS( XML_c, XML_forward ),
4305  XML_val, OString::number(fExtrapolateForward) );
4306 
4307  pFS->singleElement( FSNS( XML_c, XML_backward ),
4308  XML_val, OString::number(fExtrapolateBackward) );
4309 
4310  bool bForceIntercept = false;
4311  xProperties->getPropertyValue("ForceIntercept") >>= bForceIntercept;
4312 
4313  if (bForceIntercept)
4314  {
4315  double fInterceptValue = 0.0;
4316  xProperties->getPropertyValue("InterceptValue") >>= fInterceptValue;
4317 
4318  pFS->singleElement( FSNS( XML_c, XML_intercept ),
4319  XML_val, OString::number(fInterceptValue) );
4320  }
4321 
4322  // Equation properties
4323  Reference< XPropertySet > xEquationProperties( xRegCurve->getEquationProperties() );
4324 
4325  // Show Equation
4326  bool bShowEquation = false;
4327  xEquationProperties->getPropertyValue("ShowEquation") >>= bShowEquation;
4328 
4329  // Show R^2
4330  bool bShowCorrelationCoefficient = false;
4331  xEquationProperties->getPropertyValue("ShowCorrelationCoefficient") >>= bShowCorrelationCoefficient;
4332 
4333  pFS->singleElement( FSNS( XML_c, XML_dispRSqr ),
4334  XML_val, ToPsz10(bShowCorrelationCoefficient) );
4335 
4336  pFS->singleElement(FSNS(XML_c, XML_dispEq), XML_val, ToPsz10(bShowEquation));
4337 
4338  pFS->endElement( FSNS( XML_c, XML_trendline ) );
4339  }
4340 }
4341 
4343 {
4344  chart2::Symbol aSymbol;
4345  if( GetProperty( xPropSet, "Symbol" ) )
4346  mAny >>= aSymbol;
4347 
4348  if(aSymbol.Style != chart2::SymbolStyle_STANDARD && aSymbol.Style != chart2::SymbolStyle_NONE)
4349  return;
4350 
4351  FSHelperPtr pFS = GetFS();
4352  pFS->startElement(FSNS(XML_c, XML_marker));
4353 
4354  sal_Int32 nSymbol = aSymbol.StandardSymbol;
4355  // TODO: more properties support for marker
4356  const char* pSymbolType; // no initialization here, to let compiler warn if we have a code path
4357  // where it stays uninitialized
4358  switch( nSymbol )
4359  {
4360  case 0:
4361  pSymbolType = "square";
4362  break;
4363  case 1:
4364  pSymbolType = "diamond";
4365  break;
4366  case 2:
4367  case 3:
4368  case 4:
4369  case 5:
4370  pSymbolType = "triangle";
4371  break;
4372  case 8:
4373  pSymbolType = "circle";
4374  break;
4375  case 9:
4376  pSymbolType = "star";
4377  break;
4378  case 10:
4379  pSymbolType = "x"; // in MS office 2010 built in symbol marker 'X' is represented as 'x'
4380  break;
4381  case 11:
4382  pSymbolType = "plus";
4383  break;
4384  case 13:
4385  pSymbolType = "dash";
4386  break;
4387  default:
4388  pSymbolType = "square";
4389  break;
4390  }
4391 
4392  bool bSkipFormatting = false;
4393  if (aSymbol.Style == chart2::SymbolStyle_NONE)
4394  {
4395  bSkipFormatting = true;
4396  pSymbolType = "none";
4397  }
4398 
4399  pFS->singleElement(FSNS(XML_c, XML_symbol), XML_val, pSymbolType);
4400 
4401  if (!bSkipFormatting)
4402  {
4403  awt::Size aSymbolSize = aSymbol.Size;
4404  sal_Int32 nSize = std::max( aSymbolSize.Width, aSymbolSize.Height );
4405 
4406  nSize = nSize/250.0*7.0 + 1; // just guessed based on some test cases,
4407  //the value is always 1 less than the actual value.
4408  nSize = std::clamp( int(nSize), 2, 72 );
4409  pFS->singleElement(FSNS(XML_c, XML_size), XML_val, OString::number(nSize));
4410 
4411  pFS->startElement(FSNS(XML_c, XML_spPr));
4412 
4413  util::Color aColor = aSymbol.FillColor;
4414  if (GetProperty(xPropSet, "Color"))
4415  mAny >>= aColor;
4416 
4417  if (aColor == -1)
4418  {
4419  pFS->singleElement(FSNS(XML_a, XML_noFill));
4420  }
4421  else
4423 
4424  pFS->endElement( FSNS( XML_c, XML_spPr ) );
4425  }
4426 
4427  pFS->endElement( FSNS( XML_c, XML_marker ) );
4428 }
4429 
4431 {
4432  FSHelperPtr pFS = GetFS();
4433  Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY );
4434  sal_Int32 nSplineType = 0;
4435  if( GetProperty( xPropSet, "SplineType" ) )
4436  mAny >>= nSplineType;
4437  const char* pVal = nSplineType != 0 ? "1" : "0";
4438  pFS->singleElement(FSNS(XML_c, XML_smooth), XML_val, pVal);
4439 }
4440 
4442 {
4443  FSHelperPtr pFS = GetFS();
4444  sal_Int32 nStartingAngle = 0;
4445  Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
4446  if( GetProperty( xPropSet, "StartingAngle" ) )
4447  mAny >>= nStartingAngle;
4448 
4449  // convert to ooxml angle
4450  nStartingAngle = (450 - nStartingAngle ) % 360;
4451  pFS->singleElement(FSNS(XML_c, XML_firstSliceAng), XML_val, OString::number(nStartingAngle));
4452 }
4453 
4454 namespace {
4455 
4456 const char* getErrorBarStyle(sal_Int32 nErrorBarStyle)
4457 {
4458  switch(nErrorBarStyle)
4459  {
4460  case cssc::ErrorBarStyle::NONE:
4461  return nullptr;
4462  case cssc::ErrorBarStyle::VARIANCE:
4463  break;
4464  case cssc::ErrorBarStyle::STANDARD_DEVIATION:
4465  return "stdDev";
4466  case cssc::ErrorBarStyle::ABSOLUTE:
4467  return "fixedVal";
4468  case cssc::ErrorBarStyle::RELATIVE:
4469  return "percentage";
4470  case cssc::ErrorBarStyle::ERROR_MARGIN:
4471  break;
4472  case cssc::ErrorBarStyle::STANDARD_ERROR:
4473  return "stdErr";
4474  case cssc::ErrorBarStyle::FROM_DATA:
4475  return "cust";
4476  default:
4477  assert(false && "can't happen");
4478  }
4479  return nullptr;
4480 }
4481 
4482 Reference< chart2::data::XDataSequence> getLabeledSequence(
4483  const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > >& aSequences,
4484  bool bPositive )
4485 {
4486  OUString aDirection;
4487  if(bPositive)
4488  aDirection = "positive";
4489  else
4490  aDirection = "negative";
4491 
4492  for( const auto& rSequence : aSequences )
4493  {
4494  if( rSequence.is())
4495  {
4496  uno::Reference< chart2::data::XDataSequence > xSequence( rSequence->getValues());
4497  uno::Reference< beans::XPropertySet > xSeqProp( xSequence, uno::UNO_QUERY_THROW );
4498  OUString aRole;
4499  if( ( xSeqProp->getPropertyValue( "Role" ) >>= aRole ) &&
4500  aRole.match( "error-bars" ) && aRole.indexOf(aDirection) >= 0 )
4501  {
4502  return xSequence;
4503  }
4504  }
4505  }
4506 
4507  return Reference< chart2::data::XDataSequence > ();
4508 }
4509 
4510 }
4511 
4512 void ChartExport::exportErrorBar(const Reference< XPropertySet>& xErrorBarProps, bool bYError)
4513 {
4514  sal_Int32 nErrorBarStyle = cssc::ErrorBarStyle::NONE;
4515  xErrorBarProps->getPropertyValue("ErrorBarStyle") >>= nErrorBarStyle;
4516  const char* pErrorBarStyle = getErrorBarStyle(nErrorBarStyle);
4517  if(!pErrorBarStyle)
4518  return;
4519 
4520  FSHelperPtr pFS = GetFS();
4521  pFS->startElement(FSNS(XML_c, XML_errBars));
4522  pFS->singleElement(FSNS(XML_c, XML_errDir), XML_val, bYError ? "y" : "x");
4523  bool bPositive = false, bNegative = false;
4524  xErrorBarProps->getPropertyValue("ShowPositiveError") >>= bPositive;
4525  xErrorBarProps->getPropertyValue("ShowNegativeError") >>= bNegative;
4526  const char* pErrBarType;
4527  if(bPositive && bNegative)
4528  pErrBarType = "both";
4529  else if(bPositive)
4530  pErrBarType = "plus";
4531  else if(bNegative)
4532  pErrBarType = "minus";
4533  else
4534  {
4535  // what the hell should we do now?
4536  // at least this makes the file valid
4537  pErrBarType = "both";
4538  }
4539  pFS->singleElement(FSNS(XML_c, XML_errBarType), XML_val, pErrBarType);
4540  pFS->singleElement(FSNS(XML_c, XML_errValType), XML_val, pErrorBarStyle);
4541  pFS->singleElement(FSNS(XML_c, XML_noEndCap), XML_val, "0");
4542  if(nErrorBarStyle == cssc::ErrorBarStyle::FROM_DATA)
4543  {
4544  uno::Reference< chart2::data::XDataSource > xDataSource(xErrorBarProps, uno::UNO_QUERY);
4546  xDataSource->getDataSequences();
4547 
4548  if(bPositive)
4549  {
4550  exportSeriesValues(getLabeledSequence(aSequences, true), XML_plus);
4551  }
4552 
4553  if(bNegative)
4554  {
4555  exportSeriesValues(getLabeledSequence(aSequences, false), XML_minus);
4556  }
4557  }
4558  else
4559  {
4560  double nVal = 0.0;
4561  if(nErrorBarStyle == cssc::ErrorBarStyle::STANDARD_DEVIATION)
4562  {
4563  xErrorBarProps->getPropertyValue("Weight") >>= nVal;
4564  }
4565  else
4566  {
4567  if(bPositive)
4568  xErrorBarProps->getPropertyValue("PositiveError") >>= nVal;
4569  else
4570  xErrorBarProps->getPropertyValue("NegativeError") >>= nVal;
4571  }
4572 
4573  pFS->singleElement(FSNS(XML_c, XML_val), XML_val, OString::number(nVal));
4574  }
4575 
4576  exportShapeProps( xErrorBarProps );
4577 
4578  pFS->endElement( FSNS( XML_c, XML_errBars) );
4579 }
4580 
4582 {
4583  Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
4584  if( !xPropSet.is() )
4585  return;
4586  FSHelperPtr pFS = GetFS();
4587  pFS->startElement(FSNS(XML_c, XML_view3D));
4588  sal_Int32 eChartType = getChartType( );
4589  // rotX
4590  if( GetProperty( xPropSet, "RotationHorizontal" ) )
4591  {
4592  sal_Int32 nRotationX = 0;
4593  mAny >>= nRotationX;
4594  if( nRotationX < 0 )
4595  {
4596  if(eChartType == chart::TYPEID_PIE)
4597  {
4598  /* In OOXML we get value in 0..90 range for pie chart X rotation , whereas we expect it to be in -90..90 range,
4599  so we convert that during import. It is modified in View3DConverter::convertFromModel()
4600  here we convert it back to 0..90 as we received in import */
4601  nRotationX += 90; // X rotation (map Chart2 [-179,180] to OOXML [0..90])
4602  }
4603  else
4604  nRotationX += 360; // X rotation (map Chart2 [-179,180] to OOXML [-90..90])
4605  }
4606  pFS->singleElement(FSNS(XML_c, XML_rotX), XML_val, OString::number(nRotationX));
4607  }
4608  // rotY
4609  if( GetProperty( xPropSet, "RotationVertical" ) )
4610  {
4611  // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
4612  if( eChartType == chart::TYPEID_PIE && GetProperty( xPropSet, "StartingAngle" ) )
4613  {
4614  // Y rotation used as 'first pie slice angle' in 3D pie charts
4615  sal_Int32 nStartingAngle=0;
4616  mAny >>= nStartingAngle;
4617  // convert to ooxml angle
4618  nStartingAngle = (450 - nStartingAngle ) % 360;
4619  pFS->singleElement(FSNS(XML_c, XML_rotY), XML_val, OString::number(nStartingAngle));
4620  }
4621  else
4622  {
4623  sal_Int32 nRotationY = 0;
4624  mAny >>= nRotationY;
4625  // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
4626  if( nRotationY < 0 )
4627  nRotationY += 360;
4628  pFS->singleElement(FSNS(XML_c, XML_rotY), XML_val, OString::number(nRotationY));
4629  }
4630  }
4631  // rAngAx
4632  if( GetProperty( xPropSet, "RightAngledAxes" ) )
4633  {
4634  bool bRightAngled = false;
4635  mAny >>= bRightAngled;
4636  const char* sRightAngled = bRightAngled ? "1":"0";
4637  pFS->singleElement(FSNS(XML_c, XML_rAngAx), XML_val, sRightAngled);
4638  }
4639  // perspective
4640  if( GetProperty( xPropSet, "Perspective" ) )
4641  {
4642  sal_Int32 nPerspective = 0;
4643  mAny >>= nPerspective;
4644  // map Chart2 [0,100] to OOXML [0..200]
4645  nPerspective *= 2;
4646  pFS->singleElement(FSNS(XML_c, XML_perspective), XML_val, OString::number(nPerspective));
4647  }
4648  pFS->endElement( FSNS( XML_c, XML_view3D ) );
4649 }
4650 
4652 {
4653  bool isDeep = false;
4654  if( mbIs3DChart )
4655  {
4656  Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
4657  if( GetProperty( xPropSet, "Deep" ) )
4658  mAny >>= isDeep;
4659  }
4660  return isDeep;
4661 }
4662 
4663 OUString ChartExport::getNumberFormatCode(sal_Int32 nKey) const
4664 {
4665  /* XXX if this was called more than one or two times per export the two
4666  * SvNumberFormatter instances and NfKeywordTable should be member
4667  * variables and initialized only once. */
4668 
4669  OUString aCode("General"); // init with fallback
4670  uno::Reference<util::XNumberFormatsSupplier> xNumberFormatsSupplier(mxChartModel, uno::UNO_QUERY_THROW);
4671  SvNumberFormatsSupplierObj* pSupplierObj = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( xNumberFormatsSupplier);
4672  if (!pSupplierObj)
4673  return aCode;
4674 
4675  SvNumberFormatter* pNumberFormatter = pSupplierObj->GetNumberFormatter();
4676  if (!pNumberFormatter)
4677  return aCode;
4678 
4680  NfKeywordTable aKeywords;
4681  aTempFormatter.FillKeywordTableForExcel( aKeywords);
4682  aCode = pNumberFormatter->GetFormatStringForExcel( nKey, aKeywords, aTempFormatter);
4683 
4684  return aCode;
4685 }
4686 
4687 }// oox
4688 
4689 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::chart::XDiagram > mxDiagram
void WritePattFill(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
bool empty() const
Returns whether the container is empty or not.
const char * sName
Definition: olehelper.cxx:92
void _exportAxis(const css::uno::Reference< css::beans::XPropertySet > &xAxisProp, const css::uno::Reference< css::drawing::XShape > &xAxisTitle, const css::uno::Reference< css::beans::XPropertySet > &xMajorGrid, const css::uno::Reference< css::beans::XPropertySet > &xMinorGrid, sal_Int32 nAxisType, const char *sAxisPos, const AxisIdPair &rAxisIdPair)
void exportTextProps(const css::uno::Reference< css::beans::XPropertySet > &xPropSet)
void exportSeriesText(const css::uno::Reference< css::chart2::data::XDataSequence > &xValueSeq)
bool hasValue()
sal_Int32 nIndex
bool bVisible
css::uno::Reference< css::chart2::XDiagram > mxNewDiagram
std::unordered_set< sal_Int32 > maAllowedValues
#define LANGUAGE_ENGLISH_US
void WriteXGraphicBlipFill(css::uno::Reference< css::beans::XPropertySet > const &rXPropSet, css::uno::Reference< css::graphic::XGraphic > const &rxGraphic, sal_Int32 nXmlNamespace, bool bWriteMode, bool bRelPathToMedia=false)
Definition: drawingml.cxx:1555
bool getProperty(Type &orValue, sal_Int32 nPropId) const
Gets the specified property from the property set.
Definition: propertyset.hxx:94
const sal_Int32 PER_PERCENT
void exportBubbleChart(const css::uno::Reference< css::chart2::XChartType > &xChartType)
static css::uno::Reference< css::beans::XPropertySet > createOldAPISeriesPropertySet(const css::uno::Reference< css::chart2::XDataSeries > &xSeries, const css::uno::Reference< css::frame::XModel > &xChartModel)
static sal_Int32 lcl_getChartType(std::u16string_view sChartType)
void WriteShapeTransformation(const css::uno::Reference< css::drawing::XShape > &rXShape, sal_Int32 nXmlNamespace, bool bFlipH=false, bool bFlipV=false, bool bSuppressRotation=false, bool bSuppressFlipping=false, bool bFlippedBeforeRotation=false)
Definition: drawingml.cxx:1773
void exportCandleStickSeries(const css::uno::Sequence< css::uno::Reference< css::chart2::XDataSeries > > &aSeriesSeq, bool &rPrimaryAxes)
void exportSeriesValues(const css::uno::Reference< css::chart2::data::XDataSequence > &xValueSeq, sal_Int32 nValueType=XML_val)
void exportTitle(const css::uno::Reference< css::drawing::XShape > &xShape, const OUString *pSubText=nullptr)
OUString getRange() const
Returns the address of the cell[range] from which label contents are sourced.
static bool lcl_isSeriesAttachedToFirstAxis(const Reference< chart2::XDataSeries > &xDataSeries)
void FillKeywordTableForExcel(NfKeywordTable &rKeywords)
const_iterator find(const Value &x) const
const css::uno::Reference< css::frame::XModel > & getModel() const
::sax_fastparser::FSHelperPtr mpFS
Definition: drawingml.hxx:160
std::shared_ptr< T > make_shared(Args &&...args)
void exportAdditionalShapes(const css::uno::Reference< css::chart::XChartDocument > &rChartDoc)
float x
void exportDataLabels(const css::uno::Reference< css::chart2::XDataSeries > &xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType, DataLabelsRange &rDLblsRange)
static constexpr const char * ToPsz10(bool b)
Definition: utils.hxx:49
static::std::vector< double > lcl_getAllValuesFromSequence(const Reference< chart2::data::XDataSequence > &xSeq)
bool GetProperty(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, const OUString &aName)
Definition: drawingml.cxx:263
std::set< sal_Int32 > maExportedAxis
void exportGrouping(bool isBar=false)
void exportScatterChartSeries(const css::uno::Reference< css::chart2::XChartType > &xChartType, const css::uno::Sequence< css::uno::Reference< css::chart2::XDataSeries >> *pSeries)
Sequence< PropertyValue > aArguments
void WriteSolidFill(::Color nColor, sal_Int32 nAlpha=MAX_PERCENT)
Definition: drawingml.cxx:403
OUString getNamespaceURL(sal_Int32 nNSID) const
void exportPieChart(const css::uno::Reference< css::chart2::XChartType > &xChartType)
void exportRadarChart(const css::uno::Reference< css::chart2::XChartType > &xChartType)
void exportMissingValueTreatment(const css::uno::Reference< css::beans::XPropertySet > &xPropSet)
ShapeExport & WriteShape(const css::uno::Reference< css::drawing::XShape > &xShape)
Write the DrawingML for a particular shape.
Definition: shapes.cxx:1704
void exportHatch(const css::uno::Reference< css::beans::XPropertySet > &xPropSet)
void WriteChartObj(const css::uno::Reference< css::drawing::XShape > &xShape, sal_Int32 nID, sal_Int32 nChartCount)
void SetURLTranslator(const std::shared_ptr< URLTransformer > &pTransformer)
void exportErrorBar(const css::uno::Reference< css::beans::XPropertySet > &xErrorBarProps, bool bYError)
void reserve(size_type amount)
void exportExternalData(const css::uno::Reference< css::chart::XChartDocument > &rChartDoc)
void exportSeries(const css::uno::Reference< css::chart2::XChartType > &xChartType, const css::uno::Sequence< css::uno::Reference< css::chart2::XDataSeries > > &rSeriesSeq, bool &rPrimaryAxes)
sax_fastparser::FSHelperPtr CreateOutputStream(const OUString &sFullStream, std::u16string_view sRelativeStream, const css::uno::Reference< css::io::XOutputStream > &xParentRelation, const char *sContentType, const char *sRelationshipType, OUString *pRelationshipId)
Definition: drawingml.cxx:4101
void exportVaryColors(const css::uno::Reference< css::chart2::XChartType > &xChartType)
static cssc::TimeIncrement lcl_getDateTimeIncrement(const Reference< chart2::XDiagram > &xDiagram, sal_Int32 nAxisIndex)
std::shared_ptr< URLTransformer > mpURLTransformer
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
uint8_t maLabels[mnMaxStaticSize]
OUString getRelationship(Relationship eRelationship)
bool getOutputStream(ProgramOptions const &options, OString const &extension, std::ostream **ppOutputStream, OString &targetSourceFileName, OString &tmpSourceFileName)
const ::sax_fastparser::FSHelperPtr & GetFS() const
Definition: drawingml.hxx:207
float y
void exportAreaChart(const css::uno::Reference< css::chart2::XChartType > &xChartType)
void exportShapeProps(const css::uno::Reference< css::beans::XPropertySet > &xPropSet)
void exportLineChart(const css::uno::Reference< css::chart2::XChartType > &xChartType)
void exportStockChart(const css::uno::Reference< css::chart2::XChartType > &xChartType)
#define DBG_UNHANDLED_EXCEPTION(...)
static Reference< chart2::data::XLabeledDataSequence > lcl_getCategories(const Reference< chart2::XDiagram > &xDiagram, bool &bHasDateCategories)
DocumentType GetDocumentType() const
Definition: drawingml.hxx:209
css::uno::Reference< css::chart2::data::XDataSequence > mxCategoriesValues
TypeId
Enumerates different chart types.
#define TOOLS_WARN_EXCEPTION(area, stream)
Contains info for a chart type related to the OpenOffice.org chart module.
void exportSeriesCategory(const css::uno::Reference< css::chart2::data::XDataSequence > &xValueSeq, sal_Int32 nValueType=XML_cat)
OString calcRotationValue(sal_Int32 nRotation)
nRotation is a 100th of a degree and the return value is in a 60,000th of a degree ...
void WriteGradientFill(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
static sal_Int32 lcl_generateRandomValue()
uno_Any a
static sal_Int32 lcl_getCategoryAxisType(const Reference< chart2::XDiagram > &xDiagram, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex)
css::drawing::Direction3D aDirection
const sal_Int32 MAX_PERCENT
void exportAllSeries(const css::uno::Reference< css::chart2::XChartType > &xChartType, bool &rPrimaryAxes)
static css::uno::Reference< css::beans::XPropertySet > createOldAPIDataPointPropertySet(const css::uno::Reference< css::chart2::XDataSeries > &xSeries, sal_Int32 nPointIndex, const css::uno::Reference< css::frame::XModel > &xChartModel)
OUString GetFormatStringForExcel(sal_uInt32 nKey, const NfKeywordTable &rKeywords, SvNumberFormatter &rTempFormatter) const
size_t count() const
Returns the count of labels stored.
static sal_Int32 getNewDrawingUniqueId()
Definition: drawingml.hxx:341
OUString m_aRole
const TypeGroupInfo & GetTypeGroupInfo(TypeId eType)
sal_Int64 convertHmmToEmu(sal_Int32 nValue)
Converts the passed 32-bit integer value from 1/100 mm to EMUs.
float u
Pie-to-pie or pie-to-bar chart.
sal_Int32 w
void WriteRunProperties(const css::uno::Reference< css::beans::XPropertySet > &rRun, bool bIsField, sal_Int32 nElement, bool bCheckDirect, bool &rbOverridingCharHeight, sal_Int32 &rnCharHeight, sal_Int16 nScriptType=css::i18n::ScriptType::LATIN, const css::uno::Reference< css::beans::XPropertySet > &rXShapePropSet={})
Definition: drawingml.cxx:1893
OUString parseFormula(const OUString &rRange)
ColorTransparency
const_iterator end() const
void exportBarChart(const css::uno::Reference< css::chart2::XChartType > &xChartType)
A wrapper for a UNO property set.
Definition: propertyset.hxx:57
std::shared_ptr< FastSerializerHelper > FSHelperPtr
void exportChartSpace(const css::uno::Reference< css::chart::XChartDocument > &rChartDoc, bool bIncludeTable)
enumrange< T >::Iterator end(enumrange< T >)
A helper container class to collect the chart data point labels and the address of the cell[range] fr...
constexpr T & temporary(T &&x)
void exportTrendlines(const css::uno::Reference< css::chart2::XDataSeries > &xSeries)
const sal_uInt16 idx[]
void WriteOutline(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, css::uno::Reference< css::frame::XModel > const &xModel=nullptr)
Definition: drawingml.cxx:818
sal_Int32 mnDefLabelPos
Mode for varying point colors.
void exportChart(const css::uno::Reference< css::chart::XChartDocument > &rChartDoc)
void exportBitmapFill(const css::uno::Reference< css::beans::XPropertySet > &xPropSet)
std::unique_ptr< uint8_t[]> pLabels
void exportLegend(const css::uno::Reference< css::chart::XChartDocument > &rChartDoc)
void exportFill(const css::uno::Reference< css::beans::XPropertySet > &xPropSet)
#define TOOLS_INFO_EXCEPTION(area, stream)
void exportDataPoints(const css::uno::Reference< css::beans::XPropertySet > &xSeriesProperties, sal_Int32 nSeriesLength, sal_Int32 eChartType)
const css::uno::Reference< css::lang::XMultiServiceFactory > & getModelFactory() const
Returns the service factory provided by the document model (always existing).
Definition: filterbase.cxx:225
bool mbExport
bool getBoolProperty(sal_Int32 nPropId) const
Gets the specified boolean property from the property set.
Definition: propertyset.hxx:99
static void lcl_fillCategoriesIntoStringVector(const Reference< chart2::data::XDataSequence > &xCategories,::std::vector< OUString > &rOutCategories)
css::uno::Reference< css::frame::XModel > mxChartModel
css::uno::Sequence< css::uno::Sequence< rtl::OUString > > getSplitCategoriesList(const OUString &rRange)
static bool lcl_isCategoryAxisShifted(const Reference< chart2::XDiagram > &xDiagram)
void exportAxis(const AxisIdPair &rAxisIdPair)
static OUString lclGetTimeUnitToken(sal_Int32 nTimeUnit)
static sal_Int32 lcl_getAlphaFromTransparenceGradient(const awt::Gradient &rGradient, bool bStart)
static Reference< chart2::data::XLabeledDataSequence > lcl_getDataSequenceByRole(const Sequence< Reference< chart2::data::XLabeledDataSequence > > &aLabeledSeq, const OUString &rRole)
void exportUpDownBars(const css::uno::Reference< css::chart2::XChartType > &xChartType)
void exportSurfaceChart(const css::uno::Reference< css::chart2::XChartType > &xChartType)
#define SAL_WARN_IF(condition, area, stream)
unsigned char sal_uInt8
orientation
void exportSolidFill(const css::uno::Reference< css::beans::XPropertySet > &xPropSet)
void exportScatterChart(const css::uno::Reference< css::chart2::XChartType > &xChartType)
static void WriteFromTo(const css::uno::Reference< css::drawing::XShape > &rXShape, const css::awt::Size &aPageSize, const sax_fastparser::FSHelperPtr &pDrawing)
Definition: drawingml.cxx:5312
void SetFS(::sax_fastparser::FSHelperPtr pFS)
Definition: drawingml.hxx:206
OUString aName
::oox::core::XmlFilterBase * GetFB()
Definition: drawingml.hxx:208
if(aStr!=aBuf) UpdateName_Impl(m_xFollowLb.get()
int uniform_int_distribution(int a, int b)
void exportDoughnutChart(const css::uno::Reference< css::chart2::XChartType > &xChartType)
SvNumberFormatter * GetNumberFormatter() const
OUString aLabel
void * p
Reference< XComponentContext > getProcessComponentContext()
double getLength(const B2DPolygon &rCandidate)
void exportPlotArea(const css::uno::Reference< css::chart::XChartDocument > &rChartDoc)
constexpr sal_Int32 FSNS(sal_Int32 namespc, sal_Int32 element)
void exportMarker(const css::uno::Reference< css::beans::XPropertySet > &xPropSet)
sal_Int32 meDefault
void exportGradientFill(const css::uno::Reference< css::beans::XPropertySet > &xPropSet)
::oox::core::XmlFilterBase * mpFB
Definition: drawingml.hxx:161
ResultType type
static OUString lcl_flattenStringSequence(const Sequence< OUString > &rSequence)
#define SAL_WARN(area, stream)
static Sequence< OUString > lcl_getLabelSequence(const Reference< chart2::data::XDataSequence > &xLabelSeq)
sal_Int32 nLength
void exportManualLayout(const css::chart2::RelativePosition &rPos, const css::chart2::RelativeSize &rSize, const bool bIsExcludingDiagramPositioning)
std::pair< const_iterator, bool > insert(Value &&x)
void exportAxesId(bool bPrimaryAxes, bool bCheckCombinedAxes=false)
static bool lcl_hasCategoryLabels(const Reference< chart2::XChartDocument > &xChartDoc)
sal_Int32 h
OUString addRelation(const OUString &rType, std::u16string_view rTarget)
Adds new relation.
aStr
OUString getNumberFormatCode(sal_Int32 nKey) const
B2DRange maRange
sal_Int16 nValue
void InitRangeSegmentationProperties(const css::uno::Reference< css::chart2::XChartDocument > &xChartDoc)
OUString sId