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