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