Bug 1874684 - Part 28: Return DateDuration from DifferenceISODateTime. r=mgaudet
[gecko.git] / gfx / thebes / CoreTextFontList.cpp
blobe83435b638fdb2c2466d6f782c9becf2697de2d6
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 #ifdef MOZ_WIDGET_COCOA
39 // Building with newer macOS SDKs can cause a bunch of font-family names to be
40 // hidden from the Core Text API we use to enumerate available fonts. Because
41 // some content still benefits from having these names recognized, we forcibly
42 // include them in the list. Some day we might want to drop support for these.
43 # define USE_DEPRECATED_FONT_FAMILY_NAMES 1
44 #endif
46 #if USE_DEPRECATED_FONT_FAMILY_NAMES
47 // List generated by diffing the arrays returned by
48 // CTFontManagerCopyAvailableFontFamilyNames() when built with
49 // MACOSX_DEPLOYMENT_TARGET=10.12 vs 11.0, to identify the font family names
50 // that Core Text is treating as "deprecated" and hiding from the app on newer
51 // systems.
52 constexpr nsLiteralCString kDeprecatedFontFamilies[] = {
53 // Dot-prefixed font families are supposed to be hidden from the
54 // user-visible
55 // font list anyhow, so we don't need to add them here.
56 // ".Al Bayan PUA"_ns,
57 // ".Al Nile PUA"_ns,
58 // ".Al Tarikh PUA"_ns,
59 // ".Apple Color Emoji UI"_ns,
60 // ".Apple SD Gothic NeoI"_ns,
61 // ".Aqua Kana"_ns,
62 // ".Arial Hebrew Desk Interface"_ns,
63 // ".Baghdad PUA"_ns,
64 // ".Beirut PUA"_ns,
65 // ".Damascus PUA"_ns,
66 // ".DecoType Naskh PUA"_ns,
67 // ".Diwan Kufi PUA"_ns,
68 // ".Farah PUA"_ns,
69 // ".Geeza Pro Interface"_ns,
70 // ".Geeza Pro PUA"_ns,
71 // ".Helvetica LT MM"_ns,
72 // ".Hiragino Kaku Gothic Interface"_ns,
73 // ".Hiragino Sans GB Interface"_ns,
74 // ".Keyboard"_ns,
75 // ".KufiStandardGK PUA"_ns,
76 // ".LastResort"_ns,
77 // ".Lucida Grande UI"_ns,
78 // ".Muna PUA"_ns,
79 // ".Nadeem PUA"_ns,
80 // ".New York"_ns,
81 // ".Noto Nastaliq Urdu UI"_ns,
82 // ".PingFang HK"_ns,
83 // ".PingFang SC"_ns,
84 // ".PingFang TC"_ns,
85 // ".Sana PUA"_ns,
86 // ".Savoye LET CC."_ns,
87 // ".SF Arabic"_ns,
88 // ".SF Compact Rounded"_ns,
89 // ".SF Compact"_ns,
90 // ".SF NS Mono"_ns,
91 // ".SF NS Rounded"_ns,
92 // ".SF NS"_ns,
93 // ".Times LT MM"_ns,
94 "Hiragino Kaku Gothic Pro"_ns,
95 "Hiragino Kaku Gothic ProN"_ns,
96 "Hiragino Kaku Gothic Std"_ns,
97 "Hiragino Kaku Gothic StdN"_ns,
98 "Hiragino Maru Gothic Pro"_ns,
99 "Hiragino Mincho Pro"_ns,
100 "Iowan Old Style"_ns,
101 "Noto Sans Adlam"_ns,
102 "Noto Sans Armenian"_ns,
103 "Noto Sans Avestan"_ns,
104 "Noto Sans Bamum"_ns,
105 "Noto Sans Bassa Vah"_ns,
106 "Noto Sans Batak"_ns,
107 "Noto Sans Bhaiksuki"_ns,
108 "Noto Sans Brahmi"_ns,
109 "Noto Sans Buginese"_ns,
110 "Noto Sans Buhid"_ns,
111 "Noto Sans Carian"_ns,
112 "Noto Sans Caucasian Albanian"_ns,
113 "Noto Sans Chakma"_ns,
114 "Noto Sans Cham"_ns,
115 "Noto Sans Coptic"_ns,
116 "Noto Sans Cuneiform"_ns,
117 "Noto Sans Cypriot"_ns,
118 "Noto Sans Duployan"_ns,
119 "Noto Sans Egyptian Hieroglyphs"_ns,
120 "Noto Sans Elbasan"_ns,
121 "Noto Sans Glagolitic"_ns,
122 "Noto Sans Gothic"_ns,
123 "Noto Sans Gunjala Gondi"_ns,
124 "Noto Sans Hanifi Rohingya"_ns,
125 "Noto Sans Hanunoo"_ns,
126 "Noto Sans Hatran"_ns,
127 "Noto Sans Imperial Aramaic"_ns,
128 "Noto Sans Inscriptional Pahlavi"_ns,
129 "Noto Sans Inscriptional Parthian"_ns,
130 "Noto Sans Javanese"_ns,
131 "Noto Sans Kaithi"_ns,
132 "Noto Sans Kayah Li"_ns,
133 "Noto Sans Kharoshthi"_ns,
134 "Noto Sans Khojki"_ns,
135 "Noto Sans Khudawadi"_ns,
136 "Noto Sans Lepcha"_ns,
137 "Noto Sans Limbu"_ns,
138 "Noto Sans Linear A"_ns,
139 "Noto Sans Linear B"_ns,
140 "Noto Sans Lisu"_ns,
141 "Noto Sans Lycian"_ns,
142 "Noto Sans Lydian"_ns,
143 "Noto Sans Mahajani"_ns,
144 "Noto Sans Mandaic"_ns,
145 "Noto Sans Manichaean"_ns,
146 "Noto Sans Marchen"_ns,
147 "Noto Sans Masaram Gondi"_ns,
148 "Noto Sans Meetei Mayek"_ns,
149 "Noto Sans Mende Kikakui"_ns,
150 "Noto Sans Meroitic"_ns,
151 "Noto Sans Miao"_ns,
152 "Noto Sans Modi"_ns,
153 "Noto Sans Mongolian"_ns,
154 "Noto Sans Mro"_ns,
155 "Noto Sans Multani"_ns,
156 "Noto Sans Nabataean"_ns,
157 "Noto Sans New Tai Lue"_ns,
158 "Noto Sans Newa"_ns,
159 "Noto Sans NKo"_ns,
160 "Noto Sans Ol Chiki"_ns,
161 "Noto Sans Old Hungarian"_ns,
162 "Noto Sans Old Italic"_ns,
163 "Noto Sans Old North Arabian"_ns,
164 "Noto Sans Old Permic"_ns,
165 "Noto Sans Old Persian"_ns,
166 "Noto Sans Old South Arabian"_ns,
167 "Noto Sans Old Turkic"_ns,
168 "Noto Sans Osage"_ns,
169 "Noto Sans Osmanya"_ns,
170 "Noto Sans Pahawh Hmong"_ns,
171 "Noto Sans Palmyrene"_ns,
172 "Noto Sans Pau Cin Hau"_ns,
173 "Noto Sans PhagsPa"_ns,
174 "Noto Sans Phoenician"_ns,
175 "Noto Sans Psalter Pahlavi"_ns,
176 "Noto Sans Rejang"_ns,
177 "Noto Sans Samaritan"_ns,
178 "Noto Sans Saurashtra"_ns,
179 "Noto Sans Sharada"_ns,
180 "Noto Sans Siddham"_ns,
181 "Noto Sans Sora Sompeng"_ns,
182 "Noto Sans Sundanese"_ns,
183 "Noto Sans Syloti Nagri"_ns,
184 "Noto Sans Syriac"_ns,
185 "Noto Sans Tagalog"_ns,
186 "Noto Sans Tagbanwa"_ns,
187 "Noto Sans Tai Le"_ns,
188 "Noto Sans Tai Tham"_ns,
189 "Noto Sans Tai Viet"_ns,
190 "Noto Sans Takri"_ns,
191 "Noto Sans Thaana"_ns,
192 "Noto Sans Tifinagh"_ns,
193 "Noto Sans Tirhuta"_ns,
194 "Noto Sans Ugaritic"_ns,
195 "Noto Sans Vai"_ns,
196 "Noto Sans Wancho"_ns,
197 "Noto Sans Warang Citi"_ns,
198 "Noto Sans Yi"_ns,
199 "Noto Sans Zawgyi"_ns,
200 "Noto Serif Ahom"_ns,
201 "Noto Serif Balinese"_ns,
202 "Noto Serif Yezidi"_ns,
203 "Athelas"_ns,
204 "Courier"_ns,
205 "Marion"_ns,
206 "Seravek"_ns,
207 "Superclarendon"_ns,
208 "Times"_ns,
210 #endif // USE_DEPRECATED_FONT_FAMILY_NAMES
212 static void GetStringForCFString(CFStringRef aSrc, nsAString& aDest) {
213 auto len = CFStringGetLength(aSrc);
214 aDest.SetLength(len);
215 CFStringGetCharacters(aSrc, CFRangeMake(0, len),
216 (UniChar*)aDest.BeginWriting());
219 static CFStringRef CreateCFStringForString(const nsACString& aSrc) {
220 return CFStringCreateWithBytes(kCFAllocatorDefault,
221 (const UInt8*)aSrc.BeginReading(),
222 aSrc.Length(), kCFStringEncodingUTF8, false);
225 #define LOG_FONTLIST(args) \
226 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug, args)
227 #define LOG_FONTLIST_ENABLED() \
228 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), mozilla::LogLevel::Debug)
229 #define LOG_CMAPDATA_ENABLED() \
230 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), mozilla::LogLevel::Debug)
232 #pragma mark -
234 // Complex scripts will not render correctly unless appropriate AAT or OT
235 // layout tables are present.
236 // For OpenType, we also check that the GSUB table supports the relevant
237 // script tag, to avoid using things like Arial Unicode MS for Lao (it has
238 // the characters, but lacks OpenType support).
240 // TODO: consider whether we should move this to gfxFontEntry and do similar
241 // cmap-masking on other platforms to avoid using fonts that won't shape
242 // properly.
244 nsresult CTFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
245 // attempt this once, if errors occur leave a blank cmap
246 if (mCharacterMap || mShmemCharacterMap) {
247 return NS_OK;
250 RefPtr<gfxCharacterMap> charmap;
251 nsresult rv;
253 uint32_t uvsOffset = 0;
254 if (aFontInfoData &&
255 (charmap = GetCMAPFromFontInfo(aFontInfoData, uvsOffset))) {
256 rv = NS_OK;
257 } else {
258 uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
259 charmap = new gfxCharacterMap();
260 AutoTable cmapTable(this, kCMAP);
262 if (cmapTable) {
263 uint32_t cmapLen;
264 const uint8_t* cmapData = reinterpret_cast<const uint8_t*>(
265 hb_blob_get_data(cmapTable, &cmapLen));
266 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, uvsOffset);
267 } else {
268 rv = NS_ERROR_NOT_AVAILABLE;
271 mUVSOffset.exchange(uvsOffset);
273 if (NS_SUCCEEDED(rv) && !mIsDataUserFont && !HasGraphiteTables()) {
274 // For downloadable fonts, trust the author and don't
275 // try to munge the cmap based on script shaping support.
277 // We also assume a Graphite font knows what it's doing,
278 // and provides whatever shaping is needed for the
279 // characters it supports, so only check/clear the
280 // complex-script ranges for non-Graphite fonts
282 // for layout support, check for the presence of mort/morx/kerx and/or
283 // opentype layout tables
284 bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m', 'o', 'r', 'x')) ||
285 HasFontTable(TRUETYPE_TAG('m', 'o', 'r', 't'));
286 bool hasAppleKerning = HasFontTable(TRUETYPE_TAG('k', 'e', 'r', 'x'));
287 bool hasGSUB = HasFontTable(TRUETYPE_TAG('G', 'S', 'U', 'B'));
288 bool hasGPOS = HasFontTable(TRUETYPE_TAG('G', 'P', 'O', 'S'));
289 if ((hasAATLayout && !(hasGSUB || hasGPOS)) || hasAppleKerning) {
290 mRequiresAAT = true; // prefer CoreText if font has no OTL tables,
291 // or if it uses the Apple-specific 'kerx'
292 // variant of kerning table
295 for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
296 sr->rangeStart; sr++) {
297 // check to see if the cmap includes complex script codepoints
298 if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
299 if (hasAATLayout) {
300 // prefer CoreText for Apple's complex-script fonts,
301 // even if they also have some OpenType tables
302 // (e.g. Geeza Pro Bold on 10.6; see bug 614903)
303 mRequiresAAT = true;
304 // and don't mask off complex-script ranges, we assume
305 // the AAT tables will provide the necessary shaping
306 continue;
309 // We check for GSUB here, as GPOS alone would not be ok.
310 if (hasGSUB && SupportsScriptInGSUB(sr->tags, sr->numTags)) {
311 continue;
314 charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
318 // Bug 1360309, 1393624: several of Apple's Chinese fonts have spurious
319 // blank glyphs for obscure Tibetan and Arabic-script codepoints.
320 // Blocklist these so that font fallback will not use them.
321 if (mRequiresAAT &&
322 (FamilyName().EqualsLiteral("Songti SC") ||
323 FamilyName().EqualsLiteral("Songti TC") ||
324 FamilyName().EqualsLiteral("STSong") ||
325 // Bug 1390980: on 10.11, the Kaiti fonts are also affected.
326 FamilyName().EqualsLiteral("Kaiti SC") ||
327 FamilyName().EqualsLiteral("Kaiti TC") ||
328 FamilyName().EqualsLiteral("STKaiti"))) {
329 charmap->ClearRange(0x0f6b, 0x0f70);
330 charmap->ClearRange(0x0f8c, 0x0f8f);
331 charmap->clear(0x0f98);
332 charmap->clear(0x0fbd);
333 charmap->ClearRange(0x0fcd, 0x0fff);
334 charmap->clear(0x0620);
335 charmap->clear(0x065f);
336 charmap->ClearRange(0x06ee, 0x06ef);
337 charmap->clear(0x06ff);
341 bool setCharMap = true;
342 if (NS_SUCCEEDED(rv)) {
343 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
344 fontlist::FontList* sharedFontList = pfl->SharedFontList();
345 if (!IsUserFont() && mShmemFace && mShmemFamily) {
346 mShmemFace->SetCharacterMap(sharedFontList, charmap, mShmemFamily);
347 if (TrySetShmemCharacterMap()) {
348 setCharMap = false;
350 } else {
351 charmap = pfl->FindCharMap(charmap);
353 mHasCmapTable = true;
354 } else {
355 // if error occurred, initialize to null cmap
356 charmap = new gfxCharacterMap();
357 mHasCmapTable = false;
359 if (setCharMap) {
360 // Temporarily retain charmap, until the shared version is
361 // ready for use.
362 if (mCharacterMap.compareExchange(nullptr, charmap.get())) {
363 charmap.get()->AddRef();
367 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n",
368 mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
369 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
370 if (LOG_CMAPDATA_ENABLED()) {
371 char prefix[256];
372 SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
373 charmap->Dump(prefix, eGfxLog_cmapdata);
376 return rv;
379 gfxFont* CTFontEntry::CreateFontInstance(const gfxFontStyle* aFontStyle) {
380 RefPtr<UnscaledFontMac> unscaledFont(mUnscaledFont);
381 if (!unscaledFont) {
382 CGFontRef baseFont = GetFontRef();
383 if (!baseFont) {
384 return nullptr;
386 unscaledFont = new UnscaledFontMac(baseFont, mIsDataUserFont);
387 mUnscaledFont = unscaledFont;
390 return new gfxMacFont(unscaledFont, this, aFontStyle);
393 bool CTFontEntry::HasVariations() {
394 if (!mHasVariationsInitialized) {
395 mHasVariationsInitialized = true;
396 mHasVariations = gfxPlatform::HasVariationFontSupport() &&
397 HasFontTable(TRUETYPE_TAG('f', 'v', 'a', 'r'));
400 return mHasVariations;
403 void CTFontEntry::GetVariationAxes(
404 nsTArray<gfxFontVariationAxis>& aVariationAxes) {
405 // We could do this by creating a CTFont and calling CTFontCopyVariationAxes,
406 // but it is expensive to instantiate a CTFont for every face just to set up
407 // the axis information.
408 // Instead we use gfxFontUtils to read the font tables directly.
409 gfxFontUtils::GetVariationData(this, &aVariationAxes, nullptr);
412 void CTFontEntry::GetVariationInstances(
413 nsTArray<gfxFontVariationInstance>& aInstances) {
414 // Core Text doesn't offer API for this, so we use gfxFontUtils to read the
415 // font tables directly.
416 gfxFontUtils::GetVariationData(this, nullptr, &aInstances);
419 bool CTFontEntry::IsCFF() {
420 if (!mIsCFFInitialized) {
421 mIsCFFInitialized = true;
422 mIsCFF = HasFontTable(TRUETYPE_TAG('C', 'F', 'F', ' '));
425 return mIsCFF;
428 CTFontEntry::CTFontEntry(const nsACString& aPostscriptName, WeightRange aWeight,
429 bool aIsStandardFace, double aSizeHint)
430 : gfxFontEntry(aPostscriptName, aIsStandardFace),
431 mFontRef(NULL),
432 mSizeHint(aSizeHint),
433 mFontRefInitialized(false),
434 mRequiresAAT(false),
435 mIsCFF(false),
436 mIsCFFInitialized(false),
437 mHasVariations(false),
438 mHasVariationsInitialized(false),
439 mHasAATSmallCaps(false),
440 mHasAATSmallCapsInitialized(false) {
441 mWeightRange = aWeight;
442 mOpszAxis.mTag = 0;
445 CTFontEntry::CTFontEntry(const nsACString& aPostscriptName, CGFontRef aFontRef,
446 WeightRange aWeight, StretchRange aStretch,
447 SlantStyleRange aStyle, bool aIsDataUserFont,
448 bool aIsLocalUserFont)
449 : gfxFontEntry(aPostscriptName, false),
450 mFontRef(NULL),
451 mSizeHint(0.0),
452 mFontRefInitialized(false),
453 mRequiresAAT(false),
454 mIsCFF(false),
455 mIsCFFInitialized(false),
456 mHasVariations(false),
457 mHasVariationsInitialized(false),
458 mHasAATSmallCaps(false),
459 mHasAATSmallCapsInitialized(false) {
460 mFontRef = aFontRef;
461 mFontRefInitialized = true;
462 CFRetain(mFontRef);
464 mWeightRange = aWeight;
465 mStretchRange = aStretch;
466 mFixedPitch = false; // xxx - do we need this for downloaded fonts?
467 mStyleRange = aStyle;
468 mOpszAxis.mTag = 0;
470 NS_ASSERTION(!(aIsDataUserFont && aIsLocalUserFont),
471 "userfont is either a data font or a local font");
472 mIsDataUserFont = aIsDataUserFont;
473 mIsLocalUserFont = aIsLocalUserFont;
476 gfxFontEntry* CTFontEntry::Clone() const {
477 MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
478 CTFontEntry* fe = new CTFontEntry(Name(), Weight(), mStandardFace, mSizeHint);
479 fe->mStyleRange = mStyleRange;
480 fe->mStretchRange = mStretchRange;
481 fe->mFixedPitch = mFixedPitch;
482 return fe;
485 CGFontRef CTFontEntry::GetFontRef() {
487 AutoReadLock lock(mLock);
488 if (mFontRefInitialized) {
489 return mFontRef;
492 AutoWriteLock lock(mLock);
493 if (!mFontRefInitialized) {
494 // Cache the CGFontRef, to be released by our destructor.
495 mFontRef = CreateOrCopyFontRef();
496 mFontRefInitialized = true;
498 // Return a non-retained reference; caller does not need to release.
499 return mFontRef;
502 CGFontRef CTFontEntry::CreateOrCopyFontRef() {
503 if (mFontRef) {
504 // We have a cached CGFont, just add a reference. Caller must
505 // release, but we'll still own our reference.
506 ::CGFontRetain(mFontRef);
507 return mFontRef;
510 CrashReporter::AutoRecordAnnotation autoFontName(
511 CrashReporter::Annotation::FontName, mName);
513 // Create a new CGFont; caller will own the only reference to it.
514 AutoCFRelease<CFStringRef> psname = CreateCFStringForString(mName);
515 if (!psname) {
516 return nullptr;
519 CGFontRef ref = CGFontCreateWithFontName(psname);
520 return ref; // Not saved in mFontRef; caller will own the reference
523 // For a logging build, we wrap the CFDataRef in a FontTableRec so that we can
524 // use the MOZ_COUNT_[CD]TOR macros in it. A release build without logging
525 // does not get this overhead.
526 class FontTableRec {
527 public:
528 explicit FontTableRec(CFDataRef aDataRef) : mDataRef(aDataRef) {
529 MOZ_COUNT_CTOR(FontTableRec);
532 ~FontTableRec() {
533 MOZ_COUNT_DTOR(FontTableRec);
534 CFRelease(mDataRef);
537 private:
538 CFDataRef mDataRef;
541 /*static*/ void CTFontEntry::DestroyBlobFunc(void* aUserData) {
542 #ifdef NS_BUILD_REFCNT_LOGGING
543 FontTableRec* ftr = static_cast<FontTableRec*>(aUserData);
544 delete ftr;
545 #else
546 CFRelease((CFDataRef)aUserData);
547 #endif
550 hb_blob_t* CTFontEntry::GetFontTable(uint32_t aTag) {
551 mLock.ReadLock();
552 AutoCFRelease<CGFontRef> fontRef = CreateOrCopyFontRef();
553 mLock.ReadUnlock();
554 if (!fontRef) {
555 return nullptr;
558 CFDataRef dataRef = ::CGFontCopyTableForTag(fontRef, aTag);
559 if (dataRef) {
560 return hb_blob_create((const char*)CFDataGetBytePtr(dataRef),
561 CFDataGetLength(dataRef), HB_MEMORY_MODE_READONLY,
562 #ifdef NS_BUILD_REFCNT_LOGGING
563 new FontTableRec(dataRef),
564 #else
565 (void*)dataRef,
566 #endif
567 DestroyBlobFunc);
570 return nullptr;
573 bool CTFontEntry::HasFontTable(uint32_t aTableTag) {
575 // If we've already initialized mAvailableTables, we can return without
576 // needing to take an exclusive lock.
577 AutoReadLock lock(mLock);
578 if (mAvailableTables.Count()) {
579 return mAvailableTables.GetEntry(aTableTag);
583 AutoWriteLock lock(mLock);
584 if (mAvailableTables.Count() == 0) {
585 AutoCFRelease<CGFontRef> fontRef = CreateOrCopyFontRef();
586 if (!fontRef) {
587 return false;
589 AutoCFRelease<CFArrayRef> tags = ::CGFontCopyTableTags(fontRef);
590 if (!tags) {
591 return false;
593 int numTags = (int)CFArrayGetCount(tags);
594 for (int t = 0; t < numTags; t++) {
595 uint32_t tag = (uint32_t)(uintptr_t)CFArrayGetValueAtIndex(tags, t);
596 mAvailableTables.PutEntry(tag);
600 return mAvailableTables.GetEntry(aTableTag);
603 static bool CheckForAATSmallCaps(CFArrayRef aFeatures) {
604 // Walk the array of feature descriptors from the font, and see whether
605 // a small-caps feature setting is available.
606 // Just bail out (returning false) if at any point we fail to find the
607 // expected dictionary keys, etc; if the font has bad data, we don't even
608 // try to search the rest of it.
609 auto numFeatures = CFArrayGetCount(aFeatures);
610 for (auto f = 0; f < numFeatures; ++f) {
611 auto featureDict = (CFDictionaryRef)CFArrayGetValueAtIndex(aFeatures, f);
612 if (!featureDict) {
613 return false;
615 auto featureNum = (CFNumberRef)CFDictionaryGetValue(
616 featureDict, CFSTR("CTFeatureTypeIdentifier"));
617 if (!featureNum) {
618 return false;
620 int16_t featureType;
621 if (!CFNumberGetValue(featureNum, kCFNumberSInt16Type, &featureType)) {
622 return false;
624 if (featureType == kLetterCaseType || featureType == kLowerCaseType) {
625 // Which selector to look for, depending whether we've found the
626 // legacy LetterCase feature or the new LowerCase one.
627 const uint16_t smallCaps = (featureType == kLetterCaseType)
628 ? kSmallCapsSelector
629 : kLowerCaseSmallCapsSelector;
630 auto selectors = (CFArrayRef)CFDictionaryGetValue(
631 featureDict, CFSTR("CTFeatureTypeSelectors"));
632 if (!selectors) {
633 return false;
635 auto numSelectors = CFArrayGetCount(selectors);
636 for (auto s = 0; s < numSelectors; s++) {
637 auto selectorDict =
638 (CFDictionaryRef)CFArrayGetValueAtIndex(selectors, s);
639 if (!selectorDict) {
640 return false;
642 auto selectorNum = (CFNumberRef)CFDictionaryGetValue(
643 selectorDict, CFSTR("CTFeatureSelectorIdentifier"));
644 if (!selectorNum) {
645 return false;
647 int16_t selectorValue;
648 if (!CFNumberGetValue(selectorNum, kCFNumberSInt16Type,
649 &selectorValue)) {
650 return false;
652 if (selectorValue == smallCaps) {
653 return true;
658 return false;
661 bool CTFontEntry::SupportsOpenTypeFeature(Script aScript,
662 uint32_t aFeatureTag) {
663 // If we're going to shape with Core Text, we don't support added
664 // OpenType features (aside from any CT applies by default), except
665 // for 'smcp' which we map to an AAT feature selector.
666 if (RequiresAATLayout()) {
667 if (aFeatureTag != HB_TAG('s', 'm', 'c', 'p')) {
668 return false;
670 if (mHasAATSmallCapsInitialized) {
671 return mHasAATSmallCaps;
673 mHasAATSmallCapsInitialized = true;
674 CGFontRef cgFont = GetFontRef();
675 if (!cgFont) {
676 return mHasAATSmallCaps;
679 CrashReporter::AutoRecordAnnotation autoFontName(
680 CrashReporter::Annotation::FontName, FamilyName());
682 AutoCFRelease<CTFontRef> ctFont =
683 CTFontCreateWithGraphicsFont(cgFont, 0.0, nullptr, nullptr);
684 if (ctFont) {
685 AutoCFRelease<CFArrayRef> features = CTFontCopyFeatures(ctFont);
686 if (features) {
687 mHasAATSmallCaps = CheckForAATSmallCaps(features);
690 return mHasAATSmallCaps;
692 return gfxFontEntry::SupportsOpenTypeFeature(aScript, aFeatureTag);
695 void CTFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
696 FontListSizes* aSizes) const {
697 aSizes->mFontListSize += aMallocSizeOf(this);
698 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
701 static CTFontDescriptorRef CreateDescriptorForFamily(
702 const nsACString& aFamilyName, bool aNormalized) {
703 AutoCFRelease<CFStringRef> family = CreateCFStringForString(aFamilyName);
704 const void* values[] = {family};
705 const void* keys[] = {kCTFontFamilyNameAttribute};
706 AutoCFRelease<CFDictionaryRef> attributes = CFDictionaryCreate(
707 kCFAllocatorDefault, keys, values, 1, &kCFTypeDictionaryKeyCallBacks,
708 &kCFTypeDictionaryValueCallBacks);
710 // Not AutoCFRelease, because we might return it.
711 CTFontDescriptorRef descriptor =
712 CTFontDescriptorCreateWithAttributes(attributes);
714 if (aNormalized) {
715 CTFontDescriptorRef normalized =
716 CTFontDescriptorCreateMatchingFontDescriptor(descriptor, nullptr);
717 if (normalized) {
718 CFRelease(descriptor);
719 return normalized;
723 return descriptor;
726 void CTFontFamily::LocalizedName(nsACString& aLocalizedName) {
727 AutoCFRelease<CTFontDescriptorRef> descriptor =
728 CreateDescriptorForFamily(mName, true);
729 if (descriptor) {
730 AutoCFRelease<CFStringRef> name =
731 static_cast<CFStringRef>(CTFontDescriptorCopyLocalizedAttribute(
732 descriptor, kCTFontFamilyNameAttribute, nullptr));
733 if (name) {
734 nsAutoString localized;
735 GetStringForCFString(name, localized);
736 if (!localized.IsEmpty()) {
737 CopyUTF16toUTF8(localized, aLocalizedName);
738 return;
743 // failed to get localized name, just use the canonical one
744 aLocalizedName = mName;
747 // Return the CSS weight value to use for the given face, overriding what
748 // AppKit gives us (used to adjust families with bad weight values, see
749 // bug 931426).
750 // A return value of 0 indicates no override - use the existing weight.
751 static inline int GetWeightOverride(const nsAString& aPSName) {
752 nsAutoCString prefName("font.weight-override.");
753 // The PostScript name is required to be ASCII; if it's not, the font is
754 // broken anyway, so we really don't care that this is lossy.
755 LossyAppendUTF16toASCII(aPSName, prefName);
756 return Preferences::GetInt(prefName.get(), 0);
759 // The Core Text weight trait is documented as
761 // ...a float value between -1.0 and 1.0 for normalized weight.
762 // The value of 0.0 corresponds to the regular or medium font weight.
764 // (https://developer.apple.com/documentation/coretext/kctfontweighttrait)
766 // CSS 'normal' font-weight is defined as 400, so we map 0.0 to this.
767 // The exact mapping to use for other values is not well defined; the table
768 // here is empirically determined by looking at what Core Text returns for
769 // the various system fonts that have a range of weights.
770 static inline int32_t CoreTextWeightToCSSWeight(CGFloat aCTWeight) {
771 using Mapping = std::pair<CGFloat, int32_t>;
772 constexpr Mapping kCoreTextToCSSWeights[] = {
773 // clang-format off
774 {-1.0, 1},
775 {-0.8, 100},
776 {-0.6, 200},
777 {-0.4, 300},
778 {0.0, 400}, // standard 'regular' weight
779 {0.23, 500},
780 {0.3, 600},
781 {0.4, 700}, // standard 'bold' weight
782 {0.56, 800},
783 {0.62, 900}, // Core Text seems to return 0.62 for faces with both
784 // usWeightClass=800 and 900 in their OS/2 tables!
785 // We use 900 as there are also fonts that return 0.56,
786 // so we want an intermediate value for that.
787 {1.0, 1000},
788 // clang-format on
790 const auto* begin = &kCoreTextToCSSWeights[0];
791 const auto* end = begin + ArrayLength(kCoreTextToCSSWeights);
792 auto m = std::upper_bound(begin, end, aCTWeight,
793 [](CGFloat aValue, const Mapping& aMapping) {
794 return aValue <= aMapping.first;
796 if (m == end) {
797 NS_WARNING("Core Text weight out of range");
798 return 1000;
800 if (m->first == aCTWeight || m == begin) {
801 return m->second;
803 // Interpolate between the preceding and found entries:
804 const auto* prev = m - 1;
805 const auto t = (aCTWeight - prev->first) / (m->first - prev->first);
806 return NS_round(prev->second * (1.0 - t) + m->second * t);
809 // The Core Text width trait is documented as
811 // ...a float between -1.0 and 1.0. The value of 0.0 corresponds to regular
812 // glyph spacing, and negative values represent condensed glyph spacing
814 // (https://developer.apple.com/documentation/coretext/kctfontweighttrait)
816 // CSS 'normal' font-stretch is 100%; 'ultra-expanded' is 200%, and 'ultra-
817 // condensed' is 50%. We map the extremes of the Core Text trait to these
818 // values, and interpolate in between these and normal.
819 static inline FontStretch CoreTextWidthToCSSStretch(CGFloat aCTWidth) {
820 if (aCTWidth >= 0.0) {
821 return FontStretch::FromFloat(100.0 + aCTWidth * 100.0);
823 return FontStretch::FromFloat(100.0 + aCTWidth * 50.0);
826 void CTFontFamily::AddFace(CTFontDescriptorRef aFace) {
827 AutoCFRelease<CFStringRef> psname =
828 (CFStringRef)CTFontDescriptorCopyAttribute(aFace, kCTFontNameAttribute);
829 AutoCFRelease<CFStringRef> facename =
830 (CFStringRef)CTFontDescriptorCopyAttribute(aFace,
831 kCTFontStyleNameAttribute);
833 AutoCFRelease<CFDictionaryRef> traitsDict =
834 (CFDictionaryRef)CTFontDescriptorCopyAttribute(aFace,
835 kCTFontTraitsAttribute);
836 CFNumberRef weight =
837 (CFNumberRef)CFDictionaryGetValue(traitsDict, kCTFontWeightTrait);
838 CFNumberRef width =
839 (CFNumberRef)CFDictionaryGetValue(traitsDict, kCTFontWidthTrait);
840 CFNumberRef symbolicTraits =
841 (CFNumberRef)CFDictionaryGetValue(traitsDict, kCTFontSymbolicTrait);
843 bool isStandardFace = false;
845 // make a nsString
846 nsAutoString postscriptFontName;
847 GetStringForCFString(psname, postscriptFontName);
849 int32_t cssWeight = GetWeightOverride(postscriptFontName);
850 if (cssWeight) {
851 // scale down and clamp, to get a value from 1..9
852 cssWeight = ((cssWeight + 50) / 100);
853 cssWeight = std::max(1, std::min(cssWeight, 9));
854 cssWeight *= 100; // scale up to CSS values
855 } else {
856 CGFloat weightValue;
857 CFNumberGetValue(weight, kCFNumberCGFloatType, &weightValue);
858 cssWeight = CoreTextWeightToCSSWeight(weightValue);
861 if (kCFCompareEqualTo == CFStringCompare(facename, CFSTR("Regular"), 0) ||
862 kCFCompareEqualTo == CFStringCompare(facename, CFSTR("Bold"), 0) ||
863 kCFCompareEqualTo == CFStringCompare(facename, CFSTR("Italic"), 0) ||
864 kCFCompareEqualTo == CFStringCompare(facename, CFSTR("Oblique"), 0) ||
865 kCFCompareEqualTo == CFStringCompare(facename, CFSTR("Bold Italic"), 0) ||
866 kCFCompareEqualTo ==
867 CFStringCompare(facename, CFSTR("Bold Oblique"), 0)) {
868 isStandardFace = true;
871 // create a font entry
872 CTFontEntry* fontEntry = new CTFontEntry(
873 NS_ConvertUTF16toUTF8(postscriptFontName),
874 WeightRange(FontWeight::FromInt(cssWeight)), isStandardFace);
876 CGFloat widthValue;
877 CFNumberGetValue(width, kCFNumberCGFloatType, &widthValue);
878 fontEntry->mStretchRange =
879 StretchRange(CoreTextWidthToCSSStretch(widthValue));
881 SInt32 traitsValue;
882 CFNumberGetValue(symbolicTraits, kCFNumberSInt32Type, &traitsValue);
883 if (traitsValue & kCTFontItalicTrait) {
884 fontEntry->mStyleRange = SlantStyleRange(FontSlantStyle::ITALIC);
887 if (traitsValue & kCTFontMonoSpaceTrait) {
888 fontEntry->mFixedPitch = true;
891 if (gfxPlatform::HasVariationFontSupport()) {
892 fontEntry->SetupVariationRanges();
895 if (LOG_FONTLIST_ENABLED()) {
896 nsAutoCString weightString;
897 fontEntry->Weight().ToString(weightString);
898 nsAutoCString stretchString;
899 fontEntry->Stretch().ToString(stretchString);
900 LOG_FONTLIST(
901 ("(fontlist) added (%s) to family (%s)"
902 " with style: %s weight: %s stretch: %s",
903 fontEntry->Name().get(), Name().get(),
904 fontEntry->IsItalic() ? "italic" : "normal", weightString.get(),
905 stretchString.get()));
908 // insert into font entry array of family
909 AddFontEntryLocked(fontEntry);
912 void CTFontFamily::FindStyleVariationsLocked(FontInfoData* aFontInfoData) {
913 if (mHasStyles) {
914 return;
917 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("CTFontFamily::FindStyleVariations",
918 LAYOUT, mName);
920 if (mForSystemFont) {
921 MOZ_ASSERT(gfxPlatform::HasVariationFontSupport());
923 auto addToFamily = [&](CTFontRef aFont) MOZ_REQUIRES(mLock) {
924 AutoCFRelease<CFStringRef> psName = CTFontCopyPostScriptName(aFont);
925 nsAutoString nameUTF16;
926 nsAutoCString nameUTF8;
927 GetStringForCFString(psName, nameUTF16);
928 CopyUTF16toUTF8(nameUTF16, nameUTF8);
930 auto* fe =
931 new CTFontEntry(nameUTF8, WeightRange(FontWeight::NORMAL), true, 0.0);
933 // Set the appropriate style, assuming it may not have a variation range.
934 CTFontSymbolicTraits traits = CTFontGetSymbolicTraits(aFont);
935 fe->mStyleRange = SlantStyleRange((traits & kCTFontTraitItalic)
936 ? FontSlantStyle::ITALIC
937 : FontSlantStyle::NORMAL);
939 // Set up weight (and width, if present) ranges.
940 fe->SetupVariationRanges();
941 AddFontEntryLocked(fe);
944 addToFamily(mForSystemFont);
946 // See if there is a corresponding italic face, and add it to the family.
947 AutoCFRelease<CTFontRef> italicFont = CTFontCreateCopyWithSymbolicTraits(
948 mForSystemFont, 0.0, nullptr, kCTFontTraitItalic, kCTFontTraitItalic);
949 if (italicFont != mForSystemFont) {
950 addToFamily(italicFont);
953 CFRelease(mForSystemFont);
954 mForSystemFont = nullptr;
956 SetHasStyles(true);
958 return;
961 struct Context {
962 CTFontFamily* family;
963 const void* prevValue = nullptr;
966 auto addFaceFunc = [](const void* aValue, void* aContext) -> void {
967 Context* context = (Context*)aContext;
968 if (aValue == context->prevValue) {
969 return;
971 context->prevValue = aValue;
972 CTFontFamily* family = context->family;
973 // Calling family->AddFace requires that family->mLock is held. We know
974 // this will be true because FindStyleVariationsLocked already requires it,
975 // but the thread-safety analysis can't track that through into the lambda
976 // here, so we disable the check to avoid a spurious warning.
977 MOZ_PUSH_IGNORE_THREAD_SAFETY;
978 family->AddFace((CTFontDescriptorRef)aValue);
979 MOZ_POP_THREAD_SAFETY;
982 AutoCFRelease<CTFontDescriptorRef> descriptor =
983 CreateDescriptorForFamily(mName, false);
984 AutoCFRelease<CFArrayRef> faces =
985 CTFontDescriptorCreateMatchingFontDescriptors(descriptor, nullptr);
987 if (faces) {
988 Context context{this};
989 CFArrayApplyFunction(faces, CFRangeMake(0, CFArrayGetCount(faces)),
990 addFaceFunc, &context);
993 SortAvailableFonts();
994 SetHasStyles(true);
996 if (mIsBadUnderlineFamily) {
997 SetBadUnderlineFonts();
1000 CheckForSimpleFamily();
1003 /* CoreTextFontList */
1004 #pragma mark -
1006 CoreTextFontList::CoreTextFontList()
1007 : gfxPlatformFontList(false), mDefaultFont(nullptr) {
1008 #ifdef MOZ_BUNDLED_FONTS
1009 // We activate bundled fonts if the pref is > 0 (on) or < 0 (auto), only an
1010 // explicit value of 0 (off) will disable them.
1011 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1012 TimeStamp start = TimeStamp::Now();
1013 ActivateBundledFonts();
1014 TimeStamp end = TimeStamp::Now();
1015 Telemetry::Accumulate(Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
1016 (end - start).ToMilliseconds());
1018 #endif
1020 // Load the font-list preferences now, so that we don't have to do it from
1021 // Init[Shared]FontListForPlatform, which may be called off-main-thread.
1022 gfxFontUtils::GetPrefsFontList("font.preload-names-list", mPreloadFonts);
1025 CoreTextFontList::~CoreTextFontList() {
1026 AutoLock lock(mLock);
1028 if (XRE_IsParentProcess()) {
1029 CFNotificationCenterRemoveObserver(
1030 CFNotificationCenterGetLocalCenter(), this,
1031 kCTFontManagerRegisteredFontsChangedNotification, 0);
1034 if (mDefaultFont) {
1035 CFRelease(mDefaultFont);
1039 void CoreTextFontList::AddFamily(const nsACString& aFamilyName,
1040 FontVisibility aVisibility) {
1041 nsAutoCString key;
1042 ToLowerCase(aFamilyName, key);
1044 RefPtr<gfxFontFamily> familyEntry =
1045 new CTFontFamily(aFamilyName, aVisibility);
1046 mFontFamilies.InsertOrUpdate(key, RefPtr{familyEntry});
1048 // check the bad underline blocklist
1049 if (mBadUnderlineFamilyNames.ContainsSorted(key)) {
1050 familyEntry->SetBadUnderlineFamily();
1054 void CoreTextFontList::AddFamily(CFStringRef aFamily) {
1055 // CTFontManager includes internal family names and LastResort; skip those.
1056 if (!aFamily ||
1057 CFStringCompare(aFamily, CFSTR("LastResort"),
1058 kCFCompareCaseInsensitive) == kCFCompareEqualTo ||
1059 CFStringCompare(aFamily, CFSTR(".LastResort"),
1060 kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
1061 return;
1064 nsAutoString familyName;
1065 GetStringForCFString(aFamily, familyName);
1067 NS_ConvertUTF16toUTF8 nameUtf8(familyName);
1068 AddFamily(nameUtf8, GetVisibilityForFamily(nameUtf8));
1071 /* static */
1072 void CoreTextFontList::ActivateFontsFromDir(
1073 const nsACString& aDir, nsTHashSet<nsCStringHashKey>* aLoadedFamilies) {
1074 AutoCFRelease<CFURLRef> directory = CFURLCreateFromFileSystemRepresentation(
1075 kCFAllocatorDefault, (const UInt8*)nsPromiseFlatCString(aDir).get(),
1076 aDir.Length(), true);
1077 if (!directory) {
1078 return;
1080 AutoCFRelease<CFURLEnumeratorRef> enumerator =
1081 CFURLEnumeratorCreateForDirectoryURL(kCFAllocatorDefault, directory,
1082 kCFURLEnumeratorDefaultBehavior,
1083 nullptr);
1084 if (!enumerator) {
1085 return;
1087 AutoCFRelease<CFMutableArrayRef> urls =
1088 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
1089 if (!urls) {
1090 return;
1093 CFURLRef url;
1094 CFURLEnumeratorResult result;
1095 do {
1096 result = CFURLEnumeratorGetNextURL(enumerator, &url, nullptr);
1097 if (result != kCFURLEnumeratorSuccess) {
1098 continue;
1100 CFArrayAppendValue(urls, url);
1102 if (!aLoadedFamilies) {
1103 continue;
1105 AutoCFRelease<CFArrayRef> descriptors =
1106 CTFontManagerCreateFontDescriptorsFromURL(url);
1107 if (!descriptors || !CFArrayGetCount(descriptors)) {
1108 continue;
1110 CTFontDescriptorRef desc =
1111 (CTFontDescriptorRef)CFArrayGetValueAtIndex(descriptors, 0);
1112 AutoCFRelease<CFStringRef> name =
1113 (CFStringRef)CTFontDescriptorCopyAttribute(desc,
1114 kCTFontFamilyNameAttribute);
1115 nsAutoCString key;
1116 key.SetLength((CFStringGetLength(name) + 1) * 3);
1117 if (CFStringGetCString(name, key.BeginWriting(), key.Length(),
1118 kCFStringEncodingUTF8)) {
1119 key.SetLength(strlen(key.get()));
1120 aLoadedFamilies->Insert(key);
1122 } while (result != kCFURLEnumeratorEnd);
1124 CTFontManagerRegisterFontURLs(urls, kCTFontManagerScopeProcess, false,
1125 nullptr);
1128 void CoreTextFontList::ReadSystemFontList(dom::SystemFontList* aList)
1129 MOZ_NO_THREAD_SAFETY_ANALYSIS {
1130 // Note: We rely on the records for mSystemFontFamilyName (if present) being
1131 // *before* the main font list, so that name is known in the content process
1132 // by the time we add the actual family records to the font list.
1133 aList->entries().AppendElement(FontFamilyListEntry(
1134 mSystemFontFamilyName, FontVisibility::Unknown, kSystemFontFamily));
1136 // Now collect the list of available families, with visibility attributes.
1137 for (auto f = mFontFamilies.Iter(); !f.Done(); f.Next()) {
1138 auto macFamily = f.Data().get();
1139 aList->entries().AppendElement(FontFamilyListEntry(
1140 macFamily->Name(), macFamily->Visibility(), kStandardFontFamily));
1144 void CoreTextFontList::PreloadNamesList() {
1145 uint32_t numFonts = mPreloadFonts.Length();
1146 for (uint32_t i = 0; i < numFonts; i++) {
1147 nsAutoCString key;
1148 GenerateFontListKey(mPreloadFonts[i], key);
1150 // only search canonical names!
1151 gfxFontFamily* familyEntry = mFontFamilies.GetWeak(key);
1152 if (familyEntry) {
1153 familyEntry->ReadOtherFamilyNames(this);
1158 nsresult CoreTextFontList::InitFontListForPlatform() {
1159 // The font registration thread was created early in startup, to give the
1160 // system a head start on activating all the supplemental-language fonts.
1161 // Here, we need to wait until it has finished its work.
1162 gfxPlatformMac::WaitForFontRegistration();
1164 Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer;
1166 InitSystemFontNames();
1168 if (XRE_IsParentProcess()) {
1169 static bool firstTime = true;
1170 if (firstTime) {
1171 CFNotificationCenterAddObserver(
1172 CFNotificationCenterGetLocalCenter(), this,
1173 RegisteredFontsChangedNotificationCallback,
1174 kCTFontManagerRegisteredFontsChangedNotification, 0,
1175 CFNotificationSuspensionBehaviorDeliverImmediately);
1176 firstTime = false;
1179 // We're not a content process, so get the available fonts directly
1180 // from Core Text.
1181 AutoCFRelease<CFArrayRef> familyNames =
1182 CTFontManagerCopyAvailableFontFamilyNames();
1183 for (CFIndex i = 0; i < CFArrayGetCount(familyNames); i++) {
1184 CFStringRef familyName =
1185 (CFStringRef)CFArrayGetValueAtIndex(familyNames, i);
1186 AddFamily(familyName);
1188 #if USE_DEPRECATED_FONT_FAMILY_NAMES
1189 for (const auto& name : kDeprecatedFontFamilies) {
1190 if (DeprecatedFamilyIsAvailable(name)) {
1191 AddFamily(name, GetVisibilityForFamily(name));
1194 #endif
1195 } else {
1196 // Content process: use font list passed from the chrome process via
1197 // the GetXPCOMProcessAttributes message, because it's much faster than
1198 // querying Core Text again in the child.
1199 auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
1200 for (FontFamilyListEntry& ffe : fontList.entries()) {
1201 switch (ffe.entryType()) {
1202 case kStandardFontFamily:
1203 if (ffe.familyName() == mSystemFontFamilyName) {
1204 continue;
1206 AddFamily(ffe.familyName(), ffe.visibility());
1207 break;
1208 case kSystemFontFamily:
1209 mSystemFontFamilyName = ffe.familyName();
1210 break;
1213 fontList.entries().Clear();
1216 InitSingleFaceList();
1218 // to avoid full search of font name tables, seed the other names table with
1219 // localized names from some of the prefs fonts which are accessed via their
1220 // localized names. changes in the pref fonts will only cause a font lookup
1221 // miss earlier. this is a simple optimization, it's not required for
1222 // correctness
1223 PreloadNamesList();
1225 // start the delayed cmap loader
1226 GetPrefsAndStartLoader();
1228 return NS_OK;
1231 void CoreTextFontList::InitSharedFontListForPlatform() {
1232 gfxPlatformMac::WaitForFontRegistration();
1234 InitSystemFontNames();
1236 if (XRE_IsParentProcess()) {
1237 // Only the parent process listens for OS font-changed notifications;
1238 // after rebuilding its list, it will update the content processes.
1239 static bool firstTime = true;
1240 if (firstTime) {
1241 CFNotificationCenterAddObserver(
1242 CFNotificationCenterGetLocalCenter(), this,
1243 RegisteredFontsChangedNotificationCallback,
1244 kCTFontManagerRegisteredFontsChangedNotification, 0,
1245 CFNotificationSuspensionBehaviorDeliverImmediately);
1246 firstTime = false;
1249 AutoCFRelease<CFArrayRef> familyNames =
1250 CTFontManagerCopyAvailableFontFamilyNames();
1251 nsTArray<fontlist::Family::InitData> families;
1252 families.SetCapacity(CFArrayGetCount(familyNames)
1253 #if USE_DEPRECATED_FONT_FAMILY_NAMES
1254 + ArrayLength(kDeprecatedFontFamilies)
1255 #endif
1257 for (CFIndex i = 0; i < CFArrayGetCount(familyNames); ++i) {
1258 nsAutoString name16;
1259 CFStringRef familyName =
1260 (CFStringRef)CFArrayGetValueAtIndex(familyNames, i);
1261 GetStringForCFString(familyName, name16);
1262 NS_ConvertUTF16toUTF8 name(name16);
1263 nsAutoCString key;
1264 GenerateFontListKey(name, key);
1265 families.AppendElement(fontlist::Family::InitData(
1266 key, name, fontlist::Family::kNoIndex, GetVisibilityForFamily(name)));
1268 #if USE_DEPRECATED_FONT_FAMILY_NAMES
1269 for (const nsACString& name : kDeprecatedFontFamilies) {
1270 if (DeprecatedFamilyIsAvailable(name)) {
1271 nsAutoCString key;
1272 GenerateFontListKey(name, key);
1273 families.AppendElement(
1274 fontlist::Family::InitData(key, name, fontlist::Family::kNoIndex,
1275 GetVisibilityForFamily(name)));
1278 #endif
1279 SharedFontList()->SetFamilyNames(families);
1280 InitAliasesForSingleFaceList();
1281 GetPrefsAndStartLoader();
1285 gfxFontFamily* CoreTextFontList::FindSystemFontFamily(
1286 const nsACString& aFamily) {
1287 nsAutoCString key;
1288 GenerateFontListKey(aFamily, key);
1290 gfxFontFamily* familyEntry;
1291 if ((familyEntry = mFontFamilies.GetWeak(key))) {
1292 return CheckFamily(familyEntry);
1295 return nullptr;
1298 void CoreTextFontList::RegisteredFontsChangedNotificationCallback(
1299 CFNotificationCenterRef center, void* observer, CFStringRef name,
1300 const void* object, CFDictionaryRef userInfo) {
1301 if (!CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) {
1302 return;
1305 CoreTextFontList* fl = static_cast<CoreTextFontList*>(observer);
1306 if (!fl->IsInitialized()) {
1307 return;
1310 // xxx - should be carefully pruning the list of fonts, not rebuilding it from
1311 // scratch
1312 fl->UpdateFontList();
1314 gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::Yes);
1315 dom::ContentParent::NotifyUpdatedFonts(true);
1318 gfxFontEntry* CoreTextFontList::PlatformGlobalFontFallback(
1319 nsPresContext* aPresContext, const uint32_t aCh, Script aRunScript,
1320 const gfxFontStyle* aMatchStyle, FontFamily& aMatchedFamily) {
1321 CFStringRef str;
1322 UniChar ch[2];
1323 CFIndex length = 1;
1325 if (IS_IN_BMP(aCh)) {
1326 ch[0] = aCh;
1327 str = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1,
1328 kCFAllocatorNull);
1329 } else {
1330 ch[0] = H_SURROGATE(aCh);
1331 ch[1] = L_SURROGATE(aCh);
1332 str = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2,
1333 kCFAllocatorNull);
1334 length = 2;
1336 if (!str) {
1337 return nullptr;
1340 // use CoreText to find the fallback family
1342 gfxFontEntry* fontEntry = nullptr;
1343 bool cantUseFallbackFont = false;
1345 if (!mDefaultFont) {
1346 mDefaultFont = CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f, NULL);
1349 AutoCFRelease<CTFontRef> fallback =
1350 CTFontCreateForString(mDefaultFont, str, CFRangeMake(0, length));
1352 if (fallback) {
1353 AutoCFRelease<CFStringRef> familyNameRef = CTFontCopyFamilyName(fallback);
1355 if (familyNameRef &&
1356 CFStringCompare(familyNameRef, CFSTR("LastResort"),
1357 kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
1358 CFStringCompare(familyNameRef, CFSTR(".LastResort"),
1359 kCFCompareCaseInsensitive) != kCFCompareEqualTo) {
1360 AutoTArray<UniChar, 1024> buffer;
1361 CFIndex familyNameLen = CFStringGetLength(familyNameRef);
1362 buffer.SetLength(familyNameLen + 1);
1363 CFStringGetCharacters(familyNameRef, CFRangeMake(0, familyNameLen),
1364 buffer.Elements());
1365 buffer[familyNameLen] = 0;
1366 NS_ConvertUTF16toUTF8 familyNameString(
1367 reinterpret_cast<char16_t*>(buffer.Elements()), familyNameLen);
1369 if (SharedFontList()) {
1370 fontlist::Family* family =
1371 FindSharedFamily(aPresContext, familyNameString);
1372 if (family) {
1373 fontlist::Face* face =
1374 family->FindFaceForStyle(SharedFontList(), *aMatchStyle);
1375 if (face) {
1376 fontEntry = GetOrCreateFontEntryLocked(face, family);
1378 if (fontEntry) {
1379 if (fontEntry->HasCharacter(aCh)) {
1380 aMatchedFamily = FontFamily(family);
1381 } else {
1382 fontEntry = nullptr;
1383 cantUseFallbackFont = true;
1389 // The macOS system font does not appear in the shared font list, so if
1390 // we didn't find the fallback font above, we should also check for an
1391 // unshared fontFamily in the system list.
1392 if (!fontEntry) {
1393 gfxFontFamily* family = FindSystemFontFamily(familyNameString);
1394 if (family) {
1395 fontEntry = family->FindFontForStyle(*aMatchStyle);
1396 if (fontEntry) {
1397 if (fontEntry->HasCharacter(aCh)) {
1398 aMatchedFamily = FontFamily(family);
1399 } else {
1400 fontEntry = nullptr;
1401 cantUseFallbackFont = true;
1409 if (cantUseFallbackFont) {
1410 Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont);
1413 CFRelease(str);
1415 return fontEntry;
1418 gfxFontEntry* CoreTextFontList::LookupLocalFont(
1419 nsPresContext* aPresContext, const nsACString& aFontName,
1420 WeightRange aWeightForEntry, StretchRange aStretchForEntry,
1421 SlantStyleRange aStyleForEntry) {
1422 if (aFontName.IsEmpty() || aFontName[0] == '.') {
1423 return nullptr;
1426 AutoLock lock(mLock);
1428 CrashReporter::AutoRecordAnnotation autoFontName(
1429 CrashReporter::Annotation::FontName, aFontName);
1431 AutoCFRelease<CFStringRef> faceName = CreateCFStringForString(aFontName);
1432 if (!faceName) {
1433 return nullptr;
1436 // lookup face based on postscript or full name
1437 AutoCFRelease<CGFontRef> fontRef = CGFontCreateWithFontName(faceName);
1438 if (!fontRef) {
1439 return nullptr;
1442 // It's possible for CGFontCreateWithFontName to return a font that has been
1443 // deactivated/uninstalled, or a font that is excluded from the font list due
1444 // to CSS font-visibility restriction. So we need to check whether this font
1445 // is allowed to be used.
1447 // CGFontRef doesn't offer a family-name API, so we go via a CTFontRef.
1448 AutoCFRelease<CTFontRef> ctFont =
1449 CTFontCreateWithGraphicsFont(fontRef, 0.0, nullptr, nullptr);
1450 if (!ctFont) {
1451 return nullptr;
1453 AutoCFRelease<CFStringRef> name = CTFontCopyFamilyName(ctFont);
1455 // Convert the family name to a key suitable for font-list lookup (8-bit,
1456 // lowercased).
1457 nsAutoCString key;
1458 // CFStringGetLength is in UTF-16 code units. The maximum this count can
1459 // expand when converted to UTF-8 is 3x. We add 1 to ensure there will also be
1460 // space for null-termination of the resulting C string.
1461 key.SetLength((CFStringGetLength(name) + 1) * 3);
1462 if (!CFStringGetCString(name, key.BeginWriting(), key.Length(),
1463 kCFStringEncodingUTF8)) {
1464 // This shouldn't ever happen, but if it does we just bail.
1465 NS_WARNING("Failed to get family name?");
1466 key.Truncate(0);
1468 if (key.IsEmpty()) {
1469 return nullptr;
1471 // Reset our string length to match the actual C string we got, which will
1472 // usually be much shorter than the maximal buffer we allocated.
1473 key.Truncate(strlen(key.get()));
1474 ToLowerCase(key);
1475 // If the family can't be looked up, this font is not available for use.
1476 FontFamily family = FindFamily(aPresContext, key);
1477 if (family.IsNull()) {
1478 return nullptr;
1481 return new CTFontEntry(aFontName, fontRef, aWeightForEntry, aStretchForEntry,
1482 aStyleForEntry, false, true);
1485 static void ReleaseData(void* info, const void* data, size_t size) {
1486 free((void*)data);
1489 gfxFontEntry* CoreTextFontList::MakePlatformFont(const nsACString& aFontName,
1490 WeightRange aWeightForEntry,
1491 StretchRange aStretchForEntry,
1492 SlantStyleRange aStyleForEntry,
1493 const uint8_t* aFontData,
1494 uint32_t aLength) {
1495 NS_ASSERTION(aFontData, "MakePlatformFont called with null data");
1497 // create the font entry
1498 nsAutoString uniqueName;
1500 nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
1501 if (NS_FAILED(rv)) {
1502 return nullptr;
1505 CrashReporter::AutoRecordAnnotation autoFontName(
1506 CrashReporter::Annotation::FontName, aFontName);
1508 AutoCFRelease<CGDataProviderRef> provider =
1509 ::CGDataProviderCreateWithData(nullptr, aFontData, aLength, &ReleaseData);
1510 AutoCFRelease<CGFontRef> fontRef = ::CGFontCreateWithDataProvider(provider);
1511 if (!fontRef) {
1512 return nullptr;
1515 auto newFontEntry = MakeUnique<CTFontEntry>(
1516 NS_ConvertUTF16toUTF8(uniqueName), fontRef, aWeightForEntry,
1517 aStretchForEntry, aStyleForEntry, true, false);
1518 return newFontEntry.release();
1521 // Webkit code uses a system font meta name, so mimic that here
1522 // WebCore/platform/graphics/mac/FontCacheMac.mm
1523 static const char kSystemFont_system[] = "-apple-system";
1525 bool CoreTextFontList::FindAndAddFamiliesLocked(
1526 nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
1527 const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
1528 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
1529 gfxFloat aDevToCssSize) {
1530 if (aFamily.EqualsLiteral(kSystemFont_system)) {
1531 // Search for special system font name, -apple-system. This is not done via
1532 // the shared fontlist because the hidden system font may not be included
1533 // there; we create a separate gfxFontFamily to manage this family.
1534 if (auto* fam = FindSystemFontFamily(mSystemFontFamilyName)) {
1535 aOutput->AppendElement(fam);
1536 return true;
1538 return false;
1541 return gfxPlatformFontList::FindAndAddFamiliesLocked(
1542 aPresContext, aGeneric, aFamily, aOutput, aFlags, aStyle, aLanguage,
1543 aDevToCssSize);
1546 // used to load system-wide font info on off-main thread
1547 class CTFontInfo final : public FontInfoData {
1548 public:
1549 CTFontInfo(bool aLoadOtherNames, bool aLoadFaceNames, bool aLoadCmaps,
1550 RecursiveMutex& aLock)
1551 : FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps),
1552 mLock(aLock) {}
1554 virtual ~CTFontInfo() = default;
1556 virtual void Load() { FontInfoData::Load(); }
1558 // loads font data for all members of a given family
1559 virtual void LoadFontFamilyData(const nsACString& aFamilyName);
1561 RecursiveMutex& mLock;
1564 void CTFontInfo::LoadFontFamilyData(const nsACString& aFamilyName) {
1565 CrashReporter::AutoRecordAnnotation autoFontName(
1566 CrashReporter::Annotation::FontName, aFamilyName);
1567 // Prevent this from running concurrently with CGFont operations on the main
1568 // thread, because the macOS font cache is fragile with concurrent access.
1569 // This appears to be a vulnerability within CoreText in versions of macOS
1570 // before macOS 13. In time, we can remove this lock.
1571 RecursiveMutexAutoLock lock(mLock);
1573 // family name ==> CTFontDescriptor
1574 AutoCFRelease<CFStringRef> family = CreateCFStringForString(aFamilyName);
1576 AutoCFRelease<CFMutableDictionaryRef> attr =
1577 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
1578 &kCFTypeDictionaryValueCallBacks);
1579 CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family);
1580 AutoCFRelease<CTFontDescriptorRef> fd =
1581 CTFontDescriptorCreateWithAttributes(attr);
1582 AutoCFRelease<CFArrayRef> matchingFonts =
1583 CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL);
1584 if (!matchingFonts) {
1585 return;
1588 nsTArray<nsCString> otherFamilyNames;
1589 bool hasOtherFamilyNames = true;
1591 // iterate over faces in the family
1592 int f, numFaces = (int)CFArrayGetCount(matchingFonts);
1593 CTFontDescriptorRef prevFace = nullptr;
1594 for (f = 0; f < numFaces; f++) {
1595 mLoadStats.fonts++;
1597 CTFontDescriptorRef faceDesc =
1598 (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f);
1599 if (!faceDesc) {
1600 continue;
1603 if (faceDesc == prevFace) {
1604 continue;
1606 prevFace = faceDesc;
1608 AutoCFRelease<CTFontRef> fontRef =
1609 CTFontCreateWithFontDescriptor(faceDesc, 0.0, nullptr);
1610 if (!fontRef) {
1611 NS_WARNING("failed to create a CTFontRef");
1612 continue;
1615 if (mLoadCmaps) {
1616 // face name
1617 AutoCFRelease<CFStringRef> faceName =
1618 (CFStringRef)CTFontDescriptorCopyAttribute(faceDesc,
1619 kCTFontNameAttribute);
1621 AutoTArray<UniChar, 1024> buffer;
1622 CFIndex len = CFStringGetLength(faceName);
1623 buffer.SetLength(len + 1);
1624 CFStringGetCharacters(faceName, CFRangeMake(0, len), buffer.Elements());
1625 buffer[len] = 0;
1626 NS_ConvertUTF16toUTF8 fontName(
1627 reinterpret_cast<char16_t*>(buffer.Elements()), len);
1629 // load the cmap data
1630 FontFaceData fontData;
1631 AutoCFRelease<CFDataRef> cmapTable = CTFontCopyTable(
1632 fontRef, kCTFontTableCmap, kCTFontTableOptionNoOptions);
1634 if (cmapTable) {
1635 const uint8_t* cmapData = (const uint8_t*)CFDataGetBytePtr(cmapTable);
1636 uint32_t cmapLen = CFDataGetLength(cmapTable);
1637 RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
1638 uint32_t offset;
1639 nsresult rv;
1641 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset);
1642 if (NS_SUCCEEDED(rv)) {
1643 fontData.mCharacterMap = charmap;
1644 fontData.mUVSOffset = offset;
1645 mLoadStats.cmaps++;
1649 mFontFaceData.InsertOrUpdate(fontName, fontData);
1652 if (mLoadOtherNames && hasOtherFamilyNames) {
1653 AutoCFRelease<CFDataRef> nameTable = CTFontCopyTable(
1654 fontRef, kCTFontTableName, kCTFontTableOptionNoOptions);
1656 if (nameTable) {
1657 const char* nameData = (const char*)CFDataGetBytePtr(nameTable);
1658 uint32_t nameLen = CFDataGetLength(nameTable);
1659 gfxFontUtils::ReadOtherFamilyNamesForFace(
1660 aFamilyName, nameData, nameLen, otherFamilyNames, false);
1661 hasOtherFamilyNames = otherFamilyNames.Length() != 0;
1666 // if found other names, insert them in the hash table
1667 if (otherFamilyNames.Length() != 0) {
1668 mOtherFamilyNames.InsertOrUpdate(aFamilyName, otherFamilyNames);
1669 mLoadStats.othernames += otherFamilyNames.Length();
1673 already_AddRefed<FontInfoData> CoreTextFontList::CreateFontInfoData() {
1674 bool loadCmaps = !UsesSystemFallback() ||
1675 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1677 mLock.AssertCurrentThreadIn();
1678 RefPtr<CTFontInfo> fi =
1679 new CTFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps, mLock);
1680 return fi.forget();
1683 gfxFontFamily* CoreTextFontList::CreateFontFamily(
1684 const nsACString& aName, FontVisibility aVisibility) const {
1685 return new CTFontFamily(aName, aVisibility);
1688 gfxFontEntry* CoreTextFontList::CreateFontEntry(
1689 fontlist::Face* aFace, const fontlist::Family* aFamily) {
1690 CTFontEntry* fe = new CTFontEntry(
1691 aFace->mDescriptor.AsString(SharedFontList()), aFace->mWeight, false,
1692 0.0); // XXX standardFace, sizeHint
1693 fe->InitializeFrom(aFace, aFamily);
1694 return fe;
1697 void CoreTextFontList::AddFaceInitData(
1698 CTFontDescriptorRef aFontDesc, nsTArray<fontlist::Face::InitData>& aFaces,
1699 bool aLoadCmaps) {
1700 AutoCFRelease<CFStringRef> psname =
1701 (CFStringRef)CTFontDescriptorCopyAttribute(aFontDesc,
1702 kCTFontNameAttribute);
1703 AutoCFRelease<CFStringRef> facename =
1704 (CFStringRef)CTFontDescriptorCopyAttribute(aFontDesc,
1705 kCTFontStyleNameAttribute);
1706 AutoCFRelease<CFDictionaryRef> traitsDict =
1707 (CFDictionaryRef)CTFontDescriptorCopyAttribute(aFontDesc,
1708 kCTFontTraitsAttribute);
1710 CFNumberRef weight =
1711 (CFNumberRef)CFDictionaryGetValue(traitsDict, kCTFontWeightTrait);
1712 CFNumberRef width =
1713 (CFNumberRef)CFDictionaryGetValue(traitsDict, kCTFontWidthTrait);
1714 CFNumberRef symbolicTraits =
1715 (CFNumberRef)CFDictionaryGetValue(traitsDict, kCTFontSymbolicTrait);
1717 // make a nsString
1718 nsAutoString postscriptFontName;
1719 GetStringForCFString(psname, postscriptFontName);
1721 int32_t cssWeight = PR_GetCurrentThread() == sInitFontListThread
1723 : GetWeightOverride(postscriptFontName);
1724 if (cssWeight) {
1725 // scale down and clamp, to get a value from 1..9
1726 cssWeight = ((cssWeight + 50) / 100);
1727 cssWeight = std::max(1, std::min(cssWeight, 9));
1728 cssWeight *= 100; // scale up to CSS values
1729 } else {
1730 CGFloat weightValue;
1731 CFNumberGetValue(weight, kCFNumberCGFloatType, &weightValue);
1732 cssWeight = CoreTextWeightToCSSWeight(weightValue);
1735 CGFloat widthValue;
1736 CFNumberGetValue(width, kCFNumberCGFloatType, &widthValue);
1737 StretchRange stretch(CoreTextWidthToCSSStretch(widthValue));
1739 SlantStyleRange slantStyle(FontSlantStyle::NORMAL);
1740 SInt32 traitsValue;
1741 CFNumberGetValue(symbolicTraits, kCFNumberSInt32Type, &traitsValue);
1742 if (traitsValue & kCTFontItalicTrait) {
1743 slantStyle = SlantStyleRange(FontSlantStyle::ITALIC);
1746 bool fixedPitch = traitsValue & kCTFontMonoSpaceTrait;
1748 RefPtr<gfxCharacterMap> charmap;
1749 if (aLoadCmaps) {
1750 AutoCFRelease<CGFontRef> font =
1751 CGFontCreateWithFontName(CFStringRef(psname));
1752 if (font) {
1753 uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
1754 AutoCFRelease<CFDataRef> data = CGFontCopyTableForTag(font, kCMAP);
1755 if (data) {
1756 uint32_t offset;
1757 charmap = new gfxCharacterMap();
1758 gfxFontUtils::ReadCMAP(CFDataGetBytePtr(data), CFDataGetLength(data),
1759 *charmap, offset);
1764 // Ensure that a face named "Regular" goes to the front of the list, so it
1765 // will take precedence over other faces with the same style attributes but
1766 // a different name (such as "Outline").
1767 auto data = fontlist::Face::InitData{
1768 NS_ConvertUTF16toUTF8(postscriptFontName),
1770 fixedPitch,
1771 WeightRange(FontWeight::FromInt(cssWeight)),
1772 stretch,
1773 slantStyle,
1774 charmap,
1776 if (kCFCompareEqualTo == CFStringCompare(facename, CFSTR("Regular"), 0)) {
1777 aFaces.InsertElementAt(0, std::move(data));
1778 } else {
1779 aFaces.AppendElement(std::move(data));
1783 void CoreTextFontList::GetFacesInitDataForFamily(
1784 const fontlist::Family* aFamily, nsTArray<fontlist::Face::InitData>& aFaces,
1785 bool aLoadCmaps) const {
1786 auto name = aFamily->Key().AsString(SharedFontList());
1787 CrashReporter::AutoRecordAnnotation autoFontName(
1788 CrashReporter::Annotation::FontName, name);
1790 struct Context {
1791 nsTArray<fontlist::Face::InitData>& mFaces;
1792 bool mLoadCmaps;
1793 const void* prevValue = nullptr;
1795 auto addFaceFunc = [](const void* aValue, void* aContext) -> void {
1796 Context* context = (Context*)aContext;
1797 if (aValue == context->prevValue) {
1798 return;
1800 context->prevValue = aValue;
1801 CTFontDescriptorRef fontDesc = (CTFontDescriptorRef)aValue;
1802 CoreTextFontList::AddFaceInitData(fontDesc, context->mFaces,
1803 context->mLoadCmaps);
1806 AutoCFRelease<CTFontDescriptorRef> descriptor =
1807 CreateDescriptorForFamily(name, false);
1808 AutoCFRelease<CFArrayRef> faces =
1809 CTFontDescriptorCreateMatchingFontDescriptors(descriptor, nullptr);
1811 if (faces) {
1812 Context context{aFaces, aLoadCmaps};
1813 CFArrayApplyFunction(faces, CFRangeMake(0, CFArrayGetCount(faces)),
1814 addFaceFunc, &context);
1818 void CoreTextFontList::ReadFaceNamesForFamily(
1819 fontlist::Family* aFamily, bool aNeedFullnamePostscriptNames) {
1820 if (!aFamily->IsInitialized()) {
1821 if (!InitializeFamily(aFamily)) {
1822 return;
1825 const uint32_t kNAME = TRUETYPE_TAG('n', 'a', 'm', 'e');
1826 fontlist::FontList* list = SharedFontList();
1827 nsAutoCString canonicalName(aFamily->DisplayName().AsString(list));
1828 const auto* facePtrs = aFamily->Faces(list);
1829 for (uint32_t i = 0, n = aFamily->NumFaces(); i < n; i++) {
1830 auto* face = facePtrs[i].ToPtr<const fontlist::Face>(list);
1831 if (!face) {
1832 continue;
1834 nsAutoCString name(face->mDescriptor.AsString(list));
1835 // We create a temporary CTFontEntry just to read family names from the
1836 // 'name' table in the font resource. The style attributes here are ignored
1837 // as this entry is not used for font style matching.
1838 // The size hint might be used to select which face is accessed in the case
1839 // of the macOS UI font; see CTFontEntry::GetFontRef(). We pass 16.0 in
1840 // order to get a standard text-size face in this case, although it's
1841 // unlikely to matter for the purpose of just reading family names.
1842 auto fe = MakeUnique<CTFontEntry>(name, WeightRange(FontWeight::NORMAL),
1843 false, 16.0);
1844 if (!fe) {
1845 continue;
1847 gfxFontEntry::AutoTable nameTable(fe.get(), kNAME);
1848 if (!nameTable) {
1849 continue;
1851 uint32_t dataLength;
1852 const char* nameData = hb_blob_get_data(nameTable, &dataLength);
1853 AutoTArray<nsCString, 4> otherFamilyNames;
1854 gfxFontUtils::ReadOtherFamilyNamesForFace(
1855 canonicalName, nameData, dataLength, otherFamilyNames, false);
1856 for (const auto& alias : otherFamilyNames) {
1857 nsAutoCString key;
1858 GenerateFontListKey(alias, key);
1859 auto aliasData = mAliasTable.GetOrInsertNew(key);
1860 aliasData->InitFromFamily(aFamily, canonicalName);
1861 aliasData->mFaces.AppendElement(facePtrs[i]);
1866 static CFStringRef CopyRealFamilyName(CTFontRef aFont) {
1867 AutoCFRelease<CFStringRef> psName = CTFontCopyPostScriptName(aFont);
1868 AutoCFRelease<CGFontRef> cgFont =
1869 CGFontCreateWithFontName(CFStringRef(psName));
1870 if (!cgFont) {
1871 return CTFontCopyFamilyName(aFont);
1873 AutoCFRelease<CTFontRef> ctFont =
1874 CTFontCreateWithGraphicsFont(cgFont, 0.0, nullptr, nullptr);
1875 if (!ctFont) {
1876 return CTFontCopyFamilyName(aFont);
1878 return CTFontCopyFamilyName(ctFont);
1881 void CoreTextFontList::InitSystemFontNames() {
1882 // text font family
1883 AutoCFRelease<CTFontRef> font = CTFontCreateUIFontForLanguage(
1884 kCTFontUIFontSystem, 0.0, nullptr); // TODO: language
1885 AutoCFRelease<CFStringRef> name = CopyRealFamilyName(font);
1887 nsAutoString familyName;
1888 GetStringForCFString(name, familyName);
1889 CopyUTF16toUTF8(familyName, mSystemFontFamilyName);
1891 // We store an in-process gfxFontFamily for the system font even if using the
1892 // shared fontlist to manage "normal" fonts, because the hidden system fonts
1893 // may be excluded from the font list altogether. This family will be
1894 // populated based on the given NSFont.
1895 RefPtr<gfxFontFamily> fam = new CTFontFamily(mSystemFontFamilyName, font);
1896 if (fam) {
1897 nsAutoCString key;
1898 GenerateFontListKey(mSystemFontFamilyName, key);
1899 mFontFamilies.InsertOrUpdate(key, std::move(fam));
1903 FontFamily CoreTextFontList::GetDefaultFontForPlatform(
1904 nsPresContext* aPresContext, const gfxFontStyle* aStyle,
1905 nsAtom* aLanguage) {
1906 AutoCFRelease<CTFontRef> font = CTFontCreateUIFontForLanguage(
1907 kCTFontUIFontUser, 0.0, nullptr); // TODO: language
1908 AutoCFRelease<CFStringRef> name = CTFontCopyFamilyName(font);
1910 nsAutoString familyName;
1911 GetStringForCFString(name, familyName);
1913 return FindFamily(aPresContext, NS_ConvertUTF16toUTF8(familyName));
1916 #ifdef MOZ_BUNDLED_FONTS
1917 void CoreTextFontList::ActivateBundledFonts() {
1918 nsCOMPtr<nsIFile> localDir;
1919 if (NS_FAILED(NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir)))) {
1920 return;
1922 if (NS_FAILED(localDir->Append(u"fonts"_ns))) {
1923 return;
1925 nsAutoCString path;
1926 if (NS_FAILED(localDir->GetNativePath(path))) {
1927 return;
1929 ActivateFontsFromDir(path, &mBundledFamilies);
1931 #endif