1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <sal/config.h>
21
22 #include <string_view>
23
24 #include <com/sun/star/i18n/XBreakIterator.hpp>
25 #include <com/sun/star/i18n/ScriptType.hpp>
26 #include <sfx2/objsh.hxx>
27 #include <vcl/font.hxx>
28 #include <tools/urlobj.hxx>
29 #include <svl/itemset.hxx>
30 #include <svtools/ctrltool.hxx>
31 #include <svx/svdotext.hxx>
32 #include <editeng/outlobj.hxx>
33 #include <scitems.hxx>
34 #include <editeng/fhgtitem.hxx>
35 #include <editeng/flstitem.hxx>
36 #include <editeng/colritem.hxx>
37 #include <editeng/eeitem.hxx>
38 #include <editeng/flditem.hxx>
39 #include <editeng/escapementitem.hxx>
40 #include <editeng/svxfont.hxx>
41 #include <editeng/editids.hrc>
42
43 #include <document.hxx>
44 #include <docpool.hxx>
45 #include <editutil.hxx>
46 #include <patattr.hxx>
47 #include <scmatrix.hxx>
48 #include <xestyle.hxx>
49 #include <fprogressbar.hxx>
50 #include <globstr.hrc>
51 #include <xltracer.hxx>
52 #include <xltools.hxx>
53 #include <xecontent.hxx>
54 #include <xelink.hxx>
55 #include <xehelper.hxx>
56
57 using ::com::sun::star::uno::Reference;
58 using ::com::sun::star::i18n::XBreakIterator;
59
60 // Export progress bar ========================================================
61
XclExpProgressBar(const XclExpRoot & rRoot)62 XclExpProgressBar::XclExpProgressBar( const XclExpRoot& rRoot ) :
63 XclExpRoot( rRoot ),
64 mxProgress( new ScfProgressBar( rRoot.GetDocShell(), STR_SAVE_DOC ) ),
65 mpSubProgress( nullptr ),
66 mpSubRowCreate( nullptr ),
67 mpSubRowFinal( nullptr ),
68 mnSegRowFinal( SCF_INV_SEGMENT ),
69 mnRowCount( 0 )
70 {
71 }
72
~XclExpProgressBar()73 XclExpProgressBar::~XclExpProgressBar()
74 {
75 }
76
Initialize()77 void XclExpProgressBar::Initialize()
78 {
79 const ScDocument& rDoc = GetDoc();
80 const XclExpTabInfo& rTabInfo = GetTabInfo();
81 SCTAB nScTabCount = rTabInfo.GetScTabCount();
82
83 // *** segment: creation of ROW records *** -------------------------------
84
85 sal_Int32 nSegRowCreate = mxProgress->AddSegment( 2000 );
86 mpSubRowCreate = &mxProgress->GetSegmentProgressBar( nSegRowCreate );
87 maSubSegRowCreate.resize( nScTabCount, SCF_INV_SEGMENT );
88
89 for( SCTAB nScTab = 0; nScTab < nScTabCount; ++nScTab )
90 {
91 if( rTabInfo.IsExportTab( nScTab ) )
92 {
93 SCCOL nLastUsedScCol;
94 SCROW nLastUsedScRow;
95 rDoc.GetTableArea( nScTab, nLastUsedScCol, nLastUsedScRow );
96 std::size_t nSegSize = static_cast< std::size_t >( nLastUsedScRow + 1 );
97 maSubSegRowCreate[ nScTab ] = mpSubRowCreate->AddSegment( nSegSize );
98 }
99 }
100
101 // *** segment: writing all ROW records *** -------------------------------
102
103 mnSegRowFinal = mxProgress->AddSegment( 1000 );
104 // sub progress bar and segment are created later in ActivateFinalRowsSegment()
105 }
106
IncRowRecordCount()107 void XclExpProgressBar::IncRowRecordCount()
108 {
109 ++mnRowCount;
110 }
111
ActivateCreateRowsSegment()112 void XclExpProgressBar::ActivateCreateRowsSegment()
113 {
114 OSL_ENSURE( (0 <= GetCurrScTab()) && (GetCurrScTab() < GetTabInfo().GetScTabCount()),
115 "XclExpProgressBar::ActivateCreateRowsSegment - invalid sheet" );
116 sal_Int32 nSeg = maSubSegRowCreate[ GetCurrScTab() ];
117 OSL_ENSURE( nSeg != SCF_INV_SEGMENT, "XclExpProgressBar::ActivateCreateRowsSegment - invalid segment" );
118 if( nSeg != SCF_INV_SEGMENT )
119 {
120 mpSubProgress = mpSubRowCreate;
121 mpSubProgress->ActivateSegment( nSeg );
122 }
123 else
124 mpSubProgress = nullptr;
125 }
126
ActivateFinalRowsSegment()127 void XclExpProgressBar::ActivateFinalRowsSegment()
128 {
129 if( !mpSubRowFinal && (mnRowCount > 0) )
130 {
131 mpSubRowFinal = &mxProgress->GetSegmentProgressBar( mnSegRowFinal );
132 mpSubRowFinal->AddSegment( mnRowCount );
133 }
134 mpSubProgress = mpSubRowFinal;
135 if( mpSubProgress )
136 mpSubProgress->Activate();
137 }
138
Progress()139 void XclExpProgressBar::Progress()
140 {
141 if( mpSubProgress && !mpSubProgress->IsFull() )
142 mpSubProgress->Progress();
143 }
144
145 // Calc->Excel cell address/range conversion ==================================
146
147 namespace {
148
149 /** Fills the passed Excel address with the passed Calc cell coordinates without checking any limits. */
lclFillAddress(XclAddress & rXclPos,SCCOL nScCol,SCROW nScRow)150 void lclFillAddress( XclAddress& rXclPos, SCCOL nScCol, SCROW nScRow )
151 {
152 rXclPos.mnCol = static_cast< sal_uInt16 >( nScCol );
153 rXclPos.mnRow = static_cast< sal_uInt32 >( nScRow );
154 }
155
156 } // namespace
157
XclExpAddressConverter(const XclExpRoot & rRoot)158 XclExpAddressConverter::XclExpAddressConverter( const XclExpRoot& rRoot ) :
159 XclAddressConverterBase( rRoot.GetTracer(), rRoot.GetXclMaxPos() )
160 {
161 }
162
163 // cell address ---------------------------------------------------------------
164
CheckAddress(const ScAddress & rScPos,bool bWarn)165 bool XclExpAddressConverter::CheckAddress( const ScAddress& rScPos, bool bWarn )
166 {
167 // ScAddress::operator<=() doesn't do what we want here
168 bool bValidCol = (0 <= rScPos.Col()) && (rScPos.Col() <= maMaxPos.Col());
169 bool bValidRow = (0 <= rScPos.Row()) && (rScPos.Row() <= maMaxPos.Row());
170 bool bValidTab = (0 <= rScPos.Tab()) && (rScPos.Tab() <= maMaxPos.Tab());
171
172 bool bValid = bValidCol && bValidRow && bValidTab;
173 if( !bValid )
174 {
175 mbColTrunc |= !bValidCol;
176 mbRowTrunc |= !bValidRow;
177 }
178 if( !bValid && bWarn )
179 {
180 mbTabTrunc |= (rScPos.Tab() > maMaxPos.Tab()); // do not warn for deleted refs
181 mrTracer.TraceInvalidAddress( rScPos, maMaxPos );
182 }
183 return bValid;
184 }
185
ConvertAddress(XclAddress & rXclPos,const ScAddress & rScPos,bool bWarn)186 bool XclExpAddressConverter::ConvertAddress( XclAddress& rXclPos,
187 const ScAddress& rScPos, bool bWarn )
188 {
189 bool bValid = CheckAddress( rScPos, bWarn );
190 if( bValid )
191 lclFillAddress( rXclPos, rScPos.Col(), rScPos.Row() );
192 return bValid;
193 }
194
CreateValidAddress(const ScAddress & rScPos,bool bWarn)195 XclAddress XclExpAddressConverter::CreateValidAddress( const ScAddress& rScPos, bool bWarn )
196 {
197 XclAddress aXclPos( ScAddress::UNINITIALIZED );
198 if( !ConvertAddress( aXclPos, rScPos, bWarn ) )
199 lclFillAddress( aXclPos, ::std::min( rScPos.Col(), maMaxPos.Col() ), ::std::min( rScPos.Row(), maMaxPos.Row() ) );
200 return aXclPos;
201 }
202
203 // cell range -----------------------------------------------------------------
204
CheckRange(const ScRange & rScRange,bool bWarn)205 bool XclExpAddressConverter::CheckRange( const ScRange& rScRange, bool bWarn )
206 {
207 return CheckAddress( rScRange.aStart, bWarn ) && CheckAddress( rScRange.aEnd, bWarn );
208 }
209
ValidateRange(ScRange & rScRange,bool bWarn)210 bool XclExpAddressConverter::ValidateRange( ScRange& rScRange, bool bWarn )
211 {
212 rScRange.PutInOrder();
213
214 // check start position
215 bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
216 if( bValidStart )
217 {
218 // check & correct end position
219 ScAddress& rScEnd = rScRange.aEnd;
220 if( !CheckAddress( rScEnd, bWarn ) )
221 {
222 rScEnd.SetCol( ::std::min( rScEnd.Col(), maMaxPos.Col() ) );
223 rScEnd.SetRow( ::std::min( rScEnd.Row(), maMaxPos.Row() ) );
224 rScEnd.SetTab( ::std::min( rScEnd.Tab(), maMaxPos.Tab() ) );
225 }
226 }
227
228 return bValidStart;
229 }
230
ConvertRange(XclRange & rXclRange,const ScRange & rScRange,bool bWarn)231 bool XclExpAddressConverter::ConvertRange( XclRange& rXclRange,
232 const ScRange& rScRange, bool bWarn )
233 {
234 // check start position
235 bool bValidStart = CheckAddress( rScRange.aStart, bWarn );
236 if( bValidStart )
237 {
238 lclFillAddress( rXclRange.maFirst, rScRange.aStart.Col(), rScRange.aStart.Row() );
239
240 // check & correct end position
241 SCCOL nScCol2 = rScRange.aEnd.Col();
242 SCROW nScRow2 = rScRange.aEnd.Row();
243 if( !CheckAddress( rScRange.aEnd, bWarn ) )
244 {
245 nScCol2 = ::std::min( nScCol2, maMaxPos.Col() );
246 nScRow2 = ::std::min( nScRow2, maMaxPos.Row() );
247 }
248 lclFillAddress( rXclRange.maLast, nScCol2, nScRow2 );
249 }
250 return bValidStart;
251 }
252
253 // cell range list ------------------------------------------------------------
254
ValidateRangeList(ScRangeList & rScRanges,bool bWarn)255 void XclExpAddressConverter::ValidateRangeList( ScRangeList& rScRanges, bool bWarn )
256 {
257 for ( size_t nRange = rScRanges.size(); nRange > 0; )
258 {
259 ScRange & rScRange = rScRanges[ --nRange ];
260 if( !CheckRange( rScRange, bWarn ) )
261 rScRanges.Remove(nRange);
262 }
263 }
264
ConvertRangeList(XclRangeList & rXclRanges,const ScRangeList & rScRanges,bool bWarn)265 void XclExpAddressConverter::ConvertRangeList( XclRangeList& rXclRanges,
266 const ScRangeList& rScRanges, bool bWarn )
267 {
268 rXclRanges.clear();
269 for( size_t nPos = 0, nCount = rScRanges.size(); nPos < nCount; ++nPos )
270 {
271 const ScRange & rScRange = rScRanges[ nPos ];
272 XclRange aXclRange( ScAddress::UNINITIALIZED );
273 if( ConvertRange( aXclRange, rScRange, bWarn ) )
274 rXclRanges.push_back( aXclRange );
275 }
276 }
277
278 // EditEngine->String conversion ==============================================
279
280 namespace {
281
lclGetUrlRepresentation(const SvxURLField & rUrlField)282 OUString lclGetUrlRepresentation( const SvxURLField& rUrlField )
283 {
284 const OUString& aRepr = rUrlField.GetRepresentation();
285 // no representation -> use URL
286 return aRepr.isEmpty() ? rUrlField.GetURL() : aRepr;
287 }
288
289 } // namespace
290
XclExpHyperlinkHelper(const XclExpRoot & rRoot,const ScAddress & rScPos)291 XclExpHyperlinkHelper::XclExpHyperlinkHelper( const XclExpRoot& rRoot, const ScAddress& rScPos ) :
292 XclExpRoot( rRoot ),
293 maScPos( rScPos ),
294 mbMultipleUrls( false )
295 {
296 }
297
~XclExpHyperlinkHelper()298 XclExpHyperlinkHelper::~XclExpHyperlinkHelper()
299 {
300 }
301
ProcessUrlField(const SvxURLField & rUrlField)302 OUString XclExpHyperlinkHelper::ProcessUrlField( const SvxURLField& rUrlField )
303 {
304 OUString aUrlRepr;
305
306 if( GetBiff() == EXC_BIFF8 ) // no HLINK records in BIFF2-BIFF7
307 {
308 // there was/is already a HLINK record
309 mbMultipleUrls = static_cast< bool >(mxLinkRec);
310
311 mxLinkRec = new XclExpHyperlink( GetRoot(), rUrlField, maScPos );
312
313 if( const OUString* pRepr = mxLinkRec->GetRepr() )
314 aUrlRepr = *pRepr;
315
316 // add URL to note text
317 maUrlList = ScGlobal::addToken( maUrlList, rUrlField.GetURL(), '\n' );
318 }
319
320 // no hyperlink representation from Excel HLINK record -> use it from text field
321 return aUrlRepr.isEmpty() ? lclGetUrlRepresentation(rUrlField) : aUrlRepr;
322 }
323
HasLinkRecord() const324 bool XclExpHyperlinkHelper::HasLinkRecord() const
325 {
326 return !mbMultipleUrls && mxLinkRec;
327 }
328
GetLinkRecord() const329 XclExpHyperlinkHelper::XclExpHyperlinkRef XclExpHyperlinkHelper::GetLinkRecord() const
330 {
331 if( HasLinkRecord() )
332 return mxLinkRec;
333 return XclExpHyperlinkRef();
334 }
335
336 namespace {
337
338 /** Creates a new formatted string from the passed unformatted string.
339
340 Creates a Unicode string or a byte string, depending on the current BIFF
341 version contained in the passed XclExpRoot object. May create a formatted
342 string object, if the text contains different script types.
343
344 @param pCellAttr
345 Cell attributes used for font formatting.
346 @param nFlags
347 Modifiers for string export.
348 @param nMaxLen
349 The maximum number of characters to store in this string.
350 @return
351 The new string object.
352 */
lclCreateFormattedString(const XclExpRoot & rRoot,const OUString & rText,const ScPatternAttr * pCellAttr,XclStrFlags nFlags,sal_uInt16 nMaxLen)353 XclExpStringRef lclCreateFormattedString(
354 const XclExpRoot& rRoot, const OUString& rText, const ScPatternAttr* pCellAttr,
355 XclStrFlags nFlags, sal_uInt16 nMaxLen )
356 {
357 /* Create an empty Excel string object with correctly initialized BIFF mode,
358 because this function only uses Append() functions that require this. */
359 XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_OUSTRING, nFlags, nMaxLen );
360
361 // script type handling
362 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
363 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
364 // #i63255# get script type for leading weak characters
365 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rText );
366
367 // font buffer and cell item set
368 XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
369 const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
370
371 // process all script portions
372 sal_Int32 nPortionPos = 0;
373 sal_Int32 nTextLen = rText.getLength();
374 while( nPortionPos < nTextLen )
375 {
376 // get script type and end position of next script portion
377 sal_Int16 nScript = xBreakIt->getScriptType( rText, nPortionPos );
378 sal_Int32 nPortionEnd = xBreakIt->endOfScript( rText, nPortionPos, nScript );
379
380 // reuse previous script for following weak portions
381 if( nScript == ApiScriptType::WEAK )
382 nScript = nLastScript;
383
384 // construct font from current text portion
385 SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, rItemSet, nScript ) );
386
387 // Excel start position of this portion
388 sal_Int32 nXclPortionStart = xString->Len();
389 // add portion text to Excel string
390 XclExpStringHelper::AppendString( *xString, rRoot, rText.copy( nPortionPos, nPortionEnd - nPortionPos ) );
391 if( nXclPortionStart < xString->Len() )
392 {
393 // insert font into buffer
394 sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
395 // insert font index into format run vector
396 xString->AppendFormat( nXclPortionStart, nFontIdx );
397 }
398
399 // go to next script portion
400 nLastScript = nScript;
401 nPortionPos = nPortionEnd;
402 }
403
404 return xString;
405 }
406
407 /** Creates a new formatted string from an edit engine text object.
408
409 Creates a Unicode string or a byte string, depending on the current BIFF
410 version contained in the passed XclExpRoot object.
411
412 @param rEE
413 The edit engine in use. The text object must already be set.
414 @param nFlags
415 Modifiers for string export.
416 @param nMaxLen
417 The maximum number of characters to store in this string.
418 @return
419 The new string object.
420 */
lclCreateFormattedString(const XclExpRoot & rRoot,EditEngine & rEE,XclExpHyperlinkHelper * pLinkHelper,XclStrFlags nFlags,sal_uInt16 nMaxLen)421 XclExpStringRef lclCreateFormattedString(
422 const XclExpRoot& rRoot, EditEngine& rEE, XclExpHyperlinkHelper* pLinkHelper,
423 XclStrFlags nFlags, sal_uInt16 nMaxLen )
424 {
425 /* Create an empty Excel string object with correctly initialized BIFF mode,
426 because this function only uses Append() functions that require this. */
427 XclExpStringRef xString = XclExpStringHelper::CreateString( rRoot, EMPTY_OUSTRING, nFlags, nMaxLen );
428
429 // font buffer and helper item set for edit engine -> Calc item conversion
430 XclExpFontBuffer& rFontBuffer = rRoot.GetFontBuffer();
431 SfxItemSet aItemSet( *rRoot.GetDoc().GetPool(), svl::Items<ATTR_PATTERN_START, ATTR_PATTERN_END>{} );
432
433 // script type handling
434 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
435 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
436 // #i63255# get script type for leading weak characters
437 sal_Int16 nLastScript = XclExpStringHelper::GetLeadingScriptType( rRoot, rEE.GetText() );
438
439 // process all paragraphs
440 sal_Int32 nParaCount = rEE.GetParagraphCount();
441 for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
442 {
443 ESelection aSel( nPara, 0 );
444 OUString aParaText( rEE.GetText( nPara ) );
445
446 std::vector<sal_Int32> aPosList;
447 rEE.GetPortions( nPara, aPosList );
448
449 // process all portions in the paragraph
450 for( const auto& rPos : aPosList )
451 {
452 aSel.nEndPos = rPos;
453 OUString aXclPortionText = aParaText.copy( aSel.nStartPos, aSel.nEndPos - aSel.nStartPos );
454
455 aItemSet.ClearItem();
456 SfxItemSet aEditSet( rEE.GetAttribs( aSel ) );
457 ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
458
459 // get escapement value
460 short nEsc = aEditSet.Get( EE_CHAR_ESCAPEMENT ).GetEsc();
461
462 // process text fields
463 bool bIsHyperlink = false;
464 if( aSel.nStartPos + 1 == aSel.nEndPos )
465 {
466 // test if the character is a text field
467 const SfxPoolItem* pItem;
468 if( aEditSet.GetItemState( EE_FEATURE_FIELD, false, &pItem ) == SfxItemState::SET )
469 {
470 const SvxFieldData* pField = static_cast< const SvxFieldItem* >( pItem )->GetField();
471 if( const SvxURLField* pUrlField = dynamic_cast<const SvxURLField*>( pField ) )
472 {
473 // convert URL field to string representation
474 aXclPortionText = pLinkHelper ?
475 pLinkHelper->ProcessUrlField( *pUrlField ) :
476 lclGetUrlRepresentation( *pUrlField );
477 bIsHyperlink = true;
478 }
479 else
480 {
481 OSL_FAIL( "lclCreateFormattedString - unknown text field" );
482 aXclPortionText.clear();
483 }
484 }
485 }
486
487 // Excel start position of this portion
488 sal_Int32 nXclPortionStart = xString->Len();
489 // add portion text to Excel string
490 XclExpStringHelper::AppendString( *xString, rRoot, aXclPortionText );
491 if( (nXclPortionStart < xString->Len()) || (aParaText.isEmpty()) )
492 {
493 /* Construct font from current edit engine text portion. Edit engine
494 creates different portions for different script types, no need to loop. */
495 sal_Int16 nScript = xBreakIt->getScriptType( aXclPortionText, 0 );
496 if( nScript == ApiScriptType::WEAK )
497 nScript = nLastScript;
498 SvxFont aFont( XclExpFontHelper::GetFontFromItemSet( rRoot, aItemSet, nScript ) );
499 nLastScript = nScript;
500
501 // add escapement
502 aFont.SetEscapement( nEsc );
503 // modify automatic font color for hyperlinks
504 if( bIsHyperlink && aItemSet.Get( ATTR_FONT_COLOR ).GetValue() == COL_AUTO)
505 aFont.SetColor( COL_LIGHTBLUE );
506
507 // insert font into buffer
508 sal_uInt16 nFontIdx = rFontBuffer.Insert( aFont, EXC_COLOR_CELLTEXT );
509 // insert font index into format run vector
510 xString->AppendFormat( nXclPortionStart, nFontIdx );
511 }
512
513 aSel.nStartPos = aSel.nEndPos;
514 }
515
516 // add trailing newline (important for correct character index calculation)
517 if( nPara + 1 < nParaCount )
518 XclExpStringHelper::AppendChar( *xString, rRoot, '\n' );
519 }
520
521 return xString;
522 }
523
524 } // namespace
525
CreateString(const XclExpRoot & rRoot,const OUString & rString,XclStrFlags nFlags,sal_uInt16 nMaxLen)526 XclExpStringRef XclExpStringHelper::CreateString(
527 const XclExpRoot& rRoot, const OUString& rString, XclStrFlags nFlags, sal_uInt16 nMaxLen )
528 {
529 XclExpStringRef xString = std::make_shared<XclExpString>();
530 if( rRoot.GetBiff() == EXC_BIFF8 )
531 xString->Assign( rString, nFlags, nMaxLen );
532 else
533 xString->AssignByte( rString, rRoot.GetTextEncoding(), nFlags, nMaxLen );
534 return xString;
535 }
536
CreateString(const XclExpRoot & rRoot,sal_Unicode cChar,XclStrFlags nFlags,sal_uInt16 nMaxLen)537 XclExpStringRef XclExpStringHelper::CreateString(
538 const XclExpRoot& rRoot, sal_Unicode cChar, XclStrFlags nFlags, sal_uInt16 nMaxLen )
539 {
540 XclExpStringRef xString = CreateString( rRoot, EMPTY_OUSTRING, nFlags, nMaxLen );
541 AppendChar( *xString, rRoot, cChar );
542 return xString;
543 }
544
AppendString(XclExpString & rXclString,const XclExpRoot & rRoot,const OUString & rString)545 void XclExpStringHelper::AppendString( XclExpString& rXclString, const XclExpRoot& rRoot, const OUString& rString )
546 {
547 if( rRoot.GetBiff() == EXC_BIFF8 )
548 rXclString.Append( rString );
549 else
550 rXclString.AppendByte( rString, rRoot.GetTextEncoding() );
551 }
552
AppendChar(XclExpString & rXclString,const XclExpRoot & rRoot,sal_Unicode cChar)553 void XclExpStringHelper::AppendChar( XclExpString& rXclString, const XclExpRoot& rRoot, sal_Unicode cChar )
554 {
555 if( rRoot.GetBiff() == EXC_BIFF8 )
556 rXclString.Append( OUString(cChar) );
557 else
558 rXclString.AppendByte( cChar, rRoot.GetTextEncoding() );
559 }
560
CreateCellString(const XclExpRoot & rRoot,const OUString & rString,const ScPatternAttr * pCellAttr,XclStrFlags nFlags,sal_uInt16 nMaxLen)561 XclExpStringRef XclExpStringHelper::CreateCellString(
562 const XclExpRoot& rRoot, const OUString& rString, const ScPatternAttr* pCellAttr,
563 XclStrFlags nFlags, sal_uInt16 nMaxLen )
564 {
565 return lclCreateFormattedString(rRoot, rString, pCellAttr, nFlags, nMaxLen);
566 }
567
CreateCellString(const XclExpRoot & rRoot,const EditTextObject & rEditText,const ScPatternAttr * pCellAttr,XclExpHyperlinkHelper & rLinkHelper,XclStrFlags nFlags,sal_uInt16 nMaxLen)568 XclExpStringRef XclExpStringHelper::CreateCellString(
569 const XclExpRoot& rRoot, const EditTextObject& rEditText, const ScPatternAttr* pCellAttr,
570 XclExpHyperlinkHelper& rLinkHelper, XclStrFlags nFlags, sal_uInt16 nMaxLen )
571 {
572 XclExpStringRef xString;
573
574 // formatted cell
575 ScEditEngineDefaulter& rEE = rRoot.GetEditEngine();
576 bool bOldUpdateMode = rEE.GetUpdateMode();
577 rEE.SetUpdateMode( true );
578
579 // default items
580 const SfxItemSet& rItemSet = pCellAttr ? pCellAttr->GetItemSet() : rRoot.GetDoc().GetDefPattern()->GetItemSet();
581 auto pEEItemSet = std::make_unique<SfxItemSet>( rEE.GetEmptyItemSet() );
582 ScPatternAttr::FillToEditItemSet( *pEEItemSet, rItemSet );
583 rEE.SetDefaults( std::move(pEEItemSet) ); // edit engine takes ownership
584
585 // create the string
586 rEE.SetTextCurrentDefaults(rEditText);
587 xString = lclCreateFormattedString( rRoot, rEE, &rLinkHelper, nFlags, nMaxLen );
588 rEE.SetUpdateMode( bOldUpdateMode );
589
590 return xString;
591 }
592
CreateString(const XclExpRoot & rRoot,const SdrTextObj & rTextObj,XclStrFlags nFlags)593 XclExpStringRef XclExpStringHelper::CreateString(
594 const XclExpRoot& rRoot, const SdrTextObj& rTextObj,
595 XclStrFlags nFlags )
596 {
597 XclExpStringRef xString;
598 if( const OutlinerParaObject* pParaObj = rTextObj.GetOutlinerParaObject() )
599 {
600 EditEngine& rEE = rRoot.GetDrawEditEngine();
601 bool bOldUpdateMode = rEE.GetUpdateMode();
602 rEE.SetUpdateMode( true );
603 // create the string
604 rEE.SetText( pParaObj->GetTextObject() );
605 xString = lclCreateFormattedString( rRoot, rEE, nullptr, nFlags, EXC_STR_MAXLEN );
606 rEE.SetUpdateMode( bOldUpdateMode );
607 // limit formats - TODO: BIFF dependent
608 if( !xString->IsEmpty() )
609 {
610 xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
611 xString->AppendTrailingFormat( EXC_FONT_APP );
612 }
613 }
614 else
615 {
616 OSL_FAIL( "XclExpStringHelper::CreateString - textbox without para object" );
617 // create BIFF dependent empty Excel string
618 xString = CreateString( rRoot, EMPTY_OUSTRING, nFlags );
619 }
620 return xString;
621 }
622
CreateString(const XclExpRoot & rRoot,const EditTextObject & rEditObj,XclStrFlags nFlags)623 XclExpStringRef XclExpStringHelper::CreateString(
624 const XclExpRoot& rRoot, const EditTextObject& rEditObj,
625 XclStrFlags nFlags )
626 {
627 XclExpStringRef xString;
628 EditEngine& rEE = rRoot.GetDrawEditEngine();
629 bool bOldUpdateMode = rEE.GetUpdateMode();
630 rEE.SetUpdateMode( true );
631 rEE.SetText( rEditObj );
632 xString = lclCreateFormattedString( rRoot, rEE, nullptr, nFlags, EXC_STR_MAXLEN );
633 rEE.SetUpdateMode( bOldUpdateMode );
634 // limit formats - TODO: BIFF dependent
635 if( !xString->IsEmpty() )
636 {
637 xString->LimitFormatCount( EXC_MAXRECSIZE_BIFF8 / 8 - 1 );
638 xString->AppendTrailingFormat( EXC_FONT_APP );
639 }
640 return xString;
641 }
642
GetLeadingScriptType(const XclExpRoot & rRoot,const OUString & rString)643 sal_Int16 XclExpStringHelper::GetLeadingScriptType( const XclExpRoot& rRoot, const OUString& rString )
644 {
645 namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
646 Reference< XBreakIterator > xBreakIt = rRoot.GetDoc().GetBreakIterator();
647 sal_Int32 nStrPos = 0;
648 sal_Int32 nStrLen = rString.getLength();
649 sal_Int16 nScript = ApiScriptType::WEAK;
650 while( (nStrPos < nStrLen) && (nScript == ApiScriptType::WEAK) )
651 {
652 nScript = xBreakIt->getScriptType( rString, nStrPos );
653 nStrPos = xBreakIt->endOfScript( rString, nStrPos, nScript );
654 }
655 return (nScript == ApiScriptType::WEAK) ? rRoot.GetDefApiScript() : nScript;
656 }
657
658 // Header/footer conversion ===================================================
659
XclExpHFConverter(const XclExpRoot & rRoot)660 XclExpHFConverter::XclExpHFConverter( const XclExpRoot& rRoot ) :
661 XclExpRoot( rRoot ),
662 mrEE( rRoot.GetHFEditEngine() ),
663 mnTotalHeight( 0 )
664 {
665 }
666
GenerateString(const EditTextObject * pLeftObj,const EditTextObject * pCenterObj,const EditTextObject * pRightObj)667 void XclExpHFConverter::GenerateString(
668 const EditTextObject* pLeftObj,
669 const EditTextObject* pCenterObj,
670 const EditTextObject* pRightObj )
671 {
672 maHFString.clear();
673 mnTotalHeight = 0;
674 AppendPortion( pLeftObj, 'L' );
675 AppendPortion( pCenterObj, 'C' );
676 AppendPortion( pRightObj, 'R' );
677 }
678
AppendPortion(const EditTextObject * pTextObj,sal_Unicode cPortionCode)679 void XclExpHFConverter::AppendPortion( const EditTextObject* pTextObj, sal_Unicode cPortionCode )
680 {
681 if( !pTextObj ) return;
682
683 OUString aText;
684 sal_Int32 nHeight = 0;
685 SfxItemSet aItemSet( *GetDoc().GetPool(), svl::Items<ATTR_PATTERN_START, ATTR_PATTERN_END>{} );
686
687 // edit engine
688 bool bOldUpdateMode = mrEE.GetUpdateMode();
689 mrEE.SetUpdateMode( true );
690 mrEE.SetText( *pTextObj );
691
692 // font information
693 XclFontData aFontData, aNewData;
694 if( const XclExpFont* pFirstFont = GetFontBuffer().GetFont( EXC_FONT_APP ) )
695 {
696 aFontData = pFirstFont->GetFontData();
697 aFontData.mnHeight = (aFontData.mnHeight + 10) / 20; // using pt here, not twips
698 }
699 else
700 aFontData.mnHeight = 10;
701
702 const FontList* pFontList = nullptr;
703 if( SfxObjectShell* pDocShell = GetDocShell() )
704 {
705 if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
706 pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
707 pFontList = pInfoItem->GetFontList();
708 }
709
710 sal_Int32 nParaCount = mrEE.GetParagraphCount();
711 for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
712 {
713 ESelection aSel( nPara, 0 );
714 OUStringBuffer aParaText;
715 sal_Int32 nParaHeight = 0;
716 std::vector<sal_Int32> aPosList;
717 mrEE.GetPortions( nPara, aPosList );
718
719 for( const auto& rPos : aPosList )
720 {
721 aSel.nEndPos = rPos;
722 if( aSel.nStartPos < aSel.nEndPos )
723 {
724
725 // --- font attributes ---
726
727 vcl::Font aFont;
728 aItemSet.ClearItem();
729 SfxItemSet aEditSet( mrEE.GetAttribs( aSel ) );
730 ScPatternAttr::GetFromEditItemSet( aItemSet, aEditSet );
731 ScPatternAttr::GetFont( aFont, aItemSet, SC_AUTOCOL_RAW );
732
733 // font name and style
734 aNewData.maName = XclTools::GetXclFontName( aFont.GetFamilyName() );
735 aNewData.mnWeight = (aFont.GetWeight() > WEIGHT_NORMAL) ? EXC_FONTWGHT_BOLD : EXC_FONTWGHT_NORMAL;
736 aNewData.mbItalic = (aFont.GetItalic() != ITALIC_NONE);
737 bool bNewFont = (aFontData.maName != aNewData.maName);
738 bool bNewStyle = (aFontData.mnWeight != aNewData.mnWeight) ||
739 (aFontData.mbItalic != aNewData.mbItalic);
740 if( bNewFont || (bNewStyle && pFontList) )
741 {
742 aParaText.append("&\"" + aNewData.maName);
743 if( pFontList )
744 {
745 FontMetric aFontMetric( pFontList->Get(
746 aNewData.maName,
747 (aNewData.mnWeight > EXC_FONTWGHT_NORMAL) ? WEIGHT_BOLD : WEIGHT_NORMAL,
748 aNewData.mbItalic ? ITALIC_NORMAL : ITALIC_NONE ) );
749 aNewData.maStyle = pFontList->GetStyleName( aFontMetric );
750 if( !aNewData.maStyle.isEmpty() )
751 aParaText.append("," + aNewData.maStyle);
752 }
753 aParaText.append("\"");
754 }
755
756 // height
757 // is calculated wrong in ScPatternAttr::GetFromEditItemSet, because already in twips and not 100thmm
758 // -> get it directly from edit engine item set
759 aNewData.mnHeight = ulimit_cast< sal_uInt16 >( aEditSet.Get( EE_CHAR_FONTHEIGHT ).GetHeight() );
760 aNewData.mnHeight = (aNewData.mnHeight + 10) / 20;
761 bool bFontHtChanged = (aFontData.mnHeight != aNewData.mnHeight);
762 if( bFontHtChanged )
763 aParaText.append("&" + OUString::number(aNewData.mnHeight));
764 // update maximum paragraph height, convert to twips
765 nParaHeight = ::std::max< sal_Int32 >( nParaHeight, aNewData.mnHeight * 20 );
766
767 // underline
768 aNewData.mnUnderline = EXC_FONTUNDERL_NONE;
769 switch( aFont.GetUnderline() )
770 {
771 case LINESTYLE_NONE: aNewData.mnUnderline = EXC_FONTUNDERL_NONE; break;
772 case LINESTYLE_SINGLE: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE; break;
773 case LINESTYLE_DOUBLE: aNewData.mnUnderline = EXC_FONTUNDERL_DOUBLE; break;
774 default: aNewData.mnUnderline = EXC_FONTUNDERL_SINGLE;
775 }
776 if( aFontData.mnUnderline != aNewData.mnUnderline )
777 {
778 sal_uInt8 nTmpUnderl = (aNewData.mnUnderline == EXC_FONTUNDERL_NONE) ?
779 aFontData.mnUnderline : aNewData.mnUnderline;
780 (nTmpUnderl == EXC_FONTUNDERL_SINGLE)? aParaText.append("&U") : aParaText.append("&E");
781 }
782
783 // font color
784 aNewData.maColor = aFont.GetColor();
785 if ( !aFontData.maColor.IsRGBEqual( aNewData.maColor ) )
786 {
787 aParaText.append("&K" + aNewData.maColor.AsRGBHexString());
788 }
789
790 // strikeout
791 aNewData.mbStrikeout = (aFont.GetStrikeout() != STRIKEOUT_NONE);
792 if( aFontData.mbStrikeout != aNewData.mbStrikeout )
793 aParaText.append("&S");
794
795 // super/sub script
796 const SvxEscapementItem& rEscapeItem = aEditSet.Get( EE_CHAR_ESCAPEMENT );
797 aNewData.SetScEscapement( rEscapeItem.GetEsc() );
798 if( aFontData.mnEscapem != aNewData.mnEscapem )
799 {
800 switch(aNewData.mnEscapem)
801 {
802 // close the previous super/sub script.
803 case EXC_FONTESC_NONE: (aFontData.mnEscapem == EXC_FONTESC_SUPER) ? aParaText.append("&X") : aParaText.append("&Y"); break;
804 case EXC_FONTESC_SUPER: aParaText.append("&X"); break;
805 case EXC_FONTESC_SUB: aParaText.append("&Y"); break;
806 default: break;
807 }
808 }
809
810 aFontData = aNewData;
811
812 // --- text content or text fields ---
813
814 const SfxPoolItem* pItem;
815 if( (aSel.nStartPos + 1 == aSel.nEndPos) && // fields are single characters
816 (aEditSet.GetItemState( EE_FEATURE_FIELD, false, &pItem ) == SfxItemState::SET) )
817 {
818 if( const SvxFieldData* pFieldData = static_cast< const SvxFieldItem* >( pItem )->GetField() )
819 {
820 if( dynamic_cast<const SvxPageField*>( pFieldData) != nullptr )
821 aParaText.append("&P");
822 else if( dynamic_cast<const SvxPagesField*>( pFieldData) != nullptr )
823 aParaText.append("&N");
824 else if( dynamic_cast<const SvxDateField*>( pFieldData) != nullptr )
825 aParaText.append("&D");
826 else if( dynamic_cast<const SvxTimeField*>( pFieldData) != nullptr || dynamic_cast<const SvxExtTimeField*>( pFieldData) != nullptr )
827 aParaText.append("&T");
828 else if( dynamic_cast<const SvxTableField*>( pFieldData) != nullptr )
829 aParaText.append("&A");
830 else if( dynamic_cast<const SvxFileField*>( pFieldData) != nullptr ) // title -> file name
831 aParaText.append("&F");
832 else if( const SvxExtFileField* pFileField = dynamic_cast<const SvxExtFileField*>( pFieldData ) )
833 {
834 switch( pFileField->GetFormat() )
835 {
836 case SvxFileFormat::NameAndExt:
837 case SvxFileFormat::NameOnly:
838 aParaText.append("&F");
839 break;
840 case SvxFileFormat::PathOnly:
841 aParaText.append("&Z");
842 break;
843 case SvxFileFormat::PathFull:
844 aParaText.append("&Z&F");
845 break;
846 default:
847 OSL_FAIL( "XclExpHFConverter::AppendPortion - unknown file field" );
848 }
849 }
850 }
851 }
852 else
853 {
854 OUString aPortionText( mrEE.GetText( aSel ) );
855 aPortionText = aPortionText.replaceAll( "&", "&&" );
856 // #i17440# space between font height and numbers in text
857 if( bFontHtChanged && aParaText.getLength() && !aPortionText.isEmpty() )
858 {
859 sal_Unicode cLast = aParaText[ aParaText.getLength() - 1 ];
860 sal_Unicode cFirst = aPortionText[0];
861 if( ('0' <= cLast) && (cLast <= '9') && ('0' <= cFirst) && (cFirst <= '9') )
862 aParaText.append(" ");
863 }
864 aParaText.append(aPortionText);
865 }
866 }
867
868 aSel.nStartPos = aSel.nEndPos;
869 }
870
871 aText = ScGlobal::addToken( aText, aParaText.makeStringAndClear(), '\n' );
872 if( nParaHeight == 0 )
873 nParaHeight = aFontData.mnHeight * 20; // points -> twips
874 nHeight += nParaHeight;
875 }
876
877 mrEE.SetUpdateMode( bOldUpdateMode );
878
879 if( !aText.isEmpty() )
880 {
881 maHFString += "&" + OUStringChar(cPortionCode) + aText;
882 mnTotalHeight = ::std::max( mnTotalHeight, nHeight );
883 }
884 }
885
886 // URL conversion =============================================================
887
888 namespace {
889
890 /** Encodes special parts of the URL, i.e. directory separators and volume names.
891 @param pTableName Pointer to a table name to be encoded in this URL, or 0. */
lclEncodeDosUrl(XclBiff eBiff,const OUString & rUrl,std::u16string_view rBase,const OUString * pTableName)892 OUString lclEncodeDosUrl(
893 XclBiff eBiff, const OUString& rUrl, std::u16string_view rBase, const OUString* pTableName)
894 {
895 OUStringBuffer aBuf;
896
897 if (!rUrl.isEmpty())
898 {
899 OUString aOldUrl = rUrl;
900 aBuf.append(EXC_URLSTART_ENCODED);
901
902 if ( aOldUrl.getLength() > 2 && aOldUrl.startsWith("\\\\") )
903 {
904 // UNC
905 aBuf.append(EXC_URL_DOSDRIVE).append('@');
906 aOldUrl = aOldUrl.copy(2);
907 }
908 else if ( aOldUrl.getLength() > 2 && aOldUrl.match(":\\", 1) )
909 {
910 // drive letter
911 sal_Unicode cThisDrive = rBase.empty() ? ' ' : rBase[0];
912 sal_Unicode cDrive = aOldUrl[0];
913 if (cThisDrive == cDrive)
914 // This document and the referenced document are under the same drive.
915 aBuf.append(EXC_URL_DRIVEROOT);
916 else
917 aBuf.append(EXC_URL_DOSDRIVE).append(cDrive);
918 aOldUrl = aOldUrl.copy(3);
919 }
920 else
921 {
922 // URL probably points to a document on a Unix-like file system
923 aBuf.append(EXC_URL_DRIVEROOT);
924 }
925
926 // directories
927 sal_Int32 nPos = -1;
928 while((nPos = aOldUrl.indexOf('\\')) != -1)
929 {
930 if ( aOldUrl.startsWith("..") )
931 // parent dir (NOTE: the MS-XLS spec doesn't mention this, and
932 // Excel seems confused by this token).
933 aBuf.append(EXC_URL_PARENTDIR);
934 else
935 aBuf.append(aOldUrl.subView(0,nPos)).append(EXC_URL_SUBDIR);
936
937 aOldUrl = aOldUrl.copy(nPos + 1);
938 }
939
940 // file name
941 if (pTableName) // enclose file name in brackets if table name follows
942 aBuf.append('[').append(aOldUrl).append(']');
943 else
944 aBuf.append(aOldUrl);
945 }
946 else // empty URL -> self reference
947 {
948 switch( eBiff )
949 {
950 case EXC_BIFF5:
951 aBuf.append(pTableName ? EXC_URLSTART_SELFENCODED : EXC_URLSTART_SELF);
952 break;
953 case EXC_BIFF8:
954 DBG_ASSERT( pTableName, "lclEncodeDosUrl - sheet name required for BIFF8" );
955 aBuf.append(EXC_URLSTART_SELF);
956 break;
957 default:
958 DBG_ERROR_BIFF();
959 }
960 }
961
962 // table name
963 if (pTableName)
964 aBuf.append(*pTableName);
965
966 // VirtualPath must be shorter than 255 chars ([MS-XLS].pdf 2.5.277)
967 // It's better to truncate, than generate invalid file that Excel cannot open.
968 if (aBuf.getLength() > 255)
969 aBuf.setLength(255);
970
971 return aBuf.makeStringAndClear();
972 }
973
974 } // namespace
975
EncodeUrl(const XclExpRoot & rRoot,const OUString & rAbsUrl,const OUString * pTableName)976 OUString XclExpUrlHelper::EncodeUrl( const XclExpRoot& rRoot, const OUString& rAbsUrl, const OUString* pTableName )
977 {
978 OUString aDosUrl = INetURLObject(rAbsUrl).getFSysPath(FSysStyle::Dos);
979 OUString aDosBase = INetURLObject(rRoot.GetBasePath()).getFSysPath(FSysStyle::Dos);
980 return lclEncodeDosUrl(rRoot.GetBiff(), aDosUrl, aDosBase, pTableName);
981 }
982
EncodeDde(std::u16string_view rApplic,std::u16string_view rTopic)983 OUString XclExpUrlHelper::EncodeDde( std::u16string_view rApplic, std::u16string_view rTopic )
984 {
985 return rApplic + OUStringChar(EXC_DDE_DELIM) + rTopic;
986 }
987
988 // Cached Value Lists =========================================================
989
XclExpCachedMatrix(const ScMatrix & rMatrix)990 XclExpCachedMatrix::XclExpCachedMatrix( const ScMatrix& rMatrix )
991 : mrMatrix( rMatrix )
992 {
993 mrMatrix.IncRef();
994 }
~XclExpCachedMatrix()995 XclExpCachedMatrix::~XclExpCachedMatrix()
996 {
997 mrMatrix.DecRef();
998 }
999
GetDimensions(SCSIZE & nCols,SCSIZE & nRows) const1000 void XclExpCachedMatrix::GetDimensions( SCSIZE & nCols, SCSIZE & nRows ) const
1001 {
1002 mrMatrix.GetDimensions( nCols, nRows );
1003
1004 OSL_ENSURE( nCols && nRows, "XclExpCachedMatrix::GetDimensions - empty matrix" );
1005 OSL_ENSURE( nCols <= 256, "XclExpCachedMatrix::GetDimensions - too many columns" );
1006 }
1007
GetSize() const1008 std::size_t XclExpCachedMatrix::GetSize() const
1009 {
1010 SCSIZE nCols, nRows;
1011
1012 GetDimensions( nCols, nRows );
1013
1014 /* The returned size may be wrong if the matrix contains strings. The only
1015 effect is that the export stream has to update a wrong record size which is
1016 faster than to iterate through all cached values and calculate their sizes. */
1017 return 3 + 9 * (nCols * nRows);
1018 }
1019
Save(XclExpStream & rStrm) const1020 void XclExpCachedMatrix::Save( XclExpStream& rStrm ) const
1021 {
1022 SCSIZE nCols, nRows;
1023
1024 GetDimensions( nCols, nRows );
1025
1026 if( rStrm.GetRoot().GetBiff() <= EXC_BIFF5 )
1027 // in BIFF2-BIFF7: 256 columns represented by 0 columns
1028 rStrm << static_cast< sal_uInt8 >( nCols ) << static_cast< sal_uInt16 >( nRows );
1029 else
1030 // in BIFF8: columns and rows decreased by 1
1031 rStrm << static_cast< sal_uInt8 >( nCols - 1 ) << static_cast< sal_uInt16 >( nRows - 1 );
1032
1033 for( SCSIZE nRow = 0; nRow < nRows; ++nRow )
1034 {
1035 for( SCSIZE nCol = 0; nCol < nCols; ++nCol )
1036 {
1037 ScMatrixValue nMatVal = mrMatrix.Get( nCol, nRow );
1038
1039 FormulaError nScError;
1040 if( ScMatValType::Empty == nMatVal.nType )
1041 {
1042 rStrm.SetSliceSize( 9 );
1043 rStrm << EXC_CACHEDVAL_EMPTY;
1044 rStrm.WriteZeroBytes( 8 );
1045 }
1046 else if( ScMatrix::IsNonValueType( nMatVal.nType ) )
1047 {
1048 XclExpString aStr( nMatVal.GetString().getString(), XclStrFlags::NONE );
1049 rStrm.SetSliceSize( 6 );
1050 rStrm << EXC_CACHEDVAL_STRING << aStr;
1051 }
1052 else if( ScMatValType::Boolean == nMatVal.nType )
1053 {
1054 sal_Int8 nBool = sal_Int8(nMatVal.GetBoolean());
1055 rStrm.SetSliceSize( 9 );
1056 rStrm << EXC_CACHEDVAL_BOOL << nBool;
1057 rStrm.WriteZeroBytes( 7 );
1058 }
1059 else if( (nScError = nMatVal.GetError()) != FormulaError::NONE )
1060 {
1061 sal_Int8 nError ( XclTools::GetXclErrorCode( nScError ) );
1062 rStrm.SetSliceSize( 9 );
1063 rStrm << EXC_CACHEDVAL_ERROR << nError;
1064 rStrm.WriteZeroBytes( 7 );
1065 }
1066 else
1067 {
1068 rStrm.SetSliceSize( 9 );
1069 rStrm << EXC_CACHEDVAL_DOUBLE << nMatVal.fVal;
1070 }
1071 }
1072 }
1073 }
1074
1075 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1076