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