tdf#130857 qt weld: Support mail merge "Server Auth" dialog
[LibreOffice.git] / vcl / source / font / PhysicalFontCollection.cxx
blob6c3838772546590d4faed58fd4159ec5d9b55d53
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 <sal/config.h>
22 #include <memory>
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>
30 #include <font/PhysicalFontFaceCollection.hxx>
31 #include <font/PhysicalFontCollection.hxx>
32 #include <font/fontsubstitution.hxx>
34 static ImplFontAttrs lcl_IsCJKFont( std::u16string_view rFontName )
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++)
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;
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;
54 // chinese
55 if ( (ch >= 0x3400) && (ch <= 0x9FFF) )
56 return ImplFontAttrs::CJK|ImplFontAttrs::CJK_TC|ImplFontAttrs::CJK_SC;
58 // cjk
59 if ( ((ch >= 0x3000) && (ch <= 0xD7AF)) ||
60 ((ch >= 0xFF00) && (ch <= 0xFFEE)) )
61 return ImplFontAttrs::CJK;
65 return ImplFontAttrs::None;
68 namespace vcl::font
71 PhysicalFontCollection::PhysicalFontCollection()
72 : mbMatchData( false )
73 , mpPreMatchHook( nullptr )
74 , mpFallbackHook( nullptr )
75 , mnFallbackCount( -1 )
78 PhysicalFontCollection::~PhysicalFontCollection()
80 Clear();
83 void PhysicalFontCollection::SetPreMatchHook(PreMatchFontSubstitution* pHook)
85 mpPreMatchHook = pHook;
88 void PhysicalFontCollection::SetFallbackHook(GlyphFallbackFontSubstitution* pHook)
90 mpFallbackHook = pHook;
93 void PhysicalFontCollection::Clear()
95 // remove fallback lists
96 mpFallbackList.reset();
97 mnFallbackCount = -1;
99 // clear all entries in the device font list
100 maPhysicalFontFamilies.clear();
102 // match data must be recalculated too
103 mbMatchData = false;
106 void PhysicalFontCollection::ImplInitGenericGlyphFallback() const
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
135 bool bHasEudc = false;
136 int nMaxLevel = 0;
137 int nBestQuality = 0;
138 std::unique_ptr<std::array<PhysicalFontFamily*,MAX_GLYPHFALLBACK>> pFallbackList;
140 for( const char** ppNames = &aGlyphFallbackList[0];; ++ppNames )
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
145 if( nBestQuality > 0 )
146 if( ++nMaxLevel >= MAX_GLYPHFALLBACK )
147 break;
149 if( !ppNames[1] )
150 break;
152 nBestQuality = 0;
153 continue;
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 );
160 if( !pFallbackFont )
161 continue;
163 // keep the best font of the glyph fallback sub-list
164 if( nBestQuality < pFallbackFont->GetMinQuality() )
166 nBestQuality = pFallbackFont->GetMinQuality();
167 // store available glyph fallback fonts
168 if( !pFallbackList )
169 pFallbackList.reset(new std::array<PhysicalFontFamily*,MAX_GLYPHFALLBACK>);
171 (*pFallbackList)[ nMaxLevel ] = pFallbackFont;
172 if( !bHasEudc && !nMaxLevel )
173 bHasEudc = !strncmp( *ppNames, "eudc", 5 );
177 mnFallbackCount = nMaxLevel;
178 mpFallbackList = std::move(pFallbackList);
181 PhysicalFontFamily* PhysicalFontCollection::GetGlyphFallbackFont(FontSelectPattern& rFontSelData,
182 LogicalFontInstance* pFontInstance,
183 OUString& rMissingCodes,
184 int nFallbackLevel) const
186 PhysicalFontFamily* pFallbackData = nullptr;
188 // find a matching font candidate for platform specific glyph fallback
189 if( mpFallbackHook )
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() )
198 cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
199 bCached = pFontInstance->GetFallbackForUnicode(cChar, rFontSelData.GetWeight(),
200 &rFontSelData.maSearchName,
201 &rFontSelData.mbEmbolden,
202 &rFontSelData.maItalicMatrix);
204 // ignore entries which don't have a fallback
205 if( !bCached || !rFontSelData.maSearchName.isEmpty() )
206 break;
209 if( bCached )
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;
219 while( nStrIndex < rMissingCodes.getLength() )
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)
228 pRemainingCodes[ nRemainingLength++ ] = cChar;
231 rMissingCodes = OUString( pRemainingCodes.get(), nRemainingLength );
233 else
235 OUString aOldMissingCodes = rMissingCodes;
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();
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(;;)
250 if (!pFontInstance->GetFallbackForUnicode(cChar, rFontSelData.GetWeight(),
251 &rFontSelData.maSearchName,
252 &rFontSelData.mbEmbolden,
253 &rFontSelData.maItalicMatrix))
255 pFontInstance->AddFallbackForUnicode(cChar, rFontSelData.GetWeight(),
256 rFontSelData.maSearchName,
257 rFontSelData.mbEmbolden,
258 rFontSelData.maItalicMatrix);
260 if( nStrIndex >= aOldMissingCodes.getLength() )
261 break;
262 cChar = aOldMissingCodes.iterateCodePoints( &nStrIndex );
264 if( !rFontSelData.maSearchName.isEmpty() )
266 // remove cache entries that were still not resolved
267 for( nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
269 cChar = rMissingCodes.iterateCodePoints( &nStrIndex );
270 pFontInstance->IgnoreFallbackForUnicode( cChar, rFontSelData.GetWeight(), rFontSelData.maSearchName );
275 // find the matching device font
276 if( !rFontSelData.maSearchName.isEmpty() )
277 pFallbackData = FindFontFamily( rFontSelData.maSearchName );
280 // else find a matching font candidate for generic glyph fallback
281 if( !pFallbackData )
283 // initialize font candidates for generic glyph fallback if needed
284 if( mnFallbackCount < 0 )
285 ImplInitGenericGlyphFallback();
287 // TODO: adjust nFallbackLevel by number of levels resolved by the fallback hook
288 if( nFallbackLevel < mnFallbackCount )
289 pFallbackData = (*mpFallbackList)[ nFallbackLevel ];
292 return pFallbackData;
295 void PhysicalFontCollection::Add(PhysicalFontFace* pNewData)
297 OUString aSearchName = GetEnglishSearchFontName( pNewData->GetFamilyName() );
299 PhysicalFontFamily* pFoundData = FindOrCreateFontFamily(aSearchName);
301 pFoundData->AddFontFace( pNewData );
304 // find the font from the normalized font family name
305 PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyBySearchName(const OUString& rSearchName) const
307 // must be called with a normalized name.
308 assert( GetEnglishSearchFontName( rSearchName ) == rSearchName );
310 PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.find( rSearchName );
311 if( it == maPhysicalFontFamilies.end() )
312 return nullptr;
314 PhysicalFontFamily* pFoundData = (*it).second.get();
315 return pFoundData;
318 PhysicalFontFamily* PhysicalFontCollection::FindFontFamily(std::u16string_view rFontName) const
320 return ImplFindFontFamilyBySearchName( GetEnglishSearchFontName( rFontName ) );
323 PhysicalFontFamily *PhysicalFontCollection::FindOrCreateFontFamily(OUString const& rFamilyName)
325 PhysicalFontFamilies::const_iterator it = maPhysicalFontFamilies.find( rFamilyName );
326 PhysicalFontFamily* pFoundData = nullptr;
328 if( it != maPhysicalFontFamilies.end() )
329 pFoundData = (*it).second.get();
331 if( !pFoundData )
333 pFoundData = new PhysicalFontFamily(rFamilyName);
334 maPhysicalFontFamilies[ rFamilyName ].reset(pFoundData);
337 return pFoundData;
340 PhysicalFontFamily* PhysicalFontCollection::FindFontFamilyByTokenNames(std::u16string_view rTokenStr) const
342 PhysicalFontFamily* pFoundData = nullptr;
344 // use normalized font name tokens to find the font
345 for( sal_Int32 nTokenPos = 0; nTokenPos != -1; )
347 std::u16string_view aFamilyName = GetNextFontToken( rTokenStr, nTokenPos );
348 if( aFamilyName.empty() )
349 continue;
351 pFoundData = FindFontFamily( aFamilyName );
353 if( pFoundData )
354 break;
357 return pFoundData;
360 PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyBySubstFontAttr(utl::FontNameAttr const& rFontAttr) const
362 PhysicalFontFamily* pFoundData = nullptr;
364 // use the font substitutions suggested by the FontNameAttr to find the font
365 for (auto const& substitution : rFontAttr.Substitutions)
367 pFoundData = FindFontFamily(substitution);
368 if( pFoundData )
369 return pFoundData;
372 // use known attributes from the configuration to find a matching substitute
373 const ImplFontAttrs nSearchType = rFontAttr.Type;
374 if( nSearchType != ImplFontAttrs::None )
376 const FontWeight eSearchWeight = rFontAttr.Weight;
377 const FontWidth eSearchWidth = rFontAttr.Width;
378 const FontItalic eSearchSlant = ITALIC_DONTKNOW;
380 pFoundData = FindFontFamilyByAttributes( nSearchType,
381 eSearchWeight, eSearchWidth, eSearchSlant, u"" );
383 if( pFoundData )
384 return pFoundData;
387 return nullptr;
390 void PhysicalFontCollection::ImplInitMatchData() const
392 // short circuit if already done
393 if( mbMatchData )
394 return;
395 mbMatchData = true;
397 if (comphelper::IsFuzzing())
398 return;
400 // calculate MatchData for all entries
401 const utl::FontSubstConfiguration& rFontSubst = utl::FontSubstConfiguration::get();
403 for (auto const& family : maPhysicalFontFamilies)
405 const OUString& rSearchName = family.first;
406 PhysicalFontFamily* pEntry = family.second.get();
408 pEntry->InitMatchData( rFontSubst, rSearchName );
412 PhysicalFontFamily* PhysicalFontCollection::FindFontFamilyByAttributes(ImplFontAttrs nSearchType,
413 FontWeight eSearchWeight,
414 FontWidth eSearchWidth,
415 FontItalic eSearchItalic,
416 std::u16string_view rSearchFamilyName ) const
418 if( (eSearchItalic != ITALIC_NONE) && (eSearchItalic != ITALIC_DONTKNOW) )
419 nSearchType |= ImplFontAttrs::Italic;
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;
427 ImplInitMatchData();
428 PhysicalFontFamily* pFoundData = nullptr;
430 tools::Long nBestMatch = 40000;
431 ImplFontAttrs nBestType = ImplFontAttrs::None;
433 for (auto const& family : maPhysicalFontFamilies)
435 PhysicalFontFamily* pData = family.second.get();
437 // Get all information about the matching font
438 ImplFontAttrs nMatchType = pData->GetMatchType();
439 FontWeight eMatchWeight= pData->GetMatchWeight();
440 FontWidth eMatchWidth = pData->GetMatchWidth();
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;
457 // test CJK script attributes
458 if ( nSearchType & ImplFontAttrs::CJK )
460 // if the matching font doesn't support any CJK languages, then
461 // it is not appropriate
462 if ( !(nMatchType & ImplFontAttrs::CJK_AllLang) )
464 nTestMatch -= 10000000;
466 else
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;
478 else if ( nMatchType & ImplFontAttrs::CJK )
480 nTestMatch -= 10000000;
483 // test CTL script attributes
484 if( nSearchType & ImplFontAttrs::CTL )
486 if( nMatchType & ImplFontAttrs::CTL )
487 nTestMatch += 10000000*2;
488 if( nMatchType & ImplFontAttrs::Full )
489 nTestMatch += 10000000;
491 else if ( nMatchType & ImplFontAttrs::CTL )
493 nTestMatch -= 10000000;
496 // test LATIN script attributes
497 if( nSearchType & ImplFontAttrs::NoneLatin )
499 if( nMatchType & ImplFontAttrs::NoneLatin )
500 nTestMatch += 10000000*2;
501 if( nMatchType & ImplFontAttrs::Full )
502 nTestMatch += 10000000;
505 // test SYMBOL attributes
506 if ( nSearchType & ImplFontAttrs::Symbol )
508 const OUString& rSearchName = family.first;
509 // prefer some special known symbol fonts
510 if ( rSearchName == "starsymbol" )
512 nTestMatch += 10000000*6+(10000*3);
514 else if ( rSearchName == "opensymbol" )
516 nTestMatch += 10000000*6;
518 else if ( rSearchName == "starbats" ||
519 rSearchName == "wingdings" ||
520 rSearchName == "monotypesorts" ||
521 rSearchName == "dingbats" ||
522 rSearchName == "zapfdingbats" )
524 nTestMatch += 10000000*5;
526 else if (pData->GetTypeFaces() & FontTypeFaces::Symbol)
528 nTestMatch += 10000000*4;
530 else
532 if( nMatchType & ImplFontAttrs::Symbol )
533 nTestMatch += 10000000*2;
534 if( nMatchType & ImplFontAttrs::Full )
535 nTestMatch += 10000000;
538 else if ((pData->GetTypeFaces() & (FontTypeFaces::Symbol | FontTypeFaces::NoneSymbol)) == FontTypeFaces::Symbol)
540 nTestMatch -= 10000000;
542 else if ( nMatchType & ImplFontAttrs::Symbol )
544 nTestMatch -= 10000;
547 // match stripped family name
548 if( !rSearchFamilyName.empty() && (rSearchFamilyName == pData->GetMatchFamilyName()) )
550 nTestMatch += 1000000*3;
553 // match ALLSCRIPT? attribute
554 if( nSearchType & ImplFontAttrs::AllScript )
556 if( nMatchType & ImplFontAttrs::AllScript )
558 nTestMatch += 1000000*2;
560 if( nSearchType & ImplFontAttrs::AllSubscript )
562 if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::AllSubscript) )
563 nTestMatch += 1000000*2;
564 if( ImplFontAttrs::None != ((nSearchType ^ nMatchType) & ImplFontAttrs::BrushScript) )
565 nTestMatch -= 1000000;
568 else if( nMatchType & ImplFontAttrs::AllScript )
570 nTestMatch -= 1000000;
573 // test MONOSPACE+TYPEWRITER attributes
574 if( nSearchType & ImplFontAttrs::Fixed )
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;
582 else if( nMatchType & ImplFontAttrs::Fixed )
584 nTestMatch -= 1000000;
587 // test SPECIAL attribute
588 if( nSearchType & ImplFontAttrs::Special )
590 if( nMatchType & ImplFontAttrs::Special )
592 nTestMatch += 10000;
594 else if( !(nSearchType & ImplFontAttrs::AllSerifStyle) )
596 if( nMatchType & ImplFontAttrs::Serif )
598 nTestMatch += 1000*2;
600 else if( nMatchType & ImplFontAttrs::SansSerif )
602 nTestMatch += 1000;
606 else if( (nMatchType & ImplFontAttrs::Special) && !(nSearchType & ImplFontAttrs::Symbol) )
608 nTestMatch -= 1000000;
611 // test DECORATIVE attribute
612 if( nSearchType & ImplFontAttrs::Decorative )
614 if( nMatchType & ImplFontAttrs::Decorative )
616 nTestMatch += 10000;
618 else if( !(nSearchType & ImplFontAttrs::AllSerifStyle) )
620 if( nMatchType & ImplFontAttrs::Serif )
621 nTestMatch += 1000*2;
622 else if ( nMatchType & ImplFontAttrs::SansSerif )
623 nTestMatch += 1000;
626 else if( nMatchType & ImplFontAttrs::Decorative )
628 nTestMatch -= 1000000;
631 // test TITLE+CAPITALS attributes
632 if( nSearchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals) )
634 if( nMatchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals) )
636 nTestMatch += 1000000*2;
638 if( ImplFontAttrs::None == ((nSearchType^nMatchType) & ImplFontAttrs(ImplFontAttrs::Titling | ImplFontAttrs::Capitals)))
640 nTestMatch += 1000000;
642 else if( (nMatchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals)) &&
643 (nMatchType & (ImplFontAttrs::Standard | ImplFontAttrs::Default)) )
645 nTestMatch += 1000000;
648 else if( nMatchType & (ImplFontAttrs::Titling | ImplFontAttrs::Capitals) )
650 nTestMatch -= 1000000;
653 // test OUTLINE+SHADOW attributes
654 if( nSearchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow) )
656 if( nMatchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow) )
658 nTestMatch += 1000000*2;
660 if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs(ImplFontAttrs::Outline | ImplFontAttrs::Shadow)) )
662 nTestMatch += 1000000;
664 else if( (nMatchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow)) &&
665 (nMatchType & (ImplFontAttrs::Standard | ImplFontAttrs::Default)) )
667 nTestMatch += 1000000;
670 else if ( nMatchType & (ImplFontAttrs::Outline | ImplFontAttrs::Shadow) )
672 nTestMatch -= 1000000;
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)) )
682 nTestMatch += 5000;
684 // test SERIF attribute
685 if( nSearchType & ImplFontAttrs::Serif )
687 if( nMatchType & ImplFontAttrs::Serif )
688 nTestMatch += 1000000*2;
689 else if( nMatchType & ImplFontAttrs::SansSerif )
690 nTestMatch -= 1000000;
693 // test SANSERIF attribute
694 if( nSearchType & ImplFontAttrs::SansSerif )
696 if( nMatchType & ImplFontAttrs::SansSerif )
697 nTestMatch += 1000000;
698 else if ( nMatchType & ImplFontAttrs::Serif )
699 nTestMatch -= 1000000;
702 // test ITALIC attribute
703 if( nSearchType & ImplFontAttrs::Italic )
705 if (pData->GetTypeFaces() & FontTypeFaces::Italic)
706 nTestMatch += 1000000*3;
707 if( nMatchType & ImplFontAttrs::Italic )
708 nTestMatch += 1000000;
710 else if (!(nSearchType & ImplFontAttrs::AllScript)
711 && ((nMatchType & ImplFontAttrs::Italic)
712 || !(pData->GetTypeFaces() & FontTypeFaces::NoneItalic)))
714 nTestMatch -= 1000000*2;
717 // test WIDTH attribute
718 if( (eSearchWidth != WIDTH_DONTKNOW) && (eSearchWidth != WIDTH_NORMAL) )
720 if( eSearchWidth < WIDTH_NORMAL )
722 if( eSearchWidth == eMatchWidth )
723 nTestMatch += 1000000*3;
724 else if( (eMatchWidth < WIDTH_NORMAL) && (eMatchWidth != WIDTH_DONTKNOW) )
725 nTestMatch += 1000000;
727 else
729 if( eSearchWidth == eMatchWidth )
730 nTestMatch += 1000000*3;
731 else if( eMatchWidth > WIDTH_NORMAL )
732 nTestMatch += 1000000;
735 else if( (eMatchWidth != WIDTH_DONTKNOW) && (eMatchWidth != WIDTH_NORMAL) )
737 nTestMatch -= 1000000;
740 // test WEIGHT attribute
741 if( (eSearchWeight != WEIGHT_DONTKNOW) &&
742 (eSearchWeight != WEIGHT_NORMAL) &&
743 (eSearchWeight != WEIGHT_MEDIUM) )
745 if( eSearchWeight < WEIGHT_NORMAL )
747 if (pData->GetTypeFaces() & FontTypeFaces::Light)
748 nTestMatch += 1000000;
749 if( (eMatchWeight < WEIGHT_NORMAL) && (eMatchWeight != WEIGHT_DONTKNOW) )
750 nTestMatch += 1000000;
752 else
754 if (pData->GetTypeFaces() & FontTypeFaces::Bold)
755 nTestMatch += 1000000;
756 if( eMatchWeight > WEIGHT_BOLD )
757 nTestMatch += 1000000;
760 else if (((eMatchWeight != WEIGHT_DONTKNOW)
761 && (eMatchWeight != WEIGHT_NORMAL)
762 && (eMatchWeight != WEIGHT_MEDIUM))
763 || !(pData->GetTypeFaces() & FontTypeFaces::Normal))
765 nTestMatch -= 1000000;
768 // prefer scalable fonts
769 if (pData->GetTypeFaces() & FontTypeFaces::Scalable)
770 nTestMatch += 10000*4;
771 else
772 nTestMatch -= 10000*4;
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;
784 // test OTHERSTYLE attribute
785 if( ((nSearchType ^ nMatchType) & ImplFontAttrs::OtherStyle) != ImplFontAttrs::None )
787 nTestMatch -= 10000;
790 // test ROUNDED attribute
791 if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::Rounded) )
792 nTestMatch += 1000;
794 // test TYPEWRITER attribute
795 if( ImplFontAttrs::None == ((nSearchType ^ nMatchType) & ImplFontAttrs::Typewriter) )
796 nTestMatch += 1000;
798 // test GOTHIC attribute
799 if( nSearchType & ImplFontAttrs::Gothic )
801 if( nMatchType & ImplFontAttrs::Gothic )
802 nTestMatch += 1000*3;
803 if( nMatchType & ImplFontAttrs::SansSerif )
804 nTestMatch += 1000*2;
807 // test SCHOOLBOOK attribute
808 if( nSearchType & ImplFontAttrs::Schoolbook )
810 if( nMatchType & ImplFontAttrs::Schoolbook )
811 nTestMatch += 1000*3;
812 if( nMatchType & ImplFontAttrs::Serif )
813 nTestMatch += 1000*2;
816 // compare with best matching font yet
817 if ( nTestMatch > nBestMatch )
819 pFoundData = pData;
820 nBestMatch = nTestMatch;
821 nBestType = nMatchType;
823 else if( nTestMatch == nBestMatch )
825 // some fonts are more suitable defaults
826 if( nMatchType & ImplFontAttrs::Default )
828 pFoundData = pData;
829 nBestType = nMatchType;
831 else if( (nMatchType & ImplFontAttrs::Standard) &&
832 !(nBestType & ImplFontAttrs::Default) )
834 pFoundData = pData;
835 nBestType = nMatchType;
840 return pFoundData;
843 PhysicalFontFamily* PhysicalFontCollection::ImplFindFontFamilyOfDefaultFont() const
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())
850 const utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
851 LanguageTag aLanguageTag(u"en"_ustr);
852 OUString aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::SANS_UNICODE );
853 pFoundData = FindFontFamilyByTokenNames( aFontname );
855 if( pFoundData )
856 return pFoundData;
858 aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::SANS );
859 pFoundData = FindFontFamilyByTokenNames( aFontname );
860 if( pFoundData )
861 return pFoundData;
863 aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::SERIF );
864 pFoundData = FindFontFamilyByTokenNames( aFontname );
865 if( pFoundData )
866 return pFoundData;
868 aFontname = rDefaults.getDefaultFont( aLanguageTag, DefaultFontType::FIXED );
869 pFoundData = FindFontFamilyByTokenNames( aFontname );
870 if( pFoundData )
871 return pFoundData;
874 // now try to find a reasonable non-symbol font
876 ImplInitMatchData();
878 for (auto const& family : maPhysicalFontFamilies)
880 PhysicalFontFamily* pData = family.second.get();
881 if( pData->GetMatchType() & ImplFontAttrs::Symbol )
882 continue;
884 pFoundData = pData;
885 if( pData->GetMatchType() & (ImplFontAttrs::Default|ImplFontAttrs::Standard) )
886 break;
888 if( pFoundData )
889 return pFoundData;
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();
896 return pFoundData;
899 std::shared_ptr<PhysicalFontCollection> PhysicalFontCollection::Clone() const
901 auto xClonedCollection = std::make_shared<PhysicalFontCollection>();
902 xClonedCollection->mpPreMatchHook = mpPreMatchHook;
903 xClonedCollection->mpFallbackHook = mpFallbackHook;
905 // TODO: clone the config-font attributes too?
906 xClonedCollection->mbMatchData = false;
908 for (auto const& family : maPhysicalFontFamilies)
910 const PhysicalFontFamily* pFontFace = family.second.get();
911 pFontFace->UpdateCloneFontList(*xClonedCollection);
914 return xClonedCollection;
917 std::unique_ptr<PhysicalFontFaceCollection> PhysicalFontCollection::GetFontFaceCollection() const
919 std::unique_ptr<PhysicalFontFaceCollection> pDeviceFontList(new PhysicalFontFaceCollection);
921 for (auto const& family : maPhysicalFontFamilies)
923 const PhysicalFontFamily* pFontFamily = family.second.get();
924 pFontFamily->UpdateDevFontList( *pDeviceFontList );
927 return pDeviceFontList;
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 =
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" },
943 static bool FindMetricCompatibleFont(FontSelectPattern& rFontSelData)
945 for (const auto& aSub : aMetricCompatibleMap)
947 if (rFontSelData.maSearchName == GetEnglishSearchFontName(aSub.first))
949 rFontSelData.maSearchName = aSub.second;
950 return true;
954 return false;
957 PhysicalFontFamily* PhysicalFontCollection::FindFontFamily(FontSelectPattern& rFSD) const
959 // give up if no fonts are available
960 if( !Count() )
961 return nullptr;
963 static bool noFontLookup = getenv("SAL_NO_FONT_LOOKUP") != nullptr;
964 if (noFontLookup)
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;
975 bool bMultiToken = false;
976 sal_Int32 nTokenPos = 0;
977 OUString& aSearchName = rFSD.maSearchName; // TODO: get rid of reference
978 for(;;)
980 rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos );
981 aSearchName = rFSD.maTargetName;
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() );
989 if (nFeat != -1)
991 aSearchName = aBaseFontName;
992 rFSD.maTargetName = aBaseFontName;
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" ) )
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";
1016 if( !aBoldName.isEmpty() && ImplFindFontFamilyBySearchName( aBoldName ) )
1018 // the other font is available => use it
1019 aSearchName = aBoldName;
1020 // prevent synthetic emboldening of bold version
1021 rFSD.SetWeight(WEIGHT_DONTKNOW);
1025 // restore the features to make the font selection data unique
1026 rFSD.maTargetName = aOrigName;
1028 // check if the current font name token or its substitute is valid
1029 PhysicalFontFamily* pFoundData = ImplFindFontFamilyBySearchName(aSearchName);
1030 if( pFoundData )
1031 return pFoundData;
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"
1037 // use the target name to search in the prematch hook
1038 rFSD.maTargetName = aBaseFontName;
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)
1047 rFSD.maTargetName = sStrippedName;
1048 aSearchName = GetEnglishSearchFontName(rFSD.maTargetName);
1049 pFoundData = ImplFindFontFamilyBySearchName(aSearchName);
1050 if( pFoundData )
1051 return pFoundData;
1054 if (FindMetricCompatibleFont(rFSD) ||
1055 (mpPreMatchHook && mpPreMatchHook->FindFontSubstitute(rFSD)))
1057 aSearchName = GetEnglishSearchFontName(aSearchName);
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;
1064 pFoundData = ImplFindFontFamilyBySearchName( aSearchName );
1065 if( pFoundData )
1066 return pFoundData;
1068 // break after last font name token was checked unsuccessfully
1069 if( nTokenPos == -1)
1070 break;
1071 bMultiToken = true;
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; )
1081 if( bMultiToken )
1083 rFSD.maTargetName = GetNextFontToken( rFSD.GetFamilyName(), nTokenPos );
1084 aSearchName = GetEnglishSearchFontName( rFSD.maTargetName );
1086 else
1087 nTokenPos = -1;
1088 if (FindMetricCompatibleFont(rFSD) ||
1089 (mpPreMatchHook && mpPreMatchHook->FindFontSubstitute(rFSD)))
1091 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 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
1132 return pFoundData;
1137 // use font fallback
1138 const utl::FontNameAttr* pFontAttr = nullptr;
1139 if (!aSearchName.isEmpty() && !comphelper::IsFuzzing())
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.IsMicrosoftSymbolEncoded() )
1162 LanguageTag aDefaultLanguageTag(u"en"_ustr);
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;
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 shortened 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 (!comphelper::IsFuzzing())
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 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
1235 nSearchType |= lcl_IsCJKFont( rFSD.GetFamilyName() );
1236 if( rFSD.IsMicrosoftSymbolEncoded() )
1237 nSearchType |= ImplFontAttrs::Symbol;
1240 PhysicalFontFamily::CalcType(nSearchType, eSearchWeight, eSearchWidth, rFSD.GetFamilyType(), pFontAttr);
1241 PhysicalFontFamily* pFoundData = FindFontFamilyByAttributes(nSearchType,
1242 eSearchWeight, eSearchWidth, rFSD.GetItalic(), aSearchFamilyName);
1244 if( pFoundData )
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))
1251 rFSD.SetWeight( eSearchWeight );
1253 else if ((eSearchWeight < WEIGHT_NORMAL)
1254 && (eSearchWeight < rFSD.GetWeight())
1255 && (eSearchWeight != WEIGHT_DONTKNOW)
1256 && (pFoundData->GetTypeFaces() & FontTypeFaces::Light))
1258 rFSD.SetWeight( eSearchWeight );
1261 if ((nSearchType & ImplFontAttrs::Italic)
1262 && ((rFSD.GetItalic() == ITALIC_DONTKNOW)
1263 || (rFSD.GetItalic() == ITALIC_NONE))
1264 && (pFoundData->GetTypeFaces() & FontTypeFaces::Italic))
1266 rFSD.SetItalic( ITALIC_NORMAL );
1269 else
1271 // if still needed fall back to default fonts
1272 pFoundData = ImplFindFontFamilyOfDefaultFont();
1275 return pFoundData;
1279 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */