xref: /core/sc/source/filter/excel/xestyle.cxx (revision 05fbb253)
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 <memory>
21 #include <xestyle.hxx>
22 
23 #include <iostream>
24 #include <algorithm>
25 #include <iterator>
26 #include <set>
27 #include <com/sun/star/i18n/ScriptType.hpp>
28 #include <rtl/tencinfo.h>
29 #include <vcl/font.hxx>
30 #include <svl/zformat.hxx>
31 #include <svl/itempool.hxx>
32 #include <svl/languageoptions.hxx>
33 #include <sfx2/printer.hxx>
34 #include <scitems.hxx>
35 #include <svx/algitem.hxx>
36 #include <editeng/boxitem.hxx>
37 #include <editeng/lineitem.hxx>
38 #include <svx/rotmodit.hxx>
39 #include <editeng/colritem.hxx>
40 #include <editeng/brushitem.hxx>
41 #include <editeng/frmdiritem.hxx>
42 #include <editeng/fontitem.hxx>
43 #include <editeng/eeitem.hxx>
44 #include <editeng/escapementitem.hxx>
45 #include <editeng/justifyitem.hxx>
46 #include <document.hxx>
47 #include <stlpool.hxx>
48 #include <stlsheet.hxx>
49 #include <patattr.hxx>
50 #include <attrib.hxx>
51 #include <globstr.hrc>
52 #include <xestring.hxx>
53 #include <conditio.hxx>
54 
55 #include <oox/export/utils.hxx>
56 #include <oox/token/tokens.hxx>
57 #include <oox/token/namespaces.hxx>
58 #include <oox/token/relationship.hxx>
59 #include <o3tl/make_unique.hxx>
60 
61 using namespace ::com::sun::star;
62 using namespace oox;
63 
64 // PALETTE record - color information =========================================
65 
66 namespace {
67 
68 sal_uInt32 lclGetWeighting( XclExpColorType eType )
69 {
70     switch( eType )
71     {
72         case EXC_COLOR_CHARTLINE:   return 1;
73         case EXC_COLOR_CELLBORDER:
74         case EXC_COLOR_CHARTAREA:   return 2;
75         case EXC_COLOR_CELLTEXT:
76         case EXC_COLOR_CHARTTEXT:
77         case EXC_COLOR_CTRLTEXT:    return 10;
78         case EXC_COLOR_TABBG:
79         case EXC_COLOR_CELLAREA:    return 20;
80         case EXC_COLOR_GRID:        return 50;
81         default:    OSL_FAIL( "lclGetWeighting - unknown color type" );
82     }
83     return 1;
84 }
85 
86 sal_Int32 lclGetColorDistance( const Color& rColor1, const Color& rColor2 )
87 {
88     sal_Int32 nDist = rColor1.GetRed() - rColor2.GetRed();
89     nDist *= nDist * 77;
90     sal_Int32 nDummy = rColor1.GetGreen() - rColor2.GetGreen();
91     nDist += nDummy * nDummy * 151;
92     nDummy = rColor1.GetBlue() - rColor2.GetBlue();
93     nDist += nDummy * nDummy * 28;
94     return nDist;
95 }
96 
97 sal_uInt8 lclGetMergedColorComp( sal_uInt8 nComp1, sal_uInt32 nWeight1, sal_uInt8 nComp2, sal_uInt32 nWeight2 )
98 {
99     sal_uInt8 nComp1Dist = ::std::min< sal_uInt8 >( nComp1, 0xFF - nComp1 );
100     sal_uInt8 nComp2Dist = ::std::min< sal_uInt8 >( nComp2, 0xFF - nComp2 );
101     if( nComp1Dist != nComp2Dist )
102     {
103         /*  #i36945# One of the passed RGB components is nearer at the limits (0x00 or 0xFF).
104             Increase its weighting to prevent fading of the colors during reduction. */
105         const sal_uInt8& rnCompNearer = (nComp1Dist < nComp2Dist) ? nComp1 : nComp2;
106         sal_uInt32& rnWeight = (nComp1Dist < nComp2Dist) ? nWeight1 : nWeight2;
107         rnWeight *= ((rnCompNearer - 0x80L) * (rnCompNearer - 0x7FL) / 0x1000L + 1);
108     }
109     sal_uInt32 nWSum = nWeight1 + nWeight2;
110     return static_cast< sal_uInt8 >( (nComp1 * nWeight1 + nComp2 * nWeight2 + nWSum / 2) / nWSum );
111 }
112 
113 void lclSetMixedColor( Color& rDest, const Color& rSrc1, const Color& rSrc2 )
114 {
115     rDest.SetRed( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetRed() ) + rSrc2.GetRed()) / 2 ) );
116     rDest.SetGreen( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetGreen() ) + rSrc2.GetGreen()) / 2 ) );
117     rDest.SetBlue( static_cast< sal_uInt8 >( (static_cast< sal_uInt16 >( rSrc1.GetBlue() ) + rSrc2.GetBlue()) / 2 ) );
118 }
119 
120 } // namespace
121 
122 // additional classes for color reduction -------------------------------------
123 
124 namespace {
125 
126 /** Represents an entry in a color list.
127 
128     The color stores a weighting value, which increases the more the color is
129     used in the document. Heavy-weighted colors will change less than others on
130     color reduction.
131  */
132 class XclListColor
133 {
134     DECL_FIXEDMEMPOOL_NEWDEL( XclListColor )
135 
136 private:
137     Color               maColor;        /// The color value of this palette entry.
138     sal_uInt32          mnColorId;      /// Unique color ID for color reduction.
139     sal_uInt32          mnWeight;       /// Weighting for color reduction.
140     bool                mbBaseColor;    /// true = Handle as base color, (don't remove/merge).
141 
142 public:
143     explicit            XclListColor( const Color& rColor, sal_uInt32 nColorId );
144 
145     /** Returns the RGB color value of the color. */
146     const Color& GetColor() const { return maColor; }
147     /** Returns the unique ID of the color. */
148     sal_uInt32   GetColorId() const { return mnColorId; }
149     /** Returns the current weighting of the color. */
150     sal_uInt32   GetWeighting() const { return mnWeight; }
151     /** Returns true, if this color is a base color, i.e. it will not be removed or merged. */
152     bool         IsBaseColor() const { return mbBaseColor; }
153 
154     /** Adds the passed weighting to this color. */
155     void         AddWeighting( sal_uInt32 nWeight ) { mnWeight += nWeight; }
156     /** Merges this color with rColor, regarding weighting settings. */
157     void                Merge( const XclListColor& rColor );
158 };
159 
160 IMPL_FIXEDMEMPOOL_NEWDEL( XclListColor )
161 
162 XclListColor::XclListColor( const Color& rColor, sal_uInt32 nColorId ) :
163     maColor( rColor ),
164     mnColorId( nColorId ),
165     mnWeight( 0 )
166 {
167     mbBaseColor =
168         ((rColor.GetRed()   == 0x00) || (rColor.GetRed()   == 0xFF)) &&
169         ((rColor.GetGreen() == 0x00) || (rColor.GetGreen() == 0xFF)) &&
170         ((rColor.GetBlue()  == 0x00) || (rColor.GetBlue()  == 0xFF));
171 }
172 
173 void XclListColor::Merge( const XclListColor& rColor )
174 {
175     sal_uInt32 nWeight2 = rColor.GetWeighting();
176     // do not change RGB value of base colors
177     if( !mbBaseColor )
178     {
179         maColor.SetRed(   lclGetMergedColorComp( maColor.GetRed(),   mnWeight, rColor.maColor.GetRed(),   nWeight2 ) );
180         maColor.SetGreen( lclGetMergedColorComp( maColor.GetGreen(), mnWeight, rColor.maColor.GetGreen(), nWeight2 ) );
181         maColor.SetBlue(  lclGetMergedColorComp( maColor.GetBlue(),  mnWeight, rColor.maColor.GetBlue(),  nWeight2 ) );
182     }
183     AddWeighting( nWeight2 );
184 }
185 
186 /** Data for each inserted original color, represented by a color ID. */
187 struct XclColorIdData
188 {
189     Color               maColor;        /// The original inserted color.
190     sal_uInt32          mnIndex;        /// Maps current color ID to color list or export color vector.
191     /** Sets the contents of this struct. */
192     void         Set( const Color& rColor, sal_uInt32 nIndex ) { maColor = rColor; mnIndex = nIndex; }
193 };
194 
195 /** A color that will be written to the Excel file. */
196 struct XclPaletteColor
197 {
198     Color               maColor;        /// Resulting color to export.
199     bool                mbUsed;         /// true = Entry is used in the document.
200 
201     explicit     XclPaletteColor( const Color& rColor ) : maColor( rColor ), mbUsed( false ) {}
202     void         SetColor( const Color& rColor ) { maColor = rColor; mbUsed = true; }
203 };
204 
205 /** Maps a color list index to a palette index.
206     @descr  Used to remap the color ID data vector from list indexes to palette indexes. */
207 struct XclRemap
208 {
209     sal_uInt32          mnPalIndex;     /// Index to palette.
210     bool                mbProcessed;    /// true = List color already processed.
211 
212     explicit     XclRemap() : mnPalIndex( 0 ), mbProcessed( false ) {}
213     void         SetIndex( sal_uInt32 nPalIndex )
214                             { mnPalIndex = nPalIndex; mbProcessed = true; }
215 };
216 
217 /** Stores the nearest palette color index of a list color. */
218 struct XclNearest
219 {
220     sal_uInt32          mnPalIndex;     /// Index to nearest palette color.
221     sal_Int32           mnDist;         /// Distance to palette color.
222 
223     explicit     XclNearest() : mnPalIndex( 0 ), mnDist( 0 ) {}
224 };
225 
226 } // namespace
227 
228 class XclExpPaletteImpl
229 {
230 public:
231     explicit            XclExpPaletteImpl( const XclDefaultPalette& rDefPal );
232 
233     /** Inserts the color into the list and updates weighting.
234         @param nAutoDefault  The Excel palette index for automatic color.
235         @return  A unique ID for this color. */
236     sal_uInt32          InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault = 0 );
237     /** Returns the color ID representing a fixed Excel palette index (i.e. for auto colors). */
238     static sal_uInt32   GetColorIdFromIndex( sal_uInt16 nIndex );
239 
240     /** Reduces the color list to the maximum count of the current BIFF version. */
241     void                Finalize();
242 
243     /** Returns the Excel palette index of the color with passed color ID. */
244     sal_uInt16          GetColorIndex( sal_uInt32 nColorId ) const;
245 
246     /** Returns a foreground and background color for the two passed color IDs.
247         @descr  If rnXclPattern contains a solid pattern, this function tries to find
248         the two best fitting colors and a mix pattern (25%, 50% or 75%) for nForeColorId.
249         This will result in a better approximation to the passed foreground color. */
250     void                GetMixedColors(
251                             sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
252                             sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const;
253 
254     /** Returns the RGB color data for a (non-zero-based) Excel palette entry.
255         @return  The color from current or default palette or COL_AUTO, if nothing else found. */
256     ColorData           GetColorData( sal_uInt16 nXclIndex ) const;
257 
258     /** Returns true, if all colors of the palette are equal to default palette colors. */
259     bool                IsDefaultPalette() const;
260     /** Writes the color list (contents of the palette record) to the passed stream. */
261     void                WriteBody( XclExpStream& rStrm );
262     void                SaveXml( XclExpXmlStream& rStrm );
263 
264 private:
265     /** Returns the Excel index of a 0-based color index. */
266     static sal_uInt16 GetXclIndex( sal_uInt32 nIndex )
267                             { return static_cast< sal_uInt16 >( nIndex + EXC_COLOR_USEROFFSET ); }
268 
269     /** Returns the original inserted color represented by the color ID nColorId. */
270     const Color&        GetOriginalColor( sal_uInt32 nColorId ) const;
271 
272     /** Searches for rColor, returns the ordered insertion index for rColor in rnIndex. */
273     XclListColor*       SearchListEntry( const Color& rColor, sal_uInt32& rnIndex );
274     /** Creates and inserts a new color list entry at the specified list position. */
275     XclListColor*       CreateListEntry( const Color& rColor, sal_uInt32 nIndex );
276 
277     /** Raw and fast reduction of the palette. */
278     void                RawReducePalette( sal_uInt32 nPass );
279     /** Reduction of one color using advanced color merging based on color weighting. */
280     void                ReduceLeastUsedColor();
281 
282     /** Finds the least used color and returns its current list index. */
283     sal_uInt32          GetLeastUsedListColor() const;
284     /** Returns the list index of the color nearest to rColor.
285         @param nIgnore  List index of a color which will be ignored.
286         @return  The list index of the found color. */
287     sal_uInt32          GetNearestListColor( const Color& rColor, sal_uInt32 nIgnore ) const;
288     /** Returns the list index of the color nearest to the color with list index nIndex. */
289     sal_uInt32          GetNearestListColor( sal_uInt32 nIndex ) const;
290 
291     /** Returns in rnIndex the palette index of the color nearest to rColor.
292         Searches for default colors only (colors never replaced).
293         @return  The distance from passed color to found color. */
294     sal_Int32           GetNearestPaletteColor(
295                             sal_uInt32& rnIndex,
296                             const Color& rColor ) const;
297     /** Returns in rnFirst and rnSecond the palette indexes of the two colors nearest to rColor.
298         @return  The minimum distance from passed color to found colors. */
299     sal_Int32           GetNearPaletteColors(
300                             sal_uInt32& rnFirst, sal_uInt32& rnSecond,
301                             const Color& rColor ) const;
302 
303 private:
304     typedef std::vector< std::unique_ptr<XclListColor> >     XclListColorList;
305     typedef std::shared_ptr< XclListColorList > XclListColorListRef;
306     typedef ::std::vector< XclColorIdData >       XclColorIdDataVec;
307     typedef ::std::vector< XclPaletteColor >      XclPaletteColorVec;
308 
309     const XclDefaultPalette& mrDefPal;      /// The default palette for the current BIFF version.
310     XclListColorListRef mxColorList;        /// Working color list.
311     XclColorIdDataVec   maColorIdDataVec;   /// Data of all CIDs.
312     XclPaletteColorVec  maPalette;          /// Contains resulting colors to export.
313     sal_uInt32          mnLastIdx;          /// Last insertion index for search opt.
314 };
315 
316 const sal_uInt32 EXC_PAL_INDEXBASE          = 0xFFFF0000;
317 const sal_uInt32 EXC_PAL_MAXRAWSIZE         = 1024;
318 
319 XclExpPaletteImpl::XclExpPaletteImpl( const XclDefaultPalette& rDefPal ) :
320     mrDefPal( rDefPal ),
321     mxColorList( new XclListColorList ),
322     mnLastIdx( 0 )
323 {
324     // initialize maPalette with default colors
325     sal_uInt16 nCount = static_cast< sal_uInt16 >( mrDefPal.GetColorCount() );
326     maPalette.reserve( nCount );
327     for( sal_uInt16 nIdx = 0; nIdx < nCount; ++nIdx )
328         maPalette.emplace_back( mrDefPal.GetDefColor( GetXclIndex( nIdx ) ) );
329 
330     InsertColor( Color( COL_BLACK ), EXC_COLOR_CELLTEXT );
331 }
332 
333 sal_uInt32 XclExpPaletteImpl::InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault )
334 {
335     if( rColor.GetColor() == COL_AUTO )
336         return GetColorIdFromIndex( nAutoDefault );
337 
338     sal_uInt32 nFoundIdx = 0;
339     XclListColor* pEntry = SearchListEntry( rColor, nFoundIdx );
340     if( !pEntry || (pEntry->GetColor() != rColor) )
341         pEntry = CreateListEntry( rColor, nFoundIdx );
342     pEntry->AddWeighting( lclGetWeighting( eType ) );
343 
344     return pEntry->GetColorId();
345 }
346 
347 sal_uInt32 XclExpPaletteImpl::GetColorIdFromIndex( sal_uInt16 nIndex )
348 {
349     return EXC_PAL_INDEXBASE | nIndex;
350 }
351 
352 void XclExpPaletteImpl::Finalize()
353 {
354 // --- build initial color ID data vector (maColorIdDataVec) ---
355 
356     sal_uInt32 nCount = mxColorList->size();
357     maColorIdDataVec.resize( nCount );
358     for( sal_uInt32 nIdx = 0; nIdx < nCount; ++nIdx )
359     {
360         const XclListColor& listColor = *mxColorList->at( nIdx ).get();
361         maColorIdDataVec[ listColor.GetColorId() ].Set( listColor.GetColor(), nIdx );
362     }
363 
364 // --- loop as long as current color count does not fit into palette of current BIFF ---
365 
366     // phase 1: raw reduction (performance reasons, #i36945#)
367     sal_uInt32 nPass = 0;
368     while( mxColorList->size() > EXC_PAL_MAXRAWSIZE )
369         RawReducePalette( nPass++ );
370 
371     // phase 2: precise reduction using advanced color merging based on color weighting
372     while( mxColorList->size() > mrDefPal.GetColorCount() )
373         ReduceLeastUsedColor();
374 
375 // --- use default palette and replace colors with nearest used colors ---
376 
377     nCount = mxColorList->size();
378     std::vector< XclRemap > aRemapVec( nCount );
379     std::vector< XclNearest > aNearestVec( nCount );
380 
381     // in each run: search the best fitting color and replace a default color with it
382     for( sal_uInt32 nRun = 0; nRun < nCount; ++nRun )
383     {
384         sal_uInt32 nIndex;
385         // find nearest unused default color for each unprocessed list color
386         for( nIndex = 0; nIndex < nCount; ++nIndex )
387             aNearestVec[ nIndex ].mnDist = aRemapVec[ nIndex ].mbProcessed ? SAL_MAX_INT32 :
388                 GetNearestPaletteColor( aNearestVec[ nIndex ].mnPalIndex, mxColorList->at( nIndex )->GetColor() );
389         // find the list color which is nearest to a default color
390         sal_uInt32 nFound = 0;
391         for( nIndex = 1; nIndex < nCount; ++nIndex )
392             if( aNearestVec[ nIndex ].mnDist < aNearestVec[ nFound ].mnDist )
393                 nFound = nIndex;
394         // replace default color with list color
395         sal_uInt32 nNearest = aNearestVec[ nFound ].mnPalIndex;
396         OSL_ENSURE( nNearest < maPalette.size(), "XclExpPaletteImpl::Finalize - algorithm error" );
397         maPalette[ nNearest ].SetColor( mxColorList->at( nFound )->GetColor() );
398         aRemapVec[ nFound ].SetIndex( nNearest );
399     }
400 
401     // remap color ID data map (maColorIdDataVec) from list indexes to palette indexes
402     for( XclColorIdDataVec::iterator aIt = maColorIdDataVec.begin(), aEnd = maColorIdDataVec.end(); aIt != aEnd; ++aIt )
403         aIt->mnIndex = aRemapVec[ aIt->mnIndex ].mnPalIndex;
404 }
405 
406 sal_uInt16 XclExpPaletteImpl::GetColorIndex( sal_uInt32 nColorId ) const
407 {
408     sal_uInt16 nRet = 0;
409     if( nColorId >= EXC_PAL_INDEXBASE )
410         nRet = static_cast< sal_uInt16 >( nColorId & ~EXC_PAL_INDEXBASE );
411     else if( nColorId < maColorIdDataVec.size() )
412         nRet = GetXclIndex( maColorIdDataVec[ nColorId ].mnIndex );
413     return nRet;
414 }
415 
416 void XclExpPaletteImpl::GetMixedColors(
417         sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
418         sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const
419 {
420     rnXclForeIx = GetColorIndex( nForeColorId );
421     rnXclBackIx = GetColorIndex( nBackColorId );
422     if( (rnXclPattern != EXC_PATT_SOLID) || (nForeColorId >= maColorIdDataVec.size()) )
423         return;
424 
425     // now we have solid pattern, and a defined foreground (background doesn't care for solid pattern)
426 
427     sal_uInt32 nIndex1, nIndex2;
428     Color aForeColor( GetOriginalColor( nForeColorId ) );
429     sal_Int32 nFirstDist = GetNearPaletteColors( nIndex1, nIndex2, aForeColor );
430     if( (nIndex1 >= maPalette.size()) || (nIndex2 >= maPalette.size()) )
431         return;
432 
433     Color aColorArr[ 5 ];
434     aColorArr[ 0 ] = maPalette[ nIndex1 ].maColor;
435     aColorArr[ 4 ] = maPalette[ nIndex2 ].maColor;
436     lclSetMixedColor( aColorArr[ 2 ], aColorArr[ 0 ], aColorArr[ 4 ] );
437     lclSetMixedColor( aColorArr[ 1 ], aColorArr[ 0 ], aColorArr[ 2 ] );
438     lclSetMixedColor( aColorArr[ 3 ], aColorArr[ 2 ], aColorArr[ 4 ] );
439 
440     sal_Int32 nMinDist = nFirstDist;
441     sal_uInt32 nMinIndex = 0;
442     for( sal_uInt32 nCnt = 1; nCnt < 4; ++nCnt )
443     {
444         sal_Int32 nDist = lclGetColorDistance( aForeColor, aColorArr[ nCnt ] );
445         if( nDist < nMinDist )
446         {
447             nMinDist = nDist;
448             nMinIndex = nCnt;
449         }
450     }
451     rnXclForeIx = GetXclIndex( nIndex1 );
452     rnXclBackIx = GetXclIndex( nIndex2 );
453     if( nMinDist < nFirstDist )
454     {
455         switch( nMinIndex )
456         {
457             case 1: rnXclPattern = EXC_PATT_75_PERC;    break;
458             case 2: rnXclPattern = EXC_PATT_50_PERC;    break;
459             case 3: rnXclPattern = EXC_PATT_25_PERC;    break;
460         }
461     }
462 }
463 
464 ColorData XclExpPaletteImpl::GetColorData( sal_uInt16 nXclIndex ) const
465 {
466     if( nXclIndex >= EXC_COLOR_USEROFFSET )
467     {
468         sal_uInt32 nIdx = nXclIndex - EXC_COLOR_USEROFFSET;
469         if( nIdx < maPalette.size() )
470             return maPalette[ nIdx ].maColor.GetColor();
471     }
472     return mrDefPal.GetDefColorData( nXclIndex );
473 }
474 
475 bool XclExpPaletteImpl::IsDefaultPalette() const
476 {
477     bool bDefault = true;
478     for( sal_uInt32 nIdx = 0, nSize = static_cast< sal_uInt32 >( maPalette.size() ); bDefault && (nIdx < nSize); ++nIdx )
479         bDefault = maPalette[ nIdx ].maColor == mrDefPal.GetDefColor( GetXclIndex( nIdx ) );
480     return bDefault;
481 }
482 
483 void XclExpPaletteImpl::WriteBody( XclExpStream& rStrm )
484 {
485     rStrm << static_cast< sal_uInt16 >( maPalette.size() );
486     for( XclPaletteColorVec::const_iterator aIt = maPalette.begin(), aEnd = maPalette.end(); aIt != aEnd; ++aIt )
487         rStrm << aIt->maColor;
488 }
489 
490 void XclExpPaletteImpl::SaveXml( XclExpXmlStream& rStrm )
491 {
492     if( maPalette.empty() )
493         return;
494 
495     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
496     rStyleSheet->startElement( XML_colors, FSEND );
497     rStyleSheet->startElement( XML_indexedColors, FSEND );
498     for( XclPaletteColorVec::const_iterator aIt = maPalette.begin(), aEnd = maPalette.end(); aIt != aEnd; ++aIt )
499         rStyleSheet->singleElement( XML_rgbColor,
500                 XML_rgb,    XclXmlUtils::ToOString( aIt->maColor ).getStr(),
501                 FSEND );
502     rStyleSheet->endElement( XML_indexedColors );
503     rStyleSheet->endElement( XML_colors );
504 }
505 
506 const Color& XclExpPaletteImpl::GetOriginalColor( sal_uInt32 nColorId ) const
507 {
508     if( nColorId < maColorIdDataVec.size() )
509         return maColorIdDataVec[ nColorId ].maColor;
510     return maPalette[ 0 ].maColor;
511 }
512 
513 XclListColor* XclExpPaletteImpl::SearchListEntry( const Color& rColor, sal_uInt32& rnIndex )
514 {
515     rnIndex = 0;
516 
517     if (mxColorList->empty())
518         return nullptr;
519 
520     XclListColor* pEntry = nullptr;
521 
522     // search optimization for equal-colored objects occurring repeatedly
523     if (mnLastIdx < mxColorList->size())
524     {
525         pEntry = (*mxColorList)[mnLastIdx].get();
526         if( pEntry->GetColor() == rColor )
527         {
528             rnIndex = mnLastIdx;
529             return pEntry;
530         }
531     }
532 
533     // binary search for color
534     sal_uInt32 nBegIdx = 0;
535     sal_uInt32 nEndIdx = mxColorList->size();
536     bool bFound = false;
537     while( !bFound && (nBegIdx < nEndIdx) )
538     {
539         rnIndex = (nBegIdx + nEndIdx) / 2;
540         pEntry = (*mxColorList)[rnIndex].get();
541         bFound = pEntry->GetColor() == rColor;
542         if( !bFound )
543         {
544             if( pEntry->GetColor().GetColor() < rColor.GetColor() )
545                 nBegIdx = rnIndex + 1;
546             else
547                 nEndIdx = rnIndex;
548         }
549     }
550 
551     // not found - use end of range as new insertion position
552     if( !bFound )
553         rnIndex = nEndIdx;
554 
555     mnLastIdx = rnIndex;
556     return pEntry;
557 }
558 
559 XclListColor* XclExpPaletteImpl::CreateListEntry( const Color& rColor, sal_uInt32 nIndex )
560 {
561     XclListColor* pEntry = new XclListColor( rColor, mxColorList->size() );
562     mxColorList->insert(mxColorList->begin() + nIndex, std::unique_ptr<XclListColor>(pEntry));
563     return pEntry;
564 }
565 
566 void XclExpPaletteImpl::RawReducePalette( sal_uInt32 nPass )
567 {
568     /*  Fast palette reduction - in each call of this function one RGB component
569         of each color is reduced to a lower number of distinct values.
570         Pass 0: Blue is reduced to 128 distinct values.
571         Pass 1: Red is reduced to 128 distinct values.
572         Pass 2: Green is reduced to 128 distinct values.
573         Pass 3: Blue is reduced to 64 distinct values.
574         Pass 4: Red is reduced to 64 distinct values.
575         Pass 5: Green is reduced to 64 distinct values.
576         And so on...
577      */
578 
579     XclListColorListRef xOldList = mxColorList;
580     mxColorList.reset( new XclListColorList );
581 
582     // maps old list indexes to new list indexes, used to update maColorIdDataVec
583     ScfUInt32Vec aListIndexMap;
584     aListIndexMap.reserve( xOldList->size() );
585 
586     // preparations
587     sal_uInt8 nR, nG, nB;
588     sal_uInt8& rnComp = ((nPass % 3 == 0) ? nB : ((nPass % 3 == 1) ? nR : nG));
589     nPass /= 3;
590     OSL_ENSURE( nPass < 7, "XclExpPaletteImpl::RawReducePalette - reduction not terminated" );
591 
592     static const sal_uInt8 spnFactor2[] = { 0x81, 0x82, 0x84, 0x88, 0x92, 0xAA, 0xFF };
593     sal_uInt8 nFactor1 = static_cast< sal_uInt8 >( 0x02 << nPass );
594     sal_uInt8 nFactor2 = spnFactor2[ nPass ];
595     sal_uInt8 nFactor3 = static_cast< sal_uInt8 >( 0x40 >> nPass );
596 
597     // process each color in the old color list
598     for(std::unique_ptr<XclListColor> & pOldColor : *xOldList)
599     {
600         // get the old list entry
601         const XclListColor* pOldEntry = pOldColor.get();
602         nR = pOldEntry->GetColor().GetRed();
603         nG = pOldEntry->GetColor().GetGreen();
604         nB = pOldEntry->GetColor().GetBlue();
605 
606         /*  Calculate the new RGB component (rnComp points to one of nR, nG, nB).
607             Using integer arithmetic with its rounding errors, the results of
608             this calculation are always exactly in the range 0x00 to 0xFF
609             (simply cutting the lower bits would darken the colors slightly). */
610         sal_uInt32 nNewComp = rnComp;
611         nNewComp /= nFactor1;
612         nNewComp *= nFactor2;
613         nNewComp /= nFactor3;
614         rnComp = static_cast< sal_uInt8 >( nNewComp );
615         Color aNewColor( nR, nG, nB );
616 
617         // find or insert the new color
618         sal_uInt32 nFoundIdx = 0;
619         XclListColor* pNewEntry = SearchListEntry( aNewColor, nFoundIdx );
620         if( !pNewEntry || (pNewEntry->GetColor() != aNewColor) )
621             pNewEntry = CreateListEntry( aNewColor, nFoundIdx );
622         pNewEntry->AddWeighting( pOldEntry->GetWeighting() );
623         aListIndexMap.push_back( nFoundIdx );
624     }
625 
626     // update color ID data map (maps color IDs to color list indexes), replace old by new list indexes
627     for( XclColorIdDataVec::iterator aIt = maColorIdDataVec.begin(), aEnd = maColorIdDataVec.end(); aIt != aEnd; ++aIt )
628         aIt->mnIndex = aListIndexMap[ aIt->mnIndex ];
629 }
630 
631 void XclExpPaletteImpl::ReduceLeastUsedColor()
632 {
633     // find a list color to remove
634     sal_uInt32 nRemove = GetLeastUsedListColor();
635     // find its nearest neighbor
636     sal_uInt32 nKeep = GetNearestListColor( nRemove );
637 
638     // merge both colors to one color, remove one color from list
639     XclListColor* pKeepEntry = mxColorList->at(nKeep).get();
640     XclListColor* pRemoveEntry = mxColorList->at(nRemove).get();
641     if( pKeepEntry && pRemoveEntry )
642     {
643         // merge both colors (if pKeepEntry is a base color, it will not change)
644         pKeepEntry->Merge( *pRemoveEntry );
645         // remove the less used color, adjust nKeep index if kept color follows removed color
646         XclListColorList::iterator itr = mxColorList->begin();
647         ::std::advance(itr, nRemove);
648         mxColorList->erase(itr);
649         if( nKeep > nRemove ) --nKeep;
650 
651         // recalculate color ID data map (maps color IDs to color list indexes)
652         for( XclColorIdDataVec::iterator aIt = maColorIdDataVec.begin(), aEnd = maColorIdDataVec.end(); aIt != aEnd; ++aIt )
653         {
654             if( aIt->mnIndex > nRemove )
655                 --aIt->mnIndex;
656             else if( aIt->mnIndex == nRemove )
657                 aIt->mnIndex = nKeep;
658         }
659     }
660 }
661 
662 sal_uInt32 XclExpPaletteImpl::GetLeastUsedListColor() const
663 {
664     sal_uInt32 nFound = 0;
665     sal_uInt32 nMinW = SAL_MAX_UINT32;
666 
667     for( sal_uInt32 nIdx = 0, nCount = mxColorList->size(); nIdx < nCount; ++nIdx )
668     {
669         XclListColor& rEntry = *mxColorList->at( nIdx ).get();
670         // ignore the base colors
671         if( !rEntry.IsBaseColor() && (rEntry.GetWeighting() < nMinW) )
672         {
673             nFound = nIdx;
674             nMinW = rEntry.GetWeighting();
675         }
676     }
677     return nFound;
678 }
679 
680 sal_uInt32 XclExpPaletteImpl::GetNearestListColor( const Color& rColor, sal_uInt32 nIgnore ) const
681 {
682     sal_uInt32 nFound = 0;
683     sal_Int32 nMinD = SAL_MAX_INT32;
684 
685     for( sal_uInt32 nIdx = 0, nCount = mxColorList->size(); nIdx < nCount; ++nIdx )
686     {
687         if( nIdx != nIgnore )
688         {
689             if( XclListColor* pEntry = mxColorList->at(nIdx).get() )
690             {
691                 sal_Int32 nDist = lclGetColorDistance( rColor, pEntry->GetColor() );
692                 if( nDist < nMinD )
693                 {
694                     nFound = nIdx;
695                     nMinD = nDist;
696                 }
697             }
698         }
699     }
700     return nFound;
701 }
702 
703 sal_uInt32 XclExpPaletteImpl::GetNearestListColor( sal_uInt32 nIndex ) const
704 {
705     if (nIndex >= mxColorList->size())
706         return 0;
707     XclListColor* pEntry = mxColorList->at(nIndex).get();
708     return GetNearestListColor( pEntry->GetColor(), nIndex );
709 }
710 
711 sal_Int32 XclExpPaletteImpl::GetNearestPaletteColor(
712         sal_uInt32& rnIndex, const Color& rColor ) const
713 {
714     rnIndex = 0;
715     sal_Int32 nDist = SAL_MAX_INT32;
716 
717     for( XclPaletteColorVec::const_iterator aIt = maPalette.begin(), aEnd = maPalette.end();
718             aIt != aEnd; ++aIt )
719     {
720         if( !aIt->mbUsed )
721         {
722             sal_Int32 nCurrDist = lclGetColorDistance( rColor, aIt->maColor );
723             if( nCurrDist < nDist )
724             {
725                 rnIndex = aIt - maPalette.begin();
726                 nDist = nCurrDist;
727             }
728         }
729     }
730     return nDist;
731 }
732 
733 sal_Int32 XclExpPaletteImpl::GetNearPaletteColors(
734         sal_uInt32& rnFirst, sal_uInt32& rnSecond, const Color& rColor ) const
735 {
736     rnFirst = rnSecond = 0;
737     sal_Int32 nDist1 = SAL_MAX_INT32;
738     sal_Int32 nDist2 = SAL_MAX_INT32;
739 
740     for( XclPaletteColorVec::const_iterator aIt = maPalette.begin(), aEnd = maPalette.end();
741             aIt != aEnd; ++aIt )
742     {
743         sal_Int32 nCurrDist = lclGetColorDistance( rColor, aIt->maColor );
744         if( nCurrDist < nDist1 )
745         {
746             rnSecond = rnFirst;
747             nDist2 = nDist1;
748             rnFirst = aIt - maPalette.begin();
749             nDist1 = nCurrDist;
750         }
751         else if( nCurrDist < nDist2 )
752         {
753             rnSecond = aIt - maPalette.begin();
754             nDist2 = nCurrDist;
755         }
756     }
757     return nDist1;
758 }
759 
760 XclExpPalette::XclExpPalette( const XclExpRoot& rRoot ) :
761     XclDefaultPalette( rRoot ),
762     XclExpRecord( EXC_ID_PALETTE )
763 {
764     mxImpl.reset( new XclExpPaletteImpl( *this ) );
765     SetRecSize( GetColorCount() * 4 + 2 );
766 }
767 
768 XclExpPalette::~XclExpPalette()
769 {
770 }
771 
772 sal_uInt32 XclExpPalette::InsertColor( const Color& rColor, XclExpColorType eType, sal_uInt16 nAutoDefault )
773 {
774     return mxImpl->InsertColor( rColor, eType, nAutoDefault );
775 }
776 
777 sal_uInt32 XclExpPalette::GetColorIdFromIndex( sal_uInt16 nIndex )
778 {
779     return XclExpPaletteImpl::GetColorIdFromIndex( nIndex );
780 }
781 
782 void XclExpPalette::Finalize()
783 {
784     mxImpl->Finalize();
785 }
786 
787 sal_uInt16 XclExpPalette::GetColorIndex( sal_uInt32 nColorId ) const
788 {
789     return mxImpl->GetColorIndex( nColorId );
790 }
791 
792 void XclExpPalette::GetMixedColors(
793         sal_uInt16& rnXclForeIx, sal_uInt16& rnXclBackIx, sal_uInt8& rnXclPattern,
794         sal_uInt32 nForeColorId, sal_uInt32 nBackColorId ) const
795 {
796     return mxImpl->GetMixedColors( rnXclForeIx, rnXclBackIx, rnXclPattern, nForeColorId, nBackColorId );
797 }
798 
799 Color XclExpPalette::GetColor( sal_uInt16 nXclIndex ) const
800 {
801     return Color(mxImpl->GetColorData( nXclIndex ));
802 }
803 
804 void XclExpPalette::Save( XclExpStream& rStrm )
805 {
806     if( !mxImpl->IsDefaultPalette() )
807         XclExpRecord::Save( rStrm );
808 }
809 
810 void XclExpPalette::SaveXml( XclExpXmlStream& rStrm )
811 {
812     if( !mxImpl->IsDefaultPalette() )
813         mxImpl->SaveXml( rStrm );
814 }
815 
816 void XclExpPalette::WriteBody( XclExpStream& rStrm )
817 {
818     mxImpl->WriteBody( rStrm );
819 }
820 
821 // FONT record - font information =============================================
822 
823 namespace {
824 
825 typedef ::std::pair< sal_uInt16, sal_Int16 > WhichAndScript;
826 
827 sal_Int16 lclCheckFontItems( const SfxItemSet& rItemSet,
828         const WhichAndScript& rWAS1, const WhichAndScript& rWAS2, const WhichAndScript& rWAS3 )
829 {
830     if( ScfTools::CheckItem( rItemSet, rWAS1.first, false ) ) return rWAS1.second;
831     if( ScfTools::CheckItem( rItemSet, rWAS2.first, false ) ) return rWAS2.second;
832     if( ScfTools::CheckItem( rItemSet, rWAS3.first, false ) ) return rWAS3.second;
833     return 0;
834 };
835 
836 } // namespace
837 
838 sal_Int16 XclExpFontHelper::GetFirstUsedScript( const XclExpRoot& rRoot, const SfxItemSet& rItemSet )
839 {
840     namespace ApiScriptType = ::com::sun::star::i18n::ScriptType;
841 
842     /*  #i17050# #i107170# We need to determine which font items are set in the
843         item set, and which script type we should prefer according to the
844         current language settings. */
845 
846     static const WhichAndScript WAS_LATIN( ATTR_FONT, css::i18n::ScriptType::LATIN );
847     static const WhichAndScript WAS_ASIAN( ATTR_CJK_FONT, css::i18n::ScriptType::ASIAN );
848     static const WhichAndScript WAS_CMPLX( ATTR_CTL_FONT, css::i18n::ScriptType::COMPLEX );
849 
850     /*  do not let a font from a parent style override an explicit
851         cell font. */
852 
853     sal_Int16 nDefScript = rRoot.GetDefApiScript();
854     sal_Int16 nScript = 0;
855     const SfxItemSet* pCurrSet = &rItemSet;
856 
857     while( (nScript == 0) && pCurrSet )
858     {
859         switch( nDefScript )
860         {
861             case ApiScriptType::LATIN:
862                 nScript = lclCheckFontItems( *pCurrSet, WAS_LATIN, WAS_CMPLX, WAS_ASIAN );
863             break;
864             case ApiScriptType::ASIAN:
865                 nScript = lclCheckFontItems( *pCurrSet, WAS_ASIAN, WAS_CMPLX, WAS_LATIN );
866             break;
867             case ApiScriptType::COMPLEX:
868                 nScript = lclCheckFontItems( *pCurrSet, WAS_CMPLX, WAS_ASIAN, WAS_LATIN );
869             break;
870             default:
871                 OSL_FAIL( "XclExpFontHelper::GetFirstUsedScript - unknown script type" );
872                 nScript = ApiScriptType::LATIN;
873         };
874         pCurrSet = pCurrSet->GetParent();
875     }
876 
877     if (nScript == 0)
878         nScript = nDefScript;
879 
880     if (nScript == 0)
881     {
882         OSL_FAIL( "XclExpFontHelper::GetFirstUsedScript - unknown script type" );
883         nScript = ApiScriptType::LATIN;
884     }
885 
886     return nScript;
887 }
888 
889 vcl::Font XclExpFontHelper::GetFontFromItemSet( const XclExpRoot& rRoot, const SfxItemSet& rItemSet, sal_Int16 nScript )
890 {
891     // if WEAK is passed, guess script type from existing items in the item set
892     if( nScript == css::i18n::ScriptType::WEAK )
893         nScript = GetFirstUsedScript( rRoot, rItemSet );
894 
895     // convert to core script type constants
896     SvtScriptType nScScript = SvtLanguageOptions::FromI18NToSvtScriptType(nScript);
897 
898     // fill the font object
899     vcl::Font aFont;
900     ScPatternAttr::GetFont( aFont, rItemSet, SC_AUTOCOL_RAW, nullptr, nullptr, nullptr, nScScript );
901     return aFont;
902 }
903 
904 ScDxfFont XclExpFontHelper::GetDxfFontFromItemSet(const XclExpRoot& rRoot, const SfxItemSet& rItemSet)
905 {
906     sal_Int16 nScript = GetFirstUsedScript(rRoot, rItemSet);
907 
908     // convert to core script type constants
909     SvtScriptType nScScript = SvtLanguageOptions::FromI18NToSvtScriptType(nScript);
910     return ScPatternAttr::GetDxfFont(rItemSet, nScScript);
911 }
912 
913 bool XclExpFontHelper::CheckItems( const XclExpRoot& rRoot, const SfxItemSet& rItemSet, sal_Int16 nScript, bool bDeep )
914 {
915     static const sal_uInt16 pnCommonIds[] = {
916         ATTR_FONT_UNDERLINE, ATTR_FONT_CROSSEDOUT, ATTR_FONT_CONTOUR,
917         ATTR_FONT_SHADOWED, ATTR_FONT_COLOR, ATTR_FONT_LANGUAGE, 0 };
918     static const sal_uInt16 pnLatinIds[] = {
919         ATTR_FONT, ATTR_FONT_HEIGHT, ATTR_FONT_WEIGHT, ATTR_FONT_POSTURE, 0 };
920     static const sal_uInt16 pnAsianIds[] = {
921         ATTR_CJK_FONT, ATTR_CJK_FONT_HEIGHT, ATTR_CJK_FONT_WEIGHT, ATTR_CJK_FONT_POSTURE, 0 };
922     static const sal_uInt16 pnComplexIds[] = {
923         ATTR_CTL_FONT, ATTR_CTL_FONT_HEIGHT, ATTR_CTL_FONT_WEIGHT, ATTR_CTL_FONT_POSTURE, 0 };
924 
925     bool bUsed = ScfTools::CheckItems( rItemSet, pnCommonIds, bDeep );
926     if( !bUsed )
927     {
928         namespace ApiScriptType = css::i18n::ScriptType;
929         // if WEAK is passed, guess script type from existing items in the item set
930         if( nScript == ApiScriptType::WEAK )
931             nScript = GetFirstUsedScript( rRoot, rItemSet );
932         // check the correct items
933         switch( nScript )
934         {
935             case ApiScriptType::LATIN:      bUsed = ScfTools::CheckItems( rItemSet, pnLatinIds, bDeep );    break;
936             case ApiScriptType::ASIAN:      bUsed = ScfTools::CheckItems( rItemSet, pnAsianIds, bDeep );    break;
937             case ApiScriptType::COMPLEX:    bUsed = ScfTools::CheckItems( rItemSet, pnComplexIds, bDeep );  break;
938             default:    OSL_FAIL( "XclExpFontHelper::CheckItems - unknown script type" );
939         }
940     }
941     return bUsed;
942 }
943 
944 namespace {
945 
946 sal_uInt32 lclCalcHash( const XclFontData& rFontData )
947 {
948     sal_uInt32 nHash = rFontData.maName.getLength();
949     nHash += rFontData.maColor.GetColor() * 2;
950     nHash += rFontData.mnWeight * 3;
951     nHash += rFontData.mnCharSet * 5;
952     nHash += rFontData.mnFamily * 7;
953     nHash += rFontData.mnHeight * 11;
954     nHash += rFontData.mnUnderline * 13;
955     nHash += rFontData.mnEscapem * 17;
956     if( rFontData.mbItalic ) nHash += 19;
957     if( rFontData.mbStrikeout ) nHash += 23;
958     if( rFontData.mbOutline ) nHash += 29;
959     if( rFontData.mbShadow ) nHash += 31;
960     return nHash;
961 }
962 
963 } // namespace
964 
965 XclExpFont::XclExpFont( const XclExpRoot& rRoot,
966         const XclFontData& rFontData, XclExpColorType eColorType ) :
967     XclExpRecord( EXC_ID2_FONT, 14 ),
968     XclExpRoot( rRoot ),
969     maData( rFontData )
970 {
971     // insert font color into palette
972     mnColorId = rRoot.GetPalette().InsertColor( rFontData.maColor, eColorType, EXC_COLOR_FONTAUTO );
973     // hash value for faster comparison
974     mnHash = lclCalcHash( maData );
975     // record size
976     sal_Int32 nStrLen = maData.maName.getLength();
977     SetRecSize( ((GetBiff() == EXC_BIFF8) ? (nStrLen * 2 + 1) : nStrLen) + 15 );
978 }
979 
980 bool XclExpFont::Equals( const XclFontData& rFontData, sal_uInt32 nHash ) const
981 {
982     return (mnHash == nHash) && (maData == rFontData);
983 }
984 
985 void XclExpFont::SaveXml( XclExpXmlStream& rStrm )
986 {
987     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
988     rStyleSheet->startElement( XML_font, FSEND );
989     XclXmlUtils::WriteFontData( rStyleSheet, maData, XML_name );
990     // OOXTODO: XML_scheme; //scheme/@val values: "major", "minor", "none"
991     rStyleSheet->endElement( XML_font );
992 }
993 
994 // private --------------------------------------------------------------------
995 
996 void XclExpFont::WriteBody( XclExpStream& rStrm )
997 {
998     sal_uInt16 nAttr = EXC_FONTATTR_NONE;
999     ::set_flag( nAttr, EXC_FONTATTR_ITALIC, maData.mbItalic );
1000     if( maData.mnUnderline > 0 )
1001         ::set_flag( nAttr, EXC_FONTATTR_UNDERLINE, true );
1002     ::set_flag( nAttr, EXC_FONTATTR_STRIKEOUT, maData.mbStrikeout );
1003     ::set_flag( nAttr, EXC_FONTATTR_OUTLINE, maData.mbOutline );
1004     ::set_flag( nAttr, EXC_FONTATTR_SHADOW, maData.mbShadow );
1005 
1006     OSL_ENSURE( maData.maName.getLength() < 256, "XclExpFont::WriteBody - font name too long" );
1007     XclExpString aFontName;
1008     if( GetBiff() <= EXC_BIFF5 )
1009         aFontName.AssignByte( maData.maName, GetTextEncoding(), XclStrFlags::EightBitLength );
1010     else
1011         aFontName.Assign( maData.maName, XclStrFlags::ForceUnicode | XclStrFlags::EightBitLength );
1012 
1013     rStrm   << maData.mnHeight
1014             << nAttr
1015             << GetPalette().GetColorIndex( mnColorId )
1016             << maData.mnWeight
1017             << maData.mnEscapem
1018             << maData.mnUnderline
1019             << maData.mnFamily
1020             << maData.mnCharSet
1021             << sal_uInt8( 0 )
1022             << aFontName;
1023 }
1024 
1025 XclExpDxfFont::XclExpDxfFont(const XclExpRoot& rRoot,
1026         const SfxItemSet& rItemSet):
1027     XclExpRoot(rRoot)
1028 {
1029     maDxfData = XclExpFontHelper::GetDxfFontFromItemSet(rRoot, rItemSet);
1030 }
1031 
1032 namespace {
1033 
1034 const char* getUnderlineOOXValue(FontLineStyle eUnderline)
1035 {
1036     switch (eUnderline)
1037     {
1038         case LINESTYLE_NONE:
1039         case LINESTYLE_DONTKNOW:
1040             return "none";
1041         case LINESTYLE_DOUBLE:
1042         case LINESTYLE_DOUBLEWAVE:
1043             return "double";
1044         default:
1045             return "single";
1046     }
1047 }
1048 
1049 const char* getFontFamilyOOXValue(FontFamily eValue)
1050 {
1051     switch (eValue)
1052     {
1053         case FAMILY_DONTKNOW:
1054             return "0";
1055         break;
1056         case FAMILY_SWISS:
1057         case FAMILY_SYSTEM:
1058             return "2";
1059         case FAMILY_ROMAN:
1060             return "1";
1061         case FAMILY_SCRIPT:
1062             return "4";
1063         case FAMILY_MODERN:
1064             return "3";
1065         case FAMILY_DECORATIVE:
1066             return "5";
1067         default:
1068             return "0";
1069     }
1070 }
1071 
1072 }
1073 
1074 void XclExpDxfFont::SaveXml(XclExpXmlStream& rStrm)
1075 {
1076     if (maDxfData.isEmpty())
1077         return;
1078 
1079     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
1080     rStyleSheet->startElement(XML_font, FSEND);
1081 
1082     if (maDxfData.pFontAttr)
1083     {
1084         OUString aFontName = (*maDxfData.pFontAttr)->GetFamilyName();
1085 
1086         aFontName = XclTools::GetXclFontName(aFontName);
1087         if (!aFontName.isEmpty())
1088         {
1089             rStyleSheet->singleElement(XML_name,
1090                     XML_val, XclXmlUtils::ToOString(aFontName).getStr(),
1091                     FSEND);
1092         }
1093 
1094         rtl_TextEncoding eTextEnc = (*maDxfData.pFontAttr)->GetCharSet();
1095         sal_uInt8 nExcelCharSet = rtl_getBestWindowsCharsetFromTextEncoding(eTextEnc);
1096         if (nExcelCharSet)
1097         {
1098             rStyleSheet->singleElement(XML_charset,
1099                     XML_val, OString::number(nExcelCharSet).getStr(),
1100                     FSEND);
1101         }
1102 
1103         FontFamily eFamily = (*maDxfData.pFontAttr)->GetFamily();
1104         const char* pVal = getFontFamilyOOXValue(eFamily);
1105         if (pVal)
1106         {
1107             rStyleSheet->singleElement(XML_family,
1108                     XML_val, pVal,
1109                     FSEND);
1110         }
1111     }
1112 
1113     if (maDxfData.eWeight)
1114     {
1115         rStyleSheet->singleElement(XML_b,
1116                 XML_val, ToPsz10(maDxfData.eWeight.get() != WEIGHT_NORMAL),
1117                 FSEND);
1118     }
1119 
1120     if (maDxfData.eItalic)
1121     {
1122         bool bItalic = (maDxfData.eItalic.get() == ITALIC_OBLIQUE) || (maDxfData.eItalic.get() == ITALIC_NORMAL);
1123         rStyleSheet->singleElement(XML_i,
1124                 XML_val, ToPsz10(bItalic),
1125                 FSEND);
1126     }
1127 
1128     if (maDxfData.eStrike)
1129     {
1130         bool bStrikeout =
1131             (maDxfData.eStrike.get() == STRIKEOUT_SINGLE) || (maDxfData.eStrike.get() == STRIKEOUT_DOUBLE) ||
1132             (maDxfData.eStrike.get() == STRIKEOUT_BOLD)   || (maDxfData.eStrike.get() == STRIKEOUT_SLASH)  ||
1133             (maDxfData.eStrike.get() == STRIKEOUT_X);
1134 
1135         rStyleSheet->singleElement(XML_strike,
1136                 XML_val, ToPsz10(bStrikeout),
1137                 FSEND);
1138     }
1139 
1140     if (maDxfData.bOutline)
1141     {
1142         rStyleSheet->singleElement(XML_outline,
1143                 XML_val, ToPsz10(maDxfData.bOutline.get()),
1144                 FSEND);
1145     }
1146 
1147     if (maDxfData.bShadow)
1148     {
1149         rStyleSheet->singleElement(XML_shadow,
1150                 XML_val, ToPsz10(maDxfData.bShadow.get()),
1151                 FSEND);
1152     }
1153 
1154     if (maDxfData.aColor)
1155     {
1156         rStyleSheet->singleElement(XML_color,
1157                 XML_rgb, XclXmlUtils::ToOString(maDxfData.aColor.get()).getStr(),
1158                 FSEND);
1159     }
1160 
1161     if (maDxfData.eUnder)
1162     {
1163         const char* pVal = getUnderlineOOXValue(maDxfData.eUnder.get());
1164         rStyleSheet->singleElement(XML_u,
1165                 XML_val, pVal,
1166                 FSEND);
1167     }
1168 
1169     rStyleSheet->endElement(XML_font);
1170 }
1171 
1172 XclExpBlindFont::XclExpBlindFont( const XclExpRoot& rRoot ) :
1173     XclExpFont( rRoot, XclFontData(), EXC_COLOR_CELLTEXT )
1174 {
1175 }
1176 
1177 bool XclExpBlindFont::Equals( const XclFontData& /*rFontData*/, sal_uInt32 /*nHash*/ ) const
1178 {
1179     return false;
1180 }
1181 
1182 void XclExpBlindFont::Save( XclExpStream& /*rStrm*/ )
1183 {
1184     // do nothing
1185 }
1186 
1187 XclExpFontBuffer::XclExpFontBuffer( const XclExpRoot& rRoot ) :
1188     XclExpRoot( rRoot ),
1189     mnXclMaxSize( 0 )
1190 {
1191     switch( GetBiff() )
1192     {
1193         case EXC_BIFF4: mnXclMaxSize = EXC_FONT_MAXCOUNT4;  break;
1194         case EXC_BIFF5: mnXclMaxSize = EXC_FONT_MAXCOUNT5;  break;
1195         case EXC_BIFF8: mnXclMaxSize = EXC_FONT_MAXCOUNT8;  break;
1196         default:        DBG_ERROR_BIFF();
1197     }
1198     InitDefaultFonts();
1199 }
1200 
1201 const XclExpFont* XclExpFontBuffer::GetFont( sal_uInt16 nXclFont ) const
1202 {
1203     return maFontList.GetRecord( nXclFont ).get();
1204 }
1205 
1206 const XclFontData& XclExpFontBuffer::GetAppFontData() const
1207 {
1208     return maFontList.GetRecord( EXC_FONT_APP )->GetFontData(); // exists always
1209 }
1210 
1211 sal_uInt16 XclExpFontBuffer::Insert(
1212         const XclFontData& rFontData, XclExpColorType eColorType, bool bAppFont )
1213 {
1214     if( bAppFont )
1215     {
1216         XclExpFontRef xFont( new XclExpFont( GetRoot(), rFontData, eColorType ) );
1217         maFontList.ReplaceRecord( xFont, EXC_FONT_APP );
1218         // set width of '0' character for column width export
1219         SetCharWidth( xFont->GetFontData() );
1220         return EXC_FONT_APP;
1221     }
1222 
1223     size_t nPos = Find( rFontData );
1224     if( nPos == EXC_FONTLIST_NOTFOUND )
1225     {
1226         // not found in buffer - create new font
1227         size_t nSize = maFontList.GetSize();
1228         if( nSize < mnXclMaxSize )
1229         {
1230             // possible to insert
1231             maFontList.AppendNewRecord( new XclExpFont( GetRoot(), rFontData, eColorType ) );
1232             nPos = nSize;       // old size is last position now
1233         }
1234         else
1235         {
1236             // buffer is full - ignore new font, use default font
1237             nPos = EXC_FONT_APP;
1238         }
1239     }
1240     return static_cast< sal_uInt16 >( nPos );
1241 }
1242 
1243 sal_uInt16 XclExpFontBuffer::Insert(
1244         const SvxFont& rFont, XclExpColorType eColorType )
1245 {
1246     return Insert( XclFontData( rFont ), eColorType );
1247 }
1248 
1249 sal_uInt16 XclExpFontBuffer::Insert( const SfxItemSet& rItemSet,
1250         sal_Int16 nScript, XclExpColorType eColorType, bool bAppFont )
1251 {
1252     // #i17050# script type now provided by caller
1253     vcl::Font aFont = XclExpFontHelper::GetFontFromItemSet( GetRoot(), rItemSet, nScript );
1254     return Insert( XclFontData( aFont ), eColorType, bAppFont );
1255 }
1256 
1257 void XclExpFontBuffer::Save( XclExpStream& rStrm )
1258 {
1259     maFontList.Save( rStrm );
1260 }
1261 
1262 void XclExpFontBuffer::SaveXml( XclExpXmlStream& rStrm )
1263 {
1264     if( maFontList.IsEmpty() )
1265         return;
1266 
1267     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
1268     rStyleSheet->startElement( XML_fonts,
1269             XML_count,  OString::number(  maFontList.GetSize() ).getStr(),
1270             FSEND );
1271 
1272     maFontList.SaveXml( rStrm );
1273 
1274     rStyleSheet->endElement( XML_fonts );
1275 }
1276 
1277 // private --------------------------------------------------------------------
1278 
1279 void XclExpFontBuffer::InitDefaultFonts()
1280 {
1281     XclFontData aFontData;
1282     aFontData.maName = "Arial";
1283     aFontData.SetScFamily( FAMILY_DONTKNOW );
1284     aFontData.SetFontEncoding( ScfTools::GetSystemTextEncoding() );
1285     aFontData.SetScHeight( 200 );   // 200 twips = 10 pt
1286     aFontData.SetScWeight( WEIGHT_NORMAL );
1287 
1288     switch( GetBiff() )
1289     {
1290         case EXC_BIFF5:
1291         {
1292             maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
1293             aFontData.SetScWeight( WEIGHT_BOLD );
1294             maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
1295             aFontData.SetScWeight( WEIGHT_NORMAL );
1296             aFontData.SetScPosture( ITALIC_NORMAL );
1297             maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
1298             aFontData.SetScWeight( WEIGHT_BOLD );
1299             maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
1300             // the blind font with index 4
1301             maFontList.AppendNewRecord( new XclExpBlindFont( GetRoot() ) );
1302             // already add the first user defined font (Excel does it too)
1303             aFontData.SetScWeight( WEIGHT_NORMAL );
1304             aFontData.SetScPosture( ITALIC_NONE );
1305             maFontList.AppendNewRecord( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
1306         }
1307         break;
1308         case EXC_BIFF8:
1309         {
1310             XclExpFontRef xFont( new XclExpFont( GetRoot(), aFontData, EXC_COLOR_CELLTEXT ) );
1311             maFontList.AppendRecord( xFont );
1312             maFontList.AppendRecord( xFont );
1313             maFontList.AppendRecord( xFont );
1314             maFontList.AppendRecord( xFont );
1315             if( GetOutput() == EXC_OUTPUT_BINARY )
1316                 // the blind font with index 4
1317                 maFontList.AppendNewRecord( new XclExpBlindFont( GetRoot() ) );
1318         }
1319         break;
1320         default:
1321             DBG_ERROR_BIFF();
1322     }
1323 }
1324 
1325 size_t XclExpFontBuffer::Find( const XclFontData& rFontData )
1326 {
1327     sal_uInt32 nHash = lclCalcHash( rFontData );
1328     for( size_t nPos = 0, nSize = maFontList.GetSize(); nPos < nSize; ++nPos )
1329         if( maFontList.GetRecord( nPos )->Equals( rFontData, nHash ) )
1330             return nPos;
1331     return EXC_FONTLIST_NOTFOUND;
1332 }
1333 
1334 // FORMAT record - number formats =============================================
1335 
1336 /** Predicate for search algorithm. */
1337 struct XclExpNumFmtPred
1338 {
1339     sal_uLong               mnScNumFmt;
1340     explicit     XclExpNumFmtPred( sal_uLong nScNumFmt ) : mnScNumFmt( nScNumFmt ) {}
1341     bool         operator()( const XclExpNumFmt& rFormat ) const
1342                             { return rFormat.mnScNumFmt == mnScNumFmt; }
1343 };
1344 
1345 void XclExpNumFmt::SaveXml( XclExpXmlStream& rStrm )
1346 {
1347     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
1348     rStyleSheet->singleElement( XML_numFmt,
1349             XML_numFmtId,   OString::number( mnXclNumFmt ).getStr(),
1350             XML_formatCode, OUStringToOString(maNumFmtString, RTL_TEXTENCODING_UTF8).getStr(),
1351             FSEND );
1352 }
1353 
1354 XclExpNumFmtBuffer::XclExpNumFmtBuffer( const XclExpRoot& rRoot ) :
1355     XclExpRoot( rRoot ),
1356     /*  Compiler needs a hint, this doesn't work: new NfKeywordTable;
1357         cannot convert from 'class String *' to 'class String (*)[54]'
1358         The effective result here is class String (*)[54*1] */
1359     mxFormatter( new SvNumberFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US ) ),
1360     mpKeywordTable( new NfKeywordTable ),
1361     mnStdFmt( GetFormatter().GetStandardIndex( ScGlobal::eLnge ) )
1362 {
1363     switch( GetBiff() )
1364     {
1365         case EXC_BIFF5: mnXclOffset = EXC_FORMAT_OFFSET5;   break;
1366         case EXC_BIFF8: mnXclOffset = EXC_FORMAT_OFFSET8;   break;
1367         default:        mnXclOffset = 0; DBG_ERROR_BIFF();
1368     }
1369 
1370     mxFormatter->FillKeywordTableForExcel( *mpKeywordTable );
1371 }
1372 
1373 XclExpNumFmtBuffer::~XclExpNumFmtBuffer()
1374 {
1375 }
1376 
1377 sal_uInt16 XclExpNumFmtBuffer::Insert( sal_uInt32 nScNumFmt )
1378 {
1379     XclExpNumFmtVec::const_iterator aIt =
1380         ::std::find_if( maFormatMap.begin(), maFormatMap.end(), XclExpNumFmtPred( nScNumFmt ) );
1381     if( aIt != maFormatMap.end() )
1382         return aIt->mnXclNumFmt;
1383 
1384     size_t nSize = maFormatMap.size();
1385     if( nSize < static_cast< size_t >( 0xFFFF - mnXclOffset ) )
1386     {
1387         sal_uInt16 nXclNumFmt = static_cast< sal_uInt16 >( nSize + mnXclOffset );
1388         maFormatMap.emplace_back( nScNumFmt, nXclNumFmt, GetFormatCode( nScNumFmt ) );
1389         return nXclNumFmt;
1390     }
1391 
1392     return 0;
1393 }
1394 
1395 void XclExpNumFmtBuffer::Save( XclExpStream& rStrm )
1396 {
1397     for( XclExpNumFmtVec::const_iterator aIt = maFormatMap.begin(), aEnd = maFormatMap.end(); aIt != aEnd; ++aIt )
1398         WriteFormatRecord( rStrm, *aIt );
1399 }
1400 
1401 void XclExpNumFmtBuffer::SaveXml( XclExpXmlStream& rStrm )
1402 {
1403     if( maFormatMap.empty() )
1404         return;
1405 
1406     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
1407     rStyleSheet->startElement( XML_numFmts,
1408             XML_count,  OString::number(  maFormatMap.size() ).getStr(),
1409             FSEND );
1410     for( XclExpNumFmtVec::iterator aIt = maFormatMap.begin(), aEnd = maFormatMap.end(); aIt != aEnd; ++aIt )
1411     {
1412         aIt->SaveXml( rStrm );
1413     }
1414     rStyleSheet->endElement( XML_numFmts );
1415 }
1416 
1417 void XclExpNumFmtBuffer::WriteFormatRecord( XclExpStream& rStrm, sal_uInt16 nXclNumFmt, const OUString& rFormatStr )
1418 {
1419     XclExpString aExpStr;
1420     if( GetBiff() <= EXC_BIFF5 )
1421         aExpStr.AssignByte( rFormatStr, GetTextEncoding(), XclStrFlags::EightBitLength );
1422     else
1423         aExpStr.Assign( rFormatStr );
1424 
1425     rStrm.StartRecord( EXC_ID4_FORMAT, 2 + aExpStr.GetSize() );
1426     rStrm << nXclNumFmt << aExpStr;
1427     rStrm.EndRecord();
1428 }
1429 
1430 void XclExpNumFmtBuffer::WriteFormatRecord( XclExpStream& rStrm, const XclExpNumFmt& rFormat )
1431 {
1432     WriteFormatRecord( rStrm, rFormat.mnXclNumFmt, GetFormatCode( rFormat.mnScNumFmt ) );
1433 }
1434 
1435 namespace {
1436 
1437 OUString GetNumberFormatCode(const XclRoot& rRoot, const sal_uInt32 nScNumFmt, SvNumberFormatter* pFormatter, const NfKeywordTable* pKeywordTable)
1438 {
1439     return rRoot.GetFormatter().GetFormatStringForExcel( nScNumFmt, *pKeywordTable, *pFormatter);
1440 }
1441 
1442 }
1443 
1444 OUString XclExpNumFmtBuffer::GetFormatCode( sal_uInt32 nScNumFmt )
1445 {
1446     return GetNumberFormatCode( *this, nScNumFmt, mxFormatter.get(), mpKeywordTable.get() );
1447 }
1448 
1449 // XF, STYLE record - Cell formatting =========================================
1450 
1451 bool XclExpCellProt::FillFromItemSet( const SfxItemSet& rItemSet, bool bStyle )
1452 {
1453     const ScProtectionAttr& rProtItem = GETITEM( rItemSet, ScProtectionAttr, ATTR_PROTECTION );
1454     mbLocked = rProtItem.GetProtection();
1455     mbHidden = rProtItem.GetHideFormula() || rProtItem.GetHideCell();
1456     return ScfTools::CheckItem( rItemSet, ATTR_PROTECTION, bStyle );
1457 }
1458 
1459 void XclExpCellProt::FillToXF3( sal_uInt16& rnProt ) const
1460 {
1461     ::set_flag( rnProt, EXC_XF_LOCKED, mbLocked );
1462     ::set_flag( rnProt, EXC_XF_HIDDEN, mbHidden );
1463 }
1464 
1465 void XclExpCellProt::SaveXml( XclExpXmlStream& rStrm ) const
1466 {
1467     rStrm.GetCurrentStream()->singleElement( XML_protection,
1468             XML_locked,     ToPsz( mbLocked ),
1469             XML_hidden,     ToPsz( mbHidden ),
1470             FSEND );
1471 }
1472 
1473 bool XclExpCellAlign::FillFromItemSet(
1474         const SfxItemSet& rItemSet, bool bForceLineBreak, XclBiff eBiff, bool bStyle )
1475 {
1476     bool bUsed = false;
1477     SvxCellHorJustify eHorAlign = GETITEM( rItemSet, SvxHorJustifyItem, ATTR_HOR_JUSTIFY ).GetValue();
1478     SvxCellVerJustify eVerAlign = GETITEM( rItemSet, SvxVerJustifyItem, ATTR_VER_JUSTIFY ).GetValue();
1479 
1480     switch( eBiff )
1481     {
1482         case EXC_BIFF8: // attributes new in BIFF8
1483         {
1484             // text indent
1485             long nTmpIndent = GETITEM( rItemSet, SfxUInt16Item, ATTR_INDENT ).GetValue();
1486             (nTmpIndent += 100) /= 200; // 1 Excel unit == 10 pt == 200 twips
1487             mnIndent = limit_cast< sal_uInt8 >( nTmpIndent, 0, 15 );
1488             bUsed |= ScfTools::CheckItem( rItemSet, ATTR_INDENT, bStyle );
1489 
1490             // shrink to fit
1491             mbShrink = GETITEM( rItemSet, SfxBoolItem, ATTR_SHRINKTOFIT ).GetValue();
1492             bUsed |= ScfTools::CheckItem( rItemSet, ATTR_SHRINKTOFIT, bStyle );
1493 
1494             // CTL text direction
1495             SetScFrameDir( GETITEM( rItemSet, SvxFrameDirectionItem, ATTR_WRITINGDIR ).GetValue() );
1496             bUsed |= ScfTools::CheckItem( rItemSet, ATTR_WRITINGDIR, bStyle );
1497 
1498             SAL_FALLTHROUGH;
1499         }
1500 
1501         case EXC_BIFF5: // attributes new in BIFF5
1502         case EXC_BIFF4: // attributes new in BIFF4
1503         {
1504             // vertical alignment
1505             SetScVerAlign( eVerAlign );
1506             bUsed |= ScfTools::CheckItem( rItemSet, ATTR_VER_JUSTIFY, bStyle );
1507 
1508             // stacked/rotation
1509             bool bStacked = GETITEM( rItemSet, SfxBoolItem, ATTR_STACKED ).GetValue();
1510             bUsed |= ScfTools::CheckItem( rItemSet, ATTR_STACKED, bStyle );
1511             if( bStacked )
1512             {
1513                 mnRotation = EXC_ROT_STACKED;
1514             }
1515             else
1516             {
1517                 // rotation
1518                 sal_Int32 nScRot = GETITEM( rItemSet, SfxInt32Item, ATTR_ROTATE_VALUE ).GetValue();
1519                 mnRotation = XclTools::GetXclRotation( nScRot );
1520                 bUsed |= ScfTools::CheckItem( rItemSet, ATTR_ROTATE_VALUE, bStyle );
1521             }
1522             mnOrient = XclTools::GetXclOrientFromRot( mnRotation );
1523 
1524             SAL_FALLTHROUGH;
1525         }
1526 
1527         case EXC_BIFF3: // attributes new in BIFF3
1528         {
1529             // text wrap
1530             mbLineBreak = bForceLineBreak || GETITEMBOOL( rItemSet, ATTR_LINEBREAK );
1531             bUsed |= bForceLineBreak || ScfTools::CheckItem( rItemSet, ATTR_LINEBREAK, bStyle );
1532 
1533             SAL_FALLTHROUGH;
1534         }
1535 
1536         case EXC_BIFF2: // attributes new in BIFF2
1537         {
1538             // horizontal alignment
1539             SetScHorAlign( eHorAlign );
1540             bUsed |= ScfTools::CheckItem( rItemSet, ATTR_HOR_JUSTIFY, bStyle );
1541         }
1542 
1543         break;
1544         default:    DBG_ERROR_BIFF();
1545     }
1546 
1547     if (eBiff == EXC_BIFF8)
1548     {
1549         // Adjust for distributed alignments.
1550         if (eHorAlign == SvxCellHorJustify::Block)
1551         {
1552             SvxCellJustifyMethod eHorJustMethod =
1553                 rItemSet.GetItem<SvxJustifyMethodItem>(ATTR_HOR_JUSTIFY_METHOD)->GetValue();
1554             if (eHorJustMethod == SvxCellJustifyMethod::Distribute)
1555                 mnHorAlign = EXC_XF_HOR_DISTRIB;
1556         }
1557 
1558         if (eVerAlign == SvxCellVerJustify::Block)
1559         {
1560             SvxCellJustifyMethod eVerJustMethod =
1561                 rItemSet.GetItem<SvxJustifyMethodItem>(ATTR_VER_JUSTIFY_METHOD)->GetValue();
1562             if (eVerJustMethod == SvxCellJustifyMethod::Distribute)
1563                 mnVerAlign = EXC_XF_VER_DISTRIB;
1564         }
1565     }
1566 
1567     return bUsed;
1568 }
1569 
1570 void XclExpCellAlign::FillToXF5( sal_uInt16& rnAlign ) const
1571 {
1572     ::insert_value( rnAlign, mnHorAlign, 0, 3 );
1573     ::set_flag( rnAlign, EXC_XF_LINEBREAK, mbLineBreak );
1574     ::insert_value( rnAlign, mnVerAlign, 4, 3 );
1575     ::insert_value( rnAlign, mnOrient, 8, 2 );
1576 }
1577 
1578 void XclExpCellAlign::FillToXF8( sal_uInt16& rnAlign, sal_uInt16& rnMiscAttrib ) const
1579 {
1580     ::insert_value( rnAlign, mnHorAlign, 0, 3 );
1581     ::set_flag( rnAlign, EXC_XF_LINEBREAK, mbLineBreak );
1582     ::insert_value( rnAlign, mnVerAlign, 4, 3 );
1583     ::insert_value( rnAlign, mnRotation, 8, 8 );
1584     ::insert_value( rnMiscAttrib, mnIndent, 0, 4 );
1585     ::set_flag( rnMiscAttrib, EXC_XF8_SHRINK, mbShrink );
1586     ::insert_value( rnMiscAttrib, mnTextDir, 6, 2 );
1587 }
1588 
1589 static const char* ToHorizontalAlignment( sal_uInt8 nHorAlign )
1590 {
1591     switch( nHorAlign )
1592     {
1593         case EXC_XF_HOR_GENERAL:    return "general";
1594         case EXC_XF_HOR_LEFT:       return "left";
1595         case EXC_XF_HOR_CENTER:     return "center";
1596         case EXC_XF_HOR_RIGHT:      return "right";
1597         case EXC_XF_HOR_FILL:       return "fill";
1598         case EXC_XF_HOR_JUSTIFY:    return "justify";
1599         case EXC_XF_HOR_CENTER_AS:  return "centerContinuous";
1600         case EXC_XF_HOR_DISTRIB:    return "distributed";
1601     }
1602     return "*unknown*";
1603 }
1604 
1605 static const char* ToVerticalAlignment( sal_uInt8 nVerAlign )
1606 {
1607     switch( nVerAlign )
1608     {
1609         case EXC_XF_VER_TOP:        return "top";
1610         case EXC_XF_VER_CENTER:     return "center";
1611         case EXC_XF_VER_BOTTOM:     return "bottom";
1612         case EXC_XF_VER_JUSTIFY:    return "justify";
1613         case EXC_XF_VER_DISTRIB:    return "distributed";
1614     }
1615     return "*unknown*";
1616 }
1617 
1618 void XclExpCellAlign::SaveXml( XclExpXmlStream& rStrm ) const
1619 {
1620     rStrm.GetCurrentStream()->singleElement( XML_alignment,
1621             XML_horizontal,         ToHorizontalAlignment( mnHorAlign ),
1622             XML_vertical,           ToVerticalAlignment( mnVerAlign ),
1623             XML_textRotation,       OString::number(  mnRotation ).getStr(),
1624             XML_wrapText,           ToPsz( mbLineBreak ),
1625             XML_indent,             OString::number(  mnIndent ).getStr(),
1626             // OOXTODO: XML_relativeIndent,     mnIndent?
1627             // OOXTODO: XML_justifyLastLine,
1628             XML_shrinkToFit,        ToPsz( mbShrink ),
1629             XML_readingOrder, mnTextDir == EXC_XF_TEXTDIR_CONTEXT ? nullptr : OString::number(  mnTextDir ).getStr(),
1630             FSEND );
1631 }
1632 
1633 namespace {
1634 
1635 void lclGetBorderLine(
1636         sal_uInt8& rnXclLine, sal_uInt32& rnColorId,
1637         const ::editeng::SvxBorderLine* pLine, XclExpPalette& rPalette, XclBiff eBiff )
1638 {
1639     // Document: sc/qa/unit/data/README.cellborders
1640 
1641     enum CalcLineIndex{Idx_None, Idx_Solid, Idx_Dotted, Idx_Dashed, Idx_FineDashed, Idx_DashDot, Idx_DashDotDot, Idx_DoubleThin, Idx_Last};
1642     enum ExcelWidthIndex{Width_Hair, Width_Thin, Width_Medium, Width_Thick, Width_Last};
1643     static sal_uInt8 Map_LineLO_toMS[Idx_Last][Width_Last] =
1644     {
1645     //    0,05  -  0,74                  0,75  -  1,49                   1,50  -  2,49                 2,50  -  9,00          Width Range [pt]
1646     //   EXC_BORDER_HAIR                EXC_BORDER_THIN                EXC_BORDER_MEDIUM              EXC_BORDER_THICK        MS Width
1647         {EXC_LINE_NONE                , EXC_LINE_NONE                , EXC_LINE_NONE                , EXC_LINE_NONE                }, //  0    BorderLineStyle::NONE
1648         {EXC_LINE_HAIR                , EXC_LINE_THIN                , EXC_LINE_MEDIUM              , EXC_LINE_THICK               }, //  1    BorderLineStyle::SOLID
1649         {EXC_LINE_DOTTED              , EXC_LINE_DOTTED              , EXC_LINE_MEDIUM_SLANT_DASHDOT, EXC_LINE_MEDIUM_SLANT_DASHDOT}, //  2    BorderLineStyle::DOTTED
1650         {EXC_LINE_DOTTED              , EXC_LINE_DASHED              , EXC_LINE_MEDIUM_DASHED       , EXC_LINE_MEDIUM_DASHED       }, //  3    BorderLineStyle::DASHED
1651         {EXC_LINE_DASHED              , EXC_LINE_DASHED              , EXC_LINE_MEDIUM_SLANT_DASHDOT, EXC_LINE_MEDIUM_SLANT_DASHDOT}, //  4    BorderLineStyle::FINE_DASHED
1652         {EXC_LINE_DASHED              , EXC_LINE_THIN_DASHDOT        , EXC_LINE_MEDIUM_DASHDOT      , EXC_LINE_MEDIUM_DASHDOT      }, //  5    BorderLineStyle::DASH_DOT
1653         {EXC_LINE_DASHED              , EXC_LINE_THIN_DASHDOTDOT     , EXC_LINE_MEDIUM_DASHDOTDOT   , EXC_LINE_MEDIUM_DASHDOTDOT   }, //  6    BorderLineStyle::DASH_DOT_DOT
1654         {EXC_LINE_DOUBLE              , EXC_LINE_DOUBLE              , EXC_LINE_DOUBLE              , EXC_LINE_DOUBLE              }  //  7    BorderLineStyle::DOUBLE_THIN
1655     };                                                                                                                                // Line  Name
1656 
1657     rnXclLine = EXC_LINE_NONE;
1658     if( pLine )
1659     {
1660         sal_uInt16 nOuterWidth = pLine->GetOutWidth();
1661         ExcelWidthIndex nOuterWidthIndx;
1662         CalcLineIndex  nStyleIndex;
1663 
1664         switch (pLine->GetBorderLineStyle())
1665         {
1666             case SvxBorderLineStyle::NONE:
1667                 nStyleIndex = Idx_None;
1668                 break;
1669             case SvxBorderLineStyle::SOLID:
1670                 nStyleIndex = Idx_Solid;
1671                 break;
1672             case SvxBorderLineStyle::DOTTED:
1673                 nStyleIndex = Idx_Dotted;
1674                 break;
1675             case SvxBorderLineStyle::DASHED:
1676                 nStyleIndex = Idx_Dashed;
1677                 break;
1678             case SvxBorderLineStyle::FINE_DASHED:
1679                 nStyleIndex = Idx_FineDashed;
1680                 break;
1681             case SvxBorderLineStyle::DASH_DOT:
1682                 nStyleIndex = Idx_DashDot;
1683                 break;
1684             case SvxBorderLineStyle::DASH_DOT_DOT:
1685                 nStyleIndex = Idx_DashDotDot;
1686                 break;
1687             case SvxBorderLineStyle::DOUBLE_THIN:
1688                 // the "nOuterWidth" is not right for this line type
1689                 // but at the moment width it not important for that
1690                 // the right function is nOuterWidth = (sal_uInt16) pLine->GetWidth();
1691                 nStyleIndex = Idx_DoubleThin;
1692                 break;
1693             default:
1694                 nStyleIndex = Idx_Solid;
1695         }
1696 
1697         if( nOuterWidth >= EXC_BORDER_THICK )
1698             nOuterWidthIndx = Width_Thick;
1699         else if( nOuterWidth >= EXC_BORDER_MEDIUM )
1700             nOuterWidthIndx = Width_Medium;
1701         else if( nOuterWidth >= EXC_BORDER_THIN )
1702             nOuterWidthIndx = Width_Thin;
1703         else if ( nOuterWidth >= EXC_BORDER_HAIR )
1704             nOuterWidthIndx = Width_Hair;
1705         else
1706             nOuterWidthIndx = Width_Thin;
1707 
1708         rnXclLine = Map_LineLO_toMS[nStyleIndex][nOuterWidthIndx];
1709     }
1710 
1711     if( (eBiff == EXC_BIFF2) && (rnXclLine != EXC_LINE_NONE) )
1712         rnXclLine = EXC_LINE_THIN;
1713 
1714     rnColorId = (pLine && (rnXclLine != EXC_LINE_NONE)) ?
1715         rPalette.InsertColor( pLine->GetColor(), EXC_COLOR_CELLBORDER ) :
1716         XclExpPalette::GetColorIdFromIndex( 0 );
1717 }
1718 
1719 } // namespace
1720 
1721 XclExpCellBorder::XclExpCellBorder() :
1722     mnLeftColorId(   XclExpPalette::GetColorIdFromIndex( mnLeftColor ) ),
1723     mnRightColorId(  XclExpPalette::GetColorIdFromIndex( mnRightColor ) ),
1724     mnTopColorId(    XclExpPalette::GetColorIdFromIndex( mnTopColor ) ),
1725     mnBottomColorId( XclExpPalette::GetColorIdFromIndex( mnBottomColor ) ),
1726     mnDiagColorId(   XclExpPalette::GetColorIdFromIndex( mnDiagColor ) )
1727 {
1728 }
1729 
1730 bool XclExpCellBorder::FillFromItemSet(
1731         const SfxItemSet& rItemSet, XclExpPalette& rPalette, XclBiff eBiff, bool bStyle )
1732 {
1733     bool bUsed = false;
1734 
1735     switch( eBiff )
1736     {
1737         case EXC_BIFF8: // attributes new in BIFF8
1738         {
1739             const SvxLineItem& rTLBRItem = GETITEM( rItemSet, SvxLineItem, ATTR_BORDER_TLBR );
1740             sal_uInt8 nTLBRLine;
1741             sal_uInt32 nTLBRColorId;
1742             lclGetBorderLine( nTLBRLine, nTLBRColorId, rTLBRItem.GetLine(), rPalette, eBiff );
1743             mbDiagTLtoBR = (nTLBRLine != EXC_LINE_NONE);
1744 
1745             const SvxLineItem& rBLTRItem = GETITEM( rItemSet, SvxLineItem, ATTR_BORDER_BLTR );
1746             sal_uInt8 nBLTRLine;
1747             sal_uInt32 nBLTRColorId;
1748             lclGetBorderLine( nBLTRLine, nBLTRColorId, rBLTRItem.GetLine(), rPalette, eBiff );
1749             mbDiagBLtoTR = (nBLTRLine != EXC_LINE_NONE);
1750 
1751             if( ::ScHasPriority( rTLBRItem.GetLine(), rBLTRItem.GetLine() ) )
1752             {
1753                 mnDiagLine = nTLBRLine;
1754                 mnDiagColorId = nTLBRColorId;
1755             }
1756             else
1757             {
1758                 mnDiagLine = nBLTRLine;
1759                 mnDiagColorId = nBLTRColorId;
1760             }
1761 
1762             bUsed |= ScfTools::CheckItem( rItemSet, ATTR_BORDER_TLBR, bStyle ) ||
1763                      ScfTools::CheckItem( rItemSet, ATTR_BORDER_BLTR, bStyle );
1764 
1765             SAL_FALLTHROUGH;
1766         }
1767 
1768         case EXC_BIFF5:
1769         case EXC_BIFF4:
1770         case EXC_BIFF3:
1771         case EXC_BIFF2:
1772         {
1773             const SvxBoxItem& rBoxItem = GETITEM( rItemSet, SvxBoxItem, ATTR_BORDER );
1774             lclGetBorderLine( mnLeftLine,   mnLeftColorId,   rBoxItem.GetLeft(),   rPalette, eBiff );
1775             lclGetBorderLine( mnRightLine,  mnRightColorId,  rBoxItem.GetRight(),  rPalette, eBiff );
1776             lclGetBorderLine( mnTopLine,    mnTopColorId,    rBoxItem.GetTop(),    rPalette, eBiff );
1777             lclGetBorderLine( mnBottomLine, mnBottomColorId, rBoxItem.GetBottom(), rPalette, eBiff );
1778             bUsed |= ScfTools::CheckItem( rItemSet, ATTR_BORDER, bStyle );
1779         }
1780 
1781         break;
1782         default:    DBG_ERROR_BIFF();
1783     }
1784 
1785     return bUsed;
1786 }
1787 
1788 void XclExpCellBorder::SetFinalColors( const XclExpPalette& rPalette )
1789 {
1790     mnLeftColor   = rPalette.GetColorIndex( mnLeftColorId );
1791     mnRightColor  = rPalette.GetColorIndex( mnRightColorId );
1792     mnTopColor    = rPalette.GetColorIndex( mnTopColorId );
1793     mnBottomColor = rPalette.GetColorIndex( mnBottomColorId );
1794     mnDiagColor   = rPalette.GetColorIndex( mnDiagColorId );
1795 }
1796 
1797 void XclExpCellBorder::FillToXF5( sal_uInt32& rnBorder, sal_uInt32& rnArea ) const
1798 {
1799     ::insert_value( rnBorder, mnTopLine,      0, 3 );
1800     ::insert_value( rnBorder, mnLeftLine,     3, 3 );
1801     ::insert_value( rnArea,   mnBottomLine,  22, 3 );
1802     ::insert_value( rnBorder, mnRightLine,    6, 3 );
1803     ::insert_value( rnBorder, mnTopColor,     9, 7 );
1804     ::insert_value( rnBorder, mnLeftColor,   16, 7 );
1805     ::insert_value( rnArea,   mnBottomColor, 25, 7 );
1806     ::insert_value( rnBorder, mnRightColor,  23, 7 );
1807 }
1808 
1809 void XclExpCellBorder::FillToXF8( sal_uInt32& rnBorder1, sal_uInt32& rnBorder2 ) const
1810 {
1811     ::insert_value( rnBorder1, mnLeftLine,     0, 4 );
1812     ::insert_value( rnBorder1, mnRightLine,    4, 4 );
1813     ::insert_value( rnBorder1, mnTopLine,      8, 4 );
1814     ::insert_value( rnBorder1, mnBottomLine,  12, 4 );
1815     ::insert_value( rnBorder1, mnLeftColor,   16, 7 );
1816     ::insert_value( rnBorder1, mnRightColor,  23, 7 );
1817     ::insert_value( rnBorder2, mnTopColor,     0, 7 );
1818     ::insert_value( rnBorder2, mnBottomColor,  7, 7 );
1819     ::insert_value( rnBorder2, mnDiagColor,   14, 7 );
1820     ::insert_value( rnBorder2, mnDiagLine,    21, 4 );
1821     ::set_flag( rnBorder1, EXC_XF_DIAGONAL_TL_TO_BR, mbDiagTLtoBR );
1822     ::set_flag( rnBorder1, EXC_XF_DIAGONAL_BL_TO_TR, mbDiagBLtoTR );
1823 }
1824 
1825 void XclExpCellBorder::FillToCF8( sal_uInt16& rnLine, sal_uInt32& rnColor ) const
1826 {
1827     ::insert_value( rnLine,  mnLeftLine,     0, 4 );
1828     ::insert_value( rnLine,  mnRightLine,    4, 4 );
1829     ::insert_value( rnLine,  mnTopLine,      8, 4 );
1830     ::insert_value( rnLine,  mnBottomLine,  12, 4 );
1831     ::insert_value( rnColor, mnLeftColor,    0, 7 );
1832     ::insert_value( rnColor, mnRightColor,   7, 7 );
1833     ::insert_value( rnColor, mnTopColor,    16, 7 );
1834     ::insert_value( rnColor, mnBottomColor, 23, 7 );
1835 }
1836 
1837 static const char* ToLineStyle( sal_uInt8 nLineStyle )
1838 {
1839     switch( nLineStyle )
1840     {
1841         case EXC_LINE_NONE:              return "none";
1842         case EXC_LINE_THIN:              return "thin";
1843         case EXC_LINE_MEDIUM:            return "medium";
1844         case EXC_LINE_THICK:             return "thick";
1845         case EXC_LINE_DOUBLE:            return "double";
1846         case EXC_LINE_HAIR:              return "hair";
1847         case EXC_LINE_DOTTED:            return "dotted";
1848         case EXC_LINE_DASHED:            return "dashed";
1849         case EXC_LINE_MEDIUM_DASHED:     return "mediumDashed";
1850         case EXC_LINE_THIN_DASHDOT:      return "dashDot";
1851         case EXC_LINE_THIN_DASHDOTDOT:   return "dashDotDot";
1852         case EXC_LINE_MEDIUM_DASHDOT:    return "mediumDashDot";
1853         case EXC_LINE_MEDIUM_DASHDOTDOT: return "mediumDashDotDot";
1854         case EXC_LINE_MEDIUM_SLANT_DASHDOT: return "slantDashDot";
1855     }
1856     return "*unknown*";
1857 }
1858 
1859 static void lcl_WriteBorder( XclExpXmlStream& rStrm, sal_Int32 nElement, sal_uInt8 nLineStyle, const Color& rColor )
1860 {
1861     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
1862     if( nLineStyle == EXC_LINE_NONE )
1863         rStyleSheet->singleElement( nElement, FSEND );
1864     else if( rColor == Color( 0, 0, 0, 0 ) )
1865         rStyleSheet->singleElement( nElement,
1866                 XML_style,  ToLineStyle( nLineStyle ),
1867                 FSEND );
1868     else
1869     {
1870         rStyleSheet->startElement( nElement,
1871                 XML_style,  ToLineStyle( nLineStyle ),
1872                 FSEND );
1873         rStyleSheet->singleElement( XML_color,
1874                 XML_rgb,    XclXmlUtils::ToOString( rColor ).getStr(),
1875                 FSEND );
1876         rStyleSheet->endElement( nElement );
1877     }
1878 }
1879 
1880 void XclExpCellBorder::SaveXml( XclExpXmlStream& rStrm ) const
1881 {
1882     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
1883 
1884     XclExpPalette& rPalette = rStrm.GetRoot().GetPalette();
1885 
1886     rStyleSheet->startElement( XML_border,
1887             XML_diagonalUp,     ToPsz( mbDiagBLtoTR ),
1888             XML_diagonalDown,   ToPsz( mbDiagTLtoBR ),
1889             // OOXTODO: XML_outline,
1890             FSEND );
1891     lcl_WriteBorder( rStrm, XML_left,       mnLeftLine,     rPalette.GetColor( mnLeftColor ) );
1892     lcl_WriteBorder( rStrm, XML_right,      mnRightLine,    rPalette.GetColor( mnRightColor ) );
1893     lcl_WriteBorder( rStrm, XML_top,        mnTopLine,      rPalette.GetColor( mnTopColor ) );
1894     lcl_WriteBorder( rStrm, XML_bottom,     mnBottomLine,   rPalette.GetColor( mnBottomColor ) );
1895     lcl_WriteBorder( rStrm, XML_diagonal,   mnDiagLine,     rPalette.GetColor( mnDiagColor ) );
1896     // OOXTODO: XML_vertical, XML_horizontal
1897     rStyleSheet->endElement( XML_border );
1898 }
1899 
1900 XclExpCellArea::XclExpCellArea() :
1901     mnForeColorId( XclExpPalette::GetColorIdFromIndex( mnForeColor ) ),
1902     mnBackColorId( XclExpPalette::GetColorIdFromIndex( mnBackColor ) )
1903 {
1904 }
1905 
1906 bool XclExpCellArea::FillFromItemSet( const SfxItemSet& rItemSet, XclExpPalette& rPalette, bool bStyle )
1907 {
1908     const SvxBrushItem& rBrushItem = GETITEM( rItemSet, SvxBrushItem, ATTR_BACKGROUND );
1909     if( rBrushItem.GetColor().GetTransparency() )
1910     {
1911         mnPattern = EXC_PATT_NONE;
1912         mnForeColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT );
1913         mnBackColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWBACK );
1914     }
1915     else
1916     {
1917         mnPattern = EXC_PATT_SOLID;
1918         mnForeColorId = rPalette.InsertColor( rBrushItem.GetColor(), EXC_COLOR_CELLAREA );
1919         mnBackColorId = XclExpPalette::GetColorIdFromIndex( EXC_COLOR_WINDOWTEXT );
1920     }
1921     return ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, bStyle );
1922 }
1923 
1924 void XclExpCellArea::SetFinalColors( const XclExpPalette& rPalette )
1925 {
1926     rPalette.GetMixedColors( mnForeColor, mnBackColor, mnPattern, mnForeColorId, mnBackColorId );
1927 }
1928 
1929 void XclExpCellArea::FillToXF5( sal_uInt32& rnArea ) const
1930 {
1931     ::insert_value( rnArea, mnPattern,   16, 6 );
1932     ::insert_value( rnArea, mnForeColor,  0, 7 );
1933     ::insert_value( rnArea, mnBackColor,  7, 7 );
1934 }
1935 
1936 void XclExpCellArea::FillToXF8( sal_uInt32& rnBorder2, sal_uInt16& rnArea ) const
1937 {
1938     ::insert_value( rnBorder2, mnPattern,   26, 6 );
1939     ::insert_value( rnArea,    mnForeColor,  0, 7 );
1940     ::insert_value( rnArea,    mnBackColor,  7, 7 );
1941 }
1942 
1943 void XclExpCellArea::FillToCF8( sal_uInt16& rnPattern, sal_uInt16& rnColor ) const
1944 {
1945     XclCellArea aTmp( *this );
1946     if( !aTmp.IsTransparent() && (aTmp.mnBackColor == EXC_COLOR_WINDOWTEXT) )
1947         aTmp.mnBackColor = 0;
1948     if( aTmp.mnPattern == EXC_PATT_SOLID )
1949         ::std::swap( aTmp.mnForeColor, aTmp.mnBackColor );
1950     ::insert_value( rnColor,   aTmp.mnForeColor,  0, 7 );
1951     ::insert_value( rnColor,   aTmp.mnBackColor,  7, 7 );
1952     ::insert_value( rnPattern, aTmp.mnPattern,   10, 6 );
1953 }
1954 
1955 static const char* ToPatternType( sal_uInt8 nPattern )
1956 {
1957     switch( nPattern )
1958     {
1959         case EXC_PATT_NONE:         return "none";
1960         case EXC_PATT_SOLID:        return "solid";
1961         case EXC_PATT_50_PERC:      return "mediumGray";
1962         case EXC_PATT_75_PERC:      return "darkGray";
1963         case EXC_PATT_25_PERC:      return "lightGray";
1964         case EXC_PATT_12_5_PERC:    return "gray125";
1965         case EXC_PATT_6_25_PERC:    return "gray0625";
1966     }
1967     return "*unknown*";
1968 }
1969 
1970 void XclExpCellArea::SaveXml( XclExpXmlStream& rStrm ) const
1971 {
1972     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
1973     rStyleSheet->startElement( XML_fill,
1974             FSEND );
1975 
1976     // OOXTODO: XML_gradientFill
1977 
1978     XclExpPalette& rPalette = rStrm.GetRoot().GetPalette();
1979 
1980     if( mnPattern == EXC_PATT_NONE || ( mnForeColor == 0 && mnBackColor == 0 ) )
1981         rStyleSheet->singleElement( XML_patternFill,
1982                 XML_patternType,    ToPatternType( mnPattern ),
1983                 FSEND );
1984     else
1985     {
1986         rStyleSheet->startElement( XML_patternFill,
1987                 XML_patternType,    ToPatternType( mnPattern ),
1988                 FSEND );
1989         rStyleSheet->singleElement( XML_fgColor,
1990                 XML_rgb,    XclXmlUtils::ToOString( rPalette.GetColor( mnForeColor ) ).getStr(),
1991                 FSEND );
1992         rStyleSheet->singleElement( XML_bgColor,
1993                 XML_rgb,    XclXmlUtils::ToOString( rPalette.GetColor( mnBackColor ) ).getStr(),
1994                 FSEND );
1995         rStyleSheet->endElement( XML_patternFill );
1996     }
1997 
1998     rStyleSheet->endElement( XML_fill );
1999 }
2000 
2001 bool XclExpColor::FillFromItemSet( const SfxItemSet& rItemSet )
2002 {
2003     if( !ScfTools::CheckItem( rItemSet, ATTR_BACKGROUND, true ) )
2004         return false;
2005 
2006     const SvxBrushItem& rBrushItem = GETITEM( rItemSet, SvxBrushItem, ATTR_BACKGROUND );
2007     maColor = rBrushItem.GetColor();
2008 
2009     return true;
2010 }
2011 
2012 void XclExpColor::SaveXml( XclExpXmlStream& rStrm ) const
2013 {
2014     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
2015     rStyleSheet->startElement( XML_fill,
2016             FSEND );
2017     rStyleSheet->startElement( XML_patternFill,
2018             FSEND );
2019     rStyleSheet->singleElement( XML_bgColor,
2020             XML_rgb, XclXmlUtils::ToOString(maColor).getStr(),
2021             FSEND );
2022 
2023     rStyleSheet->endElement( XML_patternFill );
2024     rStyleSheet->endElement( XML_fill );
2025 }
2026 
2027 XclExpXFId::XclExpXFId() :
2028     mnXFId( XclExpXFBuffer::GetDefCellXFId() ),
2029     mnXFIndex( EXC_XF_DEFAULTCELL )
2030 {
2031 }
2032 
2033 XclExpXFId::XclExpXFId( sal_uInt32 nXFId ) :
2034     mnXFId( nXFId ),
2035     mnXFIndex( EXC_XF_DEFAULTCELL )
2036 {
2037 }
2038 
2039 void XclExpXFId::ConvertXFIndex( const XclExpRoot& rRoot )
2040 {
2041     mnXFIndex = rRoot.GetXFBuffer().GetXFIndex( mnXFId );
2042 }
2043 
2044 XclExpXF::XclExpXF(
2045         const XclExpRoot& rRoot, const ScPatternAttr& rPattern, sal_Int16 nScript,
2046         sal_uLong nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) :
2047     XclXFBase( true ),
2048     XclExpRoot( rRoot )
2049 {
2050     mnParentXFId = GetXFBuffer().InsertStyle( rPattern.GetStyleSheet() );
2051     Init( rPattern.GetItemSet(), nScript, nForceScNumFmt, nForceXclFont, bForceLineBreak, false );
2052 }
2053 
2054 XclExpXF::XclExpXF( const XclExpRoot& rRoot, const SfxStyleSheetBase& rStyleSheet ) :
2055     XclXFBase( false ),
2056     XclExpRoot( rRoot ),
2057     mnParentXFId( XclExpXFBuffer::GetXFIdFromIndex( EXC_XF_STYLEPARENT ) )
2058 {
2059     bool bDefStyle = (rStyleSheet.GetName() == ScGlobal::GetRscString( STR_STYLENAME_STANDARD ));
2060     sal_Int16 nScript = bDefStyle ? GetDefApiScript() : css::i18n::ScriptType::WEAK;
2061     Init( const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet(), nScript,
2062         NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND, false, bDefStyle );
2063 }
2064 
2065 XclExpXF::XclExpXF( const XclExpRoot& rRoot, bool bCellXF ) :
2066     XclXFBase( bCellXF ),
2067     XclExpRoot( rRoot ),
2068     mnParentXFId( XclExpXFBuffer::GetXFIdFromIndex( EXC_XF_STYLEPARENT ) )
2069 {
2070     InitDefault();
2071 }
2072 
2073 bool XclExpXF::Equals( const ScPatternAttr& rPattern,
2074         sal_uLong nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) const
2075 {
2076     return IsCellXF() && (mpItemSet == &rPattern.GetItemSet()) &&
2077         (!bForceLineBreak || maAlignment.mbLineBreak) &&
2078         ((nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND) || (mnScNumFmt == nForceScNumFmt)) &&
2079         ((nForceXclFont == EXC_FONT_NOTFOUND) || (mnXclFont == nForceXclFont));
2080 }
2081 
2082 bool XclExpXF::Equals( const SfxStyleSheetBase& rStyleSheet ) const
2083 {
2084     return IsStyleXF() && (mpItemSet == &const_cast< SfxStyleSheetBase& >( rStyleSheet ).GetItemSet());
2085 }
2086 
2087 void XclExpXF::SetFinalColors()
2088 {
2089     maBorder.SetFinalColors( GetPalette() );
2090     maArea.SetFinalColors( GetPalette() );
2091 }
2092 
2093 bool XclExpXF::Equals( const XclExpXF& rCmpXF ) const
2094 {
2095     return XclXFBase::Equals( rCmpXF ) &&
2096         (maProtection == rCmpXF.maProtection) && (maAlignment  == rCmpXF.maAlignment) &&
2097         (maBorder     == rCmpXF.maBorder)     && (maArea       == rCmpXF.maArea)      &&
2098         (mnXclFont    == rCmpXF.mnXclFont)    && (mnXclNumFmt  == rCmpXF.mnXclNumFmt) &&
2099         (mnParentXFId == rCmpXF.mnParentXFId);
2100 }
2101 
2102 void XclExpXF::InitDefault()
2103 {
2104     SetRecHeader( EXC_ID5_XF, (GetBiff() == EXC_BIFF8) ? 20 : 16 );
2105     mpItemSet = nullptr;
2106     mnScNumFmt = NUMBERFORMAT_ENTRY_NOT_FOUND;
2107     mnXclFont = mnXclNumFmt = 0;
2108     SetXmlIds(0, 0);
2109 }
2110 
2111 void XclExpXF::Init( const SfxItemSet& rItemSet, sal_Int16 nScript,
2112         sal_uLong nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak, bool bDefStyle )
2113 {
2114     InitDefault();
2115     mpItemSet = &rItemSet;
2116 
2117     // cell protection
2118     mbProtUsed = maProtection.FillFromItemSet( rItemSet, IsStyleXF() );
2119 
2120     // font
2121     if( nForceXclFont == EXC_FONT_NOTFOUND )
2122     {
2123         mnXclFont = GetFontBuffer().Insert( rItemSet, nScript, EXC_COLOR_CELLTEXT, bDefStyle );
2124         mbFontUsed = XclExpFontHelper::CheckItems( GetRoot(), rItemSet, nScript, IsStyleXF() );
2125     }
2126     else
2127     {
2128         mnXclFont = nForceXclFont;
2129         mbFontUsed = true;
2130     }
2131 
2132     // number format
2133     mnScNumFmt = (nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND) ?
2134         GETITEM( rItemSet, SfxUInt32Item, ATTR_VALUE_FORMAT ).GetValue() : nForceScNumFmt;
2135     mnXclNumFmt = GetNumFmtBuffer().Insert( mnScNumFmt );
2136     mbFmtUsed = ScfTools::CheckItem( rItemSet, ATTR_VALUE_FORMAT, IsStyleXF() );
2137     // alignment
2138     mbAlignUsed = maAlignment.FillFromItemSet( rItemSet, bForceLineBreak, GetBiff(), IsStyleXF() );
2139 
2140     // cell border
2141     mbBorderUsed = maBorder.FillFromItemSet( rItemSet, GetPalette(), GetBiff(), IsStyleXF() );
2142 
2143     // background area
2144     mbAreaUsed = maArea.FillFromItemSet( rItemSet, GetPalette(), IsStyleXF() );
2145 
2146     // set all b***Used flags to true in "Default"/"Normal" style
2147     if( bDefStyle )
2148         SetAllUsedFlags( true );
2149 }
2150 
2151 sal_uInt8 XclExpXF::GetUsedFlags() const
2152 {
2153     sal_uInt8 nUsedFlags = 0;
2154     /*  In cell XFs a set bit means a used attribute, in style XFs a cleared bit.
2155         "mbCellXF == mb***Used" evaluates to correct value in cell and style XFs. */
2156     ::set_flag( nUsedFlags, EXC_XF_DIFF_PROT,   mbCellXF == mbProtUsed );
2157     ::set_flag( nUsedFlags, EXC_XF_DIFF_FONT,   mbCellXF == mbFontUsed );
2158     ::set_flag( nUsedFlags, EXC_XF_DIFF_VALFMT, mbCellXF == mbFmtUsed );
2159     ::set_flag( nUsedFlags, EXC_XF_DIFF_ALIGN,  mbCellXF == mbAlignUsed );
2160     ::set_flag( nUsedFlags, EXC_XF_DIFF_BORDER, mbCellXF == mbBorderUsed );
2161     ::set_flag( nUsedFlags, EXC_XF_DIFF_AREA,   mbCellXF == mbAreaUsed );
2162     return nUsedFlags;
2163 }
2164 
2165 void XclExpXF::WriteBody5( XclExpStream& rStrm )
2166 {
2167     sal_uInt16 nTypeProt = 0, nAlign = 0;
2168     sal_uInt32 nArea = 0, nBorder = 0;
2169 
2170     ::set_flag( nTypeProt, EXC_XF_STYLE, IsStyleXF() );
2171     ::insert_value( nTypeProt, mnParent, 4, 12 );
2172     ::insert_value( nAlign, GetUsedFlags(), 10, 6 );
2173 
2174     maProtection.FillToXF3( nTypeProt );
2175     maAlignment.FillToXF5( nAlign );
2176     maBorder.FillToXF5( nBorder, nArea );
2177     maArea.FillToXF5( nArea );
2178 
2179     rStrm << mnXclFont << mnXclNumFmt << nTypeProt << nAlign << nArea << nBorder;
2180 }
2181 
2182 void XclExpXF::WriteBody8( XclExpStream& rStrm )
2183 {
2184     sal_uInt16 nTypeProt = 0, nAlign = 0, nMiscAttrib = 0, nArea = 0;
2185     sal_uInt32 nBorder1 = 0, nBorder2 = 0;
2186 
2187     ::set_flag( nTypeProt, EXC_XF_STYLE, IsStyleXF() );
2188     ::insert_value( nTypeProt, mnParent, 4, 12 );
2189     ::insert_value( nMiscAttrib, GetUsedFlags(), 10, 6 );
2190 
2191     maProtection.FillToXF3( nTypeProt );
2192     maAlignment.FillToXF8( nAlign, nMiscAttrib );
2193     maBorder.FillToXF8( nBorder1, nBorder2 );
2194     maArea.FillToXF8( nBorder2, nArea );
2195 
2196     rStrm << mnXclFont << mnXclNumFmt << nTypeProt << nAlign << nMiscAttrib << nBorder1 << nBorder2 << nArea;
2197 }
2198 
2199 void XclExpXF::WriteBody( XclExpStream& rStrm )
2200 {
2201     XclExpXFId aParentId( mnParentXFId );
2202     aParentId.ConvertXFIndex( GetRoot() );
2203     mnParent = aParentId.mnXFIndex;
2204     switch( GetBiff() )
2205     {
2206         case EXC_BIFF5: WriteBody5( rStrm );    break;
2207         case EXC_BIFF8: WriteBody8( rStrm );    break;
2208         default:        DBG_ERROR_BIFF();
2209     }
2210 }
2211 
2212 void XclExpXF::SetXmlIds( sal_uInt32 nBorderId, sal_uInt32 nFillId )
2213 {
2214     mnBorderId = nBorderId;
2215     mnFillId   = nFillId;
2216 }
2217 
2218 void XclExpXF::SaveXml( XclExpXmlStream& rStrm )
2219 {
2220     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
2221 
2222     sal_Int32 nXfId = 0;
2223     const XclExpXF* pStyleXF = nullptr;
2224     if( IsCellXF() )
2225     {
2226         sal_uInt16 nXFIndex = rStrm.GetRoot().GetXFBuffer().GetXFIndex( mnParentXFId );
2227         nXfId = rStrm.GetRoot().GetXFBuffer().GetXmlStyleIndex( nXFIndex );
2228         pStyleXF = rStrm.GetRoot().GetXFBuffer().GetXFById( mnParentXFId );
2229     }
2230 
2231     rStyleSheet->startElement( XML_xf,
2232             XML_numFmtId,           OString::number(  mnXclNumFmt ).getStr(),
2233             XML_fontId,             OString::number(  mnXclFont ).getStr(),
2234             XML_fillId,             OString::number(  mnFillId ).getStr(),
2235             XML_borderId,           OString::number(  mnBorderId ).getStr(),
2236             XML_xfId,               IsStyleXF() ? nullptr : OString::number( nXfId ).getStr(),
2237             // OOXTODO: XML_quotePrefix,
2238             // OOXTODO: XML_pivotButton,
2239             // OOXTODO: XML_applyNumberFormat,  ;
2240             XML_applyFont,          ToPsz( mbFontUsed ),
2241             // OOXTODO: XML_applyFill,
2242             XML_applyBorder,        ToPsz( mbBorderUsed ),
2243             XML_applyAlignment,     ToPsz( mbAlignUsed ),
2244             XML_applyProtection,    ToPsz( mbProtUsed ),
2245             FSEND );
2246     if( mbAlignUsed )
2247         maAlignment.SaveXml( rStrm );
2248     else if ( pStyleXF )
2249         pStyleXF->GetAlignmentData().SaveXml( rStrm );
2250     if( mbProtUsed )
2251         maProtection.SaveXml( rStrm );
2252     else if ( pStyleXF )
2253         pStyleXF->GetProtectionData().SaveXml( rStrm );
2254 
2255     // OOXTODO: XML_extLst
2256     rStyleSheet->endElement( XML_xf );
2257 }
2258 
2259 XclExpDefaultXF::XclExpDefaultXF( const XclExpRoot& rRoot, bool bCellXF ) :
2260     XclExpXF( rRoot, bCellXF )
2261 {
2262 }
2263 
2264 void XclExpDefaultXF::SetFont( sal_uInt16 nXclFont )
2265 {
2266     mnXclFont = nXclFont;
2267     mbFontUsed = true;
2268 }
2269 
2270 void XclExpDefaultXF::SetNumFmt( sal_uInt16 nXclNumFmt )
2271 {
2272     mnXclNumFmt = nXclNumFmt;
2273     mbFmtUsed = true;
2274 }
2275 
2276 XclExpStyle::XclExpStyle( sal_uInt32 nXFId, const OUString& rStyleName ) :
2277     XclExpRecord( EXC_ID_STYLE, 4 ),
2278     maName( rStyleName ),
2279     maXFId( nXFId ),
2280     mnStyleId( EXC_STYLE_USERDEF ),
2281     mnLevel( EXC_STYLE_NOLEVEL )
2282 {
2283     OSL_ENSURE( !maName.isEmpty(), "XclExpStyle::XclExpStyle - empty style name" );
2284 #if OSL_DEBUG_LEVEL > 0
2285     sal_uInt8 nStyleId, nLevel; // do not use members for debug tests
2286     OSL_ENSURE( !XclTools::GetBuiltInStyleId( nStyleId, nLevel, maName ),
2287         "XclExpStyle::XclExpStyle - this is a built-in style" );
2288 #endif
2289 }
2290 
2291 XclExpStyle::XclExpStyle( sal_uInt32 nXFId, sal_uInt8 nStyleId, sal_uInt8 nLevel ) :
2292     XclExpRecord( EXC_ID_STYLE, 4 ),
2293     maXFId( nXFId ),
2294     mnStyleId( nStyleId ),
2295     mnLevel( nLevel )
2296 {
2297 }
2298 
2299 void XclExpStyle::WriteBody( XclExpStream& rStrm )
2300 {
2301     maXFId.ConvertXFIndex( rStrm.GetRoot() );
2302     ::set_flag( maXFId.mnXFIndex, EXC_STYLE_BUILTIN, IsBuiltIn() );
2303     rStrm << maXFId.mnXFIndex;
2304 
2305     if( IsBuiltIn() )
2306     {
2307         rStrm << mnStyleId << mnLevel;
2308     }
2309     else
2310     {
2311         XclExpString aNameEx;
2312         if( rStrm.GetRoot().GetBiff() == EXC_BIFF8 )
2313             aNameEx.Assign( maName );
2314         else
2315             aNameEx.AssignByte( maName, rStrm.GetRoot().GetTextEncoding(), XclStrFlags::EightBitLength );
2316         rStrm << aNameEx;
2317     }
2318 }
2319 
2320 static const char* lcl_StyleNameFromId( sal_Int32 nStyleId )
2321 {
2322     switch( nStyleId )
2323     {
2324         case 0:     return "Normal";
2325         case 3:     return "Comma";
2326         case 4:     return "Currency";
2327         case 5:     return "Percent";
2328         case 6:     return "Comma [0]";
2329         case 7:     return "Currency [0]";
2330     }
2331     return "*unknown*";
2332 }
2333 
2334 void XclExpStyle::SaveXml( XclExpXmlStream& rStrm )
2335 {
2336     OString sName;
2337     if( IsBuiltIn() )
2338     {
2339         sName = OString( lcl_StyleNameFromId( mnStyleId ) );
2340     }
2341     else
2342         sName = XclXmlUtils::ToOString( maName );
2343     // get the index in sortedlist associated with the mnXId
2344     sal_Int32 nXFId = rStrm.GetRoot().GetXFBuffer().GetXFIndex( maXFId.mnXFId );
2345     // get the style index associated with index into sortedlist
2346     nXFId = rStrm.GetRoot().GetXFBuffer().GetXmlStyleIndex( nXFId );
2347     rStrm.GetCurrentStream()->singleElement( XML_cellStyle,
2348             XML_name,           sName.getStr(),
2349             XML_xfId,           OString::number( nXFId ).getStr(),
2350 // builtinId of 54 or above is invalid according to OpenXML SDK validator.
2351 #define CELL_STYLE_MAX_BUILTIN_ID 54
2352                                              XML_builtinId, OString::number( std::min( static_cast<sal_Int32>( CELL_STYLE_MAX_BUILTIN_ID - 1 ), static_cast <sal_Int32>( mnStyleId ) ) ).getStr(),
2353             // OOXTODO: XML_iLevel,
2354             // OOXTODO: XML_hidden,
2355             XML_customBuiltin,  ToPsz( ! IsBuiltIn() ),
2356             FSEND );
2357     // OOXTODO: XML_extLst
2358 }
2359 
2360 namespace {
2361 
2362 const sal_uInt32 EXC_XFLIST_INDEXBASE   = 0xFFFE0000;
2363 /** Maximum count of XF records to store in the XF list (performance). */
2364 const sal_uInt32 EXC_XFLIST_HARDLIMIT   = 256 * 1024;
2365 
2366 bool lclIsBuiltInStyle( const OUString& rStyleName )
2367 {
2368     return
2369         XclTools::IsBuiltInStyleName( rStyleName ) ||
2370         XclTools::IsCondFormatStyleName( rStyleName );
2371 }
2372 
2373 } // namespace
2374 
2375 XclExpXFBuffer::XclExpBuiltInInfo::XclExpBuiltInInfo() :
2376     mnStyleId( EXC_STYLE_USERDEF ),
2377     mnLevel( EXC_STYLE_NOLEVEL ),
2378     mbPredefined( true ),
2379     mbHasStyleRec( false )
2380 {
2381 }
2382 
2383 /** Predicate for search algorithm. */
2384 struct XclExpBorderPred
2385 {
2386     const XclExpCellBorder&
2387                         mrBorder;
2388     explicit     XclExpBorderPred( const XclExpCellBorder& rBorder ) : mrBorder( rBorder ) {}
2389     bool                operator()( const XclExpCellBorder& rBorder ) const;
2390 };
2391 
2392 bool XclExpBorderPred::operator()( const XclExpCellBorder& rBorder ) const
2393 {
2394     return
2395         mrBorder.mnLeftColor     == rBorder.mnLeftColor &&
2396         mrBorder.mnRightColor    == rBorder.mnRightColor &&
2397         mrBorder.mnTopColor      == rBorder.mnTopColor &&
2398         mrBorder.mnBottomColor   == rBorder.mnBottomColor &&
2399         mrBorder.mnDiagColor     == rBorder.mnDiagColor &&
2400         mrBorder.mnLeftLine      == rBorder.mnLeftLine &&
2401         mrBorder.mnRightLine     == rBorder.mnRightLine &&
2402         mrBorder.mnTopLine       == rBorder.mnTopLine &&
2403         mrBorder.mnBottomLine    == rBorder.mnBottomLine &&
2404         mrBorder.mnDiagLine      == rBorder.mnDiagLine &&
2405         mrBorder.mbDiagTLtoBR    == rBorder.mbDiagTLtoBR &&
2406         mrBorder.mbDiagBLtoTR    == rBorder.mbDiagBLtoTR &&
2407         mrBorder.mnLeftColorId   == rBorder.mnLeftColorId &&
2408         mrBorder.mnRightColorId  == rBorder.mnRightColorId &&
2409         mrBorder.mnTopColorId    == rBorder.mnTopColorId &&
2410         mrBorder.mnBottomColorId == rBorder.mnBottomColorId &&
2411         mrBorder.mnDiagColorId   == rBorder.mnDiagColorId;
2412 }
2413 
2414 struct XclExpFillPred
2415 {
2416     const XclExpCellArea&
2417                         mrFill;
2418     explicit     XclExpFillPred( const XclExpCellArea& rFill ) : mrFill( rFill ) {}
2419     bool                operator()( const XclExpCellArea& rFill ) const;
2420 };
2421 
2422 bool XclExpFillPred::operator()( const XclExpCellArea& rFill ) const
2423 {
2424     return
2425         mrFill.mnForeColor      == rFill.mnForeColor &&
2426         mrFill.mnBackColor      == rFill.mnBackColor &&
2427         mrFill.mnPattern        == rFill.mnPattern &&
2428         mrFill.mnForeColorId    == rFill.mnForeColorId &&
2429         mrFill.mnBackColorId    == rFill.mnBackColorId;
2430 }
2431 
2432 XclExpXFBuffer::XclExpXFBuffer( const XclExpRoot& rRoot ) :
2433     XclExpRoot( rRoot )
2434 {
2435 }
2436 
2437 void XclExpXFBuffer::Initialize()
2438 {
2439     InsertDefaultRecords();
2440     InsertUserStyles();
2441 }
2442 
2443 sal_uInt32 XclExpXFBuffer::Insert( const ScPatternAttr* pPattern, sal_Int16 nScript )
2444 {
2445     return InsertCellXF( pPattern, nScript, NUMBERFORMAT_ENTRY_NOT_FOUND, EXC_FONT_NOTFOUND, false );
2446 }
2447 
2448 sal_uInt32 XclExpXFBuffer::InsertWithFont( const ScPatternAttr* pPattern, sal_Int16 nScript,
2449         sal_uInt16 nForceXclFont, bool bForceLineBreak )
2450 {
2451     return InsertCellXF( pPattern, nScript, NUMBERFORMAT_ENTRY_NOT_FOUND, nForceXclFont, bForceLineBreak );
2452 }
2453 
2454 sal_uInt32 XclExpXFBuffer::InsertWithNumFmt( const ScPatternAttr* pPattern, sal_Int16 nScript, sal_uLong nForceScNumFmt, bool bForceLineBreak )
2455 {
2456     return InsertCellXF( pPattern, nScript, nForceScNumFmt, EXC_FONT_NOTFOUND, bForceLineBreak );
2457 }
2458 
2459 sal_uInt32 XclExpXFBuffer::InsertStyle( const SfxStyleSheetBase* pStyleSheet )
2460 {
2461     return pStyleSheet ? InsertStyleXF( *pStyleSheet ) : GetXFIdFromIndex( EXC_XF_DEFAULTSTYLE );
2462 }
2463 
2464 sal_uInt32 XclExpXFBuffer::GetXFIdFromIndex( sal_uInt16 nXFIndex )
2465 {
2466     return EXC_XFLIST_INDEXBASE | nXFIndex;
2467 }
2468 
2469 sal_uInt32 XclExpXFBuffer::GetDefCellXFId()
2470 {
2471     return GetXFIdFromIndex( EXC_XF_DEFAULTCELL );
2472 }
2473 
2474 const XclExpXF* XclExpXFBuffer::GetXFById( sal_uInt32 nXFId ) const
2475 {
2476     return maXFList.GetRecord( nXFId ).get();
2477 }
2478 
2479 void XclExpXFBuffer::Finalize()
2480 {
2481     for( size_t nPos = 0, nSize = maXFList.GetSize(); nPos < nSize; ++nPos )
2482         maXFList.GetRecord( nPos )->SetFinalColors();
2483 
2484     sal_uInt32 nTotalCount = static_cast< sal_uInt32 >( maXFList.GetSize() );
2485     sal_uInt32 nId;
2486     maXFIndexVec.resize( nTotalCount, EXC_XF_DEFAULTCELL );
2487     maStyleIndexes.resize( nTotalCount, EXC_XF_DEFAULTCELL );
2488     maCellIndexes.resize( nTotalCount, EXC_XF_DEFAULTCELL );
2489 
2490     XclExpBuiltInMap::const_iterator aBuiltInEnd = maBuiltInMap.end();
2491     /*  nMaxBuiltInXFId used to decide faster whether an XF record is
2492         user-defined. If the current XF ID is greater than this value,
2493         maBuiltInMap doesn't need to be searched. */
2494     sal_uInt32 nMaxBuiltInXFId = maBuiltInMap.empty() ? 0 : maBuiltInMap.rbegin()->first;
2495 
2496     // *** map all built-in XF records (cell and style) *** -------------------
2497 
2498     // do not change XF order -> std::map<> iterates elements in ascending order
2499     for( XclExpBuiltInMap::const_iterator aIt = maBuiltInMap.begin(); aIt != aBuiltInEnd; ++aIt )
2500         AppendXFIndex( aIt->first );
2501 
2502     // *** insert all user-defined style XF records, without reduce *** -------
2503 
2504     sal_uInt32 nStyleXFCount = 0;       // counts up to EXC_XF_MAXSTYLECOUNT limit
2505 
2506     for( nId = 0; nId < nTotalCount; ++nId )
2507     {
2508         XclExpXFRef xXF = maXFList.GetRecord( nId );
2509         if( xXF->IsStyleXF() && ((nId > nMaxBuiltInXFId) || (maBuiltInMap.find( nId ) == aBuiltInEnd)) )
2510         {
2511             if( nStyleXFCount < EXC_XF_MAXSTYLECOUNT )
2512             {
2513                 // maximum count of styles not reached
2514                 AppendXFIndex( nId );
2515                 ++nStyleXFCount;
2516             }
2517             else
2518             {
2519                 /*  Maximum count of styles reached - do not append more
2520                     pointers to XFs; use default style XF instead; do not break
2521                     the loop to initialize all maXFIndexVec elements. */
2522                 maXFIndexVec[ nId ] = EXC_XF_DEFAULTSTYLE;
2523             }
2524         }
2525     }
2526 
2527     // *** insert all cell XF records *** -------------------------------------
2528 
2529     // start position to search for equal inserted XF records
2530     size_t nSearchStart = maSortedXFList.GetSize();
2531 
2532     // break the loop if XF limit reached - maXFIndexVec is already initialized with default index
2533     XclExpXFRef xDefCellXF = maXFList.GetRecord( EXC_XF_DEFAULTCELL );
2534     for( nId = 0; (nId < nTotalCount) && (maSortedXFList.GetSize() < EXC_XF_MAXCOUNT); ++nId )
2535     {
2536         XclExpXFRef xXF = maXFList.GetRecord( nId );
2537         if( xXF->IsCellXF() && ((nId > nMaxBuiltInXFId) || (maBuiltInMap.find( nId ) == aBuiltInEnd)) )
2538         {
2539             // try to find an XF record equal to *xXF, which is already inserted
2540             sal_uInt16 nFoundIndex = EXC_XF_NOTFOUND;
2541 
2542             // first try if it is equal to the default cell XF
2543             if( xDefCellXF->Equals( *xXF ) )
2544             {
2545                 nFoundIndex = EXC_XF_DEFAULTCELL;
2546             }
2547             else for( size_t nSearchPos = nSearchStart, nSearchEnd = maSortedXFList.GetSize();
2548                         (nSearchPos < nSearchEnd) && (nFoundIndex == EXC_XF_NOTFOUND); ++nSearchPos )
2549             {
2550                 if( maSortedXFList.GetRecord( nSearchPos )->Equals( *xXF ) )
2551                     nFoundIndex = static_cast< sal_uInt16 >( nSearchPos );
2552             }
2553 
2554             if( nFoundIndex != EXC_XF_NOTFOUND )
2555                 // equal XF already in the list, use its resulting XF index
2556                 maXFIndexVec[ nId ] = nFoundIndex;
2557             else
2558                 AppendXFIndex( nId );
2559         }
2560     }
2561 
2562     sal_uInt16 nXmlStyleIndex   = 0;
2563     sal_uInt16 nXmlCellIndex    = 0;
2564 
2565     size_t nXFCount = maSortedXFList.GetSize();
2566     for( size_t i = 0; i < nXFCount; ++i )
2567     {
2568         XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i );
2569         if( xXF->IsStyleXF() )
2570             maStyleIndexes[ i ] = nXmlStyleIndex++;
2571         else
2572             maCellIndexes[ i ] = nXmlCellIndex++;
2573     }
2574 }
2575 
2576 sal_uInt16 XclExpXFBuffer::GetXFIndex( sal_uInt32 nXFId ) const
2577 {
2578     sal_uInt16 nXFIndex = EXC_XF_DEFAULTSTYLE;
2579     if( nXFId >= EXC_XFLIST_INDEXBASE )
2580         nXFIndex = static_cast< sal_uInt16 >( nXFId & ~EXC_XFLIST_INDEXBASE );
2581     else if( nXFId < maXFIndexVec.size() )
2582         nXFIndex = maXFIndexVec[ nXFId ];
2583     return nXFIndex;
2584 }
2585 
2586 sal_Int32 XclExpXFBuffer::GetXmlStyleIndex( sal_uInt32 nXFIndex ) const
2587 {
2588     OSL_ENSURE( nXFIndex < maStyleIndexes.size(), "XclExpXFBuffer::GetXmlStyleIndex - invalid index!" );
2589     if( nXFIndex > maStyleIndexes.size() )
2590         return 0;   // should be caught/debugged via above assert; return "valid" index.
2591     return maStyleIndexes[ nXFIndex ];
2592 }
2593 
2594 sal_Int32 XclExpXFBuffer::GetXmlCellIndex( sal_uInt32 nXFIndex ) const
2595 {
2596     OSL_ENSURE( nXFIndex < maCellIndexes.size(), "XclExpXFBuffer::GetXmlStyleIndex - invalid index!" );
2597     if( nXFIndex > maCellIndexes.size() )
2598         return 0;   // should be caught/debugged via above assert; return "valid" index.
2599     return maCellIndexes[ nXFIndex ];
2600 }
2601 
2602 void XclExpXFBuffer::Save( XclExpStream& rStrm )
2603 {
2604     // save all XF records contained in the maSortedXFList vector (sorted by XF index)
2605     maSortedXFList.Save( rStrm );
2606     // save all STYLE records
2607     maStyleList.Save( rStrm );
2608 }
2609 
2610 static void lcl_GetCellCounts( const XclExpRecordList< XclExpXF >& rXFList, sal_Int32& rCells, sal_Int32& rStyles )
2611 {
2612     rCells  = 0;
2613     rStyles = 0;
2614     size_t nXFCount = rXFList.GetSize();
2615     for( size_t i = 0; i < nXFCount; ++i )
2616     {
2617         XclExpRecordList< XclExpXF >::RecordRefType xXF = rXFList.GetRecord( i );
2618         if( xXF->IsCellXF() )
2619             ++rCells;
2620         else if( xXF->IsStyleXF() )
2621             ++rStyles;
2622     }
2623 }
2624 
2625 void XclExpXFBuffer::SaveXml( XclExpXmlStream& rStrm )
2626 {
2627     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
2628 
2629     rStyleSheet->startElement( XML_fills,
2630             XML_count,  OString::number(  maFills.size() ).getStr(),
2631             FSEND );
2632     for( XclExpFillList::iterator aIt = maFills.begin(), aEnd = maFills.end();
2633             aIt != aEnd; ++aIt )
2634     {
2635         aIt->SaveXml( rStrm );
2636     }
2637     rStyleSheet->endElement( XML_fills );
2638 
2639     rStyleSheet->startElement( XML_borders,
2640             XML_count,  OString::number(  maBorders.size() ).getStr(),
2641             FSEND );
2642     for( XclExpBorderList::iterator aIt = maBorders.begin(), aEnd = maBorders.end();
2643             aIt != aEnd; ++aIt )
2644     {
2645         aIt->SaveXml( rStrm );
2646     }
2647     rStyleSheet->endElement( XML_borders );
2648 
2649     // save all XF records contained in the maSortedXFList vector (sorted by XF index)
2650     sal_Int32 nCells, nStyles;
2651     lcl_GetCellCounts( maSortedXFList, nCells, nStyles );
2652 
2653     if( nStyles > 0 )
2654     {
2655         rStyleSheet->startElement( XML_cellStyleXfs,
2656                 XML_count,  OString::number( nStyles ).getStr(),
2657                 FSEND );
2658         size_t nXFCount = maSortedXFList.GetSize();
2659         for( size_t i = 0; i < nXFCount; ++i )
2660         {
2661             XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i );
2662             if( ! xXF->IsStyleXF() )
2663                 continue;
2664             SaveXFXml( rStrm, *xXF );
2665         }
2666         rStyleSheet->endElement( XML_cellStyleXfs );
2667     }
2668 
2669     if( nCells > 0 )
2670     {
2671         rStyleSheet->startElement( XML_cellXfs,
2672                 XML_count,  OString::number( nCells ).getStr(),
2673                 FSEND );
2674         size_t nXFCount = maSortedXFList.GetSize();
2675         for( size_t i = 0; i < nXFCount; ++i )
2676         {
2677             XclExpXFList::RecordRefType xXF = maSortedXFList.GetRecord( i );
2678             if( ! xXF->IsCellXF() )
2679                 continue;
2680             SaveXFXml( rStrm, *xXF );
2681         }
2682         rStyleSheet->endElement( XML_cellXfs );
2683     }
2684 
2685     // save all STYLE records
2686     rStyleSheet->startElement( XML_cellStyles,
2687             XML_count,  OString::number(  maStyleList.GetSize() ).getStr(),
2688             FSEND );
2689     maStyleList.SaveXml( rStrm );
2690     rStyleSheet->endElement( XML_cellStyles );
2691 }
2692 
2693 void XclExpXFBuffer::SaveXFXml( XclExpXmlStream& rStrm, XclExpXF& rXF )
2694 {
2695     XclExpBorderList::iterator aBorderPos =
2696         std::find_if( maBorders.begin(), maBorders.end(), XclExpBorderPred( rXF.GetBorderData() ) );
2697     OSL_ENSURE( aBorderPos != maBorders.end(), "XclExpXFBuffer::SaveXml - Invalid @borderId!" );
2698     XclExpFillList::iterator aFillPos =
2699         std::find_if( maFills.begin(), maFills.end(), XclExpFillPred( rXF.GetAreaData() ) );
2700     OSL_ENSURE( aFillPos != maFills.end(), "XclExpXFBuffer::SaveXml - Invalid @fillId!" );
2701 
2702     sal_Int32 nBorderId = 0, nFillId = 0;
2703     if( aBorderPos != maBorders.end() )
2704         nBorderId = std::distance( maBorders.begin(), aBorderPos );
2705     if( aFillPos != maFills.end() )
2706         nFillId = std::distance( maFills.begin(), aFillPos );
2707 
2708     rXF.SetXmlIds( nBorderId, nFillId );
2709     rXF.SaveXml( rStrm );
2710 }
2711 
2712 sal_uInt32 XclExpXFBuffer::FindXF( const ScPatternAttr& rPattern,
2713         sal_uLong nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak ) const
2714 {
2715     for( size_t nPos = 0, nSize = maXFList.GetSize(); nPos < nSize; ++nPos )
2716         if( maXFList.GetRecord( nPos )->Equals( rPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak ) )
2717             return static_cast< sal_uInt32 >( nPos );
2718     return EXC_XFID_NOTFOUND;
2719 }
2720 
2721 sal_uInt32 XclExpXFBuffer::FindXF( const SfxStyleSheetBase& rStyleSheet ) const
2722 {
2723     for( size_t nPos = 0, nSize = maXFList.GetSize(); nPos < nSize; ++nPos )
2724         if( maXFList.GetRecord( nPos )->Equals( rStyleSheet ) )
2725             return static_cast< sal_uInt32 >( nPos );
2726     return EXC_XFID_NOTFOUND;
2727 }
2728 
2729 sal_uInt32 XclExpXFBuffer::FindBuiltInXF( sal_uInt8 nStyleId, sal_uInt8 nLevel ) const
2730 {
2731     for( XclExpBuiltInMap::const_iterator aIt = maBuiltInMap.begin(), aEnd = maBuiltInMap.end(); aIt != aEnd; ++aIt )
2732         if( (aIt->second.mnStyleId == nStyleId) && (aIt->second.mnLevel == nLevel) )
2733             return aIt->first;
2734     return EXC_XFID_NOTFOUND;
2735 }
2736 
2737 sal_uInt32 XclExpXFBuffer::InsertCellXF( const ScPatternAttr* pPattern, sal_Int16 nScript,
2738         sal_uLong nForceScNumFmt, sal_uInt16 nForceXclFont, bool bForceLineBreak )
2739 {
2740     const ScPatternAttr* pDefPattern = GetDoc().GetDefPattern();
2741     if( !pPattern )
2742         pPattern = pDefPattern;
2743 
2744     // special handling for default cell formatting
2745     if( (pPattern == pDefPattern) && !bForceLineBreak &&
2746         (nForceScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND) &&
2747         (nForceXclFont == EXC_FONT_NOTFOUND) )
2748     {
2749         // Is it the first try to insert the default cell format?
2750         bool& rbPredefined = maBuiltInMap[ EXC_XF_DEFAULTCELL ].mbPredefined;
2751         if( rbPredefined )
2752         {
2753             // replace default cell pattern
2754             XclExpXFRef xNewXF( new XclExpXF( GetRoot(), *pPattern, nScript ) );
2755             maXFList.ReplaceRecord( xNewXF, EXC_XF_DEFAULTCELL );
2756             rbPredefined = false;
2757         }
2758         return GetDefCellXFId();
2759     }
2760 
2761     sal_uInt32 nXFId = FindXF( *pPattern, nForceScNumFmt, nForceXclFont, bForceLineBreak );
2762     if( nXFId == EXC_XFID_NOTFOUND )
2763     {
2764         // not found - insert new cell XF
2765         if( maXFList.GetSize() < EXC_XFLIST_HARDLIMIT )
2766         {
2767             maXFList.AppendNewRecord( new XclExpXF(
2768                 GetRoot(), *pPattern, nScript, nForceScNumFmt, nForceXclFont, bForceLineBreak ) );
2769             // do not set nXFId before the AppendNewRecord() call - it may insert 2 XFs (style+cell)
2770             nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() - 1 );
2771         }
2772         else
2773         {
2774             // list full - fall back to default cell XF
2775             nXFId = GetDefCellXFId();
2776         }
2777     }
2778     return nXFId;
2779 }
2780 
2781 sal_uInt32 XclExpXFBuffer::InsertStyleXF( const SfxStyleSheetBase& rStyleSheet )
2782 {
2783     // *** try, if it is a built-in style - create new XF or replace existing predefined XF ***
2784 
2785     sal_uInt8 nStyleId, nLevel;
2786     if( XclTools::GetBuiltInStyleId( nStyleId, nLevel, rStyleSheet.GetName() ) )
2787     {
2788         // try to find the built-in XF record (if already created in InsertDefaultRecords())
2789         sal_uInt32 nXFId = FindBuiltInXF( nStyleId, nLevel );
2790         if( nXFId == EXC_XFID_NOTFOUND )
2791         {
2792             // built-in style XF not yet created - do it now
2793             XclExpXFRef xXF( new XclExpXF( GetRoot(), rStyleSheet ) );
2794             nXFId = AppendBuiltInXFWithStyle( xXF, nStyleId, nLevel );
2795             // this new XF record is not predefined
2796             maBuiltInMap[ nXFId ].mbPredefined = false;
2797         }
2798         else
2799         {
2800             OSL_ENSURE( maXFList.HasRecord( nXFId ), "XclExpXFBuffer::InsertStyleXF - built-in XF not found" );
2801             // XF record still predefined? -> Replace with real XF
2802             bool& rbPredefined = maBuiltInMap[ nXFId ].mbPredefined;
2803             if( rbPredefined )
2804             {
2805                 // replace predefined built-in style (ReplaceRecord() deletes old record)
2806                 maXFList.ReplaceRecord( std::make_shared<XclExpXF>( GetRoot(), rStyleSheet ), nXFId );
2807                 rbPredefined = false;
2808             }
2809         }
2810 
2811         // STYLE already inserted? (may be not, i.e. for RowLevel/ColLevel or Hyperlink styles)
2812         bool& rbHasStyleRec = maBuiltInMap[ nXFId ].mbHasStyleRec;
2813         if( !rbHasStyleRec )
2814         {
2815             maStyleList.AppendNewRecord( new XclExpStyle( nXFId, nStyleId, nLevel ) );
2816             rbHasStyleRec = true;
2817         }
2818 
2819         return nXFId;
2820     }
2821 
2822     // *** try to find the XF record of a user-defined style ***
2823 
2824     sal_uInt32 nXFId = FindXF( rStyleSheet );
2825     if( nXFId == EXC_XFID_NOTFOUND )
2826     {
2827         // not found - insert new style XF and STYLE
2828         nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() );
2829         if( nXFId < EXC_XFLIST_HARDLIMIT )
2830         {
2831             maXFList.AppendNewRecord( new XclExpXF( GetRoot(), rStyleSheet ) );
2832             // create the STYLE record
2833             if( !rStyleSheet.GetName().isEmpty() )
2834                 maStyleList.AppendNewRecord( new XclExpStyle( nXFId, rStyleSheet.GetName() ) );
2835         }
2836         else
2837             // list full - fall back to default style XF
2838             nXFId = GetXFIdFromIndex( EXC_XF_DEFAULTSTYLE );
2839     }
2840     return nXFId;
2841 }
2842 
2843 void XclExpXFBuffer::InsertUserStyles()
2844 {
2845     SfxStyleSheetIterator aStyleIter( GetDoc().GetStyleSheetPool(), SfxStyleFamily::Para );
2846     for( SfxStyleSheetBase* pStyleSheet = aStyleIter.First(); pStyleSheet; pStyleSheet = aStyleIter.Next() )
2847         if( pStyleSheet->IsUserDefined() && !lclIsBuiltInStyle( pStyleSheet->GetName() ) )
2848             InsertStyleXF( *pStyleSheet );
2849 }
2850 
2851 sal_uInt32 XclExpXFBuffer::AppendBuiltInXF( XclExpXFRef const & xXF, sal_uInt8 nStyleId, sal_uInt8 nLevel )
2852 {
2853     sal_uInt32 nXFId = static_cast< sal_uInt32 >( maXFList.GetSize() );
2854     maXFList.AppendRecord( xXF );
2855     XclExpBuiltInInfo& rInfo = maBuiltInMap[ nXFId ];
2856     rInfo.mnStyleId = nStyleId;
2857     rInfo.mnLevel = nLevel;
2858     rInfo.mbPredefined = true;
2859     return nXFId;
2860 }
2861 
2862 sal_uInt32 XclExpXFBuffer::AppendBuiltInXFWithStyle( XclExpXFRef const & xXF, sal_uInt8 nStyleId, sal_uInt8 nLevel )
2863 {
2864     sal_uInt32 nXFId = AppendBuiltInXF( xXF, nStyleId, nLevel );
2865     maStyleList.AppendNewRecord( new XclExpStyle( nXFId, nStyleId, nLevel ) );
2866     maBuiltInMap[ nXFId ].mbHasStyleRec = true;  // mark existing STYLE record
2867     return nXFId;
2868 }
2869 
2870 static XclExpCellArea lcl_GetPatternFill_None()
2871 {
2872     XclExpCellArea aFill;
2873     aFill.mnPattern = EXC_PATT_NONE;
2874     return aFill;
2875 }
2876 
2877 static XclExpCellArea lcl_GetPatternFill_Gray125()
2878 {
2879     XclExpCellArea aFill;
2880     aFill.mnPattern     = EXC_PATT_12_5_PERC;
2881     aFill.mnForeColor   = 0;
2882     aFill.mnBackColor   = 0;
2883     return aFill;
2884 }
2885 
2886 void XclExpXFBuffer::InsertDefaultRecords()
2887 {
2888     maFills.push_back( lcl_GetPatternFill_None() );
2889     maFills.push_back( lcl_GetPatternFill_Gray125() );
2890 
2891     // index 0: default style
2892     if( SfxStyleSheetBase* pDefStyleSheet = GetStyleSheetPool().Find( ScGlobal::GetRscString( STR_STYLENAME_STANDARD ), SfxStyleFamily::Para ) )
2893     {
2894         XclExpXFRef xDefStyle( new XclExpXF( GetRoot(), *pDefStyleSheet ) );
2895         sal_uInt32 nXFId = AppendBuiltInXFWithStyle( xDefStyle, EXC_STYLE_NORMAL );
2896         // mark this XF as not predefined, prevents overwriting
2897         maBuiltInMap[ nXFId ].mbPredefined = false;
2898     }
2899     else
2900     {
2901         OSL_FAIL( "XclExpXFBuffer::InsertDefaultRecords - default style not found" );
2902         XclExpXFRef xDefStyle( new XclExpDefaultXF( GetRoot(), false ) );
2903         xDefStyle->SetAllUsedFlags( true );
2904         AppendBuiltInXFWithStyle( xDefStyle, EXC_STYLE_NORMAL );
2905     }
2906 
2907     // index 1-14: RowLevel and ColLevel styles (without STYLE records)
2908     XclExpDefaultXF aLevelStyle( GetRoot(), false );
2909     // RowLevel_1, ColLevel_1
2910     aLevelStyle.SetFont( 1 );
2911     AppendBuiltInXF( XclExpXFRef( new XclExpDefaultXF( aLevelStyle ) ), EXC_STYLE_ROWLEVEL, 0 );
2912     AppendBuiltInXF( XclExpXFRef( new XclExpDefaultXF( aLevelStyle ) ), EXC_STYLE_COLLEVEL, 0 );
2913     // RowLevel_2, ColLevel_2
2914     aLevelStyle.SetFont( 2 );
2915     AppendBuiltInXF( XclExpXFRef( new XclExpDefaultXF( aLevelStyle ) ), EXC_STYLE_ROWLEVEL, 1 );
2916     AppendBuiltInXF( XclExpXFRef( new XclExpDefaultXF( aLevelStyle ) ), EXC_STYLE_COLLEVEL, 1 );
2917     // RowLevel_3, ColLevel_3 ... RowLevel_7, ColLevel_7
2918     aLevelStyle.SetFont( 0 );
2919     for( sal_uInt8 nLevel = 2; nLevel < EXC_STYLE_LEVELCOUNT; ++nLevel )
2920     {
2921         AppendBuiltInXF( XclExpXFRef( new XclExpDefaultXF( aLevelStyle ) ), EXC_STYLE_ROWLEVEL, nLevel );
2922         AppendBuiltInXF( XclExpXFRef( new XclExpDefaultXF( aLevelStyle ) ), EXC_STYLE_COLLEVEL, nLevel );
2923     }
2924 
2925     // index 15: default hard cell format, placeholder to be able to add more built-in styles
2926     maXFList.AppendNewRecord( new XclExpDefaultXF( GetRoot(), true ) );
2927     maBuiltInMap[ EXC_XF_DEFAULTCELL ].mbPredefined = true;
2928 
2929     // index 16-20: other built-in styles
2930     XclExpDefaultXF aFormatStyle( GetRoot(), false );
2931     aFormatStyle.SetFont( 1 );
2932     aFormatStyle.SetNumFmt( 43 );
2933     AppendBuiltInXFWithStyle( XclExpXFRef( new XclExpDefaultXF( aFormatStyle ) ), EXC_STYLE_COMMA );
2934     aFormatStyle.SetNumFmt( 41 );
2935     AppendBuiltInXFWithStyle( XclExpXFRef( new XclExpDefaultXF( aFormatStyle ) ), EXC_STYLE_COMMA_0 );
2936     aFormatStyle.SetNumFmt( 44 );
2937     AppendBuiltInXFWithStyle( XclExpXFRef( new XclExpDefaultXF( aFormatStyle ) ), EXC_STYLE_CURRENCY );
2938     aFormatStyle.SetNumFmt( 42 );
2939     AppendBuiltInXFWithStyle( XclExpXFRef( new XclExpDefaultXF( aFormatStyle ) ), EXC_STYLE_CURRENCY_0 );
2940     aFormatStyle.SetNumFmt( 9 );
2941     AppendBuiltInXFWithStyle( XclExpXFRef( new XclExpDefaultXF( aFormatStyle ) ), EXC_STYLE_PERCENT );
2942 
2943     // other built-in style XF records (i.e. Hyperlink styles) are created on demand
2944 
2945     /*  Insert the real default hard cell format -> 0 is document default pattern.
2946         Do it here (and not already above) to really have all built-in styles. */
2947     Insert( nullptr, GetDefApiScript() );
2948 }
2949 
2950 void XclExpXFBuffer::AppendXFIndex( sal_uInt32 nXFId )
2951 {
2952     OSL_ENSURE( nXFId < maXFIndexVec.size(), "XclExpXFBuffer::AppendXFIndex - XF ID out of range" );
2953     maXFIndexVec[ nXFId ] = static_cast< sal_uInt16 >( maSortedXFList.GetSize() );
2954     XclExpXFRef xXF = maXFList.GetRecord( nXFId );
2955     AddBorderAndFill( *xXF );
2956     maSortedXFList.AppendRecord( xXF );
2957     OSL_ENSURE( maXFList.HasRecord( nXFId ), "XclExpXFBuffer::AppendXFIndex - XF not found" );
2958 }
2959 
2960 void XclExpXFBuffer::AddBorderAndFill( const XclExpXF& rXF )
2961 {
2962     if( std::none_of( maBorders.begin(), maBorders.end(), XclExpBorderPred( rXF.GetBorderData() ) ) )
2963     {
2964         maBorders.push_back( rXF.GetBorderData() );
2965     }
2966 
2967     if( std::none_of( maFills.begin(), maFills.end(), XclExpFillPred( rXF.GetAreaData() ) ) )
2968     {
2969         maFills.push_back( rXF.GetAreaData() );
2970     }
2971 }
2972 
2973 XclExpDxfs::XclExpDxfs( const XclExpRoot& rRoot )
2974     : XclExpRoot( rRoot ),
2975     mxFormatter( new SvNumberFormatter( comphelper::getProcessComponentContext(), LANGUAGE_ENGLISH_US ) ),
2976     mpKeywordTable( new NfKeywordTable )
2977 {
2978     mxFormatter->FillKeywordTableForExcel( *mpKeywordTable );
2979 
2980     SCTAB nTables = rRoot.GetDoc().GetTableCount();
2981     for(SCTAB nTab = 0; nTab < nTables; ++nTab)
2982     {
2983         ScConditionalFormatList* pList = rRoot.GetDoc().GetCondFormList(nTab);
2984         if (pList)
2985         {
2986             sal_Int32 nIndex = 0;
2987             for (ScConditionalFormatList::const_iterator itr = pList->begin();
2988                     itr != pList->end(); ++itr)
2989             {
2990                 size_t nEntryCount = (*itr)->size();
2991                 for (size_t nFormatEntry = 0; nFormatEntry < nEntryCount; ++nFormatEntry)
2992                 {
2993                     const ScFormatEntry* pFormatEntry = (*itr)->GetEntry(nFormatEntry);
2994                     if (!pFormatEntry || (pFormatEntry->GetType() != condformat::CONDITION &&
2995                                 pFormatEntry->GetType() != condformat::DATE))
2996                         continue;
2997 
2998                     OUString aStyleName;
2999                     if(pFormatEntry->GetType() == condformat::CONDITION)
3000                     {
3001                         const ScCondFormatEntry* pEntry = static_cast<const ScCondFormatEntry*>(pFormatEntry);
3002                         aStyleName= pEntry->GetStyle();
3003                     }
3004                     else
3005                     {
3006                         const ScCondDateFormatEntry* pEntry = static_cast<const ScCondDateFormatEntry*>(pFormatEntry);
3007                         aStyleName = pEntry->GetStyleName();
3008                     }
3009 
3010                     if (maStyleNameToDxfId.find(aStyleName) == maStyleNameToDxfId.end())
3011                     {
3012                         maStyleNameToDxfId.insert(std::pair<OUString, sal_Int32>(aStyleName, nIndex));
3013 
3014                         SfxStyleSheetBase* pStyle = rRoot.GetDoc().GetStyleSheetPool()->Find(aStyleName);
3015                         if(!pStyle)
3016                             continue;
3017 
3018                         SfxItemSet& rSet = pStyle->GetItemSet();
3019 
3020                         XclExpCellBorder* pBorder = new XclExpCellBorder;
3021                         if (!pBorder->FillFromItemSet( rSet, GetPalette(), GetBiff()) )
3022                         {
3023                             delete pBorder;
3024                             pBorder = nullptr;
3025                         }
3026 
3027                         XclExpCellAlign* pAlign = new XclExpCellAlign;
3028                         if (!pAlign->FillFromItemSet( rSet, false, GetBiff()))
3029                         {
3030                             delete pAlign;
3031                             pAlign = nullptr;
3032                         }
3033 
3034                         XclExpCellProt* pCellProt = new XclExpCellProt;
3035                         if (!pCellProt->FillFromItemSet( rSet ))
3036                         {
3037                             delete pCellProt;
3038                             pCellProt = nullptr;
3039                         }
3040 
3041                         XclExpColor* pColor = new XclExpColor;
3042                         if(!pColor->FillFromItemSet( rSet ))
3043                         {
3044                             delete pColor;
3045                             pColor = nullptr;
3046                         }
3047 
3048                         XclExpDxfFont* pFont = new XclExpDxfFont(rRoot, rSet);
3049 
3050                         XclExpNumFmt* pNumFormat = nullptr;
3051                         const SfxPoolItem *pPoolItem = nullptr;
3052                         if( rSet.GetItemState( ATTR_VALUE_FORMAT, true, &pPoolItem ) == SfxItemState::SET )
3053                         {
3054                             sal_uInt32 nScNumFmt = static_cast< const SfxUInt32Item* >(pPoolItem)->GetValue();
3055                             sal_Int32 nXclNumFmt = GetRoot().GetNumFmtBuffer().Insert(nScNumFmt);
3056                             pNumFormat = new XclExpNumFmt( nScNumFmt, nXclNumFmt, GetNumberFormatCode( *this, nScNumFmt, mxFormatter.get(), mpKeywordTable.get() ));
3057                         }
3058 
3059                         maDxf.push_back(o3tl::make_unique<XclExpDxf>( rRoot, pAlign, pBorder, pFont, pNumFormat, pCellProt, pColor ));
3060                         ++nIndex;
3061                     }
3062 
3063                 }
3064             }
3065         }
3066     }
3067 }
3068 
3069 sal_Int32 XclExpDxfs::GetDxfId( const OUString& rStyleName )
3070 {
3071     std::map<OUString, sal_Int32>::iterator itr = maStyleNameToDxfId.find(rStyleName);
3072     if(itr!= maStyleNameToDxfId.end())
3073         return itr->second;
3074     return -1;
3075 }
3076 
3077 void XclExpDxfs::SaveXml( XclExpXmlStream& rStrm )
3078 {
3079     if(maDxf.empty())
3080         return;
3081 
3082     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
3083     rStyleSheet->startElement( XML_dxfs,
3084             XML_count, OString::number(maDxf.size()).getStr(),
3085             FSEND );
3086 
3087     for ( DxfContainer::iterator itr = maDxf.begin(); itr != maDxf.end(); ++itr )
3088     {
3089         (*itr)->SaveXml( rStrm );
3090     }
3091 
3092     rStyleSheet->endElement( XML_dxfs );
3093 }
3094 
3095 XclExpDxf::XclExpDxf( const XclExpRoot& rRoot, XclExpCellAlign* pAlign, XclExpCellBorder* pBorder,
3096             XclExpDxfFont* pFont, XclExpNumFmt* pNumberFmt, XclExpCellProt* pProt, XclExpColor* pColor)
3097     : XclExpRoot( rRoot ),
3098     mpAlign(pAlign),
3099     mpBorder(pBorder),
3100     mpFont(pFont),
3101     mpNumberFmt(pNumberFmt),
3102     mpProt(pProt),
3103     mpColor(pColor)
3104 {
3105 }
3106 
3107 XclExpDxf::~XclExpDxf()
3108 {
3109 }
3110 
3111 void XclExpDxf::SaveXml( XclExpXmlStream& rStrm )
3112 {
3113     sax_fastparser::FSHelperPtr& rStyleSheet = rStrm.GetCurrentStream();
3114     rStyleSheet->startElement( XML_dxf, FSEND );
3115 
3116     if (mpFont)
3117         mpFont->SaveXml(rStrm);
3118     if (mpNumberFmt)
3119         mpNumberFmt->SaveXml(rStrm);
3120     if (mpColor)
3121         mpColor->SaveXml(rStrm);
3122     if (mpAlign)
3123         mpAlign->SaveXml(rStrm);
3124     if (mpBorder)
3125         mpBorder->SaveXml(rStrm);
3126     if (mpProt)
3127         mpProt->SaveXml(rStrm);
3128     rStyleSheet->endElement( XML_dxf );
3129 }
3130 
3131 XclExpXmlStyleSheet::XclExpXmlStyleSheet( const XclExpRoot& rRoot )
3132     : XclExpRoot( rRoot )
3133 {
3134 }
3135 
3136 void XclExpXmlStyleSheet::SaveXml( XclExpXmlStream& rStrm )
3137 {
3138     sax_fastparser::FSHelperPtr aStyleSheet = rStrm.CreateOutputStream(
3139             "xl/styles.xml",
3140             "styles.xml",
3141             rStrm.GetCurrentStream()->getOutputStream(),
3142             "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
3143             rtl::OUStringToOString(oox::getRelationship(Relationship::STYLES), RTL_TEXTENCODING_UTF8).getStr());
3144     rStrm.PushStream( aStyleSheet );
3145 
3146     aStyleSheet->startElement( XML_styleSheet,
3147             XML_xmlns, XclXmlUtils::ToOString(rStrm.getNamespaceURL(OOX_NS(xls))).getStr(),
3148             FSEND );
3149 
3150     CreateRecord( EXC_ID_FORMATLIST )->SaveXml( rStrm );
3151     CreateRecord( EXC_ID_FONTLIST )->SaveXml( rStrm );
3152     CreateRecord( EXC_ID_XFLIST )->SaveXml( rStrm );
3153     CreateRecord( EXC_ID_DXFS )->SaveXml( rStrm );
3154     CreateRecord( EXC_ID_PALETTE )->SaveXml( rStrm );
3155 
3156     aStyleSheet->endElement( XML_styleSheet );
3157 
3158     rStrm.PopStream();
3159 }
3160 
3161 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3162