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
->IsWrapped();
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" );
748 bool bForceLineBreak
= mxText
->IsWrapped();
749 SetXFId( rRoot
.GetXFBuffer().InsertWithFont( pPattern
, ApiScriptType::WEAK
, nXclFont
, bForceLineBreak
) );
752 // get auto-wrap attribute from cell format
753 const XclExpXF
* pXF
= rRoot
.GetXFBuffer().GetXFById( GetXFId() );
754 mbLineBreak
= pXF
&& pXF
->GetAlignmentData().mbLineBreak
;
756 // initialize the record contents
757 switch( rRoot
.GetBiff() )
760 // BIFF5-BIFF7: create a LABEL or RSTRING record
761 OSL_ENSURE( mxText
->Len() <= EXC_LABEL_MAXLEN
, "XclExpLabelCell::XclExpLabelCell - string too long" );
762 SetContSize( mxText
->GetSize() );
763 // formatted string is exported in an RSTRING record
764 if( mxText
->IsRich() )
766 OSL_ENSURE( mxText
->GetFormatsCount() <= EXC_LABEL_MAXLEN
, "XclExpLabelCell::WriteContents - too many formats" );
767 mxText
->LimitFormatCount( EXC_LABEL_MAXLEN
);
768 SetRecId( EXC_ID_RSTRING
);
769 SetContSize( GetContSize() + 1 + 2 * mxText
->GetFormatsCount() );
773 // BIFF8+: create a LABELSST record
774 mnSstIndex
= rRoot
.GetSst().Insert( xText
);
775 SetRecId( EXC_ID_LABELSST
);
778 default: DBG_ERROR_BIFF();
782 void XclExpLabelCell::SaveXml( XclExpXmlStream
& rStrm
)
784 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
785 rWorksheet
->startElement( XML_c
,
786 XML_r
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
787 XML_s
, lcl_GetStyleId(rStrm
, *this),
789 // OOXTODO: XML_cm, XML_vm, XML_ph
791 rWorksheet
->startElement( XML_v
);
792 rWorksheet
->write( static_cast<sal_Int32
>(mnSstIndex
) );
793 rWorksheet
->endElement( XML_v
);
794 rWorksheet
->endElement( XML_c
);
797 void XclExpLabelCell::WriteContents( XclExpStream
& rStrm
)
799 switch( rStrm
.GetRoot().GetBiff() )
803 if( mxText
->IsRich() )
805 rStrm
<< static_cast< sal_uInt8
>( mxText
->GetFormatsCount() );
806 mxText
->WriteFormats( rStrm
);
812 default: DBG_ERROR_BIFF();
816 XclExpFormulaCell::XclExpFormulaCell(
817 const XclExpRoot
& rRoot
, const XclAddress
& rXclPos
,
818 const ScPatternAttr
* pPattern
, sal_uInt32 nForcedXFId
,
819 const ScFormulaCell
& rScFmlaCell
,
820 XclExpArrayBuffer
& rArrayBfr
,
821 XclExpShrfmlaBuffer
& rShrfmlaBfr
,
822 XclExpTableopBuffer
& rTableopBfr
) :
823 XclExpSingleCellBase( EXC_ID2_FORMULA
, 0, rXclPos
, nForcedXFId
),
824 mrScFmlaCell( const_cast< ScFormulaCell
& >( rScFmlaCell
) )
826 // *** Find result number format overwriting cell number format *** -------
828 if( GetXFId() == EXC_XFID_NOTFOUND
)
830 SvNumberFormatter
& rFormatter
= rRoot
.GetFormatter();
831 XclExpNumFmtBuffer
& rNumFmtBfr
= rRoot
.GetNumFmtBuffer();
833 // current cell number format
834 sal_uInt32 nScNumFmt
= pPattern
?
835 pPattern
->GetItemSet().Get( ATTR_VALUE_FORMAT
).GetValue() :
836 rNumFmtBfr
.GetStandardFormat();
838 // alternative number format passed to XF buffer
839 sal_uInt32 nAltScNumFmt
= NUMBERFORMAT_ENTRY_NOT_FOUND
;
840 /* Xcl doesn't know Boolean number formats, we write
841 "TRUE";"FALSE" (language dependent). Don't do it for automatic
842 formula formats, because Excel gets them right. */
843 /* #i8640# Don't set text format, if we have string results. */
844 SvNumFormatType nFormatType
= mrScFmlaCell
.GetFormatType();
845 if( ((nScNumFmt
% SV_COUNTRY_LANGUAGE_OFFSET
) == 0) &&
846 (nFormatType
!= SvNumFormatType::LOGICAL
) &&
847 (nFormatType
!= SvNumFormatType::TEXT
) )
848 nAltScNumFmt
= nScNumFmt
;
849 /* If cell number format is Boolean and automatic formula
850 format is Boolean don't write that ugly special format. */
851 else if( (nFormatType
== SvNumFormatType::LOGICAL
) &&
852 (rFormatter
.GetType( nScNumFmt
) == SvNumFormatType::LOGICAL
) )
853 nAltScNumFmt
= rNumFmtBfr
.GetStandardFormat();
855 // #i41420# find script type according to result type (always latin for numeric results)
856 sal_Int16 nScript
= ApiScriptType::LATIN
;
857 bool bForceLineBreak
= false;
858 if( nFormatType
== SvNumFormatType::TEXT
)
860 OUString aResult
= mrScFmlaCell
.GetString().getString();
861 bForceLineBreak
= mrScFmlaCell
.IsMultilineResult();
862 nScript
= XclExpStringHelper::GetLeadingScriptType( rRoot
, aResult
);
864 SetXFId( rRoot
.GetXFBuffer().InsertWithNumFmt( pPattern
, nScript
, nAltScNumFmt
, bForceLineBreak
) );
867 // *** Convert the formula token array *** --------------------------------
869 ScAddress
aScPos( static_cast< SCCOL
>( rXclPos
.mnCol
), static_cast< SCROW
>( rXclPos
.mnRow
), rRoot
.GetCurrScTab() );
870 const ScTokenArray
& rScTokArr
= *mrScFmlaCell
.GetCode();
872 // first try to create multiple operations
873 mxAddRec
= rTableopBfr
.CreateOrExtendTableop( rScTokArr
, aScPos
);
875 // no multiple operation found - try to create matrix formula
877 switch( mrScFmlaCell
.GetMatrixFlag() )
879 case ScMatrixMode::Formula
:
881 // origin of the matrix - find the used matrix range
884 mrScFmlaCell
.GetMatColsRows( nMatWidth
, nMatHeight
);
885 OSL_ENSURE( nMatWidth
&& nMatHeight
, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
886 ScRange
aMatScRange( aScPos
);
887 ScAddress
& rMatEnd
= aMatScRange
.aEnd
;
888 rMatEnd
.IncCol( static_cast< SCCOL
>( nMatWidth
- 1 ) );
889 rMatEnd
.IncRow( static_cast< SCROW
>( nMatHeight
- 1 ) );
890 // reduce to valid range (range keeps valid, because start position IS valid)
891 rRoot
.GetAddressConverter().ValidateRange( aMatScRange
, true );
892 // create the ARRAY record
893 mxAddRec
= rArrayBfr
.CreateArray( rScTokArr
, aMatScRange
);
896 case ScMatrixMode::Reference
:
898 // other formula cell covered by a matrix - find the ARRAY record
899 mxAddRec
= rArrayBfr
.FindArray(rScTokArr
, aScPos
);
900 // should always be found, if Calc document is not broken
901 OSL_ENSURE( mxAddRec
, "XclExpFormulaCell::XclExpFormulaCell - no matrix found" );
907 // no matrix found - try to create shared formula
909 mxAddRec
= rShrfmlaBfr
.CreateOrExtendShrfmla(mrScFmlaCell
, aScPos
);
911 // no shared formula found - create a simple cell formula
913 mxTokArr
= rRoot
.GetFormulaCompiler().CreateFormula( EXC_FMLATYPE_CELL
, rScTokArr
, &aScPos
);
916 void XclExpFormulaCell::Save( XclExpStream
& rStrm
)
918 // create token array for FORMULA cells with additional record
920 mxTokArr
= mxAddRec
->CreateCellTokenArray( rStrm
.GetRoot() );
922 // FORMULA record itself
923 OSL_ENSURE( mxTokArr
, "XclExpFormulaCell::Save - missing token array" );
925 mxTokArr
= rStrm
.GetRoot().GetFormulaCompiler().CreateErrorFormula( EXC_ERR_NA
);
926 SetContSize( 16 + mxTokArr
->GetSize() );
927 XclExpSingleCellBase::Save( rStrm
);
929 // additional record (ARRAY, SHRFMLA, or TABLEOP), only for first FORMULA record
930 if( mxAddRec
&& mxAddRec
->IsBasePos( GetXclCol(), GetXclRow() ) )
931 mxAddRec
->Save( rStrm
);
933 // STRING record for string result
935 mxStringRec
->Save( rStrm
);
938 void XclExpFormulaCell::SaveXml( XclExpXmlStream
& rStrm
)
940 const char* sType
= nullptr;
942 XclXmlUtils::GetFormulaTypeAndValue( mrScFmlaCell
, sType
, sValue
);
943 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
944 rWorksheet
->startElement( XML_c
,
945 XML_r
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetStringBuf(), GetXclPos()).getStr(),
946 XML_s
, lcl_GetStyleId(rStrm
, *this),
948 // OOXTODO: XML_cm, XML_vm, XML_ph
951 bool bWriteFormula
= true;
952 bool bTagStarted
= false;
953 ScAddress
aScPos( static_cast< SCCOL
>( GetXclPos().mnCol
),
954 static_cast< SCROW
>( GetXclPos().mnRow
), rStrm
.GetRoot().GetCurrScTab() );
956 switch (mrScFmlaCell
.GetMatrixFlag())
958 case ScMatrixMode::NONE
:
960 case ScMatrixMode::Reference
:
961 bWriteFormula
= false;
963 case ScMatrixMode::Formula
:
965 // origin of the matrix - find the used matrix range
968 mrScFmlaCell
.GetMatColsRows( nMatWidth
, nMatHeight
);
969 OSL_ENSURE( nMatWidth
&& nMatHeight
, "XclExpFormulaCell::XclExpFormulaCell - empty matrix" );
970 ScRange
aMatScRange( aScPos
);
971 ScAddress
& rMatEnd
= aMatScRange
.aEnd
;
972 rMatEnd
.IncCol( static_cast< SCCOL
>( nMatWidth
- 1 ) );
973 rMatEnd
.IncRow( static_cast< SCROW
>( nMatHeight
- 1 ) );
974 // reduce to valid range (range keeps valid, because start position IS valid
975 rStrm
.GetRoot().GetAddressConverter().ValidateRange( aMatScRange
, true );
977 OStringBuffer sFmlaCellRange
;
978 if (rStrm
.GetRoot().GetDoc().ValidRange(aMatScRange
))
980 // calculate the cell range.
981 sFmlaCellRange
.append( XclXmlUtils::ToOString(
982 rStrm
.GetRoot().GetStringBuf(), aMatScRange
.aStart
).getStr());
983 sFmlaCellRange
.append(":");
984 sFmlaCellRange
.append( XclXmlUtils::ToOString(
985 rStrm
.GetRoot().GetStringBuf(), aMatScRange
.aEnd
).getStr());
988 if ( aMatScRange
.aStart
.Col() == GetXclPos().mnCol
&&
989 aMatScRange
.aStart
.Row() == static_cast<SCROW
>(GetXclPos().mnRow
))
991 rWorksheet
->startElement( XML_f
,
992 XML_aca
, ToPsz( (mxTokArr
&& mxTokArr
->IsVolatile()) ||
993 (mxAddRec
&& mxAddRec
->IsVolatile())),
994 XML_t
, mxAddRec
? "array" : nullptr,
995 XML_ref
, !sFmlaCellRange
.isEmpty()? sFmlaCellRange
.getStr() : nullptr
996 // OOXTODO: XML_dt2D, bool
997 // OOXTODO: XML_dtr, bool
998 // OOXTODO: XML_del1, bool
999 // OOXTODO: XML_del2, bool
1000 // OOXTODO: XML_r1, ST_CellRef
1001 // OOXTODO: XML_r2, ST_CellRef
1002 // OOXTODO: XML_ca, bool
1003 // OOXTODO: XML_si, uint
1004 // OOXTODO: XML_bx bool
1016 rWorksheet
->startElement( XML_f
,
1017 XML_aca
, ToPsz( (mxTokArr
&& mxTokArr
->IsVolatile()) ||
1018 (mxAddRec
&& mxAddRec
->IsVolatile()) ) );
1020 rWorksheet
->writeEscaped( XclXmlUtils::ToOUString(
1021 rStrm
.GetRoot().GetCompileFormulaContext(), mrScFmlaCell
.aPos
, mrScFmlaCell
.GetCode(),
1022 mrScFmlaCell
.GetErrCode()));
1023 rWorksheet
->endElement( XML_f
);
1026 if( strcmp( sType
, "inlineStr" ) == 0 )
1028 rWorksheet
->startElement(XML_is
);
1029 rWorksheet
->startElement(XML_t
);
1030 rWorksheet
->writeEscaped( sValue
);
1031 rWorksheet
->endElement( XML_t
);
1032 rWorksheet
->endElement( XML_is
);
1036 rWorksheet
->startElement(XML_v
);
1037 rWorksheet
->writeEscaped( sValue
);
1038 rWorksheet
->endElement( XML_v
);
1040 rWorksheet
->endElement( XML_c
);
1043 void XclExpFormulaCell::WriteContents( XclExpStream
& rStrm
)
1045 FormulaError nScErrCode
= mrScFmlaCell
.GetErrCode();
1046 if( nScErrCode
!= FormulaError::NONE
)
1048 rStrm
<< EXC_FORMULA_RES_ERROR
<< sal_uInt8( 0 )
1049 << XclTools::GetXclErrorCode( nScErrCode
)
1050 << sal_uInt8( 0 ) << sal_uInt16( 0 )
1051 << sal_uInt16( 0xFFFF );
1055 // result of the formula
1056 switch( mrScFmlaCell
.GetFormatType() )
1058 case SvNumFormatType::NUMBER
:
1060 // either value or error code
1061 rStrm
<< mrScFmlaCell
.GetValue();
1065 case SvNumFormatType::TEXT
:
1067 OUString aResult
= mrScFmlaCell
.GetString().getString();
1068 if( !aResult
.isEmpty() || (rStrm
.GetRoot().GetBiff() <= EXC_BIFF5
) )
1070 rStrm
<< EXC_FORMULA_RES_STRING
;
1071 mxStringRec
= new XclExpStringRec( rStrm
.GetRoot(), aResult
);
1074 rStrm
<< EXC_FORMULA_RES_EMPTY
; // BIFF8 only
1075 rStrm
<< sal_uInt8( 0 ) << sal_uInt32( 0 ) << sal_uInt16( 0xFFFF );
1079 case SvNumFormatType::LOGICAL
:
1081 sal_uInt8 nXclValue
= (mrScFmlaCell
.GetValue() == 0.0) ? 0 : 1;
1082 rStrm
<< EXC_FORMULA_RES_BOOL
<< sal_uInt8( 0 )
1083 << nXclValue
<< sal_uInt8( 0 ) << sal_uInt16( 0 )
1084 << sal_uInt16( 0xFFFF );
1089 rStrm
<< mrScFmlaCell
.GetValue();
1093 // flags and formula token array
1094 sal_uInt16 nFlags
= EXC_FORMULA_DEFAULTFLAGS
;
1095 ::set_flag( nFlags
, EXC_FORMULA_RECALC_ALWAYS
, mxTokArr
->IsVolatile() || (mxAddRec
&& mxAddRec
->IsVolatile()) );
1096 ::set_flag( nFlags
, EXC_FORMULA_SHARED
, mxAddRec
&& (mxAddRec
->GetRecId() == EXC_ID_SHRFMLA
) );
1097 rStrm
<< nFlags
<< sal_uInt32( 0 ) << *mxTokArr
;
1100 // Multiple cell records ======================================================
1102 XclExpMultiCellBase::XclExpMultiCellBase(
1103 sal_uInt16 nRecId
, sal_uInt16 nMulRecId
, std::size_t nContSize
, const XclAddress
& rXclPos
) :
1104 XclExpCellBase( nRecId
, 0, rXclPos
),
1105 mnMulRecId( nMulRecId
),
1106 mnContSize( nContSize
)
1110 sal_uInt16
XclExpMultiCellBase::GetLastXclCol() const
1112 return GetXclCol() + GetCellCount() - 1;
1115 sal_uInt32
XclExpMultiCellBase::GetFirstXFId() const
1117 return maXFIds
.empty() ? XclExpXFBuffer::GetDefCellXFId() : maXFIds
.front().mnXFId
;
1120 bool XclExpMultiCellBase::IsEmpty() const
1122 return maXFIds
.empty();
1125 void XclExpMultiCellBase::ConvertXFIndexes( const XclExpRoot
& rRoot
)
1127 for( auto& rXFId
: maXFIds
)
1128 rXFId
.ConvertXFIndex( rRoot
);
1131 void XclExpMultiCellBase::Save( XclExpStream
& rStrm
)
1133 OSL_ENSURE_BIFF( rStrm
.GetRoot().GetBiff() >= EXC_BIFF3
);
1135 XclExpMultiXFIdDeq::const_iterator aEnd
= maXFIds
.end();
1136 XclExpMultiXFIdDeq::const_iterator aRangeBeg
= maXFIds
.begin();
1137 XclExpMultiXFIdDeq::const_iterator aRangeEnd
= aRangeBeg
;
1138 sal_uInt16 nBegXclCol
= GetXclCol();
1139 sal_uInt16 nEndXclCol
= nBegXclCol
;
1141 while( aRangeEnd
!= aEnd
)
1143 // find begin of next used XF range
1144 aRangeBeg
= aRangeEnd
;
1145 nBegXclCol
= nEndXclCol
;
1146 while( (aRangeBeg
!= aEnd
) && (aRangeBeg
->mnXFIndex
== EXC_XF_NOTFOUND
) )
1148 nBegXclCol
= nBegXclCol
+ aRangeBeg
->mnCount
;
1151 // find end of next used XF range
1152 aRangeEnd
= aRangeBeg
;
1153 nEndXclCol
= nBegXclCol
;
1154 while( (aRangeEnd
!= aEnd
) && (aRangeEnd
->mnXFIndex
!= EXC_XF_NOTFOUND
) )
1156 nEndXclCol
= nEndXclCol
+ aRangeEnd
->mnCount
;
1160 // export this range as a record
1161 if( aRangeBeg
!= aRangeEnd
)
1163 sal_uInt16 nCount
= nEndXclCol
- nBegXclCol
;
1164 bool bIsMulti
= nCount
> 1;
1165 std::size_t nTotalSize
= GetRecSize() + (2 + mnContSize
) * nCount
;
1166 if( bIsMulti
) nTotalSize
+= 2;
1168 rStrm
.StartRecord( bIsMulti
? mnMulRecId
: GetRecId(), nTotalSize
);
1169 rStrm
<< static_cast<sal_uInt16
> (GetXclRow()) << nBegXclCol
;
1171 sal_uInt16 nRelCol
= nBegXclCol
- GetXclCol();
1172 for( XclExpMultiXFIdDeq::const_iterator aIt
= aRangeBeg
; aIt
!= aRangeEnd
; ++aIt
)
1174 for( sal_uInt16 nIdx
= 0; nIdx
< aIt
->mnCount
; ++nIdx
)
1176 rStrm
<< aIt
->mnXFIndex
;
1177 WriteContents( rStrm
, nRelCol
);
1182 rStrm
<< static_cast< sal_uInt16
>( nEndXclCol
- 1 );
1188 void XclExpMultiCellBase::SaveXml( XclExpXmlStream
& rStrm
)
1190 XclExpMultiXFIdDeq::const_iterator aEnd
= maXFIds
.end();
1191 XclExpMultiXFIdDeq::const_iterator aRangeBeg
= maXFIds
.begin();
1192 XclExpMultiXFIdDeq::const_iterator aRangeEnd
= aRangeBeg
;
1193 sal_uInt16 nBegXclCol
= GetXclCol();
1194 sal_uInt16 nEndXclCol
= nBegXclCol
;
1196 while( aRangeEnd
!= aEnd
)
1198 // find begin of next used XF range
1199 aRangeBeg
= aRangeEnd
;
1200 nBegXclCol
= nEndXclCol
;
1201 while( (aRangeBeg
!= aEnd
) && (aRangeBeg
->mnXFIndex
== EXC_XF_NOTFOUND
) )
1203 nBegXclCol
= nBegXclCol
+ aRangeBeg
->mnCount
;
1206 // find end of next used XF range
1207 aRangeEnd
= aRangeBeg
;
1208 nEndXclCol
= nBegXclCol
;
1209 while( (aRangeEnd
!= aEnd
) && (aRangeEnd
->mnXFIndex
!= EXC_XF_NOTFOUND
) )
1211 nEndXclCol
= nEndXclCol
+ aRangeEnd
->mnCount
;
1215 // export this range as a record
1216 if( aRangeBeg
!= aRangeEnd
)
1218 sal_uInt16 nRelColIdx
= nBegXclCol
- GetXclCol();
1219 sal_Int32 nRelCol
= 0;
1220 for( XclExpMultiXFIdDeq::const_iterator aIt
= aRangeBeg
; aIt
!= aRangeEnd
; ++aIt
)
1222 for( sal_uInt16 nIdx
= 0; nIdx
< aIt
->mnCount
; ++nIdx
)
1226 XclAddress( static_cast<sal_uInt16
>(nBegXclCol
+ nRelCol
), GetXclRow() ),
1237 sal_uInt16
XclExpMultiCellBase::GetCellCount() const
1239 return std::accumulate(maXFIds
.begin(), maXFIds
.end(), sal_uInt16(0),
1240 [](const sal_uInt16
& rSum
, const XclExpMultiXFId
& rXFId
) { return rSum
+ rXFId
.mnCount
; });
1243 void XclExpMultiCellBase::AppendXFId( const XclExpMultiXFId
& rXFId
)
1245 if( maXFIds
.empty() || (maXFIds
.back().mnXFId
!= rXFId
.mnXFId
) )
1246 maXFIds
.push_back( rXFId
);
1248 maXFIds
.back().mnCount
+= rXFId
.mnCount
;
1251 void XclExpMultiCellBase::AppendXFId( const XclExpRoot
& rRoot
,
1252 const ScPatternAttr
* pPattern
, sal_uInt16 nScript
, sal_uInt32 nForcedXFId
, sal_uInt16 nCount
)
1254 sal_uInt32 nXFId
= (nForcedXFId
== EXC_XFID_NOTFOUND
) ?
1255 rRoot
.GetXFBuffer().Insert( pPattern
, nScript
) : nForcedXFId
;
1256 AppendXFId( XclExpMultiXFId( nXFId
, nCount
) );
1259 bool XclExpMultiCellBase::TryMergeXFIds( const XclExpMultiCellBase
& rCell
)
1261 if( GetLastXclCol() + 1 == rCell
.GetXclCol() )
1263 maXFIds
.insert( maXFIds
.end(), rCell
.maXFIds
.begin(), rCell
.maXFIds
.end() );
1269 void XclExpMultiCellBase::GetXFIndexes( ScfUInt16Vec
& rXFIndexes
) const
1271 OSL_ENSURE( GetLastXclCol() < rXFIndexes
.size(), "XclExpMultiCellBase::GetXFIndexes - vector too small" );
1272 ScfUInt16Vec::iterator aDestIt
= rXFIndexes
.begin() + GetXclCol();
1273 for( const auto& rXFId
: maXFIds
)
1275 ::std::fill( aDestIt
, aDestIt
+ rXFId
.mnCount
, rXFId
.mnXFIndex
);
1276 aDestIt
+= rXFId
.mnCount
;
1280 void XclExpMultiCellBase::RemoveUnusedXFIndexes( const ScfUInt16Vec
& rXFIndexes
, size_t nStartAllNotFound
)
1282 // save last column before calling maXFIds.clear()
1283 sal_uInt16 nLastXclCol
= GetLastXclCol();
1284 OSL_ENSURE( nLastXclCol
< rXFIndexes
.size(), "XclExpMultiCellBase::RemoveUnusedXFIndexes - XF index vector too small" );
1286 // build new XF index vector, containing passed XF indexes
1288 // Process only all that possibly are not EXC_XF_NOTFOUND.
1289 size_t nEnd
= std::min
<size_t>(nLastXclCol
+ 1, nStartAllNotFound
);
1290 for( size_t i
= GetXclCol(); i
< nEnd
; ++i
)
1292 XclExpMultiXFId
aXFId( 0 );
1293 // AppendXFId() tests XclExpXFIndex::mnXFId, set it too
1294 aXFId
.mnXFId
= aXFId
.mnXFIndex
= rXFIndexes
[ i
];
1295 AppendXFId( aXFId
);
1298 // remove leading and trailing unused XF indexes
1299 if( !maXFIds
.empty() && (maXFIds
.front().mnXFIndex
== EXC_XF_NOTFOUND
) )
1301 SetXclCol( GetXclCol() + maXFIds
.front().mnCount
);
1302 maXFIds
.erase(maXFIds
.begin(), maXFIds
.begin() + 1);
1304 if( !maXFIds
.empty() && (maXFIds
.back().mnXFIndex
== EXC_XF_NOTFOUND
) )
1307 // The Save() function will skip all XF indexes equal to EXC_XF_NOTFOUND.
1310 sal_uInt16
XclExpMultiCellBase::GetStartColAllDefaultCell() const
1312 sal_uInt16 col
= GetXclCol();
1313 sal_uInt16 nMaxNonDefCol
= col
;
1314 for( const auto& rXFId
: maXFIds
)
1316 col
+= rXFId
.mnCount
;
1317 if (rXFId
.mnXFIndex
!= EXC_XF_DEFAULTCELL
)
1318 nMaxNonDefCol
= col
;
1320 return nMaxNonDefCol
;
1323 XclExpBlankCell::XclExpBlankCell( const XclAddress
& rXclPos
, const XclExpMultiXFId
& rXFId
) :
1324 XclExpMultiCellBase( EXC_ID3_BLANK
, EXC_ID_MULBLANK
, 0, rXclPos
)
1326 OSL_ENSURE( rXFId
.mnCount
> 0, "XclExpBlankCell::XclExpBlankCell - invalid count" );
1327 AppendXFId( rXFId
);
1330 XclExpBlankCell::XclExpBlankCell(
1331 const XclExpRoot
& rRoot
, const XclAddress
& rXclPos
, sal_uInt16 nLastXclCol
,
1332 const ScPatternAttr
* pPattern
, sal_uInt32 nForcedXFId
) :
1333 XclExpMultiCellBase( EXC_ID3_BLANK
, EXC_ID_MULBLANK
, 0, rXclPos
)
1335 OSL_ENSURE( rXclPos
.mnCol
<= nLastXclCol
, "XclExpBlankCell::XclExpBlankCell - invalid column range" );
1336 // #i46627# use default script type instead of ApiScriptType::WEAK
1337 AppendXFId( rRoot
, pPattern
, rRoot
.GetDefApiScript(), nForcedXFId
, nLastXclCol
- rXclPos
.mnCol
+ 1 );
1340 bool XclExpBlankCell::TryMerge( const XclExpCellBase
& rCell
)
1342 const XclExpBlankCell
* pBlankCell
= dynamic_cast< const XclExpBlankCell
* >( &rCell
);
1343 return pBlankCell
&& TryMergeXFIds( *pBlankCell
);
1346 void XclExpBlankCell::GetBlankXFIndexes( ScfUInt16Vec
& rXFIndexes
) const
1348 GetXFIndexes( rXFIndexes
);
1351 void XclExpBlankCell::RemoveUnusedBlankCells( const ScfUInt16Vec
& rXFIndexes
, size_t nStartAllNotFound
)
1353 RemoveUnusedXFIndexes( rXFIndexes
, nStartAllNotFound
);
1356 void XclExpBlankCell::WriteContents( XclExpStream
& /*rStrm*/, sal_uInt16
/*nRelCol*/ )
1360 void XclExpBlankCell::WriteXmlContents( XclExpXmlStream
& rStrm
, const XclAddress
& rAddress
, sal_uInt32 nXFId
, sal_uInt16
/* nRelCol */ )
1362 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1363 rWorksheet
->singleElement( XML_c
,
1364 XML_r
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetStringBuf(), rAddress
).getStr(),
1365 XML_s
, lcl_GetStyleId(rStrm
, nXFId
) );
1368 XclExpRkCell::XclExpRkCell(
1369 const XclExpRoot
& rRoot
, const XclAddress
& rXclPos
,
1370 const ScPatternAttr
* pPattern
, sal_uInt32 nForcedXFId
, sal_Int32 nRkValue
) :
1371 XclExpMultiCellBase( EXC_ID_RK
, EXC_ID_MULRK
, 4, rXclPos
)
1373 // #i41210# always use latin script for number cells - may look wrong for special number formats...
1374 AppendXFId( rRoot
, pPattern
, ApiScriptType::LATIN
, nForcedXFId
);
1375 maRkValues
.push_back( nRkValue
);
1378 bool XclExpRkCell::TryMerge( const XclExpCellBase
& rCell
)
1380 const XclExpRkCell
* pRkCell
= dynamic_cast< const XclExpRkCell
* >( &rCell
);
1381 if( pRkCell
&& TryMergeXFIds( *pRkCell
) )
1383 maRkValues
.insert( maRkValues
.end(), pRkCell
->maRkValues
.begin(), pRkCell
->maRkValues
.end() );
1389 void XclExpRkCell::WriteXmlContents( XclExpXmlStream
& rStrm
, const XclAddress
& rAddress
, sal_uInt32 nXFId
, sal_uInt16 nRelCol
)
1391 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1392 rWorksheet
->startElement( XML_c
,
1393 XML_r
, XclXmlUtils::ToOString(rStrm
.GetRoot().GetStringBuf(), rAddress
).getStr(),
1394 XML_s
, lcl_GetStyleId(rStrm
, nXFId
),
1396 // OOXTODO: XML_cm, XML_vm, XML_ph
1398 rWorksheet
->startElement( XML_v
);
1399 rWorksheet
->write( XclTools::GetDoubleFromRK( maRkValues
[ nRelCol
] ) );
1400 rWorksheet
->endElement( XML_v
);
1401 rWorksheet
->endElement( XML_c
);
1404 void XclExpRkCell::WriteContents( XclExpStream
& rStrm
, sal_uInt16 nRelCol
)
1406 OSL_ENSURE( nRelCol
< maRkValues
.size(), "XclExpRkCell::WriteContents - overflow error" );
1407 rStrm
<< maRkValues
[ nRelCol
];
1412 XclExpOutlineBuffer::XclExpOutlineBuffer( const XclExpRoot
& rRoot
, bool bRows
) :
1413 mpScOLArray( nullptr ),
1414 maLevelInfos( SC_OL_MAXDEPTH
),
1416 mbCurrCollapse( false )
1418 if( const ScOutlineTable
* pOutlineTable
= rRoot
.GetDoc().GetOutlineTable( rRoot
.GetCurrScTab() ) )
1419 mpScOLArray
= &(bRows
? pOutlineTable
->GetRowArray() : pOutlineTable
->GetColArray());
1422 for( size_t nLevel
= 0; nLevel
< SC_OL_MAXDEPTH
; ++nLevel
)
1423 if( const ScOutlineEntry
* pEntry
= mpScOLArray
->GetEntryByPos( nLevel
, 0 ) )
1424 maLevelInfos
[ nLevel
].mnScEndPos
= pEntry
->GetEnd();
1427 void XclExpOutlineBuffer::UpdateColRow( SCCOLROW nScPos
)
1432 // find open level index for passed position
1433 size_t nNewOpenScLevel
= 0; // new open level (0-based Calc index)
1434 sal_uInt8 nNewLevel
= 0; // new open level (1-based Excel index)
1436 if( mpScOLArray
->FindTouchedLevel( nScPos
, nScPos
, nNewOpenScLevel
) )
1437 nNewLevel
= static_cast< sal_uInt8
>( nNewOpenScLevel
+ 1 );
1438 // else nNewLevel keeps 0 to show that there are no groups
1440 mbCurrCollapse
= false;
1441 if( nNewLevel
>= mnCurrLevel
)
1443 // new level(s) opened, or no level closed - update all level infos
1444 for( size_t nScLevel
= 0; nScLevel
<= nNewOpenScLevel
; ++nScLevel
)
1446 /* In each level: check if a new group is started (there may be
1447 neighbored groups without gap - therefore check ALL levels). */
1448 if( maLevelInfos
[ nScLevel
].mnScEndPos
< nScPos
)
1450 if( const ScOutlineEntry
* pEntry
= mpScOLArray
->GetEntryByPos( nScLevel
, nScPos
) )
1452 maLevelInfos
[ nScLevel
].mnScEndPos
= pEntry
->GetEnd();
1453 maLevelInfos
[ nScLevel
].mbHidden
= pEntry
->IsHidden();
1460 // level(s) closed - check if any of the closed levels are collapsed
1461 // Calc uses 0-based level indexes
1462 sal_uInt16 nOldOpenScLevel
= mnCurrLevel
- 1;
1463 for( sal_uInt16 nScLevel
= nNewOpenScLevel
+ 1; !mbCurrCollapse
&& (nScLevel
<= nOldOpenScLevel
); ++nScLevel
)
1464 mbCurrCollapse
= maLevelInfos
[ nScLevel
].mbHidden
;
1467 // cache new opened level
1468 mnCurrLevel
= nNewLevel
;
1471 XclExpGuts::XclExpGuts( const XclExpRoot
& rRoot
) :
1472 XclExpRecord( EXC_ID_GUTS
, 8 ),
1478 const ScOutlineTable
* pOutlineTable
= rRoot
.GetDoc().GetOutlineTable( rRoot
.GetCurrScTab() );
1482 // column outline groups
1483 const ScOutlineArray
& rColArray
= pOutlineTable
->GetColArray();
1484 mnColLevels
= ulimit_cast
< sal_uInt16
>( rColArray
.GetDepth(), EXC_OUTLINE_MAX
);
1488 mnColWidth
= 12 * mnColLevels
+ 5;
1491 // row outline groups
1492 const ScOutlineArray
& rRowArray
= pOutlineTable
->GetRowArray();
1493 mnRowLevels
= ulimit_cast
< sal_uInt16
>( rRowArray
.GetDepth(), EXC_OUTLINE_MAX
);
1497 mnRowWidth
= 12 * mnRowLevels
+ 5;
1501 void XclExpGuts::WriteBody( XclExpStream
& rStrm
)
1503 rStrm
<< mnRowWidth
<< mnColWidth
<< mnRowLevels
<< mnColLevels
;
1506 XclExpDimensions::XclExpDimensions( const XclExpRoot
& rRoot
) :
1508 mnFirstUsedXclRow( 0 ),
1509 mnFirstFreeXclRow( 0 ),
1510 mnFirstUsedXclCol( 0 ),
1511 mnFirstFreeXclCol( 0 )
1513 switch( rRoot
.GetBiff() )
1515 case EXC_BIFF2
: SetRecHeader( EXC_ID2_DIMENSIONS
, 8 ); break;
1518 case EXC_BIFF5
: SetRecHeader( EXC_ID3_DIMENSIONS
, 10 ); break;
1519 case EXC_BIFF8
: SetRecHeader( EXC_ID3_DIMENSIONS
, 14 ); break;
1520 default: DBG_ERROR_BIFF();
1524 void XclExpDimensions::SetDimensions(
1525 sal_uInt16 nFirstUsedXclCol
, sal_uInt32 nFirstUsedXclRow
,
1526 sal_uInt16 nFirstFreeXclCol
, sal_uInt32 nFirstFreeXclRow
)
1528 mnFirstUsedXclRow
= nFirstUsedXclRow
;
1529 mnFirstFreeXclRow
= nFirstFreeXclRow
;
1530 mnFirstUsedXclCol
= nFirstUsedXclCol
;
1531 mnFirstFreeXclCol
= nFirstFreeXclCol
;
1534 void XclExpDimensions::SaveXml( XclExpXmlStream
& rStrm
)
1537 aRange
.aStart
.SetRow( static_cast<SCROW
>(mnFirstUsedXclRow
) );
1538 aRange
.aStart
.SetCol( static_cast<SCCOL
>(mnFirstUsedXclCol
) );
1540 if( mnFirstFreeXclRow
!= mnFirstUsedXclRow
&& mnFirstFreeXclCol
!= mnFirstUsedXclCol
)
1542 aRange
.aEnd
.SetRow( static_cast<SCROW
>(mnFirstFreeXclRow
-1) );
1543 aRange
.aEnd
.SetCol( static_cast<SCCOL
>(mnFirstFreeXclCol
-1) );
1546 aRange
.PutInOrder();
1547 rStrm
.GetCurrentStream()->singleElement( XML_dimension
,
1548 // To be compatible with MS Office 2007,
1549 // we need full address notation format
1550 // e.g. "A1:AMJ177" and not partial like: "1:177".
1551 XML_ref
, XclXmlUtils::ToOString(mrRoot
.GetDoc(), aRange
, true) );
1554 void XclExpDimensions::WriteBody( XclExpStream
& rStrm
)
1556 XclBiff eBiff
= rStrm
.GetRoot().GetBiff();
1557 if( eBiff
== EXC_BIFF8
)
1558 rStrm
<< mnFirstUsedXclRow
<< mnFirstFreeXclRow
;
1560 rStrm
<< static_cast< sal_uInt16
>( mnFirstUsedXclRow
) << static_cast< sal_uInt16
>( mnFirstFreeXclRow
);
1561 rStrm
<< mnFirstUsedXclCol
<< mnFirstFreeXclCol
;
1562 if( eBiff
>= EXC_BIFF3
)
1563 rStrm
<< sal_uInt16( 0 );
1568 double lclGetCChCorrection(const XclExpRoot
& rRoot
)
1570 // Convert the correction from 1/256ths of a character size to count of chars
1571 // TODO: make to fit ECMA-376-1:2016 18.3.1.81 sheetFormatPr (Sheet Format Properties):
1572 // 5 pixels are added to the base width: 2 for margin padding on each side, plus 1 for gridline
1573 // So this should depend on rRoot.GetCharWidth(), not on font height
1575 tools::Long nFontHt
= rRoot
.GetFontBuffer().GetAppFontData().mnHeight
;
1576 return XclTools::GetXclDefColWidthCorrection(nFontHt
) / 256.0;
1581 XclExpDefcolwidth::XclExpDefcolwidth( const XclExpRoot
& rRoot
) :
1582 XclExpDoubleRecord(EXC_ID_DEFCOLWIDTH
, EXC_DEFCOLWIDTH_DEF
+ lclGetCChCorrection(rRoot
)),
1587 bool XclExpDefcolwidth::IsDefWidth( sal_uInt16 nXclColWidth
) const
1589 // This formula is taking number of characters with GetValue()
1590 // and it is translating it into default column width.
1591 // https://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.column.aspx
1592 double defaultColumnWidth
= 256.0 * GetValue();
1594 // exactly matched, if difference is less than 1/16 of a character to the left or to the right
1595 return std::abs(defaultColumnWidth
- nXclColWidth
) < 256.0 * 1.0 / 16.0;
1598 void XclExpDefcolwidth::SetDefWidth( sal_uInt16 nXclColWidth
, bool bXLS
)
1600 double fCCh
= nXclColWidth
/ 256.0;
1603 const double fCorrection
= lclGetCChCorrection(GetRoot());
1604 const double fCorrectedCCh
= fCCh
- fCorrection
;
1605 // Now get the value which would be stored in XLS DefColWidth struct
1606 double fCChRound
= std::round(fCorrectedCCh
);
1607 // If default width was set to a value that is not representable as integral CCh between 0
1608 // and 255, then just ignore that value, and use an arbitrary default. That way, the stored
1609 // default might not represent the most used column width (or any used column width), but
1610 // that's OK, and it just means that those columns will explicitly store their width in
1611 // 1/256ths of char, and have fUserSet in their ColInfo records.
1612 if (fCChRound
< 0.0 || fCChRound
> 255.0 || std::abs(fCChRound
- fCorrectedCCh
) > 1.0 / 512)
1614 fCCh
= fCChRound
+ fCorrection
;
1620 void XclExpDefcolwidth::Save(XclExpStream
& rStrm
)
1622 double fCorrectedCCh
= GetValue() - lclGetCChCorrection(GetRoot());
1623 // Convert double to sal_uInt16
1624 XclExpUInt16Record
aUInt16Rec(GetRecId(),
1625 static_cast<sal_uInt16
>(std::round(fCorrectedCCh
)));
1626 aUInt16Rec
.Save(rStrm
);
1629 XclExpColinfo::XclExpColinfo( const XclExpRoot
& rRoot
,
1630 SCCOL nScCol
, SCROW nLastScRow
, XclExpColOutlineBuffer
& rOutlineBfr
) :
1631 XclExpRecord( EXC_ID_COLINFO
, 12 ),
1632 XclExpRoot( rRoot
),
1633 mbCustomWidth( false ),
1637 mnOutlineLevel( 0 ),
1638 mnFirstXclCol( static_cast< sal_uInt16
>( nScCol
) ),
1639 mnLastXclCol( static_cast< sal_uInt16
>( nScCol
) )
1641 ScDocument
& rDoc
= GetDoc();
1642 SCTAB nScTab
= GetCurrScTab();
1644 // column default format
1645 maXFId
.mnXFId
= GetXFBuffer().Insert(
1646 rDoc
.GetMostUsedPattern( nScCol
, 0, nLastScRow
, nScTab
), GetDefApiScript() );
1648 // column width. If column is hidden then we should return real value (not zero)
1649 sal_uInt16 nScWidth
= rDoc
.GetColWidth( nScCol
, nScTab
, false );
1650 mnWidth
= XclTools::GetXclColumnWidth( nScWidth
, GetCharWidth() );
1651 mnScWidth
= convertTwipToMm100(nScWidth
);
1654 ::set_flag( mnFlags
, EXC_COLINFO_HIDDEN
, rDoc
.ColHidden(nScCol
, nScTab
) );
1657 rOutlineBfr
.Update( nScCol
);
1658 ::set_flag( mnFlags
, EXC_COLINFO_COLLAPSED
, rOutlineBfr
.IsCollapsed() );
1659 ::insert_value( mnFlags
, rOutlineBfr
.GetLevel(), 8, 3 );
1660 mnOutlineLevel
= rOutlineBfr
.GetLevel();
1663 void XclExpColinfo::ConvertXFIndexes()
1665 maXFId
.ConvertXFIndex( GetRoot() );
1668 bool XclExpColinfo::IsDefault( const XclExpDefcolwidth
& rDefColWidth
)
1670 mbCustomWidth
= !rDefColWidth
.IsDefWidth(mnWidth
);
1671 return (maXFId
.mnXFIndex
== EXC_XF_DEFAULTCELL
) &&
1673 (mnOutlineLevel
== 0) &&
1677 bool XclExpColinfo::TryMerge( const XclExpColinfo
& rColInfo
)
1679 if( (maXFId
.mnXFIndex
== rColInfo
.maXFId
.mnXFIndex
) &&
1680 (mnWidth
== rColInfo
.mnWidth
) &&
1681 (mnFlags
== rColInfo
.mnFlags
) &&
1682 (mnOutlineLevel
== rColInfo
.mnOutlineLevel
) &&
1683 (mnLastXclCol
+ 1 == rColInfo
.mnFirstXclCol
) )
1685 mnLastXclCol
= rColInfo
.mnLastXclCol
;
1691 void XclExpColinfo::WriteBody( XclExpStream
& rStrm
)
1693 // if last column is equal to last possible column, Excel adds one more
1694 sal_uInt16 nLastXclCol
= mnLastXclCol
;
1695 if( nLastXclCol
== static_cast< sal_uInt16
>( rStrm
.GetRoot().GetMaxPos().Col() ) )
1698 rStrm
<< mnFirstXclCol
1706 void XclExpColinfo::SaveXml( XclExpXmlStream
& rStrm
)
1708 const double nExcelColumnWidth
= mnScWidth
/ static_cast< double >( convertTwipToMm100( GetCharWidth() ) );
1710 // tdf#101363 In MS specification the output value is set with double precision after delimiter:
1711 // =Truncate(({width in pixels} - 5)/{Maximum Digit Width} * 100 + 0.5)/100
1712 // Explanation of magic numbers:
1713 // 5 number - are 4 pixels of margin padding (two on each side), plus 1 pixel padding for the gridlines.
1714 // It is unknown if it should be applied during LibreOffice export
1715 // 100 number - used to limit precision to 0.01 with formula =Truncate( {value}*100+0.5 ) / 100
1716 // 0.5 number (0.005 to output value) - used to increase value before truncating,
1717 // to avoid situation when 2.997 will be truncated to 2.99 and not to 3.00
1718 const double nTruncatedExcelColumnWidth
= std::trunc( nExcelColumnWidth
* 100.0 + 0.5 ) / 100.0;
1719 rStrm
.GetCurrentStream()->singleElement( XML_col
,
1720 // OOXTODO: XML_bestFit,
1721 XML_collapsed
, ToPsz( ::get_flag( mnFlags
, EXC_COLINFO_COLLAPSED
) ),
1722 XML_customWidth
, ToPsz( mbCustomWidth
),
1723 XML_hidden
, ToPsz( ::get_flag( mnFlags
, EXC_COLINFO_HIDDEN
) ),
1724 XML_outlineLevel
, OString::number(mnOutlineLevel
),
1725 XML_max
, OString::number(mnLastXclCol
+ 1),
1726 XML_min
, OString::number(mnFirstXclCol
+ 1),
1727 // OOXTODO: XML_phonetic,
1728 XML_style
, lcl_GetStyleId(rStrm
, maXFId
.mnXFIndex
),
1729 XML_width
, OString::number(nTruncatedExcelColumnWidth
) );
1732 XclExpColinfoBuffer::XclExpColinfoBuffer( const XclExpRoot
& rRoot
) :
1733 XclExpRoot( rRoot
),
1734 maDefcolwidth( rRoot
),
1735 maOutlineBfr( rRoot
),
1736 mnHighestOutlineLevel( 0 )
1740 void XclExpColinfoBuffer::Initialize( SCROW nLastScRow
)
1743 for( sal_uInt16 nScCol
= 0, nLastScCol
= GetMaxPos().Col(); nScCol
<= nLastScCol
; ++nScCol
)
1745 maColInfos
.AppendNewRecord( new XclExpColinfo( GetRoot(), nScCol
, nLastScRow
, maOutlineBfr
) );
1746 if( maOutlineBfr
.GetLevel() > mnHighestOutlineLevel
)
1748 mnHighestOutlineLevel
= maOutlineBfr
.GetLevel();
1753 void XclExpColinfoBuffer::Finalize( ScfUInt16Vec
& rXFIndexes
, bool bXLS
)
1756 rXFIndexes
.reserve( maColInfos
.GetSize() );
1758 if( !maColInfos
.IsEmpty())
1760 XclExpColinfo
* xPrevRec
= maColInfos
.GetRecord( 0 );
1761 xPrevRec
->ConvertXFIndexes();
1762 for( size_t nPos
= 1; nPos
< maColInfos
.GetSize(); ++nPos
)
1764 XclExpColinfo
* xRec
= maColInfos
.GetRecord( nPos
);
1765 xRec
->ConvertXFIndexes();
1767 // try to merge with previous record
1768 if( xPrevRec
->TryMerge( *xRec
) )
1769 maColInfos
.InvalidateRecord( nPos
);
1773 maColInfos
.RemoveInvalidatedRecords();
1776 // put XF indexes into passed vector, collect use count of all different widths
1777 std::unordered_map
< sal_uInt16
, sal_uInt16
> aWidthMap
;
1778 sal_uInt16 nMaxColCount
= 0;
1779 sal_uInt16 nMaxUsedWidth
= 0;
1780 for( size_t nPos
= 0; nPos
< maColInfos
.GetSize(); ++nPos
)
1782 const XclExpColinfo
* xRec
= maColInfos
.GetRecord( nPos
);
1783 sal_uInt16 nColCount
= xRec
->GetColCount();
1785 // add XF index to passed vector
1786 rXFIndexes
.resize( rXFIndexes
.size() + nColCount
, xRec
->GetXFIndex() );
1788 // collect use count of column width
1789 sal_uInt16 nWidth
= xRec
->GetColWidth();
1790 sal_uInt16
& rnMapCount
= aWidthMap
[ nWidth
];
1791 rnMapCount
= rnMapCount
+ nColCount
;
1792 if( rnMapCount
> nMaxColCount
)
1794 nMaxColCount
= rnMapCount
;
1795 nMaxUsedWidth
= nWidth
;
1798 maDefcolwidth
.SetDefWidth( nMaxUsedWidth
, bXLS
);
1800 // remove all default COLINFO records
1801 for( size_t nPos
= 0; nPos
< maColInfos
.GetSize(); ++nPos
)
1803 XclExpColinfo
* xRec
= maColInfos
.GetRecord( nPos
);
1804 if( xRec
->IsDefault( maDefcolwidth
) )
1805 maColInfos
.InvalidateRecord( nPos
);
1807 maColInfos
.RemoveInvalidatedRecords();
1810 void XclExpColinfoBuffer::Save( XclExpStream
& rStrm
)
1813 maDefcolwidth
.Save( rStrm
);
1815 maColInfos
.Save( rStrm
);
1818 void XclExpColinfoBuffer::SaveXml( XclExpXmlStream
& rStrm
)
1820 if( maColInfos
.IsEmpty() )
1823 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
1824 rWorksheet
->startElement(XML_cols
);
1825 maColInfos
.SaveXml( rStrm
);
1826 rWorksheet
->endElement( XML_cols
);
1829 XclExpDefaultRowData::XclExpDefaultRowData() :
1830 mnFlags( EXC_DEFROW_DEFAULTFLAGS
),
1831 mnHeight( EXC_DEFROW_DEFAULTHEIGHT
)
1835 XclExpDefaultRowData::XclExpDefaultRowData( const XclExpRow
& rRow
) :
1836 mnFlags( EXC_DEFROW_DEFAULTFLAGS
),
1837 mnHeight( rRow
.GetHeight() )
1839 ::set_flag( mnFlags
, EXC_DEFROW_HIDDEN
, rRow
.IsHidden() );
1840 ::set_flag( mnFlags
, EXC_DEFROW_UNSYNCED
, rRow
.IsUnsynced() );
1843 static bool operator<( const XclExpDefaultRowData
& rLeft
, const XclExpDefaultRowData
& rRight
)
1845 return (rLeft
.mnHeight
< rRight
.mnHeight
) ||
1846 ((rLeft
.mnHeight
== rRight
.mnHeight
) && (rLeft
.mnFlags
< rRight
.mnFlags
));
1849 XclExpDefrowheight::XclExpDefrowheight() :
1850 XclExpRecord( EXC_ID3_DEFROWHEIGHT
, 4 )
1854 void XclExpDefrowheight::SetDefaultData( const XclExpDefaultRowData
& rDefData
)
1856 maDefData
= rDefData
;
1859 void XclExpDefrowheight::WriteBody( XclExpStream
& rStrm
)
1861 OSL_ENSURE_BIFF( rStrm
.GetRoot().GetBiff() >= EXC_BIFF3
);
1862 rStrm
<< maDefData
.mnFlags
<< maDefData
.mnHeight
;
1865 XclExpRow::XclExpRow( const XclExpRoot
& rRoot
, sal_uInt32 nXclRow
,
1866 XclExpRowOutlineBuffer
& rOutlineBfr
, bool bAlwaysEmpty
, bool bHidden
, sal_uInt16 nHeight
) :
1867 XclExpRecord( EXC_ID3_ROW
, 16 ),
1868 XclExpRoot( rRoot
),
1869 mnXclRow( nXclRow
),
1870 mnHeight( nHeight
),
1871 mnFlags( EXC_ROW_DEFAULTFLAGS
),
1872 mnXFIndex( EXC_XF_DEFAULTCELL
),
1873 mnOutlineLevel( 0 ),
1875 mnCurrentRow( nXclRow
),
1876 mbAlwaysEmpty( bAlwaysEmpty
),
1879 SCTAB nScTab
= GetCurrScTab();
1880 SCROW nScRow
= static_cast< SCROW
>( mnXclRow
);
1882 // *** Row flags *** ------------------------------------------------------
1884 CRFlags nRowFlags
= GetDoc().GetRowFlags( nScRow
, nScTab
);
1885 bool bUserHeight( nRowFlags
& CRFlags::ManualSize
);
1886 ::set_flag( mnFlags
, EXC_ROW_UNSYNCED
, bUserHeight
);
1887 ::set_flag( mnFlags
, EXC_ROW_HIDDEN
, bHidden
);
1889 // *** Outline data *** ---------------------------------------------------
1891 rOutlineBfr
.Update( nScRow
);
1892 ::set_flag( mnFlags
, EXC_ROW_COLLAPSED
, rOutlineBfr
.IsCollapsed() );
1893 ::insert_value( mnFlags
, rOutlineBfr
.GetLevel(), 0, 3 );
1894 mnOutlineLevel
= rOutlineBfr
.GetLevel();
1896 // *** Progress bar *** ---------------------------------------------------
1898 XclExpProgressBar
& rProgress
= GetProgressBar();
1899 rProgress
.IncRowRecordCount();
1900 rProgress
.Progress();
1903 static size_t findFirstAllSameUntilEnd( const ScfUInt16Vec
& rIndexes
, sal_uInt16 value
,
1904 size_t searchStart
= std::numeric_limits
<size_t>::max())
1906 for( size_t i
= std::min(rIndexes
.size(), searchStart
); i
>= 1; --i
)
1908 if( rIndexes
[ i
- 1 ] != value
)
1914 void XclExpRow::AppendCell( XclExpCellRef
const & xCell
, bool bIsMergedBase
)
1916 OSL_ENSURE( !mbAlwaysEmpty
, "XclExpRow::AppendCell - row is marked to be always empty" );
1917 // try to merge with last existing cell
1918 InsertCell( xCell
, maCellList
.GetSize(), bIsMergedBase
);
1921 void XclExpRow::Finalize( const ScfUInt16Vec
& rColXFIndexes
, ScfUInt16Vec
& aXFIndexes
, size_t nStartColAllDefault
, bool bProgress
)
1925 // *** Convert XF identifiers *** -----------------------------------------
1927 // additionally collect the blank XF indexes
1928 size_t nColCount
= GetMaxPos().Col() + 1;
1929 OSL_ENSURE( rColXFIndexes
.size() == nColCount
, "XclExpRow::Finalize - wrong column XF index count" );
1931 // The vector should be preset to all items being EXC_XF_NOTFOUND, to avoid repeated allocations
1933 assert( aXFIndexes
.size() == nColCount
);
1934 assert( aXFIndexes
.front() == EXC_XF_NOTFOUND
);
1935 assert( aXFIndexes
.back() == EXC_XF_NOTFOUND
);
1936 for( nPos
= 0, nSize
= maCellList
.GetSize(); nPos
< nSize
; ++nPos
)
1938 XclExpCellBase
* pCell
= maCellList
.GetRecord( nPos
);
1939 pCell
->ConvertXFIndexes( GetRoot() );
1940 pCell
->GetBlankXFIndexes( aXFIndexes
);
1943 // *** Fill gaps with BLANK/MULBLANK cell records *** ---------------------
1945 /* This is needed because nonexistent cells in Calc are not formatted at all,
1946 but in Excel they would have the column default format. Blank cells that
1947 are equal to the respective column default are removed later in this function. */
1948 if( !mbAlwaysEmpty
)
1950 // XF identifier representing default cell XF
1951 XclExpMultiXFId
aXFId( XclExpXFBuffer::GetDefCellXFId() );
1952 aXFId
.ConvertXFIndex( GetRoot() );
1955 while( nPos
<= maCellList
.GetSize() ) // don't cache list size, may change in the loop
1957 // get column index that follows previous cell
1958 sal_uInt16 nFirstFreeXclCol
= (nPos
> 0) ? (maCellList
.GetRecord( nPos
- 1 )->GetLastXclCol() + 1) : 0;
1959 // get own column index
1960 sal_uInt16 nNextUsedXclCol
= (nPos
< maCellList
.GetSize()) ? maCellList
.GetRecord( nPos
)->GetXclCol() : (GetMaxPos().Col() + 1);
1963 if( nFirstFreeXclCol
< nNextUsedXclCol
)
1965 aXFId
.mnCount
= nNextUsedXclCol
- nFirstFreeXclCol
;
1966 XclExpCellRef xNewCell
= new XclExpBlankCell( XclAddress( nFirstFreeXclCol
, mnXclRow
), aXFId
);
1967 // insert the cell, InsertCell() may merge it with existing BLANK records
1968 InsertCell( xNewCell
, nPos
, false );
1969 // insert default XF indexes into aXFIndexes
1970 for( size_t i
= nFirstFreeXclCol
; i
< nNextUsedXclCol
; ++i
)
1971 aXFIndexes
[ i
] = aXFId
.mnXFIndex
;
1972 // don't step forward with nPos, InsertCell() may remove records
1979 // *** Find default row format *** ----------------------------------------
1981 // Often there will be many EXC_XF_DEFAULTCELL at the end, optimize by ignoring them.
1982 size_t nStartSearchAllDefault
= aXFIndexes
.size();
1983 if( !maCellList
.IsEmpty() && dynamic_cast< const XclExpBlankCell
* >( maCellList
.GetLastRecord()))
1985 const XclExpBlankCell
* pLastBlank
= static_cast< const XclExpBlankCell
* >( maCellList
.GetLastRecord());
1986 assert(pLastBlank
->GetLastXclCol() == aXFIndexes
.size() - 1);
1987 nStartSearchAllDefault
= pLastBlank
->GetStartColAllDefaultCell();
1989 size_t nStartAllDefault
= findFirstAllSameUntilEnd( aXFIndexes
, EXC_XF_DEFAULTCELL
, nStartSearchAllDefault
);
1991 // find most used XF index in the row
1992 sal_uInt16 nRowXFIndex
= EXC_XF_DEFAULTCELL
;
1993 const size_t nHalfIndexes
= aXFIndexes
.size() / 2;
1994 if( nStartAllDefault
> nHalfIndexes
) // Otherwise most are EXC_XF_DEFAULTCELL.
1996 // Very likely the most common one is going to be the last one.
1997 nRowXFIndex
= aXFIndexes
.back();
1998 size_t nStartLastSame
= findFirstAllSameUntilEnd( aXFIndexes
, nRowXFIndex
);
1999 if( nStartLastSame
> nHalfIndexes
) // No, find out the most used one by counting.
2001 std::unordered_map
< sal_uInt16
, size_t > aIndexMap
;
2002 size_t nMaxXFCount
= 0;
2003 for( const auto& rXFIndex
: aXFIndexes
)
2005 if( rXFIndex
!= EXC_XF_NOTFOUND
)
2007 size_t& rnCount
= aIndexMap
[ rXFIndex
];
2009 if( rnCount
> nMaxXFCount
)
2011 nRowXFIndex
= rXFIndex
;
2012 nMaxXFCount
= rnCount
;
2013 if (nMaxXFCount
> nHalfIndexes
)
2015 // No other XF index can have a greater usage count, we
2016 // don't need to loop through the remaining cells.
2017 // Specifically for the tail of unused default
2018 // cells/columns this makes a difference.
2027 // decide whether to use the row default XF index or column default XF indexes
2028 bool bUseColDefXFs
= nRowXFIndex
== EXC_XF_DEFAULTCELL
;
2029 if( !bUseColDefXFs
)
2031 // count needed XF indexes for blank cells with and without row default XF index
2032 size_t nXFCountWithRowDefXF
= 0;
2033 size_t nXFCountWithoutRowDefXF
= 0;
2034 ScfUInt16Vec::const_iterator aColIt
= rColXFIndexes
.begin();
2035 for( const auto& rXFIndex
: aXFIndexes
)
2037 sal_uInt16 nXFIndex
= rXFIndex
;
2038 if( nXFIndex
!= EXC_XF_NOTFOUND
)
2040 if( nXFIndex
!= nRowXFIndex
)
2041 ++nXFCountWithRowDefXF
; // with row default XF index
2042 if( nXFIndex
!= *aColIt
)
2043 ++nXFCountWithoutRowDefXF
; // without row default XF index
2048 // use column XF indexes if this would cause less or equal number of BLANK records
2049 bUseColDefXFs
= nXFCountWithoutRowDefXF
<= nXFCountWithRowDefXF
;
2052 // *** Remove unused BLANK cell records *** -------------------------------
2054 size_t maxStartAllNotFound
;
2057 size_t maxStartAllDefault
= std::max( nStartAllDefault
, nStartColAllDefault
);
2058 // use column default XF indexes
2059 // #i194#: remove cell XF indexes equal to column default XF indexes
2060 for( size_t i
= 0; i
< maxStartAllDefault
; ++i
)
2062 if( aXFIndexes
[ i
] == rColXFIndexes
[ i
] )
2063 aXFIndexes
[ i
] = EXC_XF_NOTFOUND
;
2065 // They can differ only up to maxNonDefault, in the rest they are the same.
2066 for( size_t i
= maxStartAllDefault
; i
< aXFIndexes
.size(); ++i
)
2067 aXFIndexes
[ i
] = EXC_XF_NOTFOUND
;
2068 maxStartAllNotFound
= maxStartAllDefault
;
2072 // use row default XF index
2073 mnXFIndex
= nRowXFIndex
;
2074 ::set_flag( mnFlags
, EXC_ROW_USEDEFXF
);
2075 // #98133#, #i194#, #i27407#: remove cell XF indexes equal to row default XF index
2076 for( auto& rXFIndex
: aXFIndexes
)
2077 if( rXFIndex
== nRowXFIndex
)
2078 rXFIndex
= EXC_XF_NOTFOUND
;
2079 maxStartAllNotFound
= aXFIndexes
.size();
2082 // remove unused parts of BLANK/MULBLANK cell records
2083 size_t nStartAllNotFound
= findFirstAllSameUntilEnd( aXFIndexes
, EXC_XF_NOTFOUND
, maxStartAllNotFound
);
2085 while( nPos
< maCellList
.GetSize() ) // do not cache list size, may change in the loop
2087 XclExpCellBase
* xCell
= maCellList
.GetRecord( nPos
);
2088 xCell
->RemoveUnusedBlankCells( aXFIndexes
, nStartAllNotFound
);
2089 if( xCell
->IsEmpty() )
2090 maCellList
.RemoveRecord( nPos
);
2094 // Ensure it's all EXC_XF_NOTFOUND again for next reuse.
2095 for( size_t i
= 0; i
< nStartAllNotFound
; ++i
)
2096 aXFIndexes
[ i
] = EXC_XF_NOTFOUND
;
2098 // progress bar includes disabled rows; only update it in the lead thread.
2100 GetProgressBar().Progress();
2102 sal_uInt16
XclExpRow::GetFirstUsedXclCol() const
2104 return maCellList
.IsEmpty() ? 0 : maCellList
.GetFirstRecord()->GetXclCol();
2107 sal_uInt16
XclExpRow::GetFirstFreeXclCol() const
2109 return maCellList
.IsEmpty() ? 0 : (maCellList
.GetLastRecord()->GetLastXclCol() + 1);
2112 bool XclExpRow::IsDefaultable() const
2114 const sal_uInt16 nFlagsAlwaysMarkedAsDefault
= EXC_ROW_DEFAULTFLAGS
| EXC_ROW_HIDDEN
| EXC_ROW_UNSYNCED
;
2115 return !::get_flag( mnFlags
, static_cast< sal_uInt16
>( ~nFlagsAlwaysMarkedAsDefault
) ) &&
2119 void XclExpRow::DisableIfDefault( const XclExpDefaultRowData
& rDefRowData
)
2121 mbEnabled
= !IsDefaultable() ||
2122 (mnHeight
!= rDefRowData
.mnHeight
) ||
2123 (IsHidden() != rDefRowData
.IsHidden()) ||
2124 (IsUnsynced() != rDefRowData
.IsUnsynced());
2127 void XclExpRow::WriteCellList( XclExpStream
& rStrm
)
2129 OSL_ENSURE( mbEnabled
|| maCellList
.IsEmpty(), "XclExpRow::WriteCellList - cells in disabled row" );
2130 maCellList
.Save( rStrm
);
2133 void XclExpRow::Save( XclExpStream
& rStrm
)
2137 mnCurrentRow
= mnXclRow
;
2138 for ( sal_uInt32 i
= 0; i
< mnXclRowRpt
; ++i
, ++mnCurrentRow
)
2139 XclExpRecord::Save( rStrm
);
2143 void XclExpRow::InsertCell( XclExpCellRef xCell
, size_t nPos
, bool bIsMergedBase
)
2145 OSL_ENSURE( xCell
, "XclExpRow::InsertCell - missing cell" );
2147 /* If we have a multi-line text in a merged cell, and the resulting
2148 row height has not been confirmed, we need to force the EXC_ROW_UNSYNCED
2149 flag to be true to ensure Excel works correctly. */
2150 if( bIsMergedBase
&& xCell
->IsMultiLineText() )
2151 ::set_flag( mnFlags
, EXC_ROW_UNSYNCED
);
2153 // try to merge with previous cell, insert the new cell if not successful
2154 XclExpCellBase
* xPrevCell
= maCellList
.GetRecord( nPos
- 1 );
2155 if( xPrevCell
&& xPrevCell
->TryMerge( *xCell
) )
2158 maCellList
.InsertRecord( xCell
, nPos
++ );
2159 // nPos points now to following cell
2161 // try to merge with following cell, remove it if successful
2162 XclExpCellRef xNextCell
= maCellList
.GetRecord( nPos
);
2163 if( xNextCell
&& xCell
->TryMerge( *xNextCell
) )
2164 maCellList
.RemoveRecord( nPos
);
2167 void XclExpRow::WriteBody( XclExpStream
& rStrm
)
2169 rStrm
<< static_cast< sal_uInt16
>(mnCurrentRow
)
2170 << GetFirstUsedXclCol()
2171 << GetFirstFreeXclCol()
2178 void XclExpRow::SaveXml( XclExpXmlStream
& rStrm
)
2182 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
2183 bool haveFormat
= ::get_flag( mnFlags
, EXC_ROW_USEDEFXF
);
2184 mnCurrentRow
= mnXclRow
+ 1;
2185 for ( sal_uInt32 i
=0; i
<mnXclRowRpt
; ++i
)
2187 rWorksheet
->startElement( XML_row
,
2188 XML_r
, OString::number(mnCurrentRow
++),
2189 // OOXTODO: XML_spans, optional
2190 XML_s
, haveFormat
? lcl_GetStyleId( rStrm
, mnXFIndex
).getStr() : nullptr,
2191 XML_customFormat
, ToPsz( haveFormat
),
2192 XML_ht
, OString::number(static_cast<double>(mnHeight
) / 20.0),
2193 XML_hidden
, ToPsz( ::get_flag( mnFlags
, EXC_ROW_HIDDEN
) ),
2194 XML_customHeight
, ToPsz( ::get_flag( mnFlags
, EXC_ROW_UNSYNCED
) ),
2195 XML_outlineLevel
, OString::number(mnOutlineLevel
),
2196 XML_collapsed
, ToPsz( ::get_flag( mnFlags
, EXC_ROW_COLLAPSED
) )
2197 // OOXTODO: XML_thickTop, bool
2198 // OOXTODO: XML_thickBot, bool
2199 // OOXTODO: XML_ph, bool
2201 // OOXTODO: XML_extLst
2202 maCellList
.SaveXml( rStrm
);
2203 rWorksheet
->endElement( XML_row
);
2207 XclExpRowBuffer::XclExpRowBuffer( const XclExpRoot
& rRoot
) :
2208 XclExpRoot( rRoot
),
2209 maOutlineBfr( rRoot
),
2210 maDimensions( rRoot
),
2211 mnHighestOutlineLevel( 0 )
2215 void XclExpRowBuffer::AppendCell( XclExpCellRef
const & xCell
, bool bIsMergedBase
)
2217 OSL_ENSURE( xCell
, "XclExpRowBuffer::AppendCell - missing cell" );
2218 GetOrCreateRow( xCell
->GetXclRow(), false ).AppendCell( xCell
, bIsMergedBase
);
2221 void XclExpRowBuffer::CreateRows( SCROW nFirstFreeScRow
)
2223 if( nFirstFreeScRow
> 0 )
2224 GetOrCreateRow( ::std::max ( nFirstFreeScRow
- 1, GetMaxPos().Row() ), true );
2229 class RowFinalizeTask
: public comphelper::ThreadTask
2232 const ScfUInt16Vec
& mrColXFIndexes
;
2233 size_t mnStartColAllDefault
;
2234 std::vector
< XclExpRow
* > maRows
;
2236 RowFinalizeTask( const std::shared_ptr
<comphelper::ThreadTaskTag
> & pTag
,
2237 const ScfUInt16Vec
& rColXFIndexes
,
2238 size_t nStartColAllDefault
,
2240 comphelper::ThreadTask( pTag
),
2241 mbProgress( bProgress
),
2242 mrColXFIndexes( rColXFIndexes
),
2243 mnStartColAllDefault( nStartColAllDefault
)
2246 void push_back( XclExpRow
*pRow
) { maRows
.push_back( pRow
); }
2247 virtual void doWork() override
2249 ScfUInt16Vec
aXFIndexes( mrColXFIndexes
.size(), EXC_XF_NOTFOUND
);
2250 for (XclExpRow
* p
: maRows
)
2251 p
->Finalize( mrColXFIndexes
, aXFIndexes
, mnStartColAllDefault
, mbProgress
);
2257 void XclExpRowBuffer::Finalize( XclExpDefaultRowData
& rDefRowData
,
2258 const ScfUInt16Vec
& rColXFIndexes
,
2259 size_t nStartColAllDefault
)
2261 // *** Finalize all rows *** ----------------------------------------------
2263 GetProgressBar().ActivateFinalRowsSegment();
2266 // This is staggeringly slow, and each element operates only
2268 const size_t nRows
= maRowMap
.size();
2269 const size_t nThreads
= nRows
< 128 ? 1 : comphelper::ThreadPool::getPreferredConcurrency();
2271 const size_t nThreads
= 1; // globally disable multi-threading for now.
2275 ScfUInt16Vec
aXFIndexes( rColXFIndexes
.size(), EXC_XF_NOTFOUND
);
2276 for (auto& rEntry
: maRowMap
)
2277 rEntry
.second
->Finalize( rColXFIndexes
, aXFIndexes
, nStartColAllDefault
, true );
2281 comphelper::ThreadPool
&rPool
= comphelper::ThreadPool::getSharedOptimalPool();
2282 std::shared_ptr
<comphelper::ThreadTaskTag
> pTag
= comphelper::ThreadPool::createThreadTaskTag();
2283 std::vector
<std::unique_ptr
<RowFinalizeTask
>> aTasks(nThreads
);
2284 for ( size_t i
= 0; i
< nThreads
; i
++ )
2285 aTasks
[ i
].reset( new RowFinalizeTask( pTag
, rColXFIndexes
, nStartColAllDefault
, i
== 0 ) );
2288 for ( const auto& rEntry
: maRowMap
)
2290 aTasks
[ nIdx
% nThreads
]->push_back( rEntry
.second
.get() );
2294 for ( size_t i
= 1; i
< nThreads
; i
++ )
2295 rPool
.pushTask( std::move(aTasks
[ i
]) );
2297 // Progress bar updates must be synchronous to avoid deadlock
2298 aTasks
[0]->doWork();
2300 rPool
.waitUntilDone(pTag
);
2303 // *** Default row format *** ---------------------------------------------
2305 std::map
< XclExpDefaultRowData
, size_t > aDefRowMap
;
2307 XclExpDefaultRowData aMaxDefData
;
2308 size_t nMaxDefCount
= 0;
2309 // only look for default format in existing rows, if there are more than unused
2310 // if the row is hidden, then row xml must be created even if it not contain cells
2311 XclExpRow
* pPrev
= nullptr;
2312 std::vector
< XclExpRow
* > aRepeated
;
2313 for (const auto& rEntry
: maRowMap
)
2315 const RowRef
& rRow
= rEntry
.second
;
2316 if ( rRow
->IsDefaultable() )
2318 XclExpDefaultRowData
aDefData( *rRow
);
2319 size_t& rnDefCount
= aDefRowMap
[ aDefData
];
2321 if( rnDefCount
> nMaxDefCount
)
2323 nMaxDefCount
= rnDefCount
;
2324 aMaxDefData
= aDefData
;
2329 if ( pPrev
->IsDefaultable() )
2331 // if the previous row we processed is not
2332 // defaultable then afaict the rows in between are
2333 // not used ( and not repeatable )
2334 sal_uInt32 nRpt
= rRow
->GetXclRow() - pPrev
->GetXclRow();
2336 aRepeated
.push_back( pPrev
);
2337 pPrev
->SetXclRowRpt( nRpt
);
2338 XclExpDefaultRowData
aDefData( *pPrev
);
2339 size_t& rnDefCount
= aDefRowMap
[ aDefData
];
2340 rnDefCount
+= ( pPrev
->GetXclRowRpt() - 1 );
2341 if( rnDefCount
> nMaxDefCount
)
2343 nMaxDefCount
= rnDefCount
;
2344 aMaxDefData
= aDefData
;
2350 // return the default row format to caller
2351 rDefRowData
= aMaxDefData
;
2353 // now disable repeating extra (empty) rows that are equal to the default row
2354 for (auto& rpRow
: aRepeated
)
2356 if ( rpRow
->GetXclRowRpt() > 1
2357 && rpRow
->GetHeight() == rDefRowData
.mnHeight
2358 && rpRow
->IsHidden() == rDefRowData
.IsHidden() )
2360 rpRow
->SetXclRowRpt( 1 );
2364 // *** Disable unused ROW records, find used area *** ---------------------
2366 sal_uInt16 nFirstUsedXclCol
= SAL_MAX_UINT16
;
2367 sal_uInt16 nFirstFreeXclCol
= 0;
2368 sal_uInt32 nFirstUsedXclRow
= SAL_MAX_UINT32
;
2369 sal_uInt32 nFirstFreeXclRow
= 0;
2371 for (const auto& rEntry
: maRowMap
)
2373 const RowRef
& rRow
= rEntry
.second
;
2374 // disable unused rows
2375 rRow
->DisableIfDefault( aMaxDefData
);
2377 // find used column range
2378 if( !rRow
->IsEmpty() ) // empty rows return (0...0) as used range
2380 nFirstUsedXclCol
= ::std::min( nFirstUsedXclCol
, rRow
->GetFirstUsedXclCol() );
2381 nFirstFreeXclCol
= ::std::max( nFirstFreeXclCol
, rRow
->GetFirstFreeXclCol() );
2384 // find used row range
2385 if( rRow
->IsEnabled() )
2387 sal_uInt32 nXclRow
= rRow
->GetXclRow();
2388 nFirstUsedXclRow
= ::std::min
< sal_uInt32
>( nFirstUsedXclRow
, nXclRow
);
2389 nFirstFreeXclRow
= ::std::max
< sal_uInt32
>( nFirstFreeXclRow
, nXclRow
+ 1 );
2393 // adjust start position, if there are no or only empty/disabled ROW records
2394 nFirstUsedXclCol
= ::std::min( nFirstUsedXclCol
, nFirstFreeXclCol
);
2395 nFirstUsedXclRow
= ::std::min( nFirstUsedXclRow
, nFirstFreeXclRow
);
2397 // initialize the DIMENSIONS record
2398 maDimensions
.SetDimensions(
2399 nFirstUsedXclCol
, nFirstUsedXclRow
, nFirstFreeXclCol
, nFirstFreeXclRow
);
2402 void XclExpRowBuffer::Save( XclExpStream
& rStrm
)
2404 // DIMENSIONS record
2405 maDimensions
.Save( rStrm
);
2407 // save in blocks of 32 rows, each block contains first all ROWs, then all cells
2408 size_t nSize
= maRowMap
.size();
2409 RowMap::iterator itr
= maRowMap
.begin(), itrEnd
= maRowMap
.end();
2410 RowMap::iterator itrBlkStart
= maRowMap
.begin(), itrBlkEnd
= maRowMap
.begin();
2411 sal_uInt16 nStartXclRow
= (nSize
== 0) ? 0 : itr
->second
->GetXclRow();
2413 for (; itr
!= itrEnd
; ++itr
)
2415 // find end of row block
2416 itrBlkEnd
= std::find_if_not(itrBlkEnd
, itrEnd
,
2417 [&nStartXclRow
](const RowMap::value_type
& rRow
) { return rRow
.second
->GetXclRow() - nStartXclRow
< EXC_ROW_ROWBLOCKSIZE
; });
2419 // write the ROW records
2420 std::for_each(itrBlkStart
, itrBlkEnd
, [&rStrm
](const RowMap::value_type
& rRow
) { rRow
.second
->Save( rStrm
); });
2422 // write the cell records
2423 std::for_each(itrBlkStart
, itrBlkEnd
, [&rStrm
](const RowMap::value_type
& rRow
) { rRow
.second
->WriteCellList( rStrm
); });
2425 itrBlkStart
= (itrBlkEnd
== itrEnd
) ? itrBlkEnd
: itrBlkEnd
++;
2426 nStartXclRow
+= EXC_ROW_ROWBLOCKSIZE
;
2430 void XclExpRowBuffer::SaveXml( XclExpXmlStream
& rStrm
)
2432 if (std::none_of(maRowMap
.begin(), maRowMap
.end(), [](const RowMap::value_type
& rRow
) { return rRow
.second
->IsEnabled(); }))
2434 rStrm
.GetCurrentStream()->singleElement(XML_sheetData
);
2438 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
2439 rWorksheet
->startElement(XML_sheetData
);
2440 for (const auto& rEntry
: maRowMap
)
2441 rEntry
.second
->SaveXml(rStrm
);
2442 rWorksheet
->endElement( XML_sheetData
);
2445 XclExpRow
& XclExpRowBuffer::GetOrCreateRow( sal_uInt32 nXclRow
, bool bRowAlwaysEmpty
)
2447 // This is called rather often, so optimize for the most common case of saving row by row
2448 // (so the requested row is often the last one in the map or belongs after the last one).
2449 RowMap::iterator itr
;
2450 if(maRowMap
.empty())
2451 itr
= maRowMap
.end();
2454 RowMap::reverse_iterator last
= maRowMap
.rbegin();
2455 if( last
->first
== nXclRow
)
2456 return *last
->second
;
2457 if( nXclRow
> last
->first
)
2458 itr
= maRowMap
.end();
2460 itr
= maRowMap
.lower_bound( nXclRow
);
2462 const bool bFound
= itr
!= maRowMap
.end();
2463 // bFoundHigher: nXclRow was identical to the previous entry, so not explicitly created earlier
2464 const bool bFoundHigher
= bFound
&& itr
->first
!= nXclRow
;
2465 if( bFound
&& !bFoundHigher
)
2466 return *itr
->second
;
2470 if( itr
!= maRowMap
.begin() )
2473 pPrevEntry
= itr
->second
;
2477 nFrom
= itr
->first
+ 1;
2480 const ScDocument
& rDoc
= GetRoot().GetDoc();
2481 const SCTAB nScTab
= GetRoot().GetCurrScTab();
2482 // Do not repeatedly call RowHidden() / GetRowHeight() for same values.
2483 bool bHidden
= false;
2484 SCROW lastSameHiddenRow
= -1;
2485 sal_uInt16 nHeight
= 0;
2486 SCROW lastSameHeightRow
= -1;
2487 // create the missing rows first
2488 while( nFrom
<= nXclRow
)
2490 // only create RowMap entries if it is first row in spreadsheet,
2491 // if it is the desired row, or for rows that differ from previous.
2492 if( static_cast<SCROW
>(nFrom
) > lastSameHiddenRow
)
2493 bHidden
= rDoc
.RowHidden(nFrom
, nScTab
, nullptr, &lastSameHiddenRow
);
2494 // Always get the actual row height even if the manual size flag is
2495 // not set, to correctly export the heights of rows with wrapped
2497 if( static_cast<SCROW
>(nFrom
) > lastSameHeightRow
)
2498 nHeight
= rDoc
.GetRowHeight(nFrom
, nScTab
, nullptr, &lastSameHeightRow
, false);
2499 if ( !pPrevEntry
|| ( nFrom
== nXclRow
) ||
2500 ( maOutlineBfr
.IsCollapsed() ) ||
2501 ( maOutlineBfr
.GetLevel() != 0 ) ||
2502 ( bRowAlwaysEmpty
&& !pPrevEntry
->IsEmpty() ) ||
2503 ( bHidden
!= pPrevEntry
->IsHidden() ) ||
2504 ( nHeight
!= pPrevEntry
->GetHeight() ) )
2506 if( maOutlineBfr
.GetLevel() > mnHighestOutlineLevel
)
2508 mnHighestOutlineLevel
= maOutlineBfr
.GetLevel();
2510 RowRef p
= std::make_shared
<XclExpRow
>(GetRoot(), nFrom
, maOutlineBfr
, bRowAlwaysEmpty
, bHidden
, nHeight
);
2511 maRowMap
.emplace(nFrom
, p
);
2516 itr
= maRowMap
.find(nXclRow
);
2517 return *itr
->second
;
2522 XclExpCellTable::XclExpCellTable( const XclExpRoot
& rRoot
) :
2523 XclExpRoot( rRoot
),
2524 maColInfoBfr( rRoot
),
2526 maArrayBfr( rRoot
),
2527 maShrfmlaBfr( rRoot
),
2528 maTableopBfr( rRoot
),
2529 mxDefrowheight( new XclExpDefrowheight() ),
2530 mxGuts( new XclExpGuts( rRoot
) ),
2531 mxNoteList( new XclExpNoteList
),
2532 mxMergedcells( new XclExpMergedcells( rRoot
) ),
2533 mxHyperlinkList( new XclExpHyperlinkList
),
2534 mxDval( new XclExpDval( rRoot
) ),
2535 mxExtLst( new XclExtLst( rRoot
) )
2537 ScDocument
& rDoc
= GetDoc();
2538 SCTAB nScTab
= GetCurrScTab();
2539 SvNumberFormatter
& rFormatter
= GetFormatter();
2541 // maximum sheet limits
2542 SCCOL nMaxScCol
= GetMaxPos().Col();
2543 SCROW nMaxScRow
= GetMaxPos().Row();
2545 // find used area (non-empty cells)
2546 SCCOL nLastUsedScCol
;
2547 SCROW nLastUsedScRow
;
2548 rDoc
.GetTableArea( nScTab
, nLastUsedScCol
, nLastUsedScRow
);
2550 if(nLastUsedScCol
> nMaxScCol
)
2551 nLastUsedScCol
= nMaxScCol
;
2553 // check extra blank rows to avoid of losing their not default settings (workaround for tdf#41425)
2554 nLastUsedScRow
+= 1000;
2556 if(nLastUsedScRow
> nMaxScRow
)
2557 nLastUsedScRow
= nMaxScRow
;
2559 ScRange
aUsedRange( 0, 0, nScTab
, nLastUsedScCol
, nLastUsedScRow
, nScTab
);
2560 GetAddressConverter().ValidateRange( aUsedRange
, true );
2561 nLastUsedScRow
= aUsedRange
.aEnd
.Row();
2563 // first row without any set attributes (height/hidden/...)
2564 SCROW nFirstUnflaggedScRow
= rDoc
.GetLastFlaggedRow( nScTab
) + 1;
2566 // find range of outlines
2567 SCROW nFirstUngroupedScRow
= 0;
2568 if( const ScOutlineTable
* pOutlineTable
= rDoc
.GetOutlineTable( nScTab
) )
2570 SCCOLROW nScStartPos
, nScEndPos
;
2571 const ScOutlineArray
& rRowArray
= pOutlineTable
->GetRowArray();
2572 rRowArray
.GetRange( nScStartPos
, nScEndPos
);
2573 // +1 because open/close button is in next row in Excel, +1 for "end->first unused"
2574 nFirstUngroupedScRow
= static_cast< SCROW
>( nScEndPos
+ 2 );
2578 /* #i30411# Files saved with SO7/OOo1.x with nonstandard default column
2579 formatting cause big Excel files, because all rows from row 1 to row
2580 32000 are exported. Now, if the used area goes exactly to row 32000,
2581 use this row as default and ignore all rows >32000.
2582 #i59220# Tolerance of +-128 rows for inserted/removed rows. */
2583 if( (31871 <= nLastUsedScRow
) && (nLastUsedScRow
<= 32127) && (nFirstUnflaggedScRow
< nLastUsedScRow
) && (nFirstUngroupedScRow
<= nLastUsedScRow
) )
2584 nMaxScRow
= nLastUsedScRow
;
2585 maColInfoBfr
.Initialize( nMaxScRow
);
2587 // range for cell iterator
2588 SCCOL nLastIterScCol
= nMaxScCol
;
2589 SCROW nLastIterScRow
= ulimit_cast
< SCROW
>( nLastUsedScRow
, nMaxScRow
);
2590 ScUsedAreaIterator
aIt( rDoc
, nScTab
, 0, 0, nLastIterScCol
, nLastIterScRow
);
2592 // activate the correct segment and sub segment at the progress bar
2593 GetProgressBar().ActivateCreateRowsSegment();
2595 for( bool bIt
= aIt
.GetNext(); bIt
; bIt
= aIt
.GetNext() )
2597 SCCOL nScCol
= aIt
.GetStartCol();
2598 SCROW nScRow
= aIt
.GetRow();
2599 SCCOL nLastScCol
= aIt
.GetEndCol();
2600 ScAddress
aScPos( nScCol
, nScRow
, nScTab
);
2602 XclAddress
aXclPos( static_cast< sal_uInt16
>( nScCol
), static_cast< sal_uInt32
>( nScRow
) );
2603 sal_uInt16 nLastXclCol
= static_cast< sal_uInt16
>( nLastScCol
);
2605 const ScRefCellValue
& rScCell
= aIt
.GetCell();
2606 XclExpCellRef xCell
;
2608 const ScPatternAttr
* pPattern
= aIt
.GetPattern();
2610 // handle overlapped merged cells before creating the cell record
2611 sal_uInt32 nMergeBaseXFId
= EXC_XFID_NOTFOUND
;
2612 bool bIsMergedBase
= false;
2615 const SfxItemSet
& rItemSet
= pPattern
->GetItemSet();
2616 // base cell in a merged range
2617 const ScMergeAttr
& rMergeItem
= rItemSet
.Get( ATTR_MERGE
);
2618 bIsMergedBase
= rMergeItem
.IsMerged();
2619 /* overlapped cell in a merged range; in Excel all merged cells
2620 must contain same XF index, for correct border */
2621 const ScMergeFlagAttr
& rMergeFlagItem
= rItemSet
.Get( ATTR_MERGE_FLAG
);
2622 if( rMergeFlagItem
.IsOverlapped() )
2623 nMergeBaseXFId
= mxMergedcells
->GetBaseXFId( aScPos
);
2626 OUString aAddNoteText
; // additional text to be appended to a note
2628 switch (rScCell
.meType
)
2630 case CELLTYPE_VALUE
:
2632 double fValue
= rScCell
.mfValue
;
2636 OUString aUrl
= pPattern
->GetItemSet().Get(ATTR_HYPERLINK
).GetValue();
2637 if (!aUrl
.isEmpty())
2639 rtl::Reference
<XclExpHyperlink
> aLink
=
2640 new XclExpHyperlink(GetRoot(), SvxURLField(aUrl
, aUrl
), aScPos
);
2641 mxHyperlinkList
->AppendRecord(aLink
);
2645 // try to create a Boolean cell
2646 if( pPattern
&& ((fValue
== 0.0) || (fValue
== 1.0)) )
2648 sal_uInt32 nScNumFmt
= pPattern
->GetItemSet().Get( ATTR_VALUE_FORMAT
).GetValue();
2649 if( rFormatter
.GetType( nScNumFmt
) == SvNumFormatType::LOGICAL
)
2650 xCell
= new XclExpBooleanCell(
2651 GetRoot(), aXclPos
, pPattern
, nMergeBaseXFId
, fValue
!= 0.0 );
2654 // try to create an RK value (compressed floating-point number)
2656 if( !xCell
&& XclTools::GetRKFromDouble( nRkValue
, fValue
) )
2657 xCell
= new XclExpRkCell(
2658 GetRoot(), aXclPos
, pPattern
, nMergeBaseXFId
, nRkValue
);
2660 // else: simple floating-point number cell
2662 xCell
= new XclExpNumberCell(
2663 GetRoot(), aXclPos
, pPattern
, nMergeBaseXFId
, fValue
);
2667 case CELLTYPE_STRING
:
2669 xCell
= new XclExpLabelCell(
2670 GetRoot(), aXclPos
, pPattern
, nMergeBaseXFId
, rScCell
.mpString
->getString());
2676 XclExpHyperlinkHelper
aLinkHelper( GetRoot(), aScPos
);
2677 xCell
= new XclExpLabelCell(
2678 GetRoot(), aXclPos
, pPattern
, nMergeBaseXFId
, rScCell
.mpEditText
, aLinkHelper
);
2680 // add a single created HLINK record to the record list
2681 if( aLinkHelper
.HasLinkRecord() )
2682 mxHyperlinkList
->AppendRecord( aLinkHelper
.GetLinkRecord() );
2683 // add list of multiple URLs to the additional cell note text
2684 if( aLinkHelper
.HasMultipleUrls() )
2685 aAddNoteText
= ScGlobal::addToken( aAddNoteText
, aLinkHelper
.GetUrlList(), '\n', 2 );
2689 case CELLTYPE_FORMULA
:
2693 OUString aUrl
= pPattern
->GetItemSet().Get(ATTR_HYPERLINK
).GetValue();
2694 if (!aUrl
.isEmpty())
2696 rtl::Reference
<XclExpHyperlink
> aLink
=
2697 new XclExpHyperlink(GetRoot(), SvxURLField(aUrl
, aUrl
), aScPos
);
2698 mxHyperlinkList
->AppendRecord(aLink
);
2702 xCell
= new XclExpFormulaCell(
2703 GetRoot(), aXclPos
, pPattern
, nMergeBaseXFId
,
2704 *rScCell
.mpFormula
, maArrayBfr
, maShrfmlaBfr
, maTableopBfr
);
2709 OSL_FAIL( "XclExpCellTable::XclExpCellTable - unknown cell type" );
2713 xCell
= new XclExpBlankCell(
2714 GetRoot(), aXclPos
, nLastXclCol
, pPattern
, nMergeBaseXFId
);
2719 assert(xCell
&& "can only reach here with xCell set");
2721 // insert the cell into the current row
2722 maRowBfr
.AppendCell( xCell
, bIsMergedBase
);
2724 if ( !aAddNoteText
.isEmpty() )
2725 mxNoteList
->AppendNewRecord( new XclExpNote( GetRoot(), aScPos
, nullptr, aAddNoteText
) );
2727 // other sheet contents
2730 const SfxItemSet
& rItemSet
= pPattern
->GetItemSet();
2732 // base cell in a merged range
2735 const ScMergeAttr
& rMergeItem
= rItemSet
.Get( ATTR_MERGE
);
2736 ScRange
aScRange( aScPos
);
2737 aScRange
.aEnd
.IncCol( rMergeItem
.GetColMerge() - 1 );
2738 aScRange
.aEnd
.IncRow( rMergeItem
.GetRowMerge() - 1 );
2739 sal_uInt32 nXFId
= xCell
->GetFirstXFId();
2740 // blank cells merged vertically may occur repeatedly
2741 OSL_ENSURE( (aScRange
.aStart
.Col() == aScRange
.aEnd
.Col()) || (nScCol
== nLastScCol
),
2742 "XclExpCellTable::XclExpCellTable - invalid repeated blank merged cell" );
2743 for( SCCOL nIndex
= nScCol
; nIndex
<= nLastScCol
; ++nIndex
)
2745 mxMergedcells
->AppendRange( aScRange
, nXFId
);
2746 aScRange
.aStart
.IncCol();
2747 aScRange
.aEnd
.IncCol();
2752 if( ScfTools::CheckItem( rItemSet
, ATTR_VALIDDATA
, false ) )
2754 sal_uLong nScHandle
= rItemSet
.Get( ATTR_VALIDDATA
).GetValue();
2755 ScRange
aScRange( aScPos
);
2756 aScRange
.aEnd
.SetCol( nLastScCol
);
2757 mxDval
->InsertCellRange( aScRange
, nScHandle
);
2762 // create missing row settings for rows anyhow flagged or with outlines
2763 maRowBfr
.CreateRows( ::std::max( nFirstUnflaggedScRow
, nFirstUngroupedScRow
) );
2766 void XclExpCellTable::Finalize(bool bXLS
)
2768 // Finalize multiple operations.
2769 maTableopBfr
.Finalize();
2771 /* Finalize column buffer. This calculates column default XF indexes from
2772 the XF identifiers and fills a vector with these XF indexes. */
2773 ScfUInt16Vec aColXFIndexes
;
2774 maColInfoBfr
.Finalize( aColXFIndexes
, bXLS
);
2776 // Usually many indexes towards the end will be EXC_XF_DEFAULTCELL, find
2777 // the index that starts all EXC_XF_DEFAULTCELL until the end.
2778 size_t nStartColAllDefault
= findFirstAllSameUntilEnd( aColXFIndexes
, EXC_XF_DEFAULTCELL
);
2780 /* Finalize row buffer. This calculates all cell XF indexes from the XF
2781 identifiers. Then the XF index vector aColXFIndexes (filled above) is
2782 used to calculate the row default formats. With this, all unneeded blank
2783 cell records (equal to row default or column default) will be removed.
2784 The function returns the (most used) default row format in aDefRowData. */
2785 XclExpDefaultRowData aDefRowData
;
2786 maRowBfr
.Finalize( aDefRowData
, aColXFIndexes
, nStartColAllDefault
);
2788 // Initialize the DEFROWHEIGHT record.
2789 mxDefrowheight
->SetDefaultData( aDefRowData
);
2792 XclExpRecordRef
XclExpCellTable::CreateRecord( sal_uInt16 nRecId
) const
2794 XclExpRecordRef xRec
;
2797 case EXC_ID3_DIMENSIONS
: xRec
= new XclExpDelegatingRecord( &const_cast<XclExpRowBuffer
*>(&maRowBfr
)->GetDimensions() ); break;
2798 case EXC_ID2_DEFROWHEIGHT
: xRec
= mxDefrowheight
; break;
2799 case EXC_ID_GUTS
: xRec
= mxGuts
; break;
2800 case EXC_ID_NOTE
: xRec
= mxNoteList
; break;
2801 case EXC_ID_MERGEDCELLS
: xRec
= mxMergedcells
; break;
2802 case EXC_ID_HLINK
: xRec
= mxHyperlinkList
; break;
2803 case EXC_ID_DVAL
: xRec
= mxDval
; break;
2804 case EXC_ID_EXTLST
: xRec
= mxExtLst
; break;
2805 default: OSL_FAIL( "XclExpCellTable::CreateRecord - unknown record id" );
2810 void XclExpCellTable::Save( XclExpStream
& rStrm
)
2812 // DEFCOLWIDTH and COLINFOs
2813 maColInfoBfr
.Save( rStrm
);
2814 // ROWs and cell records
2815 maRowBfr
.Save( rStrm
);
2818 void XclExpCellTable::SaveXml( XclExpXmlStream
& rStrm
)
2820 // DEFAULT row height
2821 XclExpDefaultRowData
& rDefData
= mxDefrowheight
->GetDefaultData();
2822 sax_fastparser::FSHelperPtr
& rWorksheet
= rStrm
.GetCurrentStream();
2823 rWorksheet
->startElement( XML_sheetFormatPr
,
2824 // OOXTODO: XML_baseColWidth
2825 XML_defaultColWidth
, OString::number(maColInfoBfr
.GetDefColWidth()),
2826 // OOXTODO: XML_customHeight
2827 // OOXTODO: XML_thickTop
2828 // OOXTODO: XML_thickBottom
2829 XML_defaultRowHeight
, OString::number(static_cast<double> (rDefData
.mnHeight
) / 20.0),
2830 XML_zeroHeight
, ToPsz( rDefData
.IsHidden() ),
2831 XML_outlineLevelRow
, OString::number(maRowBfr
.GetHighestOutlineLevel()),
2832 XML_outlineLevelCol
, OString::number(maColInfoBfr
.GetHighestOutlineLevel()) );
2833 rWorksheet
->endElement( XML_sheetFormatPr
);
2835 maColInfoBfr
.SaveXml( rStrm
);
2836 maRowBfr
.SaveXml( rStrm
);
2837 mxExtLst
->SaveXml( rStrm
);
2840 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */