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