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