gfxDWriteFontList.cpp (92883B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "mozilla/FontPropertyTypes.h" 7 #include "mozilla/MemoryReporting.h" 8 #include "mozilla/intl/OSPreferences.h" 9 10 #include "gfxDWriteFontList.h" 11 #include "gfxDWriteFonts.h" 12 #include "gfxDWriteCommon.h" 13 #include "nsUnicharUtils.h" 14 #include "nsPresContext.h" 15 #include "nsServiceManagerUtils.h" 16 #include "nsCharSeparatedTokenizer.h" 17 #include "mozilla/dom/Document.h" 18 #include "mozilla/gfx/Logging.h" 19 #include "mozilla/LookAndFeel.h" 20 #include "mozilla/Preferences.h" 21 #include "mozilla/ProfilerLabels.h" 22 #include "mozilla/Sprintf.h" 23 #include "mozilla/StaticPrefs_gfx.h" 24 #include "mozilla/glean/GfxMetrics.h" 25 #include "mozilla/WindowsProcessMitigations.h" 26 #include "nsDirectoryServiceUtils.h" 27 #include "nsDirectoryServiceDefs.h" 28 #include "nsAppDirectoryServiceDefs.h" 29 30 #include "../2d/AutoHelpersWin.h" 31 #include "gfxRect.h" 32 #include "SharedFontList-impl.h" 33 34 #include "harfbuzz/hb.h" 35 36 #define StandardFonts 37 #include "StandardFonts-win10.inc" 38 #undef StandardFonts 39 #include "StandardFonts-win10-bb.inc" 40 41 using namespace mozilla; 42 using namespace mozilla::gfx; 43 using mozilla::intl::OSPreferences; 44 45 #define LOG_FONTLIST(args) \ 46 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args) 47 #define LOG_FONTLIST_ENABLED() \ 48 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug) 49 50 #define LOG_FONTINIT(args) \ 51 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args) 52 #define LOG_FONTINIT_ENABLED() \ 53 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug) 54 55 #define LOG_CMAPDATA_ENABLED() \ 56 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug) 57 58 static __inline void BuildKeyNameFromFontName(nsACString& aName) { 59 ToLowerCase(aName); 60 } 61 62 //////////////////////////////////////////////////////////////////////////////// 63 // gfxDWriteFontFamily 64 65 gfxDWriteFontFamily::~gfxDWriteFontFamily() {} 66 67 static bool GetNameAsUtf8(nsACString& aName, IDWriteLocalizedStrings* aStrings, 68 UINT32 aIndex) { 69 AutoTArray<WCHAR, 32> name; 70 UINT32 length; 71 HRESULT hr = aStrings->GetStringLength(aIndex, &length); 72 if (FAILED(hr)) { 73 return false; 74 } 75 if (!name.SetLength(length + 1, fallible)) { 76 return false; 77 } 78 hr = aStrings->GetString(aIndex, name.Elements(), length + 1); 79 if (FAILED(hr)) { 80 return false; 81 } 82 aName.Truncate(); 83 AppendUTF16toUTF8( 84 Substring(reinterpret_cast<const char16_t*>(name.Elements()), 85 name.Length() - 1), 86 aName); 87 return true; 88 } 89 90 static bool GetEnglishOrFirstName(nsACString& aName, 91 IDWriteLocalizedStrings* aStrings) { 92 UINT32 englishIdx = 0; 93 BOOL exists; 94 HRESULT hr = aStrings->FindLocaleName(L"en-us", &englishIdx, &exists); 95 if (FAILED(hr) || !exists) { 96 // Use 0 index if english is not found. 97 englishIdx = 0; 98 } 99 return GetNameAsUtf8(aName, aStrings, englishIdx); 100 } 101 102 static HRESULT GetDirectWriteFontName(IDWriteFont* aFont, 103 nsACString& aFontName) { 104 HRESULT hr; 105 106 RefPtr<IDWriteLocalizedStrings> names; 107 hr = aFont->GetFaceNames(getter_AddRefs(names)); 108 if (FAILED(hr)) { 109 return hr; 110 } 111 112 if (!GetEnglishOrFirstName(aFontName, names)) { 113 return E_FAIL; 114 } 115 116 return S_OK; 117 } 118 119 #define FULLNAME_ID DWRITE_INFORMATIONAL_STRING_FULL_NAME 120 #define PSNAME_ID DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME 121 122 // for use in reading postscript or fullname 123 static HRESULT GetDirectWriteFaceName(IDWriteFont* aFont, 124 DWRITE_INFORMATIONAL_STRING_ID aWhichName, 125 nsACString& aFontName) { 126 HRESULT hr; 127 128 BOOL exists; 129 RefPtr<IDWriteLocalizedStrings> infostrings; 130 hr = aFont->GetInformationalStrings(aWhichName, getter_AddRefs(infostrings), 131 &exists); 132 if (FAILED(hr) || !exists) { 133 return E_FAIL; 134 } 135 136 if (!GetEnglishOrFirstName(aFontName, infostrings)) { 137 return E_FAIL; 138 } 139 140 return S_OK; 141 } 142 143 void gfxDWriteFontFamily::FindStyleVariationsLocked( 144 FontInfoData* aFontInfoData) { 145 HRESULT hr; 146 if (mHasStyles) { 147 return; 148 } 149 150 mHasStyles = true; 151 152 gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList(); 153 154 bool skipFaceNames = 155 mFaceNamesInitialized || !fp->NeedFullnamePostscriptNames(); 156 bool fontInfoShouldHaveFaceNames = !mFaceNamesInitialized && 157 fp->NeedFullnamePostscriptNames() && 158 aFontInfoData; 159 160 for (UINT32 i = 0; i < mDWFamily->GetFontCount(); i++) { 161 RefPtr<IDWriteFont> font; 162 hr = mDWFamily->GetFont(i, getter_AddRefs(font)); 163 if (FAILED(hr)) { 164 // This should never happen. 165 NS_WARNING("Failed to get existing font from family."); 166 continue; 167 } 168 169 if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) { 170 // We don't want these in the font list; we'll apply simulations 171 // on the fly when appropriate. 172 continue; 173 } 174 175 // name 176 nsCString fullID(mName); 177 nsAutoCString faceName; 178 hr = GetDirectWriteFontName(font, faceName); 179 if (FAILED(hr)) { 180 continue; 181 } 182 fullID.Append(' '); 183 fullID.Append(faceName); 184 185 // Ignore italic style's "Meiryo" because "Meiryo (Bold) Italic" has 186 // non-italic style glyphs as Japanese characters. However, using it 187 // causes serious problem if web pages wants some elements to be 188 // different style from others only with font-style. For example, 189 // <em> and <i> should be rendered as italic in the default style. 190 if (fullID.EqualsLiteral("Meiryo Italic") || 191 fullID.EqualsLiteral("Meiryo Bold Italic")) { 192 continue; 193 } 194 195 gfxDWriteFontEntry* fe = 196 new gfxDWriteFontEntry(fullID, font, mIsSystemFontFamily); 197 fe->SetForceGDIClassic(mForceGDIClassic); 198 199 fe->SetupVariationRanges(); 200 201 AddFontEntryLocked(fe); 202 203 // postscript/fullname if needed 204 nsAutoCString psname, fullname; 205 if (fontInfoShouldHaveFaceNames) { 206 aFontInfoData->GetFaceNames(fe->Name(), fullname, psname); 207 if (!fullname.IsEmpty()) { 208 fp->AddFullname(fe, fullname); 209 } 210 if (!psname.IsEmpty()) { 211 fp->AddPostscriptName(fe, psname); 212 } 213 } else if (!skipFaceNames) { 214 hr = GetDirectWriteFaceName(font, PSNAME_ID, psname); 215 if (FAILED(hr)) { 216 skipFaceNames = true; 217 } else if (psname.Length() > 0) { 218 fp->AddPostscriptName(fe, psname); 219 } 220 221 hr = GetDirectWriteFaceName(font, FULLNAME_ID, fullname); 222 if (FAILED(hr)) { 223 skipFaceNames = true; 224 } else if (fullname.Length() > 0) { 225 fp->AddFullname(fe, fullname); 226 } 227 } 228 229 if (LOG_FONTLIST_ENABLED()) { 230 nsAutoCString weightString; 231 fe->Weight().ToString(weightString); 232 LOG_FONTLIST( 233 ("(fontlist) added (%s) to family (%s)" 234 " with style: %s weight: %s stretch: %d psname: %s fullname: %s", 235 fe->Name().get(), Name().get(), 236 (fe->IsItalic()) ? "italic" 237 : (fe->IsOblique() ? "oblique" : "normal"), 238 weightString.get(), fe->Stretch().AsScalar(), psname.get(), 239 fullname.get())); 240 } 241 } 242 243 // assume that if no error, all postscript/fullnames were initialized 244 if (!skipFaceNames) { 245 mFaceNamesInitialized = true; 246 } 247 248 if (!mAvailableFonts.Length()) { 249 NS_WARNING("Family with no font faces in it."); 250 } 251 252 if (mIsBadUnderlineFamily) { 253 SetBadUnderlineFonts(); 254 } 255 256 CheckForSimpleFamily(); 257 if (mIsSimpleFamily) { 258 for (auto& f : mAvailableFonts) { 259 if (f) { 260 static_cast<gfxDWriteFontEntry*>(f.get())->mMayUseGDIAccess = true; 261 } 262 } 263 } 264 } 265 266 void gfxDWriteFontFamily::ReadFaceNames(gfxPlatformFontList* aPlatformFontList, 267 bool aNeedFullnamePostscriptNames, 268 FontInfoData* aFontInfoData) { 269 // if all needed names have already been read, skip 270 if (mOtherFamilyNamesInitialized && 271 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) { 272 return; 273 } 274 275 // If we've been passed a FontInfoData, we skip the DWrite implementation 276 // here and fall back to the generic code which will use that info. 277 if (!aFontInfoData) { 278 // DirectWrite version of this will try to read 279 // postscript/fullnames via DirectWrite API 280 FindStyleVariations(); 281 } 282 283 // fallback to looking up via name table 284 if (!mOtherFamilyNamesInitialized || !mFaceNamesInitialized) { 285 gfxFontFamily::ReadFaceNames(aPlatformFontList, 286 aNeedFullnamePostscriptNames, aFontInfoData); 287 } 288 } 289 290 void gfxDWriteFontFamily::LocalizedName(nsACString& aLocalizedName) { 291 aLocalizedName = Name(); // just return canonical name in case of failure 292 293 if (!mDWFamily) { 294 return; 295 } 296 297 HRESULT hr; 298 nsAutoCString locale; 299 // We use system locale here because it's what user expects to see. 300 // See bug 1349454 for details. 301 RefPtr<OSPreferences> osprefs = OSPreferences::GetInstanceAddRefed(); 302 if (!osprefs) { 303 return; 304 } 305 osprefs->GetSystemLocale(locale); 306 307 RefPtr<IDWriteLocalizedStrings> names; 308 309 hr = mDWFamily->GetFamilyNames(getter_AddRefs(names)); 310 if (FAILED(hr)) { 311 return; 312 } 313 UINT32 idx = 0; 314 BOOL exists; 315 hr = 316 names->FindLocaleName(NS_ConvertUTF8toUTF16(locale).get(), &idx, &exists); 317 if (FAILED(hr)) { 318 return; 319 } 320 if (!exists) { 321 // Use english is localized is not found. 322 hr = names->FindLocaleName(L"en-us", &idx, &exists); 323 if (FAILED(hr)) { 324 return; 325 } 326 if (!exists) { 327 // Use 0 index if english is not found. 328 idx = 0; 329 } 330 } 331 AutoTArray<WCHAR, 32> famName; 332 UINT32 length; 333 334 hr = names->GetStringLength(idx, &length); 335 if (FAILED(hr)) { 336 return; 337 } 338 339 if (!famName.SetLength(length + 1, fallible)) { 340 // Eeep - running out of memory. Unlikely to end well. 341 return; 342 } 343 344 hr = names->GetString(idx, famName.Elements(), length + 1); 345 if (FAILED(hr)) { 346 return; 347 } 348 349 aLocalizedName = NS_ConvertUTF16toUTF8((const char16_t*)famName.Elements(), 350 famName.Length() - 1); 351 } 352 353 bool gfxDWriteFontFamily::IsSymbolFontFamily() const { 354 // Just check the first font in the family 355 if (mDWFamily->GetFontCount() > 0) { 356 RefPtr<IDWriteFont> font; 357 if (SUCCEEDED(mDWFamily->GetFont(0, getter_AddRefs(font)))) { 358 return font->IsSymbolFont(); 359 } 360 } 361 return false; 362 } 363 364 void gfxDWriteFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 365 FontListSizes* aSizes) const { 366 gfxFontFamily::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 367 // TODO: 368 // This doesn't currently account for |mDWFamily| 369 } 370 371 void gfxDWriteFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 372 FontListSizes* aSizes) const { 373 aSizes->mFontListSize += aMallocSizeOf(this); 374 AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 375 } 376 377 //////////////////////////////////////////////////////////////////////////////// 378 // gfxDWriteFontEntry 379 380 gfxFontEntry* gfxDWriteFontEntry::Clone() const { 381 MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!"); 382 gfxDWriteFontEntry* fe = new gfxDWriteFontEntry(Name(), mFont); 383 fe->mWeightRange = mWeightRange; 384 fe->mStretchRange = mStretchRange; 385 fe->mStyleRange = mStyleRange; 386 return fe; 387 } 388 389 gfxDWriteFontEntry::~gfxDWriteFontEntry() {} 390 391 static bool UsingArabicOrHebrewScriptSystemLocale() { 392 LANGID langid = PRIMARYLANGID(::GetSystemDefaultLangID()); 393 switch (langid) { 394 case LANG_ARABIC: 395 case LANG_DARI: 396 case LANG_PASHTO: 397 case LANG_PERSIAN: 398 case LANG_SINDHI: 399 case LANG_UIGHUR: 400 case LANG_URDU: 401 case LANG_HEBREW: 402 return true; 403 default: 404 return false; 405 } 406 } 407 408 nsresult gfxDWriteFontEntry::CopyFontTable(uint32_t aTableTag, 409 nsTArray<uint8_t>& aBuffer) { 410 gfxDWriteFontList* pFontList = gfxDWriteFontList::PlatformFontList(); 411 const uint32_t tagBE = NativeEndian::swapToBigEndian(aTableTag); 412 413 // Don't use GDI table loading for symbol fonts or for 414 // italic fonts in Arabic-script system locales because of 415 // potential cmap discrepancies, see bug 629386. 416 // Ditto for Hebrew, bug 837498. 417 if (mFont && mMayUseGDIAccess && pFontList->UseGDIFontTableAccess() && 418 !(!IsUpright() && UsingArabicOrHebrewScriptSystemLocale()) && 419 !mFont->IsSymbolFont()) { 420 LOGFONTW logfont = {0}; 421 if (InitLogFont(mFont, &logfont)) { 422 AutoDC dc; 423 AutoSelectFont font(dc.GetDC(), &logfont); 424 if (font.IsValid()) { 425 uint32_t tableSize = ::GetFontData(dc.GetDC(), tagBE, 0, nullptr, 0); 426 if (tableSize != GDI_ERROR) { 427 if (aBuffer.SetLength(tableSize, fallible)) { 428 ::GetFontData(dc.GetDC(), tagBE, 0, aBuffer.Elements(), 429 aBuffer.Length()); 430 return NS_OK; 431 } 432 return NS_ERROR_OUT_OF_MEMORY; 433 } 434 } 435 } 436 } 437 438 RefPtr<IDWriteFontFace> fontFace; 439 nsresult rv = CreateFontFace(getter_AddRefs(fontFace)); 440 if (NS_FAILED(rv)) { 441 return rv; 442 } 443 444 uint8_t* tableData; 445 uint32_t len; 446 void* tableContext = nullptr; 447 BOOL exists; 448 HRESULT hr = fontFace->TryGetFontTable(tagBE, (const void**)&tableData, &len, 449 &tableContext, &exists); 450 if (FAILED(hr) || !exists) { 451 return NS_ERROR_FAILURE; 452 } 453 454 if (aBuffer.SetLength(len, fallible)) { 455 memcpy(aBuffer.Elements(), tableData, len); 456 rv = NS_OK; 457 } else { 458 rv = NS_ERROR_OUT_OF_MEMORY; 459 } 460 461 if (tableContext) { 462 fontFace->ReleaseFontTable(&tableContext); 463 } 464 465 return rv; 466 } 467 468 // Access to font tables packaged in hb_blob_t form 469 470 // object attached to the Harfbuzz blob, used to release 471 // the table when the blob is destroyed 472 class FontTableRec { 473 public: 474 FontTableRec(IDWriteFontFace* aFontFace, void* aContext) 475 : mFontFace(aFontFace), mContext(aContext) { 476 MOZ_COUNT_CTOR(FontTableRec); 477 } 478 479 ~FontTableRec() { 480 MOZ_COUNT_DTOR(FontTableRec); 481 mFontFace->ReleaseFontTable(mContext); 482 } 483 484 private: 485 RefPtr<IDWriteFontFace> mFontFace; 486 void* mContext; 487 }; 488 489 static void DestroyBlobFunc(void* aUserData) { 490 FontTableRec* ftr = static_cast<FontTableRec*>(aUserData); 491 delete ftr; 492 } 493 494 hb_blob_t* gfxDWriteFontEntry::GetFontTable(uint32_t aTag) { 495 // try to avoid potentially expensive DWrite call if we haven't actually 496 // created the font face yet, by using the gfxFontEntry method that will 497 // use CopyFontTable and then cache the data 498 if (!mFontFace) { 499 return gfxFontEntry::GetFontTable(aTag); 500 } 501 502 const void* data; 503 UINT32 size; 504 void* context; 505 BOOL exists; 506 HRESULT hr = mFontFace->TryGetFontTable(NativeEndian::swapToBigEndian(aTag), 507 &data, &size, &context, &exists); 508 if (SUCCEEDED(hr) && exists) { 509 FontTableRec* ftr = new FontTableRec(mFontFace, context); 510 return hb_blob_create(static_cast<const char*>(data), size, 511 HB_MEMORY_MODE_READONLY, ftr, DestroyBlobFunc); 512 } 513 514 return nullptr; 515 } 516 517 nsresult gfxDWriteFontEntry::ReadCMAP(FontInfoData* aFontInfoData) { 518 AUTO_PROFILER_LABEL("gfxDWriteFontEntry::ReadCMAP", GRAPHICS); 519 520 // attempt this once, if errors occur leave a blank cmap 521 if (mCharacterMap || mShmemCharacterMap) { 522 return NS_OK; 523 } 524 525 RefPtr<gfxCharacterMap> charmap; 526 nsresult rv; 527 528 uint32_t uvsOffset = 0; 529 if (aFontInfoData && 530 (charmap = GetCMAPFromFontInfo(aFontInfoData, uvsOffset))) { 531 rv = NS_OK; 532 } else { 533 uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p'); 534 charmap = new gfxCharacterMap(256); 535 AutoTable cmapTable(this, kCMAP); 536 537 if (cmapTable) { 538 uint32_t cmapLen; 539 const uint8_t* cmapData = reinterpret_cast<const uint8_t*>( 540 hb_blob_get_data(cmapTable, &cmapLen)); 541 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, uvsOffset); 542 } else { 543 rv = NS_ERROR_NOT_AVAILABLE; 544 } 545 } 546 mUVSOffset.exchange(uvsOffset); 547 548 bool setCharMap = true; 549 if (NS_SUCCEEDED(rv)) { 550 // Bug 969504: exclude U+25B6 from Segoe UI family, because it's used 551 // by sites to represent a "Play" icon, but the glyph in Segoe UI Light 552 // and Semibold on Windows 7 is too thin. (Ditto for leftward U+25C0.) 553 // Fallback to Segoe UI Symbol is preferred. 554 if (FamilyName().EqualsLiteral("Segoe UI")) { 555 charmap->clear(0x25b6); 556 charmap->clear(0x25c0); 557 } 558 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList(); 559 fontlist::FontList* sharedFontList = pfl->SharedFontList(); 560 if (!IsUserFont() && mShmemFace) { 561 mShmemFace->SetCharacterMap(sharedFontList, charmap, mShmemFamily); 562 if (TrySetShmemCharacterMap()) { 563 setCharMap = false; 564 } 565 } else { 566 charmap = pfl->FindCharMap(charmap); 567 } 568 mHasCmapTable = true; 569 } else { 570 // if error occurred, initialize to null cmap 571 charmap = new gfxCharacterMap(0); 572 mHasCmapTable = false; 573 } 574 if (setCharMap) { 575 // Temporarily retain charmap, until the shared version is 576 // ready for use. 577 if (mCharacterMap.compareExchange(nullptr, charmap.get())) { 578 charmap.get()->AddRef(); 579 } 580 } 581 582 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n", 583 mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of), 584 charmap->mHash, mCharacterMap == charmap ? " new" : "")); 585 if (LOG_CMAPDATA_ENABLED()) { 586 char prefix[256]; 587 SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get()); 588 charmap->Dump(prefix, eGfxLog_cmapdata); 589 } 590 591 return rv; 592 } 593 594 bool gfxDWriteFontEntry::HasVariations() { 595 if (mHasVariationsInitialized) { 596 return mHasVariations; 597 } 598 mHasVariationsInitialized = true; 599 mHasVariations = false; 600 601 if (!gfxPlatform::HasVariationFontSupport()) { 602 return mHasVariations; 603 } 604 605 if (!mFontFace) { 606 // CreateFontFace will initialize the mFontFace field, and also 607 // mFontFace5 if available on the current DWrite version. 608 RefPtr<IDWriteFontFace> fontFace; 609 if (NS_FAILED(CreateFontFace(getter_AddRefs(fontFace)))) { 610 return mHasVariations; 611 } 612 } 613 if (mFontFace5) { 614 mHasVariations = mFontFace5->HasVariations(); 615 } 616 return mHasVariations; 617 } 618 619 void gfxDWriteFontEntry::GetVariationAxes( 620 nsTArray<gfxFontVariationAxis>& aAxes) { 621 if (!HasVariations()) { 622 return; 623 } 624 // HasVariations() will have ensured the mFontFace5 interface is available; 625 // so we can get an IDWriteFontResource and ask it for the axis info. 626 RefPtr<IDWriteFontResource> resource; 627 HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource)); 628 if (FAILED(hr) || !resource) { 629 return; 630 } 631 632 uint32_t count = resource->GetFontAxisCount(); 633 AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> defaultValues; 634 AutoTArray<DWRITE_FONT_AXIS_RANGE, 4> ranges; 635 defaultValues.SetLength(count); 636 ranges.SetLength(count); 637 resource->GetDefaultFontAxisValues(defaultValues.Elements(), count); 638 resource->GetFontAxisRanges(ranges.Elements(), count); 639 for (uint32_t i = 0; i < count; ++i) { 640 gfxFontVariationAxis axis; 641 MOZ_ASSERT(ranges[i].axisTag == defaultValues[i].axisTag); 642 DWRITE_FONT_AXIS_ATTRIBUTES attrs = resource->GetFontAxisAttributes(i); 643 if (attrs & DWRITE_FONT_AXIS_ATTRIBUTES_HIDDEN) { 644 continue; 645 } 646 if (!(attrs & DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE)) { 647 continue; 648 } 649 // Extract the 4 chars of the tag from DWrite's packed version, 650 // and reassemble them in the order we use for TRUETYPE_TAG. 651 uint32_t t = defaultValues[i].axisTag; 652 axis.mTag = TRUETYPE_TAG(t & 0xff, (t >> 8) & 0xff, (t >> 16) & 0xff, 653 (t >> 24) & 0xff); 654 // Try to get a human-friendly name (may not be present) 655 RefPtr<IDWriteLocalizedStrings> names; 656 resource->GetAxisNames(i, getter_AddRefs(names)); 657 if (names) { 658 GetEnglishOrFirstName(axis.mName, names); 659 } 660 axis.mMinValue = ranges[i].minValue; 661 axis.mMaxValue = ranges[i].maxValue; 662 axis.mDefaultValue = defaultValues[i].value; 663 aAxes.AppendElement(axis); 664 } 665 } 666 667 void gfxDWriteFontEntry::GetVariationInstances( 668 nsTArray<gfxFontVariationInstance>& aInstances) { 669 gfxFontUtils::GetVariationData(this, nullptr, &aInstances); 670 } 671 672 gfxFont* gfxDWriteFontEntry::CreateFontInstance( 673 const gfxFontStyle* aFontStyle) { 674 // We use the DirectWrite bold simulation for installed fonts, but NOT for 675 // webfonts; those will use multi-strike synthetic bold instead. 676 bool useBoldSim = false; 677 if (aFontStyle->NeedsSyntheticBold(this)) { 678 switch (StaticPrefs::gfx_font_rendering_directwrite_bold_simulation()) { 679 case 0: // never use the DWrite simulation 680 break; 681 case 1: // use DWrite simulation for installed fonts except COLR fonts, 682 // but not webfonts 683 useBoldSim = 684 !mIsDataUserFont && !HasFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R')); 685 break; 686 default: // always use DWrite bold simulation, except for COLR fonts 687 useBoldSim = !HasFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R')); 688 break; 689 } 690 } 691 DWRITE_FONT_SIMULATIONS sims = 692 useBoldSim ? DWRITE_FONT_SIMULATIONS_BOLD : DWRITE_FONT_SIMULATIONS_NONE; 693 ThreadSafeWeakPtr<UnscaledFontDWrite>& unscaledFontPtr = 694 useBoldSim ? mUnscaledFontBold : mUnscaledFont; 695 RefPtr<UnscaledFontDWrite> unscaledFont(unscaledFontPtr); 696 if (!unscaledFont) { 697 RefPtr<IDWriteFontFace> fontFace; 698 nsresult rv = 699 CreateFontFace(getter_AddRefs(fontFace), nullptr, sims, nullptr); 700 if (NS_FAILED(rv)) { 701 return nullptr; 702 } 703 // Only pass in the underlying IDWriteFont if the unscaled font doesn't 704 // reflect a data font. This signals whether or not we can safely query 705 // a descriptor to represent the font for various transport use-cases. 706 unscaledFont = 707 new UnscaledFontDWrite(fontFace, !mIsDataUserFont ? mFont : nullptr); 708 unscaledFontPtr = unscaledFont; 709 } 710 RefPtr<IDWriteFontFace> fontFace; 711 if (HasVariations()) { 712 // Get the variation settings needed to instantiate the fontEntry 713 // for a particular fontStyle. 714 AutoTArray<gfxFontVariation, 4> vars; 715 GetVariationsForStyle(vars, *aFontStyle); 716 717 if (!vars.IsEmpty()) { 718 nsresult rv = 719 CreateFontFace(getter_AddRefs(fontFace), aFontStyle, sims, &vars); 720 if (NS_FAILED(rv)) { 721 return nullptr; 722 } 723 } 724 } 725 return new gfxDWriteFont(unscaledFont, this, aFontStyle, fontFace); 726 } 727 728 nsresult gfxDWriteFontEntry::CreateFontFace( 729 IDWriteFontFace** aFontFace, const gfxFontStyle* aFontStyle, 730 DWRITE_FONT_SIMULATIONS aSimulations, 731 const nsTArray<gfxFontVariation>* aVariations) { 732 // Convert an OpenType font tag from our uint32_t representation 733 // (as constructed by TRUETYPE_TAG(...)) to the order DWrite wants. 734 auto makeDWriteAxisTag = [](uint32_t aTag) { 735 return DWRITE_MAKE_FONT_AXIS_TAG((aTag >> 24) & 0xff, (aTag >> 16) & 0xff, 736 (aTag >> 8) & 0xff, aTag & 0xff); 737 }; 738 739 MOZ_SEH_TRY { 740 // initialize mFontFace if this hasn't been done before 741 if (!mFontFace) { 742 HRESULT hr; 743 if (mFont) { 744 hr = mFont->CreateFontFace(getter_AddRefs(mFontFace)); 745 } else if (mFontFile) { 746 IDWriteFontFile* fontFile = mFontFile.get(); 747 hr = Factory::GetDWriteFactory()->CreateFontFace( 748 mFaceType, 1, &fontFile, 0, DWRITE_FONT_SIMULATIONS_NONE, 749 getter_AddRefs(mFontFace)); 750 } else { 751 MOZ_ASSERT_UNREACHABLE("invalid font entry"); 752 return NS_ERROR_FAILURE; 753 } 754 if (FAILED(hr)) { 755 return NS_ERROR_FAILURE; 756 } 757 // Also get the IDWriteFontFace5 interface if we're running on a 758 // sufficiently new DWrite version where it is available. 759 if (mFontFace) { 760 mFontFace->QueryInterface(__uuidof(IDWriteFontFace5), 761 (void**)getter_AddRefs(mFontFace5)); 762 if (!mVariationSettings.IsEmpty()) { 763 // If the font entry has variations specified, mFontFace5 will 764 // be a distinct face that has the variations applied. 765 RefPtr<IDWriteFontResource> resource; 766 HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource)); 767 if (SUCCEEDED(hr) && resource) { 768 AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues; 769 for (const auto& v : mVariationSettings) { 770 DWRITE_FONT_AXIS_VALUE axisValue = {makeDWriteAxisTag(v.mTag), 771 v.mValue}; 772 fontAxisValues.AppendElement(axisValue); 773 } 774 resource->CreateFontFace( 775 mFontFace->GetSimulations(), fontAxisValues.Elements(), 776 fontAxisValues.Length(), getter_AddRefs(mFontFace5)); 777 } 778 } 779 } 780 } 781 782 // Do we need to modify DWrite simulations from what mFontFace has? 783 bool needSimulations = 784 (aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) && 785 !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD); 786 787 // If the IDWriteFontFace5 interface is available, we can try using 788 // IDWriteFontResource to create a new modified face. 789 if (mFontFace5 && (HasVariations() || needSimulations)) { 790 RefPtr<IDWriteFontResource> resource; 791 HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource)); 792 if (SUCCEEDED(hr) && resource) { 793 AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues; 794 795 // Copy variation settings to DWrite's type. 796 if (aVariations) { 797 for (const auto& v : *aVariations) { 798 DWRITE_FONT_AXIS_VALUE axisValue = {makeDWriteAxisTag(v.mTag), 799 v.mValue}; 800 fontAxisValues.AppendElement(axisValue); 801 } 802 } 803 804 IDWriteFontFace5* ff5; 805 resource->CreateFontFace(aSimulations, fontAxisValues.Elements(), 806 fontAxisValues.Length(), &ff5); 807 if (ff5) { 808 *aFontFace = ff5; 809 return NS_OK; 810 } 811 } 812 } 813 814 // Do we need to add DWrite simulations to the face? 815 if (needSimulations) { 816 // if so, we need to return not mFontFace itself but a version that 817 // has the Bold simulation - unfortunately, old DWrite doesn't provide 818 // a simple API for this 819 UINT32 numberOfFiles = 0; 820 if (FAILED(mFontFace->GetFiles(&numberOfFiles, nullptr))) { 821 return NS_ERROR_FAILURE; 822 } 823 AutoTArray<IDWriteFontFile*, 1> files; 824 files.AppendElements(numberOfFiles); 825 if (FAILED(mFontFace->GetFiles(&numberOfFiles, files.Elements()))) { 826 return NS_ERROR_FAILURE; 827 } 828 HRESULT hr = Factory::GetDWriteFactory()->CreateFontFace( 829 mFontFace->GetType(), numberOfFiles, files.Elements(), 830 mFontFace->GetIndex(), aSimulations, aFontFace); 831 for (UINT32 i = 0; i < numberOfFiles; ++i) { 832 files[i]->Release(); 833 } 834 return FAILED(hr) ? NS_ERROR_FAILURE : NS_OK; 835 } 836 } 837 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 838 gfxCriticalNote << "Exception occurred creating font face for " 839 << mName.get(); 840 } 841 842 // no simulation: we can just add a reference to mFontFace5 (if present) 843 // or mFontFace (otherwise) and return that 844 if (mFontFace5) { 845 *aFontFace = mFontFace5; 846 } else { 847 *aFontFace = mFontFace; 848 } 849 (*aFontFace)->AddRef(); 850 return NS_OK; 851 } 852 853 bool gfxDWriteFontEntry::InitLogFont(IDWriteFont* aFont, LOGFONTW* aLogFont) { 854 HRESULT hr; 855 856 BOOL isInSystemCollection; 857 IDWriteGdiInterop* gdi = 858 gfxDWriteFontList::PlatformFontList()->GetGDIInterop(); 859 hr = gdi->ConvertFontToLOGFONT(aFont, aLogFont, &isInSystemCollection); 860 // If the font is not in the system collection, GDI will be unable to 861 // select it and load its tables, so we return false here to indicate 862 // failure, and let CopyFontTable fall back to DWrite native methods. 863 return (SUCCEEDED(hr) && isInSystemCollection); 864 } 865 866 bool gfxDWriteFontEntry::IsCJKFont() { 867 if (mIsCJK != UNINITIALIZED_VALUE) { 868 return mIsCJK; 869 } 870 871 mIsCJK = false; 872 873 const uint32_t kOS2Tag = TRUETYPE_TAG('O', 'S', '/', '2'); 874 gfxFontUtils::AutoHBBlob blob(GetFontTable(kOS2Tag)); 875 if (!blob) { 876 return mIsCJK; 877 } 878 879 uint32_t len; 880 const OS2Table* os2 = 881 reinterpret_cast<const OS2Table*>(hb_blob_get_data(blob, &len)); 882 // ulCodePageRange bit definitions for the CJK codepages, 883 // from http://www.microsoft.com/typography/otspec/os2.htm#cpr 884 const uint32_t CJK_CODEPAGE_BITS = 885 (1 << 17) | // codepage 932 - JIS/Japan 886 (1 << 18) | // codepage 936 - Chinese (simplified) 887 (1 << 19) | // codepage 949 - Korean Wansung 888 (1 << 20) | // codepage 950 - Chinese (traditional) 889 (1 << 21); // codepage 1361 - Korean Johab 890 if (len >= offsetof(OS2Table, sxHeight)) { 891 if ((uint32_t(os2->codePageRange1) & CJK_CODEPAGE_BITS) != 0) { 892 mIsCJK = true; 893 } 894 } 895 896 return mIsCJK; 897 } 898 899 void gfxDWriteFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 900 FontListSizes* aSizes) const { 901 gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 902 // TODO: 903 // This doesn't currently account for the |mFont| and |mFontFile| members 904 } 905 906 void gfxDWriteFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 907 FontListSizes* aSizes) const { 908 aSizes->mFontListSize += aMallocSizeOf(this); 909 AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 910 } 911 912 size_t gfxDWriteFontEntry::ComputedSizeOfExcludingThis( 913 mozilla::MallocSizeOf aMallocSizeOf) { 914 size_t result = gfxFontEntry::ComputedSizeOfExcludingThis(aMallocSizeOf); 915 if (mFontFileStream) { 916 result += mFontFileStream->SizeOfExcludingThis(aMallocSizeOf); 917 } 918 return result; 919 } 920 921 //////////////////////////////////////////////////////////////////////////////// 922 // gfxDWriteFontList 923 924 gfxDWriteFontList::gfxDWriteFontList() : mForceGDIClassicMaxFontSize(0.0) { 925 CheckFamilyList(kBaseFonts); 926 #ifndef BASE_BROWSER_VERSION 927 CheckFamilyList(kLangPackFonts); 928 #endif 929 } 930 931 // bug 602792 - CJK systems default to large CJK fonts which cause excessive 932 // I/O strain during cold startup due to dwrite caching bugs. Default to 933 // Arial to avoid this. 934 935 FontFamily gfxDWriteFontList::GetDefaultFontForPlatform( 936 FontVisibilityProvider* aFontVisibilityProvider, const gfxFontStyle* aStyle, 937 nsAtom* aLanguage) { 938 // try Arial first 939 FontFamily ff; 940 ff = FindFamily(aFontVisibilityProvider, "Arial"_ns); 941 if (!ff.IsNull()) { 942 return ff; 943 } 944 945 // otherwise, use local default 946 gfxFontStyle fontStyle; 947 nsAutoString systemFontName; 948 if (!mozilla::LookAndFeel::GetFont(mozilla::StyleSystemFont::MessageBox, 949 systemFontName, fontStyle)) { 950 return ff; 951 } 952 953 ff = FindFamily(aFontVisibilityProvider, 954 NS_ConvertUTF16toUTF8(systemFontName)); 955 return ff; 956 } 957 958 gfxFontEntry* gfxDWriteFontList::LookupLocalFont( 959 FontVisibilityProvider* aFontVisibilityProvider, 960 const nsACString& aFontName, WeightRange aWeightForEntry, 961 StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry) { 962 AutoLock lock(mLock); 963 964 if (SharedFontList()) { 965 return LookupInSharedFaceNameList(aFontVisibilityProvider, aFontName, 966 aWeightForEntry, aStretchForEntry, 967 aStyleForEntry); 968 } 969 970 gfxFontEntry* lookup; 971 972 lookup = LookupInFaceNameLists(aFontName); 973 if (!lookup) { 974 return nullptr; 975 } 976 977 gfxDWriteFontEntry* dwriteLookup = static_cast<gfxDWriteFontEntry*>(lookup); 978 gfxDWriteFontEntry* fe = 979 new gfxDWriteFontEntry(lookup->Name(), dwriteLookup->mFont, 980 aWeightForEntry, aStretchForEntry, aStyleForEntry); 981 fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic()); 982 return fe; 983 } 984 985 gfxFontEntry* gfxDWriteFontList::MakePlatformFont( 986 const nsACString& aFontName, WeightRange aWeightForEntry, 987 StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry, 988 const uint8_t* aFontData, uint32_t aLength) { 989 RefPtr<gfxDWriteFontFileStream> fontFileStream; 990 RefPtr<IDWriteFontFile> fontFile; 991 HRESULT hr = gfxDWriteFontFileLoader::CreateCustomFontFile( 992 aFontData, aLength, getter_AddRefs(fontFile), 993 getter_AddRefs(fontFileStream)); 994 free((void*)aFontData); 995 NS_ASSERTION(SUCCEEDED(hr), "Failed to create font file reference"); 996 if (FAILED(hr)) { 997 return nullptr; 998 } 999 1000 nsAutoString uniqueName; 1001 nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName); 1002 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to make unique user font name"); 1003 if (NS_FAILED(rv)) { 1004 return nullptr; 1005 } 1006 1007 BOOL isSupported; 1008 DWRITE_FONT_FILE_TYPE fileType; 1009 UINT32 numFaces; 1010 1011 auto entry = MakeUnique<gfxDWriteFontEntry>( 1012 NS_ConvertUTF16toUTF8(uniqueName), fontFile, fontFileStream, 1013 aWeightForEntry, aStretchForEntry, aStyleForEntry); 1014 1015 hr = fontFile->Analyze(&isSupported, &fileType, &entry->mFaceType, &numFaces); 1016 NS_ASSERTION(SUCCEEDED(hr), "IDWriteFontFile::Analyze failed"); 1017 if (FAILED(hr)) { 1018 return nullptr; 1019 } 1020 NS_ASSERTION(isSupported, "Unsupported font file"); 1021 if (!isSupported) { 1022 return nullptr; 1023 } 1024 NS_ASSERTION(numFaces == 1, "Font file does not contain exactly 1 face"); 1025 if (numFaces != 1) { 1026 // We don't know how to deal with 0 faces either. 1027 return nullptr; 1028 } 1029 1030 return entry.release(); 1031 } 1032 1033 bool gfxDWriteFontList::UseGDIFontTableAccess() const { 1034 // Using GDI font table access for DWrite is controlled by a pref, but also we 1035 // must be able to make win32k calls. 1036 return mGDIFontTableAccess && !IsWin32kLockedDown(); 1037 } 1038 1039 static void GetPostScriptNameFromNameTable(IDWriteFontFace* aFace, 1040 nsCString& aName) { 1041 const auto kNAME = 1042 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')); 1043 const char* data; 1044 UINT32 size; 1045 void* context; 1046 BOOL exists; 1047 if (SUCCEEDED(aFace->TryGetFontTable(kNAME, (const void**)&data, &size, 1048 &context, &exists)) && 1049 exists) { 1050 if (NS_FAILED(gfxFontUtils::ReadCanonicalName( 1051 data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, aName))) { 1052 aName.Truncate(0); 1053 } 1054 aFace->ReleaseFontTable(context); 1055 } 1056 } 1057 1058 gfxFontEntry* gfxDWriteFontList::CreateFontEntry( 1059 fontlist::Face* aFace, const fontlist::Family* aFamily) { 1060 IDWriteFontCollection* collection = 1061 #ifdef MOZ_BUNDLED_FONTS 1062 aFamily->IsBundled() ? mBundledFonts : mSystemFonts; 1063 #else 1064 mSystemFonts; 1065 #endif 1066 RefPtr<IDWriteFontFamily> family; 1067 bool foundExpectedFamily = false; 1068 const nsCString& familyName = 1069 aFamily->DisplayName().AsString(SharedFontList()); 1070 1071 // The DirectWrite calls here might throw exceptions, e.g. in case of disk 1072 // errors when trying to read the font file. 1073 MOZ_SEH_TRY { 1074 if (aFamily->Index() < collection->GetFontFamilyCount()) { 1075 HRESULT hr = 1076 collection->GetFontFamily(aFamily->Index(), getter_AddRefs(family)); 1077 // Check that the family name is what we expected; if not, fall back to 1078 // search by name. It's sad we have to do this, but it is possible for 1079 // Windows to have given different versions of the system font collection 1080 // to the parent and child processes. 1081 if (SUCCEEDED(hr) && family) { 1082 RefPtr<IDWriteLocalizedStrings> names; 1083 hr = family->GetFamilyNames(getter_AddRefs(names)); 1084 if (SUCCEEDED(hr) && names) { 1085 nsAutoCString name; 1086 if (GetEnglishOrFirstName(name, names)) { 1087 foundExpectedFamily = name.Equals(familyName); 1088 } 1089 } 1090 } 1091 } 1092 if (!foundExpectedFamily) { 1093 // Try to get family by name instead of index (to deal with the case of 1094 // collection mismatch). 1095 UINT32 index; 1096 BOOL exists; 1097 NS_ConvertUTF8toUTF16 name16(familyName); 1098 HRESULT hr = collection->FindFamilyName( 1099 reinterpret_cast<const WCHAR*>(name16.BeginReading()), &index, 1100 &exists); 1101 if (FAILED(hr) || !exists || index == UINT_MAX || 1102 FAILED(collection->GetFontFamily(index, getter_AddRefs(family))) || 1103 !family) { 1104 return nullptr; 1105 } 1106 } 1107 1108 // Retrieve the required face by index within the family. 1109 RefPtr<IDWriteFont> font; 1110 if (FAILED(family->GetFont(aFace->mIndex, getter_AddRefs(font))) || !font) { 1111 return nullptr; 1112 } 1113 1114 // Retrieve the psName from the font, so we can check we've found the 1115 // expected face. 1116 nsAutoCString psName; 1117 if (FAILED(GetDirectWriteFaceName(font, PSNAME_ID, psName))) { 1118 RefPtr<IDWriteFontFace> dwFontFace; 1119 if (SUCCEEDED(font->CreateFontFace(getter_AddRefs(dwFontFace)))) { 1120 GetPostScriptNameFromNameTable(dwFontFace, psName); 1121 } 1122 } 1123 1124 // If it doesn't match, DirectWrite must have shuffled the order of faces 1125 // returned for the family; search by name as a fallback. 1126 nsCString faceName = aFace->mDescriptor.AsString(SharedFontList()); 1127 if (psName != faceName) { 1128 gfxWarning() << "Face name mismatch for index " << aFace->mIndex 1129 << " in family " << familyName.get() << ": expected " 1130 << faceName.get() << ", found " << psName.get(); 1131 for (uint32_t i = 0; i < family->GetFontCount(); ++i) { 1132 if (i == aFace->mIndex) { 1133 continue; // this was the face we already tried 1134 } 1135 if (FAILED(family->GetFont(i, getter_AddRefs(font))) || !font) { 1136 return nullptr; // this font family is broken! 1137 } 1138 if (FAILED(GetDirectWriteFaceName(font, PSNAME_ID, psName))) { 1139 RefPtr<IDWriteFontFace> dwFontFace; 1140 if (SUCCEEDED(font->CreateFontFace(getter_AddRefs(dwFontFace)))) { 1141 GetPostScriptNameFromNameTable(dwFontFace, psName); 1142 } 1143 } 1144 if (psName == faceName) { 1145 break; 1146 } 1147 } 1148 } 1149 if (psName != faceName) { 1150 return nullptr; 1151 } 1152 1153 auto fe = new gfxDWriteFontEntry(faceName, font, !aFamily->IsBundled()); 1154 fe->InitializeFrom(aFace, aFamily); 1155 fe->mForceGDIClassic = aFamily->IsForceClassic(); 1156 fe->mMayUseGDIAccess = aFamily->IsSimple(); 1157 return fe; 1158 } 1159 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 1160 gfxCriticalNote << "Exception occurred while creating font entry for " 1161 << familyName.get(); 1162 } 1163 return nullptr; 1164 } 1165 1166 FontVisibility gfxDWriteFontList::GetVisibilityForFamily( 1167 const nsACString& aName) const { 1168 if (FamilyInList(aName, kBaseFonts)) { 1169 return FontVisibility::Base; 1170 } 1171 #ifndef BASE_BROWSER_VERSION 1172 if (FamilyInList(aName, kLangPackFonts)) { 1173 return FontVisibility::LangPack; 1174 } 1175 if (nsRFPService::FontIsAllowedByLocale(aName)) { 1176 return FontVisibility::LangPack; 1177 } 1178 #endif 1179 return FontVisibility::User; 1180 } 1181 1182 nsTArray<std::pair<const char**, uint32_t>> 1183 gfxDWriteFontList::GetFilteredPlatformFontLists() { 1184 nsTArray<std::pair<const char**, uint32_t>> fontLists; 1185 1186 fontLists.AppendElement(std::make_pair(kBaseFonts, std::size(kBaseFonts))); 1187 #ifndef BASE_BROWSER_VERSION 1188 fontLists.AppendElement( 1189 std::make_pair(kLangPackFonts, std::size(kLangPackFonts))); 1190 #endif 1191 1192 return fontLists; 1193 } 1194 1195 void gfxDWriteFontList::AppendFamiliesFromCollection( 1196 IDWriteFontCollection* aCollection, 1197 nsTArray<fontlist::Family::InitData>& aFamilies, 1198 const nsTArray<nsCString>* aForceClassicFams) { 1199 auto allFacesUltraBold = [](IDWriteFontFamily* aFamily) -> bool { 1200 for (UINT32 i = 0; i < aFamily->GetFontCount(); i++) { 1201 RefPtr<IDWriteFont> font; 1202 HRESULT hr = aFamily->GetFont(i, getter_AddRefs(font)); 1203 if (FAILED(hr)) { 1204 NS_WARNING("Failed to get existing font from family."); 1205 continue; 1206 } 1207 nsAutoCString faceName; 1208 hr = GetDirectWriteFontName(font, faceName); 1209 if (FAILED(hr)) { 1210 continue; 1211 } 1212 if (faceName.Find("Ultra Bold"_ns) == kNotFound) { 1213 return false; 1214 } 1215 } 1216 return true; 1217 }; 1218 1219 nsAutoCString locale; 1220 OSPreferences::GetInstance()->GetSystemLocale(locale); 1221 ToLowerCase(locale); 1222 NS_ConvertUTF8toUTF16 loc16(locale); 1223 1224 for (unsigned i = 0; i < aCollection->GetFontFamilyCount(); ++i) { 1225 RefPtr<IDWriteFontFamily> family; 1226 aCollection->GetFontFamily(i, getter_AddRefs(family)); 1227 RefPtr<IDWriteLocalizedStrings> localizedNames; 1228 HRESULT hr = family->GetFamilyNames(getter_AddRefs(localizedNames)); 1229 if (FAILED(hr)) { 1230 gfxWarning() << "Failed to get names for font-family " << i; 1231 continue; 1232 } 1233 1234 auto addFamily = [&](const nsACString& name, FontVisibility visibility, 1235 bool altLocale = false) { 1236 nsAutoCString key; 1237 key = name; 1238 BuildKeyNameFromFontName(key); 1239 bool bad = mBadUnderlineFamilyNames.ContainsSorted(key); 1240 bool classic = 1241 aForceClassicFams && aForceClassicFams->ContainsSorted(key); 1242 aFamilies.AppendElement(fontlist::Family::InitData( 1243 key, name, i, visibility, aCollection != mSystemFonts, bad, classic, 1244 altLocale)); 1245 }; 1246 1247 auto visibilityForName = [&](const nsACString& aName) -> FontVisibility { 1248 // Special case: hide the "Gill Sans" family that contains only UltraBold 1249 // faces, as this leads to breakage on sites with CSS that targeted the 1250 // Gill Sans family as found on macOS. (Bug 551313, bug 1632738) 1251 // TODO (jfkthame): the ultrabold faces from Gill Sans should be treated 1252 // as belonging to the Gill Sans MT family. 1253 if (aName.EqualsLiteral("Gill Sans") && allFacesUltraBold(family)) { 1254 return FontVisibility::Hidden; 1255 } 1256 // Bundled fonts are always available, so only system fonts are checked 1257 // against the standard font names list. 1258 return aCollection == mSystemFonts ? GetVisibilityForFamily(aName) 1259 : FontVisibility::Base; 1260 }; 1261 1262 unsigned count = localizedNames->GetCount(); 1263 if (count == 1) { 1264 // This is the common case: the great majority of fonts only provide an 1265 // en-US family name. 1266 nsAutoCString name; 1267 if (!GetNameAsUtf8(name, localizedNames, 0)) { 1268 gfxWarning() << "GetNameAsUtf8 failed for index 0 in font-family " << i; 1269 continue; 1270 } 1271 addFamily(name, visibilityForName(name)); 1272 } else { 1273 AutoTArray<nsCString, 4> names; 1274 int sysLocIndex = -1; 1275 FontVisibility visibility = FontVisibility::User; 1276 for (unsigned index = 0; index < count; ++index) { 1277 nsAutoCString name; 1278 if (!GetNameAsUtf8(name, localizedNames, index)) { 1279 gfxWarning() << "GetNameAsUtf8 failed for index " << index 1280 << " in font-family " << i; 1281 continue; 1282 } 1283 if (names.Contains(name)) { 1284 continue; 1285 } 1286 if (sysLocIndex == -1) { 1287 WCHAR buf[32]; 1288 if (SUCCEEDED(localizedNames->GetLocaleName(index, buf, 32))) { 1289 if (loc16.Equals(buf)) { 1290 sysLocIndex = names.Length(); 1291 } 1292 } 1293 } 1294 names.AppendElement(name); 1295 // We give the family the least-restrictive visibility of all its 1296 // localized names, so that the used visibility will not depend on 1297 // locale; with the exception that if any name is explicitly Hidden, 1298 // this hides the family as a whole. 1299 if (visibility != FontVisibility::Hidden) { 1300 FontVisibility v = visibilityForName(name); 1301 if (v == FontVisibility::Hidden) { 1302 visibility = FontVisibility::Hidden; 1303 } else { 1304 visibility = std::min(visibility, v); 1305 } 1306 } 1307 } 1308 // If we didn't find a name that matched the system locale, use the 1309 // first (which is most often en-US). 1310 if (sysLocIndex == -1) { 1311 sysLocIndex = 0; 1312 } 1313 // Hack to work around EPSON fonts with bad names (tagged as MacRoman 1314 // but actually contain MacJapanese data): if we've chosen the first 1315 // name, *and* it is non-ASCII, *and* there is an alternative present, 1316 // use the next option instead as being more likely to be valid. 1317 if (sysLocIndex == 0 && names.Length() > 1 && !IsAscii(names[0])) { 1318 sysLocIndex = 1; 1319 } 1320 for (unsigned index = 0; index < names.Length(); ++index) { 1321 addFamily(names[index], visibility, 1322 index != static_cast<unsigned>(sysLocIndex)); 1323 } 1324 } 1325 } 1326 } 1327 1328 void gfxDWriteFontList::GetFacesInitDataForFamily( 1329 const fontlist::Family* aFamily, nsTArray<fontlist::Face::InitData>& aFaces, 1330 bool aLoadCmaps) const { 1331 IDWriteFontCollection* collection = 1332 #ifdef MOZ_BUNDLED_FONTS 1333 aFamily->IsBundled() ? mBundledFonts : mSystemFonts; 1334 #else 1335 mSystemFonts; 1336 #endif 1337 if (!collection) { 1338 return; 1339 } 1340 RefPtr<IDWriteFontFamily> family; 1341 collection->GetFontFamily(aFamily->Index(), getter_AddRefs(family)); 1342 for (unsigned i = 0; i < family->GetFontCount(); ++i) { 1343 RefPtr<IDWriteFont> dwFont; 1344 family->GetFont(i, getter_AddRefs(dwFont)); 1345 if (!dwFont || dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) { 1346 continue; 1347 } 1348 DWRITE_FONT_STYLE dwstyle = dwFont->GetStyle(); 1349 // Ignore italic styles of Meiryo because "Meiryo (Bold) Italic" has 1350 // non-italic style glyphs as Japanese characters. However, using it 1351 // causes serious problem if web pages wants some elements to be 1352 // different style from others only with font-style. For example, 1353 // <em> and <i> should be rendered as italic in the default style. 1354 if (dwstyle != DWRITE_FONT_STYLE_NORMAL && 1355 aFamily->Key().AsString(SharedFontList()).EqualsLiteral("meiryo")) { 1356 continue; 1357 } 1358 WeightRange weight(FontWeight::FromInt(dwFont->GetWeight())); 1359 StretchRange stretch(FontStretchFromDWriteStretch(dwFont->GetStretch())); 1360 // Try to read PSName as a unique face identifier; if this fails we'll get 1361 // it directly from the 'name' table, and if that also fails we consider 1362 // the face unusable. 1363 MOZ_SEH_TRY { 1364 nsAutoCString name; 1365 RefPtr<gfxCharacterMap> charmap; 1366 if (FAILED(GetDirectWriteFaceName(dwFont, PSNAME_ID, name)) || 1367 aLoadCmaps) { 1368 RefPtr<IDWriteFontFace> dwFontFace; 1369 if (SUCCEEDED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) { 1370 if (name.IsEmpty()) { 1371 GetPostScriptNameFromNameTable(dwFontFace, name); 1372 } 1373 const auto kCMAP = 1374 NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p')); 1375 const char* data; 1376 UINT32 size; 1377 void* context; 1378 BOOL exists; 1379 if (aLoadCmaps) { 1380 if (SUCCEEDED(dwFontFace->TryGetFontTable( 1381 kCMAP, (const void**)&data, &size, &context, &exists)) && 1382 exists) { 1383 charmap = new gfxCharacterMap(256); 1384 uint32_t offset; 1385 gfxFontUtils::ReadCMAP((const uint8_t*)data, size, *charmap, 1386 offset); 1387 dwFontFace->ReleaseFontTable(context); 1388 } 1389 } 1390 } 1391 } 1392 if (name.IsEmpty()) { 1393 gfxWarning() << "Failed to get name for face " << i << " in family " 1394 << aFamily->Key().AsString(SharedFontList()).get(); 1395 continue; 1396 } 1397 SlantStyleRange slant( 1398 dwstyle == DWRITE_FONT_STYLE_NORMAL ? FontSlantStyle::NORMAL 1399 : dwstyle == DWRITE_FONT_STYLE_ITALIC ? FontSlantStyle::ITALIC 1400 : FontSlantStyle::OBLIQUE); 1401 aFaces.AppendElement(fontlist::Face::InitData{ 1402 name, uint16_t(i), false, weight, stretch, slant, charmap}); 1403 } 1404 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 1405 // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use 1406 // the font resource. We'll just skip the bad face. 1407 gfxCriticalNote << "Exception occurred reading faces for " 1408 << aFamily->Key().AsString(SharedFontList()).get(); 1409 } 1410 } 1411 } 1412 1413 bool gfxDWriteFontList::ReadFaceNames(const fontlist::Family* aFamily, 1414 const fontlist::Face* aFace, 1415 nsCString& aPSName, 1416 nsCString& aFullName) { 1417 IDWriteFontCollection* collection = 1418 #ifdef MOZ_BUNDLED_FONTS 1419 aFamily->IsBundled() ? mBundledFonts : mSystemFonts; 1420 #else 1421 mSystemFonts; 1422 #endif 1423 RefPtr<IDWriteFontFamily> family; 1424 if (FAILED(collection->GetFontFamily(aFamily->Index(), 1425 getter_AddRefs(family)))) { 1426 MOZ_ASSERT_UNREACHABLE("failed to get font-family"); 1427 return false; 1428 } 1429 RefPtr<IDWriteFont> dwFont; 1430 if (FAILED(family->GetFont(aFace->mIndex, getter_AddRefs(dwFont)))) { 1431 MOZ_ASSERT_UNREACHABLE("failed to get font from family"); 1432 return false; 1433 } 1434 HRESULT ps = GetDirectWriteFaceName(dwFont, PSNAME_ID, aPSName); 1435 HRESULT full = GetDirectWriteFaceName(dwFont, FULLNAME_ID, aFullName); 1436 if (FAILED(ps) || FAILED(full) || aPSName.IsEmpty() || aFullName.IsEmpty()) { 1437 // We'll return true if either name was found, false if both fail. 1438 // Note that on older Win7 systems, GetDirectWriteFaceName may "succeed" 1439 // but return an empty string, so we have to check for non-empty strings 1440 // to be sure we actually got a usable name. 1441 1442 // Initialize result to true if either name was already found. 1443 bool result = (SUCCEEDED(ps) && !aPSName.IsEmpty()) || 1444 (SUCCEEDED(full) && !aFullName.IsEmpty()); 1445 RefPtr<IDWriteFontFace> dwFontFace; 1446 if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) { 1447 NS_WARNING("failed to create font face"); 1448 return result; 1449 } 1450 void* context; 1451 const char* data; 1452 UINT32 size; 1453 BOOL exists; 1454 if (FAILED(dwFontFace->TryGetFontTable( 1455 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')), 1456 (const void**)&data, &size, &context, &exists)) || 1457 !exists) { 1458 NS_WARNING("failed to get name table"); 1459 return result; 1460 } 1461 MOZ_SEH_TRY { 1462 // Try to read the name table entries, and ensure result is true if either 1463 // one succeeds. 1464 if (FAILED(ps) || aPSName.IsEmpty()) { 1465 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName( 1466 data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, aPSName))) { 1467 result = true; 1468 } else { 1469 NS_WARNING("failed to read psname"); 1470 } 1471 } 1472 if (FAILED(full) || aFullName.IsEmpty()) { 1473 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName( 1474 data, size, gfxFontUtils::NAME_ID_FULL, aFullName))) { 1475 result = true; 1476 } else { 1477 NS_WARNING("failed to read fullname"); 1478 } 1479 } 1480 } 1481 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 1482 gfxCriticalNote << "Exception occurred reading face names for " 1483 << aFamily->Key().AsString(SharedFontList()).get(); 1484 } 1485 if (dwFontFace && context) { 1486 dwFontFace->ReleaseFontTable(context); 1487 } 1488 return result; 1489 } 1490 return true; 1491 } 1492 1493 void gfxDWriteFontList::ReadFaceNamesForFamily( 1494 fontlist::Family* aFamily, bool aNeedFullnamePostscriptNames) { 1495 if (!aFamily->IsInitialized()) { 1496 if (!InitializeFamily(aFamily)) { 1497 return; 1498 } 1499 } 1500 IDWriteFontCollection* collection = 1501 #ifdef MOZ_BUNDLED_FONTS 1502 aFamily->IsBundled() ? mBundledFonts : mSystemFonts; 1503 #else 1504 mSystemFonts; 1505 #endif 1506 RefPtr<IDWriteFontFamily> family; 1507 if (FAILED(collection->GetFontFamily(aFamily->Index(), 1508 getter_AddRefs(family)))) { 1509 return; 1510 } 1511 fontlist::FontList* list = SharedFontList(); 1512 const fontlist::Pointer* facePtrs = aFamily->Faces(list); 1513 nsAutoCString familyName(aFamily->DisplayName().AsString(list)); 1514 nsAutoCString key(aFamily->Key().AsString(list)); 1515 1516 MOZ_SEH_TRY { 1517 // Read PS-names and fullnames of the faces, and any alternate family names 1518 // (either localizations or legacy subfamily names) 1519 for (unsigned i = 0; i < aFamily->NumFaces(); ++i) { 1520 auto* face = facePtrs[i].ToPtr<fontlist::Face>(list); 1521 if (!face) { 1522 continue; 1523 } 1524 RefPtr<IDWriteFont> dwFont; 1525 if (FAILED(family->GetFont(face->mIndex, getter_AddRefs(dwFont)))) { 1526 continue; 1527 } 1528 RefPtr<IDWriteFontFace> dwFontFace; 1529 if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) { 1530 continue; 1531 } 1532 1533 const char* data; 1534 UINT32 size; 1535 void* context; 1536 BOOL exists; 1537 if (FAILED(dwFontFace->TryGetFontTable( 1538 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')), 1539 (const void**)&data, &size, &context, &exists)) || 1540 !exists) { 1541 continue; 1542 } 1543 1544 AutoTArray<nsCString, 4> otherFamilyNames; 1545 gfxFontUtils::ReadOtherFamilyNamesForFace(familyName, data, size, 1546 otherFamilyNames, false); 1547 for (const auto& alias : otherFamilyNames) { 1548 nsAutoCString key(alias); 1549 ToLowerCase(key); 1550 auto aliasData = mAliasTable.GetOrInsertNew(key); 1551 aliasData->InitFromFamily(aFamily, familyName); 1552 aliasData->mFaces.AppendElement(facePtrs[i]); 1553 } 1554 1555 nsAutoCString psname, fullname; 1556 // Bug 1854090: don't load PSname if the family name ends with ".tmp", 1557 // as some PDF-related software appears to pollute the font collection 1558 // with spurious re-encoded versions of standard fonts like Arial, fails 1559 // to alter the PSname, and thus can result in garbled rendering because 1560 // the wrong resource may be found via src:local(...). 1561 if (!StringEndsWith(key, ".tmp"_ns)) { 1562 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName( 1563 data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, psname))) { 1564 ToLowerCase(psname); 1565 MaybeAddToLocalNameTable(psname, 1566 fontlist::LocalFaceRec::InitData(key, i)); 1567 } 1568 } 1569 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName( 1570 data, size, gfxFontUtils::NAME_ID_FULL, fullname))) { 1571 ToLowerCase(fullname); 1572 if (fullname != psname) { 1573 MaybeAddToLocalNameTable(fullname, 1574 fontlist::LocalFaceRec::InitData(key, i)); 1575 } 1576 } 1577 1578 dwFontFace->ReleaseFontTable(context); 1579 } 1580 } 1581 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 1582 gfxCriticalNote << "Exception occurred reading names for " 1583 << familyName.get(); 1584 } 1585 } 1586 1587 enum DWriteInitError { 1588 errGDIInterop = 1, 1589 errSystemFontCollection = 2, 1590 errNoFonts = 3 1591 }; 1592 1593 void gfxDWriteFontList::InitSharedFontListForPlatform() { 1594 mGDIFontTableAccess = Preferences::GetBool( 1595 "gfx.font_rendering.directwrite.use_gdi_table_loading", false); 1596 mForceGDIClassicMaxFontSize = Preferences::GetInt( 1597 "gfx.font_rendering.cleartype_params.force_gdi_classic_max_size", 1598 mForceGDIClassicMaxFontSize); 1599 1600 mSubstitutions.Clear(); 1601 mHardcodedSubstitutions.Clear(); 1602 mNonExistingFonts.Clear(); 1603 1604 RefPtr<IDWriteFactory> factory = Factory::EnsureDWriteFactory(); 1605 HRESULT hr = factory->GetGdiInterop(getter_AddRefs(mGDIInterop)); 1606 if (FAILED(hr)) { 1607 glean::fontlist::dwritefont_init_problem.AccumulateSingleSample( 1608 uint32_t(errGDIInterop)); 1609 mSharedFontList.reset(nullptr); 1610 return; 1611 } 1612 1613 mSystemFonts = Factory::GetDWriteSystemFonts(true); 1614 NS_ASSERTION(mSystemFonts != nullptr, "GetSystemFontCollection failed!"); 1615 if (!mSystemFonts) { 1616 glean::fontlist::dwritefont_init_problem.AccumulateSingleSample( 1617 uint32_t(errSystemFontCollection)); 1618 mSharedFontList.reset(nullptr); 1619 return; 1620 } 1621 #ifdef MOZ_BUNDLED_FONTS 1622 // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an 1623 // explicit value of 0 (off) will disable them. 1624 TimeStamp start1 = TimeStamp::Now(); 1625 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) { 1626 mBundledFonts = CreateBundledFontsCollection(factory); 1627 } 1628 TimeStamp end1 = TimeStamp::Now(); 1629 #endif 1630 1631 if (XRE_IsParentProcess()) { 1632 nsAutoCString classicFamilies; 1633 AutoTArray<nsCString, 16> forceClassicFams; 1634 nsresult rv = Preferences::GetCString( 1635 "gfx.font_rendering.cleartype_params.force_gdi_classic_for_families", 1636 classicFamilies); 1637 if (NS_SUCCEEDED(rv)) { 1638 for (auto name : 1639 nsCCharSeparatedTokenizer(classicFamilies, ',').ToRange()) { 1640 BuildKeyNameFromFontName(name); 1641 forceClassicFams.AppendElement(name); 1642 } 1643 forceClassicFams.Sort(); 1644 } 1645 nsTArray<fontlist::Family::InitData> families; 1646 AppendFamiliesFromCollection(mSystemFonts, families, &forceClassicFams); 1647 #ifdef MOZ_BUNDLED_FONTS 1648 if (mBundledFonts) { 1649 TimeStamp start2 = TimeStamp::Now(); 1650 AppendFamiliesFromCollection(mBundledFonts, families); 1651 TimeStamp end2 = TimeStamp::Now(); 1652 glean::fontlist::bundledfonts_activate.AccumulateRawDuration( 1653 (end1 - start1) + (end2 - start2)); 1654 } 1655 #endif 1656 SharedFontList()->SetFamilyNames(families); 1657 GetPrefsAndStartLoader(); 1658 } 1659 1660 if (!SharedFontList()->Initialized()) { 1661 return; 1662 } 1663 1664 GetDirectWriteSubstitutes(); 1665 GetFontSubstitutes(); 1666 } 1667 1668 nsresult gfxDWriteFontList::InitFontListForPlatform() { 1669 LARGE_INTEGER frequency; // ticks per second 1670 LARGE_INTEGER t1, t2, t3, t4, t5; // ticks 1671 double elapsedTime, upTime; 1672 char nowTime[256], nowDate[256]; 1673 1674 if (LOG_FONTINIT_ENABLED()) { 1675 GetTimeFormatA(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT, nullptr, nullptr, 1676 nowTime, 256); 1677 GetDateFormatA(LOCALE_INVARIANT, 0, nullptr, nullptr, nowDate, 256); 1678 upTime = (double)GetTickCount(); 1679 } 1680 QueryPerformanceFrequency(&frequency); 1681 QueryPerformanceCounter(&t1); // start 1682 1683 HRESULT hr; 1684 mGDIFontTableAccess = Preferences::GetBool( 1685 "gfx.font_rendering.directwrite.use_gdi_table_loading", false); 1686 1687 mFontSubstitutes.Clear(); 1688 mHardcodedSubstitutes.Clear(); 1689 mNonExistingFonts.Clear(); 1690 1691 RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory(); 1692 1693 hr = factory->GetGdiInterop(getter_AddRefs(mGDIInterop)); 1694 if (FAILED(hr)) { 1695 glean::fontlist::dwritefont_init_problem.AccumulateSingleSample( 1696 uint32_t(errGDIInterop)); 1697 return NS_ERROR_FAILURE; 1698 } 1699 1700 QueryPerformanceCounter(&t2); // base-class/interop initialization 1701 1702 mSystemFonts = Factory::GetDWriteSystemFonts(true); 1703 NS_ASSERTION(mSystemFonts != nullptr, "GetSystemFontCollection failed!"); 1704 1705 if (!mSystemFonts) { 1706 glean::fontlist::dwritefont_init_problem.AccumulateSingleSample( 1707 uint32_t(errSystemFontCollection)); 1708 return NS_ERROR_FAILURE; 1709 } 1710 1711 #ifdef MOZ_BUNDLED_FONTS 1712 // Get bundled fonts before the system collection, so that in the case of 1713 // duplicate names, we have recorded the family as bundled (and therefore 1714 // available regardless of visibility settings). 1715 // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an 1716 // explicit value of 0 (off) will disable them. 1717 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) { 1718 auto timerId = glean::fontlist::bundledfonts_activate.Start(); 1719 mBundledFonts = CreateBundledFontsCollection(factory); 1720 if (mBundledFonts) { 1721 GetFontsFromCollection(mBundledFonts); 1722 } 1723 glean::fontlist::bundledfonts_activate.StopAndAccumulate( 1724 std::move(timerId)); 1725 } 1726 #endif 1727 const uint32_t kBundledCount = mFontFamilies.Count(); 1728 1729 QueryPerformanceCounter(&t3); // system font collection 1730 1731 GetFontsFromCollection(mSystemFonts); 1732 1733 // if no fonts found, something is out of whack, bail and use GDI backend 1734 NS_ASSERTION(mFontFamilies.Count() > kBundledCount, 1735 "no fonts found in the system fontlist -- holy crap batman!"); 1736 if (mFontFamilies.Count() == kBundledCount) { 1737 glean::fontlist::dwritefont_init_problem.AccumulateSingleSample( 1738 uint32_t(errNoFonts)); 1739 return NS_ERROR_FAILURE; 1740 } 1741 1742 QueryPerformanceCounter(&t4); // iterate over system fonts 1743 1744 mOtherFamilyNamesInitialized = true; 1745 GetFontSubstitutes(); 1746 1747 // bug 642093 - DirectWrite does not support old bitmap (.fon) 1748 // font files, but a few of these such as "Courier" and "MS Sans Serif" 1749 // are frequently specified in shoddy CSS, without appropriate fallbacks. 1750 // By mapping these to TrueType equivalents, we provide better consistency 1751 // with both pre-DW systems and with IE9, which appears to do the same. 1752 GetDirectWriteSubstitutes(); 1753 1754 // bug 551313 - DirectWrite creates a Gill Sans family out of 1755 // poorly named members of the Gill Sans MT family containing 1756 // only Ultra Bold weights. This causes big problems for pages 1757 // using Gill Sans which is usually only available on OSX 1758 1759 nsAutoCString nameGillSans("Gill Sans"); 1760 nsAutoCString nameGillSansMT("Gill Sans MT"); 1761 BuildKeyNameFromFontName(nameGillSans); 1762 BuildKeyNameFromFontName(nameGillSansMT); 1763 1764 gfxFontFamily* gillSansFamily = mFontFamilies.GetWeak(nameGillSans); 1765 gfxFontFamily* gillSansMTFamily = mFontFamilies.GetWeak(nameGillSansMT); 1766 1767 if (gillSansFamily && gillSansMTFamily) { 1768 gillSansFamily->FindStyleVariations(); 1769 1770 gillSansFamily->ReadLock(); 1771 const auto& faces = gillSansFamily->GetFontList(); 1772 1773 bool allUltraBold = true; 1774 for (const auto& face : faces) { 1775 // does the face have 'Ultra Bold' in the name? 1776 if (face->Name().Find("Ultra Bold"_ns) == -1) { 1777 allUltraBold = false; 1778 break; 1779 } 1780 } 1781 1782 // if all the Gill Sans faces are Ultra Bold ==> move faces 1783 // for Gill Sans into Gill Sans MT family 1784 if (allUltraBold) { 1785 // add faces to Gill Sans MT 1786 for (const auto& face : faces) { 1787 // change the entry's family name to match its adoptive family 1788 face->mFamilyName = gillSansMTFamily->Name(); 1789 gillSansMTFamily->AddFontEntry(face); 1790 1791 if (LOG_FONTLIST_ENABLED()) { 1792 nsAutoCString weightString; 1793 face->Weight().ToString(weightString); 1794 LOG_FONTLIST( 1795 ("(fontlist) moved (%s) to family (%s)" 1796 " with style: %s weight: %s stretch: %d", 1797 face->Name().get(), gillSansMTFamily->Name().get(), 1798 (face->IsItalic()) ? "italic" 1799 : (face->IsOblique() ? "oblique" : "normal"), 1800 weightString.get(), face->Stretch().AsScalar())); 1801 } 1802 } 1803 gillSansFamily->ReadUnlock(); 1804 1805 // remove Gill Sans 1806 mFontFamilies.Remove(nameGillSans); 1807 } else { 1808 gillSansFamily->ReadUnlock(); 1809 } 1810 } 1811 1812 nsAutoCString classicFamilies; 1813 nsresult rv = Preferences::GetCString( 1814 "gfx.font_rendering.cleartype_params.force_gdi_classic_for_families", 1815 classicFamilies); 1816 if (NS_SUCCEEDED(rv)) { 1817 nsCCharSeparatedTokenizer tokenizer(classicFamilies, ','); 1818 while (tokenizer.hasMoreTokens()) { 1819 nsAutoCString name(tokenizer.nextToken()); 1820 BuildKeyNameFromFontName(name); 1821 gfxFontFamily* family = mFontFamilies.GetWeak(name); 1822 if (family) { 1823 static_cast<gfxDWriteFontFamily*>(family)->SetForceGDIClassic(true); 1824 } 1825 } 1826 } 1827 mForceGDIClassicMaxFontSize = Preferences::GetInt( 1828 "gfx.font_rendering.cleartype_params.force_gdi_classic_max_size", 1829 mForceGDIClassicMaxFontSize); 1830 1831 GetPrefsAndStartLoader(); 1832 1833 QueryPerformanceCounter(&t5); // misc initialization 1834 1835 if (LOG_FONTINIT_ENABLED()) { 1836 // determine dwrite version 1837 nsAutoString dwriteVers; 1838 gfxWindowsPlatform::GetDLLVersion(L"dwrite.dll", dwriteVers); 1839 LOG_FONTINIT(("(fontinit) Start: %s %s\n", nowDate, nowTime)); 1840 LOG_FONTINIT(("(fontinit) Uptime: %9.3f s\n", upTime / 1000)); 1841 LOG_FONTINIT(("(fontinit) dwrite version: %s\n", 1842 NS_ConvertUTF16toUTF8(dwriteVers).get())); 1843 } 1844 1845 elapsedTime = (t5.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart; 1846 glean::fontlist::dwritefont_delayedinit_total.AccumulateRawDuration( 1847 TimeDuration::FromMilliseconds(elapsedTime)); 1848 glean::fontlist::dwritefont_delayedinit_count.AccumulateSingleSample( 1849 mSystemFonts->GetFontFamilyCount()); 1850 LOG_FONTINIT(( 1851 "(fontinit) Total time in InitFontList: %9.3f ms (families: %d, %s)\n", 1852 elapsedTime, mSystemFonts->GetFontFamilyCount(), 1853 (mGDIFontTableAccess ? "gdi table access" : "dwrite table access"))); 1854 1855 elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart; 1856 LOG_FONTINIT( 1857 ("(fontinit) --- base/interop obj initialization init: %9.3f ms\n", 1858 elapsedTime)); 1859 1860 elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart; 1861 glean::fontlist::dwritefont_delayedinit_collect.AccumulateRawDuration( 1862 TimeDuration::FromMilliseconds(elapsedTime)); 1863 LOG_FONTINIT( 1864 ("(fontinit) --- GetSystemFontCollection: %9.3f ms\n", elapsedTime)); 1865 1866 elapsedTime = (t4.QuadPart - t3.QuadPart) * 1000.0 / frequency.QuadPart; 1867 LOG_FONTINIT( 1868 ("(fontinit) --- iterate over families: %9.3f ms\n", elapsedTime)); 1869 1870 elapsedTime = (t5.QuadPart - t4.QuadPart) * 1000.0 / frequency.QuadPart; 1871 LOG_FONTINIT( 1872 ("(fontinit) --- misc initialization: %9.3f ms\n", elapsedTime)); 1873 1874 return NS_OK; 1875 } 1876 1877 void gfxDWriteFontList::GetFontsFromCollection( 1878 IDWriteFontCollection* aCollection) { 1879 for (UINT32 i = 0; i < aCollection->GetFontFamilyCount(); i++) { 1880 RefPtr<IDWriteFontFamily> family; 1881 aCollection->GetFontFamily(i, getter_AddRefs(family)); 1882 1883 RefPtr<IDWriteLocalizedStrings> names; 1884 HRESULT hr = family->GetFamilyNames(getter_AddRefs(names)); 1885 if (FAILED(hr)) { 1886 continue; 1887 } 1888 1889 nsAutoCString name; 1890 if (!GetEnglishOrFirstName(name, names)) { 1891 continue; 1892 } 1893 nsAutoCString familyName( 1894 name); // keep a copy before we lowercase it as a key 1895 1896 BuildKeyNameFromFontName(name); 1897 1898 RefPtr<gfxFontFamily> fam; 1899 1900 if (mFontFamilies.GetWeak(name)) { 1901 continue; 1902 } 1903 1904 FontVisibility visibility = aCollection == mSystemFonts 1905 ? GetVisibilityForFamily(familyName) 1906 : FontVisibility::Base; 1907 1908 fam = new gfxDWriteFontFamily(familyName, visibility, family, 1909 aCollection == mSystemFonts); 1910 if (!fam) { 1911 continue; 1912 } 1913 1914 if (mBadUnderlineFamilyNames.ContainsSorted(name)) { 1915 fam->SetBadUnderlineFamily(); 1916 } 1917 mFontFamilies.InsertOrUpdate(name, RefPtr{fam}); 1918 1919 // now add other family name localizations, if present 1920 uint32_t nameCount = names->GetCount(); 1921 uint32_t nameIndex; 1922 1923 if (nameCount > 1) { 1924 UINT32 englishIdx = 0; 1925 BOOL exists; 1926 // if this fails/doesn't exist, we'll have used name index 0, 1927 // so that's the one we'll want to skip here 1928 names->FindLocaleName(L"en-us", &englishIdx, &exists); 1929 AutoTArray<nsCString, 4> otherFamilyNames; 1930 for (nameIndex = 0; nameIndex < nameCount; nameIndex++) { 1931 UINT32 nameLen; 1932 AutoTArray<WCHAR, 32> localizedName; 1933 1934 // only add other names 1935 if (nameIndex == englishIdx) { 1936 continue; 1937 } 1938 1939 hr = names->GetStringLength(nameIndex, &nameLen); 1940 if (FAILED(hr)) { 1941 continue; 1942 } 1943 1944 if (!localizedName.SetLength(nameLen + 1, fallible)) { 1945 continue; 1946 } 1947 1948 hr = names->GetString(nameIndex, localizedName.Elements(), nameLen + 1); 1949 if (FAILED(hr)) { 1950 continue; 1951 } 1952 1953 NS_ConvertUTF16toUTF8 locName(localizedName.Elements()); 1954 1955 if (!familyName.Equals(locName)) { 1956 otherFamilyNames.AppendElement(locName); 1957 } 1958 } 1959 if (!otherFamilyNames.IsEmpty()) { 1960 AddOtherFamilyNames(fam, otherFamilyNames); 1961 } 1962 } 1963 1964 // at this point, all family names have been read in 1965 fam->SetOtherFamilyNamesInitialized(); 1966 } 1967 } 1968 1969 static void RemoveCharsetFromFontSubstitute(nsACString& aName) { 1970 int32_t comma = aName.FindChar(','); 1971 if (comma >= 0) aName.Truncate(comma); 1972 } 1973 1974 #define MAX_VALUE_NAME 512 1975 #define MAX_VALUE_DATA 512 1976 1977 nsresult gfxDWriteFontList::GetFontSubstitutes() { 1978 for (const FontSubstitute& fs : kFontSubstitutes) { 1979 nsAutoCString substituteName(fs.substituteName); 1980 nsAutoCString actualFontName(fs.actualFontName); 1981 BuildKeyNameFromFontName(substituteName); 1982 BuildKeyNameFromFontName(actualFontName); 1983 AddSubstitute(substituteName, actualFontName, true); 1984 } 1985 1986 HKEY hKey; 1987 DWORD i, rv, lenAlias, lenActual, valueType; 1988 WCHAR aliasName[MAX_VALUE_NAME]; 1989 WCHAR actualName[MAX_VALUE_DATA]; 1990 1991 if (RegOpenKeyExW( 1992 HKEY_LOCAL_MACHINE, 1993 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", 1994 0, KEY_READ, &hKey) != ERROR_SUCCESS) { 1995 return NS_ERROR_FAILURE; 1996 } 1997 1998 for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) { 1999 aliasName[0] = 0; 2000 lenAlias = std::size(aliasName); 2001 actualName[0] = 0; 2002 lenActual = sizeof(actualName); 2003 rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType, 2004 (LPBYTE)actualName, &lenActual); 2005 2006 if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) { 2007 continue; 2008 } 2009 2010 if (aliasName[0] == WCHAR('@')) { 2011 continue; 2012 } 2013 2014 NS_ConvertUTF16toUTF8 substituteName((char16_t*)aliasName); 2015 NS_ConvertUTF16toUTF8 actualFontName((char16_t*)actualName); 2016 RemoveCharsetFromFontSubstitute(substituteName); 2017 BuildKeyNameFromFontName(substituteName); 2018 RemoveCharsetFromFontSubstitute(actualFontName); 2019 BuildKeyNameFromFontName(actualFontName); 2020 AddSubstitute(substituteName, actualFontName, false); 2021 } 2022 2023 return NS_OK; 2024 } 2025 2026 void gfxDWriteFontList::AddSubstitute(const nsCString& aSubstituteName, 2027 const nsCString& aActualFontName, 2028 bool aIsHardcoded) { 2029 if (SharedFontList()) { 2030 auto& substitutions = 2031 aIsHardcoded ? mHardcodedSubstitutions : mSubstitutions; 2032 // Skip substitution if the original font is available, unless the 2033 // option to apply substitutions unconditionally is enabled. 2034 if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) { 2035 // Font substitutions are recorded for the canonical family names; 2036 // we don't need FindFamily to consider localized aliases when 2037 // searching. 2038 if (SharedFontList()->FindFamily(aSubstituteName, 2039 /*aPrimaryNameOnly*/ true)) { 2040 return; 2041 } 2042 } 2043 if (SharedFontList()->FindFamily(aActualFontName, 2044 /*aPrimaryNameOnly*/ true)) { 2045 substitutions.InsertOrUpdate(aSubstituteName, 2046 MakeUnique<nsCString>(aActualFontName)); 2047 } else if (substitutions.Get(aActualFontName)) { 2048 substitutions.InsertOrUpdate( 2049 aSubstituteName, 2050 MakeUnique<nsCString>(*substitutions.Get(aActualFontName))); 2051 } else { 2052 mNonExistingFonts.AppendElement(aSubstituteName); 2053 } 2054 } else { 2055 gfxFontFamily* ff; 2056 if (!aActualFontName.IsEmpty() && 2057 (ff = mFontFamilies.GetWeak(aActualFontName))) { 2058 if (aIsHardcoded) { 2059 mHardcodedSubstitutes.InsertOrUpdate(aSubstituteName, RefPtr{ff}); 2060 } else { 2061 mFontSubstitutes.InsertOrUpdate(aSubstituteName, RefPtr{ff}); 2062 } 2063 } else { 2064 mNonExistingFonts.AppendElement(aSubstituteName); 2065 } 2066 } 2067 } 2068 2069 struct FontSubstitution { 2070 const char* aliasName; 2071 const char* actualName; 2072 }; 2073 2074 static const FontSubstitution sDirectWriteSubs[] = { 2075 {"MS Sans Serif", "Microsoft Sans Serif"}, 2076 {"MS Serif", "Times New Roman"}, 2077 {"Courier", "Courier New"}, 2078 {"Small Fonts", "Arial"}, 2079 {"Roman", "Times New Roman"}, 2080 {"Script", "Mistral"}}; 2081 2082 void gfxDWriteFontList::GetDirectWriteSubstitutes() { 2083 for (uint32_t i = 0; i < std::size(sDirectWriteSubs); ++i) { 2084 const FontSubstitution& sub(sDirectWriteSubs[i]); 2085 nsAutoCString substituteName(sub.aliasName); 2086 BuildKeyNameFromFontName(substituteName); 2087 if (SharedFontList()) { 2088 // Skip substitution if the original font is available, unless the option 2089 // to apply substitutions unconditionally is enabled. 2090 if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) { 2091 // We don't need FindFamily to consider localized aliases when searching 2092 // for the DirectWrite substitutes, we know the canonical names. 2093 if (SharedFontList()->FindFamily(substituteName, 2094 /*aPrimaryNameOnly*/ true)) { 2095 continue; 2096 } 2097 } 2098 nsAutoCString actualFontName(sub.actualName); 2099 BuildKeyNameFromFontName(actualFontName); 2100 if (SharedFontList()->FindFamily(actualFontName, 2101 /*aPrimaryNameOnly*/ true)) { 2102 mSubstitutions.InsertOrUpdate(substituteName, 2103 MakeUnique<nsCString>(actualFontName)); 2104 mHardcodedSubstitutions.InsertOrUpdate( 2105 substituteName, MakeUnique<nsCString>(actualFontName)); 2106 } else { 2107 mNonExistingFonts.AppendElement(substituteName); 2108 } 2109 } else { 2110 if (nullptr != mFontFamilies.GetWeak(substituteName)) { 2111 // don't do the substitution if user actually has a usable font 2112 // with this name installed 2113 continue; 2114 } 2115 nsAutoCString actualFontName(sub.actualName); 2116 BuildKeyNameFromFontName(actualFontName); 2117 gfxFontFamily* ff; 2118 if (nullptr != (ff = mFontFamilies.GetWeak(actualFontName))) { 2119 mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff}); 2120 mHardcodedSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff}); 2121 } else { 2122 mNonExistingFonts.AppendElement(substituteName); 2123 } 2124 } 2125 } 2126 } 2127 2128 bool gfxDWriteFontList::FindAndAddFamiliesLocked( 2129 FontVisibilityProvider* aFontVisibilityProvider, 2130 StyleGenericFontFamily aGeneric, const nsACString& aFamily, 2131 nsTArray<FamilyAndGeneric>* aOutput, FindFamiliesFlags aFlags, 2132 gfxFontStyle* aStyle, nsAtom* aLanguage, gfxFloat aDevToCssSize) { 2133 nsAutoCString keyName(aFamily); 2134 BuildKeyNameFromFontName(keyName); 2135 2136 const bool useHardcodedList = 2137 aFontVisibilityProvider 2138 ? aFontVisibilityProvider->ShouldResistFingerprinting( 2139 RFPTarget::UseHardcodedFontSubstitutes) 2140 : nsContentUtils::ShouldResistFingerprinting( 2141 "aFontVisibilityProvider is not available", 2142 RFPTarget::UseHardcodedFontSubstitutes); 2143 if (SharedFontList()) { 2144 nsACString* subst = useHardcodedList ? mHardcodedSubstitutions.Get(keyName) 2145 : mSubstitutions.Get(keyName); 2146 if (subst) { 2147 keyName = *subst; 2148 } 2149 } else { 2150 gfxFontFamily* ff = useHardcodedList 2151 ? mHardcodedSubstitutes.GetWeak(keyName) 2152 : mFontSubstitutes.GetWeak(keyName); 2153 FontVisibility level = aFontVisibilityProvider 2154 ? aFontVisibilityProvider->GetFontVisibility() 2155 : FontVisibility::User; 2156 if (ff && IsVisibleToCSS(*ff, level)) { 2157 aOutput->AppendElement(FamilyAndGeneric(ff, aGeneric)); 2158 return true; 2159 } 2160 } 2161 2162 if (mNonExistingFonts.Contains(keyName)) { 2163 return false; 2164 } 2165 2166 return gfxPlatformFontList::FindAndAddFamiliesLocked( 2167 aFontVisibilityProvider, aGeneric, keyName, aOutput, aFlags, aStyle, 2168 aLanguage, aDevToCssSize); 2169 } 2170 2171 void gfxDWriteFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 2172 FontListSizes* aSizes) const { 2173 gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 2174 2175 AutoLock lock(mLock); 2176 2177 // We are a singleton, so include the font loader singleton's memory. 2178 MOZ_ASSERT(static_cast<const gfxPlatformFontList*>(this) == 2179 gfxPlatformFontList::PlatformFontList()); 2180 gfxDWriteFontFileLoader* loader = static_cast<gfxDWriteFontFileLoader*>( 2181 gfxDWriteFontFileLoader::Instance()); 2182 aSizes->mLoaderSize += loader->SizeOfIncludingThis(aMallocSizeOf); 2183 2184 aSizes->mFontListSize += 2185 SizeOfFontFamilyTableExcludingThis(mFontSubstitutes, aMallocSizeOf); 2186 aSizes->mFontListSize += 2187 SizeOfFontFamilyTableExcludingThis(mHardcodedSubstitutes, aMallocSizeOf); 2188 2189 aSizes->mFontListSize += 2190 mNonExistingFonts.ShallowSizeOfExcludingThis(aMallocSizeOf); 2191 for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) { 2192 aSizes->mFontListSize += 2193 mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf); 2194 } 2195 } 2196 2197 void gfxDWriteFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 2198 FontListSizes* aSizes) const { 2199 aSizes->mFontListSize += aMallocSizeOf(this); 2200 AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 2201 } 2202 2203 static HRESULT GetFamilyName(IDWriteFont* aFont, nsCString& aFamilyName) { 2204 HRESULT hr; 2205 RefPtr<IDWriteFontFamily> family; 2206 2207 // clean out previous value 2208 aFamilyName.Truncate(); 2209 2210 hr = aFont->GetFontFamily(getter_AddRefs(family)); 2211 if (FAILED(hr)) { 2212 return hr; 2213 } 2214 2215 RefPtr<IDWriteLocalizedStrings> familyNames; 2216 2217 hr = family->GetFamilyNames(getter_AddRefs(familyNames)); 2218 if (FAILED(hr)) { 2219 return hr; 2220 } 2221 2222 if (!GetEnglishOrFirstName(aFamilyName, familyNames)) { 2223 return E_FAIL; 2224 } 2225 2226 return S_OK; 2227 } 2228 2229 // bug 705594 - the method below doesn't actually do any "drawing", it's only 2230 // used to invoke the DirectWrite layout engine to determine the fallback font 2231 // for a given character. 2232 2233 IFACEMETHODIMP DWriteFontFallbackRenderer::DrawGlyphRun( 2234 void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, 2235 DWRITE_MEASURING_MODE measuringMode, DWRITE_GLYPH_RUN const* glyphRun, 2236 DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, 2237 IUnknown* clientDrawingEffect) { 2238 if (!mSystemFonts) { 2239 return E_FAIL; 2240 } 2241 2242 HRESULT hr = S_OK; 2243 2244 RefPtr<IDWriteFont> font; 2245 hr = mSystemFonts->GetFontFromFontFace(glyphRun->fontFace, 2246 getter_AddRefs(font)); 2247 if (FAILED(hr)) { 2248 return hr; 2249 } 2250 2251 // copy the family name 2252 hr = GetFamilyName(font, mFamilyName); 2253 if (FAILED(hr)) { 2254 return hr; 2255 } 2256 2257 // Arial is used as the default fallback font 2258 // so if it matches ==> no font found 2259 if (mFamilyName.EqualsLiteral("Arial")) { 2260 mFamilyName.Truncate(); 2261 return E_FAIL; 2262 } 2263 return hr; 2264 } 2265 2266 gfxFontEntry* gfxDWriteFontList::PlatformGlobalFontFallback( 2267 FontVisibilityProvider* aFontVisibilityProvider, const uint32_t aCh, 2268 Script aRunScript, const gfxFontStyle* aMatchStyle, 2269 FontFamily& aMatchedFamily) { 2270 HRESULT hr; 2271 2272 RefPtr<IDWriteFactory> dwFactory = Factory::GetDWriteFactory(); 2273 if (!dwFactory) { 2274 return nullptr; 2275 } 2276 2277 // initialize fallback renderer 2278 if (!mFallbackRenderer) { 2279 mFallbackRenderer = new DWriteFontFallbackRenderer(dwFactory); 2280 } 2281 if (!mFallbackRenderer->IsValid()) { 2282 return nullptr; 2283 } 2284 2285 // initialize text format 2286 if (!mFallbackFormat) { 2287 hr = dwFactory->CreateTextFormat( 2288 L"Arial", nullptr, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, 2289 DWRITE_FONT_STRETCH_NORMAL, 72.0f, L"en-us", 2290 getter_AddRefs(mFallbackFormat)); 2291 if (FAILED(hr)) { 2292 return nullptr; 2293 } 2294 } 2295 2296 // set up string with fallback character 2297 wchar_t str[16]; 2298 uint32_t strLen; 2299 2300 if (IS_IN_BMP(aCh)) { 2301 str[0] = static_cast<wchar_t>(aCh); 2302 str[1] = 0; 2303 strLen = 1; 2304 } else { 2305 str[0] = static_cast<wchar_t>(H_SURROGATE(aCh)); 2306 str[1] = static_cast<wchar_t>(L_SURROGATE(aCh)); 2307 str[2] = 0; 2308 strLen = 2; 2309 } 2310 2311 // set up layout 2312 RefPtr<IDWriteTextLayout> fallbackLayout; 2313 2314 hr = dwFactory->CreateTextLayout(str, strLen, mFallbackFormat, 200.0f, 200.0f, 2315 getter_AddRefs(fallbackLayout)); 2316 if (FAILED(hr)) { 2317 return nullptr; 2318 } 2319 2320 // call the draw method to invoke the DirectWrite layout functions 2321 // which determine the fallback font 2322 MOZ_SEH_TRY { 2323 hr = fallbackLayout->Draw(nullptr, mFallbackRenderer, 50.0f, 50.0f); 2324 if (FAILED(hr)) { 2325 return nullptr; 2326 } 2327 } 2328 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 2329 gfxCriticalNote << "Exception occurred during DWrite font fallback"; 2330 return nullptr; 2331 } 2332 2333 FontFamily family = FindFamily(aFontVisibilityProvider, 2334 mFallbackRenderer->FallbackFamilyName()); 2335 if (!family.IsNull()) { 2336 gfxFontEntry* fontEntry = nullptr; 2337 if (family.mShared) { 2338 auto face = 2339 family.mShared->FindFaceForStyle(SharedFontList(), *aMatchStyle); 2340 if (face) { 2341 fontEntry = GetOrCreateFontEntry(face, family.mShared); 2342 } 2343 } else { 2344 fontEntry = family.mUnshared->FindFontForStyle(*aMatchStyle); 2345 } 2346 if (fontEntry && fontEntry->HasCharacter(aCh)) { 2347 aMatchedFamily = family; 2348 return fontEntry; 2349 } 2350 glean::fontlist::bad_fallback_font 2351 .EnumGet(glean::fontlist::BadFallbackFontLabel::eTrue) 2352 .Add(); 2353 } 2354 2355 return nullptr; 2356 } 2357 2358 // used to load system-wide font info on off-main thread 2359 class DirectWriteFontInfo : public FontInfoData { 2360 public: 2361 DirectWriteFontInfo(bool aLoadOtherNames, bool aLoadFaceNames, 2362 bool aLoadCmaps, IDWriteFontCollection* aSystemFonts 2363 #ifdef MOZ_BUNDLED_FONTS 2364 , 2365 IDWriteFontCollection* aBundledFonts 2366 #endif 2367 ) 2368 : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps), 2369 mSystemFonts(aSystemFonts) 2370 #ifdef MOZ_BUNDLED_FONTS 2371 , 2372 mBundledFonts(aBundledFonts) 2373 #endif 2374 { 2375 } 2376 2377 virtual ~DirectWriteFontInfo() = default; 2378 2379 // loads font data for all members of a given family 2380 virtual void LoadFontFamilyData(const nsACString& aFamilyName); 2381 2382 private: 2383 RefPtr<IDWriteFontCollection> mSystemFonts; 2384 #ifdef MOZ_BUNDLED_FONTS 2385 RefPtr<IDWriteFontCollection> mBundledFonts; 2386 #endif 2387 }; 2388 2389 void DirectWriteFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) { 2390 // lookup the family 2391 NS_ConvertUTF8toUTF16 famName(aFamilyName); 2392 2393 HRESULT hr; 2394 BOOL exists = false; 2395 2396 uint32_t index; 2397 RefPtr<IDWriteFontFamily> family; 2398 hr = mSystemFonts->FindFamilyName((const wchar_t*)famName.get(), &index, 2399 &exists); 2400 if (SUCCEEDED(hr) && exists) { 2401 mSystemFonts->GetFontFamily(index, getter_AddRefs(family)); 2402 if (!family) { 2403 return; 2404 } 2405 } 2406 2407 #ifdef MOZ_BUNDLED_FONTS 2408 if (!family && mBundledFonts) { 2409 hr = mBundledFonts->FindFamilyName((const wchar_t*)famName.get(), &index, 2410 &exists); 2411 if (SUCCEEDED(hr) && exists) { 2412 mBundledFonts->GetFontFamily(index, getter_AddRefs(family)); 2413 } 2414 } 2415 #endif 2416 2417 if (!family) { 2418 return; 2419 } 2420 2421 // later versions of DirectWrite support querying the fullname/psname 2422 bool loadFaceNamesUsingDirectWrite = mLoadFaceNames; 2423 2424 for (uint32_t i = 0; i < family->GetFontCount(); i++) { 2425 // get the font 2426 RefPtr<IDWriteFont> dwFont; 2427 hr = family->GetFont(i, getter_AddRefs(dwFont)); 2428 if (FAILED(hr)) { 2429 // This should never happen. 2430 NS_WARNING("Failed to get existing font from family."); 2431 continue; 2432 } 2433 2434 if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) { 2435 // We don't want these in the font list; we'll apply simulations 2436 // on the fly when appropriate. 2437 continue; 2438 } 2439 2440 mLoadStats.fonts++; 2441 2442 // get the name of the face 2443 nsCString fullID(aFamilyName); 2444 nsAutoCString fontName; 2445 hr = GetDirectWriteFontName(dwFont, fontName); 2446 if (FAILED(hr)) { 2447 continue; 2448 } 2449 fullID.Append(' '); 2450 fullID.Append(fontName); 2451 2452 FontFaceData fontData; 2453 bool haveData = true; 2454 RefPtr<IDWriteFontFace> dwFontFace; 2455 2456 if (mLoadFaceNames) { 2457 // try to load using DirectWrite first 2458 if (loadFaceNamesUsingDirectWrite) { 2459 hr = 2460 GetDirectWriteFaceName(dwFont, PSNAME_ID, fontData.mPostscriptName); 2461 if (FAILED(hr)) { 2462 loadFaceNamesUsingDirectWrite = false; 2463 } 2464 hr = GetDirectWriteFaceName(dwFont, FULLNAME_ID, fontData.mFullName); 2465 if (FAILED(hr)) { 2466 loadFaceNamesUsingDirectWrite = false; 2467 } 2468 } 2469 2470 // if DirectWrite read fails, load directly from name table 2471 if (!loadFaceNamesUsingDirectWrite) { 2472 hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace)); 2473 if (SUCCEEDED(hr)) { 2474 uint32_t kNAME = 2475 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')); 2476 const char* nameData; 2477 BOOL exists; 2478 void* ctx; 2479 uint32_t nameSize; 2480 2481 hr = dwFontFace->TryGetFontTable(kNAME, (const void**)&nameData, 2482 &nameSize, &ctx, &exists); 2483 if (SUCCEEDED(hr) && nameData && nameSize > 0) { 2484 MOZ_SEH_TRY { 2485 gfxFontUtils::ReadCanonicalName(nameData, nameSize, 2486 gfxFontUtils::NAME_ID_FULL, 2487 fontData.mFullName); 2488 gfxFontUtils::ReadCanonicalName(nameData, nameSize, 2489 gfxFontUtils::NAME_ID_POSTSCRIPT, 2490 fontData.mPostscriptName); 2491 } 2492 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 2493 gfxCriticalNote << "Exception occurred reading names for " 2494 << PromiseFlatCString(aFamilyName).get(); 2495 } 2496 dwFontFace->ReleaseFontTable(ctx); 2497 } 2498 } 2499 } 2500 2501 haveData = 2502 !fontData.mPostscriptName.IsEmpty() || !fontData.mFullName.IsEmpty(); 2503 if (haveData) { 2504 mLoadStats.facenames++; 2505 } 2506 } 2507 2508 // cmaps 2509 if (mLoadCmaps) { 2510 if (!dwFontFace) { 2511 hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace)); 2512 if (!SUCCEEDED(hr)) { 2513 continue; 2514 } 2515 } 2516 2517 uint32_t kCMAP = 2518 NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p')); 2519 const uint8_t* cmapData; 2520 BOOL exists; 2521 void* ctx; 2522 uint32_t cmapSize; 2523 2524 hr = dwFontFace->TryGetFontTable(kCMAP, (const void**)&cmapData, 2525 &cmapSize, &ctx, &exists); 2526 2527 if (SUCCEEDED(hr) && exists) { 2528 bool cmapLoaded = false; 2529 RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(256); 2530 uint32_t offset; 2531 MOZ_SEH_TRY { 2532 if (cmapData && cmapSize > 0 && 2533 NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData, cmapSize, *charmap, 2534 offset))) { 2535 fontData.mCharacterMap = charmap; 2536 fontData.mUVSOffset = offset; 2537 cmapLoaded = true; 2538 mLoadStats.cmaps++; 2539 } 2540 } 2541 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { 2542 gfxCriticalNote << "Exception occurred reading cmaps for " 2543 << PromiseFlatCString(aFamilyName).get(); 2544 } 2545 dwFontFace->ReleaseFontTable(ctx); 2546 haveData = haveData || cmapLoaded; 2547 } 2548 } 2549 2550 // if have data, load 2551 if (haveData) { 2552 mFontFaceData.InsertOrUpdate(fullID, fontData); 2553 } 2554 } 2555 } 2556 2557 already_AddRefed<FontInfoData> gfxDWriteFontList::CreateFontInfoData() { 2558 bool loadCmaps = !UsesSystemFallback() || 2559 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback(); 2560 2561 RefPtr<DirectWriteFontInfo> fi = new DirectWriteFontInfo( 2562 false, NeedFullnamePostscriptNames(), loadCmaps, mSystemFonts 2563 #ifdef MOZ_BUNDLED_FONTS 2564 , 2565 mBundledFonts 2566 #endif 2567 ); 2568 2569 return fi.forget(); 2570 } 2571 2572 gfxFontFamily* gfxDWriteFontList::CreateFontFamily( 2573 const nsACString& aName, FontVisibility aVisibility) const { 2574 return new gfxDWriteFontFamily(aName, aVisibility, nullptr); 2575 } 2576 2577 #ifdef MOZ_BUNDLED_FONTS 2578 2579 # define IMPL_QI_FOR_DWRITE(_interface) \ 2580 public: \ 2581 IFACEMETHOD(QueryInterface)(IID const& riid, void** ppvObject) { \ 2582 if (__uuidof(_interface) == riid) { \ 2583 *ppvObject = this; \ 2584 } else if (__uuidof(IUnknown) == riid) { \ 2585 *ppvObject = this; \ 2586 } else { \ 2587 *ppvObject = nullptr; \ 2588 return E_NOINTERFACE; \ 2589 } \ 2590 this->AddRef(); \ 2591 return S_OK; \ 2592 } 2593 2594 class BundledFontFileEnumerator : public IDWriteFontFileEnumerator { 2595 IMPL_QI_FOR_DWRITE(IDWriteFontFileEnumerator) 2596 2597 NS_INLINE_DECL_REFCOUNTING(BundledFontFileEnumerator) 2598 2599 public: 2600 BundledFontFileEnumerator(IDWriteFactory* aFactory, nsIFile* aFontDir); 2601 2602 IFACEMETHODIMP MoveNext(BOOL* hasCurrentFile); 2603 2604 IFACEMETHODIMP GetCurrentFontFile(IDWriteFontFile** fontFile); 2605 2606 private: 2607 BundledFontFileEnumerator() = delete; 2608 BundledFontFileEnumerator(const BundledFontFileEnumerator&) = delete; 2609 BundledFontFileEnumerator& operator=(const BundledFontFileEnumerator&) = 2610 delete; 2611 virtual ~BundledFontFileEnumerator() = default; 2612 2613 RefPtr<IDWriteFactory> mFactory; 2614 2615 nsCOMPtr<nsIFile> mFontDir; 2616 nsCOMPtr<nsIDirectoryEnumerator> mEntries; 2617 nsCOMPtr<nsISupports> mCurrent; 2618 }; 2619 2620 BundledFontFileEnumerator::BundledFontFileEnumerator(IDWriteFactory* aFactory, 2621 nsIFile* aFontDir) 2622 : mFactory(aFactory), mFontDir(aFontDir) { 2623 mFontDir->GetDirectoryEntries(getter_AddRefs(mEntries)); 2624 } 2625 2626 IFACEMETHODIMP 2627 BundledFontFileEnumerator::MoveNext(BOOL* aHasCurrentFile) { 2628 bool hasMore = false; 2629 if (mEntries) { 2630 if (NS_SUCCEEDED(mEntries->HasMoreElements(&hasMore)) && hasMore) { 2631 if (NS_SUCCEEDED(mEntries->GetNext(getter_AddRefs(mCurrent)))) { 2632 hasMore = true; 2633 } 2634 } 2635 } 2636 *aHasCurrentFile = hasMore; 2637 return S_OK; 2638 } 2639 2640 IFACEMETHODIMP 2641 BundledFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** aFontFile) { 2642 nsCOMPtr<nsIFile> file = do_QueryInterface(mCurrent); 2643 if (!file) { 2644 return E_FAIL; 2645 } 2646 nsString path; 2647 if (NS_FAILED(file->GetPath(path))) { 2648 return E_FAIL; 2649 } 2650 return mFactory->CreateFontFileReference((const WCHAR*)path.get(), nullptr, 2651 aFontFile); 2652 } 2653 2654 class BundledFontLoader : public IDWriteFontCollectionLoader { 2655 IMPL_QI_FOR_DWRITE(IDWriteFontCollectionLoader) 2656 2657 NS_INLINE_DECL_REFCOUNTING(BundledFontLoader) 2658 2659 public: 2660 BundledFontLoader() {} 2661 2662 IFACEMETHODIMP CreateEnumeratorFromKey( 2663 IDWriteFactory* aFactory, const void* aCollectionKey, 2664 UINT32 aCollectionKeySize, 2665 IDWriteFontFileEnumerator** aFontFileEnumerator); 2666 2667 private: 2668 BundledFontLoader(const BundledFontLoader&) = delete; 2669 BundledFontLoader& operator=(const BundledFontLoader&) = delete; 2670 virtual ~BundledFontLoader() = default; 2671 }; 2672 2673 IFACEMETHODIMP 2674 BundledFontLoader::CreateEnumeratorFromKey( 2675 IDWriteFactory* aFactory, const void* aCollectionKey, 2676 UINT32 aCollectionKeySize, 2677 IDWriteFontFileEnumerator** aFontFileEnumerator) { 2678 nsIFile* fontDir = *(nsIFile**)aCollectionKey; 2679 *aFontFileEnumerator = new BundledFontFileEnumerator(aFactory, fontDir); 2680 NS_ADDREF(*aFontFileEnumerator); 2681 return S_OK; 2682 } 2683 2684 already_AddRefed<IDWriteFontCollection> 2685 gfxDWriteFontList::CreateBundledFontsCollection(IDWriteFactory* aFactory) { 2686 nsCOMPtr<nsIFile> localDir; 2687 nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir)); 2688 if (NS_FAILED(rv)) { 2689 return nullptr; 2690 } 2691 if (NS_FAILED(localDir->Append(u"fonts"_ns))) { 2692 return nullptr; 2693 } 2694 bool isDir; 2695 if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) { 2696 return nullptr; 2697 } 2698 2699 RefPtr<BundledFontLoader> loader = new BundledFontLoader(); 2700 if (FAILED(aFactory->RegisterFontCollectionLoader(loader))) { 2701 return nullptr; 2702 } 2703 2704 const void* key = localDir.get(); 2705 RefPtr<IDWriteFontCollection> collection; 2706 HRESULT hr = aFactory->CreateCustomFontCollection(loader, &key, sizeof(key), 2707 getter_AddRefs(collection)); 2708 2709 aFactory->UnregisterFontCollectionLoader(loader); 2710 2711 if (FAILED(hr)) { 2712 return nullptr; 2713 } else { 2714 return collection.forget(); 2715 } 2716 } 2717 2718 #endif