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 <hintids.hxx>
21 
22 #include <com/sun/star/i18n/ScriptType.hpp>
23 #include <vcl/outdev.hxx>
24 #include <editeng/brushitem.hxx>
25 #include <editeng/wrlmitem.hxx>
26 #include <editeng/kernitem.hxx>
27 #include <editeng/cmapitem.hxx>
28 #include <editeng/langitem.hxx>
29 #include <editeng/escapementitem.hxx>
30 #include <editeng/autokernitem.hxx>
31 #include <editeng/shdditem.hxx>
32 #include <editeng/charreliefitem.hxx>
33 #include <editeng/contouritem.hxx>
34 #include <editeng/colritem.hxx>
35 #include <editeng/crossedoutitem.hxx>
36 #include <editeng/udlnitem.hxx>
37 #include <editeng/wghtitem.hxx>
38 #include <editeng/postitem.hxx>
39 #include <editeng/fhgtitem.hxx>
40 #include <editeng/fontitem.hxx>
41 #include <editeng/emphasismarkitem.hxx>
42 #include <editeng/charscaleitem.hxx>
43 #include <editeng/charrotateitem.hxx>
44 #include <editeng/twolinesitem.hxx>
45 #include <editeng/charhiddenitem.hxx>
46 #include <editeng/boxitem.hxx>
47 #include <editeng/shaditem.hxx>
48 #include <IDocumentSettingAccess.hxx>
49 #include <charatr.hxx>
50 #include <viewsh.hxx>
51 #include <swfont.hxx>
52 #include <fntcache.hxx>
53 #include <txtfrm.hxx>
54 #include <scriptinfo.hxx>
55 
56 #ifdef DBG_UTIL
57 // global Variable
58 SvStatistics g_SvStat;
59 #endif
60 
61 using namespace ::com::sun::star;
62 
63 // set background brush, depending on character formatting
SetBackColor(std::optional<Color> xNewColor)64 void SwFont::SetBackColor( std::optional<Color> xNewColor )
65 {
66     mxBackColor = xNewColor;
67     m_bFontChg = true;
68     m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
69 }
70 
SetTopBorder(const editeng::SvxBorderLine * pTopBorder)71 void SwFont::SetTopBorder( const editeng::SvxBorderLine* pTopBorder )
72 {
73     if( pTopBorder )
74         m_aTopBorder = *pTopBorder;
75     else
76     {
77         m_aTopBorder.reset();
78         m_nTopBorderDist = 0;
79     }
80     m_bFontChg = true;
81     m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
82 }
83 
SetBottomBorder(const editeng::SvxBorderLine * pBottomBorder)84 void SwFont::SetBottomBorder( const editeng::SvxBorderLine* pBottomBorder )
85 {
86     if( pBottomBorder )
87         m_aBottomBorder = *pBottomBorder;
88     else
89     {
90         m_aBottomBorder.reset();
91         m_nBottomBorderDist = 0;
92     }
93     m_bFontChg = true;
94     m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
95 }
96 
SetRightBorder(const editeng::SvxBorderLine * pRightBorder)97 void SwFont::SetRightBorder( const editeng::SvxBorderLine* pRightBorder )
98 {
99     if( pRightBorder )
100         m_aRightBorder = *pRightBorder;
101     else
102     {
103         m_aRightBorder.reset();
104         m_nRightBorderDist = 0;
105     }
106     m_bFontChg = true;
107     m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
108 }
109 
SetLeftBorder(const editeng::SvxBorderLine * pLeftBorder)110 void SwFont::SetLeftBorder( const editeng::SvxBorderLine* pLeftBorder )
111 {
112     if( pLeftBorder )
113         m_aLeftBorder = *pLeftBorder;
114     else
115     {
116         m_aLeftBorder.reset();
117         m_nLeftBorderDist = 0;
118     }
119     m_bFontChg = true;
120     m_aSub[SwFontScript::Latin].m_nFontCacheId = m_aSub[SwFontScript::CJK].m_nFontCacheId = m_aSub[SwFontScript::CTL].m_nFontCacheId = nullptr;
121 }
122 
123 const std::optional<editeng::SvxBorderLine>&
GetAbsTopBorder(const bool bVertLayout,const bool bVertLayoutLRBT) const124 SwFont::GetAbsTopBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const
125 {
126     switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
127     {
128         case 0 :
129             return m_aTopBorder;
130         case 900 :
131             return m_aRightBorder;
132         case 1800 :
133             return m_aBottomBorder;
134         case 2700 :
135             return m_aLeftBorder;
136         default :
137             assert(false);
138             return m_aTopBorder;
139     }
140 }
141 
142 const std::optional<editeng::SvxBorderLine>&
GetAbsBottomBorder(const bool bVertLayout,const bool bVertLayoutLRBT) const143 SwFont::GetAbsBottomBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const
144 {
145     switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
146     {
147         case 0 :
148             return m_aBottomBorder;
149         case 900 :
150             return m_aLeftBorder;
151         case 1800 :
152             return m_aTopBorder;
153         case 2700 :
154             return m_aRightBorder;
155         default :
156             assert(false);
157             return m_aBottomBorder;
158     }
159 }
160 
161 const std::optional<editeng::SvxBorderLine>&
GetAbsLeftBorder(const bool bVertLayout,const bool bVertLayoutLRBT) const162 SwFont::GetAbsLeftBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const
163 {
164     switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
165     {
166         case 0 :
167             return m_aLeftBorder;
168         case 900 :
169             return m_aTopBorder;
170         case 1800 :
171             return m_aRightBorder;
172         case 2700 :
173             return m_aBottomBorder;
174         default :
175             assert(false);
176             return m_aLeftBorder;
177     }
178 }
179 
180 const std::optional<editeng::SvxBorderLine>&
GetAbsRightBorder(const bool bVertLayout,const bool bVertLayoutLRBT) const181 SwFont::GetAbsRightBorder(const bool bVertLayout, const bool bVertLayoutLRBT) const
182 {
183     switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
184     {
185         case 0 :
186             return m_aRightBorder;
187         case 900 :
188             return m_aBottomBorder;
189         case 1800 :
190             return m_aLeftBorder;
191         case 2700 :
192             return m_aTopBorder;
193         default :
194             assert(false);
195             return m_aRightBorder;
196     }
197 }
198 
GetAbsShadowLocation(const bool bVertLayout,const bool bVertLayoutLRBT) const199 SvxShadowLocation SwFont::GetAbsShadowLocation(const bool bVertLayout,
200                                                const bool bVertLayoutLRBT) const
201 {
202     SvxShadowLocation aLocation = SvxShadowLocation::NONE;
203     switch (GetOrientation(bVertLayout, bVertLayoutLRBT).get())
204     {
205         case 0:
206             aLocation = m_aShadowLocation;
207             break;
208 
209         case 900:
210             switch ( m_aShadowLocation )
211             {
212                 case SvxShadowLocation::TopLeft:
213                     aLocation = SvxShadowLocation::BottomLeft;
214                     break;
215                 case SvxShadowLocation::TopRight:
216                     aLocation = SvxShadowLocation::TopLeft;
217                     break;
218                 case SvxShadowLocation::BottomLeft:
219                     aLocation = SvxShadowLocation::BottomRight;
220                     break;
221                 case SvxShadowLocation::BottomRight:
222                     aLocation = SvxShadowLocation::TopRight;
223                     break;
224                 case SvxShadowLocation::NONE:
225                 case SvxShadowLocation::End:
226                     aLocation = m_aShadowLocation;
227                     break;
228             }
229             break;
230 
231         case 1800:
232             switch ( m_aShadowLocation )
233             {
234                 case SvxShadowLocation::TopLeft:
235                     aLocation = SvxShadowLocation::BottomRight;
236                     break;
237                 case SvxShadowLocation::TopRight:
238                     aLocation = SvxShadowLocation::BottomLeft;
239                     break;
240                 case SvxShadowLocation::BottomLeft:
241                     aLocation = SvxShadowLocation::TopRight;
242                     break;
243                 case SvxShadowLocation::BottomRight:
244                     aLocation = SvxShadowLocation::TopLeft;
245                     break;
246                 case SvxShadowLocation::NONE:
247                 case SvxShadowLocation::End:
248                     aLocation = m_aShadowLocation;
249                     break;
250             }
251             break;
252 
253         case 2700:
254             switch ( m_aShadowLocation )
255             {
256                 case SvxShadowLocation::TopLeft:
257                     aLocation = SvxShadowLocation::TopRight;
258                     break;
259                 case SvxShadowLocation::TopRight:
260                     aLocation = SvxShadowLocation::BottomRight;
261                     break;
262                 case SvxShadowLocation::BottomLeft:
263                     aLocation = SvxShadowLocation::TopLeft;
264                     break;
265                 case SvxShadowLocation::BottomRight:
266                     aLocation = SvxShadowLocation::BottomLeft;
267                     break;
268                 case SvxShadowLocation::NONE:
269                 case SvxShadowLocation::End:
270                     aLocation = m_aShadowLocation;
271                     break;
272             }
273             break;
274 
275         default:
276             assert(false);
277             break;
278     }
279     return aLocation;
280 }
281 
CalcShadowSpace(const SvxShadowItemSide nShadow,const bool bVertLayout,const bool bVertLayoutLRBT,const bool bSkipLeft,const bool bSkipRight) const282 sal_uInt16 SwFont::CalcShadowSpace(const SvxShadowItemSide nShadow, const bool bVertLayout,
283                                    const bool bVertLayoutLRBT, const bool bSkipLeft,
284                                    const bool bSkipRight) const
285 {
286     sal_uInt16 nSpace = 0;
287     const Degree10 nOrient = GetOrientation(bVertLayout, bVertLayoutLRBT);
288     const SvxShadowLocation aLoc = GetAbsShadowLocation(bVertLayout, bVertLayoutLRBT);
289     switch( nShadow )
290     {
291         case SvxShadowItemSide::TOP:
292             if(( aLoc == SvxShadowLocation::TopLeft ||
293                aLoc == SvxShadowLocation::TopRight ) &&
294                ( nOrient == 0_deg10 || nOrient == 1800_deg10 ||
295                ( nOrient == 900_deg10 && !bSkipRight ) ||
296                ( nOrient == 2700_deg10 && !bSkipLeft )))
297             {
298                 nSpace = m_nShadowWidth;
299             }
300             break;
301 
302         case SvxShadowItemSide::BOTTOM:
303             if(( aLoc == SvxShadowLocation::BottomLeft ||
304                aLoc == SvxShadowLocation::BottomRight ) &&
305                ( nOrient == 0_deg10 || nOrient == 1800_deg10 ||
306                ( nOrient == 900_deg10 && !bSkipLeft ) ||
307                ( nOrient == 2700_deg10 && !bSkipRight )))
308             {
309                 nSpace = m_nShadowWidth;
310             }
311             break;
312 
313         case SvxShadowItemSide::LEFT:
314             if(( aLoc == SvxShadowLocation::TopLeft ||
315                aLoc == SvxShadowLocation::BottomLeft ) &&
316                ( nOrient == 900_deg10 || nOrient == 2700_deg10 ||
317                ( nOrient == 0_deg10 && !bSkipLeft ) ||
318                ( nOrient == 1800_deg10 && !bSkipRight )))
319             {
320                 nSpace = m_nShadowWidth;
321             }
322             break;
323 
324          case SvxShadowItemSide::RIGHT:
325             if(( aLoc == SvxShadowLocation::TopRight ||
326                aLoc == SvxShadowLocation::BottomRight ) &&
327                ( nOrient == 900_deg10 || nOrient == 2700_deg10 ||
328                ( nOrient == 0_deg10 && !bSkipRight ) ||
329                ( nOrient == 1800_deg10 && !bSkipLeft )))
330             {
331                 nSpace = m_nShadowWidth;
332             }
333             break;
334         default:
335             assert(false);
336             break;
337     }
338 
339     return nSpace;
340 }
341 
342 // maps directions for vertical layout
MapDirection(Degree10 nDir,const bool bVertFormat,const bool bVertFormatLRBT)343 static Degree10 MapDirection(Degree10 nDir, const bool bVertFormat, const bool bVertFormatLRBT)
344 {
345     if ( bVertFormat )
346     {
347         switch ( nDir.get() )
348         {
349         case 0 :
350             if (bVertFormatLRBT)
351                 nDir = 900_deg10;
352             else
353                 nDir = 2700_deg10;
354             break;
355         case 900 :
356             nDir = 0_deg10;
357             break;
358         case 2700 :
359             nDir = 1800_deg10;
360             break;
361 #if OSL_DEBUG_LEVEL > 0
362         default :
363             OSL_FAIL( "Unsupported direction" );
364             break;
365 #endif
366         }
367     }
368     return nDir;
369 }
370 
371 // maps the absolute direction set at the font to its logical counterpart
372 // in the rotated environment
UnMapDirection(Degree10 nDir,const bool bVertFormat,const bool bVertFormatLRBT)373 Degree10 UnMapDirection(Degree10 nDir, const bool bVertFormat, const bool bVertFormatLRBT)
374 {
375     if (bVertFormatLRBT)
376     {
377         switch (nDir.get())
378         {
379             case 900:
380                 nDir = 0_deg10;
381                 break;
382             default:
383                 SAL_WARN("sw.core", "unsupported direction for VertLRBT");
384                 break;
385         }
386         return nDir;
387     }
388 
389     if ( bVertFormat )
390     {
391         switch ( nDir.get() )
392         {
393         case 0 :
394             nDir = 900_deg10;
395             break;
396         case 1800 :
397             nDir = 2700_deg10;
398             break;
399         case 2700 :
400             nDir = 0_deg10;
401             break;
402 #if OSL_DEBUG_LEVEL > 0
403         default :
404             OSL_FAIL( "Unsupported direction" );
405             break;
406 #endif
407         }
408     }
409     return nDir;
410 }
411 
GetOrientation(const bool bVertFormat,const bool bVertFormatLRBT) const412 Degree10 SwFont::GetOrientation(const bool bVertFormat, const bool bVertFormatLRBT) const
413 {
414     return UnMapDirection(m_aSub[m_nActual].GetOrientation(), bVertFormat, bVertFormatLRBT);
415 }
416 
SetVertical(Degree10 nDir,const bool bVertFormat,const bool bVertLayoutLRBT)417 void SwFont::SetVertical(Degree10 nDir, const bool bVertFormat, const bool bVertLayoutLRBT)
418 {
419     // map direction if frame has vertical layout
420     nDir = MapDirection(nDir, bVertFormat, bVertLayoutLRBT);
421 
422     if( nDir != m_aSub[SwFontScript::Latin].GetOrientation() )
423     {
424         m_bFontChg = true;
425         bool bVertical = bVertFormat && !bVertLayoutLRBT;
426         m_aSub[SwFontScript::Latin].SetVertical(nDir, bVertical);
427         m_aSub[SwFontScript::CJK].SetVertical(nDir, bVertical);
428         m_aSub[SwFontScript::CTL].SetVertical(nDir, bVertical);
429     }
430 }
431 
432 /*
433  Escapement:
434     frEsc:  Fraction, ratio of Escapements
435     Esc = resulting Escapement
436     A1 = original Ascent            (nOrgAscent)
437     A2 = shrunk Ascent              (nEscAscent)
438     Ax = resulting Ascent           (GetAscent())
439     H1 = original Height            (nOrgHeight)
440     H2 = shrunk Height              (nEscHeight)
441     Hx = resulting Height           (GetHeight())
442     Bx = resulting Baseline for Text (CalcPos())
443          (Attention: Y - A1!)
444 
445     Escapement:
446         Esc = H1 * frEsc;
447 
448     Superscript:
449         Ax = A2 + Esc;
450         Hx = H2 + Esc;
451         Bx = A1 - Esc;
452 
453     Subscript:
454         Ax = A1;
455         Hx = A1 + Esc + (H2 - A2);
456         Bx = A1 + Esc;
457 */
458 
459 // nEsc is the percentage
CalcEscAscent(const sal_uInt16 nOldAscent) const460 sal_uInt16 SwSubFont::CalcEscAscent( const sal_uInt16 nOldAscent ) const
461 {
462     if( DFLT_ESC_AUTO_SUPER != GetEscapement() &&
463         DFLT_ESC_AUTO_SUB != GetEscapement() )
464     {
465         const tools::Long nAscent = nOldAscent +
466                              ( static_cast<tools::Long>(m_nOrgHeight) * GetEscapement() ) / 100;
467         if ( nAscent>0 )
468             return std::max<sal_uInt16>( nAscent, m_nOrgAscent );
469     }
470     return m_nOrgAscent;
471 }
472 
SetDiffFnt(const SfxItemSet * pAttrSet,const IDocumentSettingAccess * pIDocumentSettingAccess)473 void SwFont::SetDiffFnt( const SfxItemSet *pAttrSet,
474                          const IDocumentSettingAccess *pIDocumentSettingAccess )
475 {
476     mxBackColor.reset();
477 
478     if( pAttrSet )
479     {
480         const SfxPoolItem* pItem;
481         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_FONT,
482             true, &pItem ))
483         {
484             const SvxFontItem *pFont = static_cast<const SvxFontItem *>(pItem);
485             m_aSub[SwFontScript::Latin].SetFamily( pFont->GetFamily() );
486             m_aSub[SwFontScript::Latin].Font::SetFamilyName( pFont->GetFamilyName() );
487             m_aSub[SwFontScript::Latin].Font::SetStyleName( pFont->GetStyleName() );
488             m_aSub[SwFontScript::Latin].Font::SetPitch( pFont->GetPitch() );
489             m_aSub[SwFontScript::Latin].Font::SetCharSet( pFont->GetCharSet() );
490         }
491         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_FONTSIZE,
492             true, &pItem ))
493         {
494             const SvxFontHeightItem *pHeight = static_cast<const SvxFontHeightItem *>(pItem);
495             m_aSub[SwFontScript::Latin].SvxFont::SetPropr( 100 );
496             m_aSub[SwFontScript::Latin].m_aSize = m_aSub[SwFontScript::Latin].Font::GetFontSize();
497             Size aTmpSize = m_aSub[SwFontScript::Latin].m_aSize;
498             aTmpSize.setHeight( pHeight->GetHeight() );
499             m_aSub[SwFontScript::Latin].SetSize( aTmpSize );
500         }
501         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_POSTURE,
502             true, &pItem ))
503             m_aSub[SwFontScript::Latin].Font::SetItalic( static_cast<const SvxPostureItem*>(pItem)->GetPosture() );
504         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_WEIGHT,
505             true, &pItem ))
506             m_aSub[SwFontScript::Latin].Font::SetWeight( static_cast<const SvxWeightItem*>(pItem)->GetWeight() );
507         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_LANGUAGE,
508             true, &pItem ))
509             m_aSub[SwFontScript::Latin].SetLanguage( static_cast<const SvxLanguageItem*>(pItem)->GetLanguage() );
510 
511         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CJK_FONT,
512             true, &pItem ))
513         {
514             const SvxFontItem *pFont = static_cast<const SvxFontItem *>(pItem);
515             m_aSub[SwFontScript::CJK].SetFamily( pFont->GetFamily() );
516             m_aSub[SwFontScript::CJK].Font::SetFamilyName( pFont->GetFamilyName() );
517             m_aSub[SwFontScript::CJK].Font::SetStyleName( pFont->GetStyleName() );
518             m_aSub[SwFontScript::CJK].Font::SetPitch( pFont->GetPitch() );
519             m_aSub[SwFontScript::CJK].Font::SetCharSet( pFont->GetCharSet() );
520         }
521         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CJK_FONTSIZE,
522             true, &pItem ))
523         {
524             const SvxFontHeightItem *pHeight = static_cast<const SvxFontHeightItem *>(pItem);
525             m_aSub[SwFontScript::CJK].SvxFont::SetPropr( 100 );
526             m_aSub[SwFontScript::CJK].m_aSize = m_aSub[SwFontScript::CJK].Font::GetFontSize();
527             Size aTmpSize = m_aSub[SwFontScript::CJK].m_aSize;
528             aTmpSize.setHeight( pHeight->GetHeight() );
529             m_aSub[SwFontScript::CJK].SetSize( aTmpSize );
530         }
531         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CJK_POSTURE,
532             true, &pItem ))
533             m_aSub[SwFontScript::CJK].Font::SetItalic( static_cast<const SvxPostureItem*>(pItem)->GetPosture() );
534         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CJK_WEIGHT,
535             true, &pItem ))
536             m_aSub[SwFontScript::CJK].Font::SetWeight( static_cast<const SvxWeightItem*>(pItem)->GetWeight() );
537         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CJK_LANGUAGE,
538             true, &pItem ))
539         {
540             LanguageType eNewLang = static_cast<const SvxLanguageItem*>(pItem)->GetLanguage();
541             m_aSub[SwFontScript::CJK].SetLanguage( eNewLang );
542             m_aSub[SwFontScript::Latin].SetCJKContextLanguage( eNewLang );
543             m_aSub[SwFontScript::CJK].SetCJKContextLanguage( eNewLang );
544             m_aSub[SwFontScript::CTL].SetCJKContextLanguage( eNewLang );
545         }
546 
547         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CTL_FONT,
548             true, &pItem ))
549         {
550             const SvxFontItem *pFont = static_cast<const SvxFontItem *>(pItem);
551             m_aSub[SwFontScript::CTL].SetFamily( pFont->GetFamily() );
552             m_aSub[SwFontScript::CTL].Font::SetFamilyName( pFont->GetFamilyName() );
553             m_aSub[SwFontScript::CTL].Font::SetStyleName( pFont->GetStyleName() );
554             m_aSub[SwFontScript::CTL].Font::SetPitch( pFont->GetPitch() );
555             m_aSub[SwFontScript::CTL].Font::SetCharSet( pFont->GetCharSet() );
556         }
557         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CTL_FONTSIZE,
558             true, &pItem ))
559         {
560             const SvxFontHeightItem *pHeight = static_cast<const SvxFontHeightItem *>(pItem);
561             m_aSub[SwFontScript::CTL].SvxFont::SetPropr( 100 );
562             m_aSub[SwFontScript::CTL].m_aSize = m_aSub[SwFontScript::CTL].Font::GetFontSize();
563             Size aTmpSize = m_aSub[SwFontScript::CTL].m_aSize;
564             aTmpSize.setHeight( pHeight->GetHeight() );
565             m_aSub[SwFontScript::CTL].SetSize( aTmpSize );
566         }
567         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CTL_POSTURE,
568             true, &pItem ))
569             m_aSub[SwFontScript::CTL].Font::SetItalic( static_cast<const SvxPostureItem*>(pItem)->GetPosture() );
570         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CTL_WEIGHT,
571             true, &pItem ))
572             m_aSub[SwFontScript::CTL].Font::SetWeight( static_cast<const SvxWeightItem*>(pItem)->GetWeight() );
573         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CTL_LANGUAGE,
574             true, &pItem ))
575             m_aSub[SwFontScript::CTL].SetLanguage( static_cast<const SvxLanguageItem*>(pItem)->GetLanguage() );
576 
577         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_UNDERLINE,
578             true, &pItem ))
579         {
580             SetUnderline( static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle() );
581             SetUnderColor( static_cast<const SvxUnderlineItem*>(pItem)->GetColor() );
582         }
583         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_OVERLINE,
584             true, &pItem ))
585         {
586             SetOverline( static_cast<const SvxOverlineItem*>(pItem)->GetLineStyle() );
587             SetOverColor( static_cast<const SvxOverlineItem*>(pItem)->GetColor() );
588         }
589         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CROSSEDOUT,
590             true, &pItem ))
591             SetStrikeout( static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout() );
592         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_COLOR,
593             true, &pItem ))
594             SetColor( static_cast<const SvxColorItem*>(pItem)->GetValue() );
595         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_EMPHASIS_MARK,
596             true, &pItem ))
597             SetEmphasisMark( static_cast<const SvxEmphasisMarkItem*>(pItem)->GetEmphasisMark() );
598 
599         SetTransparent( true );
600         SetAlign( ALIGN_BASELINE );
601         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CONTOUR,
602             true, &pItem ))
603             SetOutline( static_cast<const SvxContourItem*>(pItem)->GetValue() );
604         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_SHADOWED,
605             true, &pItem ))
606             SetShadow( static_cast<const SvxShadowedItem*>(pItem)->GetValue() );
607         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_RELIEF,
608             true, &pItem ))
609             SetRelief( static_cast<const SvxCharReliefItem*>(pItem)->GetValue() );
610         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_SHADOWED,
611             true, &pItem ))
612             SetPropWidth(static_cast<const SvxShadowedItem*>(pItem)->GetValue() ? 50 : 100 );
613         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_AUTOKERN,
614             true, &pItem ))
615         {
616             if( static_cast<const SvxAutoKernItem*>(pItem)->GetValue() )
617             {
618                 SetAutoKern( ( !pIDocumentSettingAccess ||
619                                !pIDocumentSettingAccess->get(DocumentSettingId::KERN_ASIAN_PUNCTUATION) ) ?
620                                 FontKerning::FontSpecific :
621                                 FontKerning::Asian );
622             }
623             else
624                 SetAutoKern( FontKerning::NONE );
625         }
626         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_WORDLINEMODE,
627             true, &pItem ))
628             SetWordLineMode( static_cast<const SvxWordLineModeItem*>(pItem)->GetValue() );
629 
630         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_ESCAPEMENT,
631             true, &pItem ))
632         {
633             const SvxEscapementItem *pEsc = static_cast<const SvxEscapementItem *>(pItem);
634             SetEscapement( pEsc->GetEsc() );
635             if( m_aSub[SwFontScript::Latin].IsEsc() )
636                 SetProportion( pEsc->GetProportionalHeight() );
637         }
638         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_CASEMAP,
639             true, &pItem ))
640             SetCaseMap( static_cast<const SvxCaseMapItem*>(pItem)->GetCaseMap() );
641         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_KERNING,
642             true, &pItem ))
643             SetFixKerning( static_cast<const SvxKerningItem*>(pItem)->GetValue() );
644         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_ROTATE,
645             true, &pItem ))
646             SetVertical( static_cast<const SvxCharRotateItem*>(pItem)->GetValue() );
647         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_BACKGROUND,
648             true, &pItem ))
649             mxBackColor = static_cast<const SvxBrushItem*>(pItem)->GetColor();
650         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_HIGHLIGHT,
651             true, &pItem ))
652             SetHighlightColor(static_cast<const SvxBrushItem*>(pItem)->GetColor());
653         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_BOX,
654             true, &pItem ))
655         {
656             const SvxBoxItem* pBoxItem = static_cast<const SvxBoxItem*>(pItem);
657             SetTopBorder(pBoxItem->GetTop());
658             SetBottomBorder(pBoxItem->GetBottom());
659             SetRightBorder(pBoxItem->GetRight());
660             SetLeftBorder(pBoxItem->GetLeft());
661             SetTopBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::TOP));
662             SetBottomBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::BOTTOM));
663             SetRightBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::RIGHT));
664             SetLeftBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::LEFT));
665         }
666         if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_SHADOW,
667             true, &pItem ))
668         {
669             const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem);
670             SetShadowColor(pShadowItem->GetColor());
671             SetShadowWidth(pShadowItem->GetWidth());
672             SetShadowLocation(pShadowItem->GetLocation());
673         }
674         const SfxPoolItem* pTwoLinesItem = nullptr;
675         if( SfxItemState::SET ==
676                 pAttrSet->GetItemState( RES_CHRATR_TWO_LINES, true, &pTwoLinesItem ))
677             if ( static_cast<const SvxTwoLinesItem*>(pTwoLinesItem)->GetValue() )
678                 SetVertical( 0_deg10 );
679     }
680     else
681     {
682         Invalidate();
683     }
684     m_bPaintBlank = false;
685     OSL_ENSURE( m_aSub[SwFontScript::Latin].IsTransparent(), "SwFont: Transparent revolution" );
686 }
687 
SwFont(const SwFont & rFont)688 SwFont::SwFont( const SwFont &rFont )
689     : m_aSub(rFont.m_aSub)
690 {
691     m_nActual = rFont.m_nActual;
692     mxBackColor = rFont.mxBackColor;
693     m_aHighlightColor = rFont.m_aHighlightColor;
694     m_aTopBorder = rFont.m_aTopBorder;
695     m_aBottomBorder = rFont.m_aBottomBorder;
696     m_aRightBorder = rFont.m_aRightBorder;
697     m_aLeftBorder = rFont.m_aLeftBorder;
698     m_nTopBorderDist = rFont.m_nTopBorderDist;
699     m_nBottomBorderDist = rFont.m_nBottomBorderDist;
700     m_nRightBorderDist = rFont.m_nRightBorderDist;
701     m_nLeftBorderDist = rFont.m_nLeftBorderDist;
702     m_aShadowColor = rFont.m_aShadowColor;
703     m_nShadowWidth = rFont.m_nShadowWidth;
704     m_aShadowLocation = rFont.m_aShadowLocation;
705     m_aUnderColor = rFont.GetUnderColor();
706     m_aOverColor  = rFont.GetOverColor();
707     m_nToxCount = 0;
708     m_nRefCount = 0;
709     m_nMetaCount = 0;
710     m_nInputFieldCount = 0;
711     m_bFontChg = rFont.m_bFontChg;
712     m_bOrgChg = rFont.m_bOrgChg;
713     m_bPaintBlank = rFont.m_bPaintBlank;
714     m_bGreyWave = rFont.m_bGreyWave;
715 }
716 
SwFont(const SwAttrSet * pAttrSet,const IDocumentSettingAccess * pIDocumentSettingAccess)717 SwFont::SwFont( const SwAttrSet* pAttrSet,
718                 const IDocumentSettingAccess* pIDocumentSettingAccess )
719     : m_aSub()
720 {
721     m_nActual = SwFontScript::Latin;
722     m_nToxCount = 0;
723     m_nRefCount = 0;
724     m_nMetaCount = 0;
725     m_nInputFieldCount = 0;
726     m_bPaintBlank = false;
727     m_bGreyWave = false;
728     m_bOrgChg = true;
729     {
730         const SvxFontItem& rFont = pAttrSet->GetFont();
731         m_aSub[SwFontScript::Latin].SetFamily( rFont.GetFamily() );
732         m_aSub[SwFontScript::Latin].SetFamilyName( rFont.GetFamilyName() );
733         m_aSub[SwFontScript::Latin].SetStyleName( rFont.GetStyleName() );
734         m_aSub[SwFontScript::Latin].SetPitch( rFont.GetPitch() );
735         m_aSub[SwFontScript::Latin].SetCharSet( rFont.GetCharSet() );
736         m_aSub[SwFontScript::Latin].SvxFont::SetPropr( 100 ); // 100% of FontSize
737         Size aTmpSize = m_aSub[SwFontScript::Latin].m_aSize;
738         aTmpSize.setHeight( pAttrSet->GetSize().GetHeight() );
739         m_aSub[SwFontScript::Latin].SetSize( aTmpSize );
740         m_aSub[SwFontScript::Latin].SetItalic( pAttrSet->GetPosture().GetPosture() );
741         m_aSub[SwFontScript::Latin].SetWeight( pAttrSet->GetWeight().GetWeight() );
742         m_aSub[SwFontScript::Latin].SetLanguage( pAttrSet->GetLanguage().GetLanguage() );
743     }
744 
745     {
746         const SvxFontItem& rFont = pAttrSet->GetCJKFont();
747         m_aSub[SwFontScript::CJK].SetFamily( rFont.GetFamily() );
748         m_aSub[SwFontScript::CJK].SetFamilyName( rFont.GetFamilyName() );
749         m_aSub[SwFontScript::CJK].SetStyleName( rFont.GetStyleName() );
750         m_aSub[SwFontScript::CJK].SetPitch( rFont.GetPitch() );
751         m_aSub[SwFontScript::CJK].SetCharSet( rFont.GetCharSet() );
752         m_aSub[SwFontScript::CJK].SvxFont::SetPropr( 100 ); // 100% of FontSize
753         Size aTmpSize = m_aSub[SwFontScript::CJK].m_aSize;
754         aTmpSize.setHeight( pAttrSet->GetCJKSize().GetHeight() );
755         m_aSub[SwFontScript::CJK].SetSize( aTmpSize );
756         m_aSub[SwFontScript::CJK].SetItalic( pAttrSet->GetCJKPosture().GetPosture() );
757         m_aSub[SwFontScript::CJK].SetWeight( pAttrSet->GetCJKWeight().GetWeight() );
758         LanguageType eNewLang = pAttrSet->GetCJKLanguage().GetLanguage();
759         m_aSub[SwFontScript::CJK].SetLanguage( eNewLang );
760         m_aSub[SwFontScript::Latin].SetCJKContextLanguage( eNewLang );
761         m_aSub[SwFontScript::CJK].SetCJKContextLanguage( eNewLang );
762         m_aSub[SwFontScript::CTL].SetCJKContextLanguage( eNewLang );
763     }
764 
765     {
766         const SvxFontItem& rFont = pAttrSet->GetCTLFont();
767         m_aSub[SwFontScript::CTL].SetFamily( rFont.GetFamily() );
768         m_aSub[SwFontScript::CTL].SetFamilyName( rFont.GetFamilyName() );
769         m_aSub[SwFontScript::CTL].SetStyleName( rFont.GetStyleName() );
770         m_aSub[SwFontScript::CTL].SetPitch( rFont.GetPitch() );
771         m_aSub[SwFontScript::CTL].SetCharSet( rFont.GetCharSet() );
772         m_aSub[SwFontScript::CTL].SvxFont::SetPropr( 100 ); // 100% of FontSize
773         Size aTmpSize = m_aSub[SwFontScript::CTL].m_aSize;
774         aTmpSize.setHeight( pAttrSet->GetCTLSize().GetHeight() );
775         m_aSub[SwFontScript::CTL].SetSize( aTmpSize );
776         m_aSub[SwFontScript::CTL].SetItalic( pAttrSet->GetCTLPosture().GetPosture() );
777         m_aSub[SwFontScript::CTL].SetWeight( pAttrSet->GetCTLWeight().GetWeight() );
778         m_aSub[SwFontScript::CTL].SetLanguage( pAttrSet->GetCTLLanguage().GetLanguage() );
779     }
780     if ( pAttrSet->GetCharHidden().GetValue() )
781         SetUnderline( LINESTYLE_DOTTED );
782     else
783         SetUnderline( pAttrSet->GetUnderline().GetLineStyle() );
784     SetUnderColor( pAttrSet->GetUnderline().GetColor() );
785     SetOverline( pAttrSet->GetOverline().GetLineStyle() );
786     SetOverColor( pAttrSet->GetOverline().GetColor() );
787     SetEmphasisMark( pAttrSet->GetEmphasisMark().GetEmphasisMark() );
788     SetStrikeout( pAttrSet->GetCrossedOut().GetStrikeout() );
789     SetColor( pAttrSet->GetColor().GetValue() );
790     SetTransparent( true );
791     SetAlign( ALIGN_BASELINE );
792     SetOutline( pAttrSet->GetContour().GetValue() );
793     SetShadow( pAttrSet->GetShadowed().GetValue() );
794     SetPropWidth( pAttrSet->GetCharScaleW().GetValue() );
795     SetRelief( pAttrSet->GetCharRelief().GetValue() );
796     if( pAttrSet->GetAutoKern().GetValue() )
797     {
798         SetAutoKern( ( !pIDocumentSettingAccess ||
799                        !pIDocumentSettingAccess->get(DocumentSettingId::KERN_ASIAN_PUNCTUATION) ) ?
800                         FontKerning::FontSpecific :
801                         FontKerning::Asian );
802     }
803     else
804         SetAutoKern( FontKerning::NONE );
805     SetWordLineMode( pAttrSet->GetWordLineMode().GetValue() );
806     const SvxEscapementItem &rEsc = pAttrSet->GetEscapement();
807     SetEscapement( rEsc.GetEsc() );
808     if( m_aSub[SwFontScript::Latin].IsEsc() )
809         SetProportion( rEsc.GetProportionalHeight() );
810     SetCaseMap( pAttrSet->GetCaseMap().GetCaseMap() );
811     SetFixKerning( pAttrSet->GetKerning().GetValue() );
812     const SfxPoolItem* pItem;
813     if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_BACKGROUND,
814         true, &pItem ))
815         mxBackColor = static_cast<const SvxBrushItem*>(pItem)->GetColor();
816     if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_HIGHLIGHT,
817         true, &pItem ))
818         SetHighlightColor(static_cast<const SvxBrushItem*>(pItem)->GetColor());
819     else
820         SetHighlightColor(COL_TRANSPARENT);
821     if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_BOX,
822         true, &pItem ))
823     {
824         const SvxBoxItem* pBoxItem = static_cast<const SvxBoxItem*>(pItem);
825         SetTopBorder(pBoxItem->GetTop());
826         SetBottomBorder(pBoxItem->GetBottom());
827         SetRightBorder(pBoxItem->GetRight());
828         SetLeftBorder(pBoxItem->GetLeft());
829         SetTopBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::TOP));
830         SetBottomBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::BOTTOM));
831         SetRightBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::RIGHT));
832         SetLeftBorderDist(pBoxItem->GetDistance(SvxBoxItemLine::LEFT));
833     }
834     else
835     {
836         SetTopBorder(nullptr);
837         SetBottomBorder(nullptr);
838         SetRightBorder(nullptr);
839         SetLeftBorder(nullptr);
840         SetTopBorderDist(0);
841         SetBottomBorderDist(0);
842         SetRightBorderDist(0);
843         SetLeftBorderDist(0);
844     }
845 
846     if( SfxItemState::SET == pAttrSet->GetItemState( RES_CHRATR_SHADOW,
847         true, &pItem ))
848     {
849         const SvxShadowItem* pShadowItem = static_cast<const SvxShadowItem*>(pItem);
850         SetShadowColor(pShadowItem->GetColor());
851         SetShadowWidth(pShadowItem->GetWidth());
852         SetShadowLocation(pShadowItem->GetLocation());
853     }
854     else
855     {
856         SetShadowColor(COL_TRANSPARENT);
857         SetShadowWidth(0);
858         SetShadowLocation(SvxShadowLocation::NONE);
859     }
860 
861     const SvxTwoLinesItem& rTwoLinesItem = pAttrSet->Get2Lines();
862     if ( ! rTwoLinesItem.GetValue() )
863         SetVertical( pAttrSet->GetCharRotate().GetValue() );
864     else
865         SetVertical( 0_deg10 );
866     if( pIDocumentSettingAccess && pIDocumentSettingAccess->get( DocumentSettingId::SMALL_CAPS_PERCENTAGE_66 ))
867     {
868         m_aSub[ SwFontScript::Latin ].m_bSmallCapsPercentage66 = true;
869         m_aSub[ SwFontScript::CJK ].m_bSmallCapsPercentage66 = true;
870         m_aSub[ SwFontScript::CTL ].m_bSmallCapsPercentage66 = true;
871     }
872 }
873 
~SwFont()874 SwFont::~SwFont()
875 {
876 }
877 
operator =(const SwFont & rFont)878 SwFont& SwFont::operator=( const SwFont &rFont )
879 {
880     if (this != &rFont)
881     {
882         m_aSub[SwFontScript::Latin] = rFont.m_aSub[SwFontScript::Latin];
883         m_aSub[SwFontScript::CJK] = rFont.m_aSub[SwFontScript::CJK];
884         m_aSub[SwFontScript::CTL] = rFont.m_aSub[SwFontScript::CTL];
885         m_nActual = rFont.m_nActual;
886         mxBackColor = rFont.mxBackColor;
887         m_aHighlightColor = rFont.m_aHighlightColor;
888         m_aTopBorder = rFont.m_aTopBorder;
889         m_aBottomBorder = rFont.m_aBottomBorder;
890         m_aRightBorder = rFont.m_aRightBorder;
891         m_aLeftBorder = rFont.m_aLeftBorder;
892         m_nTopBorderDist = rFont.m_nTopBorderDist;
893         m_nBottomBorderDist = rFont.m_nBottomBorderDist;
894         m_nRightBorderDist = rFont.m_nRightBorderDist;
895         m_nLeftBorderDist = rFont.m_nLeftBorderDist;
896         m_aShadowColor = rFont.m_aShadowColor;
897         m_nShadowWidth = rFont.m_nShadowWidth;
898         m_aShadowLocation = rFont.m_aShadowLocation;
899         m_aUnderColor = rFont.GetUnderColor();
900         m_aOverColor  = rFont.GetOverColor();
901         m_nToxCount = 0;
902         m_nRefCount = 0;
903         m_nMetaCount = 0;
904         m_nInputFieldCount = 0;
905         m_bFontChg = rFont.m_bFontChg;
906         m_bOrgChg = rFont.m_bOrgChg;
907         m_bPaintBlank = rFont.m_bPaintBlank;
908         m_bGreyWave = rFont.m_bGreyWave;
909     }
910     return *this;
911 }
912 
AllocFontCacheId(SwViewShell const * pSh,SwFontScript nWhich)913 void SwFont::AllocFontCacheId( SwViewShell const *pSh, SwFontScript nWhich )
914 {
915     SwFntAccess aFntAccess( m_aSub[nWhich].m_nFontCacheId, m_aSub[nWhich].m_nFontIndex,
916                             &m_aSub[nWhich], pSh, true );
917 }
918 
IsSymbol(SwViewShell const * pSh)919 bool SwSubFont::IsSymbol( SwViewShell const *pSh )
920 {
921     SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh, false );
922     return aFntAccess.Get()->IsSymbol();
923 }
924 
ChgFnt(SwViewShell const * pSh,OutputDevice & rOut)925 bool SwSubFont::ChgFnt( SwViewShell const *pSh, OutputDevice& rOut )
926 {
927     if ( pLastFont )
928         pLastFont->Unlock();
929     SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh, true );
930     SV_STAT( nChangeFont );
931 
932     pLastFont = aFntAccess.Get();
933 
934     pLastFont->SetDevFont( pSh, rOut );
935 
936     pLastFont->Lock();
937     return LINESTYLE_NONE != GetUnderline() ||
938            LINESTYLE_NONE != GetOverline()  ||
939            STRIKEOUT_NONE != GetStrikeout();
940 }
941 
ChgPhysFnt(SwViewShell const * pSh,OutputDevice & rOut)942 void SwFont::ChgPhysFnt( SwViewShell const *pSh, OutputDevice& rOut )
943 {
944     if( m_bOrgChg && m_aSub[m_nActual].IsEsc() )
945     {
946         const sal_uInt8 nOldProp = m_aSub[m_nActual].GetPropr();
947         SetProportion( 100 );
948         ChgFnt( pSh, rOut );
949         SwFntAccess aFntAccess( m_aSub[m_nActual].m_nFontCacheId, m_aSub[m_nActual].m_nFontIndex,
950                                 &m_aSub[m_nActual], pSh );
951         m_aSub[m_nActual].m_nOrgHeight = aFntAccess.Get()->GetFontHeight( pSh, rOut );
952         m_aSub[m_nActual].m_nOrgAscent = aFntAccess.Get()->GetFontAscent( pSh, rOut );
953         SetProportion( nOldProp );
954         m_bOrgChg = false;
955     }
956 
957     if( m_bFontChg )
958     {
959         ChgFnt( pSh, rOut );
960         m_bFontChg = m_bOrgChg;
961     }
962     if( rOut.GetTextLineColor() != m_aUnderColor )
963         rOut.SetTextLineColor( m_aUnderColor );
964     if( rOut.GetOverlineColor() != m_aOverColor )
965         rOut.SetOverlineColor( m_aOverColor );
966 }
967 
968 //         Height = MaxAscent + MaxDescent
969 //      MaxAscent = Max (T1_ascent, T2_ascent + (Esc * T1_height) );
970 //     MaxDescent = Max (T1_height-T1_ascent,
971 //                       T2_height-T2_ascent - (Esc * T1_height)
CalcEscHeight(const sal_uInt16 nOldHeight,const sal_uInt16 nOldAscent) const972 sal_uInt16 SwSubFont::CalcEscHeight( const sal_uInt16 nOldHeight,
973                               const sal_uInt16 nOldAscent  ) const
974 {
975     if( DFLT_ESC_AUTO_SUPER != GetEscapement() &&
976         DFLT_ESC_AUTO_SUB != GetEscapement() )
977     {
978         tools::Long nDescent = nOldHeight - nOldAscent -
979                              ( static_cast<tools::Long>(m_nOrgHeight) * GetEscapement() ) / 100;
980         const sal_uInt16 nDesc = nDescent>0
981                 ? std::max<sal_uInt16>( nDescent, m_nOrgHeight - m_nOrgAscent)
982                 : m_nOrgHeight - m_nOrgAscent;
983         return ( nDesc + CalcEscAscent( nOldAscent ) );
984     }
985     return m_nOrgHeight;
986 }
987 
CheckKerning_()988 short SwSubFont::CheckKerning_( )
989 {
990     short nKernx = - short( Font::GetFontSize().Height() / 6 );
991 
992     if ( nKernx < GetFixKerning() )
993         return GetFixKerning();
994     return nKernx;
995 }
996 
GetAscent(SwViewShell const * pSh,const OutputDevice & rOut)997 sal_uInt16 SwSubFont::GetAscent( SwViewShell const *pSh, const OutputDevice& rOut )
998 {
999     SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh );
1000     const sal_uInt16 nAscent = aFntAccess.Get()->GetFontAscent( pSh, rOut );
1001     return GetEscapement() ? CalcEscAscent( nAscent ) : nAscent;
1002 }
1003 
GetHeight(SwViewShell const * pSh,const OutputDevice & rOut)1004 sal_uInt16 SwSubFont::GetHeight( SwViewShell const *pSh, const OutputDevice& rOut )
1005 {
1006     SV_STAT( nGetTextSize );
1007     SwFntAccess aFntAccess( m_nFontCacheId, m_nFontIndex, this, pSh );
1008     const sal_uInt16 nHeight = aFntAccess.Get()->GetFontHeight( pSh, rOut );
1009     if ( GetEscapement() )
1010     {
1011         const sal_uInt16 nAscent = aFntAccess.Get()->GetFontAscent( pSh, rOut );
1012         return CalcEscHeight( nHeight, nAscent ); // + nLeading;
1013     }
1014     return nHeight; // + nLeading;
1015 }
1016 
GetTextSize_(SwDrawTextInfo & rInf)1017 Size SwSubFont::GetTextSize_( SwDrawTextInfo& rInf )
1018 {
1019     // Robust: the font is supposed to be set already, but better safe than
1020     // sorry...
1021     if ( !pLastFont || pLastFont->GetOwner() != reinterpret_cast<const void*>(m_nFontCacheId) ||
1022          !IsSameInstance( rInf.GetpOut()->GetFont() ) )
1023         ChgFnt( rInf.GetShell(), rInf.GetOut() );
1024 
1025     SwDigitModeModifier aDigitModeModifier( rInf.GetOut(), rInf.GetFont()->GetLanguage() );
1026 
1027     Size aTextSize;
1028     TextFrameIndex const nLn = rInf.GetLen() == TextFrameIndex(COMPLETE_STRING)
1029             ? TextFrameIndex(rInf.GetText().getLength())
1030             : rInf.GetLen();
1031     rInf.SetLen( nLn );
1032     if( IsCapital() && nLn )
1033         aTextSize = GetCapitalSize( rInf );
1034     else
1035     {
1036         SV_STAT( nGetTextSize );
1037         tools::Long nOldKern = rInf.GetKern();
1038         const OUString oldText = rInf.GetText();
1039         rInf.SetKern( CheckKerning() );
1040         if ( !IsCaseMap() )
1041             aTextSize = pLastFont->GetTextSize( rInf );
1042         else
1043         {
1044             const OUString aTmp = CalcCaseMap( rInf.GetText() );
1045             const OUString oldStr = rInf.GetText();
1046             bool bCaseMapLengthDiffers(aTmp.getLength() != oldStr.getLength());
1047 
1048             if(bCaseMapLengthDiffers && rInf.GetLen())
1049             {
1050                 // If the length of the original string and the CaseMapped one
1051                 // are different, it is necessary to handle the given text part as
1052                 // a single snippet since its size may differ, too.
1053                 TextFrameIndex const nOldIdx(rInf.GetIdx());
1054                 TextFrameIndex const nOldLen(rInf.GetLen());
1055                 const OUString aSnippet(oldStr.copy(sal_Int32(nOldIdx), sal_Int32(nOldLen)));
1056                 const OUString aNewText(CalcCaseMap(aSnippet));
1057 
1058                 rInf.SetText( aNewText );
1059                 rInf.SetIdx( TextFrameIndex(0) );
1060                 rInf.SetLen( TextFrameIndex(aNewText.getLength()) );
1061 
1062                 aTextSize = pLastFont->GetTextSize( rInf );
1063 
1064                 rInf.SetIdx( nOldIdx );
1065                 rInf.SetLen( nOldLen );
1066             }
1067             else
1068             {
1069                 rInf.SetText( aTmp );
1070                 aTextSize = pLastFont->GetTextSize( rInf );
1071             }
1072 
1073             rInf.SetText(oldStr);
1074         }
1075         rInf.SetKern( nOldKern );
1076         rInf.SetText(oldText);
1077         // A word that's longer than one line, with escapement at the line
1078         // break, must report its effective height.
1079         if( GetEscapement() )
1080         {
1081             const sal_uInt16 nAscent = pLastFont->GetFontAscent( rInf.GetShell(),
1082                                                              rInf.GetOut() );
1083             aTextSize.setHeight(
1084                 static_cast<tools::Long>(CalcEscHeight( o3tl::narrowing<sal_uInt16>(aTextSize.Height()), nAscent)) );
1085         }
1086     }
1087 
1088     if (TextFrameIndex(1) == rInf.GetLen()
1089         && CH_TXT_ATR_FIELDSTART == rInf.GetText()[sal_Int32(rInf.GetIdx())])
1090     {
1091         assert(!"this is presumably dead code");
1092         TextFrameIndex const nOldIdx(rInf.GetIdx());
1093         TextFrameIndex const nOldLen(rInf.GetLen());
1094         const OUString aNewText(CH_TXT_ATR_SUBST_FIELDSTART);
1095         rInf.SetText( aNewText );
1096         rInf.SetIdx( TextFrameIndex(0) );
1097         rInf.SetLen( TextFrameIndex(aNewText.getLength()) );
1098         aTextSize = pLastFont->GetTextSize( rInf );
1099         rInf.SetIdx( nOldIdx );
1100         rInf.SetLen( nOldLen );
1101     }
1102     else if (TextFrameIndex(1) == rInf.GetLen()
1103             && CH_TXT_ATR_FIELDEND == rInf.GetText()[sal_Int32(rInf.GetIdx())])
1104     {
1105         assert(!"this is presumably dead code");
1106         TextFrameIndex const nOldIdx(rInf.GetIdx());
1107         TextFrameIndex const nOldLen(rInf.GetLen());
1108         const OUString aNewText(CH_TXT_ATR_SUBST_FIELDEND);
1109         rInf.SetText( aNewText );
1110         rInf.SetIdx( TextFrameIndex(0) );
1111         rInf.SetLen( TextFrameIndex(aNewText.getLength()) );
1112         aTextSize = pLastFont->GetTextSize( rInf );
1113         rInf.SetIdx( nOldIdx );
1114         rInf.SetLen( nOldLen );
1115     }
1116 
1117     return aTextSize;
1118 }
1119 
DrawText_(SwDrawTextInfo & rInf,const bool bGrey)1120 void SwSubFont::DrawText_( SwDrawTextInfo &rInf, const bool bGrey )
1121 {
1122     rInf.SetGreyWave( bGrey );
1123     TextFrameIndex const nLn(rInf.GetText().getLength());
1124     if( !rInf.GetLen() || !nLn )
1125         return;
1126     if (TextFrameIndex(COMPLETE_STRING) == rInf.GetLen())
1127         rInf.SetLen( nLn );
1128 
1129     FontLineStyle nOldUnder = LINESTYLE_NONE;
1130     SwUnderlineFont* pUnderFnt = nullptr;
1131 
1132     if( rInf.GetUnderFnt() )
1133     {
1134         nOldUnder = GetUnderline();
1135         SetUnderline( LINESTYLE_NONE );
1136         pUnderFnt = rInf.GetUnderFnt();
1137     }
1138 
1139     if( !pLastFont || pLastFont->GetOwner() != reinterpret_cast<const void*>(m_nFontCacheId) )
1140         ChgFnt( rInf.GetShell(), rInf.GetOut() );
1141 
1142     SwDigitModeModifier aDigitModeModifier( rInf.GetOut(), rInf.GetFont()->GetLanguage() );
1143 
1144     const Point aOldPos(rInf.GetPos());
1145     Point aPos( rInf.GetPos() );
1146 
1147     if( GetEscapement() )
1148         CalcEsc( rInf, aPos );
1149 
1150     rInf.SetPos( aPos );
1151     rInf.SetKern( CheckKerning() + rInf.GetSperren() / SPACING_PRECISION_FACTOR );
1152 
1153     if( IsCapital() )
1154         DrawCapital( rInf );
1155     else
1156     {
1157         SV_STAT( nDrawText );
1158         if ( !IsCaseMap() )
1159             pLastFont->DrawText( rInf );
1160         else
1161         {
1162             const OUString oldStr = rInf.GetText();
1163             const OUString aString( CalcCaseMap(oldStr) );
1164             bool bCaseMapLengthDiffers(aString.getLength() != oldStr.getLength());
1165 
1166             if(bCaseMapLengthDiffers && rInf.GetLen())
1167             {
1168                 // If the length of the original string and the CaseMapped one
1169                 // are different, it is necessary to handle the given text part as
1170                 // a single snippet since its size may differ, too.
1171                 TextFrameIndex const nOldIdx(rInf.GetIdx());
1172                 TextFrameIndex const nOldLen(rInf.GetLen());
1173                 const OUString aSnippet(oldStr.copy(sal_Int32(nOldIdx), sal_Int32(nOldLen)));
1174                 const OUString aNewText = CalcCaseMap(aSnippet);
1175 
1176                 rInf.SetText( aNewText );
1177                 rInf.SetIdx( TextFrameIndex(0) );
1178                 rInf.SetLen( TextFrameIndex(aNewText.getLength()) );
1179 
1180                 pLastFont->DrawText( rInf );
1181 
1182                 rInf.SetIdx( nOldIdx );
1183                 rInf.SetLen( nOldLen );
1184             }
1185             else
1186             {
1187                 rInf.SetText( aString );
1188                 pLastFont->DrawText( rInf );
1189             }
1190 
1191             rInf.SetText(oldStr);
1192         }
1193     }
1194 
1195     if( pUnderFnt && nOldUnder != LINESTYLE_NONE )
1196     {
1197         Size aFontSize = GetTextSize_( rInf );
1198         const OUString oldStr = rInf.GetText();
1199 
1200         TextFrameIndex const nOldIdx = rInf.GetIdx();
1201         TextFrameIndex const nOldLen = rInf.GetLen();
1202         tools::Long nSpace = 0;
1203         if( rInf.GetSpace() )
1204         {
1205             TextFrameIndex nTmpEnd = nOldIdx + nOldLen;
1206             if (nTmpEnd > TextFrameIndex(oldStr.getLength()))
1207                 nTmpEnd = TextFrameIndex(oldStr.getLength());
1208 
1209             const SwScriptInfo* pSI = rInf.GetScriptInfo();
1210 
1211             const bool bAsianFont =
1212                 ( rInf.GetFont() && SwFontScript::CJK == rInf.GetFont()->GetActual() );
1213             for (TextFrameIndex nTmp = nOldIdx; nTmp < nTmpEnd; ++nTmp)
1214             {
1215                 if (CH_BLANK == oldStr[sal_Int32(nTmp)] || bAsianFont ||
1216                     (nTmp + TextFrameIndex(1) < TextFrameIndex(oldStr.getLength())
1217                      && pSI
1218                      && i18n::ScriptType::ASIAN == pSI->ScriptType(nTmp + TextFrameIndex(1))))
1219                 {
1220                     ++nSpace;
1221                 }
1222             }
1223 
1224             // if next portion if a hole portion we do not consider any
1225             // extra space added because the last character was ASIAN
1226             if ( nSpace && rInf.IsSpaceStop() && bAsianFont )
1227                  --nSpace;
1228 
1229             nSpace *= rInf.GetSpace() / SPACING_PRECISION_FACTOR;
1230         }
1231 
1232         rInf.SetWidth( sal_uInt16(aFontSize.Width() + nSpace) );
1233         rInf.SetText( "  " );
1234         rInf.SetIdx( TextFrameIndex(0) );
1235         rInf.SetLen( TextFrameIndex(2) );
1236         SetUnderline( nOldUnder );
1237         rInf.SetUnderFnt( nullptr );
1238 
1239         // set position for underline font
1240         rInf.SetPos( pUnderFnt->GetPos() );
1241 
1242         pUnderFnt->GetFont().DrawStretchText_( rInf );
1243 
1244         rInf.SetUnderFnt( pUnderFnt );
1245         rInf.SetText(oldStr);
1246         rInf.SetIdx( nOldIdx );
1247         rInf.SetLen( nOldLen );
1248     }
1249 
1250     rInf.SetPos(aOldPos);
1251 }
1252 
DrawStretchText_(SwDrawTextInfo & rInf)1253 void SwSubFont::DrawStretchText_( SwDrawTextInfo &rInf )
1254 {
1255     if( !rInf.GetLen() || !rInf.GetText().getLength() )
1256         return;
1257 
1258     FontLineStyle nOldUnder = LINESTYLE_NONE;
1259     SwUnderlineFont* pUnderFnt = nullptr;
1260 
1261     if( rInf.GetUnderFnt() )
1262     {
1263         nOldUnder = GetUnderline();
1264         SetUnderline( LINESTYLE_NONE );
1265         pUnderFnt = rInf.GetUnderFnt();
1266     }
1267 
1268     if ( !pLastFont || pLastFont->GetOwner() != reinterpret_cast<const void*>(m_nFontCacheId) )
1269         ChgFnt( rInf.GetShell(), rInf.GetOut() );
1270 
1271     SwDigitModeModifier aDigitModeModifier( rInf.GetOut(), rInf.GetFont()->GetLanguage() );
1272 
1273     rInf.ApplyAutoColor();
1274 
1275     const Point aOldPos(rInf.GetPos());
1276     Point aPos( rInf.GetPos() );
1277 
1278     if( GetEscapement() )
1279         CalcEsc( rInf, aPos );
1280 
1281     rInf.SetKern( CheckKerning() + rInf.GetSperren() / SPACING_PRECISION_FACTOR );
1282     rInf.SetPos( aPos );
1283 
1284     if( IsCapital() )
1285         DrawStretchCapital( rInf );
1286     else
1287     {
1288         SV_STAT( nDrawStretchText );
1289 
1290         if ( rInf.GetFrame() )
1291         {
1292             if ( rInf.GetFrame()->IsRightToLeft() )
1293                 rInf.GetFrame()->SwitchLTRtoRTL( aPos );
1294 
1295             if ( rInf.GetFrame()->IsVertical() )
1296                 rInf.GetFrame()->SwitchHorizontalToVertical( aPos );
1297 
1298             rInf.SetPos( aPos );
1299         }
1300 
1301         if ( !IsCaseMap() )
1302             rInf.GetOut().DrawStretchText( aPos, rInf.GetWidth(),
1303                 rInf.GetText(), sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1304         else
1305             rInf.GetOut().DrawStretchText( aPos, rInf.GetWidth(),
1306                     CalcCaseMap(rInf.GetText()),
1307                     sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
1308     }
1309 
1310     if( pUnderFnt && nOldUnder != LINESTYLE_NONE )
1311     {
1312         const OUString oldStr = rInf.GetText();
1313         TextFrameIndex const nOldIdx = rInf.GetIdx();
1314         TextFrameIndex const nOldLen = rInf.GetLen();
1315         rInf.SetText( "  " );
1316         rInf.SetIdx( TextFrameIndex(0) );
1317         rInf.SetLen( TextFrameIndex(2) );
1318         SetUnderline( nOldUnder );
1319         rInf.SetUnderFnt( nullptr );
1320 
1321         // set position for underline font
1322         rInf.SetPos( pUnderFnt->GetPos() );
1323 
1324         pUnderFnt->GetFont().DrawStretchText_( rInf );
1325 
1326         rInf.SetUnderFnt( pUnderFnt );
1327         rInf.SetText(oldStr);
1328         rInf.SetIdx( nOldIdx );
1329         rInf.SetLen( nOldLen );
1330     }
1331 
1332     rInf.SetPos(aOldPos);
1333 }
1334 
GetModelPositionForViewPoint_(SwDrawTextInfo & rInf)1335 TextFrameIndex SwSubFont::GetModelPositionForViewPoint_( SwDrawTextInfo& rInf )
1336 {
1337     if ( !pLastFont || pLastFont->GetOwner() != reinterpret_cast<const void*>(m_nFontCacheId) )
1338         ChgFnt( rInf.GetShell(), rInf.GetOut() );
1339 
1340     SwDigitModeModifier aDigitModeModifier( rInf.GetOut(), rInf.GetFont()->GetLanguage() );
1341 
1342     TextFrameIndex const nLn = rInf.GetLen() == TextFrameIndex(COMPLETE_STRING)
1343             ? TextFrameIndex(rInf.GetText().getLength())
1344             : rInf.GetLen();
1345     rInf.SetLen( nLn );
1346     TextFrameIndex nCursor(0);
1347     if( IsCapital() && nLn )
1348         nCursor = GetCapitalCursorOfst( rInf );
1349     else
1350     {
1351         const OUString oldText = rInf.GetText();
1352         tools::Long nOldKern = rInf.GetKern();
1353         rInf.SetKern( CheckKerning() );
1354         SV_STAT( nGetTextSize );
1355         if ( !IsCaseMap() )
1356             nCursor = pLastFont->GetModelPositionForViewPoint( rInf );
1357         else
1358         {
1359             rInf.SetText( CalcCaseMap( rInf.GetText() ) );
1360             nCursor = pLastFont->GetModelPositionForViewPoint( rInf );
1361         }
1362         rInf.SetKern( nOldKern );
1363         rInf.SetText(oldText);
1364     }
1365     return nCursor;
1366 }
1367 
CalcEsc(SwDrawTextInfo const & rInf,Point & rPos)1368 void SwSubFont::CalcEsc( SwDrawTextInfo const & rInf, Point& rPos )
1369 {
1370     tools::Long nOfst;
1371 
1372     bool bVert = false;
1373     bool bVertLRBT = false;
1374     if (rInf.GetFrame())
1375     {
1376         bVert = rInf.GetFrame()->IsVertical();
1377         bVertLRBT = rInf.GetFrame()->IsVertLRBT();
1378     }
1379     const Degree10 nDir = UnMapDirection(GetOrientation(), bVert, bVertLRBT);
1380 
1381     switch ( GetEscapement() )
1382     {
1383     case DFLT_ESC_AUTO_SUB :
1384         nOfst = m_nOrgHeight - m_nOrgAscent -
1385             pLastFont->GetFontHeight( rInf.GetShell(), rInf.GetOut() ) +
1386             pLastFont->GetFontAscent( rInf.GetShell(), rInf.GetOut() );
1387 
1388         switch ( nDir.get() )
1389         {
1390         case 0 :
1391             rPos.AdjustY(nOfst );
1392             break;
1393         case 900 :
1394             rPos.AdjustX(nOfst );
1395             break;
1396         case 2700 :
1397             rPos.AdjustX( -nOfst );
1398             break;
1399         }
1400 
1401         break;
1402     case DFLT_ESC_AUTO_SUPER :
1403         nOfst = pLastFont->GetFontAscent( rInf.GetShell(), rInf.GetOut() ) -
1404                 m_nOrgAscent;
1405 
1406         switch ( nDir.get() )
1407         {
1408         case 0 :
1409             rPos.AdjustY(nOfst );
1410             break;
1411         case 900 :
1412             rPos.AdjustX(nOfst );
1413             break;
1414         case 2700 :
1415             rPos.AdjustX( -nOfst );
1416             break;
1417         }
1418 
1419         break;
1420     default :
1421         nOfst = (static_cast<tools::Long>(m_nOrgHeight) * GetEscapement()) / 100;
1422 
1423         switch ( nDir.get() )
1424         {
1425         case 0 :
1426             rPos.AdjustY( -nOfst );
1427             break;
1428         case 900 :
1429             rPos.AdjustX( -nOfst );
1430             break;
1431         case 2700 :
1432             rPos.AdjustX(nOfst );
1433             break;
1434         }
1435     }
1436 }
1437 
1438 // used during painting of small capitals
Shift(Degree10 nDir)1439 void SwDrawTextInfo::Shift( Degree10 nDir )
1440 {
1441 #ifdef DBG_UTIL
1442     OSL_ENSURE( m_bPos, "DrawTextInfo: Undefined Position" );
1443     OSL_ENSURE( m_bSize, "DrawTextInfo: Undefined Width" );
1444 #endif
1445 
1446     const bool bBidiPor = ( GetFrame() && GetFrame()->IsRightToLeft() ) !=
1447                           ( ComplexTextLayoutFlags::Default != ( ComplexTextLayoutFlags::BiDiRtl & GetpOut()->GetLayoutMode() ) );
1448 
1449     bool bVert = false;
1450     bool bVertLRBT = false;
1451     if (GetFrame())
1452     {
1453         bVert = GetFrame()->IsVertical();
1454         bVertLRBT = GetFrame()->IsVertLRBT();
1455     }
1456     nDir = bBidiPor ? 1800_deg10 : UnMapDirection(nDir, bVert, bVertLRBT);
1457 
1458     switch ( nDir.get() )
1459     {
1460     case 0 :
1461         m_aPos.AdjustX(GetSize().Width() );
1462         break;
1463     case 900 :
1464         OSL_ENSURE( m_aPos.Y() >= GetSize().Width(), "Going underground" );
1465         m_aPos.AdjustY( -(GetSize().Width()) );
1466         break;
1467     case 1800 :
1468         m_aPos.AdjustX( -(GetSize().Width()) );
1469         break;
1470     case 2700 :
1471         m_aPos.AdjustY(GetSize().Width() );
1472         break;
1473     }
1474 }
1475 
1476 /**
1477  * @note Used for the "continuous underline" feature.
1478  **/
SwUnderlineFont(SwFont & rFnt,TextFrameIndex const nEnd,const Point & rPoint)1479 SwUnderlineFont::SwUnderlineFont(SwFont& rFnt, TextFrameIndex const nEnd, const Point& rPoint)
1480         : m_aPos( rPoint ), m_nEnd( nEnd ), m_pFont( &rFnt )
1481 {
1482 };
1483 
~SwUnderlineFont()1484 SwUnderlineFont::~SwUnderlineFont()
1485 {
1486 }
1487 
1488 /// Helper for filters to find true lineheight of a font
AttrSetToLineHeight(const IDocumentSettingAccess & rIDocumentSettingAccess,const SwAttrSet & rSet,const vcl::RenderContext & rOut,sal_Int16 nScript)1489 tools::Long AttrSetToLineHeight( const IDocumentSettingAccess& rIDocumentSettingAccess,
1490                           const SwAttrSet &rSet,
1491                           const vcl::RenderContext &rOut, sal_Int16 nScript)
1492 {
1493     SwFont aFont(&rSet, &rIDocumentSettingAccess);
1494     SwFontScript nActual;
1495     switch (nScript)
1496     {
1497         default:
1498         case i18n::ScriptType::LATIN:
1499             nActual = SwFontScript::Latin;
1500             break;
1501         case i18n::ScriptType::ASIAN:
1502             nActual = SwFontScript::CJK;
1503             break;
1504         case i18n::ScriptType::COMPLEX:
1505             nActual = SwFontScript::CTL;
1506             break;
1507     }
1508     aFont.SetActual(nActual);
1509 
1510     vcl::RenderContext &rMutableOut = const_cast<vcl::RenderContext &>(rOut);
1511     const vcl::Font aOldFont(rMutableOut.GetFont());
1512 
1513     rMutableOut.SetFont(aFont.GetActualFont());
1514     tools::Long nHeight = rMutableOut.GetTextHeight();
1515 
1516     rMutableOut.SetFont(aOldFont);
1517     return nHeight;
1518 }
1519 
1520 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1521