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