xref: /core/sc/source/filter/excel/xetable.cxx (revision 117f4c05)
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->IsWrapped();
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        bool bForceLineBreak = pPattern->GetItem(ATTR_LINEBREAK ).GetValue();
749        SetXFId( rRoot.GetXFBuffer().InsertWithFont( pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak ) );
750     }
751 
752     // get auto-wrap attribute from cell format
753     const XclExpXF* pXF = rRoot.GetXFBuffer().GetXFById( GetXFId() );
754     mbLineBreak = pXF && pXF->GetAlignmentData().mbLineBreak;
755 
756     // initialize the record contents
757     switch( rRoot.GetBiff() )
758     {
759         case EXC_BIFF5:
760             // BIFF5-BIFF7: create a LABEL or RSTRING record
761             OSL_ENSURE( mxText->Len() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::XclExpLabelCell - string too long" );
762             SetContSize( mxText->GetSize() );
763             // formatted string is exported in an RSTRING record
764             if( mxText->IsRich() )
765             {
766                 OSL_ENSURE( mxText->GetFormatsCount() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::WriteContents - too many formats" );
767                 mxText->LimitFormatCount( EXC_LABEL_MAXLEN );
768                 SetRecId( EXC_ID_RSTRING );
769                 SetContSize( GetContSize() + 1 + 2 * mxText->GetFormatsCount() );
770             }
771         break;
772         case EXC_BIFF8:
773             // BIFF8+: create a LABELSST record
774             mnSstIndex = rRoot.GetSst().Insert( xText );
775             SetRecId( EXC_ID_LABELSST );
776             SetContSize( 4 );
777         break;
778         default:    DBG_ERROR_BIFF();
779     }
780 }
781 
SaveXml(XclExpXmlStream & rStrm)782 void XclExpLabelCell::SaveXml( XclExpXmlStream& rStrm )
783 {
784     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
785     rWorksheet->startElement( XML_c,
786             XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
787             XML_s, lcl_GetStyleId(rStrm, *this),
788             XML_t, "s"
789             // OOXTODO: XML_cm, XML_vm, XML_ph
790     );
791     rWorksheet->startElement( XML_v );
792     rWorksheet->write( static_cast<sal_Int32>(mnSstIndex) );
793     rWorksheet->endElement( XML_v );
794     rWorksheet->endElement( XML_c );
795 }
796 
WriteContents(XclExpStream & rStrm)797 void XclExpLabelCell::WriteContents( XclExpStream& rStrm )
798 {
799     switch( rStrm.GetRoot().GetBiff() )
800     {
801         case EXC_BIFF5:
802             rStrm << *mxText;
803             if( mxText->IsRich() )
804             {
805                 rStrm << static_cast< sal_uInt8 >( mxText->GetFormatsCount() );
806                 mxText->WriteFormats( rStrm );
807             }
808         break;
809         case EXC_BIFF8:
810             rStrm << mnSstIndex;
811         break;
812         default:    DBG_ERROR_BIFF();
813     }
814 }
815 
XclExpFormulaCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,const ScFormulaCell & rScFmlaCell,XclExpArrayBuffer & rArrayBfr,XclExpShrfmlaBuffer & rShrfmlaBfr,XclExpTableopBuffer & rTableopBfr)816 XclExpFormulaCell::XclExpFormulaCell(
817         const XclExpRoot& rRoot, const XclAddress& rXclPos,
818         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
819         const ScFormulaCell& rScFmlaCell,
820         XclExpArrayBuffer& rArrayBfr,
821         XclExpShrfmlaBuffer& rShrfmlaBfr,
822         XclExpTableopBuffer& rTableopBfr ) :
823     XclExpSingleCellBase( EXC_ID2_FORMULA, 0, rXclPos, nForcedXFId ),
824     mrScFmlaCell( const_cast< ScFormulaCell& >( rScFmlaCell ) )
825 {
826     // *** Find result number format overwriting cell number format *** -------
827 
828     if( GetXFId() == EXC_XFID_NOTFOUND )
829     {
830         SvNumberFormatter& rFormatter = rRoot.GetFormatter();
831         XclExpNumFmtBuffer& rNumFmtBfr = rRoot.GetNumFmtBuffer();
832 
833         // current cell number format
834         sal_uInt32 nScNumFmt = pPattern ?
835             pPattern->GetItem( ATTR_VALUE_FORMAT ).GetValue() :
836             rNumFmtBfr.GetStandardFormat();
837 
838         // alternative number format passed to XF buffer
839         sal_uInt32 nAltScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
840         /*  Xcl doesn't know Boolean number formats, we write
841             "TRUE";"FALSE" (language dependent). Don't do it for automatic
842             formula formats, because Excel gets them right. */
843         /*  #i8640# Don't set text format, if we have string results. */
844         SvNumFormatType nFormatType = mrScFmlaCell.GetFormatType();
845         if( ((nScNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0) &&
846                 (nFormatType != SvNumFormatType::LOGICAL) &&
847                 (nFormatType != SvNumFormatType::TEXT) )
848             nAltScNumFmt = nScNumFmt;
849         /*  If cell number format is Boolean and automatic formula
850             format is Boolean don't write that ugly special format. */
851         else if( (nFormatType == SvNumFormatType::LOGICAL) &&
852                 (rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL) )
853             nAltScNumFmt = rNumFmtBfr.GetStandardFormat();
854 
855         // #i41420# find script type according to result type (always latin for numeric results)
856         sal_Int16 nScript = ApiScriptType::LATIN;
857         bool bForceLineBreak = false;
858         if( nFormatType == SvNumFormatType::TEXT )
859         {
860             OUString aResult = mrScFmlaCell.GetString().getString();
861             bForceLineBreak = mrScFmlaCell.IsMultilineResult();
862             nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult );
863         }
864         SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) );
865     }
866 
867     // *** Convert the formula token array *** --------------------------------
868 
869     ScAddress aScPos( static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), rRoot.GetCurrScTab() );
870     const ScTokenArray& rScTokArr = *mrScFmlaCell.GetCode();
871 
872     // first try to create multiple operations
873     mxAddRec = rTableopBfr.CreateOrExtendTableop( rScTokArr, aScPos );
874 
875     // no multiple operation found - try to create matrix formula
876     if( !mxAddRec )
877         switch( mrScFmlaCell.GetMatrixFlag() )
878         {
879             case ScMatrixMode::Formula:
880             {
881                 // origin of the matrix - find the used matrix range
882                 SCCOL nMatWidth;
883                 SCROW nMatHeight;
884                 mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
885                 OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
886                 ScRange aMatScRange( aScPos );
887                 ScAddress& rMatEnd = aMatScRange.aEnd;
888                 rMatEnd.IncCol( static_cast< SCCOL >( nMatWidth - 1 ) );
889                 rMatEnd.IncRow( static_cast< SCROW >( nMatHeight - 1 ) );
890                 // reduce to valid range (range keeps valid, because start position IS valid)
891                 rRoot.GetAddressConverter().ValidateRange( aMatScRange, true );
892                 // create the ARRAY record
893                 mxAddRec = rArrayBfr.CreateArray( rScTokArr, aMatScRange );
894             }
895             break;
896             case ScMatrixMode::Reference:
897             {
898                 // other formula cell covered by a matrix - find the ARRAY record
899                 mxAddRec = rArrayBfr.FindArray(rScTokArr, aScPos);
900                 // should always be found, if Calc document is not broken
901                 OSL_ENSURE( mxAddRec, "XclExpFormulaCell::XclExpFormulaCell - no matrix found" );
902             }
903             break;
904             default:;
905         }
906 
907     // no matrix found - try to create shared formula
908     if( !mxAddRec )
909         mxAddRec = rShrfmlaBfr.CreateOrExtendShrfmla(mrScFmlaCell, aScPos);
910 
911     // no shared formula found - create a simple cell formula
912     if( !mxAddRec )
913         mxTokArr = rRoot.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL, rScTokArr, &aScPos );
914 }
915 
Save(XclExpStream & rStrm)916 void XclExpFormulaCell::Save( XclExpStream& rStrm )
917 {
918     // create token array for FORMULA cells with additional record
919     if( mxAddRec )
920         mxTokArr = mxAddRec->CreateCellTokenArray( rStrm.GetRoot() );
921 
922     // FORMULA record itself
923     OSL_ENSURE( mxTokArr, "XclExpFormulaCell::Save - missing token array" );
924     if( !mxTokArr )
925         mxTokArr = rStrm.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA );
926     SetContSize( 16 + mxTokArr->GetSize() );
927     XclExpSingleCellBase::Save( rStrm );
928 
929     // additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record
930     if( mxAddRec && mxAddRec->IsBasePos( GetXclCol(), GetXclRow() ) )
931         mxAddRec->Save( rStrm );
932 
933     // STRING record for string result
934     if( mxStringRec )
935         mxStringRec->Save( rStrm );
936 }
937 
SaveXml(XclExpXmlStream & rStrm)938 void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
939 {
940     const char* sType = nullptr;
941     OUString    sValue;
942     XclXmlUtils::GetFormulaTypeAndValue( mrScFmlaCell, sType, sValue );
943     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
944     rWorksheet->startElement( XML_c,
945             XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
946             XML_s, lcl_GetStyleId(rStrm, *this),
947             XML_t, sType
948             // OOXTODO: XML_cm, XML_vm, XML_ph
949     );
950 
951     bool bWriteFormula = true;
952     bool bTagStarted = false;
953     ScAddress aScPos( static_cast< SCCOL >( GetXclPos().mnCol ),
954             static_cast< SCROW >( GetXclPos().mnRow ), rStrm.GetRoot().GetCurrScTab() );
955 
956     switch (mrScFmlaCell.GetMatrixFlag())
957     {
958         case ScMatrixMode::NONE:
959             break;
960         case ScMatrixMode::Reference:
961             bWriteFormula = false;
962             break;
963         case ScMatrixMode::Formula:
964             {
965                 // origin of the matrix - find the used matrix range
966                 SCCOL nMatWidth;
967                 SCROW nMatHeight;
968                 mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
969                 OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
970                 ScRange aMatScRange( aScPos );
971                 ScAddress& rMatEnd = aMatScRange.aEnd;
972                 rMatEnd.IncCol( static_cast< SCCOL >( nMatWidth - 1 ) );
973                 rMatEnd.IncRow( static_cast< SCROW >( nMatHeight - 1 ) );
974                 // reduce to valid range (range keeps valid, because start position IS valid
975                 rStrm.GetRoot().GetAddressConverter().ValidateRange( aMatScRange, true );
976 
977                 OStringBuffer sFmlaCellRange;
978                 if (rStrm.GetRoot().GetDoc().ValidRange(aMatScRange))
979                 {
980                     // calculate the cell range.
981                     sFmlaCellRange.append( XclXmlUtils::ToOString(
982                                 rStrm.GetRoot().GetStringBuf(), aMatScRange.aStart )
983                                 + OString::Concat(":"));
984                     sFmlaCellRange.append( XclXmlUtils::ToOString(
985                                     rStrm.GetRoot().GetStringBuf(), aMatScRange.aEnd ));
986                 }
987 
988                 if (    aMatScRange.aStart.Col() == GetXclPos().mnCol &&
989                         aMatScRange.aStart.Row() == static_cast<SCROW>(GetXclPos().mnRow))
990                 {
991                     rWorksheet->startElement( XML_f,
992                             XML_aca, ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
993                                 (mxAddRec && mxAddRec->IsVolatile())),
994                             XML_t, mxAddRec ? "array" : nullptr,
995                             XML_ref, !sFmlaCellRange.isEmpty()? sFmlaCellRange.getStr() : nullptr
996                             // OOXTODO: XML_dt2D,   bool
997                             // OOXTODO: XML_dtr,    bool
998                             // OOXTODO: XML_del1,   bool
999                             // OOXTODO: XML_del2,   bool
1000                             // OOXTODO: XML_r1,     ST_CellRef
1001                             // OOXTODO: XML_r2,     ST_CellRef
1002                             // OOXTODO: XML_ca,     bool
1003                             // OOXTODO: XML_si,     uint
1004                             // OOXTODO: XML_bx      bool
1005                     );
1006                     bTagStarted = true;
1007                 }
1008             }
1009             break;
1010     }
1011 
1012     if (bWriteFormula)
1013     {
1014         if (!bTagStarted)
1015         {
1016             rWorksheet->startElement( XML_f,
1017                     XML_aca, ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
1018                         (mxAddRec && mxAddRec->IsVolatile()) ) );
1019         }
1020         rWorksheet->writeEscaped( XclXmlUtils::ToOUString(
1021                     rStrm.GetRoot().GetCompileFormulaContext(), mrScFmlaCell.aPos, mrScFmlaCell.GetCode(),
1022                     mrScFmlaCell.GetErrCode()));
1023         rWorksheet->endElement( XML_f );
1024     }
1025 
1026     if( strcmp( sType, "inlineStr" ) == 0 )
1027     {
1028         rWorksheet->startElement(XML_is);
1029         rWorksheet->startElement(XML_t);
1030         rWorksheet->writeEscaped( sValue );
1031         rWorksheet->endElement( XML_t );
1032         rWorksheet->endElement( XML_is );
1033     }
1034     else
1035     {
1036         rWorksheet->startElement(XML_v);
1037         rWorksheet->writeEscaped( sValue );
1038         rWorksheet->endElement( XML_v );
1039     }
1040     rWorksheet->endElement( XML_c );
1041 }
1042 
WriteContents(XclExpStream & rStrm)1043 void XclExpFormulaCell::WriteContents( XclExpStream& rStrm )
1044 {
1045     FormulaError nScErrCode = mrScFmlaCell.GetErrCode();
1046     if( nScErrCode != FormulaError::NONE )
1047     {
1048         rStrm << EXC_FORMULA_RES_ERROR << sal_uInt8( 0 )
1049             << XclTools::GetXclErrorCode( nScErrCode )
1050             << sal_uInt8( 0 ) << sal_uInt16( 0 )
1051             << sal_uInt16( 0xFFFF );
1052     }
1053     else
1054     {
1055         // result of the formula
1056         switch( mrScFmlaCell.GetFormatType() )
1057         {
1058             case SvNumFormatType::NUMBER:
1059                 {
1060                     // either value or error code
1061                     rStrm << mrScFmlaCell.GetValue();
1062                 }
1063                 break;
1064 
1065             case SvNumFormatType::TEXT:
1066                 {
1067                     OUString aResult = mrScFmlaCell.GetString().getString();
1068                     if( !aResult.isEmpty() || (rStrm.GetRoot().GetBiff() <= EXC_BIFF5) )
1069                     {
1070                         rStrm << EXC_FORMULA_RES_STRING;
1071                         mxStringRec = new XclExpStringRec( rStrm.GetRoot(), aResult );
1072                     }
1073                     else
1074                         rStrm << EXC_FORMULA_RES_EMPTY;     // BIFF8 only
1075                     rStrm << sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
1076                 }
1077                 break;
1078 
1079             case SvNumFormatType::LOGICAL:
1080                 {
1081                     sal_uInt8 nXclValue = (mrScFmlaCell.GetValue() == 0.0) ? 0 : 1;
1082                     rStrm << EXC_FORMULA_RES_BOOL << sal_uInt8( 0 )
1083                         << nXclValue << sal_uInt8( 0 ) << sal_uInt16( 0 )
1084                         << sal_uInt16( 0xFFFF );
1085                 }
1086                 break;
1087 
1088             default:
1089                 rStrm << mrScFmlaCell.GetValue();
1090         }
1091     }
1092 
1093     // flags and formula token array
1094     sal_uInt16 nFlags = EXC_FORMULA_DEFAULTFLAGS;
1095     ::set_flag( nFlags, EXC_FORMULA_RECALC_ALWAYS, mxTokArr->IsVolatile() || (mxAddRec && mxAddRec->IsVolatile()) );
1096     ::set_flag( nFlags, EXC_FORMULA_SHARED, mxAddRec && (mxAddRec->GetRecId() == EXC_ID_SHRFMLA) );
1097     rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
1098 }
1099 
1100 // Multiple cell records ======================================================
1101 
XclExpMultiCellBase(sal_uInt16 nRecId,sal_uInt16 nMulRecId,std::size_t nContSize,const XclAddress & rXclPos)1102 XclExpMultiCellBase::XclExpMultiCellBase(
1103         sal_uInt16 nRecId, sal_uInt16 nMulRecId, std::size_t nContSize, const XclAddress& rXclPos ) :
1104     XclExpCellBase( nRecId, 0, rXclPos ),
1105     mnMulRecId( nMulRecId ),
1106     mnContSize( nContSize )
1107 {
1108 }
1109 
GetLastXclCol() const1110 sal_uInt16 XclExpMultiCellBase::GetLastXclCol() const
1111 {
1112     return GetXclCol() + GetCellCount() - 1;
1113 }
1114 
GetFirstXFId() const1115 sal_uInt32 XclExpMultiCellBase::GetFirstXFId() const
1116 {
1117     return maXFIds.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds.front().mnXFId;
1118 }
1119 
IsEmpty() const1120 bool XclExpMultiCellBase::IsEmpty() const
1121 {
1122     return maXFIds.empty();
1123 }
1124 
ConvertXFIndexes(const XclExpRoot & rRoot)1125 void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
1126 {
1127     for( auto& rXFId : maXFIds )
1128         rXFId.ConvertXFIndex( rRoot );
1129 }
1130 
Save(XclExpStream & rStrm)1131 void XclExpMultiCellBase::Save( XclExpStream& rStrm )
1132 {
1133     OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1134 
1135     XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1136     XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1137     XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1138     sal_uInt16 nBegXclCol = GetXclCol();
1139     sal_uInt16 nEndXclCol = nBegXclCol;
1140 
1141     while( aRangeEnd != aEnd )
1142     {
1143         // find begin of next used XF range
1144         aRangeBeg = aRangeEnd;
1145         nBegXclCol = nEndXclCol;
1146         while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1147         {
1148             nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1149             ++aRangeBeg;
1150         }
1151         // find end of next used XF range
1152         aRangeEnd = aRangeBeg;
1153         nEndXclCol = nBegXclCol;
1154         while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1155         {
1156             nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1157             ++aRangeEnd;
1158         }
1159 
1160         // export this range as a record
1161         if( aRangeBeg != aRangeEnd )
1162         {
1163             sal_uInt16 nCount = nEndXclCol - nBegXclCol;
1164             bool bIsMulti = nCount > 1;
1165             std::size_t nTotalSize = GetRecSize() + (2 + mnContSize) * nCount;
1166             if( bIsMulti ) nTotalSize += 2;
1167 
1168             rStrm.StartRecord( bIsMulti ? mnMulRecId : GetRecId(), nTotalSize );
1169             rStrm << static_cast<sal_uInt16> (GetXclRow()) << nBegXclCol;
1170 
1171             sal_uInt16 nRelCol = nBegXclCol - GetXclCol();
1172             for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1173             {
1174                 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1175                 {
1176                     rStrm << aIt->mnXFIndex;
1177                     WriteContents( rStrm, nRelCol );
1178                     ++nRelCol;
1179                 }
1180             }
1181             if( bIsMulti )
1182                 rStrm << static_cast< sal_uInt16 >( nEndXclCol - 1 );
1183             rStrm.EndRecord();
1184         }
1185     }
1186 }
1187 
SaveXml(XclExpXmlStream & rStrm)1188 void XclExpMultiCellBase::SaveXml( XclExpXmlStream& rStrm )
1189 {
1190     XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1191     XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1192     XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1193     sal_uInt16 nBegXclCol = GetXclCol();
1194     sal_uInt16 nEndXclCol = nBegXclCol;
1195 
1196     while( aRangeEnd != aEnd )
1197     {
1198         // find begin of next used XF range
1199         aRangeBeg = aRangeEnd;
1200         nBegXclCol = nEndXclCol;
1201         while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1202         {
1203             nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1204             ++aRangeBeg;
1205         }
1206         // find end of next used XF range
1207         aRangeEnd = aRangeBeg;
1208         nEndXclCol = nBegXclCol;
1209         while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1210         {
1211             nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1212             ++aRangeEnd;
1213         }
1214 
1215         // export this range as a record
1216         if( aRangeBeg != aRangeEnd )
1217         {
1218             sal_uInt16 nRelColIdx = nBegXclCol - GetXclCol();
1219             sal_Int32  nRelCol    = 0;
1220             for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1221             {
1222                 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1223                 {
1224                     WriteXmlContents(
1225                             rStrm,
1226                             XclAddress( static_cast<sal_uInt16>(nBegXclCol + nRelCol), GetXclRow() ),
1227                             aIt->mnXFIndex,
1228                             nRelColIdx );
1229                     ++nRelCol;
1230                     ++nRelColIdx;
1231                 }
1232             }
1233         }
1234     }
1235 }
1236 
GetCellCount() const1237 sal_uInt16 XclExpMultiCellBase::GetCellCount() const
1238 {
1239     return std::accumulate(maXFIds.begin(), maXFIds.end(), sal_uInt16(0),
1240         [](const sal_uInt16& rSum, const XclExpMultiXFId& rXFId) { return rSum + rXFId.mnCount; });
1241 }
1242 
AppendXFId(const XclExpMultiXFId & rXFId)1243 void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId& rXFId )
1244 {
1245     if( maXFIds.empty() || (maXFIds.back().mnXFId != rXFId.mnXFId) )
1246         maXFIds.push_back( rXFId );
1247     else
1248         maXFIds.back().mnCount += rXFId.mnCount;
1249 }
1250 
AppendXFId(const XclExpRoot & rRoot,const ScPatternAttr * pPattern,sal_uInt16 nScript,sal_uInt32 nForcedXFId,sal_uInt16 nCount)1251 void XclExpMultiCellBase::AppendXFId( const XclExpRoot& rRoot,
1252         const ScPatternAttr* pPattern, sal_uInt16 nScript, sal_uInt32 nForcedXFId, sal_uInt16 nCount )
1253 {
1254     sal_uInt32 nXFId = (nForcedXFId == EXC_XFID_NOTFOUND) ?
1255         rRoot.GetXFBuffer().Insert( pPattern, nScript ) : nForcedXFId;
1256     AppendXFId( XclExpMultiXFId( nXFId, nCount ) );
1257 }
1258 
TryMergeXFIds(const XclExpMultiCellBase & rCell)1259 bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase& rCell )
1260 {
1261     if( GetLastXclCol() + 1 == rCell.GetXclCol() )
1262     {
1263         maXFIds.insert( maXFIds.end(), rCell.maXFIds.begin(), rCell.maXFIds.end() );
1264         return true;
1265     }
1266     return false;
1267 }
1268 
GetXFIndexes(ScfUInt16Vec & rXFIndexes) const1269 void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1270 {
1271     OSL_ENSURE( GetLastXclCol() < rXFIndexes.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" );
1272     ScfUInt16Vec::iterator aDestIt = rXFIndexes.begin() + GetXclCol();
1273     for( const auto& rXFId : maXFIds )
1274     {
1275         ::std::fill( aDestIt, aDestIt + rXFId.mnCount, rXFId.mnXFIndex );
1276         aDestIt += rXFId.mnCount;
1277     }
1278 }
1279 
RemoveUnusedXFIndexes(const ScfUInt16Vec & rXFIndexes,size_t nStartAllNotFound)1280 void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound )
1281 {
1282     // save last column before calling maXFIds.clear()
1283     sal_uInt16 nLastXclCol = GetLastXclCol();
1284     OSL_ENSURE( nLastXclCol < rXFIndexes.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" );
1285 
1286     // build new XF index vector, containing passed XF indexes
1287     maXFIds.clear();
1288     // Process only all that possibly are not EXC_XF_NOTFOUND.
1289     size_t nEnd = std::min<size_t>(nLastXclCol + 1, nStartAllNotFound);
1290     for( size_t i = GetXclCol(); i < nEnd; ++i )
1291     {
1292         XclExpMultiXFId aXFId( 0 );
1293         // AppendXFId() tests XclExpXFIndex::mnXFId, set it too
1294         aXFId.mnXFId = aXFId.mnXFIndex = rXFIndexes[ i ];
1295         AppendXFId( aXFId );
1296     }
1297 
1298     // remove leading and trailing unused XF indexes
1299     if( !maXFIds.empty() && (maXFIds.front().mnXFIndex == EXC_XF_NOTFOUND) )
1300     {
1301         SetXclCol( GetXclCol() + maXFIds.front().mnCount );
1302         maXFIds.erase(maXFIds.begin(), maXFIds.begin() + 1);
1303     }
1304     if( !maXFIds.empty() && (maXFIds.back().mnXFIndex == EXC_XF_NOTFOUND) )
1305         maXFIds.pop_back();
1306 
1307     // The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND.
1308 }
1309 
GetStartColAllDefaultCell() const1310 sal_uInt16 XclExpMultiCellBase::GetStartColAllDefaultCell() const
1311 {
1312     sal_uInt16 col = GetXclCol();
1313     sal_uInt16 nMaxNonDefCol = col;
1314     for( const auto& rXFId : maXFIds )
1315     {
1316         col += rXFId.mnCount;
1317         if (rXFId.mnXFIndex != EXC_XF_DEFAULTCELL)
1318             nMaxNonDefCol = col;
1319     }
1320     return nMaxNonDefCol;
1321 }
1322 
XclExpBlankCell(const XclAddress & rXclPos,const XclExpMultiXFId & rXFId)1323 XclExpBlankCell::XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId ) :
1324     XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1325 {
1326     OSL_ENSURE( rXFId.mnCount > 0, "XclExpBlankCell::XclExpBlankCell - invalid count" );
1327     AppendXFId( rXFId );
1328 }
1329 
XclExpBlankCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,sal_uInt16 nLastXclCol,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId)1330 XclExpBlankCell::XclExpBlankCell(
1331         const XclExpRoot& rRoot, const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
1332         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId ) :
1333     XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1334 {
1335     OSL_ENSURE( rXclPos.mnCol <= nLastXclCol, "XclExpBlankCell::XclExpBlankCell - invalid column range" );
1336     // #i46627# use default script type instead of ApiScriptType::WEAK
1337     AppendXFId( rRoot, pPattern, rRoot.GetDefApiScript(), nForcedXFId, nLastXclCol - rXclPos.mnCol + 1 );
1338 }
1339 
TryMerge(const XclExpCellBase & rCell)1340 bool XclExpBlankCell::TryMerge( const XclExpCellBase& rCell )
1341 {
1342     const XclExpBlankCell* pBlankCell = dynamic_cast< const XclExpBlankCell* >( &rCell );
1343     return pBlankCell && TryMergeXFIds( *pBlankCell );
1344 }
1345 
GetBlankXFIndexes(ScfUInt16Vec & rXFIndexes) const1346 void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1347 {
1348     GetXFIndexes( rXFIndexes );
1349 }
1350 
RemoveUnusedBlankCells(const ScfUInt16Vec & rXFIndexes,size_t nStartAllNotFound)1351 void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound )
1352 {
1353     RemoveUnusedXFIndexes( rXFIndexes, nStartAllNotFound );
1354 }
1355 
WriteContents(XclExpStream &,sal_uInt16)1356 void XclExpBlankCell::WriteContents( XclExpStream& /*rStrm*/, sal_uInt16 /*nRelCol*/ )
1357 {
1358 }
1359 
WriteXmlContents(XclExpXmlStream & rStrm,const XclAddress & rAddress,sal_uInt32 nXFId,sal_uInt16)1360 void XclExpBlankCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 /* nRelCol */ )
1361 {
1362     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1363     rWorksheet->singleElement( XML_c,
1364             XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(),
1365             XML_s, lcl_GetStyleId(rStrm, nXFId) );
1366 }
1367 
XclExpRkCell(const XclExpRoot & rRoot,const XclAddress & rXclPos,const ScPatternAttr * pPattern,sal_uInt32 nForcedXFId,sal_Int32 nRkValue)1368 XclExpRkCell::XclExpRkCell(
1369         const XclExpRoot& rRoot, const XclAddress& rXclPos,
1370         const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_Int32 nRkValue ) :
1371     XclExpMultiCellBase( EXC_ID_RK, EXC_ID_MULRK, 4, rXclPos )
1372 {
1373     // #i41210# always use latin script for number cells - may look wrong for special number formats...
1374     AppendXFId( rRoot, pPattern, ApiScriptType::LATIN, nForcedXFId );
1375     maRkValues.push_back( nRkValue );
1376 }
1377 
TryMerge(const XclExpCellBase & rCell)1378 bool XclExpRkCell::TryMerge( const XclExpCellBase& rCell )
1379 {
1380     const XclExpRkCell* pRkCell = dynamic_cast< const XclExpRkCell* >( &rCell );
1381     if( pRkCell && TryMergeXFIds( *pRkCell ) )
1382     {
1383         maRkValues.insert( maRkValues.end(), pRkCell->maRkValues.begin(), pRkCell->maRkValues.end() );
1384         return true;
1385     }
1386     return false;
1387 }
1388 
WriteXmlContents(XclExpXmlStream & rStrm,const XclAddress & rAddress,sal_uInt32 nXFId,sal_uInt16 nRelCol)1389 void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol )
1390 {
1391     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1392     rWorksheet->startElement( XML_c,
1393             XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(),
1394             XML_s, lcl_GetStyleId(rStrm, nXFId),
1395             XML_t, "n"
1396             // OOXTODO: XML_cm, XML_vm, XML_ph
1397     );
1398     rWorksheet->startElement( XML_v );
1399     rWorksheet->write( XclTools::GetDoubleFromRK( maRkValues[ nRelCol ] ) );
1400     rWorksheet->endElement( XML_v );
1401     rWorksheet->endElement( XML_c );
1402 }
1403 
WriteContents(XclExpStream & rStrm,sal_uInt16 nRelCol)1404 void XclExpRkCell::WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol )
1405 {
1406     OSL_ENSURE( nRelCol < maRkValues.size(), "XclExpRkCell::WriteContents - overflow error" );
1407     rStrm << maRkValues[ nRelCol ];
1408 }
1409 
1410 // Rows and Columns
1411 
XclExpOutlineBuffer(const XclExpRoot & rRoot,bool bRows)1412 XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows ) :
1413         mpScOLArray( nullptr ),
1414         maLevelInfos( SC_OL_MAXDEPTH ),
1415         mnCurrLevel( 0 ),
1416         mbCurrCollapse( false )
1417 {
1418     if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
1419         mpScOLArray = &(bRows ? pOutlineTable->GetRowArray() : pOutlineTable->GetColArray());
1420 
1421     if( mpScOLArray )
1422         for( size_t nLevel = 0; nLevel < SC_OL_MAXDEPTH; ++nLevel )
1423             if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nLevel, 0 ) )
1424                 maLevelInfos[ nLevel ].mnScEndPos = pEntry->GetEnd();
1425 }
1426 
UpdateColRow(SCCOLROW nScPos)1427 void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos )
1428 {
1429     if( !mpScOLArray )
1430         return;
1431 
1432     // find open level index for passed position
1433     size_t nNewOpenScLevel = 0; // new open level (0-based Calc index)
1434     sal_uInt8 nNewLevel = 0;    // new open level (1-based Excel index)
1435 
1436     if( mpScOLArray->FindTouchedLevel( nScPos, nScPos, nNewOpenScLevel ) )
1437         nNewLevel = static_cast< sal_uInt8 >( nNewOpenScLevel + 1 );
1438     // else nNewLevel keeps 0 to show that there are no groups
1439 
1440     mbCurrCollapse = false;
1441     if( nNewLevel >= mnCurrLevel )
1442     {
1443         // new level(s) opened, or no level closed - update all level infos
1444         for( size_t nScLevel = 0; nScLevel <= nNewOpenScLevel; ++nScLevel )
1445         {
1446             /*  In each level: check if a new group is started (there may be
1447                 neighbored groups without gap - therefore check ALL levels). */
1448             if( maLevelInfos[ nScLevel ].mnScEndPos < nScPos )
1449             {
1450                 if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nScLevel, nScPos ) )
1451                 {
1452                     maLevelInfos[ nScLevel ].mnScEndPos = pEntry->GetEnd();
1453                     maLevelInfos[ nScLevel ].mbHidden = pEntry->IsHidden();
1454                 }
1455             }
1456         }
1457     }
1458     else
1459     {
1460         // level(s) closed - check if any of the closed levels are collapsed
1461         // Calc uses 0-based level indexes
1462         sal_uInt16 nOldOpenScLevel = mnCurrLevel - 1;
1463         for( sal_uInt16 nScLevel = nNewOpenScLevel + 1; !mbCurrCollapse && (nScLevel <= nOldOpenScLevel); ++nScLevel )
1464             mbCurrCollapse = maLevelInfos[ nScLevel ].mbHidden;
1465     }
1466 
1467     // cache new opened level
1468     mnCurrLevel = nNewLevel;
1469 }
1470 
XclExpGuts(const XclExpRoot & rRoot)1471 XclExpGuts::XclExpGuts( const XclExpRoot& rRoot ) :
1472     XclExpRecord( EXC_ID_GUTS, 8 ),
1473     mnColLevels( 0 ),
1474     mnColWidth( 0 ),
1475     mnRowLevels( 0 ),
1476     mnRowWidth( 0 )
1477 {
1478     const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() );
1479     if(!pOutlineTable)
1480         return;
1481 
1482     // column outline groups
1483     const ScOutlineArray& rColArray = pOutlineTable->GetColArray();
1484     mnColLevels = ulimit_cast< sal_uInt16 >( rColArray.GetDepth(), EXC_OUTLINE_MAX );
1485     if( mnColLevels )
1486     {
1487         ++mnColLevels;
1488         mnColWidth = 12 * mnColLevels + 5;
1489     }
1490 
1491     // row outline groups
1492     const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
1493     mnRowLevels = ulimit_cast< sal_uInt16 >( rRowArray.GetDepth(), EXC_OUTLINE_MAX );
1494     if( mnRowLevels )
1495     {
1496         ++mnRowLevels;
1497         mnRowWidth = 12 * mnRowLevels + 5;
1498     }
1499 }
1500 
WriteBody(XclExpStream & rStrm)1501 void XclExpGuts::WriteBody( XclExpStream& rStrm )
1502 {
1503     rStrm << mnRowWidth << mnColWidth << mnRowLevels << mnColLevels;
1504 }
1505 
XclExpDimensions(const XclExpRoot & rRoot)1506 XclExpDimensions::XclExpDimensions( const XclExpRoot& rRoot ) :
1507     mrRoot(rRoot),
1508     mnFirstUsedXclRow( 0 ),
1509     mnFirstFreeXclRow( 0 ),
1510     mnFirstUsedXclCol( 0 ),
1511     mnFirstFreeXclCol( 0 )
1512 {
1513     switch( rRoot.GetBiff() )
1514     {
1515         case EXC_BIFF2: SetRecHeader( EXC_ID2_DIMENSIONS, 8 );  break;
1516         case EXC_BIFF3:
1517         case EXC_BIFF4:
1518         case EXC_BIFF5: SetRecHeader( EXC_ID3_DIMENSIONS, 10 ); break;
1519         case EXC_BIFF8: SetRecHeader( EXC_ID3_DIMENSIONS, 14 ); break;
1520         default:        DBG_ERROR_BIFF();
1521     }
1522 }
1523 
SetDimensions(sal_uInt16 nFirstUsedXclCol,sal_uInt32 nFirstUsedXclRow,sal_uInt16 nFirstFreeXclCol,sal_uInt32 nFirstFreeXclRow)1524 void XclExpDimensions::SetDimensions(
1525         sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
1526         sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow )
1527 {
1528     mnFirstUsedXclRow = nFirstUsedXclRow;
1529     mnFirstFreeXclRow = nFirstFreeXclRow;
1530     mnFirstUsedXclCol = nFirstUsedXclCol;
1531     mnFirstFreeXclCol = nFirstFreeXclCol;
1532 }
1533 
SaveXml(XclExpXmlStream & rStrm)1534 void XclExpDimensions::SaveXml( XclExpXmlStream& rStrm )
1535 {
1536     ScRange aRange;
1537     aRange.aStart.SetRow( static_cast<SCROW>(mnFirstUsedXclRow) );
1538     aRange.aStart.SetCol( static_cast<SCCOL>(mnFirstUsedXclCol) );
1539 
1540     if( mnFirstFreeXclRow != mnFirstUsedXclRow && mnFirstFreeXclCol != mnFirstUsedXclCol )
1541     {
1542         aRange.aEnd.SetRow( static_cast<SCROW>(mnFirstFreeXclRow-1) );
1543         aRange.aEnd.SetCol( static_cast<SCCOL>(mnFirstFreeXclCol-1) );
1544     }
1545 
1546     aRange.PutInOrder();
1547     rStrm.GetCurrentStream()->singleElement( XML_dimension,
1548             // To be compatible with MS Office 2007,
1549             // we need full address notation format
1550             // e.g. "A1:AMJ177" and not partial like: "1:177".
1551             XML_ref, XclXmlUtils::ToOString(mrRoot.GetDoc(), aRange, true) );
1552 }
1553 
WriteBody(XclExpStream & rStrm)1554 void XclExpDimensions::WriteBody( XclExpStream& rStrm )
1555 {
1556     XclBiff eBiff = rStrm.GetRoot().GetBiff();
1557     if( eBiff == EXC_BIFF8 )
1558         rStrm << mnFirstUsedXclRow << mnFirstFreeXclRow;
1559     else
1560         rStrm << static_cast< sal_uInt16 >( mnFirstUsedXclRow ) << static_cast< sal_uInt16 >( mnFirstFreeXclRow );
1561     rStrm << mnFirstUsedXclCol << mnFirstFreeXclCol;
1562     if( eBiff >= EXC_BIFF3 )
1563         rStrm << sal_uInt16( 0 );
1564 }
1565 
1566 namespace {
1567 
lclGetCChCorrection(const XclExpRoot & rRoot)1568 double lclGetCChCorrection(const XclExpRoot& rRoot)
1569 {
1570     // Convert the correction from 1/256ths of a character size to count of chars
1571     // TODO: make to fit ECMA-376-1:2016 18.3.1.81 sheetFormatPr (Sheet Format Properties):
1572     // 5 pixels are added to the base width: 2 for margin padding on each side, plus 1 for gridline
1573     // So this should depend on rRoot.GetCharWidth(), not on font height
1574 
1575     tools::Long nFontHt = rRoot.GetFontBuffer().GetAppFontData().mnHeight;
1576     return XclTools::GetXclDefColWidthCorrection(nFontHt) / 256.0;
1577 }
1578 
1579 } // namespace
1580 
XclExpDefcolwidth(const XclExpRoot & rRoot)1581 XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot& rRoot ) :
1582     XclExpDoubleRecord(EXC_ID_DEFCOLWIDTH, EXC_DEFCOLWIDTH_DEF + lclGetCChCorrection(rRoot)),
1583     XclExpRoot( rRoot )
1584 {
1585 }
1586 
IsDefWidth(sal_uInt16 nXclColWidth) const1587 bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth ) const
1588 {
1589     // This formula is taking number of characters with GetValue()
1590     // and it is translating it into default column width.
1591     // https://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.column.aspx
1592     double defaultColumnWidth = 256.0 * GetValue();
1593 
1594     // exactly matched, if difference is less than 1/16 of a character to the left or to the right
1595     return std::abs(defaultColumnWidth - nXclColWidth) < 256.0 * 1.0 / 16.0;
1596 }
1597 
SetDefWidth(sal_uInt16 nXclColWidth,bool bXLS)1598 void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth, bool bXLS )
1599 {
1600     double fCCh = nXclColWidth / 256.0;
1601     if (bXLS)
1602     {
1603         const double fCorrection = lclGetCChCorrection(GetRoot());
1604         const double fCorrectedCCh = fCCh - fCorrection;
1605         // Now get the value which would be stored in XLS DefColWidth struct
1606         double fCChRound = std::round(fCorrectedCCh);
1607         // If default width was set to a value that is not representable as integral CCh between 0
1608         // and 255, then just ignore that value, and use an arbitrary default. That way, the stored
1609         // default might not represent the most used column width (or any used column width), but
1610         // that's OK, and it just means that those columns will explicitly store their width in
1611         // 1/256ths of char, and have fUserSet in their ColInfo records.
1612         if (fCChRound < 0.0 || fCChRound > 255.0 || std::abs(fCChRound - fCorrectedCCh) > 1.0 / 512)
1613             fCChRound = 8.0;
1614         fCCh = fCChRound + fCorrection;
1615     }
1616 
1617     SetValue(fCCh);
1618 }
1619 
Save(XclExpStream & rStrm)1620 void XclExpDefcolwidth::Save(XclExpStream& rStrm)
1621 {
1622     double fCorrectedCCh = GetValue() - lclGetCChCorrection(GetRoot());
1623     // Convert double to sal_uInt16
1624     XclExpUInt16Record aUInt16Rec(GetRecId(),
1625                                   static_cast<sal_uInt16>(std::round(fCorrectedCCh)));
1626     aUInt16Rec.Save(rStrm);
1627 }
1628 
XclExpColinfo(const XclExpRoot & rRoot,SCCOL nScCol,SCROW nLastScRow,XclExpColOutlineBuffer & rOutlineBfr)1629 XclExpColinfo::XclExpColinfo( const XclExpRoot& rRoot,
1630         SCCOL nScCol, SCROW nLastScRow, XclExpColOutlineBuffer& rOutlineBfr ) :
1631     XclExpRecord( EXC_ID_COLINFO, 12 ),
1632     XclExpRoot( rRoot ),
1633     mbCustomWidth( false ),
1634     mnWidth( 0 ),
1635     mnScWidth( 0 ),
1636     mnFlags( 0 ),
1637     mnOutlineLevel( 0 ),
1638     mnFirstXclCol( static_cast< sal_uInt16 >( nScCol ) ),
1639     mnLastXclCol( static_cast< sal_uInt16 >( nScCol ) )
1640 {
1641     ScDocument& rDoc = GetDoc();
1642     SCTAB nScTab = GetCurrScTab();
1643 
1644     // column default format
1645     maXFId.mnXFId = GetXFBuffer().Insert(
1646         rDoc.GetMostUsedPattern( nScCol, 0, nLastScRow, nScTab ), GetDefApiScript() );
1647 
1648     // column width. If column is hidden then we should return real value (not zero)
1649     sal_uInt16 nScWidth = rDoc.GetColWidth( nScCol, nScTab, false );
1650     mnWidth = XclTools::GetXclColumnWidth( nScWidth, GetCharWidth() );
1651     mnScWidth = convertTwipToMm100(nScWidth);
1652 
1653     // column flags
1654     ::set_flag( mnFlags, EXC_COLINFO_HIDDEN, rDoc.ColHidden(nScCol, nScTab) );
1655 
1656     // outline data
1657     rOutlineBfr.Update( nScCol );
1658     ::set_flag( mnFlags, EXC_COLINFO_COLLAPSED, rOutlineBfr.IsCollapsed() );
1659     ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 8, 3 );
1660     mnOutlineLevel = rOutlineBfr.GetLevel();
1661 }
1662 
ConvertXFIndexes()1663 void XclExpColinfo::ConvertXFIndexes()
1664 {
1665     maXFId.ConvertXFIndex( GetRoot() );
1666 }
1667 
IsDefault(const XclExpDefcolwidth & rDefColWidth)1668 bool XclExpColinfo::IsDefault( const XclExpDefcolwidth& rDefColWidth )
1669 {
1670     mbCustomWidth = !rDefColWidth.IsDefWidth(mnWidth);
1671     return (maXFId.mnXFIndex == EXC_XF_DEFAULTCELL) &&
1672            (mnFlags == 0) &&
1673            (mnOutlineLevel == 0) &&
1674            !mbCustomWidth;
1675 }
1676 
TryMerge(const XclExpColinfo & rColInfo)1677 bool XclExpColinfo::TryMerge( const XclExpColinfo& rColInfo )
1678 {
1679     if( (maXFId.mnXFIndex == rColInfo.maXFId.mnXFIndex) &&
1680         (mnWidth == rColInfo.mnWidth) &&
1681         (mnFlags == rColInfo.mnFlags) &&
1682         (mnOutlineLevel == rColInfo.mnOutlineLevel) &&
1683         (mnLastXclCol + 1 == rColInfo.mnFirstXclCol) )
1684     {
1685         mnLastXclCol = rColInfo.mnLastXclCol;
1686         return true;
1687     }
1688     return false;
1689 }
1690 
WriteBody(XclExpStream & rStrm)1691 void XclExpColinfo::WriteBody( XclExpStream& rStrm )
1692 {
1693     // if last column is equal to last possible column, Excel adds one more
1694     sal_uInt16 nLastXclCol = mnLastXclCol;
1695     if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
1696         ++nLastXclCol;
1697 
1698     rStrm   << mnFirstXclCol
1699             << nLastXclCol
1700             << mnWidth
1701             << maXFId.mnXFIndex
1702             << mnFlags
1703             << sal_uInt16( 0 );
1704 }
1705 
SaveXml(XclExpXmlStream & rStrm)1706 void XclExpColinfo::SaveXml( XclExpXmlStream& rStrm )
1707 {
1708     const double nExcelColumnWidth = mnScWidth / convertTwipToMm100<double>(GetCharWidth());
1709 
1710     // tdf#101363 In MS specification the output value is set with double precision after delimiter:
1711     // =Truncate(({width in pixels} - 5)/{Maximum Digit Width} * 100 + 0.5)/100
1712     // Explanation of magic numbers:
1713     // 5 number - are 4 pixels of margin padding (two on each side), plus 1 pixel padding for the gridlines.
1714     //            It is unknown if it should be applied during LibreOffice export
1715     // 100 number - used to limit precision to 0.01 with formula =Truncate( {value}*100+0.5 ) / 100
1716     // 0.5 number (0.005 to output value) - used to increase value before truncating,
1717     //            to avoid situation when 2.997 will be truncated to 2.99 and not to 3.00
1718     const double nTruncatedExcelColumnWidth = std::trunc( nExcelColumnWidth * 100.0 + 0.5 ) / 100.0;
1719     rStrm.GetCurrentStream()->singleElement( XML_col,
1720             // OOXTODO: XML_bestFit,
1721             XML_collapsed,      ToPsz( ::get_flag( mnFlags, EXC_COLINFO_COLLAPSED ) ),
1722             XML_customWidth,    ToPsz( mbCustomWidth ),
1723             XML_hidden,         ToPsz( ::get_flag( mnFlags, EXC_COLINFO_HIDDEN ) ),
1724             XML_outlineLevel,   OString::number(mnOutlineLevel),
1725             XML_max,            OString::number(mnLastXclCol + 1),
1726             XML_min,            OString::number(mnFirstXclCol + 1),
1727             // OOXTODO: XML_phonetic,
1728             XML_style,          lcl_GetStyleId(rStrm, maXFId.mnXFIndex),
1729             XML_width,          OString::number(nTruncatedExcelColumnWidth) );
1730 }
1731 
XclExpColinfoBuffer(const XclExpRoot & rRoot)1732 XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot& rRoot ) :
1733     XclExpRoot( rRoot ),
1734     maDefcolwidth( rRoot ),
1735     maOutlineBfr( rRoot ),
1736     mnHighestOutlineLevel( 0 )
1737 {
1738 }
1739 
Initialize(SCROW nLastScRow)1740 void XclExpColinfoBuffer::Initialize( SCROW nLastScRow )
1741 {
1742 
1743     for( sal_uInt16 nScCol = 0, nLastScCol = GetMaxPos().Col(); nScCol <= nLastScCol; ++nScCol )
1744     {
1745         maColInfos.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol, nLastScRow, maOutlineBfr ) );
1746         if( maOutlineBfr.GetLevel() > mnHighestOutlineLevel )
1747         {
1748            mnHighestOutlineLevel = maOutlineBfr.GetLevel();
1749         }
1750     }
1751 }
1752 
Finalize(ScfUInt16Vec & rXFIndexes,bool bXLS)1753 void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes, bool bXLS )
1754 {
1755     rXFIndexes.clear();
1756     rXFIndexes.reserve( maColInfos.GetSize() );
1757 
1758     if( !maColInfos.IsEmpty())
1759     {
1760         XclExpColinfo* xPrevRec = maColInfos.GetRecord( 0 );
1761         xPrevRec->ConvertXFIndexes();
1762         for( size_t nPos = 1; nPos < maColInfos.GetSize(); ++nPos )
1763         {
1764             XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
1765             xRec->ConvertXFIndexes();
1766 
1767             // try to merge with previous record
1768             if( xPrevRec->TryMerge( *xRec ) )
1769                 maColInfos.InvalidateRecord( nPos );
1770             else
1771                 xPrevRec = xRec;
1772         }
1773         maColInfos.RemoveInvalidatedRecords();
1774     }
1775 
1776     // put XF indexes into passed vector, collect use count of all different widths
1777     std::unordered_map< sal_uInt16, sal_uInt16 > aWidthMap;
1778     sal_uInt16 nMaxColCount = 0;
1779     sal_uInt16 nMaxUsedWidth = 0;
1780     for( size_t nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
1781     {
1782         const XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
1783         sal_uInt16 nColCount = xRec->GetColCount();
1784 
1785         // add XF index to passed vector
1786         rXFIndexes.resize( rXFIndexes.size() + nColCount, xRec->GetXFIndex() );
1787 
1788         // collect use count of column width
1789         sal_uInt16 nWidth = xRec->GetColWidth();
1790         sal_uInt16& rnMapCount = aWidthMap[ nWidth ];
1791         rnMapCount = rnMapCount + nColCount;
1792         if( rnMapCount > nMaxColCount )
1793         {
1794             nMaxColCount = rnMapCount;
1795             nMaxUsedWidth = nWidth;
1796         }
1797     }
1798     maDefcolwidth.SetDefWidth( nMaxUsedWidth, bXLS );
1799 
1800     // remove all default COLINFO records
1801     for( size_t nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
1802     {
1803         XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
1804         if( xRec->IsDefault( maDefcolwidth ) )
1805             maColInfos.InvalidateRecord( nPos );
1806     }
1807     maColInfos.RemoveInvalidatedRecords();
1808 }
1809 
Save(XclExpStream & rStrm)1810 void XclExpColinfoBuffer::Save( XclExpStream& rStrm )
1811 {
1812     // DEFCOLWIDTH
1813     maDefcolwidth.Save( rStrm );
1814     // COLINFO records
1815     maColInfos.Save( rStrm );
1816 }
1817 
SaveXml(XclExpXmlStream & rStrm)1818 void XclExpColinfoBuffer::SaveXml( XclExpXmlStream& rStrm )
1819 {
1820     if( maColInfos.IsEmpty() )
1821         return;
1822 
1823     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1824     rWorksheet->startElement(XML_cols);
1825     maColInfos.SaveXml( rStrm );
1826     rWorksheet->endElement( XML_cols );
1827 }
1828 
XclExpDefaultRowData()1829 XclExpDefaultRowData::XclExpDefaultRowData() :
1830     mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1831     mnHeight( EXC_DEFROW_DEFAULTHEIGHT )
1832 {
1833 }
1834 
XclExpDefaultRowData(const XclExpRow & rRow)1835 XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow& rRow ) :
1836     mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1837     mnHeight( rRow.GetHeight() )
1838 {
1839     ::set_flag( mnFlags, EXC_DEFROW_HIDDEN, rRow.IsHidden() );
1840     ::set_flag( mnFlags, EXC_DEFROW_UNSYNCED, rRow.IsUnsynced() );
1841 }
1842 
operator <(const XclExpDefaultRowData & rLeft,const XclExpDefaultRowData & rRight)1843 static bool operator<( const XclExpDefaultRowData& rLeft, const XclExpDefaultRowData& rRight )
1844 {
1845     return (rLeft.mnHeight < rRight.mnHeight) ||
1846         ((rLeft.mnHeight == rRight.mnHeight) && (rLeft.mnFlags < rRight.mnFlags));
1847 }
1848 
XclExpDefrowheight()1849 XclExpDefrowheight::XclExpDefrowheight() :
1850     XclExpRecord( EXC_ID3_DEFROWHEIGHT, 4 )
1851 {
1852 }
1853 
SetDefaultData(const XclExpDefaultRowData & rDefData)1854 void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData& rDefData )
1855 {
1856     maDefData = rDefData;
1857 }
1858 
WriteBody(XclExpStream & rStrm)1859 void XclExpDefrowheight::WriteBody( XclExpStream& rStrm )
1860 {
1861     OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1862     rStrm << maDefData.mnFlags << maDefData.mnHeight;
1863 }
1864 
XclExpRow(const XclExpRoot & rRoot,sal_uInt32 nXclRow,XclExpRowOutlineBuffer & rOutlineBfr,bool bAlwaysEmpty,bool bHidden,sal_uInt16 nHeight)1865 XclExpRow::XclExpRow( const XclExpRoot& rRoot, sal_uInt32 nXclRow,
1866         XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty, bool bHidden, sal_uInt16 nHeight ) :
1867     XclExpRecord( EXC_ID3_ROW, 16 ),
1868     XclExpRoot( rRoot ),
1869     mnXclRow( nXclRow ),
1870     mnHeight( nHeight ),
1871     mnFlags( EXC_ROW_DEFAULTFLAGS ),
1872     mnXFIndex( EXC_XF_DEFAULTCELL ),
1873     mnOutlineLevel( 0 ),
1874     mnXclRowRpt( 1 ),
1875     mnCurrentRow( nXclRow ),
1876     mbAlwaysEmpty( bAlwaysEmpty ),
1877     mbEnabled( true )
1878 {
1879     SCTAB nScTab = GetCurrScTab();
1880     SCROW nScRow = static_cast< SCROW >( mnXclRow );
1881 
1882     // *** Row flags *** ------------------------------------------------------
1883 
1884     CRFlags nRowFlags = GetDoc().GetRowFlags( nScRow, nScTab );
1885     bool bUserHeight( nRowFlags & CRFlags::ManualSize );
1886     ::set_flag( mnFlags, EXC_ROW_UNSYNCED, bUserHeight );
1887     ::set_flag( mnFlags, EXC_ROW_HIDDEN, bHidden );
1888 
1889     // *** Outline data *** ---------------------------------------------------
1890 
1891     rOutlineBfr.Update( nScRow );
1892     ::set_flag( mnFlags, EXC_ROW_COLLAPSED, rOutlineBfr.IsCollapsed() );
1893     ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 0, 3 );
1894     mnOutlineLevel = rOutlineBfr.GetLevel();
1895 
1896     // *** Progress bar *** ---------------------------------------------------
1897 
1898     XclExpProgressBar& rProgress = GetProgressBar();
1899     rProgress.IncRowRecordCount();
1900     rProgress.Progress();
1901 }
1902 
findFirstAllSameUntilEnd(const ScfUInt16Vec & rIndexes,sal_uInt16 value,size_t searchStart=std::numeric_limits<size_t>::max ())1903 static size_t findFirstAllSameUntilEnd( const ScfUInt16Vec& rIndexes, sal_uInt16 value,
1904     size_t searchStart = std::numeric_limits<size_t>::max())
1905 {
1906     for( size_t i = std::min(rIndexes.size(), searchStart); i >= 1; --i )
1907     {
1908         if( rIndexes[ i - 1 ] != value )
1909             return i;
1910     }
1911     return 0;
1912 }
1913 
AppendCell(XclExpCellRef const & xCell,bool bIsMergedBase)1914 void XclExpRow::AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase )
1915 {
1916     OSL_ENSURE( !mbAlwaysEmpty, "XclExpRow::AppendCell - row is marked to be always empty" );
1917     // try to merge with last existing cell
1918     InsertCell( xCell, maCellList.GetSize(), bIsMergedBase );
1919 }
1920 
Finalize(const ScfUInt16Vec & rColXFIndexes,ScfUInt16Vec & aXFIndexes,size_t nStartColAllDefault,bool bProgress)1921 void XclExpRow::Finalize( const ScfUInt16Vec& rColXFIndexes, ScfUInt16Vec& aXFIndexes, size_t nStartColAllDefault, bool bProgress )
1922 {
1923     size_t nPos, nSize;
1924 
1925     // *** Convert XF identifiers *** -----------------------------------------
1926 
1927     // additionally collect the blank XF indexes
1928     size_t nColCount = GetMaxPos().Col() + 1;
1929     OSL_ENSURE( rColXFIndexes.size() == nColCount, "XclExpRow::Finalize - wrong column XF index count" );
1930 
1931     // The vector should be preset to all items being EXC_XF_NOTFOUND, to avoid repeated allocations
1932     // and clearing.
1933     assert( aXFIndexes.size() == nColCount );
1934     assert( aXFIndexes.front() == EXC_XF_NOTFOUND );
1935     assert( aXFIndexes.back() == EXC_XF_NOTFOUND );
1936     for( nPos = 0, nSize = maCellList.GetSize(); nPos < nSize; ++nPos )
1937     {
1938         XclExpCellBase* pCell = maCellList.GetRecord( nPos );
1939         pCell->ConvertXFIndexes( GetRoot() );
1940         pCell->GetBlankXFIndexes( aXFIndexes );
1941     }
1942 
1943     // *** Fill gaps with BLANK/MULBLANK cell records *** ---------------------
1944 
1945     /*  This is needed because nonexistent cells in Calc are not formatted at all,
1946         but in Excel they would have the column default format. Blank cells that
1947         are equal to the respective column default are removed later in this function. */
1948     if( !mbAlwaysEmpty )
1949     {
1950         // XF identifier representing default cell XF
1951         XclExpMultiXFId aXFId( XclExpXFBuffer::GetDefCellXFId() );
1952         aXFId.ConvertXFIndex( GetRoot() );
1953 
1954         nPos = 0;
1955         while( nPos <= maCellList.GetSize() )  // don't cache list size, may change in the loop
1956         {
1957             // get column index that follows previous cell
1958             sal_uInt16 nFirstFreeXclCol = (nPos > 0) ? (maCellList.GetRecord( nPos - 1 )->GetLastXclCol() + 1) : 0;
1959             // get own column index
1960             sal_uInt16 nNextUsedXclCol = (nPos < maCellList.GetSize()) ? maCellList.GetRecord( nPos )->GetXclCol() : (GetMaxPos().Col() + 1);
1961 
1962             // is there a gap?
1963             if( nFirstFreeXclCol < nNextUsedXclCol )
1964             {
1965                 aXFId.mnCount = nNextUsedXclCol - nFirstFreeXclCol;
1966                 XclExpCellRef xNewCell = new XclExpBlankCell( XclAddress( nFirstFreeXclCol, mnXclRow ), aXFId );
1967                 // insert the cell, InsertCell() may merge it with existing BLANK records
1968                 InsertCell( xNewCell, nPos, false );
1969                 // insert default XF indexes into aXFIndexes
1970                 for( size_t i = nFirstFreeXclCol; i < nNextUsedXclCol; ++i )
1971                     aXFIndexes[ i ] = aXFId.mnXFIndex;
1972                 // don't step forward with nPos, InsertCell() may remove records
1973             }
1974             else
1975                 ++nPos;
1976         }
1977     }
1978 
1979     // *** Find default row format *** ----------------------------------------
1980 
1981     // Often there will be many EXC_XF_DEFAULTCELL at the end, optimize by ignoring them.
1982     size_t nStartSearchAllDefault = aXFIndexes.size();
1983     if( !maCellList.IsEmpty() && dynamic_cast< const XclExpBlankCell* >( maCellList.GetLastRecord()))
1984     {
1985         const XclExpBlankCell* pLastBlank = static_cast< const XclExpBlankCell* >( maCellList.GetLastRecord());
1986         assert(pLastBlank->GetLastXclCol() == aXFIndexes.size() - 1);
1987         nStartSearchAllDefault = pLastBlank->GetStartColAllDefaultCell();
1988     }
1989     size_t nStartAllDefault = findFirstAllSameUntilEnd( aXFIndexes, EXC_XF_DEFAULTCELL, nStartSearchAllDefault);
1990 
1991     // find most used XF index in the row
1992     sal_uInt16 nRowXFIndex = EXC_XF_DEFAULTCELL;
1993     const size_t nHalfIndexes = aXFIndexes.size() / 2;
1994     if( nStartAllDefault > nHalfIndexes ) // Otherwise most are EXC_XF_DEFAULTCELL.
1995     {
1996         // Very likely the most common one is going to be the last one.
1997         nRowXFIndex = aXFIndexes.back();
1998         size_t nStartLastSame = findFirstAllSameUntilEnd( aXFIndexes, nRowXFIndex );
1999         if( nStartLastSame > nHalfIndexes ) // No, find out the most used one by counting.
2000         {
2001             std::unordered_map< sal_uInt16, size_t > aIndexMap;
2002             size_t nMaxXFCount = 0;
2003             for( const auto& rXFIndex : aXFIndexes )
2004             {
2005                 if( rXFIndex != EXC_XF_NOTFOUND )
2006                 {
2007                     size_t& rnCount = aIndexMap[ rXFIndex ];
2008                     ++rnCount;
2009                     if( rnCount > nMaxXFCount )
2010                     {
2011                         nRowXFIndex = rXFIndex;
2012                         nMaxXFCount = rnCount;
2013                         if (nMaxXFCount > nHalfIndexes)
2014                         {
2015                             // No other XF index can have a greater usage count, we
2016                             // don't need to loop through the remaining cells.
2017                             // Specifically for the tail of unused default
2018                             // cells/columns this makes a difference.
2019                             break;  // for
2020                         }
2021                     }
2022                 }
2023             }
2024         }
2025     }
2026 
2027     // decide whether to use the row default XF index or column default XF indexes
2028     bool bUseColDefXFs = nRowXFIndex == EXC_XF_DEFAULTCELL;
2029     if( !bUseColDefXFs )
2030     {
2031         // count needed XF indexes for blank cells with and without row default XF index
2032         size_t nXFCountWithRowDefXF = 0;
2033         size_t nXFCountWithoutRowDefXF = 0;
2034         ScfUInt16Vec::const_iterator aColIt = rColXFIndexes.begin();
2035         for( const auto& rXFIndex : aXFIndexes )
2036         {
2037             sal_uInt16 nXFIndex = rXFIndex;
2038             if( nXFIndex != EXC_XF_NOTFOUND )
2039             {
2040                 if( nXFIndex != nRowXFIndex )
2041                     ++nXFCountWithRowDefXF;     // with row default XF index
2042                 if( nXFIndex != *aColIt )
2043                     ++nXFCountWithoutRowDefXF;  // without row default XF index
2044             }
2045             ++aColIt;
2046         }
2047 
2048         // use column XF indexes if this would cause less or equal number of BLANK records
2049         bUseColDefXFs = nXFCountWithoutRowDefXF <= nXFCountWithRowDefXF;
2050     }
2051 
2052     // *** Remove unused BLANK cell records *** -------------------------------
2053 
2054     size_t maxStartAllNotFound;
2055     if( bUseColDefXFs )
2056     {
2057         size_t maxStartAllDefault = std::max( nStartAllDefault, nStartColAllDefault );
2058         // use column default XF indexes
2059         // #i194#: remove cell XF indexes equal to column default XF indexes
2060         for( size_t i = 0; i < maxStartAllDefault; ++i )
2061         {
2062             if( aXFIndexes[ i ] == rColXFIndexes[ i ] )
2063                 aXFIndexes[ i ] = EXC_XF_NOTFOUND;
2064         }
2065         // They can differ only up to maxNonDefault, in the rest they are the same.
2066         for( size_t i = maxStartAllDefault; i < aXFIndexes.size(); ++i )
2067             aXFIndexes[ i ] = EXC_XF_NOTFOUND;
2068         maxStartAllNotFound = maxStartAllDefault;
2069     }
2070     else
2071     {
2072         // use row default XF index
2073         mnXFIndex = nRowXFIndex;
2074         ::set_flag( mnFlags, EXC_ROW_USEDEFXF );
2075         // #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index
2076         for( auto& rXFIndex : aXFIndexes )
2077             if( rXFIndex == nRowXFIndex )
2078                 rXFIndex = EXC_XF_NOTFOUND;
2079         maxStartAllNotFound = aXFIndexes.size();
2080     }
2081 
2082     // remove unused parts of BLANK/MULBLANK cell records
2083     size_t nStartAllNotFound = findFirstAllSameUntilEnd( aXFIndexes, EXC_XF_NOTFOUND, maxStartAllNotFound );
2084     nPos = 0;
2085     while( nPos < maCellList.GetSize() )   // do not cache list size, may change in the loop
2086     {
2087         XclExpCellBase* xCell = maCellList.GetRecord( nPos );
2088         xCell->RemoveUnusedBlankCells( aXFIndexes, nStartAllNotFound );
2089         if( xCell->IsEmpty() )
2090             maCellList.RemoveRecord( nPos );
2091         else
2092             ++nPos;
2093     }
2094     // Ensure it's all EXC_XF_NOTFOUND again for next reuse.
2095     for( size_t i = 0; i < nStartAllNotFound; ++i )
2096         aXFIndexes[ i ] = EXC_XF_NOTFOUND;
2097 
2098     // progress bar includes disabled rows; only update it in the lead thread.
2099     if (bProgress)
2100         GetProgressBar().Progress();
2101 }
GetFirstUsedXclCol() const2102 sal_uInt16 XclExpRow::GetFirstUsedXclCol() const
2103 {
2104     return maCellList.IsEmpty() ? 0 : maCellList.GetFirstRecord()->GetXclCol();
2105 }
2106 
GetFirstFreeXclCol() const2107 sal_uInt16 XclExpRow::GetFirstFreeXclCol() const
2108 {
2109     return maCellList.IsEmpty() ? 0 : (maCellList.GetLastRecord()->GetLastXclCol() + 1);
2110 }
2111 
IsDefaultable() const2112 bool XclExpRow::IsDefaultable() const
2113 {
2114     const sal_uInt16 nFlagsAlwaysMarkedAsDefault = EXC_ROW_DEFAULTFLAGS | EXC_ROW_HIDDEN | EXC_ROW_UNSYNCED;
2115     return !::get_flag( mnFlags, static_cast< sal_uInt16 >( ~nFlagsAlwaysMarkedAsDefault ) ) &&
2116            IsEmpty();
2117 }
2118 
DisableIfDefault(const XclExpDefaultRowData & rDefRowData)2119 void XclExpRow::DisableIfDefault( const XclExpDefaultRowData& rDefRowData )
2120 {
2121     mbEnabled = !IsDefaultable() ||
2122         (mnHeight != rDefRowData.mnHeight) ||
2123         (IsHidden() != rDefRowData.IsHidden()) ||
2124         (IsUnsynced() != rDefRowData.IsUnsynced());
2125 }
2126 
WriteCellList(XclExpStream & rStrm)2127 void XclExpRow::WriteCellList( XclExpStream& rStrm )
2128 {
2129     OSL_ENSURE( mbEnabled || maCellList.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" );
2130     maCellList.Save( rStrm );
2131 }
2132 
Save(XclExpStream & rStrm)2133 void XclExpRow::Save( XclExpStream& rStrm )
2134 {
2135     if( mbEnabled )
2136     {
2137         mnCurrentRow = mnXclRow;
2138         for ( sal_uInt32 i = 0; i < mnXclRowRpt; ++i, ++mnCurrentRow )
2139             XclExpRecord::Save( rStrm );
2140     }
2141 }
2142 
InsertCell(XclExpCellRef xCell,size_t nPos,bool bIsMergedBase)2143 void XclExpRow::InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase )
2144 {
2145     OSL_ENSURE( xCell, "XclExpRow::InsertCell - missing cell" );
2146 
2147     /*  If we have a multi-line text in a merged cell, and the resulting
2148         row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED
2149         flag to be true to ensure Excel works correctly. */
2150     if( bIsMergedBase && xCell->IsMultiLineText() )
2151         ::set_flag( mnFlags, EXC_ROW_UNSYNCED );
2152 
2153     // try to merge with previous cell, insert the new cell if not successful
2154     XclExpCellBase* xPrevCell = maCellList.GetRecord( nPos - 1 );
2155     if( xPrevCell && xPrevCell->TryMerge( *xCell ) )
2156         xCell = xPrevCell;
2157     else
2158         maCellList.InsertRecord( xCell, nPos++ );
2159     // nPos points now to following cell
2160 
2161     // try to merge with following cell, remove it if successful
2162     XclExpCellRef xNextCell = maCellList.GetRecord( nPos );
2163     if( xNextCell && xCell->TryMerge( *xNextCell ) )
2164         maCellList.RemoveRecord( nPos );
2165 }
2166 
WriteBody(XclExpStream & rStrm)2167 void XclExpRow::WriteBody( XclExpStream& rStrm )
2168 {
2169     rStrm   << static_cast< sal_uInt16 >(mnCurrentRow)
2170             << GetFirstUsedXclCol()
2171             << GetFirstFreeXclCol()
2172             << mnHeight
2173             << sal_uInt32( 0 )
2174             << mnFlags
2175             << mnXFIndex;
2176 }
2177 
SaveXml(XclExpXmlStream & rStrm)2178 void XclExpRow::SaveXml( XclExpXmlStream& rStrm )
2179 {
2180     if( !mbEnabled )
2181         return;
2182     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2183     bool haveFormat = ::get_flag( mnFlags, EXC_ROW_USEDEFXF );
2184     mnCurrentRow = mnXclRow + 1;
2185     for ( sal_uInt32 i=0; i<mnXclRowRpt; ++i )
2186     {
2187         rWorksheet->startElement( XML_row,
2188                 XML_r,              OString::number(mnCurrentRow++),
2189                 // OOXTODO: XML_spans,          optional
2190                 XML_s,              haveFormat ? lcl_GetStyleId( rStrm, mnXFIndex ).getStr() : nullptr,
2191                 XML_customFormat,   ToPsz( haveFormat ),
2192                 XML_ht,             OString::number(static_cast<double>(mnHeight) / 20.0),
2193                 XML_hidden,         ToPsz( ::get_flag( mnFlags, EXC_ROW_HIDDEN ) ),
2194                 XML_customHeight,   ToPsz( ::get_flag( mnFlags, EXC_ROW_UNSYNCED ) ),
2195                 XML_outlineLevel,   OString::number(mnOutlineLevel),
2196                 XML_collapsed,      ToPsz( ::get_flag( mnFlags, EXC_ROW_COLLAPSED ) )
2197                 // OOXTODO: XML_thickTop,       bool
2198                 // OOXTODO: XML_thickBot,       bool
2199                 // OOXTODO: XML_ph,             bool
2200         );
2201         // OOXTODO: XML_extLst
2202         maCellList.SaveXml( rStrm );
2203         rWorksheet->endElement( XML_row );
2204     }
2205 }
2206 
XclExpRowBuffer(const XclExpRoot & rRoot)2207 XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot& rRoot ) :
2208     XclExpRoot( rRoot ),
2209     maOutlineBfr( rRoot ),
2210     maDimensions( rRoot ),
2211     mnHighestOutlineLevel( 0 )
2212 {
2213 }
2214 
AppendCell(XclExpCellRef const & xCell,bool bIsMergedBase)2215 void XclExpRowBuffer::AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase )
2216 {
2217     OSL_ENSURE( xCell, "XclExpRowBuffer::AppendCell - missing cell" );
2218     GetOrCreateRow( xCell->GetXclRow(), false ).AppendCell( xCell, bIsMergedBase );
2219 }
2220 
CreateRows(SCROW nFirstFreeScRow)2221 void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow )
2222 {
2223     if( nFirstFreeScRow > 0 )
2224         GetOrCreateRow(  ::std::max ( nFirstFreeScRow - 1, GetMaxPos().Row() ), true );
2225 }
2226 
2227 namespace {
2228 
2229 class RowFinalizeTask : public comphelper::ThreadTask
2230 {
2231     bool mbProgress;
2232     const ScfUInt16Vec& mrColXFIndexes;
2233     size_t mnStartColAllDefault;
2234     std::vector< XclExpRow * > maRows;
2235 public:
RowFinalizeTask(const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,const ScfUInt16Vec & rColXFIndexes,size_t nStartColAllDefault,bool bProgress)2236              RowFinalizeTask( const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,
2237                               const ScfUInt16Vec& rColXFIndexes,
2238                               size_t nStartColAllDefault,
2239                               bool bProgress ) :
2240                  comphelper::ThreadTask( pTag ),
2241                  mbProgress( bProgress ),
2242                  mrColXFIndexes( rColXFIndexes ),
2243                  mnStartColAllDefault( nStartColAllDefault )
2244                  {}
2245 
push_back(XclExpRow * pRow)2246     void     push_back( XclExpRow *pRow ) { maRows.push_back( pRow ); }
doWork()2247     virtual void doWork() override
2248     {
2249         ScfUInt16Vec aXFIndexes( mrColXFIndexes.size(), EXC_XF_NOTFOUND );
2250         for (XclExpRow* p : maRows)
2251             p->Finalize( mrColXFIndexes, aXFIndexes, mnStartColAllDefault, mbProgress );
2252     }
2253 };
2254 
2255 }
2256 
Finalize(XclExpDefaultRowData & rDefRowData,const ScfUInt16Vec & rColXFIndexes,size_t nStartColAllDefault)2257 void XclExpRowBuffer::Finalize( XclExpDefaultRowData& rDefRowData,
2258                                 const ScfUInt16Vec& rColXFIndexes,
2259                                 size_t nStartColAllDefault )
2260 {
2261     // *** Finalize all rows *** ----------------------------------------------
2262 
2263     GetProgressBar().ActivateFinalRowsSegment();
2264 
2265 #if 1
2266     // This is staggeringly slow, and each element operates only
2267     // on its own data.
2268     const size_t nRows = maRowMap.size();
2269     const size_t nThreads = nRows < 128 ? 1 : comphelper::ThreadPool::getPreferredConcurrency();
2270 #else
2271     const size_t nThreads = 1; // globally disable multi-threading for now.
2272 #endif
2273     if (nThreads == 1)
2274     {
2275         ScfUInt16Vec aXFIndexes( rColXFIndexes.size(), EXC_XF_NOTFOUND );
2276         for (auto& rEntry : maRowMap)
2277             rEntry.second->Finalize( rColXFIndexes, aXFIndexes, nStartColAllDefault, true );
2278     }
2279     else
2280     {
2281         comphelper::ThreadPool &rPool = comphelper::ThreadPool::getSharedOptimalPool();
2282         std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
2283         std::vector<std::unique_ptr<RowFinalizeTask>> aTasks(nThreads);
2284         for ( size_t i = 0; i < nThreads; i++ )
2285             aTasks[ i ].reset( new RowFinalizeTask( pTag, rColXFIndexes, nStartColAllDefault, i == 0 ) );
2286 
2287         size_t nIdx = 0;
2288         for ( const auto& rEntry : maRowMap )
2289         {
2290             aTasks[ nIdx % nThreads ]->push_back( rEntry.second.get() );
2291             ++nIdx;
2292         }
2293 
2294         for ( size_t i = 1; i < nThreads; i++ )
2295             rPool.pushTask( std::move(aTasks[ i ]) );
2296 
2297         // Progress bar updates must be synchronous to avoid deadlock
2298         aTasks[0]->doWork();
2299 
2300         rPool.waitUntilDone(pTag);
2301     }
2302 
2303     // *** Default row format *** ---------------------------------------------
2304 
2305     std::map< XclExpDefaultRowData, size_t > aDefRowMap;
2306 
2307     XclExpDefaultRowData aMaxDefData;
2308     size_t nMaxDefCount = 0;
2309     // only look for default format in existing rows, if there are more than unused
2310     // if the row is hidden, then row xml must be created even if it not contain cells
2311     XclExpRow* pPrev = nullptr;
2312     std::vector< XclExpRow* > aRepeated;
2313     for (const auto& rEntry : maRowMap)
2314     {
2315         const RowRef& rRow = rEntry.second;
2316         if ( rRow->IsDefaultable() )
2317         {
2318             XclExpDefaultRowData aDefData( *rRow );
2319             size_t& rnDefCount = aDefRowMap[ aDefData ];
2320             ++rnDefCount;
2321             if( rnDefCount > nMaxDefCount )
2322             {
2323                 nMaxDefCount = rnDefCount;
2324                 aMaxDefData = aDefData;
2325             }
2326         }
2327         if ( pPrev )
2328         {
2329             if ( pPrev->IsDefaultable() )
2330             {
2331                 // if the previous row we processed is not
2332                 // defaultable then afaict the rows in between are
2333                 // not used ( and not repeatable )
2334                 sal_uInt32 nRpt =  rRow->GetXclRow() - pPrev->GetXclRow();
2335                 if ( nRpt > 1 )
2336                     aRepeated.push_back( pPrev );
2337                 pPrev->SetXclRowRpt( nRpt );
2338                 XclExpDefaultRowData aDefData( *pPrev );
2339                 size_t& rnDefCount = aDefRowMap[ aDefData ];
2340                 rnDefCount += ( pPrev->GetXclRowRpt() - 1 );
2341                 if( rnDefCount > nMaxDefCount )
2342                 {
2343                     nMaxDefCount = rnDefCount;
2344                     aMaxDefData = aDefData;
2345                 }
2346             }
2347         }
2348         pPrev = rRow.get();
2349     }
2350     // return the default row format to caller
2351     rDefRowData = aMaxDefData;
2352 
2353     // now disable repeating extra (empty) rows that are equal to the default row
2354     for (auto& rpRow : aRepeated)
2355     {
2356         if ( rpRow->GetXclRowRpt() > 1
2357              && rpRow->GetHeight() == rDefRowData.mnHeight
2358              && rpRow->IsHidden() == rDefRowData.IsHidden() )
2359         {
2360             rpRow->SetXclRowRpt( 1 );
2361         }
2362     }
2363 
2364     // *** Disable unused ROW records, find used area *** ---------------------
2365 
2366     sal_uInt16 nFirstUsedXclCol = SAL_MAX_UINT16;
2367     sal_uInt16 nFirstFreeXclCol = 0;
2368     sal_uInt32 nFirstUsedXclRow = SAL_MAX_UINT32;
2369     sal_uInt32 nFirstFreeXclRow = 0;
2370 
2371     for (const auto& rEntry : maRowMap)
2372     {
2373         const RowRef& rRow = rEntry.second;
2374         // disable unused rows
2375         rRow->DisableIfDefault( aMaxDefData );
2376 
2377         // find used column range
2378         if( !rRow->IsEmpty() )      // empty rows return (0...0) as used range
2379         {
2380             nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, rRow->GetFirstUsedXclCol() );
2381             nFirstFreeXclCol = ::std::max( nFirstFreeXclCol, rRow->GetFirstFreeXclCol() );
2382         }
2383 
2384         // find used row range
2385         if( rRow->IsEnabled() )
2386         {
2387             sal_uInt32 nXclRow = rRow->GetXclRow();
2388             nFirstUsedXclRow = ::std::min< sal_uInt32 >( nFirstUsedXclRow, nXclRow );
2389             nFirstFreeXclRow = ::std::max< sal_uInt32 >( nFirstFreeXclRow, nXclRow + 1 );
2390         }
2391     }
2392 
2393     // adjust start position, if there are no or only empty/disabled ROW records
2394     nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, nFirstFreeXclCol );
2395     nFirstUsedXclRow = ::std::min( nFirstUsedXclRow, nFirstFreeXclRow );
2396 
2397     // initialize the DIMENSIONS record
2398     maDimensions.SetDimensions(
2399         nFirstUsedXclCol, nFirstUsedXclRow, nFirstFreeXclCol, nFirstFreeXclRow );
2400 }
2401 
Save(XclExpStream & rStrm)2402 void XclExpRowBuffer::Save( XclExpStream& rStrm )
2403 {
2404     // DIMENSIONS record
2405     maDimensions.Save( rStrm );
2406 
2407     // save in blocks of 32 rows, each block contains first all ROWs, then all cells
2408     size_t nSize = maRowMap.size();
2409     RowMap::iterator itr = maRowMap.begin(), itrEnd = maRowMap.end();
2410     RowMap::iterator itrBlkStart = maRowMap.begin(), itrBlkEnd = maRowMap.begin();
2411     sal_uInt16 nStartXclRow = (nSize == 0) ? 0 : itr->second->GetXclRow();
2412 
2413     for (; itr != itrEnd; ++itr)
2414     {
2415         // find end of row block
2416         itrBlkEnd = std::find_if_not(itrBlkEnd, itrEnd,
2417             [&nStartXclRow](const RowMap::value_type& rRow) { return rRow.second->GetXclRow() - nStartXclRow < EXC_ROW_ROWBLOCKSIZE; });
2418 
2419         // write the ROW records
2420         std::for_each(itrBlkStart, itrBlkEnd, [&rStrm](const RowMap::value_type& rRow) { rRow.second->Save( rStrm ); });
2421 
2422         // write the cell records
2423         std::for_each(itrBlkStart, itrBlkEnd, [&rStrm](const RowMap::value_type& rRow) { rRow.second->WriteCellList( rStrm ); });
2424 
2425         itrBlkStart = (itrBlkEnd == itrEnd) ? itrBlkEnd : itrBlkEnd++;
2426         nStartXclRow += EXC_ROW_ROWBLOCKSIZE;
2427     }
2428 }
2429 
SaveXml(XclExpXmlStream & rStrm)2430 void XclExpRowBuffer::SaveXml( XclExpXmlStream& rStrm )
2431 {
2432     if (std::none_of(maRowMap.begin(), maRowMap.end(), [](const RowMap::value_type& rRow) { return rRow.second->IsEnabled(); }))
2433     {
2434         rStrm.GetCurrentStream()->singleElement(XML_sheetData);
2435         return;
2436     }
2437 
2438     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2439     rWorksheet->startElement(XML_sheetData);
2440     for (const auto& rEntry : maRowMap)
2441         rEntry.second->SaveXml(rStrm);
2442     rWorksheet->endElement( XML_sheetData );
2443 }
2444 
GetOrCreateRow(sal_uInt32 nXclRow,bool bRowAlwaysEmpty)2445 XclExpRow& XclExpRowBuffer::GetOrCreateRow( sal_uInt32 nXclRow, bool bRowAlwaysEmpty )
2446 {
2447     // This is called rather often, so optimize for the most common case of saving row by row
2448     // (so the requested row is often the last one in the map or belongs after the last one).
2449     RowMap::iterator itr;
2450     if(maRowMap.empty())
2451         itr = maRowMap.end();
2452     else
2453     {
2454         RowMap::reverse_iterator last = maRowMap.rbegin();
2455         if( last->first == nXclRow )
2456             return *last->second;
2457         if( nXclRow > last->first )
2458             itr = maRowMap.end();
2459         else
2460             itr = maRowMap.lower_bound( nXclRow );
2461     }
2462     const bool bFound = itr != maRowMap.end();
2463     // bFoundHigher: nXclRow was identical to the previous entry, so not explicitly created earlier
2464     // coverity[deref_iterator : FALSE] - clearly itr if only derefed if bFound which checks for valid itr
2465     const bool bFoundHigher = bFound && itr->first != nXclRow;
2466     if( bFound && !bFoundHigher )
2467         return *itr->second;
2468 
2469     size_t nFrom = 0;
2470     RowRef pPrevEntry;
2471     if( itr != maRowMap.begin() )
2472     {
2473         --itr;
2474         pPrevEntry = itr->second;
2475         if( bFoundHigher )
2476             nFrom = nXclRow;
2477         else
2478             nFrom = itr->first + 1;
2479     }
2480 
2481     const ScDocument& rDoc = GetRoot().GetDoc();
2482     const SCTAB nScTab = GetRoot().GetCurrScTab();
2483     // Do not repeatedly call RowHidden() / GetRowHeight() for same values.
2484     bool bHidden = false;
2485     SCROW lastSameHiddenRow = -1;
2486     sal_uInt16 nHeight = 0;
2487     SCROW lastSameHeightRow = -1;
2488     // create the missing rows first
2489     while( nFrom <= nXclRow )
2490     {
2491         // only create RowMap entries if it is first row in spreadsheet,
2492         // if it is the desired row, or for rows that differ from previous.
2493         if( static_cast<SCROW>(nFrom) > lastSameHiddenRow )
2494             bHidden = rDoc.RowHidden(nFrom, nScTab, nullptr, &lastSameHiddenRow);
2495         // Always get the actual row height even if the manual size flag is
2496         // not set, to correctly export the heights of rows with wrapped
2497         // texts.
2498         if( static_cast<SCROW>(nFrom) > lastSameHeightRow )
2499             nHeight = rDoc.GetRowHeight(nFrom, nScTab, nullptr, &lastSameHeightRow, false);
2500         if ( !pPrevEntry || ( nFrom == nXclRow ) ||
2501              ( maOutlineBfr.IsCollapsed() ) ||
2502              ( maOutlineBfr.GetLevel() != 0 ) ||
2503              ( bRowAlwaysEmpty && !pPrevEntry->IsEmpty() ) ||
2504              ( bHidden != pPrevEntry->IsHidden() ) ||
2505              ( nHeight != pPrevEntry->GetHeight() ) )
2506         {
2507             if( maOutlineBfr.GetLevel() > mnHighestOutlineLevel )
2508             {
2509                 mnHighestOutlineLevel = maOutlineBfr.GetLevel();
2510             }
2511             RowRef p = std::make_shared<XclExpRow>(GetRoot(), nFrom, maOutlineBfr, bRowAlwaysEmpty, bHidden, nHeight);
2512             maRowMap.emplace(nFrom, p);
2513             pPrevEntry = p;
2514         }
2515         ++nFrom;
2516     }
2517     itr = maRowMap.find(nXclRow);
2518     return *itr->second;
2519 }
2520 
2521 // Cell Table
2522 
XclExpCellTable(const XclExpRoot & rRoot)2523 XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) :
2524     XclExpRoot( rRoot ),
2525     maColInfoBfr( rRoot ),
2526     maRowBfr( rRoot ),
2527     maArrayBfr( rRoot ),
2528     maShrfmlaBfr( rRoot ),
2529     maTableopBfr( rRoot ),
2530     mxDefrowheight( new XclExpDefrowheight() ),
2531     mxGuts( new XclExpGuts( rRoot ) ),
2532     mxNoteList( new XclExpNoteList ),
2533     mxMergedcells( new XclExpMergedcells( rRoot ) ),
2534     mxHyperlinkList( new XclExpHyperlinkList ),
2535     mxDval( new XclExpDval( rRoot ) ),
2536     mxExtLst( new XclExtLst( rRoot ) )
2537 {
2538     ScDocument& rDoc = GetDoc();
2539     SCTAB nScTab = GetCurrScTab();
2540     SvNumberFormatter& rFormatter = GetFormatter();
2541 
2542     // maximum sheet limits
2543     SCCOL nMaxScCol = GetMaxPos().Col();
2544     SCROW nMaxScRow = GetMaxPos().Row();
2545 
2546     // find used area (non-empty cells)
2547     SCCOL nLastUsedScCol;
2548     SCROW nLastUsedScRow;
2549     rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
2550 
2551     if(nLastUsedScCol > nMaxScCol)
2552         nLastUsedScCol = nMaxScCol;
2553 
2554     // check extra blank rows to avoid of losing their not default settings (workaround for tdf#41425)
2555     nLastUsedScRow += 1000;
2556 
2557     if(nLastUsedScRow > nMaxScRow)
2558         nLastUsedScRow = nMaxScRow;
2559 
2560     ScRange aUsedRange( 0, 0, nScTab, nLastUsedScCol, nLastUsedScRow, nScTab );
2561     GetAddressConverter().ValidateRange( aUsedRange, true );
2562     nLastUsedScRow = aUsedRange.aEnd.Row();
2563 
2564     // first row without any set attributes (height/hidden/...)
2565     SCROW nFirstUnflaggedScRow = rDoc.GetLastFlaggedRow( nScTab ) + 1;
2566 
2567     // find range of outlines
2568     SCROW nFirstUngroupedScRow = 0;
2569     if( const ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable( nScTab ) )
2570     {
2571         SCCOLROW nScStartPos, nScEndPos;
2572         const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
2573         rRowArray.GetRange( nScStartPos, nScEndPos );
2574         // +1 because open/close button is in next row in Excel, +1 for "end->first unused"
2575         nFirstUngroupedScRow = static_cast< SCROW >( nScEndPos + 2 );
2576     }
2577 
2578     // column settings
2579     /*  #i30411# Files saved with SO7/OOo1.x with nonstandard default column
2580         formatting cause big Excel files, because all rows from row 1 to row
2581         32000 are exported. Now, if the used area goes exactly to row 32000,
2582         use this row as default and ignore all rows >32000.
2583         #i59220# Tolerance of +-128 rows for inserted/removed rows. */
2584     if( (31871 <= nLastUsedScRow) && (nLastUsedScRow <= 32127) && (nFirstUnflaggedScRow < nLastUsedScRow) && (nFirstUngroupedScRow <= nLastUsedScRow) )
2585         nMaxScRow = nLastUsedScRow;
2586     maColInfoBfr.Initialize( nMaxScRow );
2587 
2588     // range for cell iterator
2589     SCCOL nLastIterScCol = nMaxScCol;
2590     SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow, nMaxScRow );
2591     ScUsedAreaIterator aIt( rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow );
2592 
2593     // activate the correct segment and sub segment at the progress bar
2594     GetProgressBar().ActivateCreateRowsSegment();
2595 
2596     for( bool bIt = aIt.GetNext(); bIt; bIt = aIt.GetNext() )
2597     {
2598         SCCOL nScCol = aIt.GetStartCol();
2599         SCROW nScRow = aIt.GetRow();
2600         SCCOL nLastScCol = aIt.GetEndCol();
2601         ScAddress aScPos( nScCol, nScRow, nScTab );
2602 
2603         XclAddress aXclPos( static_cast< sal_uInt16 >( nScCol ), static_cast< sal_uInt32 >( nScRow ) );
2604         sal_uInt16 nLastXclCol = static_cast< sal_uInt16 >( nLastScCol );
2605 
2606         const ScRefCellValue& rScCell = aIt.GetCell();
2607         XclExpCellRef xCell;
2608 
2609         const ScPatternAttr* pPattern = aIt.GetPattern();
2610 
2611         // handle overlapped merged cells before creating the cell record
2612         sal_uInt32 nMergeBaseXFId = EXC_XFID_NOTFOUND;
2613         bool bIsMergedBase = false;
2614         if( pPattern )
2615         {
2616             const SfxItemSet& rItemSet = pPattern->GetItemSet();
2617             // base cell in a merged range
2618             const ScMergeAttr& rMergeItem = rItemSet.Get( ATTR_MERGE );
2619             bIsMergedBase = rMergeItem.IsMerged();
2620             /*  overlapped cell in a merged range; in Excel all merged cells
2621                 must contain same XF index, for correct border */
2622             const ScMergeFlagAttr& rMergeFlagItem = rItemSet.Get( ATTR_MERGE_FLAG );
2623             if( rMergeFlagItem.IsOverlapped() )
2624                 nMergeBaseXFId = mxMergedcells->GetBaseXFId( aScPos );
2625         }
2626 
2627         OUString aAddNoteText;    // additional text to be appended to a note
2628 
2629         switch (rScCell.getType())
2630         {
2631             case CELLTYPE_VALUE:
2632             {
2633                 double fValue = rScCell.getDouble();
2634 
2635                 if (pPattern)
2636                 {
2637                     OUString aUrl = pPattern->GetItem(ATTR_HYPERLINK).GetValue();
2638                     if (!aUrl.isEmpty())
2639                     {
2640                         rtl::Reference<XclExpHyperlink> aLink =
2641                             new XclExpHyperlink(GetRoot(), SvxURLField(aUrl, aUrl), aScPos);
2642                         mxHyperlinkList->AppendRecord(aLink);
2643                     }
2644                 }
2645 
2646                 // try to create a Boolean cell
2647                 if( pPattern && ((fValue == 0.0) || (fValue == 1.0)) )
2648                 {
2649                     sal_uInt32 nScNumFmt = pPattern->GetItem( ATTR_VALUE_FORMAT ).GetValue();
2650                     if( rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL )
2651                         xCell = new XclExpBooleanCell(
2652                             GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue != 0.0 );
2653                 }
2654 
2655                 // try to create an RK value (compressed floating-point number)
2656                 sal_Int32 nRkValue;
2657                 if( !xCell && XclTools::GetRKFromDouble( nRkValue, fValue ) )
2658                     xCell = new XclExpRkCell(
2659                         GetRoot(), aXclPos, pPattern, nMergeBaseXFId, nRkValue );
2660 
2661                 // else: simple floating-point number cell
2662                 if( !xCell )
2663                     xCell = new XclExpNumberCell(
2664                         GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue );
2665             }
2666             break;
2667 
2668             case CELLTYPE_STRING:
2669             {
2670                 xCell = new XclExpLabelCell(
2671                     GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.getSharedString()->getString());
2672             }
2673             break;
2674 
2675             case CELLTYPE_EDIT:
2676             {
2677                 XclExpHyperlinkHelper aLinkHelper( GetRoot(), aScPos );
2678                 xCell = new XclExpLabelCell(
2679                     GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.getEditText(), aLinkHelper);
2680 
2681                 // add a single created HLINK record to the record list
2682                 if( aLinkHelper.HasLinkRecord() )
2683                     mxHyperlinkList->AppendRecord( aLinkHelper.GetLinkRecord() );
2684                 // add list of multiple URLs to the additional cell note text
2685                 if( aLinkHelper.HasMultipleUrls() )
2686                     aAddNoteText = ScGlobal::addToken( aAddNoteText, aLinkHelper.GetUrlList(), '\n', 2 );
2687             }
2688             break;
2689 
2690             case CELLTYPE_FORMULA:
2691             {
2692                 if (pPattern)
2693                 {
2694                     OUString aUrl = pPattern->GetItem(ATTR_HYPERLINK).GetValue();
2695                     if (!aUrl.isEmpty())
2696                     {
2697                         rtl::Reference<XclExpHyperlink> aLink =
2698                             new XclExpHyperlink(GetRoot(), SvxURLField(aUrl, aUrl), aScPos);
2699                         mxHyperlinkList->AppendRecord(aLink);
2700                     }
2701                 }
2702 
2703                 xCell = new XclExpFormulaCell(
2704                     GetRoot(), aXclPos, pPattern, nMergeBaseXFId,
2705                     *rScCell.getFormula(), maArrayBfr, maShrfmlaBfr, maTableopBfr);
2706             }
2707             break;
2708 
2709             default:
2710                 OSL_FAIL( "XclExpCellTable::XclExpCellTable - unknown cell type" );
2711                 [[fallthrough]];
2712             case CELLTYPE_NONE:
2713             {
2714                 xCell = new XclExpBlankCell(
2715                     GetRoot(), aXclPos, nLastXclCol, pPattern, nMergeBaseXFId );
2716             }
2717             break;
2718         }
2719 
2720         assert(xCell && "can only reach here with xCell set");
2721 
2722         // insert the cell into the current row
2723         maRowBfr.AppendCell( xCell, bIsMergedBase );
2724 
2725         if ( !aAddNoteText.isEmpty()  )
2726             mxNoteList->AppendNewRecord( new XclExpNote( GetRoot(), aScPos, nullptr, aAddNoteText ) );
2727 
2728         // other sheet contents
2729         if( pPattern )
2730         {
2731             const SfxItemSet& rItemSet = pPattern->GetItemSet();
2732 
2733             // base cell in a merged range
2734             if( bIsMergedBase )
2735             {
2736                 const ScMergeAttr& rMergeItem = rItemSet.Get( ATTR_MERGE );
2737                 ScRange aScRange( aScPos );
2738                 aScRange.aEnd.IncCol( rMergeItem.GetColMerge() - 1 );
2739                 aScRange.aEnd.IncRow( rMergeItem.GetRowMerge() - 1 );
2740                 sal_uInt32 nXFId = xCell->GetFirstXFId();
2741                 // blank cells merged vertically may occur repeatedly
2742                 OSL_ENSURE( (aScRange.aStart.Col() == aScRange.aEnd.Col()) || (nScCol == nLastScCol),
2743                     "XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" );
2744                 for( SCCOL nIndex = nScCol; nIndex <= nLastScCol; ++nIndex )
2745                 {
2746                     mxMergedcells->AppendRange( aScRange, nXFId );
2747                     aScRange.aStart.IncCol();
2748                     aScRange.aEnd.IncCol();
2749                 }
2750             }
2751 
2752             // data validation
2753             if( ScfTools::CheckItem( rItemSet, ATTR_VALIDDATA, false ) )
2754             {
2755                 sal_uInt32 nScHandle = rItemSet.Get( ATTR_VALIDDATA ).GetValue();
2756                 ScRange aScRange( aScPos );
2757                 aScRange.aEnd.SetCol( nLastScCol );
2758                 mxDval->InsertCellRange( aScRange, nScHandle );
2759             }
2760         }
2761     }
2762 
2763     // create missing row settings for rows anyhow flagged or with outlines
2764     maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, nFirstUngroupedScRow ) );
2765 }
2766 
Finalize(bool bXLS)2767 void XclExpCellTable::Finalize(bool bXLS)
2768 {
2769     // Finalize multiple operations.
2770     maTableopBfr.Finalize();
2771 
2772     /*  Finalize column buffer. This calculates column default XF indexes from
2773         the XF identifiers and fills a vector with these XF indexes. */
2774     ScfUInt16Vec aColXFIndexes;
2775     maColInfoBfr.Finalize( aColXFIndexes, bXLS );
2776 
2777     // Usually many indexes towards the end will be EXC_XF_DEFAULTCELL, find
2778     // the index that starts all EXC_XF_DEFAULTCELL until the end.
2779     size_t nStartColAllDefault = findFirstAllSameUntilEnd( aColXFIndexes, EXC_XF_DEFAULTCELL );
2780 
2781     /*  Finalize row buffer. This calculates all cell XF indexes from the XF
2782         identifiers. Then the XF index vector aColXFIndexes (filled above) is
2783         used to calculate the row default formats. With this, all unneeded blank
2784         cell records (equal to row default or column default) will be removed.
2785         The function returns the (most used) default row format in aDefRowData. */
2786     XclExpDefaultRowData aDefRowData;
2787     maRowBfr.Finalize( aDefRowData, aColXFIndexes, nStartColAllDefault );
2788 
2789     // Initialize the DEFROWHEIGHT record.
2790     mxDefrowheight->SetDefaultData( aDefRowData );
2791 }
2792 
CreateRecord(sal_uInt16 nRecId) const2793 XclExpRecordRef XclExpCellTable::CreateRecord( sal_uInt16 nRecId ) const
2794 {
2795     XclExpRecordRef xRec;
2796     switch( nRecId )
2797     {
2798         case EXC_ID3_DIMENSIONS:    xRec = new XclExpDelegatingRecord( &const_cast<XclExpRowBuffer*>(&maRowBfr)->GetDimensions() );   break;
2799         case EXC_ID2_DEFROWHEIGHT:  xRec = mxDefrowheight;  break;
2800         case EXC_ID_GUTS:           xRec = mxGuts;          break;
2801         case EXC_ID_NOTE:           xRec = mxNoteList;      break;
2802         case EXC_ID_MERGEDCELLS:    xRec = mxMergedcells;   break;
2803         case EXC_ID_HLINK:          xRec = mxHyperlinkList; break;
2804         case EXC_ID_DVAL:           xRec = mxDval;          break;
2805         case EXC_ID_EXTLST:         xRec = mxExtLst;        break;
2806         default:    OSL_FAIL( "XclExpCellTable::CreateRecord - unknown record id" );
2807     }
2808     return xRec;
2809 }
2810 
Save(XclExpStream & rStrm)2811 void XclExpCellTable::Save( XclExpStream& rStrm )
2812 {
2813     // DEFCOLWIDTH and COLINFOs
2814     maColInfoBfr.Save( rStrm );
2815     // ROWs and cell records
2816     maRowBfr.Save( rStrm );
2817 }
2818 
SaveXml(XclExpXmlStream & rStrm)2819 void XclExpCellTable::SaveXml( XclExpXmlStream& rStrm )
2820 {
2821     // DEFAULT row height
2822     XclExpDefaultRowData& rDefData = mxDefrowheight->GetDefaultData();
2823     sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2824     rWorksheet->startElement( XML_sheetFormatPr,
2825         // OOXTODO: XML_baseColWidth
2826         XML_defaultColWidth, OString::number(maColInfoBfr.GetDefColWidth()),
2827         // OOXTODO: XML_customHeight
2828         // OOXTODO: XML_thickTop
2829         // OOXTODO: XML_thickBottom
2830         XML_defaultRowHeight, OString::number(static_cast<double> (rDefData.mnHeight) / 20.0),
2831         XML_zeroHeight, ToPsz( rDefData.IsHidden() ),
2832         XML_outlineLevelRow, OString::number(maRowBfr.GetHighestOutlineLevel()),
2833         XML_outlineLevelCol, OString::number(maColInfoBfr.GetHighestOutlineLevel()) );
2834     rWorksheet->endElement( XML_sheetFormatPr );
2835 
2836     maColInfoBfr.SaveXml( rStrm );
2837     maRowBfr.SaveXml( rStrm );
2838     mxExtLst->SaveXml( rStrm );
2839 }
2840 
2841 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2842