Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / excel / xichart.cxx
blob4c4354233f51c0a994e515e5d568c3177ba62d3c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <xichart.hxx>
22 #include <algorithm>
23 #include <memory>
24 #include <utility>
26 #include <com/sun/star/frame/XModel.hpp>
27 #include <com/sun/star/drawing/Direction3D.hpp>
28 #include <com/sun/star/drawing/ProjectionMode.hpp>
29 #include <com/sun/star/drawing/ShadeMode.hpp>
30 #include <com/sun/star/drawing/XShape.hpp>
31 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
32 #include <com/sun/star/chart/ChartAxisArrangeOrderType.hpp>
33 #include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
34 #include <com/sun/star/chart/ChartAxisMarkPosition.hpp>
35 #include <com/sun/star/chart/ChartAxisPosition.hpp>
36 #include <com/sun/star/chart/ChartLegendExpansion.hpp>
37 #include <com/sun/star/chart/TimeInterval.hpp>
38 #include <com/sun/star/chart/TimeUnit.hpp>
39 #include <com/sun/star/chart/XChartDocument.hpp>
40 #include <com/sun/star/chart/XDiagramPositioning.hpp>
41 #include <com/sun/star/chart/DataLabelPlacement.hpp>
42 #include <com/sun/star/chart/ErrorBarStyle.hpp>
43 #include <com/sun/star/chart/MissingValueTreatment.hpp>
44 #include <com/sun/star/chart2/LinearRegressionCurve.hpp>
45 #include <com/sun/star/chart2/ExponentialRegressionCurve.hpp>
46 #include <com/sun/star/chart2/LogarithmicRegressionCurve.hpp>
47 #include <com/sun/star/chart2/PotentialRegressionCurve.hpp>
48 #include <com/sun/star/chart2/PolynomialRegressionCurve.hpp>
49 #include <com/sun/star/chart2/MovingAverageRegressionCurve.hpp>
50 #include <com/sun/star/chart2/CartesianCoordinateSystem2d.hpp>
51 #include <com/sun/star/chart2/CartesianCoordinateSystem3d.hpp>
52 #include <com/sun/star/chart2/FormattedString.hpp>
53 #include <com/sun/star/chart2/LogarithmicScaling.hpp>
54 #include <com/sun/star/chart2/LinearScaling.hpp>
55 #include <com/sun/star/chart2/PolarCoordinateSystem2d.hpp>
56 #include <com/sun/star/chart2/PolarCoordinateSystem3d.hpp>
57 #include <com/sun/star/chart2/XChartDocument.hpp>
58 #include <com/sun/star/chart2/XDiagram.hpp>
59 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
60 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
61 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
62 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
63 #include <com/sun/star/chart2/XTitled.hpp>
64 #include <com/sun/star/chart2/AxisType.hpp>
65 #include <com/sun/star/chart2/CurveStyle.hpp>
66 #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
67 #include <com/sun/star/chart2/DataPointLabel.hpp>
68 #include <com/sun/star/chart2/LegendPosition.hpp>
69 #include <com/sun/star/chart2/StackingDirection.hpp>
70 #include <com/sun/star/chart2/TickmarkStyle.hpp>
71 #include <com/sun/star/chart2/RelativePosition.hpp>
72 #include <com/sun/star/chart2/RelativeSize.hpp>
73 #include <com/sun/star/chart2/data/XDataProvider.hpp>
74 #include <com/sun/star/chart2/data/XDataReceiver.hpp>
75 #include <com/sun/star/chart2/data/XDataSink.hpp>
76 #include <com/sun/star/chart2/data/LabeledDataSequence.hpp>
77 #include <comphelper/processfactory.hxx>
78 #include <o3tl/numeric.hxx>
79 #include <sfx2/objsh.hxx>
80 #include <svx/svdpage.hxx>
81 #include <svx/unoapi.hxx>
82 #include <sal/log.hxx>
83 #include <tools/helpers.hxx>
85 #include <document.hxx>
86 #include <drwlayer.hxx>
87 #include <tokenarray.hxx>
88 #include <compiler.hxx>
89 #include <reftokenhelper.hxx>
90 #include <chartlis.hxx>
91 #include <globstr.hrc>
92 #include <scresid.hxx>
93 #include <xltracer.hxx>
94 #include <xltools.hxx>
95 #include <xistream.hxx>
96 #include <xiformula.hxx>
97 #include <xistyle.hxx>
98 #include <xipage.hxx>
99 #include <xiview.hxx>
101 using ::com::sun::star::uno::Any;
102 using ::com::sun::star::uno::Reference;
103 using ::com::sun::star::uno::Sequence;
104 using ::com::sun::star::uno::UNO_QUERY;
105 using ::com::sun::star::uno::UNO_QUERY_THROW;
106 using ::com::sun::star::uno::UNO_SET_THROW;
107 using ::com::sun::star::uno::Exception;
108 using ::com::sun::star::beans::XPropertySet;
109 using ::com::sun::star::frame::XModel;
110 using ::com::sun::star::util::XNumberFormatsSupplier;
111 using ::com::sun::star::drawing::XDrawPage;
112 using ::com::sun::star::drawing::XDrawPageSupplier;
113 using ::com::sun::star::drawing::XShape;
115 using namespace ::com::sun::star::chart2;
117 using ::com::sun::star::chart2::data::XDataProvider;
118 using ::com::sun::star::chart2::data::XDataReceiver;
119 using ::com::sun::star::chart2::data::XDataSequence;
120 using ::com::sun::star::chart2::data::XDataSink;
121 using ::com::sun::star::chart2::data::XLabeledDataSequence;
122 using ::com::sun::star::chart2::data::LabeledDataSequence;
124 using ::formula::FormulaToken;
125 using ::formula::FormulaTokenArrayPlainIterator;
126 using ::std::unique_ptr;
128 namespace cssc = ::com::sun::star::chart;
129 namespace cssc2 = ::com::sun::star::chart2;
131 // Helpers ====================================================================
133 namespace {
135 XclImpStream& operator>>( XclImpStream& rStrm, XclChRectangle& rRect )
137 rRect.mnX = rStrm.ReadInt32();
138 rRect.mnY = rStrm.ReadInt32();
139 rRect.mnWidth = rStrm.ReadInt32();
140 rRect.mnHeight = rStrm.ReadInt32();
141 return rStrm;
144 void lclSetValueOrClearAny( Any& rAny, double fValue, bool bClear )
146 if( bClear )
147 rAny.clear();
148 else
149 rAny <<= fValue;
152 void lclSetExpValueOrClearAny( Any& rAny, double fValue, bool bLogScale, bool bClear )
154 if( !bClear && bLogScale )
155 fValue = pow( 10.0, fValue );
156 lclSetValueOrClearAny( rAny, fValue, bClear );
159 double lclGetSerialDay( const XclImpRoot& rRoot, sal_uInt16 nValue, sal_uInt16 nTimeUnit )
161 switch( nTimeUnit )
163 case EXC_CHDATERANGE_DAYS:
164 return nValue;
165 case EXC_CHDATERANGE_MONTHS:
166 return rRoot.GetDoubleFromDateTime( Date( 1, static_cast< sal_uInt16 >( 1 + nValue % 12 ), static_cast< sal_uInt16 >( rRoot.GetBaseYear() + nValue / 12 ) ) );
167 case EXC_CHDATERANGE_YEARS:
168 return rRoot.GetDoubleFromDateTime( Date( 1, 1, static_cast< sal_uInt16 >( rRoot.GetBaseYear() + nValue ) ) );
169 default:
170 OSL_ENSURE( false, "lclGetSerialDay - unexpected time unit" );
172 return nValue;
175 void lclConvertTimeValue( const XclImpRoot& rRoot, Any& rAny, sal_uInt16 nValue, bool bAuto, sal_uInt16 nTimeUnit )
177 if( bAuto )
178 rAny.clear();
179 else
180 rAny <<= lclGetSerialDay( rRoot, nValue, nTimeUnit );
183 sal_Int32 lclGetApiTimeUnit( sal_uInt16 nTimeUnit )
185 switch( nTimeUnit )
187 case EXC_CHDATERANGE_DAYS: return cssc::TimeUnit::DAY;
188 case EXC_CHDATERANGE_MONTHS: return cssc::TimeUnit::MONTH;
189 case EXC_CHDATERANGE_YEARS: return cssc::TimeUnit::YEAR;
190 default: OSL_ENSURE( false, "lclGetApiTimeUnit - unexpected time unit" );
192 return cssc::TimeUnit::DAY;
195 void lclConvertTimeInterval( Any& rInterval, sal_uInt16 nValue, bool bAuto, sal_uInt16 nTimeUnit )
197 if( bAuto || (nValue == 0) )
198 rInterval.clear();
199 else
200 rInterval <<= cssc::TimeInterval( nValue, lclGetApiTimeUnit( nTimeUnit ) );
203 } // namespace
205 // Common =====================================================================
207 /** Stores global data needed in various classes of the Chart import filter. */
208 struct XclImpChRootData : public XclChRootData
210 XclImpChChart& mrChartData; /// The chart data object.
212 explicit XclImpChRootData( XclImpChChart& rChartData ) : mrChartData( rChartData ) {}
215 XclImpChRoot::XclImpChRoot( const XclImpRoot& rRoot, XclImpChChart& rChartData ) :
216 XclImpRoot( rRoot ),
217 mxChData( new XclImpChRootData( rChartData ) )
221 XclImpChRoot::~XclImpChRoot()
225 XclImpChChart& XclImpChRoot::GetChartData() const
227 return mxChData->mrChartData;
230 const XclChTypeInfo& XclImpChRoot::GetChartTypeInfo( XclChTypeId eType ) const
232 return mxChData->mxTypeInfoProv->GetTypeInfo( eType );
235 const XclChTypeInfo& XclImpChRoot::GetChartTypeInfo( sal_uInt16 nRecId ) const
237 return mxChData->mxTypeInfoProv->GetTypeInfoFromRecId( nRecId );
240 const XclChFormatInfo& XclImpChRoot::GetFormatInfo( XclChObjectType eObjType ) const
242 return mxChData->mxFmtInfoProv->GetFormatInfo( eObjType );
245 Color XclImpChRoot::GetFontAutoColor() const
247 return GetPalette().GetColor( EXC_COLOR_CHWINDOWTEXT );
250 Color XclImpChRoot::GetSeriesLineAutoColor( sal_uInt16 nFormatIdx ) const
252 return GetPalette().GetColor( XclChartHelper::GetSeriesLineAutoColorIdx( nFormatIdx ) );
255 Color XclImpChRoot::GetSeriesFillAutoColor( sal_uInt16 nFormatIdx ) const
257 const XclImpPalette& rPal = GetPalette();
258 Color aColor = rPal.GetColor( XclChartHelper::GetSeriesFillAutoColorIdx( nFormatIdx ) );
259 sal_uInt8 nTrans = XclChartHelper::GetSeriesFillAutoTransp( nFormatIdx );
260 return ScfTools::GetMixedColor( aColor, rPal.GetColor( EXC_COLOR_CHWINDOWBACK ), nTrans );
263 void XclImpChRoot::InitConversion( const Reference<XChartDocument>& xChartDoc, const tools::Rectangle& rChartRect ) const
265 // create formatting object tables
266 mxChData->InitConversion( GetRoot(), xChartDoc, rChartRect );
268 // lock the model to suppress any internal updates
269 if( xChartDoc.is() )
270 xChartDoc->lockControllers();
272 SfxObjectShell* pDocShell = GetDocShell();
273 Reference< XDataReceiver > xDataRec( xChartDoc, UNO_QUERY );
274 if( pDocShell && xDataRec.is() )
276 // create and register a data provider
277 Reference< XDataProvider > xDataProv(
278 ScfApiHelper::CreateInstance( pDocShell, SERVICE_CHART2_DATAPROVIDER ), UNO_QUERY );
279 if( xDataProv.is() )
280 xDataRec->attachDataProvider( xDataProv );
281 // attach the number formatter
282 Reference< XNumberFormatsSupplier > xNumFmtSupp( pDocShell->GetModel(), UNO_QUERY );
283 if( xNumFmtSupp.is() )
284 xDataRec->attachNumberFormatsSupplier( xNumFmtSupp );
288 void XclImpChRoot::FinishConversion( XclImpDffConverter& rDffConv ) const
290 rDffConv.Progress( EXC_CHART_PROGRESS_SIZE );
291 // unlock the model
292 Reference< XModel > xModel = mxChData->mxChartDoc;
293 if( xModel.is() )
294 xModel->unlockControllers();
295 rDffConv.Progress( EXC_CHART_PROGRESS_SIZE );
297 mxChData->FinishConversion();
300 Reference< XDataProvider > XclImpChRoot::GetDataProvider() const
302 return mxChData->mxChartDoc->getDataProvider();
305 Reference< XShape > XclImpChRoot::GetTitleShape( const XclChTextKey& rTitleKey ) const
307 return mxChData->GetTitleShape( rTitleKey );
310 sal_Int32 XclImpChRoot::CalcHmmFromChartX( sal_Int32 nPosX ) const
312 return static_cast< sal_Int32 >( mxChData->mfUnitSizeX * nPosX + mxChData->mnBorderGapX + 0.5 );
315 sal_Int32 XclImpChRoot::CalcHmmFromChartY( sal_Int32 nPosY ) const
317 return static_cast< sal_Int32 >( mxChData->mfUnitSizeY * nPosY + mxChData->mnBorderGapY + 0.5 );
320 css::awt::Rectangle XclImpChRoot::CalcHmmFromChartRect( const XclChRectangle& rRect ) const
322 return css::awt::Rectangle(
323 CalcHmmFromChartX( rRect.mnX ),
324 CalcHmmFromChartY( rRect.mnY ),
325 CalcHmmFromChartX( rRect.mnWidth ),
326 CalcHmmFromChartY( rRect.mnHeight ) );
329 double XclImpChRoot::CalcRelativeFromHmmX( sal_Int32 nPosX ) const
331 const long nWidth = mxChData->maChartRect.GetWidth();
332 if (!nWidth)
333 throw o3tl::divide_by_zero();
334 return static_cast<double>(nPosX) / nWidth;
337 double XclImpChRoot::CalcRelativeFromHmmY( sal_Int32 nPosY ) const
339 const long nHeight = mxChData->maChartRect.GetHeight();
340 if (!nHeight)
341 throw o3tl::divide_by_zero();
342 return static_cast<double >(nPosY) / nHeight;
345 double XclImpChRoot::CalcRelativeFromChartX( sal_Int32 nPosX ) const
347 return CalcRelativeFromHmmX( CalcHmmFromChartX( nPosX ) );
350 double XclImpChRoot::CalcRelativeFromChartY( sal_Int32 nPosY ) const
352 return CalcRelativeFromHmmY( CalcHmmFromChartY( nPosY ) );
355 void XclImpChRoot::ConvertLineFormat( ScfPropertySet& rPropSet,
356 const XclChLineFormat& rLineFmt, XclChPropertyMode ePropMode ) const
358 GetChartPropSetHelper().WriteLineProperties(
359 rPropSet, *mxChData->mxLineDashTable, rLineFmt, ePropMode );
362 void XclImpChRoot::ConvertAreaFormat( ScfPropertySet& rPropSet,
363 const XclChAreaFormat& rAreaFmt, XclChPropertyMode ePropMode ) const
365 GetChartPropSetHelper().WriteAreaProperties( rPropSet, rAreaFmt, ePropMode );
368 void XclImpChRoot::ConvertEscherFormat( ScfPropertySet& rPropSet,
369 const XclChEscherFormat& rEscherFmt, const XclChPicFormat* pPicFmt,
370 sal_uInt32 nDffFillType, XclChPropertyMode ePropMode ) const
372 GetChartPropSetHelper().WriteEscherProperties( rPropSet,
373 *mxChData->mxGradientTable, *mxChData->mxBitmapTable,
374 rEscherFmt, pPicFmt, nDffFillType, ePropMode );
377 void XclImpChRoot::ConvertFont( ScfPropertySet& rPropSet,
378 sal_uInt16 nFontIdx, const Color* pFontColor ) const
380 GetFontBuffer().WriteFontProperties( rPropSet, EXC_FONTPROPSET_CHART, nFontIdx, pFontColor );
383 void XclImpChRoot::ConvertPieRotation( ScfPropertySet& rPropSet, sal_uInt16 nAngle )
385 sal_Int32 nApiRot = (450 - (nAngle % 360)) % 360;
386 rPropSet.SetProperty( EXC_CHPROP_STARTINGANGLE, nApiRot );
389 XclImpChGroupBase::~XclImpChGroupBase()
393 void XclImpChGroupBase::ReadRecordGroup( XclImpStream& rStrm )
395 // read contents of the header record
396 ReadHeaderRecord( rStrm );
398 // only read sub records, if the next record is a CHBEGIN
399 if( rStrm.GetNextRecId() == EXC_ID_CHBEGIN )
401 // read the CHBEGIN record, may be used for special initial processing
402 rStrm.StartNextRecord();
403 ReadSubRecord( rStrm );
405 // read the nested records
406 bool bLoop = true;
407 while( bLoop && rStrm.StartNextRecord() )
409 sal_uInt16 nRecId = rStrm.GetRecId();
410 bLoop = nRecId != EXC_ID_CHEND;
411 // skip unsupported nested blocks
412 if( nRecId == EXC_ID_CHBEGIN )
413 SkipBlock( rStrm );
414 else
415 ReadSubRecord( rStrm );
418 /* Returns with current CHEND record or unchanged stream, if no record
419 group present. In every case another call to StartNextRecord() will go
420 to next record of interest. */
423 void XclImpChGroupBase::SkipBlock( XclImpStream& rStrm )
425 OSL_ENSURE( rStrm.GetRecId() == EXC_ID_CHBEGIN, "XclImpChGroupBase::SkipBlock - no CHBEGIN record" );
426 // do nothing if current record is not CHBEGIN
427 bool bLoop = rStrm.GetRecId() == EXC_ID_CHBEGIN;
428 while( bLoop && rStrm.StartNextRecord() )
430 sal_uInt16 nRecId = rStrm.GetRecId();
431 bLoop = nRecId != EXC_ID_CHEND;
432 // skip nested record groups
433 if( nRecId == EXC_ID_CHBEGIN )
434 SkipBlock( rStrm );
438 // Frame formatting ===========================================================
440 void XclImpChFramePos::ReadChFramePos( XclImpStream& rStrm )
442 maData.mnTLMode = rStrm.ReaduInt16();
443 maData.mnBRMode = rStrm.ReaduInt16();
444 /* According to the spec, the upper 16 bits of all members in the
445 rectangle are unused and may contain garbage. */
446 maData.maRect.mnX = rStrm.ReadInt16(); rStrm.Ignore( 2 );
447 maData.maRect.mnY = rStrm.ReadInt16(); rStrm.Ignore( 2 );
448 maData.maRect.mnWidth = rStrm.ReadInt16(); rStrm.Ignore( 2 );
449 maData.maRect.mnHeight = rStrm.ReadInt16(); rStrm.Ignore( 2 );
452 void XclImpChLineFormat::ReadChLineFormat( XclImpStream& rStrm )
454 rStrm >> maData.maColor;
455 maData.mnPattern = rStrm.ReaduInt16();
456 maData.mnWeight = rStrm.ReadInt16();
457 maData.mnFlags = rStrm.ReaduInt16();
459 const XclImpRoot& rRoot = rStrm.GetRoot();
460 if( rRoot.GetBiff() == EXC_BIFF8 )
461 // BIFF8: index into palette used instead of RGB data
462 maData.maColor = rRoot.GetPalette().GetColor( rStrm.ReaduInt16() );
465 void XclImpChLineFormat::Convert( const XclImpChRoot& rRoot,
466 ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
468 const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
469 if( IsAuto() )
471 XclChLineFormat aLineFmt;
472 aLineFmt.maColor = (eObjType == EXC_CHOBJTYPE_LINEARSERIES) ?
473 rRoot.GetSeriesLineAutoColor( nFormatIdx ) :
474 rRoot.GetPalette().GetColor( rFmtInfo.mnAutoLineColorIdx );
475 aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
476 aLineFmt.mnWeight = rFmtInfo.mnAutoLineWeight;
477 rRoot.ConvertLineFormat( rPropSet, aLineFmt, rFmtInfo.mePropMode );
479 else
481 rRoot.ConvertLineFormat( rPropSet, maData, rFmtInfo.mePropMode );
485 void XclImpChAreaFormat::ReadChAreaFormat( XclImpStream& rStrm )
487 rStrm >> maData.maPattColor >> maData.maBackColor;
488 maData.mnPattern = rStrm.ReaduInt16();
489 maData.mnFlags = rStrm.ReaduInt16();
491 const XclImpRoot& rRoot = rStrm.GetRoot();
492 if( rRoot.GetBiff() == EXC_BIFF8 )
494 // BIFF8: index into palette used instead of RGB data
495 const XclImpPalette& rPal = rRoot.GetPalette();
496 maData.maPattColor = rPal.GetColor( rStrm.ReaduInt16() );
497 maData.maBackColor = rPal.GetColor( rStrm.ReaduInt16());
501 void XclImpChAreaFormat::Convert( const XclImpChRoot& rRoot,
502 ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
504 const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
505 if( IsAuto() )
507 XclChAreaFormat aAreaFmt;
508 aAreaFmt.maPattColor = (eObjType == EXC_CHOBJTYPE_FILLEDSERIES) ?
509 rRoot.GetSeriesFillAutoColor( nFormatIdx ) :
510 rRoot.GetPalette().GetColor( rFmtInfo.mnAutoPattColorIdx );
511 aAreaFmt.mnPattern = EXC_PATT_SOLID;
512 rRoot.ConvertAreaFormat( rPropSet, aAreaFmt, rFmtInfo.mePropMode );
514 else
516 rRoot.ConvertAreaFormat( rPropSet, maData, rFmtInfo.mePropMode );
520 XclImpChEscherFormat::XclImpChEscherFormat( const XclImpRoot& rRoot ) :
521 mnDffFillType( mso_fillSolid )
523 maData.mxItemSet.reset(
524 new SfxItemSet( rRoot.GetDoc().GetDrawLayer()->GetItemPool() ) );
527 void XclImpChEscherFormat::ReadHeaderRecord( XclImpStream& rStrm )
529 // read from stream - CHESCHERFORMAT uses own ID for record continuation
530 XclImpDffPropSet aPropSet( rStrm.GetRoot() );
531 rStrm.ResetRecord( true, rStrm.GetRecId() );
532 rStrm >> aPropSet;
533 // get the data
534 aPropSet.FillToItemSet( *maData.mxItemSet );
535 // get fill type from DFF property set
536 mnDffFillType = aPropSet.GetPropertyValue( DFF_Prop_fillType );
539 void XclImpChEscherFormat::ReadSubRecord( XclImpStream& rStrm )
541 switch( rStrm.GetRecId() )
543 case EXC_ID_CHPICFORMAT:
544 maPicFmt.mnBmpMode = rStrm.ReaduInt16();
545 rStrm.Ignore( 2 );
546 maPicFmt.mnFlags = rStrm.ReaduInt16();
547 maPicFmt.mfScale = rStrm.ReadDouble();
548 break;
552 void XclImpChEscherFormat::Convert( const XclImpChRoot& rRoot,
553 ScfPropertySet& rPropSet, XclChObjectType eObjType, bool bUsePicFmt ) const
555 const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
556 rRoot.ConvertEscherFormat( rPropSet, maData, bUsePicFmt ? &maPicFmt : nullptr, mnDffFillType, rFmtInfo.mePropMode );
559 XclImpChFrameBase::XclImpChFrameBase( const XclChFormatInfo& rFmtInfo )
561 if( rFmtInfo.mbCreateDefFrame ) switch( rFmtInfo.meDefFrameType )
563 case EXC_CHFRAMETYPE_AUTO:
564 mxLineFmt.reset( new XclImpChLineFormat );
565 if( rFmtInfo.mbIsFrame )
566 mxAreaFmt.reset( new XclImpChAreaFormat );
567 break;
568 case EXC_CHFRAMETYPE_INVISIBLE:
570 XclChLineFormat aLineFmt;
571 ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, false );
572 aLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE;
573 mxLineFmt.reset( new XclImpChLineFormat( aLineFmt ) );
574 if( rFmtInfo.mbIsFrame )
576 XclChAreaFormat aAreaFmt;
577 ::set_flag( aAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, false );
578 aAreaFmt.mnPattern = EXC_PATT_NONE;
579 mxAreaFmt.reset( new XclImpChAreaFormat( aAreaFmt ) );
582 break;
583 default:
584 OSL_FAIL( "XclImpChFrameBase::XclImpChFrameBase - unknown frame type" );
588 void XclImpChFrameBase::ReadSubRecord( XclImpStream& rStrm )
590 switch( rStrm.GetRecId() )
592 case EXC_ID_CHLINEFORMAT:
593 mxLineFmt.reset( new XclImpChLineFormat );
594 mxLineFmt->ReadChLineFormat( rStrm );
595 break;
596 case EXC_ID_CHAREAFORMAT:
597 mxAreaFmt.reset( new XclImpChAreaFormat );
598 mxAreaFmt->ReadChAreaFormat( rStrm );
599 break;
600 case EXC_ID_CHESCHERFORMAT:
601 mxEscherFmt.reset( new XclImpChEscherFormat( rStrm.GetRoot() ) );
602 mxEscherFmt->ReadRecordGroup( rStrm );
603 break;
607 void XclImpChFrameBase::ConvertLineBase( const XclImpChRoot& rRoot,
608 ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx ) const
610 if( mxLineFmt )
611 mxLineFmt->Convert( rRoot, rPropSet, eObjType, nFormatIdx );
614 void XclImpChFrameBase::ConvertAreaBase( const XclImpChRoot& rRoot,
615 ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx, bool bUsePicFmt ) const
617 if( rRoot.GetFormatInfo( eObjType ).mbIsFrame )
619 // CHESCHERFORMAT overrides CHAREAFORMAT (even if it is auto)
620 if( mxEscherFmt )
621 mxEscherFmt->Convert( rRoot, rPropSet, eObjType, bUsePicFmt );
622 else if( mxAreaFmt )
623 mxAreaFmt->Convert( rRoot, rPropSet, eObjType, nFormatIdx );
627 void XclImpChFrameBase::ConvertFrameBase( const XclImpChRoot& rRoot,
628 ScfPropertySet& rPropSet, XclChObjectType eObjType, sal_uInt16 nFormatIdx, bool bUsePicFmt ) const
630 ConvertLineBase( rRoot, rPropSet, eObjType, nFormatIdx );
631 ConvertAreaBase( rRoot, rPropSet, eObjType, nFormatIdx, bUsePicFmt );
634 XclImpChFrame::XclImpChFrame( const XclImpChRoot& rRoot, XclChObjectType eObjType ) :
635 XclImpChFrameBase( rRoot.GetFormatInfo( eObjType ) ),
636 XclImpChRoot( rRoot ),
637 meObjType( eObjType )
641 void XclImpChFrame::ReadHeaderRecord( XclImpStream& rStrm )
643 maData.mnFormat = rStrm.ReaduInt16();
644 maData.mnFlags = rStrm.ReaduInt16();
647 void XclImpChFrame::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
649 const XclImpPalette& rPal = GetPalette();
651 if( rLineData.IsVisible() && (!mxLineFmt || !mxLineFmt->HasLine()) )
653 // line formatting
654 XclChLineFormat aLineFmt;
655 aLineFmt.maColor = rPal.GetColor( rLineData.mnColorIdx );
656 switch( rLineData.mnStyle )
658 case EXC_OBJ_LINE_SOLID: aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID; break;
659 case EXC_OBJ_LINE_DASH: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASH; break;
660 case EXC_OBJ_LINE_DOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DOT; break;
661 case EXC_OBJ_LINE_DASHDOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASHDOT; break;
662 case EXC_OBJ_LINE_DASHDOTDOT: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DASHDOTDOT; break;
663 case EXC_OBJ_LINE_MEDTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_MEDTRANS; break;
664 case EXC_OBJ_LINE_DARKTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_DARKTRANS; break;
665 case EXC_OBJ_LINE_LIGHTTRANS: aLineFmt.mnPattern = EXC_CHLINEFORMAT_LIGHTTRANS; break;
666 case EXC_OBJ_LINE_NONE: aLineFmt.mnPattern = EXC_CHLINEFORMAT_NONE; break;
667 default: aLineFmt.mnPattern = EXC_CHLINEFORMAT_SOLID;
669 switch( rLineData.mnWidth )
671 case EXC_OBJ_LINE_HAIR: aLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR; break;
672 case EXC_OBJ_LINE_THIN: aLineFmt.mnWeight = EXC_CHLINEFORMAT_SINGLE; break;
673 case EXC_OBJ_LINE_MEDIUM: aLineFmt.mnWeight = EXC_CHLINEFORMAT_DOUBLE; break;
674 case EXC_OBJ_LINE_THICK: aLineFmt.mnWeight = EXC_CHLINEFORMAT_TRIPLE; break;
675 default: aLineFmt.mnWeight = EXC_CHLINEFORMAT_HAIR;
677 ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_AUTO, rLineData.IsAuto() );
678 mxLineFmt.reset( new XclImpChLineFormat( aLineFmt ) );
681 if( rFillData.IsFilled() && (!mxAreaFmt || !mxAreaFmt->HasArea()) && !mxEscherFmt )
683 // area formatting
684 XclChAreaFormat aAreaFmt;
685 aAreaFmt.maPattColor = rPal.GetColor( rFillData.mnPattColorIdx );
686 aAreaFmt.maBackColor = rPal.GetColor( rFillData.mnBackColorIdx );
687 aAreaFmt.mnPattern = rFillData.mnPattern;
688 ::set_flag( aAreaFmt.mnFlags, EXC_CHAREAFORMAT_AUTO, rFillData.IsAuto() );
689 mxAreaFmt.reset( new XclImpChAreaFormat( aAreaFmt ) );
693 void XclImpChFrame::Convert( ScfPropertySet& rPropSet, bool bUsePicFmt ) const
695 ConvertFrameBase( GetChRoot(), rPropSet, meObjType, EXC_CHDATAFORMAT_UNKNOWN, bUsePicFmt );
698 // Source links ===============================================================
700 namespace {
702 /** Creates a labeled data sequence object, adds link for series title if present. */
703 Reference< XLabeledDataSequence > lclCreateLabeledDataSequence(
704 const XclImpChSourceLinkRef& xValueLink, const OUString& rValueRole,
705 const XclImpChSourceLink* pTitleLink = nullptr )
707 // create data sequence for values and title
708 Reference< XDataSequence > xValueSeq;
709 if( xValueLink )
710 xValueSeq = xValueLink->CreateDataSequence( rValueRole );
711 Reference< XDataSequence > xTitleSeq;
712 if( pTitleLink )
713 xTitleSeq = pTitleLink->CreateDataSequence( EXC_CHPROP_ROLE_LABEL );
715 // create the labeled data sequence, if values or title are present
716 Reference< XLabeledDataSequence > xLabeledSeq;
717 if( xValueSeq.is() || xTitleSeq.is() )
718 xLabeledSeq = LabeledDataSequence::create(comphelper::getProcessComponentContext());
719 if( xLabeledSeq.is() )
721 if( xValueSeq.is() )
722 xLabeledSeq->setValues( xValueSeq );
723 if( xTitleSeq.is() )
724 xLabeledSeq->setLabel( xTitleSeq );
726 return xLabeledSeq;
729 } // namespace
731 XclImpChSourceLink::XclImpChSourceLink( const XclImpChRoot& rRoot ) :
732 XclImpChRoot( rRoot )
736 XclImpChSourceLink::~XclImpChSourceLink()
740 void XclImpChSourceLink::ReadChSourceLink( XclImpStream& rStrm )
742 maData.mnDestType = rStrm.ReaduInt8();
743 maData.mnLinkType = rStrm.ReaduInt8();
744 maData.mnFlags = rStrm.ReaduInt16();
745 maData.mnNumFmtIdx = rStrm.ReaduInt16();
747 mxTokenArray.reset();
748 if( GetLinkType() == EXC_CHSRCLINK_WORKSHEET )
750 // read token array
751 XclTokenArray aXclTokArr;
752 rStrm >> aXclTokArr;
754 // convert BIFF formula tokens to Calc token array
755 if( std::unique_ptr<ScTokenArray> pTokens = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CHART, aXclTokArr ) )
756 mxTokenArray = std::move( pTokens );
759 // try to read a following CHSTRING record
760 if( (rStrm.GetNextRecId() == EXC_ID_CHSTRING) && rStrm.StartNextRecord() )
762 mxString.reset( new XclImpString );
763 rStrm.Ignore( 2 );
764 mxString->Read( rStrm, XclStrFlags::EightBitLength | XclStrFlags::SeparateFormats );
768 void XclImpChSourceLink::SetString( const OUString& rString )
770 if( !mxString )
771 mxString.reset( new XclImpString );
772 mxString->SetText( rString );
775 void XclImpChSourceLink::SetTextFormats( const XclFormatRunVec& rFormats )
777 if( mxString )
778 mxString->SetFormats( rFormats );
781 sal_uInt16 XclImpChSourceLink::GetCellCount() const
783 sal_uInt32 nCellCount = 0;
784 if( mxTokenArray )
786 FormulaTokenArrayPlainIterator aIter(*mxTokenArray);
787 for( const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next() )
789 switch( pToken->GetType() )
791 case ::formula::svSingleRef:
792 case ::formula::svExternalSingleRef:
793 // single cell
794 ++nCellCount;
795 break;
796 case ::formula::svDoubleRef:
797 case ::formula::svExternalDoubleRef:
799 // cell range
800 const ScComplexRefData& rComplexRef = *pToken->GetDoubleRef();
801 ScAddress aAbs1 = rComplexRef.Ref1.toAbs(ScAddress());
802 ScAddress aAbs2 = rComplexRef.Ref2.toAbs(ScAddress());
803 sal_uInt32 nTabs = static_cast<sal_uInt32>(aAbs2.Tab() - aAbs1.Tab() + 1);
804 sal_uInt32 nCols = static_cast<sal_uInt32>(aAbs2.Col() - aAbs1.Col() + 1);
805 sal_uInt32 nRows = static_cast<sal_uInt32>(aAbs2.Row() - aAbs1.Row() + 1);
806 nCellCount += nCols * nRows * nTabs;
808 break;
809 default: ;
813 return limit_cast< sal_uInt16 >( nCellCount );
816 void XclImpChSourceLink::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const
818 bool bLinkToSource = ::get_flag( maData.mnFlags, EXC_CHSRCLINK_NUMFMT );
819 sal_uInt32 nScNumFmt = bLinkToSource ? GetNumFmtBuffer().GetScFormat( maData.mnNumFmtIdx ) : NUMBERFORMAT_ENTRY_NOT_FOUND;
820 OUString aPropName = bPercent ? OUString( EXC_CHPROP_PERCENTAGENUMFMT ) : OUString( EXC_CHPROP_NUMBERFORMAT );
821 if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND )
822 rPropSet.SetProperty( aPropName, static_cast< sal_Int32 >( nScNumFmt ) );
823 else
824 // restore 'link to source' at data point (series may contain manual number format)
825 rPropSet.SetAnyProperty( aPropName, Any() );
828 Reference< XDataSequence > XclImpChSourceLink::CreateDataSequence( const OUString& rRole ) const
830 Reference< XDataSequence > xDataSeq;
831 Reference< XDataProvider > xDataProv = GetDataProvider();
832 if( xDataProv.is() )
834 if ( mxTokenArray )
836 ScCompiler aComp( &GetDoc(), ScAddress(), *mxTokenArray, GetDoc().GetGrammar() );
837 OUStringBuffer aRangeRep;
838 aComp.CreateStringFromTokenArray( aRangeRep );
841 xDataSeq = xDataProv->createDataSequenceByRangeRepresentation( aRangeRep.makeStringAndClear() );
842 // set sequence role
843 ScfPropertySet aSeqProp( xDataSeq );
844 aSeqProp.SetProperty( EXC_CHPROP_ROLE, rRole );
846 catch( Exception& )
848 // OSL_FAIL( "XclImpChSourceLink::CreateDataSequence - cannot create data sequence" );
851 else if( rRole == EXC_CHPROP_ROLE_LABEL && mxString && !mxString->GetText().isEmpty() )
855 OUString aString("\"");
856 xDataSeq = xDataProv->createDataSequenceByRangeRepresentation( aString + mxString->GetText() + aString );
857 // set sequence role
858 ScfPropertySet aSeqProp( xDataSeq );
859 aSeqProp.SetProperty( EXC_CHPROP_ROLE, rRole );
861 catch( Exception& ) { }
864 return xDataSeq;
867 Sequence< Reference< XFormattedString > > XclImpChSourceLink::CreateStringSequence(
868 const XclImpChRoot& rRoot, sal_uInt16 nLeadFontIdx, const Color& rLeadFontColor ) const
870 ::std::vector< Reference< XFormattedString > > aStringVec;
871 if( mxString )
873 for( XclImpStringIterator aIt( *mxString ); aIt.Is(); ++aIt )
875 Reference< css::chart2::XFormattedString2 > xFmtStr = css::chart2::FormattedString::create( comphelper::getProcessComponentContext() );
876 // set text data
877 xFmtStr->setString( aIt.GetPortionText() );
879 // set font formatting and font color
880 ScfPropertySet aStringProp( xFmtStr );
881 sal_uInt16 nFontIdx = aIt.GetPortionFont();
882 if( (nFontIdx == EXC_FONT_NOTFOUND) && (aIt.GetPortionIndex() == 0) )
883 // leading unformatted portion - use passed font settings
884 rRoot.ConvertFont( aStringProp, nLeadFontIdx, &rLeadFontColor );
885 else
886 rRoot.ConvertFont( aStringProp, nFontIdx );
888 // add string to vector of strings
889 aStringVec.emplace_back(xFmtStr );
892 return ScfApiHelper::VectorToSequence( aStringVec );
895 void XclImpChSourceLink::FillSourceLink( ::std::vector< ScTokenRef >& rTokens ) const
897 if( !mxTokenArray )
898 // no links to fill.
899 return;
901 FormulaTokenArrayPlainIterator aIter(*mxTokenArray);
902 for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
904 ScTokenRef pToken(p->Clone());
905 if (ScRefTokenHelper::isRef(pToken))
906 // This is a reference token. Store it.
907 ScRefTokenHelper::join(rTokens, pToken, ScAddress());
911 // Text =======================================================================
913 XclImpChFontBase::~XclImpChFontBase()
917 void XclImpChFontBase::ConvertFontBase( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const
919 Color aFontColor = GetFontColor();
920 rRoot.ConvertFont( rPropSet, GetFontIndex(), &aFontColor );
923 void XclImpChFontBase::ConvertRotationBase( ScfPropertySet& rPropSet, bool bSupportsStacked ) const
925 XclChPropSetHelper::WriteRotationProperties( rPropSet, GetRotation(), bSupportsStacked );
928 XclImpChFont::XclImpChFont() :
929 mnFontIdx( EXC_FONT_NOTFOUND )
933 void XclImpChFont::ReadChFont( XclImpStream& rStrm )
935 mnFontIdx = rStrm.ReaduInt16();
938 XclImpChText::XclImpChText( const XclImpChRoot& rRoot ) :
939 XclImpChRoot( rRoot )
943 void XclImpChText::ReadHeaderRecord( XclImpStream& rStrm )
945 maData.mnHAlign = rStrm.ReaduInt8();
946 maData.mnVAlign = rStrm.ReaduInt8();
947 maData.mnBackMode = rStrm.ReaduInt16();
948 rStrm >> maData.maTextColor
949 >> maData.maRect;
950 maData.mnFlags = rStrm.ReaduInt16();
952 if( GetBiff() == EXC_BIFF8 )
954 // BIFF8: index into palette used instead of RGB data
955 maData.maTextColor = GetPalette().GetColor( rStrm.ReaduInt16() );
956 // placement and rotation
957 maData.mnFlags2 = rStrm.ReaduInt16();
958 maData.mnRotation = rStrm.ReaduInt16();
960 else
962 // BIFF2-BIFF7: get rotation from text orientation
963 sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 8, 3 );
964 maData.mnRotation = XclTools::GetXclRotFromOrient( nOrient );
968 void XclImpChText::ReadSubRecord( XclImpStream& rStrm )
970 switch( rStrm.GetRecId() )
972 case EXC_ID_CHFRAMEPOS:
973 mxFramePos.reset( new XclImpChFramePos );
974 mxFramePos->ReadChFramePos( rStrm );
975 break;
976 case EXC_ID_CHFONT:
977 mxFont.reset( new XclImpChFont );
978 mxFont->ReadChFont( rStrm );
979 break;
980 case EXC_ID_CHFORMATRUNS:
981 if( GetBiff() == EXC_BIFF8 )
982 XclImpString::ReadFormats( rStrm, maFormats );
983 break;
984 case EXC_ID_CHSOURCELINK:
985 mxSrcLink.reset( new XclImpChSourceLink( GetChRoot() ) );
986 mxSrcLink->ReadChSourceLink( rStrm );
987 break;
988 case EXC_ID_CHFRAME:
989 mxFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_TEXT ) );
990 mxFrame->ReadRecordGroup( rStrm );
991 break;
992 case EXC_ID_CHOBJECTLINK:
993 maObjLink.mnTarget = rStrm.ReaduInt16();
994 maObjLink.maPointPos.mnSeriesIdx = rStrm.ReaduInt16();
995 maObjLink.maPointPos.mnPointIdx = rStrm.ReaduInt16();
996 break;
997 case EXC_ID_CHFRLABELPROPS:
998 ReadChFrLabelProps( rStrm );
999 break;
1000 case EXC_ID_CHEND:
1001 if( mxSrcLink && !maFormats.empty() )
1002 mxSrcLink->SetTextFormats( maFormats );
1003 break;
1007 sal_uInt16 XclImpChText::GetFontIndex() const
1009 return mxFont ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND;
1012 Color XclImpChText::GetFontColor() const
1014 return ::get_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR ) ? GetFontAutoColor() : maData.maTextColor;
1017 sal_uInt16 XclImpChText::GetRotation() const
1019 return maData.mnRotation;
1022 void XclImpChText::SetString( const OUString& rString )
1024 if( !mxSrcLink )
1025 mxSrcLink.reset( new XclImpChSourceLink( GetChRoot() ) );
1026 mxSrcLink->SetString( rString );
1029 void XclImpChText::UpdateText( const XclImpChText* pParentText )
1031 if( pParentText )
1033 // update missing members
1034 if( !mxFrame )
1035 mxFrame = pParentText->mxFrame;
1036 if( !mxFont )
1038 mxFont = pParentText->mxFont;
1039 // text color is taken from CHTEXT record, not from font in CHFONT
1040 ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR, ::get_flag( pParentText->maData.mnFlags, EXC_CHTEXT_AUTOCOLOR ) );
1041 maData.maTextColor = pParentText->maData.maTextColor;
1046 void XclImpChText::UpdateDataLabel( bool bCateg, bool bValue, bool bPercent )
1048 ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG, bCateg );
1049 ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE, bValue );
1050 ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT, bPercent );
1051 ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bCateg && bPercent );
1052 ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bCateg && !bValue && !bPercent );
1055 void XclImpChText::ConvertFont( ScfPropertySet& rPropSet ) const
1057 ConvertFontBase( GetChRoot(), rPropSet );
1060 void XclImpChText::ConvertRotation( ScfPropertySet& rPropSet, bool bSupportsStacked ) const
1062 ConvertRotationBase( rPropSet, bSupportsStacked );
1065 void XclImpChText::ConvertFrame( ScfPropertySet& rPropSet ) const
1067 if( mxFrame )
1068 mxFrame->Convert( rPropSet );
1071 void XclImpChText::ConvertNumFmt( ScfPropertySet& rPropSet, bool bPercent ) const
1073 if( mxSrcLink )
1074 mxSrcLink->ConvertNumFmt( rPropSet, bPercent );
1077 void XclImpChText::ConvertDataLabel( ScfPropertySet& rPropSet, const XclChTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const
1079 // existing CHFRLABELPROPS record wins over flags from CHTEXT
1080 sal_uInt16 nShowFlags = mxLabelProps ? mxLabelProps->mnFlags : maData.mnFlags;
1081 sal_uInt16 SHOWANYCATEG = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWCATEG : (EXC_CHTEXT_SHOWCATEGPERC | EXC_CHTEXT_SHOWCATEG);
1082 sal_uInt16 SHOWANYVALUE = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWVALUE : EXC_CHTEXT_SHOWVALUE;
1083 sal_uInt16 SHOWANYPERCENT = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWPERCENT : (EXC_CHTEXT_SHOWPERCENT | EXC_CHTEXT_SHOWCATEGPERC);
1084 sal_uInt16 SHOWANYBUBBLE = mxLabelProps ? EXC_CHFRLABELPROPS_SHOWBUBBLE : EXC_CHTEXT_SHOWBUBBLE;
1086 // get raw flags for label values
1087 bool bShowNone = IsDeleted();
1088 bool bShowCateg = !bShowNone && ::get_flag( nShowFlags, SHOWANYCATEG );
1089 bool bShowPercent = !bShowNone && ::get_flag( nShowFlags, SHOWANYPERCENT );
1090 bool bShowValue = !bShowNone && ::get_flag( nShowFlags, SHOWANYVALUE );
1091 bool bShowBubble = !bShowNone && ::get_flag( nShowFlags, SHOWANYBUBBLE );
1093 // adjust to Chart2 behaviour
1094 if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES )
1095 bShowValue = bShowBubble; // Chart2 bubble charts show bubble size if 'ShowValue' is set
1097 // other flags
1098 bool bShowAny = bShowValue || bShowPercent || bShowCateg;
1099 bool bShowSymbol = bShowAny && ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL );
1101 // create API struct for label values, set API label separator
1102 cssc2::DataPointLabel aPointLabel( bShowValue, bShowPercent, bShowCateg, bShowSymbol );
1103 rPropSet.SetProperty( EXC_CHPROP_LABEL, aPointLabel );
1104 OUString aSep = mxLabelProps ? mxLabelProps->maSeparator : OUString('\n');
1105 if( aSep.isEmpty() )
1106 aSep = "; ";
1107 rPropSet.SetStringProperty( EXC_CHPROP_LABELSEPARATOR, aSep );
1109 // text properties of attached label
1110 if( bShowAny )
1112 ConvertFont( rPropSet );
1113 ConvertRotation( rPropSet, false );
1114 // label placement
1115 using namespace cssc::DataLabelPlacement;
1116 sal_Int32 nPlacement = rTypeInfo.mnDefaultLabelPos;
1117 switch( ::extract_value< sal_uInt16 >( maData.mnFlags2, 0, 4 ) )
1119 case EXC_CHTEXT_POS_DEFAULT: nPlacement = rTypeInfo.mnDefaultLabelPos; break;
1120 case EXC_CHTEXT_POS_OUTSIDE: nPlacement = OUTSIDE; break;
1121 case EXC_CHTEXT_POS_INSIDE: nPlacement = INSIDE; break;
1122 case EXC_CHTEXT_POS_CENTER: nPlacement = CENTER; break;
1123 case EXC_CHTEXT_POS_AXIS: nPlacement = NEAR_ORIGIN; break;
1124 case EXC_CHTEXT_POS_ABOVE: nPlacement = TOP; break;
1125 case EXC_CHTEXT_POS_BELOW: nPlacement = BOTTOM; break;
1126 case EXC_CHTEXT_POS_LEFT: nPlacement = LEFT; break;
1127 case EXC_CHTEXT_POS_RIGHT: nPlacement = RIGHT; break;
1128 case EXC_CHTEXT_POS_AUTO: nPlacement = AVOID_OVERLAP; break;
1130 sal_Int32 nGlobalPlacement = 0;
1131 if ( ( nPlacement == rTypeInfo.mnDefaultLabelPos ) && pGlobalPropSet &&
1132 pGlobalPropSet->GetProperty( nGlobalPlacement, EXC_CHPROP_LABELPLACEMENT ) )
1133 nPlacement = nGlobalPlacement;
1135 rPropSet.SetProperty( EXC_CHPROP_LABELPLACEMENT, nPlacement );
1136 // label number format (percentage format wins over value format)
1137 if( bShowPercent || bShowValue )
1138 ConvertNumFmt( rPropSet, bShowPercent );
1142 Reference< XTitle > XclImpChText::CreateTitle() const
1144 Reference< XTitle > xTitle;
1145 if( mxSrcLink && mxSrcLink->HasString() )
1147 // create the formatted strings
1148 Sequence< Reference< XFormattedString > > aStringSeq(
1149 mxSrcLink->CreateStringSequence( GetChRoot(), GetFontIndex(), GetFontColor() ) );
1150 if( aStringSeq.hasElements() )
1152 // create the title object
1153 xTitle.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_TITLE ), UNO_QUERY );
1154 if( xTitle.is() )
1156 // set the formatted strings
1157 xTitle->setText( aStringSeq );
1158 // more title formatting properties
1159 ScfPropertySet aTitleProp( xTitle );
1160 ConvertFrame( aTitleProp );
1161 ConvertRotation( aTitleProp, true );
1165 return xTitle;
1168 void XclImpChText::ConvertTitlePosition( const XclChTextKey& rTitleKey ) const
1170 if( !mxFramePos ) return;
1172 const XclChFramePos& rPosData = mxFramePos->GetFramePosData();
1173 OSL_ENSURE( (rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rPosData.mnBRMode == EXC_CHFRAMEPOS_PARENT),
1174 "XclImpChText::ConvertTitlePosition - unexpected frame position mode" );
1176 /* Check if title is moved manually. To get the actual position of the
1177 title, we do some kind of hack and use the values from the CHTEXT
1178 record, effectively ignoring the contents of the CHFRAMEPOS record
1179 which contains the position relative to the default title position
1180 (according to the spec, the CHFRAMEPOS supersedes the CHTEXT record).
1181 Especially when it comes to axis titles, things would become very
1182 complicated here, because the relative title position is stored in a
1183 measurement unit that is dependent on the size of the inner plot area,
1184 the interpretation of the X and Y coordinate is dependent on the
1185 direction of the axis, and in 3D charts, and the title default
1186 positions are dependent on the 3D view settings (rotation, elevation,
1187 and perspective). Thus, it is easier to assume that the creator has
1188 written out the correct absolute position and size of the title in the
1189 CHTEXT record. This is assured by checking that the shape size stored
1190 in the CHTEXT record is non-zero. */
1191 if( (rPosData.mnTLMode == EXC_CHFRAMEPOS_PARENT) &&
1192 ((rPosData.maRect.mnX != 0) || (rPosData.maRect.mnY != 0)) &&
1193 (maData.maRect.mnWidth > 0) && (maData.maRect.mnHeight > 0) ) try
1195 Reference< XShape > xTitleShape( GetTitleShape( rTitleKey ), UNO_SET_THROW );
1196 // the call to XShape.getSize() may recalc the chart view
1197 css::awt::Size aTitleSize = xTitleShape->getSize();
1198 // rotated titles need special handling...
1199 sal_Int32 nScRot = XclTools::GetScRotation( GetRotation(), 0 );
1200 double fRad = nScRot * F_PI18000;
1201 double fSin = fabs( sin( fRad ) );
1202 // calculate the title position from the values in the CHTEXT record
1203 css::awt::Point aTitlePos(
1204 CalcHmmFromChartX( maData.maRect.mnX ),
1205 CalcHmmFromChartY( maData.maRect.mnY ) );
1206 // add part of height to X direction, if title is rotated down (clockwise)
1207 if( nScRot > 18000 )
1208 aTitlePos.X += static_cast< sal_Int32 >( fSin * aTitleSize.Height + 0.5 );
1209 // add part of width to Y direction, if title is rotated up (counterclockwise)
1210 else if( nScRot > 0 )
1211 aTitlePos.Y += static_cast< sal_Int32 >( fSin * aTitleSize.Width + 0.5 );
1212 // set the resulting position at the title shape
1213 xTitleShape->setPosition( aTitlePos );
1215 catch( Exception& )
1220 void XclImpChText::ReadChFrLabelProps( XclImpStream& rStrm )
1222 if( GetBiff() == EXC_BIFF8 )
1224 mxLabelProps.reset( new XclChFrLabelProps );
1225 sal_uInt16 nSepLen;
1226 rStrm.Ignore( 12 );
1227 mxLabelProps->mnFlags = rStrm.ReaduInt16();
1228 nSepLen = rStrm.ReaduInt16();
1229 if( nSepLen > 0 )
1230 mxLabelProps->maSeparator = rStrm.ReadUniString( nSepLen );
1234 namespace {
1236 void lclUpdateText( XclImpChTextRef& rxText, const XclImpChText* xDefText )
1238 if (rxText)
1239 rxText->UpdateText( xDefText );
1240 else if (xDefText)
1242 XclImpChTextRef xNew(new XclImpChText(*xDefText));
1243 rxText = xNew;
1247 void lclFinalizeTitle( XclImpChTextRef& rxTitle, const XclImpChText* pDefText, const OUString& rAutoTitle )
1249 /* Do not update a title, if it is not visible (if rxTitle is null).
1250 Existing reference indicates enabled title. */
1251 if( rxTitle )
1253 if( !rxTitle->HasString() )
1254 rxTitle->SetString( rAutoTitle );
1255 if( rxTitle->HasString() )
1256 rxTitle->UpdateText(pDefText);
1257 else
1258 rxTitle.reset();
1262 } // namespace
1264 // Data series ================================================================
1266 void XclImpChMarkerFormat::ReadChMarkerFormat( XclImpStream& rStrm )
1268 rStrm >> maData.maLineColor >> maData.maFillColor;
1269 maData.mnMarkerType = rStrm.ReaduInt16();
1270 maData.mnFlags = rStrm.ReaduInt16();
1272 const XclImpRoot& rRoot = rStrm.GetRoot();
1273 if( rRoot.GetBiff() == EXC_BIFF8 )
1275 // BIFF8: index into palette used instead of RGB data
1276 const XclImpPalette& rPal = rRoot.GetPalette();
1277 maData.maLineColor = rPal.GetColor( rStrm.ReaduInt16() );
1278 maData.maFillColor = rPal.GetColor( rStrm.ReaduInt16() );
1279 // marker size
1280 maData.mnMarkerSize = rStrm.ReaduInt32();
1284 void XclImpChMarkerFormat::Convert( const XclImpChRoot& rRoot,
1285 ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx, sal_Int16 nLineWeight ) const
1287 if( IsAuto() )
1289 XclChMarkerFormat aMarkerFmt;
1290 // line and fill color of the symbol are equal to series line color
1291 //TODO: Excel sets no fill color for specific symbols (e.g. cross)
1292 aMarkerFmt.maLineColor = aMarkerFmt.maFillColor = rRoot.GetSeriesLineAutoColor( nFormatIdx );
1293 switch( nLineWeight )
1295 case EXC_CHLINEFORMAT_HAIR: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_HAIRSIZE; break;
1296 case EXC_CHLINEFORMAT_SINGLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_SINGLESIZE; break;
1297 case EXC_CHLINEFORMAT_DOUBLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_DOUBLESIZE; break;
1298 case EXC_CHLINEFORMAT_TRIPLE: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_TRIPLESIZE; break;
1299 default: aMarkerFmt.mnMarkerSize = EXC_CHMARKERFORMAT_SINGLESIZE;
1301 aMarkerFmt.mnMarkerType = XclChartHelper::GetAutoMarkerType( nFormatIdx );
1302 XclChPropSetHelper::WriteMarkerProperties( rPropSet, aMarkerFmt );
1304 else
1306 XclChPropSetHelper::WriteMarkerProperties( rPropSet, maData );
1310 void XclImpChMarkerFormat::ConvertColor( const XclImpChRoot& rRoot,
1311 ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const
1313 Color aLineColor = IsAuto() ? rRoot.GetSeriesLineAutoColor( nFormatIdx ) : maData.maFillColor;
1314 rPropSet.SetColorProperty( EXC_CHPROP_COLOR, aLineColor );
1317 XclImpChPieFormat::XclImpChPieFormat() :
1318 mnPieDist( 0 )
1322 void XclImpChPieFormat::ReadChPieFormat( XclImpStream& rStrm )
1324 mnPieDist = rStrm.ReaduInt16();
1327 void XclImpChPieFormat::Convert( ScfPropertySet& rPropSet ) const
1329 double fApiDist = ::std::min< double >( mnPieDist / 100.0, 1.0 );
1330 rPropSet.SetProperty( EXC_CHPROP_OFFSET, fApiDist );
1333 XclImpChSeriesFormat::XclImpChSeriesFormat() :
1334 mnFlags( 0 )
1338 void XclImpChSeriesFormat::ReadChSeriesFormat( XclImpStream& rStrm )
1340 mnFlags = rStrm.ReaduInt16();
1343 void XclImpCh3dDataFormat::ReadCh3dDataFormat( XclImpStream& rStrm )
1345 maData.mnBase = rStrm.ReaduInt8();
1346 maData.mnTop = rStrm.ReaduInt8();
1349 void XclImpCh3dDataFormat::Convert( ScfPropertySet& rPropSet ) const
1351 using namespace ::com::sun::star::chart2::DataPointGeometry3D;
1352 sal_Int32 nApiType = (maData.mnBase == EXC_CH3DDATAFORMAT_RECT) ?
1353 ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CUBOID : PYRAMID) :
1354 ((maData.mnTop == EXC_CH3DDATAFORMAT_STRAIGHT) ? CYLINDER : CONE);
1355 rPropSet.SetProperty( EXC_CHPROP_GEOMETRY3D, nApiType );
1358 XclImpChAttachedLabel::XclImpChAttachedLabel( const XclImpChRoot& rRoot ) :
1359 XclImpChRoot( rRoot ),
1360 mnFlags( 0 )
1364 void XclImpChAttachedLabel::ReadChAttachedLabel( XclImpStream& rStrm )
1366 mnFlags = rStrm.ReaduInt16();
1369 XclImpChTextRef XclImpChAttachedLabel::CreateDataLabel( const XclImpChText* pParent ) const
1371 const sal_uInt16 EXC_CHATTLABEL_SHOWANYVALUE = EXC_CHATTLABEL_SHOWVALUE;
1372 const sal_uInt16 EXC_CHATTLABEL_SHOWANYPERCENT = EXC_CHATTLABEL_SHOWPERCENT | EXC_CHATTLABEL_SHOWCATEGPERC;
1373 const sal_uInt16 EXC_CHATTLABEL_SHOWANYCATEG = EXC_CHATTLABEL_SHOWCATEG | EXC_CHATTLABEL_SHOWCATEGPERC;
1375 XclImpChTextRef xLabel( pParent ? new XclImpChText( *pParent ) : new XclImpChText( GetChRoot() ) );
1376 xLabel->UpdateDataLabel(
1377 ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYCATEG ),
1378 ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYVALUE ),
1379 ::get_flag( mnFlags, EXC_CHATTLABEL_SHOWANYPERCENT ) );
1380 return xLabel;
1383 XclImpChDataFormat::XclImpChDataFormat( const XclImpChRoot& rRoot ) :
1384 XclImpChRoot( rRoot )
1388 void XclImpChDataFormat::ReadHeaderRecord( XclImpStream& rStrm )
1390 maData.maPointPos.mnPointIdx = rStrm.ReaduInt16();
1391 maData.maPointPos.mnSeriesIdx = rStrm.ReaduInt16();
1392 maData.mnFormatIdx = rStrm.ReaduInt16();
1393 maData.mnFlags = rStrm.ReaduInt16();
1396 void XclImpChDataFormat::ReadSubRecord( XclImpStream& rStrm )
1398 switch( rStrm.GetRecId() )
1400 case EXC_ID_CHMARKERFORMAT:
1401 mxMarkerFmt.reset( new XclImpChMarkerFormat );
1402 mxMarkerFmt->ReadChMarkerFormat( rStrm );
1403 break;
1404 case EXC_ID_CHPIEFORMAT:
1405 mxPieFmt.reset( new XclImpChPieFormat );
1406 mxPieFmt->ReadChPieFormat( rStrm );
1407 break;
1408 case EXC_ID_CHSERIESFORMAT:
1409 mxSeriesFmt.reset( new XclImpChSeriesFormat );
1410 mxSeriesFmt->ReadChSeriesFormat( rStrm );
1411 break;
1412 case EXC_ID_CH3DDATAFORMAT:
1413 mx3dDataFmt.reset( new XclImpCh3dDataFormat );
1414 mx3dDataFmt->ReadCh3dDataFormat( rStrm );
1415 break;
1416 case EXC_ID_CHATTACHEDLABEL:
1417 mxAttLabel.reset( new XclImpChAttachedLabel( GetChRoot() ) );
1418 mxAttLabel->ReadChAttachedLabel( rStrm );
1419 break;
1420 default:
1421 XclImpChFrameBase::ReadSubRecord( rStrm );
1425 void XclImpChDataFormat::SetPointPos( const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx )
1427 maData.maPointPos = rPointPos;
1428 maData.mnFormatIdx = nFormatIdx;
1431 void XclImpChDataFormat::UpdateGroupFormat( const XclChExtTypeInfo& rTypeInfo )
1433 // remove formats not used for the current chart type
1434 RemoveUnusedFormats( rTypeInfo );
1437 void XclImpChDataFormat::UpdateSeriesFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pGroupFmt )
1439 // update missing formats from passed chart type group format
1440 if( pGroupFmt )
1442 if( !mxLineFmt )
1443 mxLineFmt = pGroupFmt->mxLineFmt;
1444 if( !mxAreaFmt && !mxEscherFmt )
1446 mxAreaFmt = pGroupFmt->mxAreaFmt;
1447 mxEscherFmt = pGroupFmt->mxEscherFmt;
1449 if( !mxMarkerFmt )
1450 mxMarkerFmt = pGroupFmt->mxMarkerFmt;
1451 if( !mxPieFmt )
1452 mxPieFmt = pGroupFmt->mxPieFmt;
1453 if( !mxSeriesFmt )
1454 mxSeriesFmt = pGroupFmt->mxSeriesFmt;
1455 if( !mx3dDataFmt )
1456 mx3dDataFmt = pGroupFmt->mx3dDataFmt;
1457 if( !mxAttLabel )
1458 mxAttLabel = pGroupFmt->mxAttLabel;
1461 /* Create missing but required formats. Existing line, area, and marker
1462 format objects are needed to create automatic series formatting. */
1463 if( !mxLineFmt )
1464 mxLineFmt.reset( new XclImpChLineFormat );
1465 if( !mxAreaFmt && !mxEscherFmt )
1466 mxAreaFmt.reset( new XclImpChAreaFormat );
1467 if( !mxMarkerFmt )
1468 mxMarkerFmt.reset( new XclImpChMarkerFormat );
1470 // remove formats not used for the current chart type
1471 RemoveUnusedFormats( rTypeInfo );
1472 // update data label
1473 UpdateDataLabel( pGroupFmt );
1476 void XclImpChDataFormat::UpdatePointFormat( const XclChExtTypeInfo& rTypeInfo, const XclImpChDataFormat* pSeriesFmt )
1478 // remove formats if they are automatic in this and in the passed series format
1479 if( pSeriesFmt )
1481 if( IsAutoLine() && pSeriesFmt->IsAutoLine() )
1482 mxLineFmt.reset();
1483 if( IsAutoArea() && pSeriesFmt->IsAutoArea() )
1484 mxAreaFmt.reset();
1485 if( IsAutoMarker() && pSeriesFmt->IsAutoMarker() )
1486 mxMarkerFmt.reset();
1487 mxSeriesFmt.reset();
1490 // Excel ignores 3D bar format for single data points
1491 mx3dDataFmt.reset();
1492 // remove point line formats for linear chart types, TODO: implement in OOChart
1493 if( !rTypeInfo.IsSeriesFrameFormat() )
1494 mxLineFmt.reset();
1496 // remove formats not used for the current chart type
1497 RemoveUnusedFormats( rTypeInfo );
1498 // update data label
1499 UpdateDataLabel( pSeriesFmt );
1502 void XclImpChDataFormat::UpdateTrendLineFormat()
1504 if( !mxLineFmt )
1505 mxLineFmt.reset( new XclImpChLineFormat );
1506 mxAreaFmt.reset();
1507 mxEscherFmt.reset();
1508 mxMarkerFmt.reset();
1509 mxPieFmt.reset();
1510 mxSeriesFmt.reset();
1511 mx3dDataFmt.reset();
1512 mxAttLabel.reset();
1513 // update data label
1514 UpdateDataLabel( nullptr );
1517 void XclImpChDataFormat::Convert( ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, const ScfPropertySet* pGlobalPropSet ) const
1519 /* Line and area format.
1520 #i71810# If the data points are filled with bitmaps, textures, or
1521 patterns, then only bar charts will use the CHPICFORMAT record to
1522 determine stacking/stretching mode. All other chart types ignore this
1523 record and always use the property 'fill-type' from the DFF property
1524 set (stretched for bitmaps, and stacked for textures and patterns). */
1525 bool bUsePicFmt = rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR;
1526 ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType(), maData.mnFormatIdx, bUsePicFmt );
1528 // #i83151# only hair lines in 3D charts with filled data points
1529 if( rTypeInfo.mb3dChart && rTypeInfo.IsSeriesFrameFormat() && mxLineFmt && mxLineFmt->HasLine() )
1530 rPropSet.SetProperty< sal_Int32 >( "BorderWidth", 0 );
1532 // other formatting
1533 if( mxMarkerFmt )
1534 mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx, GetLineWeight() );
1535 if( mxPieFmt )
1536 mxPieFmt->Convert( rPropSet );
1537 if( mx3dDataFmt )
1538 mx3dDataFmt->Convert( rPropSet );
1539 if( mxLabel )
1540 mxLabel->ConvertDataLabel( rPropSet, rTypeInfo, pGlobalPropSet );
1542 // 3D settings
1543 rPropSet.SetProperty< sal_Int16 >( EXC_CHPROP_PERCENTDIAGONAL, 0 );
1545 /* Special case: set marker color as line color, if series line is not
1546 visible. This makes the color visible in the marker area.
1547 TODO: remove this if OOChart supports own colors in markers. */
1548 if( !rTypeInfo.IsSeriesFrameFormat() && !HasLine() && mxMarkerFmt )
1549 mxMarkerFmt->ConvertColor( GetChRoot(), rPropSet, maData.mnFormatIdx );
1552 void XclImpChDataFormat::ConvertLine( ScfPropertySet& rPropSet, XclChObjectType eObjType ) const
1554 ConvertLineBase( GetChRoot(), rPropSet, eObjType );
1557 void XclImpChDataFormat::ConvertArea( ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx ) const
1559 ConvertAreaBase( GetChRoot(), rPropSet, EXC_CHOBJTYPE_FILLEDSERIES, nFormatIdx );
1562 void XclImpChDataFormat::RemoveUnusedFormats( const XclChExtTypeInfo& rTypeInfo )
1564 // data point marker only in linear 2D charts
1565 if( rTypeInfo.IsSeriesFrameFormat() )
1566 mxMarkerFmt.reset();
1567 // pie format only in pie/donut charts
1568 if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE )
1569 mxPieFmt.reset();
1570 // 3D format only in 3D bar charts
1571 if( !rTypeInfo.mb3dChart || (rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_BAR) )
1572 mx3dDataFmt.reset();
1575 void XclImpChDataFormat::UpdateDataLabel( const XclImpChDataFormat* pParentFmt )
1577 /* CHTEXT groups linked to data labels override existing CHATTACHEDLABEL
1578 records. Only if there is a CHATTACHEDLABEL record without a CHTEXT
1579 group, the contents of the CHATTACHEDLABEL record are used. In this
1580 case a new CHTEXT group is created and filled with the settings from
1581 the CHATTACHEDLABEL record. */
1582 const XclImpChText* pDefText = nullptr;
1583 if (pParentFmt)
1584 pDefText = pParentFmt->GetDataLabel();
1585 if (!pDefText)
1586 pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_DATALABEL );
1587 if (mxLabel)
1588 mxLabel->UpdateText(pDefText);
1589 else if (mxAttLabel)
1590 mxLabel = mxAttLabel->CreateDataLabel( pDefText );
1593 XclImpChSerTrendLine::XclImpChSerTrendLine( const XclImpChRoot& rRoot ) :
1594 XclImpChRoot( rRoot )
1598 void XclImpChSerTrendLine::ReadChSerTrendLine( XclImpStream& rStrm )
1600 maData.mnLineType = rStrm.ReaduInt8();
1601 maData.mnOrder = rStrm.ReaduInt8();
1602 maData.mfIntercept = rStrm.ReadDouble();
1603 maData.mnShowEquation = rStrm.ReaduInt8();
1604 maData.mnShowRSquared = rStrm.ReaduInt8();
1605 maData.mfForecastFor = rStrm.ReadDouble();
1606 maData.mfForecastBack = rStrm.ReadDouble();
1609 Reference< XRegressionCurve > XclImpChSerTrendLine::CreateRegressionCurve() const
1611 // trend line type
1612 Reference< XRegressionCurve > xRegCurve;
1613 switch( maData.mnLineType )
1615 case EXC_CHSERTREND_POLYNOMIAL:
1616 if( maData.mnOrder == 1 )
1618 xRegCurve = LinearRegressionCurve::create( comphelper::getProcessComponentContext() );
1619 } else {
1620 xRegCurve = PolynomialRegressionCurve::create( comphelper::getProcessComponentContext() );
1622 break;
1623 case EXC_CHSERTREND_EXPONENTIAL:
1624 xRegCurve = ExponentialRegressionCurve::create( comphelper::getProcessComponentContext() );
1625 break;
1626 case EXC_CHSERTREND_LOGARITHMIC:
1627 xRegCurve = LogarithmicRegressionCurve::create( comphelper::getProcessComponentContext() );
1628 break;
1629 case EXC_CHSERTREND_POWER:
1630 xRegCurve = PotentialRegressionCurve::create( comphelper::getProcessComponentContext() );
1631 break;
1632 case EXC_CHSERTREND_MOVING_AVG:
1633 xRegCurve = MovingAverageRegressionCurve::create( comphelper::getProcessComponentContext() );
1634 break;
1637 // trend line formatting
1638 if( xRegCurve.is() && mxDataFmt )
1640 ScfPropertySet aPropSet( xRegCurve );
1641 mxDataFmt->ConvertLine( aPropSet, EXC_CHOBJTYPE_TRENDLINE );
1643 aPropSet.SetProperty(EXC_CHPROP_CURVENAME, maTrendLineName);
1644 aPropSet.SetProperty(EXC_CHPROP_POLYNOMIAL_DEGREE, static_cast<sal_Int32> (maData.mnOrder) );
1645 aPropSet.SetProperty(EXC_CHPROP_MOVING_AVERAGE_PERIOD, static_cast<sal_Int32> (maData.mnOrder) );
1646 aPropSet.SetProperty(EXC_CHPROP_EXTRAPOLATE_FORWARD, maData.mfForecastFor);
1647 aPropSet.SetProperty(EXC_CHPROP_EXTRAPOLATE_BACKWARD, maData.mfForecastBack);
1649 bool bForceIntercept = rtl::math::isFinite(maData.mfIntercept);
1650 aPropSet.SetProperty(EXC_CHPROP_FORCE_INTERCEPT, bForceIntercept);
1651 if (bForceIntercept)
1653 aPropSet.SetProperty(EXC_CHPROP_INTERCEPT_VALUE, maData.mfIntercept);
1656 // #i83100# show equation and correlation coefficient
1657 ScfPropertySet aLabelProp( xRegCurve->getEquationProperties() );
1658 aLabelProp.SetBoolProperty( EXC_CHPROP_SHOWEQUATION, maData.mnShowEquation != 0 );
1659 aLabelProp.SetBoolProperty( EXC_CHPROP_SHOWCORRELATION, maData.mnShowRSquared != 0 );
1661 // #i83100# formatting of the equation text box
1662 if (const XclImpChText* pLabel = mxDataFmt->GetDataLabel())
1664 pLabel->ConvertFont( aLabelProp );
1665 pLabel->ConvertFrame( aLabelProp );
1666 pLabel->ConvertNumFmt( aLabelProp, false );
1670 return xRegCurve;
1673 XclImpChSerErrorBar::XclImpChSerErrorBar( const XclImpChRoot& rRoot ) :
1674 XclImpChRoot( rRoot )
1678 void XclImpChSerErrorBar::ReadChSerErrorBar( XclImpStream& rStrm )
1680 maData.mnBarType = rStrm.ReaduInt8();
1681 maData.mnSourceType = rStrm.ReaduInt8();
1682 maData.mnLineEnd = rStrm.ReaduInt8();
1683 rStrm.Ignore( 1 );
1684 maData.mfValue = rStrm.ReadDouble();
1685 maData.mnValueCount = rStrm.ReaduInt16();
1688 void XclImpChSerErrorBar::SetSeriesData( XclImpChSourceLinkRef const & xValueLink, XclImpChDataFormatRef const & xDataFmt )
1690 mxValueLink = xValueLink;
1691 mxDataFmt = xDataFmt;
1694 Reference< XLabeledDataSequence > XclImpChSerErrorBar::CreateValueSequence() const
1696 return lclCreateLabeledDataSequence( mxValueLink, XclChartHelper::GetErrorBarValuesRole( maData.mnBarType ) );
1699 Reference< XPropertySet > XclImpChSerErrorBar::CreateErrorBar( const XclImpChSerErrorBar* pPosBar, const XclImpChSerErrorBar* pNegBar )
1701 Reference< XPropertySet > xErrorBar;
1703 if( const XclImpChSerErrorBar* pPrimaryBar = pPosBar ? pPosBar : pNegBar )
1705 xErrorBar.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_ERRORBAR ), UNO_QUERY );
1706 ScfPropertySet aBarProp( xErrorBar );
1708 // plus/minus bars visible?
1709 aBarProp.SetBoolProperty( EXC_CHPROP_SHOWPOSITIVEERROR, pPosBar != nullptr );
1710 aBarProp.SetBoolProperty( EXC_CHPROP_SHOWNEGATIVEERROR, pNegBar != nullptr );
1712 // type of displayed error
1713 switch( pPrimaryBar->maData.mnSourceType )
1715 case EXC_CHSERERR_PERCENT:
1716 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::RELATIVE );
1717 aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue );
1718 aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue );
1719 break;
1720 case EXC_CHSERERR_FIXED:
1721 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::ABSOLUTE );
1722 aBarProp.SetProperty( EXC_CHPROP_POSITIVEERROR, pPrimaryBar->maData.mfValue );
1723 aBarProp.SetProperty( EXC_CHPROP_NEGATIVEERROR, pPrimaryBar->maData.mfValue );
1724 break;
1725 case EXC_CHSERERR_STDDEV:
1726 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_DEVIATION );
1727 aBarProp.SetProperty( EXC_CHPROP_WEIGHT, pPrimaryBar->maData.mfValue );
1728 break;
1729 case EXC_CHSERERR_STDERR:
1730 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::STANDARD_ERROR );
1731 break;
1732 case EXC_CHSERERR_CUSTOM:
1734 aBarProp.SetProperty( EXC_CHPROP_ERRORBARSTYLE, cssc::ErrorBarStyle::FROM_DATA );
1735 // attach data sequences to error bar
1736 Reference< XDataSink > xDataSink( xErrorBar, UNO_QUERY );
1737 if( xDataSink.is() )
1739 // create vector of all value sequences
1740 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
1741 // add positive values
1742 if( pPosBar )
1744 Reference< XLabeledDataSequence > xValueSeq = pPosBar->CreateValueSequence();
1745 if( xValueSeq.is() )
1746 aLabeledSeqVec.push_back( xValueSeq );
1748 // add negative values
1749 if( pNegBar )
1751 Reference< XLabeledDataSequence > xValueSeq = pNegBar->CreateValueSequence();
1752 if( xValueSeq.is() )
1753 aLabeledSeqVec.push_back( xValueSeq );
1755 // attach labeled data sequences to series
1756 if( aLabeledSeqVec.empty() )
1757 xErrorBar.clear();
1758 else
1759 xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
1762 break;
1763 default:
1764 xErrorBar.clear();
1767 // error bar formatting
1768 if( pPrimaryBar->mxDataFmt && xErrorBar.is() )
1769 pPrimaryBar->mxDataFmt->ConvertLine( aBarProp, EXC_CHOBJTYPE_ERRORBAR );
1772 return xErrorBar;
1775 XclImpChSeries::XclImpChSeries( const XclImpChRoot& rRoot, sal_uInt16 nSeriesIdx ) :
1776 XclImpChRoot( rRoot ),
1777 mnGroupIdx( EXC_CHSERGROUP_NONE ),
1778 mnSeriesIdx( nSeriesIdx ),
1779 mnParentIdx( EXC_CHSERIES_INVALID ),
1780 mbLabelDeleted( false )
1784 void XclImpChSeries::ReadHeaderRecord( XclImpStream& rStrm )
1786 maData.mnCategType = rStrm.ReaduInt16();
1787 maData.mnValueType = rStrm.ReaduInt16();
1788 maData.mnCategCount = rStrm.ReaduInt16();
1789 maData.mnValueCount = rStrm.ReaduInt16();
1790 if( GetBiff() == EXC_BIFF8 )
1792 maData.mnBubbleType = rStrm.ReaduInt16();
1793 maData.mnBubbleCount = rStrm.ReaduInt16();
1797 void XclImpChSeries::ReadSubRecord( XclImpStream& rStrm )
1799 switch( rStrm.GetRecId() )
1801 case EXC_ID_CHSOURCELINK:
1802 ReadChSourceLink( rStrm );
1803 break;
1804 case EXC_ID_CHDATAFORMAT:
1805 ReadChDataFormat( rStrm );
1806 break;
1807 case EXC_ID_CHSERGROUP:
1808 mnGroupIdx = rStrm.ReaduInt16();
1809 break;
1810 case EXC_ID_CHSERPARENT:
1811 ReadChSerParent( rStrm );
1812 break;
1813 case EXC_ID_CHSERTRENDLINE:
1814 ReadChSerTrendLine( rStrm );
1815 break;
1816 case EXC_ID_CHSERERRORBAR:
1817 ReadChSerErrorBar( rStrm );
1818 break;
1819 case EXC_ID_CHLEGENDEXCEPTION:
1820 ReadChLegendException( rStrm );
1821 break;
1825 void XclImpChSeries::SetDataFormat( const XclImpChDataFormatRef& xDataFmt )
1827 if (!xDataFmt)
1828 return;
1830 sal_uInt16 nPointIdx = xDataFmt->GetPointPos().mnPointIdx;
1831 if (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS)
1833 if (mxSeriesFmt)
1834 // Don't overwrite the existing format.
1835 return;
1837 mxSeriesFmt = xDataFmt;
1838 if (HasParentSeries())
1839 return;
1841 XclImpChTypeGroupRef pTypeGroup = GetChartData().GetTypeGroup(mnGroupIdx);
1842 if (pTypeGroup)
1843 pTypeGroup->SetUsedFormatIndex(xDataFmt->GetFormatIdx());
1845 return;
1848 if (nPointIdx >= EXC_CHDATAFORMAT_MAXPOINTCOUNT)
1849 // Above the max point count. Bail out.
1850 return;
1852 XclImpChDataFormatMap::iterator itr = maPointFmts.lower_bound(nPointIdx);
1853 if (itr == maPointFmts.end() || maPointFmts.key_comp()(nPointIdx, itr->first))
1855 // No object exists at this point index position. Insert it.
1856 itr = maPointFmts.insert(itr, XclImpChDataFormatMap::value_type(nPointIdx, xDataFmt));
1860 void XclImpChSeries::SetDataLabel( const XclImpChTextRef& xLabel )
1862 if (!xLabel)
1863 return;
1865 sal_uInt16 nPointIdx = xLabel->GetPointPos().mnPointIdx;
1866 if ((nPointIdx != EXC_CHDATAFORMAT_ALLPOINTS) && (nPointIdx >= EXC_CHDATAFORMAT_MAXPOINTCOUNT))
1867 // Above the maximum allowed data points. Bail out.
1868 return;
1870 XclImpChTextMap::iterator itr = maLabels.lower_bound(nPointIdx);
1871 if (itr == maLabels.end() || maLabels.key_comp()(nPointIdx, itr->first))
1873 // No object exists at this point index position. Insert it.
1874 itr = maLabels.insert(itr, XclImpChTextMap::value_type(nPointIdx, xLabel));
1878 void XclImpChSeries::AddChildSeries( const XclImpChSeries& rSeries )
1880 OSL_ENSURE( !HasParentSeries(), "XclImpChSeries::AddChildSeries - not allowed for child series" );
1881 if (&rSeries == this)
1883 SAL_WARN("sc.filter", "self add attempt");
1884 return;
1887 /* In Excel, trend lines and error bars are stored as own series. In Calc,
1888 these are properties of the parent series. This function adds the
1889 settings of the passed series to this series. */
1890 maTrendLines.insert( maTrendLines.end(), rSeries.maTrendLines.begin(), rSeries.maTrendLines.end() );
1891 for (auto const& it : rSeries.m_ErrorBars)
1893 m_ErrorBars.insert(std::make_pair(it.first, std::make_unique<XclImpChSerErrorBar>(*it.second)));
1897 void XclImpChSeries::FinalizeDataFormats()
1899 if( HasParentSeries() )
1901 // *** series is a child series, e.g. trend line or error bar ***
1903 // create missing series format
1904 if( !mxSeriesFmt )
1905 mxSeriesFmt = CreateDataFormat( EXC_CHDATAFORMAT_ALLPOINTS, 0 );
1907 if( mxSeriesFmt )
1909 // #i83100# set text label format, e.g. for trend line equations
1910 XclImpChTextRef xLabel;
1911 XclImpChTextMap::iterator itr = maLabels.find(EXC_CHDATAFORMAT_ALLPOINTS);
1912 if (itr != maLabels.end())
1913 xLabel = itr->second;
1914 mxSeriesFmt->SetDataLabel(xLabel);
1915 // create missing automatic formats
1916 mxSeriesFmt->UpdateTrendLineFormat();
1919 // copy series formatting to child objects
1920 for (auto const& trendLine : maTrendLines)
1922 trendLine->SetDataFormat(mxSeriesFmt);
1923 if (mxTitleLink && mxTitleLink->HasString())
1925 trendLine->SetTrendlineName(mxTitleLink->GetString());
1928 for (auto const& it : m_ErrorBars)
1930 it.second->SetSeriesData( mxValueLink, mxSeriesFmt );
1933 else if( XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() )
1935 // *** series is a regular data series ***
1937 // create missing series format
1938 if( !mxSeriesFmt )
1940 // #i51639# use a new unused format index to create series default format
1941 sal_uInt16 nFormatIdx = pTypeGroup->PopUnusedFormatIndex();
1942 mxSeriesFmt = CreateDataFormat( EXC_CHDATAFORMAT_ALLPOINTS, nFormatIdx );
1945 // set text labels to data formats
1946 for (auto const& label : maLabels)
1948 sal_uInt16 nPointIdx = label.first;
1949 if (nPointIdx == EXC_CHDATAFORMAT_ALLPOINTS)
1951 if (!mxSeriesFmt)
1952 mxSeriesFmt = CreateDataFormat(nPointIdx, EXC_CHDATAFORMAT_DEFAULT);
1953 mxSeriesFmt->SetDataLabel(label.second);
1955 else if (nPointIdx < EXC_CHDATAFORMAT_MAXPOINTCOUNT)
1957 XclImpChDataFormatRef p;
1958 XclImpChDataFormatMap::iterator itr = maPointFmts.lower_bound(nPointIdx);
1959 if (itr == maPointFmts.end() || maPointFmts.key_comp()(nPointIdx, itr->first))
1961 // No object exists at this point index position. Insert
1962 // a new one.
1963 p = CreateDataFormat(nPointIdx, EXC_CHDATAFORMAT_DEFAULT);
1964 itr = maPointFmts.insert(
1965 itr, XclImpChDataFormatMap::value_type(nPointIdx, p));
1967 else
1968 p = itr->second;
1969 p->SetDataLabel(label.second);
1973 // update series format (copy missing formatting from group default format)
1974 if( mxSeriesFmt )
1975 mxSeriesFmt->UpdateSeriesFormat( pTypeGroup->GetTypeInfo(), pTypeGroup->GetGroupFormat().get() );
1977 // update data point formats (removes unchanged automatic formatting)
1978 for (auto const& pointFormat : maPointFmts)
1979 pointFormat.second->UpdatePointFormat( pTypeGroup->GetTypeInfo(), mxSeriesFmt.get() );
1983 namespace {
1985 /** Returns the property set of the specified data point. */
1986 ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > const & xDataSeries, sal_uInt16 nPointIdx )
1988 ScfPropertySet aPropSet;
1991 aPropSet.Set( xDataSeries->getDataPointByIndex( static_cast< sal_Int32 >( nPointIdx ) ) );
1993 catch( Exception& )
1995 OSL_FAIL( "lclGetPointPropSet - no data point property set" );
1997 return aPropSet;
2000 } // namespace
2002 Reference< XLabeledDataSequence > XclImpChSeries::CreateValueSequence( const OUString& rValueRole ) const
2004 return lclCreateLabeledDataSequence( mxValueLink, rValueRole, mxTitleLink.get() );
2007 Reference< XLabeledDataSequence > XclImpChSeries::CreateCategSequence( const OUString& rCategRole ) const
2009 return lclCreateLabeledDataSequence( mxCategLink, rCategRole );
2012 Reference< XDataSeries > XclImpChSeries::CreateDataSeries() const
2014 Reference< XDataSeries > xDataSeries;
2015 if( const XclImpChTypeGroup* pTypeGroup = GetChartData().GetTypeGroup( mnGroupIdx ).get() )
2017 const XclChExtTypeInfo& rTypeInfo = pTypeGroup->GetTypeInfo();
2019 // create the data series object
2020 xDataSeries.set( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY );
2022 // attach data and title sequences to series
2023 Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
2024 if( xDataSink.is() )
2026 // create vector of all value sequences
2027 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
2028 // add Y values
2029 Reference< XLabeledDataSequence > xYValueSeq =
2030 CreateValueSequence( EXC_CHPROP_ROLE_YVALUES );
2031 if( xYValueSeq.is() )
2032 aLabeledSeqVec.push_back( xYValueSeq );
2033 // add X values
2034 if( !rTypeInfo.mbCategoryAxis )
2036 Reference< XLabeledDataSequence > xXValueSeq =
2037 CreateCategSequence( EXC_CHPROP_ROLE_XVALUES );
2038 if( xXValueSeq.is() )
2039 aLabeledSeqVec.push_back( xXValueSeq );
2040 // add size values of bubble charts
2041 if( rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES )
2043 Reference< XLabeledDataSequence > xSizeValueSeq =
2044 lclCreateLabeledDataSequence( mxBubbleLink, EXC_CHPROP_ROLE_SIZEVALUES, mxTitleLink.get() );
2045 if( xSizeValueSeq.is() )
2046 aLabeledSeqVec.push_back( xSizeValueSeq );
2049 // attach labeled data sequences to series
2050 if( !aLabeledSeqVec.empty() )
2051 xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
2054 // series formatting
2055 ScfPropertySet aSeriesProp( xDataSeries );
2056 if( mxSeriesFmt )
2057 mxSeriesFmt->Convert( aSeriesProp, rTypeInfo );
2059 if (mbLabelDeleted)
2060 aSeriesProp.SetProperty(EXC_CHPROP_SHOWLEGENDENTRY, false);
2062 // trend lines
2063 ConvertTrendLines( xDataSeries );
2065 // error bars
2066 Reference< XPropertySet > xErrorBarX = CreateErrorBar( EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS );
2067 if( xErrorBarX.is() )
2068 aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARX, xErrorBarX );
2069 Reference< XPropertySet > xErrorBarY = CreateErrorBar( EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS );
2070 if( xErrorBarY.is() )
2071 aSeriesProp.SetProperty( EXC_CHPROP_ERRORBARY, xErrorBarY );
2073 // own area formatting for every data point (TODO: varying line color not supported)
2074 bool bVarPointFmt = pTypeGroup->HasVarPointFormat() && rTypeInfo.IsSeriesFrameFormat();
2075 aSeriesProp.SetBoolProperty( EXC_CHPROP_VARYCOLORSBY, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE );
2076 // #i91271# always set area formatting for every point in pie/doughnut charts
2077 if (mxSeriesFmt && mxValueLink && ((bVarPointFmt && mxSeriesFmt->IsAutoArea()) || (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE)))
2079 for( sal_uInt16 nPointIdx = 0, nPointCount = mxValueLink->GetCellCount(); nPointIdx < nPointCount; ++nPointIdx )
2081 ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx );
2082 mxSeriesFmt->ConvertArea( aPointProp, bVarPointFmt ? nPointIdx : mnSeriesIdx );
2086 // data point formatting
2087 for (auto const& pointFormat : maPointFmts)
2089 ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, pointFormat.first );
2090 pointFormat.second->Convert( aPointProp, rTypeInfo, &aSeriesProp );
2093 return xDataSeries;
2096 void XclImpChSeries::FillAllSourceLinks( ::std::vector< ScTokenRef >& rTokens ) const
2098 if( mxValueLink )
2099 mxValueLink->FillSourceLink( rTokens );
2100 if( mxCategLink )
2101 mxCategLink->FillSourceLink( rTokens );
2102 if( mxTitleLink )
2103 mxTitleLink->FillSourceLink( rTokens );
2104 if( mxBubbleLink )
2105 mxBubbleLink->FillSourceLink( rTokens );
2108 void XclImpChSeries::ReadChSourceLink( XclImpStream& rStrm )
2110 XclImpChSourceLinkRef xSrcLink( new XclImpChSourceLink( GetChRoot() ) );
2111 xSrcLink->ReadChSourceLink( rStrm );
2112 switch( xSrcLink->GetDestType() )
2114 case EXC_CHSRCLINK_TITLE: mxTitleLink = xSrcLink; break;
2115 case EXC_CHSRCLINK_VALUES: mxValueLink = xSrcLink; break;
2116 case EXC_CHSRCLINK_CATEGORY: mxCategLink = xSrcLink; break;
2117 case EXC_CHSRCLINK_BUBBLES: mxBubbleLink = xSrcLink; break;
2121 void XclImpChSeries::ReadChDataFormat( XclImpStream& rStrm )
2123 // #i51639# chart stores all data formats and assigns them later to the series
2124 GetChartData().ReadChDataFormat( rStrm );
2127 void XclImpChSeries::ReadChSerParent( XclImpStream& rStrm )
2129 mnParentIdx = rStrm.ReaduInt16();
2130 // index to parent series is 1-based, convert it to 0-based
2131 if( mnParentIdx > 0 )
2132 --mnParentIdx;
2133 else
2134 mnParentIdx = EXC_CHSERIES_INVALID;
2137 void XclImpChSeries::ReadChSerTrendLine( XclImpStream& rStrm )
2139 XclImpChSerTrendLineRef xTrendLine( new XclImpChSerTrendLine( GetChRoot() ) );
2140 xTrendLine->ReadChSerTrendLine( rStrm );
2141 maTrendLines.push_back( xTrendLine );
2144 void XclImpChSeries::ReadChSerErrorBar( XclImpStream& rStrm )
2146 unique_ptr<XclImpChSerErrorBar> pErrorBar(new XclImpChSerErrorBar(GetChRoot()));
2147 pErrorBar->ReadChSerErrorBar(rStrm);
2148 sal_uInt8 nBarType = pErrorBar->GetBarType();
2149 m_ErrorBars.insert(std::make_pair(nBarType, std::move(pErrorBar)));
2152 XclImpChDataFormatRef XclImpChSeries::CreateDataFormat( sal_uInt16 nPointIdx, sal_uInt16 nFormatIdx )
2154 XclImpChDataFormatRef xDataFmt( new XclImpChDataFormat( GetChRoot() ) );
2155 xDataFmt->SetPointPos( XclChDataPointPos( mnSeriesIdx, nPointIdx ), nFormatIdx );
2156 return xDataFmt;
2159 void XclImpChSeries::ConvertTrendLines( Reference< XDataSeries > const & xDataSeries ) const
2161 Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY );
2162 if( xRegCurveCont.is() )
2164 for (auto const& trendLine : maTrendLines)
2168 Reference< XRegressionCurve > xRegCurve = trendLine->CreateRegressionCurve();
2169 if( xRegCurve.is() )
2171 xRegCurveCont->addRegressionCurve( xRegCurve );
2174 catch( Exception& )
2176 OSL_FAIL( "XclImpChSeries::ConvertTrendLines - cannot add regression curve" );
2182 Reference< XPropertySet > XclImpChSeries::CreateErrorBar( sal_uInt8 nPosBarId, sal_uInt8 nNegBarId ) const
2184 XclImpChSerErrorBarMap::const_iterator itrPosBar = m_ErrorBars.find(nPosBarId);
2185 XclImpChSerErrorBarMap::const_iterator itrNegBar = m_ErrorBars.find(nNegBarId);
2186 XclImpChSerErrorBarMap::const_iterator itrEnd = m_ErrorBars.end();
2187 if (itrPosBar == itrEnd || itrNegBar == itrEnd)
2188 return Reference<XPropertySet>();
2190 return XclImpChSerErrorBar::CreateErrorBar(itrPosBar->second.get(), itrNegBar->second.get());
2193 void XclImpChSeries::ReadChLegendException(XclImpStream& rStrm)
2195 rStrm.Ignore(2);
2196 sal_uInt16 nFlags = rStrm.ReaduInt16();
2197 mbLabelDeleted = (nFlags & EXC_CHLEGENDEXCEPTION_DELETED);
2200 // Chart type groups ==========================================================
2202 XclImpChType::XclImpChType( const XclImpChRoot& rRoot ) :
2203 XclImpChRoot( rRoot ),
2204 mnRecId( EXC_ID_CHUNKNOWN ),
2205 maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) )
2209 void XclImpChType::ReadChType( XclImpStream& rStrm )
2211 sal_uInt16 nRecId = rStrm.GetRecId();
2212 bool bKnownType = true;
2214 switch( nRecId )
2216 case EXC_ID_CHBAR:
2217 maData.mnOverlap = rStrm.ReadInt16();
2218 maData.mnGap = rStrm.ReadInt16();
2219 maData.mnFlags = rStrm.ReaduInt16();
2220 break;
2222 case EXC_ID_CHLINE:
2223 case EXC_ID_CHAREA:
2224 case EXC_ID_CHRADARLINE:
2225 case EXC_ID_CHRADARAREA:
2226 maData.mnFlags = rStrm.ReaduInt16();
2227 break;
2229 case EXC_ID_CHPIE:
2230 maData.mnRotation = rStrm.ReaduInt16();
2231 maData.mnPieHole = rStrm.ReaduInt16();
2232 if( GetBiff() == EXC_BIFF8 )
2233 maData.mnFlags = rStrm.ReaduInt16();
2234 else
2235 maData.mnFlags = 0;
2236 break;
2238 case EXC_ID_CHPIEEXT:
2239 maData.mnRotation = 0;
2240 maData.mnPieHole = 0;
2241 maData.mnFlags = 0;
2242 break;
2244 case EXC_ID_CHSCATTER:
2245 if( GetBiff() == EXC_BIFF8 )
2247 maData.mnBubbleSize = rStrm.ReaduInt16();
2248 maData.mnBubbleType = rStrm.ReaduInt16();
2249 maData.mnFlags = rStrm.ReaduInt16();
2251 else
2252 maData.mnFlags = 0;
2253 break;
2255 case EXC_ID_CHSURFACE:
2256 maData.mnFlags = rStrm.ReaduInt16();
2257 break;
2259 default:
2260 bKnownType = false;
2263 if( bKnownType )
2264 mnRecId = nRecId;
2267 void XclImpChType::Finalize( bool bStockChart )
2269 switch( mnRecId )
2271 case EXC_ID_CHLINE:
2272 maTypeInfo = GetChartTypeInfo( bStockChart ?
2273 EXC_CHTYPEID_STOCK : EXC_CHTYPEID_LINE );
2274 break;
2275 case EXC_ID_CHBAR:
2276 maTypeInfo = GetChartTypeInfo( ::get_flagvalue(
2277 maData.mnFlags, EXC_CHBAR_HORIZONTAL,
2278 EXC_CHTYPEID_HORBAR, EXC_CHTYPEID_BAR ) );
2279 break;
2280 case EXC_ID_CHPIE:
2281 maTypeInfo = GetChartTypeInfo( (maData.mnPieHole > 0) ?
2282 EXC_CHTYPEID_DONUT : EXC_CHTYPEID_PIE );
2283 break;
2284 case EXC_ID_CHSCATTER:
2285 maTypeInfo = GetChartTypeInfo( ::get_flagvalue(
2286 maData.mnFlags, EXC_CHSCATTER_BUBBLES,
2287 EXC_CHTYPEID_BUBBLES, EXC_CHTYPEID_SCATTER ) );
2288 break;
2289 default:
2290 maTypeInfo = GetChartTypeInfo( mnRecId );
2293 switch( maTypeInfo.meTypeId )
2295 case EXC_CHTYPEID_PIEEXT:
2296 case EXC_CHTYPEID_BUBBLES:
2297 case EXC_CHTYPEID_SURFACE:
2298 case EXC_CHTYPEID_UNKNOWN:
2299 GetTracer().TraceChartUnKnownType();
2300 break;
2301 default:;
2305 bool XclImpChType::IsStacked() const
2307 bool bStacked = false;
2308 if( maTypeInfo.mbSupportsStacking ) switch( maTypeInfo.meTypeCateg )
2310 case EXC_CHTYPECATEG_LINE:
2311 bStacked =
2312 ::get_flag( maData.mnFlags, EXC_CHLINE_STACKED ) &&
2313 !::get_flag( maData.mnFlags, EXC_CHLINE_PERCENT );
2314 break;
2315 case EXC_CHTYPECATEG_BAR:
2316 bStacked =
2317 ::get_flag( maData.mnFlags, EXC_CHBAR_STACKED ) &&
2318 !::get_flag( maData.mnFlags, EXC_CHBAR_PERCENT );
2319 break;
2320 default:;
2322 return bStacked;
2325 bool XclImpChType::IsPercent() const
2327 bool bPercent = false;
2328 if( maTypeInfo.mbSupportsStacking ) switch( maTypeInfo.meTypeCateg )
2330 case EXC_CHTYPECATEG_LINE:
2331 bPercent =
2332 ::get_flag( maData.mnFlags, EXC_CHLINE_STACKED ) &&
2333 ::get_flag( maData.mnFlags, EXC_CHLINE_PERCENT );
2334 break;
2335 case EXC_CHTYPECATEG_BAR:
2336 bPercent =
2337 ::get_flag( maData.mnFlags, EXC_CHBAR_STACKED ) &&
2338 ::get_flag( maData.mnFlags, EXC_CHBAR_PERCENT );
2339 break;
2340 default:;
2342 return bPercent;
2345 bool XclImpChType::HasCategoryLabels() const
2347 // radar charts disable category labels in chart type, not in CHTICK of X axis
2348 return (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_RADAR) || ::get_flag( maData.mnFlags, EXC_CHRADAR_AXISLABELS );
2351 Reference< XCoordinateSystem > XclImpChType::CreateCoordSystem( bool b3dChart ) const
2353 // create the coordinate system object
2354 Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
2355 Reference< XCoordinateSystem > xCoordSystem;
2356 if( maTypeInfo.mbPolarCoordSystem )
2358 if( b3dChart )
2359 xCoordSystem = css::chart2::PolarCoordinateSystem3d::create(xContext);
2360 else
2361 xCoordSystem = css::chart2::PolarCoordinateSystem2d::create(xContext);
2363 else
2365 if( b3dChart )
2366 xCoordSystem = css::chart2::CartesianCoordinateSystem3d::create(xContext);
2367 else
2368 xCoordSystem = css::chart2::CartesianCoordinateSystem2d::create(xContext);
2371 // swap X and Y axis
2372 if( maTypeInfo.mbSwappedAxesSet )
2374 ScfPropertySet aCoordSysProp( xCoordSystem );
2375 aCoordSysProp.SetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS, true );
2378 return xCoordSystem;
2381 Reference< XChartType > XclImpChType::CreateChartType( Reference< XDiagram > const & xDiagram, bool b3dChart ) const
2383 OUString aService = OUString::createFromAscii( maTypeInfo.mpcServiceName );
2384 Reference< XChartType > xChartType( ScfApiHelper::CreateInstance( aService ), UNO_QUERY );
2386 // additional properties
2387 switch( maTypeInfo.meTypeCateg )
2389 case EXC_CHTYPECATEG_BAR:
2391 ScfPropertySet aTypeProp( xChartType );
2392 Sequence< sal_Int32 > aInt32Seq( 2 );
2393 aInt32Seq[ 0 ] = aInt32Seq[ 1 ] = -maData.mnOverlap;
2394 aTypeProp.SetProperty( EXC_CHPROP_OVERLAPSEQ, aInt32Seq );
2395 aInt32Seq[ 0 ] = aInt32Seq[ 1 ] = maData.mnGap;
2396 aTypeProp.SetProperty( EXC_CHPROP_GAPWIDTHSEQ, aInt32Seq );
2398 break;
2399 case EXC_CHTYPECATEG_PIE:
2401 ScfPropertySet aTypeProp( xChartType );
2402 aTypeProp.SetBoolProperty( EXC_CHPROP_USERINGS, maTypeInfo.meTypeId == EXC_CHTYPEID_DONUT );
2403 /* #i85166# starting angle of first pie slice. 3D pie charts use Y
2404 rotation setting in view3D element. Of-pie charts do not
2405 support pie rotation. */
2406 if( !b3dChart && (maTypeInfo.meTypeId != EXC_CHTYPEID_PIEEXT) )
2408 ScfPropertySet aDiaProp( xDiagram );
2409 XclImpChRoot::ConvertPieRotation( aDiaProp, maData.mnRotation );
2412 break;
2413 default:;
2416 return xChartType;
2419 void XclImpChChart3d::ReadChChart3d( XclImpStream& rStrm )
2421 maData.mnRotation = rStrm.ReaduInt16();
2422 maData.mnElevation = rStrm.ReadInt16();
2423 maData.mnEyeDist = rStrm.ReaduInt16();
2424 maData.mnRelHeight = rStrm.ReaduInt16();
2425 maData.mnRelDepth = rStrm.ReaduInt16();
2426 maData.mnDepthGap = rStrm.ReaduInt16();
2427 maData.mnFlags = rStrm.ReaduInt16();
2430 void XclImpChChart3d::Convert( ScfPropertySet& rPropSet, bool b3dWallChart ) const
2432 namespace cssd = ::com::sun::star::drawing;
2434 // #i104057# do not assert this, written by broken external generators
2435 // OSL_ENSURE( ::get_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS ) == b3dWallChart, "XclImpChChart3d::Convert - wrong wall flag" );
2437 sal_Int32 nRotationY = 0;
2438 sal_Int32 nRotationX = 0;
2439 sal_Int32 nPerspective = 15;
2440 bool bRightAngled = false;
2441 cssd::ProjectionMode eProjMode = cssd::ProjectionMode_PERSPECTIVE;
2442 Color aAmbientColor, aLightColor;
2444 if( b3dWallChart )
2446 // Y rotation (Excel [0..359], Chart2 [-179,180])
2447 nRotationY = NormAngle180<sal_Int32>(maData.mnRotation);
2448 // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180])
2449 nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, -90, 90 );
2450 // perspective (Excel and Chart2 [0,100])
2451 nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 );
2452 // right-angled axes
2453 bRightAngled = !::get_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D );
2454 // projection mode (parallel axes, if right-angled, #i90360# or if perspective is at 0%)
2455 bool bParallel = bRightAngled || (nPerspective == 0);
2456 eProjMode = bParallel ? cssd::ProjectionMode_PARALLEL : cssd::ProjectionMode_PERSPECTIVE;
2457 // ambient color (Gray 20%)
2458 aAmbientColor = Color( 204, 204, 204 );
2459 // light color (Gray 60%)
2460 aLightColor = Color( 102, 102, 102 );
2462 else
2464 // Y rotation not used in pie charts, but 'first pie slice angle'
2465 nRotationY = 0;
2466 XclImpChRoot::ConvertPieRotation( rPropSet, maData.mnRotation );
2467 // X rotation a.k.a. elevation (map Excel [10..80] to Chart2 [-80,-10])
2468 nRotationX = limit_cast< sal_Int32, sal_Int32 >( maData.mnElevation, 10, 80 ) - 90;
2469 // perspective (Excel and Chart2 [0,100])
2470 nPerspective = limit_cast< sal_Int32, sal_Int32 >( maData.mnEyeDist, 0, 100 );
2471 // no right-angled axes in pie charts, but parallel projection
2472 bRightAngled = false;
2473 eProjMode = cssd::ProjectionMode_PARALLEL;
2474 // ambient color (Gray 30%)
2475 aAmbientColor = Color( 179, 179, 179 );
2476 // light color (Gray 70%)
2477 aLightColor = Color( 76, 76, 76 );
2480 // properties
2481 rPropSet.SetProperty( EXC_CHPROP_3DRELATIVEHEIGHT, static_cast<sal_Int32>(maData.mnRelHeight / 2)); // seems to be 200%, change to 100%
2482 rPropSet.SetProperty( EXC_CHPROP_ROTATIONVERTICAL, nRotationY );
2483 rPropSet.SetProperty( EXC_CHPROP_ROTATIONHORIZONTAL, nRotationX );
2484 rPropSet.SetProperty( EXC_CHPROP_PERSPECTIVE, nPerspective );
2485 rPropSet.SetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES, bRightAngled );
2486 rPropSet.SetProperty( EXC_CHPROP_D3DSCENEPERSPECTIVE, eProjMode );
2488 // light settings
2489 rPropSet.SetProperty( EXC_CHPROP_D3DSCENESHADEMODE, cssd::ShadeMode_FLAT );
2490 rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENEAMBIENTCOLOR, aAmbientColor );
2491 rPropSet.SetBoolProperty( EXC_CHPROP_D3DSCENELIGHTON1, false );
2492 rPropSet.SetBoolProperty( EXC_CHPROP_D3DSCENELIGHTON2, true );
2493 rPropSet.SetColorProperty( EXC_CHPROP_D3DSCENELIGHTCOLOR2, aLightColor );
2494 rPropSet.SetProperty( EXC_CHPROP_D3DSCENELIGHTDIR2, cssd::Direction3D( 0.2, 0.4, 1.0 ) );
2497 XclImpChLegend::XclImpChLegend( const XclImpChRoot& rRoot ) :
2498 XclImpChRoot( rRoot )
2502 void XclImpChLegend::ReadHeaderRecord( XclImpStream& rStrm )
2504 rStrm >> maData.maRect;
2505 maData.mnDockMode = rStrm.ReaduInt8();
2506 maData.mnSpacing = rStrm.ReaduInt8();
2507 maData.mnFlags = rStrm.ReaduInt16();
2509 // trace unsupported features
2510 if( GetTracer().IsEnabled() )
2512 if( maData.mnDockMode == EXC_CHLEGEND_NOTDOCKED )
2513 GetTracer().TraceChartLegendPosition();
2514 if( ::get_flag( maData.mnFlags, EXC_CHLEGEND_DATATABLE ) )
2515 GetTracer().TraceChartDataTable();
2519 void XclImpChLegend::ReadSubRecord( XclImpStream& rStrm )
2521 switch( rStrm.GetRecId() )
2523 case EXC_ID_CHFRAMEPOS:
2524 mxFramePos.reset( new XclImpChFramePos );
2525 mxFramePos->ReadChFramePos( rStrm );
2526 break;
2527 case EXC_ID_CHTEXT:
2528 mxText.reset( new XclImpChText( GetChRoot() ) );
2529 mxText->ReadRecordGroup( rStrm );
2530 break;
2531 case EXC_ID_CHFRAME:
2532 mxFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_LEGEND ) );
2533 mxFrame->ReadRecordGroup( rStrm );
2534 break;
2538 void XclImpChLegend::Finalize()
2540 // legend default formatting differs in OOChart and Excel, missing frame means automatic
2541 if( !mxFrame )
2542 mxFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_LEGEND ) );
2543 // Update text formatting. If mxText is empty, the passed default text is used.
2544 lclUpdateText( mxText, GetChartData().GetDefaultText( EXC_CHTEXTTYPE_LEGEND ) );
2547 Reference< XLegend > XclImpChLegend::CreateLegend() const
2549 Reference< XLegend > xLegend( ScfApiHelper::CreateInstance( SERVICE_CHART2_LEGEND ), UNO_QUERY );
2550 if( xLegend.is() )
2552 ScfPropertySet aLegendProp( xLegend );
2553 aLegendProp.SetBoolProperty( EXC_CHPROP_SHOW, true );
2555 // frame properties
2556 if( mxFrame )
2557 mxFrame->Convert( aLegendProp );
2558 // text properties
2559 if( mxText )
2560 mxText->ConvertFont( aLegendProp );
2562 /* Legend position and size. Default positions are used only if the
2563 plot area is positioned automatically (Excel sets the plot area to
2564 manual mode, if the legend is moved or resized). With manual plot
2565 areas, Excel ignores the value in maData.mnDockMode completely. */
2566 cssc2::LegendPosition eApiPos = cssc2::LegendPosition_CUSTOM;
2567 cssc::ChartLegendExpansion eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
2568 if( !GetChartData().IsManualPlotArea() ) switch( maData.mnDockMode )
2570 case EXC_CHLEGEND_LEFT:
2571 eApiPos = cssc2::LegendPosition_LINE_START;
2572 eApiExpand = cssc::ChartLegendExpansion_HIGH;
2573 break;
2574 case EXC_CHLEGEND_RIGHT:
2575 // top-right not supported
2576 case EXC_CHLEGEND_CORNER:
2577 eApiPos = cssc2::LegendPosition_LINE_END;
2578 eApiExpand = cssc::ChartLegendExpansion_HIGH;
2579 break;
2580 case EXC_CHLEGEND_TOP:
2581 eApiPos = cssc2::LegendPosition_PAGE_START;
2582 eApiExpand = cssc::ChartLegendExpansion_WIDE;
2583 break;
2584 case EXC_CHLEGEND_BOTTOM:
2585 eApiPos = cssc2::LegendPosition_PAGE_END;
2586 eApiExpand = cssc::ChartLegendExpansion_WIDE;
2587 break;
2590 // no automatic position/size: try to find the correct position and size
2591 if( eApiPos == cssc2::LegendPosition_CUSTOM )
2593 const XclChFramePos* pFramePos = mxFramePos ? &mxFramePos->GetFramePosData() : nullptr;
2595 /* Legend position. Only the settings from the CHFRAMEPOS record
2596 are used by Excel, the position in the CHLEGEND record will be
2597 ignored. */
2598 if( pFramePos )
2600 RelativePosition aRelPos(
2601 CalcRelativeFromChartX( pFramePos->maRect.mnX ),
2602 CalcRelativeFromChartY( pFramePos->maRect.mnY ),
2603 css::drawing::Alignment_TOP_LEFT );
2604 aLegendProp.SetProperty( EXC_CHPROP_RELATIVEPOSITION, aRelPos );
2606 else
2608 // no manual position/size found, just go for the default
2609 eApiPos = cssc2::LegendPosition_LINE_END;
2612 /* Legend size. The member mnBRMode specifies whether size is
2613 automatic or changes manually. Manual size is given in points,
2614 not in chart units. */
2615 if( pFramePos && (pFramePos->mnBRMode == EXC_CHFRAMEPOS_ABSSIZE_POINTS) &&
2616 (pFramePos->maRect.mnWidth > 0) && (pFramePos->maRect.mnHeight > 0) )
2618 eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
2619 sal_Int32 nWidthHmm = static_cast< sal_Int32 >( pFramePos->maRect.mnWidth / EXC_POINTS_PER_HMM );
2620 sal_Int32 nHeightHmm = static_cast< sal_Int32 >( pFramePos->maRect.mnHeight / EXC_POINTS_PER_HMM );
2621 RelativeSize aRelSize( CalcRelativeFromHmmX( nWidthHmm ), CalcRelativeFromHmmY( nHeightHmm ) );
2622 aLegendProp.SetProperty( EXC_CHPROP_RELATIVESIZE, aRelSize );
2624 else
2626 // automatic size: determine entry direction from flags
2627 eApiExpand = ::get_flagvalue( maData.mnFlags, EXC_CHLEGEND_STACKED,
2628 cssc::ChartLegendExpansion_HIGH, cssc::ChartLegendExpansion_WIDE );
2631 aLegendProp.SetProperty( EXC_CHPROP_ANCHORPOSITION, eApiPos );
2632 aLegendProp.SetProperty( EXC_CHPROP_EXPANSION, eApiExpand );
2634 return xLegend;
2637 XclImpChDropBar::XclImpChDropBar( sal_uInt16 nDropBar ) :
2638 mnDropBar( nDropBar ),
2639 mnBarDist( 0 )
2643 void XclImpChDropBar::ReadHeaderRecord( XclImpStream& rStrm )
2645 mnBarDist = rStrm.ReaduInt16();
2648 void XclImpChDropBar::Convert( const XclImpChRoot& rRoot, ScfPropertySet& rPropSet ) const
2650 XclChObjectType eObjType = EXC_CHOBJTYPE_BACKGROUND;
2651 switch( mnDropBar )
2653 case EXC_CHDROPBAR_UP: eObjType = EXC_CHOBJTYPE_WHITEDROPBAR; break;
2654 case EXC_CHDROPBAR_DOWN: eObjType = EXC_CHOBJTYPE_BLACKDROPBAR; break;
2656 ConvertFrameBase( rRoot, rPropSet, eObjType );
2659 XclImpChTypeGroup::XclImpChTypeGroup( const XclImpChRoot& rRoot ) :
2660 XclImpChRoot( rRoot ),
2661 maType( rRoot ),
2662 maTypeInfo( maType.GetTypeInfo() )
2664 // Initialize unused format indexes set. At this time, all formats are unused.
2665 for( sal_uInt16 nFormatIdx = 0; nFormatIdx <= EXC_CHSERIES_MAXSERIES; ++nFormatIdx )
2666 maUnusedFormats.insert( maUnusedFormats.end(), nFormatIdx );
2669 void XclImpChTypeGroup::ReadHeaderRecord( XclImpStream& rStrm )
2671 rStrm.Ignore( 16 );
2672 maData.mnFlags = rStrm.ReaduInt16();
2673 maData.mnGroupIdx = rStrm.ReaduInt16();
2676 void XclImpChTypeGroup::ReadSubRecord( XclImpStream& rStrm )
2678 switch( rStrm.GetRecId() )
2680 case EXC_ID_CHCHART3D:
2681 mxChart3d.reset( new XclImpChChart3d );
2682 mxChart3d->ReadChChart3d( rStrm );
2683 break;
2684 case EXC_ID_CHLEGEND:
2685 mxLegend.reset( new XclImpChLegend( GetChRoot() ) );
2686 mxLegend->ReadRecordGroup( rStrm );
2687 break;
2688 case EXC_ID_CHDEFAULTTEXT:
2689 GetChartData().ReadChDefaultText( rStrm );
2690 break;
2691 case EXC_ID_CHDROPBAR:
2692 ReadChDropBar( rStrm );
2693 break;
2694 case EXC_ID_CHCHARTLINE:
2695 ReadChChartLine( rStrm );
2696 break;
2697 case EXC_ID_CHDATAFORMAT:
2698 ReadChDataFormat( rStrm );
2699 break;
2700 default:
2701 maType.ReadChType( rStrm );
2705 void XclImpChTypeGroup::Finalize()
2707 // check and set valid chart type
2708 bool bStockChart =
2709 (maType.GetRecId() == EXC_ID_CHLINE) && // must be a line chart
2710 !mxChart3d && // must be a 2d chart
2711 m_ChartLines.find(EXC_CHCHARTLINE_HILO) != m_ChartLines.end() && // must contain hi-lo lines
2712 (maSeries.size() == static_cast<XclImpChSeriesVec::size_type>(HasDropBars() ? 4 : 3)); // correct series count
2713 maType.Finalize( bStockChart );
2715 // extended type info
2716 maTypeInfo.Set( maType.GetTypeInfo(), static_cast< bool >(mxChart3d), false );
2718 // reverse series order for some unstacked 2D chart types
2719 if( maTypeInfo.mbReverseSeries && !Is3dChart() && !maType.IsStacked() && !maType.IsPercent() )
2720 ::std::reverse( maSeries.begin(), maSeries.end() );
2722 // update chart type group format, may depend on chart type finalized above
2723 if( mxGroupFmt )
2724 mxGroupFmt->UpdateGroupFormat( maTypeInfo );
2727 void XclImpChTypeGroup::AddSeries( XclImpChSeriesRef const & xSeries )
2729 if( xSeries )
2730 maSeries.push_back( xSeries );
2731 // store first inserted series separately, series order may be reversed later
2732 if( !mxFirstSeries )
2733 mxFirstSeries = xSeries;
2736 void XclImpChTypeGroup::SetUsedFormatIndex( sal_uInt16 nFormatIdx )
2738 maUnusedFormats.erase( nFormatIdx );
2741 sal_uInt16 XclImpChTypeGroup::PopUnusedFormatIndex()
2743 OSL_ENSURE( !maUnusedFormats.empty(), "XclImpChTypeGroup::PopUnusedFormatIndex - no more format indexes available" );
2744 sal_uInt16 nFormatIdx = maUnusedFormats.empty() ? 0 : *maUnusedFormats.begin();
2745 SetUsedFormatIndex( nFormatIdx );
2746 return nFormatIdx;
2749 bool XclImpChTypeGroup::HasVarPointFormat() const
2751 return ::get_flag( maData.mnFlags, EXC_CHTYPEGROUP_VARIEDCOLORS ) &&
2752 ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_MULTI) || // multiple series allowed
2753 ((maTypeInfo.meVarPointMode == EXC_CHVARPOINT_SINGLE) && // or exactly 1 series?
2754 (maSeries.size() == 1)));
2757 bool XclImpChTypeGroup::HasConnectorLines() const
2759 // existence of connector lines (only in stacked bar charts)
2760 if ( !(maType.IsStacked() || maType.IsPercent()) || (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_BAR) )
2761 return false;
2762 XclImpChLineFormatMap::const_iterator aConLine = m_ChartLines.find(EXC_CHCHARTLINE_CONNECT);
2763 return (aConLine != m_ChartLines.end() && aConLine->second.HasLine());
2766 OUString XclImpChTypeGroup::GetSingleSeriesTitle() const
2768 // no automatic title for series with trendlines or error bars
2769 // pie charts always show an automatic title, even if more series exist
2770 return (mxFirstSeries && !mxFirstSeries->HasChildSeries() && (maTypeInfo.mbSingleSeriesVis || (maSeries.size() == 1))) ?
2771 mxFirstSeries->GetTitle() : OUString();
2774 void XclImpChTypeGroup::ConvertChart3d( ScfPropertySet& rPropSet ) const
2776 if( mxChart3d )
2777 mxChart3d->Convert( rPropSet, Is3dWallChart() );
2780 Reference< XCoordinateSystem > XclImpChTypeGroup::CreateCoordSystem() const
2782 return maType.CreateCoordSystem( Is3dChart() );
2785 Reference< XChartType > XclImpChTypeGroup::CreateChartType( Reference< XDiagram > const & xDiagram, sal_Int32 nApiAxesSetIdx ) const
2787 OSL_ENSURE( IsValidGroup(), "XclImpChTypeGroup::CreateChartType - type group without series" );
2789 // create the chart type object
2790 Reference< XChartType > xChartType = maType.CreateChartType( xDiagram, Is3dChart() );
2792 // bar chart connector lines
2793 if( HasConnectorLines() )
2795 ScfPropertySet aDiaProp( xDiagram );
2796 aDiaProp.SetBoolProperty( EXC_CHPROP_CONNECTBARS, true );
2799 /* Stock chart needs special processing. Create one 'big' series with
2800 data sequences of different roles. */
2801 if( maTypeInfo.meTypeId == EXC_CHTYPEID_STOCK )
2802 CreateStockSeries( xChartType, nApiAxesSetIdx );
2803 else
2804 CreateDataSeries( xChartType, nApiAxesSetIdx );
2806 return xChartType;
2809 Reference< XLabeledDataSequence > XclImpChTypeGroup::CreateCategSequence() const
2811 Reference< XLabeledDataSequence > xLabeledSeq;
2812 // create category sequence from first visible series
2813 if( mxFirstSeries )
2814 xLabeledSeq = mxFirstSeries->CreateCategSequence( EXC_CHPROP_ROLE_CATEG );
2815 return xLabeledSeq;
2818 void XclImpChTypeGroup::ReadChDropBar( XclImpStream& rStrm )
2820 if (m_DropBars.find(EXC_CHDROPBAR_UP) == m_DropBars.end())
2822 unique_ptr<XclImpChDropBar> p(new XclImpChDropBar(EXC_CHDROPBAR_UP));
2823 p->ReadRecordGroup(rStrm);
2824 m_DropBars.insert(std::make_pair(EXC_CHDROPBAR_UP, std::move(p)));
2826 else if (m_DropBars.find(EXC_CHDROPBAR_DOWN) == m_DropBars.end())
2828 unique_ptr<XclImpChDropBar> p(new XclImpChDropBar(EXC_CHDROPBAR_DOWN));
2829 p->ReadRecordGroup(rStrm);
2830 m_DropBars.insert(std::make_pair(EXC_CHDROPBAR_DOWN, std::move(p)));
2834 void XclImpChTypeGroup::ReadChChartLine( XclImpStream& rStrm )
2836 sal_uInt16 nLineId = rStrm.ReaduInt16();
2837 if( (rStrm.GetNextRecId() == EXC_ID_CHLINEFORMAT) && rStrm.StartNextRecord() )
2839 XclImpChLineFormat aLineFmt;
2840 aLineFmt.ReadChLineFormat( rStrm );
2841 m_ChartLines[ nLineId ] = aLineFmt;
2845 void XclImpChTypeGroup::ReadChDataFormat( XclImpStream& rStrm )
2847 // global series and data point format
2848 XclImpChDataFormatRef xDataFmt( new XclImpChDataFormat( GetChRoot() ) );
2849 xDataFmt->ReadRecordGroup( rStrm );
2850 const XclChDataPointPos& rPos = xDataFmt->GetPointPos();
2851 if( (rPos.mnSeriesIdx == 0) && (rPos.mnPointIdx == 0) &&
2852 (xDataFmt->GetFormatIdx() == EXC_CHDATAFORMAT_DEFAULT) )
2853 mxGroupFmt = xDataFmt;
2856 void XclImpChTypeGroup::InsertDataSeries( Reference< XChartType > const & xChartType,
2857 Reference< XDataSeries > const & xSeries, sal_Int32 nApiAxesSetIdx ) const
2859 Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY );
2860 if( xSeriesCont.is() && xSeries.is() )
2862 // series stacking mode
2863 cssc2::StackingDirection eStacking = cssc2::StackingDirection_NO_STACKING;
2864 // stacked overrides deep-3d
2865 if( maType.IsStacked() || maType.IsPercent() )
2866 eStacking = cssc2::StackingDirection_Y_STACKING;
2867 else if( Is3dDeepChart() )
2868 eStacking = cssc2::StackingDirection_Z_STACKING;
2870 // additional series properties
2871 ScfPropertySet aSeriesProp( xSeries );
2872 aSeriesProp.SetProperty( EXC_CHPROP_STACKINGDIR, eStacking );
2873 aSeriesProp.SetProperty( EXC_CHPROP_ATTAXISINDEX, nApiAxesSetIdx );
2875 // insert series into container
2878 xSeriesCont->addDataSeries( xSeries );
2880 catch( Exception& )
2882 OSL_FAIL( "XclImpChTypeGroup::InsertDataSeries - cannot add data series" );
2887 void XclImpChTypeGroup::CreateDataSeries( Reference< XChartType > const & xChartType, sal_Int32 nApiAxesSetIdx ) const
2889 bool bSpline = false;
2890 for (auto const& elem : maSeries)
2892 Reference< XDataSeries > xDataSeries = elem->CreateDataSeries();
2893 InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx );
2894 bSpline |= elem->HasSpline();
2896 // spline - TODO: set at single series (#i66858#)
2897 if( bSpline && !maTypeInfo.IsSeriesFrameFormat() && (maTypeInfo.meTypeCateg != EXC_CHTYPECATEG_RADAR) )
2899 ScfPropertySet aTypeProp( xChartType );
2900 aTypeProp.SetProperty( EXC_CHPROP_CURVESTYLE, css::chart2::CurveStyle_CUBIC_SPLINES );
2904 void XclImpChTypeGroup::CreateStockSeries( Reference< XChartType > const & xChartType, sal_Int32 nApiAxesSetIdx ) const
2906 // create the data series object
2907 Reference< XDataSeries > xDataSeries( ScfApiHelper::CreateInstance( SERVICE_CHART2_DATASERIES ), UNO_QUERY );
2908 Reference< XDataSink > xDataSink( xDataSeries, UNO_QUERY );
2909 if( xDataSink.is() )
2911 // create a list of data sequences from all series
2912 ::std::vector< Reference< XLabeledDataSequence > > aLabeledSeqVec;
2913 OSL_ENSURE( maSeries.size() >= 3, "XclImpChTypeGroup::CreateChartType - missing stock series" );
2914 int nRoleIdx = (maSeries.size() == 3) ? 1 : 0;
2915 for( const auto& rxSeries : maSeries )
2917 // create a data sequence with a specific role
2918 OUString aRole;
2919 switch( nRoleIdx )
2921 case 0: aRole = EXC_CHPROP_ROLE_OPENVALUES; break;
2922 case 1: aRole = EXC_CHPROP_ROLE_HIGHVALUES; break;
2923 case 2: aRole = EXC_CHPROP_ROLE_LOWVALUES; break;
2924 case 3: aRole = EXC_CHPROP_ROLE_CLOSEVALUES; break;
2926 Reference< XLabeledDataSequence > xDataSeq = rxSeries->CreateValueSequence( aRole );
2927 if( xDataSeq.is() )
2928 aLabeledSeqVec.push_back( xDataSeq );
2929 ++nRoleIdx;
2930 if (nRoleIdx >= 4)
2931 break;
2934 // attach labeled data sequences to series and insert series into chart type
2935 xDataSink->setData( ScfApiHelper::VectorToSequence( aLabeledSeqVec ) );
2937 // formatting of special stock chart elements
2938 ScfPropertySet aTypeProp( xChartType );
2939 aTypeProp.SetBoolProperty( EXC_CHPROP_JAPANESE, HasDropBars() );
2940 aTypeProp.SetBoolProperty( EXC_CHPROP_SHOWFIRST, HasDropBars() );
2941 aTypeProp.SetBoolProperty( EXC_CHPROP_SHOWHIGHLOW, true );
2942 // hi-lo line format
2943 XclImpChLineFormatMap::const_iterator aHiLoLine = m_ChartLines.find( EXC_CHCHARTLINE_HILO );
2944 if (aHiLoLine != m_ChartLines.end())
2946 ScfPropertySet aSeriesProp( xDataSeries );
2947 aHiLoLine->second.Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE );
2949 // white dropbar format
2950 XclImpChDropBarMap::const_iterator itr = m_DropBars.find(EXC_CHDROPBAR_UP);
2951 Reference<XPropertySet> xWhitePropSet;
2952 if (itr != m_DropBars.end() && aTypeProp.GetProperty(xWhitePropSet, EXC_CHPROP_WHITEDAY))
2954 ScfPropertySet aBarProp( xWhitePropSet );
2955 itr->second->Convert(GetChRoot(), aBarProp);
2957 // black dropbar format
2958 itr = m_DropBars.find(EXC_CHDROPBAR_DOWN);
2959 Reference<XPropertySet> xBlackPropSet;
2960 if (itr != m_DropBars.end() && aTypeProp.GetProperty(xBlackPropSet, EXC_CHPROP_BLACKDAY))
2962 ScfPropertySet aBarProp( xBlackPropSet );
2963 itr->second->Convert(GetChRoot(), aBarProp);
2966 // insert the series into the chart type object
2967 InsertDataSeries( xChartType, xDataSeries, nApiAxesSetIdx );
2971 // Axes =======================================================================
2973 XclImpChLabelRange::XclImpChLabelRange( const XclImpChRoot& rRoot ) :
2974 XclImpChRoot( rRoot )
2978 void XclImpChLabelRange::ReadChLabelRange( XclImpStream& rStrm )
2980 maLabelData.mnCross = rStrm.ReaduInt16();
2981 maLabelData.mnLabelFreq = rStrm.ReaduInt16();
2982 maLabelData.mnTickFreq = rStrm.ReaduInt16();
2983 maLabelData.mnFlags = rStrm.ReaduInt16();
2986 void XclImpChLabelRange::ReadChDateRange( XclImpStream& rStrm )
2988 maDateData.mnMinDate = rStrm.ReaduInt16();
2989 maDateData.mnMaxDate = rStrm.ReaduInt16();
2990 maDateData.mnMajorStep = rStrm.ReaduInt16();
2991 maDateData.mnMajorUnit = rStrm.ReaduInt16();
2992 maDateData.mnMinorStep = rStrm.ReaduInt16();
2993 maDateData.mnMinorUnit = rStrm.ReaduInt16();
2994 maDateData.mnBaseUnit = rStrm.ReaduInt16();
2995 maDateData.mnCross = rStrm.ReaduInt16();
2996 maDateData.mnFlags = rStrm.ReaduInt16();
2999 void XclImpChLabelRange::Convert( ScfPropertySet& rPropSet, ScaleData& rScaleData, bool bMirrorOrient ) const
3001 // automatic axis type detection
3002 rScaleData.AutoDateAxis = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTODATE );
3004 // the flag EXC_CHDATERANGE_DATEAXIS specifies whether this is a date axis
3005 if( ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS ) )
3007 /* Chart2 requires axis type CATEGORY for automatic category/date axis
3008 (even if it is a date axis currently). */
3009 rScaleData.AxisType = rScaleData.AutoDateAxis ? cssc2::AxisType::CATEGORY : cssc2::AxisType::DATE;
3010 rScaleData.Scaling = css::chart2::LinearScaling::create( comphelper::getProcessComponentContext() );
3011 /* Min/max values depend on base time unit, they specify the number of
3012 days, months, or years starting from null date. */
3013 lclConvertTimeValue( GetRoot(), rScaleData.Minimum, maDateData.mnMinDate, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMIN ), maDateData.mnBaseUnit );
3014 lclConvertTimeValue( GetRoot(), rScaleData.Maximum, maDateData.mnMaxDate, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAX ), maDateData.mnBaseUnit );
3015 // increment
3016 cssc::TimeIncrement& rTimeIncrement = rScaleData.TimeIncrement;
3017 lclConvertTimeInterval( rTimeIncrement.MajorTimeInterval, maDateData.mnMajorStep, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAJOR ), maDateData.mnMajorUnit );
3018 lclConvertTimeInterval( rTimeIncrement.MinorTimeInterval, maDateData.mnMinorStep, ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMINOR ), maDateData.mnMinorUnit );
3019 // base unit
3020 if( ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOBASE ) )
3021 rTimeIncrement.TimeResolution.clear();
3022 else
3023 rTimeIncrement.TimeResolution <<= lclGetApiTimeUnit( maDateData.mnBaseUnit );
3025 else
3027 // do not overlap text unless all labels are visible
3028 rPropSet.SetBoolProperty( EXC_CHPROP_TEXTOVERLAP, maLabelData.mnLabelFreq == 1 );
3029 // do not break text into several lines unless all labels are visible
3030 rPropSet.SetBoolProperty( EXC_CHPROP_TEXTBREAK, maLabelData.mnLabelFreq == 1 );
3031 // do not stagger labels in two lines
3032 rPropSet.SetProperty( EXC_CHPROP_ARRANGEORDER, cssc::ChartAxisArrangeOrderType_SIDE_BY_SIDE );
3035 // reverse order
3036 bool bReverse = ::get_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_REVERSE ) != bMirrorOrient;
3037 rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL;
3039 //TODO #i58731# show n-th category
3042 void XclImpChLabelRange::ConvertAxisPosition( ScfPropertySet& rPropSet, bool b3dChart ) const
3044 /* Crossing mode (max-cross flag overrides other crossing settings). Excel
3045 does not move the Y axis in 3D charts, regardless of actual settings.
3046 But: the Y axis has to be moved to "end", if the X axis is mirrored,
3047 to keep it at the left end of the chart. */
3048 bool bMaxCross = ::get_flag( maLabelData.mnFlags, b3dChart ? EXC_CHLABELRANGE_REVERSE : EXC_CHLABELRANGE_MAXCROSS );
3049 cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE;
3050 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos );
3052 // crossing position (depending on axis type text/date)
3053 if( ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS ) )
3055 bool bAutoCross = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
3056 /* Crossing position value depends on base time unit, it specifies the
3057 number of days, months, or years from null date. Note that Excel
3058 2007/2010 write broken BIFF8 files, they always stores the number
3059 of days regardless of the base time unit (and they are reading it
3060 the same way, thus wrongly displaying files written by Excel
3061 97-2003). This filter sticks to the correct behaviour of Excel
3062 97-2003. */
3063 double fCrossingPos = bAutoCross ? 1.0 : lclGetSerialDay( GetRoot(), maDateData.mnCross, maDateData.mnBaseUnit );
3064 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
3066 else
3068 double fCrossingPos = b3dChart ? 1.0 : maLabelData.mnCross;
3069 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
3073 XclImpChValueRange::XclImpChValueRange( const XclImpChRoot& rRoot ) :
3074 XclImpChRoot( rRoot )
3078 void XclImpChValueRange::ReadChValueRange( XclImpStream& rStrm )
3080 maData.mfMin = rStrm.ReadDouble();
3081 maData.mfMax = rStrm.ReadDouble();
3082 maData.mfMajorStep = rStrm.ReadDouble();
3083 maData.mfMinorStep = rStrm.ReadDouble();
3084 maData.mfCross = rStrm.ReadDouble();
3085 maData.mnFlags = rStrm.ReaduInt16();
3088 void XclImpChValueRange::Convert( ScaleData& rScaleData, bool bMirrorOrient ) const
3090 // scaling algorithm
3091 const bool bLogScale = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE );
3092 if( bLogScale )
3093 rScaleData.Scaling = css::chart2::LogarithmicScaling::create( comphelper::getProcessComponentContext() );
3094 else
3095 rScaleData.Scaling = css::chart2::LinearScaling::create( comphelper::getProcessComponentContext() );
3097 // min/max
3098 lclSetExpValueOrClearAny( rScaleData.Minimum, maData.mfMin, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN ) );
3099 lclSetExpValueOrClearAny( rScaleData.Maximum, maData.mfMax, bLogScale, ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX ) );
3101 // increment
3102 bool bAutoMajor = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAJOR );
3103 bool bAutoMinor = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMINOR );
3104 // major increment
3105 IncrementData& rIncrementData = rScaleData.IncrementData;
3106 lclSetValueOrClearAny( rIncrementData.Distance, maData.mfMajorStep, bAutoMajor );
3107 // minor increment
3108 Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements;
3109 rSubIncrementSeq.realloc( 1 );
3110 Any& rIntervalCount = rSubIncrementSeq[ 0 ].IntervalCount;
3111 rIntervalCount.clear();
3112 if( bLogScale )
3114 if( !bAutoMinor )
3115 rIntervalCount <<= sal_Int32( 9 );
3117 else if( !bAutoMajor && !bAutoMinor && (0.0 < maData.mfMinorStep) && (maData.mfMinorStep <= maData.mfMajorStep) )
3119 double fCount = maData.mfMajorStep / maData.mfMinorStep + 0.5;
3120 if( (1.0 <= fCount) && (fCount < 1001.0) )
3121 rIntervalCount <<= static_cast< sal_Int32 >( fCount );
3123 else if( bAutoMinor )
3125 // tdf#114168 If minor unit is not set then set interval to 5, as MS Excel do.
3126 rIntervalCount <<= static_cast< sal_Int32 >( 5 );
3129 // reverse order
3130 bool bReverse = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE ) != bMirrorOrient;
3131 rScaleData.Orientation = bReverse ? cssc2::AxisOrientation_REVERSE : cssc2::AxisOrientation_MATHEMATICAL;
3134 void XclImpChValueRange::ConvertAxisPosition( ScfPropertySet& rPropSet ) const
3136 bool bMaxCross = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_MAXCROSS );
3137 bool bAutoCross = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS );
3138 bool bLogScale = ::get_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE );
3140 // crossing mode (max-cross flag overrides other crossing settings)
3141 cssc::ChartAxisPosition eAxisPos = bMaxCross ? cssc::ChartAxisPosition_END : cssc::ChartAxisPosition_VALUE;
3142 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERPOSITION, eAxisPos );
3144 // crossing position
3145 double fCrossingPos = bAutoCross ? 0.0 : maData.mfCross;
3146 if( bLogScale ) fCrossingPos = pow( 10.0, fCrossingPos );
3147 rPropSet.SetProperty( EXC_CHPROP_CROSSOVERVALUE, fCrossingPos );
3150 namespace {
3152 sal_Int32 lclGetApiTickmarks( sal_uInt8 nXclTickPos )
3154 using namespace ::com::sun::star::chart2::TickmarkStyle;
3155 sal_Int32 nApiTickmarks = css::chart2::TickmarkStyle::NONE;
3156 ::set_flag( nApiTickmarks, INNER, ::get_flag( nXclTickPos, EXC_CHTICK_INSIDE ) );
3157 ::set_flag( nApiTickmarks, OUTER, ::get_flag( nXclTickPos, EXC_CHTICK_OUTSIDE ) );
3158 return nApiTickmarks;
3161 cssc::ChartAxisLabelPosition lclGetApiLabelPosition( sal_Int8 nXclLabelPos )
3163 using namespace ::com::sun::star::chart;
3164 switch( nXclLabelPos )
3166 case EXC_CHTICK_LOW: return ChartAxisLabelPosition_OUTSIDE_START;
3167 case EXC_CHTICK_HIGH: return ChartAxisLabelPosition_OUTSIDE_END;
3168 case EXC_CHTICK_NEXT: return ChartAxisLabelPosition_NEAR_AXIS;
3170 return ChartAxisLabelPosition_NEAR_AXIS;
3173 } // namespace
3175 XclImpChTick::XclImpChTick( const XclImpChRoot& rRoot ) :
3176 XclImpChRoot( rRoot )
3180 void XclImpChTick::ReadChTick( XclImpStream& rStrm )
3182 maData.mnMajor = rStrm.ReaduInt8();
3183 maData.mnMinor = rStrm.ReaduInt8();
3184 maData.mnLabelPos = rStrm.ReaduInt8();
3185 maData.mnBackMode = rStrm.ReaduInt8();
3186 rStrm.Ignore( 16 );
3187 rStrm >> maData.maTextColor;
3188 maData.mnFlags = rStrm.ReaduInt16();
3190 if( GetBiff() == EXC_BIFF8 )
3192 // BIFF8: index into palette used instead of RGB data
3193 maData.maTextColor = GetPalette().GetColor( rStrm.ReaduInt16() );
3194 // rotation
3195 maData.mnRotation = rStrm.ReaduInt16();
3197 else
3199 // BIFF2-BIFF7: get rotation from text orientation
3200 sal_uInt8 nOrient = ::extract_value< sal_uInt8 >( maData.mnFlags, 2, 3 );
3201 maData.mnRotation = XclTools::GetXclRotFromOrient( nOrient );
3205 Color XclImpChTick::GetFontColor() const
3207 return ::get_flag( maData.mnFlags, EXC_CHTICK_AUTOCOLOR ) ? GetFontAutoColor() : maData.maTextColor;
3210 sal_uInt16 XclImpChTick::GetRotation() const
3212 /* n#720443: Ignore auto-rotation if there is a suggested rotation.
3213 * Better fix would be to improve our axis auto rotation algorithm.
3215 if( maData.mnRotation != EXC_ROT_NONE )
3216 return maData.mnRotation;
3217 return ::get_flag( maData.mnFlags, EXC_CHTICK_AUTOROT ) ? EXC_CHART_AUTOROTATION : maData.mnRotation;
3220 void XclImpChTick::Convert( ScfPropertySet& rPropSet ) const
3222 rPropSet.SetProperty( EXC_CHPROP_MAJORTICKS, lclGetApiTickmarks( maData.mnMajor ) );
3223 rPropSet.SetProperty( EXC_CHPROP_MINORTICKS, lclGetApiTickmarks( maData.mnMinor ) );
3224 rPropSet.SetProperty( EXC_CHPROP_LABELPOSITION, lclGetApiLabelPosition( maData.mnLabelPos ) );
3225 rPropSet.SetProperty( EXC_CHPROP_MARKPOSITION, cssc::ChartAxisMarkPosition_AT_AXIS );
3228 XclImpChAxis::XclImpChAxis( const XclImpChRoot& rRoot, sal_uInt16 nAxisType ) :
3229 XclImpChRoot( rRoot ),
3230 mnNumFmtIdx( EXC_FORMAT_NOTFOUND )
3232 maData.mnType = nAxisType;
3235 void XclImpChAxis::ReadHeaderRecord( XclImpStream& rStrm )
3237 maData.mnType = rStrm.ReaduInt16();
3240 void XclImpChAxis::ReadSubRecord( XclImpStream& rStrm )
3242 switch( rStrm.GetRecId() )
3244 case EXC_ID_CHLABELRANGE:
3245 mxLabelRange.reset( new XclImpChLabelRange( GetChRoot() ) );
3246 mxLabelRange->ReadChLabelRange( rStrm );
3247 break;
3248 case EXC_ID_CHDATERANGE:
3249 if( !mxLabelRange )
3250 mxLabelRange.reset( new XclImpChLabelRange( GetChRoot() ) );
3251 mxLabelRange->ReadChDateRange( rStrm );
3252 break;
3253 case EXC_ID_CHVALUERANGE:
3254 mxValueRange.reset( new XclImpChValueRange( GetChRoot() ) );
3255 mxValueRange->ReadChValueRange( rStrm );
3256 break;
3257 case EXC_ID_CHFORMAT:
3258 mnNumFmtIdx = rStrm.ReaduInt16();
3259 break;
3260 case EXC_ID_CHTICK:
3261 mxTick.reset( new XclImpChTick( GetChRoot() ) );
3262 mxTick->ReadChTick( rStrm );
3263 break;
3264 case EXC_ID_CHFONT:
3265 mxFont.reset( new XclImpChFont );
3266 mxFont->ReadChFont( rStrm );
3267 break;
3268 case EXC_ID_CHAXISLINE:
3269 ReadChAxisLine( rStrm );
3270 break;
3274 void XclImpChAxis::Finalize()
3276 // add default scaling, needed e.g. to adjust rotation direction of pie and radar charts
3277 if( !mxLabelRange )
3278 mxLabelRange.reset( new XclImpChLabelRange( GetChRoot() ) );
3279 if( !mxValueRange )
3280 mxValueRange.reset( new XclImpChValueRange( GetChRoot() ) );
3281 // remove invisible grid lines completely
3282 if( mxMajorGrid && !mxMajorGrid->HasLine() )
3283 mxMajorGrid.reset();
3284 if( mxMinorGrid && !mxMinorGrid->HasLine() )
3285 mxMinorGrid.reset();
3286 // default tick settings different in OOChart and Excel
3287 if( !mxTick )
3288 mxTick.reset( new XclImpChTick( GetChRoot() ) );
3289 // #i4140# different default axis line color
3290 if( !mxAxisLine )
3292 XclChLineFormat aLineFmt;
3293 // set "show axis" flag, default if line format record is missing
3294 ::set_flag( aLineFmt.mnFlags, EXC_CHLINEFORMAT_SHOWAXIS );
3295 mxAxisLine.reset( new XclImpChLineFormat( aLineFmt ) );
3297 // add wall/floor frame for 3d charts
3298 if( !mxWallFrame )
3299 CreateWallFrame();
3302 sal_uInt16 XclImpChAxis::GetFontIndex() const
3304 return mxFont ? mxFont->GetFontIndex() : EXC_FONT_NOTFOUND;
3307 Color XclImpChAxis::GetFontColor() const
3309 return mxTick ? mxTick->GetFontColor() : GetFontAutoColor();
3312 sal_uInt16 XclImpChAxis::GetRotation() const
3314 return mxTick ? mxTick->GetRotation() : EXC_CHART_AUTOROTATION;
3317 Reference< XAxis > XclImpChAxis::CreateAxis( const XclImpChTypeGroup& rTypeGroup, const XclImpChAxis* pCrossingAxis ) const
3319 // create the axis object (always)
3320 Reference< XAxis > xAxis( ScfApiHelper::CreateInstance( SERVICE_CHART2_AXIS ), UNO_QUERY );
3321 if( xAxis.is() )
3323 ScfPropertySet aAxisProp( xAxis );
3324 // #i58688# axis enabled
3325 aAxisProp.SetBoolProperty( EXC_CHPROP_SHOW, !mxAxisLine || mxAxisLine->IsShowAxis() );
3327 // axis line properties
3328 if( mxAxisLine )
3329 mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE );
3330 // axis ticks properties
3331 if( mxTick )
3332 mxTick->Convert( aAxisProp );
3334 // axis caption text --------------------------------------------------
3336 // radar charts disable their category labels via chart type, not via axis
3337 bool bHasLabels = (!mxTick || mxTick->HasLabels()) &&
3338 ((GetAxisType() != EXC_CHAXIS_X) || rTypeGroup.HasCategoryLabels());
3339 aAxisProp.SetBoolProperty( EXC_CHPROP_DISPLAYLABELS, bHasLabels );
3340 if( bHasLabels )
3342 // font settings from CHFONT record or from default text
3343 if( mxFont )
3344 ConvertFontBase( GetChRoot(), aAxisProp );
3345 else if( const XclImpChText* pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_AXISLABEL ) )
3346 pDefText->ConvertFont( aAxisProp );
3347 // label text rotation
3348 ConvertRotationBase( aAxisProp, true );
3349 // number format
3350 bool bLinkNumberFmtToSource = true;
3351 if ( mnNumFmtIdx != EXC_FORMAT_NOTFOUND )
3353 sal_uInt32 nScNumFmt = GetNumFmtBuffer().GetScFormat( mnNumFmtIdx );
3354 if( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND )
3356 aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT, static_cast< sal_Int32 >( nScNumFmt ) );
3357 bLinkNumberFmtToSource = false;
3361 aAxisProp.SetProperty( EXC_CHPROP_NUMBERFORMAT_LINKSRC, bLinkNumberFmtToSource );
3364 // axis scaling and increment -----------------------------------------
3366 const XclChExtTypeInfo& rTypeInfo = rTypeGroup.GetTypeInfo();
3367 ScaleData aScaleData = xAxis->getScaleData();
3368 // set axis type
3369 switch( GetAxisType() )
3371 case EXC_CHAXIS_X:
3372 if( rTypeInfo.mbCategoryAxis )
3374 aScaleData.AxisType = cssc2::AxisType::CATEGORY;
3375 aScaleData.Categories = rTypeGroup.CreateCategSequence();
3377 else
3378 aScaleData.AxisType = cssc2::AxisType::REALNUMBER;
3379 break;
3380 case EXC_CHAXIS_Y:
3381 aScaleData.AxisType = rTypeGroup.IsPercent() ?
3382 cssc2::AxisType::PERCENT : cssc2::AxisType::REALNUMBER;
3383 break;
3384 case EXC_CHAXIS_Z:
3385 aScaleData.AxisType = cssc2::AxisType::SERIES;
3386 break;
3388 // axis scaling settings, dependent on axis type
3389 switch( aScaleData.AxisType )
3391 case cssc2::AxisType::CATEGORY:
3392 case cssc2::AxisType::SERIES:
3393 // #i71684# radar charts have reversed rotation direction
3394 if (mxLabelRange)
3395 mxLabelRange->Convert( aAxisProp, aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR );
3396 else
3397 SAL_WARN("sc.filter", "missing LabelRange");
3398 break;
3399 case cssc2::AxisType::REALNUMBER:
3400 case cssc2::AxisType::PERCENT:
3401 // #i85167# pie/donut charts have reversed rotation direction (at Y axis!)
3402 if (mxValueRange)
3403 mxValueRange->Convert( aScaleData, rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE );
3404 else
3405 SAL_WARN("sc.filter", "missing ValueRange");
3406 break;
3407 default:
3408 OSL_FAIL( "XclImpChAxis::CreateAxis - unknown axis type" );
3411 /* Do not set a value to the Origin member anymore (will be done via
3412 new axis properties 'CrossoverPosition' and 'CrossoverValue'). */
3413 aScaleData.Origin.clear();
3415 // write back
3416 xAxis->setScaleData( aScaleData );
3418 // grid ---------------------------------------------------------------
3420 // main grid
3421 ScfPropertySet aGridProp( xAxis->getGridProperties() );
3422 aGridProp.SetBoolProperty( EXC_CHPROP_SHOW, static_cast<bool>(mxMajorGrid) );
3423 if( mxMajorGrid )
3424 mxMajorGrid->Convert( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE );
3425 // sub grid
3426 Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties();
3427 if( aSubGridPropSeq.hasElements() )
3429 ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] );
3430 aSubGridProp.SetBoolProperty( EXC_CHPROP_SHOW, static_cast<bool>(mxMinorGrid) );
3431 if( mxMinorGrid )
3432 mxMinorGrid->Convert( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE );
3435 // position of crossing axis ------------------------------------------
3437 if( pCrossingAxis )
3438 pCrossingAxis->ConvertAxisPosition( aAxisProp, rTypeGroup );
3440 return xAxis;
3443 void XclImpChAxis::ConvertWall( ScfPropertySet& rPropSet ) const
3445 // #i71810# walls and floor in 3D charts use the CHPICFORMAT record for bitmap mode
3446 if( mxWallFrame )
3447 mxWallFrame->Convert( rPropSet, true );
3450 void XclImpChAxis::ConvertAxisPosition( ScfPropertySet& rPropSet, const XclImpChTypeGroup& rTypeGroup ) const
3452 if( ((GetAxisType() == EXC_CHAXIS_X) && rTypeGroup.GetTypeInfo().mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z) )
3454 if (mxLabelRange)
3455 mxLabelRange->ConvertAxisPosition( rPropSet, rTypeGroup.Is3dChart() );
3456 else
3457 SAL_WARN("sc.filter", "missing LabelRange");
3459 else
3461 if (mxValueRange)
3462 mxValueRange->ConvertAxisPosition( rPropSet );
3463 else
3464 SAL_WARN("sc.filter", "missing ValueRange");
3468 void XclImpChAxis::ReadChAxisLine( XclImpStream& rStrm )
3470 XclImpChLineFormatRef* pxLineFmt = nullptr;
3471 bool bWallFrame = false;
3472 switch( rStrm.ReaduInt16() )
3474 case EXC_CHAXISLINE_AXISLINE: pxLineFmt = &mxAxisLine; break;
3475 case EXC_CHAXISLINE_MAJORGRID: pxLineFmt = &mxMajorGrid; break;
3476 case EXC_CHAXISLINE_MINORGRID: pxLineFmt = &mxMinorGrid; break;
3477 case EXC_CHAXISLINE_WALLS: bWallFrame = true; break;
3479 if( bWallFrame )
3480 CreateWallFrame();
3482 bool bLoop = pxLineFmt || bWallFrame;
3483 while( bLoop )
3485 sal_uInt16 nRecId = rStrm.GetNextRecId();
3486 bLoop = ((nRecId == EXC_ID_CHLINEFORMAT) ||
3487 (nRecId == EXC_ID_CHAREAFORMAT) ||
3488 (nRecId == EXC_ID_CHESCHERFORMAT))
3489 && rStrm.StartNextRecord();
3490 if( bLoop )
3492 if( pxLineFmt && (nRecId == EXC_ID_CHLINEFORMAT) )
3494 pxLineFmt->reset( new XclImpChLineFormat );
3495 (*pxLineFmt)->ReadChLineFormat( rStrm );
3497 else if( bWallFrame && mxWallFrame )
3499 mxWallFrame->ReadSubRecord( rStrm );
3505 void XclImpChAxis::CreateWallFrame()
3507 switch( GetAxisType() )
3509 case EXC_CHAXIS_X:
3510 mxWallFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_WALL3D ) );
3511 break;
3512 case EXC_CHAXIS_Y:
3513 mxWallFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_FLOOR3D ) );
3514 break;
3515 default:
3516 mxWallFrame.reset();
3520 XclImpChAxesSet::XclImpChAxesSet( const XclImpChRoot& rRoot, sal_uInt16 nAxesSetId ) :
3521 XclImpChRoot( rRoot )
3523 maData.mnAxesSetId = nAxesSetId;
3526 void XclImpChAxesSet::ReadHeaderRecord( XclImpStream& rStrm )
3528 maData.mnAxesSetId = rStrm.ReaduInt16();
3529 rStrm >> maData.maRect;
3532 void XclImpChAxesSet::ReadSubRecord( XclImpStream& rStrm )
3534 switch( rStrm.GetRecId() )
3536 case EXC_ID_CHFRAMEPOS:
3537 mxFramePos.reset( new XclImpChFramePos );
3538 mxFramePos->ReadChFramePos( rStrm );
3539 break;
3540 case EXC_ID_CHAXIS:
3541 ReadChAxis( rStrm );
3542 break;
3543 case EXC_ID_CHTEXT:
3544 ReadChText( rStrm );
3545 break;
3546 case EXC_ID_CHPLOTFRAME:
3547 ReadChPlotFrame( rStrm );
3548 break;
3549 case EXC_ID_CHTYPEGROUP:
3550 ReadChTypeGroup( rStrm );
3551 break;
3555 void XclImpChAxesSet::Finalize()
3557 if( IsValidAxesSet() )
3559 // finalize chart type groups, erase empty groups without series
3560 XclImpChTypeGroupMap aValidGroups;
3561 for (auto const& typeGroup : maTypeGroups)
3563 XclImpChTypeGroupRef xTypeGroup = typeGroup.second;
3564 xTypeGroup->Finalize();
3565 if( xTypeGroup->IsValidGroup() )
3566 aValidGroups.emplace(typeGroup.first, xTypeGroup);
3568 maTypeGroups.swap( aValidGroups );
3571 // invalid chart type groups are deleted now, check again with IsValidAxesSet()
3572 if( IsValidAxesSet() )
3574 // always create missing axis objects
3575 if( !mxXAxis )
3576 mxXAxis.reset( new XclImpChAxis( GetChRoot(), EXC_CHAXIS_X ) );
3577 if( !mxYAxis )
3578 mxYAxis.reset( new XclImpChAxis( GetChRoot(), EXC_CHAXIS_Y ) );
3579 if( !mxZAxis && GetFirstTypeGroup()->Is3dDeepChart() )
3580 mxZAxis.reset( new XclImpChAxis( GetChRoot(), EXC_CHAXIS_Z ) );
3582 // finalize axes
3583 if( mxXAxis ) mxXAxis->Finalize();
3584 if( mxYAxis ) mxYAxis->Finalize();
3585 if( mxZAxis ) mxZAxis->Finalize();
3587 // finalize axis titles
3588 const XclImpChText* pDefText = GetChartData().GetDefaultText( EXC_CHTEXTTYPE_AXISTITLE );
3589 OUString aAutoTitle(ScResId(STR_AXISTITLE));
3590 lclFinalizeTitle( mxXAxisTitle, pDefText, aAutoTitle );
3591 lclFinalizeTitle( mxYAxisTitle, pDefText, aAutoTitle );
3592 lclFinalizeTitle( mxZAxisTitle, pDefText, aAutoTitle );
3594 // #i47745# missing plot frame -> invisible border and area
3595 if( !mxPlotFrame )
3596 mxPlotFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME ) );
3600 XclImpChTypeGroupRef XclImpChAxesSet::GetTypeGroup( sal_uInt16 nGroupIdx ) const
3602 XclImpChTypeGroupMap::const_iterator itr = maTypeGroups.find(nGroupIdx);
3603 return itr == maTypeGroups.end() ? XclImpChTypeGroupRef() : itr->second;
3606 XclImpChTypeGroupRef XclImpChAxesSet::GetFirstTypeGroup() const
3608 XclImpChTypeGroupRef xTypeGroup;
3609 if( !maTypeGroups.empty() )
3610 xTypeGroup = maTypeGroups.begin()->second;
3611 return xTypeGroup;
3614 XclImpChLegendRef XclImpChAxesSet::GetLegend() const
3616 XclImpChLegendRef xLegend;
3617 for( const auto& rEntry : maTypeGroups )
3619 xLegend = rEntry.second->GetLegend();
3620 if (xLegend)
3621 break;
3623 return xLegend;
3626 OUString XclImpChAxesSet::GetSingleSeriesTitle() const
3628 return (maTypeGroups.size() == 1) ? maTypeGroups.begin()->second->GetSingleSeriesTitle() : OUString();
3631 void XclImpChAxesSet::Convert( Reference< XDiagram > const & xDiagram ) const
3633 if( IsValidAxesSet() && xDiagram.is() )
3635 // diagram background formatting
3636 if( GetAxesSetId() == EXC_CHAXESSET_PRIMARY )
3637 ConvertBackground( xDiagram );
3639 // create the coordinate system, this inserts all chart types and series
3640 Reference< XCoordinateSystem > xCoordSystem = CreateCoordSystem( xDiagram );
3641 if( xCoordSystem.is() )
3643 // insert coordinate system, if not already done
3646 Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY_THROW );
3647 Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
3648 if( !aCoordSystems.hasElements() )
3649 xCoordSystemCont->addCoordinateSystem( xCoordSystem );
3651 catch( Exception& )
3653 OSL_FAIL( "XclImpChAxesSet::Convert - cannot insert coordinate system" );
3656 // create the axes with grids and axis titles and insert them into the diagram
3657 ConvertAxis( mxXAxis, mxXAxisTitle, xCoordSystem, mxYAxis.get() );
3658 ConvertAxis( mxYAxis, mxYAxisTitle, xCoordSystem, mxXAxis.get() );
3659 ConvertAxis( mxZAxis, mxZAxisTitle, xCoordSystem, nullptr );
3664 void XclImpChAxesSet::ConvertTitlePositions() const
3666 if( mxXAxisTitle )
3667 mxXAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_X ) );
3668 if( mxYAxisTitle )
3669 mxYAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_Y ) );
3670 if( mxZAxisTitle )
3671 mxZAxisTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_AXISTITLE, maData.mnAxesSetId, EXC_CHAXIS_Z ) );
3674 void XclImpChAxesSet::ReadChAxis( XclImpStream& rStrm )
3676 XclImpChAxisRef xAxis( new XclImpChAxis( GetChRoot() ) );
3677 xAxis->ReadRecordGroup( rStrm );
3679 switch( xAxis->GetAxisType() )
3681 case EXC_CHAXIS_X: mxXAxis = xAxis; break;
3682 case EXC_CHAXIS_Y: mxYAxis = xAxis; break;
3683 case EXC_CHAXIS_Z: mxZAxis = xAxis; break;
3687 void XclImpChAxesSet::ReadChText( XclImpStream& rStrm )
3689 XclImpChTextRef xText( new XclImpChText( GetChRoot() ) );
3690 xText->ReadRecordGroup( rStrm );
3692 switch( xText->GetLinkTarget() )
3694 case EXC_CHOBJLINK_XAXIS: mxXAxisTitle = xText; break;
3695 case EXC_CHOBJLINK_YAXIS: mxYAxisTitle = xText; break;
3696 case EXC_CHOBJLINK_ZAXIS: mxZAxisTitle = xText; break;
3700 void XclImpChAxesSet::ReadChPlotFrame( XclImpStream& rStrm )
3702 if( (rStrm.GetNextRecId() == EXC_ID_CHFRAME) && rStrm.StartNextRecord() )
3704 mxPlotFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_PLOTFRAME ) );
3705 mxPlotFrame->ReadRecordGroup( rStrm );
3709 void XclImpChAxesSet::ReadChTypeGroup( XclImpStream& rStrm )
3711 XclImpChTypeGroupRef xTypeGroup( new XclImpChTypeGroup( GetChRoot() ) );
3712 xTypeGroup->ReadRecordGroup( rStrm );
3713 sal_uInt16 nGroupIdx = xTypeGroup->GetGroupIdx();
3714 XclImpChTypeGroupMap::iterator itr = maTypeGroups.lower_bound(nGroupIdx);
3715 if (itr != maTypeGroups.end() && !maTypeGroups.key_comp()(nGroupIdx, itr->first))
3716 // Overwrite the existing element.
3717 itr->second = xTypeGroup;
3718 else
3719 maTypeGroups.insert(
3720 itr, XclImpChTypeGroupMap::value_type(nGroupIdx, xTypeGroup));
3723 Reference< XCoordinateSystem > XclImpChAxesSet::CreateCoordSystem( Reference< XDiagram > const & xDiagram ) const
3725 Reference< XCoordinateSystem > xCoordSystem;
3727 /* Try to get existing coordinate system. For now, all series from primary
3728 and secondary axes sets are inserted into one coordinate system. Later,
3729 this should be changed to use one coordinate system for each axes set. */
3730 Reference< XCoordinateSystemContainer > xCoordSystemCont( xDiagram, UNO_QUERY );
3731 if( xCoordSystemCont.is() )
3733 Sequence< Reference< XCoordinateSystem > > aCoordSystems = xCoordSystemCont->getCoordinateSystems();
3734 OSL_ENSURE( aCoordSystems.getLength() <= 1, "XclImpChAxesSet::CreateCoordSystem - too many existing coordinate systems" );
3735 if( aCoordSystems.hasElements() )
3736 xCoordSystem = aCoordSystems[ 0 ];
3739 // create the coordinate system according to the first chart type
3740 if( !xCoordSystem.is() )
3742 XclImpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
3743 if( xTypeGroup )
3745 xCoordSystem = xTypeGroup->CreateCoordSystem();
3746 // convert 3d chart settings
3747 ScfPropertySet aDiaProp( xDiagram );
3748 xTypeGroup->ConvertChart3d( aDiaProp );
3752 /* Create XChartType objects for all chart type groups. Each group will
3753 add its series to the data provider attached to the chart document. */
3754 Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY );
3755 if( xChartTypeCont.is() )
3757 sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
3758 for( const auto& rEntry : maTypeGroups )
3762 Reference< XChartType > xChartType = rEntry.second->CreateChartType( xDiagram, nApiAxesSetIdx );
3763 if( xChartType.is() )
3764 xChartTypeCont->addChartType( xChartType );
3766 catch( Exception& )
3768 OSL_FAIL( "XclImpChAxesSet::CreateCoordSystem - cannot add chart type" );
3773 return xCoordSystem;
3776 void XclImpChAxesSet::ConvertAxis(
3777 XclImpChAxisRef const & xChAxis, XclImpChTextRef const & xChAxisTitle,
3778 Reference< XCoordinateSystem > const & xCoordSystem, const XclImpChAxis* pCrossingAxis ) const
3780 if( xChAxis )
3782 // create and attach the axis object
3783 Reference< XAxis > xAxis = CreateAxis( *xChAxis, pCrossingAxis );
3784 if( xAxis.is() )
3786 // create and attach the axis title
3787 if( xChAxisTitle ) try
3789 Reference< XTitled > xTitled( xAxis, UNO_QUERY_THROW );
3790 Reference< XTitle > xTitle( xChAxisTitle->CreateTitle(), UNO_SET_THROW );
3791 xTitled->setTitleObject( xTitle );
3793 catch( Exception& )
3795 OSL_FAIL( "XclImpChAxesSet::ConvertAxis - cannot set axis title" );
3798 // insert axis into coordinate system
3801 sal_Int32 nApiAxisDim = xChAxis->GetApiAxisDimension();
3802 sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
3803 xCoordSystem->setAxisByDimension( nApiAxisDim, xAxis, nApiAxesSetIdx );
3805 catch( Exception& )
3807 OSL_FAIL( "XclImpChAxesSet::ConvertAxis - cannot set axis" );
3813 Reference< XAxis > XclImpChAxesSet::CreateAxis( const XclImpChAxis& rChAxis, const XclImpChAxis* pCrossingAxis ) const
3815 Reference< XAxis > xAxis;
3816 if( const XclImpChTypeGroup* pTypeGroup = GetFirstTypeGroup().get() )
3817 xAxis = rChAxis.CreateAxis( *pTypeGroup, pCrossingAxis );
3818 return xAxis;
3821 void XclImpChAxesSet::ConvertBackground( Reference< XDiagram > const & xDiagram ) const
3823 XclImpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
3824 if( xTypeGroup && xTypeGroup->Is3dWallChart() )
3826 // wall/floor formatting (3D charts)
3827 if( mxXAxis )
3829 ScfPropertySet aWallProp( xDiagram->getWall() );
3830 mxXAxis->ConvertWall( aWallProp );
3832 if( mxYAxis )
3834 ScfPropertySet aFloorProp( xDiagram->getFloor() );
3835 mxYAxis->ConvertWall( aFloorProp );
3838 else if( mxPlotFrame )
3840 // diagram background formatting
3841 ScfPropertySet aWallProp( xDiagram->getWall() );
3842 mxPlotFrame->Convert( aWallProp );
3846 // The chart object ===========================================================
3848 XclImpChChart::XclImpChChart( const XclImpRoot& rRoot ) :
3849 XclImpChRoot( rRoot, *this )
3851 mxPrimAxesSet.reset( new XclImpChAxesSet( GetChRoot(), EXC_CHAXESSET_PRIMARY ) );
3852 mxSecnAxesSet.reset( new XclImpChAxesSet( GetChRoot(), EXC_CHAXESSET_SECONDARY ) );
3855 XclImpChChart::~XclImpChChart()
3859 void XclImpChChart::ReadHeaderRecord( XclImpStream& rStrm )
3861 // coordinates are stored as 16.16 fixed point
3862 rStrm >> maRect;
3865 void XclImpChChart::ReadSubRecord( XclImpStream& rStrm )
3867 switch( rStrm.GetRecId() )
3869 case EXC_ID_CHFRAME:
3870 mxFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND ) );
3871 mxFrame->ReadRecordGroup( rStrm );
3872 break;
3873 case EXC_ID_CHSERIES:
3874 ReadChSeries( rStrm );
3875 break;
3876 case EXC_ID_CHPROPERTIES:
3877 ReadChProperties( rStrm );
3878 break;
3879 case EXC_ID_CHDEFAULTTEXT:
3880 ReadChDefaultText( rStrm );
3881 break;
3882 case EXC_ID_CHAXESSET:
3883 ReadChAxesSet( rStrm );
3884 break;
3885 case EXC_ID_CHTEXT:
3886 ReadChText( rStrm );
3887 break;
3888 case EXC_ID_CHEND:
3889 Finalize(); // finalize the entire chart object
3890 break;
3894 void XclImpChChart::ReadChDefaultText( XclImpStream& rStrm )
3896 sal_uInt16 nTextId = rStrm.ReaduInt16();
3897 if( (rStrm.GetNextRecId() == EXC_ID_CHTEXT) && rStrm.StartNextRecord() )
3899 unique_ptr<XclImpChText> pText(new XclImpChText(GetChRoot()));
3900 pText->ReadRecordGroup(rStrm);
3901 m_DefTexts.insert(std::make_pair(nTextId, std::move(pText)));
3905 void XclImpChChart::ReadChDataFormat( XclImpStream& rStrm )
3907 XclImpChDataFormatRef xDataFmt( new XclImpChDataFormat( GetChRoot() ) );
3908 xDataFmt->ReadRecordGroup( rStrm );
3909 if( xDataFmt->GetPointPos().mnSeriesIdx <= EXC_CHSERIES_MAXSERIES )
3911 const XclChDataPointPos& rPos = xDataFmt->GetPointPos();
3912 XclImpChDataFormatMap::iterator itr = maDataFmts.lower_bound(rPos);
3913 if (itr == maDataFmts.end() || maDataFmts.key_comp()(rPos, itr->first))
3914 // No element exists for this data point. Insert it.
3915 maDataFmts.insert(
3916 itr, XclImpChDataFormatMap::value_type(rPos, xDataFmt));
3918 /* Do not overwrite existing data format group, Excel always uses the
3919 first data format group occurring in any CHSERIES group. */
3923 void XclImpChChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
3925 if( !mxFrame )
3926 mxFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND ) );
3927 mxFrame->UpdateObjFrame( rLineData, rFillData );
3930 XclImpChTypeGroupRef XclImpChChart::GetTypeGroup( sal_uInt16 nGroupIdx ) const
3932 XclImpChTypeGroupRef xTypeGroup = mxPrimAxesSet->GetTypeGroup( nGroupIdx );
3933 if( !xTypeGroup ) xTypeGroup = mxSecnAxesSet->GetTypeGroup( nGroupIdx );
3934 if( !xTypeGroup ) xTypeGroup = mxPrimAxesSet->GetFirstTypeGroup();
3935 return xTypeGroup;
3938 const XclImpChText* XclImpChChart::GetDefaultText( XclChTextType eTextType ) const
3940 sal_uInt16 nDefTextId = EXC_CHDEFTEXT_GLOBAL;
3941 bool bBiff8 = GetBiff() == EXC_BIFF8;
3942 switch( eTextType )
3944 case EXC_CHTEXTTYPE_TITLE: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break;
3945 case EXC_CHTEXTTYPE_LEGEND: nDefTextId = EXC_CHDEFTEXT_GLOBAL; break;
3946 case EXC_CHTEXTTYPE_AXISTITLE: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
3947 case EXC_CHTEXTTYPE_AXISLABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
3948 case EXC_CHTEXTTYPE_DATALABEL: nDefTextId = bBiff8 ? EXC_CHDEFTEXT_AXESSET : EXC_CHDEFTEXT_GLOBAL; break;
3951 XclImpChTextMap::const_iterator const itr = m_DefTexts.find(nDefTextId);
3952 return itr == m_DefTexts.end() ? nullptr : itr->second.get();
3955 bool XclImpChChart::IsManualPlotArea() const
3957 // there is no real automatic mode in BIFF5 charts
3958 return (GetBiff() <= EXC_BIFF5) || ::get_flag( maProps.mnFlags, EXC_CHPROPS_USEMANPLOTAREA );
3961 void XclImpChChart::Convert( const Reference<XChartDocument>& xChartDoc,
3962 XclImpDffConverter& rDffConv, const OUString& rObjName, const tools::Rectangle& rChartRect ) const
3964 // initialize conversion (locks the model to suppress any internal updates)
3965 InitConversion( xChartDoc, rChartRect );
3967 // chart frame formatting
3968 if( mxFrame )
3970 ScfPropertySet aFrameProp( xChartDoc->getPageBackground() );
3971 mxFrame->Convert( aFrameProp );
3974 // chart title
3975 if( mxTitle ) try
3977 Reference< XTitled > xTitled( xChartDoc, UNO_QUERY_THROW );
3978 Reference< XTitle > xTitle( mxTitle->CreateTitle(), UNO_SET_THROW );
3979 xTitled->setTitleObject( xTitle );
3981 catch( Exception& )
3985 /* Create the diagram object and attach it to the chart document. Currently,
3986 one diagram is used to carry all coordinate systems and data series. */
3987 Reference< XDiagram > xDiagram = CreateDiagram();
3988 xChartDoc->setFirstDiagram( xDiagram );
3990 // coordinate systems and chart types, convert axis settings
3991 mxPrimAxesSet->Convert( xDiagram );
3992 mxSecnAxesSet->Convert( xDiagram );
3994 // legend
3995 if( xDiagram.is() && mxLegend )
3996 xDiagram->setLegend( mxLegend->CreateLegend() );
3998 /* Following all conversions needing the old Chart1 API that involves full
3999 initialization of the chart view. */
4000 Reference< cssc::XChartDocument > xChart1Doc( xChartDoc, UNO_QUERY );
4001 if( xChart1Doc.is() )
4003 Reference< cssc::XDiagram > xDiagram1 = xChart1Doc->getDiagram();
4005 /* Set the 'IncludeHiddenCells' property via the old API as only this
4006 ensures that the data provider and all created sequences get this
4007 flag correctly. */
4008 ScfPropertySet aDiaProp( xDiagram1 );
4009 bool bShowVisCells = ::get_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY );
4010 aDiaProp.SetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS, !bShowVisCells );
4012 // plot area position and size (there is no real automatic mode in BIFF5 charts)
4013 XclImpChFramePosRef xPlotAreaPos = mxPrimAxesSet->GetPlotAreaFramePos();
4014 if( IsManualPlotArea() && xPlotAreaPos ) try
4016 const XclChFramePos& rFramePos = xPlotAreaPos->GetFramePosData();
4017 if( (rFramePos.mnTLMode == EXC_CHFRAMEPOS_PARENT) && (rFramePos.mnBRMode == EXC_CHFRAMEPOS_PARENT) )
4019 Reference< cssc::XDiagramPositioning > xPositioning( xDiagram1, UNO_QUERY_THROW );
4020 css::awt::Rectangle aDiagramRect = CalcHmmFromChartRect( rFramePos.maRect );
4021 // for pie charts, always set inner plot area size to exclude the data labels as Excel does
4022 const XclImpChTypeGroup* pFirstTypeGroup = mxPrimAxesSet->GetFirstTypeGroup().get();
4023 if( pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE) )
4024 xPositioning->setDiagramPositionExcludingAxes( aDiagramRect );
4025 else if( pFirstTypeGroup && pFirstTypeGroup->Is3dChart() )
4026 xPositioning->setDiagramPositionIncludingAxesAndAxisTitles( aDiagramRect );
4027 else
4028 xPositioning->setDiagramPositionIncludingAxes( aDiagramRect );
4031 catch( Exception& )
4035 // positions of all title objects
4036 if( mxTitle )
4037 mxTitle->ConvertTitlePosition( XclChTextKey( EXC_CHTEXTTYPE_TITLE ) );
4038 mxPrimAxesSet->ConvertTitlePositions();
4039 mxSecnAxesSet->ConvertTitlePositions();
4042 // unlock the model
4043 FinishConversion( rDffConv );
4045 // start listening to this chart
4046 ScDocument& rDoc = GetRoot().GetDoc();
4047 if( ScChartListenerCollection* pChartCollection = rDoc.GetChartListenerCollection() )
4049 ::std::unique_ptr< ::std::vector< ScTokenRef > > xRefTokens( new ::std::vector< ScTokenRef > );
4050 for( const auto& rxSeries : maSeries )
4051 rxSeries->FillAllSourceLinks( *xRefTokens );
4052 if( !xRefTokens->empty() )
4054 ::std::unique_ptr< ScChartListener > xListener( new ScChartListener( rObjName, &rDoc, std::move(xRefTokens) ) );
4055 xListener->SetUsed( true );
4056 xListener->StartListeningTo();
4057 pChartCollection->insert( xListener.release() );
4062 void XclImpChChart::ReadChSeries( XclImpStream& rStrm )
4064 sal_uInt16 nNewSeriesIdx = static_cast< sal_uInt16 >( maSeries.size() );
4065 XclImpChSeriesRef xSeries( new XclImpChSeries( GetChRoot(), nNewSeriesIdx ) );
4066 xSeries->ReadRecordGroup( rStrm );
4067 maSeries.push_back( xSeries );
4070 void XclImpChChart::ReadChProperties( XclImpStream& rStrm )
4072 maProps.mnFlags = rStrm.ReaduInt16();
4073 maProps.mnEmptyMode = rStrm.ReaduInt8();
4076 void XclImpChChart::ReadChAxesSet( XclImpStream& rStrm )
4078 XclImpChAxesSetRef xAxesSet( new XclImpChAxesSet( GetChRoot(), EXC_CHAXESSET_NONE ) );
4079 xAxesSet->ReadRecordGroup( rStrm );
4080 switch( xAxesSet->GetAxesSetId() )
4082 case EXC_CHAXESSET_PRIMARY: mxPrimAxesSet = xAxesSet; break;
4083 case EXC_CHAXESSET_SECONDARY: mxSecnAxesSet = xAxesSet; break;
4087 void XclImpChChart::ReadChText( XclImpStream& rStrm )
4089 XclImpChTextRef xText( new XclImpChText( GetChRoot() ) );
4090 xText->ReadRecordGroup( rStrm );
4091 switch( xText->GetLinkTarget() )
4093 case EXC_CHOBJLINK_TITLE:
4094 mxTitle = xText;
4095 break;
4096 case EXC_CHOBJLINK_DATA:
4098 sal_uInt16 nSeriesIdx = xText->GetPointPos().mnSeriesIdx;
4099 if( nSeriesIdx < maSeries.size() )
4100 maSeries[ nSeriesIdx ]->SetDataLabel( xText );
4102 break;
4106 void XclImpChChart::Finalize()
4108 // finalize series (must be done first)
4109 FinalizeSeries();
4110 // #i49218# legend may be attached to primary or secondary axes set
4111 mxLegend = mxPrimAxesSet->GetLegend();
4112 if( !mxLegend )
4113 mxLegend = mxSecnAxesSet->GetLegend();
4114 if( mxLegend )
4115 mxLegend->Finalize();
4116 // axes sets, updates chart type group default formats -> must be called before FinalizeDataFormats()
4117 mxPrimAxesSet->Finalize();
4118 mxSecnAxesSet->Finalize();
4119 // formatting of all series
4120 FinalizeDataFormats();
4121 // #i47745# missing frame -> invisible border and area
4122 if( !mxFrame )
4123 mxFrame.reset( new XclImpChFrame( GetChRoot(), EXC_CHOBJTYPE_BACKGROUND ) );
4124 // chart title
4125 FinalizeTitle();
4128 void XclImpChChart::FinalizeSeries()
4130 for( const XclImpChSeriesRef& xSeries : maSeries )
4132 if( xSeries->HasParentSeries() )
4134 /* Process child series (trend lines and error bars). Data of
4135 child series will be set at the connected parent series. */
4136 if( xSeries->GetParentIdx() < maSeries.size() )
4137 maSeries[ xSeries->GetParentIdx() ]->AddChildSeries( *xSeries );
4139 else
4141 // insert the series into the related chart type group
4142 if( XclImpChTypeGroup* pTypeGroup = GetTypeGroup( xSeries->GetGroupIdx() ).get() )
4143 pTypeGroup->AddSeries( xSeries );
4148 void XclImpChChart::FinalizeDataFormats()
4150 /* #i51639# (part 1): CHDATAFORMAT groups are part of CHSERIES groups.
4151 Each CHDATAFORMAT group specifies the series and data point it is
4152 assigned to. This makes it possible to have a data format that is
4153 related to another series, e.g. a CHDATAFORMAT group for series 2 is
4154 part of a CHSERIES group that describes series 1. Therefore the chart
4155 itself has collected all CHDATAFORMAT groups to be able to store data
4156 format groups for series that have not been imported at that time. This
4157 loop finally assigns these groups to the related series. */
4158 for( const auto& [rPos, rDataFmt] : maDataFmts )
4160 sal_uInt16 nSeriesIdx = rPos.mnSeriesIdx;
4161 if( nSeriesIdx < maSeries.size() )
4162 maSeries[ nSeriesIdx ]->SetDataFormat( rDataFmt );
4165 /* #i51639# (part 2): Finalize data formats of all series. This adds for
4166 example missing CHDATAFORMAT groups for entire series that are needed
4167 for automatic colors of lines and areas. */
4168 for( auto& rxSeries : maSeries )
4169 rxSeries->FinalizeDataFormats();
4172 void XclImpChChart::FinalizeTitle()
4174 // special handling for auto-generated title
4175 OUString aAutoTitle;
4176 if( !mxTitle || (!mxTitle->IsDeleted() && !mxTitle->HasString()) )
4178 // automatic title from first series name (if there are no series on secondary axes set)
4179 if( !mxSecnAxesSet->IsValidAxesSet() )
4180 aAutoTitle = mxPrimAxesSet->GetSingleSeriesTitle();
4181 if( mxTitle || (!aAutoTitle.isEmpty()) )
4183 if( !mxTitle )
4184 mxTitle.reset( new XclImpChText( GetChRoot() ) );
4185 if( aAutoTitle.isEmpty() )
4186 aAutoTitle = ScResId(STR_CHARTTITLE);
4190 // will reset mxTitle, if it does not contain a string and no auto title exists
4191 lclFinalizeTitle( mxTitle, GetDefaultText( EXC_CHTEXTTYPE_TITLE ), aAutoTitle );
4194 Reference< XDiagram > XclImpChChart::CreateDiagram() const
4196 // create a diagram object
4197 Reference< XDiagram > xDiagram( ScfApiHelper::CreateInstance( SERVICE_CHART2_DIAGRAM ), UNO_QUERY );
4199 // convert global chart settings
4200 ScfPropertySet aDiaProp( xDiagram );
4202 // treatment of missing values
4203 using namespace cssc::MissingValueTreatment;
4204 sal_Int32 nMissingValues = LEAVE_GAP;
4205 switch( maProps.mnEmptyMode )
4207 case EXC_CHPROPS_EMPTY_SKIP: nMissingValues = LEAVE_GAP; break;
4208 case EXC_CHPROPS_EMPTY_ZERO: nMissingValues = USE_ZERO; break;
4209 case EXC_CHPROPS_EMPTY_INTERPOLATE: nMissingValues = CONTINUE; break;
4211 aDiaProp.SetProperty( EXC_CHPROP_MISSINGVALUETREATMENT, nMissingValues );
4213 return xDiagram;
4216 XclImpChartDrawing::XclImpChartDrawing( const XclImpRoot& rRoot, bool bOwnTab ) :
4217 XclImpDrawing( rRoot, bOwnTab ), // sheet charts may contain OLE objects
4218 mnScTab( rRoot.GetCurrScTab() ),
4219 mbOwnTab( bOwnTab )
4223 void XclImpChartDrawing::ConvertObjects( XclImpDffConverter& rDffConv,
4224 const Reference< XModel >& rxModel, const tools::Rectangle& rChartRect )
4226 maChartRect = rChartRect; // needed in CalcAnchorRect() callback
4228 SdrModel* pSdrModel = nullptr;
4229 SdrPage* pSdrPage = nullptr;
4230 if( mbOwnTab )
4232 // chart sheet: insert all shapes into the sheet, not into the chart object
4233 pSdrModel = GetDoc().GetDrawLayer();
4234 pSdrPage = GetSdrPage( mnScTab );
4236 else
4238 // embedded chart object: insert all shapes into the chart
4241 Reference< XDrawPageSupplier > xDrawPageSupp( rxModel, UNO_QUERY_THROW );
4242 Reference< XDrawPage > xDrawPage( xDrawPageSupp->getDrawPage(), UNO_SET_THROW );
4243 pSdrPage = ::GetSdrPageFromXDrawPage( xDrawPage );
4244 pSdrModel = pSdrPage ? &pSdrPage->getSdrModelFromSdrPage() : nullptr;
4246 catch( Exception& )
4251 if( pSdrModel && pSdrPage )
4252 ImplConvertObjects( rDffConv, *pSdrModel, *pSdrPage );
4255 tools::Rectangle XclImpChartDrawing::CalcAnchorRect( const XclObjAnchor& rAnchor, bool bDffAnchor ) const
4257 /* In objects with DFF client anchor, the position of the shape is stored
4258 in the cell address components of the client anchor. In old BIFF3-BIFF5
4259 objects, the position is stored in the offset components of the anchor. */
4260 tools::Rectangle aRect(
4261 static_cast< long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnCol : rAnchor.mnLX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ),
4262 static_cast< long >( static_cast< double >( bDffAnchor ? rAnchor.maFirst.mnRow : rAnchor.mnTY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ),
4263 static_cast< long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnCol : rAnchor.mnRX ) / EXC_CHART_TOTALUNITS * maChartRect.GetWidth() + 0.5 ),
4264 static_cast< long >( static_cast< double >( bDffAnchor ? rAnchor.maLast.mnRow : rAnchor.mnBY ) / EXC_CHART_TOTALUNITS * maChartRect.GetHeight() + 0.5 ) );
4265 aRect.Justify();
4266 // move shapes into chart area for sheet charts
4267 if( mbOwnTab )
4268 aRect.Move( maChartRect.Left(), maChartRect.Top() );
4269 return aRect;
4272 void XclImpChartDrawing::OnObjectInserted( const XclImpDrawObjBase& )
4276 XclImpChart::XclImpChart( const XclImpRoot& rRoot, bool bOwnTab ) :
4277 XclImpRoot( rRoot ),
4278 mbOwnTab( bOwnTab ),
4279 mbIsPivotChart( false )
4283 XclImpChart::~XclImpChart()
4287 void XclImpChart::ReadChartSubStream( XclImpStream& rStrm )
4289 XclImpPageSettings& rPageSett = GetPageSettings();
4290 XclImpTabViewSettings& rTabViewSett = GetTabViewSettings();
4292 bool bLoop = true;
4293 while( bLoop && rStrm.StartNextRecord() )
4295 // page settings - only for charts in entire sheet
4296 if( mbOwnTab ) switch( rStrm.GetRecId() )
4298 case EXC_ID_HORPAGEBREAKS:
4299 case EXC_ID_VERPAGEBREAKS: rPageSett.ReadPageBreaks( rStrm ); break;
4300 case EXC_ID_HEADER:
4301 case EXC_ID_FOOTER: rPageSett.ReadHeaderFooter( rStrm ); break;
4302 case EXC_ID_LEFTMARGIN:
4303 case EXC_ID_RIGHTMARGIN:
4304 case EXC_ID_TOPMARGIN:
4305 case EXC_ID_BOTTOMMARGIN: rPageSett.ReadMargin( rStrm ); break;
4306 case EXC_ID_PRINTHEADERS: rPageSett.ReadPrintHeaders( rStrm ); break;
4307 case EXC_ID_PRINTGRIDLINES: rPageSett.ReadPrintGridLines( rStrm ); break;
4308 case EXC_ID_HCENTER:
4309 case EXC_ID_VCENTER: rPageSett.ReadCenter( rStrm ); break;
4310 case EXC_ID_SETUP: rPageSett.ReadSetup( rStrm ); break;
4311 case EXC_ID8_IMGDATA: rPageSett.ReadImgData( rStrm ); break;
4313 case EXC_ID_WINDOW2: rTabViewSett.ReadWindow2( rStrm, true );break;
4314 case EXC_ID_SCL: rTabViewSett.ReadScl( rStrm ); break;
4316 case EXC_ID_SHEETEXT: //0x0862
4318 // FIXME: do not need to pass palette, XclImpTabVieSettings is derived from root
4319 XclImpPalette& rPal = GetPalette();
4320 rTabViewSett.ReadTabBgColor( rStrm, rPal);
4322 break;
4324 case EXC_ID_CODENAME: ReadCodeName( rStrm, false ); break;
4327 // common records
4328 switch( rStrm.GetRecId() )
4330 case EXC_ID_EOF: bLoop = false; break;
4332 // #i31882# ignore embedded chart objects
4333 case EXC_ID2_BOF:
4334 case EXC_ID3_BOF:
4335 case EXC_ID4_BOF:
4336 case EXC_ID5_BOF: XclTools::SkipSubStream( rStrm ); break;
4338 case EXC_ID_CHCHART: ReadChChart( rStrm ); break;
4340 case EXC_ID8_CHPIVOTREF:
4341 GetTracer().TracePivotChartExists();
4342 mbIsPivotChart = true;
4343 break;
4345 // BIFF specific records
4346 default: switch( GetBiff() )
4348 case EXC_BIFF5: switch( rStrm.GetRecId() )
4350 case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break;
4352 break;
4353 case EXC_BIFF8: switch( rStrm.GetRecId() )
4355 case EXC_ID_MSODRAWING: GetChartDrawing().ReadMsoDrawing( rStrm ); break;
4356 // #i61786# weird documents: OBJ without MSODRAWING -> read in BIFF5 format
4357 case EXC_ID_OBJ: GetChartDrawing().ReadObj( rStrm ); break;
4359 break;
4360 default:;
4366 void XclImpChart::UpdateObjFrame( const XclObjLineData& rLineData, const XclObjFillData& rFillData )
4368 if( !mxChartData )
4369 mxChartData.reset( new XclImpChChart( GetRoot() ) );
4370 mxChartData->UpdateObjFrame( rLineData, rFillData );
4373 std::size_t XclImpChart::GetProgressSize() const
4375 return
4376 (mxChartData ? XclImpChChart::GetProgressSize() : 0) +
4377 (mxChartDrawing ? mxChartDrawing->GetProgressSize() : 0);
4380 void XclImpChart::Convert( Reference< XModel > const & xModel, XclImpDffConverter& rDffConv, const OUString& rObjName, const tools::Rectangle& rChartRect ) const
4382 Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY );
4383 if( xChartDoc.is() )
4385 if( mxChartData )
4386 mxChartData->Convert( xChartDoc, rDffConv, rObjName, rChartRect );
4387 if( mxChartDrawing )
4388 mxChartDrawing->ConvertObjects( rDffConv, xModel, rChartRect );
4392 XclImpChartDrawing& XclImpChart::GetChartDrawing()
4394 if( !mxChartDrawing )
4395 mxChartDrawing.reset( new XclImpChartDrawing( GetRoot(), mbOwnTab ) );
4396 return *mxChartDrawing;
4399 void XclImpChart::ReadChChart( XclImpStream& rStrm )
4401 mxChartData.reset( new XclImpChChart( GetRoot() ) );
4402 mxChartData->ReadRecordGroup( rStrm );
4405 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */