Version 7.5.1.1, tag libreoffice-7.5.1.1
[LibreOffice.git] / xmloff / source / chart / SchXMLExport.cxx
blobbd8de071eb2022419685a9130b7695efc71d88c6
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 OUStringLiteral gsTableName = u"local-table";
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 OUString( XML_STYLE_FAMILY_SCH_CHART_NAME ),
1084 mxExpPropMapper.get(),
1085 OUString( XML_STYLE_FAMILY_SCH_CHART_PREFIX ));
1087 // register shape family
1088 mrAutoStylePool.AddFamily(
1089 XmlStyleFamily::SD_GRAPHICS_ID,
1090 OUString( XML_STYLE_FAMILY_SD_GRAPHICS_NAME ),
1091 mxExpPropMapper.get(),
1092 OUString( 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 css::uno::Sequence<OUString> aAutoStylePropNames = mrAutoStylePool.GetPropertyNames();
1593 Reference< drawing::XShape > xShape;
1594 const sal_Int32 nShapeCount( mxAdditionalShapes->getCount());
1595 for( sal_Int32 nShapeId = 0; nShapeId < nShapeCount; nShapeId++ )
1597 mxAdditionalShapes->getByIndex( nShapeId ) >>= xShape;
1598 SAL_WARN_IF( !xShape.is(), "xmloff.chart", "Shape without an XShape?" );
1599 if( ! xShape.is())
1600 continue;
1602 rShapeExport->collectShapeAutoStyles( xShape, aAutoStylePropNames );
1608 // table element
1609 // (is included as subelement of chart)
1610 if( bExportContent )
1612 // #85929# always export table, otherwise clipboard may lose data
1613 exportTable();
1617 static void lcl_exportComplexLabel( const Sequence< uno::Any >& rComplexLabel, SvXMLExport& rExport )
1619 sal_Int32 nLength = rComplexLabel.getLength();
1620 if( nLength<=1 )
1621 return;
1622 SvXMLElementExport aTextList( rExport, XML_NAMESPACE_TEXT, XML_LIST, true, true );
1623 for(const auto& rElem : rComplexLabel)
1625 SvXMLElementExport aListItem( rExport, XML_NAMESPACE_TEXT, XML_LIST_ITEM, true, true );
1626 OUString aString;
1627 if( !(rElem >>= aString) )
1629 double aNum;
1630 if (rElem >>= aNum)
1632 aString = OUString::number(aNum);
1635 SchXMLTools::exportText( rExport, aString, false /*bConvertTabsLFs*/ );
1639 void SchXMLExportHelper_Impl::exportTable()
1641 // table element
1642 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NAME, gsTableName );
1646 bool bProtected = false;
1647 Reference< beans::XPropertySet > xProps( mrExport.GetModel(), uno::UNO_QUERY_THROW );
1648 if ( ( xProps->getPropertyValue("DisableDataTableDialog") >>= bProtected ) &&
1649 bProtected )
1651 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_PROTECTED, XML_TRUE );
1654 catch ( const uno::Exception& )
1658 SvXMLElementExport aTable( mrExport, XML_NAMESPACE_TABLE, XML_TABLE, true, true );
1660 bool bHasOwnData = false;
1661 Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
1662 Reference< chart2::data::XRangeXMLConversion > xRangeConversion;
1663 if( xNewDoc.is())
1665 bHasOwnData = xNewDoc->hasInternalDataProvider();
1666 xRangeConversion.set( xNewDoc->getDataProvider(), uno::UNO_QUERY );
1669 Reference< chart2::XAnyDescriptionAccess > xAnyDescriptionAccess;
1671 Reference< chart::XChartDocument > xChartDoc( mrExport.GetModel(), uno::UNO_QUERY );
1672 if( xChartDoc.is() )
1673 xAnyDescriptionAccess.set( xChartDoc->getData(), uno::UNO_QUERY );
1676 if( bHasOwnData )
1677 lcl_ReorderInternalSequencesAccordingToTheirRangeName( m_aDataSequencesToExport );
1678 lcl_TableData aData( lcl_getDataForLocalTable( m_aDataSequencesToExport
1679 , xAnyDescriptionAccess, maCategoriesRange
1680 , mbRowSourceColumns, xRangeConversion ));
1682 tStringVector::const_iterator aDataRangeIter( aData.aDataRangeRepresentations.begin());
1683 const tStringVector::const_iterator aDataRangeEndIter( aData.aDataRangeRepresentations.end());
1685 tStringVector::const_iterator aRowDescriptions_RangeIter( aData.aRowDescriptions_Ranges.begin());
1686 const tStringVector::const_iterator aRowDescriptions_RangeEnd( aData.aRowDescriptions_Ranges.end());
1688 // declare columns
1690 SvXMLElementExport aHeaderColumns( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_HEADER_COLUMNS, true, true );
1691 SvXMLElementExport aHeaderColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true );
1694 SvXMLElementExport aColumns( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMNS, true, true );
1696 sal_Int32 nNextIndex = 0;
1697 for(sal_Int32 nHiddenIndex : aData.aHiddenColumns)
1699 //i91578 display of hidden values (copy paste scenario; export hidden flag thus it can be used during migration to locale table upon paste )
1700 if( nHiddenIndex > nNextIndex )
1702 sal_Int64 nRepeat = static_cast< sal_Int64 >( nHiddenIndex - nNextIndex );
1703 if(nRepeat>1)
1704 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
1705 OUString::number( nRepeat ));
1706 SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true );
1708 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_VISIBILITY, GetXMLToken( XML_COLLAPSE ) );
1709 SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true );
1710 nNextIndex = nHiddenIndex+1;
1713 sal_Int32 nEndIndex = aData.aColumnDescriptions.size()-1;
1714 if( nEndIndex >= nNextIndex )
1716 sal_Int64 nRepeat = static_cast< sal_Int64 >( nEndIndex - nNextIndex + 1 );
1717 if(nRepeat>1)
1718 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_NUMBER_COLUMNS_REPEATED,
1719 OUString::number( nRepeat ));
1720 SvXMLElementExport aColumn( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_COLUMN, true, true );
1724 // export rows with content
1725 //export header row
1727 SvXMLElementExport aHeaderRows( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_HEADER_ROWS, true, true );
1728 SvXMLElementExport aRow( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true );
1730 //first one empty cell for the row descriptions
1732 SvXMLElementExport aEmptyCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true );
1733 SvXMLElementExport aEmptyParagraph( mrExport, XML_NAMESPACE_TEXT, XML_P, true, true );
1736 //export column descriptions
1737 tStringVector::const_iterator aColumnDescriptions_RangeIter( aData.aColumnDescriptions_Ranges.begin());
1738 const tStringVector::const_iterator aColumnDescriptions_RangeEnd( aData.aColumnDescriptions_Ranges.end());
1739 const Sequence< Sequence< uno::Any > >& rComplexColumnDescriptions = aData.aComplexColumnDescriptions;
1740 sal_Int32 nComplexCount = rComplexColumnDescriptions.getLength();
1741 sal_Int32 nC = 0;
1742 for( const auto& rDesc : aData.aColumnDescriptions )
1744 bool bExportString = true;
1745 if( nC < nComplexCount )
1747 const Sequence< uno::Any >& rComplexLabel = rComplexColumnDescriptions[nC];
1748 if( rComplexLabel.hasElements() )
1750 double fValue=0.0;
1751 if( rComplexLabel[0] >>=fValue )
1753 bExportString = false;
1755 ::sax::Converter::convertDouble(
1756 msStringBuffer, fValue);
1757 msString = msStringBuffer.makeStringAndClear();
1758 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT );
1759 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE, msString );
1763 if( bExportString )
1765 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING );
1768 SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true );
1769 exportText( rDesc );
1770 if( nC < nComplexCount )
1771 lcl_exportComplexLabel( rComplexColumnDescriptions[nC], mrExport );
1772 if( !bHasOwnData && aColumnDescriptions_RangeIter != aColumnDescriptions_RangeEnd )
1774 // remind the original range to allow a correct re-association when copying via clipboard
1775 if (!(*aColumnDescriptions_RangeIter).isEmpty())
1776 SchXMLTools::exportRangeToSomewhere( mrExport, *aColumnDescriptions_RangeIter );
1777 ++aColumnDescriptions_RangeIter;
1780 nC++;
1782 SAL_WARN_IF( !bHasOwnData && (aColumnDescriptions_RangeIter != aColumnDescriptions_RangeEnd), "xmloff.chart", "bHasOwnData == false && aColumnDescriptions_RangeIter != aColumnDescriptions_RangeEnd" );
1783 } // closing row and header-rows elements
1785 // export value rows
1787 SvXMLElementExport aRows( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROWS, true, true );
1788 tStringVector::const_iterator aRowDescriptionsIter( aData.aRowDescriptions.begin());
1789 const Sequence< Sequence< uno::Any > >& rComplexRowDescriptions = aData.aComplexRowDescriptions;
1790 sal_Int32 nComplexCount = rComplexRowDescriptions.getLength();
1791 sal_Int32 nC = 0;
1793 for( const auto& rRow : aData.aDataInRows )
1795 SvXMLElementExport aRow( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_ROW, true, true );
1797 //export row descriptions
1799 bool bExportString = true;
1800 if( nC < nComplexCount )
1802 const Sequence< uno::Any >& rComplexLabel = rComplexRowDescriptions[nC];
1803 if( rComplexLabel.hasElements() )
1805 double fValue=0.0;
1806 if( rComplexLabel[0] >>=fValue )
1808 bExportString = false;
1810 ::sax::Converter::convertDouble(msStringBuffer, fValue);
1811 msString = msStringBuffer.makeStringAndClear();
1812 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT );
1813 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE, msString );
1817 if( bExportString )
1819 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_STRING );
1822 SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true );
1823 if( aRowDescriptionsIter != aData.aRowDescriptions.end())
1825 exportText( *aRowDescriptionsIter );
1826 if( nC < nComplexCount )
1827 lcl_exportComplexLabel( rComplexRowDescriptions[nC], mrExport );
1828 if( !bHasOwnData && aRowDescriptions_RangeIter != aRowDescriptions_RangeEnd )
1830 // remind the original range to allow a correct re-association when copying via clipboard
1831 SchXMLTools::exportRangeToSomewhere( mrExport, *aRowDescriptions_RangeIter );
1832 ++aRowDescriptions_RangeIter;
1834 ++aRowDescriptionsIter;
1838 //export row values
1839 for( t2DNumberContainer::value_type::const_iterator aColIt( rRow.begin());
1840 aColIt != rRow.end(); ++aColIt )
1842 ::sax::Converter::convertDouble( msStringBuffer, *aColIt );
1843 msString = msStringBuffer.makeStringAndClear();
1844 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE_TYPE, XML_FLOAT );
1845 mrExport.AddAttribute( XML_NAMESPACE_OFFICE, XML_VALUE, msString );
1846 SvXMLElementExport aCell( mrExport, XML_NAMESPACE_TABLE, XML_TABLE_CELL, true, true );
1847 exportText( msString ); // do not convert tabs and lfs
1848 if( ( !bHasOwnData && aDataRangeIter != aDataRangeEndIter ) &&
1849 ( mbRowSourceColumns || (aColIt == rRow.begin()) ) )
1851 // remind the original range to allow a correct re-association when copying via clipboard
1852 if (!(*aDataRangeIter).isEmpty())
1853 SchXMLTools::exportRangeToSomewhere( mrExport, *aDataRangeIter );
1854 ++aDataRangeIter;
1858 ++nC;
1862 // if range iterator was used it should have reached its end
1863 SAL_WARN_IF( !bHasOwnData && (aDataRangeIter != aDataRangeEndIter), "xmloff.chart", "bHasOwnData == false && aDataRangeIter != aDataRangeEndIter" );
1864 SAL_WARN_IF( !bHasOwnData && (aRowDescriptions_RangeIter != aRowDescriptions_RangeEnd), "xmloff.chart", "bHasOwnData == false && aRowDescriptions_RangeIter != aRowDescriptions_RangeEnd" );
1867 namespace
1870 Reference< chart2::XCoordinateSystem > lcl_getCooSys( const Reference< chart2::XDiagram > & xNewDiagram )
1872 Reference< chart2::XCoordinateSystem > xCooSys;
1873 Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xNewDiagram, uno::UNO_QUERY );
1874 if(xCooSysCnt.is())
1876 Sequence< Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems() );
1877 if(aCooSysSeq.hasElements())
1878 xCooSys = aCooSysSeq[0];
1880 return xCooSys;
1883 Reference< chart2::XAxis > lcl_getAxis( const Reference< chart2::XCoordinateSystem >& xCooSys,
1884 enum XMLTokenEnum eDimension, bool bPrimary=true )
1886 Reference< chart2::XAxis > xNewAxis;
1889 if( xCooSys.is() )
1891 sal_Int32 nDimensionIndex=0;
1892 switch( eDimension )
1894 case XML_X:
1895 nDimensionIndex=0;
1896 break;
1897 case XML_Y:
1898 nDimensionIndex=1;
1899 break;
1900 case XML_Z:
1901 nDimensionIndex=2;
1902 break;
1903 default:
1904 break;
1907 xNewAxis = xCooSys->getAxisByDimension( nDimensionIndex, bPrimary ? 0 : 1 );
1910 catch( const uno::Exception & )
1913 return xNewAxis;
1918 void SchXMLExportHelper_Impl::exportPlotArea(
1919 const Reference< chart::XDiagram >& xDiagram,
1920 const Reference< chart2::XDiagram >& xNewDiagram,
1921 const awt::Size & rPageSize,
1922 bool bExportContent,
1923 bool bIncludeTable )
1925 SAL_WARN_IF( !xDiagram.is(), "xmloff.chart", "Invalid XDiagram as parameter" );
1926 if( ! xDiagram.is())
1927 return;
1929 // variables for autostyles
1930 Reference< beans::XPropertySet > xPropSet;
1931 std::vector< XMLPropertyState > aPropertyStates;
1933 msStringBuffer.setLength( 0 );
1935 // plot-area element
1937 std::unique_ptr<SvXMLElementExport> xElPlotArea;
1938 // get property states for autostyles
1939 xPropSet.set( xDiagram, uno::UNO_QUERY );
1940 if( xPropSet.is())
1942 if( mxExpPropMapper.is())
1943 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
1945 if( bExportContent )
1947 rtl::Reference< XMLShapeExport > rShapeExport;
1949 // write style name
1950 AddAutoStyleAttribute( aPropertyStates );
1952 if( !msChartAddress.isEmpty() )
1954 if( !bIncludeTable )
1955 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, msChartAddress );
1957 Reference< chart::XChartDocument > xDoc( mrExport.GetModel(), uno::UNO_QUERY );
1958 if( xDoc.is() )
1960 Reference< beans::XPropertySet > xDocProp( xDoc, uno::UNO_QUERY );
1961 if( xDocProp.is())
1963 Any aAny;
1967 bool bFirstCol = false, bFirstRow = false;
1969 aAny = xDocProp->getPropertyValue( "DataSourceLabelsInFirstColumn" );
1970 aAny >>= bFirstCol;
1971 aAny = xDocProp->getPropertyValue( "DataSourceLabelsInFirstRow" );
1972 aAny >>= bFirstRow;
1974 if( bFirstCol || bFirstRow )
1976 mrExport.AddAttribute( XML_NAMESPACE_CHART,
1977 ::xmloff::token::GetXMLToken( ::xmloff::token::XML_DATA_SOURCE_HAS_LABELS ),
1978 ( bFirstCol
1979 ? ( bFirstRow
1980 ? ::xmloff::token::GetXMLToken( ::xmloff::token::XML_BOTH )
1981 : ::xmloff::token::GetXMLToken( ::xmloff::token::XML_COLUMN ))
1982 : ::xmloff::token::GetXMLToken( ::xmloff::token::XML_ROW )));
1985 catch( const beans::UnknownPropertyException & )
1987 SAL_WARN("xmloff.chart", "Properties missing" );
1993 // attributes
1994 if( xDiagram.is())
1996 addPosition( xDiagram );
1997 addSize( xDiagram );
2000 bool bIs3DChart = false;
2002 if( xPropSet.is())
2004 Any aAny;
2006 // 3d attributes
2009 aAny = xPropSet->getPropertyValue("Dim3D");
2010 aAny >>= bIs3DChart;
2012 if( bIs3DChart )
2014 rShapeExport = mrExport.GetShapeExport();
2015 if( rShapeExport.is())
2016 rShapeExport->export3DSceneAttributes( xPropSet );
2019 catch( const uno::Exception & )
2021 TOOLS_INFO_EXCEPTION("xmloff.chart", "chart:exportPlotAreaException caught");
2025 // plot-area element
2026 xElPlotArea.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_PLOT_AREA, true, true ));
2028 //inner position rectangle element
2029 exportCoordinateRegion( xDiagram );
2031 // light sources (inside plot area element)
2032 if( bIs3DChart &&
2033 rShapeExport.is())
2034 rShapeExport->export3DLamps( xPropSet );
2036 else // autostyles
2038 CollectAutoStyle( std::move(aPropertyStates) );
2040 // remove property states for autostyles
2041 aPropertyStates.clear();
2043 // axis elements
2044 exportAxes( xDiagram, xNewDiagram, bExportContent );
2046 // series elements
2047 Reference< chart2::XAxis > xSecondYAxis = lcl_getAxis( lcl_getCooSys( xNewDiagram ), XML_Y, false );
2048 exportSeries( xNewDiagram, rPageSize, bExportContent, xSecondYAxis.is() );
2050 // stock-chart elements
2051 OUString sChartType ( xDiagram->getDiagramType());
2052 if( sChartType == "com.sun.star.chart.StockDiagram" )
2054 Reference< chart::XStatisticDisplay > xStockPropProvider( xDiagram, uno::UNO_QUERY );
2055 if( xStockPropProvider.is())
2057 // stock-gain-marker
2058 Reference< beans::XPropertySet > xStockPropSet = xStockPropProvider->getUpBar();
2059 if( xStockPropSet.is())
2061 aPropertyStates.clear();
2062 aPropertyStates = mxExpPropMapper->Filter(mrExport, xStockPropSet);
2064 if( !aPropertyStates.empty() )
2066 if( bExportContent )
2068 AddAutoStyleAttribute( aPropertyStates );
2070 SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_GAIN_MARKER, true, true );
2072 else
2074 CollectAutoStyle( std::move(aPropertyStates) );
2079 // stock-loss-marker
2080 xStockPropSet = xStockPropProvider->getDownBar();
2081 if( xStockPropSet.is())
2083 aPropertyStates.clear();
2084 aPropertyStates = mxExpPropMapper->Filter(mrExport, xStockPropSet);
2086 if( !aPropertyStates.empty() )
2088 if( bExportContent )
2090 AddAutoStyleAttribute( aPropertyStates );
2092 SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_LOSS_MARKER, true, true );
2094 else
2096 CollectAutoStyle( std::move(aPropertyStates) );
2101 // stock-range-line
2102 xStockPropSet = xStockPropProvider->getMinMaxLine();
2103 if( xStockPropSet.is())
2105 aPropertyStates.clear();
2106 aPropertyStates = mxExpPropMapper->Filter(mrExport, xStockPropSet);
2108 if( !aPropertyStates.empty() )
2110 if( bExportContent )
2112 AddAutoStyleAttribute( aPropertyStates );
2114 SvXMLElementExport aGain( mrExport, XML_NAMESPACE_CHART, XML_STOCK_RANGE_LINE, true, true );
2116 else
2118 CollectAutoStyle( std::move(aPropertyStates) );
2125 // wall and floor element
2126 Reference< chart::X3DDisplay > xWallFloorSupplier( xDiagram, uno::UNO_QUERY );
2127 if( !(mxExpPropMapper.is() &&
2128 xWallFloorSupplier.is()))
2129 return;
2131 // remove property states for autostyles
2132 aPropertyStates.clear();
2134 Reference< beans::XPropertySet > xWallPropSet = xWallFloorSupplier->getWall();
2135 if( xWallPropSet.is())
2137 aPropertyStates = mxExpPropMapper->Filter(mrExport, xWallPropSet);
2139 if( !aPropertyStates.empty() )
2141 // write element
2142 if( bExportContent )
2144 // add style name attribute
2145 AddAutoStyleAttribute( aPropertyStates );
2147 SvXMLElementExport aWall( mrExport, XML_NAMESPACE_CHART, XML_WALL, true, true );
2149 else // autostyles
2151 CollectAutoStyle( std::move(aPropertyStates) );
2156 // floor element
2157 // remove property states for autostyles
2158 aPropertyStates.clear();
2160 Reference< beans::XPropertySet > xFloorPropSet = xWallFloorSupplier->getFloor();
2161 if( !xFloorPropSet.is())
2162 return;
2164 aPropertyStates = mxExpPropMapper->Filter(mrExport, xFloorPropSet);
2166 if( aPropertyStates.empty() )
2167 return;
2169 // write element
2170 if( bExportContent )
2172 // add style name attribute
2173 AddAutoStyleAttribute( aPropertyStates );
2175 SvXMLElementExport aFloor( mrExport, XML_NAMESPACE_CHART, XML_FLOOR, true, true );
2177 else // autostyles
2179 CollectAutoStyle( std::move(aPropertyStates) );
2183 void SchXMLExportHelper_Impl::exportCoordinateRegion( const uno::Reference< chart::XDiagram >& xDiagram )
2185 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
2186 mrExport.getSaneDefaultVersion());
2187 if (nCurrentODFVersion <= SvtSaveOptions::ODFSVER_012) //do not export to ODF 1.2 or older
2188 return;
2190 Reference< chart::XDiagramPositioning > xDiaPos( xDiagram, uno::UNO_QUERY );
2191 SAL_WARN_IF( !xDiaPos.is(), "xmloff.chart", "Invalid xDiaPos as parameter" );
2192 if( !xDiaPos.is() )
2193 return;
2195 awt::Rectangle aRect( xDiaPos->calculateDiagramPositionExcludingAxes() );
2196 addPosition( awt::Point(aRect.X,aRect.Y) );
2197 addSize( awt::Size(aRect.Width,aRect.Height) );
2199 // ODF 1.3 OFFICE-3928
2200 SvXMLElementExport aCoordinateRegion( mrExport,
2201 (SvtSaveOptions::ODFSVER_013 <= nCurrentODFVersion) ? XML_NAMESPACE_CHART : XML_NAMESPACE_CHART_EXT,
2202 XML_COORDINATE_REGION, true, true );
2205 namespace
2207 XMLTokenEnum lcl_getTimeUnitToken( sal_Int32 nTimeUnit )
2209 XMLTokenEnum eToken = XML_DAYS;
2210 switch( nTimeUnit )
2212 case css::chart::TimeUnit::YEAR:
2213 eToken = XML_YEARS;
2214 break;
2215 case css::chart::TimeUnit::MONTH:
2216 eToken = XML_MONTHS;
2217 break;
2218 default://days
2219 break;
2221 return eToken;
2225 void SchXMLExportHelper_Impl::exportDateScale( const Reference< beans::XPropertySet >& rAxisProps )
2227 if( !rAxisProps.is() )
2228 return;
2230 chart::TimeIncrement aIncrement;
2231 if( !(rAxisProps->getPropertyValue("TimeIncrement") >>= aIncrement) )
2232 return;
2234 sal_Int32 nTimeResolution = css::chart::TimeUnit::DAY;
2235 if( aIncrement.TimeResolution >>= nTimeResolution )
2236 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_BASE_TIME_UNIT, lcl_getTimeUnitToken( nTimeResolution ) );
2238 chart::TimeInterval aInterval;
2239 if( aIncrement.MajorTimeInterval >>= aInterval )
2241 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MAJOR_INTERVAL_VALUE, OUString::number(aInterval.Number) );
2242 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MAJOR_INTERVAL_UNIT, lcl_getTimeUnitToken( aInterval.TimeUnit ) );
2244 if( aIncrement.MinorTimeInterval >>= aInterval )
2246 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MINOR_INTERVAL_VALUE, OUString::number(aInterval.Number) );
2247 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_MINOR_INTERVAL_UNIT, lcl_getTimeUnitToken( aInterval.TimeUnit ) );
2250 SvXMLElementExport aDateScale( mrExport, XML_NAMESPACE_CHART_EXT, XML_DATE_SCALE, true, true );//#i25706#todo: change namespace for next ODF version
2253 void SchXMLExportHelper_Impl::exportAxisTitle( const Reference< beans::XPropertySet >& rTitleProps, bool bExportContent )
2255 if( !rTitleProps.is() )
2256 return;
2257 std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, rTitleProps);
2258 if( bExportContent )
2260 OUString aText;
2261 Any aAny( rTitleProps->getPropertyValue( "String" ));
2262 aAny >>= aText;
2264 Reference< drawing::XShape > xShape( rTitleProps, uno::UNO_QUERY );
2265 if( xShape.is())
2266 addPosition( xShape );
2268 AddAutoStyleAttribute( aPropertyStates );
2269 SvXMLElementExport aTitle( mrExport, XML_NAMESPACE_CHART, XML_TITLE, true, true );
2271 // paragraph containing title
2272 exportText( aText );
2274 else
2276 CollectAutoStyle( std::move(aPropertyStates) );
2278 aPropertyStates.clear();
2281 void SchXMLExportHelper_Impl::exportGrid( const Reference< beans::XPropertySet >& rGridProperties, bool bMajor, bool bExportContent )
2283 if( !rGridProperties.is() )
2284 return;
2285 std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, rGridProperties);
2286 if( bExportContent )
2288 AddAutoStyleAttribute( aPropertyStates );
2289 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS, bMajor ? XML_MAJOR : XML_MINOR );
2290 SvXMLElementExport aGrid( mrExport, XML_NAMESPACE_CHART, XML_GRID, true, true );
2292 else
2294 CollectAutoStyle( std::move(aPropertyStates) );
2296 aPropertyStates.clear();
2299 namespace
2302 //returns true if a date scale needs to be exported
2303 bool lcl_exportAxisType( const Reference< chart2::XAxis >& rChart2Axis, SvXMLExport& rExport)
2305 bool bExportDateScale = false;
2306 if( !rChart2Axis.is() )
2307 return bExportDateScale;
2309 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
2310 rExport.getSaneDefaultVersion());
2311 if ((nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) == 0) //do not export to ODF 1.3 or older
2312 return bExportDateScale;
2314 chart2::ScaleData aScale( rChart2Axis->getScaleData() );
2315 //#i25706#todo: change namespace for next ODF version
2316 sal_uInt16 nNameSpace = XML_NAMESPACE_CHART_EXT;
2318 switch(aScale.AxisType)
2320 case chart2::AxisType::CATEGORY:
2321 if( aScale.AutoDateAxis )
2323 rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_AUTO );
2324 bExportDateScale = true;
2326 else
2327 rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_TEXT );
2328 break;
2329 case chart2::AxisType::DATE:
2330 rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_DATE );
2331 bExportDateScale = true;
2332 break;
2333 default: //AUTOMATIC
2334 rExport.AddAttribute( nNameSpace, XML_AXIS_TYPE, XML_AUTO );
2335 break;
2338 return bExportDateScale;
2341 void disableLinkedNumberFormat(
2342 std::vector<XMLPropertyState>& rPropStates, const rtl::Reference<XMLPropertySetMapper>& rMapper )
2344 for (XMLPropertyState & rState : rPropStates)
2346 if (rState.mnIndex < 0 || rMapper->GetEntryCount() <= rState.mnIndex)
2347 continue;
2349 OUString aXMLName = rMapper->GetEntryXMLName(rState.mnIndex);
2351 if (aXMLName != "link-data-style-to-source")
2352 continue;
2354 // Entry found. Set the value to false and bail out.
2355 rState.maValue <<= false;
2356 return;
2359 // Entry not found. Insert a new entry for this.
2360 sal_Int32 nIndex = rMapper->GetEntryIndex(XML_NAMESPACE_CHART, u"link-data-style-to-source", 0);
2361 XMLPropertyState aState(nIndex);
2362 aState.maValue <<= false;
2363 rPropStates.push_back(aState);
2368 void SchXMLExportHelper_Impl::exportAxis(
2369 enum XMLTokenEnum eDimension,
2370 enum XMLTokenEnum eAxisName,
2371 const Reference< beans::XPropertySet >& rAxisProps,
2372 const Reference< chart2::XAxis >& rChart2Axis,
2373 const OUString& rCategoriesRange,
2374 bool bHasTitle, bool bHasMajorGrid, bool bHasMinorGrid,
2375 bool bExportContent, std::u16string_view sChartType )
2377 std::vector< XMLPropertyState > aPropertyStates;
2378 std::unique_ptr<SvXMLElementExport> pAxis;
2380 // get property states for autostyles
2381 if( rAxisProps.is() && mxExpPropMapper.is() )
2383 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
2384 mrExport.getSaneDefaultVersion());
2385 if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED
2386 && eDimension == XML_X)
2388 chart2::ScaleData aScaleData(rChart2Axis->getScaleData());
2389 bool bShiftedCatPos = aScaleData.ShiftedCategoryPosition;
2390 if (sChartType == u"com.sun.star.chart.BarDiagram" || sChartType == u"com.sun.star.chart.StockDiagram")
2392 if (!bShiftedCatPos)
2393 rAxisProps->setPropertyValue("MajorOrigin", uno::Any(0.0));
2395 else if (bShiftedCatPos)
2396 rAxisProps->setPropertyValue("MajorOrigin", uno::Any(0.5));
2399 lcl_exportNumberFormat( "NumberFormat", rAxisProps, mrExport );
2400 aPropertyStates = mxExpPropMapper->Filter(mrExport, rAxisProps);
2402 if (!maSrcShellID.isEmpty() && !maDestShellID.isEmpty() && maSrcShellID != maDestShellID)
2404 // Disable link to source number format property when pasting to
2405 // a different doc shell. These shell ID's should be both empty
2406 // during real ODF export.
2407 disableLinkedNumberFormat(aPropertyStates, mxExpPropMapper->getPropertySetMapper());
2411 bool bExportDateScale = false;
2412 if( bExportContent )
2414 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DIMENSION, eDimension );
2415 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_NAME, eAxisName );
2416 AddAutoStyleAttribute( aPropertyStates ); // write style name
2417 if( !rCategoriesRange.isEmpty() )
2418 bExportDateScale = lcl_exportAxisType( rChart2Axis, mrExport );
2420 // open axis element
2421 pAxis.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_AXIS, true, true ));
2423 else
2425 CollectAutoStyle( std::move(aPropertyStates) );
2427 aPropertyStates.clear();
2429 //date scale
2430 if( bExportDateScale )
2431 exportDateScale( rAxisProps );
2433 Reference< beans::XPropertySet > xTitleProps;
2434 Reference< beans::XPropertySet > xMajorGridProps;
2435 Reference< beans::XPropertySet > xMinorGridProps;
2436 Reference< chart::XAxis > xAxis( rAxisProps, uno::UNO_QUERY );
2437 if( xAxis.is() )
2439 xTitleProps = bHasTitle ? xAxis->getAxisTitle() : nullptr;
2440 xMajorGridProps = bHasMajorGrid ? xAxis->getMajorGrid() : nullptr;
2441 xMinorGridProps = bHasMinorGrid ? xAxis->getMinorGrid() : nullptr;
2444 // axis-title
2445 exportAxisTitle( xTitleProps , bExportContent );
2447 // categories if we have a categories chart
2448 if( bExportContent && !rCategoriesRange.isEmpty() )
2450 mrExport.AddAttribute( XML_NAMESPACE_TABLE, XML_CELL_RANGE_ADDRESS, rCategoriesRange );
2451 SvXMLElementExport aCategories( mrExport, XML_NAMESPACE_CHART, XML_CATEGORIES, true, true );
2454 // grid
2455 exportGrid( xMajorGridProps, true, bExportContent );
2456 exportGrid( xMinorGridProps, false, bExportContent );
2459 void SchXMLExportHelper_Impl::exportAxes(
2460 const Reference< chart::XDiagram > & xDiagram,
2461 const Reference< chart2::XDiagram > & xNewDiagram,
2462 bool bExportContent )
2464 SAL_WARN_IF( !xDiagram.is(), "xmloff.chart", "Invalid XDiagram as parameter" );
2465 if( ! xDiagram.is())
2466 return;
2468 // get some properties from document first
2469 bool bHasXAxis = false,
2470 bHasYAxis = false,
2471 bHasZAxis = false,
2472 bHasSecondaryXAxis = false,
2473 bHasSecondaryYAxis = false;
2474 bool bHasXAxisTitle = false,
2475 bHasYAxisTitle = false,
2476 bHasZAxisTitle = false,
2477 bHasSecondaryXAxisTitle = false,
2478 bHasSecondaryYAxisTitle = false;
2479 bool bHasXAxisMajorGrid = false,
2480 bHasXAxisMinorGrid = false,
2481 bHasYAxisMajorGrid = false,
2482 bHasYAxisMinorGrid = false,
2483 bHasZAxisMajorGrid = false,
2484 bHasZAxisMinorGrid = false;
2486 // get multiple properties using XMultiPropertySet
2487 MultiPropertySetHandler aDiagramProperties (xDiagram);
2489 aDiagramProperties.Add ("HasXAxis", bHasXAxis);
2490 aDiagramProperties.Add ("HasYAxis", bHasYAxis);
2491 aDiagramProperties.Add ("HasZAxis", bHasZAxis);
2492 aDiagramProperties.Add ("HasSecondaryXAxis", bHasSecondaryXAxis);
2493 aDiagramProperties.Add ("HasSecondaryYAxis", bHasSecondaryYAxis);
2495 aDiagramProperties.Add ("HasXAxisTitle", bHasXAxisTitle);
2496 aDiagramProperties.Add ("HasYAxisTitle", bHasYAxisTitle);
2497 aDiagramProperties.Add ("HasZAxisTitle", bHasZAxisTitle);
2498 aDiagramProperties.Add ("HasSecondaryXAxisTitle", bHasSecondaryXAxisTitle);
2499 aDiagramProperties.Add ("HasSecondaryYAxisTitle", bHasSecondaryYAxisTitle);
2501 aDiagramProperties.Add ("HasXAxisGrid", bHasXAxisMajorGrid);
2502 aDiagramProperties.Add ("HasYAxisGrid", bHasYAxisMajorGrid);
2503 aDiagramProperties.Add ("HasZAxisGrid", bHasZAxisMajorGrid);
2505 aDiagramProperties.Add ("HasXAxisHelpGrid", bHasXAxisMinorGrid);
2506 aDiagramProperties.Add ("HasYAxisHelpGrid", bHasYAxisMinorGrid);
2507 aDiagramProperties.Add ("HasZAxisHelpGrid", bHasZAxisMinorGrid);
2509 if ( ! aDiagramProperties.GetProperties ())
2511 SAL_INFO("xmloff.chart", "Required properties not found in Chart diagram");
2514 Reference< chart2::XCoordinateSystem > xCooSys( lcl_getCooSys(xNewDiagram) );
2516 // write an axis element also if the axis itself is not visible, but a grid or a title
2518 OUString aCategoriesRange;
2519 Reference< chart::XAxisSupplier > xAxisSupp( xDiagram, uno::UNO_QUERY );
2520 OUString sChartType = xDiagram->getDiagramType();
2522 // x axis
2524 Reference< css::chart2::XAxis > xNewAxis = lcl_getAxis( xCooSys, XML_X );
2525 if( xNewAxis.is() )
2527 Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getAxis(0) : nullptr, uno::UNO_QUERY );
2528 if( mbHasCategoryLabels && bExportContent )
2530 Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xNewDiagram ) );
2531 if( xCategories.is() )
2533 Reference< chart2::data::XDataSequence > xValues( xCategories->getValues() );
2534 if( xValues.is() )
2536 Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
2537 maCategoriesRange = xValues->getSourceRangeRepresentation();
2538 aCategoriesRange = lcl_ConvertRange( maCategoriesRange, xNewDoc );
2542 exportAxis( XML_X, XML_PRIMARY_X, xAxisProps, xNewAxis, aCategoriesRange, bHasXAxisTitle, bHasXAxisMajorGrid, bHasXAxisMinorGrid, bExportContent, sChartType );
2543 aCategoriesRange.clear();
2546 // secondary x axis
2548 xNewAxis = lcl_getAxis( xCooSys, XML_X, false );
2549 if( xNewAxis.is() )
2551 Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getSecondaryAxis(0) : nullptr, uno::UNO_QUERY );
2552 exportAxis( XML_X, XML_SECONDARY_X, xAxisProps, xNewAxis, aCategoriesRange, bHasSecondaryXAxisTitle, false, false, bExportContent, sChartType );
2555 // y axis
2557 xNewAxis = lcl_getAxis( xCooSys, XML_Y );
2558 if( xNewAxis.is() )
2560 Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getAxis(1) : nullptr, uno::UNO_QUERY );
2561 exportAxis( XML_Y, XML_PRIMARY_Y, xAxisProps, xNewAxis, aCategoriesRange, bHasYAxisTitle, bHasYAxisMajorGrid, bHasYAxisMinorGrid, bExportContent, sChartType );
2564 // secondary y axis
2566 xNewAxis = lcl_getAxis( xCooSys, XML_Y, false );
2567 if( xNewAxis.is() )
2569 Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getSecondaryAxis(1) : nullptr, uno::UNO_QUERY );
2570 exportAxis( XML_Y, XML_SECONDARY_Y, xAxisProps, xNewAxis, aCategoriesRange, bHasSecondaryYAxisTitle, false, false, bExportContent, sChartType );
2573 // z axis
2575 xNewAxis = lcl_getAxis( xCooSys, XML_Z );
2576 if( xNewAxis.is() )
2578 Reference< beans::XPropertySet > xAxisProps( xAxisSupp.is() ? xAxisSupp->getAxis(2) : nullptr, uno::UNO_QUERY );
2579 exportAxis( XML_Z, XML_PRIMARY_Z, xAxisProps, xNewAxis, aCategoriesRange, bHasZAxisTitle, bHasZAxisMajorGrid, bHasZAxisMinorGrid, bExportContent, sChartType );
2583 namespace
2585 bool lcl_hasNoValuesButText( const uno::Reference< chart2::data::XDataSequence >& xDataSequence )
2587 if( !xDataSequence.is() )
2588 return false;//have no data
2590 Sequence< uno::Any > aData;
2591 Reference< chart2::data::XNumericalDataSequence > xNumericalDataSequence( xDataSequence, uno::UNO_QUERY );
2592 if( xNumericalDataSequence.is() )
2594 const Sequence< double > aDoubles( xNumericalDataSequence->getNumericalData() );
2595 if (std::any_of(aDoubles.begin(), aDoubles.end(), [](double fDouble) { return !std::isnan( fDouble ); }))
2596 return false;//have double value
2598 else
2600 aData = xDataSequence->getData();
2601 double fDouble = 0.0;
2602 bool bHaveDouble = std::any_of(std::cbegin(aData), std::cend(aData),
2603 [&fDouble](const uno::Any& rData) { return (rData >>= fDouble) && !std::isnan( fDouble ); });
2604 if (bHaveDouble)
2605 return false;//have double value
2607 //no values found
2609 Reference< chart2::data::XTextualDataSequence > xTextualDataSequence( xDataSequence, uno::UNO_QUERY );
2610 if( xTextualDataSequence.is() )
2612 const uno::Sequence< OUString > aStrings( xTextualDataSequence->getTextualData() );
2613 if (std::any_of(aStrings.begin(), aStrings.end(), [](const OUString& rString) { return !rString.isEmpty(); }))
2614 return true;//have text
2616 else
2618 if( !aData.hasElements() )
2619 aData = xDataSequence->getData();
2620 OUString aString;
2621 bool bHaveText = std::any_of(std::cbegin(aData), std::cend(aData),
2622 [&aString](const uno::Any& rData) { return (rData >>= aString) && !aString.isEmpty(); });
2623 if (bHaveText)
2624 return true;//have text
2626 //no doubles and no texts
2627 return false;
2630 // ODF has the line and fill properties in a <style:style> element, which is referenced by the
2631 // <chart:data-label> element. But LibreOffice has them as special label properties of the series
2632 // or point respectively. The following method generates ODF from internal API name.
2633 void lcl_createDataLabelProperties(
2634 std::vector<XMLPropertyState>& rDataLabelPropertyStates,
2635 const Reference<beans::XPropertySet>& xPropSet,
2636 const rtl::Reference<XMLChartExportPropertyMapper>& xExpPropMapper)
2638 if (!xExpPropMapper.is() || !xPropSet.is())
2639 return;
2641 const uno::Reference<beans::XPropertySetInfo> xInfo(xPropSet->getPropertySetInfo());
2642 const uno::Reference<beans::XPropertyState> xPropState(xPropSet, uno::UNO_QUERY);
2643 const rtl::Reference<XMLPropertySetMapper>& rPropertySetMapper(
2644 xExpPropMapper->getPropertySetMapper());
2645 if (!xInfo.is() || !xPropState.is() || !rPropertySetMapper.is())
2646 return;
2648 struct API2ODFMapItem
2650 OUString sAPIName;
2651 sal_uInt16 nNameSpace; // from include/xmloff/xmlnamespace.hxx
2652 OUString sLocalName;
2653 API2ODFMapItem(OUString sAPI, const sal_uInt16 nNS, OUString sLocal)
2654 : sAPIName(std::move(sAPI))
2655 , nNameSpace(nNS)
2656 , sLocalName(std::move(sLocal))
2661 const API2ODFMapItem aLabelFoo2ODFArray[]
2662 = { API2ODFMapItem("LabelBorderStyle", XML_NAMESPACE_DRAW, "stroke"),
2663 API2ODFMapItem("LabelBorderWidth", XML_NAMESPACE_SVG, "stroke-width"),
2664 API2ODFMapItem("LabelBorderColor", XML_NAMESPACE_SVG, "stroke-color"),
2665 API2ODFMapItem("LabelBorderDashName", XML_NAMESPACE_DRAW, "stroke-dash"),
2666 API2ODFMapItem("LabelBorderTransparency", XML_NAMESPACE_SVG, "stroke-opacity"),
2667 API2ODFMapItem("LabelFillStyle", XML_NAMESPACE_DRAW, "fill"),
2668 API2ODFMapItem("LabelFillBackground", XML_NAMESPACE_DRAW, "fill-hatch-solid"),
2669 API2ODFMapItem("LabelFillHatchName", XML_NAMESPACE_DRAW, "fill-hatch-name"),
2670 API2ODFMapItem("LabelFillColor", XML_NAMESPACE_DRAW, "fill-color") };
2672 for (const auto& rIt : aLabelFoo2ODFArray)
2674 if (!xInfo->hasPropertyByName(rIt.sAPIName)
2675 || xPropState->getPropertyState(rIt.sAPIName) != beans::PropertyState_DIRECT_VALUE)
2676 continue;
2677 sal_Int32 nTargetIndex
2678 = rPropertySetMapper->GetEntryIndex(rIt.nNameSpace, rIt.sLocalName, 0);
2679 if (nTargetIndex < 0)
2680 continue;
2681 XMLPropertyState aDataLabelStateItem(nTargetIndex,
2682 xPropSet->getPropertyValue(rIt.sAPIName));
2683 rDataLabelPropertyStates.emplace_back(aDataLabelStateItem);
2686 } // anonymous namespace
2688 void SchXMLExportHelper_Impl::exportSeries(
2689 const Reference< chart2::XDiagram > & xNewDiagram,
2690 const awt::Size & rPageSize,
2691 bool bExportContent,
2692 bool bHasTwoYAxes )
2694 Reference< chart2::XCoordinateSystemContainer > xBCooSysCnt( xNewDiagram, uno::UNO_QUERY );
2695 if( ! xBCooSysCnt.is())
2696 return;
2697 Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
2699 OUString aFirstXDomainRange;
2700 OUString aFirstYDomainRange;
2702 std::vector< XMLPropertyState > aPropertyStates;
2703 std::vector< XMLPropertyState > aDataLabelPropertyStates;
2705 const Sequence< Reference< chart2::XCoordinateSystem > >
2706 aCooSysSeq( xBCooSysCnt->getCoordinateSystems());
2707 for( const auto& rCooSys : aCooSysSeq )
2709 Reference< chart2::XChartTypeContainer > xCTCnt( rCooSys, uno::UNO_QUERY );
2710 if( ! xCTCnt.is())
2711 continue;
2712 const Sequence< Reference< chart2::XChartType > > aCTSeq( xCTCnt->getChartTypes());
2713 for( const auto& rChartType : aCTSeq )
2715 Reference< chart2::XDataSeriesContainer > xDSCnt( rChartType, uno::UNO_QUERY );
2716 if( ! xDSCnt.is())
2717 continue;
2718 // note: if xDSCnt.is() then also aCTSeq[nCTIdx]
2719 OUString aChartType( rChartType->getChartType());
2720 OUString aLabelRole = rChartType->getRoleOfSequenceForSeriesLabel();
2722 // special export for stock charts
2723 if ( aChartType == "com.sun.star.chart2.CandleStickChartType" )
2725 bool bJapaneseCandleSticks = false;
2726 Reference< beans::XPropertySet > xCTProp( rChartType, uno::UNO_QUERY );
2727 if( xCTProp.is())
2728 xCTProp->getPropertyValue("Japanese") >>= bJapaneseCandleSticks;
2729 exportCandleStickSeries(
2730 xDSCnt->getDataSeries(), xNewDiagram, bJapaneseCandleSticks, bExportContent );
2731 continue;
2734 // export dataseries for current chart-type
2735 Sequence< Reference< chart2::XDataSeries > > aSeriesSeq( xDSCnt->getDataSeries());
2736 for( sal_Int32 nSeriesIdx=0; nSeriesIdx<aSeriesSeq.getLength(); ++nSeriesIdx )
2738 // export series
2739 Reference< chart2::data::XDataSource > xSource( aSeriesSeq[nSeriesIdx], uno::UNO_QUERY );
2740 if( xSource.is())
2742 std::unique_ptr<SvXMLElementExport> pSeries;
2743 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
2744 xSource->getDataSequences());
2745 sal_Int32 nMainSequenceIndex = -1;
2746 sal_Int32 nSeriesLength = 0;
2747 bool bHasMeanValueLine = false;
2748 Reference< beans::XPropertySet > xPropSet;
2749 tLabelValuesDataPair aSeriesLabelValuesPair;
2751 // search for main sequence and create a series element
2753 Reference< chart2::data::XDataSequence > xValuesSeq;
2754 Reference< chart2::data::XDataSequence > xLabelSeq;
2755 sal_Int32 nSeqIdx=0;
2756 for( ; nSeqIdx<aSeqCnt.getLength(); ++nSeqIdx )
2758 Reference< chart2::data::XDataSequence > xTempValueSeq( aSeqCnt[nSeqIdx]->getValues() );
2759 if( nMainSequenceIndex==-1 )
2761 OUString aRole;
2762 Reference< beans::XPropertySet > xSeqProp( xTempValueSeq, uno::UNO_QUERY );
2763 if( xSeqProp.is())
2764 xSeqProp->getPropertyValue("Role") >>= aRole;
2765 // "main" sequence
2766 if( aRole == aLabelRole )
2768 xValuesSeq.set( xTempValueSeq );
2769 xLabelSeq.set( aSeqCnt[nSeqIdx]->getLabel());
2770 nMainSequenceIndex = nSeqIdx;
2773 sal_Int32 nSequenceLength = (xTempValueSeq.is()? xTempValueSeq->getData().getLength() : sal_Int32(0));
2774 if( nSeriesLength < nSequenceLength )
2775 nSeriesLength = nSequenceLength;
2778 // have found the main sequence, then xValuesSeq and
2779 // xLabelSeq contain those. Otherwise both are empty
2781 sal_Int32 nAttachedAxis = chart::ChartAxisAssign::PRIMARY_Y;
2782 // get property states for autostyles
2785 xPropSet = SchXMLSeriesHelper::createOldAPISeriesPropertySet(
2786 aSeriesSeq[nSeriesIdx], mrExport.GetModel() );
2788 catch( const uno::Exception & )
2790 TOOLS_INFO_EXCEPTION("xmloff.chart", "Series not found or no XPropertySet" );
2791 continue;
2793 if( xPropSet.is())
2795 // determine attached axis
2798 Any aAny( xPropSet->getPropertyValue( "Axis" ));
2799 aAny >>= nAttachedAxis;
2801 aAny = xPropSet->getPropertyValue( "MeanValue" );
2802 aAny >>= bHasMeanValueLine;
2804 catch( const beans::UnknownPropertyException & )
2806 TOOLS_INFO_EXCEPTION("xmloff.chart", "Required property not found in DataRowProperties" );
2809 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
2810 mrExport.getSaneDefaultVersion());
2811 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
2813 lcl_exportNumberFormat( "NumberFormat", xPropSet, mrExport );
2814 lcl_exportNumberFormat( "PercentageNumberFormat", xPropSet, mrExport );
2817 if( mxExpPropMapper.is())
2818 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
2821 if( bExportContent )
2823 if( bHasTwoYAxes )
2825 if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
2826 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
2827 else
2828 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
2831 // write style name
2832 AddAutoStyleAttribute( aPropertyStates );
2834 if( xValuesSeq.is())
2835 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS,
2836 lcl_ConvertRange(
2837 xValuesSeq->getSourceRangeRepresentation(),
2838 xNewDoc ));
2839 else
2840 // #i75297# allow empty series, export empty range to have all ranges on import
2841 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, OUString());
2843 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
2844 mrExport.getSaneDefaultVersion());
2845 if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older
2847 if (xPropSet.is())
2849 Any aAny = xPropSet->getPropertyValue("ShowLegendEntry");
2850 if (!aAny.get<bool>())
2852 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDE_LEGEND, OUString::boolean(true));
2857 if (xLabelSeq.is())
2859 // Check if the label is direct string value rather than a reference.
2860 bool bHasString = false;
2861 uno::Reference<beans::XPropertySet> xLSProp(xLabelSeq, uno::UNO_QUERY);
2862 if (xLSProp.is())
2866 xLSProp->getPropertyValue("HasStringLabel") >>= bHasString;
2868 catch (const beans::UnknownPropertyException&) {}
2871 OUString aRange = xLabelSeq->getSourceRangeRepresentation();
2873 if (bHasString)
2875 mrExport.AddAttribute(
2876 XML_NAMESPACE_LO_EXT, XML_LABEL_STRING, aRange);
2878 else
2880 mrExport.AddAttribute(
2881 XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS,
2882 lcl_ConvertRange(
2883 xLabelSeq->getSourceRangeRepresentation(), xNewDoc));
2887 if( xLabelSeq.is() || xValuesSeq.is() )
2888 aSeriesLabelValuesPair = tLabelValuesDataPair( xLabelSeq, xValuesSeq );
2890 // chart-type for mixed types
2891 enum XMLTokenEnum eCTToken(
2892 SchXMLTools::getTokenByChartType( aChartType, false /* bUseOldNames */ ));
2893 //@todo: get token for current charttype
2894 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_CLASS,
2895 mrExport.GetNamespaceMap().GetQNameByKey(
2896 XML_NAMESPACE_CHART, GetXMLToken( eCTToken )));
2898 // open series element until end of for loop
2899 pSeries.reset(new SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true ));
2901 else // autostyles
2903 CollectAutoStyle( std::move(aPropertyStates) );
2905 // remove property states for autostyles
2906 aPropertyStates.clear();
2910 // export domain elements if we have a series parent element
2911 if( pSeries )
2913 // domain elements
2914 if( bExportContent )
2916 bool bIsScatterChart = aChartType == "com.sun.star.chart2.ScatterChartType";
2917 bool bIsBubbleChart = aChartType == "com.sun.star.chart2.BubbleChartType";
2918 Reference< chart2::data::XDataSequence > xYValuesForBubbleChart;
2919 if( bIsBubbleChart )
2921 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, "values-y" ) );
2922 if( xSequence.is() )
2924 xYValuesForBubbleChart = xSequence->getValues();
2925 if( !lcl_exportDomainForThisSequence( xYValuesForBubbleChart, aFirstYDomainRange, mrExport ) )
2926 xYValuesForBubbleChart = nullptr;
2929 if( bIsScatterChart || bIsBubbleChart )
2931 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, "values-x" ) );
2932 if( xSequence.is() )
2934 Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
2935 if( lcl_exportDomainForThisSequence( xValues, aFirstXDomainRange, mrExport ) )
2936 m_aDataSequencesToExport.emplace_back(
2937 uno::Reference< chart2::data::XDataSequence >(), xValues );
2939 else if( nSeriesIdx==0 )
2941 //might be that the categories are used as x-values (e.g. for date axis) -> export them accordingly
2942 Reference< chart2::data::XLabeledDataSequence > xCategories( lcl_getCategories( xNewDiagram ) );
2943 if( xCategories.is() )
2945 Reference< chart2::data::XDataSequence > xValues( xCategories->getValues() );
2946 if( !lcl_hasNoValuesButText( xValues ) )
2947 lcl_exportDomainForThisSequence( xValues, aFirstXDomainRange, mrExport );
2951 if( xYValuesForBubbleChart.is() )
2952 m_aDataSequencesToExport.emplace_back(
2953 uno::Reference< chart2::data::XDataSequence >(), xYValuesForBubbleChart );
2957 // add sequences for main sequence after domain sequences,
2958 // so that the export of the local table has the correct order
2959 if( bExportContent &&
2960 (aSeriesLabelValuesPair.first.is() || aSeriesLabelValuesPair.second.is()))
2961 m_aDataSequencesToExport.push_back( aSeriesLabelValuesPair );
2963 // statistical objects:
2964 // regression curves and mean value lines
2965 if( bHasMeanValueLine &&
2966 xPropSet.is() &&
2967 mxExpPropMapper.is() )
2969 Reference< beans::XPropertySet > xStatProp;
2972 Any aPropAny( xPropSet->getPropertyValue( "DataMeanValueProperties" ));
2973 aPropAny >>= xStatProp;
2975 catch( const uno::Exception & )
2977 TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during Export of series - optional DataMeanValueProperties not available" );
2980 if( xStatProp.is() )
2982 aPropertyStates = mxExpPropMapper->Filter(mrExport, xStatProp);
2984 if( !aPropertyStates.empty() )
2986 // write element
2987 if( bExportContent )
2989 // add style name attribute
2990 AddAutoStyleAttribute( aPropertyStates );
2992 SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_MEAN_VALUE, true, true );
2994 else // autostyles
2996 CollectAutoStyle( std::move(aPropertyStates) );
3002 if( xPropSet.is() &&
3003 mxExpPropMapper.is() )
3005 exportRegressionCurve( aSeriesSeq[nSeriesIdx], rPageSize, bExportContent );
3008 exportErrorBar( xPropSet,false, bExportContent ); // X ErrorBar
3009 exportErrorBar( xPropSet,true, bExportContent ); // Y ErrorBar
3011 exportDataPoints(
3012 uno::Reference< beans::XPropertySet >( aSeriesSeq[nSeriesIdx], uno::UNO_QUERY ),
3013 nSeriesLength, xNewDiagram, bExportContent );
3015 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
3016 mrExport.getSaneDefaultVersion());
3018 // create <chart:data-label> child element if needed.
3019 if (xPropSet.is() && mxExpPropMapper.is())
3021 // Generate style for <chart:data-label> child element
3022 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
3024 lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet,
3025 mxExpPropMapper);
3028 if (bExportContent)
3030 if (!aDataLabelPropertyStates.empty())
3032 // write style name
3033 AddAutoStyleAttribute(aDataLabelPropertyStates);
3034 // Further content does currently not exist for a <chart:data-label>
3035 // element as child of a <chart:series>.
3036 SvXMLElementExport(mrExport, XML_NAMESPACE_CHART, XML_DATA_LABEL, true,
3037 true);
3040 else
3042 // add the style for the to be <chart:data-label> too
3043 if (!aDataLabelPropertyStates.empty())
3044 CollectAutoStyle(std::move(aDataLabelPropertyStates));
3046 aDataLabelPropertyStates.clear();
3048 if (bExportContent && nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older
3050 Sequence< OUString > aSupportedMappings = rChartType->getSupportedPropertyRoles();
3051 exportPropertyMapping( xSource, aSupportedMappings );
3054 // close series element
3055 pSeries.reset();
3058 aPropertyStates.clear();
3059 aDataLabelPropertyStates.clear();
3064 void SchXMLExportHelper_Impl::exportPropertyMapping(
3065 const Reference< chart2::data::XDataSource > & xSource, const Sequence< OUString >& rSupportedMappings )
3067 Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
3068 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
3069 xSource->getDataSequences());
3071 for(const auto& rSupportedMapping : rSupportedMappings)
3073 Reference< chart2::data::XLabeledDataSequence > xSequence( lcl_getDataSequenceByRole( aSeqCnt, rSupportedMapping ) );
3074 if(xSequence.is())
3076 Reference< chart2::data::XDataSequence > xValues( xSequence->getValues() );
3077 if( xValues.is())
3079 mrExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_PROPERTY, rSupportedMapping);
3080 mrExport.AddAttribute( XML_NAMESPACE_LO_EXT, XML_CELL_RANGE_ADDRESS,
3081 lcl_ConvertRange(
3082 xValues->getSourceRangeRepresentation(),
3083 xNewDoc ));
3084 SvXMLElementExport( mrExport, XML_NAMESPACE_LO_EXT, XML_PROPERTY_MAPPING, true, true );
3086 // register range for data table export
3087 m_aDataSequencesToExport.emplace_back(
3088 uno::Reference< chart2::data::XDataSequence >(), xValues );
3094 void SchXMLExportHelper_Impl::exportRegressionCurve(
3095 const Reference< chart2::XDataSeries >& xSeries,
3096 const awt::Size& rPageSize,
3097 bool bExportContent )
3099 OSL_ASSERT( mxExpPropMapper.is());
3101 Reference< chart2::XRegressionCurveContainer > xRegressionCurveContainer( xSeries, uno::UNO_QUERY );
3102 if( !xRegressionCurveContainer.is() )
3103 return;
3105 const Sequence< Reference< chart2::XRegressionCurve > > aRegCurveSeq = xRegressionCurveContainer->getRegressionCurves();
3107 for( const auto& xRegCurve : aRegCurveSeq )
3109 std::vector< XMLPropertyState > aEquationPropertyStates;
3110 if (!xRegCurve.is())
3111 continue;
3113 Reference< beans::XPropertySet > xProperties( xRegCurve , uno::UNO_QUERY );
3114 if( !xProperties.is() )
3115 continue;
3117 Reference< lang::XServiceName > xServiceName( xProperties, uno::UNO_QUERY );
3118 if( !xServiceName.is() )
3119 continue;
3121 bool bShowEquation = false;
3122 bool bShowRSquared = false;
3123 bool bExportEquation = false;
3125 OUString aService = xServiceName->getServiceName();
3127 std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, xProperties);
3129 // Add service name (which is regression type)
3130 sal_Int32 nIndex = GetPropertySetMapper()->FindEntryIndex(XML_SCH_CONTEXT_SPECIAL_REGRESSION_TYPE);
3131 XMLPropertyState property(nIndex, uno::Any(aService));
3132 aPropertyStates.push_back(property);
3134 Reference< beans::XPropertySet > xEquationProperties;
3135 xEquationProperties.set( xRegCurve->getEquationProperties() );
3136 if( xEquationProperties.is())
3138 xEquationProperties->getPropertyValue( "ShowEquation") >>= bShowEquation;
3139 xEquationProperties->getPropertyValue( "ShowCorrelationCoefficient") >>= bShowRSquared;
3141 bExportEquation = ( bShowEquation || bShowRSquared );
3142 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion(
3143 mrExport.getSaneDefaultVersion());
3144 if (nCurrentVersion < SvtSaveOptions::ODFSVER_012)
3146 bExportEquation=false;
3148 if( bExportEquation )
3150 // number format
3151 sal_Int32 nNumberFormat = 0;
3152 if( (xEquationProperties->getPropertyValue("NumberFormat") >>= nNumberFormat ) &&
3153 nNumberFormat != -1 )
3155 mrExport.addDataStyle( nNumberFormat );
3157 aEquationPropertyStates = mxExpPropMapper->Filter(mrExport, xEquationProperties);
3161 if( !aPropertyStates.empty() || bExportEquation )
3163 // write element
3164 if( bExportContent )
3166 // add style name attribute
3167 if( !aPropertyStates.empty())
3169 AddAutoStyleAttribute( aPropertyStates );
3172 SvXMLElementExport aRegressionExport( mrExport, XML_NAMESPACE_CHART, XML_REGRESSION_CURVE, true, true );
3173 if( bExportEquation )
3175 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DISPLAY_EQUATION, (bShowEquation ? XML_TRUE : XML_FALSE) );
3176 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DISPLAY_R_SQUARE, (bShowRSquared ? XML_TRUE : XML_FALSE) );
3178 // export position
3179 chart2::RelativePosition aRelativePosition;
3180 if( xEquationProperties->getPropertyValue( "RelativePosition" ) >>= aRelativePosition )
3182 double fX = aRelativePosition.Primary * rPageSize.Width;
3183 double fY = aRelativePosition.Secondary * rPageSize.Height;
3184 awt::Point aPos;
3185 aPos.X = static_cast< sal_Int32 >( ::rtl::math::round( fX ));
3186 aPos.Y = static_cast< sal_Int32 >( ::rtl::math::round( fY ));
3187 addPosition( aPos );
3190 if( !aEquationPropertyStates.empty())
3192 AddAutoStyleAttribute( aEquationPropertyStates );
3195 SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_EQUATION, true, true );
3198 else // autostyles
3200 if( !aPropertyStates.empty())
3202 CollectAutoStyle( std::move(aPropertyStates) );
3204 if( bExportEquation && !aEquationPropertyStates.empty())
3206 CollectAutoStyle( std::move(aEquationPropertyStates) );
3213 void SchXMLExportHelper_Impl::exportErrorBar( const Reference<beans::XPropertySet> &xSeriesProp,
3214 bool bYError, bool bExportContent )
3216 assert(mxExpPropMapper.is());
3218 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentVersion(
3219 mrExport.getSaneDefaultVersion());
3221 /// Don't export X ErrorBars for older ODF versions.
3222 if (!bYError && nCurrentVersion < SvtSaveOptions::ODFSVER_012)
3223 return;
3225 if (!xSeriesProp.is())
3226 return;
3228 bool bNegative = false, bPositive = false;
3229 sal_Int32 nErrorBarStyle = chart::ErrorBarStyle::NONE;
3230 Reference< beans::XPropertySet > xErrorBarProp;
3234 Any aAny = xSeriesProp->getPropertyValue( bYError ? OUString("ErrorBarY") : OUString("ErrorBarX") );
3235 aAny >>= xErrorBarProp;
3237 if ( xErrorBarProp.is() )
3239 aAny = xErrorBarProp->getPropertyValue("ShowNegativeError" );
3240 aAny >>= bNegative;
3242 aAny = xErrorBarProp->getPropertyValue("ShowPositiveError" );
3243 aAny >>= bPositive;
3245 aAny = xErrorBarProp->getPropertyValue("ErrorBarStyle" );
3246 aAny >>= nErrorBarStyle;
3249 catch( const beans::UnknownPropertyException & )
3251 TOOLS_INFO_EXCEPTION("xmloff.chart", "Required property not found in DataRowProperties" );
3254 if( !(nErrorBarStyle != chart::ErrorBarStyle::NONE && (bNegative || bPositive)))
3255 return;
3257 if( bExportContent && nErrorBarStyle == chart::ErrorBarStyle::FROM_DATA )
3259 // register data ranges for error bars for export in local table
3260 ::std::vector< Reference< chart2::data::XDataSequence > > aErrorBarSequences(
3261 lcl_getErrorBarSequences( xErrorBarProp ));
3262 for( const auto& rErrorBarSequence : aErrorBarSequences )
3264 m_aDataSequencesToExport.emplace_back(
3265 uno::Reference< chart2::data::XDataSequence >(), rErrorBarSequence );
3269 std::vector<XMLPropertyState> aPropertyStates = mxExpPropMapper->Filter(mrExport, xErrorBarProp);
3271 if( aPropertyStates.empty() )
3272 return;
3274 // write element
3275 if( bExportContent )
3277 // add style name attribute
3278 AddAutoStyleAttribute( aPropertyStates );
3280 if (nCurrentVersion >= SvtSaveOptions::ODFSVER_012)
3281 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_DIMENSION, bYError ? XML_Y : XML_X );//#i114149#
3282 SvXMLElementExport( mrExport, XML_NAMESPACE_CHART, XML_ERROR_INDICATOR, true, true );
3284 else // autostyles
3286 CollectAutoStyle( std::move(aPropertyStates) );
3290 void SchXMLExportHelper_Impl::exportCandleStickSeries(
3291 const Sequence< Reference< chart2::XDataSeries > > & aSeriesSeq,
3292 const Reference< chart2::XDiagram > & xDiagram,
3293 bool bJapaneseCandleSticks,
3294 bool bExportContent )
3297 for( const auto& xSeries : aSeriesSeq )
3299 sal_Int32 nAttachedAxis = lcl_isSeriesAttachedToFirstAxis( xSeries )
3300 ? chart::ChartAxisAssign::PRIMARY_Y
3301 : chart::ChartAxisAssign::SECONDARY_Y;
3303 Reference< chart2::data::XDataSource > xSource( xSeries, uno::UNO_QUERY );
3304 if( xSource.is())
3306 // export series in correct order (as we don't store roles)
3307 // with japanese candlesticks: open, low, high, close
3308 // otherwise: low, high, close
3309 Sequence< Reference< chart2::data::XLabeledDataSequence > > aSeqCnt(
3310 xSource->getDataSequences());
3312 sal_Int32 nSeriesLength =
3313 lcl_getSequenceLengthByRole( aSeqCnt, "values-last");
3315 if( bExportContent )
3317 Reference< chart2::XChartDocument > xNewDoc( mrExport.GetModel(), uno::UNO_QUERY );
3318 //@todo: export data points
3320 //TODO: moggi: same code three times
3321 // open
3322 if( bJapaneseCandleSticks )
3324 tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole(
3325 aSeqCnt, "values-first", xNewDoc, m_aDataSequencesToExport ));
3326 if( !aRanges.second.isEmpty())
3327 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second );
3328 if( !aRanges.first.isEmpty())
3329 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first );
3330 if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
3331 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
3332 else
3333 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
3334 SvXMLElementExport aOpenSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true );
3335 // export empty data points
3336 exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent );
3339 // low
3341 tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole(
3342 aSeqCnt, "values-min", xNewDoc, m_aDataSequencesToExport ));
3343 if( !aRanges.second.isEmpty())
3344 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second );
3345 if( !aRanges.first.isEmpty())
3346 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first );
3347 if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
3348 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
3349 else
3350 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
3351 SvXMLElementExport aLowSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true );
3352 // export empty data points
3353 exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent );
3356 // high
3358 tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole(
3359 aSeqCnt, "values-max", xNewDoc, m_aDataSequencesToExport ));
3360 if( !aRanges.second.isEmpty())
3361 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second );
3362 if( !aRanges.first.isEmpty())
3363 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first );
3364 if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
3365 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
3366 else
3367 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
3368 SvXMLElementExport aHighSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true );
3369 // export empty data points
3370 exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent );
3373 // close
3375 tLabelAndValueRange aRanges( lcl_getLabelAndValueRangeByRole(
3376 aSeqCnt, "values-last", xNewDoc, m_aDataSequencesToExport ));
3377 if( !aRanges.second.isEmpty())
3378 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_VALUES_CELL_RANGE_ADDRESS, aRanges.second );
3379 if( !aRanges.first.isEmpty())
3380 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_LABEL_CELL_ADDRESS, aRanges.first );
3381 if( nAttachedAxis == chart::ChartAxisAssign::SECONDARY_Y )
3382 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_SECONDARY_Y );
3383 else
3384 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_ATTACHED_AXIS, XML_PRIMARY_Y );
3385 SvXMLElementExport aCloseSeries( mrExport, XML_NAMESPACE_CHART, XML_SERIES, true, true );
3386 // export empty data points
3387 exportDataPoints( nullptr, nSeriesLength, xDiagram, bExportContent );
3390 else // autostyles
3392 // for close series
3394 // remove property states for autostyles
3399 void SchXMLExportHelper_Impl::exportDataPoints(
3400 const uno::Reference< beans::XPropertySet > & xSeriesProperties,
3401 sal_Int32 nSeriesLength,
3402 const uno::Reference< chart2::XDiagram > & xDiagram,
3403 bool bExportContent )
3405 // data-points
3407 // write data-points only if they contain autostyles
3408 // objects with equal autostyles are grouped using the attribute
3409 // repeat="number"
3411 // Note: if only the nth data-point has autostyles there is an element
3412 // without style and repeat="n-1" attribute written in advance.
3414 // the sequence aDataPointSeq contains indices of data-points that
3415 // do have own attributes. This increases the performance substantially.
3417 // more performant version for #93600#
3418 if (!mxExpPropMapper.is())
3419 return;
3421 uno::Reference< chart2::XDataSeries > xSeries( xSeriesProperties, uno::UNO_QUERY );
3423 std::vector< XMLPropertyState > aPropertyStates;
3424 std::vector<XMLPropertyState> aDataLabelPropertyStates;
3426 bool bVaryColorsByPoint = false;
3427 Sequence< sal_Int32 > aDataPointSeq;
3428 Sequence<sal_Int32> deletedLegendEntriesSeq;
3429 if( xSeriesProperties.is())
3431 xSeriesProperties->getPropertyValue("AttributedDataPoints") >>= aDataPointSeq;
3432 xSeriesProperties->getPropertyValue("VaryColorsByPoint") >>= bVaryColorsByPoint;
3434 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
3435 mrExport.getSaneDefaultVersion());
3436 if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED) // do not export to ODF 1.3 or older
3437 xSeriesProperties->getPropertyValue("DeletedLegendEntries") >>= deletedLegendEntriesSeq;
3440 sal_Int32 nSize = aDataPointSeq.getLength();
3441 SAL_WARN_IF( nSize > nSeriesLength, "xmloff.chart", "Too many point attributes" );
3443 const sal_Int32 * pPoints = aDataPointSeq.getConstArray();
3444 sal_Int32 nElement;
3445 Reference< chart2::XColorScheme > xColorScheme;
3446 if( xDiagram.is())
3447 xColorScheme.set( xDiagram->getDefaultColorScheme());
3449 ::std::vector< SchXMLDataPointStruct > aDataPointVector;
3451 sal_Int32 nLastIndex = -1;
3453 // collect elements
3454 if( bVaryColorsByPoint && xColorScheme.is() )
3456 o3tl::sorted_vector< sal_Int32 > aAttrPointSet;
3457 aAttrPointSet.reserve(aDataPointSeq.getLength());
3458 for (auto p = pPoints; p < pPoints + aDataPointSeq.getLength(); ++p)
3459 aAttrPointSet.insert( *p );
3460 const auto aEndIt = aAttrPointSet.end();
3461 for( nElement = 0; nElement < nSeriesLength; ++nElement )
3463 aPropertyStates.clear();
3464 aDataLabelPropertyStates.clear();
3465 uno::Reference< beans::XPropertySet > xPropSet;
3466 bool bExportNumFmt = false;
3467 if( aAttrPointSet.find( nElement ) != aEndIt )
3471 xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
3472 xSeries, nElement, mrExport.GetModel() );
3473 bExportNumFmt = true;
3475 catch( const uno::Exception & )
3477 TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during Export of data point" );
3480 else
3482 // property set only containing the color
3483 xPropSet.set( new ::xmloff::chart::ColorPropertySet(
3484 ::Color(ColorTransparency, xColorScheme->getColorByIndex( nElement ))));
3486 SAL_WARN_IF( !xPropSet.is(), "xmloff.chart", "Pie Segments should have properties" );
3487 if( xPropSet.is())
3489 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
3490 mrExport.getSaneDefaultVersion());
3491 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012 && bExportNumFmt)
3493 lcl_exportNumberFormat( "NumberFormat", xPropSet, mrExport );
3494 lcl_exportNumberFormat( "PercentageNumberFormat", xPropSet, mrExport );
3497 // Generate style for <chart:data-label> child element
3498 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
3500 lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet,
3501 mxExpPropMapper);
3504 if (nCurrentODFVersion & SvtSaveOptions::ODFSVER_EXTENDED)
3506 sal_Int32 nPlacement = 0;
3507 xPropSet->getPropertyValue("LabelPlacement") >>= nPlacement;
3508 if (nPlacement == chart::DataLabelPlacement::CUSTOM)
3510 xPropSet->setPropertyValue("LabelPlacement",
3511 uno::Any(chart::DataLabelPlacement::OUTSIDE));
3515 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
3516 if (!aPropertyStates.empty() || !aDataLabelPropertyStates.empty())
3518 if (bExportContent)
3520 // write data-point with style
3521 SchXMLDataPointStruct aPoint;
3522 if (!aPropertyStates.empty())
3524 SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart",
3525 "Autostyle queue empty!");
3526 aPoint.maStyleName = maAutoStyleNameQueue.front();
3527 maAutoStyleNameQueue.pop();
3529 if (!aDataLabelPropertyStates.empty())
3531 SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart",
3532 "Autostyle queue empty!");
3533 aPoint.msDataLabelStyleName = maAutoStyleNameQueue.front();
3534 maAutoStyleNameQueue.pop();
3536 if(bExportNumFmt)
3537 aPoint.mCustomLabel = lcl_getCustomLabelField(mrExport, nElement, xSeries);
3538 aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(mrExport, nElement, xSeries);
3540 aDataPointVector.push_back( aPoint );
3542 else
3544 if (!aPropertyStates.empty())
3545 CollectAutoStyle(std::move(aPropertyStates));
3546 if (!aDataLabelPropertyStates.empty())
3547 CollectAutoStyle(std::move(aDataLabelPropertyStates));
3552 SAL_WARN_IF( bExportContent && (static_cast<sal_Int32>(aDataPointVector.size()) != nSeriesLength), "xmloff.chart", "not enough data points on content export" );
3554 else
3556 for( sal_Int32 nCurrIndex : std::as_const(aDataPointSeq) )
3558 aPropertyStates.clear();
3559 aDataLabelPropertyStates.clear();
3560 //assuming sorted indices in pPoints
3562 if( nCurrIndex<0 || nCurrIndex>=nSeriesLength )
3563 break;
3565 // write leading empty data points
3566 if( nCurrIndex - nLastIndex > 1 )
3568 SchXMLDataPointStruct aPoint;
3569 aPoint.mnRepeat = nCurrIndex - nLastIndex - 1;
3570 aDataPointVector.push_back( aPoint );
3573 uno::Reference< beans::XPropertySet > xPropSet;
3574 // get property states
3577 xPropSet = SchXMLSeriesHelper::createOldAPIDataPointPropertySet(
3578 xSeries, nCurrIndex, mrExport.GetModel() );
3580 catch( const uno::Exception & )
3582 TOOLS_INFO_EXCEPTION("xmloff.chart", "Exception caught during Export of data point" );
3584 if( xPropSet.is())
3586 const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion(
3587 mrExport.getSaneDefaultVersion());
3588 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
3590 lcl_exportNumberFormat( "NumberFormat", xPropSet, mrExport );
3591 lcl_exportNumberFormat( "PercentageNumberFormat", xPropSet, mrExport );
3594 // Generate style for <chart:data-label> child element
3595 if (nCurrentODFVersion >= SvtSaveOptions::ODFSVER_012)
3597 lcl_createDataLabelProperties(aDataLabelPropertyStates, xPropSet,
3598 mxExpPropMapper);
3601 aPropertyStates = mxExpPropMapper->Filter(mrExport, xPropSet);
3603 if (!aPropertyStates.empty() || !aDataLabelPropertyStates.empty())
3605 if( bExportContent )
3607 // write data-point with style
3608 SchXMLDataPointStruct aPoint;
3609 if (!aPropertyStates.empty())
3611 SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart",
3612 "Autostyle queue empty!");
3613 aPoint.maStyleName = maAutoStyleNameQueue.front();
3614 maAutoStyleNameQueue.pop();
3616 aPoint.mCustomLabel = lcl_getCustomLabelField(mrExport, nCurrIndex, xSeries);
3617 aPoint.mCustomLabelPos = lcl_getCustomLabelPosition(mrExport, nCurrIndex, xSeries);
3618 if (!aDataLabelPropertyStates.empty())
3620 SAL_WARN_IF(maAutoStyleNameQueue.empty(), "xmloff.chart",
3621 "Autostyle queue empty!");
3622 aPoint.msDataLabelStyleName = maAutoStyleNameQueue.front();
3623 maAutoStyleNameQueue.pop();
3626 aDataPointVector.push_back( aPoint );
3627 nLastIndex = nCurrIndex;
3629 else
3631 if (!aPropertyStates.empty())
3632 CollectAutoStyle(std::move(aPropertyStates));
3633 if (!aDataLabelPropertyStates.empty())
3634 CollectAutoStyle(std::move(aDataLabelPropertyStates));
3636 continue;
3640 // if we get here the property states are empty
3641 SchXMLDataPointStruct aPoint;
3642 aDataPointVector.push_back( aPoint );
3644 nLastIndex = nCurrIndex;
3646 // final empty elements
3647 sal_Int32 nRepeat = nSeriesLength - nLastIndex - 1;
3648 if( nRepeat > 0 )
3650 SchXMLDataPointStruct aPoint;
3651 aPoint.mnRepeat = nRepeat;
3652 aDataPointVector.push_back( aPoint );
3656 if (!bExportContent)
3657 return;
3659 // write elements (merge equal ones)
3660 SchXMLDataPointStruct aPoint;
3661 SchXMLDataPointStruct aLastPoint;
3663 // initialize so that it doesn't matter if
3664 // the element is counted in the first iteration
3665 aLastPoint.mnRepeat = 0;
3666 sal_Int32 nIndex = 0;
3667 for( const auto& rPoint : aDataPointVector )
3669 aPoint = rPoint;
3671 if (aPoint.maStyleName == aLastPoint.maStyleName
3672 && aLastPoint.mCustomLabel.maFields.getLength() < 1
3673 && aLastPoint.mCustomLabelPos.Primary == 0.0
3674 && aLastPoint.mCustomLabelPos.Secondary == 0.0
3675 && aPoint.msDataLabelStyleName == aLastPoint.msDataLabelStyleName)
3676 aPoint.mnRepeat += aLastPoint.mnRepeat;
3677 else if( aLastPoint.mnRepeat > 0 )
3679 // write last element
3680 if( !aLastPoint.maStyleName.isEmpty() )
3681 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aLastPoint.maStyleName );
3683 if( aLastPoint.mnRepeat > 1 )
3684 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_REPEATED,
3685 OUString::number( aLastPoint.mnRepeat ));
3687 for (const auto& deletedLegendEntry : std::as_const(deletedLegendEntriesSeq))
3689 if (nIndex == deletedLegendEntry)
3691 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDE_LEGEND, OUString::boolean(true));
3692 break;
3695 nIndex++;
3696 exportCustomLabelPosition(aLastPoint.mCustomLabelPos); // adds attributes
3697 SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, true, true );
3698 exportCustomLabel(aLastPoint);
3700 aLastPoint = aPoint;
3702 // write last element if it hasn't been written in last iteration
3703 if( aPoint.maStyleName != aLastPoint.maStyleName )
3704 return;
3706 if( !aLastPoint.maStyleName.isEmpty() )
3707 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, aLastPoint.maStyleName );
3709 if( aLastPoint.mnRepeat > 1 )
3710 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_REPEATED,
3711 OUString::number( aLastPoint.mnRepeat ));
3713 for (const auto& deletedLegendEntry : std::as_const(deletedLegendEntriesSeq))
3715 if (nIndex == deletedLegendEntry)
3717 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDE_LEGEND, OUString::boolean(true));
3718 break;
3722 exportCustomLabelPosition(aLastPoint.mCustomLabelPos); // adds attributes
3723 SvXMLElementExport aPointElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_POINT, true, true );
3724 exportCustomLabel(aLastPoint);
3727 void SchXMLExportHelper_Impl::exportCustomLabel(const SchXMLDataPointStruct& rPoint)
3729 if (rPoint.mCustomLabel.maFields.getLength() < 1 && rPoint.msDataLabelStyleName.isEmpty())
3730 return; // nothing to export
3732 if (!rPoint.msDataLabelStyleName.isEmpty())
3733 mrExport.AddAttribute(XML_NAMESPACE_CHART, XML_STYLE_NAME, rPoint.msDataLabelStyleName);
3735 if (rPoint.mCustomLabel.mbDataLabelsRange)
3737 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_LABELS_CELL_RANGE, rPoint.mCustomLabel.maRange);
3738 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_DATA_LABEL_GUID, rPoint.mCustomLabel.maGuid);
3740 // TODO svg:x and svg:y for <chart:data-label>
3741 SvXMLElementExport aLabelElem( mrExport, XML_NAMESPACE_CHART, XML_DATA_LABEL, true, true);
3742 SvXMLElementExport aPara( mrExport, XML_NAMESPACE_TEXT, XML_P, true, false );
3744 for (const Reference<chart2::XDataPointCustomLabelField>& label : rPoint.mCustomLabel.maFields)
3746 // TODO add style
3747 SvXMLElementExport aSpan( mrExport, XML_NAMESPACE_TEXT, XML_SPAN, true, false);
3748 mrExport.GetDocHandler()->characters(label->getString());
3752 void SchXMLExportHelper_Impl::exportCustomLabelPosition( const chart2::RelativePosition & xCustomLabelPosition)
3754 if( xCustomLabelPosition.Primary == 0.0 && xCustomLabelPosition.Secondary == 0.0 )
3755 return; // nothing to export
3757 OUStringBuffer aCustomLabelPosString;
3758 ::sax::Converter::convertDouble(aCustomLabelPosString, xCustomLabelPosition.Primary);
3759 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_CUSTOM_LABEL_POS_X, aCustomLabelPosString.makeStringAndClear());
3761 ::sax::Converter::convertDouble(aCustomLabelPosString, xCustomLabelPosition.Secondary);
3762 mrExport.AddAttribute(XML_NAMESPACE_LO_EXT, XML_CUSTOM_LABEL_POS_Y, aCustomLabelPosString.makeStringAndClear());
3765 void SchXMLExportHelper_Impl::addPosition( const awt::Point & rPosition )
3767 mrExport.GetMM100UnitConverter().convertMeasureToXML(
3768 msStringBuffer, rPosition.X );
3769 msString = msStringBuffer.makeStringAndClear();
3770 mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_X, msString );
3772 mrExport.GetMM100UnitConverter().convertMeasureToXML(
3773 msStringBuffer, rPosition.Y );
3774 msString = msStringBuffer.makeStringAndClear();
3775 mrExport.AddAttribute( XML_NAMESPACE_SVG, XML_Y, msString );
3778 void SchXMLExportHelper_Impl::addPosition( const Reference< drawing::XShape >& xShape )
3780 if( xShape.is())
3781 addPosition( xShape->getPosition());
3784 void SchXMLExportHelper_Impl::addSize( const awt::Size & rSize, bool bIsOOoNamespace)
3786 mrExport.GetMM100UnitConverter().convertMeasureToXML(
3787 msStringBuffer, rSize.Width );
3788 msString = msStringBuffer.makeStringAndClear();
3789 mrExport.AddAttribute( bIsOOoNamespace ? XML_NAMESPACE_CHART_EXT : XML_NAMESPACE_SVG , XML_WIDTH, msString );
3791 mrExport.GetMM100UnitConverter().convertMeasureToXML(
3792 msStringBuffer, rSize.Height);
3793 msString = msStringBuffer.makeStringAndClear();
3794 mrExport.AddAttribute( bIsOOoNamespace ? XML_NAMESPACE_CHART_EXT : XML_NAMESPACE_SVG, XML_HEIGHT, msString );
3797 void SchXMLExportHelper_Impl::addSize( const Reference< drawing::XShape >& xShape )
3799 if( xShape.is())
3800 addSize( xShape->getSize() );
3803 awt::Size SchXMLExportHelper_Impl::getPageSize( const Reference< chart2::XChartDocument > & xChartDoc )
3805 awt::Size aSize( 8000, 7000 );
3806 uno::Reference< embed::XVisualObject > xVisualObject( xChartDoc, uno::UNO_QUERY );
3807 SAL_WARN_IF( !xVisualObject.is(), "xmloff.chart", "need XVisualObject for page size" );
3808 if( xVisualObject.is() )
3809 aSize = xVisualObject->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT );
3811 return aSize;
3814 void SchXMLExportHelper_Impl::CollectAutoStyle( std::vector< XMLPropertyState >&& aStates )
3816 if( !aStates.empty() )
3817 maAutoStyleNameQueue.push( mrAutoStylePool.Add( XmlStyleFamily::SCH_CHART_ID, std::move(aStates) ));
3820 void SchXMLExportHelper_Impl::AddAutoStyleAttribute( const std::vector< XMLPropertyState >& aStates )
3822 if( !aStates.empty() )
3824 SAL_WARN_IF( maAutoStyleNameQueue.empty(), "xmloff.chart", "Autostyle queue empty!" );
3826 mrExport.AddAttribute( XML_NAMESPACE_CHART, XML_STYLE_NAME, maAutoStyleNameQueue.front() );
3827 maAutoStyleNameQueue.pop();
3831 void SchXMLExportHelper_Impl::exportText( const OUString& rText )
3833 SchXMLTools::exportText( mrExport, rText, false/*bConvertTabsLFs*/ );
3837 SchXMLExport::SchXMLExport(const Reference<uno::XComponentContext>& xContext,
3838 OUString const& implementationName, SvXMLExportFlags nExportFlags)
3839 : SvXMLExport(xContext, implementationName, util::MeasureUnit::CM, ::xmloff::token::XML_CHART,
3840 nExportFlags)
3841 , maAutoStylePool(new SchXMLAutoStylePoolP(*this))
3842 , maExportHelper(new SchXMLExportHelper(*this, *maAutoStylePool))
3844 if (getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)
3845 GetNamespaceMap_().Add( GetXMLToken(XML_NP_CHART_EXT), GetXMLToken(XML_N_CHART_EXT), XML_NAMESPACE_CHART_EXT);
3848 SchXMLExport::~SchXMLExport()
3852 ErrCode SchXMLExport::exportDoc( enum ::xmloff::token::XMLTokenEnum eClass )
3854 maExportHelper->SetSourceShellID(GetSourceShellID());
3855 maExportHelper->SetDestinationShellID(GetDestinationShellID());
3857 Reference< chart2::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
3858 maExportHelper->m_pImpl->InitRangeSegmentationProperties( xChartDoc );
3859 return SvXMLExport::exportDoc( eClass );
3862 void SchXMLExport::ExportMasterStyles_()
3864 // not available in chart
3865 SAL_INFO("xmloff.chart", "Master Style Export requested. Not available for Chart" );
3868 void SchXMLExport::collectAutoStyles()
3870 SvXMLExport::collectAutoStyles();
3872 if (mbAutoStylesCollected)
3873 return;
3875 // there are no styles that require their own autostyles
3876 if( getExportFlags() & SvXMLExportFlags::CONTENT )
3878 Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
3879 if( xChartDoc.is())
3881 maExportHelper->m_pImpl->collectAutoStyles( xChartDoc );
3883 else
3885 SAL_WARN("xmloff.chart", "Couldn't export chart due to wrong XModel (must be XChartDocument)" );
3888 mbAutoStylesCollected = true;
3891 void SchXMLExport::ExportAutoStyles_()
3893 collectAutoStyles();
3895 if( getExportFlags() & SvXMLExportFlags::CONTENT )
3897 Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
3898 if( xChartDoc.is())
3900 maExportHelper->m_pImpl->exportAutoStyles();
3902 else
3904 SAL_WARN("xmloff.chart", "Couldn't export chart due to wrong XModel (must be XChartDocument)" );
3909 void SchXMLExport::ExportContent_()
3911 Reference< chart::XChartDocument > xChartDoc( GetModel(), uno::UNO_QUERY );
3912 if( xChartDoc.is())
3914 // determine if data comes from the outside
3915 bool bIncludeTable = true;
3917 Reference< chart2::XChartDocument > xNewDoc( xChartDoc, uno::UNO_QUERY );
3918 if( xNewDoc.is())
3920 // check if we have own data. If so we must not export the complete
3921 // range string, as this is our only indicator for having own or
3922 // external data. @todo: fix this in the file format!
3923 Reference< lang::XServiceInfo > xDPServiceInfo( xNewDoc->getDataProvider(), uno::UNO_QUERY );
3924 if( ! (xDPServiceInfo.is() && xDPServiceInfo->getImplementationName() == "com.sun.star.comp.chart.InternalDataProvider" ))
3926 bIncludeTable = false;
3929 else
3931 Reference< lang::XServiceInfo > xServ( xChartDoc, uno::UNO_QUERY );
3932 if( xServ.is())
3934 if( xServ->supportsService( "com.sun.star.chart.ChartTableAddressSupplier" ))
3936 Reference< beans::XPropertySet > xProp( xServ, uno::UNO_QUERY );
3937 if( xProp.is())
3939 Any aAny;
3942 OUString sChartAddress;
3943 aAny = xProp->getPropertyValue( "ChartRangeAddress" );
3944 aAny >>= sChartAddress;
3945 maExportHelper->m_pImpl->SetChartRangeAddress( sChartAddress );
3947 // do not include own table if there are external addresses
3948 bIncludeTable = sChartAddress.isEmpty();
3950 catch( const beans::UnknownPropertyException & )
3952 SAL_WARN("xmloff.chart", "Property ChartRangeAddress not supported by ChartDocument" );
3958 maExportHelper->m_pImpl->exportChart( xChartDoc, bIncludeTable );
3960 else
3962 SAL_WARN("xmloff.chart", "Couldn't export chart due to wrong XModel" );
3966 rtl::Reference< XMLPropertySetMapper > const & SchXMLExport::GetPropertySetMapper() const
3968 return maExportHelper->m_pImpl->GetPropertySetMapper();
3971 void SchXMLExportHelper_Impl::InitRangeSegmentationProperties( const Reference< chart2::XChartDocument > & xChartDoc )
3973 if( !xChartDoc.is())
3974 return;
3978 Reference< chart2::data::XDataProvider > xDataProvider( xChartDoc->getDataProvider() );
3979 SAL_WARN_IF( !xDataProvider.is(), "xmloff.chart", "No DataProvider" );
3980 if( xDataProvider.is())
3982 Reference< chart2::data::XDataSource > xDataSource( lcl_pressUsedDataIntoRectangularFormat( xChartDoc, mbHasCategoryLabels ));
3983 const Sequence< beans::PropertyValue > aArgs( xDataProvider->detectArguments( xDataSource ));
3984 OUString sCellRange, sBrokenRange;
3985 bool bBrokenRangeAvailable = false;
3986 for( const auto& rArg : aArgs )
3988 if ( rArg.Name == "CellRangeRepresentation" )
3989 rArg.Value >>= sCellRange;
3990 else if ( rArg.Name == "BrokenCellRangeForExport" )
3992 if( rArg.Value >>= sBrokenRange )
3993 bBrokenRangeAvailable = true;
3995 else if ( rArg.Name == "DataRowSource" )
3997 chart::ChartDataRowSource eRowSource;
3998 rArg.Value >>= eRowSource;
3999 mbRowSourceColumns = ( eRowSource == chart::ChartDataRowSource_COLUMNS );
4001 else if ( rArg.Name == "SequenceMapping" )
4002 rArg.Value >>= maSequenceMapping;
4005 // #i79009# For Writer we have to export a broken version of the
4006 // range, where every row number is not too large, so that older
4007 // version can correctly read those files.
4008 msChartAddress = (bBrokenRangeAvailable ? sBrokenRange : sCellRange);
4009 if( !msChartAddress.isEmpty() )
4011 // convert format to XML-conform one
4012 Reference< chart2::data::XRangeXMLConversion > xConversion( xDataProvider, uno::UNO_QUERY );
4013 if( xConversion.is())
4014 msChartAddress = xConversion->convertRangeToXML( msChartAddress );
4018 catch( const uno::Exception & )
4020 DBG_UNHANDLED_EXCEPTION("xmloff.chart");
4024 // first version: everything goes in one storage
4026 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4027 com_sun_star_comp_Chart_XMLExporter_get_implementation(uno::XComponentContext* pCtx,
4028 uno::Sequence<uno::Any> const& /*rSeq*/)
4030 return cppu::acquire(
4031 new SchXMLExport(pCtx, "SchXMLExport.Compact",
4032 SvXMLExportFlags::ALL
4033 ^ (SvXMLExportFlags::SETTINGS | SvXMLExportFlags::MASTERSTYLES
4034 | SvXMLExportFlags::SCRIPTS)));
4037 // Oasis format
4038 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4039 com_sun_star_comp_Chart_XMLOasisExporter_get_implementation(uno::XComponentContext* pCtx,
4040 uno::Sequence<uno::Any> const& /*rSeq*/)
4042 return cppu::acquire(
4043 new SchXMLExport(pCtx, "SchXMLExport.Oasis.Compact",
4044 (SvXMLExportFlags::ALL
4045 ^ (SvXMLExportFlags::SETTINGS | SvXMLExportFlags::MASTERSTYLES
4046 | SvXMLExportFlags::SCRIPTS))
4047 | SvXMLExportFlags::OASIS));
4050 // multiple storage version: one for content / styles / meta
4052 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4053 com_sun_star_comp_Chart_XMLStylesExporter_get_implementation(
4054 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
4056 return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Styles", SvXMLExportFlags::STYLES));
4059 // Oasis format
4060 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4061 com_sun_star_comp_Chart_XMLOasisStylesExporter_get_implementation(
4062 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
4064 return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Oasis.Styles",
4065 SvXMLExportFlags::STYLES | SvXMLExportFlags::OASIS));
4068 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4069 com_sun_star_comp_Chart_XMLContentExporter_get_implementation(
4070 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
4072 return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Content",
4073 SvXMLExportFlags::AUTOSTYLES | SvXMLExportFlags::CONTENT
4074 | SvXMLExportFlags::FONTDECLS));
4077 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4078 com_sun_star_comp_Chart_XMLOasisContentExporter_get_implementation(
4079 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
4081 return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Oasis.Content",
4082 SvXMLExportFlags::AUTOSTYLES | SvXMLExportFlags::CONTENT
4083 | SvXMLExportFlags::FONTDECLS
4084 | SvXMLExportFlags::OASIS));
4087 // Oasis format
4089 extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
4090 com_sun_star_comp_Chart_XMLOasisMetaExporter_get_implementation(
4091 uno::XComponentContext* pCtx, uno::Sequence<uno::Any> const& /*rSeq*/)
4093 return cppu::acquire(new SchXMLExport(pCtx, "SchXMLExport.Oasis.Meta",
4094 SvXMLExportFlags::META | SvXMLExportFlags::OASIS));
4097 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */