xref: /core/sc/source/filter/excel/xetable.cxx (revision 507c6fc66cda7c1022008b3d1d4c087540cb2d18)
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 <xetable.hxx>
21 
22 #include <map>
23 #include <numeric>
24 #include <com/sun/star/i18n/ScriptType.hpp>
25 #include <scitems.hxx>
26 #include <svl/intitem.hxx>
27 #include <svl/numformat.hxx>
28 #include <svl/stritem.hxx>
29 #include <tools/UnitConversion.hxx>
30 #include <editeng/flditem.hxx>
31 #include <document.hxx>
32 #include <dociter.hxx>
33 #include <olinetab.hxx>
34 #include <formulacell.hxx>
35 #include <patattr.hxx>
36 #include <attrib.hxx>
37 #include <xehelper.hxx>
38 #include <xecontent.hxx>
39 #include <xeescher.hxx>
40 #include <xeextlst.hxx>
41 #include <xeformula.hxx>
42 #include <xlcontent.hxx>
43 #include <xltools.hxx>
44 #include <tokenarray.hxx>
45 #include <formula/errorcodes.hxx>
46 #include <comphelper/threadpool.hxx>
47 #include <oox/token/tokens.hxx>
48 #include <oox/export/utils.hxx>
49 
50 using namespace ::oox;
51 
52 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
53 
54 // Helper records for cell records
55 
XclExpStringRec(const XclExpRoot & rRoot,const OUString & rResult)56 XclExpStringRec::XclExpStringRec( const XclExpRoot& rRoot, const OUString& rResult ) :
57     XclExpRecord( EXC_ID3_STRING ),
58     mxResult( XclExpStringHelper::CreateString( rRoot, rResult ) )
59 {
60     OSL_ENSURE( (rRoot.GetBiff() <= EXC_BIFF5) || (mxResult->Len() > 0),
61         "XclExpStringRec::XclExpStringRec - empty result not allowed in BIFF8+" );
62     SetRecSize( mxResult->GetSize() );
63 }
64 
WriteBody(XclExpStream & rStrm)65 void XclExpStringRec::WriteBody( XclExpStream& rStrm )
66 {
67     rStrm << *mxResult;
68 }
69 
70 // Additional records for special formula ranges ==============================
71 
XclExpRangeFmlaBase(sal_uInt16 nRecId,sal_uInt32 nRecSize,const ScAddress & rScPos)72 XclExpRangeFmlaBase::XclExpRangeFmlaBase(
73         sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScAddress& rScPos ) :
74     XclExpRecord( nRecId, nRecSize ),
75     maXclRange( ScAddress::UNINITIALIZED ),
76     maBaseXclPos( ScAddress::UNINITIALIZED )
77 {
78     maBaseXclPos.Set( static_cast< sal_uInt16 >( rScPos.Col() ), static_cast< sal_uInt16 >( rScPos.Row() ) );
79     maXclRange.maFirst = maXclRange.maLast = maBaseXclPos;
80 }
81 
XclExpRangeFmlaBase(sal_uInt16 nRecId,sal_uInt32 nRecSize,const ScRange & rScRange)82 XclExpRangeFmlaBase::XclExpRangeFmlaBase(
83         sal_uInt16 nRecId, sal_uInt32 nRecSize, const ScRange& rScRange ) :
84     XclExpRecord( nRecId, nRecSize ),
85     maXclRange( ScAddress::UNINITIALIZED ),
86     maBaseXclPos( ScAddress::UNINITIALIZED )
87 {
88     maXclRange.Set(
89         static_cast< sal_uInt16 >( rScRange.aStart.Col() ),
90         static_cast< sal_uInt16 >( rScRange.aStart.Row() ),
91         static_cast< sal_uInt16 >( rScRange.aEnd.Col() ),
92         static_cast< sal_uInt16 >( rScRange.aEnd.Row() ) );
93     maBaseXclPos = maXclRange.maFirst;
94 }
95 
IsBasePos(sal_uInt16 nXclCol,sal_uInt32 nXclRow) const96 bool XclExpRangeFmlaBase::IsBasePos( sal_uInt16 nXclCol, sal_uInt32 nXclRow ) const
97 {
98     return (maBaseXclPos.mnCol == nXclCol) && (maBaseXclPos.mnRow == nXclRow);
99 }
100 
Extend(const ScAddress & rScPos)101 void XclExpRangeFmlaBase::Extend( const ScAddress& rScPos )
102 {
103     sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
104     sal_uInt32 nXclRow = static_cast< sal_uInt32 >( rScPos.Row() );
105     maXclRange.maFirst.mnCol = ::std::min( maXclRange.maFirst.mnCol, nXclCol );
106     maXclRange.maFirst.mnRow = ::std::min( maXclRange.maFirst.mnRow, nXclRow );
107     maXclRange.maLast.mnCol  = ::std::max( maXclRange.maLast.mnCol,  nXclCol );
108     maXclRange.maLast.mnRow  = ::std::max( maXclRange.maLast.mnRow,  nXclRow );
109 }
110 
WriteRangeAddress(XclExpStream & rStrm) const111 void XclExpRangeFmlaBase::WriteRangeAddress( XclExpStream& rStrm ) const
112 {
113     maXclRange.Write( rStrm, false );
114 }
115 
116 // Array formulas =============================================================
117 
XclExpArray(const XclTokenArrayRef & xTokArr,const ScRange & rScRange)118 XclExpArray::XclExpArray( const XclTokenArrayRef& xTokArr, const ScRange& rScRange ) :
119     XclExpRangeFmlaBase( EXC_ID3_ARRAY, 14 + xTokArr->GetSize(), rScRange ),
120     mxTokArr( xTokArr )
121 {
122 }
123 
CreateCellTokenArray(const XclExpRoot & rRoot) const124 XclTokenArrayRef XclExpArray::CreateCellTokenArray( const XclExpRoot& rRoot ) const
125 {
126     return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
127 }
128 
IsVolatile() const129 bool XclExpArray::IsVolatile() const
130 {
131     return mxTokArr->IsVolatile();
132 }
133 
WriteBody(XclExpStream & rStrm)134 void XclExpArray::WriteBody( XclExpStream& rStrm )
135 {
136     WriteRangeAddress( rStrm );
137     sal_uInt16 nFlags = EXC_ARRAY_DEFAULTFLAGS;
138     ::set_flag( nFlags, EXC_ARRAY_RECALC_ALWAYS, IsVolatile() );
139     rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
140 }
141 
XclExpArrayBuffer(const XclExpRoot & rRoot)142 XclExpArrayBuffer::XclExpArrayBuffer( const XclExpRoot& rRoot ) :
143     XclExpRoot( rRoot )
144 {
145 }
146 
CreateArray(const ScTokenArray & rScTokArr,const ScRange & rScRange)147 XclExpArrayRef XclExpArrayBuffer::CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange )
148 {
149     const ScAddress& rScPos = rScRange.aStart;
150     XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_MATRIX, rScTokArr, &rScPos );
151 
152     OSL_ENSURE( maRecMap.find( rScPos ) == maRecMap.end(), "XclExpArrayBuffer::CreateArray - array exists already" );
153     XclExpArrayRef& rxRec = maRecMap[ rScPos ];
154     rxRec = new XclExpArray( xTokArr, rScRange );
155     return rxRec;
156 }
157 
FindArray(const ScTokenArray & rScTokArr,const ScAddress & rBasePos) const158 XclExpArrayRef XclExpArrayBuffer::FindArray( const ScTokenArray& rScTokArr, const ScAddress& rBasePos ) const
159 {
160     XclExpArrayRef xRec;
161     // try to extract a matrix reference token
162     if (rScTokArr.GetLen() != 1)
163         // Must consist of a single reference token.
164         return xRec;
165 
166     const formula::FormulaToken* pToken = rScTokArr.GetArray()[0];
167     if (!pToken || pToken->GetOpCode() != ocMatRef)
168         // not a matrix reference token.
169         return xRec;
170 
171     const ScSingleRefData& rRef = *pToken->GetSingleRef();
172     ScAddress aAbsPos = rRef.toAbs(GetRoot().GetDoc(), rBasePos);
173     XclExpArrayMap::const_iterator it = maRecMap.find(aAbsPos);
174 
175     if (it != maRecMap.end())
176         xRec = it->second;
177     return xRec;
178 }
179 
180 // Shared formulas ============================================================
181 
XclExpShrfmla(const XclTokenArrayRef & xTokArr,const ScAddress & rScPos)182 XclExpShrfmla::XclExpShrfmla( const XclTokenArrayRef& xTokArr, const ScAddress& rScPos ) :
183     XclExpRangeFmlaBase( EXC_ID_SHRFMLA, 10 + xTokArr->GetSize(), rScPos ),
184     mxTokArr( xTokArr ),
185     mnUsedCount( 1 )
186 {
187 }
188 
ExtendRange(const ScAddress & rScPos)189 void XclExpShrfmla::ExtendRange( const ScAddress& rScPos )
190 {
191     Extend( rScPos );
192     ++mnUsedCount;
193 }
194 
CreateCellTokenArray(const XclExpRoot & rRoot) const195 XclTokenArrayRef XclExpShrfmla::CreateCellTokenArray( const XclExpRoot& rRoot ) const
196 {
197     return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
198 }
199 
IsVolatile() const200 bool XclExpShrfmla::IsVolatile() const
201 {
202     return mxTokArr->IsVolatile();
203 }
204 
WriteBody(XclExpStream & rStrm)205 void XclExpShrfmla::WriteBody( XclExpStream& rStrm )
206 {
207     WriteRangeAddress( rStrm );
208     rStrm << sal_uInt8( 0 ) << mnUsedCount << *mxTokArr;
209 }
210 
XclExpShrfmlaBuffer(const XclExpRoot & rRoot)211 XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) :
212     XclExpRoot( rRoot )
213 {
214 }
215 
IsValidTokenArray(const ScTokenArray & rArray) const216 bool XclExpShrfmlaBuffer::IsValidTokenArray( const ScTokenArray& rArray ) const
217 {
218     using namespace formula;
219 
220     FormulaToken** pTokens = rArray.GetArray();
221     sal_uInt16 nLen = rArray.GetLen();
222     for (sal_uInt16 i = 0; i < nLen; ++i)
223     {
224         const FormulaToken* p = pTokens[i];
225         switch (p->GetType())
226         {
227             case svSingleRef:
228             {
229                 const ScSingleRefData& rRefData = *p->GetSingleRef();
230                 if (!GetFormulaCompiler().IsRef2D(rRefData))
231                     // Excel's shared formula cannot include 3D reference.
232                     return false;
233             }
234             break;
235             case svDoubleRef:
236             {
237                 const ScComplexRefData& rRefData = *p->GetDoubleRef();
238                 if (!GetFormulaCompiler().IsRef2D(rRefData))
239                     // Excel's shared formula cannot include 3D reference.
240                     return false;
241             }
242             break;
243             case svExternalSingleRef:
244             case svExternalDoubleRef:
245             case svExternalName:
246                 // External references aren't allowed.
247                 return false;
248             default:
249                 ;
250         }
251     }
252     return true;
253 }
254 
CreateOrExtendShrfmla(const ScFormulaCell & rScCell,const ScAddress & rScPos)255 XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
256     const ScFormulaCell& rScCell, const ScAddress& rScPos )
257 {
258     XclExpShrfmlaRef xRec;
259     const ScTokenArray* pShrdScTokArr = rScCell.GetSharedCode();
260     if (!pShrdScTokArr)
261         // This formula cell is not shared formula cell.
262         return xRec;
263 
264     // Check to see if this shared formula contains any tokens that Excel's shared formula cannot handle.
265     if (maBadTokens.count(pShrdScTokArr) > 0)
266         // Already on the black list. Skip it.
267         return xRec;
268 
269     if (!IsValidTokenArray(*pShrdScTokArr))
270     {
271         // We can't export this as shared formula.
272         maBadTokens.insert(pShrdScTokArr);
273         return xRec;
274     }
275 
276     TokensType::iterator aIt = maRecMap.find(pShrdScTokArr);
277     if( aIt == maRecMap.end() )
278     {
279         // create a new record
280         XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_SHARED, *pShrdScTokArr, &rScPos );
281         xRec = new XclExpShrfmla( xTokArr, rScPos );
282         maRecMap[ pShrdScTokArr ] = xRec;
283     }
284     else
285     {
286         // extend existing record
287         OSL_ENSURE( aIt->second, "XclExpShrfmlaBuffer::CreateOrExtendShrfmla - missing record" );
288         xRec = aIt->second;
289         xRec->ExtendRange( rScPos );
290     }
291 
292     return xRec;
293 }
294 
295 // Multiple operations ========================================================
296 
XclExpTableop(const ScAddress & rScPos,const XclMultipleOpRefs & rRefs,sal_uInt8 nScMode)297 XclExpTableop::XclExpTableop( const ScAddress& rScPos,
298         const XclMultipleOpRefs& rRefs, sal_uInt8 nScMode ) :
299     XclExpRangeFmlaBase( EXC_ID3_TABLEOP, 16, rScPos ),
300     mnLastAppXclCol( static_cast< sal_uInt16 >( rScPos.Col() ) ),
301     mnColInpXclCol( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Col() ) ),
302     mnColInpXclRow( static_cast< sal_uInt16 >( rRefs.maColFirstScPos.Row() ) ),
303     mnRowInpXclCol( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Col() ) ),
304     mnRowInpXclRow( static_cast< sal_uInt16 >( rRefs.maRowFirstScPos.Row() ) ),
305     mnScMode( nScMode ),
306     mbValid( false )
307 {
308 }
309 
TryExtend(const ScAddress & rScPos,const XclMultipleOpRefs & rRefs)310 bool XclExpTableop::TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
311 {
312     sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
313     sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() );
314 
315     bool bOk = IsAppendable( nXclCol, nXclRow );
316     if( bOk )
317     {
318         SCCOL nFirstScCol  = static_cast< SCCOL >( maXclRange.maFirst.mnCol );
319         SCROW nFirstScRow  = static_cast< SCROW >( maXclRange.maFirst.mnRow );
320         SCCOL nColInpScCol = static_cast< SCCOL >( mnColInpXclCol );
321         SCROW nColInpScRow = static_cast< SCROW >( mnColInpXclRow );
322         SCCOL nRowInpScCol = static_cast< SCCOL >( mnRowInpXclCol );
323         SCROW nRowInpScRow = static_cast< SCROW >( mnRowInpXclRow );
324 
325         bOk =   ((mnScMode == 2) == rRefs.mbDblRefMode) &&
326                 (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
327                 (nColInpScCol == rRefs.maColFirstScPos.Col()) &&
328                 (nColInpScRow == rRefs.maColFirstScPos.Row()) &&
329                 (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
330                 (rScPos.Tab() == rRefs.maColRelScPos.Tab());
331 
332         if( bOk ) switch( mnScMode )
333         {
334             case 0:
335                 bOk =   (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
336                         (nFirstScRow  == rRefs.maFmlaScPos.Row() + 1) &&
337                         (nFirstScCol  == rRefs.maColRelScPos.Col() + 1) &&
338                         (rScPos.Row() == rRefs.maColRelScPos.Row());
339             break;
340             case 1:
341                 bOk =   (nFirstScCol  == rRefs.maFmlaScPos.Col() + 1) &&
342                         (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
343                         (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
344                         (nFirstScRow  == rRefs.maColRelScPos.Row() + 1);
345             break;
346             case 2:
347                 bOk =   (nFirstScCol  == rRefs.maFmlaScPos.Col() + 1) &&
348                         (nFirstScRow  == rRefs.maFmlaScPos.Row() + 1) &&
349                         (nFirstScCol  == rRefs.maColRelScPos.Col() + 1) &&
350                         (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
351                         (nRowInpScCol == rRefs.maRowFirstScPos.Col()) &&
352                         (nRowInpScRow == rRefs.maRowFirstScPos.Row()) &&
353                         (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
354                         (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
355                         (nFirstScRow  == rRefs.maRowRelScPos.Row() + 1) &&
356                         (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
357             break;
358             default:
359                 bOk = false;
360         }
361 
362         if( bOk )
363         {
364             // extend the cell range
365             OSL_ENSURE( IsAppendable( nXclCol, nXclRow ), "XclExpTableop::TryExtend - wrong cell address" );
366             Extend( rScPos );
367             mnLastAppXclCol = nXclCol;
368         }
369     }
370 
371     return bOk;
372 }
373 
Finalize()374 void XclExpTableop::Finalize()
375 {
376     // is the range complete? (last appended cell is in last column)
377     mbValid = maXclRange.maLast.mnCol == mnLastAppXclCol;
378     // if last row is incomplete, try to shorten the used range
379     if( !mbValid && (maXclRange.maFirst.mnRow < maXclRange.maLast.mnRow) )
380     {
381         --maXclRange.maLast.mnRow;
382         mbValid = true;
383     }
384 
385     // check if referred cells are outside of own range
386     if( !mbValid )
387         return;
388 
389     switch( mnScMode )
390     {
391     case 0:
392         mbValid =   (mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
393                     (mnColInpXclRow     < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
394     break;
395     case 1:
396         mbValid =   (mnColInpXclCol     < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
397                     (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow);
398     break;
399     case 2:
400         mbValid =   ((mnColInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnColInpXclCol > maXclRange.maLast.mnCol) ||
401                      (mnColInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnColInpXclRow > maXclRange.maLast.mnRow)) &&
402                     ((mnRowInpXclCol + 1 < maXclRange.maFirst.mnCol) || (mnRowInpXclCol > maXclRange.maLast.mnCol) ||
403                      (mnRowInpXclRow + 1 < maXclRange.maFirst.mnRow) || (mnRowInpXclRow > maXclRange.maLast.mnRow));
404     break;
405     }
406 }
407 
CreateCellTokenArray(const XclExpRoot & rRoot) const408 XclTokenArrayRef XclExpTableop::CreateCellTokenArray( const XclExpRoot& rRoot ) const
409 {
410     XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
411     return mbValid ?
412         rFmlaComp.CreateSpecialRefFormula( EXC_TOKID_TBL, maBaseXclPos ) :
413         rFmlaComp.CreateErrorFormula( EXC_ERR_NA );
414 }
415 
IsVolatile() const416 bool XclExpTableop::IsVolatile() const
417 {
418     return true;
419 }
420 
Save(XclExpStream & rStrm)421 void XclExpTableop::Save( XclExpStream& rStrm )
422 {
423     if( mbValid )
424         XclExpRangeFmlaBase::Save( rStrm );
425 }
426 
IsAppendable(sal_uInt16 nXclCol,sal_uInt16 nXclRow) const427 bool XclExpTableop::IsAppendable( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const
428 {
429     return  ((nXclCol == mnLastAppXclCol + 1) && (nXclRow == maXclRange.maFirst.mnRow)) ||
430             ((nXclCol == mnLastAppXclCol + 1) && (nXclCol <= maXclRange.maLast.mnCol) && (nXclRow == maXclRange.maLast.mnRow)) ||
431             ((mnLastAppXclCol == maXclRange.maLast.mnCol) && (nXclCol == maXclRange.maFirst.mnCol) && (nXclRow == maXclRange.maLast.mnRow + 1));
432 }
433 
WriteBody(XclExpStream & rStrm)434 void XclExpTableop::WriteBody( XclExpStream& rStrm )
435 {
436     sal_uInt16 nFlags = EXC_TABLEOP_DEFAULTFLAGS;
437     ::set_flag( nFlags, EXC_TABLEOP_RECALC_ALWAYS, IsVolatile() );
438     switch( mnScMode )
439     {
440         case 1: ::set_flag( nFlags, EXC_TABLEOP_ROW );  break;
441         case 2: ::set_flag( nFlags, EXC_TABLEOP_BOTH ); break;
442     }
443 
444     WriteRangeAddress( rStrm );
445     rStrm << nFlags;
446     if( mnScMode == 2 )
447         rStrm << mnRowInpXclRow << mnRowInpXclCol << mnColInpXclRow << mnColInpXclCol;
448     else
449         rStrm << mnColInpXclRow << mnColInpXclCol << sal_uInt32( 0 );
450 }
451 
XclExpTableopBuffer(const XclExpRoot & rRoot)452 XclExpTableopBuffer::XclExpTableopBuffer( const XclExpRoot& rRoot ) :
453     XclExpRoot( rRoot )
454 {
455 }
456 
CreateOrExtendTableop(const ScTokenArray & rScTokArr,const ScAddress & rScPos)457 XclExpTableopRef XclExpTableopBuffer::CreateOrExtendTableop(
458         const ScTokenArray& rScTokArr, const ScAddress& rScPos )
459 {
460     XclExpTableopRef xRec;
461 
462     // try to extract cell references of a multiple operations formula
463     XclMultipleOpRefs aRefs;
464     if (XclTokenArrayHelper::GetMultipleOpRefs(GetDoc(), aRefs, rScTokArr, rScPos))
465     {
466         // try to find an existing TABLEOP record for this cell position
467         for( size_t nPos = 0, nSize = maTableopList.GetSize(); !xRec && (nPos < nSize); ++nPos )
468         {
469             XclExpTableop* xTempRec = maTableopList.GetRecord( nPos );
470             if( xTempRec->TryExtend( rScPos, aRefs ) )
471                 xRec = xTempRec;
472         }
473 
474         // no record found, or found record not extensible
475         if( !xRec )
476             xRec = TryCreate( rScPos, aRefs );
477     }
478 
479     return xRec;
480 }
481 
Finalize()482 void XclExpTableopBuffer::Finalize()
483 {
484     for( size_t nPos = 0, nSize = maTableopList.GetSize(); nPos < nSize; ++nPos )
485         maTableopList.GetRecord( nPos )->Finalize();
486 }
487 
TryCreate(const ScAddress & rScPos,const XclMultipleOpRefs & rRefs)488 XclExpTableopRef XclExpTableopBuffer::TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
489 {
490     sal_uInt8 nScMode = 0;
491     bool bOk =  (rScPos.Tab() == rRefs.maFmlaScPos.Tab()) &&
492                 (rScPos.Tab() == rRefs.maColFirstScPos.Tab()) &&
493                 (rScPos.Tab() == rRefs.maColRelScPos.Tab());
494 
495     if( bOk )
496     {
497         if( rRefs.mbDblRefMode )
498         {
499             nScMode = 2;
500             bOk =   (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
501                     (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
502                     (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
503                     (rScPos.Row() == rRefs.maColRelScPos.Row()) &&
504                     (rScPos.Tab() == rRefs.maRowFirstScPos.Tab()) &&
505                     (rScPos.Col() == rRefs.maRowRelScPos.Col()) &&
506                     (rScPos.Row() == rRefs.maRowRelScPos.Row() + 1) &&
507                     (rScPos.Tab() == rRefs.maRowRelScPos.Tab());
508         }
509         else if( (rScPos.Col() == rRefs.maFmlaScPos.Col()) &&
510                  (rScPos.Row() == rRefs.maFmlaScPos.Row() + 1) &&
511                  (rScPos.Col() == rRefs.maColRelScPos.Col() + 1) &&
512                  (rScPos.Row() == rRefs.maColRelScPos.Row()) )
513         {
514             nScMode = 0;
515         }
516         else if( (rScPos.Col() == rRefs.maFmlaScPos.Col() + 1) &&
517                  (rScPos.Row() == rRefs.maFmlaScPos.Row()) &&
518                  (rScPos.Col() == rRefs.maColRelScPos.Col()) &&
519                  (rScPos.Row() == rRefs.maColRelScPos.Row() + 1) )
520         {
521             nScMode = 1;
522         }
523         else
524         {
525             bOk = false;
526         }
527     }
528 
529     XclExpTableopRef xRec;
530     if( bOk )
531     {
532         xRec = new XclExpTableop( rScPos, rRefs, nScMode );
533         maTableopList.AppendRecord( xRec );
534     }
535 
536     return xRec;
537 }
538 
539 // Cell records
540 
XclExpCellBase(sal_uInt16 nRecId,std::size_t nContSize,const XclAddress & rXclPos)541 XclExpCellBase::XclExpCellBase(
542         sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos ) :
543     XclExpRecord( nRecId, nContSize + 4 ),
544     maXclPos( rXclPos )
545 {
546 }
547 
IsMultiLineText() const548 bool XclExpCellBase::IsMultiLineText() const
549 {
550     return false;
551 }
552 
TryMerge(const XclExpCellBase &)553 bool XclExpCellBase::TryMerge( const XclExpCellBase& /*rCell*/ )
554 {
555     return false;
556 }
557 
GetBlankXFIndexes(ScfUInt16Vec &) const558 void XclExpCellBase::GetBlankXFIndexes( ScfUInt16Vec& /*rXFIndexes*/ ) const
559 {
560     // default: do nothing
561 }
562 
RemoveUnusedBlankCells(const ScfUInt16Vec &,size_t)563 void XclExpCellBase::RemoveUnusedBlankCells( const ScfUInt16Vec& /*rXFIndexes*/, size_t /*nStartAllNotFound*/ )
564 {
565     // default: do nothing
566 }
567 
568 // Single cell records ========================================================
569 
XclExpSingleCellBase(sal_uInt16 nRecId,std::size_t nContSize,const XclAddress & rXclPos,sal_uInt32 nXFId)570 XclExpSingleCellBase::XclExpSingleCellBase(
571         sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos, sal_uInt32 nXFId ) :
572     XclExpCellBase( nRecId, 2, rXclPos ),
573     maXFId( nXFId ),
574     mnContSize( nContSize )
575 {
576 }
577 
XclExpSingleCellBase(const XclExpRoot & rRoot,sal_uInt16 nRecId,std::size_t nContSize,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_Int16 nScript,sal_uInt32 nForcedXFId)578 XclExpSingleCellBase::XclExpSingleCellBase( const XclExpRoot& rRoot,
579         sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos,
580         const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uInt32 nForcedXFId ) :
581     XclExpCellBase( nRecId, 2, rXclPos ),
582     maXFId( nForcedXFId ),
583     mnContSize( nContSize )
584 {
585     if( GetXFId() == EXC_XFID_NOTFOUND )
586         SetXFId( rRoot.GetXFBuffer().Insert( pPattern, nScript ) );
587 }
588 
GetLastXclCol() const589 sal_uInt16 XclExpSingleCellBase::GetLastXclCol() const
590 {
591     return GetXclCol();
592 }
593 
GetFirstXFId() const594 sal_uInt32 XclExpSingleCellBase::GetFirstXFId() const
595 {
596     return GetXFId();
597 }
598 
IsEmpty() const599 bool XclExpSingleCellBase::IsEmpty() const
600 {
601     return false;
602 }
603 
ConvertXFIndexes(const XclExpRoot & rRoot)604 void XclExpSingleCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
605 {
606     maXFId.ConvertXFIndex( rRoot );
607 }
608 
Save(XclExpStream & rStrm)609 void XclExpSingleCellBase::Save( XclExpStream& rStrm )
610 {
611     OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
612     AddRecSize( mnContSize );
613     XclExpCellBase::Save( rStrm );
614 }
615 
WriteBody(XclExpStream & rStrm)616 void XclExpSingleCellBase::WriteBody( XclExpStream& rStrm )
617 {
618     rStrm << static_cast<sal_uInt16> (GetXclRow()) << GetXclCol() << maXFId.mnXFIndex;
619     WriteContents( rStrm );
620 }
621 
XclExpNumberCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,double fValue)622 XclExpNumberCell::XclExpNumberCell(
623         const XclExpRoot& rRoot, const XclAddress& rXclPos,
624         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, double fValue ) :
625     // #i41210# always use latin script for number cells - may look wrong for special number formats...
626     XclExpSingleCellBase( rRoot, EXC_ID3_NUMBER, 8, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
627     mfValue( fValue )
628 {
629 }
630 
lcl_GetStyleId(const XclExpXmlStream & rStrm,sal_uInt32 nXFIndex)631 static OString lcl_GetStyleId( const XclExpXmlStream& rStrm, sal_uInt32 nXFIndex )
632 {
633     return OString::number( rStrm.GetRoot().GetXFBuffer()
634             .GetXmlCellIndex( nXFIndex ) );
635 }
636 
lcl_GetStyleId(const XclExpXmlStream & rStrm,const XclExpCellBase & rCell)637 static OString lcl_GetStyleId( const XclExpXmlStream& rStrm, const XclExpCellBase& rCell )
638 {
639     sal_uInt32 nXFId    = rCell.GetFirstXFId();
640     sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( nXFId );
641     return lcl_GetStyleId( rStrm, nXFIndex );
642 }
643 
SaveXml(XclExpXmlStream & rStrm)644 void XclExpNumberCell::SaveXml( XclExpXmlStream& rStrm )
645 {
646     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
647     rWorksheet->startElement( XML_c,
648             XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
649             XML_s, lcl_GetStyleId(rStrm, *this),
650             XML_t, "n"
651             // OOXTODO: XML_cm, XML_vm, XML_ph
652     );
653     rWorksheet->startElement(XML_v);
654     rWorksheet->write( mfValue );
655     rWorksheet->endElement( XML_v );
656     rWorksheet->endElement( XML_c );
657 }
658 
WriteContents(XclExpStream & rStrm)659 void XclExpNumberCell::WriteContents( XclExpStream& rStrm )
660 {
661     rStrm << mfValue;
662 }
663 
XclExpBooleanCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,bool bValue)664 XclExpBooleanCell::XclExpBooleanCell(
665         const XclExpRoot& rRoot, const XclAddress& rXclPos,
666         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, bool bValue ) :
667     // #i41210# always use latin script for boolean cells
668     XclExpSingleCellBase( rRoot, EXC_ID3_BOOLERR, 2, rXclPos, pPattern, ApiScriptType::LATIN, nForcedXFId ),
669     mbValue( bValue )
670 {
671 }
672 
SaveXml(XclExpXmlStream & rStrm)673 void XclExpBooleanCell::SaveXml( XclExpXmlStream& rStrm )
674 {
675     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
676     rWorksheet->startElement( XML_c,
677             XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
678             XML_s, lcl_GetStyleId(rStrm, *this),
679             XML_t, "b"
680             // OOXTODO: XML_cm, XML_vm, XML_ph
681     );
682     rWorksheet->startElement( XML_v );
683     rWorksheet->write( mbValue ? "1" : "0" );
684     rWorksheet->endElement( XML_v );
685     rWorksheet->endElement( XML_c );
686 }
687 
WriteContents(XclExpStream & rStrm)688 void XclExpBooleanCell::WriteContents( XclExpStream& rStrm )
689 {
690     rStrm << sal_uInt16( mbValue ? 1 : 0 ) << EXC_BOOLERR_BOOL;
691 }
692 
XclExpLabelCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,const OUString & rStr)693 XclExpLabelCell::XclExpLabelCell(
694         const XclExpRoot& rRoot, const XclAddress& rXclPos,
695         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, const OUString& rStr ) :
696     XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
697 {
698     sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
699     XclExpStringRef xText = XclExpStringHelper::CreateCellString(
700         rRoot, rStr, pPattern, XclStrFlags::NONE, nMaxLen);
701     Init( rRoot, pPattern, xText );
702 }
703 
XclExpLabelCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,const EditTextObject * pEditText,XclExpHyperlinkHelper & rLinkHelper)704 XclExpLabelCell::XclExpLabelCell(
705         const XclExpRoot& rRoot, const XclAddress& rXclPos,
706         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
707         const EditTextObject* pEditText, XclExpHyperlinkHelper& rLinkHelper ) :
708     XclExpSingleCellBase( EXC_ID3_LABEL, 0, rXclPos, nForcedXFId )
709 {
710     sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
711 
712     XclExpStringRef xText;
713     if (pEditText)
714         xText = XclExpStringHelper::CreateCellString(
715             rRoot, *pEditText, pPattern, rLinkHelper, XclStrFlags::NONE, nMaxLen);
716     else
717         xText = XclExpStringHelper::CreateCellString(
718             rRoot, OUString(), pPattern, XclStrFlags::NONE, nMaxLen);
719 
720     Init( rRoot, pPattern, xText );
721 }
722 
IsMultiLineText() const723 bool XclExpLabelCell::IsMultiLineText() const
724 {
725     return mbLineBreak || mxText->HasNewline();
726 }
727 
Init(const XclExpRoot & rRoot,const ScPatternAttr * pPattern,XclExpStringRef const & xText)728 void XclExpLabelCell::Init( const XclExpRoot& rRoot,
729         const ScPatternAttr* pPattern, XclExpStringRef const & xText )
730 {
731     OSL_ENSURE( xText && xText->Len(), "XclExpLabelCell::XclExpLabelCell - empty string passed" );
732     mxText = xText;
733     mnSstIndex = 0;
734 
735     const XclFormatRunVec& rFormats = mxText->GetFormats();
736     // remove formatting of the leading run if the entire string
737     // is equally formatted
738     sal_uInt16 nXclFont = EXC_FONT_NOTFOUND;
739     if( rFormats.size() == 1 )
740         nXclFont = mxText->RemoveLeadingFont();
741     else
742         nXclFont = mxText->GetLeadingFont();
743 
744     // create cell format
745     if( GetXFId() == EXC_XFID_NOTFOUND )
746     {
747         OSL_ENSURE(nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found");
748 
749         // Buggy Excel behaviour - newlines are ignored unless wrap-text is enabled,
750         // so always force text-wrapping (unless it was imported that way and not modified).
751         bool bForceLineBreak = mxText->HasNewline() && !mxText->IsSingleLineForMultipleParagraphs();
752         SetXFId(rRoot.GetXFBuffer().InsertWithFont(
753             pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak));
754     }
755 
756     // get auto-wrap attribute from cell format
757     const XclExpXF* pXF = rRoot.GetXFBuffer().GetXFById( GetXFId() );
758     mbLineBreak = pXF && pXF->GetAlignmentData().mbLineBreak;
759 
760     // initialize the record contents
761     switch( rRoot.GetBiff() )
762     {
763         case EXC_BIFF5:
764             // BIFF5-BIFF7: create a LABEL or RSTRING record
765             OSL_ENSURE( mxText->Len() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::XclExpLabelCell - string too long" );
766             SetContSize( mxText->GetSize() );
767             // formatted string is exported in an RSTRING record
768             if( mxText->IsRich() )
769             {
770                 OSL_ENSURE( mxText->GetFormatsCount() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::WriteContents - too many formats" );
771                 mxText->LimitFormatCount( EXC_LABEL_MAXLEN );
772                 SetRecId( EXC_ID_RSTRING );
773                 SetContSize( GetContSize() + 1 + 2 * mxText->GetFormatsCount() );
774             }
775         break;
776         case EXC_BIFF8:
777             // BIFF8+: create a LABELSST record
778             mnSstIndex = rRoot.GetSst().Insert( xText );
779             SetRecId( EXC_ID_LABELSST );
780             SetContSize( 4 );
781         break;
782         default:    DBG_ERROR_BIFF();
783     }
784 }
785 
SaveXml(XclExpXmlStream & rStrm)786 void XclExpLabelCell::SaveXml( XclExpXmlStream& rStrm )
787 {
788     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
789     rWorksheet->startElement( XML_c,
790             XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
791             XML_s, lcl_GetStyleId(rStrm, *this),
792             XML_t, "s"
793             // OOXTODO: XML_cm, XML_vm, XML_ph
794     );
795     rWorksheet->startElement( XML_v );
796     rWorksheet->write( static_cast<sal_Int32>(mnSstIndex) );
797     rWorksheet->endElement( XML_v );
798     rWorksheet->endElement( XML_c );
799 }
800 
WriteContents(XclExpStream & rStrm)801 void XclExpLabelCell::WriteContents( XclExpStream& rStrm )
802 {
803     switch( rStrm.GetRoot().GetBiff() )
804     {
805         case EXC_BIFF5:
806             rStrm << *mxText;
807             if( mxText->IsRich() )
808             {
809                 rStrm << static_cast< sal_uInt8 >( mxText->GetFormatsCount() );
810                 mxText->WriteFormats( rStrm );
811             }
812         break;
813         case EXC_BIFF8:
814             rStrm << mnSstIndex;
815         break;
816         default:    DBG_ERROR_BIFF();
817     }
818 }
819 
XclExpFormulaCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,const ScFormulaCell & rScFmlaCell,XclExpArrayBuffer & rArrayBfr,XclExpShrfmlaBuffer & rShrfmlaBfr,XclExpTableopBuffer & rTableopBfr)820 XclExpFormulaCell::XclExpFormulaCell(
821         const XclExpRoot& rRoot, const XclAddress& rXclPos,
822         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
823         const ScFormulaCell& rScFmlaCell,
824         XclExpArrayBuffer& rArrayBfr,
825         XclExpShrfmlaBuffer& rShrfmlaBfr,
826         XclExpTableopBuffer& rTableopBfr ) :
827     XclExpSingleCellBase( EXC_ID2_FORMULA, 0, rXclPos, nForcedXFId ),
828     mrScFmlaCell( const_cast< ScFormulaCell& >( rScFmlaCell ) )
829 {
830     // *** Find result number format overwriting cell number format *** -------
831 
832     if( GetXFId() == EXC_XFID_NOTFOUND )
833     {
834         SvNumberFormatter& rFormatter = rRoot.GetFormatter();
835         XclExpNumFmtBuffer& rNumFmtBfr = rRoot.GetNumFmtBuffer();
836 
837         // current cell number format
838         sal_uInt32 nScNumFmt = pPattern ?
839             pPattern->GetItem( ATTR_VALUE_FORMAT ).GetValue() :
840             rNumFmtBfr.GetStandardFormat();
841 
842         // alternative number format passed to XF buffer
843         sal_uInt32 nAltScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
844         /*  Xcl doesn't know Boolean number formats, we write
845             "TRUE";"FALSE" (language dependent). Don't do it for automatic
846             formula formats, because Excel gets them right. */
847         /*  #i8640# Don't set text format, if we have string results. */
848         SvNumFormatType nFormatType = mrScFmlaCell.GetFormatType();
849         if( ((nScNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0) &&
850                 (nFormatType != SvNumFormatType::LOGICAL) &&
851                 (nFormatType != SvNumFormatType::TEXT) )
852             nAltScNumFmt = nScNumFmt;
853         /*  If cell number format is Boolean and automatic formula
854             format is Boolean don't write that ugly special format. */
855         else if( (nFormatType == SvNumFormatType::LOGICAL) &&
856                 (rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL) )
857             nAltScNumFmt = rNumFmtBfr.GetStandardFormat();
858 
859         // #i41420# find script type according to result type (always latin for numeric results)
860         sal_Int16 nScript = ApiScriptType::LATIN;
861         bool bForceLineBreak = false;
862         if( nFormatType == SvNumFormatType::TEXT )
863         {
864             OUString aResult = mrScFmlaCell.GetString().getString();
865             bForceLineBreak = mrScFmlaCell.IsMultilineResult();
866             nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult );
867         }
868         SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) );
869     }
870 
871     // *** Convert the formula token array *** --------------------------------
872 
873     ScAddress aScPos( static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), rRoot.GetCurrScTab() );
874     const ScTokenArray& rScTokArr = *mrScFmlaCell.GetCode();
875 
876     // first try to create multiple operations
877     mxAddRec = rTableopBfr.CreateOrExtendTableop( rScTokArr, aScPos );
878 
879     // no multiple operation found - try to create matrix formula
880     if( !mxAddRec )
881         switch( mrScFmlaCell.GetMatrixFlag() )
882         {
883             case ScMatrixMode::Formula:
884             {
885                 // origin of the matrix - find the used matrix range
886                 SCCOL nMatWidth;
887                 SCROW nMatHeight;
888                 mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
889                 OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
890                 ScRange aMatScRange( aScPos );
891                 ScAddress& rMatEnd = aMatScRange.aEnd;
892                 rMatEnd.IncCol( static_cast< SCCOL >( nMatWidth - 1 ) );
893                 rMatEnd.IncRow( static_cast< SCROW >( nMatHeight - 1 ) );
894                 // reduce to valid range (range keeps valid, because start position IS valid)
895                 rRoot.GetAddressConverter().ValidateRange( aMatScRange, true );
896                 // create the ARRAY record
897                 mxAddRec = rArrayBfr.CreateArray( rScTokArr, aMatScRange );
898             }
899             break;
900             case ScMatrixMode::Reference:
901             {
902                 // other formula cell covered by a matrix - find the ARRAY record
903                 mxAddRec = rArrayBfr.FindArray(rScTokArr, aScPos);
904                 // should always be found, if Calc document is not broken
905                 OSL_ENSURE( mxAddRec, "XclExpFormulaCell::XclExpFormulaCell - no matrix found" );
906             }
907             break;
908             default:;
909         }
910 
911     // no matrix found - try to create shared formula
912     if( !mxAddRec )
913         mxAddRec = rShrfmlaBfr.CreateOrExtendShrfmla(mrScFmlaCell, aScPos);
914 
915     // no shared formula found - create a simple cell formula
916     if( !mxAddRec )
917         mxTokArr = rRoot.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL, rScTokArr, &aScPos );
918 }
919 
Save(XclExpStream & rStrm)920 void XclExpFormulaCell::Save( XclExpStream& rStrm )
921 {
922     // create token array for FORMULA cells with additional record
923     if( mxAddRec )
924         mxTokArr = mxAddRec->CreateCellTokenArray( rStrm.GetRoot() );
925 
926     // FORMULA record itself
927     OSL_ENSURE( mxTokArr, "XclExpFormulaCell::Save - missing token array" );
928     if( !mxTokArr )
929         mxTokArr = rStrm.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA );
930     SetContSize( 16 + mxTokArr->GetSize() );
931     XclExpSingleCellBase::Save( rStrm );
932 
933     // additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record
934     if( mxAddRec && mxAddRec->IsBasePos( GetXclCol(), GetXclRow() ) )
935         mxAddRec->Save( rStrm );
936 
937     // STRING record for string result
938     if( mxStringRec )
939         mxStringRec->Save( rStrm );
940 }
941 
SaveXml(XclExpXmlStream & rStrm)942 void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
943 {
944     const char* sType = nullptr;
945     OUString    sValue;
946     XclXmlUtils::GetFormulaTypeAndValue( mrScFmlaCell, sType, sValue );
947     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
948     rWorksheet->startElement( XML_c,
949             XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
950             XML_s, lcl_GetStyleId(rStrm, *this),
951             XML_t, sType
952             // OOXTODO: XML_cm, XML_vm, XML_ph
953     );
954 
955     bool bWriteFormula = true;
956     bool bTagStarted = false;
957     ScAddress aScPos( static_cast< SCCOL >( GetXclPos().mnCol ),
958             static_cast< SCROW >( GetXclPos().mnRow ), rStrm.GetRoot().GetCurrScTab() );
959 
960     switch (mrScFmlaCell.GetMatrixFlag())
961     {
962         case ScMatrixMode::NONE:
963             break;
964         case ScMatrixMode::Reference:
965             bWriteFormula = false;
966             break;
967         case ScMatrixMode::Formula:
968             {
969                 // origin of the matrix - find the used matrix range
970                 SCCOL nMatWidth;
971                 SCROW nMatHeight;
972                 mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
973                 OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
974                 ScRange aMatScRange( aScPos );
975                 ScAddress& rMatEnd = aMatScRange.aEnd;
976                 rMatEnd.IncCol( static_cast< SCCOL >( nMatWidth - 1 ) );
977                 rMatEnd.IncRow( static_cast< SCROW >( nMatHeight - 1 ) );
978                 // reduce to valid range (range keeps valid, because start position IS valid
979                 rStrm.GetRoot().GetAddressConverter().ValidateRange( aMatScRange, true );
980 
981                 OStringBuffer sFmlaCellRange;
982                 if (rStrm.GetRoot().GetDoc().ValidRange(aMatScRange))
983                 {
984                     // calculate the cell range.
985                     sFmlaCellRange.append( XclXmlUtils::ToOString(
986                                 rStrm.GetRoot().GetStringBuf(), aMatScRange.aStart )
987                                 + OString::Concat(":"));
988                     sFmlaCellRange.append( XclXmlUtils::ToOString(
989                                     rStrm.GetRoot().GetStringBuf(), aMatScRange.aEnd ));
990                 }
991 
992                 if (    aMatScRange.aStart.Col() == GetXclPos().mnCol &&
993                         aMatScRange.aStart.Row() == static_cast<SCROW>(GetXclPos().mnRow))
994                 {
995                     rWorksheet->startElement( XML_f,
996                             XML_aca, ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
997                                 (mxAddRec && mxAddRec->IsVolatile())),
998                             XML_t, mxAddRec ? "array" : nullptr,
999                             XML_ref, !sFmlaCellRange.isEmpty()? sFmlaCellRange.getStr() : nullptr
1000                             // OOXTODO: XML_dt2D,   bool
1001                             // OOXTODO: XML_dtr,    bool
1002                             // OOXTODO: XML_del1,   bool
1003                             // OOXTODO: XML_del2,   bool
1004                             // OOXTODO: XML_r1,     ST_CellRef
1005                             // OOXTODO: XML_r2,     ST_CellRef
1006                             // OOXTODO: XML_ca,     bool
1007                             // OOXTODO: XML_si,     uint
1008                             // OOXTODO: XML_bx      bool
1009                     );
1010                     bTagStarted = true;
1011                 }
1012             }
1013             break;
1014     }
1015 
1016     if (bWriteFormula)
1017     {
1018         if (!bTagStarted)
1019         {
1020             rWorksheet->startElement( XML_f,
1021                     XML_aca, ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
1022                         (mxAddRec && mxAddRec->IsVolatile()) ) );
1023         }
1024         rWorksheet->writeEscaped( XclXmlUtils::ToOUString(
1025                     rStrm.GetRoot().GetCompileFormulaContext(), mrScFmlaCell.aPos, mrScFmlaCell.GetCode(),
1026                     mrScFmlaCell.GetErrCode()));
1027         rWorksheet->endElement( XML_f );
1028     }
1029 
1030     if( strcmp( sType, "inlineStr" ) == 0 )
1031     {
1032         rWorksheet->startElement(XML_is);
1033         rWorksheet->startElement(XML_t);
1034         rWorksheet->writeEscaped( sValue );
1035         rWorksheet->endElement( XML_t );
1036         rWorksheet->endElement( XML_is );
1037     }
1038     else
1039     {
1040         rWorksheet->startElement(XML_v);
1041         rWorksheet->writeEscaped( sValue );
1042         rWorksheet->endElement( XML_v );
1043     }
1044     rWorksheet->endElement( XML_c );
1045 }
1046 
WriteContents(XclExpStream & rStrm)1047 void XclExpFormulaCell::WriteContents( XclExpStream& rStrm )
1048 {
1049     FormulaError nScErrCode = mrScFmlaCell.GetErrCode();
1050     if( nScErrCode != FormulaError::NONE )
1051     {
1052         rStrm << EXC_FORMULA_RES_ERROR << sal_uInt8( 0 )
1053             << XclTools::GetXclErrorCode( nScErrCode )
1054             << sal_uInt8( 0 ) << sal_uInt16( 0 )
1055             << sal_uInt16( 0xFFFF );
1056     }
1057     else
1058     {
1059         // result of the formula
1060         switch( mrScFmlaCell.GetFormatType() )
1061         {
1062             case SvNumFormatType::NUMBER:
1063                 {
1064                     // either value or error code
1065                     rStrm << mrScFmlaCell.GetValue();
1066                 }
1067                 break;
1068 
1069             case SvNumFormatType::TEXT:
1070                 {
1071                     OUString aResult = mrScFmlaCell.GetString().getString();
1072                     if( !aResult.isEmpty() || (rStrm.GetRoot().GetBiff() <= EXC_BIFF5) )
1073                     {
1074                         rStrm << EXC_FORMULA_RES_STRING;
1075                         mxStringRec = new XclExpStringRec( rStrm.GetRoot(), aResult );
1076                     }
1077                     else
1078                         rStrm << EXC_FORMULA_RES_EMPTY;     // BIFF8 only
1079                     rStrm << sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
1080                 }
1081                 break;
1082 
1083             case SvNumFormatType::LOGICAL:
1084                 {
1085                     sal_uInt8 nXclValue = (mrScFmlaCell.GetValue() == 0.0) ? 0 : 1;
1086                     rStrm << EXC_FORMULA_RES_BOOL << sal_uInt8( 0 )
1087                         << nXclValue << sal_uInt8( 0 ) << sal_uInt16( 0 )
1088                         << sal_uInt16( 0xFFFF );
1089                 }
1090                 break;
1091 
1092             default:
1093                 rStrm << mrScFmlaCell.GetValue();
1094         }
1095     }
1096 
1097     // flags and formula token array
1098     sal_uInt16 nFlags = EXC_FORMULA_DEFAULTFLAGS;
1099     ::set_flag( nFlags, EXC_FORMULA_RECALC_ALWAYS, mxTokArr->IsVolatile() || (mxAddRec && mxAddRec->IsVolatile()) );
1100     ::set_flag( nFlags, EXC_FORMULA_SHARED, mxAddRec && (mxAddRec->GetRecId() == EXC_ID_SHRFMLA) );
1101     rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
1102 }
1103 
1104 // Multiple cell records ======================================================
1105 
XclExpMultiCellBase(sal_uInt16 nRecId,sal_uInt16 nMulRecId,std::size_t nContSize,const XclAddress & rXclPos)1106 XclExpMultiCellBase::XclExpMultiCellBase(
1107         sal_uInt16 nRecId, sal_uInt16 nMulRecId, std::size_t nContSize, const XclAddress& rXclPos ) :
1108     XclExpCellBase( nRecId, 0, rXclPos ),
1109     mnMulRecId( nMulRecId ),
1110     mnContSize( nContSize )
1111 {
1112 }
1113 
GetLastXclCol() const1114 sal_uInt16 XclExpMultiCellBase::GetLastXclCol() const
1115 {
1116     return GetXclCol() + GetCellCount() - 1;
1117 }
1118 
GetFirstXFId() const1119 sal_uInt32 XclExpMultiCellBase::GetFirstXFId() const
1120 {
1121     return maXFIds.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds.front().mnXFId;
1122 }
1123 
IsEmpty() const1124 bool XclExpMultiCellBase::IsEmpty() const
1125 {
1126     return maXFIds.empty();
1127 }
1128 
ConvertXFIndexes(const XclExpRoot & rRoot)1129 void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
1130 {
1131     for( auto& rXFId : maXFIds )
1132         rXFId.ConvertXFIndex( rRoot );
1133 }
1134 
Save(XclExpStream & rStrm)1135 void XclExpMultiCellBase::Save( XclExpStream& rStrm )
1136 {
1137     OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1138 
1139     XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1140     XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1141     XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1142     sal_uInt16 nBegXclCol = GetXclCol();
1143     sal_uInt16 nEndXclCol = nBegXclCol;
1144 
1145     while( aRangeEnd != aEnd )
1146     {
1147         // find begin of next used XF range
1148         aRangeBeg = aRangeEnd;
1149         nBegXclCol = nEndXclCol;
1150         while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1151         {
1152             nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1153             ++aRangeBeg;
1154         }
1155         // find end of next used XF range
1156         aRangeEnd = aRangeBeg;
1157         nEndXclCol = nBegXclCol;
1158         while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1159         {
1160             nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1161             ++aRangeEnd;
1162         }
1163 
1164         // export this range as a record
1165         if( aRangeBeg != aRangeEnd )
1166         {
1167             sal_uInt16 nCount = nEndXclCol - nBegXclCol;
1168             bool bIsMulti = nCount > 1;
1169             std::size_t nTotalSize = GetRecSize() + (2 + mnContSize) * nCount;
1170             if( bIsMulti ) nTotalSize += 2;
1171 
1172             rStrm.StartRecord( bIsMulti ? mnMulRecId : GetRecId(), nTotalSize );
1173             rStrm << static_cast<sal_uInt16> (GetXclRow()) << nBegXclCol;
1174 
1175             sal_uInt16 nRelCol = nBegXclCol - GetXclCol();
1176             for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1177             {
1178                 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1179                 {
1180                     rStrm << aIt->mnXFIndex;
1181                     WriteContents( rStrm, nRelCol );
1182                     ++nRelCol;
1183                 }
1184             }
1185             if( bIsMulti )
1186                 rStrm << static_cast< sal_uInt16 >( nEndXclCol - 1 );
1187             rStrm.EndRecord();
1188         }
1189     }
1190 }
1191 
SaveXml(XclExpXmlStream & rStrm)1192 void XclExpMultiCellBase::SaveXml( XclExpXmlStream& rStrm )
1193 {
1194     XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1195     XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1196     XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1197     sal_uInt16 nBegXclCol = GetXclCol();
1198     sal_uInt16 nEndXclCol = nBegXclCol;
1199 
1200     while( aRangeEnd != aEnd )
1201     {
1202         // find begin of next used XF range
1203         aRangeBeg = aRangeEnd;
1204         nBegXclCol = nEndXclCol;
1205         while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1206         {
1207             nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1208             ++aRangeBeg;
1209         }
1210         // find end of next used XF range
1211         aRangeEnd = aRangeBeg;
1212         nEndXclCol = nBegXclCol;
1213         while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1214         {
1215             nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1216             ++aRangeEnd;
1217         }
1218 
1219         // export this range as a record
1220         if( aRangeBeg != aRangeEnd )
1221         {
1222             sal_uInt16 nRelColIdx = nBegXclCol - GetXclCol();
1223             sal_Int32  nRelCol    = 0;
1224             for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1225             {
1226                 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1227                 {
1228                     WriteXmlContents(
1229                             rStrm,
1230                             XclAddress( static_cast<sal_uInt16>(nBegXclCol + nRelCol), GetXclRow() ),
1231                             aIt->mnXFIndex,
1232                             nRelColIdx );
1233                     ++nRelCol;
1234                     ++nRelColIdx;
1235                 }
1236             }
1237         }
1238     }
1239 }
1240 
GetCellCount() const1241 sal_uInt16 XclExpMultiCellBase::GetCellCount() const
1242 {
1243     return std::accumulate(maXFIds.begin(), maXFIds.end(), sal_uInt16(0),
1244         [](const sal_uInt16& rSum, const XclExpMultiXFId& rXFId) { return rSum + rXFId.mnCount; });
1245 }
1246 
AppendXFId(const XclExpMultiXFId & rXFId)1247 void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId& rXFId )
1248 {
1249     if( maXFIds.empty() || (maXFIds.back().mnXFId != rXFId.mnXFId) )
1250         maXFIds.push_back( rXFId );
1251     else
1252         maXFIds.back().mnCount += rXFId.mnCount;
1253 }
1254 
AppendXFId(const XclExpRoot & rRoot,const ScPatternAttr * pPattern,sal_uInt16 nScript,sal_uInt32 nForcedXFId,sal_uInt16 nCount)1255 void XclExpMultiCellBase::AppendXFId( const XclExpRoot& rRoot,
1256         const ScPatternAttr* pPattern, sal_uInt16 nScript, sal_uInt32 nForcedXFId, sal_uInt16 nCount )
1257 {
1258     sal_uInt32 nXFId = (nForcedXFId == EXC_XFID_NOTFOUND) ?
1259         rRoot.GetXFBuffer().Insert( pPattern, nScript ) : nForcedXFId;
1260     AppendXFId( XclExpMultiXFId( nXFId, nCount ) );
1261 }
1262 
TryMergeXFIds(const XclExpMultiCellBase & rCell)1263 bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase& rCell )
1264 {
1265     if( GetLastXclCol() + 1 == rCell.GetXclCol() )
1266     {
1267         maXFIds.insert( maXFIds.end(), rCell.maXFIds.begin(), rCell.maXFIds.end() );
1268         return true;
1269     }
1270     return false;
1271 }
1272 
GetXFIndexes(ScfUInt16Vec & rXFIndexes) const1273 void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1274 {
1275     OSL_ENSURE( GetLastXclCol() < rXFIndexes.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" );
1276     ScfUInt16Vec::iterator aDestIt = rXFIndexes.begin() + GetXclCol();
1277     for( const auto& rXFId : maXFIds )
1278     {
1279         ::std::fill( aDestIt, aDestIt + rXFId.mnCount, rXFId.mnXFIndex );
1280         aDestIt += rXFId.mnCount;
1281     }
1282 }
1283 
RemoveUnusedXFIndexes(const ScfUInt16Vec & rXFIndexes,size_t nStartAllNotFound)1284 void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound )
1285 {
1286     // save last column before calling maXFIds.clear()
1287     sal_uInt16 nLastXclCol = GetLastXclCol();
1288     OSL_ENSURE( nLastXclCol < rXFIndexes.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" );
1289 
1290     // build new XF index vector, containing passed XF indexes
1291     maXFIds.clear();
1292     // Process only all that possibly are not EXC_XF_NOTFOUND.
1293     size_t nEnd = std::min<size_t>(nLastXclCol + 1, nStartAllNotFound);
1294     for( size_t i = GetXclCol(); i < nEnd; ++i )
1295     {
1296         XclExpMultiXFId aXFId( 0 );
1297         // AppendXFId() tests XclExpXFIndex::mnXFId, set it too
1298         aXFId.mnXFId = aXFId.mnXFIndex = rXFIndexes[ i ];
1299         AppendXFId( aXFId );
1300     }
1301 
1302     // remove leading and trailing unused XF indexes
1303     if( !maXFIds.empty() && (maXFIds.front().mnXFIndex == EXC_XF_NOTFOUND) )
1304     {
1305         SetXclCol( GetXclCol() + maXFIds.front().mnCount );
1306         maXFIds.erase(maXFIds.begin(), maXFIds.begin() + 1);
1307     }
1308     if( !maXFIds.empty() && (maXFIds.back().mnXFIndex == EXC_XF_NOTFOUND) )
1309         maXFIds.pop_back();
1310 
1311     // The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND.
1312 }
1313 
GetStartColAllDefaultCell() const1314 sal_uInt16 XclExpMultiCellBase::GetStartColAllDefaultCell() const
1315 {
1316     sal_uInt16 col = GetXclCol();
1317     sal_uInt16 nMaxNonDefCol = col;
1318     for( const auto& rXFId : maXFIds )
1319     {
1320         col += rXFId.mnCount;
1321         if (rXFId.mnXFIndex != EXC_XF_DEFAULTCELL)
1322             nMaxNonDefCol = col;
1323     }
1324     return nMaxNonDefCol;
1325 }
1326 
XclExpBlankCell(const XclAddress & rXclPos,const XclExpMultiXFId & rXFId)1327 XclExpBlankCell::XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId ) :
1328     XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1329 {
1330     OSL_ENSURE( rXFId.mnCount > 0, "XclExpBlankCell::XclExpBlankCell - invalid count" );
1331     AppendXFId( rXFId );
1332 }
1333 
XclExpBlankCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,sal_uInt16 nLastXclCol,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId)1334 XclExpBlankCell::XclExpBlankCell(
1335         const XclExpRoot& rRoot, const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
1336         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId ) :
1337     XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1338 {
1339     OSL_ENSURE( rXclPos.mnCol <= nLastXclCol, "XclExpBlankCell::XclExpBlankCell - invalid column range" );
1340     // #i46627# use default script type instead of ApiScriptType::WEAK
1341     AppendXFId( rRoot, pPattern, rRoot.GetDefApiScript(), nForcedXFId, nLastXclCol - rXclPos.mnCol + 1 );
1342 }
1343 
TryMerge(const XclExpCellBase & rCell)1344 bool XclExpBlankCell::TryMerge( const XclExpCellBase& rCell )
1345 {
1346     const XclExpBlankCell* pBlankCell = dynamic_cast< const XclExpBlankCell* >( &rCell );
1347     return pBlankCell && TryMergeXFIds( *pBlankCell );
1348 }
1349 
GetBlankXFIndexes(ScfUInt16Vec & rXFIndexes) const1350 void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1351 {
1352     GetXFIndexes( rXFIndexes );
1353 }
1354 
RemoveUnusedBlankCells(const ScfUInt16Vec & rXFIndexes,size_t nStartAllNotFound)1355 void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound )
1356 {
1357     RemoveUnusedXFIndexes( rXFIndexes, nStartAllNotFound );
1358 }
1359 
WriteContents(XclExpStream &,sal_uInt16)1360 void XclExpBlankCell::WriteContents( XclExpStream& /*rStrm*/, sal_uInt16 /*nRelCol*/ )
1361 {
1362 }
1363 
WriteXmlContents(XclExpXmlStream & rStrm,const XclAddress & rAddress,sal_uInt32 nXFId,sal_uInt16)1364 void XclExpBlankCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 /* nRelCol */ )
1365 {
1366     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1367     rWorksheet->singleElement( XML_c,
1368             XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(),
1369             XML_s, lcl_GetStyleId(rStrm, nXFId) );
1370 }
1371 
XclExpRkCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,sal_Int32 nRkValue)1372 XclExpRkCell::XclExpRkCell(
1373         const XclExpRoot& rRoot, const XclAddress& rXclPos,
1374         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_Int32 nRkValue ) :
1375     XclExpMultiCellBase( EXC_ID_RK, EXC_ID_MULRK, 4, rXclPos )
1376 {
1377     // #i41210# always use latin script for number cells - may look wrong for special number formats...
1378     AppendXFId( rRoot, pPattern, ApiScriptType::LATIN, nForcedXFId );
1379     maRkValues.push_back( nRkValue );
1380 }
1381 
TryMerge(const XclExpCellBase & rCell)1382 bool XclExpRkCell::TryMerge( const XclExpCellBase& rCell )
1383 {
1384     const XclExpRkCell* pRkCell = dynamic_cast< const XclExpRkCell* >( &rCell );
1385     if( pRkCell && TryMergeXFIds( *pRkCell ) )
1386     {
1387         maRkValues.insert( maRkValues.end(), pRkCell->maRkValues.begin(), pRkCell->maRkValues.end() );
1388         return true;
1389     }
1390     return false;
1391 }
1392 
WriteXmlContents(XclExpXmlStream & rStrm,const XclAddress & rAddress,sal_uInt32 nXFId,sal_uInt16 nRelCol)1393 void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol )
1394 {
1395     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1396     rWorksheet->startElement( XML_c,
1397             XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(),
1398             XML_s, lcl_GetStyleId(rStrm, nXFId),
1399             XML_t, "n"
1400             // OOXTODO: XML_cm, XML_vm, XML_ph
1401     );
1402     rWorksheet->startElement( XML_v );
1403     rWorksheet->write( XclTools::GetDoubleFromRK( maRkValues[ nRelCol ] ) );
1404     rWorksheet->endElement( XML_v );
1405     rWorksheet->endElement( XML_c );
1406 }
1407 
WriteContents(XclExpStream & rStrm,sal_uInt16 nRelCol)1408 void XclExpRkCell::WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol )
1409 {
1410     OSL_ENSURE( nRelCol < maRkValues.size(), "XclExpRkCell::WriteContents - overflow error" );
1411     rStrm << maRkValues[ nRelCol ];
1412 }
1413 
1414 // Rows and Columns
1415 
XclExpOutlineBuffer(const XclExpRoot & rRoot,bool bRows)1416 XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows ) :
1417         mpScOLArray( nullptr ),
1418         maLevelInfos( SC_OL_MAXDEPTH ),
1419         mnCurrLevel( 0 ),
1420         mbCurrCollapse( false )
1421 {
1422     if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
1423         mpScOLArray = &(bRows ? pOutlineTable->GetRowArray() : pOutlineTable->GetColArray());
1424 
1425     if( mpScOLArray )
1426         for( size_t nLevel = 0; nLevel < SC_OL_MAXDEPTH; ++nLevel )
1427             if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nLevel, 0 ) )
1428                 maLevelInfos[ nLevel ].mnScEndPos = pEntry->GetEnd();
1429 }
1430 
UpdateColRow(SCCOLROW nScPos)1431 void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos )
1432 {
1433     if( !mpScOLArray )
1434         return;
1435 
1436     // find open level index for passed position
1437     size_t nNewOpenScLevel = 0; // new open level (0-based Calc index)
1438     sal_uInt8 nNewLevel = 0;    // new open level (1-based Excel index)
1439 
1440     if( mpScOLArray->FindTouchedLevel( nScPos, nScPos, nNewOpenScLevel ) )
1441         nNewLevel = static_cast< sal_uInt8 >( nNewOpenScLevel + 1 );
1442     // else nNewLevel keeps 0 to show that there are no groups
1443 
1444     mbCurrCollapse = false;
1445     if( nNewLevel >= mnCurrLevel )
1446     {
1447         // new level(s) opened, or no level closed - update all level infos
1448         for( size_t nScLevel = 0; nScLevel <= nNewOpenScLevel; ++nScLevel )
1449         {
1450             /*  In each level: check if a new group is started (there may be
1451                 neighbored groups without gap - therefore check ALL levels). */
1452             if( maLevelInfos[ nScLevel ].mnScEndPos < nScPos )
1453             {
1454                 if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nScLevel, nScPos ) )
1455                 {
1456                     maLevelInfos[ nScLevel ].mnScEndPos = pEntry->GetEnd();
1457                     maLevelInfos[ nScLevel ].mbHidden = pEntry->IsHidden();
1458                 }
1459             }
1460         }
1461     }
1462     else
1463     {
1464         // level(s) closed - check if any of the closed levels are collapsed
1465         // Calc uses 0-based level indexes
1466         sal_uInt16 nOldOpenScLevel = mnCurrLevel - 1;
1467         for( sal_uInt16 nScLevel = nNewOpenScLevel + 1; !mbCurrCollapse && (nScLevel <= nOldOpenScLevel); ++nScLevel )
1468             mbCurrCollapse = maLevelInfos[ nScLevel ].mbHidden;
1469     }
1470 
1471     // cache new opened level
1472     mnCurrLevel = nNewLevel;
1473 }
1474 
XclExpGuts(const XclExpRoot & rRoot)1475 XclExpGuts::XclExpGuts( const XclExpRoot& rRoot ) :
1476     XclExpRecord( EXC_ID_GUTS, 8 ),
1477     mnColLevels( 0 ),
1478     mnColWidth( 0 ),
1479     mnRowLevels( 0 ),
1480     mnRowWidth( 0 )
1481 {
1482     const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() );
1483     if(!pOutlineTable)
1484         return;
1485 
1486     // column outline groups
1487     const ScOutlineArray& rColArray = pOutlineTable->GetColArray();
1488     mnColLevels = ulimit_cast< sal_uInt16 >( rColArray.GetDepth(), EXC_OUTLINE_MAX );
1489     if( mnColLevels )
1490     {
1491         ++mnColLevels;
1492         mnColWidth = 12 * mnColLevels + 5;
1493     }
1494 
1495     // row outline groups
1496     const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
1497     mnRowLevels = ulimit_cast< sal_uInt16 >( rRowArray.GetDepth(), EXC_OUTLINE_MAX );
1498     if( mnRowLevels )
1499     {
1500         ++mnRowLevels;
1501         mnRowWidth = 12 * mnRowLevels + 5;
1502     }
1503 }
1504 
WriteBody(XclExpStream & rStrm)1505 void XclExpGuts::WriteBody( XclExpStream& rStrm )
1506 {
1507     rStrm << mnRowWidth << mnColWidth << mnRowLevels << mnColLevels;
1508 }
1509 
XclExpDimensions(const XclExpRoot & rRoot)1510 XclExpDimensions::XclExpDimensions( const XclExpRoot& rRoot ) :
1511     mrRoot(rRoot),
1512     mnFirstUsedXclRow( 0 ),
1513     mnFirstFreeXclRow( 0 ),
1514     mnFirstUsedXclCol( 0 ),
1515     mnFirstFreeXclCol( 0 )
1516 {
1517     switch( rRoot.GetBiff() )
1518     {
1519         case EXC_BIFF2: SetRecHeader( EXC_ID2_DIMENSIONS, 8 );  break;
1520         case EXC_BIFF3:
1521         case EXC_BIFF4:
1522         case EXC_BIFF5: SetRecHeader( EXC_ID3_DIMENSIONS, 10 ); break;
1523         case EXC_BIFF8: SetRecHeader( EXC_ID3_DIMENSIONS, 14 ); break;
1524         default:        DBG_ERROR_BIFF();
1525     }
1526 }
1527 
SetDimensions(sal_uInt16 nFirstUsedXclCol,sal_uInt32 nFirstUsedXclRow,sal_uInt16 nFirstFreeXclCol,sal_uInt32 nFirstFreeXclRow)1528 void XclExpDimensions::SetDimensions(
1529         sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
1530         sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow )
1531 {
1532     mnFirstUsedXclRow = nFirstUsedXclRow;
1533     mnFirstFreeXclRow = nFirstFreeXclRow;
1534     mnFirstUsedXclCol = nFirstUsedXclCol;
1535     mnFirstFreeXclCol = nFirstFreeXclCol;
1536 }
1537 
SaveXml(XclExpXmlStream & rStrm)1538 void XclExpDimensions::SaveXml( XclExpXmlStream& rStrm )
1539 {
1540     ScRange aRange;
1541     aRange.aStart.SetRow( static_cast<SCROW>(mnFirstUsedXclRow) );
1542     aRange.aStart.SetCol( static_cast<SCCOL>(mnFirstUsedXclCol) );
1543 
1544     if( mnFirstFreeXclRow != mnFirstUsedXclRow && mnFirstFreeXclCol != mnFirstUsedXclCol )
1545     {
1546         aRange.aEnd.SetRow( static_cast<SCROW>(mnFirstFreeXclRow-1) );
1547         aRange.aEnd.SetCol( static_cast<SCCOL>(mnFirstFreeXclCol-1) );
1548     }
1549 
1550     aRange.PutInOrder();
1551     rStrm.GetCurrentStream()->singleElement( XML_dimension,
1552             // To be compatible with MS Office 2007,
1553             // we need full address notation format
1554             // e.g. "A1:AMJ177" and not partial like: "1:177".
1555             XML_ref, XclXmlUtils::ToOString(mrRoot.GetDoc(), aRange, true) );
1556 }
1557 
WriteBody(XclExpStream & rStrm)1558 void XclExpDimensions::WriteBody( XclExpStream& rStrm )
1559 {
1560     XclBiff eBiff = rStrm.GetRoot().GetBiff();
1561     if( eBiff == EXC_BIFF8 )
1562         rStrm << mnFirstUsedXclRow << mnFirstFreeXclRow;
1563     else
1564         rStrm << static_cast< sal_uInt16 >( mnFirstUsedXclRow ) << static_cast< sal_uInt16 >( mnFirstFreeXclRow );
1565     rStrm << mnFirstUsedXclCol << mnFirstFreeXclCol;
1566     if( eBiff >= EXC_BIFF3 )
1567         rStrm << sal_uInt16( 0 );
1568 }
1569 
1570 namespace {
1571 
lclGetCChCorrection(const XclExpRoot & rRoot)1572 double lclGetCChCorrection(const XclExpRoot& rRoot)
1573 {
1574     // Convert the correction from 1/256ths of a character size to count of chars
1575     // TODO: make to fit ECMA-376-1:2016 18.3.1.81 sheetFormatPr (Sheet Format Properties):
1576     // 5 pixels are added to the base width: 2 for margin padding on each side, plus 1 for gridline
1577     // So this should depend on rRoot.GetCharWidth(), not on font height
1578 
1579     tools::Long nFontHt = rRoot.GetFontBuffer().GetAppFontData().mnHeight;
1580     return XclTools::GetXclDefColWidthCorrection(nFontHt) / 256.0;
1581 }
1582 
1583 } // namespace
1584 
XclExpDefcolwidth(const XclExpRoot & rRoot)1585 XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot& rRoot ) :
1586     XclExpDoubleRecord(EXC_ID_DEFCOLWIDTH, EXC_DEFCOLWIDTH_DEF + lclGetCChCorrection(rRoot)),
1587     XclExpRoot( rRoot )
1588 {
1589 }
1590 
IsDefWidth(sal_uInt16 nXclColWidth) const1591 bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth ) const
1592 {
1593     // This formula is taking number of characters with GetValue()
1594     // and it is translating it into default column width.
1595     // https://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.column.aspx
1596     double defaultColumnWidth = 256.0 * GetValue();
1597 
1598     // exactly matched, if difference is less than 1/16 of a character to the left or to the right
1599     return std::abs(defaultColumnWidth - nXclColWidth) < 256.0 * 1.0 / 16.0;
1600 }
1601 
SetDefWidth(sal_uInt16 nXclColWidth,bool bXLS)1602 void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth, bool bXLS )
1603 {
1604     double fCCh = nXclColWidth / 256.0;
1605     if (bXLS)
1606     {
1607         const double fCorrection = lclGetCChCorrection(GetRoot());
1608         const double fCorrectedCCh = fCCh - fCorrection;
1609         // Now get the value which would be stored in XLS DefColWidth struct
1610         double fCChRound = std::round(fCorrectedCCh);
1611         // If default width was set to a value that is not representable as integral CCh between 0
1612         // and 255, then just ignore that value, and use an arbitrary default. That way, the stored
1613         // default might not represent the most used column width (or any used column width), but
1614         // that's OK, and it just means that those columns will explicitly store their width in
1615         // 1/256ths of char, and have fUserSet in their ColInfo records.
1616         if (fCChRound < 0.0 || fCChRound > 255.0 || std::abs(fCChRound - fCorrectedCCh) > 1.0 / 512)
1617             fCChRound = 8.0;
1618         fCCh = fCChRound + fCorrection;
1619     }
1620 
1621     SetValue(fCCh);
1622 }
1623 
Save(XclExpStream & rStrm)1624 void XclExpDefcolwidth::Save(XclExpStream& rStrm)
1625 {
1626     double fCorrectedCCh = GetValue() - lclGetCChCorrection(GetRoot());
1627     // Convert double to sal_uInt16
1628     XclExpUInt16Record aUInt16Rec(GetRecId(),
1629                                   static_cast<sal_uInt16>(std::round(fCorrectedCCh)));
1630     aUInt16Rec.Save(rStrm);
1631 }
1632 
XclExpColinfo(const XclExpRoot & rRoot,SCCOL nScCol,SCROW nLastScRow,XclExpColOutlineBuffer & rOutlineBfr)1633 XclExpColinfo::XclExpColinfo( const XclExpRoot& rRoot,
1634         SCCOL nScCol, SCROW nLastScRow, XclExpColOutlineBuffer& rOutlineBfr ) :
1635     XclExpRecord( EXC_ID_COLINFO, 12 ),
1636     XclExpRoot( rRoot ),
1637     mbCustomWidth( false ),
1638     mnWidth( 0 ),
1639     mnScWidth( 0 ),
1640     mnFlags( 0 ),
1641     mnOutlineLevel( 0 ),
1642     mnFirstXclCol( static_cast< sal_uInt16 >( nScCol ) ),
1643     mnLastXclCol( static_cast< sal_uInt16 >( nScCol ) )
1644 {
1645     ScDocument& rDoc = GetDoc();
1646     SCTAB nScTab = GetCurrScTab();
1647 
1648     // column default format
1649     maXFId.mnXFId = GetXFBuffer().Insert(
1650         rDoc.GetMostUsedPattern( nScCol, 0, nLastScRow, nScTab ), GetDefApiScript() );
1651 
1652     // column width. If column is hidden then we should return real value (not zero)
1653     sal_uInt16 nScWidth = rDoc.GetColWidth( nScCol, nScTab, false );
1654     mnWidth = XclTools::GetXclColumnWidth( nScWidth, GetCharWidth() );
1655     mnScWidth = convertTwipToMm100(nScWidth);
1656 
1657     // column flags
1658     ::set_flag( mnFlags, EXC_COLINFO_HIDDEN, rDoc.ColHidden(nScCol, nScTab) );
1659 
1660     // outline data
1661     rOutlineBfr.Update( nScCol );
1662     ::set_flag( mnFlags, EXC_COLINFO_COLLAPSED, rOutlineBfr.IsCollapsed() );
1663     ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 8, 3 );
1664     mnOutlineLevel = rOutlineBfr.GetLevel();
1665 }
1666 
ConvertXFIndexes()1667 void XclExpColinfo::ConvertXFIndexes()
1668 {
1669     maXFId.ConvertXFIndex( GetRoot() );
1670 }
1671 
IsDefault(const XclExpDefcolwidth & rDefColWidth)1672 bool XclExpColinfo::IsDefault( const XclExpDefcolwidth& rDefColWidth )
1673 {
1674     mbCustomWidth = !rDefColWidth.IsDefWidth(mnWidth);
1675     return (maXFId.mnXFIndex == EXC_XF_DEFAULTCELL) &&
1676            (mnFlags == 0) &&
1677            (mnOutlineLevel == 0) &&
1678            !mbCustomWidth;
1679 }
1680 
TryMerge(const XclExpColinfo & rColInfo)1681 bool XclExpColinfo::TryMerge( const XclExpColinfo& rColInfo )
1682 {
1683     if( (maXFId.mnXFIndex == rColInfo.maXFId.mnXFIndex) &&
1684         (mnWidth == rColInfo.mnWidth) &&
1685         (mnFlags == rColInfo.mnFlags) &&
1686         (mnOutlineLevel == rColInfo.mnOutlineLevel) &&
1687         (mnLastXclCol + 1 == rColInfo.mnFirstXclCol) )
1688     {
1689         mnLastXclCol = rColInfo.mnLastXclCol;
1690         return true;
1691     }
1692     return false;
1693 }
1694 
WriteBody(XclExpStream & rStrm)1695 void XclExpColinfo::WriteBody( XclExpStream& rStrm )
1696 {
1697     // if last column is equal to last possible column, Excel adds one more
1698     sal_uInt16 nLastXclCol = mnLastXclCol;
1699     if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
1700         ++nLastXclCol;
1701 
1702     rStrm   << mnFirstXclCol
1703             << nLastXclCol
1704             << mnWidth
1705             << maXFId.mnXFIndex
1706             << mnFlags
1707             << sal_uInt16( 0 );
1708 }
1709 
SaveXml(XclExpXmlStream & rStrm)1710 void XclExpColinfo::SaveXml( XclExpXmlStream& rStrm )
1711 {
1712     const double nExcelColumnWidth = mnScWidth / convertTwipToMm100<double>(GetCharWidth());
1713 
1714     // tdf#101363 In MS specification the output value is set with double precision after delimiter:
1715     // =Truncate(({width in pixels} - 5)/{Maximum Digit Width} * 100 + 0.5)/100
1716     // Explanation of magic numbers:
1717     // 5 number - are 4 pixels of margin padding (two on each side), plus 1 pixel padding for the gridlines.
1718     //            It is unknown if it should be applied during LibreOffice export
1719     // 100 number - used to limit precision to 0.01 with formula =Truncate( {value}*100+0.5 ) / 100
1720     // 0.5 number (0.005 to output value) - used to increase value before truncating,
1721     //            to avoid situation when 2.997 will be truncated to 2.99 and not to 3.00
1722     const double nTruncatedExcelColumnWidth = std::trunc( nExcelColumnWidth * 100.0 + 0.5 ) / 100.0;
1723     rStrm.GetCurrentStream()->singleElement( XML_col,
1724             // OOXTODO: XML_bestFit,
1725             XML_collapsed,      ToPsz( ::get_flag( mnFlags, EXC_COLINFO_COLLAPSED ) ),
1726             XML_customWidth,    ToPsz( mbCustomWidth ),
1727             XML_hidden,         ToPsz( ::get_flag( mnFlags, EXC_COLINFO_HIDDEN ) ),
1728             XML_outlineLevel,   OString::number(mnOutlineLevel),
1729             XML_max,            OString::number(mnLastXclCol + 1),
1730             XML_min,            OString::number(mnFirstXclCol + 1),
1731             // OOXTODO: XML_phonetic,
1732             XML_style,          lcl_GetStyleId(rStrm, maXFId.mnXFIndex),
1733             XML_width,          OString::number(nTruncatedExcelColumnWidth) );
1734 }
1735 
XclExpColinfoBuffer(const XclExpRoot & rRoot)1736 XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot& rRoot ) :
1737     XclExpRoot( rRoot ),
1738     maDefcolwidth( rRoot ),
1739     maOutlineBfr( rRoot ),
1740     mnHighestOutlineLevel( 0 )
1741 {
1742 }
1743 
Initialize(SCROW nLastScRow)1744 void XclExpColinfoBuffer::Initialize( SCROW nLastScRow )
1745 {
1746 
1747     for( sal_uInt16 nScCol = 0, nLastScCol = GetMaxPos().Col(); nScCol <= nLastScCol; ++nScCol )
1748     {
1749         maColInfos.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol, nLastScRow, maOutlineBfr ) );
1750         if( maOutlineBfr.GetLevel() > mnHighestOutlineLevel )
1751         {
1752            mnHighestOutlineLevel = maOutlineBfr.GetLevel();
1753         }
1754     }
1755 }
1756 
Finalize(ScfUInt16Vec & rXFIndexes,bool bXLS)1757 void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes, bool bXLS )
1758 {
1759     rXFIndexes.clear();
1760     rXFIndexes.reserve( maColInfos.GetSize() );
1761 
1762     if( !maColInfos.IsEmpty())
1763     {
1764         XclExpColinfo* xPrevRec = maColInfos.GetRecord( 0 );
1765         xPrevRec->ConvertXFIndexes();
1766         for( size_t nPos = 1; nPos < maColInfos.GetSize(); ++nPos )
1767         {
1768             XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
1769             xRec->ConvertXFIndexes();
1770 
1771             // try to merge with previous record
1772             if( xPrevRec->TryMerge( *xRec ) )
1773                 maColInfos.InvalidateRecord( nPos );
1774             else
1775                 xPrevRec = xRec;
1776         }
1777         maColInfos.RemoveInvalidatedRecords();
1778     }
1779 
1780     // put XF indexes into passed vector, collect use count of all different widths
1781     std::unordered_map< sal_uInt16, sal_uInt16 > aWidthMap;
1782     sal_uInt16 nMaxColCount = 0;
1783     sal_uInt16 nMaxUsedWidth = 0;
1784     for( size_t nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
1785     {
1786         const XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
1787         sal_uInt16 nColCount = xRec->GetColCount();
1788 
1789         // add XF index to passed vector
1790         rXFIndexes.resize( rXFIndexes.size() + nColCount, xRec->GetXFIndex() );
1791 
1792         // collect use count of column width
1793         sal_uInt16 nWidth = xRec->GetColWidth();
1794         sal_uInt16& rnMapCount = aWidthMap[ nWidth ];
1795         rnMapCount = rnMapCount + nColCount;
1796         if( rnMapCount > nMaxColCount )
1797         {
1798             nMaxColCount = rnMapCount;
1799             nMaxUsedWidth = nWidth;
1800         }
1801     }
1802     maDefcolwidth.SetDefWidth( nMaxUsedWidth, bXLS );
1803 
1804     // remove all default COLINFO records
1805     for( size_t nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
1806     {
1807         XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
1808         if( xRec->IsDefault( maDefcolwidth ) )
1809             maColInfos.InvalidateRecord( nPos );
1810     }
1811     maColInfos.RemoveInvalidatedRecords();
1812 }
1813 
Save(XclExpStream & rStrm)1814 void XclExpColinfoBuffer::Save( XclExpStream& rStrm )
1815 {
1816     // DEFCOLWIDTH
1817     maDefcolwidth.Save( rStrm );
1818     // COLINFO records
1819     maColInfos.Save( rStrm );
1820 }
1821 
SaveXml(XclExpXmlStream & rStrm)1822 void XclExpColinfoBuffer::SaveXml( XclExpXmlStream& rStrm )
1823 {
1824     if( maColInfos.IsEmpty() )
1825         return;
1826 
1827     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1828     rWorksheet->startElement(XML_cols);
1829     maColInfos.SaveXml( rStrm );
1830     rWorksheet->endElement( XML_cols );
1831 }
1832 
XclExpDefaultRowData()1833 XclExpDefaultRowData::XclExpDefaultRowData() :
1834     mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1835     mnHeight( EXC_DEFROW_DEFAULTHEIGHT )
1836 {
1837 }
1838 
XclExpDefaultRowData(const XclExpRow & rRow)1839 XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow& rRow ) :
1840     mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1841     mnHeight( rRow.GetHeight() )
1842 {
1843     ::set_flag( mnFlags, EXC_DEFROW_HIDDEN, rRow.IsHidden() );
1844     ::set_flag( mnFlags, EXC_DEFROW_UNSYNCED, rRow.IsUnsynced() );
1845 }
1846 
operator <(const XclExpDefaultRowData & rLeft,const XclExpDefaultRowData & rRight)1847 static bool operator<( const XclExpDefaultRowData& rLeft, const XclExpDefaultRowData& rRight )
1848 {
1849     return (rLeft.mnHeight < rRight.mnHeight) ||
1850         ((rLeft.mnHeight == rRight.mnHeight) && (rLeft.mnFlags < rRight.mnFlags));
1851 }
1852 
XclExpDefrowheight()1853 XclExpDefrowheight::XclExpDefrowheight() :
1854     XclExpRecord( EXC_ID3_DEFROWHEIGHT, 4 )
1855 {
1856 }
1857 
SetDefaultData(const XclExpDefaultRowData & rDefData)1858 void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData& rDefData )
1859 {
1860     maDefData = rDefData;
1861 }
1862 
WriteBody(XclExpStream & rStrm)1863 void XclExpDefrowheight::WriteBody( XclExpStream& rStrm )
1864 {
1865     OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1866     rStrm << maDefData.mnFlags << maDefData.mnHeight;
1867 }
1868 
XclExpRow(const XclExpRoot & rRoot,sal_uInt32 nXclRow,XclExpRowOutlineBuffer & rOutlineBfr,bool bAlwaysEmpty,bool bHidden,sal_uInt16 nHeight)1869 XclExpRow::XclExpRow( const XclExpRoot& rRoot, sal_uInt32 nXclRow,
1870         XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty, bool bHidden, sal_uInt16 nHeight ) :
1871     XclExpRecord( EXC_ID3_ROW, 16 ),
1872     XclExpRoot( rRoot ),
1873     mnXclRow( nXclRow ),
1874     mnHeight( nHeight ),
1875     mnFlags( EXC_ROW_DEFAULTFLAGS ),
1876     mnXFIndex( EXC_XF_DEFAULTCELL ),
1877     mnOutlineLevel( 0 ),
1878     mnXclRowRpt( 1 ),
1879     mnCurrentRow( nXclRow ),
1880     mbAlwaysEmpty( bAlwaysEmpty ),
1881     mbEnabled( true )
1882 {
1883     SCTAB nScTab = GetCurrScTab();
1884     SCROW nScRow = static_cast< SCROW >( mnXclRow );
1885 
1886     // *** Row flags *** ------------------------------------------------------
1887 
1888     CRFlags nRowFlags = GetDoc().GetRowFlags( nScRow, nScTab );
1889     bool bUserHeight( nRowFlags & CRFlags::ManualSize );
1890     ::set_flag( mnFlags, EXC_ROW_UNSYNCED, bUserHeight );
1891     ::set_flag( mnFlags, EXC_ROW_HIDDEN, bHidden );
1892 
1893     // *** Outline data *** ---------------------------------------------------
1894 
1895     rOutlineBfr.Update( nScRow );
1896     ::set_flag( mnFlags, EXC_ROW_COLLAPSED, rOutlineBfr.IsCollapsed() );
1897     ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 0, 3 );
1898     mnOutlineLevel = rOutlineBfr.GetLevel();
1899 
1900     // *** Progress bar *** ---------------------------------------------------
1901 
1902     XclExpProgressBar& rProgress = GetProgressBar();
1903     rProgress.IncRowRecordCount();
1904     rProgress.Progress();
1905 }
1906 
findFirstAllSameUntilEnd(const ScfUInt16Vec & rIndexes,sal_uInt16 value,size_t searchStart=std::numeric_limits<size_t>::max ())1907 static size_t findFirstAllSameUntilEnd( const ScfUInt16Vec& rIndexes, sal_uInt16 value,
1908     size_t searchStart = std::numeric_limits<size_t>::max())
1909 {
1910     for( size_t i = std::min(rIndexes.size(), searchStart); i >= 1; --i )
1911     {
1912         if( rIndexes[ i - 1 ] != value )
1913             return i;
1914     }
1915     return 0;
1916 }
1917 
AppendCell(XclExpCellRef const & xCell,bool bIsMergedBase)1918 void XclExpRow::AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase )
1919 {
1920     OSL_ENSURE( !mbAlwaysEmpty, "XclExpRow::AppendCell - row is marked to be always empty" );
1921     // try to merge with last existing cell
1922     InsertCell( xCell, maCellList.GetSize(), bIsMergedBase );
1923 }
1924 
Finalize(const ScfUInt16Vec & rColXFIndexes,ScfUInt16Vec & aXFIndexes,size_t nStartColAllDefault,bool bProgress)1925 void XclExpRow::Finalize( const ScfUInt16Vec& rColXFIndexes, ScfUInt16Vec& aXFIndexes, size_t nStartColAllDefault, bool bProgress )
1926 {
1927     size_t nPos, nSize;
1928 
1929     // *** Convert XF identifiers *** -----------------------------------------
1930 
1931     // additionally collect the blank XF indexes
1932     size_t nColCount = GetMaxPos().Col() + 1;
1933     OSL_ENSURE( rColXFIndexes.size() == nColCount, "XclExpRow::Finalize - wrong column XF index count" );
1934 
1935     // The vector should be preset to all items being EXC_XF_NOTFOUND, to avoid repeated allocations
1936     // and clearing.
1937     assert( aXFIndexes.size() == nColCount );
1938     assert( aXFIndexes.front() == EXC_XF_NOTFOUND );
1939     assert( aXFIndexes.back() == EXC_XF_NOTFOUND );
1940     for( nPos = 0, nSize = maCellList.GetSize(); nPos < nSize; ++nPos )
1941     {
1942         XclExpCellBase* pCell = maCellList.GetRecord( nPos );
1943         pCell->ConvertXFIndexes( GetRoot() );
1944         pCell->GetBlankXFIndexes( aXFIndexes );
1945     }
1946 
1947     // *** Fill gaps with BLANK/MULBLANK cell records *** ---------------------
1948 
1949     /*  This is needed because nonexistent cells in Calc are not formatted at all,
1950         but in Excel they would have the column default format. Blank cells that
1951         are equal to the respective column default are removed later in this function. */
1952     if( !mbAlwaysEmpty )
1953     {
1954         // XF identifier representing default cell XF
1955         XclExpMultiXFId aXFId( XclExpXFBuffer::GetDefCellXFId() );
1956         aXFId.ConvertXFIndex( GetRoot() );
1957 
1958         nPos = 0;
1959         while( nPos <= maCellList.GetSize() )  // don't cache list size, may change in the loop
1960         {
1961             // get column index that follows previous cell
1962             sal_uInt16 nFirstFreeXclCol = (nPos > 0) ? (maCellList.GetRecord( nPos - 1 )->GetLastXclCol() + 1) : 0;
1963             // get own column index
1964             sal_uInt16 nNextUsedXclCol = (nPos < maCellList.GetSize()) ? maCellList.GetRecord( nPos )->GetXclCol() : (GetMaxPos().Col() + 1);
1965 
1966             // is there a gap?
1967             if( nFirstFreeXclCol < nNextUsedXclCol )
1968             {
1969                 aXFId.mnCount = nNextUsedXclCol - nFirstFreeXclCol;
1970                 XclExpCellRef xNewCell = new XclExpBlankCell( XclAddress( nFirstFreeXclCol, mnXclRow ), aXFId );
1971                 // insert the cell, InsertCell() may merge it with existing BLANK records
1972                 InsertCell( xNewCell, nPos, false );
1973                 // insert default XF indexes into aXFIndexes
1974                 for( size_t i = nFirstFreeXclCol; i < nNextUsedXclCol; ++i )
1975                     aXFIndexes[ i ] = aXFId.mnXFIndex;
1976                 // don't step forward with nPos, InsertCell() may remove records
1977             }
1978             else
1979                 ++nPos;
1980         }
1981     }
1982 
1983     // *** Find default row format *** ----------------------------------------
1984 
1985     // Often there will be many EXC_XF_DEFAULTCELL at the end, optimize by ignoring them.
1986     size_t nStartSearchAllDefault = aXFIndexes.size();
1987     if( !maCellList.IsEmpty() && dynamic_cast< const XclExpBlankCell* >( maCellList.GetLastRecord()))
1988     {
1989         const XclExpBlankCell* pLastBlank = static_cast< const XclExpBlankCell* >( maCellList.GetLastRecord());
1990         assert(pLastBlank->GetLastXclCol() == aXFIndexes.size() - 1);
1991         nStartSearchAllDefault = pLastBlank->GetStartColAllDefaultCell();
1992     }
1993     size_t nStartAllDefault = findFirstAllSameUntilEnd( aXFIndexes, EXC_XF_DEFAULTCELL, nStartSearchAllDefault);
1994 
1995     // find most used XF index in the row
1996     sal_uInt16 nRowXFIndex = EXC_XF_DEFAULTCELL;
1997     const size_t nHalfIndexes = aXFIndexes.size() / 2;
1998     if( nStartAllDefault > nHalfIndexes ) // Otherwise most are EXC_XF_DEFAULTCELL.
1999     {
2000         // Very likely the most common one is going to be the last one.
2001         nRowXFIndex = aXFIndexes.back();
2002         size_t nStartLastSame = findFirstAllSameUntilEnd( aXFIndexes, nRowXFIndex );
2003         if( nStartLastSame > nHalfIndexes ) // No, find out the most used one by counting.
2004         {
2005             std::unordered_map< sal_uInt16, size_t > aIndexMap;
2006             size_t nMaxXFCount = 0;
2007             for( const auto& rXFIndex : aXFIndexes )
2008             {
2009                 if( rXFIndex != EXC_XF_NOTFOUND )
2010                 {
2011                     size_t& rnCount = aIndexMap[ rXFIndex ];
2012                     ++rnCount;
2013                     if( rnCount > nMaxXFCount )
2014                     {
2015                         nRowXFIndex = rXFIndex;
2016                         nMaxXFCount = rnCount;
2017                         if (nMaxXFCount > nHalfIndexes)
2018                         {
2019                             // No other XF index can have a greater usage count, we
2020                             // don't need to loop through the remaining cells.
2021                             // Specifically for the tail of unused default
2022                             // cells/columns this makes a difference.
2023                             break;  // for
2024                         }
2025                     }
2026                 }
2027             }
2028         }
2029     }
2030 
2031     // decide whether to use the row default XF index or column default XF indexes
2032     bool bUseColDefXFs = nRowXFIndex == EXC_XF_DEFAULTCELL;
2033     if( !bUseColDefXFs )
2034     {
2035         // count needed XF indexes for blank cells with and without row default XF index
2036         size_t nXFCountWithRowDefXF = 0;
2037         size_t nXFCountWithoutRowDefXF = 0;
2038         ScfUInt16Vec::const_iterator aColIt = rColXFIndexes.begin();
2039         for( const auto& rXFIndex : aXFIndexes )
2040         {
2041             sal_uInt16 nXFIndex = rXFIndex;
2042             if( nXFIndex != EXC_XF_NOTFOUND )
2043             {
2044                 if( nXFIndex != nRowXFIndex )
2045                     ++nXFCountWithRowDefXF;     // with row default XF index
2046                 if( nXFIndex != *aColIt )
2047                     ++nXFCountWithoutRowDefXF;  // without row default XF index
2048             }
2049             ++aColIt;
2050         }
2051 
2052         // use column XF indexes if this would cause less or equal number of BLANK records
2053         bUseColDefXFs = nXFCountWithoutRowDefXF <= nXFCountWithRowDefXF;
2054     }
2055 
2056     // *** Remove unused BLANK cell records *** -------------------------------
2057 
2058     size_t maxStartAllNotFound;
2059     if( bUseColDefXFs )
2060     {
2061         size_t maxStartAllDefault = std::max( nStartAllDefault, nStartColAllDefault );
2062         // use column default XF indexes
2063         // #i194#: remove cell XF indexes equal to column default XF indexes
2064         for( size_t i = 0; i < maxStartAllDefault; ++i )
2065         {
2066             if( aXFIndexes[ i ] == rColXFIndexes[ i ] )
2067                 aXFIndexes[ i ] = EXC_XF_NOTFOUND;
2068         }
2069         // They can differ only up to maxNonDefault, in the rest they are the same.
2070         for( size_t i = maxStartAllDefault; i < aXFIndexes.size(); ++i )
2071             aXFIndexes[ i ] = EXC_XF_NOTFOUND;
2072         maxStartAllNotFound = maxStartAllDefault;
2073     }
2074     else
2075     {
2076         // use row default XF index
2077         mnXFIndex = nRowXFIndex;
2078         ::set_flag( mnFlags, EXC_ROW_USEDEFXF );
2079         // #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index
2080         for( auto& rXFIndex : aXFIndexes )
2081             if( rXFIndex == nRowXFIndex )
2082                 rXFIndex = EXC_XF_NOTFOUND;
2083         maxStartAllNotFound = aXFIndexes.size();
2084     }
2085 
2086     // remove unused parts of BLANK/MULBLANK cell records
2087     size_t nStartAllNotFound = findFirstAllSameUntilEnd( aXFIndexes, EXC_XF_NOTFOUND, maxStartAllNotFound );
2088     nPos = 0;
2089     while( nPos < maCellList.GetSize() )   // do not cache list size, may change in the loop
2090     {
2091         XclExpCellBase* xCell = maCellList.GetRecord( nPos );
2092         xCell->RemoveUnusedBlankCells( aXFIndexes, nStartAllNotFound );
2093         if( xCell->IsEmpty() )
2094             maCellList.RemoveRecord( nPos );
2095         else
2096             ++nPos;
2097     }
2098     // Ensure it's all EXC_XF_NOTFOUND again for next reuse.
2099     for( size_t i = 0; i < nStartAllNotFound; ++i )
2100         aXFIndexes[ i ] = EXC_XF_NOTFOUND;
2101 
2102     // progress bar includes disabled rows; only update it in the lead thread.
2103     if (bProgress)
2104         GetProgressBar().Progress();
2105 }
GetFirstUsedXclCol() const2106 sal_uInt16 XclExpRow::GetFirstUsedXclCol() const
2107 {
2108     return maCellList.IsEmpty() ? 0 : maCellList.GetFirstRecord()->GetXclCol();
2109 }
2110 
GetFirstFreeXclCol() const2111 sal_uInt16 XclExpRow::GetFirstFreeXclCol() const
2112 {
2113     return maCellList.IsEmpty() ? 0 : (maCellList.GetLastRecord()->GetLastXclCol() + 1);
2114 }
2115 
IsDefaultable() const2116 bool XclExpRow::IsDefaultable() const
2117 {
2118     const sal_uInt16 nFlagsAlwaysMarkedAsDefault = EXC_ROW_DEFAULTFLAGS | EXC_ROW_HIDDEN | EXC_ROW_UNSYNCED;
2119     return !::get_flag( mnFlags, static_cast< sal_uInt16 >( ~nFlagsAlwaysMarkedAsDefault ) ) &&
2120            IsEmpty();
2121 }
2122 
DisableIfDefault(const XclExpDefaultRowData & rDefRowData)2123 void XclExpRow::DisableIfDefault( const XclExpDefaultRowData& rDefRowData )
2124 {
2125     mbEnabled = !IsDefaultable() ||
2126         (mnHeight != rDefRowData.mnHeight) ||
2127         (IsHidden() != rDefRowData.IsHidden()) ||
2128         (IsUnsynced() != rDefRowData.IsUnsynced());
2129 }
2130 
WriteCellList(XclExpStream & rStrm)2131 void XclExpRow::WriteCellList( XclExpStream& rStrm )
2132 {
2133     OSL_ENSURE( mbEnabled || maCellList.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" );
2134     maCellList.Save( rStrm );
2135 }
2136 
Save(XclExpStream & rStrm)2137 void XclExpRow::Save( XclExpStream& rStrm )
2138 {
2139     if( mbEnabled )
2140     {
2141         mnCurrentRow = mnXclRow;
2142         for ( sal_uInt32 i = 0; i < mnXclRowRpt; ++i, ++mnCurrentRow )
2143             XclExpRecord::Save( rStrm );
2144     }
2145 }
2146 
InsertCell(XclExpCellRef xCell,size_t nPos,bool bIsMergedBase)2147 void XclExpRow::InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase )
2148 {
2149     OSL_ENSURE( xCell, "XclExpRow::InsertCell - missing cell" );
2150 
2151     /*  If we have a multi-line text in a merged cell, and the resulting
2152         row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED
2153         flag to be true to ensure Excel works correctly. */
2154     if( bIsMergedBase && xCell->IsMultiLineText() )
2155         ::set_flag( mnFlags, EXC_ROW_UNSYNCED );
2156 
2157     // try to merge with previous cell, insert the new cell if not successful
2158     XclExpCellBase* xPrevCell = maCellList.GetRecord( nPos - 1 );
2159     if( xPrevCell && xPrevCell->TryMerge( *xCell ) )
2160         xCell = xPrevCell;
2161     else
2162         maCellList.InsertRecord( xCell, nPos++ );
2163     // nPos points now to following cell
2164 
2165     // try to merge with following cell, remove it if successful
2166     XclExpCellRef xNextCell = maCellList.GetRecord( nPos );
2167     if( xNextCell && xCell->TryMerge( *xNextCell ) )
2168         maCellList.RemoveRecord( nPos );
2169 }
2170 
WriteBody(XclExpStream & rStrm)2171 void XclExpRow::WriteBody( XclExpStream& rStrm )
2172 {
2173     rStrm   << static_cast< sal_uInt16 >(mnCurrentRow)
2174             << GetFirstUsedXclCol()
2175             << GetFirstFreeXclCol()
2176             << mnHeight
2177             << sal_uInt32( 0 )
2178             << mnFlags
2179             << mnXFIndex;
2180 }
2181 
SaveXml(XclExpXmlStream & rStrm)2182 void XclExpRow::SaveXml( XclExpXmlStream& rStrm )
2183 {
2184     if( !mbEnabled )
2185         return;
2186     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2187     bool haveFormat = ::get_flag( mnFlags, EXC_ROW_USEDEFXF );
2188     mnCurrentRow = mnXclRow + 1;
2189     for ( sal_uInt32 i=0; i<mnXclRowRpt; ++i )
2190     {
2191         rWorksheet->startElement( XML_row,
2192                 XML_r,              OString::number(mnCurrentRow++),
2193                 // OOXTODO: XML_spans,          optional
2194                 XML_s,              haveFormat ? lcl_GetStyleId( rStrm, mnXFIndex ).getStr() : nullptr,
2195                 XML_customFormat,   ToPsz( haveFormat ),
2196                 XML_ht,             OString::number(static_cast<double>(mnHeight) / 20.0),
2197                 XML_hidden,         ToPsz( ::get_flag( mnFlags, EXC_ROW_HIDDEN ) ),
2198                 XML_customHeight,   ToPsz( ::get_flag( mnFlags, EXC_ROW_UNSYNCED ) ),
2199                 XML_outlineLevel,   OString::number(mnOutlineLevel),
2200                 XML_collapsed,      ToPsz( ::get_flag( mnFlags, EXC_ROW_COLLAPSED ) )
2201                 // OOXTODO: XML_thickTop,       bool
2202                 // OOXTODO: XML_thickBot,       bool
2203                 // OOXTODO: XML_ph,             bool
2204         );
2205         // OOXTODO: XML_extLst
2206         maCellList.SaveXml( rStrm );
2207         rWorksheet->endElement( XML_row );
2208     }
2209 }
2210 
XclExpRowBuffer(const XclExpRoot & rRoot)2211 XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot& rRoot ) :
2212     XclExpRoot( rRoot ),
2213     maOutlineBfr( rRoot ),
2214     maDimensions( rRoot ),
2215     mnHighestOutlineLevel( 0 )
2216 {
2217 }
2218 
AppendCell(XclExpCellRef const & xCell,bool bIsMergedBase)2219 void XclExpRowBuffer::AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase )
2220 {
2221     OSL_ENSURE( xCell, "XclExpRowBuffer::AppendCell - missing cell" );
2222     GetOrCreateRow( xCell->GetXclRow(), false ).AppendCell( xCell, bIsMergedBase );
2223 }
2224 
CreateRows(SCROW nFirstFreeScRow)2225 void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow )
2226 {
2227     if( nFirstFreeScRow > 0 )
2228         GetOrCreateRow(  ::std::max ( nFirstFreeScRow - 1, GetMaxPos().Row() ), true );
2229 }
2230 
2231 namespace {
2232 
2233 class RowFinalizeTask : public comphelper::ThreadTask
2234 {
2235     bool mbProgress;
2236     const ScfUInt16Vec& mrColXFIndexes;
2237     size_t mnStartColAllDefault;
2238     std::vector< XclExpRow * > maRows;
2239 public:
RowFinalizeTask(const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,const ScfUInt16Vec & rColXFIndexes,size_t nStartColAllDefault,bool bProgress)2240              RowFinalizeTask( const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,
2241                               const ScfUInt16Vec& rColXFIndexes,
2242                               size_t nStartColAllDefault,
2243                               bool bProgress ) :
2244                  comphelper::ThreadTask( pTag ),
2245                  mbProgress( bProgress ),
2246                  mrColXFIndexes( rColXFIndexes ),
2247                  mnStartColAllDefault( nStartColAllDefault )
2248                  {}
2249 
push_back(XclExpRow * pRow)2250     void     push_back( XclExpRow *pRow ) { maRows.push_back( pRow ); }
doWork()2251     virtual void doWork() override
2252     {
2253         ScfUInt16Vec aXFIndexes( mrColXFIndexes.size(), EXC_XF_NOTFOUND );
2254         for (XclExpRow* p : maRows)
2255             p->Finalize( mrColXFIndexes, aXFIndexes, mnStartColAllDefault, mbProgress );
2256     }
2257 };
2258 
2259 }
2260 
Finalize(XclExpDefaultRowData & rDefRowData,const ScfUInt16Vec & rColXFIndexes,size_t nStartColAllDefault)2261 void XclExpRowBuffer::Finalize( XclExpDefaultRowData& rDefRowData,
2262                                 const ScfUInt16Vec& rColXFIndexes,
2263                                 size_t nStartColAllDefault )
2264 {
2265     // *** Finalize all rows *** ----------------------------------------------
2266 
2267     GetProgressBar().ActivateFinalRowsSegment();
2268 
2269 #if 1
2270     // This is staggeringly slow, and each element operates only
2271     // on its own data.
2272     const size_t nRows = maRowMap.size();
2273     const size_t nThreads = nRows < 128 ? 1 : comphelper::ThreadPool::getPreferredConcurrency();
2274 #else
2275     const size_t nThreads = 1; // globally disable multi-threading for now.
2276 #endif
2277     if (nThreads == 1)
2278     {
2279         ScfUInt16Vec aXFIndexes( rColXFIndexes.size(), EXC_XF_NOTFOUND );
2280         for (auto& rEntry : maRowMap)
2281             rEntry.second->Finalize( rColXFIndexes, aXFIndexes, nStartColAllDefault, true );
2282     }
2283     else
2284     {
2285         comphelper::ThreadPool &rPool = comphelper::ThreadPool::getSharedOptimalPool();
2286         std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
2287         std::vector<std::unique_ptr<RowFinalizeTask>> aTasks(nThreads);
2288         for ( size_t i = 0; i < nThreads; i++ )
2289             aTasks[ i ].reset( new RowFinalizeTask( pTag, rColXFIndexes, nStartColAllDefault, i == 0 ) );
2290 
2291         size_t nIdx = 0;
2292         for ( const auto& rEntry : maRowMap )
2293         {
2294             aTasks[ nIdx % nThreads ]->push_back( rEntry.second.get() );
2295             ++nIdx;
2296         }
2297 
2298         for ( size_t i = 1; i < nThreads; i++ )
2299             rPool.pushTask( std::move(aTasks[ i ]) );
2300 
2301         // Progress bar updates must be synchronous to avoid deadlock
2302         aTasks[0]->doWork();
2303 
2304         rPool.waitUntilDone(pTag);
2305     }
2306 
2307     // *** Default row format *** ---------------------------------------------
2308 
2309     std::map< XclExpDefaultRowData, size_t > aDefRowMap;
2310 
2311     XclExpDefaultRowData aMaxDefData;
2312     size_t nMaxDefCount = 0;
2313     // only look for default format in existing rows, if there are more than unused
2314     // if the row is hidden, then row xml must be created even if it not contain cells
2315     XclExpRow* pPrev = nullptr;
2316     std::vector< XclExpRow* > aRepeated;
2317     for (const auto& rEntry : maRowMap)
2318     {
2319         const RowRef& rRow = rEntry.second;
2320         if ( rRow->IsDefaultable() )
2321         {
2322             XclExpDefaultRowData aDefData( *rRow );
2323             size_t& rnDefCount = aDefRowMap[ aDefData ];
2324             ++rnDefCount;
2325             if( rnDefCount > nMaxDefCount )
2326             {
2327                 nMaxDefCount = rnDefCount;
2328                 aMaxDefData = aDefData;
2329             }
2330         }
2331         if ( pPrev )
2332         {
2333             if ( pPrev->IsDefaultable() )
2334             {
2335                 // if the previous row we processed is not
2336                 // defaultable then afaict the rows in between are
2337                 // not used ( and not repeatable )
2338                 sal_uInt32 nRpt =  rRow->GetXclRow() - pPrev->GetXclRow();
2339                 if ( nRpt > 1 )
2340                     aRepeated.push_back( pPrev );
2341                 pPrev->SetXclRowRpt( nRpt );
2342                 XclExpDefaultRowData aDefData( *pPrev );
2343                 size_t& rnDefCount = aDefRowMap[ aDefData ];
2344                 rnDefCount += ( pPrev->GetXclRowRpt() - 1 );
2345                 if( rnDefCount > nMaxDefCount )
2346                 {
2347                     nMaxDefCount = rnDefCount;
2348                     aMaxDefData = aDefData;
2349                 }
2350             }
2351         }
2352         pPrev = rRow.get();
2353     }
2354     // return the default row format to caller
2355     rDefRowData = aMaxDefData;
2356 
2357     // now disable repeating extra (empty) rows that are equal to the default row
2358     for (auto& rpRow : aRepeated)
2359     {
2360         if ( rpRow->GetXclRowRpt() > 1
2361              && rpRow->GetHeight() == rDefRowData.mnHeight
2362              && rpRow->IsHidden() == rDefRowData.IsHidden() )
2363         {
2364             rpRow->SetXclRowRpt( 1 );
2365         }
2366     }
2367 
2368     // *** Disable unused ROW records, find used area *** ---------------------
2369 
2370     sal_uInt16 nFirstUsedXclCol = SAL_MAX_UINT16;
2371     sal_uInt16 nFirstFreeXclCol = 0;
2372     sal_uInt32 nFirstUsedXclRow = SAL_MAX_UINT32;
2373     sal_uInt32 nFirstFreeXclRow = 0;
2374 
2375     for (const auto& rEntry : maRowMap)
2376     {
2377         const RowRef& rRow = rEntry.second;
2378         // disable unused rows
2379         rRow->DisableIfDefault( aMaxDefData );
2380 
2381         // find used column range
2382         if( !rRow->IsEmpty() )      // empty rows return (0...0) as used range
2383         {
2384             nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, rRow->GetFirstUsedXclCol() );
2385             nFirstFreeXclCol = ::std::max( nFirstFreeXclCol, rRow->GetFirstFreeXclCol() );
2386         }
2387 
2388         // find used row range
2389         if( rRow->IsEnabled() )
2390         {
2391             sal_uInt32 nXclRow = rRow->GetXclRow();
2392             nFirstUsedXclRow = ::std::min< sal_uInt32 >( nFirstUsedXclRow, nXclRow );
2393             nFirstFreeXclRow = ::std::max< sal_uInt32 >( nFirstFreeXclRow, nXclRow + 1 );
2394         }
2395     }
2396 
2397     // adjust start position, if there are no or only empty/disabled ROW records
2398     nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, nFirstFreeXclCol );
2399     nFirstUsedXclRow = ::std::min( nFirstUsedXclRow, nFirstFreeXclRow );
2400 
2401     // initialize the DIMENSIONS record
2402     maDimensions.SetDimensions(
2403         nFirstUsedXclCol, nFirstUsedXclRow, nFirstFreeXclCol, nFirstFreeXclRow );
2404 }
2405 
Save(XclExpStream & rStrm)2406 void XclExpRowBuffer::Save( XclExpStream& rStrm )
2407 {
2408     // DIMENSIONS record
2409     maDimensions.Save( rStrm );
2410 
2411     // save in blocks of 32 rows, each block contains first all ROWs, then all cells
2412     size_t nSize = maRowMap.size();
2413     RowMap::iterator itr = maRowMap.begin(), itrEnd = maRowMap.end();
2414     RowMap::iterator itrBlkStart = maRowMap.begin(), itrBlkEnd = maRowMap.begin();
2415     sal_uInt16 nStartXclRow = (nSize == 0) ? 0 : itr->second->GetXclRow();
2416 
2417     for (; itr != itrEnd; ++itr)
2418     {
2419         // find end of row block
2420         itrBlkEnd = std::find_if_not(itrBlkEnd, itrEnd,
2421             [&nStartXclRow](const RowMap::value_type& rRow) { return rRow.second->GetXclRow() - nStartXclRow < EXC_ROW_ROWBLOCKSIZE; });
2422 
2423         // write the ROW records
2424         std::for_each(itrBlkStart, itrBlkEnd, [&rStrm](const RowMap::value_type& rRow) { rRow.second->Save( rStrm ); });
2425 
2426         // write the cell records
2427         std::for_each(itrBlkStart, itrBlkEnd, [&rStrm](const RowMap::value_type& rRow) { rRow.second->WriteCellList( rStrm ); });
2428 
2429         itrBlkStart = (itrBlkEnd == itrEnd) ? itrBlkEnd : itrBlkEnd++;
2430         nStartXclRow += EXC_ROW_ROWBLOCKSIZE;
2431     }
2432 }
2433 
SaveXml(XclExpXmlStream & rStrm)2434 void XclExpRowBuffer::SaveXml( XclExpXmlStream& rStrm )
2435 {
2436     if (std::none_of(maRowMap.begin(), maRowMap.end(), [](const RowMap::value_type& rRow) { return rRow.second->IsEnabled(); }))
2437     {
2438         rStrm.GetCurrentStream()->singleElement(XML_sheetData);
2439         return;
2440     }
2441 
2442     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2443     rWorksheet->startElement(XML_sheetData);
2444     for (const auto& rEntry : maRowMap)
2445         rEntry.second->SaveXml(rStrm);
2446     rWorksheet->endElement( XML_sheetData );
2447 }
2448 
GetOrCreateRow(sal_uInt32 nXclRow,bool bRowAlwaysEmpty)2449 XclExpRow& XclExpRowBuffer::GetOrCreateRow( sal_uInt32 nXclRow, bool bRowAlwaysEmpty )
2450 {
2451     // This is called rather often, so optimize for the most common case of saving row by row
2452     // (so the requested row is often the last one in the map or belongs after the last one).
2453     RowMap::iterator itr;
2454     if(maRowMap.empty())
2455         itr = maRowMap.end();
2456     else
2457     {
2458         RowMap::reverse_iterator last = maRowMap.rbegin();
2459         if( last->first == nXclRow )
2460             return *last->second;
2461         if( nXclRow > last->first )
2462             itr = maRowMap.end();
2463         else
2464             itr = maRowMap.lower_bound( nXclRow );
2465     }
2466     const bool bFound = itr != maRowMap.end();
2467     // bFoundHigher: nXclRow was identical to the previous entry, so not explicitly created earlier
2468     // coverity[deref_iterator : FALSE] - clearly itr if only derefed if bFound which checks for valid itr
2469     const bool bFoundHigher = bFound && itr->first != nXclRow;
2470     if( bFound && !bFoundHigher )
2471         return *itr->second;
2472 
2473     size_t nFrom = 0;
2474     RowRef pPrevEntry;
2475     if( itr != maRowMap.begin() )
2476     {
2477         --itr;
2478         pPrevEntry = itr->second;
2479         if( bFoundHigher )
2480             nFrom = nXclRow;
2481         else
2482             nFrom = itr->first + 1;
2483     }
2484 
2485     const ScDocument& rDoc = GetRoot().GetDoc();
2486     const SCTAB nScTab = GetRoot().GetCurrScTab();
2487     // Do not repeatedly call RowHidden() / GetRowHeight() for same values.
2488     bool bHidden = false;
2489     SCROW lastSameHiddenRow = -1;
2490     sal_uInt16 nHeight = 0;
2491     SCROW lastSameHeightRow = -1;
2492     // create the missing rows first
2493     while( nFrom <= nXclRow )
2494     {
2495         // only create RowMap entries if it is first row in spreadsheet,
2496         // if it is the desired row, or for rows that differ from previous.
2497         if( static_cast<SCROW>(nFrom) > lastSameHiddenRow )
2498             bHidden = rDoc.RowHidden(nFrom, nScTab, nullptr, &lastSameHiddenRow);
2499         // Always get the actual row height even if the manual size flag is
2500         // not set, to correctly export the heights of rows with wrapped
2501         // texts.
2502         if( static_cast<SCROW>(nFrom) > lastSameHeightRow )
2503             nHeight = rDoc.GetRowHeight(nFrom, nScTab, nullptr, &lastSameHeightRow, false);
2504         if ( !pPrevEntry || ( nFrom == nXclRow ) ||
2505              ( maOutlineBfr.IsCollapsed() ) ||
2506              ( maOutlineBfr.GetLevel() != 0 ) ||
2507              ( bRowAlwaysEmpty && !pPrevEntry->IsEmpty() ) ||
2508              ( bHidden != pPrevEntry->IsHidden() ) ||
2509              ( nHeight != pPrevEntry->GetHeight() ) )
2510         {
2511             if( maOutlineBfr.GetLevel() > mnHighestOutlineLevel )
2512             {
2513                 mnHighestOutlineLevel = maOutlineBfr.GetLevel();
2514             }
2515             RowRef p = std::make_shared<XclExpRow>(GetRoot(), nFrom, maOutlineBfr, bRowAlwaysEmpty, bHidden, nHeight);
2516             maRowMap.emplace(nFrom, p);
2517             pPrevEntry = std::move(p);
2518         }
2519         ++nFrom;
2520     }
2521     itr = maRowMap.find(nXclRow);
2522     return *itr->second;
2523 }
2524 
2525 // Cell Table
2526 
XclExpCellTable(const XclExpRoot & rRoot)2527 XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) :
2528     XclExpRoot( rRoot ),
2529     maColInfoBfr( rRoot ),
2530     maRowBfr( rRoot ),
2531     maArrayBfr( rRoot ),
2532     maShrfmlaBfr( rRoot ),
2533     maTableopBfr( rRoot ),
2534     mxDefrowheight( new XclExpDefrowheight() ),
2535     mxGuts( new XclExpGuts( rRoot ) ),
2536     mxNoteList( new XclExpNoteList ),
2537     mxMergedcells( new XclExpMergedcells( rRoot ) ),
2538     mxHyperlinkList( new XclExpHyperlinkList ),
2539     mxDval( new XclExpDval( rRoot ) ),
2540     mxExtLst( new XclExtLst( rRoot ) )
2541 {
2542     ScDocument& rDoc = GetDoc();
2543     SCTAB nScTab = GetCurrScTab();
2544     SvNumberFormatter& rFormatter = GetFormatter();
2545 
2546     // maximum sheet limits
2547     SCCOL nMaxScCol = GetMaxPos().Col();
2548     SCROW nMaxScRow = GetMaxPos().Row();
2549 
2550     // find used area (non-empty cells)
2551     SCCOL nLastUsedScCol;
2552     SCROW nLastUsedScRow;
2553     rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
2554 
2555     if(nLastUsedScCol > nMaxScCol)
2556         nLastUsedScCol = nMaxScCol;
2557 
2558     // check extra blank rows to avoid of losing their not default settings (workaround for tdf#41425)
2559     nLastUsedScRow += 1000;
2560 
2561     if(nLastUsedScRow > nMaxScRow)
2562         nLastUsedScRow = nMaxScRow;
2563 
2564     ScRange aUsedRange( 0, 0, nScTab, nLastUsedScCol, nLastUsedScRow, nScTab );
2565     GetAddressConverter().ValidateRange( aUsedRange, true );
2566     nLastUsedScRow = aUsedRange.aEnd.Row();
2567 
2568     // first row without any set attributes (height/hidden/...)
2569     SCROW nFirstUnflaggedScRow = rDoc.GetLastFlaggedRow( nScTab ) + 1;
2570 
2571     // find range of outlines
2572     SCROW nFirstUngroupedScRow = 0;
2573     if( const ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable( nScTab ) )
2574     {
2575         SCCOLROW nScStartPos, nScEndPos;
2576         const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
2577         rRowArray.GetRange( nScStartPos, nScEndPos );
2578         // +1 because open/close button is in next row in Excel, +1 for "end->first unused"
2579         nFirstUngroupedScRow = static_cast< SCROW >( nScEndPos + 2 );
2580     }
2581 
2582     // column settings
2583     /*  #i30411# Files saved with SO7/OOo1.x with nonstandard default column
2584         formatting cause big Excel files, because all rows from row 1 to row
2585         32000 are exported. Now, if the used area goes exactly to row 32000,
2586         use this row as default and ignore all rows >32000.
2587         #i59220# Tolerance of +-128 rows for inserted/removed rows. */
2588     if( (31871 <= nLastUsedScRow) && (nLastUsedScRow <= 32127) && (nFirstUnflaggedScRow < nLastUsedScRow) && (nFirstUngroupedScRow <= nLastUsedScRow) )
2589         nMaxScRow = nLastUsedScRow;
2590     maColInfoBfr.Initialize( nMaxScRow );
2591 
2592     // range for cell iterator
2593     SCCOL nLastIterScCol = nMaxScCol;
2594     SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow, nMaxScRow );
2595     ScUsedAreaIterator aIt( rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow );
2596 
2597     // activate the correct segment and sub segment at the progress bar
2598     GetProgressBar().ActivateCreateRowsSegment();
2599 
2600     for( bool bIt = aIt.GetNext(); bIt; bIt = aIt.GetNext() )
2601     {
2602         SCCOL nScCol = aIt.GetStartCol();
2603         SCROW nScRow = aIt.GetRow();
2604         SCCOL nLastScCol = aIt.GetEndCol();
2605         ScAddress aScPos( nScCol, nScRow, nScTab );
2606 
2607         XclAddress aXclPos( static_cast< sal_uInt16 >( nScCol ), static_cast< sal_uInt32 >( nScRow ) );
2608         sal_uInt16 nLastXclCol = static_cast< sal_uInt16 >( nLastScCol );
2609 
2610         const ScRefCellValue& rScCell = aIt.GetCell();
2611         XclExpCellRef xCell;
2612 
2613         const ScPatternAttr* pPattern = aIt.GetPattern();
2614 
2615         // handle overlapped merged cells before creating the cell record
2616         sal_uInt32 nMergeBaseXFId = EXC_XFID_NOTFOUND;
2617         bool bIsMergedBase = false;
2618         if( pPattern )
2619         {
2620             const SfxItemSet& rItemSet = pPattern->GetItemSet();
2621             // base cell in a merged range
2622             const ScMergeAttr& rMergeItem = rItemSet.Get( ATTR_MERGE );
2623             bIsMergedBase = rMergeItem.IsMerged();
2624             /*  overlapped cell in a merged range; in Excel all merged cells
2625                 must contain same XF index, for correct border */
2626             const ScMergeFlagAttr& rMergeFlagItem = rItemSet.Get( ATTR_MERGE_FLAG );
2627             if( rMergeFlagItem.IsOverlapped() )
2628                 nMergeBaseXFId = mxMergedcells->GetBaseXFId( aScPos );
2629         }
2630 
2631         OUString aAddNoteText;    // additional text to be appended to a note
2632 
2633         switch (rScCell.getType())
2634         {
2635             case CELLTYPE_VALUE:
2636             {
2637                 double fValue = rScCell.getDouble();
2638 
2639                 if (pPattern)
2640                 {
2641                     OUString aUrl = pPattern->GetItem(ATTR_HYPERLINK).GetValue();
2642                     if (!aUrl.isEmpty())
2643                     {
2644                         rtl::Reference<XclExpHyperlink> aLink =
2645                             new XclExpHyperlink(GetRoot(), SvxURLField(aUrl, aUrl), aScPos);
2646                         mxHyperlinkList->AppendRecord(aLink);
2647                     }
2648                 }
2649 
2650                 // try to create a Boolean cell
2651                 if( pPattern && ((fValue == 0.0) || (fValue == 1.0)) )
2652                 {
2653                     sal_uInt32 nScNumFmt = pPattern->GetItem( ATTR_VALUE_FORMAT ).GetValue();
2654                     if( rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL )
2655                         xCell = new XclExpBooleanCell(
2656                             GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue != 0.0 );
2657                 }
2658 
2659                 // try to create an RK value (compressed floating-point number)
2660                 sal_Int32 nRkValue;
2661                 if( !xCell && XclTools::GetRKFromDouble( nRkValue, fValue ) )
2662                     xCell = new XclExpRkCell(
2663                         GetRoot(), aXclPos, pPattern, nMergeBaseXFId, nRkValue );
2664 
2665                 // else: simple floating-point number cell
2666                 if( !xCell )
2667                     xCell = new XclExpNumberCell(
2668                         GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue );
2669             }
2670             break;
2671 
2672             case CELLTYPE_STRING:
2673             {
2674                 xCell = new XclExpLabelCell(
2675                     GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.getSharedString()->getString());
2676             }
2677             break;
2678 
2679             case CELLTYPE_EDIT:
2680             {
2681                 XclExpHyperlinkHelper aLinkHelper( GetRoot(), aScPos );
2682                 xCell = new XclExpLabelCell(
2683                     GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.getEditText(), aLinkHelper);
2684 
2685                 // add a single created HLINK record to the record list
2686                 if( aLinkHelper.HasLinkRecord() )
2687                     mxHyperlinkList->AppendRecord( aLinkHelper.GetLinkRecord() );
2688                 // add list of multiple URLs to the additional cell note text
2689                 if( aLinkHelper.HasMultipleUrls() )
2690                     aAddNoteText = ScGlobal::addToken( aAddNoteText, aLinkHelper.GetUrlList(), '\n', 2 );
2691             }
2692             break;
2693 
2694             case CELLTYPE_FORMULA:
2695             {
2696                 if (pPattern)
2697                 {
2698                     OUString aUrl = pPattern->GetItem(ATTR_HYPERLINK).GetValue();
2699                     if (!aUrl.isEmpty())
2700                     {
2701                         rtl::Reference<XclExpHyperlink> aLink =
2702                             new XclExpHyperlink(GetRoot(), SvxURLField(aUrl, aUrl), aScPos);
2703                         mxHyperlinkList->AppendRecord(aLink);
2704                     }
2705                 }
2706 
2707                 xCell = new XclExpFormulaCell(
2708                     GetRoot(), aXclPos, pPattern, nMergeBaseXFId,
2709                     *rScCell.getFormula(), maArrayBfr, maShrfmlaBfr, maTableopBfr);
2710             }
2711             break;
2712 
2713             default:
2714                 OSL_FAIL( "XclExpCellTable::XclExpCellTable - unknown cell type" );
2715                 [[fallthrough]];
2716             case CELLTYPE_NONE:
2717             {
2718                 xCell = new XclExpBlankCell(
2719                     GetRoot(), aXclPos, nLastXclCol, pPattern, nMergeBaseXFId );
2720             }
2721             break;
2722         }
2723 
2724         assert(xCell && "can only reach here with xCell set");
2725 
2726         // insert the cell into the current row
2727         maRowBfr.AppendCell( xCell, bIsMergedBase );
2728 
2729         if ( !aAddNoteText.isEmpty()  )
2730             mxNoteList->AppendNewRecord( new XclExpNote( GetRoot(), aScPos, nullptr, aAddNoteText ) );
2731 
2732         // other sheet contents
2733         if( pPattern )
2734         {
2735             const SfxItemSet& rItemSet = pPattern->GetItemSet();
2736 
2737             // base cell in a merged range
2738             if( bIsMergedBase )
2739             {
2740                 const ScMergeAttr& rMergeItem = rItemSet.Get( ATTR_MERGE );
2741                 ScRange aScRange( aScPos );
2742                 aScRange.aEnd.IncCol( rMergeItem.GetColMerge() - 1 );
2743                 aScRange.aEnd.IncRow( rMergeItem.GetRowMerge() - 1 );
2744                 sal_uInt32 nXFId = xCell->GetFirstXFId();
2745                 // blank cells merged vertically may occur repeatedly
2746                 OSL_ENSURE( (aScRange.aStart.Col() == aScRange.aEnd.Col()) || (nScCol == nLastScCol),
2747                     "XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" );
2748                 for( SCCOL nIndex = nScCol; nIndex <= nLastScCol; ++nIndex )
2749                 {
2750                     mxMergedcells->AppendRange( aScRange, nXFId );
2751                     aScRange.aStart.IncCol();
2752                     aScRange.aEnd.IncCol();
2753                 }
2754             }
2755 
2756             // data validation
2757             if( ScfTools::CheckItem( rItemSet, ATTR_VALIDDATA, false ) )
2758             {
2759                 sal_uInt32 nScHandle = rItemSet.Get( ATTR_VALIDDATA ).GetValue();
2760                 ScRange aScRange( aScPos );
2761                 aScRange.aEnd.SetCol( nLastScCol );
2762                 mxDval->InsertCellRange( aScRange, nScHandle );
2763             }
2764         }
2765     }
2766 
2767     // create missing row settings for rows anyhow flagged or with outlines
2768     maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, nFirstUngroupedScRow ) );
2769 }
2770 
Finalize(bool bXLS)2771 void XclExpCellTable::Finalize(bool bXLS)
2772 {
2773     // Finalize multiple operations.
2774     maTableopBfr.Finalize();
2775 
2776     /*  Finalize column buffer. This calculates column default XF indexes from
2777         the XF identifiers and fills a vector with these XF indexes. */
2778     ScfUInt16Vec aColXFIndexes;
2779     maColInfoBfr.Finalize( aColXFIndexes, bXLS );
2780 
2781     // Usually many indexes towards the end will be EXC_XF_DEFAULTCELL, find
2782     // the index that starts all EXC_XF_DEFAULTCELL until the end.
2783     size_t nStartColAllDefault = findFirstAllSameUntilEnd( aColXFIndexes, EXC_XF_DEFAULTCELL );
2784 
2785     /*  Finalize row buffer. This calculates all cell XF indexes from the XF
2786         identifiers. Then the XF index vector aColXFIndexes (filled above) is
2787         used to calculate the row default formats. With this, all unneeded blank
2788         cell records (equal to row default or column default) will be removed.
2789         The function returns the (most used) default row format in aDefRowData. */
2790     XclExpDefaultRowData aDefRowData;
2791     maRowBfr.Finalize( aDefRowData, aColXFIndexes, nStartColAllDefault );
2792 
2793     // Initialize the DEFROWHEIGHT record.
2794     mxDefrowheight->SetDefaultData( aDefRowData );
2795 }
2796 
CreateRecord(sal_uInt16 nRecId) const2797 XclExpRecordRef XclExpCellTable::CreateRecord( sal_uInt16 nRecId ) const
2798 {
2799     XclExpRecordRef xRec;
2800     switch( nRecId )
2801     {
2802         case EXC_ID3_DIMENSIONS:    xRec = new XclExpDelegatingRecord( &const_cast<XclExpRowBuffer*>(&maRowBfr)->GetDimensions() );   break;
2803         case EXC_ID2_DEFROWHEIGHT:  xRec = mxDefrowheight;  break;
2804         case EXC_ID_GUTS:           xRec = mxGuts;          break;
2805         case EXC_ID_NOTE:           xRec = mxNoteList;      break;
2806         case EXC_ID_MERGEDCELLS:    xRec = mxMergedcells;   break;
2807         case EXC_ID_HLINK:          xRec = mxHyperlinkList; break;
2808         case EXC_ID_DVAL:           xRec = mxDval;          break;
2809         case EXC_ID_EXTLST:         xRec = mxExtLst;        break;
2810         default:    OSL_FAIL( "XclExpCellTable::CreateRecord - unknown record id" );
2811     }
2812     return xRec;
2813 }
2814 
Save(XclExpStream & rStrm)2815 void XclExpCellTable::Save( XclExpStream& rStrm )
2816 {
2817     // DEFCOLWIDTH and COLINFOs
2818     maColInfoBfr.Save( rStrm );
2819     // ROWs and cell records
2820     maRowBfr.Save( rStrm );
2821 }
2822 
SaveXml(XclExpXmlStream & rStrm)2823 void XclExpCellTable::SaveXml( XclExpXmlStream& rStrm )
2824 {
2825     // DEFAULT row height
2826     XclExpDefaultRowData& rDefData = mxDefrowheight->GetDefaultData();
2827     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2828     rWorksheet->startElement( XML_sheetFormatPr,
2829         // OOXTODO: XML_baseColWidth
2830         XML_defaultColWidth, OString::number(maColInfoBfr.GetDefColWidth()),
2831         // OOXTODO: XML_thickTop
2832         // OOXTODO: XML_thickBottom
2833         XML_defaultRowHeight, OString::number(static_cast<double> (rDefData.mnHeight) / 20.0),
2834         XML_customHeight, ToPsz(true),
2835         XML_zeroHeight, ToPsz( rDefData.IsHidden() ),
2836         XML_outlineLevelRow, OString::number(maRowBfr.GetHighestOutlineLevel()),
2837         XML_outlineLevelCol, OString::number(maColInfoBfr.GetHighestOutlineLevel()) );
2838     rWorksheet->endElement( XML_sheetFormatPr );
2839 
2840     maColInfoBfr.SaveXml( rStrm );
2841     maRowBfr.SaveXml( rStrm );
2842     mxExtLst->SaveXml( rStrm );
2843 }
2844 
2845 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2846