use insert function instead of for loop
[LibreOffice.git] / sc / source / filter / excel / xistyle.cxx
blobdddf509b0944ae9697abeb2356d95a8adb229926
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <memory>
21 #include <xistyle.hxx>
22 #include <sfx2/objsh.hxx>
23 #include <svtools/ctrltool.hxx>
24 #include <editeng/editobj.hxx>
25 #include <scitems.hxx>
26 #include <editeng/fontitem.hxx>
27 #include <editeng/fhgtitem.hxx>
28 #include <editeng/wghtitem.hxx>
29 #include <editeng/udlnitem.hxx>
30 #include <editeng/postitem.hxx>
31 #include <editeng/crossedoutitem.hxx>
32 #include <editeng/contouritem.hxx>
33 #include <editeng/shdditem.hxx>
34 #include <editeng/escapementitem.hxx>
35 #include <svx/algitem.hxx>
36 #include <editeng/borderline.hxx>
37 #include <editeng/boxitem.hxx>
38 #include <editeng/lineitem.hxx>
39 #include <svx/rotmodit.hxx>
40 #include <editeng/colritem.hxx>
41 #include <editeng/brushitem.hxx>
42 #include <editeng/frmdiritem.hxx>
43 #include <editeng/eeitem.hxx>
44 #include <editeng/flstitem.hxx>
45 #include <editeng/justifyitem.hxx>
46 #include <editeng/editids.hrc>
47 #include <sal/macros.h>
48 #include <sal/log.hxx>
49 #include <tools/UnitConversion.hxx>
50 #include <vcl/fontcharmap.hxx>
51 #include <vcl/outdev.hxx>
52 #include <document.hxx>
53 #include <documentimport.hxx>
54 #include <docpool.hxx>
55 #include <docsh.hxx>
56 #include <attrib.hxx>
57 #include <patattr.hxx>
58 #include <stlpool.hxx>
59 #include <stlsheet.hxx>
60 #include <globstr.hrc>
61 #include <scresid.hxx>
62 #include <attarray.hxx>
63 #include <xladdress.hxx>
64 #include <xlcontent.hxx>
65 #include <xltracer.hxx>
66 #include <xltools.hxx>
67 #include <xistream.hxx>
68 #include <xicontent.hxx>
70 #include <root.hxx>
71 #include <colrowst.hxx>
73 #include <string_view>
74 #include <vector>
76 #include <cppuhelper/implbase.hxx>
77 #include <com/sun/star/container/XIndexAccess.hpp>
78 #include <com/sun/star/beans/XPropertySet.hpp>
79 #include <com/sun/star/frame/XModel.hpp>
80 #include <svl/numformat.hxx>
81 #include <o3tl/string_view.hxx>
83 using ::std::vector;
84 using namespace ::com::sun::star;
86 typedef ::cppu::WeakImplHelper< container::XIndexAccess > XIndexAccess_BASE;
87 typedef ::std::vector< Color > ColorVec;
89 namespace {
91 class PaletteIndex : public XIndexAccess_BASE
93 public:
94 explicit PaletteIndex( ColorVec&& rColorTable ) : maColor( std::move(rColorTable) ) {}
96 // Methods XIndexAccess
97 virtual ::sal_Int32 SAL_CALL getCount() override
99 return maColor.size();
102 virtual uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override
104 //--Index; // apparently the palette is already 1 based
105 return uno::Any( sal_Int32( maColor[ Index ] ) );
108 // Methods XElementAccess
109 virtual uno::Type SAL_CALL getElementType() override
111 return ::cppu::UnoType<sal_Int32>::get();
113 virtual sal_Bool SAL_CALL hasElements() override
115 return (!maColor.empty());
118 private:
119 ColorVec maColor;
124 void
125 XclImpPalette::ExportPalette()
127 ScDocShell* pDocShell = mrRoot.GetDocShell();
128 if(!pDocShell)
129 return;
131 // copy values in color palette
132 sal_Int16 nColors = maColorTable.size();
133 ColorVec aColors;
134 aColors.resize( nColors );
135 for( sal_uInt16 nIndex = 0; nIndex < nColors; ++nIndex )
136 aColors[ nIndex ] = GetColor( nIndex );
138 ScModelObj* pModel = pDocShell->GetModel();
139 if ( pModel )
141 uno::Reference< container::XIndexAccess > xIndex( new PaletteIndex( std::move(aColors) ) );
142 pModel->setPropertyValue( u"ColorPalette"_ustr, uno::Any( xIndex ) );
146 // PALETTE record - color information =========================================
148 XclImpPalette::XclImpPalette( const XclImpRoot& rRoot ) :
149 XclDefaultPalette( rRoot ), mrRoot( rRoot )
153 void XclImpPalette::Initialize()
155 maColorTable.clear();
158 Color XclImpPalette::GetColor( sal_uInt16 nXclIndex ) const
160 if( nXclIndex >= EXC_COLOR_USEROFFSET )
162 sal_uInt32 nIx = nXclIndex - EXC_COLOR_USEROFFSET;
163 if( nIx < maColorTable.size() )
164 return maColorTable[ nIx ];
166 return GetDefColor( nXclIndex );
169 void XclImpPalette::ReadPalette( XclImpStream& rStrm )
171 sal_uInt16 nCount;
172 nCount = rStrm.ReaduInt16();
174 const size_t nMinRecordSize = 4;
175 const size_t nMaxRecords = rStrm.GetRecLeft() / nMinRecordSize;
176 if (nCount > nMaxRecords)
178 SAL_WARN("sc", "Parsing error: " << nMaxRecords <<
179 " max possible entries, but " << nCount << " claimed, truncating");
180 nCount = nMaxRecords;
183 maColorTable.resize( nCount );
184 Color aColor;
185 for( sal_uInt16 nIndex = 0; nIndex < nCount; ++nIndex )
187 rStrm >> aColor;
188 maColorTable[ nIndex ] = aColor;
190 ExportPalette();
193 // FONT record - font information =============================================
194 XclImpFont::XclImpFont( const XclImpRoot& rRoot ) :
195 XclImpRoot( rRoot ),
196 mbHasCharSet( false ),
197 mbHasWstrn( true ),
198 mbHasAsian( false ),
199 mbHasCmplx( false )
201 SetAllUsedFlags( false );
204 XclImpFont::XclImpFont( const XclImpRoot& rRoot, const XclFontData& rFontData ) :
205 XclImpRoot( rRoot )
207 SetFontData( rFontData, false );
210 void XclImpFont::SetAllUsedFlags( bool bUsed )
212 mbFontNameUsed = mbHeightUsed = mbColorUsed = mbWeightUsed = mbEscapemUsed =
213 mbUnderlUsed = mbItalicUsed = mbStrikeUsed = mbOutlineUsed = mbShadowUsed = bUsed;
216 void XclImpFont::SetFontData( const XclFontData& rFontData, bool bHasCharSet )
218 maData = rFontData;
219 mbHasCharSet = bHasCharSet;
220 if( !maData.maStyle.isEmpty() )
222 if( ScDocShell* pDocShell = GetDocShell() )
224 if( const SvxFontListItem* pInfoItem = static_cast< const SvxFontListItem* >(
225 pDocShell->GetItem( SID_ATTR_CHAR_FONTLIST ) ) )
227 if( const FontList* pFontList = pInfoItem->GetFontList() )
229 FontMetric aFontMetric( pFontList->Get( maData.maName, maData.maStyle ) );
230 maData.SetScWeight( aFontMetric.GetWeight() );
231 maData.SetScPosture( aFontMetric.GetItalic() );
235 maData.maStyle.clear();
237 GuessScriptType();
238 SetAllUsedFlags( true );
241 rtl_TextEncoding XclImpFont::GetFontEncoding() const
243 // #i63105# use text encoding from FONT record
244 // #i67768# BIFF2-BIFF4 FONT records do not contain character set
245 rtl_TextEncoding eFontEnc = mbHasCharSet ? maData.GetFontEncoding() : GetTextEncoding();
246 return (eFontEnc == RTL_TEXTENCODING_DONTKNOW) ? GetTextEncoding() : eFontEnc;
249 void XclImpFont::ReadFont( XclImpStream& rStrm )
251 switch( GetBiff() )
253 case EXC_BIFF2:
254 ReadFontData2( rStrm );
255 ReadFontName2( rStrm );
256 break;
257 case EXC_BIFF3:
258 case EXC_BIFF4:
259 ReadFontData2( rStrm );
260 ReadFontColor( rStrm );
261 ReadFontName2( rStrm );
262 break;
263 case EXC_BIFF5:
264 ReadFontData5( rStrm );
265 ReadFontName2( rStrm );
266 break;
267 case EXC_BIFF8:
268 ReadFontData5( rStrm );
269 ReadFontName8( rStrm );
270 break;
271 default:
272 DBG_ERROR_BIFF();
273 return;
275 GuessScriptType();
276 SetAllUsedFlags( true );
279 void XclImpFont::ReadEfont( XclImpStream& rStrm )
281 ReadFontColor( rStrm );
284 void XclImpFont::ReadCFFontBlock( XclImpStream& rStrm )
286 OSL_ENSURE_BIFF( GetBiff() == EXC_BIFF8 );
287 if( GetBiff() != EXC_BIFF8 )
288 return;
290 rStrm.Ignore( 64 );
291 sal_uInt32 nHeight = rStrm.ReaduInt32();
292 sal_uInt32 nStyle = rStrm.ReaduInt32();
293 sal_uInt16 nWeight = rStrm.ReaduInt16();
294 rStrm.Ignore( 2 ); //nEscapem
295 sal_uInt8 nUnderl = rStrm.ReaduInt8();
296 rStrm.Ignore( 3 );
297 sal_uInt32 nColor = rStrm.ReaduInt32();
298 rStrm.Ignore( 4 );
299 sal_uInt32 nFontFlags1 = rStrm.ReaduInt32();
300 rStrm.Ignore( 4 ); //nFontFlags2
301 sal_uInt32 nFontFlags3 = rStrm.ReaduInt32();
302 rStrm.Ignore( 18 );
304 if( (mbHeightUsed = (nHeight <= 0x7FFF)) )
305 maData.mnHeight = static_cast< sal_uInt16 >( nHeight );
306 if( (mbWeightUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STYLE ) && (nWeight < 0x7FFF)) )
307 maData.mnWeight = nWeight;
308 if( (mbItalicUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STYLE )) )
309 maData.mbItalic = ::get_flag( nStyle, EXC_CF_FONT_STYLE );
310 if( (mbUnderlUsed = !::get_flag( nFontFlags3, EXC_CF_FONT_UNDERL ) && (nUnderl <= 0x7F)) )
311 maData.mnUnderline = nUnderl;
312 if( (mbColorUsed = (nColor <= 0x7FFF)) )
313 maData.maComplexColor.setColor(GetPalette().GetColor(sal_uInt16(nColor)));
314 if( (mbStrikeUsed = !::get_flag( nFontFlags1, EXC_CF_FONT_STRIKEOUT )) )
315 maData.mbStrikeout = ::get_flag( nStyle, EXC_CF_FONT_STRIKEOUT );
318 void XclImpFont::FillToItemSet( SfxItemSet& rItemSet, XclFontItemType eType, bool bSkipPoolDefs ) const
320 // true = edit engine Which-IDs (EE_CHAR_*); false = Calc Which-IDs (ATTR_*)
321 bool bEE = eType != XclFontItemType::Cell;
323 // item = the item to put into the item set
324 // sc_which = the Calc Which-ID of the item
325 // ee_which = the edit engine Which-ID of the item
326 #define PUTITEM( item, sc_which, ee_which ) \
327 ScfTools::PutItem( rItemSet, item, (bEE ? (static_cast<sal_uInt16>(ee_which)) : (sc_which)), bSkipPoolDefs )
329 // Font item
330 if( mbFontNameUsed )
332 rtl_TextEncoding eFontEnc = maData.GetFontEncoding();
333 rtl_TextEncoding eTempTextEnc = (bEE && (eFontEnc == GetTextEncoding())) ?
334 ScfTools::GetSystemTextEncoding() : eFontEnc;
336 //add corresponding pitch for FontFamily
337 FontPitch ePitch = PITCH_DONTKNOW;
338 FontFamily eFtFamily = maData.GetScFamily( GetTextEncoding() );
339 switch( eFtFamily ) //refer http://msdn.microsoft.com/en-us/library/aa246306(v=VS.60).aspx
341 case FAMILY_ROMAN: ePitch = PITCH_VARIABLE; break;
342 case FAMILY_SWISS: ePitch = PITCH_VARIABLE; break;
343 case FAMILY_MODERN: ePitch = PITCH_FIXED; break;
344 default: break;
346 SvxFontItem aFontItem( eFtFamily , maData.maName, OUString(), ePitch, eTempTextEnc, ATTR_FONT );
348 // set only for valid script types
349 if( mbHasWstrn )
350 PUTITEM( aFontItem, ATTR_FONT, EE_CHAR_FONTINFO );
351 if( mbHasAsian )
352 PUTITEM( aFontItem, ATTR_CJK_FONT, EE_CHAR_FONTINFO_CJK );
353 if( mbHasCmplx )
354 PUTITEM( aFontItem, ATTR_CTL_FONT, EE_CHAR_FONTINFO_CTL );
357 // Font height (for all script types)
358 if( mbHeightUsed )
360 sal_Int32 nHeight = maData.mnHeight;
361 if( bEE && (eType != XclFontItemType::HeaderFooter) ) // do not convert header/footer height
362 nHeight = convertTwipToMm100(nHeight);
364 SvxFontHeightItem aHeightItem( nHeight, 100, ATTR_FONT_HEIGHT );
365 PUTITEM( aHeightItem, ATTR_FONT_HEIGHT, EE_CHAR_FONTHEIGHT );
366 PUTITEM( aHeightItem, ATTR_CJK_FONT_HEIGHT, EE_CHAR_FONTHEIGHT_CJK );
367 PUTITEM( aHeightItem, ATTR_CTL_FONT_HEIGHT, EE_CHAR_FONTHEIGHT_CTL );
370 // Font color - pass AUTO_COL to item
371 if( mbColorUsed )
372 PUTITEM(SvxColorItem(maData.maComplexColor.getFinalColor(), maData.maComplexColor, ATTR_FONT_COLOR ), ATTR_FONT_COLOR, EE_CHAR_COLOR);
374 // Font weight (for all script types)
375 if( mbWeightUsed )
377 SvxWeightItem aWeightItem( maData.GetScWeight(), ATTR_FONT_WEIGHT );
378 PUTITEM( aWeightItem, ATTR_FONT_WEIGHT, EE_CHAR_WEIGHT );
379 PUTITEM( aWeightItem, ATTR_CJK_FONT_WEIGHT, EE_CHAR_WEIGHT_CJK );
380 PUTITEM( aWeightItem, ATTR_CTL_FONT_WEIGHT, EE_CHAR_WEIGHT_CTL );
383 // Font underline
384 if( mbUnderlUsed )
386 SvxUnderlineItem aUnderlItem( maData.GetScUnderline(), ATTR_FONT_UNDERLINE );
387 PUTITEM( aUnderlItem, ATTR_FONT_UNDERLINE, EE_CHAR_UNDERLINE );
390 // Font posture (for all script types)
391 if( mbItalicUsed )
393 SvxPostureItem aPostItem( maData.GetScPosture(), ATTR_FONT_POSTURE );
394 PUTITEM( aPostItem, ATTR_FONT_POSTURE, EE_CHAR_ITALIC );
395 PUTITEM( aPostItem, ATTR_CJK_FONT_POSTURE, EE_CHAR_ITALIC_CJK );
396 PUTITEM( aPostItem, ATTR_CTL_FONT_POSTURE, EE_CHAR_ITALIC_CTL );
399 // Boolean attributes crossed out, contoured, shadowed
400 if( mbStrikeUsed )
401 PUTITEM( SvxCrossedOutItem( maData.GetScStrikeout(), ATTR_FONT_CROSSEDOUT ), ATTR_FONT_CROSSEDOUT, EE_CHAR_STRIKEOUT );
402 if( mbOutlineUsed )
403 PUTITEM( SvxContourItem( maData.mbOutline, ATTR_FONT_CONTOUR ), ATTR_FONT_CONTOUR, EE_CHAR_OUTLINE );
404 if( mbShadowUsed )
405 PUTITEM( SvxShadowedItem( maData.mbShadow, ATTR_FONT_SHADOWED ), ATTR_FONT_SHADOWED, EE_CHAR_SHADOW );
407 // Super-/subscript: only on edit engine objects
408 if( mbEscapemUsed && bEE )
409 rItemSet.Put( SvxEscapementItem( maData.GetScEscapement(), EE_CHAR_ESCAPEMENT ) );
411 #undef PUTITEM
414 void XclImpFont::WriteFontProperties( ScfPropertySet& rPropSet,
415 XclFontPropSetType eType, const Color* pFontColor ) const
417 GetFontPropSetHelper().WriteFontProperties(
418 rPropSet, eType, maData, mbHasWstrn, mbHasAsian, mbHasCmplx, pFontColor );
421 void XclImpFont::ReadFontData2( XclImpStream& rStrm )
423 sal_uInt16 nFlags;
424 maData.mnHeight = rStrm.ReaduInt16();
425 nFlags = rStrm.ReaduInt16();
427 maData.mnWeight = ::get_flagvalue( nFlags, EXC_FONTATTR_BOLD, EXC_FONTWGHT_BOLD, EXC_FONTWGHT_NORMAL );
428 maData.mnUnderline = ::get_flagvalue( nFlags, EXC_FONTATTR_UNDERLINE, EXC_FONTUNDERL_SINGLE, EXC_FONTUNDERL_NONE );
429 maData.mbItalic = ::get_flag( nFlags, EXC_FONTATTR_ITALIC );
430 maData.mbStrikeout = ::get_flag( nFlags, EXC_FONTATTR_STRIKEOUT );
431 maData.mbOutline = ::get_flag( nFlags, EXC_FONTATTR_OUTLINE );
432 maData.mbShadow = ::get_flag( nFlags, EXC_FONTATTR_SHADOW );
433 mbHasCharSet = false;
436 void XclImpFont::ReadFontData5( XclImpStream& rStrm )
438 sal_uInt16 nFlags;
440 maData.mnHeight = rStrm.ReaduInt16();
441 nFlags = rStrm.ReaduInt16();
442 ReadFontColor( rStrm );
443 maData.mnWeight = rStrm.ReaduInt16();
444 maData.mnEscapem = rStrm.ReaduInt16();
445 maData.mnUnderline = rStrm.ReaduInt8();
446 maData.mnFamily = rStrm.ReaduInt8();
447 maData.mnCharSet = rStrm.ReaduInt8();
448 rStrm.Ignore( 1 );
450 maData.mbItalic = ::get_flag( nFlags, EXC_FONTATTR_ITALIC );
451 maData.mbStrikeout = ::get_flag( nFlags, EXC_FONTATTR_STRIKEOUT );
452 maData.mbOutline = ::get_flag( nFlags, EXC_FONTATTR_OUTLINE );
453 maData.mbShadow = ::get_flag( nFlags, EXC_FONTATTR_SHADOW );
454 mbHasCharSet = maData.mnCharSet != 0;
457 void XclImpFont::ReadFontColor( XclImpStream& rStrm )
459 maData.maComplexColor.setColor(GetPalette().GetColor(rStrm.ReaduInt16()));
462 void XclImpFont::ReadFontName2( XclImpStream& rStrm )
464 maData.maName = rStrm.ReadByteString( false );
467 void XclImpFont::ReadFontName8( XclImpStream& rStrm )
469 maData.maName = rStrm.ReadUniString( rStrm.ReaduInt8() );
472 void XclImpFont::GuessScriptType()
474 mbHasWstrn = true;
475 mbHasAsian = mbHasCmplx = false;
477 // find the script types for which the font contains characters
478 OutputDevice* pPrinter = GetPrinter();
479 if(!pPrinter)
480 return;
482 vcl::Font aFont( maData.maName, Size( 0, 10 ) );
483 FontCharMapRef xFontCharMap;
485 pPrinter->SetFont( aFont );
486 if( !pPrinter->GetFontCharMap( xFontCharMap ) )
487 return;
489 // CJK fonts
490 mbHasAsian =
491 xFontCharMap->HasChar( 0x3041 ) || // 3040-309F: Hiragana
492 xFontCharMap->HasChar( 0x30A1 ) || // 30A0-30FF: Katakana
493 xFontCharMap->HasChar( 0x3111 ) || // 3100-312F: Bopomofo
494 xFontCharMap->HasChar( 0x3131 ) || // 3130-318F: Hangul Compatibility Jamo
495 xFontCharMap->HasChar( 0x3301 ) || // 3300-33FF: CJK Compatibility
496 xFontCharMap->HasChar( 0x3401 ) || // 3400-4DBF: CJK Unified Ideographs Extension A
497 xFontCharMap->HasChar( 0x4E01 ) || // 4E00-9FFF: CJK Unified Ideographs
498 xFontCharMap->HasChar( 0x7E01 ) || // 4E00-9FFF: CJK Unified Ideographs
499 xFontCharMap->HasChar( 0xA001 ) || // A001-A48F: Yi Syllables
500 xFontCharMap->HasChar( 0xAC01 ) || // AC00-D7AF: Hangul Syllables
501 xFontCharMap->HasChar( 0xCC01 ) || // AC00-D7AF: Hangul Syllables
502 xFontCharMap->HasChar( 0xF901 ) || // F900-FAFF: CJK Compatibility Ideographs
503 xFontCharMap->HasChar( 0xFF71 ); // FF00-FFEF: Halfwidth/Fullwidth Forms
504 // CTL fonts
505 mbHasCmplx =
506 xFontCharMap->HasChar( 0x05D1 ) || // 0590-05FF: Hebrew
507 xFontCharMap->HasChar( 0x0631 ) || // 0600-06FF: Arabic
508 xFontCharMap->HasChar( 0x0721 ) || // 0700-074F: Syriac
509 xFontCharMap->HasChar( 0x0911 ) || // 0900-0DFF: Indic scripts
510 xFontCharMap->HasChar( 0x0E01 ) || // 0E00-0E7F: Thai
511 xFontCharMap->HasChar( 0xFB21 ) || // FB1D-FB4F: Hebrew Presentation Forms
512 xFontCharMap->HasChar( 0xFB51 ) || // FB50-FDFF: Arabic Presentation Forms-A
513 xFontCharMap->HasChar( 0xFE71 ); // FE70-FEFF: Arabic Presentation Forms-B
514 // Western fonts
515 mbHasWstrn = (!mbHasAsian && !mbHasCmplx) || xFontCharMap->HasChar( 'A' );
518 XclImpFontBuffer::XclImpFontBuffer( const XclImpRoot& rRoot ) :
519 XclImpRoot( rRoot ),
520 maFont4( rRoot ),
521 maCtrlFont( rRoot )
523 Initialize();
525 // default font for form controls without own font information
526 XclFontData aCtrlFontData;
527 switch( GetBiff() )
529 case EXC_BIFF2:
530 case EXC_BIFF3:
531 case EXC_BIFF4:
532 case EXC_BIFF5:
533 aCtrlFontData.maName = "Helv";
534 aCtrlFontData.mnHeight = 160;
535 aCtrlFontData.mnWeight = EXC_FONTWGHT_BOLD;
536 break;
537 case EXC_BIFF8:
538 aCtrlFontData.maName = "Tahoma";
539 aCtrlFontData.mnHeight = 160;
540 aCtrlFontData.mnWeight = EXC_FONTWGHT_NORMAL;
541 break;
542 default:
543 DBG_ERROR_BIFF();
545 maCtrlFont.SetFontData( aCtrlFontData, false );
548 void XclImpFontBuffer::Initialize()
550 maFontList.clear();
552 // application font for column width calculation, later filled with first font from font list
553 XclFontData aAppFontData;
554 aAppFontData.maName = "Arial";
555 aAppFontData.mnHeight = 200;
556 aAppFontData.mnWeight = EXC_FONTWGHT_NORMAL;
557 UpdateAppFont( aAppFontData, false );
560 const XclImpFont* XclImpFontBuffer::GetFont( sal_uInt16 nFontIndex ) const
562 /* Font with index 4 is not stored in an Excel file, but used e.g. by
563 BIFF5 form pushbutton objects. It is the bold default font.
564 This also means that entries above 4 are out by one in the list. */
566 if (nFontIndex == 4)
567 return &maFont4;
569 if (nFontIndex < 4)
571 // Font ID is zero-based when it's less than 4.
572 return nFontIndex >= maFontList.size() ? nullptr : &maFontList[nFontIndex];
575 // Font ID is greater than 4. It is now 1-based.
576 return nFontIndex > maFontList.size() ? nullptr : &maFontList[nFontIndex-1];
579 void XclImpFontBuffer::ReadFont( XclImpStream& rStrm )
581 maFontList.emplace_back( GetRoot() );
582 XclImpFont& rFont = maFontList.back();
583 rFont.ReadFont( rStrm );
585 if( maFontList.size() == 1 )
587 UpdateAppFont( rFont.GetFontData(), rFont.HasCharSet() );
591 void XclImpFontBuffer::ReadEfont( XclImpStream& rStrm )
593 if( !maFontList.empty() )
594 maFontList.back().ReadEfont( rStrm );
597 void XclImpFontBuffer::FillToItemSet(
598 SfxItemSet& rItemSet, XclFontItemType eType,
599 sal_uInt16 nFontIdx, bool bSkipPoolDefs ) const
601 if( const XclImpFont* pFont = GetFont( nFontIdx ) )
602 pFont->FillToItemSet( rItemSet, eType, bSkipPoolDefs );
605 void XclImpFontBuffer::WriteFontProperties( ScfPropertySet& rPropSet,
606 XclFontPropSetType eType, sal_uInt16 nFontIdx, const Color* pFontColor ) const
608 if( const XclImpFont* pFont = GetFont( nFontIdx ) )
609 pFont->WriteFontProperties( rPropSet, eType, pFontColor );
612 void XclImpFontBuffer::WriteDefaultCtrlFontProperties( ScfPropertySet& rPropSet ) const
614 maCtrlFont.WriteFontProperties( rPropSet, EXC_FONTPROPSET_CONTROL );
617 void XclImpFontBuffer::UpdateAppFont( const XclFontData& rFontData, bool bHasCharSet )
619 maAppFont = rFontData;
620 // #i3006# Calculate the width of '0' from first font and current printer.
621 SetCharWidth( maAppFont );
623 // font 4 is bold font 0
624 XclFontData aFont4Data( maAppFont );
625 aFont4Data.mnWeight = EXC_FONTWGHT_BOLD;
626 maFont4.SetFontData( aFont4Data, bHasCharSet );
629 // FORMAT record - number formats =============================================
631 XclImpNumFmtBuffer::XclImpNumFmtBuffer( const XclImpRoot& rRoot ) :
632 XclNumFmtBuffer( rRoot ),
633 XclImpRoot( rRoot ),
634 mnNextXclIdx( 0 )
638 void XclImpNumFmtBuffer::Initialize()
640 maIndexMap.clear();
641 mnNextXclIdx = 0;
642 InitializeImport(); // base class
645 void XclImpNumFmtBuffer::ReadFormat( XclImpStream& rStrm )
647 OUString aFormat;
648 switch( GetBiff() )
650 case EXC_BIFF2:
651 case EXC_BIFF3:
652 aFormat = rStrm.ReadByteString( false );
653 break;
655 case EXC_BIFF4:
656 rStrm.Ignore( 2 ); // in BIFF4 the index field exists, but is undefined
657 aFormat = rStrm.ReadByteString( false );
658 break;
660 case EXC_BIFF5:
661 mnNextXclIdx = rStrm.ReaduInt16();
662 aFormat = rStrm.ReadByteString( false );
663 break;
665 case EXC_BIFF8:
666 mnNextXclIdx = rStrm.ReaduInt16();
667 aFormat = rStrm.ReadUniString();
668 break;
670 default:
671 DBG_ERROR_BIFF();
672 return;
675 if( mnNextXclIdx < 0xFFFF )
677 InsertFormat( mnNextXclIdx, aFormat );
678 ++mnNextXclIdx;
682 sal_uInt16 XclImpNumFmtBuffer::ReadCFFormat( XclImpStream& rStrm, bool bIFmt )
684 // internal number format ?
685 if(bIFmt)
687 rStrm.Ignore(1);
688 sal_uInt8 nIndex;
689 nIndex = rStrm.ReaduInt8();
690 return nIndex;
692 else
694 OUString aFormat = rStrm.ReadUniString();
695 InsertFormat( mnNextXclIdx, aFormat );
696 ++mnNextXclIdx;
697 return mnNextXclIdx - 1;
701 void XclImpNumFmtBuffer::CreateScFormats()
703 OSL_ENSURE( maIndexMap.empty(), "XclImpNumFmtBuffer::CreateScFormats - already created" );
705 SvNumberFormatter& rFormatter = GetFormatter();
706 for( const auto& [rXclNumFmt, rNumFmt] : GetFormatMap() )
708 // insert/convert the Excel number format
709 sal_uInt32 nKey;
710 if( !rNumFmt.maFormat.isEmpty() )
712 OUString aFormat( rNumFmt.maFormat );
713 sal_Int32 nCheckPos;
714 SvNumFormatType nType = SvNumFormatType::DEFINED;
715 rFormatter.PutandConvertEntry( aFormat, nCheckPos,
716 nType, nKey, LANGUAGE_ENGLISH_US, rNumFmt.meLanguage, false);
718 else
719 nKey = rFormatter.GetFormatIndex( rNumFmt.meOffset, rNumFmt.meLanguage );
721 // insert the resulting format key into the Excel->Calc index map
722 maIndexMap[ rXclNumFmt ] = nKey;
726 sal_uInt32 XclImpNumFmtBuffer::GetScFormat( sal_uInt16 nXclNumFmt ) const
728 XclImpIndexMap::const_iterator aIt = maIndexMap.find( nXclNumFmt );
729 return (aIt != maIndexMap.end()) ? aIt->second : NUMBERFORMAT_ENTRY_NOT_FOUND;
732 void XclImpNumFmtBuffer::FillToItemSet( SfxItemSet& rItemSet, sal_uInt16 nXclNumFmt, bool bSkipPoolDefs ) const
734 sal_uInt32 nScNumFmt = GetScFormat( nXclNumFmt );
735 if( nScNumFmt == NUMBERFORMAT_ENTRY_NOT_FOUND )
736 nScNumFmt = GetStdScNumFmt();
737 FillScFmtToItemSet( rItemSet, nScNumFmt, bSkipPoolDefs );
740 void XclImpNumFmtBuffer::FillScFmtToItemSet( SfxItemSet& rItemSet, sal_uInt32 nScNumFmt, bool bSkipPoolDefs ) const
742 OSL_ENSURE( nScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND, "XclImpNumFmtBuffer::FillScFmtToItemSet - invalid number format" );
743 ScfTools::PutItem( rItemSet, SfxUInt32Item( ATTR_VALUE_FORMAT, nScNumFmt ), bSkipPoolDefs );
744 if( rItemSet.GetItemState( ATTR_VALUE_FORMAT, false ) == SfxItemState::SET )
745 ScGlobal::AddLanguage( rItemSet, GetFormatter() );
748 // XF, STYLE record - Cell formatting =========================================
750 void XclImpCellProt::FillFromXF2( sal_uInt8 nNumFmt )
752 mbLocked = ::get_flag( nNumFmt, EXC_XF2_LOCKED );
753 mbHidden = ::get_flag( nNumFmt, EXC_XF2_HIDDEN );
756 void XclImpCellProt::FillFromXF3( sal_uInt16 nProt )
758 mbLocked = ::get_flag( nProt, EXC_XF_LOCKED );
759 mbHidden = ::get_flag( nProt, EXC_XF_HIDDEN );
762 void XclImpCellProt::FillToItemSet( SfxItemSet& rItemSet, bool bSkipPoolDefs ) const
764 ScfTools::PutItem( rItemSet, ScProtectionAttr( mbLocked, mbHidden ), bSkipPoolDefs );
767 void XclImpCellAlign::FillFromXF2( sal_uInt8 nFlags )
769 mnHorAlign = ::extract_value< sal_uInt8 >( nFlags, 0, 3 );
772 void XclImpCellAlign::FillFromXF3( sal_uInt16 nAlign )
774 mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 );
775 mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK ); // new in BIFF3
778 void XclImpCellAlign::FillFromXF4( sal_uInt16 nAlign )
780 FillFromXF3( nAlign );
781 mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 2 ); // new in BIFF4
782 mnOrient = ::extract_value< sal_uInt8 >( nAlign, 6, 2 ); // new in BIFF4
785 void XclImpCellAlign::FillFromXF5( sal_uInt16 nAlign )
787 mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 );
788 mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 );
789 mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK );
790 mnOrient = ::extract_value< sal_uInt8 >( nAlign, 8, 2 );
793 void XclImpCellAlign::FillFromXF8( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib )
795 mnHorAlign = ::extract_value< sal_uInt8 >( nAlign, 0, 3 );
796 mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 );
797 mbLineBreak = ::get_flag( nAlign, EXC_XF_LINEBREAK );
798 mnRotation = ::extract_value< sal_uInt8 >( nAlign, 8, 8 ); // new in BIFF8
799 mnIndent = ::extract_value< sal_uInt8 >( nMiscAttrib, 0, 4 ); // new in BIFF8
800 mbShrink = ::get_flag( nMiscAttrib, EXC_XF8_SHRINK ); // new in BIFF8
801 mnTextDir = ::extract_value< sal_uInt8 >( nMiscAttrib, 6, 2 ); // new in BIFF8
804 void XclImpCellAlign::FillFromCF( sal_uInt16 nAlign, sal_uInt16 nMiscAttrib )
806 mnHorAlign = extract_value< sal_uInt8 >( nAlign, 0, 3 );
807 mbLineBreak = get_flag< sal_uInt8 >( nAlign, EXC_XF_LINEBREAK );
808 mnVerAlign = ::extract_value< sal_uInt8 >( nAlign, 4, 3 );
809 mnRotation = ::extract_value< sal_uInt8 >( nAlign, 8, 8 );
810 mnIndent = ::extract_value< sal_uInt8 >( nMiscAttrib, 0, 4 );
811 mbShrink = ::get_flag( nMiscAttrib, EXC_XF8_SHRINK );
812 mnTextDir = ::extract_value< sal_uInt8 >( nMiscAttrib, 6, 2 );
815 void XclImpCellAlign::FillToItemSet( SfxItemSet& rItemSet, const XclImpFont* pFont, bool bSkipPoolDefs ) const
817 // horizontal alignment
818 ScfTools::PutItem( rItemSet, SvxHorJustifyItem( GetScHorAlign(), ATTR_HOR_JUSTIFY ), bSkipPoolDefs );
819 ScfTools::PutItem( rItemSet, SvxJustifyMethodItem( GetScHorJustifyMethod(), ATTR_HOR_JUSTIFY_METHOD ), bSkipPoolDefs );
821 // text wrap (#i74508# always if vertical alignment is justified or distributed)
822 bool bLineBreak = mbLineBreak || (mnVerAlign == EXC_XF_VER_JUSTIFY) || (mnVerAlign == EXC_XF_VER_DISTRIB);
823 ScfTools::PutItem( rItemSet, ScLineBreakCell( bLineBreak ), bSkipPoolDefs );
825 // vertical alignment
826 ScfTools::PutItem( rItemSet, SvxVerJustifyItem( GetScVerAlign(), ATTR_VER_JUSTIFY ), bSkipPoolDefs );
827 ScfTools::PutItem( rItemSet, SvxJustifyMethodItem( GetScVerJustifyMethod(), ATTR_VER_JUSTIFY_METHOD ), bSkipPoolDefs );
829 // indent
830 sal_uInt16 nScIndent = mnIndent * 200; // 1 Excel unit == 10 pt == 200 twips
831 ScfTools::PutItem( rItemSet, ScIndentItem( nScIndent ), bSkipPoolDefs );
833 // shrink to fit
834 ScfTools::PutItem( rItemSet, ScShrinkToFitCell( mbShrink ), bSkipPoolDefs );
836 // text orientation/rotation (BIFF2-BIFF7 sets mnOrient)
837 sal_uInt8 nXclRot = (mnOrient == EXC_ORIENT_NONE) ? mnRotation : XclTools::GetXclRotFromOrient( mnOrient );
838 bool bStacked = (nXclRot == EXC_ROT_STACKED);
839 ScfTools::PutItem( rItemSet, ScVerticalStackCell( bStacked ), bSkipPoolDefs );
840 // set an angle in the range from -90 to 90 degrees
841 Degree100 nAngle = XclTools::GetScRotation( nXclRot, 0_deg100 );
842 ScfTools::PutItem( rItemSet, ScRotateValueItem( nAngle ), bSkipPoolDefs );
843 // set "Use asian vertical layout", if cell is stacked and font contains CKJ characters
844 bool bAsianVert = bStacked && pFont && pFont->HasAsianChars();
845 ScfTools::PutItem( rItemSet, SfxBoolItem( ATTR_VERTICAL_ASIAN, bAsianVert ), bSkipPoolDefs );
847 // CTL text direction
848 ScfTools::PutItem( rItemSet, SvxFrameDirectionItem( GetScFrameDir(), ATTR_WRITINGDIR ), bSkipPoolDefs );
851 XclImpCellBorder::XclImpCellBorder()
853 SetUsedFlags( false, false );
856 void XclImpCellBorder::SetUsedFlags( bool bOuterUsed, bool bDiagUsed )
858 mbLeftUsed = mbRightUsed = mbTopUsed = mbBottomUsed = bOuterUsed;
859 mbDiagUsed = bDiagUsed;
862 void XclImpCellBorder::FillFromXF2( sal_uInt8 nFlags )
864 mnLeftLine = ::get_flagvalue( nFlags, EXC_XF2_LEFTLINE, EXC_LINE_THIN, EXC_LINE_NONE );
865 mnRightLine = ::get_flagvalue( nFlags, EXC_XF2_RIGHTLINE, EXC_LINE_THIN, EXC_LINE_NONE );
866 mnTopLine = ::get_flagvalue( nFlags, EXC_XF2_TOPLINE, EXC_LINE_THIN, EXC_LINE_NONE );
867 mnBottomLine = ::get_flagvalue( nFlags, EXC_XF2_BOTTOMLINE, EXC_LINE_THIN, EXC_LINE_NONE );
868 mnLeftColor = mnRightColor = mnTopColor = mnBottomColor = EXC_COLOR_BIFF2_BLACK;
869 SetUsedFlags( true, false );
872 void XclImpCellBorder::FillFromXF3( sal_uInt32 nBorder )
874 mnTopLine = ::extract_value< sal_uInt8 >( nBorder, 0, 3 );
875 mnLeftLine = ::extract_value< sal_uInt8 >( nBorder, 8, 3 );
876 mnBottomLine = ::extract_value< sal_uInt8 >( nBorder, 16, 3 );
877 mnRightLine = ::extract_value< sal_uInt8 >( nBorder, 24, 3 );
878 mnTopColor = ::extract_value< sal_uInt16 >( nBorder, 3, 5 );
879 mnLeftColor = ::extract_value< sal_uInt16 >( nBorder, 11, 5 );
880 mnBottomColor = ::extract_value< sal_uInt16 >( nBorder, 19, 5 );
881 mnRightColor = ::extract_value< sal_uInt16 >( nBorder, 27, 5 );
882 SetUsedFlags( true, false );
885 void XclImpCellBorder::FillFromXF5( sal_uInt32 nBorder, sal_uInt32 nArea )
887 mnTopLine = ::extract_value< sal_uInt8 >( nBorder, 0, 3 );
888 mnLeftLine = ::extract_value< sal_uInt8 >( nBorder, 3, 3 );
889 mnBottomLine = ::extract_value< sal_uInt8 >( nArea, 22, 3 );
890 mnRightLine = ::extract_value< sal_uInt8 >( nBorder, 6, 3 );
891 mnTopColor = ::extract_value< sal_uInt16 >( nBorder, 9, 7 );
892 mnLeftColor = ::extract_value< sal_uInt16 >( nBorder, 16, 7 );
893 mnBottomColor = ::extract_value< sal_uInt16 >( nArea, 25, 7 );
894 mnRightColor = ::extract_value< sal_uInt16 >( nBorder, 23, 7 );
895 SetUsedFlags( true, false );
898 void XclImpCellBorder::FillFromXF8( sal_uInt32 nBorder1, sal_uInt32 nBorder2 )
900 mnLeftLine = ::extract_value< sal_uInt8 >( nBorder1, 0, 4 );
901 mnRightLine = ::extract_value< sal_uInt8 >( nBorder1, 4, 4 );
902 mnTopLine = ::extract_value< sal_uInt8 >( nBorder1, 8, 4 );
903 mnBottomLine = ::extract_value< sal_uInt8 >( nBorder1, 12, 4 );
904 mnLeftColor = ::extract_value< sal_uInt16 >( nBorder1, 16, 7 );
905 mnRightColor = ::extract_value< sal_uInt16 >( nBorder1, 23, 7 );
906 mnTopColor = ::extract_value< sal_uInt16 >( nBorder2, 0, 7 );
907 mnBottomColor = ::extract_value< sal_uInt16 >( nBorder2, 7, 7 );
908 mbDiagTLtoBR = ::get_flag( nBorder1, EXC_XF_DIAGONAL_TL_TO_BR );
909 mbDiagBLtoTR = ::get_flag( nBorder1, EXC_XF_DIAGONAL_BL_TO_TR );
910 if( mbDiagTLtoBR || mbDiagBLtoTR )
912 mnDiagLine = ::extract_value< sal_uInt8 >( nBorder2, 21, 4 );
913 mnDiagColor = ::extract_value< sal_uInt16 >( nBorder2, 14, 7 );
915 SetUsedFlags( true, true );
918 void XclImpCellBorder::FillFromCF8( sal_uInt16 nLineStyle, sal_uInt32 nLineColor, sal_uInt32 nFlags )
920 mnLeftLine = ::extract_value< sal_uInt8 >( nLineStyle, 0, 4 );
921 mnRightLine = ::extract_value< sal_uInt8 >( nLineStyle, 4, 4 );
922 mnTopLine = ::extract_value< sal_uInt8 >( nLineStyle, 8, 4 );
923 mnBottomLine = ::extract_value< sal_uInt8 >( nLineStyle, 12, 4 );
924 mnLeftColor = ::extract_value< sal_uInt16 >( nLineColor, 0, 7 );
925 mnRightColor = ::extract_value< sal_uInt16 >( nLineColor, 7, 7 );
926 mnTopColor = ::extract_value< sal_uInt16 >( nLineColor, 16, 7 );
927 mnBottomColor = ::extract_value< sal_uInt16 >( nLineColor, 23, 7 );
928 mbLeftUsed = !::get_flag( nFlags, EXC_CF_BORDER_LEFT );
929 mbRightUsed = !::get_flag( nFlags, EXC_CF_BORDER_RIGHT );
930 mbTopUsed = !::get_flag( nFlags, EXC_CF_BORDER_TOP );
931 mbBottomUsed = !::get_flag( nFlags, EXC_CF_BORDER_BOTTOM );
932 mbDiagUsed = false;
935 bool XclImpCellBorder::HasAnyOuterBorder() const
937 return
938 (mbLeftUsed && (mnLeftLine != EXC_LINE_NONE)) ||
939 (mbRightUsed && (mnRightLine != EXC_LINE_NONE)) ||
940 (mbTopUsed && (mnTopLine != EXC_LINE_NONE)) ||
941 (mbBottomUsed && (mnBottomLine != EXC_LINE_NONE));
944 namespace {
946 /** Converts the passed line style to a ::editeng::SvxBorderLine, or returns false, if style is "no line". */
947 bool lclConvertBorderLine( ::editeng::SvxBorderLine& rLine, const XclImpPalette& rPalette, sal_uInt8 nXclLine, sal_uInt16 nXclColor )
949 static const sal_uInt16 ppnLineParam[][ 4 ] =
951 // outer width, type
952 { 0, table::BorderLineStyle::SOLID }, // 0 = none
953 { EXC_BORDER_THIN, table::BorderLineStyle::SOLID }, // 1 = thin
954 { EXC_BORDER_MEDIUM, table::BorderLineStyle::SOLID }, // 2 = medium
955 { EXC_BORDER_THIN, table::BorderLineStyle::FINE_DASHED }, // 3 = dashed
956 { EXC_BORDER_THIN, table::BorderLineStyle::DOTTED }, // 4 = dotted
957 { EXC_BORDER_THICK, table::BorderLineStyle::SOLID }, // 5 = thick
958 { EXC_BORDER_THICK, table::BorderLineStyle::DOUBLE_THIN }, // 6 = double
959 { EXC_BORDER_HAIR, table::BorderLineStyle::SOLID }, // 7 = hair
960 { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASHED }, // 8 = med dash
961 { EXC_BORDER_THIN, table::BorderLineStyle::DASH_DOT }, // 9 = thin dashdot
962 { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASH_DOT }, // A = med dashdot
963 { EXC_BORDER_THIN, table::BorderLineStyle::DASH_DOT_DOT }, // B = thin dashdotdot
964 { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASH_DOT_DOT }, // C = med dashdotdot
965 { EXC_BORDER_MEDIUM, table::BorderLineStyle::DASH_DOT } // D = med slant dashdot
968 if( nXclLine == EXC_LINE_NONE )
969 return false;
970 if( nXclLine >= std::size( ppnLineParam ) )
971 nXclLine = EXC_LINE_THIN;
973 rLine.SetColor( rPalette.GetColor( nXclColor ) );
974 rLine.SetWidth( ppnLineParam[ nXclLine ][ 0 ] );
975 rLine.SetBorderLineStyle( static_cast< SvxBorderLineStyle>(
976 ppnLineParam[ nXclLine ][ 1 ]) );
977 return true;
980 } // namespace
982 void XclImpCellBorder::FillToItemSet( SfxItemSet& rItemSet, const XclImpPalette& rPalette, bool bSkipPoolDefs ) const
984 if( mbLeftUsed || mbRightUsed || mbTopUsed || mbBottomUsed )
986 SvxBoxItem aBoxItem( ATTR_BORDER );
987 ::editeng::SvxBorderLine aLine;
988 if( mbLeftUsed && lclConvertBorderLine( aLine, rPalette, mnLeftLine, mnLeftColor ) )
989 aBoxItem.SetLine( &aLine, SvxBoxItemLine::LEFT );
990 if( mbRightUsed && lclConvertBorderLine( aLine, rPalette, mnRightLine, mnRightColor ) )
991 aBoxItem.SetLine( &aLine, SvxBoxItemLine::RIGHT );
992 if( mbTopUsed && lclConvertBorderLine( aLine, rPalette, mnTopLine, mnTopColor ) )
993 aBoxItem.SetLine( &aLine, SvxBoxItemLine::TOP );
994 if( mbBottomUsed && lclConvertBorderLine( aLine, rPalette, mnBottomLine, mnBottomColor ) )
995 aBoxItem.SetLine( &aLine, SvxBoxItemLine::BOTTOM );
996 ScfTools::PutItem( rItemSet, aBoxItem, bSkipPoolDefs );
998 if( !mbDiagUsed )
999 return;
1001 SvxLineItem aTLBRItem( ATTR_BORDER_TLBR );
1002 SvxLineItem aBLTRItem( ATTR_BORDER_BLTR );
1003 ::editeng::SvxBorderLine aLine;
1004 if( lclConvertBorderLine( aLine, rPalette, mnDiagLine, mnDiagColor ) )
1006 if( mbDiagTLtoBR )
1007 aTLBRItem.SetLine( &aLine );
1008 if( mbDiagBLtoTR )
1009 aBLTRItem.SetLine( &aLine );
1011 ScfTools::PutItem( rItemSet, aTLBRItem, bSkipPoolDefs );
1012 ScfTools::PutItem( rItemSet, aBLTRItem, bSkipPoolDefs );
1015 XclImpCellArea::XclImpCellArea()
1017 SetUsedFlags( false );
1020 void XclImpCellArea::SetUsedFlags( bool bUsed )
1022 mbForeUsed = mbBackUsed = mbPattUsed = bUsed;
1025 void XclImpCellArea::FillFromXF2( sal_uInt8 nFlags )
1027 mnPattern = ::get_flagvalue( nFlags, EXC_XF2_BACKGROUND, EXC_PATT_12_5_PERC, EXC_PATT_NONE );
1028 mnForeColor = EXC_COLOR_BIFF2_BLACK;
1029 mnBackColor = EXC_COLOR_BIFF2_WHITE;
1030 SetUsedFlags( true );
1033 void XclImpCellArea::FillFromXF3( sal_uInt16 nArea )
1035 mnPattern = ::extract_value< sal_uInt8 >( nArea, 0, 6 );
1036 mnForeColor = ::extract_value< sal_uInt16 >( nArea, 6, 5 );
1037 mnBackColor = ::extract_value< sal_uInt16 >( nArea, 11, 5 );
1038 SetUsedFlags( true );
1041 void XclImpCellArea::FillFromXF5( sal_uInt32 nArea )
1043 mnPattern = ::extract_value< sal_uInt8 >( nArea, 16, 6 );
1044 mnForeColor = ::extract_value< sal_uInt16 >( nArea, 0, 7 );
1045 mnBackColor = ::extract_value< sal_uInt16 >( nArea, 7, 7 );
1046 SetUsedFlags( true );
1049 void XclImpCellArea::FillFromXF8( sal_uInt32 nBorder2, sal_uInt16 nArea )
1051 mnPattern = ::extract_value< sal_uInt8 >( nBorder2, 26, 6 );
1052 mnForeColor = ::extract_value< sal_uInt16 >( nArea, 0, 7 );
1053 mnBackColor = ::extract_value< sal_uInt16 >( nArea, 7, 7 );
1054 SetUsedFlags( true );
1057 void XclImpCellArea::FillFromCF8( sal_uInt16 nPattern, sal_uInt16 nColor, sal_uInt32 nFlags )
1059 mnForeColor = ::extract_value< sal_uInt16 >( nColor, 0, 7 );
1060 mnBackColor = ::extract_value< sal_uInt16 >( nColor, 7, 7 );
1061 mnPattern = ::extract_value< sal_uInt8 >( nPattern, 10, 6 );
1062 mbForeUsed = !::get_flag( nFlags, EXC_CF_AREA_FGCOLOR );
1063 mbBackUsed = !::get_flag( nFlags, EXC_CF_AREA_BGCOLOR );
1064 mbPattUsed = !::get_flag( nFlags, EXC_CF_AREA_PATTERN );
1066 if( mbBackUsed && (!mbPattUsed || (mnPattern == EXC_PATT_SOLID)) )
1068 mnForeColor = mnBackColor;
1069 mnPattern = EXC_PATT_SOLID;
1070 mbForeUsed = mbPattUsed = true;
1072 else if( !mbBackUsed && mbPattUsed && (mnPattern == EXC_PATT_SOLID) )
1074 mbPattUsed = false;
1078 void XclImpCellArea::FillToItemSet( SfxItemSet& rItemSet, const XclImpPalette& rPalette, bool bSkipPoolDefs ) const
1080 if( !mbPattUsed ) // colors may be both unused in cond. formats
1081 return;
1083 SvxBrushItem aBrushItem( ATTR_BACKGROUND );
1085 // do not use IsTransparent() - old Calc filter writes transparency with different color indexes
1086 if( mnPattern == EXC_PATT_NONE )
1088 aBrushItem.SetColor( COL_TRANSPARENT );
1090 else
1092 Color aFore( rPalette.GetColor( mbForeUsed ? mnForeColor : EXC_COLOR_WINDOWTEXT ) );
1093 Color aBack( rPalette.GetColor( mbBackUsed ? mnBackColor : EXC_COLOR_WINDOWBACK ) );
1094 aBrushItem.SetColor( XclTools::GetPatternColor( aFore, aBack, mnPattern ) );
1097 ScfTools::PutItem( rItemSet, aBrushItem, bSkipPoolDefs );
1100 XclImpXF::XclImpXF( const XclImpRoot& rRoot ) :
1101 XclXFBase( true ), // default is cell XF
1102 XclImpRoot( rRoot ),
1103 mpStyleSheet( nullptr ),
1104 mnXclNumFmt( 0 ),
1105 mnXclFont( 0 )
1109 XclImpXF::~XclImpXF()
1113 void XclImpXF::ReadXF2( XclImpStream& rStrm )
1115 sal_uInt8 nReadFont, nReadNumFmt, nFlags;
1116 nReadFont = rStrm.ReaduInt8();
1117 rStrm.Ignore( 1 );
1118 nReadNumFmt = rStrm.ReaduInt8();
1119 nFlags = rStrm.ReaduInt8();
1121 // XF type always cell, no parent, used flags always true
1122 SetAllUsedFlags( true );
1124 // attributes
1125 maProtection.FillFromXF2( nReadNumFmt );
1126 mnXclFont = nReadFont;
1127 mnXclNumFmt = nReadNumFmt & EXC_XF2_VALFMT_MASK;
1128 maAlignment.FillFromXF2( nFlags );
1129 maBorder.FillFromXF2( nFlags );
1130 maArea.FillFromXF2( nFlags );
1133 void XclImpXF::ReadXF3( XclImpStream& rStrm )
1135 sal_uInt32 nBorder;
1136 sal_uInt16 nTypeProt, nAlign, nArea;
1137 sal_uInt8 nReadFont, nReadNumFmt;
1138 nReadFont = rStrm.ReaduInt8();
1139 nReadNumFmt = rStrm.ReaduInt8();
1140 nTypeProt = rStrm.ReaduInt16();
1141 nAlign = rStrm.ReaduInt16();
1142 nArea = rStrm.ReaduInt16();
1143 nBorder = rStrm.ReaduInt32();
1145 // XF type/parent, attribute used flags
1146 mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE ); // new in BIFF3
1147 mnParent = ::extract_value< sal_uInt16 >( nAlign, 4, 12 ); // new in BIFF3
1148 SetUsedFlags( ::extract_value< sal_uInt8 >( nTypeProt, 10, 6 ) );
1150 // attributes
1151 maProtection.FillFromXF3( nTypeProt );
1152 mnXclFont = nReadFont;
1153 mnXclNumFmt = nReadNumFmt;
1154 maAlignment.FillFromXF3( nAlign );
1155 maBorder.FillFromXF3( nBorder );
1156 maArea.FillFromXF3( nArea ); // new in BIFF3
1159 void XclImpXF::ReadXF4( XclImpStream& rStrm )
1161 sal_uInt32 nBorder;
1162 sal_uInt16 nTypeProt, nAlign, nArea;
1163 sal_uInt8 nReadFont, nReadNumFmt;
1164 nReadFont = rStrm.ReaduInt8();
1165 nReadNumFmt = rStrm.ReaduInt8();
1166 nTypeProt = rStrm.ReaduInt16();
1167 nAlign = rStrm.ReaduInt16();
1168 nArea = rStrm.ReaduInt16();
1169 nBorder = rStrm.ReaduInt32();
1171 // XF type/parent, attribute used flags
1172 mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE );
1173 mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 );
1174 SetUsedFlags( ::extract_value< sal_uInt8 >( nAlign, 10, 6 ) );
1176 // attributes
1177 maProtection.FillFromXF3( nTypeProt );
1178 mnXclFont = nReadFont;
1179 mnXclNumFmt = nReadNumFmt;
1180 maAlignment.FillFromXF4( nAlign );
1181 maBorder.FillFromXF3( nBorder );
1182 maArea.FillFromXF3( nArea );
1185 void XclImpXF::ReadXF5( XclImpStream& rStrm )
1187 sal_uInt32 nArea, nBorder;
1188 sal_uInt16 nTypeProt, nAlign;
1189 mnXclFont = rStrm.ReaduInt16();
1190 mnXclNumFmt = rStrm.ReaduInt16();
1191 nTypeProt = rStrm.ReaduInt16();
1192 nAlign = rStrm.ReaduInt16();
1193 nArea = rStrm.ReaduInt32();
1194 nBorder = rStrm.ReaduInt32();
1196 // XF type/parent, attribute used flags
1197 mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE );
1198 mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 );
1199 SetUsedFlags( ::extract_value< sal_uInt8 >( nAlign, 10, 6 ) );
1201 // attributes
1202 maProtection.FillFromXF3( nTypeProt );
1203 maAlignment.FillFromXF5( nAlign );
1204 maBorder.FillFromXF5( nBorder, nArea );
1205 maArea.FillFromXF5( nArea );
1208 void XclImpXF::ReadXF8( XclImpStream& rStrm )
1210 sal_uInt32 nBorder1, nBorder2;
1211 sal_uInt16 nTypeProt, nAlign, nMiscAttrib, nArea;
1212 mnXclFont = rStrm.ReaduInt16();
1213 mnXclNumFmt = rStrm.ReaduInt16();
1214 nTypeProt = rStrm.ReaduInt16();
1215 nAlign = rStrm.ReaduInt16();
1216 nMiscAttrib = rStrm.ReaduInt16();
1217 nBorder1 = rStrm.ReaduInt32();
1218 nBorder2 = rStrm.ReaduInt32( );
1219 nArea = rStrm.ReaduInt16();
1221 // XF type/parent, attribute used flags
1222 mbCellXF = !::get_flag( nTypeProt, EXC_XF_STYLE );
1223 mnParent = ::extract_value< sal_uInt16 >( nTypeProt, 4, 12 );
1224 SetUsedFlags( ::extract_value< sal_uInt8 >( nMiscAttrib, 10, 6 ) );
1226 // attributes
1227 maProtection.FillFromXF3( nTypeProt );
1228 maAlignment.FillFromXF8( nAlign, nMiscAttrib );
1229 maBorder.FillFromXF8( nBorder1, nBorder2 );
1230 maArea.FillFromXF8( nBorder2, nArea );
1233 void XclImpXF::ReadXF( XclImpStream& rStrm )
1235 switch( GetBiff() )
1237 case EXC_BIFF2: ReadXF2( rStrm ); break;
1238 case EXC_BIFF3: ReadXF3( rStrm ); break;
1239 case EXC_BIFF4: ReadXF4( rStrm ); break;
1240 case EXC_BIFF5: ReadXF5( rStrm ); break;
1241 case EXC_BIFF8: ReadXF8( rStrm ); break;
1242 default: DBG_ERROR_BIFF();
1246 const ScPatternAttr& XclImpXF::CreatePattern( bool bSkipPoolDefs )
1248 if( mpPattern )
1249 return *mpPattern;
1251 // create new pattern attribute set
1252 mpPattern.reset( new ScPatternAttr(GetDoc().getCellAttributeHelper()) );
1253 SfxItemSet& rItemSet = mpPattern->GetItemSet();
1254 XclImpXF* pParentXF = IsCellXF() ? GetXFBuffer().GetXF( mnParent ) : nullptr;
1256 // parent cell style
1257 if( IsCellXF() && !mpStyleSheet )
1259 mpStyleSheet = GetXFBuffer().CreateStyleSheet( mnParent );
1261 /* Enables mb***Used flags, if the formatting attributes differ from
1262 the passed XF record. In cell XFs Excel uses the cell attributes,
1263 if they differ from the parent style XF.
1264 ...or if the respective flag is not set in parent style XF. */
1265 if( pParentXF )
1267 if( !mbProtUsed )
1268 mbProtUsed = !pParentXF->mbProtUsed || !(maProtection == pParentXF->maProtection);
1269 if( !mbFontUsed )
1270 mbFontUsed = !pParentXF->mbFontUsed || (mnXclFont != pParentXF->mnXclFont);
1271 if( !mbFmtUsed )
1272 mbFmtUsed = !pParentXF->mbFmtUsed || (mnXclNumFmt != pParentXF->mnXclNumFmt);
1273 if( !mbAlignUsed )
1274 mbAlignUsed = !pParentXF->mbAlignUsed || !(maAlignment == pParentXF->maAlignment);
1275 if( !mbBorderUsed )
1276 mbBorderUsed = !pParentXF->mbBorderUsed || !(maBorder == pParentXF->maBorder);
1277 if( !mbAreaUsed )
1278 mbAreaUsed = !pParentXF->mbAreaUsed || !(maArea == pParentXF->maArea);
1282 // cell protection
1283 if( mbProtUsed )
1284 maProtection.FillToItemSet( rItemSet, bSkipPoolDefs );
1286 // font
1287 if( mbFontUsed )
1288 GetFontBuffer().FillToItemSet( rItemSet, XclFontItemType::Cell, mnXclFont, bSkipPoolDefs );
1290 // value format
1291 if( mbFmtUsed )
1293 GetNumFmtBuffer().FillToItemSet( rItemSet, mnXclNumFmt, bSkipPoolDefs );
1294 // Trace occurrences of Windows date formats
1295 GetTracer().TraceDates( mnXclNumFmt );
1298 // alignment
1299 if( mbAlignUsed )
1300 maAlignment.FillToItemSet( rItemSet, GetFontBuffer().GetFont( mnXclFont ), bSkipPoolDefs );
1302 // border
1303 if( mbBorderUsed )
1305 maBorder.FillToItemSet( rItemSet, GetPalette(), bSkipPoolDefs );
1306 GetTracer().TraceBorderLineStyle(maBorder.mnLeftLine > EXC_LINE_HAIR ||
1307 maBorder.mnRightLine > EXC_LINE_HAIR || maBorder.mnTopLine > EXC_LINE_HAIR ||
1308 maBorder.mnBottomLine > EXC_LINE_HAIR );
1311 // area
1312 if( mbAreaUsed )
1314 maArea.FillToItemSet( rItemSet, GetPalette(), bSkipPoolDefs );
1315 GetTracer().TraceFillPattern(maArea.mnPattern != EXC_PATT_NONE &&
1316 maArea.mnPattern != EXC_PATT_SOLID);
1319 /* #i38709# Decide which rotation reference mode to use. If any outer
1320 border line of the cell is set (either explicitly or via cell style),
1321 and the cell contents are rotated, set rotation reference to bottom of
1322 cell. This causes the borders to be painted rotated with the text. */
1323 if( mbAlignUsed || mbBorderUsed )
1325 SvxRotateMode eRotateMode = SVX_ROTATE_MODE_STANDARD;
1326 const XclImpCellAlign* pAlign = mbAlignUsed ? &maAlignment : (pParentXF ? &pParentXF->maAlignment : nullptr);
1327 const XclImpCellBorder* pBorder = mbBorderUsed ? &maBorder : (pParentXF ? &pParentXF->maBorder : nullptr);
1328 if( pAlign && pBorder && (0 < pAlign->mnRotation) && (pAlign->mnRotation <= 180) && pBorder->HasAnyOuterBorder() )
1329 eRotateMode = SVX_ROTATE_MODE_BOTTOM;
1330 ScfTools::PutItem( rItemSet, SvxRotateModeItem( eRotateMode, ATTR_ROTATE_MODE ), bSkipPoolDefs );
1333 // Excel's cell margins are different from Calc's default margins.
1334 SvxMarginItem aItem(40, 40, 40, 40, ATTR_MARGIN);
1335 ScfTools::PutItem(rItemSet, aItem, bSkipPoolDefs);
1337 return *mpPattern;
1340 void XclImpXF::ApplyPatternToAttrVector(
1341 std::vector<ScAttrEntry>& rAttrs, SCROW nRow1, SCROW nRow2, sal_uInt32 nForceScNumFmt)
1343 // force creation of cell style and hard formatting, do it here to have mpStyleSheet
1344 CreatePattern();
1345 ScPatternAttr& rPat = *mpPattern;
1347 // insert into document
1348 ScDocument& rDoc = GetDoc();
1350 if (IsCellXF())
1352 if (mpStyleSheet)
1354 // Apply style sheet. Don't clear the direct formats.
1355 rPat.SetStyleSheet(mpStyleSheet, false);
1357 else
1359 // When the cell format is not associated with any style, use the
1360 // 'Default' style. Some buggy XLS docs generated by apps other
1361 // than Excel (such as 1C) may not have any built-in styles at
1362 // all.
1363 ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool();
1364 if (pStylePool)
1366 ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>(
1367 pStylePool->Find(
1368 ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Para));
1370 if (pStyleSheet)
1371 rPat.SetStyleSheet(pStyleSheet, false);
1377 if (nForceScNumFmt != NUMBERFORMAT_ENTRY_NOT_FOUND)
1379 ScPatternAttr aNumPat(rDoc.getCellAttributeHelper());
1380 GetNumFmtBuffer().FillScFmtToItemSet(aNumPat.GetItemSet(), nForceScNumFmt);
1381 rPat.GetItemSet().Put(aNumPat.GetItemSet());
1384 // Make sure we skip unnamed styles.
1385 if (!rPat.GetStyleName())
1386 return;
1388 // Check for a gap between the last entry and this one.
1389 bool bHasGap = false;
1390 if (rAttrs.empty() && nRow1 > 0)
1391 // First attribute range doesn't start at row 0.
1392 bHasGap = true;
1394 if (!rAttrs.empty() && rAttrs.back().nEndRow + 1 < nRow1)
1395 bHasGap = true;
1397 if (bHasGap)
1399 // Fill this gap with the default pattern.
1400 ScAttrEntry aEntry;
1401 aEntry.nEndRow = nRow1 - 1;
1402 aEntry.setScPatternAttr(&rDoc.getCellAttributeHelper().getDefaultCellAttribute());
1403 rAttrs.push_back(aEntry);
1406 ScAttrEntry aEntry;
1407 aEntry.nEndRow = nRow2;
1408 aEntry.setScPatternAttr(&rPat, false);
1409 rAttrs.push_back(aEntry);
1412 void XclImpXF::ApplyPattern(
1413 SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2,
1414 SCTAB nScTab )
1416 // force creation of cell style and hard formatting, do it here to have mpStyleSheet
1417 const ScPatternAttr& rPattern = CreatePattern();
1419 // insert into document
1420 ScDocument& rDoc = GetDoc();
1421 if( IsCellXF() && mpStyleSheet )
1422 rDoc.ApplyStyleAreaTab( nScCol1, nScRow1, nScCol2, nScRow2, nScTab, *mpStyleSheet );
1423 if( HasUsedFlags() )
1424 rDoc.ApplyPatternAreaTab( nScCol1, nScRow1, nScCol2, nScRow2, nScTab, rPattern );
1428 /*static*/ void XclImpXF::ApplyPatternForBiff2CellFormat( const XclImpRoot& rRoot,
1429 const ScAddress& rScPos, sal_uInt8 nFlags1, sal_uInt8 nFlags2, sal_uInt8 nFlags3 )
1431 /* Create an XF object and let it do the work. We will have access to its
1432 private members here. */
1433 XclImpXF aXF( rRoot );
1435 // no used flags available in BIFF2 (always true)
1436 aXF.SetAllUsedFlags( true );
1438 // set the attributes
1439 aXF.maProtection.FillFromXF2( nFlags1 );
1440 aXF.maAlignment.FillFromXF2( nFlags3 );
1441 aXF.maBorder.FillFromXF2( nFlags3 );
1442 aXF.maArea.FillFromXF2( nFlags3 );
1443 aXF.mnXclNumFmt = ::extract_value< sal_uInt16 >( nFlags2, 0, 6 );
1444 aXF.mnXclFont = ::extract_value< sal_uInt16 >( nFlags2, 6, 2 );
1446 // write the attributes to the cell
1447 aXF.ApplyPattern( rScPos.Col(), rScPos.Row(), rScPos.Col(), rScPos.Row(), rScPos.Tab() );
1450 void XclImpXF::SetUsedFlags( sal_uInt8 nUsedFlags )
1452 /* Notes about finding the mb***Used flags:
1453 - In cell XFs a *set* bit means a used attribute.
1454 - In style XFs a *cleared* bit means a used attribute.
1455 The mb***Used members always store true, if the attribute is used.
1456 The "mbCellXF == ::get_flag(...)" construct evaluates to true in
1457 both mentioned cases: cell XF and set bit; or style XF and cleared bit.
1459 mbProtUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_PROT ));
1460 mbFontUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_FONT ));
1461 mbFmtUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_VALFMT ));
1462 mbAlignUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_ALIGN ));
1463 mbBorderUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_BORDER ));
1464 mbAreaUsed = (mbCellXF == ::get_flag( nUsedFlags, EXC_XF_DIFF_AREA ));
1467 XclImpStyle::XclImpStyle( const XclImpRoot& rRoot ) :
1468 XclImpRoot( rRoot ),
1469 mnXfId( EXC_XF_NOTFOUND ),
1470 mnBuiltinId( EXC_STYLE_USERDEF ),
1471 mnLevel( EXC_STYLE_NOLEVEL ),
1472 mbBuiltin( false ),
1473 mbCustom( false ),
1474 mbHidden( false ),
1475 mpStyleSheet( nullptr )
1479 void XclImpStyle::ReadStyle( XclImpStream& rStrm )
1481 OSL_ENSURE_BIFF( GetBiff() >= EXC_BIFF3 );
1483 sal_uInt16 nXFIndex;
1484 nXFIndex = rStrm.ReaduInt16();
1485 mnXfId = nXFIndex & EXC_STYLE_XFMASK;
1486 mbBuiltin = ::get_flag( nXFIndex, EXC_STYLE_BUILTIN );
1488 if( mbBuiltin )
1490 mnBuiltinId = rStrm.ReaduInt8();
1491 mnLevel = rStrm.ReaduInt8();
1493 else
1495 maName = (GetBiff() <= EXC_BIFF5) ? rStrm.ReadByteString( false ) : rStrm.ReadUniString();
1496 // #i103281# check if this is a new built-in style introduced in XL2007
1497 if( (GetBiff() == EXC_BIFF8) && (rStrm.GetNextRecId() == EXC_ID_STYLEEXT) && rStrm.StartNextRecord() )
1499 sal_uInt8 nExtFlags;
1500 rStrm.Ignore( 12 );
1501 nExtFlags = rStrm.ReaduInt8();
1502 mbBuiltin = ::get_flag( nExtFlags, EXC_STYLEEXT_BUILTIN );
1503 mbCustom = ::get_flag( nExtFlags, EXC_STYLEEXT_CUSTOM );
1504 mbHidden = ::get_flag( nExtFlags, EXC_STYLEEXT_HIDDEN );
1505 if( mbBuiltin )
1507 rStrm.Ignore( 1 ); // category
1508 mnBuiltinId = rStrm.ReaduInt8();
1509 mnLevel = rStrm.ReaduInt8();
1515 ScStyleSheet* XclImpStyle::CreateStyleSheet()
1517 // #i1624# #i1768# ignore unnamed user styles
1518 if( !mpStyleSheet && (!maFinalName.isEmpty()) )
1520 bool bCreatePattern = false;
1521 XclImpXF* pXF = GetXFBuffer().GetXF( mnXfId );
1523 bool bDefStyle = mbBuiltin && (mnBuiltinId == EXC_STYLE_NORMAL);
1524 if( bDefStyle )
1526 // set all flags to true to get all items in XclImpXF::CreatePattern()
1527 if( pXF ) pXF->SetAllUsedFlags( true );
1528 // use existing "Default" style sheet
1529 mpStyleSheet = static_cast< ScStyleSheet* >( GetStyleSheetPool().Find(
1530 ScResId( STR_STYLENAME_STANDARD ), SfxStyleFamily::Para ) );
1531 OSL_ENSURE( mpStyleSheet, "XclImpStyle::CreateStyleSheet - Default style not found" );
1532 bCreatePattern = true;
1534 else
1536 /* #i103281# do not create another style sheet of the same name,
1537 if it exists already. This is needed to prevent that styles
1538 pasted from clipboard get duplicated over and over. */
1539 mpStyleSheet = static_cast< ScStyleSheet* >( GetStyleSheetPool().Find( maFinalName, SfxStyleFamily::Para ) );
1540 if( !mpStyleSheet )
1542 mpStyleSheet = &static_cast< ScStyleSheet& >( GetStyleSheetPool().Make( maFinalName, SfxStyleFamily::Para, SfxStyleSearchBits::UserDefined ) );
1543 bCreatePattern = true;
1547 // bDefStyle==true omits default pool items in CreatePattern()
1548 if( bCreatePattern && mpStyleSheet && pXF )
1549 mpStyleSheet->GetItemSet().Put( pXF->CreatePattern( bDefStyle ).GetItemSet() );
1551 return mpStyleSheet;
1554 void XclImpStyle::CreateUserStyle( const OUString& rFinalName )
1556 maFinalName = rFinalName;
1557 if( !IsBuiltin() || mbCustom )
1558 CreateStyleSheet();
1561 XclImpXFBuffer::XclImpXFBuffer( const XclImpRoot& rRoot ) :
1562 XclImpRoot( rRoot )
1566 void XclImpXFBuffer::Initialize()
1568 maXFList.clear();
1569 maBuiltinStyles.clear();
1570 maUserStyles.clear();
1571 maStylesByXf.clear();
1574 void XclImpXFBuffer::ReadXF( XclImpStream& rStrm )
1576 std::unique_ptr<XclImpXF> xXF = std::make_unique<XclImpXF>(GetRoot());
1577 xXF->ReadXF(rStrm);
1578 maXFList.emplace_back(std::move(xXF));
1581 void XclImpXFBuffer::ReadStyle( XclImpStream& rStrm )
1583 std::unique_ptr<XclImpStyle> xStyle(std::make_unique<XclImpStyle>(GetRoot()));
1584 xStyle->ReadStyle(rStrm);
1585 XclImpStyleList& rStyleList = (xStyle->IsBuiltin() ? maBuiltinStyles : maUserStyles);
1586 rStyleList.emplace_back(std::move(xStyle));
1587 XclImpStyle* pStyle = rStyleList.back().get();
1588 OSL_ENSURE( maStylesByXf.count( pStyle->GetXfId() ) == 0, "XclImpXFBuffer::ReadStyle - multiple styles with equal XF identifier" );
1589 maStylesByXf[ pStyle->GetXfId() ] = pStyle;
1592 sal_uInt16 XclImpXFBuffer::GetFontIndex( sal_uInt16 nXFIndex ) const
1594 const XclImpXF* pXF = GetXF( nXFIndex );
1595 return pXF ? pXF->GetFontIndex() : EXC_FONT_NOTFOUND;
1598 const XclImpFont* XclImpXFBuffer::GetFont( sal_uInt16 nXFIndex ) const
1600 return GetFontBuffer().GetFont( GetFontIndex( nXFIndex ) );
1603 namespace {
1605 /** Functor for case-insensitive string comparison, usable in maps etc. */
1606 struct IgnoreCaseCompare
1608 bool operator()( std::u16string_view rName1, std::u16string_view rName2 ) const
1609 { return o3tl::compareToIgnoreAsciiCase( rName1, rName2 ) < 0; }
1612 } // namespace
1614 void XclImpXFBuffer::CreateUserStyles()
1616 // calculate final names of all styles
1617 std::map< OUString, XclImpStyle*, IgnoreCaseCompare > aCellStyles;
1618 std::vector< XclImpStyle* > aConflictNameStyles;
1620 /* First, reserve style names that are built-in in Calc. This causes that
1621 imported cell styles get different unused names and thus do not try to
1622 overwrite these built-in styles. For BIFF4 workbooks (which contain a
1623 separate list of cell styles per sheet), reserve all existing styles if
1624 current sheet is not the first sheet (this styles buffer will be
1625 initialized again for every new sheet). This will create unique names
1626 for styles in different sheets with the same name. Assuming that the
1627 BIFF4W import filter is never used to import from clipboard... */
1628 bool bReserveAll = (GetBiff() == EXC_BIFF4) && (GetCurrScTab() > 0);
1629 SfxStyleSheetIterator aStyleIter( GetDoc().GetStyleSheetPool(), SfxStyleFamily::Para );
1630 OUString aStandardName = ScResId( STR_STYLENAME_STANDARD );
1631 for( SfxStyleSheetBase* pStyleSheet = aStyleIter.First(); pStyleSheet; pStyleSheet = aStyleIter.Next() )
1632 if( (pStyleSheet->GetName() != aStandardName) && (bReserveAll || !pStyleSheet->IsUserDefined()) )
1633 if( aCellStyles.count( pStyleSheet->GetName() ) == 0 )
1634 aCellStyles[ pStyleSheet->GetName() ] = nullptr;
1636 /* Calculate names of built-in styles. Store styles with reserved names
1637 in the aConflictNameStyles list. */
1638 for( const auto& rxStyle : maBuiltinStyles )
1640 OUString aStyleName = XclTools::GetBuiltInStyleName( rxStyle->GetBuiltinId(), rxStyle->GetName(), rxStyle->GetLevel() );
1641 OSL_ENSURE( bReserveAll || (aCellStyles.count( aStyleName ) == 0),
1642 "XclImpXFBuffer::CreateUserStyles - multiple styles with equal built-in identifier" );
1643 if( aCellStyles.count( aStyleName ) > 0 )
1644 aConflictNameStyles.push_back( rxStyle.get() );
1645 else
1646 aCellStyles[ aStyleName ] = rxStyle.get();
1649 /* Calculate names of user defined styles. Store styles with reserved
1650 names in the aConflictNameStyles list. */
1651 for( const auto& rxStyle : maUserStyles )
1653 // #i1624# #i1768# ignore unnamed user styles
1654 if( !rxStyle->GetName().isEmpty() )
1656 if( aCellStyles.count( rxStyle->GetName() ) > 0 )
1657 aConflictNameStyles.push_back( rxStyle.get() );
1658 else
1659 aCellStyles[ rxStyle->GetName() ] = rxStyle.get();
1663 // find unused names for all styles with conflicting names
1664 for( XclImpStyle* pStyle : aConflictNameStyles )
1666 OUString aUnusedName;
1667 sal_Int32 nIndex = 0;
1670 aUnusedName = pStyle->GetName() + " " + OUString::number( ++nIndex );
1672 while( aCellStyles.count( aUnusedName ) > 0 );
1673 aCellStyles[ aUnusedName ] = pStyle;
1676 // set final names and create user-defined and modified built-in cell styles
1677 for( auto& [rStyleName, rpStyle] : aCellStyles )
1678 if( rpStyle )
1679 rpStyle->CreateUserStyle( rStyleName );
1682 ScStyleSheet* XclImpXFBuffer::CreateStyleSheet( sal_uInt16 nXFIndex )
1684 XclImpStyleMap::iterator aIt = maStylesByXf.find( nXFIndex );
1685 return (aIt == maStylesByXf.end()) ? nullptr : aIt->second->CreateStyleSheet();
1688 // Buffer for XF indexes in cells =============================================
1690 bool XclImpXFRange::Expand( SCROW nScRow, const XclImpXFIndex& rXFIndex )
1692 if( maXFIndex != rXFIndex )
1693 return false;
1695 if( mnScRow2 + 1 == nScRow )
1697 ++mnScRow2;
1698 return true;
1700 if( mnScRow1 > 0 && (mnScRow1 - 1 == nScRow) )
1702 --mnScRow1;
1703 return true;
1706 return false;
1709 bool XclImpXFRange::Expand( const XclImpXFRange& rNextRange )
1711 OSL_ENSURE( mnScRow2 < rNextRange.mnScRow1, "XclImpXFRange::Expand - rows out of order" );
1712 if( (maXFIndex == rNextRange.maXFIndex) && (mnScRow2 + 1 == rNextRange.mnScRow1) )
1714 mnScRow2 = rNextRange.mnScRow2;
1715 return true;
1717 return false;
1720 void XclImpXFRangeColumn::SetDefaultXF( const XclImpXFIndex& rXFIndex, const XclImpRoot& rRoot )
1722 // List should be empty when inserting the default column format.
1723 // Later explicit SetXF() calls will break up this range.
1724 OSL_ENSURE( maIndexList.empty(), "XclImpXFRangeColumn::SetDefaultXF - Setting Default Column XF is not empty" );
1726 // insert a complete row range with one insert.
1727 maIndexList.push_back( XclImpXFRange( 0, rRoot.GetDoc().MaxRow(), rXFIndex ) );
1730 void XclImpXFRangeColumn::SetXF( SCROW nScRow, const XclImpXFIndex& rXFIndex )
1732 XclImpXFRange* pPrevRange;
1733 XclImpXFRange* pNextRange;
1734 sal_uLong nNextIndex;
1736 Find( pPrevRange, pNextRange, nNextIndex, nScRow );
1738 // previous range:
1739 // try to overwrite XF (if row is contained in) or try to expand range
1740 if( pPrevRange )
1742 if( pPrevRange->Contains( nScRow ) ) // overwrite old XF
1744 if( rXFIndex == pPrevRange->maXFIndex )
1745 return;
1747 SCROW nFirstScRow = pPrevRange->mnScRow1;
1748 SCROW nLastScRow = pPrevRange->mnScRow2;
1749 sal_uLong nIndex = nNextIndex - 1;
1750 XclImpXFRange* pThisRange = pPrevRange;
1751 pPrevRange = (nIndex > 0 && nIndex <= maIndexList.size()) ? &maIndexList[ nIndex - 1 ] : nullptr;
1753 if( nFirstScRow == nLastScRow ) // replace solely XF
1755 pThisRange->maXFIndex = rXFIndex;
1756 TryConcatPrev( nNextIndex ); // try to concat. next with this
1757 TryConcatPrev( nIndex ); // try to concat. this with previous
1759 else if( nFirstScRow == nScRow ) // replace first XF
1761 ++(pThisRange->mnScRow1);
1762 // try to concatenate with previous of this
1763 if( !pPrevRange || !pPrevRange->Expand( nScRow, rXFIndex ) )
1764 Insert( XclImpXFRange( nScRow, rXFIndex ), nIndex );
1766 else if( nLastScRow == nScRow ) // replace last XF
1768 --(pThisRange->mnScRow2);
1769 if( !pNextRange || !pNextRange->Expand( nScRow, rXFIndex ) )
1770 Insert( XclImpXFRange( nScRow, rXFIndex ), nNextIndex );
1772 else // insert in the middle of the range
1774 pThisRange->mnScRow1 = nScRow + 1;
1775 XclImpXFIndex aXFIndex(pThisRange->maXFIndex);
1776 // List::Insert() moves entries towards end of list, so insert twice at nIndex
1777 Insert( XclImpXFRange( nScRow, rXFIndex ), nIndex );
1778 Insert( XclImpXFRange( nFirstScRow, nScRow - 1, aXFIndex ), nIndex );
1780 return;
1782 else if( pPrevRange->Expand( nScRow, rXFIndex ) ) // try to expand
1784 TryConcatPrev( nNextIndex ); // try to concatenate next with expanded
1785 return;
1789 // try to expand next range
1790 if( pNextRange && pNextRange->Expand( nScRow, rXFIndex ) )
1791 return;
1793 // create new range
1794 Insert( XclImpXFRange( nScRow, rXFIndex ), nNextIndex );
1797 void XclImpXFRangeColumn::Insert(XclImpXFRange aXFRange, sal_uLong nIndex)
1799 maIndexList.insert( maIndexList.begin() + nIndex, std::move(aXFRange) );
1802 void XclImpXFRangeColumn::Find(
1803 XclImpXFRange*& rpPrevRange, XclImpXFRange*& rpNextRange,
1804 sal_uLong& rnNextIndex, SCROW nScRow )
1807 // test whether list is empty
1808 if( maIndexList.empty() )
1810 rpPrevRange = rpNextRange = nullptr;
1811 rnNextIndex = 0;
1812 return;
1815 rpPrevRange = &maIndexList.front();
1816 rpNextRange = &maIndexList.back();
1818 // test whether row is at end of list (contained in or behind last range)
1819 // rpPrevRange will contain a possible existing row
1820 if( rpNextRange->mnScRow1 <= nScRow )
1822 rpPrevRange = rpNextRange;
1823 rpNextRange = nullptr;
1824 rnNextIndex = maIndexList.size();
1825 return;
1828 // test whether row is at beginning of list (really before first range)
1829 if( nScRow < rpPrevRange->mnScRow1 )
1831 rpNextRange = rpPrevRange;
1832 rpPrevRange = nullptr;
1833 rnNextIndex = 0;
1834 return;
1837 // loop: find range entries before and after new row
1838 // break the loop if there is no more range between first and last -or-
1839 // if rpPrevRange contains nScRow (rpNextRange will never contain nScRow)
1840 sal_uLong nPrevIndex = 0;
1841 sal_uLong nMidIndex;
1842 rnNextIndex = maIndexList.size() - 1;
1843 XclImpXFRange* pMidRange;
1844 while( ((rnNextIndex - nPrevIndex) > 1) && (rpPrevRange->mnScRow2 < nScRow) )
1846 nMidIndex = (nPrevIndex + rnNextIndex) / 2;
1847 pMidRange = &maIndexList[nMidIndex];
1848 assert(pMidRange && "XclImpXFRangeColumn::Find - missing XF index range");
1849 if( nScRow < pMidRange->mnScRow1 ) // row is really before pMidRange
1851 rpNextRange = pMidRange;
1852 rnNextIndex = nMidIndex;
1854 else // row is in or after pMidRange
1856 rpPrevRange = pMidRange;
1857 nPrevIndex = nMidIndex;
1861 // find next rpNextRange if rpPrevRange contains nScRow
1862 if( nScRow <= rpPrevRange->mnScRow2 )
1864 rnNextIndex = nPrevIndex + 1;
1865 rpNextRange = &maIndexList[rnNextIndex];
1869 void XclImpXFRangeColumn::TryConcatPrev( sal_uLong nIndex )
1871 if( !nIndex || nIndex >= maIndexList.size() )
1872 return;
1874 XclImpXFRange& prevRange = maIndexList[ nIndex - 1 ];
1875 XclImpXFRange& nextRange = maIndexList[ nIndex ];
1877 if( prevRange.Expand( nextRange ) )
1878 maIndexList.erase( maIndexList.begin() + nIndex );
1881 XclImpXFRangeBuffer::XclImpXFRangeBuffer( const XclImpRoot& rRoot ) :
1882 XclImpRoot( rRoot )
1886 XclImpXFRangeBuffer::~XclImpXFRangeBuffer()
1890 void XclImpXFRangeBuffer::Initialize()
1892 maColumns.clear();
1893 maRows.clear();
1894 maHyperlinks.clear();
1895 maMergeList.RemoveAll();
1898 void XclImpXFRangeBuffer::SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex, XclImpXFInsertMode eMode )
1900 SCCOL nScCol = rScPos.Col();
1901 SCROW nScRow = rScPos.Row();
1903 // set cell XF's
1904 size_t nIndex = static_cast< size_t >( nScCol );
1905 if( maColumns.size() <= nIndex )
1906 maColumns.resize( nIndex + 1 );
1907 if( !maColumns[ nIndex ] )
1908 maColumns[ nIndex ].emplace();
1909 // remember all Boolean cells, they will get 'Standard' number format
1910 maColumns[ nIndex ]->SetXF( nScRow, XclImpXFIndex( nXFIndex, eMode == xlXFModeBoolCell ) );
1912 // set "center across selection" and "fill" attribute for all following empty cells
1913 // ignore it on row default XFs
1914 if( eMode == xlXFModeRow )
1915 return;
1917 const XclImpXF* pXF = GetXFBuffer().GetXF( nXFIndex );
1918 if( pXF && ((pXF->GetHorAlign() == EXC_XF_HOR_CENTER_AS) || (pXF->GetHorAlign() == EXC_XF_HOR_FILL)) )
1920 // expand last merged range if this attribute is set repeatedly
1921 ScRange* pRange = maMergeList.empty() ? nullptr : &maMergeList.back();
1922 if (pRange && (pRange->aEnd.Row() == nScRow) && (pRange->aEnd.Col() + 1 == nScCol) && (eMode == xlXFModeBlank))
1923 pRange->aEnd.IncCol();
1924 else if( eMode != xlXFModeBlank ) // do not merge empty cells
1925 maMergeList.push_back( ScRange( nScCol, nScRow, 0 ) );
1929 void XclImpXFRangeBuffer::SetXF( const ScAddress& rScPos, sal_uInt16 nXFIndex )
1931 SetXF( rScPos, nXFIndex, xlXFModeCell );
1934 void XclImpXFRangeBuffer::SetBlankXF( const ScAddress& rScPos, sal_uInt16 nXFIndex )
1936 SetXF( rScPos, nXFIndex, xlXFModeBlank );
1939 void XclImpXFRangeBuffer::SetBoolXF( const ScAddress& rScPos, sal_uInt16 nXFIndex )
1941 SetXF( rScPos, nXFIndex, xlXFModeBoolCell );
1944 void XclImpXFRangeBuffer::SetRowDefXF( SCROW nScRow, sal_uInt16 nXFIndex )
1946 size_t nIndex = static_cast< size_t >( nScRow );
1947 if( maRows.size() <= nIndex )
1948 maRows.resize( nIndex + 1 );
1949 maRows[ nIndex ].emplace(nXFIndex);
1952 void XclImpXFRangeBuffer::SetColumnDefXF( SCCOL nScCol, sal_uInt16 nXFIndex )
1954 // our array should not have values when creating the default column format.
1955 size_t nIndex = static_cast< size_t >( nScCol );
1956 if( maColumns.size() <= nIndex )
1957 maColumns.resize( nIndex + 1 );
1958 OSL_ENSURE( !maColumns[ nIndex ], "XclImpXFRangeBuffer::SetColumnDefXF - default column of XFs already has values" );
1959 maColumns[ nIndex ].emplace();
1960 maColumns[ nIndex ]->SetDefaultXF( XclImpXFIndex( nXFIndex ), GetRoot());
1963 void XclImpXFRangeBuffer::SetBorderLine( const ScRange& rRange, SCTAB nScTab, SvxBoxItemLine nLine )
1965 SCCOL nFromScCol = (nLine == SvxBoxItemLine::RIGHT) ? rRange.aEnd.Col() : rRange.aStart.Col();
1966 SCROW nFromScRow = (nLine == SvxBoxItemLine::BOTTOM) ? rRange.aEnd.Row() : rRange.aStart.Row();
1967 ScDocument& rDoc = GetDoc();
1969 const SvxBoxItem* pFromItem =
1970 rDoc.GetAttr( nFromScCol, nFromScRow, nScTab, ATTR_BORDER );
1971 const SvxBoxItem* pToItem =
1972 rDoc.GetAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, ATTR_BORDER );
1974 SvxBoxItem aNewItem( *pToItem );
1975 aNewItem.SetLine( pFromItem->GetLine( nLine ), nLine );
1976 rDoc.ApplyAttr( rRange.aStart.Col(), rRange.aStart.Row(), nScTab, aNewItem );
1979 void XclImpXFRangeBuffer::SetHyperlink( const XclRange& rXclRange, const OUString& rUrl )
1981 maHyperlinks.emplace_back( rXclRange, rUrl );
1984 void XclImpXFRangeBuffer::SetMerge( SCCOL nScCol1, SCROW nScRow1, SCCOL nScCol2, SCROW nScRow2 )
1986 if( (nScCol1 < nScCol2) || (nScRow1 < nScRow2) )
1987 maMergeList.push_back( ScRange( nScCol1, nScRow1, 0, nScCol2, nScRow2, 0 ) );
1990 void XclImpXFRangeBuffer::Finalize()
1992 ScDocumentImport& rDocImport = GetDocImport();
1993 ScDocument& rDoc = rDocImport.getDoc();
1994 SCTAB nScTab = GetCurrScTab();
1996 // apply row styles
1997 for( SCROW nScRow = 0; nScRow < static_cast<SCROW>(maRows.size()); ++nScRow )
1999 if (!maRows[nScRow])
2000 continue;
2001 sal_uInt16 nXFIndex = *maRows[nScRow];
2002 for( SCCOL nScCol = 0; nScCol < static_cast<SCCOL>(maColumns.size()); ++nScCol )
2003 if (maColumns[nScCol])
2004 SetXF( ScAddress( nScCol, nScRow, 0 ), nXFIndex, xlXFModeRow );
2007 // apply patterns
2008 XclImpXFBuffer& rXFBuffer = GetXFBuffer();
2009 ScDocumentImport::Attrs aPendingAttrParam;
2010 SCCOL pendingColStart = -1;
2011 SCCOL pendingColEnd = -1;
2012 SCCOL nScCol = 0;
2013 for( auto& rxColumn : maColumns )
2015 // apply all cell styles of an existing column
2016 if( rxColumn )
2018 XclImpXFRangeColumn& rColumn = *rxColumn;
2019 std::vector<ScAttrEntry> aAttrs;
2020 aAttrs.reserve(rColumn.end() - rColumn.begin());
2022 for (const auto& rStyle : rColumn)
2024 const XclImpXFIndex& rXFIndex = rStyle.maXFIndex;
2025 XclImpXF* pXF = rXFBuffer.GetXF( rXFIndex.GetXFIndex() );
2026 if (!pXF)
2027 continue;
2029 sal_uInt32 nForceScNumFmt = rXFIndex.IsBoolCell() ?
2030 GetNumFmtBuffer().GetStdScNumFmt() : NUMBERFORMAT_ENTRY_NOT_FOUND;
2032 pXF->ApplyPatternToAttrVector(aAttrs, rStyle.mnScRow1, rStyle.mnScRow2, nForceScNumFmt);
2035 if (aAttrs.empty() || aAttrs.back().nEndRow != rDoc.MaxRow())
2037 ScAttrEntry aEntry;
2038 aEntry.nEndRow = rDoc.MaxRow();
2039 aEntry.setScPatternAttr(&rDoc.getCellAttributeHelper().getDefaultCellAttribute());
2040 aAttrs.push_back(aEntry);
2043 aAttrs.shrink_to_fit();
2044 assert(aAttrs.size() > 0);
2045 ScDocumentImport::Attrs aAttrParam;
2046 aAttrParam.mvData.swap(aAttrs);
2047 aAttrParam.mbLatinNumFmtOnly = false; // when unsure, set it to false.
2049 // Compress setting the attributes, set the same set in one call.
2050 if( pendingColStart != -1 && pendingColEnd == nScCol - 1 && aAttrParam == aPendingAttrParam )
2051 ++pendingColEnd;
2052 else
2054 if( pendingColStart != -1 )
2055 rDocImport.setAttrEntries(nScTab, pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
2056 pendingColStart = pendingColEnd = nScCol;
2057 aPendingAttrParam = std::move( aAttrParam );
2060 ++nScCol;
2062 if( pendingColStart != -1 )
2063 rDocImport.setAttrEntries(nScTab, pendingColStart, pendingColEnd, std::move(aPendingAttrParam));
2065 // insert hyperlink cells
2066 for( const auto& [rXclRange, rUrl] : maHyperlinks )
2067 XclImpHyperlink::InsertUrl( GetRoot(), rXclRange, rUrl );
2069 // apply cell merging
2070 for ( size_t i = 0, nRange = maMergeList.size(); i < nRange; ++i )
2072 const ScRange & rRange = maMergeList[ i ];
2073 const ScAddress& rStart = rRange.aStart;
2074 const ScAddress& rEnd = rRange.aEnd;
2075 bool bMultiCol = rStart.Col() != rEnd.Col();
2076 bool bMultiRow = rStart.Row() != rEnd.Row();
2077 // set correct right border
2078 if( bMultiCol )
2079 SetBorderLine( rRange, nScTab, SvxBoxItemLine::RIGHT );
2080 // set correct lower border
2081 if( bMultiRow )
2082 SetBorderLine( rRange, nScTab, SvxBoxItemLine::BOTTOM );
2083 // do merge
2084 if( bMultiCol || bMultiRow )
2085 rDoc.DoMerge( rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row(), nScTab );
2086 // #i93609# merged range in a single row: test if manual row height is needed
2087 if( !bMultiRow )
2089 bool bTextWrap = rDoc.GetAttr( rStart, ATTR_LINEBREAK )->GetValue();
2090 if( !bTextWrap && (rDoc.GetCellType( rStart ) == CELLTYPE_EDIT) )
2091 if (const EditTextObject* pEditObj = rDoc.GetEditText(rStart))
2092 bTextWrap = pEditObj->GetParagraphCount() > 1;
2093 if( bTextWrap )
2094 GetOldRoot().pColRowBuff->SetManualRowHeight( rStart.Row() );
2099 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */