gfxFT2FontList.cpp (69251B)
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/Base64.h" 7 #include "mozilla/MemoryReporting.h" 8 9 #include "mozilla/dom/ContentChild.h" 10 #include "gfxAndroidPlatform.h" 11 #include "mozilla/Omnijar.h" 12 #include "mozilla/UniquePtr.h" 13 #include "mozilla/UniquePtrExtensions.h" 14 #include "nsReadableUtils.h" 15 16 #include "nsXULAppAPI.h" 17 #include <dirent.h> 18 #include <android/log.h> 19 #define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko", ##args) 20 21 #include "ft2build.h" 22 #include FT_FREETYPE_H 23 #include FT_TRUETYPE_TAGS_H 24 #include FT_TRUETYPE_TABLES_H 25 #include FT_MULTIPLE_MASTERS_H 26 #include "cairo-ft.h" 27 28 #include "gfxFT2FontList.h" 29 #include "gfxFT2Fonts.h" 30 #include "gfxFT2Utils.h" 31 #include "gfxUserFontSet.h" 32 #include "gfxFontUtils.h" 33 #include "SharedFontList-impl.h" 34 #define StandardFonts 35 #include "StandardFonts-android.inc" 36 #undef StandardFonts 37 #include "harfbuzz/hb-ot.h" // for name ID constants 38 39 #include "nsServiceManagerUtils.h" 40 #include "nsIGfxInfo.h" 41 #include "mozilla/Components.h" 42 #include "nsIObserverService.h" 43 #include "nsTArray.h" 44 #include "nsUnicharUtils.h" 45 #include "nsCRT.h" 46 47 #include "nsDirectoryServiceUtils.h" 48 #include "nsDirectoryServiceDefs.h" 49 #include "nsAppDirectoryServiceDefs.h" 50 #include "nsMemory.h" 51 #include "nsPresContext.h" 52 #include "gfxFontConstants.h" 53 54 #include "mozilla/EndianUtils.h" 55 #include "mozilla/Preferences.h" 56 #include "mozilla/scache/StartupCache.h" 57 #include "mozilla/glean/GfxMetrics.h" 58 #include <fcntl.h> 59 #include <sys/mman.h> 60 #include <sys/stat.h> 61 62 #ifdef MOZ_WIDGET_ANDROID 63 # include "AndroidBuild.h" 64 # include "AndroidSystemFontIterator.h" 65 # include "mozilla/jni/Utils.h" 66 #endif 67 68 using namespace mozilla; 69 using namespace mozilla::gfx; 70 71 static LazyLogModule sFontInfoLog("fontInfoLog"); 72 73 #undef LOG 74 #define LOG(args) MOZ_LOG(sFontInfoLog, mozilla::LogLevel::Debug, args) 75 #define LOG_ENABLED() MOZ_LOG_TEST(sFontInfoLog, mozilla::LogLevel::Debug) 76 77 static __inline void BuildKeyNameFromFontName(nsACString& aName) { 78 ToLowerCase(aName); 79 } 80 81 // Helper to access the FT_Face for a given FT2FontEntry, 82 // creating a temporary face if the entry does not have one yet. 83 // This allows us to read font names, tables, etc if necessary 84 // without permanently instantiating a freetype face and consuming 85 // memory long-term. 86 // This may fail (resulting in a null FT_Face), e.g. if it fails to 87 // allocate memory to uncompress a font from omnijar. 88 already_AddRefed<SharedFTFace> FT2FontEntry::GetFTFace(bool aCommit) { 89 if (mFTFace) { 90 // Create a new reference, and return it. 91 RefPtr<SharedFTFace> face(mFTFace); 92 return face.forget(); 93 } 94 95 NS_ASSERTION(!mFilename.IsEmpty(), 96 "can't use GetFTFace for fonts without a filename"); 97 98 // A relative path (no initial "/") means this is a resource in 99 // omnijar, not an installed font on the device. 100 // The NS_ASSERTIONs here should never fail, as the resource must have 101 // been read successfully during font-list initialization or we'd never 102 // have created the font entry. The only legitimate runtime failure 103 // here would be memory allocation, in which case mFace remains null. 104 RefPtr<SharedFTFace> face; 105 if (mFilename[0] != '/') { 106 RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE); 107 nsZipItem* item = reader->GetItem(mFilename); 108 NS_ASSERTION(item, "failed to find zip entry"); 109 110 uint32_t bufSize = item->RealSize(); 111 uint8_t* fontDataBuf = static_cast<uint8_t*>(malloc(bufSize)); 112 if (fontDataBuf) { 113 nsZipCursor cursor(item, reader, fontDataBuf, bufSize); 114 cursor.Copy(&bufSize); 115 NS_ASSERTION(bufSize == item->RealSize(), "error reading bundled font"); 116 RefPtr<FTUserFontData> ufd = new FTUserFontData(fontDataBuf, bufSize); 117 face = ufd->CloneFace(mFTFontIndex); 118 if (!face) { 119 NS_WARNING("failed to create freetype face"); 120 return nullptr; 121 } 122 } 123 } else { 124 RefPtr<FTUserFontData> fd = new FTUserFontData(mFilename.get()); 125 face = fd->CloneFace(mFTFontIndex); 126 if (!face) { 127 NS_WARNING("failed to create freetype face"); 128 return nullptr; 129 } 130 if (FT_Err_Ok != FT_Select_Charmap(face->GetFace(), FT_ENCODING_UNICODE) && 131 FT_Err_Ok != 132 FT_Select_Charmap(face->GetFace(), FT_ENCODING_MS_SYMBOL)) { 133 NS_WARNING("failed to select Unicode or symbol charmap"); 134 } 135 } 136 137 if (aCommit) { 138 if (mFTFace.compareExchange(nullptr, face.get())) { 139 // The reference we created is now owned by mFTFace. 140 face.forget().leak(); 141 } else { 142 // We lost a race! Just discard our new face and use the existing one. 143 } 144 } 145 146 // Create a new reference, and return it. 147 face = mFTFace; 148 return face.forget(); 149 } 150 151 FTUserFontData* FT2FontEntry::GetUserFontData() { 152 if (SharedFTFace* face = mFTFace) { 153 return static_cast<FTUserFontData*>(face->GetData()); 154 } 155 return nullptr; 156 } 157 158 /* 159 * FT2FontEntry 160 * gfxFontEntry subclass corresponding to a specific face that can be 161 * rendered by freetype. This is associated with a face index in a 162 * file (normally a .ttf/.otf file holding a single face, but in principle 163 * there could be .ttc files with multiple faces). 164 * The FT2FontEntry can create the necessary FT_Face on demand, and can 165 * then create a Cairo font_face and scaled_font for drawing. 166 */ 167 168 FT2FontEntry::~FT2FontEntry() { 169 if (mMMVar) { 170 SharedFTFace* face = mFTFace; 171 FT_Done_MM_Var(face->GetFace()->glyph->library, mMMVar); 172 } 173 if (mFTFace) { 174 auto face = mFTFace.exchange(nullptr); 175 NS_IF_RELEASE(face); 176 } 177 } 178 179 gfxFontEntry* FT2FontEntry::Clone() const { 180 MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!"); 181 FT2FontEntry* fe = new FT2FontEntry(Name()); 182 fe->mFilename = mFilename; 183 fe->mFTFontIndex = mFTFontIndex; 184 fe->mWeightRange = mWeightRange; 185 fe->mStretchRange = mStretchRange; 186 fe->mStyleRange = mStyleRange; 187 return fe; 188 } 189 190 gfxFont* FT2FontEntry::CreateFontInstance(const gfxFontStyle* aStyle) { 191 RefPtr<SharedFTFace> face = GetFTFace(true); 192 if (!face) { 193 return nullptr; 194 } 195 196 if (face->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { 197 // Resolve variations from entry (descriptor) and style (property) 198 AutoTArray<gfxFontVariation, 8> settings; 199 GetVariationsForStyle(settings, aStyle ? *aStyle : gfxFontStyle()); 200 201 // If variations are present, we will not use our cached mFTFace 202 // but always create a new one as it will have custom variation 203 // coordinates applied. 204 if (!settings.IsEmpty()) { 205 // Create a separate FT_Face because we need to apply custom 206 // variation settings to it. 207 RefPtr<SharedFTFace> varFace; 208 if (!mFilename.IsEmpty() && mFilename[0] == '/') { 209 varFace = 210 Factory::NewSharedFTFace(nullptr, mFilename.get(), mFTFontIndex); 211 } else { 212 varFace = face->GetData()->CloneFace(mFTFontIndex); 213 } 214 if (varFace) { 215 gfxFT2FontBase::SetupVarCoords(GetMMVar(), settings, 216 varFace->GetFace()); 217 face = std::move(varFace); 218 } 219 } 220 } 221 222 int loadFlags = gfxPlatform::GetPlatform()->FontHintingEnabled() 223 ? FT_LOAD_DEFAULT 224 : (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING); 225 if (face->GetFace()->face_flags & FT_FACE_FLAG_TRICKY) { 226 loadFlags &= ~FT_LOAD_NO_AUTOHINT; 227 } 228 229 RefPtr<UnscaledFontFreeType> unscaledFont(mUnscaledFont); 230 if (!unscaledFont) { 231 RefPtr<SharedFTFace> origFace(mFTFace); 232 unscaledFont = 233 !mFilename.IsEmpty() && mFilename[0] == '/' 234 ? new UnscaledFontFreeType(mFilename.BeginReading(), mFTFontIndex, 235 std::move(origFace)) 236 : new UnscaledFontFreeType(std::move(origFace)); 237 mUnscaledFont = unscaledFont; 238 } 239 240 gfxFont* font = 241 new gfxFT2Font(unscaledFont, std::move(face), this, aStyle, loadFlags); 242 return font; 243 } 244 245 /* static */ 246 FT2FontEntry* FT2FontEntry::CreateFontEntry( 247 const nsACString& aFontName, WeightRange aWeight, StretchRange aStretch, 248 SlantStyleRange aStyle, const uint8_t* aFontData, uint32_t aLength) { 249 // Ownership of aFontData is passed in here; the fontEntry must 250 // retain it as long as the FT_Face needs it, and ensure it is 251 // eventually deleted. 252 RefPtr<FTUserFontData> ufd = new FTUserFontData(aFontData, aLength); 253 RefPtr<SharedFTFace> face = ufd->CloneFace(); 254 if (!face) { 255 return nullptr; 256 } 257 // Create our FT2FontEntry, which inherits the name of the userfont entry 258 // as it's not guaranteed that the face has valid names (bug 737315) 259 FT2FontEntry* fe = 260 FT2FontEntry::CreateFontEntry(aFontName, nullptr, 0, nullptr); 261 if (fe) { 262 fe->mFTFace = face.forget().take(); // mFTFace takes ownership. 263 fe->mStyleRange = aStyle; 264 fe->mWeightRange = aWeight; 265 fe->mStretchRange = aStretch; 266 fe->mIsDataUserFont = true; 267 } 268 return fe; 269 } 270 271 /* static */ 272 FT2FontEntry* FT2FontEntry::CreateFontEntry(const FontListEntry& aFLE) { 273 FT2FontEntry* fe = new FT2FontEntry(aFLE.faceName()); 274 fe->mFilename = aFLE.filepath(); 275 fe->mFTFontIndex = aFLE.index(); 276 fe->mWeightRange = WeightRange::FromScalar(aFLE.weightRange()); 277 fe->mStretchRange = StretchRange::FromScalar(aFLE.stretchRange()); 278 fe->mStyleRange = SlantStyleRange::FromScalar(aFLE.styleRange()); 279 return fe; 280 } 281 282 // Extract font entry properties from an hb_face_t 283 static void SetPropertiesFromFace(gfxFontEntry* aFontEntry, 284 const hb_face_t* aFace) { 285 // OS2 width class to CSS 'stretch' mapping from 286 // https://docs.microsoft.com/en-gb/typography/opentype/spec/os2#uswidthclass 287 const float kOS2WidthToStretch[] = { 288 100, // (invalid, treat as normal) 289 50, // Ultra-condensed 290 62.5, // Extra-condensed 291 75, // Condensed 292 87.5, // Semi-condensed 293 100, // Normal 294 112.5, // Semi-expanded 295 125, // Expanded 296 150, // Extra-expanded 297 200 // Ultra-expanded 298 }; 299 300 // Get the macStyle field from the 'head' table 301 gfxFontUtils::AutoHBBlob headBlob( 302 hb_face_reference_table(aFace, HB_TAG('h', 'e', 'a', 'd'))); 303 unsigned int len; 304 const char* data = hb_blob_get_data(headBlob, &len); 305 uint16_t style = 0; 306 if (len >= sizeof(HeadTable)) { 307 const HeadTable* head = reinterpret_cast<const HeadTable*>(data); 308 style = head->macStyle; 309 } 310 311 // Get the OS/2 table for weight & width fields 312 gfxFontUtils::AutoHBBlob os2blob( 313 hb_face_reference_table(aFace, HB_TAG('O', 'S', '/', '2'))); 314 data = hb_blob_get_data(os2blob, &len); 315 uint16_t os2weight = 400; 316 float stretch = 100.0; 317 if (len >= offsetof(OS2Table, fsType)) { 318 const OS2Table* os2 = reinterpret_cast<const OS2Table*>(data); 319 os2weight = os2->usWeightClass; 320 uint16_t os2width = os2->usWidthClass; 321 if (os2width < std::size(kOS2WidthToStretch)) { 322 stretch = kOS2WidthToStretch[os2width]; 323 } 324 } 325 326 aFontEntry->mStyleRange = SlantStyleRange( 327 (style & 2) ? FontSlantStyle::ITALIC : FontSlantStyle::NORMAL); 328 aFontEntry->mWeightRange = WeightRange(FontWeight::FromInt(int(os2weight))); 329 aFontEntry->mStretchRange = StretchRange(FontStretch::FromFloat(stretch)); 330 331 // For variable fonts, update the style/weight/stretch attributes if the 332 // corresponding variation axes are present. 333 aFontEntry->SetupVariationRanges(); 334 } 335 336 // Used to create the font entry for installed faces on the device, 337 // when iterating over the fonts directories. 338 // We use the hb_face_t to retrieve the details needed for the font entry, 339 // but do *not* save a reference to it, nor create a cairo face. 340 /* static */ 341 FT2FontEntry* FT2FontEntry::CreateFontEntry(const nsACString& aName, 342 const char* aFilename, 343 uint8_t aIndex, 344 const hb_face_t* aFace) { 345 FT2FontEntry* fe = new FT2FontEntry(aName); 346 fe->mFilename = aFilename; 347 fe->mFTFontIndex = aIndex; 348 349 if (aFace) { 350 SetPropertiesFromFace(fe, aFace); 351 } else { 352 // If nullptr is passed for aFace, the caller is intending to override 353 // these attributes anyway. We just set defaults here to be safe. 354 fe->mStyleRange = SlantStyleRange(FontSlantStyle::NORMAL); 355 fe->mWeightRange = WeightRange(FontWeight::NORMAL); 356 fe->mStretchRange = StretchRange(FontStretch::NORMAL); 357 } 358 359 return fe; 360 } 361 362 FT2FontEntry* gfxFT2Font::GetFontEntry() { 363 return static_cast<FT2FontEntry*>(mFontEntry.get()); 364 } 365 366 // Copied/modified from similar code in gfxMacPlatformFontList.mm: 367 // Complex scripts will not render correctly unless Graphite or OT 368 // layout tables are present. 369 // For OpenType, we also check that the GSUB table supports the relevant 370 // script tag, to avoid using things like Arial Unicode MS for Lao (it has 371 // the characters, but lacks OpenType support). 372 373 // TODO: consider whether we should move this to gfxFontEntry and do similar 374 // cmap-masking on all platforms to avoid using fonts that won't shape 375 // properly. 376 377 nsresult FT2FontEntry::ReadCMAP(FontInfoData* aFontInfoData) { 378 if (mCharacterMap || mShmemCharacterMap) { 379 return NS_OK; 380 } 381 382 RefPtr<gfxCharacterMap> charmap = new gfxCharacterMap(256); 383 384 nsresult rv = NS_ERROR_NOT_AVAILABLE; 385 uint32_t uvsOffset = 0; 386 gfxFontUtils::AutoHBBlob cmapBlob(GetFontTable(TTAG_cmap)); 387 if (cmapBlob) { 388 unsigned int length; 389 const char* data = hb_blob_get_data(cmapBlob, &length); 390 rv = gfxFontUtils::ReadCMAP((const uint8_t*)data, length, *charmap, 391 uvsOffset); 392 } 393 mUVSOffset.exchange(uvsOffset); 394 395 if (NS_SUCCEEDED(rv) && !mIsDataUserFont && !HasGraphiteTables()) { 396 // For downloadable fonts, trust the author and don't 397 // try to munge the cmap based on script shaping support. 398 399 // We also assume a Graphite font knows what it's doing, 400 // and provides whatever shaping is needed for the 401 // characters it supports, so only check/clear the 402 // complex-script ranges for non-Graphite fonts 403 404 // for layout support, check for the presence of opentype layout tables 405 bool hasGSUB = HasFontTable(TRUETYPE_TAG('G', 'S', 'U', 'B')); 406 407 for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges; 408 sr->rangeStart; sr++) { 409 // check to see if the cmap includes complex script codepoints 410 if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) { 411 // We check for GSUB here, as GPOS alone would not be ok. 412 if (hasGSUB && SupportsScriptInGSUB(sr->tags, sr->numTags)) { 413 continue; 414 } 415 charmap->ClearRange(sr->rangeStart, sr->rangeEnd); 416 } 417 } 418 419 // Bug 1980258: the Cutive Mono font, widely present on Android, has 420 // spurious blank glyphs for several codepoints. Mask them out, to avoid 421 // the risk of font fallback using it (resulting in blank characters). 422 if (FamilyName().EqualsLiteral("Cutive Mono")) { 423 charmap->ClearRange(0x0080, 0x009f); // C1 controls: not all present, 424 // but let's just mask the block. 425 charmap->clear(0x2074); // superscript four 426 charmap->clear(0xfb00); // ff ligature 427 charmap->ClearRange(0xfb03, 0xfb04); // ffi, ffl 428 } 429 } 430 431 #if defined(MOZ_WIDGET_ANDROID) && !defined(NIGHTLY_BUILD) 432 // Hack for the SamsungDevanagari font, bug 1012365: 433 // pretend the font supports U+0972. 434 if (!charmap->test(0x0972) && charmap->test(0x0905) && 435 charmap->test(0x0945)) { 436 charmap->set(0x0972); 437 } 438 #endif 439 440 bool setCharMap = true; 441 if (NS_SUCCEEDED(rv)) { 442 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList(); 443 fontlist::FontList* sharedFontList = pfl->SharedFontList(); 444 if (!IsUserFont() && mShmemFace) { 445 mShmemFace->SetCharacterMap(sharedFontList, charmap, mShmemFamily); 446 if (TrySetShmemCharacterMap()) { 447 setCharMap = false; 448 } 449 } else { 450 charmap = pfl->FindCharMap(charmap); 451 } 452 mHasCmapTable = true; 453 } else { 454 // if error occurred, initialize to null cmap 455 charmap = new gfxCharacterMap(0); 456 mHasCmapTable = false; 457 } 458 if (setCharMap) { 459 if (mCharacterMap.compareExchange(nullptr, charmap.get())) { 460 // We forget rather than addref because we don't use the charmap below. 461 charmap.forget().leak(); 462 } 463 } 464 465 return rv; 466 } 467 468 hb_face_t* FT2FontEntry::CreateHBFace() const { 469 hb_face_t* result = nullptr; 470 471 if (mFilename[0] == '/') { 472 // An absolute path means a normal file in the filesystem, so we can use 473 // hb_blob_create_from_file to read it. 474 gfxFontUtils::AutoHBBlob fileBlob( 475 hb_blob_create_from_file(mFilename.get())); 476 if (hb_blob_get_length(fileBlob) > 0) { 477 result = hb_face_create(fileBlob, mFTFontIndex); 478 } 479 } else { 480 // A relative path means an omnijar resource, which we may need to 481 // decompress to a temporary buffer. 482 RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE); 483 nsZipItem* item = reader->GetItem(mFilename); 484 MOZ_ASSERT(item, "failed to find zip entry"); 485 if (item) { 486 // TODO(jfkthame): 487 // Check whether the item is compressed; if not, we could just get a 488 // pointer without needing to allocate a buffer and copy the data. 489 // (Currently this configuration isn't used for Gecko on Android.) 490 uint32_t length = item->RealSize(); 491 uint8_t* buffer = static_cast<uint8_t*>(malloc(length)); 492 if (buffer) { 493 nsZipCursor cursor(item, reader, buffer, length); 494 cursor.Copy(&length); 495 MOZ_ASSERT(length == item->RealSize(), "error reading font"); 496 if (length == item->RealSize()) { 497 gfxFontUtils::AutoHBBlob blob( 498 hb_blob_create((const char*)buffer, length, 499 HB_MEMORY_MODE_READONLY, buffer, free)); 500 result = hb_face_create(blob, mFTFontIndex); 501 } 502 } 503 } 504 } 505 506 return result; 507 } 508 509 bool FT2FontEntry::HasFontTable(uint32_t aTableTag) { 510 // If we already have a FreeType face, we can just use that. 511 if (mFTFace) { 512 RefPtr<SharedFTFace> face = GetFTFace(); 513 return gfxFT2FontEntryBase::FaceHasTable(face, aTableTag); 514 } 515 516 { 517 // If we have a cached set of tables, query that. 518 AutoReadLock lock(mLock); 519 if (mAvailableTables.Count() > 0) { 520 return mAvailableTables.Contains(aTableTag); 521 } 522 } 523 524 // If we haven't created a FreeType face already, try to avoid that by 525 // reading the available table tags via harfbuzz and caching in a hashset. 526 AutoWriteLock lock(mLock); 527 if (!mFTFace && !mFilename.IsEmpty()) { 528 hb_face_t* face = CreateHBFace(); 529 if (face) { 530 // Read table tags in batches; 32 should be enough for most fonts in a 531 // single operation. 532 const unsigned TAG_BUF_LENGTH = 32; 533 hb_tag_t tags[TAG_BUF_LENGTH]; 534 unsigned int startOffset = 0; 535 unsigned int totalTables = 0; 536 do { 537 unsigned int count = TAG_BUF_LENGTH; 538 // Updates count to the number of table tags actually retrieved 539 totalTables = hb_face_get_table_tags(face, startOffset, &count, tags); 540 startOffset += count; 541 while (count-- > 0) { 542 mAvailableTables.Insert(tags[count]); 543 } 544 } while (startOffset < totalTables); 545 hb_face_destroy(face); 546 } else { 547 // Failed to create the HarfBuzz face! The font is probably broken. 548 // Put a dummy entry in mAvailableTables so that we don't bother 549 // re-trying here. 550 mAvailableTables.Insert(uint32_t(-1)); 551 } 552 return mAvailableTables.Contains(aTableTag); 553 } 554 555 // Last resort: we'll have to create a (temporary) FreeType face to query 556 // for table presence. 557 RefPtr<SharedFTFace> face = GetFTFace(); 558 return gfxFT2FontEntryBase::FaceHasTable(face, aTableTag); 559 } 560 561 nsresult FT2FontEntry::CopyFontTable(uint32_t aTableTag, 562 nsTArray<uint8_t>& aBuffer) { 563 RefPtr<SharedFTFace> face = GetFTFace(); 564 return gfxFT2FontEntryBase::CopyFaceTable(face, aTableTag, aBuffer); 565 } 566 567 hb_blob_t* FT2FontEntry::GetFontTable(uint32_t aTableTag) { 568 if (FTUserFontData* userFontData = GetUserFontData()) { 569 // If there's a cairo font face, we may be able to return a blob 570 // that just wraps a range of the attached user font data 571 if (userFontData->FontData()) { 572 return gfxFontUtils::GetTableFromFontData(userFontData->FontData(), 573 aTableTag); 574 } 575 } 576 577 // If the FT_Face hasn't been instantiated, try to read table directly 578 // via harfbuzz API to avoid expensive FT_Face creation. 579 if (!mFTFace && !mFilename.IsEmpty()) { 580 hb_face_t* face = CreateHBFace(); 581 if (face) { 582 hb_blob_t* result = hb_face_reference_table(face, aTableTag); 583 hb_face_destroy(face); 584 return result; 585 } 586 } 587 588 // Otherwise, use the default method (which in turn will call our 589 // implementation of CopyFontTable). 590 return gfxFontEntry::GetFontTable(aTableTag); 591 } 592 593 bool FT2FontEntry::HasVariations() { 594 switch (mHasVariations) { 595 case HasVariationsState::No: 596 return false; 597 case HasVariationsState::Yes: 598 return true; 599 case HasVariationsState::Uninitialized: 600 break; 601 } 602 603 SharedFTFace* face = mFTFace; 604 bool hasVariations = 605 face ? face->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS 606 : gfxPlatform::HasVariationFontSupport() && 607 HasFontTable(TRUETYPE_TAG('f', 'v', 'a', 'r')); 608 mHasVariations = 609 hasVariations ? HasVariationsState::Yes : HasVariationsState::No; 610 return hasVariations; 611 } 612 613 void FT2FontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aAxes) { 614 if (!HasVariations()) { 615 return; 616 } 617 if (FT_MM_Var* mmVar = GetMMVar()) { 618 gfxFT2Utils::GetVariationAxes(mmVar, aAxes); 619 } 620 } 621 622 void FT2FontEntry::GetVariationInstances( 623 nsTArray<gfxFontVariationInstance>& aInstances) { 624 if (!HasVariations()) { 625 return; 626 } 627 if (FT_MM_Var* mmVar = GetMMVar()) { 628 gfxFT2Utils::GetVariationInstances(this, mmVar, aInstances); 629 } 630 } 631 632 FT_MM_Var* FT2FontEntry::GetMMVar() { 633 if (mMMVarInitialized) { 634 return mMMVar; 635 } 636 AutoWriteLock lock(mLock); 637 RefPtr<SharedFTFace> face = GetFTFace(true); 638 if (!face) { 639 return nullptr; 640 } 641 if (FT_Err_Ok != FT_Get_MM_Var(face->GetFace(), &mMMVar)) { 642 mMMVar = nullptr; 643 } 644 mMMVarInitialized = true; 645 return mMMVar; 646 } 647 648 void FT2FontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 649 FontListSizes* aSizes) const { 650 gfxFontEntry::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 651 aSizes->mFontListSize += 652 mFilename.SizeOfExcludingThisIfUnshared(aMallocSizeOf); 653 } 654 655 void FT2FontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 656 FontListSizes* aSizes) const { 657 aSizes->mFontListSize += aMallocSizeOf(this); 658 AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 659 } 660 661 /* 662 * FT2FontFamily 663 * A standard gfxFontFamily; just adds a method used to support sending 664 * the font list from chrome to content via IPC. 665 */ 666 667 void FT2FontFamily::AddFacesToFontList(nsTArray<FontListEntry>* aFontList) { 668 AutoReadLock lock(mLock); 669 for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) { 670 const FT2FontEntry* fe = 671 static_cast<const FT2FontEntry*>(mAvailableFonts[i].get()); 672 if (!fe) { 673 continue; 674 } 675 676 aFontList->AppendElement(FontListEntry( 677 Name(), fe->Name(), fe->mFilename, fe->Weight().AsScalar(), 678 fe->Stretch().AsScalar(), fe->SlantStyle().AsScalar(), fe->mFTFontIndex, 679 Visibility())); 680 } 681 } 682 683 /* 684 * Startup cache support for the font list: 685 * We store the list of families and faces, with their style attributes and the 686 * corresponding font files, in the startup cache. 687 * This allows us to recreate the gfxFT2FontList collection of families and 688 * faces without instantiating Freetype faces for each font file (in order to 689 * find their attributes), leading to significantly quicker startup. 690 */ 691 692 #define CACHE_KEY "font.cached-list" 693 694 void gfxFT2FontList::CollectInitData(const FontListEntry& aFLE, 695 const nsCString& aPSName, 696 const nsCString& aFullName, 697 StandardFile aStdFile) { 698 nsAutoCString key(aFLE.familyName()); 699 BuildKeyNameFromFontName(key); 700 mFaceInitData 701 .LookupOrInsertWith( 702 key, 703 [&] { 704 mFamilyInitData.AppendElement(fontlist::Family::InitData{ 705 key, aFLE.familyName(), fontlist::Family::kNoIndex, 706 aFLE.visibility()}); 707 return MakeUnique<nsTArray<fontlist::Face::InitData>>(); 708 }) 709 ->AppendElement(fontlist::Face::InitData{ 710 aFLE.filepath(), aFLE.index(), false, 711 WeightRange::FromScalar(aFLE.weightRange()), 712 StretchRange::FromScalar(aFLE.stretchRange()), 713 SlantStyleRange::FromScalar(aFLE.styleRange())}); 714 nsAutoCString psname(aPSName), fullname(aFullName); 715 if (!psname.IsEmpty()) { 716 ToLowerCase(psname); 717 MaybeAddToLocalNameTable( 718 psname, fontlist::LocalFaceRec::InitData(key, aFLE.filepath())); 719 } 720 if (!fullname.IsEmpty()) { 721 ToLowerCase(fullname); 722 if (fullname != psname) { 723 MaybeAddToLocalNameTable( 724 fullname, fontlist::LocalFaceRec::InitData(key, aFLE.filepath())); 725 } 726 } 727 } 728 729 class FontNameCache { 730 public: 731 // Delimiters used in the cached font-list records we store in startupCache 732 static const char kFileSep = 0x1c; 733 static const char kGroupSep = 0x1d; 734 static const char kRecordSep = 0x1e; 735 static const char kFieldSep = 0x1f; 736 737 // Separator for font property ranges; we only look for this within a 738 // field that holds a serialized FontPropertyValue or Range, so there's no 739 // risk of conflicting with printable characters in font names. 740 // Note that this must be a character that will terminate strtof() parsing 741 // of a number. 742 static const char kRangeSep = ':'; 743 744 // Creates the object but does NOT load the cached data from the startup 745 // cache; call Init() after creation to do that. 746 FontNameCache() : mMap(&mOps, sizeof(FNCMapEntry), 0), mWriteNeeded(false) { 747 // HACK ALERT: it's weird to assign |mOps| after we passed a pointer to 748 // it to |mMap|'s constructor. A more normal approach here would be to 749 // have a static |sOps| member. Unfortunately, this mysteriously but 750 // consistently makes Fennec start-up slower, so we take this 751 // unorthodox approach instead. It's safe because PLDHashTable's 752 // constructor doesn't dereference the pointer; it just makes a copy of 753 // it. 754 mOps = (PLDHashTableOps){StringHash, HashMatchEntry, MoveEntry, 755 PLDHashTable::ClearEntryStub, nullptr}; 756 757 MOZ_ASSERT(XRE_IsParentProcess(), 758 "FontNameCache should only be used in chrome process"); 759 mCache = mozilla::scache::StartupCache::GetSingleton(); 760 } 761 762 ~FontNameCache() { WriteCache(); } 763 764 size_t EntryCount() const { return mMap.EntryCount(); } 765 766 void DropStaleEntries() { 767 for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) { 768 auto entry = static_cast<FNCMapEntry*>(iter.Get()); 769 if (!entry->mFileExists) { 770 iter.Remove(); 771 } 772 } 773 } 774 775 void WriteCache() { 776 if (!mWriteNeeded || !mCache) { 777 return; 778 } 779 780 LOG(("Writing FontNameCache:")); 781 nsAutoCString buf; 782 for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) { 783 auto entry = static_cast<FNCMapEntry*>(iter.Get()); 784 MOZ_ASSERT(entry->mFileExists); 785 buf.Append(entry->mFilename); 786 buf.Append(kGroupSep); 787 buf.Append(entry->mFaces); 788 buf.Append(kGroupSep); 789 buf.AppendInt(entry->mTimestamp); 790 buf.Append(kGroupSep); 791 buf.AppendInt(entry->mFilesize); 792 buf.Append(kFileSep); 793 } 794 795 LOG(("putting FontNameCache to " CACHE_KEY ", length %zu", 796 buf.Length() + 1)); 797 mCache->PutBuffer(CACHE_KEY, UniqueFreePtr<char[]>(ToNewCString(buf)), 798 buf.Length() + 1); 799 mWriteNeeded = false; 800 } 801 802 // This may be called more than once (if we re-load the font list). 803 void Init() { 804 if (!mCache) { 805 return; 806 } 807 808 uint32_t size; 809 const char* cur; 810 if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &cur, &size))) { 811 LOG(("no cache of " CACHE_KEY)); 812 return; 813 } 814 815 LOG(("got: %u bytes from the cache " CACHE_KEY, size)); 816 817 mMap.Clear(); 818 mWriteNeeded = false; 819 820 while (const char* fileEnd = strchr(cur, kFileSep)) { 821 // The cached record for one file is at [cur, fileEnd]. 822 823 // Find end of field that starts at aStart, terminated by kGroupSep or 824 // end of record. 825 auto endOfField = [=](const char* aStart) -> const char* { 826 MOZ_ASSERT(aStart <= fileEnd); 827 const char* end = static_cast<const char*>( 828 memchr(aStart, kGroupSep, fileEnd - aStart)); 829 if (end) { 830 return end; 831 } 832 return fileEnd; 833 }; 834 835 // Advance aStart and aEnd to indicate the range of the next field and 836 // return true, or just return false if already at end of record. 837 auto nextField = [=](const char*& aStart, const char*& aEnd) -> bool { 838 if (aEnd < fileEnd) { 839 aStart = aEnd + 1; 840 aEnd = endOfField(aStart); 841 return true; 842 } 843 return false; 844 }; 845 846 const char* end = endOfField(cur); 847 nsCString filename(cur, end - cur); 848 if (!nextField(cur, end)) { 849 break; 850 } 851 nsCString faceList(cur, end - cur); 852 if (!nextField(cur, end)) { 853 break; 854 } 855 uint32_t timestamp = strtoul(cur, nullptr, 10); 856 if (!nextField(cur, end)) { 857 break; 858 } 859 uint32_t filesize = strtoul(cur, nullptr, 10); 860 861 auto mapEntry = 862 static_cast<FNCMapEntry*>(mMap.Add(filename.get(), fallible)); 863 if (mapEntry) { 864 mapEntry->mFilename.Assign(filename); 865 mapEntry->mTimestamp = timestamp; 866 mapEntry->mFilesize = filesize; 867 mapEntry->mFaces.Assign(faceList); 868 // entries from the startupcache are marked "non-existing" 869 // until we have confirmed that the file still exists 870 mapEntry->mFileExists = false; 871 } 872 873 cur = fileEnd + 1; 874 } 875 } 876 877 void GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList, 878 uint32_t* aTimestamp, uint32_t* aFilesize) { 879 auto entry = static_cast<FNCMapEntry*>(mMap.Search(aFileName.get())); 880 if (entry) { 881 *aTimestamp = entry->mTimestamp; 882 *aFilesize = entry->mFilesize; 883 aFaceList.Assign(entry->mFaces); 884 // this entry does correspond to an existing file 885 // (although it might not be up-to-date, in which case 886 // it will get overwritten via CacheFileInfo) 887 entry->mFileExists = true; 888 } 889 } 890 891 void CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList, 892 uint32_t aTimestamp, uint32_t aFilesize) { 893 auto entry = static_cast<FNCMapEntry*>(mMap.Add(aFileName.get(), fallible)); 894 if (entry) { 895 entry->mFilename.Assign(aFileName); 896 entry->mTimestamp = aTimestamp; 897 entry->mFilesize = aFilesize; 898 entry->mFaces.Assign(aFaceList); 899 entry->mFileExists = true; 900 } 901 mWriteNeeded = true; 902 } 903 904 private: 905 mozilla::scache::StartupCache* mCache; 906 PLDHashTable mMap; 907 bool mWriteNeeded; 908 909 PLDHashTableOps mOps; 910 911 struct FNCMapEntry : public PLDHashEntryHdr { 912 public: 913 nsCString mFilename; 914 uint32_t mTimestamp; 915 uint32_t mFilesize; 916 nsCString mFaces; 917 bool mFileExists; 918 }; 919 920 static PLDHashNumber StringHash(const void* key) { 921 return HashString(reinterpret_cast<const char*>(key)); 922 } 923 924 static bool HashMatchEntry(const PLDHashEntryHdr* aHdr, const void* key) { 925 const FNCMapEntry* entry = static_cast<const FNCMapEntry*>(aHdr); 926 return entry->mFilename.Equals(reinterpret_cast<const char*>(key)); 927 } 928 929 static void MoveEntry(PLDHashTable* table, const PLDHashEntryHdr* aFrom, 930 PLDHashEntryHdr* aTo) { 931 FNCMapEntry* to = static_cast<FNCMapEntry*>(aTo); 932 const FNCMapEntry* from = static_cast<const FNCMapEntry*>(aFrom); 933 to->mFilename.Assign(from->mFilename); 934 to->mTimestamp = from->mTimestamp; 935 to->mFilesize = from->mFilesize; 936 to->mFaces.Assign(from->mFaces); 937 to->mFileExists = from->mFileExists; 938 } 939 }; 940 941 /*************************************************************** 942 * 943 * gfxFT2FontList 944 * 945 */ 946 947 // For Mobile, we use gfxFT2Fonts, and we build the font list by directly 948 // scanning the system's Fonts directory for OpenType and TrueType files. 949 950 #define JAR_LAST_MODIFED_TIME "jar-last-modified-time" 951 952 class WillShutdownObserver : public nsIObserver { 953 public: 954 NS_DECL_ISUPPORTS 955 NS_DECL_NSIOBSERVER 956 957 explicit WillShutdownObserver(gfxFT2FontList* aFontList) 958 : mFontList(aFontList) {} 959 960 void Remove() { 961 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 962 if (obs) { 963 obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID); 964 } 965 mFontList = nullptr; 966 } 967 968 protected: 969 virtual ~WillShutdownObserver() = default; 970 971 gfxFT2FontList* mFontList; 972 }; 973 974 NS_IMPL_ISUPPORTS(WillShutdownObserver, nsIObserver) 975 976 NS_IMETHODIMP 977 WillShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic, 978 const char16_t* aData) { 979 if (!nsCRT::strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) { 980 mFontList->WillShutdown(); 981 } else { 982 MOZ_ASSERT_UNREACHABLE("unexpected notification topic"); 983 } 984 return NS_OK; 985 } 986 987 gfxFT2FontList::gfxFT2FontList() : mJarModifiedTime(0) { 988 CheckFamilyList(kBaseFonts_Android); 989 CheckFamilyList(kBaseFonts_Android5_8); 990 CheckFamilyList(kBaseFonts_Android9_Higher); 991 CheckFamilyList(kBaseFonts_Android9_11); 992 CheckFamilyList(kBaseFonts_Android12_Higher); 993 CheckFamilyList(kLangPack_MFR_Android12_Higher); 994 995 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 996 if (obs) { 997 mObserver = new WillShutdownObserver(this); 998 obs->AddObserver(mObserver, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false); 999 } 1000 } 1001 1002 gfxFT2FontList::~gfxFT2FontList() { 1003 AutoLock lock(mLock); 1004 if (mObserver) { 1005 mObserver->Remove(); 1006 } 1007 } 1008 1009 bool gfxFT2FontList::AppendFacesFromCachedFaceList(CollectFunc aCollectFace, 1010 const nsCString& aFileName, 1011 const nsCString& aFaceList, 1012 StandardFile aStdFile) { 1013 const char* start = aFaceList.get(); 1014 int count = 0; 1015 1016 while (const char* recEnd = strchr(start, FontNameCache::kRecordSep)) { 1017 auto endOfField = [=](const char* aStart) -> const char* { 1018 MOZ_ASSERT(aStart <= recEnd); 1019 const char* end = static_cast<const char*>( 1020 memchr(aStart, FontNameCache::kFieldSep, recEnd - aStart)); 1021 if (end) { 1022 return end; 1023 } 1024 return recEnd; 1025 }; 1026 1027 auto nextField = [=](const char*& aStart, const char*& aEnd) -> bool { 1028 if (aEnd < recEnd) { 1029 aStart = aEnd + 1; 1030 aEnd = endOfField(aStart); 1031 return true; 1032 } 1033 return false; 1034 }; 1035 1036 const char* end = endOfField(start); 1037 nsAutoCString familyName(start, end - start); 1038 nsAutoCString key(familyName); 1039 ToLowerCase(key); 1040 1041 if (!nextField(start, end)) { 1042 break; 1043 } 1044 nsAutoCString faceName(start, end - start); 1045 1046 if (!nextField(start, end)) { 1047 break; 1048 } 1049 uint32_t index = strtoul(start, nullptr, 10); 1050 1051 if (!nextField(start, end)) { 1052 break; 1053 } 1054 1055 auto readIntPair = [&](int32_t& aStart, int32_t& aEnd) { 1056 char* limit = nullptr; 1057 aStart = strtol(start, &limit, 10); 1058 if (*limit == FontNameCache::kRangeSep && limit + 1 < end) { 1059 aEnd = strtof(limit + 1, nullptr); 1060 } 1061 }; 1062 1063 int32_t minStyle, maxStyle; 1064 readIntPair(minStyle, maxStyle); 1065 1066 if (!nextField(start, end)) { 1067 break; 1068 } 1069 1070 int32_t minWeight, maxWeight; 1071 readIntPair(minWeight, maxWeight); 1072 1073 if (!nextField(start, end)) { 1074 break; 1075 } 1076 1077 int32_t minStretch, maxStretch; 1078 readIntPair(minStretch, maxStretch); 1079 1080 if (!nextField(start, end)) { 1081 break; 1082 } 1083 nsAutoCString psname(start, end - start); 1084 1085 if (!nextField(start, end)) { 1086 break; 1087 } 1088 nsAutoCString fullname(start, end - start); 1089 1090 if (!nextField(start, end)) { 1091 break; 1092 } 1093 FontVisibility visibility = FontVisibility(strtoul(start, nullptr, 10)); 1094 1095 FontListEntry fle(familyName, faceName, aFileName, 1096 WeightRange(FontWeight::FromRaw(minWeight), 1097 FontWeight::FromRaw(maxWeight)) 1098 .AsScalar(), 1099 StretchRange(FontStretch::FromRaw(minStretch), 1100 FontStretch::FromRaw(maxStretch)) 1101 .AsScalar(), 1102 SlantStyleRange(FontSlantStyle::FromRaw(minStyle), 1103 FontSlantStyle::FromRaw(maxStyle)) 1104 .AsScalar(), 1105 index, visibility); 1106 1107 aCollectFace(fle, psname, fullname, aStdFile); 1108 count++; 1109 1110 start = recEnd + 1; 1111 } 1112 1113 return count > 0; 1114 } 1115 1116 void FT2FontEntry::AppendToFaceList(nsCString& aFaceList, 1117 const nsACString& aFamilyName, 1118 const nsACString& aPSName, 1119 const nsACString& aFullName, 1120 FontVisibility aVisibility) { 1121 aFaceList.Append(aFamilyName); 1122 aFaceList.Append(FontNameCache::kFieldSep); 1123 aFaceList.Append(Name()); 1124 aFaceList.Append(FontNameCache::kFieldSep); 1125 aFaceList.AppendInt(mFTFontIndex); 1126 aFaceList.Append(FontNameCache::kFieldSep); 1127 aFaceList.AppendInt(SlantStyle().Min().Raw()); 1128 aFaceList.Append(FontNameCache::kRangeSep); 1129 aFaceList.AppendInt(SlantStyle().Max().Raw()); 1130 aFaceList.Append(FontNameCache::kFieldSep); 1131 aFaceList.AppendInt(Weight().Min().Raw()); 1132 aFaceList.Append(FontNameCache::kRangeSep); 1133 aFaceList.AppendInt(Weight().Max().Raw()); 1134 aFaceList.Append(FontNameCache::kFieldSep); 1135 aFaceList.AppendInt(Stretch().Min().Raw()); 1136 aFaceList.Append(FontNameCache::kRangeSep); 1137 aFaceList.AppendInt(Stretch().Max().Raw()); 1138 aFaceList.Append(FontNameCache::kFieldSep); 1139 aFaceList.Append(aPSName); 1140 aFaceList.Append(FontNameCache::kFieldSep); 1141 aFaceList.Append(aFullName); 1142 aFaceList.Append(FontNameCache::kFieldSep); 1143 aFaceList.AppendInt(int(aVisibility)); 1144 aFaceList.Append(FontNameCache::kRecordSep); 1145 } 1146 1147 void FT2FontEntry::CheckForBrokenFont(gfxFontFamily* aFamily) { 1148 // note if the family is in the "bad underline" blocklist 1149 if (aFamily->IsBadUnderlineFamily()) { 1150 mIsBadUnderlineFont = true; 1151 } 1152 nsAutoCString familyKey(aFamily->Name()); 1153 BuildKeyNameFromFontName(familyKey); 1154 CheckForBrokenFont(familyKey); 1155 } 1156 1157 void FT2FontEntry::CheckForBrokenFont(const nsACString& aFamilyKey) { 1158 // bug 721719 - set the IgnoreGSUB flag on entries for Roboto 1159 // because of unwanted on-by-default "ae" ligature. 1160 // (See also AppendFaceFromFontListEntry.) 1161 if (aFamilyKey.EqualsLiteral("roboto")) { 1162 mIgnoreGSUB = true; 1163 return; 1164 } 1165 1166 // bug 706888 - set the IgnoreGSUB flag on the broken version of 1167 // Droid Sans Arabic from certain phones, as identified by the 1168 // font checksum in the 'head' table 1169 if (aFamilyKey.EqualsLiteral("droid sans arabic")) { 1170 RefPtr<SharedFTFace> face = GetFTFace(); 1171 if (face) { 1172 const TT_Header* head = static_cast<const TT_Header*>( 1173 FT_Get_Sfnt_Table(face->GetFace(), ft_sfnt_head)); 1174 if (head && head->CheckSum_Adjust == 0xe445242) { 1175 mIgnoreGSUB = true; 1176 } 1177 } 1178 } 1179 } 1180 1181 void gfxFT2FontList::AppendFacesFromBlob( 1182 const nsCString& aFileName, StandardFile aStdFile, hb_blob_t* aBlob, 1183 FontNameCache* aCache, uint32_t aTimestamp, uint32_t aFilesize) { 1184 nsCString newFaceList; 1185 uint32_t numFaces = 1; 1186 unsigned int length; 1187 const char* data = hb_blob_get_data(aBlob, &length); 1188 // Check for a possible TrueType Collection 1189 if (length >= sizeof(TTCHeader)) { 1190 const TTCHeader* ttc = reinterpret_cast<const TTCHeader*>(data); 1191 if (ttc->ttcTag == TRUETYPE_TAG('t', 't', 'c', 'f')) { 1192 numFaces = ttc->numFonts; 1193 } 1194 } 1195 for (unsigned int index = 0; index < numFaces; index++) { 1196 hb_face_t* face = hb_face_create(aBlob, index); 1197 if (face != hb_face_get_empty()) { 1198 AddFaceToList(aFileName, index, aStdFile, face, newFaceList); 1199 } 1200 hb_face_destroy(face); 1201 } 1202 if (aCache && !newFaceList.IsEmpty()) { 1203 aCache->CacheFileInfo(aFileName, newFaceList, aTimestamp, aFilesize); 1204 } 1205 } 1206 1207 void gfxFT2FontList::AppendFacesFromFontFile(const nsCString& aFileName, 1208 FontNameCache* aCache, 1209 StandardFile aStdFile) { 1210 nsCString cachedFaceList; 1211 uint32_t filesize = 0, timestamp = 0; 1212 if (aCache) { 1213 aCache->GetInfoForFile(aFileName, cachedFaceList, ×tamp, &filesize); 1214 } 1215 1216 struct stat s; 1217 int statRetval = stat(aFileName.get(), &s); 1218 if (!cachedFaceList.IsEmpty() && 0 == statRetval && 1219 uint32_t(s.st_mtime) == timestamp && s.st_size == filesize) { 1220 CollectFunc unshared = 1221 [](const FontListEntry& aFLE, const nsCString& aPSName, 1222 const nsCString& aFullName, StandardFile aStdFile) 1223 MOZ_REQUIRES(PlatformFontList()->mLock) { 1224 auto* pfl = PlatformFontList(); 1225 pfl->mLock.AssertCurrentThreadIn(); 1226 pfl->AppendFaceFromFontListEntry(aFLE, aStdFile); 1227 }; 1228 CollectFunc shared = [](const FontListEntry& aFLE, const nsCString& aPSName, 1229 const nsCString& aFullName, StandardFile aStdFile) 1230 MOZ_REQUIRES(PlatformFontList()->mLock) { 1231 PlatformFontList()->CollectInitData( 1232 aFLE, aPSName, aFullName, aStdFile); 1233 }; 1234 if (AppendFacesFromCachedFaceList(SharedFontList() ? shared : unshared, 1235 aFileName, cachedFaceList, aStdFile)) { 1236 LOG(("using cached font info for %s", aFileName.get())); 1237 return; 1238 } 1239 } 1240 1241 gfxFontUtils::AutoHBBlob fileBlob(hb_blob_create_from_file(aFileName.get())); 1242 if (hb_blob_get_length(fileBlob) > 0) { 1243 LOG(("reading font info via harfbuzz for %s", aFileName.get())); 1244 AppendFacesFromBlob(aFileName, aStdFile, fileBlob, 1245 0 == statRetval ? aCache : nullptr, s.st_mtime, 1246 s.st_size); 1247 } 1248 } 1249 1250 void gfxFT2FontList::FindFontsInOmnijar(FontNameCache* aCache) { 1251 bool jarChanged = false; 1252 1253 mozilla::scache::StartupCache* cache = 1254 mozilla::scache::StartupCache::GetSingleton(); 1255 const char* cachedModifiedTimeBuf; 1256 uint32_t longSize; 1257 if (cache && 1258 NS_SUCCEEDED(cache->GetBuffer(JAR_LAST_MODIFED_TIME, 1259 &cachedModifiedTimeBuf, &longSize)) && 1260 longSize == sizeof(int64_t)) { 1261 nsCOMPtr<nsIFile> jarFile = Omnijar::GetPath(Omnijar::Type::GRE); 1262 jarFile->GetLastModifiedTime(&mJarModifiedTime); 1263 if (mJarModifiedTime > LittleEndian::readInt64(cachedModifiedTimeBuf)) { 1264 jarChanged = true; 1265 } 1266 } 1267 1268 static const char* sJarSearchPaths[] = { 1269 "res/fonts/*.ttf$", 1270 }; 1271 RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE); 1272 for (unsigned i = 0; i < std::size(sJarSearchPaths); i++) { 1273 nsZipFind* find; 1274 if (NS_SUCCEEDED(reader->FindInit(sJarSearchPaths[i], &find))) { 1275 const char* path; 1276 uint16_t len; 1277 while (NS_SUCCEEDED(find->FindNext(&path, &len))) { 1278 nsCString entryName(path, len); 1279 AppendFacesFromOmnijarEntry(reader, entryName, aCache, jarChanged); 1280 } 1281 delete find; 1282 } 1283 } 1284 } 1285 1286 using Device = nsIGfxInfo::FontVisibilityDeviceDetermination; 1287 FontVisibility gfxFT2FontList::GetVisibilityForFamily( 1288 const nsACString& aName) const { 1289 static Device fontVisibilityDevice = Device::Unassigned; 1290 if (fontVisibilityDevice == Device::Unassigned) { 1291 nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service(); 1292 NS_ENSURE_SUCCESS( 1293 gfxInfo->GetFontVisibilityDetermination(&fontVisibilityDevice), 1294 FontVisibility::Unknown); 1295 } 1296 1297 if (fontVisibilityDevice == Device::Android_Unknown_Release_Version || 1298 fontVisibilityDevice == Device::Android_Unknown_Peloton || 1299 fontVisibilityDevice == Device::Android_Unknown_vbox || 1300 fontVisibilityDevice == Device::Android_Unknown_mitv || 1301 fontVisibilityDevice == Device::Android_Chromebook || 1302 fontVisibilityDevice == Device::Android_Amazon) { 1303 return FontVisibility::Unknown; 1304 } 1305 1306 // Sanity Check 1307 if (fontVisibilityDevice != Device::Android_sub_9 && 1308 fontVisibilityDevice != Device::Android_9_11 && 1309 fontVisibilityDevice != Device::Android_12_plus) { 1310 return FontVisibility::Unknown; 1311 } 1312 1313 if (FamilyInList(aName, kBaseFonts_Android)) { 1314 return FontVisibility::Base; 1315 } 1316 1317 if (fontVisibilityDevice == Device::Android_sub_9) { 1318 if (FamilyInList(aName, kBaseFonts_Android5_8)) { 1319 return FontVisibility::Base; 1320 } 1321 } else { 1322 if (FamilyInList(aName, kBaseFonts_Android9_Higher)) { 1323 return FontVisibility::Base; 1324 } 1325 1326 if (fontVisibilityDevice == Device::Android_9_11) { 1327 if (FamilyInList(aName, kBaseFonts_Android9_11)) { 1328 return FontVisibility::Base; 1329 } 1330 } else { 1331 if (FamilyInList(aName, kBaseFonts_Android12_Higher)) { 1332 return FontVisibility::Base; 1333 } 1334 1335 if (FamilyInList(aName, kLangPack_MFR_Android12_Higher)) { 1336 return FontVisibility::LangPack; 1337 } 1338 } 1339 } 1340 1341 return FontVisibility::User; 1342 } 1343 1344 nsTArray<std::pair<const char**, uint32_t>> 1345 gfxFT2FontList::GetFilteredPlatformFontLists() { 1346 static Device fontVisibilityDevice = Device::Unassigned; 1347 if (fontVisibilityDevice == Device::Unassigned) { 1348 nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service(); 1349 (void)gfxInfo->GetFontVisibilityDetermination(&fontVisibilityDevice); 1350 } 1351 1352 nsTArray<std::pair<const char**, uint32_t>> fontLists; 1353 1354 if (fontVisibilityDevice == Device::Android_Unknown_Release_Version || 1355 fontVisibilityDevice == Device::Android_Unknown_Peloton || 1356 fontVisibilityDevice == Device::Android_Unknown_vbox || 1357 fontVisibilityDevice == Device::Android_Unknown_mitv || 1358 fontVisibilityDevice == Device::Android_Chromebook || 1359 fontVisibilityDevice == Device::Android_Amazon) { 1360 return fontLists; 1361 } 1362 1363 // Sanity Check 1364 if (fontVisibilityDevice != Device::Android_sub_9 && 1365 fontVisibilityDevice != Device::Android_9_11 && 1366 fontVisibilityDevice != Device::Android_12_plus) { 1367 return fontLists; 1368 } 1369 1370 fontLists.AppendElement( 1371 std::make_pair(kBaseFonts_Android, std::size(kBaseFonts_Android))); 1372 1373 if (fontVisibilityDevice == Device::Android_sub_9) { 1374 fontLists.AppendElement(std::make_pair(kBaseFonts_Android5_8, 1375 std::size(kBaseFonts_Android5_8))); 1376 } else { 1377 fontLists.AppendElement(std::make_pair( 1378 kBaseFonts_Android9_Higher, std::size(kBaseFonts_Android9_Higher))); 1379 1380 if (fontVisibilityDevice == Device::Android_9_11) { 1381 fontLists.AppendElement(std::make_pair( 1382 kBaseFonts_Android9_11, std::size(kBaseFonts_Android9_11))); 1383 } else { 1384 fontLists.AppendElement(std::make_pair( 1385 kBaseFonts_Android12_Higher, std::size(kBaseFonts_Android12_Higher))); 1386 fontLists.AppendElement( 1387 std::make_pair(kLangPack_MFR_Android12_Higher, 1388 std::size(kLangPack_MFR_Android12_Higher))); 1389 } 1390 } 1391 1392 return fontLists; 1393 } 1394 1395 static void GetName(hb_face_t* aFace, hb_ot_name_id_t aNameID, 1396 nsACString& aName) { 1397 unsigned int n = 0; 1398 n = hb_ot_name_get_utf8(aFace, aNameID, HB_LANGUAGE_INVALID, &n, nullptr); 1399 if (n) { 1400 aName.SetLength(n++); // increment n to account for NUL terminator 1401 n = hb_ot_name_get_utf8(aFace, aNameID, HB_LANGUAGE_INVALID, &n, 1402 aName.BeginWriting()); 1403 } 1404 } 1405 1406 // Given the harfbuzz face corresponding to an entryName and face index, 1407 // add the face to the available font list and to the faceList string 1408 void gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex, 1409 StandardFile aStdFile, hb_face_t* aFace, 1410 nsCString& aFaceList) { 1411 nsAutoCString familyName; 1412 bool preferTypographicNames = true; 1413 GetName(aFace, HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY, familyName); 1414 if (familyName.IsEmpty()) { 1415 preferTypographicNames = false; 1416 GetName(aFace, HB_OT_NAME_ID_FONT_FAMILY, familyName); 1417 } 1418 if (familyName.IsEmpty()) { 1419 return; 1420 } 1421 1422 nsAutoCString fullname; 1423 GetName(aFace, HB_OT_NAME_ID_FULL_NAME, fullname); 1424 if (fullname.IsEmpty()) { 1425 // Construct fullname from family + style 1426 fullname = familyName; 1427 nsAutoCString styleName; 1428 if (preferTypographicNames) { 1429 GetName(aFace, HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY, styleName); 1430 } 1431 if (styleName.IsEmpty()) { 1432 GetName(aFace, HB_OT_NAME_ID_FONT_SUBFAMILY, styleName); 1433 } 1434 if (!styleName.IsEmpty() && !styleName.EqualsLiteral("Regular")) { 1435 fullname.Append(' '); 1436 fullname.Append(styleName); 1437 } 1438 } 1439 1440 // Build the font entry name and create an FT2FontEntry, 1441 // but do -not- keep a reference to the FT_Face. 1442 // (When using the shared font list, this entry will not be retained, 1443 // it is used only to call AppendToFaceList.) 1444 RefPtr<FT2FontEntry> fe = 1445 FT2FontEntry::CreateFontEntry(fullname, aEntryName.get(), aIndex, aFace); 1446 1447 if (fe) { 1448 fe->mStandardFace = (aStdFile == kStandard); 1449 nsAutoCString familyKey(familyName); 1450 BuildKeyNameFromFontName(familyKey); 1451 1452 FontVisibility visibility = GetVisibilityForFamily(familyName); 1453 1454 nsAutoCString psname; 1455 GetName(aFace, HB_OT_NAME_ID_POSTSCRIPT_NAME, psname); 1456 1457 if (SharedFontList()) { 1458 FontListEntry fle(familyName, fe->Name(), fe->mFilename, 1459 fe->Weight().AsScalar(), fe->Stretch().AsScalar(), 1460 fe->SlantStyle().AsScalar(), fe->mFTFontIndex, 1461 visibility); 1462 CollectInitData(fle, psname, fullname, aStdFile); 1463 } else { 1464 RefPtr<gfxFontFamily> family = 1465 mFontFamilies.LookupOrInsertWith(familyKey, [&] { 1466 auto family = MakeRefPtr<FT2FontFamily>(familyName, visibility); 1467 if (mSkipSpaceLookupCheckFamilies.Contains(familyKey)) { 1468 family->SetSkipSpaceFeatureCheck(true); 1469 } 1470 if (mBadUnderlineFamilyNames.ContainsSorted(familyKey)) { 1471 family->SetBadUnderlineFamily(); 1472 } 1473 return family; 1474 }); 1475 family->AddFontEntry(fe); 1476 fe->CheckForBrokenFont(family); 1477 } 1478 1479 fe->AppendToFaceList(aFaceList, familyName, psname, fullname, visibility); 1480 if (LOG_ENABLED()) { 1481 nsAutoCString weightString; 1482 fe->Weight().ToString(weightString); 1483 nsAutoCString stretchString; 1484 fe->Stretch().ToString(stretchString); 1485 LOG( 1486 ("(fontinit) added (%s) to family (%s)" 1487 " with style: %s weight: %s stretch: %s", 1488 fe->Name().get(), familyName.get(), 1489 fe->IsItalic() ? "italic" : "normal", weightString.get(), 1490 stretchString.get())); 1491 } 1492 } 1493 } 1494 1495 void gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive, 1496 const nsCString& aEntryName, 1497 FontNameCache* aCache, 1498 bool aJarChanged) { 1499 nsCString faceList; 1500 if (aCache && !aJarChanged) { 1501 uint32_t filesize, timestamp; 1502 aCache->GetInfoForFile(aEntryName, faceList, ×tamp, &filesize); 1503 if (faceList.Length() > 0) { 1504 CollectFunc unshared = 1505 [](const FontListEntry& aFLE, const nsCString& aPSName, 1506 const nsCString& aFullName, StandardFile aStdFile) { 1507 auto* pfl = PlatformFontList(); 1508 pfl->mLock.AssertCurrentThreadIn(); 1509 pfl->AppendFaceFromFontListEntry(aFLE, aStdFile); 1510 }; 1511 CollectFunc shared = [](const FontListEntry& aFLE, 1512 const nsCString& aPSName, 1513 const nsCString& aFullName, StandardFile aStdFile) 1514 MOZ_REQUIRES(PlatformFontList()->mLock) { 1515 PlatformFontList()->CollectInitData( 1516 aFLE, aPSName, aFullName, aStdFile); 1517 }; 1518 if (AppendFacesFromCachedFaceList(SharedFontList() ? shared : unshared, 1519 aEntryName, faceList, kStandard)) { 1520 return; 1521 } 1522 } 1523 } 1524 1525 nsZipItem* item = aArchive->GetItem(aEntryName); 1526 NS_ASSERTION(item, "failed to find zip entry"); 1527 1528 uint32_t bufSize = item->RealSize(); 1529 1530 // We use fallible allocation here; if there's not enough RAM, we'll simply 1531 // ignore the bundled fonts and fall back to the device's installed fonts. 1532 char* buffer = static_cast<char*>(malloc(bufSize)); 1533 if (!buffer) { 1534 return; 1535 } 1536 1537 nsZipCursor cursor(item, aArchive, (uint8_t*)buffer, bufSize); 1538 uint8_t* data = cursor.Copy(&bufSize); 1539 MOZ_ASSERT(data && bufSize == item->RealSize(), "error reading bundled font"); 1540 if (!data) { 1541 return; 1542 } 1543 1544 gfxFontUtils::AutoHBBlob blob( 1545 hb_blob_create(buffer, bufSize, HB_MEMORY_MODE_READONLY, buffer, free)); 1546 AppendFacesFromBlob(aEntryName, kStandard, blob, aCache, 0, bufSize); 1547 } 1548 1549 // Called on each family after all fonts are added to the list; 1550 // if aSortFaces is true this will sort faces to give priority to "standard" 1551 // font files. 1552 void FT2FontFamily::FinalizeMemberList(bool aSortFaces) { 1553 AutoWriteLock lock(mLock); 1554 1555 SetHasStyles(true); 1556 1557 if (aSortFaces) { 1558 SortAvailableFonts(); 1559 } 1560 CheckForSimpleFamily(); 1561 } 1562 1563 void gfxFT2FontList::FindFonts() { 1564 MOZ_ASSERT(XRE_IsParentProcess()); 1565 1566 // Chrome process: get the cached list (if any) 1567 if (!mFontNameCache) { 1568 mFontNameCache = MakeUnique<FontNameCache>(); 1569 } 1570 mFontNameCache->Init(); 1571 1572 #if defined(MOZ_WIDGET_ANDROID) 1573 // Android API 29+ provides system font and font matcher API for native code. 1574 1575 nsAutoCString androidFontsRoot = [&] { 1576 // ANDROID_ROOT is the root of the android system, typically /system; 1577 // font files are in /$ANDROID_ROOT/fonts/ 1578 nsAutoCString root; 1579 char* androidRoot = PR_GetEnv("ANDROID_ROOT"); 1580 if (androidRoot) { 1581 root = androidRoot; 1582 } else { 1583 root = "/system"_ns; 1584 } 1585 root.AppendLiteral("/fonts"); 1586 return root; 1587 }(); 1588 1589 bool useSystemFontAPI = !gfxAndroidPlatform::IsFontAPIDisabled(); 1590 1591 if (useSystemFontAPI) { 1592 bool noFontByFontAPI = true; 1593 if (__builtin_available(android 29, *)) { 1594 gfxAndroidPlatform::WaitForInitializeFontAPI(); 1595 AndroidSystemFontIterator iter; 1596 while (Maybe<AndroidFont> androidFont = iter.Next()) { 1597 const char* _Nonnull fontPath = androidFont->GetFontFilePath(); 1598 noFontByFontAPI = false; 1599 AppendFacesFromFontFile(nsDependentCString(fontPath), 1600 mFontNameCache.get(), kStandard); 1601 } 1602 } 1603 1604 if (noFontByFontAPI) { 1605 // Font API doesn't seem to work. Use legacy way. 1606 useSystemFontAPI = false; 1607 } 1608 1609 if (!StaticPrefs::gfx_font_rendering_colr_v1_enabled()) { 1610 // We turn off COLRv1 fonts support. Newer android versions have 1611 // COLRv1 emoji font, and a legacy and hidden CBDT font we understand, 1612 // so try to find NotoColorEmojiLegacy.ttf explicitly for now. 1613 nsAutoCString legacyEmojiFont(androidFontsRoot); 1614 legacyEmojiFont.Append("/NotoColorEmojiLegacy.ttf"); 1615 AppendFacesFromFontFile(legacyEmojiFont, mFontNameCache.get(), kStandard); 1616 } 1617 } 1618 1619 if (!useSystemFontAPI) 1620 #endif 1621 { 1622 FindFontsInDir(androidFontsRoot, mFontNameCache.get()); 1623 } 1624 1625 // Look for fonts stored in omnijar, unless we're on a low-memory 1626 // device where we don't want to spend the RAM to decompress them. 1627 // (Prefs may disable this, or force-enable it even with low memory.) 1628 if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() > 0 || 1629 (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() < 0 && 1630 !nsMemory::IsLowMemoryPlatform())) { 1631 auto timer = glean::fontlist::bundledfonts_activate.Measure(); 1632 FindFontsInOmnijar(mFontNameCache.get()); 1633 } 1634 1635 // Look for downloaded fonts in a profile-agnostic "fonts" directory. 1636 nsCOMPtr<nsIProperties> dirSvc = 1637 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); 1638 if (dirSvc) { 1639 nsCOMPtr<nsIFile> appDir; 1640 nsresult rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile), 1641 getter_AddRefs(appDir)); 1642 if (NS_SUCCEEDED(rv)) { 1643 appDir->AppendNative("fonts"_ns); 1644 nsCString localPath; 1645 if (NS_SUCCEEDED(appDir->GetNativePath(localPath))) { 1646 FindFontsInDir(localPath, mFontNameCache.get()); 1647 } 1648 } 1649 } 1650 1651 // look for locally-added fonts in a "fonts" subdir of the profile 1652 nsCOMPtr<nsIFile> localDir; 1653 nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, 1654 getter_AddRefs(localDir)); 1655 if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(localDir->Append(u"fonts"_ns))) { 1656 nsCString localPath; 1657 rv = localDir->GetNativePath(localPath); 1658 if (NS_SUCCEEDED(rv)) { 1659 FindFontsInDir(localPath, mFontNameCache.get()); 1660 } 1661 } 1662 1663 mFontNameCache->DropStaleEntries(); 1664 if (!mFontNameCache->EntryCount()) { 1665 // if we can't find any usable fonts, we are doomed! 1666 MOZ_CRASH("No font files found"); 1667 } 1668 1669 // Write out FontCache data if needed 1670 WriteCache(); 1671 } 1672 1673 void gfxFT2FontList::WriteCache() { 1674 if (mFontNameCache) { 1675 mFontNameCache->WriteCache(); 1676 } 1677 mozilla::scache::StartupCache* cache = 1678 mozilla::scache::StartupCache::GetSingleton(); 1679 if (cache && mJarModifiedTime > 0) { 1680 const size_t bufSize = sizeof(mJarModifiedTime); 1681 auto buf = UniqueFreePtr<char[]>( 1682 reinterpret_cast<char*>(malloc(sizeof(char) * bufSize))); 1683 LittleEndian::writeInt64(buf.get(), mJarModifiedTime); 1684 1685 LOG(("WriteCache: putting Jar, length %zu", bufSize)); 1686 cache->PutBuffer(JAR_LAST_MODIFED_TIME, std::move(buf), bufSize); 1687 } 1688 LOG(("Done with writecache")); 1689 } 1690 1691 void gfxFT2FontList::FindFontsInDir(const nsCString& aDir, 1692 FontNameCache* aFNC) { 1693 static const char* sStandardFonts[] = {"DroidSans.ttf", 1694 "DroidSans-Bold.ttf", 1695 "DroidSerif-Regular.ttf", 1696 "DroidSerif-Bold.ttf", 1697 "DroidSerif-Italic.ttf", 1698 "DroidSerif-BoldItalic.ttf", 1699 "DroidSansMono.ttf", 1700 "DroidSansArabic.ttf", 1701 "DroidSansHebrew.ttf", 1702 "DroidSansThai.ttf", 1703 "MTLmr3m.ttf", 1704 "MTLc3m.ttf", 1705 "NanumGothic.ttf", 1706 "NotoColorEmoji.ttf", 1707 "NotoColorEmojiFlags.ttf", 1708 "NotoColorEmojiLegacy.ttf", 1709 "DroidSansJapanese.ttf", 1710 "DroidSansFallback.ttf"}; 1711 1712 DIR* d = opendir(aDir.get()); 1713 if (!d) { 1714 return; 1715 } 1716 1717 struct dirent* ent = nullptr; 1718 while ((ent = readdir(d)) != nullptr) { 1719 const char* ext = strrchr(ent->d_name, '.'); 1720 if (!ext) { 1721 continue; 1722 } 1723 if (strcasecmp(ext, ".ttf") == 0 || strcasecmp(ext, ".otf") == 0 || 1724 strcasecmp(ext, ".woff") == 0 || strcasecmp(ext, ".ttc") == 0) { 1725 bool isStdFont = false; 1726 for (unsigned int i = 0; i < std::size(sStandardFonts) && !isStdFont; 1727 i++) { 1728 isStdFont = strcmp(sStandardFonts[i], ent->d_name) == 0; 1729 } 1730 1731 nsAutoCString s(aDir); 1732 s.Append('/'); 1733 s.Append(ent->d_name); 1734 1735 // Add the face(s) from this file to our font list; 1736 // note that if we have cached info for this file in fnc, 1737 // and the file is unchanged, we won't actually need to read it. 1738 // If the file is new/changed, this will update the FontNameCache. 1739 AppendFacesFromFontFile(s, aFNC, isStdFont ? kStandard : kUnknown); 1740 } 1741 } 1742 1743 closedir(d); 1744 } 1745 1746 void gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE, 1747 StandardFile aStdFile) { 1748 FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE); 1749 if (fe) { 1750 nsAutoCString key(aFLE.familyName()); 1751 BuildKeyNameFromFontName(key); 1752 fe->mStandardFace = (aStdFile == kStandard); 1753 RefPtr<gfxFontFamily> family = mFontFamilies.LookupOrInsertWith(key, [&] { 1754 auto family = 1755 MakeRefPtr<FT2FontFamily>(aFLE.familyName(), aFLE.visibility()); 1756 if (mSkipSpaceLookupCheckFamilies.Contains(key)) { 1757 family->SetSkipSpaceFeatureCheck(true); 1758 } 1759 if (mBadUnderlineFamilyNames.ContainsSorted(key)) { 1760 family->SetBadUnderlineFamily(); 1761 } 1762 return family; 1763 }); 1764 family->AddFontEntry(fe); 1765 1766 fe->CheckForBrokenFont(family); 1767 } 1768 } 1769 1770 void gfxFT2FontList::ReadSystemFontList(dom::SystemFontList* aList) { 1771 AutoLock lock(mLock); 1772 for (const auto& entry : mFontFamilies) { 1773 auto family = static_cast<FT2FontFamily*>(entry.GetData().get()); 1774 family->AddFacesToFontList(&aList->entries()); 1775 } 1776 } 1777 1778 static void LoadSkipSpaceLookupCheck( 1779 nsTHashSet<nsCString>& aSkipSpaceLookupCheck) { 1780 AutoTArray<nsCString, 5> skiplist; 1781 gfxFontUtils::GetPrefsFontList( 1782 "font.whitelist.skip_default_features_space_check", skiplist); 1783 uint32_t numFonts = skiplist.Length(); 1784 for (uint32_t i = 0; i < numFonts; i++) { 1785 ToLowerCase(skiplist[i]); 1786 aSkipSpaceLookupCheck.Insert(skiplist[i]); 1787 } 1788 } 1789 1790 nsresult gfxFT2FontList::InitFontListForPlatform() { 1791 LoadSkipSpaceLookupCheck(mSkipSpaceLookupCheckFamilies); 1792 1793 if (XRE_IsParentProcess()) { 1794 // This will populate/update mFontNameCache and store it in the 1795 // startupCache for future startups. 1796 FindFonts(); 1797 1798 // Finalize the families by sorting faces into standard order 1799 // and marking "simple" families. 1800 for (const auto& entry : mFontFamilies) { 1801 auto* family = static_cast<FT2FontFamily*>(entry.GetData().get()); 1802 family->FinalizeMemberList(/* aSortFaces */ true); 1803 } 1804 1805 return NS_OK; 1806 } 1807 1808 // Content process: use font list passed from the chrome process via 1809 // the GetXPCOMProcessAttributes message. 1810 auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList(); 1811 for (FontListEntry& fle : fontList.entries()) { 1812 // We don't need to identify "standard" font files here, 1813 // as the faces are already sorted. 1814 AppendFaceFromFontListEntry(fle, kUnknown); 1815 } 1816 1817 // We don't need to sort faces (because they were already sorted by the 1818 // chrome process, so we just maintain the existing order) 1819 for (const auto& entry : mFontFamilies) { 1820 auto* family = static_cast<FT2FontFamily*>(entry.GetData().get()); 1821 family->FinalizeMemberList(/* aSortFaces */ false); 1822 } 1823 1824 LOG(("got font list from chrome process: %" PRIdPTR " faces in %" PRIu32 1825 " families", 1826 fontList.entries().Length(), mFontFamilies.Count())); 1827 fontList.entries().Clear(); 1828 1829 return NS_OK; 1830 } 1831 1832 void gfxFT2FontList::InitSharedFontListForPlatform() { 1833 if (!XRE_IsParentProcess()) { 1834 // Content processes will access the shared-memory data created by the 1835 // parent, so don't need to scan for available fonts themselves. 1836 return; 1837 } 1838 1839 // This will populate mFontNameCache with entries for all the available font 1840 // files, and record them in mFamilies (unshared list) or mFamilyInitData and 1841 // mFaceInitData (shared font list). 1842 FindFonts(); 1843 1844 mozilla::fontlist::FontList* list = SharedFontList(); 1845 list->SetFamilyNames(mFamilyInitData); 1846 1847 auto families = list->Families(); 1848 for (uint32_t i = 0; i < mFamilyInitData.Length(); i++) { 1849 auto faceList = mFaceInitData.Get(mFamilyInitData[i].mKey); 1850 MOZ_ASSERT(faceList); 1851 families[i].AddFaces(list, *faceList); 1852 } 1853 1854 mFamilyInitData.Clear(); 1855 mFaceInitData.Clear(); 1856 } 1857 1858 gfxFontEntry* gfxFT2FontList::CreateFontEntry(fontlist::Face* aFace, 1859 const fontlist::Family* aFamily) { 1860 fontlist::FontList* list = SharedFontList(); 1861 nsAutoCString desc(aFace->mDescriptor.AsString(list)); 1862 FT2FontEntry* fe = 1863 FT2FontEntry::CreateFontEntry(desc, desc.get(), aFace->mIndex, nullptr); 1864 fe->InitializeFrom(aFace, aFamily); 1865 fe->CheckForBrokenFont(aFamily->Key().AsString(list)); 1866 return fe; 1867 } 1868 1869 // called for each family name, based on the assumption that the 1870 // first part of the full name is the family name 1871 1872 gfxFontEntry* gfxFT2FontList::LookupLocalFont( 1873 FontVisibilityProvider* aFontVisibilityProvider, 1874 const nsACString& aFontName, WeightRange aWeightForEntry, 1875 StretchRange aStretchForEntry, SlantStyleRange aStyleForEntry) { 1876 AutoLock lock(mLock); 1877 1878 if (SharedFontList()) { 1879 return LookupInSharedFaceNameList(aFontVisibilityProvider, aFontName, 1880 aWeightForEntry, aStretchForEntry, 1881 aStyleForEntry); 1882 } 1883 1884 // walk over list of names 1885 FT2FontEntry* fontEntry = nullptr; 1886 FontVisibility level = aFontVisibilityProvider 1887 ? aFontVisibilityProvider->GetFontVisibility() 1888 : FontVisibility::User; 1889 1890 for (const RefPtr<gfxFontFamily>& fontFamily : mFontFamilies.Values()) { 1891 if (!IsVisibleToCSS(*fontFamily, level)) { 1892 continue; 1893 } 1894 1895 // Check family name, based on the assumption that the 1896 // first part of the full name is the family name 1897 1898 // does the family name match up to the length of the family name? 1899 const nsCString& family = fontFamily->Name(); 1900 1901 const nsAutoCString fullNameFamily( 1902 Substring(aFontName, 0, family.Length())); 1903 1904 // if so, iterate over faces in this family to see if there is a match 1905 if (family.Equals(fullNameFamily, nsCaseInsensitiveCStringComparator)) { 1906 gfxFontEntry* fe = 1907 fontFamily->FindFont(aFontName, nsCaseInsensitiveCStringComparator); 1908 if (fe) { 1909 fontEntry = static_cast<FT2FontEntry*>(fe); 1910 goto searchDone; 1911 } 1912 } 1913 } 1914 1915 searchDone: 1916 if (!fontEntry) { 1917 return nullptr; 1918 } 1919 1920 // Clone the font entry so that we can then set its style descriptors 1921 // from the userfont entry rather than the actual font. 1922 1923 // Ensure existence of mFTFace in the original entry 1924 RefPtr<SharedFTFace> face = fontEntry->GetFTFace(true); 1925 if (!face) { 1926 return nullptr; 1927 } 1928 1929 FT2FontEntry* fe = FT2FontEntry::CreateFontEntry( 1930 fontEntry->Name(), fontEntry->mFilename.get(), fontEntry->mFTFontIndex, 1931 nullptr); 1932 if (fe) { 1933 fe->mStyleRange = aStyleForEntry; 1934 fe->mWeightRange = aWeightForEntry; 1935 fe->mStretchRange = aStretchForEntry; 1936 fe->mIsLocalUserFont = true; 1937 } 1938 1939 return fe; 1940 } 1941 1942 FontFamily gfxFT2FontList::GetDefaultFontForPlatform( 1943 FontVisibilityProvider* aFontVisibilityProvider, const gfxFontStyle* aStyle, 1944 nsAtom* aLanguage) { 1945 FontFamily ff; 1946 #if defined(MOZ_WIDGET_ANDROID) 1947 ff = FindFamily(aFontVisibilityProvider, "Roboto"_ns); 1948 if (ff.IsNull()) { 1949 ff = FindFamily(aFontVisibilityProvider, "Droid Sans"_ns); 1950 } 1951 #endif 1952 /* TODO: what about Qt or other platforms that may use this? */ 1953 return ff; 1954 } 1955 1956 gfxFontEntry* gfxFT2FontList::MakePlatformFont(const nsACString& aFontName, 1957 WeightRange aWeightForEntry, 1958 StretchRange aStretchForEntry, 1959 SlantStyleRange aStyleForEntry, 1960 const uint8_t* aFontData, 1961 uint32_t aLength) { 1962 // The FT2 font needs the font data to persist, so we do NOT free it here 1963 // but instead pass ownership to the font entry. 1964 // Deallocation will happen later, when the font face is destroyed. 1965 return FT2FontEntry::CreateFontEntry(aFontName, aWeightForEntry, 1966 aStretchForEntry, aStyleForEntry, 1967 aFontData, aLength); 1968 } 1969 1970 gfxFontFamily* gfxFT2FontList::CreateFontFamily( 1971 const nsACString& aName, FontVisibility aVisibility) const { 1972 return new FT2FontFamily(aName, aVisibility); 1973 } 1974 1975 void gfxFT2FontList::WillShutdown() { 1976 LOG(("WillShutdown")); 1977 WriteCache(); 1978 mFontNameCache = nullptr; 1979 }