Bug 1874684 - Part 28: Return DateDuration from DifferenceISODateTime. r=mgaudet
[gecko.git] / gfx / thebes / gfxFcPlatformFontList.cpp
blob30c202d6eea22f7766f08d242ac41fd8cb27b632
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Logging.h"
8 #include "gfxFcPlatformFontList.h"
9 #include "gfxFont.h"
10 #include "gfxFontConstants.h"
11 #include "gfxFT2Utils.h"
12 #include "gfxPlatform.h"
13 #include "nsPresContext.h"
14 #include "mozilla/ArrayUtils.h"
15 #include "mozilla/dom/ContentChild.h"
16 #include "mozilla/dom/ContentParent.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/Sprintf.h"
19 #include "mozilla/StaticPrefs_gfx.h"
20 #include "mozilla/Telemetry.h"
21 #include "mozilla/TimeStamp.h"
22 #include "nsGkAtoms.h"
23 #include "nsIConsoleService.h"
24 #include "nsIGfxInfo.h"
25 #include "mozilla/Components.h"
26 #include "nsString.h"
27 #include "nsStringFwd.h"
28 #include "nsUnicodeProperties.h"
29 #include "nsDirectoryServiceUtils.h"
30 #include "nsDirectoryServiceDefs.h"
31 #include "nsAppDirectoryServiceDefs.h"
32 #include "nsCharSeparatedTokenizer.h"
33 #include "nsXULAppAPI.h"
34 #include "SharedFontList-impl.h"
35 #include "StandardFonts-linux.inc"
36 #include "mozilla/intl/Locale.h"
38 #include "mozilla/gfx/HelpersCairo.h"
40 #include <cairo-ft.h>
41 #include <fontconfig/fcfreetype.h>
42 #include <fontconfig/fontconfig.h>
43 #include <harfbuzz/hb.h>
44 #include <dlfcn.h>
45 #include <unistd.h>
47 #ifdef MOZ_WIDGET_GTK
48 # include <gdk/gdk.h>
49 # include <gtk/gtk.h>
50 # include "gfxPlatformGtk.h"
51 # include "mozilla/WidgetUtilsGtk.h"
52 #endif
54 #ifdef MOZ_X11
55 # include "mozilla/X11Util.h"
56 #endif
58 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
59 # include "mozilla/SandboxBrokerPolicyFactory.h"
60 # include "mozilla/SandboxSettings.h"
61 #endif
63 #include FT_MULTIPLE_MASTERS_H
65 using namespace mozilla;
66 using namespace mozilla::gfx;
67 using namespace mozilla::unicode;
68 using namespace mozilla::intl;
70 #ifndef FC_POSTSCRIPT_NAME
71 # define FC_POSTSCRIPT_NAME "postscriptname" /* String */
72 #endif
73 #ifndef FC_VARIABLE
74 # define FC_VARIABLE "variable" /* Bool */
75 #endif
77 #define PRINTING_FC_PROPERTY "gfx.printing"
79 #define LOG_FONTLIST(args) \
80 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
81 #define LOG_FONTLIST_ENABLED() \
82 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
83 #define LOG_CMAPDATA_ENABLED() \
84 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)
86 static const FcChar8* ToFcChar8Ptr(const char* aStr) {
87 return reinterpret_cast<const FcChar8*>(aStr);
90 static const char* ToCharPtr(const FcChar8* aStr) {
91 return reinterpret_cast<const char*>(aStr);
94 // canonical name ==> first en name or first name if no en name
95 // This is the required logic for fullname lookups as per CSS3 Fonts spec.
96 static uint32_t FindCanonicalNameIndex(FcPattern* aFont,
97 const char* aLangField) {
98 uint32_t n = 0, en = 0;
99 FcChar8* lang;
100 while (FcPatternGetString(aFont, aLangField, n, &lang) == FcResultMatch) {
101 // look for 'en' or variants, en-US, en-JP etc.
102 uint32_t len = strlen(ToCharPtr(lang));
103 bool enPrefix = (strncmp(ToCharPtr(lang), "en", 2) == 0);
104 if (enPrefix && (len == 2 || (len > 2 && aLangField[2] == '-'))) {
105 en = n;
106 break;
108 n++;
110 return en;
113 static void GetFaceNames(FcPattern* aFont, const nsACString& aFamilyName,
114 nsACString& aPostscriptName, nsACString& aFullname) {
115 // get the Postscript name
116 FcChar8* psname;
117 if (FcPatternGetString(aFont, FC_POSTSCRIPT_NAME, 0, &psname) ==
118 FcResultMatch) {
119 aPostscriptName = ToCharPtr(psname);
122 // get the canonical fullname (i.e. en name or first name)
123 uint32_t en = FindCanonicalNameIndex(aFont, FC_FULLNAMELANG);
124 FcChar8* fullname;
125 if (FcPatternGetString(aFont, FC_FULLNAME, en, &fullname) == FcResultMatch) {
126 aFullname = ToCharPtr(fullname);
129 // if have fullname, done
130 if (!aFullname.IsEmpty()) {
131 return;
134 // otherwise, set the fullname to family + style name [en] and use that
135 aFullname = aFamilyName;
137 // figure out the en style name
138 en = FindCanonicalNameIndex(aFont, FC_STYLELANG);
139 nsAutoCString style;
140 FcChar8* stylename = nullptr;
141 FcPatternGetString(aFont, FC_STYLE, en, &stylename);
142 if (stylename) {
143 style = ToCharPtr(stylename);
146 if (!style.IsEmpty() && !style.EqualsLiteral("Regular")) {
147 aFullname.Append(' ');
148 aFullname.Append(style);
152 static FontWeight MapFcWeight(int aFcWeight) {
153 if (aFcWeight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2) {
154 return FontWeight::FromInt(100);
156 if (aFcWeight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2) {
157 return FontWeight::FromInt(200);
159 if (aFcWeight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2) {
160 return FontWeight::FromInt(300);
162 if (aFcWeight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2) {
163 // This includes FC_WEIGHT_BOOK
164 return FontWeight::FromInt(400);
166 if (aFcWeight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) {
167 return FontWeight::FromInt(500);
169 if (aFcWeight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) {
170 return FontWeight::FromInt(600);
172 if (aFcWeight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2) {
173 return FontWeight::FromInt(700);
175 if (aFcWeight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2) {
176 return FontWeight::FromInt(800);
178 if (aFcWeight <= FC_WEIGHT_BLACK) {
179 return FontWeight::FromInt(900);
182 // including FC_WEIGHT_EXTRABLACK
183 return FontWeight::FromInt(901);
186 // TODO(emilio, jfkthame): I think this can now be more fine-grained.
187 static FontStretch MapFcWidth(int aFcWidth) {
188 if (aFcWidth <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
189 return FontStretch::ULTRA_CONDENSED;
191 if (aFcWidth <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
192 return FontStretch::EXTRA_CONDENSED;
194 if (aFcWidth <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
195 return FontStretch::CONDENSED;
197 if (aFcWidth <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
198 return FontStretch::SEMI_CONDENSED;
200 if (aFcWidth <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
201 return FontStretch::NORMAL;
203 if (aFcWidth <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
204 return FontStretch::SEMI_EXPANDED;
206 if (aFcWidth <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
207 return FontStretch::EXPANDED;
209 if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
210 return FontStretch::EXTRA_EXPANDED;
212 return FontStretch::ULTRA_EXPANDED;
215 static void GetFontProperties(FcPattern* aFontPattern, WeightRange* aWeight,
216 StretchRange* aStretch,
217 SlantStyleRange* aSlantStyle,
218 uint16_t* aSize = nullptr) {
219 // weight
220 int weight;
221 if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) !=
222 FcResultMatch) {
223 weight = FC_WEIGHT_REGULAR;
225 *aWeight = WeightRange(MapFcWeight(weight));
227 // width
228 int width;
229 if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
230 width = FC_WIDTH_NORMAL;
232 *aStretch = StretchRange(MapFcWidth(width));
234 // italic
235 int slant;
236 if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
237 slant = FC_SLANT_ROMAN;
239 if (slant == FC_SLANT_OBLIQUE) {
240 *aSlantStyle = SlantStyleRange(FontSlantStyle::OBLIQUE);
241 } else if (slant > 0) {
242 *aSlantStyle = SlantStyleRange(FontSlantStyle::ITALIC);
245 if (aSize) {
246 // pixel size, or zero if scalable
247 FcBool scalable;
248 if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) ==
249 FcResultMatch &&
250 scalable) {
251 *aSize = 0;
252 } else {
253 double size;
254 if (FcPatternGetDouble(aFontPattern, FC_PIXEL_SIZE, 0, &size) ==
255 FcResultMatch) {
256 *aSize = uint16_t(NS_round(size));
257 } else {
258 *aSize = 0;
264 void gfxFontconfigFontEntry::GetUserFontFeatures(FcPattern* aPattern) {
265 int fontFeaturesNum = 0;
266 char* s;
267 hb_feature_t tmpFeature;
268 while (FcResultMatch == FcPatternGetString(aPattern, "fontfeatures",
269 fontFeaturesNum, (FcChar8**)&s)) {
270 bool ret = hb_feature_from_string(s, -1, &tmpFeature);
271 if (ret) {
272 mFeatureSettings.AppendElement(
273 (gfxFontFeature){tmpFeature.tag, tmpFeature.value});
275 fontFeaturesNum++;
279 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
280 FcPattern* aFontPattern,
281 bool aIgnoreFcCharmap)
282 : gfxFT2FontEntryBase(aFaceName),
283 mFontPattern(aFontPattern),
284 mFTFaceInitialized(false),
285 mIgnoreFcCharmap(aIgnoreFcCharmap) {
286 GetFontProperties(aFontPattern, &mWeightRange, &mStretchRange, &mStyleRange);
287 GetUserFontFeatures(mFontPattern);
290 gfxFontEntry* gfxFontconfigFontEntry::Clone() const {
291 MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
292 return new gfxFontconfigFontEntry(Name(), mFontPattern, mIgnoreFcCharmap);
295 static already_AddRefed<FcPattern> CreatePatternForFace(FT_Face aFace) {
296 // Use fontconfig to fill out the pattern from the FTFace.
297 // The "file" argument cannot be nullptr (in fontconfig-2.6.0 at
298 // least). The dummy file passed here is removed below.
300 // When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr)
301 // is passed as the "blanks" argument, which provides that unexpectedly
302 // blank glyphs are elided. Here, however, we pass nullptr for
303 // "blanks", effectively assuming that, if the font has a blank glyph,
304 // then the author intends any associated character to be rendered
305 // blank.
306 RefPtr<FcPattern> pattern =
307 dont_AddRef(FcFreeTypeQueryFace(aFace, ToFcChar8Ptr(""), 0, nullptr));
308 // given that we have a FT_Face, not really sure this is possible...
309 if (!pattern) {
310 pattern = dont_AddRef(FcPatternCreate());
312 FcPatternDel(pattern, FC_FILE);
313 FcPatternDel(pattern, FC_INDEX);
315 // Make a new pattern and store the face in it so that cairo uses
316 // that when creating a cairo font face.
317 FcPatternAddFTFace(pattern, FC_FT_FACE, aFace);
319 return pattern.forget();
322 static already_AddRefed<SharedFTFace> CreateFaceForPattern(
323 FcPattern* aPattern) {
324 FcChar8* filename;
325 if (FcPatternGetString(aPattern, FC_FILE, 0, &filename) != FcResultMatch) {
326 return nullptr;
328 int index;
329 if (FcPatternGetInteger(aPattern, FC_INDEX, 0, &index) != FcResultMatch) {
330 index = 0; // default to 0 if not found in pattern
332 return Factory::NewSharedFTFace(nullptr, ToCharPtr(filename), index);
335 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
336 WeightRange aWeight,
337 StretchRange aStretch,
338 SlantStyleRange aStyle,
339 RefPtr<SharedFTFace>&& aFace)
340 : gfxFT2FontEntryBase(aFaceName),
341 mFontPattern(CreatePatternForFace(aFace->GetFace())),
342 mFTFace(aFace.forget().take()),
343 mFTFaceInitialized(true),
344 mIgnoreFcCharmap(true) {
345 mWeightRange = aWeight;
346 mStyleRange = aStyle;
347 mStretchRange = aStretch;
348 mIsDataUserFont = true;
351 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
352 FcPattern* aFontPattern,
353 WeightRange aWeight,
354 StretchRange aStretch,
355 SlantStyleRange aStyle)
356 : gfxFT2FontEntryBase(aFaceName),
357 mFontPattern(aFontPattern),
358 mFTFaceInitialized(false) {
359 mWeightRange = aWeight;
360 mStyleRange = aStyle;
361 mStretchRange = aStretch;
362 mIsLocalUserFont = true;
364 // The proper setting of mIgnoreFcCharmap is tricky for fonts loaded
365 // via src:local()...
366 // If the local font happens to come from the application fontset,
367 // we want to set it to true so that color/svg fonts will work even
368 // if the default glyphs are blank; but if the local font is a non-
369 // sfnt face (e.g. legacy type 1) then we need to set it to false
370 // because our cmap-reading code will fail and we depend on FT+Fc to
371 // determine the coverage.
372 // We set the flag here, but may flip it the first time TestCharacterMap
373 // is called, at which point we'll look to see whether a 'cmap' is
374 // actually present in the font.
375 mIgnoreFcCharmap = true;
377 GetUserFontFeatures(mFontPattern);
380 typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**);
381 typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*);
382 static GetVarFunc sGetVar;
383 static DoneVarFunc sDoneVar;
384 static bool sInitializedVarFuncs = false;
386 static void InitializeVarFuncs() {
387 if (sInitializedVarFuncs) {
388 return;
390 sInitializedVarFuncs = true;
391 #if MOZ_TREE_FREETYPE
392 sGetVar = &FT_Get_MM_Var;
393 sDoneVar = &FT_Done_MM_Var;
394 #else
395 sGetVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
396 sDoneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var");
397 #endif
400 gfxFontconfigFontEntry::~gfxFontconfigFontEntry() {
401 if (mMMVar) {
402 // Prior to freetype 2.9, there was no specific function to free the
403 // FT_MM_Var record, and the docs just said to use free().
404 // InitializeVarFuncs must have been called in order for mMMVar to be
405 // non-null here, so we don't need to do it again.
406 if (sDoneVar) {
407 auto ftFace = GetFTFace();
408 MOZ_ASSERT(ftFace, "How did mMMVar get set without a face?");
409 (*sDoneVar)(ftFace->GetFace()->glyph->library, mMMVar);
410 } else {
411 free(mMMVar);
414 if (mFTFaceInitialized) {
415 auto face = mFTFace.exchange(nullptr);
416 NS_IF_RELEASE(face);
420 nsresult gfxFontconfigFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
421 // attempt this once, if errors occur leave a blank cmap
422 if (mCharacterMap) {
423 return NS_OK;
426 RefPtr<gfxCharacterMap> charmap;
427 nsresult rv;
429 uint32_t uvsOffset = 0;
430 if (aFontInfoData &&
431 (charmap = GetCMAPFromFontInfo(aFontInfoData, uvsOffset))) {
432 rv = NS_OK;
433 } else {
434 uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
435 charmap = new gfxCharacterMap();
436 AutoTable cmapTable(this, kCMAP);
438 if (cmapTable) {
439 uint32_t cmapLen;
440 const uint8_t* cmapData = reinterpret_cast<const uint8_t*>(
441 hb_blob_get_data(cmapTable, &cmapLen));
442 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, uvsOffset);
443 } else {
444 rv = NS_ERROR_NOT_AVAILABLE;
447 mUVSOffset.exchange(uvsOffset);
449 bool setCharMap = true;
450 if (NS_SUCCEEDED(rv)) {
451 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
452 fontlist::FontList* sharedFontList = pfl->SharedFontList();
453 if (!IsUserFont() && mShmemFace) {
454 mShmemFace->SetCharacterMap(sharedFontList, charmap, mShmemFamily);
455 if (TrySetShmemCharacterMap()) {
456 setCharMap = false;
458 } else {
459 charmap = pfl->FindCharMap(charmap);
461 mHasCmapTable = true;
462 } else {
463 // if error occurred, initialize to null cmap
464 charmap = new gfxCharacterMap();
465 mHasCmapTable = false;
467 if (setCharMap) {
468 if (mCharacterMap.compareExchange(nullptr, charmap.get())) {
469 charmap.get()->AddRef();
473 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n",
474 mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
475 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
476 if (LOG_CMAPDATA_ENABLED()) {
477 char prefix[256];
478 SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
479 charmap->Dump(prefix, eGfxLog_cmapdata);
482 return rv;
485 static bool HasChar(FcPattern* aFont, FcChar32 aCh) {
486 FcCharSet* charset = nullptr;
487 FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
488 return charset && FcCharSetHasChar(charset, aCh);
491 bool gfxFontconfigFontEntry::TestCharacterMap(uint32_t aCh) {
492 // For user fonts, or for fonts bundled with the app (which might include
493 // color/svg glyphs where the default glyphs may be blank, and thus confuse
494 // fontconfig/freetype's char map checking), we instead check the cmap
495 // directly for character coverage.
496 if (mIgnoreFcCharmap) {
497 // If it does not actually have a cmap, switch our strategy to use
498 // fontconfig's charmap after all (except for data fonts, which must
499 // always have a cmap to have passed OTS validation).
500 if (!mIsDataUserFont && !HasFontTable(TRUETYPE_TAG('c', 'm', 'a', 'p'))) {
501 mIgnoreFcCharmap = false;
502 // ...and continue with HasChar() below.
503 } else {
504 return gfxFontEntry::TestCharacterMap(aCh);
507 // otherwise (for system fonts), use the charmap in the pattern
508 return HasChar(mFontPattern, aCh);
511 bool gfxFontconfigFontEntry::HasFontTable(uint32_t aTableTag) {
512 if (FTUserFontData* ufd = GetUserFontData()) {
513 if (ufd->FontData()) {
514 return !!gfxFontUtils::FindTableDirEntry(ufd->FontData(), aTableTag);
517 return gfxFT2FontEntryBase::FaceHasTable(GetFTFace(), aTableTag);
520 hb_blob_t* gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag) {
521 // for data fonts, read directly from the font data
522 if (FTUserFontData* ufd = GetUserFontData()) {
523 if (ufd->FontData()) {
524 return gfxFontUtils::GetTableFromFontData(ufd->FontData(), aTableTag);
528 return gfxFontEntry::GetFontTable(aTableTag);
531 double gfxFontconfigFontEntry::GetAspect(uint8_t aSizeAdjustBasis) {
532 using FontSizeAdjust = gfxFont::FontSizeAdjust;
533 if (FontSizeAdjust::Tag(aSizeAdjustBasis) == FontSizeAdjust::Tag::ExHeight ||
534 FontSizeAdjust::Tag(aSizeAdjustBasis) == FontSizeAdjust::Tag::CapHeight) {
535 // try to compute aspect from OS/2 metrics if available
536 AutoTable os2Table(this, TRUETYPE_TAG('O', 'S', '/', '2'));
537 if (os2Table) {
538 uint16_t upem = UnitsPerEm();
539 if (upem != kInvalidUPEM) {
540 uint32_t len;
541 const auto* os2 =
542 reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
543 if (uint16_t(os2->version) >= 2) {
544 // XXX(jfkthame) Other implementations don't have the check for
545 // values <= 0.1em; should we drop that here? Just require it to be
546 // a positive number?
547 if (FontSizeAdjust::Tag(aSizeAdjustBasis) ==
548 FontSizeAdjust::Tag::ExHeight) {
549 if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
550 int16_t(os2->sxHeight) > 0.1 * upem) {
551 return double(int16_t(os2->sxHeight)) / upem;
554 if (FontSizeAdjust::Tag(aSizeAdjustBasis) ==
555 FontSizeAdjust::Tag::CapHeight) {
556 if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) &&
557 int16_t(os2->sCapHeight) > 0.1 * upem) {
558 return double(int16_t(os2->sCapHeight)) / upem;
566 // create a font to calculate the requested aspect
567 gfxFontStyle s;
568 s.size = 256.0; // pick large size to reduce hinting artifacts
569 RefPtr<gfxFont> font = FindOrMakeFont(&s);
570 if (font) {
571 const gfxFont::Metrics& metrics =
572 font->GetMetrics(nsFontMetrics::eHorizontal);
573 if (metrics.emHeight == 0) {
574 return 0;
576 switch (FontSizeAdjust::Tag(aSizeAdjustBasis)) {
577 case FontSizeAdjust::Tag::ExHeight:
578 return metrics.xHeight / metrics.emHeight;
579 case FontSizeAdjust::Tag::CapHeight:
580 return metrics.capHeight / metrics.emHeight;
581 case FontSizeAdjust::Tag::ChWidth:
582 return metrics.zeroWidth > 0 ? metrics.zeroWidth / metrics.emHeight
583 : 0.5;
584 case FontSizeAdjust::Tag::IcWidth:
585 case FontSizeAdjust::Tag::IcHeight: {
586 bool vertical = FontSizeAdjust::Tag(aSizeAdjustBasis) ==
587 FontSizeAdjust::Tag::IcHeight;
588 gfxFloat advance =
589 font->GetCharAdvance(gfxFont::kWaterIdeograph, vertical);
590 return advance > 0 ? advance / metrics.emHeight : 1.0;
592 default:
593 break;
597 MOZ_ASSERT_UNREACHABLE("failed to compute size-adjust aspect");
598 return 0.5;
601 static void PrepareFontOptions(FcPattern* aPattern, int* aOutLoadFlags,
602 unsigned int* aOutSynthFlags) {
603 int loadFlags = FT_LOAD_DEFAULT;
604 unsigned int synthFlags = 0;
606 // xxx - taken from the gfxFontconfigFonts code, needs to be reviewed
608 FcBool printing;
609 if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) !=
610 FcResultMatch) {
611 printing = FcFalse;
614 // Font options are set explicitly here to improve cairo's caching
615 // behavior and to record the relevant parts of the pattern so that
616 // the pattern can be released.
618 // Most font_options have already been set as defaults on the FcPattern
619 // with cairo_ft_font_options_substitute(), then user and system
620 // fontconfig configurations were applied. The resulting font_options
621 // have been recorded on the face during
622 // cairo_ft_font_face_create_for_pattern().
624 // None of the settings here cause this scaled_font to behave any
625 // differently from how it would behave if it were created from the same
626 // face with default font_options.
628 // We set options explicitly so that the same scaled_font will be found in
629 // the cairo_scaled_font_map when cairo loads glyphs from a context with
630 // the same font_face, font_matrix, ctm, and surface font_options.
632 // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
633 // font_options on the cairo_ft_font_face, and doesn't consider default
634 // option values to not match any explicit values.
636 // Even after cairo_set_scaled_font is used to set font_options for the
637 // cairo context, when cairo looks for a scaled_font for the context, it
638 // will look for a font with some option values from the target surface if
639 // any values are left default on the context font_options. If this
640 // scaled_font is created with default font_options, cairo will not find
641 // it.
643 // The one option not recorded in the pattern is hint_metrics, which will
644 // affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON.
645 // We should be considering the font_options of the surface on which this
646 // font will be used, but currently we don't have different gfxFonts for
647 // different surface font_options, so we'll create a font suitable for the
648 // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
650 // The remaining options have been recorded on the pattern and the face.
651 // _cairo_ft_options_merge has some logic to decide which options from the
652 // scaled_font or from the cairo_ft_font_face take priority in the way the
653 // font behaves.
655 // In the majority of cases, _cairo_ft_options_merge uses the options from
656 // the cairo_ft_font_face, so sometimes it is not so important which
657 // values are set here so long as they are not defaults, but we'll set
658 // them to the exact values that we expect from the font, to be consistent
659 // and to protect against changes in cairo.
661 // In some cases, _cairo_ft_options_merge uses some options from the
662 // scaled_font's font_options rather than options on the
663 // cairo_ft_font_face (from fontconfig).
664 // https://bugs.freedesktop.org/show_bug.cgi?id=11838
666 // Surface font options were set on the pattern in
667 // cairo_ft_font_options_substitute. If fontconfig has changed the
668 // hint_style then that is what the user (or distribution) wants, so we
669 // use the setting from the FcPattern.
671 // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
672 FcBool hinting = FcFalse;
673 if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
674 hinting = FcTrue;
677 int fc_hintstyle = FC_HINT_NONE;
678 if (!printing && hinting &&
679 FcPatternGetInteger(aPattern, FC_HINT_STYLE, 0, &fc_hintstyle) !=
680 FcResultMatch) {
681 fc_hintstyle = FC_HINT_FULL;
683 switch (fc_hintstyle) {
684 case FC_HINT_NONE:
685 loadFlags = FT_LOAD_NO_HINTING;
686 break;
687 case FC_HINT_SLIGHT:
688 loadFlags = FT_LOAD_TARGET_LIGHT;
689 break;
692 FcBool fc_antialias;
693 if (FcPatternGetBool(aPattern, FC_ANTIALIAS, 0, &fc_antialias) !=
694 FcResultMatch) {
695 fc_antialias = FcTrue;
697 if (!fc_antialias) {
698 if (fc_hintstyle != FC_HINT_NONE) {
699 loadFlags = FT_LOAD_TARGET_MONO;
701 loadFlags |= FT_LOAD_MONOCHROME;
702 } else if (fc_hintstyle == FC_HINT_FULL) {
703 int fc_rgba;
704 if (FcPatternGetInteger(aPattern, FC_RGBA, 0, &fc_rgba) != FcResultMatch) {
705 fc_rgba = FC_RGBA_UNKNOWN;
707 switch (fc_rgba) {
708 case FC_RGBA_RGB:
709 case FC_RGBA_BGR:
710 loadFlags = FT_LOAD_TARGET_LCD;
711 break;
712 case FC_RGBA_VRGB:
713 case FC_RGBA_VBGR:
714 loadFlags = FT_LOAD_TARGET_LCD_V;
715 break;
719 if (!FcPatternAllowsBitmaps(aPattern, fc_antialias != FcFalse,
720 fc_hintstyle != FC_HINT_NONE)) {
721 loadFlags |= FT_LOAD_NO_BITMAP;
724 FcBool autohint;
725 if (FcPatternGetBool(aPattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch &&
726 autohint) {
727 loadFlags |= FT_LOAD_FORCE_AUTOHINT;
730 FcBool embolden;
731 if (FcPatternGetBool(aPattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch &&
732 embolden) {
733 synthFlags |= CAIRO_FT_SYNTHESIZE_BOLD;
736 *aOutLoadFlags = loadFlags;
737 *aOutSynthFlags = synthFlags;
740 #ifdef MOZ_X11
741 static bool GetXftInt(Display* aDisplay, const char* aName, int* aResult) {
742 if (!aDisplay) {
743 return false;
745 char* value = XGetDefault(aDisplay, "Xft", aName);
746 if (!value) {
747 return false;
749 if (FcNameConstant(const_cast<FcChar8*>(ToFcChar8Ptr(value)), aResult)) {
750 return true;
752 char* end;
753 *aResult = strtol(value, &end, 0);
754 if (end != value) {
755 return true;
757 return false;
759 #endif
761 static void PreparePattern(FcPattern* aPattern, bool aIsPrinterFont) {
762 FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
764 // This gets cairo_font_options_t for the Screen. We should have
765 // different font options for printing (no hinting) but we are not told
766 // what we are measuring for.
768 // If cairo adds support for lcd_filter, gdk will not provide the default
769 // setting for that option. We could get the default setting by creating
770 // an xlib surface once, recording its font_options, and then merging the
771 // gdk options.
773 // Using an xlib surface would also be an option to get Screen font
774 // options for non-GTK X11 toolkits, but less efficient than using GDK to
775 // pick up dynamic changes.
776 if (aIsPrinterFont) {
777 cairo_font_options_t* options = cairo_font_options_create();
778 cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);
779 cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY);
780 cairo_ft_font_options_substitute(options, aPattern);
781 cairo_font_options_destroy(options);
782 FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
783 #ifdef MOZ_WIDGET_GTK
784 } else {
785 gfxFcPlatformFontList::PlatformFontList()->SubstituteSystemFontOptions(
786 aPattern);
787 #endif // MOZ_WIDGET_GTK
790 FcDefaultSubstitute(aPattern);
793 void gfxFontconfigFontEntry::UnscaledFontCache::MoveToFront(size_t aIndex) {
794 if (aIndex > 0) {
795 ThreadSafeWeakPtr<UnscaledFontFontconfig> front =
796 std::move(mUnscaledFonts[aIndex]);
797 for (size_t i = aIndex; i > 0; i--) {
798 mUnscaledFonts[i] = std::move(mUnscaledFonts[i - 1]);
800 mUnscaledFonts[0] = std::move(front);
804 already_AddRefed<UnscaledFontFontconfig>
805 gfxFontconfigFontEntry::UnscaledFontCache::Lookup(const std::string& aFile,
806 uint32_t aIndex) {
807 for (size_t i = 0; i < kNumEntries; i++) {
808 RefPtr<UnscaledFontFontconfig> entry(mUnscaledFonts[i]);
809 if (entry && entry->GetFile() == aFile && entry->GetIndex() == aIndex) {
810 MoveToFront(i);
811 return entry.forget();
814 return nullptr;
817 static inline gfxFloat SizeForStyle(gfxFontconfigFontEntry* aEntry,
818 const gfxFontStyle& aStyle) {
819 return StyleFontSizeAdjust::Tag(aStyle.sizeAdjustBasis) !=
820 StyleFontSizeAdjust::Tag::None
821 ? aStyle.GetAdjustedSize(aEntry->GetAspect(aStyle.sizeAdjustBasis))
822 : aStyle.size * aEntry->mSizeAdjust;
825 static double ChooseFontSize(gfxFontconfigFontEntry* aEntry,
826 const gfxFontStyle& aStyle) {
827 double requestedSize = SizeForStyle(aEntry, aStyle);
828 double bestDist = -1.0;
829 double bestSize = requestedSize;
830 double size;
831 int v = 0;
832 while (FcPatternGetDouble(aEntry->GetPattern(), FC_PIXEL_SIZE, v, &size) ==
833 FcResultMatch) {
834 ++v;
835 double dist = fabs(size - requestedSize);
836 if (bestDist < 0.0 || dist < bestDist) {
837 bestDist = dist;
838 bestSize = size;
841 // If the font has bitmaps but wants to be scaled, then let it scale.
842 if (bestSize >= 0.0) {
843 FcBool scalable;
844 if (FcPatternGetBool(aEntry->GetPattern(), FC_SCALABLE, 0, &scalable) ==
845 FcResultMatch &&
846 scalable) {
847 return requestedSize;
850 return bestSize;
853 gfxFont* gfxFontconfigFontEntry::CreateFontInstance(
854 const gfxFontStyle* aFontStyle) {
855 RefPtr<FcPattern> pattern = dont_AddRef(FcPatternCreate());
856 if (!pattern) {
857 NS_WARNING("Failed to create Fontconfig pattern for font instance");
858 return nullptr;
861 double size = ChooseFontSize(this, *aFontStyle);
862 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
864 RefPtr<SharedFTFace> face = GetFTFace();
865 if (!face) {
866 NS_WARNING("Failed to get FreeType face for pattern");
867 return nullptr;
869 if (HasVariations()) {
870 // For variation fonts, we create a new FT_Face here so that
871 // variation coordinates from the style can be applied without
872 // affecting other font instances created from the same entry
873 // (font resource).
874 // For user fonts: create a new FT_Face from the font data, and then make
875 // a pattern from that.
876 // For system fonts: create a new FT_Face and store it in a copy of the
877 // original mFontPattern.
878 RefPtr<SharedFTFace> varFace = face->GetData()
879 ? face->GetData()->CloneFace()
880 : CreateFaceForPattern(mFontPattern);
881 if (varFace) {
882 AutoTArray<gfxFontVariation, 8> settings;
883 GetVariationsForStyle(settings, *aFontStyle);
884 gfxFT2FontBase::SetupVarCoords(GetMMVar(), settings, varFace->GetFace());
885 face = std::move(varFace);
889 PreparePattern(pattern, aFontStyle->printerFont);
890 RefPtr<FcPattern> renderPattern =
891 dont_AddRef(FcFontRenderPrepare(nullptr, pattern, mFontPattern));
892 if (!renderPattern) {
893 NS_WARNING("Failed to prepare Fontconfig pattern for font instance");
894 return nullptr;
897 if (aFontStyle->NeedsSyntheticBold(this)) {
898 FcPatternAddBool(renderPattern, FC_EMBOLDEN, FcTrue);
901 // will synthetic oblique be applied using a transform?
902 if (IsUpright() && !aFontStyle->style.IsNormal() &&
903 aFontStyle->allowSyntheticStyle) {
904 // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
905 FcPatternDel(renderPattern, FC_EMBEDDED_BITMAP);
906 FcPatternAddBool(renderPattern, FC_EMBEDDED_BITMAP, FcFalse);
909 int loadFlags;
910 unsigned int synthFlags;
911 PrepareFontOptions(renderPattern, &loadFlags, &synthFlags);
913 std::string file;
914 int index = 0;
915 if (!face->GetData()) {
916 const FcChar8* fcFile;
917 if (FcPatternGetString(renderPattern, FC_FILE, 0,
918 const_cast<FcChar8**>(&fcFile)) != FcResultMatch ||
919 FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index) !=
920 FcResultMatch) {
921 NS_WARNING("No file in Fontconfig pattern for font instance");
922 return nullptr;
924 file = ToCharPtr(fcFile);
927 RefPtr<UnscaledFontFontconfig> unscaledFont;
929 AutoReadLock lock(mLock);
930 unscaledFont = mUnscaledFontCache.Lookup(file, index);
933 if (!unscaledFont) {
934 AutoWriteLock lock(mLock);
935 // Here, we use the original mFTFace, not a potential clone with variation
936 // settings applied.
937 auto ftFace = GetFTFace();
938 unscaledFont = ftFace->GetData() ? new UnscaledFontFontconfig(ftFace)
939 : new UnscaledFontFontconfig(
940 std::move(file), index, ftFace);
941 mUnscaledFontCache.Add(unscaledFont);
944 gfxFont* newFont = new gfxFontconfigFont(
945 unscaledFont, std::move(face), renderPattern, size, this, aFontStyle,
946 loadFlags, (synthFlags & CAIRO_FT_SYNTHESIZE_BOLD) != 0);
948 return newFont;
951 SharedFTFace* gfxFontconfigFontEntry::GetFTFace() {
952 if (!mFTFaceInitialized) {
953 RefPtr<SharedFTFace> face = CreateFaceForPattern(mFontPattern);
954 if (face) {
955 if (mFTFace.compareExchange(nullptr, face.get())) {
956 Unused << face.forget(); // The reference is now owned by mFTFace.
957 mFTFaceInitialized = true;
958 } else {
959 // We lost a race to set mFTFace! Just discard our new face.
963 return mFTFace;
966 FTUserFontData* gfxFontconfigFontEntry::GetUserFontData() {
967 auto face = GetFTFace();
968 if (face && face->GetData()) {
969 return static_cast<FTUserFontData*>(face->GetData());
971 return nullptr;
974 bool gfxFontconfigFontEntry::HasVariations() {
975 // If the answer is already cached, just return it.
976 switch (mHasVariations) {
977 case HasVariationsState::No:
978 return false;
979 case HasVariationsState::Yes:
980 return true;
981 case HasVariationsState::Uninitialized:
982 break;
985 // Figure out whether we have variations, and record in mHasVariations.
986 // (It doesn't matter if we race with another thread to set this; the result
987 // will be the same.)
989 if (!gfxPlatform::HasVariationFontSupport()) {
990 mHasVariations = HasVariationsState::No;
991 return false;
994 // For installed fonts, query the fontconfig pattern rather than paying
995 // the cost of loading a FT_Face that we otherwise might never need.
996 if (!IsUserFont() || IsLocalUserFont()) {
997 FcBool variable;
998 if ((FcPatternGetBool(mFontPattern, FC_VARIABLE, 0, &variable) ==
999 FcResultMatch) &&
1000 variable) {
1001 mHasVariations = HasVariationsState::Yes;
1002 return true;
1004 } else {
1005 if (auto ftFace = GetFTFace()) {
1006 if (ftFace->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
1007 mHasVariations = HasVariationsState::Yes;
1008 return true;
1013 mHasVariations = HasVariationsState::No;
1014 return false;
1017 FT_MM_Var* gfxFontconfigFontEntry::GetMMVar() {
1019 AutoReadLock lock(mLock);
1020 if (mMMVarInitialized) {
1021 return mMMVar;
1025 AutoWriteLock lock(mLock);
1027 mMMVarInitialized = true;
1028 InitializeVarFuncs();
1029 if (!sGetVar) {
1030 return nullptr;
1032 auto ftFace = GetFTFace();
1033 if (!ftFace) {
1034 return nullptr;
1036 if (FT_Err_Ok != (*sGetVar)(ftFace->GetFace(), &mMMVar)) {
1037 mMMVar = nullptr;
1039 return mMMVar;
1042 void gfxFontconfigFontEntry::GetVariationAxes(
1043 nsTArray<gfxFontVariationAxis>& aAxes) {
1044 if (!HasVariations()) {
1045 return;
1047 gfxFT2Utils::GetVariationAxes(GetMMVar(), aAxes);
1050 void gfxFontconfigFontEntry::GetVariationInstances(
1051 nsTArray<gfxFontVariationInstance>& aInstances) {
1052 if (!HasVariations()) {
1053 return;
1055 gfxFT2Utils::GetVariationInstances(this, GetMMVar(), aInstances);
1058 nsresult gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
1059 nsTArray<uint8_t>& aBuffer) {
1060 NS_ASSERTION(!mIsDataUserFont,
1061 "data fonts should be reading tables directly from memory");
1062 return gfxFT2FontEntryBase::CopyFaceTable(GetFTFace(), aTableTag, aBuffer);
1065 void gfxFontconfigFontFamily::FindStyleVariationsLocked(
1066 FontInfoData* aFontInfoData) {
1067 if (mHasStyles) {
1068 return;
1071 // add font entries for each of the faces
1072 uint32_t numFonts = mFontPatterns.Length();
1073 NS_ASSERTION(numFonts, "font family containing no faces!!");
1074 uint32_t numRegularFaces = 0;
1075 for (uint32_t i = 0; i < numFonts; i++) {
1076 FcPattern* face = mFontPatterns[i];
1078 // figure out the psname/fullname and choose which to use as the facename
1079 nsAutoCString psname, fullname;
1080 GetFaceNames(face, mName, psname, fullname);
1081 const nsAutoCString& faceName = !psname.IsEmpty() ? psname : fullname;
1083 gfxFontconfigFontEntry* fontEntry =
1084 new gfxFontconfigFontEntry(faceName, face, mContainsAppFonts);
1086 if (gfxPlatform::HasVariationFontSupport()) {
1087 fontEntry->SetupVariationRanges();
1090 AddFontEntryLocked(fontEntry);
1092 if (fontEntry->IsNormalStyle()) {
1093 numRegularFaces++;
1096 if (LOG_FONTLIST_ENABLED()) {
1097 nsAutoCString weightString;
1098 fontEntry->Weight().ToString(weightString);
1099 nsAutoCString stretchString;
1100 fontEntry->Stretch().ToString(stretchString);
1101 nsAutoCString styleString;
1102 fontEntry->SlantStyle().ToString(styleString);
1103 LOG_FONTLIST(
1104 ("(fontlist) added (%s) to family (%s)"
1105 " with style: %s weight: %s stretch: %s"
1106 " psname: %s fullname: %s",
1107 fontEntry->Name().get(), Name().get(), styleString.get(),
1108 weightString.get(), stretchString.get(), psname.get(),
1109 fullname.get()));
1113 // somewhat arbitrary, but define a family with two or more regular
1114 // faces as a family for which intra-family fallback should be used
1115 if (numRegularFaces > 1) {
1116 mCheckForFallbackFaces = true;
1118 mFaceNamesInitialized = true;
1119 mFontPatterns.Clear();
1120 SetHasStyles(true);
1122 CheckForSimpleFamily();
1125 void gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern,
1126 bool aSingleName) {
1127 NS_ASSERTION(
1128 !mHasStyles,
1129 "font patterns must not be added to already enumerated families");
1131 FcBool outline;
1132 if (FcPatternGetBool(aFontPattern, FC_OUTLINE, 0, &outline) !=
1133 FcResultMatch ||
1134 !outline) {
1135 mHasNonScalableFaces = true;
1137 FcBool scalable;
1138 if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) ==
1139 FcResultMatch &&
1140 scalable) {
1141 mForceScalable = true;
1145 if (aSingleName) {
1146 mFontPatterns.InsertElementAt(mUniqueNameFaceCount++, aFontPattern);
1147 } else {
1148 mFontPatterns.AppendElement(aFontPattern);
1152 static const double kRejectDistance = 10000.0;
1154 // Calculate a distance score representing the size disparity between the
1155 // requested style's size and the font entry's size.
1156 static double SizeDistance(gfxFontconfigFontEntry* aEntry,
1157 const gfxFontStyle& aStyle, bool aForceScalable) {
1158 double requestedSize = SizeForStyle(aEntry, aStyle);
1159 double bestDist = -1.0;
1160 double size;
1161 int v = 0;
1162 while (FcPatternGetDouble(aEntry->GetPattern(), FC_PIXEL_SIZE, v, &size) ==
1163 FcResultMatch) {
1164 ++v;
1165 double dist = fabs(size - requestedSize);
1166 if (bestDist < 0.0 || dist < bestDist) {
1167 bestDist = dist;
1170 if (bestDist < 0.0) {
1171 // No size means scalable
1172 return -1.0;
1173 } else if (aForceScalable || 5.0 * bestDist < requestedSize) {
1174 // fontconfig prefers a matching family or lang to pixelsize of bitmap
1175 // fonts. CSS suggests a tolerance of 20% on pixelsize.
1176 return bestDist;
1177 } else {
1178 // Reject any non-scalable fonts that are not within tolerance.
1179 return kRejectDistance;
1183 void gfxFontconfigFontFamily::FindAllFontsForStyle(
1184 const gfxFontStyle& aFontStyle, nsTArray<gfxFontEntry*>& aFontEntryList,
1185 bool aIgnoreSizeTolerance) {
1186 gfxFontFamily::FindAllFontsForStyle(aFontStyle, aFontEntryList,
1187 aIgnoreSizeTolerance);
1189 if (!mHasNonScalableFaces) {
1190 return;
1193 // Iterate over the the available fonts while compacting any groups
1194 // of unscalable fonts with matching styles into a single entry
1195 // corresponding to the closest available size. If the closest
1196 // available size is rejected for being outside tolerance, then the
1197 // entire group will be skipped.
1198 size_t skipped = 0;
1199 gfxFontconfigFontEntry* bestEntry = nullptr;
1200 double bestDist = -1.0;
1201 for (size_t i = 0; i < aFontEntryList.Length(); i++) {
1202 gfxFontconfigFontEntry* entry =
1203 static_cast<gfxFontconfigFontEntry*>(aFontEntryList[i]);
1204 double dist =
1205 SizeDistance(entry, aFontStyle, mForceScalable || aIgnoreSizeTolerance);
1206 // If the entry is scalable or has a style that does not match
1207 // the group of unscalable fonts, then start a new group.
1208 if (dist < 0.0 || !bestEntry || bestEntry->Stretch() != entry->Stretch() ||
1209 bestEntry->Weight() != entry->Weight() ||
1210 bestEntry->SlantStyle() != entry->SlantStyle()) {
1211 // If the best entry in this group is still outside the tolerance,
1212 // then skip the entire group.
1213 if (bestDist >= kRejectDistance) {
1214 skipped++;
1216 // Remove any compacted entries from the previous group.
1217 if (skipped) {
1218 i -= skipped;
1219 aFontEntryList.RemoveElementsAt(i, skipped);
1220 skipped = 0;
1222 // Mark the start of the new group.
1223 bestEntry = entry;
1224 bestDist = dist;
1225 } else {
1226 // If this entry more closely matches the requested size than the
1227 // current best in the group, then take this entry instead.
1228 if (dist < bestDist) {
1229 aFontEntryList[i - 1 - skipped] = entry;
1230 bestEntry = entry;
1231 bestDist = dist;
1233 skipped++;
1236 // If the best entry in this group is still outside the tolerance,
1237 // then skip the entire group.
1238 if (bestDist >= kRejectDistance) {
1239 skipped++;
1241 // Remove any compacted entries from the current group.
1242 if (skipped) {
1243 aFontEntryList.TruncateLength(aFontEntryList.Length() - skipped);
1247 static bool PatternHasLang(const FcPattern* aPattern, const FcChar8* aLang) {
1248 FcLangSet* langset;
1250 if (FcPatternGetLangSet(aPattern, FC_LANG, 0, &langset) != FcResultMatch) {
1251 return false;
1254 if (FcLangSetHasLang(langset, aLang) != FcLangDifferentLang) {
1255 return true;
1257 return false;
1260 bool gfxFontconfigFontFamily::SupportsLangGroup(nsAtom* aLangGroup) const {
1261 if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) {
1262 return true;
1265 nsAutoCString fcLang;
1266 gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
1267 pfl->GetSampleLangForGroup(aLangGroup, fcLang);
1268 if (fcLang.IsEmpty()) {
1269 return true;
1272 // Before FindStyleVariations has been called, mFontPatterns will contain
1273 // the font patterns. Afterward, it'll be empty, but mAvailableFonts
1274 // will contain the font entries, each of which holds a reference to its
1275 // pattern. We only check the first pattern in each list, because support
1276 // for langs is considered to be consistent across all faces in a family.
1277 AutoReadLock lock(mLock);
1278 FcPattern* fontPattern;
1279 if (mFontPatterns.Length()) {
1280 fontPattern = mFontPatterns[0];
1281 } else if (mAvailableFonts.Length()) {
1282 fontPattern = static_cast<gfxFontconfigFontEntry*>(mAvailableFonts[0].get())
1283 ->GetPattern();
1284 } else {
1285 return true;
1288 // is lang included in the underlying pattern?
1289 return PatternHasLang(fontPattern, ToFcChar8Ptr(fcLang.get()));
1292 /* virtual */
1293 gfxFontconfigFontFamily::~gfxFontconfigFontFamily() {
1294 // Should not be dropped by stylo
1295 MOZ_ASSERT(NS_IsMainThread());
1298 template <typename Func>
1299 void gfxFontconfigFontFamily::AddFacesToFontList(Func aAddPatternFunc) {
1300 AutoReadLock lock(mLock);
1301 if (HasStyles()) {
1302 for (auto& fe : mAvailableFonts) {
1303 if (!fe) {
1304 continue;
1306 auto fce = static_cast<gfxFontconfigFontEntry*>(fe.get());
1307 aAddPatternFunc(fce->GetPattern(), mContainsAppFonts);
1309 } else {
1310 for (auto& pat : mFontPatterns) {
1311 aAddPatternFunc(pat, mContainsAppFonts);
1316 gfxFontconfigFont::gfxFontconfigFont(
1317 const RefPtr<UnscaledFontFontconfig>& aUnscaledFont,
1318 RefPtr<SharedFTFace>&& aFTFace, FcPattern* aPattern, gfxFloat aAdjustedSize,
1319 gfxFontEntry* aFontEntry, const gfxFontStyle* aFontStyle, int aLoadFlags,
1320 bool aEmbolden)
1321 : gfxFT2FontBase(aUnscaledFont, std::move(aFTFace), aFontEntry, aFontStyle,
1322 aLoadFlags, aEmbolden),
1323 mPattern(aPattern) {
1324 mAdjustedSize = aAdjustedSize;
1325 InitMetrics();
1328 gfxFontconfigFont::~gfxFontconfigFont() = default;
1330 already_AddRefed<ScaledFont> gfxFontconfigFont::GetScaledFont(
1331 const TextRunDrawParams& aRunParams) {
1332 if (ScaledFont* scaledFont = mAzureScaledFont) {
1333 return do_AddRef(scaledFont);
1336 RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForFontconfigFont(
1337 GetUnscaledFont(), GetAdjustedSize(), mFTFace, GetPattern());
1338 if (!newScaledFont) {
1339 return nullptr;
1342 InitializeScaledFont(newScaledFont);
1344 if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) {
1345 Unused << newScaledFont.forget();
1347 ScaledFont* scaledFont = mAzureScaledFont;
1348 return do_AddRef(scaledFont);
1351 bool gfxFontconfigFont::ShouldHintMetrics() const {
1352 return !GetStyle()->printerFont;
1355 gfxFcPlatformFontList::gfxFcPlatformFontList()
1356 : mLocalNames(64),
1357 mGenericMappings(32),
1358 mFcSubstituteCache(64),
1359 mLastConfig(nullptr),
1360 mAlwaysUseFontconfigGenerics(true) {
1361 CheckFamilyList(kBaseFonts_Ubuntu_22_04);
1362 CheckFamilyList(kLangFonts_Ubuntu_22_04);
1363 CheckFamilyList(kBaseFonts_Ubuntu_20_04);
1364 CheckFamilyList(kLangFonts_Ubuntu_20_04);
1365 CheckFamilyList(kBaseFonts_Fedora_39);
1366 CheckFamilyList(kBaseFonts_Fedora_38);
1367 mLastConfig = FcConfigGetCurrent();
1368 if (XRE_IsParentProcess()) {
1369 // if the rescan interval is set, start the timer
1370 int rescanInterval = FcConfigGetRescanInterval(nullptr);
1371 if (rescanInterval) {
1372 NS_NewTimerWithFuncCallback(
1373 getter_AddRefs(mCheckFontUpdatesTimer), CheckFontUpdates, this,
1374 (rescanInterval + 1) * 1000, nsITimer::TYPE_REPEATING_SLACK,
1375 "gfxFcPlatformFontList::gfxFcPlatformFontList");
1376 if (!mCheckFontUpdatesTimer) {
1377 NS_WARNING("Failure to create font updates timer");
1382 #ifdef MOZ_BUNDLED_FONTS
1383 mBundledFontsInitialized = false;
1384 #endif
1387 gfxFcPlatformFontList::~gfxFcPlatformFontList() {
1388 AutoLock lock(mLock);
1390 if (mCheckFontUpdatesTimer) {
1391 mCheckFontUpdatesTimer->Cancel();
1392 mCheckFontUpdatesTimer = nullptr;
1394 #ifdef MOZ_WIDGET_GTK
1395 ClearSystemFontOptions();
1396 #endif
1399 void gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet,
1400 const SandboxPolicy* aPolicy,
1401 bool aAppFonts) {
1402 // This iterates over the fonts in a font set and adds in gfxFontFamily
1403 // objects for each family. Individual gfxFontEntry objects for each face
1404 // are not created here; the patterns are just stored in the family. When
1405 // a family is actually used, it will be populated with gfxFontEntry
1406 // records and the patterns moved to those.
1408 if (NS_WARN_IF(!aFontSet)) {
1409 return;
1412 FcChar8* lastFamilyName = (FcChar8*)"";
1413 RefPtr<gfxFontconfigFontFamily> fontFamily;
1414 nsAutoCString familyName;
1415 for (int f = 0; f < aFontSet->nfont; f++) {
1416 FcPattern* pattern = aFontSet->fonts[f];
1418 // Skip any fonts that aren't readable for us (e.g. due to restrictive
1419 // file ownership/permissions).
1420 FcChar8* path;
1421 if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
1422 continue;
1424 if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
1425 continue;
1428 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
1429 // Skip any fonts that will be blocked by the content-process sandbox
1430 // policy.
1431 if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
1432 SandboxBroker::Perms::MAY_READ)) {
1433 continue;
1435 #endif
1437 AddPatternToFontList(pattern, lastFamilyName, familyName, fontFamily,
1438 aAppFonts);
1442 void gfxFcPlatformFontList::AddPatternToFontList(
1443 FcPattern* aFont, FcChar8*& aLastFamilyName, nsACString& aFamilyName,
1444 RefPtr<gfxFontconfigFontFamily>& aFontFamily, bool aAppFonts) {
1445 // get canonical name
1446 uint32_t cIndex = FindCanonicalNameIndex(aFont, FC_FAMILYLANG);
1447 FcChar8* canonical = nullptr;
1448 FcPatternGetString(aFont, FC_FAMILY, cIndex, &canonical);
1449 if (!canonical) {
1450 return;
1453 // same as the last one? no need to add a new family, skip
1454 if (FcStrCmp(canonical, aLastFamilyName) != 0) {
1455 aLastFamilyName = canonical;
1457 // add new family if one doesn't already exist
1458 aFamilyName.Truncate();
1459 aFamilyName = ToCharPtr(canonical);
1460 nsAutoCString keyName(aFamilyName);
1461 ToLowerCase(keyName);
1463 aFontFamily = static_cast<gfxFontconfigFontFamily*>(
1464 mFontFamilies
1465 .LookupOrInsertWith(keyName,
1466 [&] {
1467 FontVisibility visibility =
1468 aAppFonts
1469 ? FontVisibility::Base
1470 : GetVisibilityForFamily(keyName);
1471 return MakeRefPtr<gfxFontconfigFontFamily>(
1472 aFamilyName, visibility);
1474 .get());
1475 // Record if the family contains fonts from the app font set
1476 // (in which case we won't rely on fontconfig's charmap, due to
1477 // bug 1276594).
1478 if (aAppFonts) {
1479 aFontFamily->SetFamilyContainsAppFonts(true);
1483 // Add pointers to other localized family names. Most fonts
1484 // only have a single name, so the first call to GetString
1485 // will usually not match
1486 FcChar8* otherName;
1487 int n = (cIndex == 0 ? 1 : 0);
1488 AutoTArray<nsCString, 4> otherFamilyNames;
1489 while (FcPatternGetString(aFont, FC_FAMILY, n, &otherName) == FcResultMatch) {
1490 otherFamilyNames.AppendElement(nsCString(ToCharPtr(otherName)));
1491 n++;
1492 if (n == int(cIndex)) {
1493 n++; // skip over canonical name
1496 if (!otherFamilyNames.IsEmpty()) {
1497 AddOtherFamilyNames(aFontFamily, otherFamilyNames);
1500 const bool singleName = n == 1;
1502 MOZ_ASSERT(aFontFamily, "font must belong to a font family");
1503 aFontFamily->AddFontPattern(aFont, singleName);
1505 // map the psname, fullname ==> font family for local font lookups
1506 nsAutoCString psname, fullname;
1507 GetFaceNames(aFont, aFamilyName, psname, fullname);
1508 if (!psname.IsEmpty()) {
1509 ToLowerCase(psname);
1510 mLocalNames.InsertOrUpdate(psname, RefPtr{aFont});
1512 if (!fullname.IsEmpty()) {
1513 ToLowerCase(fullname);
1514 mLocalNames.WithEntryHandle(fullname, [&](auto&& entry) {
1515 if (entry && !singleName) {
1516 return;
1518 entry.InsertOrUpdate(RefPtr{aFont});
1523 nsresult gfxFcPlatformFontList::InitFontListForPlatform() {
1524 #ifdef MOZ_BUNDLED_FONTS
1525 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1526 ActivateBundledFonts();
1528 #endif
1530 mLocalNames.Clear();
1531 mFcSubstituteCache.Clear();
1533 ClearSystemFontOptions();
1535 mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
1536 mOtherFamilyNamesInitialized = true;
1538 mLastConfig = FcConfigGetCurrent();
1540 if (XRE_IsContentProcess()) {
1541 // Content process: use the font list passed from the chrome process,
1542 // because we can't rely on fontconfig in the presence of sandboxing;
1543 // it may report fonts that we can't actually access.
1545 FcChar8* lastFamilyName = (FcChar8*)"";
1546 RefPtr<gfxFontconfigFontFamily> fontFamily;
1547 nsAutoCString familyName;
1549 // Get font list that was passed during XPCOM startup
1550 // or in an UpdateFontList message.
1551 auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
1553 #ifdef MOZ_WIDGET_GTK
1554 UpdateSystemFontOptionsFromIpc(fontList.options());
1555 #endif
1557 // For fontconfig versions between 2.10.94 and 2.11.1 inclusive,
1558 // we need to escape any leading space in the charset element,
1559 // otherwise FcNameParse will fail. :(
1561 // The bug was introduced on 2013-05-24 by
1562 // https://cgit.freedesktop.org/fontconfig/commit/?id=cd9b1033a68816a7acfbba1718ba0aa5888f6ec7
1563 // "Bug 64906 - FcNameParse() should ignore leading whitespace in
1564 // parameters"
1565 // because ignoring a leading space in the encoded value of charset
1566 // causes erroneous decoding of the whole element.
1567 // This first shipped in version 2.10.94, and was eventually fixed as
1568 // a side-effect of switching to the "human-readable" representation of
1569 // charsets on 2014-07-03 in
1570 // https://cgit.freedesktop.org/fontconfig/commit/?id=e708e97c351d3bc9f7030ef22ac2f007d5114730
1571 // "Change charset parse/unparse format to be human readable"
1572 // (with a followup fix next day) which means a leading space is no
1573 // longer significant. This fix landed after 2.11.1 had been shipped,
1574 // so the first version tag without the bug is 2.11.91.
1575 int fcVersion = FcGetVersion();
1576 bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;
1578 for (FontPatternListEntry& fpe : fontList.entries()) {
1579 nsCString& patternStr = fpe.pattern();
1580 if (fcCharsetParseBug) {
1581 int32_t index = patternStr.Find(":charset= ");
1582 if (index != kNotFound) {
1583 // insert backslash after the =, before the space
1584 patternStr.Insert('\\', index + 9);
1587 FcPattern* pattern = FcNameParse((const FcChar8*)patternStr.get());
1588 AddPatternToFontList(pattern, lastFamilyName, familyName, fontFamily,
1589 fpe.appFontFamily());
1590 FcPatternDestroy(pattern);
1593 LOG_FONTLIST(
1594 ("got font list from chrome process: "
1595 "%u faces in %u families",
1596 (unsigned)fontList.entries().Length(), mFontFamilies.Count()));
1598 fontList.entries().Clear();
1599 return NS_OK;
1602 UpdateSystemFontOptions();
1604 UniquePtr<SandboxPolicy> policy;
1606 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
1607 // If read sandboxing is enabled, create a temporary SandboxPolicy to
1608 // check font paths; use a fake PID to avoid picking up any PID-specific
1609 // rules by accident.
1610 SandboxBrokerPolicyFactory policyFactory;
1611 if (GetEffectiveContentSandboxLevel() > 2 &&
1612 !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
1613 policy = policyFactory.GetContentPolicy(-1, false);
1615 #endif
1617 #ifdef MOZ_BUNDLED_FONTS
1618 // https://bugzilla.mozilla.org/show_bug.cgi?id=1745715:
1619 // It's important to do this *before* processing the standard system fonts,
1620 // so that if a family is present in both font sets, we'll treat it as app-
1621 // bundled and therefore always visible.
1622 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1623 FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
1624 AddFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
1626 #endif
1628 // iterate over available fonts
1629 FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
1630 AddFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false);
1632 return NS_OK;
1635 void gfxFcPlatformFontList::ReadSystemFontList(dom::SystemFontList* retValue) {
1636 AutoLock lock(mLock);
1638 // Fontconfig versions below 2.9 drop the FC_FILE element in FcNameUnparse
1639 // (see https://bugs.freedesktop.org/show_bug.cgi?id=26718), so when using
1640 // an older version, we manually append it to the unparsed pattern.
1641 #ifdef MOZ_WIDGET_GTK
1642 SystemFontOptionsToIpc(retValue->options());
1643 #endif
1645 if (FcGetVersion() < 20900) {
1646 for (const auto& entry : mFontFamilies) {
1647 auto* family = static_cast<gfxFontconfigFontFamily*>(entry.GetWeak());
1648 family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
1649 char* s = (char*)FcNameUnparse(aPat);
1650 nsDependentCString patternStr(s);
1651 char* file = nullptr;
1652 if (FcResultMatch ==
1653 FcPatternGetString(aPat, FC_FILE, 0, (FcChar8**)&file)) {
1654 patternStr.Append(":file=");
1655 patternStr.Append(file);
1657 retValue->entries().AppendElement(
1658 FontPatternListEntry(patternStr, aAppFonts));
1659 free(s);
1662 } else {
1663 for (const auto& entry : mFontFamilies) {
1664 auto* family = static_cast<gfxFontconfigFontFamily*>(entry.GetWeak());
1665 family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
1666 char* s = (char*)FcNameUnparse(aPat);
1667 nsDependentCString patternStr(s);
1668 retValue->entries().AppendElement(
1669 FontPatternListEntry(patternStr, aAppFonts));
1670 free(s);
1676 using Device = nsIGfxInfo::FontVisibilityDeviceDetermination;
1677 static Device sFontVisibilityDevice = Device::Unassigned;
1679 void AssignFontVisibilityDevice() {
1680 if (sFontVisibilityDevice == Device::Unassigned) {
1681 nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
1682 NS_ENSURE_SUCCESS_VOID(
1683 gfxInfo->GetFontVisibilityDetermination(&sFontVisibilityDevice));
1687 // Per family array of faces.
1688 class FacesData {
1689 using FaceInitArray = AutoTArray<fontlist::Face::InitData, 8>;
1691 FaceInitArray mFaces;
1693 // Number of faces that have a single name. Faces that have multiple names are
1694 // sorted last.
1695 uint32_t mUniqueNameFaceCount = 0;
1697 public:
1698 void Add(fontlist::Face::InitData&& aData, bool aSingleName) {
1699 if (aSingleName) {
1700 mFaces.InsertElementAt(mUniqueNameFaceCount++, std::move(aData));
1701 } else {
1702 mFaces.AppendElement(std::move(aData));
1706 const FaceInitArray& Get() const { return mFaces; }
1709 void gfxFcPlatformFontList::InitSharedFontListForPlatform() {
1710 mLocalNames.Clear();
1711 mFcSubstituteCache.Clear();
1713 mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
1714 mOtherFamilyNamesInitialized = true;
1716 mLastConfig = FcConfigGetCurrent();
1718 if (!XRE_IsParentProcess()) {
1719 #ifdef MOZ_WIDGET_GTK
1720 auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
1721 UpdateSystemFontOptionsFromIpc(fontList.options());
1722 #endif
1723 // Content processes will access the shared-memory data created by the
1724 // parent, so they do not need to query fontconfig for the available
1725 // fonts themselves.
1726 return;
1729 #ifdef MOZ_WIDGET_GTK
1730 UpdateSystemFontOptions();
1731 #endif
1733 #ifdef MOZ_BUNDLED_FONTS
1734 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1735 TimeStamp start = TimeStamp::Now();
1736 ActivateBundledFonts();
1737 TimeStamp end = TimeStamp::Now();
1738 Telemetry::Accumulate(Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
1739 (end - start).ToMilliseconds());
1741 #endif
1743 UniquePtr<SandboxPolicy> policy;
1745 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
1746 // If read sandboxing is enabled, create a temporary SandboxPolicy to
1747 // check font paths; use a fake PID to avoid picking up any PID-specific
1748 // rules by accident.
1749 SandboxBrokerPolicyFactory policyFactory;
1750 if (GetEffectiveContentSandboxLevel() > 2 &&
1751 !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
1752 policy = policyFactory.GetContentPolicy(-1, false);
1754 #endif
1756 nsTArray<fontlist::Family::InitData> families;
1758 nsClassHashtable<nsCStringHashKey, FacesData> faces;
1760 // Do we need to work around the fontconfig FcNameParse/FcNameUnparse bug
1761 // (present in versions between 2.10.94 and 2.11.1 inclusive)? See comment
1762 // in InitFontListForPlatform for details.
1763 int fcVersion = FcGetVersion();
1764 bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;
1766 // Returns true if the font was added with FontVisibility::Base.
1767 // This enables us to count how many known Base fonts are present.
1768 auto addPattern = [this, fcCharsetParseBug, &families, &faces](
1769 FcPattern* aPattern, FcChar8*& aLastFamilyName,
1770 nsCString& aFamilyName, bool aAppFont) -> bool {
1771 // get canonical name
1772 uint32_t cIndex = FindCanonicalNameIndex(aPattern, FC_FAMILYLANG);
1773 FcChar8* canonical = nullptr;
1774 FcPatternGetString(aPattern, FC_FAMILY, cIndex, &canonical);
1775 if (!canonical) {
1776 return false;
1779 nsAutoCString keyName;
1780 keyName = ToCharPtr(canonical);
1781 ToLowerCase(keyName);
1783 aLastFamilyName = canonical;
1784 aFamilyName = ToCharPtr(canonical);
1786 const FontVisibility visibility =
1787 aAppFont ? FontVisibility::Base : GetVisibilityForFamily(keyName);
1789 // Same canonical family name as the last one? Definitely no need to add a
1790 // new family record.
1791 auto* faceList =
1792 faces
1793 .LookupOrInsertWith(
1794 keyName,
1795 [&] {
1796 families.AppendElement(fontlist::Family::InitData(
1797 keyName, aFamilyName, fontlist::Family::kNoIndex,
1798 visibility,
1799 /*bundled*/ aAppFont, /*badUnderline*/ false));
1800 return MakeUnique<FacesData>();
1802 .get();
1804 char* s = (char*)FcNameUnparse(aPattern);
1805 nsAutoCString descriptor(s);
1806 free(s);
1808 if (fcCharsetParseBug) {
1809 // Escape any leading space in charset to work around FcNameParse bug.
1810 int32_t index = descriptor.Find(":charset= ");
1811 if (index != kNotFound) {
1812 // insert backslash after the =, before the space
1813 descriptor.Insert('\\', index + 9);
1817 WeightRange weight(FontWeight::NORMAL);
1818 StretchRange stretch(FontStretch::NORMAL);
1819 SlantStyleRange style(FontSlantStyle::NORMAL);
1820 uint16_t size;
1821 GetFontProperties(aPattern, &weight, &stretch, &style, &size);
1823 auto initData = fontlist::Face::InitData{descriptor, 0, size, false,
1824 weight, stretch, style};
1826 // Add entries for any other localized family names. (Most fonts only have
1827 // a single family name, so the first call to GetString will usually fail).
1828 // These get the same visibility level as we looked up for the first name.
1829 FcChar8* otherName;
1830 int n = (cIndex == 0 ? 1 : 0);
1831 while (FcPatternGetString(aPattern, FC_FAMILY, n, &otherName) ==
1832 FcResultMatch) {
1833 nsAutoCString otherFamilyName(ToCharPtr(otherName));
1834 keyName = otherFamilyName;
1835 ToLowerCase(keyName);
1837 faces
1838 .LookupOrInsertWith(
1839 keyName,
1840 [&] {
1841 families.AppendElement(fontlist::Family::InitData(
1842 keyName, otherFamilyName, fontlist::Family::kNoIndex,
1843 visibility,
1844 /*bundled*/ aAppFont, /*badUnderline*/ false));
1846 return MakeUnique<FacesData>();
1848 .get()
1849 ->Add(fontlist::Face::InitData(initData), /* singleName = */ false);
1851 n++;
1852 if (n == int(cIndex)) {
1853 n++; // skip over canonical name
1857 const bool singleName = n == 1;
1858 faceList->Add(std::move(initData), singleName);
1860 // map the psname, fullname ==> font family for local font lookups
1861 nsAutoCString psname, fullname;
1862 GetFaceNames(aPattern, aFamilyName, psname, fullname);
1863 if (!psname.IsEmpty()) {
1864 ToLowerCase(psname);
1865 mLocalNameTable.InsertOrUpdate(
1866 psname, fontlist::LocalFaceRec::InitData(keyName, descriptor));
1868 if (!fullname.IsEmpty()) {
1869 ToLowerCase(fullname);
1870 if (fullname != psname) {
1871 mLocalNameTable.WithEntryHandle(fullname, [&](auto&& entry) {
1872 if (entry && !singleName) {
1873 // We only override an existing entry if this is the only way to
1874 // name this family. This prevents dubious aliases from clobbering
1875 // the local name table.
1876 return;
1878 entry.InsertOrUpdate(
1879 fontlist::LocalFaceRec::InitData(keyName, descriptor));
1884 return visibility == FontVisibility::Base;
1887 // Returns the number of families with FontVisibility::Base that were found.
1888 auto addFontSetFamilies = [&addPattern](FcFontSet* aFontSet,
1889 SandboxPolicy* aPolicy,
1890 bool aAppFonts) -> size_t {
1891 size_t count = 0;
1892 if (NS_WARN_IF(!aFontSet)) {
1893 return count;
1895 FcChar8* lastFamilyName = (FcChar8*)"";
1896 RefPtr<gfxFontconfigFontFamily> fontFamily;
1897 nsAutoCString familyName;
1898 for (int f = 0; f < aFontSet->nfont; f++) {
1899 FcPattern* pattern = aFontSet->fonts[f];
1901 // Skip any fonts that aren't readable for us (e.g. due to restrictive
1902 // file ownership/permissions).
1903 FcChar8* path;
1904 if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
1905 continue;
1907 if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
1908 continue;
1911 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
1912 // Skip any fonts that will be blocked by the content-process sandbox
1913 // policy.
1914 if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
1915 SandboxBroker::Perms::MAY_READ)) {
1916 continue;
1918 #endif
1920 // Clone the pattern, because we can't operate on the one belonging to
1921 // the FcFontSet directly.
1922 FcPattern* clone = FcPatternDuplicate(pattern);
1924 // Pick up any configuration options applicable to the font (e.g. custom
1925 // fontfeatures settings).
1926 if (!FcConfigSubstitute(nullptr, clone, FcMatchFont)) {
1927 // Out of memory?! We're probably doomed, but just skip this font.
1928 FcPatternDestroy(clone);
1929 continue;
1931 // But ignore hinting settings from FcConfigSubstitute, as we don't want
1932 // to bake them into the pattern in the font list.
1933 FcPatternDel(clone, FC_HINT_STYLE);
1934 FcPatternDel(clone, FC_HINTING);
1936 // If this is a TrueType or OpenType font, discard the FC_CHARSET object
1937 // (which may be very large), because we'll read the 'cmap' directly.
1938 // This substantially reduces the pressure on shared memory (bug 1664151)
1939 // due to the large font descriptors (serialized patterns).
1940 FcChar8* fontFormat;
1941 if (FcPatternGetString(clone, FC_FONTFORMAT, 0, &fontFormat) ==
1942 FcResultMatch &&
1943 (!FcStrCmp(fontFormat, (const FcChar8*)"TrueType") ||
1944 !FcStrCmp(fontFormat, (const FcChar8*)"CFF"))) {
1945 FcPatternDel(clone, FC_CHARSET);
1946 if (addPattern(clone, lastFamilyName, familyName, aAppFonts)) {
1947 ++count;
1949 } else {
1950 if (addPattern(clone, lastFamilyName, familyName, aAppFonts)) {
1951 ++count;
1955 FcPatternDestroy(clone);
1957 return count;
1960 #ifdef MOZ_BUNDLED_FONTS
1961 // Add bundled fonts before system fonts, to set correct visibility status
1962 // for any families that appear in both.
1963 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1964 FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
1965 addFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
1967 #endif
1969 // iterate over available fonts
1970 FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
1971 auto numBaseFamilies = addFontSetFamilies(systemFonts, policy.get(),
1972 /* aAppFonts = */ false);
1973 AssignFontVisibilityDevice();
1974 if (numBaseFamilies < 3 && sFontVisibilityDevice != Device::Linux_Unknown) {
1975 // If we found fewer than 3 known FontVisibility::Base families in the
1976 // system (ignoring app-bundled fonts), we must be dealing with a very
1977 // non-standard configuration; disable the distro-specific font
1978 // fingerprinting protection by marking all fonts as Unknown.
1979 for (auto& f : families) {
1980 f.mVisibility = FontVisibility::Unknown;
1982 // Issue a warning that we're disabling this protection.
1983 nsCOMPtr<nsIConsoleService> console(
1984 do_GetService("@mozilla.org/consoleservice;1"));
1985 if (console) {
1986 console->LogStringMessage(
1987 u"Font-fingerprinting protection disabled; not enough standard "
1988 u"distro fonts installed.");
1992 mozilla::fontlist::FontList* list = SharedFontList();
1993 list->SetFamilyNames(families);
1995 for (uint32_t i = 0; i < families.Length(); i++) {
1996 list->Families()[i].AddFaces(list, faces.Get(families[i].mKey)->Get());
2000 FontVisibility gfxFcPlatformFontList::GetVisibilityForFamily(
2001 const nsACString& aName) const {
2002 AssignFontVisibilityDevice();
2004 switch (sFontVisibilityDevice) {
2005 case Device::Linux_Ubuntu_any:
2006 case Device::Linux_Ubuntu_22:
2007 if (FamilyInList(aName, kBaseFonts_Ubuntu_22_04)) {
2008 return FontVisibility::Base;
2010 if (FamilyInList(aName, kLangFonts_Ubuntu_22_04)) {
2011 return FontVisibility::LangPack;
2013 if (sFontVisibilityDevice == Device::Linux_Ubuntu_22) {
2014 return FontVisibility::User;
2016 // For Ubuntu_any, we fall through to also check the 20_04 lists.
2017 [[fallthrough]];
2019 case Device::Linux_Ubuntu_20:
2020 if (FamilyInList(aName, kBaseFonts_Ubuntu_20_04)) {
2021 return FontVisibility::Base;
2023 if (FamilyInList(aName, kLangFonts_Ubuntu_20_04)) {
2024 return FontVisibility::LangPack;
2026 return FontVisibility::User;
2028 case Device::Linux_Fedora_any:
2029 case Device::Linux_Fedora_39:
2030 if (FamilyInList(aName, kBaseFonts_Fedora_39)) {
2031 return FontVisibility::Base;
2033 if (sFontVisibilityDevice == Device::Linux_Fedora_39) {
2034 return FontVisibility::User;
2036 // For Fedora_any, fall through to also check Fedora 38 list.
2037 [[fallthrough]];
2039 case Device::Linux_Fedora_38:
2040 if (FamilyInList(aName, kBaseFonts_Fedora_38)) {
2041 return FontVisibility::Base;
2043 return FontVisibility::User;
2045 default:
2046 // We don't know how to categorize fonts on this system
2047 return FontVisibility::Unknown;
2051 nsTArray<std::pair<const char**, uint32_t>>
2052 gfxFcPlatformFontList::GetFilteredPlatformFontLists() {
2053 AssignFontVisibilityDevice();
2055 nsTArray<std::pair<const char**, uint32_t>> fontLists;
2057 switch (sFontVisibilityDevice) {
2058 case Device::Linux_Ubuntu_any:
2059 case Device::Linux_Ubuntu_22:
2060 fontLists.AppendElement(std::make_pair(
2061 kBaseFonts_Ubuntu_22_04, ArrayLength(kBaseFonts_Ubuntu_22_04)));
2062 fontLists.AppendElement(std::make_pair(
2063 kLangFonts_Ubuntu_22_04, ArrayLength(kLangFonts_Ubuntu_22_04)));
2064 // For Ubuntu_any, we fall through to also check the 20_04 lists.
2065 [[fallthrough]];
2067 case Device::Linux_Ubuntu_20:
2068 fontLists.AppendElement(std::make_pair(
2069 kBaseFonts_Ubuntu_20_04, ArrayLength(kBaseFonts_Ubuntu_20_04)));
2070 fontLists.AppendElement(std::make_pair(
2071 kLangFonts_Ubuntu_20_04, ArrayLength(kLangFonts_Ubuntu_20_04)));
2072 break;
2074 case Device::Linux_Fedora_any:
2075 case Device::Linux_Fedora_39:
2076 fontLists.AppendElement(std::make_pair(
2077 kBaseFonts_Fedora_39, ArrayLength(kBaseFonts_Fedora_39)));
2078 // For Fedora_any, fall through to also check Fedora 38 list.
2079 [[fallthrough]];
2081 case Device::Linux_Fedora_38:
2082 fontLists.AppendElement(std::make_pair(
2083 kBaseFonts_Fedora_38, ArrayLength(kBaseFonts_Fedora_38)));
2084 break;
2086 default:
2087 // We don't know how to categorize fonts on this system
2088 break;
2091 return fontLists;
2094 gfxFontEntry* gfxFcPlatformFontList::CreateFontEntry(
2095 fontlist::Face* aFace, const fontlist::Family* aFamily) {
2096 nsAutoCString desc(aFace->mDescriptor.AsString(SharedFontList()));
2097 FcPattern* pattern = FcNameParse((const FcChar8*)desc.get());
2098 auto* fe = new gfxFontconfigFontEntry(desc, pattern, true);
2099 FcPatternDestroy(pattern);
2100 fe->InitializeFrom(aFace, aFamily);
2101 return fe;
2104 // For displaying the fontlist in UI, use explicit call to FcFontList. Using
2105 // FcFontList results in the list containing the localized names as dictated
2106 // by system defaults.
2107 static void GetSystemFontList(nsTArray<nsString>& aListOfFonts,
2108 nsAtom* aLangGroup) {
2109 aListOfFonts.Clear();
2111 RefPtr<FcPattern> pat = dont_AddRef(FcPatternCreate());
2112 if (!pat) {
2113 return;
2116 UniquePtr<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
2117 if (!os) {
2118 return;
2121 // add the lang to the pattern
2122 nsAutoCString fcLang;
2123 gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
2124 pfl->GetSampleLangForGroup(aLangGroup, fcLang,
2125 /*aForFontEnumerationThread*/ true);
2126 if (!fcLang.IsEmpty()) {
2127 FcPatternAddString(pat, FC_LANG, ToFcChar8Ptr(fcLang.get()));
2130 UniquePtr<FcFontSet> fs(FcFontList(nullptr, pat, os.get()));
2131 if (!fs) {
2132 return;
2135 for (int i = 0; i < fs->nfont; i++) {
2136 char* family;
2138 if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, (FcChar8**)&family) !=
2139 FcResultMatch) {
2140 continue;
2143 // Remove duplicates...
2144 nsAutoString strFamily;
2145 AppendUTF8toUTF16(MakeStringSpan(family), strFamily);
2146 if (aListOfFonts.Contains(strFamily)) {
2147 continue;
2150 aListOfFonts.AppendElement(strFamily);
2153 aListOfFonts.Sort();
2156 void gfxFcPlatformFontList::GetFontList(nsAtom* aLangGroup,
2157 const nsACString& aGenericFamily,
2158 nsTArray<nsString>& aListOfFonts) {
2159 // Get the list of font family names using fontconfig
2160 GetSystemFontList(aListOfFonts, aLangGroup);
2162 // Under Linux, the generics "serif", "sans-serif" and "monospace"
2163 // are included in the pref fontlist. These map to whatever fontconfig
2164 // decides they should be for a given language, rather than one of the
2165 // fonts listed in the prefs font lists (e.g. font.name.*, font.name-list.*)
2166 bool serif = false, sansSerif = false, monospace = false;
2167 if (aGenericFamily.IsEmpty())
2168 serif = sansSerif = monospace = true;
2169 else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
2170 serif = true;
2171 else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
2172 sansSerif = true;
2173 else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
2174 monospace = true;
2175 else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
2176 aGenericFamily.LowerCaseEqualsLiteral("fantasy"))
2177 serif = sansSerif = true;
2178 else
2179 MOZ_ASSERT_UNREACHABLE("unexpected CSS generic font family");
2181 // The first in the list becomes the default in
2182 // FontBuilder.readFontSelection() if the preference-selected font is not
2183 // available, so put system configured defaults first.
2184 if (monospace) aListOfFonts.InsertElementAt(0, u"monospace"_ns);
2185 if (sansSerif) aListOfFonts.InsertElementAt(0, u"sans-serif"_ns);
2186 if (serif) aListOfFonts.InsertElementAt(0, u"serif"_ns);
2189 FontFamily gfxFcPlatformFontList::GetDefaultFontForPlatform(
2190 nsPresContext* aPresContext, const gfxFontStyle* aStyle,
2191 nsAtom* aLanguage) {
2192 // Get the default font by using a fake name to retrieve the first
2193 // scalable font that fontconfig suggests for the given language.
2194 PrefFontList* prefFonts =
2195 FindGenericFamilies(aPresContext, "-moz-default"_ns,
2196 aLanguage ? aLanguage : nsGkAtoms::x_western);
2197 NS_ASSERTION(prefFonts, "null list of generic fonts");
2198 if (prefFonts && !prefFonts->IsEmpty()) {
2199 return (*prefFonts)[0];
2201 return FontFamily();
2204 gfxFontEntry* gfxFcPlatformFontList::LookupLocalFont(
2205 nsPresContext* aPresContext, const nsACString& aFontName,
2206 WeightRange aWeightForEntry, StretchRange aStretchForEntry,
2207 SlantStyleRange aStyleForEntry) {
2208 AutoLock lock(mLock);
2210 nsAutoCString keyName(aFontName);
2211 ToLowerCase(keyName);
2213 if (SharedFontList()) {
2214 return LookupInSharedFaceNameList(aPresContext, aFontName, aWeightForEntry,
2215 aStretchForEntry, aStyleForEntry);
2218 // if name is not in the global list, done
2219 const auto fontPattern = mLocalNames.Lookup(keyName);
2220 if (!fontPattern) {
2221 return nullptr;
2224 return new gfxFontconfigFontEntry(aFontName, *fontPattern, aWeightForEntry,
2225 aStretchForEntry, aStyleForEntry);
2228 gfxFontEntry* gfxFcPlatformFontList::MakePlatformFont(
2229 const nsACString& aFontName, WeightRange aWeightForEntry,
2230 StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry,
2231 const uint8_t* aFontData, uint32_t aLength) {
2232 RefPtr<FTUserFontData> ufd = new FTUserFontData(aFontData, aLength);
2233 RefPtr<SharedFTFace> face = ufd->CloneFace();
2234 if (!face) {
2235 return nullptr;
2237 return new gfxFontconfigFontEntry(aFontName, aWeightForEntry,
2238 aStretchForEntry, aStyleForEntry,
2239 std::move(face));
2242 static bool UseCustomFontconfigLookupsForLocale(const Locale& aLocale) {
2243 return aLocale.Script().EqualTo("Hans") || aLocale.Script().EqualTo("Hant") ||
2244 aLocale.Script().EqualTo("Jpan") || aLocale.Script().EqualTo("Kore") ||
2245 aLocale.Script().EqualTo("Arab");
2248 bool gfxFcPlatformFontList::FindAndAddFamiliesLocked(
2249 nsPresContext* aPresContext, StyleGenericFontFamily aGeneric,
2250 const nsACString& aFamily, nsTArray<FamilyAndGeneric>* aOutput,
2251 FindFamiliesFlags aFlags, gfxFontStyle* aStyle, nsAtom* aLanguage,
2252 gfxFloat aDevToCssSize) {
2253 nsAutoCString familyName(aFamily);
2254 ToLowerCase(familyName);
2256 if (!(aFlags & FindFamiliesFlags::eQuotedFamilyName)) {
2257 // deprecated generic names are explicitly converted to standard generics
2258 bool isDeprecatedGeneric = false;
2259 if (familyName.EqualsLiteral("sans") ||
2260 familyName.EqualsLiteral("sans serif")) {
2261 familyName.AssignLiteral("sans-serif");
2262 isDeprecatedGeneric = true;
2263 } else if (familyName.EqualsLiteral("mono")) {
2264 familyName.AssignLiteral("monospace");
2265 isDeprecatedGeneric = true;
2268 // fontconfig generics? use fontconfig to determine the family for lang
2269 if (isDeprecatedGeneric ||
2270 mozilla::StyleSingleFontFamily::Parse(familyName).IsGeneric()) {
2271 PrefFontList* prefFonts =
2272 FindGenericFamilies(aPresContext, familyName, aLanguage);
2273 if (prefFonts && !prefFonts->IsEmpty()) {
2274 aOutput->AppendElements(*prefFonts);
2275 return true;
2277 return false;
2281 // fontconfig allows conditional substitutions in such a way that it's
2282 // difficult to distinguish an explicit substitution from other suggested
2283 // choices. To sniff out explicit substitutions, compare the substitutions
2284 // for "font, -moz-sentinel" to "-moz-sentinel" to sniff out the
2285 // substitutions
2287 // Example:
2289 // serif ==> DejaVu Serif, ...
2290 // Helvetica, serif ==> Helvetica, TeX Gyre Heros, Nimbus Sans L, DejaVu
2291 // Serif
2293 // In this case fontconfig is including Tex Gyre Heros and
2294 // Nimbus Sans L as alternatives for Helvetica.
2296 // Because the FcConfigSubstitute call is quite expensive, we cache the
2297 // actual font families found via this process.
2298 nsAutoCString cacheKey;
2300 // For languages that use CJK or Arabic script, we include the language as
2301 // part of the cache key because fontconfig may have lang-specific rules that
2302 // specify different substitutions. (In theory, this could apply to *any*
2303 // language, but it's highly unlikely to matter for non-CJK/Arabic scripts,
2304 // and it gets really expensive to do separate lookups for 300+ distinct lang
2305 // tags e.g. on wikipedia.org, when they all end up mapping to the same font
2306 // list.)
2307 // We remember the most recently checked aLanguage atom so that when the same
2308 // language is used in many successive calls, we can avoid repeating the
2309 // locale code processing every time.
2310 if (aLanguage != mPrevLanguage) {
2311 GetSampleLangForGroup(aLanguage, mSampleLang);
2312 ToLowerCase(mSampleLang);
2313 Locale locale;
2314 mUseCustomLookups = LocaleParser::TryParse(mSampleLang, locale).isOk() &&
2315 locale.AddLikelySubtags().isOk() &&
2316 UseCustomFontconfigLookupsForLocale(locale);
2317 mPrevLanguage = aLanguage;
2319 if (mUseCustomLookups) {
2320 cacheKey = mSampleLang;
2321 cacheKey.Append(':');
2324 cacheKey.Append(familyName);
2325 auto vis =
2326 aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
2327 cacheKey.Append(':');
2328 cacheKey.AppendInt(int(vis));
2329 if (const auto& cached = mFcSubstituteCache.Lookup(cacheKey)) {
2330 if (cached->IsEmpty()) {
2331 return false;
2333 aOutput->AppendElements(*cached);
2334 return true;
2337 // It wasn't in the cache, so we need to ask fontconfig...
2338 const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel");
2339 const FcChar8* terminator = nullptr;
2340 RefPtr<FcPattern> sentinelSubst = dont_AddRef(FcPatternCreate());
2341 FcPatternAddString(sentinelSubst, FC_FAMILY, kSentinelName);
2342 if (!mSampleLang.IsEmpty()) {
2343 FcPatternAddString(sentinelSubst, FC_LANG, ToFcChar8Ptr(mSampleLang.get()));
2345 FcConfigSubstitute(nullptr, sentinelSubst, FcMatchPattern);
2347 // If the sentinel name is still present, we'll use that as the terminator
2348 // for the family names we collect; this means that if fontconfig prepends
2349 // additional family names (e.g. an emoji font, or lang-specific preferred
2350 // font) to all patterns, it won't simply mask all actual requested names.
2351 // If the sentinel has been deleted/replaced altogether, then we'll take
2352 // the first substitute name as the new terminator.
2353 FcChar8* substName;
2354 for (int i = 0; FcPatternGetString(sentinelSubst, FC_FAMILY, i, &substName) ==
2355 FcResultMatch;
2356 i++) {
2357 if (FcStrCmp(substName, kSentinelName) == 0) {
2358 terminator = kSentinelName;
2359 break;
2361 if (!terminator) {
2362 terminator = substName;
2366 // substitutions for font, -moz-sentinel pattern
2367 RefPtr<FcPattern> fontWithSentinel = dont_AddRef(FcPatternCreate());
2368 FcPatternAddString(fontWithSentinel, FC_FAMILY,
2369 ToFcChar8Ptr(familyName.get()));
2370 FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName);
2371 if (!mSampleLang.IsEmpty()) {
2372 FcPatternAddString(sentinelSubst, FC_LANG, ToFcChar8Ptr(mSampleLang.get()));
2374 FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern);
2376 // Add all font family matches until reaching the terminator.
2377 AutoTArray<FamilyAndGeneric, 10> cachedFamilies;
2378 for (int i = 0; FcPatternGetString(fontWithSentinel, FC_FAMILY, i,
2379 &substName) == FcResultMatch;
2380 i++) {
2381 if (terminator && FcStrCmp(substName, terminator) == 0) {
2382 break;
2384 gfxPlatformFontList::FindAndAddFamiliesLocked(
2385 aPresContext, aGeneric, nsDependentCString(ToCharPtr(substName)),
2386 &cachedFamilies, aFlags, aStyle, aLanguage);
2389 const auto& insertedCachedFamilies =
2390 mFcSubstituteCache.InsertOrUpdate(cacheKey, std::move(cachedFamilies));
2392 if (insertedCachedFamilies.IsEmpty()) {
2393 return false;
2395 aOutput->AppendElements(insertedCachedFamilies);
2396 return true;
2399 bool gfxFcPlatformFontList::GetStandardFamilyName(const nsCString& aFontName,
2400 nsACString& aFamilyName) {
2401 aFamilyName.Truncate();
2403 // The fontconfig list of fonts includes generic family names in the
2404 // font list. For these, just use the generic name.
2405 if (aFontName.EqualsLiteral("serif") ||
2406 aFontName.EqualsLiteral("sans-serif") ||
2407 aFontName.EqualsLiteral("monospace")) {
2408 aFamilyName.Assign(aFontName);
2409 return true;
2412 RefPtr<FcPattern> pat = dont_AddRef(FcPatternCreate());
2413 if (!pat) {
2414 return true;
2417 UniquePtr<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
2418 if (!os) {
2419 return true;
2422 // add the family name to the pattern
2423 FcPatternAddString(pat, FC_FAMILY, ToFcChar8Ptr(aFontName.get()));
2425 UniquePtr<FcFontSet> givenFS(FcFontList(nullptr, pat, os.get()));
2426 if (!givenFS) {
2427 return true;
2430 // See if there is a font face with first family equal to the given family
2431 // (needs to be in sync with names coming from GetFontList())
2432 nsTArray<nsCString> candidates;
2433 for (int i = 0; i < givenFS->nfont; i++) {
2434 char* firstFamily;
2436 if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
2437 (FcChar8**)&firstFamily) != FcResultMatch) {
2438 continue;
2441 nsDependentCString first(firstFamily);
2442 if (!candidates.Contains(first)) {
2443 candidates.AppendElement(first);
2445 if (aFontName.Equals(first)) {
2446 aFamilyName.Assign(aFontName);
2447 return true;
2452 // Because fontconfig conflates different family name types, need to
2453 // double check that the candidate name is not simply a different
2454 // name type. For example, if a font with nameID=16 "Minion Pro" and
2455 // nameID=21 "Minion Pro Caption" exists, calling FcFontList with
2456 // family="Minion Pro" will return a set of patterns some of which
2457 // will have a first family of "Minion Pro Caption". Ignore these
2458 // patterns and use the first candidate that maps to a font set with
2459 // the same number of faces and an identical set of patterns.
2460 for (uint32_t j = 0; j < candidates.Length(); ++j) {
2461 FcPatternDel(pat, FC_FAMILY);
2462 FcPatternAddString(pat, FC_FAMILY, (FcChar8*)candidates[j].get());
2464 UniquePtr<FcFontSet> candidateFS(FcFontList(nullptr, pat, os.get()));
2465 if (!candidateFS) {
2466 return true;
2469 if (candidateFS->nfont != givenFS->nfont) {
2470 continue;
2473 bool equal = true;
2474 for (int i = 0; i < givenFS->nfont; ++i) {
2475 if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
2476 equal = false;
2477 break;
2480 if (equal) {
2481 aFamilyName = candidates[j];
2482 return true;
2486 // didn't find localized name, leave family name blank
2487 return true;
2490 void gfxFcPlatformFontList::AddGenericFonts(
2491 nsPresContext* aPresContext, StyleGenericFontFamily aGenericType,
2492 nsAtom* aLanguage, nsTArray<FamilyAndGeneric>& aFamilyList) {
2493 const char* generic = GetGenericName(aGenericType);
2494 NS_ASSERTION(generic, "weird generic font type");
2495 if (!generic) {
2496 return;
2499 // By default, most font prefs on Linux map to "use fontconfig"
2500 // keywords. So only need to explicitly lookup font pref if
2501 // non-default settings exist, or if we are the system-ui font, which we deal
2502 // with in the base class.
2503 const bool isSystemUi = aGenericType == StyleGenericFontFamily::SystemUi;
2504 bool usePrefFontList = isSystemUi;
2506 nsAutoCString genericToLookup(generic);
2507 if ((!mAlwaysUseFontconfigGenerics && aLanguage) ||
2508 aLanguage == nsGkAtoms::x_math) {
2509 nsAtom* langGroup = GetLangGroup(aLanguage);
2510 nsAutoCString fontlistValue;
2511 mFontPrefs->LookupName(PrefName(generic, langGroup), fontlistValue);
2512 if (fontlistValue.IsEmpty()) {
2513 // The font name list may have two or more family names as comma
2514 // separated list. In such case, not matching with generic font
2515 // name is fine because if the list prefers specific font, we
2516 // should try to use the pref with complicated path.
2517 mFontPrefs->LookupNameList(PrefName(generic, langGroup), fontlistValue);
2519 if (!fontlistValue.IsEmpty()) {
2520 if (!fontlistValue.EqualsLiteral("serif") &&
2521 !fontlistValue.EqualsLiteral("sans-serif") &&
2522 !fontlistValue.EqualsLiteral("monospace")) {
2523 usePrefFontList = true;
2524 } else {
2525 // serif, sans-serif or monospace was specified
2526 genericToLookup = fontlistValue;
2531 // when pref fonts exist, use standard pref font lookup
2532 if (usePrefFontList) {
2533 gfxPlatformFontList::AddGenericFonts(aPresContext, aGenericType, aLanguage,
2534 aFamilyList);
2535 if (!isSystemUi) {
2536 return;
2540 AutoLock lock(mLock);
2541 PrefFontList* prefFonts =
2542 FindGenericFamilies(aPresContext, genericToLookup, aLanguage);
2543 NS_ASSERTION(prefFonts, "null generic font list");
2544 aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length());
2545 for (auto& f : *prefFonts) {
2546 aFamilyList.AppendElement(FamilyAndGeneric(f, aGenericType));
2550 void gfxFcPlatformFontList::ClearLangGroupPrefFontsLocked() {
2551 ClearGenericMappingsLocked();
2552 gfxPlatformFontList::ClearLangGroupPrefFontsLocked();
2553 mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
2556 gfxPlatformFontList::PrefFontList* gfxFcPlatformFontList::FindGenericFamilies(
2557 nsPresContext* aPresContext, const nsCString& aGeneric, nsAtom* aLanguage) {
2558 // set up name
2559 nsAutoCString fcLang;
2560 GetSampleLangForGroup(aLanguage, fcLang);
2561 ToLowerCase(fcLang);
2563 nsAutoCString cacheKey(aGeneric);
2564 if (fcLang.Length() > 0) {
2565 cacheKey.Append('-');
2566 // If the script is CJK or Arabic, we cache by lang so that different fonts
2567 // various locales can be supported; but otherwise, we cache by script
2568 // subtag, to avoid a proliferation of entries for Western & similar
2569 // languages.
2570 // In theory, this means we could fail to respect custom fontconfig rules
2571 // for individual (non-CJK/Arab) languages that share the same script, but
2572 // such setups are probably vanishingly rare.
2573 Locale locale;
2574 if (LocaleParser::TryParse(fcLang, locale).isOk() &&
2575 locale.AddLikelySubtags().isOk()) {
2576 if (UseCustomFontconfigLookupsForLocale(locale)) {
2577 cacheKey.Append(fcLang);
2578 } else {
2579 cacheKey.Append(locale.Script().Span());
2581 } else {
2582 cacheKey.Append(fcLang);
2586 // try to get the family from the cache
2587 return mGenericMappings.WithEntryHandle(
2588 cacheKey, [&](auto&& entry) -> PrefFontList* {
2589 if (!entry) {
2590 // if not found, ask fontconfig to pick the appropriate font
2591 RefPtr<FcPattern> genericPattern = dont_AddRef(FcPatternCreate());
2592 FcPatternAddString(genericPattern, FC_FAMILY,
2593 ToFcChar8Ptr(aGeneric.get()));
2595 // -- prefer scalable fonts
2596 FcPatternAddBool(genericPattern, FC_SCALABLE, FcTrue);
2598 // -- add the lang to the pattern
2599 if (!fcLang.IsEmpty()) {
2600 FcPatternAddString(genericPattern, FC_LANG,
2601 ToFcChar8Ptr(fcLang.get()));
2604 // -- perform substitutions
2605 FcConfigSubstitute(nullptr, genericPattern, FcMatchPattern);
2606 FcDefaultSubstitute(genericPattern);
2608 // -- sort to get the closest matches
2609 FcResult result;
2610 UniquePtr<FcFontSet> faces(
2611 FcFontSort(nullptr, genericPattern, FcFalse, nullptr, &result));
2613 if (!faces) {
2614 return nullptr;
2617 // -- select the fonts to be used for the generic
2618 auto prefFonts = MakeUnique<PrefFontList>(); // can be empty but in
2619 // practice won't happen
2620 uint32_t limit = StaticPrefs::
2621 gfx_font_rendering_fontconfig_max_generic_substitutions();
2622 bool foundFontWithLang = false;
2623 for (int i = 0; i < faces->nfont; i++) {
2624 FcPattern* font = faces->fonts[i];
2625 FcChar8* mappedGeneric = nullptr;
2627 FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric);
2628 if (mappedGeneric) {
2629 mLock.AssertCurrentThreadIn();
2630 nsAutoCString mappedGenericName(ToCharPtr(mappedGeneric));
2631 AutoTArray<FamilyAndGeneric, 1> genericFamilies;
2632 if (gfxPlatformFontList::FindAndAddFamiliesLocked(
2633 aPresContext, StyleGenericFontFamily::None,
2634 mappedGenericName, &genericFamilies,
2635 FindFamiliesFlags(0))) {
2636 MOZ_ASSERT(genericFamilies.Length() == 1,
2637 "expected a single family");
2638 if (!prefFonts->Contains(genericFamilies[0].mFamily)) {
2639 prefFonts->AppendElement(genericFamilies[0].mFamily);
2640 bool foundLang =
2641 !fcLang.IsEmpty() &&
2642 PatternHasLang(font, ToFcChar8Ptr(fcLang.get()));
2643 foundFontWithLang = foundFontWithLang || foundLang;
2644 // check to see if the list is full
2645 if (prefFonts->Length() >= limit) {
2646 break;
2653 // if no font in the list matches the lang, trim all but the first one
2654 if (!prefFonts->IsEmpty() && !foundFontWithLang) {
2655 prefFonts->TruncateLength(1);
2658 entry.Insert(std::move(prefFonts));
2660 return entry->get();
2664 bool gfxFcPlatformFontList::PrefFontListsUseOnlyGenerics() {
2665 for (auto iter = mFontPrefs->NameIter(); !iter.Done(); iter.Next()) {
2666 // Check whether all font.name prefs map to generic keywords
2667 // and that the pref name and keyword match.
2668 // Ex: font.name.serif.ar ==> "serif" (ok)
2669 // Ex: font.name.serif.ar ==> "monospace" (return false)
2670 // Ex: font.name.serif.ar ==> "DejaVu Serif" (return false)
2671 // Ex: font.name.serif.ar ==> "" and
2672 // font.name-list.serif.ar ==> "serif" (ok)
2673 // Ex: font.name.serif.ar ==> "" and
2674 // font.name-list.serif.ar ==> "Something, serif"
2675 // (return false)
2676 const nsACString* prefValue = &iter.Data();
2677 nsAutoCString listValue;
2678 if (iter.Data().IsEmpty()) {
2679 // The font name list may have two or more family names as comma
2680 // separated list. In such case, not matching with generic font
2681 // name is fine because if the list prefers specific font, this
2682 // should return false.
2683 mFontPrefs->LookupNameList(iter.Key(), listValue);
2684 prefValue = &listValue;
2687 nsCCharSeparatedTokenizer tokenizer(iter.Key(), '.');
2688 const nsDependentCSubstring& generic = tokenizer.nextToken();
2689 const nsDependentCSubstring& langGroup = tokenizer.nextToken();
2691 if (!langGroup.EqualsLiteral("x-math") && !generic.Equals(*prefValue)) {
2692 return false;
2695 return true;
2698 /* static */
2699 void gfxFcPlatformFontList::CheckFontUpdates(nsITimer* aTimer, void* aThis) {
2700 // A content process is not supposed to check this directly;
2701 // it will be notified by the parent when the font list changes.
2702 MOZ_ASSERT(XRE_IsParentProcess());
2704 // check for font updates
2705 FcInitBringUptoDate();
2707 // update fontlist if current config changed
2708 gfxFcPlatformFontList* pfl = static_cast<gfxFcPlatformFontList*>(aThis);
2709 FcConfig* current = FcConfigGetCurrent();
2710 if (current != pfl->GetLastConfig()) {
2711 pfl->UpdateFontList();
2713 gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::Yes);
2714 mozilla::dom::ContentParent::NotifyUpdatedFonts(true);
2718 gfxFontFamily* gfxFcPlatformFontList::CreateFontFamily(
2719 const nsACString& aName, FontVisibility aVisibility) const {
2720 return new gfxFontconfigFontFamily(aName, aVisibility);
2723 // mapping of moz lang groups ==> default lang
2724 struct MozLangGroupData {
2725 nsAtom* const& mozLangGroup;
2726 const char* defaultLang;
2729 const MozLangGroupData MozLangGroups[] = {
2730 {nsGkAtoms::x_western, "en"}, {nsGkAtoms::x_cyrillic, "ru"},
2731 {nsGkAtoms::x_devanagari, "hi"}, {nsGkAtoms::x_tamil, "ta"},
2732 {nsGkAtoms::x_armn, "hy"}, {nsGkAtoms::x_beng, "bn"},
2733 {nsGkAtoms::x_cans, "iu"}, {nsGkAtoms::x_ethi, "am"},
2734 {nsGkAtoms::x_geor, "ka"}, {nsGkAtoms::x_gujr, "gu"},
2735 {nsGkAtoms::x_guru, "pa"}, {nsGkAtoms::x_khmr, "km"},
2736 {nsGkAtoms::x_knda, "kn"}, {nsGkAtoms::x_mlym, "ml"},
2737 {nsGkAtoms::x_orya, "or"}, {nsGkAtoms::x_sinh, "si"},
2738 {nsGkAtoms::x_tamil, "ta"}, {nsGkAtoms::x_telu, "te"},
2739 {nsGkAtoms::x_tibt, "bo"}, {nsGkAtoms::Unicode, 0}};
2741 bool gfxFcPlatformFontList::TryLangForGroup(const nsACString& aOSLang,
2742 nsAtom* aLangGroup,
2743 nsACString& aFcLang,
2744 bool aForFontEnumerationThread) {
2745 // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
2746 // aOSLang is in the form "language[_territory][.codeset][@modifier]".
2747 // fontconfig takes languages in the form "language-territory".
2748 // nsLanguageAtomService takes languages in the form language-subtag,
2749 // where subtag may be a territory. fontconfig and nsLanguageAtomService
2750 // handle case-conversion for us.
2751 const char *pos, *end;
2752 aOSLang.BeginReading(pos);
2753 aOSLang.EndReading(end);
2754 aFcLang.Truncate();
2755 while (pos < end) {
2756 switch (*pos) {
2757 case '.':
2758 case '@':
2759 end = pos;
2760 break;
2761 case '_':
2762 aFcLang.Append('-');
2763 break;
2764 default:
2765 aFcLang.Append(*pos);
2767 ++pos;
2770 if (!aForFontEnumerationThread) {
2771 nsAtom* atom = mLangService->LookupLanguage(aFcLang);
2772 return atom == aLangGroup;
2775 // If we were called by the font enumeration thread, we can't use
2776 // mLangService->LookupLanguage because it is not thread-safe.
2777 // Use GetUncachedLanguageGroup to avoid unsafe access to the lang-group
2778 // mapping cache hashtable.
2779 nsAutoCString lowered(aFcLang);
2780 ToLowerCase(lowered);
2781 RefPtr<nsAtom> lang = NS_Atomize(lowered);
2782 RefPtr<nsAtom> group = mLangService->GetUncachedLanguageGroup(lang);
2783 return group.get() == aLangGroup;
2786 void gfxFcPlatformFontList::GetSampleLangForGroup(
2787 nsAtom* aLanguage, nsACString& aLangStr, bool aForFontEnumerationThread) {
2788 aLangStr.Truncate();
2789 if (!aLanguage) {
2790 return;
2793 // set up lang string
2794 const MozLangGroupData* mozLangGroup = nullptr;
2796 // -- look it up in the list of moz lang groups
2797 for (unsigned int i = 0; i < ArrayLength(MozLangGroups); ++i) {
2798 if (aLanguage == MozLangGroups[i].mozLangGroup) {
2799 mozLangGroup = &MozLangGroups[i];
2800 break;
2804 // -- not a mozilla lang group? Just return the BCP47 string
2805 // representation of the lang group
2806 if (!mozLangGroup) {
2807 // Not a special mozilla language group.
2808 // Use aLanguage as a language code.
2809 aLanguage->ToUTF8String(aLangStr);
2810 return;
2813 // -- check the environment for the user's preferred language that
2814 // corresponds to this mozilla lang group.
2815 const char* languages = getenv("LANGUAGE");
2816 if (languages) {
2817 const char separator = ':';
2819 for (const char* pos = languages; true; ++pos) {
2820 if (*pos == '\0' || *pos == separator) {
2821 if (languages < pos &&
2822 TryLangForGroup(Substring(languages, pos), aLanguage, aLangStr,
2823 aForFontEnumerationThread)) {
2824 return;
2827 if (*pos == '\0') {
2828 break;
2831 languages = pos + 1;
2835 const char* ctype = setlocale(LC_CTYPE, nullptr);
2836 if (ctype && TryLangForGroup(nsDependentCString(ctype), aLanguage, aLangStr,
2837 aForFontEnumerationThread)) {
2838 return;
2841 if (mozLangGroup->defaultLang) {
2842 aLangStr.Assign(mozLangGroup->defaultLang);
2843 } else {
2844 aLangStr.Truncate();
2848 #ifdef MOZ_BUNDLED_FONTS
2849 void gfxFcPlatformFontList::ActivateBundledFonts() {
2850 if (!mBundledFontsInitialized) {
2851 mBundledFontsInitialized = true;
2852 nsCOMPtr<nsIFile> localDir;
2853 nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
2854 if (NS_FAILED(rv)) {
2855 return;
2857 if (NS_FAILED(localDir->Append(u"fonts"_ns))) {
2858 return;
2860 bool isDir;
2861 if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
2862 return;
2864 if (NS_FAILED(localDir->GetNativePath(mBundledFontsPath))) {
2865 return;
2868 if (!mBundledFontsPath.IsEmpty()) {
2869 FcConfigAppFontAddDir(nullptr, ToFcChar8Ptr(mBundledFontsPath.get()));
2872 #endif
2874 #ifdef MOZ_WIDGET_GTK
2875 /***************************************************************************
2877 * These functions must be last in the file because it uses the system cairo
2878 * library. Above this point the cairo library used is the tree cairo.
2881 // Tree cairo symbols have different names. Disable their activation through
2882 // preprocessor macros.
2883 # undef cairo_ft_font_options_substitute
2885 # undef cairo_font_options_create
2886 # undef cairo_font_options_destroy
2887 # undef cairo_font_options_copy
2888 # undef cairo_font_options_equal
2890 # undef cairo_font_options_get_antialias
2891 # undef cairo_font_options_set_antialias
2892 # undef cairo_font_options_get_hint_style
2893 # undef cairo_font_options_set_hint_style
2894 # undef cairo_font_options_get_lcd_filter
2895 # undef cairo_font_options_set_lcd_filter
2896 # undef cairo_font_options_get_subpixel_order
2897 # undef cairo_font_options_set_subpixel_order
2899 // The system cairo functions are not declared because the include paths cause
2900 // the gdk headers to pick up the tree cairo.h.
2901 extern "C" {
2902 NS_VISIBILITY_DEFAULT void cairo_ft_font_options_substitute(
2903 const cairo_font_options_t* options, FcPattern* pattern);
2905 NS_VISIBILITY_DEFAULT cairo_font_options_t* cairo_font_options_copy(
2906 const cairo_font_options_t*);
2907 NS_VISIBILITY_DEFAULT cairo_font_options_t* cairo_font_options_create();
2908 NS_VISIBILITY_DEFAULT void cairo_font_options_destroy(cairo_font_options_t*);
2909 NS_VISIBILITY_DEFAULT cairo_bool_t cairo_font_options_equal(
2910 const cairo_font_options_t*, const cairo_font_options_t*);
2912 NS_VISIBILITY_DEFAULT cairo_antialias_t
2913 cairo_font_options_get_antialias(const cairo_font_options_t*);
2914 NS_VISIBILITY_DEFAULT void cairo_font_options_set_antialias(
2915 cairo_font_options_t*, cairo_antialias_t);
2916 NS_VISIBILITY_DEFAULT cairo_hint_style_t
2917 cairo_font_options_get_hint_style(const cairo_font_options_t*);
2918 NS_VISIBILITY_DEFAULT void cairo_font_options_set_hint_style(
2919 cairo_font_options_t*, cairo_hint_style_t);
2920 NS_VISIBILITY_DEFAULT cairo_subpixel_order_t
2921 cairo_font_options_get_subpixel_order(const cairo_font_options_t*);
2922 NS_VISIBILITY_DEFAULT void cairo_font_options_set_subpixel_order(
2923 cairo_font_options_t*, cairo_subpixel_order_t);
2926 void gfxFcPlatformFontList::ClearSystemFontOptions() {
2927 if (mSystemFontOptions) {
2928 cairo_font_options_destroy(mSystemFontOptions);
2929 mSystemFontOptions = nullptr;
2933 bool gfxFcPlatformFontList::UpdateSystemFontOptions() {
2934 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
2936 if (gfxPlatform::IsHeadless()) {
2937 return false;
2940 # ifdef MOZ_X11
2942 // This one shouldn't change during the X session.
2943 int lcdfilter;
2944 GdkDisplay* dpy = gdk_display_get_default();
2945 if (mozilla::widget::GdkIsX11Display(dpy) &&
2946 GetXftInt(GDK_DISPLAY_XDISPLAY(dpy), "lcdfilter", &lcdfilter)) {
2947 mFreetypeLcdSetting = lcdfilter;
2950 # endif // MOZ_X11
2952 const cairo_font_options_t* options =
2953 gdk_screen_get_font_options(gdk_screen_get_default());
2954 if (!options) {
2955 bool changed = !!mSystemFontOptions;
2956 ClearSystemFontOptions();
2957 return changed;
2960 cairo_font_options_t* newOptions = cairo_font_options_copy(options);
2962 if (mSystemFontOptions &&
2963 cairo_font_options_equal(mSystemFontOptions, options)) {
2964 cairo_font_options_destroy(newOptions);
2965 return false;
2968 ClearSystemFontOptions();
2969 mSystemFontOptions = newOptions;
2970 return true;
2973 void gfxFcPlatformFontList::SystemFontOptionsToIpc(
2974 dom::SystemFontOptions& aOptions) {
2975 aOptions.antialias() =
2976 mSystemFontOptions ? cairo_font_options_get_antialias(mSystemFontOptions)
2977 : CAIRO_ANTIALIAS_DEFAULT;
2978 aOptions.subpixelOrder() =
2979 mSystemFontOptions
2980 ? cairo_font_options_get_subpixel_order(mSystemFontOptions)
2981 : CAIRO_SUBPIXEL_ORDER_DEFAULT;
2982 aOptions.hintStyle() =
2983 mSystemFontOptions ? cairo_font_options_get_hint_style(mSystemFontOptions)
2984 : CAIRO_HINT_STYLE_DEFAULT;
2985 aOptions.lcdFilter() = mFreetypeLcdSetting;
2988 void gfxFcPlatformFontList::UpdateSystemFontOptionsFromIpc(
2989 const dom::SystemFontOptions& aOptions) {
2990 ClearSystemFontOptions();
2991 mSystemFontOptions = cairo_font_options_create();
2992 cairo_font_options_set_antialias(mSystemFontOptions,
2993 cairo_antialias_t(aOptions.antialias()));
2994 cairo_font_options_set_hint_style(mSystemFontOptions,
2995 cairo_hint_style_t(aOptions.hintStyle()));
2996 cairo_font_options_set_subpixel_order(
2997 mSystemFontOptions, cairo_subpixel_order_t(aOptions.subpixelOrder()));
2998 mFreetypeLcdSetting = aOptions.lcdFilter();
3001 void gfxFcPlatformFontList::SubstituteSystemFontOptions(FcPattern* aPattern) {
3002 if (mSystemFontOptions) {
3003 cairo_ft_font_options_substitute(mSystemFontOptions, aPattern);
3006 if (mFreetypeLcdSetting != -1) {
3007 FcValue value;
3008 if (FcPatternGet(aPattern, FC_LCD_FILTER, 0, &value) == FcResultNoMatch) {
3009 FcPatternAddInteger(aPattern, FC_LCD_FILTER, mFreetypeLcdSetting);
3014 #endif // MOZ_WIDGET_GTK