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 "AppleUtils.h"
7 #include "CoreTextFontList.h"
8 #include "gfxFontConstants.h"
9 #include "gfxMacFont.h"
10 #include "gfxUserFontSet.h"
12 #include "harfbuzz/hb.h"
14 #include "MainThreadUtils.h"
16 #include "mozilla/dom/ContentChild.h"
17 #include "mozilla/dom/ContentParent.h"
18 #include "mozilla/gfx/2D.h"
19 #include "mozilla/Logging.h"
20 #include "mozilla/Preferences.h"
21 #include "mozilla/ProfilerLabels.h"
22 #include "mozilla/Sprintf.h"
23 #include "mozilla/StaticPrefs_gfx.h"
24 #include "mozilla/Telemetry.h"
26 #include "nsAppDirectoryServiceDefs.h"
27 #include "nsCharTraits.h"
28 #include "nsComponentManagerUtils.h"
29 #include "nsDirectoryServiceDefs.h"
30 #include "nsDirectoryServiceUtils.h"
31 #include "nsIDirectoryEnumerator.h"
32 #include "nsServiceManagerUtils.h"
33 #include "SharedFontList-impl.h"
35 using namespace mozilla
;
36 using namespace mozilla::gfx
;
38 // List generated by diffing the arrays returned by
39 // CTFontManagerCopyAvailableFontFamilyNames() when built with
40 // MACOSX_DEPLOYMENT_TARGET=10.12 vs 11.0, to identify the font family names
41 // that Core Text is treating as "deprecated" and hiding from the app on newer
43 constexpr nsLiteralCString kDeprecatedFontFamilies
[] = {
44 // Dot-prefixed font families are supposed to be hidden from the
46 // font list anyhow, so we don't need to add them here.
47 // ".Al Bayan PUA"_ns,
49 // ".Al Tarikh PUA"_ns,
50 // ".Apple Color Emoji UI"_ns,
51 // ".Apple SD Gothic NeoI"_ns,
53 // ".Arial Hebrew Desk Interface"_ns,
56 // ".Damascus PUA"_ns,
57 // ".DecoType Naskh PUA"_ns,
58 // ".Diwan Kufi PUA"_ns,
60 // ".Geeza Pro Interface"_ns,
61 // ".Geeza Pro PUA"_ns,
62 // ".Helvetica LT MM"_ns,
63 // ".Hiragino Kaku Gothic Interface"_ns,
64 // ".Hiragino Sans GB Interface"_ns,
66 // ".KufiStandardGK PUA"_ns,
68 // ".Lucida Grande UI"_ns,
72 // ".Noto Nastaliq Urdu UI"_ns,
77 // ".Savoye LET CC."_ns,
79 // ".SF Compact Rounded"_ns,
82 // ".SF NS Rounded"_ns,
85 "Hiragino Kaku Gothic Pro"_ns
,
86 "Hiragino Kaku Gothic ProN"_ns
,
87 "Hiragino Kaku Gothic Std"_ns
,
88 "Hiragino Kaku Gothic StdN"_ns
,
89 "Hiragino Maru Gothic Pro"_ns
,
90 "Hiragino Mincho Pro"_ns
,
93 "Noto Sans Armenian"_ns
,
94 "Noto Sans Avestan"_ns
,
96 "Noto Sans Bassa Vah"_ns
,
98 "Noto Sans Bhaiksuki"_ns
,
99 "Noto Sans Brahmi"_ns
,
100 "Noto Sans Buginese"_ns
,
101 "Noto Sans Buhid"_ns
,
102 "Noto Sans Carian"_ns
,
103 "Noto Sans Caucasian Albanian"_ns
,
104 "Noto Sans Chakma"_ns
,
106 "Noto Sans Coptic"_ns
,
107 "Noto Sans Cuneiform"_ns
,
108 "Noto Sans Cypriot"_ns
,
109 "Noto Sans Duployan"_ns
,
110 "Noto Sans Egyptian Hieroglyphs"_ns
,
111 "Noto Sans Elbasan"_ns
,
112 "Noto Sans Glagolitic"_ns
,
113 "Noto Sans Gothic"_ns
,
114 "Noto Sans Gunjala Gondi"_ns
,
115 "Noto Sans Hanifi Rohingya"_ns
,
116 "Noto Sans Hanunoo"_ns
,
117 "Noto Sans Hatran"_ns
,
118 "Noto Sans Imperial Aramaic"_ns
,
119 "Noto Sans Inscriptional Pahlavi"_ns
,
120 "Noto Sans Inscriptional Parthian"_ns
,
121 "Noto Sans Javanese"_ns
,
122 "Noto Sans Kaithi"_ns
,
123 "Noto Sans Kayah Li"_ns
,
124 "Noto Sans Kharoshthi"_ns
,
125 "Noto Sans Khojki"_ns
,
126 "Noto Sans Khudawadi"_ns
,
127 "Noto Sans Lepcha"_ns
,
128 "Noto Sans Limbu"_ns
,
129 "Noto Sans Linear A"_ns
,
130 "Noto Sans Linear B"_ns
,
132 "Noto Sans Lycian"_ns
,
133 "Noto Sans Lydian"_ns
,
134 "Noto Sans Mahajani"_ns
,
135 "Noto Sans Mandaic"_ns
,
136 "Noto Sans Manichaean"_ns
,
137 "Noto Sans Marchen"_ns
,
138 "Noto Sans Masaram Gondi"_ns
,
139 "Noto Sans Meetei Mayek"_ns
,
140 "Noto Sans Mende Kikakui"_ns
,
141 "Noto Sans Meroitic"_ns
,
144 "Noto Sans Mongolian"_ns
,
146 "Noto Sans Multani"_ns
,
147 "Noto Sans Nabataean"_ns
,
148 "Noto Sans New Tai Lue"_ns
,
151 "Noto Sans Ol Chiki"_ns
,
152 "Noto Sans Old Hungarian"_ns
,
153 "Noto Sans Old Italic"_ns
,
154 "Noto Sans Old North Arabian"_ns
,
155 "Noto Sans Old Permic"_ns
,
156 "Noto Sans Old Persian"_ns
,
157 "Noto Sans Old South Arabian"_ns
,
158 "Noto Sans Old Turkic"_ns
,
159 "Noto Sans Osage"_ns
,
160 "Noto Sans Osmanya"_ns
,
161 "Noto Sans Pahawh Hmong"_ns
,
162 "Noto Sans Palmyrene"_ns
,
163 "Noto Sans Pau Cin Hau"_ns
,
164 "Noto Sans PhagsPa"_ns
,
165 "Noto Sans Phoenician"_ns
,
166 "Noto Sans Psalter Pahlavi"_ns
,
167 "Noto Sans Rejang"_ns
,
168 "Noto Sans Samaritan"_ns
,
169 "Noto Sans Saurashtra"_ns
,
170 "Noto Sans Sharada"_ns
,
171 "Noto Sans Siddham"_ns
,
172 "Noto Sans Sora Sompeng"_ns
,
173 "Noto Sans Sundanese"_ns
,
174 "Noto Sans Syloti Nagri"_ns
,
175 "Noto Sans Syriac"_ns
,
176 "Noto Sans Tagalog"_ns
,
177 "Noto Sans Tagbanwa"_ns
,
178 "Noto Sans Tai Le"_ns
,
179 "Noto Sans Tai Tham"_ns
,
180 "Noto Sans Tai Viet"_ns
,
181 "Noto Sans Takri"_ns
,
182 "Noto Sans Thaana"_ns
,
183 "Noto Sans Tifinagh"_ns
,
184 "Noto Sans Tirhuta"_ns
,
185 "Noto Sans Ugaritic"_ns
,
187 "Noto Sans Wancho"_ns
,
188 "Noto Sans Warang Citi"_ns
,
190 "Noto Sans Zawgyi"_ns
,
191 "Noto Serif Ahom"_ns
,
192 "Noto Serif Balinese"_ns
,
193 "Noto Serif Yezidi"_ns
,
202 static void GetStringForCFString(CFStringRef aSrc
, nsAString
& aDest
) {
203 auto len
= CFStringGetLength(aSrc
);
204 aDest
.SetLength(len
);
205 CFStringGetCharacters(aSrc
, CFRangeMake(0, len
),
206 (UniChar
*)aDest
.BeginWriting());
209 static CFStringRef
CreateCFStringForString(const nsACString
& aSrc
) {
210 return CFStringCreateWithBytes(kCFAllocatorDefault
,
211 (const UInt8
*)aSrc
.BeginReading(),
212 aSrc
.Length(), kCFStringEncodingUTF8
, false);
215 #define LOG_FONTLIST(args) \
216 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug, args)
217 #define LOG_FONTLIST_ENABLED() \
218 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug)
219 #define LOG_CMAPDATA_ENABLED() \
220 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), mozilla::LogLevel::Debug)
224 // Complex scripts will not render correctly unless appropriate AAT or OT
225 // layout tables are present.
226 // For OpenType, we also check that the GSUB table supports the relevant
227 // script tag, to avoid using things like Arial Unicode MS for Lao (it has
228 // the characters, but lacks OpenType support).
230 // TODO: consider whether we should move this to gfxFontEntry and do similar
231 // cmap-masking on other platforms to avoid using fonts that won't shape
234 nsresult
CTFontEntry::ReadCMAP(FontInfoData
* aFontInfoData
) {
235 // attempt this once, if errors occur leave a blank cmap
236 if (mCharacterMap
|| mShmemCharacterMap
) {
240 RefPtr
<gfxCharacterMap
> charmap
;
243 uint32_t uvsOffset
= 0;
245 (charmap
= GetCMAPFromFontInfo(aFontInfoData
, uvsOffset
))) {
248 uint32_t kCMAP
= TRUETYPE_TAG('c', 'm', 'a', 'p');
249 charmap
= new gfxCharacterMap();
250 AutoTable
cmapTable(this, kCMAP
);
254 const uint8_t* cmapData
= reinterpret_cast<const uint8_t*>(
255 hb_blob_get_data(cmapTable
, &cmapLen
));
256 rv
= gfxFontUtils::ReadCMAP(cmapData
, cmapLen
, *charmap
, uvsOffset
);
258 rv
= NS_ERROR_NOT_AVAILABLE
;
261 mUVSOffset
.exchange(uvsOffset
);
263 if (NS_SUCCEEDED(rv
) && !mIsDataUserFont
&& !HasGraphiteTables()) {
264 // For downloadable fonts, trust the author and don't
265 // try to munge the cmap based on script shaping support.
267 // We also assume a Graphite font knows what it's doing,
268 // and provides whatever shaping is needed for the
269 // characters it supports, so only check/clear the
270 // complex-script ranges for non-Graphite fonts
272 // for layout support, check for the presence of mort/morx/kerx and/or
273 // opentype layout tables
274 bool hasAATLayout
= HasFontTable(TRUETYPE_TAG('m', 'o', 'r', 'x')) ||
275 HasFontTable(TRUETYPE_TAG('m', 'o', 'r', 't'));
276 bool hasAppleKerning
= HasFontTable(TRUETYPE_TAG('k', 'e', 'r', 'x'));
277 bool hasGSUB
= HasFontTable(TRUETYPE_TAG('G', 'S', 'U', 'B'));
278 bool hasGPOS
= HasFontTable(TRUETYPE_TAG('G', 'P', 'O', 'S'));
279 if ((hasAATLayout
&& !(hasGSUB
|| hasGPOS
)) || hasAppleKerning
) {
280 mRequiresAAT
= true; // prefer CoreText if font has no OTL tables,
281 // or if it uses the Apple-specific 'kerx'
282 // variant of kerning table
285 for (const ScriptRange
* sr
= gfxPlatformFontList::sComplexScriptRanges
;
286 sr
->rangeStart
; sr
++) {
287 // check to see if the cmap includes complex script codepoints
288 if (charmap
->TestRange(sr
->rangeStart
, sr
->rangeEnd
)) {
290 // prefer CoreText for Apple's complex-script fonts,
291 // even if they also have some OpenType tables
292 // (e.g. Geeza Pro Bold on 10.6; see bug 614903)
294 // and don't mask off complex-script ranges, we assume
295 // the AAT tables will provide the necessary shaping
299 // We check for GSUB here, as GPOS alone would not be ok.
300 if (hasGSUB
&& SupportsScriptInGSUB(sr
->tags
, sr
->numTags
)) {
304 charmap
->ClearRange(sr
->rangeStart
, sr
->rangeEnd
);
308 // Bug 1360309, 1393624: several of Apple's Chinese fonts have spurious
309 // blank glyphs for obscure Tibetan and Arabic-script codepoints.
310 // Blocklist these so that font fallback will not use them.
312 (FamilyName().EqualsLiteral("Songti SC") ||
313 FamilyName().EqualsLiteral("Songti TC") ||
314 FamilyName().EqualsLiteral("STSong") ||
315 // Bug 1390980: on 10.11, the Kaiti fonts are also affected.
316 FamilyName().EqualsLiteral("Kaiti SC") ||
317 FamilyName().EqualsLiteral("Kaiti TC") ||
318 FamilyName().EqualsLiteral("STKaiti"))) {
319 charmap
->ClearRange(0x0f6b, 0x0f70);
320 charmap
->ClearRange(0x0f8c, 0x0f8f);
321 charmap
->clear(0x0f98);
322 charmap
->clear(0x0fbd);
323 charmap
->ClearRange(0x0fcd, 0x0fff);
324 charmap
->clear(0x0620);
325 charmap
->clear(0x065f);
326 charmap
->ClearRange(0x06ee, 0x06ef);
327 charmap
->clear(0x06ff);
331 bool setCharMap
= true;
332 if (NS_SUCCEEDED(rv
)) {
333 gfxPlatformFontList
* pfl
= gfxPlatformFontList::PlatformFontList();
334 fontlist::FontList
* sharedFontList
= pfl
->SharedFontList();
335 if (!IsUserFont() && mShmemFace
) {
336 mShmemFace
->SetCharacterMap(sharedFontList
, charmap
); // async
337 if (TrySetShmemCharacterMap()) {
341 charmap
= pfl
->FindCharMap(charmap
);
343 mHasCmapTable
= true;
345 // if error occurred, initialize to null cmap
346 charmap
= new gfxCharacterMap();
347 mHasCmapTable
= false;
350 // Temporarily retain charmap, until the shared version is
352 if (mCharacterMap
.compareExchange(nullptr, charmap
.get())) {
353 charmap
.get()->AddRef();
357 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n",
358 mName
.get(), charmap
->SizeOfIncludingThis(moz_malloc_size_of
),
359 charmap
->mHash
, mCharacterMap
== charmap
? " new" : ""));
360 if (LOG_CMAPDATA_ENABLED()) {
362 SprintfLiteral(prefix
, "(cmapdata) name: %.220s", mName
.get());
363 charmap
->Dump(prefix
, eGfxLog_cmapdata
);
369 gfxFont
* CTFontEntry::CreateFontInstance(const gfxFontStyle
* aFontStyle
) {
370 RefPtr
<UnscaledFontMac
> unscaledFont(mUnscaledFont
);
372 CGFontRef baseFont
= GetFontRef();
376 unscaledFont
= new UnscaledFontMac(baseFont
, mIsDataUserFont
);
377 mUnscaledFont
= unscaledFont
;
380 return new gfxMacFont(unscaledFont
, this, aFontStyle
);
383 bool CTFontEntry::HasVariations() {
384 if (!mHasVariationsInitialized
) {
385 mHasVariationsInitialized
= true;
386 mHasVariations
= gfxPlatform::HasVariationFontSupport() &&
387 HasFontTable(TRUETYPE_TAG('f', 'v', 'a', 'r'));
390 return mHasVariations
;
393 void CTFontEntry::GetVariationAxes(
394 nsTArray
<gfxFontVariationAxis
>& aVariationAxes
) {
395 // We could do this by creating a CTFont and calling CTFontCopyVariationAxes,
396 // but it is expensive to instantiate a CTFont for every face just to set up
397 // the axis information.
398 // Instead we use gfxFontUtils to read the font tables directly.
399 gfxFontUtils::GetVariationData(this, &aVariationAxes
, nullptr);
402 void CTFontEntry::GetVariationInstances(
403 nsTArray
<gfxFontVariationInstance
>& aInstances
) {
404 // Core Text doesn't offer API for this, so we use gfxFontUtils to read the
405 // font tables directly.
406 gfxFontUtils::GetVariationData(this, nullptr, &aInstances
);
409 bool CTFontEntry::IsCFF() {
410 if (!mIsCFFInitialized
) {
411 mIsCFFInitialized
= true;
412 mIsCFF
= HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' '));
418 CTFontEntry::CTFontEntry(const nsACString
& aPostscriptName
, WeightRange aWeight
,
419 bool aIsStandardFace
, double aSizeHint
)
420 : gfxFontEntry(aPostscriptName
, aIsStandardFace
),
422 mSizeHint(aSizeHint
),
423 mFontRefInitialized(false),
426 mIsCFFInitialized(false),
427 mHasVariations(false),
428 mHasVariationsInitialized(false),
429 mHasAATSmallCaps(false),
430 mHasAATSmallCapsInitialized(false) {
431 mWeightRange
= aWeight
;
435 CTFontEntry::CTFontEntry(const nsACString
& aPostscriptName
, CGFontRef aFontRef
,
436 WeightRange aWeight
, StretchRange aStretch
,
437 SlantStyleRange aStyle
, bool aIsDataUserFont
,
438 bool aIsLocalUserFont
)
439 : gfxFontEntry(aPostscriptName
, false),
442 mFontRefInitialized(false),
445 mIsCFFInitialized(false),
446 mHasVariations(false),
447 mHasVariationsInitialized(false),
448 mHasAATSmallCaps(false),
449 mHasAATSmallCapsInitialized(false) {
451 mFontRefInitialized
= true;
454 mWeightRange
= aWeight
;
455 mStretchRange
= aStretch
;
456 mFixedPitch
= false; // xxx - do we need this for downloaded fonts?
457 mStyleRange
= aStyle
;
460 NS_ASSERTION(!(aIsDataUserFont
&& aIsLocalUserFont
),
461 "userfont is either a data font or a local font");
462 mIsDataUserFont
= aIsDataUserFont
;
463 mIsLocalUserFont
= aIsLocalUserFont
;
466 gfxFontEntry
* CTFontEntry::Clone() const {
467 MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
468 CTFontEntry
* fe
= new CTFontEntry(Name(), Weight(), mStandardFace
, mSizeHint
);
469 fe
->mStyleRange
= mStyleRange
;
470 fe
->mStretchRange
= mStretchRange
;
471 fe
->mFixedPitch
= mFixedPitch
;
475 CGFontRef
CTFontEntry::GetFontRef() {
476 if (!mFontRefInitialized
) {
477 // Cache the CGFontRef, to be released by our destructor.
478 mFontRef
= CreateOrCopyFontRef();
479 mFontRefInitialized
= true;
481 // Return a non-retained reference; caller does not need to release.
485 CGFontRef
CTFontEntry::CreateOrCopyFontRef() {
487 // We have a cached CGFont, just add a reference. Caller must
488 // release, but we'll still own our reference.
489 ::CGFontRetain(mFontRef
);
493 CrashReporter::AutoAnnotateCrashReport
autoFontName(
494 CrashReporter::Annotation::FontName
, mName
);
496 // Create a new CGFont; caller will own the only reference to it.
497 AutoCFRelease
<CFStringRef
> psname
= CreateCFStringForString(mName
);
502 CGFontRef ref
= CGFontCreateWithFontName(psname
);
503 return ref
; // Not saved in mFontRef; caller will own the reference
506 // For a logging build, we wrap the CFDataRef in a FontTableRec so that we can
507 // use the MOZ_COUNT_[CD]TOR macros in it. A release build without logging
508 // does not get this overhead.
511 explicit FontTableRec(CFDataRef aDataRef
) : mDataRef(aDataRef
) {
512 MOZ_COUNT_CTOR(FontTableRec
);
516 MOZ_COUNT_DTOR(FontTableRec
);
524 /*static*/ void CTFontEntry::DestroyBlobFunc(void* aUserData
) {
525 #ifdef NS_BUILD_REFCNT_LOGGING
526 FontTableRec
* ftr
= static_cast<FontTableRec
*>(aUserData
);
529 CFRelease((CFDataRef
)aUserData
);
533 hb_blob_t
* CTFontEntry::GetFontTable(uint32_t aTag
) {
534 AutoCFRelease
<CGFontRef
> fontRef
= CreateOrCopyFontRef();
539 CFDataRef dataRef
= ::CGFontCopyTableForTag(fontRef
, aTag
);
541 return hb_blob_create((const char*)CFDataGetBytePtr(dataRef
),
542 CFDataGetLength(dataRef
), HB_MEMORY_MODE_READONLY
,
543 #ifdef NS_BUILD_REFCNT_LOGGING
544 new FontTableRec(dataRef
),
554 bool CTFontEntry::HasFontTable(uint32_t aTableTag
) {
555 if (mAvailableTables
.Count() == 0) {
556 AutoCFRelease
<CGFontRef
> fontRef
= CreateOrCopyFontRef();
560 AutoCFRelease
<CFArrayRef
> tags
= ::CGFontCopyTableTags(fontRef
);
564 int numTags
= (int)CFArrayGetCount(tags
);
565 for (int t
= 0; t
< numTags
; t
++) {
566 uint32_t tag
= (uint32_t)(uintptr_t)CFArrayGetValueAtIndex(tags
, t
);
567 mAvailableTables
.PutEntry(tag
);
571 return mAvailableTables
.GetEntry(aTableTag
);
574 static bool CheckForAATSmallCaps(CFArrayRef aFeatures
) {
575 // Walk the array of feature descriptors from the font, and see whether
576 // a small-caps feature setting is available.
577 // Just bail out (returning false) if at any point we fail to find the
578 // expected dictionary keys, etc; if the font has bad data, we don't even
579 // try to search the rest of it.
580 auto numFeatures
= CFArrayGetCount(aFeatures
);
581 for (auto f
= 0; f
< numFeatures
; ++f
) {
582 auto featureDict
= (CFDictionaryRef
)CFArrayGetValueAtIndex(aFeatures
, f
);
586 auto featureNum
= (CFNumberRef
)CFDictionaryGetValue(
587 featureDict
, CFSTR("CTFeatureTypeIdentifier"));
592 if (!CFNumberGetValue(featureNum
, kCFNumberSInt16Type
, &featureType
)) {
595 if (featureType
== kLetterCaseType
|| featureType
== kLowerCaseType
) {
596 // Which selector to look for, depending whether we've found the
597 // legacy LetterCase feature or the new LowerCase one.
598 const uint16_t smallCaps
= (featureType
== kLetterCaseType
)
600 : kLowerCaseSmallCapsSelector
;
601 auto selectors
= (CFArrayRef
)CFDictionaryGetValue(
602 featureDict
, CFSTR("CTFeatureTypeSelectors"));
606 auto numSelectors
= CFArrayGetCount(selectors
);
607 for (auto s
= 0; s
< numSelectors
; s
++) {
609 (CFDictionaryRef
)CFArrayGetValueAtIndex(selectors
, s
);
613 auto selectorNum
= (CFNumberRef
)CFDictionaryGetValue(
614 selectorDict
, CFSTR("CTFeatureSelectorIdentifier"));
618 int16_t selectorValue
;
619 if (!CFNumberGetValue(selectorNum
, kCFNumberSInt16Type
,
623 if (selectorValue
== smallCaps
) {
632 bool CTFontEntry::SupportsOpenTypeFeature(Script aScript
,
633 uint32_t aFeatureTag
) {
634 // If we're going to shape with Core Text, we don't support added
635 // OpenType features (aside from any CT applies by default), except
636 // for 'smcp' which we map to an AAT feature selector.
637 if (RequiresAATLayout()) {
638 if (aFeatureTag
!= HB_TAG('s', 'm', 'c', 'p')) {
641 if (mHasAATSmallCapsInitialized
) {
642 return mHasAATSmallCaps
;
644 mHasAATSmallCapsInitialized
= true;
645 CGFontRef cgFont
= GetFontRef();
647 return mHasAATSmallCaps
;
650 CrashReporter::AutoAnnotateCrashReport
autoFontName(
651 CrashReporter::Annotation::FontName
, FamilyName());
653 AutoCFRelease
<CTFontRef
> ctFont
=
654 CTFontCreateWithGraphicsFont(cgFont
, 0.0, nullptr, nullptr);
656 AutoCFRelease
<CFArrayRef
> features
= CTFontCopyFeatures(ctFont
);
658 mHasAATSmallCaps
= CheckForAATSmallCaps(features
);
661 return mHasAATSmallCaps
;
663 return gfxFontEntry::SupportsOpenTypeFeature(aScript
, aFeatureTag
);
666 void CTFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf
,
667 FontListSizes
* aSizes
) const {
668 aSizes
->mFontListSize
+= aMallocSizeOf(this);
669 AddSizeOfExcludingThis(aMallocSizeOf
, aSizes
);
672 static CTFontDescriptorRef
CreateDescriptorForFamily(
673 const nsACString
& aFamilyName
, bool aNormalized
) {
674 AutoCFRelease
<CFStringRef
> family
= CreateCFStringForString(aFamilyName
);
675 const void* values
[] = {family
};
676 const void* keys
[] = {kCTFontFamilyNameAttribute
};
677 AutoCFRelease
<CFDictionaryRef
> attributes
= CFDictionaryCreate(
678 kCFAllocatorDefault
, keys
, values
, 1, &kCFTypeDictionaryKeyCallBacks
,
679 &kCFTypeDictionaryValueCallBacks
);
681 // Not AutoCFRelease, because we might return it.
682 CTFontDescriptorRef descriptor
=
683 CTFontDescriptorCreateWithAttributes(attributes
);
686 CTFontDescriptorRef normalized
=
687 CTFontDescriptorCreateMatchingFontDescriptor(descriptor
, nullptr);
689 CFRelease(descriptor
);
697 void CTFontFamily::LocalizedName(nsACString
& aLocalizedName
) {
698 AutoCFRelease
<CTFontDescriptorRef
> descriptor
=
699 CreateDescriptorForFamily(mName
, true);
701 AutoCFRelease
<CFStringRef
> name
=
702 static_cast<CFStringRef
>(CTFontDescriptorCopyLocalizedAttribute(
703 descriptor
, kCTFontFamilyNameAttribute
, nullptr));
705 nsAutoString localized
;
706 GetStringForCFString(name
, localized
);
707 if (!localized
.IsEmpty()) {
708 CopyUTF16toUTF8(localized
, aLocalizedName
);
714 // failed to get localized name, just use the canonical one
715 aLocalizedName
= mName
;
718 // Return the CSS weight value to use for the given face, overriding what
719 // AppKit gives us (used to adjust families with bad weight values, see
721 // A return value of 0 indicates no override - use the existing weight.
722 static inline int GetWeightOverride(const nsAString
& aPSName
) {
723 nsAutoCString
prefName("font.weight-override.");
724 // The PostScript name is required to be ASCII; if it's not, the font is
725 // broken anyway, so we really don't care that this is lossy.
726 LossyAppendUTF16toASCII(aPSName
, prefName
);
727 return Preferences::GetInt(prefName
.get(), 0);
730 // The Core Text weight trait is documented as
732 // ...a float value between -1.0 and 1.0 for normalized weight.
733 // The value of 0.0 corresponds to the regular or medium font weight.
735 // (https://developer.apple.com/documentation/coretext/kctfontweighttrait)
737 // CSS 'normal' font-weight is defined as 400, so we map 0.0 to this.
738 // The exact mapping to use for other values is not well defined; for now,
739 // we arbitrarily map the smallest value (-1.0) to CSS weight 100, and the
740 // largest (+1.0) to CSS weight 900, interpolating linearly between normal
741 // and each of these endpoints.
742 static inline int32_t CoreTextWeightToCSSWeight(CGFloat aCTWeight
) {
743 if (aCTWeight
>= 0.0) {
744 return 400 + NS_round(aCTWeight
* 500);
746 return 400 + NS_round(aCTWeight
* 300);
749 // The Core Text width trait is documented as
751 // ...a float between -1.0 and 1.0. The value of 0.0 corresponds to regular
752 // glyph spacing, and negative values represent condensed glyph spacing
754 // (https://developer.apple.com/documentation/coretext/kctfontweighttrait)
756 // CSS 'normal' font-stretch is 100%; 'ultra-expanded' is 200%, and 'ultra-
757 // condensed' is 50%. We map the extremes of the Core Text trait to these
758 // values, and interpolate in between these and normal.
759 static inline FontStretch
CoreTextWidthToCSSStretch(CGFloat aCTWidth
) {
760 if (aCTWidth
>= 0.0) {
761 return FontStretch::FromFloat(100.0 + aCTWidth
* 100.0);
763 return FontStretch::FromFloat(100.0 + aCTWidth
* 50.0);
766 void CTFontFamily::AddFace(CTFontDescriptorRef aFace
) {
767 AutoCFRelease
<CFStringRef
> psname
=
768 (CFStringRef
)CTFontDescriptorCopyAttribute(aFace
, kCTFontNameAttribute
);
769 AutoCFRelease
<CFStringRef
> facename
=
770 (CFStringRef
)CTFontDescriptorCopyAttribute(aFace
,
771 kCTFontStyleNameAttribute
);
773 AutoCFRelease
<CFDictionaryRef
> traitsDict
=
774 (CFDictionaryRef
)CTFontDescriptorCopyAttribute(aFace
,
775 kCTFontTraitsAttribute
);
777 (CFNumberRef
)CFDictionaryGetValue(traitsDict
, kCTFontWeightTrait
);
779 (CFNumberRef
)CFDictionaryGetValue(traitsDict
, kCTFontWidthTrait
);
780 CFNumberRef symbolicTraits
=
781 (CFNumberRef
)CFDictionaryGetValue(traitsDict
, kCTFontSymbolicTrait
);
783 bool isStandardFace
= false;
786 nsAutoString postscriptFontName
;
787 GetStringForCFString(psname
, postscriptFontName
);
789 int32_t cssWeight
= GetWeightOverride(postscriptFontName
);
791 // scale down and clamp, to get a value from 1..9
792 cssWeight
= ((cssWeight
+ 50) / 100);
793 cssWeight
= std::max(1, std::min(cssWeight
, 9));
794 cssWeight
*= 100; // scale up to CSS values
797 CFNumberGetValue(weight
, kCFNumberCGFloatType
, &weightValue
);
798 cssWeight
= CoreTextWeightToCSSWeight(weightValue
);
801 if (kCFCompareEqualTo
== CFStringCompare(facename
, CFSTR("Regular"), 0) ||
802 kCFCompareEqualTo
== CFStringCompare(facename
, CFSTR("Bold"), 0) ||
803 kCFCompareEqualTo
== CFStringCompare(facename
, CFSTR("Italic"), 0) ||
804 kCFCompareEqualTo
== CFStringCompare(facename
, CFSTR("Oblique"), 0) ||
805 kCFCompareEqualTo
== CFStringCompare(facename
, CFSTR("Bold Italic"), 0) ||
807 CFStringCompare(facename
, CFSTR("Bold Oblique"), 0)) {
808 isStandardFace
= true;
811 // create a font entry
812 CTFontEntry
* fontEntry
= new CTFontEntry(
813 NS_ConvertUTF16toUTF8(postscriptFontName
),
814 WeightRange(FontWeight::FromInt(cssWeight
)), isStandardFace
);
817 CFNumberGetValue(width
, kCFNumberCGFloatType
, &widthValue
);
818 fontEntry
->mStretchRange
=
819 StretchRange(CoreTextWidthToCSSStretch(widthValue
));
822 CFNumberGetValue(symbolicTraits
, kCFNumberSInt32Type
, &traitsValue
);
823 if (traitsValue
& kCTFontItalicTrait
) {
824 fontEntry
->mStyleRange
= SlantStyleRange(FontSlantStyle::ITALIC
);
827 if (traitsValue
& kCTFontMonoSpaceTrait
) {
828 fontEntry
->mFixedPitch
= true;
831 if (gfxPlatform::HasVariationFontSupport()) {
832 fontEntry
->SetupVariationRanges();
835 if (LOG_FONTLIST_ENABLED()) {
836 nsAutoCString weightString
;
837 fontEntry
->Weight().ToString(weightString
);
838 nsAutoCString stretchString
;
839 fontEntry
->Stretch().ToString(stretchString
);
841 ("(fontlist) added (%s) to family (%s)"
842 " with style: %s weight: %s stretch: %s",
843 fontEntry
->Name().get(), Name().get(),
844 fontEntry
->IsItalic() ? "italic" : "normal", weightString
.get(),
845 stretchString
.get()));
848 // insert into font entry array of family
849 AddFontEntryLocked(fontEntry
);
852 void CTFontFamily::FindStyleVariationsLocked(FontInfoData
* aFontInfoData
) {
857 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("CTFontFamily::FindStyleVariations",
860 auto addFaceFunc
= [](const void* aValue
, void* aContext
) -> void {
861 CTFontFamily
* family
= (CTFontFamily
*)aContext
;
862 // Calling family->AddFace requires that family->mLock is held. We know
863 // this will be true because FindStyleVariationsLocked already requires it,
864 // but the thread-safety analysis can't track that through into the lambda
865 // here, so we disable the check to avoid a spurious warning.
866 MOZ_PUSH_IGNORE_THREAD_SAFETY
;
867 family
->AddFace((CTFontDescriptorRef
)aValue
);
868 MOZ_POP_THREAD_SAFETY
;
871 AutoCFRelease
<CTFontDescriptorRef
> descriptor
=
872 CreateDescriptorForFamily(mName
, false);
873 AutoCFRelease
<CFArrayRef
> faces
=
874 CTFontDescriptorCreateMatchingFontDescriptors(descriptor
, nullptr);
877 CFArrayApplyFunction(faces
, CFRangeMake(0, CFArrayGetCount(faces
)),
881 SortAvailableFonts();
884 if (mIsBadUnderlineFamily
) {
885 SetBadUnderlineFonts();
888 CheckForSimpleFamily();
891 /* CoreTextFontList */
894 CoreTextFontList::CoreTextFontList()
895 : gfxPlatformFontList(false),
896 mDefaultFont(nullptr),
897 mUseSizeSensitiveSystemFont(false) {
898 #ifdef MOZ_BUNDLED_FONTS
899 // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an
900 // explicit value of 0 (off) will disable them.
901 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
902 TimeStamp start
= TimeStamp::Now();
903 ActivateBundledFonts();
904 TimeStamp end
= TimeStamp::Now();
905 Telemetry::Accumulate(Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE
,
906 (end
- start
).ToMilliseconds());
910 // Load the font-list preferences now, so that we don't have to do it from
911 // Init[Shared]FontListForPlatform, which may be called off-main-thread.
912 gfxFontUtils::GetPrefsFontList("font.preload-names-list", mPreloadFonts
);
915 CoreTextFontList::~CoreTextFontList() {
916 AutoLock
lock(mLock
);
918 if (XRE_IsParentProcess()) {
919 CFNotificationCenterRemoveObserver(
920 CFNotificationCenterGetLocalCenter(), this,
921 kCTFontManagerRegisteredFontsChangedNotification
, 0);
925 CFRelease(mDefaultFont
);
929 void CoreTextFontList::AddFamily(const nsACString
& aFamilyName
,
930 FontVisibility aVisibility
) {
931 double sizeHint
= 0.0;
932 if (aVisibility
== FontVisibility::Hidden
&& mUseSizeSensitiveSystemFont
&&
933 mSystemDisplayFontFamilyName
.Equals(aFamilyName
)) {
938 ToLowerCase(aFamilyName
, key
);
940 RefPtr
<gfxFontFamily
> familyEntry
=
941 new CTFontFamily(aFamilyName
, aVisibility
, sizeHint
);
942 mFontFamilies
.InsertOrUpdate(key
, RefPtr
{familyEntry
});
944 // check the bad underline blocklist
945 if (mBadUnderlineFamilyNames
.ContainsSorted(key
)) {
946 familyEntry
->SetBadUnderlineFamily();
950 void CoreTextFontList::AddFamily(CFStringRef aFamily
) {
951 // CTFontManager includes internal family names and LastResort; skip those.
953 CFStringCompare(aFamily
, CFSTR("LastResort"),
954 kCFCompareCaseInsensitive
) == kCFCompareEqualTo
||
955 CFStringCompare(aFamily
, CFSTR(".LastResort"),
956 kCFCompareCaseInsensitive
) == kCFCompareEqualTo
) {
960 nsAutoString familyName
;
961 GetStringForCFString(aFamily
, familyName
);
963 NS_ConvertUTF16toUTF8
nameUtf8(familyName
);
964 AddFamily(nameUtf8
, GetVisibilityForFamily(nameUtf8
));
968 void CoreTextFontList::ActivateFontsFromDir(
969 const nsACString
& aDir
, nsTHashSet
<nsCStringHashKey
>* aLoadedFamilies
) {
970 AutoCFRelease
<CFURLRef
> directory
= CFURLCreateFromFileSystemRepresentation(
971 kCFAllocatorDefault
, (const UInt8
*)nsPromiseFlatCString(aDir
).get(),
972 aDir
.Length(), true);
976 AutoCFRelease
<CFURLEnumeratorRef
> enumerator
=
977 CFURLEnumeratorCreateForDirectoryURL(kCFAllocatorDefault
, directory
,
978 kCFURLEnumeratorDefaultBehavior
,
983 AutoCFRelease
<CFMutableArrayRef
> urls
=
984 CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
990 CFURLEnumeratorResult result
;
992 result
= CFURLEnumeratorGetNextURL(enumerator
, &url
, nullptr);
993 if (result
!= kCFURLEnumeratorSuccess
) {
996 CFArrayAppendValue(urls
, url
);
998 if (!aLoadedFamilies
) {
1001 AutoCFRelease
<CFArrayRef
> descriptors
=
1002 CTFontManagerCreateFontDescriptorsFromURL(url
);
1003 if (!descriptors
|| !CFArrayGetCount(descriptors
)) {
1006 CTFontDescriptorRef desc
=
1007 (CTFontDescriptorRef
)CFArrayGetValueAtIndex(descriptors
, 0);
1008 AutoCFRelease
<CFStringRef
> name
=
1009 (CFStringRef
)CTFontDescriptorCopyAttribute(desc
,
1010 kCTFontFamilyNameAttribute
);
1012 key
.SetLength((CFStringGetLength(name
) + 1) * 3);
1013 if (CFStringGetCString(name
, key
.BeginWriting(), key
.Length(),
1014 kCFStringEncodingUTF8
)) {
1015 key
.SetLength(strlen(key
.get()));
1016 aLoadedFamilies
->Insert(key
);
1018 } while (result
!= kCFURLEnumeratorEnd
);
1020 CTFontManagerRegisterFontsForURLs(urls
, kCTFontManagerScopeProcess
, nullptr);
1023 void CoreTextFontList::ReadSystemFontList(dom::SystemFontList
* aList
)
1024 MOZ_NO_THREAD_SAFETY_ANALYSIS
{
1025 // Note: We rely on the records for mSystemTextFontFamilyName and
1026 // mSystemDisplayFontFamilyName (if present) being *before* the main
1027 // font list, so that those names are known in the content process
1028 // by the time we add the actual family records to the font list.
1029 aList
->entries().AppendElement(
1030 FontFamilyListEntry(mSystemTextFontFamilyName
, FontVisibility::Unknown
,
1031 kTextSizeSystemFontFamily
));
1032 if (mUseSizeSensitiveSystemFont
) {
1033 aList
->entries().AppendElement(FontFamilyListEntry(
1034 mSystemDisplayFontFamilyName
, FontVisibility::Unknown
,
1035 kDisplaySizeSystemFontFamily
));
1037 // Now collect the list of available families, with visibility attributes.
1038 for (auto f
= mFontFamilies
.Iter(); !f
.Done(); f
.Next()) {
1039 auto macFamily
= f
.Data().get();
1040 aList
->entries().AppendElement(FontFamilyListEntry(
1041 macFamily
->Name(), macFamily
->Visibility(), kStandardFontFamily
));
1045 void CoreTextFontList::PreloadNamesList() {
1046 uint32_t numFonts
= mPreloadFonts
.Length();
1047 for (uint32_t i
= 0; i
< numFonts
; i
++) {
1049 GenerateFontListKey(mPreloadFonts
[i
], key
);
1051 // only search canonical names!
1052 gfxFontFamily
* familyEntry
= mFontFamilies
.GetWeak(key
);
1054 familyEntry
->ReadOtherFamilyNames(this);
1059 nsresult
CoreTextFontList::InitFontListForPlatform() {
1060 // The font registration thread was created early in startup, to give the
1061 // system a head start on activating all the supplemental-language fonts.
1062 // Here, we need to wait until it has finished its work.
1063 gfxPlatformMac::WaitForFontRegistration();
1065 Telemetry::AutoTimer
<Telemetry::MAC_INITFONTLIST_TOTAL
> timer
;
1067 InitSystemFontNames();
1069 if (XRE_IsParentProcess()) {
1070 static bool firstTime
= true;
1072 CFNotificationCenterAddObserver(
1073 CFNotificationCenterGetLocalCenter(), this,
1074 RegisteredFontsChangedNotificationCallback
,
1075 kCTFontManagerRegisteredFontsChangedNotification
, 0,
1076 CFNotificationSuspensionBehaviorDeliverImmediately
);
1080 // We're not a content process, so get the available fonts directly
1082 AutoCFRelease
<CFArrayRef
> familyNames
=
1083 CTFontManagerCopyAvailableFontFamilyNames();
1084 for (CFIndex i
= 0; i
< CFArrayGetCount(familyNames
); i
++) {
1085 CFStringRef familyName
=
1086 (CFStringRef
)CFArrayGetValueAtIndex(familyNames
, i
);
1087 AddFamily(familyName
);
1089 for (const auto& name
: kDeprecatedFontFamilies
) {
1090 if (DeprecatedFamilyIsAvailable(name
)) {
1091 AddFamily(name
, GetVisibilityForFamily(name
));
1095 // Content process: use font list passed from the chrome process via
1096 // the GetXPCOMProcessAttributes message, because it's much faster than
1097 // querying Core Text again in the child.
1098 auto& fontList
= dom::ContentChild::GetSingleton()->SystemFontList();
1099 for (FontFamilyListEntry
& ffe
: fontList
.entries()) {
1100 switch (ffe
.entryType()) {
1101 case kStandardFontFamily
:
1102 if (ffe
.familyName() == mSystemTextFontFamilyName
||
1103 ffe
.familyName() == mSystemDisplayFontFamilyName
) {
1106 AddFamily(ffe
.familyName(), ffe
.visibility());
1108 case kTextSizeSystemFontFamily
:
1109 mSystemTextFontFamilyName
= ffe
.familyName();
1111 case kDisplaySizeSystemFontFamily
:
1112 mSystemDisplayFontFamilyName
= ffe
.familyName();
1113 mUseSizeSensitiveSystemFont
= true;
1117 fontList
.entries().Clear();
1120 InitSingleFaceList();
1122 // to avoid full search of font name tables, seed the other names table with
1123 // localized names from some of the prefs fonts which are accessed via their
1124 // localized names. changes in the pref fonts will only cause a font lookup
1125 // miss earlier. this is a simple optimization, it's not required for
1129 // start the delayed cmap loader
1130 GetPrefsAndStartLoader();
1135 void CoreTextFontList::InitSharedFontListForPlatform() {
1136 gfxPlatformMac::WaitForFontRegistration();
1138 InitSystemFontNames();
1140 if (XRE_IsParentProcess()) {
1141 // Only the parent process listens for OS font-changed notifications;
1142 // after rebuilding its list, it will update the content processes.
1143 static bool firstTime
= true;
1145 CFNotificationCenterAddObserver(
1146 CFNotificationCenterGetLocalCenter(), this,
1147 RegisteredFontsChangedNotificationCallback
,
1148 kCTFontManagerRegisteredFontsChangedNotification
, 0,
1149 CFNotificationSuspensionBehaviorDeliverImmediately
);
1153 AutoCFRelease
<CFArrayRef
> familyNames
=
1154 CTFontManagerCopyAvailableFontFamilyNames();
1155 nsTArray
<fontlist::Family::InitData
> families
;
1156 families
.SetCapacity(CFArrayGetCount(familyNames
) +
1157 ArrayLength(kDeprecatedFontFamilies
));
1158 for (CFIndex i
= 0; i
< CFArrayGetCount(familyNames
); ++i
) {
1159 nsAutoString name16
;
1160 CFStringRef familyName
=
1161 (CFStringRef
)CFArrayGetValueAtIndex(familyNames
, i
);
1162 GetStringForCFString(familyName
, name16
);
1163 NS_ConvertUTF16toUTF8
name(name16
);
1165 GenerateFontListKey(name
, key
);
1166 families
.AppendElement(fontlist::Family::InitData(
1167 key
, name
, fontlist::Family::kNoIndex
, GetVisibilityForFamily(name
)));
1169 for (const nsACString
& name
: kDeprecatedFontFamilies
) {
1170 if (DeprecatedFamilyIsAvailable(name
)) {
1172 GenerateFontListKey(name
, key
);
1173 families
.AppendElement(
1174 fontlist::Family::InitData(key
, name
, fontlist::Family::kNoIndex
,
1175 GetVisibilityForFamily(name
)));
1178 SharedFontList()->SetFamilyNames(families
);
1179 InitAliasesForSingleFaceList();
1180 GetPrefsAndStartLoader();
1184 gfxFontFamily
* CoreTextFontList::FindSystemFontFamily(
1185 const nsACString
& aFamily
) {
1187 GenerateFontListKey(aFamily
, key
);
1189 gfxFontFamily
* familyEntry
;
1190 if ((familyEntry
= mFontFamilies
.GetWeak(key
))) {
1191 return CheckFamily(familyEntry
);
1197 void CoreTextFontList::RegisteredFontsChangedNotificationCallback(
1198 CFNotificationCenterRef center
, void* observer
, CFStringRef name
,
1199 const void* object
, CFDictionaryRef userInfo
) {
1200 if (!CFEqual(name
, kCTFontManagerRegisteredFontsChangedNotification
)) {
1204 CoreTextFontList
* fl
= static_cast<CoreTextFontList
*>(observer
);
1205 if (!fl
->IsInitialized()) {
1209 // xxx - should be carefully pruning the list of fonts, not rebuilding it from
1211 fl
->UpdateFontList();
1213 gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::Yes
);
1214 dom::ContentParent::NotifyUpdatedFonts(true);
1217 gfxFontEntry
* CoreTextFontList::PlatformGlobalFontFallback(
1218 nsPresContext
* aPresContext
, const uint32_t aCh
, Script aRunScript
,
1219 const gfxFontStyle
* aMatchStyle
, FontFamily
& aMatchedFamily
) {
1224 if (IS_IN_BMP(aCh
)) {
1226 str
= CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault
, ch
, 1,
1229 ch
[0] = H_SURROGATE(aCh
);
1230 ch
[1] = L_SURROGATE(aCh
);
1231 str
= CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault
, ch
, 2,
1239 // use CoreText to find the fallback family
1241 gfxFontEntry
* fontEntry
= nullptr;
1242 bool cantUseFallbackFont
= false;
1244 if (!mDefaultFont
) {
1245 mDefaultFont
= CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f
, NULL
);
1248 AutoCFRelease
<CTFontRef
> fallback
=
1249 CTFontCreateForString(mDefaultFont
, str
, CFRangeMake(0, length
));
1252 AutoCFRelease
<CFStringRef
> familyNameRef
= CTFontCopyFamilyName(fallback
);
1254 if (familyNameRef
&&
1255 CFStringCompare(familyNameRef
, CFSTR("LastResort"),
1256 kCFCompareCaseInsensitive
) != kCFCompareEqualTo
&&
1257 CFStringCompare(familyNameRef
, CFSTR(".LastResort"),
1258 kCFCompareCaseInsensitive
) != kCFCompareEqualTo
) {
1259 AutoTArray
<UniChar
, 1024> buffer
;
1260 CFIndex familyNameLen
= CFStringGetLength(familyNameRef
);
1261 buffer
.SetLength(familyNameLen
+ 1);
1262 CFStringGetCharacters(familyNameRef
, CFRangeMake(0, familyNameLen
),
1264 buffer
[familyNameLen
] = 0;
1265 NS_ConvertUTF16toUTF8
familyNameString(
1266 reinterpret_cast<char16_t
*>(buffer
.Elements()), familyNameLen
);
1268 if (SharedFontList()) {
1269 fontlist::Family
* family
=
1270 FindSharedFamily(aPresContext
, familyNameString
);
1272 fontlist::Face
* face
=
1273 family
->FindFaceForStyle(SharedFontList(), *aMatchStyle
);
1275 fontEntry
= GetOrCreateFontEntryLocked(face
, family
);
1278 if (fontEntry
->HasCharacter(aCh
)) {
1279 aMatchedFamily
= FontFamily(family
);
1281 fontEntry
= nullptr;
1282 cantUseFallbackFont
= true;
1288 // The macOS system font does not appear in the shared font list, so if
1289 // we didn't find the fallback font above, we should also check for an
1290 // unshared fontFamily in the system list.
1292 gfxFontFamily
* family
= FindSystemFontFamily(familyNameString
);
1294 fontEntry
= family
->FindFontForStyle(*aMatchStyle
);
1296 if (fontEntry
->HasCharacter(aCh
)) {
1297 aMatchedFamily
= FontFamily(family
);
1299 fontEntry
= nullptr;
1300 cantUseFallbackFont
= true;
1308 if (cantUseFallbackFont
) {
1309 Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT
, cantUseFallbackFont
);
1317 gfxFontEntry
* CoreTextFontList::LookupLocalFont(
1318 nsPresContext
* aPresContext
, const nsACString
& aFontName
,
1319 WeightRange aWeightForEntry
, StretchRange aStretchForEntry
,
1320 SlantStyleRange aStyleForEntry
) {
1321 if (aFontName
.IsEmpty() || aFontName
[0] == '.') {
1325 AutoLock
lock(mLock
);
1327 CrashReporter::AutoAnnotateCrashReport
autoFontName(
1328 CrashReporter::Annotation::FontName
, aFontName
);
1330 AutoCFRelease
<CFStringRef
> faceName
= CreateCFStringForString(aFontName
);
1335 // lookup face based on postscript or full name
1336 AutoCFRelease
<CGFontRef
> fontRef
= CGFontCreateWithFontName(faceName
);
1341 // It's possible for CGFontCreateWithFontName to return a font that has been
1342 // deactivated/uninstalled, or a font that is excluded from the font list due
1343 // to CSS font-visibility restriction. So we need to check whether this font
1344 // is allowed to be used.
1346 // CGFontRef doesn't offer a family-name API, so we go via a CTFontRef.
1347 AutoCFRelease
<CTFontRef
> ctFont
=
1348 CTFontCreateWithGraphicsFont(fontRef
, 0.0, nullptr, nullptr);
1352 AutoCFRelease
<CFStringRef
> name
= CTFontCopyFamilyName(ctFont
);
1354 // Convert the family name to a key suitable for font-list lookup (8-bit,
1357 // CFStringGetLength is in UTF-16 code units. The maximum this count can
1358 // expand when converted to UTF-8 is 3x. We add 1 to ensure there will also be
1359 // space for null-termination of the resulting C string.
1360 key
.SetLength((CFStringGetLength(name
) + 1) * 3);
1361 if (!CFStringGetCString(name
, key
.BeginWriting(), key
.Length(),
1362 kCFStringEncodingUTF8
)) {
1363 // This shouldn't ever happen, but if it does we just bail.
1364 NS_WARNING("Failed to get family name?");
1367 if (key
.IsEmpty()) {
1370 // Reset our string length to match the actual C string we got, which will
1371 // usually be much shorter than the maximal buffer we allocated.
1372 key
.Truncate(strlen(key
.get()));
1374 // If the family can't be looked up, this font is not available for use.
1375 FontFamily family
= FindFamily(aPresContext
, key
);
1376 if (family
.IsNull()) {
1380 return new CTFontEntry(aFontName
, fontRef
, aWeightForEntry
, aStretchForEntry
,
1381 aStyleForEntry
, false, true);
1384 static void ReleaseData(void* info
, const void* data
, size_t size
) {
1388 gfxFontEntry
* CoreTextFontList::MakePlatformFont(const nsACString
& aFontName
,
1389 WeightRange aWeightForEntry
,
1390 StretchRange aStretchForEntry
,
1391 SlantStyleRange aStyleForEntry
,
1392 const uint8_t* aFontData
,
1394 NS_ASSERTION(aFontData
, "MakePlatformFont called with null data");
1396 // create the font entry
1397 nsAutoString uniqueName
;
1399 nsresult rv
= gfxFontUtils::MakeUniqueUserFontName(uniqueName
);
1400 if (NS_FAILED(rv
)) {
1404 CrashReporter::AutoAnnotateCrashReport
autoFontName(
1405 CrashReporter::Annotation::FontName
, aFontName
);
1407 AutoCFRelease
<CGDataProviderRef
> provider
=
1408 ::CGDataProviderCreateWithData(nullptr, aFontData
, aLength
, &ReleaseData
);
1409 AutoCFRelease
<CGFontRef
> fontRef
= ::CGFontCreateWithDataProvider(provider
);
1414 auto newFontEntry
= MakeUnique
<CTFontEntry
>(
1415 NS_ConvertUTF16toUTF8(uniqueName
), fontRef
, aWeightForEntry
,
1416 aStretchForEntry
, aStyleForEntry
, true, false);
1417 return newFontEntry
.release();
1420 // Webkit code uses a system font meta name, so mimic that here
1421 // WebCore/platform/graphics/mac/FontCacheMac.mm
1422 static const char kSystemFont_system
[] = "-apple-system";
1424 // System fonts under OSX 10.11 use a combination of two families, one
1425 // for text sizes and another for larger, display sizes. Each has a
1426 // different number of weights. There aren't efficient API's for looking
1427 // this information up, so hard code the logic here but confirm via
1428 // debug assertions that the logic is correct.
1430 const CGFloat kTextDisplayCrossover
= 20.0; // use text family below this size
1432 bool CoreTextFontList::FindAndAddFamiliesLocked(
1433 nsPresContext
* aPresContext
, StyleGenericFontFamily aGeneric
,
1434 const nsACString
& aFamily
, nsTArray
<FamilyAndGeneric
>* aOutput
,
1435 FindFamiliesFlags aFlags
, gfxFontStyle
* aStyle
, nsAtom
* aLanguage
,
1436 gfxFloat aDevToCssSize
) {
1437 if (aFamily
.EqualsLiteral(kSystemFont_system
)) {
1438 // Search for special system font name, -apple-system. This is not done via
1439 // the shared fontlist on Catalina or later, because the hidden system font
1440 // may not be included there; we create a separate gfxFontFamily to manage
1442 const nsCString
& systemFontFamilyName
=
1443 mUseSizeSensitiveSystemFont
&& aStyle
&&
1444 (aStyle
->size
* aDevToCssSize
) >= kTextDisplayCrossover
1445 ? mSystemDisplayFontFamilyName
1446 : mSystemTextFontFamilyName
;
1447 if (auto* fam
= FindSystemFontFamily(systemFontFamilyName
)) {
1448 aOutput
->AppendElement(fam
);
1454 return gfxPlatformFontList::FindAndAddFamiliesLocked(
1455 aPresContext
, aGeneric
, aFamily
, aOutput
, aFlags
, aStyle
, aLanguage
,
1459 // used to load system-wide font info on off-main thread
1460 class CTFontInfo final
: public FontInfoData
{
1462 CTFontInfo(bool aLoadOtherNames
, bool aLoadFaceNames
, bool aLoadCmaps
,
1463 RecursiveMutex
& aLock
)
1464 : FontInfoData(aLoadOtherNames
, aLoadFaceNames
, aLoadCmaps
),
1467 virtual ~CTFontInfo() = default;
1469 virtual void Load() { FontInfoData::Load(); }
1471 // loads font data for all members of a given family
1472 virtual void LoadFontFamilyData(const nsACString
& aFamilyName
);
1474 RecursiveMutex
& mLock
;
1477 void CTFontInfo::LoadFontFamilyData(const nsACString
& aFamilyName
) {
1478 CrashReporter::AutoAnnotateCrashReport
autoFontName(
1479 CrashReporter::Annotation::FontName
, aFamilyName
);
1480 // Prevent this from running concurrently with CGFont operations on the main
1481 // thread, because the macOS font cache is fragile with concurrent access.
1482 // This appears to be a vulnerability within CoreText in versions of macOS
1483 // before macOS 13. In time, we can remove this lock.
1484 RecursiveMutexAutoLock
lock(mLock
);
1486 // family name ==> CTFontDescriptor
1487 AutoCFRelease
<CFStringRef
> family
= CreateCFStringForString(aFamilyName
);
1489 AutoCFRelease
<CFMutableDictionaryRef
> attr
=
1490 CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
1491 &kCFTypeDictionaryValueCallBacks
);
1492 CFDictionaryAddValue(attr
, kCTFontFamilyNameAttribute
, family
);
1493 AutoCFRelease
<CTFontDescriptorRef
> fd
=
1494 CTFontDescriptorCreateWithAttributes(attr
);
1495 AutoCFRelease
<CFArrayRef
> matchingFonts
=
1496 CTFontDescriptorCreateMatchingFontDescriptors(fd
, NULL
);
1497 if (!matchingFonts
) {
1501 nsTArray
<nsCString
> otherFamilyNames
;
1502 bool hasOtherFamilyNames
= true;
1504 // iterate over faces in the family
1505 int f
, numFaces
= (int)CFArrayGetCount(matchingFonts
);
1506 for (f
= 0; f
< numFaces
; f
++) {
1509 CTFontDescriptorRef faceDesc
=
1510 (CTFontDescriptorRef
)CFArrayGetValueAtIndex(matchingFonts
, f
);
1514 AutoCFRelease
<CTFontRef
> fontRef
=
1515 CTFontCreateWithFontDescriptor(faceDesc
, 0.0, nullptr);
1517 NS_WARNING("failed to create a CTFontRef");
1523 AutoCFRelease
<CFStringRef
> faceName
=
1524 (CFStringRef
)CTFontDescriptorCopyAttribute(faceDesc
,
1525 kCTFontNameAttribute
);
1527 AutoTArray
<UniChar
, 1024> buffer
;
1528 CFIndex len
= CFStringGetLength(faceName
);
1529 buffer
.SetLength(len
+ 1);
1530 CFStringGetCharacters(faceName
, CFRangeMake(0, len
), buffer
.Elements());
1532 NS_ConvertUTF16toUTF8
fontName(
1533 reinterpret_cast<char16_t
*>(buffer
.Elements()), len
);
1535 // load the cmap data
1536 FontFaceData fontData
;
1537 AutoCFRelease
<CFDataRef
> cmapTable
= CTFontCopyTable(
1538 fontRef
, kCTFontTableCmap
, kCTFontTableOptionNoOptions
);
1541 const uint8_t* cmapData
= (const uint8_t*)CFDataGetBytePtr(cmapTable
);
1542 uint32_t cmapLen
= CFDataGetLength(cmapTable
);
1543 RefPtr
<gfxCharacterMap
> charmap
= new gfxCharacterMap();
1547 rv
= gfxFontUtils::ReadCMAP(cmapData
, cmapLen
, *charmap
, offset
);
1548 if (NS_SUCCEEDED(rv
)) {
1549 fontData
.mCharacterMap
= charmap
;
1550 fontData
.mUVSOffset
= offset
;
1555 mFontFaceData
.InsertOrUpdate(fontName
, fontData
);
1558 if (mLoadOtherNames
&& hasOtherFamilyNames
) {
1559 AutoCFRelease
<CFDataRef
> nameTable
= CTFontCopyTable(
1560 fontRef
, kCTFontTableName
, kCTFontTableOptionNoOptions
);
1563 const char* nameData
= (const char*)CFDataGetBytePtr(nameTable
);
1564 uint32_t nameLen
= CFDataGetLength(nameTable
);
1565 gfxFontUtils::ReadOtherFamilyNamesForFace(
1566 aFamilyName
, nameData
, nameLen
, otherFamilyNames
, false);
1567 hasOtherFamilyNames
= otherFamilyNames
.Length() != 0;
1572 // if found other names, insert them in the hash table
1573 if (otherFamilyNames
.Length() != 0) {
1574 mOtherFamilyNames
.InsertOrUpdate(aFamilyName
, otherFamilyNames
);
1575 mLoadStats
.othernames
+= otherFamilyNames
.Length();
1579 already_AddRefed
<FontInfoData
> CoreTextFontList::CreateFontInfoData() {
1580 bool loadCmaps
= !UsesSystemFallback() ||
1581 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1583 mLock
.AssertCurrentThreadIn();
1584 RefPtr
<CTFontInfo
> fi
=
1585 new CTFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps
, mLock
);
1589 gfxFontFamily
* CoreTextFontList::CreateFontFamily(
1590 const nsACString
& aName
, FontVisibility aVisibility
) const {
1591 return new CTFontFamily(aName
, aVisibility
);
1594 gfxFontEntry
* CoreTextFontList::CreateFontEntry(
1595 fontlist::Face
* aFace
, const fontlist::Family
* aFamily
) {
1596 CTFontEntry
* fe
= new CTFontEntry(
1597 aFace
->mDescriptor
.AsString(SharedFontList()), aFace
->mWeight
, false,
1598 0.0); // XXX standardFace, sizeHint
1599 fe
->InitializeFrom(aFace
, aFamily
);
1603 void CoreTextFontList::AddFaceInitData(
1604 CTFontDescriptorRef aFontDesc
, nsTArray
<fontlist::Face::InitData
>& aFaces
,
1606 AutoCFRelease
<CFStringRef
> psname
=
1607 (CFStringRef
)CTFontDescriptorCopyAttribute(aFontDesc
,
1608 kCTFontNameAttribute
);
1609 AutoCFRelease
<CFStringRef
> facename
=
1610 (CFStringRef
)CTFontDescriptorCopyAttribute(aFontDesc
,
1611 kCTFontStyleNameAttribute
);
1612 AutoCFRelease
<CFDictionaryRef
> traitsDict
=
1613 (CFDictionaryRef
)CTFontDescriptorCopyAttribute(aFontDesc
,
1614 kCTFontTraitsAttribute
);
1616 CFNumberRef weight
=
1617 (CFNumberRef
)CFDictionaryGetValue(traitsDict
, kCTFontWeightTrait
);
1619 (CFNumberRef
)CFDictionaryGetValue(traitsDict
, kCTFontWidthTrait
);
1620 CFNumberRef symbolicTraits
=
1621 (CFNumberRef
)CFDictionaryGetValue(traitsDict
, kCTFontSymbolicTrait
);
1624 nsAutoString postscriptFontName
;
1625 GetStringForCFString(psname
, postscriptFontName
);
1627 int32_t cssWeight
= PR_GetCurrentThread() == sInitFontListThread
1629 : GetWeightOverride(postscriptFontName
);
1631 // scale down and clamp, to get a value from 1..9
1632 cssWeight
= ((cssWeight
+ 50) / 100);
1633 cssWeight
= std::max(1, std::min(cssWeight
, 9));
1634 cssWeight
*= 100; // scale up to CSS values
1636 CGFloat weightValue
;
1637 CFNumberGetValue(weight
, kCFNumberCGFloatType
, &weightValue
);
1638 cssWeight
= CoreTextWeightToCSSWeight(weightValue
);
1642 CFNumberGetValue(width
, kCFNumberCGFloatType
, &widthValue
);
1643 StretchRange
stretch(CoreTextWidthToCSSStretch(widthValue
));
1645 SlantStyleRange
slantStyle(FontSlantStyle::NORMAL
);
1647 CFNumberGetValue(symbolicTraits
, kCFNumberSInt32Type
, &traitsValue
);
1648 if (traitsValue
& kCTFontItalicTrait
) {
1649 slantStyle
= SlantStyleRange(FontSlantStyle::ITALIC
);
1652 bool fixedPitch
= traitsValue
& kCTFontMonoSpaceTrait
;
1654 RefPtr
<gfxCharacterMap
> charmap
;
1656 AutoCFRelease
<CGFontRef
> font
=
1657 CGFontCreateWithFontName(CFStringRef(psname
));
1659 uint32_t kCMAP
= TRUETYPE_TAG('c', 'm', 'a', 'p');
1660 AutoCFRelease
<CFDataRef
> data
= CGFontCopyTableForTag(font
, kCMAP
);
1663 charmap
= new gfxCharacterMap();
1664 gfxFontUtils::ReadCMAP(CFDataGetBytePtr(data
), CFDataGetLength(data
),
1670 // Ensure that a face named "Regular" goes to the front of the list, so it
1671 // will take precedence over other faces with the same style attributes but
1672 // a different name (such as "Outline").
1673 auto data
= fontlist::Face::InitData
{
1674 NS_ConvertUTF16toUTF8(postscriptFontName
),
1677 WeightRange(FontWeight::FromInt(cssWeight
)),
1682 if (kCFCompareEqualTo
== CFStringCompare(facename
, CFSTR("Regular"), 0)) {
1683 aFaces
.InsertElementAt(0, std::move(data
));
1685 aFaces
.AppendElement(std::move(data
));
1689 void CoreTextFontList::GetFacesInitDataForFamily(
1690 const fontlist::Family
* aFamily
, nsTArray
<fontlist::Face::InitData
>& aFaces
,
1691 bool aLoadCmaps
) const {
1692 auto name
= aFamily
->Key().AsString(SharedFontList());
1693 CrashReporter::AutoAnnotateCrashReport
autoFontName(
1694 CrashReporter::Annotation::FontName
, name
);
1697 nsTArray
<fontlist::Face::InitData
>& mFaces
;
1700 auto addFaceFunc
= [](const void* aValue
, void* aContext
) -> void {
1701 CTFontDescriptorRef fontDesc
= (CTFontDescriptorRef
)aValue
;
1702 Context
* context
= (Context
*)aContext
;
1703 CoreTextFontList::AddFaceInitData(fontDesc
, context
->mFaces
,
1704 context
->mLoadCmaps
);
1707 AutoCFRelease
<CTFontDescriptorRef
> descriptor
=
1708 CreateDescriptorForFamily(name
, false);
1709 AutoCFRelease
<CFArrayRef
> faces
=
1710 CTFontDescriptorCreateMatchingFontDescriptors(descriptor
, nullptr);
1713 Context context
{aFaces
, aLoadCmaps
};
1714 CFArrayApplyFunction(faces
, CFRangeMake(0, CFArrayGetCount(faces
)),
1715 addFaceFunc
, &context
);
1719 void CoreTextFontList::ReadFaceNamesForFamily(
1720 fontlist::Family
* aFamily
, bool aNeedFullnamePostscriptNames
) {
1721 if (!aFamily
->IsInitialized()) {
1722 if (!InitializeFamily(aFamily
)) {
1726 const uint32_t kNAME
= TRUETYPE_TAG('n', 'a', 'm', 'e');
1727 fontlist::FontList
* list
= SharedFontList();
1728 nsAutoCString
canonicalName(aFamily
->DisplayName().AsString(list
));
1729 const auto* facePtrs
= aFamily
->Faces(list
);
1730 for (uint32_t i
= 0, n
= aFamily
->NumFaces(); i
< n
; i
++) {
1731 auto* face
= facePtrs
[i
].ToPtr
<const fontlist::Face
>(list
);
1735 nsAutoCString
name(face
->mDescriptor
.AsString(list
));
1736 // We create a temporary CTFontEntry just to read family names from the
1737 // 'name' table in the font resource. The style attributes here are ignored
1738 // as this entry is not used for font style matching.
1739 // The size hint might be used to select which face is accessed in the case
1740 // of the macOS UI font; see CTFontEntry::GetFontRef(). We pass 16.0 in
1741 // order to get a standard text-size face in this case, although it's
1742 // unlikely to matter for the purpose of just reading family names.
1743 auto fe
= MakeUnique
<CTFontEntry
>(name
, WeightRange(FontWeight::NORMAL
),
1748 gfxFontEntry::AutoTable
nameTable(fe
.get(), kNAME
);
1752 uint32_t dataLength
;
1753 const char* nameData
= hb_blob_get_data(nameTable
, &dataLength
);
1754 AutoTArray
<nsCString
, 4> otherFamilyNames
;
1755 gfxFontUtils::ReadOtherFamilyNamesForFace(
1756 canonicalName
, nameData
, dataLength
, otherFamilyNames
, false);
1757 for (const auto& alias
: otherFamilyNames
) {
1759 GenerateFontListKey(alias
, key
);
1760 auto aliasData
= mAliasTable
.GetOrInsertNew(key
);
1761 aliasData
->InitFromFamily(aFamily
, canonicalName
);
1762 aliasData
->mFaces
.AppendElement(facePtrs
[i
]);
1767 #ifdef MOZ_BUNDLED_FONTS
1768 void CoreTextFontList::ActivateBundledFonts() {
1769 nsCOMPtr
<nsIFile
> localDir
;
1770 if (NS_FAILED(NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(localDir
)))) {
1773 if (NS_FAILED(localDir
->Append(u
"fonts"_ns
))) {
1777 if (NS_FAILED(localDir
->GetNativePath(path
))) {
1780 ActivateFontsFromDir(path
, &mBundledFamilies
);