xref: /core/vcl/source/outdev/font.cxx (revision 55f07d4d)
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 <i18nlangtag/mslangid.hxx>
21 
22 #include <unotools/configmgr.hxx>
23 #include <vcl/virdev.hxx>
24 #include <vcl/print.hxx>
25 #include <vcl/sysdata.hxx>
26 #include <vcl/fontcharmap.hxx>
27 
28 #include <sallayout.hxx>
29 #include <salgdi.hxx>
30 #include <svdata.hxx>
31 
32 #include <outdev.h>
33 #include <window.h>
34 
35 #include <PhysicalFontCollection.hxx>
36 
37 #include <strings.hrc>
38 
39 FontMetric OutputDevice::GetDevFont( int nDevFontIndex ) const
40 {
41     FontMetric aFontMetric;
42 
43     ImplInitFontList();
44 
45     int nCount = GetDevFontCount();
46     if( nDevFontIndex < nCount )
47     {
48         const PhysicalFontFace& rData = *mpDeviceFontList->Get( nDevFontIndex );
49         aFontMetric.SetFamilyName( rData.GetFamilyName() );
50         aFontMetric.SetStyleName( rData.GetStyleName() );
51         aFontMetric.SetCharSet( rData.GetCharSet() );
52         aFontMetric.SetFamily( rData.GetFamilyType() );
53         aFontMetric.SetPitch( rData.GetPitch() );
54         aFontMetric.SetWeight( rData.GetWeight() );
55         aFontMetric.SetItalic( rData.GetItalic() );
56         aFontMetric.SetAlignment( TextAlign::ALIGN_TOP );
57         aFontMetric.SetWidthType( rData.GetWidthType() );
58         aFontMetric.SetQuality( rData.GetQuality() );
59         aFontMetric.SetMapNames( rData.GetMapNames() );
60     }
61 
62     return aFontMetric;
63 }
64 
65 int OutputDevice::GetDevFontCount() const
66 {
67     if( !mpDeviceFontList )
68     {
69         if (!mpFontCollection)
70         {
71             return 0;
72         }
73 
74         mpDeviceFontList = mpFontCollection->GetDeviceFontList();
75 
76         if (!mpDeviceFontList->Count())
77         {
78             delete mpDeviceFontList;
79             mpDeviceFontList = nullptr;
80 
81             return 0;
82         }
83     }
84     return mpDeviceFontList->Count();
85 }
86 
87 bool OutputDevice::IsFontAvailable( const OUString& rFontName ) const
88 {
89     PhysicalFontFamily* pFound = mpFontCollection->FindFontFamily( rFontName );
90     return (pFound != nullptr);
91 }
92 
93 int OutputDevice::GetDevFontSizeCount( const vcl::Font& rFont ) const
94 {
95     delete mpDeviceFontSizeList;
96 
97     ImplInitFontList();
98     mpDeviceFontSizeList = mpFontCollection->GetDeviceFontSizeList( rFont.GetFamilyName() );
99     return mpDeviceFontSizeList->Count();
100 }
101 
102 Size OutputDevice::GetDevFontSize( const vcl::Font& rFont, int nSizeIndex ) const
103 {
104     // check range
105     int nCount = GetDevFontSizeCount( rFont );
106     if ( nSizeIndex >= nCount )
107         return Size();
108 
109     // when mapping is enabled round to .5 points
110     Size aSize( 0, mpDeviceFontSizeList->Get( nSizeIndex ) );
111     if ( mbMap )
112     {
113         aSize.Height() *= 10;
114         MapMode aMap( MapUnit::Map10thInch, Point(), Fraction( 1, 72 ), Fraction( 1, 72 ) );
115         aSize = PixelToLogic( aSize, aMap );
116         aSize.Height() += 5;
117         aSize.Height() /= 10;
118         long nRound = aSize.Height() % 5;
119         if ( nRound >= 3 )
120             aSize.Height() += (5-nRound);
121         else
122             aSize.Height() -= nRound;
123         aSize.Height() *= 10;
124         aSize = LogicToPixel( aSize, aMap );
125         aSize = PixelToLogic( aSize );
126         aSize.Height() += 5;
127         aSize.Height() /= 10;
128     }
129     return aSize;
130 }
131 
132 namespace
133 {
134     struct UpdateFontsGuard
135     {
136         UpdateFontsGuard()
137         {
138             OutputDevice::ImplClearAllFontData(true);
139         }
140 
141         ~UpdateFontsGuard()
142         {
143             OutputDevice::ImplRefreshAllFontData(true);
144         }
145     };
146 }
147 
148 bool OutputDevice::AddTempDevFont( const OUString& rFileURL, const OUString& rFontName )
149 {
150     UpdateFontsGuard aUpdateFontsGuard;
151 
152     ImplInitFontList();
153 
154     if( !mpGraphics && !AcquireGraphics() )
155         return false;
156 
157     bool bRC = mpGraphics->AddTempDevFont( mpFontCollection, rFileURL, rFontName );
158     if( !bRC )
159         return false;
160 
161     if( mpAlphaVDev )
162         mpAlphaVDev->AddTempDevFont( rFileURL, rFontName );
163 
164     return true;
165 }
166 
167 FontMetric OutputDevice::GetFontMetric() const
168 {
169     FontMetric aMetric;
170     if( mbNewFont && !ImplNewFont() )
171         return aMetric;
172 
173     LogicalFontInstance* pFontInstance = mpFontInstance;
174     ImplFontMetricDataRef xFontMetric = pFontInstance->mxFontMetric;
175 
176     // prepare metric
177     aMetric.Font::operator=( maFont );
178 
179     // set aMetric with info from font
180     aMetric.SetFamilyName( maFont.GetFamilyName() );
181     aMetric.SetStyleName( xFontMetric->GetStyleName() );
182     aMetric.SetFontSize( PixelToLogic( Size( xFontMetric->GetWidth(), xFontMetric->GetAscent() + xFontMetric->GetDescent() - xFontMetric->GetInternalLeading() ) ) );
183     aMetric.SetCharSet( xFontMetric->IsSymbolFont() ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UNICODE );
184     aMetric.SetFamily( xFontMetric->GetFamilyType() );
185     aMetric.SetPitch( xFontMetric->GetPitch() );
186     aMetric.SetWeight( xFontMetric->GetWeight() );
187     aMetric.SetItalic( xFontMetric->GetItalic() );
188     aMetric.SetAlignment( TextAlign::ALIGN_TOP );
189     aMetric.SetWidthType( xFontMetric->GetWidthType() );
190     if ( pFontInstance->mnOwnOrientation )
191         aMetric.SetOrientation( pFontInstance->mnOwnOrientation );
192     else
193         aMetric.SetOrientation( xFontMetric->GetOrientation() );
194 
195     // set remaining metric fields
196     aMetric.SetFullstopCenteredFlag( xFontMetric->IsFullstopCentered() );
197     aMetric.SetBulletOffset( xFontMetric->GetBulletOffset() );
198     aMetric.SetAscent( ImplDevicePixelToLogicHeight( xFontMetric->GetAscent() + mnEmphasisAscent ) );
199     aMetric.SetDescent( ImplDevicePixelToLogicHeight( xFontMetric->GetDescent() + mnEmphasisDescent ) );
200     aMetric.SetInternalLeading( ImplDevicePixelToLogicHeight( xFontMetric->GetInternalLeading() + mnEmphasisAscent ) );
201     // OutputDevice has its own external leading function due to #i60945#
202     aMetric.SetExternalLeading( ImplDevicePixelToLogicHeight( GetFontExtLeading() ) );
203     aMetric.SetLineHeight( ImplDevicePixelToLogicHeight( xFontMetric->GetAscent() + xFontMetric->GetDescent() + mnEmphasisAscent + mnEmphasisDescent ) );
204     aMetric.SetSlant( ImplDevicePixelToLogicHeight( xFontMetric->GetSlant() ) );
205 
206     // get miscellaneous data
207     aMetric.SetQuality( xFontMetric->GetQuality() );
208     aMetric.SetMapNames( xFontMetric->GetMapNames() );
209 
210     SAL_INFO("vcl.gdi.fontmetric", "OutputDevice::GetFontMetric:" << aMetric);
211 
212     xFontMetric = nullptr;
213 
214     return aMetric;
215 }
216 
217 FontMetric OutputDevice::GetFontMetric( const vcl::Font& rFont ) const
218 {
219     // select font, query metrics, select original font again
220     vcl::Font aOldFont = GetFont();
221     const_cast<OutputDevice*>(this)->SetFont( rFont );
222     FontMetric aMetric( GetFontMetric() );
223     const_cast<OutputDevice*>(this)->SetFont( aOldFont );
224     return aMetric;
225 }
226 
227 bool OutputDevice::GetFontCharMap( FontCharMapRef& rxFontCharMap ) const
228 {
229     // we need a graphics
230     if( !mpGraphics && !AcquireGraphics() )
231         return false;
232 
233     if( mbNewFont )
234         ImplNewFont();
235     if( mbInitFont )
236         InitFont();
237     if( !mpFontInstance )
238         return false;
239 
240     FontCharMapRef xFontCharMap ( mpGraphics->GetFontCharMap() );
241     if (!xFontCharMap.is())
242     {
243         FontCharMapRef xDefaultMap( new FontCharMap() );
244         rxFontCharMap = xDefaultMap;
245     }
246     else
247         rxFontCharMap = xFontCharMap;
248 
249     return !rxFontCharMap->IsDefaultMap();
250 }
251 
252 bool OutputDevice::GetFontCapabilities( vcl::FontCapabilities& rFontCapabilities ) const
253 {
254     // we need a graphics
255     if( !mpGraphics && !AcquireGraphics() )
256         return false;
257 
258     if( mbNewFont )
259         ImplNewFont();
260     if( mbInitFont )
261         InitFont();
262     if( !mpFontInstance )
263         return false;
264 
265     return mpGraphics->GetFontCapabilities(rFontCapabilities);
266 }
267 
268 #if ENABLE_CAIRO_CANVAS
269 
270 SystemFontData OutputDevice::GetSysFontData(int nFallbacklevel) const
271 {
272     SystemFontData aSysFontData;
273 
274     if (!mpGraphics)
275         (void) AcquireGraphics();
276 
277     if (mpGraphics)
278         aSysFontData = mpGraphics->GetSysFontData(nFallbacklevel);
279 
280     return aSysFontData;
281 }
282 
283 #endif // ENABLE_CAIRO_CANVAS
284 
285 void OutputDevice::ImplGetEmphasisMark( tools::PolyPolygon& rPolyPoly, bool& rPolyLine,
286                                         tools::Rectangle& rRect1, tools::Rectangle& rRect2,
287                                         long& rYOff, long& rWidth,
288                                         FontEmphasisMark eEmphasis,
289                                         long nHeight )
290 {
291     static const PolyFlags aAccentPolyFlags[24] =
292     {
293         PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control,
294         PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control,
295         PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control,
296         PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control,
297         PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control,
298         PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control,
299         PolyFlags::Normal, PolyFlags::Normal,  PolyFlags::Control,
300         PolyFlags::Normal, PolyFlags::Control, PolyFlags::Control
301     };
302 
303     static const long aAccentPos[48] =
304     {
305          78,      0,
306         348,     79,
307         599,    235,
308         843,    469,
309         938,    574,
310         990,    669,
311         990,    773,
312         990,    843,
313         964,    895,
314         921,    947,
315         886,    982,
316         860,    999,
317         825,    999,
318         764,    999,
319         721,    964,
320         686,    895,
321         625,    791,
322         556,    660,
323         469,    504,
324         400,    400,
325         261,    252,
326          61,     61,
327           0,     27,
328           9,      0
329     };
330 
331     rWidth      = 0;
332     rYOff       = 0;
333     rPolyLine   = false;
334 
335     if ( !nHeight )
336         return;
337 
338     FontEmphasisMark    nEmphasisStyle = eEmphasis & FontEmphasisMark::Style;
339     long                nDotSize = 0;
340     switch ( nEmphasisStyle )
341     {
342         case FontEmphasisMark::Dot:
343             // Dot has 55% of the height
344             nDotSize = (nHeight*550)/1000;
345             if ( !nDotSize )
346                 nDotSize = 1;
347             if ( nDotSize <= 2 )
348                 rRect1 = tools::Rectangle( Point(), Size( nDotSize, nDotSize ) );
349             else
350             {
351                 long nRad = nDotSize/2;
352                 tools::Polygon aPoly( Point( nRad, nRad ), nRad, nRad );
353                 rPolyPoly.Insert( aPoly );
354             }
355             rYOff = ((nHeight*250)/1000)/2; // Center to the another EmphasisMarks
356             rWidth = nDotSize;
357             break;
358 
359         case FontEmphasisMark::Circle:
360             // Dot has 80% of the height
361             nDotSize = (nHeight*800)/1000;
362             if ( !nDotSize )
363                 nDotSize = 1;
364             if ( nDotSize <= 2 )
365                 rRect1 = tools::Rectangle( Point(), Size( nDotSize, nDotSize ) );
366             else
367             {
368                 long nRad = nDotSize/2;
369                 tools::Polygon aPoly( Point( nRad, nRad ), nRad, nRad );
370                 rPolyPoly.Insert( aPoly );
371                 // BorderWidth is 15%
372                 long nBorder = (nDotSize*150)/1000;
373                 if ( nBorder <= 1 )
374                     rPolyLine = true;
375                 else
376                 {
377                     tools::Polygon aPoly2( Point( nRad, nRad ),
378                                            nRad-nBorder, nRad-nBorder );
379                     rPolyPoly.Insert( aPoly2 );
380                 }
381             }
382             rWidth = nDotSize;
383             break;
384 
385         case FontEmphasisMark::Disc:
386             // Dot has 80% of the height
387             nDotSize = (nHeight*800)/1000;
388             if ( !nDotSize )
389                 nDotSize = 1;
390             if ( nDotSize <= 2 )
391                 rRect1 = tools::Rectangle( Point(), Size( nDotSize, nDotSize ) );
392             else
393             {
394                 long nRad = nDotSize/2;
395                 tools::Polygon aPoly( Point( nRad, nRad ), nRad, nRad );
396                 rPolyPoly.Insert( aPoly );
397             }
398             rWidth = nDotSize;
399             break;
400 
401         case FontEmphasisMark::Accent:
402             // Dot has 80% of the height
403             nDotSize = (nHeight*800)/1000;
404             if ( !nDotSize )
405                 nDotSize = 1;
406             if ( nDotSize <= 2 )
407             {
408                 if ( nDotSize == 1 )
409                 {
410                     rRect1 = tools::Rectangle( Point(), Size( nDotSize, nDotSize ) );
411                     rWidth = nDotSize;
412                 }
413                 else
414                 {
415                     rRect1 = tools::Rectangle( Point(), Size( 1, 1 ) );
416                     rRect2 = tools::Rectangle( Point( 1, 1 ), Size( 1, 1 ) );
417                 }
418             }
419             else
420             {
421                 tools::Polygon aPoly( SAL_N_ELEMENTS( aAccentPos ) / 2,
422                                       reinterpret_cast<const Point*>(aAccentPos),
423                                       aAccentPolyFlags );
424                 double dScale = static_cast<double>(nDotSize)/1000.0;
425                 aPoly.Scale( dScale, dScale );
426                 tools::Polygon aTemp;
427                 aPoly.AdaptiveSubdivide( aTemp );
428                 tools::Rectangle aBoundRect = aTemp.GetBoundRect();
429                 rWidth = aBoundRect.GetWidth();
430                 nDotSize = aBoundRect.GetHeight();
431                 rPolyPoly.Insert( aTemp );
432             }
433             break;
434         default: break;
435     }
436 
437     // calculate position
438     long nOffY = 1+(mnDPIY/300); // one visible pixel space
439     long nSpaceY = nHeight-nDotSize;
440     if ( nSpaceY >= nOffY*2 )
441         rYOff += nOffY;
442     if ( !(eEmphasis & FontEmphasisMark::PosBelow) )
443         rYOff += nDotSize;
444 }
445 
446 FontEmphasisMark OutputDevice::ImplGetEmphasisMarkStyle( const vcl::Font& rFont )
447 {
448     FontEmphasisMark nEmphasisMark = rFont.GetEmphasisMark();
449 
450     // If no Position is set, then calculate the default position, which
451     // depends on the language
452     if ( !(nEmphasisMark & (FontEmphasisMark::PosAbove | FontEmphasisMark::PosBelow)) )
453     {
454         LanguageType eLang = rFont.GetLanguage();
455         // In Chinese Simplified the EmphasisMarks are below/left
456         if (MsLangId::isSimplifiedChinese(eLang))
457             nEmphasisMark |= FontEmphasisMark::PosBelow;
458         else
459         {
460             eLang = rFont.GetCJKContextLanguage();
461             // In Chinese Simplified the EmphasisMarks are below/left
462             if (MsLangId::isSimplifiedChinese(eLang))
463                 nEmphasisMark |= FontEmphasisMark::PosBelow;
464             else
465                 nEmphasisMark |= FontEmphasisMark::PosAbove;
466         }
467     }
468 
469     return nEmphasisMark;
470 }
471 
472 long OutputDevice::GetFontExtLeading() const
473 {
474     return mpFontInstance->mxFontMetric->GetExternalLeading();
475 }
476 
477 void OutputDevice::ImplClearFontData( const bool bNewFontLists )
478 {
479     // the currently selected logical font is no longer needed
480     if ( mpFontInstance )
481     {
482         mpFontInstance->Release();
483         mpFontInstance = nullptr;
484     }
485 
486     mbInitFont = true;
487     mbNewFont = true;
488 
489     if ( bNewFontLists )
490     {
491         if ( mpDeviceFontList )
492         {
493             delete mpDeviceFontList;
494             mpDeviceFontList = nullptr;
495         }
496         if ( mpDeviceFontSizeList )
497         {
498             delete mpDeviceFontSizeList;
499             mpDeviceFontSizeList = nullptr;
500         }
501 
502         // release all physically selected fonts on this device
503         if( AcquireGraphics() )
504             mpGraphics->ReleaseFonts();
505     }
506 
507 //    if ( GetOutDevType() == OUTDEV_PRINTER || mpPDFWriter )
508     {
509         ImplSVData* pSVData = ImplGetSVData();
510 
511         if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache )
512             mpFontCache->Invalidate();
513 
514         if ( bNewFontLists )
515         {
516             // we need a graphics
517             if ( AcquireGraphics() )
518             {
519                 if( mpFontCollection && mpFontCollection != pSVData->maGDIData.mpScreenFontList )
520                     mpFontCollection->Clear();
521 
522                 if( mpPDFWriter )
523                 {
524                     if( mpFontCollection && mpFontCollection != pSVData->maGDIData.mpScreenFontList )
525                         delete mpFontCollection;
526                     if( mpFontCache && mpFontCache != pSVData->maGDIData.mpScreenFontCache )
527                         delete mpFontCache;
528                     mpFontCollection = nullptr;
529                     mpFontCache = nullptr;
530                 }
531             }
532         }
533     }
534 
535     // also update child windows if needed
536     if ( GetOutDevType() == OUTDEV_WINDOW )
537     {
538         vcl::Window* pChild = static_cast<vcl::Window*>(this)->mpWindowImpl->mpFirstChild;
539         while ( pChild )
540         {
541             pChild->ImplClearFontData( true );
542             pChild = pChild->mpWindowImpl->mpNext;
543         }
544     }
545 }
546 
547 void OutputDevice::RefreshFontData( const bool bNewFontLists )
548 {
549     ImplRefreshFontData( bNewFontLists );
550 }
551 
552 void OutputDevice::ImplRefreshFontData( const bool bNewFontLists )
553 {
554 //    if ( GetOutDevType() == OUTDEV_PRINTER || mpPDFWriter )
555     {
556         ImplSVData* pSVData = ImplGetSVData();
557 
558         if ( bNewFontLists )
559         {
560             // we need a graphics
561             if ( AcquireGraphics() )
562             {
563                 if( mpPDFWriter )
564                 {
565                     mpFontCollection = pSVData->maGDIData.mpScreenFontList->Clone();
566                     mpFontCache = new ImplFontCache();
567                 }
568                 else
569                 {
570                     mpGraphics->GetDevFontList( mpFontCollection );
571                 }
572             }
573         }
574     }
575 
576     // also update child windows if needed
577     if ( GetOutDevType() == OUTDEV_WINDOW )
578     {
579         vcl::Window* pChild = static_cast<vcl::Window*>(this)->mpWindowImpl->mpFirstChild;
580         while ( pChild )
581         {
582             pChild->ImplRefreshFontData( true );
583             pChild = pChild->mpWindowImpl->mpNext;
584         }
585     }
586 }
587 
588 void OutputDevice::ImplUpdateFontData()
589 {
590     ImplClearFontData( true/*bNewFontLists*/ );
591     ImplRefreshFontData( true/*bNewFontLists*/ );
592 }
593 
594 void OutputDevice::ImplClearAllFontData(bool bNewFontLists)
595 {
596     ImplSVData* pSVData = ImplGetSVData();
597 
598     ImplUpdateFontDataForAllFrames( &OutputDevice::ImplClearFontData, bNewFontLists );
599 
600     // clear global font lists to have them updated
601     pSVData->maGDIData.mpScreenFontCache->Invalidate();
602     if ( bNewFontLists )
603     {
604         pSVData->maGDIData.mpScreenFontList->Clear();
605         vcl::Window * pFrame = pSVData->maWinData.mpFirstFrame;
606         if ( pFrame )
607         {
608             if ( pFrame->AcquireGraphics() )
609             {
610                 OutputDevice *pDevice = pFrame;
611                 pDevice->mpGraphics->ClearDevFontCache();
612                 pDevice->mpGraphics->GetDevFontList(pFrame->mpWindowImpl->mpFrameData->mpFontCollection);
613             }
614         }
615     }
616 }
617 
618 void OutputDevice::ImplRefreshAllFontData(bool bNewFontLists)
619 {
620     ImplUpdateFontDataForAllFrames( &OutputDevice::ImplRefreshFontData, bNewFontLists );
621 }
622 
623 void OutputDevice::ImplUpdateAllFontData(bool bNewFontLists)
624 {
625     OutputDevice::ImplClearAllFontData(bNewFontLists);
626     OutputDevice::ImplRefreshAllFontData(bNewFontLists);
627 }
628 
629 void OutputDevice::ImplUpdateFontDataForAllFrames( const FontUpdateHandler_t pHdl, const bool bNewFontLists )
630 {
631     ImplSVData* const pSVData = ImplGetSVData();
632 
633     // update all windows
634     vcl::Window* pFrame = pSVData->maWinData.mpFirstFrame;
635     while ( pFrame )
636     {
637         ( pFrame->*pHdl )( bNewFontLists );
638 
639         vcl::Window* pSysWin = pFrame->mpWindowImpl->mpFrameData->mpFirstOverlap;
640         while ( pSysWin )
641         {
642             ( pSysWin->*pHdl )( bNewFontLists );
643             pSysWin = pSysWin->mpWindowImpl->mpNextOverlap;
644         }
645 
646         pFrame = pFrame->mpWindowImpl->mpFrameData->mpNextFrame;
647     }
648 
649     // update all virtual devices
650     VirtualDevice* pVirDev = pSVData->maGDIData.mpFirstVirDev;
651     while ( pVirDev )
652     {
653         ( pVirDev->*pHdl )( bNewFontLists );
654         pVirDev = pVirDev->mpNext;
655     }
656 
657     // update all printers
658     Printer* pPrinter = pSVData->maGDIData.mpFirstPrinter;
659     while ( pPrinter )
660     {
661         ( pPrinter->*pHdl )( bNewFontLists );
662         pPrinter = pPrinter->mpNext;
663     }
664 }
665 
666 void OutputDevice::BeginFontSubstitution()
667 {
668     ImplSVData* pSVData = ImplGetSVData();
669     pSVData->maGDIData.mbFontSubChanged = false;
670 }
671 
672 void OutputDevice::EndFontSubstitution()
673 {
674     ImplSVData* pSVData = ImplGetSVData();
675     if ( pSVData->maGDIData.mbFontSubChanged )
676     {
677         ImplUpdateAllFontData( false );
678 
679         DataChangedEvent aDCEvt( DataChangedEventType::FONTSUBSTITUTION );
680         Application::NotifyAllWindows( aDCEvt );
681         pSVData->maGDIData.mbFontSubChanged = false;
682     }
683 }
684 
685 void OutputDevice::AddFontSubstitute( const OUString& rFontName,
686                                       const OUString& rReplaceFontName,
687                                       AddFontSubstituteFlags nFlags )
688 {
689     ImplDirectFontSubstitution*& rpSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
690     if( !rpSubst )
691         rpSubst = new ImplDirectFontSubstitution;
692     rpSubst->AddFontSubstitute( rFontName, rReplaceFontName, nFlags );
693     ImplGetSVData()->maGDIData.mbFontSubChanged = true;
694 }
695 
696 void ImplDirectFontSubstitution::AddFontSubstitute( const OUString& rFontName,
697     const OUString& rSubstFontName, AddFontSubstituteFlags nFlags )
698 {
699     maFontSubstList.emplace_back( rFontName, rSubstFontName, nFlags );
700 }
701 
702 ImplFontSubstEntry::ImplFontSubstEntry( const OUString& rFontName,
703     const OUString& rSubstFontName, AddFontSubstituteFlags nSubstFlags )
704 :   mnFlags( nSubstFlags )
705 {
706     maSearchName = GetEnglishSearchFontName( rFontName );
707     maSearchReplaceName = GetEnglishSearchFontName( rSubstFontName );
708 }
709 
710 void OutputDevice::RemoveFontsSubstitute()
711 {
712     ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
713     if( pSubst )
714         pSubst->RemoveFontsSubstitute();
715 }
716 
717 void ImplDirectFontSubstitution::RemoveFontsSubstitute()
718 {
719     maFontSubstList.clear();
720 }
721 
722 bool ImplDirectFontSubstitution::FindFontSubstitute( OUString& rSubstName,
723     const OUString& rSearchName ) const
724 {
725     // TODO: get rid of O(N) searches
726     std::vector<ImplFontSubstEntry>::const_iterator it = std::find_if (
727                          maFontSubstList.begin(), maFontSubstList.end(),
728                          [&] (const ImplFontSubstEntry& s) { return (s.mnFlags & AddFontSubstituteFlags::ALWAYS)
729                                    && (s.maSearchName == rSearchName); } );
730     if (it != maFontSubstList.end())
731     {
732         rSubstName = it->maSearchReplaceName;
733         return true;
734     }
735     return false;
736 }
737 
738 void ImplFontSubstitute( OUString& rFontName )
739 {
740     // must be canonicalised
741     assert( GetEnglishSearchFontName( rFontName ) == rFontName );
742 
743     OUString aSubstFontName;
744 
745     // apply user-configurable font replacement (eg, from the list in Tools->Options)
746     const ImplDirectFontSubstitution* pSubst = ImplGetSVData()->maGDIData.mpDirectFontSubst;
747     if( pSubst && pSubst->FindFontSubstitute( aSubstFontName, rFontName ) )
748     {
749         rFontName = aSubstFontName;
750         return;
751     }
752 }
753 
754 //hidpi TODO: This routine has hard-coded font-sizes that break places such as DialControl
755 vcl::Font OutputDevice::GetDefaultFont( DefaultFontType nType, LanguageType eLang,
756                                         GetDefaultFontFlags nFlags, const OutputDevice* pOutDev )
757 {
758     if (!pOutDev && !utl::ConfigManager::IsFuzzing()) // default is NULL
759         pOutDev = Application::GetDefaultDevice();
760 
761     OUString aSearch;
762     if (!utl::ConfigManager::IsFuzzing())
763     {
764         LanguageTag aLanguageTag(
765                 ( eLang == LANGUAGE_NONE || eLang == LANGUAGE_SYSTEM || eLang == LANGUAGE_DONTKNOW ) ?
766                 Application::GetSettings().GetUILanguageTag() :
767                 LanguageTag( eLang ));
768 
769         utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
770         OUString aDefault = rDefaults.getDefaultFont( aLanguageTag, nType );
771 
772         if( !aDefault.isEmpty() )
773             aSearch = aDefault;
774         else
775             aSearch = rDefaults.getUserInterfaceFont( aLanguageTag ); // use the UI font as a fallback
776     }
777     else
778         aSearch = "Liberation Serif";
779 
780     vcl::Font aFont;
781     aFont.SetPitch( PITCH_VARIABLE );
782 
783     switch ( nType )
784     {
785         case DefaultFontType::SANS_UNICODE:
786         case DefaultFontType::UI_SANS:
787             aFont.SetFamily( FAMILY_SWISS );
788             break;
789 
790         case DefaultFontType::SANS:
791         case DefaultFontType::LATIN_HEADING:
792         case DefaultFontType::LATIN_SPREADSHEET:
793         case DefaultFontType::LATIN_DISPLAY:
794             aFont.SetFamily( FAMILY_SWISS );
795             break;
796 
797         case DefaultFontType::SERIF:
798         case DefaultFontType::LATIN_TEXT:
799         case DefaultFontType::LATIN_PRESENTATION:
800             aFont.SetFamily( FAMILY_ROMAN );
801             break;
802 
803         case DefaultFontType::FIXED:
804         case DefaultFontType::LATIN_FIXED:
805         case DefaultFontType::UI_FIXED:
806             aFont.SetPitch( PITCH_FIXED );
807             aFont.SetFamily( FAMILY_MODERN );
808             break;
809 
810         case DefaultFontType::SYMBOL:
811             aFont.SetCharSet( RTL_TEXTENCODING_SYMBOL );
812             break;
813 
814         case DefaultFontType::CJK_TEXT:
815         case DefaultFontType::CJK_PRESENTATION:
816         case DefaultFontType::CJK_SPREADSHEET:
817         case DefaultFontType::CJK_HEADING:
818         case DefaultFontType::CJK_DISPLAY:
819             aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later...
820             break;
821 
822         case DefaultFontType::CTL_TEXT:
823         case DefaultFontType::CTL_PRESENTATION:
824         case DefaultFontType::CTL_SPREADSHEET:
825         case DefaultFontType::CTL_HEADING:
826         case DefaultFontType::CTL_DISPLAY:
827             aFont.SetFamily( FAMILY_SYSTEM ); // don't care, but don't use font subst config later...
828             break;
829     }
830 
831     if ( !aSearch.isEmpty() )
832     {
833         aFont.SetFontHeight( 12 ); // corresponds to nDefaultHeight
834         aFont.SetWeight( WEIGHT_NORMAL );
835         aFont.SetLanguage( eLang );
836 
837         if ( aFont.GetCharSet() == RTL_TEXTENCODING_DONTKNOW )
838             aFont.SetCharSet( osl_getThreadTextEncoding() );
839 
840         // Should we only return available fonts on the given device
841         if ( pOutDev )
842         {
843             pOutDev->ImplInitFontList();
844 
845             // Search Font in the FontList
846             OUString      aName;
847             sal_Int32     nIndex = 0;
848             do
849             {
850                 PhysicalFontFamily* pFontFamily = pOutDev->mpFontCollection->FindFontFamily( GetNextFontToken( aSearch, nIndex ) );
851                 if( pFontFamily )
852                 {
853                     AddTokenFontName( aName, pFontFamily->GetFamilyName() );
854                     if( nFlags & GetDefaultFontFlags::OnlyOne )
855                         break;
856                 }
857             }
858             while ( nIndex != -1 );
859             aFont.SetFamilyName( aName );
860         }
861 
862         // No Name, than set all names
863         if ( aFont.GetFamilyName().isEmpty() )
864         {
865             if ( nFlags & GetDefaultFontFlags::OnlyOne )
866             {
867                 if( !pOutDev )
868                 {
869                     SAL_WARN_IF(!utl::ConfigManager::IsFuzzing(), "vcl.gdi", "No default window has been set for the application - we really shouldn't be able to get here");
870                     sal_Int32 nIndex = 0;
871                     aFont.SetFamilyName( aSearch.getToken( 0, ';', nIndex ) );
872                 }
873                 else
874                 {
875                     pOutDev->ImplInitFontList();
876 
877                     aFont.SetFamilyName( aSearch );
878 
879                     // convert to pixel height
880                     Size aSize = pOutDev->ImplLogicToDevicePixel( aFont.GetFontSize() );
881                     if ( !aSize.Height() )
882                     {
883                         // use default pixel height only when logical height is zero
884                         if ( aFont.GetFontHeight() )
885                             aSize.Height() = 1;
886                         else
887                             aSize.Height() = (12*pOutDev->mnDPIY)/72;
888                     }
889 
890                     // use default width only when logical width is zero
891                     if( (0 == aSize.Width()) && (0 != aFont.GetFontSize().Width()) )
892                         aSize.Width() = 1;
893 
894                     // get the name of the first available font
895                     float fExactHeight = static_cast<float>(aSize.Height());
896                     LogicalFontInstance* pFontInstance = pOutDev->mpFontCache->GetFontInstance( pOutDev->mpFontCollection, aFont, aSize, fExactHeight );
897                     if (pFontInstance)
898                     {
899                         if( pFontInstance->maFontSelData.mpFontData )
900                             aFont.SetFamilyName( pFontInstance->maFontSelData.mpFontData->GetFamilyName() );
901                         else
902                             aFont.SetFamilyName( pFontInstance->maFontSelData.maTargetName );
903                         pFontInstance->Release();
904                     }
905                 }
906             }
907             else
908                 aFont.SetFamilyName( aSearch );
909         }
910     }
911 
912 #if OSL_DEBUG_LEVEL > 2
913     const char* s = "SANS_UNKNOWN";
914     switch ( nType )
915     {
916     case DefaultFontType::SANS_UNICODE: s = "SANS_UNICODE"; break;
917     case DefaultFontType::UI_SANS: s = "UI_SANS"; break;
918 
919     case DefaultFontType::SANS: s = "SANS"; break;
920     case DefaultFontType::LATIN_HEADING: s = "LATIN_HEADING"; break;
921     case DefaultFontType::LATIN_SPREADSHEET: s = "LATIN_SPREADSHEET"; break;
922     case DefaultFontType::LATIN_DISPLAY: s = "LATIN_DISPLAY"; break;
923 
924     case DefaultFontType::SERIF: s = "SERIF"; break;
925     case DefaultFontType::LATIN_TEXT: s = "LATIN_TEXT"; break;
926     case DefaultFontType::LATIN_PRESENTATION: s = "LATIN_PRESENTATION"; break;
927 
928     case DefaultFontType::FIXED: s = "FIXED"; break;
929     case DefaultFontType::LATIN_FIXED: s = "LATIN_FIXED"; break;
930     case DefaultFontType::UI_FIXED: s = "UI_FIXED"; break;
931 
932     case DefaultFontType::SYMBOL: s = "SYMBOL"; break;
933 
934     case DefaultFontType::CJK_TEXT: s = "CJK_TEXT"; break;
935     case DefaultFontType::CJK_PRESENTATION: s = "CJK_PRESENTATION"; break;
936     case DefaultFontType::CJK_SPREADSHEET: s = "CJK_SPREADSHEET"; break;
937     case DefaultFontType::CJK_HEADING: s = "CJK_HEADING"; break;
938     case DefaultFontType::CJK_DISPLAY: s = "CJK_DISPLAY"; break;
939 
940     case DefaultFontType::CTL_TEXT: s = "CTL_TEXT"; break;
941     case DefaultFontType::CTL_PRESENTATION: s = "CTL_PRESENTATION"; break;
942     case DefaultFontType::CTL_SPREADSHEET: s = "CTL_SPREADSHEET"; break;
943     case DefaultFontType::CTL_HEADING: s = "CTL_HEADING"; break;
944     case DefaultFontType::CTL_DISPLAY: s = "CTL_DISPLAY"; break;
945     }
946     SAL_INFO("vcl.gdi",
947              "OutputDevice::GetDefaultFont() Type=" << s
948              << " lang=" << eLang
949              << " flags=" << static_cast<int>(nFlags)
950              << " family=\"" << aFont.GetFamilyName() << "\"");
951 #endif
952 
953     return aFont;
954 }
955 
956 void OutputDevice::ImplInitFontList() const
957 {
958     if( !mpFontCollection->Count() )
959     {
960         if( mpGraphics || AcquireGraphics() )
961         {
962             SAL_INFO( "vcl.gdi", "OutputDevice::ImplInitFontList()" );
963             mpGraphics->GetDevFontList( mpFontCollection );
964 
965             // There is absolutely no way there should be no fonts available on the device
966             if( !mpFontCollection->Count() )
967             {
968                 OUString aError( "Application error: no fonts and no vcl resource found on your system" );
969                 OUString aResStr(VclResId(SV_ACCESSERROR_NO_FONTS));
970                 if (!aResStr.isEmpty())
971                     aError = aResStr;
972                 Application::Abort(aError);
973             }
974         }
975     }
976 }
977 
978 void OutputDevice::InitFont() const
979 {
980     DBG_TESTSOLARMUTEX();
981 
982     if (!mpFontInstance)
983         return;
984 
985     if ( mbInitFont )
986     {
987         // decide if antialiasing is appropriate
988         bool bNonAntialiased(GetAntialiasing() & AntialiasingFlags::DisableText);
989         if (!utl::ConfigManager::IsFuzzing())
990         {
991             const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
992             bNonAntialiased |= bool(rStyleSettings.GetDisplayOptions() & DisplayOptions::AADisable);
993             bNonAntialiased |= (int(rStyleSettings.GetAntialiasingMinPixelHeight()) > mpFontInstance->maFontSelData.mnHeight);
994         }
995         mpFontInstance->maFontSelData.mbNonAntialiased = bNonAntialiased;
996 
997         // select font in the device layers
998         mpGraphics->SetFont( &(mpFontInstance->maFontSelData), 0 );
999         mbInitFont = false;
1000     }
1001 }
1002 
1003 bool OutputDevice::ImplNewFont() const
1004 {
1005     DBG_TESTSOLARMUTEX();
1006 
1007     // get correct font list on the PDF writer if necessary
1008     if( mpPDFWriter )
1009     {
1010         const ImplSVData* pSVData = ImplGetSVData();
1011         if( mpFontCollection == pSVData->maGDIData.mpScreenFontList
1012         ||  mpFontCache == pSVData->maGDIData.mpScreenFontCache )
1013             const_cast<OutputDevice&>(*this).ImplUpdateFontData();
1014     }
1015 
1016     if ( !mbNewFont )
1017         return true;
1018 
1019     // we need a graphics
1020     if ( !mpGraphics && !AcquireGraphics() )
1021     {
1022         SAL_WARN("vcl.gdi", "OutputDevice::ImplNewFont(): no Graphics, no Font");
1023         return false;
1024     }
1025     SalGraphics* pGraphics = mpGraphics;
1026     ImplInitFontList();
1027 
1028     // convert to pixel height
1029     // TODO: replace integer based aSize completely with subpixel accurate type
1030     float fExactHeight = ImplFloatLogicHeightToDevicePixel( static_cast<float>(maFont.GetFontHeight()) );
1031     Size aSize = ImplLogicToDevicePixel( maFont.GetFontSize() );
1032     if ( !aSize.Height() )
1033     {
1034         // use default pixel height only when logical height is zero
1035         if ( maFont.GetFontSize().Height() )
1036             aSize.Height() = 1;
1037         else
1038             aSize.Height() = (12*mnDPIY)/72;
1039         fExactHeight =  static_cast<float>(aSize.Height());
1040     }
1041 
1042     // select the default width only when logical width is zero
1043     if( (0 == aSize.Width()) && (0 != maFont.GetFontSize().Width()) )
1044         aSize.Width() = 1;
1045 
1046     // get font entry
1047     LogicalFontInstance* pOldFontInstance = mpFontInstance;
1048     mpFontInstance = mpFontCache->GetFontInstance( mpFontCollection, maFont, aSize, fExactHeight );
1049     if( pOldFontInstance )
1050         pOldFontInstance->Release();
1051 
1052     LogicalFontInstance* pFontInstance = mpFontInstance;
1053 
1054     if (!pFontInstance)
1055     {
1056         SAL_WARN("vcl.gdi", "OutputDevice::ImplNewFont(): no LogicalFontInstance, no Font");
1057         return false;
1058     }
1059 
1060     // mark when lower layers need to get involved
1061     mbNewFont = false;
1062     if( pFontInstance != pOldFontInstance )
1063         mbInitFont = true;
1064 
1065     // select font when it has not been initialized yet
1066     if ( !pFontInstance->mbInit )
1067     {
1068         InitFont();
1069 
1070         // get metric data from device layers
1071         if ( pGraphics )
1072         {
1073             pFontInstance->mbInit = true;
1074 
1075             pFontInstance->mxFontMetric->SetOrientation( sal::static_int_cast<short>(pFontInstance->maFontSelData.mnOrientation) );
1076             pGraphics->GetFontMetric( pFontInstance->mxFontMetric, 0 );
1077 
1078             pFontInstance->mxFontMetric->ImplInitTextLineSize( this );
1079             pFontInstance->mxFontMetric->ImplInitAboveTextLineSize();
1080             pFontInstance->mxFontMetric->ImplInitFlags( this );
1081 
1082             pFontInstance->mnLineHeight = pFontInstance->mxFontMetric->GetAscent() + pFontInstance->mxFontMetric->GetDescent();
1083 
1084             SetFontOrientation( pFontInstance );
1085         }
1086     }
1087 
1088     // calculate EmphasisArea
1089     mnEmphasisAscent = 0;
1090     mnEmphasisDescent = 0;
1091     if ( maFont.GetEmphasisMark() & FontEmphasisMark::Style )
1092     {
1093         FontEmphasisMark    nEmphasisMark = ImplGetEmphasisMarkStyle( maFont );
1094         long                nEmphasisHeight = (pFontInstance->mnLineHeight*250)/1000;
1095         if ( nEmphasisHeight < 1 )
1096             nEmphasisHeight = 1;
1097         if ( nEmphasisMark & FontEmphasisMark::PosBelow )
1098             mnEmphasisDescent = nEmphasisHeight;
1099         else
1100             mnEmphasisAscent = nEmphasisHeight;
1101     }
1102 
1103     // calculate text offset depending on TextAlignment
1104     TextAlign eAlign = maFont.GetAlignment();
1105     if ( eAlign == ALIGN_BASELINE )
1106     {
1107         mnTextOffX = 0;
1108         mnTextOffY = 0;
1109     }
1110     else if ( eAlign == ALIGN_TOP )
1111     {
1112         mnTextOffX = 0;
1113         mnTextOffY = +pFontInstance->mxFontMetric->GetAscent() + mnEmphasisAscent;
1114         if ( pFontInstance->mnOrientation )
1115         {
1116             Point aOriginPt(0, 0);
1117             aOriginPt.RotateAround( mnTextOffX, mnTextOffY, pFontInstance->mnOrientation );
1118         }
1119     }
1120     else // eAlign == ALIGN_BOTTOM
1121     {
1122         mnTextOffX = 0;
1123         mnTextOffY = -pFontInstance->mxFontMetric->GetDescent() + mnEmphasisDescent;
1124         if ( pFontInstance->mnOrientation )
1125         {
1126             Point aOriginPt(0, 0);
1127             aOriginPt.RotateAround( mnTextOffX, mnTextOffY, pFontInstance->mnOrientation );
1128         }
1129     }
1130 
1131     mbTextLines     = ((maFont.GetUnderline() != LINESTYLE_NONE) && (maFont.GetUnderline() != LINESTYLE_DONTKNOW)) ||
1132                       ((maFont.GetOverline()  != LINESTYLE_NONE) && (maFont.GetOverline()  != LINESTYLE_DONTKNOW)) ||
1133                       ((maFont.GetStrikeout() != STRIKEOUT_NONE) && (maFont.GetStrikeout() != STRIKEOUT_DONTKNOW));
1134     mbTextSpecial   = maFont.IsShadow() || maFont.IsOutline() ||
1135                       (maFont.GetRelief() != FontRelief::NONE);
1136 
1137 
1138     // #95414# fix for OLE objects which use scale factors very creatively
1139     if( mbMap && !aSize.Width() )
1140     {
1141         int nOrigWidth = pFontInstance->mxFontMetric->GetWidth();
1142         float fStretch = static_cast<float>(maMapRes.mnMapScNumX) * maMapRes.mnMapScDenomY;
1143         fStretch /= static_cast<float>(maMapRes.mnMapScNumY) * maMapRes.mnMapScDenomX;
1144         int nNewWidth = static_cast<int>(nOrigWidth * fStretch + 0.5);
1145         if( (nNewWidth != nOrigWidth) && (nNewWidth != 0) )
1146         {
1147             Size aOrigSize = maFont.GetFontSize();
1148             const_cast<vcl::Font&>(maFont).SetFontSize( Size( nNewWidth, aSize.Height() ) );
1149             mbMap = false;
1150             mbNewFont = true;
1151             ImplNewFont();  // recurse once using stretched width
1152             mbMap = true;
1153             const_cast<vcl::Font&>(maFont).SetFontSize( aOrigSize );
1154         }
1155     }
1156 
1157     return true;
1158 }
1159 
1160 void OutputDevice::SetFontOrientation( LogicalFontInstance* const pFontInstance ) const
1161 {
1162     if( pFontInstance->maFontSelData.mnOrientation && !pFontInstance->mxFontMetric->GetOrientation() )
1163     {
1164         pFontInstance->mnOwnOrientation = sal::static_int_cast<short>(pFontInstance->maFontSelData.mnOrientation);
1165         pFontInstance->mnOrientation = pFontInstance->mnOwnOrientation;
1166     }
1167     else
1168     {
1169         pFontInstance->mnOrientation = pFontInstance->mxFontMetric->GetOrientation();
1170     }
1171 }
1172 
1173 void OutputDevice::ImplDrawEmphasisMark( long nBaseX, long nX, long nY,
1174                                          const tools::PolyPolygon& rPolyPoly, bool bPolyLine,
1175                                          const tools::Rectangle& rRect1, const tools::Rectangle& rRect2 )
1176 {
1177     if( IsRTLEnabled() )
1178         nX = nBaseX - (nX - nBaseX - 1);
1179 
1180     nX -= mnOutOffX;
1181     nY -= mnOutOffY;
1182 
1183     if ( rPolyPoly.Count() )
1184     {
1185         if ( bPolyLine )
1186         {
1187             tools::Polygon aPoly = rPolyPoly.GetObject( 0 );
1188             aPoly.Move( nX, nY );
1189             DrawPolyLine( aPoly );
1190         }
1191         else
1192         {
1193             tools::PolyPolygon aPolyPoly = rPolyPoly;
1194             aPolyPoly.Move( nX, nY );
1195             DrawPolyPolygon( aPolyPoly );
1196         }
1197     }
1198 
1199     if ( !rRect1.IsEmpty() )
1200     {
1201         tools::Rectangle aRect( Point( nX+rRect1.Left(),
1202                                 nY+rRect1.Top() ), rRect1.GetSize() );
1203         DrawRect( aRect );
1204     }
1205 
1206     if ( !rRect2.IsEmpty() )
1207     {
1208         tools::Rectangle aRect( Point( nX+rRect2.Left(),
1209                                 nY+rRect2.Top() ), rRect2.GetSize() );
1210 
1211         DrawRect( aRect );
1212     }
1213 }
1214 
1215 void OutputDevice::ImplDrawEmphasisMarks( SalLayout& rSalLayout )
1216 {
1217     Color               aOldLineColor   = GetLineColor();
1218     Color               aOldFillColor   = GetFillColor();
1219     bool                bOldMap         = mbMap;
1220     GDIMetaFile*        pOldMetaFile    = mpMetaFile;
1221     mpMetaFile = nullptr;
1222     EnableMapMode( false );
1223 
1224     FontEmphasisMark    nEmphasisMark = ImplGetEmphasisMarkStyle( maFont );
1225     tools::PolyPolygon  aPolyPoly;
1226     tools::Rectangle           aRect1;
1227     tools::Rectangle           aRect2;
1228     long                nEmphasisYOff;
1229     long                nEmphasisWidth;
1230     long                nEmphasisHeight;
1231     bool                bPolyLine;
1232 
1233     if ( nEmphasisMark & FontEmphasisMark::PosBelow )
1234         nEmphasisHeight = mnEmphasisDescent;
1235     else
1236         nEmphasisHeight = mnEmphasisAscent;
1237 
1238     ImplGetEmphasisMark( aPolyPoly, bPolyLine,
1239                          aRect1, aRect2,
1240                          nEmphasisYOff, nEmphasisWidth,
1241                          nEmphasisMark,
1242                          nEmphasisHeight );
1243 
1244     if ( bPolyLine )
1245     {
1246         SetLineColor( GetTextColor() );
1247         SetFillColor();
1248     }
1249     else
1250     {
1251         SetLineColor();
1252         SetFillColor( GetTextColor() );
1253     }
1254 
1255     Point aOffset = Point(0,0);
1256 
1257     if ( nEmphasisMark & FontEmphasisMark::PosBelow )
1258         aOffset.Y() += mpFontInstance->mxFontMetric->GetDescent() + nEmphasisYOff;
1259     else
1260         aOffset.Y() -= mpFontInstance->mxFontMetric->GetAscent() + nEmphasisYOff;
1261 
1262     long nEmphasisWidth2  = nEmphasisWidth / 2;
1263     long nEmphasisHeight2 = nEmphasisHeight / 2;
1264     aOffset += Point( nEmphasisWidth2, nEmphasisHeight2 );
1265 
1266     Point aOutPoint;
1267     tools::Rectangle aRectangle;
1268     const GlyphItem* pGlyph;
1269     int nStart = 0;
1270     while (rSalLayout.GetNextGlyphs(1, &pGlyph, aOutPoint, nStart))
1271     {
1272         if (!mpGraphics->GetGlyphBoundRect(*pGlyph, aRectangle ) )
1273             continue;
1274 
1275         if (!pGlyph->IsSpacing())
1276         {
1277             Point aAdjPoint = aOffset;
1278             aAdjPoint.X() += aRectangle.Left() + (aRectangle.GetWidth() - nEmphasisWidth) / 2;
1279             if ( mpFontInstance->mnOrientation )
1280             {
1281                 Point aOriginPt(0, 0);
1282                 aOriginPt.RotateAround( aAdjPoint.X(), aAdjPoint.Y(), mpFontInstance->mnOrientation );
1283             }
1284             aOutPoint += aAdjPoint;
1285             aOutPoint -= Point( nEmphasisWidth2, nEmphasisHeight2 );
1286             ImplDrawEmphasisMark( rSalLayout.DrawBase().X(),
1287                                   aOutPoint.X(), aOutPoint.Y(),
1288                                   aPolyPoly, bPolyLine, aRect1, aRect2 );
1289         }
1290     }
1291 
1292     SetLineColor( aOldLineColor );
1293     SetFillColor( aOldFillColor );
1294     EnableMapMode( bOldMap );
1295     mpMetaFile = pOldMetaFile;
1296 }
1297 
1298 std::unique_ptr<SalLayout> OutputDevice::getFallbackFont(
1299     FontSelectPattern &rFontSelData, int nFallbackLevel,
1300     ImplLayoutArgs& rLayoutArgs) const
1301 {
1302     // we need a graphics
1303     if (!mpGraphics && !AcquireGraphics())
1304         return nullptr;
1305 
1306     assert(mpGraphics != nullptr);
1307     mpGraphics->SetFont( &rFontSelData, nFallbackLevel );
1308 
1309     rLayoutArgs.ResetPos();
1310     std::unique_ptr<SalLayout> pFallback = mpGraphics->GetTextLayout( rLayoutArgs, nFallbackLevel );
1311 
1312     if (!pFallback)
1313         return nullptr;
1314 
1315     if (!pFallback->LayoutText(rLayoutArgs))
1316     {
1317         // there is no need for a font that couldn't resolve anything
1318         return nullptr;
1319     }
1320 
1321     pFallback->AdjustLayout( rLayoutArgs );
1322 
1323     return pFallback;
1324 }
1325 
1326 std::unique_ptr<SalLayout> OutputDevice::ImplGlyphFallbackLayout( std::unique_ptr<SalLayout> pSalLayout, ImplLayoutArgs& rLayoutArgs ) const
1327 {
1328     // This function relies on a valid mpFontInstance, if it doesn't exist bail out
1329     // - we'd have crashed later on anyway. At least here we can catch the error in debug
1330     // mode.
1331     if ( !mpFontInstance )
1332     {
1333         SAL_WARN ("vcl.gdi", "No font entry set in OutputDevice");
1334         assert(mpFontInstance);
1335         return nullptr;
1336     }
1337 
1338     // prepare multi level glyph fallback
1339     std::unique_ptr<MultiSalLayout> pMultiSalLayout;
1340     ImplLayoutRuns aLayoutRuns = rLayoutArgs.maRuns;
1341     rLayoutArgs.PrepareFallback();
1342     rLayoutArgs.mnFlags |= SalLayoutFlags::ForFallback;
1343 
1344     // get list of code units that need glyph fallback
1345     int nCharPos = -1;
1346     bool bRTL = false;
1347     OUStringBuffer aMissingCodeBuf;
1348     while (rLayoutArgs.GetNextPos( &nCharPos, &bRTL))
1349         aMissingCodeBuf.append(rLayoutArgs.mrStr[nCharPos]);
1350     rLayoutArgs.ResetPos();
1351     OUString aMissingCodes = aMissingCodeBuf.makeStringAndClear();
1352 
1353     FontSelectPattern aFontSelData = mpFontInstance->maFontSelData;
1354 
1355     // try if fallback fonts support the missing code units
1356     for( int nFallbackLevel = 1; nFallbackLevel < MAX_FALLBACK; ++nFallbackLevel )
1357     {
1358         // find a font family suited for glyph fallback
1359         // GetGlyphFallbackFont() needs a valid aFontSelData.mpFontInstance
1360         // if the system-specific glyph fallback is active
1361         aFontSelData.mpFontInstance = mpFontInstance; // reset the fontinstance to base-level
1362 
1363         LogicalFontInstance* pFallbackFont = mpFontCache->GetGlyphFallbackFont( mpFontCollection,
1364             aFontSelData, nFallbackLevel, aMissingCodes );
1365         if( !pFallbackFont )
1366             break;
1367 
1368         aFontSelData.mpFontInstance = pFallbackFont;
1369         aFontSelData.mpFontData = pFallbackFont->maFontSelData.mpFontData;
1370         if( nFallbackLevel < MAX_FALLBACK-1)
1371         {
1372             // ignore fallback font if it is the same as the original font
1373             // unless we are looking for a substitution for 0x202F, in which
1374             // case we'll just use a normal space
1375             if( mpFontInstance->maFontSelData.mpFontData == aFontSelData.mpFontData &&
1376                 aMissingCodes.indexOf(0x202F) == -1 )
1377             {
1378                 pFallbackFont->Release();
1379                 continue;
1380             }
1381         }
1382 
1383         // create and add glyph fallback layout to multilayout
1384         std::unique_ptr<SalLayout> pFallback = getFallbackFont(aFontSelData,
1385             nFallbackLevel, rLayoutArgs);
1386         if (pFallback)
1387         {
1388             if( !pMultiSalLayout )
1389                 pMultiSalLayout.reset( new MultiSalLayout( std::move(pSalLayout) ) );
1390             pMultiSalLayout->AddFallback( std::move(pFallback),
1391                 rLayoutArgs.maRuns, aFontSelData.mpFontData );
1392             if (nFallbackLevel == MAX_FALLBACK-1)
1393                 pMultiSalLayout->SetIncomplete(true);
1394         }
1395 
1396         pFallbackFont->Release();
1397 
1398         // break when this fallback was sufficient
1399         if( !rLayoutArgs.PrepareFallback() )
1400             break;
1401     }
1402 
1403     if( pMultiSalLayout && pMultiSalLayout->LayoutText( rLayoutArgs ) )
1404         pSalLayout = std::move(pMultiSalLayout);
1405 
1406     // restore orig font settings
1407     pSalLayout->InitFont();
1408     rLayoutArgs.maRuns = aLayoutRuns;
1409 
1410     return pSalLayout;
1411 }
1412 
1413 long OutputDevice::GetMinKashida() const
1414 {
1415     if( mbNewFont && !ImplNewFont() )
1416         return 0;
1417 
1418     return ImplDevicePixelToLogicWidth( mpFontInstance->mxFontMetric->GetMinKashida() );
1419 }
1420 
1421 sal_Int32 OutputDevice::ValidateKashidas ( const OUString& rTxt,
1422                                             sal_Int32 nIdx, sal_Int32 nLen,
1423                                             sal_Int32 nKashCount,
1424                                             const sal_Int32* pKashidaPos,
1425                                             sal_Int32* pKashidaPosDropped ) const
1426 {
1427    // do layout
1428     std::unique_ptr<SalLayout> pSalLayout = ImplLayout( rTxt, nIdx, nLen );
1429     if( !pSalLayout )
1430         return 0;
1431     sal_Int32 nDropped = 0;
1432     for( int i = 0; i < nKashCount; ++i )
1433     {
1434         if( !pSalLayout->IsKashidaPosValid( pKashidaPos[ i ] ))
1435         {
1436             pKashidaPosDropped[ nDropped ] = pKashidaPos [ i ];
1437             ++nDropped;
1438         }
1439     }
1440     return nDropped;
1441 }
1442 
1443 bool OutputDevice::GetGlyphBoundRects( const Point& rOrigin, const OUString& rStr,
1444                                            int nIndex, int nLen, MetricVector& rVector )
1445 {
1446     rVector.clear();
1447 
1448     if( nIndex >= rStr.getLength() )
1449         return false;
1450 
1451     if( nLen < 0 || nIndex + nLen >= rStr.getLength() )
1452     {
1453         nLen = rStr.getLength() - nIndex;
1454     }
1455 
1456     tools::Rectangle aRect;
1457     for( int i = 0; i < nLen; i++ )
1458     {
1459         if( !GetTextBoundRect( aRect, rStr, nIndex, nIndex + i, 1 ) )
1460             break;
1461         aRect.Move( rOrigin.X(), rOrigin.Y() );
1462         rVector.push_back( aRect );
1463     }
1464 
1465     return (nLen == static_cast<int>(rVector.size()));
1466 }
1467 
1468 sal_Int32 OutputDevice::HasGlyphs( const vcl::Font& rTempFont, const OUString& rStr,
1469     sal_Int32 nIndex, sal_Int32 nLen ) const
1470 {
1471     if( nIndex >= rStr.getLength() )
1472         return nIndex;
1473     sal_Int32 nEnd;
1474     if( nLen == -1 )
1475         nEnd = rStr.getLength();
1476     else
1477         nEnd = std::min( rStr.getLength(), nIndex + nLen );
1478 
1479     SAL_WARN_IF( nIndex >= nEnd, "vcl.gdi", "StartPos >= EndPos?" );
1480     SAL_WARN_IF( nEnd > rStr.getLength(), "vcl.gdi", "String too short" );
1481 
1482     // to get the map temporarily set font
1483     const vcl::Font aOrigFont = GetFont();
1484     const_cast<OutputDevice&>(*this).SetFont( rTempFont );
1485     FontCharMapRef xFontCharMap ( new FontCharMap() );
1486     bool bRet = GetFontCharMap( xFontCharMap );
1487     const_cast<OutputDevice&>(*this).SetFont( aOrigFont );
1488 
1489     // if fontmap is unknown assume it doesn't have the glyphs
1490     if( !bRet )
1491         return nIndex;
1492 
1493     for( sal_Int32 i = nIndex; nIndex < nEnd; ++i, ++nIndex )
1494         if( ! xFontCharMap->HasChar( rStr[i] ) )
1495             return nIndex;
1496 
1497     return -1;
1498 }
1499 
1500 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1501