build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / font / PhysicalFontCollection.cxx
blob3a7da5921b62c4d2bb0a73d2e50986857a9b3f51
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <i18nlangtag/mslangid.hxx>
21 #include <unotools/configmgr.hxx>
22 #include <unotools/fontdefs.hxx>
24 #include "outdev.h"
25 #include "PhysicalFontCollection.hxx"
27 static ImplFontAttrs lcl_IsCJKFont( const OUString& rFontName )
29 // Test, if Fontname includes CJK characters --> In this case we
30 // mention that it is a CJK font
31 for(int i = 0; i < rFontName.getLength(); i++)
33 const sal_Unicode ch = rFontName[i];
34 // japanese
35 if ( ((ch >= 0x3040) && (ch <= 0x30FF)) ||
36 ((ch >= 0x3190) && (ch <= 0x319F)) )
37 return ImplFontAttrs::CJK|ImplFontAttrs::CJK_JP;
39 // korean
40 if ( ((ch >= 0xAC00) && (ch <= 0xD7AF)) ||
41 ((ch >= 0x3130) && (ch <= 0x318F)) ||
42 ((ch >= 0x1100) && (ch <= 0x11FF)) )
43 return ImplFontAttrs::CJK|ImplFontAttrs::CJK_KR;
45 // chinese
46 if ( ((ch >= 0x3400) && (ch <= 0x9FFF)) )
47 return ImplFontAttrs::CJK|ImplFontAttrs::CJK_TC|ImplFontAttrs::CJK_SC;
49 // cjk
50 if ( ((ch >= 0x3000) && (ch <= 0xD7AF)) ||
51 ((ch >= 0xFF00) && (ch <= 0xFFEE)) )
52 return ImplFontAttrs::CJK;
56 return ImplFontAttrs::None;
59 PhysicalFontCollection::PhysicalFontCollection()
60 : mbMatchData( false )
61 , mbMapNames( false )
62 , mpPreMatchHook( nullptr )
63 , mpFallbackHook( nullptr )
64 , mpFallbackList( nullptr )
65 , mnFallbackCount( -1 )
68 PhysicalFontCollection::~PhysicalFontCollection()
70 Clear();
73 void PhysicalFontCollection::SetPreMatchHook( ImplPreMatchFontSubstitution* pHook )
75 mpPreMatchHook = pHook;
78 void PhysicalFontCollection::SetFallbackHook( ImplGlyphFallbackFontSubstitution* pHook )
80 mpFallbackHook = pHook;
83 void PhysicalFontCollection::Clear()
85 // remove fallback lists
86 delete[] mpFallbackList;
87 mpFallbackList = nullptr;
88 mnFallbackCount = -1;
90 // clear all entries in the device font list
91 PhysicalFontFamilies::iterator it = maPhysicalFontFamilies.begin();
92 for(; it != maPhysicalFontFamilies.end(); ++it )
94 PhysicalFontFamily* pEntry = (*it).second;
95 delete pEntry;
98 maPhysicalFontFamilies.clear();
100 // match data must be recalculated too
101 mbMatchData = false;
104 void PhysicalFontCollection::ImplInitGenericGlyphFallback() const
106 // normalized family names of fonts suited for glyph fallback
107 // if a font is available related fonts can be ignored
108 // TODO: implement dynamic lists
109 static const char* aGlyphFallbackList[] = {
110 // empty strings separate the names of unrelated fonts
111 "eudc", "",
112 "arialunicodems", "cyberbit", "code2000", "",
113 "andalesansui", "",
114 "starsymbol", "opensymbol", "",
115 "msmincho", "fzmingti", "fzheiti", "ipamincho", "sazanamimincho", "kochimincho", "",
116 "sunbatang", "sundotum", "baekmukdotum", "gulim", "batang", "dotum", "",
117 "hgmincholightj", "msunglightsc", "msunglighttc", "hymyeongjolightk", "",
118 "tahoma", "dejavusans", "timesnewroman", "liberationsans", "",
119 "shree", "mangal", "",
120 "raavi", "shruti", "tunga", "",
121 "latha", "gautami", "kartika", "vrinda", "",
122 "shayyalmt", "naskmt", "scheherazade", "",
123 "david", "nachlieli", "lucidagrande", "",
124 "norasi", "angsanaupc", "",
125 "khmerossystem", "",
126 "muktinarrow", "",
127 "phetsarathot", "",
128 "padauk", "pinlonmyanmar", "",
129 "iskoolapota", "lklug", "",
130 nullptr
133 bool bHasEudc = false;
134 int nMaxLevel = 0;
135 int nBestQuality = 0;
136 PhysicalFontFamily** pFallbackList = nullptr;
138 for( const char** ppNames = &aGlyphFallbackList[0];; ++ppNames )
140 // advance to next sub-list when end-of-sublist marker
141 if( !**ppNames ) // #i46456# check for empty string, i.e., deref string itself not only ptr to it
143 if( nBestQuality > 0 )
144 if( ++nMaxLevel >= MAX_GLYPHFALLBACK )
145 break;
147 if( !ppNames[1] )
148 break;
150 nBestQuality = 0;
151 continue;
154 // test if the glyph fallback candidate font is available and scalable
155 OUString aTokenName( *ppNames, strlen(*ppNames), RTL_TEXTENCODING_UTF8 );
156 PhysicalFontFamily* pFallbackFont = FindFontFamily( aTokenName );
158 if( !pFallbackFont )
159 continue;
161 // keep the best font of the glyph fallback sub-list
162 if( nBestQuality < pFallbackFont->GetMinQuality() )
164 nBestQuality = pFallbackFont->GetMinQuality();
165 // store available glyph fallback fonts
166 if( !pFallbackList )
167 pFallbackList = new PhysicalFontFamily*[ MAX_GLYPHFALLBACK ];
169 pFallbackList[ nMaxLevel ] = pFallbackFont;
170 if( !bHasEudc && !nMaxLevel )
171 bHasEudc = !strncmp( *ppNames, "eudc", 5 );
175 mnFallbackCount = nMaxLevel;
176 mpFallbackList = pFallbackList;
179 PhysicalFontFamily* PhysicalFontCollection::GetGlyphFallbackFont( FontSelectPattern& rFontSelData,
180 OUString& rMissingCodes,
181 int nFallbackLevel ) const
183 PhysicalFontFamily* pFallbackData = nullptr;
185 // find a matching font candidate for platform specific glyph fallback
186 if( mpFallbackHook )
188 // check cache for the first matching entry
189 // to avoid calling the expensive fallback hook (#i83491#)
190 sal_UCS4 cChar = 0;
191 bool bCached = true;
192 sal_Int32 nStrIndex = 0;
193 while( nStrIndex < rMissingCodes.getLength() )
195 cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
196 bCached = rFontSelData.mpFontInstance->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &rFontSelData.maSearchName );
198 // ignore entries which don't have a fallback
199 if( !bCached || !rFontSelData.maSearchName.isEmpty() )
200 break;
203 if( bCached )
205 // there is a matching fallback in the cache
206 // so update rMissingCodes with codepoints not yet resolved by this fallback
207 int nRemainingLength = 0;
208 std::unique_ptr<sal_UCS4[]> const pRemainingCodes(new sal_UCS4[rMissingCodes.getLength()]);
209 OUString aFontName;
211 while( nStrIndex < rMissingCodes.getLength() )
213 cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
214 bCached = rFontSelData.mpFontInstance->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &aFontName );
215 if( !bCached || (rFontSelData.maSearchName != aFontName) )
216 pRemainingCodes[ nRemainingLength++ ] = cChar;
218 rMissingCodes = OUString( pRemainingCodes.get(), nRemainingLength );
220 else
222 OUString aOldMissingCodes = rMissingCodes;
224 // call the hook to query the best matching glyph fallback font
225 if( mpFallbackHook->FindFontSubstitute( rFontSelData, rMissingCodes ) )
226 // apply outdev3.cxx specific fontname normalization
227 rFontSelData.maSearchName = GetEnglishSearchFontName( rFontSelData.maSearchName );
228 else
229 rFontSelData.maSearchName.clear();
231 // See fdo#32665 for an example. FreeSerif that has glyphs in normal
232 // font, but not in the italic or bold version
233 bool bSubSetOfFontRequiresPropertyFaking = rFontSelData.mbEmbolden || rFontSelData.maItalicMatrix != ItalicMatrix();
235 // Cache the result even if there was no match, unless its from part of a font for which the properties need
236 // to be faked. We need to rework this cache to take into account that fontconfig can return different fonts
237 // for different input sizes, weights, etc. Basically the cache is way to naive
238 if (!bSubSetOfFontRequiresPropertyFaking)
240 for(;;)
242 if( !rFontSelData.mpFontInstance->GetFallbackForUnicode( cChar, rFontSelData.GetWeight(), &rFontSelData.maSearchName ) )
243 rFontSelData.mpFontInstance->AddFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName );
244 if( nStrIndex >= aOldMissingCodes.getLength() )
245 break;
246 cChar = aOldMissingCodes.iterateCodePoints( &nStrIndex );
248 if( !rFontSelData.maSearchName.isEmpty() )
250 // remove cache entries that were still not resolved
251 for( nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
253 cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
254 rFontSelData.mpFontInstance->IgnoreFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName );
260 // find the matching device font
261 if( !rFontSelData.maSearchName.isEmpty() )
262 pFallbackData = FindFontFamily( rFontSelData.maSearchName );
265 // else find a matching font candidate for generic glyph fallback
266 if( !pFallbackData )
268 // initialize font candidates for generic glyph fallback if needed
269 if( mnFallbackCount < 0 )
270 ImplInitGenericGlyphFallback();
272 // TODO: adjust nFallbackLevel by number of levels resolved by the fallback hook
273 if( nFallbackLevel < mnFallbackCount )
274 pFallbackData = mpFallbackList[ nFallbackLevel ];
277 return pFallbackData;
280 void PhysicalFontCollection::Add( PhysicalFontFace* pNewData )
282 OUString aSearchName = GetEnglishSearchFontName( pNewData->GetFamilyName() );
284 PhysicalFontFamily* pFoundData = FindOrCreateFontFamily( aSearchName );
286 bool bKeepNewData = pFoundData->AddFontFace( pNewData );
288 if( !bKeepNewData )
289 delete pNewData;
292 // find the font from the normalized font family name
293 PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyBySearchName( const OUString& rSearchName ) const
295 // must be called with a normalized name.
296 assert( GetEnglishSearchFontName( rSearchName ) == rSearchName );
298 PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.find( rSearchName );
299 if( it == maPhysicalFontFamilies.end() )
300 return nullptr;
302 PhysicalFontFamily* pFoundData = (*it).second;
303 return pFoundData;
306 PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyByAliasName(const OUString& rSearchName,
307 const OUString& rShortName) const
309 // short circuit for impossible font name alias
310 if (rSearchName.isEmpty())
311 return nullptr;
313 // short circuit if no alias names are available
314 if (!mbMapNames)
315 return nullptr;
317 // use the font's alias names to find the font
318 // TODO: get rid of linear search
319 PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.begin();
320 while( it != maPhysicalFontFamilies.end() )
322 PhysicalFontFamily* pData = (*it).second;
323 if( pData->GetAliasNames().isEmpty() )
324 continue;
326 // if one alias name matches we found a matching font
327 OUString aTempName;
328 sal_Int32 nIndex = 0;
332 aTempName = GetNextFontToken( pData->GetAliasNames(), nIndex );
333 // Test, if the Font name match with one of the mapping names
334 if ( (aTempName == rSearchName) || (aTempName == rShortName) )
335 return pData;
337 while ( nIndex != -1 );
340 return nullptr;
343 PhysicalFontFamily* PhysicalFontCollection::FindFontFamily( const OUString& rFontName ) const
345 return ImplFindFontFamilyBySearchName( GetEnglishSearchFontName( rFontName ) );
348 PhysicalFontFamily *PhysicalFontCollection::FindOrCreateFontFamily( const OUString &rFamilyName )
350 PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.find( rFamilyName );
351 PhysicalFontFamily* pFoundData = nullptr;
353 if( it != maPhysicalFontFamilies.end() )
354 pFoundData = (*it).second;
356 if( !pFoundData )
358 pFoundData = new PhysicalFontFamily( rFamilyName );
359 maPhysicalFontFamilies[ rFamilyName ] = pFoundData;
362 return pFoundData;
365 PhysicalFontFamily* PhysicalFontCollection::FindFontFamilyByTokenNames(const OUString& rTokenStr) const
367 PhysicalFontFamily* pFoundData = nullptr;
369 // use normalized font name tokens to find the font
370 for( sal_Int32 nTokenPos = 0; nTokenPos != -1; )
372 OUString aFamilyName = GetNextFontToken( rTokenStr, nTokenPos );
373 if( aFamilyName.isEmpty() )
374 continue;
376 pFoundData = FindFontFamily( aFamilyName );
378 if( pFoundData )
379 break;
382 return pFoundData;
385 PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyBySubstFontAttr( const utl::FontNameAttr& rFontAttr ) const
387 PhysicalFontFamily* pFoundData = nullptr;
389 // use the font substitutions suggested by the FontNameAttr to find the font
390 ::std::vector< OUString >::const_iterator it = rFontAttr.Substitutions.begin();
391 for(; it != rFontAttr.Substitutions.end(); ++it )
393 pFoundData = FindFontFamily( *it );
394 if( pFoundData )
395 return pFoundData;
398 // use known attributes from the configuration to find a matching substitute
399 const ImplFontAttrs nSearchType = rFontAttr.Type;
400 if( nSearchType != ImplFontAttrs::None )
402 const FontWeight eSearchWeight = rFontAttr.Weight;
403 const FontWidth eSearchWidth = rFontAttr.Width;
404 const FontItalic eSearchSlant = ITALIC_DONTKNOW;
405 const OUString aSearchName;
407 pFoundData = FindFontFamilyByAttributes( nSearchType,
408 eSearchWeight, eSearchWidth, eSearchSlant, aSearchName );
410 if( pFoundData )
411 return pFoundData;
414 return nullptr;
417 void PhysicalFontCollection::ImplInitMatchData() const
419 // short circuit if already done
420 if( mbMatchData )
421 return;
422 mbMatchData = true;
424 if (utl::ConfigManager::IsAvoidConfig())
425 return;
427 // calculate MatchData for all entries
428 const utl::FontSubstConfiguration& rFontSubst = utl::FontSubstConfiguration::get();
430 PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.begin();
431 for(; it != maPhysicalFontFamilies.end(); ++it )
433 const OUString& rSearchName = (*it).first;
434 PhysicalFontFamily* pEntry = (*it).second;
436 pEntry->InitMatchData( rFontSubst, rSearchName );
440 PhysicalFontFamily* PhysicalFontCollection::FindFontFamilyByAttributes( ImplFontAttrs nSearchType,
441 FontWeight eSearchWeight,
442 FontWidth eSearchWidth,
443 FontItalic eSearchItalic,
444 const OUString& rSearchFamilyName ) const
446 if( (eSearchItalic != ITALIC_NONE) && (eSearchItalic != ITALIC_DONTKNOW) )
447 nSearchType |= ImplFontAttrs::Italic;
449 // don't bother to match attributes if the attributes aren't worth matching
450 if( nSearchType == ImplFontAttrs::None
451 && ((eSearchWeight == WEIGHT_DONTKNOW) || (eSearchWeight == WEIGHT_NORMAL))
452 && ((eSearchWidth == WIDTH_DONTKNOW) || (eSearchWidth == WIDTH_NORMAL)) )
453 return nullptr;
455 ImplInitMatchData();
456 PhysicalFontFamily* pFoundData = nullptr;
458 long nBestMatch = 40000;
459 ImplFontAttrs nBestType = ImplFontAttrs::None;
461 PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.begin();
462 for(; it != maPhysicalFontFamilies.end(); ++it )
464 PhysicalFontFamily* pData = (*it).second;
466 // Get all information about the matching font
467 ImplFontAttrs nMatchType = pData->GetMatchType();
468 FontWeight eMatchWeight= pData->GetMatchWeight();
469 FontWidth eMatchWidth = pData->GetMatchWidth();
471 // Calculate Match Value
472 // 1000000000
473 // 100000000
474 // 10000000 CJK, CTL, None-Latin, Symbol
475 // 1000000 FamilyName, Script, Fixed, -Special, -Decorative,
476 // Titling, Capitals, Outline, Shadow
477 // 100000 Match FamilyName, Serif, SansSerif, Italic,
478 // Width, Weight
479 // 10000 Scalable, Standard, Default,
480 // full, Normal, Knownfont,
481 // Otherstyle, +Special, +Decorative,
482 // 1000 Typewriter, Rounded, Gothic, Schollbook
483 // 100
484 long nTestMatch = 0;
486 // test CJK script attributes
487 if ( nSearchType & ImplFontAttrs::CJK )
489 // Matching language
490 if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::CJK_AllLang) )
491 nTestMatch += 10000000*3;
492 if( nMatchType & ImplFontAttrs::CJK )
493 nTestMatch += 10000000*2;
494 if( nMatchType & ImplFontAttrs::Full )
495 nTestMatch += 10000000;
497 else if ( nMatchType & ImplFontAttrs::CJK )
499 nTestMatch -= 10000000;
502 // test CTL script attributes
503 if( nSearchType & ImplFontAttrs::CTL )
505 if( nMatchType & ImplFontAttrs::CTL )
506 nTestMatch += 10000000*2;
507 if( nMatchType & ImplFontAttrs::Full )
508 nTestMatch += 10000000;
510 else if ( nMatchType & ImplFontAttrs::CTL )
512 nTestMatch -= 10000000;
515 // test LATIN script attributes
516 if( nSearchType & ImplFontAttrs::NoneLatin )
518 if( nMatchType & ImplFontAttrs::NoneLatin )
519 nTestMatch += 10000000*2;
520 if( nMatchType & ImplFontAttrs::Full )
521 nTestMatch += 10000000;
524 // test SYMBOL attributes
525 if ( nSearchType & ImplFontAttrs::Symbol )
527 const OUString& rSearchName = it->first;
528 // prefer some special known symbol fonts
529 if ( rSearchName == "starsymbol" )
531 nTestMatch += 10000000*6+(10000*3);
533 else if ( rSearchName == "opensymbol" )
535 nTestMatch += 10000000*6;
537 else if ( rSearchName == "starbats" ||
538 rSearchName == "wingdings" ||
539 rSearchName == "monotypesorts" ||
540 rSearchName == "dingbats" ||
541 rSearchName == "zapfdingbats" )
543 nTestMatch += 10000000*5;
545 else if ( pData->GetTypeFaces() & FontTypeFaces::Symbol )
547 nTestMatch += 10000000*4;
549 else
551 if( nMatchType & ImplFontAttrs::Symbol )
552 nTestMatch += 10000000*2;
553 if( nMatchType & ImplFontAttrs::Full )
554 nTestMatch += 10000000;
557 else if ( (pData->GetTypeFaces() & (FontTypeFaces::Symbol | FontTypeFaces::NoneSymbol)) == FontTypeFaces::Symbol )
559 nTestMatch -= 10000000;
561 else if ( nMatchType & ImplFontAttrs::Symbol )
563 nTestMatch -= 10000;
566 // match stripped family name
567 if( !rSearchFamilyName.isEmpty() && (rSearchFamilyName == pData->GetMatchFamilyName()) )
569 nTestMatch += 1000000*3;
572 // match ALLSCRIPT? attribute
573 if( nSearchType & ImplFontAttrs::AllScript )
575 if( nMatchType & ImplFontAttrs::AllScript )
577 nTestMatch += 1000000*2;
579 if( nSearchType & ImplFontAttrs::AllSubscript )
581 if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::AllSubscript) )
582 nTestMatch += 1000000*2;
583 if( ImplFontAttrs::None != ((nSearchType ^ nMatchType) & ImplFontAttrs::BrushScript) )
584 nTestMatch -= 1000000;
587 else if( nMatchType & ImplFontAttrs::AllScript )
589 nTestMatch -= 1000000;
592 // test MONOSPACE+TYPEWRITER attributes
593 if( nSearchType & ImplFontAttrs::Fixed )
595 if( nMatchType & ImplFontAttrs::Fixed )
596 nTestMatch += 1000000*2;
597 // a typewriter attribute is even better
598 if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::Typewriter) )
599 nTestMatch += 10000*2;
601 else if( nMatchType & ImplFontAttrs::Fixed )
603 nTestMatch -= 1000000;
606 // test SPECIAL attribute
607 if( nSearchType & ImplFontAttrs::Special )
609 if( nMatchType & ImplFontAttrs::Special )
611 nTestMatch += 10000;
613 else if( !(nSearchType & ImplFontAttrs::AllSerifStyle) )
615 if( nMatchType & ImplFontAttrs::Serif )
617 nTestMatch += 1000*2;
619 else if( nMatchType & ImplFontAttrs::SansSerif )
621 nTestMatch += 1000;
625 else if( (nMatchType & ImplFontAttrs::Special) && !(nSearchType & ImplFontAttrs::Symbol) )
627 nTestMatch -= 1000000;
630 // test DECORATIVE attribute
631 if( nSearchType & ImplFontAttrs::Decorative )
633 if( nMatchType & ImplFontAttrs::Decorative )
635 nTestMatch += 10000;
637 else if( !(nSearchType & ImplFontAttrs::AllSerifStyle) )
639 if( nMatchType & ImplFontAttrs::Serif )
640 nTestMatch += 1000*2;
641 else if ( nMatchType & ImplFontAttrs::SansSerif )
642 nTestMatch += 1000;
645 else if( nMatchType & ImplFontAttrs::Decorative )
647 nTestMatch -= 1000000;
650 // test TITLE+CAPITALS attributes
651 if( nSearchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals) )
653 if( nMatchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals) )
655 nTestMatch += 1000000*2;
657 if( ImplFontAttrs::None == ((nSearchType^nMatchType) & ImplFontAttrs(ImplFontAttrs::Titling | ImplFontAttrs::Capitals)))
659 nTestMatch += 1000000;
661 else if( (nMatchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals)) &&
662 (nMatchType & (ImplFontAttrs::Standard | ImplFontAttrs::Default)) )
664 nTestMatch += 1000000;
667 else if( nMatchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals) )
669 nTestMatch -= 1000000;
672 // test OUTLINE+SHADOW attributes
673 if( nSearchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow) )
675 if( nMatchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow) )
677 nTestMatch += 1000000*2;
679 if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs(ImplFontAttrs::Outline | ImplFontAttrs::Shadow)) )
681 nTestMatch += 1000000;
683 else if( (nMatchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow)) &&
684 (nMatchType & (ImplFontAttrs::Standard | ImplFontAttrs::Default)) )
686 nTestMatch += 1000000;
689 else if ( nMatchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow) )
691 nTestMatch -= 1000000;
694 // test font name substrings
695 // TODO: calculate name matching score using e.g. Levenstein distance
696 if( (rSearchFamilyName.getLength() >= 4) &&
697 (pData->GetMatchFamilyName().getLength() >= 4) &&
698 ((rSearchFamilyName.indexOf( pData->GetMatchFamilyName() ) != -1) ||
699 (pData->GetMatchFamilyName().indexOf( rSearchFamilyName ) != -1)) )
701 nTestMatch += 5000;
703 // test SERIF attribute
704 if( nSearchType & ImplFontAttrs::Serif )
706 if( nMatchType & ImplFontAttrs::Serif )
707 nTestMatch += 1000000*2;
708 else if( nMatchType & ImplFontAttrs::SansSerif )
709 nTestMatch -= 1000000;
712 // test SANSERIF attribute
713 if( nSearchType & ImplFontAttrs::SansSerif )
715 if( nMatchType & ImplFontAttrs::SansSerif )
716 nTestMatch += 1000000;
717 else if ( nMatchType & ImplFontAttrs::Serif )
718 nTestMatch -= 1000000;
721 // test ITALIC attribute
722 if( nSearchType & ImplFontAttrs::Italic )
724 if( pData->GetTypeFaces() & FontTypeFaces::Italic )
725 nTestMatch += 1000000*3;
726 if( nMatchType & ImplFontAttrs::Italic )
727 nTestMatch += 1000000;
729 else if( !(nSearchType & ImplFontAttrs::AllScript) &&
730 ((nMatchType & ImplFontAttrs::Italic) ||
731 !(pData->GetTypeFaces() & FontTypeFaces::NoneItalic)) )
733 nTestMatch -= 1000000*2;
736 // test WIDTH attribute
737 if( (eSearchWidth != WIDTH_DONTKNOW) && (eSearchWidth != WIDTH_NORMAL) )
739 if( eSearchWidth < WIDTH_NORMAL )
741 if( eSearchWidth == eMatchWidth )
742 nTestMatch += 1000000*3;
743 else if( (eMatchWidth < WIDTH_NORMAL) && (eMatchWidth != WIDTH_DONTKNOW) )
744 nTestMatch += 1000000;
746 else
748 if( eSearchWidth == eMatchWidth )
749 nTestMatch += 1000000*3;
750 else if( eMatchWidth > WIDTH_NORMAL )
751 nTestMatch += 1000000;
754 else if( (eMatchWidth != WIDTH_DONTKNOW) && (eMatchWidth != WIDTH_NORMAL) )
756 nTestMatch -= 1000000;
759 // test WEIGHT attribute
760 if( (eSearchWeight != WEIGHT_DONTKNOW) &&
761 (eSearchWeight != WEIGHT_NORMAL) &&
762 (eSearchWeight != WEIGHT_MEDIUM) )
764 if( eSearchWeight < WEIGHT_NORMAL )
766 if( pData->GetTypeFaces() & FontTypeFaces::Light )
767 nTestMatch += 1000000;
768 if( (eMatchWeight < WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_DONTKNOW) )
769 nTestMatch += 1000000;
771 else
773 if( pData->GetTypeFaces() & FontTypeFaces::Bold )
774 nTestMatch += 1000000;
775 if( eMatchWeight > WEIGHT_BOLD )
776 nTestMatch += 1000000;
779 else if( ((eMatchWeight != WEIGHT_DONTKNOW) &&
780 (eMatchWeight != WEIGHT_NORMAL) &&
781 (eMatchWeight != WEIGHT_MEDIUM)) ||
782 !(pData->GetTypeFaces() & FontTypeFaces::Normal) )
784 nTestMatch -= 1000000;
787 // prefer scalable fonts
788 if( pData->GetTypeFaces() & FontTypeFaces::Scalable )
789 nTestMatch += 10000*4;
790 else
791 nTestMatch -= 10000*4;
793 // test STANDARD+DEFAULT+FULL+NORMAL attributes
794 if( nMatchType & ImplFontAttrs::Standard )
795 nTestMatch += 10000*2;
796 if( nMatchType & ImplFontAttrs::Default )
797 nTestMatch += 10000;
798 if( nMatchType & ImplFontAttrs::Full )
799 nTestMatch += 10000;
800 if( nMatchType & ImplFontAttrs::Normal )
801 nTestMatch += 10000;
803 // test OTHERSTYLE attribute
804 if( ((nSearchType ^ nMatchType) & ImplFontAttrs::OtherStyle) != ImplFontAttrs::None )
806 nTestMatch -= 10000;
809 // test ROUNDED attribute
810 if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::Rounded) )
811 nTestMatch += 1000;
813 // test TYPEWRITER attribute
814 if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::Typewriter) )
815 nTestMatch += 1000;
817 // test GOTHIC attribute
818 if( nSearchType & ImplFontAttrs::Gothic )
820 if( nMatchType & ImplFontAttrs::Gothic )
821 nTestMatch += 1000*3;
822 if( nMatchType & ImplFontAttrs::SansSerif )
823 nTestMatch += 1000*2;
826 // test SCHOOLBOOK attribute
827 if( nSearchType & ImplFontAttrs::Schoolbook )
829 if( nMatchType & ImplFontAttrs::Schoolbook )
830 nTestMatch += 1000*3;
831 if( nMatchType & ImplFontAttrs::Serif )
832 nTestMatch += 1000*2;
835 // compare with best matching font yet
836 if ( nTestMatch > nBestMatch )
838 pFoundData = pData;
839 nBestMatch = nTestMatch;
840 nBestType = nMatchType;
842 else if( nTestMatch == nBestMatch )
844 // some fonts are more suitable defaults
845 if( nMatchType & ImplFontAttrs::Default )
847 pFoundData = pData;
848 nBestType = nMatchType;
850 else if( (nMatchType & ImplFontAttrs::Standard) &&
851 !(nBestType & ImplFontAttrs::Default) )
853 pFoundData = pData;
854 nBestType = nMatchType;
859 return pFoundData;
862 PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyOfDefaultFont() const
864 // try to find one of the default fonts of the
865 // UNICODE, SANSSERIF, SERIF or FIXED default font lists
866 PhysicalFontFamily* pFoundData = nullptr;
867 if (!utl::ConfigManager::IsAvoidConfig())
869 const utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
870 LanguageTag aLanguageTag( OUString( "en"));
871 OUString aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::SANS_UNICODE );
872 pFoundData = FindFontFamilyByTokenNames( aFontname );
874 if( pFoundData )
875 return pFoundData;
877 aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::SANS );
878 pFoundData = FindFontFamilyByTokenNames( aFontname );
879 if( pFoundData )
880 return pFoundData;
882 aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::SERIF );
883 pFoundData = FindFontFamilyByTokenNames( aFontname );
884 if( pFoundData )
885 return pFoundData;
887 aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::FIXED );
888 pFoundData = FindFontFamilyByTokenNames( aFontname );
889 if( pFoundData )
890 return pFoundData;
893 // now try to find a reasonable non-symbol font
895 ImplInitMatchData();
897 PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.begin();
898 for(; it != maPhysicalFontFamilies.end(); ++it )
900 PhysicalFontFamily* pData = (*it).second;
901 if( pData->GetMatchType() & ImplFontAttrs::Symbol )
902 continue;
904 pFoundData = pData;
905 if( pData->GetMatchType() & (ImplFontAttrs::Default|ImplFontAttrs::Standard) )
906 break;
908 if( pFoundData )
909 return pFoundData;
911 // finding any font is better than finding no font at all
912 it = maPhysicalFontFamilies.begin();
913 if( it != maPhysicalFontFamilies.end() )
914 pFoundData = (*it).second;
916 return pFoundData;
919 PhysicalFontCollection* PhysicalFontCollection::Clone() const
921 PhysicalFontCollection* pClonedCollection = new PhysicalFontCollection;
922 pClonedCollection->mbMapNames = mbMapNames;
923 pClonedCollection->mpPreMatchHook = mpPreMatchHook;
924 pClonedCollection->mpFallbackHook = mpFallbackHook;
926 // TODO: clone the config-font attributes too?
927 pClonedCollection->mbMatchData = false;
929 PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.begin();
930 for(; it != maPhysicalFontFamilies.end(); ++it )
932 const PhysicalFontFamily* pFontFace = (*it).second;
933 pFontFace->UpdateCloneFontList(*pClonedCollection);
936 return pClonedCollection;
939 ImplDeviceFontList* PhysicalFontCollection::GetDeviceFontList() const
941 ImplDeviceFontList* pDeviceFontList = new ImplDeviceFontList;
943 PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.begin();
944 for(; it != maPhysicalFontFamilies.end(); ++it )
946 const PhysicalFontFamily* pFontFamily = (*it).second;
947 pFontFamily->UpdateDevFontList( *pDeviceFontList );
950 return pDeviceFontList;
953 ImplDeviceFontSizeList* PhysicalFontCollection::GetDeviceFontSizeList( const OUString& rFontName ) const
955 ImplDeviceFontSizeList* pDeviceFontSizeList = new ImplDeviceFontSizeList;
957 PhysicalFontFamily* pFontFamily = FindFontFamily( rFontName );
958 if( pFontFamily != nullptr )
960 std::set<int> rHeights;
961 pFontFamily->GetFontHeights( rHeights );
963 std::set<int>::const_iterator it = rHeights.begin();
964 for(; it != rHeights.begin(); ++it )
965 pDeviceFontSizeList->Add( *it );
968 return pDeviceFontSizeList;
971 PhysicalFontFamily* PhysicalFontCollection::FindFontFamily( FontSelectPattern& rFSD ) const
973 // give up if no fonts are available
974 if( !Count() )
975 return nullptr;
977 bool bMultiToken = false;
978 sal_Int32 nTokenPos = 0;
979 OUString& aSearchName = rFSD.maSearchName; // TODO: get rid of reference
980 for(;;)
982 rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos );
983 aSearchName = rFSD.maTargetName;
985 // Until features are properly supported, they are appended to the
986 // font name, so we need to strip them off so the font is found.
987 sal_Int32 nFeat = aSearchName.indexOf(FontSelectPatternAttributes::FEAT_PREFIX);
988 OUString aOrigName = rFSD.maTargetName;
989 OUString aBaseFontName = aSearchName.copy( 0, (nFeat != -1) ? nFeat : aSearchName.getLength() );
991 if (nFeat != -1)
993 aSearchName = aBaseFontName;
994 rFSD.maTargetName = aBaseFontName;
997 aSearchName = GetEnglishSearchFontName( aSearchName );
998 ImplFontSubstitute( aSearchName );
999 // #114999# special emboldening for Ricoh fonts
1000 // TODO: smarter check for special cases by using PreMatch infrastructure?
1001 if( (rFSD.GetWeight() > WEIGHT_MEDIUM) &&
1002 aSearchName.startsWithIgnoreAsciiCase( "hg" ) )
1004 OUString aBoldName;
1005 if( aSearchName.startsWithIgnoreAsciiCase( "hggothicb" ) )
1006 aBoldName = "hggothice";
1007 else if( aSearchName.startsWithIgnoreAsciiCase( "hgpgothicb" ) )
1008 aBoldName = "hgpgothice";
1009 else if( aSearchName.startsWithIgnoreAsciiCase( "hgminchol" ) )
1010 aBoldName = "hgminchob";
1011 else if( aSearchName.startsWithIgnoreAsciiCase( "hgpminchol" ) )
1012 aBoldName = "hgpminchob";
1013 else if( aSearchName.equalsIgnoreAsciiCase( "hgminchob" ) )
1014 aBoldName = "hgminchoe";
1015 else if( aSearchName.equalsIgnoreAsciiCase( "hgpminchob" ) )
1016 aBoldName = "hgpminchoe";
1018 if( !aBoldName.isEmpty() && ImplFindFontFamilyBySearchName( aBoldName ) )
1020 // the other font is available => use it
1021 aSearchName = aBoldName;
1022 // prevent synthetic emboldening of bold version
1023 rFSD.SetWeight(WEIGHT_DONTKNOW);
1027 // restore the features to make the font selection data unique
1028 rFSD.maTargetName = aOrigName;
1030 // check if the current font name token or its substitute is valid
1031 PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySearchName( aSearchName );
1032 if( pFoundData )
1033 return pFoundData;
1035 // some systems provide special customization
1036 // e.g. they suggest "serif" as UI-font, but this name cannot be used directly
1037 // because the system wants to map it to another font first, e.g. "Helvetica"
1039 // use the target name to search in the prematch hook
1040 rFSD.maTargetName = aBaseFontName;
1042 // Related: fdo#49271 RTF files often contain weird-ass
1043 // Win 3.1/Win95 style fontnames which attempt to put the
1044 // charset encoding into the filename
1045 // http://www.webcenter.ru/~kazarn/eng/fonts_ttf.htm
1046 OUString sStrippedName = StripScriptFromName(rFSD.maTargetName);
1047 if (sStrippedName != rFSD.maTargetName)
1049 rFSD.maTargetName = sStrippedName;
1050 aSearchName = GetEnglishSearchFontName(rFSD.maTargetName);
1051 pFoundData = ImplFindFontFamilyBySearchName(aSearchName);
1052 if( pFoundData )
1053 return pFoundData;
1056 if( mpPreMatchHook )
1058 if( mpPreMatchHook->FindFontSubstitute( rFSD ) )
1059 aSearchName = GetEnglishSearchFontName( aSearchName );
1062 // the prematch hook uses the target name to search, but we now need
1063 // to restore the features to make the font selection data unique
1064 rFSD.maTargetName = aOrigName;
1066 pFoundData = ImplFindFontFamilyBySearchName( aSearchName );
1067 if( pFoundData )
1068 return pFoundData;
1070 // break after last font name token was checked unsuccessfully
1071 if( nTokenPos == -1)
1072 break;
1073 bMultiToken = true;
1076 // if the first font was not available find the next available font in
1077 // the semicolon separated list of font names. A font is also considered
1078 // available when there is a matching entry in the Tools->Options->Fonts
1079 // dialog with neither ALWAYS nor SCREENONLY flags set and the substitution
1080 // font is available
1081 for( nTokenPos = 0; nTokenPos != -1; )
1083 if( bMultiToken )
1085 rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos );
1086 aSearchName = GetEnglishSearchFontName( rFSD.maTargetName );
1088 else
1089 nTokenPos = -1;
1090 if( mpPreMatchHook )
1091 if( mpPreMatchHook->FindFontSubstitute( rFSD ) )
1092 aSearchName = GetEnglishSearchFontName( aSearchName );
1093 ImplFontSubstitute( aSearchName );
1094 PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySearchName( aSearchName );
1095 if( pFoundData )
1096 return pFoundData;
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 )
1103 nTokenPos = 0;
1104 rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos );
1105 aSearchName = GetEnglishSearchFontName( rFSD.maTargetName );
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 );
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 )
1120 PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySearchName( aSearchShortName );
1121 if( pFoundData )
1123 #ifdef UNX
1124 /* #96738# don't use mincho as an 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
1132 return pFoundData;
1137 // use font fallback
1138 const utl::FontNameAttr* pFontAttr = nullptr;
1139 if (!aSearchName.isEmpty() && !utl::ConfigManager::IsAvoidConfig())
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 );
1150 // try the font substitutions suggested by the fallback info
1151 if( pFontAttr )
1153 PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySubstFontAttr( *pFontAttr );
1154 if( pFoundData )
1155 return pFoundData;
1159 // if a target symbol font is not available use a default symbol font
1160 if( rFSD.IsSymbolFont() )
1162 LanguageTag aDefaultLanguageTag( OUString( "en"));
1163 if (utl::ConfigManager::IsAvoidConfig())
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;
1172 // now try the other font name tokens
1173 while( nTokenPos != -1 )
1175 rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos );
1176 if( rFSD.maTargetName.isEmpty() )
1177 continue;
1179 aSearchName = GetEnglishSearchFontName( rFSD.maTargetName );
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 );
1189 // use a shortend token name if available
1190 if( aTempShortName != aSearchName )
1192 PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySearchName( aTempShortName );
1193 if( pFoundData )
1194 return pFoundData;
1197 const utl::FontNameAttr* pTempFontAttr = nullptr;
1198 if (!utl::ConfigManager::IsAvoidConfig())
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 );
1206 if ( !pTempFontAttr && (aTempShortName != aSearchName) )
1207 pTempFontAttr = rFontSubst.getSubstInfo( aTempShortName );
1209 if ( !pTempFontAttr && (aTempFamilyName != aTempShortName) )
1210 pTempFontAttr = rFontSubst.getSubstInfo( aTempFamilyName );
1213 // try the font substitutions suggested by the fallback info
1214 if( pTempFontAttr )
1216 PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySubstFontAttr( *pTempFontAttr );
1217 if( pFoundData )
1218 return pFoundData;
1219 if( !pFontAttr )
1220 pFontAttr = pTempFontAttr;
1224 // if still needed use the alias names of the installed fonts
1225 if( mbMapNames )
1227 PhysicalFontFamily* pFoundData = ImplFindFontFamilyByAliasName( rFSD.maTargetName, aSearchShortName );
1228 if( pFoundData )
1229 return pFoundData;
1232 // if still needed use the font request's attributes to find a good match
1233 if (MsLangId::isSimplifiedChinese(rFSD.meLanguage))
1234 nSearchType |= ImplFontAttrs::CJK | ImplFontAttrs::CJK_SC;
1235 else if (MsLangId::isTraditionalChinese(rFSD.meLanguage))
1236 nSearchType |= ImplFontAttrs::CJK | ImplFontAttrs::CJK_TC;
1237 else if (MsLangId::isKorean(rFSD.meLanguage))
1238 nSearchType |= ImplFontAttrs::CJK | ImplFontAttrs::CJK_KR;
1239 else if (rFSD.meLanguage == LANGUAGE_JAPANESE)
1240 nSearchType |= ImplFontAttrs::CJK | ImplFontAttrs::CJK_JP;
1241 else
1243 nSearchType |= lcl_IsCJKFont( rFSD.GetFamilyName() );
1244 if( rFSD.IsSymbolFont() )
1245 nSearchType |= ImplFontAttrs::Symbol;
1248 PhysicalFontFamily::CalcType( nSearchType, eSearchWeight, eSearchWidth, rFSD.GetFamilyType(), pFontAttr );
1249 PhysicalFontFamily* pFoundData = FindFontFamilyByAttributes( nSearchType,
1250 eSearchWeight, eSearchWidth, rFSD.GetItalic(), aSearchFamilyName );
1252 if( pFoundData )
1254 // overwrite font selection attributes using info from the typeface flags
1255 if( (eSearchWeight >= WEIGHT_BOLD) &&
1256 (eSearchWeight > rFSD.GetWeight()) &&
1257 (pFoundData->GetTypeFaces() & FontTypeFaces::Bold) )
1259 rFSD.SetWeight( eSearchWeight );
1261 else if( (eSearchWeight < WEIGHT_NORMAL) &&
1262 (eSearchWeight < rFSD.GetWeight()) &&
1263 (eSearchWeight != WEIGHT_DONTKNOW) &&
1264 (pFoundData->GetTypeFaces() & FontTypeFaces::Light) )
1266 rFSD.SetWeight( eSearchWeight );
1269 if( (nSearchType & ImplFontAttrs::Italic) &&
1270 ((rFSD.GetItalic() == ITALIC_DONTKNOW) ||
1271 (rFSD.GetItalic() == ITALIC_NONE)) &&
1272 (pFoundData->GetTypeFaces() & FontTypeFaces::Italic) )
1274 rFSD.SetItalic( ITALIC_NORMAL );
1277 else
1279 // if still needed fall back to default fonts
1280 pFoundData = ImplFindFontFamilyOfDefaultFont();
1283 return pFoundData;
1286 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */