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