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>
23 #include <oox/core/xmlfilterbase.hxx>
24 #include <oox/export/chartexport.hxx>
25 #include <oox/token/relationship.hxx>
26 #include <oox/export/utils.hxx>
27 #include <drawingml/chart/typegroupconverter.hxx>
28 #include <basegfx/utils/gradienttools.hxx>
29 #include <docmodel/uno/UnoGradientTools.hxx>
30
31 #include <cstdio>
32 #include <limits>
33
34 #include <com/sun/star/awt/Gradient2.hpp>
35 #include <com/sun/star/chart/XChartDocument.hpp>
36 #include <com/sun/star/chart/ChartLegendPosition.hpp>
37 #include <com/sun/star/chart/XTwoAxisXSupplier.hpp>
38 #include <com/sun/star/chart/XTwoAxisYSupplier.hpp>
39 #include <com/sun/star/chart/XAxisZSupplier.hpp>
40 #include <com/sun/star/chart/ChartDataRowSource.hpp>
41 #include <com/sun/star/chart/X3DDisplay.hpp>
42 #include <com/sun/star/chart/XStatisticDisplay.hpp>
43 #include <com/sun/star/chart/XSecondAxisTitleSupplier.hpp>
44 #include <com/sun/star/chart/ChartSymbolType.hpp>
45 #include <com/sun/star/chart/ChartAxisMarks.hpp>
46 #include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
47 #include <com/sun/star/chart/ChartAxisPosition.hpp>
48 #include <com/sun/star/chart/ChartSolidType.hpp>
49 #include <com/sun/star/chart/DataLabelPlacement.hpp>
50 #include <com/sun/star/chart/ErrorBarStyle.hpp>
51 #include <com/sun/star/chart/MissingValueTreatment.hpp>
52 #include <com/sun/star/chart/XDiagramPositioning.hpp>
53 #include <com/sun/star/chart/TimeIncrement.hpp>
54 #include <com/sun/star/chart/TimeInterval.hpp>
55 #include <com/sun/star/chart/TimeUnit.hpp>
56
57 #include <com/sun/star/chart2/RelativePosition.hpp>
58 #include <com/sun/star/chart2/RelativeSize.hpp>
59 #include <com/sun/star/chart2/XChartDocument.hpp>
60 #include <com/sun/star/chart2/XDiagram.hpp>
61 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
62 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
63 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
64 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
65 #include <com/sun/star/chart2/DataPointLabel.hpp>
66 #include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
67 #include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
68 #include <com/sun/star/chart2/PieChartSubType.hpp>
69 #include <com/sun/star/chart2/Symbol.hpp>
70 #include <com/sun/star/chart2/data/XDataSource.hpp>
71 #include <com/sun/star/chart2/data/XDataProvider.hpp>
72 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
73 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
74 #include <com/sun/star/chart2/data/XLabeledDataSequence.hpp>
75 #include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
76 #include <com/sun/star/chart2/AxisType.hpp>
77
78 #include <com/sun/star/beans/XPropertySet.hpp>
79 #include <com/sun/star/container/XNameAccess.hpp>
80 #include <com/sun/star/drawing/XShape.hpp>
81 #include <com/sun/star/drawing/XShapes.hpp>
82 #include <com/sun/star/drawing/FillStyle.hpp>
83 #include <com/sun/star/drawing/LineStyle.hpp>
84 #include <com/sun/star/awt/XBitmap.hpp>
85 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
86 #include <com/sun/star/lang/XServiceName.hpp>
87
88 #include <com/sun/star/table/CellAddress.hpp>
89 #include <com/sun/star/sheet/XFormulaParser.hpp>
90 #include <com/sun/star/sheet/FormulaToken.hpp>
91 #include <com/sun/star/sheet/AddressConvention.hpp>
92
93 #include <com/sun/star/container/XNamed.hpp>
94 #include <com/sun/star/embed/XVisualObject.hpp>
95 #include <com/sun/star/embed/Aspects.hpp>
96
97 #include <comphelper/processfactory.hxx>
98 #include <comphelper/random.hxx>
99 #include <utility>
100 #include <xmloff/SchXMLSeriesHelper.hxx>
101 #include "ColorPropertySet.hxx"
102
103 #include <svl/numformat.hxx>
104 #include <svl/numuno.hxx>
105 #include <comphelper/diagnose_ex.hxx>
106 #include <sal/log.hxx>
107
108 #include <set>
109 #include <unordered_set>
110
111 #include <frozen/bits/defines.h>
112 #include <frozen/bits/elsa_std.h>
113 #include <frozen/unordered_map.h>
114
115 #include <o3tl/temporary.hxx>
116 #include <o3tl/sorted_vector.hxx>
117
118 using namespace css;
119 using namespace css::uno;
120 using namespace css::drawing;
121 using namespace ::oox::core;
122 using css::beans::PropertyValue;
123 using css::beans::XPropertySet;
124 using css::container::XNamed;
125 using css::table::CellAddress;
126 using css::sheet::XFormulaParser;
127 using ::oox::core::XmlFilterBase;
128 using ::sax_fastparser::FSHelperPtr;
129
130 namespace cssc = css::chart;
131
132 namespace oox::drawingml {
133
134 namespace {
135
isPrimaryAxes(sal_Int32 nIndex)136 bool isPrimaryAxes(sal_Int32 nIndex)
137 {
138 assert(nIndex == 0 || nIndex == 1);
139 return nIndex != 1;
140 }
141
142 class lcl_MatchesRole
143 {
144 public:
lcl_MatchesRole(OUString aRole)145 explicit lcl_MatchesRole( OUString aRole ) :
146 m_aRole(std::move( aRole ))
147 {}
148
operator ()(const Reference<chart2::data::XLabeledDataSequence> & xSeq) const149 bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const
150 {
151 if( !xSeq.is() )
152 return false;
153 Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY );
154 OUString aRole;
155
156 return ( xProp.is() &&
157 (xProp->getPropertyValue( u"Role"_ustr ) >>= aRole ) &&
158 m_aRole == aRole );
159 }
160
161 private:
162 OUString m_aRole;
163 };
164
outputStyleEntry(FSHelperPtr pFS,sal_Int32 nElTokenId)165 void outputStyleEntry(FSHelperPtr pFS, sal_Int32 nElTokenId)
166 {
167 // Just default values for now
168 pFS->startElement(FSNS(XML_cs, nElTokenId));
169 pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0");
170 pFS->singleElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0");
171 pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0");
172 pFS->singleElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor");
173 pFS->endElement(FSNS(XML_cs, nElTokenId));
174 }
175
outputChartAreaStyleEntry(FSHelperPtr pFS)176 void outputChartAreaStyleEntry(FSHelperPtr pFS)
177 {
178 // Just default values for now
179 pFS->startElement(FSNS(XML_cs, XML_chartArea), XML_mods, "allowNoFillOverride allowNoLineOverride");
180 pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0");
181 pFS->singleElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0");
182 pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0");
183
184 pFS->startElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor");
185 pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "tx1");
186 pFS->endElement(FSNS(XML_cs, XML_fontRef));
187
188 pFS->startElement(FSNS(XML_cs, XML_spPr));
189
190 pFS->startElement(FSNS(XML_a, XML_solidFill));
191 pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "bg1");
192 pFS->endElement(FSNS(XML_a, XML_solidFill));
193
194 pFS->startElement(FSNS(XML_a, XML_ln), XML_w, "9525", XML_cap, "flat",
195 XML_cmpd, "sng", XML_algn, "ctr");
196 pFS->startElement(FSNS(XML_a, XML_solidFill));
197 pFS->startElement(FSNS(XML_a, XML_schemeClr), XML_val, "tx1");
198 pFS->singleElement(FSNS(XML_a, XML_lumMod), XML_val, "15000");
199 pFS->singleElement(FSNS(XML_a, XML_lumOff), XML_val, "85000");
200 pFS->endElement(FSNS(XML_a, XML_schemeClr));
201 pFS->endElement(FSNS(XML_a, XML_solidFill));
202 pFS->singleElement(FSNS(XML_a, XML_round));
203 pFS->endElement(FSNS(XML_a, XML_ln));
204
205 pFS->endElement(FSNS(XML_cs, XML_spPr));
206
207 pFS->endElement(FSNS(XML_cs, XML_chartArea));
208 }
209
outputDataPointStyleEntry(FSHelperPtr pFS)210 void outputDataPointStyleEntry(FSHelperPtr pFS)
211 {
212 pFS->startElement(FSNS(XML_cs, XML_dataPoint));
213 pFS->singleElement(FSNS(XML_cs, XML_lnRef), XML_idx, "0");
214
215 pFS->startElement(FSNS(XML_cs, XML_fillRef), XML_idx, "0");
216 pFS->singleElement(FSNS(XML_cs, XML_styleClr), XML_val, "auto");
217 pFS->endElement(FSNS(XML_cs, XML_fillRef));
218
219 pFS->singleElement(FSNS(XML_cs, XML_effectRef), XML_idx, "0");
220
221 pFS->startElement(FSNS(XML_cs, XML_fontRef), XML_idx, "minor");
222 pFS->singleElement(FSNS(XML_cs, XML_schemeClr), XML_val, "tx1");
223 pFS->endElement(FSNS(XML_cs, XML_fontRef));
224
225 pFS->startElement(FSNS(XML_cs, XML_spPr));
226 pFS->startElement(FSNS(XML_a, XML_solidFill));
227 pFS->singleElement(FSNS(XML_a, XML_schemeClr), XML_val, "phClr");
228 pFS->endElement(FSNS(XML_a, XML_solidFill));
229 pFS->endElement(FSNS(XML_cs, XML_spPr));
230
231 pFS->endElement(FSNS(XML_cs, XML_dataPoint));
232 }
233
splitDataSeriesByAxis(const Reference<chart2::XChartType> & xChartType)234 std::vector<Sequence<Reference<chart2::XDataSeries> > > splitDataSeriesByAxis(const Reference< chart2::XChartType >& xChartType)
235 {
236 std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitSeries;
237 std::map<sal_Int32, size_t> aMapAxisToIndex;
238
239 Reference< chart2::XDataSeriesContainer > xDSCnt(xChartType, uno::UNO_QUERY);
240 if (xDSCnt.is())
241 {
242 sal_Int32 nAxisIndexOfFirstSeries = -1;
243 const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq(xDSCnt->getDataSeries());
244 for (const uno::Reference<chart2::XDataSeries>& xSeries : aSeriesSeq)
245 {
246 Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY);
247 if (!xPropSet.is())
248 continue;
249
250 sal_Int32 nAxisIndex = -1;
251 uno::Any aAny = xPropSet->getPropertyValue(u"AttachedAxisIndex"_ustr);
252 aAny >>= nAxisIndex;
253 size_t nVectorPos = 0;
254 if (nAxisIndexOfFirstSeries == -1)
255 {
256 nAxisIndexOfFirstSeries = nAxisIndex;
257 }
258
259 auto it = aMapAxisToIndex.find(nAxisIndex);
260 if (it == aMapAxisToIndex.end())
261 {
262 aSplitSeries.emplace_back();
263 nVectorPos = aSplitSeries.size() - 1;
264 aMapAxisToIndex.insert(std::pair<sal_Int32, size_t>(nAxisIndex, nVectorPos));
265 }
266 else
267 {
268 nVectorPos = it->second;
269 }
270
271 uno::Sequence<Reference<chart2::XDataSeries> >& rAxisSeriesSeq = aSplitSeries[nVectorPos];
272 sal_Int32 nLength = rAxisSeriesSeq.getLength();
273 rAxisSeriesSeq.realloc(nLength + 1);
274 rAxisSeriesSeq.getArray()[nLength] = xSeries;
275 }
276 // if the first series attached to secondary axis, then export those series first, which are attached to primary axis
277 // also the MS Office export every time in this order
278 if (aSplitSeries.size() > 1 && nAxisIndexOfFirstSeries == 1)
279 {
280 std::swap(aSplitSeries[0], aSplitSeries[1]);
281 }
282 }
283
284 return aSplitSeries;
285 }
286
287 } // unnamed namespace
288
lcl_getCategories(const Reference<chart2::XDiagram> & xDiagram,bool * pbHasDateCategories)289 static Reference< chart2::data::XLabeledDataSequence > lcl_getCategories(
290 const Reference< chart2::XDiagram > & xDiagram, bool *pbHasDateCategories )
291 {
292 *pbHasDateCategories = false;
293 Reference< chart2::data::XLabeledDataSequence > xResult;
294 try
295 {
296 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
297 xDiagram, uno::UNO_QUERY_THROW );
298 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
299 xCooSysCnt->getCoordinateSystems());
300 for( const auto& xCooSys : aCooSysSeq )
301 {
302 OSL_ASSERT( xCooSys.is());
303 for( sal_Int32 nN = xCooSys->getDimension(); nN--; )
304 {
305 const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nN);
306 for(sal_Int32 nI=0; nI<=nMaxAxisIndex; ++nI)
307 {
308 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( nN, nI );
309 OSL_ASSERT( xAxis.is());
310 if( xAxis.is())
311 {
312 chart2::ScaleData aScaleData = xAxis->getScaleData();
313 if( aScaleData.Categories.is())
314 {
315 *pbHasDateCategories = aScaleData.AxisType == chart2::AxisType::DATE;
316 xResult.set( aScaleData.Categories );
317 break;
318 }
319 }
320 }
321 }
322 }
323 }
324 catch( const uno::Exception & )
325 {
326 DBG_UNHANDLED_EXCEPTION("oox");
327 }
328
329 return xResult;
330 }
331
332 static Reference< chart2::data::XLabeledDataSequence >
lcl_getDataSequenceByRole(const Sequence<Reference<chart2::data::XLabeledDataSequence>> & aLabeledSeq,const OUString & rRole)333 lcl_getDataSequenceByRole(
334 const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aLabeledSeq,
335 const OUString & rRole )
336 {
337 Reference< chart2::data::XLabeledDataSequence > aNoResult;
338
339 const Reference< chart2::data::XLabeledDataSequence > * pBegin = aLabeledSeq.getConstArray();
340 const Reference< chart2::data::XLabeledDataSequence > * pEnd = pBegin + aLabeledSeq.getLength();
341 const Reference< chart2::data::XLabeledDataSequence > * pMatch =
342 ::std::find_if( pBegin, pEnd, lcl_MatchesRole( rRole ));
343
344 if( pMatch != pEnd )
345 return *pMatch;
346
347 return aNoResult;
348 }
349
lcl_hasCategoryLabels(const Reference<chart2::XChartDocument> & xChartDoc)350 static bool lcl_hasCategoryLabels( const Reference< chart2::XChartDocument >& xChartDoc )
351 {
352 //categories are always the first sequence
353 Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram());
354 bool bDateCategories;
355 Reference< chart2::data::XLabeledDataSequence > xCategories(
356 lcl_getCategories( xDiagram, &bDateCategories ) );
357 return xCategories.is();
358 }
359
lcl_isCategoryAxisShifted(const Reference<chart2::XDiagram> & xDiagram)360 static bool lcl_isCategoryAxisShifted( const Reference< chart2::XDiagram >& xDiagram )
361 {
362 bool bCategoryPositionShifted = false;
363 try
364 {
365 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
366 xDiagram, uno::UNO_QUERY_THROW);
367 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
368 xCooSysCnt->getCoordinateSystems());
369 for (const auto& xCooSys : aCooSysSeq)
370 {
371 OSL_ASSERT(xCooSys.is());
372 if( 0 < xCooSys->getDimension() && 0 <= xCooSys->getMaximumAxisIndexByDimension(0) )
373 {
374 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(0, 0);
375 OSL_ASSERT(xAxis.is());
376 if (xAxis.is())
377 {
378 chart2::ScaleData aScaleData = xAxis->getScaleData();
379 bCategoryPositionShifted = aScaleData.ShiftedCategoryPosition;
380 break;
381 }
382 }
383 }
384 }
385 catch (const uno::Exception&)
386 {
387 DBG_UNHANDLED_EXCEPTION("oox");
388 }
389
390 return bCategoryPositionShifted;
391 }
392
lcl_getCategoryAxisType(const Reference<chart2::XDiagram> & xDiagram,sal_Int32 nDimensionIndex,sal_Int32 nAxisIndex)393 static sal_Int32 lcl_getCategoryAxisType( const Reference< chart2::XDiagram >& xDiagram, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex )
394 {
395 sal_Int32 nAxisType = -1;
396 try
397 {
398 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
399 xDiagram, uno::UNO_QUERY_THROW);
400 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
401 xCooSysCnt->getCoordinateSystems());
402 for( const auto& xCooSys : aCooSysSeq )
403 {
404 OSL_ASSERT(xCooSys.is());
405 if( nDimensionIndex < xCooSys->getDimension() && nAxisIndex <= xCooSys->getMaximumAxisIndexByDimension(nDimensionIndex) )
406 {
407 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(nDimensionIndex, nAxisIndex);
408 OSL_ASSERT(xAxis.is());
409 if( xAxis.is() )
410 {
411 chart2::ScaleData aScaleData = xAxis->getScaleData();
412 nAxisType = aScaleData.AxisType;
413 break;
414 }
415 }
416 }
417 }
418 catch (const uno::Exception&)
419 {
420 DBG_UNHANDLED_EXCEPTION("oox");
421 }
422
423 return nAxisType;
424 }
425
lclGetTimeUnitToken(sal_Int32 nTimeUnit)426 static OUString lclGetTimeUnitToken( sal_Int32 nTimeUnit )
427 {
428 switch( nTimeUnit )
429 {
430 case cssc::TimeUnit::DAY: return u"days"_ustr;
431 case cssc::TimeUnit::MONTH: return u"months"_ustr;
432 case cssc::TimeUnit::YEAR: return u"years"_ustr;
433 default: OSL_ENSURE(false, "lclGetTimeUnitToken - unexpected time unit");
434 }
435 return u"days"_ustr;
436 }
437
lcl_getDateTimeIncrement(const Reference<chart2::XDiagram> & xDiagram,sal_Int32 nAxisIndex)438 static cssc::TimeIncrement lcl_getDateTimeIncrement( const Reference< chart2::XDiagram >& xDiagram, sal_Int32 nAxisIndex )
439 {
440 cssc::TimeIncrement aTimeIncrement;
441 try
442 {
443 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
444 xDiagram, uno::UNO_QUERY_THROW);
445 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
446 xCooSysCnt->getCoordinateSystems());
447 for( const auto& xCooSys : aCooSysSeq )
448 {
449 OSL_ASSERT(xCooSys.is());
450 if( 0 < xCooSys->getDimension() && nAxisIndex <= xCooSys->getMaximumAxisIndexByDimension(0) )
451 {
452 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension(0, nAxisIndex);
453 OSL_ASSERT(xAxis.is());
454 if( xAxis.is() )
455 {
456 chart2::ScaleData aScaleData = xAxis->getScaleData();
457 aTimeIncrement = aScaleData.TimeIncrement;
458 break;
459 }
460 }
461 }
462 }
463 catch (const uno::Exception&)
464 {
465 DBG_UNHANDLED_EXCEPTION("oox");
466 }
467
468 return aTimeIncrement;
469 }
470
lcl_isSeriesAttachedToFirstAxis(const Reference<chart2::XDataSeries> & xDataSeries)471 static bool lcl_isSeriesAttachedToFirstAxis(
472 const Reference< chart2::XDataSeries > & xDataSeries )
473 {
474 bool bResult=true;
475
476 try
477 {
478 sal_Int32 nAxisIndex = 0;
479 Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW );
480 xProp->getPropertyValue(u"AttachedAxisIndex"_ustr) >>= nAxisIndex;
481 bResult = (0==nAxisIndex);
482 }
483 catch( const uno::Exception & )
484 {
485 DBG_UNHANDLED_EXCEPTION("oox");
486 }
487
488 return bResult;
489 }
490
lcl_flattenStringSequence(const Sequence<OUString> & rSequence)491 static OUString lcl_flattenStringSequence( const Sequence< OUString > & rSequence )
492 {
493 OUStringBuffer aResult;
494 bool bPrecedeWithSpace = false;
495 for( const auto& rString : rSequence )
496 {
497 if( !rString.isEmpty())
498 {
499 if( bPrecedeWithSpace )
500 aResult.append( ' ' );
501 aResult.append( rString );
502 bPrecedeWithSpace = true;
503 }
504 }
505 return aResult.makeStringAndClear();
506 }
507
lcl_writeChartexString(const FSHelperPtr & pFS,std::u16string_view sOut)508 static void lcl_writeChartexString(const FSHelperPtr& pFS, std::u16string_view sOut)
509 {
510 pFS->startElement(FSNS(XML_cx, XML_tx));
511 // cell range doesn't seem to be supported in chartex?
512 // TODO: also handle <cx:rich>
513 pFS->startElement(FSNS(XML_cx, XML_txData));
514 // TODO: also handle <cx:f> <cx:v>
515 pFS->startElement(FSNS(XML_cx, XML_v));
516 pFS->writeEscaped(sOut);
517 pFS->endElement( FSNS( XML_cx, XML_v ) );
518 pFS->endElement( FSNS( XML_cx, XML_txData ) );
519 pFS->endElement( FSNS( XML_cx, XML_tx ) );
520 }
521
lcl_getLabelSequence(const Reference<chart2::data::XDataSequence> & xLabelSeq)522 static Sequence< OUString > lcl_getLabelSequence( const Reference< chart2::data::XDataSequence > & xLabelSeq )
523 {
524 Sequence< OUString > aLabels;
525
526 uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xLabelSeq, uno::UNO_QUERY );
527 if( xTextualDataSequence.is())
528 {
529 aLabels = xTextualDataSequence->getTextualData();
530 }
531 else if( xLabelSeq.is())
532 {
533 const Sequence< uno::Any > aAnies( xLabelSeq->getData());
534 aLabels.realloc( aAnies.getLength());
535 auto pLabels = aLabels.getArray();
536 for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
537 aAnies[i] >>= pLabels[i];
538 }
539
540 return aLabels;
541 }
542
lcl_fillCategoriesIntoStringVector(const Reference<chart2::data::XDataSequence> & xCategories,::std::vector<OUString> & rOutCategories)543 static void lcl_fillCategoriesIntoStringVector(
544 const Reference< chart2::data::XDataSequence > & xCategories,
545 ::std::vector< OUString > & rOutCategories )
546 {
547 OSL_ASSERT( xCategories.is());
548 if( !xCategories.is())
549 return;
550 Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xCategories, uno::UNO_QUERY );
551 if( xTextualDataSequence.is())
552 {
553 rOutCategories.clear();
554 const Sequence< OUString > aTextData( xTextualDataSequence->getTextualData());
555 rOutCategories.insert( rOutCategories.end(), aTextData.begin(), aTextData.end() );
556 }
557 else
558 {
559 Sequence< uno::Any > aAnies( xCategories->getData());
560 rOutCategories.resize( aAnies.getLength());
561 for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
562 aAnies[i] >>= rOutCategories[i];
563 }
564 }
565
lcl_getAllValuesFromSequence(const Reference<chart2::data::XDataSequence> & xSeq)566 static ::std::vector< double > lcl_getAllValuesFromSequence( const Reference< chart2::data::XDataSequence > & xSeq )
567 {
568 ::std::vector< double > aResult;
569
570 Reference< chart2::data::XNumericalDataSequence > xNumSeq( xSeq, uno::UNO_QUERY );
571 if( xNumSeq.is())
572 {
573 const Sequence< double > aValues( xNumSeq->getNumericalData());
574 aResult.insert( aResult.end(), aValues.begin(), aValues.end() );
575 }
576 else if( xSeq.is())
577 {
578 Sequence< uno::Any > aAnies( xSeq->getData());
579 aResult.resize( aAnies.getLength(), std::numeric_limits<double>::quiet_NaN() );
580 for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
581 aAnies[i] >>= aResult[i];
582 }
583 return aResult;
584 }
585
586 namespace
587 {
588
589 constexpr auto constChartTypeMap = frozen::make_unordered_map<std::u16string_view, chart::TypeId>(
590 {
591 { u"com.sun.star.chart.BarDiagram", chart::TYPEID_BAR },
592 { u"com.sun.star.chart2.ColumnChartType", chart::TYPEID_BAR },
593 { u"com.sun.star.chart.AreaDiagram", chart::TYPEID_AREA },
594 { u"com.sun.star.chart2.AreaChartType", chart::TYPEID_AREA },
595 { u"com.sun.star.chart.LineDiagram", chart::TYPEID_LINE },
596 { u"com.sun.star.chart2.LineChartType", chart::TYPEID_LINE },
597 { u"com.sun.star.chart.PieDiagram", chart::TYPEID_PIE },
598 { u"com.sun.star.chart2.PieChartType", chart::TYPEID_PIE },
599 { u"com.sun.star.chart.DonutDiagram", chart::TYPEID_DOUGHNUT },
600 { u"com.sun.star.chart2.DonutChartType", chart::TYPEID_DOUGHNUT },
601 { u"com.sun.star.chart.XYDiagram", chart::TYPEID_SCATTER },
602 { u"com.sun.star.chart2.ScatterChartType", chart::TYPEID_SCATTER },
603 { u"com.sun.star.chart.NetDiagram", chart::TYPEID_RADARLINE },
604 { u"com.sun.star.chart2.NetChartType", chart::TYPEID_RADARLINE },
605 { u"com.sun.star.chart.FilledNetDiagram", chart::TYPEID_RADARAREA },
606 { u"com.sun.star.chart2.FilledNetChartType", chart::TYPEID_RADARAREA },
607 { u"com.sun.star.chart.StockDiagram", chart::TYPEID_STOCK },
608 { u"com.sun.star.chart2.CandleStickChartType", chart::TYPEID_STOCK },
609 { u"com.sun.star.chart.BubbleDiagram", chart::TYPEID_BUBBLE },
610 { u"com.sun.star.chart2.BubbleChartType", chart::TYPEID_BUBBLE },
611 { u"com.sun.star.chart.FunnelDiagram", chart::TYPEID_FUNNEL },
612 { u"com.sun.star.chart2.FunnelChartType", chart::TYPEID_FUNNEL },
613 });
614
615 } // end anonymous namespace
616
lcl_getChartType(std::u16string_view sChartType)617 static sal_Int32 lcl_getChartType(std::u16string_view sChartType)
618 {
619 auto aIterator = constChartTypeMap.find(sChartType);
620 if (aIterator == constChartTypeMap.end())
621 return chart::TYPEID_UNKNOWN;
622 return aIterator->second;
623 }
624
lcl_generateRandomValue()625 static sal_Int32 lcl_generateRandomValue()
626 {
627 return comphelper::rng::uniform_int_distribution(0, 100000000-1);
628 }
629
empty() const630 bool DataLabelsRange::empty() const
631 {
632 return maLabels.empty();
633 }
634
count() const635 size_t DataLabelsRange::count() const
636 {
637 return maLabels.size();
638 }
639
hasLabel(sal_Int32 nIndex) const640 bool DataLabelsRange::hasLabel(sal_Int32 nIndex) const
641 {
642 return maLabels.find(nIndex) != maLabels.end();
643 }
644
getRange() const645 const OUString & DataLabelsRange::getRange() const
646 {
647 return maRange;
648 }
649
setRange(const OUString & rRange)650 void DataLabelsRange::setRange(const OUString& rRange)
651 {
652 maRange = rRange;
653 }
654
setLabel(sal_Int32 nIndex,const OUString & rText)655 void DataLabelsRange::setLabel(sal_Int32 nIndex, const OUString& rText)
656 {
657 maLabels.emplace(nIndex, rText);
658 }
659
begin() const660 DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::begin() const
661 {
662 return maLabels.begin();
663 }
664
end() const665 DataLabelsRange::LabelsRangeMap::const_iterator DataLabelsRange::end() const
666 {
667 return maLabels.end();
668 }
669
ChartExport(sal_Int32 nXmlNamespace,FSHelperPtr pFS,Reference<frame::XModel> const & xModel,XmlFilterBase * pFB,DocumentType eDocumentType)670 ChartExport::ChartExport( sal_Int32 nXmlNamespace, FSHelperPtr pFS, Reference< frame::XModel > const & xModel, XmlFilterBase* pFB, DocumentType eDocumentType )
671 : DrawingML( std::move(pFS), pFB, eDocumentType )
672 , mnXmlNamespace( nXmlNamespace )
673 , mnSeriesCount(0)
674 , mxChartModel( xModel )
675 , mpURLTransformer(std::make_shared<URLTransformer>())
676 , mbHasCategoryLabels( false )
677 , mbHasZAxis( false )
678 , mbIs3DChart( false )
679 , mbStacked(false)
680 , mbPercent(false)
681 , mbHasDateCategories(false)
682 {
683 }
684
SetURLTranslator(const std::shared_ptr<URLTransformer> & pTransformer)685 void ChartExport::SetURLTranslator(const std::shared_ptr<URLTransformer>& pTransformer)
686 {
687 mpURLTransformer = pTransformer;
688 }
689
getChartType()690 sal_Int32 ChartExport::getChartType( )
691 {
692 OUString sChartType = mxDiagram->getDiagramType();
693 return lcl_getChartType( sChartType );
694 }
695
696 namespace {
697
createArguments(const OUString & rRangeRepresentation,bool bUseColumns)698 uno::Sequence< beans::PropertyValue > createArguments(
699 const OUString & rRangeRepresentation, bool bUseColumns)
700 {
701 css::chart::ChartDataRowSource eRowSource = css::chart::ChartDataRowSource_ROWS;
702 if (bUseColumns)
703 eRowSource = css::chart::ChartDataRowSource_COLUMNS;
704
705 uno::Sequence<beans::PropertyValue> aArguments{
706 { u"DataRowSource"_ustr, -1, uno::Any(eRowSource), beans::PropertyState_DIRECT_VALUE },
707 { u"FirstCellAsLabel"_ustr, -1, uno::Any(false), beans::PropertyState_DIRECT_VALUE },
708 { u"HasCategories"_ustr, -1, uno::Any(false), beans::PropertyState_DIRECT_VALUE },
709 { u"CellRangeRepresentation"_ustr, -1, uno::Any(rRangeRepresentation),
710 beans::PropertyState_DIRECT_VALUE }
711 };
712
713 return aArguments;
714 }
715
getPrimaryDataSeries(const Reference<chart2::XChartType> & xChartType)716 Reference<chart2::XDataSeries> getPrimaryDataSeries(const Reference<chart2::XChartType>& xChartType)
717 {
718 Reference< chart2::XDataSeriesContainer > xDSCnt(xChartType, uno::UNO_QUERY_THROW);
719
720 // export dataseries for current chart-type
721 const Sequence< Reference< chart2::XDataSeries > > aSeriesSeq(xDSCnt->getDataSeries());
722 for (const auto& rSeries : aSeriesSeq)
723 {
724 Reference<chart2::XDataSeries> xSource(rSeries, uno::UNO_QUERY);
725 if (xSource.is())
726 return xSource;
727 }
728
729 return Reference<chart2::XDataSeries>();
730 }
731
732 }
733
getSplitCategoriesList(const OUString & rRange)734 Sequence< Sequence< OUString > > ChartExport::getSplitCategoriesList( const OUString& rRange )
735 {
736 Reference< chart2::XChartDocument > xChartDoc(getModel(), uno::UNO_QUERY);
737 OSL_ASSERT(xChartDoc.is());
738 if (xChartDoc.is())
739 {
740 Reference< chart2::data::XDataProvider > xDataProvider(xChartDoc->getDataProvider());
741 OSL_ENSURE(xDataProvider.is(), "No DataProvider");
742 if (xDataProvider.is())
743 {
744 //detect whether the first series is a row or a column
745 bool bSeriesUsesColumns = true;
746 Reference< chart2::XDiagram > xDiagram(xChartDoc->getFirstDiagram());
747 try
748 {
749 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(xDiagram, uno::UNO_QUERY_THROW);
750 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(xCooSysCnt->getCoordinateSystems());
751 for (const auto& rCooSys : aCooSysSeq)
752 {
753 const Reference< chart2::XChartTypeContainer > xCTCnt(rCooSys, uno::UNO_QUERY_THROW);
754 const Sequence< Reference< chart2::XChartType > > aChartTypeSeq(xCTCnt->getChartTypes());
755 for (const auto& rChartType : aChartTypeSeq)
756 {
757 Reference< chart2::XDataSeries > xDataSeries = getPrimaryDataSeries(rChartType);
758 if (xDataSeries.is())
759 {
760 uno::Reference< chart2::data::XDataSource > xSeriesSource(xDataSeries, uno::UNO_QUERY);
761 const uno::Sequence< beans::PropertyValue > rArguments = xDataProvider->detectArguments(xSeriesSource);
762 for (const beans::PropertyValue& rProperty : rArguments)
763 {
764 if (rProperty.Name == "DataRowSource")
765 {
766 css::chart::ChartDataRowSource eRowSource;
767 if (rProperty.Value >>= eRowSource)
768 {
769 bSeriesUsesColumns = (eRowSource == css::chart::ChartDataRowSource_COLUMNS);
770 break;
771 }
772 }
773 }
774 }
775 }
776 }
777 }
778 catch (const uno::Exception &)
779 {
780 DBG_UNHANDLED_EXCEPTION("chart2");
781 }
782 // detect we have an inner data table or not
783 if (xChartDoc->hasInternalDataProvider() && rRange == "categories")
784 {
785 try
786 {
787 css::uno::Reference< css::chart2::XAnyDescriptionAccess > xDataAccess(xChartDoc->getDataProvider(), uno::UNO_QUERY);
788 const Sequence< Sequence< uno::Any > >aAnyCategories(bSeriesUsesColumns ? xDataAccess->getAnyRowDescriptions() : xDataAccess->getAnyColumnDescriptions());
789 auto pMax = std::max_element(aAnyCategories.begin(), aAnyCategories.end(),
790 [](const Sequence<uno::Any>& a, const Sequence<uno::Any>& b) {
791 return a.getLength() < b.getLength(); });
792
793 //minimum is 1!
794 if (pMax != aAnyCategories.end() && pMax->getLength() > 1)
795 {
796 sal_Int32 nLevelCount = pMax->getLength();
797 //we have complex categories
798 //sort the categories name
799 Sequence<Sequence<OUString>>aFinalSplitSource(nLevelCount);
800 auto pFinalSplitSource = aFinalSplitSource.getArray();
801 for (sal_Int32 i = 0; i < nLevelCount; i++)
802 {
803 sal_Int32 nElemLabel = 0;
804 pFinalSplitSource[nLevelCount - i - 1].realloc(aAnyCategories.getLength());
805 auto pSeq = pFinalSplitSource[nLevelCount - i - 1].getArray();
806 for (auto const& elemLabel : aAnyCategories)
807 {
808 // make sure elemLabel[i] exists!
809 if (elemLabel.getLength() > i)
810 {
811 pSeq[nElemLabel] = elemLabel[i].get<OUString>();
812 nElemLabel++;
813 }
814 }
815 }
816 return aFinalSplitSource;
817 }
818 }
819 catch (const uno::Exception &)
820 {
821 DBG_UNHANDLED_EXCEPTION("oox");
822 }
823 }
824 else
825 {
826 try
827 {
828 uno::Reference< chart2::data::XDataSource > xCategoriesSource(xDataProvider->createDataSource(
829 createArguments(rRange, bSeriesUsesColumns)));
830
831 if (xCategoriesSource.is())
832 {
833 const Sequence< Reference< chart2::data::XLabeledDataSequence >> aCategories = xCategoriesSource->getDataSequences();
834 if (aCategories.getLength() > 1)
835 {
836 //we have complex categories
837 //sort the categories name
838 Sequence<Sequence<OUString>> aFinalSplitSource(aCategories.getLength());
839 std::transform(aCategories.begin(), aCategories.end(),
840 std::reverse_iterator(asNonConstRange(aFinalSplitSource).end()),
841 [](const Reference<chart2::data::XLabeledDataSequence>& xCat) {
842 return lcl_getLabelSequence(xCat->getValues()); });
843 return aFinalSplitSource;
844 }
845 }
846 }
847 catch (const uno::Exception &)
848 {
849 DBG_UNHANDLED_EXCEPTION("oox");
850 }
851 }
852 }
853 }
854
855 return Sequence< Sequence< OUString>>(0);
856 }
857
parseFormula(const OUString & rRange)858 OUString ChartExport::parseFormula( const OUString& rRange )
859 {
860 OUString aResult;
861 Reference< XFormulaParser > xParser;
862 uno::Reference< lang::XMultiServiceFactory > xSF = GetFB()->getModelFactory();
863 if( xSF.is() )
864 {
865 try
866 {
867 xParser.set( xSF->createInstance(u"com.sun.star.sheet.FormulaParser"_ustr), UNO_QUERY );
868 }
869 catch( Exception& )
870 {
871 }
872 }
873
874 SAL_WARN_IF(!xParser.is(), "oox", "creating formula parser failed");
875
876 if( xParser.is() )
877 {
878 Reference< XPropertySet > xParserProps( xParser, uno::UNO_QUERY );
879 // rRange is the result of a
880 // css::chart2::data::XDataSequence::getSourceRangeRepresentation()
881 // call that returns the range in the document's current UI notation.
882 // Creating a FormulaParser defaults to the same notation, for
883 // parseFormula() do not attempt to override the FormulaConvention
884 // property with css::sheet::AddressConvention::OOO or some such.
885 /* TODO: it would be much better to introduce a
886 * getSourceRangeRepresentation(css::sheet::AddressConvention) to
887 * return the ranges in a specific convention than converting them with
888 * the overhead of creating an XFormulaParser for each... */
889 uno::Sequence<sheet::FormulaToken> aTokens = xParser->parseFormula( rRange, CellAddress( 0, 0, 0 ) );
890 if( xParserProps.is() )
891 {
892 xParserProps->setPropertyValue(u"FormulaConvention"_ustr, uno::Any(css::sheet::AddressConvention::XL_OOX) );
893 // For referencing named ranges correctly with special excel chart syntax.
894 xParserProps->setPropertyValue(u"RefConventionChartOOXML"_ustr, uno::Any(true) );
895 }
896 aResult = xParser->printFormula( aTokens, CellAddress( 0, 0, 0 ) );
897 }
898 else
899 {
900 //FIXME: currently just using simple converter, e.g $Sheet1.$A$1:$C$1 -> Sheet1!$A$1:$C$1
901 OUString aRange( rRange );
902 if( aRange.startsWith("$") )
903 aRange = aRange.copy(1);
904 aRange = aRange.replaceAll(".$", "!$" );
905 aResult = aRange;
906 }
907
908 return aResult;
909 }
910
WriteChartObj(const Reference<XShape> & xShape,sal_Int32 nID,sal_Int32 nChartCount)911 void ChartExport::WriteChartObj( const Reference< XShape >& xShape, sal_Int32 nID, sal_Int32 nChartCount )
912 {
913 FSHelperPtr pFS = GetFS();
914
915 Reference< chart2::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
916 OSL_ASSERT( xChartDoc.is() );
917 if( !xChartDoc.is() )
918 return;
919
920 // We need to get the new diagram here so we can know if this is a chartex
921 // chart.
922 mxNewDiagram.set( xChartDoc->getFirstDiagram());
923
924 const bool bIsChartex = isChartexNotChartNS();
925
926 if (bIsChartex) {
927 // Do the AlternateContent header
928 mpFS->startElementNS(XML_mc, XML_AlternateContent, FSNS(XML_xmlns, XML_mc),
929 "http://schemas.openxmlformats.org/markup-compatibility/2006");
930 mpFS->startElementNS(XML_mc, XML_Choice,
931 FSNS(XML_xmlns, XML_cx2), "http://schemas.microsoft.com/office/drawing/2015/10/21/chartex",
932 XML_Requires, "cx2");
933 }
934
935 Reference< XPropertySet > xShapeProps( xShape, UNO_QUERY );
936
937 pFS->startElementNS(mnXmlNamespace, XML_graphicFrame);
938
939 pFS->startElementNS(mnXmlNamespace, XML_nvGraphicFramePr);
940
941 // TODO: get the correct chart name chart id
942 OUString sName = u"Object 1"_ustr;
943 Reference< XNamed > xNamed( xShape, UNO_QUERY );
944 if (xNamed.is())
945 sName = xNamed->getName();
946
947 pFS->startElementNS( mnXmlNamespace, XML_cNvPr,
948 XML_id, OString::number(nID),
949 XML_name, sName);
950
951 OUString sURL;
952 if ( GetProperty( xShapeProps, u"URL"_ustr ) )
953 mAny >>= sURL;
954 if( !sURL.isEmpty() )
955 {
956 OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
957 oox::getRelationship(Relationship::HYPERLINK),
958 mpURLTransformer->getTransformedString(sURL),
959 mpURLTransformer->isExternalURL(sURL));
960
961 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
962 }
963
964 if (bIsChartex) {
965 pFS->startElement(FSNS(XML_a, XML_extLst));
966 pFS->startElement(FSNS(XML_a, XML_ext), XML_uri,
967 "{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}");
968 pFS->singleElement(FSNS(XML_a16, XML_creationId),
969 FSNS(XML_xmlns, XML_a16), "http://schemas.microsoft.com/office/drawing/2014/main",
970 XML_id, "{393D7C90-AF84-3958-641C-0FEC03FE8894}");
971
972 pFS->endElement(FSNS(XML_a, XML_ext));
973 pFS->endElement(FSNS(XML_a, XML_extLst));
974 }
975
976 pFS->endElementNS(mnXmlNamespace, XML_cNvPr);
977
978 pFS->singleElementNS(mnXmlNamespace, XML_cNvGraphicFramePr);
979
980 if( GetDocumentType() == DOCUMENT_PPTX )
981 pFS->singleElementNS(mnXmlNamespace, XML_nvPr);
982 pFS->endElementNS( mnXmlNamespace, XML_nvGraphicFramePr );
983
984 // visual chart properties
985 WriteShapeTransformation( xShape, mnXmlNamespace );
986
987 const char *sSchemaURL = bIsChartex?
988 "http://schemas.microsoft.com/office/drawing/2014/chartex" :
989 "http://schemas.openxmlformats.org/drawingml/2006/chart";
990
991 // writer chart object
992 pFS->startElement(FSNS(XML_a, XML_graphic));
993 pFS->startElement( FSNS( XML_a, XML_graphicData ), XML_uri, sSchemaURL );
994 OUString sId;
995 const char* sFullPath = nullptr;
996 const char* sRelativePath = nullptr;
997 const char *sChartFnamePrefix = bIsChartex? "chartEx" : "chart";
998 switch( GetDocumentType() )
999 {
1000 case DOCUMENT_DOCX:
1001 {
1002 sFullPath = "word/charts/";
1003 sRelativePath = "charts/";
1004 break;
1005 }
1006 case DOCUMENT_PPTX:
1007 {
1008 sFullPath = "ppt/charts/";
1009 sRelativePath = "../charts/";
1010 break;
1011 }
1012 case DOCUMENT_XLSX:
1013 {
1014 sFullPath = "xl/charts/";
1015 sRelativePath = "../charts/";
1016 break;
1017 }
1018 default:
1019 {
1020 sFullPath = "charts/";
1021 sRelativePath = "charts/";
1022 break;
1023 }
1024 }
1025 OUString sFullStream = OUStringBuffer()
1026 .appendAscii(sFullPath)
1027 .appendAscii(sChartFnamePrefix)
1028 .append(OUString::number(nChartCount) + ".xml")
1029 .makeStringAndClear();
1030 OUString sRelativeStream = OUStringBuffer()
1031 .appendAscii(sRelativePath)
1032 .appendAscii(sChartFnamePrefix)
1033 .append(OUString::number(nChartCount) + ".xml" )
1034 .makeStringAndClear();
1035
1036 const OUString sAppURL = bIsChartex?
1037 u"application/vnd.ms-office.chartex+xml"_ustr :
1038 u"application/vnd.openxmlformats-officedocument.drawingml.chart+xml"_ustr;
1039
1040 const Relationship eChartRel = bIsChartex ?
1041 Relationship::CHARTEX :
1042 Relationship::CHART;
1043
1044 FSHelperPtr pChart = CreateOutputStream(
1045 sFullStream,
1046 sRelativeStream,
1047 pFS->getOutputStream(),
1048 sAppURL,
1049 oox::getRelationship(eChartRel),
1050 &sId );
1051
1052 XmlFilterBase* pFB = GetFB();
1053
1054 if (bIsChartex) {
1055 // Use chartex namespace
1056 pFS->singleElement( FSNS( XML_cx, XML_chart ),
1057 FSNS(XML_xmlns, XML_cx), pFB->getNamespaceURL(OOX_NS(cx)),
1058 FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)),
1059 FSNS(XML_r, XML_id), sId );
1060 } else {
1061 pFS->singleElement( FSNS( XML_c, XML_chart ),
1062 FSNS(XML_xmlns, XML_c), pFB->getNamespaceURL(OOX_NS(dmlChart)),
1063 FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)),
1064 FSNS(XML_r, XML_id), sId );
1065 }
1066
1067 pFS->endElement( FSNS( XML_a, XML_graphicData ) );
1068 pFS->endElement( FSNS( XML_a, XML_graphic ) );
1069 pFS->endElementNS( mnXmlNamespace, XML_graphicFrame );
1070
1071 if (bIsChartex) {
1072 // Do the AlternateContent fallback path
1073 pFS->endElementNS(XML_mc, XML_Choice);
1074 pFS->startElementNS(XML_mc, XML_Fallback);
1075 pFS->startElementNS(XML_xdr, XML_sp, XML_macro, "", XML_textlink, "");
1076 pFS->startElementNS(XML_xdr, XML_nvSpPr);
1077 pFS->singleElementNS(XML_xdr, XML_cNvPr, XML_id, "0", XML_name, "");
1078 pFS->startElementNS(XML_xdr, XML_cNvSpPr);
1079 pFS->singleElementNS(XML_a, XML_spLocks, XML_noTextEdit, "1");
1080 pFS->endElementNS(XML_xdr, XML_cNvSpPr);
1081 pFS->endElementNS(XML_xdr, XML_nvSpPr);
1082 pFS->startElementNS(XML_xdr, XML_spPr);
1083 pFS->startElementNS(XML_a, XML_xfrm);
1084 pFS->singleElementNS(XML_a, XML_off, XML_x, "6600825", XML_y, "2533650");
1085 pFS->singleElementNS(XML_a, XML_ext, XML_cx, "4572000", XML_cy, "2743200");
1086 pFS->endElementNS(XML_a, XML_xfrm);
1087 pFS->startElementNS(XML_a, XML_prstGeom, XML_prst, "rect");
1088 pFS->singleElementNS(XML_a, XML_avLst);
1089 pFS->endElementNS(XML_a, XML_prstGeom);
1090 pFS->startElementNS(XML_a, XML_solidFill);
1091 pFS->singleElementNS(XML_a, XML_prstClr, XML_val, "white");
1092 pFS->endElementNS(XML_a, XML_solidFill);
1093 pFS->startElementNS(XML_a, XML_ln, XML_w, "1");
1094 pFS->startElementNS(XML_a, XML_solidFill);
1095 pFS->singleElementNS(XML_a, XML_prstClr, XML_val, "green");
1096 pFS->endElementNS(XML_a, XML_solidFill);
1097 pFS->endElementNS(XML_a, XML_ln);
1098 pFS->endElementNS(XML_xdr, XML_spPr);
1099 pFS->startElementNS(XML_xdr, XML_txBody);
1100 pFS->singleElementNS(XML_a, XML_bodyPr, XML_vertOverflow, "clip", XML_horzOverflow, "clip");
1101 pFS->singleElementNS(XML_a, XML_lstStyle);
1102 pFS->startElementNS(XML_a, XML_p);
1103 pFS->startElementNS(XML_a, XML_r);
1104 pFS->singleElementNS(XML_a, XML_rPr, XML_sz, "1100");
1105 pFS->startElementNS(XML_a, XML_t);
1106
1107 const std::string_view sErrTxt("This chart isn't available in your version of Excel.\n\n"
1108 "Editing this shape or saving this workbook into a different file format will permanently break the chart.");
1109 pFS->writeEscaped( sErrTxt );
1110
1111 pFS->endElementNS(XML_a, XML_t);
1112 pFS->endElementNS(XML_a, XML_r);
1113 pFS->endElementNS(XML_a, XML_p);
1114 pFS->endElementNS(XML_xdr, XML_txBody);
1115 pFS->endElementNS(XML_xdr, XML_sp);
1116
1117 pFS->endElementNS(XML_mc, XML_Fallback);
1118 pFS->endElementNS(XML_mc, XML_AlternateContent);
1119 }
1120
1121 SetFS( pChart );
1122 ExportContent();
1123
1124 if (bIsChartex) {
1125 SetFS( pChart );
1126 sRelativePath ="";
1127
1128 FSHelperPtr pChartFS = GetFS();
1129
1130 // output style and colorstyle files
1131
1132 // first style
1133 static constexpr char sStyleFnamePrefix[] = "style";
1134 OUStringBuffer sFullStreamBuf;
1135 sFullStreamBuf.appendAscii(sFullPath);
1136 sFullStreamBuf = sFullStreamBuf + sStyleFnamePrefix + OUString::number(nChartCount) + ".xml";
1137 sFullStream = sFullStreamBuf.makeStringAndClear();
1138 OUStringBuffer sRelativeStreamBuf;
1139 sRelativeStreamBuf.appendAscii(sRelativePath);
1140 sRelativeStreamBuf = sRelativeStreamBuf + sStyleFnamePrefix + OUString::number(nChartCount) + ".xml";
1141 sRelativeStream = sRelativeStreamBuf.makeStringAndClear();
1142
1143 FSHelperPtr pStyle = CreateOutputStream(
1144 sFullStream,
1145 sRelativeStream,
1146 pChartFS->getOutputStream(),
1147 u"application/vnd.ms-office.chartstyle+xml"_ustr,
1148 oox::getRelationship(Relationship::CHARTSTYLE),
1149 &sId,
1150 true /* for some reason this doesn't have a header line */);
1151
1152 SetFS( pStyle );
1153 pFS = GetFS();
1154
1155 pFS->startElement(FSNS(XML_cs, XML_chartStyle),
1156 FSNS( XML_xmlns, XML_cs ), pFB->getNamespaceURL(OOX_NS(cs)),
1157 FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
1158 XML_id, "419" /* no idea what this number is supposed to be */);
1159
1160 outputStyleEntry(pFS, XML_axisTitle);;
1161 outputStyleEntry(pFS, XML_categoryAxis);
1162 outputChartAreaStyleEntry(pFS);
1163 outputStyleEntry(pFS, XML_dataLabel);
1164 outputDataPointStyleEntry(pFS);
1165 outputStyleEntry(pFS, XML_dataPoint3D);
1166 outputStyleEntry(pFS, XML_dataPointLine);
1167 outputStyleEntry(pFS, XML_dataPointMarker);
1168 outputStyleEntry(pFS, XML_dataPointWireframe);
1169 outputStyleEntry(pFS, XML_dataTable);
1170 outputStyleEntry(pFS, XML_downBar);
1171 outputStyleEntry(pFS, XML_dropLine);
1172 outputStyleEntry(pFS, XML_errorBar);
1173 outputStyleEntry(pFS, XML_floor);
1174 outputStyleEntry(pFS, XML_gridlineMajor);
1175 outputStyleEntry(pFS, XML_gridlineMinor);
1176 outputStyleEntry(pFS, XML_hiLoLine);
1177 outputStyleEntry(pFS, XML_leaderLine);
1178 outputStyleEntry(pFS, XML_legend);
1179 outputStyleEntry(pFS, XML_plotArea);
1180 outputStyleEntry(pFS, XML_plotArea3D);
1181 outputStyleEntry(pFS, XML_seriesAxis);
1182 outputStyleEntry(pFS, XML_seriesLine);
1183 outputStyleEntry(pFS, XML_title);
1184 outputStyleEntry(pFS, XML_trendline);
1185 outputStyleEntry(pFS, XML_trendlineLabel);
1186 outputStyleEntry(pFS, XML_upBar);
1187 outputStyleEntry(pFS, XML_valueAxis);
1188 outputStyleEntry(pFS, XML_wall);
1189
1190 pFS->endElement(FSNS(XML_cs, XML_chartStyle));
1191
1192 pStyle->endDocument();
1193
1194 // now colorstyle
1195 static constexpr char sColorFnamePrefix[] = "colors";
1196 sFullStreamBuf = OUStringBuffer();
1197 sFullStreamBuf.appendAscii(sFullPath);
1198 sFullStreamBuf = sFullStreamBuf + sColorFnamePrefix + OUString::number(nChartCount) + ".xml";
1199 sFullStream = sFullStreamBuf.makeStringAndClear();
1200 sRelativeStreamBuf = OUStringBuffer();
1201 sRelativeStreamBuf.appendAscii(sRelativePath);
1202 sRelativeStreamBuf = sRelativeStreamBuf + sColorFnamePrefix + OUString::number(nChartCount) + ".xml";
1203 sRelativeStream = sRelativeStreamBuf.makeStringAndClear();
1204
1205 FSHelperPtr pColorStyle = CreateOutputStream(
1206 sFullStream,
1207 sRelativeStream,
1208 pChartFS->getOutputStream(),
1209 u"application/vnd.ms-office.chartcolorstyle+xml"_ustr,
1210 oox::getRelationship(Relationship::CHARTCOLORSTYLE),
1211 &sId,
1212 true /* also no header line */);
1213
1214 SetFS( pColorStyle );
1215 pFS = GetFS();
1216
1217 pFS->startElement(FSNS(XML_cs, XML_colorStyle),
1218 FSNS( XML_xmlns, XML_cs ), pFB->getNamespaceURL(OOX_NS(cs)),
1219 FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
1220 XML_meth, "cycle",
1221 XML_id, "10" /* no idea what this number is supposed to be */);
1222
1223 pFS->singleElement(FSNS(XML_a, XML_schemeClr),
1224 XML_val, "accent1");
1225
1226 pFS->endElement(FSNS(XML_cs, XML_colorStyle));
1227
1228 pColorStyle->endDocument();
1229 }
1230
1231 pChart->endDocument();
1232 }
1233
InitRangeSegmentationProperties(const Reference<chart2::XChartDocument> & xChartDoc)1234 void ChartExport::InitRangeSegmentationProperties( const Reference< chart2::XChartDocument > & xChartDoc )
1235 {
1236 if( !xChartDoc.is())
1237 return;
1238
1239 try
1240 {
1241 Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
1242 OSL_ENSURE( xDataProvider.is(), "No DataProvider" );
1243 if( xDataProvider.is())
1244 {
1245 mbHasCategoryLabels = lcl_hasCategoryLabels( xChartDoc );
1246 }
1247 }
1248 catch( const uno::Exception & )
1249 {
1250 DBG_UNHANDLED_EXCEPTION("oox");
1251 }
1252 }
1253
ExportContent()1254 void ChartExport::ExportContent()
1255 {
1256 Reference< chart2::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
1257 OSL_ASSERT( xChartDoc.is() );
1258 if( !xChartDoc.is() )
1259 return;
1260 InitRangeSegmentationProperties( xChartDoc );
1261
1262 const bool bIsChartex = isChartexNotChartNS();
1263 ExportContent_( bIsChartex );
1264 }
1265
ExportContent_(bool bIsChartex)1266 void ChartExport::ExportContent_( bool bIsChartex )
1267 {
1268 Reference< css::chart::XChartDocument > xChartDoc( getModel(), uno::UNO_QUERY );
1269 if( xChartDoc.is())
1270 {
1271 // determine if data comes from the outside
1272 bool bIncludeTable = true;
1273
1274 Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
1275 if( xNewDoc.is())
1276 {
1277 // check if we have own data. If so we must not export the complete
1278 // range string, as this is our only indicator for having own or
1279 // external data. @todo: fix this in the file format!
1280 Reference< lang::XServiceInfo > xDPServiceInfo( xNewDoc->getDataProvider(), uno::UNO_QUERY );
1281 if( ! (xDPServiceInfo.is() && xDPServiceInfo->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" ))
1282 {
1283 bIncludeTable = false;
1284 }
1285 }
1286 exportChartSpace( xChartDoc, bIncludeTable, bIsChartex );
1287 }
1288 else
1289 {
1290 OSL_FAIL( "Couldn't export chart due to wrong XModel" );
1291 }
1292 }
1293
exportChartSpace(const Reference<css::chart::XChartDocument> & xChartDoc,bool bIncludeTable,bool bIsChartex)1294 void ChartExport::exportChartSpace( const Reference< css::chart::XChartDocument >& xChartDoc,
1295 bool bIncludeTable,
1296 bool bIsChartex)
1297 {
1298 FSHelperPtr pFS = GetFS();
1299 XmlFilterBase* pFB = GetFB();
1300
1301 const sal_Int32 nChartNS = bIsChartex ? XML_cx : XML_c;
1302
1303 if (bIsChartex) {
1304 pFS->startElement( FSNS( nChartNS, XML_chartSpace ),
1305 FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
1306 FSNS( XML_xmlns, XML_r ), pFB->getNamespaceURL(OOX_NS(officeRel)),
1307 FSNS( XML_xmlns, XML_cx ), pFB->getNamespaceURL(OOX_NS(cx)));
1308 } else {
1309 pFS->startElement( FSNS( nChartNS, XML_chartSpace ),
1310 FSNS( XML_xmlns, XML_c ), pFB->getNamespaceURL(OOX_NS(dmlChart)),
1311 FSNS( XML_xmlns, XML_a ), pFB->getNamespaceURL(OOX_NS(dml)),
1312 FSNS( XML_xmlns, XML_r ), pFB->getNamespaceURL(OOX_NS(officeRel)));
1313 }
1314
1315 if( !bIncludeTable )
1316 {
1317 // TODO:external data
1318 }
1319 else
1320 {
1321 Reference< XPropertySet > xPropSet(xChartDoc, UNO_QUERY);
1322 Any aNullDate = xPropSet->getPropertyValue("NullDate");
1323 util::DateTime aDate;
1324 if ((aNullDate >>= aDate) && (aDate.Year == 1904 && aDate.Month == 1 && aDate.Day == 1))
1325 {
1326 pFS->singleElement(FSNS(XML_c, XML_date1904), XML_val, "1");
1327 }
1328 else
1329 {
1330 pFS->singleElement(FSNS(XML_c, XML_date1904), XML_val, "0");
1331 }
1332 }
1333
1334 // TODO: get the correct editing language
1335 if (bIsChartex) {
1336 // chartData
1337 pFS->startElement(FSNS(XML_cx, XML_chartData));
1338
1339 exportExternalData(xChartDoc, true);
1340 exportData_chartex(xChartDoc);
1341
1342 pFS->endElement(FSNS(XML_cx, XML_chartData));
1343 } else {
1344 pFS->singleElement(FSNS(XML_c, XML_lang), XML_val, "en-US");
1345
1346 pFS->singleElement(FSNS(XML_c, XML_roundedCorners), XML_val, "0");
1347 }
1348
1349 // style
1350 if (!bIsChartex) {
1351 mxDiagram.set( xChartDoc->getDiagram() );
1352 Reference< XPropertySet > xPropSet(mxDiagram, uno::UNO_QUERY);
1353 if (GetProperty(xPropSet, u"StyleIndex"_ustr)) {
1354 sal_Int32 nStyleIdx = -1;
1355 mAny >>= nStyleIdx;
1356 assert(nStyleIdx >= 0);
1357 pFS->singleElement(FSNS(XML_c, XML_style), XML_val,
1358 OUString::number(nStyleIdx));
1359 }
1360 }
1361
1362 //XML_chart
1363 exportChart(xChartDoc, bIsChartex);
1364
1365 // TODO: printSettings
1366 // TODO: text properties
1367 Reference< XPropertySet > xPropSet = xChartDoc->getArea();
1368 if( xPropSet.is() )
1369 exportShapeProps( xPropSet, bIsChartex );
1370
1371 // TODO for chartex
1372 if (!bIsChartex) {
1373 //XML_externalData
1374 exportExternalData(xChartDoc, false);
1375 }
1376
1377 // export additional shapes in chart
1378 if (!bIsChartex) {
1379 exportAdditionalShapes(xChartDoc);
1380 }
1381
1382 pFS->endElement( FSNS( nChartNS, XML_chartSpace ) );
1383 }
1384
exportData_chartex(const Reference<css::chart::XChartDocument> & xChartDoc)1385 void ChartExport::exportData_chartex( [[maybe_unused]] const Reference< css::chart::XChartDocument >& xChartDoc)
1386 {
1387 Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( mxNewDiagram, uno::UNO_QUERY );
1388 if( ! xBCooSysCnt.is()) return;
1389 const Sequence< Reference< chart2::XCoordinateSystem > >
1390 aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
1391
1392 if (!aCooSysSeq.hasElements()) return;
1393
1394 for( const auto& rCS : aCooSysSeq ) {
1395 Reference< chart2::XChartTypeContainer > xCTCnt( rCS, uno::UNO_QUERY );
1396 if( ! xCTCnt.is())
1397 continue;
1398 const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
1399
1400 for( const auto& rCT : aCTSeq ) {
1401 Reference< chart2::XDataSeriesContainer > xDSCnt( rCT, uno::UNO_QUERY );
1402 if( ! xDSCnt.is())
1403 return;
1404 Reference< chart2::XChartType > xChartType( rCT, uno::UNO_QUERY );
1405 if( ! xChartType.is())
1406 continue;
1407
1408 OUString aLabelRole = xChartType->getRoleOfSequenceForSeriesLabel();
1409
1410 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
1411
1412 for (const auto& splitDataSeries : aSplitDataSeries) {
1413 sal_Int32 nSeriesIndex = 0;
1414 for( const auto& rSeries : splitDataSeries )
1415 {
1416 // export series
1417 Reference< chart2::data::XDataSource > xSource( rSeries, uno::UNO_QUERY );
1418 if( !xSource.is()) continue;
1419
1420 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
1421 xSource->getDataSequences());
1422
1423 // search for main sequence and create a series element
1424 sal_Int32 nMainSequenceIndex = -1;
1425 sal_Int32 nSeriesLength = 0;
1426 Reference< chart2::data::XDataSequence > xValueSeq;
1427 Reference< chart2::data::XDataSequence > xLabelSeq;
1428 sal_Int32 nSeqIdx=0;
1429 for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx )
1430 {
1431 Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() );
1432 if( nMainSequenceIndex==-1 )
1433 {
1434 Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY );
1435 OUString aRole;
1436 if( xSeqProp.is())
1437 xSeqProp->getPropertyValue(u"Role"_ustr) >>= aRole;
1438 // "main" sequence
1439 if( aRole == aLabelRole )
1440 {
1441 xValueSeq.set( xTempValueSeq );
1442 xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel());
1443 nMainSequenceIndex = nSeqIdx;
1444 }
1445 }
1446 sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0));
1447 if( nSeriesLength < nSequenceLength )
1448 nSeriesLength = nSequenceLength;
1449 }
1450 FSHelperPtr pFS = GetFS();
1451
1452 // The data id needs to agree with the id in exportSeries(). See DATA_ID_COMMENT
1453 pFS->startElement(FSNS(XML_cx, XML_data), XML_id, OUString::number(nSeriesIndex++));
1454
1455 // .xlsx chartex files seem to have this magical "_xlchart.v2.0" string,
1456 // and no explicit data, while .docx and .pptx contain the literal data,
1457 // as well as a ../embeddings file (which LO doesn't seem to produce).
1458 // But there's probably a smarter way to determine which pathway to take
1459 // than based on document type.
1460 if (GetDocumentType() == DOCUMENT_XLSX) {
1461 // Just hard-coding this for now
1462 pFS->startElement(FSNS(XML_cx, XML_numDim), XML_type, "val");
1463 pFS->startElement(FSNS(XML_cx, XML_f));
1464 pFS->writeEscaped("_xlchart.v2.0"); // I have no idea what this
1465 // means or what it should be in
1466 // general
1467 pFS->endElement(FSNS(XML_cx, XML_f));
1468 pFS->endElement(FSNS(XML_cx, XML_numDim));
1469 } else { // PPTX, DOCX
1470 OUString aCellRange = mxCategoriesValues.is() ? mxCategoriesValues->getSourceRangeRepresentation() : OUString();
1471 #undef OUTPUT_SPLIT_CATEGORIES // do we need this or not? TODO
1472 #ifdef OUTPUT_SPLIT_CATEGORIES
1473 const Sequence< Sequence< OUString >> aFinalSplitSource = getSplitCategoriesList(aCellRange);
1474 #endif
1475 aCellRange = parseFormula( aCellRange );
1476
1477 #ifdef OUTPUT_SPLIT_CATEGORIES
1478 if (aFinalSplitSource.getLength() > 1) {
1479
1480 // export multi level category axis labels
1481 pFS->startElement(FSNS(XML_cx, XML_strDim), XML_type, "cat");
1482
1483 pFS->startElement(FSNS(XML_cx, XML_f));
1484 pFS->writeEscaped(aCellRange);
1485 pFS->endElement(FSNS(XML_cx, XML_f));
1486
1487 for (const auto& rSeq : aFinalSplitSource) {
1488 pFS->startElement(FSNS(XML_cx, XML_lvl),
1489 XML_ptCount, OString::number(aFinalSplitSource[0].getLength()));
1490
1491 for (sal_Int32 j = 0; j < rSeq.getLength(); j++) {
1492 if(!rSeq[j].isEmpty()) {
1493 pFS->startElement(FSNS(XML_cx, XML_pt), XML_idx, OString::number(j));
1494 pFS->writeEscaped(rSeq[j]);
1495 pFS->endElement(FSNS(XML_cx, XML_pt));
1496 }
1497 }
1498 pFS->endElement(FSNS(XML_cx, XML_lvl));
1499 }
1500
1501 pFS->endElement(FSNS(XML_cx, XML_strDim));
1502 }
1503 else
1504 #endif
1505 {
1506 // export single category axis labels
1507 // TODO: seems like this should consider mbHasCategoryLabels
1508 bool bWriteDateCategories = mbHasDateCategories;
1509 OUString aNumberFormatString;
1510 if (bWriteDateCategories)
1511 {
1512 Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY );
1513 if( xAxisXSupp.is())
1514 {
1515 Reference< XPropertySet > xAxisProp = xAxisXSupp->getXAxis();
1516 if (GetProperty(xAxisProp, u"NumberFormat"_ustr))
1517 {
1518 sal_Int32 nKey = 0;
1519 mAny >>= nKey;
1520 aNumberFormatString = getNumberFormatCode(nKey);
1521 }
1522 }
1523 if (aNumberFormatString.isEmpty()) bWriteDateCategories = false;
1524 }
1525
1526 // === Output the categories
1527 if (bWriteDateCategories)
1528 {
1529 std::vector<double> aDateCategories = lcl_getAllValuesFromSequence(xValueSeq);
1530 const sal_Int32 ptCount = aDateCategories.size();
1531
1532 pFS->startElement(FSNS(XML_cx, XML_numDim), XML_type, "x"); // is "x" right?
1533 // TODO: check this
1534
1535 pFS->startElement(FSNS(XML_cx, XML_f));
1536 pFS->writeEscaped(aCellRange);
1537 pFS->endElement(FSNS(XML_cx, XML_f));
1538
1539 pFS->startElement(FSNS(XML_cx, XML_lvl),
1540 XML_ptCount, OString::number(ptCount),
1541 XML_formatCode, aNumberFormatString);
1542
1543 for (sal_Int32 i = 0; i < ptCount; i++) {
1544 if (!std::isnan(aDateCategories[i])) {
1545 pFS->startElement(FSNS(XML_cx, XML_pt), XML_idx, OString::number(i));
1546 pFS->write(OString::number(aDateCategories[i]));
1547 pFS->endElement(FSNS(XML_cx, XML_pt));
1548 }
1549 }
1550
1551 pFS->endElement(FSNS(XML_cx, XML_lvl));
1552 pFS->endElement(FSNS(XML_cx, XML_numDim));
1553 }
1554 else
1555 {
1556 std::vector<OUString> aCategories;
1557 lcl_fillCategoriesIntoStringVector(xValueSeq, aCategories);
1558 const sal_Int32 ptCount = aCategories.size();
1559
1560 // TODO: shouldn't have "cat" hard-coded here:
1561 // other options are colorStr, entityId
1562 pFS->startElement(FSNS(XML_cx, XML_strDim), XML_type, "cat");
1563
1564 pFS->startElement(FSNS(XML_cx, XML_f));
1565 pFS->writeEscaped(aCellRange);
1566 pFS->endElement(FSNS(XML_cx, XML_f));
1567
1568 pFS->startElement(FSNS(XML_cx, XML_lvl), XML_ptCount, OString::number(ptCount));
1569
1570 for (sal_Int32 i = 0; i < ptCount; i++) {
1571 pFS->startElement(FSNS(XML_cx, XML_pt), XML_idx, OString::number(i));
1572 pFS->writeEscaped(aCategories[i]);
1573 pFS->endElement(FSNS(XML_cx, XML_pt));
1574 }
1575
1576 pFS->endElement(FSNS(XML_cx, XML_lvl));
1577 pFS->endElement(FSNS(XML_cx, XML_strDim));
1578 }
1579
1580 // === Output the values
1581 pFS->startElement(FSNS(XML_cx, XML_numDim), XML_type, "val");
1582
1583 aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString();
1584 aCellRange = parseFormula( aCellRange );
1585 // TODO: need to handle XML_multiLvlStrRef according to aCellRange
1586
1587 pFS->startElement(FSNS(XML_cx, XML_f));
1588 pFS->writeEscaped( aCellRange );
1589 pFS->endElement( FSNS( XML_cx, XML_f ) );
1590
1591 ::std::vector< double > aValues = lcl_getAllValuesFromSequence( xValueSeq );
1592 sal_Int32 ptCount = aValues.size();
1593 OUString sNumberFormatString(u"General"_ustr);
1594 const sal_Int32 nKey = xValueSeq.is() ? xValueSeq->getNumberFormatKeyByIndex(-1) : 0;
1595 if (nKey > 0) {
1596 sNumberFormatString = getNumberFormatCode(nKey);
1597 }
1598 pFS->startElement(FSNS(XML_cx, XML_lvl),
1599 XML_ptCount, OString::number(ptCount),
1600 XML_formatCode, sNumberFormatString);
1601
1602 for( sal_Int32 i = 0; i < ptCount; i++ ) {
1603
1604 pFS->startElement(FSNS(XML_cx, XML_pt), XML_idx, OString::number(i));
1605 pFS->write(std::isnan(aValues[i]) ? 0 : aValues[i]);
1606 pFS->endElement(FSNS(XML_cx, XML_pt));
1607 }
1608
1609 pFS->endElement(FSNS(XML_cx, XML_lvl));
1610 pFS->endElement(FSNS(XML_cx, XML_numDim));
1611 }
1612 }
1613 pFS->endElement(FSNS(XML_cx, XML_data));
1614 }
1615 }
1616 }
1617 }
1618 }
1619
exportExternalData(const Reference<css::chart::XChartDocument> & xChartDoc,bool bIsChartex)1620 void ChartExport::exportExternalData( const Reference< css::chart::XChartDocument >& xChartDoc,
1621 bool bIsChartex)
1622 {
1623 if (bIsChartex) return; // TODO!!
1624 // Embedded external data is grab bagged for docx file hence adding export part of
1625 // external data for docx files only.
1626 if(GetDocumentType() != DOCUMENT_DOCX)
1627 return;
1628
1629 OUString externalDataPath;
1630 Reference< beans::XPropertySet > xDocPropSet( xChartDoc->getDiagram(), uno::UNO_QUERY );
1631 if( xDocPropSet.is())
1632 {
1633 try
1634 {
1635 Any aAny( xDocPropSet->getPropertyValue( u"ExternalData"_ustr ));
1636 aAny >>= externalDataPath;
1637 }
1638 catch( beans::UnknownPropertyException & )
1639 {
1640 SAL_WARN("oox", "Required property not found in ChartDocument");
1641 }
1642 }
1643 if(externalDataPath.isEmpty())
1644 return;
1645
1646 // Here adding external data entry to relationship.
1647 OUString relationPath = externalDataPath;
1648 // Converting absolute path to relative path.
1649 if( externalDataPath[ 0 ] != '.' && externalDataPath[ 1 ] != '.')
1650 {
1651 sal_Int32 nSepPos = externalDataPath.indexOf( '/', 0 );
1652 if( nSepPos > 0)
1653 {
1654 relationPath = relationPath.copy( nSepPos, ::std::max< sal_Int32 >( externalDataPath.getLength(), 0 ) - nSepPos );
1655 relationPath = ".." + relationPath;
1656 }
1657 }
1658 FSHelperPtr pFS = GetFS();
1659 OUString type = oox::getRelationship(Relationship::PACKAGE);
1660 if (relationPath.endsWith(".bin"))
1661 type = oox::getRelationship(Relationship::OLEOBJECT);
1662
1663 OUString sRelId = GetFB()->addRelation(pFS->getOutputStream(),
1664 type,
1665 relationPath);
1666 pFS->singleElementNS(XML_c, XML_externalData, FSNS(XML_r, XML_id), sRelId);
1667 }
1668
exportAdditionalShapes(const Reference<css::chart::XChartDocument> & xChartDoc)1669 void ChartExport::exportAdditionalShapes( const Reference< css::chart::XChartDocument >& xChartDoc )
1670 {
1671 // Not used in chartex
1672
1673 Reference< beans::XPropertySet > xDocPropSet(xChartDoc, uno::UNO_QUERY);
1674 if (!xDocPropSet.is())
1675 return;
1676
1677 css::uno::Reference< css::drawing::XShapes > mxAdditionalShapes;
1678 // get a sequence of non-chart shapes
1679 try
1680 {
1681 Any aShapesAny = xDocPropSet->getPropertyValue(u"AdditionalShapes"_ustr);
1682 if( (aShapesAny >>= mxAdditionalShapes) && mxAdditionalShapes.is() )
1683 {
1684 OUString sId;
1685 const char* sFullPath = nullptr;
1686 const char* sRelativePath = nullptr;
1687 sal_Int32 nDrawing = getNewDrawingUniqueId();
1688
1689 switch (GetDocumentType())
1690 {
1691 case DOCUMENT_DOCX:
1692 {
1693 sFullPath = "word/drawings/drawing";
1694 sRelativePath = "../drawings/drawing";
1695 break;
1696 }
1697 case DOCUMENT_PPTX:
1698 {
1699 sFullPath = "ppt/drawings/drawing";
1700 sRelativePath = "../drawings/drawing";
1701 break;
1702 }
1703 case DOCUMENT_XLSX:
1704 {
1705 sFullPath = "xl/drawings/drawing";
1706 sRelativePath = "../drawings/drawing";
1707 break;
1708 }
1709 default:
1710 {
1711 sFullPath = "drawings/drawing";
1712 sRelativePath = "drawings/drawing";
1713 break;
1714 }
1715 }
1716 OUString sFullStream = OUStringBuffer()
1717 .appendAscii(sFullPath)
1718 .append(OUString::number(nDrawing) + ".xml")
1719 .makeStringAndClear();
1720 OUString sRelativeStream = OUStringBuffer()
1721 .appendAscii(sRelativePath)
1722 .append(OUString::number(nDrawing) + ".xml")
1723 .makeStringAndClear();
1724
1725 sax_fastparser::FSHelperPtr pDrawing = CreateOutputStream(
1726 sFullStream,
1727 sRelativeStream,
1728 GetFS()->getOutputStream(),
1729 u"application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml"_ustr,
1730 oox::getRelationship(Relationship::CHARTUSERSHAPES),
1731 &sId);
1732
1733 GetFS()->singleElementNS(XML_c, XML_userShapes, FSNS(XML_r, XML_id), sId);
1734
1735 XmlFilterBase* pFB = GetFB();
1736 pDrawing->startElement(FSNS(XML_c, XML_userShapes),
1737 FSNS(XML_xmlns, XML_cdr), pFB->getNamespaceURL(OOX_NS(dmlChartDr)),
1738 FSNS(XML_xmlns, XML_a), pFB->getNamespaceURL(OOX_NS(dml)),
1739 FSNS(XML_xmlns, XML_c), pFB->getNamespaceURL(OOX_NS(dmlChart)),
1740 FSNS(XML_xmlns, XML_r), pFB->getNamespaceURL(OOX_NS(officeRel)));
1741
1742 const sal_Int32 nShapeCount(mxAdditionalShapes->getCount());
1743 for (sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++)
1744 {
1745 Reference< drawing::XShape > xShape;
1746 mxAdditionalShapes->getByIndex(nShapeId) >>= xShape;
1747 SAL_WARN_IF(!xShape.is(), "xmloff.chart", "Shape without an XShape?");
1748 if (!xShape.is())
1749 continue;
1750
1751 // TODO: absSizeAnchor: we import both (absSizeAnchor and relSizeAnchor), but there is no essential difference between them.
1752 pDrawing->startElement(FSNS(XML_cdr, XML_relSizeAnchor));
1753 uno::Reference< beans::XPropertySet > xShapeProperties(xShape, uno::UNO_QUERY);
1754 if( xShapeProperties.is() )
1755 {
1756 Reference<embed::XVisualObject> xVisObject(mxChartModel, uno::UNO_QUERY);
1757 awt::Size aPageSize = xVisObject->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT);
1758 WriteFromTo( xShape, aPageSize, pDrawing );
1759
1760 ShapeExport aExport(XML_cdr, pDrawing, nullptr, GetFB(), GetDocumentType(), nullptr, true);
1761 aExport.WriteShape(xShape);
1762 }
1763 pDrawing->endElement(FSNS(XML_cdr, XML_relSizeAnchor));
1764 }
1765 pDrawing->endElement(FSNS(XML_c, XML_userShapes));
1766 pDrawing->endDocument();
1767 }
1768 }
1769 catch (const uno::Exception&)
1770 {
1771 TOOLS_INFO_EXCEPTION("xmloff.chart", "AdditionalShapes not found");
1772 }
1773 }
1774
exportChart(const Reference<css::chart::XChartDocument> & xChartDoc,bool bIsChartex)1775 void ChartExport::exportChart( const Reference< css::chart::XChartDocument >& xChartDoc,
1776 bool bIsChartex)
1777 {
1778 Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
1779 mxDiagram.set( xChartDoc->getDiagram() );
1780 if( xNewDoc.is()) {
1781 mxNewDiagram.set( xNewDoc->getFirstDiagram());
1782 }
1783
1784 // get Properties of ChartDocument
1785 bool bHasMainTitle = false;
1786 bool bHasLegend = false;
1787 Reference< beans::XPropertySet > xDocPropSet( xChartDoc, uno::UNO_QUERY );
1788 if( xDocPropSet.is())
1789 {
1790 try
1791 {
1792 Any aAny( xDocPropSet->getPropertyValue(u"HasMainTitle"_ustr));
1793 aAny >>= bHasMainTitle;
1794 aAny = xDocPropSet->getPropertyValue(u"HasLegend"_ustr);
1795 aAny >>= bHasLegend;
1796 }
1797 catch( beans::UnknownPropertyException & )
1798 {
1799 SAL_WARN("oox", "Required property not found in ChartDocument");
1800 }
1801 } // if( xDocPropSet.is())
1802
1803 Sequence< uno::Reference< chart2::XFormattedString > > xFormattedSubTitle;
1804 Reference< beans::XPropertySet > xPropSubTitle( xChartDoc->getSubTitle(), UNO_QUERY );
1805 if( xPropSubTitle.is())
1806 {
1807 OUString aSubTitle;
1808 if ((xPropSubTitle->getPropertyValue(u"String"_ustr) >>= aSubTitle) && !aSubTitle.isEmpty())
1809 xPropSubTitle->getPropertyValue(u"FormattedStrings"_ustr) >>= xFormattedSubTitle;
1810 }
1811
1812 // chart element
1813 FSHelperPtr pFS = GetFS();
1814
1815 const sal_Int32 nChartNS = bIsChartex ? XML_cx : XML_c;
1816 pFS->startElement(FSNS(nChartNS, XML_chart));
1817
1818 // titles
1819 if( bHasMainTitle )
1820 {
1821 exportTitle( xChartDoc->getTitle(), bIsChartex, xFormattedSubTitle);
1822 if (!bIsChartex) {
1823 pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
1824 }
1825 }
1826 else if( xFormattedSubTitle.hasElements() )
1827 {
1828 exportTitle( xChartDoc->getSubTitle(), bIsChartex );
1829 if (!bIsChartex) {
1830 pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "0");
1831 }
1832 }
1833 else if (!bIsChartex) {
1834 pFS->singleElement(FSNS(XML_c, XML_autoTitleDeleted), XML_val, "1");
1835 }
1836
1837 InitPlotArea( );
1838 if( mbIs3DChart )
1839 {
1840 if (!bIsChartex) {
1841 exportView3D();
1842
1843 // floor
1844 Reference< beans::XPropertySet > xFloor = mxNewDiagram->getFloor();
1845 if( xFloor.is() )
1846 {
1847 pFS->startElement(FSNS(XML_c, XML_floor));
1848 exportShapeProps( xFloor, false );
1849 pFS->endElement( FSNS( XML_c, XML_floor ) );
1850 }
1851
1852 // LibreOffice doesn't distinguish between sideWall and backWall (both are using the same color).
1853 // It is controlled by the same Wall property.
1854 Reference< beans::XPropertySet > xWall = mxNewDiagram->getWall();
1855 if( xWall.is() )
1856 {
1857 // sideWall
1858 pFS->startElement(FSNS(XML_c, XML_sideWall));
1859 exportShapeProps( xWall, false );
1860 pFS->endElement( FSNS( XML_c, XML_sideWall ) );
1861
1862 // backWall
1863 pFS->startElement(FSNS(XML_c, XML_backWall));
1864 exportShapeProps( xWall, false );
1865 pFS->endElement( FSNS( XML_c, XML_backWall ) );
1866 }
1867 }
1868 }
1869 // plot area
1870 exportPlotArea( xChartDoc, bIsChartex );
1871 // legend
1872 if( bHasLegend ) {
1873 exportLegend( xChartDoc, bIsChartex );
1874 }
1875
1876 if (!bIsChartex) {
1877 uno::Reference<beans::XPropertySet> xDiagramPropSet(xChartDoc->getDiagram(), uno::UNO_QUERY);
1878 uno::Any aPlotVisOnly = xDiagramPropSet->getPropertyValue(u"IncludeHiddenCells"_ustr);
1879 bool bIncludeHiddenCells = false;
1880 aPlotVisOnly >>= bIncludeHiddenCells;
1881 pFS->singleElement(FSNS(XML_c, XML_plotVisOnly), XML_val, ToPsz10(!bIncludeHiddenCells));
1882
1883 exportMissingValueTreatment(Reference<beans::XPropertySet>(mxDiagram, uno::UNO_QUERY));
1884 }
1885
1886 pFS->endElement( FSNS( nChartNS, XML_chart ) );
1887 }
1888
exportMissingValueTreatment(const uno::Reference<beans::XPropertySet> & xPropSet)1889 void ChartExport::exportMissingValueTreatment(const uno::Reference<beans::XPropertySet>& xPropSet)
1890 {
1891 if (!xPropSet.is())
1892 return;
1893
1894 sal_Int32 nVal = 0;
1895 uno::Any aAny = xPropSet->getPropertyValue(u"MissingValueTreatment"_ustr);
1896 if (!(aAny >>= nVal))
1897 return;
1898
1899 const char* pVal = nullptr;
1900 switch (nVal)
1901 {
1902 case cssc::MissingValueTreatment::LEAVE_GAP:
1903 pVal = "gap";
1904 break;
1905 case cssc::MissingValueTreatment::USE_ZERO:
1906 pVal = "zero";
1907 break;
1908 case cssc::MissingValueTreatment::CONTINUE:
1909 pVal = "span";
1910 break;
1911 default:
1912 SAL_WARN("oox", "unknown MissingValueTreatment value");
1913 break;
1914 }
1915
1916 FSHelperPtr pFS = GetFS();
1917 pFS->singleElement(FSNS(XML_c, XML_dispBlanksAs), XML_val, pVal);
1918 }
1919
exportLegend(const Reference<css::chart::XChartDocument> & xChartDoc,bool bIsChartex)1920 void ChartExport::exportLegend( const Reference< css::chart::XChartDocument >& xChartDoc,
1921 bool bIsChartex)
1922 {
1923 FSHelperPtr pFS = GetFS();
1924
1925 Reference< beans::XPropertySet > xProp( xChartDoc->getLegend(), uno::UNO_QUERY );
1926 if( xProp.is() )
1927 {
1928 if (!bIsChartex) {
1929 pFS->startElement(FSNS(XML_c, XML_legend));
1930 }
1931
1932 // position
1933 css::chart::ChartLegendPosition aLegendPos = css::chart::ChartLegendPosition_NONE;
1934 try
1935 {
1936 Any aAny( xProp->getPropertyValue( u"Alignment"_ustr ));
1937 aAny >>= aLegendPos;
1938 }
1939 catch( beans::UnknownPropertyException & )
1940 {
1941 SAL_WARN("oox", "Property Align not found in ChartLegend");
1942 }
1943
1944 const char* strPos = nullptr;
1945 switch( aLegendPos )
1946 {
1947 case css::chart::ChartLegendPosition_LEFT:
1948 strPos = "l";
1949 break;
1950 case css::chart::ChartLegendPosition_RIGHT:
1951 strPos = "r";
1952 break;
1953 case css::chart::ChartLegendPosition_TOP:
1954 strPos = "t";
1955 break;
1956 case css::chart::ChartLegendPosition_BOTTOM:
1957 strPos = "b";
1958 break;
1959 case css::chart::ChartLegendPosition_NONE:
1960 case css::chart::ChartLegendPosition::ChartLegendPosition_MAKE_FIXED_SIZE:
1961 // nothing
1962 break;
1963 }
1964
1965 if (!bIsChartex) {
1966 if( strPos != nullptr )
1967 {
1968 pFS->singleElement(FSNS(XML_c, XML_legendPos), XML_val, strPos);
1969 }
1970
1971 // legendEntry
1972 Reference<chart2::XCoordinateSystemContainer> xCooSysContainer(mxNewDiagram, UNO_QUERY_THROW);
1973 const Sequence<Reference<chart2::XCoordinateSystem>> xCooSysSequence(xCooSysContainer->getCoordinateSystems());
1974
1975 sal_Int32 nIndex = 0;
1976 bool bShowLegendEntry;
1977 for (const auto& rCooSys : xCooSysSequence)
1978 {
1979 PropertySet aCooSysProp(rCooSys);
1980 bool bSwapXAndY = aCooSysProp.getBoolProperty(PROP_SwapXAndYAxis);
1981
1982 Reference<chart2::XChartTypeContainer> xChartTypeContainer(rCooSys, UNO_QUERY_THROW);
1983 const Sequence<Reference<chart2::XChartType>> xChartTypeSequence(xChartTypeContainer->getChartTypes());
1984 if (!xChartTypeSequence.hasElements())
1985 continue;
1986
1987 for (const auto& rCT : xChartTypeSequence)
1988 {
1989 Reference<chart2::XDataSeriesContainer> xDSCont(rCT, UNO_QUERY);
1990 if (!xDSCont.is())
1991 continue;
1992
1993 OUString aChartType(rCT->getChartType());
1994 bool bIsPie = lcl_getChartType(aChartType) == chart::TYPEID_PIE;
1995 if (bIsPie)
1996 {
1997 PropertySet xChartTypeProp(rCT);
1998 bIsPie = !xChartTypeProp.getBoolProperty(PROP_UseRings);
1999 }
2000 const Sequence<Reference<chart2::XDataSeries>> aDataSeriesSeq = xDSCont->getDataSeries();
2001 if (bSwapXAndY)
2002 nIndex += aDataSeriesSeq.getLength() - 1;
2003 for (const auto& rDataSeries : aDataSeriesSeq)
2004 {
2005 PropertySet aSeriesProp(rDataSeries);
2006 bool bVaryColorsByPoint = aSeriesProp.getBoolProperty(PROP_VaryColorsByPoint);
2007 if (bVaryColorsByPoint || bIsPie)
2008 {
2009 Sequence<sal_Int32> deletedLegendEntriesSeq;
2010 aSeriesProp.getProperty(deletedLegendEntriesSeq, PROP_DeletedLegendEntries);
2011 for (const auto& deletedLegendEntry : std::as_const(deletedLegendEntriesSeq))
2012 {
2013 pFS->startElement(FSNS(XML_c, XML_legendEntry));
2014 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val,
2015 OString::number(nIndex + deletedLegendEntry));
2016 pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, "1");
2017 pFS->endElement(FSNS(XML_c, XML_legendEntry));
2018 }
2019 Reference<chart2::data::XDataSource> xDSrc(rDataSeries, UNO_QUERY);
2020 if (!xDSrc.is())
2021 continue;
2022
2023 const Sequence<Reference<chart2::data::XLabeledDataSequence>> aDataSeqs = xDSrc->getDataSequences();
2024 for (const auto& rDataSeq : aDataSeqs)
2025 {
2026 Reference<chart2::data::XDataSequence> xValues = rDataSeq->getValues();
2027 if (!xValues.is())
2028 continue;
2029
2030 sal_Int32 nDataSeqSize = xValues->getData().getLength();
2031 nIndex += nDataSeqSize;
2032 }
2033 }
2034 else
2035 {
2036 bShowLegendEntry = aSeriesProp.getBoolProperty(PROP_ShowLegendEntry);
2037 if (!bShowLegendEntry)
2038 {
2039 pFS->startElement(FSNS(XML_c, XML_legendEntry));
2040 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val,
2041 OString::number(nIndex));
2042 pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, "1");
2043 pFS->endElement(FSNS(XML_c, XML_legendEntry));
2044 }
2045 bSwapXAndY ? nIndex-- : nIndex++;
2046 }
2047 }
2048 if (bSwapXAndY)
2049 nIndex += aDataSeriesSeq.getLength() + 1;
2050 }
2051 }
2052
2053 uno::Any aRelativePos = xProp->getPropertyValue(u"RelativePosition"_ustr);
2054 if (aRelativePos.hasValue())
2055 {
2056 pFS->startElement(FSNS(XML_c, XML_layout));
2057 pFS->startElement(FSNS(XML_c, XML_manualLayout));
2058
2059 pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
2060 pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
2061 chart2::RelativePosition aPos = aRelativePos.get<chart2::RelativePosition>();
2062
2063 const double x = aPos.Primary;
2064 const double y = aPos.Secondary;
2065
2066 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
2067 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
2068
2069 uno::Any aRelativeSize = xProp->getPropertyValue(u"RelativeSize"_ustr);
2070 if (aRelativeSize.hasValue())
2071 {
2072 chart2::RelativeSize aSize = aRelativeSize.get<chart2::RelativeSize>();
2073
2074 const double w = aSize.Primary;
2075 const double h = aSize.Secondary;
2076
2077 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, OString::number(w));
2078
2079 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, OString::number(h));
2080 }
2081
2082 SAL_WARN_IF(aPos.Anchor != css::drawing::Alignment_TOP_LEFT, "oox", "unsupported anchor position");
2083
2084 pFS->endElement(FSNS(XML_c, XML_manualLayout));
2085 pFS->endElement(FSNS(XML_c, XML_layout));
2086 }
2087 }
2088
2089 const char *sOverlay = nullptr;
2090 if (strPos != nullptr)
2091 {
2092 uno::Any aOverlay = xProp->getPropertyValue(u"Overlay"_ustr);
2093 if(aOverlay.get<bool>())
2094 sOverlay = "1";
2095 else
2096 sOverlay = "0";
2097 }
2098
2099 if (bIsChartex) {
2100 pFS->startElement(FSNS(XML_cx, XML_legend),
2101 XML_pos, strPos ? strPos : "r",
2102 XML_align, "ctr", // is this supported?
2103 XML_overlay, sOverlay ? sOverlay : "0");
2104 } else {
2105 pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, sOverlay);
2106 }
2107
2108 // shape properties
2109 exportShapeProps( xProp, bIsChartex );
2110
2111 // draw-chart:txPr text properties
2112 exportTextProps( xProp, bIsChartex );
2113
2114 if (bIsChartex) {
2115 pFS->endElement( FSNS( XML_cx, XML_legend ) );
2116 } else {
2117 pFS->endElement( FSNS( XML_c, XML_legend ) );
2118 }
2119 }
2120 }
2121
exportTitle(const Reference<XShape> & xShape,bool bIsChartex,const css::uno::Sequence<uno::Reference<css::chart2::XFormattedString>> & xFormattedSubTitle)2122 void ChartExport::exportTitle( const Reference< XShape >& xShape, bool bIsChartex,
2123 const css::uno::Sequence< uno::Reference< css::chart2::XFormattedString > >& xFormattedSubTitle )
2124 {
2125 Sequence< uno::Reference< chart2::XFormattedString > > xFormattedTitle;
2126 Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
2127 if( xPropSet.is())
2128 {
2129 OUString aTitle;
2130 if ((xPropSet->getPropertyValue(u"String"_ustr) >>= aTitle) && !aTitle.isEmpty())
2131 xPropSet->getPropertyValue(u"FormattedStrings"_ustr) >>= xFormattedTitle;
2132 }
2133
2134 // tdf#101322: add subtitle to title
2135 if (xFormattedSubTitle.hasElements())
2136 {
2137 if (!xFormattedTitle.hasElements())
2138 {
2139 xFormattedTitle = xFormattedSubTitle;
2140 }
2141 else
2142 {
2143 sal_uInt32 nLength = xFormattedTitle.size();
2144 const OUString aLastString = xFormattedTitle.getArray()[nLength - 1]->getString();
2145 xFormattedTitle.getArray()[nLength - 1]->setString(aLastString + OUStringChar('\n'));
2146 for (const uno::Reference<chart2::XFormattedString>& rxFS : xFormattedSubTitle)
2147 {
2148 if (!rxFS->getString().isEmpty())
2149 {
2150 xFormattedTitle.realloc(nLength + 1);
2151 xFormattedTitle.getArray()[nLength++] = rxFS;
2152 }
2153 }
2154 }
2155 }
2156
2157 if (!xFormattedTitle.hasElements())
2158 return;
2159
2160 FSHelperPtr pFS = GetFS();
2161
2162 if (bIsChartex) {
2163 pFS->startElement(FSNS(XML_cx, XML_title));
2164 lcl_writeChartexString(pFS, xFormattedTitle[0]->getString());
2165 } else {
2166 pFS->startElement(FSNS(XML_c, XML_title));
2167 pFS->startElement(FSNS(XML_c, XML_tx));
2168 pFS->startElement(FSNS(XML_c, XML_rich));
2169 }
2170
2171 if (bIsChartex) {
2172 // shape properties
2173 if( xPropSet.is() )
2174 {
2175 exportShapeProps( xPropSet, bIsChartex );
2176 }
2177
2178 pFS->startElement(FSNS(XML_cx, XML_txPr));
2179 }
2180
2181 // TODO: bodyPr
2182 const char* sWritingMode = nullptr;
2183 bool bVertical = false;
2184 xPropSet->getPropertyValue(u"StackedText"_ustr) >>= bVertical;
2185 if( bVertical )
2186 sWritingMode = "wordArtVert";
2187
2188 sal_Int32 nRotation = 0;
2189 xPropSet->getPropertyValue(u"TextRotation"_ustr) >>= nRotation;
2190
2191 pFS->singleElement( FSNS( XML_a, XML_bodyPr ),
2192 XML_vert, sWritingMode,
2193 XML_rot, oox::drawingml::calcRotationValue(nRotation) );
2194 // TODO: lstStyle
2195 pFS->singleElement(FSNS(XML_a, XML_lstStyle));
2196 pFS->startElement(FSNS(XML_a, XML_p));
2197
2198 pFS->startElement(FSNS(XML_a, XML_pPr));
2199
2200 bool bDummy = false;
2201 sal_Int32 nDummy;
2202 WriteRunProperties(xPropSet, false, XML_defRPr, true, bDummy, nDummy );
2203
2204 pFS->endElement( FSNS( XML_a, XML_pPr ) );
2205
2206 for (const uno::Reference<chart2::XFormattedString>& rxFS : xFormattedTitle)
2207 {
2208 pFS->startElement(FSNS(XML_a, XML_r));
2209 bDummy = false;
2210 Reference< beans::XPropertySet > xRunPropSet(rxFS, uno::UNO_QUERY);
2211 WriteRunProperties(xRunPropSet, false, XML_rPr, true, bDummy, nDummy);
2212 pFS->startElement(FSNS(XML_a, XML_t));
2213
2214 // the linebreak should always be at the end of the XFormattedString text
2215 bool bNextPara = rxFS->getString().endsWith(u"\n");
2216 if (!bNextPara)
2217 pFS->writeEscaped(rxFS->getString());
2218 else
2219 {
2220 sal_Int32 nEnd = rxFS->getString().lastIndexOf('\n');
2221 pFS->writeEscaped(rxFS->getString().replaceAt(nEnd, 1, u""));
2222 }
2223 pFS->endElement(FSNS(XML_a, XML_t));
2224 pFS->endElement(FSNS(XML_a, XML_r));
2225
2226 if (bNextPara)
2227 {
2228 pFS->endElement(FSNS(XML_a, XML_p));
2229
2230 pFS->startElement(FSNS(XML_a, XML_p));
2231 pFS->startElement(FSNS(XML_a, XML_pPr));
2232 bDummy = false;
2233 WriteRunProperties(xPropSet, false, XML_defRPr, true, bDummy, nDummy);
2234 pFS->endElement(FSNS(XML_a, XML_pPr));
2235 }
2236 }
2237
2238 pFS->endElement( FSNS( XML_a, XML_p ) );
2239
2240 if (bIsChartex) {
2241 pFS->endElement( FSNS( XML_cx, XML_txPr ) );
2242 } else {
2243 pFS->endElement( FSNS( XML_c, XML_rich ) );
2244 pFS->endElement( FSNS( XML_c, XML_tx ) );
2245 }
2246
2247 uno::Any aManualLayout = xPropSet->getPropertyValue(u"RelativePosition"_ustr);
2248 if (aManualLayout.hasValue())
2249 {
2250 if (bIsChartex) {
2251 // TODO. Chartex doesn't have a manualLayout tag, but does have
2252 // "pos" and "align" attributes. Not sure how these correspond.
2253 } else {
2254 pFS->startElement(FSNS(XML_c, XML_layout));
2255 pFS->startElement(FSNS(XML_c, XML_manualLayout));
2256 pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
2257 pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
2258
2259 Reference<embed::XVisualObject> xVisObject(mxChartModel, uno::UNO_QUERY);
2260 awt::Size aPageSize = xVisObject->getVisualAreaSize(embed::Aspects::MSOLE_CONTENT);
2261
2262 awt::Size aSize = xShape->getSize();
2263 awt::Point aPos2 = xShape->getPosition();
2264 // rotated shapes need special handling...
2265 double fSin = fabs(sin(basegfx::deg2rad<100>(nRotation)));
2266 // remove part of height from X direction, if title is rotated down
2267 if( nRotation*0.01 > 180.0 )
2268 aPos2.X -= static_cast<sal_Int32>(fSin * aSize.Height + 0.5);
2269 // remove part of width from Y direction, if title is rotated up
2270 else if( nRotation*0.01 > 0.0 )
2271 aPos2.Y -= static_cast<sal_Int32>(fSin * aSize.Width + 0.5);
2272
2273 double x = static_cast<double>(aPos2.X) / static_cast<double>(aPageSize.Width);
2274 double y = static_cast<double>(aPos2.Y) / static_cast<double>(aPageSize.Height);
2275 /*
2276 pFS->singleElement(FSNS(XML_c, XML_wMode), XML_val, "edge");
2277 pFS->singleElement(FSNS(XML_c, XML_hMode), XML_val, "edge");
2278 */
2279 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
2280 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
2281 /*
2282 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, "");
2283 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, "");
2284 */
2285 pFS->endElement(FSNS(XML_c, XML_manualLayout));
2286 pFS->endElement(FSNS(XML_c, XML_layout));
2287 }
2288 }
2289
2290 if (!bIsChartex) {
2291 pFS->singleElement(FSNS(XML_c, XML_overlay), XML_val, "0");
2292 }
2293
2294 if (!bIsChartex) {
2295 // shape properties
2296 if( xPropSet.is() )
2297 {
2298 exportShapeProps( xPropSet, bIsChartex );
2299 }
2300 }
2301
2302 if (bIsChartex) {
2303 pFS->endElement( FSNS( XML_cx, XML_title ) );
2304 } else {
2305 pFS->endElement( FSNS( XML_c, XML_title ) );
2306 }
2307 }
2308
exportPlotArea(const Reference<css::chart::XChartDocument> & xChartDoc,bool bIsChartex)2309 void ChartExport::exportPlotArea(const Reference< css::chart::XChartDocument >& xChartDoc,
2310 bool bIsChartex)
2311 {
2312 Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( mxNewDiagram, uno::UNO_QUERY );
2313 if( ! xBCooSysCnt.is())
2314 return;
2315
2316 // plot-area element
2317
2318 FSHelperPtr pFS = GetFS();
2319
2320 if (bIsChartex) {
2321 pFS->startElement(FSNS(XML_cx, XML_plotArea));
2322 pFS->startElement(FSNS(XML_cx, XML_plotAreaRegion));
2323 } else {
2324 pFS->startElement(FSNS(XML_c, XML_plotArea));
2325
2326 Reference<beans::XPropertySet> xWall(mxNewDiagram, uno::UNO_QUERY);
2327 if( xWall.is() )
2328 {
2329 uno::Any aAny = xWall->getPropertyValue(u"RelativePosition"_ustr);
2330 if (aAny.hasValue())
2331 {
2332 chart2::RelativePosition aPos = aAny.get<chart2::RelativePosition>();
2333 aAny = xWall->getPropertyValue(u"RelativeSize"_ustr);
2334 chart2::RelativeSize aSize = aAny.get<chart2::RelativeSize>();
2335 uno::Reference< css::chart::XDiagramPositioning > xDiagramPositioning( xChartDoc->getDiagram(), uno::UNO_QUERY );
2336 exportManualLayout(aPos, aSize, xDiagramPositioning->isExcludingDiagramPositioning() );
2337 }
2338 }
2339 }
2340
2341 // chart type
2342 const Sequence< Reference< chart2::XCoordinateSystem > >
2343 aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
2344
2345 // tdf#123647 Save empty chart as empty bar chart.
2346 if (!aCooSysSeq.hasElements())
2347 {
2348 assert(!bIsChartex);
2349
2350 pFS->startElement(FSNS(XML_c, XML_barChart));
2351 pFS->singleElement(FSNS(XML_c, XML_barDir), XML_val, "col");
2352 pFS->singleElement(FSNS(XML_c, XML_grouping), XML_val, "clustered");
2353 pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, "0");
2354 createAxes(true, false);
2355 pFS->endElement(FSNS(XML_c, XML_barChart));
2356 }
2357
2358 for( const auto& rCS : aCooSysSeq )
2359 {
2360 Reference< chart2::XChartTypeContainer > xCTCnt( rCS, uno::UNO_QUERY );
2361 if( ! xCTCnt.is())
2362 continue;
2363 mnSeriesCount=0;
2364 const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
2365 for( const auto& rCT : aCTSeq )
2366 {
2367 Reference< chart2::XDataSeriesContainer > xDSCnt( rCT, uno::UNO_QUERY );
2368 if( ! xDSCnt.is())
2369 return;
2370 Reference< chart2::XChartType > xChartType( rCT, uno::UNO_QUERY );
2371 if( ! xChartType.is())
2372 continue;
2373 // note: if xDSCnt.is() then also aCTSeq[nCTIdx]
2374 OUString aChartType( xChartType->getChartType());
2375 sal_Int32 eChartType = lcl_getChartType( aChartType );
2376 switch( eChartType )
2377 {
2378 case chart::TYPEID_BAR:
2379 {
2380 exportBarChart( xChartType );
2381 break;
2382 }
2383 case chart::TYPEID_AREA:
2384 {
2385 exportAreaChart( xChartType );
2386 break;
2387 }
2388 case chart::TYPEID_LINE:
2389 {
2390 exportLineChart( xChartType );
2391 break;
2392 }
2393 case chart::TYPEID_BUBBLE:
2394 {
2395 exportBubbleChart( xChartType );
2396 break;
2397 }
2398 case chart::TYPEID_FUNNEL:
2399 {
2400 exportFunnelChart( xChartType );
2401 break;
2402 }
2403 case chart::TYPEID_DOUGHNUT: // doesn't currently happen
2404 case chart::TYPEID_OFPIE: // doesn't currently happen
2405 case chart::TYPEID_PIE:
2406 {
2407 sal_Int32 eCT = getChartType( );
2408 if(eCT == chart::TYPEID_DOUGHNUT)
2409 {
2410 exportDoughnutChart( xChartType );
2411 }
2412 else
2413 {
2414
2415 PropertySet xChartTypeProp(rCT);
2416 chart2::PieChartSubType subtype(chart2::PieChartSubType_NONE);
2417 if (!xChartTypeProp.getProperty(subtype, PROP_SubPieType))
2418 {
2419 subtype = chart2::PieChartSubType_NONE;
2420 }
2421 if (subtype != chart2::PieChartSubType_NONE)
2422 {
2423 const char* sSubType = "pie"; // default
2424 switch (subtype) {
2425 case chart2::PieChartSubType_PIE:
2426 sSubType = "pie";
2427 break;
2428 case chart2::PieChartSubType_BAR:
2429 sSubType = "bar";
2430 break;
2431 default:
2432 assert(false);
2433 }
2434 double fSplitPos;
2435 if (!xChartTypeProp.getProperty(fSplitPos,
2436 PROP_SplitPos)) {
2437 fSplitPos = 2;
2438 }
2439
2440 exportOfPieChart(xChartType, sSubType, fSplitPos);
2441 } else {
2442 exportPieChart( xChartType );
2443 }
2444 }
2445 break;
2446 }
2447 case chart::TYPEID_RADARLINE:
2448 case chart::TYPEID_RADARAREA:
2449 {
2450 exportRadarChart( xChartType );
2451 break;
2452 }
2453 case chart::TYPEID_SCATTER:
2454 {
2455 exportScatterChart( xChartType );
2456 break;
2457 }
2458 case chart::TYPEID_STOCK:
2459 {
2460 exportStockChart( xChartType );
2461 break;
2462 }
2463 case chart::TYPEID_SURFACE:
2464 {
2465 exportSurfaceChart( xChartType );
2466 break;
2467 }
2468 default:
2469 {
2470 SAL_WARN("oox", "ChartExport::exportPlotArea -- not support chart type");
2471 break;
2472 }
2473 }
2474
2475 }
2476 }
2477
2478 if (bIsChartex) {
2479 pFS->endElement( FSNS( XML_cx, XML_plotAreaRegion ) );
2480 }
2481
2482 //Axis Data
2483 exportAxes(bIsChartex);
2484
2485 if (!bIsChartex) {
2486 // Data Table
2487 // not supported in chartex?
2488 exportDataTable();
2489 }
2490
2491 // shape properties
2492 /*
2493 * Export the Plot area Shape Properties
2494 * eg: Fill and Outline
2495 */
2496 Reference< css::chart::X3DDisplay > xWallFloorSupplier( mxDiagram, uno::UNO_QUERY );
2497 // tdf#114139 For 2D charts Plot Area equivalent is Chart Wall.
2498 // Unfortunately LibreOffice doesn't have Plot Area equivalent for 3D charts.
2499 // It means that Plot Area couldn't be displayed and changed for 3D chars in LibreOffice.
2500 // We cannot write Wall attributes into Plot Area for 3D charts, because Wall us used as background wall.
2501 if( !mbIs3DChart && xWallFloorSupplier.is() )
2502 {
2503 Reference< beans::XPropertySet > xWallPropSet = xWallFloorSupplier->getWall();
2504 if( xWallPropSet.is() )
2505 {
2506 uno::Any aAny = xWallPropSet->getPropertyValue(u"LineStyle"_ustr);
2507 sal_Int32 eChartType = getChartType( );
2508 // Export LineStyle_NONE instead of default linestyle of PlotArea border, because LibreOffice
2509 // make invisible the Wall shape properties, in case of these charts. Or in the future set
2510 // the default LineStyle of these charts to LineStyle_NONE.
2511 bool noSupportWallProp = ( (eChartType == chart::TYPEID_PIE) || (eChartType == chart::TYPEID_RADARLINE) || (eChartType == chart::TYPEID_RADARAREA) );
2512 if ( noSupportWallProp && (aAny != drawing::LineStyle_NONE) )
2513 {
2514 xWallPropSet->setPropertyValue( u"LineStyle"_ustr, uno::Any(drawing::LineStyle_NONE) );
2515 }
2516 exportShapeProps( xWallPropSet, bIsChartex );
2517 }
2518 }
2519
2520 if (bIsChartex) {
2521 pFS->endElement( FSNS( XML_cx, XML_plotArea ) );
2522 } else {
2523 pFS->endElement( FSNS( XML_c, XML_plotArea ) );
2524 }
2525
2526 }
2527
exportManualLayout(const css::chart2::RelativePosition & rPos,const css::chart2::RelativeSize & rSize,const bool bIsExcludingDiagramPositioning)2528 void ChartExport::exportManualLayout(const css::chart2::RelativePosition& rPos,
2529 const css::chart2::RelativeSize& rSize,
2530 const bool bIsExcludingDiagramPositioning)
2531 {
2532 // 2006 chart schema only
2533 FSHelperPtr pFS = GetFS();
2534 pFS->startElement(FSNS(XML_c, XML_layout));
2535 pFS->startElement(FSNS(XML_c, XML_manualLayout));
2536
2537 // By default layoutTarget is set to "outer" and we shouldn't save it in that case
2538 if ( bIsExcludingDiagramPositioning )
2539 {
2540 pFS->singleElement(FSNS(XML_c, XML_layoutTarget), XML_val, "inner");
2541 }
2542 pFS->singleElement(FSNS(XML_c, XML_xMode), XML_val, "edge");
2543 pFS->singleElement(FSNS(XML_c, XML_yMode), XML_val, "edge");
2544
2545 double x = rPos.Primary;
2546 double y = rPos.Secondary;
2547 const double w = rSize.Primary;
2548 const double h = rSize.Secondary;
2549 switch (rPos.Anchor)
2550 {
2551 case drawing::Alignment_LEFT:
2552 y -= (h/2);
2553 break;
2554 case drawing::Alignment_TOP_LEFT:
2555 break;
2556 case drawing::Alignment_BOTTOM_LEFT:
2557 y -= h;
2558 break;
2559 case drawing::Alignment_TOP:
2560 x -= (w/2);
2561 break;
2562 case drawing::Alignment_CENTER:
2563 x -= (w/2);
2564 y -= (h/2);
2565 break;
2566 case drawing::Alignment_BOTTOM:
2567 x -= (w/2);
2568 y -= h;
2569 break;
2570 case drawing::Alignment_TOP_RIGHT:
2571 x -= w;
2572 break;
2573 case drawing::Alignment_BOTTOM_RIGHT:
2574 x -= w;
2575 y -= h;
2576 break;
2577 case drawing::Alignment_RIGHT:
2578 y -= (h/2);
2579 x -= w;
2580 break;
2581 default:
2582 SAL_WARN("oox", "unhandled alignment case for manual layout export " << static_cast<sal_uInt16>(rPos.Anchor));
2583 }
2584
2585 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(x));
2586
2587 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(y));
2588
2589 pFS->singleElement(FSNS(XML_c, XML_w), XML_val, OString::number(w));
2590
2591 pFS->singleElement(FSNS(XML_c, XML_h), XML_val, OString::number(h));
2592
2593 pFS->endElement(FSNS(XML_c, XML_manualLayout));
2594 pFS->endElement(FSNS(XML_c, XML_layout));
2595 }
2596
exportFill(const Reference<XPropertySet> & xPropSet)2597 void ChartExport::exportFill( const Reference< XPropertySet >& xPropSet )
2598 {
2599 // Similar to DrawingML::WriteFill, but gradient access via name
2600 if (!GetProperty( xPropSet, u"FillStyle"_ustr ))
2601 return;
2602 FillStyle aFillStyle(FillStyle_NONE);
2603 mAny >>= aFillStyle;
2604
2605 // map full transparent background to no fill
2606 if (aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, u"FillTransparence"_ustr ))
2607 {
2608 sal_Int16 nVal = 0;
2609 mAny >>= nVal;
2610 if ( nVal == 100 )
2611 aFillStyle = FillStyle_NONE;
2612 }
2613 OUString sFillTransparenceGradientName;
2614 if (aFillStyle == FillStyle_SOLID
2615 && GetProperty(xPropSet, u"FillTransparenceGradientName"_ustr) && (mAny >>= sFillTransparenceGradientName)
2616 && !sFillTransparenceGradientName.isEmpty())
2617 {
2618 awt::Gradient aTransparenceGradient;
2619 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
2620 uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance(u"com.sun.star.drawing.TransparencyGradientTable"_ustr), uno::UNO_QUERY);
2621 uno::Any rTransparenceValue = xTransparenceGradient->getByName(sFillTransparenceGradientName);
2622 rTransparenceValue >>= aTransparenceGradient;
2623 if (aTransparenceGradient.StartColor == 0xffffff && aTransparenceGradient.EndColor == 0xffffff)
2624 aFillStyle = FillStyle_NONE;
2625 }
2626 switch( aFillStyle )
2627 {
2628 case FillStyle_SOLID:
2629 exportSolidFill(xPropSet);
2630 break;
2631 case FillStyle_GRADIENT :
2632 exportGradientFill( xPropSet );
2633 break;
2634 case FillStyle_BITMAP :
2635 exportBitmapFill( xPropSet );
2636 break;
2637 case FillStyle_HATCH:
2638 exportHatch(xPropSet);
2639 break;
2640 case FillStyle_NONE:
2641 mpFS->singleElementNS(XML_a, XML_noFill);
2642 break;
2643 default:
2644 ;
2645 }
2646 }
2647
exportSolidFill(const Reference<XPropertySet> & xPropSet)2648 void ChartExport::exportSolidFill(const Reference< XPropertySet >& xPropSet)
2649 {
2650 // Similar to DrawingML::WriteSolidFill, but gradient access via name
2651 // and currently no InteropGrabBag
2652 // get fill color
2653 sal_uInt32 nFillColor = 0;
2654 if (!GetProperty(xPropSet, u"FillColor"_ustr) || !(mAny >>= nFillColor))
2655 return;
2656
2657 sal_Int32 nAlpha = MAX_PERCENT;
2658 if (GetProperty( xPropSet, u"FillTransparence"_ustr ))
2659 {
2660 sal_Int32 nTransparency = 0;
2661 mAny >>= nTransparency;
2662 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
2663 nAlpha = (MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
2664 }
2665 // OOXML has no separate transparence gradient but uses transparency in the gradient stops.
2666 // So we merge transparency and color and use gradient fill in such case.
2667 basegfx::BGradient aTransparenceGradient;
2668 bool bNeedGradientFill(false);
2669 OUString sFillTransparenceGradientName;
2670
2671 if (GetProperty(xPropSet, u"FillTransparenceGradientName"_ustr)
2672 && (mAny >>= sFillTransparenceGradientName)
2673 && !sFillTransparenceGradientName.isEmpty())
2674 {
2675 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
2676 uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance(u"com.sun.star.drawing.TransparencyGradientTable"_ustr), uno::UNO_QUERY);
2677 const uno::Any rTransparenceAny = xTransparenceGradient->getByName(sFillTransparenceGradientName);
2678
2679 aTransparenceGradient = model::gradient::getFromAny(rTransparenceAny);
2680 basegfx::BColor aSingleColor;
2681 bNeedGradientFill = !aTransparenceGradient.GetColorStops().isSingleColor(aSingleColor);
2682
2683 if (!bNeedGradientFill)
2684 {
2685 // Our alpha is a single gray color value.
2686 const sal_uInt8 nRed(aSingleColor.getRed() * 255.0);
2687
2688 // drawingML alpha is a percentage on a 0..100000 scale.
2689 nAlpha = (255 - nRed) * oox::drawingml::MAX_PERCENT / 255;
2690 }
2691 }
2692 // write XML
2693 if (bNeedGradientFill)
2694 {
2695 // no longer create copy/PseudoColorGradient, use new API of
2696 // WriteGradientFill to express fix fill color
2697 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
2698 WriteGradientFill(nullptr, nFillColor, &aTransparenceGradient);
2699 mpFS->endElementNS(XML_a, XML_gradFill);
2700 }
2701 else
2702 WriteSolidFill(::Color(ColorTransparency, nFillColor & 0xffffff), nAlpha);
2703 }
2704
exportHatch(const Reference<XPropertySet> & xPropSet)2705 void ChartExport::exportHatch( const Reference< XPropertySet >& xPropSet )
2706 {
2707 if (!xPropSet.is())
2708 return;
2709
2710 if (GetProperty(xPropSet, u"FillHatchName"_ustr))
2711 {
2712 OUString aHatchName;
2713 mAny >>= aHatchName;
2714 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
2715 uno::Reference< container::XNameAccess > xHatchTable( xFact->createInstance(u"com.sun.star.drawing.HatchTable"_ustr), uno::UNO_QUERY );
2716 uno::Any rValue = xHatchTable->getByName(aHatchName);
2717 css::drawing::Hatch aHatch;
2718 rValue >>= aHatch;
2719 WritePattFill(xPropSet, aHatch);
2720 }
2721
2722 }
2723
exportBitmapFill(const Reference<XPropertySet> & xPropSet)2724 void ChartExport::exportBitmapFill( const Reference< XPropertySet >& xPropSet )
2725 {
2726 if( !xPropSet.is() )
2727 return;
2728
2729 OUString sFillBitmapName;
2730 xPropSet->getPropertyValue(u"FillBitmapName"_ustr) >>= sFillBitmapName;
2731
2732 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
2733 try
2734 {
2735 uno::Reference< container::XNameAccess > xBitmapTable( xFact->createInstance(u"com.sun.star.drawing.BitmapTable"_ustr), uno::UNO_QUERY );
2736 uno::Any rValue = xBitmapTable->getByName( sFillBitmapName );
2737 if (rValue.has<uno::Reference<awt::XBitmap>>())
2738 {
2739 uno::Reference<awt::XBitmap> xBitmap = rValue.get<uno::Reference<awt::XBitmap>>();
2740 uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY);
2741 if (xGraphic.is())
2742 {
2743 WriteXGraphicBlipFill(xPropSet, xGraphic, XML_a, true, true);
2744 }
2745 }
2746 }
2747 catch (const uno::Exception &)
2748 {
2749 TOOLS_WARN_EXCEPTION("oox", "ChartExport::exportBitmapFill");
2750 }
2751 }
2752
exportGradientFill(const Reference<XPropertySet> & xPropSet)2753 void ChartExport::exportGradientFill( const Reference< XPropertySet >& xPropSet )
2754 {
2755 if( !xPropSet.is() )
2756 return;
2757
2758 OUString sFillGradientName;
2759 xPropSet->getPropertyValue(u"FillGradientName"_ustr) >>= sFillGradientName;
2760
2761 uno::Reference< lang::XMultiServiceFactory > xFact( getModel(), uno::UNO_QUERY );
2762 try
2763 {
2764 uno::Reference< container::XNameAccess > xGradient( xFact->createInstance(u"com.sun.star.drawing.GradientTable"_ustr), uno::UNO_QUERY );
2765 const uno::Any rGradientAny(xGradient->getByName( sFillGradientName ));
2766 const basegfx::BGradient aGradient = model::gradient::getFromAny(rGradientAny);
2767 basegfx::BColor aSingleColor;
2768
2769 if (!aGradient.GetColorStops().isSingleColor(aSingleColor))
2770 {
2771 basegfx::BGradient aTransparenceGradient;
2772 mpFS->startElementNS(XML_a, XML_gradFill);
2773 OUString sFillTransparenceGradientName;
2774
2775 if( (xPropSet->getPropertyValue(u"FillTransparenceGradientName"_ustr) >>= sFillTransparenceGradientName) && !sFillTransparenceGradientName.isEmpty())
2776 {
2777 uno::Reference< container::XNameAccess > xTransparenceGradient(xFact->createInstance(u"com.sun.star.drawing.TransparencyGradientTable"_ustr), uno::UNO_QUERY);
2778 const uno::Any rTransparenceAny(xTransparenceGradient->getByName(sFillTransparenceGradientName));
2779
2780 aTransparenceGradient = model::gradient::getFromAny(rTransparenceAny);
2781
2782 WriteGradientFill(&aGradient, 0, &aTransparenceGradient);
2783 }
2784 else if (GetProperty(xPropSet, u"FillTransparence"_ustr) )
2785 {
2786 // no longer create PseudoTransparencyGradient, use new API of
2787 // WriteGradientFill to express fix transparency
2788 sal_Int32 nTransparency = 0;
2789 mAny >>= nTransparency;
2790 // nTransparency is [0..100]%
2791 WriteGradientFill(&aGradient, 0, nullptr, nTransparency * 0.01);
2792 }
2793 else
2794 {
2795 WriteGradientFill(&aGradient, 0, nullptr);
2796 }
2797
2798 mpFS->endElementNS(XML_a, XML_gradFill);
2799 }
2800 }
2801 catch (const uno::Exception &)
2802 {
2803 TOOLS_INFO_EXCEPTION("oox", "ChartExport::exportGradientFill");
2804 }
2805 }
2806
exportDataTable()2807 void ChartExport::exportDataTable( )
2808 {
2809 // Not supported in chartex 2014 schema
2810 auto xDataTable = mxNewDiagram->getDataTable();
2811 if (!xDataTable.is())
2812 return;
2813
2814 FSHelperPtr pFS = GetFS();
2815 uno::Reference<beans::XPropertySet> aPropSet(xDataTable, uno::UNO_QUERY);
2816
2817 bool bShowVBorder = false;
2818 bool bShowHBorder = false;
2819 bool bShowOutline = false;
2820 bool bShowKeys = false;
2821
2822 if (GetProperty(aPropSet, u"HBorder"_ustr))
2823 mAny >>= bShowHBorder;
2824 if (GetProperty(aPropSet, u"VBorder"_ustr))
2825 mAny >>= bShowVBorder;
2826 if (GetProperty(aPropSet, u"Outline"_ustr))
2827 mAny >>= bShowOutline;
2828 if (GetProperty(aPropSet, u"Keys"_ustr))
2829 mAny >>= bShowKeys;
2830
2831 pFS->startElement(FSNS(XML_c, XML_dTable));
2832
2833 if (bShowHBorder)
2834 pFS->singleElement(FSNS(XML_c, XML_showHorzBorder), XML_val, "1" );
2835 if (bShowVBorder)
2836 pFS->singleElement(FSNS(XML_c, XML_showVertBorder), XML_val, "1");
2837 if (bShowOutline)
2838 pFS->singleElement(FSNS(XML_c, XML_showOutline), XML_val, "1");
2839 if (bShowKeys)
2840 pFS->singleElement(FSNS(XML_c, XML_showKeys), XML_val, "1");
2841
2842 exportShapeProps(aPropSet, false);
2843 exportTextProps(aPropSet, false);
2844
2845 pFS->endElement(FSNS(XML_c, XML_dTable));
2846 }
2847
exportAreaChart(const Reference<chart2::XChartType> & xChartType)2848 void ChartExport::exportAreaChart( const Reference< chart2::XChartType >& xChartType )
2849 {
2850 FSHelperPtr pFS = GetFS();
2851 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2852 for (const auto& splitDataSeries : aSplitDataSeries)
2853 {
2854 if (!splitDataSeries.hasElements())
2855 continue;
2856
2857 sal_Int32 nTypeId = XML_areaChart;
2858 if (mbIs3DChart)
2859 nTypeId = XML_area3DChart;
2860 pFS->startElement(FSNS(XML_c, nTypeId));
2861
2862 exportGrouping();
2863 bool bPrimaryAxes = true;
2864 exportSeries_chart(xChartType, splitDataSeries, bPrimaryAxes);
2865 createAxes(bPrimaryAxes, false);
2866 //exportAxesId(bPrimaryAxes);
2867
2868 pFS->endElement(FSNS(XML_c, nTypeId));
2869 }
2870 }
2871
exportBarChart(const Reference<chart2::XChartType> & xChartType)2872 void ChartExport::exportBarChart(const Reference< chart2::XChartType >& xChartType)
2873 {
2874 sal_Int32 nTypeId = XML_barChart;
2875 if (mbIs3DChart)
2876 nTypeId = XML_bar3DChart;
2877 FSHelperPtr pFS = GetFS();
2878
2879 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2880 for (const auto& splitDataSeries : aSplitDataSeries)
2881 {
2882 if (!splitDataSeries.hasElements())
2883 continue;
2884
2885 pFS->startElement(FSNS(XML_c, nTypeId));
2886 // bar direction
2887 bool bVertical = false;
2888 Reference< XPropertySet > xPropSet(mxDiagram, uno::UNO_QUERY);
2889 if (GetProperty(xPropSet, u"Vertical"_ustr))
2890 mAny >>= bVertical;
2891
2892 const char* bardir = bVertical ? "bar" : "col";
2893 pFS->singleElement(FSNS(XML_c, XML_barDir), XML_val, bardir);
2894
2895 exportGrouping(true);
2896
2897 exportVaryColors(xChartType);
2898
2899 bool bPrimaryAxes = true;
2900 exportSeries_chart(xChartType, splitDataSeries, bPrimaryAxes);
2901
2902 Reference< XPropertySet > xTypeProp(xChartType, uno::UNO_QUERY);
2903
2904 if (xTypeProp.is() && GetProperty(xTypeProp, u"GapwidthSequence"_ustr))
2905 {
2906 uno::Sequence< sal_Int32 > aBarPositionSequence;
2907 mAny >>= aBarPositionSequence;
2908 if (aBarPositionSequence.hasElements())
2909 {
2910 sal_Int32 nGapWidth = aBarPositionSequence[0];
2911 pFS->singleElement(FSNS(XML_c, XML_gapWidth), XML_val, OString::number(nGapWidth));
2912 }
2913 }
2914
2915 if (mbIs3DChart)
2916 {
2917 // Shape
2918 namespace cssc = css::chart;
2919 sal_Int32 nGeom3d = cssc::ChartSolidType::RECTANGULAR_SOLID;
2920 if (xPropSet.is() && GetProperty(xPropSet, u"SolidType"_ustr))
2921 mAny >>= nGeom3d;
2922 const char* sShapeType = nullptr;
2923 switch (nGeom3d)
2924 {
2925 case cssc::ChartSolidType::RECTANGULAR_SOLID:
2926 sShapeType = "box";
2927 break;
2928 case cssc::ChartSolidType::CONE:
2929 sShapeType = "cone";
2930 break;
2931 case cssc::ChartSolidType::CYLINDER:
2932 sShapeType = "cylinder";
2933 break;
2934 case cssc::ChartSolidType::PYRAMID:
2935 sShapeType = "pyramid";
2936 break;
2937 }
2938 pFS->singleElement(FSNS(XML_c, XML_shape), XML_val, sShapeType);
2939 }
2940
2941 //overlap
2942 if (!mbIs3DChart && xTypeProp.is() && GetProperty(xTypeProp, u"OverlapSequence"_ustr))
2943 {
2944 uno::Sequence< sal_Int32 > aBarPositionSequence;
2945 mAny >>= aBarPositionSequence;
2946 if (aBarPositionSequence.hasElements())
2947 {
2948 sal_Int32 nOverlap = aBarPositionSequence[0];
2949 // Stacked/Percent Bar/Column chart Overlap-workaround
2950 // Export the Overlap value with 100% for stacked charts,
2951 // because the default overlap value of the Bar/Column chart is 0% and
2952 // LibreOffice do nothing with the overlap value in Stacked charts case,
2953 // unlike the MS Office, which is interpreted differently.
2954 if ((mbStacked || mbPercent) && nOverlap != 100)
2955 {
2956 nOverlap = 100;
2957 pFS->singleElement(FSNS(XML_c, XML_overlap), XML_val, OString::number(nOverlap));
2958 }
2959 else // Normal bar chart
2960 {
2961 pFS->singleElement(FSNS(XML_c, XML_overlap), XML_val, OString::number(nOverlap));
2962 }
2963 }
2964 }
2965
2966 createAxes(bPrimaryAxes, false);
2967
2968 pFS->endElement(FSNS(XML_c, nTypeId));
2969 }
2970 }
2971
exportBubbleChart(const Reference<chart2::XChartType> & xChartType)2972 void ChartExport::exportBubbleChart( const Reference< chart2::XChartType >& xChartType )
2973 {
2974 FSHelperPtr pFS = GetFS();
2975 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2976 for (const auto& splitDataSeries : aSplitDataSeries)
2977 {
2978 if (!splitDataSeries.hasElements())
2979 continue;
2980
2981 pFS->startElement(FSNS(XML_c, XML_bubbleChart));
2982
2983 exportVaryColors(xChartType);
2984
2985 bool bPrimaryAxes = true;
2986 exportSeries_chart(xChartType, splitDataSeries, bPrimaryAxes);
2987
2988 createAxes(bPrimaryAxes, false);
2989
2990 pFS->endElement(FSNS(XML_c, XML_bubbleChart));
2991 }
2992 }
2993
exportFunnelChart(const Reference<chart2::XChartType> & xChartType)2994 void ChartExport::exportFunnelChart( const Reference< chart2::XChartType >& xChartType )
2995 {
2996 FSHelperPtr pFS = GetFS();
2997 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
2998 for (const auto& splitDataSeries : aSplitDataSeries)
2999 {
3000 if (!splitDataSeries.hasElements())
3001 continue;
3002
3003 //exportVaryColors(xChartType);
3004
3005 exportSeries_chartex(xChartType, splitDataSeries, "funnel");
3006 }
3007 }
3008
exportDoughnutChart(const Reference<chart2::XChartType> & xChartType)3009 void ChartExport::exportDoughnutChart( const Reference< chart2::XChartType >& xChartType )
3010 {
3011 FSHelperPtr pFS = GetFS();
3012 pFS->startElement(FSNS(XML_c, XML_doughnutChart));
3013
3014 exportVaryColors(xChartType);
3015
3016 bool bPrimaryAxes = true;
3017 exportAllSeries(xChartType, bPrimaryAxes);
3018 // firstSliceAng
3019 exportFirstSliceAng( );
3020 //FIXME: holeSize
3021 pFS->singleElement(FSNS(XML_c, XML_holeSize), XML_val, OString::number(50));
3022
3023 pFS->endElement( FSNS( XML_c, XML_doughnutChart ) );
3024 }
3025
exportOfPieChart(const Reference<chart2::XChartType> & xChartType,const char * sSubType,double fSplitPos)3026 void ChartExport::exportOfPieChart(
3027 const Reference< chart2::XChartType >& xChartType,
3028 const char* sSubType,
3029 double fSplitPos)
3030 {
3031 FSHelperPtr pFS = GetFS();
3032 pFS->startElement(FSNS(XML_c, XML_ofPieChart));
3033
3034 pFS->singleElement(FSNS(XML_c, XML_ofPieType), XML_val, sSubType);
3035
3036 exportVaryColors(xChartType);
3037
3038 bool bPrimaryAxes = true;
3039 exportAllSeries(xChartType, bPrimaryAxes);
3040
3041 pFS->singleElement(FSNS(XML_c, XML_splitType), XML_val, "pos");
3042 pFS->singleElement(FSNS(XML_c, XML_splitPos), XML_val, OString::number(fSplitPos));
3043
3044 pFS->endElement( FSNS( XML_c, XML_ofPieChart ) );
3045 }
3046
3047 namespace {
3048
writeDataLabelsRange(const FSHelperPtr & pFS,const XmlFilterBase * pFB,DataLabelsRange & rDLblsRange)3049 void writeDataLabelsRange(const FSHelperPtr& pFS, const XmlFilterBase* pFB, DataLabelsRange& rDLblsRange)
3050 {
3051 if (rDLblsRange.empty())
3052 return;
3053
3054 pFS->startElement(FSNS(XML_c, XML_extLst));
3055 pFS->startElement(FSNS(XML_c, XML_ext), XML_uri, "{02D57815-91ED-43cb-92C2-25804820EDAC}", FSNS(XML_xmlns, XML_c15), pFB->getNamespaceURL(OOX_NS(c15)));
3056 pFS->startElement(FSNS(XML_c15, XML_datalabelsRange));
3057
3058 // Write cell range.
3059 pFS->startElement(FSNS(XML_c15, XML_f));
3060 pFS->writeEscaped(rDLblsRange.getRange());
3061 pFS->endElement(FSNS(XML_c15, XML_f));
3062
3063 // Write all labels.
3064 pFS->startElement(FSNS(XML_c15, XML_dlblRangeCache));
3065 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(rDLblsRange.count()));
3066 for (const auto& rLabelKV: rDLblsRange)
3067 {
3068 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(rLabelKV.first));
3069 pFS->startElement(FSNS(XML_c, XML_v));
3070 pFS->writeEscaped(rLabelKV.second);
3071 pFS->endElement(FSNS( XML_c, XML_v ));
3072 pFS->endElement(FSNS(XML_c, XML_pt));
3073 }
3074
3075 pFS->endElement(FSNS(XML_c15, XML_dlblRangeCache));
3076
3077 pFS->endElement(FSNS(XML_c15, XML_datalabelsRange));
3078 pFS->endElement(FSNS(XML_c, XML_ext));
3079 pFS->endElement(FSNS(XML_c, XML_extLst));
3080 }
3081
3082 }
3083
exportLineChart(const Reference<chart2::XChartType> & xChartType)3084 void ChartExport::exportLineChart( const Reference< chart2::XChartType >& xChartType )
3085 {
3086 FSHelperPtr pFS = GetFS();
3087 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
3088 for (const auto& splitDataSeries : aSplitDataSeries)
3089 {
3090 if (!splitDataSeries.hasElements())
3091 continue;
3092
3093 sal_Int32 nTypeId = XML_lineChart;
3094 if( mbIs3DChart )
3095 nTypeId = XML_line3DChart;
3096 pFS->startElement(FSNS(XML_c, nTypeId));
3097
3098 exportGrouping( );
3099
3100 exportVaryColors(xChartType);
3101 // TODO: show marker symbol in series?
3102 bool bPrimaryAxes = true;
3103 exportSeries_chart(xChartType, splitDataSeries, bPrimaryAxes);
3104
3105 // show marker?
3106 sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE;
3107 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
3108 if( GetProperty( xPropSet, u"SymbolType"_ustr ) )
3109 mAny >>= nSymbolType;
3110
3111 if( !mbIs3DChart )
3112 {
3113 exportHiLowLines();
3114 exportUpDownBars(xChartType);
3115 const char* marker = nSymbolType == css::chart::ChartSymbolType::NONE? "0":"1";
3116 pFS->singleElement(FSNS(XML_c, XML_marker), XML_val, marker);
3117 }
3118
3119 createAxes(bPrimaryAxes, true);
3120
3121 pFS->endElement( FSNS( XML_c, nTypeId ) );
3122 }
3123 }
3124
exportPieChart(const Reference<chart2::XChartType> & xChartType)3125 void ChartExport::exportPieChart( const Reference< chart2::XChartType >& xChartType )
3126 {
3127 FSHelperPtr pFS = GetFS();
3128 sal_Int32 nTypeId = XML_pieChart;
3129 if( mbIs3DChart )
3130 nTypeId = XML_pie3DChart;
3131 pFS->startElement(FSNS(XML_c, nTypeId));
3132
3133 exportVaryColors(xChartType);
3134
3135 bool bPrimaryAxes = true;
3136 exportAllSeries(xChartType, bPrimaryAxes);
3137
3138 if( !mbIs3DChart )
3139 {
3140 // firstSliceAng
3141 exportFirstSliceAng( );
3142 }
3143
3144 pFS->endElement( FSNS( XML_c, nTypeId ) );
3145 }
3146
exportRadarChart(const Reference<chart2::XChartType> & xChartType)3147 void ChartExport::exportRadarChart( const Reference< chart2::XChartType >& xChartType)
3148 {
3149 FSHelperPtr pFS = GetFS();
3150 pFS->startElement(FSNS(XML_c, XML_radarChart));
3151
3152 // radarStyle
3153 sal_Int32 eChartType = getChartType( );
3154 const char* radarStyle = nullptr;
3155 if( eChartType == chart::TYPEID_RADARAREA )
3156 radarStyle = "filled";
3157 else
3158 radarStyle = "marker";
3159 pFS->singleElement(FSNS(XML_c, XML_radarStyle), XML_val, radarStyle);
3160
3161 exportVaryColors(xChartType);
3162 bool bPrimaryAxes = true;
3163 exportAllSeries(xChartType, bPrimaryAxes);
3164 createAxes(bPrimaryAxes, false);
3165
3166 pFS->endElement( FSNS( XML_c, XML_radarChart ) );
3167 }
3168
exportScatterChartSeries(const Reference<chart2::XChartType> & xChartType,const css::uno::Sequence<css::uno::Reference<chart2::XDataSeries>> * pSeries)3169 void ChartExport::exportScatterChartSeries( const Reference< chart2::XChartType >& xChartType,
3170 const css::uno::Sequence<css::uno::Reference<chart2::XDataSeries>>* pSeries)
3171 {
3172 FSHelperPtr pFS = GetFS();
3173 pFS->startElement(FSNS(XML_c, XML_scatterChart));
3174 // TODO:scatterStyle
3175
3176 sal_Int32 nSymbolType = css::chart::ChartSymbolType::NONE;
3177 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
3178 if( GetProperty( xPropSet, u"SymbolType"_ustr ) )
3179 mAny >>= nSymbolType;
3180
3181 const char* scatterStyle = "lineMarker";
3182 if (nSymbolType == css::chart::ChartSymbolType::NONE)
3183 {
3184 scatterStyle = "line";
3185 }
3186
3187 pFS->singleElement(FSNS(XML_c, XML_scatterStyle), XML_val, scatterStyle);
3188
3189 exportVaryColors(xChartType);
3190 // FIXME: should export xVal and yVal
3191 bool bPrimaryAxes = true;
3192 if (pSeries)
3193 exportSeries_chart(xChartType, *pSeries, bPrimaryAxes);
3194 createAxes(bPrimaryAxes, false);
3195 //exportAxesId(bPrimaryAxes);
3196
3197 pFS->endElement( FSNS( XML_c, XML_scatterChart ) );
3198 }
3199
exportScatterChart(const Reference<chart2::XChartType> & xChartType)3200 void ChartExport::exportScatterChart( const Reference< chart2::XChartType >& xChartType )
3201 {
3202 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
3203 bool bExported = false;
3204 for (const auto& splitDataSeries : aSplitDataSeries)
3205 {
3206 if (!splitDataSeries.hasElements())
3207 continue;
3208
3209 bExported = true;
3210 exportScatterChartSeries(xChartType, &splitDataSeries);
3211 }
3212 if (!bExported)
3213 exportScatterChartSeries(xChartType, nullptr);
3214 }
3215
exportStockChart(const Reference<chart2::XChartType> & xChartType)3216 void ChartExport::exportStockChart( const Reference< chart2::XChartType >& xChartType )
3217 {
3218 FSHelperPtr pFS = GetFS();
3219 const std::vector<Sequence<Reference<chart2::XDataSeries> > > aSplitDataSeries = splitDataSeriesByAxis(xChartType);
3220 for (const auto& splitDataSeries : aSplitDataSeries)
3221 {
3222 if (!splitDataSeries.hasElements())
3223 continue;
3224
3225 pFS->startElement(FSNS(XML_c, XML_stockChart));
3226
3227 bool bPrimaryAxes = true;
3228 exportCandleStickSeries(splitDataSeries, bPrimaryAxes);
3229
3230 // export stock properties
3231 Reference< css::chart::XStatisticDisplay > xStockPropProvider(mxDiagram, uno::UNO_QUERY);
3232 if (xStockPropProvider.is())
3233 {
3234 exportHiLowLines();
3235 exportUpDownBars(xChartType);
3236 }
3237
3238 createAxes(bPrimaryAxes, false);
3239
3240 pFS->endElement(FSNS(XML_c, XML_stockChart));
3241 }
3242 }
3243
exportHiLowLines()3244 void ChartExport::exportHiLowLines()
3245 {
3246 FSHelperPtr pFS = GetFS();
3247 // export the chart property
3248 Reference< css::chart::XStatisticDisplay > xChartPropProvider( mxDiagram, uno::UNO_QUERY );
3249
3250 if (!xChartPropProvider.is())
3251 return;
3252
3253 Reference< beans::XPropertySet > xStockPropSet = xChartPropProvider->getMinMaxLine();
3254 if( !xStockPropSet.is() )
3255 return;
3256
3257 pFS->startElement(FSNS(XML_c, XML_hiLowLines));
3258 exportShapeProps( xStockPropSet, false );
3259 pFS->endElement( FSNS( XML_c, XML_hiLowLines ) );
3260 }
3261
exportUpDownBars(const Reference<chart2::XChartType> & xChartType)3262 void ChartExport::exportUpDownBars( const Reference< chart2::XChartType >& xChartType)
3263 {
3264 if(xChartType->getChartType() != "com.sun.star.chart2.CandleStickChartType")
3265 return;
3266
3267 FSHelperPtr pFS = GetFS();
3268 // export the chart property
3269 Reference< css::chart::XStatisticDisplay > xChartPropProvider( mxDiagram, uno::UNO_QUERY );
3270 if(!xChartPropProvider.is())
3271 return;
3272
3273 // updownbar
3274 pFS->startElement(FSNS(XML_c, XML_upDownBars));
3275 // TODO: gapWidth
3276 pFS->singleElement(FSNS(XML_c, XML_gapWidth), XML_val, OString::number(150));
3277
3278 Reference< beans::XPropertySet > xChartPropSet = xChartPropProvider->getUpBar();
3279 if( xChartPropSet.is() )
3280 {
3281 pFS->startElement(FSNS(XML_c, XML_upBars));
3282 // For Linechart with UpDownBars, spPr is not getting imported
3283 // so no need to call the exportShapeProps() for LineChart
3284 if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType")
3285 {
3286 exportShapeProps(xChartPropSet, false);
3287 }
3288 pFS->endElement( FSNS( XML_c, XML_upBars ) );
3289 }
3290 xChartPropSet = xChartPropProvider->getDownBar();
3291 if( xChartPropSet.is() )
3292 {
3293 pFS->startElement(FSNS(XML_c, XML_downBars));
3294 if(xChartType->getChartType() == "com.sun.star.chart2.CandleStickChartType")
3295 {
3296 exportShapeProps(xChartPropSet, false);
3297 }
3298 pFS->endElement( FSNS( XML_c, XML_downBars ) );
3299 }
3300 pFS->endElement( FSNS( XML_c, XML_upDownBars ) );
3301 }
3302
exportSurfaceChart(const Reference<chart2::XChartType> & xChartType)3303 void ChartExport::exportSurfaceChart( const Reference< chart2::XChartType >& xChartType )
3304 {
3305 FSHelperPtr pFS = GetFS();
3306 sal_Int32 nTypeId = XML_surfaceChart;
3307 if( mbIs3DChart )
3308 nTypeId = XML_surface3DChart;
3309 pFS->startElement(FSNS(XML_c, nTypeId));
3310 exportVaryColors(xChartType);
3311 bool bPrimaryAxes = true;
3312 exportAllSeries(xChartType, bPrimaryAxes);
3313 createAxes(bPrimaryAxes, false);
3314
3315 pFS->endElement( FSNS( XML_c, nTypeId ) );
3316 }
3317
exportAllSeries(const Reference<chart2::XChartType> & xChartType,bool & rPrimaryAxes)3318 void ChartExport::exportAllSeries(const Reference<chart2::XChartType>& xChartType, bool& rPrimaryAxes)
3319 {
3320 Reference< chart2::XDataSeriesContainer > xDSCnt( xChartType, uno::UNO_QUERY );
3321 if( ! xDSCnt.is())
3322 return;
3323
3324 // export dataseries for current chart-type
3325 Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries());
3326 exportSeries_chart(xChartType, aSeriesSeq, rPrimaryAxes);
3327 }
3328
exportVaryColors(const Reference<chart2::XChartType> & xChartType)3329 void ChartExport::exportVaryColors(const Reference<chart2::XChartType>& xChartType)
3330 {
3331 FSHelperPtr pFS = GetFS();
3332 try
3333 {
3334 Reference<chart2::XDataSeries> xDataSeries = getPrimaryDataSeries(xChartType);
3335 Reference<beans::XPropertySet> xDataSeriesProps(xDataSeries, uno::UNO_QUERY_THROW);
3336 Any aAnyVaryColors = xDataSeriesProps->getPropertyValue(u"VaryColorsByPoint"_ustr);
3337 bool bVaryColors = false;
3338 aAnyVaryColors >>= bVaryColors;
3339 pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, ToPsz10(bVaryColors));
3340 }
3341 catch (...)
3342 {
3343 pFS->singleElement(FSNS(XML_c, XML_varyColors), XML_val, "0");
3344 }
3345 }
3346
exportSeries_chart(const Reference<chart2::XChartType> & xChartType,const Sequence<Reference<chart2::XDataSeries>> & rSeriesSeq,bool & rPrimaryAxes)3347 void ChartExport::exportSeries_chart( const Reference<chart2::XChartType>& xChartType,
3348 const Sequence<Reference<chart2::XDataSeries> >& rSeriesSeq,
3349 bool& rPrimaryAxes)
3350 {
3351 OUString aLabelRole = xChartType->getRoleOfSequenceForSeriesLabel();
3352 OUString aChartType( xChartType->getChartType());
3353 sal_Int32 eChartType = lcl_getChartType( aChartType );
3354
3355 for( const auto& rSeries : rSeriesSeq )
3356 {
3357 // export series
3358 Reference< chart2::data::XDataSource > xSource( rSeries, uno::UNO_QUERY );
3359 if( xSource.is())
3360 {
3361 Reference< chart2::XDataSeries > xDataSeries( xSource, uno::UNO_QUERY );
3362 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
3363 xSource->getDataSequences());
3364 // search for main sequence and create a series element
3365 {
3366 sal_Int32 nMainSequenceIndex = -1;
3367 sal_Int32 nSeriesLength = 0;
3368 Reference< chart2::data::XDataSequence > xValuesSeq;
3369 Reference< chart2::data::XDataSequence > xLabelSeq;
3370 sal_Int32 nSeqIdx=0;
3371 for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx )
3372 {
3373 Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() );
3374 if( nMainSequenceIndex==-1 )
3375 {
3376 Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY );
3377 OUString aRole;
3378 if( xSeqProp.is())
3379 xSeqProp->getPropertyValue(u"Role"_ustr) >>= aRole;
3380 // "main" sequence
3381 if( aRole == aLabelRole )
3382 {
3383 xValuesSeq.set( xTempValueSeq );
3384 xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel());
3385 nMainSequenceIndex = nSeqIdx;
3386 }
3387 }
3388 sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0));
3389 if( nSeriesLength < nSequenceLength )
3390 nSeriesLength = nSequenceLength;
3391 }
3392
3393 // have found the main sequence, then xValuesSeq and
3394 // xLabelSeq contain those. Otherwise both are empty
3395 FSHelperPtr pFS = GetFS();
3396
3397 pFS->startElement(FSNS(XML_c, XML_ser));
3398
3399 // TODO: idx and order
3400 pFS->singleElement( FSNS( XML_c, XML_idx ),
3401 XML_val, OString::number(mnSeriesCount) );
3402 pFS->singleElement( FSNS( XML_c, XML_order ),
3403 XML_val, OString::number(mnSeriesCount++) );
3404
3405 // export label
3406 if( xLabelSeq.is() )
3407 exportSeriesText( xLabelSeq, false );
3408
3409 Reference<XPropertySet> xPropSet(xDataSeries, UNO_QUERY_THROW);
3410 if( GetProperty( xPropSet, u"AttachedAxisIndex"_ustr) )
3411 {
3412 sal_Int32 nLocalAttachedAxis = 0;
3413 mAny >>= nLocalAttachedAxis;
3414 rPrimaryAxes = isPrimaryAxes(nLocalAttachedAxis);
3415 }
3416
3417 // export shape properties
3418 Reference< XPropertySet > xOldPropSet = SchXMLSeriesHelper::createOldAPISeriesPropertySet(
3419 rSeries, getModel() );
3420 if( xOldPropSet.is() )
3421 {
3422 exportShapeProps( xOldPropSet, false );
3423 }
3424
3425 switch( eChartType )
3426 {
3427 case chart::TYPEID_BUBBLE:
3428 case chart::TYPEID_HORBAR:
3429 case chart::TYPEID_BAR:
3430 {
3431 pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0");
3432 }
3433 break;
3434 case chart::TYPEID_LINE:
3435 {
3436 exportMarker(xOldPropSet);
3437 break;
3438 }
3439 case chart::TYPEID_PIE:
3440 case chart::TYPEID_DOUGHNUT:
3441 {
3442 if( xOldPropSet.is() && GetProperty( xOldPropSet, u"SegmentOffset"_ustr) )
3443 {
3444 sal_Int32 nOffset = 0;
3445 mAny >>= nOffset;
3446 pFS->singleElement( FSNS( XML_c, XML_explosion ),
3447 XML_val, OString::number( nOffset ) );
3448 }
3449 break;
3450 }
3451 case chart::TYPEID_SCATTER:
3452 {
3453 exportMarker(xOldPropSet);
3454 break;
3455 }
3456 case chart::TYPEID_RADARLINE:
3457 {
3458 exportMarker(xOldPropSet);
3459 break;
3460 }
3461 }
3462
3463 // export data points
3464 exportDataPoints( uno::Reference< beans::XPropertySet >( rSeries, uno::UNO_QUERY ), nSeriesLength, eChartType );
3465
3466 DataLabelsRange aDLblsRange;
3467 // export data labels
3468 exportDataLabels(rSeries, nSeriesLength, eChartType, aDLblsRange, false);
3469
3470 exportTrendlines( rSeries );
3471
3472 if( eChartType != chart::TYPEID_PIE &&
3473 eChartType != chart::TYPEID_RADARLINE )
3474 {
3475 //export error bars here
3476 Reference< XPropertySet > xSeriesPropSet( xSource, uno::UNO_QUERY );
3477 Reference< XPropertySet > xErrorBarYProps;
3478 xSeriesPropSet->getPropertyValue(u"ErrorBarY"_ustr) >>= xErrorBarYProps;
3479 if(xErrorBarYProps.is())
3480 exportErrorBar(xErrorBarYProps, true);
3481 if (eChartType != chart::TYPEID_BAR &&
3482 eChartType != chart::TYPEID_HORBAR)
3483 {
3484 Reference< XPropertySet > xErrorBarXProps;
3485 xSeriesPropSet->getPropertyValue(u"ErrorBarX"_ustr) >>= xErrorBarXProps;
3486 if(xErrorBarXProps.is())
3487 exportErrorBar(xErrorBarXProps, false);
3488 }
3489 }
3490
3491 // export categories
3492 if( eChartType != chart::TYPEID_SCATTER && eChartType != chart::TYPEID_BUBBLE && mxCategoriesValues.is() )
3493 exportSeriesCategory( mxCategoriesValues );
3494
3495 if( (eChartType == chart::TYPEID_SCATTER)
3496 || (eChartType == chart::TYPEID_BUBBLE) )
3497 {
3498 // export xVal
3499 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, u"values-x"_ustr ) );
3500 if( xSequence.is() )
3501 {
3502 Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
3503 if( xValues.is() )
3504 exportSeriesValues( xValues, XML_xVal );
3505 }
3506 else if( mxCategoriesValues.is() )
3507 exportSeriesCategory( mxCategoriesValues, XML_xVal );
3508 }
3509
3510 if( eChartType == chart::TYPEID_BUBBLE )
3511 {
3512 // export yVal
3513 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, u"values-y"_ustr ) );
3514 if( xSequence.is() )
3515 {
3516 Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
3517 if( xValues.is() )
3518 exportSeriesValues( xValues, XML_yVal );
3519 }
3520 }
3521
3522 // export values
3523 if( xValuesSeq.is() )
3524 {
3525 sal_Int32 nYValueType = XML_val;
3526 if( eChartType == chart::TYPEID_SCATTER )
3527 nYValueType = XML_yVal;
3528 else if( eChartType == chart::TYPEID_BUBBLE )
3529 nYValueType = XML_bubbleSize;
3530 exportSeriesValues( xValuesSeq, nYValueType );
3531 }
3532
3533 if( eChartType == chart::TYPEID_SCATTER
3534 || eChartType == chart::TYPEID_LINE )
3535 exportSmooth();
3536
3537 // tdf103988: "corrupted" files with Bubble chart opening in MSO
3538 if( eChartType == chart::TYPEID_BUBBLE )
3539 pFS->singleElement(FSNS(XML_c, XML_bubble3D), XML_val, "0");
3540
3541 if (!aDLblsRange.empty())
3542 writeDataLabelsRange(pFS, GetFB(), aDLblsRange);
3543
3544 pFS->endElement( FSNS( XML_c, XML_ser ) );
3545 }
3546 }
3547 }
3548 }
3549
exportSeries_chartex(const Reference<chart2::XChartType> & xChartType,const Sequence<Reference<chart2::XDataSeries>> & rSeriesSeq,const char * sTypeName)3550 void ChartExport::exportSeries_chartex( const Reference<chart2::XChartType>& xChartType,
3551 const Sequence<Reference<chart2::XDataSeries> >& rSeriesSeq,
3552 const char* sTypeName)
3553 {
3554 OUString aLabelRole = xChartType->getRoleOfSequenceForSeriesLabel();
3555 OUString aChartType( xChartType->getChartType());
3556 sal_Int32 eChartType = lcl_getChartType( aChartType );
3557
3558 sal_Int32 nSeriesCnt = 0;
3559 for( const auto& rSeries : rSeriesSeq )
3560 {
3561 // export series
3562 Reference< chart2::data::XDataSource > xSource( rSeries, uno::UNO_QUERY );
3563 if( xSource.is())
3564 {
3565 FSHelperPtr pFS = GetFS();
3566 pFS->startElement(FSNS(XML_cx, XML_series), XML_layoutId, sTypeName);
3567
3568 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
3569 xSource->getDataSequences());
3570
3571 // search for main sequence and create a series element
3572 sal_Int32 nMainSequenceIndex = -1;
3573 sal_Int32 nSeriesLength = 0;
3574 Reference< chart2::data::XDataSequence > xLabelSeq;
3575 sal_Int32 nSeqIdx=0;
3576 for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx )
3577 {
3578 Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() );
3579 if( nMainSequenceIndex==-1 )
3580 {
3581 Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY );
3582 OUString aRole;
3583 if( xSeqProp.is())
3584 xSeqProp->getPropertyValue(u"Role"_ustr) >>= aRole;
3585 // "main" sequence
3586 if( aRole == aLabelRole )
3587 {
3588 xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel());
3589 nMainSequenceIndex = nSeqIdx;
3590 }
3591 }
3592 sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0));
3593 if( nSeriesLength < nSequenceLength )
3594 nSeriesLength = nSequenceLength;
3595 }
3596
3597 // export label
3598 if( xLabelSeq.is() )
3599 exportSeriesText( xLabelSeq, true );
3600
3601 // export shape properties
3602 Reference< XPropertySet > xOldPropSet = SchXMLSeriesHelper::createOldAPISeriesPropertySet(
3603 rSeries, getModel() );
3604 if( xOldPropSet.is() )
3605 {
3606 exportShapeProps( xOldPropSet, true );
3607 }
3608
3609 DataLabelsRange aDLblsRange;
3610 // export data labels
3611 exportDataLabels(rSeries, nSeriesLength, eChartType, aDLblsRange, true);
3612
3613 // dataId links to the correct data set in the <cx:chartData>. See
3614 // DATA_ID_COMMENT
3615 pFS->singleElement(FSNS(XML_cx, XML_dataId), XML_val,
3616 OString::number(nSeriesCnt++));
3617
3618 // layoutPr
3619
3620 // axisId
3621
3622 // extLst
3623
3624 pFS->endElement(FSNS(XML_cx, XML_series));
3625 }
3626 }
3627 }
3628
exportCandleStickSeries(const Sequence<Reference<chart2::XDataSeries>> & aSeriesSeq,bool & rPrimaryAxes)3629 void ChartExport::exportCandleStickSeries(
3630 const Sequence< Reference< chart2::XDataSeries > > & aSeriesSeq,
3631 bool& rPrimaryAxes)
3632 {
3633 for( const Reference< chart2::XDataSeries >& xSeries : aSeriesSeq )
3634 {
3635 rPrimaryAxes = lcl_isSeriesAttachedToFirstAxis(xSeries);
3636
3637 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
3638 if( xSource.is())
3639 {
3640 // export series in correct order (as we don't store roles)
3641 // with japanese candlesticks: open, low, high, close
3642 // otherwise: low, high, close
3643 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
3644 xSource->getDataSequences());
3645
3646 const char* sSeries[] = {"values-first","values-max","values-min","values-last",nullptr};
3647
3648 for( sal_Int32 idx = 0; sSeries[idx] != nullptr ; idx++ )
3649 {
3650 Reference< chart2::data::XLabeledDataSequence > xLabeledSeq( lcl_getDataSequenceByRole( aSeqCnt, OUString::createFromAscii(sSeries[idx]) ) );
3651 if( xLabeledSeq.is())
3652 {
3653 Reference< chart2::data::XDataSequence > xLabelSeq( xLabeledSeq->getLabel());
3654 Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues());
3655 {
3656 FSHelperPtr pFS = GetFS();
3657 pFS->startElement(FSNS(XML_c, XML_ser));
3658
3659 // TODO: idx and order
3660 // idx attribute should start from 1 and not from 0.
3661 pFS->singleElement( FSNS( XML_c, XML_idx ),
3662 XML_val, OString::number(idx+1) );
3663 pFS->singleElement( FSNS( XML_c, XML_order ),
3664 XML_val, OString::number(idx+1) );
3665
3666 // export label
3667 if( xLabelSeq.is() )
3668 exportSeriesText( xLabelSeq, false );
3669
3670 // TODO:export shape properties
3671
3672 // export categories
3673 if( mxCategoriesValues.is() )
3674 exportSeriesCategory( mxCategoriesValues );
3675
3676 // export values
3677 if( xValueSeq.is() )
3678 exportSeriesValues( xValueSeq );
3679
3680 pFS->endElement( FSNS( XML_c, XML_ser ) );
3681 }
3682 }
3683 }
3684 }
3685 }
3686 }
3687
exportSeriesText(const Reference<chart2::data::XDataSequence> & xValueSeq,bool bIsChartex)3688 void ChartExport::exportSeriesText( const Reference< chart2::data::XDataSequence > & xValueSeq,
3689 bool bIsChartex)
3690 {
3691 FSHelperPtr pFS = GetFS();
3692
3693 OUString aLabelString = lcl_flattenStringSequence(lcl_getLabelSequence(xValueSeq));
3694
3695 if (bIsChartex) {
3696 lcl_writeChartexString(pFS, aLabelString);
3697 } else {
3698 pFS->startElement(FSNS(XML_c, XML_tx));
3699
3700 OUString aCellRange = xValueSeq->getSourceRangeRepresentation();
3701 aCellRange = parseFormula( aCellRange );
3702 pFS->startElement(FSNS(XML_c, XML_strRef));
3703
3704 pFS->startElement(FSNS(XML_c, XML_f));
3705 pFS->writeEscaped( aCellRange );
3706 pFS->endElement( FSNS( XML_c, XML_f ) );
3707
3708 pFS->startElement(FSNS(XML_c, XML_strCache));
3709 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, "1");
3710 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, "0");
3711 pFS->startElement(FSNS(XML_c, XML_v));
3712 pFS->writeEscaped( aLabelString );
3713 pFS->endElement( FSNS( XML_c, XML_v ) );
3714 pFS->endElement( FSNS( XML_c, XML_pt ) );
3715 pFS->endElement( FSNS( XML_c, XML_strCache ) );
3716 pFS->endElement( FSNS( XML_c, XML_strRef ) );
3717 pFS->endElement( FSNS( XML_c, XML_tx ) );
3718 }
3719 }
3720
exportSeriesCategory(const Reference<chart2::data::XDataSequence> & xValueSeq,sal_Int32 nValueType)3721 void ChartExport::exportSeriesCategory( const Reference< chart2::data::XDataSequence > & xValueSeq, sal_Int32 nValueType )
3722 {
3723 FSHelperPtr pFS = GetFS();
3724 pFS->startElement(FSNS(XML_c, nValueType));
3725
3726 OUString aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString();
3727 const Sequence< Sequence< OUString >> aFinalSplitSource = (nValueType == XML_cat) ? getSplitCategoriesList(aCellRange) : Sequence< Sequence< OUString>>(0);
3728 aCellRange = parseFormula( aCellRange );
3729
3730 if(aFinalSplitSource.getLength() > 1)
3731 {
3732 // export multi level category axis labels
3733 pFS->startElement(FSNS(XML_c, XML_multiLvlStrRef));
3734
3735 pFS->startElement(FSNS(XML_c, XML_f));
3736 pFS->writeEscaped(aCellRange);
3737 pFS->endElement(FSNS(XML_c, XML_f));
3738
3739 pFS->startElement(FSNS(XML_c, XML_multiLvlStrCache));
3740 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(aFinalSplitSource[0].getLength()));
3741 for(const auto& rSeq : aFinalSplitSource)
3742 {
3743 pFS->startElement(FSNS(XML_c, XML_lvl));
3744 for(sal_Int32 j = 0; j < rSeq.getLength(); j++)
3745 {
3746 if(!rSeq[j].isEmpty())
3747 {
3748 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(j));
3749 pFS->startElement(FSNS(XML_c, XML_v));
3750 pFS->writeEscaped(rSeq[j]);
3751 pFS->endElement(FSNS(XML_c, XML_v));
3752 pFS->endElement(FSNS(XML_c, XML_pt));
3753 }
3754 }
3755 pFS->endElement(FSNS(XML_c, XML_lvl));
3756 }
3757
3758 pFS->endElement(FSNS(XML_c, XML_multiLvlStrCache));
3759 pFS->endElement(FSNS(XML_c, XML_multiLvlStrRef));
3760 }
3761 else
3762 {
3763 // export single category axis labels
3764 bool bWriteDateCategories = mbHasDateCategories && (nValueType == XML_cat);
3765 OUString aNumberFormatString;
3766 if (bWriteDateCategories)
3767 {
3768 Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY );
3769 if( xAxisXSupp.is())
3770 {
3771 Reference< XPropertySet > xAxisProp = xAxisXSupp->getXAxis();
3772 if (GetProperty(xAxisProp, u"NumberFormat"_ustr))
3773 {
3774 sal_Int32 nKey = 0;
3775 mAny >>= nKey;
3776 aNumberFormatString = getNumberFormatCode(nKey);
3777 }
3778 }
3779 if (aNumberFormatString.isEmpty())
3780 bWriteDateCategories = false;
3781 }
3782
3783 pFS->startElement(FSNS(XML_c, bWriteDateCategories ? XML_numRef : XML_strRef));
3784
3785 pFS->startElement(FSNS(XML_c, XML_f));
3786 pFS->writeEscaped(aCellRange);
3787 pFS->endElement(FSNS(XML_c, XML_f));
3788
3789 pFS->startElement(FSNS(XML_c, bWriteDateCategories ? XML_numCache : XML_strCache));
3790 if (bWriteDateCategories)
3791 {
3792 pFS->startElement(FSNS(XML_c, XML_formatCode));
3793 pFS->writeEscaped(aNumberFormatString);
3794 pFS->endElement(FSNS(XML_c, XML_formatCode));
3795
3796 std::vector<double> aDateCategories = lcl_getAllValuesFromSequence(xValueSeq);
3797 const sal_Int32 ptCount = aDateCategories.size();
3798 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount));
3799 for (sal_Int32 i = 0; i < ptCount; i++)
3800 {
3801 if (!std::isnan(aDateCategories[i]))
3802 {
3803 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i));
3804 pFS->startElement(FSNS(XML_c, XML_v));
3805 pFS->write(OString::number(aDateCategories[i]));
3806 pFS->endElement(FSNS(XML_c, XML_v));
3807 pFS->endElement(FSNS(XML_c, XML_pt));
3808 }
3809 }
3810 }
3811 else
3812 {
3813 std::vector<OUString> aCategories;
3814 lcl_fillCategoriesIntoStringVector(xValueSeq, aCategories);
3815 const sal_Int32 ptCount = aCategories.size();
3816 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount));
3817 for (sal_Int32 i = 0; i < ptCount; i++)
3818 {
3819 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i));
3820 pFS->startElement(FSNS(XML_c, XML_v));
3821 pFS->writeEscaped(aCategories[i]);
3822 pFS->endElement(FSNS(XML_c, XML_v));
3823 pFS->endElement(FSNS(XML_c, XML_pt));
3824 }
3825 }
3826
3827 pFS->endElement(FSNS(XML_c, bWriteDateCategories ? XML_numCache : XML_strCache));
3828 pFS->endElement(FSNS(XML_c, bWriteDateCategories ? XML_numRef : XML_strRef));
3829 }
3830
3831 pFS->endElement( FSNS( XML_c, nValueType ) );
3832 }
3833
exportSeriesValues(const Reference<chart2::data::XDataSequence> & xValueSeq,sal_Int32 nValueType)3834 void ChartExport::exportSeriesValues( const Reference< chart2::data::XDataSequence > & xValueSeq, sal_Int32 nValueType )
3835 {
3836 FSHelperPtr pFS = GetFS();
3837 pFS->startElement(FSNS(XML_c, nValueType));
3838
3839 OUString aCellRange = xValueSeq.is() ? xValueSeq->getSourceRangeRepresentation() : OUString();
3840 aCellRange = parseFormula( aCellRange );
3841 // TODO: need to handle XML_multiLvlStrRef according to aCellRange
3842 pFS->startElement(FSNS(XML_c, XML_numRef));
3843
3844 pFS->startElement(FSNS(XML_c, XML_f));
3845 pFS->writeEscaped( aCellRange );
3846 pFS->endElement( FSNS( XML_c, XML_f ) );
3847
3848 ::std::vector< double > aValues = lcl_getAllValuesFromSequence( xValueSeq );
3849 sal_Int32 ptCount = aValues.size();
3850 pFS->startElement(FSNS(XML_c, XML_numCache));
3851 pFS->startElement(FSNS(XML_c, XML_formatCode));
3852 OUString sNumberFormatString(u"General"_ustr);
3853 const sal_Int32 nKey = xValueSeq.is() ? xValueSeq->getNumberFormatKeyByIndex(-1) : 0;
3854 if (nKey > 0)
3855 sNumberFormatString = getNumberFormatCode(nKey);
3856 pFS->writeEscaped(sNumberFormatString);
3857 pFS->endElement( FSNS( XML_c, XML_formatCode ) );
3858 pFS->singleElement(FSNS(XML_c, XML_ptCount), XML_val, OString::number(ptCount));
3859
3860 for( sal_Int32 i = 0; i < ptCount; i++ )
3861 {
3862 if (!std::isnan(aValues[i]))
3863 {
3864 pFS->startElement(FSNS(XML_c, XML_pt), XML_idx, OString::number(i));
3865 pFS->startElement(FSNS(XML_c, XML_v));
3866 pFS->write(aValues[i]);
3867 pFS->endElement(FSNS(XML_c, XML_v));
3868 pFS->endElement(FSNS(XML_c, XML_pt));
3869 }
3870 }
3871
3872 pFS->endElement( FSNS( XML_c, XML_numCache ) );
3873 pFS->endElement( FSNS( XML_c, XML_numRef ) );
3874 pFS->endElement( FSNS( XML_c, nValueType ) );
3875 }
3876
exportShapeProps(const Reference<XPropertySet> & xPropSet,bool bIsChartex)3877 void ChartExport::exportShapeProps( const Reference< XPropertySet >& xPropSet,
3878 bool bIsChartex)
3879 {
3880 sal_Int32 nChartNS = bIsChartex ? XML_cx : XML_c;
3881 FSHelperPtr pFS = GetFS();
3882 pFS->startElement(FSNS(nChartNS, XML_spPr));
3883
3884 exportFill( xPropSet );
3885 WriteOutline( xPropSet, getModel() );
3886
3887 pFS->endElement( FSNS( nChartNS, XML_spPr ) );
3888 }
3889
exportTextProps(const Reference<XPropertySet> & xPropSet,bool bIsChartex)3890 void ChartExport::exportTextProps(const Reference<XPropertySet>& xPropSet,
3891 bool bIsChartex)
3892 {
3893 FSHelperPtr pFS = GetFS();
3894
3895 const sal_Int32 nChartNS = bIsChartex ? XML_cx : XML_c;
3896 pFS->startElement(FSNS(nChartNS, XML_txPr));
3897
3898 sal_Int32 nRotation = 0;
3899 const char* textWordWrap = nullptr;
3900
3901 if (auto xServiceInfo = uno::Reference<lang::XServiceInfo>(xPropSet, uno::UNO_QUERY))
3902 {
3903 double fMultiplier = 0.0;
3904 // We have at least two possible units of returned value: degrees (e.g., for data labels),
3905 // and 100ths of degree (e.g., for axes labels). The latter is returned as an Any wrapping
3906 // a sal_Int32 value (see WrappedTextRotationProperty::convertInnerToOuterValue), while
3907 // the former is double. So we could test the contained type to decide which multiplier to
3908 // use. But testing the service info should be more robust.
3909 if (xServiceInfo->supportsService(u"com.sun.star.chart.ChartAxis"_ustr))
3910 fMultiplier = -600.0;
3911 else if (xServiceInfo->supportsService(u"com.sun.star.chart2.DataSeries"_ustr) || xServiceInfo->supportsService(u"com.sun.star.chart2.DataPointProperties"_ustr))
3912 {
3913 fMultiplier = -60000.0;
3914 bool bTextWordWrap = false;
3915 if ((xPropSet->getPropertyValue(u"TextWordWrap"_ustr) >>= bTextWordWrap) && bTextWordWrap)
3916 textWordWrap = "square";
3917 else
3918 textWordWrap = "none";
3919 }
3920
3921 if (fMultiplier)
3922 {
3923 double fTextRotation = 0.0;
3924 uno::Any aAny = xPropSet->getPropertyValue(u"TextRotation"_ustr);
3925 if (aAny.hasValue() && (aAny >>= fTextRotation))
3926 {
3927 fTextRotation *= fMultiplier;
3928 // The MS Office UI allows values only in range of [-90,90].
3929 if (fTextRotation < -5400000.0 && fTextRotation > -16200000.0)
3930 {
3931 // Reflect the angle if the value is between 90° and 270°
3932 fTextRotation += 10800000.0;
3933 }
3934 else if (fTextRotation <= -16200000.0)
3935 {
3936 fTextRotation += 21600000.0;
3937 }
3938 nRotation = std::round(fTextRotation);
3939 }
3940 }
3941 }
3942
3943 if (nRotation)
3944 pFS->singleElement(FSNS(XML_a, XML_bodyPr), XML_rot, OString::number(nRotation), XML_wrap, textWordWrap);
3945 else
3946 pFS->singleElement(FSNS(XML_a, XML_bodyPr), XML_wrap, textWordWrap);
3947
3948 pFS->singleElement(FSNS(XML_a, XML_lstStyle));
3949
3950 pFS->startElement(FSNS(XML_a, XML_p));
3951 pFS->startElement(FSNS(XML_a, XML_pPr));
3952
3953 WriteRunProperties(xPropSet, false, XML_defRPr, true, o3tl::temporary(false),
3954 o3tl::temporary(sal_Int32()));
3955
3956 pFS->endElement(FSNS(XML_a, XML_pPr));
3957 pFS->endElement(FSNS(XML_a, XML_p));
3958 pFS->endElement(FSNS(nChartNS, XML_txPr));
3959 }
3960
InitPlotArea()3961 void ChartExport::InitPlotArea( )
3962 {
3963 Reference< XPropertySet > xDiagramProperties (mxDiagram, uno::UNO_QUERY);
3964
3965 // Check for supported services and then the properties provided by this service.
3966 Reference<lang::XServiceInfo> xServiceInfo (mxDiagram, uno::UNO_QUERY);
3967 if (xServiceInfo.is())
3968 {
3969 if (xServiceInfo->supportsService(u"com.sun.star.chart.ChartAxisZSupplier"_ustr))
3970 {
3971 xDiagramProperties->getPropertyValue(u"HasZAxis"_ustr) >>= mbHasZAxis;
3972 }
3973 }
3974
3975 xDiagramProperties->getPropertyValue(u"Dim3D"_ustr) >>= mbIs3DChart;
3976
3977 if( mbHasCategoryLabels && mxNewDiagram.is())
3978 {
3979 Reference< chart2::data::XLabeledDataSequence > xCategories(
3980 lcl_getCategories( mxNewDiagram, &mbHasDateCategories ) );
3981 if( xCategories.is() )
3982 {
3983 mxCategoriesValues.set( xCategories->getValues() );
3984 }
3985 }
3986 }
3987
exportAxes(bool bIsChartex)3988 void ChartExport::exportAxes( bool bIsChartex )
3989 {
3990 sal_Int32 nSize = maAxes.size();
3991 // let's export the axis types in the right order
3992 for ( sal_Int32 nSortIdx = AXIS_PRIMARY_X; nSortIdx <= AXIS_SECONDARY_Y; nSortIdx++ )
3993 {
3994 for ( sal_Int32 nIdx = 0; nIdx < nSize; nIdx++ )
3995 {
3996 if (nSortIdx == maAxes[nIdx].nAxisType)
3997 exportAxis( maAxes[nIdx], bIsChartex );
3998 }
3999 }
4000 }
4001
4002 namespace {
4003
getXAxisTypeByChartType(sal_Int32 eChartType)4004 sal_Int32 getXAxisTypeByChartType(sal_Int32 eChartType)
4005 {
4006 if( (eChartType == chart::TYPEID_SCATTER)
4007 || (eChartType == chart::TYPEID_BUBBLE) )
4008 return XML_valAx;
4009 else if( eChartType == chart::TYPEID_STOCK )
4010 return XML_dateAx;
4011
4012 return XML_catAx;
4013 }
4014
getRealXAxisType(sal_Int32 nAxisType)4015 sal_Int32 getRealXAxisType(sal_Int32 nAxisType)
4016 {
4017 if( nAxisType == chart2::AxisType::CATEGORY )
4018 return XML_catAx;
4019 else if( nAxisType == chart2::AxisType::DATE )
4020 return XML_dateAx;
4021 else if( nAxisType == chart2::AxisType::SERIES )
4022 return XML_serAx;
4023
4024 return XML_valAx;
4025 }
4026
4027 }
4028
exportAxis(const AxisIdPair & rAxisIdPair,bool bIsChartex)4029 void ChartExport::exportAxis(const AxisIdPair& rAxisIdPair, bool bIsChartex)
4030 {
4031 // get some properties from document first
4032 bool bHasXAxisTitle = false,
4033 bHasYAxisTitle = false,
4034 bHasZAxisTitle = false,
4035 bHasSecondaryXAxisTitle = false,
4036 bHasSecondaryYAxisTitle = false;
4037 bool bHasXAxisMajorGrid = false,
4038 bHasXAxisMinorGrid = false,
4039 bHasYAxisMajorGrid = false,
4040 bHasYAxisMinorGrid = false,
4041 bHasZAxisMajorGrid = false,
4042 bHasZAxisMinorGrid = false;
4043
4044 Reference< XPropertySet > xDiagramProperties (mxDiagram, uno::UNO_QUERY);
4045
4046 xDiagramProperties->getPropertyValue(u"HasXAxisTitle"_ustr) >>= bHasXAxisTitle;
4047 xDiagramProperties->getPropertyValue(u"HasYAxisTitle"_ustr) >>= bHasYAxisTitle;
4048 xDiagramProperties->getPropertyValue(u"HasZAxisTitle"_ustr) >>= bHasZAxisTitle;
4049 xDiagramProperties->getPropertyValue(u"HasSecondaryXAxisTitle"_ustr) >>= bHasSecondaryXAxisTitle;
4050 xDiagramProperties->getPropertyValue(u"HasSecondaryYAxisTitle"_ustr) >>= bHasSecondaryYAxisTitle;
4051
4052 xDiagramProperties->getPropertyValue(u"HasXAxisGrid"_ustr) >>= bHasXAxisMajorGrid;
4053 xDiagramProperties->getPropertyValue(u"HasYAxisGrid"_ustr) >>= bHasYAxisMajorGrid;
4054 xDiagramProperties->getPropertyValue(u"HasZAxisGrid"_ustr) >>= bHasZAxisMajorGrid;
4055
4056 xDiagramProperties->getPropertyValue(u"HasXAxisHelpGrid"_ustr) >>= bHasXAxisMinorGrid;
4057 xDiagramProperties->getPropertyValue(u"HasYAxisHelpGrid"_ustr) >>= bHasYAxisMinorGrid;
4058 xDiagramProperties->getPropertyValue(u"HasZAxisHelpGrid"_ustr) >>= bHasZAxisMinorGrid;
4059
4060 Reference< XPropertySet > xAxisProp;
4061 Reference< drawing::XShape > xAxisTitle;
4062 Reference< beans::XPropertySet > xMajorGrid;
4063 Reference< beans::XPropertySet > xMinorGrid;
4064 sal_Int32 nAxisType = XML_catAx;
4065 const char* sAxPos = nullptr;
4066
4067 switch( rAxisIdPair.nAxisType )
4068 {
4069 case AXIS_PRIMARY_X:
4070 {
4071 Reference< css::chart::XAxisXSupplier > xAxisXSupp( mxDiagram, uno::UNO_QUERY );
4072 if( xAxisXSupp.is())
4073 xAxisProp = xAxisXSupp->getXAxis();
4074 if( bHasXAxisTitle )
4075 xAxisTitle = xAxisXSupp->getXAxisTitle();
4076 if( bHasXAxisMajorGrid )
4077 xMajorGrid = xAxisXSupp->getXMainGrid();
4078 if( bHasXAxisMinorGrid )
4079 xMinorGrid = xAxisXSupp->getXHelpGrid();
4080
4081 nAxisType = lcl_getCategoryAxisType(mxNewDiagram, 0, 0);
4082 if( nAxisType != -1 )
4083 nAxisType = getRealXAxisType(nAxisType);
4084 else
4085 nAxisType = getXAxisTypeByChartType( getChartType() );
4086 // FIXME: axPos, need to check axis direction
4087 sAxPos = "b";
4088 break;
4089 }
4090 case AXIS_PRIMARY_Y:
4091 {
4092 Reference< css::chart::XAxisYSupplier > xAxisYSupp( mxDiagram, uno::UNO_QUERY );
4093 if( xAxisYSupp.is())
4094 xAxisProp = xAxisYSupp->getYAxis();
4095 if( bHasYAxisTitle )
4096 xAxisTitle = xAxisYSupp->getYAxisTitle();
4097 if( bHasYAxisMajorGrid )
4098 xMajorGrid = xAxisYSupp->getYMainGrid();
4099 if( bHasYAxisMinorGrid )
4100 xMinorGrid = xAxisYSupp->getYHelpGrid();
4101
4102 nAxisType = XML_valAx;
4103 // FIXME: axPos, need to check axis direction
4104 sAxPos = "l";
4105 break;
4106 }
4107 case AXIS_PRIMARY_Z:
4108 {
4109 Reference< css::chart::XAxisZSupplier > xAxisZSupp( mxDiagram, uno::UNO_QUERY );
4110 if( xAxisZSupp.is())
4111 xAxisProp = xAxisZSupp->getZAxis();
4112 if( bHasZAxisTitle )
4113 xAxisTitle = xAxisZSupp->getZAxisTitle();
4114 if( bHasZAxisMajorGrid )
4115 xMajorGrid = xAxisZSupp->getZMainGrid();
4116 if( bHasZAxisMinorGrid )
4117 xMinorGrid = xAxisZSupp->getZHelpGrid();
4118
4119 sal_Int32 eChartType = getChartType( );
4120 if( (eChartType == chart::TYPEID_SCATTER)
4121 || (eChartType == chart::TYPEID_BUBBLE) )
4122 nAxisType = XML_valAx;
4123 else if( eChartType == chart::TYPEID_STOCK )
4124 nAxisType = XML_dateAx;
4125 else if( eChartType == chart::TYPEID_BAR || eChartType == chart::TYPEID_AREA )
4126 nAxisType = XML_serAx;
4127 // FIXME: axPos, need to check axis direction
4128 sAxPos = "b";
4129 break;
4130 }
4131 case AXIS_SECONDARY_X:
4132 {
4133 Reference< css::chart::XTwoAxisXSupplier > xAxisTwoXSupp( mxDiagram, uno::UNO_QUERY );
4134 if( xAxisTwoXSupp.is())
4135 xAxisProp = xAxisTwoXSupp->getSecondaryXAxis();
4136 if( bHasSecondaryXAxisTitle )
4137 {
4138 Reference< css::chart::XSecondAxisTitleSupplier > xAxisSupp( mxDiagram, uno::UNO_QUERY );
4139 xAxisTitle = xAxisSupp->getSecondXAxisTitle();
4140 }
4141
4142 nAxisType = lcl_getCategoryAxisType(mxNewDiagram, 0, 1);
4143 if( nAxisType != -1 )
4144 nAxisType = getRealXAxisType(nAxisType);
4145 else
4146 nAxisType = getXAxisTypeByChartType( getChartType() );
4147 // FIXME: axPos, need to check axis direction
4148 sAxPos = "t";
4149 break;
4150 }
4151 case AXIS_SECONDARY_Y:
4152 {
4153 Reference< css::chart::XTwoAxisYSupplier > xAxisTwoYSupp( mxDiagram, uno::UNO_QUERY );
4154 if( xAxisTwoYSupp.is())
4155 xAxisProp = xAxisTwoYSupp->getSecondaryYAxis();
4156 if( bHasSecondaryYAxisTitle )
4157 {
4158 Reference< css::chart::XSecondAxisTitleSupplier > xAxisSupp( mxDiagram, uno::UNO_QUERY );
4159 xAxisTitle = xAxisSupp->getSecondYAxisTitle();
4160 }
4161
4162 nAxisType = XML_valAx;
4163 // FIXME: axPos, need to check axis direction
4164 sAxPos = "r";
4165 break;
4166 }
4167 }
4168
4169 if (bIsChartex) {
4170 exportOneAxis_chartex(xAxisProp, xAxisTitle, xMajorGrid, xMinorGrid, nAxisType,
4171 rAxisIdPair);
4172 } else {
4173 exportOneAxis_chart(xAxisProp, xAxisTitle, xMajorGrid, xMinorGrid, nAxisType,
4174 sAxPos, rAxisIdPair);
4175 }
4176 }
4177
getTickMarkLocStr(sal_Int32 nValue)4178 static const char *getTickMarkLocStr(sal_Int32 nValue)
4179 {
4180 const bool bInner = nValue & css::chart::ChartAxisMarks::INNER;
4181 const bool bOuter = nValue & css::chart::ChartAxisMarks::OUTER;
4182 if( bInner && bOuter ) {
4183 return "cross";
4184 } else if( bInner ) {
4185 return "in";
4186 } else if( bOuter ) {
4187 return "out";
4188 } else {
4189 return "none";
4190 }
4191 }
4192
exportOneAxis_chart(const Reference<XPropertySet> & xAxisProp,const Reference<drawing::XShape> & xAxisTitle,const Reference<XPropertySet> & xMajorGrid,const Reference<XPropertySet> & xMinorGrid,sal_Int32 nAxisType,const char * sAxisPos,const AxisIdPair & rAxisIdPair)4193 void ChartExport::exportOneAxis_chart(
4194 const Reference< XPropertySet >& xAxisProp,
4195 const Reference< drawing::XShape >& xAxisTitle,
4196 const Reference< XPropertySet >& xMajorGrid,
4197 const Reference< XPropertySet >& xMinorGrid,
4198 sal_Int32 nAxisType,
4199 const char* sAxisPos,
4200 const AxisIdPair& rAxisIdPair)
4201 {
4202 FSHelperPtr pFS = GetFS();
4203 pFS->startElement(FSNS(XML_c, nAxisType));
4204 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(rAxisIdPair.nAxisId));
4205
4206 pFS->startElement(FSNS(XML_c, XML_scaling));
4207
4208 // logBase, min, max
4209 if(GetProperty( xAxisProp, u"Logarithmic"_ustr ) )
4210 {
4211 bool bLogarithmic = false;
4212 mAny >>= bLogarithmic;
4213 if( bLogarithmic )
4214 {
4215 // default value is 10?
4216 pFS->singleElement(FSNS(XML_c, XML_logBase), XML_val, OString::number(10));
4217 }
4218 }
4219
4220 // orientation: minMax, maxMin
4221 bool bReverseDirection = false;
4222 if(GetProperty( xAxisProp, u"ReverseDirection"_ustr ) )
4223 mAny >>= bReverseDirection;
4224
4225 const char* orientation = bReverseDirection ? "maxMin":"minMax";
4226 pFS->singleElement(FSNS(XML_c, XML_orientation), XML_val, orientation);
4227
4228 bool bAutoMax = false;
4229 if(GetProperty( xAxisProp, u"AutoMax"_ustr ) )
4230 mAny >>= bAutoMax;
4231
4232 if( !bAutoMax && (GetProperty( xAxisProp, u"Max"_ustr ) ) )
4233 {
4234 double dMax = 0;
4235 mAny >>= dMax;
4236 pFS->singleElement(FSNS(XML_c, XML_max), XML_val, OString::number(dMax));
4237 }
4238
4239 bool bAutoMin = false;
4240 if(GetProperty( xAxisProp, u"AutoMin"_ustr ) )
4241 mAny >>= bAutoMin;
4242
4243 if( !bAutoMin && (GetProperty( xAxisProp, u"Min"_ustr ) ) )
4244 {
4245 double dMin = 0;
4246 mAny >>= dMin;
4247 pFS->singleElement(FSNS(XML_c, XML_min), XML_val, OString::number(dMin));
4248 }
4249
4250 pFS->endElement( FSNS( XML_c, XML_scaling ) );
4251
4252 bool bVisible = true;
4253 if( xAxisProp.is() )
4254 {
4255 xAxisProp->getPropertyValue(u"Visible"_ustr) >>= bVisible;
4256 }
4257
4258 // only export each axis only once non-deleted
4259 auto aItInsertedPair = maExportedAxis.insert(rAxisIdPair.nAxisType);
4260 bool bDeleted = !aItInsertedPair.second;
4261
4262 pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, !bDeleted && bVisible ? "0" : "1");
4263
4264 // FIXME: axPos, need to check the property "ReverseDirection"
4265 pFS->singleElement(FSNS(XML_c, XML_axPos), XML_val, sAxisPos);
4266 // major grid line
4267 if( xMajorGrid.is())
4268 {
4269 pFS->startElement(FSNS(XML_c, XML_majorGridlines));
4270 exportShapeProps( xMajorGrid, false );
4271 pFS->endElement( FSNS( XML_c, XML_majorGridlines ) );
4272 }
4273
4274 // minor grid line
4275 if( xMinorGrid.is())
4276 {
4277 pFS->startElement(FSNS(XML_c, XML_minorGridlines));
4278 exportShapeProps( xMinorGrid, false );
4279 pFS->endElement( FSNS( XML_c, XML_minorGridlines ) );
4280 }
4281
4282 // title
4283 if( xAxisTitle.is() )
4284 exportTitle( xAxisTitle, false );
4285
4286 bool bLinkedNumFmt = true;
4287 if (GetProperty(xAxisProp, u"LinkNumberFormatToSource"_ustr))
4288 mAny >>= bLinkedNumFmt;
4289
4290 OUString aNumberFormatString(u"General"_ustr);
4291 if (GetProperty(xAxisProp, u"NumberFormat"_ustr))
4292 {
4293 sal_Int32 nKey = 0;
4294 mAny >>= nKey;
4295 aNumberFormatString = getNumberFormatCode(nKey);
4296 }
4297
4298 pFS->singleElement(FSNS(XML_c, XML_numFmt),
4299 XML_formatCode, aNumberFormatString,
4300 XML_sourceLinked, bLinkedNumFmt ? "1" : "0");
4301
4302 // majorTickMark
4303 sal_Int32 nValue = 0;
4304 if(GetProperty( xAxisProp, u"Marks"_ustr ) )
4305 {
4306 mAny >>= nValue;
4307 pFS->singleElement(FSNS(XML_c, XML_majorTickMark), XML_val,
4308 getTickMarkLocStr(nValue));
4309 }
4310 // minorTickMark
4311 if(GetProperty( xAxisProp, u"HelpMarks"_ustr ) )
4312 {
4313 mAny >>= nValue;
4314 pFS->singleElement(FSNS(XML_c, XML_minorTickMark), XML_val,
4315 getTickMarkLocStr(nValue));
4316 }
4317 // tickLblPos
4318 const char* sTickLblPos = nullptr;
4319 bool bDisplayLabel = true;
4320 if(GetProperty( xAxisProp, u"DisplayLabels"_ustr ) )
4321 mAny >>= bDisplayLabel;
4322 if( bDisplayLabel && (GetProperty( xAxisProp, u"LabelPosition"_ustr ) ) )
4323 {
4324 css::chart::ChartAxisLabelPosition eLabelPosition = css::chart::ChartAxisLabelPosition_NEAR_AXIS;
4325 mAny >>= eLabelPosition;
4326 switch( eLabelPosition )
4327 {
4328 case css::chart::ChartAxisLabelPosition_NEAR_AXIS:
4329 case css::chart::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE:
4330 sTickLblPos = "nextTo";
4331 break;
4332 case css::chart::ChartAxisLabelPosition_OUTSIDE_START:
4333 sTickLblPos = "low";
4334 break;
4335 case css::chart::ChartAxisLabelPosition_OUTSIDE_END:
4336 sTickLblPos = "high";
4337 break;
4338 default:
4339 sTickLblPos = "nextTo";
4340 break;
4341 }
4342 }
4343 else
4344 {
4345 sTickLblPos = "none";
4346 }
4347 pFS->singleElement(FSNS(XML_c, XML_tickLblPos), XML_val, sTickLblPos);
4348
4349 // shape properties
4350 exportShapeProps( xAxisProp, false );
4351
4352 exportTextProps(xAxisProp, false);
4353
4354 pFS->singleElement(FSNS(XML_c, XML_crossAx), XML_val, OString::number(rAxisIdPair.nCrossAx));
4355
4356 // crosses & crossesAt
4357 bool bCrossesValue = false;
4358 const char* sCrosses = nullptr;
4359 // do not export the CrossoverPosition/CrossoverValue, if the axis is deleted and not visible
4360 if( GetProperty( xAxisProp, u"CrossoverPosition"_ustr ) && !bDeleted && bVisible )
4361 {
4362 css::chart::ChartAxisPosition ePosition( css::chart::ChartAxisPosition_ZERO );
4363 mAny >>= ePosition;
4364 switch( ePosition )
4365 {
4366 case css::chart::ChartAxisPosition_START:
4367 sCrosses = "min";
4368 break;
4369 case css::chart::ChartAxisPosition_END:
4370 sCrosses = "max";
4371 break;
4372 case css::chart::ChartAxisPosition_ZERO:
4373 sCrosses = "autoZero";
4374 break;
4375 default:
4376 bCrossesValue = true;
4377 break;
4378 }
4379 }
4380
4381 if( bCrossesValue && GetProperty( xAxisProp, u"CrossoverValue"_ustr ) )
4382 {
4383 double dValue = 0;
4384 mAny >>= dValue;
4385 pFS->singleElement(FSNS(XML_c, XML_crossesAt), XML_val, OString::number(dValue));
4386 }
4387 else
4388 {
4389 if(sCrosses)
4390 {
4391 pFS->singleElement(FSNS(XML_c, XML_crosses), XML_val, sCrosses);
4392 }
4393 }
4394
4395 if( ( nAxisType == XML_catAx )
4396 || ( nAxisType == XML_dateAx ) )
4397 {
4398 // FIXME: seems not support? use default value,
4399 const char* const isAuto = "1";
4400 pFS->singleElement(FSNS(XML_c, XML_auto), XML_val, isAuto);
4401
4402 if( nAxisType == XML_catAx )
4403 {
4404 // FIXME: seems not support? lblAlgn
4405 const char* const sLblAlgn = "ctr";
4406 pFS->singleElement(FSNS(XML_c, XML_lblAlgn), XML_val, sLblAlgn);
4407 }
4408
4409 // FIXME: seems not support? lblOffset
4410 pFS->singleElement(FSNS(XML_c, XML_lblOffset), XML_val, OString::number(100));
4411
4412 // export baseTimeUnit, majorTimeUnit, minorTimeUnit of Date axis
4413 if( nAxisType == XML_dateAx )
4414 {
4415 sal_Int32 nAxisIndex = -1;
4416 if( rAxisIdPair.nAxisType == AXIS_PRIMARY_X )
4417 nAxisIndex = 0;
4418 else if( rAxisIdPair.nAxisType == AXIS_SECONDARY_X )
4419 nAxisIndex = 1;
4420
4421 cssc::TimeIncrement aTimeIncrement = lcl_getDateTimeIncrement( mxNewDiagram, nAxisIndex );
4422 sal_Int32 nTimeResolution = css::chart::TimeUnit::DAY;
4423 if( aTimeIncrement.TimeResolution >>= nTimeResolution )
4424 pFS->singleElement(FSNS(XML_c, XML_baseTimeUnit), XML_val, lclGetTimeUnitToken(nTimeResolution));
4425
4426 cssc::TimeInterval aInterval;
4427 if( aTimeIncrement.MajorTimeInterval >>= aInterval )
4428 {
4429 pFS->singleElement(FSNS(XML_c, XML_majorUnit), XML_val, OString::number(aInterval.Number));
4430 pFS->singleElement(FSNS(XML_c, XML_majorTimeUnit), XML_val, lclGetTimeUnitToken(aInterval.TimeUnit));
4431 }
4432 if( aTimeIncrement.MinorTimeInterval >>= aInterval )
4433 {
4434 pFS->singleElement(FSNS(XML_c, XML_minorUnit), XML_val, OString::number(aInterval.Number));
4435 pFS->singleElement(FSNS(XML_c, XML_minorTimeUnit), XML_val, lclGetTimeUnitToken(aInterval.TimeUnit));
4436 }
4437 }
4438
4439 // FIXME: seems not support? noMultiLvlLbl
4440 pFS->singleElement(FSNS(XML_c, XML_noMultiLvlLbl), XML_val, OString::number(0));
4441 }
4442
4443 // crossBetween
4444 if( nAxisType == XML_valAx )
4445 {
4446 if( lcl_isCategoryAxisShifted( mxNewDiagram ))
4447 pFS->singleElement(FSNS(XML_c, XML_crossBetween), XML_val, "between");
4448 else
4449 pFS->singleElement(FSNS(XML_c, XML_crossBetween), XML_val, "midCat");
4450 }
4451
4452 // majorUnit
4453 bool bAutoStepMain = false;
4454 if(GetProperty( xAxisProp, u"AutoStepMain"_ustr ) )
4455 mAny >>= bAutoStepMain;
4456
4457 if( !bAutoStepMain && (GetProperty( xAxisProp, u"StepMain"_ustr ) ) )
4458 {
4459 double dMajorUnit = 0;
4460 mAny >>= dMajorUnit;
4461 pFS->singleElement(FSNS(XML_c, XML_majorUnit), XML_val, OString::number(dMajorUnit));
4462 }
4463 // minorUnit
4464 bool bAutoStepHelp = false;
4465 if(GetProperty( xAxisProp, u"AutoStepHelp"_ustr ) )
4466 mAny >>= bAutoStepHelp;
4467
4468 if( !bAutoStepHelp && (GetProperty( xAxisProp, u"StepHelp"_ustr ) ) )
4469 {
4470 double dMinorUnit = 0;
4471 mAny >>= dMinorUnit;
4472 if( GetProperty( xAxisProp, u"StepHelpCount"_ustr ) )
4473 {
4474 sal_Int32 dMinorUnitCount = 0;
4475 mAny >>= dMinorUnitCount;
4476 // tdf#114168 Don't save minor unit if number of step help count is 5 (which is default for MS Excel),
4477 // to allow proper .xlsx import. If minorUnit is set and majorUnit not, then it is impossible
4478 // to calculate StepHelpCount.
4479 if( dMinorUnitCount != 5 )
4480 {
4481 pFS->singleElement( FSNS( XML_c, XML_minorUnit ),
4482 XML_val, OString::number( dMinorUnit ) );
4483 }
4484 }
4485 }
4486
4487 if( nAxisType == XML_valAx && GetProperty( xAxisProp, u"DisplayUnits"_ustr ) )
4488 {
4489 bool bDisplayUnits = false;
4490 mAny >>= bDisplayUnits;
4491 if(bDisplayUnits)
4492 {
4493 if(GetProperty( xAxisProp, u"BuiltInUnit"_ustr ))
4494 {
4495 OUString aVal;
4496 mAny >>= aVal;
4497 if(!aVal.isEmpty())
4498 {
4499 pFS->startElement(FSNS(XML_c, XML_dispUnits));
4500
4501 pFS->singleElement(FSNS(XML_c, XML_builtInUnit), XML_val, aVal);
4502
4503 pFS->singleElement(FSNS( XML_c, XML_dispUnitsLbl ));
4504 pFS->endElement( FSNS( XML_c, XML_dispUnits ) );
4505 }
4506 }
4507 }
4508 }
4509
4510 pFS->endElement( FSNS( XML_c, nAxisType ) );
4511 }
4512
exportOneAxis_chartex(const Reference<XPropertySet> & xAxisProp,const Reference<drawing::XShape> & xAxisTitle,const Reference<XPropertySet> & xMajorGrid,const Reference<XPropertySet> & xMinorGrid,sal_Int32 nAxisType,const AxisIdPair & rAxisIdPair)4513 void ChartExport::exportOneAxis_chartex(
4514 const Reference< XPropertySet >& xAxisProp,
4515 const Reference< drawing::XShape >& xAxisTitle,
4516 const Reference< XPropertySet >& xMajorGrid,
4517 const Reference< XPropertySet >& xMinorGrid,
4518 sal_Int32 nAxisType,
4519 const AxisIdPair& rAxisIdPair)
4520 {
4521 FSHelperPtr pFS = GetFS();
4522 pFS->startElement(FSNS(XML_cx, XML_axis), XML_id, OString::number(rAxisIdPair.nAxisId));
4523
4524 // The following is in the 2010 chart code above:
4525 // bool bVisible = true;
4526 // if( xAxisProp.is() )
4527 // {
4528 // xAxisProp->getPropertyValue(u"Visible"_ustr) >>= bVisible;
4529 // }
4530 // // only export each axis only once non-deleted
4531 // auto aItInsertedPair = maExportedAxis.insert(rAxisIdPair.nAxisType);
4532 // bool bDeleted = !aItInsertedPair.second;
4533 //
4534 // pFS->singleElement(FSNS(XML_c, XML_delete), XML_val, !bDeleted && bVisible ? "0" : "1");
4535 //
4536 // Is chartex attribute "hidden" the same as !bVisible? And what to do if
4537 // the axis is deleted, per above?
4538
4539 // ==== catScaling/valScaling
4540 switch (nAxisType) {
4541 case XML_catAx:
4542 pFS->singleElement(FSNS(XML_cx, XML_catScaling) /* TODO: handle gapWidth */);
4543 break;
4544 case XML_valAx:
4545 {
4546 bool bAutoMax = false;
4547 double dMax = 0; // Make VS happy
4548 bool bMaxSpecified = false;
4549 if(GetProperty( xAxisProp, u"AutoMax"_ustr ) )
4550 mAny >>= bAutoMax;
4551
4552 if( !bAutoMax && (GetProperty( xAxisProp, u"Max"_ustr ) ) )
4553 {
4554 mAny >>= dMax;
4555 bMaxSpecified = true;
4556 }
4557
4558 bool bAutoMin = false;
4559 double dMin = 0; // Make VS happy
4560 bool bMinSpecified = false;
4561 if(GetProperty( xAxisProp, u"AutoMin"_ustr ) )
4562 mAny >>= bAutoMin;
4563
4564 if( !bAutoMin && (GetProperty( xAxisProp, u"Min"_ustr ) ) )
4565 {
4566 mAny >>= dMin;
4567 bMinSpecified = true;
4568 }
4569
4570 // TODO: handle majorUnit/minorUnit in the following
4571 if (bMaxSpecified && bMinSpecified) {
4572 pFS->singleElement(FSNS(XML_cx, XML_valScaling),
4573 XML_max, OString::number(dMax),
4574 XML_min, OString::number(dMin));
4575 } else if (!bMaxSpecified && bMinSpecified) {
4576 pFS->singleElement(FSNS(XML_cx, XML_valScaling),
4577 XML_min, OString::number(dMin));
4578 } else if (bMaxSpecified && !bMinSpecified) {
4579 pFS->singleElement(FSNS(XML_cx, XML_valScaling),
4580 XML_max, OString::number(dMax));
4581 } else {
4582 pFS->singleElement(FSNS(XML_cx, XML_valScaling));
4583 }
4584
4585 }
4586 break;
4587 default:
4588 // shouldn't happen
4589 assert(false);
4590 }
4591
4592 // ==== title
4593 if( xAxisTitle.is() ) {
4594 exportTitle( xAxisTitle, true );
4595 }
4596
4597 // ==== units
4598 if (GetProperty( xAxisProp, u"DisplayUnits"_ustr ) )
4599 {
4600 bool bDisplayUnits = false;
4601 mAny >>= bDisplayUnits;
4602 if (bDisplayUnits)
4603 {
4604 if (GetProperty( xAxisProp, u"BuiltInUnit"_ustr ))
4605 {
4606 OUString aVal;
4607 mAny >>= aVal;
4608 if(!aVal.isEmpty())
4609 {
4610 pFS->startElement(FSNS(XML_cx, XML_units));
4611
4612 pFS->startElement(FSNS(XML_cx, XML_unitsLabel));
4613
4614 lcl_writeChartexString(pFS, aVal);
4615
4616 pFS->endElement(FSNS(XML_cx, XML_unitsLabel));
4617
4618 pFS->endElement( FSNS( XML_cx, XML_units ) );
4619 }
4620 }
4621 }
4622 }
4623
4624 // ==== majorGridlines
4625 if( xMajorGrid.is())
4626 {
4627 pFS->startElement(FSNS(XML_cx, XML_majorGridlines));
4628 exportShapeProps( xMajorGrid, true );
4629 pFS->endElement( FSNS( XML_cx, XML_majorGridlines ) );
4630 }
4631
4632 // ==== minorGridlines
4633 if( xMinorGrid.is())
4634 {
4635 pFS->startElement(FSNS(XML_cx, XML_minorGridlines));
4636 exportShapeProps( xMinorGrid, true );
4637 pFS->endElement( FSNS( XML_cx, XML_minorGridlines ) );
4638 }
4639
4640 // ==== majorTickMarks
4641 if (GetProperty( xAxisProp, u"Marks"_ustr ) )
4642 {
4643 sal_Int32 nValue = 0;
4644 mAny >>= nValue;
4645 pFS->singleElement(FSNS(XML_cx, XML_majorTickMarks), XML_type,
4646 getTickMarkLocStr(nValue));
4647 }
4648
4649 // ==== minorTickMarks
4650 if (GetProperty( xAxisProp, u"HelpMarks"_ustr ) )
4651 {
4652 sal_Int32 nValue = 0;
4653 mAny >>= nValue;
4654 pFS->singleElement(FSNS(XML_cx, XML_minorTickMarks), XML_type,
4655 getTickMarkLocStr(nValue));
4656 }
4657
4658 // ==== tickLabels consists of nothing but an extLst so I don't know how to
4659 // handle it
4660
4661 // ==== numFmt
4662 bool bLinkedNumFmt = true;
4663 if (GetProperty(xAxisProp, u"LinkNumberFormatToSource"_ustr))
4664 mAny >>= bLinkedNumFmt;
4665
4666 OUString aNumberFormatString(u"General"_ustr);
4667 if (GetProperty(xAxisProp, u"NumberFormat"_ustr))
4668 {
4669 sal_Int32 nKey = 0;
4670 mAny >>= nKey;
4671 aNumberFormatString = getNumberFormatCode(nKey);
4672 }
4673
4674 // We're always outputting this, which presumably isn't necessary, but it's
4675 // not clear what the defaults are for determining if an explicit element is
4676 // needed
4677 pFS->singleElement(FSNS(XML_cx, XML_numFmt),
4678 XML_formatCode, aNumberFormatString,
4679 XML_sourceLinked, bLinkedNumFmt ? "1" : "0");
4680
4681 // ==== spPr
4682 exportShapeProps( xAxisProp, true );
4683
4684 // ==== txPr
4685 exportTextProps(xAxisProp, true);
4686
4687 pFS->endElement( FSNS( XML_cx, XML_axis ) );
4688 }
4689
4690 namespace {
4691
4692 struct LabelPlacementParam
4693 {
4694 bool mbExport;
4695 sal_Int32 meDefault;
4696
4697 std::unordered_set<sal_Int32> maAllowedValues;
4698
LabelPlacementParamoox::drawingml::__anon0ed582600811::LabelPlacementParam4699 LabelPlacementParam(bool bExport, sal_Int32 nDefault) :
4700 mbExport(bExport),
4701 meDefault(nDefault),
4702 maAllowedValues(
4703 {
4704 css::chart::DataLabelPlacement::OUTSIDE,
4705 css::chart::DataLabelPlacement::INSIDE,
4706 css::chart::DataLabelPlacement::CENTER,
4707 css::chart::DataLabelPlacement::NEAR_ORIGIN,
4708 css::chart::DataLabelPlacement::TOP,
4709 css::chart::DataLabelPlacement::BOTTOM,
4710 css::chart::DataLabelPlacement::LEFT,
4711 css::chart::DataLabelPlacement::RIGHT,
4712 css::chart::DataLabelPlacement::AVOID_OVERLAP
4713 }
4714 )
4715 {}
4716 };
4717
toOOXMLPlacement(sal_Int32 nPlacement)4718 const char* toOOXMLPlacement( sal_Int32 nPlacement )
4719 {
4720 switch (nPlacement)
4721 {
4722 case css::chart::DataLabelPlacement::OUTSIDE: return "outEnd";
4723 case css::chart::DataLabelPlacement::INSIDE: return "inEnd";
4724 case css::chart::DataLabelPlacement::CENTER: return "ctr";
4725 case css::chart::DataLabelPlacement::NEAR_ORIGIN: return "inBase";
4726 case css::chart::DataLabelPlacement::TOP: return "t";
4727 case css::chart::DataLabelPlacement::BOTTOM: return "b";
4728 case css::chart::DataLabelPlacement::LEFT: return "l";
4729 case css::chart::DataLabelPlacement::RIGHT: return "r";
4730 case css::chart::DataLabelPlacement::CUSTOM:
4731 case css::chart::DataLabelPlacement::AVOID_OVERLAP: return "bestFit";
4732 default:
4733 ;
4734 }
4735
4736 return "outEnd";
4737 }
4738
getFieldTypeString(const chart2::DataPointCustomLabelFieldType aType)4739 OUString getFieldTypeString( const chart2::DataPointCustomLabelFieldType aType )
4740 {
4741 switch (aType)
4742 {
4743 case chart2::DataPointCustomLabelFieldType_CATEGORYNAME:
4744 return u"CATEGORYNAME"_ustr;
4745
4746 case chart2::DataPointCustomLabelFieldType_SERIESNAME:
4747 return u"SERIESNAME"_ustr;
4748
4749 case chart2::DataPointCustomLabelFieldType_VALUE:
4750 return u"VALUE"_ustr;
4751
4752 case chart2::DataPointCustomLabelFieldType_CELLREF:
4753 return u"CELLREF"_ustr;
4754
4755 case chart2::DataPointCustomLabelFieldType_CELLRANGE:
4756 return u"CELLRANGE"_ustr;
4757
4758 default:
4759 break;
4760 }
4761 return OUString();
4762 }
4763
writeRunProperties(ChartExport * pChartExport,Reference<XPropertySet> const & xPropertySet)4764 void writeRunProperties( ChartExport* pChartExport, Reference<XPropertySet> const & xPropertySet )
4765 {
4766 bool bDummy = false;
4767 sal_Int32 nDummy;
4768 pChartExport->WriteRunProperties(xPropertySet, false, XML_rPr, true, bDummy, nDummy);
4769 }
4770
writeCustomLabel(const FSHelperPtr & pFS,ChartExport * pChartExport,const Sequence<Reference<chart2::XDataPointCustomLabelField>> & rCustomLabelFields,sal_Int32 nLabelIndex,DataLabelsRange & rDLblsRange)4771 void writeCustomLabel( const FSHelperPtr& pFS, ChartExport* pChartExport,
4772 const Sequence<Reference<chart2::XDataPointCustomLabelField>>& rCustomLabelFields,
4773 sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange )
4774 {
4775 pFS->startElement(FSNS(XML_c, XML_tx));
4776 pFS->startElement(FSNS(XML_c, XML_rich));
4777
4778 // TODO: body properties?
4779 pFS->singleElement(FSNS(XML_a, XML_bodyPr));
4780
4781 OUString sFieldType;
4782 OUString sContent;
4783 pFS->startElement(FSNS(XML_a, XML_p));
4784
4785 for (auto& rField : rCustomLabelFields)
4786 {
4787 Reference<XPropertySet> xPropertySet(rField, UNO_QUERY);
4788 chart2::DataPointCustomLabelFieldType aType = rField->getFieldType();
4789 sFieldType.clear();
4790 sContent.clear();
4791 bool bNewParagraph = false;
4792
4793 if (aType == chart2::DataPointCustomLabelFieldType_CELLRANGE &&
4794 rField->getDataLabelsRange())
4795 {
4796 if (rDLblsRange.getRange().isEmpty())
4797 rDLblsRange.setRange(rField->getCellRange());
4798
4799 if (!rDLblsRange.hasLabel(nLabelIndex))
4800 rDLblsRange.setLabel(nLabelIndex, rField->getString());
4801
4802 sContent = "[CELLRANGE]";
4803 }
4804 else
4805 {
4806 sContent = rField->getString();
4807 }
4808
4809 if (aType == chart2::DataPointCustomLabelFieldType_NEWLINE)
4810 bNewParagraph = true;
4811 else if (aType != chart2::DataPointCustomLabelFieldType_TEXT)
4812 sFieldType = getFieldTypeString(aType);
4813
4814 if (bNewParagraph)
4815 {
4816 pFS->endElement(FSNS(XML_a, XML_p));
4817 pFS->startElement(FSNS(XML_a, XML_p));
4818 continue;
4819 }
4820
4821 if (sFieldType.isEmpty())
4822 {
4823 // Normal text run
4824 pFS->startElement(FSNS(XML_a, XML_r));
4825 writeRunProperties(pChartExport, xPropertySet);
4826
4827 pFS->startElement(FSNS(XML_a, XML_t));
4828 pFS->writeEscaped(sContent);
4829 pFS->endElement(FSNS(XML_a, XML_t));
4830
4831 pFS->endElement(FSNS(XML_a, XML_r));
4832 }
4833 else
4834 {
4835 // Field
4836 pFS->startElement(FSNS(XML_a, XML_fld), XML_id, rField->getGuid(), XML_type,
4837 sFieldType);
4838 writeRunProperties(pChartExport, xPropertySet);
4839
4840 pFS->startElement(FSNS(XML_a, XML_t));
4841 pFS->writeEscaped(sContent);
4842 pFS->endElement(FSNS(XML_a, XML_t));
4843
4844 pFS->endElement(FSNS(XML_a, XML_fld));
4845 }
4846 }
4847
4848 pFS->endElement(FSNS(XML_a, XML_p));
4849 pFS->endElement(FSNS(XML_c, XML_rich));
4850 pFS->endElement(FSNS(XML_c, XML_tx));
4851 }
4852
writeLabelProperties(const FSHelperPtr & pFS,ChartExport * pChartExport,const uno::Reference<beans::XPropertySet> & xPropSet,const LabelPlacementParam & rLabelParam,sal_Int32 nLabelIndex,DataLabelsRange & rDLblsRange,bool bIsChartex)4853 void writeLabelProperties( const FSHelperPtr& pFS, ChartExport* pChartExport,
4854 const uno::Reference<beans::XPropertySet>& xPropSet, const LabelPlacementParam& rLabelParam,
4855 sal_Int32 nLabelIndex, DataLabelsRange& rDLblsRange,
4856 bool bIsChartex)
4857 {
4858 if (!xPropSet.is())
4859 return;
4860
4861 const sal_Int32 nChartNS = bIsChartex ? XML_cx : XML_c;
4862
4863 chart2::DataPointLabel aLabel;
4864 Sequence<Reference<chart2::XDataPointCustomLabelField>> aCustomLabelFields;
4865 sal_Int32 nLabelBorderWidth = 0;
4866 sal_Int32 nLabelBorderColor = 0x00FFFFFF;
4867 sal_Int32 nLabelFillColor = -1;
4868
4869 xPropSet->getPropertyValue(u"Label"_ustr) >>= aLabel;
4870 xPropSet->getPropertyValue(u"CustomLabelFields"_ustr) >>= aCustomLabelFields;
4871 xPropSet->getPropertyValue(u"LabelBorderWidth"_ustr) >>= nLabelBorderWidth;
4872 xPropSet->getPropertyValue(u"LabelBorderColor"_ustr) >>= nLabelBorderColor;
4873 xPropSet->getPropertyValue(u"LabelFillColor"_ustr) >>= nLabelFillColor;
4874
4875 if (nLabelBorderWidth > 0 || nLabelFillColor != -1)
4876 {
4877 pFS->startElement(FSNS(nChartNS, XML_spPr));
4878
4879 if (nLabelFillColor != -1)
4880 {
4881 ::Color nColor(ColorTransparency, nLabelFillColor);
4882 if (nColor.IsTransparent())
4883 pChartExport->WriteSolidFill(nColor, nColor.GetAlpha());
4884 else
4885 pChartExport->WriteSolidFill(nColor);
4886 }
4887
4888 if (nLabelBorderWidth > 0)
4889 {
4890 pFS->startElement(FSNS(XML_a, XML_ln), XML_w,
4891 OString::number(convertHmmToEmu(nLabelBorderWidth)));
4892
4893 if (nLabelBorderColor != -1)
4894 {
4895 ::Color nColor(ColorTransparency, nLabelBorderColor);
4896 if (nColor.IsTransparent())
4897 pChartExport->WriteSolidFill(nColor, nColor.GetAlpha());
4898 else
4899 pChartExport->WriteSolidFill(nColor);
4900 }
4901
4902 pFS->endElement(FSNS(XML_a, XML_ln));
4903 }
4904
4905 pFS->endElement(FSNS(nChartNS, XML_spPr));
4906 }
4907
4908 pChartExport->exportTextProps(xPropSet, bIsChartex);
4909
4910 if (aCustomLabelFields.hasElements())
4911 writeCustomLabel(pFS, pChartExport, aCustomLabelFields, nLabelIndex, rDLblsRange);
4912
4913 if (!bIsChartex) {
4914 // In chartex label position is an attribute of cx:dataLabel
4915 if (rLabelParam.mbExport)
4916 {
4917 sal_Int32 nLabelPlacement = rLabelParam.meDefault;
4918 if (xPropSet->getPropertyValue(u"LabelPlacement"_ustr) >>= nLabelPlacement)
4919 {
4920 if (!rLabelParam.maAllowedValues.count(nLabelPlacement))
4921 nLabelPlacement = rLabelParam.meDefault;
4922 pFS->singleElement(FSNS(XML_c, XML_dLblPos), XML_val, toOOXMLPlacement(nLabelPlacement));
4923 }
4924 }
4925
4926 pFS->singleElement(FSNS(XML_c, XML_showLegendKey), XML_val, ToPsz10(aLabel.ShowLegendSymbol));
4927 pFS->singleElement(FSNS(XML_c, XML_showVal), XML_val, ToPsz10(aLabel.ShowNumber));
4928 pFS->singleElement(FSNS(XML_c, XML_showCatName), XML_val, ToPsz10(aLabel.ShowCategoryName));
4929 pFS->singleElement(FSNS(XML_c, XML_showSerName), XML_val, ToPsz10(aLabel.ShowSeriesName));
4930 pFS->singleElement(FSNS(XML_c, XML_showPercent), XML_val, ToPsz10(aLabel.ShowNumberInPercent));
4931 }
4932
4933 // Export the text "separator" if exists
4934 uno::Any aAny = xPropSet->getPropertyValue(u"LabelSeparator"_ustr);
4935 if( aAny.hasValue() )
4936 {
4937 OUString nLabelSeparator;
4938 aAny >>= nLabelSeparator;
4939 pFS->startElement(FSNS(nChartNS, XML_separator));
4940 pFS->writeEscaped( nLabelSeparator );
4941 pFS->endElement( FSNS(nChartNS, XML_separator ) );
4942 }
4943
4944 if (rDLblsRange.hasLabel(nLabelIndex))
4945 {
4946 pFS->startElement(FSNS(nChartNS, XML_extLst));
4947 // TODO: is the following correct for chartex?
4948 pFS->startElement(FSNS(nChartNS, XML_ext), XML_uri,
4949 "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}", FSNS(XML_xmlns, XML_c15),
4950 pChartExport->GetFB()->getNamespaceURL(OOX_NS(c15)));
4951
4952 pFS->singleElement(FSNS(XML_c15, XML_showDataLabelsRange), XML_val, "1");
4953
4954 pFS->endElement(FSNS(nChartNS, XML_ext));
4955 pFS->endElement(FSNS(nChartNS, XML_extLst));
4956 }
4957 }
4958
4959 }
4960
exportDataLabels(const uno::Reference<chart2::XDataSeries> & xSeries,sal_Int32 nSeriesLength,sal_Int32 eChartType,DataLabelsRange & rDLblsRange,bool bIsChartex)4961 void ChartExport::exportDataLabels(
4962 const uno::Reference<chart2::XDataSeries> & xSeries, sal_Int32 nSeriesLength, sal_Int32 eChartType,
4963 DataLabelsRange& rDLblsRange,
4964 bool bIsChartex)
4965 {
4966 if (!xSeries.is() || nSeriesLength <= 0)
4967 return;
4968
4969 uno::Reference<beans::XPropertySet> xPropSet(xSeries, uno::UNO_QUERY);
4970 if (!xPropSet.is())
4971 return;
4972
4973 FSHelperPtr pFS = GetFS();
4974
4975 if (bIsChartex) {
4976 pFS->startElement(FSNS(XML_cx, XML_dataLabels));
4977 } else {
4978 pFS->startElement(FSNS(XML_c, XML_dLbls));
4979 }
4980
4981 bool bLinkedNumFmt = true;
4982 if (GetProperty(xPropSet, u"LinkNumberFormatToSource"_ustr))
4983 mAny >>= bLinkedNumFmt;
4984
4985 chart2::DataPointLabel aLabel;
4986 bool bLabelIsNumberFormat = true;
4987 if( xPropSet->getPropertyValue(u"Label"_ustr) >>= aLabel )
4988 bLabelIsNumberFormat = aLabel.ShowNumber;
4989
4990 if (GetProperty(xPropSet, bLabelIsNumberFormat ? u"NumberFormat"_ustr : u"PercentageNumberFormat"_ustr))
4991 {
4992 sal_Int32 nKey = 0;
4993 mAny >>= nKey;
4994
4995 OUString aNumberFormatString = getNumberFormatCode(nKey);
4996
4997 if (bIsChartex) {
4998 pFS->singleElement(FSNS(XML_cx, XML_numFmt),
4999 XML_formatCode, aNumberFormatString,
5000 XML_sourceLinked, ToPsz10(bLinkedNumFmt));
5001 } else {
5002 pFS->singleElement(FSNS(XML_c, XML_numFmt),
5003 XML_formatCode, aNumberFormatString,
5004 XML_sourceLinked, ToPsz10(bLinkedNumFmt));
5005 }
5006 }
5007
5008 uno::Sequence<sal_Int32> aAttrLabelIndices;
5009 xPropSet->getPropertyValue(u"AttributedDataPoints"_ustr) >>= aAttrLabelIndices;
5010
5011 // We must not export label placement property when the chart type doesn't
5012 // support this option in MS Office, else MS Office would think the file
5013 // is corrupt & refuse to open it.
5014
5015 const chart::TypeGroupInfo& rInfo = chart::GetTypeGroupInfo(static_cast<chart::TypeId>(eChartType));
5016 LabelPlacementParam aParam(!mbIs3DChart, rInfo.mnDefLabelPos);
5017 switch (eChartType) // diagram chart type
5018 {
5019 case chart::TYPEID_PIE:
5020 if(getChartType() == chart::TYPEID_DOUGHNUT)
5021 aParam.mbExport = false;
5022 else
5023 // All pie charts support label placement.
5024 aParam.mbExport = true;
5025 break;
5026 case chart::TYPEID_AREA:
5027 case chart::TYPEID_RADARLINE:
5028 case chart::TYPEID_RADARAREA:
5029 // These chart types don't support label placement.
5030 aParam.mbExport = false;
5031 break;
5032 case chart::TYPEID_BAR:
5033 if (mbStacked || mbPercent)
5034 {
5035 aParam.maAllowedValues.clear();
5036 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER);
5037 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE);
5038 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN);
5039 aParam.meDefault = css::chart::DataLabelPlacement::CENTER;
5040 }
5041 else // Clustered bar chart
5042 {
5043 aParam.maAllowedValues.clear();
5044 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::CENTER);
5045 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::INSIDE);
5046 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::OUTSIDE);
5047 aParam.maAllowedValues.insert(css::chart::DataLabelPlacement::NEAR_ORIGIN);
5048 aParam.meDefault = css::chart::DataLabelPlacement::OUTSIDE;
5049 }
5050 break;
5051 // TODO: How do chartex charts handle this?
5052 default:
5053 ;
5054 }
5055
5056 for (const sal_Int32 nIdx : aAttrLabelIndices)
5057 {
5058 uno::Reference<beans::XPropertySet> xLabelPropSet = xSeries->getDataPointByIndex(nIdx);
5059
5060 if (!xLabelPropSet.is())
5061 continue;
5062
5063 if (bIsChartex) {
5064 if (aParam.mbExport)
5065 {
5066 sal_Int32 nLabelPlacement = aParam.meDefault;
5067 if (xPropSet->getPropertyValue(u"LabelPlacement"_ustr) >>= nLabelPlacement)
5068 {
5069 if (!aParam.maAllowedValues.count(nLabelPlacement))
5070 nLabelPlacement = aParam.meDefault;
5071 pFS->startElement(FSNS(XML_cx, XML_dataLabel),
5072 XML_idx, OString::number(nIdx),
5073 XML_pos, toOOXMLPlacement(nLabelPlacement));
5074 }
5075 } else {
5076 pFS->startElement(FSNS(XML_cx, XML_dataLabel), XML_idx, OString::number(nIdx));
5077 }
5078 } else {
5079 pFS->startElement(FSNS(XML_c, XML_dLbl));
5080 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nIdx));
5081
5082 // As far as i know there can be issues with the Positions,
5083 // if a piechart label use AVOID_OVERLAP placement (== BestFit)
5084 // because LO and MS may calculate the bestFit positions differently.
5085 bool bWritePosition = true;
5086 if (eChartType == chart::TYPEID_PIE)
5087 {
5088 sal_Int32 nLabelPlacement = aParam.meDefault;
5089 xLabelPropSet->getPropertyValue(u"LabelPlacement"_ustr) >>= nLabelPlacement;
5090 if (nLabelPlacement == css::chart::DataLabelPlacement::AVOID_OVERLAP)
5091 bWritePosition = false;
5092 }
5093
5094 // export custom position of data label
5095 if (bWritePosition)
5096 {
5097 chart2::RelativePosition aCustomLabelPosition;
5098 if( xLabelPropSet->getPropertyValue(u"CustomLabelPosition"_ustr) >>= aCustomLabelPosition )
5099 {
5100 pFS->startElement(FSNS(XML_c, XML_layout));
5101 pFS->startElement(FSNS(XML_c, XML_manualLayout));
5102
5103 pFS->singleElement(FSNS(XML_c, XML_x), XML_val, OString::number(aCustomLabelPosition.Primary));
5104 pFS->singleElement(FSNS(XML_c, XML_y), XML_val, OString::number(aCustomLabelPosition.Secondary));
5105
5106 SAL_WARN_IF(aCustomLabelPosition.Anchor != css::drawing::Alignment_TOP_LEFT, "oox", "unsupported anchor position");
5107
5108 pFS->endElement(FSNS(XML_c, XML_manualLayout));
5109 pFS->endElement(FSNS(XML_c, XML_layout));
5110 }
5111 }
5112 }
5113
5114 if( GetProperty(xLabelPropSet, u"LinkNumberFormatToSource"_ustr) )
5115 mAny >>= bLinkedNumFmt;
5116
5117 if( xLabelPropSet->getPropertyValue(u"Label"_ustr) >>= aLabel )
5118 bLabelIsNumberFormat = aLabel.ShowNumber;
5119 else
5120 bLabelIsNumberFormat = true;
5121
5122 if (GetProperty(xLabelPropSet, bLabelIsNumberFormat ? u"NumberFormat"_ustr : u"PercentageNumberFormat"_ustr))
5123 {
5124 sal_Int32 nKey = 0;
5125 mAny >>= nKey;
5126
5127 OUString aNumberFormatString = getNumberFormatCode(nKey);
5128
5129 if (bIsChartex) {
5130 pFS->singleElement(FSNS(XML_cx, XML_numFmt), XML_formatCode, aNumberFormatString,
5131 XML_sourceLinked, ToPsz10(bLinkedNumFmt));
5132 } else {
5133 pFS->singleElement(FSNS(XML_c, XML_numFmt), XML_formatCode, aNumberFormatString,
5134 XML_sourceLinked, ToPsz10(bLinkedNumFmt));
5135 }
5136 }
5137
5138 // Individual label property that overwrites the baseline.
5139 writeLabelProperties(pFS, this, xLabelPropSet, aParam, nIdx,
5140 rDLblsRange, bIsChartex);
5141 pFS->endElement(FSNS(XML_c, XML_dLbl));
5142 }
5143
5144 // Baseline label properties for all labels.
5145 writeLabelProperties(pFS, this, xPropSet, aParam, -1, rDLblsRange,
5146 bIsChartex);
5147
5148 if (!bIsChartex) {
5149 bool bShowLeaderLines = false;
5150 xPropSet->getPropertyValue(u"ShowCustomLeaderLines"_ustr) >>= bShowLeaderLines;
5151 pFS->singleElement(FSNS(XML_c, XML_showLeaderLines), XML_val, ToPsz10(bShowLeaderLines));
5152
5153 // Export LeaderLine properties
5154 // TODO: import all kind of LeaderLine props (not just LineColor/LineWidth)
5155 if (bShowLeaderLines)
5156 {
5157 pFS->startElement(FSNS(XML_c, XML_leaderLines));
5158 pFS->startElement(FSNS(XML_c, XML_spPr));
5159 WriteOutline(xPropSet, getModel());
5160 pFS->endElement(FSNS(XML_c, XML_spPr));
5161 pFS->endElement(FSNS(XML_c, XML_leaderLines));
5162 }
5163
5164 // Export leader line
5165 if( eChartType != chart::TYPEID_PIE )
5166 {
5167 pFS->startElement(FSNS(XML_c, XML_extLst));
5168 pFS->startElement(FSNS(XML_c, XML_ext), XML_uri, "{CE6537A1-D6FC-4f65-9D91-7224C49458BB}", FSNS(XML_xmlns, XML_c15), GetFB()->getNamespaceURL(OOX_NS(c15)));
5169 pFS->singleElement(FSNS(XML_c15, XML_showLeaderLines), XML_val, ToPsz10(bShowLeaderLines));
5170 pFS->endElement(FSNS(XML_c, XML_ext));
5171 pFS->endElement(FSNS(XML_c, XML_extLst));
5172 }
5173 }
5174
5175 if (bIsChartex) {
5176 pFS->endElement(FSNS(XML_cx, XML_dataLabels));
5177 } else {
5178 pFS->endElement(FSNS(XML_c, XML_dLbls));
5179 }
5180 }
5181
exportDataPoints(const uno::Reference<beans::XPropertySet> & xSeriesProperties,sal_Int32 nSeriesLength,sal_Int32 eChartType)5182 void ChartExport::exportDataPoints(
5183 const uno::Reference< beans::XPropertySet > & xSeriesProperties,
5184 sal_Int32 nSeriesLength, sal_Int32 eChartType )
5185 {
5186 uno::Reference< chart2::XDataSeries > xSeries( xSeriesProperties, uno::UNO_QUERY );
5187 bool bVaryColorsByPoint = false;
5188 Sequence< sal_Int32 > aDataPointSeq;
5189 if( xSeriesProperties.is())
5190 {
5191 Any aAny = xSeriesProperties->getPropertyValue( u"AttributedDataPoints"_ustr );
5192 aAny >>= aDataPointSeq;
5193 xSeriesProperties->getPropertyValue( u"VaryColorsByPoint"_ustr ) >>= bVaryColorsByPoint;
5194 }
5195
5196 const sal_Int32 * pPoints = aDataPointSeq.getConstArray();
5197 sal_Int32 nElement;
5198 Reference< chart2::XColorScheme > xColorScheme;
5199 if( mxNewDiagram.is())
5200 xColorScheme.set( mxNewDiagram->getDefaultColorScheme());
5201
5202 if( bVaryColorsByPoint && xColorScheme.is() )
5203 {
5204 o3tl::sorted_vector< sal_Int32 > aAttrPointSet;
5205 aAttrPointSet.reserve(aDataPointSeq.getLength());
5206 for (auto p = pPoints; p < pPoints + aDataPointSeq.getLength(); ++p)
5207 aAttrPointSet.insert(*p);
5208 const auto aEndIt = aAttrPointSet.end();
5209 for( nElement = 0; nElement < nSeriesLength; ++nElement )
5210 {
5211 uno::Reference< beans::XPropertySet > xPropSet;
5212 if( aAttrPointSet.find( nElement ) != aEndIt )
5213 {
5214 try
5215 {
5216 xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
5217 xSeries, nElement, getModel() );
5218 }
5219 catch( const uno::Exception & )
5220 {
5221 DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
5222 }
5223 }
5224 else
5225 {
5226 // property set only containing the color
5227 xPropSet.set( new ColorPropertySet( ColorTransparency, xColorScheme->getColorByIndex( nElement )));
5228 }
5229
5230 if( xPropSet.is() )
5231 {
5232 FSHelperPtr pFS = GetFS();
5233 pFS->startElement(FSNS(XML_c, XML_dPt));
5234 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nElement));
5235
5236 switch (eChartType)
5237 {
5238 case chart::TYPEID_PIE:
5239 case chart::TYPEID_DOUGHNUT:
5240 {
5241 if( xPropSet.is() && GetProperty( xPropSet, u"SegmentOffset"_ustr) )
5242 {
5243 sal_Int32 nOffset = 0;
5244 mAny >>= nOffset;
5245 if (nOffset)
5246 pFS->singleElement( FSNS( XML_c, XML_explosion ),
5247 XML_val, OString::number( nOffset ) );
5248 }
5249 break;
5250 }
5251 default:
5252 break;
5253 }
5254 exportShapeProps( xPropSet, false );
5255
5256 pFS->endElement( FSNS( XML_c, XML_dPt ) );
5257 }
5258 }
5259 }
5260
5261 // Export Data Point Property in Charts even if the VaryColors is false
5262 if( bVaryColorsByPoint )
5263 return;
5264
5265 o3tl::sorted_vector< sal_Int32 > aAttrPointSet;
5266 aAttrPointSet.reserve(aDataPointSeq.getLength());
5267 for (auto p = pPoints; p < pPoints + aDataPointSeq.getLength(); ++p)
5268 aAttrPointSet.insert(*p);
5269 const auto aEndIt = aAttrPointSet.end();
5270 for( nElement = 0; nElement < nSeriesLength; ++nElement )
5271 {
5272 uno::Reference< beans::XPropertySet > xPropSet;
5273 if( aAttrPointSet.find( nElement ) != aEndIt )
5274 {
5275 try
5276 {
5277 xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
5278 xSeries, nElement, getModel() );
5279 }
5280 catch( const uno::Exception & )
5281 {
5282 DBG_UNHANDLED_EXCEPTION( "oox", "Exception caught during Export of data point" );
5283 }
5284 }
5285
5286 if( xPropSet.is() )
5287 {
5288 FSHelperPtr pFS = GetFS();
5289 pFS->startElement(FSNS(XML_c, XML_dPt));
5290 pFS->singleElement(FSNS(XML_c, XML_idx), XML_val, OString::number(nElement));
5291
5292 switch( eChartType )
5293 {
5294 case chart::TYPEID_BUBBLE:
5295 case chart::TYPEID_HORBAR:
5296 case chart::TYPEID_BAR:
5297 pFS->singleElement(FSNS(XML_c, XML_invertIfNegative), XML_val, "0");
5298 exportShapeProps(xPropSet, false);
5299 break;
5300
5301 case chart::TYPEID_LINE:
5302 case chart::TYPEID_SCATTER:
5303 case chart::TYPEID_RADARLINE:
5304 exportMarker(xPropSet);
5305 break;
5306
5307 default:
5308 exportShapeProps(xPropSet, false);
5309 break;
5310 }
5311
5312 pFS->endElement( FSNS( XML_c, XML_dPt ) );
5313 }
5314 }
5315 }
5316
5317 // Generalized axis output
createAxes(bool bPrimaryAxes,bool bCheckCombinedAxes)5318 void ChartExport::createAxes(bool bPrimaryAxes, bool bCheckCombinedAxes)
5319 {
5320 sal_Int32 nAxisIdx, nAxisIdy;
5321 bool bPrimaryAxisExists = false;
5322 bool bSecondaryAxisExists = false;
5323 // let's check which axis already exists and which axis is attached to the actual dataseries
5324 if (maAxes.size() >= 2)
5325 {
5326 bPrimaryAxisExists = bPrimaryAxes && maAxes[1].nAxisType == AXIS_PRIMARY_Y;
5327 bSecondaryAxisExists = !bPrimaryAxes && maAxes[1].nAxisType == AXIS_SECONDARY_Y;
5328 }
5329 // tdf#114181 keep axes of combined charts
5330 if ( bCheckCombinedAxes && ( bPrimaryAxisExists || bSecondaryAxisExists ) )
5331 {
5332 nAxisIdx = maAxes[0].nAxisId;
5333 nAxisIdy = maAxes[1].nAxisId;
5334 }
5335 else
5336 {
5337 nAxisIdx = lcl_generateRandomValue();
5338 nAxisIdy = lcl_generateRandomValue();
5339 AxesType eXAxis = bPrimaryAxes ? AXIS_PRIMARY_X : AXIS_SECONDARY_X;
5340 AxesType eYAxis = bPrimaryAxes ? AXIS_PRIMARY_Y : AXIS_SECONDARY_Y;
5341 maAxes.emplace_back( eXAxis, nAxisIdx, nAxisIdy );
5342 maAxes.emplace_back( eYAxis, nAxisIdy, nAxisIdx );
5343 }
5344 // Export IDs
5345 FSHelperPtr pFS = GetFS();
5346 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdx));
5347 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdy));
5348 if (mbHasZAxis)
5349 {
5350 sal_Int32 nAxisIdz = 0;
5351 if( isDeep3dChart() )
5352 {
5353 nAxisIdz = lcl_generateRandomValue();
5354 maAxes.emplace_back( AXIS_PRIMARY_Z, nAxisIdz, nAxisIdy );
5355 }
5356 pFS->singleElement(FSNS(XML_c, XML_axId), XML_val, OString::number(nAxisIdz));
5357 }
5358 }
5359
exportGrouping(bool isBar)5360 void ChartExport::exportGrouping( bool isBar )
5361 {
5362 FSHelperPtr pFS = GetFS();
5363 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
5364 // grouping
5365 if( GetProperty( xPropSet, u"Stacked"_ustr ) )
5366 mAny >>= mbStacked;
5367 if( GetProperty( xPropSet, u"Percent"_ustr ) )
5368 mAny >>= mbPercent;
5369
5370 const char* grouping = nullptr;
5371 if (mbStacked)
5372 grouping = "stacked";
5373 else if (mbPercent)
5374 grouping = "percentStacked";
5375 else
5376 {
5377 if( isBar && !isDeep3dChart() )
5378 {
5379 grouping = "clustered";
5380 }
5381 else
5382 grouping = "standard";
5383 }
5384 pFS->singleElement(FSNS(XML_c, XML_grouping), XML_val, grouping);
5385 }
5386
exportTrendlines(const Reference<chart2::XDataSeries> & xSeries)5387 void ChartExport::exportTrendlines( const Reference< chart2::XDataSeries >& xSeries )
5388 {
5389 FSHelperPtr pFS = GetFS();
5390 Reference< chart2::XRegressionCurveContainer > xRegressionCurveContainer( xSeries, UNO_QUERY );
5391 if( !xRegressionCurveContainer.is() )
5392 return;
5393
5394 const Sequence< Reference< chart2::XRegressionCurve > > aRegCurveSeq = xRegressionCurveContainer->getRegressionCurves();
5395 for( const Reference< chart2::XRegressionCurve >& xRegCurve : aRegCurveSeq )
5396 {
5397 if (!xRegCurve.is())
5398 continue;
5399
5400 Reference< XPropertySet > xProperties( xRegCurve , uno::UNO_QUERY );
5401
5402 OUString aService;
5403 Reference< lang::XServiceName > xServiceName( xProperties, UNO_QUERY );
5404 if( !xServiceName.is() )
5405 continue;
5406
5407 aService = xServiceName->getServiceName();
5408
5409 if(aService != "com.sun.star.chart2.LinearRegressionCurve" &&
5410 aService != "com.sun.star.chart2.ExponentialRegressionCurve" &&
5411 aService != "com.sun.star.chart2.LogarithmicRegressionCurve" &&
5412 aService != "com.sun.star.chart2.PotentialRegressionCurve" &&
5413 aService != "com.sun.star.chart2.PolynomialRegressionCurve" &&
5414 aService != "com.sun.star.chart2.MovingAverageRegressionCurve")
5415 continue;
5416
5417 pFS->startElement(FSNS(XML_c, XML_trendline));
5418
5419 OUString aName;
5420 xProperties->getPropertyValue(u"CurveName"_ustr) >>= aName;
5421 if(!aName.isEmpty())
5422 {
5423 pFS->startElement(FSNS(XML_c, XML_name));
5424 pFS->writeEscaped(aName);
5425 pFS->endElement( FSNS( XML_c, XML_name) );
5426 }
5427
5428 exportShapeProps( xProperties, false );
5429
5430 if( aService == "com.sun.star.chart2.LinearRegressionCurve" )
5431 {
5432 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "linear");
5433 }
5434 else if( aService == "com.sun.star.chart2.ExponentialRegressionCurve" )
5435 {
5436 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "exp");
5437 }
5438 else if( aService == "com.sun.star.chart2.LogarithmicRegressionCurve" )
5439 {
5440 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "log");
5441 }
5442 else if( aService == "com.sun.star.chart2.PotentialRegressionCurve" )
5443 {
5444 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "power");
5445 }
5446 else if( aService == "com.sun.star.chart2.PolynomialRegressionCurve" )
5447 {
5448 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "poly");
5449
5450 sal_Int32 aDegree = 2;
5451 xProperties->getPropertyValue( u"PolynomialDegree"_ustr) >>= aDegree;
5452 pFS->singleElement(FSNS(XML_c, XML_order), XML_val, OString::number(aDegree));
5453 }
5454 else if( aService == "com.sun.star.chart2.MovingAverageRegressionCurve" )
5455 {
5456 pFS->singleElement(FSNS(XML_c, XML_trendlineType), XML_val, "movingAvg");
5457
5458 sal_Int32 aPeriod = 2;
5459 xProperties->getPropertyValue( u"MovingAveragePeriod"_ustr) >>= aPeriod;
5460
5461 pFS->singleElement(FSNS(XML_c, XML_period), XML_val, OString::number(aPeriod));
5462 }
5463 else
5464 {
5465 // should never happen
5466 // This would produce invalid OOXML files so we check earlier for the type
5467 assert(false);
5468 }
5469
5470 double fExtrapolateForward = 0.0;
5471 double fExtrapolateBackward = 0.0;
5472
5473 xProperties->getPropertyValue(u"ExtrapolateForward"_ustr) >>= fExtrapolateForward;
5474 xProperties->getPropertyValue(u"ExtrapolateBackward"_ustr) >>= fExtrapolateBackward;
5475
5476 pFS->singleElement( FSNS( XML_c, XML_forward ),
5477 XML_val, OString::number(fExtrapolateForward) );
5478
5479 pFS->singleElement( FSNS( XML_c, XML_backward ),
5480 XML_val, OString::number(fExtrapolateBackward) );
5481
5482 bool bForceIntercept = false;
5483 xProperties->getPropertyValue(u"ForceIntercept"_ustr) >>= bForceIntercept;
5484
5485 if (bForceIntercept)
5486 {
5487 double fInterceptValue = 0.0;
5488 xProperties->getPropertyValue(u"InterceptValue"_ustr) >>= fInterceptValue;
5489
5490 pFS->singleElement( FSNS( XML_c, XML_intercept ),
5491 XML_val, OString::number(fInterceptValue) );
5492 }
5493
5494 // Equation properties
5495 Reference< XPropertySet > xEquationProperties( xRegCurve->getEquationProperties() );
5496
5497 // Show Equation
5498 bool bShowEquation = false;
5499 xEquationProperties->getPropertyValue(u"ShowEquation"_ustr) >>= bShowEquation;
5500
5501 // Show R^2
5502 bool bShowCorrelationCoefficient = false;
5503 xEquationProperties->getPropertyValue(u"ShowCorrelationCoefficient"_ustr) >>= bShowCorrelationCoefficient;
5504
5505 pFS->singleElement( FSNS( XML_c, XML_dispRSqr ),
5506 XML_val, ToPsz10(bShowCorrelationCoefficient) );
5507
5508 pFS->singleElement(FSNS(XML_c, XML_dispEq), XML_val, ToPsz10(bShowEquation));
5509
5510 pFS->endElement( FSNS( XML_c, XML_trendline ) );
5511 }
5512 }
5513
exportMarker(const Reference<XPropertySet> & xPropSet)5514 void ChartExport::exportMarker(const Reference< XPropertySet >& xPropSet)
5515 {
5516 chart2::Symbol aSymbol;
5517 if( GetProperty( xPropSet, u"Symbol"_ustr ) )
5518 mAny >>= aSymbol;
5519
5520 if(aSymbol.Style != chart2::SymbolStyle_STANDARD && aSymbol.Style != chart2::SymbolStyle_NONE)
5521 return;
5522
5523 FSHelperPtr pFS = GetFS();
5524 pFS->startElement(FSNS(XML_c, XML_marker));
5525
5526 sal_Int32 nSymbol = aSymbol.StandardSymbol;
5527 // TODO: more properties support for marker
5528 const char* pSymbolType; // no initialization here, to let compiler warn if we have a code path
5529 // where it stays uninitialized
5530 switch( nSymbol )
5531 {
5532 case 0:
5533 pSymbolType = "square";
5534 break;
5535 case 1:
5536 pSymbolType = "diamond";
5537 break;
5538 case 2:
5539 case 3:
5540 case 4:
5541 case 5:
5542 pSymbolType = "triangle";
5543 break;
5544 case 8:
5545 pSymbolType = "circle";
5546 break;
5547 case 9:
5548 pSymbolType = "star";
5549 break;
5550 case 10:
5551 pSymbolType = "x"; // in MS office 2010 built in symbol marker 'X' is represented as 'x'
5552 break;
5553 case 11:
5554 pSymbolType = "plus";
5555 break;
5556 case 13:
5557 pSymbolType = "dash";
5558 break;
5559 default:
5560 pSymbolType = "square";
5561 break;
5562 }
5563
5564 bool bSkipFormatting = false;
5565 if (aSymbol.Style == chart2::SymbolStyle_NONE)
5566 {
5567 bSkipFormatting = true;
5568 pSymbolType = "none";
5569 }
5570
5571 pFS->singleElement(FSNS(XML_c, XML_symbol), XML_val, pSymbolType);
5572
5573 if (!bSkipFormatting)
5574 {
5575 awt::Size aSymbolSize = aSymbol.Size;
5576 sal_Int32 nSize = std::max( aSymbolSize.Width, aSymbolSize.Height );
5577
5578 nSize = nSize/250.0*7.0 + 1; // just guessed based on some test cases,
5579 //the value is always 1 less than the actual value.
5580 nSize = std::clamp( int(nSize), 2, 72 );
5581 pFS->singleElement(FSNS(XML_c, XML_size), XML_val, OString::number(nSize));
5582
5583 pFS->startElement(FSNS(XML_c, XML_spPr));
5584
5585 util::Color aColor = aSymbol.FillColor;
5586 if (GetProperty(xPropSet, u"Color"_ustr))
5587 mAny >>= aColor;
5588
5589 if (aColor == -1)
5590 {
5591 pFS->singleElement(FSNS(XML_a, XML_noFill));
5592 }
5593 else
5594 WriteSolidFill(::Color(ColorTransparency, aColor));
5595
5596 pFS->endElement( FSNS( XML_c, XML_spPr ) );
5597 }
5598
5599 pFS->endElement( FSNS( XML_c, XML_marker ) );
5600 }
5601
exportSmooth()5602 void ChartExport::exportSmooth()
5603 {
5604 FSHelperPtr pFS = GetFS();
5605 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY );
5606 sal_Int32 nSplineType = 0;
5607 if( GetProperty( xPropSet, u"SplineType"_ustr ) )
5608 mAny >>= nSplineType;
5609 const char* pVal = nSplineType != 0 ? "1" : "0";
5610 pFS->singleElement(FSNS(XML_c, XML_smooth), XML_val, pVal);
5611 }
5612
exportFirstSliceAng()5613 void ChartExport::exportFirstSliceAng( )
5614 {
5615 FSHelperPtr pFS = GetFS();
5616 sal_Int32 nStartingAngle = 0;
5617 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
5618 if( GetProperty( xPropSet, u"StartingAngle"_ustr ) )
5619 mAny >>= nStartingAngle;
5620
5621 // convert to ooxml angle
5622 nStartingAngle = (450 - nStartingAngle ) % 360;
5623 pFS->singleElement(FSNS(XML_c, XML_firstSliceAng), XML_val, OString::number(nStartingAngle));
5624 }
5625
5626 namespace {
5627
getErrorBarStyle(sal_Int32 nErrorBarStyle)5628 const char* getErrorBarStyle(sal_Int32 nErrorBarStyle)
5629 {
5630 switch(nErrorBarStyle)
5631 {
5632 case cssc::ErrorBarStyle::NONE:
5633 return nullptr;
5634 case cssc::ErrorBarStyle::VARIANCE:
5635 break;
5636 case cssc::ErrorBarStyle::STANDARD_DEVIATION:
5637 return "stdDev";
5638 case cssc::ErrorBarStyle::ABSOLUTE:
5639 return "fixedVal";
5640 case cssc::ErrorBarStyle::RELATIVE:
5641 return "percentage";
5642 case cssc::ErrorBarStyle::ERROR_MARGIN:
5643 break;
5644 case cssc::ErrorBarStyle::STANDARD_ERROR:
5645 return "stdErr";
5646 case cssc::ErrorBarStyle::FROM_DATA:
5647 return "cust";
5648 default:
5649 assert(false && "can't happen");
5650 }
5651 return nullptr;
5652 }
5653
getLabeledSequence(const uno::Sequence<uno::Reference<chart2::data::XLabeledDataSequence>> & aSequences,bool bPositive)5654 Reference< chart2::data::XDataSequence> getLabeledSequence(
5655 const uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > >& aSequences,
5656 bool bPositive )
5657 {
5658 OUString aDirection;
5659 if(bPositive)
5660 aDirection = "positive";
5661 else
5662 aDirection = "negative";
5663
5664 for( const auto& rSequence : aSequences )
5665 {
5666 if( rSequence.is())
5667 {
5668 uno::Reference< chart2::data::XDataSequence > xSequence( rSequence->getValues());
5669 uno::Reference< beans::XPropertySet > xSeqProp( xSequence, uno::UNO_QUERY_THROW );
5670 OUString aRole;
5671 if( ( xSeqProp->getPropertyValue( u"Role"_ustr ) >>= aRole ) &&
5672 aRole.match( "error-bars" ) && aRole.indexOf(aDirection) >= 0 )
5673 {
5674 return xSequence;
5675 }
5676 }
5677 }
5678
5679 return Reference< chart2::data::XDataSequence > ();
5680 }
5681
5682 }
5683
exportErrorBar(const Reference<XPropertySet> & xErrorBarProps,bool bYError)5684 void ChartExport::exportErrorBar(const Reference< XPropertySet>& xErrorBarProps, bool bYError)
5685 {
5686 sal_Int32 nErrorBarStyle = cssc::ErrorBarStyle::NONE;
5687 xErrorBarProps->getPropertyValue(u"ErrorBarStyle"_ustr) >>= nErrorBarStyle;
5688 const char* pErrorBarStyle = getErrorBarStyle(nErrorBarStyle);
5689 if(!pErrorBarStyle)
5690 return;
5691
5692 FSHelperPtr pFS = GetFS();
5693 pFS->startElement(FSNS(XML_c, XML_errBars));
5694 pFS->singleElement(FSNS(XML_c, XML_errDir), XML_val, bYError ? "y" : "x");
5695 bool bPositive = false, bNegative = false;
5696 xErrorBarProps->getPropertyValue(u"ShowPositiveError"_ustr) >>= bPositive;
5697 xErrorBarProps->getPropertyValue(u"ShowNegativeError"_ustr) >>= bNegative;
5698 const char* pErrBarType;
5699 if(bPositive && bNegative)
5700 pErrBarType = "both";
5701 else if(bPositive)
5702 pErrBarType = "plus";
5703 else if(bNegative)
5704 pErrBarType = "minus";
5705 else
5706 {
5707 // what the hell should we do now?
5708 // at least this makes the file valid
5709 pErrBarType = "both";
5710 }
5711 pFS->singleElement(FSNS(XML_c, XML_errBarType), XML_val, pErrBarType);
5712 pFS->singleElement(FSNS(XML_c, XML_errValType), XML_val, pErrorBarStyle);
5713 pFS->singleElement(FSNS(XML_c, XML_noEndCap), XML_val, "0");
5714 if(nErrorBarStyle == cssc::ErrorBarStyle::FROM_DATA)
5715 {
5716 uno::Reference< chart2::data::XDataSource > xDataSource(xErrorBarProps, uno::UNO_QUERY);
5717 Sequence< Reference < chart2::data::XLabeledDataSequence > > aSequences =
5718 xDataSource->getDataSequences();
5719
5720 if(bPositive)
5721 {
5722 exportSeriesValues(getLabeledSequence(aSequences, true), XML_plus);
5723 }
5724
5725 if(bNegative)
5726 {
5727 exportSeriesValues(getLabeledSequence(aSequences, false), XML_minus);
5728 }
5729 }
5730 else
5731 {
5732 double nVal = 0.0;
5733 if(nErrorBarStyle == cssc::ErrorBarStyle::STANDARD_DEVIATION)
5734 {
5735 xErrorBarProps->getPropertyValue(u"Weight"_ustr) >>= nVal;
5736 }
5737 else
5738 {
5739 if(bPositive)
5740 xErrorBarProps->getPropertyValue(u"PositiveError"_ustr) >>= nVal;
5741 else
5742 xErrorBarProps->getPropertyValue(u"NegativeError"_ustr) >>= nVal;
5743 }
5744
5745 pFS->singleElement(FSNS(XML_c, XML_val), XML_val, OString::number(nVal));
5746 }
5747
5748 exportShapeProps( xErrorBarProps, false );
5749
5750 pFS->endElement( FSNS( XML_c, XML_errBars) );
5751 }
5752
exportView3D()5753 void ChartExport::exportView3D()
5754 {
5755 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
5756 if( !xPropSet.is() )
5757 return;
5758 FSHelperPtr pFS = GetFS();
5759 pFS->startElement(FSNS(XML_c, XML_view3D));
5760 sal_Int32 eChartType = getChartType( );
5761 // rotX
5762 if( GetProperty( xPropSet, u"RotationHorizontal"_ustr ) )
5763 {
5764 sal_Int32 nRotationX = 0;
5765 mAny >>= nRotationX;
5766 if( nRotationX < 0 )
5767 {
5768 if(eChartType == chart::TYPEID_PIE)
5769 {
5770 /* In OOXML we get value in 0..90 range for pie chart X rotation , whereas we expect it to be in -90..90 range,
5771 so we convert that during import. It is modified in View3DConverter::convertFromModel()
5772 here we convert it back to 0..90 as we received in import */
5773 nRotationX += 90; // X rotation (map Chart2 [-179,180] to OOXML [0..90])
5774 }
5775 else
5776 nRotationX += 360; // X rotation (map Chart2 [-179,180] to OOXML [-90..90])
5777 }
5778 pFS->singleElement(FSNS(XML_c, XML_rotX), XML_val, OString::number(nRotationX));
5779 }
5780 // rotY
5781 if( GetProperty( xPropSet, u"RotationVertical"_ustr ) )
5782 {
5783 // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
5784 if( eChartType == chart::TYPEID_PIE && GetProperty( xPropSet, u"StartingAngle"_ustr ) )
5785 {
5786 // Y rotation used as 'first pie slice angle' in 3D pie charts
5787 sal_Int32 nStartingAngle=0;
5788 mAny >>= nStartingAngle;
5789 // convert to ooxml angle
5790 nStartingAngle = (450 - nStartingAngle ) % 360;
5791 pFS->singleElement(FSNS(XML_c, XML_rotY), XML_val, OString::number(nStartingAngle));
5792 }
5793 else
5794 {
5795 sal_Int32 nRotationY = 0;
5796 mAny >>= nRotationY;
5797 // Y rotation (map Chart2 [-179,180] to OOXML [0..359])
5798 if( nRotationY < 0 )
5799 nRotationY += 360;
5800 pFS->singleElement(FSNS(XML_c, XML_rotY), XML_val, OString::number(nRotationY));
5801 }
5802 }
5803 // rAngAx
5804 if( GetProperty( xPropSet, u"RightAngledAxes"_ustr ) )
5805 {
5806 bool bRightAngled = false;
5807 mAny >>= bRightAngled;
5808 const char* sRightAngled = bRightAngled ? "1":"0";
5809 pFS->singleElement(FSNS(XML_c, XML_rAngAx), XML_val, sRightAngled);
5810 }
5811 // perspective
5812 if( GetProperty( xPropSet, u"Perspective"_ustr ) )
5813 {
5814 sal_Int32 nPerspective = 0;
5815 mAny >>= nPerspective;
5816 // map Chart2 [0,100] to OOXML [0..200]
5817 nPerspective *= 2;
5818 pFS->singleElement(FSNS(XML_c, XML_perspective), XML_val, OString::number(nPerspective));
5819 }
5820 pFS->endElement( FSNS( XML_c, XML_view3D ) );
5821 }
5822
isDeep3dChart()5823 bool ChartExport::isDeep3dChart()
5824 {
5825 bool isDeep = false;
5826 if( mbIs3DChart )
5827 {
5828 Reference< XPropertySet > xPropSet( mxDiagram , uno::UNO_QUERY);
5829 if( GetProperty( xPropSet, u"Deep"_ustr ) )
5830 mAny >>= isDeep;
5831 }
5832 return isDeep;
5833 }
5834
isChartexNotChartNS() const5835 bool ChartExport::isChartexNotChartNS() const
5836 {
5837 Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( mxNewDiagram, uno::UNO_QUERY );
5838 if( ! xBCooSysCnt.is()) return false;
5839
5840 // chart type
5841 const Sequence< Reference< chart2::XCoordinateSystem > >
5842 aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
5843
5844 for( const auto& rCS : aCooSysSeq ) {
5845 Reference< chart2::XChartTypeContainer > xCTCnt( rCS, uno::UNO_QUERY );
5846 if( ! xCTCnt.is())
5847 continue;
5848 const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
5849 for( const auto& rCT : aCTSeq ) {
5850 Reference< chart2::XDataSeriesContainer > xDSCnt( rCT, uno::UNO_QUERY );
5851 if( ! xDSCnt.is())
5852 return false;
5853 Reference< chart2::XChartType > xChartType( rCT, uno::UNO_QUERY );
5854 if( ! xChartType.is())
5855 continue;
5856 // note: if xDSCnt.is() then also aCTSeq[nCTIdx]
5857 OUString aChartType( xChartType->getChartType());
5858 sal_Int32 eChartType = lcl_getChartType( aChartType );
5859 switch( eChartType )
5860 {
5861 case chart::TYPEID_BAR:
5862 case chart::TYPEID_AREA:
5863 case chart::TYPEID_LINE:
5864 case chart::TYPEID_BUBBLE:
5865 case chart::TYPEID_OFPIE:
5866 case chart::TYPEID_DOUGHNUT:
5867 case chart::TYPEID_PIE:
5868 case chart::TYPEID_RADARLINE:
5869 case chart::TYPEID_RADARAREA:
5870 case chart::TYPEID_SCATTER:
5871 case chart::TYPEID_STOCK:
5872 case chart::TYPEID_SURFACE:
5873 break;
5874 case chart::TYPEID_FUNNEL:
5875 return true;
5876 default:
5877 assert(false);
5878 break;
5879 }
5880 }
5881 }
5882 return false;
5883 }
5884
getNumberFormatCode(sal_Int32 nKey) const5885 OUString ChartExport::getNumberFormatCode(sal_Int32 nKey) const
5886 {
5887 /* XXX if this was called more than one or two times per export the two
5888 * SvNumberFormatter instances and NfKeywordTable should be member
5889 * variables and initialized only once. */
5890
5891 OUString aCode(u"General"_ustr); // init with fallback
5892 uno::Reference<util::XNumberFormatsSupplier> xNumberFormatsSupplier(mxChartModel, uno::UNO_QUERY_THROW);
5893 SvNumberFormatsSupplierObj* pSupplierObj = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( xNumberFormatsSupplier);
5894 if (!pSupplierObj)
5895 return aCode;
5896
5897 SvNumberFormatter* pNumberFormatter = pSupplierObj->GetNumberFormatter();
5898 if (!pNumberFormatter)
5899 return aCode;
5900
5901 SvNumberFormatter aTempFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US);
5902 NfKeywordTable aKeywords;
5903 aTempFormatter.FillKeywordTableForExcel( aKeywords);
5904 aCode = pNumberFormatter->GetFormatStringForExcel( nKey, aKeywords, aTempFormatter);
5905
5906 return aCode;
5907 }
5908
5909 }// oox
5910
5911 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
5912