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     {
258         // write the leading CHFRINFO record
259         if( maWrittenFrBlocks.empty() )
260         {
261             rStrm.StartRecord( EXC_ID_CHFRINFO, 20 );
262             rStrm << EXC_ID_CHFRINFO << EXC_FUTUREREC_EMPTYFLAGS << EXC_CHFRINFO_EXCELXP2003 << EXC_CHFRINFO_EXCELXP2003 << sal_uInt16( 3 );
263             rStrm << sal_uInt16( 0x0850 ) << sal_uInt16( 0x085A ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x0861 ) << sal_uInt16( 0x086A ) << sal_uInt16( 0x086B );
264             rStrm.EndRecord();
265         }
266         // write all unwritten CHFRBLOCKBEGIN records
267         for( const auto& rUnwrittenFrBlock : maUnwrittenFrBlocks )
268         {
269             OSL_ENSURE( rUnwrittenFrBlock.mnType != EXC_CHFRBLOCK_TYPE_UNKNOWN, "XclExpChRootData::InitializeFutureRecBlock - unknown future record block type" );
270             lclWriteChFrBlockRecord( rStrm, rUnwrittenFrBlock, true );
271         }
272         // move all record infos to vector of written blocks
273         maWrittenFrBlocks.insert( maWrittenFrBlocks.end(), maUnwrittenFrBlocks.begin(), maUnwrittenFrBlocks.end() );
274         maUnwrittenFrBlocks.clear();
275     }
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( new 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     {
441         // register the future record context corresponding to this record group
442         RegisterFutureRecBlock( maFrBlock );
443         // CHBEGIN record
444         XclExpEmptyRecord( EXC_ID_CHBEGIN ).Save( rStrm );
445         // embedded records
446         WriteSubRecords( rStrm );
447         // finalize the future records, must be done before the closing CHEND
448         FinalizeFutureRecBlock( rStrm );
449         // CHEND record
450         XclExpEmptyRecord( EXC_ID_CHEND ).Save( rStrm );
451     }
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.reset();
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.reset( new XclExpChLineFormat( rRoot ) );
735     mxLineFmt->Convert( rRoot, rPropSet, eObjType );
736     // area format (only for frame objects)
737     if( rRoot.GetFormatInfo( eObjType ).mbIsFrame )
738     {
739         mxAreaFmt.reset( new XclExpChAreaFormat( rRoot ) );
740         bool bComplexFill = mxAreaFmt->Convert( rRoot, rPropSet, eObjType );
741         if( (rRoot.GetBiff() == EXC_BIFF8) && bComplexFill )
742         {
743             mxEscherFmt.reset( new XclExpChEscherFormat( rRoot ) );
744             mxEscherFmt->Convert( rPropSet, eObjType );
745             if( mxEscherFmt->IsValid() )
746                 mxAreaFmt->SetAuto( false );
747             else
748                 mxEscherFmt.reset();
749         }
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.reset( new XclExpChLineFormat( rRoot ) );
758     mxLineFmt->SetDefault( eDefFrameType );
759     // area format (only for frame objects)
760     if( bIsFrame )
761     {
762         mxAreaFmt.reset( new XclExpChAreaFormat( rRoot ) );
763         mxAreaFmt->SetDefault( eDefFrameType );
764         mxEscherFmt.reset();
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.reset();
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;
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(ScAddress());
921                 ScAddress aAbs2 = rComplexRef.Ref2.toAbs(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.reset();
1181     mxObjLink.reset( 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.reset( 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.reset( 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.reset( 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.reset( 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.reset( 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.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE ) );
1354     mxSrcLink->ConvertNumFmt( rPropSet, false );
1355     // object link
1356     mxObjLink.reset( 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.reset();
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     {
1516         using namespace cssc2::DataPointGeometry3D;
1517         switch( nApiType )
1518         {
1519             case CUBOID:
1520                 maData.mnBase = EXC_CH3DDATAFORMAT_RECT;
1521                 maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT;
1522             break;
1523             case PYRAMID:
1524                 maData.mnBase = EXC_CH3DDATAFORMAT_RECT;
1525                 maData.mnTop = EXC_CH3DDATAFORMAT_SHARP;
1526             break;
1527             case CYLINDER:
1528                 maData.mnBase = EXC_CH3DDATAFORMAT_CIRC;
1529                 maData.mnTop = EXC_CH3DDATAFORMAT_STRAIGHT;
1530             break;
1531             case CONE:
1532                 maData.mnBase = EXC_CH3DDATAFORMAT_CIRC;
1533                 maData.mnTop = EXC_CH3DDATAFORMAT_SHARP;
1534             break;
1535             default:
1536                 OSL_FAIL( "XclExpCh3dDataFormat::Convert - unknown 3D bar format" );
1537         }
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.reset( 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.reset( 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.reset( new XclExpCh3dDataFormat );
1583         mx3dDataFmt->Convert( rPropSet );
1584     }
1585 
1586     // spline
1587     if( IsSeriesFormat() && rTypeInfo.mbSpline && !bIsFrame )
1588         mxSeriesFmt.reset( 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.reset( 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.reset( 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.reset( 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.reset( 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.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_TITLE ) );
1830     mxValueLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_VALUES ) );
1831     mxCategLink.reset( new XclExpChSourceLink( GetChRoot(), EXC_CHSRCLINK_CATEGORY ) );
1832     if( GetBiff() == EXC_BIFF8 )
1833         mxBubbleLink.reset( 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.reset( 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                         const OUString aFillStyleName = "FillStyle";
1916                         const OUString aColorName = "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,const OUString & rValueRole,sal_uInt16 nGroupIdx,sal_uInt16 nFormatIdx,bool bCloseSymbol)1955 bool XclExpChSeries::ConvertStockSeries( css::uno::Reference< css::chart2::XDataSeries > const & xDataSeries,
1956         const OUString& 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.reset( 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.reset( 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.reset( new XclExpChSerErrorBar( GetChRoot(), nBarId ) );
2020     bool bOk = mxErrorBar->Convert( *mxValueLink, maData.mnValueCount, rPropSet );
2021     if( bOk )
2022     {
2023         // error bar formatting
2024         mxSeriesFmt.reset( 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     {
2121         maTypeInfo = GetChartTypeInfo( xChartType->getChartType() );
2122         // special handling for some chart types
2123         switch( maTypeInfo.meTypeCateg )
2124         {
2125             case EXC_CHTYPECATEG_BAR:
2126             {
2127                 maTypeInfo = GetChartTypeInfo( bSwappedAxesSet ? EXC_CHTYPEID_HORBAR : EXC_CHTYPEID_BAR );
2128                 ::set_flag( maData.mnFlags, EXC_CHBAR_HORIZONTAL, bSwappedAxesSet );
2129                 ScfPropertySet aTypeProp( xChartType );
2130                 Sequence< sal_Int32 > aInt32Seq;
2131                 maData.mnOverlap = 0;
2132                 if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_OVERLAPSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) )
2133                     maData.mnOverlap = limit_cast< sal_Int16 >( -aInt32Seq[ nApiAxesSetIdx ], -100, 100 );
2134                 maData.mnGap = 150;
2135                 if( aTypeProp.GetProperty( aInt32Seq, EXC_CHPROP_GAPWIDTHSEQ ) && (nApiAxesSetIdx < aInt32Seq.getLength()) )
2136                     maData.mnGap = limit_cast< sal_uInt16 >( aInt32Seq[ nApiAxesSetIdx ], 0, 500 );
2137             }
2138             break;
2139             case EXC_CHTYPECATEG_RADAR:
2140                 ::set_flag( maData.mnFlags, EXC_CHRADAR_AXISLABELS, bHasXLabels );
2141             break;
2142             case EXC_CHTYPECATEG_PIE:
2143             {
2144                 ScfPropertySet aTypeProp( xChartType );
2145                 bool bDonut = aTypeProp.GetBoolProperty( EXC_CHPROP_USERINGS );
2146                 maTypeInfo = GetChartTypeInfo( bDonut ? EXC_CHTYPEID_DONUT : EXC_CHTYPEID_PIE );
2147                 maData.mnPieHole = bDonut ? 50 : 0;
2148                 // #i85166# starting angle of first pie slice
2149                 ScfPropertySet aDiaProp( xDiagram );
2150                 maData.mnRotation = XclExpChRoot::ConvertPieRotation( aDiaProp );
2151             }
2152             break;
2153             case EXC_CHTYPECATEG_SCATTER:
2154                 if( GetBiff() == EXC_BIFF8 )
2155                     ::set_flag( maData.mnFlags, EXC_CHSCATTER_BUBBLES, maTypeInfo.meTypeId == EXC_CHTYPEID_BUBBLES );
2156             break;
2157             default:;
2158         }
2159         SetRecId( maTypeInfo.mnRecId );
2160     }
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.reset( 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.reset( 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 = static_cast< sal_uInt16 >( aLegendSize.Width * EXC_POINTS_PER_HMM + 0.5 );
2306             rFramePos.maRect.mnHeight = static_cast< sal_uInt16 >( aLegendSize.Height * EXC_POINTS_PER_HMM + 0.5 );
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.reset( 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_CUSTOM;
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.reset( 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     {
2425         typedef ::std::vector< Reference< XDataSeries > > XDataSeriesVec;
2426         XDataSeriesVec 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         {
2441             // stacking direction (stacked/percent/deep 3d) from first series
2442             ScfPropertySet aSeriesProp( aSeriesVec.front() );
2443             cssc2::StackingDirection eStacking;
2444             if( !aSeriesProp.GetProperty( eStacking, EXC_CHPROP_STACKINGDIR ) )
2445                 eStacking = cssc2::StackingDirection_NO_STACKING;
2446 
2447             // stacked or percent chart
2448             if( maTypeInfo.mbSupportsStacking && (eStacking == cssc2::StackingDirection_Y_STACKING) )
2449             {
2450                 // percent overrides simple stacking
2451                 maType.SetStacked( bPercent );
2452 
2453                 // connected data points (only in stacked bar charts)
2454                 if (bConnectBars && (maTypeInfo.meTypeCateg == EXC_CHTYPECATEG_BAR))
2455                 {
2456                     sal_uInt16 nKey = EXC_CHCHARTLINE_CONNECT;
2457                     m_ChartLines.insert(std::make_pair(nKey, std::make_unique<XclExpChLineFormat>(GetChRoot())));
2458                 }
2459             }
2460             else
2461             {
2462                 // reverse series order for some unstacked 2D chart types
2463                 if( maTypeInfo.mbReverseSeries && !Is3dChart() )
2464                     ::std::reverse( aSeriesVec.begin(), aSeriesVec.end() );
2465             }
2466 
2467             // deep 3d chart or clustered 3d chart (stacked is not clustered)
2468             if( (eStacking == cssc2::StackingDirection_NO_STACKING) && Is3dWallChart() )
2469                 mxChart3d->SetClustered();
2470 
2471             // varied point colors
2472             ::set_flag( maData.mnFlags, EXC_CHTYPEGROUP_VARIEDCOLORS, aSeriesProp.GetBoolProperty( EXC_CHPROP_VARYCOLORSBY ) );
2473 
2474             // process all series
2475             for( const auto& rxSeries : aSeriesVec )
2476             {
2477                 // create Excel series object, stock charts need special processing
2478                 if( maTypeInfo.meTypeId == EXC_CHTYPEID_STOCK )
2479                     CreateAllStockSeries( xChartType, rxSeries );
2480                 else
2481                     CreateDataSeries( xDiagram, rxSeries );
2482             }
2483         }
2484     }
2485 }
2486 
ConvertCategSequence(Reference<XLabeledDataSequence> const & xCategSeq)2487 void XclExpChTypeGroup::ConvertCategSequence( Reference< XLabeledDataSequence > const & xCategSeq )
2488 {
2489     for( size_t nIdx = 0, nSize = maSeries.GetSize(); nIdx < nSize; ++nIdx )
2490         maSeries.GetRecord( nIdx )->ConvertCategSequence( xCategSeq );
2491 }
2492 
ConvertLegend(const ScfPropertySet & rPropSet)2493 void XclExpChTypeGroup::ConvertLegend( const ScfPropertySet& rPropSet )
2494 {
2495     if( rPropSet.GetBoolProperty( EXC_CHPROP_SHOW ) )
2496     {
2497         mxLegend.reset( new XclExpChLegend( GetChRoot() ) );
2498         mxLegend->Convert( rPropSet );
2499     }
2500 }
2501 
WriteSubRecords(XclExpStream & rStrm)2502 void XclExpChTypeGroup::WriteSubRecords( XclExpStream& rStrm )
2503 {
2504     maType.Save( rStrm );
2505     lclSaveRecord( rStrm, mxChart3d );
2506     lclSaveRecord( rStrm, mxLegend );
2507     lclSaveRecord( rStrm, mxUpBar );
2508     lclSaveRecord( rStrm, mxDownBar );
2509     for (auto const& it : m_ChartLines)
2510     {
2511         lclSaveRecord( rStrm, it.second.get(), EXC_ID_CHCHARTLINE, it.first );
2512     }
2513 }
2514 
GetFreeFormatIdx() const2515 sal_uInt16 XclExpChTypeGroup::GetFreeFormatIdx() const
2516 {
2517     return static_cast< sal_uInt16 >( maSeries.GetSize() );
2518 }
2519 
CreateDataSeries(Reference<XDiagram> const & xDiagram,Reference<XDataSeries> const & xDataSeries)2520 void XclExpChTypeGroup::CreateDataSeries(
2521         Reference< XDiagram > const & xDiagram, Reference< XDataSeries > const & xDataSeries )
2522 {
2523     // let chart create series object with correct series index
2524     XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
2525     if( xSeries )
2526     {
2527         if( xSeries->ConvertDataSeries( xDiagram, xDataSeries, maTypeInfo, GetGroupIdx(), GetFreeFormatIdx() ) )
2528             maSeries.AppendRecord( xSeries );
2529         else
2530             GetChartData().RemoveLastSeries();
2531     }
2532 }
2533 
CreateAllStockSeries(Reference<XChartType> const & xChartType,Reference<XDataSeries> const & xDataSeries)2534 void XclExpChTypeGroup::CreateAllStockSeries(
2535         Reference< XChartType > const & xChartType, Reference< XDataSeries > const & xDataSeries )
2536 {
2537     // create existing series objects
2538     bool bHasOpen = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_OPENVALUES, false );
2539     bool bHasHigh = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_HIGHVALUES, false );
2540     bool bHasLow = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_LOWVALUES, false );
2541     bool bHasClose = CreateStockSeries( xDataSeries, EXC_CHPROP_ROLE_CLOSEVALUES, !bHasOpen );
2542 
2543     // formatting of special stock chart elements
2544     ScfPropertySet aTypeProp( xChartType );
2545     // hi-lo lines
2546     if( bHasHigh && bHasLow && aTypeProp.GetBoolProperty( EXC_CHPROP_SHOWHIGHLOW ) )
2547     {
2548         ScfPropertySet aSeriesProp( xDataSeries );
2549         XclExpChLineFormatRef xLineFmt( new XclExpChLineFormat( GetChRoot() ) );
2550         xLineFmt->Convert( GetChRoot(), aSeriesProp, EXC_CHOBJTYPE_HILOLINE );
2551         sal_uInt16 nKey = EXC_CHCHARTLINE_HILO;
2552         m_ChartLines.insert(std::make_pair(nKey, std::make_unique<XclExpChLineFormat>(GetChRoot())));
2553     }
2554     // dropbars
2555     if( bHasOpen && bHasClose )
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.reset( 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.reset( new XclExpChDropBar( GetChRoot(), EXC_CHOBJTYPE_BLACKDROPBAR ) );
2568         mxDownBar->Convert( aBlackProp );
2569     }
2570 }
2571 
CreateStockSeries(Reference<XDataSeries> const & xDataSeries,const OUString & rValueRole,bool bCloseSymbol)2572 bool XclExpChTypeGroup::CreateStockSeries( Reference< XDataSeries > const & xDataSeries,
2573         const OUString& rValueRole, bool bCloseSymbol )
2574 {
2575     bool bOk = false;
2576     // let chart create series object with correct series index
2577     XclExpChSeriesRef xSeries = GetChartData().CreateSeries();
2578     if( xSeries )
2579     {
2580         bOk = xSeries->ConvertStockSeries( xDataSeries,
2581             rValueRole, GetGroupIdx(), GetFreeFormatIdx(), bCloseSymbol );
2582         if( bOk )
2583             maSeries.AppendRecord( xSeries );
2584         else
2585             GetChartData().RemoveLastSeries();
2586     }
2587     return bOk;
2588 }
2589 
WriteBody(XclExpStream & rStrm)2590 void XclExpChTypeGroup::WriteBody( XclExpStream& rStrm )
2591 {
2592     rStrm.WriteZeroBytes( 16 );
2593     rStrm << maData.mnFlags << maData.mnGroupIdx;
2594 }
2595 
2596 // Axes =======================================================================
2597 
XclExpChLabelRange(const XclExpChRoot & rRoot)2598 XclExpChLabelRange::XclExpChLabelRange( const XclExpChRoot& rRoot ) :
2599     XclExpRecord( EXC_ID_CHLABELRANGE, 8 ),
2600     XclExpChRoot( rRoot )
2601 {
2602 }
2603 
Convert(const ScaleData & rScaleData,const ScfPropertySet & rChart1Axis,bool bMirrorOrient)2604 void XclExpChLabelRange::Convert( const ScaleData& rScaleData, const ScfPropertySet& rChart1Axis, bool bMirrorOrient )
2605 {
2606     /*  Base time unit (using the property 'ExplicitTimeIncrement' from the old
2607         chart API allows to detect axis type (date axis, if property exists),
2608         and to receive the base time unit currently used in case the base time
2609         unit is set to 'automatic'. */
2610     cssc::TimeIncrement aTimeIncrement;
2611     if( rChart1Axis.GetProperty( aTimeIncrement, EXC_CHPROP_EXPTIMEINCREMENT ) )
2612     {
2613         // property exists -> this is a date axis currently
2614         ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS );
2615 
2616         // automatic base time unit, if the UNO Any 'rScaleData.TimeIncrement.TimeResolution' does not contain a valid value...
2617         bool bAutoBase = !rScaleData.TimeIncrement.TimeResolution.has< cssc::TimeIncrement >();
2618         ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOBASE, bAutoBase );
2619 
2620         // ...but get the current base time unit from the property of the old chart API
2621         sal_Int32 nApiTimeUnit = 0;
2622         bool bValidBaseUnit = aTimeIncrement.TimeResolution >>= nApiTimeUnit;
2623         OSL_ENSURE( bValidBaseUnit, "XclExpChLabelRange::Convert - cannot get base time unit" );
2624         maDateData.mnBaseUnit = bValidBaseUnit ? lclGetTimeUnit( nApiTimeUnit ) : EXC_CHDATERANGE_DAYS;
2625 
2626         /*  Min/max values depend on base time unit, they specify the number of
2627             days, months, or years starting from null date. */
2628         bool bAutoMin = lclConvertTimeValue( GetRoot(), maDateData.mnMinDate, rScaleData.Minimum, maDateData.mnBaseUnit );
2629         ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMIN, bAutoMin );
2630         bool bAutoMax = lclConvertTimeValue( GetRoot(), maDateData.mnMaxDate, rScaleData.Maximum, maDateData.mnBaseUnit );
2631         ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAX, bAutoMax );
2632     }
2633 
2634     // automatic axis type detection
2635     ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTODATE, rScaleData.AutoDateAxis );
2636 
2637     // increment
2638     bool bAutoMajor = lclConvertTimeInterval( maDateData.mnMajorStep, maDateData.mnMajorUnit, rScaleData.TimeIncrement.MajorTimeInterval );
2639     ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMAJOR, bAutoMajor );
2640     bool bAutoMinor = lclConvertTimeInterval( maDateData.mnMinorStep, maDateData.mnMinorUnit, rScaleData.TimeIncrement.MinorTimeInterval );
2641     ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOMINOR, bAutoMinor );
2642 
2643     // origin
2644     double fOrigin = 0.0;
2645     if( !lclIsAutoAnyOrGetValue( fOrigin, rScaleData.Origin ) )
2646         maLabelData.mnCross = limit_cast< sal_uInt16 >( fOrigin, 1, 31999 );
2647 
2648     // reverse order
2649     if( (rScaleData.Orientation == cssc2::AxisOrientation_REVERSE) != bMirrorOrient )
2650         ::set_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_REVERSE );
2651 }
2652 
ConvertAxisPosition(const ScfPropertySet & rPropSet)2653 void XclExpChLabelRange::ConvertAxisPosition( const ScfPropertySet& rPropSet )
2654 {
2655     cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE;
2656     rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION );
2657     double fCrossingPos = 1.0;
2658     rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE );
2659 
2660     bool bDateAxis = ::get_flag( maDateData.mnFlags, EXC_CHDATERANGE_DATEAXIS );
2661     switch( eAxisPos )
2662     {
2663         case cssc::ChartAxisPosition_ZERO:
2664         case cssc::ChartAxisPosition_START:
2665             maLabelData.mnCross = 1;
2666             ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
2667         break;
2668         case cssc::ChartAxisPosition_END:
2669             ::set_flag( maLabelData.mnFlags, EXC_CHLABELRANGE_MAXCROSS );
2670         break;
2671         case cssc::ChartAxisPosition_VALUE:
2672             maLabelData.mnCross = limit_cast< sal_uInt16 >( fCrossingPos, 1, 31999 );
2673             ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS, false );
2674             if( bDateAxis )
2675                 maDateData.mnCross = lclGetTimeValue( GetRoot(), fCrossingPos, maDateData.mnBaseUnit );
2676         break;
2677         default:
2678             maLabelData.mnCross = 1;
2679             ::set_flag( maDateData.mnFlags, EXC_CHDATERANGE_AUTOCROSS );
2680     }
2681 }
2682 
Save(XclExpStream & rStrm)2683 void XclExpChLabelRange::Save( XclExpStream& rStrm )
2684 {
2685     // the CHLABELRANGE record
2686     XclExpRecord::Save( rStrm );
2687 
2688     // the CHDATERANGE record with date axis settings (BIFF8 only)
2689     if( GetBiff() == EXC_BIFF8 )
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 }
2704 
WriteBody(XclExpStream & rStrm)2705 void XclExpChLabelRange::WriteBody( XclExpStream& rStrm )
2706 {
2707     rStrm << maLabelData.mnCross << maLabelData.mnLabelFreq << maLabelData.mnTickFreq << maLabelData.mnFlags;
2708 }
2709 
XclExpChValueRange(const XclExpChRoot & rRoot)2710 XclExpChValueRange::XclExpChValueRange( const XclExpChRoot& rRoot ) :
2711     XclExpRecord( EXC_ID_CHVALUERANGE, 42 ),
2712     XclExpChRoot( rRoot )
2713 {
2714 }
2715 
Convert(const ScaleData & rScaleData)2716 void XclExpChValueRange::Convert( const ScaleData& rScaleData )
2717 {
2718     // scaling algorithm
2719     bool bLogScale = ScfApiHelper::GetServiceName( rScaleData.Scaling ) == "com.sun.star.chart2.LogarithmicScaling";
2720     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_LOGSCALE, bLogScale );
2721 
2722     // min/max
2723     bool bAutoMin = lclIsAutoAnyOrGetScaledValue( maData.mfMin, rScaleData.Minimum, bLogScale );
2724     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMIN, bAutoMin );
2725     bool bAutoMax = lclIsAutoAnyOrGetScaledValue( maData.mfMax, rScaleData.Maximum, bLogScale );
2726     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAX, bAutoMax );
2727 
2728     // origin
2729     bool bAutoCross = lclIsAutoAnyOrGetScaledValue( maData.mfCross, rScaleData.Origin, bLogScale );
2730     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOCROSS, bAutoCross );
2731 
2732     // major increment
2733     const IncrementData& rIncrementData = rScaleData.IncrementData;
2734     const bool bAutoMajor = lclIsAutoAnyOrGetValue( maData.mfMajorStep, rIncrementData.Distance ) || (maData.mfMajorStep <= 0.0);
2735     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMAJOR, bAutoMajor );
2736     // minor increment
2737     const Sequence< SubIncrement >& rSubIncrementSeq = rIncrementData.SubIncrements;
2738     sal_Int32 nCount = 0;
2739 
2740     // tdf#114168 If IntervalCount is 5, then enable automatic minor calculation.
2741     // During import, if minorUnit is set and majorUnit not, then it is impossible
2742     // to calculate IntervalCount.
2743     const bool bAutoMinor = bLogScale || bAutoMajor || !rSubIncrementSeq.hasElements() ||
2744         lclIsAutoAnyOrGetValue( nCount, rSubIncrementSeq[ 0 ].IntervalCount ) || (nCount < 1) || (nCount == 5);
2745 
2746     if( maData.mfMajorStep && !bAutoMinor )
2747         maData.mfMinorStep = maData.mfMajorStep / nCount;
2748     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_AUTOMINOR, bAutoMinor );
2749 
2750     // reverse order
2751     ::set_flag( maData.mnFlags, EXC_CHVALUERANGE_REVERSE, rScaleData.Orientation == cssc2::AxisOrientation_REVERSE );
2752 }
2753 
ConvertAxisPosition(const ScfPropertySet & rPropSet)2754 void XclExpChValueRange::ConvertAxisPosition( const ScfPropertySet& rPropSet )
2755 {
2756     cssc::ChartAxisPosition eAxisPos = cssc::ChartAxisPosition_VALUE;
2757     double fCrossingPos = 0.0;
2758     if( rPropSet.GetProperty( eAxisPos, EXC_CHPROP_CROSSOVERPOSITION ) && rPropSet.GetProperty( fCrossingPos, EXC_CHPROP_CROSSOVERVALUE ) )
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 }
2778 
WriteBody(XclExpStream & rStrm)2779 void XclExpChValueRange::WriteBody( XclExpStream& rStrm )
2780 {
2781     rStrm   << maData.mfMin
2782             << maData.mfMax
2783             << maData.mfMajorStep
2784             << maData.mfMinorStep
2785             << maData.mfCross
2786             << maData.mnFlags;
2787 }
2788 
2789 namespace {
2790 
lclGetXclTickPos(sal_Int32 nApiTickmarks)2791 sal_uInt8 lclGetXclTickPos( sal_Int32 nApiTickmarks )
2792 {
2793     using namespace cssc2::TickmarkStyle;
2794     sal_uInt8 nXclTickPos = 0;
2795     ::set_flag( nXclTickPos, EXC_CHTICK_INSIDE,  ::get_flag( nApiTickmarks, INNER ) );
2796     ::set_flag( nXclTickPos, EXC_CHTICK_OUTSIDE, ::get_flag( nApiTickmarks, OUTER ) );
2797     return nXclTickPos;
2798 }
2799 
2800 } // namespace
2801 
XclExpChTick(const XclExpChRoot & rRoot)2802 XclExpChTick::XclExpChTick( const XclExpChRoot& rRoot ) :
2803     XclExpRecord( EXC_ID_CHTICK, (rRoot.GetBiff() == EXC_BIFF8) ? 30 : 26 ),
2804     XclExpChRoot( rRoot ),
2805     mnTextColorId( XclExpPalette::GetColorIdFromIndex( EXC_COLOR_CHWINDOWTEXT ) )
2806 {
2807 }
2808 
Convert(const ScfPropertySet & rPropSet,const XclChExtTypeInfo & rTypeInfo,sal_uInt16 nAxisType)2809 void XclExpChTick::Convert( const ScfPropertySet& rPropSet, const XclChExtTypeInfo& rTypeInfo, sal_uInt16 nAxisType )
2810 {
2811     // tick mark style
2812     sal_Int32 nApiTickmarks = 0;
2813     if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MAJORTICKS ) )
2814         maData.mnMajor = lclGetXclTickPos( nApiTickmarks );
2815     if( rPropSet.GetProperty( nApiTickmarks, EXC_CHPROP_MINORTICKS ) )
2816         maData.mnMinor = lclGetXclTickPos( nApiTickmarks );
2817 
2818     // axis labels
2819     if( (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) && (nAxisType == EXC_CHAXIS_X) )
2820     {
2821         /*  Radar charts disable their category labels via chart type, not via
2822             axis, and axis labels are always 'near axis'. */
2823         maData.mnLabelPos = EXC_CHTICK_NEXT;
2824     }
2825     else if( !rPropSet.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS ) )
2826     {
2827         // no labels
2828         maData.mnLabelPos = EXC_CHTICK_NOLABEL;
2829     }
2830     else if( rTypeInfo.mb3dChart && (nAxisType == EXC_CHAXIS_Y) )
2831     {
2832         // Excel expects 'near axis' at Y axes in 3D charts
2833         maData.mnLabelPos = EXC_CHTICK_NEXT;
2834     }
2835     else
2836     {
2837         cssc::ChartAxisLabelPosition eApiLabelPos = cssc::ChartAxisLabelPosition_NEAR_AXIS;
2838         rPropSet.GetProperty( eApiLabelPos, EXC_CHPROP_LABELPOSITION );
2839         switch( eApiLabelPos )
2840         {
2841             case cssc::ChartAxisLabelPosition_NEAR_AXIS:
2842             case cssc::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE: maData.mnLabelPos = EXC_CHTICK_NEXT;    break;
2843             case cssc::ChartAxisLabelPosition_OUTSIDE_START:        maData.mnLabelPos = EXC_CHTICK_LOW;     break;
2844             case cssc::ChartAxisLabelPosition_OUTSIDE_END:          maData.mnLabelPos = EXC_CHTICK_HIGH;    break;
2845             default:                                                maData.mnLabelPos = EXC_CHTICK_NEXT;
2846         }
2847     }
2848 }
2849 
SetFontColor(const Color & rColor,sal_uInt32 nColorId)2850 void XclExpChTick::SetFontColor( const Color& rColor, sal_uInt32 nColorId )
2851 {
2852     maData.maTextColor = rColor;
2853     ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOCOLOR, rColor == COL_AUTO );
2854     mnTextColorId = nColorId;
2855 }
2856 
SetRotation(sal_uInt16 nRotation)2857 void XclExpChTick::SetRotation( sal_uInt16 nRotation )
2858 {
2859     maData.mnRotation = nRotation;
2860     ::set_flag( maData.mnFlags, EXC_CHTICK_AUTOROT, false );
2861     ::insert_value( maData.mnFlags, XclTools::GetXclOrientFromRot( nRotation ), 2, 3 );
2862 }
2863 
WriteBody(XclExpStream & rStrm)2864 void XclExpChTick::WriteBody( XclExpStream& rStrm )
2865 {
2866     rStrm   << maData.mnMajor
2867             << maData.mnMinor
2868             << maData.mnLabelPos
2869             << maData.mnBackMode;
2870     rStrm.WriteZeroBytes( 16 );
2871     rStrm   << maData.maTextColor
2872             << maData.mnFlags;
2873     if( GetBiff() == EXC_BIFF8 )
2874         rStrm << GetPalette().GetColorIndex( mnTextColorId ) << maData.mnRotation;
2875 }
2876 
2877 namespace {
2878 
2879 /** Returns an API axis object from the passed coordinate system. */
lclGetApiAxis(Reference<XCoordinateSystem> const & xCoordSystem,sal_Int32 nApiAxisDim,sal_Int32 nApiAxesSetIdx)2880 Reference< XAxis > lclGetApiAxis( Reference< XCoordinateSystem > const & xCoordSystem,
2881         sal_Int32 nApiAxisDim, sal_Int32 nApiAxesSetIdx )
2882 {
2883     Reference< XAxis > xAxis;
2884     if( (nApiAxisDim >= 0) && xCoordSystem.is() ) try
2885     {
2886         xAxis = xCoordSystem->getAxisByDimension( nApiAxisDim, nApiAxesSetIdx );
2887     }
2888     catch( Exception& )
2889     {
2890     }
2891     return xAxis;
2892 }
2893 
lclGetApiChart1Axis(Reference<XChartDocument> const & xChartDoc,sal_Int32 nApiAxisDim,sal_Int32 nApiAxesSetIdx)2894 Reference< cssc::XAxis > lclGetApiChart1Axis( Reference< XChartDocument > const & xChartDoc,
2895         sal_Int32 nApiAxisDim, sal_Int32 nApiAxesSetIdx )
2896 {
2897     Reference< cssc::XAxis > xChart1Axis;
2898     try
2899     {
2900         Reference< cssc::XChartDocument > xChart1Doc( xChartDoc, UNO_QUERY_THROW );
2901         Reference< cssc::XAxisSupplier > xChart1AxisSupp( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
2902         switch( nApiAxesSetIdx )
2903         {
2904             case EXC_CHART_AXESSET_PRIMARY:
2905                 xChart1Axis = xChart1AxisSupp->getAxis( nApiAxisDim );
2906             break;
2907             case EXC_CHART_AXESSET_SECONDARY:
2908                 xChart1Axis = xChart1AxisSupp->getSecondaryAxis( nApiAxisDim );
2909             break;
2910         }
2911     }
2912     catch( Exception& )
2913     {
2914     }
2915     return xChart1Axis;
2916 }
2917 
2918 } // namespace
2919 
XclExpChAxis(const XclExpChRoot & rRoot,sal_uInt16 nAxisType)2920 XclExpChAxis::XclExpChAxis( const XclExpChRoot& rRoot, sal_uInt16 nAxisType ) :
2921     XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXIS, EXC_ID_CHAXIS, 18 ),
2922     mnNumFmtIdx( EXC_FORMAT_NOTFOUND )
2923 {
2924     maData.mnType = nAxisType;
2925 }
2926 
SetFont(XclExpChFontRef xFont,const Color & rColor,sal_uInt32 nColorId)2927 void XclExpChAxis::SetFont( XclExpChFontRef xFont, const Color& rColor, sal_uInt32 nColorId )
2928 {
2929     mxFont = xFont;
2930     if( mxTick )
2931         mxTick->SetFontColor( rColor, nColorId );
2932 }
2933 
SetRotation(sal_uInt16 nRotation)2934 void XclExpChAxis::SetRotation( sal_uInt16 nRotation )
2935 {
2936     if( mxTick )
2937         mxTick->SetRotation( nRotation );
2938 }
2939 
Convert(Reference<XAxis> const & xAxis,Reference<XAxis> const & xCrossingAxis,Reference<cssc::XAxis> const & xChart1Axis,const XclChExtTypeInfo & rTypeInfo)2940 void XclExpChAxis::Convert( Reference< XAxis > const & xAxis, Reference< XAxis > const & xCrossingAxis,
2941         Reference< cssc::XAxis > const & xChart1Axis, const XclChExtTypeInfo& rTypeInfo )
2942 {
2943     ScfPropertySet aAxisProp( xAxis );
2944     bool bCategoryAxis = ((GetAxisType() == EXC_CHAXIS_X) && rTypeInfo.mbCategoryAxis) || (GetAxisType() == EXC_CHAXIS_Z);
2945 
2946     // axis line format -------------------------------------------------------
2947 
2948     mxAxisLine.reset( new XclExpChLineFormat( GetChRoot() ) );
2949     mxAxisLine->Convert( GetChRoot(), aAxisProp, EXC_CHOBJTYPE_AXISLINE );
2950     // #i58688# axis enabled
2951     mxAxisLine->SetShowAxis( aAxisProp.GetBoolProperty( EXC_CHPROP_SHOW ) );
2952 
2953     // axis scaling and increment ---------------------------------------------
2954 
2955     ScfPropertySet aCrossingProp( xCrossingAxis );
2956     if( bCategoryAxis )
2957     {
2958         mxLabelRange.reset( new XclExpChLabelRange( GetChRoot() ) );
2959         mxLabelRange->SetTicksBetweenCateg( rTypeInfo.mbTicksBetweenCateg );
2960         if( xAxis.is() )
2961         {
2962             ScfPropertySet aChart1AxisProp( xChart1Axis );
2963             // #i71684# radar charts have reversed rotation direction
2964             mxLabelRange->Convert( xAxis->getScaleData(), aChart1AxisProp, (GetAxisType() == EXC_CHAXIS_X) && (rTypeInfo.meTypeCateg == EXC_CHTYPECATEG_RADAR) );
2965         }
2966         // get position of crossing axis on this axis from passed axis object
2967         if( aCrossingProp.Is() )
2968             mxLabelRange->ConvertAxisPosition( aCrossingProp );
2969     }
2970     else
2971     {
2972         mxValueRange.reset( new XclExpChValueRange( GetChRoot() ) );
2973         if( xAxis.is() )
2974             mxValueRange->Convert( xAxis->getScaleData() );
2975         // get position of crossing axis on this axis from passed axis object
2976         if( aCrossingProp.Is() )
2977             mxValueRange->ConvertAxisPosition( aCrossingProp );
2978     }
2979 
2980     // axis caption text ------------------------------------------------------
2981 
2982     // axis ticks properties
2983     mxTick.reset( new XclExpChTick( GetChRoot() ) );
2984     mxTick->Convert( aAxisProp, rTypeInfo, GetAxisType() );
2985 
2986     // axis label formatting and rotation
2987     ConvertFontBase( GetChRoot(), aAxisProp );
2988     ConvertRotationBase( aAxisProp, true );
2989 
2990     // axis number format
2991     sal_Int32 nApiNumFmt = 0;
2992     if( !bCategoryAxis && aAxisProp.GetProperty( nApiNumFmt, EXC_CHPROP_NUMBERFORMAT ) )
2993     {
2994         bool bLinkNumberFmtToSource = false;
2995         if ( !aAxisProp.GetProperty( bLinkNumberFmtToSource, EXC_CHPROP_NUMBERFORMAT_LINKSRC ) || !bLinkNumberFmtToSource )
2996             mnNumFmtIdx = GetNumFmtBuffer().Insert( static_cast< sal_uInt32 >( nApiNumFmt ) );
2997     }
2998 
2999     // grid -------------------------------------------------------------------
3000 
3001     if( xAxis.is() )
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 }
3017 
ConvertWall(css::uno::Reference<css::chart2::XDiagram> const & xDiagram)3018 void XclExpChAxis::ConvertWall( css::uno::Reference< css::chart2::XDiagram > const & xDiagram )
3019 {
3020     if( xDiagram.is() ) switch( GetAxisType() )
3021     {
3022         case EXC_CHAXIS_X:
3023         {
3024             ScfPropertySet aWallProp( xDiagram->getWall() );
3025             mxWallFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_WALL3D );
3026         }
3027         break;
3028         case EXC_CHAXIS_Y:
3029         {
3030             ScfPropertySet aFloorProp( xDiagram->getFloor() );
3031             mxWallFrame = lclCreateFrame( GetChRoot(), aFloorProp, EXC_CHOBJTYPE_FLOOR3D );
3032         }
3033         break;
3034         default:
3035             mxWallFrame.reset();
3036     }
3037 }
3038 
WriteSubRecords(XclExpStream & rStrm)3039 void XclExpChAxis::WriteSubRecords( XclExpStream& rStrm )
3040 {
3041     lclSaveRecord( rStrm, mxLabelRange );
3042     lclSaveRecord( rStrm, mxValueRange );
3043     if( mnNumFmtIdx != EXC_FORMAT_NOTFOUND )
3044         XclExpUInt16Record( EXC_ID_CHFORMAT, mnNumFmtIdx ).Save( rStrm );
3045     lclSaveRecord( rStrm, mxTick );
3046     lclSaveRecord( rStrm, mxFont );
3047     lclSaveRecord( rStrm, mxAxisLine, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_AXISLINE );
3048     lclSaveRecord( rStrm, mxMajorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MAJORGRID );
3049     lclSaveRecord( rStrm, mxMinorGrid, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_MINORGRID );
3050     lclSaveRecord( rStrm, mxWallFrame, EXC_ID_CHAXISLINE, EXC_CHAXISLINE_WALLS );
3051 }
3052 
WriteBody(XclExpStream & rStrm)3053 void XclExpChAxis::WriteBody( XclExpStream& rStrm )
3054 {
3055     rStrm << maData.mnType;
3056     rStrm.WriteZeroBytes( 16 );
3057 }
3058 
XclExpChAxesSet(const XclExpChRoot & rRoot,sal_uInt16 nAxesSetId)3059 XclExpChAxesSet::XclExpChAxesSet( const XclExpChRoot& rRoot, sal_uInt16 nAxesSetId ) :
3060     XclExpChGroupBase( rRoot, EXC_CHFRBLOCK_TYPE_AXESSET, EXC_ID_CHAXESSET, 18 )
3061 {
3062     maData.mnAxesSetId = nAxesSetId;
3063     SetFutureRecordContext( 0, nAxesSetId );
3064 
3065     /*  Need to set a reasonable size for the plot area, otherwise Excel will
3066         move away embedded shapes while auto-sizing the plot area. This is just
3067         a wild guess, but will be fixed with implementing manual positioning of
3068         chart elements. */
3069     maData.maRect.mnX = 262;
3070     maData.maRect.mnY = 626;
3071     maData.maRect.mnWidth = 3187;
3072     maData.maRect.mnHeight = 2633;
3073 }
3074 
Convert(Reference<XDiagram> const & xDiagram,sal_uInt16 nFirstGroupIdx)3075 sal_uInt16 XclExpChAxesSet::Convert( Reference< XDiagram > const & xDiagram, sal_uInt16 nFirstGroupIdx )
3076 {
3077     /*  First unused chart type group index is passed to be able to continue
3078         counting of chart type groups for secondary axes set. */
3079     sal_uInt16 nGroupIdx = nFirstGroupIdx;
3080     Reference< XCoordinateSystemContainer > xCoordSysCont( xDiagram, UNO_QUERY );
3081     if( xCoordSysCont.is() )
3082     {
3083         Sequence< Reference< XCoordinateSystem > > aCoordSysSeq = xCoordSysCont->getCoordinateSystems();
3084         if( aCoordSysSeq.hasElements() )
3085         {
3086             /*  Process first coordinate system only. Import filter puts all
3087                 chart types into one coordinate system. */
3088             Reference< XCoordinateSystem > xCoordSystem = aCoordSysSeq[ 0 ];
3089             sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
3090 
3091             // 3d mode
3092             bool b3dChart = xCoordSystem.is() && (xCoordSystem->getDimension() == 3);
3093 
3094             // percent charts
3095             namespace ApiAxisType = cssc2::AxisType;
3096             Reference< XAxis > xApiYAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_Y, nApiAxesSetIdx );
3097             bool bPercent = xApiYAxis.is() && (xApiYAxis->getScaleData().AxisType == ApiAxisType::PERCENT);
3098 
3099             // connector lines in bar charts
3100             ScfPropertySet aDiaProp( xDiagram );
3101             bool bConnectBars = aDiaProp.GetBoolProperty( EXC_CHPROP_CONNECTBARS );
3102 
3103             // swapped axes sets
3104             ScfPropertySet aCoordSysProp( xCoordSystem );
3105             bool bSwappedAxesSet = aCoordSysProp.GetBoolProperty( EXC_CHPROP_SWAPXANDYAXIS );
3106 
3107             // X axis for later use
3108             Reference< XAxis > xApiXAxis = lclGetApiAxis( xCoordSystem, EXC_CHART_AXIS_X, nApiAxesSetIdx );
3109             // X axis labels
3110             ScfPropertySet aXAxisProp( xApiXAxis );
3111             bool bHasXLabels = aXAxisProp.GetBoolProperty( EXC_CHPROP_DISPLAYLABELS );
3112 
3113             // process chart types
3114             Reference< XChartTypeContainer > xChartTypeCont( xCoordSystem, UNO_QUERY );
3115             if( xChartTypeCont.is() )
3116             {
3117                 const Sequence< Reference< XChartType > > aChartTypeSeq = xChartTypeCont->getChartTypes();
3118                 for( const Reference< XChartType >& rChartType : aChartTypeSeq )
3119                 {
3120                     XclExpChTypeGroupRef xTypeGroup( new XclExpChTypeGroup( GetChRoot(), nGroupIdx ) );
3121                     xTypeGroup->ConvertType( xDiagram, rChartType, nApiAxesSetIdx, b3dChart, bSwappedAxesSet, bHasXLabels );
3122                     /*  If new chart type group cannot be inserted into a combination
3123                         chart with existing type groups, insert all series into last
3124                         contained chart type group instead of creating a new group. */
3125                     XclExpChTypeGroupRef xLastGroup = GetLastTypeGroup();
3126                     if( xLastGroup && !(xTypeGroup->IsCombinable2d() && xLastGroup->IsCombinable2d()) )
3127                     {
3128                         xLastGroup->ConvertSeries( xDiagram, rChartType, nApiAxesSetIdx, bPercent, bConnectBars );
3129                     }
3130                     else
3131                     {
3132                         xTypeGroup->ConvertSeries( xDiagram, rChartType, nApiAxesSetIdx, bPercent, bConnectBars );
3133                         if( xTypeGroup->IsValidGroup() )
3134                         {
3135                             maTypeGroups.AppendRecord( xTypeGroup );
3136                             ++nGroupIdx;
3137                         }
3138                     }
3139                 }
3140             }
3141 
3142             if( XclExpChTypeGroup* pGroup = GetFirstTypeGroup().get() )
3143             {
3144                 const XclChExtTypeInfo& rTypeInfo = pGroup->GetTypeInfo();
3145 
3146                 // create axes according to chart type (no axes for pie and donut charts)
3147                 if( rTypeInfo.meTypeCateg != EXC_CHTYPECATEG_PIE )
3148                 {
3149                     ConvertAxis( mxXAxis, EXC_CHAXIS_X, mxXAxisTitle, EXC_CHOBJLINK_XAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_Y );
3150                     ConvertAxis( mxYAxis, EXC_CHAXIS_Y, mxYAxisTitle, EXC_CHOBJLINK_YAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_X );
3151                     if( pGroup->Is3dDeepChart() )
3152                         ConvertAxis( mxZAxis, EXC_CHAXIS_Z, mxZAxisTitle, EXC_CHOBJLINK_ZAXIS, xCoordSystem, rTypeInfo, EXC_CHART_AXIS_NONE );
3153                 }
3154 
3155                 // X axis category ranges
3156                 if( rTypeInfo.mbCategoryAxis && xApiXAxis.is() )
3157                 {
3158                     const ScaleData aScaleData = xApiXAxis->getScaleData();
3159                     for( size_t nIdx = 0, nSize = maTypeGroups.GetSize(); nIdx < nSize; ++nIdx )
3160                         maTypeGroups.GetRecord( nIdx )->ConvertCategSequence( aScaleData.Categories );
3161                 }
3162 
3163                 // legend
3164                 if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) )
3165                 {
3166                     Reference< XLegend > xLegend = xDiagram->getLegend();
3167                     if( xLegend.is() )
3168                     {
3169                         ScfPropertySet aLegendProp( xLegend );
3170                         pGroup->ConvertLegend( aLegendProp );
3171                     }
3172                 }
3173             }
3174         }
3175     }
3176 
3177     // wall/floor/diagram frame formatting
3178     if( xDiagram.is() && (GetAxesSetId() == EXC_CHAXESSET_PRIMARY) )
3179     {
3180         XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
3181         if( xTypeGroup && xTypeGroup->Is3dWallChart() )
3182         {
3183             // wall/floor formatting (3D charts)
3184             if( mxXAxis )
3185                 mxXAxis->ConvertWall( xDiagram );
3186             if( mxYAxis )
3187                 mxYAxis->ConvertWall( xDiagram );
3188         }
3189         else
3190         {
3191             // diagram background formatting
3192             ScfPropertySet aWallProp( xDiagram->getWall() );
3193             mxPlotFrame = lclCreateFrame( GetChRoot(), aWallProp, EXC_CHOBJTYPE_PLOTFRAME );
3194         }
3195     }
3196 
3197     // inner and outer plot area position and size
3198     try
3199     {
3200         Reference< cssc::XChartDocument > xChart1Doc( GetChartDocument(), UNO_QUERY_THROW );
3201         Reference< cssc::XDiagramPositioning > xPositioning( xChart1Doc->getDiagram(), UNO_QUERY_THROW );
3202         // set manual flag in chart data
3203         if( !xPositioning->isAutomaticDiagramPositioning() )
3204             GetChartData().SetManualPlotArea();
3205         // the CHAXESSET record contains the inner plot area
3206         maData.maRect = CalcChartRectFromHmm( xPositioning->calculateDiagramPositionExcludingAxes() );
3207         // the embedded CHFRAMEPOS record contains the outer plot area
3208         mxFramePos.reset( new XclExpChFramePos( EXC_CHFRAMEPOS_PARENT ) );
3209         // for pie charts, always use inner plot area size to exclude the data labels as Excel does
3210         const XclExpChTypeGroup* pFirstTypeGroup = GetFirstTypeGroup().get();
3211         bool bPieChart = pFirstTypeGroup && (pFirstTypeGroup->GetTypeInfo().meTypeCateg == EXC_CHTYPECATEG_PIE);
3212         mxFramePos->GetFramePosData().maRect = bPieChart ? maData.maRect :
3213             CalcChartRectFromHmm( xPositioning->calculateDiagramPositionIncludingAxes() );
3214     }
3215     catch( Exception& )
3216     {
3217     }
3218 
3219     // return first unused chart type group index for next axes set
3220     return nGroupIdx;
3221 }
3222 
Is3dChart() const3223 bool XclExpChAxesSet::Is3dChart() const
3224 {
3225     XclExpChTypeGroupRef xTypeGroup = GetFirstTypeGroup();
3226     return xTypeGroup && xTypeGroup->Is3dChart();
3227 }
3228 
WriteSubRecords(XclExpStream & rStrm)3229 void XclExpChAxesSet::WriteSubRecords( XclExpStream& rStrm )
3230 {
3231     lclSaveRecord( rStrm, mxFramePos );
3232     lclSaveRecord( rStrm, mxXAxis );
3233     lclSaveRecord( rStrm, mxYAxis );
3234     lclSaveRecord( rStrm, mxZAxis );
3235     lclSaveRecord( rStrm, mxXAxisTitle );
3236     lclSaveRecord( rStrm, mxYAxisTitle );
3237     lclSaveRecord( rStrm, mxZAxisTitle );
3238     if( mxPlotFrame )
3239     {
3240         XclExpEmptyRecord( EXC_ID_CHPLOTFRAME ).Save( rStrm );
3241         mxPlotFrame->Save( rStrm );
3242     }
3243     maTypeGroups.Save( rStrm );
3244 }
3245 
GetFirstTypeGroup() const3246 XclExpChTypeGroupRef XclExpChAxesSet::GetFirstTypeGroup() const
3247 {
3248     return maTypeGroups.GetFirstRecord();
3249 }
3250 
GetLastTypeGroup() const3251 XclExpChTypeGroupRef XclExpChAxesSet::GetLastTypeGroup() const
3252 {
3253     return maTypeGroups.GetLastRecord();
3254 }
3255 
ConvertAxis(XclExpChAxisRef & rxChAxis,sal_uInt16 nAxisType,XclExpChTextRef & rxChAxisTitle,sal_uInt16 nTitleTarget,Reference<XCoordinateSystem> const & xCoordSystem,const XclChExtTypeInfo & rTypeInfo,sal_Int32 nCrossingAxisDim)3256 void XclExpChAxesSet::ConvertAxis(
3257         XclExpChAxisRef& rxChAxis, sal_uInt16 nAxisType,
3258         XclExpChTextRef& rxChAxisTitle, sal_uInt16 nTitleTarget,
3259         Reference< XCoordinateSystem > const & xCoordSystem, const XclChExtTypeInfo& rTypeInfo,
3260         sal_Int32 nCrossingAxisDim )
3261 {
3262     // create and convert axis object
3263     rxChAxis.reset( new XclExpChAxis( GetChRoot(), nAxisType ) );
3264     sal_Int32 nApiAxisDim = rxChAxis->GetApiAxisDimension();
3265     sal_Int32 nApiAxesSetIdx = GetApiAxesSetIndex();
3266     Reference< XAxis > xAxis = lclGetApiAxis( xCoordSystem, nApiAxisDim, nApiAxesSetIdx );
3267     Reference< XAxis > xCrossingAxis = lclGetApiAxis( xCoordSystem, nCrossingAxisDim, nApiAxesSetIdx );
3268     Reference< cssc::XAxis > xChart1Axis = lclGetApiChart1Axis( GetChartDocument(), nApiAxisDim, nApiAxesSetIdx );
3269     rxChAxis->Convert( xAxis, xCrossingAxis, xChart1Axis, rTypeInfo );
3270 
3271     // create and convert axis title
3272     Reference< XTitled > xTitled( xAxis, UNO_QUERY );
3273     rxChAxisTitle = lclCreateTitle( GetChRoot(), xTitled, nTitleTarget );
3274 }
3275 
WriteBody(XclExpStream & rStrm)3276 void XclExpChAxesSet::WriteBody( XclExpStream& rStrm )
3277 {
3278     rStrm << maData.mnAxesSetId << maData.maRect;
3279 }
3280 
3281 // The chart object ===========================================================
3282 
lcl_getChartSubTitle(const Reference<XChartDocument> & xChartDoc,OUString & rSubTitle)3283 static void lcl_getChartSubTitle(const Reference<XChartDocument>& xChartDoc,
3284                                  OUString& rSubTitle)
3285 {
3286     Reference< css::chart::XChartDocument > xChartDoc1(xChartDoc, UNO_QUERY);
3287     if (!xChartDoc1.is())
3288         return;
3289 
3290     Reference< XPropertySet > xProp(xChartDoc1->getSubTitle(), UNO_QUERY);
3291     if (!xProp.is())
3292         return;
3293 
3294     OUString aTitle;
3295     Any any = xProp->getPropertyValue("String");
3296     if (any >>= aTitle)
3297         rSubTitle = aTitle;
3298 }
3299 
XclExpChChart(const XclExpRoot & rRoot,Reference<XChartDocument> const & xChartDoc,const tools::Rectangle & rChartRect)3300 XclExpChChart::XclExpChChart( const XclExpRoot& rRoot,
3301         Reference< XChartDocument > const & xChartDoc, const tools::Rectangle& rChartRect ) :
3302     XclExpChGroupBase( XclExpChRoot( rRoot, *this ), EXC_CHFRBLOCK_TYPE_CHART, EXC_ID_CHCHART, 16 )
3303 {
3304     Size aPtSize = OutputDevice::LogicToLogic( rChartRect.GetSize(), MapMode( MapUnit::Map100thMM ), MapMode( MapUnit::MapPoint ) );
3305     // rectangle is stored in 16.16 fixed-point format
3306     maRect.mnX = maRect.mnY = 0;
3307     maRect.mnWidth = static_cast< sal_Int32 >( aPtSize.Width() << 16 );
3308     maRect.mnHeight = static_cast< sal_Int32 >( aPtSize.Height() << 16 );
3309 
3310     // global chart properties (default values)
3311     ::set_flag( maProps.mnFlags, EXC_CHPROPS_SHOWVISIBLEONLY, false );
3312     ::set_flag( maProps.mnFlags, EXC_CHPROPS_MANPLOTAREA );
3313     maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP;
3314 
3315     // always create both axes set objects
3316     mxPrimAxesSet.reset( new XclExpChAxesSet( GetChRoot(), EXC_CHAXESSET_PRIMARY ) );
3317     mxSecnAxesSet.reset( new XclExpChAxesSet( GetChRoot(), EXC_CHAXESSET_SECONDARY ) );
3318 
3319     if( xChartDoc.is() )
3320     {
3321         Reference< XDiagram > xDiagram = xChartDoc->getFirstDiagram();
3322 
3323         // global chart properties (only 'include hidden cells' attribute for now)
3324         ScfPropertySet aDiagramProp( xDiagram );
3325         bool bIncludeHidden = aDiagramProp.GetBoolProperty( EXC_CHPROP_INCLUDEHIDDENCELLS );
3326         ::set_flag( maProps.mnFlags,  EXC_CHPROPS_SHOWVISIBLEONLY, !bIncludeHidden );
3327 
3328         // initialize API conversion (remembers xChartDoc and rChartRect internally)
3329         InitConversion( xChartDoc, rChartRect );
3330 
3331         // chart frame
3332         ScfPropertySet aFrameProp( xChartDoc->getPageBackground() );
3333         mxFrame = lclCreateFrame( GetChRoot(), aFrameProp, EXC_CHOBJTYPE_BACKGROUND );
3334 
3335         // chart title
3336         Reference< XTitled > xTitled( xChartDoc, UNO_QUERY );
3337         OUString aSubTitle;
3338         lcl_getChartSubTitle(xChartDoc, aSubTitle);
3339         mxTitle = lclCreateTitle( GetChRoot(), xTitled, EXC_CHOBJLINK_TITLE,
3340                                   !aSubTitle.isEmpty() ? &aSubTitle : nullptr );
3341 
3342         // diagrams (axes sets)
3343         sal_uInt16 nFreeGroupIdx = mxPrimAxesSet->Convert( xDiagram, 0 );
3344         if( !mxPrimAxesSet->Is3dChart() )
3345             mxSecnAxesSet->Convert( xDiagram, nFreeGroupIdx );
3346 
3347         // treatment of missing values
3348         ScfPropertySet aDiaProp( xDiagram );
3349         sal_Int32 nMissingValues = 0;
3350         if( aDiaProp.GetProperty( nMissingValues, EXC_CHPROP_MISSINGVALUETREATMENT ) )
3351         {
3352             using namespace cssc::MissingValueTreatment;
3353             switch( nMissingValues )
3354             {
3355                 case LEAVE_GAP: maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_SKIP;           break;
3356                 case USE_ZERO:  maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_ZERO;           break;
3357                 case CONTINUE:  maProps.mnEmptyMode = EXC_CHPROPS_EMPTY_INTERPOLATE;    break;
3358             }
3359         }
3360 
3361         // finish API conversion
3362         FinishConversion();
3363     }
3364 }
3365 
CreateSeries()3366 XclExpChSeriesRef XclExpChChart::CreateSeries()
3367 {
3368     XclExpChSeriesRef xSeries;
3369     sal_uInt16 nSeriesIdx = static_cast< sal_uInt16 >( maSeries.GetSize() );
3370     if( nSeriesIdx <= EXC_CHSERIES_MAXSERIES )
3371     {
3372         xSeries.reset( new XclExpChSeries( GetChRoot(), nSeriesIdx ) );
3373         maSeries.AppendRecord( xSeries );
3374     }
3375     return xSeries;
3376 }
3377 
RemoveLastSeries()3378 void XclExpChChart::RemoveLastSeries()
3379 {
3380     if( !maSeries.IsEmpty() )
3381         maSeries.RemoveRecord( maSeries.GetSize() - 1 );
3382 }
3383 
SetDataLabel(XclExpChTextRef const & xText)3384 void XclExpChChart::SetDataLabel( XclExpChTextRef const & xText )
3385 {
3386     if( xText )
3387         maLabels.AppendRecord( xText );
3388 }
3389 
SetManualPlotArea()3390 void XclExpChChart::SetManualPlotArea()
3391 {
3392     // this flag does not exist in BIFF5
3393     if( GetBiff() == EXC_BIFF8 )
3394         ::set_flag( maProps.mnFlags, EXC_CHPROPS_USEMANPLOTAREA );
3395 }
3396 
WriteSubRecords(XclExpStream & rStrm)3397 void XclExpChChart::WriteSubRecords( XclExpStream& rStrm )
3398 {
3399     // background format
3400     lclSaveRecord( rStrm, mxFrame );
3401 
3402     // data series
3403     maSeries.Save( rStrm );
3404 
3405     // CHPROPERTIES record
3406     rStrm.StartRecord( EXC_ID_CHPROPERTIES, 4 );
3407     rStrm << maProps.mnFlags << maProps.mnEmptyMode << sal_uInt8( 0 );
3408     rStrm.EndRecord();
3409 
3410     // axes sets (always save primary axes set)
3411     sal_uInt16 nUsedAxesSets = mxSecnAxesSet->IsValidAxesSet() ? 2 : 1;
3412     XclExpUInt16Record( EXC_ID_CHUSEDAXESSETS, nUsedAxesSets ).Save( rStrm );
3413     mxPrimAxesSet->Save( rStrm );
3414     if( mxSecnAxesSet->IsValidAxesSet() )
3415         mxSecnAxesSet->Save( rStrm );
3416 
3417     // chart title and data labels
3418     lclSaveRecord( rStrm, mxTitle );
3419     maLabels.Save( rStrm );
3420 }
3421 
WriteBody(XclExpStream & rStrm)3422 void XclExpChChart::WriteBody( XclExpStream& rStrm )
3423 {
3424      rStrm << maRect;
3425 }
3426 
XclExpChartDrawing(const XclExpRoot & rRoot,const Reference<XModel> & rxModel,const Size & rChartSize)3427 XclExpChartDrawing::XclExpChartDrawing( const XclExpRoot& rRoot,
3428         const Reference< XModel >& rxModel, const Size& rChartSize ) :
3429     XclExpRoot( rRoot )
3430 {
3431     if( (rChartSize.Width() > 0) && (rChartSize.Height() > 0) )
3432     {
3433         ScfPropertySet aPropSet( rxModel );
3434         Reference< XShapes > xShapes;
3435         if( aPropSet.GetProperty( xShapes, EXC_CHPROP_ADDITIONALSHAPES ) && xShapes.is() && (xShapes->getCount() > 0) )
3436         {
3437             /*  Create a new independent object manager with own DFF stream for the
3438                 DGCONTAINER, pass global manager as parent for shared usage of
3439                 global DFF data (picture container etc.). */
3440             mxObjMgr.reset( new XclExpEmbeddedObjectManager( GetObjectManager(), rChartSize, EXC_CHART_TOTALUNITS, EXC_CHART_TOTALUNITS ) );
3441             // initialize the drawing object list
3442             mxObjMgr->StartSheet();
3443             // process the draw page (convert all shapes)
3444             mxObjRecs = mxObjMgr->ProcessDrawing( xShapes );
3445             // finalize the DFF stream
3446             mxObjMgr->EndDocument();
3447         }
3448     }
3449 }
3450 
~XclExpChartDrawing()3451 XclExpChartDrawing::~XclExpChartDrawing()
3452 {
3453 }
3454 
Save(XclExpStream & rStrm)3455 void XclExpChartDrawing::Save( XclExpStream& rStrm )
3456 {
3457     if( mxObjRecs )
3458         mxObjRecs->Save( rStrm );
3459 }
3460 
XclExpChart(const XclExpRoot & rRoot,Reference<XModel> const & xModel,const tools::Rectangle & rChartRect)3461 XclExpChart::XclExpChart( const XclExpRoot& rRoot, Reference< XModel > const & xModel, const tools::Rectangle& rChartRect ) :
3462     XclExpSubStream( EXC_BOF_CHART ),
3463     XclExpRoot( rRoot )
3464 {
3465     AppendNewRecord( new XclExpChartPageSettings( rRoot ) );
3466     AppendNewRecord( new XclExpBoolRecord( EXC_ID_PROTECT, false ) );
3467     AppendNewRecord( new XclExpChartDrawing( rRoot, xModel, rChartRect.GetSize() ) );
3468     AppendNewRecord( new XclExpUInt16Record( EXC_ID_CHUNITS, EXC_CHUNITS_TWIPS ) );
3469 
3470     Reference< XChartDocument > xChartDoc( xModel, UNO_QUERY );
3471     AppendNewRecord( new XclExpChChart( rRoot, xChartDoc, rChartRect ) );
3472 }
3473 
3474 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3475