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