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"
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"
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"
41 #include <fontconfig/fcfreetype.h>
42 #include <fontconfig/fontconfig.h>
43 #include <harfbuzz/hb.h>
50 # include "gfxPlatformGtk.h"
51 # include "mozilla/WidgetUtilsGtk.h"
55 # include "mozilla/X11Util.h"
58 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
59 # include "mozilla/SandboxBrokerPolicyFactory.h"
60 # include "mozilla/SandboxSettings.h"
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 */
74 # define FC_VARIABLE "variable" /* Bool */
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;
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] == '-'))) {
113 static void GetFaceNames(FcPattern
* aFont
, const nsACString
& aFamilyName
,
114 nsACString
& aPostscriptName
, nsACString
& aFullname
) {
115 // get the Postscript name
117 if (FcPatternGetString(aFont
, FC_POSTSCRIPT_NAME
, 0, &psname
) ==
119 aPostscriptName
= ToCharPtr(psname
);
122 // get the canonical fullname (i.e. en name or first name)
123 uint32_t en
= FindCanonicalNameIndex(aFont
, FC_FULLNAMELANG
);
125 if (FcPatternGetString(aFont
, FC_FULLNAME
, en
, &fullname
) == FcResultMatch
) {
126 aFullname
= ToCharPtr(fullname
);
129 // if have fullname, done
130 if (!aFullname
.IsEmpty()) {
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
);
140 FcChar8
* stylename
= nullptr;
141 FcPatternGetString(aFont
, FC_STYLE
, en
, &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) {
221 if (FcPatternGetInteger(aFontPattern
, FC_WEIGHT
, 0, &weight
) !=
223 weight
= FC_WEIGHT_REGULAR
;
225 *aWeight
= WeightRange(MapFcWeight(weight
));
229 if (FcPatternGetInteger(aFontPattern
, FC_WIDTH
, 0, &width
) != FcResultMatch
) {
230 width
= FC_WIDTH_NORMAL
;
232 *aStretch
= StretchRange(MapFcWidth(width
));
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
);
246 // pixel size, or zero if scalable
248 if (FcPatternGetBool(aFontPattern
, FC_SCALABLE
, 0, &scalable
) ==
254 if (FcPatternGetDouble(aFontPattern
, FC_PIXEL_SIZE
, 0, &size
) ==
256 *aSize
= uint16_t(NS_round(size
));
264 void gfxFontconfigFontEntry::GetUserFontFeatures(FcPattern
* aPattern
) {
265 int fontFeaturesNum
= 0;
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
);
272 mFeatureSettings
.AppendElement(
273 (gfxFontFeature
){tmpFeature
.tag
, tmpFeature
.value
});
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
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...
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
) {
325 if (FcPatternGetString(aPattern
, FC_FILE
, 0, &filename
) != FcResultMatch
) {
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
,
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
,
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
) {
390 sInitializedVarFuncs
= true;
391 #if MOZ_TREE_FREETYPE
392 sGetVar
= &FT_Get_MM_Var
;
393 sDoneVar
= &FT_Done_MM_Var
;
395 sGetVar
= (GetVarFunc
)dlsym(RTLD_DEFAULT
, "FT_Get_MM_Var");
396 sDoneVar
= (DoneVarFunc
)dlsym(RTLD_DEFAULT
, "FT_Done_MM_Var");
400 gfxFontconfigFontEntry::~gfxFontconfigFontEntry() {
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.
407 auto ftFace
= GetFTFace();
408 MOZ_ASSERT(ftFace
, "How did mMMVar get set without a face?");
409 (*sDoneVar
)(ftFace
->GetFace()->glyph
->library
, mMMVar
);
414 if (mFTFaceInitialized
) {
415 auto face
= mFTFace
.exchange(nullptr);
420 nsresult
gfxFontconfigFontEntry::ReadCMAP(FontInfoData
* aFontInfoData
) {
421 // attempt this once, if errors occur leave a blank cmap
426 RefPtr
<gfxCharacterMap
> charmap
;
429 uint32_t uvsOffset
= 0;
431 (charmap
= GetCMAPFromFontInfo(aFontInfoData
, uvsOffset
))) {
434 uint32_t kCMAP
= TRUETYPE_TAG('c', 'm', 'a', 'p');
435 charmap
= new gfxCharacterMap();
436 AutoTable
cmapTable(this, kCMAP
);
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
);
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()) {
459 charmap
= pfl
->FindCharMap(charmap
);
461 mHasCmapTable
= true;
463 // if error occurred, initialize to null cmap
464 charmap
= new gfxCharacterMap();
465 mHasCmapTable
= false;
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()) {
478 SprintfLiteral(prefix
, "(cmapdata) name: %.220s", mName
.get());
479 charmap
->Dump(prefix
, eGfxLog_cmapdata
);
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.
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'));
538 uint16_t upem
= UnitsPerEm();
539 if (upem
!= kInvalidUPEM
) {
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
568 s
.size
= 256.0; // pick large size to reduce hinting artifacts
569 RefPtr
<gfxFont
> font
= FindOrMakeFont(&s
);
571 const gfxFont::Metrics
& metrics
=
572 font
->GetMetrics(nsFontMetrics::eHorizontal
);
573 if (metrics
.emHeight
== 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
584 case FontSizeAdjust::Tag::IcWidth
:
585 case FontSizeAdjust::Tag::IcHeight
: {
586 bool vertical
= FontSizeAdjust::Tag(aSizeAdjustBasis
) ==
587 FontSizeAdjust::Tag::IcHeight
;
589 font
->GetCharAdvance(gfxFont::kWaterIdeograph
, vertical
);
590 return advance
> 0 ? advance
/ metrics
.emHeight
: 1.0;
597 MOZ_ASSERT_UNREACHABLE("failed to compute size-adjust aspect");
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
609 if (FcPatternGetBool(aPattern
, PRINTING_FC_PROPERTY
, 0, &printing
) !=
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
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
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
) {
677 int fc_hintstyle
= FC_HINT_NONE
;
678 if (!printing
&& hinting
&&
679 FcPatternGetInteger(aPattern
, FC_HINT_STYLE
, 0, &fc_hintstyle
) !=
681 fc_hintstyle
= FC_HINT_FULL
;
683 switch (fc_hintstyle
) {
685 loadFlags
= FT_LOAD_NO_HINTING
;
688 loadFlags
= FT_LOAD_TARGET_LIGHT
;
693 if (FcPatternGetBool(aPattern
, FC_ANTIALIAS
, 0, &fc_antialias
) !=
695 fc_antialias
= FcTrue
;
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
) {
704 if (FcPatternGetInteger(aPattern
, FC_RGBA
, 0, &fc_rgba
) != FcResultMatch
) {
705 fc_rgba
= FC_RGBA_UNKNOWN
;
710 loadFlags
= FT_LOAD_TARGET_LCD
;
714 loadFlags
= FT_LOAD_TARGET_LCD_V
;
719 if (!FcPatternAllowsBitmaps(aPattern
, fc_antialias
!= FcFalse
,
720 fc_hintstyle
!= FC_HINT_NONE
)) {
721 loadFlags
|= FT_LOAD_NO_BITMAP
;
725 if (FcPatternGetBool(aPattern
, FC_AUTOHINT
, 0, &autohint
) == FcResultMatch
&&
727 loadFlags
|= FT_LOAD_FORCE_AUTOHINT
;
731 if (FcPatternGetBool(aPattern
, FC_EMBOLDEN
, 0, &embolden
) == FcResultMatch
&&
733 synthFlags
|= CAIRO_FT_SYNTHESIZE_BOLD
;
736 *aOutLoadFlags
= loadFlags
;
737 *aOutSynthFlags
= synthFlags
;
741 static bool GetXftInt(Display
* aDisplay
, const char* aName
, int* aResult
) {
745 char* value
= XGetDefault(aDisplay
, "Xft", aName
);
749 if (FcNameConstant(const_cast<FcChar8
*>(ToFcChar8Ptr(value
)), aResult
)) {
753 *aResult
= strtol(value
, &end
, 0);
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
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
785 gfxFcPlatformFontList::PlatformFontList()->SubstituteSystemFontOptions(
787 #endif // MOZ_WIDGET_GTK
790 FcDefaultSubstitute(aPattern
);
793 void gfxFontconfigFontEntry::UnscaledFontCache::MoveToFront(size_t aIndex
) {
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
,
807 for (size_t i
= 0; i
< kNumEntries
; i
++) {
808 RefPtr
<UnscaledFontFontconfig
> entry(mUnscaledFonts
[i
]);
809 if (entry
&& entry
->GetFile() == aFile
&& entry
->GetIndex() == aIndex
) {
811 return entry
.forget();
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
;
832 while (FcPatternGetDouble(aEntry
->GetPattern(), FC_PIXEL_SIZE
, v
, &size
) ==
835 double dist
= fabs(size
- requestedSize
);
836 if (bestDist
< 0.0 || dist
< bestDist
) {
841 // If the font has bitmaps but wants to be scaled, then let it scale.
842 if (bestSize
>= 0.0) {
844 if (FcPatternGetBool(aEntry
->GetPattern(), FC_SCALABLE
, 0, &scalable
) ==
847 return requestedSize
;
853 gfxFont
* gfxFontconfigFontEntry::CreateFontInstance(
854 const gfxFontStyle
* aFontStyle
) {
855 RefPtr
<FcPattern
> pattern
= dont_AddRef(FcPatternCreate());
857 NS_WARNING("Failed to create Fontconfig pattern for font instance");
861 double size
= ChooseFontSize(this, *aFontStyle
);
862 FcPatternAddDouble(pattern
, FC_PIXEL_SIZE
, size
);
864 RefPtr
<SharedFTFace
> face
= GetFTFace();
866 NS_WARNING("Failed to get FreeType face for pattern");
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
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
);
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");
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
);
910 unsigned int synthFlags
;
911 PrepareFontOptions(renderPattern
, &loadFlags
, &synthFlags
);
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
) !=
921 NS_WARNING("No file in Fontconfig pattern for font instance");
924 file
= ToCharPtr(fcFile
);
927 RefPtr
<UnscaledFontFontconfig
> unscaledFont
;
929 AutoReadLock
lock(mLock
);
930 unscaledFont
= mUnscaledFontCache
.Lookup(file
, index
);
934 AutoWriteLock
lock(mLock
);
935 // Here, we use the original mFTFace, not a potential clone with variation
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);
951 SharedFTFace
* gfxFontconfigFontEntry::GetFTFace() {
952 if (!mFTFaceInitialized
) {
953 RefPtr
<SharedFTFace
> face
= CreateFaceForPattern(mFontPattern
);
955 if (mFTFace
.compareExchange(nullptr, face
.get())) {
956 Unused
<< face
.forget(); // The reference is now owned by mFTFace.
957 mFTFaceInitialized
= true;
959 // We lost a race to set mFTFace! Just discard our new face.
966 FTUserFontData
* gfxFontconfigFontEntry::GetUserFontData() {
967 auto face
= GetFTFace();
968 if (face
&& face
->GetData()) {
969 return static_cast<FTUserFontData
*>(face
->GetData());
974 bool gfxFontconfigFontEntry::HasVariations() {
975 // If the answer is already cached, just return it.
976 switch (mHasVariations
) {
977 case HasVariationsState::No
:
979 case HasVariationsState::Yes
:
981 case HasVariationsState::Uninitialized
:
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
;
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()) {
998 if ((FcPatternGetBool(mFontPattern
, FC_VARIABLE
, 0, &variable
) ==
1001 mHasVariations
= HasVariationsState::Yes
;
1005 if (auto ftFace
= GetFTFace()) {
1006 if (ftFace
->GetFace()->face_flags
& FT_FACE_FLAG_MULTIPLE_MASTERS
) {
1007 mHasVariations
= HasVariationsState::Yes
;
1013 mHasVariations
= HasVariationsState::No
;
1017 FT_MM_Var
* gfxFontconfigFontEntry::GetMMVar() {
1019 AutoReadLock
lock(mLock
);
1020 if (mMMVarInitialized
) {
1025 AutoWriteLock
lock(mLock
);
1027 mMMVarInitialized
= true;
1028 InitializeVarFuncs();
1032 auto ftFace
= GetFTFace();
1036 if (FT_Err_Ok
!= (*sGetVar
)(ftFace
->GetFace(), &mMMVar
)) {
1042 void gfxFontconfigFontEntry::GetVariationAxes(
1043 nsTArray
<gfxFontVariationAxis
>& aAxes
) {
1044 if (!HasVariations()) {
1047 gfxFT2Utils::GetVariationAxes(GetMMVar(), aAxes
);
1050 void gfxFontconfigFontEntry::GetVariationInstances(
1051 nsTArray
<gfxFontVariationInstance
>& aInstances
) {
1052 if (!HasVariations()) {
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
) {
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()) {
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
);
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(),
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();
1122 CheckForSimpleFamily();
1125 void gfxFontconfigFontFamily::AddFontPattern(FcPattern
* aFontPattern
,
1129 "font patterns must not be added to already enumerated families");
1132 if (FcPatternGetBool(aFontPattern
, FC_OUTLINE
, 0, &outline
) !=
1135 mHasNonScalableFaces
= true;
1138 if (FcPatternGetBool(aFontPattern
, FC_SCALABLE
, 0, &scalable
) ==
1141 mForceScalable
= true;
1146 mFontPatterns
.InsertElementAt(mUniqueNameFaceCount
++, aFontPattern
);
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;
1162 while (FcPatternGetDouble(aEntry
->GetPattern(), FC_PIXEL_SIZE
, v
, &size
) ==
1165 double dist
= fabs(size
- requestedSize
);
1166 if (bestDist
< 0.0 || dist
< bestDist
) {
1170 if (bestDist
< 0.0) {
1171 // No size means scalable
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.
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
) {
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.
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
]);
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
) {
1216 // Remove any compacted entries from the previous group.
1219 aFontEntryList
.RemoveElementsAt(i
, skipped
);
1222 // Mark the start of the new group.
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
;
1236 // If the best entry in this group is still outside the tolerance,
1237 // then skip the entire group.
1238 if (bestDist
>= kRejectDistance
) {
1241 // Remove any compacted entries from the current group.
1243 aFontEntryList
.TruncateLength(aFontEntryList
.Length() - skipped
);
1247 static bool PatternHasLang(const FcPattern
* aPattern
, const FcChar8
* aLang
) {
1250 if (FcPatternGetLangSet(aPattern
, FC_LANG
, 0, &langset
) != FcResultMatch
) {
1254 if (FcLangSetHasLang(langset
, aLang
) != FcLangDifferentLang
) {
1260 bool gfxFontconfigFontFamily::SupportsLangGroup(nsAtom
* aLangGroup
) const {
1261 if (!aLangGroup
|| aLangGroup
== nsGkAtoms::Unicode
) {
1265 nsAutoCString fcLang
;
1266 gfxFcPlatformFontList
* pfl
= gfxFcPlatformFontList::PlatformFontList();
1267 pfl
->GetSampleLangForGroup(aLangGroup
, fcLang
);
1268 if (fcLang
.IsEmpty()) {
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())
1288 // is lang included in the underlying pattern?
1289 return PatternHasLang(fontPattern
, ToFcChar8Ptr(fcLang
.get()));
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
);
1302 for (auto& fe
: mAvailableFonts
) {
1306 auto fce
= static_cast<gfxFontconfigFontEntry
*>(fe
.get());
1307 aAddPatternFunc(fce
->GetPattern(), mContainsAppFonts
);
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
,
1321 : gfxFT2FontBase(aUnscaledFont
, std::move(aFTFace
), aFontEntry
, aFontStyle
,
1322 aLoadFlags
, aEmbolden
),
1323 mPattern(aPattern
) {
1324 mAdjustedSize
= aAdjustedSize
;
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
) {
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()
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;
1387 gfxFcPlatformFontList::~gfxFcPlatformFontList() {
1388 AutoLock
lock(mLock
);
1390 if (mCheckFontUpdatesTimer
) {
1391 mCheckFontUpdatesTimer
->Cancel();
1392 mCheckFontUpdatesTimer
= nullptr;
1394 #ifdef MOZ_WIDGET_GTK
1395 ClearSystemFontOptions();
1399 void gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet
* aFontSet
,
1400 const SandboxPolicy
* aPolicy
,
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
)) {
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).
1421 if (FcPatternGetString(pattern
, FC_FILE
, 0, &path
) != FcResultMatch
) {
1424 if (access(reinterpret_cast<const char*>(path
), F_OK
| R_OK
) != 0) {
1428 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
1429 // Skip any fonts that will be blocked by the content-process sandbox
1431 if (aPolicy
&& !(aPolicy
->Lookup(reinterpret_cast<const char*>(path
)) &
1432 SandboxBroker::Perms::MAY_READ
)) {
1437 AddPatternToFontList(pattern
, lastFamilyName
, familyName
, fontFamily
,
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
);
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
*>(
1465 .LookupOrInsertWith(keyName
,
1467 FontVisibility visibility
=
1469 ? FontVisibility::Base
1470 : GetVisibilityForFamily(keyName
);
1471 return MakeRefPtr
<gfxFontconfigFontFamily
>(
1472 aFamilyName
, visibility
);
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
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
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
)));
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
) {
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();
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());
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
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
);
1594 ("got font list from chrome process: "
1595 "%u faces in %u families",
1596 (unsigned)fontList
.entries().Length(), mFontFamilies
.Count()));
1598 fontList
.entries().Clear();
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);
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);
1628 // iterate over available fonts
1629 FcFontSet
* systemFonts
= FcConfigGetFonts(nullptr, FcSetSystem
);
1630 AddFontSetFamilies(systemFonts
, policy
.get(), /* aAppFonts = */ false);
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());
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
));
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
));
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.
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
1695 uint32_t mUniqueNameFaceCount
= 0;
1698 void Add(fontlist::Face::InitData
&& aData
, bool aSingleName
) {
1700 mFaces
.InsertElementAt(mUniqueNameFaceCount
++, std::move(aData
));
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());
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.
1729 #ifdef MOZ_WIDGET_GTK
1730 UpdateSystemFontOptions();
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());
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);
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
);
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.
1793 .LookupOrInsertWith(
1796 families
.AppendElement(fontlist::Family::InitData(
1797 keyName
, aFamilyName
, fontlist::Family::kNoIndex
,
1799 /*bundled*/ aAppFont
, /*badUnderline*/ false));
1800 return MakeUnique
<FacesData
>();
1804 char* s
= (char*)FcNameUnparse(aPattern
);
1805 nsAutoCString
descriptor(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
);
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.
1830 int n
= (cIndex
== 0 ? 1 : 0);
1831 while (FcPatternGetString(aPattern
, FC_FAMILY
, n
, &otherName
) ==
1833 nsAutoCString
otherFamilyName(ToCharPtr(otherName
));
1834 keyName
= otherFamilyName
;
1835 ToLowerCase(keyName
);
1838 .LookupOrInsertWith(
1841 families
.AppendElement(fontlist::Family::InitData(
1842 keyName
, otherFamilyName
, fontlist::Family::kNoIndex
,
1844 /*bundled*/ aAppFont
, /*badUnderline*/ false));
1846 return MakeUnique
<FacesData
>();
1849 ->Add(fontlist::Face::InitData(initData
), /* singleName = */ false);
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.
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 {
1892 if (NS_WARN_IF(!aFontSet
)) {
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).
1904 if (FcPatternGetString(pattern
, FC_FILE
, 0, &path
) != FcResultMatch
) {
1907 if (access(reinterpret_cast<const char*>(path
), F_OK
| R_OK
) != 0) {
1911 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
1912 // Skip any fonts that will be blocked by the content-process sandbox
1914 if (aPolicy
&& !(aPolicy
->Lookup(reinterpret_cast<const char*>(path
)) &
1915 SandboxBroker::Perms::MAY_READ
)) {
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
);
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
) ==
1943 (!FcStrCmp(fontFormat
, (const FcChar8
*)"TrueType") ||
1944 !FcStrCmp(fontFormat
, (const FcChar8
*)"CFF"))) {
1945 FcPatternDel(clone
, FC_CHARSET
);
1946 if (addPattern(clone
, lastFamilyName
, familyName
, aAppFonts
)) {
1950 if (addPattern(clone
, lastFamilyName
, familyName
, aAppFonts
)) {
1955 FcPatternDestroy(clone
);
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);
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"));
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.
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.
2039 case Device::Linux_Fedora_38
:
2040 if (FamilyInList(aName
, kBaseFonts_Fedora_38
)) {
2041 return FontVisibility::Base
;
2043 return FontVisibility::User
;
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.
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
)));
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.
2081 case Device::Linux_Fedora_38
:
2082 fontLists
.AppendElement(std::make_pair(
2083 kBaseFonts_Fedora_38
, ArrayLength(kBaseFonts_Fedora_38
)));
2087 // We don't know how to categorize fonts on this system
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
);
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());
2116 UniquePtr
<FcObjectSet
> os(FcObjectSetBuild(FC_FAMILY
, nullptr));
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()));
2135 for (int i
= 0; i
< fs
->nfont
; i
++) {
2138 if (FcPatternGetString(fs
->fonts
[i
], FC_FAMILY
, 0, (FcChar8
**)&family
) !=
2143 // Remove duplicates...
2144 nsAutoString strFamily
;
2145 AppendUTF8toUTF16(MakeStringSpan(family
), strFamily
);
2146 if (aListOfFonts
.Contains(strFamily
)) {
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"))
2171 else if (aGenericFamily
.LowerCaseEqualsLiteral("sans-serif"))
2173 else if (aGenericFamily
.LowerCaseEqualsLiteral("monospace"))
2175 else if (aGenericFamily
.LowerCaseEqualsLiteral("cursive") ||
2176 aGenericFamily
.LowerCaseEqualsLiteral("fantasy"))
2177 serif
= sansSerif
= true;
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
);
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();
2237 return new gfxFontconfigFontEntry(aFontName
, aWeightForEntry
,
2238 aStretchForEntry
, aStyleForEntry
,
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
);
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
2289 // serif ==> DejaVu Serif, ...
2290 // Helvetica, serif ==> Helvetica, TeX Gyre Heros, Nimbus Sans L, DejaVu
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
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
);
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
);
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()) {
2333 aOutput
->AppendElements(*cached
);
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.
2354 for (int i
= 0; FcPatternGetString(sentinelSubst
, FC_FAMILY
, i
, &substName
) ==
2357 if (FcStrCmp(substName
, kSentinelName
) == 0) {
2358 terminator
= kSentinelName
;
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
;
2381 if (terminator
&& FcStrCmp(substName
, terminator
) == 0) {
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()) {
2395 aOutput
->AppendElements(insertedCachedFamilies
);
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
);
2412 RefPtr
<FcPattern
> pat
= dont_AddRef(FcPatternCreate());
2417 UniquePtr
<FcObjectSet
> os(FcObjectSetBuild(FC_FAMILY
, nullptr));
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()));
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
++) {
2436 if (FcPatternGetString(givenFS
->fonts
[i
], FC_FAMILY
, 0,
2437 (FcChar8
**)&firstFamily
) != FcResultMatch
) {
2441 nsDependentCString
first(firstFamily
);
2442 if (!candidates
.Contains(first
)) {
2443 candidates
.AppendElement(first
);
2445 if (aFontName
.Equals(first
)) {
2446 aFamilyName
.Assign(aFontName
);
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()));
2469 if (candidateFS
->nfont
!= givenFS
->nfont
) {
2474 for (int i
= 0; i
< givenFS
->nfont
; ++i
) {
2475 if (!FcPatternEqual(candidateFS
->fonts
[i
], givenFS
->fonts
[i
])) {
2481 aFamilyName
= candidates
[j
];
2486 // didn't find localized name, leave family name blank
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");
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;
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
,
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
) {
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
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.
2574 if (LocaleParser::TryParse(fcLang
, locale
).isOk() &&
2575 locale
.AddLikelySubtags().isOk()) {
2576 if (UseCustomFontconfigLookupsForLocale(locale
)) {
2577 cacheKey
.Append(fcLang
);
2579 cacheKey
.Append(locale
.Script().Span());
2582 cacheKey
.Append(fcLang
);
2586 // try to get the family from the cache
2587 return mGenericMappings
.WithEntryHandle(
2588 cacheKey
, [&](auto&& entry
) -> PrefFontList
* {
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
2610 UniquePtr
<FcFontSet
> faces(
2611 FcFontSort(nullptr, genericPattern
, FcFalse
, nullptr, &result
));
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
);
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
) {
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"
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
)) {
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
,
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
);
2762 aFcLang
.Append('-');
2765 aFcLang
.Append(*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();
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
];
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
);
2813 // -- check the environment for the user's preferred language that
2814 // corresponds to this mozilla lang group.
2815 const char* languages
= getenv("LANGUAGE");
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
)) {
2831 languages
= pos
+ 1;
2835 const char* ctype
= setlocale(LC_CTYPE
, nullptr);
2836 if (ctype
&& TryLangForGroup(nsDependentCString(ctype
), aLanguage
, aLangStr
,
2837 aForFontEnumerationThread
)) {
2841 if (mozLangGroup
->defaultLang
) {
2842 aLangStr
.Assign(mozLangGroup
->defaultLang
);
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
)) {
2857 if (NS_FAILED(localDir
->Append(u
"fonts"_ns
))) {
2861 if (NS_FAILED(localDir
->IsDirectory(&isDir
)) || !isDir
) {
2864 if (NS_FAILED(localDir
->GetNativePath(mBundledFontsPath
))) {
2868 if (!mBundledFontsPath
.IsEmpty()) {
2869 FcConfigAppFontAddDir(nullptr, ToFcChar8Ptr(mBundledFontsPath
.get()));
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.
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()) {
2942 // This one shouldn't change during the X session.
2944 GdkDisplay
* dpy
= gdk_display_get_default();
2945 if (mozilla::widget::GdkIsX11Display(dpy
) &&
2946 GetXftInt(GDK_DISPLAY_XDISPLAY(dpy
), "lcdfilter", &lcdfilter
)) {
2947 mFreetypeLcdSetting
= lcdfilter
;
2952 const cairo_font_options_t
* options
=
2953 gdk_screen_get_font_options(gdk_screen_get_default());
2955 bool changed
= !!mSystemFontOptions
;
2956 ClearSystemFontOptions();
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
);
2968 ClearSystemFontOptions();
2969 mSystemFontOptions
= newOptions
;
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() =
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) {
3008 if (FcPatternGet(aPattern
, FC_LCD_FILTER
, 0, &value
) == FcResultNoMatch
) {
3009 FcPatternAddInteger(aPattern
, FC_LCD_FILTER
, mFreetypeLcdSetting
);
3014 #endif // MOZ_WIDGET_GTK