1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 
22 #include <i18nlangtag/languagetag.hxx>
23 #include <i18nlangtag/mslangid.hxx>
24 #include <unotools/configmgr.hxx>
25 #include <unotools/fontdefs.hxx>
26 #include <o3tl/sorted_vector.hxx>
27 #include <outdev.h>
28 #include <PhysicalFontCollection.hxx>
29 
lcl_IsCJKFont(const OUString & rFontName)30 static ImplFontAttrs lcl_IsCJKFont( const OUString& rFontName )
31 {
32     // Test, if Fontname includes CJK characters --> In this case we
33     // mention that it is a CJK font
34     for(int i = 0; i < rFontName.getLength(); i++)
35     {
36         const sal_Unicode ch = rFontName[i];
37         // japanese
38         if ( ((ch >= 0x3040) && (ch <= 0x30FF)) ||
39              ((ch >= 0x3190) && (ch <= 0x319F)) )
40             return ImplFontAttrs::CJK|ImplFontAttrs::CJK_JP;
41 
42         // korean
43         if ( ((ch >= 0xAC00) && (ch <= 0xD7AF)) ||
44              ((ch >= 0xA960) && (ch <= 0xA97F)) ||
45              ((ch >= 0xD7B0) && (ch <= 0xD7FF)) ||
46              ((ch >= 0x3130) && (ch <= 0x318F)) ||
47              ((ch >= 0x1100) && (ch <= 0x11FF)) )
48             return ImplFontAttrs::CJK|ImplFontAttrs::CJK_KR;
49 
50         // chinese
51         if ( (ch >= 0x3400) && (ch <= 0x9FFF) )
52             return ImplFontAttrs::CJK|ImplFontAttrs::CJK_TC|ImplFontAttrs::CJK_SC;
53 
54         // cjk
55         if ( ((ch >= 0x3000) && (ch <= 0xD7AF)) ||
56              ((ch >= 0xFF00) && (ch <= 0xFFEE)) )
57             return ImplFontAttrs::CJK;
58 
59     }
60 
61     return ImplFontAttrs::None;
62 }
63 
PhysicalFontCollection()64 PhysicalFontCollection::PhysicalFontCollection()
65     : mbMatchData( false )
66     , mpPreMatchHook( nullptr )
67     , mpFallbackHook( nullptr )
68     , mnFallbackCount( -1 )
69 {}
70 
~PhysicalFontCollection()71 PhysicalFontCollection::~PhysicalFontCollection()
72 {
73     Clear();
74 }
75 
SetPreMatchHook(ImplPreMatchFontSubstitution * pHook)76 void PhysicalFontCollection::SetPreMatchHook( ImplPreMatchFontSubstitution* pHook )
77 {
78     mpPreMatchHook = pHook;
79 }
80 
SetFallbackHook(ImplGlyphFallbackFontSubstitution * pHook)81 void PhysicalFontCollection::SetFallbackHook( ImplGlyphFallbackFontSubstitution* pHook )
82 {
83     mpFallbackHook = pHook;
84 }
85 
Clear()86 void PhysicalFontCollection::Clear()
87 {
88     // remove fallback lists
89     mpFallbackList.reset();
90     mnFallbackCount = -1;
91 
92     // clear all entries in the device font list
93     maPhysicalFontFamilies.clear();
94 
95     // match data must be recalculated too
96     mbMatchData = false;
97 }
98 
ImplInitGenericGlyphFallback() const99 void PhysicalFontCollection::ImplInitGenericGlyphFallback() const
100 {
101     // normalized family names of fonts suited for glyph fallback
102     // if a font is available related fonts can be ignored
103     // TODO: implement dynamic lists
104     static const char* aGlyphFallbackList[] = {
105         // empty strings separate the names of unrelated fonts
106         "eudc", "",
107         "arialunicodems", "cyberbit", "code2000", "",
108         "andalesansui", "",
109         "starsymbol", "opensymbol", "",
110         "msmincho", "fzmingti", "fzheiti", "ipamincho", "sazanamimincho", "kochimincho", "",
111         "sunbatang", "sundotum", "baekmukdotum", "gulim", "batang", "dotum", "",
112         "hgmincholightj", "msunglightsc", "msunglighttc", "hymyeongjolightk", "",
113         "tahoma", "dejavusans", "timesnewroman", "liberationsans", "",
114         "shree", "mangal", "",
115         "raavi", "shruti", "tunga", "",
116         "latha", "gautami", "kartika", "vrinda", "",
117         "shayyalmt", "naskmt", "scheherazade", "",
118         "david", "nachlieli", "lucidagrande", "",
119         "norasi", "angsanaupc", "",
120         "khmerossystem", "",
121         "muktinarrow", "",
122         "phetsarathot", "",
123         "padauk", "pinlonmyanmar", "",
124         "iskoolapota", "lklug", "",
125         nullptr
126     };
127 
128     bool bHasEudc = false;
129     int nMaxLevel = 0;
130     int nBestQuality = 0;
131     std::unique_ptr<std::array<PhysicalFontFamily*,MAX_GLYPHFALLBACK>> pFallbackList;
132 
133     for( const char** ppNames = &aGlyphFallbackList[0];; ++ppNames )
134     {
135         // advance to next sub-list when end-of-sublist marker
136         if( !**ppNames ) // #i46456# check for empty string, i.e., deref string itself not only ptr to it
137         {
138             if( nBestQuality > 0 )
139                 if( ++nMaxLevel >= MAX_GLYPHFALLBACK )
140                     break;
141 
142             if( !ppNames[1] )
143                 break;
144 
145             nBestQuality = 0;
146             continue;
147         }
148 
149         // test if the glyph fallback candidate font is available and scalable
150         OUString aTokenName( *ppNames, strlen(*ppNames), RTL_TEXTENCODING_UTF8 );
151         PhysicalFontFamily* pFallbackFont = FindFontFamily( aTokenName );
152 
153         if( !pFallbackFont )
154             continue;
155 
156         // keep the best font of the glyph fallback sub-list
157         if( nBestQuality < pFallbackFont->GetMinQuality() )
158         {
159             nBestQuality = pFallbackFont->GetMinQuality();
160             // store available glyph fallback fonts
161             if( !pFallbackList )
162                 pFallbackList.reset(new std::array<PhysicalFontFamily*,MAX_GLYPHFALLBACK>);
163 
164             (*pFallbackList)[ nMaxLevel ] = pFallbackFont;
165             if( !bHasEudc && !nMaxLevel )
166                 bHasEudc = !strncmp( *ppNames, "eudc", 5 );
167         }
168     }
169 
170     mnFallbackCount = nMaxLevel;
171     mpFallbackList  = std::move(pFallbackList);
172 }
173 
GetGlyphFallbackFont(FontSelectPattern & rFontSelData,LogicalFontInstance * pFontInstance,OUString & rMissingCodes,int nFallbackLevel) const174 PhysicalFontFamily* PhysicalFontCollection::GetGlyphFallbackFont( FontSelectPattern& rFontSelData,
175                                                                   LogicalFontInstance* pFontInstance,
176                                                                   OUString& rMissingCodes,
177                                                                   int nFallbackLevel ) const
178 {
179     PhysicalFontFamily* pFallbackData = nullptr;
180 
181     // find a matching font candidate for platform specific glyph fallback
182     if( mpFallbackHook )
183     {
184         // check cache for the first matching entry
185         // to avoid calling the expensive fallback hook (#i83491#)
186         sal_UCS4 cChar = 0;
187         bool bCached = true;
188         sal_Int32 nStrIndex = 0;
189         while( nStrIndex < rMissingCodes.getLength() )
190         {
191             cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
192             bCached = pFontInstance->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &rFontSelData.maSearchName );
193 
194             // ignore entries which don't have a fallback
195             if( !bCached || !rFontSelData.maSearchName.isEmpty() )
196                 break;
197         }
198 
199         if( bCached )
200         {
201             // there is a matching fallback in the cache
202             // so update rMissingCodes with codepoints not yet resolved by this fallback
203             int nRemainingLength = 0;
204             std::unique_ptr<sal_UCS4[]> const pRemainingCodes(new sal_UCS4[rMissingCodes.getLength()]);
205             OUString aFontName;
206 
207             while( nStrIndex < rMissingCodes.getLength() )
208             {
209                 cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
210                 bCached = pFontInstance->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &aFontName );
211                 if( !bCached || (rFontSelData.maSearchName != aFontName) )
212                     pRemainingCodes[ nRemainingLength++ ] = cChar;
213             }
214             rMissingCodes = OUString( pRemainingCodes.get(), nRemainingLength );
215         }
216         else
217         {
218             OUString aOldMissingCodes = rMissingCodes;
219 
220             // call the hook to query the best matching glyph fallback font
221             if (mpFallbackHook->FindFontSubstitute(rFontSelData, pFontInstance, rMissingCodes))
222                 // apply outdev3.cxx specific fontname normalization
223                 rFontSelData.maSearchName = GetEnglishSearchFontName( rFontSelData.maSearchName );
224             else
225                 rFontSelData.maSearchName.clear();
226 
227             // See fdo#32665 for an example. FreeSerif that has glyphs in normal
228             // font, but not in the italic or bold version
229             bool bSubSetOfFontRequiresPropertyFaking = rFontSelData.mbEmbolden || rFontSelData.maItalicMatrix != ItalicMatrix();
230 
231             // Cache the result even if there was no match, unless its from part of a font for which the properties need
232             // to be faked. We need to rework this cache to take into account that fontconfig can return different fonts
233             // for different input sizes, weights, etc. Basically the cache is way to naive
234             if (!bSubSetOfFontRequiresPropertyFaking)
235             {
236                 for(;;)
237                 {
238                      if( !pFontInstance->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &rFontSelData.maSearchName ) )
239                          pFontInstance->AddFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName );
240                      if( nStrIndex >= aOldMissingCodes.getLength() )
241                          break;
242                      cChar = aOldMissingCodes.iterateCodePoints( &nStrIndex );
243                 }
244                 if( !rFontSelData.maSearchName.isEmpty() )
245                 {
246                     // remove cache entries that were still not resolved
247                     for( nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
248                     {
249                         cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
250                         pFontInstance->IgnoreFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName );
251                     }
252                 }
253             }
254         }
255 
256         // find the matching device font
257         if( !rFontSelData.maSearchName.isEmpty() )
258             pFallbackData = FindFontFamily( rFontSelData.maSearchName );
259     }
260 
261     // else find a matching font candidate for generic glyph fallback
262     if( !pFallbackData )
263     {
264         // initialize font candidates for generic glyph fallback if needed
265         if( mnFallbackCount < 0 )
266             ImplInitGenericGlyphFallback();
267 
268         // TODO: adjust nFallbackLevel by number of levels resolved by the fallback hook
269         if( nFallbackLevel < mnFallbackCount )
270             pFallbackData = (*mpFallbackList)[ nFallbackLevel ];
271     }
272 
273     return pFallbackData;
274 }
275 
Add(PhysicalFontFace * pNewData)276 void PhysicalFontCollection::Add( PhysicalFontFace* pNewData )
277 {
278     OUString aSearchName = GetEnglishSearchFontName( pNewData->GetFamilyName() );
279 
280     PhysicalFontFamily* pFoundData = FindOrCreateFontFamily( aSearchName );
281 
282     pFoundData->AddFontFace( pNewData );
283 }
284 
285 // find the font from the normalized font family name
ImplFindFontFamilyBySearchName(const OUString & rSearchName) const286 PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyBySearchName( const OUString& rSearchName ) const
287 {
288     // must be called with a normalized name.
289     assert( GetEnglishSearchFontName( rSearchName ) == rSearchName );
290 
291     PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.find( rSearchName );
292     if( it == maPhysicalFontFamilies.end() )
293         return nullptr;
294 
295     PhysicalFontFamily* pFoundData = (*it).second.get();
296     return pFoundData;
297 }
298 
FindFontFamily(const OUString & rFontName) const299 PhysicalFontFamily* PhysicalFontCollection::FindFontFamily( const OUString& rFontName ) const
300 {
301     return ImplFindFontFamilyBySearchName( GetEnglishSearchFontName( rFontName ) );
302 }
303 
FindOrCreateFontFamily(const OUString & rFamilyName)304 PhysicalFontFamily *PhysicalFontCollection::FindOrCreateFontFamily( const OUString &rFamilyName )
305 {
306     PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.find( rFamilyName );
307     PhysicalFontFamily* pFoundData = nullptr;
308 
309     if( it != maPhysicalFontFamilies.end() )
310         pFoundData = (*it).second.get();
311 
312     if( !pFoundData )
313     {
314         pFoundData = new PhysicalFontFamily( rFamilyName );
315         maPhysicalFontFamilies[ rFamilyName ].reset(pFoundData);
316     }
317 
318     return pFoundData;
319 }
320 
FindFontFamilyByTokenNames(const OUString & rTokenStr) const321 PhysicalFontFamily* PhysicalFontCollection::FindFontFamilyByTokenNames(const OUString& rTokenStr) const
322 {
323     PhysicalFontFamily* pFoundData = nullptr;
324 
325     // use normalized font name tokens to find the font
326     for( sal_Int32 nTokenPos = 0; nTokenPos != -1; )
327     {
328         OUString aFamilyName = GetNextFontToken( rTokenStr, nTokenPos );
329         if( aFamilyName.isEmpty() )
330             continue;
331 
332         pFoundData = FindFontFamily( aFamilyName );
333 
334         if( pFoundData )
335             break;
336     }
337 
338     return pFoundData;
339 }
340 
ImplFindFontFamilyBySubstFontAttr(const utl::FontNameAttr & rFontAttr) const341 PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyBySubstFontAttr( const utl::FontNameAttr& rFontAttr ) const
342 {
343     PhysicalFontFamily* pFoundData = nullptr;
344 
345     // use the font substitutions suggested by the FontNameAttr to find the font
346     for (auto const& substitution : rFontAttr.Substitutions)
347     {
348         pFoundData = FindFontFamily(substitution);
349         if( pFoundData )
350             return pFoundData;
351     }
352 
353     // use known attributes from the configuration to find a matching substitute
354     const ImplFontAttrs nSearchType = rFontAttr.Type;
355     if( nSearchType != ImplFontAttrs::None )
356     {
357         const FontWeight eSearchWeight = rFontAttr.Weight;
358         const FontWidth  eSearchWidth  = rFontAttr.Width;
359         const FontItalic eSearchSlant  = ITALIC_DONTKNOW;
360 
361         pFoundData = FindFontFamilyByAttributes( nSearchType,
362             eSearchWeight, eSearchWidth, eSearchSlant, "" );
363 
364         if( pFoundData )
365             return pFoundData;
366     }
367 
368     return nullptr;
369 }
370 
ImplInitMatchData() const371 void PhysicalFontCollection::ImplInitMatchData() const
372 {
373     // short circuit if already done
374     if( mbMatchData )
375         return;
376     mbMatchData = true;
377 
378     if (utl::ConfigManager::IsFuzzing())
379         return;
380 
381     // calculate MatchData for all entries
382     const utl::FontSubstConfiguration& rFontSubst = utl::FontSubstConfiguration::get();
383 
384     for (auto const& family : maPhysicalFontFamilies)
385     {
386         const OUString& rSearchName = family.first;
387         PhysicalFontFamily* pEntry = family.second.get();
388 
389         pEntry->InitMatchData( rFontSubst, rSearchName );
390     }
391 }
392 
FindFontFamilyByAttributes(ImplFontAttrs nSearchType,FontWeight eSearchWeight,FontWidth eSearchWidth,FontItalic eSearchItalic,const OUString & rSearchFamilyName) const393 PhysicalFontFamily* PhysicalFontCollection::FindFontFamilyByAttributes( ImplFontAttrs nSearchType,
394                                                                         FontWeight eSearchWeight,
395                                                                         FontWidth eSearchWidth,
396                                                                         FontItalic eSearchItalic,
397                                                                         const OUString& rSearchFamilyName ) const
398 {
399     if( (eSearchItalic != ITALIC_NONE) && (eSearchItalic != ITALIC_DONTKNOW) )
400         nSearchType |= ImplFontAttrs::Italic;
401 
402     // don't bother to match attributes if the attributes aren't worth matching
403     if( nSearchType == ImplFontAttrs::None
404     && ((eSearchWeight == WEIGHT_DONTKNOW) || (eSearchWeight == WEIGHT_NORMAL))
405     && ((eSearchWidth == WIDTH_DONTKNOW) || (eSearchWidth == WIDTH_NORMAL)) )
406         return nullptr;
407 
408     ImplInitMatchData();
409     PhysicalFontFamily* pFoundData = nullptr;
410 
411     tools::Long    nBestMatch = 40000;
412     ImplFontAttrs  nBestType = ImplFontAttrs::None;
413 
414     for (auto const& family : maPhysicalFontFamilies)
415     {
416         PhysicalFontFamily* pData = family.second.get();
417 
418         // Get all information about the matching font
419         ImplFontAttrs nMatchType  = pData->GetMatchType();
420         FontWeight    eMatchWeight= pData->GetMatchWeight();
421         FontWidth     eMatchWidth = pData->GetMatchWidth();
422 
423         // Calculate Match Value
424         // 1000000000
425         //  100000000
426         //   10000000   CJK, CTL, None-Latin, Symbol
427         //    1000000   FamilyName, Script, Fixed, -Special, -Decorative,
428         //              Titling, Capitals, Outline, Shadow
429         //     100000   Match FamilyName, Serif, SansSerif, Italic,
430         //              Width, Weight
431         //      10000   Scalable, Standard, Default,
432         //              full, Normal, Knownfont,
433         //              Otherstyle, +Special, +Decorative,
434         //       1000   Typewriter, Rounded, Gothic, Schollbook
435         //        100
436         tools::Long nTestMatch = 0;
437 
438         // test CJK script attributes
439         if ( nSearchType & ImplFontAttrs::CJK )
440         {
441             // Matching language
442             if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::CJK_AllLang) )
443                 nTestMatch += 10000000*3;
444             if( nMatchType & ImplFontAttrs::CJK )
445                 nTestMatch += 10000000*2;
446             if( nMatchType & ImplFontAttrs::Full )
447                 nTestMatch += 10000000;
448         }
449         else if ( nMatchType & ImplFontAttrs::CJK )
450         {
451             nTestMatch -= 10000000;
452         }
453 
454         // test CTL script attributes
455         if( nSearchType & ImplFontAttrs::CTL )
456         {
457             if( nMatchType & ImplFontAttrs::CTL )
458                 nTestMatch += 10000000*2;
459             if( nMatchType & ImplFontAttrs::Full )
460                 nTestMatch += 10000000;
461         }
462         else if ( nMatchType & ImplFontAttrs::CTL )
463         {
464             nTestMatch -= 10000000;
465         }
466 
467         // test LATIN script attributes
468         if( nSearchType & ImplFontAttrs::NoneLatin )
469         {
470             if( nMatchType & ImplFontAttrs::NoneLatin )
471                 nTestMatch += 10000000*2;
472             if( nMatchType & ImplFontAttrs::Full )
473                 nTestMatch += 10000000;
474         }
475 
476         // test SYMBOL attributes
477         if ( nSearchType & ImplFontAttrs::Symbol )
478         {
479             const OUString& rSearchName = family.first;
480             // prefer some special known symbol fonts
481             if ( rSearchName == "starsymbol" )
482             {
483                 nTestMatch += 10000000*6+(10000*3);
484             }
485             else if ( rSearchName == "opensymbol" )
486             {
487                 nTestMatch += 10000000*6;
488             }
489             else if ( rSearchName == "starbats" ||
490                       rSearchName == "wingdings" ||
491                       rSearchName == "monotypesorts" ||
492                       rSearchName == "dingbats" ||
493                       rSearchName == "zapfdingbats" )
494             {
495                 nTestMatch += 10000000*5;
496             }
497             else if ( pData->GetTypeFaces() & FontTypeFaces::Symbol )
498             {
499                 nTestMatch += 10000000*4;
500             }
501             else
502             {
503                 if( nMatchType & ImplFontAttrs::Symbol )
504                     nTestMatch += 10000000*2;
505                 if( nMatchType & ImplFontAttrs::Full )
506                     nTestMatch += 10000000;
507             }
508         }
509         else if ( (pData->GetTypeFaces() & (FontTypeFaces::Symbol | FontTypeFaces::NoneSymbol)) == FontTypeFaces::Symbol )
510         {
511             nTestMatch -= 10000000;
512         }
513         else if ( nMatchType & ImplFontAttrs::Symbol )
514         {
515             nTestMatch -= 10000;
516         }
517 
518         // match stripped family name
519         if( !rSearchFamilyName.isEmpty() && (rSearchFamilyName == pData->GetMatchFamilyName()) )
520         {
521             nTestMatch += 1000000*3;
522         }
523 
524         // match ALLSCRIPT? attribute
525         if( nSearchType & ImplFontAttrs::AllScript )
526         {
527             if( nMatchType & ImplFontAttrs::AllScript )
528             {
529                 nTestMatch += 1000000*2;
530             }
531             if( nSearchType & ImplFontAttrs::AllSubscript )
532             {
533                 if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::AllSubscript) )
534                     nTestMatch += 1000000*2;
535                 if( ImplFontAttrs::None != ((nSearchType ^ nMatchType) & ImplFontAttrs::BrushScript) )
536                     nTestMatch -= 1000000;
537             }
538         }
539         else if( nMatchType & ImplFontAttrs::AllScript )
540         {
541             nTestMatch -= 1000000;
542         }
543 
544         // test MONOSPACE+TYPEWRITER attributes
545         if( nSearchType & ImplFontAttrs::Fixed )
546         {
547             if( nMatchType & ImplFontAttrs::Fixed )
548                 nTestMatch += 1000000*2;
549             // a typewriter attribute is even better
550             if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::Typewriter) )
551                 nTestMatch += 10000*2;
552         }
553         else if( nMatchType & ImplFontAttrs::Fixed )
554         {
555             nTestMatch -= 1000000;
556         }
557 
558         // test SPECIAL attribute
559         if( nSearchType & ImplFontAttrs::Special )
560         {
561             if( nMatchType & ImplFontAttrs::Special )
562             {
563                 nTestMatch += 10000;
564             }
565             else if( !(nSearchType & ImplFontAttrs::AllSerifStyle) )
566             {
567                  if( nMatchType & ImplFontAttrs::Serif )
568                  {
569                      nTestMatch += 1000*2;
570                  }
571                  else if( nMatchType & ImplFontAttrs::SansSerif )
572                  {
573                      nTestMatch += 1000;
574                  }
575              }
576         }
577         else if( (nMatchType & ImplFontAttrs::Special) && !(nSearchType & ImplFontAttrs::Symbol) )
578         {
579             nTestMatch -= 1000000;
580         }
581 
582         // test DECORATIVE attribute
583         if( nSearchType & ImplFontAttrs::Decorative )
584         {
585             if( nMatchType & ImplFontAttrs::Decorative )
586             {
587                 nTestMatch += 10000;
588             }
589             else if( !(nSearchType & ImplFontAttrs::AllSerifStyle) )
590             {
591                 if( nMatchType & ImplFontAttrs::Serif )
592                     nTestMatch += 1000*2;
593                 else if ( nMatchType & ImplFontAttrs::SansSerif )
594                     nTestMatch += 1000;
595             }
596         }
597         else if( nMatchType & ImplFontAttrs::Decorative )
598         {
599             nTestMatch -= 1000000;
600         }
601 
602         // test TITLE+CAPITALS attributes
603         if( nSearchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals) )
604         {
605             if( nMatchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals) )
606             {
607                 nTestMatch += 1000000*2;
608             }
609             if( ImplFontAttrs::None == ((nSearchType^nMatchType) & ImplFontAttrs(ImplFontAttrs::Titling | ImplFontAttrs::Capitals)))
610             {
611                 nTestMatch += 1000000;
612             }
613             else if( (nMatchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals)) &&
614                      (nMatchType & (ImplFontAttrs::Standard | ImplFontAttrs::Default)) )
615             {
616                 nTestMatch += 1000000;
617             }
618         }
619         else if( nMatchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals) )
620         {
621             nTestMatch -= 1000000;
622         }
623 
624         // test OUTLINE+SHADOW attributes
625         if( nSearchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow) )
626         {
627             if( nMatchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow) )
628             {
629                 nTestMatch += 1000000*2;
630             }
631             if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs(ImplFontAttrs::Outline | ImplFontAttrs::Shadow)) )
632             {
633                 nTestMatch += 1000000;
634             }
635             else if( (nMatchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow)) &&
636                      (nMatchType & (ImplFontAttrs::Standard | ImplFontAttrs::Default)) )
637             {
638                 nTestMatch += 1000000;
639             }
640         }
641         else if ( nMatchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow) )
642         {
643             nTestMatch -= 1000000;
644         }
645 
646         // test font name substrings
647         // TODO: calculate name matching score using e.g. Levenstein distance
648         if( (rSearchFamilyName.getLength() >= 4) &&
649             (pData->GetMatchFamilyName().getLength() >= 4) &&
650             ((rSearchFamilyName.indexOf( pData->GetMatchFamilyName() ) != -1) ||
651              (pData->GetMatchFamilyName().indexOf( rSearchFamilyName ) != -1)) )
652         {
653             nTestMatch += 5000;
654         }
655         // test SERIF attribute
656         if( nSearchType & ImplFontAttrs::Serif )
657         {
658             if( nMatchType & ImplFontAttrs::Serif )
659                 nTestMatch += 1000000*2;
660             else if( nMatchType & ImplFontAttrs::SansSerif )
661                 nTestMatch -= 1000000;
662         }
663 
664         // test SANSERIF attribute
665         if( nSearchType & ImplFontAttrs::SansSerif )
666         {
667             if( nMatchType & ImplFontAttrs::SansSerif )
668                 nTestMatch += 1000000;
669             else if ( nMatchType & ImplFontAttrs::Serif )
670                 nTestMatch -= 1000000;
671         }
672 
673         // test ITALIC attribute
674         if( nSearchType & ImplFontAttrs::Italic )
675         {
676             if( pData->GetTypeFaces() & FontTypeFaces::Italic )
677                 nTestMatch += 1000000*3;
678             if( nMatchType & ImplFontAttrs::Italic )
679                 nTestMatch += 1000000;
680         }
681         else if( !(nSearchType & ImplFontAttrs::AllScript) &&
682                  ((nMatchType & ImplFontAttrs::Italic) ||
683                   !(pData->GetTypeFaces() & FontTypeFaces::NoneItalic)) )
684         {
685             nTestMatch -= 1000000*2;
686         }
687 
688         // test WIDTH attribute
689         if( (eSearchWidth != WIDTH_DONTKNOW) && (eSearchWidth != WIDTH_NORMAL) )
690         {
691             if( eSearchWidth < WIDTH_NORMAL )
692             {
693                 if( eSearchWidth == eMatchWidth )
694                     nTestMatch += 1000000*3;
695                 else if( (eMatchWidth < WIDTH_NORMAL) && (eMatchWidth != WIDTH_DONTKNOW) )
696                     nTestMatch += 1000000;
697             }
698             else
699             {
700                 if( eSearchWidth == eMatchWidth )
701                     nTestMatch += 1000000*3;
702                 else if( eMatchWidth > WIDTH_NORMAL )
703                     nTestMatch += 1000000;
704             }
705         }
706         else if( (eMatchWidth != WIDTH_DONTKNOW) && (eMatchWidth != WIDTH_NORMAL) )
707         {
708             nTestMatch -= 1000000;
709         }
710 
711         // test WEIGHT attribute
712         if( (eSearchWeight != WEIGHT_DONTKNOW) &&
713             (eSearchWeight != WEIGHT_NORMAL) &&
714             (eSearchWeight != WEIGHT_MEDIUM) )
715         {
716             if( eSearchWeight < WEIGHT_NORMAL )
717             {
718                 if( pData->GetTypeFaces() & FontTypeFaces::Light )
719                     nTestMatch += 1000000;
720                 if( (eMatchWeight < WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_DONTKNOW) )
721                     nTestMatch += 1000000;
722             }
723             else
724             {
725                 if( pData->GetTypeFaces() & FontTypeFaces::Bold )
726                     nTestMatch += 1000000;
727                 if( eMatchWeight > WEIGHT_BOLD )
728                     nTestMatch += 1000000;
729             }
730         }
731         else if( ((eMatchWeight != WEIGHT_DONTKNOW) &&
732                   (eMatchWeight != WEIGHT_NORMAL) &&
733                   (eMatchWeight != WEIGHT_MEDIUM)) ||
734                  !(pData->GetTypeFaces() & FontTypeFaces::Normal) )
735         {
736             nTestMatch -= 1000000;
737         }
738 
739         // prefer scalable fonts
740         if( pData->GetTypeFaces() & FontTypeFaces::Scalable )
741             nTestMatch += 10000*4;
742         else
743             nTestMatch -= 10000*4;
744 
745         // test STANDARD+DEFAULT+FULL+NORMAL attributes
746         if( nMatchType & ImplFontAttrs::Standard )
747             nTestMatch += 10000*2;
748         if( nMatchType & ImplFontAttrs::Default )
749             nTestMatch += 10000;
750         if( nMatchType & ImplFontAttrs::Full )
751             nTestMatch += 10000;
752         if( nMatchType & ImplFontAttrs::Normal )
753             nTestMatch += 10000;
754 
755         // test OTHERSTYLE attribute
756         if( ((nSearchType ^ nMatchType) & ImplFontAttrs::OtherStyle) != ImplFontAttrs::None )
757         {
758             nTestMatch -= 10000;
759         }
760 
761         // test ROUNDED attribute
762         if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::Rounded) )
763             nTestMatch += 1000;
764 
765         // test TYPEWRITER attribute
766         if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::Typewriter) )
767             nTestMatch += 1000;
768 
769         // test GOTHIC attribute
770         if( nSearchType & ImplFontAttrs::Gothic )
771         {
772             if( nMatchType & ImplFontAttrs::Gothic )
773                 nTestMatch += 1000*3;
774             if( nMatchType & ImplFontAttrs::SansSerif )
775                 nTestMatch += 1000*2;
776         }
777 
778         // test SCHOOLBOOK attribute
779         if( nSearchType & ImplFontAttrs::Schoolbook )
780         {
781             if( nMatchType & ImplFontAttrs::Schoolbook )
782                 nTestMatch += 1000*3;
783             if( nMatchType & ImplFontAttrs::Serif )
784                 nTestMatch += 1000*2;
785         }
786 
787         // compare with best matching font yet
788         if ( nTestMatch > nBestMatch )
789         {
790             pFoundData  = pData;
791             nBestMatch  = nTestMatch;
792             nBestType   = nMatchType;
793         }
794         else if( nTestMatch == nBestMatch )
795         {
796             // some fonts are more suitable defaults
797             if( nMatchType & ImplFontAttrs::Default )
798             {
799                 pFoundData  = pData;
800                 nBestType   = nMatchType;
801             }
802             else if( (nMatchType & ImplFontAttrs::Standard) &&
803                     !(nBestType & ImplFontAttrs::Default) )
804             {
805                  pFoundData  = pData;
806                  nBestType   = nMatchType;
807             }
808         }
809     }
810 
811     return pFoundData;
812 }
813 
ImplFindFontFamilyOfDefaultFont() const814 PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyOfDefaultFont() const
815 {
816     // try to find one of the default fonts of the
817     // UNICODE, SANSSERIF, SERIF or FIXED default font lists
818     PhysicalFontFamily* pFoundData = nullptr;
819     if (!utl::ConfigManager::IsFuzzing())
820     {
821         const utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
822         LanguageTag aLanguageTag("en");
823         OUString aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::SANS_UNICODE );
824         pFoundData = FindFontFamilyByTokenNames( aFontname );
825 
826         if( pFoundData )
827             return pFoundData;
828 
829         aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::SANS );
830         pFoundData = FindFontFamilyByTokenNames( aFontname );
831         if( pFoundData )
832             return pFoundData;
833 
834         aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::SERIF );
835         pFoundData = FindFontFamilyByTokenNames( aFontname );
836         if( pFoundData )
837             return pFoundData;
838 
839         aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::FIXED );
840         pFoundData = FindFontFamilyByTokenNames( aFontname );
841         if( pFoundData )
842             return pFoundData;
843     }
844 
845     // now try to find a reasonable non-symbol font
846 
847     ImplInitMatchData();
848 
849     for (auto const& family : maPhysicalFontFamilies)
850     {
851         PhysicalFontFamily* pData = family.second.get();
852         if( pData->GetMatchType() & ImplFontAttrs::Symbol )
853             continue;
854 
855         pFoundData = pData;
856         if( pData->GetMatchType() & (ImplFontAttrs::Default|ImplFontAttrs::Standard) )
857             break;
858     }
859     if( pFoundData )
860         return pFoundData;
861 
862     // finding any font is better than finding no font at all
863     auto it = maPhysicalFontFamilies.begin();
864     if( it !=  maPhysicalFontFamilies.end() )
865         pFoundData = (*it).second.get();
866 
867     return pFoundData;
868 }
869 
Clone() const870 std::shared_ptr<PhysicalFontCollection> PhysicalFontCollection::Clone() const
871 {
872     auto xClonedCollection = std::make_shared<PhysicalFontCollection>();
873     xClonedCollection->mpPreMatchHook = mpPreMatchHook;
874     xClonedCollection->mpFallbackHook = mpFallbackHook;
875 
876     // TODO: clone the config-font attributes too?
877     xClonedCollection->mbMatchData    = false;
878 
879     for (auto const& family : maPhysicalFontFamilies)
880     {
881         const PhysicalFontFamily* pFontFace = family.second.get();
882         pFontFace->UpdateCloneFontList(*xClonedCollection);
883     }
884 
885     return xClonedCollection;
886 }
887 
GetDeviceFontList() const888 std::unique_ptr<ImplDeviceFontList> PhysicalFontCollection::GetDeviceFontList() const
889 {
890     std::unique_ptr<ImplDeviceFontList> pDeviceFontList(new ImplDeviceFontList);
891 
892     for (auto const& family : maPhysicalFontFamilies)
893     {
894         const PhysicalFontFamily* pFontFamily = family.second.get();
895         pFontFamily->UpdateDevFontList( *pDeviceFontList );
896     }
897 
898     return pDeviceFontList;
899 }
900 
GetDeviceFontSizeList(const OUString & rFontName) const901 std::unique_ptr<ImplDeviceFontSizeList> PhysicalFontCollection::GetDeviceFontSizeList( const OUString& rFontName ) const
902 {
903     std::unique_ptr<ImplDeviceFontSizeList> pDeviceFontSizeList(new ImplDeviceFontSizeList);
904 
905     PhysicalFontFamily* pFontFamily = FindFontFamily( rFontName );
906     if( pFontFamily != nullptr )
907     {
908         o3tl::sorted_vector<int> rHeights;
909         pFontFamily->GetFontHeights( rHeights );
910 
911         for( const auto& rHeight : rHeights )
912             pDeviceFontSizeList->Add( rHeight );
913     }
914 
915     return pDeviceFontSizeList;
916 }
917 
918 // These are the metric-compatible replacement fonts that are bundled with
919 // LibreOffice, we prefer them over generic substitutions that might be
920 // provided by the system.
921 const std::vector<std::pair<OUString, OUString>> aMetricCompatibleMap =
922 {
923     { "Times New Roman", "Liberation Serif" },
924     { "Arial",           "Liberation Sans" },
925     { "Arial Narrow",    "Liberation Sans Narrow" },
926     { "Courier New",     "Liberation Mono" },
927     { "Cambria",         "Caladea" },
928     { "Calibri",         "Carlito" },
929 };
930 
FindMetricCompatibleFont(FontSelectPattern & rFontSelData)931 static bool FindMetricCompatibleFont(FontSelectPattern& rFontSelData)
932 {
933     for (const auto& aSub : aMetricCompatibleMap)
934     {
935         if (rFontSelData.maSearchName == GetEnglishSearchFontName(aSub.first))
936         {
937             rFontSelData.maSearchName = aSub.second;
938             return true;
939         }
940     }
941 
942     return false;
943 }
944 
FindFontFamily(FontSelectPattern & rFSD) const945 PhysicalFontFamily* PhysicalFontCollection::FindFontFamily( FontSelectPattern& rFSD ) const
946 {
947     // give up if no fonts are available
948     if( !Count() )
949         return nullptr;
950 
951     static bool noFontLookup = getenv("SAL_NO_FONT_LOOKUP") != nullptr;
952     if (noFontLookup)
953     {
954         // Hard code the use of Liberation Sans and skip font search.
955         sal_Int32 nIndex = 0;
956         rFSD.maTargetName = GetNextFontToken(rFSD.GetFamilyName(), nIndex);
957         rFSD.maSearchName = "liberationsans";
958         PhysicalFontFamily* pFont = ImplFindFontFamilyBySearchName(rFSD.maSearchName);
959         assert(pFont);
960         return pFont;
961     }
962 
963     bool bMultiToken = false;
964     sal_Int32 nTokenPos = 0;
965     OUString& aSearchName = rFSD.maSearchName; // TODO: get rid of reference
966     for(;;)
967     {
968         rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos );
969         aSearchName = rFSD.maTargetName;
970 
971         // Until features are properly supported, they are appended to the
972         // font name, so we need to strip them off so the font is found.
973         sal_Int32 nFeat = aSearchName.indexOf(FontSelectPattern::FEAT_PREFIX);
974         OUString aOrigName = rFSD.maTargetName;
975         OUString aBaseFontName = aSearchName.copy( 0, (nFeat != -1) ? nFeat : aSearchName.getLength() );
976 
977         if (nFeat != -1)
978         {
979             aSearchName = aBaseFontName;
980             rFSD.maTargetName = aBaseFontName;
981         }
982 
983         aSearchName = GetEnglishSearchFontName( aSearchName );
984         ImplFontSubstitute( aSearchName );
985         // #114999# special emboldening for Ricoh fonts
986         // TODO: smarter check for special cases by using PreMatch infrastructure?
987         if( (rFSD.GetWeight() > WEIGHT_MEDIUM) &&
988             aSearchName.startsWithIgnoreAsciiCase( "hg" ) )
989         {
990             OUString aBoldName;
991             if( aSearchName.startsWithIgnoreAsciiCase( "hggothicb" ) )
992                 aBoldName = "hggothice";
993             else if( aSearchName.startsWithIgnoreAsciiCase( "hgpgothicb" ) )
994                 aBoldName = "hgpgothice";
995             else if( aSearchName.startsWithIgnoreAsciiCase( "hgminchol" ) )
996                 aBoldName = "hgminchob";
997             else if( aSearchName.startsWithIgnoreAsciiCase( "hgpminchol" ) )
998                 aBoldName = "hgpminchob";
999             else if( aSearchName.equalsIgnoreAsciiCase( "hgminchob" ) )
1000                 aBoldName = "hgminchoe";
1001             else if( aSearchName.equalsIgnoreAsciiCase( "hgpminchob" ) )
1002                 aBoldName = "hgpminchoe";
1003 
1004             if( !aBoldName.isEmpty() && ImplFindFontFamilyBySearchName( aBoldName ) )
1005             {
1006                 // the other font is available => use it
1007                 aSearchName = aBoldName;
1008                 // prevent synthetic emboldening of bold version
1009                 rFSD.SetWeight(WEIGHT_DONTKNOW);
1010             }
1011         }
1012 
1013         // restore the features to make the font selection data unique
1014         rFSD.maTargetName = aOrigName;
1015 
1016         // check if the current font name token or its substitute is valid
1017         PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySearchName( aSearchName );
1018         if( pFoundData )
1019             return pFoundData;
1020 
1021         // some systems provide special customization
1022         // e.g. they suggest "serif" as UI-font, but this name cannot be used directly
1023         //      because the system wants to map it to another font first, e.g. "Helvetica"
1024 
1025         // use the target name to search in the prematch hook
1026         rFSD.maTargetName = aBaseFontName;
1027 
1028         // Related: fdo#49271 RTF files often contain weird-ass
1029         // Win 3.1/Win95 style fontnames which attempt to put the
1030         // charset encoding into the filename
1031         // http://www.webcenter.ru/~kazarn/eng/fonts_ttf.htm
1032         OUString sStrippedName = StripScriptFromName(rFSD.maTargetName);
1033         if (sStrippedName != rFSD.maTargetName)
1034         {
1035             rFSD.maTargetName = sStrippedName;
1036             aSearchName = GetEnglishSearchFontName(rFSD.maTargetName);
1037             pFoundData = ImplFindFontFamilyBySearchName(aSearchName);
1038             if( pFoundData )
1039                 return pFoundData;
1040         }
1041 
1042         if (FindMetricCompatibleFont(rFSD) ||
1043             (mpPreMatchHook && mpPreMatchHook->FindFontSubstitute(rFSD)))
1044         {
1045             aSearchName = GetEnglishSearchFontName(aSearchName);
1046         }
1047 
1048         // the prematch hook uses the target name to search, but we now need
1049         // to restore the features to make the font selection data unique
1050         rFSD.maTargetName = aOrigName;
1051 
1052         pFoundData = ImplFindFontFamilyBySearchName( aSearchName );
1053         if( pFoundData )
1054             return pFoundData;
1055 
1056         // break after last font name token was checked unsuccessfully
1057         if( nTokenPos == -1)
1058             break;
1059         bMultiToken = true;
1060     }
1061 
1062     // if the first font was not available find the next available font in
1063     // the semicolon separated list of font names. A font is also considered
1064     // available when there is a matching entry in the Tools->Options->Fonts
1065     // dialog with neither ALWAYS nor SCREENONLY flags set and the substitution
1066     // font is available
1067     for( nTokenPos = 0; nTokenPos != -1; )
1068     {
1069         if( bMultiToken )
1070         {
1071             rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos );
1072             aSearchName = GetEnglishSearchFontName( rFSD.maTargetName );
1073         }
1074         else
1075             nTokenPos = -1;
1076         if (FindMetricCompatibleFont(rFSD) ||
1077             (mpPreMatchHook && mpPreMatchHook->FindFontSubstitute(rFSD)))
1078         {
1079             aSearchName = GetEnglishSearchFontName( aSearchName );
1080         }
1081         ImplFontSubstitute( aSearchName );
1082         PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySearchName( aSearchName );
1083         if( pFoundData )
1084             return pFoundData;
1085     }
1086 
1087     // if no font with a directly matching name is available use the
1088     // first font name token and get its attributes to find a replacement
1089     if ( bMultiToken )
1090     {
1091         nTokenPos = 0;
1092         rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos );
1093         aSearchName = GetEnglishSearchFontName( rFSD.maTargetName );
1094     }
1095 
1096     OUString      aSearchShortName;
1097     OUString      aSearchFamilyName;
1098     FontWeight    eSearchWeight   = rFSD.GetWeight();
1099     FontWidth     eSearchWidth    = rFSD.GetWidthType();
1100     ImplFontAttrs nSearchType     = ImplFontAttrs::None;
1101     utl::FontSubstConfiguration::getMapName( aSearchName, aSearchShortName, aSearchFamilyName,
1102                                              eSearchWeight, eSearchWidth, nSearchType );
1103 
1104     // note: the search name was already translated to english (if possible)
1105     // use the font's shortened name if needed
1106     if ( aSearchShortName != aSearchName )
1107     {
1108        PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySearchName( aSearchShortName );
1109        if( pFoundData )
1110        {
1111 #ifdef UNX
1112             /* #96738# don't use mincho as a replacement for "MS Mincho" on X11: Mincho is
1113             a korean bitmap font that is not suitable here. Use the font replacement table,
1114             that automatically leads to the desired "HG Mincho Light J". Same story for
1115             MS Gothic, there are thai and korean "Gothic" fonts, so we even prefer Andale */
1116             if ((aSearchName != "msmincho") && (aSearchName != "msgothic"))
1117                 // TODO: add heuristic to only throw out the fake ms* fonts
1118 #endif
1119             {
1120                 return pFoundData;
1121             }
1122         }
1123     }
1124 
1125     // use font fallback
1126     const utl::FontNameAttr* pFontAttr = nullptr;
1127     if (!aSearchName.isEmpty() && !utl::ConfigManager::IsFuzzing())
1128     {
1129         // get fallback info using FontSubstConfiguration and
1130         // the target name, it's shortened name and family name in that order
1131         const utl::FontSubstConfiguration& rFontSubst = utl::FontSubstConfiguration::get();
1132         pFontAttr = rFontSubst.getSubstInfo( aSearchName );
1133         if ( !pFontAttr && (aSearchShortName != aSearchName) )
1134             pFontAttr = rFontSubst.getSubstInfo( aSearchShortName );
1135         if ( !pFontAttr && (aSearchFamilyName != aSearchShortName) )
1136             pFontAttr = rFontSubst.getSubstInfo( aSearchFamilyName );
1137 
1138         // try the font substitutions suggested by the fallback info
1139         if( pFontAttr )
1140         {
1141             PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySubstFontAttr( *pFontAttr );
1142             if( pFoundData )
1143                 return pFoundData;
1144         }
1145     }
1146 
1147     // if a target symbol font is not available use a default symbol font
1148     if( rFSD.IsSymbolFont() )
1149     {
1150         LanguageTag aDefaultLanguageTag("en");
1151         if (utl::ConfigManager::IsFuzzing())
1152             aSearchName = "OpenSymbol";
1153         else
1154             aSearchName = utl::DefaultFontConfiguration::get().getDefaultFont( aDefaultLanguageTag, DefaultFontType::SYMBOL );
1155         PhysicalFontFamily* pFoundData = FindFontFamilyByTokenNames( aSearchName );
1156         if( pFoundData )
1157             return pFoundData;
1158     }
1159 
1160     // now try the other font name tokens
1161     while( nTokenPos != -1 )
1162     {
1163         rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos );
1164         if( rFSD.maTargetName.isEmpty() )
1165             continue;
1166 
1167         aSearchName = GetEnglishSearchFontName( rFSD.maTargetName );
1168 
1169         OUString      aTempShortName;
1170         OUString      aTempFamilyName;
1171         ImplFontAttrs nTempType   = ImplFontAttrs::None;
1172         FontWeight    eTempWeight = rFSD.GetWeight();
1173         FontWidth     eTempWidth  = WIDTH_DONTKNOW;
1174         utl::FontSubstConfiguration::getMapName( aSearchName, aTempShortName, aTempFamilyName,
1175                                                  eTempWeight, eTempWidth, nTempType );
1176 
1177         // use a shortened token name if available
1178         if( aTempShortName != aSearchName )
1179         {
1180             PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySearchName( aTempShortName );
1181             if( pFoundData )
1182                 return pFoundData;
1183         }
1184 
1185         const utl::FontNameAttr* pTempFontAttr = nullptr;
1186         if (!utl::ConfigManager::IsFuzzing())
1187         {
1188             // use a font name from font fallback list to determine font attributes
1189             // get fallback info using FontSubstConfiguration and
1190             // the target name, it's shortened name and family name in that order
1191             const utl::FontSubstConfiguration& rFontSubst = utl::FontSubstConfiguration::get();
1192             pTempFontAttr = rFontSubst.getSubstInfo( aSearchName );
1193 
1194             if ( !pTempFontAttr && (aTempShortName != aSearchName) )
1195                 pTempFontAttr = rFontSubst.getSubstInfo( aTempShortName );
1196 
1197             if ( !pTempFontAttr && (aTempFamilyName != aTempShortName) )
1198                 pTempFontAttr = rFontSubst.getSubstInfo( aTempFamilyName );
1199         }
1200 
1201         // try the font substitutions suggested by the fallback info
1202         if( pTempFontAttr )
1203         {
1204             PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySubstFontAttr( *pTempFontAttr );
1205             if( pFoundData )
1206                 return pFoundData;
1207             if( !pFontAttr )
1208                 pFontAttr = pTempFontAttr;
1209         }
1210     }
1211 
1212     // if still needed use the font request's attributes to find a good match
1213     if (MsLangId::isSimplifiedChinese(rFSD.meLanguage))
1214         nSearchType |= ImplFontAttrs::CJK | ImplFontAttrs::CJK_SC;
1215     else if (MsLangId::isTraditionalChinese(rFSD.meLanguage))
1216         nSearchType |= ImplFontAttrs::CJK | ImplFontAttrs::CJK_TC;
1217     else if (MsLangId::isKorean(rFSD.meLanguage))
1218         nSearchType |= ImplFontAttrs::CJK | ImplFontAttrs::CJK_KR;
1219     else if (rFSD.meLanguage == LANGUAGE_JAPANESE)
1220         nSearchType |= ImplFontAttrs::CJK | ImplFontAttrs::CJK_JP;
1221     else
1222     {
1223         nSearchType |= lcl_IsCJKFont( rFSD.GetFamilyName() );
1224         if( rFSD.IsSymbolFont() )
1225             nSearchType |= ImplFontAttrs::Symbol;
1226     }
1227 
1228     PhysicalFontFamily::CalcType( nSearchType, eSearchWeight, eSearchWidth, rFSD.GetFamilyType(), pFontAttr );
1229     PhysicalFontFamily* pFoundData = FindFontFamilyByAttributes( nSearchType,
1230         eSearchWeight, eSearchWidth, rFSD.GetItalic(), aSearchFamilyName );
1231 
1232     if( pFoundData )
1233     {
1234         // overwrite font selection attributes using info from the typeface flags
1235         if( (eSearchWeight >= WEIGHT_BOLD) &&
1236             (eSearchWeight > rFSD.GetWeight()) &&
1237             (pFoundData->GetTypeFaces() & FontTypeFaces::Bold) )
1238         {
1239             rFSD.SetWeight( eSearchWeight );
1240         }
1241         else if( (eSearchWeight < WEIGHT_NORMAL) &&
1242                  (eSearchWeight < rFSD.GetWeight()) &&
1243                  (eSearchWeight != WEIGHT_DONTKNOW) &&
1244                  (pFoundData->GetTypeFaces() & FontTypeFaces::Light) )
1245         {
1246             rFSD.SetWeight( eSearchWeight );
1247         }
1248 
1249         if( (nSearchType & ImplFontAttrs::Italic) &&
1250             ((rFSD.GetItalic() == ITALIC_DONTKNOW) ||
1251              (rFSD.GetItalic() == ITALIC_NONE)) &&
1252             (pFoundData->GetTypeFaces() & FontTypeFaces::Italic) )
1253         {
1254             rFSD.SetItalic( ITALIC_NORMAL );
1255         }
1256     }
1257     else
1258     {
1259         // if still needed fall back to default fonts
1260         pFoundData = ImplFindFontFamilyOfDefaultFont();
1261     }
1262 
1263     return pFoundData;
1264 }
1265 
1266 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1267 
1268