LibreOffice Module oox (master) 1
seriesconverter.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
21
22#include <com/sun/star/chart/DataLabelPlacement.hpp>
23#include <com/sun/star/chart2/RelativePosition.hpp>
24#include <com/sun/star/chart/ErrorBarStyle.hpp>
25#include <com/sun/star/chart2/DataPointLabel.hpp>
26#include <com/sun/star/drawing/Hatch.hpp>
27#include <com/sun/star/chart2/XChartDocument.hpp>
28#include <com/sun/star/chart2/XDataPointCustomLabelField.hpp>
29#include <com/sun/star/chart2/DataPointCustomLabelField.hpp>
30#include <com/sun/star/chart2/DataPointCustomLabelFieldType.hpp>
31#include <com/sun/star/chart2/XDataSeries.hpp>
32#include <com/sun/star/chart2/XRegressionCurve.hpp>
33#include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
34#include <com/sun/star/chart2/data/XDataSink.hpp>
35#include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
36#include <com/sun/star/lang/XMultiServiceFactory.hpp>
37#include <com/sun/star/drawing/FillStyle.hpp>
38
40#include <osl/diagnose.h>
49#include <oox/token/properties.hxx>
50#include <oox/token/tokens.hxx>
53#include <drawingml/textrun.hxx>
57
58namespace oox::drawingml::chart {
59
60using namespace com::sun::star;
61using namespace ::com::sun::star::beans;
62using namespace ::com::sun::star::chart2;
63using namespace ::com::sun::star::chart2::data;
64using namespace ::com::sun::star::uno;
65
66namespace {
67
68Reference< XLabeledDataSequence > lclCreateLabeledDataSequence(
69 const ConverterRoot& rParent,
70 DataSourceModel* pValues, const OUString& rRole,
71 TextModel* pTitle = nullptr )
72{
73 // create data sequence for values
74 Reference< XDataSequence > xValueSeq;
75 if( pValues )
76 {
77 DataSourceConverter aSourceConv( rParent, *pValues );
78 xValueSeq = aSourceConv.createDataSequence( rRole );
79 }
80
81 // create data sequence for title
82 Reference< XDataSequence > xTitleSeq;
83 if( pTitle )
84 {
85 TextConverter aTextConv( rParent, *pTitle );
86 xTitleSeq = aTextConv.createDataSequence( "label" );
87 }
88
89 // create the labeled data sequence, if values or title are present
90 Reference< XLabeledDataSequence > xLabeledSeq;
91 if( xValueSeq.is() || xTitleSeq.is() )
92 {
93 xLabeledSeq = LabeledDataSequence::create(rParent.getComponentContext());
94 if( xLabeledSeq.is() )
95 {
96 xLabeledSeq->setValues( xValueSeq );
97 xLabeledSeq->setLabel( xTitleSeq );
98 }
99 }
100 return xLabeledSeq;
101}
102
103void convertTextProperty(PropertySet& rPropSet, ObjectFormatter& rFormatter,
105{
106 rFormatter.convertTextFormatting( rPropSet, xTextProps, OBJECTTYPE_DATALABEL );
107 ObjectFormatter::convertTextRotation( rPropSet, xTextProps, false );
108 ObjectFormatter::convertTextWrap( rPropSet, xTextProps );
109}
110
111void lclConvertLabelFormatting( PropertySet& rPropSet, ObjectFormatter& rFormatter,
112 DataLabelModelBase& rDataLabel, const TypeGroupConverter& rTypeGroup,
113 bool bDataSeriesLabel, bool bCustomLabelField, bool bHasInternalData, bool bMSO2007Doc )
114{
115 const TypeGroupInfo& rTypeInfo = rTypeGroup.getTypeInfo();
116
117 /* Excel 2007 does not change the series setting for a single data point,
118 if none of some specific elements occur. But only one existing element
119 in a data point will reset most other of these elements from the series
120 (e.g.: series has <c:showVal>, data point has <c:showCatName>, this
121 will reset <c:showVal> for this point, unless <c:showVal> is repeated
122 in the data point). The elements <c:layout>, <c:numberFormat>,
123 <c:spPr>, <c:tx>, and <c:txPr> are not affected at all. */
124 bool bHasAnyElement = true;
125 if (bMSO2007Doc)
126 {
127 bHasAnyElement = rDataLabel.moaSeparator.has_value() || rDataLabel.monLabelPos.has_value() ||
128 rDataLabel.mobShowCatName.has_value() || rDataLabel.mobShowLegendKey.has_value() ||
129 rDataLabel.mobShowPercent.has_value() || rDataLabel.mobShowSerName.has_value() ||
130 rDataLabel.mobShowVal.has_value();
131 }
132
133 bool bShowValue = !rDataLabel.mbDeleted && rDataLabel.mobShowVal.value_or( !bMSO2007Doc );
134 bool bShowPercent = !rDataLabel.mbDeleted && rDataLabel.mobShowPercent.value_or( !bMSO2007Doc ) && (rTypeInfo.meTypeCategory == TYPECATEGORY_PIE);
135 bool bShowCateg = !rDataLabel.mbDeleted && rDataLabel.mobShowCatName.value_or( !bMSO2007Doc );
136 bool bShowSerName = !rDataLabel.mbDeleted && rDataLabel.mobShowSerName.value_or( !bMSO2007Doc );
137 bool bShowSymbol = !rDataLabel.mbDeleted && rDataLabel.mobShowLegendKey.value_or( !bMSO2007Doc );
138
139 // tdf#132174, tdf#136650: the inner data table has no own cell number format.
140 if( bHasInternalData && bShowValue && !bShowPercent )
141 rDataLabel.maNumberFormat.mbSourceLinked = false;
142
143 // type of attached label
144 if( bHasAnyElement || rDataLabel.mbDeleted )
145 {
146 DataPointLabel aPointLabel( bShowValue, bShowPercent, bShowCateg, bShowSymbol, bCustomLabelField, bShowSerName );
147 rPropSet.setProperty( PROP_Label, aPointLabel );
148 }
149
150 if( rDataLabel.mbDeleted )
151 return;
152
153 // data label number format (percentage format wins over value format)
154 rFormatter.convertNumberFormat( rPropSet, rDataLabel.maNumberFormat, false, bShowPercent );
155
156 // data label text formatting (frame formatting not supported by Chart2)
157 if( bDataSeriesLabel || (rDataLabel.mxTextProp.is() && !rDataLabel.mxTextProp->getParagraphs().empty()) )
158 convertTextProperty(rPropSet, rFormatter, rDataLabel.mxTextProp);
159
160 // data label separator (do not overwrite series separator, if no explicit point separator is present)
161 // Set the data label separator to "new line" if the value is shown as percentage with a category name,
162 // just like in MS-Office. In any other case the default separator will be a semicolon.
163 if( bShowPercent && !bShowValue && ( bDataSeriesLabel || rDataLabel.moaSeparator.has_value() ) )
164 rPropSet.setProperty( PROP_LabelSeparator, rDataLabel.moaSeparator.value_or( "\n" ) );
165 else if( bDataSeriesLabel || rDataLabel.moaSeparator.has_value() )
166 rPropSet.setProperty( PROP_LabelSeparator, rDataLabel.moaSeparator.value_or( "; " ) );
167
168 // data label placement (do not overwrite series placement, if no explicit point placement is present)
169 if( !(bDataSeriesLabel || rDataLabel.monLabelPos.has_value()) )
170 return;
171
172 namespace csscd = ::com::sun::star::chart::DataLabelPlacement;
173 sal_Int32 nPlacement = -1;
174 switch( rDataLabel.monLabelPos.value_or( XML_TOKEN_INVALID ) )
175 {
176 case XML_outEnd: nPlacement = csscd::OUTSIDE; break;
177 case XML_inEnd: nPlacement = csscd::INSIDE; break;
178 case XML_ctr: nPlacement = csscd::CENTER; break;
179 case XML_inBase: nPlacement = csscd::NEAR_ORIGIN; break;
180 case XML_t: nPlacement = csscd::TOP; break;
181 case XML_b: nPlacement = csscd::BOTTOM; break;
182 case XML_l: nPlacement = csscd::LEFT; break;
183 case XML_r: nPlacement = csscd::RIGHT; break;
184 case XML_bestFit: nPlacement = csscd::AVOID_OVERLAP; break;
185 }
186
187 if( !bDataSeriesLabel && nPlacement == -1 )
188 return;
189
190 if( nPlacement == -1 )
191 nPlacement = rTypeInfo.mnDefLabelPos;
192
193 rPropSet.setProperty( PROP_LabelPlacement, nPlacement );
194}
195
196void importBorderProperties( PropertySet& rPropSet, Shape& rShape, const GraphicHelper& rGraphicHelper )
197{
198 LineProperties& rLP = rShape.getLineProperties();
199 // no fill has the same effect as no border so skip it
200 if (rLP.maLineFill.moFillType.has_value() && rLP.maLineFill.moFillType.value() == XML_noFill)
201 return;
202
203 if (rLP.moLineWidth.has_value())
204 {
205 sal_Int32 nWidth = convertEmuToHmm(rLP.moLineWidth.value());
206 rPropSet.setProperty(PROP_LabelBorderWidth, uno::Any(nWidth));
207 rPropSet.setProperty(PROP_LabelBorderStyle, uno::Any(drawing::LineStyle_SOLID));
208 }
209 const Color& aColor = rLP.maLineFill.maFillColor;
210 ::Color nColor = aColor.getColor(rGraphicHelper);
211 rPropSet.setProperty(PROP_LabelBorderColor, uno::Any(nColor));
212}
213
214void importFillProperties( PropertySet& rPropSet, Shape& rShape, const GraphicHelper& rGraphicHelper, ModelObjectHelper& rModelObjHelper )
215{
216 FillProperties& rFP = rShape.getFillProperties();
217
218 if (rFP.moFillType.has_value() && rFP.moFillType.value() == XML_solidFill)
219 {
220 rPropSet.setProperty(PROP_LabelFillStyle, drawing::FillStyle_SOLID);
221
222 const Color& aColor = rFP.maFillColor;
223 ::Color nColor = aColor.getColor(rGraphicHelper);
224 rPropSet.setProperty(PROP_LabelFillColor, uno::Any(nColor));
225 }
226 else if(rFP.moFillType.has_value() && rFP.moFillType.value() == XML_pattFill)
227 {
228 rPropSet.setProperty(PROP_LabelFillStyle, drawing::FillStyle_HATCH);
229 rPropSet.setProperty(PROP_LabelFillBackground, true);
230
231 Color aHatchColor( rFP.maPatternProps.maPattFgColor );
232 drawing::Hatch aHatch = createHatch(rFP.maPatternProps.moPattPreset.value(), aHatchColor.getColor(rGraphicHelper, 0));
233
234 OUString sHatchName = rModelObjHelper.insertFillHatch(aHatch);
235 rPropSet.setProperty(PROP_LabelFillHatchName, sHatchName);
236
237 const Color& aColor = rFP.maPatternProps.maPattBgColor;
238 ::Color nColor = aColor.getColor(rGraphicHelper);
239 rPropSet.setProperty(PROP_LabelFillColor, uno::Any(nColor));
240 }
241
242}
243
244DataPointCustomLabelFieldType lcl_ConvertFieldNameToFieldEnum( std::u16string_view rField )
245{
246 if (rField == u"VALUE")
247 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_VALUE;
248 else if (rField == u"SERIESNAME")
249 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_SERIESNAME;
250 else if (rField == u"CATEGORYNAME")
251 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CATEGORYNAME;
252 else if (rField == u"CELLREF")
253 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLREF;
254 else if (rField == u"CELLRANGE")
255 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLRANGE;
256 else if (rField == u"PERCENTAGE")
257 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_PERCENTAGE;
258 else
259 return DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT;
260}
261
262} // namespace
263
265 ConverterBase< DataLabelModel >( rParent, rModel )
266{
267}
268
270{
271}
272
274{
275 if (!rxDataSeries.is())
276 return;
277
278 try
279 {
280 bool bMSO2007Doc = getFilter().isMSO2007Document();
281 bool bHasInternalData = getChartDocument()->hasInternalDataProvider();
282 bool bCustomLabelField = mrModel.mxText && mrModel.mxText->mxTextBody && !mrModel.mxText->mxTextBody->getParagraphs().empty();
283 PropertySet aPropSet( rxDataSeries->getDataPointByIndex( mrModel.mnIndex ) );
284
285 lclConvertLabelFormatting( aPropSet, getFormatter(), mrModel, rTypeGroup, false, bCustomLabelField, bHasInternalData, bMSO2007Doc );
286
287 const TypeGroupInfo& rTypeInfo = rTypeGroup.getTypeInfo();
288 bool bIsPie = rTypeInfo.meTypeCategory == TYPECATEGORY_PIE;
289
290 if( mrModel.mxLayout && !mrModel.mxLayout->mbAutoLayout )
291 {
292 RelativePosition aPos(mrModel.mxLayout->mfX, mrModel.mxLayout->mfY, css::drawing::Alignment_TOP_LEFT);
293 aPropSet.setProperty(PROP_CustomLabelPosition, aPos);
294 sal_Int32 nPlacement = -1;
295 if (bIsPie && aPropSet.getProperty(nPlacement, PROP_LabelPlacement)
296 && nPlacement == css::chart::DataLabelPlacement::AVOID_OVERLAP)
297 aPropSet.setProperty(PROP_LabelPlacement, css::chart::DataLabelPlacement::CUSTOM);
298 }
299
301 {
302 importBorderProperties(aPropSet, *mrModel.mxShapeProp, getFilter().getGraphicHelper());
303 uno::Reference<lang::XMultiServiceFactory> xFactory(getChartDocument(), uno::UNO_QUERY);
305 importFillProperties(aPropSet, *mrModel.mxShapeProp, getFilter().getGraphicHelper(),
306 rHelper);
307 }
308 if( bCustomLabelField )
309 {
310 css::uno::Reference< XComponentContext > xContext = getComponentContext();
311
312 auto& rParagraphs = mrModel.mxText->mxTextBody->getParagraphs();
313
314 int nSequenceSize = 0;
315 for( auto& pParagraph : rParagraphs )
316 nSequenceSize += pParagraph->getRuns().size();
317
318 int nParagraphs = rParagraphs.size();
319 if( nParagraphs > 1 )
320 nSequenceSize += nParagraphs - 1;
321
322 std::optional< OUString > oaLabelText;
323 std::optional< OUString > oaCellRange;
324 if (mrModel.mobShowDataLabelsRange.value_or(false))
325 {
326 const DataSourceModel* pLabelSource = mrModel.mrParent.mpLabelsSource;
327 if (pLabelSource && pLabelSource->mxDataSeq.is())
328 {
329 oaCellRange = pLabelSource->mxDataSeq->maFormula;
330 const auto& rLabelMap = pLabelSource->mxDataSeq->maData;
331 const auto& rKV = rLabelMap.find(mrModel.mnIndex);
332 if (rKV != rLabelMap.end())
333 {
334 oaLabelText.emplace();
335 rKV->second >>= *oaLabelText;
336 }
337 }
338 }
339
340 uno::Sequence< css::uno::Reference< XDataPointCustomLabelField > > aSequence( nSequenceSize );
341 auto aSequenceRange = asNonConstRange(aSequence);
342
343 int nPos = 0;
344
345 for( auto& pParagraph : rParagraphs )
346 {
347 for( auto& pRun : pParagraph->getRuns() )
348 {
349 css::uno::Reference< XDataPointCustomLabelField > xCustomLabel = DataPointCustomLabelField::create( xContext );
350
351 // Store properties
352 oox::PropertySet aPropertySet( xCustomLabel );
353 convertTextProperty( aPropertySet, getFormatter(), mrModel.mxText->mxTextBody );
354 pRun->getTextCharacterProperties().pushToPropSet( aPropertySet, getFilter() );
355
356 TextField* pField = nullptr;
357 if( ( pField = dynamic_cast< TextField* >( pRun.get() ) ) )
358 {
359 DataPointCustomLabelFieldType eType = lcl_ConvertFieldNameToFieldEnum( pField->getType() );
360
361 if (eType == DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_CELLRANGE && oaCellRange.has_value())
362 {
363 xCustomLabel->setCellRange( oaCellRange.value() );
364 xCustomLabel->setString( oaLabelText.value() );
365 xCustomLabel->setDataLabelsRange( true );
366 }
367 else
368 xCustomLabel->setString( pField->getText() );
369
370 xCustomLabel->setFieldType( eType );
371 xCustomLabel->setGuid( pField->getUuid() );
372 }
373 else if( pRun )
374 {
375 xCustomLabel->setString( pRun->getText() );
376 xCustomLabel->setFieldType( DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_TEXT );
377 }
378 aSequenceRange[ nPos++ ] = xCustomLabel;
379 }
380
381 if( nParagraphs > 1 && nPos < nSequenceSize )
382 {
383 css::uno::Reference< XDataPointCustomLabelField > xCustomLabel = DataPointCustomLabelField::create( xContext );
384 xCustomLabel->setFieldType( DataPointCustomLabelFieldType::DataPointCustomLabelFieldType_NEWLINE );
385 xCustomLabel->setString("\n");
386 aSequenceRange[ nPos++ ] = xCustomLabel;
387 }
388 }
389
390 aPropSet.setProperty( PROP_CustomLabelFields, Any( aSequence ) );
391 convertTextProperty(aPropSet, getFormatter(), mrModel.mxText->mxTextBody);
392 }
393 }
394 catch( Exception& )
395 {
396 }
397}
398
400 ConverterBase< DataLabelsModel >( rParent, rModel )
401{
402}
403
405{
406}
407
408namespace
409{
411void InheritFromDataLabelsTextProps(const DataLabelsModel& rLabels, const DataLabelModel& rLabel)
412{
413 // See if <c:dLbls> contains text properties to inherit.
414 if (!rLabels.mxTextProp.is() || rLabels.mxTextProp->getParagraphs().empty())
415 {
416 return;
417 }
418
419 const std::shared_ptr<TextParagraph>& rLabelsParagraph = rLabels.mxTextProp->getParagraphs()[0];
420
421 // See if <c:dLbl> lacks text properties.
422 if (rLabel.mxTextProp.is())
423 {
424 return;
425 }
426
427 if (!rLabel.mxText || !rLabel.mxText->mxTextBody
428 || rLabel.mxText->mxTextBody->getParagraphs().empty())
429 {
430 return;
431 }
432
433 const std::shared_ptr<TextParagraph>& rLabelParagraph
434 = rLabel.mxText->mxTextBody->getParagraphs()[0];
435
436 // Inherit rLabel.mxText's char props from rLabels.mxTextProp's char props.
437 TextCharacterProperties aCharProps;
438 aCharProps.assignUsed(rLabelsParagraph->getProperties().getTextCharacterProperties());
439 aCharProps.assignUsed(rLabelParagraph->getProperties().getTextCharacterProperties());
440 rLabelParagraph->getProperties().getTextCharacterProperties().assignUsed(aCharProps);
441}
442}
443
445{
446 PropertySet aPropSet( rxDataSeries );
447 if( !mrModel.mbDeleted )
448 {
449 bool bMSO2007Doc = getFilter().isMSO2007Document();
450 bool bHasInternalData = getChartDocument()->hasInternalDataProvider();
451
452 lclConvertLabelFormatting( aPropSet, getFormatter(), mrModel, rTypeGroup, true, false, bHasInternalData, bMSO2007Doc );
453
455 {
456 // Import baseline border properties for these data labels.
457 importBorderProperties(aPropSet, *mrModel.mxShapeProp, getFilter().getGraphicHelper());
458 uno::Reference<lang::XMultiServiceFactory> xFactory(getChartDocument(), uno::UNO_QUERY);
460 importFillProperties(aPropSet, *mrModel.mxShapeProp, getFilter().getGraphicHelper(),
461 rHelper);
462 }
463 }
464 // import leaderline of data labels
466 aPropSet.setProperty( PROP_ShowCustomLeaderLines, false );
467
468 // data point label settings
469 for (auto const& pointLabel : mrModel.maPointLabels)
470 {
471 if (pointLabel->maNumberFormat.maFormatCode.isEmpty())
472 pointLabel->maNumberFormat = mrModel.maNumberFormat;
473 InheritFromDataLabelsTextProps(mrModel, *pointLabel);
474
475 DataLabelConverter aLabelConv(*this, *pointLabel);
476 aLabelConv.convertFromModel( rxDataSeries, rTypeGroup );
477 }
478}
479
481 ConverterBase< ErrorBarModel >( rParent, rModel )
482{
483}
484
486{
487}
488
490{
491 bool bShowPos = (mrModel.mnTypeId == XML_plus) || (mrModel.mnTypeId == XML_both);
492 bool bShowNeg = (mrModel.mnTypeId == XML_minus) || (mrModel.mnTypeId == XML_both);
493 if( !(bShowPos || bShowNeg) )
494 return;
495
496 try
497 {
498 Reference< XPropertySet > xErrorBar( createInstance( "com.sun.star.chart2.ErrorBar" ), UNO_QUERY_THROW );
499 PropertySet aBarProp( xErrorBar );
500
501 // plus/minus bars
502 aBarProp.setProperty( PROP_ShowPositiveError, bShowPos );
503 aBarProp.setProperty( PROP_ShowNegativeError, bShowNeg );
504
505 // type of displayed error
506 namespace cssc = ::com::sun::star::chart;
507 switch( mrModel.mnValueType )
508 {
509 case XML_cust:
510 {
511 // #i87806# manual error bars
512 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::FROM_DATA );
513 // attach data sequences to error bar
514 Reference< XDataSink > xDataSink( xErrorBar, UNO_QUERY );
515 if( xDataSink.is() )
516 {
517 // create vector of all value sequences
518 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
519 // add positive values
520 if( bShowPos )
521 {
523 if( xValueSeq.is() )
524 aLabeledSeqVec.push_back( xValueSeq );
525 }
526 // add negative values
527 if( bShowNeg )
528 {
530 if( xValueSeq.is() )
531 aLabeledSeqVec.push_back( xValueSeq );
532 }
533 // attach labeled data sequences to series
534 if( aLabeledSeqVec.empty() )
535 xErrorBar.clear();
536 else
537 xDataSink->setData( comphelper::containerToSequence( aLabeledSeqVec ) );
538 }
539 }
540 break;
541 case XML_fixedVal:
542 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::ABSOLUTE );
543 aBarProp.setProperty( PROP_PositiveError, mrModel.mfValue );
544 aBarProp.setProperty( PROP_NegativeError, mrModel.mfValue );
545 break;
546 case XML_percentage:
547 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::RELATIVE );
548 aBarProp.setProperty( PROP_PositiveError, mrModel.mfValue );
549 aBarProp.setProperty( PROP_NegativeError, mrModel.mfValue );
550 break;
551 case XML_stdDev:
552 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::STANDARD_DEVIATION );
553 aBarProp.setProperty( PROP_Weight, mrModel.mfValue );
554 break;
555 case XML_stdErr:
556 aBarProp.setProperty( PROP_ErrorBarStyle, cssc::ErrorBarStyle::STANDARD_ERROR );
557 break;
558 default:
559 OSL_FAIL( "ErrorBarConverter::convertFromModel - unknown error bar type" );
560 xErrorBar.clear();
561 }
562
563 // error bar formatting
565
566 if( xErrorBar.is() )
567 {
568 PropertySet aSeriesProp( rxDataSeries );
569 switch( mrModel.mnDirection )
570 {
571 case XML_x: aSeriesProp.setProperty( PROP_ErrorBarX, xErrorBar ); break;
572 case XML_y: aSeriesProp.setProperty( PROP_ErrorBarY, xErrorBar ); break;
573 default: OSL_FAIL( "ErrorBarConverter::convertFromModel - invalid error bar direction" );
574 }
575 }
576 }
577 catch( Exception& )
578 {
579 OSL_FAIL( "ErrorBarConverter::convertFromModel - error while creating error bars" );
580 }
581}
582
584{
585 OUString aRole;
586 switch( eSourceType )
587 {
589 switch( mrModel.mnDirection )
590 {
591 case XML_x: aRole = "error-bars-x-positive"; break;
592 case XML_y: aRole = "error-bars-y-positive"; break;
593 }
594 break;
596 switch( mrModel.mnDirection )
597 {
598 case XML_x: aRole = "error-bars-x-negative"; break;
599 case XML_y: aRole = "error-bars-y-negative"; break;
600 }
601 break;
602 }
603 OSL_ENSURE( !aRole.isEmpty(), "ErrorBarConverter::createLabeledDataSequence - invalid error bar direction" );
604 return lclCreateLabeledDataSequence( *this, mrModel.maSources.get( eSourceType ).get(), aRole );
605}
606
608 ConverterBase< TrendlineLabelModel >( rParent, rModel )
609{
610}
611
613{
614}
615
617{
618 // formatting
620}
621
623 ConverterBase< TrendlineModel >( rParent, rModel )
624{
625}
626
628{
629}
630
632{
633 try
634 {
635 // trend line type
636 OUString aServiceName;
637 switch( mrModel.mnTypeId )
638 {
639 case XML_exp:
640 aServiceName = "com.sun.star.chart2.ExponentialRegressionCurve";
641 break;
642 case XML_linear:
643 aServiceName = "com.sun.star.chart2.LinearRegressionCurve";
644 break;
645 case XML_log:
646 aServiceName = "com.sun.star.chart2.LogarithmicRegressionCurve";
647 break;
648 case XML_movingAvg:
649 aServiceName = "com.sun.star.chart2.MovingAverageRegressionCurve";
650 break;
651 case XML_poly:
652 aServiceName = "com.sun.star.chart2.PolynomialRegressionCurve";
653 break;
654 case XML_power:
655 aServiceName = "com.sun.star.chart2.PotentialRegressionCurve";
656 break;
657 default:
658 OSL_FAIL( "TrendlineConverter::convertFromModel - unknown trendline type" );
659 }
660 if( !aServiceName.isEmpty() )
661 {
662 Reference< XRegressionCurve > xRegCurve( createInstance( aServiceName ), UNO_QUERY_THROW );
663 PropertySet aPropSet( xRegCurve );
664
665 // Name
666 aPropSet.setProperty( PROP_CurveName, mrModel.maName );
667 aPropSet.setProperty( PROP_PolynomialDegree, mrModel.mnOrder );
668 aPropSet.setProperty( PROP_MovingAveragePeriod, mrModel.mnPeriod );
669
670 // Intercept
671 bool hasIntercept = mrModel.mfIntercept.has_value();
672 aPropSet.setProperty( PROP_ForceIntercept, hasIntercept);
673 if (hasIntercept)
674 aPropSet.setProperty( PROP_InterceptValue, mrModel.mfIntercept.value());
675
676 // Extrapolation
677 if (mrModel.mfForward.has_value())
678 aPropSet.setProperty( PROP_ExtrapolateForward, mrModel.mfForward.value() );
679 if (mrModel.mfBackward.has_value())
680 aPropSet.setProperty( PROP_ExtrapolateBackward, mrModel.mfBackward.value() );
681
682 // trendline formatting
684
685 // #i83100# show equation and correlation coefficient
686 PropertySet aLabelProp( xRegCurve->getEquationProperties() );
687 aLabelProp.setProperty( PROP_ShowEquation, mrModel.mbDispEquation );
688 aLabelProp.setProperty( PROP_ShowCorrelationCoefficient, mrModel.mbDispRSquared );
689
690 // #i83100# formatting of the equation text box
692 {
693 TrendlineLabelConverter aLabelConv( *this, mrModel.mxLabel.getOrCreate() );
694 aLabelConv.convertFromModel( aLabelProp );
695 }
696
697 // unsupported: #i5085# manual trendline size
698 // unsupported: #i34093# manual crossing point
699
700 Reference< XRegressionCurveContainer > xRegCurveCont( rxDataSeries, UNO_QUERY_THROW );
701 xRegCurveCont->addRegressionCurve( xRegCurve );
702 }
703 }
704 catch( Exception& )
705 {
706 OSL_FAIL( "TrendlineConverter::convertFromModel - error while creating trendline" );
707 }
708}
709
711 ConverterBase< DataPointModel >( rParent, rModel )
712{
713}
714
716{
717}
718
720 const TypeGroupConverter& rTypeGroup, const SeriesModel& rSeries )
721{
722 bool bMSO2007Doc = getFilter().isMSO2007Document();
723 try
724 {
725 PropertySet aPropSet( rxDataSeries->getDataPointByIndex( mrModel.mnIndex ) );
726
727 // data point marker
728 if( ( mrModel.monMarkerSymbol.has_value() && mrModel.monMarkerSymbol.value() != rSeries.mnMarkerSymbol ) ||
729 ( mrModel.monMarkerSize.has_value() && mrModel.monMarkerSize.value() != rSeries.mnMarkerSize ) )
730 rTypeGroup.convertMarker( aPropSet, mrModel.monMarkerSymbol.value_or( rSeries.mnMarkerSymbol ),
732
733 // data point pie explosion
734 if( mrModel.monExplosion.has_value() && mrModel.monExplosion.value() != rSeries.mnExplosion )
735 rTypeGroup.convertPieExplosion( aPropSet, mrModel.monExplosion.value() );
736
737 // point formatting
738 if( mrModel.mxShapeProp.is() )
739 {
740 if( rTypeGroup.getTypeInfo().mbPictureOptions )
742 else
744 }
745 else if (rSeries.mxShapeProp.is())
746 {
747 getFormatter().convertFrameFormatting( aPropSet, rSeries.mxShapeProp, rTypeGroup.getSeriesObjectType(), rSeries.mnIndex );
748 }
749 }
750 catch( Exception& )
751 {
752 }
753}
754
756 ConverterBase< SeriesModel >( rParent, rModel )
757{
758}
759
761{
762}
763
765{
767}
768
770{
771 return createLabeledDataSequence( SeriesModel::VALUES, rRole, true );
772}
773
775{
776 const TypeGroupInfo& rTypeInfo = rTypeGroup.getTypeInfo();
777
778 // create the data series object
779 Reference< XDataSeries > xDataSeries( createInstance( "com.sun.star.chart2.DataSeries" ), UNO_QUERY );
780 PropertySet aSeriesProp( xDataSeries );
781
782 // attach data and title sequences to series
783 sal_Int32 nDataPointCount = 0;
784 Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
785 if( xDataSink.is() )
786 {
787 // create vector of all value sequences
788 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
789 // add Y values
791 if( xYValueSeq.is() )
792 {
793 aLabeledSeqVec.push_back( xYValueSeq );
794 Reference< XDataSequence > xValues = xYValueSeq->getValues();
795 if( xValues.is() )
796 nDataPointCount = xValues->getData().getLength();
797
798 if (!nDataPointCount)
799 // No values present. Don't create a data series.
800 return Reference<XDataSeries>();
801 }
802 // add X values of scatter and bubble charts
803 if( !rTypeInfo.mbCategoryAxis )
804 {
806 if( xXValueSeq.is() )
807 aLabeledSeqVec.push_back( xXValueSeq );
808 // add size values of bubble charts
809 if( rTypeInfo.meTypeId == TYPEID_BUBBLE )
810 {
812 if( xSizeValueSeq.is() )
813 aLabeledSeqVec.push_back( xSizeValueSeq );
814 }
815 }
816 // attach labeled data sequences to series
817 if( !aLabeledSeqVec.empty() )
818 xDataSink->setData( comphelper::containerToSequence( aLabeledSeqVec ) );
819 }
820
821 // error bars
822 for (auto const& errorBar : mrModel.maErrorBars)
823 {
824 ErrorBarConverter aErrorBarConv(*this, *errorBar);
825 aErrorBarConv.convertFromModel( xDataSeries );
826 }
827
828 // trendlines
829 for (auto const& trendLine : mrModel.maTrendlines)
830 {
831 TrendlineConverter aTrendlineConv(*this, *trendLine);
832 aTrendlineConv.convertFromModel( xDataSeries );
833 }
834
835 // data point markers
837#if OOX_CHART_SMOOTHED_PER_SERIES
838 // #i66858# smoothed series lines
839 rTypeGroup.convertLineSmooth( aSeriesProp, mrModel.mbSmooth );
840#endif
841 // 3D bar style (not possible to set at chart type -> set at all series)
842 rTypeGroup.convertBarGeometry( aSeriesProp, mrModel.monShape.value_or( rTypeGroup.getModel().mnShape ) );
843 // pie explosion (restricted to [0%,100%] in Chart2)
844 rTypeGroup.convertPieExplosion( aSeriesProp, mrModel.mnExplosion );
845
846 // series formatting
847 ObjectFormatter& rFormatter = getFormatter();
848 ObjectType eObjType = rTypeGroup.getSeriesObjectType();
849 bool bMSO2007Doc = getFilter().isMSO2007Document();
850 if( rTypeInfo.mbPictureOptions )
851 rFormatter.convertFrameFormatting( aSeriesProp, mrModel.mxShapeProp, mrModel.mxPicOptions.getOrCreate(bMSO2007Doc), eObjType, mrModel.mnIndex );
852 else
853 rFormatter.convertFrameFormatting( aSeriesProp, mrModel.mxShapeProp, eObjType, mrModel.mnIndex );
854
855 // set the (unused) property default value used by the Chart2 templates (true for pie/doughnut charts)
856 bool bIsPie = rTypeInfo.meTypeCategory == TYPECATEGORY_PIE;
857 aSeriesProp.setProperty( PROP_VaryColorsByPoint, bVaryColorsByPoint );
858
859 // own area formatting for every data point (TODO: varying line color not supported)
860 // #i91271# always set area formatting for every point in pie/doughnut charts to override their automatic point formatting
861 if( bIsPie || (bVaryColorsByPoint && rTypeGroup.isSeriesFrameFormat() && ObjectFormatter::isAutomaticFill( mrModel.mxShapeProp )) )
862 {
863 /* Set the series point number as color cycle size at the object
864 formatter to get correct start-shade/end-tint. TODO: in doughnut
865 charts, the sizes of the series may vary, need to use the maximum
866 point count of all series. */
867 sal_Int32 nOldMax = rFormatter.getMaxSeriesIndex();
868 if( bVaryColorsByPoint )
869 rFormatter.setMaxSeriesIndex( nDataPointCount - 1 );
870 for( sal_Int32 nIndex = 0; nIndex < nDataPointCount; ++nIndex )
871 {
872 try
873 {
874 PropertySet aPointProp( xDataSeries->getDataPointByIndex( nIndex ) );
875 rFormatter.convertAutomaticFill( aPointProp, eObjType, bVaryColorsByPoint ? nIndex : mrModel.mnIndex );
876 }
877 catch( Exception& )
878 {
879 }
880 }
881 rFormatter.setMaxSeriesIndex( nOldMax );
882 }
883
884 // data point settings
885 for (auto const& point : mrModel.maPoints)
886 {
887 DataPointConverter aPointConv(*this, *point);
888 aPointConv.convertFromModel( xDataSeries, rTypeGroup, mrModel );
889 }
890
891 /* Series data label settings. If and only if the series does not contain
892 a c:dLbls element, then the c:dLbls element of the parent chart type is
893 used (data label settings of the parent chart type are *not* merged
894 into own existing data label settings). */
896 if( xLabels.is() )
897 {
898 if( xLabels->maNumberFormat.maFormatCode.isEmpty() )
899 {
900 // Use number format code from Value series
902 if( pValues )
903 xLabels->maNumberFormat.maFormatCode = pValues->mxDataSeq->maFormatCode;
904 }
905 DataLabelsConverter aLabelsConv( *this, *xLabels );
906 aLabelsConv.convertFromModel( xDataSeries, rTypeGroup );
907 }
908
909 return xDataSeries;
910}
911
912// private --------------------------------------------------------------------
913
915 SeriesModel::SourceType eSourceType, const OUString& rRole, bool bUseTextLabel )
916{
917 DataSourceModel* pValues = mrModel.maSources.get( eSourceType ).get();
918 TextModel* pTitle = bUseTextLabel ? mrModel.mxText.get() : nullptr;
919 return lclCreateLabeledDataSequence( *this, pValues, rRole, pTitle );
920}
921
922} // namespace oox
923
924/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const PropertyValue * pValues
Provides helper functions for colors, device measurement conversion, graphics, and graphic objects ha...
Contains tables for named drawing objects for a document model.
OUString insertFillHatch(const css::drawing::Hatch &rHatch)
A wrapper for a UNO property set.
Definition: propertyset.hxx:58
bool getProperty(Type &orValue, sal_Int32 nPropId) const
Gets the specified property from the property set.
Definition: propertyset.hxx:94
bool setProperty(sal_Int32 nPropId, const Type &rValue)
Puts the passed value into the property set.
mapped_type get(key_type nKey) const
Returns a reference to the object associated to the passed key, or an empty reference on error.
Definition: refmap.hxx:59
ModelObjectHelper & getModelObjectHelperForModel(const css::uno::Reference< css::lang::XMultiServiceFactory > &xFactory) const
Definition: filterbase.cxx:360
const OUString & getUuid() const
Definition: textfield.hxx:45
const OUString & getType() const
Definition: textfield.hxx:43
OUString & getText()
Definition: textrun.hxx:37
Base class of all converter classes.
const ModelType & getModel() const
::oox::core::XmlFilterBase & getFilter() const
Returns the filter object of the imported/exported document.
css::uno::Reference< css::chart2::XChartDocument > const & getChartDocument() const
Returns the API chart document model.
ObjectFormatter & getFormatter() const
Returns the object formatter.
css::uno::Reference< css::uno::XInterface > createInstance(const OUString &rServiceName) const
Creates an instance for the passed service name, using the process service factory.
css::uno::Reference< css::uno::XComponentContext > const & getComponentContext() const
void convertFromModel(const css::uno::Reference< css::chart2::XDataSeries > &rxDataSeries, const TypeGroupConverter &rTypeGroup)
Converts OOXML data label settings for the passed data point.
DataLabelConverter(const ConverterRoot &rParent, DataLabelModel &rModel)
DataLabelsConverter(const ConverterRoot &rParent, DataLabelsModel &rModel)
void convertFromModel(const css::uno::Reference< css::chart2::XDataSeries > &rxDataSeries, const TypeGroupConverter &rTypeGroup)
Converts OOXML data label settings for the passed data series.
DataPointConverter(const ConverterRoot &rParent, DataPointModel &rModel)
void convertFromModel(const css::uno::Reference< css::chart2::XDataSeries > &rxDataSeries, const TypeGroupConverter &rTypeGroup, const SeriesModel &rSeries)
Converts settings for a data point in the passed series.
css::uno::Reference< css::chart2::data::XLabeledDataSequence > createLabeledDataSequence(ErrorBarModel::SourceType eSourceType)
ErrorBarConverter(const ConverterRoot &rParent, ErrorBarModel &rModel)
void convertFromModel(const css::uno::Reference< css::chart2::XDataSeries > &rxDataSeries)
Converts an OOXML errorbar and inserts it into the passed data series.
void convertFrameFormatting(PropertySet &rPropSet, const ModelRef< Shape > &rxShapeProp, ObjectType eObjType, sal_Int32 nSeriesIdx=-1)
Sets frame formatting properties to the passed property set.
sal_Int32 getMaxSeriesIndex() const
Returns the current maximum series index used for color cycling/fading.
void convertAutomaticFill(PropertySet &rPropSet, ObjectType eObjType, sal_Int32 nSeriesIdx)
Sets automatic fill properties to the passed property set.
void convertFormatting(PropertySet &rPropSet, const ModelRef< Shape > &rxShapeProp, const ModelRef< TextBody > &rxTextProp, ObjectType eObjType)
Sets frame/text formatting properties to the passed property set.
void setMaxSeriesIndex(sal_Int32 nMaxSeriesIdx)
Sets the maximum series index used for color cycling/fading.
static void convertTextWrap(PropertySet &rPropSet, const ModelRef< TextBody > &rxTextProp)
Sets text wrap properties to the passed property set.
static void convertTextRotation(PropertySet &rPropSet, const ModelRef< TextBody > &rxTextProp, bool bSupportsStacked, sal_Int32 nDefaultRotation=0)
Sets text rotation properties to the passed property set.
static bool isAutomaticFill(const ModelRef< Shape > &rxShapeProp)
Returns true, if the passed shape properties have automatic fill mode.
css::uno::Reference< css::chart2::XDataSeries > createDataSeries(const TypeGroupConverter &rTypeGroup, bool bVaryColorsByPoint)
Creates a data series object with initialized source links.
css::uno::Reference< css::chart2::data::XLabeledDataSequence > createCategorySequence(const OUString &rRole)
Creates a labeled data sequence object from category data link.
css::uno::Reference< css::chart2::data::XLabeledDataSequence > createValueSequence(const OUString &rRole)
Creates a labeled data sequence object from value data link.
SeriesConverter(const ConverterRoot &rParent, SeriesModel &rModel)
css::uno::Reference< css::chart2::data::XLabeledDataSequence > createLabeledDataSequence(SeriesModel::SourceType eSourceType, const OUString &rRole, bool bUseTextLabel)
void convertFromModel(const css::uno::Reference< css::chart2::XDataSeries > &rxDataSeries)
Converts an OOXML trendline and inserts it into the passed data series.
TrendlineConverter(const ConverterRoot &rParent, TrendlineModel &rModel)
TrendlineLabelConverter(const ConverterRoot &rParent, TrendlineLabelModel &rModel)
void convertFromModel(PropertySet &rPropSet)
Converts the OOXML trendline label.
void convertBarGeometry(PropertySet &rPropSet, sal_Int32 nOoxShape) const
Sets the passed OOXML bar 3D geometry at the passed property set.
void convertPieExplosion(PropertySet &rPropSet, sal_Int32 nOoxExplosion) const
Sets the passed OOXML pie explosion at the passed property set.
bool isSeriesFrameFormat() const
Returns true, if this chart type supports area formatting for its series.
void convertMarker(PropertySet &rPropSet, sal_Int32 nOoxSymbol, sal_Int32 nOoxSize, const ModelRef< Shape > &xShapeProps) const
Sets the passed OOXML marker style at the passed property set.
ObjectType getSeriesObjectType() const
Returns the object type for a series depending on the chart type.
void convertLineSmooth(PropertySet &rPropSet, bool bOoxSmooth) const
Sets the passed OOXML line smoothing at the passed property set.
const TypeGroupInfo & getTypeInfo() const
Returns the type info struct that describes this chart type group.
Reference< XSingleServiceFactory > xFactory
DocumentType eType
static drawing::Hatch createHatch(sal_Int32 nHatchToken, ::Color nColor)
Definition: hatchmap.hxx:18
sal_Int32 nIndex
sal_uInt16 nPos
def point()
@ Exception
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
@ TYPECATEGORY_PIE
Radar charts (linear or filled).
ObjectType
Enumerates different object types for specific automatic formatting behaviour.
@ OBJECTTYPE_ERRORBAR
Trend line label.
@ OBJECTTYPE_TRENDLINELABEL
Data series trend line.
@ OBJECTTYPE_DATALABEL
Filled series in 3D charts.
@ OBJECTTYPE_TRENDLINE
Labels for data points.
@ TYPEID_BUBBLE
Scatter (XY) chart.
sal_Int32 convertEmuToHmm(sal_Int64 nValue)
Converts the passed 64-bit integer value from EMUs to 1/100 mm.
TextBodyRef mxTextProp
Data label frame formatting.
Definition: seriesmodel.hxx:34
std::optional< bool > mobShowDataLabelsRange
True = show data point value.
Definition: seriesmodel.hxx:47
NumberFormat maNumberFormat
Data label text formatting.
Definition: seriesmodel.hxx:35
const DataLabelsModel & mrParent
Manual or linked text for this data point label.
Definition: seriesmodel.hxx:63
sal_Int32 mnIndex
Reference to the labels container.
Definition: seriesmodel.hxx:64
TextRef mxText
Layout/position of the data point label frame.
Definition: seriesmodel.hxx:62
const DataSourceModel * mpLabelsSource
Formatting of connector lines between data points and labels.
Definition: seriesmodel.hxx:79
std::optional< sal_Int32 > monMarkerSize
Pie slice moved from pie center.
sal_Int32 mnIndex
True = show bubbles with 3D shade.
std::optional< sal_Int32 > monExplosion
Data point marker formatting.
ShapeRef mxMarkerProp
Fill bitmap settings.
std::optional< sal_Int32 > monMarkerSymbol
Size of the series line marker (2...72).
PictureOptionsRef mxPicOptions
Data point formatting.
sal_Int32 mnValueType
Type of the error bars (plus/minus/both).
sal_Int32 mnTypeId
Direction of the error bars (x/y).
ShapeRef mxShapeProp
Source ranges for manual error bar values.
double mfValue
Error line formatting.
sal_Int32 mnDirection
Fixed value for several error bar types.
sal_Int32 mnMarkerSymbol
Size of the series line marker (2...72).
DataPointVector maPoints
All trendlines of this series.
TrendlineVector maTrendlines
All error bars of this series.
sal_Int32 mnExplosion
3D bar shape type.
DataLabelsRef mxLabels
Series title source.
ErrorBarVector maErrorBars
Series source ranges.
@ VALUES
Data point categories.
sal_Int32 mnMarkerSize
Series index used for automatic formatting.
sal_Int32 mnIndex
Pie slice moved from pie center.
std::optional< sal_Int32 > monShape
Data point label settings for all points.
PictureOptionsRef mxPicOptions
Series formatting.
ShapeRef mxMarkerProp
Fill bitmap settings.
TextRef mxText
Data point marker formatting.
ShapeRef mxShapeProp
Explicit formatted data points.
bool mbSmooth
True = invert negative data points.
TextBodyRef mxTextProp
Label frame formatting.
TrendlineLabelRef mxLabel
Trendline formatting.
sal_Int32 mnTypeId
Moving average period in range [2, 255].
std::optional< double > mfBackward
User-defined name of the trendline.
bool mbDispRSquared
True = show equation of the trendline.
bool mbDispEquation
Type of the trendline.
OUString maName
Trendline label text object.
std::optional< double > mfIntercept
Size of trendline behind last data point.
sal_Int32 mnPeriod
Polynomial order in range [2, 6].
sal_Int32 mnOrder
Crossing point with Y axis.
std::optional< double > mfForward
Size of trendline before first data point.
Contains info for a chart type related to the OpenOffice.org chart module.
bool mbPictureOptions
True = data points can be stacked on each other.
bool mbCategoryAxis
True = only first series visible (e.g. pie charts).
TypeCategory meTypeCategory
Unique chart type identifier.
DataLabelsRef mxLabels
List of axis identifiers used by this chart type.
sal_Int32 mnShape
relative size of second pie/bar in pie-to charts (percent).