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