1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <xechart.hxx>
21 
22 #include <com/sun/star/i18n/XBreakIterator.hpp>
23 #include <com/sun/star/i18n/ScriptType.hpp>
24 #include <com/sun/star/drawing/FillStyle.hpp>
25 #include <com/sun/star/drawing/XShapes.hpp>
26 #include <com/sun/star/chart/XChartDocument.hpp>
27 #include <com/sun/star/chart/ChartAxisLabelPosition.hpp>
28 #include <com/sun/star/chart/ChartAxisPosition.hpp>
29 #include <com/sun/star/chart/ChartLegendExpansion.hpp>
30 #include <com/sun/star/chart/DataLabelPlacement.hpp>
31 #include <com/sun/star/chart/ErrorBarStyle.hpp>
32 #include <com/sun/star/chart/MissingValueTreatment.hpp>
33 #include <com/sun/star/chart/TimeInterval.hpp>
34 #include <com/sun/star/chart/TimeUnit.hpp>
35 #include <com/sun/star/chart/XAxisSupplier.hpp>
36 #include <com/sun/star/chart/XDiagramPositioning.hpp>
37 #include <com/sun/star/chart2/XChartDocument.hpp>
38 #include <com/sun/star/chart2/XDiagram.hpp>
39 #include <com/sun/star/chart2/XCoordinateSystemContainer.hpp>
40 #include <com/sun/star/chart2/XChartTypeContainer.hpp>
41 #include <com/sun/star/chart2/XDataSeriesContainer.hpp>
42 #include <com/sun/star/chart2/XRegressionCurveContainer.hpp>
43 #include <com/sun/star/chart2/XTitled.hpp>
44 #include <com/sun/star/chart2/XColorScheme.hpp>
45 #include <com/sun/star/chart2/data/XDataSource.hpp>
46 #include <com/sun/star/chart2/AxisType.hpp>
47 #include <com/sun/star/chart2/CurveStyle.hpp>
48 #include <com/sun/star/chart2/DataPointGeometry3D.hpp>
49 #include <com/sun/star/chart2/DataPointLabel.hpp>
50 #include <com/sun/star/chart2/LegendPosition.hpp>
51 #include <com/sun/star/chart2/RelativePosition.hpp>
52 #include <com/sun/star/chart2/RelativeSize.hpp>
53 #include <com/sun/star/chart2/StackingDirection.hpp>
54 #include <com/sun/star/chart2/TickmarkStyle.hpp>
55 
56 #include <tools/gen.hxx>
57 #include <vcl/outdev.hxx>
58 #include <filter/msfilter/escherex.hxx>
59 
60 #include <document.hxx>
61 #include <compiler.hxx>
62 #include <tokenarray.hxx>
63 #include <xeescher.hxx>
64 #include <xeformula.hxx>
65 #include <xehelper.hxx>
66 #include <xepage.hxx>
67 #include <xestyle.hxx>
68 #include <xltools.hxx>
69 
70 #include <memory>
71 
72 using ::com::sun::star::uno::Any;
73 using ::com::sun::star::uno::Reference;
74 using ::com::sun::star::uno::Sequence;
75 using ::com::sun::star::uno::UNO_QUERY;
76 using ::com::sun::star::uno::UNO_QUERY_THROW;
77 using ::com::sun::star::uno::UNO_SET_THROW;
78 using ::com::sun::star::uno::Exception;
79 using ::com::sun::star::beans::XPropertySet;
80 using ::com::sun::star::i18n::XBreakIterator;
81 using ::com::sun::star::frame::XModel;
82 using ::com::sun::star::drawing::XShape;
83 using ::com::sun::star::drawing::XShapes;
84 
85 using ::com::sun::star::chart2::IncrementData;
86 using ::com::sun::star::chart2::RelativePosition;
87 using ::com::sun::star::chart2::RelativeSize;
88 using ::com::sun::star::chart2::ScaleData;
89 using ::com::sun::star::chart2::SubIncrement;
90 using ::com::sun::star::chart2::XAxis;
91 using ::com::sun::star::chart2::XChartDocument;
92 using ::com::sun::star::chart2::XChartTypeContainer;
93 using ::com::sun::star::chart2::XColorScheme;
94 using ::com::sun::star::chart2::XCoordinateSystem;
95 using ::com::sun::star::chart2::XCoordinateSystemContainer;
96 using ::com::sun::star::chart2::XChartType;
97 using ::com::sun::star::chart2::XDataSeries;
98 using ::com::sun::star::chart2::XDataSeriesContainer;
99 using ::com::sun::star::chart2::XDiagram;
100 using ::com::sun::star::chart2::XFormattedString;
101 using ::com::sun::star::chart2::XLegend;
102 using ::com::sun::star::chart2::XRegressionCurve;
103 using ::com::sun::star::chart2::XRegressionCurveContainer;
104 using ::com::sun::star::chart2::XTitle;
105 using ::com::sun::star::chart2::XTitled;
106 
107 using ::com::sun::star::chart2::data::XDataSequence;
108 using ::com::sun::star::chart2::data::XDataSource;
109 using ::com::sun::star::chart2::data::XLabeledDataSequence;
110 
111 using ::formula::FormulaToken;
112 using ::formula::FormulaTokenArrayPlainIterator;
113 
114 namespace cssc = ::com::sun::star::chart;
115 namespace cssc2 = ::com::sun::star::chart2;
116 
117 // Helpers ====================================================================
118 
119 namespace {
120 
operator <<(XclExpStream & rStrm,const XclChRectangle & rRect)121 XclExpStream& operator<<( XclExpStream& rStrm, const XclChRectangle& rRect )
122 {
123     return rStrm << rRect.mnX << rRect.mnY << rRect.mnWidth << rRect.mnHeight;
124 }
125 
lclSaveRecord(XclExpStream & rStrm,XclExpRecordRef const & xRec)126 void lclSaveRecord( XclExpStream& rStrm, XclExpRecordRef const & xRec )
127 {
128     if( xRec )
129         xRec->Save( rStrm );
130 }
131 
132 /** Saves the passed record (group) together with a leading value record. */
133 template< typename Type >
lclSaveRecord(XclExpStream & rStrm,XclExpRecordRef const & xRec,sal_uInt16 nRecId,Type nValue)134 void lclSaveRecord( XclExpStream& rStrm, XclExpRecordRef const & xRec, sal_uInt16 nRecId, Type nValue )
135 {
136     if( xRec )
137     {
138         XclExpValueRecord< Type >( nRecId, nValue ).Save( rStrm );
139         xRec->Save( rStrm );
140     }
141 }
142 
143 template<typename ValueType, typename KeyType>
lclSaveRecord(XclExpStream & rStrm,ValueType * pRec,sal_uInt16 nRecId,KeyType nValue)144 void lclSaveRecord(XclExpStream& rStrm, ValueType* pRec, sal_uInt16 nRecId, KeyType nValue)
145 {
146     if (pRec)
147     {
148         XclExpValueRecord<KeyType>(nRecId, nValue).Save(rStrm);
149         pRec->Save(rStrm);
150     }
151 }
152 
lclWriteChFrBlockRecord(XclExpStream & rStrm,const XclChFrBlock & rFrBlock,bool bBegin)153 void lclWriteChFrBlockRecord( XclExpStream& rStrm, const XclChFrBlock& rFrBlock, bool bBegin )
154 {
155     sal_uInt16 nRecId = bBegin ? EXC_ID_CHFRBLOCKBEGIN : EXC_ID_CHFRBLOCKEND;
156     rStrm.StartRecord( nRecId, 12 );
157     rStrm << nRecId << EXC_FUTUREREC_EMPTYFLAGS << rFrBlock.mnType << rFrBlock.mnContext << rFrBlock.mnValue1 << rFrBlock.mnValue2;
158     rStrm.EndRecord();
159 }
160 
161 template< typename Type >
lclIsAutoAnyOrGetValue(Type & rValue,const Any & rAny)162 bool lclIsAutoAnyOrGetValue( Type& rValue, const Any& rAny )
163 {
164     return !rAny.hasValue() || !(rAny >>= rValue);
165 }
166 
lclIsAutoAnyOrGetScaledValue(double & rfValue,const Any & rAny,bool bLogScale)167 bool lclIsAutoAnyOrGetScaledValue( double& rfValue, const Any& rAny, bool bLogScale )
168 {
169     bool bIsAuto = lclIsAutoAnyOrGetValue( rfValue, rAny );
170     if( !bIsAuto && bLogScale )
171         rfValue = log( rfValue ) / log( 10.0 );
172     return bIsAuto;
173 }
174 
lclGetTimeValue(const XclExpRoot & rRoot,double fSerialDate,sal_uInt16 nTimeUnit)175 sal_uInt16 lclGetTimeValue( const XclExpRoot& rRoot, double fSerialDate, sal_uInt16 nTimeUnit )
176 {
177     DateTime aDateTime = rRoot.GetDateTimeFromDouble( fSerialDate );
178     switch( nTimeUnit )
179     {
180         case EXC_CHDATERANGE_DAYS:
181             return ::limit_cast< sal_uInt16, double >( fSerialDate, 0, SAL_MAX_UINT16 );
182         case EXC_CHDATERANGE_MONTHS:
183             return ::limit_cast< sal_uInt16, sal_uInt16 >( 12 * (aDateTime.GetYear() - rRoot.GetBaseYear()) + aDateTime.GetMonth() - 1, 0, SAL_MAX_INT16 );
184         case EXC_CHDATERANGE_YEARS:
185             return ::limit_cast< sal_uInt16, sal_uInt16 >( aDateTime.GetYear() - rRoot.GetBaseYear(), 0, SAL_MAX_INT16 );
186         default:
187             OSL_ENSURE( false, "lclGetTimeValue - unexpected time unit" );
188     }
189     return ::limit_cast< sal_uInt16, double >( fSerialDate, 0, SAL_MAX_UINT16 );
190 }
191 
lclConvertTimeValue(const XclExpRoot & rRoot,sal_uInt16 & rnValue,const Any & rAny,sal_uInt16 nTimeUnit)192 bool lclConvertTimeValue( const XclExpRoot& rRoot, sal_uInt16& rnValue, const Any& rAny, sal_uInt16 nTimeUnit )
193 {
194     double fSerialDate = 0;
195     bool bAuto = lclIsAutoAnyOrGetValue( fSerialDate, rAny );
196     if( !bAuto )
197         rnValue = lclGetTimeValue( rRoot, fSerialDate, nTimeUnit );
198     return bAuto;
199 }
200 
lclGetTimeUnit(sal_Int32 nApiTimeUnit)201 sal_uInt16 lclGetTimeUnit( sal_Int32 nApiTimeUnit )
202 {
203     switch( nApiTimeUnit )
204     {
205         case cssc::TimeUnit::DAY:   return EXC_CHDATERANGE_DAYS;
206         case cssc::TimeUnit::MONTH: return EXC_CHDATERANGE_MONTHS;
207         case cssc::TimeUnit::YEAR:  return EXC_CHDATERANGE_YEARS;
208         default:                    OSL_ENSURE( false, "lclGetTimeUnit - unexpected time unit" );
209     }
210     return EXC_CHDATERANGE_DAYS;
211 }
212 
lclConvertTimeInterval(sal_uInt16 & rnValue,sal_uInt16 & rnTimeUnit,const Any & rAny)213 bool lclConvertTimeInterval( sal_uInt16& rnValue, sal_uInt16& rnTimeUnit, const Any& rAny )
214 {
215     cssc::TimeInterval aInterval;
216     bool bAuto = lclIsAutoAnyOrGetValue( aInterval, rAny );
217     if( !bAuto )
218     {
219         rnValue = ::limit_cast< sal_uInt16, sal_Int32 >( aInterval.Number, 1, SAL_MAX_UINT16 );
220         rnTimeUnit = lclGetTimeUnit( aInterval.TimeUnit );
221     }
222     return bAuto;
223 }
224 
225 } // namespace
226 
227 // Common =====================================================================
228 
229 /** Stores global data needed in various classes of the Chart export filter. */
230 struct XclExpChRootData : public XclChRootData
231 {
232     typedef ::std::vector< XclChFrBlock > XclChFrBlockVector;
233 
234     XclExpChChart&      mrChartData;            /// The chart data object.
235     XclChFrBlockVector  maWrittenFrBlocks;      /// Stack of future record levels already written out.
236     XclChFrBlockVector  maUnwrittenFrBlocks;    /// Stack of future record levels not yet written out.
237 
XclExpChRootDataXclExpChRootData238     explicit     XclExpChRootData( XclExpChChart& rChartData ) : mrChartData( rChartData ) {}
239 
240     /** Registers a new future record level. */
241     void                RegisterFutureRecBlock( const XclChFrBlock& rFrBlock );
242     /** Initializes the current future record level (writes all unwritten CHFRBLOCKBEGIN records). */
243     void                InitializeFutureRecBlock( XclExpStream& rStrm );
244     /** Finalizes the current future record level (writes CHFRBLOCKEND record if needed). */
245     void                FinalizeFutureRecBlock( XclExpStream& rStrm );
246 };
247 
RegisterFutureRecBlock(const XclChFrBlock & rFrBlock)248 void XclExpChRootData::RegisterFutureRecBlock( const XclChFrBlock& rFrBlock )
249 {
250     maUnwrittenFrBlocks.push_back( rFrBlock );
251 }
252 
InitializeFutureRecBlock(XclExpStream & rStrm)253 void XclExpChRootData::InitializeFutureRecBlock( XclExpStream& rStrm )
254 {
255     // first call from a future record writes all missing CHFRBLOCKBEGIN records
256     if( maUnwrittenFrBlocks.empty() )
257         return;
258 
259     // write the leading CHFRINFO record
260     if( maWrittenFrBlocks.empty() )
261     {
262         rStrm.StartRecord( EXC_ID_CHFRINFO, 20 );
263         rStrm << EXC_ID_CHFRINFO << EXC_FUTUREREC_EMPTYFLAGS << EXC_CHFRINFO_EXCELXP2003 << EXC_CHFRINFO_EXCELXP2003 << sal_uInt16( 3 );
264         rStrm << sal_uInt16( 0x0850 ) << sal_uInt16( 0x085A ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x086A ) << sal_uInt16( 0x086B );
265         rStrm.EndRecord();
266     }
267     // write all unwritten CHFRBLOCKBEGIN records
268     for( const auto& rUnwrittenFrBlock : maUnwrittenFrBlocks )
269     {
270         OSL_ENSURE( rUnwrittenFrBlock.mnType != EXC_CHFRBLOCK_TYPE_UNKNOWN, "XclExpChRootData::InitializeFutureRecBlock - unknown future record block type" );
271         lclWriteChFrBlockRecord( rStrm, rUnwrittenFrBlock, true );
272     }
273     // move all record infos to vector of written blocks
274     maWrittenFrBlocks.insert( maWrittenFrBlocks.end(), maUnwrittenFrBlocks.begin(), maUnwrittenFrBlocks.end() );
275     maUnwrittenFrBlocks.clear();
276 }
277 
FinalizeFutureRecBlock(XclExpStream & rStrm)278 void XclExpChRootData::FinalizeFutureRecBlock( XclExpStream& rStrm )
279 {
280     OSL_ENSURE( !maUnwrittenFrBlocks.empty() || !maWrittenFrBlocks.empty(), "XclExpChRootData::FinalizeFutureRecBlock - no future record level found" );
281     if( !maUnwrittenFrBlocks.empty() )
282     {
283         // no future record has been written, just forget the topmost level
284         maUnwrittenFrBlocks.pop_back();
285     }
286     else if( !maWrittenFrBlocks.empty() )
287     {
288         // write the CHFRBLOCKEND record for the topmost block and delete it
289         lclWriteChFrBlockRecord( rStrm, maWrittenFrBlocks.back(), false );
290         maWrittenFrBlocks.pop_back();
291     }
292 }
293 
XclExpChRoot(const XclExpRoot & rRoot,XclExpChChart & rChartData)294 XclExpChRoot::XclExpChRoot( const XclExpRoot& rRoot, XclExpChChart& rChartData ) :
295     XclExpRoot( rRoot ),
296     mxChData( std::make_shared<XclExpChRootData>( rChartData ) )
297 {
298 }
299 
~XclExpChRoot()300 XclExpChRoot::~XclExpChRoot()
301 {
302 }
303 
GetChartDocument() const304 Reference< XChartDocument > const & XclExpChRoot::GetChartDocument() const
305 {
306     return mxChData->mxChartDoc;
307 }
308 
GetChartData() const309 XclExpChChart& XclExpChRoot::GetChartData() const
310 {
311     return mxChData->mrChartData;
312 }
313 
GetChartTypeInfo(XclChTypeId eType) const314 const XclChTypeInfo& XclExpChRoot::GetChartTypeInfo( XclChTypeId eType ) const
315 {
316     return mxChData->mxTypeInfoProv->GetTypeInfo( eType );
317 }
318 
GetChartTypeInfo(const OUString & rServiceName) const319 const XclChTypeInfo& XclExpChRoot::GetChartTypeInfo( const OUString& rServiceName ) const
320 {
321     return mxChData->mxTypeInfoProv->GetTypeInfoFromService( rServiceName );
322 }
323 
GetFormatInfo(XclChObjectType eObjType) const324 const XclChFormatInfo& XclExpChRoot::GetFormatInfo( XclChObjectType eObjType ) const
325 {
326     return mxChData->mxFmtInfoProv->GetFormatInfo( eObjType );
327 }
328 
InitConversion(css::uno::Reference<css::chart2::XChartDocument> const & xChartDoc,const tools::Rectangle & rChartRect) const329 void XclExpChRoot::InitConversion( css::uno::Reference< css::chart2::XChartDocument > const & xChartDoc, const tools::Rectangle& rChartRect ) const
330 {
331     mxChData->InitConversion( GetRoot(), xChartDoc, rChartRect );
332 }
333 
FinishConversion() const334 void XclExpChRoot::FinishConversion() const
335 {
336     mxChData->FinishConversion();
337 }
338 
IsSystemColor(const Color & rColor,sal_uInt16 nSysColorIdx) const339 bool XclExpChRoot::IsSystemColor( const Color& rColor, sal_uInt16 nSysColorIdx ) const
340 {
341     XclExpPalette& rPal = GetPalette();
342     return rPal.IsSystemColor( nSysColorIdx ) && (rColor == rPal.GetDefColor( nSysColorIdx ));
343 }
344 
SetSystemColor(Color & rColor,sal_uInt32 & rnColorId,sal_uInt16 nSysColorIdx) const345 void XclExpChRoot::SetSystemColor( Color& rColor, sal_uInt32& rnColorId, sal_uInt16 nSysColorIdx ) const
346 {
347     OSL_ENSURE( GetPalette().IsSystemColor( nSysColorIdx ), "XclExpChRoot::SetSystemColor - invalid color index" );
348     rColor = GetPalette().GetDefColor( nSysColorIdx );
349     rnColorId = XclExpPalette::GetColorIdFromIndex( nSysColorIdx );
350 }
351 
CalcChartXFromHmm(sal_Int32 nPosX) const352 sal_Int32 XclExpChRoot::CalcChartXFromHmm( sal_Int32 nPosX ) const
353 {
354     return ::limit_cast< sal_Int32, double >( (nPosX - mxChData->mnBorderGapX) / mxChData->mfUnitSizeX, 0, EXC_CHART_TOTALUNITS );
355 }
356 
CalcChartYFromHmm(sal_Int32 nPosY) const357 sal_Int32 XclExpChRoot::CalcChartYFromHmm( sal_Int32 nPosY ) const
358 {
359     return ::limit_cast< sal_Int32, double >( (nPosY - mxChData->mnBorderGapY) / mxChData->mfUnitSizeY, 0, EXC_CHART_TOTALUNITS );
360 }
361 
CalcChartRectFromHmm(const css::awt::Rectangle & rRect) const362 XclChRectangle XclExpChRoot::CalcChartRectFromHmm( const css::awt::Rectangle& rRect ) const
363 {
364     XclChRectangle aRect;
365     aRect.mnX = CalcChartXFromHmm( rRect.X );
366     aRect.mnY = CalcChartYFromHmm( rRect.Y );
367     aRect.mnWidth = CalcChartXFromHmm( rRect.Width );
368     aRect.mnHeight = CalcChartYFromHmm( rRect.Height );
369     return aRect;
370 }
371 
ConvertLineFormat(XclChLineFormat & rLineFmt,const ScfPropertySet & rPropSet,XclChPropertyMode ePropMode) const372 void XclExpChRoot::ConvertLineFormat( XclChLineFormat& rLineFmt,
373         const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const
374 {
375     GetChartPropSetHelper().ReadLineProperties(
376         rLineFmt, *mxChData->mxLineDashTable, rPropSet, ePropMode );
377 }
378 
ConvertAreaFormat(XclChAreaFormat & rAreaFmt,const ScfPropertySet & rPropSet,XclChPropertyMode ePropMode) const379 bool XclExpChRoot::ConvertAreaFormat( XclChAreaFormat& rAreaFmt,
380         const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const
381 {
382     return GetChartPropSetHelper().ReadAreaProperties( rAreaFmt, rPropSet, ePropMode );
383 }
384 
ConvertEscherFormat(XclChEscherFormat & rEscherFmt,XclChPicFormat & rPicFmt,const ScfPropertySet & rPropSet,XclChPropertyMode ePropMode) const385 void XclExpChRoot::ConvertEscherFormat(
386         XclChEscherFormat& rEscherFmt, XclChPicFormat& rPicFmt,
387         const ScfPropertySet& rPropSet, XclChPropertyMode ePropMode ) const
388 {
389     GetChartPropSetHelper().ReadEscherProperties( rEscherFmt, rPicFmt,
390         *mxChData->mxGradientTable, *mxChData->mxHatchTable, *mxChData->mxBitmapTable, rPropSet, ePropMode );
391 }
392 
ConvertFont(const ScfPropertySet & rPropSet,sal_Int16 nScript) const393 sal_uInt16 XclExpChRoot::ConvertFont( const ScfPropertySet& rPropSet, sal_Int16 nScript ) const
394 {
395     XclFontData aFontData;
396     GetFontPropSetHelper().ReadFontProperties( aFontData, rPropSet, EXC_FONTPROPSET_CHART, nScript );
397     return GetFontBuffer().Insert( aFontData, EXC_COLOR_CHARTTEXT );
398 }
399 
ConvertPieRotation(const ScfPropertySet & rPropSet)400 sal_uInt16 XclExpChRoot::ConvertPieRotation( const ScfPropertySet& rPropSet )
401 {
402     sal_Int32 nApiRot = 0;
403     rPropSet.GetProperty( nApiRot, EXC_CHPROP_STARTINGANGLE );
404     return static_cast< sal_uInt16 >( (450 - (nApiRot % 360)) % 360 );
405 }
406 
RegisterFutureRecBlock(const XclChFrBlock & rFrBlock)407 void XclExpChRoot::RegisterFutureRecBlock( const XclChFrBlock& rFrBlock )
408 {
409     mxChData->RegisterFutureRecBlock( rFrBlock );
410 }
411 
InitializeFutureRecBlock(XclExpStream & rStrm)412 void XclExpChRoot::InitializeFutureRecBlock( XclExpStream& rStrm )
413 {
414     mxChData->InitializeFutureRecBlock( rStrm );
415 }
416 
FinalizeFutureRecBlock(XclExpStream & rStrm)417 void XclExpChRoot::FinalizeFutureRecBlock( XclExpStream& rStrm )
418 {
419     mxChData->FinalizeFutureRecBlock( rStrm );
420 }
421 
XclExpChGroupBase(const XclExpChRoot & rRoot,sal_uInt16 nFrType,sal_uInt16 nRecId,std::size_t nRecSize)422 XclExpChGroupBase::XclExpChGroupBase( const XclExpChRoot& rRoot,
423         sal_uInt16 nFrType, sal_uInt16 nRecId, std::size_t nRecSize ) :
424     XclExpRecord( nRecId, nRecSize ),
425     XclExpChRoot( rRoot ),
426     maFrBlock( nFrType )
427 {
428 }
429 
~XclExpChGroupBase()430 XclExpChGroupBase::~XclExpChGroupBase()
431 {
432 }
433 
Save(XclExpStream & rStrm)434 void XclExpChGroupBase::Save( XclExpStream& rStrm )
435 {
436     // header record
437     XclExpRecord::Save( rStrm );
438     // group records
439     if( !HasSubRecords() )
440         return;
441 
442     // register the future record context corresponding to this record group
443     RegisterFutureRecBlock( maFrBlock );
444     // CHBEGIN record
445     XclExpEmptyRecord( EXC_ID_CHBEGIN ).Save( rStrm );
446     // embedded records
447     WriteSubRecords( rStrm );
448     // finalize the future records, must be done before the closing CHEND
449     FinalizeFutureRecBlock( rStrm );
450     // CHEND record
451     XclExpEmptyRecord( EXC_ID_CHEND ).Save( rStrm );
452 }
453 
HasSubRecords() const454 bool XclExpChGroupBase::HasSubRecords() const
455 {
456     return true;
457 }
458 
SetFutureRecordContext(sal_uInt16 nFrContext,sal_uInt16 nFrValue1,sal_uInt16 nFrValue2)459 void XclExpChGroupBase::SetFutureRecordContext( sal_uInt16 nFrContext, sal_uInt16 nFrValue1, sal_uInt16 nFrValue2 )
460 {
461     maFrBlock.mnContext = nFrContext;
462     maFrBlock.mnValue1  = nFrValue1;
463     maFrBlock.mnValue2  = nFrValue2;
464 }
465 
XclExpChFutureRecordBase(const XclExpChRoot & rRoot,XclFutureRecType eRecType,sal_uInt16 nRecId,std::size_t nRecSize)466 XclExpChFutureRecordBase::XclExpChFutureRecordBase( const XclExpChRoot& rRoot,
467         XclFutureRecType eRecType, sal_uInt16 nRecId, std::size_t nRecSize ) :
468     XclExpFutureRecord( eRecType, nRecId, nRecSize ),
469     XclExpChRoot( rRoot )
470 {
471 }
472 
Save(XclExpStream & rStrm)473 void XclExpChFutureRecordBase::Save( XclExpStream& rStrm )
474 {
475     InitializeFutureRecBlock( rStrm );
476     XclExpFutureRecord::Save( rStrm );
477 }
478 
479 // Frame formatting ===========================================================
480 
XclExpChFramePos(sal_uInt16 nTLMode)481 XclExpChFramePos::XclExpChFramePos( sal_uInt16 nTLMode ) :
482     XclExpRecord( EXC_ID_CHFRAMEPOS, 20 )
483 {
484     maData.mnTLMode = nTLMode;
485     maData.mnBRMode = EXC_CHFRAMEPOS_PARENT;
486 }
487 
WriteBody(XclExpStream & rStrm)488 void XclExpChFramePos::WriteBody( XclExpStream& rStrm )
489 {
490     rStrm << maData.mnTLMode << maData.mnBRMode << maData.maRect;
491 }
492 
XclExpChLineFormat(const XclExpChRoot & rRoot)493 XclExpChLineFormat::XclExpChLineFormat( const XclExpChRoot& rRoot ) :
494     XclExpRecord( EXC_ID_CHLINEFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 12 : 10 ),
495     mnColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
496 {
497 }
498 
SetDefault(XclChFrameType eDefFrameType)499 void XclExpChLineFormat::SetDefault( XclChFrameType eDefFrameType )
500 {
501     switch( eDefFrameType )
502     {
503         case EXC_CHFRAMETYPE_AUTO:
504             SetAuto( true );
505         break;
506         case EXC_CHFRAMETYPE_INVISIBLE:
507             SetAuto( false );
508             maData.mnPattern = EXC_CHLINEFORMAT_NONE;
509         break;
510         default:
511             OSL_FAIL( "XclExpChLineFormat::SetDefault - unknown frame type" );
512     }
513 }
514 
Convert(const XclExpChRoot & rRoot,const ScfPropertySet & rPropSet,XclChObjectType eObjType)515 void XclExpChLineFormat::Convert( const XclExpChRoot& rRoot,
516         const ScfPropertySet& rPropSet, XclChObjectType eObjType )
517 {
518     const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
519     rRoot.ConvertLineFormat( maData, rPropSet, rFmtInfo.mePropMode );
520     if( HasLine() )
521     {
522         // detect system color, set color identifier (TODO: detect automatic series line)
523         if( (eObjType != EXC_CHOBJTYPE_LINEARSERIES) && rRoot.IsSystemColor( maData.maColor, rFmtInfo.mnAutoLineColorIdx ) )
524         {
525             // store color index from automatic format data
526             mnColorId = XclExpPalette::GetColorIdFromIndex( rFmtInfo.mnAutoLineColorIdx );
527             // try to set automatic mode
528             bool bAuto = (maData.mnPattern == EXC_CHLINEFORMAT_SOLID) && (maData.mnWeight == rFmtInfo.mnAutoLineWeight);
529             ::set_flag( maData.mnFlags, EXC_CHLINEFORMAT_AUTO, bAuto );
530         }
531         else
532         {
533             // user defined color - register in palette
534             mnColorId = rRoot.GetPalette().InsertColor( maData.maColor, EXC_COLOR_CHARTLINE );
535         }
536     }
537     else
538     {
539         // no line - set default system color
540         rRoot.SetSystemColor( maData.maColor, mnColorId, EXC_COLOR_CHWINDOWTEXT );
541     }
542 }
543 
IsDefault(XclChFrameType eDefFrameType) const544 bool XclExpChLineFormat::IsDefault( XclChFrameType eDefFrameType ) const
545 {
546     return
547         ((eDefFrameType == EXC_CHFRAMETYPE_INVISIBLE) && !HasLine()) ||
548         ((eDefFrameType == EXC_CHFRAMETYPE_AUTO) && IsAuto());
549 }
550 
WriteBody(XclExpStream & rStrm)551 void XclExpChLineFormat::WriteBody( XclExpStream& rStrm )
552 {
553     rStrm << maData.maColor << maData.mnPattern << maData.mnWeight << maData.mnFlags;
554     if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
555         rStrm << rStrm.GetRoot().GetPalette().GetColorIndex( mnColorId );
556 }
557 
558 namespace {
559 
560 /** Creates a CHLINEFORMAT record from the passed property set. */
lclCreateLineFormat(const XclExpChRoot & rRoot,const ScfPropertySet & rPropSet,XclChObjectType eObjType)561 XclExpChLineFormatRef lclCreateLineFormat( const XclExpChRoot& rRoot,
562         const ScfPropertySet& rPropSet, XclChObjectType eObjType )
563 {
564     XclExpChLineFormatRef xLineFmt = new XclExpChLineFormat( rRoot );
565     xLineFmt->Convert( rRoot, rPropSet, eObjType );
566     const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
567     if( rFmtInfo.mbDeleteDefFrame && xLineFmt->IsDefault( rFmtInfo.meDefFrameType ) )
568         xLineFmt.clear();
569     return xLineFmt;
570 }
571 
572 } // namespace
573 
XclExpChAreaFormat(const XclExpChRoot & rRoot)574 XclExpChAreaFormat::XclExpChAreaFormat( const XclExpChRoot& rRoot ) :
575     XclExpRecord( EXC_ID_CHAREAFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 16 : 12 ),
576     mnPattColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ),
577     mnBackColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
578 {
579 }
580 
Convert(const XclExpChRoot & rRoot,const ScfPropertySet & rPropSet,XclChObjectType eObjType)581 bool XclExpChAreaFormat::Convert( const XclExpChRoot& rRoot,
582         const ScfPropertySet& rPropSet, XclChObjectType eObjType )
583 {
584     const XclChFormatInfo& rFmtInfo = rRoot.GetFormatInfo( eObjType );
585     bool bComplexFill = rRoot.ConvertAreaFormat( maData, rPropSet, rFmtInfo.mePropMode );
586     if( HasArea() )
587     {
588         bool bSolid = maData.mnPattern == EXC_PATT_SOLID;
589         // detect system color, set color identifier (TODO: detect automatic series area)
590         if( (eObjType != EXC_CHOBJTYPE_FILLEDSERIES) && rRoot.IsSystemColor( maData.maPattColor, rFmtInfo.mnAutoPattColorIdx ) )
591         {
592             // store color index from automatic format data
593             mnPattColorId = XclExpPalette::GetColorIdFromIndex( rFmtInfo.mnAutoPattColorIdx );
594             // set automatic mode
595             ::set_flag( maData.mnFlags, EXC_CHAREAFORMAT_AUTO, bSolid );
596         }
597         else
598         {
599             // user defined color - register color in palette
600             mnPattColorId = rRoot.GetPalette().InsertColor( maData.maPattColor, EXC_COLOR_CHARTAREA );
601         }
602         // background color (default system color for solid fills)
603         if( bSolid )
604             rRoot.SetSystemColor( maData.maBackColor, mnBackColorId, EXC_COLOR_CHWINDOWTEXT );
605         else
606             mnBackColorId = rRoot.GetPalette().InsertColor( maData.maBackColor, EXC_COLOR_CHARTAREA );
607     }
608     else
609     {
610         // no area - set default system colors
611         rRoot.SetSystemColor( maData.maPattColor, mnPattColorId, EXC_COLOR_CHWINDOWBACK );
612         rRoot.SetSystemColor( maData.maBackColor, mnBackColorId, EXC_COLOR_CHWINDOWTEXT );
613     }
614     return bComplexFill;
615 }
616 
SetDefault(XclChFrameType eDefFrameType)617 void XclExpChAreaFormat::SetDefault( XclChFrameType eDefFrameType )
618 {
619     switch( eDefFrameType )
620     {
621         case EXC_CHFRAMETYPE_AUTO:
622             SetAuto( true );
623         break;
624         case EXC_CHFRAMETYPE_INVISIBLE:
625             SetAuto( false );
626             maData.mnPattern = EXC_PATT_NONE;
627         break;
628         default:
629             OSL_FAIL( "XclExpChAreaFormat::SetDefault - unknown frame type" );
630     }
631 }
632 
IsDefault(XclChFrameType eDefFrameType) const633 bool XclExpChAreaFormat::IsDefault( XclChFrameType eDefFrameType ) const
634 {
635     return
636         ((eDefFrameType == EXC_CHFRAMETYPE_INVISIBLE) && !HasArea()) ||
637         ((eDefFrameType == EXC_CHFRAMETYPE_AUTO) && IsAuto());
638 }
639 
WriteBody(XclExpStream & rStrm)640 void XclExpChAreaFormat::WriteBody( XclExpStream& rStrm )
641 {
642     rStrm << maData.maPattColor << maData.maBackColor << maData.mnPattern << maData.mnFlags;
643     if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
644     {
645         const XclExpPalette& rPal = rStrm.GetRoot().GetPalette();
646         rStrm << rPal.GetColorIndex( mnPattColorId ) << rPal.GetColorIndex( mnBackColorId );
647     }
648 }
649 
XclExpChEscherFormat(const XclExpChRoot & rRoot)650 XclExpChEscherFormat::XclExpChEscherFormat( const XclExpChRoot& rRoot ) :
651     XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_UNKNOWN, EXC_ID_CHESCHERFORMAT ),
652     mnColor1Id( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) ),
653     mnColor2Id( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) )
654 {
655     OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
656 }
657 
Convert(const ScfPropertySet & rPropSet,XclChObjectType eObjType)658 void XclExpChEscherFormat::Convert( const ScfPropertySet& rPropSet, XclChObjectType eObjType )
659 {
660     const XclChFormatInfo& rFmtInfo = GetFormatInfo( eObjType );
661     ConvertEscherFormat( maData, maPicFmt, rPropSet, rFmtInfo.mePropMode );
662     // register colors in palette
663     mnColor1Id = RegisterColor( ESCHER_Prop_fillColor );
664     mnColor2Id = RegisterColor( ESCHER_Prop_fillBackColor );
665 }
666 
IsValid() const667 bool XclExpChEscherFormat::IsValid() const
668 {
669     return static_cast< bool >(maData.mxEscherSet);
670 }
671 
Save(XclExpStream & rStrm)672 void XclExpChEscherFormat::Save( XclExpStream& rStrm )
673 {
674     if( maData.mxEscherSet )
675     {
676         // replace RGB colors with palette indexes in the Escher container
677         const XclExpPalette& rPal = GetPalette();
678         maData.mxEscherSet->AddOpt( ESCHER_Prop_fillColor, 0x08000000 | rPal.GetColorIndex( mnColor1Id ) );
679         maData.mxEscherSet->AddOpt( ESCHER_Prop_fillBackColor, 0x08000000 | rPal.GetColorIndex( mnColor2Id ) );
680 
681         // save the record group
682         XclExpChGroupBase::Save( rStrm );
683     }
684 }
685 
HasSubRecords() const686 bool XclExpChEscherFormat::HasSubRecords() const
687 {
688     // no subrecords for gradients
689     return maPicFmt.mnBmpMode != EXC_CHPICFORMAT_NONE;
690 }
691 
WriteSubRecords(XclExpStream & rStrm)692 void XclExpChEscherFormat::WriteSubRecords( XclExpStream& rStrm )
693 {
694     rStrm.StartRecord( EXC_ID_CHPICFORMAT, 14 );
695     rStrm << maPicFmt.mnBmpMode << sal_uInt16( 0 ) << maPicFmt.mnFlags << maPicFmt.mfScale;
696     rStrm.EndRecord();
697 }
698 
RegisterColor(sal_uInt16 nPropId)699 sal_uInt32 XclExpChEscherFormat::RegisterColor( sal_uInt16 nPropId )
700 {
701     sal_uInt32 nBGRValue;
702     if( maData.mxEscherSet && maData.mxEscherSet->GetOpt( nPropId, nBGRValue ) )
703     {
704         // swap red and blue
705         Color aColor( nBGRValue & 0xff, (nBGRValue >> 8) & 0xff, (nBGRValue >> 16) & 0xff );
706         return GetPalette().InsertColor( aColor, EXC_COLOR_CHARTAREA );
707     }
708     return XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK );
709 }
710 
WriteBody(XclExpStream & rStrm)711 void XclExpChEscherFormat::WriteBody( XclExpStream& rStrm )
712 {
713     OSL_ENSURE( maData.mxEscherSet, "XclExpChEscherFormat::WriteBody - missing property container" );
714     // write Escher property container via temporary memory stream
715     SvMemoryStream aMemStrm;
716     maData.mxEscherSet->Commit( aMemStrm );
717     aMemStrm.Flush();
718     aMemStrm.Seek( STREAM_SEEK_TO_BEGIN );
719     rStrm.CopyFromStream( aMemStrm );
720 }
721 
XclExpChFrameBase()722 XclExpChFrameBase::XclExpChFrameBase()
723 {
724 }
725 
~XclExpChFrameBase()726 XclExpChFrameBase::~XclExpChFrameBase()
727 {
728 }
729 
ConvertFrameBase(const XclExpChRoot & rRoot,const ScfPropertySet & rPropSet,XclChObjectType eObjType)730 void XclExpChFrameBase::ConvertFrameBase( const XclExpChRoot& rRoot,
731         const ScfPropertySet& rPropSet, XclChObjectType eObjType )
732 {
733     // line format
734     mxLineFmt = new XclExpChLineFormat( rRoot );
735     mxLineFmt->Convert( rRoot, rPropSet, eObjType );
736     // area format (only for frame objects)
737     if( !rRoot.GetFormatInfo( eObjType ).mbIsFrame )
738         return;
739 
740     mxAreaFmt = new XclExpChAreaFormat( rRoot );
741     bool bComplexFill = mxAreaFmt->Convert( rRoot, rPropSet, eObjType );
742     if( (rRoot.GetBiff() == EXC_BIFF8) && bComplexFill )
743     {
744         mxEscherFmt = new XclExpChEscherFormat( rRoot );
745         mxEscherFmt->Convert( rPropSet, eObjType );
746         if( mxEscherFmt->IsValid() )
747             mxAreaFmt->SetAuto( false );
748         else
749             mxEscherFmt.clear();
750     }
751 }
752 
SetDefaultFrameBase(const XclExpChRoot & rRoot,XclChFrameType eDefFrameType,bool bIsFrame)753 void XclExpChFrameBase::SetDefaultFrameBase( const XclExpChRoot& rRoot,
754         XclChFrameType eDefFrameType, bool bIsFrame )
755 {
756     // line format
757     mxLineFmt = new XclExpChLineFormat( rRoot );
758     mxLineFmt->SetDefault( eDefFrameType );
759     // area format (only for frame objects)
760     if( bIsFrame )
761     {
762         mxAreaFmt = new XclExpChAreaFormat( rRoot );
763         mxAreaFmt->SetDefault( eDefFrameType );
764         mxEscherFmt.clear();
765     }
766 }
767 
IsDefaultFrameBase(XclChFrameType eDefFrameType) const768 bool XclExpChFrameBase::IsDefaultFrameBase( XclChFrameType eDefFrameType ) const
769 {
770     return
771         (!mxLineFmt || mxLineFmt->IsDefault( eDefFrameType )) &&
772         (!mxAreaFmt || mxAreaFmt->IsDefault( eDefFrameType ));
773 }
774 
WriteFrameRecords(XclExpStream & rStrm)775 void XclExpChFrameBase::WriteFrameRecords( XclExpStream& rStrm )
776 {
777     lclSaveRecord( rStrm, mxLineFmt );
778     lclSaveRecord( rStrm, mxAreaFmt );
779     lclSaveRecord( rStrm, mxEscherFmt );
780 }
781 
XclExpChFrame(const XclExpChRoot & rRoot,XclChObjectType eObjType)782 XclExpChFrame::XclExpChFrame( const XclExpChRoot& rRoot, XclChObjectType eObjType ) :
783     XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_FRAME, EXC_ID_CHFRAME, 4 ),
784     meObjType( eObjType )
785 {
786 }
787 
Convert(const ScfPropertySet & rPropSet)788 void XclExpChFrame::Convert( const ScfPropertySet& rPropSet )
789 {
790     ConvertFrameBase( GetChRoot(), rPropSet, meObjType );
791 }
792 
SetAutoFlags(bool bAutoPos,bool bAutoSize)793 void XclExpChFrame::SetAutoFlags( bool bAutoPos, bool bAutoSize )
794 {
795     ::set_flag( maData.mnFlags, EXC_CHFRAME_AUTOPOS, bAutoPos );
796     ::set_flag( maData.mnFlags, EXC_CHFRAME_AUTOSIZE, bAutoSize );
797 }
798 
IsDefault() const799 bool XclExpChFrame::IsDefault() const
800 {
801     return IsDefaultFrameBase( GetFormatInfo( meObjType ).meDefFrameType );
802 }
803 
IsDeleteable() const804 bool XclExpChFrame::IsDeleteable() const
805 {
806     return IsDefault() && GetFormatInfo( meObjType ).mbDeleteDefFrame;
807 }
808 
Save(XclExpStream & rStrm)809 void XclExpChFrame::Save( XclExpStream& rStrm )
810 {
811     switch( meObjType )
812     {
813         // wall/floor frame without CHFRAME header record
814         case EXC_CHOBJTYPE_WALL3D:
815         case EXC_CHOBJTYPE_FLOOR3D:
816             WriteFrameRecords( rStrm );
817         break;
818         default:
819             XclExpChGroupBase::Save( rStrm );
820     }
821 }
822 
WriteSubRecords(XclExpStream & rStrm)823 void XclExpChFrame::WriteSubRecords( XclExpStream& rStrm )
824 {
825     WriteFrameRecords( rStrm );
826 }
827 
WriteBody(XclExpStream & rStrm)828 void XclExpChFrame::WriteBody( XclExpStream& rStrm )
829 {
830     rStrm << maData.mnFormat << maData.mnFlags;
831 }
832 
833 namespace {
834 
835 /** Creates a CHFRAME record from the passed property set. */
lclCreateFrame(const XclExpChRoot & rRoot,const ScfPropertySet & rPropSet,XclChObjectType eObjType)836 XclExpChFrameRef lclCreateFrame( const XclExpChRoot& rRoot,
837         const ScfPropertySet& rPropSet, XclChObjectType eObjType )
838 {
839     XclExpChFrameRef xFrame = new XclExpChFrame( rRoot, eObjType );
840     xFrame->Convert( rPropSet );
841     if( xFrame->IsDeleteable() )
842         xFrame.clear();
843     return xFrame;
844 }
845 
846 } // namespace
847 
848 // Source links ===============================================================
849 
850 namespace {
851 
lclAddDoubleRefData(ScTokenArray & orArray,const FormulaToken & rToken,SCTAB nScTab1,SCCOL nScCol1,SCROW nScRow1,SCTAB nScTab2,SCCOL nScCol2,SCROW nScRow2)852 void lclAddDoubleRefData(
853         ScTokenArray& orArray, const FormulaToken& rToken,
854         SCTAB nScTab1, SCCOL nScCol1, SCROW nScRow1,
855         SCTAB nScTab2, SCCOL nScCol2, SCROW nScRow2 )
856 {
857     ScComplexRefData aComplexRef;
858     aComplexRef.InitRange(ScRange(nScCol1,nScRow1,nScTab1,nScCol2,nScRow2,nScTab2));
859     aComplexRef.Ref1.SetFlag3D( true );
860 
861     if( orArray.GetLen() > 0 )
862         orArray.AddOpCode( ocUnion );
863 
864     OSL_ENSURE( (rToken.GetType() == ::formula::svDoubleRef) || (rToken.GetType() == ::formula::svExternalDoubleRef),
865         "lclAddDoubleRefData - double reference token expected");
866     if( rToken.GetType() == ::formula::svExternalDoubleRef )
867         orArray.AddExternalDoubleReference(
868             rToken.GetIndex(), rToken.GetString(), aComplexRef);
869     else
870         orArray.AddDoubleReference( aComplexRef );
871 }
872 
873 } // namespace
874 
XclExpChSourceLink(const XclExpChRoot & rRoot,sal_uInt8 nDestType)875 XclExpChSourceLink::XclExpChSourceLink( const XclExpChRoot& rRoot, sal_uInt8 nDestType ) :
876     XclExpRecord( EXC_ID_CHSOURCELINK ),
877     XclExpChRoot( rRoot )
878 {
879     maData.mnDestType = nDestType;
880     maData.mnLinkType = EXC_CHSRCLINK_DIRECTLY;
881 }
882 
ConvertDataSequence(Reference<XDataSequence> const & xDataSeq,bool bSplitToColumns,sal_uInt16 nDefCount)883 sal_uInt16 XclExpChSourceLink::ConvertDataSequence( Reference< XDataSequence > const & xDataSeq, bool bSplitToColumns, sal_uInt16 nDefCount )
884 {
885     mxLinkFmla.reset();
886     maData.mnLinkType = EXC_CHSRCLINK_DEFAULT;
887 
888     if( !xDataSeq.is() )
889         return nDefCount;
890 
891     // Compile the range representation string into token array.  Note that the
892     // source range text depends on the current grammar.
893     OUString aRangeRepr = xDataSeq->getSourceRangeRepresentation();
894     ScCompiler aComp( GetDoc(), ScAddress(), GetDoc().GetGrammar() );
895     std::unique_ptr<ScTokenArray> pArray(aComp.CompileString(aRangeRepr));
896     if( !pArray )
897         return nDefCount;
898 
899     ScTokenArray aArray(GetRoot().GetDoc());
900     sal_uInt32 nValueCount = 0;
901     FormulaTokenArrayPlainIterator aIter(*pArray);
902     for( const FormulaToken* pToken = aIter.First(); pToken; pToken = aIter.Next() )
903     {
904         switch( pToken->GetType() )
905         {
906             case ::formula::svSingleRef:
907             case ::formula::svExternalSingleRef:
908                 // for a single ref token, just add it to the new token array as is
909                 if( aArray.GetLen() > 0 )
910                     aArray.AddOpCode( ocUnion );
911                 aArray.AddToken( *pToken );
912                 ++nValueCount;
913             break;
914 
915             case ::formula::svDoubleRef:
916             case ::formula::svExternalDoubleRef:
917             {
918                 // split 3-dimensional ranges into single sheets
919                 const ScComplexRefData& rComplexRef = *pToken->GetDoubleRef();
920                 ScAddress aAbs1 = rComplexRef.Ref1.toAbs(GetRoot().GetDoc(), ScAddress());
921                 ScAddress aAbs2 = rComplexRef.Ref2.toAbs(GetRoot().GetDoc(), ScAddress());
922                 for (SCTAB nScTab = aAbs1.Tab(); nScTab <= aAbs2.Tab(); ++nScTab)
923                 {
924                     // split 2-dimensional ranges into single columns
925                     if (bSplitToColumns && (aAbs1.Col() < aAbs2.Col()) && (aAbs1.Row() < aAbs2.Row()))
926                         for (SCCOL nScCol = aAbs1.Col(); nScCol <= aAbs2.Col(); ++nScCol)
927                             lclAddDoubleRefData(aArray, *pToken, nScTab, nScCol, aAbs1.Row(), nScTab, nScCol, aAbs2.Row());
928                     else
929                         lclAddDoubleRefData(aArray, *pToken, nScTab, aAbs1.Col(), aAbs1.Row(), nScTab, aAbs2.Col(), aAbs2.Row());
930                 }
931                 sal_uInt32 nTabs = static_cast<sal_uInt32>(aAbs2.Tab() - aAbs1.Tab() + 1);
932                 sal_uInt32 nCols = static_cast<sal_uInt32>(aAbs2.Col() - aAbs1.Col() + 1);
933                 sal_uInt32 nRows = static_cast<sal_uInt32>(aAbs2.Row() - aAbs1.Row() + 1);
934                 nValueCount += nCols * nRows * nTabs;
935             }
936             break;
937 
938             default:;
939         }
940     }
941 
942     const ScAddress aBaseCell;
943     mxLinkFmla = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CHART, aArray, &aBaseCell );
944     maData.mnLinkType = EXC_CHSRCLINK_WORKSHEET;
945     return ulimit_cast< sal_uInt16 >( nValueCount, EXC_CHDATAFORMAT_MAXPOINTCOUNT );
946 }
947 
ConvertString(const OUString & aString)948 void XclExpChSourceLink::ConvertString( const OUString& aString )
949 {
950     mxString = XclExpStringHelper::CreateString( GetRoot(), aString, XclStrFlags::ForceUnicode | XclStrFlags::EightBitLength | XclStrFlags::SeparateFormats );
951 }
952 
ConvertStringSequence(const Sequence<Reference<XFormattedString>> & rStringSeq)953 sal_uInt16 XclExpChSourceLink::ConvertStringSequence( const Sequence< Reference< XFormattedString > >& rStringSeq )
954 {
955     mxString.reset();
956     sal_uInt16 nFontIdx = EXC_FONT_APP;
957     if( rStringSeq.hasElements() )
958     {
959         mxString = XclExpStringHelper::CreateString( GetRoot(), OUString(), XclStrFlags::ForceUnicode | XclStrFlags::EightBitLength | XclStrFlags::SeparateFormats );
960         Reference< XBreakIterator > xBreakIt = GetDoc().GetBreakIterator();
961         namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
962 
963         // convert all formatted string entries from the sequence
964         for( const Reference< XFormattedString >& rString : rStringSeq )
965         {
966             if( rString.is() )
967             {
968                 sal_uInt16 nWstrnFontIdx = EXC_FONT_NOTFOUND;
969                 sal_uInt16 nAsianFontIdx = EXC_FONT_NOTFOUND;
970                 sal_uInt16 nCmplxFontIdx = EXC_FONT_NOTFOUND;
971                 OUString aText = rString->getString();
972                 ScfPropertySet aStrProp( rString );
973 
974                 // #i63255# get script type for leading weak characters
975                 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( GetRoot(), aText );
976 
977                 // process all script portions
978                 sal_Int32 nPortionPos = 0;
979                 sal_Int32 nTextLen = aText.getLength();
980                 while( nPortionPos < nTextLen )
981                 {
982                     // get script type and end position of next script portion
983                     sal_Int16 nScript = xBreakIt->getScriptType( aText, nPortionPos );
984                     sal_Int32 nPortionEnd = xBreakIt->endOfScript( aText, nPortionPos, nScript );
985 
986                     // reuse previous script for following weak portions
987                     if( nScript == ApiScriptType::WEAK )
988                         nScript = nLastScript;
989 
990                     // Excel start position of this portion
991                     sal_uInt16 nXclPortionStart = mxString->Len();
992                     // add portion text to Excel string
993                     XclExpStringHelper::AppendString( *mxString, GetRoot(), aText.copy( nPortionPos, nPortionEnd - nPortionPos ) );
994                     if( nXclPortionStart < mxString->Len() )
995                     {
996                         // find font index variable dependent on script type
997                         sal_uInt16& rnFontIdx = (nScript == ApiScriptType::COMPLEX) ? nCmplxFontIdx :
998                             ((nScript == ApiScriptType::ASIAN) ? nAsianFontIdx : nWstrnFontIdx);
999 
1000                         // insert font into buffer (if not yet done)
1001                         if( rnFontIdx == EXC_FONT_NOTFOUND )
1002                             rnFontIdx = ConvertFont( aStrProp, nScript );
1003 
1004                         // insert font index into format run vector
1005                         mxString->AppendFormat( nXclPortionStart, rnFontIdx );
1006                     }
1007 
1008                     // go to next script portion
1009                     nLastScript = nScript;
1010                     nPortionPos = nPortionEnd;
1011                 }
1012             }
1013         }
1014         if( !mxString->IsEmpty() )
1015         {
1016             // get leading font index
1017             const XclFormatRunVec& rFormats = mxString->GetFormats();
1018             OSL_ENSURE( !rFormats.empty() && (rFormats.front().mnChar == 0),
1019                 "XclExpChSourceLink::ConvertStringSequenc - missing leading format" );
1020             // remove leading format run, if entire string is equally formatted
1021             if( rFormats.size() == 1 )
1022                 nFontIdx = mxString->RemoveLeadingFont();
1023             else if( !rFormats.empty() )
1024                 nFontIdx = rFormats.front().mnFontIdx;
1025             // add trailing format run, if string is rich-formatted
1026             if( mxString->IsRich() )
1027                 mxString->AppendTrailingFormat( EXC_FONT_APP );
1028         }
1029     }
1030     return nFontIdx;
1031 }
1032 
ConvertNumFmt(const ScfPropertySet & rPropSet,bool bPercent)1033 void XclExpChSourceLink::ConvertNumFmt( const ScfPropertySet& rPropSet, bool bPercent )
1034 {
1035     sal_Int32 nApiNumFmt = 0;
1036     if( bPercent ? rPropSet.GetProperty( nApiNumFmt, EXC_CHPROP_PERCENTAGENUMFMT ) : rPropSet.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) )
1037     {
1038         ::set_flag( maData.mnFlags, EXC_CHSRCLINK_NUMFMT );
1039         maData.mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) );
1040     }
1041 }
1042 
AppendString(const OUString & rStr)1043 void XclExpChSourceLink::AppendString( const OUString& rStr )
1044 {
1045     if (!mxString)
1046         return;
1047     XclExpStringHelper::AppendString( *mxString, GetRoot(), rStr );
1048 }
1049 
Save(XclExpStream & rStrm)1050 void XclExpChSourceLink::Save( XclExpStream& rStrm )
1051 {
1052     // CHFORMATRUNS record
1053     if( mxString && mxString->IsRich() )
1054     {
1055         std::size_t nRecSize = (1 + mxString->GetFormatsCount()) * ((GetBiff() == EXC_BIFF8) ? 2 : 1);
1056         rStrm.StartRecord( EXC_ID_CHFORMATRUNS, nRecSize );
1057         mxString->WriteFormats( rStrm, true );
1058         rStrm.EndRecord();
1059     }
1060     // CHSOURCELINK record
1061     XclExpRecord::Save( rStrm );
1062     // CHSTRING record
1063     if( mxString && !mxString->IsEmpty() )
1064     {
1065         rStrm.StartRecord( EXC_ID_CHSTRING, 2 + mxString->GetSize() );
1066         rStrm << sal_uInt16( 0 ) << *mxString;
1067         rStrm.EndRecord();
1068     }
1069 }
1070 
WriteBody(XclExpStream & rStrm)1071 void XclExpChSourceLink::WriteBody( XclExpStream& rStrm )
1072 {
1073     rStrm   << maData.mnDestType
1074             << maData.mnLinkType
1075             << maData.mnFlags
1076             << maData.mnNumFmtIdx
1077             << mxLinkFmla;
1078 }
1079 
1080 // Text =======================================================================
1081 
XclExpChFont(sal_uInt16 nFontIdx)1082 XclExpChFont::XclExpChFont( sal_uInt16 nFontIdx ) :
1083     XclExpUInt16Record( EXC_ID_CHFONT, nFontIdx )
1084 {
1085 }
1086 
XclExpChObjectLink(sal_uInt16 nLinkTarget,const XclChDataPointPos & rPointPos)1087 XclExpChObjectLink::XclExpChObjectLink( sal_uInt16 nLinkTarget, const XclChDataPointPos& rPointPos ) :
1088     XclExpRecord( EXC_ID_CHOBJECTLINK, 6 )
1089 {
1090     maData.mnTarget = nLinkTarget;
1091     maData.maPointPos = rPointPos;
1092 }
1093 
WriteBody(XclExpStream & rStrm)1094 void XclExpChObjectLink::WriteBody( XclExpStream& rStrm )
1095 {
1096     rStrm << maData.mnTarget << maData.maPointPos.mnSeriesIdx << maData.maPointPos.mnPointIdx;
1097 }
1098 
XclExpChFrLabelProps(const XclExpChRoot & rRoot)1099 XclExpChFrLabelProps::XclExpChFrLabelProps( const XclExpChRoot& rRoot ) :
1100     XclExpChFutureRecordBase( rRoot, EXC_FUTUREREC_UNUSEDREF, EXC_ID_CHFRLABELPROPS, 4 )
1101 {
1102 }
1103 
Convert(const ScfPropertySet & rPropSet,bool bShowCateg,bool bShowValue,bool bShowPercent,bool bShowBubble)1104 void XclExpChFrLabelProps::Convert( const ScfPropertySet& rPropSet,
1105         bool bShowCateg, bool bShowValue, bool bShowPercent, bool bShowBubble )
1106 {
1107     // label value flags
1108     ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWSERIES,  false );
1109     ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWCATEG,   bShowCateg );
1110     ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWVALUE,   bShowValue );
1111     ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWPERCENT, bShowPercent );
1112     ::set_flag( maData.mnFlags, EXC_CHFRLABELPROPS_SHOWBUBBLE,  bShowBubble );
1113 
1114     // label value separator
1115     maData.maSeparator = rPropSet.GetStringProperty( EXC_CHPROP_LABELSEPARATOR );
1116     if( maData.maSeparator.isEmpty() )
1117         maData.maSeparator = " ";
1118 }
1119 
WriteBody(XclExpStream & rStrm)1120 void XclExpChFrLabelProps::WriteBody( XclExpStream& rStrm )
1121 {
1122     XclExpString aXclSep( maData.maSeparator, XclStrFlags::ForceUnicode | XclStrFlags::SmartFlags );
1123     rStrm << maData.mnFlags << aXclSep;
1124 }
1125 
~XclExpChFontBase()1126 XclExpChFontBase::~XclExpChFontBase()
1127 {
1128 }
1129 
ConvertFontBase(const XclExpChRoot & rRoot,sal_uInt16 nFontIdx)1130 void XclExpChFontBase::ConvertFontBase( const XclExpChRoot& rRoot, sal_uInt16 nFontIdx )
1131 {
1132     if( const XclExpFont* pFont = rRoot.GetFontBuffer().GetFont( nFontIdx ) )
1133     {
1134         XclExpChFontRef xFont = new XclExpChFont( nFontIdx );
1135         SetFont( xFont, pFont->GetFontData().maColor, pFont->GetFontColorId() );
1136     }
1137 }
1138 
ConvertFontBase(const XclExpChRoot & rRoot,const ScfPropertySet & rPropSet)1139 void XclExpChFontBase::ConvertFontBase( const XclExpChRoot& rRoot, const ScfPropertySet& rPropSet )
1140 {
1141     ConvertFontBase( rRoot, rRoot.ConvertFont( rPropSet, rRoot.GetDefApiScript() ) );
1142 }
1143 
ConvertRotationBase(const ScfPropertySet & rPropSet,bool bSupportsStacked)1144 void XclExpChFontBase::ConvertRotationBase(const ScfPropertySet& rPropSet, bool bSupportsStacked )
1145 {
1146     sal_uInt16 nRotation = XclChPropSetHelper::ReadRotationProperties( rPropSet, bSupportsStacked );
1147     SetRotation( nRotation );
1148 }
1149 
XclExpChText(const XclExpChRoot & rRoot)1150 XclExpChText::XclExpChText( const XclExpChRoot& rRoot ) :
1151     XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_TEXT, EXC_ID_CHTEXT, (rRoot.GetBiff() == EXC_BIFF8) ? 32 : 26 ),
1152     mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
1153 {
1154 }
1155 
SetFont(XclExpChFontRef xFont,const Color & rColor,sal_uInt32 nColorId)1156 void XclExpChText::SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId )
1157 {
1158     mxFont = xFont;
1159     maData.maTextColor = rColor;
1160     ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOCOLOR, rColor == COL_AUTO );
1161     mnTextColorId = nColorId;
1162 }
1163 
SetRotation(sal_uInt16 nRotation)1164 void XclExpChText::SetRotation( sal_uInt16 nRotation )
1165 {
1166     maData.mnRotation = nRotation;
1167     ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 8, 3 );
1168 }
1169 
ConvertTitle(Reference<XTitle> const & xTitle,sal_uInt16 nTarget,const OUString * pSubTitle)1170 void XclExpChText::ConvertTitle( Reference< XTitle > const & xTitle, sal_uInt16 nTarget, const OUString* pSubTitle )
1171 {
1172     switch( nTarget )
1173     {
1174         case EXC_CHOBJLINK_TITLE:   SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_TITLE );         break;
1175         case EXC_CHOBJLINK_YAXIS:   SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 1 );  break;
1176         case EXC_CHOBJLINK_XAXIS:   SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE );  break;
1177         case EXC_CHOBJLINK_ZAXIS:   SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_AXISTITLE, 2 );  break;
1178     }
1179 
1180     mxSrcLink.clear();
1181     mxObjLink = new XclExpChObjectLink( nTarget, XclChDataPointPos( 0, 0 ) );
1182 
1183     if( xTitle.is() )
1184     {
1185         // title frame formatting
1186         ScfPropertySet aTitleProp( xTitle );
1187         mxFrame = lclCreateFrame( GetChRoot(), aTitleProp, EXC_CHOBJTYPE_TEXT );
1188 
1189         // string sequence
1190         mxSrcLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
1191         sal_uInt16 nFontIdx = mxSrcLink->ConvertStringSequence( xTitle->getText() );
1192         if (pSubTitle)
1193         {
1194             // append subtitle as the 2nd line of the title.
1195             OUString aSubTitle = "\n" + *pSubTitle;
1196             mxSrcLink->AppendString(aSubTitle);
1197         }
1198 
1199         ConvertFontBase( GetChRoot(), nFontIdx );
1200 
1201         // rotation
1202         ConvertRotationBase( aTitleProp, true );
1203 
1204         // manual text position - only for main title
1205         mxFramePos = new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT );
1206         if( nTarget == EXC_CHOBJLINK_TITLE )
1207         {
1208             Any aRelPos;
1209             if( aTitleProp.GetAnyProperty( aRelPos, EXC_CHPROP_RELATIVEPOSITION ) && aRelPos.has< RelativePosition >() ) try
1210             {
1211                 // calculate absolute position for CHTEXT record
1212                 Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
1213                 Reference< XShape > xTitleShape( xChart1Doc->getTitle(), UNO_SET_THROW );
1214                 css::awt::Point aPos = xTitleShape->getPosition();
1215                 css::awt::Size aSize = xTitleShape->getSize();
1216                 css::awt::Rectangle aRect( aPos.X, aPos.Y, aSize.Width, aSize.Height );
1217                 maData.maRect = CalcChartRectFromHmm( aRect );
1218                 ::insert_value( maData.mnFlags2, EXC_CHTEXT_POS_MOVED, 0, 4 );
1219                 // manual title position implies manual plot area
1220                 GetChartData().SetManualPlotArea();
1221                 // calculate the default title position in chart units
1222                 sal_Int32 nDefPosX = ::std::max< sal_Int32 >( (EXC_CHART_TOTALUNITS - maData.maRect.mnWidth) / 2, 0 );
1223                 sal_Int32 nDefPosY = 85;
1224                 // set the position relative to the standard position
1225                 XclChRectangle& rFrameRect = mxFramePos->GetFramePosData().maRect;
1226                 rFrameRect.mnX = maData.maRect.mnX - nDefPosX;
1227                 rFrameRect.mnY = maData.maRect.mnY - nDefPosY;
1228             }
1229             catch( Exception& )
1230             {
1231             }
1232         }
1233     }
1234     else
1235     {
1236         ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED );
1237     }
1238 }
1239 
ConvertLegend(const ScfPropertySet & rPropSet)1240 void XclExpChText::ConvertLegend( const ScfPropertySet& rPropSet )
1241 {
1242     ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT );
1243     ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOGEN );
1244     ConvertFontBase( GetChRoot(), rPropSet );
1245 }
1246 
ConvertDataLabel(const ScfPropertySet & rPropSet,const XclChTypeInfo & rTypeInfo,const XclChDataPointPos & rPointPos)1247 bool XclExpChText::ConvertDataLabel( const ScfPropertySet& rPropSet,
1248         const XclChTypeInfo& rTypeInfo, const XclChDataPointPos& rPointPos )
1249 {
1250     SetFutureRecordContext( EXC_CHFRBLOCK_TEXT_DATALABEL, rPointPos.mnPointIdx, rPointPos.mnSeriesIdx );
1251 
1252     cssc2::DataPointLabel aPointLabel;
1253     if( !rPropSet.GetProperty( aPointLabel, EXC_CHPROP_LABEL ) )
1254         return false;
1255 
1256     // percentage only allowed in pie and donut charts
1257     bool bIsPie = rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE;
1258     // bubble sizes only allowed in bubble charts
1259     bool bIsBubble = rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES;
1260     OSL_ENSURE( (GetBiff() == EXC_BIFF8) || !bIsBubble, "XclExpChText::ConvertDataLabel - bubble charts only in BIFF8" );
1261 
1262     // raw show flags
1263     bool bShowValue   = !bIsBubble && aPointLabel.ShowNumber;       // Chart2 uses 'ShowNumber' for bubble size
1264     bool bShowPercent = bIsPie && aPointLabel.ShowNumberInPercent;  // percentage only in pie/donut charts
1265     bool bShowCateg   = aPointLabel.ShowCategoryName;
1266     bool bShowBubble  = bIsBubble && aPointLabel.ShowNumber;        // Chart2 uses 'ShowNumber' for bubble size
1267     bool bShowAny     = bShowValue || bShowPercent || bShowCateg || bShowBubble;
1268 
1269     // create the CHFRLABELPROPS record for extended settings in BIFF8
1270     if( bShowAny && (GetBiff() == EXC_BIFF8) )
1271     {
1272         mxLabelProps = new XclExpChFrLabelProps( GetChRoot() );
1273         mxLabelProps->Convert( rPropSet, bShowCateg, bShowValue, bShowPercent, bShowBubble );
1274     }
1275 
1276     // restrict to combinations allowed in CHTEXT
1277     if( bShowPercent ) bShowValue = false;              // percent wins over value
1278     if( bShowValue ) bShowCateg = false;                // value wins over category
1279     if( bShowValue || bShowCateg ) bShowBubble = false; // value or category wins over bubble size
1280 
1281     // set all flags
1282     ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT );
1283     ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE, bShowValue );
1284     ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT, bShowPercent );
1285     ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG, bShowCateg );
1286     ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC, bShowPercent && bShowCateg );
1287     ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWBUBBLE, bShowBubble );
1288     ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWSYMBOL, bShowAny && aPointLabel.ShowLegendSymbol );
1289     ::set_flag( maData.mnFlags, EXC_CHTEXT_DELETED, !bShowAny );
1290 
1291     if( bShowAny )
1292     {
1293         // font settings
1294         ConvertFontBase( GetChRoot(), rPropSet );
1295         ConvertRotationBase( rPropSet, false );
1296         // label placement
1297         sal_Int32 nPlacement = 0;
1298         sal_uInt16 nLabelPos = EXC_CHTEXT_POS_AUTO;
1299         if( rPropSet.GetProperty( nPlacement, EXC_CHPROP_LABELPLACEMENT ) )
1300         {
1301             using namespace cssc::DataLabelPlacement;
1302             if( nPlacement == rTypeInfo.mnDefaultLabelPos )
1303             {
1304                 nLabelPos = EXC_CHTEXT_POS_DEFAULT;
1305             }
1306             else switch( nPlacement )
1307             {
1308                 case AVOID_OVERLAP:     nLabelPos = EXC_CHTEXT_POS_AUTO;    break;
1309                 case CENTER:            nLabelPos = EXC_CHTEXT_POS_CENTER;  break;
1310                 case TOP:               nLabelPos = EXC_CHTEXT_POS_ABOVE;   break;
1311                 case TOP_LEFT:          nLabelPos = EXC_CHTEXT_POS_LEFT;    break;
1312                 case LEFT:              nLabelPos = EXC_CHTEXT_POS_LEFT;    break;
1313                 case BOTTOM_LEFT:       nLabelPos = EXC_CHTEXT_POS_LEFT;    break;
1314                 case BOTTOM:            nLabelPos = EXC_CHTEXT_POS_BELOW;   break;
1315                 case BOTTOM_RIGHT:      nLabelPos = EXC_CHTEXT_POS_RIGHT;   break;
1316                 case RIGHT:             nLabelPos = EXC_CHTEXT_POS_RIGHT;   break;
1317                 case TOP_RIGHT:         nLabelPos = EXC_CHTEXT_POS_RIGHT;   break;
1318                 case INSIDE:            nLabelPos = EXC_CHTEXT_POS_INSIDE;  break;
1319                 case OUTSIDE:           nLabelPos = EXC_CHTEXT_POS_OUTSIDE; break;
1320                 case NEAR_ORIGIN:       nLabelPos = EXC_CHTEXT_POS_AXIS;    break;
1321                 default:                OSL_FAIL( "XclExpChText::ConvertDataLabel - unknown label placement type" );
1322             }
1323         }
1324         ::insert_value( maData.mnFlags2, nLabelPos, 0, 4 );
1325         // source link (contains number format)
1326         mxSrcLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
1327         if( bShowValue || bShowPercent )
1328             // percentage format wins over value format
1329             mxSrcLink->ConvertNumFmt( rPropSet, bShowPercent );
1330         // object link
1331         mxObjLink = new XclExpChObjectLink( EXC_CHOBJLINK_DATA, rPointPos );
1332     }
1333 
1334     /*  Return true to indicate valid label settings:
1335         - for existing labels at entire series
1336         - for any settings at single data point (to be able to delete a point label) */
1337     return bShowAny || (rPointPos.mnPointIdx != EXC_CHDATAFORMAT_ALLPOINTS);
1338 }
1339 
ConvertTrendLineEquation(const ScfPropertySet & rPropSet,const XclChDataPointPos & rPointPos)1340 void XclExpChText::ConvertTrendLineEquation( const ScfPropertySet& rPropSet, const XclChDataPointPos& rPointPos )
1341 {
1342     // required flags
1343     ::set_flag( maData.mnFlags, EXC_CHTEXT_AUTOTEXT );
1344     if( GetBiff() == EXC_BIFF8 )
1345         ::set_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG ); // must set this to make equation visible in Excel
1346     // frame formatting
1347     mxFrame = lclCreateFrame( GetChRoot(), rPropSet, EXC_CHOBJTYPE_TEXT );
1348     // font settings
1349     maData.mnHAlign = EXC_CHTEXT_ALIGN_TOPLEFT;
1350     maData.mnVAlign = EXC_CHTEXT_ALIGN_TOPLEFT;
1351     ConvertFontBase( GetChRoot(), rPropSet );
1352     // source link (contains number format)
1353     mxSrcLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
1354     mxSrcLink->ConvertNumFmt( rPropSet, false );
1355     // object link
1356     mxObjLink = new XclExpChObjectLink( EXC_CHOBJLINK_DATA, rPointPos );
1357 }
1358 
GetAttLabelFlags() const1359 sal_uInt16 XclExpChText::GetAttLabelFlags() const
1360 {
1361     sal_uInt16 nFlags = 0;
1362     ::set_flag( nFlags, EXC_CHATTLABEL_SHOWVALUE,     ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWVALUE ) );
1363     ::set_flag( nFlags, EXC_CHATTLABEL_SHOWPERCENT,   ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWPERCENT ) );
1364     ::set_flag( nFlags, EXC_CHATTLABEL_SHOWCATEGPERC, ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEGPERC ) );
1365     ::set_flag( nFlags, EXC_CHATTLABEL_SHOWCATEG,     ::get_flag( maData.mnFlags, EXC_CHTEXT_SHOWCATEG ) );
1366     return nFlags;
1367 }
1368 
WriteSubRecords(XclExpStream & rStrm)1369 void XclExpChText::WriteSubRecords( XclExpStream& rStrm )
1370 {
1371     // CHFRAMEPOS record
1372     lclSaveRecord( rStrm, mxFramePos );
1373     // CHFONT record
1374     lclSaveRecord( rStrm, mxFont );
1375     // CHSOURCELINK group
1376     lclSaveRecord( rStrm, mxSrcLink );
1377     // CHFRAME group
1378     lclSaveRecord( rStrm, mxFrame );
1379     // CHOBJECTLINK record
1380     lclSaveRecord( rStrm, mxObjLink );
1381     // CHFRLABELPROPS record
1382     lclSaveRecord( rStrm, mxLabelProps );
1383 }
1384 
WriteBody(XclExpStream & rStrm)1385 void XclExpChText::WriteBody( XclExpStream& rStrm )
1386 {
1387     rStrm   << maData.mnHAlign
1388             << maData.mnVAlign
1389             << maData.mnBackMode
1390             << maData.maTextColor
1391             << maData.maRect
1392             << maData.mnFlags;
1393 
1394     if( GetBiff() == EXC_BIFF8 )
1395     {
1396         rStrm   << GetPalette().GetColorIndex( mnTextColorId )
1397                 << maData.mnFlags2
1398                 << maData.mnRotation;
1399     }
1400 }
1401 
1402 namespace {
1403 
1404 /** Creates and returns an Excel text object from the passed title. */
lclCreateTitle(const XclExpChRoot & rRoot,Reference<XTitled> const & xTitled,sal_uInt16 nTarget,const OUString * pSubTitle=nullptr)1405 XclExpChTextRef lclCreateTitle( const XclExpChRoot& rRoot, Reference< XTitled > const & xTitled, sal_uInt16 nTarget,
1406                                 const OUString* pSubTitle = nullptr )
1407 {
1408     Reference< XTitle > xTitle;
1409     if( xTitled.is() )
1410         xTitle = xTitled->getTitleObject();
1411 
1412     XclExpChTextRef xText = new XclExpChText( rRoot );
1413     xText->ConvertTitle( xTitle, nTarget, pSubTitle );
1414     /*  Do not delete the CHTEXT group for the main title. A missing CHTEXT
1415         will be interpreted as auto-generated title showing the series title in
1416         charts that contain exactly one data series. */
1417     if( (nTarget != EXC_CHOBJLINK_TITLE) && !xText->HasString() )
1418         xText.clear();
1419 
1420     return xText;
1421 }
1422 
1423 }
1424 
1425 // Data series ================================================================
1426 
XclExpChMarkerFormat(const XclExpChRoot & rRoot)1427 XclExpChMarkerFormat::XclExpChMarkerFormat( const XclExpChRoot& rRoot ) :
1428     XclExpRecord( EXC_ID_CHMARKERFORMAT, (rRoot.GetBiff() == EXC_BIFF8) ? 20 : 12 ),
1429     mnLineColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) ),
1430     mnFillColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWBACK ) )
1431 {
1432 }
1433 
Convert(const XclExpChRoot & rRoot,const ScfPropertySet & rPropSet,sal_uInt16 nFormatIdx)1434 void XclExpChMarkerFormat::Convert( const XclExpChRoot& rRoot,
1435         const ScfPropertySet& rPropSet, sal_uInt16 nFormatIdx )
1436 {
1437     XclChPropSetHelper::ReadMarkerProperties( maData, rPropSet, nFormatIdx );
1438     /*  Set marker line/fill color to series line color.
1439         TODO: remove this if OOChart supports own colors in markers. */
1440     Color aLineColor;
1441     if( rPropSet.GetColorProperty( aLineColor, EXC_CHPROP_COLOR ) )
1442         maData.maLineColor = maData.maFillColor = aLineColor;
1443     // register colors in palette
1444     RegisterColors( rRoot );
1445 }
1446 
ConvertStockSymbol(const XclExpChRoot & rRoot,const ScfPropertySet & rPropSet,bool bCloseSymbol)1447 void XclExpChMarkerFormat::ConvertStockSymbol( const XclExpChRoot& rRoot,
1448         const ScfPropertySet& rPropSet, bool bCloseSymbol )
1449 {
1450     // clear the automatic flag
1451     ::set_flag( maData.mnFlags, EXC_CHMARKERFORMAT_AUTO, false );
1452     // symbol type and color
1453     if( bCloseSymbol )
1454     {
1455         // set symbol type for the 'close' data series
1456         maData.mnMarkerType = EXC_CHMARKERFORMAT_DOWJ;
1457         maData.mnMarkerSize = EXC_CHMARKERFORMAT_DOUBLESIZE;
1458         // set symbol line/fill color to series line color
1459         Color aLineColor;
1460         if( rPropSet.GetColorProperty( aLineColor, EXC_CHPROP_COLOR ) )
1461         {
1462             maData.maLineColor = maData.maFillColor = aLineColor;
1463             RegisterColors( rRoot );
1464         }
1465     }
1466     else
1467     {
1468         // set invisible symbol
1469         maData.mnMarkerType = EXC_CHMARKERFORMAT_NOSYMBOL;
1470     }
1471 }
1472 
RegisterColors(const XclExpChRoot & rRoot)1473 void XclExpChMarkerFormat::RegisterColors( const XclExpChRoot& rRoot )
1474 {
1475     if( HasMarker() )
1476     {
1477         if( HasLineColor() )
1478             mnLineColorId = rRoot.GetPalette().InsertColor( maData.maLineColor, EXC_COLOR_CHARTLINE );
1479         if( HasFillColor() )
1480             mnFillColorId = rRoot.GetPalette().InsertColor( maData.maFillColor, EXC_COLOR_CHARTAREA );
1481     }
1482 }
1483 
WriteBody(XclExpStream & rStrm)1484 void XclExpChMarkerFormat::WriteBody( XclExpStream& rStrm )
1485 {
1486     rStrm << maData.maLineColor << maData.maFillColor << maData.mnMarkerType << maData.mnFlags;
1487     if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
1488     {
1489         const XclExpPalette& rPal = rStrm.GetRoot().GetPalette();
1490         rStrm << rPal.GetColorIndex( mnLineColorId ) << rPal.GetColorIndex( mnFillColorId ) << maData.mnMarkerSize;
1491     }
1492 }
1493 
XclExpChPieFormat()1494 XclExpChPieFormat::XclExpChPieFormat() :
1495     XclExpUInt16Record( EXC_ID_CHPIEFORMAT, 0 )
1496 {
1497 }
1498 
Convert(const ScfPropertySet & rPropSet)1499 void XclExpChPieFormat::Convert( const ScfPropertySet& rPropSet )
1500 {
1501     double fApiDist(0.0);
1502     if( rPropSet.GetProperty( fApiDist, EXC_CHPROP_OFFSET ) )
1503         SetValue( limit_cast< sal_uInt16 >( fApiDist * 100.0, 0, 100 ) );
1504 }
1505 
XclExpCh3dDataFormat()1506 XclExpCh3dDataFormat::XclExpCh3dDataFormat() :
1507     XclExpRecord( EXC_ID_CH3DDATAFORMAT, 2 )
1508 {
1509 }
1510 
Convert(const ScfPropertySet & rPropSet)1511 void XclExpCh3dDataFormat::Convert( const ScfPropertySet& rPropSet )
1512 {
1513     sal_Int32 nApiType(0);
1514     if( !rPropSet.GetProperty( nApiType, EXC_CHPROP_GEOMETRY3D ) )
1515         return;
1516 
1517     using namespace cssc2::DataPointGeometry3D;
1518     switch( nApiType )
1519     {
1520         case CUBOID:
1521             maData.mnBase = EXC_CH3DDATAFORMAT_RECT;
1522             maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT;
1523         break;
1524         case PYRAMID:
1525             maData.mnBase = EXC_CH3DDATAFORMAT_RECT;
1526             maData.mnTop = EXC_CH3DDATAFORMAT_SHARP;
1527         break;
1528         case CYLINDER:
1529             maData.mnBase = EXC_CH3DDATAFORMAT_CIRC;
1530             maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT;
1531         break;
1532         case CONE:
1533             maData.mnBase = EXC_CH3DDATAFORMAT_CIRC;
1534             maData.mnTop = EXC_CH3DDATAFORMAT_SHARP;
1535         break;
1536         default:
1537             OSL_FAIL( "XclExpCh3dDataFormat::Convert - unknown 3D bar format" );
1538     }
1539 }
1540 
WriteBody(XclExpStream & rStrm)1541 void XclExpCh3dDataFormat::WriteBody( XclExpStream& rStrm )
1542 {
1543     rStrm << maData.mnBase << maData.mnTop;
1544 }
1545 
XclExpChAttachedLabel(sal_uInt16 nFlags)1546 XclExpChAttachedLabel::XclExpChAttachedLabel( sal_uInt16 nFlags ) :
1547     XclExpUInt16Record( EXC_ID_CHATTACHEDLABEL, nFlags )
1548 {
1549 }
1550 
XclExpChDataFormat(const XclExpChRoot & rRoot,const XclChDataPointPos & rPointPos,sal_uInt16 nFormatIdx)1551 XclExpChDataFormat::XclExpChDataFormat( const XclExpChRoot& rRoot,
1552         const XclChDataPointPos& rPointPos, sal_uInt16 nFormatIdx ) :
1553     XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_DATAFORMAT, EXC_ID_CHDATAFORMAT, 8 )
1554 {
1555     maData.maPointPos = rPointPos;
1556     maData.mnFormatIdx = nFormatIdx;
1557 }
1558 
ConvertDataSeries(const ScfPropertySet & rPropSet,const XclChExtTypeInfo & rTypeInfo)1559 void XclExpChDataFormat::ConvertDataSeries( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo )
1560 {
1561     // line and area formatting
1562     ConvertFrameBase( GetChRoot(), rPropSet, rTypeInfo.GetSeriesObjectType() );
1563 
1564     // data point symbols
1565     bool bIsFrame = rTypeInfo.IsSeriesFrameFormat();
1566     if( !bIsFrame )
1567     {
1568         mxMarkerFmt = new XclExpChMarkerFormat( GetChRoot() );
1569         mxMarkerFmt->Convert( GetChRoot(), rPropSet, maData.mnFormatIdx );
1570     }
1571 
1572     // pie segments
1573     if( rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE )
1574     {
1575         mxPieFmt = new XclExpChPieFormat();
1576         mxPieFmt->Convert( rPropSet );
1577     }
1578 
1579     // 3D bars (only allowed for entire series in BIFF8)
1580     if( IsSeriesFormat() && (GetBiff() == EXC_BIFF8) && rTypeInfo.mb3dChart && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR) )
1581     {
1582         mx3dDataFmt = new XclExpCh3dDataFormat();
1583         mx3dDataFmt->Convert( rPropSet );
1584     }
1585 
1586     // spline
1587     if( IsSeriesFormat() && rTypeInfo.mbSpline && !bIsFrame )
1588         mxSeriesFmt = new XclExpUInt16Record( EXC_ID_CHSERIESFORMAT, EXC_CHSERIESFORMAT_SMOOTHED );
1589 
1590     // data point labels
1591     XclExpChTextRef xLabel = new XclExpChText( GetChRoot() );
1592     if( xLabel->ConvertDataLabel( rPropSet, rTypeInfo, maData.maPointPos ) )
1593     {
1594         // CHTEXT groups for data labels are stored in global CHCHART group
1595         GetChartData().SetDataLabel( xLabel );
1596         mxAttLabel = new XclExpChAttachedLabel( xLabel->GetAttLabelFlags() );
1597     }
1598 }
1599 
ConvertStockSeries(const ScfPropertySet & rPropSet,bool bCloseSymbol)1600 void XclExpChDataFormat::ConvertStockSeries( const ScfPropertySet& rPropSet, bool bCloseSymbol )
1601 {
1602     // set line format to invisible
1603     SetDefaultFrameBase( GetChRoot(), EXC_CHFRAMETYPE_INVISIBLE, false );
1604     // set symbols to invisible or to 'close' series symbol
1605     mxMarkerFmt = new XclExpChMarkerFormat( GetChRoot() );
1606     mxMarkerFmt->ConvertStockSymbol( GetChRoot(), rPropSet, bCloseSymbol );
1607 }
1608 
ConvertLine(const ScfPropertySet & rPropSet,XclChObjectType eObjType)1609 void XclExpChDataFormat::ConvertLine( const ScfPropertySet& rPropSet, XclChObjectType eObjType )
1610 {
1611     ConvertFrameBase( GetChRoot(), rPropSet, eObjType );
1612 }
1613 
WriteSubRecords(XclExpStream & rStrm)1614 void XclExpChDataFormat::WriteSubRecords( XclExpStream& rStrm )
1615 {
1616     lclSaveRecord( rStrm, mx3dDataFmt );
1617     WriteFrameRecords( rStrm );
1618     lclSaveRecord( rStrm, mxPieFmt );
1619     lclSaveRecord( rStrm, mxMarkerFmt );
1620     lclSaveRecord( rStrm, mxSeriesFmt );
1621     lclSaveRecord( rStrm, mxAttLabel );
1622 }
1623 
WriteBody(XclExpStream & rStrm)1624 void XclExpChDataFormat::WriteBody( XclExpStream& rStrm )
1625 {
1626     rStrm   << maData.maPointPos.mnPointIdx
1627             << maData.maPointPos.mnSeriesIdx
1628             << maData.mnFormatIdx
1629             << maData.mnFlags;
1630 }
1631 
XclExpChSerTrendLine(const XclExpChRoot & rRoot)1632 XclExpChSerTrendLine::XclExpChSerTrendLine( const XclExpChRoot& rRoot ) :
1633     XclExpRecord( EXC_ID_CHSERTRENDLINE, 28 ),
1634     XclExpChRoot( rRoot )
1635 {
1636 }
1637 
Convert(Reference<XRegressionCurve> const & xRegCurve,sal_uInt16 nSeriesIdx)1638 bool XclExpChSerTrendLine::Convert( Reference< XRegressionCurve > const & xRegCurve, sal_uInt16 nSeriesIdx )
1639 {
1640     if( !xRegCurve.is() )
1641         return false;
1642 
1643     // trend line type
1644     ScfPropertySet aCurveProp( xRegCurve );
1645 
1646     OUString aService = aCurveProp.GetServiceName();
1647     if( aService == "com.sun.star.chart2.LinearRegressionCurve" )
1648     {
1649         maData.mnLineType = EXC_CHSERTREND_POLYNOMIAL;
1650         maData.mnOrder = 1;
1651     }
1652     else if( aService == "com.sun.star.chart2.ExponentialRegressionCurve" )
1653     {
1654         maData.mnLineType = EXC_CHSERTREND_EXPONENTIAL;
1655     }
1656     else if( aService == "com.sun.star.chart2.LogarithmicRegressionCurve" )
1657     {
1658         maData.mnLineType = EXC_CHSERTREND_LOGARITHMIC;
1659     }
1660     else if( aService == "com.sun.star.chart2.PotentialRegressionCurve" )
1661     {
1662         maData.mnLineType = EXC_CHSERTREND_POWER;
1663     }
1664     else if( aService == "com.sun.star.chart2.PolynomialRegressionCurve" )
1665     {
1666         maData.mnLineType = EXC_CHSERTREND_POLYNOMIAL;
1667         sal_Int32 aDegree;
1668         aCurveProp.GetProperty(aDegree, EXC_CHPROP_POLYNOMIAL_DEGREE);
1669         maData.mnOrder = static_cast<sal_uInt8> (aDegree);
1670     }
1671     else if( aService == "com.sun.star.chart2.MovingAverageRegressionCurve" )
1672     {
1673         maData.mnLineType = EXC_CHSERTREND_MOVING_AVG;
1674         sal_Int32 aPeriod;
1675         aCurveProp.GetProperty(aPeriod, EXC_CHPROP_MOVING_AVERAGE_PERIOD);
1676         maData.mnOrder = static_cast<sal_uInt8> (aPeriod);
1677     }
1678     else
1679     {
1680         return false;
1681     }
1682 
1683     aCurveProp.GetProperty(maData.mfForecastFor,  EXC_CHPROP_EXTRAPOLATE_FORWARD);
1684     aCurveProp.GetProperty(maData.mfForecastBack, EXC_CHPROP_EXTRAPOLATE_BACKWARD);
1685     bool bIsForceIntercept = false;
1686     aCurveProp.GetProperty(bIsForceIntercept,  EXC_CHPROP_FORCE_INTERCEPT);
1687     if (bIsForceIntercept)
1688         aCurveProp.GetProperty(maData.mfIntercept, EXC_CHPROP_INTERCEPT_VALUE);
1689 
1690     // line formatting
1691     XclChDataPointPos aPointPos( nSeriesIdx );
1692     mxDataFmt = new XclExpChDataFormat( GetChRoot(), aPointPos, 0 );
1693     mxDataFmt->ConvertLine( aCurveProp, EXC_CHOBJTYPE_TRENDLINE );
1694 
1695     // #i83100# show equation and correlation coefficient
1696     ScfPropertySet aEquationProp( xRegCurve->getEquationProperties() );
1697     maData.mnShowEquation = aEquationProp.GetBoolProperty( EXC_CHPROP_SHOWEQUATION ) ? 1 : 0;
1698     maData.mnShowRSquared = aEquationProp.GetBoolProperty( EXC_CHPROP_SHOWCORRELATION ) ? 1 : 0;
1699 
1700     // #i83100# formatting of the equation text box
1701     if( (maData.mnShowEquation != 0) || (maData.mnShowRSquared != 0) )
1702     {
1703         mxLabel = new XclExpChText( GetChRoot() );
1704         mxLabel->ConvertTrendLineEquation( aEquationProp, aPointPos );
1705     }
1706 
1707     // missing features
1708     // #i5085# manual trend line size
1709     // #i34093# manual crossing point
1710     return true;
1711 }
1712 
WriteBody(XclExpStream & rStrm)1713 void XclExpChSerTrendLine::WriteBody( XclExpStream& rStrm )
1714 {
1715     rStrm   << maData.mnLineType
1716             << maData.mnOrder
1717             << maData.mfIntercept
1718             << maData.mnShowEquation
1719             << maData.mnShowRSquared
1720             << maData.mfForecastFor
1721             << maData.mfForecastBack;
1722 }
1723 
XclExpChSerErrorBar(const XclExpChRoot & rRoot,sal_uInt8 nBarType)1724 XclExpChSerErrorBar::XclExpChSerErrorBar( const XclExpChRoot& rRoot, sal_uInt8 nBarType ) :
1725     XclExpRecord( EXC_ID_CHSERERRORBAR, 14 ),
1726     XclExpChRoot( rRoot )
1727 {
1728     maData.mnBarType = nBarType;
1729 }
1730 
Convert(XclExpChSourceLink & rValueLink,sal_uInt16 & rnValueCount,const ScfPropertySet & rPropSet)1731 bool XclExpChSerErrorBar::Convert( XclExpChSourceLink& rValueLink, sal_uInt16& rnValueCount, const ScfPropertySet& rPropSet )
1732 {
1733     sal_Int32 nBarStyle = 0;
1734     bool bOk = rPropSet.GetProperty( nBarStyle, EXC_CHPROP_ERRORBARSTYLE );
1735     if( bOk )
1736     {
1737         switch( nBarStyle )
1738         {
1739             case cssc::ErrorBarStyle::ABSOLUTE:
1740                 maData.mnSourceType = EXC_CHSERERR_FIXED;
1741                 rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_POSITIVEERROR );
1742             break;
1743             case cssc::ErrorBarStyle::RELATIVE:
1744                 maData.mnSourceType = EXC_CHSERERR_PERCENT;
1745                 rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_POSITIVEERROR );
1746             break;
1747             case cssc::ErrorBarStyle::STANDARD_DEVIATION:
1748                 maData.mnSourceType = EXC_CHSERERR_STDDEV;
1749                 rPropSet.GetProperty( maData.mfValue, EXC_CHPROP_WEIGHT );
1750             break;
1751             case cssc::ErrorBarStyle::STANDARD_ERROR:
1752                 maData.mnSourceType = EXC_CHSERERR_STDERR;
1753             break;
1754             case cssc::ErrorBarStyle::FROM_DATA:
1755             {
1756                 bOk = false;
1757                 maData.mnSourceType = EXC_CHSERERR_CUSTOM;
1758                 Reference< XDataSource > xDataSource( rPropSet.GetApiPropertySet(), UNO_QUERY );
1759                 if( xDataSource.is() )
1760                 {
1761                     // find first sequence with current role
1762                     OUString aRole = XclChartHelper::GetErrorBarValuesRole( maData.mnBarType );
1763                     Reference< XDataSequence > xValueSeq;
1764 
1765                     const Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences();
1766                     for( const Reference< XLabeledDataSequence >& rLabeledSeq : aLabeledSeqVec )
1767                     {
1768                         Reference< XDataSequence > xTmpValueSeq = rLabeledSeq->getValues();
1769                         ScfPropertySet aValueProp( xTmpValueSeq );
1770                         OUString aCurrRole;
1771                         if( aValueProp.GetProperty( aCurrRole, EXC_CHPROP_ROLE ) && (aCurrRole == aRole) )
1772                         {
1773                             xValueSeq = xTmpValueSeq;
1774                             break;
1775                         }
1776                     }
1777                     if( xValueSeq.is() )
1778                     {
1779                         // #i86465# pass value count back to series
1780                         rnValueCount = maData.mnValueCount = rValueLink.ConvertDataSequence( xValueSeq, true );
1781                         bOk = maData.mnValueCount > 0;
1782                     }
1783                 }
1784             }
1785             break;
1786             default:
1787                 bOk = false;
1788         }
1789     }
1790     return bOk;
1791 }
1792 
WriteBody(XclExpStream & rStrm)1793 void XclExpChSerErrorBar::WriteBody( XclExpStream& rStrm )
1794 {
1795     rStrm   << maData.mnBarType
1796             << maData.mnSourceType
1797             << maData.mnLineEnd
1798             << sal_uInt8( 1 )       // must be 1 to make line visible
1799             << maData.mfValue
1800             << maData.mnValueCount;
1801 }
1802 
1803 namespace {
1804 
1805 /** Returns the property set of the specified data point. */
lclGetPointPropSet(Reference<XDataSeries> const & xDataSeries,sal_Int32 nPointIdx)1806 ScfPropertySet lclGetPointPropSet( Reference< XDataSeries > const & xDataSeries, sal_Int32 nPointIdx )
1807 {
1808     ScfPropertySet aPropSet;
1809     try
1810     {
1811         aPropSet.Set( xDataSeries->getDataPointByIndex( nPointIdx ) );
1812     }
1813     catch( Exception& )
1814     {
1815         OSL_FAIL( "lclGetPointPropSet - no data point property set" );
1816     }
1817     return aPropSet;
1818 }
1819 
1820 } // namespace
1821 
XclExpChSeries(const XclExpChRoot & rRoot,sal_uInt16 nSeriesIdx)1822 XclExpChSeries::XclExpChSeries( const XclExpChRoot& rRoot, sal_uInt16 nSeriesIdx ) :
1823     XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_SERIES, EXC_ID_CHSERIES, (rRoot.GetBiff() == EXC_BIFF8) ? 12 : 8 ),
1824     mnGroupIdx( EXC_CHSERGROUP_NONE ),
1825     mnSeriesIdx( nSeriesIdx ),
1826     mnParentIdx( EXC_CHSERIES_INVALID )
1827 {
1828     // CHSOURCELINK records are always required, even if unused
1829     mxTitleLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE );
1830     mxValueLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_VALUES );
1831     mxCategLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_CATEGORY );
1832     if( GetBiff() == EXC_BIFF8 )
1833         mxBubbleLink = new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_BUBBLES );
1834 }
1835 
ConvertDataSeries(Reference<XDiagram> const & xDiagram,Reference<XDataSeries> const & xDataSeries,const XclChExtTypeInfo & rTypeInfo,sal_uInt16 nGroupIdx,sal_uInt16 nFormatIdx)1836 bool XclExpChSeries::ConvertDataSeries(
1837         Reference< XDiagram > const & xDiagram, Reference< XDataSeries > const & xDataSeries,
1838         const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx )
1839 {
1840     bool bOk = false;
1841     Reference< XDataSource > xDataSource( xDataSeries, UNO_QUERY );
1842     if( xDataSource.is() )
1843     {
1844         Reference< XDataSequence > xYValueSeq, xTitleSeq, xXValueSeq, xBubbleSeq;
1845 
1846         // find first sequence with role 'values-y'
1847         const Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences();
1848         for( const Reference< XLabeledDataSequence >& rLabeledSeq : aLabeledSeqVec )
1849         {
1850             Reference< XDataSequence > xTmpValueSeq = rLabeledSeq->getValues();
1851             ScfPropertySet aValueProp( xTmpValueSeq );
1852             OUString aRole;
1853             if( aValueProp.GetProperty( aRole, EXC_CHPROP_ROLE ) )
1854             {
1855                 if( !xYValueSeq.is() && (aRole == EXC_CHPROP_ROLE_YVALUES) )
1856                 {
1857                     xYValueSeq = xTmpValueSeq;
1858                     if( !xTitleSeq.is() )
1859                         xTitleSeq = rLabeledSeq->getLabel(); // ignore role of label sequence
1860                 }
1861                 else if( !xXValueSeq.is() && !rTypeInfo.mbCategoryAxis && (aRole == EXC_CHPROP_ROLE_XVALUES) )
1862                 {
1863                     xXValueSeq = xTmpValueSeq;
1864                 }
1865                 else if( !xBubbleSeq.is() && (rTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES) && (aRole == EXC_CHPROP_ROLE_SIZEVALUES) )
1866                 {
1867                     xBubbleSeq = xTmpValueSeq;
1868                     xTitleSeq = rLabeledSeq->getLabel();     // ignore role of label sequence
1869                 }
1870             }
1871         }
1872 
1873         bOk = xYValueSeq.is();
1874         if( bOk )
1875         {
1876             // chart type group index
1877             mnGroupIdx = nGroupIdx;
1878 
1879             // convert source links
1880             maData.mnValueCount = mxValueLink->ConvertDataSequence( xYValueSeq, true );
1881             mxTitleLink->ConvertDataSequence( xTitleSeq, true );
1882 
1883             // X values of XY charts
1884             maData.mnCategCount = mxCategLink->ConvertDataSequence( xXValueSeq, false, maData.mnValueCount );
1885 
1886             // size values of bubble charts
1887             if( mxBubbleLink )
1888                 mxBubbleLink->ConvertDataSequence( xBubbleSeq, false, maData.mnValueCount );
1889 
1890             // series formatting
1891             XclChDataPointPos aPointPos( mnSeriesIdx );
1892             ScfPropertySet aSeriesProp( xDataSeries );
1893             mxSeriesFmt = new XclExpChDataFormat( GetChRoot(), aPointPos, nFormatIdx );
1894             mxSeriesFmt->ConvertDataSeries( aSeriesProp, rTypeInfo );
1895 
1896             // trend lines
1897             CreateTrendLines( xDataSeries );
1898 
1899             // error bars
1900             CreateErrorBars( aSeriesProp, EXC_CHPROP_ERRORBARX, EXC_CHSERERR_XPLUS, EXC_CHSERERR_XMINUS );
1901             CreateErrorBars( aSeriesProp, EXC_CHPROP_ERRORBARY, EXC_CHSERERR_YPLUS, EXC_CHSERERR_YMINUS );
1902 
1903             if( maData.mnValueCount > 0 )
1904             {
1905                 const sal_Int32 nMaxPointCount = maData.mnValueCount;
1906 
1907                 /*  #i91063# Create missing fill properties in pie/doughnut charts.
1908                     If freshly created (never saved to ODF), these charts show
1909                     varying point colors but do not return these points via API. */
1910                 if( xDiagram.is() && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_PIE) )
1911                 {
1912                     Reference< XColorScheme > xColorScheme = xDiagram->getDefaultColorScheme();
1913                     if( xColorScheme.is() )
1914                     {
1915                         static const OUStringLiteral aFillStyleName = u"FillStyle";
1916                         static const OUStringLiteral aColorName = u"Color";
1917                         namespace cssd = ::com::sun::star::drawing;
1918                         for( sal_Int32 nPointIdx = 0; nPointIdx < nMaxPointCount; ++nPointIdx )
1919                         {
1920                             aPointPos.mnPointIdx = static_cast< sal_uInt16 >( nPointIdx );
1921                             ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIdx );
1922                             // test that the point fill style is solid, but no color is set
1923                             cssd::FillStyle eFillStyle = cssd::FillStyle_NONE;
1924                             if( aPointProp.GetProperty( eFillStyle, aFillStyleName ) &&
1925                                 (eFillStyle == cssd::FillStyle_SOLID) &&
1926                                 !aPointProp.HasProperty( aColorName ) )
1927                             {
1928                                 aPointProp.SetProperty( aColorName, xColorScheme->getColorByIndex( nPointIdx ) );
1929                             }
1930                         }
1931                     }
1932                 }
1933 
1934                 // data point formatting
1935                 Sequence< sal_Int32 > aPointIndexes;
1936                 if( aSeriesProp.GetProperty( aPointIndexes, EXC_CHPROP_ATTRIBDATAPOINTS ) && aPointIndexes.hasElements() )
1937                 {
1938                     for( const sal_Int32 nPointIndex : aPointIndexes )
1939                     {
1940                         if (nPointIndex >= nMaxPointCount)
1941                             break;
1942                         aPointPos.mnPointIdx = static_cast< sal_uInt16 >( nPointIndex );
1943                         ScfPropertySet aPointProp = lclGetPointPropSet( xDataSeries, nPointIndex );
1944                         XclExpChDataFormatRef xPointFmt = new XclExpChDataFormat( GetChRoot(), aPointPos, nFormatIdx );
1945                         xPointFmt->ConvertDataSeries( aPointProp, rTypeInfo );
1946                         maPointFmts.AppendRecord( xPointFmt );
1947                     }
1948                 }
1949             }
1950         }
1951     }
1952     return bOk;
1953 }
1954 
ConvertStockSeries(css::uno::Reference<css::chart2::XDataSeries> const & xDataSeries,std::u16string_view rValueRole,sal_uInt16 nGroupIdx,sal_uInt16 nFormatIdx,bool bCloseSymbol)1955 bool XclExpChSeries::ConvertStockSeries( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries,
1956         std::u16string_view rValueRole, sal_uInt16 nGroupIdx, sal_uInt16 nFormatIdx, bool bCloseSymbol )
1957 {
1958     bool bOk = false;
1959     Reference< XDataSource > xDataSource( xDataSeries, UNO_QUERY );
1960     if( xDataSource.is() )
1961     {
1962         Reference< XDataSequence > xYValueSeq, xTitleSeq;
1963 
1964         // find first sequence with passed role
1965         const Sequence< Reference< XLabeledDataSequence > > aLabeledSeqVec = xDataSource->getDataSequences();
1966         for( const Reference< XLabeledDataSequence >& rLabeledSeq : aLabeledSeqVec )
1967         {
1968             Reference< XDataSequence > xTmpValueSeq = rLabeledSeq->getValues();
1969             ScfPropertySet aValueProp( xTmpValueSeq );
1970             OUString aRole;
1971             if( aValueProp.GetProperty( aRole, EXC_CHPROP_ROLE ) && (aRole == rValueRole) )
1972             {
1973                 xYValueSeq = xTmpValueSeq;
1974                 xTitleSeq = rLabeledSeq->getLabel();     // ignore role of label sequence
1975                 break;
1976             }
1977         }
1978 
1979         bOk = xYValueSeq.is();
1980         if( bOk )
1981         {
1982             // chart type group index
1983             mnGroupIdx = nGroupIdx;
1984             // convert source links
1985             maData.mnValueCount = mxValueLink->ConvertDataSequence( xYValueSeq, true );
1986             mxTitleLink->ConvertDataSequence( xTitleSeq, true );
1987             // series formatting
1988             ScfPropertySet aSeriesProp( xDataSeries );
1989             mxSeriesFmt = new XclExpChDataFormat( GetChRoot(), XclChDataPointPos( mnSeriesIdx ), nFormatIdx );
1990             mxSeriesFmt->ConvertStockSeries( aSeriesProp, bCloseSymbol );
1991         }
1992     }
1993     return bOk;
1994 }
1995 
ConvertTrendLine(const XclExpChSeries & rParent,Reference<XRegressionCurve> const & xRegCurve)1996 bool XclExpChSeries::ConvertTrendLine( const XclExpChSeries& rParent, Reference< XRegressionCurve > const & xRegCurve )
1997 {
1998     InitFromParent( rParent );
1999 
2000     mxTrendLine = new XclExpChSerTrendLine( GetChRoot() );
2001     bool bOk = mxTrendLine->Convert( xRegCurve, mnSeriesIdx );
2002     if( bOk )
2003     {
2004         OUString aName;
2005         ScfPropertySet aProperties( xRegCurve );
2006         aProperties.GetProperty(aName, EXC_CHPROP_CURVENAME);
2007         mxTitleLink->ConvertString(aName);
2008 
2009         mxSeriesFmt = mxTrendLine->GetDataFormat();
2010         GetChartData().SetDataLabel( mxTrendLine->GetDataLabel() );
2011     }
2012     return bOk;
2013 }
2014 
ConvertErrorBar(const XclExpChSeries & rParent,const ScfPropertySet & rPropSet,sal_uInt8 nBarId)2015 bool XclExpChSeries::ConvertErrorBar( const XclExpChSeries& rParent, const ScfPropertySet& rPropSet, sal_uInt8 nBarId )
2016 {
2017     InitFromParent( rParent );
2018     // error bar settings
2019     mxErrorBar = new XclExpChSerErrorBar( GetChRoot(), nBarId );
2020     bool bOk = mxErrorBar->Convert( *mxValueLink, maData.mnValueCount, rPropSet );
2021     if( bOk )
2022     {
2023         // error bar formatting
2024         mxSeriesFmt = new XclExpChDataFormat( GetChRoot(), XclChDataPointPos( mnSeriesIdx ), 0 );
2025         mxSeriesFmt->ConvertLine( rPropSet, EXC_CHOBJTYPE_ERRORBAR );
2026     }
2027     return bOk;
2028 }
2029 
ConvertCategSequence(Reference<XLabeledDataSequence> const & xCategSeq)2030 void XclExpChSeries::ConvertCategSequence( Reference< XLabeledDataSequence > const & xCategSeq )
2031 {
2032     if( xCategSeq.is() )
2033         maData.mnCategCount = mxCategLink->ConvertDataSequence( xCategSeq->getValues(), false );
2034 }
2035 
WriteSubRecords(XclExpStream & rStrm)2036 void XclExpChSeries::WriteSubRecords( XclExpStream& rStrm )
2037 {
2038     lclSaveRecord( rStrm, mxTitleLink );
2039     lclSaveRecord( rStrm, mxValueLink );
2040     lclSaveRecord( rStrm, mxCategLink );
2041     lclSaveRecord( rStrm, mxBubbleLink );
2042     lclSaveRecord( rStrm, mxSeriesFmt );
2043     maPointFmts.Save( rStrm );
2044     if( mnGroupIdx != EXC_CHSERGROUP_NONE )
2045         XclExpUInt16Record( EXC_ID_CHSERGROUP, mnGroupIdx ).Save( rStrm );
2046     if( mnParentIdx != EXC_CHSERIES_INVALID )
2047         XclExpUInt16Record( EXC_ID_CHSERPARENT, mnParentIdx ).Save( rStrm );
2048     lclSaveRecord( rStrm, mxTrendLine );
2049     lclSaveRecord( rStrm, mxErrorBar );
2050 }
2051 
InitFromParent(const XclExpChSeries & rParent)2052 void XclExpChSeries::InitFromParent( const XclExpChSeries& rParent )
2053 {
2054     // index to parent series is stored 1-based
2055     mnParentIdx = rParent.mnSeriesIdx + 1;
2056     /*  #i86465# MSO2007 SP1 expects correct point counts in child series
2057         (there was no problem in Excel2003 or Excel2007 without SP1...) */
2058     maData.mnCategCount = rParent.maData.mnCategCount;
2059     maData.mnValueCount = rParent.maData.mnValueCount;
2060 }
2061 
CreateTrendLines(css::uno::Reference<css::chart2::XDataSeries> const & xDataSeries)2062 void XclExpChSeries::CreateTrendLines( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries )
2063 {
2064     Reference< XRegressionCurveContainer > xRegCurveCont( xDataSeries, UNO_QUERY );
2065     if( xRegCurveCont.is() )
2066     {
2067         const Sequence< Reference< XRegressionCurve > > aRegCurveSeq = xRegCurveCont->getRegressionCurves();
2068         for( const Reference< XRegressionCurve >& rRegCurve : aRegCurveSeq )
2069         {
2070             XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
2071             if( xSeries && !xSeries->ConvertTrendLine( *this, rRegCurve ) )
2072                 GetChartData().RemoveLastSeries();
2073         }
2074     }
2075 }
2076 
CreateErrorBars(const ScfPropertySet & rPropSet,const OUString & rBarPropName,sal_uInt8 nPosBarId,sal_uInt8 nNegBarId)2077 void XclExpChSeries::CreateErrorBars( const ScfPropertySet& rPropSet,
2078         const OUString& rBarPropName, sal_uInt8 nPosBarId, sal_uInt8 nNegBarId )
2079 {
2080     Reference< XPropertySet > xErrorBar;
2081     if( rPropSet.GetProperty( xErrorBar, rBarPropName ) && xErrorBar.is() )
2082     {
2083         ScfPropertySet aErrorProp( xErrorBar );
2084         CreateErrorBar( aErrorProp, EXC_CHPROP_SHOWPOSITIVEERROR, nPosBarId );
2085         CreateErrorBar( aErrorProp, EXC_CHPROP_SHOWNEGATIVEERROR, nNegBarId );
2086     }
2087 }
2088 
CreateErrorBar(const ScfPropertySet & rPropSet,const OUString & rShowPropName,sal_uInt8 nBarId)2089 void XclExpChSeries::CreateErrorBar( const ScfPropertySet& rPropSet,
2090         const OUString& rShowPropName, sal_uInt8 nBarId )
2091 {
2092     if( rPropSet.GetBoolProperty( rShowPropName ) )
2093     {
2094         XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
2095         if( xSeries && !xSeries->ConvertErrorBar( *this, rPropSet, nBarId ) )
2096             GetChartData().RemoveLastSeries();
2097     }
2098 }
2099 
WriteBody(XclExpStream & rStrm)2100 void XclExpChSeries::WriteBody( XclExpStream& rStrm )
2101 {
2102     rStrm << maData.mnCategType << maData.mnValueType << maData.mnCategCount << maData.mnValueCount;
2103     if( GetBiff() == EXC_BIFF8 )
2104         rStrm << maData.mnBubbleType << maData.mnBubbleCount;
2105 }
2106 
2107 // Chart type groups ==========================================================
2108 
XclExpChType(const XclExpChRoot & rRoot)2109 XclExpChType::XclExpChType( const XclExpChRoot& rRoot ) :
2110     XclExpRecord( EXC_ID_CHUNKNOWN ),
2111     XclExpChRoot( rRoot ),
2112     maTypeInfo( rRoot.GetChartTypeInfo( EXC_CHTYPEID_UNKNOWN ) )
2113 {
2114 }
2115 
Convert(Reference<XDiagram> const & xDiagram,Reference<XChartType> const & xChartType,sal_Int32 nApiAxesSetIdx,bool bSwappedAxesSet,bool bHasXLabels)2116 void XclExpChType::Convert( Reference< XDiagram > const & xDiagram, Reference< XChartType > const & xChartType,
2117         sal_Int32 nApiAxesSetIdx, bool bSwappedAxesSet, bool bHasXLabels )
2118 {
2119     if( !xChartType.is() )
2120         return;
2121 
2122     maTypeInfo = GetChartTypeInfo( xChartType->getChartType() );
2123     // special handling for some chart types
2124     switch( maTypeInfo.meTypeCateg )
2125     {
2126         case EXC_CHTYPECATEG_BAR:
2127         {
2128             maTypeInfo = GetChartTypeInfo( bSwappedAxesSet ? EXC_CHTYPEID_HORBAR : EXC_CHTYPEID_BAR );
2129             ::set_flag( maData.mnFlags, EXC_CHBAR_HORIZONTAL, bSwappedAxesSet );
2130             ScfPropertySet aTypeProp( xChartType );
2131             Sequence< sal_Int32 > aInt32Seq;
2132             maData.mnOverlap = 0;
2133             if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_OVERLAPSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) )
2134                 maData.mnOverlap = limit_cast< sal_Int16 >( -aInt32Seq[ nApiAxesSetIdx ], -100, 100 );
2135             maData.mnGap = 150;
2136             if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_GAPWIDTHSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) )
2137                 maData.mnGap = limit_cast< sal_uInt16 >( aInt32Seq[ nApiAxesSetIdx ], 0, 500 );
2138         }
2139         break;
2140         case EXC_CHTYPECATEG_RADAR:
2141             ::set_flag( maData.mnFlags, EXC_CHRADAR_AXISLABELS, bHasXLabels );
2142         break;
2143         case EXC_CHTYPECATEG_PIE:
2144         {
2145             ScfPropertySet aTypeProp( xChartType );
2146             bool bDonut = aTypeProp.GetBoolProperty( EXC_CHPROP_USERINGS );
2147             maTypeInfo = GetChartTypeInfo( bDonut ? EXC_CHTYPEID_DONUT : EXC_CHTYPEID_PIE );
2148             maData.mnPieHole = bDonut ? 50 : 0;
2149             // #i85166# starting angle of first pie slice
2150             ScfPropertySet aDiaProp( xDiagram );
2151             maData.mnRotation = XclExpChRoot::ConvertPieRotation( aDiaProp );
2152         }
2153         break;
2154         case EXC_CHTYPECATEG_SCATTER:
2155             if( GetBiff() == EXC_BIFF8 )
2156                 ::set_flag( maData.mnFlags, EXC_CHSCATTER_BUBBLES, maTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES );
2157         break;
2158         default:;
2159     }
2160     SetRecId( maTypeInfo.mnRecId );
2161 }
2162 
SetStacked(bool bPercent)2163 void XclExpChType::SetStacked( bool bPercent )
2164 {
2165     switch( maTypeInfo.meTypeCateg )
2166     {
2167         case EXC_CHTYPECATEG_LINE:
2168             ::set_flag( maData.mnFlags, EXC_CHLINE_STACKED );
2169             ::set_flag( maData.mnFlags, EXC_CHLINE_PERCENT, bPercent );
2170         break;
2171         case EXC_CHTYPECATEG_BAR:
2172             ::set_flag( maData.mnFlags, EXC_CHBAR_STACKED );
2173             ::set_flag( maData.mnFlags, EXC_CHBAR_PERCENT, bPercent );
2174             maData.mnOverlap = -100;
2175         break;
2176         default:;
2177     }
2178 }
2179 
WriteBody(XclExpStream & rStrm)2180 void XclExpChType::WriteBody( XclExpStream& rStrm )
2181 {
2182     switch( GetRecId() )
2183     {
2184         case EXC_ID_CHBAR:
2185             rStrm << maData.mnOverlap << maData.mnGap << maData.mnFlags;
2186         break;
2187 
2188         case EXC_ID_CHLINE:
2189         case EXC_ID_CHAREA:
2190         case EXC_ID_CHRADARLINE:
2191         case EXC_ID_CHRADARAREA:
2192             rStrm << maData.mnFlags;
2193         break;
2194 
2195         case EXC_ID_CHPIE:
2196             rStrm << maData.mnRotation << maData.mnPieHole;
2197             if( GetBiff() == EXC_BIFF8 )
2198                 rStrm << maData.mnFlags;
2199         break;
2200 
2201         case EXC_ID_CHSCATTER:
2202             if( GetBiff() == EXC_BIFF8 )
2203                 rStrm << maData.mnBubbleSize << maData.mnBubbleType << maData.mnFlags;
2204         break;
2205 
2206         default:
2207             OSL_FAIL( "XclExpChType::WriteBody - unknown chart type" );
2208     }
2209 }
2210 
XclExpChChart3d()2211 XclExpChChart3d::XclExpChChart3d() :
2212     XclExpRecord( EXC_ID_CHCHART3D, 14 )
2213 {
2214 }
2215 
Convert(const ScfPropertySet & rPropSet,bool b3dWallChart)2216 void XclExpChChart3d::Convert( const ScfPropertySet& rPropSet, bool b3dWallChart )
2217 {
2218     sal_Int32 nRotationY = 0;
2219     rPropSet.GetProperty( nRotationY, EXC_CHPROP_ROTATIONVERTICAL );
2220     sal_Int32 nRotationX = 0;
2221     rPropSet.GetProperty( nRotationX, EXC_CHPROP_ROTATIONHORIZONTAL );
2222     sal_Int32 nPerspective = 15;
2223     rPropSet.GetProperty( nPerspective, EXC_CHPROP_PERSPECTIVE );
2224 
2225     if( b3dWallChart )
2226     {
2227         // Y rotation (Excel [0..359], Chart2 [-179,180])
2228         if( nRotationY < 0 ) nRotationY += 360;
2229         maData.mnRotation = static_cast< sal_uInt16 >( nRotationY );
2230         // X rotation a.k.a. elevation (Excel [-90..90], Chart2 [-179,180])
2231         maData.mnElevation = limit_cast< sal_Int16 >( nRotationX, -90, 90 );
2232         // perspective (Excel and Chart2 [0,100])
2233         maData.mnEyeDist = limit_cast< sal_uInt16 >( nPerspective, 0, 100 );
2234         // flags
2235         maData.mnFlags = 0;
2236         ::set_flag( maData.mnFlags, EXC_CHCHART3D_REAL3D, !rPropSet.GetBoolProperty( EXC_CHPROP_RIGHTANGLEDAXES ) );
2237         ::set_flag( maData.mnFlags, EXC_CHCHART3D_AUTOHEIGHT );
2238         ::set_flag( maData.mnFlags, EXC_CHCHART3D_HASWALLS );
2239     }
2240     else
2241     {
2242         // Y rotation not used in pie charts, but 'first pie slice angle'
2243         maData.mnRotation = XclExpChRoot::ConvertPieRotation( rPropSet );
2244         // X rotation a.k.a. elevation (map Chart2 [-80,-10] to Excel [10..80])
2245         maData.mnElevation = limit_cast< sal_Int16 >( (nRotationX + 270) % 180, 10, 80 );
2246         // perspective (Excel and Chart2 [0,100])
2247         maData.mnEyeDist = limit_cast< sal_uInt16 >( nPerspective, 0, 100 );
2248         // flags
2249         maData.mnFlags = 0;
2250     }
2251 }
2252 
WriteBody(XclExpStream & rStrm)2253 void XclExpChChart3d::WriteBody( XclExpStream& rStrm )
2254 {
2255     rStrm   << maData.mnRotation
2256             << maData.mnElevation
2257             << maData.mnEyeDist
2258             << maData.mnRelHeight
2259             << maData.mnRelDepth
2260             << maData.mnDepthGap
2261             << maData.mnFlags;
2262 }
2263 
XclExpChLegend(const XclExpChRoot & rRoot)2264 XclExpChLegend::XclExpChLegend( const XclExpChRoot& rRoot ) :
2265     XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_LEGEND, EXC_ID_CHLEGEND, 20 )
2266 {
2267 }
2268 
Convert(const ScfPropertySet & rPropSet)2269 void XclExpChLegend::Convert( const ScfPropertySet& rPropSet )
2270 {
2271     // frame properties
2272     mxFrame = lclCreateFrame( GetChRoot(), rPropSet, EXC_CHOBJTYPE_LEGEND );
2273     // text properties
2274     mxText = new XclExpChText( GetChRoot() );
2275     mxText->ConvertLegend( rPropSet );
2276 
2277     // legend position and size
2278     Any aRelPosAny, aRelSizeAny;
2279     rPropSet.GetAnyProperty( aRelPosAny, EXC_CHPROP_RELATIVEPOSITION );
2280     rPropSet.GetAnyProperty( aRelSizeAny, EXC_CHPROP_RELATIVESIZE );
2281     cssc::ChartLegendExpansion eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
2282     rPropSet.GetProperty( eApiExpand, EXC_CHPROP_EXPANSION );
2283     if( aRelPosAny.has< RelativePosition >() || ((eApiExpand == cssc::ChartLegendExpansion_CUSTOM) && aRelSizeAny.has< RelativeSize >()) )
2284     {
2285         try
2286         {
2287             /*  The 'RelativePosition' or 'RelativeSize' properties are used as
2288                 indicator of manually changed legend position/size, but due to
2289                 the different anchor modes used by this property (in the
2290                 RelativePosition.Anchor member) it cannot be used to calculate
2291                 the position easily. For this, the Chart1 API will be used
2292                 instead. */
2293             Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
2294             Reference< XShape > xChart1Legend( xChart1Doc->getLegend(), UNO_SET_THROW );
2295             // coordinates in CHLEGEND record written but not used by Excel
2296             mxFramePos = new XclExpChFramePos( EXC_CHFRAMEPOS_CHARTSIZE );
2297             XclChFramePos& rFramePos = mxFramePos->GetFramePosData();
2298             rFramePos.mnTLMode = EXC_CHFRAMEPOS_CHARTSIZE;
2299             css::awt::Point aLegendPos = xChart1Legend->getPosition();
2300             rFramePos.maRect.mnX = maData.maRect.mnX = CalcChartXFromHmm( aLegendPos.X );
2301             rFramePos.maRect.mnY = maData.maRect.mnY = CalcChartYFromHmm( aLegendPos.Y );
2302             // legend size, Excel expects points in CHFRAMEPOS record
2303             rFramePos.mnBRMode = EXC_CHFRAMEPOS_ABSSIZE_POINTS;
2304             css::awt::Size aLegendSize = xChart1Legend->getSize();
2305             rFramePos.maRect.mnWidth = o3tl::convert(aLegendSize.Width, o3tl::Length::mm100, o3tl::Length::pt);
2306             rFramePos.maRect.mnHeight = o3tl::convert(aLegendSize.Height, o3tl::Length::mm100, o3tl::Length::pt);
2307             maData.maRect.mnWidth = CalcChartXFromHmm( aLegendSize.Width );
2308             maData.maRect.mnHeight = CalcChartYFromHmm( aLegendSize.Height );
2309             eApiExpand = cssc::ChartLegendExpansion_CUSTOM;
2310             // manual legend position implies manual plot area
2311             GetChartData().SetManualPlotArea();
2312             maData.mnDockMode = EXC_CHLEGEND_NOTDOCKED;
2313             // a CHFRAME record with cleared auto flags is needed
2314             if( !mxFrame )
2315                 mxFrame = new XclExpChFrame( GetChRoot(), EXC_CHOBJTYPE_LEGEND );
2316             mxFrame->SetAutoFlags( false, false );
2317         }
2318         catch( Exception& )
2319         {
2320             OSL_FAIL( "XclExpChLegend::Convert - cannot get legend shape" );
2321             maData.mnDockMode = EXC_CHLEGEND_RIGHT;
2322             eApiExpand = cssc::ChartLegendExpansion_HIGH;
2323         }
2324     }
2325     else
2326     {
2327         cssc2::LegendPosition eApiPos = cssc2::LegendPosition_LINE_END;
2328         rPropSet.GetProperty( eApiPos, EXC_CHPROP_ANCHORPOSITION );
2329         switch( eApiPos )
2330         {
2331             case cssc2::LegendPosition_LINE_START:   maData.mnDockMode = EXC_CHLEGEND_LEFT;      break;
2332             case cssc2::LegendPosition_LINE_END:     maData.mnDockMode = EXC_CHLEGEND_RIGHT;     break;
2333             case cssc2::LegendPosition_PAGE_START:   maData.mnDockMode = EXC_CHLEGEND_TOP;       break;
2334             case cssc2::LegendPosition_PAGE_END:     maData.mnDockMode = EXC_CHLEGEND_BOTTOM;    break;
2335             default:
2336                 OSL_FAIL( "XclExpChLegend::Convert - unrecognized legend position" );
2337                 maData.mnDockMode = EXC_CHLEGEND_RIGHT;
2338                 eApiExpand = cssc::ChartLegendExpansion_HIGH;
2339         }
2340     }
2341     ::set_flag( maData.mnFlags, EXC_CHLEGEND_STACKED, eApiExpand == cssc::ChartLegendExpansion_HIGH );
2342 
2343     // other flags
2344     ::set_flag( maData.mnFlags, EXC_CHLEGEND_AUTOSERIES );
2345     const sal_uInt16 nAutoFlags = EXC_CHLEGEND_DOCKED | EXC_CHLEGEND_AUTOPOSX | EXC_CHLEGEND_AUTOPOSY;
2346     ::set_flag( maData.mnFlags, nAutoFlags, maData.mnDockMode != EXC_CHLEGEND_NOTDOCKED );
2347 }
2348 
WriteSubRecords(XclExpStream & rStrm)2349 void XclExpChLegend::WriteSubRecords( XclExpStream& rStrm )
2350 {
2351     lclSaveRecord( rStrm, mxFramePos );
2352     lclSaveRecord( rStrm, mxText );
2353     lclSaveRecord( rStrm, mxFrame );
2354 }
2355 
WriteBody(XclExpStream & rStrm)2356 void XclExpChLegend::WriteBody( XclExpStream& rStrm )
2357 {
2358     rStrm << maData.maRect << maData.mnDockMode << maData.mnSpacing << maData.mnFlags;
2359 }
2360 
XclExpChDropBar(const XclExpChRoot & rRoot,XclChObjectType eObjType)2361 XclExpChDropBar::XclExpChDropBar( const XclExpChRoot& rRoot, XclChObjectType eObjType ) :
2362     XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_DROPBAR, EXC_ID_CHDROPBAR, 2 ),
2363     meObjType( eObjType )
2364 {
2365 }
2366 
Convert(const ScfPropertySet & rPropSet)2367 void XclExpChDropBar::Convert( const ScfPropertySet& rPropSet )
2368 {
2369     if( rPropSet.Is() )
2370         ConvertFrameBase( GetChRoot(), rPropSet, meObjType );
2371     else
2372         SetDefaultFrameBase( GetChRoot(), EXC_CHFRAMETYPE_INVISIBLE, true );
2373 }
2374 
WriteSubRecords(XclExpStream & rStrm)2375 void XclExpChDropBar::WriteSubRecords( XclExpStream& rStrm )
2376 {
2377     WriteFrameRecords( rStrm );
2378 }
2379 
WriteBody(XclExpStream & rStrm)2380 void XclExpChDropBar::WriteBody( XclExpStream& rStrm )
2381 {
2382     rStrm << sal_uInt16(100); // Distance between bars (CHDROPBAR record).
2383 }
2384 
XclExpChTypeGroup(const XclExpChRoot & rRoot,sal_uInt16 nGroupIdx)2385 XclExpChTypeGroup::XclExpChTypeGroup( const XclExpChRoot& rRoot, sal_uInt16 nGroupIdx ) :
2386     XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_TYPEGROUP, EXC_ID_CHTYPEGROUP, 20 ),
2387     maType( rRoot ),
2388     maTypeInfo( maType.GetTypeInfo() )
2389 {
2390     maData.mnGroupIdx = nGroupIdx;
2391 }
2392 
ConvertType(Reference<XDiagram> const & xDiagram,Reference<XChartType> const & xChartType,sal_Int32 nApiAxesSetIdx,bool b3dChart,bool bSwappedAxesSet,bool bHasXLabels)2393 void XclExpChTypeGroup::ConvertType(
2394         Reference< XDiagram > const & xDiagram, Reference< XChartType > const & xChartType,
2395         sal_Int32 nApiAxesSetIdx, bool b3dChart, bool bSwappedAxesSet, bool bHasXLabels )
2396 {
2397     // chart type settings
2398     maType.Convert( xDiagram, xChartType, nApiAxesSetIdx, bSwappedAxesSet, bHasXLabels );
2399 
2400     // spline - TODO: get from single series (#i66858#)
2401     ScfPropertySet aTypeProp( xChartType );
2402     cssc2::CurveStyle eCurveStyle;
2403     bool bSpline = aTypeProp.GetProperty( eCurveStyle, EXC_CHPROP_CURVESTYLE ) &&
2404         (eCurveStyle != cssc2::CurveStyle_LINES);
2405 
2406     // extended type info
2407     maTypeInfo.Set( maType.GetTypeInfo(), b3dChart, bSpline );
2408 
2409     // 3d chart settings
2410     if( maTypeInfo.mb3dChart )  // only true, if Excel chart supports 3d mode
2411     {
2412         mxChart3d = new XclExpChChart3d();
2413         ScfPropertySet aDiaProp( xDiagram );
2414         mxChart3d->Convert( aDiaProp, Is3dWallChart() );
2415     }
2416 }
2417 
ConvertSeries(Reference<XDiagram> const & xDiagram,Reference<XChartType> const & xChartType,sal_Int32 nGroupAxesSetIdx,bool bPercent,bool bConnectBars)2418 void XclExpChTypeGroup::ConvertSeries(
2419         Reference< XDiagram > const & xDiagram, Reference< XChartType > const & xChartType,
2420         sal_Int32 nGroupAxesSetIdx, bool bPercent, bool bConnectBars )
2421 {
2422     Reference< XDataSeriesContainer > xSeriesCont( xChartType, UNO_QUERY );
2423     if( !xSeriesCont.is() )
2424         return;
2425 
2426     std::vector< Reference< XDataSeries > > aSeriesVec;
2427 
2428     // copy data series attached to the current axes set to the vector
2429     const Sequence< Reference< XDataSeries > > aSeriesSeq = xSeriesCont->getDataSeries();
2430     for( const Reference< XDataSeries >& rSeries : aSeriesSeq )
2431     {
2432         ScfPropertySet aSeriesProp( rSeries );
2433         sal_Int32 nSeriesAxesSetIdx(0);
2434         if( aSeriesProp.GetProperty( nSeriesAxesSetIdx, EXC_CHPROP_ATTAXISINDEX ) && (nSeriesAxesSetIdx == nGroupAxesSetIdx) )
2435             aSeriesVec.push_back( rSeries );
2436     }
2437 
2438     // Are there any series in the current axes set?
2439     if( aSeriesVec.empty() )
2440         return;
2441 
2442     // stacking direction (stacked/percent/deep 3d) from first series
2443     ScfPropertySet aSeriesProp( aSeriesVec.front() );
2444     cssc2::StackingDirection eStacking;
2445     if( !aSeriesProp.GetProperty( eStacking, EXC_CHPROP_STACKINGDIR ) )
2446         eStacking = cssc2::StackingDirection_NO_STACKING;
2447 
2448     // stacked or percent chart
2449     if( maTypeInfo.mbSupportsStacking && (eStacking == cssc2::StackingDirection_Y_STACKING) )
2450     {
2451         // percent overrides simple stacking
2452         maType.SetStacked( bPercent );
2453 
2454         // connected data points (only in stacked bar charts)
2455         if (bConnectBars && (maTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR))
2456         {
2457             sal_uInt16 nKey = EXC_CHCHARTLINE_CONNECT;
2458             m_ChartLines.insert(std::make_pair(nKey, std::make_unique<XclExpChLineFormat>(GetChRoot())));
2459         }
2460     }
2461     else
2462     {
2463         // reverse series order for some unstacked 2D chart types
2464         if( maTypeInfo.mbReverseSeries && !Is3dChart() )
2465             ::std::reverse( aSeriesVec.begin(), aSeriesVec.end() );
2466     }
2467 
2468     // deep 3d chart or clustered 3d chart (stacked is not clustered)
2469     if( (eStacking == cssc2::StackingDirection_NO_STACKING) && Is3dWallChart() )
2470         mxChart3d->SetClustered();
2471 
2472     // varied point colors
2473     ::set_flag( maData.mnFlags, EXC_CHTYPEGROUP_VARIEDCOLORS, aSeriesProp.GetBoolProperty( EXC_CHPROP_VARYCOLORSBY ) );
2474 
2475     // process all series
2476     for( const auto& rxSeries : aSeriesVec )
2477     {
2478         // create Excel series object, stock charts need special processing
2479         if( maTypeInfo.meTypeId == EXC_CHTYPEID_STOCK )
2480             CreateAllStockSeries( xChartType, rxSeries );
2481         else
2482             CreateDataSeries( xDiagram, rxSeries );
2483     }
2484 }
2485 
ConvertCategSequence(Reference<XLabeledDataSequence> const & xCategSeq)2486 void XclExpChTypeGroup::ConvertCategSequence( Reference< XLabeledDataSequence > const & xCategSeq )
2487 {
2488     for( size_t nIdx = 0, nSize = maSeries.GetSize(); nIdx < nSize; ++nIdx )
2489         maSeries.GetRecord( nIdx )->ConvertCategSequence( xCategSeq );
2490 }
2491 
ConvertLegend(const ScfPropertySet & rPropSet)2492 void XclExpChTypeGroup::ConvertLegend( const ScfPropertySet& rPropSet )
2493 {
2494     if( rPropSet.GetBoolProperty( EXC_CHPROP_SHOW ) )
2495     {
2496         mxLegend = new XclExpChLegend( GetChRoot() );
2497         mxLegend->Convert( rPropSet );
2498     }
2499 }
2500 
WriteSubRecords(XclExpStream & rStrm)2501 void XclExpChTypeGroup::WriteSubRecords( XclExpStream& rStrm )
2502 {
2503     maType.Save( rStrm );
2504     lclSaveRecord( rStrm, mxChart3d );
2505     lclSaveRecord( rStrm, mxLegend );
2506     lclSaveRecord( rStrm, mxUpBar );
2507     lclSaveRecord( rStrm, mxDownBar );
2508     for (auto const& it : m_ChartLines)
2509     {
2510         lclSaveRecord( rStrm, it.second.get(), EXC_ID_CHCHARTLINE, it.first );
2511     }
2512 }
2513 
GetFreeFormatIdx() const2514 sal_uInt16 XclExpChTypeGroup::GetFreeFormatIdx() const
2515 {
2516     return static_cast< sal_uInt16 >( maSeries.GetSize() );
2517 }
2518 
CreateDataSeries(Reference<XDiagram> const & xDiagram,Reference<XDataSeries> const & xDataSeries)2519 void XclExpChTypeGroup::CreateDataSeries(
2520         Reference< XDiagram > const & xDiagram, Reference< XDataSeries > const & xDataSeries )
2521 {
2522     // let chart create series object with correct series index
2523     XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
2524     if( xSeries )
2525     {
2526         if( xSeries->ConvertDataSeries( xDiagram, xDataSeries, maTypeInfo, GetGroupIdx(), GetFreeFormatIdx() ) )
2527             maSeries.AppendRecord( xSeries );
2528         else
2529             GetChartData().RemoveLastSeries();
2530     }
2531 }
2532 
CreateAllStockSeries(Reference<XChartType> const & xChartType,Reference<XDataSeries> const & xDataSeries)2533 void XclExpChTypeGroup::CreateAllStockSeries(
2534         Reference< XChartType > const & xChartType, Reference< XDataSeries > const & xDataSeries )
2535 {
2536     // create existing series objects
2537     bool bHasOpen = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_OPENVALUES, false );
2538     bool bHasHigh = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_HIGHVALUES, false );
2539     bool bHasLow = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_LOWVALUES, false );
2540     bool bHasClose = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_CLOSEVALUES, !bHasOpen );
2541 
2542     // formatting of special stock chart elements
2543     ScfPropertySet aTypeProp( xChartType );
2544     // hi-lo lines
2545     if( bHasHigh && bHasLow && aTypeProp.GetBoolProperty( EXC_CHPROP_SHOWHIGHLOW ) )
2546     {
2547         ScfPropertySet aSeriesProp( xDataSeries );
2548         XclExpChLineFormatRef xLineFmt = new XclExpChLineFormat( GetChRoot() );
2549         xLineFmt->Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE );
2550         sal_uInt16 nKey = EXC_CHCHARTLINE_HILO;
2551         m_ChartLines.insert(std::make_pair(nKey, std::make_unique<XclExpChLineFormat>(GetChRoot())));
2552     }
2553     // dropbars
2554     if( !(bHasOpen && bHasClose) )
2555         return;
2556 
2557     // dropbar type is dependent on position in the file - always create both
2558     Reference< XPropertySet > xWhitePropSet, xBlackPropSet;
2559     // white dropbar format
2560     aTypeProp.GetProperty( xWhitePropSet, EXC_CHPROP_WHITEDAY );
2561     ScfPropertySet aWhiteProp( xWhitePropSet );
2562     mxUpBar = new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_WHITEDROPBAR );
2563     mxUpBar->Convert( aWhiteProp );
2564     // black dropbar format
2565     aTypeProp.GetProperty( xBlackPropSet, EXC_CHPROP_BLACKDAY );
2566     ScfPropertySet aBlackProp( xBlackPropSet );
2567     mxDownBar = new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_BLACKDROPBAR );
2568     mxDownBar->Convert( aBlackProp );
2569 }
2570 
CreateStockSeries(Reference<XDataSeries> const & xDataSeries,std::u16string_view rValueRole,bool bCloseSymbol)2571 bool XclExpChTypeGroup::CreateStockSeries( Reference< XDataSeries > const & xDataSeries,
2572         std::u16string_view rValueRole, bool bCloseSymbol )
2573 {
2574     bool bOk = false;
2575     // let chart create series object with correct series index
2576     XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
2577     if( xSeries )
2578     {
2579         bOk = xSeries->ConvertStockSeries( xDataSeries,
2580             rValueRole, GetGroupIdx(), GetFreeFormatIdx(), bCloseSymbol );
2581         if( bOk )
2582             maSeries.AppendRecord( xSeries );
2583         else
2584             GetChartData().RemoveLastSeries();
2585     }
2586     return bOk;
2587 }
2588 
WriteBody(XclExpStream & rStrm)2589 void XclExpChTypeGroup::WriteBody( XclExpStream& rStrm )
2590 {
2591     rStrm.WriteZeroBytes( 16 );
2592     rStrm << maData.mnFlags << maData.mnGroupIdx;
2593 }
2594 
2595 // Axes =======================================================================
2596 
XclExpChLabelRange(const XclExpChRoot & rRoot)2597 XclExpChLabelRange::XclExpChLabelRange( const XclExpChRoot& rRoot ) :
2598     XclExpRecord( EXC_ID_CHLABELRANGE, 8 ),
2599     XclExpChRoot( rRoot )
2600 {
2601 }
2602 
Convert(const ScaleData & rScaleData,const ScfPropertySet & rChart1Axis,bool bMirrorOrient)2603 void XclExpChLabelRange::Convert( const ScaleData& rScaleData, const ScfPropertySet& rChart1Axis, bool bMirrorOrient )
2604 {
2605     /*  Base time unit (using the property 'ExplicitTimeIncrement' from the old
2606         chart API allows to detect axis type (date axis, if property exists),
2607         and to receive the base time unit currently used in case the base time
2608         unit is set to 'automatic'. */
2609     cssc::TimeIncrement aTimeIncrement;
2610     if( rChart1Axis.GetProperty( aTimeIncrement, EXC_CHPROP_EXPTIMEINCREMENT ) )
2611     {
2612         // property exists -> this is a date axis currently
2613         ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS );
2614 
2615         // automatic base time unit, if the UNO Any 'rScaleData.TimeIncrement.TimeResolution' does not contain a valid value...
2616         bool bAutoBase = !rScaleData.TimeIncrement.TimeResolution.has< cssc::TimeIncrement >();
2617         ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOBASE, bAutoBase );
2618 
2619         // ...but get the current base time unit from the property of the old chart API
2620         sal_Int32 nApiTimeUnit = 0;
2621         bool bValidBaseUnit = aTimeIncrement.TimeResolution >>= nApiTimeUnit;
2622         OSL_ENSURE( bValidBaseUnit, "XclExpChLabelRange::Convert - cannot get base time unit" );
2623         maDateData.mnBaseUnit = bValidBaseUnit ? lclGetTimeUnit( nApiTimeUnit ) : EXC_CHDATERANGE_DAYS;
2624 
2625         /*  Min/max values depend on base time unit, they specify the number of
2626             days, months, or years starting from null date. */
2627         bool bAutoMin = lclConvertTimeValue( GetRoot(), maDateData.mnMinDate, rScaleData.Minimum, maDateData.mnBaseUnit );
2628         ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMIN, bAutoMin );
2629         bool bAutoMax = lclConvertTimeValue( GetRoot(), maDateData.mnMaxDate, rScaleData.Maximum, maDateData.mnBaseUnit );
2630         ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAX, bAutoMax );
2631     }
2632 
2633     // automatic axis type detection
2634     ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTODATE, rScaleData.AutoDateAxis );
2635 
2636     // increment
2637     bool bAutoMajor = lclConvertTimeInterval( maDateData.mnMajorStep, maDateData.mnMajorUnit, rScaleData.TimeIncrement.MajorTimeInterval );
2638     ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAJOR, bAutoMajor );
2639     bool bAutoMinor = lclConvertTimeInterval( maDateData.mnMinorStep, maDateData.mnMinorUnit, rScaleData.TimeIncrement.MinorTimeInterval );
2640     ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMINOR, bAutoMinor );
2641 
2642     // origin
2643     double fOrigin = 0.0;
2644     if( !lclIsAutoAnyOrGetValue( fOrigin, rScaleData.Origin ) )
2645         maLabelData.mnCross = limit_cast< sal_uInt16 >( fOrigin, 1, 31999 );
2646 
2647     // reverse order
2648     if( (rScaleData.Orientation == cssc2::AxisOrientation_REVERSE) != bMirrorOrient )
2649         ::set_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_REVERSE );
2650 }
2651 
ConvertAxisPosition(const ScfPropertySet & rPropSet)2652 void XclExpChLabelRange::ConvertAxisPosition( const ScfPropertySet& rPropSet )
2653 {
2654     cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE;
2655     rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION );
2656     double fCrossingPos = 1.0;
2657     rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE );
2658 
2659     bool bDateAxis = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS );
2660     switch( eAxisPos )
2661     {
2662         case cssc::ChartAxisPosition_ZERO:
2663         case cssc::ChartAxisPosition_START:
2664             maLabelData.mnCross = 1;
2665             ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
2666         break;
2667         case cssc::ChartAxisPosition_END:
2668             ::set_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_MAXCROSS );
2669         break;
2670         case cssc::ChartAxisPosition_VALUE:
2671             maLabelData.mnCross = limit_cast< sal_uInt16 >( fCrossingPos, 1, 31999 );
2672             ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS, false );
2673             if( bDateAxis )
2674                 maDateData.mnCross = lclGetTimeValue( GetRoot(), fCrossingPos, maDateData.mnBaseUnit );
2675         break;
2676         default:
2677             maLabelData.mnCross = 1;
2678             ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
2679     }
2680 }
2681 
Save(XclExpStream & rStrm)2682 void XclExpChLabelRange::Save( XclExpStream& rStrm )
2683 {
2684     // the CHLABELRANGE record
2685     XclExpRecord::Save( rStrm );
2686 
2687     // the CHDATERANGE record with date axis settings (BIFF8 only)
2688     if( GetBiff() != EXC_BIFF8 )
2689         return;
2690 
2691     rStrm.StartRecord( EXC_ID_CHDATERANGE, 18 );
2692     rStrm   << maDateData.mnMinDate
2693             << maDateData.mnMaxDate
2694             << maDateData.mnMajorStep
2695             << maDateData.mnMajorUnit
2696             << maDateData.mnMinorStep
2697             << maDateData.mnMinorUnit
2698             << maDateData.mnBaseUnit
2699             << maDateData.mnCross
2700             << maDateData.mnFlags;
2701     rStrm.EndRecord();
2702 }
2703 
WriteBody(XclExpStream & rStrm)2704 void XclExpChLabelRange::WriteBody( XclExpStream& rStrm )
2705 {
2706     rStrm << maLabelData.mnCross << maLabelData.mnLabelFreq << maLabelData.mnTickFreq << maLabelData.mnFlags;
2707 }
2708 
XclExpChValueRange(const XclExpChRoot & rRoot)2709 XclExpChValueRange::XclExpChValueRange( const XclExpChRoot& rRoot ) :
2710     XclExpRecord( EXC_ID_CHVALUERANGE, 42 ),
2711     XclExpChRoot( rRoot )
2712 {
2713 }
2714 
Convert(const ScaleData & rScaleData)2715 void XclExpChValueRange::Convert( const ScaleData& rScaleData )
2716 {
2717     // scaling algorithm
2718     bool bLogScale = ScfApiHelper::GetServiceName( rScaleData.Scaling ) == "com.sun.star.chart2.LogarithmicScaling";
2719     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, bLogScale );
2720 
2721     // min/max
2722     bool bAutoMin = lclIsAutoAnyOrGetScaledValue( maData.mfMin, rScaleData.Minimum, bLogScale );
2723     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN, bAutoMin );
2724     bool bAutoMax = lclIsAutoAnyOrGetScaledValue( maData.mfMax, rScaleData.Maximum, bLogScale );
2725     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX, bAutoMax );
2726 
2727     // origin
2728     bool bAutoCross = lclIsAutoAnyOrGetScaledValue( maData.mfCross, rScaleData.Origin, bLogScale );
2729     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, bAutoCross );
2730 
2731     // major increment
2732     const IncrementData& rIncrementData = rScaleData.IncrementData;
2733     const bool bAutoMajor = lclIsAutoAnyOrGetValue( maData.mfMajorStep, rIncrementData.Distance ) || (maData.mfMajorStep <= 0.0);
2734     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAJOR, bAutoMajor );
2735     // minor increment
2736     const Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements;
2737     sal_Int32 nCount = 0;
2738 
2739     // tdf#114168 If IntervalCount is 5, then enable automatic minor calculation.
2740     // During import, if minorUnit is set and majorUnit not, then it is impossible
2741     // to calculate IntervalCount.
2742     const bool bAutoMinor = bLogScale || bAutoMajor || !rSubIncrementSeq.hasElements() ||
2743         lclIsAutoAnyOrGetValue( nCount, rSubIncrementSeq[ 0 ].IntervalCount ) || (nCount < 1) || (nCount == 5);
2744 
2745     if( maData.mfMajorStep && !bAutoMinor )
2746         maData.mfMinorStep = maData.mfMajorStep / nCount;
2747     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMINOR, bAutoMinor );
2748 
2749     // reverse order
2750     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE, rScaleData.Orientation == cssc2::AxisOrientation_REVERSE );
2751 }
2752 
ConvertAxisPosition(const ScfPropertySet & rPropSet)2753 void XclExpChValueRange::ConvertAxisPosition( const ScfPropertySet& rPropSet )
2754 {
2755     cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE;
2756     double fCrossingPos = 0.0;
2757     if( !(rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION ) && rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE )) )
2758         return;
2759 
2760     switch( eAxisPos )
2761     {
2762         case cssc::ChartAxisPosition_ZERO:
2763         case cssc::ChartAxisPosition_START:
2764             ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS );
2765         break;
2766         case cssc::ChartAxisPosition_END:
2767             ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_MAXCROSS );
2768         break;
2769         case cssc::ChartAxisPosition_VALUE:
2770             ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, false );
2771             maData.mfCross = ::get_flagvalue< double >( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, log( fCrossingPos ) / log( 10.0 ), fCrossingPos );
2772         break;
2773         default:
2774             ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS );
2775     }
2776 }
2777 
WriteBody(XclExpStream & rStrm)2778 void XclExpChValueRange::WriteBody( XclExpStream& rStrm )
2779 {
2780     rStrm   << maData.mfMin
2781             << maData.mfMax
2782             << maData.mfMajorStep
2783             << maData.mfMinorStep
2784             << maData.mfCross
2785             << maData.mnFlags;
2786 }
2787 
2788 namespace {
2789 
lclGetXclTickPos(sal_Int32 nApiTickmarks)2790 sal_uInt8 lclGetXclTickPos( sal_Int32 nApiTickmarks )
2791 {
2792     using namespace cssc2::TickmarkStyle;
2793     sal_uInt8 nXclTickPos = 0;
2794     ::set_flag( nXclTickPos, EXC_CHTICK_INSIDE,  ::get_flag( nApiTickmarks, INNER ) );
2795     ::set_flag( nXclTickPos, EXC_CHTICK_OUTSIDE, ::get_flag( nApiTickmarks, OUTER ) );
2796     return nXclTickPos;
2797 }
2798 
2799 } // namespace
2800 
XclExpChTick(const XclExpChRoot & rRoot)2801 XclExpChTick::XclExpChTick( const XclExpChRoot& rRoot ) :
2802     XclExpRecord( EXC_ID_CHTICK, (rRoot.GetBiff() == EXC_BIFF8) ? 30 : 26 ),
2803     XclExpChRoot( rRoot ),
2804     mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
2805 {
2806 }
2807 
Convert(const ScfPropertySet & rPropSet,const XclChExtTypeInfo & rTypeInfo,sal_uInt16 nAxisType)2808 void XclExpChTick::Convert( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nAxisType )
2809 {
2810     // tick mark style
2811     sal_Int32 nApiTickmarks = 0;
2812     if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MAJORTICKS ) )
2813         maData.mnMajor = lclGetXclTickPos( nApiTickmarks );
2814     if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MINORTICKS ) )
2815         maData.mnMinor = lclGetXclTickPos( nApiTickmarks );
2816 
2817     // axis labels
2818     if( (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) && (nAxisType == EXC_CHAXIS_X) )
2819     {
2820         /*  Radar charts disable their category labels via chart type, not via
2821             axis, and axis labels are always 'near axis'. */
2822         maData.mnLabelPos = EXC_CHTICK_NEXT;
2823     }
2824     else if( !rPropSet.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS ) )
2825     {
2826         // no labels
2827         maData.mnLabelPos = EXC_CHTICK_NOLABEL;
2828     }
2829     else if( rTypeInfo.mb3dChart && (nAxisType == EXC_CHAXIS_Y) )
2830     {
2831         // Excel expects 'near axis' at Y axes in 3D charts
2832         maData.mnLabelPos = EXC_CHTICK_NEXT;
2833     }
2834     else
2835     {
2836         cssc::ChartAxisLabelPosition eApiLabelPos = cssc::ChartAxisLabelPosition_NEAR_AXIS;
2837         rPropSet.GetProperty( eApiLabelPos, EXC_CHPROP_LABELPOSITION );
2838         switch( eApiLabelPos )
2839         {
2840             case cssc::ChartAxisLabelPosition_NEAR_AXIS:
2841             case cssc::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE: maData.mnLabelPos = EXC_CHTICK_NEXT;    break;
2842             case cssc::ChartAxisLabelPosition_OUTSIDE_START:        maData.mnLabelPos = EXC_CHTICK_LOW;     break;
2843             case cssc::ChartAxisLabelPosition_OUTSIDE_END:          maData.mnLabelPos = EXC_CHTICK_HIGH;    break;
2844             default:                                                maData.mnLabelPos = EXC_CHTICK_NEXT;
2845         }
2846     }
2847 }
2848 
SetFontColor(const Color & rColor,sal_uInt32 nColorId)2849 void XclExpChTick::SetFontColor( const Color& rColor, sal_uInt32 nColorId )
2850 {
2851     maData.maTextColor = rColor;
2852     ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOCOLOR, rColor == COL_AUTO );
2853     mnTextColorId = nColorId;
2854 }
2855 
SetRotation(sal_uInt16 nRotation)2856 void XclExpChTick::SetRotation( sal_uInt16 nRotation )
2857 {
2858     maData.mnRotation = nRotation;
2859     ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOROT, false );
2860     ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 2, 3 );
2861 }
2862 
WriteBody(XclExpStream & rStrm)2863 void XclExpChTick::WriteBody( XclExpStream& rStrm )
2864 {
2865     rStrm   << maData.mnMajor
2866             << maData.mnMinor
2867             << maData.mnLabelPos
2868             << maData.mnBackMode;
2869     rStrm.WriteZeroBytes( 16 );
2870     rStrm   << maData.maTextColor
2871             << maData.mnFlags;
2872     if( GetBiff() == EXC_BIFF8 )
2873         rStrm << GetPalette().GetColorIndex( mnTextColorId ) << maData.mnRotation;
2874 }
2875 
2876 namespace {
2877 
2878 /** Returns an API axis object from the passed coordinate system. */
lclGetApiAxis(Reference<XCoordinateSystem> const & xCoordSystem,sal_Int32 nApiAxisDim,sal_Int32 nApiAxesSetIdx)2879 Reference< XAxis > lclGetApiAxis( Reference< XCoordinateSystem > const & xCoordSystem,
2880         sal_Int32 nApiAxisDim, sal_Int32 nApiAxesSetIdx )
2881 {
2882     Reference< XAxis > xAxis;
2883     if( (nApiAxisDim >= 0) && xCoordSystem.is() ) try
2884     {
2885         xAxis = xCoordSystem->getAxisByDimension( nApiAxisDim, nApiAxesSetIdx );
2886     }
2887     catch( Exception& )
2888     {
2889     }
2890     return xAxis;
2891 }
2892 
lclGetApiChart1Axis(Reference<XChartDocument> const & xChartDoc,sal_Int32 nApiAxisDim,sal_Int32 nApiAxesSetIdx)2893 Reference< cssc::XAxis > lclGetApiChart1Axis( Reference< XChartDocument > const & xChartDoc,
2894         sal_Int32 nApiAxisDim, sal_Int32 nApiAxesSetIdx )
2895 {
2896     Reference< cssc::XAxis > xChart1Axis;
2897     try
2898     {
2899         Reference< cssc::XChartDocument > xChart1Doc( xChartDoc, UNO_QUERY_THROW );
2900         Reference< cssc::XAxisSupplier > xChart1AxisSupp( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
2901         switch( nApiAxesSetIdx )
2902         {
2903             case EXC_CHART_AXESSET_PRIMARY:
2904                 xChart1Axis = xChart1AxisSupp->getAxis( nApiAxisDim );
2905             break;
2906             case EXC_CHART_AXESSET_SECONDARY:
2907                 xChart1Axis = xChart1AxisSupp->getSecondaryAxis( nApiAxisDim );
2908             break;
2909         }
2910     }
2911     catch( Exception& )
2912     {
2913     }
2914     return xChart1Axis;
2915 }
2916 
2917 } // namespace
2918 
XclExpChAxis(const XclExpChRoot & rRoot,sal_uInt16 nAxisType)2919 XclExpChAxis::XclExpChAxis( const XclExpChRoot& rRoot, sal_uInt16 nAxisType ) :
2920     XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXIS, EXC_ID_CHAXIS, 18 ),
2921     mnNumFmtIdx( EXC_FORMAT_NOTFOUND )
2922 {
2923     maData.mnType = nAxisType;
2924 }
2925 
SetFont(XclExpChFontRef xFont,const Color & rColor,sal_uInt32 nColorId)2926 void XclExpChAxis::SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId )
2927 {
2928     mxFont = xFont;
2929     if( mxTick )
2930         mxTick->SetFontColor( rColor, nColorId );
2931 }
2932 
SetRotation(sal_uInt16 nRotation)2933 void XclExpChAxis::SetRotation( sal_uInt16 nRotation )
2934 {
2935     if( mxTick )
2936         mxTick->SetRotation( nRotation );
2937 }
2938 
Convert(Reference<XAxis> const & xAxis,Reference<XAxis> const & xCrossingAxis,Reference<cssc::XAxis> const & xChart1Axis,const XclChExtTypeInfo & rTypeInfo)2939 void XclExpChAxis::Convert( Reference< XAxis > const & xAxis, Reference< XAxis > const & xCrossingAxis,
2940         Reference< cssc::XAxis > const & xChart1Axis, const XclChExtTypeInfo& rTypeInfo )
2941 {
2942     ScfPropertySet aAxisProp( xAxis );
2943     bool bCategoryAxis = ((GetAxisType() == EXC_CHAXIS_X) && rTypeInfo.mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z);
2944 
2945     // axis line format -------------------------------------------------------
2946 
2947     mxAxisLine = new XclExpChLineFormat( GetChRoot() );
2948     mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE );
2949     // #i58688# axis enabled
2950     mxAxisLine->SetShowAxis( aAxisProp.GetBoolProperty( EXC_CHPROP_SHOW ) );
2951 
2952     // axis scaling and increment ---------------------------------------------
2953 
2954     ScfPropertySet aCrossingProp( xCrossingAxis );
2955     if( bCategoryAxis )
2956     {
2957         mxLabelRange = new XclExpChLabelRange( GetChRoot() );
2958         mxLabelRange->SetTicksBetweenCateg( rTypeInfo.mbTicksBetweenCateg );
2959         if( xAxis.is() )
2960         {
2961             ScfPropertySet aChart1AxisProp( xChart1Axis );
2962             // #i71684# radar charts have reversed rotation direction
2963             mxLabelRange->Convert( xAxis->getScaleData(), aChart1AxisProp, (GetAxisType() == EXC_CHAXIS_X) && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) );
2964         }
2965         // get position of crossing axis on this axis from passed axis object
2966         if( aCrossingProp.Is() )
2967             mxLabelRange->ConvertAxisPosition( aCrossingProp );
2968     }
2969     else
2970     {
2971         mxValueRange = new XclExpChValueRange( GetChRoot() );
2972         if( xAxis.is() )
2973             mxValueRange->Convert( xAxis->getScaleData() );
2974         // get position of crossing axis on this axis from passed axis object
2975         if( aCrossingProp.Is() )
2976             mxValueRange->ConvertAxisPosition( aCrossingProp );
2977     }
2978 
2979     // axis caption text ------------------------------------------------------
2980 
2981     // axis ticks properties
2982     mxTick = new XclExpChTick( GetChRoot() );
2983     mxTick->Convert( aAxisProp, rTypeInfo, GetAxisType() );
2984 
2985     // axis label formatting and rotation
2986     ConvertFontBase( GetChRoot(), aAxisProp );
2987     ConvertRotationBase( aAxisProp, true );
2988 
2989     // axis number format
2990     sal_Int32 nApiNumFmt = 0;
2991     if( !bCategoryAxis && aAxisProp.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) )
2992     {
2993         bool bLinkNumberFmtToSource = false;
2994         if ( !aAxisProp.GetProperty( bLinkNumberFmtToSource, EXC_CHPROP_NUMBERFORMAT_LINKSRC ) || !bLinkNumberFmtToSource )
2995             mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) );
2996     }
2997 
2998     // grid -------------------------------------------------------------------
2999 
3000     if( !xAxis.is() )
3001         return;
3002 
3003     // main grid
3004     ScfPropertySet aGridProp( xAxis->getGridProperties() );
3005     if( aGridProp.GetBoolProperty( EXC_CHPROP_SHOW ) )
3006         mxMajorGrid = lclCreateLineFormat( GetChRoot(), aGridProp, EXC_CHOBJTYPE_GRIDLINE );
3007     // sub grid
3008     Sequence< Reference< XPropertySet > > aSubGridPropSeq = xAxis->getSubGridProperties();
3009     if( aSubGridPropSeq.hasElements() )
3010     {
3011         ScfPropertySet aSubGridProp( aSubGridPropSeq[ 0 ] );
3012         if( aSubGridProp.GetBoolProperty( EXC_CHPROP_SHOW ) )
3013             mxMinorGrid = lclCreateLineFormat( GetChRoot(), aSubGridProp, EXC_CHOBJTYPE_GRIDLINE );
3014     }
3015 }
3016 
ConvertWall(css::uno::Reference<css::chart2::XDiagram> const & xDiagram)3017 void XclExpChAxis::ConvertWall( css::uno::Reference< css::chart2::XDiagram > const & xDiagram )
3018 {
3019     if( !xDiagram.is() )
3020         return;
3021 
3022     switch( GetAxisType() )
3023     {
3024     case EXC_CHAXIS_X:
3025     {
3026         ScfPropertySet aWallProp( xDiagram->getWall() );
3027         mxWallFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_WALL3D );
3028     }
3029     break;
3030     case EXC_CHAXIS_Y:
3031     {
3032         ScfPropertySet aFloorProp( xDiagram->getFloor() );
3033         mxWallFrame = lclCreateFrame( GetChRoot(), aFloorProp, EXC_CHOBJTYPE_FLOOR3D );
3034     }
3035     break;
3036     default:
3037         mxWallFrame.clear();
3038     }
3039 }
3040 
WriteSubRecords(XclExpStream & rStrm)3041 void XclExpChAxis::WriteSubRecords( XclExpStream& rStrm )
3042 {
3043     lclSaveRecord( rStrm, mxLabelRange );
3044     lclSaveRecord( rStrm, mxValueRange );
3045     if( mnNumFmtIdx != EXC_FORMAT_NOTFOUND )
3046         XclExpUInt16Record( EXC_ID_CHFORMAT, mnNumFmtIdx ).Save( rStrm );
3047     lclSaveRecord( rStrm, mxTick );
3048     lclSaveRecord( rStrm, mxFont );
3049     lclSaveRecord( rStrm, mxAxisLine, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_AXISLINE );
3050     lclSaveRecord( rStrm, mxMajorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MAJORGRID );
3051     lclSaveRecord( rStrm, mxMinorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MINORGRID );
3052     lclSaveRecord( rStrm, mxWallFrame, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_WALLS );
3053 }
3054 
WriteBody(XclExpStream & rStrm)3055 void XclExpChAxis::WriteBody( XclExpStream& rStrm )
3056 {
3057     rStrm << maData.mnType;
3058     rStrm.WriteZeroBytes( 16 );
3059 }
3060 
XclExpChAxesSet(const XclExpChRoot & rRoot,sal_uInt16 nAxesSetId)3061 XclExpChAxesSet::XclExpChAxesSet( const XclExpChRoot& rRoot, sal_uInt16 nAxesSetId ) :
3062     XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXESSET, EXC_ID_CHAXESSET, 18 )
3063 {
3064     maData.mnAxesSetId = nAxesSetId;
3065     SetFutureRecordContext( 0, nAxesSetId );
3066 
3067     /*  Need to set a reasonable size for the plot area, otherwise Excel will
3068         move away embedded shapes while auto-sizing the plot area. This is just
3069         a wild guess, but will be fixed with implementing manual positioning of
3070         chart elements. */
3071     maData.maRect.mnX = 262;
3072     maData.maRect.mnY = 626;
3073     maData.maRect.mnWidth = 3187;
3074     maData.maRect.mnHeight = 2633;
3075 }
3076 
Convert(Reference<XDiagram> const & xDiagram,sal_uInt16 nFirstGroupIdx)3077 sal_uInt16 XclExpChAxesSet::Convert( Reference< XDiagram > const & xDiagram, sal_uInt16 nFirstGroupIdx )
3078 {
3079     /*  First unused chart type group index is passed to be able to continue
3080         counting of chart type groups for secondary axes set. */
3081     sal_uInt16 nGroupIdx = nFirstGroupIdx;
3082     Reference< XCoordinateSystemContainer > xCoordSysCont( xDiagram, UNO_QUERY );
3083     if( xCoordSysCont.is() )
3084     {
3085         Sequence< Reference< XCoordinateSystem > > aCoordSysSeq = xCoordSysCont->getCoordinateSystems();
3086         if( aCoordSysSeq.hasElements() )
3087         {
3088             /*  Process first coordinate system only. Import filter puts all
3089                 chart types into one coordinate system. */
3090             Reference< XCoordinateSystem > xCoordSystem = aCoordSysSeq[ 0 ];
3091             sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
3092 
3093             // 3d mode
3094             bool b3dChart = xCoordSystem.is() && (xCoordSystem->getDimension() == 3);
3095 
3096             // percent charts
3097             namespace ApiAxisType = cssc2::AxisType;
3098             Reference< XAxis > xApiYAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_Y, nApiAxesSetIdx );
3099             bool bPercent = xApiYAxis.is() && (xApiYAxis->getScaleData().AxisType == ApiAxisType::PERCENT);
3100 
3101             // connector lines in bar charts
3102             ScfPropertySet aDiaProp( xDiagram );
3103             bool bConnectBars = aDiaProp.GetBoolProperty( EXC_CHPROP_CONNECTBARS );
3104 
3105             // swapped axes sets
3106             ScfPropertySet aCoordSysProp( xCoordSystem );
3107             bool bSwappedAxesSet = aCoordSysProp.GetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS );
3108 
3109             // X axis for later use
3110             Reference< XAxis > xApiXAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_X, nApiAxesSetIdx );
3111             // X axis labels
3112             ScfPropertySet aXAxisProp( xApiXAxis );
3113             bool bHasXLabels = aXAxisProp.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS );
3114 
3115             // process chart types
3116             Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY );
3117             if( xChartTypeCont.is() )
3118             {
3119                 const Sequence< Reference< XChartType > > aChartTypeSeq = xChartTypeCont->getChartTypes();
3120                 for( const Reference< XChartType >& rChartType : aChartTypeSeq )
3121                 {
3122                     XclExpChTypeGroupRef xTypeGroup = new XclExpChTypeGroup( GetChRoot(), nGroupIdx );
3123                     xTypeGroup->ConvertType( xDiagram, rChartType, nApiAxesSetIdx, b3dChart, bSwappedAxesSet, bHasXLabels );
3124                     /*  If new chart type group cannot be inserted into a combination
3125                         chart with existing type groups, insert all series into last
3126                         contained chart type group instead of creating a new group. */
3127                     XclExpChTypeGroupRef xLastGroup = GetLastTypeGroup();
3128                     if( xLastGroup && !(xTypeGroup->IsCombinable2d() && xLastGroup->IsCombinable2d()) )
3129                     {
3130                         xLastGroup->ConvertSeries( xDiagram, rChartType, nApiAxesSetIdx, bPercent, bConnectBars );
3131                     }
3132                     else
3133                     {
3134                         xTypeGroup->ConvertSeries( xDiagram, rChartType, nApiAxesSetIdx, bPercent, bConnectBars );
3135                         if( xTypeGroup->IsValidGroup() )
3136                         {
3137                             maTypeGroups.AppendRecord( xTypeGroup );
3138                             ++nGroupIdx;
3139                         }
3140                     }
3141                 }
3142             }
3143 
3144             if( XclExpChTypeGroup* pGroup = GetFirstTypeGroup().get() )
3145             {
3146                 const XclChExtTypeInfo& rTypeInfo = pGroup->GetTypeInfo();
3147 
3148                 // create axes according to chart type (no axes for pie and donut charts)
3149                 if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE )
3150                 {
3151                     ConvertAxis( mxXAxis, EXC_CHAXIS_X, mxXAxisTitle, EXC_CHOBJLINK_XAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_Y );
3152                     ConvertAxis( mxYAxis, EXC_CHAXIS_Y, mxYAxisTitle, EXC_CHOBJLINK_YAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_X );
3153                     if( pGroup->Is3dDeepChart() )
3154                         ConvertAxis( mxZAxis, EXC_CHAXIS_Z, mxZAxisTitle, EXC_CHOBJLINK_ZAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_NONE );
3155                 }
3156 
3157                 // X axis category ranges
3158                 if( rTypeInfo.mbCategoryAxis && xApiXAxis.is() )
3159                 {
3160                     const ScaleData aScaleData = xApiXAxis->getScaleData();
3161                     for( size_t nIdx = 0, nSize = maTypeGroups.GetSize(); nIdx < nSize; ++nIdx )
3162                         maTypeGroups.GetRecord( nIdx )->ConvertCategSequence( aScaleData.Categories );
3163                 }
3164 
3165                 // legend
3166                 if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) )
3167                 {
3168                     Reference< XLegend > xLegend = xDiagram->getLegend();
3169                     if( xLegend.is() )
3170                     {
3171                         ScfPropertySet aLegendProp( xLegend );
3172                         pGroup->ConvertLegend( aLegendProp );
3173                     }
3174                 }
3175             }
3176         }
3177     }
3178 
3179     // wall/floor/diagram frame formatting
3180     if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) )
3181     {
3182         XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
3183         if( xTypeGroup && xTypeGroup->Is3dWallChart() )
3184         {
3185             // wall/floor formatting (3D charts)
3186             if( mxXAxis )
3187                 mxXAxis->ConvertWall( xDiagram );
3188             if( mxYAxis )
3189                 mxYAxis->ConvertWall( xDiagram );
3190         }
3191         else
3192         {
3193             // diagram background formatting
3194             ScfPropertySet aWallProp( xDiagram->getWall() );
3195             mxPlotFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_PLOTFRAME );
3196         }
3197     }
3198 
3199     // inner and outer plot area position and size
3200     try
3201     {
3202         Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
3203         Reference< cssc::XDiagramPositioning > xPositioning( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
3204         // set manual flag in chart data
3205         if( !xPositioning->isAutomaticDiagramPositioning() )
3206             GetChartData().SetManualPlotArea();
3207         // the CHAXESSET record contains the inner plot area
3208         maData.maRect = CalcChartRectFromHmm( xPositioning->calculateDiagramPositionExcludingAxes() );
3209         // the embedded CHFRAMEPOS record contains the outer plot area
3210         mxFramePos = new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT );
3211         // for pie charts, always use inner plot area size to exclude the data labels as Excel does
3212         const XclExpChTypeGroup* pFirstTypeGroup = GetFirstTypeGroup().get();
3213         bool bPieChart = pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE);
3214         mxFramePos->GetFramePosData().maRect = bPieChart ? maData.maRect :
3215             CalcChartRectFromHmm( xPositioning->calculateDiagramPositionIncludingAxes() );
3216     }
3217     catch( Exception& )
3218     {
3219     }
3220 
3221     // return first unused chart type group index for next axes set
3222     return nGroupIdx;
3223 }
3224 
Is3dChart() const3225 bool XclExpChAxesSet::Is3dChart() const
3226 {
3227     XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
3228     return xTypeGroup && xTypeGroup->Is3dChart();
3229 }
3230 
WriteSubRecords(XclExpStream & rStrm)3231 void XclExpChAxesSet::WriteSubRecords( XclExpStream& rStrm )
3232 {
3233     lclSaveRecord( rStrm, mxFramePos );
3234     lclSaveRecord( rStrm, mxXAxis );
3235     lclSaveRecord( rStrm, mxYAxis );
3236     lclSaveRecord( rStrm, mxZAxis );
3237     lclSaveRecord( rStrm, mxXAxisTitle );
3238     lclSaveRecord( rStrm, mxYAxisTitle );
3239     lclSaveRecord( rStrm, mxZAxisTitle );
3240     if( mxPlotFrame )
3241     {
3242         XclExpEmptyRecord( EXC_ID_CHPLOTFRAME ).Save( rStrm );
3243         mxPlotFrame->Save( rStrm );
3244     }
3245     maTypeGroups.Save( rStrm );
3246 }
3247 
GetFirstTypeGroup() const3248 XclExpChTypeGroupRef XclExpChAxesSet::GetFirstTypeGroup() const
3249 {
3250     return maTypeGroups.GetFirstRecord();
3251 }
3252 
GetLastTypeGroup() const3253 XclExpChTypeGroupRef XclExpChAxesSet::GetLastTypeGroup() const
3254 {
3255     return maTypeGroups.GetLastRecord();
3256 }
3257 
ConvertAxis(XclExpChAxisRef & rxChAxis,sal_uInt16 nAxisType,XclExpChTextRef & rxChAxisTitle,sal_uInt16 nTitleTarget,Reference<XCoordinateSystem> const & xCoordSystem,const XclChExtTypeInfo & rTypeInfo,sal_Int32 nCrossingAxisDim)3258 void XclExpChAxesSet::ConvertAxis(
3259         XclExpChAxisRef& rxChAxis, sal_uInt16 nAxisType,
3260         XclExpChTextRef& rxChAxisTitle, sal_uInt16 nTitleTarget,
3261         Reference< XCoordinateSystem > const & xCoordSystem, const XclChExtTypeInfo& rTypeInfo,
3262         sal_Int32 nCrossingAxisDim )
3263 {
3264     // create and convert axis object
3265     rxChAxis = new XclExpChAxis( GetChRoot(), nAxisType );
3266     sal_Int32 nApiAxisDim = rxChAxis->GetApiAxisDimension();
3267     sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
3268     Reference< XAxis > xAxis = lclGetApiAxis( xCoordSystem, nApiAxisDim, nApiAxesSetIdx );
3269     Reference< XAxis > xCrossingAxis = lclGetApiAxis( xCoordSystem, nCrossingAxisDim, nApiAxesSetIdx );
3270     Reference< cssc::XAxis > xChart1Axis = lclGetApiChart1Axis( GetChartDocument(), nApiAxisDim, nApiAxesSetIdx );
3271     rxChAxis->Convert( xAxis, xCrossingAxis, xChart1Axis, rTypeInfo );
3272 
3273     // create and convert axis title
3274     Reference< XTitled > xTitled( xAxis, UNO_QUERY );
3275     rxChAxisTitle = lclCreateTitle( GetChRoot(), xTitled, nTitleTarget );
3276 }
3277 
WriteBody(XclExpStream & rStrm)3278 void XclExpChAxesSet::WriteBody( XclExpStream& rStrm )
3279 {
3280     rStrm << maData.mnAxesSetId << maData.maRect;
3281 }
3282 
3283 // The chart object ===========================================================
3284 
lcl_getChartSubTitle(const Reference<XChartDocument> & xChartDoc,OUString & rSubTitle)3285 static void lcl_getChartSubTitle(const Reference<XChartDocument>& xChartDoc,
3286                                  OUString& rSubTitle)
3287 {
3288     Reference< css::chart::XChartDocument > xChartDoc1(xChartDoc, UNO_QUERY);
3289     if (!xChartDoc1.is())
3290         return;
3291 
3292     Reference< XPropertySet > xProp(xChartDoc1->getSubTitle(), UNO_QUERY);
3293     if (!xProp.is())
3294         return;
3295 
3296     OUString aTitle;
3297     Any any = xProp->getPropertyValue("String");
3298     if (any >>= aTitle)
3299         rSubTitle = aTitle;
3300 }
3301 
XclExpChChart(const XclExpRoot & rRoot,Reference<XChartDocument> const & xChartDoc,const tools::Rectangle & rChartRect)3302 XclExpChChart::XclExpChChart( const XclExpRoot& rRoot,
3303         Reference< XChartDocument > const & xChartDoc, const tools::Rectangle& rChartRect ) :
3304     XclExpChGroupBase( XclExpChRoot( rRoot, *this ), EXC_CHFRBLOCK_TYPE_CHART, EXC_ID_CHCHART, 16 )
3305 {
3306     Size aPtSize = OutputDevice::LogicToLogic( rChartRect.GetSize(), MapMode( MapUnit::Map100thMM ), MapMode( MapUnit::MapPoint ) );
3307     // rectangle is stored in 16.16 fixed-point format
3308     maRect.mnX = maRect.mnY = 0;
3309     maRect.mnWidth = static_cast< sal_Int32 >( aPtSize.Width() << 16 );
3310     maRect.mnHeight = static_cast< sal_Int32 >( aPtSize.Height() << 16 );
3311 
3312     // global chart properties (default values)
3313     ::set_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY, false );
3314     ::set_flag( maProps.mnFlags, EXC_CHPROPS_MANPLOTAREA );
3315     maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP;
3316 
3317     // always create both axes set objects
3318     mxPrimAxesSet = std::make_shared<XclExpChAxesSet>( GetChRoot(), EXC_CHAXESSET_PRIMARY );
3319     mxSecnAxesSet = std::make_shared<XclExpChAxesSet>( GetChRoot(), EXC_CHAXESSET_SECONDARY );
3320 
3321     if( !xChartDoc.is() )
3322         return;
3323 
3324     Reference< XDiagram > xDiagram = xChartDoc->getFirstDiagram();
3325 
3326     // global chart properties (only 'include hidden cells' attribute for now)
3327     ScfPropertySet aDiagramProp( xDiagram );
3328     bool bIncludeHidden = aDiagramProp.GetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS );
3329     ::set_flag( maProps.mnFlags,  EXC_CHPROPS_SHOWVISIBLEONLY, !bIncludeHidden );
3330 
3331     // initialize API conversion (remembers xChartDoc and rChartRect internally)
3332     InitConversion( xChartDoc, rChartRect );
3333 
3334     // chart frame
3335     ScfPropertySet aFrameProp( xChartDoc->getPageBackground() );
3336     mxFrame = lclCreateFrame( GetChRoot(), aFrameProp, EXC_CHOBJTYPE_BACKGROUND );
3337 
3338     // chart title
3339     Reference< XTitled > xTitled( xChartDoc, UNO_QUERY );
3340     OUString aSubTitle;
3341     lcl_getChartSubTitle(xChartDoc, aSubTitle);
3342     mxTitle = lclCreateTitle( GetChRoot(), xTitled, EXC_CHOBJLINK_TITLE,
3343                               !aSubTitle.isEmpty() ? &aSubTitle : nullptr );
3344 
3345     // diagrams (axes sets)
3346     sal_uInt16 nFreeGroupIdx = mxPrimAxesSet->Convert( xDiagram, 0 );
3347     if( !mxPrimAxesSet->Is3dChart() )
3348         mxSecnAxesSet->Convert( xDiagram, nFreeGroupIdx );
3349 
3350     // treatment of missing values
3351     ScfPropertySet aDiaProp( xDiagram );
3352     sal_Int32 nMissingValues = 0;
3353     if( aDiaProp.GetProperty( nMissingValues, EXC_CHPROP_MISSINGVALUETREATMENT ) )
3354     {
3355         using namespace cssc::MissingValueTreatment;
3356         switch( nMissingValues )
3357         {
3358             case LEAVE_GAP: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP;           break;
3359             case USE_ZERO:  maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_ZERO;           break;
3360             case CONTINUE:  maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_INTERPOLATE;    break;
3361         }
3362     }
3363 
3364     // finish API conversion
3365     FinishConversion();
3366 }
3367 
CreateSeries()3368 XclExpChSeriesRef XclExpChChart::CreateSeries()
3369 {
3370     XclExpChSeriesRef xSeries;
3371     sal_uInt16 nSeriesIdx = static_cast< sal_uInt16 >( maSeries.GetSize() );
3372     if( nSeriesIdx <= EXC_CHSERIES_MAXSERIES )
3373     {
3374         xSeries = new XclExpChSeries( GetChRoot(), nSeriesIdx );
3375         maSeries.AppendRecord( xSeries );
3376     }
3377     return xSeries;
3378 }
3379 
RemoveLastSeries()3380 void XclExpChChart::RemoveLastSeries()
3381 {
3382     if( !maSeries.IsEmpty() )
3383         maSeries.RemoveRecord( maSeries.GetSize() - 1 );
3384 }
3385 
SetDataLabel(XclExpChTextRef const & xText)3386 void XclExpChChart::SetDataLabel( XclExpChTextRef const & xText )
3387 {
3388     if( xText )
3389         maLabels.AppendRecord( xText );
3390 }
3391 
SetManualPlotArea()3392 void XclExpChChart::SetManualPlotArea()
3393 {
3394     // this flag does not exist in BIFF5
3395     if( GetBiff() == EXC_BIFF8 )
3396         ::set_flag( maProps.mnFlags, EXC_CHPROPS_USEMANPLOTAREA );
3397 }
3398 
WriteSubRecords(XclExpStream & rStrm)3399 void XclExpChChart::WriteSubRecords( XclExpStream& rStrm )
3400 {
3401     // background format
3402     lclSaveRecord( rStrm, mxFrame );
3403 
3404     // data series
3405     maSeries.Save( rStrm );
3406 
3407     // CHPROPERTIES record
3408     rStrm.StartRecord( EXC_ID_CHPROPERTIES, 4 );
3409     rStrm << maProps.mnFlags << maProps.mnEmptyMode << sal_uInt8( 0 );
3410     rStrm.EndRecord();
3411 
3412     // axes sets (always save primary axes set)
3413     sal_uInt16 nUsedAxesSets = mxSecnAxesSet->IsValidAxesSet() ? 2 : 1;
3414     XclExpUInt16Record( EXC_ID_CHUSEDAXESSETS, nUsedAxesSets ).Save( rStrm );
3415     mxPrimAxesSet->Save( rStrm );
3416     if( mxSecnAxesSet->IsValidAxesSet() )
3417         mxSecnAxesSet->Save( rStrm );
3418 
3419     // chart title and data labels
3420     lclSaveRecord( rStrm, mxTitle );
3421     maLabels.Save( rStrm );
3422 }
3423 
WriteBody(XclExpStream & rStrm)3424 void XclExpChChart::WriteBody( XclExpStream& rStrm )
3425 {
3426      rStrm << maRect;
3427 }
3428 
XclExpChartDrawing(const XclExpRoot & rRoot,const Reference<XModel> & rxModel,const Size & rChartSize)3429 XclExpChartDrawing::XclExpChartDrawing( const XclExpRoot& rRoot,
3430         const Reference< XModel >& rxModel, const Size& rChartSize ) :
3431     XclExpRoot( rRoot )
3432 {
3433     if( rChartSize.IsEmpty() )
3434         return;
3435 
3436     ScfPropertySet aPropSet( rxModel );
3437     Reference< XShapes > xShapes;
3438     if( !(aPropSet.GetProperty( xShapes, EXC_CHPROP_ADDITIONALSHAPES ) && xShapes.is() && (xShapes->getCount() > 0)) )
3439         return;
3440 
3441     /*  Create a new independent object manager with own DFF stream for the
3442         DGCONTAINER, pass global manager as parent for shared usage of
3443         global DFF data (picture container etc.). */
3444     mxObjMgr = std::make_shared<XclExpEmbeddedObjectManager>( GetObjectManager(), rChartSize, EXC_CHART_TOTALUNITS, EXC_CHART_TOTALUNITS );
3445     // initialize the drawing object list
3446     mxObjMgr->StartSheet();
3447     // process the draw page (convert all shapes)
3448     mxObjRecs = mxObjMgr->ProcessDrawing( xShapes );
3449     // finalize the DFF stream
3450     mxObjMgr->EndDocument();
3451 }
3452 
~XclExpChartDrawing()3453 XclExpChartDrawing::~XclExpChartDrawing()
3454 {
3455 }
3456 
Save(XclExpStream & rStrm)3457 void XclExpChartDrawing::Save( XclExpStream& rStrm )
3458 {
3459     if( mxObjRecs )
3460         mxObjRecs->Save( rStrm );
3461 }
3462 
XclExpChart(const XclExpRoot & rRoot,Reference<XModel> const & xModel,const tools::Rectangle & rChartRect)3463 XclExpChart::XclExpChart( const XclExpRoot& rRoot, Reference< XModel > const & xModel, const tools::Rectangle& rChartRect ) :
3464     XclExpSubStream( EXC_BOF_CHART ),
3465     XclExpRoot( rRoot )
3466 {
3467     AppendNewRecord( new XclExpChartPageSettings( rRoot ) );
3468     AppendNewRecord( new XclExpBoolRecord( EXC_ID_PROTECT, false ) );
3469     AppendNewRecord( new XclExpChartDrawing( rRoot, xModel, rChartRect.GetSize() ) );
3470     AppendNewRecord( new XclExpUInt16Record( EXC_ID_CHUNITS, EXC_CHUNITS_TWIPS ) );
3471 
3472     Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY );
3473     AppendNewRecord( new XclExpChChart( rRoot, xChartDoc, rChartRect ) );
3474 }
3475 
3476 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3477