1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "mozilla/ArrayUtils.h"
7 #include "mozilla/FontPropertyTypes.h"
8 #include "mozilla/MemoryReporting.h"
9 #include "mozilla/intl/OSPreferences.h"
10
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"
26
27 #include "gfxGDIFontList.h"
28
29 #include "harfbuzz/hb.h"
30
31 #include "StandardFonts-win10.inc"
32
33 using namespace mozilla;
34 using namespace mozilla::gfx;
35 using mozilla::intl::OSPreferences;
36
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)
41
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)
46
47 #define LOG_CMAPDATA_ENABLED() \
48 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)
49
BuildKeyNameFromFontName(nsACString & aName)50 static __inline void BuildKeyNameFromFontName(nsACString& aName) {
51 ToLowerCase(aName);
52 }
53
54 ////////////////////////////////////////////////////////////////////////////////
55 // gfxDWriteFontFamily
56
~gfxDWriteFontFamily()57 gfxDWriteFontFamily::~gfxDWriteFontFamily() {}
58
GetNameAsUtf8(nsACString & aName,IDWriteLocalizedStrings * aStrings,UINT32 aIndex)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;
66 }
67 if (!name.SetLength(length + 1, fallible)) {
68 return false;
69 }
70 hr = aStrings->GetString(aIndex, name.Elements(), length + 1);
71 if (FAILED(hr)) {
72 return false;
73 }
74 aName.Truncate();
75 AppendUTF16toUTF8(
76 Substring(reinterpret_cast<const char16_t*>(name.Elements()),
77 name.Length() - 1),
78 aName);
79 return true;
80 }
81
GetEnglishOrFirstName(nsACString & aName,IDWriteLocalizedStrings * aStrings)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;
90 }
91 return GetNameAsUtf8(aName, aStrings, englishIdx);
92 }
93
GetDirectWriteFontName(IDWriteFont * aFont,nsACString & aFontName)94 static HRESULT GetDirectWriteFontName(IDWriteFont* aFont,
95 nsACString& aFontName) {
96 HRESULT hr;
97
98 RefPtr<IDWriteLocalizedStrings> names;
99 hr = aFont->GetFaceNames(getter_AddRefs(names));
100 if (FAILED(hr)) {
101 return hr;
102 }
103
104 if (!GetEnglishOrFirstName(aFontName, names)) {
105 return E_FAIL;
106 }
107
108 return S_OK;
109 }
110
111 #define FULLNAME_ID DWRITE_INFORMATIONAL_STRING_FULL_NAME
112 #define PSNAME_ID DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME
113
114 // for use in reading postscript or fullname
GetDirectWriteFaceName(IDWriteFont * aFont,DWRITE_INFORMATIONAL_STRING_ID aWhichName,nsACString & aFontName)115 static HRESULT GetDirectWriteFaceName(IDWriteFont* aFont,
116 DWRITE_INFORMATIONAL_STRING_ID aWhichName,
117 nsACString& aFontName) {
118 HRESULT hr;
119
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;
126 }
127
128 if (!GetEnglishOrFirstName(aFontName, infostrings)) {
129 return E_FAIL;
130 }
131
132 return S_OK;
133 }
134
FindStyleVariations(FontInfoData * aFontInfoData)135 void gfxDWriteFontFamily::FindStyleVariations(FontInfoData* aFontInfoData) {
136 HRESULT hr;
137 if (mHasStyles) {
138 return;
139 }
140 mHasStyles = true;
141
142 gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
143
144 bool skipFaceNames =
145 mFaceNamesInitialized || !fp->NeedFullnamePostscriptNames();
146 bool fontInfoShouldHaveFaceNames = !mFaceNamesInitialized &&
147 fp->NeedFullnamePostscriptNames() &&
148 aFontInfoData;
149
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;
157 }
158
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;
163 }
164
165 // name
166 nsCString fullID(mName);
167 nsAutoCString faceName;
168 hr = GetDirectWriteFontName(font, faceName);
169 if (FAILED(hr)) {
170 continue;
171 }
172 fullID.Append(' ');
173 fullID.Append(faceName);
174
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;
183 }
184
185 gfxDWriteFontEntry* fe =
186 new gfxDWriteFontEntry(fullID, font, mIsSystemFontFamily);
187 fe->SetForceGDIClassic(mForceGDIClassic);
188
189 fe->SetupVariationRanges();
190
191 AddFontEntry(fe);
192
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);
199 }
200 if (!psname.IsEmpty()) {
201 fp->AddPostscriptName(fe, psname);
202 }
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);
209 }
210
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);
216 }
217 }
218
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()));
229 }
230 }
231
232 // assume that if no error, all postscript/fullnames were initialized
233 if (!skipFaceNames) {
234 mFaceNamesInitialized = true;
235 }
236
237 if (!mAvailableFonts.Length()) {
238 NS_WARNING("Family with no font faces in it.");
239 }
240
241 if (mIsBadUnderlineFamily) {
242 SetBadUnderlineFonts();
243 }
244
245 CheckForSimpleFamily();
246 if (mIsSimpleFamily) {
247 for (auto& f : mAvailableFonts) {
248 if (f) {
249 static_cast<gfxDWriteFontEntry*>(f.get())->mMayUseGDIAccess = true;
250 }
251 }
252 }
253 }
254
ReadFaceNames(gfxPlatformFontList * aPlatformFontList,bool aNeedFullnamePostscriptNames,FontInfoData * aFontInfoData)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;
262 }
263
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();
270 }
271
272 // fallback to looking up via name table
273 if (!mOtherFamilyNamesInitialized || !mFaceNamesInitialized) {
274 gfxFontFamily::ReadFaceNames(aPlatformFontList,
275 aNeedFullnamePostscriptNames, aFontInfoData);
276 }
277 }
278
LocalizedName(nsACString & aLocalizedName)279 void gfxDWriteFontFamily::LocalizedName(nsACString& aLocalizedName) {
280 aLocalizedName = Name(); // just return canonical name in case of failure
281
282 if (!mDWFamily) {
283 return;
284 }
285
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;
293 }
294 osprefs->GetSystemLocale(locale);
295
296 RefPtr<IDWriteLocalizedStrings> names;
297
298 hr = mDWFamily->GetFamilyNames(getter_AddRefs(names));
299 if (FAILED(hr)) {
300 return;
301 }
302 UINT32 idx = 0;
303 BOOL exists;
304 hr =
305 names->FindLocaleName(NS_ConvertUTF8toUTF16(locale).get(), &idx, &exists);
306 if (FAILED(hr)) {
307 return;
308 }
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;
314 }
315 if (!exists) {
316 // Use 0 index if english is not found.
317 idx = 0;
318 }
319 }
320 AutoTArray<WCHAR, 32> famName;
321 UINT32 length;
322
323 hr = names->GetStringLength(idx, &length);
324 if (FAILED(hr)) {
325 return;
326 }
327
328 if (!famName.SetLength(length + 1, fallible)) {
329 // Eeep - running out of memory. Unlikely to end well.
330 return;
331 }
332
333 hr = names->GetString(idx, famName.Elements(), length + 1);
334 if (FAILED(hr)) {
335 return;
336 }
337
338 aLocalizedName = NS_ConvertUTF16toUTF8((const char16_t*)famName.Elements(),
339 famName.Length() - 1);
340 }
341
IsSymbolFontFamily() const342 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();
348 }
349 }
350 return false;
351 }
352
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const353 void gfxDWriteFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
354 FontListSizes* aSizes) const {
355 gfxFontFamily::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
356 // TODO:
357 // This doesn't currently account for |mDWFamily|
358 }
359
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const360 void gfxDWriteFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
361 FontListSizes* aSizes) const {
362 aSizes->mFontListSize += aMallocSizeOf(this);
363 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
364 }
365
366 ////////////////////////////////////////////////////////////////////////////////
367 // gfxDWriteFontEntry
368
Clone() const369 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;
376 }
377
~gfxDWriteFontEntry()378 gfxDWriteFontEntry::~gfxDWriteFontEntry() {}
379
UsingArabicOrHebrewScriptSystemLocale()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;
394 }
395 }
396
CopyFontTable(uint32_t aTableTag,nsTArray<uint8_t> & aBuffer)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);
401
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;
420 }
421 return NS_ERROR_OUT_OF_MEMORY;
422 }
423 }
424 }
425 }
426
427 RefPtr<IDWriteFontFace> fontFace;
428 nsresult rv = CreateFontFace(getter_AddRefs(fontFace));
429 if (NS_FAILED(rv)) {
430 return rv;
431 }
432
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;
441 }
442
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;
448 }
449
450 if (tableContext) {
451 fontFace->ReleaseFontTable(&tableContext);
452 }
453
454 return rv;
455 }
456
457 // Access to font tables packaged in hb_blob_t form
458
459 // object attached to the Harfbuzz blob, used to release
460 // the table when the blob is destroyed
461 class FontTableRec {
462 public:
FontTableRec(IDWriteFontFace * aFontFace,void * aContext)463 FontTableRec(IDWriteFontFace* aFontFace, void* aContext)
464 : mFontFace(aFontFace), mContext(aContext) {
465 MOZ_COUNT_CTOR(FontTableRec);
466 }
467
~FontTableRec()468 ~FontTableRec() {
469 MOZ_COUNT_DTOR(FontTableRec);
470 mFontFace->ReleaseFontTable(mContext);
471 }
472
473 private:
474 RefPtr<IDWriteFontFace> mFontFace;
475 void* mContext;
476 };
477
DestroyBlobFunc(void * aUserData)478 static void DestroyBlobFunc(void* aUserData) {
479 FontTableRec* ftr = static_cast<FontTableRec*>(aUserData);
480 delete ftr;
481 }
482
GetFontTable(uint32_t aTag)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);
489 }
490
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);
501 }
502
503 return nullptr;
504 }
505
ReadCMAP(FontInfoData * aFontInfoData)506 nsresult gfxDWriteFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
507 AUTO_PROFILER_LABEL("gfxDWriteFontEntry::ReadCMAP", GRAPHICS);
508
509 // attempt this once, if errors occur leave a blank cmap
510 if (mCharacterMap || mShmemCharacterMap) {
511 return NS_OK;
512 }
513
514 RefPtr<gfxCharacterMap> charmap;
515 nsresult rv;
516
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);
524
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;
532 }
533 }
534
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);
544 }
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;
553 }
554 } else {
555 mCharacterMap = pfl->FindCharMap(charmap);
556 }
557 } else {
558 // if error occurred, initialize to null cmap
559 mCharacterMap = new gfxCharacterMap();
560 }
561
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);
569 }
570
571 return rv;
572 }
573
HasVariations()574 bool gfxDWriteFontEntry::HasVariations() {
575 if (mHasVariationsInitialized) {
576 return mHasVariations;
577 }
578 mHasVariationsInitialized = true;
579 mHasVariations = false;
580
581 if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
582 return mHasVariations;
583 }
584
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;
591 }
592 }
593 if (mFontFace5) {
594 mHasVariations = mFontFace5->HasVariations();
595 }
596 return mHasVariations;
597 }
598
GetVariationAxes(nsTArray<gfxFontVariationAxis> & aAxes)599 void gfxDWriteFontEntry::GetVariationAxes(
600 nsTArray<gfxFontVariationAxis>& aAxes) {
601 if (!HasVariations()) {
602 return;
603 }
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;
610 }
611
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;
625 }
626 if (!(attrs & DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE)) {
627 continue;
628 }
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);
639 }
640 axis.mMinValue = ranges[i].minValue;
641 axis.mMaxValue = ranges[i].maxValue;
642 axis.mDefaultValue = defaultValues[i].value;
643 aAxes.AppendElement(axis);
644 }
645 }
646
GetVariationInstances(nsTArray<gfxFontVariationInstance> & aInstances)647 void gfxDWriteFontEntry::GetVariationInstances(
648 nsTArray<gfxFontVariationInstance>& aInstances) {
649 gfxFontUtils::GetVariationData(this, nullptr, &aInstances);
650 }
651
CreateFontInstance(const gfxFontStyle * aFontStyle)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;
666 }
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;
673 }
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);
680
681 if (!vars.IsEmpty()) {
682 nsresult rv =
683 CreateFontFace(getter_AddRefs(fontFace), aFontStyle, sims, &vars);
684 if (NS_FAILED(rv)) {
685 return nullptr;
686 }
687 }
688 }
689 return new gfxDWriteFont(unscaledFont, this, aFontStyle, fontFace);
690 }
691
CreateFontFace(IDWriteFontFace ** aFontFace,const gfxFontStyle * aFontStyle,DWRITE_FONT_SIMULATIONS aSimulations,const nsTArray<gfxFontVariation> * aVariations)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);
701 };
702
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;
716 }
717 if (FAILED(hr)) {
718 return NS_ERROR_FAILURE;
719 }
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);
736 }
737 resource->CreateFontFace(
738 mFontFace->GetSimulations(), fontAxisValues.Elements(),
739 fontAxisValues.Length(), getter_AddRefs(mFontFace5));
740 }
741 }
742 }
743 }
744
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);
749
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;
757
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);
764 }
765 }
766
767 IDWriteFontFace5* ff5;
768 resource->CreateFontFace(aSimulations, fontAxisValues.Elements(),
769 fontAxisValues.Length(), &ff5);
770 if (ff5) {
771 *aFontFace = ff5;
772 return NS_OK;
773 }
774 }
775 }
776
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;
785 }
786 AutoTArray<IDWriteFontFile*, 1> files;
787 files.AppendElements(numberOfFiles);
788 if (FAILED(mFontFace->GetFiles(&numberOfFiles, files.Elements()))) {
789 return NS_ERROR_FAILURE;
790 }
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();
796 }
797 return FAILED(hr) ? NS_ERROR_FAILURE : NS_OK;
798 }
799
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;
806 }
807 (*aFontFace)->AddRef();
808 return NS_OK;
809 }
810
InitLogFont(IDWriteFont * aFont,LOGFONTW * aLogFont)811 bool gfxDWriteFontEntry::InitLogFont(IDWriteFont* aFont, LOGFONTW* aLogFont) {
812 HRESULT hr;
813
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);
822 }
823
IsCJKFont()824 bool gfxDWriteFontEntry::IsCJKFont() {
825 if (mIsCJK != UNINITIALIZED_VALUE) {
826 return mIsCJK;
827 }
828
829 mIsCJK = false;
830
831 const uint32_t kOS2Tag = TRUETYPE_TAG('O', 'S', '/', '2');
832 hb_blob_t* blob = GetFontTable(kOS2Tag);
833 if (!blob) {
834 return mIsCJK;
835 }
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!)
839
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;
854 }
855 }
856 hb_blob_destroy(blob);
857
858 return mIsCJK;
859 }
860
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const861 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
866 }
867
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const868 void gfxDWriteFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
869 FontListSizes* aSizes) const {
870 aSizes->mFontListSize += aMallocSizeOf(this);
871 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
872 }
873
874 ////////////////////////////////////////////////////////////////////////////////
875 // gfxDWriteFontList
876
gfxDWriteFontList()877 gfxDWriteFontList::gfxDWriteFontList() : mForceGDIClassicMaxFontSize(0.0) {
878 CheckFamilyList(kBaseFonts, ArrayLength(kBaseFonts));
879 CheckFamilyList(kLangPackFonts, ArrayLength(kLangPackFonts));
880 }
881
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.
885
GetDefaultFontForPlatform(const gfxFontStyle * aStyle,nsAtom * aLanguage)886 FontFamily gfxDWriteFontList::GetDefaultFontForPlatform(
887 const gfxFontStyle* aStyle, nsAtom* aLanguage) {
888 // try Arial first
889 FontFamily ff;
890 ff = FindFamily("Arial"_ns);
891 if (!ff.IsNull()) {
892 return ff;
893 }
894
895 // otherwise, use local default
896 NONCLIENTMETRICSW ncm;
897 ncm.cbSize = sizeof(ncm);
898 BOOL status =
899 ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
900
901 if (status) {
902 ff = FindFamily(NS_ConvertUTF16toUTF8(ncm.lfMessageFont.lfFaceName));
903 }
904
905 return ff;
906 }
907
LookupLocalFont(const nsACString & aFontName,WeightRange aWeightForEntry,StretchRange aStretchForEntry,SlantStyleRange aStyleForEntry)908 gfxFontEntry* gfxDWriteFontList::LookupLocalFont(
909 const nsACString& aFontName, WeightRange aWeightForEntry,
910 StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry) {
911 if (SharedFontList()) {
912 return LookupInSharedFaceNameList(aFontName, aWeightForEntry,
913 aStretchForEntry, aStyleForEntry);
914 }
915
916 gfxFontEntry* lookup;
917
918 lookup = LookupInFaceNameLists(aFontName);
919 if (!lookup) {
920 return nullptr;
921 }
922
923 gfxDWriteFontEntry* dwriteLookup = static_cast<gfxDWriteFontEntry*>(lookup);
924 gfxDWriteFontEntry* fe =
925 new gfxDWriteFontEntry(lookup->Name(), dwriteLookup->mFont,
926 aWeightForEntry, aStretchForEntry, aStyleForEntry);
927 fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic());
928 return fe;
929 }
930
MakePlatformFont(const nsACString & aFontName,WeightRange aWeightForEntry,StretchRange aStretchForEntry,SlantStyleRange aStyleForEntry,const uint8_t * aFontData,uint32_t aLength)931 gfxFontEntry* gfxDWriteFontList::MakePlatformFont(
932 const nsACString& aFontName, WeightRange aWeightForEntry,
933 StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry,
934 const uint8_t* aFontData, uint32_t aLength) {
935 RefPtr<IDWriteFontFileStream> fontFileStream;
936 RefPtr<IDWriteFontFile> fontFile;
937 HRESULT hr = gfxDWriteFontFileLoader::CreateCustomFontFile(
938 aFontData, aLength, getter_AddRefs(fontFile),
939 getter_AddRefs(fontFileStream));
940 free((void*)aFontData);
941 NS_ASSERTION(SUCCEEDED(hr), "Failed to create font file reference");
942 if (FAILED(hr)) {
943 return nullptr;
944 }
945
946 nsAutoString uniqueName;
947 nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
948 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to make unique user font name");
949 if (NS_FAILED(rv)) {
950 return nullptr;
951 }
952
953 BOOL isSupported;
954 DWRITE_FONT_FILE_TYPE fileType;
955 UINT32 numFaces;
956
957 auto entry = MakeUnique<gfxDWriteFontEntry>(
958 NS_ConvertUTF16toUTF8(uniqueName), fontFile, fontFileStream,
959 aWeightForEntry, aStretchForEntry, aStyleForEntry);
960
961 hr = fontFile->Analyze(&isSupported, &fileType, &entry->mFaceType, &numFaces);
962 NS_ASSERTION(SUCCEEDED(hr), "IDWriteFontFile::Analyze failed");
963 if (FAILED(hr)) {
964 return nullptr;
965 }
966 NS_ASSERTION(isSupported, "Unsupported font file");
967 if (!isSupported) {
968 return nullptr;
969 }
970 NS_ASSERTION(numFaces == 1, "Font file does not contain exactly 1 face");
971 if (numFaces != 1) {
972 // We don't know how to deal with 0 faces either.
973 return nullptr;
974 }
975
976 return entry.release();
977 }
978
UseGDIFontTableAccess() const979 bool gfxDWriteFontList::UseGDIFontTableAccess() const {
980 // Using GDI font table access for DWrite is controlled by a pref, but also we
981 // must be able to make win32k calls.
982 return mGDIFontTableAccess && !IsWin32kLockedDown();
983 }
984
GetPostScriptNameFromNameTable(IDWriteFontFace * aFace,nsCString & aName)985 static void GetPostScriptNameFromNameTable(IDWriteFontFace* aFace,
986 nsCString& aName) {
987 const auto kNAME =
988 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
989 const char* data;
990 UINT32 size;
991 void* context;
992 BOOL exists;
993 if (SUCCEEDED(aFace->TryGetFontTable(kNAME, (const void**)&data, &size,
994 &context, &exists)) &&
995 exists) {
996 if (NS_FAILED(gfxFontUtils::ReadCanonicalName(
997 data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, aName))) {
998 aName.Truncate(0);
999 }
1000 aFace->ReleaseFontTable(context);
1001 }
1002 }
1003
CreateFontEntry(fontlist::Face * aFace,const fontlist::Family * aFamily)1004 gfxFontEntry* gfxDWriteFontList::CreateFontEntry(
1005 fontlist::Face* aFace, const fontlist::Family* aFamily) {
1006 IDWriteFontCollection* collection =
1007 #ifdef MOZ_BUNDLED_FONTS
1008 aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
1009 #else
1010 mSystemFonts;
1011 #endif
1012 RefPtr<IDWriteFontFamily> family;
1013 bool foundExpectedFamily = false;
1014 const nsCString& familyName =
1015 aFamily->DisplayName().AsString(SharedFontList());
1016 if (aFamily->Index() < collection->GetFontFamilyCount()) {
1017 HRESULT hr =
1018 collection->GetFontFamily(aFamily->Index(), getter_AddRefs(family));
1019 // Check that the family name is what we expected; if not, fall back to
1020 // search by name. It's sad we have to do this, but it is possible for
1021 // Windows to have given different versions of the system font collection
1022 // to the parent and child processes.
1023 if (SUCCEEDED(hr) && family) {
1024 RefPtr<IDWriteLocalizedStrings> names;
1025 hr = family->GetFamilyNames(getter_AddRefs(names));
1026 if (SUCCEEDED(hr) && names) {
1027 nsAutoCString name;
1028 if (GetEnglishOrFirstName(name, names)) {
1029 foundExpectedFamily = name.Equals(familyName);
1030 }
1031 }
1032 }
1033 }
1034 if (!foundExpectedFamily) {
1035 // Try to get family by name instead of index (to deal with the case of
1036 // collection mismatch).
1037 UINT32 index;
1038 BOOL exists;
1039 NS_ConvertUTF8toUTF16 name16(familyName);
1040 HRESULT hr = collection->FindFamilyName(
1041 reinterpret_cast<const WCHAR*>(name16.BeginReading()), &index, &exists);
1042 if (FAILED(hr) || !exists || index == UINT_MAX ||
1043 FAILED(collection->GetFontFamily(index, getter_AddRefs(family))) ||
1044 !family) {
1045 return nullptr;
1046 }
1047 }
1048
1049 // Retrieve the required face by index within the family.
1050 RefPtr<IDWriteFont> font;
1051 if (FAILED(family->GetFont(aFace->mIndex, getter_AddRefs(font))) || !font) {
1052 return nullptr;
1053 }
1054
1055 // Retrieve the psName from the font, so we can check we've found the
1056 // expected face.
1057 nsAutoCString psName;
1058 if (FAILED(GetDirectWriteFaceName(font, PSNAME_ID, psName))) {
1059 RefPtr<IDWriteFontFace> dwFontFace;
1060 if (SUCCEEDED(font->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1061 GetPostScriptNameFromNameTable(dwFontFace, psName);
1062 }
1063 }
1064
1065 // If it doesn't match, DirectWrite must have shuffled the order of faces
1066 // returned for the family; search by name as a fallback.
1067 nsCString faceName = aFace->mDescriptor.AsString(SharedFontList());
1068 if (psName != faceName) {
1069 gfxWarning() << "Face name mismatch for index " << aFace->mIndex
1070 << " in family " << familyName.get() << ": expected "
1071 << faceName.get() << ", found " << psName.get();
1072 for (uint32_t i = 0; i < family->GetFontCount(); ++i) {
1073 if (i == aFace->mIndex) {
1074 continue; // this was the face we already tried
1075 }
1076 if (FAILED(family->GetFont(i, getter_AddRefs(font))) || !font) {
1077 return nullptr; // this font family is broken!
1078 }
1079 if (FAILED(GetDirectWriteFaceName(font, PSNAME_ID, psName))) {
1080 RefPtr<IDWriteFontFace> dwFontFace;
1081 if (SUCCEEDED(font->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1082 GetPostScriptNameFromNameTable(dwFontFace, psName);
1083 }
1084 }
1085 if (psName == faceName) {
1086 break;
1087 }
1088 }
1089 }
1090 if (psName != faceName) {
1091 return nullptr;
1092 }
1093
1094 auto fe = new gfxDWriteFontEntry(faceName, font, !aFamily->IsBundled());
1095 fe->InitializeFrom(aFace, aFamily);
1096 fe->mForceGDIClassic = aFamily->IsForceClassic();
1097 fe->mMayUseGDIAccess = aFamily->IsSimple();
1098 return fe;
1099 }
1100
GetVisibilityForFamily(const nsACString & aName) const1101 FontVisibility gfxDWriteFontList::GetVisibilityForFamily(
1102 const nsACString& aName) const {
1103 if (FamilyInList(aName, kBaseFonts, ArrayLength(kBaseFonts))) {
1104 return FontVisibility::Base;
1105 }
1106 if (FamilyInList(aName, kLangPackFonts, ArrayLength(kLangPackFonts))) {
1107 return FontVisibility::LangPack;
1108 }
1109 return FontVisibility::User;
1110 }
1111
AppendFamiliesFromCollection(IDWriteFontCollection * aCollection,nsTArray<fontlist::Family::InitData> & aFamilies,const nsTArray<nsCString> * aForceClassicFams)1112 void gfxDWriteFontList::AppendFamiliesFromCollection(
1113 IDWriteFontCollection* aCollection,
1114 nsTArray<fontlist::Family::InitData>& aFamilies,
1115 const nsTArray<nsCString>* aForceClassicFams) {
1116 auto allFacesUltraBold = [](IDWriteFontFamily* aFamily) -> bool {
1117 for (UINT32 i = 0; i < aFamily->GetFontCount(); i++) {
1118 RefPtr<IDWriteFont> font;
1119 HRESULT hr = aFamily->GetFont(i, getter_AddRefs(font));
1120 if (FAILED(hr)) {
1121 NS_WARNING("Failed to get existing font from family.");
1122 continue;
1123 }
1124 nsAutoCString faceName;
1125 hr = GetDirectWriteFontName(font, faceName);
1126 if (FAILED(hr)) {
1127 continue;
1128 }
1129 if (faceName.Find("Ultra Bold"_ns) == kNotFound) {
1130 return false;
1131 }
1132 }
1133 return true;
1134 };
1135
1136 nsAutoCString locale;
1137 OSPreferences::GetInstance()->GetSystemLocale(locale);
1138 ToLowerCase(locale);
1139 NS_ConvertUTF8toUTF16 loc16(locale);
1140
1141 for (unsigned i = 0; i < aCollection->GetFontFamilyCount(); ++i) {
1142 RefPtr<IDWriteFontFamily> family;
1143 aCollection->GetFontFamily(i, getter_AddRefs(family));
1144 RefPtr<IDWriteLocalizedStrings> localizedNames;
1145 HRESULT hr = family->GetFamilyNames(getter_AddRefs(localizedNames));
1146 if (FAILED(hr)) {
1147 gfxWarning() << "Failed to get names for font-family " << i;
1148 continue;
1149 }
1150
1151 auto addFamily = [&](const nsACString& name, bool altLocale = false) {
1152 nsAutoCString key;
1153 key = name;
1154 BuildKeyNameFromFontName(key);
1155 bool bad = mBadUnderlineFamilyNames.ContainsSorted(key);
1156 bool classic =
1157 aForceClassicFams && aForceClassicFams->ContainsSorted(key);
1158 FontVisibility visibility;
1159 // Special case: hide the "Gill Sans" family that contains only UltraBold
1160 // faces, as this leads to breakage on sites with CSS that targeted the
1161 // Gill Sans family as found on macOS. (Bug 551313, bug 1632738)
1162 // TODO (jfkthame): the ultrabold faces from Gill Sans should be treated
1163 // as belonging to the Gill Sans MT family.
1164 if (key.EqualsLiteral("gill sans") && allFacesUltraBold(family)) {
1165 visibility = FontVisibility::Hidden;
1166 } else {
1167 visibility = aCollection == mSystemFonts ? GetVisibilityForFamily(name)
1168 : FontVisibility::Base;
1169 }
1170 aFamilies.AppendElement(fontlist::Family::InitData(
1171 key, name, i, visibility, aCollection != mSystemFonts, bad, classic,
1172 altLocale));
1173 };
1174
1175 unsigned count = localizedNames->GetCount();
1176 if (count == 1) {
1177 // This is the common case: the great majority of fonts only provide an
1178 // en-US family name.
1179 nsAutoCString name;
1180 if (!GetNameAsUtf8(name, localizedNames, 0)) {
1181 gfxWarning() << "GetNameAsUtf8 failed for index 0 in font-family " << i;
1182 continue;
1183 }
1184 addFamily(name);
1185 } else {
1186 AutoTArray<nsCString, 4> names;
1187 int sysLocIndex = -1;
1188 for (unsigned index = 0; index < count; ++index) {
1189 nsAutoCString name;
1190 if (!GetNameAsUtf8(name, localizedNames, index)) {
1191 gfxWarning() << "GetNameAsUtf8 failed for index " << index
1192 << " in font-family " << i;
1193 continue;
1194 }
1195 if (!names.Contains(name)) {
1196 if (sysLocIndex == -1) {
1197 WCHAR buf[32];
1198 if (SUCCEEDED(localizedNames->GetLocaleName(index, buf, 32))) {
1199 if (loc16.Equals(buf)) {
1200 sysLocIndex = names.Length();
1201 }
1202 }
1203 }
1204 names.AppendElement(name);
1205 }
1206 }
1207 // If we didn't find a name that matched the system locale, use the
1208 // first (which is most often en-US).
1209 if (sysLocIndex == -1) {
1210 sysLocIndex = 0;
1211 }
1212 // Hack to work around EPSON fonts with bad names (tagged as MacRoman
1213 // but actually contain MacJapanese data): if we've chosen the first
1214 // name, *and* it is non-ASCII, *and* there is an alternative present,
1215 // use the next option instead as being more likely to be valid.
1216 if (sysLocIndex == 0 && names.Length() > 1 && !IsAscii(names[0])) {
1217 sysLocIndex = 1;
1218 }
1219 for (unsigned index = 0; index < names.Length(); ++index) {
1220 addFamily(names[index], index != sysLocIndex);
1221 }
1222 }
1223 }
1224 }
1225
GetFacesInitDataForFamily(const fontlist::Family * aFamily,nsTArray<fontlist::Face::InitData> & aFaces,bool aLoadCmaps) const1226 void gfxDWriteFontList::GetFacesInitDataForFamily(
1227 const fontlist::Family* aFamily, nsTArray<fontlist::Face::InitData>& aFaces,
1228 bool aLoadCmaps) const {
1229 IDWriteFontCollection* collection =
1230 #ifdef MOZ_BUNDLED_FONTS
1231 aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
1232 #else
1233 mSystemFonts;
1234 #endif
1235 if (!collection) {
1236 return;
1237 }
1238 RefPtr<IDWriteFontFamily> family;
1239 collection->GetFontFamily(aFamily->Index(), getter_AddRefs(family));
1240 for (unsigned i = 0; i < family->GetFontCount(); ++i) {
1241 RefPtr<IDWriteFont> dwFont;
1242 family->GetFont(i, getter_AddRefs(dwFont));
1243 if (!dwFont || dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
1244 continue;
1245 }
1246 DWRITE_FONT_STYLE dwstyle = dwFont->GetStyle();
1247 // Ignore italic styles of Meiryo because "Meiryo (Bold) Italic" has
1248 // non-italic style glyphs as Japanese characters. However, using it
1249 // causes serious problem if web pages wants some elements to be
1250 // different style from others only with font-style. For example,
1251 // <em> and <i> should be rendered as italic in the default style.
1252 if (dwstyle != DWRITE_FONT_STYLE_NORMAL &&
1253 aFamily->Key().AsString(SharedFontList()).EqualsLiteral("meiryo")) {
1254 continue;
1255 }
1256 WeightRange weight(FontWeight(dwFont->GetWeight()));
1257 StretchRange stretch(FontStretchFromDWriteStretch(dwFont->GetStretch()));
1258 // Try to read PSName as a unique face identifier; if this fails we'll get
1259 // it directly from the 'name' table, and if that also fails we consider
1260 // the face unusable.
1261 MOZ_SEH_TRY {
1262 nsAutoCString name;
1263 RefPtr<gfxCharacterMap> charmap;
1264 if (FAILED(GetDirectWriteFaceName(dwFont, PSNAME_ID, name)) ||
1265 aLoadCmaps) {
1266 RefPtr<IDWriteFontFace> dwFontFace;
1267 if (SUCCEEDED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1268 if (name.IsEmpty()) {
1269 GetPostScriptNameFromNameTable(dwFontFace, name);
1270 }
1271 const auto kCMAP =
1272 NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
1273 const char* data;
1274 UINT32 size;
1275 void* context;
1276 BOOL exists;
1277 if (aLoadCmaps) {
1278 if (SUCCEEDED(dwFontFace->TryGetFontTable(
1279 kCMAP, (const void**)&data, &size, &context, &exists)) &&
1280 exists) {
1281 charmap = new gfxCharacterMap();
1282 uint32_t offset;
1283 gfxFontUtils::ReadCMAP((const uint8_t*)data, size, *charmap,
1284 offset);
1285 dwFontFace->ReleaseFontTable(context);
1286 }
1287 }
1288 }
1289 }
1290 if (name.IsEmpty()) {
1291 gfxWarning() << "Failed to get name for face " << i << " in family "
1292 << aFamily->Key().AsString(SharedFontList()).get();
1293 continue;
1294 }
1295 SlantStyleRange slant(
1296 dwstyle == DWRITE_FONT_STYLE_NORMAL ? FontSlantStyle::Normal()
1297 : dwstyle == DWRITE_FONT_STYLE_ITALIC ? FontSlantStyle::Italic()
1298 : FontSlantStyle::Oblique());
1299 aFaces.AppendElement(fontlist::Face::InitData{
1300 name, uint16_t(i), false, weight, stretch, slant, charmap});
1301 }
1302 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1303 // Exception (e.g. disk i/o error) occurred when DirectWrite tried to use
1304 // the font resource. We'll just skip the bad face.
1305 gfxCriticalNote << "Exception occurred reading faces for "
1306 << aFamily->Key().AsString(SharedFontList()).get();
1307 }
1308 }
1309 }
1310
ReadFaceNames(fontlist::Family * aFamily,fontlist::Face * aFace,nsCString & aPSName,nsCString & aFullName)1311 bool gfxDWriteFontList::ReadFaceNames(fontlist::Family* aFamily,
1312 fontlist::Face* aFace, nsCString& aPSName,
1313 nsCString& aFullName) {
1314 IDWriteFontCollection* collection =
1315 #ifdef MOZ_BUNDLED_FONTS
1316 aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
1317 #else
1318 mSystemFonts;
1319 #endif
1320 RefPtr<IDWriteFontFamily> family;
1321 if (FAILED(collection->GetFontFamily(aFamily->Index(),
1322 getter_AddRefs(family)))) {
1323 MOZ_ASSERT_UNREACHABLE("failed to get font-family");
1324 return false;
1325 }
1326 RefPtr<IDWriteFont> dwFont;
1327 if (FAILED(family->GetFont(aFace->mIndex, getter_AddRefs(dwFont)))) {
1328 MOZ_ASSERT_UNREACHABLE("failed to get font from family");
1329 return false;
1330 }
1331 HRESULT ps = GetDirectWriteFaceName(dwFont, PSNAME_ID, aPSName);
1332 HRESULT full = GetDirectWriteFaceName(dwFont, FULLNAME_ID, aFullName);
1333 if (FAILED(ps) || FAILED(full) || aPSName.IsEmpty() || aFullName.IsEmpty()) {
1334 // We'll return true if either name was found, false if both fail.
1335 // Note that on older Win7 systems, GetDirectWriteFaceName may "succeed"
1336 // but return an empty string, so we have to check for non-empty strings
1337 // to be sure we actually got a usable name.
1338
1339 // Initialize result to true if either name was already found.
1340 bool result = (SUCCEEDED(ps) && !aPSName.IsEmpty()) ||
1341 (SUCCEEDED(full) && !aFullName.IsEmpty());
1342 RefPtr<IDWriteFontFace> dwFontFace;
1343 if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1344 NS_WARNING("failed to create font face");
1345 return result;
1346 }
1347 void* context;
1348 const char* data;
1349 UINT32 size;
1350 BOOL exists;
1351 if (FAILED(dwFontFace->TryGetFontTable(
1352 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')),
1353 (const void**)&data, &size, &context, &exists)) ||
1354 !exists) {
1355 NS_WARNING("failed to get name table");
1356 return result;
1357 }
1358 MOZ_SEH_TRY {
1359 // Try to read the name table entries, and ensure result is true if either
1360 // one succeeds.
1361 if (FAILED(ps) || aPSName.IsEmpty()) {
1362 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1363 data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, aPSName))) {
1364 result = true;
1365 } else {
1366 NS_WARNING("failed to read psname");
1367 }
1368 }
1369 if (FAILED(full) || aFullName.IsEmpty()) {
1370 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1371 data, size, gfxFontUtils::NAME_ID_FULL, aFullName))) {
1372 result = true;
1373 } else {
1374 NS_WARNING("failed to read fullname");
1375 }
1376 }
1377 }
1378 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1379 gfxCriticalNote << "Exception occurred reading face names for "
1380 << aFamily->Key().AsString(SharedFontList()).get();
1381 }
1382 if (dwFontFace && context) {
1383 dwFontFace->ReleaseFontTable(context);
1384 }
1385 return result;
1386 }
1387 return true;
1388 }
1389
ReadFaceNamesForFamily(fontlist::Family * aFamily,bool aNeedFullnamePostscriptNames)1390 void gfxDWriteFontList::ReadFaceNamesForFamily(
1391 fontlist::Family* aFamily, bool aNeedFullnamePostscriptNames) {
1392 if (!aFamily->IsInitialized()) {
1393 if (!InitializeFamily(aFamily)) {
1394 return;
1395 }
1396 }
1397 IDWriteFontCollection* collection =
1398 #ifdef MOZ_BUNDLED_FONTS
1399 aFamily->IsBundled() ? mBundledFonts : mSystemFonts;
1400 #else
1401 mSystemFonts;
1402 #endif
1403 RefPtr<IDWriteFontFamily> family;
1404 if (FAILED(collection->GetFontFamily(aFamily->Index(),
1405 getter_AddRefs(family)))) {
1406 return;
1407 }
1408 fontlist::FontList* list = SharedFontList();
1409 const fontlist::Pointer* facePtrs = aFamily->Faces(list);
1410 nsAutoCString familyName(aFamily->DisplayName().AsString(list));
1411 nsAutoCString key(aFamily->Key().AsString(list));
1412
1413 // Read PS-names and fullnames of the faces, and any alternate family names
1414 // (either localizations or legacy subfamily names)
1415 for (unsigned i = 0; i < aFamily->NumFaces(); ++i) {
1416 auto face = static_cast<fontlist::Face*>(facePtrs[i].ToPtr(list));
1417 if (!face) {
1418 continue;
1419 }
1420 RefPtr<IDWriteFont> dwFont;
1421 if (FAILED(family->GetFont(face->mIndex, getter_AddRefs(dwFont)))) {
1422 continue;
1423 }
1424 RefPtr<IDWriteFontFace> dwFontFace;
1425 if (FAILED(dwFont->CreateFontFace(getter_AddRefs(dwFontFace)))) {
1426 continue;
1427 }
1428
1429 const char* data;
1430 UINT32 size;
1431 void* context;
1432 BOOL exists;
1433 if (FAILED(dwFontFace->TryGetFontTable(
1434 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')),
1435 (const void**)&data, &size, &context, &exists)) ||
1436 !exists) {
1437 continue;
1438 }
1439
1440 MOZ_SEH_TRY {
1441 AutoTArray<nsCString, 4> otherFamilyNames;
1442 gfxFontUtils::ReadOtherFamilyNamesForFace(familyName, data, size,
1443 otherFamilyNames, false);
1444 for (const auto& alias : otherFamilyNames) {
1445 nsAutoCString key(alias);
1446 ToLowerCase(key);
1447 auto aliasData = mAliasTable.GetOrInsertNew(key);
1448 aliasData->InitFromFamily(aFamily, familyName);
1449 aliasData->mFaces.AppendElement(facePtrs[i]);
1450 }
1451
1452 nsAutoCString psname, fullname;
1453 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1454 data, size, gfxFontUtils::NAME_ID_POSTSCRIPT, psname))) {
1455 ToLowerCase(psname);
1456 mLocalNameTable.InsertOrUpdate(
1457 psname, fontlist::LocalFaceRec::InitData(key, i));
1458 }
1459 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1460 data, size, gfxFontUtils::NAME_ID_FULL, fullname))) {
1461 ToLowerCase(fullname);
1462 if (fullname != psname) {
1463 mLocalNameTable.InsertOrUpdate(
1464 fullname, fontlist::LocalFaceRec::InitData(key, i));
1465 }
1466 }
1467 }
1468 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
1469 gfxCriticalNote << "Exception occurred reading names for "
1470 << familyName.get();
1471 }
1472
1473 dwFontFace->ReleaseFontTable(context);
1474 }
1475 }
1476
1477 enum DWriteInitError {
1478 errGDIInterop = 1,
1479 errSystemFontCollection = 2,
1480 errNoFonts = 3
1481 };
1482
InitSharedFontListForPlatform()1483 void gfxDWriteFontList::InitSharedFontListForPlatform() {
1484 mGDIFontTableAccess = Preferences::GetBool(
1485 "gfx.font_rendering.directwrite.use_gdi_table_loading", false);
1486 mForceGDIClassicMaxFontSize = Preferences::GetInt(
1487 "gfx.font_rendering.cleartype_params.force_gdi_classic_max_size",
1488 mForceGDIClassicMaxFontSize);
1489
1490 mSubstitutions.Clear();
1491 mNonExistingFonts.Clear();
1492
1493 RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
1494 HRESULT hr = factory->GetGdiInterop(getter_AddRefs(mGDIInterop));
1495 if (FAILED(hr)) {
1496 Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1497 uint32_t(errGDIInterop));
1498 mSharedFontList.reset(nullptr);
1499 return;
1500 }
1501
1502 mSystemFonts = Factory::GetDWriteSystemFonts(true);
1503 NS_ASSERTION(mSystemFonts != nullptr, "GetSystemFontCollection failed!");
1504 if (!mSystemFonts) {
1505 Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1506 uint32_t(errSystemFontCollection));
1507 mSharedFontList.reset(nullptr);
1508 return;
1509 }
1510 #ifdef MOZ_BUNDLED_FONTS
1511 // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an
1512 // explicit value of 0 (off) will disable them.
1513 TimeStamp start1 = TimeStamp::Now();
1514 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1515 mBundledFonts = CreateBundledFontsCollection(factory);
1516 }
1517 TimeStamp end1 = TimeStamp::Now();
1518 #endif
1519
1520 if (XRE_IsParentProcess()) {
1521 nsAutoCString classicFamilies;
1522 AutoTArray<nsCString, 16> forceClassicFams;
1523 nsresult rv = Preferences::GetCString(
1524 "gfx.font_rendering.cleartype_params.force_gdi_classic_for_families",
1525 classicFamilies);
1526 if (NS_SUCCEEDED(rv)) {
1527 for (auto name :
1528 nsCCharSeparatedTokenizer(classicFamilies, ',').ToRange()) {
1529 BuildKeyNameFromFontName(name);
1530 forceClassicFams.AppendElement(name);
1531 }
1532 forceClassicFams.Sort();
1533 }
1534 nsTArray<fontlist::Family::InitData> families;
1535 AppendFamiliesFromCollection(mSystemFonts, families, &forceClassicFams);
1536 #ifdef MOZ_BUNDLED_FONTS
1537 if (mBundledFonts) {
1538 TimeStamp start2 = TimeStamp::Now();
1539 AppendFamiliesFromCollection(mBundledFonts, families);
1540 TimeStamp end2 = TimeStamp::Now();
1541 Telemetry::Accumulate(
1542 Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
1543 (end1 - start1).ToMilliseconds() + (end2 - start2).ToMilliseconds());
1544 }
1545 #endif
1546 SharedFontList()->SetFamilyNames(families);
1547 GetPrefsAndStartLoader();
1548 }
1549
1550 if (!SharedFontList()->Initialized()) {
1551 return;
1552 }
1553
1554 GetDirectWriteSubstitutes();
1555 GetFontSubstitutes();
1556 }
1557
InitFontListForPlatform()1558 nsresult gfxDWriteFontList::InitFontListForPlatform() {
1559 LARGE_INTEGER frequency; // ticks per second
1560 LARGE_INTEGER t1, t2, t3, t4, t5; // ticks
1561 double elapsedTime, upTime;
1562 char nowTime[256], nowDate[256];
1563
1564 if (LOG_FONTINIT_ENABLED()) {
1565 GetTimeFormatA(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT, nullptr, nullptr,
1566 nowTime, 256);
1567 GetDateFormatA(LOCALE_INVARIANT, 0, nullptr, nullptr, nowDate, 256);
1568 upTime = (double)GetTickCount();
1569 }
1570 QueryPerformanceFrequency(&frequency);
1571 QueryPerformanceCounter(&t1); // start
1572
1573 HRESULT hr;
1574 mGDIFontTableAccess = Preferences::GetBool(
1575 "gfx.font_rendering.directwrite.use_gdi_table_loading", false);
1576
1577 mFontSubstitutes.Clear();
1578 mNonExistingFonts.Clear();
1579
1580 RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
1581
1582 hr = factory->GetGdiInterop(getter_AddRefs(mGDIInterop));
1583 if (FAILED(hr)) {
1584 Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1585 uint32_t(errGDIInterop));
1586 return NS_ERROR_FAILURE;
1587 }
1588
1589 QueryPerformanceCounter(&t2); // base-class/interop initialization
1590
1591 mSystemFonts = Factory::GetDWriteSystemFonts(true);
1592 NS_ASSERTION(mSystemFonts != nullptr, "GetSystemFontCollection failed!");
1593
1594 if (!mSystemFonts) {
1595 Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1596 uint32_t(errSystemFontCollection));
1597 return NS_ERROR_FAILURE;
1598 }
1599
1600 #ifdef MOZ_BUNDLED_FONTS
1601 // Get bundled fonts before the system collection, so that in the case of
1602 // duplicate names, we have recorded the family as bundled (and therefore
1603 // available regardless of visibility settings).
1604 // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an
1605 // explicit value of 0 (off) will disable them.
1606 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1607 TimeStamp start = TimeStamp::Now();
1608 mBundledFonts = CreateBundledFontsCollection(factory);
1609 if (mBundledFonts) {
1610 GetFontsFromCollection(mBundledFonts);
1611 }
1612 TimeStamp end = TimeStamp::Now();
1613 Telemetry::Accumulate(Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
1614 (end - start).ToMilliseconds());
1615 }
1616 #endif
1617 const uint32_t kBundledCount = mFontFamilies.Count();
1618
1619 QueryPerformanceCounter(&t3); // system font collection
1620
1621 GetFontsFromCollection(mSystemFonts);
1622
1623 // if no fonts found, something is out of whack, bail and use GDI backend
1624 NS_ASSERTION(mFontFamilies.Count() > kBundledCount,
1625 "no fonts found in the system fontlist -- holy crap batman!");
1626 if (mFontFamilies.Count() == kBundledCount) {
1627 Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
1628 uint32_t(errNoFonts));
1629 return NS_ERROR_FAILURE;
1630 }
1631
1632 QueryPerformanceCounter(&t4); // iterate over system fonts
1633
1634 mOtherFamilyNamesInitialized = true;
1635 GetFontSubstitutes();
1636
1637 // bug 642093 - DirectWrite does not support old bitmap (.fon)
1638 // font files, but a few of these such as "Courier" and "MS Sans Serif"
1639 // are frequently specified in shoddy CSS, without appropriate fallbacks.
1640 // By mapping these to TrueType equivalents, we provide better consistency
1641 // with both pre-DW systems and with IE9, which appears to do the same.
1642 GetDirectWriteSubstitutes();
1643
1644 // bug 551313 - DirectWrite creates a Gill Sans family out of
1645 // poorly named members of the Gill Sans MT family containing
1646 // only Ultra Bold weights. This causes big problems for pages
1647 // using Gill Sans which is usually only available on OSX
1648
1649 nsAutoCString nameGillSans("Gill Sans");
1650 nsAutoCString nameGillSansMT("Gill Sans MT");
1651 BuildKeyNameFromFontName(nameGillSans);
1652 BuildKeyNameFromFontName(nameGillSansMT);
1653
1654 gfxFontFamily* gillSansFamily = mFontFamilies.GetWeak(nameGillSans);
1655 gfxFontFamily* gillSansMTFamily = mFontFamilies.GetWeak(nameGillSansMT);
1656
1657 if (gillSansFamily && gillSansMTFamily) {
1658 gillSansFamily->FindStyleVariations();
1659 nsTArray<RefPtr<gfxFontEntry> >& faces = gillSansFamily->GetFontList();
1660 uint32_t i;
1661
1662 bool allUltraBold = true;
1663 for (i = 0; i < faces.Length(); i++) {
1664 // does the face have 'Ultra Bold' in the name?
1665 if (faces[i]->Name().Find("Ultra Bold"_ns) == -1) {
1666 allUltraBold = false;
1667 break;
1668 }
1669 }
1670
1671 // if all the Gill Sans faces are Ultra Bold ==> move faces
1672 // for Gill Sans into Gill Sans MT family
1673 if (allUltraBold) {
1674 // add faces to Gill Sans MT
1675 for (i = 0; i < faces.Length(); i++) {
1676 // change the entry's family name to match its adoptive family
1677 faces[i]->mFamilyName = gillSansMTFamily->Name();
1678 gillSansMTFamily->AddFontEntry(faces[i]);
1679
1680 if (LOG_FONTLIST_ENABLED()) {
1681 gfxFontEntry* fe = faces[i];
1682 nsAutoCString weightString;
1683 fe->Weight().ToString(weightString);
1684 LOG_FONTLIST(
1685 ("(fontlist) moved (%s) to family (%s)"
1686 " with style: %s weight: %s stretch: %d",
1687 fe->Name().get(), gillSansMTFamily->Name().get(),
1688 (fe->IsItalic()) ? "italic"
1689 : (fe->IsOblique() ? "oblique" : "normal"),
1690 weightString.get(), fe->Stretch()));
1691 }
1692 }
1693
1694 // remove Gills Sans
1695 mFontFamilies.Remove(nameGillSans);
1696 }
1697 }
1698
1699 nsAutoCString classicFamilies;
1700 nsresult rv = Preferences::GetCString(
1701 "gfx.font_rendering.cleartype_params.force_gdi_classic_for_families",
1702 classicFamilies);
1703 if (NS_SUCCEEDED(rv)) {
1704 nsCCharSeparatedTokenizer tokenizer(classicFamilies, ',');
1705 while (tokenizer.hasMoreTokens()) {
1706 nsAutoCString name(tokenizer.nextToken());
1707 BuildKeyNameFromFontName(name);
1708 gfxFontFamily* family = mFontFamilies.GetWeak(name);
1709 if (family) {
1710 static_cast<gfxDWriteFontFamily*>(family)->SetForceGDIClassic(true);
1711 }
1712 }
1713 }
1714 mForceGDIClassicMaxFontSize = Preferences::GetInt(
1715 "gfx.font_rendering.cleartype_params.force_gdi_classic_max_size",
1716 mForceGDIClassicMaxFontSize);
1717
1718 GetPrefsAndStartLoader();
1719
1720 QueryPerformanceCounter(&t5); // misc initialization
1721
1722 if (LOG_FONTINIT_ENABLED()) {
1723 // determine dwrite version
1724 nsAutoString dwriteVers;
1725 gfxWindowsPlatform::GetDLLVersion(L"dwrite.dll", dwriteVers);
1726 LOG_FONTINIT(("(fontinit) Start: %s %s\n", nowDate, nowTime));
1727 LOG_FONTINIT(("(fontinit) Uptime: %9.3f s\n", upTime / 1000));
1728 LOG_FONTINIT(("(fontinit) dwrite version: %s\n",
1729 NS_ConvertUTF16toUTF8(dwriteVers).get()));
1730 }
1731
1732 elapsedTime = (t5.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
1733 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_TOTAL,
1734 elapsedTime);
1735 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COUNT,
1736 mSystemFonts->GetFontFamilyCount());
1737 LOG_FONTINIT((
1738 "(fontinit) Total time in InitFontList: %9.3f ms (families: %d, %s)\n",
1739 elapsedTime, mSystemFonts->GetFontFamilyCount(),
1740 (mGDIFontTableAccess ? "gdi table access" : "dwrite table access")));
1741
1742 elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
1743 LOG_FONTINIT(
1744 ("(fontinit) --- base/interop obj initialization init: %9.3f ms\n",
1745 elapsedTime));
1746
1747 elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart;
1748 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COLLECT,
1749 elapsedTime);
1750 LOG_FONTINIT(
1751 ("(fontinit) --- GetSystemFontCollection: %9.3f ms\n", elapsedTime));
1752
1753 elapsedTime = (t4.QuadPart - t3.QuadPart) * 1000.0 / frequency.QuadPart;
1754 LOG_FONTINIT(
1755 ("(fontinit) --- iterate over families: %9.3f ms\n", elapsedTime));
1756
1757 elapsedTime = (t5.QuadPart - t4.QuadPart) * 1000.0 / frequency.QuadPart;
1758 LOG_FONTINIT(
1759 ("(fontinit) --- misc initialization: %9.3f ms\n", elapsedTime));
1760
1761 return NS_OK;
1762 }
1763
GetFontsFromCollection(IDWriteFontCollection * aCollection)1764 void gfxDWriteFontList::GetFontsFromCollection(
1765 IDWriteFontCollection* aCollection) {
1766 for (UINT32 i = 0; i < aCollection->GetFontFamilyCount(); i++) {
1767 RefPtr<IDWriteFontFamily> family;
1768 aCollection->GetFontFamily(i, getter_AddRefs(family));
1769
1770 RefPtr<IDWriteLocalizedStrings> names;
1771 HRESULT hr = family->GetFamilyNames(getter_AddRefs(names));
1772 if (FAILED(hr)) {
1773 continue;
1774 }
1775
1776 nsAutoCString name;
1777 if (!GetEnglishOrFirstName(name, names)) {
1778 continue;
1779 }
1780 nsAutoCString familyName(
1781 name); // keep a copy before we lowercase it as a key
1782
1783 BuildKeyNameFromFontName(name);
1784
1785 RefPtr<gfxFontFamily> fam;
1786
1787 if (mFontFamilies.GetWeak(name)) {
1788 continue;
1789 }
1790
1791 FontVisibility visibility = aCollection == mSystemFonts
1792 ? GetVisibilityForFamily(familyName)
1793 : FontVisibility::Base;
1794
1795 fam = new gfxDWriteFontFamily(familyName, visibility, family,
1796 aCollection == mSystemFonts);
1797 if (!fam) {
1798 continue;
1799 }
1800
1801 if (mBadUnderlineFamilyNames.ContainsSorted(name)) {
1802 fam->SetBadUnderlineFamily();
1803 }
1804 mFontFamilies.InsertOrUpdate(name, RefPtr{fam});
1805
1806 // now add other family name localizations, if present
1807 uint32_t nameCount = names->GetCount();
1808 uint32_t nameIndex;
1809
1810 if (nameCount > 1) {
1811 UINT32 englishIdx = 0;
1812 BOOL exists;
1813 // if this fails/doesn't exist, we'll have used name index 0,
1814 // so that's the one we'll want to skip here
1815 names->FindLocaleName(L"en-us", &englishIdx, &exists);
1816
1817 for (nameIndex = 0; nameIndex < nameCount; nameIndex++) {
1818 UINT32 nameLen;
1819 AutoTArray<WCHAR, 32> localizedName;
1820
1821 // only add other names
1822 if (nameIndex == englishIdx) {
1823 continue;
1824 }
1825
1826 hr = names->GetStringLength(nameIndex, &nameLen);
1827 if (FAILED(hr)) {
1828 continue;
1829 }
1830
1831 if (!localizedName.SetLength(nameLen + 1, fallible)) {
1832 continue;
1833 }
1834
1835 hr = names->GetString(nameIndex, localizedName.Elements(), nameLen + 1);
1836 if (FAILED(hr)) {
1837 continue;
1838 }
1839
1840 NS_ConvertUTF16toUTF8 locName(localizedName.Elements());
1841
1842 if (!familyName.Equals(locName)) {
1843 AddOtherFamilyName(fam, locName);
1844 }
1845 }
1846 }
1847
1848 // at this point, all family names have been read in
1849 fam->SetOtherFamilyNamesInitialized();
1850 }
1851 }
1852
RemoveCharsetFromFontSubstitute(nsACString & aName)1853 static void RemoveCharsetFromFontSubstitute(nsACString& aName) {
1854 int32_t comma = aName.FindChar(',');
1855 if (comma >= 0) aName.Truncate(comma);
1856 }
1857
1858 #define MAX_VALUE_NAME 512
1859 #define MAX_VALUE_DATA 512
1860
GetFontSubstitutes()1861 nsresult gfxDWriteFontList::GetFontSubstitutes() {
1862 HKEY hKey;
1863 DWORD i, rv, lenAlias, lenActual, valueType;
1864 WCHAR aliasName[MAX_VALUE_NAME];
1865 WCHAR actualName[MAX_VALUE_DATA];
1866
1867 if (RegOpenKeyExW(
1868 HKEY_LOCAL_MACHINE,
1869 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
1870 0, KEY_READ, &hKey) != ERROR_SUCCESS) {
1871 return NS_ERROR_FAILURE;
1872 }
1873
1874 for (i = 0, rv = ERROR_SUCCESS; rv != ERROR_NO_MORE_ITEMS; i++) {
1875 aliasName[0] = 0;
1876 lenAlias = ArrayLength(aliasName);
1877 actualName[0] = 0;
1878 lenActual = sizeof(actualName);
1879 rv = RegEnumValueW(hKey, i, aliasName, &lenAlias, nullptr, &valueType,
1880 (LPBYTE)actualName, &lenActual);
1881
1882 if (rv != ERROR_SUCCESS || valueType != REG_SZ || lenAlias == 0) {
1883 continue;
1884 }
1885
1886 if (aliasName[0] == WCHAR('@')) {
1887 continue;
1888 }
1889
1890 NS_ConvertUTF16toUTF8 substituteName((char16_t*)aliasName);
1891 NS_ConvertUTF16toUTF8 actualFontName((char16_t*)actualName);
1892 RemoveCharsetFromFontSubstitute(substituteName);
1893 BuildKeyNameFromFontName(substituteName);
1894 RemoveCharsetFromFontSubstitute(actualFontName);
1895 BuildKeyNameFromFontName(actualFontName);
1896 if (SharedFontList()) {
1897 // Skip substitution if the original font is available, unless the option
1898 // to apply substitutions unconditionally is enabled.
1899 if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) {
1900 // Font substitutions are recorded for the canonical family names; we
1901 // don't need FindFamily to consider localized aliases when searching.
1902 if (SharedFontList()->FindFamily(substituteName,
1903 /*aPrimaryNameOnly*/ true)) {
1904 continue;
1905 }
1906 }
1907 if (SharedFontList()->FindFamily(actualFontName,
1908 /*aPrimaryNameOnly*/ true)) {
1909 mSubstitutions.InsertOrUpdate(substituteName,
1910 MakeUnique<nsCString>(actualFontName));
1911 } else if (mSubstitutions.Get(actualFontName)) {
1912 mSubstitutions.InsertOrUpdate(
1913 substituteName,
1914 MakeUnique<nsCString>(*mSubstitutions.Get(actualFontName)));
1915 } else {
1916 mNonExistingFonts.AppendElement(substituteName);
1917 }
1918 } else {
1919 gfxFontFamily* ff;
1920 if (!actualFontName.IsEmpty() &&
1921 (ff = mFontFamilies.GetWeak(actualFontName))) {
1922 mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff});
1923 } else {
1924 mNonExistingFonts.AppendElement(substituteName);
1925 }
1926 }
1927 }
1928 return NS_OK;
1929 }
1930
1931 struct FontSubstitution {
1932 const char* aliasName;
1933 const char* actualName;
1934 };
1935
1936 static const FontSubstitution sDirectWriteSubs[] = {
1937 {"MS Sans Serif", "Microsoft Sans Serif"},
1938 {"MS Serif", "Times New Roman"},
1939 {"Courier", "Courier New"},
1940 {"Small Fonts", "Arial"},
1941 {"Roman", "Times New Roman"},
1942 {"Script", "Mistral"}};
1943
GetDirectWriteSubstitutes()1944 void gfxDWriteFontList::GetDirectWriteSubstitutes() {
1945 for (uint32_t i = 0; i < ArrayLength(sDirectWriteSubs); ++i) {
1946 const FontSubstitution& sub(sDirectWriteSubs[i]);
1947 nsAutoCString substituteName(sub.aliasName);
1948 BuildKeyNameFromFontName(substituteName);
1949 if (SharedFontList()) {
1950 // Skip substitution if the original font is available, unless the option
1951 // to apply substitutions unconditionally is enabled.
1952 if (!StaticPrefs::gfx_windows_font_substitutes_always_AtStartup()) {
1953 // We don't need FindFamily to consider localized aliases when searching
1954 // for the DirectWrite substitutes, we know the canonical names.
1955 if (SharedFontList()->FindFamily(substituteName,
1956 /*aPrimaryNameOnly*/ true)) {
1957 continue;
1958 }
1959 }
1960 nsAutoCString actualFontName(sub.actualName);
1961 BuildKeyNameFromFontName(actualFontName);
1962 if (SharedFontList()->FindFamily(actualFontName,
1963 /*aPrimaryNameOnly*/ true)) {
1964 mSubstitutions.InsertOrUpdate(substituteName,
1965 MakeUnique<nsCString>(actualFontName));
1966 } else {
1967 mNonExistingFonts.AppendElement(substituteName);
1968 }
1969 } else {
1970 if (nullptr != mFontFamilies.GetWeak(substituteName)) {
1971 // don't do the substitution if user actually has a usable font
1972 // with this name installed
1973 continue;
1974 }
1975 nsAutoCString actualFontName(sub.actualName);
1976 BuildKeyNameFromFontName(actualFontName);
1977 gfxFontFamily* ff;
1978 if (nullptr != (ff = mFontFamilies.GetWeak(actualFontName))) {
1979 mFontSubstitutes.InsertOrUpdate(substituteName, RefPtr{ff});
1980 } else {
1981 mNonExistingFonts.AppendElement(substituteName);
1982 }
1983 }
1984 }
1985 }
1986
FindAndAddFamilies(StyleGenericFontFamily aGeneric,const nsACString & aFamily,nsTArray<FamilyAndGeneric> * aOutput,FindFamiliesFlags aFlags,gfxFontStyle * aStyle,nsAtom * aLanguage,gfxFloat aDevToCssSize)1987 bool gfxDWriteFontList::FindAndAddFamilies(
1988 StyleGenericFontFamily aGeneric, const nsACString& aFamily,
1989 nsTArray<FamilyAndGeneric>* aOutput, FindFamiliesFlags aFlags,
1990 gfxFontStyle* aStyle, nsAtom* aLanguage, gfxFloat aDevToCssSize) {
1991 nsAutoCString keyName(aFamily);
1992 BuildKeyNameFromFontName(keyName);
1993
1994 if (SharedFontList()) {
1995 nsACString* subst = mSubstitutions.Get(keyName);
1996 if (subst) {
1997 keyName = *subst;
1998 }
1999 } else {
2000 gfxFontFamily* ff = mFontSubstitutes.GetWeak(keyName);
2001 if (ff) {
2002 aOutput->AppendElement(FamilyAndGeneric(ff, aGeneric));
2003 return true;
2004 }
2005 }
2006
2007 if (mNonExistingFonts.Contains(keyName)) {
2008 return false;
2009 }
2010
2011 return gfxPlatformFontList::FindAndAddFamilies(
2012 aGeneric, keyName, aOutput, aFlags, aStyle, aLanguage, aDevToCssSize);
2013 }
2014
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const2015 void gfxDWriteFontList::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
2016 FontListSizes* aSizes) const {
2017 gfxPlatformFontList::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
2018
2019 // We are a singleton, so include the font loader singleton's memory.
2020 MOZ_ASSERT(static_cast<const gfxPlatformFontList*>(this) ==
2021 gfxPlatformFontList::PlatformFontList());
2022 gfxDWriteFontFileLoader* loader = static_cast<gfxDWriteFontFileLoader*>(
2023 gfxDWriteFontFileLoader::Instance());
2024 aSizes->mLoaderSize += loader->SizeOfIncludingThis(aMallocSizeOf);
2025
2026 aSizes->mFontListSize +=
2027 SizeOfFontFamilyTableExcludingThis(mFontSubstitutes, aMallocSizeOf);
2028
2029 aSizes->mFontListSize +=
2030 mNonExistingFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
2031 for (uint32_t i = 0; i < mNonExistingFonts.Length(); ++i) {
2032 aSizes->mFontListSize +=
2033 mNonExistingFonts[i].SizeOfExcludingThisIfUnshared(aMallocSizeOf);
2034 }
2035 }
2036
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontListSizes * aSizes) const2037 void gfxDWriteFontList::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
2038 FontListSizes* aSizes) const {
2039 aSizes->mFontListSize += aMallocSizeOf(this);
2040 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
2041 }
2042
GetFamilyName(IDWriteFont * aFont,nsCString & aFamilyName)2043 static HRESULT GetFamilyName(IDWriteFont* aFont, nsCString& aFamilyName) {
2044 HRESULT hr;
2045 RefPtr<IDWriteFontFamily> family;
2046
2047 // clean out previous value
2048 aFamilyName.Truncate();
2049
2050 hr = aFont->GetFontFamily(getter_AddRefs(family));
2051 if (FAILED(hr)) {
2052 return hr;
2053 }
2054
2055 RefPtr<IDWriteLocalizedStrings> familyNames;
2056
2057 hr = family->GetFamilyNames(getter_AddRefs(familyNames));
2058 if (FAILED(hr)) {
2059 return hr;
2060 }
2061
2062 if (!GetEnglishOrFirstName(aFamilyName, familyNames)) {
2063 return E_FAIL;
2064 }
2065
2066 return S_OK;
2067 }
2068
2069 // bug 705594 - the method below doesn't actually do any "drawing", it's only
2070 // used to invoke the DirectWrite layout engine to determine the fallback font
2071 // for a given character.
2072
DrawGlyphRun(void * clientDrawingContext,FLOAT baselineOriginX,FLOAT baselineOriginY,DWRITE_MEASURING_MODE measuringMode,DWRITE_GLYPH_RUN const * glyphRun,DWRITE_GLYPH_RUN_DESCRIPTION const * glyphRunDescription,IUnknown * clientDrawingEffect)2073 IFACEMETHODIMP DWriteFontFallbackRenderer::DrawGlyphRun(
2074 void* clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY,
2075 DWRITE_MEASURING_MODE measuringMode, DWRITE_GLYPH_RUN const* glyphRun,
2076 DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
2077 IUnknown* clientDrawingEffect) {
2078 if (!mSystemFonts) {
2079 return E_FAIL;
2080 }
2081
2082 HRESULT hr = S_OK;
2083
2084 RefPtr<IDWriteFont> font;
2085 hr = mSystemFonts->GetFontFromFontFace(glyphRun->fontFace,
2086 getter_AddRefs(font));
2087 if (FAILED(hr)) {
2088 return hr;
2089 }
2090
2091 // copy the family name
2092 hr = GetFamilyName(font, mFamilyName);
2093 if (FAILED(hr)) {
2094 return hr;
2095 }
2096
2097 // Arial is used as the default fallback font
2098 // so if it matches ==> no font found
2099 if (mFamilyName.EqualsLiteral("Arial")) {
2100 mFamilyName.Truncate();
2101 return E_FAIL;
2102 }
2103 return hr;
2104 }
2105
PlatformGlobalFontFallback(const uint32_t aCh,Script aRunScript,const gfxFontStyle * aMatchStyle,FontFamily & aMatchedFamily)2106 gfxFontEntry* gfxDWriteFontList::PlatformGlobalFontFallback(
2107 const uint32_t aCh, Script aRunScript, const gfxFontStyle* aMatchStyle,
2108 FontFamily& aMatchedFamily) {
2109 HRESULT hr;
2110
2111 RefPtr<IDWriteFactory> dwFactory = Factory::GetDWriteFactory();
2112 if (!dwFactory) {
2113 return nullptr;
2114 }
2115
2116 // initialize fallback renderer
2117 if (!mFallbackRenderer) {
2118 mFallbackRenderer = new DWriteFontFallbackRenderer(dwFactory);
2119 }
2120 if (!mFallbackRenderer->IsValid()) {
2121 return nullptr;
2122 }
2123
2124 // initialize text format
2125 if (!mFallbackFormat) {
2126 hr = dwFactory->CreateTextFormat(
2127 L"Arial", nullptr, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL,
2128 DWRITE_FONT_STRETCH_NORMAL, 72.0f, L"en-us",
2129 getter_AddRefs(mFallbackFormat));
2130 if (FAILED(hr)) {
2131 return nullptr;
2132 }
2133 }
2134
2135 // set up string with fallback character
2136 wchar_t str[16];
2137 uint32_t strLen;
2138
2139 if (IS_IN_BMP(aCh)) {
2140 str[0] = static_cast<wchar_t>(aCh);
2141 str[1] = 0;
2142 strLen = 1;
2143 } else {
2144 str[0] = static_cast<wchar_t>(H_SURROGATE(aCh));
2145 str[1] = static_cast<wchar_t>(L_SURROGATE(aCh));
2146 str[2] = 0;
2147 strLen = 2;
2148 }
2149
2150 // set up layout
2151 RefPtr<IDWriteTextLayout> fallbackLayout;
2152
2153 hr = dwFactory->CreateTextLayout(str, strLen, mFallbackFormat, 200.0f, 200.0f,
2154 getter_AddRefs(fallbackLayout));
2155 if (FAILED(hr)) {
2156 return nullptr;
2157 }
2158
2159 // call the draw method to invoke the DirectWrite layout functions
2160 // which determine the fallback font
2161 MOZ_SEH_TRY {
2162 hr = fallbackLayout->Draw(nullptr, mFallbackRenderer, 50.0f, 50.0f);
2163 if (FAILED(hr)) {
2164 return nullptr;
2165 }
2166 }
2167 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
2168 gfxCriticalNote << "Exception occurred during DWrite font fallback";
2169 return nullptr;
2170 }
2171
2172 FontFamily family = FindFamily(mFallbackRenderer->FallbackFamilyName());
2173 if (!family.IsNull()) {
2174 gfxFontEntry* fontEntry = nullptr;
2175 if (family.mIsShared) {
2176 auto face =
2177 family.mShared->FindFaceForStyle(SharedFontList(), *aMatchStyle);
2178 if (face) {
2179 fontEntry = GetOrCreateFontEntry(face, family.mShared);
2180 }
2181 } else {
2182 fontEntry = family.mUnshared->FindFontForStyle(*aMatchStyle);
2183 }
2184 if (fontEntry && fontEntry->HasCharacter(aCh)) {
2185 aMatchedFamily = family;
2186 return fontEntry;
2187 }
2188 Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, true);
2189 }
2190
2191 return nullptr;
2192 }
2193
2194 // used to load system-wide font info on off-main thread
2195 class DirectWriteFontInfo : public FontInfoData {
2196 public:
DirectWriteFontInfo(bool aLoadOtherNames,bool aLoadFaceNames,bool aLoadCmaps,IDWriteFontCollection * aSystemFonts,IDWriteFontCollection * aBundledFonts)2197 DirectWriteFontInfo(bool aLoadOtherNames, bool aLoadFaceNames,
2198 bool aLoadCmaps, IDWriteFontCollection* aSystemFonts
2199 #ifdef MOZ_BUNDLED_FONTS
2200 ,
2201 IDWriteFontCollection* aBundledFonts
2202 #endif
2203 )
2204 : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps),
2205 mSystemFonts(aSystemFonts)
2206 #ifdef MOZ_BUNDLED_FONTS
2207 ,
2208 mBundledFonts(aBundledFonts)
2209 #endif
2210 {
2211 }
2212
2213 virtual ~DirectWriteFontInfo() = default;
2214
2215 // loads font data for all members of a given family
2216 virtual void LoadFontFamilyData(const nsACString& aFamilyName);
2217
2218 private:
2219 RefPtr<IDWriteFontCollection> mSystemFonts;
2220 #ifdef MOZ_BUNDLED_FONTS
2221 RefPtr<IDWriteFontCollection> mBundledFonts;
2222 #endif
2223 };
2224
LoadFontFamilyData(const nsACString & aFamilyName)2225 void DirectWriteFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
2226 // lookup the family
2227 NS_ConvertUTF8toUTF16 famName(aFamilyName);
2228
2229 HRESULT hr;
2230 BOOL exists = false;
2231
2232 uint32_t index;
2233 RefPtr<IDWriteFontFamily> family;
2234 hr = mSystemFonts->FindFamilyName((const wchar_t*)famName.get(), &index,
2235 &exists);
2236 if (SUCCEEDED(hr) && exists) {
2237 mSystemFonts->GetFontFamily(index, getter_AddRefs(family));
2238 if (!family) {
2239 return;
2240 }
2241 }
2242
2243 #ifdef MOZ_BUNDLED_FONTS
2244 if (!family && mBundledFonts) {
2245 hr = mBundledFonts->FindFamilyName((const wchar_t*)famName.get(), &index,
2246 &exists);
2247 if (SUCCEEDED(hr) && exists) {
2248 mBundledFonts->GetFontFamily(index, getter_AddRefs(family));
2249 }
2250 }
2251 #endif
2252
2253 if (!family) {
2254 return;
2255 }
2256
2257 // later versions of DirectWrite support querying the fullname/psname
2258 bool loadFaceNamesUsingDirectWrite = mLoadFaceNames;
2259
2260 for (uint32_t i = 0; i < family->GetFontCount(); i++) {
2261 // get the font
2262 RefPtr<IDWriteFont> dwFont;
2263 hr = family->GetFont(i, getter_AddRefs(dwFont));
2264 if (FAILED(hr)) {
2265 // This should never happen.
2266 NS_WARNING("Failed to get existing font from family.");
2267 continue;
2268 }
2269
2270 if (dwFont->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) {
2271 // We don't want these in the font list; we'll apply simulations
2272 // on the fly when appropriate.
2273 continue;
2274 }
2275
2276 mLoadStats.fonts++;
2277
2278 // get the name of the face
2279 nsCString fullID(aFamilyName);
2280 nsAutoCString fontName;
2281 hr = GetDirectWriteFontName(dwFont, fontName);
2282 if (FAILED(hr)) {
2283 continue;
2284 }
2285 fullID.Append(' ');
2286 fullID.Append(fontName);
2287
2288 FontFaceData fontData;
2289 bool haveData = true;
2290 RefPtr<IDWriteFontFace> dwFontFace;
2291
2292 if (mLoadFaceNames) {
2293 // try to load using DirectWrite first
2294 if (loadFaceNamesUsingDirectWrite) {
2295 hr =
2296 GetDirectWriteFaceName(dwFont, PSNAME_ID, fontData.mPostscriptName);
2297 if (FAILED(hr)) {
2298 loadFaceNamesUsingDirectWrite = false;
2299 }
2300 hr = GetDirectWriteFaceName(dwFont, FULLNAME_ID, fontData.mFullName);
2301 if (FAILED(hr)) {
2302 loadFaceNamesUsingDirectWrite = false;
2303 }
2304 }
2305
2306 // if DirectWrite read fails, load directly from name table
2307 if (!loadFaceNamesUsingDirectWrite) {
2308 hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
2309 if (SUCCEEDED(hr)) {
2310 uint32_t kNAME =
2311 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
2312 const char* nameData;
2313 BOOL exists;
2314 void* ctx;
2315 uint32_t nameSize;
2316
2317 hr = dwFontFace->TryGetFontTable(kNAME, (const void**)&nameData,
2318 &nameSize, &ctx, &exists);
2319 if (SUCCEEDED(hr) && nameData && nameSize > 0) {
2320 MOZ_SEH_TRY {
2321 gfxFontUtils::ReadCanonicalName(nameData, nameSize,
2322 gfxFontUtils::NAME_ID_FULL,
2323 fontData.mFullName);
2324 gfxFontUtils::ReadCanonicalName(nameData, nameSize,
2325 gfxFontUtils::NAME_ID_POSTSCRIPT,
2326 fontData.mPostscriptName);
2327 }
2328 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
2329 gfxCriticalNote << "Exception occurred reading names for "
2330 << PromiseFlatCString(aFamilyName).get();
2331 }
2332 dwFontFace->ReleaseFontTable(ctx);
2333 }
2334 }
2335 }
2336
2337 haveData =
2338 !fontData.mPostscriptName.IsEmpty() || !fontData.mFullName.IsEmpty();
2339 if (haveData) {
2340 mLoadStats.facenames++;
2341 }
2342 }
2343
2344 // cmaps
2345 if (mLoadCmaps) {
2346 if (!dwFontFace) {
2347 hr = dwFont->CreateFontFace(getter_AddRefs(dwFontFace));
2348 if (!SUCCEEDED(hr)) {
2349 continue;
2350 }
2351 }
2352
2353 uint32_t kCMAP =
2354 NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
2355 const uint8_t* cmapData;
2356 BOOL exists;
2357 void* ctx;
2358 uint32_t cmapSize;
2359
2360 hr = dwFontFace->TryGetFontTable(kCMAP, (const void**)&cmapData,
2361 &cmapSize, &ctx, &exists);
2362
2363 if (SUCCEEDED(hr) && exists) {
2364 bool cmapLoaded = false;
2365 RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
2366 uint32_t offset;
2367 MOZ_SEH_TRY {
2368 if (cmapData && cmapSize > 0 &&
2369 NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData, cmapSize, *charmap,
2370 offset))) {
2371 fontData.mCharacterMap = charmap;
2372 fontData.mUVSOffset = offset;
2373 cmapLoaded = true;
2374 mLoadStats.cmaps++;
2375 }
2376 }
2377 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
2378 gfxCriticalNote << "Exception occurred reading cmaps for "
2379 << PromiseFlatCString(aFamilyName).get();
2380 }
2381 dwFontFace->ReleaseFontTable(ctx);
2382 haveData = haveData || cmapLoaded;
2383 }
2384 }
2385
2386 // if have data, load
2387 if (haveData) {
2388 mFontFaceData.InsertOrUpdate(fullID, fontData);
2389 }
2390 }
2391 }
2392
CreateFontInfoData()2393 already_AddRefed<FontInfoData> gfxDWriteFontList::CreateFontInfoData() {
2394 bool loadCmaps = !UsesSystemFallback() ||
2395 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
2396
2397 RefPtr<DirectWriteFontInfo> fi = new DirectWriteFontInfo(
2398 false, NeedFullnamePostscriptNames(), loadCmaps, mSystemFonts
2399 #ifdef MOZ_BUNDLED_FONTS
2400 ,
2401 mBundledFonts
2402 #endif
2403 );
2404
2405 return fi.forget();
2406 }
2407
CreateFontFamily(const nsACString & aName,FontVisibility aVisibility) const2408 gfxFontFamily* gfxDWriteFontList::CreateFontFamily(
2409 const nsACString& aName, FontVisibility aVisibility) const {
2410 return new gfxDWriteFontFamily(aName, aVisibility, nullptr);
2411 }
2412
2413 #ifdef MOZ_BUNDLED_FONTS
2414
2415 # define IMPL_QI_FOR_DWRITE(_interface) \
2416 public: \
2417 IFACEMETHOD(QueryInterface)(IID const& riid, void** ppvObject) { \
2418 if (__uuidof(_interface) == riid) { \
2419 *ppvObject = this; \
2420 } else if (__uuidof(IUnknown) == riid) { \
2421 *ppvObject = this; \
2422 } else { \
2423 *ppvObject = nullptr; \
2424 return E_NOINTERFACE; \
2425 } \
2426 this->AddRef(); \
2427 return S_OK; \
2428 }
2429
2430 class BundledFontFileEnumerator : public IDWriteFontFileEnumerator {
2431 IMPL_QI_FOR_DWRITE(IDWriteFontFileEnumerator)
2432
2433 NS_INLINE_DECL_REFCOUNTING(BundledFontFileEnumerator)
2434
2435 public:
2436 BundledFontFileEnumerator(IDWriteFactory* aFactory, nsIFile* aFontDir);
2437
2438 IFACEMETHODIMP MoveNext(BOOL* hasCurrentFile);
2439
2440 IFACEMETHODIMP GetCurrentFontFile(IDWriteFontFile** fontFile);
2441
2442 private:
2443 BundledFontFileEnumerator() = delete;
2444 BundledFontFileEnumerator(const BundledFontFileEnumerator&) = delete;
2445 BundledFontFileEnumerator& operator=(const BundledFontFileEnumerator&) =
2446 delete;
2447 virtual ~BundledFontFileEnumerator() = default;
2448
2449 RefPtr<IDWriteFactory> mFactory;
2450
2451 nsCOMPtr<nsIFile> mFontDir;
2452 nsCOMPtr<nsIDirectoryEnumerator> mEntries;
2453 nsCOMPtr<nsISupports> mCurrent;
2454 };
2455
BundledFontFileEnumerator(IDWriteFactory * aFactory,nsIFile * aFontDir)2456 BundledFontFileEnumerator::BundledFontFileEnumerator(IDWriteFactory* aFactory,
2457 nsIFile* aFontDir)
2458 : mFactory(aFactory), mFontDir(aFontDir) {
2459 mFontDir->GetDirectoryEntries(getter_AddRefs(mEntries));
2460 }
2461
2462 IFACEMETHODIMP
MoveNext(BOOL * aHasCurrentFile)2463 BundledFontFileEnumerator::MoveNext(BOOL* aHasCurrentFile) {
2464 bool hasMore = false;
2465 if (mEntries) {
2466 if (NS_SUCCEEDED(mEntries->HasMoreElements(&hasMore)) && hasMore) {
2467 if (NS_SUCCEEDED(mEntries->GetNext(getter_AddRefs(mCurrent)))) {
2468 hasMore = true;
2469 }
2470 }
2471 }
2472 *aHasCurrentFile = hasMore;
2473 return S_OK;
2474 }
2475
2476 IFACEMETHODIMP
GetCurrentFontFile(IDWriteFontFile ** aFontFile)2477 BundledFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** aFontFile) {
2478 nsCOMPtr<nsIFile> file = do_QueryInterface(mCurrent);
2479 if (!file) {
2480 return E_FAIL;
2481 }
2482 nsString path;
2483 if (NS_FAILED(file->GetPath(path))) {
2484 return E_FAIL;
2485 }
2486 return mFactory->CreateFontFileReference((const WCHAR*)path.get(), nullptr,
2487 aFontFile);
2488 }
2489
2490 class BundledFontLoader : public IDWriteFontCollectionLoader {
2491 IMPL_QI_FOR_DWRITE(IDWriteFontCollectionLoader)
2492
2493 NS_INLINE_DECL_REFCOUNTING(BundledFontLoader)
2494
2495 public:
BundledFontLoader()2496 BundledFontLoader() {}
2497
2498 IFACEMETHODIMP CreateEnumeratorFromKey(
2499 IDWriteFactory* aFactory, const void* aCollectionKey,
2500 UINT32 aCollectionKeySize,
2501 IDWriteFontFileEnumerator** aFontFileEnumerator);
2502
2503 private:
2504 BundledFontLoader(const BundledFontLoader&) = delete;
2505 BundledFontLoader& operator=(const BundledFontLoader&) = delete;
2506 virtual ~BundledFontLoader() = default;
2507 };
2508
2509 IFACEMETHODIMP
CreateEnumeratorFromKey(IDWriteFactory * aFactory,const void * aCollectionKey,UINT32 aCollectionKeySize,IDWriteFontFileEnumerator ** aFontFileEnumerator)2510 BundledFontLoader::CreateEnumeratorFromKey(
2511 IDWriteFactory* aFactory, const void* aCollectionKey,
2512 UINT32 aCollectionKeySize,
2513 IDWriteFontFileEnumerator** aFontFileEnumerator) {
2514 nsIFile* fontDir = *(nsIFile**)aCollectionKey;
2515 *aFontFileEnumerator = new BundledFontFileEnumerator(aFactory, fontDir);
2516 NS_ADDREF(*aFontFileEnumerator);
2517 return S_OK;
2518 }
2519
2520 already_AddRefed<IDWriteFontCollection>
CreateBundledFontsCollection(IDWriteFactory * aFactory)2521 gfxDWriteFontList::CreateBundledFontsCollection(IDWriteFactory* aFactory) {
2522 nsCOMPtr<nsIFile> localDir;
2523 nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
2524 if (NS_FAILED(rv)) {
2525 return nullptr;
2526 }
2527 if (NS_FAILED(localDir->Append(u"fonts"_ns))) {
2528 return nullptr;
2529 }
2530 bool isDir;
2531 if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
2532 return nullptr;
2533 }
2534
2535 RefPtr<BundledFontLoader> loader = new BundledFontLoader();
2536 if (FAILED(aFactory->RegisterFontCollectionLoader(loader))) {
2537 return nullptr;
2538 }
2539
2540 const void* key = localDir.get();
2541 RefPtr<IDWriteFontCollection> collection;
2542 HRESULT hr = aFactory->CreateCustomFontCollection(loader, &key, sizeof(key),
2543 getter_AddRefs(collection));
2544
2545 aFactory->UnregisterFontCollectionLoader(loader);
2546
2547 if (FAILED(hr)) {
2548 return nullptr;
2549 } else {
2550 return collection.forget();
2551 }
2552 }
2553
2554 #endif
2555