1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
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
)
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
)
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
),
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
) :
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
);
158 XclExpArrayRef
XclExpArrayBuffer::FindArray( const ScTokenArray
& rScTokArr
, const ScAddress
& rBasePos
) const
161 // try to extract a matrix reference token
162 if (rScTokArr
.GetLen() != 1)
163 // Must consist of a single reference token.
166 const formula::FormulaToken
* pToken
= rScTokArr
.GetArray()[0];
167 if (!pToken
|| pToken
->GetOpCode() != ocMatRef
)
168 // not a matrix reference token.
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())
180 // Shared formulas ============================================================
182 XclExpShrfmla::XclExpShrfmla( const XclTokenArrayRef
& xTokArr
, const ScAddress
& rScPos
) :
183 XclExpRangeFmlaBase( EXC_ID_SHRFMLA
, 10 + xTokArr
->GetSize(), rScPos
),
189 void XclExpShrfmla::ExtendRange( const ScAddress
& rScPos
)
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
) :
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())
229 const ScSingleRefData
& rRefData
= *p
->GetSingleRef();
230 if (!GetFormulaCompiler().IsRef2D(rRefData
))
231 // Excel's shared formula cannot include 3D reference.
237 const ScComplexRefData
& rRefData
= *p
->GetDoubleRef();
238 if (!GetFormulaCompiler().IsRef2D(rRefData
))
239 // Excel's shared formula cannot include 3D reference.
243 case svExternalSingleRef
:
244 case svExternalDoubleRef
:
246 // External references aren't allowed.
255 XclExpShrfmlaRef
XclExpShrfmlaBuffer::CreateOrExtendShrfmla(
256 const ScFormulaCell
& rScCell
, const ScAddress
& rScPos
)
258 XclExpShrfmlaRef xRec
;
259 const ScTokenArray
* pShrdScTokArr
= rScCell
.GetSharedCode();
261 // This formula cell is not shared formula cell.
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.
269 if (!IsValidTokenArray(*pShrdScTokArr
))
271 // We can't export this as shared formula.
272 maBadTokens
.insert(pShrdScTokArr
);
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
;
286 // extend existing record
287 OSL_ENSURE( aIt
->second
, "XclExpShrfmlaBuffer::CreateOrExtendShrfmla - missing record" );
289 xRec
->ExtendRange( rScPos
);
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() ) ),
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
);
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
)
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());
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);
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());
364 // extend the cell range
365 OSL_ENSURE( IsAppendable( nXclCol
, nXclRow
), "XclExpTableop::TryExtend - wrong cell address" );
367 mnLastAppXclCol
= nXclCol
;
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
;
385 // check if referred cells are outside of own range
392 mbValid
= (mnColInpXclCol
+ 1 < maXclRange
.maFirst
.mnCol
) || (mnColInpXclCol
> maXclRange
.maLast
.mnCol
) ||
393 (mnColInpXclRow
< maXclRange
.maFirst
.mnRow
) || (mnColInpXclRow
> maXclRange
.maLast
.mnRow
);
396 mbValid
= (mnColInpXclCol
< maXclRange
.maFirst
.mnCol
) || (mnColInpXclCol
> maXclRange
.maLast
.mnCol
) ||
397 (mnColInpXclRow
+ 1 < maXclRange
.maFirst
.mnRow
) || (mnColInpXclRow
> maXclRange
.maLast
.mnRow
);
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
));
408 XclTokenArrayRef
XclExpTableop::CreateCellTokenArray( const XclExpRoot
& rRoot
) const
410 XclExpFormulaCompiler
& rFmlaComp
= rRoot
.GetFormulaCompiler();
412 rFmlaComp
.CreateSpecialRefFormula( EXC_TOKID_TBL
, maBaseXclPos
) :
413 rFmlaComp
.CreateErrorFormula( EXC_ERR_NA
);
416 bool XclExpTableop::IsVolatile() const
421 void XclExpTableop::Save( XclExpStream
& rStrm
)
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() );
440 case 1: ::set_flag( nFlags
, EXC_TABLEOP_ROW
); break;
441 case 2: ::set_flag( nFlags
, EXC_TABLEOP_BOTH
); break;
444 WriteRangeAddress( rStrm
);
447 rStrm
<< mnRowInpXclRow
<< mnRowInpXclCol
<< mnColInpXclRow
<< mnColInpXclCol
;
449 rStrm
<< mnColInpXclRow
<< mnColInpXclCol
<< sal_uInt32( 0 );
452 XclExpTableopBuffer::XclExpTableopBuffer( const 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
) )
474 // no record found, or found record not extensible
476 xRec
= TryCreate( rScPos
, aRefs
);
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());
497 if( rRefs
.mbDblRefMode
)
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()) )
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) )
529 XclExpTableopRef xRec
;
532 xRec
= new XclExpTableop( rScPos
, rRefs
, nScMode
);
533 maTableopList
.AppendRecord( xRec
);
541 XclExpCellBase::XclExpCellBase(
542 sal_uInt16 nRecId
, std::size_t nContSize
, const XclAddress
& rXclPos
) :
543 XclExpRecord( nRecId
, nContSize
+ 4 ),
548 bool XclExpCellBase::IsMultiLineText() const
553 bool XclExpCellBase::TryMerge( const XclExpCellBase
& /*rCell*/ )
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
),
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
594 sal_uInt32
XclExpSingleCellBase::GetFirstXFId() const
599 bool XclExpSingleCellBase::IsEmpty() const
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
),
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),
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
)
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
),
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),
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
;
714 xText
= XclExpStringHelper::CreateCellString(
715 rRoot
, *pEditText
, pPattern
, rLinkHelper
, XclStrFlags::NONE
, nMaxLen
);
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" );
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();
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() )
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() );
777 // BIFF8+: create a LABELSST record
778 mnSstIndex
= rRoot
.GetSst().Insert( xText
);
779 SetRecId( EXC_ID_LABELSST
);
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),
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() )
807 if( mxText
->IsRich() )
809 rStrm
<< static_cast< sal_uInt8
>( mxText
->GetFormatsCount() );
810 mxText
->WriteFormats( rStrm
);
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
881 switch( mrScFmlaCell
.GetMatrixFlag() )
883 case ScMatrixMode::Formula
:
885 // origin of the matrix - find the used matrix range
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
);
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" );
911 // no matrix found - try to create shared formula
913 mxAddRec
= rShrfmlaBfr
.CreateOrExtendShrfmla(mrScFmlaCell
, aScPos
);
915 // no shared formula found - create a simple cell formula
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
924 mxTokArr
= mxAddRec
->CreateCellTokenArray( rStrm
.GetRoot() );
926 // FORMULA record itself
927 OSL_ENSURE( mxTokArr
, "XclExpFormulaCell::Save - missing token array" );
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
939 mxStringRec
->Save( rStrm
);
942 void XclExpFormulaCell::SaveXml( XclExpXmlStream
& rStrm
)
944 const char* sType
= nullptr;
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),
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
:
964 case ScMatrixMode::Reference
:
965 bWriteFormula
= false;
967 case ScMatrixMode::Formula
:
969 // origin of the matrix - find the used matrix range
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
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
);
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 );
1059 // result of the formula
1060 switch( mrScFmlaCell
.GetFormatType() )
1062 case SvNumFormatType::NUMBER
:
1064 // either value or error code
1065 rStrm
<< mrScFmlaCell
.GetValue();
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
);
1078 rStrm
<< EXC_FORMULA_RES_EMPTY
; // BIFF8 only
1079 rStrm
<< sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
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 );
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
;
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
;
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
);
1186 rStrm
<< static_cast< sal_uInt16
>( nEndXclCol
- 1 );
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
;
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
;
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
)
1230 XclAddress( static_cast<sal_uInt16
>(nBegXclCol
+ nRelCol
), GetXclRow() ),
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
);
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() );
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
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
) )
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() );
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
),
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
];
1416 XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot
& rRoot
, bool bRows
) :
1417 mpScOLArray( nullptr ),
1418 maLevelInfos( SC_OL_MAXDEPTH
),
1420 mbCurrCollapse( false )
1422 if( const ScOutlineTable
* pOutlineTable
= rRoot
.GetDoc().GetOutlineTable( rRoot
.GetCurrScTab() ) )
1423 mpScOLArray
= &(bRows
? pOutlineTable
->GetRowArray() : pOutlineTable
->GetColArray());
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
)
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();
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 ),
1482 const ScOutlineTable
* pOutlineTable
= rRoot
.GetDoc().GetOutlineTable( rRoot
.GetCurrScTab() );
1486 // column outline groups
1487 const ScOutlineArray
& rColArray
= pOutlineTable
->GetColArray();
1488 mnColLevels
= ulimit_cast
< sal_uInt16
>( rColArray
.GetDepth(), EXC_OUTLINE_MAX
);
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
);
1501 mnRowWidth
= 12 * mnRowLevels
+ 5;
1505 void XclExpGuts::WriteBody( XclExpStream
& rStrm
)
1507 rStrm
<< mnRowWidth
<< mnColWidth
<< mnRowLevels
<< mnColLevels
;
1510 XclExpDimensions::XclExpDimensions( const XclExpRoot
& 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;
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
)
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
;
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 );
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;
1585 XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot
& rRoot
) :
1586 XclExpDoubleRecord(EXC_ID_DEFCOLWIDTH
, EXC_DEFCOLWIDTH_DEF
+ lclGetCChCorrection(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;
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)
1618 fCCh
= fCChRound
+ fCorrection
;
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 ),
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
);
1658 ::set_flag( mnFlags
, EXC_COLINFO_HIDDEN
, rDoc
.ColHidden(nScCol
, nScTab
) );
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
) &&
1677 (mnOutlineLevel
== 0) &&
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
;
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() ) )
1702 rStrm
<< mnFirstXclCol
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
)
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
);
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
)
1817 maDefcolwidth
.Save( rStrm
);
1819 maColInfos
.Save( rStrm
);
1822 void XclExpColinfoBuffer::SaveXml( XclExpXmlStream
& rStrm
)
1824 if( maColInfos
.IsEmpty() )
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 ),
1879 mnCurrentRow( nXclRow
),
1880 mbAlwaysEmpty( bAlwaysEmpty
),
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
)
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
)
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
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() );
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);
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
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
];
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.
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
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
;
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
;
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
);
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
);
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.
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
) ) &&
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
)
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
) )
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()
2182 void XclExpRow::SaveXml( XclExpXmlStream
& rStrm
)
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 );
2233 class RowFinalizeTask
: public comphelper::ThreadTask
2236 const ScfUInt16Vec
& mrColXFIndexes
;
2237 size_t mnStartColAllDefault
;
2238 std::vector
< XclExpRow
* > maRows
;
2240 RowFinalizeTask( const std::shared_ptr
<comphelper::ThreadTaskTag
> & pTag
,
2241 const ScfUInt16Vec
& rColXFIndexes
,
2242 size_t nStartColAllDefault
,
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();
2270 // This is staggeringly slow, and each element operates only
2272 const size_t nRows
= maRowMap
.size();
2273 const size_t nThreads
= nRows
< 128 ? 1 : comphelper::ThreadPool::getPreferredConcurrency();
2275 const size_t nThreads
= 1; // globally disable multi-threading for now.
2279 ScfUInt16Vec
aXFIndexes( rColXFIndexes
.size(), EXC_XF_NOTFOUND
);
2280 for (auto& rEntry
: maRowMap
)
2281 rEntry
.second
->Finalize( rColXFIndexes
, aXFIndexes
, nStartColAllDefault
, true );
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 ) );
2292 for ( const auto& rEntry
: maRowMap
)
2294 aTasks
[ nIdx
% nThreads
]->push_back( rEntry
.second
.get() );
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
];
2325 if( rnDefCount
> nMaxDefCount
)
2327 nMaxDefCount
= rnDefCount
;
2328 aMaxDefData
= aDefData
;
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();
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
;
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
);
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();
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();
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
;
2475 if( itr
!= maRowMap
.begin() )
2478 pPrevEntry
= itr
->second
;
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
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
);
2521 itr
= maRowMap
.find(nXclRow
);
2522 return *itr
->second
;
2527 XclExpCellTable::XclExpCellTable( const XclExpRoot
& rRoot
) :
2528 XclExpRoot( rRoot
),
2529 maColInfoBfr( 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 );
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;
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();
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)
2661 if( !xCell
&& XclTools::GetRKFromDouble( nRkValue
, fValue
) )
2662 xCell
= new XclExpRkCell(
2663 GetRoot(), aXclPos
, pPattern
, nMergeBaseXFId
, nRkValue
);
2665 // else: simple floating-point number cell
2667 xCell
= new XclExpNumberCell(
2668 GetRoot(), aXclPos
, pPattern
, nMergeBaseXFId
, fValue
);
2672 case CELLTYPE_STRING
:
2674 xCell
= new XclExpLabelCell(
2675 GetRoot(), aXclPos
, pPattern
, nMergeBaseXFId
, rScCell
.getSharedString()->getString());
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 );
2694 case CELLTYPE_FORMULA
:
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
);
2714 OSL_FAIL( "XclExpCellTable::XclExpCellTable - unknown cell type" );
2718 xCell
= new XclExpBlankCell(
2719 GetRoot(), aXclPos
, nLastXclCol
, pPattern
, nMergeBaseXFId
);
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
2735 const SfxItemSet
& rItemSet
= pPattern
->GetItemSet();
2737 // base cell in a merged range
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();
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
;
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" );
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: */