Bug 1732219 - Add API for fetching the preview image. r=geckoview-reviewers,agi,mconley
[gecko.git] / gfx / thebes / gfxDWriteFontList.cpp
blob29690ba2c32bafa715f361733a4e588e23be87b5
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 "nsServiceManagerUtils.h"
15 #include "nsCharSeparatedTokenizer.h"
16 #include "mozilla/Preferences.h"
17 #include "mozilla/ProfilerLabels.h"
18 #include "mozilla/Sprintf.h"
19 #include "mozilla/StaticPrefs_gfx.h"
20 #include "mozilla/Telemetry.h"
21 #include "mozilla/WindowsProcessMitigations.h"
22 #include "mozilla/WindowsVersion.h"
23 #include "nsDirectoryServiceUtils.h"
24 #include "nsDirectoryServiceDefs.h"
25 #include "nsAppDirectoryServiceDefs.h"
27 #include "gfxGDIFontList.h"
29 #include "harfbuzz/hb.h"
31 #include "StandardFonts-win10.inc"
33 using namespace mozilla;
34 using namespace mozilla::gfx;
35 using mozilla::intl::OSPreferences;
37 #define LOG_FONTLIST(args) \
38 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
39 #define LOG_FONTLIST_ENABLED() \
40 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
42 #define LOG_FONTINIT(args) \
43 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
44 #define LOG_FONTINIT_ENABLED() \
45 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
47 #define LOG_CMAPDATA_ENABLED() \
48 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)
50 static __inline void BuildKeyNameFromFontName(nsACString& aName) {
51 ToLowerCase(aName);
54 ////////////////////////////////////////////////////////////////////////////////
55 // gfxDWriteFontFamily
57 gfxDWriteFontFamily::~gfxDWriteFontFamily() {}
59 static bool GetNameAsUtf8(nsACString& aName, IDWriteLocalizedStrings* aStrings,
60 UINT32 aIndex) {
61 AutoTArray<WCHAR, 32> name;
62 UINT32 length;
63 HRESULT hr = aStrings->GetStringLength(aIndex, &length);
64 if (FAILED(hr)) {
65 return false;
67 if (!name.SetLength(length + 1, fallible)) {
68 return false;
70 hr = aStrings->GetString(aIndex, name.Elements(), length + 1);
71 if (FAILED(hr)) {
72 return false;
74 aName.Truncate();
75 AppendUTF16toUTF8(
76 Substring(reinterpret_cast<const char16_t*>(name.Elements()),
77 name.Length() - 1),
78 aName);
79 return true;
82 static bool GetEnglishOrFirstName(nsACString& aName,
83 IDWriteLocalizedStrings* aStrings) {
84 UINT32 englishIdx = 0;
85 BOOL exists;
86 HRESULT hr = aStrings->FindLocaleName(L"en-us", &englishIdx, &exists);
87 if (FAILED(hr) || !exists) {
88 // Use 0 index if english is not found.
89 englishIdx = 0;
91 return GetNameAsUtf8(aName, aStrings, englishIdx);
94 static HRESULT GetDirectWriteFontName(IDWriteFont* aFont,
95 nsACString& aFontName) {
96 HRESULT hr;
98 RefPtr<IDWriteLocalizedStrings> names;
99 hr = aFont->GetFaceNames(getter_AddRefs(names));
100 if (FAILED(hr)) {
101 return hr;
104 if (!GetEnglishOrFirstName(aFontName, names)) {
105 return E_FAIL;
108 return S_OK;
111 #define FULLNAME_ID DWRITE_INFORMATIONAL_STRING_FULL_NAME
112 #define PSNAME_ID DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME
114 // for use in reading postscript or fullname
115 static HRESULT GetDirectWriteFaceName(IDWriteFont* aFont,
116 DWRITE_INFORMATIONAL_STRING_ID aWhichName,
117 nsACString& aFontName) {
118 HRESULT hr;
120 BOOL exists;
121 RefPtr<IDWriteLocalizedStrings> infostrings;
122 hr = aFont->GetInformationalStrings(aWhichName, getter_AddRefs(infostrings),
123 &exists);
124 if (FAILED(hr) || !exists) {
125 return E_FAIL;
128 if (!GetEnglishOrFirstName(aFontName, infostrings)) {
129 return E_FAIL;
132 return S_OK;
135 void gfxDWriteFontFamily::FindStyleVariations(FontInfoData* aFontInfoData) {
136 HRESULT hr;
137 if (mHasStyles) {
138 return;
140 mHasStyles = true;
142 gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
144 bool skipFaceNames =
145 mFaceNamesInitialized || !fp->NeedFullnamePostscriptNames();
146 bool fontInfoShouldHaveFaceNames = !mFaceNamesInitialized &&
147 fp->NeedFullnamePostscriptNames() &&
148 aFontInfoData;
150 for (UINT32 i = 0; i < mDWFamily->GetFontCount(); i++) {
151 RefPtr<IDWriteFont> font;
152 hr = mDWFamily->GetFont(i, getter_AddRefs(font));
153 if (FAILED(hr)) {
154 // This should never happen.
155 NS_WARNING("Failed to get existing font from family.");
156 continue;
159 if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
160 // We don't want these in the font list; we'll apply simulations
161 // on the fly when appropriate.
162 continue;
165 // name
166 nsCString fullID(mName);
167 nsAutoCString faceName;
168 hr = GetDirectWriteFontName(font, faceName);
169 if (FAILED(hr)) {
170 continue;
172 fullID.Append(' ');
173 fullID.Append(faceName);
175 // Ignore italic style's "Meiryo" because "Meiryo (Bold) Italic" has
176 // non-italic style glyphs as Japanese characters. However, using it
177 // causes serious problem if web pages wants some elements to be
178 // different style from others only with font-style. For example,
179 // <em> and <i> should be rendered as italic in the default style.
180 if (fullID.EqualsLiteral("Meiryo Italic") ||
181 fullID.EqualsLiteral("Meiryo Bold Italic")) {
182 continue;
185 gfxDWriteFontEntry* fe =
186 new gfxDWriteFontEntry(fullID, font, mIsSystemFontFamily);
187 fe->SetForceGDIClassic(mForceGDIClassic);
189 fe->SetupVariationRanges();
191 AddFontEntry(fe);
193 // postscript/fullname if needed
194 nsAutoCString psname, fullname;
195 if (fontInfoShouldHaveFaceNames) {
196 aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
197 if (!fullname.IsEmpty()) {
198 fp->AddFullname(fe, fullname);
200 if (!psname.IsEmpty()) {
201 fp->AddPostscriptName(fe, psname);
203 } else if (!skipFaceNames) {
204 hr = GetDirectWriteFaceName(font, PSNAME_ID, psname);
205 if (FAILED(hr)) {
206 skipFaceNames = true;
207 } else if (psname.Length() > 0) {
208 fp->AddPostscriptName(fe, psname);
211 hr = GetDirectWriteFaceName(font, FULLNAME_ID, fullname);
212 if (FAILED(hr)) {
213 skipFaceNames = true;
214 } else if (fullname.Length() > 0) {
215 fp->AddFullname(fe, fullname);
219 if (LOG_FONTLIST_ENABLED()) {
220 nsAutoCString weightString;
221 fe->Weight().ToString(weightString);
222 LOG_FONTLIST(
223 ("(fontlist) added (%s) to family (%s)"
224 " with style: %s weight: %s stretch: %d psname: %s fullname: %s",
225 fe->Name().get(), Name().get(),
226 (fe->IsItalic()) ? "italic"
227 : (fe->IsOblique() ? "oblique" : "normal"),
228 weightString.get(), fe->Stretch(), psname.get(), fullname.get()));
232 // assume that if no error, all postscript/fullnames were initialized
233 if (!skipFaceNames) {
234 mFaceNamesInitialized = true;
237 if (!mAvailableFonts.Length()) {
238 NS_WARNING("Family with no font faces in it.");
241 if (mIsBadUnderlineFamily) {
242 SetBadUnderlineFonts();
245 CheckForSimpleFamily();
246 if (mIsSimpleFamily) {
247 for (auto& f : mAvailableFonts) {
248 if (f) {
249 static_cast<gfxDWriteFontEntry*>(f.get())->mMayUseGDIAccess = true;
255 void gfxDWriteFontFamily::ReadFaceNames(gfxPlatformFontList* aPlatformFontList,
256 bool aNeedFullnamePostscriptNames,
257 FontInfoData* aFontInfoData) {
258 // if all needed names have already been read, skip
259 if (mOtherFamilyNamesInitialized &&
260 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
261 return;
264 // If we've been passed a FontInfoData, we skip the DWrite implementation
265 // here and fall back to the generic code which will use that info.
266 if (!aFontInfoData) {
267 // DirectWrite version of this will try to read
268 // postscript/fullnames via DirectWrite API
269 FindStyleVariations();
272 // fallback to looking up via name table
273 if (!mOtherFamilyNamesInitialized || !mFaceNamesInitialized) {
274 gfxFontFamily::ReadFaceNames(aPlatformFontList,
275 aNeedFullnamePostscriptNames, aFontInfoData);
279 void gfxDWriteFontFamily::LocalizedName(nsACString& aLocalizedName) {
280 aLocalizedName = Name(); // just return canonical name in case of failure
282 if (!mDWFamily) {
283 return;
286 HRESULT hr;
287 nsAutoCString locale;
288 // We use system locale here because it's what user expects to see.
289 // See bug 1349454 for details.
290 RefPtr<OSPreferences> osprefs = OSPreferences::GetInstanceAddRefed();
291 if (!osprefs) {
292 return;
294 osprefs->GetSystemLocale(locale);
296 RefPtr<IDWriteLocalizedStrings> names;
298 hr = mDWFamily->GetFamilyNames(getter_AddRefs(names));
299 if (FAILED(hr)) {
300 return;
302 UINT32 idx = 0;
303 BOOL exists;
304 hr =
305 names->FindLocaleName(NS_ConvertUTF8toUTF16(locale).get(), &idx, &exists);
306 if (FAILED(hr)) {
307 return;
309 if (!exists) {
310 // Use english is localized is not found.
311 hr = names->FindLocaleName(L"en-us", &idx, &exists);
312 if (FAILED(hr)) {
313 return;
315 if (!exists) {
316 // Use 0 index if english is not found.
317 idx = 0;
320 AutoTArray<WCHAR, 32> famName;
321 UINT32 length;
323 hr = names->GetStringLength(idx, &length);
324 if (FAILED(hr)) {
325 return;
328 if (!famName.SetLength(length + 1, fallible)) {
329 // Eeep - running out of memory. Unlikely to end well.
330 return;
333 hr = names->GetString(idx, famName.Elements(), length + 1);
334 if (FAILED(hr)) {
335 return;
338 aLocalizedName = NS_ConvertUTF16toUTF8((const char16_t*)famName.Elements(),
339 famName.Length() - 1);
342 bool gfxDWriteFontFamily::IsSymbolFontFamily() const {
343 // Just check the first font in the family
344 if (mDWFamily->GetFontCount() > 0) {
345 RefPtr<IDWriteFont> font;
346 if (SUCCEEDED(mDWFamily->GetFont(0, getter_AddRefs(font)))) {
347 return font->IsSymbolFont();
350 return false;
353 void gfxDWriteFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
354 FontListSizes* aSizes) const {
355 gfxFontFamily::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
356 // TODO:
357 // This doesn't currently account for |mDWFamily|
360 void gfxDWriteFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
361 FontListSizes* aSizes) const {
362 aSizes->mFontListSize += aMallocSizeOf(this);
363 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
366 ////////////////////////////////////////////////////////////////////////////////
367 // gfxDWriteFontEntry
369 gfxFontEntry* gfxDWriteFontEntry::Clone() const {
370 MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
371 gfxDWriteFontEntry* fe = new gfxDWriteFontEntry(Name(), mFont);
372 fe->mWeightRange = mWeightRange;
373 fe->mStretchRange = mStretchRange;
374 fe->mStyleRange = mStyleRange;
375 return fe;
378 gfxDWriteFontEntry::~gfxDWriteFontEntry() {}
380 static bool UsingArabicOrHebrewScriptSystemLocale() {
381 LANGID langid = PRIMARYLANGID(::GetSystemDefaultLangID());
382 switch (langid) {
383 case LANG_ARABIC:
384 case LANG_DARI:
385 case LANG_PASHTO:
386 case LANG_PERSIAN:
387 case LANG_SINDHI:
388 case LANG_UIGHUR:
389 case LANG_URDU:
390 case LANG_HEBREW:
391 return true;
392 default:
393 return false;
397 nsresult gfxDWriteFontEntry::CopyFontTable(uint32_t aTableTag,
398 nsTArray<uint8_t>& aBuffer) {
399 gfxDWriteFontList* pFontList = gfxDWriteFontList::PlatformFontList();
400 const uint32_t tagBE = NativeEndian::swapToBigEndian(aTableTag);
402 // Don't use GDI table loading for symbol fonts or for
403 // italic fonts in Arabic-script system locales because of
404 // potential cmap discrepancies, see bug 629386.
405 // Ditto for Hebrew, bug 837498.
406 if (mFont && mMayUseGDIAccess && pFontList->UseGDIFontTableAccess() &&
407 !(!IsUpright() && UsingArabicOrHebrewScriptSystemLocale()) &&
408 !mFont->IsSymbolFont()) {
409 LOGFONTW logfont = {0};
410 if (InitLogFont(mFont, &logfont)) {
411 AutoDC dc;
412 AutoSelectFont font(dc.GetDC(), &logfont);
413 if (font.IsValid()) {
414 uint32_t tableSize = ::GetFontData(dc.GetDC(), tagBE, 0, nullptr, 0);
415 if (tableSize != GDI_ERROR) {
416 if (aBuffer.SetLength(tableSize, fallible)) {
417 ::GetFontData(dc.GetDC(), tagBE, 0, aBuffer.Elements(),
418 aBuffer.Length());
419 return NS_OK;
421 return NS_ERROR_OUT_OF_MEMORY;
427 RefPtr<IDWriteFontFace> fontFace;
428 nsresult rv = CreateFontFace(getter_AddRefs(fontFace));
429 if (NS_FAILED(rv)) {
430 return rv;
433 uint8_t* tableData;
434 uint32_t len;
435 void* tableContext = nullptr;
436 BOOL exists;
437 HRESULT hr = fontFace->TryGetFontTable(tagBE, (const void**)&tableData, &len,
438 &tableContext, &exists);
439 if (FAILED(hr) || !exists) {
440 return NS_ERROR_FAILURE;
443 if (aBuffer.SetLength(len, fallible)) {
444 memcpy(aBuffer.Elements(), tableData, len);
445 rv = NS_OK;
446 } else {
447 rv = NS_ERROR_OUT_OF_MEMORY;
450 if (tableContext) {
451 fontFace->ReleaseFontTable(&tableContext);
454 return rv;
457 // Access to font tables packaged in hb_blob_t form
459 // object attached to the Harfbuzz blob, used to release
460 // the table when the blob is destroyed
461 class FontTableRec {
462 public:
463 FontTableRec(IDWriteFontFace* aFontFace, void* aContext)
464 : mFontFace(aFontFace), mContext(aContext) {
465 MOZ_COUNT_CTOR(FontTableRec);
468 ~FontTableRec() {
469 MOZ_COUNT_DTOR(FontTableRec);
470 mFontFace->ReleaseFontTable(mContext);
473 private:
474 RefPtr<IDWriteFontFace> mFontFace;
475 void* mContext;
478 static void DestroyBlobFunc(void* aUserData) {
479 FontTableRec* ftr = static_cast<FontTableRec*>(aUserData);
480 delete ftr;
483 hb_blob_t* gfxDWriteFontEntry::GetFontTable(uint32_t aTag) {
484 // try to avoid potentially expensive DWrite call if we haven't actually
485 // created the font face yet, by using the gfxFontEntry method that will
486 // use CopyFontTable and then cache the data
487 if (!mFontFace) {
488 return gfxFontEntry::GetFontTable(aTag);
491 const void* data;
492 UINT32 size;
493 void* context;
494 BOOL exists;
495 HRESULT hr = mFontFace->TryGetFontTable(NativeEndian::swapToBigEndian(aTag),
496 &data, &size, &context, &exists);
497 if (SUCCEEDED(hr) && exists) {
498 FontTableRec* ftr = new FontTableRec(mFontFace, context);
499 return hb_blob_create(static_cast<const char*>(data), size,
500 HB_MEMORY_MODE_READONLY, ftr, DestroyBlobFunc);
503 return nullptr;
506 nsresult gfxDWriteFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
507 AUTO_PROFILER_LABEL("gfxDWriteFontEntry::ReadCMAP", GRAPHICS);
509 // attempt this once, if errors occur leave a blank cmap
510 if (mCharacterMap || mShmemCharacterMap) {
511 return NS_OK;
514 RefPtr<gfxCharacterMap> charmap;
515 nsresult rv;
517 if (aFontInfoData &&
518 (charmap = GetCMAPFromFontInfo(aFontInfoData, mUVSOffset))) {
519 rv = NS_OK;
520 } else {
521 uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
522 charmap = new gfxCharacterMap();
523 AutoTable cmapTable(this, kCMAP);
525 if (cmapTable) {
526 uint32_t cmapLen;
527 const uint8_t* cmapData = reinterpret_cast<const uint8_t*>(
528 hb_blob_get_data(cmapTable, &cmapLen));
529 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, mUVSOffset);
530 } else {
531 rv = NS_ERROR_NOT_AVAILABLE;
535 mHasCmapTable = NS_SUCCEEDED(rv);
536 if (mHasCmapTable) {
537 // Bug 969504: exclude U+25B6 from Segoe UI family, because it's used
538 // by sites to represent a "Play" icon, but the glyph in Segoe UI Light
539 // and Semibold on Windows 7 is too thin. (Ditto for leftward U+25C0.)
540 // Fallback to Segoe UI Symbol is preferred.
541 if (FamilyName().EqualsLiteral("Segoe UI")) {
542 charmap->clear(0x25b6);
543 charmap->clear(0x25c0);
545 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
546 fontlist::FontList* sharedFontList = pfl->SharedFontList();
547 if (!IsUserFont() && mShmemFace) {
548 mShmemFace->SetCharacterMap(sharedFontList, charmap); // async
549 if (!TrySetShmemCharacterMap()) {
550 // Temporarily retain charmap, until the shared version is
551 // ready for use.
552 mCharacterMap = charmap;
554 } else {
555 mCharacterMap = pfl->FindCharMap(charmap);
557 } else {
558 // if error occurred, initialize to null cmap
559 mCharacterMap = new gfxCharacterMap();
562 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
563 mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
564 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
565 if (LOG_CMAPDATA_ENABLED()) {
566 char prefix[256];
567 SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
568 charmap->Dump(prefix, eGfxLog_cmapdata);
571 return rv;
574 bool gfxDWriteFontEntry::HasVariations() {
575 if (mHasVariationsInitialized) {
576 return mHasVariations;
578 mHasVariationsInitialized = true;
579 mHasVariations = false;
581 if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
582 return mHasVariations;
585 if (!mFontFace) {
586 // CreateFontFace will initialize the mFontFace field, and also
587 // mFontFace5 if available on the current DWrite version.
588 RefPtr<IDWriteFontFace> fontFace;
589 if (NS_FAILED(CreateFontFace(getter_AddRefs(fontFace)))) {
590 return mHasVariations;
593 if (mFontFace5) {
594 mHasVariations = mFontFace5->HasVariations();
596 return mHasVariations;
599 void gfxDWriteFontEntry::GetVariationAxes(
600 nsTArray<gfxFontVariationAxis>& aAxes) {
601 if (!HasVariations()) {
602 return;
604 // HasVariations() will have ensured the mFontFace5 interface is available;
605 // so we can get an IDWriteFontResource and ask it for the axis info.
606 RefPtr<IDWriteFontResource> resource;
607 HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
608 if (FAILED(hr) || !resource) {
609 return;
612 uint32_t count = resource->GetFontAxisCount();
613 AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> defaultValues;
614 AutoTArray<DWRITE_FONT_AXIS_RANGE, 4> ranges;
615 defaultValues.SetLength(count);
616 ranges.SetLength(count);
617 resource->GetDefaultFontAxisValues(defaultValues.Elements(), count);
618 resource->GetFontAxisRanges(ranges.Elements(), count);
619 for (uint32_t i = 0; i < count; ++i) {
620 gfxFontVariationAxis axis;
621 MOZ_ASSERT(ranges[i].axisTag == defaultValues[i].axisTag);
622 DWRITE_FONT_AXIS_ATTRIBUTES attrs = resource->GetFontAxisAttributes(i);
623 if (attrs & DWRITE_FONT_AXIS_ATTRIBUTES_HIDDEN) {
624 continue;
626 if (!(attrs & DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE)) {
627 continue;
629 // Extract the 4 chars of the tag from DWrite's packed version,
630 // and reassemble them in the order we use for TRUETYPE_TAG.
631 uint32_t t = defaultValues[i].axisTag;
632 axis.mTag = TRUETYPE_TAG(t & 0xff, (t >> 8) & 0xff, (t >> 16) & 0xff,
633 (t >> 24) & 0xff);
634 // Try to get a human-friendly name (may not be present)
635 RefPtr<IDWriteLocalizedStrings> names;
636 resource->GetAxisNames(i, getter_AddRefs(names));
637 if (names) {
638 GetEnglishOrFirstName(axis.mName, names);
640 axis.mMinValue = ranges[i].minValue;
641 axis.mMaxValue = ranges[i].maxValue;
642 axis.mDefaultValue = defaultValues[i].value;
643 aAxes.AppendElement(axis);
647 void gfxDWriteFontEntry::GetVariationInstances(
648 nsTArray<gfxFontVariationInstance>& aInstances) {
649 gfxFontUtils::GetVariationData(this, nullptr, &aInstances);
652 gfxFont* gfxDWriteFontEntry::CreateFontInstance(
653 const gfxFontStyle* aFontStyle) {
654 bool needsBold = aFontStyle->NeedsSyntheticBold(this);
655 DWRITE_FONT_SIMULATIONS sims =
656 needsBold ? DWRITE_FONT_SIMULATIONS_BOLD : DWRITE_FONT_SIMULATIONS_NONE;
657 ThreadSafeWeakPtr<UnscaledFontDWrite>& unscaledFontPtr =
658 needsBold ? mUnscaledFontBold : mUnscaledFont;
659 RefPtr<UnscaledFontDWrite> unscaledFont(unscaledFontPtr);
660 if (!unscaledFont) {
661 RefPtr<IDWriteFontFace> fontFace;
662 nsresult rv =
663 CreateFontFace(getter_AddRefs(fontFace), nullptr, sims, nullptr);
664 if (NS_FAILED(rv)) {
665 return nullptr;
667 // Only pass in the underlying IDWriteFont if the unscaled font doesn't
668 // reflect a data font. This signals whether or not we can safely query
669 // a descriptor to represent the font for various transport use-cases.
670 unscaledFont =
671 new UnscaledFontDWrite(fontFace, !mIsDataUserFont ? mFont : nullptr);
672 unscaledFontPtr = unscaledFont;
674 RefPtr<IDWriteFontFace> fontFace;
675 if (HasVariations()) {
676 // Get the variation settings needed to instantiate the fontEntry
677 // for a particular fontStyle.
678 AutoTArray<gfxFontVariation, 4> vars;
679 GetVariationsForStyle(vars, *aFontStyle);
681 if (!vars.IsEmpty()) {
682 nsresult rv =
683 CreateFontFace(getter_AddRefs(fontFace), aFontStyle, sims, &vars);
684 if (NS_FAILED(rv)) {
685 return nullptr;
689 return new gfxDWriteFont(unscaledFont, this, aFontStyle, fontFace);
692 nsresult gfxDWriteFontEntry::CreateFontFace(
693 IDWriteFontFace** aFontFace, const gfxFontStyle* aFontStyle,
694 DWRITE_FONT_SIMULATIONS aSimulations,
695 const nsTArray<gfxFontVariation>* aVariations) {
696 // Convert an OpenType font tag from our uint32_t representation
697 // (as constructed by TRUETYPE_TAG(...)) to the order DWrite wants.
698 auto makeDWriteAxisTag = [](uint32_t aTag) {
699 return DWRITE_MAKE_FONT_AXIS_TAG((aTag >> 24) & 0xff, (aTag >> 16) & 0xff,
700 (aTag >> 8) & 0xff, aTag & 0xff);
703 // initialize mFontFace if this hasn't been done before
704 if (!mFontFace) {
705 HRESULT hr;
706 if (mFont) {
707 hr = mFont->CreateFontFace(getter_AddRefs(mFontFace));
708 } else if (mFontFile) {
709 IDWriteFontFile* fontFile = mFontFile.get();
710 hr = Factory::GetDWriteFactory()->CreateFontFace(
711 mFaceType, 1, &fontFile, 0, DWRITE_FONT_SIMULATIONS_NONE,
712 getter_AddRefs(mFontFace));
713 } else {
714 MOZ_ASSERT_UNREACHABLE("invalid font entry");
715 return NS_ERROR_FAILURE;
717 if (FAILED(hr)) {
718 return NS_ERROR_FAILURE;
720 // Also get the IDWriteFontFace5 interface if we're running on a
721 // sufficiently new DWrite version where it is available.
722 if (mFontFace) {
723 mFontFace->QueryInterface(__uuidof(IDWriteFontFace5),
724 (void**)getter_AddRefs(mFontFace5));
725 if (!mVariationSettings.IsEmpty()) {
726 // If the font entry has variations specified, mFontFace5 will
727 // be a distinct face that has the variations applied.
728 RefPtr<IDWriteFontResource> resource;
729 HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
730 if (SUCCEEDED(hr) && resource) {
731 AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues;
732 for (const auto& v : mVariationSettings) {
733 DWRITE_FONT_AXIS_VALUE axisValue = {makeDWriteAxisTag(v.mTag),
734 v.mValue};
735 fontAxisValues.AppendElement(axisValue);
737 resource->CreateFontFace(
738 mFontFace->GetSimulations(), fontAxisValues.Elements(),
739 fontAxisValues.Length(), getter_AddRefs(mFontFace5));
745 // Do we need to modify DWrite simulations from what mFontFace has?
746 bool needSimulations =
747 (aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) &&
748 !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
750 // If the IDWriteFontFace5 interface is available, we can try using
751 // IDWriteFontResource to create a new modified face.
752 if (mFontFace5 && (HasVariations() || needSimulations)) {
753 RefPtr<IDWriteFontResource> resource;
754 HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
755 if (SUCCEEDED(hr) && resource) {
756 AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues;
758 // Copy variation settings to DWrite's type.
759 if (aVariations) {
760 for (const auto& v : *aVariations) {
761 DWRITE_FONT_AXIS_VALUE axisValue = {makeDWriteAxisTag(v.mTag),
762 v.mValue};
763 fontAxisValues.AppendElement(axisValue);
767 IDWriteFontFace5* ff5;
768 resource->CreateFontFace(aSimulations, fontAxisValues.Elements(),
769 fontAxisValues.Length(), &ff5);
770 if (ff5) {
771 *aFontFace = ff5;
772 return NS_OK;
777 // Do we need to add DWrite simulations to the face?
778 if (needSimulations) {
779 // if so, we need to return not mFontFace itself but a version that
780 // has the Bold simulation - unfortunately, old DWrite doesn't provide
781 // a simple API for this
782 UINT32 numberOfFiles = 0;
783 if (FAILED(mFontFace->GetFiles(&numberOfFiles, nullptr))) {
784 return NS_ERROR_FAILURE;
786 AutoTArray<IDWriteFontFile*, 1> files;
787 files.AppendElements(numberOfFiles);
788 if (FAILED(mFontFace->GetFiles(&numberOfFiles, files.Elements()))) {
789 return NS_ERROR_FAILURE;
791 HRESULT hr = Factory::GetDWriteFactory()->CreateFontFace(
792 mFontFace->GetType(), numberOfFiles, files.Elements(),
793 mFontFace->GetIndex(), aSimulations, aFontFace);
794 for (UINT32 i = 0; i < numberOfFiles; ++i) {
795 files[i]->Release();
797 return FAILED(hr) ? NS_ERROR_FAILURE : NS_OK;
800 // no simulation: we can just add a reference to mFontFace5 (if present)
801 // or mFontFace (otherwise) and return that
802 if (mFontFace5) {
803 *aFontFace = mFontFace5;
804 } else {
805 *aFontFace = mFontFace;
807 (*aFontFace)->AddRef();
808 return NS_OK;
811 bool gfxDWriteFontEntry::InitLogFont(IDWriteFont* aFont, LOGFONTW* aLogFont) {
812 HRESULT hr;
814 BOOL isInSystemCollection;
815 IDWriteGdiInterop* gdi =
816 gfxDWriteFontList::PlatformFontList()->GetGDIInterop();
817 hr = gdi->ConvertFontToLOGFONT(aFont, aLogFont, &isInSystemCollection);
818 // If the font is not in the system collection, GDI will be unable to
819 // select it and load its tables, so we return false here to indicate
820 // failure, and let CopyFontTable fall back to DWrite native methods.
821 return (SUCCEEDED(hr) && isInSystemCollection);
824 bool gfxDWriteFontEntry::IsCJKFont() {
825 if (mIsCJK != UNINITIALIZED_VALUE) {
826 return mIsCJK;
829 mIsCJK = false;
831 const uint32_t kOS2Tag = TRUETYPE_TAG('O', 'S', '/', '2');
832 hb_blob_t* blob = GetFontTable(kOS2Tag);
833 if (!blob) {
834 return mIsCJK;
836 // |blob| is an owning reference, but is not RAII-managed, so it must be
837 // explicitly freed using |hb_blob_destroy| before we return. (Beware of
838 // adding any early-return codepaths!)
840 uint32_t len;
841 const OS2Table* os2 =
842 reinterpret_cast<const OS2Table*>(hb_blob_get_data(blob, &len));
843 // ulCodePageRange bit definitions for the CJK codepages,
844 // from http://www.microsoft.com/typography/otspec/os2.htm#cpr
845 const uint32_t CJK_CODEPAGE_BITS =
846 (1 << 17) | // codepage 932 - JIS/Japan
847 (1 << 18) | // codepage 936 - Chinese (simplified)
848 (1 << 19) | // codepage 949 - Korean Wansung
849 (1 << 20) | // codepage 950 - Chinese (traditional)
850 (1 << 21); // codepage 1361 - Korean Johab
851 if (len >= offsetof(OS2Table, sxHeight)) {
852 if ((uint32_t(os2->codePageRange1) & CJK_CODEPAGE_BITS) != 0) {
853 mIsCJK = true;
856 hb_blob_destroy(blob);
858 return mIsCJK;
861 void gfxDWriteFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
862 FontListSizes* aSizes) const {
863 gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
864 // TODO:
865 // This doesn't currently account for the |mFont| and |mFontFile| members
868 void gfxDWriteFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
869 FontListSizes* aSizes) const {
870 aSizes->mFontListSize += aMallocSizeOf(this);
871 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
874 ////////////////////////////////////////////////////////////////////////////////
875 // gfxDWriteFontList
877 gfxDWriteFontList::gfxDWriteFontList() : mForceGDIClassicMaxFontSize(0.0) {
878 CheckFamilyList(kBaseFonts);
879 CheckFamilyList(kLangPackFonts);
882 // bug 602792 - CJK systems default to large CJK fonts which cause excessive
883 // I/O strain during cold startup due to dwrite caching bugs. Default to
884 // Arial to avoid this.
886 FontFamily gfxDWriteFontList::GetDefaultFontForPlatform(
887 nsPresContext* aPresContext, const gfxFontStyle* aStyle,
888 nsAtom* aLanguage) {
889 // try Arial first
890 FontFamily ff;
891 ff = FindFamily(aPresContext, "Arial"_ns);
892 if (!ff.IsNull()) {
893 return ff;
896 // otherwise, use local default
897 NONCLIENTMETRICSW ncm;
898 ncm.cbSize = sizeof(ncm);
899 BOOL status =
900 ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
902 if (status) {
903 ff = FindFamily(aPresContext,
904 NS_ConvertUTF16toUTF8(ncm.lfMessageFont.lfFaceName));
907 return ff;
910 gfxFontEntry* gfxDWriteFontList::LookupLocalFont(
911 nsPresContext* aPresContext, const nsACString& aFontName,
912 WeightRange aWeightForEntry, StretchRange aStretchForEntry,
913 SlantStyleRange aStyleForEntry) {
914 if (SharedFontList()) {
915 return LookupInSharedFaceNameList(aPresContext, aFontName, aWeightForEntry,
916 aStretchForEntry, aStyleForEntry);
919 gfxFontEntry* lookup;
921 lookup = LookupInFaceNameLists(aFontName);
922 if (!lookup) {
923 return nullptr;
926 gfxDWriteFontEntry* dwriteLookup = static_cast<gfxDWriteFontEntry*>(lookup);
927 gfxDWriteFontEntry* fe =
928 new gfxDWriteFontEntry(lookup->Name(), dwriteLookup->mFont,
929 aWeightForEntry, aStretchForEntry, aStyleForEntry);
930 fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic());
931 return fe;
934 gfxFontEntry* gfxDWriteFontList::MakePlatformFont(
935 const nsACString& aFontName, WeightRange aWeightForEntry,
936 StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry,
937 const uint8_t* aFontData, uint32_t aLength) {
938 RefPtr<IDWriteFontFileStream> fontFileStream;
939 RefPtr<IDWriteFontFile> fontFile;
940 HRESULT hr = gfxDWriteFontFileLoader::CreateCustomFontFile(
941 aFontData, aLength, getter_AddRefs(fontFile),
942 getter_AddRefs(fontFileStream));
943 free((void*)aFontData);
944 NS_ASSERTION(SUCCEEDED(hr), "Failed to create font file reference");
945 if (FAILED(hr)) {
946 return nullptr;
949 nsAutoString uniqueName;
950 nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
951 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to make unique user font name");
952 if (NS_FAILED(rv)) {
953 return nullptr;
956 BOOL isSupported;
957 DWRITE_FONT_FILE_TYPE fileType;
958 UINT32 numFaces;
960 auto entry = MakeUnique<gfxDWriteFontEntry>(
961 NS_ConvertUTF16toUTF8(uniqueName), fontFile, fontFileStream,
962 aWeightForEntry, aStretchForEntry, aStyleForEntry);
964 hr = fontFile->Analyze(&isSupported, &fileType, &entry->mFaceType, &numFaces);
965 NS_ASSERTION(SUCCEEDED(hr), "IDWriteFontFile::Analyze failed");
966 if (FAILED(hr)) {
967 return nullptr;
969 NS_ASSERTION(isSupported, "Unsupported font file");
970 if (!isSupported) {
971 return nullptr;
973 NS_ASSERTION(numFaces == 1, "Font file does not contain exactly 1 face");
974 if (numFaces != 1) {
975 // We don't know how to deal with 0 faces either.
976 return nullptr;
979 return entry.release();
982 bool gfxDWriteFontList::UseGDIFontTableAccess() const {
983 // Using GDI font table access for DWrite is controlled by a pref, but also we
984 // must be able to make win32k calls.
985 return mGDIFontTableAccess && !IsWin32kLockedDown();
988 static void GetPostScriptNameFromNameTable(IDWriteFontFace* aFace,
989 nsCString& aName) {
990 const auto kNAME =
991 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
992 const char* data;
993 UINT32 size;
994 void* context;
995 BOOL exists;
996 if (SUCCEEDED(aFace->TryGetFontTable(kNAME, (const void**)&data, &size,
997 &context, &exists)) &&
998 exists) {
999 if (NS_FAILED(gfxFontUtils::ReadCanonicalName(
1000 data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, aName))) {
1001 aName.Truncate(0);
1003 aFace->ReleaseFontTable(context);
1007 gfxFontEntry* gfxDWriteFontList::CreateFontEntry(
1008 fontlist::Face* aFace, const fontlist::Family* aFamily) {
1009 IDWriteFontCollection* collection =
1010 #ifdef MOZ_BUNDLED_FONTS
1011 aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
1012 #else
1013 mSystemFonts;
1014 #endif
1015 RefPtr<IDWriteFontFamily> family;
1016 bool foundExpectedFamily = false;
1017 const nsCString& familyName =
1018 aFamily->DisplayName().AsString(SharedFontList());
1019 if (aFamily->Index() < collection->GetFontFamilyCount()) {
1020 HRESULT hr =
1021 collection->GetFontFamily(aFamily->Index(), getter_AddRefs(family));
1022 // Check that the family name is what we expected; if not, fall back to
1023 // search by name. It's sad we have to do this, but it is possible for
1024 // Windows to have given different versions of the system font collection
1025 // to the parent and child processes.
1026 if (SUCCEEDED(hr) && family) {
1027 RefPtr<IDWriteLocalizedStrings> names;
1028 hr = family->GetFamilyNames(getter_AddRefs(names));
1029 if (SUCCEEDED(hr) && names) {
1030 nsAutoCString name;
1031 if (GetEnglishOrFirstName(name, names)) {
1032 foundExpectedFamily = name.Equals(familyName);
1037 if (!foundExpectedFamily) {
1038 // Try to get family by name instead of index (to deal with the case of
1039 // collection mismatch).
1040 UINT32 index;
1041 BOOL exists;
1042 NS_ConvertUTF8toUTF16 name16(familyName);
1043 HRESULT hr = collection->FindFamilyName(
1044 reinterpret_cast<const WCHAR*>(name16.BeginReading()), &index, &exists);
1045 if (FAILED(hr) || !exists || index == UINT_MAX ||
1046 FAILED(collection->GetFontFamily(index, getter_AddRefs(family))) ||
1047 !family) {
1048 return nullptr;
1052 // Retrieve the required face by index within the family.
1053 RefPtr<IDWriteFont> font;
1054 if (FAILED(family->GetFont(aFace->mIndex, getter_AddRefs(font))) || !font) {
1055 return nullptr;
1058 // Retrieve the psName from the font, so we can check we've found the
1059 // expected face.
1060 nsAutoCString psName;
1061 if (FAILED(GetDirectWriteFaceName(font, PSNAME_ID, psName))) {
1062 RefPtr<IDWriteFontFace> dwFontFace;
1063 if (SUCCEEDED(font->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1064 GetPostScriptNameFromNameTable(dwFontFace, psName);
1068 // If it doesn't match, DirectWrite must have shuffled the order of faces
1069 // returned for the family; search by name as a fallback.
1070 nsCString faceName = aFace->mDescriptor.AsString(SharedFontList());
1071 if (psName != faceName) {
1072 gfxWarning() << "Face name mismatch for index " << aFace->mIndex
1073 << " in family " << familyName.get() << ": expected "
1074 << faceName.get() << ", found " << psName.get();
1075 for (uint32_t i = 0; i < family->GetFontCount(); ++i) {
1076 if (i == aFace->mIndex) {
1077 continue; // this was the face we already tried
1079 if (FAILED(family->GetFont(i, getter_AddRefs(font))) || !font) {
1080 return nullptr; // this font family is broken!
1082 if (FAILED(GetDirectWriteFaceName(font, PSNAME_ID, psName))) {
1083 RefPtr<IDWriteFontFace> dwFontFace;
1084 if (SUCCEEDED(font->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1085 GetPostScriptNameFromNameTable(dwFontFace, psName);
1088 if (psName == faceName) {
1089 break;
1093 if (psName != faceName) {
1094 return nullptr;
1097 auto fe = new gfxDWriteFontEntry(faceName, font, !aFamily->IsBundled());
1098 fe->InitializeFrom(aFace, aFamily);
1099 fe->mForceGDIClassic = aFamily->IsForceClassic();
1100 fe->mMayUseGDIAccess = aFamily->IsSimple();
1101 return fe;
1104 FontVisibility gfxDWriteFontList::GetVisibilityForFamily(
1105 const nsACString& aName) const {
1106 if (FamilyInList(aName, kBaseFonts)) {
1107 return FontVisibility::Base;
1109 if (FamilyInList(aName, kLangPackFonts)) {
1110 return FontVisibility::LangPack;
1112 return FontVisibility::User;
1115 void gfxDWriteFontList::AppendFamiliesFromCollection(
1116 IDWriteFontCollection* aCollection,
1117 nsTArray<fontlist::Family::InitData>& aFamilies,
1118 const nsTArray<nsCString>* aForceClassicFams) {
1119 auto allFacesUltraBold = [](IDWriteFontFamily* aFamily) -> bool {
1120 for (UINT32 i = 0; i < aFamily->GetFontCount(); i++) {
1121 RefPtr<IDWriteFont> font;
1122 HRESULT hr = aFamily->GetFont(i, getter_AddRefs(font));
1123 if (FAILED(hr)) {
1124 NS_WARNING("Failed to get existing font from family.");
1125 continue;
1127 nsAutoCString faceName;
1128 hr = GetDirectWriteFontName(font, faceName);
1129 if (FAILED(hr)) {
1130 continue;
1132 if (faceName.Find("Ultra Bold"_ns) == kNotFound) {
1133 return false;
1136 return true;
1139 nsAutoCString locale;
1140 OSPreferences::GetInstance()->GetSystemLocale(locale);
1141 ToLowerCase(locale);
1142 NS_ConvertUTF8toUTF16 loc16(locale);
1144 for (unsigned i = 0; i < aCollection->GetFontFamilyCount(); ++i) {
1145 RefPtr<IDWriteFontFamily> family;
1146 aCollection->GetFontFamily(i, getter_AddRefs(family));
1147 RefPtr<IDWriteLocalizedStrings> localizedNames;
1148 HRESULT hr = family->GetFamilyNames(getter_AddRefs(localizedNames));
1149 if (FAILED(hr)) {
1150 gfxWarning() << "Failed to get names for font-family " << i;
1151 continue;
1154 auto addFamily = [&](const nsACString& name, bool altLocale = false) {
1155 nsAutoCString key;
1156 key = name;
1157 BuildKeyNameFromFontName(key);
1158 bool bad = mBadUnderlineFamilyNames.ContainsSorted(key);
1159 bool classic =
1160 aForceClassicFams && aForceClassicFams->ContainsSorted(key);
1161 FontVisibility visibility;
1162 // Special case: hide the "Gill Sans" family that contains only UltraBold
1163 // faces, as this leads to breakage on sites with CSS that targeted the
1164 // Gill Sans family as found on macOS. (Bug 551313, bug 1632738)
1165 // TODO (jfkthame): the ultrabold faces from Gill Sans should be treated
1166 // as belonging to the Gill Sans MT family.
1167 if (key.EqualsLiteral("gill sans") && allFacesUltraBold(family)) {
1168 visibility = FontVisibility::Hidden;
1169 } else {
1170 visibility = aCollection == mSystemFonts ? GetVisibilityForFamily(name)
1171 : FontVisibility::Base;
1173 aFamilies.AppendElement(fontlist::Family::InitData(
1174 key, name, i, visibility, aCollection != mSystemFonts, bad, classic,
1175 altLocale));
1178 unsigned count = localizedNames->GetCount();
1179 if (count == 1) {
1180 // This is the common case: the great majority of fonts only provide an
1181 // en-US family name.
1182 nsAutoCString name;
1183 if (!GetNameAsUtf8(name, localizedNames, 0)) {
1184 gfxWarning() << "GetNameAsUtf8 failed for index 0 in font-family " << i;
1185 continue;
1187 addFamily(name);
1188 } else {
1189 AutoTArray<nsCString, 4> names;
1190 int sysLocIndex = -1;
1191 for (unsigned index = 0; index < count; ++index) {
1192 nsAutoCString name;
1193 if (!GetNameAsUtf8(name, localizedNames, index)) {
1194 gfxWarning() << "GetNameAsUtf8 failed for index " << index
1195 << " in font-family " << i;
1196 continue;
1198 if (!names.Contains(name)) {
1199 if (sysLocIndex == -1) {
1200 WCHAR buf[32];
1201 if (SUCCEEDED(localizedNames->GetLocaleName(index, buf, 32))) {
1202 if (loc16.Equals(buf)) {
1203 sysLocIndex = names.Length();
1207 names.AppendElement(name);
1210 // If we didn't find a name that matched the system locale, use the
1211 // first (which is most often en-US).
1212 if (sysLocIndex == -1) {
1213 sysLocIndex = 0;
1215 // Hack to work around EPSON fonts with bad names (tagged as MacRoman
1216 // but actually contain MacJapanese data): if we've chosen the first
1217 // name, *and* it is non-ASCII, *and* there is an alternative present,
1218 // use the next option instead as being more likely to be valid.
1219 if (sysLocIndex == 0 && names.Length() > 1 && !IsAscii(names[0])) {
1220 sysLocIndex = 1;
1222 for (unsigned index = 0; index < names.Length(); ++index) {
1223 addFamily(names[index], index != sysLocIndex);
1229 void gfxDWriteFontList::GetFacesInitDataForFamily(
1230 const fontlist::Family* aFamily, nsTArray<fontlist::Face::InitData>& aFaces,
1231 bool aLoadCmaps) const {
1232 IDWriteFontCollection* collection =
1233 #ifdef MOZ_BUNDLED_FONTS
1234 aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
1235 #else
1236 mSystemFonts;
1237 #endif
1238 if (!collection) {
1239 return;
1241 RefPtr<IDWriteFontFamily> family;
1242 collection->GetFontFamily(aFamily->Index(), getter_AddRefs(family));
1243 for (unsigned i = 0; i < family->GetFontCount(); ++i) {
1244 RefPtr<IDWriteFont> dwFont;
1245 family->GetFont(i, getter_AddRefs(dwFont));
1246 if (!dwFont || dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
1247 continue;
1249 DWRITE_FONT_STYLE dwstyle = dwFont->GetStyle();
1250 // Ignore italic styles of Meiryo because "Meiryo (Bold) Italic" has
1251 // non-italic style glyphs as Japanese characters. However, using it
1252 // causes serious problem if web pages wants some elements to be
1253 // different style from others only with font-style. For example,
1254 // <em> and <i> should be rendered as italic in the default style.
1255 if (dwstyle != DWRITE_FONT_STYLE_NORMAL &&
1256 aFamily->Key().AsString(SharedFontList()).EqualsLiteral("meiryo")) {
1257 continue;
1259 WeightRange weight(FontWeight(dwFont->GetWeight()));
1260 StretchRange stretch(FontStretchFromDWriteStretch(dwFont->GetStretch()));
1261 // Try to read PSName as a unique face identifier; if this fails we'll get
1262 // it directly from the 'name' table, and if that also fails we consider
1263 // the face unusable.
1264 MOZ_SEH_TRY {
1265 nsAutoCString name;
1266 RefPtr<gfxCharacterMap> charmap;
1267 if (FAILED(GetDirectWriteFaceName(dwFont, PSNAME_ID, name)) ||
1268 aLoadCmaps) {
1269 RefPtr<IDWriteFontFace> dwFontFace;
1270 if (SUCCEEDED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1271 if (name.IsEmpty()) {
1272 GetPostScriptNameFromNameTable(dwFontFace, name);
1274 const auto kCMAP =
1275 NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
1276 const char* data;
1277 UINT32 size;
1278 void* context;
1279 BOOL exists;
1280 if (aLoadCmaps) {
1281 if (SUCCEEDED(dwFontFace->TryGetFontTable(
1282 kCMAP, (const void**)&data, &size, &context, &exists)) &&
1283 exists) {
1284 charmap = new gfxCharacterMap();
1285 uint32_t offset;
1286 gfxFontUtils::ReadCMAP((const uint8_t*)data, size, *charmap,
1287 offset);
1288 dwFontFace->ReleaseFontTable(context);
1293 if (name.IsEmpty()) {
1294 gfxWarning() << "Failed to get name for face " << i << " in family "
1295 << aFamily->Key().AsString(SharedFontList()).get();
1296 continue;
1298 SlantStyleRange slant(
1299 dwstyle == DWRITE_FONT_STYLE_NORMAL ? FontSlantStyle::Normal()
1300 : dwstyle == DWRITE_FONT_STYLE_ITALIC ? FontSlantStyle::Italic()
1301 : FontSlantStyle::Oblique());
1302 aFaces.AppendElement(fontlist::Face::InitData{
1303 name, uint16_t(i), false, weight, stretch, slant, charmap});
1305 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1306 // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
1307 // the font resource. We'll just skip the bad face.
1308 gfxCriticalNote << "Exception occurred reading faces for "
1309 << aFamily->Key().AsString(SharedFontList()).get();
1314 bool gfxDWriteFontList::ReadFaceNames(fontlist::Family* aFamily,
1315 fontlist::Face* aFace, nsCString& aPSName,
1316 nsCString& aFullName) {
1317 IDWriteFontCollection* collection =
1318 #ifdef MOZ_BUNDLED_FONTS
1319 aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
1320 #else
1321 mSystemFonts;
1322 #endif
1323 RefPtr<IDWriteFontFamily> family;
1324 if (FAILED(collection->GetFontFamily(aFamily->Index(),
1325 getter_AddRefs(family)))) {
1326 MOZ_ASSERT_UNREACHABLE("failed to get font-family");
1327 return false;
1329 RefPtr<IDWriteFont> dwFont;
1330 if (FAILED(family->GetFont(aFace->mIndex, getter_AddRefs(dwFont)))) {
1331 MOZ_ASSERT_UNREACHABLE("failed to get font from family");
1332 return false;
1334 HRESULT ps = GetDirectWriteFaceName(dwFont, PSNAME_ID, aPSName);
1335 HRESULT full = GetDirectWriteFaceName(dwFont, FULLNAME_ID, aFullName);
1336 if (FAILED(ps) || FAILED(full) || aPSName.IsEmpty() || aFullName.IsEmpty()) {
1337 // We'll return true if either name was found, false if both fail.
1338 // Note that on older Win7 systems, GetDirectWriteFaceName may "succeed"
1339 // but return an empty string, so we have to check for non-empty strings
1340 // to be sure we actually got a usable name.
1342 // Initialize result to true if either name was already found.
1343 bool result = (SUCCEEDED(ps) && !aPSName.IsEmpty()) ||
1344 (SUCCEEDED(full) && !aFullName.IsEmpty());
1345 RefPtr<IDWriteFontFace> dwFontFace;
1346 if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1347 NS_WARNING("failed to create font face");
1348 return result;
1350 void* context;
1351 const char* data;
1352 UINT32 size;
1353 BOOL exists;
1354 if (FAILED(dwFontFace->TryGetFontTable(
1355 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')),
1356 (const void**)&data, &size, &context, &exists)) ||
1357 !exists) {
1358 NS_WARNING("failed to get name table");
1359 return result;
1361 MOZ_SEH_TRY {
1362 // Try to read the name table entries, and ensure result is true if either
1363 // one succeeds.
1364 if (FAILED(ps) || aPSName.IsEmpty()) {
1365 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1366 data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, aPSName))) {
1367 result = true;
1368 } else {
1369 NS_WARNING("failed to read psname");
1372 if (FAILED(full) || aFullName.IsEmpty()) {
1373 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1374 data, size, gfxFontUtils::NAME_ID_FULL, aFullName))) {
1375 result = true;
1376 } else {
1377 NS_WARNING("failed to read fullname");
1381 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1382 gfxCriticalNote << "Exception occurred reading face names for "
1383 << aFamily->Key().AsString(SharedFontList()).get();
1385 if (dwFontFace && context) {
1386 dwFontFace->ReleaseFontTable(context);
1388 return result;
1390 return true;
1393 void gfxDWriteFontList::ReadFaceNamesForFamily(
1394 fontlist::Family* aFamily, bool aNeedFullnamePostscriptNames) {
1395 if (!aFamily->IsInitialized()) {
1396 if (!InitializeFamily(aFamily)) {
1397 return;
1400 IDWriteFontCollection* collection =
1401 #ifdef MOZ_BUNDLED_FONTS
1402 aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
1403 #else
1404 mSystemFonts;
1405 #endif
1406 RefPtr<IDWriteFontFamily> family;
1407 if (FAILED(collection->GetFontFamily(aFamily->Index(),
1408 getter_AddRefs(family)))) {
1409 return;
1411 fontlist::FontList* list = SharedFontList();
1412 const fontlist::Pointer* facePtrs = aFamily->Faces(list);
1413 nsAutoCString familyName(aFamily->DisplayName().AsString(list));
1414 nsAutoCString key(aFamily->Key().AsString(list));
1416 // Read PS-names and fullnames of the faces, and any alternate family names
1417 // (either localizations or legacy subfamily names)
1418 for (unsigned i = 0; i < aFamily->NumFaces(); ++i) {
1419 auto face = static_cast<fontlist::Face*>(facePtrs[i].ToPtr(list));
1420 if (!face) {
1421 continue;
1423 RefPtr<IDWriteFont> dwFont;
1424 if (FAILED(family->GetFont(face->mIndex, getter_AddRefs(dwFont)))) {
1425 continue;
1427 RefPtr<IDWriteFontFace> dwFontFace;
1428 if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1429 continue;
1432 const char* data;
1433 UINT32 size;
1434 void* context;
1435 BOOL exists;
1436 if (FAILED(dwFontFace->TryGetFontTable(
1437 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')),
1438 (const void**)&data, &size, &context, &exists)) ||
1439 !exists) {
1440 continue;
1443 MOZ_SEH_TRY {
1444 AutoTArray<nsCString, 4> otherFamilyNames;
1445 gfxFontUtils::ReadOtherFamilyNamesForFace(familyName, data, size,
1446 otherFamilyNames, false);
1447 for (const auto& alias : otherFamilyNames) {
1448 nsAutoCString key(alias);
1449 ToLowerCase(key);
1450 auto aliasData = mAliasTable.GetOrInsertNew(key);
1451 aliasData->InitFromFamily(aFamily, familyName);
1452 aliasData->mFaces.AppendElement(facePtrs[i]);
1455 nsAutoCString psname, fullname;
1456 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1457 data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, psname))) {
1458 ToLowerCase(psname);
1459 mLocalNameTable.InsertOrUpdate(
1460 psname, fontlist::LocalFaceRec::InitData(key, i));
1462 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1463 data, size, gfxFontUtils::NAME_ID_FULL, fullname))) {
1464 ToLowerCase(fullname);
1465 if (fullname != psname) {
1466 mLocalNameTable.InsertOrUpdate(
1467 fullname, fontlist::LocalFaceRec::InitData(key, i));
1471 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1472 gfxCriticalNote << "Exception occurred reading names for "
1473 << familyName.get();
1476 dwFontFace->ReleaseFontTable(context);
1480 enum DWriteInitError {
1481 errGDIInterop = 1,
1482 errSystemFontCollection = 2,
1483 errNoFonts = 3
1486 void gfxDWriteFontList::InitSharedFontListForPlatform() {
1487 mGDIFontTableAccess = Preferences::GetBool(
1488 "gfx.font_rendering.directwrite.use_gdi_table_loading", false);
1489 mForceGDIClassicMaxFontSize = Preferences::GetInt(
1490 "gfx.font_rendering.cleartype_params.force_gdi_classic_max_size",
1491 mForceGDIClassicMaxFontSize);
1493 mSubstitutions.Clear();
1494 mNonExistingFonts.Clear();
1496 RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
1497 HRESULT hr = factory->GetGdiInterop(getter_AddRefs(mGDIInterop));
1498 if (FAILED(hr)) {
1499 Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1500 uint32_t(errGDIInterop));
1501 mSharedFontList.reset(nullptr);
1502 return;
1505 mSystemFonts = Factory::GetDWriteSystemFonts(true);
1506 NS_ASSERTION(mSystemFonts != nullptr, "GetSystemFontCollection failed!");
1507 if (!mSystemFonts) {
1508 Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1509 uint32_t(errSystemFontCollection));
1510 mSharedFontList.reset(nullptr);
1511 return;
1513 #ifdef MOZ_BUNDLED_FONTS
1514 // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an
1515 // explicit value of 0 (off) will disable them.
1516 TimeStamp start1 = TimeStamp::Now();
1517 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1518 mBundledFonts = CreateBundledFontsCollection(factory);
1520 TimeStamp end1 = TimeStamp::Now();
1521 #endif
1523 if (XRE_IsParentProcess()) {
1524 nsAutoCString classicFamilies;
1525 AutoTArray<nsCString, 16> forceClassicFams;
1526 nsresult rv = Preferences::GetCString(
1527 "gfx.font_rendering.cleartype_params.force_gdi_classic_for_families",
1528 classicFamilies);
1529 if (NS_SUCCEEDED(rv)) {
1530 for (auto name :
1531 nsCCharSeparatedTokenizer(classicFamilies, ',').ToRange()) {
1532 BuildKeyNameFromFontName(name);
1533 forceClassicFams.AppendElement(name);
1535 forceClassicFams.Sort();
1537 nsTArray<fontlist::Family::InitData> families;
1538 AppendFamiliesFromCollection(mSystemFonts, families, &forceClassicFams);
1539 #ifdef MOZ_BUNDLED_FONTS
1540 if (mBundledFonts) {
1541 TimeStamp start2 = TimeStamp::Now();
1542 AppendFamiliesFromCollection(mBundledFonts, families);
1543 TimeStamp end2 = TimeStamp::Now();
1544 Telemetry::Accumulate(
1545 Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
1546 (end1 - start1).ToMilliseconds() + (end2 - start2).ToMilliseconds());
1548 #endif
1549 SharedFontList()->SetFamilyNames(families);
1550 GetPrefsAndStartLoader();
1553 if (!SharedFontList()->Initialized()) {
1554 return;
1557 GetDirectWriteSubstitutes();
1558 GetFontSubstitutes();
1561 nsresult gfxDWriteFontList::InitFontListForPlatform() {
1562 LARGE_INTEGER frequency; // ticks per second
1563 LARGE_INTEGER t1, t2, t3, t4, t5; // ticks
1564 double elapsedTime, upTime;
1565 char nowTime[256], nowDate[256];
1567 if (LOG_FONTINIT_ENABLED()) {
1568 GetTimeFormatA(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT, nullptr, nullptr,
1569 nowTime, 256);
1570 GetDateFormatA(LOCALE_INVARIANT, 0, nullptr, nullptr, nowDate, 256);
1571 upTime = (double)GetTickCount();
1573 QueryPerformanceFrequency(&frequency);
1574 QueryPerformanceCounter(&t1); // start
1576 HRESULT hr;
1577 mGDIFontTableAccess = Preferences::GetBool(
1578 "gfx.font_rendering.directwrite.use_gdi_table_loading", false);
1580 mFontSubstitutes.Clear();
1581 mNonExistingFonts.Clear();
1583 RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
1585 hr = factory->GetGdiInterop(getter_AddRefs(mGDIInterop));
1586 if (FAILED(hr)) {
1587 Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1588 uint32_t(errGDIInterop));
1589 return NS_ERROR_FAILURE;
1592 QueryPerformanceCounter(&t2); // base-class/interop initialization
1594 mSystemFonts = Factory::GetDWriteSystemFonts(true);
1595 NS_ASSERTION(mSystemFonts != nullptr, "GetSystemFontCollection failed!");
1597 if (!mSystemFonts) {
1598 Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1599 uint32_t(errSystemFontCollection));
1600 return NS_ERROR_FAILURE;
1603 #ifdef MOZ_BUNDLED_FONTS
1604 // Get bundled fonts before the system collection, so that in the case of
1605 // duplicate names, we have recorded the family as bundled (and therefore
1606 // available regardless of visibility settings).
1607 // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an
1608 // explicit value of 0 (off) will disable them.
1609 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1610 TimeStamp start = TimeStamp::Now();
1611 mBundledFonts = CreateBundledFontsCollection(factory);
1612 if (mBundledFonts) {
1613 GetFontsFromCollection(mBundledFonts);
1615 TimeStamp end = TimeStamp::Now();
1616 Telemetry::Accumulate(Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
1617 (end - start).ToMilliseconds());
1619 #endif
1620 const uint32_t kBundledCount = mFontFamilies.Count();
1622 QueryPerformanceCounter(&t3); // system font collection
1624 GetFontsFromCollection(mSystemFonts);
1626 // if no fonts found, something is out of whack, bail and use GDI backend
1627 NS_ASSERTION(mFontFamilies.Count() > kBundledCount,
1628 "no fonts found in the system fontlist -- holy crap batman!");
1629 if (mFontFamilies.Count() == kBundledCount) {
1630 Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1631 uint32_t(errNoFonts));
1632 return NS_ERROR_FAILURE;
1635 QueryPerformanceCounter(&t4); // iterate over system fonts
1637 mOtherFamilyNamesInitialized = true;
1638 GetFontSubstitutes();
1640 // bug 642093 - DirectWrite does not support old bitmap (.fon)
1641 // font files, but a few of these such as "Courier" and "MS Sans Serif"
1642 // are frequently specified in shoddy CSS, without appropriate fallbacks.
1643 // By mapping these to TrueType equivalents, we provide better consistency
1644 // with both pre-DW systems and with IE9, which appears to do the same.
1645 GetDirectWriteSubstitutes();
1647 // bug 551313 - DirectWrite creates a Gill Sans family out of
1648 // poorly named members of the Gill Sans MT family containing
1649 // only Ultra Bold weights. This causes big problems for pages
1650 // using Gill Sans which is usually only available on OSX
1652 nsAutoCString nameGillSans("Gill Sans");
1653 nsAutoCString nameGillSansMT("Gill Sans MT");
1654 BuildKeyNameFromFontName(nameGillSans);
1655 BuildKeyNameFromFontName(nameGillSansMT);
1657 gfxFontFamily* gillSansFamily = mFontFamilies.GetWeak(nameGillSans);
1658 gfxFontFamily* gillSansMTFamily = mFontFamilies.GetWeak(nameGillSansMT);
1660 if (gillSansFamily && gillSansMTFamily) {
1661 gillSansFamily->FindStyleVariations();
1662 nsTArray<RefPtr<gfxFontEntry> >& faces = gillSansFamily->GetFontList();
1663 uint32_t i;
1665 bool allUltraBold = true;
1666 for (i = 0; i < faces.Length(); i++) {
1667 // does the face have 'Ultra Bold' in the name?
1668 if (faces[i]->Name().Find("Ultra Bold"_ns) == -1) {
1669 allUltraBold = false;
1670 break;
1674 // if all the Gill Sans faces are Ultra Bold ==> move faces
1675 // for Gill Sans into Gill Sans MT family
1676 if (allUltraBold) {
1677 // add faces to Gill Sans MT
1678 for (i = 0; i < faces.Length(); i++) {
1679 // change the entry's family name to match its adoptive family
1680 faces[i]->mFamilyName = gillSansMTFamily->Name();
1681 gillSansMTFamily->AddFontEntry(faces[i]);
1683 if (LOG_FONTLIST_ENABLED()) {
1684 gfxFontEntry* fe = faces[i];
1685 nsAutoCString weightString;
1686 fe->Weight().ToString(weightString);
1687 LOG_FONTLIST(
1688 ("(fontlist) moved (%s) to family (%s)"
1689 " with style: %s weight: %s stretch: %d",
1690 fe->Name().get(), gillSansMTFamily->Name().get(),
1691 (fe->IsItalic()) ? "italic"
1692 : (fe->IsOblique() ? "oblique" : "normal"),
1693 weightString.get(), fe->Stretch()));
1697 // remove Gills Sans
1698 mFontFamilies.Remove(nameGillSans);
1702 nsAutoCString classicFamilies;
1703 nsresult rv = Preferences::GetCString(
1704 "gfx.font_rendering.cleartype_params.force_gdi_classic_for_families",
1705 classicFamilies);
1706 if (NS_SUCCEEDED(rv)) {
1707 nsCCharSeparatedTokenizer tokenizer(classicFamilies, ',');
1708 while (tokenizer.hasMoreTokens()) {
1709 nsAutoCString name(tokenizer.nextToken());
1710 BuildKeyNameFromFontName(name);
1711 gfxFontFamily* family = mFontFamilies.GetWeak(name);
1712 if (family) {
1713 static_cast<gfxDWriteFontFamily*>(family)->SetForceGDIClassic(true);
1717 mForceGDIClassicMaxFontSize = Preferences::GetInt(
1718 "gfx.font_rendering.cleartype_params.force_gdi_classic_max_size",
1719 mForceGDIClassicMaxFontSize);
1721 GetPrefsAndStartLoader();
1723 QueryPerformanceCounter(&t5); // misc initialization
1725 if (LOG_FONTINIT_ENABLED()) {
1726 // determine dwrite version
1727 nsAutoString dwriteVers;
1728 gfxWindowsPlatform::GetDLLVersion(L"dwrite.dll", dwriteVers);
1729 LOG_FONTINIT(("(fontinit) Start: %s %s\n", nowDate, nowTime));
1730 LOG_FONTINIT(("(fontinit) Uptime: %9.3f s\n", upTime / 1000));
1731 LOG_FONTINIT(("(fontinit) dwrite version: %s\n",
1732 NS_ConvertUTF16toUTF8(dwriteVers).get()));
1735 elapsedTime = (t5.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
1736 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_TOTAL,
1737 elapsedTime);
1738 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COUNT,
1739 mSystemFonts->GetFontFamilyCount());
1740 LOG_FONTINIT((
1741 "(fontinit) Total time in InitFontList: %9.3f ms (families: %d, %s)\n",
1742 elapsedTime, mSystemFonts->GetFontFamilyCount(),
1743 (mGDIFontTableAccess ? "gdi table access" : "dwrite table access")));
1745 elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
1746 LOG_FONTINIT(
1747 ("(fontinit) --- base/interop obj initialization init: %9.3f ms\n",
1748 elapsedTime));
1750 elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart;
1751 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COLLECT,
1752 elapsedTime);
1753 LOG_FONTINIT(
1754 ("(fontinit) --- GetSystemFontCollection: %9.3f ms\n", elapsedTime));
1756 elapsedTime = (t4.QuadPart - t3.QuadPart) * 1000.0 / frequency.QuadPart;
1757 LOG_FONTINIT(
1758 ("(fontinit) --- iterate over families: %9.3f ms\n", elapsedTime));
1760 elapsedTime = (t5.QuadPart - t4.QuadPart) * 1000.0 / frequency.QuadPart;
1761 LOG_FONTINIT(
1762 ("(fontinit) --- misc initialization: %9.3f ms\n", elapsedTime));
1764 return NS_OK;
1767 void gfxDWriteFontList::GetFontsFromCollection(
1768 IDWriteFontCollection* aCollection) {
1769 for (UINT32 i = 0; i < aCollection->GetFontFamilyCount(); i++) {
1770 RefPtr<IDWriteFontFamily> family;
1771 aCollection->GetFontFamily(i, getter_AddRefs(family));
1773 RefPtr<IDWriteLocalizedStrings> names;
1774 HRESULT hr = family->GetFamilyNames(getter_AddRefs(names));
1775 if (FAILED(hr)) {
1776 continue;
1779 nsAutoCString name;
1780 if (!GetEnglishOrFirstName(name, names)) {
1781 continue;
1783 nsAutoCString familyName(
1784 name); // keep a copy before we lowercase it as a key
1786 BuildKeyNameFromFontName(name);
1788 RefPtr<gfxFontFamily> fam;
1790 if (mFontFamilies.GetWeak(name)) {
1791 continue;
1794 FontVisibility visibility = aCollection == mSystemFonts
1795 ? GetVisibilityForFamily(familyName)
1796 : FontVisibility::Base;
1798 fam = new gfxDWriteFontFamily(familyName, visibility, family,
1799 aCollection == mSystemFonts);
1800 if (!fam) {
1801 continue;
1804 if (mBadUnderlineFamilyNames.ContainsSorted(name)) {
1805 fam->SetBadUnderlineFamily();
1807 mFontFamilies.InsertOrUpdate(name, RefPtr{fam});
1809 // now add other family name localizations, if present
1810 uint32_t nameCount = names->GetCount();
1811 uint32_t nameIndex;
1813 if (nameCount > 1) {
1814 UINT32 englishIdx = 0;
1815 BOOL exists;
1816 // if this fails/doesn't exist, we'll have used name index 0,
1817 // so that's the one we'll want to skip here
1818 names->FindLocaleName(L"en-us", &englishIdx, &exists);
1820 for (nameIndex = 0; nameIndex < nameCount; nameIndex++) {
1821 UINT32 nameLen;
1822 AutoTArray<WCHAR, 32> localizedName;
1824 // only add other names
1825 if (nameIndex == englishIdx) {
1826 continue;
1829 hr = names->GetStringLength(nameIndex, &nameLen);
1830 if (FAILED(hr)) {
1831 continue;
1834 if (!localizedName.SetLength(nameLen + 1, fallible)) {
1835 continue;
1838 hr = names->GetString(nameIndex, localizedName.Elements(), nameLen + 1);
1839 if (FAILED(hr)) {
1840 continue;
1843 NS_ConvertUTF16toUTF8 locName(localizedName.Elements());
1845 if (!familyName.Equals(locName)) {
1846 AddOtherFamilyName(fam, locName);
1851 // at this point, all family names have been read in
1852 fam->SetOtherFamilyNamesInitialized();
1856 static void RemoveCharsetFromFontSubstitute(nsACString& aName) {
1857 int32_t comma = aName.FindChar(',');
1858 if (comma >= 0) aName.Truncate(comma);
1861 #define MAX_VALUE_NAME 512
1862 #define MAX_VALUE_DATA 512
1864 nsresult gfxDWriteFontList::GetFontSubstitutes() {
1865 HKEY hKey;
1866 DWORD i, rv, lenAlias, lenActual, valueType;
1867 WCHAR aliasName[MAX_VALUE_NAME];
1868 WCHAR actualName[MAX_VALUE_DATA];
1870 if (RegOpenKeyExW(
1871 HKEY_LOCAL_MACHINE,
1872 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
1873 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
1874 return NS_ERROR_FAILURE;
1877 for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) {
1878 aliasName[0] = 0;
1879 lenAlias = ArrayLength(aliasName);
1880 actualName[0] = 0;
1881 lenActual = sizeof(actualName);
1882 rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType,
1883 (LPBYTE)actualName, &lenActual);
1885 if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) {
1886 continue;
1889 if (aliasName[0] == WCHAR('@')) {
1890 continue;
1893 NS_ConvertUTF16toUTF8 substituteName((char16_t*)aliasName);
1894 NS_ConvertUTF16toUTF8 actualFontName((char16_t*)actualName);
1895 RemoveCharsetFromFontSubstitute(substituteName);
1896 BuildKeyNameFromFontName(substituteName);
1897 RemoveCharsetFromFontSubstitute(actualFontName);
1898 BuildKeyNameFromFontName(actualFontName);
1899 if (SharedFontList()) {
1900 // Skip substitution if the original font is available, unless the option
1901 // to apply substitutions unconditionally is enabled.
1902 if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) {
1903 // Font substitutions are recorded for the canonical family names; we
1904 // don't need FindFamily to consider localized aliases when searching.
1905 if (SharedFontList()->FindFamily(substituteName,
1906 /*aPrimaryNameOnly*/ true)) {
1907 continue;
1910 if (SharedFontList()->FindFamily(actualFontName,
1911 /*aPrimaryNameOnly*/ true)) {
1912 mSubstitutions.InsertOrUpdate(substituteName,
1913 MakeUnique<nsCString>(actualFontName));
1914 } else if (mSubstitutions.Get(actualFontName)) {
1915 mSubstitutions.InsertOrUpdate(
1916 substituteName,
1917 MakeUnique<nsCString>(*mSubstitutions.Get(actualFontName)));
1918 } else {
1919 mNonExistingFonts.AppendElement(substituteName);
1921 } else {
1922 gfxFontFamily* ff;
1923 if (!actualFontName.IsEmpty() &&
1924 (ff = mFontFamilies.GetWeak(actualFontName))) {
1925 mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff});
1926 } else {
1927 mNonExistingFonts.AppendElement(substituteName);
1931 return NS_OK;
1934 struct FontSubstitution {
1935 const char* aliasName;
1936 const char* actualName;
1939 static const FontSubstitution sDirectWriteSubs[] = {
1940 {"MS Sans Serif", "Microsoft Sans Serif"},
1941 {"MS Serif", "Times New Roman"},
1942 {"Courier", "Courier New"},
1943 {"Small Fonts", "Arial"},
1944 {"Roman", "Times New Roman"},
1945 {"Script", "Mistral"}};
1947 void gfxDWriteFontList::GetDirectWriteSubstitutes() {
1948 for (uint32_t i = 0; i < ArrayLength(sDirectWriteSubs); ++i) {
1949 const FontSubstitution& sub(sDirectWriteSubs[i]);
1950 nsAutoCString substituteName(sub.aliasName);
1951 BuildKeyNameFromFontName(substituteName);
1952 if (SharedFontList()) {
1953 // Skip substitution if the original font is available, unless the option
1954 // to apply substitutions unconditionally is enabled.
1955 if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) {
1956 // We don't need FindFamily to consider localized aliases when searching
1957 // for the DirectWrite substitutes, we know the canonical names.
1958 if (SharedFontList()->FindFamily(substituteName,
1959 /*aPrimaryNameOnly*/ true)) {
1960 continue;
1963 nsAutoCString actualFontName(sub.actualName);
1964 BuildKeyNameFromFontName(actualFontName);
1965 if (SharedFontList()->FindFamily(actualFontName,
1966 /*aPrimaryNameOnly*/ true)) {
1967 mSubstitutions.InsertOrUpdate(substituteName,
1968 MakeUnique<nsCString>(actualFontName));
1969 } else {
1970 mNonExistingFonts.AppendElement(substituteName);
1972 } else {
1973 if (nullptr != mFontFamilies.GetWeak(substituteName)) {
1974 // don't do the substitution if user actually has a usable font
1975 // with this name installed
1976 continue;
1978 nsAutoCString actualFontName(sub.actualName);
1979 BuildKeyNameFromFontName(actualFontName);
1980 gfxFontFamily* ff;
1981 if (nullptr != (ff = mFontFamilies.GetWeak(actualFontName))) {
1982 mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff});
1983 } else {
1984 mNonExistingFonts.AppendElement(substituteName);
1990 bool gfxDWriteFontList::FindAndAddFamilies(
1991 nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
1992 const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
1993 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
1994 gfxFloat aDevToCssSize) {
1995 nsAutoCString keyName(aFamily);
1996 BuildKeyNameFromFontName(keyName);
1998 if (SharedFontList()) {
1999 nsACString* subst = mSubstitutions.Get(keyName);
2000 if (subst) {
2001 keyName = *subst;
2003 } else {
2004 gfxFontFamily* ff = mFontSubstitutes.GetWeak(keyName);
2005 FontVisibility level =
2006 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
2007 if (ff && IsVisibleToCSS(*ff, level)) {
2008 aOutput->AppendElement(FamilyAndGeneric(ff, aGeneric));
2009 return true;
2013 if (mNonExistingFonts.Contains(keyName)) {
2014 return false;
2017 return gfxPlatformFontList::FindAndAddFamilies(
2018 aPresContext, aGeneric, keyName, aOutput, aFlags, aStyle, aLanguage,
2019 aDevToCssSize);
2022 void gfxDWriteFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
2023 FontListSizes* aSizes) const {
2024 gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
2026 // We are a singleton, so include the font loader singleton's memory.
2027 MOZ_ASSERT(static_cast<const gfxPlatformFontList*>(this) ==
2028 gfxPlatformFontList::PlatformFontList());
2029 gfxDWriteFontFileLoader* loader = static_cast<gfxDWriteFontFileLoader*>(
2030 gfxDWriteFontFileLoader::Instance());
2031 aSizes->mLoaderSize += loader->SizeOfIncludingThis(aMallocSizeOf);
2033 aSizes->mFontListSize +=
2034 SizeOfFontFamilyTableExcludingThis(mFontSubstitutes, aMallocSizeOf);
2036 aSizes->mFontListSize +=
2037 mNonExistingFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
2038 for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) {
2039 aSizes->mFontListSize +=
2040 mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2044 void gfxDWriteFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
2045 FontListSizes* aSizes) const {
2046 aSizes->mFontListSize += aMallocSizeOf(this);
2047 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
2050 static HRESULT GetFamilyName(IDWriteFont* aFont, nsCString& aFamilyName) {
2051 HRESULT hr;
2052 RefPtr<IDWriteFontFamily> family;
2054 // clean out previous value
2055 aFamilyName.Truncate();
2057 hr = aFont->GetFontFamily(getter_AddRefs(family));
2058 if (FAILED(hr)) {
2059 return hr;
2062 RefPtr<IDWriteLocalizedStrings> familyNames;
2064 hr = family->GetFamilyNames(getter_AddRefs(familyNames));
2065 if (FAILED(hr)) {
2066 return hr;
2069 if (!GetEnglishOrFirstName(aFamilyName, familyNames)) {
2070 return E_FAIL;
2073 return S_OK;
2076 // bug 705594 - the method below doesn't actually do any "drawing", it's only
2077 // used to invoke the DirectWrite layout engine to determine the fallback font
2078 // for a given character.
2080 IFACEMETHODIMP DWriteFontFallbackRenderer::DrawGlyphRun(
2081 void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY,
2082 DWRITE_MEASURING_MODE measuringMode, DWRITE_GLYPH_RUN const* glyphRun,
2083 DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
2084 IUnknown* clientDrawingEffect) {
2085 if (!mSystemFonts) {
2086 return E_FAIL;
2089 HRESULT hr = S_OK;
2091 RefPtr<IDWriteFont> font;
2092 hr = mSystemFonts->GetFontFromFontFace(glyphRun->fontFace,
2093 getter_AddRefs(font));
2094 if (FAILED(hr)) {
2095 return hr;
2098 // copy the family name
2099 hr = GetFamilyName(font, mFamilyName);
2100 if (FAILED(hr)) {
2101 return hr;
2104 // Arial is used as the default fallback font
2105 // so if it matches ==> no font found
2106 if (mFamilyName.EqualsLiteral("Arial")) {
2107 mFamilyName.Truncate();
2108 return E_FAIL;
2110 return hr;
2113 gfxFontEntry* gfxDWriteFontList::PlatformGlobalFontFallback(
2114 nsPresContext* aPresContext, const uint32_t aCh, Script aRunScript,
2115 const gfxFontStyle* aMatchStyle, FontFamily& aMatchedFamily) {
2116 HRESULT hr;
2118 RefPtr<IDWriteFactory> dwFactory = Factory::GetDWriteFactory();
2119 if (!dwFactory) {
2120 return nullptr;
2123 // initialize fallback renderer
2124 if (!mFallbackRenderer) {
2125 mFallbackRenderer = new DWriteFontFallbackRenderer(dwFactory);
2127 if (!mFallbackRenderer->IsValid()) {
2128 return nullptr;
2131 // initialize text format
2132 if (!mFallbackFormat) {
2133 hr = dwFactory->CreateTextFormat(
2134 L"Arial", nullptr, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL,
2135 DWRITE_FONT_STRETCH_NORMAL, 72.0f, L"en-us",
2136 getter_AddRefs(mFallbackFormat));
2137 if (FAILED(hr)) {
2138 return nullptr;
2142 // set up string with fallback character
2143 wchar_t str[16];
2144 uint32_t strLen;
2146 if (IS_IN_BMP(aCh)) {
2147 str[0] = static_cast<wchar_t>(aCh);
2148 str[1] = 0;
2149 strLen = 1;
2150 } else {
2151 str[0] = static_cast<wchar_t>(H_SURROGATE(aCh));
2152 str[1] = static_cast<wchar_t>(L_SURROGATE(aCh));
2153 str[2] = 0;
2154 strLen = 2;
2157 // set up layout
2158 RefPtr<IDWriteTextLayout> fallbackLayout;
2160 hr = dwFactory->CreateTextLayout(str, strLen, mFallbackFormat, 200.0f, 200.0f,
2161 getter_AddRefs(fallbackLayout));
2162 if (FAILED(hr)) {
2163 return nullptr;
2166 // call the draw method to invoke the DirectWrite layout functions
2167 // which determine the fallback font
2168 MOZ_SEH_TRY {
2169 hr = fallbackLayout->Draw(nullptr, mFallbackRenderer, 50.0f, 50.0f);
2170 if (FAILED(hr)) {
2171 return nullptr;
2174 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
2175 gfxCriticalNote << "Exception occurred during DWrite font fallback";
2176 return nullptr;
2179 FontFamily family =
2180 FindFamily(aPresContext, mFallbackRenderer->FallbackFamilyName());
2181 if (!family.IsNull()) {
2182 gfxFontEntry* fontEntry = nullptr;
2183 if (family.mIsShared) {
2184 auto face =
2185 family.mShared->FindFaceForStyle(SharedFontList(), *aMatchStyle);
2186 if (face) {
2187 fontEntry = GetOrCreateFontEntry(face, family.mShared);
2189 } else {
2190 fontEntry = family.mUnshared->FindFontForStyle(*aMatchStyle);
2192 if (fontEntry && fontEntry->HasCharacter(aCh)) {
2193 aMatchedFamily = family;
2194 return fontEntry;
2196 Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, true);
2199 return nullptr;
2202 // used to load system-wide font info on off-main thread
2203 class DirectWriteFontInfo : public FontInfoData {
2204 public:
2205 DirectWriteFontInfo(bool aLoadOtherNames, bool aLoadFaceNames,
2206 bool aLoadCmaps, IDWriteFontCollection* aSystemFonts
2207 #ifdef MOZ_BUNDLED_FONTS
2209 IDWriteFontCollection* aBundledFonts
2210 #endif
2212 : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps),
2213 mSystemFonts(aSystemFonts)
2214 #ifdef MOZ_BUNDLED_FONTS
2216 mBundledFonts(aBundledFonts)
2217 #endif
2221 virtual ~DirectWriteFontInfo() = default;
2223 // loads font data for all members of a given family
2224 virtual void LoadFontFamilyData(const nsACString& aFamilyName);
2226 private:
2227 RefPtr<IDWriteFontCollection> mSystemFonts;
2228 #ifdef MOZ_BUNDLED_FONTS
2229 RefPtr<IDWriteFontCollection> mBundledFonts;
2230 #endif
2233 void DirectWriteFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
2234 // lookup the family
2235 NS_ConvertUTF8toUTF16 famName(aFamilyName);
2237 HRESULT hr;
2238 BOOL exists = false;
2240 uint32_t index;
2241 RefPtr<IDWriteFontFamily> family;
2242 hr = mSystemFonts->FindFamilyName((const wchar_t*)famName.get(), &index,
2243 &exists);
2244 if (SUCCEEDED(hr) && exists) {
2245 mSystemFonts->GetFontFamily(index, getter_AddRefs(family));
2246 if (!family) {
2247 return;
2251 #ifdef MOZ_BUNDLED_FONTS
2252 if (!family && mBundledFonts) {
2253 hr = mBundledFonts->FindFamilyName((const wchar_t*)famName.get(), &index,
2254 &exists);
2255 if (SUCCEEDED(hr) && exists) {
2256 mBundledFonts->GetFontFamily(index, getter_AddRefs(family));
2259 #endif
2261 if (!family) {
2262 return;
2265 // later versions of DirectWrite support querying the fullname/psname
2266 bool loadFaceNamesUsingDirectWrite = mLoadFaceNames;
2268 for (uint32_t i = 0; i < family->GetFontCount(); i++) {
2269 // get the font
2270 RefPtr<IDWriteFont> dwFont;
2271 hr = family->GetFont(i, getter_AddRefs(dwFont));
2272 if (FAILED(hr)) {
2273 // This should never happen.
2274 NS_WARNING("Failed to get existing font from family.");
2275 continue;
2278 if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
2279 // We don't want these in the font list; we'll apply simulations
2280 // on the fly when appropriate.
2281 continue;
2284 mLoadStats.fonts++;
2286 // get the name of the face
2287 nsCString fullID(aFamilyName);
2288 nsAutoCString fontName;
2289 hr = GetDirectWriteFontName(dwFont, fontName);
2290 if (FAILED(hr)) {
2291 continue;
2293 fullID.Append(' ');
2294 fullID.Append(fontName);
2296 FontFaceData fontData;
2297 bool haveData = true;
2298 RefPtr<IDWriteFontFace> dwFontFace;
2300 if (mLoadFaceNames) {
2301 // try to load using DirectWrite first
2302 if (loadFaceNamesUsingDirectWrite) {
2303 hr =
2304 GetDirectWriteFaceName(dwFont, PSNAME_ID, fontData.mPostscriptName);
2305 if (FAILED(hr)) {
2306 loadFaceNamesUsingDirectWrite = false;
2308 hr = GetDirectWriteFaceName(dwFont, FULLNAME_ID, fontData.mFullName);
2309 if (FAILED(hr)) {
2310 loadFaceNamesUsingDirectWrite = false;
2314 // if DirectWrite read fails, load directly from name table
2315 if (!loadFaceNamesUsingDirectWrite) {
2316 hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
2317 if (SUCCEEDED(hr)) {
2318 uint32_t kNAME =
2319 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
2320 const char* nameData;
2321 BOOL exists;
2322 void* ctx;
2323 uint32_t nameSize;
2325 hr = dwFontFace->TryGetFontTable(kNAME, (const void**)&nameData,
2326 &nameSize, &ctx, &exists);
2327 if (SUCCEEDED(hr) && nameData && nameSize > 0) {
2328 MOZ_SEH_TRY {
2329 gfxFontUtils::ReadCanonicalName(nameData, nameSize,
2330 gfxFontUtils::NAME_ID_FULL,
2331 fontData.mFullName);
2332 gfxFontUtils::ReadCanonicalName(nameData, nameSize,
2333 gfxFontUtils::NAME_ID_POSTSCRIPT,
2334 fontData.mPostscriptName);
2336 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
2337 gfxCriticalNote << "Exception occurred reading names for "
2338 << PromiseFlatCString(aFamilyName).get();
2340 dwFontFace->ReleaseFontTable(ctx);
2345 haveData =
2346 !fontData.mPostscriptName.IsEmpty() || !fontData.mFullName.IsEmpty();
2347 if (haveData) {
2348 mLoadStats.facenames++;
2352 // cmaps
2353 if (mLoadCmaps) {
2354 if (!dwFontFace) {
2355 hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
2356 if (!SUCCEEDED(hr)) {
2357 continue;
2361 uint32_t kCMAP =
2362 NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
2363 const uint8_t* cmapData;
2364 BOOL exists;
2365 void* ctx;
2366 uint32_t cmapSize;
2368 hr = dwFontFace->TryGetFontTable(kCMAP, (const void**)&cmapData,
2369 &cmapSize, &ctx, &exists);
2371 if (SUCCEEDED(hr) && exists) {
2372 bool cmapLoaded = false;
2373 RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
2374 uint32_t offset;
2375 MOZ_SEH_TRY {
2376 if (cmapData && cmapSize > 0 &&
2377 NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData, cmapSize, *charmap,
2378 offset))) {
2379 fontData.mCharacterMap = charmap;
2380 fontData.mUVSOffset = offset;
2381 cmapLoaded = true;
2382 mLoadStats.cmaps++;
2385 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
2386 gfxCriticalNote << "Exception occurred reading cmaps for "
2387 << PromiseFlatCString(aFamilyName).get();
2389 dwFontFace->ReleaseFontTable(ctx);
2390 haveData = haveData || cmapLoaded;
2394 // if have data, load
2395 if (haveData) {
2396 mFontFaceData.InsertOrUpdate(fullID, fontData);
2401 already_AddRefed<FontInfoData> gfxDWriteFontList::CreateFontInfoData() {
2402 bool loadCmaps = !UsesSystemFallback() ||
2403 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
2405 RefPtr<DirectWriteFontInfo> fi = new DirectWriteFontInfo(
2406 false, NeedFullnamePostscriptNames(), loadCmaps, mSystemFonts
2407 #ifdef MOZ_BUNDLED_FONTS
2409 mBundledFonts
2410 #endif
2413 return fi.forget();
2416 gfxFontFamily* gfxDWriteFontList::CreateFontFamily(
2417 const nsACString& aName, FontVisibility aVisibility) const {
2418 return new gfxDWriteFontFamily(aName, aVisibility, nullptr);
2421 #ifdef MOZ_BUNDLED_FONTS
2423 # define IMPL_QI_FOR_DWRITE(_interface) \
2424 public: \
2425 IFACEMETHOD(QueryInterface)(IID const& riid, void** ppvObject) { \
2426 if (__uuidof(_interface) == riid) { \
2427 *ppvObject = this; \
2428 } else if (__uuidof(IUnknown) == riid) { \
2429 *ppvObject = this; \
2430 } else { \
2431 *ppvObject = nullptr; \
2432 return E_NOINTERFACE; \
2434 this->AddRef(); \
2435 return S_OK; \
2438 class BundledFontFileEnumerator : public IDWriteFontFileEnumerator {
2439 IMPL_QI_FOR_DWRITE(IDWriteFontFileEnumerator)
2441 NS_INLINE_DECL_REFCOUNTING(BundledFontFileEnumerator)
2443 public:
2444 BundledFontFileEnumerator(IDWriteFactory* aFactory, nsIFile* aFontDir);
2446 IFACEMETHODIMP MoveNext(BOOL* hasCurrentFile);
2448 IFACEMETHODIMP GetCurrentFontFile(IDWriteFontFile** fontFile);
2450 private:
2451 BundledFontFileEnumerator() = delete;
2452 BundledFontFileEnumerator(const BundledFontFileEnumerator&) = delete;
2453 BundledFontFileEnumerator& operator=(const BundledFontFileEnumerator&) =
2454 delete;
2455 virtual ~BundledFontFileEnumerator() = default;
2457 RefPtr<IDWriteFactory> mFactory;
2459 nsCOMPtr<nsIFile> mFontDir;
2460 nsCOMPtr<nsIDirectoryEnumerator> mEntries;
2461 nsCOMPtr<nsISupports> mCurrent;
2464 BundledFontFileEnumerator::BundledFontFileEnumerator(IDWriteFactory* aFactory,
2465 nsIFile* aFontDir)
2466 : mFactory(aFactory), mFontDir(aFontDir) {
2467 mFontDir->GetDirectoryEntries(getter_AddRefs(mEntries));
2470 IFACEMETHODIMP
2471 BundledFontFileEnumerator::MoveNext(BOOL* aHasCurrentFile) {
2472 bool hasMore = false;
2473 if (mEntries) {
2474 if (NS_SUCCEEDED(mEntries->HasMoreElements(&hasMore)) && hasMore) {
2475 if (NS_SUCCEEDED(mEntries->GetNext(getter_AddRefs(mCurrent)))) {
2476 hasMore = true;
2480 *aHasCurrentFile = hasMore;
2481 return S_OK;
2484 IFACEMETHODIMP
2485 BundledFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** aFontFile) {
2486 nsCOMPtr<nsIFile> file = do_QueryInterface(mCurrent);
2487 if (!file) {
2488 return E_FAIL;
2490 nsString path;
2491 if (NS_FAILED(file->GetPath(path))) {
2492 return E_FAIL;
2494 return mFactory->CreateFontFileReference((const WCHAR*)path.get(), nullptr,
2495 aFontFile);
2498 class BundledFontLoader : public IDWriteFontCollectionLoader {
2499 IMPL_QI_FOR_DWRITE(IDWriteFontCollectionLoader)
2501 NS_INLINE_DECL_REFCOUNTING(BundledFontLoader)
2503 public:
2504 BundledFontLoader() {}
2506 IFACEMETHODIMP CreateEnumeratorFromKey(
2507 IDWriteFactory* aFactory, const void* aCollectionKey,
2508 UINT32 aCollectionKeySize,
2509 IDWriteFontFileEnumerator** aFontFileEnumerator);
2511 private:
2512 BundledFontLoader(const BundledFontLoader&) = delete;
2513 BundledFontLoader& operator=(const BundledFontLoader&) = delete;
2514 virtual ~BundledFontLoader() = default;
2517 IFACEMETHODIMP
2518 BundledFontLoader::CreateEnumeratorFromKey(
2519 IDWriteFactory* aFactory, const void* aCollectionKey,
2520 UINT32 aCollectionKeySize,
2521 IDWriteFontFileEnumerator** aFontFileEnumerator) {
2522 nsIFile* fontDir = *(nsIFile**)aCollectionKey;
2523 *aFontFileEnumerator = new BundledFontFileEnumerator(aFactory, fontDir);
2524 NS_ADDREF(*aFontFileEnumerator);
2525 return S_OK;
2528 already_AddRefed<IDWriteFontCollection>
2529 gfxDWriteFontList::CreateBundledFontsCollection(IDWriteFactory* aFactory) {
2530 nsCOMPtr<nsIFile> localDir;
2531 nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
2532 if (NS_FAILED(rv)) {
2533 return nullptr;
2535 if (NS_FAILED(localDir->Append(u"fonts"_ns))) {
2536 return nullptr;
2538 bool isDir;
2539 if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
2540 return nullptr;
2543 RefPtr<BundledFontLoader> loader = new BundledFontLoader();
2544 if (FAILED(aFactory->RegisterFontCollectionLoader(loader))) {
2545 return nullptr;
2548 const void* key = localDir.get();
2549 RefPtr<IDWriteFontCollection> collection;
2550 HRESULT hr = aFactory->CreateCustomFontCollection(loader, &key, sizeof(key),
2551 getter_AddRefs(collection));
2553 aFactory->UnregisterFontCollectionLoader(loader);
2555 if (FAILED(hr)) {
2556 return nullptr;
2557 } else {
2558 return collection.forget();
2562 #endif