Bug 1444940 [wpt PR 9917] - Writable streams: test changes to abort() under error...
[gecko.git] / gfx / thebes / gfxFcPlatformFontList.cpp
blob2936d36c0340ba8d2940ab7af73a4457112a21af
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/Logging.h"
8 #include "gfxFcPlatformFontList.h"
9 #include "gfxFont.h"
10 #include "gfxFontConstants.h"
11 #include "gfxFontFamilyList.h"
12 #include "gfxFT2Utils.h"
13 #include "gfxPlatform.h"
14 #include "mozilla/ArrayUtils.h"
15 #include "mozilla/dom/ContentChild.h"
16 #include "mozilla/dom/ContentParent.h"
17 #include "mozilla/Preferences.h"
18 #include "mozilla/Sprintf.h"
19 #include "mozilla/TimeStamp.h"
20 #include "nsGkAtoms.h"
21 #include "nsUnicodeProperties.h"
22 #include "nsUnicodeRange.h"
23 #include "nsDirectoryServiceUtils.h"
24 #include "nsDirectoryServiceDefs.h"
25 #include "nsAppDirectoryServiceDefs.h"
26 #include "nsCharSeparatedTokenizer.h"
27 #include "nsXULAppAPI.h"
29 #include "mozilla/gfx/HelpersCairo.h"
31 #include <fontconfig/fcfreetype.h>
32 #include <dlfcn.h>
33 #include <unistd.h>
35 #ifdef MOZ_WIDGET_GTK
36 #include <gdk/gdk.h>
37 #include "gfxPlatformGtk.h"
38 #endif
40 #ifdef MOZ_X11
41 #include "mozilla/X11Util.h"
42 #endif
44 #ifdef MOZ_CONTENT_SANDBOX
45 #include "mozilla/SandboxBrokerPolicyFactory.h"
46 #include "mozilla/SandboxSettings.h"
47 #endif
49 #include FT_MULTIPLE_MASTERS_H
51 using namespace mozilla;
52 using namespace mozilla::gfx;
53 using namespace mozilla::unicode;
55 using mozilla::dom::SystemFontListEntry;
56 using mozilla::dom::FontPatternListEntry;
58 #ifndef FC_POSTSCRIPT_NAME
59 #define FC_POSTSCRIPT_NAME "postscriptname" /* String */
60 #endif
62 #define PRINTING_FC_PROPERTY "gfx.printing"
64 #define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
65 LogLevel::Debug, args)
66 #define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
67 gfxPlatform::GetLog(eGfxLog_fontlist), \
68 LogLevel::Debug)
69 #define LOG_CMAPDATA_ENABLED() MOZ_LOG_TEST( \
70 gfxPlatform::GetLog(eGfxLog_cmapdata), \
71 LogLevel::Debug)
73 template <>
74 class nsAutoRefTraits<FcFontSet> : public nsPointerRefTraits<FcFontSet>
76 public:
77 static void Release(FcFontSet *ptr) { FcFontSetDestroy(ptr); }
80 template <>
81 class nsAutoRefTraits<FcObjectSet> : public nsPointerRefTraits<FcObjectSet>
83 public:
84 static void Release(FcObjectSet *ptr) { FcObjectSetDestroy(ptr); }
87 static const FcChar8*
88 ToFcChar8Ptr(const char* aStr)
90 return reinterpret_cast<const FcChar8*>(aStr);
93 static const char*
94 ToCharPtr(const FcChar8 *aStr)
96 return reinterpret_cast<const char*>(aStr);
99 FT_Library gfxFcPlatformFontList::sCairoFTLibrary = nullptr;
101 static cairo_user_data_key_t sFcFontlistUserFontDataKey;
103 // canonical name ==> first en name or first name if no en name
104 // This is the required logic for fullname lookups as per CSS3 Fonts spec.
105 static uint32_t
106 FindCanonicalNameIndex(FcPattern* aFont, const char* aLangField)
108 uint32_t n = 0, en = 0;
109 FcChar8* lang;
110 while (FcPatternGetString(aFont, aLangField, n, &lang) == FcResultMatch) {
111 // look for 'en' or variants, en-US, en-JP etc.
112 uint32_t len = strlen(ToCharPtr(lang));
113 bool enPrefix = (strncmp(ToCharPtr(lang), "en", 2) == 0);
114 if (enPrefix && (len == 2 || (len > 2 && aLangField[2] == '-'))) {
115 en = n;
116 break;
118 n++;
120 return en;
123 static void
124 GetFaceNames(FcPattern* aFont, const nsAString& aFamilyName,
125 nsAString& aPostscriptName, nsAString& aFullname)
127 // get the Postscript name
128 FcChar8* psname;
129 if (FcPatternGetString(aFont, FC_POSTSCRIPT_NAME, 0, &psname) == FcResultMatch) {
130 AppendUTF8toUTF16(ToCharPtr(psname), aPostscriptName);
133 // get the canonical fullname (i.e. en name or first name)
134 uint32_t en = FindCanonicalNameIndex(aFont, FC_FULLNAMELANG);
135 FcChar8* fullname;
136 if (FcPatternGetString(aFont, FC_FULLNAME, en, &fullname) == FcResultMatch) {
137 AppendUTF8toUTF16(ToCharPtr(fullname), aFullname);
140 // if have fullname, done
141 if (!aFullname.IsEmpty()) {
142 return;
145 // otherwise, set the fullname to family + style name [en] and use that
146 aFullname.Append(aFamilyName);
148 // figure out the en style name
149 en = FindCanonicalNameIndex(aFont, FC_STYLELANG);
150 nsAutoString style;
151 FcChar8* stylename = nullptr;
152 FcPatternGetString(aFont, FC_STYLE, en, &stylename);
153 if (stylename) {
154 AppendUTF8toUTF16(ToCharPtr(stylename), style);
157 if (!style.IsEmpty() && !style.EqualsLiteral("Regular")) {
158 aFullname.Append(' ');
159 aFullname.Append(style);
163 static uint16_t
164 MapFcWeight(int aFcWeight)
166 if (aFcWeight <= (FC_WEIGHT_THIN + FC_WEIGHT_EXTRALIGHT) / 2) {
167 return 100;
169 if (aFcWeight <= (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2) {
170 return 200;
172 if (aFcWeight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_BOOK) / 2) {
173 return 300;
175 if (aFcWeight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2) {
176 // This includes FC_WEIGHT_BOOK
177 return 400;
179 if (aFcWeight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) {
180 return 500;
182 if (aFcWeight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) {
183 return 600;
185 if (aFcWeight <= (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2) {
186 return 700;
188 if (aFcWeight <= (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2) {
189 return 800;
191 if (aFcWeight <= FC_WEIGHT_BLACK) {
192 return 900;
195 // including FC_WEIGHT_EXTRABLACK
196 return 901;
199 static int16_t
200 MapFcWidth(int aFcWidth)
202 if (aFcWidth <= (FC_WIDTH_ULTRACONDENSED + FC_WIDTH_EXTRACONDENSED) / 2) {
203 return NS_FONT_STRETCH_ULTRA_CONDENSED;
205 if (aFcWidth <= (FC_WIDTH_EXTRACONDENSED + FC_WIDTH_CONDENSED) / 2) {
206 return NS_FONT_STRETCH_EXTRA_CONDENSED;
208 if (aFcWidth <= (FC_WIDTH_CONDENSED + FC_WIDTH_SEMICONDENSED) / 2) {
209 return NS_FONT_STRETCH_CONDENSED;
211 if (aFcWidth <= (FC_WIDTH_SEMICONDENSED + FC_WIDTH_NORMAL) / 2) {
212 return NS_FONT_STRETCH_SEMI_CONDENSED;
214 if (aFcWidth <= (FC_WIDTH_NORMAL + FC_WIDTH_SEMIEXPANDED) / 2) {
215 return NS_FONT_STRETCH_NORMAL;
217 if (aFcWidth <= (FC_WIDTH_SEMIEXPANDED + FC_WIDTH_EXPANDED) / 2) {
218 return NS_FONT_STRETCH_SEMI_EXPANDED;
220 if (aFcWidth <= (FC_WIDTH_EXPANDED + FC_WIDTH_EXTRAEXPANDED) / 2) {
221 return NS_FONT_STRETCH_EXPANDED;
223 if (aFcWidth <= (FC_WIDTH_EXTRAEXPANDED + FC_WIDTH_ULTRAEXPANDED) / 2) {
224 return NS_FONT_STRETCH_EXTRA_EXPANDED;
226 return NS_FONT_STRETCH_ULTRA_EXPANDED;
229 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
230 FcPattern* aFontPattern,
231 bool aIgnoreFcCharmap)
232 : gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
233 mFTFace(nullptr), mFTFaceInitialized(false),
234 mIgnoreFcCharmap(aIgnoreFcCharmap),
235 mAspect(0.0), mFontData(nullptr), mLength(0)
237 // italic
238 int slant;
239 if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
240 slant = FC_SLANT_ROMAN;
242 if (slant == FC_SLANT_OBLIQUE) {
243 mStyle = NS_FONT_STYLE_OBLIQUE;
244 } else if (slant > 0) {
245 mStyle = NS_FONT_STYLE_ITALIC;
248 // weight
249 int weight;
250 if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) != FcResultMatch) {
251 weight = FC_WEIGHT_REGULAR;
253 mWeight = MapFcWeight(weight);
255 // width
256 int width;
257 if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
258 width = FC_WIDTH_NORMAL;
260 mStretch = MapFcWidth(width);
263 gfxFontEntry*
264 gfxFontconfigFontEntry::Clone() const
266 MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
267 return new gfxFontconfigFontEntry(Name(), mFontPattern, mIgnoreFcCharmap);
270 static FcPattern*
271 CreatePatternForFace(FT_Face aFace)
273 // Use fontconfig to fill out the pattern from the FTFace.
274 // The "file" argument cannot be nullptr (in fontconfig-2.6.0 at
275 // least). The dummy file passed here is removed below.
277 // When fontconfig scans the system fonts, FcConfigGetBlanks(nullptr)
278 // is passed as the "blanks" argument, which provides that unexpectedly
279 // blank glyphs are elided. Here, however, we pass nullptr for
280 // "blanks", effectively assuming that, if the font has a blank glyph,
281 // then the author intends any associated character to be rendered
282 // blank.
283 FcPattern* pattern =
284 FcFreeTypeQueryFace(aFace, ToFcChar8Ptr(""), 0, nullptr);
285 // given that we have a FT_Face, not really sure this is possible...
286 if (!pattern) {
287 pattern = FcPatternCreate();
289 FcPatternDel(pattern, FC_FILE);
290 FcPatternDel(pattern, FC_INDEX);
292 // Make a new pattern and store the face in it so that cairo uses
293 // that when creating a cairo font face.
294 FcPatternAddFTFace(pattern, FC_FT_FACE, aFace);
296 return pattern;
299 static FT_Face
300 CreateFaceForPattern(FcPattern* aPattern)
302 FcChar8 *filename;
303 if (FcPatternGetString(aPattern, FC_FILE, 0, &filename) != FcResultMatch) {
304 return nullptr;
306 int index;
307 if (FcPatternGetInteger(aPattern, FC_INDEX, 0, &index) != FcResultMatch) {
308 index = 0; // default to 0 if not found in pattern
310 return Factory::NewFTFace(nullptr, ToCharPtr(filename), index);
313 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
314 uint16_t aWeight,
315 int16_t aStretch,
316 uint8_t aStyle,
317 const uint8_t *aData,
318 uint32_t aLength,
319 FT_Face aFace)
320 : gfxFontEntry(aFaceName),
321 mFTFace(aFace), mFTFaceInitialized(true),
322 mIgnoreFcCharmap(true),
323 mAspect(0.0), mFontData(aData), mLength(aLength)
325 mWeight = aWeight;
326 mStyle = aStyle;
327 mStretch = aStretch;
328 mIsDataUserFont = true;
330 mFontPattern = CreatePatternForFace(mFTFace);
332 mUserFontData = new FTUserFontData(mFTFace, mFontData);
335 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
336 FcPattern* aFontPattern,
337 uint16_t aWeight,
338 int16_t aStretch,
339 uint8_t aStyle)
340 : gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
341 mFTFace(nullptr), mFTFaceInitialized(false),
342 mAspect(0.0), mFontData(nullptr), mLength(0)
344 mWeight = aWeight;
345 mStyle = aStyle;
346 mStretch = aStretch;
347 mIsLocalUserFont = true;
349 // The proper setting of mIgnoreFcCharmap is tricky for fonts loaded
350 // via src:local()...
351 // If the local font happens to come from the application fontset,
352 // we want to set it to true so that color/svg fonts will work even
353 // if the default glyphs are blank; but if the local font is a non-
354 // sfnt face (e.g. legacy type 1) then we need to set it to false
355 // because our cmap-reading code will fail and we depend on FT+Fc to
356 // determine the coverage.
357 // We set the flag here, but may flip it the first time TestCharacterMap
358 // is called, at which point we'll look to see whether a 'cmap' is
359 // actually present in the font.
360 mIgnoreFcCharmap = true;
363 typedef FT_Error (*GetVarFunc)(FT_Face, FT_MM_Var**);
364 typedef FT_Error (*DoneVarFunc)(FT_Library, FT_MM_Var*);
365 static GetVarFunc sGetVar;
366 static DoneVarFunc sDoneVar;
367 static bool sInitializedVarFuncs = false;
369 static void
370 InitializeVarFuncs()
372 if (sInitializedVarFuncs) {
373 return;
375 sInitializedVarFuncs = true;
376 #if MOZ_TREE_FREETYPE
377 sGetVar = &FT_Get_MM_Var;
378 sDoneVar = &FT_Done_MM_Var;
379 #else
380 sGetVar = (GetVarFunc)dlsym(RTLD_DEFAULT, "FT_Get_MM_Var");
381 sDoneVar = (DoneVarFunc)dlsym(RTLD_DEFAULT, "FT_Done_MM_Var");
382 #endif
385 gfxFontconfigFontEntry::~gfxFontconfigFontEntry()
387 if (mMMVar) {
388 // Prior to freetype 2.9, there was no specific function to free the
389 // FT_MM_Var record, and the docs just said to use free().
390 // InitializeVarFuncs must have been called in order for mMMVar to be
391 // non-null here, so we don't need to do it again.
392 if (sDoneVar) {
393 MOZ_ASSERT(mFTFace, "How did mMMVar get set without a face?");
394 (*sDoneVar)(mFTFace->glyph->library, mMMVar);
395 } else {
396 free(mMMVar);
401 nsresult
402 gfxFontconfigFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
404 // attempt this once, if errors occur leave a blank cmap
405 if (mCharacterMap) {
406 return NS_OK;
409 RefPtr<gfxCharacterMap> charmap;
410 nsresult rv;
412 if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
413 mUVSOffset))) {
414 rv = NS_OK;
415 } else {
416 uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
417 charmap = new gfxCharacterMap();
418 AutoTable cmapTable(this, kCMAP);
420 if (cmapTable) {
421 uint32_t cmapLen;
422 const uint8_t* cmapData =
423 reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
424 &cmapLen));
425 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
426 *charmap, mUVSOffset);
427 } else {
428 rv = NS_ERROR_NOT_AVAILABLE;
432 mHasCmapTable = NS_SUCCEEDED(rv);
433 if (mHasCmapTable) {
434 gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
435 mCharacterMap = pfl->FindCharMap(charmap);
436 } else {
437 // if error occurred, initialize to null cmap
438 mCharacterMap = new gfxCharacterMap();
441 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %zu hash: %8.8x%s\n",
442 NS_ConvertUTF16toUTF8(mName).get(),
443 charmap->SizeOfIncludingThis(moz_malloc_size_of),
444 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
445 if (LOG_CMAPDATA_ENABLED()) {
446 char prefix[256];
447 SprintfLiteral(prefix, "(cmapdata) name: %.220s",
448 NS_ConvertUTF16toUTF8(mName).get());
449 charmap->Dump(prefix, eGfxLog_cmapdata);
452 return rv;
455 static bool
456 HasChar(FcPattern *aFont, FcChar32 aCh)
458 FcCharSet *charset = nullptr;
459 FcPatternGetCharSet(aFont, FC_CHARSET, 0, &charset);
460 return charset && FcCharSetHasChar(charset, aCh);
463 bool
464 gfxFontconfigFontEntry::TestCharacterMap(uint32_t aCh)
466 // For user fonts, or for fonts bundled with the app (which might include
467 // color/svg glyphs where the default glyphs may be blank, and thus confuse
468 // fontconfig/freetype's char map checking), we instead check the cmap
469 // directly for character coverage.
470 if (mIgnoreFcCharmap) {
471 // If it does not actually have a cmap, switch our strategy to use
472 // fontconfig's charmap after all (except for data fonts, which must
473 // always have a cmap to have passed OTS validation).
474 if (!mIsDataUserFont && !HasFontTable(TRUETYPE_TAG('c','m','a','p'))) {
475 mIgnoreFcCharmap = false;
476 // ...and continue with HasChar() below.
477 } else {
478 return gfxFontEntry::TestCharacterMap(aCh);
481 // otherwise (for system fonts), use the charmap in the pattern
482 return HasChar(mFontPattern, aCh);
485 hb_blob_t*
486 gfxFontconfigFontEntry::GetFontTable(uint32_t aTableTag)
488 // for data fonts, read directly from the font data
489 if (mFontData) {
490 return gfxFontUtils::GetTableFromFontData(mFontData, aTableTag);
493 return gfxFontEntry::GetFontTable(aTableTag);
496 void
497 gfxFontconfigFontEntry::MaybeReleaseFTFace()
499 // don't release if either HB or Gr face still exists
500 if (mHBFace || mGrFace) {
501 return;
503 // only close out FT_Face for system fonts, not for data fonts
504 if (!mIsDataUserFont) {
505 if (mFTFace) {
506 if (mMMVar) {
507 if (sDoneVar) {
508 (*sDoneVar)(mFTFace->glyph->library, mMMVar);
509 } else {
510 free(mMMVar);
512 mMMVar = nullptr;
514 Factory::ReleaseFTFace(mFTFace);
515 mFTFace = nullptr;
517 mFTFaceInitialized = false;
521 void
522 gfxFontconfigFontEntry::ForgetHBFace()
524 gfxFontEntry::ForgetHBFace();
525 MaybeReleaseFTFace();
528 void
529 gfxFontconfigFontEntry::ReleaseGrFace(gr_face* aFace)
531 gfxFontEntry::ReleaseGrFace(aFace);
532 MaybeReleaseFTFace();
535 double
536 gfxFontconfigFontEntry::GetAspect()
538 if (mAspect != 0.0) {
539 return mAspect;
542 // try to compute aspect from OS/2 metrics if available
543 AutoTable os2Table(this, TRUETYPE_TAG('O','S','/','2'));
544 if (os2Table) {
545 uint16_t upem = UnitsPerEm();
546 if (upem != kInvalidUPEM) {
547 uint32_t len;
548 auto os2 = reinterpret_cast<const OS2Table*>
549 (hb_blob_get_data(os2Table, &len));
550 if (uint16_t(os2->version) >= 2) {
551 if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
552 int16_t(os2->sxHeight) > 0.1 * upem) {
553 mAspect = double(int16_t(os2->sxHeight)) / upem;
554 return mAspect;
560 // default to aspect = 0.5 if the code below fails
561 mAspect = 0.5;
563 // create a font to calculate x-height / em-height
564 gfxFontStyle s;
565 s.size = 100.0; // pick large size to avoid possible hinting artifacts
566 RefPtr<gfxFont> font = FindOrMakeFont(&s, false);
567 if (font) {
568 const gfxFont::Metrics& metrics =
569 font->GetMetrics(gfxFont::eHorizontal);
571 // The factor of 0.1 ensures that xHeight is sane so fonts don't
572 // become huge. Strictly ">" ensures that xHeight and emHeight are
573 // not both zero.
574 if (metrics.xHeight > 0.1 * metrics.emHeight) {
575 mAspect = metrics.xHeight / metrics.emHeight;
579 return mAspect;
582 static void
583 PrepareFontOptions(FcPattern* aPattern,
584 cairo_font_options_t* aFontOptions)
586 NS_ASSERTION(aFontOptions, "null font options passed to PrepareFontOptions");
588 // xxx - taken from the gfxFontconfigFonts code, needs to be reviewed
590 FcBool printing;
591 if (FcPatternGetBool(aPattern, PRINTING_FC_PROPERTY, 0, &printing) !=
592 FcResultMatch) {
593 printing = FcFalse;
596 // Font options are set explicitly here to improve cairo's caching
597 // behavior and to record the relevant parts of the pattern for
598 // SetupCairoFont (so that the pattern can be released).
600 // Most font_options have already been set as defaults on the FcPattern
601 // with cairo_ft_font_options_substitute(), then user and system
602 // fontconfig configurations were applied. The resulting font_options
603 // have been recorded on the face during
604 // cairo_ft_font_face_create_for_pattern().
606 // None of the settings here cause this scaled_font to behave any
607 // differently from how it would behave if it were created from the same
608 // face with default font_options.
610 // We set options explicitly so that the same scaled_font will be found in
611 // the cairo_scaled_font_map when cairo loads glyphs from a context with
612 // the same font_face, font_matrix, ctm, and surface font_options.
614 // Unfortunately, _cairo_scaled_font_keys_equal doesn't know about the
615 // font_options on the cairo_ft_font_face, and doesn't consider default
616 // option values to not match any explicit values.
618 // Even after cairo_set_scaled_font is used to set font_options for the
619 // cairo context, when cairo looks for a scaled_font for the context, it
620 // will look for a font with some option values from the target surface if
621 // any values are left default on the context font_options. If this
622 // scaled_font is created with default font_options, cairo will not find
623 // it.
625 // The one option not recorded in the pattern is hint_metrics, which will
626 // affect glyph metrics. The default behaves as CAIRO_HINT_METRICS_ON.
627 // We should be considering the font_options of the surface on which this
628 // font will be used, but currently we don't have different gfxFonts for
629 // different surface font_options, so we'll create a font suitable for the
630 // Screen. Image and xlib surfaces default to CAIRO_HINT_METRICS_ON.
631 if (printing) {
632 cairo_font_options_set_hint_metrics(aFontOptions, CAIRO_HINT_METRICS_OFF);
633 } else {
634 cairo_font_options_set_hint_metrics(aFontOptions, CAIRO_HINT_METRICS_ON);
637 // The remaining options have been recorded on the pattern and the face.
638 // _cairo_ft_options_merge has some logic to decide which options from the
639 // scaled_font or from the cairo_ft_font_face take priority in the way the
640 // font behaves.
642 // In the majority of cases, _cairo_ft_options_merge uses the options from
643 // the cairo_ft_font_face, so sometimes it is not so important which
644 // values are set here so long as they are not defaults, but we'll set
645 // them to the exact values that we expect from the font, to be consistent
646 // and to protect against changes in cairo.
648 // In some cases, _cairo_ft_options_merge uses some options from the
649 // scaled_font's font_options rather than options on the
650 // cairo_ft_font_face (from fontconfig).
651 // https://bugs.freedesktop.org/show_bug.cgi?id=11838
653 // Surface font options were set on the pattern in
654 // cairo_ft_font_options_substitute. If fontconfig has changed the
655 // hint_style then that is what the user (or distribution) wants, so we
656 // use the setting from the FcPattern.
658 // Fallback values here mirror treatment of defaults in cairo-ft-font.c.
659 FcBool hinting = FcFalse;
660 if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch) {
661 hinting = FcTrue;
664 cairo_hint_style_t hint_style;
665 if (printing || !hinting) {
666 hint_style = CAIRO_HINT_STYLE_NONE;
667 } else {
668 int fc_hintstyle;
669 if (FcPatternGetInteger(aPattern, FC_HINT_STYLE,
670 0, &fc_hintstyle) != FcResultMatch) {
671 fc_hintstyle = FC_HINT_FULL;
673 switch (fc_hintstyle) {
674 case FC_HINT_NONE:
675 hint_style = CAIRO_HINT_STYLE_NONE;
676 break;
677 case FC_HINT_SLIGHT:
678 hint_style = CAIRO_HINT_STYLE_SLIGHT;
679 break;
680 case FC_HINT_MEDIUM:
681 default: // This fallback mirrors _get_pattern_ft_options in cairo.
682 hint_style = CAIRO_HINT_STYLE_MEDIUM;
683 break;
684 case FC_HINT_FULL:
685 hint_style = CAIRO_HINT_STYLE_FULL;
686 break;
689 cairo_font_options_set_hint_style(aFontOptions, hint_style);
691 int rgba;
692 if (FcPatternGetInteger(aPattern,
693 FC_RGBA, 0, &rgba) != FcResultMatch) {
694 rgba = FC_RGBA_UNKNOWN;
696 cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT;
697 switch (rgba) {
698 case FC_RGBA_UNKNOWN:
699 case FC_RGBA_NONE:
700 default:
701 // There is no CAIRO_SUBPIXEL_ORDER_NONE. Subpixel antialiasing
702 // is disabled through cairo_antialias_t.
703 rgba = FC_RGBA_NONE;
704 // subpixel_order won't be used by the font as we won't use
705 // CAIRO_ANTIALIAS_SUBPIXEL, but don't leave it at default for
706 // caching reasons described above. Fall through:
707 MOZ_FALLTHROUGH;
708 case FC_RGBA_RGB:
709 subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB;
710 break;
711 case FC_RGBA_BGR:
712 subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR;
713 break;
714 case FC_RGBA_VRGB:
715 subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB;
716 break;
717 case FC_RGBA_VBGR:
718 subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR;
719 break;
721 cairo_font_options_set_subpixel_order(aFontOptions, subpixel_order);
723 FcBool fc_antialias;
724 if (FcPatternGetBool(aPattern,
725 FC_ANTIALIAS, 0, &fc_antialias) != FcResultMatch) {
726 fc_antialias = FcTrue;
728 cairo_antialias_t antialias;
729 if (!fc_antialias) {
730 antialias = CAIRO_ANTIALIAS_NONE;
731 } else if (rgba == FC_RGBA_NONE) {
732 antialias = CAIRO_ANTIALIAS_GRAY;
733 } else {
734 antialias = CAIRO_ANTIALIAS_SUBPIXEL;
736 cairo_font_options_set_antialias(aFontOptions, antialias);
739 static void
740 ReleaseFTUserFontData(void* aData)
742 static_cast<FTUserFontData*>(aData)->Release();
745 cairo_scaled_font_t*
746 gfxFontconfigFontEntry::CreateScaledFont(FcPattern* aRenderPattern,
747 gfxFloat aAdjustedSize,
748 const gfxFontStyle *aStyle,
749 bool aNeedsBold)
751 if (aNeedsBold) {
752 FcPatternAddBool(aRenderPattern, FC_EMBOLDEN, FcTrue);
755 // will synthetic oblique be applied using a transform?
756 bool needsOblique = IsUpright() &&
757 aStyle->style != NS_FONT_STYLE_NORMAL &&
758 aStyle->allowSyntheticStyle;
760 if (needsOblique) {
761 // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
762 FcPatternDel(aRenderPattern, FC_EMBEDDED_BITMAP);
763 FcPatternAddBool(aRenderPattern, FC_EMBEDDED_BITMAP, FcFalse);
766 AutoTArray<FT_Fixed,8> coords;
767 if (!aStyle->variationSettings.IsEmpty() || !mVariationSettings.IsEmpty()) {
768 FT_Face ftFace = GetFTFace();
769 if (ftFace) {
770 const nsTArray<gfxFontVariation>* settings;
771 AutoTArray<gfxFontVariation,8> mergedSettings;
772 if (mVariationSettings.IsEmpty()) {
773 settings = &aStyle->variationSettings;
774 } else if (aStyle->variationSettings.IsEmpty()) {
775 settings = &mVariationSettings;
776 } else {
777 gfxFontUtils::MergeVariations(mVariationSettings,
778 aStyle->variationSettings,
779 &mergedSettings);
780 settings = &mergedSettings;
782 gfxFT2FontBase::SetupVarCoords(ftFace, *settings, &coords);
786 cairo_font_face_t *face =
787 cairo_ft_font_face_create_for_pattern(aRenderPattern,
788 coords.Elements(),
789 coords.Length());
791 if (mFontData) {
792 // for data fonts, add the face/data pointer to the cairo font face
793 // so that it gets deleted whenever cairo decides
794 NS_ASSERTION(mFTFace, "FT_Face is null when setting user data");
795 NS_ASSERTION(mUserFontData, "user font data is null when setting user data");
796 mUserFontData.get()->AddRef();
797 if (cairo_font_face_set_user_data(face,
798 &sFcFontlistUserFontDataKey,
799 mUserFontData,
800 ReleaseFTUserFontData) != CAIRO_STATUS_SUCCESS) {
801 NS_WARNING("Failed binding FTUserFontData to Cairo font face");
802 mUserFontData.get()->Release();
803 cairo_font_face_destroy(face);
804 return nullptr;
808 cairo_scaled_font_t *scaledFont = nullptr;
810 cairo_matrix_t sizeMatrix;
811 cairo_matrix_t identityMatrix;
813 cairo_matrix_init_scale(&sizeMatrix, aAdjustedSize, aAdjustedSize);
814 cairo_matrix_init_identity(&identityMatrix);
816 cairo_font_options_t *fontOptions = cairo_font_options_create();
817 PrepareFontOptions(aRenderPattern, fontOptions);
819 scaledFont = cairo_scaled_font_create(face, &sizeMatrix,
820 &identityMatrix, fontOptions);
821 cairo_font_options_destroy(fontOptions);
823 NS_ASSERTION(cairo_scaled_font_status(scaledFont) == CAIRO_STATUS_SUCCESS,
824 "Failed to make scaled font");
826 cairo_font_face_destroy(face);
828 return scaledFont;
831 #ifdef MOZ_WIDGET_GTK
832 // defintion included below
833 static void ApplyGdkScreenFontOptions(FcPattern *aPattern);
834 #endif
836 #ifdef MOZ_X11
837 static bool
838 GetXftInt(Display* aDisplay, const char* aName, int* aResult)
840 if (!aDisplay) {
841 return false;
843 char* value = XGetDefault(aDisplay, "Xft", aName);
844 if (!value) {
845 return false;
847 if (FcNameConstant(const_cast<FcChar8*>(ToFcChar8Ptr(value)), aResult)) {
848 return true;
850 char* end;
851 *aResult = strtol(value, &end, 0);
852 if (end != value) {
853 return true;
855 return false;
857 #endif
859 static void
860 PreparePattern(FcPattern* aPattern, bool aIsPrinterFont)
862 FcConfigSubstitute(nullptr, aPattern, FcMatchPattern);
864 // This gets cairo_font_options_t for the Screen. We should have
865 // different font options for printing (no hinting) but we are not told
866 // what we are measuring for.
868 // If cairo adds support for lcd_filter, gdk will not provide the default
869 // setting for that option. We could get the default setting by creating
870 // an xlib surface once, recording its font_options, and then merging the
871 // gdk options.
873 // Using an xlib surface would also be an option to get Screen font
874 // options for non-GTK X11 toolkits, but less efficient than using GDK to
875 // pick up dynamic changes.
876 if(aIsPrinterFont) {
877 cairo_font_options_t *options = cairo_font_options_create();
878 cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
879 cairo_font_options_set_antialias (options, CAIRO_ANTIALIAS_GRAY);
880 cairo_ft_font_options_substitute(options, aPattern);
881 cairo_font_options_destroy(options);
882 FcPatternAddBool(aPattern, PRINTING_FC_PROPERTY, FcTrue);
883 } else if (!gfxPlatform::IsHeadless()) {
884 #ifdef MOZ_WIDGET_GTK
885 ApplyGdkScreenFontOptions(aPattern);
887 #ifdef MOZ_X11
888 FcValue value;
889 int lcdfilter;
890 if (FcPatternGet(aPattern, FC_LCD_FILTER, 0, &value) == FcResultNoMatch) {
891 GdkDisplay* dpy = gdk_display_get_default();
892 if (GDK_IS_X11_DISPLAY(dpy) &&
893 GetXftInt(GDK_DISPLAY_XDISPLAY(dpy), "lcdfilter", &lcdfilter)) {
894 FcPatternAddInteger(aPattern, FC_LCD_FILTER, lcdfilter);
897 #endif // MOZ_X11
898 #endif // MOZ_WIDGET_GTK
901 FcDefaultSubstitute(aPattern);
904 void
905 gfxFontconfigFontEntry::UnscaledFontCache::MoveToFront(size_t aIndex) {
906 if (aIndex > 0) {
907 ThreadSafeWeakPtr<UnscaledFontFontconfig> front =
908 Move(mUnscaledFonts[aIndex]);
909 for (size_t i = aIndex; i > 0; i--) {
910 mUnscaledFonts[i] = Move(mUnscaledFonts[i-1]);
912 mUnscaledFonts[0] = Move(front);
916 already_AddRefed<UnscaledFontFontconfig>
917 gfxFontconfigFontEntry::UnscaledFontCache::Lookup(const char* aFile, uint32_t aIndex) {
918 for (size_t i = 0; i < kNumEntries; i++) {
919 RefPtr<UnscaledFontFontconfig> entry(mUnscaledFonts[i]);
920 if (entry &&
921 !strcmp(entry->GetFile(), aFile) &&
922 entry->GetIndex() == aIndex) {
923 MoveToFront(i);
924 return entry.forget();
927 return nullptr;
930 static inline gfxFloat
931 SizeForStyle(gfxFontconfigFontEntry* aEntry, const gfxFontStyle& aStyle)
933 return aStyle.sizeAdjust >= 0.0 ?
934 aStyle.GetAdjustedSize(aEntry->GetAspect()) :
935 aStyle.size;
938 static double
939 ChooseFontSize(gfxFontconfigFontEntry* aEntry,
940 const gfxFontStyle& aStyle)
942 double requestedSize = SizeForStyle(aEntry, aStyle);
943 double bestDist = -1.0;
944 double bestSize = requestedSize;
945 double size;
946 int v = 0;
947 while (FcPatternGetDouble(aEntry->GetPattern(),
948 FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
949 ++v;
950 double dist = fabs(size - requestedSize);
951 if (bestDist < 0.0 || dist < bestDist) {
952 bestDist = dist;
953 bestSize = size;
956 // If the font has bitmaps but wants to be scaled, then let it scale.
957 if (bestSize >= 0.0) {
958 FcBool scalable;
959 if (FcPatternGetBool(aEntry->GetPattern(),
960 FC_SCALABLE, 0, &scalable) == FcResultMatch &&
961 scalable) {
962 return requestedSize;
965 return bestSize;
968 gfxFont*
969 gfxFontconfigFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle,
970 bool aNeedsBold)
972 nsAutoRef<FcPattern> pattern(FcPatternCreate());
973 if (!pattern) {
974 NS_WARNING("Failed to create Fontconfig pattern for font instance");
975 return nullptr;
978 double size = ChooseFontSize(this, *aFontStyle);
979 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, size);
981 FT_Face face = mFTFace;
982 FcPattern* fontPattern = mFontPattern;
983 if (face && face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
984 // For variation fonts, we create a new FT_Face and FcPattern here
985 // so that variation coordinates from the style can be applied
986 // without affecting other font instances created from the same
987 // entry (font resource).
988 if (mFontData) {
989 // For user fonts: create a new FT_Face from the font data, and then
990 // make a pattern from that.
991 face = Factory::NewFTFaceFromData(nullptr, mFontData, mLength, 0);
992 fontPattern = CreatePatternForFace(face);
993 } else {
994 // For system fonts: create a new FT_Face and store it in a copy of
995 // the original mFontPattern.
996 fontPattern = FcPatternDuplicate(mFontPattern);
997 face = CreateFaceForPattern(fontPattern);
998 if (face) {
999 FcPatternAddFTFace(fontPattern, FC_FT_FACE, face);
1000 } else {
1001 // I don't think CreateFaceForPattern above should ever fail,
1002 // but just in case let's fall back here.
1003 face = mFTFace;
1008 PreparePattern(pattern, aFontStyle->printerFont);
1009 nsAutoRef<FcPattern> renderPattern
1010 (FcFontRenderPrepare(nullptr, pattern, fontPattern));
1011 if (fontPattern != mFontPattern) {
1012 // Discard temporary pattern used for variation support
1013 FcPatternDestroy(fontPattern);
1015 if (!renderPattern) {
1016 NS_WARNING("Failed to prepare Fontconfig pattern for font instance");
1017 return nullptr;
1020 cairo_scaled_font_t* scaledFont =
1021 CreateScaledFont(renderPattern, size, aFontStyle, aNeedsBold);
1023 const FcChar8* file = ToFcChar8Ptr("");
1024 int index = 0;
1025 if (!mFontData) {
1026 if (FcPatternGetString(renderPattern, FC_FILE, 0,
1027 const_cast<FcChar8**>(&file)) != FcResultMatch ||
1028 FcPatternGetInteger(renderPattern, FC_INDEX, 0, &index) != FcResultMatch) {
1029 NS_WARNING("No file in Fontconfig pattern for font instance");
1030 return nullptr;
1034 RefPtr<UnscaledFontFontconfig> unscaledFont =
1035 mUnscaledFontCache.Lookup(ToCharPtr(file), index);
1036 if (!unscaledFont) {
1037 unscaledFont =
1038 mFontData ?
1039 new UnscaledFontFontconfig(face) :
1040 new UnscaledFontFontconfig(ToCharPtr(file), index);
1041 mUnscaledFontCache.Add(unscaledFont);
1044 gfxFont* newFont =
1045 new gfxFontconfigFont(unscaledFont, scaledFont,
1046 renderPattern, size,
1047 this, aFontStyle, aNeedsBold);
1048 cairo_scaled_font_destroy(scaledFont);
1050 return newFont;
1053 FT_Face
1054 gfxFontconfigFontEntry::GetFTFace()
1056 if (!mFTFaceInitialized) {
1057 mFTFaceInitialized = true;
1058 mFTFace = CreateFaceForPattern(mFontPattern);
1060 return mFTFace;
1063 bool
1064 gfxFontconfigFontEntry::HasVariations()
1066 FT_Face face = GetFTFace();
1067 if (face) {
1068 return face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS;
1070 return false;
1073 FT_MM_Var*
1074 gfxFontconfigFontEntry::GetMMVar()
1076 if (mMMVarInitialized) {
1077 return mMMVar;
1079 mMMVarInitialized = true;
1080 InitializeVarFuncs();
1081 if (!sGetVar) {
1082 return nullptr;
1084 FT_Face face = GetFTFace();
1085 if (!face) {
1086 return nullptr;
1088 if (FT_Err_Ok != (*sGetVar)(face, &mMMVar)) {
1089 mMMVar = nullptr;
1091 return mMMVar;
1094 void
1095 gfxFontconfigFontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aAxes)
1097 MOZ_ASSERT(aAxes.IsEmpty());
1098 FT_MM_Var* mmVar = GetMMVar();
1099 if (!mmVar) {
1100 return;
1102 aAxes.SetCapacity(mmVar->num_axis);
1103 for (unsigned i = 0; i < mmVar->num_axis; i++) {
1104 const auto& a = mmVar->axis[i];
1105 gfxFontVariationAxis axis;
1106 axis.mMinValue = a.minimum / 65536.0;
1107 axis.mMaxValue = a.maximum / 65536.0;
1108 axis.mDefaultValue = a.def / 65536.0;
1109 axis.mTag = a.tag;
1110 axis.mName.Assign(NS_ConvertUTF8toUTF16(a.name));
1111 aAxes.AppendElement(axis);
1115 void
1116 gfxFontconfigFontEntry::GetVariationInstances(
1117 nsTArray<gfxFontVariationInstance>& aInstances)
1119 MOZ_ASSERT(aInstances.IsEmpty());
1120 FT_MM_Var* mmVar = GetMMVar();
1121 if (!mmVar) {
1122 return;
1124 hb_blob_t* nameTable = GetFontTable(TRUETYPE_TAG('n','a','m','e'));
1125 if (!nameTable) {
1126 return;
1128 aInstances.SetCapacity(mmVar->num_namedstyles);
1129 for (unsigned i = 0; i < mmVar->num_namedstyles; i++) {
1130 const auto& ns = mmVar->namedstyle[i];
1131 gfxFontVariationInstance inst;
1132 nsresult rv =
1133 gfxFontUtils::ReadCanonicalName(nameTable, ns.strid, inst.mName);
1134 if (NS_FAILED(rv)) {
1135 continue;
1137 inst.mValues.SetCapacity(mmVar->num_axis);
1138 for (unsigned j = 0; j < mmVar->num_axis; j++) {
1139 gfxFontVariationValue value;
1140 value.mAxis = mmVar->axis[j].tag;
1141 value.mValue = ns.coords[j] / 65536.0;
1142 inst.mValues.AppendElement(value);
1144 aInstances.AppendElement(inst);
1146 hb_blob_destroy(nameTable);
1149 nsresult
1150 gfxFontconfigFontEntry::CopyFontTable(uint32_t aTableTag,
1151 nsTArray<uint8_t>& aBuffer)
1153 NS_ASSERTION(!mIsDataUserFont,
1154 "data fonts should be reading tables directly from memory");
1156 FT_Face face = GetFTFace();
1157 if (!face) {
1158 return NS_ERROR_NOT_AVAILABLE;
1161 FT_ULong length = 0;
1162 if (FT_Load_Sfnt_Table(face, aTableTag, 0, nullptr, &length) != 0) {
1163 return NS_ERROR_NOT_AVAILABLE;
1165 if (!aBuffer.SetLength(length, fallible)) {
1166 return NS_ERROR_OUT_OF_MEMORY;
1168 if (FT_Load_Sfnt_Table(face, aTableTag, 0, aBuffer.Elements(), &length) != 0) {
1169 aBuffer.Clear();
1170 return NS_ERROR_FAILURE;
1173 return NS_OK;
1176 void
1177 gfxFontconfigFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
1179 if (mHasStyles) {
1180 return;
1183 // add font entries for each of the faces
1184 uint32_t numFonts = mFontPatterns.Length();
1185 NS_ASSERTION(numFonts, "font family containing no faces!!");
1186 uint32_t numRegularFaces = 0;
1187 for (uint32_t i = 0; i < numFonts; i++) {
1188 FcPattern* face = mFontPatterns[i];
1190 // figure out the psname/fullname and choose which to use as the facename
1191 nsAutoString psname, fullname;
1192 GetFaceNames(face, mName, psname, fullname);
1193 const nsAutoString& faceName = !psname.IsEmpty() ? psname : fullname;
1195 gfxFontconfigFontEntry *fontEntry =
1196 new gfxFontconfigFontEntry(faceName, face, mContainsAppFonts);
1197 AddFontEntry(fontEntry);
1199 if (fontEntry->IsNormalStyle()) {
1200 numRegularFaces++;
1203 if (LOG_FONTLIST_ENABLED()) {
1204 LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
1205 " with style: %s weight: %d stretch: %d"
1206 " psname: %s fullname: %s",
1207 NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
1208 NS_ConvertUTF16toUTF8(Name()).get(),
1209 (fontEntry->IsItalic()) ?
1210 "italic" : (fontEntry->IsOblique() ? "oblique" : "normal"),
1211 fontEntry->Weight(), fontEntry->Stretch(),
1212 NS_ConvertUTF16toUTF8(psname).get(),
1213 NS_ConvertUTF16toUTF8(fullname).get()));
1217 // somewhat arbitrary, but define a family with two or more regular
1218 // faces as a family for which intra-family fallback should be used
1219 if (numRegularFaces > 1) {
1220 mCheckForFallbackFaces = true;
1222 mFaceNamesInitialized = true;
1223 mFontPatterns.Clear();
1224 SetHasStyles(true);
1227 void
1228 gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern)
1230 NS_ASSERTION(!mHasStyles,
1231 "font patterns must not be added to already enumerated families");
1233 FcBool outline;
1234 if (FcPatternGetBool(aFontPattern, FC_OUTLINE, 0, &outline) != FcResultMatch ||
1235 !outline) {
1236 mHasNonScalableFaces = true;
1238 FcBool scalable;
1239 if (FcPatternGetBool(aFontPattern, FC_SCALABLE, 0, &scalable) == FcResultMatch &&
1240 scalable) {
1241 mForceScalable = true;
1245 nsCountedRef<FcPattern> pattern(aFontPattern);
1246 mFontPatterns.AppendElement(pattern);
1249 static const double kRejectDistance = 10000.0;
1251 // Calculate a distance score representing the size disparity between the
1252 // requested style's size and the font entry's size.
1253 static double
1254 SizeDistance(gfxFontconfigFontEntry* aEntry,
1255 const gfxFontStyle& aStyle,
1256 bool aForceScalable)
1258 double requestedSize = SizeForStyle(aEntry, aStyle);
1259 double bestDist = -1.0;
1260 double size;
1261 int v = 0;
1262 while (FcPatternGetDouble(aEntry->GetPattern(),
1263 FC_PIXEL_SIZE, v, &size) == FcResultMatch) {
1264 ++v;
1265 double dist = fabs(size - requestedSize);
1266 if (bestDist < 0.0 || dist < bestDist) {
1267 bestDist = dist;
1270 if (bestDist < 0.0) {
1271 // No size means scalable
1272 return -1.0;
1273 } else if (aForceScalable || 5.0 * bestDist < requestedSize) {
1274 // fontconfig prefers a matching family or lang to pixelsize of bitmap
1275 // fonts. CSS suggests a tolerance of 20% on pixelsize.
1276 return bestDist;
1277 } else {
1278 // Reject any non-scalable fonts that are not within tolerance.
1279 return kRejectDistance;
1283 void
1284 gfxFontconfigFontFamily::FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
1285 nsTArray<gfxFontEntry*>& aFontEntryList,
1286 bool& aNeedsSyntheticBold,
1287 bool aIgnoreSizeTolerance)
1289 gfxFontFamily::FindAllFontsForStyle(aFontStyle,
1290 aFontEntryList,
1291 aNeedsSyntheticBold,
1292 aIgnoreSizeTolerance);
1294 if (!mHasNonScalableFaces) {
1295 return;
1298 // Iterate over the the available fonts while compacting any groups
1299 // of unscalable fonts with matching styles into a single entry
1300 // corresponding to the closest available size. If the closest
1301 // available size is rejected for being outside tolerance, then the
1302 // entire group will be skipped.
1303 size_t skipped = 0;
1304 gfxFontconfigFontEntry* bestEntry = nullptr;
1305 double bestDist = -1.0;
1306 for (size_t i = 0; i < aFontEntryList.Length(); i++) {
1307 gfxFontconfigFontEntry* entry =
1308 static_cast<gfxFontconfigFontEntry*>(aFontEntryList[i]);
1309 double dist = SizeDistance(entry, aFontStyle,
1310 mForceScalable || aIgnoreSizeTolerance);
1311 // If the entry is scalable or has a style that does not match
1312 // the group of unscalable fonts, then start a new group.
1313 if (dist < 0.0 ||
1314 !bestEntry ||
1315 bestEntry->Stretch() != entry->Stretch() ||
1316 bestEntry->Weight() != entry->Weight() ||
1317 bestEntry->mStyle != entry->mStyle) {
1318 // If the best entry in this group is still outside the tolerance,
1319 // then skip the entire group.
1320 if (bestDist >= kRejectDistance) {
1321 skipped++;
1323 // Remove any compacted entries from the previous group.
1324 if (skipped) {
1325 i -= skipped;
1326 aFontEntryList.RemoveElementsAt(i, skipped);
1327 skipped = 0;
1329 // Mark the start of the new group.
1330 bestEntry = entry;
1331 bestDist = dist;
1332 } else {
1333 // If this entry more closely matches the requested size than the
1334 // current best in the group, then take this entry instead.
1335 if (dist < bestDist) {
1336 aFontEntryList[i-1-skipped] = entry;
1337 bestEntry = entry;
1338 bestDist = dist;
1340 skipped++;
1343 // If the best entry in this group is still outside the tolerance,
1344 // then skip the entire group.
1345 if (bestDist >= kRejectDistance) {
1346 skipped++;
1348 // Remove any compacted entries from the current group.
1349 if (skipped) {
1350 aFontEntryList.TruncateLength(aFontEntryList.Length() - skipped);
1354 static bool
1355 PatternHasLang(const FcPattern *aPattern, const FcChar8 *aLang)
1357 FcLangSet *langset;
1359 if (FcPatternGetLangSet(aPattern, FC_LANG, 0, &langset) != FcResultMatch) {
1360 return false;
1363 if (FcLangSetHasLang(langset, aLang) != FcLangDifferentLang) {
1364 return true;
1366 return false;
1369 bool
1370 gfxFontconfigFontFamily::SupportsLangGroup(nsAtom *aLangGroup) const
1372 if (!aLangGroup || aLangGroup == nsGkAtoms::Unicode) {
1373 return true;
1376 nsAutoCString fcLang;
1377 gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
1378 pfl->GetSampleLangForGroup(aLangGroup, fcLang);
1379 if (fcLang.IsEmpty()) {
1380 return true;
1383 // Before FindStyleVariations has been called, mFontPatterns will contain
1384 // the font patterns. Afterward, it'll be empty, but mAvailableFonts
1385 // will contain the font entries, each of which holds a reference to its
1386 // pattern. We only check the first pattern in each list, because support
1387 // for langs is considered to be consistent across all faces in a family.
1388 FcPattern* fontPattern;
1389 if (mFontPatterns.Length()) {
1390 fontPattern = mFontPatterns[0];
1391 } else if (mAvailableFonts.Length()) {
1392 fontPattern = static_cast<gfxFontconfigFontEntry*>
1393 (mAvailableFonts[0].get())->GetPattern();
1394 } else {
1395 return true;
1398 // is lang included in the underlying pattern?
1399 return PatternHasLang(fontPattern, ToFcChar8Ptr(fcLang.get()));
1402 /* virtual */
1403 gfxFontconfigFontFamily::~gfxFontconfigFontFamily()
1405 // Should not be dropped by stylo
1406 MOZ_ASSERT(NS_IsMainThread());
1409 template<typename Func>
1410 void
1411 gfxFontconfigFontFamily::AddFacesToFontList(Func aAddPatternFunc)
1413 if (HasStyles()) {
1414 for (auto& fe : mAvailableFonts) {
1415 if (!fe) {
1416 continue;
1418 auto fce = static_cast<gfxFontconfigFontEntry*>(fe.get());
1419 aAddPatternFunc(fce->GetPattern(), mContainsAppFonts);
1421 } else {
1422 for (auto& pat : mFontPatterns) {
1423 aAddPatternFunc(pat, mContainsAppFonts);
1428 gfxFontconfigFont::gfxFontconfigFont(const RefPtr<UnscaledFontFontconfig>& aUnscaledFont,
1429 cairo_scaled_font_t *aScaledFont,
1430 FcPattern *aPattern,
1431 gfxFloat aAdjustedSize,
1432 gfxFontEntry *aFontEntry,
1433 const gfxFontStyle *aFontStyle,
1434 bool aNeedsBold)
1435 : gfxFT2FontBase(aUnscaledFont, aScaledFont, aFontEntry, aFontStyle, aNeedsBold)
1436 , mPattern(aPattern)
1438 mAdjustedSize = aAdjustedSize;
1441 gfxFontconfigFont::~gfxFontconfigFont()
1445 already_AddRefed<ScaledFont>
1446 gfxFontconfigFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
1448 if (!mAzureScaledFont) {
1449 mAzureScaledFont =
1450 Factory::CreateScaledFontForFontconfigFont(GetCairoScaledFont(),
1451 GetPattern(),
1452 GetUnscaledFont(),
1453 GetAdjustedSize());
1456 RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
1457 return scaledFont.forget();
1460 gfxFcPlatformFontList::gfxFcPlatformFontList()
1461 : mLocalNames(64)
1462 , mGenericMappings(32)
1463 , mFcSubstituteCache(64)
1464 , mLastConfig(nullptr)
1465 , mAlwaysUseFontconfigGenerics(true)
1467 if (XRE_IsParentProcess()) {
1468 // if the rescan interval is set, start the timer
1469 int rescanInterval = FcConfigGetRescanInterval(nullptr);
1470 if (rescanInterval) {
1471 mLastConfig = FcConfigGetCurrent();
1472 NS_NewTimerWithFuncCallback(getter_AddRefs(mCheckFontUpdatesTimer),
1473 CheckFontUpdates,
1474 this,
1475 (rescanInterval + 1) * 1000,
1476 nsITimer::TYPE_REPEATING_SLACK,
1477 "gfxFcPlatformFontList::gfxFcPlatformFontList");
1478 if (!mCheckFontUpdatesTimer) {
1479 NS_WARNING("Failure to create font updates timer");
1484 #ifdef MOZ_BUNDLED_FONTS
1485 mBundledFontsInitialized = false;
1486 #endif
1489 gfxFcPlatformFontList::~gfxFcPlatformFontList()
1491 if (mCheckFontUpdatesTimer) {
1492 mCheckFontUpdatesTimer->Cancel();
1493 mCheckFontUpdatesTimer = nullptr;
1497 void
1498 gfxFcPlatformFontList::AddFontSetFamilies(FcFontSet* aFontSet,
1499 const SandboxPolicy* aPolicy,
1500 bool aAppFonts)
1502 // This iterates over the fonts in a font set and adds in gfxFontFamily
1503 // objects for each family. Individual gfxFontEntry objects for each face
1504 // are not created here; the patterns are just stored in the family. When
1505 // a family is actually used, it will be populated with gfxFontEntry
1506 // records and the patterns moved to those.
1508 if (!aFontSet) {
1509 NS_WARNING("AddFontSetFamilies called with a null font set.");
1510 return;
1513 FcChar8* lastFamilyName = (FcChar8*)"";
1514 RefPtr<gfxFontconfigFontFamily> fontFamily;
1515 nsAutoString familyName;
1516 for (int f = 0; f < aFontSet->nfont; f++) {
1517 FcPattern* pattern = aFontSet->fonts[f];
1519 // Skip any fonts that aren't readable for us (e.g. due to restrictive
1520 // file ownership/permissions).
1521 FcChar8* path;
1522 if (FcPatternGetString(pattern, FC_FILE, 0, &path) != FcResultMatch) {
1523 continue;
1525 if (access(reinterpret_cast<const char*>(path), F_OK | R_OK) != 0) {
1526 continue;
1529 #ifdef MOZ_CONTENT_SANDBOX
1530 // Skip any fonts that will be blocked by the content-process sandbox
1531 // policy.
1532 if (aPolicy && !(aPolicy->Lookup(reinterpret_cast<const char*>(path)) &
1533 SandboxBroker::Perms::MAY_READ)) {
1534 continue;
1536 #endif
1538 AddPatternToFontList(pattern, lastFamilyName,
1539 familyName, fontFamily, aAppFonts);
1543 void
1544 gfxFcPlatformFontList::AddPatternToFontList(FcPattern* aFont,
1545 FcChar8*& aLastFamilyName,
1546 nsAString& aFamilyName,
1547 RefPtr<gfxFontconfigFontFamily>& aFontFamily,
1548 bool aAppFonts)
1550 // get canonical name
1551 uint32_t cIndex = FindCanonicalNameIndex(aFont, FC_FAMILYLANG);
1552 FcChar8* canonical = nullptr;
1553 FcPatternGetString(aFont, FC_FAMILY, cIndex, &canonical);
1554 if (!canonical) {
1555 return;
1558 // same as the last one? no need to add a new family, skip
1559 if (FcStrCmp(canonical, aLastFamilyName) != 0) {
1560 aLastFamilyName = canonical;
1562 // add new family if one doesn't already exist
1563 aFamilyName.Truncate();
1564 AppendUTF8toUTF16(ToCharPtr(canonical), aFamilyName);
1565 nsAutoString keyName(aFamilyName);
1566 ToLowerCase(keyName);
1568 aFontFamily = static_cast<gfxFontconfigFontFamily*>
1569 (mFontFamilies.GetWeak(keyName));
1570 if (!aFontFamily) {
1571 aFontFamily = new gfxFontconfigFontFamily(aFamilyName);
1572 mFontFamilies.Put(keyName, aFontFamily);
1574 // Record if the family contains fonts from the app font set
1575 // (in which case we won't rely on fontconfig's charmap, due to
1576 // bug 1276594).
1577 if (aAppFonts) {
1578 aFontFamily->SetFamilyContainsAppFonts(true);
1581 // Add pointers to other localized family names. Most fonts
1582 // only have a single name, so the first call to GetString
1583 // will usually not match
1584 FcChar8* otherName;
1585 int n = (cIndex == 0 ? 1 : 0);
1586 while (FcPatternGetString(aFont, FC_FAMILY, n, &otherName) ==
1587 FcResultMatch) {
1588 NS_ConvertUTF8toUTF16 otherFamilyName(ToCharPtr(otherName));
1589 AddOtherFamilyName(aFontFamily, otherFamilyName);
1590 n++;
1591 if (n == int(cIndex)) {
1592 n++; // skip over canonical name
1597 MOZ_ASSERT(aFontFamily, "font must belong to a font family");
1598 aFontFamily->AddFontPattern(aFont);
1600 // map the psname, fullname ==> font family for local font lookups
1601 nsAutoString psname, fullname;
1602 GetFaceNames(aFont, aFamilyName, psname, fullname);
1603 if (!psname.IsEmpty()) {
1604 ToLowerCase(psname);
1605 mLocalNames.Put(psname, aFont);
1607 if (!fullname.IsEmpty()) {
1608 ToLowerCase(fullname);
1609 mLocalNames.Put(fullname, aFont);
1613 nsresult
1614 gfxFcPlatformFontList::InitFontListForPlatform()
1616 #ifdef MOZ_BUNDLED_FONTS
1617 ActivateBundledFonts();
1618 #endif
1620 mLocalNames.Clear();
1621 mFcSubstituteCache.Clear();
1623 mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
1624 mOtherFamilyNamesInitialized = true;
1626 if (XRE_IsContentProcess()) {
1627 // Content process: use the font list passed from the chrome process,
1628 // because we can't rely on fontconfig in the presence of sandboxing;
1629 // it may report fonts that we can't actually access.
1631 FcChar8* lastFamilyName = (FcChar8*)"";
1632 RefPtr<gfxFontconfigFontFamily> fontFamily;
1633 nsAutoString familyName;
1635 // Get font list that was passed during XPCOM startup
1636 // or in an UpdateFontList message.
1637 auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList();
1639 // For fontconfig versions between 2.10.94 and 2.11.1 inclusive,
1640 // we need to escape any leading space in the charset element,
1641 // otherwise FcNameParse will fail. :(
1643 // The bug was introduced on 2013-05-24 by
1644 // https://cgit.freedesktop.org/fontconfig/commit/?id=cd9b1033a68816a7acfbba1718ba0aa5888f6ec7
1645 // "Bug 64906 - FcNameParse() should ignore leading whitespace in parameters"
1646 // because ignoring a leading space in the encoded value of charset
1647 // causes erroneous decoding of the whole element.
1648 // This first shipped in version 2.10.94, and was eventually fixed as
1649 // a side-effect of switching to the "human-readable" representation of
1650 // charsets on 2014-07-03 in
1651 // https://cgit.freedesktop.org/fontconfig/commit/?id=e708e97c351d3bc9f7030ef22ac2f007d5114730
1652 // "Change charset parse/unparse format to be human readable"
1653 // (with a followup fix next day) which means a leading space is no
1654 // longer significant. This fix landed after 2.11.1 had been shipped,
1655 // so the first version tag without the bug is 2.11.91.
1656 int fcVersion = FcGetVersion();
1657 bool fcCharsetParseBug = fcVersion >= 21094 && fcVersion <= 21101;
1659 for (SystemFontListEntry& fle : fontList) {
1660 MOZ_ASSERT(fle.type() ==
1661 SystemFontListEntry::Type::TFontPatternListEntry);
1662 FontPatternListEntry& fpe(fle);
1663 nsCString& patternStr = fpe.pattern();
1664 if (fcCharsetParseBug) {
1665 int32_t index = patternStr.Find(":charset= ");
1666 if (index != kNotFound) {
1667 // insert backslash after the =, before the space
1668 patternStr.Insert('\\', index + 9);
1671 FcPattern* pattern =
1672 FcNameParse((const FcChar8*)patternStr.get());
1673 AddPatternToFontList(pattern, lastFamilyName, familyName,
1674 fontFamily, fpe.appFontFamily());
1675 FcPatternDestroy(pattern);
1678 LOG_FONTLIST(("got font list from chrome process: "
1679 "%u faces in %u families",
1680 (unsigned)fontList.Length(), mFontFamilies.Count()));
1682 fontList.Clear();
1684 return NS_OK;
1687 mLastConfig = FcConfigGetCurrent();
1689 UniquePtr<SandboxPolicy> policy;
1691 #ifdef MOZ_CONTENT_SANDBOX
1692 // If read sandboxing is enabled, create a temporary SandboxPolicy to
1693 // check font paths; use a fake PID to avoid picking up any PID-specific
1694 // rules by accident.
1695 SandboxBrokerPolicyFactory policyFactory;
1696 if (GetEffectiveContentSandboxLevel() > 2 &&
1697 !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX")) {
1698 policy = policyFactory.GetContentPolicy(-1, false);
1700 #endif
1702 // iterate over available fonts
1703 FcFontSet* systemFonts = FcConfigGetFonts(nullptr, FcSetSystem);
1704 AddFontSetFamilies(systemFonts, policy.get(), /* aAppFonts = */ false);
1706 #ifdef MOZ_BUNDLED_FONTS
1707 FcFontSet* appFonts = FcConfigGetFonts(nullptr, FcSetApplication);
1708 AddFontSetFamilies(appFonts, policy.get(), /* aAppFonts = */ true);
1709 #endif
1711 return NS_OK;
1714 void
1715 gfxFcPlatformFontList::ReadSystemFontList(
1716 InfallibleTArray<SystemFontListEntry>* retValue)
1718 // Fontconfig versions below 2.9 drop the FC_FILE element in FcNameUnparse
1719 // (see https://bugs.freedesktop.org/show_bug.cgi?id=26718), so when using
1720 // an older version, we manually append it to the unparsed pattern.
1721 if (FcGetVersion() < 20900) {
1722 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1723 auto family =
1724 static_cast<gfxFontconfigFontFamily*>(iter.Data().get());
1725 family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
1726 char* s = (char*)FcNameUnparse(aPat);
1727 nsAutoCString patternStr(s);
1728 free(s);
1729 if (FcResultMatch ==
1730 FcPatternGetString(aPat, FC_FILE, 0, (FcChar8**)&s)) {
1731 patternStr.Append(":file=");
1732 patternStr.Append(s);
1734 retValue->AppendElement(FontPatternListEntry(patternStr,
1735 aAppFonts));
1738 } else {
1739 for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
1740 auto family =
1741 static_cast<gfxFontconfigFontFamily*>(iter.Data().get());
1742 family->AddFacesToFontList([&](FcPattern* aPat, bool aAppFonts) {
1743 char* s = (char*)FcNameUnparse(aPat);
1744 nsDependentCString patternStr(s);
1745 retValue->AppendElement(FontPatternListEntry(patternStr,
1746 aAppFonts));
1747 free(s);
1753 // For displaying the fontlist in UI, use explicit call to FcFontList. Using
1754 // FcFontList results in the list containing the localized names as dictated
1755 // by system defaults.
1756 static void
1757 GetSystemFontList(nsTArray<nsString>& aListOfFonts, nsAtom *aLangGroup)
1759 aListOfFonts.Clear();
1761 nsAutoRef<FcPattern> pat(FcPatternCreate());
1762 if (!pat) {
1763 return;
1766 nsAutoRef<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
1767 if (!os) {
1768 return;
1771 // add the lang to the pattern
1772 nsAutoCString fcLang;
1773 gfxFcPlatformFontList* pfl = gfxFcPlatformFontList::PlatformFontList();
1774 pfl->GetSampleLangForGroup(aLangGroup, fcLang);
1775 if (!fcLang.IsEmpty()) {
1776 FcPatternAddString(pat, FC_LANG, ToFcChar8Ptr(fcLang.get()));
1779 nsAutoRef<FcFontSet> fs(FcFontList(nullptr, pat, os));
1780 if (!fs) {
1781 return;
1784 for (int i = 0; i < fs->nfont; i++) {
1785 char *family;
1787 if (FcPatternGetString(fs->fonts[i], FC_FAMILY, 0,
1788 (FcChar8 **) &family) != FcResultMatch)
1790 continue;
1793 // Remove duplicates...
1794 nsAutoString strFamily;
1795 AppendUTF8toUTF16(family, strFamily);
1796 if (aListOfFonts.Contains(strFamily)) {
1797 continue;
1800 aListOfFonts.AppendElement(strFamily);
1803 aListOfFonts.Sort();
1806 void
1807 gfxFcPlatformFontList::GetFontList(nsAtom *aLangGroup,
1808 const nsACString& aGenericFamily,
1809 nsTArray<nsString>& aListOfFonts)
1811 // Get the list of font family names using fontconfig
1812 GetSystemFontList(aListOfFonts, aLangGroup);
1814 // Under Linux, the generics "serif", "sans-serif" and "monospace"
1815 // are included in the pref fontlist. These map to whatever fontconfig
1816 // decides they should be for a given language, rather than one of the
1817 // fonts listed in the prefs font lists (e.g. font.name.*, font.name-list.*)
1818 bool serif = false, sansSerif = false, monospace = false;
1819 if (aGenericFamily.IsEmpty())
1820 serif = sansSerif = monospace = true;
1821 else if (aGenericFamily.LowerCaseEqualsLiteral("serif"))
1822 serif = true;
1823 else if (aGenericFamily.LowerCaseEqualsLiteral("sans-serif"))
1824 sansSerif = true;
1825 else if (aGenericFamily.LowerCaseEqualsLiteral("monospace"))
1826 monospace = true;
1827 else if (aGenericFamily.LowerCaseEqualsLiteral("cursive") ||
1828 aGenericFamily.LowerCaseEqualsLiteral("fantasy"))
1829 serif = sansSerif = true;
1830 else
1831 NS_NOTREACHED("unexpected CSS generic font family");
1833 // The first in the list becomes the default in
1834 // FontBuilder.readFontSelection() if the preference-selected font is not
1835 // available, so put system configured defaults first.
1836 if (monospace)
1837 aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("monospace"));
1838 if (sansSerif)
1839 aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("sans-serif"));
1840 if (serif)
1841 aListOfFonts.InsertElementAt(0, NS_LITERAL_STRING("serif"));
1844 gfxFontFamily*
1845 gfxFcPlatformFontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
1847 // Get the default font by using a fake name to retrieve the first
1848 // scalable font that fontconfig suggests for the given language.
1849 PrefFontList* prefFonts =
1850 FindGenericFamilies(NS_LITERAL_STRING("-moz-default"), aStyle->language);
1851 NS_ASSERTION(prefFonts, "null list of generic fonts");
1852 if (prefFonts && !prefFonts->IsEmpty()) {
1853 return (*prefFonts)[0];
1855 return nullptr;
1858 gfxFontEntry*
1859 gfxFcPlatformFontList::LookupLocalFont(const nsAString& aFontName,
1860 uint16_t aWeight,
1861 int16_t aStretch,
1862 uint8_t aStyle)
1864 nsAutoString keyName(aFontName);
1865 ToLowerCase(keyName);
1867 // if name is not in the global list, done
1868 FcPattern* fontPattern = mLocalNames.Get(keyName);
1869 if (!fontPattern) {
1870 return nullptr;
1873 return new gfxFontconfigFontEntry(aFontName, fontPattern,
1874 aWeight, aStretch, aStyle);
1877 gfxFontEntry*
1878 gfxFcPlatformFontList::MakePlatformFont(const nsAString& aFontName,
1879 uint16_t aWeight,
1880 int16_t aStretch,
1881 uint8_t aStyle,
1882 const uint8_t* aFontData,
1883 uint32_t aLength)
1885 FT_Face face = Factory::NewFTFaceFromData(nullptr, aFontData, aLength, 0);
1886 if (!face) {
1887 free((void*)aFontData);
1888 return nullptr;
1890 if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
1891 Factory::ReleaseFTFace(face);
1892 free((void*)aFontData);
1893 return nullptr;
1896 return new gfxFontconfigFontEntry(aFontName, aWeight, aStretch,
1897 aStyle, aFontData, aLength, face);
1900 bool
1901 gfxFcPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
1902 nsTArray<gfxFontFamily*>* aOutput,
1903 FindFamiliesFlags aFlags,
1904 gfxFontStyle* aStyle,
1905 gfxFloat aDevToCssSize)
1907 nsAutoString familyName(aFamily);
1908 ToLowerCase(familyName);
1909 nsAtom* language = (aStyle ? aStyle->language.get() : nullptr);
1911 // deprecated generic names are explicitly converted to standard generics
1912 bool isDeprecatedGeneric = false;
1913 if (familyName.EqualsLiteral("sans") ||
1914 familyName.EqualsLiteral("sans serif")) {
1915 familyName.AssignLiteral("sans-serif");
1916 isDeprecatedGeneric = true;
1917 } else if (familyName.EqualsLiteral("mono")) {
1918 familyName.AssignLiteral("monospace");
1919 isDeprecatedGeneric = true;
1922 // fontconfig generics? use fontconfig to determine the family for lang
1923 if (isDeprecatedGeneric ||
1924 mozilla::FontFamilyName::Convert(familyName).IsGeneric()) {
1925 PrefFontList* prefFonts = FindGenericFamilies(familyName, language);
1926 if (prefFonts && !prefFonts->IsEmpty()) {
1927 aOutput->AppendElements(*prefFonts);
1928 return true;
1930 return false;
1933 // fontconfig allows conditional substitutions in such a way that it's
1934 // difficult to distinguish an explicit substitution from other suggested
1935 // choices. To sniff out explicit substitutions, compare the substitutions
1936 // for "font, -moz-sentinel" to "-moz-sentinel" to sniff out the
1937 // substitutions
1939 // Example:
1941 // serif ==> DejaVu Serif, ...
1942 // Helvetica, serif ==> Helvetica, TeX Gyre Heros, Nimbus Sans L, DejaVu Serif
1944 // In this case fontconfig is including Tex Gyre Heros and
1945 // Nimbus Sans L as alternatives for Helvetica.
1947 // Because the FcConfigSubstitute call is quite expensive, we cache the
1948 // actual font families found via this process. So check the cache first:
1949 NS_ConvertUTF16toUTF8 familyToFind(familyName);
1950 AutoTArray<gfxFontFamily*,10> cachedFamilies;
1951 if (mFcSubstituteCache.Get(familyToFind, &cachedFamilies)) {
1952 if (cachedFamilies.IsEmpty()) {
1953 return false;
1955 aOutput->AppendElements(cachedFamilies);
1956 return true;
1959 // It wasn't in the cache, so we need to ask fontconfig...
1960 const FcChar8* kSentinelName = ToFcChar8Ptr("-moz-sentinel");
1961 FcChar8* sentinelFirstFamily = nullptr;
1962 nsAutoRef<FcPattern> sentinelSubst(FcPatternCreate());
1963 FcPatternAddString(sentinelSubst, FC_FAMILY, kSentinelName);
1964 FcConfigSubstitute(nullptr, sentinelSubst, FcMatchPattern);
1965 FcPatternGetString(sentinelSubst, FC_FAMILY, 0, &sentinelFirstFamily);
1967 // substitutions for font, -moz-sentinel pattern
1968 nsAutoRef<FcPattern> fontWithSentinel(FcPatternCreate());
1969 FcPatternAddString(fontWithSentinel, FC_FAMILY,
1970 ToFcChar8Ptr(familyToFind.get()));
1971 FcPatternAddString(fontWithSentinel, FC_FAMILY, kSentinelName);
1972 FcConfigSubstitute(nullptr, fontWithSentinel, FcMatchPattern);
1974 // Add all font family matches until reaching the sentinel.
1975 FcChar8* substName = nullptr;
1976 for (int i = 0;
1977 FcPatternGetString(fontWithSentinel, FC_FAMILY,
1978 i, &substName) == FcResultMatch;
1979 i++)
1981 NS_ConvertUTF8toUTF16 subst(ToCharPtr(substName));
1982 if (sentinelFirstFamily &&
1983 FcStrCmp(substName, sentinelFirstFamily) == 0) {
1984 break;
1986 gfxPlatformFontList::FindAndAddFamilies(subst,
1987 &cachedFamilies,
1988 aFlags);
1991 // Cache the resulting list, so we don't have to do this again.
1992 mFcSubstituteCache.Put(familyToFind, cachedFamilies);
1994 if (cachedFamilies.IsEmpty()) {
1995 return false;
1997 aOutput->AppendElements(cachedFamilies);
1998 return true;
2001 bool
2002 gfxFcPlatformFontList::GetStandardFamilyName(const nsAString& aFontName,
2003 nsAString& aFamilyName)
2005 aFamilyName.Truncate();
2007 // The fontconfig list of fonts includes generic family names in the
2008 // font list. For these, just use the generic name.
2009 if (aFontName.EqualsLiteral("serif") ||
2010 aFontName.EqualsLiteral("sans-serif") ||
2011 aFontName.EqualsLiteral("monospace")) {
2012 aFamilyName.Assign(aFontName);
2013 return true;
2016 nsAutoRef<FcPattern> pat(FcPatternCreate());
2017 if (!pat) {
2018 return true;
2021 nsAutoRef<FcObjectSet> os(FcObjectSetBuild(FC_FAMILY, nullptr));
2022 if (!os) {
2023 return true;
2026 // add the family name to the pattern
2027 NS_ConvertUTF16toUTF8 familyName(aFontName);
2028 FcPatternAddString(pat, FC_FAMILY, ToFcChar8Ptr(familyName.get()));
2030 nsAutoRef<FcFontSet> givenFS(FcFontList(nullptr, pat, os));
2031 if (!givenFS) {
2032 return true;
2035 // See if there is a font face with first family equal to the given family
2036 // (needs to be in sync with names coming from GetFontList())
2037 nsTArray<nsCString> candidates;
2038 for (int i = 0; i < givenFS->nfont; i++) {
2039 char* firstFamily;
2041 if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
2042 (FcChar8 **) &firstFamily) != FcResultMatch)
2044 continue;
2047 nsDependentCString first(firstFamily);
2048 if (!candidates.Contains(first)) {
2049 candidates.AppendElement(first);
2051 if (familyName.Equals(first)) {
2052 aFamilyName.Assign(aFontName);
2053 return true;
2058 // Because fontconfig conflates different family name types, need to
2059 // double check that the candidate name is not simply a different
2060 // name type. For example, if a font with nameID=16 "Minion Pro" and
2061 // nameID=21 "Minion Pro Caption" exists, calling FcFontList with
2062 // family="Minion Pro" will return a set of patterns some of which
2063 // will have a first family of "Minion Pro Caption". Ignore these
2064 // patterns and use the first candidate that maps to a font set with
2065 // the same number of faces and an identical set of patterns.
2066 for (uint32_t j = 0; j < candidates.Length(); ++j) {
2067 FcPatternDel(pat, FC_FAMILY);
2068 FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)candidates[j].get());
2070 nsAutoRef<FcFontSet> candidateFS(FcFontList(nullptr, pat, os));
2071 if (!candidateFS) {
2072 return true;
2075 if (candidateFS->nfont != givenFS->nfont) {
2076 continue;
2079 bool equal = true;
2080 for (int i = 0; i < givenFS->nfont; ++i) {
2081 if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
2082 equal = false;
2083 break;
2086 if (equal) {
2087 AppendUTF8toUTF16(candidates[j], aFamilyName);
2088 return true;
2092 // didn't find localized name, leave family name blank
2093 return true;
2096 void
2097 gfxFcPlatformFontList::AddGenericFonts(mozilla::FontFamilyType aGenericType,
2098 nsAtom* aLanguage,
2099 nsTArray<gfxFontFamily*>& aFamilyList)
2101 bool usePrefFontList = false;
2103 // treat -moz-fixed as monospace
2104 if (aGenericType == eFamily_moz_fixed) {
2105 aGenericType = eFamily_monospace;
2108 const char* generic = GetGenericName(aGenericType);
2109 NS_ASSERTION(generic, "weird generic font type");
2110 if (!generic) {
2111 return;
2114 // By default, most font prefs on Linux map to "use fontconfig"
2115 // keywords. So only need to explicitly lookup font pref if
2116 // non-default settings exist
2117 NS_ConvertASCIItoUTF16 genericToLookup(generic);
2118 if ((!mAlwaysUseFontconfigGenerics && aLanguage) ||
2119 aLanguage == nsGkAtoms::x_math) {
2120 nsAtom* langGroup = GetLangGroup(aLanguage);
2121 nsAutoString fontlistValue;
2122 Preferences::GetString(NamePref(generic, langGroup).get(),
2123 fontlistValue);
2124 nsresult rv;
2125 if (fontlistValue.IsEmpty()) {
2126 // The font name list may have two or more family names as comma
2127 // separated list. In such case, not matching with generic font
2128 // name is fine because if the list prefers specific font, we
2129 // should try to use the pref with complicated path.
2130 rv = Preferences::GetString(NameListPref(generic, langGroup).get(),
2131 fontlistValue);
2132 } else {
2133 rv = NS_OK;
2135 if (NS_SUCCEEDED(rv)) {
2136 if (!fontlistValue.EqualsLiteral("serif") &&
2137 !fontlistValue.EqualsLiteral("sans-serif") &&
2138 !fontlistValue.EqualsLiteral("monospace")) {
2139 usePrefFontList = true;
2140 } else {
2141 // serif, sans-serif or monospace was specified
2142 genericToLookup.Assign(fontlistValue);
2147 // when pref fonts exist, use standard pref font lookup
2148 if (usePrefFontList) {
2149 return gfxPlatformFontList::AddGenericFonts(aGenericType,
2150 aLanguage,
2151 aFamilyList);
2154 PrefFontList* prefFonts = FindGenericFamilies(genericToLookup, aLanguage);
2155 NS_ASSERTION(prefFonts, "null generic font list");
2156 aFamilyList.AppendElements(*prefFonts);
2159 void
2160 gfxFcPlatformFontList::ClearLangGroupPrefFonts()
2162 ClearGenericMappings();
2163 gfxPlatformFontList::ClearLangGroupPrefFonts();
2164 mAlwaysUseFontconfigGenerics = PrefFontListsUseOnlyGenerics();
2167 /* static */ FT_Library
2168 gfxFcPlatformFontList::GetFTLibrary()
2170 if (!sCairoFTLibrary) {
2171 // Use cairo's FT_Library so that cairo takes care of shutdown of the
2172 // FT_Library after it has destroyed its font_faces, and FT_Done_Face
2173 // has been called on each FT_Face, at least until this bug is fixed:
2174 // https://bugs.freedesktop.org/show_bug.cgi?id=18857
2176 // Cairo keeps it's own FT_Library object for creating FT_Face
2177 // instances, so use that. There's no simple API for accessing this
2178 // so use the hacky method below of making a font and extracting
2179 // the library pointer from that.
2181 bool needsBold;
2182 gfxFontStyle style;
2183 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
2184 gfxFontFamily* family = pfl->GetDefaultFont(&style);
2185 NS_ASSERTION(family, "couldn't find a default font family");
2186 gfxFontEntry* fe = family->FindFontForStyle(style, needsBold, true);
2187 if (!fe) {
2188 return nullptr;
2190 RefPtr<gfxFont> font = fe->FindOrMakeFont(&style, false);
2191 if (!font) {
2192 return nullptr;
2195 gfxFT2FontBase* ft2Font = reinterpret_cast<gfxFT2FontBase*>(font.get());
2196 gfxFT2LockedFace face(ft2Font);
2197 if (!face.get()) {
2198 return nullptr;
2201 sCairoFTLibrary = face.get()->glyph->library;
2204 return sCairoFTLibrary;
2207 gfxPlatformFontList::PrefFontList*
2208 gfxFcPlatformFontList::FindGenericFamilies(const nsAString& aGeneric,
2209 nsAtom* aLanguage)
2211 // set up name
2212 NS_ConvertUTF16toUTF8 generic(aGeneric);
2214 nsAutoCString fcLang;
2215 GetSampleLangForGroup(aLanguage, fcLang);
2216 ToLowerCase(fcLang);
2218 nsAutoCString genericLang(generic);
2219 if (fcLang.Length() > 0) {
2220 genericLang.Append('-');
2222 genericLang.Append(fcLang);
2224 // try to get the family from the cache
2225 PrefFontList* prefFonts = mGenericMappings.Get(genericLang);
2226 if (prefFonts) {
2227 return prefFonts;
2230 // if not found, ask fontconfig to pick the appropriate font
2231 nsAutoRef<FcPattern> genericPattern(FcPatternCreate());
2232 FcPatternAddString(genericPattern, FC_FAMILY,
2233 ToFcChar8Ptr(generic.get()));
2235 // -- prefer scalable fonts
2236 FcPatternAddBool(genericPattern, FC_SCALABLE, FcTrue);
2238 // -- add the lang to the pattern
2239 if (!fcLang.IsEmpty()) {
2240 FcPatternAddString(genericPattern, FC_LANG,
2241 ToFcChar8Ptr(fcLang.get()));
2244 // -- perform substitutions
2245 FcConfigSubstitute(nullptr, genericPattern, FcMatchPattern);
2246 FcDefaultSubstitute(genericPattern);
2248 // -- sort to get the closest matches
2249 FcResult result;
2250 nsAutoRef<FcFontSet> faces(FcFontSort(nullptr, genericPattern, FcFalse,
2251 nullptr, &result));
2253 if (!faces) {
2254 return nullptr;
2257 // -- select the fonts to be used for the generic
2258 prefFonts = new PrefFontList; // can be empty but in practice won't happen
2259 uint32_t limit = gfxPlatformGtk::GetPlatform()->MaxGenericSubstitions();
2260 bool foundFontWithLang = false;
2261 for (int i = 0; i < faces->nfont; i++) {
2262 FcPattern* font = faces->fonts[i];
2263 FcChar8* mappedGeneric = nullptr;
2265 FcPatternGetString(font, FC_FAMILY, 0, &mappedGeneric);
2266 if (mappedGeneric) {
2267 NS_ConvertUTF8toUTF16 mappedGenericName(ToCharPtr(mappedGeneric));
2268 AutoTArray<gfxFontFamily*,1> genericFamilies;
2269 if (gfxPlatformFontList::FindAndAddFamilies(mappedGenericName,
2270 &genericFamilies,
2271 FindFamiliesFlags(0))) {
2272 MOZ_ASSERT(genericFamilies.Length() == 1,
2273 "expected a single family");
2274 if (!prefFonts->Contains(genericFamilies[0])) {
2275 prefFonts->AppendElement(genericFamilies[0]);
2276 bool foundLang =
2277 !fcLang.IsEmpty() &&
2278 PatternHasLang(font, ToFcChar8Ptr(fcLang.get()));
2279 foundFontWithLang = foundFontWithLang || foundLang;
2280 // check to see if the list is full
2281 if (prefFonts->Length() >= limit) {
2282 break;
2289 // if no font in the list matches the lang, trim all but the first one
2290 if (!prefFonts->IsEmpty() && !foundFontWithLang) {
2291 prefFonts->TruncateLength(1);
2294 mGenericMappings.Put(genericLang, prefFonts);
2295 return prefFonts;
2298 bool
2299 gfxFcPlatformFontList::PrefFontListsUseOnlyGenerics()
2301 static const char kFontNamePrefix[] = "font.name.";
2303 bool prefFontsUseOnlyGenerics = true;
2304 uint32_t count;
2305 char** names;
2306 nsresult rv = Preferences::GetRootBranch()->
2307 GetChildList(kFontNamePrefix, &count, &names);
2308 if (NS_SUCCEEDED(rv) && count) {
2309 for (size_t i = 0; i < count; i++) {
2310 // Check whether all font.name prefs map to generic keywords
2311 // and that the pref name and keyword match.
2312 // Ex: font.name.serif.ar ==> "serif" (ok)
2313 // Ex: font.name.serif.ar ==> "monospace" (return false)
2314 // Ex: font.name.serif.ar ==> "DejaVu Serif" (return false)
2315 // Ex: font.name.serif.ar ==> "" and
2316 // font.name-list.serif.ar ==> "serif" (ok)
2317 // Ex: font.name.serif.ar ==> "" and
2318 // font.name-list.serif.ar ==> "Something, serif"
2319 // (return false)
2321 nsDependentCString prefName(names[i] +
2322 ArrayLength(kFontNamePrefix) - 1);
2323 nsCCharSeparatedTokenizer tokenizer(prefName, '.');
2324 const nsDependentCSubstring& generic = tokenizer.nextToken();
2325 const nsDependentCSubstring& langGroup = tokenizer.nextToken();
2326 nsAutoCString fontPrefValue;
2327 Preferences::GetCString(names[i], fontPrefValue);
2328 if (fontPrefValue.IsEmpty()) {
2329 // The font name list may have two or more family names as comma
2330 // separated list. In such case, not matching with generic font
2331 // name is fine because if the list prefers specific font, this
2332 // should return false.
2333 Preferences::GetCString(NameListPref(generic, langGroup).get(),
2334 fontPrefValue);
2337 if (!langGroup.EqualsLiteral("x-math") &&
2338 !generic.Equals(fontPrefValue)) {
2339 prefFontsUseOnlyGenerics = false;
2340 break;
2343 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, names);
2345 return prefFontsUseOnlyGenerics;
2348 /* static */ void
2349 gfxFcPlatformFontList::CheckFontUpdates(nsITimer *aTimer, void *aThis)
2351 // A content process is not supposed to check this directly;
2352 // it will be notified by the parent when the font list changes.
2353 MOZ_ASSERT(XRE_IsParentProcess());
2355 // check for font updates
2356 FcInitBringUptoDate();
2358 // update fontlist if current config changed
2359 gfxFcPlatformFontList *pfl = static_cast<gfxFcPlatformFontList*>(aThis);
2360 FcConfig* current = FcConfigGetCurrent();
2361 if (current != pfl->GetLastConfig()) {
2362 pfl->UpdateFontList();
2363 pfl->ForceGlobalReflow();
2365 mozilla::dom::ContentParent::NotifyUpdatedFonts();
2369 gfxFontFamily*
2370 gfxFcPlatformFontList::CreateFontFamily(const nsAString& aName) const
2372 return new gfxFontconfigFontFamily(aName);
2375 #ifdef MOZ_BUNDLED_FONTS
2376 void
2377 gfxFcPlatformFontList::ActivateBundledFonts()
2379 if (!mBundledFontsInitialized) {
2380 mBundledFontsInitialized = true;
2381 nsCOMPtr<nsIFile> localDir;
2382 nsresult rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(localDir));
2383 if (NS_FAILED(rv)) {
2384 return;
2386 if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("fonts")))) {
2387 return;
2389 bool isDir;
2390 if (NS_FAILED(localDir->IsDirectory(&isDir)) || !isDir) {
2391 return;
2393 if (NS_FAILED(localDir->GetNativePath(mBundledFontsPath))) {
2394 return;
2397 if (!mBundledFontsPath.IsEmpty()) {
2398 FcConfigAppFontAddDir(nullptr, ToFcChar8Ptr(mBundledFontsPath.get()));
2401 #endif
2403 #ifdef MOZ_WIDGET_GTK
2404 /***************************************************************************
2406 * This function must be last in the file because it uses the system cairo
2407 * library. Above this point the cairo library used is the tree cairo if
2408 * MOZ_TREE_CAIRO.
2411 #if MOZ_TREE_CAIRO
2412 // Tree cairo symbols have different names. Disable their activation through
2413 // preprocessor macros.
2414 #undef cairo_ft_font_options_substitute
2416 // The system cairo functions are not declared because the include paths cause
2417 // the gdk headers to pick up the tree cairo.h.
2418 extern "C" {
2419 NS_VISIBILITY_DEFAULT void
2420 cairo_ft_font_options_substitute (const cairo_font_options_t *options,
2421 FcPattern *pattern);
2423 #endif
2425 static void
2426 ApplyGdkScreenFontOptions(FcPattern *aPattern)
2428 const cairo_font_options_t *options =
2429 gdk_screen_get_font_options(gdk_screen_get_default());
2431 cairo_ft_font_options_substitute(options, aPattern);
2434 #endif // MOZ_WIDGET_GTK