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