Avoid potential negative array index access to cached text.
[LibreOffice.git] / xmloff / source / chart / SchXMLExport.cxx
blob4566941f4fca0a858afc439516ba97e3065f5edc
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <memory>
21 #include <sal/config.h>
22 #include <sal/log.hxx>
24 #include <sax/tools/converter.hxx>
26 #include <utility>
27 #include <xmloff/xmlprmap.hxx>
29 #include <SchXMLExport.hxx>
30 #include <XMLChartPropertySetMapper.hxx>
31 #include "ColorPropertySet.hxx"
32 #include "SchXMLTools.hxx"
33 #include "SchXMLEnumConverter.hxx"
35 #include <comphelper/processfactory.hxx>
36 #include <tools/globname.hxx>
37 #include <comphelper/classids.hxx>
38 #include <comphelper/sequence.hxx>
40 #include <xmloff/namespacemap.hxx>
41 #include <xmloff/xmlnamespace.hxx>
42 #include <xmloff/xmltoken.hxx>
43 #include <xmloff/families.hxx>
44 #include <xmloff/xmlaustp.hxx>
45 #include <xmloff/xmluconv.hxx>
46 #include <xmloff/SchXMLSeriesHelper.hxx>
47 #include <rtl/math.hxx>
48 #include <o3tl/sorted_vector.hxx>
49 #include <o3tl/string_view.hxx>
51 #include <limits>
52 #include <vector>
53 #include <algorithm>
54 #include <queue>
55 #include <iterator>
56 #include <numeric>
58 #include <com/sun/star/lang/XServiceInfo.hpp>
59 #include <com/sun/star/lang/XServiceName.hpp>
60 #include <com/sun/star/beans/XPropertySet.hpp>
61 #include <com/sun/star/uno/XComponentContext.hpp>
62 #include <com/sun/star/util/XRefreshable.hpp>
64 #include <com/sun/star/chart/XAxis.hpp>
65 #include <com/sun/star/chart/XAxisSupplier.hpp>
66 #include <com/sun/star/chart/XChartDocument.hpp>
67 #include <com/sun/star/chart/ChartLegendExpansion.hpp>
68 #include <com/sun/star/chart/ChartDataRowSource.hpp>
69 #include <com/sun/star/chart/ChartAxisAssign.hpp>
70 #include <com/sun/star/chart/DataLabelPlacement.hpp>
71 #include <com/sun/star/chart/TimeIncrement.hpp>
72 #include <com/sun/star/chart/TimeInterval.hpp>
73 #include <com/sun/star/chart/TimeUnit.hpp>
74 #include <com/sun/star/chart/X3DDisplay.hpp>
75 #include <com/sun/star/chart/XStatisticDisplay.hpp>
76 #include <com/sun/star/chart/XDiagramPositioning.hpp>
78 #include <com/sun/star/chart2/XAnyDescriptionAccess.hpp>
79 #include <com/sun/star/chart2/AxisType.hpp>
80 #include <com/sun/star/chart2/XChartDocument.hpp>
81 #include <com/sun/star/chart2/XDiagram.hpp>
82 #include <com/sun/star/chart2/RelativePosition.hpp>
83 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
84 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
85 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
86 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
87 #include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
88 #include <com/sun/star/chart2/data/XDataSource.hpp>
89 #include <com/sun/star/chart2/data/XDataSink.hpp>
90 #include <com/sun/star/chart2/data/XDataProvider.hpp>
91 #include <com/sun/star/chart2/data/XDatabaseDataProvider.hpp>
92 #include <com/sun/star/chart2/data/XPivotTableDataProvider.hpp>
93 #include <com/sun/star/chart2/data/XRangeXMLConversion.hpp>
94 #include <com/sun/star/chart2/data/XTextualDataSequence.hpp>
95 #include <com/sun/star/chart2/data/XNumericalDataSequence.hpp>
97 #include <com/sun/star/util/MeasureUnit.hpp>
98 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
99 #include <com/sun/star/drawing/XShapes.hpp>
100 #include <com/sun/star/embed/Aspects.hpp>
101 #include <com/sun/star/embed/XVisualObject.hpp>
102 #include <com/sun/star/container/XChild.hpp>
104 #include <comphelper/diagnose_ex.hxx>
105 #include "MultiPropertySetHandler.hxx"
106 #include "PropertyMap.hxx"
108 using namespace com::sun::star;
109 using namespace ::xmloff::token;
111 using ::com::sun::star::uno::Sequence;
112 using ::com::sun::star::uno::Reference;
113 using ::com::sun::star::uno::Any;
114 using ::std::vector;
117 namespace
120 * Used to store a data point custom-label's fields and also the helper members that
121 * indicates whether this label's contents are sourced from a cell[range] and
122 * the address of the cell[range] with guid of the CELLRANGE field.
124 struct CustomLabelData
126 CustomLabelData():
127 mbDataLabelsRange( false )
131 /// Label fields.
132 Sequence<Reference<chart2::XDataPointCustomLabelField>> maFields;
133 /// Are label's contents sourced from a cell[range] ?
134 bool mbDataLabelsRange;
135 /// cell[range] from which label's contents are sourced.
136 OUString maRange;
137 /// GUID of the CELLRANGE field.
138 OUString maGuid;
141 struct SchXMLDataPointStruct
143 OUString maStyleName;
144 sal_Int32 mnRepeat;
145 chart2::RelativePosition mCustomLabelPos; // loext:custom-label-pos-x and -y
147 // There is no internal equivalent for <chart:data-label>. It will be generated on the fly
148 // on export. All about data label is hold in the data point.
149 CustomLabelData mCustomLabel; // <text:p> child element in <chart:data-label>
150 OUString msDataLabelStyleName; // chart:style-name attribute in <chart:data-label>
152 SchXMLDataPointStruct() : mnRepeat( 1 ) {}
157 class SchXMLExportHelper_Impl
159 public:
160 // first: data sequence for label, second: data sequence for values.
161 typedef ::std::pair< css::uno::Reference< css::chart2::data::XDataSequence >,
162 css::uno::Reference< css::chart2::data::XDataSequence > > tLabelValuesDataPair;
163 typedef ::std::vector< tLabelValuesDataPair > tDataSequenceCont;
165 public:
166 SchXMLExportHelper_Impl( SvXMLExport& rExport,
167 SvXMLAutoStylePoolP& rASPool );
169 SchXMLExportHelper_Impl(const SchXMLExportHelper_Impl&) = delete;
170 SchXMLExportHelper_Impl& operator=(const SchXMLExportHelper_Impl&) = delete;
172 // auto-styles
173 /// parse chart and collect all auto-styles used in current pool
174 void collectAutoStyles( css::uno::Reference< css::chart::XChartDocument > const & rChartDoc );
176 /// write the styles collected into the current pool as <style:style> elements
177 void exportAutoStyles();
179 /** export the <chart:chart> element corresponding to rChartDoc
180 if bIncludeTable is true, the chart data is exported as <table:table>
181 element (inside the chart element).
183 Otherwise the external references stored in the chart document are used
184 for writing the corresponding attributes at series
186 All attributes contained in xAttrList are written at the chart element,
187 which is the outer element of a chart. So these attributes can easily
188 be parsed again by the container
190 void exportChart( css::uno::Reference< css::chart::XChartDocument > const & rChartDoc,
191 bool bIncludeTable );
193 const rtl::Reference<XMLPropertySetMapper>& GetPropertySetMapper() const;
195 void SetChartRangeAddress( const OUString& rAddress )
196 { msChartAddress = rAddress; }
198 void InitRangeSegmentationProperties(
199 const css::uno::Reference< css::chart2::XChartDocument > & xChartDoc );
201 static css::awt::Size getPageSize(
202 const css::uno::Reference< css::chart2::XChartDocument > & xChartDoc );
204 /** first parseDocument: collect autostyles and store names in this queue
205 second parseDocument: export content and use names from this queue
207 ::std::queue< OUString > maAutoStyleNameQueue;
208 void CollectAutoStyle(
209 std::vector< XMLPropertyState >&& aStates );
210 void AddAutoStyleAttribute(
211 const std::vector< XMLPropertyState >& aStates );
213 /// if bExportContent is false the auto-styles are collected
214 void parseDocument( css::uno::Reference< css::chart::XChartDocument > const & rChartDoc,
215 bool bExportContent,
216 bool bIncludeTable = false );
217 void exportTable();
218 void exportPlotArea(
219 const css::uno::Reference< css::chart::XDiagram >& xDiagram,
220 const css::uno::Reference< css::chart2::XDiagram >& xNewDiagram,
221 const css::awt::Size & rPageSize,
222 bool bExportContent,
223 bool bIncludeTable );
224 void exportCoordinateRegion( const css::uno::Reference< css::chart::XDiagram >& xDiagram );
225 void exportAxes( const css::uno::Reference< css::chart::XDiagram > & xDiagram,
226 const css::uno::Reference< css::chart2::XDiagram > & xNewDiagram,
227 bool bExportContent );
228 void exportAxis( enum XMLTokenEnum eDimension, enum XMLTokenEnum eAxisName,
229 const Reference< beans::XPropertySet >& rAxisProps, const Reference< chart2::XAxis >& rChart2Axis,
230 const OUString& rCategoriesRanges,
231 bool bHasTitle, bool bHasMajorGrid, bool bHasMinorGrid, bool bExportContent, std::u16string_view sChartType );
232 void exportGrid( const Reference< beans::XPropertySet >& rGridProperties, bool bMajor, bool bExportContent );
233 void exportDateScale( const Reference< beans::XPropertySet >& rAxisProps );
234 void exportAxisTitle( const Reference< beans::XPropertySet >& rTitleProps, bool bExportContent );
236 void exportSeries(
237 const css::uno::Reference< css::chart2::XDiagram > & xNewDiagram,
238 const css::awt::Size & rPageSize,
239 bool bExportContent,
240 bool bHasTwoYAxes );
242 void exportPropertyMapping(
243 const css::uno::Reference< css::chart2::data::XDataSource > & xSource,
244 const Sequence< OUString >& rSupportedMappings );
246 void exportCandleStickSeries(
247 const css::uno::Sequence<
248 css::uno::Reference< css::chart2::XDataSeries > > & aSeriesSeq,
249 const css::uno::Reference< css::chart2::XDiagram > & xDiagram,
250 bool bJapaneseCandleSticks,
251 bool bExportContent );
252 void exportDataPoints(
253 const css::uno::Reference< css::beans::XPropertySet > & xSeriesProperties,
254 sal_Int32 nSeriesLength,
255 const css::uno::Reference< css::chart2::XDiagram > & xDiagram,
256 bool bExportContent );
258 void exportCustomLabel(const SchXMLDataPointStruct& rPoint);
259 void exportCustomLabelPosition(const chart2::RelativePosition& xCustomLabelPosition);
261 void exportRegressionCurve(
262 const css::uno::Reference<css::chart2::XDataSeries>& xSeries,
263 const css::awt::Size& rPageSize,
264 bool bExportContent );
266 void exportErrorBar (
267 const css::uno::Reference<beans::XPropertySet> &xSeriesProp, bool bYError,
268 bool bExportContent );
270 /// add svg position as attribute for current element
271 void addPosition( const css::awt::Point & rPosition );
272 void addPosition( const css::uno::Reference< css::drawing::XShape >& xShape );
273 /// add svg size as attribute for current element
274 void addSize( const css::awt::Size & rSize, bool bIsOOoNamespace = false );
275 void addSize( const css::uno::Reference< css::drawing::XShape >& xShape );
276 /// exports a string as a paragraph element
277 void exportText( const OUString& rText );
279 public:
280 SvXMLExport& mrExport;
281 SvXMLAutoStylePoolP& mrAutoStylePool;
282 rtl::Reference< XMLPropertySetMapper > mxPropertySetMapper;
283 rtl::Reference< XMLChartExportPropertyMapper > mxExpPropMapper;
285 static constexpr OUString gsTableName = u"local-table"_ustr;
286 OUStringBuffer msStringBuffer;
287 OUString msString;
289 // members filled by InitRangeSegmentationProperties (retrieved from DataProvider)
290 bool mbHasCategoryLabels; //if the categories are only automatically generated this will be false
291 bool mbRowSourceColumns;
292 OUString msChartAddress;
293 css::uno::Sequence< sal_Int32 > maSequenceMapping;
295 OUString msCLSID;
297 OUString maSrcShellID;
298 OUString maDestShellID;
300 css::uno::Reference< css::drawing::XShapes > mxAdditionalShapes;
302 tDataSequenceCont m_aDataSequencesToExport;
303 OUString maCategoriesRange;
306 namespace
308 CustomLabelData lcl_getCustomLabelField(SvXMLExport const& rExport,
309 sal_Int32 nDataPointIndex,
310 const uno::Reference< chart2::XDataSeries >& rSeries)
312 if (!rSeries.is())
313 return CustomLabelData();
315 // Custom data label text will be written to the <text:p> child element of a
316 // <chart:data-label> element. That exists only since ODF 1.2.
317 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
318 rExport.getSaneDefaultVersion());
319 if (nCurrentODFVersion < SvtSaveOptions::ODFSVER_012)
320 return CustomLabelData();
322 if(Reference<beans::XPropertySet> xLabels = rSeries->getDataPointByIndex(nDataPointIndex); xLabels.is())
324 if(Any aAny = xLabels->getPropertyValue("CustomLabelFields"); aAny.hasValue())
326 CustomLabelData aData;
327 Sequence<uno::Reference<chart2::XDataPointCustomLabelField>> aCustomLabels;
328 aAny >>= aCustomLabels;
329 for (const auto& rField: std::as_const(aCustomLabels))
331 if (rField->getFieldType() == chart2::DataPointCustomLabelFieldType_CELLRANGE)
333 if (rField->getDataLabelsRange())
334 aData.mbDataLabelsRange = true;
335 aData.maRange = rField->getCellRange();
336 aData.maGuid = rField->getGuid();
340 aData.maFields = aCustomLabels;
341 return aData;
344 return CustomLabelData();
347 css::chart2::RelativePosition lcl_getCustomLabelPosition(
348 SvXMLExport const& rExport,
349 sal_Int32 const nDataPointIndex,
350 const uno::Reference< chart2::XDataSeries >& rSeries)
352 if (!rSeries.is())
353 return chart2::RelativePosition();
355 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
356 rExport.getSaneDefaultVersion());
358 if ((nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) == 0) // do not export to ODF 1.3 or older
359 return chart2::RelativePosition();
361 if (Reference<beans::XPropertySet> xLabels = rSeries->getDataPointByIndex(nDataPointIndex); xLabels.is())
363 if (Any aAny = xLabels->getPropertyValue("CustomLabelPosition"); aAny.hasValue())
365 chart2::RelativePosition aCustomLabelPos;
366 aAny >>= aCustomLabelPos;
367 return aCustomLabelPos;
370 return chart2::RelativePosition();
373 class lcl_MatchesRole
375 public:
376 explicit lcl_MatchesRole( OUString aRole ) :
377 m_aRole(std::move( aRole ))
380 bool operator () ( const Reference< chart2::data::XLabeledDataSequence > & xSeq ) const
382 if( !xSeq.is() )
383 return false;
384 Reference< beans::XPropertySet > xProp( xSeq->getValues(), uno::UNO_QUERY );
385 OUString aRole;
387 return ( xProp.is() &&
388 (xProp->getPropertyValue( "Role" ) >>= aRole ) &&
389 m_aRole == aRole );
392 private:
393 OUString m_aRole;
396 Reference< chart2::data::XLabeledDataSequence > lcl_getCategories( const Reference< chart2::XDiagram > & xDiagram )
398 Reference< chart2::data::XLabeledDataSequence > xResult;
401 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt(
402 xDiagram, uno::UNO_QUERY_THROW );
403 const Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq(
404 xCooSysCnt->getCoordinateSystems());
405 for( const auto& rCooSys : aCooSysSeq )
407 Reference< chart2::XCoordinateSystem > xCooSys( rCooSys );
408 SAL_WARN_IF( !xCooSys.is(), "xmloff.chart", "xCooSys is NULL" );
409 for( sal_Int32 nN = xCooSys->getDimension(); nN--; )
411 const sal_Int32 nMaxAxisIndex = xCooSys->getMaximumAxisIndexByDimension(nN);
412 for(sal_Int32 nI=0; nI<=nMaxAxisIndex; ++nI)
414 Reference< chart2::XAxis > xAxis = xCooSys->getAxisByDimension( nN, nI );
415 SAL_WARN_IF( !xAxis.is(), "xmloff.chart", "xAxis is NULL");
416 if( xAxis.is())
418 chart2::ScaleData aScaleData = xAxis->getScaleData();
419 if( aScaleData.Categories.is())
421 xResult.set( aScaleData.Categories );
422 break;
429 catch( const uno::Exception & )
431 DBG_UNHANDLED_EXCEPTION("xmloff.chart");
434 return xResult;
437 Reference< chart2::data::XDataSource > lcl_createDataSource(
438 const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aData )
440 Reference< uno::XComponentContext > xContext(
441 comphelper::getProcessComponentContext() );
442 Reference< chart2::data::XDataSink > xSink(
443 xContext->getServiceManager()->createInstanceWithContext(
444 "com.sun.star.chart2.data.DataSource", xContext ),
445 uno::UNO_QUERY_THROW );
446 xSink->setData( aData );
448 return Reference< chart2::data::XDataSource >( xSink, uno::UNO_QUERY );
451 Sequence< Reference< chart2::data::XLabeledDataSequence > > lcl_getAllSeriesSequences( const Reference< chart2::XChartDocument >& xChartDoc )
453 ::std::vector< Reference< chart2::data::XLabeledDataSequence > > aContainer;
454 if( xChartDoc.is() )
456 Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram());
457 ::std::vector< Reference< chart2::XDataSeries > > aSeriesVector( SchXMLSeriesHelper::getDataSeriesFromDiagram( xDiagram ));
458 for( const auto& rSeries : aSeriesVector )
460 Reference< chart2::data::XDataSource > xDataSource( rSeries, uno::UNO_QUERY );
461 if( !xDataSource.is() )
462 continue;
463 const uno::Sequence< Reference< chart2::data::XLabeledDataSequence > > aDataSequences( xDataSource->getDataSequences() );
464 aContainer.insert( aContainer.end(), aDataSequences.begin(), aDataSequences.end() );
468 return comphelper::containerToSequence( aContainer );
471 Reference< chart2::data::XLabeledDataSequence >
472 lcl_getDataSequenceByRole(
473 const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aLabeledSeq,
474 const OUString & rRole )
476 Reference< chart2::data::XLabeledDataSequence > aNoResult;
478 const Reference< chart2::data::XLabeledDataSequence > * pBegin = aLabeledSeq.getConstArray();
479 const Reference< chart2::data::XLabeledDataSequence > * pEnd = pBegin + aLabeledSeq.getLength();
480 const Reference< chart2::data::XLabeledDataSequence > * pMatch =
481 ::std::find_if( pBegin, pEnd, lcl_MatchesRole( rRole ));
483 if( pMatch != pEnd )
484 return *pMatch;
486 return aNoResult;
489 Reference< chart2::data::XDataSource > lcl_pressUsedDataIntoRectangularFormat( const Reference< chart2::XChartDocument >& xChartDoc, bool& rOutSourceHasCategoryLabels )
491 ::std::vector< Reference< chart2::data::XLabeledDataSequence > > aLabeledSeqVector;
493 //categories are always the first sequence
494 Reference< chart2::XDiagram > xDiagram( xChartDoc->getFirstDiagram());
495 Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xDiagram ) );
496 if( xCategories.is() )
497 aLabeledSeqVector.push_back( xCategories );
498 rOutSourceHasCategoryLabels = xCategories.is();
500 const Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeriesSeqVector(
501 lcl_getAllSeriesSequences( xChartDoc ) );
503 //the first x-values is always the next sequence //todo ... other x-values get lost for old format
504 Reference< chart2::data::XLabeledDataSequence > xXValues(
505 lcl_getDataSequenceByRole( aSeriesSeqVector, "values-x" ) );
506 if( xXValues.is() )
507 aLabeledSeqVector.push_back( xXValues );
509 //add all other sequences now without x-values
510 lcl_MatchesRole aHasXValues( "values-x" );
511 std::copy_if(aSeriesSeqVector.begin(), aSeriesSeqVector.end(), std::back_inserter(aLabeledSeqVector),
512 [&aHasXValues](const auto& rSeriesSeq) { return !aHasXValues( rSeriesSeq ); });
514 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeq( comphelper::containerToSequence(aLabeledSeqVector) );
516 return lcl_createDataSource( aSeq );
519 bool lcl_isSeriesAttachedToFirstAxis(
520 const Reference< chart2::XDataSeries > & xDataSeries )
522 bool bResult=true;
526 sal_Int32 nAxisIndex = 0;
527 Reference< beans::XPropertySet > xProp( xDataSeries, uno::UNO_QUERY_THROW );
528 xProp->getPropertyValue("AttachedAxisIndex") >>= nAxisIndex;
529 bResult = (0==nAxisIndex);
531 catch( const uno::Exception & )
533 DBG_UNHANDLED_EXCEPTION("xmloff.chart");
536 return bResult;
539 OUString lcl_ConvertRange( const OUString & rRange, const Reference< chart2::XChartDocument > & xDoc )
541 OUString aResult = rRange;
542 if( !xDoc.is() )
543 return aResult;
544 Reference< chart2::data::XRangeXMLConversion > xConversion(
545 xDoc->getDataProvider(), uno::UNO_QUERY );
546 if( xConversion.is())
547 aResult = xConversion->convertRangeToXML( rRange );
548 return aResult;
551 typedef ::std::pair< OUString, OUString > tLabelAndValueRange;
553 tLabelAndValueRange lcl_getLabelAndValueRangeByRole(
554 const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aSeqCnt,
555 const OUString & rRole,
556 const Reference< chart2::XChartDocument > & xDoc,
557 SchXMLExportHelper_Impl::tDataSequenceCont & rOutSequencesToExport )
559 tLabelAndValueRange aResult;
561 Reference< chart2::data::XLabeledDataSequence > xLabeledSeq(
562 lcl_getDataSequenceByRole( aSeqCnt, rRole ));
563 if( xLabeledSeq.is())
565 Reference< chart2::data::XDataSequence > xLabelSeq( xLabeledSeq->getLabel());
566 if( xLabelSeq.is())
567 aResult.first = lcl_ConvertRange( xLabelSeq->getSourceRangeRepresentation(), xDoc );
569 Reference< chart2::data::XDataSequence > xValueSeq( xLabeledSeq->getValues());
570 if( xValueSeq.is())
571 aResult.second = lcl_ConvertRange( xValueSeq->getSourceRangeRepresentation(), xDoc );
573 if( xLabelSeq.is() || xValueSeq.is())
574 rOutSequencesToExport.emplace_back( xLabelSeq, xValueSeq );
577 return aResult;
580 sal_Int32 lcl_getSequenceLengthByRole(
581 const Sequence< Reference< chart2::data::XLabeledDataSequence > > & aSeqCnt,
582 const OUString & rRole )
584 Reference< chart2::data::XLabeledDataSequence > xLabeledSeq(
585 lcl_getDataSequenceByRole( aSeqCnt, rRole ));
586 if( xLabeledSeq.is())
588 Reference< chart2::data::XDataSequence > xSeq( xLabeledSeq->getValues());
589 return xSeq->getData().getLength();
591 return 0;
594 OUString lcl_flattenStringSequence( const Sequence< OUString > & rSequence )
596 OUStringBuffer aResult;
597 bool bPrecedeWithSpace = false;
598 for( const auto& rString : rSequence )
600 if( !rString.isEmpty())
602 if( bPrecedeWithSpace )
603 aResult.append( ' ' );
604 aResult.append( rString );
605 bPrecedeWithSpace = true;
608 return aResult.makeStringAndClear();
611 void lcl_getLabelStringSequence( Sequence< OUString >& rOutLabels, const Reference< chart2::data::XDataSequence > & xLabelSeq )
613 uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xLabelSeq, uno::UNO_QUERY );
614 if( xTextualDataSequence.is())
616 rOutLabels = xTextualDataSequence->getTextualData();
618 else if( xLabelSeq.is())
620 Sequence< uno::Any > aAnies( xLabelSeq->getData());
621 rOutLabels.realloc( aAnies.getLength());
622 auto pOutLabels = rOutLabels.getArray();
623 for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
624 aAnies[i] >>= pOutLabels[i];
628 sal_Int32 lcl_getMaxSequenceLength(
629 const SchXMLExportHelper_Impl::tDataSequenceCont & rContainer )
631 sal_Int32 nResult = 0;
632 for( const auto& rDataSequence : rContainer )
634 if( rDataSequence.second.is())
636 sal_Int32 nSeqLength = rDataSequence.second->getData().getLength();
637 if( nSeqLength > nResult )
638 nResult = nSeqLength;
641 return nResult;
644 uno::Sequence< OUString > lcl_DataSequenceToStringSequence(
645 const uno::Reference< chart2::data::XDataSequence >& xDataSequence )
647 uno::Sequence< OUString > aResult;
648 if(!xDataSequence.is())
649 return aResult;
651 uno::Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xDataSequence, uno::UNO_QUERY );
652 if( xTextualDataSequence.is() )
654 aResult = xTextualDataSequence->getTextualData();
656 else
658 uno::Sequence< uno::Any > aValues = xDataSequence->getData();
659 aResult.realloc(aValues.getLength());
660 auto pResult = aResult.getArray();
662 for(sal_Int32 nN=aValues.getLength();nN--;)
663 aValues[nN] >>= pResult[nN];
666 return aResult;
668 ::std::vector< double > lcl_getAllValuesFromSequence( const Reference< chart2::data::XDataSequence > & xSeq )
670 ::std::vector< double > aResult;
671 if(!xSeq.is())
672 return aResult;
674 uno::Sequence< double > aValuesSequence;
675 Reference< chart2::data::XNumericalDataSequence > xNumSeq( xSeq, uno::UNO_QUERY );
676 if( xNumSeq.is() )
678 aValuesSequence = xNumSeq->getNumericalData();
680 else
682 Sequence< uno::Any > aAnies( xSeq->getData() );
683 aValuesSequence.realloc( aAnies.getLength() );
684 auto pValuesSequence = aValuesSequence.getArray();
685 for( sal_Int32 i=0; i<aAnies.getLength(); ++i )
686 aAnies[i] >>= pValuesSequence[i];
689 //special handling for x-values (if x-values do point to categories, indices are used instead )
690 Reference< beans::XPropertySet > xProp( xSeq, uno::UNO_QUERY );
691 if( xProp.is() )
693 OUString aRole;
694 xProp->getPropertyValue("Role") >>= aRole;
695 if( aRole.match("values-x") )
697 //lcl_clearIfNoValuesButTextIsContained - replace by indices if the values are not appropriate
698 bool bHasValue = std::any_of(std::cbegin(aValuesSequence), std::cend(aValuesSequence),
699 [](double fValue) { return !std::isnan( fValue ); });
700 if(!bHasValue)
702 //no double value is contained
703 //is there any text?
704 const uno::Sequence< OUString > aStrings( lcl_DataSequenceToStringSequence( xSeq ) );
705 bool bHasText = std::any_of(aStrings.begin(), aStrings.end(),
706 [](const OUString& rString) { return !rString.isEmpty(); });
707 if( bHasText )
709 auto [begin, end] = asNonConstRange(aValuesSequence);
710 std::iota(begin, end, 1);
716 aResult.insert( aResult.end(), std::cbegin(aValuesSequence), std::cend(aValuesSequence) );
717 return aResult;
720 bool lcl_SequenceHasUnhiddenData( const uno::Reference< chart2::data::XDataSequence >& xDataSequence )
722 if( !xDataSequence.is() )
723 return false;
724 uno::Reference< beans::XPropertySet > xProp( xDataSequence, uno::UNO_QUERY );
725 if( xProp.is() )
727 uno::Sequence< sal_Int32 > aHiddenValues;
730 xProp->getPropertyValue("HiddenValues") >>= aHiddenValues;
731 if( !aHiddenValues.hasElements() )
732 return true;
734 catch( const uno::Exception& )
736 return true;
739 return xDataSequence->getData().hasElements();
742 typedef vector< OUString > tStringVector;
743 typedef vector< vector< double > > t2DNumberContainer;
745 struct lcl_TableData
747 t2DNumberContainer aDataInRows;
748 tStringVector aDataRangeRepresentations;
750 tStringVector aColumnDescriptions;
751 tStringVector aColumnDescriptions_Ranges;
753 tStringVector aRowDescriptions;
754 tStringVector aRowDescriptions_Ranges;
756 Sequence< Sequence< uno::Any > > aComplexColumnDescriptions;//outer index is columns - inner index is level
757 Sequence< Sequence< uno::Any > > aComplexRowDescriptions;//outer index is rows - inner index is level
759 ::std::vector< sal_Int32 > aHiddenColumns;
762 typedef ::std::map< sal_Int32, SchXMLExportHelper_Impl::tLabelValuesDataPair >
763 lcl_DataSequenceMap;
765 struct lcl_SequenceToMapElement
767 std::pair<const sal_Int32, SchXMLExportHelper_Impl::tLabelValuesDataPair>
768 operator() (const SchXMLExportHelper_Impl::tLabelValuesDataPair& rContent)
770 sal_Int32 nIndex = -1;
771 if( rContent.second.is()) //has values
773 OUString aRangeRep( rContent.second->getSourceRangeRepresentation());
774 nIndex = aRangeRep.toInt32();
776 else if( rContent.first.is()) //has labels
777 nIndex = o3tl::toInt32(rContent.first->getSourceRangeRepresentation().subView( sizeof("label ")));
778 return std::make_pair(nIndex, rContent);
782 void lcl_ReorderInternalSequencesAccordingToTheirRangeName(
783 SchXMLExportHelper_Impl::tDataSequenceCont & rInOutSequences )
785 lcl_DataSequenceMap aIndexSequenceMap;
786 ::std::transform( rInOutSequences.begin(), rInOutSequences.end(),
787 ::std::inserter( aIndexSequenceMap, aIndexSequenceMap.begin()),
788 lcl_SequenceToMapElement());
790 rInOutSequences.clear();
791 sal_Int32 nIndex = 0;
792 for( const auto& rEntry : aIndexSequenceMap )
794 if( rEntry.first >= 0 )
796 // fill empty columns
797 rInOutSequences.insert(
798 rInOutSequences.end(),
799 rEntry.first - nIndex,
800 SchXMLExportHelper_Impl::tDataSequenceCont::value_type(
801 uno::Reference< chart2::data::XDataSequence >(),
802 uno::Reference< chart2::data::XDataSequence >() ));
803 nIndex = rEntry.first;
804 rInOutSequences.push_back( rEntry.second );
807 ++nIndex;
811 lcl_TableData lcl_getDataForLocalTable(
812 const SchXMLExportHelper_Impl::tDataSequenceCont & aSequencesToExport,
813 const Reference< chart2::XAnyDescriptionAccess >& xAnyDescriptionAccess,
814 const OUString& rCategoriesRange,
815 bool bSeriesFromColumns,
816 const Reference< chart2::data::XRangeXMLConversion > & xRangeConversion )
818 lcl_TableData aResult;
822 Sequence< OUString > aSimpleCategories;
823 if( xAnyDescriptionAccess.is() )
825 //categories
826 if( bSeriesFromColumns )
828 aSimpleCategories = xAnyDescriptionAccess->getRowDescriptions();
829 aResult.aComplexRowDescriptions = xAnyDescriptionAccess->getAnyRowDescriptions();
831 else
833 aSimpleCategories = xAnyDescriptionAccess->getColumnDescriptions();
834 aResult.aComplexColumnDescriptions = xAnyDescriptionAccess->getAnyColumnDescriptions();
838 //series values and series labels
839 SchXMLExportHelper_Impl::tDataSequenceCont::size_type nNumSequences = aSequencesToExport.size();
841 auto nMaxSequenceLength( lcl_getMaxSequenceLength( aSequencesToExport ));
842 if( aSimpleCategories.getLength() > nMaxSequenceLength )
844 aSimpleCategories.realloc(nMaxSequenceLength);//#i110617#
846 size_t nNumColumns( bSeriesFromColumns ? nNumSequences : nMaxSequenceLength );
847 size_t nNumRows( bSeriesFromColumns ? nMaxSequenceLength : nNumSequences );
849 // resize data
850 aResult.aDataInRows.resize( nNumRows );
852 for (auto& aData: aResult.aDataInRows)
853 aData.resize(nNumColumns, std::numeric_limits<double>::quiet_NaN());
854 aResult.aColumnDescriptions.resize( nNumColumns );
855 aResult.aComplexColumnDescriptions.realloc( nNumColumns );
856 aResult.aRowDescriptions.resize( nNumRows );
857 aResult.aComplexRowDescriptions.realloc( nNumRows );
859 tStringVector& rCategories = bSeriesFromColumns ? aResult.aRowDescriptions : aResult.aColumnDescriptions;
860 tStringVector& rLabels = bSeriesFromColumns ? aResult.aColumnDescriptions : aResult.aRowDescriptions;
862 //categories
863 rCategories.clear();
864 rCategories.insert( rCategories.begin(), std::cbegin(aSimpleCategories), std::cend(aSimpleCategories) );
865 if( !rCategoriesRange.isEmpty() )
867 OUString aRange(rCategoriesRange);
868 if( xRangeConversion.is())
869 aRange = xRangeConversion->convertRangeToXML( aRange );
870 if( bSeriesFromColumns )
871 aResult.aRowDescriptions_Ranges.push_back( aRange );
872 else
873 aResult.aColumnDescriptions_Ranges.push_back( aRange );
876 // iterate over all sequences
877 size_t nSeqIdx = 0;
878 Sequence< Sequence< OUString > > aComplexLabels(nNumSequences);
879 auto aComplexLabelsRange = asNonConstRange(aComplexLabels);
880 for( const auto& rDataSequence : aSequencesToExport )
882 OUString aRange;
883 Sequence< OUString >& rCurrentComplexLabel = aComplexLabelsRange[nSeqIdx];
884 if( rDataSequence.first.is())
886 lcl_getLabelStringSequence( rCurrentComplexLabel, rDataSequence.first );
887 rLabels[nSeqIdx] = lcl_flattenStringSequence( rCurrentComplexLabel );
888 aRange = rDataSequence.first->getSourceRangeRepresentation();
889 if( xRangeConversion.is())
890 aRange = xRangeConversion->convertRangeToXML( aRange );
892 else if( rDataSequence.second.is())
894 rCurrentComplexLabel.realloc(1);
895 rLabels[nSeqIdx] = rCurrentComplexLabel.getArray()[0] = lcl_flattenStringSequence(
896 rDataSequence.second->generateLabel( chart2::data::LabelOrigin_SHORT_SIDE ));
898 if( bSeriesFromColumns )
899 aResult.aColumnDescriptions_Ranges.push_back( aRange );
900 else
901 aResult.aRowDescriptions_Ranges.push_back( aRange );
903 ::std::vector< double > aNumbers( lcl_getAllValuesFromSequence( rDataSequence.second ));
904 if( bSeriesFromColumns )
906 const sal_Int32 nSize( static_cast< sal_Int32 >( aNumbers.size()));
907 for( sal_Int32 nIdx=0; nIdx<nSize; ++nIdx )
908 aResult.aDataInRows[nIdx][nSeqIdx] = aNumbers[nIdx];
910 else
911 aResult.aDataInRows[nSeqIdx] = aNumbers;
913 if( rDataSequence.second.is())
915 aRange = rDataSequence.second->getSourceRangeRepresentation();
916 if( xRangeConversion.is())
917 aRange = xRangeConversion->convertRangeToXML( aRange );
919 aResult.aDataRangeRepresentations.push_back( aRange );
921 //is column hidden?
922 if( !lcl_SequenceHasUnhiddenData(rDataSequence.first) && !lcl_SequenceHasUnhiddenData(rDataSequence.second) )
923 aResult.aHiddenColumns.push_back(nSeqIdx);
925 ++nSeqIdx;
927 Sequence< Sequence< Any > >& rComplexAnyLabels = bSeriesFromColumns ? aResult.aComplexColumnDescriptions : aResult.aComplexRowDescriptions;//#i116544#
928 rComplexAnyLabels.realloc(aComplexLabels.getLength());
929 auto pComplexAnyLabels = rComplexAnyLabels.getArray();
930 for( sal_Int32 nN=0; nN<aComplexLabels.getLength();nN++ )
932 Sequence< OUString >& rSource = aComplexLabelsRange[nN];
933 Sequence< Any >& rTarget = pComplexAnyLabels[nN];
934 rTarget.realloc( rSource.getLength() );
935 auto pTarget = rTarget.getArray();
936 for( sal_Int32 i=0; i<rSource.getLength(); i++ )
937 pTarget[i] <<= rSource[i];
940 catch( const uno::Exception & )
942 TOOLS_INFO_EXCEPTION("xmloff.chart", "something went wrong during table data collection");
945 return aResult;
948 void lcl_exportNumberFormat( const OUString& rPropertyName, const Reference< beans::XPropertySet >& xPropSet,
949 SvXMLExport& rExport )
951 if( xPropSet.is())
953 sal_Int32 nNumberFormat = 0;
954 Any aNumAny = xPropSet->getPropertyValue( rPropertyName );
955 if( (aNumAny >>= nNumberFormat) && (nNumberFormat != -1) )
956 rExport.addDataStyle( nNumberFormat );
960 ::std::vector< Reference< chart2::data::XDataSequence > >
961 lcl_getErrorBarSequences( const Reference< beans::XPropertySet > & xErrorBarProp )
963 ::std::vector< Reference< chart2::data::XDataSequence > > aResult;
964 Reference< chart2::data::XDataSource > xErrorBarDataSource( xErrorBarProp, uno::UNO_QUERY );
965 if( !xErrorBarDataSource.is())
966 return aResult;
968 const Sequence< Reference< chart2::data::XLabeledDataSequence > > aSequences(
969 xErrorBarDataSource->getDataSequences());
970 for( const auto& rSequence : aSequences )
974 if( rSequence.is())
976 Reference< chart2::data::XDataSequence > xSequence( rSequence->getValues());
977 Reference< beans::XPropertySet > xSeqProp( xSequence, uno::UNO_QUERY_THROW );
978 OUString aRole;
979 if( ( xSeqProp->getPropertyValue( "Role" ) >>= aRole ) &&
980 aRole.match( "error-bars-" ))
982 aResult.push_back( xSequence );
986 catch( const uno::Exception & )
988 TOOLS_INFO_EXCEPTION("xmloff.chart", "chart:exporting error bar ranges" );
992 return aResult;
995 bool lcl_exportDomainForThisSequence( const Reference< chart2::data::XDataSequence >& rValues, OUString& rFirstRangeForThisDomainIndex, SvXMLExport& rExport )
997 bool bDomainExported = false;
998 if( rValues.is())
1000 Reference< chart2::XChartDocument > xNewDoc( rExport.GetModel(), uno::UNO_QUERY );
1001 OUString aRange( lcl_ConvertRange( rValues->getSourceRangeRepresentation(), xNewDoc ) );
1003 //work around error in OOo 2.0 (problems with multiple series having a domain element)
1004 if( rFirstRangeForThisDomainIndex.isEmpty() || aRange != rFirstRangeForThisDomainIndex )
1006 rExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, aRange);
1007 SvXMLElementExport aDomain( rExport, XML_NAMESPACE_CHART, XML_DOMAIN, true, true );
1008 bDomainExported = true;
1011 if( rFirstRangeForThisDomainIndex.isEmpty() )
1012 rFirstRangeForThisDomainIndex = aRange;
1014 return bDomainExported;
1017 } // anonymous namespace
1020 SchXMLExportHelper::SchXMLExportHelper( SvXMLExport& rExport, SvXMLAutoStylePoolP& rASPool )
1021 : m_pImpl( new SchXMLExportHelper_Impl( rExport, rASPool ) )
1025 SchXMLExportHelper::~SchXMLExportHelper()
1029 const OUString& SchXMLExportHelper::getChartCLSID() const
1031 return m_pImpl->msCLSID;
1034 void SchXMLExportHelper::SetSourceShellID( const OUString& rShellID )
1036 m_pImpl->maSrcShellID = rShellID;
1039 void SchXMLExportHelper::SetDestinationShellID( const OUString& rShellID )
1041 m_pImpl->maDestShellID = rShellID;
1044 const rtl::Reference< XMLPropertySetMapper >& SchXMLExportHelper_Impl::GetPropertySetMapper() const
1046 return mxPropertySetMapper;
1049 void SchXMLExportHelper_Impl::exportAutoStyles()
1051 if( !mxExpPropMapper.is())
1052 return;
1054 //ToDo: when embedded in calc/writer this is not necessary because the
1055 // numberformatter is shared between both documents
1056 mrExport.exportAutoDataStyles();
1058 // export chart auto styles
1059 mrAutoStylePool.exportXML( XmlStyleFamily::SCH_CHART_ID );
1061 // export auto styles for additional shapes
1062 mrExport.GetShapeExport()->exportAutoStyles();
1063 // and for text in additional shapes
1064 mrExport.GetTextParagraphExport()->exportTextAutoStyles();
1067 // private methods
1069 SchXMLExportHelper_Impl::SchXMLExportHelper_Impl(
1070 SvXMLExport& rExport,
1071 SvXMLAutoStylePoolP& rASPool ) :
1072 mrExport( rExport ),
1073 mrAutoStylePool( rASPool ),
1074 mxPropertySetMapper( new XMLChartPropertySetMapper(&rExport) ),
1075 mxExpPropMapper( new XMLChartExportPropertyMapper( mxPropertySetMapper, rExport ) ),
1076 mbHasCategoryLabels( false ),
1077 mbRowSourceColumns( true ),
1078 msCLSID( SvGlobalName( SO3_SCH_CLASSID ).GetHexName() )
1080 // register chart auto-style family
1081 mrAutoStylePool.AddFamily(
1082 XmlStyleFamily::SCH_CHART_ID,
1083 XML_STYLE_FAMILY_SCH_CHART_NAME,
1084 mxExpPropMapper.get(),
1085 XML_STYLE_FAMILY_SCH_CHART_PREFIX);
1087 // register shape family
1088 mrAutoStylePool.AddFamily(
1089 XmlStyleFamily::SD_GRAPHICS_ID,
1090 XML_STYLE_FAMILY_SD_GRAPHICS_NAME,
1091 mxExpPropMapper.get(),
1092 XML_STYLE_FAMILY_SD_GRAPHICS_PREFIX);
1093 // register paragraph family also for shapes
1094 mrAutoStylePool.AddFamily(
1095 XmlStyleFamily::TEXT_PARAGRAPH,
1096 GetXMLToken( XML_PARAGRAPH ),
1097 mxExpPropMapper.get(),
1098 OUString( 'P' ));
1099 // register text family also for shapes
1100 mrAutoStylePool.AddFamily(
1101 XmlStyleFamily::TEXT_TEXT,
1102 GetXMLToken( XML_TEXT ),
1103 mxExpPropMapper.get(),
1104 OUString( 'T' ));
1107 void SchXMLExportHelper_Impl::collectAutoStyles( Reference< chart::XChartDocument > const & rChartDoc )
1109 parseDocument( rChartDoc, false );
1112 void SchXMLExportHelper_Impl::exportChart( Reference< chart::XChartDocument > const & rChartDoc,
1113 bool bIncludeTable )
1115 parseDocument( rChartDoc, true, bIncludeTable );
1116 SAL_WARN_IF( !maAutoStyleNameQueue.empty(), "xmloff.chart", "There are still remaining autostyle names in the queue" );
1119 static OUString lcl_GetStringFromNumberSequence( const css::uno::Sequence< sal_Int32 >& rSequenceMapping, bool bRemoveOneFromEachIndex /*should be true if having categories*/ )
1121 OUStringBuffer aBuf;
1122 bool bHasPredecessor = false;
1123 for( sal_Int32 nIndex : rSequenceMapping )
1125 if( bRemoveOneFromEachIndex )
1126 --nIndex;
1127 if(nIndex>=0)
1129 if(bHasPredecessor)
1130 aBuf.append( ' ' );
1131 aBuf.append( nIndex );
1132 bHasPredecessor = true;
1135 return aBuf.makeStringAndClear();
1138 /// if bExportContent is false the auto-styles are collected
1139 void SchXMLExportHelper_Impl::parseDocument( Reference< chart::XChartDocument > const & rChartDoc,
1140 bool bExportContent,
1141 bool bIncludeTable )
1143 Reference< chart2::XChartDocument > xNewDoc( rChartDoc, uno::UNO_QUERY );
1144 if( !rChartDoc.is() || !xNewDoc.is() )
1146 SAL_WARN("xmloff.chart", "No XChartDocument was given for export." );
1147 return;
1150 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(mrExport.getSaneDefaultVersion());
1152 mxExpPropMapper->setChartDoc(xNewDoc);
1154 awt::Size aPageSize( getPageSize( xNewDoc ));
1155 if( bExportContent )
1156 addSize( aPageSize );
1157 Reference< chart::XDiagram > xDiagram = rChartDoc->getDiagram();
1158 Reference< chart2::XDiagram > xNewDiagram;
1159 if( xNewDoc.is())
1160 xNewDiagram.set( xNewDoc->getFirstDiagram());
1162 //todo remove if model changes are notified and view is updated automatically
1163 if( bExportContent )
1165 Reference< util::XRefreshable > xRefreshable( xNewDoc, uno::UNO_QUERY );
1166 if( xRefreshable.is() )
1167 xRefreshable->refresh();
1170 // get Properties of ChartDocument
1171 bool bHasMainTitle = false;
1172 bool bHasSubTitle = false;
1173 bool bHasLegend = false;
1174 util::DateTime aNullDate(0,0,0,0,30,12,1899, false);
1176 std::vector< XMLPropertyState > aPropertyStates;
1178 Reference< beans::XPropertySet > xDocPropSet( rChartDoc, uno::UNO_QUERY );
1179 if( xDocPropSet.is())
1183 Any aAny = xDocPropSet->getPropertyValue("HasMainTitle");
1184 aAny >>= bHasMainTitle;
1185 aAny = xDocPropSet->getPropertyValue("HasSubTitle");
1186 aAny >>= bHasSubTitle;
1187 aAny = xDocPropSet->getPropertyValue("HasLegend");
1188 aAny >>= bHasLegend;
1189 if ( bIncludeTable )
1191 aAny = xDocPropSet->getPropertyValue("NullDate");
1192 if ( !aAny.hasValue() )
1194 Reference<container::XChild> xChild(rChartDoc, uno::UNO_QUERY );
1195 if ( xChild.is() )
1197 Reference< beans::XPropertySet > xParentDoc( xChild->getParent(),uno::UNO_QUERY);
1198 if ( xParentDoc.is() && xParentDoc->getPropertySetInfo()->hasPropertyByName("NullDate") )
1199 aAny = xParentDoc->getPropertyValue("NullDate");
1203 aAny >>= aNullDate;
1206 catch( const beans::UnknownPropertyException & )
1208 SAL_WARN("xmloff.chart", "Required property not found in ChartDocument" );
1212 if ( bIncludeTable && (aNullDate.Day != 30 || aNullDate.Month != 12 || aNullDate.Year != 1899 ) )
1214 SvXMLElementExport aSet( mrExport, XML_NAMESPACE_TABLE, XML_CALCULATION_SETTINGS, true, true );
1216 OUStringBuffer sBuffer;
1217 ::sax::Converter::convertDateTime(sBuffer, aNullDate, nullptr);
1218 mrExport.AddAttribute( XML_NAMESPACE_TABLE,XML_DATE_VALUE,sBuffer.makeStringAndClear());
1219 SvXMLElementExport aNull( mrExport, XML_NAMESPACE_TABLE, XML_NULL_DATE, true, true );
1223 // chart element
1224 std::unique_ptr<SvXMLElementExport> xElChart;
1226 // get property states for autostyles
1227 if( mxExpPropMapper.is())
1229 Reference< beans::XPropertySet > xPropSet = rChartDoc->getArea();
1230 if( xPropSet.is())
1231 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
1234 if( bExportContent )
1236 //export data provider in xlink:href attribute
1238 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
1240 OUString aDataProviderURL( ".." );
1241 if( xNewDoc->hasInternalDataProvider() )
1242 aDataProviderURL = ".";
1243 else //special handling for data base data provider necessary
1245 Reference< chart2::data::XDatabaseDataProvider > xDBDataProvider( xNewDoc->getDataProvider(), uno::UNO_QUERY );
1246 if( xDBDataProvider.is() )
1247 aDataProviderURL = ".";
1249 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_HREF, aDataProviderURL );
1250 mrExport.AddAttribute( XML_NAMESPACE_XLINK, XML_TYPE, XML_SIMPLE );
1253 Reference<chart2::data::XPivotTableDataProvider> xPivotTableDataProvider(xNewDoc->getDataProvider(), uno::UNO_QUERY);
1254 if (xPivotTableDataProvider.is() && nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED)
1256 OUString sPivotTableName = xPivotTableDataProvider->getPivotTableName();
1257 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_PILOT_SOURCE, sPivotTableName);
1260 OUString sChartType( xDiagram->getDiagramType() );
1262 // attributes
1263 // determine class
1264 if( !sChartType.isEmpty())
1266 enum XMLTokenEnum eXMLChartType = SchXMLTools::getTokenByChartType( sChartType, true /* bUseOldNames */ );
1268 SAL_WARN_IF( eXMLChartType == XML_TOKEN_INVALID, "xmloff.chart", "invalid chart class" );
1269 if( eXMLChartType == XML_TOKEN_INVALID )
1270 eXMLChartType = XML_BAR;
1272 if( eXMLChartType == XML_ADD_IN )
1274 // sChartType is the service-name of the add-in
1275 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS,
1276 mrExport.GetNamespaceMap().GetQNameByKey(
1277 XML_NAMESPACE_OOO, sChartType) );
1279 else if( eXMLChartType != XML_TOKEN_INVALID )
1281 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS,
1282 mrExport.GetNamespaceMap().GetQNameByKey(
1283 XML_NAMESPACE_CHART, GetXMLToken(eXMLChartType )) );
1286 //column-mapping or row-mapping
1287 if( maSequenceMapping.hasElements() )
1289 enum XMLTokenEnum eTransToken = ::xmloff::token::XML_ROW_MAPPING;
1290 if( mbRowSourceColumns )
1291 eTransToken = ::xmloff::token::XML_COLUMN_MAPPING;
1292 OUString aSequenceMappingStr( lcl_GetStringFromNumberSequence(
1293 maSequenceMapping, mbHasCategoryLabels && !xNewDoc->hasInternalDataProvider() ) );
1295 mrExport.AddAttribute( XML_NAMESPACE_CHART,
1296 ::xmloff::token::GetXMLToken( eTransToken ),
1297 aSequenceMappingStr );
1300 // write style name
1301 AddAutoStyleAttribute( aPropertyStates );
1303 //element
1304 xElChart.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_CHART, true, true ));
1306 else // autostyles
1308 CollectAutoStyle( std::move(aPropertyStates) );
1310 // remove property states for autostyles
1311 aPropertyStates.clear();
1313 // title element
1314 if( bHasMainTitle )
1316 // get property states for autostyles
1317 if( mxExpPropMapper.is())
1319 Reference< beans::XPropertySet > xPropSet( rChartDoc->getTitle(), uno::UNO_QUERY );
1320 if( xPropSet.is())
1321 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
1323 if( bExportContent )
1325 Reference< drawing::XShape > xShape = rChartDoc->getTitle();
1326 if( xShape.is()) // && "hasTitleBeenMoved"
1327 addPosition( xShape );
1329 // write style name
1330 AddAutoStyleAttribute( aPropertyStates );
1332 // element
1333 SvXMLElementExport aElTitle( mrExport, XML_NAMESPACE_CHART, XML_TITLE, true, true );
1335 // content (text:p)
1336 Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
1337 if( xPropSet.is())
1339 Any aAny( xPropSet->getPropertyValue( "String" ));
1340 OUString aText;
1341 aAny >>= aText;
1342 exportText( aText );
1345 else // autostyles
1347 CollectAutoStyle( std::move(aPropertyStates) );
1349 // remove property states for autostyles
1350 aPropertyStates.clear();
1353 // subtitle element
1354 if( bHasSubTitle )
1356 // get property states for autostyles
1357 if( mxExpPropMapper.is())
1359 Reference< beans::XPropertySet > xPropSet( rChartDoc->getSubTitle(), uno::UNO_QUERY );
1360 if( xPropSet.is())
1361 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
1364 if( bExportContent )
1366 Reference< drawing::XShape > xShape = rChartDoc->getSubTitle();
1367 if( xShape.is())
1368 addPosition( xShape );
1370 // write style name
1371 AddAutoStyleAttribute( aPropertyStates );
1373 // element (has no subelements)
1374 SvXMLElementExport aElSubTitle( mrExport, XML_NAMESPACE_CHART, XML_SUBTITLE, true, true );
1376 // content (text:p)
1377 Reference< beans::XPropertySet > xPropSet( xShape, uno::UNO_QUERY );
1378 if( xPropSet.is())
1380 Any aAny( xPropSet->getPropertyValue( "String" ));
1381 OUString aText;
1382 aAny >>= aText;
1383 exportText( aText );
1386 else // autostyles
1388 CollectAutoStyle( std::move(aPropertyStates) );
1390 // remove property states for autostyles
1391 aPropertyStates.clear();
1394 // legend element
1395 if( bHasLegend )
1397 // get property states for autostyles
1398 if( mxExpPropMapper.is())
1400 Reference< beans::XPropertySet > xPropSet( rChartDoc->getLegend(), uno::UNO_QUERY );
1401 if( xPropSet.is())
1402 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
1405 if( bExportContent )
1407 Reference< beans::XPropertySet > xProp( rChartDoc->getLegend(), uno::UNO_QUERY );
1408 if( xProp.is())
1410 // export legend anchor position
1413 Any aAny( xProp->getPropertyValue("Alignment"));
1414 if( SchXMLEnumConverter::getLegendPositionConverter().exportXML( msString, aAny, mrExport.GetMM100UnitConverter() ) )
1415 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LEGEND_POSITION, msString );
1417 catch( const beans::UnknownPropertyException & )
1419 SAL_WARN("xmloff.chart", "Property Align not found in ChartLegend" );
1422 // export legend overlay
1425 if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED)
1427 Any aAny( xProp->getPropertyValue("Overlay"));
1428 if(aAny.get<bool>())
1429 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_OVERLAY, OUString::boolean(true));
1432 catch( const beans::UnknownPropertyException & )
1434 SAL_WARN("xmloff.chart", "Property Overlay not found in ChartLegend" );
1437 // export absolute legend position
1438 Reference< drawing::XShape > xLegendShape( xProp, uno::UNO_QUERY );
1439 addPosition( xLegendShape );
1441 // export legend size
1442 if (xLegendShape.is() && nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
1446 chart::ChartLegendExpansion nLegendExpansion = chart::ChartLegendExpansion_HIGH;
1447 OUString aExpansionString;
1448 Any aAny( xProp->getPropertyValue("Expansion"));
1449 bool bHasExpansion = (aAny >>= nLegendExpansion);
1450 if( bHasExpansion && SchXMLEnumConverter::getLegendExpansionConverter().exportXML( aExpansionString, aAny, mrExport.GetMM100UnitConverter() ) )
1452 mrExport.AddAttribute( XML_NAMESPACE_STYLE, XML_LEGEND_EXPANSION, aExpansionString );
1453 if( nLegendExpansion == chart::ChartLegendExpansion_CUSTOM)
1455 awt::Size aSize( xLegendShape->getSize() );
1456 // tdf#131966: chart legend attributes width and height shouldn't be exported to ODF 1.2 (strict)
1457 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_013)
1458 { // ODF 1.3 OFFICE-3883
1459 addSize( aSize, false );
1461 else if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED)
1463 addSize( aSize, true );
1465 OUStringBuffer aAspectRatioString;
1466 ::sax::Converter::convertDouble(
1467 aAspectRatioString,
1468 (aSize.Height == 0
1469 ? 1.0
1470 : double(aSize.Width)/double(aSize.Height)));
1471 mrExport.AddAttribute( XML_NAMESPACE_STYLE, XML_LEGEND_EXPANSION_ASPECT_RATIO, aAspectRatioString.makeStringAndClear() );
1475 catch( const beans::UnknownPropertyException & )
1477 SAL_WARN("xmloff.chart", "Property Expansion not found in ChartLegend" );
1482 // write style name
1483 AddAutoStyleAttribute( aPropertyStates );
1485 // element
1486 SvXMLElementExport aLegend( mrExport, XML_NAMESPACE_CHART, XML_LEGEND, true, true );
1488 else // autostyles
1490 CollectAutoStyle( std::move(aPropertyStates) );
1492 // remove property states for autostyles
1493 aPropertyStates.clear();
1496 // Export data table element and properties
1497 if (xNewDiagram.is() && nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED)
1499 auto xDataTable = xNewDiagram->getDataTable();
1501 if (xDataTable.is())
1503 // get property states for autostyles
1504 if (mxExpPropMapper.is())
1506 uno::Reference<beans::XPropertySet> xPropSet(xDataTable, uno::UNO_QUERY);
1507 if (xPropSet.is())
1508 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
1511 if (bExportContent)
1513 // add style name attribute
1514 AddAutoStyleAttribute(aPropertyStates);
1515 SvXMLElementExport aDataTableElement(mrExport, XML_NAMESPACE_LO_EXT, XML_DATA_TABLE, true, true);
1517 else
1519 CollectAutoStyle(std::move(aPropertyStates));
1523 // remove property states for autostyles
1524 aPropertyStates.clear();
1527 // plot-area element
1528 if( xDiagram.is())
1529 exportPlotArea( xDiagram, xNewDiagram, aPageSize, bExportContent, bIncludeTable );
1531 // export additional shapes
1532 if( xDocPropSet.is() )
1534 if( bExportContent )
1536 if( mxAdditionalShapes.is())
1538 // can't call exportShapes with all shapes because the
1539 // initialisation happened with the complete draw page and not
1540 // the XShapes object used here. Thus the shapes have to be
1541 // exported one by one
1542 rtl::Reference< XMLShapeExport > rShapeExport = mrExport.GetShapeExport();
1543 Reference< drawing::XShape > xShape;
1544 const sal_Int32 nShapeCount( mxAdditionalShapes->getCount());
1545 for( sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++ )
1547 mxAdditionalShapes->getByIndex( nShapeId ) >>= xShape;
1548 SAL_WARN_IF( !xShape.is(), "xmloff.chart", "Shape without an XShape?" );
1549 if( ! xShape.is())
1550 continue;
1552 rShapeExport->exportShape( xShape );
1554 // this would be the easier way if it worked:
1555 //mrExport.GetShapeExport()->exportShapes( mxAdditionalShapes );
1558 else
1560 // get a sequence of non-chart shapes (inserted via clipboard)
1563 Any aShapesAny = xDocPropSet->getPropertyValue("AdditionalShapes");
1564 aShapesAny >>= mxAdditionalShapes;
1566 catch( const uno::Exception & )
1568 TOOLS_INFO_EXCEPTION("xmloff.chart", "AdditionalShapes not found" );
1571 if( mxAdditionalShapes.is())
1573 // seek shapes has to be called for the whole page because in
1574 // the shape export the vector of shapes is accessed via the
1575 // ZOrder which might be (actually is) larger than the number of
1576 // shapes in mxAdditionalShapes
1577 Reference< drawing::XDrawPageSupplier > xSupplier( rChartDoc, uno::UNO_QUERY );
1578 SAL_WARN_IF( !xSupplier.is(), "xmloff.chart", "Cannot retrieve draw page to initialize shape export" );
1579 if( xSupplier.is() )
1581 Reference< drawing::XShapes > xDrawPage = xSupplier->getDrawPage();
1582 SAL_WARN_IF( !xDrawPage.is(), "xmloff.chart", "Invalid draw page for initializing shape export" );
1583 if( xDrawPage.is())
1584 mrExport.GetShapeExport()->seekShapes( xDrawPage );
1587 // can't call collectShapesAutoStyles with all shapes because
1588 // the initialisation happened with the complete draw page and
1589 // not the XShapes object used here. Thus the shapes have to be
1590 // exported one by one
1591 rtl::Reference< XMLShapeExport > rShapeExport = mrExport.GetShapeExport();
1592 Reference< drawing::XShape > xShape;
1593 const sal_Int32 nShapeCount( mxAdditionalShapes->getCount());
1594 for( sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++ )
1596 mxAdditionalShapes->getByIndex( nShapeId ) >>= xShape;
1597 SAL_WARN_IF( !xShape.is(), "xmloff.chart", "Shape without an XShape?" );
1598 if( ! xShape.is())
1599 continue;
1601 rShapeExport->collectShapeAutoStyles( xShape );
1607 // table element
1608 // (is included as subelement of chart)
1609 if( bExportContent )
1611 // #85929# always export table, otherwise clipboard may lose data
1612 exportTable();
1616 static void lcl_exportComplexLabel( const Sequence< uno::Any >& rComplexLabel, SvXMLExport& rExport )
1618 sal_Int32 nLength = rComplexLabel.getLength();
1619 if( nLength<=1 )
1620 return;
1621 SvXMLElementExport aTextList( rExport, XML_NAMESPACE_TEXT, XML_LIST, true, true );
1622 for(const auto& rElem : rComplexLabel)
1624 SvXMLElementExport aListItem( rExport, XML_NAMESPACE_TEXT, XML_LIST_ITEM, true, true );
1625 OUString aString;
1626 if( !(rElem >>= aString) )
1628 double aNum;
1629 if (rElem >>= aNum)
1631 aString = OUString::number(aNum);
1634 SchXMLTools::exportText( rExport, aString, false /*bConvertTabsLFs*/ );
1638 void SchXMLExportHelper_Impl::exportTable()
1640 // table element
1641 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NAME, gsTableName );
1645 bool bProtected = false;
1646 Reference< beans::XPropertySet > xProps( mrExport.GetModel(), uno::UNO_QUERY_THROW );
1647 if ( ( xProps->getPropertyValue("DisableDataTableDialog") >>= bProtected ) &&
1648 bProtected )
1650 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_PROTECTED, XML_TRUE );
1653 catch ( const uno::Exception& )
1657 SvXMLElementExport aTable( mrExport, XML_NAMESPACE_TABLE, XML_TABLE, true, true );
1659 bool bHasOwnData = false;
1660 Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
1661 Reference< chart2::data::XRangeXMLConversion > xRangeConversion;
1662 if( xNewDoc.is())
1664 bHasOwnData = xNewDoc->hasInternalDataProvider();
1665 xRangeConversion.set( xNewDoc->getDataProvider(), uno::UNO_QUERY );
1668 Reference< chart2::XAnyDescriptionAccess > xAnyDescriptionAccess;
1670 Reference< chart::XChartDocument > xChartDoc( mrExport.GetModel(), uno::UNO_QUERY );
1671 if( xChartDoc.is() )
1672 xAnyDescriptionAccess.set( xChartDoc->getData(), uno::UNO_QUERY );
1675 if( bHasOwnData )
1676 lcl_ReorderInternalSequencesAccordingToTheirRangeName( m_aDataSequencesToExport );
1677 lcl_TableData aData( lcl_getDataForLocalTable( m_aDataSequencesToExport
1678 , xAnyDescriptionAccess, maCategoriesRange
1679 , mbRowSourceColumns, xRangeConversion ));
1681 tStringVector::const_iterator aDataRangeIter( aData.aDataRangeRepresentations.begin());
1682 const tStringVector::const_iterator aDataRangeEndIter( aData.aDataRangeRepresentations.end());
1684 tStringVector::const_iterator aRowDescriptions_RangeIter( aData.aRowDescriptions_Ranges.begin());
1685 const tStringVector::const_iterator aRowDescriptions_RangeEnd( aData.aRowDescriptions_Ranges.end());
1687 // declare columns
1689 SvXMLElementExport aHeaderColumns( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_HEADER_COLUMNS, true, true );
1690 SvXMLElementExport aHeaderColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true );
1693 SvXMLElementExport aColumns( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMNS, true, true );
1695 sal_Int32 nNextIndex = 0;
1696 for(sal_Int32 nHiddenIndex : aData.aHiddenColumns)
1698 //i91578 display of hidden values (copy paste scenario; export hidden flag thus it can be used during migration to locale table upon paste )
1699 if( nHiddenIndex > nNextIndex )
1701 sal_Int64 nRepeat = static_cast< sal_Int64 >( nHiddenIndex - nNextIndex );
1702 if(nRepeat>1)
1703 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
1704 OUString::number( nRepeat ));
1705 SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true );
1707 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_VISIBILITY, GetXMLToken( XML_COLLAPSE ) );
1708 SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true );
1709 nNextIndex = nHiddenIndex+1;
1712 sal_Int32 nEndIndex = aData.aColumnDescriptions.size()-1;
1713 if( nEndIndex >= nNextIndex )
1715 sal_Int64 nRepeat = static_cast< sal_Int64 >( nEndIndex - nNextIndex + 1 );
1716 if(nRepeat>1)
1717 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
1718 OUString::number( nRepeat ));
1719 SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true );
1723 // export rows with content
1724 //export header row
1726 SvXMLElementExport aHeaderRows( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_HEADER_ROWS, true, true );
1727 SvXMLElementExport aRow( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true );
1729 //first one empty cell for the row descriptions
1731 SvXMLElementExport aEmptyCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true );
1732 SvXMLElementExport aEmptyParagraph( mrExport, XML_NAMESPACE_TEXT, XML_P, true, true );
1735 //export column descriptions
1736 tStringVector::const_iterator aColumnDescriptions_RangeIter( aData.aColumnDescriptions_Ranges.begin());
1737 const tStringVector::const_iterator aColumnDescriptions_RangeEnd( aData.aColumnDescriptions_Ranges.end());
1738 const Sequence< Sequence< uno::Any > >& rComplexColumnDescriptions = aData.aComplexColumnDescriptions;
1739 sal_Int32 nComplexCount = rComplexColumnDescriptions.getLength();
1740 sal_Int32 nC = 0;
1741 for( const auto& rDesc : aData.aColumnDescriptions )
1743 bool bExportString = true;
1744 if( nC < nComplexCount )
1746 const Sequence< uno::Any >& rComplexLabel = rComplexColumnDescriptions[nC];
1747 if( rComplexLabel.hasElements() )
1749 double fValue=0.0;
1750 if( rComplexLabel[0] >>=fValue )
1752 bExportString = false;
1754 ::sax::Converter::convertDouble(
1755 msStringBuffer, fValue);
1756 msString = msStringBuffer.makeStringAndClear();
1757 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT );
1758 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE, msString );
1762 if( bExportString )
1764 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING );
1767 SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true );
1768 exportText( rDesc );
1769 if( nC < nComplexCount )
1770 lcl_exportComplexLabel( rComplexColumnDescriptions[nC], mrExport );
1771 if( !bHasOwnData && aColumnDescriptions_RangeIter != aColumnDescriptions_RangeEnd )
1773 // remind the original range to allow a correct re-association when copying via clipboard
1774 if (!(*aColumnDescriptions_RangeIter).isEmpty())
1775 SchXMLTools::exportRangeToSomewhere( mrExport, *aColumnDescriptions_RangeIter );
1776 ++aColumnDescriptions_RangeIter;
1779 nC++;
1781 SAL_WARN_IF( !bHasOwnData && (aColumnDescriptions_RangeIter != aColumnDescriptions_RangeEnd), "xmloff.chart", "bHasOwnData == false && aColumnDescriptions_RangeIter != aColumnDescriptions_RangeEnd" );
1782 } // closing row and header-rows elements
1784 // export value rows
1786 SvXMLElementExport aRows( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROWS, true, true );
1787 tStringVector::const_iterator aRowDescriptionsIter( aData.aRowDescriptions.begin());
1788 const Sequence< Sequence< uno::Any > >& rComplexRowDescriptions = aData.aComplexRowDescriptions;
1789 sal_Int32 nComplexCount = rComplexRowDescriptions.getLength();
1790 sal_Int32 nC = 0;
1792 for( const auto& rRow : aData.aDataInRows )
1794 SvXMLElementExport aRow( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true );
1796 //export row descriptions
1798 bool bExportString = true;
1799 if( nC < nComplexCount )
1801 const Sequence< uno::Any >& rComplexLabel = rComplexRowDescriptions[nC];
1802 if( rComplexLabel.hasElements() )
1804 double fValue=0.0;
1805 if( rComplexLabel[0] >>=fValue )
1807 bExportString = false;
1809 ::sax::Converter::convertDouble(msStringBuffer, fValue);
1810 msString = msStringBuffer.makeStringAndClear();
1811 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT );
1812 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE, msString );
1816 if( bExportString )
1818 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING );
1821 SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true );
1822 if( aRowDescriptionsIter != aData.aRowDescriptions.end())
1824 exportText( *aRowDescriptionsIter );
1825 if( nC < nComplexCount )
1826 lcl_exportComplexLabel( rComplexRowDescriptions[nC], mrExport );
1827 if( !bHasOwnData && aRowDescriptions_RangeIter != aRowDescriptions_RangeEnd )
1829 // remind the original range to allow a correct re-association when copying via clipboard
1830 SchXMLTools::exportRangeToSomewhere( mrExport, *aRowDescriptions_RangeIter );
1831 ++aRowDescriptions_RangeIter;
1833 ++aRowDescriptionsIter;
1837 //export row values
1838 for( t2DNumberContainer::value_type::const_iterator aColIt( rRow.begin());
1839 aColIt != rRow.end(); ++aColIt )
1841 ::sax::Converter::convertDouble( msStringBuffer, *aColIt );
1842 msString = msStringBuffer.makeStringAndClear();
1843 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT );
1844 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE, msString );
1845 SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true );
1846 exportText( msString ); // do not convert tabs and lfs
1847 if( ( !bHasOwnData && aDataRangeIter != aDataRangeEndIter ) &&
1848 ( mbRowSourceColumns || (aColIt == rRow.begin()) ) )
1850 // remind the original range to allow a correct re-association when copying via clipboard
1851 if (!(*aDataRangeIter).isEmpty())
1852 SchXMLTools::exportRangeToSomewhere( mrExport, *aDataRangeIter );
1853 ++aDataRangeIter;
1857 ++nC;
1861 // if range iterator was used it should have reached its end
1862 SAL_WARN_IF( !bHasOwnData && (aDataRangeIter != aDataRangeEndIter), "xmloff.chart", "bHasOwnData == false && aDataRangeIter != aDataRangeEndIter" );
1863 SAL_WARN_IF( !bHasOwnData && (aRowDescriptions_RangeIter != aRowDescriptions_RangeEnd), "xmloff.chart", "bHasOwnData == false && aRowDescriptions_RangeIter != aRowDescriptions_RangeEnd" );
1866 namespace
1869 Reference< chart2::XCoordinateSystem > lcl_getCooSys( const Reference< chart2::XDiagram > & xNewDiagram )
1871 Reference< chart2::XCoordinateSystem > xCooSys;
1872 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xNewDiagram, uno::UNO_QUERY );
1873 if(xCooSysCnt.is())
1875 Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems() );
1876 if(aCooSysSeq.hasElements())
1877 xCooSys = aCooSysSeq[0];
1879 return xCooSys;
1882 Reference< chart2::XAxis > lcl_getAxis( const Reference< chart2::XCoordinateSystem >& xCooSys,
1883 enum XMLTokenEnum eDimension, bool bPrimary=true )
1885 Reference< chart2::XAxis > xNewAxis;
1888 if( xCooSys.is() )
1890 sal_Int32 nDimensionIndex=0;
1891 switch( eDimension )
1893 case XML_X:
1894 nDimensionIndex=0;
1895 break;
1896 case XML_Y:
1897 nDimensionIndex=1;
1898 break;
1899 case XML_Z:
1900 nDimensionIndex=2;
1901 break;
1902 default:
1903 break;
1906 xNewAxis = xCooSys->getAxisByDimension( nDimensionIndex, bPrimary ? 0 : 1 );
1909 catch( const uno::Exception & )
1912 return xNewAxis;
1917 void SchXMLExportHelper_Impl::exportPlotArea(
1918 const Reference< chart::XDiagram >& xDiagram,
1919 const Reference< chart2::XDiagram >& xNewDiagram,
1920 const awt::Size & rPageSize,
1921 bool bExportContent,
1922 bool bIncludeTable )
1924 SAL_WARN_IF( !xDiagram.is(), "xmloff.chart", "Invalid XDiagram as parameter" );
1925 if( ! xDiagram.is())
1926 return;
1928 // variables for autostyles
1929 Reference< beans::XPropertySet > xPropSet;
1930 std::vector< XMLPropertyState > aPropertyStates;
1932 msStringBuffer.setLength( 0 );
1934 // plot-area element
1936 std::unique_ptr<SvXMLElementExport> xElPlotArea;
1937 // get property states for autostyles
1938 xPropSet.set( xDiagram, uno::UNO_QUERY );
1939 if( xPropSet.is())
1941 if( mxExpPropMapper.is())
1942 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
1944 if( bExportContent )
1946 rtl::Reference< XMLShapeExport > rShapeExport;
1948 // write style name
1949 AddAutoStyleAttribute( aPropertyStates );
1951 if( !msChartAddress.isEmpty() )
1953 if( !bIncludeTable )
1954 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, msChartAddress );
1956 Reference< chart::XChartDocument > xDoc( mrExport.GetModel(), uno::UNO_QUERY );
1957 if( xDoc.is() )
1959 Reference< beans::XPropertySet > xDocProp( xDoc, uno::UNO_QUERY );
1960 if( xDocProp.is())
1962 Any aAny;
1966 bool bFirstCol = false, bFirstRow = false;
1968 aAny = xDocProp->getPropertyValue( "DataSourceLabelsInFirstColumn" );
1969 aAny >>= bFirstCol;
1970 aAny = xDocProp->getPropertyValue( "DataSourceLabelsInFirstRow" );
1971 aAny >>= bFirstRow;
1973 if( bFirstCol || bFirstRow )
1975 mrExport.AddAttribute( XML_NAMESPACE_CHART,
1976 ::xmloff::token::GetXMLToken( ::xmloff::token::XML_DATA_SOURCE_HAS_LABELS ),
1977 ( bFirstCol
1978 ? ( bFirstRow
1979 ? ::xmloff::token::GetXMLToken( ::xmloff::token::XML_BOTH )
1980 : ::xmloff::token::GetXMLToken( ::xmloff::token::XML_COLUMN ))
1981 : ::xmloff::token::GetXMLToken( ::xmloff::token::XML_ROW )));
1984 catch( const beans::UnknownPropertyException & )
1986 SAL_WARN("xmloff.chart", "Properties missing" );
1992 // attributes
1993 if( xDiagram.is())
1995 addPosition( xDiagram );
1996 addSize( xDiagram );
1999 bool bIs3DChart = false;
2001 if( xPropSet.is())
2003 Any aAny;
2005 // 3d attributes
2008 aAny = xPropSet->getPropertyValue("Dim3D");
2009 aAny >>= bIs3DChart;
2011 if( bIs3DChart )
2013 rShapeExport = mrExport.GetShapeExport();
2014 if( rShapeExport.is())
2015 rShapeExport->export3DSceneAttributes( xPropSet );
2018 catch( const uno::Exception & )
2020 TOOLS_INFO_EXCEPTION("xmloff.chart", "chart:exportPlotAreaException caught");
2024 // plot-area element
2025 xElPlotArea.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_PLOT_AREA, true, true ));
2027 //inner position rectangle element
2028 exportCoordinateRegion( xDiagram );
2030 // light sources (inside plot area element)
2031 if( bIs3DChart &&
2032 rShapeExport.is())
2033 rShapeExport->export3DLamps( xPropSet );
2035 else // autostyles
2037 CollectAutoStyle( std::move(aPropertyStates) );
2039 // remove property states for autostyles
2040 aPropertyStates.clear();
2042 // axis elements
2043 exportAxes( xDiagram, xNewDiagram, bExportContent );
2045 // series elements
2046 Reference< chart2::XAxis > xSecondYAxis = lcl_getAxis( lcl_getCooSys( xNewDiagram ), XML_Y, false );
2047 exportSeries( xNewDiagram, rPageSize, bExportContent, xSecondYAxis.is() );
2049 // stock-chart elements
2050 OUString sChartType ( xDiagram->getDiagramType());
2051 if( sChartType == "com.sun.star.chart.StockDiagram" )
2053 Reference< chart::XStatisticDisplay > xStockPropProvider( xDiagram, uno::UNO_QUERY );
2054 if( xStockPropProvider.is())
2056 // stock-gain-marker
2057 Reference< beans::XPropertySet > xStockPropSet = xStockPropProvider->getUpBar();
2058 if( xStockPropSet.is())
2060 aPropertyStates.clear();
2061 aPropertyStates = mxExpPropMapper->Filter(mrExport, xStockPropSet);
2063 if( !aPropertyStates.empty() )
2065 if( bExportContent )
2067 AddAutoStyleAttribute( aPropertyStates );
2069 SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_GAIN_MARKER, true, true );
2071 else
2073 CollectAutoStyle( std::move(aPropertyStates) );
2078 // stock-loss-marker
2079 xStockPropSet = xStockPropProvider->getDownBar();
2080 if( xStockPropSet.is())
2082 aPropertyStates.clear();
2083 aPropertyStates = mxExpPropMapper->Filter(mrExport, xStockPropSet);
2085 if( !aPropertyStates.empty() )
2087 if( bExportContent )
2089 AddAutoStyleAttribute( aPropertyStates );
2091 SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_LOSS_MARKER, true, true );
2093 else
2095 CollectAutoStyle( std::move(aPropertyStates) );
2100 // stock-range-line
2101 xStockPropSet = xStockPropProvider->getMinMaxLine();
2102 if( xStockPropSet.is())
2104 aPropertyStates.clear();
2105 aPropertyStates = mxExpPropMapper->Filter(mrExport, xStockPropSet);
2107 if( !aPropertyStates.empty() )
2109 if( bExportContent )
2111 AddAutoStyleAttribute( aPropertyStates );
2113 SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_RANGE_LINE, true, true );
2115 else
2117 CollectAutoStyle( std::move(aPropertyStates) );
2124 // wall and floor element
2125 Reference< chart::X3DDisplay > xWallFloorSupplier( xDiagram, uno::UNO_QUERY );
2126 if( !(mxExpPropMapper.is() &&
2127 xWallFloorSupplier.is()))
2128 return;
2130 // remove property states for autostyles
2131 aPropertyStates.clear();
2133 Reference< beans::XPropertySet > xWallPropSet = xWallFloorSupplier->getWall();
2134 if( xWallPropSet.is())
2136 aPropertyStates = mxExpPropMapper->Filter(mrExport, xWallPropSet);
2138 if( !aPropertyStates.empty() )
2140 // write element
2141 if( bExportContent )
2143 // add style name attribute
2144 AddAutoStyleAttribute( aPropertyStates );
2146 SvXMLElementExport aWall( mrExport, XML_NAMESPACE_CHART, XML_WALL, true, true );
2148 else // autostyles
2150 CollectAutoStyle( std::move(aPropertyStates) );
2155 // floor element
2156 // remove property states for autostyles
2157 aPropertyStates.clear();
2159 Reference< beans::XPropertySet > xFloorPropSet = xWallFloorSupplier->getFloor();
2160 if( !xFloorPropSet.is())
2161 return;
2163 aPropertyStates = mxExpPropMapper->Filter(mrExport, xFloorPropSet);
2165 if( aPropertyStates.empty() )
2166 return;
2168 // write element
2169 if( bExportContent )
2171 // add style name attribute
2172 AddAutoStyleAttribute( aPropertyStates );
2174 SvXMLElementExport aFloor( mrExport, XML_NAMESPACE_CHART, XML_FLOOR, true, true );
2176 else // autostyles
2178 CollectAutoStyle( std::move(aPropertyStates) );
2182 void SchXMLExportHelper_Impl::exportCoordinateRegion( const uno::Reference< chart::XDiagram >& xDiagram )
2184 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
2185 mrExport.getSaneDefaultVersion());
2186 if (nCurrentODFVersion <= SvtSaveOptions::ODFSVER_012) //do not export to ODF 1.2 or older
2187 return;
2189 Reference< chart::XDiagramPositioning > xDiaPos( xDiagram, uno::UNO_QUERY );
2190 SAL_WARN_IF( !xDiaPos.is(), "xmloff.chart", "Invalid xDiaPos as parameter" );
2191 if( !xDiaPos.is() )
2192 return;
2194 awt::Rectangle aRect( xDiaPos->calculateDiagramPositionExcludingAxes() );
2195 addPosition( awt::Point(aRect.X,aRect.Y) );
2196 addSize( awt::Size(aRect.Width,aRect.Height) );
2198 // ODF 1.3 OFFICE-3928
2199 SvXMLElementExport aCoordinateRegion( mrExport,
2200 (SvtSaveOptions::ODFSVER_013 <= nCurrentODFVersion) ? XML_NAMESPACE_CHART : XML_NAMESPACE_CHART_EXT,
2201 XML_COORDINATE_REGION, true, true );
2204 namespace
2206 XMLTokenEnum lcl_getTimeUnitToken( sal_Int32 nTimeUnit )
2208 XMLTokenEnum eToken = XML_DAYS;
2209 switch( nTimeUnit )
2211 case css::chart::TimeUnit::YEAR:
2212 eToken = XML_YEARS;
2213 break;
2214 case css::chart::TimeUnit::MONTH:
2215 eToken = XML_MONTHS;
2216 break;
2217 default://days
2218 break;
2220 return eToken;
2224 void SchXMLExportHelper_Impl::exportDateScale( const Reference< beans::XPropertySet >& rAxisProps )
2226 if( !rAxisProps.is() )
2227 return;
2229 chart::TimeIncrement aIncrement;
2230 if( !(rAxisProps->getPropertyValue("TimeIncrement") >>= aIncrement) )
2231 return;
2233 sal_Int32 nTimeResolution = css::chart::TimeUnit::DAY;
2234 if( aIncrement.TimeResolution >>= nTimeResolution )
2235 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_BASE_TIME_UNIT, lcl_getTimeUnitToken( nTimeResolution ) );
2237 chart::TimeInterval aInterval;
2238 if( aIncrement.MajorTimeInterval >>= aInterval )
2240 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MAJOR_INTERVAL_VALUE, OUString::number(aInterval.Number) );
2241 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MAJOR_INTERVAL_UNIT, lcl_getTimeUnitToken( aInterval.TimeUnit ) );
2243 if( aIncrement.MinorTimeInterval >>= aInterval )
2245 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MINOR_INTERVAL_VALUE, OUString::number(aInterval.Number) );
2246 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MINOR_INTERVAL_UNIT, lcl_getTimeUnitToken( aInterval.TimeUnit ) );
2249 SvXMLElementExport aDateScale( mrExport, XML_NAMESPACE_CHART_EXT, XML_DATE_SCALE, true, true );//#i25706#todo: change namespace for next ODF version
2252 void SchXMLExportHelper_Impl::exportAxisTitle( const Reference< beans::XPropertySet >& rTitleProps, bool bExportContent )
2254 if( !rTitleProps.is() )
2255 return;
2256 std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, rTitleProps);
2257 if( bExportContent )
2259 OUString aText;
2260 Any aAny( rTitleProps->getPropertyValue( "String" ));
2261 aAny >>= aText;
2263 Reference< drawing::XShape > xShape( rTitleProps, uno::UNO_QUERY );
2264 if( xShape.is())
2265 addPosition( xShape );
2267 AddAutoStyleAttribute( aPropertyStates );
2268 SvXMLElementExport aTitle( mrExport, XML_NAMESPACE_CHART, XML_TITLE, true, true );
2270 // paragraph containing title
2271 exportText( aText );
2273 else
2275 CollectAutoStyle( std::move(aPropertyStates) );
2277 aPropertyStates.clear();
2280 void SchXMLExportHelper_Impl::exportGrid( const Reference< beans::XPropertySet >& rGridProperties, bool bMajor, bool bExportContent )
2282 if( !rGridProperties.is() )
2283 return;
2284 std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, rGridProperties);
2285 if( bExportContent )
2287 AddAutoStyleAttribute( aPropertyStates );
2288 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, bMajor ? XML_MAJOR : XML_MINOR );
2289 SvXMLElementExport aGrid( mrExport, XML_NAMESPACE_CHART, XML_GRID, true, true );
2291 else
2293 CollectAutoStyle( std::move(aPropertyStates) );
2295 aPropertyStates.clear();
2298 namespace
2301 //returns true if a date scale needs to be exported
2302 bool lcl_exportAxisType( const Reference< chart2::XAxis >& rChart2Axis, SvXMLExport& rExport)
2304 bool bExportDateScale = false;
2305 if( !rChart2Axis.is() )
2306 return bExportDateScale;
2308 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
2309 rExport.getSaneDefaultVersion());
2310 if ((nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) == 0) //do not export to ODF 1.3 or older
2311 return bExportDateScale;
2313 chart2::ScaleData aScale( rChart2Axis->getScaleData() );
2314 //#i25706#todo: change namespace for next ODF version
2315 sal_uInt16 nNameSpace = XML_NAMESPACE_CHART_EXT;
2317 switch(aScale.AxisType)
2319 case chart2::AxisType::CATEGORY:
2320 if( aScale.AutoDateAxis )
2322 rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_AUTO );
2323 bExportDateScale = true;
2325 else
2326 rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_TEXT );
2327 break;
2328 case chart2::AxisType::DATE:
2329 rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_DATE );
2330 bExportDateScale = true;
2331 break;
2332 default: //AUTOMATIC
2333 rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_AUTO );
2334 break;
2337 return bExportDateScale;
2340 void disableLinkedNumberFormat(
2341 std::vector<XMLPropertyState>& rPropStates, const rtl::Reference<XMLPropertySetMapper>& rMapper )
2343 for (XMLPropertyState & rState : rPropStates)
2345 if (rState.mnIndex < 0 || rMapper->GetEntryCount() <= rState.mnIndex)
2346 continue;
2348 OUString aXMLName = rMapper->GetEntryXMLName(rState.mnIndex);
2350 if (aXMLName != "link-data-style-to-source")
2351 continue;
2353 // Entry found. Set the value to false and bail out.
2354 rState.maValue <<= false;
2355 return;
2358 // Entry not found. Insert a new entry for this.
2359 sal_Int32 nIndex = rMapper->GetEntryIndex(XML_NAMESPACE_CHART, u"link-data-style-to-source", 0);
2360 XMLPropertyState aState(nIndex);
2361 aState.maValue <<= false;
2362 rPropStates.push_back(aState);
2367 void SchXMLExportHelper_Impl::exportAxis(
2368 enum XMLTokenEnum eDimension,
2369 enum XMLTokenEnum eAxisName,
2370 const Reference< beans::XPropertySet >& rAxisProps,
2371 const Reference< chart2::XAxis >& rChart2Axis,
2372 const OUString& rCategoriesRange,
2373 bool bHasTitle, bool bHasMajorGrid, bool bHasMinorGrid,
2374 bool bExportContent, std::u16string_view sChartType )
2376 std::vector< XMLPropertyState > aPropertyStates;
2377 std::unique_ptr<SvXMLElementExport> pAxis;
2379 // get property states for autostyles
2380 if( rAxisProps.is() && mxExpPropMapper.is() )
2382 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
2383 mrExport.getSaneDefaultVersion());
2384 if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED
2385 && eDimension == XML_X)
2387 chart2::ScaleData aScaleData(rChart2Axis->getScaleData());
2388 bool bShiftedCatPos = aScaleData.ShiftedCategoryPosition;
2389 if (sChartType == u"com.sun.star.chart.BarDiagram" || sChartType == u"com.sun.star.chart.StockDiagram")
2391 if (!bShiftedCatPos)
2392 rAxisProps->setPropertyValue("MajorOrigin", uno::Any(0.0));
2394 else if (bShiftedCatPos)
2395 rAxisProps->setPropertyValue("MajorOrigin", uno::Any(0.5));
2398 lcl_exportNumberFormat( "NumberFormat", rAxisProps, mrExport );
2399 aPropertyStates = mxExpPropMapper->Filter(mrExport, rAxisProps);
2401 if (!maSrcShellID.isEmpty() && !maDestShellID.isEmpty() && maSrcShellID != maDestShellID)
2403 // Disable link to source number format property when pasting to
2404 // a different doc shell. These shell ID's should be both empty
2405 // during real ODF export.
2406 disableLinkedNumberFormat(aPropertyStates, mxExpPropMapper->getPropertySetMapper());
2410 bool bExportDateScale = false;
2411 if( bExportContent )
2413 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DIMENSION, eDimension );
2414 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_NAME, eAxisName );
2415 AddAutoStyleAttribute( aPropertyStates ); // write style name
2416 if( !rCategoriesRange.isEmpty() )
2417 bExportDateScale = lcl_exportAxisType( rChart2Axis, mrExport );
2419 // open axis element
2420 pAxis.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_AXIS, true, true ));
2422 else
2424 CollectAutoStyle( std::move(aPropertyStates) );
2426 aPropertyStates.clear();
2428 //date scale
2429 if( bExportDateScale )
2430 exportDateScale( rAxisProps );
2432 Reference< beans::XPropertySet > xTitleProps;
2433 Reference< beans::XPropertySet > xMajorGridProps;
2434 Reference< beans::XPropertySet > xMinorGridProps;
2435 Reference< chart::XAxis > xAxis( rAxisProps, uno::UNO_QUERY );
2436 if( xAxis.is() )
2438 xTitleProps = bHasTitle ? xAxis->getAxisTitle() : nullptr;
2439 xMajorGridProps = bHasMajorGrid ? xAxis->getMajorGrid() : nullptr;
2440 xMinorGridProps = bHasMinorGrid ? xAxis->getMinorGrid() : nullptr;
2443 // axis-title
2444 exportAxisTitle( xTitleProps , bExportContent );
2446 // categories if we have a categories chart
2447 if( bExportContent && !rCategoriesRange.isEmpty() )
2449 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, rCategoriesRange );
2450 SvXMLElementExport aCategories( mrExport, XML_NAMESPACE_CHART, XML_CATEGORIES, true, true );
2453 // grid
2454 exportGrid( xMajorGridProps, true, bExportContent );
2455 exportGrid( xMinorGridProps, false, bExportContent );
2458 void SchXMLExportHelper_Impl::exportAxes(
2459 const Reference< chart::XDiagram > & xDiagram,
2460 const Reference< chart2::XDiagram > & xNewDiagram,
2461 bool bExportContent )
2463 SAL_WARN_IF( !xDiagram.is(), "xmloff.chart", "Invalid XDiagram as parameter" );
2464 if( ! xDiagram.is())
2465 return;
2467 // get some properties from document first
2468 bool bHasXAxis = false,
2469 bHasYAxis = false,
2470 bHasZAxis = false,
2471 bHasSecondaryXAxis = false,
2472 bHasSecondaryYAxis = false;
2473 bool bHasXAxisTitle = false,
2474 bHasYAxisTitle = false,
2475 bHasZAxisTitle = false,
2476 bHasSecondaryXAxisTitle = false,
2477 bHasSecondaryYAxisTitle = false;
2478 bool bHasXAxisMajorGrid = false,
2479 bHasXAxisMinorGrid = false,
2480 bHasYAxisMajorGrid = false,
2481 bHasYAxisMinorGrid = false,
2482 bHasZAxisMajorGrid = false,
2483 bHasZAxisMinorGrid = false;
2485 // get multiple properties using XMultiPropertySet
2486 MultiPropertySetHandler aDiagramProperties (xDiagram);
2488 aDiagramProperties.Add ("HasXAxis", bHasXAxis);
2489 aDiagramProperties.Add ("HasYAxis", bHasYAxis);
2490 aDiagramProperties.Add ("HasZAxis", bHasZAxis);
2491 aDiagramProperties.Add ("HasSecondaryXAxis", bHasSecondaryXAxis);
2492 aDiagramProperties.Add ("HasSecondaryYAxis", bHasSecondaryYAxis);
2494 aDiagramProperties.Add ("HasXAxisTitle", bHasXAxisTitle);
2495 aDiagramProperties.Add ("HasYAxisTitle", bHasYAxisTitle);
2496 aDiagramProperties.Add ("HasZAxisTitle", bHasZAxisTitle);
2497 aDiagramProperties.Add ("HasSecondaryXAxisTitle", bHasSecondaryXAxisTitle);
2498 aDiagramProperties.Add ("HasSecondaryYAxisTitle", bHasSecondaryYAxisTitle);
2500 aDiagramProperties.Add ("HasXAxisGrid", bHasXAxisMajorGrid);
2501 aDiagramProperties.Add ("HasYAxisGrid", bHasYAxisMajorGrid);
2502 aDiagramProperties.Add ("HasZAxisGrid", bHasZAxisMajorGrid);
2504 aDiagramProperties.Add ("HasXAxisHelpGrid", bHasXAxisMinorGrid);
2505 aDiagramProperties.Add ("HasYAxisHelpGrid", bHasYAxisMinorGrid);
2506 aDiagramProperties.Add ("HasZAxisHelpGrid", bHasZAxisMinorGrid);
2508 if ( ! aDiagramProperties.GetProperties ())
2510 SAL_INFO("xmloff.chart", "Required properties not found in Chart diagram");
2513 Reference< chart2::XCoordinateSystem > xCooSys( lcl_getCooSys(xNewDiagram) );
2515 // write an axis element also if the axis itself is not visible, but a grid or a title
2517 OUString aCategoriesRange;
2518 Reference< chart::XAxisSupplier > xAxisSupp( xDiagram, uno::UNO_QUERY );
2519 OUString sChartType = xDiagram->getDiagramType();
2521 // x axis
2523 Reference< css::chart2::XAxis > xNewAxis = lcl_getAxis( xCooSys, XML_X );
2524 if( xNewAxis.is() )
2526 Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getAxis(0) : nullptr, uno::UNO_QUERY );
2527 if( mbHasCategoryLabels && bExportContent )
2529 Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xNewDiagram ) );
2530 if( xCategories.is() )
2532 Reference< chart2::data::XDataSequence > xValues( xCategories->getValues() );
2533 if( xValues.is() )
2535 Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
2536 maCategoriesRange = xValues->getSourceRangeRepresentation();
2537 aCategoriesRange = lcl_ConvertRange( maCategoriesRange, xNewDoc );
2541 exportAxis( XML_X, XML_PRIMARY_X, xAxisProps, xNewAxis, aCategoriesRange, bHasXAxisTitle, bHasXAxisMajorGrid, bHasXAxisMinorGrid, bExportContent, sChartType );
2542 aCategoriesRange.clear();
2545 // secondary x axis
2547 xNewAxis = lcl_getAxis( xCooSys, XML_X, false );
2548 if( xNewAxis.is() )
2550 Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getSecondaryAxis(0) : nullptr, uno::UNO_QUERY );
2551 exportAxis( XML_X, XML_SECONDARY_X, xAxisProps, xNewAxis, aCategoriesRange, bHasSecondaryXAxisTitle, false, false, bExportContent, sChartType );
2554 // y axis
2556 xNewAxis = lcl_getAxis( xCooSys, XML_Y );
2557 if( xNewAxis.is() )
2559 Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getAxis(1) : nullptr, uno::UNO_QUERY );
2560 exportAxis( XML_Y, XML_PRIMARY_Y, xAxisProps, xNewAxis, aCategoriesRange, bHasYAxisTitle, bHasYAxisMajorGrid, bHasYAxisMinorGrid, bExportContent, sChartType );
2563 // secondary y axis
2565 xNewAxis = lcl_getAxis( xCooSys, XML_Y, false );
2566 if( xNewAxis.is() )
2568 Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getSecondaryAxis(1) : nullptr, uno::UNO_QUERY );
2569 exportAxis( XML_Y, XML_SECONDARY_Y, xAxisProps, xNewAxis, aCategoriesRange, bHasSecondaryYAxisTitle, false, false, bExportContent, sChartType );
2572 // z axis
2574 xNewAxis = lcl_getAxis( xCooSys, XML_Z );
2575 if( xNewAxis.is() )
2577 Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getAxis(2) : nullptr, uno::UNO_QUERY );
2578 exportAxis( XML_Z, XML_PRIMARY_Z, xAxisProps, xNewAxis, aCategoriesRange, bHasZAxisTitle, bHasZAxisMajorGrid, bHasZAxisMinorGrid, bExportContent, sChartType );
2582 namespace
2584 bool lcl_hasNoValuesButText( const uno::Reference< chart2::data::XDataSequence >& xDataSequence )
2586 if( !xDataSequence.is() )
2587 return false;//have no data
2589 Sequence< uno::Any > aData;
2590 Reference< chart2::data::XNumericalDataSequence > xNumericalDataSequence( xDataSequence, uno::UNO_QUERY );
2591 if( xNumericalDataSequence.is() )
2593 const Sequence< double > aDoubles( xNumericalDataSequence->getNumericalData() );
2594 if (std::any_of(aDoubles.begin(), aDoubles.end(), [](double fDouble) { return !std::isnan( fDouble ); }))
2595 return false;//have double value
2597 else
2599 aData = xDataSequence->getData();
2600 double fDouble = 0.0;
2601 bool bHaveDouble = std::any_of(std::cbegin(aData), std::cend(aData),
2602 [&fDouble](const uno::Any& rData) { return (rData >>= fDouble) && !std::isnan( fDouble ); });
2603 if (bHaveDouble)
2604 return false;//have double value
2606 //no values found
2608 Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xDataSequence, uno::UNO_QUERY );
2609 if( xTextualDataSequence.is() )
2611 const uno::Sequence< OUString > aStrings( xTextualDataSequence->getTextualData() );
2612 if (std::any_of(aStrings.begin(), aStrings.end(), [](const OUString& rString) { return !rString.isEmpty(); }))
2613 return true;//have text
2615 else
2617 if( !aData.hasElements() )
2618 aData = xDataSequence->getData();
2619 OUString aString;
2620 bool bHaveText = std::any_of(std::cbegin(aData), std::cend(aData),
2621 [&aString](const uno::Any& rData) { return (rData >>= aString) && !aString.isEmpty(); });
2622 if (bHaveText)
2623 return true;//have text
2625 //no doubles and no texts
2626 return false;
2629 // ODF has the line and fill properties in a <style:style> element, which is referenced by the
2630 // <chart:data-label> element. But LibreOffice has them as special label properties of the series
2631 // or point respectively. The following method generates ODF from internal API name.
2632 void lcl_createDataLabelProperties(
2633 std::vector<XMLPropertyState>& rDataLabelPropertyStates,
2634 const Reference<beans::XPropertySet>& xPropSet,
2635 const rtl::Reference<XMLChartExportPropertyMapper>& xExpPropMapper)
2637 if (!xExpPropMapper.is() || !xPropSet.is())
2638 return;
2640 const uno::Reference<beans::XPropertySetInfo> xInfo(xPropSet->getPropertySetInfo());
2641 const uno::Reference<beans::XPropertyState> xPropState(xPropSet, uno::UNO_QUERY);
2642 const rtl::Reference<XMLPropertySetMapper>& rPropertySetMapper(
2643 xExpPropMapper->getPropertySetMapper());
2644 if (!xInfo.is() || !xPropState.is() || !rPropertySetMapper.is())
2645 return;
2647 struct API2ODFMapItem
2649 OUString sAPIName;
2650 sal_uInt16 nNameSpace; // from include/xmloff/xmlnamespace.hxx
2651 OUString sLocalName;
2652 API2ODFMapItem(OUString sAPI, const sal_uInt16 nNS, OUString sLocal)
2653 : sAPIName(std::move(sAPI))
2654 , nNameSpace(nNS)
2655 , sLocalName(std::move(sLocal))
2660 const API2ODFMapItem aLabelFoo2ODFArray[]
2661 = { API2ODFMapItem("LabelBorderStyle", XML_NAMESPACE_DRAW, "stroke"),
2662 API2ODFMapItem("LabelBorderWidth", XML_NAMESPACE_SVG, "stroke-width"),
2663 API2ODFMapItem("LabelBorderColor", XML_NAMESPACE_SVG, "stroke-color"),
2664 API2ODFMapItem("LabelBorderDashName", XML_NAMESPACE_DRAW, "stroke-dash"),
2665 API2ODFMapItem("LabelBorderTransparency", XML_NAMESPACE_SVG, "stroke-opacity"),
2666 API2ODFMapItem("LabelFillStyle", XML_NAMESPACE_DRAW, "fill"),
2667 API2ODFMapItem("LabelFillBackground", XML_NAMESPACE_DRAW, "fill-hatch-solid"),
2668 API2ODFMapItem("LabelFillHatchName", XML_NAMESPACE_DRAW, "fill-hatch-name"),
2669 API2ODFMapItem("LabelFillColor", XML_NAMESPACE_DRAW, "fill-color") };
2671 for (const auto& rIt : aLabelFoo2ODFArray)
2673 if (!xInfo->hasPropertyByName(rIt.sAPIName)
2674 || xPropState->getPropertyState(rIt.sAPIName) != beans::PropertyState_DIRECT_VALUE)
2675 continue;
2676 sal_Int32 nTargetIndex
2677 = rPropertySetMapper->GetEntryIndex(rIt.nNameSpace, rIt.sLocalName, 0);
2678 if (nTargetIndex < 0)
2679 continue;
2680 XMLPropertyState aDataLabelStateItem(nTargetIndex,
2681 xPropSet->getPropertyValue(rIt.sAPIName));
2682 rDataLabelPropertyStates.emplace_back(aDataLabelStateItem);
2685 } // anonymous namespace
2687 void SchXMLExportHelper_Impl::exportSeries(
2688 const Reference< chart2::XDiagram > & xNewDiagram,
2689 const awt::Size & rPageSize,
2690 bool bExportContent,
2691 bool bHasTwoYAxes )
2693 Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( xNewDiagram, uno::UNO_QUERY );
2694 if( ! xBCooSysCnt.is())
2695 return;
2696 Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
2698 OUString aFirstXDomainRange;
2699 OUString aFirstYDomainRange;
2701 std::vector< XMLPropertyState > aPropertyStates;
2702 std::vector< XMLPropertyState > aDataLabelPropertyStates;
2704 const Sequence< Reference< chart2::XCoordinateSystem > >
2705 aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
2706 for( const auto& rCooSys : aCooSysSeq )
2708 Reference< chart2::XChartTypeContainer > xCTCnt( rCooSys, uno::UNO_QUERY );
2709 if( ! xCTCnt.is())
2710 continue;
2711 const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
2712 for( const auto& rChartType : aCTSeq )
2714 Reference< chart2::XDataSeriesContainer > xDSCnt( rChartType, uno::UNO_QUERY );
2715 if( ! xDSCnt.is())
2716 continue;
2717 // note: if xDSCnt.is() then also aCTSeq[nCTIdx]
2718 OUString aChartType( rChartType->getChartType());
2719 OUString aLabelRole = rChartType->getRoleOfSequenceForSeriesLabel();
2721 // special export for stock charts
2722 if ( aChartType == "com.sun.star.chart2.CandleStickChartType" )
2724 bool bJapaneseCandleSticks = false;
2725 Reference< beans::XPropertySet > xCTProp( rChartType, uno::UNO_QUERY );
2726 if( xCTProp.is())
2727 xCTProp->getPropertyValue("Japanese") >>= bJapaneseCandleSticks;
2728 exportCandleStickSeries(
2729 xDSCnt->getDataSeries(), xNewDiagram, bJapaneseCandleSticks, bExportContent );
2730 continue;
2733 // export dataseries for current chart-type
2734 Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries());
2735 for( sal_Int32 nSeriesIdx=0; nSeriesIdx<aSeriesSeq.getLength(); ++nSeriesIdx )
2737 // export series
2738 Reference< chart2::data::XDataSource > xSource( aSeriesSeq[nSeriesIdx], uno::UNO_QUERY );
2739 if( xSource.is())
2741 std::unique_ptr<SvXMLElementExport> pSeries;
2742 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
2743 xSource->getDataSequences());
2744 sal_Int32 nMainSequenceIndex = -1;
2745 sal_Int32 nSeriesLength = 0;
2746 bool bHasMeanValueLine = false;
2747 Reference< beans::XPropertySet > xPropSet;
2748 tLabelValuesDataPair aSeriesLabelValuesPair;
2750 // search for main sequence and create a series element
2752 Reference< chart2::data::XDataSequence > xValuesSeq;
2753 Reference< chart2::data::XDataSequence > xLabelSeq;
2754 sal_Int32 nSeqIdx=0;
2755 for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx )
2757 Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() );
2758 if( nMainSequenceIndex==-1 )
2760 OUString aRole;
2761 Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY );
2762 if( xSeqProp.is())
2763 xSeqProp->getPropertyValue("Role") >>= aRole;
2764 // "main" sequence
2765 if( aRole == aLabelRole )
2767 xValuesSeq.set( xTempValueSeq );
2768 xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel());
2769 nMainSequenceIndex = nSeqIdx;
2772 sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0));
2773 if( nSeriesLength < nSequenceLength )
2774 nSeriesLength = nSequenceLength;
2777 // have found the main sequence, then xValuesSeq and
2778 // xLabelSeq contain those. Otherwise both are empty
2780 sal_Int32 nAttachedAxis = chart::ChartAxisAssign::PRIMARY_Y;
2781 // get property states for autostyles
2784 xPropSet = SchXMLSeriesHelper::createOldAPISeriesPropertySet(
2785 aSeriesSeq[nSeriesIdx], mrExport.GetModel() );
2787 catch( const uno::Exception & )
2789 TOOLS_INFO_EXCEPTION("xmloff.chart", "Series not found or no XPropertySet" );
2790 continue;
2792 if( xPropSet.is())
2794 // determine attached axis
2797 Any aAny( xPropSet->getPropertyValue( "Axis" ));
2798 aAny >>= nAttachedAxis;
2800 aAny = xPropSet->getPropertyValue( "MeanValue" );
2801 aAny >>= bHasMeanValueLine;
2803 catch( const beans::UnknownPropertyException & )
2805 TOOLS_INFO_EXCEPTION("xmloff.chart", "Required property not found in DataRowProperties" );
2808 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
2809 mrExport.getSaneDefaultVersion());
2810 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
2812 lcl_exportNumberFormat( "NumberFormat", xPropSet, mrExport );
2813 lcl_exportNumberFormat( "PercentageNumberFormat", xPropSet, mrExport );
2816 if( mxExpPropMapper.is())
2817 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
2820 if( bExportContent )
2822 if( bHasTwoYAxes )
2824 if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
2825 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
2826 else
2827 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
2830 // write style name
2831 AddAutoStyleAttribute( aPropertyStates );
2833 if( xValuesSeq.is())
2834 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS,
2835 lcl_ConvertRange(
2836 xValuesSeq->getSourceRangeRepresentation(),
2837 xNewDoc ));
2838 else
2839 // #i75297# allow empty series, export empty range to have all ranges on import
2840 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, OUString());
2842 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
2843 mrExport.getSaneDefaultVersion());
2844 if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older
2846 if (xPropSet.is())
2848 Any aAny = xPropSet->getPropertyValue("ShowLegendEntry");
2849 if (!aAny.get<bool>())
2851 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDE_LEGEND, OUString::boolean(true));
2856 if (xLabelSeq.is())
2858 // Check if the label is direct string value rather than a reference.
2859 bool bHasString = false;
2860 uno::Reference<beans::XPropertySet> xLSProp(xLabelSeq, uno::UNO_QUERY);
2861 if (xLSProp.is())
2865 xLSProp->getPropertyValue("HasStringLabel") >>= bHasString;
2867 catch (const beans::UnknownPropertyException&) {}
2870 OUString aRange = xLabelSeq->getSourceRangeRepresentation();
2872 if (bHasString)
2874 mrExport.AddAttribute(
2875 XML_NAMESPACE_LO_EXT, XML_LABEL_STRING, aRange);
2877 else
2879 mrExport.AddAttribute(
2880 XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS,
2881 lcl_ConvertRange(
2882 xLabelSeq->getSourceRangeRepresentation(), xNewDoc));
2886 if( xLabelSeq.is() || xValuesSeq.is() )
2887 aSeriesLabelValuesPair = tLabelValuesDataPair( xLabelSeq, xValuesSeq );
2889 // chart-type for mixed types
2890 enum XMLTokenEnum eCTToken(
2891 SchXMLTools::getTokenByChartType( aChartType, false /* bUseOldNames */ ));
2892 //@todo: get token for current charttype
2893 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS,
2894 mrExport.GetNamespaceMap().GetQNameByKey(
2895 XML_NAMESPACE_CHART, GetXMLToken( eCTToken )));
2897 // open series element until end of for loop
2898 pSeries.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true ));
2900 else // autostyles
2902 CollectAutoStyle( std::move(aPropertyStates) );
2904 // remove property states for autostyles
2905 aPropertyStates.clear();
2909 // export domain elements if we have a series parent element
2910 if( pSeries )
2912 // domain elements
2913 if( bExportContent )
2915 bool bIsScatterChart = aChartType == "com.sun.star.chart2.ScatterChartType";
2916 bool bIsBubbleChart = aChartType == "com.sun.star.chart2.BubbleChartType";
2917 Reference< chart2::data::XDataSequence > xYValuesForBubbleChart;
2918 if( bIsBubbleChart )
2920 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, "values-y" ) );
2921 if( xSequence.is() )
2923 xYValuesForBubbleChart = xSequence->getValues();
2924 if( !lcl_exportDomainForThisSequence( xYValuesForBubbleChart, aFirstYDomainRange, mrExport ) )
2925 xYValuesForBubbleChart = nullptr;
2928 if( bIsScatterChart || bIsBubbleChart )
2930 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, "values-x" ) );
2931 if( xSequence.is() )
2933 Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
2934 if( lcl_exportDomainForThisSequence( xValues, aFirstXDomainRange, mrExport ) )
2935 m_aDataSequencesToExport.emplace_back(
2936 uno::Reference< chart2::data::XDataSequence >(), xValues );
2938 else if( nSeriesIdx==0 )
2940 //might be that the categories are used as x-values (e.g. for date axis) -> export them accordingly
2941 Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xNewDiagram ) );
2942 if( xCategories.is() )
2944 Reference< chart2::data::XDataSequence > xValues( xCategories->getValues() );
2945 if( !lcl_hasNoValuesButText( xValues ) )
2946 lcl_exportDomainForThisSequence( xValues, aFirstXDomainRange, mrExport );
2950 if( xYValuesForBubbleChart.is() )
2951 m_aDataSequencesToExport.emplace_back(
2952 uno::Reference< chart2::data::XDataSequence >(), xYValuesForBubbleChart );
2956 // add sequences for main sequence after domain sequences,
2957 // so that the export of the local table has the correct order
2958 if( bExportContent &&
2959 (aSeriesLabelValuesPair.first.is() || aSeriesLabelValuesPair.second.is()))
2960 m_aDataSequencesToExport.push_back( aSeriesLabelValuesPair );
2962 // statistical objects:
2963 // regression curves and mean value lines
2964 if( bHasMeanValueLine &&
2965 xPropSet.is() &&
2966 mxExpPropMapper.is() )
2968 Reference< beans::XPropertySet > xStatProp;
2971 Any aPropAny( xPropSet->getPropertyValue( "DataMeanValueProperties" ));
2972 aPropAny >>= xStatProp;
2974 catch( const uno::Exception & )
2976 TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during Export of series - optional DataMeanValueProperties not available" );
2979 if( xStatProp.is() )
2981 aPropertyStates = mxExpPropMapper->Filter(mrExport, xStatProp);
2983 if( !aPropertyStates.empty() )
2985 // write element
2986 if( bExportContent )
2988 // add style name attribute
2989 AddAutoStyleAttribute( aPropertyStates );
2991 SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_MEAN_VALUE, true, true );
2993 else // autostyles
2995 CollectAutoStyle( std::move(aPropertyStates) );
3001 if( xPropSet.is() &&
3002 mxExpPropMapper.is() )
3004 exportRegressionCurve( aSeriesSeq[nSeriesIdx], rPageSize, bExportContent );
3007 exportErrorBar( xPropSet,false, bExportContent ); // X ErrorBar
3008 exportErrorBar( xPropSet,true, bExportContent ); // Y ErrorBar
3010 exportDataPoints(
3011 uno::Reference< beans::XPropertySet >( aSeriesSeq[nSeriesIdx], uno::UNO_QUERY ),
3012 nSeriesLength, xNewDiagram, bExportContent );
3014 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
3015 mrExport.getSaneDefaultVersion());
3017 // create <chart:data-label> child element if needed.
3018 if (xPropSet.is() && mxExpPropMapper.is())
3020 // Generate style for <chart:data-label> child element
3021 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
3023 lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet,
3024 mxExpPropMapper);
3027 if (bExportContent)
3029 if (!aDataLabelPropertyStates.empty())
3031 // write style name
3032 AddAutoStyleAttribute(aDataLabelPropertyStates);
3033 // Further content does currently not exist for a <chart:data-label>
3034 // element as child of a <chart:series>.
3035 SvXMLElementExport(mrExport, XML_NAMESPACE_CHART, XML_DATA_LABEL, true,
3036 true);
3039 else
3041 // add the style for the to be <chart:data-label> too
3042 if (!aDataLabelPropertyStates.empty())
3043 CollectAutoStyle(std::move(aDataLabelPropertyStates));
3045 aDataLabelPropertyStates.clear();
3047 if (bExportContent && nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older
3049 Sequence< OUString > aSupportedMappings = rChartType->getSupportedPropertyRoles();
3050 exportPropertyMapping( xSource, aSupportedMappings );
3053 // close series element
3054 pSeries.reset();
3057 aPropertyStates.clear();
3058 aDataLabelPropertyStates.clear();
3063 void SchXMLExportHelper_Impl::exportPropertyMapping(
3064 const Reference< chart2::data::XDataSource > & xSource, const Sequence< OUString >& rSupportedMappings )
3066 Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
3067 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
3068 xSource->getDataSequences());
3070 for(const auto& rSupportedMapping : rSupportedMappings)
3072 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, rSupportedMapping ) );
3073 if(xSequence.is())
3075 Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
3076 if( xValues.is())
3078 mrExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_PROPERTY, rSupportedMapping);
3079 mrExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_CELL_RANGE_ADDRESS,
3080 lcl_ConvertRange(
3081 xValues->getSourceRangeRepresentation(),
3082 xNewDoc ));
3083 SvXMLElementExport( mrExport, XML_NAMESPACE_LO_EXT, XML_PROPERTY_MAPPING, true, true );
3085 // register range for data table export
3086 m_aDataSequencesToExport.emplace_back(
3087 uno::Reference< chart2::data::XDataSequence >(), xValues );
3093 void SchXMLExportHelper_Impl::exportRegressionCurve(
3094 const Reference< chart2::XDataSeries >& xSeries,
3095 const awt::Size& rPageSize,
3096 bool bExportContent )
3098 OSL_ASSERT( mxExpPropMapper.is());
3100 Reference< chart2::XRegressionCurveContainer > xRegressionCurveContainer( xSeries, uno::UNO_QUERY );
3101 if( !xRegressionCurveContainer.is() )
3102 return;
3104 const Sequence< Reference< chart2::XRegressionCurve > > aRegCurveSeq = xRegressionCurveContainer->getRegressionCurves();
3106 for( const auto& xRegCurve : aRegCurveSeq )
3108 std::vector< XMLPropertyState > aEquationPropertyStates;
3109 if (!xRegCurve.is())
3110 continue;
3112 Reference< beans::XPropertySet > xProperties( xRegCurve , uno::UNO_QUERY );
3113 if( !xProperties.is() )
3114 continue;
3116 Reference< lang::XServiceName > xServiceName( xProperties, uno::UNO_QUERY );
3117 if( !xServiceName.is() )
3118 continue;
3120 bool bShowEquation = false;
3121 bool bShowRSquared = false;
3122 bool bExportEquation = false;
3124 OUString aService = xServiceName->getServiceName();
3126 std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, xProperties);
3128 // Add service name (which is regression type)
3129 sal_Int32 nIndex = GetPropertySetMapper()->FindEntryIndex(XML_SCH_CONTEXT_SPECIAL_REGRESSION_TYPE);
3130 XMLPropertyState property(nIndex, uno::Any(aService));
3131 aPropertyStates.push_back(property);
3133 Reference< beans::XPropertySet > xEquationProperties;
3134 xEquationProperties.set( xRegCurve->getEquationProperties() );
3135 if( xEquationProperties.is())
3137 xEquationProperties->getPropertyValue( "ShowEquation") >>= bShowEquation;
3138 xEquationProperties->getPropertyValue( "ShowCorrelationCoefficient") >>= bShowRSquared;
3140 bExportEquation = ( bShowEquation || bShowRSquared );
3141 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion(
3142 mrExport.getSaneDefaultVersion());
3143 if (nCurrentVersion < SvtSaveOptions::ODFSVER_012)
3145 bExportEquation=false;
3147 if( bExportEquation )
3149 // number format
3150 sal_Int32 nNumberFormat = 0;
3151 if( (xEquationProperties->getPropertyValue("NumberFormat") >>= nNumberFormat ) &&
3152 nNumberFormat != -1 )
3154 mrExport.addDataStyle( nNumberFormat );
3156 aEquationPropertyStates = mxExpPropMapper->Filter(mrExport, xEquationProperties);
3160 if( !aPropertyStates.empty() || bExportEquation )
3162 // write element
3163 if( bExportContent )
3165 // add style name attribute
3166 if( !aPropertyStates.empty())
3168 AddAutoStyleAttribute( aPropertyStates );
3171 SvXMLElementExport aRegressionExport( mrExport, XML_NAMESPACE_CHART, XML_REGRESSION_CURVE, true, true );
3172 if( bExportEquation )
3174 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DISPLAY_EQUATION, (bShowEquation ? XML_TRUE : XML_FALSE) );
3175 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DISPLAY_R_SQUARE, (bShowRSquared ? XML_TRUE : XML_FALSE) );
3177 // export position
3178 chart2::RelativePosition aRelativePosition;
3179 if( xEquationProperties->getPropertyValue( "RelativePosition" ) >>= aRelativePosition )
3181 double fX = aRelativePosition.Primary * rPageSize.Width;
3182 double fY = aRelativePosition.Secondary * rPageSize.Height;
3183 awt::Point aPos;
3184 aPos.X = static_cast< sal_Int32 >( ::rtl::math::round( fX ));
3185 aPos.Y = static_cast< sal_Int32 >( ::rtl::math::round( fY ));
3186 addPosition( aPos );
3189 if( !aEquationPropertyStates.empty())
3191 AddAutoStyleAttribute( aEquationPropertyStates );
3194 SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_EQUATION, true, true );
3197 else // autostyles
3199 if( !aPropertyStates.empty())
3201 CollectAutoStyle( std::move(aPropertyStates) );
3203 if( bExportEquation && !aEquationPropertyStates.empty())
3205 CollectAutoStyle( std::move(aEquationPropertyStates) );
3212 void SchXMLExportHelper_Impl::exportErrorBar( const Reference<beans::XPropertySet> &xSeriesProp,
3213 bool bYError, bool bExportContent )
3215 assert(mxExpPropMapper.is());
3217 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion(
3218 mrExport.getSaneDefaultVersion());
3220 /// Don't export X ErrorBars for older ODF versions.
3221 if (!bYError && nCurrentVersion < SvtSaveOptions::ODFSVER_012)
3222 return;
3224 if (!xSeriesProp.is())
3225 return;
3227 bool bNegative = false, bPositive = false;
3228 sal_Int32 nErrorBarStyle = chart::ErrorBarStyle::NONE;
3229 Reference< beans::XPropertySet > xErrorBarProp;
3233 Any aAny = xSeriesProp->getPropertyValue( bYError ? OUString("ErrorBarY") : OUString("ErrorBarX") );
3234 aAny >>= xErrorBarProp;
3236 if ( xErrorBarProp.is() )
3238 aAny = xErrorBarProp->getPropertyValue("ShowNegativeError" );
3239 aAny >>= bNegative;
3241 aAny = xErrorBarProp->getPropertyValue("ShowPositiveError" );
3242 aAny >>= bPositive;
3244 aAny = xErrorBarProp->getPropertyValue("ErrorBarStyle" );
3245 aAny >>= nErrorBarStyle;
3248 catch( const beans::UnknownPropertyException & )
3250 TOOLS_INFO_EXCEPTION("xmloff.chart", "Required property not found in DataRowProperties" );
3253 if( !(nErrorBarStyle != chart::ErrorBarStyle::NONE && (bNegative || bPositive)))
3254 return;
3256 if( bExportContent && nErrorBarStyle == chart::ErrorBarStyle::FROM_DATA )
3258 // register data ranges for error bars for export in local table
3259 ::std::vector< Reference< chart2::data::XDataSequence > > aErrorBarSequences(
3260 lcl_getErrorBarSequences( xErrorBarProp ));
3261 for( const auto& rErrorBarSequence : aErrorBarSequences )
3263 m_aDataSequencesToExport.emplace_back(
3264 uno::Reference< chart2::data::XDataSequence >(), rErrorBarSequence );
3268 std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, xErrorBarProp);
3270 if( aPropertyStates.empty() )
3271 return;
3273 // write element
3274 if( bExportContent )
3276 // add style name attribute
3277 AddAutoStyleAttribute( aPropertyStates );
3279 if (nCurrentVersion >= SvtSaveOptions::ODFSVER_012)
3280 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DIMENSION, bYError ? XML_Y : XML_X );//#i114149#
3281 SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_ERROR_INDICATOR, true, true );
3283 else // autostyles
3285 CollectAutoStyle( std::move(aPropertyStates) );
3289 void SchXMLExportHelper_Impl::exportCandleStickSeries(
3290 const Sequence< Reference< chart2::XDataSeries > > & aSeriesSeq,
3291 const Reference< chart2::XDiagram > & xDiagram,
3292 bool bJapaneseCandleSticks,
3293 bool bExportContent )
3296 for( const auto& xSeries : aSeriesSeq )
3298 sal_Int32 nAttachedAxis = lcl_isSeriesAttachedToFirstAxis( xSeries )
3299 ? chart::ChartAxisAssign::PRIMARY_Y
3300 : chart::ChartAxisAssign::SECONDARY_Y;
3302 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
3303 if( xSource.is())
3305 // export series in correct order (as we don't store roles)
3306 // with japanese candlesticks: open, low, high, close
3307 // otherwise: low, high, close
3308 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
3309 xSource->getDataSequences());
3311 sal_Int32 nSeriesLength =
3312 lcl_getSequenceLengthByRole( aSeqCnt, "values-last");
3314 if( bExportContent )
3316 Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
3317 //@todo: export data points
3319 //TODO: moggi: same code three times
3320 // open
3321 if( bJapaneseCandleSticks )
3323 tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole(
3324 aSeqCnt, "values-first", xNewDoc, m_aDataSequencesToExport ));
3325 if( !aRanges.second.isEmpty())
3326 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second );
3327 if( !aRanges.first.isEmpty())
3328 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first );
3329 if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
3330 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
3331 else
3332 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
3333 SvXMLElementExport aOpenSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true );
3334 // export empty data points
3335 exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent );
3338 // low
3340 tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole(
3341 aSeqCnt, "values-min", xNewDoc, m_aDataSequencesToExport ));
3342 if( !aRanges.second.isEmpty())
3343 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second );
3344 if( !aRanges.first.isEmpty())
3345 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first );
3346 if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
3347 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
3348 else
3349 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
3350 SvXMLElementExport aLowSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true );
3351 // export empty data points
3352 exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent );
3355 // high
3357 tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole(
3358 aSeqCnt, "values-max", xNewDoc, m_aDataSequencesToExport ));
3359 if( !aRanges.second.isEmpty())
3360 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second );
3361 if( !aRanges.first.isEmpty())
3362 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first );
3363 if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
3364 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
3365 else
3366 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
3367 SvXMLElementExport aHighSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true );
3368 // export empty data points
3369 exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent );
3372 // close
3374 tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole(
3375 aSeqCnt, "values-last", xNewDoc, m_aDataSequencesToExport ));
3376 if( !aRanges.second.isEmpty())
3377 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second );
3378 if( !aRanges.first.isEmpty())
3379 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first );
3380 if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
3381 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
3382 else
3383 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
3384 SvXMLElementExport aCloseSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true );
3385 // export empty data points
3386 exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent );
3389 else // autostyles
3391 // for close series
3393 // remove property states for autostyles
3398 void SchXMLExportHelper_Impl::exportDataPoints(
3399 const uno::Reference< beans::XPropertySet > & xSeriesProperties,
3400 sal_Int32 nSeriesLength,
3401 const uno::Reference< chart2::XDiagram > & xDiagram,
3402 bool bExportContent )
3404 // data-points
3406 // write data-points only if they contain autostyles
3407 // objects with equal autostyles are grouped using the attribute
3408 // repeat="number"
3410 // Note: if only the nth data-point has autostyles there is an element
3411 // without style and repeat="n-1" attribute written in advance.
3413 // the sequence aDataPointSeq contains indices of data-points that
3414 // do have own attributes. This increases the performance substantially.
3416 // more performant version for #93600#
3417 if (!mxExpPropMapper.is())
3418 return;
3420 uno::Reference< chart2::XDataSeries > xSeries( xSeriesProperties, uno::UNO_QUERY );
3422 std::vector< XMLPropertyState > aPropertyStates;
3423 std::vector<XMLPropertyState> aDataLabelPropertyStates;
3425 bool bVaryColorsByPoint = false;
3426 Sequence< sal_Int32 > aDataPointSeq;
3427 Sequence<sal_Int32> deletedLegendEntriesSeq;
3428 if( xSeriesProperties.is())
3430 xSeriesProperties->getPropertyValue("AttributedDataPoints") >>= aDataPointSeq;
3431 xSeriesProperties->getPropertyValue("VaryColorsByPoint") >>= bVaryColorsByPoint;
3433 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
3434 mrExport.getSaneDefaultVersion());
3435 if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older
3436 xSeriesProperties->getPropertyValue("DeletedLegendEntries") >>= deletedLegendEntriesSeq;
3439 sal_Int32 nSize = aDataPointSeq.getLength();
3440 SAL_WARN_IF( nSize > nSeriesLength, "xmloff.chart", "Too many point attributes" );
3442 const sal_Int32 * pPoints = aDataPointSeq.getConstArray();
3443 sal_Int32 nElement;
3444 Reference< chart2::XColorScheme > xColorScheme;
3445 if( xDiagram.is())
3446 xColorScheme.set( xDiagram->getDefaultColorScheme());
3448 ::std::vector< SchXMLDataPointStruct > aDataPointVector;
3450 sal_Int32 nLastIndex = -1;
3452 // collect elements
3453 if( bVaryColorsByPoint && xColorScheme.is() )
3455 o3tl::sorted_vector< sal_Int32 > aAttrPointSet;
3456 aAttrPointSet.reserve(aDataPointSeq.getLength());
3457 for (auto p = pPoints; p < pPoints + aDataPointSeq.getLength(); ++p)
3458 aAttrPointSet.insert( *p );
3459 const auto aEndIt = aAttrPointSet.end();
3460 for( nElement = 0; nElement < nSeriesLength; ++nElement )
3462 aPropertyStates.clear();
3463 aDataLabelPropertyStates.clear();
3464 uno::Reference< beans::XPropertySet > xPropSet;
3465 bool bExportNumFmt = false;
3466 if( aAttrPointSet.find( nElement ) != aEndIt )
3470 xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
3471 xSeries, nElement, mrExport.GetModel() );
3472 bExportNumFmt = true;
3474 catch( const uno::Exception & )
3476 TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during Export of data point" );
3479 else
3481 // property set only containing the color
3482 xPropSet.set( new ::xmloff::chart::ColorPropertySet(
3483 ::Color(ColorTransparency, xColorScheme->getColorByIndex( nElement ))));
3485 SAL_WARN_IF( !xPropSet.is(), "xmloff.chart", "Pie Segments should have properties" );
3486 if( xPropSet.is())
3488 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
3489 mrExport.getSaneDefaultVersion());
3490 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012 && bExportNumFmt)
3492 lcl_exportNumberFormat( "NumberFormat", xPropSet, mrExport );
3493 lcl_exportNumberFormat( "PercentageNumberFormat", xPropSet, mrExport );
3496 // Generate style for <chart:data-label> child element
3497 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
3499 lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet,
3500 mxExpPropMapper);
3503 if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED)
3505 sal_Int32 nPlacement = 0;
3506 xPropSet->getPropertyValue("LabelPlacement") >>= nPlacement;
3507 if (nPlacement == chart::DataLabelPlacement::CUSTOM)
3509 xPropSet->setPropertyValue("LabelPlacement",
3510 uno::Any(chart::DataLabelPlacement::OUTSIDE));
3514 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
3515 if (!aPropertyStates.empty() || !aDataLabelPropertyStates.empty())
3517 if (bExportContent)
3519 // write data-point with style
3520 SchXMLDataPointStruct aPoint;
3521 if (!aPropertyStates.empty())
3523 SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart",
3524 "Autostyle queue empty!");
3525 aPoint.maStyleName = maAutoStyleNameQueue.front();
3526 maAutoStyleNameQueue.pop();
3528 if (!aDataLabelPropertyStates.empty())
3530 SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart",
3531 "Autostyle queue empty!");
3532 aPoint.msDataLabelStyleName = maAutoStyleNameQueue.front();
3533 maAutoStyleNameQueue.pop();
3535 if(bExportNumFmt)
3536 aPoint.mCustomLabel = lcl_getCustomLabelField(mrExport, nElement, xSeries);
3537 aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(mrExport, nElement, xSeries);
3539 aDataPointVector.push_back( aPoint );
3541 else
3543 if (!aPropertyStates.empty())
3544 CollectAutoStyle(std::move(aPropertyStates));
3545 if (!aDataLabelPropertyStates.empty())
3546 CollectAutoStyle(std::move(aDataLabelPropertyStates));
3551 SAL_WARN_IF( bExportContent && (static_cast<sal_Int32>(aDataPointVector.size()) != nSeriesLength), "xmloff.chart", "not enough data points on content export" );
3553 else
3555 for( sal_Int32 nCurrIndex : std::as_const(aDataPointSeq) )
3557 aPropertyStates.clear();
3558 aDataLabelPropertyStates.clear();
3559 //assuming sorted indices in pPoints
3561 if( nCurrIndex<0 || nCurrIndex>=nSeriesLength )
3562 break;
3564 // write leading empty data points
3565 if( nCurrIndex - nLastIndex > 1 )
3567 SchXMLDataPointStruct aPoint;
3568 aPoint.mnRepeat = nCurrIndex - nLastIndex - 1;
3569 aDataPointVector.push_back( aPoint );
3572 uno::Reference< beans::XPropertySet > xPropSet;
3573 // get property states
3576 xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
3577 xSeries, nCurrIndex, mrExport.GetModel() );
3579 catch( const uno::Exception & )
3581 TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during Export of data point" );
3583 if( xPropSet.is())
3585 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
3586 mrExport.getSaneDefaultVersion());
3587 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
3589 lcl_exportNumberFormat( "NumberFormat", xPropSet, mrExport );
3590 lcl_exportNumberFormat( "PercentageNumberFormat", xPropSet, mrExport );
3593 // Generate style for <chart:data-label> child element
3594 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
3596 lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet,
3597 mxExpPropMapper);
3600 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
3602 if (!aPropertyStates.empty() || !aDataLabelPropertyStates.empty())
3604 if( bExportContent )
3606 // write data-point with style
3607 SchXMLDataPointStruct aPoint;
3608 if (!aPropertyStates.empty())
3610 SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart",
3611 "Autostyle queue empty!");
3612 aPoint.maStyleName = maAutoStyleNameQueue.front();
3613 maAutoStyleNameQueue.pop();
3615 aPoint.mCustomLabel = lcl_getCustomLabelField(mrExport, nCurrIndex, xSeries);
3616 aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(mrExport, nCurrIndex, xSeries);
3617 if (!aDataLabelPropertyStates.empty())
3619 SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart",
3620 "Autostyle queue empty!");
3621 aPoint.msDataLabelStyleName = maAutoStyleNameQueue.front();
3622 maAutoStyleNameQueue.pop();
3625 aDataPointVector.push_back( aPoint );
3626 nLastIndex = nCurrIndex;
3628 else
3630 if (!aPropertyStates.empty())
3631 CollectAutoStyle(std::move(aPropertyStates));
3632 if (!aDataLabelPropertyStates.empty())
3633 CollectAutoStyle(std::move(aDataLabelPropertyStates));
3635 continue;
3639 // if we get here the property states are empty
3640 SchXMLDataPointStruct aPoint;
3641 aDataPointVector.push_back( aPoint );
3643 nLastIndex = nCurrIndex;
3645 // final empty elements
3646 sal_Int32 nRepeat = nSeriesLength - nLastIndex - 1;
3647 if( nRepeat > 0 )
3649 SchXMLDataPointStruct aPoint;
3650 aPoint.mnRepeat = nRepeat;
3651 aDataPointVector.push_back( aPoint );
3655 if (!bExportContent)
3656 return;
3658 // write elements (merge equal ones)
3659 SchXMLDataPointStruct aPoint;
3660 SchXMLDataPointStruct aLastPoint;
3662 // initialize so that it doesn't matter if
3663 // the element is counted in the first iteration
3664 aLastPoint.mnRepeat = 0;
3665 sal_Int32 nIndex = 0;
3666 for( const auto& rPoint : aDataPointVector )
3668 aPoint = rPoint;
3670 if (aPoint.maStyleName == aLastPoint.maStyleName
3671 && aLastPoint.mCustomLabel.maFields.getLength() < 1
3672 && aLastPoint.mCustomLabelPos.Primary == 0.0
3673 && aLastPoint.mCustomLabelPos.Secondary == 0.0
3674 && aPoint.msDataLabelStyleName == aLastPoint.msDataLabelStyleName)
3675 aPoint.mnRepeat += aLastPoint.mnRepeat;
3676 else if( aLastPoint.mnRepeat > 0 )
3678 // write last element
3679 if( !aLastPoint.maStyleName.isEmpty() )
3680 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aLastPoint.maStyleName );
3682 if( aLastPoint.mnRepeat > 1 )
3683 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_REPEATED,
3684 OUString::number( aLastPoint.mnRepeat ));
3686 for (const auto& deletedLegendEntry : std::as_const(deletedLegendEntriesSeq))
3688 if (nIndex == deletedLegendEntry)
3690 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDE_LEGEND, OUString::boolean(true));
3691 break;
3694 nIndex++;
3695 exportCustomLabelPosition(aLastPoint.mCustomLabelPos); // adds attributes
3696 SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, true, true );
3697 exportCustomLabel(aLastPoint);
3699 aLastPoint = aPoint;
3701 // write last element if it hasn't been written in last iteration
3702 if( aPoint.maStyleName != aLastPoint.maStyleName )
3703 return;
3705 if( !aLastPoint.maStyleName.isEmpty() )
3706 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aLastPoint.maStyleName );
3708 if( aLastPoint.mnRepeat > 1 )
3709 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_REPEATED,
3710 OUString::number( aLastPoint.mnRepeat ));
3712 for (const auto& deletedLegendEntry : std::as_const(deletedLegendEntriesSeq))
3714 if (nIndex == deletedLegendEntry)
3716 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDE_LEGEND, OUString::boolean(true));
3717 break;
3721 exportCustomLabelPosition(aLastPoint.mCustomLabelPos); // adds attributes
3722 SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, true, true );
3723 exportCustomLabel(aLastPoint);
3726 void SchXMLExportHelper_Impl::exportCustomLabel(const SchXMLDataPointStruct& rPoint)
3728 if (rPoint.mCustomLabel.maFields.getLength() < 1 && rPoint.msDataLabelStyleName.isEmpty())
3729 return; // nothing to export
3731 if (!rPoint.msDataLabelStyleName.isEmpty())
3732 mrExport.AddAttribute(XML_NAMESPACE_CHART, XML_STYLE_NAME, rPoint.msDataLabelStyleName);
3734 if (rPoint.mCustomLabel.mbDataLabelsRange)
3736 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_LABELS_CELL_RANGE, rPoint.mCustomLabel.maRange);
3737 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_LABEL_GUID, rPoint.mCustomLabel.maGuid);
3739 // TODO svg:x and svg:y for <chart:data-label>
3740 SvXMLElementExport aLabelElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_LABEL, true, true);
3741 SvXMLElementExport aPara( mrExport, XML_NAMESPACE_TEXT, XML_P, true, false );
3743 for (const Reference<chart2::XDataPointCustomLabelField>& label : rPoint.mCustomLabel.maFields)
3745 // TODO add style
3746 SvXMLElementExport aSpan( mrExport, XML_NAMESPACE_TEXT, XML_SPAN, true, false);
3747 mrExport.GetDocHandler()->characters(label->getString());
3751 void SchXMLExportHelper_Impl::exportCustomLabelPosition( const chart2::RelativePosition & xCustomLabelPosition)
3753 if( xCustomLabelPosition.Primary == 0.0 && xCustomLabelPosition.Secondary == 0.0 )
3754 return; // nothing to export
3756 OUStringBuffer aCustomLabelPosString;
3757 ::sax::Converter::convertDouble(aCustomLabelPosString, xCustomLabelPosition.Primary);
3758 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_CUSTOM_LABEL_POS_X, aCustomLabelPosString.makeStringAndClear());
3760 ::sax::Converter::convertDouble(aCustomLabelPosString, xCustomLabelPosition.Secondary);
3761 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_CUSTOM_LABEL_POS_Y, aCustomLabelPosString.makeStringAndClear());
3764 void SchXMLExportHelper_Impl::addPosition( const awt::Point & rPosition )
3766 mrExport.GetMM100UnitConverter().convertMeasureToXML(
3767 msStringBuffer, rPosition.X );
3768 msString = msStringBuffer.makeStringAndClear();
3769 mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_X, msString );
3771 mrExport.GetMM100UnitConverter().convertMeasureToXML(
3772 msStringBuffer, rPosition.Y );
3773 msString = msStringBuffer.makeStringAndClear();
3774 mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_Y, msString );
3777 void SchXMLExportHelper_Impl::addPosition( const Reference< drawing::XShape >& xShape )
3779 if( xShape.is())
3780 addPosition( xShape->getPosition());
3783 void SchXMLExportHelper_Impl::addSize( const awt::Size & rSize, bool bIsOOoNamespace)
3785 mrExport.GetMM100UnitConverter().convertMeasureToXML(
3786 msStringBuffer, rSize.Width );
3787 msString = msStringBuffer.makeStringAndClear();
3788 mrExport.AddAttribute( bIsOOoNamespace ? XML_NAMESPACE_CHART_EXT : XML_NAMESPACE_SVG , XML_WIDTH, msString );
3790 mrExport.GetMM100UnitConverter().convertMeasureToXML(
3791 msStringBuffer, rSize.Height);
3792 msString = msStringBuffer.makeStringAndClear();
3793 mrExport.AddAttribute( bIsOOoNamespace ? XML_NAMESPACE_CHART_EXT : XML_NAMESPACE_SVG, XML_HEIGHT, msString );
3796 void SchXMLExportHelper_Impl::addSize( const Reference< drawing::XShape >& xShape )
3798 if( xShape.is())
3799 addSize( xShape->getSize() );
3802 awt::Size SchXMLExportHelper_Impl::getPageSize( const Reference< chart2::XChartDocument > & xChartDoc )
3804 awt::Size aSize( 8000, 7000 );
3805 uno::Reference< embed::XVisualObject > xVisualObject( xChartDoc, uno::UNO_QUERY );
3806 SAL_WARN_IF( !xVisualObject.is(), "xmloff.chart", "need XVisualObject for page size" );
3807 if( xVisualObject.is() )
3808 aSize = xVisualObject->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT );
3810 return aSize;
3813 void SchXMLExportHelper_Impl::CollectAutoStyle( std::vector< XMLPropertyState >&& aStates )
3815 if( !aStates.empty() )
3816 maAutoStyleNameQueue.push( mrAutoStylePool.Add( XmlStyleFamily::SCH_CHART_ID, std::move(aStates) ));
3819 void SchXMLExportHelper_Impl::AddAutoStyleAttribute( const std::vector< XMLPropertyState >& aStates )
3821 if( !aStates.empty() )
3823 SAL_WARN_IF( maAutoStyleNameQueue.empty(), "xmloff.chart", "Autostyle queue empty!" );
3825 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, maAutoStyleNameQueue.front() );
3826 maAutoStyleNameQueue.pop();
3830 void SchXMLExportHelper_Impl::exportText( const OUString& rText )
3832 SchXMLTools::exportText( mrExport, rText, false/*bConvertTabsLFs*/ );
3836 SchXMLExport::SchXMLExport(const Reference<uno::XComponentContext>& xContext,
3837 OUString const& implementationName, SvXMLExportFlags nExportFlags)
3838 : SvXMLExport(xContext, implementationName, util::MeasureUnit::CM, ::xmloff::token::XML_CHART,
3839 nExportFlags)
3840 , maAutoStylePool(new SchXMLAutoStylePoolP(*this))
3841 , maExportHelper(new SchXMLExportHelper(*this, *maAutoStylePool))
3843 if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
3844 GetNamespaceMap_().Add( GetXMLToken(XML_NP_CHART_EXT), GetXMLToken(XML_N_CHART_EXT), XML_NAMESPACE_CHART_EXT);
3847 SchXMLExport::~SchXMLExport()
3851 ErrCode SchXMLExport::exportDoc( enum ::xmloff::token::XMLTokenEnum eClass )
3853 maExportHelper->SetSourceShellID(GetSourceShellID());
3854 maExportHelper->SetDestinationShellID(GetDestinationShellID());
3856 Reference< chart2::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
3857 maExportHelper->m_pImpl->InitRangeSegmentationProperties( xChartDoc );
3858 return SvXMLExport::exportDoc( eClass );
3861 void SchXMLExport::ExportMasterStyles_()
3863 // not available in chart
3864 SAL_INFO("xmloff.chart", "Master Style Export requested. Not available for Chart" );
3867 void SchXMLExport::collectAutoStyles()
3869 SvXMLExport::collectAutoStyles();
3871 if (mbAutoStylesCollected)
3872 return;
3874 // there are no styles that require their own autostyles
3875 if( getExportFlags() & SvXMLExportFlags::CONTENT )
3877 Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
3878 if( xChartDoc.is())
3880 maExportHelper->m_pImpl->collectAutoStyles( xChartDoc );
3882 else
3884 SAL_WARN("xmloff.chart", "Couldn't export chart due to wrong XModel (must be XChartDocument)" );
3887 mbAutoStylesCollected = true;
3890 void SchXMLExport::ExportAutoStyles_()
3892 collectAutoStyles();
3894 if( getExportFlags() & SvXMLExportFlags::CONTENT )
3896 Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
3897 if( xChartDoc.is())
3899 maExportHelper->m_pImpl->exportAutoStyles();
3901 else
3903 SAL_WARN("xmloff.chart", "Couldn't export chart due to wrong XModel (must be XChartDocument)" );
3908 void SchXMLExport::ExportContent_()
3910 Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
3911 if( xChartDoc.is())
3913 // determine if data comes from the outside
3914 bool bIncludeTable = true;
3916 Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
3917 if( xNewDoc.is())
3919 // check if we have own data. If so we must not export the complete
3920 // range string, as this is our only indicator for having own or
3921 // external data. @todo: fix this in the file format!
3922 Reference< lang::XServiceInfo > xDPServiceInfo( xNewDoc->getDataProvider(), uno::UNO_QUERY );
3923 if( ! (xDPServiceInfo.is() && xDPServiceInfo->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" ))
3925 bIncludeTable = false;
3928 else
3930 Reference< lang::XServiceInfo > xServ( xChartDoc, uno::UNO_QUERY );
3931 if( xServ.is())
3933 if( xServ->supportsService( "com.sun.star.chart.ChartTableAddressSupplier" ))
3935 Reference< beans::XPropertySet > xProp( xServ, uno::UNO_QUERY );
3936 if( xProp.is())
3938 Any aAny;
3941 OUString sChartAddress;
3942 aAny = xProp->getPropertyValue( "ChartRangeAddress" );
3943 aAny >>= sChartAddress;
3944 maExportHelper->m_pImpl->SetChartRangeAddress( sChartAddress );
3946 // do not include own table if there are external addresses
3947 bIncludeTable = sChartAddress.isEmpty();
3949 catch( const beans::UnknownPropertyException & )
3951 SAL_WARN("xmloff.chart", "Property ChartRangeAddress not supported by ChartDocument" );
3957 maExportHelper->m_pImpl->exportChart( xChartDoc, bIncludeTable );
3959 else
3961 SAL_WARN("xmloff.chart", "Couldn't export chart due to wrong XModel" );
3965 rtl::Reference< XMLPropertySetMapper > const & SchXMLExport::GetPropertySetMapper() const
3967 return maExportHelper->m_pImpl->GetPropertySetMapper();
3970 void SchXMLExportHelper_Impl::InitRangeSegmentationProperties( const Reference< chart2::XChartDocument > & xChartDoc )
3972 if( !xChartDoc.is())
3973 return;
3977 Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
3978 SAL_WARN_IF( !xDataProvider.is(), "xmloff.chart", "No DataProvider" );
3979 if( xDataProvider.is())
3981 Reference< chart2::data::XDataSource > xDataSource( lcl_pressUsedDataIntoRectangularFormat( xChartDoc, mbHasCategoryLabels ));
3982 const Sequence< beans::PropertyValue > aArgs( xDataProvider->detectArguments( xDataSource ));
3983 OUString sCellRange, sBrokenRange;
3984 bool bBrokenRangeAvailable = false;
3985 for( const auto& rArg : aArgs )
3987 if ( rArg.Name == "CellRangeRepresentation" )
3988 rArg.Value >>= sCellRange;
3989 else if ( rArg.Name == "BrokenCellRangeForExport" )
3991 if( rArg.Value >>= sBrokenRange )
3992 bBrokenRangeAvailable = true;
3994 else if ( rArg.Name == "DataRowSource" )
3996 chart::ChartDataRowSource eRowSource;
3997 rArg.Value >>= eRowSource;
3998 mbRowSourceColumns = ( eRowSource == chart::ChartDataRowSource_COLUMNS );
4000 else if ( rArg.Name == "SequenceMapping" )
4001 rArg.Value >>= maSequenceMapping;
4004 // #i79009# For Writer we have to export a broken version of the
4005 // range, where every row number is not too large, so that older
4006 // version can correctly read those files.
4007 msChartAddress = (bBrokenRangeAvailable ? sBrokenRange : sCellRange);
4008 if( !msChartAddress.isEmpty() )
4010 // convert format to XML-conform one
4011 Reference< chart2::data::XRangeXMLConversion > xConversion( xDataProvider, uno::UNO_QUERY );
4012 if( xConversion.is())
4013 msChartAddress = xConversion->convertRangeToXML( msChartAddress );
4017 catch( const uno::Exception & )
4019 DBG_UNHANDLED_EXCEPTION("xmloff.chart");
4023 // first version: everything goes in one storage
4025 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4026 com_sun_star_comp_Chart_XMLExporter_get_implementation(uno::XComponentContext* pCtx,
4027 uno::Sequence<uno::Any> const& /*rSeq*/)
4029 return cppu::acquire(
4030 new SchXMLExport(pCtx, "SchXMLExport.Compact",
4031 SvXMLExportFlags::ALL
4032 ^ (SvXMLExportFlags::SETTINGS | SvXMLExportFlags::MASTERSTYLES
4033 | SvXMLExportFlags::SCRIPTS)));
4036 // Oasis format
4037 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4038 com_sun_star_comp_Chart_XMLOasisExporter_get_implementation(uno::XComponentContext* pCtx,
4039 uno::Sequence<uno::Any> const& /*rSeq*/)
4041 return cppu::acquire(
4042 new SchXMLExport(pCtx, "SchXMLExport.Oasis.Compact",
4043 (SvXMLExportFlags::ALL
4044 ^ (SvXMLExportFlags::SETTINGS | SvXMLExportFlags::MASTERSTYLES
4045 | SvXMLExportFlags::SCRIPTS))
4046 | SvXMLExportFlags::OASIS));
4049 // multiple storage version: one for content / styles / meta
4051 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4052 com_sun_star_comp_Chart_XMLStylesExporter_get_implementation(
4053 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
4055 return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Styles", SvXMLExportFlags::STYLES));
4058 // Oasis format
4059 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4060 com_sun_star_comp_Chart_XMLOasisStylesExporter_get_implementation(
4061 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
4063 return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Oasis.Styles",
4064 SvXMLExportFlags::STYLES | SvXMLExportFlags::OASIS));
4067 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4068 com_sun_star_comp_Chart_XMLContentExporter_get_implementation(
4069 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
4071 return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Content",
4072 SvXMLExportFlags::AUTOSTYLES | SvXMLExportFlags::CONTENT
4073 | SvXMLExportFlags::FONTDECLS));
4076 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4077 com_sun_star_comp_Chart_XMLOasisContentExporter_get_implementation(
4078 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
4080 return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Oasis.Content",
4081 SvXMLExportFlags::AUTOSTYLES | SvXMLExportFlags::CONTENT
4082 | SvXMLExportFlags::FONTDECLS
4083 | SvXMLExportFlags::OASIS));
4086 // Oasis format
4088 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4089 com_sun_star_comp_Chart_XMLOasisMetaExporter_get_implementation(
4090 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
4092 return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Oasis.Meta",
4093 SvXMLExportFlags::META | SvXMLExportFlags::OASIS));
4096 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */