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/. */
5
6 #include "mozilla/Logging.h"
7
8 #include "gfxFcPlatformFontList.h"
9 #include "gfxFont.h"
10 #include "gfxFontConstants.h"
11 #include "gfxFT2Utils.h"
12 #include "gfxPlatform.h"
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/dom/ContentChild.h"
15 #include "mozilla/dom/ContentParent.h"
16 #include "mozilla/Preferences.h"
17 #include "mozilla/Sprintf.h"
18 #include "mozilla/StaticPrefs_gfx.h"
19 #include "mozilla/Telemetry.h"
20 #include "mozilla/TimeStamp.h"
21 #include "nsGkAtoms.h"
22 #include "nsUnicodeProperties.h"
23 #include "nsDirectoryServiceUtils.h"
24 #include "nsDirectoryServiceDefs.h"
25 #include "nsAppDirectoryServiceDefs.h"
26 #include "nsCharSeparatedTokenizer.h"
27 #include "nsXULAppAPI.h"
28 #include "SharedFontList-impl.h"
29 #include "StandardFonts-linux.inc"
30
31 #include "mozilla/gfx/HelpersCairo.h"
32
33 #include <cairo-ft.h>
34 #include <fontconfig/fcfreetype.h>
35 #include <dlfcn.h>
36 #include <unistd.h>
37
38 #ifdef MOZ_WIDGET_GTK
39 # include <gdk/gdk.h>
40 # include <gtk/gtk.h>
41 # include "gfxPlatformGtk.h"
42 # include "mozilla/WidgetUtilsGtk.h"
43 #endif
44
45 #ifdef MOZ_X11
46 # include "mozilla/X11Util.h"
47 #endif
48
49 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
50 # include "mozilla/SandboxBrokerPolicyFactory.h"
51 # include "mozilla/SandboxSettings.h"
52 #endif
53
54 #include FT_MULTIPLE_MASTERS_H
55
56 using namespace mozilla;
57 using namespace mozilla::gfx;
58 using namespace mozilla::unicode;
59
60 #ifndef FC_POSTSCRIPT_NAME
61 # define FC_POSTSCRIPT_NAME "postscriptname" /* String */
62 #endif
63 #ifndef FC_VARIABLE
64 # define FC_VARIABLE "variable" /* Bool */
65 #endif
66
67 #define PRINTING_FC_PROPERTY "gfx.printing"
68
69 #define LOG_FONTLIST(args) \
70 MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug, args)
71 #define LOG_FONTLIST_ENABLED() \
72 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontlist), LogLevel::Debug)
73 #define LOG_CMAPDATA_ENABLED() \
74 MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_cmapdata), LogLevel::Debug)
75
ToFcChar8Ptr(const char * aStr)76 static const FcChar8* ToFcChar8Ptr(const char* aStr) {
77 return reinterpret_cast<const FcChar8*>(aStr);
78 }
79
ToCharPtr(const FcChar8 * aStr)80 static const char* ToCharPtr(const FcChar8* aStr) {
81 return reinterpret_cast<const char*>(aStr);
82 }
83
84 // canonical name ==> first en name or first name if no en name
85 // This is the required logic for fullname lookups as per CSS3 Fonts spec.
FindCanonicalNameIndex(FcPattern * aFont,const char * aLangField)86 static uint32_t FindCanonicalNameIndex(FcPattern* aFont,
87 const char* aLangField) {
88 uint32_t n = 0, en = 0;
89 FcChar8* lang;
90 while (FcPatternGetString(aFont, aLangField, n, &lang) == FcResultMatch) {
91 // look for 'en' or variants, en-US, en-JP etc.
92 uint32_t len = strlen(ToCharPtr(lang));
93 bool enPrefix = (strncmp(ToCharPtr(lang), "en", 2) == 0);
94 if (enPrefix && (len == 2 || (len > 2 && aLangField[2] == '-'))) {
95 en = n;
96 break;
97 }
98 n++;
99 }
100 return en;
101 }
102
GetFaceNames(FcPattern * aFont,const nsACString & aFamilyName,nsACString & aPostscriptName,nsACString & aFullname)103 static void GetFaceNames(FcPattern* aFont, const nsACString& aFamilyName,
104 nsACString& aPostscriptName, nsACString& aFullname) {
105 // get the Postscript name
106 FcChar8* psname;
107 if (FcPatternGetString(aFont, FC_POSTSCRIPT_NAME, 0, &psname) ==
108 FcResultMatch) {
109 aPostscriptName = ToCharPtr(psname);
110 }
111
112 // get the canonical fullname (i.e. en name or first name)
113 uint32_t en = FindCanonicalNameIndex(aFont, FC_FULLNAMELANG);
114 FcChar8* fullname;
115 if (FcPatternGetString(aFont, FC_FULLNAME, en, &fullname) == FcResultMatch) {
116 aFullname = ToCharPtr(fullname);
117 }
118
119 // if have fullname, done
120 if (!aFullname.IsEmpty()) {
121 return;
122 }
123
124 // otherwise, set the fullname to family + style name [en] and use that
125 aFullname = aFamilyName;
126
127 // figure out the en style name
128 en = FindCanonicalNameIndex(aFont, FC_STYLELANG);
129 nsAutoCString style;
130 FcChar8* stylename = nullptr;
131 FcPatternGetString(aFont, FC_STYLE, en, &stylename);
132 if (stylename) {
133 style = ToCharPtr(stylename);
134 }
135
136 if (!style.IsEmpty() && !style.EqualsLiteral("Regular")) {
137 aFullname.Append(' ');
138 aFullname.Append(style);
139 }
140 }
141
MapFcWeight(int aFcWeight)142 static FontWeight MapFcWeight(int aFcWeight) {
143 if (aFcWeight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2) {
144 return FontWeight(100);
145 }
146 if (aFcWeight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2) {
147 return FontWeight(200);
148 }
149 if (aFcWeight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2) {
150 return FontWeight(300);
151 }
152 if (aFcWeight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2) {
153 // This includes FC_WEIGHT_BOOK
154 return FontWeight(400);
155 }
156 if (aFcWeight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) {
157 return FontWeight(500);
158 }
159 if (aFcWeight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) {
160 return FontWeight(600);
161 }
162 if (aFcWeight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2) {
163 return FontWeight(700);
164 }
165 if (aFcWeight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2) {
166 return FontWeight(800);
167 }
168 if (aFcWeight <= FC_WEIGHT_BLACK) {
169 return FontWeight(900);
170 }
171
172 // including FC_WEIGHT_EXTRABLACK
173 return FontWeight(901);
174 }
175
176 // TODO(emilio, jfkthame): I think this can now be more fine-grained.
MapFcWidth(int aFcWidth)177 static FontStretch MapFcWidth(int aFcWidth) {
178 if (aFcWidth <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
179 return FontStretch::UltraCondensed();
180 }
181 if (aFcWidth <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
182 return FontStretch::ExtraCondensed();
183 }
184 if (aFcWidth <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
185 return FontStretch::Condensed();
186 }
187 if (aFcWidth <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
188 return FontStretch::SemiCondensed();
189 }
190 if (aFcWidth <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
191 return FontStretch::Normal();
192 }
193 if (aFcWidth <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
194 return FontStretch::SemiExpanded();
195 }
196 if (aFcWidth <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
197 return FontStretch::Expanded();
198 }
199 if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
200 return FontStretch::ExtraExpanded();
201 }
202 return FontStretch::UltraExpanded();
203 }
204
GetFontProperties(FcPattern * aFontPattern,WeightRange * aWeight,StretchRange * aStretch,SlantStyleRange * aSlantStyle,uint16_t * aSize=nullptr)205 static void GetFontProperties(FcPattern* aFontPattern, WeightRange* aWeight,
206 StretchRange* aStretch,
207 SlantStyleRange* aSlantStyle,
208 uint16_t* aSize = nullptr) {
209 // weight
210 int weight;
211 if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) !=
212 FcResultMatch) {
213 weight = FC_WEIGHT_REGULAR;
214 }
215 *aWeight = WeightRange(MapFcWeight(weight));
216
217 // width
218 int width;
219 if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
220 width = FC_WIDTH_NORMAL;
221 }
222 *aStretch = StretchRange(MapFcWidth(width));
223
224 // italic
225 int slant;
226 if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
227 slant = FC_SLANT_ROMAN;
228 }
229 if (slant == FC_SLANT_OBLIQUE) {
230 *aSlantStyle = SlantStyleRange(FontSlantStyle::Oblique());
231 } else if (slant > 0) {
232 *aSlantStyle = SlantStyleRange(FontSlantStyle::Italic());
233 }
234
235 if (aSize) {
236 // pixel size, or zero if scalable
237 FcBool scalable;
238 if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) ==
239 FcResultMatch &&
240 scalable) {
241 *aSize = 0;
242 } else {
243 double size;
244 if (FcPatternGetDouble(aFontPattern, FC_PIXEL_SIZE, 0, &size) ==
245 FcResultMatch) {
246 *aSize = uint16_t(NS_round(size));
247 } else {
248 *aSize = 0;
249 }
250 }
251 }
252 }
253
gfxFontconfigFontEntry(const nsACString & aFaceName,FcPattern * aFontPattern,bool aIgnoreFcCharmap)254 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
255 FcPattern* aFontPattern,
256 bool aIgnoreFcCharmap)
257 : gfxFT2FontEntryBase(aFaceName),
258 mFontPattern(aFontPattern),
259 mFTFaceInitialized(false),
260 mIgnoreFcCharmap(aIgnoreFcCharmap),
261 mHasVariationsInitialized(false) {
262 GetFontProperties(aFontPattern, &mWeightRange, &mStretchRange, &mStyleRange);
263 }
264
Clone() const265 gfxFontEntry* gfxFontconfigFontEntry::Clone() const {
266 MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
267 return new gfxFontconfigFontEntry(Name(), mFontPattern, mIgnoreFcCharmap);
268 }
269
CreatePatternForFace(FT_Face aFace)270 static already_AddRefed<FcPattern> CreatePatternForFace(FT_Face aFace) {
271 // Use fontconfig to fill out the pattern from the FTFace.
272 // The "file" argument cannot be nullptr (in fontconfig-2.6.0 at
273 // least). The dummy file passed here is removed below.
274 //
275 // When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr)
276 // is passed as the "blanks" argument, which provides that unexpectedly
277 // blank glyphs are elided. Here, however, we pass nullptr for
278 // "blanks", effectively assuming that, if the font has a blank glyph,
279 // then the author intends any associated character to be rendered
280 // blank.
281 RefPtr<FcPattern> pattern =
282 dont_AddRef(FcFreeTypeQueryFace(aFace, ToFcChar8Ptr(""), 0, nullptr));
283 // given that we have a FT_Face, not really sure this is possible...
284 if (!pattern) {
285 pattern = dont_AddRef(FcPatternCreate());
286 }
287 FcPatternDel(pattern, FC_FILE);
288 FcPatternDel(pattern, FC_INDEX);
289
290 // Make a new pattern and store the face in it so that cairo uses
291 // that when creating a cairo font face.
292 FcPatternAddFTFace(pattern, FC_FT_FACE, aFace);
293
294 return pattern.forget();
295 }
296
CreateFaceForPattern(FcPattern * aPattern)297 static already_AddRefed<SharedFTFace> CreateFaceForPattern(
298 FcPattern* aPattern) {
299 FcChar8* filename;
300 if (FcPatternGetString(aPattern, FC_FILE, 0, &filename) != FcResultMatch) {
301 return nullptr;
302 }
303 int index;
304 if (FcPatternGetInteger(aPattern, FC_INDEX, 0, &index) != FcResultMatch) {
305 index = 0; // default to 0 if not found in pattern
306 }
307 return Factory::NewSharedFTFace(nullptr, ToCharPtr(filename), index);
308 }
309
gfxFontconfigFontEntry(const nsACString & aFaceName,WeightRange aWeight,StretchRange aStretch,SlantStyleRange aStyle,RefPtr<SharedFTFace> && aFace)310 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
311 WeightRange aWeight,
312 StretchRange aStretch,
313 SlantStyleRange aStyle,
314 RefPtr<SharedFTFace>&& aFace)
315 : gfxFT2FontEntryBase(aFaceName),
316 mFTFace(std::move(aFace)),
317 mFTFaceInitialized(true),
318 mIgnoreFcCharmap(true),
319 mHasVariationsInitialized(false) {
320 mWeightRange = aWeight;
321 mStyleRange = aStyle;
322 mStretchRange = aStretch;
323 mIsDataUserFont = true;
324
325 mFontPattern = CreatePatternForFace(mFTFace->GetFace());
326 }
327
gfxFontconfigFontEntry(const nsACString & aFaceName,FcPattern * aFontPattern,WeightRange aWeight,StretchRange aStretch,SlantStyleRange aStyle)328 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsACString& aFaceName,
329 FcPattern* aFontPattern,
330 WeightRange aWeight,
331 StretchRange aStretch,
332 SlantStyleRange aStyle)
333 : gfxFT2FontEntryBase(aFaceName),
334 mFontPattern(aFontPattern),
335 mFTFaceInitialized(false),
336 mHasVariationsInitialized(false) {
337 mWeightRange = aWeight;
338 mStyleRange = aStyle;
339 mStretchRange = aStretch;
340 mIsLocalUserFont = true;
341
342 // The proper setting of mIgnoreFcCharmap is tricky for fonts loaded
343 // via src:local()...
344 // If the local font happens to come from the application fontset,
345 // we want to set it to true so that color/svg fonts will work even
346 // if the default glyphs are blank; but if the local font is a non-
347 // sfnt face (e.g. legacy type 1) then we need to set it to false
348 // because our cmap-reading code will fail and we depend on FT+Fc to
349 // determine the coverage.
350 // We set the flag here, but may flip it the first time TestCharacterMap
351 // is called, at which point we'll look to see whether a 'cmap' is
352 // actually present in the font.
353 mIgnoreFcCharmap = true;
354 }
355
356 typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**);
357 typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*);
358 static GetVarFunc sGetVar;
359 static DoneVarFunc sDoneVar;
360 static bool sInitializedVarFuncs = false;
361
InitializeVarFuncs()362 static void InitializeVarFuncs() {
363 if (sInitializedVarFuncs) {
364 return;
365 }
366 sInitializedVarFuncs = true;
367 #if MOZ_TREE_FREETYPE
368 sGetVar = &FT_Get_MM_Var;
369 sDoneVar = &FT_Done_MM_Var;
370 #else
371 sGetVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
372 sDoneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var");
373 #endif
374 }
375
~gfxFontconfigFontEntry()376 gfxFontconfigFontEntry::~gfxFontconfigFontEntry() {
377 if (mMMVar) {
378 // Prior to freetype 2.9, there was no specific function to free the
379 // FT_MM_Var record, and the docs just said to use free().
380 // InitializeVarFuncs must have been called in order for mMMVar to be
381 // non-null here, so we don't need to do it again.
382 if (sDoneVar) {
383 MOZ_ASSERT(mFTFace, "How did mMMVar get set without a face?");
384 (*sDoneVar)(mFTFace->GetFace()->glyph->library, mMMVar);
385 } else {
386 free(mMMVar);
387 }
388 }
389 }
390
ReadCMAP(FontInfoData * aFontInfoData)391 nsresult gfxFontconfigFontEntry::ReadCMAP(FontInfoData* aFontInfoData) {
392 // attempt this once, if errors occur leave a blank cmap
393 if (mCharacterMap) {
394 return NS_OK;
395 }
396
397 RefPtr<gfxCharacterMap> charmap;
398 nsresult rv;
399
400 if (aFontInfoData &&
401 (charmap = GetCMAPFromFontInfo(aFontInfoData, mUVSOffset))) {
402 rv = NS_OK;
403 } else {
404 uint32_t kCMAP = TRUETYPE_TAG('c', 'm', 'a', 'p');
405 charmap = new gfxCharacterMap();
406 AutoTable cmapTable(this, kCMAP);
407
408 if (cmapTable) {
409 uint32_t cmapLen;
410 const uint8_t* cmapData = reinterpret_cast<const uint8_t*>(
411 hb_blob_get_data(cmapTable, &cmapLen));
412 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, mUVSOffset);
413 } else {
414 rv = NS_ERROR_NOT_AVAILABLE;
415 }
416 }
417
418 mHasCmapTable = NS_SUCCEEDED(rv);
419 if (mHasCmapTable) {
420 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
421 fontlist::FontList* sharedFontList = pfl->SharedFontList();
422 if (!IsUserFont() && mShmemFace) {
423 mShmemFace->SetCharacterMap(sharedFontList, charmap); // async
424 if (!TrySetShmemCharacterMap()) {
425 // Temporarily retain charmap, until the shared version is
426 // ready for use.
427 mCharacterMap = charmap;
428 }
429 } else {
430 mCharacterMap = pfl->FindCharMap(charmap);
431 }
432 } else {
433 // if error occurred, initialize to null cmap
434 mCharacterMap = new gfxCharacterMap();
435 }
436
437 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n",
438 mName.get(), charmap->SizeOfIncludingThis(moz_malloc_size_of),
439 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
440 if (LOG_CMAPDATA_ENABLED()) {
441 char prefix[256];
442 SprintfLiteral(prefix, "(cmapdata) name: %.220s", mName.get());
443 charmap->Dump(prefix, eGfxLog_cmapdata);
444 }
445
446 return rv;
447 }
448
HasChar(FcPattern * aFont,FcChar32 aCh)449 static bool HasChar(FcPattern* aFont, FcChar32 aCh) {
450 FcCharSet* charset = nullptr;
451 FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
452 return charset && FcCharSetHasChar(charset, aCh);
453 }
454
TestCharacterMap(uint32_t aCh)455 bool gfxFontconfigFontEntry::TestCharacterMap(uint32_t aCh) {
456 // For user fonts, or for fonts bundled with the app (which might include
457 // color/svg glyphs where the default glyphs may be blank, and thus confuse
458 // fontconfig/freetype's char map checking), we instead check the cmap
459 // directly for character coverage.
460 if (mIgnoreFcCharmap) {
461 // If it does not actually have a cmap, switch our strategy to use
462 // fontconfig's charmap after all (except for data fonts, which must
463 // always have a cmap to have passed OTS validation).
464 if (!mIsDataUserFont && !HasFontTable(TRUETYPE_TAG('c', 'm', 'a', 'p'))) {
465 mIgnoreFcCharmap = false;
466 // ...and continue with HasChar() below.
467 } else {
468 return gfxFontEntry::TestCharacterMap(aCh);
469 }
470 }
471 // otherwise (for system fonts), use the charmap in the pattern
472 return HasChar(mFontPattern, aCh);
473 }
474
HasFontTable(uint32_t aTableTag)475 bool gfxFontconfigFontEntry::HasFontTable(uint32_t aTableTag) {
476 if (FTUserFontData* ufd = GetUserFontData()) {
477 return !!gfxFontUtils::FindTableDirEntry(ufd->FontData(), aTableTag);
478 }
479 return gfxFT2FontEntryBase::FaceHasTable(GetFTFace(), aTableTag);
480 }
481
GetFontTable(uint32_t aTableTag)482 hb_blob_t* gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag) {
483 // for data fonts, read directly from the font data
484 if (FTUserFontData* ufd = GetUserFontData()) {
485 return gfxFontUtils::GetTableFromFontData(ufd->FontData(), aTableTag);
486 }
487
488 return gfxFontEntry::GetFontTable(aTableTag);
489 }
490
GetAspect(uint8_t aSizeAdjustBasis)491 double gfxFontconfigFontEntry::GetAspect(uint8_t aSizeAdjustBasis) {
492 using FontSizeAdjust = gfxFont::FontSizeAdjust;
493 if (FontSizeAdjust::Tag(aSizeAdjustBasis) == FontSizeAdjust::Tag::ExHeight ||
494 FontSizeAdjust::Tag(aSizeAdjustBasis) == FontSizeAdjust::Tag::CapHeight) {
495 // try to compute aspect from OS/2 metrics if available
496 AutoTable os2Table(this, TRUETYPE_TAG('O', 'S', '/', '2'));
497 if (os2Table) {
498 uint16_t upem = UnitsPerEm();
499 if (upem != kInvalidUPEM) {
500 uint32_t len;
501 const auto* os2 =
502 reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
503 if (uint16_t(os2->version) >= 2) {
504 // XXX(jfkthame) Other implementations don't have the check for
505 // values <= 0.1em; should we drop that here? Just require it to be
506 // a positive number?
507 if (FontSizeAdjust::Tag(aSizeAdjustBasis) ==
508 FontSizeAdjust::Tag::ExHeight) {
509 if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
510 int16_t(os2->sxHeight) > 0.1 * upem) {
511 return double(int16_t(os2->sxHeight)) / upem;
512 }
513 }
514 if (FontSizeAdjust::Tag(aSizeAdjustBasis) ==
515 FontSizeAdjust::Tag::CapHeight) {
516 if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) &&
517 int16_t(os2->sCapHeight) > 0.1 * upem) {
518 return double(int16_t(os2->sCapHeight)) / upem;
519 }
520 }
521 }
522 }
523 }
524 }
525
526 // create a font to calculate the requested aspect
527 gfxFontStyle s;
528 s.size = 256.0; // pick large size to reduce hinting artifacts
529 RefPtr<gfxFont> font = FindOrMakeFont(&s);
530 if (font) {
531 const gfxFont::Metrics& metrics =
532 font->GetMetrics(nsFontMetrics::eHorizontal);
533 if (metrics.emHeight == 0) {
534 return 0;
535 }
536 switch (FontSizeAdjust::Tag(aSizeAdjustBasis)) {
537 case FontSizeAdjust::Tag::ExHeight:
538 return metrics.xHeight / metrics.emHeight;
539 case FontSizeAdjust::Tag::CapHeight:
540 return metrics.capHeight / metrics.emHeight;
541 case FontSizeAdjust::Tag::ChWidth:
542 return metrics.zeroWidth > 0 ? metrics.zeroWidth / metrics.emHeight
543 : 0.5;
544 case FontSizeAdjust::Tag::IcWidth:
545 case FontSizeAdjust::Tag::IcHeight: {
546 bool vertical = FontSizeAdjust::Tag(aSizeAdjustBasis) ==
547 FontSizeAdjust::Tag::IcHeight;
548 gfxFloat advance = font->GetCharAdvance(0x6C34, vertical);
549 return advance > 0 ? advance / metrics.emHeight : 1.0;
550 }
551 default:
552 break;
553 }
554 }
555
556 MOZ_ASSERT_UNREACHABLE("failed to compute size-adjust aspect");
557 return 0.5;
558 }
559
PrepareFontOptions(FcPattern * aPattern,int * aOutLoadFlags,unsigned int * aOutSynthFlags)560 static void PrepareFontOptions(FcPattern* aPattern, int* aOutLoadFlags,
561 unsigned int* aOutSynthFlags) {
562 int loadFlags = FT_LOAD_DEFAULT;
563 unsigned int synthFlags = 0;
564
565 // xxx - taken from the gfxFontconfigFonts code, needs to be reviewed
566
567 FcBool printing;
568 if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) !=
569 FcResultMatch) {
570 printing = FcFalse;
571 }
572
573 // Font options are set explicitly here to improve cairo's caching
574 // behavior and to record the relevant parts of the pattern so that
575 // the pattern can be released.
576 //
577 // Most font_options have already been set as defaults on the FcPattern
578 // with cairo_ft_font_options_substitute(), then user and system
579 // fontconfig configurations were applied. The resulting font_options
580 // have been recorded on the face during
581 // cairo_ft_font_face_create_for_pattern().
582 //
583 // None of the settings here cause this scaled_font to behave any
584 // differently from how it would behave if it were created from the same
585 // face with default font_options.
586 //
587 // We set options explicitly so that the same scaled_font will be found in
588 // the cairo_scaled_font_map when cairo loads glyphs from a context with
589 // the same font_face, font_matrix, ctm, and surface font_options.
590 //
591 // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
592 // font_options on the cairo_ft_font_face, and doesn't consider default
593 // option values to not match any explicit values.
594 //
595 // Even after cairo_set_scaled_font is used to set font_options for the
596 // cairo context, when cairo looks for a scaled_font for the context, it
597 // will look for a font with some option values from the target surface if
598 // any values are left default on the context font_options. If this
599 // scaled_font is created with default font_options, cairo will not find
600 // it.
601 //
602 // The one option not recorded in the pattern is hint_metrics, which will
603 // affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON.
604 // We should be considering the font_options of the surface on which this
605 // font will be used, but currently we don't have different gfxFonts for
606 // different surface font_options, so we'll create a font suitable for the
607 // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
608
609 // The remaining options have been recorded on the pattern and the face.
610 // _cairo_ft_options_merge has some logic to decide which options from the
611 // scaled_font or from the cairo_ft_font_face take priority in the way the
612 // font behaves.
613 //
614 // In the majority of cases, _cairo_ft_options_merge uses the options from
615 // the cairo_ft_font_face, so sometimes it is not so important which
616 // values are set here so long as they are not defaults, but we'll set
617 // them to the exact values that we expect from the font, to be consistent
618 // and to protect against changes in cairo.
619 //
620 // In some cases, _cairo_ft_options_merge uses some options from the
621 // scaled_font's font_options rather than options on the
622 // cairo_ft_font_face (from fontconfig).
623 // https://bugs.freedesktop.org/show_bug.cgi?id=11838
624 //
625 // Surface font options were set on the pattern in
626 // cairo_ft_font_options_substitute. If fontconfig has changed the
627 // hint_style then that is what the user (or distribution) wants, so we
628 // use the setting from the FcPattern.
629 //
630 // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
631 FcBool hinting = FcFalse;
632 if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
633 hinting = FcTrue;
634 }
635
636 int fc_hintstyle = FC_HINT_NONE;
637 if (!printing && hinting &&
638 FcPatternGetInteger(aPattern, FC_HINT_STYLE, 0, &fc_hintstyle) !=
639 FcResultMatch) {
640 fc_hintstyle = FC_HINT_FULL;
641 }
642 switch (fc_hintstyle) {
643 case FC_HINT_NONE:
644 loadFlags = FT_LOAD_NO_HINTING;
645 break;
646 case FC_HINT_SLIGHT:
647 loadFlags = FT_LOAD_TARGET_LIGHT;
648 break;
649 }
650
651 FcBool fc_antialias;
652 if (FcPatternGetBool(aPattern, FC_ANTIALIAS, 0, &fc_antialias) !=
653 FcResultMatch) {
654 fc_antialias = FcTrue;
655 }
656 if (!fc_antialias) {
657 if (fc_hintstyle != FC_HINT_NONE) {
658 loadFlags = FT_LOAD_TARGET_MONO;
659 }
660 loadFlags |= FT_LOAD_MONOCHROME;
661 } else if (fc_hintstyle == FC_HINT_FULL) {
662 int fc_rgba;
663 if (FcPatternGetInteger(aPattern, FC_RGBA, 0, &fc_rgba) != FcResultMatch) {
664 fc_rgba = FC_RGBA_UNKNOWN;
665 }
666 switch (fc_rgba) {
667 case FC_RGBA_RGB:
668 case FC_RGBA_BGR:
669 loadFlags = FT_LOAD_TARGET_LCD;
670 break;
671 case FC_RGBA_VRGB:
672 case FC_RGBA_VBGR:
673 loadFlags = FT_LOAD_TARGET_LCD_V;
674 break;
675 }
676 }
677
678 FcBool bitmap;
679 if (FcPatternGetBool(aPattern, FC_EMBEDDED_BITMAP, 0, &bitmap) !=
680 FcResultMatch) {
681 bitmap = FcFalse;
682 }
683 if (fc_antialias && (fc_hintstyle == FC_HINT_NONE || !bitmap)) {
684 loadFlags |= FT_LOAD_NO_BITMAP;
685 }
686
687 FcBool autohint;
688 if (FcPatternGetBool(aPattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch &&
689 autohint) {
690 loadFlags |= FT_LOAD_FORCE_AUTOHINT;
691 }
692
693 FcBool embolden;
694 if (FcPatternGetBool(aPattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch &&
695 embolden) {
696 synthFlags |= CAIRO_FT_SYNTHESIZE_BOLD;
697 }
698
699 *aOutLoadFlags = loadFlags;
700 *aOutSynthFlags = synthFlags;
701 }
702
703 #ifdef MOZ_X11
GetXftInt(Display * aDisplay,const char * aName,int * aResult)704 static bool GetXftInt(Display* aDisplay, const char* aName, int* aResult) {
705 if (!aDisplay) {
706 return false;
707 }
708 char* value = XGetDefault(aDisplay, "Xft", aName);
709 if (!value) {
710 return false;
711 }
712 if (FcNameConstant(const_cast<FcChar8*>(ToFcChar8Ptr(value)), aResult)) {
713 return true;
714 }
715 char* end;
716 *aResult = strtol(value, &end, 0);
717 if (end != value) {
718 return true;
719 }
720 return false;
721 }
722 #endif
723
PreparePattern(FcPattern * aPattern,bool aIsPrinterFont)724 static void PreparePattern(FcPattern* aPattern, bool aIsPrinterFont) {
725 FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
726
727 // This gets cairo_font_options_t for the Screen. We should have
728 // different font options for printing (no hinting) but we are not told
729 // what we are measuring for.
730 //
731 // If cairo adds support for lcd_filter, gdk will not provide the default
732 // setting for that option. We could get the default setting by creating
733 // an xlib surface once, recording its font_options, and then merging the
734 // gdk options.
735 //
736 // Using an xlib surface would also be an option to get Screen font
737 // options for non-GTK X11 toolkits, but less efficient than using GDK to
738 // pick up dynamic changes.
739 if (aIsPrinterFont) {
740 cairo_font_options_t* options = cairo_font_options_create();
741 cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE);
742 cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY);
743 cairo_ft_font_options_substitute(options, aPattern);
744 cairo_font_options_destroy(options);
745 FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
746 #ifdef MOZ_WIDGET_GTK
747 } else {
748 gfxFcPlatformFontList::PlatformFontList()->SubstituteSystemFontOptions(
749 aPattern);
750 #endif // MOZ_WIDGET_GTK
751 }
752
753 FcDefaultSubstitute(aPattern);
754 }
755
MoveToFront(size_t aIndex)756 void gfxFontconfigFontEntry::UnscaledFontCache::MoveToFront(size_t aIndex) {
757 if (aIndex > 0) {
758 ThreadSafeWeakPtr<UnscaledFontFontconfig> front =
759 std::move(mUnscaledFonts[aIndex]);
760 for (size_t i = aIndex; i > 0; i--) {
761 mUnscaledFonts[i] = std::move(mUnscaledFonts[i - 1]);
762 }
763 mUnscaledFonts[0] = std::move(front);
764 }
765 }
766
767 already_AddRefed<UnscaledFontFontconfig>
Lookup(const std::string & aFile,uint32_t aIndex)768 gfxFontconfigFontEntry::UnscaledFontCache::Lookup(const std::string& aFile,
769 uint32_t aIndex) {
770 for (size_t i = 0; i < kNumEntries; i++) {
771 RefPtr<UnscaledFontFontconfig> entry(mUnscaledFonts[i]);
772 if (entry && entry->GetFile() == aFile && entry->GetIndex() == aIndex) {
773 MoveToFront(i);
774 return entry.forget();
775 }
776 }
777 return nullptr;
778 }
779
SizeForStyle(gfxFontconfigFontEntry * aEntry,const gfxFontStyle & aStyle)780 static inline gfxFloat SizeForStyle(gfxFontconfigFontEntry* aEntry,
781 const gfxFontStyle& aStyle) {
782 return StyleFontSizeAdjust::Tag(aStyle.sizeAdjustBasis) !=
783 StyleFontSizeAdjust::Tag::None
784 ? aStyle.GetAdjustedSize(aEntry->GetAspect(aStyle.sizeAdjustBasis))
785 : aStyle.size * aEntry->mSizeAdjust;
786 }
787
ChooseFontSize(gfxFontconfigFontEntry * aEntry,const gfxFontStyle & aStyle)788 static double ChooseFontSize(gfxFontconfigFontEntry* aEntry,
789 const gfxFontStyle& aStyle) {
790 double requestedSize = SizeForStyle(aEntry, aStyle);
791 double bestDist = -1.0;
792 double bestSize = requestedSize;
793 double size;
794 int v = 0;
795 while (FcPatternGetDouble(aEntry->GetPattern(), FC_PIXEL_SIZE, v, &size) ==
796 FcResultMatch) {
797 ++v;
798 double dist = fabs(size - requestedSize);
799 if (bestDist < 0.0 || dist < bestDist) {
800 bestDist = dist;
801 bestSize = size;
802 }
803 }
804 // If the font has bitmaps but wants to be scaled, then let it scale.
805 if (bestSize >= 0.0) {
806 FcBool scalable;
807 if (FcPatternGetBool(aEntry->GetPattern(), FC_SCALABLE, 0, &scalable) ==
808 FcResultMatch &&
809 scalable) {
810 return requestedSize;
811 }
812 }
813 return bestSize;
814 }
815
CreateFontInstance(const gfxFontStyle * aFontStyle)816 gfxFont* gfxFontconfigFontEntry::CreateFontInstance(
817 const gfxFontStyle* aFontStyle) {
818 RefPtr<FcPattern> pattern = dont_AddRef(FcPatternCreate());
819 if (!pattern) {
820 NS_WARNING("Failed to create Fontconfig pattern for font instance");
821 return nullptr;
822 }
823
824 double size = ChooseFontSize(this, *aFontStyle);
825 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
826
827 RefPtr<SharedFTFace> face = GetFTFace();
828 if (!face) {
829 NS_WARNING("Failed to get FreeType face for pattern");
830 return nullptr;
831 }
832 if (HasVariations()) {
833 // For variation fonts, we create a new FT_Face here so that
834 // variation coordinates from the style can be applied without
835 // affecting other font instances created from the same entry
836 // (font resource).
837 // For user fonts: create a new FT_Face from the font data, and then make
838 // a pattern from that.
839 // For system fonts: create a new FT_Face and store it in a copy of the
840 // original mFontPattern.
841 RefPtr<SharedFTFace> varFace = face->GetData()
842 ? face->GetData()->CloneFace()
843 : CreateFaceForPattern(mFontPattern);
844 if (varFace) {
845 AutoTArray<gfxFontVariation, 8> settings;
846 GetVariationsForStyle(settings, *aFontStyle);
847 gfxFT2FontBase::SetupVarCoords(GetMMVar(), settings, varFace->GetFace());
848 face = std::move(varFace);
849 }
850 }
851
852 PreparePattern(pattern, aFontStyle->printerFont);
853 RefPtr<FcPattern> renderPattern =
854 dont_AddRef(FcFontRenderPrepare(nullptr, pattern, mFontPattern));
855 if (!renderPattern) {
856 NS_WARNING("Failed to prepare Fontconfig pattern for font instance");
857 return nullptr;
858 }
859
860 if (aFontStyle->NeedsSyntheticBold(this)) {
861 FcPatternAddBool(renderPattern, FC_EMBOLDEN, FcTrue);
862 }
863
864 // will synthetic oblique be applied using a transform?
865 if (IsUpright() && aFontStyle->style != FontSlantStyle::Normal() &&
866 aFontStyle->allowSyntheticStyle) {
867 // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
868 FcPatternDel(renderPattern, FC_EMBEDDED_BITMAP);
869 FcPatternAddBool(renderPattern, FC_EMBEDDED_BITMAP, FcFalse);
870 }
871
872 int loadFlags;
873 unsigned int synthFlags;
874 PrepareFontOptions(renderPattern, &loadFlags, &synthFlags);
875
876 std::string file;
877 int index = 0;
878 if (!face->GetData()) {
879 const FcChar8* fcFile;
880 if (FcPatternGetString(renderPattern, FC_FILE, 0,
881 const_cast<FcChar8**>(&fcFile)) != FcResultMatch ||
882 FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index) !=
883 FcResultMatch) {
884 NS_WARNING("No file in Fontconfig pattern for font instance");
885 return nullptr;
886 }
887 file = ToCharPtr(fcFile);
888 }
889
890 RefPtr<UnscaledFontFontconfig> unscaledFont =
891 mUnscaledFontCache.Lookup(file, index);
892 if (!unscaledFont) {
893 unscaledFont = mFTFace->GetData() ? new UnscaledFontFontconfig(mFTFace)
894 : new UnscaledFontFontconfig(
895 std::move(file), index, mFTFace);
896 mUnscaledFontCache.Add(unscaledFont);
897 }
898
899 gfxFont* newFont = new gfxFontconfigFont(
900 unscaledFont, std::move(face), renderPattern, size, this, aFontStyle,
901 loadFlags, (synthFlags & CAIRO_FT_SYNTHESIZE_BOLD) != 0);
902
903 return newFont;
904 }
905
GetFTFace()906 const RefPtr<SharedFTFace>& gfxFontconfigFontEntry::GetFTFace() {
907 if (!mFTFaceInitialized) {
908 mFTFaceInitialized = true;
909 mFTFace = CreateFaceForPattern(mFontPattern);
910 }
911 return mFTFace;
912 }
913
GetUserFontData()914 FTUserFontData* gfxFontconfigFontEntry::GetUserFontData() {
915 if (mFTFace && mFTFace->GetData()) {
916 return static_cast<FTUserFontData*>(mFTFace->GetData());
917 }
918 return nullptr;
919 }
920
HasVariations()921 bool gfxFontconfigFontEntry::HasVariations() {
922 if (mHasVariationsInitialized) {
923 return mHasVariations;
924 }
925 mHasVariationsInitialized = true;
926 mHasVariations = false;
927
928 if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
929 return mHasVariations;
930 }
931
932 // For installed fonts, query the fontconfig pattern rather than paying
933 // the cost of loading a FT_Face that we otherwise might never need.
934 if (!IsUserFont() || IsLocalUserFont()) {
935 FcBool variable;
936 if ((FcPatternGetBool(mFontPattern, FC_VARIABLE, 0, &variable) ==
937 FcResultMatch) &&
938 variable) {
939 mHasVariations = true;
940 }
941 } else {
942 if (GetFTFace()) {
943 mHasVariations =
944 mFTFace->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS;
945 }
946 }
947
948 return mHasVariations;
949 }
950
GetMMVar()951 FT_MM_Var* gfxFontconfigFontEntry::GetMMVar() {
952 if (mMMVarInitialized) {
953 return mMMVar;
954 }
955 mMMVarInitialized = true;
956 InitializeVarFuncs();
957 if (!sGetVar) {
958 return nullptr;
959 }
960 if (!GetFTFace()) {
961 return nullptr;
962 }
963 if (FT_Err_Ok != (*sGetVar)(mFTFace->GetFace(), &mMMVar)) {
964 mMMVar = nullptr;
965 }
966 return mMMVar;
967 }
968
GetVariationAxes(nsTArray<gfxFontVariationAxis> & aAxes)969 void gfxFontconfigFontEntry::GetVariationAxes(
970 nsTArray<gfxFontVariationAxis>& aAxes) {
971 if (!HasVariations()) {
972 return;
973 }
974 gfxFT2Utils::GetVariationAxes(GetMMVar(), aAxes);
975 }
976
GetVariationInstances(nsTArray<gfxFontVariationInstance> & aInstances)977 void gfxFontconfigFontEntry::GetVariationInstances(
978 nsTArray<gfxFontVariationInstance>& aInstances) {
979 if (!HasVariations()) {
980 return;
981 }
982 gfxFT2Utils::GetVariationInstances(this, GetMMVar(), aInstances);
983 }
984
CopyFontTable(uint32_t aTableTag,nsTArray<uint8_t> & aBuffer)985 nsresult gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
986 nsTArray<uint8_t>& aBuffer) {
987 NS_ASSERTION(!mIsDataUserFont,
988 "data fonts should be reading tables directly from memory");
989 return gfxFT2FontEntryBase::CopyFaceTable(GetFTFace(), aTableTag, aBuffer);
990 }
991
FindStyleVariations(FontInfoData * aFontInfoData)992 void gfxFontconfigFontFamily::FindStyleVariations(FontInfoData* aFontInfoData) {
993 if (mHasStyles) {
994 return;
995 }
996
997 // add font entries for each of the faces
998 uint32_t numFonts = mFontPatterns.Length();
999 NS_ASSERTION(numFonts, "font family containing no faces!!");
1000 uint32_t numRegularFaces = 0;
1001 for (uint32_t i = 0; i < numFonts; i++) {
1002 FcPattern* face = mFontPatterns[i];
1003
1004 // figure out the psname/fullname and choose which to use as the facename
1005 nsAutoCString psname, fullname;
1006 GetFaceNames(face, mName, psname, fullname);
1007 const nsAutoCString& faceName = !psname.IsEmpty() ? psname : fullname;
1008
1009 gfxFontconfigFontEntry* fontEntry =
1010 new gfxFontconfigFontEntry(faceName, face, mContainsAppFonts);
1011
1012 if (gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
1013 fontEntry->SetupVariationRanges();
1014 }
1015
1016 AddFontEntry(fontEntry);
1017
1018 if (fontEntry->IsNormalStyle()) {
1019 numRegularFaces++;
1020 }
1021
1022 if (LOG_FONTLIST_ENABLED()) {
1023 nsAutoCString weightString;
1024 fontEntry->Weight().ToString(weightString);
1025 nsAutoCString stretchString;
1026 fontEntry->Stretch().ToString(stretchString);
1027 nsAutoCString styleString;
1028 fontEntry->SlantStyle().ToString(styleString);
1029 LOG_FONTLIST(
1030 ("(fontlist) added (%s) to family (%s)"
1031 " with style: %s weight: %s stretch: %s"
1032 " psname: %s fullname: %s",
1033 fontEntry->Name().get(), Name().get(), styleString.get(),
1034 weightString.get(), stretchString.get(), psname.get(),
1035 fullname.get()));
1036 }
1037 }
1038
1039 // somewhat arbitrary, but define a family with two or more regular
1040 // faces as a family for which intra-family fallback should be used
1041 if (numRegularFaces > 1) {
1042 mCheckForFallbackFaces = true;
1043 }
1044 mFaceNamesInitialized = true;
1045 mFontPatterns.Clear();
1046 SetHasStyles(true);
1047
1048 CheckForSimpleFamily();
1049 }
1050
AddFontPattern(FcPattern * aFontPattern,bool aSingleName)1051 void gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern,
1052 bool aSingleName) {
1053 NS_ASSERTION(
1054 !mHasStyles,
1055 "font patterns must not be added to already enumerated families");
1056
1057 FcBool outline;
1058 if (FcPatternGetBool(aFontPattern, FC_OUTLINE, 0, &outline) !=
1059 FcResultMatch ||
1060 !outline) {
1061 mHasNonScalableFaces = true;
1062
1063 FcBool scalable;
1064 if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) ==
1065 FcResultMatch &&
1066 scalable) {
1067 mForceScalable = true;
1068 }
1069 }
1070
1071 if (aSingleName) {
1072 mFontPatterns.InsertElementAt(mUniqueNameFaceCount++, aFontPattern);
1073 } else {
1074 mFontPatterns.AppendElement(aFontPattern);
1075 }
1076 }
1077
1078 static const double kRejectDistance = 10000.0;
1079
1080 // Calculate a distance score representing the size disparity between the
1081 // requested style's size and the font entry's size.
SizeDistance(gfxFontconfigFontEntry * aEntry,const gfxFontStyle & aStyle,bool aForceScalable)1082 static double SizeDistance(gfxFontconfigFontEntry* aEntry,
1083 const gfxFontStyle& aStyle, bool aForceScalable) {
1084 double requestedSize = SizeForStyle(aEntry, aStyle);
1085 double bestDist = -1.0;
1086 double size;
1087 int v = 0;
1088 while (FcPatternGetDouble(aEntry->GetPattern(), FC_PIXEL_SIZE, v, &size) ==
1089 FcResultMatch) {
1090 ++v;
1091 double dist = fabs(size - requestedSize);
1092 if (bestDist < 0.0 || dist < bestDist) {
1093 bestDist = dist;
1094 }
1095 }
1096 if (bestDist < 0.0) {
1097 // No size means scalable
1098 return -1.0;
1099 } else if (aForceScalable || 5.0 * bestDist < requestedSize) {
1100 // fontconfig prefers a matching family or lang to pixelsize of bitmap
1101 // fonts. CSS suggests a tolerance of 20% on pixelsize.
1102 return bestDist;
1103 } else {
1104 // Reject any non-scalable fonts that are not within tolerance.
1105 return kRejectDistance;
1106 }
1107 }
1108
FindAllFontsForStyle(const gfxFontStyle & aFontStyle,nsTArray<gfxFontEntry * > & aFontEntryList,bool aIgnoreSizeTolerance)1109 void gfxFontconfigFontFamily::FindAllFontsForStyle(
1110 const gfxFontStyle& aFontStyle, nsTArray<gfxFontEntry*>& aFontEntryList,
1111 bool aIgnoreSizeTolerance) {
1112 gfxFontFamily::FindAllFontsForStyle(aFontStyle, aFontEntryList,
1113 aIgnoreSizeTolerance);
1114
1115 if (!mHasNonScalableFaces) {
1116 return;
1117 }
1118
1119 // Iterate over the the available fonts while compacting any groups
1120 // of unscalable fonts with matching styles into a single entry
1121 // corresponding to the closest available size. If the closest
1122 // available size is rejected for being outside tolerance, then the
1123 // entire group will be skipped.
1124 size_t skipped = 0;
1125 gfxFontconfigFontEntry* bestEntry = nullptr;
1126 double bestDist = -1.0;
1127 for (size_t i = 0; i < aFontEntryList.Length(); i++) {
1128 gfxFontconfigFontEntry* entry =
1129 static_cast<gfxFontconfigFontEntry*>(aFontEntryList[i]);
1130 double dist =
1131 SizeDistance(entry, aFontStyle, mForceScalable || aIgnoreSizeTolerance);
1132 // If the entry is scalable or has a style that does not match
1133 // the group of unscalable fonts, then start a new group.
1134 if (dist < 0.0 || !bestEntry || bestEntry->Stretch() != entry->Stretch() ||
1135 bestEntry->Weight() != entry->Weight() ||
1136 bestEntry->SlantStyle() != entry->SlantStyle()) {
1137 // If the best entry in this group is still outside the tolerance,
1138 // then skip the entire group.
1139 if (bestDist >= kRejectDistance) {
1140 skipped++;
1141 }
1142 // Remove any compacted entries from the previous group.
1143 if (skipped) {
1144 i -= skipped;
1145 aFontEntryList.RemoveElementsAt(i, skipped);
1146 skipped = 0;
1147 }
1148 // Mark the start of the new group.
1149 bestEntry = entry;
1150 bestDist = dist;
1151 } else {
1152 // If this entry more closely matches the requested size than the
1153 // current best in the group, then take this entry instead.
1154 if (dist < bestDist) {
1155 aFontEntryList[i - 1 - skipped] = entry;
1156 bestEntry = entry;
1157 bestDist = dist;
1158 }
1159 skipped++;
1160 }
1161 }
1162 // If the best entry in this group is still outside the tolerance,
1163 // then skip the entire group.
1164 if (bestDist >= kRejectDistance) {
1165 skipped++;
1166 }
1167 // Remove any compacted entries from the current group.
1168 if (skipped) {
1169 aFontEntryList.TruncateLength(aFontEntryList.Length() - skipped);
1170 }
1171 }
1172
PatternHasLang(const FcPattern * aPattern,const FcChar8 * aLang)1173 static bool PatternHasLang(const FcPattern* aPattern, const FcChar8* aLang) {
1174 FcLangSet* langset;
1175
1176 if (FcPatternGetLangSet(aPattern, FC_LANG, 0, &langset) != FcResultMatch) {
1177 return false;
1178 }
1179
1180 if (FcLangSetHasLang(langset, aLang) != FcLangDifferentLang) {
1181 return true;
1182 }
1183 return false;
1184 }
1185
SupportsLangGroup(nsAtom * aLangGroup) const1186 bool gfxFontconfigFontFamily::SupportsLangGroup(nsAtom* aLangGroup) const {
1187 if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) {
1188 return true;
1189 }
1190
1191 nsAutoCString fcLang;
1192 gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
1193 pfl->GetSampleLangForGroup(aLangGroup, fcLang);
1194 if (fcLang.IsEmpty()) {
1195 return true;
1196 }
1197
1198 // Before FindStyleVariations has been called, mFontPatterns will contain
1199 // the font patterns. Afterward, it'll be empty, but mAvailableFonts
1200 // will contain the font entries, each of which holds a reference to its
1201 // pattern. We only check the first pattern in each list, because support
1202 // for langs is considered to be consistent across all faces in a family.
1203 FcPattern* fontPattern;
1204 if (mFontPatterns.Length()) {
1205 fontPattern = mFontPatterns[0];
1206 } else if (mAvailableFonts.Length()) {
1207 fontPattern = static_cast<gfxFontconfigFontEntry*>(mAvailableFonts[0].get())
1208 ->GetPattern();
1209 } else {
1210 return true;
1211 }
1212
1213 // is lang included in the underlying pattern?
1214 return PatternHasLang(fontPattern, ToFcChar8Ptr(fcLang.get()));
1215 }
1216
1217 /* virtual */
~gfxFontconfigFontFamily()1218 gfxFontconfigFontFamily::~gfxFontconfigFontFamily() {
1219 // Should not be dropped by stylo
1220 MOZ_ASSERT(NS_IsMainThread());
1221 }
1222
1223 template <typename Func>
AddFacesToFontList(Func aAddPatternFunc)1224 void gfxFontconfigFontFamily::AddFacesToFontList(Func aAddPatternFunc) {
1225 if (HasStyles()) {
1226 for (auto& fe : mAvailableFonts) {
1227 if (!fe) {
1228 continue;
1229 }
1230 auto fce = static_cast<gfxFontconfigFontEntry*>(fe.get());
1231 aAddPatternFunc(fce->GetPattern(), mContainsAppFonts);
1232 }
1233 } else {
1234 for (auto& pat : mFontPatterns) {
1235 aAddPatternFunc(pat, mContainsAppFonts);
1236 }
1237 }
1238 }
1239
gfxFontconfigFont(const RefPtr<UnscaledFontFontconfig> & aUnscaledFont,RefPtr<SharedFTFace> && aFTFace,FcPattern * aPattern,gfxFloat aAdjustedSize,gfxFontEntry * aFontEntry,const gfxFontStyle * aFontStyle,int aLoadFlags,bool aEmbolden)1240 gfxFontconfigFont::gfxFontconfigFont(
1241 const RefPtr<UnscaledFontFontconfig>& aUnscaledFont,
1242 RefPtr<SharedFTFace>&& aFTFace, FcPattern* aPattern, gfxFloat aAdjustedSize,
1243 gfxFontEntry* aFontEntry, const gfxFontStyle* aFontStyle, int aLoadFlags,
1244 bool aEmbolden)
1245 : gfxFT2FontBase(aUnscaledFont, std::move(aFTFace), aFontEntry, aFontStyle,
1246 aLoadFlags, aEmbolden),
1247 mPattern(aPattern) {
1248 mAdjustedSize = aAdjustedSize;
1249 InitMetrics();
1250 }
1251
1252 gfxFontconfigFont::~gfxFontconfigFont() = default;
1253
GetScaledFont(mozilla::gfx::DrawTarget * aTarget)1254 already_AddRefed<ScaledFont> gfxFontconfigFont::GetScaledFont(
1255 mozilla::gfx::DrawTarget* aTarget) {
1256 if (!mAzureScaledFont) {
1257 mAzureScaledFont = Factory::CreateScaledFontForFontconfigFont(
1258 GetUnscaledFont(), GetAdjustedSize(), mFTFace, GetPattern());
1259 InitializeScaledFont();
1260 }
1261
1262 RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
1263 return scaledFont.forget();
1264 }
1265
ShouldHintMetrics() const1266 bool gfxFontconfigFont::ShouldHintMetrics() const {
1267 return !GetStyle()->printerFont;
1268 }
1269
gfxFcPlatformFontList()1270 gfxFcPlatformFontList::gfxFcPlatformFontList()
1271 : mLocalNames(64),
1272 mGenericMappings(32),
1273 mFcSubstituteCache(64),
1274 mLastConfig(nullptr),
1275 mAlwaysUseFontconfigGenerics(true) {
1276 CheckFamilyList(kBaseFonts_Ubuntu_20_04,
1277 ArrayLength(kBaseFonts_Ubuntu_20_04));
1278 CheckFamilyList(kLangFonts_Ubuntu_20_04,
1279 ArrayLength(kLangFonts_Ubuntu_20_04));
1280 CheckFamilyList(kBaseFonts_Fedora_32, ArrayLength(kBaseFonts_Fedora_32));
1281 mLastConfig = FcConfigGetCurrent();
1282 if (XRE_IsParentProcess()) {
1283 // if the rescan interval is set, start the timer
1284 int rescanInterval = FcConfigGetRescanInterval(nullptr);
1285 if (rescanInterval) {
1286 NS_NewTimerWithFuncCallback(
1287 getter_AddRefs(mCheckFontUpdatesTimer), CheckFontUpdates, this,
1288 (rescanInterval + 1) * 1000, nsITimer::TYPE_REPEATING_SLACK,
1289 "gfxFcPlatformFontList::gfxFcPlatformFontList");
1290 if (!mCheckFontUpdatesTimer) {
1291 NS_WARNING("Failure to create font updates timer");
1292 }
1293 }
1294 }
1295
1296 #ifdef MOZ_BUNDLED_FONTS
1297 mBundledFontsInitialized = false;
1298 #endif
1299 }
1300
~gfxFcPlatformFontList()1301 gfxFcPlatformFontList::~gfxFcPlatformFontList() {
1302 if (mCheckFontUpdatesTimer) {
1303 mCheckFontUpdatesTimer->Cancel();
1304 mCheckFontUpdatesTimer = nullptr;
1305 }
1306 #ifdef MOZ_WIDGET_GTK
1307 ClearSystemFontOptions();
1308 #endif
1309 }
1310
AddFontSetFamilies(FcFontSet * aFontSet,const SandboxPolicy * aPolicy,bool aAppFonts)1311 void gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet,
1312 const SandboxPolicy* aPolicy,
1313 bool aAppFonts) {
1314 // This iterates over the fonts in a font set and adds in gfxFontFamily
1315 // objects for each family. Individual gfxFontEntry objects for each face
1316 // are not created here; the patterns are just stored in the family. When
1317 // a family is actually used, it will be populated with gfxFontEntry
1318 // records and the patterns moved to those.
1319
1320 if (!aFontSet) {
1321 NS_WARNING("AddFontSetFamilies called with a null font set.");
1322 return;
1323 }
1324
1325 FcChar8* lastFamilyName = (FcChar8*)"";
1326 RefPtr<gfxFontconfigFontFamily> fontFamily;
1327 nsAutoCString familyName;
1328 for (int f = 0; f < aFontSet->nfont; f++) {
1329 FcPattern* pattern = aFontSet->fonts[f];
1330
1331 // Skip any fonts that aren't readable for us (e.g. due to restrictive
1332 // file ownership/permissions).
1333 FcChar8* path;
1334 if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
1335 continue;
1336 }
1337 if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
1338 continue;
1339 }
1340
1341 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
1342 // Skip any fonts that will be blocked by the content-process sandbox
1343 // policy.
1344 if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
1345 SandboxBroker::Perms::MAY_READ)) {
1346 continue;
1347 }
1348 #endif
1349
1350 AddPatternToFontList(pattern, lastFamilyName, familyName, fontFamily,
1351 aAppFonts);
1352 }
1353 }
1354
AddPatternToFontList(FcPattern * aFont,FcChar8 * & aLastFamilyName,nsACString & aFamilyName,RefPtr<gfxFontconfigFontFamily> & aFontFamily,bool aAppFonts)1355 void gfxFcPlatformFontList::AddPatternToFontList(
1356 FcPattern* aFont, FcChar8*& aLastFamilyName, nsACString& aFamilyName,
1357 RefPtr<gfxFontconfigFontFamily>& aFontFamily, bool aAppFonts) {
1358 // get canonical name
1359 uint32_t cIndex = FindCanonicalNameIndex(aFont, FC_FAMILYLANG);
1360 FcChar8* canonical = nullptr;
1361 FcPatternGetString(aFont, FC_FAMILY, cIndex, &canonical);
1362 if (!canonical) {
1363 return;
1364 }
1365
1366 // same as the last one? no need to add a new family, skip
1367 if (FcStrCmp(canonical, aLastFamilyName) != 0) {
1368 aLastFamilyName = canonical;
1369
1370 // add new family if one doesn't already exist
1371 aFamilyName.Truncate();
1372 aFamilyName = ToCharPtr(canonical);
1373 nsAutoCString keyName(aFamilyName);
1374 ToLowerCase(keyName);
1375
1376 aFontFamily = static_cast<gfxFontconfigFontFamily*>(
1377 mFontFamilies
1378 .LookupOrInsertWith(keyName,
1379 [&] {
1380 FontVisibility visibility =
1381 aAppFonts
1382 ? FontVisibility::Base
1383 : GetVisibilityForFamily(keyName);
1384 return MakeRefPtr<gfxFontconfigFontFamily>(
1385 aFamilyName, visibility);
1386 })
1387 .get());
1388 // Record if the family contains fonts from the app font set
1389 // (in which case we won't rely on fontconfig's charmap, due to
1390 // bug 1276594).
1391 if (aAppFonts) {
1392 aFontFamily->SetFamilyContainsAppFonts(true);
1393 }
1394 }
1395
1396 // Add pointers to other localized family names. Most fonts
1397 // only have a single name, so the first call to GetString
1398 // will usually not match
1399 FcChar8* otherName;
1400 int n = (cIndex == 0 ? 1 : 0);
1401 while (FcPatternGetString(aFont, FC_FAMILY, n, &otherName) == FcResultMatch) {
1402 nsAutoCString otherFamilyName(ToCharPtr(otherName));
1403 AddOtherFamilyName(aFontFamily, otherFamilyName);
1404 n++;
1405 if (n == int(cIndex)) {
1406 n++; // skip over canonical name
1407 }
1408 }
1409
1410 const bool singleName = n == 1;
1411
1412 MOZ_ASSERT(aFontFamily, "font must belong to a font family");
1413 aFontFamily->AddFontPattern(aFont, singleName);
1414
1415 // map the psname, fullname ==> font family for local font lookups
1416 nsAutoCString psname, fullname;
1417 GetFaceNames(aFont, aFamilyName, psname, fullname);
1418 if (!psname.IsEmpty()) {
1419 ToLowerCase(psname);
1420 mLocalNames.InsertOrUpdate(psname, RefPtr{aFont});
1421 }
1422 if (!fullname.IsEmpty()) {
1423 ToLowerCase(fullname);
1424 mLocalNames.WithEntryHandle(fullname, [&](auto&& entry) {
1425 if (entry && !singleName) {
1426 return;
1427 }
1428 entry.InsertOrUpdate(RefPtr{aFont});
1429 });
1430 }
1431 }
1432
InitFontListForPlatform()1433 nsresult gfxFcPlatformFontList::InitFontListForPlatform() {
1434 #ifdef MOZ_BUNDLED_FONTS
1435 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1436 ActivateBundledFonts();
1437 }
1438 #endif
1439
1440 mLocalNames.Clear();
1441 mFcSubstituteCache.Clear();
1442
1443 ClearSystemFontOptions();
1444
1445 mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
1446 mOtherFamilyNamesInitialized = true;
1447
1448 mLastConfig = FcConfigGetCurrent();
1449
1450 if (XRE_IsContentProcess()) {
1451 // Content process: use the font list passed from the chrome process,
1452 // because we can't rely on fontconfig in the presence of sandboxing;
1453 // it may report fonts that we can't actually access.
1454
1455 FcChar8* lastFamilyName = (FcChar8*)"";
1456 RefPtr<gfxFontconfigFontFamily> fontFamily;
1457 nsAutoCString familyName;
1458
1459 // Get font list that was passed during XPCOM startup
1460 // or in an UpdateFontList message.
1461 auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
1462
1463 #ifdef MOZ_WIDGET_GTK
1464 UpdateSystemFontOptionsFromIpc(fontList.options());
1465 #endif
1466
1467 // For fontconfig versions between 2.10.94 and 2.11.1 inclusive,
1468 // we need to escape any leading space in the charset element,
1469 // otherwise FcNameParse will fail. :(
1470 //
1471 // The bug was introduced on 2013-05-24 by
1472 // https://cgit.freedesktop.org/fontconfig/commit/?id=cd9b1033a68816a7acfbba1718ba0aa5888f6ec7
1473 // "Bug 64906 - FcNameParse() should ignore leading whitespace in
1474 // parameters"
1475 // because ignoring a leading space in the encoded value of charset
1476 // causes erroneous decoding of the whole element.
1477 // This first shipped in version 2.10.94, and was eventually fixed as
1478 // a side-effect of switching to the "human-readable" representation of
1479 // charsets on 2014-07-03 in
1480 // https://cgit.freedesktop.org/fontconfig/commit/?id=e708e97c351d3bc9f7030ef22ac2f007d5114730
1481 // "Change charset parse/unparse format to be human readable"
1482 // (with a followup fix next day) which means a leading space is no
1483 // longer significant. This fix landed after 2.11.1 had been shipped,
1484 // so the first version tag without the bug is 2.11.91.
1485 int fcVersion = FcGetVersion();
1486 bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;
1487
1488 for (FontPatternListEntry& fpe : fontList.entries()) {
1489 nsCString& patternStr = fpe.pattern();
1490 if (fcCharsetParseBug) {
1491 int32_t index = patternStr.Find(":charset= ");
1492 if (index != kNotFound) {
1493 // insert backslash after the =, before the space
1494 patternStr.Insert('\\', index + 9);
1495 }
1496 }
1497 FcPattern* pattern = FcNameParse((const FcChar8*)patternStr.get());
1498 AddPatternToFontList(pattern, lastFamilyName, familyName, fontFamily,
1499 fpe.appFontFamily());
1500 FcPatternDestroy(pattern);
1501 }
1502
1503 LOG_FONTLIST(
1504 ("got font list from chrome process: "
1505 "%u faces in %u families",
1506 (unsigned)fontList.entries().Length(), mFontFamilies.Count()));
1507
1508 fontList.entries().Clear();
1509 return NS_OK;
1510 }
1511
1512 UpdateSystemFontOptions();
1513
1514 UniquePtr<SandboxPolicy> policy;
1515
1516 #if defined(MOZ_SANDBOX) && defined(XP_LINUX)
1517 // If read sandboxing is enabled, create a temporary SandboxPolicy to
1518 // check font paths; use a fake PID to avoid picking up any PID-specific
1519 // rules by accident.
1520 SandboxBrokerPolicyFactory policyFactory;
1521 if (GetEffectiveContentSandboxLevel() > 2 &&
1522 !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
1523 policy = policyFactory.GetContentPolicy(-1, false);
1524 }
1525 #endif
1526
1527 #ifdef MOZ_BUNDLED_FONTS
1528 // https://bugzilla.mozilla.org/show_bug.cgi?id=1745715:
1529 // It's important to do this *before* processing the standard system fonts,
1530 // so that if a family is present in both font sets, we'll treat it as app-
1531 // bundled and therefore always visible.
1532 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1533 FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
1534 AddFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
1535 }
1536 #endif
1537
1538 // iterate over available fonts
1539 FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
1540 AddFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false);
1541
1542 return NS_OK;
1543 }
1544
ReadSystemFontList(dom::SystemFontList * retValue)1545 void gfxFcPlatformFontList::ReadSystemFontList(dom::SystemFontList* retValue) {
1546 // Fontconfig versions below 2.9 drop the FC_FILE element in FcNameUnparse
1547 // (see https://bugs.freedesktop.org/show_bug.cgi?id=26718), so when using
1548 // an older version, we manually append it to the unparsed pattern.
1549 #ifdef MOZ_WIDGET_GTK
1550 SystemFontOptionsToIpc(retValue->options());
1551 #endif
1552
1553 if (FcGetVersion() < 20900) {
1554 for (const auto& entry : mFontFamilies) {
1555 auto* family = static_cast<gfxFontconfigFontFamily*>(entry.GetWeak());
1556 family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
1557 char* s = (char*)FcNameUnparse(aPat);
1558 nsDependentCString patternStr(s);
1559 char* file = nullptr;
1560 if (FcResultMatch ==
1561 FcPatternGetString(aPat, FC_FILE, 0, (FcChar8**)&file)) {
1562 patternStr.Append(":file=");
1563 patternStr.Append(file);
1564 }
1565 retValue->entries().AppendElement(
1566 FontPatternListEntry(patternStr, aAppFonts));
1567 free(s);
1568 });
1569 }
1570 } else {
1571 for (const auto& entry : mFontFamilies) {
1572 auto* family = static_cast<gfxFontconfigFontFamily*>(entry.GetWeak());
1573 family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
1574 char* s = (char*)FcNameUnparse(aPat);
1575 nsDependentCString patternStr(s);
1576 retValue->entries().AppendElement(
1577 FontPatternListEntry(patternStr, aAppFonts));
1578 free(s);
1579 });
1580 }
1581 }
1582 }
1583
1584 // Per family array of faces.
1585 class FacesData {
1586 using FaceInitArray = AutoTArray<fontlist::Face::InitData, 8>;
1587
1588 FaceInitArray mFaces;
1589
1590 // Number of faces that have a single name. Faces that have multiple names are
1591 // sorted last.
1592 uint32_t mUniqueNameFaceCount = 0;
1593
1594 public:
Add(fontlist::Face::InitData && aData,bool aSingleName)1595 void Add(fontlist::Face::InitData&& aData, bool aSingleName) {
1596 if (aSingleName) {
1597 mFaces.InsertElementAt(mUniqueNameFaceCount++, std::move(aData));
1598 } else {
1599 mFaces.AppendElement(std::move(aData));
1600 }
1601 }
1602
Get() const1603 const FaceInitArray& Get() const { return mFaces; }
1604 };
1605
InitSharedFontListForPlatform()1606 void gfxFcPlatformFontList::InitSharedFontListForPlatform() {
1607 mLocalNames.Clear();
1608 mFcSubstituteCache.Clear();
1609
1610 mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
1611 mOtherFamilyNamesInitialized = true;
1612
1613 mLastConfig = FcConfigGetCurrent();
1614
1615 if (!XRE_IsParentProcess()) {
1616 #ifdef MOZ_WIDGET_GTK
1617 auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
1618 UpdateSystemFontOptionsFromIpc(fontList.options());
1619 #endif
1620 // Content processes will access the shared-memory data created by the
1621 // parent, so they do not need to query fontconfig for the available
1622 // fonts themselves.
1623 return;
1624 }
1625
1626 #ifdef MOZ_WIDGET_GTK
1627 UpdateSystemFontOptions();
1628 #endif
1629
1630 #ifdef MOZ_BUNDLED_FONTS
1631 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1632 TimeStamp start = TimeStamp::Now();
1633 ActivateBundledFonts();
1634 TimeStamp end = TimeStamp::Now();
1635 Telemetry::Accumulate(Telemetry::FONTLIST_BUNDLEDFONTS_ACTIVATE,
1636 (end - start).ToMilliseconds());
1637 }
1638 #endif
1639
1640 UniquePtr<SandboxPolicy> policy;
1641
1642 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
1643 // If read sandboxing is enabled, create a temporary SandboxPolicy to
1644 // check font paths; use a fake PID to avoid picking up any PID-specific
1645 // rules by accident.
1646 SandboxBrokerPolicyFactory policyFactory;
1647 if (GetEffectiveContentSandboxLevel() > 2 &&
1648 !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
1649 policy = policyFactory.GetContentPolicy(-1, false);
1650 }
1651 #endif
1652
1653 nsTArray<fontlist::Family::InitData> families;
1654
1655 nsClassHashtable<nsCStringHashKey, FacesData> faces;
1656
1657 // Do we need to work around the fontconfig FcNameParse/FcNameUnparse bug
1658 // (present in versions between 2.10.94 and 2.11.1 inclusive)? See comment
1659 // in InitFontListForPlatform for details.
1660 int fcVersion = FcGetVersion();
1661 bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;
1662
1663 auto addPattern = [this, fcCharsetParseBug, &families, &faces](
1664 FcPattern* aPattern, FcChar8*& aLastFamilyName,
1665 nsCString& aFamilyName, bool aAppFont) -> void {
1666 // get canonical name
1667 uint32_t cIndex = FindCanonicalNameIndex(aPattern, FC_FAMILYLANG);
1668 FcChar8* canonical = nullptr;
1669 FcPatternGetString(aPattern, FC_FAMILY, cIndex, &canonical);
1670 if (!canonical) {
1671 return;
1672 }
1673
1674 nsAutoCString keyName;
1675 keyName = ToCharPtr(canonical);
1676 ToLowerCase(keyName);
1677
1678 aLastFamilyName = canonical;
1679 aFamilyName = ToCharPtr(canonical);
1680
1681 // Same canonical family name as the last one? Definitely no need to add a
1682 // new family record.
1683 auto* faceList =
1684 faces
1685 .LookupOrInsertWith(
1686 keyName,
1687 [&] {
1688 FontVisibility visibility =
1689 aAppFont ? FontVisibility::Base
1690 : GetVisibilityForFamily(keyName);
1691 families.AppendElement(fontlist::Family::InitData(
1692 keyName, aFamilyName, fontlist::Family::kNoIndex,
1693 visibility,
1694 /*bundled*/ aAppFont, /*badUnderline*/ false));
1695 return MakeUnique<FacesData>();
1696 })
1697 .get();
1698
1699 char* s = (char*)FcNameUnparse(aPattern);
1700 nsAutoCString descriptor(s);
1701 free(s);
1702
1703 if (fcCharsetParseBug) {
1704 // Escape any leading space in charset to work around FcNameParse bug.
1705 int32_t index = descriptor.Find(":charset= ");
1706 if (index != kNotFound) {
1707 // insert backslash after the =, before the space
1708 descriptor.Insert('\\', index + 9);
1709 }
1710 }
1711
1712 WeightRange weight(FontWeight::Normal());
1713 StretchRange stretch(FontStretch::Normal());
1714 SlantStyleRange style(FontSlantStyle::Normal());
1715 uint16_t size;
1716 GetFontProperties(aPattern, &weight, &stretch, &style, &size);
1717
1718 auto initData = fontlist::Face::InitData{descriptor, 0, size, false,
1719 weight, stretch, style};
1720
1721 // Add entries for any other localized family names. (Most fonts only have
1722 // a single family name, so the first call to GetString will usually fail).
1723 FcChar8* otherName;
1724 int n = (cIndex == 0 ? 1 : 0);
1725 while (FcPatternGetString(aPattern, FC_FAMILY, n, &otherName) ==
1726 FcResultMatch) {
1727 nsAutoCString otherFamilyName(ToCharPtr(otherName));
1728 keyName = otherFamilyName;
1729 ToLowerCase(keyName);
1730
1731 faces
1732 .LookupOrInsertWith(
1733 keyName,
1734 [&] {
1735 FontVisibility visibility =
1736 aAppFont ? FontVisibility::Base
1737 : GetVisibilityForFamily(keyName);
1738 families.AppendElement(fontlist::Family::InitData(
1739 keyName, otherFamilyName, fontlist::Family::kNoIndex,
1740 visibility,
1741 /*bundled*/ aAppFont, /*badUnderline*/ false));
1742
1743 return MakeUnique<FacesData>();
1744 })
1745 .get()
1746 ->Add(fontlist::Face::InitData(initData), /* singleName = */ false);
1747
1748 n++;
1749 if (n == int(cIndex)) {
1750 n++; // skip over canonical name
1751 }
1752 }
1753
1754 const bool singleName = n == 1;
1755 faceList->Add(std::move(initData), singleName);
1756
1757 // map the psname, fullname ==> font family for local font lookups
1758 nsAutoCString psname, fullname;
1759 GetFaceNames(aPattern, aFamilyName, psname, fullname);
1760 if (!psname.IsEmpty()) {
1761 ToLowerCase(psname);
1762 mLocalNameTable.InsertOrUpdate(
1763 psname, fontlist::LocalFaceRec::InitData(keyName, descriptor));
1764 }
1765 if (!fullname.IsEmpty()) {
1766 ToLowerCase(fullname);
1767 if (fullname != psname) {
1768 mLocalNameTable.WithEntryHandle(fullname, [&](auto&& entry) {
1769 if (entry && !singleName) {
1770 // We only override an existing entry if this is the only way to
1771 // name this family. This prevents dubious aliases from clobbering
1772 // the local name table.
1773 return;
1774 }
1775 entry.InsertOrUpdate(
1776 fontlist::LocalFaceRec::InitData(keyName, descriptor));
1777 });
1778 }
1779 }
1780 };
1781
1782 auto addFontSetFamilies = [&addPattern](FcFontSet* aFontSet,
1783 SandboxPolicy* aPolicy,
1784 bool aAppFonts) -> void {
1785 FcChar8* lastFamilyName = (FcChar8*)"";
1786 RefPtr<gfxFontconfigFontFamily> fontFamily;
1787 nsAutoCString familyName;
1788 for (int f = 0; f < aFontSet->nfont; f++) {
1789 FcPattern* pattern = aFontSet->fonts[f];
1790
1791 // Skip any fonts that aren't readable for us (e.g. due to restrictive
1792 // file ownership/permissions).
1793 FcChar8* path;
1794 if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
1795 continue;
1796 }
1797 if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
1798 continue;
1799 }
1800
1801 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
1802 // Skip any fonts that will be blocked by the content-process sandbox
1803 // policy.
1804 if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
1805 SandboxBroker::Perms::MAY_READ)) {
1806 continue;
1807 }
1808 #endif
1809
1810 // If this is a TrueType or OpenType font, discard the FC_CHARSET object
1811 // (which may be very large), because we'll read the 'cmap' directly.
1812 // This substantially reduces the pressure on shared memory (bug 1664151)
1813 // due to the large font descriptors (serialized patterns).
1814 FcChar8* fontFormat;
1815 if (FcPatternGetString(pattern, FC_FONTFORMAT, 0, &fontFormat) ==
1816 FcResultMatch &&
1817 (!FcStrCmp(fontFormat, (const FcChar8*)"TrueType") ||
1818 !FcStrCmp(fontFormat, (const FcChar8*)"CFF"))) {
1819 FcPattern* clone = FcPatternDuplicate(pattern);
1820 FcPatternDel(clone, FC_CHARSET);
1821 addPattern(clone, lastFamilyName, familyName, aAppFonts);
1822 FcPatternDestroy(clone);
1823 } else {
1824 addPattern(pattern, lastFamilyName, familyName, aAppFonts);
1825 }
1826 }
1827 };
1828
1829 // iterate over available fonts
1830 FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
1831 addFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false);
1832
1833 #ifdef MOZ_BUNDLED_FONTS
1834 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() != 0) {
1835 FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
1836 addFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
1837 }
1838 #endif
1839
1840 mozilla::fontlist::FontList* list = SharedFontList();
1841 list->SetFamilyNames(families);
1842
1843 for (uint32_t i = 0; i < families.Length(); i++) {
1844 list->Families()[i].AddFaces(list, faces.Get(families[i].mKey)->Get());
1845 }
1846 }
1847
GetDistroID() const1848 gfxFcPlatformFontList::DistroID gfxFcPlatformFontList::GetDistroID() const {
1849 // Helper called to initialize sResult the first time this is used.
1850 auto getDistroID = []() {
1851 DistroID result = DistroID::Unknown;
1852 FILE* fp = fopen("/etc/os-release", "r");
1853 if (fp) {
1854 char buf[512];
1855 while (fgets(buf, sizeof(buf), fp)) {
1856 if (strncmp(buf, "ID=", 3) == 0) {
1857 if (strncmp(buf + 3, "ubuntu", 6) == 0) {
1858 result = DistroID::Ubuntu;
1859 } else if (strncmp(buf + 3, "fedora", 6) == 0) {
1860 result = DistroID::Fedora;
1861 }
1862 break;
1863 }
1864 }
1865 fclose(fp);
1866 }
1867 return result;
1868 };
1869 static DistroID sResult = getDistroID();
1870 return sResult;
1871 }
1872
GetVisibilityForFamily(const nsACString & aName) const1873 FontVisibility gfxFcPlatformFontList::GetVisibilityForFamily(
1874 const nsACString& aName) const {
1875 switch (GetDistroID()) {
1876 case DistroID::Ubuntu:
1877 if (FamilyInList(aName, kBaseFonts_Ubuntu_20_04,
1878 ArrayLength(kBaseFonts_Ubuntu_20_04))) {
1879 return FontVisibility::Base;
1880 }
1881 if (FamilyInList(aName, kLangFonts_Ubuntu_20_04,
1882 ArrayLength(kLangFonts_Ubuntu_20_04))) {
1883 return FontVisibility::LangPack;
1884 }
1885 return FontVisibility::User;
1886 case DistroID::Fedora:
1887 if (FamilyInList(aName, kBaseFonts_Fedora_32,
1888 ArrayLength(kBaseFonts_Fedora_32))) {
1889 return FontVisibility::Base;
1890 }
1891 return FontVisibility::User;
1892 default:
1893 // We don't know how to categorize fonts on this system
1894 return FontVisibility::Unknown;
1895 }
1896 }
1897
CreateFontEntry(fontlist::Face * aFace,const fontlist::Family * aFamily)1898 gfxFontEntry* gfxFcPlatformFontList::CreateFontEntry(
1899 fontlist::Face* aFace, const fontlist::Family* aFamily) {
1900 nsAutoCString desc(aFace->mDescriptor.AsString(SharedFontList()));
1901 FcPattern* pattern = FcNameParse((const FcChar8*)desc.get());
1902 auto* fe = new gfxFontconfigFontEntry(desc, pattern, true);
1903 FcPatternDestroy(pattern);
1904 fe->InitializeFrom(aFace, aFamily);
1905 return fe;
1906 }
1907
1908 // For displaying the fontlist in UI, use explicit call to FcFontList. Using
1909 // FcFontList results in the list containing the localized names as dictated
1910 // by system defaults.
GetSystemFontList(nsTArray<nsString> & aListOfFonts,nsAtom * aLangGroup)1911 static void GetSystemFontList(nsTArray<nsString>& aListOfFonts,
1912 nsAtom* aLangGroup) {
1913 aListOfFonts.Clear();
1914
1915 RefPtr<FcPattern> pat = dont_AddRef(FcPatternCreate());
1916 if (!pat) {
1917 return;
1918 }
1919
1920 UniquePtr<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
1921 if (!os) {
1922 return;
1923 }
1924
1925 // add the lang to the pattern
1926 nsAutoCString fcLang;
1927 gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
1928 pfl->GetSampleLangForGroup(aLangGroup, fcLang,
1929 /*aForFontEnumerationThread*/ true);
1930 if (!fcLang.IsEmpty()) {
1931 FcPatternAddString(pat, FC_LANG, ToFcChar8Ptr(fcLang.get()));
1932 }
1933
1934 UniquePtr<FcFontSet> fs(FcFontList(nullptr, pat, os.get()));
1935 if (!fs) {
1936 return;
1937 }
1938
1939 for (int i = 0; i < fs->nfont; i++) {
1940 char* family;
1941
1942 if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, (FcChar8**)&family) !=
1943 FcResultMatch) {
1944 continue;
1945 }
1946
1947 // Remove duplicates...
1948 nsAutoString strFamily;
1949 AppendUTF8toUTF16(MakeStringSpan(family), strFamily);
1950 if (aListOfFonts.Contains(strFamily)) {
1951 continue;
1952 }
1953
1954 aListOfFonts.AppendElement(strFamily);
1955 }
1956
1957 aListOfFonts.Sort();
1958 }
1959
GetFontList(nsAtom * aLangGroup,const nsACString & aGenericFamily,nsTArray<nsString> & aListOfFonts)1960 void gfxFcPlatformFontList::GetFontList(nsAtom* aLangGroup,
1961 const nsACString& aGenericFamily,
1962 nsTArray<nsString>& aListOfFonts) {
1963 // Get the list of font family names using fontconfig
1964 GetSystemFontList(aListOfFonts, aLangGroup);
1965
1966 // Under Linux, the generics "serif", "sans-serif" and "monospace"
1967 // are included in the pref fontlist. These map to whatever fontconfig
1968 // decides they should be for a given language, rather than one of the
1969 // fonts listed in the prefs font lists (e.g. font.name.*, font.name-list.*)
1970 bool serif = false, sansSerif = false, monospace = false;
1971 if (aGenericFamily.IsEmpty())
1972 serif = sansSerif = monospace = true;
1973 else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
1974 serif = true;
1975 else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
1976 sansSerif = true;
1977 else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
1978 monospace = true;
1979 else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
1980 aGenericFamily.LowerCaseEqualsLiteral("fantasy"))
1981 serif = sansSerif = true;
1982 else
1983 MOZ_ASSERT_UNREACHABLE("unexpected CSS generic font family");
1984
1985 // The first in the list becomes the default in
1986 // FontBuilder.readFontSelection() if the preference-selected font is not
1987 // available, so put system configured defaults first.
1988 if (monospace) aListOfFonts.InsertElementAt(0, u"monospace"_ns);
1989 if (sansSerif) aListOfFonts.InsertElementAt(0, u"sans-serif"_ns);
1990 if (serif) aListOfFonts.InsertElementAt(0, u"serif"_ns);
1991 }
1992
GetDefaultFontForPlatform(const gfxFontStyle * aStyle,nsAtom * aLanguage)1993 FontFamily gfxFcPlatformFontList::GetDefaultFontForPlatform(
1994 const gfxFontStyle* aStyle, nsAtom* aLanguage) {
1995 // Get the default font by using a fake name to retrieve the first
1996 // scalable font that fontconfig suggests for the given language.
1997 PrefFontList* prefFonts = FindGenericFamilies(
1998 "-moz-default"_ns, aLanguage ? aLanguage : nsGkAtoms::x_western);
1999 NS_ASSERTION(prefFonts, "null list of generic fonts");
2000 if (prefFonts && !prefFonts->IsEmpty()) {
2001 return (*prefFonts)[0];
2002 }
2003 return FontFamily();
2004 }
2005
LookupLocalFont(const nsACString & aFontName,WeightRange aWeightForEntry,StretchRange aStretchForEntry,SlantStyleRange aStyleForEntry)2006 gfxFontEntry* gfxFcPlatformFontList::LookupLocalFont(
2007 const nsACString& aFontName, WeightRange aWeightForEntry,
2008 StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry) {
2009 nsAutoCString keyName(aFontName);
2010 ToLowerCase(keyName);
2011
2012 if (SharedFontList()) {
2013 return LookupInSharedFaceNameList(aFontName, aWeightForEntry,
2014 aStretchForEntry, aStyleForEntry);
2015 }
2016
2017 // if name is not in the global list, done
2018 const auto fontPattern = mLocalNames.Lookup(keyName);
2019 if (!fontPattern) {
2020 return nullptr;
2021 }
2022
2023 return new gfxFontconfigFontEntry(aFontName, *fontPattern, aWeightForEntry,
2024 aStretchForEntry, aStyleForEntry);
2025 }
2026
MakePlatformFont(const nsACString & aFontName,WeightRange aWeightForEntry,StretchRange aStretchForEntry,SlantStyleRange aStyleForEntry,const uint8_t * aFontData,uint32_t aLength)2027 gfxFontEntry* gfxFcPlatformFontList::MakePlatformFont(
2028 const nsACString& aFontName, WeightRange aWeightForEntry,
2029 StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry,
2030 const uint8_t* aFontData, uint32_t aLength) {
2031 RefPtr<FTUserFontData> ufd = new FTUserFontData(aFontData, aLength);
2032 RefPtr<SharedFTFace> face = ufd->CloneFace();
2033 if (!face) {
2034 return nullptr;
2035 }
2036 return new gfxFontconfigFontEntry(aFontName, aWeightForEntry,
2037 aStretchForEntry, aStyleForEntry,
2038 std::move(face));
2039 }
2040
FindAndAddFamilies(StyleGenericFontFamily aGeneric,const nsACString & aFamily,nsTArray<FamilyAndGeneric> * aOutput,FindFamiliesFlags aFlags,gfxFontStyle * aStyle,nsAtom * aLanguage,gfxFloat aDevToCssSize)2041 bool gfxFcPlatformFontList::FindAndAddFamilies(
2042 StyleGenericFontFamily aGeneric, const nsACString& aFamily,
2043 nsTArray<FamilyAndGeneric>* aOutput, FindFamiliesFlags aFlags,
2044 gfxFontStyle* aStyle, nsAtom* aLanguage, gfxFloat aDevToCssSize) {
2045 nsAutoCString familyName(aFamily);
2046 ToLowerCase(familyName);
2047
2048 if (!(aFlags & FindFamiliesFlags::eQuotedFamilyName)) {
2049 // deprecated generic names are explicitly converted to standard generics
2050 bool isDeprecatedGeneric = false;
2051 if (familyName.EqualsLiteral("sans") ||
2052 familyName.EqualsLiteral("sans serif")) {
2053 familyName.AssignLiteral("sans-serif");
2054 isDeprecatedGeneric = true;
2055 } else if (familyName.EqualsLiteral("mono")) {
2056 familyName.AssignLiteral("monospace");
2057 isDeprecatedGeneric = true;
2058 }
2059
2060 // fontconfig generics? use fontconfig to determine the family for lang
2061 if (isDeprecatedGeneric ||
2062 mozilla::StyleSingleFontFamily::Parse(familyName).IsGeneric()) {
2063 PrefFontList* prefFonts = FindGenericFamilies(familyName, aLanguage);
2064 if (prefFonts && !prefFonts->IsEmpty()) {
2065 aOutput->AppendElements(*prefFonts);
2066 return true;
2067 }
2068 return false;
2069 }
2070 }
2071
2072 // fontconfig allows conditional substitutions in such a way that it's
2073 // difficult to distinguish an explicit substitution from other suggested
2074 // choices. To sniff out explicit substitutions, compare the substitutions
2075 // for "font, -moz-sentinel" to "-moz-sentinel" to sniff out the
2076 // substitutions
2077 //
2078 // Example:
2079 //
2080 // serif ==> DejaVu Serif, ...
2081 // Helvetica, serif ==> Helvetica, TeX Gyre Heros, Nimbus Sans L, DejaVu
2082 // Serif
2083 //
2084 // In this case fontconfig is including Tex Gyre Heros and
2085 // Nimbus Sans L as alternatives for Helvetica.
2086
2087 // Because the FcConfigSubstitute call is quite expensive, we cache the
2088 // actual font families found via this process. So check the cache first:
2089 if (auto cachedFamilies = mFcSubstituteCache.Lookup(familyName)) {
2090 if (cachedFamilies->IsEmpty()) {
2091 return false;
2092 }
2093 aOutput->AppendElements(*cachedFamilies);
2094 return true;
2095 }
2096
2097 // It wasn't in the cache, so we need to ask fontconfig...
2098 const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel");
2099 FcChar8* sentinelFirstFamily = nullptr;
2100 RefPtr<FcPattern> sentinelSubst = dont_AddRef(FcPatternCreate());
2101 FcPatternAddString(sentinelSubst, FC_FAMILY, kSentinelName);
2102 FcConfigSubstitute(nullptr, sentinelSubst, FcMatchPattern);
2103 FcPatternGetString(sentinelSubst, FC_FAMILY, 0, &sentinelFirstFamily);
2104
2105 // substitutions for font, -moz-sentinel pattern
2106 RefPtr<FcPattern> fontWithSentinel = dont_AddRef(FcPatternCreate());
2107 FcPatternAddString(fontWithSentinel, FC_FAMILY,
2108 ToFcChar8Ptr(familyName.get()));
2109 FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName);
2110 FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern);
2111
2112 // Add all font family matches until reaching the sentinel.
2113 AutoTArray<FamilyAndGeneric, 10> cachedFamilies;
2114 FcChar8* substName = nullptr;
2115 for (int i = 0; FcPatternGetString(fontWithSentinel, FC_FAMILY, i,
2116 &substName) == FcResultMatch;
2117 i++) {
2118 if (sentinelFirstFamily && FcStrCmp(substName, sentinelFirstFamily) == 0) {
2119 break;
2120 }
2121 gfxPlatformFontList::FindAndAddFamilies(
2122 aGeneric, nsDependentCString(ToCharPtr(substName)), &cachedFamilies,
2123 aFlags, aStyle, aLanguage);
2124 }
2125
2126 // Cache the resulting list, so we don't have to do this again.
2127 const auto& insertedCachedFamilies =
2128 mFcSubstituteCache.InsertOrUpdate(familyName, std::move(cachedFamilies));
2129
2130 if (insertedCachedFamilies.IsEmpty()) {
2131 return false;
2132 }
2133 aOutput->AppendElements(insertedCachedFamilies);
2134 return true;
2135 }
2136
GetStandardFamilyName(const nsCString & aFontName,nsACString & aFamilyName)2137 bool gfxFcPlatformFontList::GetStandardFamilyName(const nsCString& aFontName,
2138 nsACString& aFamilyName) {
2139 aFamilyName.Truncate();
2140
2141 // The fontconfig list of fonts includes generic family names in the
2142 // font list. For these, just use the generic name.
2143 if (aFontName.EqualsLiteral("serif") ||
2144 aFontName.EqualsLiteral("sans-serif") ||
2145 aFontName.EqualsLiteral("monospace")) {
2146 aFamilyName.Assign(aFontName);
2147 return true;
2148 }
2149
2150 RefPtr<FcPattern> pat = dont_AddRef(FcPatternCreate());
2151 if (!pat) {
2152 return true;
2153 }
2154
2155 UniquePtr<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
2156 if (!os) {
2157 return true;
2158 }
2159
2160 // add the family name to the pattern
2161 FcPatternAddString(pat, FC_FAMILY, ToFcChar8Ptr(aFontName.get()));
2162
2163 UniquePtr<FcFontSet> givenFS(FcFontList(nullptr, pat, os.get()));
2164 if (!givenFS) {
2165 return true;
2166 }
2167
2168 // See if there is a font face with first family equal to the given family
2169 // (needs to be in sync with names coming from GetFontList())
2170 nsTArray<nsCString> candidates;
2171 for (int i = 0; i < givenFS->nfont; i++) {
2172 char* firstFamily;
2173
2174 if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
2175 (FcChar8**)&firstFamily) != FcResultMatch) {
2176 continue;
2177 }
2178
2179 nsDependentCString first(firstFamily);
2180 if (!candidates.Contains(first)) {
2181 candidates.AppendElement(first);
2182
2183 if (aFontName.Equals(first)) {
2184 aFamilyName.Assign(aFontName);
2185 return true;
2186 }
2187 }
2188 }
2189
2190 // Because fontconfig conflates different family name types, need to
2191 // double check that the candidate name is not simply a different
2192 // name type. For example, if a font with nameID=16 "Minion Pro" and
2193 // nameID=21 "Minion Pro Caption" exists, calling FcFontList with
2194 // family="Minion Pro" will return a set of patterns some of which
2195 // will have a first family of "Minion Pro Caption". Ignore these
2196 // patterns and use the first candidate that maps to a font set with
2197 // the same number of faces and an identical set of patterns.
2198 for (uint32_t j = 0; j < candidates.Length(); ++j) {
2199 FcPatternDel(pat, FC_FAMILY);
2200 FcPatternAddString(pat, FC_FAMILY, (FcChar8*)candidates[j].get());
2201
2202 UniquePtr<FcFontSet> candidateFS(FcFontList(nullptr, pat, os.get()));
2203 if (!candidateFS) {
2204 return true;
2205 }
2206
2207 if (candidateFS->nfont != givenFS->nfont) {
2208 continue;
2209 }
2210
2211 bool equal = true;
2212 for (int i = 0; i < givenFS->nfont; ++i) {
2213 if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
2214 equal = false;
2215 break;
2216 }
2217 }
2218 if (equal) {
2219 aFamilyName = candidates[j];
2220 return true;
2221 }
2222 }
2223
2224 // didn't find localized name, leave family name blank
2225 return true;
2226 }
2227
AddGenericFonts(StyleGenericFontFamily aGenericType,nsAtom * aLanguage,nsTArray<FamilyAndGeneric> & aFamilyList)2228 void gfxFcPlatformFontList::AddGenericFonts(
2229 StyleGenericFontFamily aGenericType, nsAtom* aLanguage,
2230 nsTArray<FamilyAndGeneric>& aFamilyList) {
2231 bool usePrefFontList = false;
2232
2233 const char* generic = GetGenericName(aGenericType);
2234 NS_ASSERTION(generic, "weird generic font type");
2235 if (!generic) {
2236 return;
2237 }
2238
2239 // By default, most font prefs on Linux map to "use fontconfig"
2240 // keywords. So only need to explicitly lookup font pref if
2241 // non-default settings exist
2242 nsAutoCString genericToLookup(generic);
2243 if ((!mAlwaysUseFontconfigGenerics && aLanguage) ||
2244 aLanguage == nsGkAtoms::x_math) {
2245 nsAtom* langGroup = GetLangGroup(aLanguage);
2246 nsAutoString fontlistValue;
2247 Preferences::GetString(NamePref(generic, langGroup).get(), fontlistValue);
2248 nsresult rv;
2249 if (fontlistValue.IsEmpty()) {
2250 // The font name list may have two or more family names as comma
2251 // separated list. In such case, not matching with generic font
2252 // name is fine because if the list prefers specific font, we
2253 // should try to use the pref with complicated path.
2254 rv = Preferences::GetString(NameListPref(generic, langGroup).get(),
2255 fontlistValue);
2256 } else {
2257 rv = NS_OK;
2258 }
2259 if (NS_SUCCEEDED(rv)) {
2260 if (!fontlistValue.EqualsLiteral("serif") &&
2261 !fontlistValue.EqualsLiteral("sans-serif") &&
2262 !fontlistValue.EqualsLiteral("monospace")) {
2263 usePrefFontList = true;
2264 } else {
2265 // serif, sans-serif or monospace was specified
2266 genericToLookup.Truncate();
2267 AppendUTF16toUTF8(fontlistValue, genericToLookup);
2268 }
2269 }
2270 }
2271
2272 // when pref fonts exist, use standard pref font lookup
2273 if (usePrefFontList) {
2274 return gfxPlatformFontList::AddGenericFonts(aGenericType, aLanguage,
2275 aFamilyList);
2276 }
2277
2278 PrefFontList* prefFonts = FindGenericFamilies(genericToLookup, aLanguage);
2279 NS_ASSERTION(prefFonts, "null generic font list");
2280 aFamilyList.SetCapacity(aFamilyList.Length() + prefFonts->Length());
2281 for (auto& f : *prefFonts) {
2282 aFamilyList.AppendElement(FamilyAndGeneric(f, aGenericType));
2283 }
2284 }
2285
ClearLangGroupPrefFonts()2286 void gfxFcPlatformFontList::ClearLangGroupPrefFonts() {
2287 ClearGenericMappings();
2288 gfxPlatformFontList::ClearLangGroupPrefFonts();
2289 mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
2290 }
2291
FindGenericFamilies(const nsCString & aGeneric,nsAtom * aLanguage)2292 gfxPlatformFontList::PrefFontList* gfxFcPlatformFontList::FindGenericFamilies(
2293 const nsCString& aGeneric, nsAtom* aLanguage) {
2294 // set up name
2295 nsAutoCString fcLang;
2296 GetSampleLangForGroup(aLanguage, fcLang);
2297 ToLowerCase(fcLang);
2298
2299 nsAutoCString genericLang(aGeneric);
2300 if (fcLang.Length() > 0) {
2301 genericLang.Append('-');
2302 }
2303 genericLang.Append(fcLang);
2304
2305 // try to get the family from the cache
2306 return mGenericMappings.WithEntryHandle(
2307 genericLang, [&](auto&& entry) -> PrefFontList* {
2308 if (!entry) {
2309 // if not found, ask fontconfig to pick the appropriate font
2310 RefPtr<FcPattern> genericPattern = dont_AddRef(FcPatternCreate());
2311 FcPatternAddString(genericPattern, FC_FAMILY,
2312 ToFcChar8Ptr(aGeneric.get()));
2313
2314 // -- prefer scalable fonts
2315 FcPatternAddBool(genericPattern, FC_SCALABLE, FcTrue);
2316
2317 // -- add the lang to the pattern
2318 if (!fcLang.IsEmpty()) {
2319 FcPatternAddString(genericPattern, FC_LANG,
2320 ToFcChar8Ptr(fcLang.get()));
2321 }
2322
2323 // -- perform substitutions
2324 FcConfigSubstitute(nullptr, genericPattern, FcMatchPattern);
2325 FcDefaultSubstitute(genericPattern);
2326
2327 // -- sort to get the closest matches
2328 FcResult result;
2329 UniquePtr<FcFontSet> faces(
2330 FcFontSort(nullptr, genericPattern, FcFalse, nullptr, &result));
2331
2332 if (!faces) {
2333 return nullptr;
2334 }
2335
2336 // -- select the fonts to be used for the generic
2337 auto prefFonts = MakeUnique<PrefFontList>(); // can be empty but in
2338 // practice won't happen
2339 uint32_t limit =
2340 gfxPlatformGtk::GetPlatform()->MaxGenericSubstitions();
2341 bool foundFontWithLang = false;
2342 for (int i = 0; i < faces->nfont; i++) {
2343 FcPattern* font = faces->fonts[i];
2344 FcChar8* mappedGeneric = nullptr;
2345
2346 FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric);
2347 if (mappedGeneric) {
2348 nsAutoCString mappedGenericName(ToCharPtr(mappedGeneric));
2349 AutoTArray<FamilyAndGeneric, 1> genericFamilies;
2350 if (gfxPlatformFontList::FindAndAddFamilies(
2351 StyleGenericFontFamily::None, mappedGenericName,
2352 &genericFamilies, FindFamiliesFlags(0))) {
2353 MOZ_ASSERT(genericFamilies.Length() == 1,
2354 "expected a single family");
2355 if (!prefFonts->Contains(genericFamilies[0].mFamily)) {
2356 prefFonts->AppendElement(genericFamilies[0].mFamily);
2357 bool foundLang =
2358 !fcLang.IsEmpty() &&
2359 PatternHasLang(font, ToFcChar8Ptr(fcLang.get()));
2360 foundFontWithLang = foundFontWithLang || foundLang;
2361 // check to see if the list is full
2362 if (prefFonts->Length() >= limit) {
2363 break;
2364 }
2365 }
2366 }
2367 }
2368 }
2369
2370 // if no font in the list matches the lang, trim all but the first one
2371 if (!prefFonts->IsEmpty() && !foundFontWithLang) {
2372 prefFonts->TruncateLength(1);
2373 }
2374
2375 entry.Insert(std::move(prefFonts));
2376 }
2377 return entry->get();
2378 });
2379 }
2380
PrefFontListsUseOnlyGenerics()2381 bool gfxFcPlatformFontList::PrefFontListsUseOnlyGenerics() {
2382 static const char kFontNamePrefix[] = "font.name.";
2383
2384 bool prefFontsUseOnlyGenerics = true;
2385 nsTArray<nsCString> names;
2386 nsresult rv =
2387 Preferences::GetRootBranch()->GetChildList(kFontNamePrefix, names);
2388 if (NS_SUCCEEDED(rv)) {
2389 for (auto& name : names) {
2390 // Check whether all font.name prefs map to generic keywords
2391 // and that the pref name and keyword match.
2392 // Ex: font.name.serif.ar ==> "serif" (ok)
2393 // Ex: font.name.serif.ar ==> "monospace" (return false)
2394 // Ex: font.name.serif.ar ==> "DejaVu Serif" (return false)
2395 // Ex: font.name.serif.ar ==> "" and
2396 // font.name-list.serif.ar ==> "serif" (ok)
2397 // Ex: font.name.serif.ar ==> "" and
2398 // font.name-list.serif.ar ==> "Something, serif"
2399 // (return false)
2400
2401 nsDependentCSubstring prefName =
2402 Substring(name, ArrayLength(kFontNamePrefix) - 1);
2403 nsCCharSeparatedTokenizer tokenizer(prefName, '.');
2404 const nsDependentCSubstring& generic = tokenizer.nextToken();
2405 const nsDependentCSubstring& langGroup = tokenizer.nextToken();
2406 nsAutoCString fontPrefValue;
2407 Preferences::GetCString(name.get(), fontPrefValue);
2408 if (fontPrefValue.IsEmpty()) {
2409 // The font name list may have two or more family names as comma
2410 // separated list. In such case, not matching with generic font
2411 // name is fine because if the list prefers specific font, this
2412 // should return false.
2413 Preferences::GetCString(NameListPref(generic, langGroup).get(),
2414 fontPrefValue);
2415 }
2416
2417 if (!langGroup.EqualsLiteral("x-math") &&
2418 !generic.Equals(fontPrefValue)) {
2419 prefFontsUseOnlyGenerics = false;
2420 break;
2421 }
2422 }
2423 }
2424 return prefFontsUseOnlyGenerics;
2425 }
2426
2427 /* static */
CheckFontUpdates(nsITimer * aTimer,void * aThis)2428 void gfxFcPlatformFontList::CheckFontUpdates(nsITimer* aTimer, void* aThis) {
2429 // A content process is not supposed to check this directly;
2430 // it will be notified by the parent when the font list changes.
2431 MOZ_ASSERT(XRE_IsParentProcess());
2432
2433 // check for font updates
2434 FcInitBringUptoDate();
2435
2436 // update fontlist if current config changed
2437 gfxFcPlatformFontList* pfl = static_cast<gfxFcPlatformFontList*>(aThis);
2438 FcConfig* current = FcConfigGetCurrent();
2439 if (current != pfl->GetLastConfig()) {
2440 pfl->UpdateFontList();
2441 pfl->ForceGlobalReflow();
2442
2443 mozilla::dom::ContentParent::NotifyUpdatedFonts(true);
2444 }
2445 }
2446
CreateFontFamily(const nsACString & aName,FontVisibility aVisibility) const2447 gfxFontFamily* gfxFcPlatformFontList::CreateFontFamily(
2448 const nsACString& aName, FontVisibility aVisibility) const {
2449 return new gfxFontconfigFontFamily(aName, aVisibility);
2450 }
2451
2452 // mapping of moz lang groups ==> default lang
2453 struct MozLangGroupData {
2454 nsAtom* const& mozLangGroup;
2455 const char* defaultLang;
2456 };
2457
2458 const MozLangGroupData MozLangGroups[] = {
2459 {nsGkAtoms::x_western, "en"}, {nsGkAtoms::x_cyrillic, "ru"},
2460 {nsGkAtoms::x_devanagari, "hi"}, {nsGkAtoms::x_tamil, "ta"},
2461 {nsGkAtoms::x_armn, "hy"}, {nsGkAtoms::x_beng, "bn"},
2462 {nsGkAtoms::x_cans, "iu"}, {nsGkAtoms::x_ethi, "am"},
2463 {nsGkAtoms::x_geor, "ka"}, {nsGkAtoms::x_gujr, "gu"},
2464 {nsGkAtoms::x_guru, "pa"}, {nsGkAtoms::x_khmr, "km"},
2465 {nsGkAtoms::x_knda, "kn"}, {nsGkAtoms::x_mlym, "ml"},
2466 {nsGkAtoms::x_orya, "or"}, {nsGkAtoms::x_sinh, "si"},
2467 {nsGkAtoms::x_tamil, "ta"}, {nsGkAtoms::x_telu, "te"},
2468 {nsGkAtoms::x_tibt, "bo"}, {nsGkAtoms::Unicode, 0}};
2469
TryLangForGroup(const nsACString & aOSLang,nsAtom * aLangGroup,nsACString & aFcLang,bool aForFontEnumerationThread)2470 bool gfxFcPlatformFontList::TryLangForGroup(const nsACString& aOSLang,
2471 nsAtom* aLangGroup,
2472 nsACString& aFcLang,
2473 bool aForFontEnumerationThread) {
2474 // Truncate at '.' or '@' from aOSLang, and convert '_' to '-'.
2475 // aOSLang is in the form "language[_territory][.codeset][@modifier]".
2476 // fontconfig takes languages in the form "language-territory".
2477 // nsLanguageAtomService takes languages in the form language-subtag,
2478 // where subtag may be a territory. fontconfig and nsLanguageAtomService
2479 // handle case-conversion for us.
2480 const char *pos, *end;
2481 aOSLang.BeginReading(pos);
2482 aOSLang.EndReading(end);
2483 aFcLang.Truncate();
2484 while (pos < end) {
2485 switch (*pos) {
2486 case '.':
2487 case '@':
2488 end = pos;
2489 break;
2490 case '_':
2491 aFcLang.Append('-');
2492 break;
2493 default:
2494 aFcLang.Append(*pos);
2495 }
2496 ++pos;
2497 }
2498
2499 if (!aForFontEnumerationThread) {
2500 nsAtom* atom = mLangService->LookupLanguage(aFcLang);
2501 return atom == aLangGroup;
2502 }
2503
2504 // If we were called by the font enumeration thread, we can't use
2505 // mLangService->LookupLanguage because it is not thread-safe.
2506 // Use GetUncachedLanguageGroup to avoid unsafe access to the lang-group
2507 // mapping cache hashtable.
2508 nsAutoCString lowered(aFcLang);
2509 ToLowerCase(lowered);
2510 RefPtr<nsAtom> lang = NS_Atomize(lowered);
2511 RefPtr<nsAtom> group = mLangService->GetUncachedLanguageGroup(lang);
2512 return group.get() == aLangGroup;
2513 }
2514
GetSampleLangForGroup(nsAtom * aLanguage,nsACString & aLangStr,bool aForFontEnumerationThread)2515 void gfxFcPlatformFontList::GetSampleLangForGroup(
2516 nsAtom* aLanguage, nsACString& aLangStr, bool aForFontEnumerationThread) {
2517 aLangStr.Truncate();
2518 if (!aLanguage) {
2519 return;
2520 }
2521
2522 // set up lang string
2523 const MozLangGroupData* mozLangGroup = nullptr;
2524
2525 // -- look it up in the list of moz lang groups
2526 for (unsigned int i = 0; i < ArrayLength(MozLangGroups); ++i) {
2527 if (aLanguage == MozLangGroups[i].mozLangGroup) {
2528 mozLangGroup = &MozLangGroups[i];
2529 break;
2530 }
2531 }
2532
2533 // -- not a mozilla lang group? Just return the BCP47 string
2534 // representation of the lang group
2535 if (!mozLangGroup) {
2536 // Not a special mozilla language group.
2537 // Use aLanguage as a language code.
2538 aLanguage->ToUTF8String(aLangStr);
2539 return;
2540 }
2541
2542 // -- check the environment for the user's preferred language that
2543 // corresponds to this mozilla lang group.
2544 const char* languages = getenv("LANGUAGE");
2545 if (languages) {
2546 const char separator = ':';
2547
2548 for (const char* pos = languages; true; ++pos) {
2549 if (*pos == '\0' || *pos == separator) {
2550 if (languages < pos &&
2551 TryLangForGroup(Substring(languages, pos), aLanguage, aLangStr,
2552 aForFontEnumerationThread)) {
2553 return;
2554 }
2555
2556 if (*pos == '\0') {
2557 break;
2558 }
2559
2560 languages = pos + 1;
2561 }
2562 }
2563 }
2564 const char* ctype = setlocale(LC_CTYPE, nullptr);
2565 if (ctype && TryLangForGroup(nsDependentCString(ctype), aLanguage, aLangStr,
2566 aForFontEnumerationThread)) {
2567 return;
2568 }
2569
2570 if (mozLangGroup->defaultLang) {
2571 aLangStr.Assign(mozLangGroup->defaultLang);
2572 } else {
2573 aLangStr.Truncate();
2574 }
2575 }
2576
2577 #ifdef MOZ_BUNDLED_FONTS
ActivateBundledFonts()2578 void gfxFcPlatformFontList::ActivateBundledFonts() {
2579 if (!mBundledFontsInitialized) {
2580 mBundledFontsInitialized = true;
2581 nsCOMPtr<nsIFile> localDir;
2582 nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
2583 if (NS_FAILED(rv)) {
2584 return;
2585 }
2586 if (NS_FAILED(localDir->Append(u"fonts"_ns))) {
2587 return;
2588 }
2589 bool isDir;
2590 if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
2591 return;
2592 }
2593 if (NS_FAILED(localDir->GetNativePath(mBundledFontsPath))) {
2594 return;
2595 }
2596 }
2597 if (!mBundledFontsPath.IsEmpty()) {
2598 FcConfigAppFontAddDir(nullptr, ToFcChar8Ptr(mBundledFontsPath.get()));
2599 }
2600 }
2601 #endif
2602
2603 #ifdef MOZ_WIDGET_GTK
2604 /***************************************************************************
2605 *
2606 * These functions must be last in the file because it uses the system cairo
2607 * library. Above this point the cairo library used is the tree cairo if
2608 * MOZ_TREE_CAIRO.
2609 */
2610
2611 # if MOZ_TREE_CAIRO
2612 // Tree cairo symbols have different names. Disable their activation through
2613 // preprocessor macros.
2614 # undef cairo_ft_font_options_substitute
2615
2616 # undef cairo_font_options_create
2617 # undef cairo_font_options_destroy
2618 # undef cairo_font_options_copy
2619 # undef cairo_font_options_equal
2620
2621 # undef cairo_font_options_get_antialias
2622 # undef cairo_font_options_set_antialias
2623 # undef cairo_font_options_get_hint_style
2624 # undef cairo_font_options_set_hint_style
2625 # undef cairo_font_options_get_lcd_filter
2626 # undef cairo_font_options_set_lcd_filter
2627 # undef cairo_font_options_get_subpixel_order
2628 # undef cairo_font_options_set_subpixel_order
2629
2630 // The system cairo functions are not declared because the include paths cause
2631 // the gdk headers to pick up the tree cairo.h.
2632 extern "C" {
2633 NS_VISIBILITY_DEFAULT void cairo_ft_font_options_substitute(
2634 const cairo_font_options_t* options, FcPattern* pattern);
2635
2636 NS_VISIBILITY_DEFAULT cairo_font_options_t* cairo_font_options_copy(
2637 const cairo_font_options_t*);
2638 NS_VISIBILITY_DEFAULT cairo_font_options_t* cairo_font_options_create();
2639 NS_VISIBILITY_DEFAULT void cairo_font_options_destroy(cairo_font_options_t*);
2640 NS_VISIBILITY_DEFAULT cairo_bool_t cairo_font_options_equal(
2641 const cairo_font_options_t*, const cairo_font_options_t*);
2642
2643 NS_VISIBILITY_DEFAULT cairo_antialias_t
2644 cairo_font_options_get_antialias(const cairo_font_options_t*);
2645 NS_VISIBILITY_DEFAULT void cairo_font_options_set_antialias(
2646 cairo_font_options_t*, cairo_antialias_t);
2647 NS_VISIBILITY_DEFAULT cairo_hint_style_t
2648 cairo_font_options_get_hint_style(const cairo_font_options_t*);
2649 NS_VISIBILITY_DEFAULT void cairo_font_options_set_hint_style(
2650 cairo_font_options_t*, cairo_hint_style_t);
2651 NS_VISIBILITY_DEFAULT cairo_subpixel_order_t
2652 cairo_font_options_get_subpixel_order(const cairo_font_options_t*);
2653 NS_VISIBILITY_DEFAULT void cairo_font_options_set_subpixel_order(
2654 cairo_font_options_t*, cairo_subpixel_order_t);
2655 }
2656 # endif
2657
ClearSystemFontOptions()2658 void gfxFcPlatformFontList::ClearSystemFontOptions() {
2659 if (mSystemFontOptions) {
2660 cairo_font_options_destroy(mSystemFontOptions);
2661 mSystemFontOptions = nullptr;
2662 }
2663 }
2664
UpdateSystemFontOptions()2665 bool gfxFcPlatformFontList::UpdateSystemFontOptions() {
2666 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
2667
2668 if (gfxPlatform::IsHeadless()) {
2669 return false;
2670 }
2671
2672 # ifdef MOZ_X11
2673 {
2674 // This one shouldn't change during the X session.
2675 int lcdfilter;
2676 GdkDisplay* dpy = gdk_display_get_default();
2677 if (mozilla::widget::GdkIsX11Display(dpy) &&
2678 GetXftInt(GDK_DISPLAY_XDISPLAY(dpy), "lcdfilter", &lcdfilter)) {
2679 mFreetypeLcdSetting = lcdfilter;
2680 }
2681 }
2682 # endif // MOZ_X11
2683
2684 const cairo_font_options_t* options =
2685 gdk_screen_get_font_options(gdk_screen_get_default());
2686 if (!options) {
2687 bool changed = !!mSystemFontOptions;
2688 ClearSystemFontOptions();
2689 return changed;
2690 }
2691
2692 cairo_font_options_t* newOptions = cairo_font_options_copy(options);
2693
2694 if (mSystemFontOptions &&
2695 cairo_font_options_equal(mSystemFontOptions, options)) {
2696 cairo_font_options_destroy(newOptions);
2697 return false;
2698 }
2699
2700 ClearSystemFontOptions();
2701 mSystemFontOptions = newOptions;
2702 return true;
2703 }
2704
SystemFontOptionsToIpc(dom::SystemFontOptions & aOptions)2705 void gfxFcPlatformFontList::SystemFontOptionsToIpc(
2706 dom::SystemFontOptions& aOptions) {
2707 aOptions.antialias() =
2708 mSystemFontOptions ? cairo_font_options_get_antialias(mSystemFontOptions)
2709 : CAIRO_ANTIALIAS_DEFAULT;
2710 aOptions.subpixelOrder() =
2711 mSystemFontOptions
2712 ? cairo_font_options_get_subpixel_order(mSystemFontOptions)
2713 : CAIRO_SUBPIXEL_ORDER_DEFAULT;
2714 aOptions.hintStyle() =
2715 mSystemFontOptions ? cairo_font_options_get_hint_style(mSystemFontOptions)
2716 : CAIRO_HINT_STYLE_DEFAULT;
2717 aOptions.lcdFilter() = mFreetypeLcdSetting;
2718 }
2719
UpdateSystemFontOptionsFromIpc(const dom::SystemFontOptions & aOptions)2720 void gfxFcPlatformFontList::UpdateSystemFontOptionsFromIpc(
2721 const dom::SystemFontOptions& aOptions) {
2722 ClearSystemFontOptions();
2723 mSystemFontOptions = cairo_font_options_create();
2724 cairo_font_options_set_antialias(mSystemFontOptions,
2725 cairo_antialias_t(aOptions.antialias()));
2726 cairo_font_options_set_hint_style(mSystemFontOptions,
2727 cairo_hint_style_t(aOptions.hintStyle()));
2728 cairo_font_options_set_subpixel_order(
2729 mSystemFontOptions, cairo_subpixel_order_t(aOptions.subpixelOrder()));
2730 mFreetypeLcdSetting = aOptions.lcdFilter();
2731 }
2732
SubstituteSystemFontOptions(FcPattern * aPattern)2733 void gfxFcPlatformFontList::SubstituteSystemFontOptions(FcPattern* aPattern) {
2734 if (mSystemFontOptions) {
2735 cairo_ft_font_options_substitute(mSystemFontOptions, aPattern);
2736 }
2737
2738 if (mFreetypeLcdSetting != -1) {
2739 FcValue value;
2740 if (FcPatternGet(aPattern, FC_LCD_FILTER, 0, &value) == FcResultNoMatch) {
2741 FcPatternAddInteger(aPattern, FC_LCD_FILTER, mFreetypeLcdSetting);
2742 }
2743 }
2744 }
2745
2746 #endif // MOZ_WIDGET_GTK
2747