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
) {
54 ////////////////////////////////////////////////////////////////////////////////
55 // gfxDWriteFontFamily
57 gfxDWriteFontFamily::~gfxDWriteFontFamily() {}
59 static bool GetNameAsUtf8(nsACString
& aName
, IDWriteLocalizedStrings
* aStrings
,
61 AutoTArray
<WCHAR
, 32> name
;
63 HRESULT hr
= aStrings
->GetStringLength(aIndex
, &length
);
67 if (!name
.SetLength(length
+ 1, fallible
)) {
70 hr
= aStrings
->GetString(aIndex
, name
.Elements(), length
+ 1);
76 Substring(reinterpret_cast<const char16_t
*>(name
.Elements()),
82 static bool GetEnglishOrFirstName(nsACString
& aName
,
83 IDWriteLocalizedStrings
* aStrings
) {
84 UINT32 englishIdx
= 0;
86 HRESULT hr
= aStrings
->FindLocaleName(L
"en-us", &englishIdx
, &exists
);
87 if (FAILED(hr
) || !exists
) {
88 // Use 0 index if english is not found.
91 return GetNameAsUtf8(aName
, aStrings
, englishIdx
);
94 static HRESULT
GetDirectWriteFontName(IDWriteFont
* aFont
,
95 nsACString
& aFontName
) {
98 RefPtr
<IDWriteLocalizedStrings
> names
;
99 hr
= aFont
->GetFaceNames(getter_AddRefs(names
));
104 if (!GetEnglishOrFirstName(aFontName
, names
)) {
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
) {
121 RefPtr
<IDWriteLocalizedStrings
> infostrings
;
122 hr
= aFont
->GetInformationalStrings(aWhichName
, getter_AddRefs(infostrings
),
124 if (FAILED(hr
) || !exists
) {
128 if (!GetEnglishOrFirstName(aFontName
, infostrings
)) {
135 void gfxDWriteFontFamily::FindStyleVariations(FontInfoData
* aFontInfoData
) {
142 gfxPlatformFontList
* fp
= gfxPlatformFontList::PlatformFontList();
145 mFaceNamesInitialized
|| !fp
->NeedFullnamePostscriptNames();
146 bool fontInfoShouldHaveFaceNames
= !mFaceNamesInitialized
&&
147 fp
->NeedFullnamePostscriptNames() &&
150 for (UINT32 i
= 0; i
< mDWFamily
->GetFontCount(); i
++) {
151 RefPtr
<IDWriteFont
> font
;
152 hr
= mDWFamily
->GetFont(i
, getter_AddRefs(font
));
154 // This should never happen.
155 NS_WARNING("Failed to get existing font from family.");
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.
166 nsCString
fullID(mName
);
167 nsAutoCString faceName
;
168 hr
= GetDirectWriteFontName(font
, faceName
);
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")) {
185 gfxDWriteFontEntry
* fe
=
186 new gfxDWriteFontEntry(fullID
, font
, mIsSystemFontFamily
);
187 fe
->SetForceGDIClassic(mForceGDIClassic
);
189 fe
->SetupVariationRanges();
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
);
206 skipFaceNames
= true;
207 } else if (psname
.Length() > 0) {
208 fp
->AddPostscriptName(fe
, psname
);
211 hr
= GetDirectWriteFaceName(font
, FULLNAME_ID
, fullname
);
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
);
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
) {
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
)) {
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
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();
294 osprefs
->GetSystemLocale(locale
);
296 RefPtr
<IDWriteLocalizedStrings
> names
;
298 hr
= mDWFamily
->GetFamilyNames(getter_AddRefs(names
));
305 names
->FindLocaleName(NS_ConvertUTF8toUTF16(locale
).get(), &idx
, &exists
);
310 // Use english is localized is not found.
311 hr
= names
->FindLocaleName(L
"en-us", &idx
, &exists
);
316 // Use 0 index if english is not found.
320 AutoTArray
<WCHAR
, 32> famName
;
323 hr
= names
->GetStringLength(idx
, &length
);
328 if (!famName
.SetLength(length
+ 1, fallible
)) {
329 // Eeep - running out of memory. Unlikely to end well.
333 hr
= names
->GetString(idx
, famName
.Elements(), length
+ 1);
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();
353 void gfxDWriteFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
354 FontListSizes
* aSizes
) const {
355 gfxFontFamily::AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);
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
;
378 gfxDWriteFontEntry::~gfxDWriteFontEntry() {}
380 static bool UsingArabicOrHebrewScriptSystemLocale() {
381 LANGID langid
= PRIMARYLANGID(::GetSystemDefaultLangID());
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
)) {
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(),
421 return NS_ERROR_OUT_OF_MEMORY
;
427 RefPtr
<IDWriteFontFace
> fontFace
;
428 nsresult rv
= CreateFontFace(getter_AddRefs(fontFace
));
435 void* tableContext
= nullptr;
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
);
447 rv
= NS_ERROR_OUT_OF_MEMORY
;
451 fontFace
->ReleaseFontTable(&tableContext
);
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
463 FontTableRec(IDWriteFontFace
* aFontFace
, void* aContext
)
464 : mFontFace(aFontFace
), mContext(aContext
) {
465 MOZ_COUNT_CTOR(FontTableRec
);
469 MOZ_COUNT_DTOR(FontTableRec
);
470 mFontFace
->ReleaseFontTable(mContext
);
474 RefPtr
<IDWriteFontFace
> mFontFace
;
478 static void DestroyBlobFunc(void* aUserData
) {
479 FontTableRec
* ftr
= static_cast<FontTableRec
*>(aUserData
);
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
488 return gfxFontEntry::GetFontTable(aTag
);
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
);
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
) {
514 RefPtr
<gfxCharacterMap
> charmap
;
518 (charmap
= GetCMAPFromFontInfo(aFontInfoData
, mUVSOffset
))) {
521 uint32_t kCMAP
= TRUETYPE_TAG('c', 'm', 'a', 'p');
522 charmap
= new gfxCharacterMap();
523 AutoTable
cmapTable(this, kCMAP
);
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
);
531 rv
= NS_ERROR_NOT_AVAILABLE
;
535 mHasCmapTable
= NS_SUCCEEDED(rv
);
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
552 mCharacterMap
= charmap
;
555 mCharacterMap
= pfl
->FindCharMap(charmap
);
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()) {
567 SprintfLiteral(prefix
, "(cmapdata) name: %.220s", mName
.get());
568 charmap
->Dump(prefix
, eGfxLog_cmapdata
);
574 bool gfxDWriteFontEntry::HasVariations() {
575 if (mHasVariationsInitialized
) {
576 return mHasVariations
;
578 mHasVariationsInitialized
= true;
579 mHasVariations
= false;
581 if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
582 return mHasVariations
;
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
;
594 mHasVariations
= mFontFace5
->HasVariations();
596 return mHasVariations
;
599 void gfxDWriteFontEntry::GetVariationAxes(
600 nsTArray
<gfxFontVariationAxis
>& aAxes
) {
601 if (!HasVariations()) {
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
) {
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
) {
626 if (!(attrs
& DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE
)) {
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,
634 // Try to get a human-friendly name (may not be present)
635 RefPtr
<IDWriteLocalizedStrings
> names
;
636 resource
->GetAxisNames(i
, getter_AddRefs(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
);
661 RefPtr
<IDWriteFontFace
> fontFace
;
663 CreateFontFace(getter_AddRefs(fontFace
), nullptr, sims
, 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.
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()) {
683 CreateFontFace(getter_AddRefs(fontFace
), aFontStyle
, sims
, &vars
);
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
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
));
714 MOZ_ASSERT_UNREACHABLE("invalid font entry");
715 return NS_ERROR_FAILURE
;
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.
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
),
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.
760 for (const auto& v
: *aVariations
) {
761 DWRITE_FONT_AXIS_VALUE axisValue
= {makeDWriteAxisTag(v
.mTag
),
763 fontAxisValues
.AppendElement(axisValue
);
767 IDWriteFontFace5
* ff5
;
768 resource
->CreateFontFace(aSimulations
, fontAxisValues
.Elements(),
769 fontAxisValues
.Length(), &ff5
);
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
) {
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
803 *aFontFace
= mFontFace5
;
805 *aFontFace
= mFontFace
;
807 (*aFontFace
)->AddRef();
811 bool gfxDWriteFontEntry::InitLogFont(IDWriteFont
* aFont
, LOGFONTW
* aLogFont
) {
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
) {
831 const uint32_t kOS2Tag
= TRUETYPE_TAG('O', 'S', '/', '2');
832 hb_blob_t
* blob
= GetFontTable(kOS2Tag
);
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!)
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) {
856 hb_blob_destroy(blob
);
861 void gfxDWriteFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf
,
862 FontListSizes
* aSizes
) const {
863 gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);
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 ////////////////////////////////////////////////////////////////////////////////
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
,
891 ff
= FindFamily(aPresContext
, "Arial"_ns
);
896 // otherwise, use local default
897 NONCLIENTMETRICSW ncm
;
898 ncm
.cbSize
= sizeof(ncm
);
900 ::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, 0);
903 ff
= FindFamily(aPresContext
,
904 NS_ConvertUTF16toUTF8(ncm
.lfMessageFont
.lfFaceName
));
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
);
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());
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");
949 nsAutoString uniqueName
;
950 nsresult rv
= gfxFontUtils::MakeUniqueUserFontName(uniqueName
);
951 NS_ASSERTION(NS_SUCCEEDED(rv
), "Failed to make unique user font name");
957 DWRITE_FONT_FILE_TYPE fileType
;
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");
969 NS_ASSERTION(isSupported
, "Unsupported font file");
973 NS_ASSERTION(numFaces
== 1, "Font file does not contain exactly 1 face");
975 // We don't know how to deal with 0 faces either.
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
,
991 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
996 if (SUCCEEDED(aFace
->TryGetFontTable(kNAME
, (const void**)&data
, &size
,
997 &context
, &exists
)) &&
999 if (NS_FAILED(gfxFontUtils::ReadCanonicalName(
1000 data
, size
, gfxFontUtils::NAME_ID_POSTSCRIPT
, aName
))) {
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
;
1015 RefPtr
<IDWriteFontFamily
> family
;
1016 bool foundExpectedFamily
= false;
1017 const nsCString
& familyName
=
1018 aFamily
->DisplayName().AsString(SharedFontList());
1019 if (aFamily
->Index() < collection
->GetFontFamilyCount()) {
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
) {
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).
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
))) ||
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
) {
1058 // Retrieve the psName from the font, so we can check we've found the
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
) {
1093 if (psName
!= faceName
) {
1097 auto fe
= new gfxDWriteFontEntry(faceName
, font
, !aFamily
->IsBundled());
1098 fe
->InitializeFrom(aFace
, aFamily
);
1099 fe
->mForceGDIClassic
= aFamily
->IsForceClassic();
1100 fe
->mMayUseGDIAccess
= aFamily
->IsSimple();
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
));
1124 NS_WARNING("Failed to get existing font from family.");
1127 nsAutoCString faceName
;
1128 hr
= GetDirectWriteFontName(font
, faceName
);
1132 if (faceName
.Find("Ultra Bold"_ns
) == kNotFound
) {
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
));
1150 gfxWarning() << "Failed to get names for font-family " << i
;
1154 auto addFamily
= [&](const nsACString
& name
, bool altLocale
= false) {
1157 BuildKeyNameFromFontName(key
);
1158 bool bad
= mBadUnderlineFamilyNames
.ContainsSorted(key
);
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
;
1170 visibility
= aCollection
== mSystemFonts
? GetVisibilityForFamily(name
)
1171 : FontVisibility::Base
;
1173 aFamilies
.AppendElement(fontlist::Family::InitData(
1174 key
, name
, i
, visibility
, aCollection
!= mSystemFonts
, bad
, classic
,
1178 unsigned count
= localizedNames
->GetCount();
1180 // This is the common case: the great majority of fonts only provide an
1181 // en-US family name.
1183 if (!GetNameAsUtf8(name
, localizedNames
, 0)) {
1184 gfxWarning() << "GetNameAsUtf8 failed for index 0 in font-family " << i
;
1189 AutoTArray
<nsCString
, 4> names
;
1190 int sysLocIndex
= -1;
1191 for (unsigned index
= 0; index
< count
; ++index
) {
1193 if (!GetNameAsUtf8(name
, localizedNames
, index
)) {
1194 gfxWarning() << "GetNameAsUtf8 failed for index " << index
1195 << " in font-family " << i
;
1198 if (!names
.Contains(name
)) {
1199 if (sysLocIndex
== -1) {
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) {
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])) {
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
;
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
) {
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")) {
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.
1266 RefPtr
<gfxCharacterMap
> charmap
;
1267 if (FAILED(GetDirectWriteFaceName(dwFont
, PSNAME_ID
, name
)) ||
1269 RefPtr
<IDWriteFontFace
> dwFontFace
;
1270 if (SUCCEEDED(dwFont
->CreateFontFace(getter_AddRefs(dwFontFace
)))) {
1271 if (name
.IsEmpty()) {
1272 GetPostScriptNameFromNameTable(dwFontFace
, name
);
1275 NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
1281 if (SUCCEEDED(dwFontFace
->TryGetFontTable(
1282 kCMAP
, (const void**)&data
, &size
, &context
, &exists
)) &&
1284 charmap
= new gfxCharacterMap();
1286 gfxFontUtils::ReadCMAP((const uint8_t*)data
, size
, *charmap
,
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();
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
;
1323 RefPtr
<IDWriteFontFamily
> family
;
1324 if (FAILED(collection
->GetFontFamily(aFamily
->Index(),
1325 getter_AddRefs(family
)))) {
1326 MOZ_ASSERT_UNREACHABLE("failed to get font-family");
1329 RefPtr
<IDWriteFont
> dwFont
;
1330 if (FAILED(family
->GetFont(aFace
->mIndex
, getter_AddRefs(dwFont
)))) {
1331 MOZ_ASSERT_UNREACHABLE("failed to get font from family");
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");
1354 if (FAILED(dwFontFace
->TryGetFontTable(
1355 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')),
1356 (const void**)&data
, &size
, &context
, &exists
)) ||
1358 NS_WARNING("failed to get name table");
1362 // Try to read the name table entries, and ensure result is true if either
1364 if (FAILED(ps
) || aPSName
.IsEmpty()) {
1365 if (NS_SUCCEEDED(gfxFontUtils::ReadCanonicalName(
1366 data
, size
, gfxFontUtils::NAME_ID_POSTSCRIPT
, aPSName
))) {
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
))) {
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
);
1393 void gfxDWriteFontList::ReadFaceNamesForFamily(
1394 fontlist::Family
* aFamily
, bool aNeedFullnamePostscriptNames
) {
1395 if (!aFamily
->IsInitialized()) {
1396 if (!InitializeFamily(aFamily
)) {
1400 IDWriteFontCollection
* collection
=
1401 #ifdef MOZ_BUNDLED_FONTS
1402 aFamily
->IsBundled() ? mBundledFonts
: mSystemFonts
;
1406 RefPtr
<IDWriteFontFamily
> family
;
1407 if (FAILED(collection
->GetFontFamily(aFamily
->Index(),
1408 getter_AddRefs(family
)))) {
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
));
1423 RefPtr
<IDWriteFont
> dwFont
;
1424 if (FAILED(family
->GetFont(face
->mIndex
, getter_AddRefs(dwFont
)))) {
1427 RefPtr
<IDWriteFontFace
> dwFontFace
;
1428 if (FAILED(dwFont
->CreateFontFace(getter_AddRefs(dwFontFace
)))) {
1436 if (FAILED(dwFontFace
->TryGetFontTable(
1437 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e')),
1438 (const void**)&data
, &size
, &context
, &exists
)) ||
1444 AutoTArray
<nsCString
, 4> otherFamilyNames
;
1445 gfxFontUtils::ReadOtherFamilyNamesForFace(familyName
, data
, size
,
1446 otherFamilyNames
, false);
1447 for (const auto& alias
: otherFamilyNames
) {
1448 nsAutoCString
key(alias
);
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
{
1482 errSystemFontCollection
= 2,
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
));
1499 Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM
,
1500 uint32_t(errGDIInterop
));
1501 mSharedFontList
.reset(nullptr);
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);
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();
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",
1529 if (NS_SUCCEEDED(rv
)) {
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());
1549 SharedFontList()->SetFamilyNames(families
);
1550 GetPrefsAndStartLoader();
1553 if (!SharedFontList()->Initialized()) {
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,
1570 GetDateFormatA(LOCALE_INVARIANT
, 0, nullptr, nullptr, nowDate
, 256);
1571 upTime
= (double)GetTickCount();
1573 QueryPerformanceFrequency(&frequency
);
1574 QueryPerformanceCounter(&t1
); // start
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
));
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());
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();
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;
1674 // if all the Gill Sans faces are Ultra Bold ==> move faces
1675 // for Gill Sans into Gill Sans MT family
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
);
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",
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
);
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
,
1738 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COUNT
,
1739 mSystemFonts
->GetFontFamilyCount());
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
;
1747 ("(fontinit) --- base/interop obj initialization init: %9.3f ms\n",
1750 elapsedTime
= (t3
.QuadPart
- t2
.QuadPart
) * 1000.0 / frequency
.QuadPart
;
1751 Telemetry::Accumulate(Telemetry::DWRITEFONT_DELAYEDINITFONTLIST_COLLECT
,
1754 ("(fontinit) --- GetSystemFontCollection: %9.3f ms\n", elapsedTime
));
1756 elapsedTime
= (t4
.QuadPart
- t3
.QuadPart
) * 1000.0 / frequency
.QuadPart
;
1758 ("(fontinit) --- iterate over families: %9.3f ms\n", elapsedTime
));
1760 elapsedTime
= (t5
.QuadPart
- t4
.QuadPart
) * 1000.0 / frequency
.QuadPart
;
1762 ("(fontinit) --- misc initialization: %9.3f ms\n", elapsedTime
));
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
));
1780 if (!GetEnglishOrFirstName(name
, names
)) {
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
)) {
1794 FontVisibility visibility
= aCollection
== mSystemFonts
1795 ? GetVisibilityForFamily(familyName
)
1796 : FontVisibility::Base
;
1798 fam
= new gfxDWriteFontFamily(familyName
, visibility
, family
,
1799 aCollection
== mSystemFonts
);
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();
1813 if (nameCount
> 1) {
1814 UINT32 englishIdx
= 0;
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
++) {
1822 AutoTArray
<WCHAR
, 32> localizedName
;
1824 // only add other names
1825 if (nameIndex
== englishIdx
) {
1829 hr
= names
->GetStringLength(nameIndex
, &nameLen
);
1834 if (!localizedName
.SetLength(nameLen
+ 1, fallible
)) {
1838 hr
= names
->GetString(nameIndex
, localizedName
.Elements(), nameLen
+ 1);
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() {
1866 DWORD i
, rv
, lenAlias
, lenActual
, valueType
;
1867 WCHAR aliasName
[MAX_VALUE_NAME
];
1868 WCHAR actualName
[MAX_VALUE_DATA
];
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
++) {
1879 lenAlias
= ArrayLength(aliasName
);
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) {
1889 if (aliasName
[0] == WCHAR('@')) {
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)) {
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(
1917 MakeUnique
<nsCString
>(*mSubstitutions
.Get(actualFontName
)));
1919 mNonExistingFonts
.AppendElement(substituteName
);
1923 if (!actualFontName
.IsEmpty() &&
1924 (ff
= mFontFamilies
.GetWeak(actualFontName
))) {
1925 mFontSubstitutes
.InsertOrUpdate(substituteName
, RefPtr
{ff
});
1927 mNonExistingFonts
.AppendElement(substituteName
);
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)) {
1963 nsAutoCString
actualFontName(sub
.actualName
);
1964 BuildKeyNameFromFontName(actualFontName
);
1965 if (SharedFontList()->FindFamily(actualFontName
,
1966 /*aPrimaryNameOnly*/ true)) {
1967 mSubstitutions
.InsertOrUpdate(substituteName
,
1968 MakeUnique
<nsCString
>(actualFontName
));
1970 mNonExistingFonts
.AppendElement(substituteName
);
1973 if (nullptr != mFontFamilies
.GetWeak(substituteName
)) {
1974 // don't do the substitution if user actually has a usable font
1975 // with this name installed
1978 nsAutoCString
actualFontName(sub
.actualName
);
1979 BuildKeyNameFromFontName(actualFontName
);
1981 if (nullptr != (ff
= mFontFamilies
.GetWeak(actualFontName
))) {
1982 mFontSubstitutes
.InsertOrUpdate(substituteName
, RefPtr
{ff
});
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
);
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
));
2013 if (mNonExistingFonts
.Contains(keyName
)) {
2017 return gfxPlatformFontList::FindAndAddFamilies(
2018 aPresContext
, aGeneric
, keyName
, aOutput
, aFlags
, aStyle
, aLanguage
,
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
) {
2052 RefPtr
<IDWriteFontFamily
> family
;
2054 // clean out previous value
2055 aFamilyName
.Truncate();
2057 hr
= aFont
->GetFontFamily(getter_AddRefs(family
));
2062 RefPtr
<IDWriteLocalizedStrings
> familyNames
;
2064 hr
= family
->GetFamilyNames(getter_AddRefs(familyNames
));
2069 if (!GetEnglishOrFirstName(aFamilyName
, familyNames
)) {
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
) {
2091 RefPtr
<IDWriteFont
> font
;
2092 hr
= mSystemFonts
->GetFontFromFontFace(glyphRun
->fontFace
,
2093 getter_AddRefs(font
));
2098 // copy the family name
2099 hr
= GetFamilyName(font
, mFamilyName
);
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();
2113 gfxFontEntry
* gfxDWriteFontList::PlatformGlobalFontFallback(
2114 nsPresContext
* aPresContext
, const uint32_t aCh
, Script aRunScript
,
2115 const gfxFontStyle
* aMatchStyle
, FontFamily
& aMatchedFamily
) {
2118 RefPtr
<IDWriteFactory
> dwFactory
= Factory::GetDWriteFactory();
2123 // initialize fallback renderer
2124 if (!mFallbackRenderer
) {
2125 mFallbackRenderer
= new DWriteFontFallbackRenderer(dwFactory
);
2127 if (!mFallbackRenderer
->IsValid()) {
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
));
2142 // set up string with fallback character
2146 if (IS_IN_BMP(aCh
)) {
2147 str
[0] = static_cast<wchar_t>(aCh
);
2151 str
[0] = static_cast<wchar_t>(H_SURROGATE(aCh
));
2152 str
[1] = static_cast<wchar_t>(L_SURROGATE(aCh
));
2158 RefPtr
<IDWriteTextLayout
> fallbackLayout
;
2160 hr
= dwFactory
->CreateTextLayout(str
, strLen
, mFallbackFormat
, 200.0f
, 200.0f
,
2161 getter_AddRefs(fallbackLayout
));
2166 // call the draw method to invoke the DirectWrite layout functions
2167 // which determine the fallback font
2169 hr
= fallbackLayout
->Draw(nullptr, mFallbackRenderer
, 50.0f
, 50.0f
);
2174 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER
) {
2175 gfxCriticalNote
<< "Exception occurred during DWrite font fallback";
2180 FindFamily(aPresContext
, mFallbackRenderer
->FallbackFamilyName());
2181 if (!family
.IsNull()) {
2182 gfxFontEntry
* fontEntry
= nullptr;
2183 if (family
.mIsShared
) {
2185 family
.mShared
->FindFaceForStyle(SharedFontList(), *aMatchStyle
);
2187 fontEntry
= GetOrCreateFontEntry(face
, family
.mShared
);
2190 fontEntry
= family
.mUnshared
->FindFontForStyle(*aMatchStyle
);
2192 if (fontEntry
&& fontEntry
->HasCharacter(aCh
)) {
2193 aMatchedFamily
= family
;
2196 Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT
, true);
2202 // used to load system-wide font info on off-main thread
2203 class DirectWriteFontInfo
: public FontInfoData
{
2205 DirectWriteFontInfo(bool aLoadOtherNames
, bool aLoadFaceNames
,
2206 bool aLoadCmaps
, IDWriteFontCollection
* aSystemFonts
2207 #ifdef MOZ_BUNDLED_FONTS
2209 IDWriteFontCollection
* aBundledFonts
2212 : FontInfoData(aLoadOtherNames
, aLoadFaceNames
, aLoadCmaps
),
2213 mSystemFonts(aSystemFonts
)
2214 #ifdef MOZ_BUNDLED_FONTS
2216 mBundledFonts(aBundledFonts
)
2221 virtual ~DirectWriteFontInfo() = default;
2223 // loads font data for all members of a given family
2224 virtual void LoadFontFamilyData(const nsACString
& aFamilyName
);
2227 RefPtr
<IDWriteFontCollection
> mSystemFonts
;
2228 #ifdef MOZ_BUNDLED_FONTS
2229 RefPtr
<IDWriteFontCollection
> mBundledFonts
;
2233 void DirectWriteFontInfo::LoadFontFamilyData(const nsACString
& aFamilyName
) {
2234 // lookup the family
2235 NS_ConvertUTF8toUTF16
famName(aFamilyName
);
2238 BOOL exists
= false;
2241 RefPtr
<IDWriteFontFamily
> family
;
2242 hr
= mSystemFonts
->FindFamilyName((const wchar_t*)famName
.get(), &index
,
2244 if (SUCCEEDED(hr
) && exists
) {
2245 mSystemFonts
->GetFontFamily(index
, getter_AddRefs(family
));
2251 #ifdef MOZ_BUNDLED_FONTS
2252 if (!family
&& mBundledFonts
) {
2253 hr
= mBundledFonts
->FindFamilyName((const wchar_t*)famName
.get(), &index
,
2255 if (SUCCEEDED(hr
) && exists
) {
2256 mBundledFonts
->GetFontFamily(index
, getter_AddRefs(family
));
2265 // later versions of DirectWrite support querying the fullname/psname
2266 bool loadFaceNamesUsingDirectWrite
= mLoadFaceNames
;
2268 for (uint32_t i
= 0; i
< family
->GetFontCount(); i
++) {
2270 RefPtr
<IDWriteFont
> dwFont
;
2271 hr
= family
->GetFont(i
, getter_AddRefs(dwFont
));
2273 // This should never happen.
2274 NS_WARNING("Failed to get existing font from family.");
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.
2286 // get the name of the face
2287 nsCString
fullID(aFamilyName
);
2288 nsAutoCString fontName
;
2289 hr
= GetDirectWriteFontName(dwFont
, fontName
);
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
) {
2304 GetDirectWriteFaceName(dwFont
, PSNAME_ID
, fontData
.mPostscriptName
);
2306 loadFaceNamesUsingDirectWrite
= false;
2308 hr
= GetDirectWriteFaceName(dwFont
, FULLNAME_ID
, fontData
.mFullName
);
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
)) {
2319 NativeEndian::swapToBigEndian(TRUETYPE_TAG('n', 'a', 'm', 'e'));
2320 const char* nameData
;
2325 hr
= dwFontFace
->TryGetFontTable(kNAME
, (const void**)&nameData
,
2326 &nameSize
, &ctx
, &exists
);
2327 if (SUCCEEDED(hr
) && nameData
&& nameSize
> 0) {
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
);
2346 !fontData
.mPostscriptName
.IsEmpty() || !fontData
.mFullName
.IsEmpty();
2348 mLoadStats
.facenames
++;
2355 hr
= dwFont
->CreateFontFace(getter_AddRefs(dwFontFace
));
2356 if (!SUCCEEDED(hr
)) {
2362 NativeEndian::swapToBigEndian(TRUETYPE_TAG('c', 'm', 'a', 'p'));
2363 const uint8_t* cmapData
;
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();
2376 if (cmapData
&& cmapSize
> 0 &&
2377 NS_SUCCEEDED(gfxFontUtils::ReadCMAP(cmapData
, cmapSize
, *charmap
,
2379 fontData
.mCharacterMap
= charmap
;
2380 fontData
.mUVSOffset
= offset
;
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
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
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) \
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; \
2431 *ppvObject = nullptr; \
2432 return E_NOINTERFACE; \
2438 class BundledFontFileEnumerator
: public IDWriteFontFileEnumerator
{
2439 IMPL_QI_FOR_DWRITE(IDWriteFontFileEnumerator
)
2441 NS_INLINE_DECL_REFCOUNTING(BundledFontFileEnumerator
)
2444 BundledFontFileEnumerator(IDWriteFactory
* aFactory
, nsIFile
* aFontDir
);
2446 IFACEMETHODIMP
MoveNext(BOOL
* hasCurrentFile
);
2448 IFACEMETHODIMP
GetCurrentFontFile(IDWriteFontFile
** fontFile
);
2451 BundledFontFileEnumerator() = delete;
2452 BundledFontFileEnumerator(const BundledFontFileEnumerator
&) = delete;
2453 BundledFontFileEnumerator
& operator=(const BundledFontFileEnumerator
&) =
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
,
2466 : mFactory(aFactory
), mFontDir(aFontDir
) {
2467 mFontDir
->GetDirectoryEntries(getter_AddRefs(mEntries
));
2471 BundledFontFileEnumerator::MoveNext(BOOL
* aHasCurrentFile
) {
2472 bool hasMore
= false;
2474 if (NS_SUCCEEDED(mEntries
->HasMoreElements(&hasMore
)) && hasMore
) {
2475 if (NS_SUCCEEDED(mEntries
->GetNext(getter_AddRefs(mCurrent
)))) {
2480 *aHasCurrentFile
= hasMore
;
2485 BundledFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile
** aFontFile
) {
2486 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(mCurrent
);
2491 if (NS_FAILED(file
->GetPath(path
))) {
2494 return mFactory
->CreateFontFileReference((const WCHAR
*)path
.get(), nullptr,
2498 class BundledFontLoader
: public IDWriteFontCollectionLoader
{
2499 IMPL_QI_FOR_DWRITE(IDWriteFontCollectionLoader
)
2501 NS_INLINE_DECL_REFCOUNTING(BundledFontLoader
)
2504 BundledFontLoader() {}
2506 IFACEMETHODIMP
CreateEnumeratorFromKey(
2507 IDWriteFactory
* aFactory
, const void* aCollectionKey
,
2508 UINT32 aCollectionKeySize
,
2509 IDWriteFontFileEnumerator
** aFontFileEnumerator
);
2512 BundledFontLoader(const BundledFontLoader
&) = delete;
2513 BundledFontLoader
& operator=(const BundledFontLoader
&) = delete;
2514 virtual ~BundledFontLoader() = default;
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
);
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
)) {
2535 if (NS_FAILED(localDir
->Append(u
"fonts"_ns
))) {
2539 if (NS_FAILED(localDir
->IsDirectory(&isDir
)) || !isDir
) {
2543 RefPtr
<BundledFontLoader
> loader
= new BundledFontLoader();
2544 if (FAILED(aFactory
->RegisterFontCollectionLoader(loader
))) {
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
);
2558 return collection
.forget();