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 <vcl/metric.hxx>
21 #include <vcl/outdev.hxx>
22 #include <vcl/print.hxx>
23 #include <tools/debug.hxx>
24 #include <tools/gen.hxx>
25 #include <tools/poly.hxx>
26 #include <unotools/charclass.hxx>
27 #include <com/sun/star/i18n/KCharacterType.hpp>
28 #include <editeng/svxfont.hxx>
29 #include <editeng/escapementitem.hxx>
30 #include <sal/log.hxx>
31 
SvxFont()32 SvxFont::SvxFont()
33 {
34     nKern = nEsc = 0;
35     nPropr = 100;
36     eCaseMap = SvxCaseMap::NotMapped;
37     SetLanguage(LANGUAGE_SYSTEM);
38 }
39 
SvxFont(const vcl::Font & rFont)40 SvxFont::SvxFont( const vcl::Font &rFont )
41     : Font( rFont )
42 {
43     nKern = nEsc = 0;
44     nPropr = 100;
45     eCaseMap = SvxCaseMap::NotMapped;
46     SetLanguage(LANGUAGE_SYSTEM);
47 }
48 
SvxFont(const SvxFont & rFont)49 SvxFont::SvxFont( const SvxFont &rFont )
50     : Font( rFont )
51 {
52     nKern = rFont.GetFixKerning();
53     nEsc  = rFont.GetEscapement();
54     nPropr = rFont.GetPropr();
55     eCaseMap = rFont.GetCaseMap();
56     SetLanguage(rFont.GetLanguage());
57 }
58 
SetNonAutoEscapement(short nNewEsc,const OutputDevice * pOutDev)59 void SvxFont::SetNonAutoEscapement(short nNewEsc, const OutputDevice* pOutDev)
60 {
61     nEsc = nNewEsc;
62     if ( abs(nEsc) == DFLT_ESC_AUTO_SUPER )
63     {
64         double fAutoAscent = .8;
65         double fAutoDescent = .2;
66         if ( pOutDev )
67         {
68             const FontMetric& rFontMetric = pOutDev->GetFontMetric();
69             double fFontHeight = rFontMetric.GetAscent() + rFontMetric.GetDescent();
70             if ( fFontHeight )
71             {
72                 fAutoAscent = rFontMetric.GetAscent() / fFontHeight;
73                 fAutoDescent = rFontMetric.GetDescent() / fFontHeight;
74             }
75         }
76 
77         if ( nEsc == DFLT_ESC_AUTO_SUPER )
78             nEsc = fAutoAscent * (100 - nPropr);
79         else //DFLT_ESC_AUTO_SUB
80             nEsc = fAutoDescent * -(100 - nPropr);
81     }
82 
83     if ( nEsc > MAX_ESC_POS )
84         nEsc = MAX_ESC_POS;
85     else if  ( nEsc < -MAX_ESC_POS )
86         nEsc = -MAX_ESC_POS;
87 }
88 
DrawArrow(OutputDevice & rOut,const tools::Rectangle & rRect,const Size & rSize,const Color & rCol,bool bLeftOrTop,bool bVertical)89 tools::Polygon SvxFont::DrawArrow( OutputDevice &rOut, const tools::Rectangle& rRect,
90     const Size& rSize, const Color& rCol, bool bLeftOrTop, bool bVertical )
91 {
92     tools::Polygon aPoly;
93     Point aTmp;
94     Point aNxt;
95     if (bVertical)
96     {
97         tools::Long nLeft = ((rRect.Left() + rRect.Right()) / 2) - (rSize.Height() / 2);
98         tools::Long nRight = ((rRect.Left() + rRect.Right()) / 2) + (rSize.Height() / 2);
99         tools::Long nMid = (rRect.Left() + rRect.Right()) / 2;
100         tools::Long nTop = ((rRect.Top() + rRect.Bottom()) / 2) - (rSize.Height() / 2);
101         tools::Long nBottom = nTop + rSize.Height();
102         if (nTop < rRect.Top())
103         {
104             if (bLeftOrTop)
105             {
106                 nTop = rRect.Top();
107                 nBottom = rRect.Bottom();
108             }
109             else
110             {
111                 nTop = rRect.Bottom();
112                 nBottom = rRect.Bottom() - (rSize.Height() / 2);
113             }
114         }
115         aTmp.setX(nRight);
116         aTmp.setY(nBottom);
117         aNxt.setX(nMid);
118         aNxt.setY(nTop);
119         aPoly.Insert(0, aTmp);
120         aPoly.Insert(0, aNxt);
121         aTmp.setX(nLeft);
122         aPoly.Insert(0, aTmp);
123     }
124     else
125     {
126         tools::Long nLeft = (rRect.Left() + rRect.Right() - rSize.Width()) / 2;
127         tools::Long nRight = nLeft + rSize.Width();
128         tools::Long nMid = (rRect.Top() + rRect.Bottom()) / 2;
129         tools::Long nTop = nMid - rSize.Height() / 2;
130         tools::Long nBottom = nTop + rSize.Height();
131         if (nLeft < rRect.Left())
132         {
133             nLeft = rRect.Left();
134             nRight = rRect.Right();
135         }
136         aTmp.setX(bLeftOrTop ? nLeft : nRight);
137         aTmp.setY(nMid);
138         aNxt.setX(bLeftOrTop ? nRight : nLeft);
139         aNxt.setY(nTop);
140         aPoly.Insert(0, aTmp);
141         aPoly.Insert(0, aNxt);
142         aNxt.setY(nBottom);
143         aPoly.Insert(0, aNxt);
144     }
145     Color aOldLineColor = rOut.GetLineColor();
146     Color aOldFillColor = rOut.GetFillColor();
147     rOut.SetFillColor( rCol );
148     rOut.SetLineColor( COL_BLACK );
149     rOut.DrawPolygon( aPoly );
150     rOut.DrawLine( aTmp, aNxt );
151     rOut.SetLineColor( aOldLineColor );
152     rOut.SetFillColor( aOldFillColor );
153     return aPoly;
154 }
155 
CalcCaseMap(const OUString & rTxt) const156 OUString SvxFont::CalcCaseMap(const OUString &rTxt) const
157 {
158     if (!IsCaseMap() || rTxt.isEmpty())
159         return rTxt;
160     OUString aTxt(rTxt);
161     // I still have to get the language
162     const LanguageType eLang = LANGUAGE_DONTKNOW == GetLanguage()
163                              ? LANGUAGE_SYSTEM : GetLanguage();
164 
165     LanguageTag aLanguageTag(eLang);
166     CharClass aCharClass( aLanguageTag );
167 
168     switch( eCaseMap )
169     {
170         case SvxCaseMap::SmallCaps:
171         case SvxCaseMap::Uppercase:
172         {
173             aTxt = aCharClass.uppercase( aTxt );
174             break;
175         }
176 
177         case SvxCaseMap::Lowercase:
178         {
179             aTxt = aCharClass.lowercase( aTxt );
180             break;
181         }
182         case SvxCaseMap::Capitalize:
183         {
184             // Every beginning of a word is capitalized,  the rest of the word
185             // is taken over as is.
186             // Bug: if the attribute starts in the middle of the word.
187             bool bBlank = true;
188 
189             for (sal_Int32 i = 0; i < aTxt.getLength(); ++i)
190             {
191                 if( aTxt[i] == ' ' || aTxt[i] == '\t')
192                     bBlank = true;
193                 else
194                 {
195                     if (bBlank)
196                     {
197                         OUString sTitle(aCharClass.uppercase(OUString(aTxt[i])));
198                         aTxt = aTxt.replaceAt(i, 1, sTitle);
199                     }
200                     bBlank = false;
201                 }
202             }
203             break;
204         }
205         default:
206         {
207             SAL_WARN( "editeng", "SvxFont::CaseMapTxt: unknown casemap");
208             break;
209         }
210     }
211     return aTxt;
212 }
213 
214 /*************************************************************************
215  *                      class SvxDoCapitals
216  * The virtual Method Do si called by SvxFont::DoOnCapitals alternately
217  * the uppercase and lowercase parts. The derivate of SvxDoCapitals fills
218  * this method with life.
219  *************************************************************************/
220 
221 class SvxDoCapitals
222 {
223 protected:
224     VclPtr<OutputDevice> pOut;
225     const OUString &rTxt;
226     const sal_Int32 nIdx;
227     const sal_Int32 nLen;
228 
229 public:
SvxDoCapitals(OutputDevice * _pOut,const OUString & _rTxt,const sal_Int32 _nIdx,const sal_Int32 _nLen)230     SvxDoCapitals( OutputDevice *_pOut, const OUString &_rTxt,
231                    const sal_Int32 _nIdx, const sal_Int32 _nLen )
232         : pOut(_pOut), rTxt(_rTxt), nIdx(_nIdx), nLen(_nLen)
233         { }
234 
~SvxDoCapitals()235     virtual ~SvxDoCapitals() {}
236 
237     virtual void DoSpace( const bool bDraw );
238     virtual void SetSpace();
239     virtual void Do( const OUString &rTxt,
240                      const sal_Int32 nIdx, const sal_Int32 nLen,
241                      const bool bUpper ) = 0;
242 
GetTxt() const243     const OUString &GetTxt() const { return rTxt; }
GetIdx() const244     sal_Int32 GetIdx() const { return nIdx; }
GetLen() const245     sal_Int32 GetLen() const { return nLen; }
246 };
247 
DoSpace(const bool)248 void SvxDoCapitals::DoSpace( const bool /*bDraw*/ ) { }
249 
SetSpace()250 void SvxDoCapitals::SetSpace() { }
251 
252 /*************************************************************************
253  *                  SvxFont::DoOnCapitals() const
254  * Decomposes the String into uppercase and lowercase letters and then
255  * calls the method SvxDoCapitals::Do( ).
256  *************************************************************************/
257 
DoOnCapitals(SvxDoCapitals & rDo) const258 void SvxFont::DoOnCapitals(SvxDoCapitals &rDo) const
259 {
260     const OUString &rTxt = rDo.GetTxt();
261     const sal_Int32 nIdx = rDo.GetIdx();
262     const sal_Int32 nLen = rDo.GetLen();
263 
264     const OUString aTxt( CalcCaseMap( rTxt ) );
265     const sal_Int32 nTxtLen = std::min( rTxt.getLength(), nLen );
266     sal_Int32 nPos = 0;
267     sal_Int32 nOldPos = nPos;
268 
269     // Test if string length differ between original and CaseMapped
270     bool bCaseMapLengthDiffers(aTxt.getLength() != rTxt.getLength());
271 
272     const LanguageType eLang = LANGUAGE_DONTKNOW == GetLanguage()
273                              ? LANGUAGE_SYSTEM : GetLanguage();
274 
275     LanguageTag aLanguageTag(eLang);
276     CharClass   aCharClass( aLanguageTag );
277     OUString    aCharString;
278 
279     while( nPos < nTxtLen )
280     {
281         // first in turn are the uppercase letters
282 
283         // There are characters that are both upper- and lower-case L (eg blank)
284         // Such ambiguities lead to chaos, this is why these characters are
285         // allocated to the lowercase characters!
286 
287         while( nPos < nTxtLen )
288         {
289             aCharString = rTxt.copy( nPos + nIdx, 1 );
290             sal_Int32 nCharacterType = aCharClass.getCharacterType( aCharString, 0 );
291             if ( nCharacterType & css::i18n::KCharacterType::LOWER )
292                 break;
293             if ( ! ( nCharacterType & css::i18n::KCharacterType::UPPER ) )
294                 break;
295             ++nPos;
296         }
297         if( nOldPos != nPos )
298         {
299             if(bCaseMapLengthDiffers)
300             {
301                 // If strings differ work preparing the necessary snippet to address that
302                 // potential difference
303                 const OUString aSnippet = rTxt.copy(nIdx + nOldPos, nPos-nOldPos);
304                 OUString aNewText = CalcCaseMap(aSnippet);
305 
306                 rDo.Do( aNewText, 0, aNewText.getLength(), true );
307             }
308             else
309             {
310                 rDo.Do( aTxt, nIdx + nOldPos, nPos-nOldPos, true );
311             }
312 
313             nOldPos = nPos;
314         }
315         // Now the lowercase are processed (without blanks)
316         while( nPos < nTxtLen )
317         {
318             sal_uInt32  nCharacterType = aCharClass.getCharacterType( aCharString, 0 );
319             if ( nCharacterType & css::i18n::KCharacterType::UPPER )
320                 break;
321             if ( aCharString == " " )
322                 break;
323             if( ++nPos < nTxtLen )
324                 aCharString = rTxt.copy( nPos + nIdx, 1 );
325         }
326         if( nOldPos != nPos )
327         {
328             if(bCaseMapLengthDiffers)
329             {
330                 // If strings differ work preparing the necessary snippet to address that
331                 // potential difference
332                 const OUString aSnippet = rTxt.copy(nIdx + nOldPos, nPos - nOldPos);
333                 OUString aNewText = CalcCaseMap(aSnippet);
334 
335                 rDo.Do( aNewText, 0, aNewText.getLength(), false );
336             }
337             else
338             {
339                 rDo.Do( aTxt, nIdx + nOldPos, nPos-nOldPos, false );
340             }
341 
342             nOldPos = nPos;
343         }
344         // Now the blanks are<processed
345         while( nPos < nTxtLen && aCharString == " " && ++nPos < nTxtLen )
346             aCharString = rTxt.copy( nPos + nIdx, 1 );
347 
348         if( nOldPos != nPos )
349         {
350             rDo.DoSpace( false );
351 
352             if(bCaseMapLengthDiffers)
353             {
354                 // If strings differ work preparing the necessary snippet to address that
355                 // potential difference
356                 const OUString aSnippet = rTxt.copy(nIdx + nOldPos, nPos - nOldPos);
357                 OUString aNewText = CalcCaseMap(aSnippet);
358 
359                 rDo.Do( aNewText, 0, aNewText.getLength(), false );
360             }
361             else
362             {
363                 rDo.Do( aTxt, nIdx + nOldPos, nPos - nOldPos, false );
364             }
365 
366             nOldPos = nPos;
367             rDo.SetSpace();
368         }
369     }
370     rDo.DoSpace( true );
371 }
372 
373 
SetPhysFont(OutputDevice & rOut) const374 void SvxFont::SetPhysFont(OutputDevice& rOut) const
375 {
376     const vcl::Font& rCurrentFont = rOut.GetFont();
377     if ( nPropr == 100 )
378     {
379         if ( !rCurrentFont.IsSameInstance( *this ) )
380             rOut.SetFont( *this );
381     }
382     else
383     {
384         Font aNewFont( *this );
385         Size aSize( aNewFont.GetFontSize() );
386         aNewFont.SetFontSize( Size( aSize.Width() * nPropr / 100,
387                                     aSize.Height() * nPropr / 100 ) );
388         if ( !rCurrentFont.IsSameInstance( aNewFont ) )
389             rOut.SetFont( aNewFont );
390     }
391 }
392 
ChgPhysFont(OutputDevice & rOut) const393 vcl::Font SvxFont::ChgPhysFont(OutputDevice& rOut) const
394 {
395     vcl::Font aOldFont(rOut.GetFont());
396     SetPhysFont(rOut);
397     return aOldFont;
398 }
399 
GetPhysTxtSize(const OutputDevice * pOut,const OUString & rTxt,const sal_Int32 nIdx,const sal_Int32 nLen) const400 Size SvxFont::GetPhysTxtSize( const OutputDevice *pOut, const OUString &rTxt,
401                          const sal_Int32 nIdx, const sal_Int32 nLen ) const
402 {
403     if ( !IsCaseMap() && !IsKern() )
404         return Size( pOut->GetTextWidth( rTxt, nIdx, nLen ),
405                      pOut->GetTextHeight() );
406 
407     Size aTxtSize;
408     aTxtSize.setHeight( pOut->GetTextHeight() );
409     if ( !IsCaseMap() )
410         aTxtSize.setWidth( pOut->GetTextWidth( rTxt, nIdx, nLen ) );
411     else
412     {
413         const OUString aNewText = CalcCaseMap(rTxt);
414         bool bCaseMapLengthDiffers(aNewText.getLength() != rTxt.getLength());
415         sal_Int32 nWidth(0);
416 
417         if(bCaseMapLengthDiffers)
418         {
419             // If strings differ work preparing the necessary snippet to address that
420             // potential difference
421             const OUString aSnippet = rTxt.copy(nIdx, nLen);
422             OUString _aNewText = CalcCaseMap(aSnippet);
423             nWidth = pOut->GetTextWidth( _aNewText, 0, _aNewText.getLength() );
424         }
425         else
426         {
427             nWidth = pOut->GetTextWidth( aNewText, nIdx, nLen );
428         }
429 
430         aTxtSize.setWidth(nWidth);
431     }
432 
433     if( IsKern() && ( nLen > 1 ) )
434         aTxtSize.AdjustWidth( ( nLen-1 ) * tools::Long( nKern ) );
435 
436     return aTxtSize;
437 }
438 
GetPhysTxtSize(const OutputDevice * pOut)439 Size SvxFont::GetPhysTxtSize( const OutputDevice *pOut )
440 {
441     if ( !IsCaseMap() && !IsKern() )
442         return Size( pOut->GetTextWidth( "" ), pOut->GetTextHeight() );
443 
444     Size aTxtSize;
445     aTxtSize.setHeight( pOut->GetTextHeight() );
446     if ( !IsCaseMap() )
447         aTxtSize.setWidth( pOut->GetTextWidth( "" ) );
448     else
449         aTxtSize.setWidth( pOut->GetTextWidth( CalcCaseMap( "" ) ) );
450 
451     return aTxtSize;
452 }
453 
QuickGetTextSize(const OutputDevice * pOut,const OUString & rTxt,const sal_Int32 nIdx,const sal_Int32 nLen,tools::Long * pDXArray) const454 Size SvxFont::QuickGetTextSize( const OutputDevice *pOut, const OUString &rTxt,
455                          const sal_Int32 nIdx, const sal_Int32 nLen, tools::Long* pDXArray ) const
456 {
457     if ( !IsCaseMap() && !IsKern() )
458         return Size( pOut->GetTextArray( rTxt, pDXArray, nIdx, nLen ),
459                      pOut->GetTextHeight() );
460 
461     Size aTxtSize;
462     aTxtSize.setHeight( pOut->GetTextHeight() );
463     if ( !IsCaseMap() )
464         aTxtSize.setWidth( pOut->GetTextArray( rTxt, pDXArray, nIdx, nLen ) );
465     else
466         aTxtSize.setWidth( pOut->GetTextArray( CalcCaseMap( rTxt ),
467                            pDXArray, nIdx, nLen ) );
468 
469     if( IsKern() && ( nLen > 1 ) )
470     {
471         aTxtSize.AdjustWidth( ( nLen-1 ) * tools::Long( nKern ) );
472 
473         if ( pDXArray )
474         {
475             for ( sal_Int32 i = 0; i < nLen; i++ )
476                 pDXArray[i] += ( (i+1) * tools::Long( nKern ) );
477             // The last one is a nKern too big:
478             pDXArray[nLen-1] -= nKern;
479         }
480     }
481     return aTxtSize;
482 }
483 
GetTextSize(const OutputDevice & rOut,const OUString & rTxt,const sal_Int32 nIdx,const sal_Int32 nLen) const484 Size SvxFont::GetTextSize(const OutputDevice& rOut, const OUString &rTxt,
485                           const sal_Int32 nIdx, const sal_Int32 nLen) const
486 {
487     sal_Int32 nTmp = nLen;
488     if ( nTmp == SAL_MAX_INT32 )   // already initialized?
489         nTmp = rTxt.getLength();
490     Font aOldFont( ChgPhysFont(const_cast<OutputDevice&>(rOut)));
491     Size aTxtSize;
492     if( IsCapital() && !rTxt.isEmpty() )
493     {
494         aTxtSize = GetCapitalSize(&rOut, rTxt, nIdx, nTmp);
495     }
496     else aTxtSize = GetPhysTxtSize(&rOut,rTxt,nIdx,nTmp);
497     const_cast<OutputDevice&>(rOut).SetFont(aOldFont);
498     return aTxtSize;
499 }
500 
QuickDrawText(OutputDevice * pOut,const Point & rPos,const OUString & rTxt,const sal_Int32 nIdx,const sal_Int32 nLen,const tools::Long * pDXArray) const501 void SvxFont::QuickDrawText( OutputDevice *pOut,
502     const Point &rPos, const OUString &rTxt,
503     const sal_Int32 nIdx, const sal_Int32 nLen, const tools::Long* pDXArray ) const
504 {
505 
506     // Font has to be selected in OutputDevice...
507     if ( !IsCaseMap() && !IsCapital() && !IsKern() && !IsEsc() )
508     {
509         pOut->DrawTextArray( rPos, rTxt, pDXArray, nIdx, nLen );
510         return;
511     }
512 
513     Point aPos( rPos );
514 
515     if ( nEsc )
516     {
517         tools::Long nDiff = GetFontSize().Height();
518         nDiff *= nEsc;
519         nDiff /= 100;
520 
521         if ( !IsVertical() )
522             aPos.AdjustY( -nDiff );
523         else
524             aPos.AdjustX(nDiff );
525     }
526 
527     if( IsCapital() )
528     {
529         DBG_ASSERT( !pDXArray, "DrawCapital not for TextArray!" );
530         DrawCapital( pOut, aPos, rTxt, nIdx, nLen );
531     }
532     else
533     {
534         if ( IsKern() && !pDXArray )
535         {
536             Size aSize = GetPhysTxtSize( pOut, rTxt, nIdx, nLen );
537 
538             if ( !IsCaseMap() )
539                 pOut->DrawStretchText( aPos, aSize.Width(), rTxt, nIdx, nLen );
540             else
541                 pOut->DrawStretchText( aPos, aSize.Width(), CalcCaseMap( rTxt ), nIdx, nLen );
542         }
543         else
544         {
545             if ( !IsCaseMap() )
546                 pOut->DrawTextArray( aPos, rTxt, pDXArray, nIdx, nLen );
547             else
548                 pOut->DrawTextArray( aPos, CalcCaseMap( rTxt ), pDXArray, nIdx, nLen );
549         }
550     }
551 }
552 
553 
DrawPrev(OutputDevice * pOut,Printer * pPrinter,const Point & rPos,const OUString & rTxt,const sal_Int32 nIdx,const sal_Int32 nLen) const554 void SvxFont::DrawPrev( OutputDevice *pOut, Printer* pPrinter,
555                         const Point &rPos, const OUString &rTxt,
556                         const sal_Int32 nIdx, const sal_Int32 nLen ) const
557 {
558     if ( !nLen || rTxt.isEmpty() )
559         return;
560     sal_Int32 nTmp = nLen;
561 
562     if ( nTmp == SAL_MAX_INT32 )   // already initialized?
563         nTmp = rTxt.getLength();
564     Point aPos( rPos );
565 
566     if ( nEsc )
567     {
568         short nTmpEsc;
569         if( DFLT_ESC_AUTO_SUPER == nEsc )
570         {
571             nTmpEsc = .8 * (100 - nPropr);
572             assert (nTmpEsc == DFLT_ESC_SUPER && "I'm sure this formula needs to be changed, but how to confirm that???");
573             nTmpEsc = DFLT_ESC_SUPER;
574         }
575         else if( DFLT_ESC_AUTO_SUB == nEsc )
576         {
577             nTmpEsc = .2 * -(100 - nPropr);
578             assert (nTmpEsc == -20 && "I'm sure this formula needs to be changed, but how to confirm that???");
579             nTmpEsc = -20;
580         }
581         else
582             nTmpEsc = nEsc;
583         Size aSize = GetFontSize();
584         aPos.AdjustY( -(( nTmpEsc * aSize.Height() ) / 100) );
585     }
586     Font aOldFont( ChgPhysFont(*pOut) );
587     Font aOldPrnFont( ChgPhysFont(*pPrinter) );
588 
589     if ( IsCapital() )
590         DrawCapital( pOut, aPos, rTxt, nIdx, nTmp );
591     else
592     {
593         Size aSize = GetPhysTxtSize( pPrinter, rTxt, nIdx, nTmp );
594 
595         if ( !IsCaseMap() )
596             pOut->DrawStretchText( aPos, aSize.Width(), rTxt, nIdx, nTmp );
597         else
598         {
599             const OUString aNewText = CalcCaseMap(rTxt);
600             bool bCaseMapLengthDiffers(aNewText.getLength() != rTxt.getLength());
601 
602             if(bCaseMapLengthDiffers)
603             {
604                 // If strings differ work preparing the necessary snippet to address that
605                 // potential difference
606                 const OUString aSnippet(rTxt.copy( nIdx, nTmp));
607                 OUString _aNewText = CalcCaseMap(aSnippet);
608 
609                 pOut->DrawStretchText( aPos, aSize.Width(), _aNewText, 0, _aNewText.getLength() );
610             }
611             else
612             {
613                 pOut->DrawStretchText( aPos, aSize.Width(), CalcCaseMap( rTxt ), nIdx, nTmp );
614             }
615         }
616     }
617     pOut->SetFont(aOldFont);
618     pPrinter->SetFont( aOldPrnFont );
619 }
620 
621 
operator =(const vcl::Font & rFont)622 SvxFont& SvxFont::operator=( const vcl::Font& rFont )
623 {
624     Font::operator=( rFont );
625     return *this;
626 }
627 
operator =(const SvxFont & rFont)628 SvxFont& SvxFont::operator=( const SvxFont& rFont )
629 {
630     Font::operator=( rFont );
631     eCaseMap = rFont.eCaseMap;
632     nEsc = rFont.nEsc;
633     nPropr = rFont.nPropr;
634     nKern = rFont.nKern;
635     return *this;
636 }
637 
638 namespace {
639 
640 class SvxDoGetCapitalSize : public SvxDoCapitals
641 {
642 protected:
643     SvxFont*    pFont;
644     Size        aTxtSize;
645     short       nKern;
646 public:
SvxDoGetCapitalSize(SvxFont * _pFnt,const OutputDevice * _pOut,const OUString & _rTxt,const sal_Int32 _nIdx,const sal_Int32 _nLen,const short _nKrn)647       SvxDoGetCapitalSize( SvxFont *_pFnt, const OutputDevice *_pOut,
648                            const OUString &_rTxt, const sal_Int32 _nIdx,
649                            const sal_Int32 _nLen, const short _nKrn )
650             : SvxDoCapitals( const_cast<OutputDevice*>(_pOut), _rTxt, _nIdx, _nLen ),
651               pFont( _pFnt ),
652               nKern( _nKrn )
653             { }
654 
655     virtual void Do( const OUString &rTxt, const sal_Int32 nIdx,
656                      const sal_Int32 nLen, const bool bUpper ) override;
657 
GetSize() const658     const Size &GetSize() const { return aTxtSize; };
659 };
660 
661 }
662 
Do(const OUString & _rTxt,const sal_Int32 _nIdx,const sal_Int32 _nLen,const bool bUpper)663 void SvxDoGetCapitalSize::Do( const OUString &_rTxt, const sal_Int32 _nIdx,
664                               const sal_Int32 _nLen, const bool bUpper )
665 {
666     Size aPartSize;
667     if ( !bUpper )
668     {
669         sal_uInt8 nProp = pFont->GetPropr();
670         pFont->SetProprRel( SMALL_CAPS_PERCENTAGE );
671         pFont->SetPhysFont( *pOut );
672         aPartSize.setWidth( pOut->GetTextWidth( _rTxt, _nIdx, _nLen ) );
673         aPartSize.setHeight( pOut->GetTextHeight() );
674         aTxtSize.setHeight( aPartSize.Height() );
675         pFont->SetPropr( nProp );
676         pFont->SetPhysFont( *pOut );
677     }
678     else
679     {
680         aPartSize.setWidth( pOut->GetTextWidth( _rTxt, _nIdx, _nLen ) );
681         aPartSize.setHeight( pOut->GetTextHeight() );
682     }
683     aTxtSize.AdjustWidth(aPartSize.Width() );
684     aTxtSize.AdjustWidth( _nLen * tools::Long( nKern ) );
685 }
686 
GetCapitalSize(const OutputDevice * pOut,const OUString & rTxt,const sal_Int32 nIdx,const sal_Int32 nLen) const687 Size SvxFont::GetCapitalSize( const OutputDevice *pOut, const OUString &rTxt,
688                              const sal_Int32 nIdx, const sal_Int32 nLen) const
689 {
690     // Start:
691     SvxDoGetCapitalSize aDo( const_cast<SvxFont *>(this), pOut, rTxt, nIdx, nLen, nKern );
692     DoOnCapitals( aDo );
693     Size aTxtSize( aDo.GetSize() );
694 
695     // End:
696     if( !aTxtSize.Height() )
697     {
698         aTxtSize.setWidth( 0 );
699         aTxtSize.setHeight( pOut->GetTextHeight() );
700     }
701     return aTxtSize;
702 }
703 
704 namespace {
705 
706 class SvxDoDrawCapital : public SvxDoCapitals
707 {
708 protected:
709     SvxFont *pFont;
710     Point aPos;
711     Point aSpacePos;
712     short nKern;
713 public:
SvxDoDrawCapital(SvxFont * pFnt,OutputDevice * _pOut,const OUString & _rTxt,const sal_Int32 _nIdx,const sal_Int32 _nLen,const Point & rPos,const short nKrn)714     SvxDoDrawCapital( SvxFont *pFnt, OutputDevice *_pOut, const OUString &_rTxt,
715                       const sal_Int32 _nIdx, const sal_Int32 _nLen,
716                       const Point &rPos, const short nKrn )
717         : SvxDoCapitals( _pOut, _rTxt, _nIdx, _nLen ),
718           pFont( pFnt ),
719           aPos( rPos ),
720           aSpacePos( rPos ),
721           nKern( nKrn )
722         { }
723     virtual void DoSpace( const bool bDraw ) override;
724     virtual void SetSpace() override;
725     virtual void Do( const OUString &rTxt, const sal_Int32 nIdx,
726                      const sal_Int32 nLen, const bool bUpper ) override;
727 };
728 
729 }
730 
DoSpace(const bool bDraw)731 void SvxDoDrawCapital::DoSpace( const bool bDraw )
732 {
733     if ( !(bDraw || pFont->IsWordLineMode()) )
734         return;
735 
736     sal_uLong nDiff = static_cast<sal_uLong>(aPos.X() - aSpacePos.X());
737     if ( nDiff )
738     {
739         bool bWordWise = pFont->IsWordLineMode();
740         bool bTrans = pFont->IsTransparent();
741         pFont->SetWordLineMode( false );
742         pFont->SetTransparent( true );
743         pFont->SetPhysFont(*pOut);
744         pOut->DrawStretchText( aSpacePos, nDiff, "  ", 0, 2 );
745         pFont->SetWordLineMode( bWordWise );
746         pFont->SetTransparent( bTrans );
747         pFont->SetPhysFont(*pOut);
748     }
749 }
750 
SetSpace()751 void SvxDoDrawCapital::SetSpace()
752 {
753     if ( pFont->IsWordLineMode() )
754         aSpacePos.setX( aPos.X() );
755 }
756 
Do(const OUString & _rTxt,const sal_Int32 _nIdx,const sal_Int32 _nLen,const bool bUpper)757 void SvxDoDrawCapital::Do( const OUString &_rTxt, const sal_Int32 _nIdx,
758                            const sal_Int32 _nLen, const bool bUpper)
759 {
760     sal_uInt8 nProp = 0;
761     Size aPartSize;
762 
763     // Set the desired font
764     FontLineStyle eUnder = pFont->GetUnderline();
765     FontStrikeout eStrike = pFont->GetStrikeout();
766     pFont->SetUnderline( LINESTYLE_NONE );
767     pFont->SetStrikeout( STRIKEOUT_NONE );
768     if ( !bUpper )
769     {
770         nProp = pFont->GetPropr();
771         pFont->SetProprRel( SMALL_CAPS_PERCENTAGE );
772     }
773     pFont->SetPhysFont(*pOut);
774 
775     aPartSize.setWidth( pOut->GetTextWidth( _rTxt, _nIdx, _nLen ) );
776     aPartSize.setHeight( pOut->GetTextHeight() );
777     tools::Long nWidth = aPartSize.Width();
778     if ( nKern )
779     {
780         aPos.AdjustX(nKern/2);
781         if ( _nLen ) nWidth += (_nLen*tools::Long(nKern));
782     }
783     pOut->DrawStretchText(aPos,nWidth-nKern,_rTxt,_nIdx,_nLen);
784 
785     // Restore Font
786     pFont->SetUnderline( eUnder );
787     pFont->SetStrikeout( eStrike );
788     if ( !bUpper )
789         pFont->SetPropr( nProp );
790     pFont->SetPhysFont(*pOut);
791 
792     aPos.AdjustX(nWidth-(nKern/2) );
793 }
794 
795 /*************************************************************************
796  * SvxFont::DrawCapital() draws the uppercase letter.
797  *************************************************************************/
798 
DrawCapital(OutputDevice * pOut,const Point & rPos,const OUString & rTxt,const sal_Int32 nIdx,const sal_Int32 nLen) const799 void SvxFont::DrawCapital( OutputDevice *pOut,
800                const Point &rPos, const OUString &rTxt,
801                const sal_Int32 nIdx, const sal_Int32 nLen ) const
802 {
803     SvxDoDrawCapital aDo( const_cast<SvxFont *>(this),pOut,rTxt,nIdx,nLen,rPos,nKern );
804     DoOnCapitals( aDo );
805 }
806 
807 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
808