use insert function instead of for loop
[LibreOffice.git] / sc / source / filter / excel / xetable.cxx
blob0b287c404871d7a8b4567bd14b6eaa537297c042
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <xetable.hxx>
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>
50 using namespace ::oox;
52 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
54 // Helper records for cell records
56 XclExpStringRec::XclExpStringRec( const XclExpRoot& rRoot, const OUString& rResult ) :
57 XclExpRecord( EXC_ID3_STRING ),
58 mxResult( XclExpStringHelper::CreateString( rRoot, rResult ) )
60 OSL_ENSURE( (rRoot.GetBiff() <= EXC_BIFF5) || (mxResult->Len() > 0),
61 "XclExpStringRec::XclExpStringRec - empty result not allowed in BIFF8+" );
62 SetRecSize( mxResult->GetSize() );
65 void XclExpStringRec::WriteBody( XclExpStream& rStrm )
67 rStrm << *mxResult;
70 // Additional records for special formula ranges ==============================
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 )
78 maBaseXclPos.Set( static_cast< sal_uInt16 >( rScPos.Col() ), static_cast< sal_uInt16 >( rScPos.Row() ) );
79 maXclRange.maFirst = maXclRange.maLast = maBaseXclPos;
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 )
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;
96 bool XclExpRangeFmlaBase::IsBasePos( sal_uInt16 nXclCol, sal_uInt32 nXclRow ) const
98 return (maBaseXclPos.mnCol == nXclCol) && (maBaseXclPos.mnRow == nXclRow);
101 void XclExpRangeFmlaBase::Extend( const ScAddress& rScPos )
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 );
111 void XclExpRangeFmlaBase::WriteRangeAddress( XclExpStream& rStrm ) const
113 maXclRange.Write( rStrm, false );
116 // Array formulas =============================================================
118 XclExpArray::XclExpArray( const XclTokenArrayRef& xTokArr, const ScRange& rScRange ) :
119 XclExpRangeFmlaBase( EXC_ID3_ARRAY, 14 + xTokArr->GetSize(), rScRange ),
120 mxTokArr( xTokArr )
124 XclTokenArrayRef XclExpArray::CreateCellTokenArray( const XclExpRoot& rRoot ) const
126 return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
129 bool XclExpArray::IsVolatile() const
131 return mxTokArr->IsVolatile();
134 void XclExpArray::WriteBody( XclExpStream& rStrm )
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;
142 XclExpArrayBuffer::XclExpArrayBuffer( const XclExpRoot& rRoot ) :
143 XclExpRoot( rRoot )
147 XclExpArrayRef XclExpArrayBuffer::CreateArray( const ScTokenArray& rScTokArr, const ScRange& rScRange )
149 const ScAddress& rScPos = rScRange.aStart;
150 XclTokenArrayRef xTokArr = GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_MATRIX, rScTokArr, &rScPos );
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;
158 XclExpArrayRef XclExpArrayBuffer::FindArray( const ScTokenArray& rScTokArr, const ScAddress& rBasePos ) const
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;
166 const formula::FormulaToken* pToken = rScTokArr.GetArray()[0];
167 if (!pToken || pToken->GetOpCode() != ocMatRef)
168 // not a matrix reference token.
169 return xRec;
171 const ScSingleRefData& rRef = *pToken->GetSingleRef();
172 ScAddress aAbsPos = rRef.toAbs(GetRoot().GetDoc(), rBasePos);
173 XclExpArrayMap::const_iterator it = maRecMap.find(aAbsPos);
175 if (it != maRecMap.end())
176 xRec = it->second;
177 return xRec;
180 // Shared formulas ============================================================
182 XclExpShrfmla::XclExpShrfmla( const XclTokenArrayRef& xTokArr, const ScAddress& rScPos ) :
183 XclExpRangeFmlaBase( EXC_ID_SHRFMLA, 10 + xTokArr->GetSize(), rScPos ),
184 mxTokArr( xTokArr ),
185 mnUsedCount( 1 )
189 void XclExpShrfmla::ExtendRange( const ScAddress& rScPos )
191 Extend( rScPos );
192 ++mnUsedCount;
195 XclTokenArrayRef XclExpShrfmla::CreateCellTokenArray( const XclExpRoot& rRoot ) const
197 return rRoot.GetFormulaCompiler().CreateSpecialRefFormula( EXC_TOKID_EXP, maBaseXclPos );
200 bool XclExpShrfmla::IsVolatile() const
202 return mxTokArr->IsVolatile();
205 void XclExpShrfmla::WriteBody( XclExpStream& rStrm )
207 WriteRangeAddress( rStrm );
208 rStrm << sal_uInt8( 0 ) << mnUsedCount << *mxTokArr;
211 XclExpShrfmlaBuffer::XclExpShrfmlaBuffer( const XclExpRoot& rRoot ) :
212 XclExpRoot( rRoot )
216 bool XclExpShrfmlaBuffer::IsValidTokenArray( const ScTokenArray& rArray ) const
218 using namespace formula;
220 FormulaToken** pTokens = rArray.GetArray();
221 sal_uInt16 nLen = rArray.GetLen();
222 for (sal_uInt16 i = 0; i < nLen; ++i)
224 const FormulaToken* p = pTokens[i];
225 switch (p->GetType())
227 case svSingleRef:
229 const ScSingleRefData& rRefData = *p->GetSingleRef();
230 if (!GetFormulaCompiler().IsRef2D(rRefData))
231 // Excel's shared formula cannot include 3D reference.
232 return false;
234 break;
235 case svDoubleRef:
237 const ScComplexRefData& rRefData = *p->GetDoubleRef();
238 if (!GetFormulaCompiler().IsRef2D(rRefData))
239 // Excel's shared formula cannot include 3D reference.
240 return false;
242 break;
243 case svExternalSingleRef:
244 case svExternalDoubleRef:
245 case svExternalName:
246 // External references aren't allowed.
247 return false;
248 default:
252 return true;
255 XclExpShrfmlaRef XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
256 const ScFormulaCell& rScCell, const ScAddress& rScPos )
258 XclExpShrfmlaRef xRec;
259 const ScTokenArray* pShrdScTokArr = rScCell.GetSharedCode();
260 if (!pShrdScTokArr)
261 // This formula cell is not shared formula cell.
262 return xRec;
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;
269 if (!IsValidTokenArray(*pShrdScTokArr))
271 // We can't export this as shared formula.
272 maBadTokens.insert(pShrdScTokArr);
273 return xRec;
276 TokensType::iterator aIt = maRecMap.find(pShrdScTokArr);
277 if( aIt == maRecMap.end() )
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;
284 else
286 // extend existing record
287 OSL_ENSURE( aIt->second, "XclExpShrfmlaBuffer::CreateOrExtendShrfmla - missing record" );
288 xRec = aIt->second;
289 xRec->ExtendRange( rScPos );
292 return xRec;
295 // Multiple operations ========================================================
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 )
310 bool XclExpTableop::TryExtend( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
312 sal_uInt16 nXclCol = static_cast< sal_uInt16 >( rScPos.Col() );
313 sal_uInt16 nXclRow = static_cast< sal_uInt16 >( rScPos.Row() );
315 bool bOk = IsAppendable( nXclCol, nXclRow );
316 if( bOk )
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 );
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());
332 if( bOk ) switch( mnScMode )
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;
362 if( bOk )
364 // extend the cell range
365 OSL_ENSURE( IsAppendable( nXclCol, nXclRow ), "XclExpTableop::TryExtend - wrong cell address" );
366 Extend( rScPos );
367 mnLastAppXclCol = nXclCol;
371 return bOk;
374 void XclExpTableop::Finalize()
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) )
381 --maXclRange.maLast.mnRow;
382 mbValid = true;
385 // check if referred cells are outside of own range
386 if( !mbValid )
387 return;
389 switch( mnScMode )
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;
408 XclTokenArrayRef XclExpTableop::CreateCellTokenArray( const XclExpRoot& rRoot ) const
410 XclExpFormulaCompiler& rFmlaComp = rRoot.GetFormulaCompiler();
411 return mbValid ?
412 rFmlaComp.CreateSpecialRefFormula( EXC_TOKID_TBL, maBaseXclPos ) :
413 rFmlaComp.CreateErrorFormula( EXC_ERR_NA );
416 bool XclExpTableop::IsVolatile() const
418 return true;
421 void XclExpTableop::Save( XclExpStream& rStrm )
423 if( mbValid )
424 XclExpRangeFmlaBase::Save( rStrm );
427 bool XclExpTableop::IsAppendable( sal_uInt16 nXclCol, sal_uInt16 nXclRow ) const
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));
434 void XclExpTableop::WriteBody( XclExpStream& rStrm )
436 sal_uInt16 nFlags = EXC_TABLEOP_DEFAULTFLAGS;
437 ::set_flag( nFlags, EXC_TABLEOP_RECALC_ALWAYS, IsVolatile() );
438 switch( mnScMode )
440 case 1: ::set_flag( nFlags, EXC_TABLEOP_ROW ); break;
441 case 2: ::set_flag( nFlags, EXC_TABLEOP_BOTH ); break;
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 );
452 XclExpTableopBuffer::XclExpTableopBuffer( const XclExpRoot& rRoot ) :
453 XclExpRoot( rRoot )
457 XclExpTableopRef XclExpTableopBuffer::CreateOrExtendTableop(
458 const ScTokenArray& rScTokArr, const ScAddress& rScPos )
460 XclExpTableopRef xRec;
462 // try to extract cell references of a multiple operations formula
463 XclMultipleOpRefs aRefs;
464 if (XclTokenArrayHelper::GetMultipleOpRefs(GetDoc(), aRefs, rScTokArr, rScPos))
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 )
469 XclExpTableop* xTempRec = maTableopList.GetRecord( nPos );
470 if( xTempRec->TryExtend( rScPos, aRefs ) )
471 xRec = xTempRec;
474 // no record found, or found record not extensible
475 if( !xRec )
476 xRec = TryCreate( rScPos, aRefs );
479 return xRec;
482 void XclExpTableopBuffer::Finalize()
484 for( size_t nPos = 0, nSize = maTableopList.GetSize(); nPos < nSize; ++nPos )
485 maTableopList.GetRecord( nPos )->Finalize();
488 XclExpTableopRef XclExpTableopBuffer::TryCreate( const ScAddress& rScPos, const XclMultipleOpRefs& rRefs )
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());
495 if( bOk )
497 if( rRefs.mbDblRefMode )
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());
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()) )
514 nScMode = 0;
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) )
521 nScMode = 1;
523 else
525 bOk = false;
529 XclExpTableopRef xRec;
530 if( bOk )
532 xRec = new XclExpTableop( rScPos, rRefs, nScMode );
533 maTableopList.AppendRecord( xRec );
536 return xRec;
539 // Cell records
541 XclExpCellBase::XclExpCellBase(
542 sal_uInt16 nRecId, std::size_t nContSize, const XclAddress& rXclPos ) :
543 XclExpRecord( nRecId, nContSize + 4 ),
544 maXclPos( rXclPos )
548 bool XclExpCellBase::IsMultiLineText() const
550 return false;
553 bool XclExpCellBase::TryMerge( const XclExpCellBase& /*rCell*/ )
555 return false;
558 void XclExpCellBase::GetBlankXFIndexes( ScfUInt16Vec& /*rXFIndexes*/ ) const
560 // default: do nothing
563 void XclExpCellBase::RemoveUnusedBlankCells( const ScfUInt16Vec& /*rXFIndexes*/, size_t /*nStartAllNotFound*/ )
565 // default: do nothing
568 // Single cell records ========================================================
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 )
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 )
585 if( GetXFId() == EXC_XFID_NOTFOUND )
586 SetXFId( rRoot.GetXFBuffer().Insert( pPattern, nScript ) );
589 sal_uInt16 XclExpSingleCellBase::GetLastXclCol() const
591 return GetXclCol();
594 sal_uInt32 XclExpSingleCellBase::GetFirstXFId() const
596 return GetXFId();
599 bool XclExpSingleCellBase::IsEmpty() const
601 return false;
604 void XclExpSingleCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
606 maXFId.ConvertXFIndex( rRoot );
609 void XclExpSingleCellBase::Save( XclExpStream& rStrm )
611 OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
612 AddRecSize( mnContSize );
613 XclExpCellBase::Save( rStrm );
616 void XclExpSingleCellBase::WriteBody( XclExpStream& rStrm )
618 rStrm << static_cast<sal_uInt16> (GetXclRow()) << GetXclCol() << maXFId.mnXFIndex;
619 WriteContents( rStrm );
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 )
631 static OString lcl_GetStyleId( const XclExpXmlStream& rStrm, sal_uInt32 nXFIndex )
633 return OString::number( rStrm.GetRoot().GetXFBuffer()
634 .GetXmlCellIndex( nXFIndex ) );
637 static OString lcl_GetStyleId( const XclExpXmlStream& rStrm, const XclExpCellBase& rCell )
639 sal_uInt32 nXFId = rCell.GetFirstXFId();
640 sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( nXFId );
641 return lcl_GetStyleId( rStrm, nXFIndex );
644 void XclExpNumberCell::SaveXml( XclExpXmlStream& rStrm )
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
653 rWorksheet->startElement(XML_v);
654 rWorksheet->write( mfValue );
655 rWorksheet->endElement( XML_v );
656 rWorksheet->endElement( XML_c );
659 void XclExpNumberCell::WriteContents( XclExpStream& rStrm )
661 rStrm << mfValue;
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 )
673 void XclExpBooleanCell::SaveXml( XclExpXmlStream& rStrm )
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
682 rWorksheet->startElement( XML_v );
683 rWorksheet->write( mbValue ? "1" : "0" );
684 rWorksheet->endElement( XML_v );
685 rWorksheet->endElement( XML_c );
688 void XclExpBooleanCell::WriteContents( XclExpStream& rStrm )
690 rStrm << sal_uInt16( mbValue ? 1 : 0 ) << EXC_BOOLERR_BOOL;
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 )
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 );
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 )
710 sal_uInt16 nMaxLen = (rRoot.GetBiff() == EXC_BIFF8) ? EXC_STR_MAXLEN : EXC_LABEL_MAXLEN;
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);
720 Init( rRoot, pPattern, xText );
723 bool XclExpLabelCell::IsMultiLineText() const
725 return mbLineBreak || mxText->HasNewline();
728 void XclExpLabelCell::Init( const XclExpRoot& rRoot,
729 const ScPatternAttr* pPattern, XclExpStringRef const & xText )
731 OSL_ENSURE( xText && xText->Len(), "XclExpLabelCell::XclExpLabelCell - empty string passed" );
732 mxText = xText;
733 mnSstIndex = 0;
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();
744 // create cell format
745 if( GetXFId() == EXC_XFID_NOTFOUND )
747 OSL_ENSURE(nXclFont != EXC_FONT_NOTFOUND, "XclExpLabelCell::Init - leading font not found");
749 // Buggy Excel behaviour - newlines are ignored unless wrap-text is enabled,
750 // so always force text-wrapping (unless it was imported that way and not modified).
751 bool bForceLineBreak = mxText->HasNewline() && !mxText->IsSingleLineForMultipleParagraphs();
752 SetXFId(rRoot.GetXFBuffer().InsertWithFont(
753 pPattern, ApiScriptType::WEAK, nXclFont, bForceLineBreak));
756 // get auto-wrap attribute from cell format
757 const XclExpXF* pXF = rRoot.GetXFBuffer().GetXFById( GetXFId() );
758 mbLineBreak = pXF && pXF->GetAlignmentData().mbLineBreak;
760 // initialize the record contents
761 switch( rRoot.GetBiff() )
763 case EXC_BIFF5:
764 // BIFF5-BIFF7: create a LABEL or RSTRING record
765 OSL_ENSURE( mxText->Len() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::XclExpLabelCell - string too long" );
766 SetContSize( mxText->GetSize() );
767 // formatted string is exported in an RSTRING record
768 if( mxText->IsRich() )
770 OSL_ENSURE( mxText->GetFormatsCount() <= EXC_LABEL_MAXLEN, "XclExpLabelCell::WriteContents - too many formats" );
771 mxText->LimitFormatCount( EXC_LABEL_MAXLEN );
772 SetRecId( EXC_ID_RSTRING );
773 SetContSize( GetContSize() + 1 + 2 * mxText->GetFormatsCount() );
775 break;
776 case EXC_BIFF8:
777 // BIFF8+: create a LABELSST record
778 mnSstIndex = rRoot.GetSst().Insert( xText );
779 SetRecId( EXC_ID_LABELSST );
780 SetContSize( 4 );
781 break;
782 default: DBG_ERROR_BIFF();
786 void XclExpLabelCell::SaveXml( XclExpXmlStream& rStrm )
788 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
789 rWorksheet->startElement( XML_c,
790 XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
791 XML_s, lcl_GetStyleId(rStrm, *this),
792 XML_t, "s"
793 // OOXTODO: XML_cm, XML_vm, XML_ph
795 rWorksheet->startElement( XML_v );
796 rWorksheet->write( static_cast<sal_Int32>(mnSstIndex) );
797 rWorksheet->endElement( XML_v );
798 rWorksheet->endElement( XML_c );
801 void XclExpLabelCell::WriteContents( XclExpStream& rStrm )
803 switch( rStrm.GetRoot().GetBiff() )
805 case EXC_BIFF5:
806 rStrm << *mxText;
807 if( mxText->IsRich() )
809 rStrm << static_cast< sal_uInt8 >( mxText->GetFormatsCount() );
810 mxText->WriteFormats( rStrm );
812 break;
813 case EXC_BIFF8:
814 rStrm << mnSstIndex;
815 break;
816 default: DBG_ERROR_BIFF();
820 XclExpFormulaCell::XclExpFormulaCell(
821 const XclExpRoot& rRoot, const XclAddress& rXclPos,
822 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId,
823 const ScFormulaCell& rScFmlaCell,
824 XclExpArrayBuffer& rArrayBfr,
825 XclExpShrfmlaBuffer& rShrfmlaBfr,
826 XclExpTableopBuffer& rTableopBfr ) :
827 XclExpSingleCellBase( EXC_ID2_FORMULA, 0, rXclPos, nForcedXFId ),
828 mrScFmlaCell( const_cast< ScFormulaCell& >( rScFmlaCell ) )
830 // *** Find result number format overwriting cell number format *** -------
832 if( GetXFId() == EXC_XFID_NOTFOUND )
834 SvNumberFormatter& rFormatter = rRoot.GetFormatter();
835 XclExpNumFmtBuffer& rNumFmtBfr = rRoot.GetNumFmtBuffer();
837 // current cell number format
838 sal_uInt32 nScNumFmt = pPattern ?
839 pPattern->GetItem( ATTR_VALUE_FORMAT ).GetValue() :
840 rNumFmtBfr.GetStandardFormat();
842 // alternative number format passed to XF buffer
843 sal_uInt32 nAltScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
844 /* Xcl doesn't know Boolean number formats, we write
845 "TRUE";"FALSE" (language dependent). Don't do it for automatic
846 formula formats, because Excel gets them right. */
847 /* #i8640# Don't set text format, if we have string results. */
848 SvNumFormatType nFormatType = mrScFmlaCell.GetFormatType();
849 if( ((nScNumFmt % SV_COUNTRY_LANGUAGE_OFFSET) == 0) &&
850 (nFormatType != SvNumFormatType::LOGICAL) &&
851 (nFormatType != SvNumFormatType::TEXT) )
852 nAltScNumFmt = nScNumFmt;
853 /* If cell number format is Boolean and automatic formula
854 format is Boolean don't write that ugly special format. */
855 else if( (nFormatType == SvNumFormatType::LOGICAL) &&
856 (rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL) )
857 nAltScNumFmt = rNumFmtBfr.GetStandardFormat();
859 // #i41420# find script type according to result type (always latin for numeric results)
860 sal_Int16 nScript = ApiScriptType::LATIN;
861 bool bForceLineBreak = false;
862 if( nFormatType == SvNumFormatType::TEXT )
864 OUString aResult = mrScFmlaCell.GetString().getString();
865 bForceLineBreak = mrScFmlaCell.IsMultilineResult();
866 nScript = XclExpStringHelper::GetLeadingScriptType( rRoot, aResult );
868 SetXFId( rRoot.GetXFBuffer().InsertWithNumFmt( pPattern, nScript, nAltScNumFmt, bForceLineBreak ) );
871 // *** Convert the formula token array *** --------------------------------
873 ScAddress aScPos( static_cast< SCCOL >( rXclPos.mnCol ), static_cast< SCROW >( rXclPos.mnRow ), rRoot.GetCurrScTab() );
874 const ScTokenArray& rScTokArr = *mrScFmlaCell.GetCode();
876 // first try to create multiple operations
877 mxAddRec = rTableopBfr.CreateOrExtendTableop( rScTokArr, aScPos );
879 // no multiple operation found - try to create matrix formula
880 if( !mxAddRec )
881 switch( mrScFmlaCell.GetMatrixFlag() )
883 case ScMatrixMode::Formula:
885 // origin of the matrix - find the used matrix range
886 SCCOL nMatWidth;
887 SCROW nMatHeight;
888 mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
889 OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
890 ScRange aMatScRange( aScPos );
891 ScAddress& rMatEnd = aMatScRange.aEnd;
892 rMatEnd.IncCol( static_cast< SCCOL >( nMatWidth - 1 ) );
893 rMatEnd.IncRow( static_cast< SCROW >( nMatHeight - 1 ) );
894 // reduce to valid range (range keeps valid, because start position IS valid)
895 rRoot.GetAddressConverter().ValidateRange( aMatScRange, true );
896 // create the ARRAY record
897 mxAddRec = rArrayBfr.CreateArray( rScTokArr, aMatScRange );
899 break;
900 case ScMatrixMode::Reference:
902 // other formula cell covered by a matrix - find the ARRAY record
903 mxAddRec = rArrayBfr.FindArray(rScTokArr, aScPos);
904 // should always be found, if Calc document is not broken
905 OSL_ENSURE( mxAddRec, "XclExpFormulaCell::XclExpFormulaCell - no matrix found" );
907 break;
908 default:;
911 // no matrix found - try to create shared formula
912 if( !mxAddRec )
913 mxAddRec = rShrfmlaBfr.CreateOrExtendShrfmla(mrScFmlaCell, aScPos);
915 // no shared formula found - create a simple cell formula
916 if( !mxAddRec )
917 mxTokArr = rRoot.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL, rScTokArr, &aScPos );
920 void XclExpFormulaCell::Save( XclExpStream& rStrm )
922 // create token array for FORMULA cells with additional record
923 if( mxAddRec )
924 mxTokArr = mxAddRec->CreateCellTokenArray( rStrm.GetRoot() );
926 // FORMULA record itself
927 OSL_ENSURE( mxTokArr, "XclExpFormulaCell::Save - missing token array" );
928 if( !mxTokArr )
929 mxTokArr = rStrm.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA );
930 SetContSize( 16 + mxTokArr->GetSize() );
931 XclExpSingleCellBase::Save( rStrm );
933 // additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record
934 if( mxAddRec && mxAddRec->IsBasePos( GetXclCol(), GetXclRow() ) )
935 mxAddRec->Save( rStrm );
937 // STRING record for string result
938 if( mxStringRec )
939 mxStringRec->Save( rStrm );
942 void XclExpFormulaCell::SaveXml( XclExpXmlStream& rStrm )
944 const char* sType = nullptr;
945 OUString sValue;
946 XclXmlUtils::GetFormulaTypeAndValue( mrScFmlaCell, sType, sValue );
947 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
948 rWorksheet->startElement( XML_c,
949 XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
950 XML_s, lcl_GetStyleId(rStrm, *this),
951 XML_t, sType
952 // OOXTODO: XML_cm, XML_vm, XML_ph
955 bool bWriteFormula = true;
956 bool bTagStarted = false;
957 ScAddress aScPos( static_cast< SCCOL >( GetXclPos().mnCol ),
958 static_cast< SCROW >( GetXclPos().mnRow ), rStrm.GetRoot().GetCurrScTab() );
960 switch (mrScFmlaCell.GetMatrixFlag())
962 case ScMatrixMode::NONE:
963 break;
964 case ScMatrixMode::Reference:
965 bWriteFormula = false;
966 break;
967 case ScMatrixMode::Formula:
969 // origin of the matrix - find the used matrix range
970 SCCOL nMatWidth;
971 SCROW nMatHeight;
972 mrScFmlaCell.GetMatColsRows( nMatWidth, nMatHeight );
973 OSL_ENSURE( nMatWidth && nMatHeight, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
974 ScRange aMatScRange( aScPos );
975 ScAddress& rMatEnd = aMatScRange.aEnd;
976 rMatEnd.IncCol( static_cast< SCCOL >( nMatWidth - 1 ) );
977 rMatEnd.IncRow( static_cast< SCROW >( nMatHeight - 1 ) );
978 // reduce to valid range (range keeps valid, because start position IS valid
979 rStrm.GetRoot().GetAddressConverter().ValidateRange( aMatScRange, true );
981 OStringBuffer sFmlaCellRange;
982 if (rStrm.GetRoot().GetDoc().ValidRange(aMatScRange))
984 // calculate the cell range.
985 sFmlaCellRange.append( XclXmlUtils::ToOString(
986 rStrm.GetRoot().GetStringBuf(), aMatScRange.aStart )
987 + OString::Concat(":"));
988 sFmlaCellRange.append( XclXmlUtils::ToOString(
989 rStrm.GetRoot().GetStringBuf(), aMatScRange.aEnd ));
992 if ( aMatScRange.aStart.Col() == GetXclPos().mnCol &&
993 aMatScRange.aStart.Row() == static_cast<SCROW>(GetXclPos().mnRow))
995 rWorksheet->startElement( XML_f,
996 XML_aca, ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
997 (mxAddRec && mxAddRec->IsVolatile())),
998 XML_t, mxAddRec ? "array" : nullptr,
999 XML_ref, !sFmlaCellRange.isEmpty()? sFmlaCellRange.getStr() : nullptr
1000 // OOXTODO: XML_dt2D, bool
1001 // OOXTODO: XML_dtr, bool
1002 // OOXTODO: XML_del1, bool
1003 // OOXTODO: XML_del2, bool
1004 // OOXTODO: XML_r1, ST_CellRef
1005 // OOXTODO: XML_r2, ST_CellRef
1006 // OOXTODO: XML_ca, bool
1007 // OOXTODO: XML_si, uint
1008 // OOXTODO: XML_bx bool
1010 bTagStarted = true;
1013 break;
1016 if (bWriteFormula)
1018 if (!bTagStarted)
1020 rWorksheet->startElement( XML_f,
1021 XML_aca, ToPsz( (mxTokArr && mxTokArr->IsVolatile()) ||
1022 (mxAddRec && mxAddRec->IsVolatile()) ) );
1024 rWorksheet->writeEscaped( XclXmlUtils::ToOUString(
1025 rStrm.GetRoot().GetCompileFormulaContext(), mrScFmlaCell.aPos, mrScFmlaCell.GetCode(),
1026 mrScFmlaCell.GetErrCode()));
1027 rWorksheet->endElement( XML_f );
1030 if( strcmp( sType, "inlineStr" ) == 0 )
1032 rWorksheet->startElement(XML_is);
1033 rWorksheet->startElement(XML_t);
1034 rWorksheet->writeEscaped( sValue );
1035 rWorksheet->endElement( XML_t );
1036 rWorksheet->endElement( XML_is );
1038 else
1040 rWorksheet->startElement(XML_v);
1041 rWorksheet->writeEscaped( sValue );
1042 rWorksheet->endElement( XML_v );
1044 rWorksheet->endElement( XML_c );
1047 void XclExpFormulaCell::WriteContents( XclExpStream& rStrm )
1049 FormulaError nScErrCode = mrScFmlaCell.GetErrCode();
1050 if( nScErrCode != FormulaError::NONE )
1052 rStrm << EXC_FORMULA_RES_ERROR << sal_uInt8( 0 )
1053 << XclTools::GetXclErrorCode( nScErrCode )
1054 << sal_uInt8( 0 ) << sal_uInt16( 0 )
1055 << sal_uInt16( 0xFFFF );
1057 else
1059 // result of the formula
1060 switch( mrScFmlaCell.GetFormatType() )
1062 case SvNumFormatType::NUMBER:
1064 // either value or error code
1065 rStrm << mrScFmlaCell.GetValue();
1067 break;
1069 case SvNumFormatType::TEXT:
1071 OUString aResult = mrScFmlaCell.GetString().getString();
1072 if( !aResult.isEmpty() || (rStrm.GetRoot().GetBiff() <= EXC_BIFF5) )
1074 rStrm << EXC_FORMULA_RES_STRING;
1075 mxStringRec = new XclExpStringRec( rStrm.GetRoot(), aResult );
1077 else
1078 rStrm << EXC_FORMULA_RES_EMPTY; // BIFF8 only
1079 rStrm << sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
1081 break;
1083 case SvNumFormatType::LOGICAL:
1085 sal_uInt8 nXclValue = (mrScFmlaCell.GetValue() == 0.0) ? 0 : 1;
1086 rStrm << EXC_FORMULA_RES_BOOL << sal_uInt8( 0 )
1087 << nXclValue << sal_uInt8( 0 ) << sal_uInt16( 0 )
1088 << sal_uInt16( 0xFFFF );
1090 break;
1092 default:
1093 rStrm << mrScFmlaCell.GetValue();
1097 // flags and formula token array
1098 sal_uInt16 nFlags = EXC_FORMULA_DEFAULTFLAGS;
1099 ::set_flag( nFlags, EXC_FORMULA_RECALC_ALWAYS, mxTokArr->IsVolatile() || (mxAddRec && mxAddRec->IsVolatile()) );
1100 ::set_flag( nFlags, EXC_FORMULA_SHARED, mxAddRec && (mxAddRec->GetRecId() == EXC_ID_SHRFMLA) );
1101 rStrm << nFlags << sal_uInt32( 0 ) << *mxTokArr;
1104 // Multiple cell records ======================================================
1106 XclExpMultiCellBase::XclExpMultiCellBase(
1107 sal_uInt16 nRecId, sal_uInt16 nMulRecId, std::size_t nContSize, const XclAddress& rXclPos ) :
1108 XclExpCellBase( nRecId, 0, rXclPos ),
1109 mnMulRecId( nMulRecId ),
1110 mnContSize( nContSize )
1114 sal_uInt16 XclExpMultiCellBase::GetLastXclCol() const
1116 return GetXclCol() + GetCellCount() - 1;
1119 sal_uInt32 XclExpMultiCellBase::GetFirstXFId() const
1121 return maXFIds.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds.front().mnXFId;
1124 bool XclExpMultiCellBase::IsEmpty() const
1126 return maXFIds.empty();
1129 void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot& rRoot )
1131 for( auto& rXFId : maXFIds )
1132 rXFId.ConvertXFIndex( rRoot );
1135 void XclExpMultiCellBase::Save( XclExpStream& rStrm )
1137 OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1139 XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1140 XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1141 XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1142 sal_uInt16 nBegXclCol = GetXclCol();
1143 sal_uInt16 nEndXclCol = nBegXclCol;
1145 while( aRangeEnd != aEnd )
1147 // find begin of next used XF range
1148 aRangeBeg = aRangeEnd;
1149 nBegXclCol = nEndXclCol;
1150 while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1152 nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1153 ++aRangeBeg;
1155 // find end of next used XF range
1156 aRangeEnd = aRangeBeg;
1157 nEndXclCol = nBegXclCol;
1158 while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1160 nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1161 ++aRangeEnd;
1164 // export this range as a record
1165 if( aRangeBeg != aRangeEnd )
1167 sal_uInt16 nCount = nEndXclCol - nBegXclCol;
1168 bool bIsMulti = nCount > 1;
1169 std::size_t nTotalSize = GetRecSize() + (2 + mnContSize) * nCount;
1170 if( bIsMulti ) nTotalSize += 2;
1172 rStrm.StartRecord( bIsMulti ? mnMulRecId : GetRecId(), nTotalSize );
1173 rStrm << static_cast<sal_uInt16> (GetXclRow()) << nBegXclCol;
1175 sal_uInt16 nRelCol = nBegXclCol - GetXclCol();
1176 for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1178 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1180 rStrm << aIt->mnXFIndex;
1181 WriteContents( rStrm, nRelCol );
1182 ++nRelCol;
1185 if( bIsMulti )
1186 rStrm << static_cast< sal_uInt16 >( nEndXclCol - 1 );
1187 rStrm.EndRecord();
1192 void XclExpMultiCellBase::SaveXml( XclExpXmlStream& rStrm )
1194 XclExpMultiXFIdDeq::const_iterator aEnd = maXFIds.end();
1195 XclExpMultiXFIdDeq::const_iterator aRangeBeg = maXFIds.begin();
1196 XclExpMultiXFIdDeq::const_iterator aRangeEnd = aRangeBeg;
1197 sal_uInt16 nBegXclCol = GetXclCol();
1198 sal_uInt16 nEndXclCol = nBegXclCol;
1200 while( aRangeEnd != aEnd )
1202 // find begin of next used XF range
1203 aRangeBeg = aRangeEnd;
1204 nBegXclCol = nEndXclCol;
1205 while( (aRangeBeg != aEnd) && (aRangeBeg->mnXFIndex == EXC_XF_NOTFOUND) )
1207 nBegXclCol = nBegXclCol + aRangeBeg->mnCount;
1208 ++aRangeBeg;
1210 // find end of next used XF range
1211 aRangeEnd = aRangeBeg;
1212 nEndXclCol = nBegXclCol;
1213 while( (aRangeEnd != aEnd) && (aRangeEnd->mnXFIndex != EXC_XF_NOTFOUND) )
1215 nEndXclCol = nEndXclCol + aRangeEnd->mnCount;
1216 ++aRangeEnd;
1219 // export this range as a record
1220 if( aRangeBeg != aRangeEnd )
1222 sal_uInt16 nRelColIdx = nBegXclCol - GetXclCol();
1223 sal_Int32 nRelCol = 0;
1224 for( XclExpMultiXFIdDeq::const_iterator aIt = aRangeBeg; aIt != aRangeEnd; ++aIt )
1226 for( sal_uInt16 nIdx = 0; nIdx < aIt->mnCount; ++nIdx )
1228 WriteXmlContents(
1229 rStrm,
1230 XclAddress( static_cast<sal_uInt16>(nBegXclCol + nRelCol), GetXclRow() ),
1231 aIt->mnXFIndex,
1232 nRelColIdx );
1233 ++nRelCol;
1234 ++nRelColIdx;
1241 sal_uInt16 XclExpMultiCellBase::GetCellCount() const
1243 return std::accumulate(maXFIds.begin(), maXFIds.end(), sal_uInt16(0),
1244 [](const sal_uInt16& rSum, const XclExpMultiXFId& rXFId) { return rSum + rXFId.mnCount; });
1247 void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId& rXFId )
1249 if( maXFIds.empty() || (maXFIds.back().mnXFId != rXFId.mnXFId) )
1250 maXFIds.push_back( rXFId );
1251 else
1252 maXFIds.back().mnCount += rXFId.mnCount;
1255 void XclExpMultiCellBase::AppendXFId( const XclExpRoot& rRoot,
1256 const ScPatternAttr* pPattern, sal_uInt16 nScript, sal_uInt32 nForcedXFId, sal_uInt16 nCount )
1258 sal_uInt32 nXFId = (nForcedXFId == EXC_XFID_NOTFOUND) ?
1259 rRoot.GetXFBuffer().Insert( pPattern, nScript ) : nForcedXFId;
1260 AppendXFId( XclExpMultiXFId( nXFId, nCount ) );
1263 bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase& rCell )
1265 if( GetLastXclCol() + 1 == rCell.GetXclCol() )
1267 maXFIds.insert( maXFIds.end(), rCell.maXFIds.begin(), rCell.maXFIds.end() );
1268 return true;
1270 return false;
1273 void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1275 OSL_ENSURE( GetLastXclCol() < rXFIndexes.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" );
1276 ScfUInt16Vec::iterator aDestIt = rXFIndexes.begin() + GetXclCol();
1277 for( const auto& rXFId : maXFIds )
1279 ::std::fill( aDestIt, aDestIt + rXFId.mnCount, rXFId.mnXFIndex );
1280 aDestIt += rXFId.mnCount;
1284 void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound )
1286 // save last column before calling maXFIds.clear()
1287 sal_uInt16 nLastXclCol = GetLastXclCol();
1288 OSL_ENSURE( nLastXclCol < rXFIndexes.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" );
1290 // build new XF index vector, containing passed XF indexes
1291 maXFIds.clear();
1292 // Process only all that possibly are not EXC_XF_NOTFOUND.
1293 size_t nEnd = std::min<size_t>(nLastXclCol + 1, nStartAllNotFound);
1294 for( size_t i = GetXclCol(); i < nEnd; ++i )
1296 XclExpMultiXFId aXFId( 0 );
1297 // AppendXFId() tests XclExpXFIndex::mnXFId, set it too
1298 aXFId.mnXFId = aXFId.mnXFIndex = rXFIndexes[ i ];
1299 AppendXFId( aXFId );
1302 // remove leading and trailing unused XF indexes
1303 if( !maXFIds.empty() && (maXFIds.front().mnXFIndex == EXC_XF_NOTFOUND) )
1305 SetXclCol( GetXclCol() + maXFIds.front().mnCount );
1306 maXFIds.erase(maXFIds.begin(), maXFIds.begin() + 1);
1308 if( !maXFIds.empty() && (maXFIds.back().mnXFIndex == EXC_XF_NOTFOUND) )
1309 maXFIds.pop_back();
1311 // The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND.
1314 sal_uInt16 XclExpMultiCellBase::GetStartColAllDefaultCell() const
1316 sal_uInt16 col = GetXclCol();
1317 sal_uInt16 nMaxNonDefCol = col;
1318 for( const auto& rXFId : maXFIds )
1320 col += rXFId.mnCount;
1321 if (rXFId.mnXFIndex != EXC_XF_DEFAULTCELL)
1322 nMaxNonDefCol = col;
1324 return nMaxNonDefCol;
1327 XclExpBlankCell::XclExpBlankCell( const XclAddress& rXclPos, const XclExpMultiXFId& rXFId ) :
1328 XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1330 OSL_ENSURE( rXFId.mnCount > 0, "XclExpBlankCell::XclExpBlankCell - invalid count" );
1331 AppendXFId( rXFId );
1334 XclExpBlankCell::XclExpBlankCell(
1335 const XclExpRoot& rRoot, const XclAddress& rXclPos, sal_uInt16 nLastXclCol,
1336 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId ) :
1337 XclExpMultiCellBase( EXC_ID3_BLANK, EXC_ID_MULBLANK, 0, rXclPos )
1339 OSL_ENSURE( rXclPos.mnCol <= nLastXclCol, "XclExpBlankCell::XclExpBlankCell - invalid column range" );
1340 // #i46627# use default script type instead of ApiScriptType::WEAK
1341 AppendXFId( rRoot, pPattern, rRoot.GetDefApiScript(), nForcedXFId, nLastXclCol - rXclPos.mnCol + 1 );
1344 bool XclExpBlankCell::TryMerge( const XclExpCellBase& rCell )
1346 const XclExpBlankCell* pBlankCell = dynamic_cast< const XclExpBlankCell* >( &rCell );
1347 return pBlankCell && TryMergeXFIds( *pBlankCell );
1350 void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec& rXFIndexes ) const
1352 GetXFIndexes( rXFIndexes );
1355 void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec& rXFIndexes, size_t nStartAllNotFound )
1357 RemoveUnusedXFIndexes( rXFIndexes, nStartAllNotFound );
1360 void XclExpBlankCell::WriteContents( XclExpStream& /*rStrm*/, sal_uInt16 /*nRelCol*/ )
1364 void XclExpBlankCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 /* nRelCol */ )
1366 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1367 rWorksheet->singleElement( XML_c,
1368 XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(),
1369 XML_s, lcl_GetStyleId(rStrm, nXFId) );
1372 XclExpRkCell::XclExpRkCell(
1373 const XclExpRoot& rRoot, const XclAddress& rXclPos,
1374 const ScPatternAttr* pPattern, sal_uInt32 nForcedXFId, sal_Int32 nRkValue ) :
1375 XclExpMultiCellBase( EXC_ID_RK, EXC_ID_MULRK, 4, rXclPos )
1377 // #i41210# always use latin script for number cells - may look wrong for special number formats...
1378 AppendXFId( rRoot, pPattern, ApiScriptType::LATIN, nForcedXFId );
1379 maRkValues.push_back( nRkValue );
1382 bool XclExpRkCell::TryMerge( const XclExpCellBase& rCell )
1384 const XclExpRkCell* pRkCell = dynamic_cast< const XclExpRkCell* >( &rCell );
1385 if( pRkCell && TryMergeXFIds( *pRkCell ) )
1387 maRkValues.insert( maRkValues.end(), pRkCell->maRkValues.begin(), pRkCell->maRkValues.end() );
1388 return true;
1390 return false;
1393 void XclExpRkCell::WriteXmlContents( XclExpXmlStream& rStrm, const XclAddress& rAddress, sal_uInt32 nXFId, sal_uInt16 nRelCol )
1395 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1396 rWorksheet->startElement( XML_c,
1397 XML_r, XclXmlUtils::ToOString(rStrm.GetRoot().GetStringBuf(), rAddress).getStr(),
1398 XML_s, lcl_GetStyleId(rStrm, nXFId),
1399 XML_t, "n"
1400 // OOXTODO: XML_cm, XML_vm, XML_ph
1402 rWorksheet->startElement( XML_v );
1403 rWorksheet->write( XclTools::GetDoubleFromRK( maRkValues[ nRelCol ] ) );
1404 rWorksheet->endElement( XML_v );
1405 rWorksheet->endElement( XML_c );
1408 void XclExpRkCell::WriteContents( XclExpStream& rStrm, sal_uInt16 nRelCol )
1410 OSL_ENSURE( nRelCol < maRkValues.size(), "XclExpRkCell::WriteContents - overflow error" );
1411 rStrm << maRkValues[ nRelCol ];
1414 // Rows and Columns
1416 XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot& rRoot, bool bRows ) :
1417 mpScOLArray( nullptr ),
1418 maLevelInfos( SC_OL_MAXDEPTH ),
1419 mnCurrLevel( 0 ),
1420 mbCurrCollapse( false )
1422 if( const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() ) )
1423 mpScOLArray = &(bRows ? pOutlineTable->GetRowArray() : pOutlineTable->GetColArray());
1425 if( mpScOLArray )
1426 for( size_t nLevel = 0; nLevel < SC_OL_MAXDEPTH; ++nLevel )
1427 if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nLevel, 0 ) )
1428 maLevelInfos[ nLevel ].mnScEndPos = pEntry->GetEnd();
1431 void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos )
1433 if( !mpScOLArray )
1434 return;
1436 // find open level index for passed position
1437 size_t nNewOpenScLevel = 0; // new open level (0-based Calc index)
1438 sal_uInt8 nNewLevel = 0; // new open level (1-based Excel index)
1440 if( mpScOLArray->FindTouchedLevel( nScPos, nScPos, nNewOpenScLevel ) )
1441 nNewLevel = static_cast< sal_uInt8 >( nNewOpenScLevel + 1 );
1442 // else nNewLevel keeps 0 to show that there are no groups
1444 mbCurrCollapse = false;
1445 if( nNewLevel >= mnCurrLevel )
1447 // new level(s) opened, or no level closed - update all level infos
1448 for( size_t nScLevel = 0; nScLevel <= nNewOpenScLevel; ++nScLevel )
1450 /* In each level: check if a new group is started (there may be
1451 neighbored groups without gap - therefore check ALL levels). */
1452 if( maLevelInfos[ nScLevel ].mnScEndPos < nScPos )
1454 if( const ScOutlineEntry* pEntry = mpScOLArray->GetEntryByPos( nScLevel, nScPos ) )
1456 maLevelInfos[ nScLevel ].mnScEndPos = pEntry->GetEnd();
1457 maLevelInfos[ nScLevel ].mbHidden = pEntry->IsHidden();
1462 else
1464 // level(s) closed - check if any of the closed levels are collapsed
1465 // Calc uses 0-based level indexes
1466 sal_uInt16 nOldOpenScLevel = mnCurrLevel - 1;
1467 for( sal_uInt16 nScLevel = nNewOpenScLevel + 1; !mbCurrCollapse && (nScLevel <= nOldOpenScLevel); ++nScLevel )
1468 mbCurrCollapse = maLevelInfos[ nScLevel ].mbHidden;
1471 // cache new opened level
1472 mnCurrLevel = nNewLevel;
1475 XclExpGuts::XclExpGuts( const XclExpRoot& rRoot ) :
1476 XclExpRecord( EXC_ID_GUTS, 8 ),
1477 mnColLevels( 0 ),
1478 mnColWidth( 0 ),
1479 mnRowLevels( 0 ),
1480 mnRowWidth( 0 )
1482 const ScOutlineTable* pOutlineTable = rRoot.GetDoc().GetOutlineTable( rRoot.GetCurrScTab() );
1483 if(!pOutlineTable)
1484 return;
1486 // column outline groups
1487 const ScOutlineArray& rColArray = pOutlineTable->GetColArray();
1488 mnColLevels = ulimit_cast< sal_uInt16 >( rColArray.GetDepth(), EXC_OUTLINE_MAX );
1489 if( mnColLevels )
1491 ++mnColLevels;
1492 mnColWidth = 12 * mnColLevels + 5;
1495 // row outline groups
1496 const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
1497 mnRowLevels = ulimit_cast< sal_uInt16 >( rRowArray.GetDepth(), EXC_OUTLINE_MAX );
1498 if( mnRowLevels )
1500 ++mnRowLevels;
1501 mnRowWidth = 12 * mnRowLevels + 5;
1505 void XclExpGuts::WriteBody( XclExpStream& rStrm )
1507 rStrm << mnRowWidth << mnColWidth << mnRowLevels << mnColLevels;
1510 XclExpDimensions::XclExpDimensions( const XclExpRoot& rRoot ) :
1511 mrRoot(rRoot),
1512 mnFirstUsedXclRow( 0 ),
1513 mnFirstFreeXclRow( 0 ),
1514 mnFirstUsedXclCol( 0 ),
1515 mnFirstFreeXclCol( 0 )
1517 switch( rRoot.GetBiff() )
1519 case EXC_BIFF2: SetRecHeader( EXC_ID2_DIMENSIONS, 8 ); break;
1520 case EXC_BIFF3:
1521 case EXC_BIFF4:
1522 case EXC_BIFF5: SetRecHeader( EXC_ID3_DIMENSIONS, 10 ); break;
1523 case EXC_BIFF8: SetRecHeader( EXC_ID3_DIMENSIONS, 14 ); break;
1524 default: DBG_ERROR_BIFF();
1528 void XclExpDimensions::SetDimensions(
1529 sal_uInt16 nFirstUsedXclCol, sal_uInt32 nFirstUsedXclRow,
1530 sal_uInt16 nFirstFreeXclCol, sal_uInt32 nFirstFreeXclRow )
1532 mnFirstUsedXclRow = nFirstUsedXclRow;
1533 mnFirstFreeXclRow = nFirstFreeXclRow;
1534 mnFirstUsedXclCol = nFirstUsedXclCol;
1535 mnFirstFreeXclCol = nFirstFreeXclCol;
1538 void XclExpDimensions::SaveXml( XclExpXmlStream& rStrm )
1540 ScRange aRange;
1541 aRange.aStart.SetRow( static_cast<SCROW>(mnFirstUsedXclRow) );
1542 aRange.aStart.SetCol( static_cast<SCCOL>(mnFirstUsedXclCol) );
1544 if( mnFirstFreeXclRow != mnFirstUsedXclRow && mnFirstFreeXclCol != mnFirstUsedXclCol )
1546 aRange.aEnd.SetRow( static_cast<SCROW>(mnFirstFreeXclRow-1) );
1547 aRange.aEnd.SetCol( static_cast<SCCOL>(mnFirstFreeXclCol-1) );
1550 aRange.PutInOrder();
1551 rStrm.GetCurrentStream()->singleElement( XML_dimension,
1552 // To be compatible with MS Office 2007,
1553 // we need full address notation format
1554 // e.g. "A1:AMJ177" and not partial like: "1:177".
1555 XML_ref, XclXmlUtils::ToOString(mrRoot.GetDoc(), aRange, true) );
1558 void XclExpDimensions::WriteBody( XclExpStream& rStrm )
1560 XclBiff eBiff = rStrm.GetRoot().GetBiff();
1561 if( eBiff == EXC_BIFF8 )
1562 rStrm << mnFirstUsedXclRow << mnFirstFreeXclRow;
1563 else
1564 rStrm << static_cast< sal_uInt16 >( mnFirstUsedXclRow ) << static_cast< sal_uInt16 >( mnFirstFreeXclRow );
1565 rStrm << mnFirstUsedXclCol << mnFirstFreeXclCol;
1566 if( eBiff >= EXC_BIFF3 )
1567 rStrm << sal_uInt16( 0 );
1570 namespace {
1572 double lclGetCChCorrection(const XclExpRoot& rRoot)
1574 // Convert the correction from 1/256ths of a character size to count of chars
1575 // TODO: make to fit ECMA-376-1:2016 18.3.1.81 sheetFormatPr (Sheet Format Properties):
1576 // 5 pixels are added to the base width: 2 for margin padding on each side, plus 1 for gridline
1577 // So this should depend on rRoot.GetCharWidth(), not on font height
1579 tools::Long nFontHt = rRoot.GetFontBuffer().GetAppFontData().mnHeight;
1580 return XclTools::GetXclDefColWidthCorrection(nFontHt) / 256.0;
1583 } // namespace
1585 XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot& rRoot ) :
1586 XclExpDoubleRecord(EXC_ID_DEFCOLWIDTH, EXC_DEFCOLWIDTH_DEF + lclGetCChCorrection(rRoot)),
1587 XclExpRoot( rRoot )
1591 bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth ) const
1593 // This formula is taking number of characters with GetValue()
1594 // and it is translating it into default column width.
1595 // https://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.column.aspx
1596 double defaultColumnWidth = 256.0 * GetValue();
1598 // exactly matched, if difference is less than 1/16 of a character to the left or to the right
1599 return std::abs(defaultColumnWidth - nXclColWidth) < 256.0 * 1.0 / 16.0;
1602 void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth, bool bXLS )
1604 double fCCh = nXclColWidth / 256.0;
1605 if (bXLS)
1607 const double fCorrection = lclGetCChCorrection(GetRoot());
1608 const double fCorrectedCCh = fCCh - fCorrection;
1609 // Now get the value which would be stored in XLS DefColWidth struct
1610 double fCChRound = std::round(fCorrectedCCh);
1611 // If default width was set to a value that is not representable as integral CCh between 0
1612 // and 255, then just ignore that value, and use an arbitrary default. That way, the stored
1613 // default might not represent the most used column width (or any used column width), but
1614 // that's OK, and it just means that those columns will explicitly store their width in
1615 // 1/256ths of char, and have fUserSet in their ColInfo records.
1616 if (fCChRound < 0.0 || fCChRound > 255.0 || std::abs(fCChRound - fCorrectedCCh) > 1.0 / 512)
1617 fCChRound = 8.0;
1618 fCCh = fCChRound + fCorrection;
1621 SetValue(fCCh);
1624 void XclExpDefcolwidth::Save(XclExpStream& rStrm)
1626 double fCorrectedCCh = GetValue() - lclGetCChCorrection(GetRoot());
1627 // Convert double to sal_uInt16
1628 XclExpUInt16Record aUInt16Rec(GetRecId(),
1629 static_cast<sal_uInt16>(std::round(fCorrectedCCh)));
1630 aUInt16Rec.Save(rStrm);
1633 XclExpColinfo::XclExpColinfo( const XclExpRoot& rRoot,
1634 SCCOL nScCol, SCROW nLastScRow, XclExpColOutlineBuffer& rOutlineBfr ) :
1635 XclExpRecord( EXC_ID_COLINFO, 12 ),
1636 XclExpRoot( rRoot ),
1637 mbCustomWidth( false ),
1638 mnWidth( 0 ),
1639 mnScWidth( 0 ),
1640 mnFlags( 0 ),
1641 mnOutlineLevel( 0 ),
1642 mnFirstXclCol( static_cast< sal_uInt16 >( nScCol ) ),
1643 mnLastXclCol( static_cast< sal_uInt16 >( nScCol ) )
1645 ScDocument& rDoc = GetDoc();
1646 SCTAB nScTab = GetCurrScTab();
1648 // column default format
1649 maXFId.mnXFId = GetXFBuffer().Insert(
1650 rDoc.GetMostUsedPattern( nScCol, 0, nLastScRow, nScTab ), GetDefApiScript() );
1652 // column width. If column is hidden then we should return real value (not zero)
1653 sal_uInt16 nScWidth = rDoc.GetColWidth( nScCol, nScTab, false );
1654 mnWidth = XclTools::GetXclColumnWidth( nScWidth, GetCharWidth() );
1655 mnScWidth = convertTwipToMm100(nScWidth);
1657 // column flags
1658 ::set_flag( mnFlags, EXC_COLINFO_HIDDEN, rDoc.ColHidden(nScCol, nScTab) );
1660 // outline data
1661 rOutlineBfr.Update( nScCol );
1662 ::set_flag( mnFlags, EXC_COLINFO_COLLAPSED, rOutlineBfr.IsCollapsed() );
1663 ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 8, 3 );
1664 mnOutlineLevel = rOutlineBfr.GetLevel();
1667 void XclExpColinfo::ConvertXFIndexes()
1669 maXFId.ConvertXFIndex( GetRoot() );
1672 bool XclExpColinfo::IsDefault( const XclExpDefcolwidth& rDefColWidth )
1674 mbCustomWidth = !rDefColWidth.IsDefWidth(mnWidth);
1675 return (maXFId.mnXFIndex == EXC_XF_DEFAULTCELL) &&
1676 (mnFlags == 0) &&
1677 (mnOutlineLevel == 0) &&
1678 !mbCustomWidth;
1681 bool XclExpColinfo::TryMerge( const XclExpColinfo& rColInfo )
1683 if( (maXFId.mnXFIndex == rColInfo.maXFId.mnXFIndex) &&
1684 (mnWidth == rColInfo.mnWidth) &&
1685 (mnFlags == rColInfo.mnFlags) &&
1686 (mnOutlineLevel == rColInfo.mnOutlineLevel) &&
1687 (mnLastXclCol + 1 == rColInfo.mnFirstXclCol) )
1689 mnLastXclCol = rColInfo.mnLastXclCol;
1690 return true;
1692 return false;
1695 void XclExpColinfo::WriteBody( XclExpStream& rStrm )
1697 // if last column is equal to last possible column, Excel adds one more
1698 sal_uInt16 nLastXclCol = mnLastXclCol;
1699 if( nLastXclCol == static_cast< sal_uInt16 >( rStrm.GetRoot().GetMaxPos().Col() ) )
1700 ++nLastXclCol;
1702 rStrm << mnFirstXclCol
1703 << nLastXclCol
1704 << mnWidth
1705 << maXFId.mnXFIndex
1706 << mnFlags
1707 << sal_uInt16( 0 );
1710 void XclExpColinfo::SaveXml( XclExpXmlStream& rStrm )
1712 const double nExcelColumnWidth = mnScWidth / convertTwipToMm100<double>(GetCharWidth());
1714 // tdf#101363 In MS specification the output value is set with double precision after delimiter:
1715 // =Truncate(({width in pixels} - 5)/{Maximum Digit Width} * 100 + 0.5)/100
1716 // Explanation of magic numbers:
1717 // 5 number - are 4 pixels of margin padding (two on each side), plus 1 pixel padding for the gridlines.
1718 // It is unknown if it should be applied during LibreOffice export
1719 // 100 number - used to limit precision to 0.01 with formula =Truncate( {value}*100+0.5 ) / 100
1720 // 0.5 number (0.005 to output value) - used to increase value before truncating,
1721 // to avoid situation when 2.997 will be truncated to 2.99 and not to 3.00
1722 const double nTruncatedExcelColumnWidth = std::trunc( nExcelColumnWidth * 100.0 + 0.5 ) / 100.0;
1723 rStrm.GetCurrentStream()->singleElement( XML_col,
1724 // OOXTODO: XML_bestFit,
1725 XML_collapsed, ToPsz( ::get_flag( mnFlags, EXC_COLINFO_COLLAPSED ) ),
1726 XML_customWidth, ToPsz( mbCustomWidth ),
1727 XML_hidden, ToPsz( ::get_flag( mnFlags, EXC_COLINFO_HIDDEN ) ),
1728 XML_outlineLevel, OString::number(mnOutlineLevel),
1729 XML_max, OString::number(mnLastXclCol + 1),
1730 XML_min, OString::number(mnFirstXclCol + 1),
1731 // OOXTODO: XML_phonetic,
1732 XML_style, lcl_GetStyleId(rStrm, maXFId.mnXFIndex),
1733 XML_width, OString::number(nTruncatedExcelColumnWidth) );
1736 XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot& rRoot ) :
1737 XclExpRoot( rRoot ),
1738 maDefcolwidth( rRoot ),
1739 maOutlineBfr( rRoot ),
1740 mnHighestOutlineLevel( 0 )
1744 void XclExpColinfoBuffer::Initialize( SCROW nLastScRow )
1747 for( sal_uInt16 nScCol = 0, nLastScCol = GetMaxPos().Col(); nScCol <= nLastScCol; ++nScCol )
1749 maColInfos.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol, nLastScRow, maOutlineBfr ) );
1750 if( maOutlineBfr.GetLevel() > mnHighestOutlineLevel )
1752 mnHighestOutlineLevel = maOutlineBfr.GetLevel();
1757 void XclExpColinfoBuffer::Finalize( ScfUInt16Vec& rXFIndexes, bool bXLS )
1759 rXFIndexes.clear();
1760 rXFIndexes.reserve( maColInfos.GetSize() );
1762 if( !maColInfos.IsEmpty())
1764 XclExpColinfo* xPrevRec = maColInfos.GetRecord( 0 );
1765 xPrevRec->ConvertXFIndexes();
1766 for( size_t nPos = 1; nPos < maColInfos.GetSize(); ++nPos )
1768 XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
1769 xRec->ConvertXFIndexes();
1771 // try to merge with previous record
1772 if( xPrevRec->TryMerge( *xRec ) )
1773 maColInfos.InvalidateRecord( nPos );
1774 else
1775 xPrevRec = xRec;
1777 maColInfos.RemoveInvalidatedRecords();
1780 // put XF indexes into passed vector, collect use count of all different widths
1781 std::unordered_map< sal_uInt16, sal_uInt16 > aWidthMap;
1782 sal_uInt16 nMaxColCount = 0;
1783 sal_uInt16 nMaxUsedWidth = 0;
1784 for( size_t nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
1786 const XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
1787 sal_uInt16 nColCount = xRec->GetColCount();
1789 // add XF index to passed vector
1790 rXFIndexes.resize( rXFIndexes.size() + nColCount, xRec->GetXFIndex() );
1792 // collect use count of column width
1793 sal_uInt16 nWidth = xRec->GetColWidth();
1794 sal_uInt16& rnMapCount = aWidthMap[ nWidth ];
1795 rnMapCount = rnMapCount + nColCount;
1796 if( rnMapCount > nMaxColCount )
1798 nMaxColCount = rnMapCount;
1799 nMaxUsedWidth = nWidth;
1802 maDefcolwidth.SetDefWidth( nMaxUsedWidth, bXLS );
1804 // remove all default COLINFO records
1805 for( size_t nPos = 0; nPos < maColInfos.GetSize(); ++nPos )
1807 XclExpColinfo* xRec = maColInfos.GetRecord( nPos );
1808 if( xRec->IsDefault( maDefcolwidth ) )
1809 maColInfos.InvalidateRecord( nPos );
1811 maColInfos.RemoveInvalidatedRecords();
1814 void XclExpColinfoBuffer::Save( XclExpStream& rStrm )
1816 // DEFCOLWIDTH
1817 maDefcolwidth.Save( rStrm );
1818 // COLINFO records
1819 maColInfos.Save( rStrm );
1822 void XclExpColinfoBuffer::SaveXml( XclExpXmlStream& rStrm )
1824 if( maColInfos.IsEmpty() )
1825 return;
1827 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
1828 rWorksheet->startElement(XML_cols);
1829 maColInfos.SaveXml( rStrm );
1830 rWorksheet->endElement( XML_cols );
1833 XclExpDefaultRowData::XclExpDefaultRowData() :
1834 mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1835 mnHeight( EXC_DEFROW_DEFAULTHEIGHT )
1839 XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow& rRow ) :
1840 mnFlags( EXC_DEFROW_DEFAULTFLAGS ),
1841 mnHeight( rRow.GetHeight() )
1843 ::set_flag( mnFlags, EXC_DEFROW_HIDDEN, rRow.IsHidden() );
1844 ::set_flag( mnFlags, EXC_DEFROW_UNSYNCED, rRow.IsUnsynced() );
1847 static bool operator<( const XclExpDefaultRowData& rLeft, const XclExpDefaultRowData& rRight )
1849 return (rLeft.mnHeight < rRight.mnHeight) ||
1850 ((rLeft.mnHeight == rRight.mnHeight) && (rLeft.mnFlags < rRight.mnFlags));
1853 XclExpDefrowheight::XclExpDefrowheight() :
1854 XclExpRecord( EXC_ID3_DEFROWHEIGHT, 4 )
1858 void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData& rDefData )
1860 maDefData = rDefData;
1863 void XclExpDefrowheight::WriteBody( XclExpStream& rStrm )
1865 OSL_ENSURE_BIFF( rStrm.GetRoot().GetBiff() >= EXC_BIFF3 );
1866 rStrm << maDefData.mnFlags << maDefData.mnHeight;
1869 XclExpRow::XclExpRow( const XclExpRoot& rRoot, sal_uInt32 nXclRow,
1870 XclExpRowOutlineBuffer& rOutlineBfr, bool bAlwaysEmpty, bool bHidden, sal_uInt16 nHeight ) :
1871 XclExpRecord( EXC_ID3_ROW, 16 ),
1872 XclExpRoot( rRoot ),
1873 mnXclRow( nXclRow ),
1874 mnHeight( nHeight ),
1875 mnFlags( EXC_ROW_DEFAULTFLAGS ),
1876 mnXFIndex( EXC_XF_DEFAULTCELL ),
1877 mnOutlineLevel( 0 ),
1878 mnXclRowRpt( 1 ),
1879 mnCurrentRow( nXclRow ),
1880 mbAlwaysEmpty( bAlwaysEmpty ),
1881 mbEnabled( true )
1883 SCTAB nScTab = GetCurrScTab();
1884 SCROW nScRow = static_cast< SCROW >( mnXclRow );
1886 // *** Row flags *** ------------------------------------------------------
1888 CRFlags nRowFlags = GetDoc().GetRowFlags( nScRow, nScTab );
1889 bool bUserHeight( nRowFlags & CRFlags::ManualSize );
1890 ::set_flag( mnFlags, EXC_ROW_UNSYNCED, bUserHeight );
1891 ::set_flag( mnFlags, EXC_ROW_HIDDEN, bHidden );
1893 // *** Outline data *** ---------------------------------------------------
1895 rOutlineBfr.Update( nScRow );
1896 ::set_flag( mnFlags, EXC_ROW_COLLAPSED, rOutlineBfr.IsCollapsed() );
1897 ::insert_value( mnFlags, rOutlineBfr.GetLevel(), 0, 3 );
1898 mnOutlineLevel = rOutlineBfr.GetLevel();
1900 // *** Progress bar *** ---------------------------------------------------
1902 XclExpProgressBar& rProgress = GetProgressBar();
1903 rProgress.IncRowRecordCount();
1904 rProgress.Progress();
1907 static size_t findFirstAllSameUntilEnd( const ScfUInt16Vec& rIndexes, sal_uInt16 value,
1908 size_t searchStart = std::numeric_limits<size_t>::max())
1910 for( size_t i = std::min(rIndexes.size(), searchStart); i >= 1; --i )
1912 if( rIndexes[ i - 1 ] != value )
1913 return i;
1915 return 0;
1918 void XclExpRow::AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase )
1920 OSL_ENSURE( !mbAlwaysEmpty, "XclExpRow::AppendCell - row is marked to be always empty" );
1921 // try to merge with last existing cell
1922 InsertCell( xCell, maCellList.GetSize(), bIsMergedBase );
1925 void XclExpRow::Finalize( const ScfUInt16Vec& rColXFIndexes, ScfUInt16Vec& aXFIndexes, size_t nStartColAllDefault, bool bProgress )
1927 size_t nPos, nSize;
1929 // *** Convert XF identifiers *** -----------------------------------------
1931 // additionally collect the blank XF indexes
1932 size_t nColCount = GetMaxPos().Col() + 1;
1933 OSL_ENSURE( rColXFIndexes.size() == nColCount, "XclExpRow::Finalize - wrong column XF index count" );
1935 // The vector should be preset to all items being EXC_XF_NOTFOUND, to avoid repeated allocations
1936 // and clearing.
1937 assert( aXFIndexes.size() == nColCount );
1938 assert( aXFIndexes.front() == EXC_XF_NOTFOUND );
1939 assert( aXFIndexes.back() == EXC_XF_NOTFOUND );
1940 for( nPos = 0, nSize = maCellList.GetSize(); nPos < nSize; ++nPos )
1942 XclExpCellBase* pCell = maCellList.GetRecord( nPos );
1943 pCell->ConvertXFIndexes( GetRoot() );
1944 pCell->GetBlankXFIndexes( aXFIndexes );
1947 // *** Fill gaps with BLANK/MULBLANK cell records *** ---------------------
1949 /* This is needed because nonexistent cells in Calc are not formatted at all,
1950 but in Excel they would have the column default format. Blank cells that
1951 are equal to the respective column default are removed later in this function. */
1952 if( !mbAlwaysEmpty )
1954 // XF identifier representing default cell XF
1955 XclExpMultiXFId aXFId( XclExpXFBuffer::GetDefCellXFId() );
1956 aXFId.ConvertXFIndex( GetRoot() );
1958 nPos = 0;
1959 while( nPos <= maCellList.GetSize() ) // don't cache list size, may change in the loop
1961 // get column index that follows previous cell
1962 sal_uInt16 nFirstFreeXclCol = (nPos > 0) ? (maCellList.GetRecord( nPos - 1 )->GetLastXclCol() + 1) : 0;
1963 // get own column index
1964 sal_uInt16 nNextUsedXclCol = (nPos < maCellList.GetSize()) ? maCellList.GetRecord( nPos )->GetXclCol() : (GetMaxPos().Col() + 1);
1966 // is there a gap?
1967 if( nFirstFreeXclCol < nNextUsedXclCol )
1969 aXFId.mnCount = nNextUsedXclCol - nFirstFreeXclCol;
1970 XclExpCellRef xNewCell = new XclExpBlankCell( XclAddress( nFirstFreeXclCol, mnXclRow ), aXFId );
1971 // insert the cell, InsertCell() may merge it with existing BLANK records
1972 InsertCell( xNewCell, nPos, false );
1973 // insert default XF indexes into aXFIndexes
1974 for( size_t i = nFirstFreeXclCol; i < nNextUsedXclCol; ++i )
1975 aXFIndexes[ i ] = aXFId.mnXFIndex;
1976 // don't step forward with nPos, InsertCell() may remove records
1978 else
1979 ++nPos;
1983 // *** Find default row format *** ----------------------------------------
1985 // Often there will be many EXC_XF_DEFAULTCELL at the end, optimize by ignoring them.
1986 size_t nStartSearchAllDefault = aXFIndexes.size();
1987 if( !maCellList.IsEmpty() && dynamic_cast< const XclExpBlankCell* >( maCellList.GetLastRecord()))
1989 const XclExpBlankCell* pLastBlank = static_cast< const XclExpBlankCell* >( maCellList.GetLastRecord());
1990 assert(pLastBlank->GetLastXclCol() == aXFIndexes.size() - 1);
1991 nStartSearchAllDefault = pLastBlank->GetStartColAllDefaultCell();
1993 size_t nStartAllDefault = findFirstAllSameUntilEnd( aXFIndexes, EXC_XF_DEFAULTCELL, nStartSearchAllDefault);
1995 // find most used XF index in the row
1996 sal_uInt16 nRowXFIndex = EXC_XF_DEFAULTCELL;
1997 const size_t nHalfIndexes = aXFIndexes.size() / 2;
1998 if( nStartAllDefault > nHalfIndexes ) // Otherwise most are EXC_XF_DEFAULTCELL.
2000 // Very likely the most common one is going to be the last one.
2001 nRowXFIndex = aXFIndexes.back();
2002 size_t nStartLastSame = findFirstAllSameUntilEnd( aXFIndexes, nRowXFIndex );
2003 if( nStartLastSame > nHalfIndexes ) // No, find out the most used one by counting.
2005 std::unordered_map< sal_uInt16, size_t > aIndexMap;
2006 size_t nMaxXFCount = 0;
2007 for( const auto& rXFIndex : aXFIndexes )
2009 if( rXFIndex != EXC_XF_NOTFOUND )
2011 size_t& rnCount = aIndexMap[ rXFIndex ];
2012 ++rnCount;
2013 if( rnCount > nMaxXFCount )
2015 nRowXFIndex = rXFIndex;
2016 nMaxXFCount = rnCount;
2017 if (nMaxXFCount > nHalfIndexes)
2019 // No other XF index can have a greater usage count, we
2020 // don't need to loop through the remaining cells.
2021 // Specifically for the tail of unused default
2022 // cells/columns this makes a difference.
2023 break; // for
2031 // decide whether to use the row default XF index or column default XF indexes
2032 bool bUseColDefXFs = nRowXFIndex == EXC_XF_DEFAULTCELL;
2033 if( !bUseColDefXFs )
2035 // count needed XF indexes for blank cells with and without row default XF index
2036 size_t nXFCountWithRowDefXF = 0;
2037 size_t nXFCountWithoutRowDefXF = 0;
2038 ScfUInt16Vec::const_iterator aColIt = rColXFIndexes.begin();
2039 for( const auto& rXFIndex : aXFIndexes )
2041 sal_uInt16 nXFIndex = rXFIndex;
2042 if( nXFIndex != EXC_XF_NOTFOUND )
2044 if( nXFIndex != nRowXFIndex )
2045 ++nXFCountWithRowDefXF; // with row default XF index
2046 if( nXFIndex != *aColIt )
2047 ++nXFCountWithoutRowDefXF; // without row default XF index
2049 ++aColIt;
2052 // use column XF indexes if this would cause less or equal number of BLANK records
2053 bUseColDefXFs = nXFCountWithoutRowDefXF <= nXFCountWithRowDefXF;
2056 // *** Remove unused BLANK cell records *** -------------------------------
2058 size_t maxStartAllNotFound;
2059 if( bUseColDefXFs )
2061 size_t maxStartAllDefault = std::max( nStartAllDefault, nStartColAllDefault );
2062 // use column default XF indexes
2063 // #i194#: remove cell XF indexes equal to column default XF indexes
2064 for( size_t i = 0; i < maxStartAllDefault; ++i )
2066 if( aXFIndexes[ i ] == rColXFIndexes[ i ] )
2067 aXFIndexes[ i ] = EXC_XF_NOTFOUND;
2069 // They can differ only up to maxNonDefault, in the rest they are the same.
2070 for( size_t i = maxStartAllDefault; i < aXFIndexes.size(); ++i )
2071 aXFIndexes[ i ] = EXC_XF_NOTFOUND;
2072 maxStartAllNotFound = maxStartAllDefault;
2074 else
2076 // use row default XF index
2077 mnXFIndex = nRowXFIndex;
2078 ::set_flag( mnFlags, EXC_ROW_USEDEFXF );
2079 // #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index
2080 for( auto& rXFIndex : aXFIndexes )
2081 if( rXFIndex == nRowXFIndex )
2082 rXFIndex = EXC_XF_NOTFOUND;
2083 maxStartAllNotFound = aXFIndexes.size();
2086 // remove unused parts of BLANK/MULBLANK cell records
2087 size_t nStartAllNotFound = findFirstAllSameUntilEnd( aXFIndexes, EXC_XF_NOTFOUND, maxStartAllNotFound );
2088 nPos = 0;
2089 while( nPos < maCellList.GetSize() ) // do not cache list size, may change in the loop
2091 XclExpCellBase* xCell = maCellList.GetRecord( nPos );
2092 xCell->RemoveUnusedBlankCells( aXFIndexes, nStartAllNotFound );
2093 if( xCell->IsEmpty() )
2094 maCellList.RemoveRecord( nPos );
2095 else
2096 ++nPos;
2098 // Ensure it's all EXC_XF_NOTFOUND again for next reuse.
2099 for( size_t i = 0; i < nStartAllNotFound; ++i )
2100 aXFIndexes[ i ] = EXC_XF_NOTFOUND;
2102 // progress bar includes disabled rows; only update it in the lead thread.
2103 if (bProgress)
2104 GetProgressBar().Progress();
2106 sal_uInt16 XclExpRow::GetFirstUsedXclCol() const
2108 return maCellList.IsEmpty() ? 0 : maCellList.GetFirstRecord()->GetXclCol();
2111 sal_uInt16 XclExpRow::GetFirstFreeXclCol() const
2113 return maCellList.IsEmpty() ? 0 : (maCellList.GetLastRecord()->GetLastXclCol() + 1);
2116 bool XclExpRow::IsDefaultable() const
2118 const sal_uInt16 nFlagsAlwaysMarkedAsDefault = EXC_ROW_DEFAULTFLAGS | EXC_ROW_HIDDEN | EXC_ROW_UNSYNCED;
2119 return !::get_flag( mnFlags, static_cast< sal_uInt16 >( ~nFlagsAlwaysMarkedAsDefault ) ) &&
2120 IsEmpty();
2123 void XclExpRow::DisableIfDefault( const XclExpDefaultRowData& rDefRowData )
2125 mbEnabled = !IsDefaultable() ||
2126 (mnHeight != rDefRowData.mnHeight) ||
2127 (IsHidden() != rDefRowData.IsHidden()) ||
2128 (IsUnsynced() != rDefRowData.IsUnsynced());
2131 void XclExpRow::WriteCellList( XclExpStream& rStrm )
2133 OSL_ENSURE( mbEnabled || maCellList.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" );
2134 maCellList.Save( rStrm );
2137 void XclExpRow::Save( XclExpStream& rStrm )
2139 if( mbEnabled )
2141 mnCurrentRow = mnXclRow;
2142 for ( sal_uInt32 i = 0; i < mnXclRowRpt; ++i, ++mnCurrentRow )
2143 XclExpRecord::Save( rStrm );
2147 void XclExpRow::InsertCell( XclExpCellRef xCell, size_t nPos, bool bIsMergedBase )
2149 OSL_ENSURE( xCell, "XclExpRow::InsertCell - missing cell" );
2151 /* If we have a multi-line text in a merged cell, and the resulting
2152 row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED
2153 flag to be true to ensure Excel works correctly. */
2154 if( bIsMergedBase && xCell->IsMultiLineText() )
2155 ::set_flag( mnFlags, EXC_ROW_UNSYNCED );
2157 // try to merge with previous cell, insert the new cell if not successful
2158 XclExpCellBase* xPrevCell = maCellList.GetRecord( nPos - 1 );
2159 if( xPrevCell && xPrevCell->TryMerge( *xCell ) )
2160 xCell = xPrevCell;
2161 else
2162 maCellList.InsertRecord( xCell, nPos++ );
2163 // nPos points now to following cell
2165 // try to merge with following cell, remove it if successful
2166 XclExpCellRef xNextCell = maCellList.GetRecord( nPos );
2167 if( xNextCell && xCell->TryMerge( *xNextCell ) )
2168 maCellList.RemoveRecord( nPos );
2171 void XclExpRow::WriteBody( XclExpStream& rStrm )
2173 rStrm << static_cast< sal_uInt16 >(mnCurrentRow)
2174 << GetFirstUsedXclCol()
2175 << GetFirstFreeXclCol()
2176 << mnHeight
2177 << sal_uInt32( 0 )
2178 << mnFlags
2179 << mnXFIndex;
2182 void XclExpRow::SaveXml( XclExpXmlStream& rStrm )
2184 if( !mbEnabled )
2185 return;
2186 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2187 bool haveFormat = ::get_flag( mnFlags, EXC_ROW_USEDEFXF );
2188 mnCurrentRow = mnXclRow + 1;
2189 for ( sal_uInt32 i=0; i<mnXclRowRpt; ++i )
2191 rWorksheet->startElement( XML_row,
2192 XML_r, OString::number(mnCurrentRow++),
2193 // OOXTODO: XML_spans, optional
2194 XML_s, haveFormat ? lcl_GetStyleId( rStrm, mnXFIndex ).getStr() : nullptr,
2195 XML_customFormat, ToPsz( haveFormat ),
2196 XML_ht, OString::number(static_cast<double>(mnHeight) / 20.0),
2197 XML_hidden, ToPsz( ::get_flag( mnFlags, EXC_ROW_HIDDEN ) ),
2198 XML_customHeight, ToPsz( ::get_flag( mnFlags, EXC_ROW_UNSYNCED ) ),
2199 XML_outlineLevel, OString::number(mnOutlineLevel),
2200 XML_collapsed, ToPsz( ::get_flag( mnFlags, EXC_ROW_COLLAPSED ) )
2201 // OOXTODO: XML_thickTop, bool
2202 // OOXTODO: XML_thickBot, bool
2203 // OOXTODO: XML_ph, bool
2205 // OOXTODO: XML_extLst
2206 maCellList.SaveXml( rStrm );
2207 rWorksheet->endElement( XML_row );
2211 XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot& rRoot ) :
2212 XclExpRoot( rRoot ),
2213 maOutlineBfr( rRoot ),
2214 maDimensions( rRoot ),
2215 mnHighestOutlineLevel( 0 )
2219 void XclExpRowBuffer::AppendCell( XclExpCellRef const & xCell, bool bIsMergedBase )
2221 OSL_ENSURE( xCell, "XclExpRowBuffer::AppendCell - missing cell" );
2222 GetOrCreateRow( xCell->GetXclRow(), false ).AppendCell( xCell, bIsMergedBase );
2225 void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow )
2227 if( nFirstFreeScRow > 0 )
2228 GetOrCreateRow( ::std::max ( nFirstFreeScRow - 1, GetMaxPos().Row() ), true );
2231 namespace {
2233 class RowFinalizeTask : public comphelper::ThreadTask
2235 bool mbProgress;
2236 const ScfUInt16Vec& mrColXFIndexes;
2237 size_t mnStartColAllDefault;
2238 std::vector< XclExpRow * > maRows;
2239 public:
2240 RowFinalizeTask( const std::shared_ptr<comphelper::ThreadTaskTag> & pTag,
2241 const ScfUInt16Vec& rColXFIndexes,
2242 size_t nStartColAllDefault,
2243 bool bProgress ) :
2244 comphelper::ThreadTask( pTag ),
2245 mbProgress( bProgress ),
2246 mrColXFIndexes( rColXFIndexes ),
2247 mnStartColAllDefault( nStartColAllDefault )
2250 void push_back( XclExpRow *pRow ) { maRows.push_back( pRow ); }
2251 virtual void doWork() override
2253 ScfUInt16Vec aXFIndexes( mrColXFIndexes.size(), EXC_XF_NOTFOUND );
2254 for (XclExpRow* p : maRows)
2255 p->Finalize( mrColXFIndexes, aXFIndexes, mnStartColAllDefault, mbProgress );
2261 void XclExpRowBuffer::Finalize( XclExpDefaultRowData& rDefRowData,
2262 const ScfUInt16Vec& rColXFIndexes,
2263 size_t nStartColAllDefault )
2265 // *** Finalize all rows *** ----------------------------------------------
2267 GetProgressBar().ActivateFinalRowsSegment();
2269 #if 1
2270 // This is staggeringly slow, and each element operates only
2271 // on its own data.
2272 const size_t nRows = maRowMap.size();
2273 const size_t nThreads = nRows < 128 ? 1 : comphelper::ThreadPool::getPreferredConcurrency();
2274 #else
2275 const size_t nThreads = 1; // globally disable multi-threading for now.
2276 #endif
2277 if (nThreads == 1)
2279 ScfUInt16Vec aXFIndexes( rColXFIndexes.size(), EXC_XF_NOTFOUND );
2280 for (auto& rEntry : maRowMap)
2281 rEntry.second->Finalize( rColXFIndexes, aXFIndexes, nStartColAllDefault, true );
2283 else
2285 comphelper::ThreadPool &rPool = comphelper::ThreadPool::getSharedOptimalPool();
2286 std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
2287 std::vector<std::unique_ptr<RowFinalizeTask>> aTasks(nThreads);
2288 for ( size_t i = 0; i < nThreads; i++ )
2289 aTasks[ i ].reset( new RowFinalizeTask( pTag, rColXFIndexes, nStartColAllDefault, i == 0 ) );
2291 size_t nIdx = 0;
2292 for ( const auto& rEntry : maRowMap )
2294 aTasks[ nIdx % nThreads ]->push_back( rEntry.second.get() );
2295 ++nIdx;
2298 for ( size_t i = 1; i < nThreads; i++ )
2299 rPool.pushTask( std::move(aTasks[ i ]) );
2301 // Progress bar updates must be synchronous to avoid deadlock
2302 aTasks[0]->doWork();
2304 rPool.waitUntilDone(pTag);
2307 // *** Default row format *** ---------------------------------------------
2309 std::map< XclExpDefaultRowData, size_t > aDefRowMap;
2311 XclExpDefaultRowData aMaxDefData;
2312 size_t nMaxDefCount = 0;
2313 // only look for default format in existing rows, if there are more than unused
2314 // if the row is hidden, then row xml must be created even if it not contain cells
2315 XclExpRow* pPrev = nullptr;
2316 std::vector< XclExpRow* > aRepeated;
2317 for (const auto& rEntry : maRowMap)
2319 const RowRef& rRow = rEntry.second;
2320 if ( rRow->IsDefaultable() )
2322 XclExpDefaultRowData aDefData( *rRow );
2323 size_t& rnDefCount = aDefRowMap[ aDefData ];
2324 ++rnDefCount;
2325 if( rnDefCount > nMaxDefCount )
2327 nMaxDefCount = rnDefCount;
2328 aMaxDefData = aDefData;
2331 if ( pPrev )
2333 if ( pPrev->IsDefaultable() )
2335 // if the previous row we processed is not
2336 // defaultable then afaict the rows in between are
2337 // not used ( and not repeatable )
2338 sal_uInt32 nRpt = rRow->GetXclRow() - pPrev->GetXclRow();
2339 if ( nRpt > 1 )
2340 aRepeated.push_back( pPrev );
2341 pPrev->SetXclRowRpt( nRpt );
2342 XclExpDefaultRowData aDefData( *pPrev );
2343 size_t& rnDefCount = aDefRowMap[ aDefData ];
2344 rnDefCount += ( pPrev->GetXclRowRpt() - 1 );
2345 if( rnDefCount > nMaxDefCount )
2347 nMaxDefCount = rnDefCount;
2348 aMaxDefData = aDefData;
2352 pPrev = rRow.get();
2354 // return the default row format to caller
2355 rDefRowData = aMaxDefData;
2357 // now disable repeating extra (empty) rows that are equal to the default row
2358 for (auto& rpRow : aRepeated)
2360 if ( rpRow->GetXclRowRpt() > 1
2361 && rpRow->GetHeight() == rDefRowData.mnHeight
2362 && rpRow->IsHidden() == rDefRowData.IsHidden() )
2364 rpRow->SetXclRowRpt( 1 );
2368 // *** Disable unused ROW records, find used area *** ---------------------
2370 sal_uInt16 nFirstUsedXclCol = SAL_MAX_UINT16;
2371 sal_uInt16 nFirstFreeXclCol = 0;
2372 sal_uInt32 nFirstUsedXclRow = SAL_MAX_UINT32;
2373 sal_uInt32 nFirstFreeXclRow = 0;
2375 for (const auto& rEntry : maRowMap)
2377 const RowRef& rRow = rEntry.second;
2378 // disable unused rows
2379 rRow->DisableIfDefault( aMaxDefData );
2381 // find used column range
2382 if( !rRow->IsEmpty() ) // empty rows return (0...0) as used range
2384 nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, rRow->GetFirstUsedXclCol() );
2385 nFirstFreeXclCol = ::std::max( nFirstFreeXclCol, rRow->GetFirstFreeXclCol() );
2388 // find used row range
2389 if( rRow->IsEnabled() )
2391 sal_uInt32 nXclRow = rRow->GetXclRow();
2392 nFirstUsedXclRow = ::std::min< sal_uInt32 >( nFirstUsedXclRow, nXclRow );
2393 nFirstFreeXclRow = ::std::max< sal_uInt32 >( nFirstFreeXclRow, nXclRow + 1 );
2397 // adjust start position, if there are no or only empty/disabled ROW records
2398 nFirstUsedXclCol = ::std::min( nFirstUsedXclCol, nFirstFreeXclCol );
2399 nFirstUsedXclRow = ::std::min( nFirstUsedXclRow, nFirstFreeXclRow );
2401 // initialize the DIMENSIONS record
2402 maDimensions.SetDimensions(
2403 nFirstUsedXclCol, nFirstUsedXclRow, nFirstFreeXclCol, nFirstFreeXclRow );
2406 void XclExpRowBuffer::Save( XclExpStream& rStrm )
2408 // DIMENSIONS record
2409 maDimensions.Save( rStrm );
2411 // save in blocks of 32 rows, each block contains first all ROWs, then all cells
2412 size_t nSize = maRowMap.size();
2413 RowMap::iterator itr = maRowMap.begin(), itrEnd = maRowMap.end();
2414 RowMap::iterator itrBlkStart = maRowMap.begin(), itrBlkEnd = maRowMap.begin();
2415 sal_uInt16 nStartXclRow = (nSize == 0) ? 0 : itr->second->GetXclRow();
2417 for (; itr != itrEnd; ++itr)
2419 // find end of row block
2420 itrBlkEnd = std::find_if_not(itrBlkEnd, itrEnd,
2421 [&nStartXclRow](const RowMap::value_type& rRow) { return rRow.second->GetXclRow() - nStartXclRow < EXC_ROW_ROWBLOCKSIZE; });
2423 // write the ROW records
2424 std::for_each(itrBlkStart, itrBlkEnd, [&rStrm](const RowMap::value_type& rRow) { rRow.second->Save( rStrm ); });
2426 // write the cell records
2427 std::for_each(itrBlkStart, itrBlkEnd, [&rStrm](const RowMap::value_type& rRow) { rRow.second->WriteCellList( rStrm ); });
2429 itrBlkStart = (itrBlkEnd == itrEnd) ? itrBlkEnd : itrBlkEnd++;
2430 nStartXclRow += EXC_ROW_ROWBLOCKSIZE;
2434 void XclExpRowBuffer::SaveXml( XclExpXmlStream& rStrm )
2436 if (std::none_of(maRowMap.begin(), maRowMap.end(), [](const RowMap::value_type& rRow) { return rRow.second->IsEnabled(); }))
2438 rStrm.GetCurrentStream()->singleElement(XML_sheetData);
2439 return;
2442 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2443 rWorksheet->startElement(XML_sheetData);
2444 for (const auto& rEntry : maRowMap)
2445 rEntry.second->SaveXml(rStrm);
2446 rWorksheet->endElement( XML_sheetData );
2449 XclExpRow& XclExpRowBuffer::GetOrCreateRow( sal_uInt32 nXclRow, bool bRowAlwaysEmpty )
2451 // This is called rather often, so optimize for the most common case of saving row by row
2452 // (so the requested row is often the last one in the map or belongs after the last one).
2453 RowMap::iterator itr;
2454 if(maRowMap.empty())
2455 itr = maRowMap.end();
2456 else
2458 RowMap::reverse_iterator last = maRowMap.rbegin();
2459 if( last->first == nXclRow )
2460 return *last->second;
2461 if( nXclRow > last->first )
2462 itr = maRowMap.end();
2463 else
2464 itr = maRowMap.lower_bound( nXclRow );
2466 const bool bFound = itr != maRowMap.end();
2467 // bFoundHigher: nXclRow was identical to the previous entry, so not explicitly created earlier
2468 // coverity[deref_iterator : FALSE] - clearly itr if only derefed if bFound which checks for valid itr
2469 const bool bFoundHigher = bFound && itr->first != nXclRow;
2470 if( bFound && !bFoundHigher )
2471 return *itr->second;
2473 size_t nFrom = 0;
2474 RowRef pPrevEntry;
2475 if( itr != maRowMap.begin() )
2477 --itr;
2478 pPrevEntry = itr->second;
2479 if( bFoundHigher )
2480 nFrom = nXclRow;
2481 else
2482 nFrom = itr->first + 1;
2485 const ScDocument& rDoc = GetRoot().GetDoc();
2486 const SCTAB nScTab = GetRoot().GetCurrScTab();
2487 // Do not repeatedly call RowHidden() / GetRowHeight() for same values.
2488 bool bHidden = false;
2489 SCROW lastSameHiddenRow = -1;
2490 sal_uInt16 nHeight = 0;
2491 SCROW lastSameHeightRow = -1;
2492 // create the missing rows first
2493 while( nFrom <= nXclRow )
2495 // only create RowMap entries if it is first row in spreadsheet,
2496 // if it is the desired row, or for rows that differ from previous.
2497 if( static_cast<SCROW>(nFrom) > lastSameHiddenRow )
2498 bHidden = rDoc.RowHidden(nFrom, nScTab, nullptr, &lastSameHiddenRow);
2499 // Always get the actual row height even if the manual size flag is
2500 // not set, to correctly export the heights of rows with wrapped
2501 // texts.
2502 if( static_cast<SCROW>(nFrom) > lastSameHeightRow )
2503 nHeight = rDoc.GetRowHeight(nFrom, nScTab, nullptr, &lastSameHeightRow, false);
2504 if ( !pPrevEntry || ( nFrom == nXclRow ) ||
2505 ( maOutlineBfr.IsCollapsed() ) ||
2506 ( maOutlineBfr.GetLevel() != 0 ) ||
2507 ( bRowAlwaysEmpty && !pPrevEntry->IsEmpty() ) ||
2508 ( bHidden != pPrevEntry->IsHidden() ) ||
2509 ( nHeight != pPrevEntry->GetHeight() ) )
2511 if( maOutlineBfr.GetLevel() > mnHighestOutlineLevel )
2513 mnHighestOutlineLevel = maOutlineBfr.GetLevel();
2515 RowRef p = std::make_shared<XclExpRow>(GetRoot(), nFrom, maOutlineBfr, bRowAlwaysEmpty, bHidden, nHeight);
2516 maRowMap.emplace(nFrom, p);
2517 pPrevEntry = std::move(p);
2519 ++nFrom;
2521 itr = maRowMap.find(nXclRow);
2522 return *itr->second;
2525 // Cell Table
2527 XclExpCellTable::XclExpCellTable( const XclExpRoot& rRoot ) :
2528 XclExpRoot( rRoot ),
2529 maColInfoBfr( rRoot ),
2530 maRowBfr( rRoot ),
2531 maArrayBfr( rRoot ),
2532 maShrfmlaBfr( rRoot ),
2533 maTableopBfr( rRoot ),
2534 mxDefrowheight( new XclExpDefrowheight() ),
2535 mxGuts( new XclExpGuts( rRoot ) ),
2536 mxNoteList( new XclExpNoteList ),
2537 mxMergedcells( new XclExpMergedcells( rRoot ) ),
2538 mxHyperlinkList( new XclExpHyperlinkList ),
2539 mxDval( new XclExpDval( rRoot ) ),
2540 mxExtLst( new XclExtLst( rRoot ) )
2542 ScDocument& rDoc = GetDoc();
2543 SCTAB nScTab = GetCurrScTab();
2544 SvNumberFormatter& rFormatter = GetFormatter();
2546 // maximum sheet limits
2547 SCCOL nMaxScCol = GetMaxPos().Col();
2548 SCROW nMaxScRow = GetMaxPos().Row();
2550 // find used area (non-empty cells)
2551 SCCOL nLastUsedScCol;
2552 SCROW nLastUsedScRow;
2553 rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
2555 if(nLastUsedScCol > nMaxScCol)
2556 nLastUsedScCol = nMaxScCol;
2558 // check extra blank rows to avoid of losing their not default settings (workaround for tdf#41425)
2559 nLastUsedScRow += 1000;
2561 if(nLastUsedScRow > nMaxScRow)
2562 nLastUsedScRow = nMaxScRow;
2564 ScRange aUsedRange( 0, 0, nScTab, nLastUsedScCol, nLastUsedScRow, nScTab );
2565 GetAddressConverter().ValidateRange( aUsedRange, true );
2566 nLastUsedScRow = aUsedRange.aEnd.Row();
2568 // first row without any set attributes (height/hidden/...)
2569 SCROW nFirstUnflaggedScRow = rDoc.GetLastFlaggedRow( nScTab ) + 1;
2571 // find range of outlines
2572 SCROW nFirstUngroupedScRow = 0;
2573 if( const ScOutlineTable* pOutlineTable = rDoc.GetOutlineTable( nScTab ) )
2575 SCCOLROW nScStartPos, nScEndPos;
2576 const ScOutlineArray& rRowArray = pOutlineTable->GetRowArray();
2577 rRowArray.GetRange( nScStartPos, nScEndPos );
2578 // +1 because open/close button is in next row in Excel, +1 for "end->first unused"
2579 nFirstUngroupedScRow = static_cast< SCROW >( nScEndPos + 2 );
2582 // column settings
2583 /* #i30411# Files saved with SO7/OOo1.x with nonstandard default column
2584 formatting cause big Excel files, because all rows from row 1 to row
2585 32000 are exported. Now, if the used area goes exactly to row 32000,
2586 use this row as default and ignore all rows >32000.
2587 #i59220# Tolerance of +-128 rows for inserted/removed rows. */
2588 if( (31871 <= nLastUsedScRow) && (nLastUsedScRow <= 32127) && (nFirstUnflaggedScRow < nLastUsedScRow) && (nFirstUngroupedScRow <= nLastUsedScRow) )
2589 nMaxScRow = nLastUsedScRow;
2590 maColInfoBfr.Initialize( nMaxScRow );
2592 // range for cell iterator
2593 SCCOL nLastIterScCol = nMaxScCol;
2594 SCROW nLastIterScRow = ulimit_cast< SCROW >( nLastUsedScRow, nMaxScRow );
2595 ScUsedAreaIterator aIt( rDoc, nScTab, 0, 0, nLastIterScCol, nLastIterScRow );
2597 // activate the correct segment and sub segment at the progress bar
2598 GetProgressBar().ActivateCreateRowsSegment();
2600 for( bool bIt = aIt.GetNext(); bIt; bIt = aIt.GetNext() )
2602 SCCOL nScCol = aIt.GetStartCol();
2603 SCROW nScRow = aIt.GetRow();
2604 SCCOL nLastScCol = aIt.GetEndCol();
2605 ScAddress aScPos( nScCol, nScRow, nScTab );
2607 XclAddress aXclPos( static_cast< sal_uInt16 >( nScCol ), static_cast< sal_uInt32 >( nScRow ) );
2608 sal_uInt16 nLastXclCol = static_cast< sal_uInt16 >( nLastScCol );
2610 const ScRefCellValue& rScCell = aIt.GetCell();
2611 XclExpCellRef xCell;
2613 const ScPatternAttr* pPattern = aIt.GetPattern();
2615 // handle overlapped merged cells before creating the cell record
2616 sal_uInt32 nMergeBaseXFId = EXC_XFID_NOTFOUND;
2617 bool bIsMergedBase = false;
2618 if( pPattern )
2620 const SfxItemSet& rItemSet = pPattern->GetItemSet();
2621 // base cell in a merged range
2622 const ScMergeAttr& rMergeItem = rItemSet.Get( ATTR_MERGE );
2623 bIsMergedBase = rMergeItem.IsMerged();
2624 /* overlapped cell in a merged range; in Excel all merged cells
2625 must contain same XF index, for correct border */
2626 const ScMergeFlagAttr& rMergeFlagItem = rItemSet.Get( ATTR_MERGE_FLAG );
2627 if( rMergeFlagItem.IsOverlapped() )
2628 nMergeBaseXFId = mxMergedcells->GetBaseXFId( aScPos );
2631 OUString aAddNoteText; // additional text to be appended to a note
2633 switch (rScCell.getType())
2635 case CELLTYPE_VALUE:
2637 double fValue = rScCell.getDouble();
2639 if (pPattern)
2641 OUString aUrl = pPattern->GetItem(ATTR_HYPERLINK).GetValue();
2642 if (!aUrl.isEmpty())
2644 rtl::Reference<XclExpHyperlink> aLink =
2645 new XclExpHyperlink(GetRoot(), SvxURLField(aUrl, aUrl), aScPos);
2646 mxHyperlinkList->AppendRecord(aLink);
2650 // try to create a Boolean cell
2651 if( pPattern && ((fValue == 0.0) || (fValue == 1.0)) )
2653 sal_uInt32 nScNumFmt = pPattern->GetItem( ATTR_VALUE_FORMAT ).GetValue();
2654 if( rFormatter.GetType( nScNumFmt ) == SvNumFormatType::LOGICAL )
2655 xCell = new XclExpBooleanCell(
2656 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue != 0.0 );
2659 // try to create an RK value (compressed floating-point number)
2660 sal_Int32 nRkValue;
2661 if( !xCell && XclTools::GetRKFromDouble( nRkValue, fValue ) )
2662 xCell = new XclExpRkCell(
2663 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, nRkValue );
2665 // else: simple floating-point number cell
2666 if( !xCell )
2667 xCell = new XclExpNumberCell(
2668 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, fValue );
2670 break;
2672 case CELLTYPE_STRING:
2674 xCell = new XclExpLabelCell(
2675 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.getSharedString()->getString());
2677 break;
2679 case CELLTYPE_EDIT:
2681 XclExpHyperlinkHelper aLinkHelper( GetRoot(), aScPos );
2682 xCell = new XclExpLabelCell(
2683 GetRoot(), aXclPos, pPattern, nMergeBaseXFId, rScCell.getEditText(), aLinkHelper);
2685 // add a single created HLINK record to the record list
2686 if( aLinkHelper.HasLinkRecord() )
2687 mxHyperlinkList->AppendRecord( aLinkHelper.GetLinkRecord() );
2688 // add list of multiple URLs to the additional cell note text
2689 if( aLinkHelper.HasMultipleUrls() )
2690 aAddNoteText = ScGlobal::addToken( aAddNoteText, aLinkHelper.GetUrlList(), '\n', 2 );
2692 break;
2694 case CELLTYPE_FORMULA:
2696 if (pPattern)
2698 OUString aUrl = pPattern->GetItem(ATTR_HYPERLINK).GetValue();
2699 if (!aUrl.isEmpty())
2701 rtl::Reference<XclExpHyperlink> aLink =
2702 new XclExpHyperlink(GetRoot(), SvxURLField(aUrl, aUrl), aScPos);
2703 mxHyperlinkList->AppendRecord(aLink);
2707 xCell = new XclExpFormulaCell(
2708 GetRoot(), aXclPos, pPattern, nMergeBaseXFId,
2709 *rScCell.getFormula(), maArrayBfr, maShrfmlaBfr, maTableopBfr);
2711 break;
2713 default:
2714 OSL_FAIL( "XclExpCellTable::XclExpCellTable - unknown cell type" );
2715 [[fallthrough]];
2716 case CELLTYPE_NONE:
2718 xCell = new XclExpBlankCell(
2719 GetRoot(), aXclPos, nLastXclCol, pPattern, nMergeBaseXFId );
2721 break;
2724 assert(xCell && "can only reach here with xCell set");
2726 // insert the cell into the current row
2727 maRowBfr.AppendCell( xCell, bIsMergedBase );
2729 if ( !aAddNoteText.isEmpty() )
2730 mxNoteList->AppendNewRecord( new XclExpNote( GetRoot(), aScPos, nullptr, aAddNoteText ) );
2732 // other sheet contents
2733 if( pPattern )
2735 const SfxItemSet& rItemSet = pPattern->GetItemSet();
2737 // base cell in a merged range
2738 if( bIsMergedBase )
2740 const ScMergeAttr& rMergeItem = rItemSet.Get( ATTR_MERGE );
2741 ScRange aScRange( aScPos );
2742 aScRange.aEnd.IncCol( rMergeItem.GetColMerge() - 1 );
2743 aScRange.aEnd.IncRow( rMergeItem.GetRowMerge() - 1 );
2744 sal_uInt32 nXFId = xCell->GetFirstXFId();
2745 // blank cells merged vertically may occur repeatedly
2746 OSL_ENSURE( (aScRange.aStart.Col() == aScRange.aEnd.Col()) || (nScCol == nLastScCol),
2747 "XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" );
2748 for( SCCOL nIndex = nScCol; nIndex <= nLastScCol; ++nIndex )
2750 mxMergedcells->AppendRange( aScRange, nXFId );
2751 aScRange.aStart.IncCol();
2752 aScRange.aEnd.IncCol();
2756 // data validation
2757 if( ScfTools::CheckItem( rItemSet, ATTR_VALIDDATA, false ) )
2759 sal_uInt32 nScHandle = rItemSet.Get( ATTR_VALIDDATA ).GetValue();
2760 ScRange aScRange( aScPos );
2761 aScRange.aEnd.SetCol( nLastScCol );
2762 mxDval->InsertCellRange( aScRange, nScHandle );
2767 // create missing row settings for rows anyhow flagged or with outlines
2768 maRowBfr.CreateRows( ::std::max( nFirstUnflaggedScRow, nFirstUngroupedScRow ) );
2771 void XclExpCellTable::Finalize(bool bXLS)
2773 // Finalize multiple operations.
2774 maTableopBfr.Finalize();
2776 /* Finalize column buffer. This calculates column default XF indexes from
2777 the XF identifiers and fills a vector with these XF indexes. */
2778 ScfUInt16Vec aColXFIndexes;
2779 maColInfoBfr.Finalize( aColXFIndexes, bXLS );
2781 // Usually many indexes towards the end will be EXC_XF_DEFAULTCELL, find
2782 // the index that starts all EXC_XF_DEFAULTCELL until the end.
2783 size_t nStartColAllDefault = findFirstAllSameUntilEnd( aColXFIndexes, EXC_XF_DEFAULTCELL );
2785 /* Finalize row buffer. This calculates all cell XF indexes from the XF
2786 identifiers. Then the XF index vector aColXFIndexes (filled above) is
2787 used to calculate the row default formats. With this, all unneeded blank
2788 cell records (equal to row default or column default) will be removed.
2789 The function returns the (most used) default row format in aDefRowData. */
2790 XclExpDefaultRowData aDefRowData;
2791 maRowBfr.Finalize( aDefRowData, aColXFIndexes, nStartColAllDefault );
2793 // Initialize the DEFROWHEIGHT record.
2794 mxDefrowheight->SetDefaultData( aDefRowData );
2797 XclExpRecordRef XclExpCellTable::CreateRecord( sal_uInt16 nRecId ) const
2799 XclExpRecordRef xRec;
2800 switch( nRecId )
2802 case EXC_ID3_DIMENSIONS: xRec = new XclExpDelegatingRecord( &const_cast<XclExpRowBuffer*>(&maRowBfr)->GetDimensions() ); break;
2803 case EXC_ID2_DEFROWHEIGHT: xRec = mxDefrowheight; break;
2804 case EXC_ID_GUTS: xRec = mxGuts; break;
2805 case EXC_ID_NOTE: xRec = mxNoteList; break;
2806 case EXC_ID_MERGEDCELLS: xRec = mxMergedcells; break;
2807 case EXC_ID_HLINK: xRec = mxHyperlinkList; break;
2808 case EXC_ID_DVAL: xRec = mxDval; break;
2809 case EXC_ID_EXTLST: xRec = mxExtLst; break;
2810 default: OSL_FAIL( "XclExpCellTable::CreateRecord - unknown record id" );
2812 return xRec;
2815 void XclExpCellTable::Save( XclExpStream& rStrm )
2817 // DEFCOLWIDTH and COLINFOs
2818 maColInfoBfr.Save( rStrm );
2819 // ROWs and cell records
2820 maRowBfr.Save( rStrm );
2823 void XclExpCellTable::SaveXml( XclExpXmlStream& rStrm )
2825 // DEFAULT row height
2826 XclExpDefaultRowData& rDefData = mxDefrowheight->GetDefaultData();
2827 sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();
2828 rWorksheet->startElement( XML_sheetFormatPr,
2829 // OOXTODO: XML_baseColWidth
2830 XML_defaultColWidth, OString::number(maColInfoBfr.GetDefColWidth()),
2831 // OOXTODO: XML_thickTop
2832 // OOXTODO: XML_thickBottom
2833 XML_defaultRowHeight, OString::number(static_cast<double> (rDefData.mnHeight) / 20.0),
2834 XML_customHeight, ToPsz(true),
2835 XML_zeroHeight, ToPsz( rDefData.IsHidden() ),
2836 XML_outlineLevelRow, OString::number(maRowBfr.GetHighestOutlineLevel()),
2837 XML_outlineLevelCol, OString::number(maColInfoBfr.GetHighestOutlineLevel()) );
2838 rWorksheet->endElement( XML_sheetFormatPr );
2840 maColInfoBfr.SaveXml( rStrm );
2841 maRowBfr.SaveXml( rStrm );
2842 mxExtLst->SaveXml( rStrm );
2845 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */