/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include"gfxFT2FontList.h" #include"gfxFT2Fonts.h" #include"gfxFT2Utils.h" #include"gfxUserFontSet.h" #include"gfxFontUtils.h" #include"SharedFontList-impl.h" #include"StandardFonts-android.inc" #include"harfbuzz/hb-ot.h"// for name ID constants
// Helper to access the FT_Face for a given FT2FontEntry, // creating a temporary face if the entry does not have one yet. // This allows us to read font names, tables, etc if necessary // without permanently instantiating a freetype face and consuming // memory long-term. // This may fail (resulting in a null FT_Face), e.g. if it fails to // allocate memory to uncompress a font from omnijar.
already_AddRefed<SharedFTFace> FT2FontEntry::GetFTFace(bool aCommit) { if (mFTFace) { // Create a new reference, and return it.
RefPtr<SharedFTFace> face(mFTFace); return face.forget();
}
NS_ASSERTION(!mFilename.IsEmpty(), "can't use GetFTFace for fonts without a filename");
// A relative path (no initial "/") means this is a resource in // omnijar, not an installed font on the device. // The NS_ASSERTIONs here should never fail, as the resource must have // been read successfully during font-list initialization or we'd never // have created the font entry. The only legitimate runtime failure // here would be memory allocation, in which case mFace remains null.
RefPtr<SharedFTFace> face; if (mFilename[0] != '/') {
RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE);
nsZipItem* item = reader->GetItem(mFilename);
NS_ASSERTION(item, "failed to find zip entry");
uint32_t bufSize = item->RealSize();
uint8_t* fontDataBuf = static_cast<uint8_t*>(malloc(bufSize)); if (fontDataBuf) {
nsZipCursor cursor(item, reader, fontDataBuf, bufSize);
cursor.Copy(&bufSize);
NS_ASSERTION(bufSize == item->RealSize(), "error reading bundled font");
RefPtr<FTUserFontData> ufd = new FTUserFontData(fontDataBuf, bufSize);
face = ufd->CloneFace(mFTFontIndex); if (!face) {
NS_WARNING("failed to create freetype face"); return nullptr;
}
}
} else {
RefPtr<FTUserFontData> fd = new FTUserFontData(mFilename.get());
face = fd->CloneFace(mFTFontIndex); if (!face) {
NS_WARNING("failed to create freetype face"); return nullptr;
} if (FT_Err_Ok != FT_Select_Charmap(face->GetFace(), FT_ENCODING_UNICODE) &&
FT_Err_Ok !=
FT_Select_Charmap(face->GetFace(), FT_ENCODING_MS_SYMBOL)) {
NS_WARNING("failed to select Unicode or symbol charmap");
}
}
if (aCommit) { if (mFTFace.compareExchange(nullptr, face.get())) { // The reference we created is now owned by mFTFace.
Unused << face.forget();
} else { // We lost a race! Just discard our new face and use the existing one.
}
}
// Create a new reference, and return it.
face = mFTFace; return face.forget();
}
FTUserFontData* FT2FontEntry::GetUserFontData() { if (SharedFTFace* face = mFTFace) { returnstatic_cast<FTUserFontData*>(face->GetData());
} return nullptr;
}
/* * FT2FontEntry * gfxFontEntry subclass corresponding to a specific face that can be * rendered by freetype. This is associated with a face index in a * file (normally a .ttf/.otf file holding a single face, but in principle * there could be .ttc files with multiple faces). * The FT2FontEntry can create the necessary FT_Face on demand, and can * then create a Cairo font_face and scaled_font for drawing.
*/
FT2FontEntry::~FT2FontEntry() { if (mMMVar) {
SharedFTFace* face = mFTFace;
FT_Done_MM_Var(face->GetFace()->glyph->library, mMMVar);
} if (mFTFace) { auto face = mFTFace.exchange(nullptr);
NS_IF_RELEASE(face);
}
}
gfxFontEntry* FT2FontEntry::Clone() const {
MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
FT2FontEntry* fe = new FT2FontEntry(Name());
fe->mFilename = mFilename;
fe->mFTFontIndex = mFTFontIndex;
fe->mWeightRange = mWeightRange;
fe->mStretchRange = mStretchRange;
fe->mStyleRange = mStyleRange; return fe;
}
gfxFont* FT2FontEntry::CreateFontInstance(const gfxFontStyle* aStyle) {
RefPtr<SharedFTFace> face = GetFTFace(true); if (!face) { return nullptr;
}
if (face->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) { // Resolve variations from entry (descriptor) and style (property)
AutoTArray<gfxFontVariation, 8> settings;
GetVariationsForStyle(settings, aStyle ? *aStyle : gfxFontStyle());
// If variations are present, we will not use our cached mFTFace // but always create a new one as it will have custom variation // coordinates applied. if (!settings.IsEmpty()) { // Create a separate FT_Face because we need to apply custom // variation settings to it.
RefPtr<SharedFTFace> varFace; if (!mFilename.IsEmpty() && mFilename[0] == '/') {
varFace =
Factory::NewSharedFTFace(nullptr, mFilename.get(), mFTFontIndex);
} else {
varFace = face->GetData()->CloneFace(mFTFontIndex);
} if (varFace) {
gfxFT2FontBase::SetupVarCoords(GetMMVar(), settings,
varFace->GetFace());
face = std::move(varFace);
}
}
}
int loadFlags = gfxPlatform::GetPlatform()->FontHintingEnabled()
? FT_LOAD_DEFAULT
: (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING); if (face->GetFace()->face_flags & FT_FACE_FLAG_TRICKY) {
loadFlags &= ~FT_LOAD_NO_AUTOHINT;
}
RefPtr<UnscaledFontFreeType> unscaledFont(mUnscaledFont); if (!unscaledFont) {
RefPtr<SharedFTFace> origFace(mFTFace);
unscaledFont =
!mFilename.IsEmpty() && mFilename[0] == '/'
? new UnscaledFontFreeType(mFilename.BeginReading(), mFTFontIndex,
std::move(origFace))
: new UnscaledFontFreeType(std::move(origFace));
mUnscaledFont = unscaledFont;
}
gfxFont* font = new gfxFT2Font(unscaledFont, std::move(face), this, aStyle, loadFlags); return font;
}
/* static */
FT2FontEntry* FT2FontEntry::CreateFontEntry( const nsACString& aFontName, WeightRange aWeight, StretchRange aStretch,
SlantStyleRange aStyle, const uint8_t* aFontData, uint32_t aLength) { // Ownership of aFontData is passed in here; the fontEntry must // retain it as long as the FT_Face needs it, and ensure it is // eventually deleted.
RefPtr<FTUserFontData> ufd = new FTUserFontData(aFontData, aLength);
RefPtr<SharedFTFace> face = ufd->CloneFace(); if (!face) { return nullptr;
} // Create our FT2FontEntry, which inherits the name of the userfont entry // as it's not guaranteed that the face has valid names (bug 737315)
FT2FontEntry* fe =
FT2FontEntry::CreateFontEntry(aFontName, nullptr, 0, nullptr); if (fe) {
fe->mFTFace = face.forget().take(); // mFTFace takes ownership.
fe->mStyleRange = aStyle;
fe->mWeightRange = aWeight;
fe->mStretchRange = aStretch;
fe->mIsDataUserFont = true;
} return fe;
}
// For variable fonts, update the style/weight/stretch attributes if the // corresponding variation axes are present.
aFontEntry->SetupVariationRanges();
}
// Used to create the font entry for installed faces on the device, // when iterating over the fonts directories. // We use the hb_face_t to retrieve the details needed for the font entry, // but do *not* save a reference to it, nor create a cairo face. /* static */
FT2FontEntry* FT2FontEntry::CreateFontEntry(const nsACString& aName, constchar* aFilename,
uint8_t aIndex, const hb_face_t* aFace) {
FT2FontEntry* fe = new FT2FontEntry(aName);
fe->mFilename = aFilename;
fe->mFTFontIndex = aIndex;
if (aFace) {
SetPropertiesFromFace(fe, aFace);
} else { // If nullptr is passed for aFace, the caller is intending to override // these attributes anyway. We just set defaults here to be safe.
fe->mStyleRange = SlantStyleRange(FontSlantStyle::NORMAL);
fe->mWeightRange = WeightRange(FontWeight::NORMAL);
fe->mStretchRange = StretchRange(FontStretch::NORMAL);
}
// Copied/modified from similar code in gfxMacPlatformFontList.mm: // Complex scripts will not render correctly unless Graphite or OT // layout tables are present. // For OpenType, we also check that the GSUB table supports the relevant // script tag, to avoid using things like Arial Unicode MS for Lao (it has // the characters, but lacks OpenType support).
// TODO: consider whether we should move this to gfxFontEntry and do similar // cmap-masking on all platforms to avoid using fonts that won't shape // properly.
if (NS_SUCCEEDED(rv) && !mIsDataUserFont && !HasGraphiteTables()) { // For downloadable fonts, trust the author and don't // try to munge the cmap based on script shaping support.
// We also assume a Graphite font knows what it's doing, // and provides whatever shaping is needed for the // characters it supports, so only check/clear the // complex-script ranges for non-Graphite fonts
// for layout support, check for the presence of opentype layout tables bool hasGSUB = HasFontTable(TRUETYPE_TAG('G', 'S', 'U', 'B'));
for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
sr->rangeStart; sr++) { // check to see if the cmap includes complex script codepoints if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) { // We check for GSUB here, as GPOS alone would not be ok. if (hasGSUB && SupportsScriptInGSUB(sr->tags, sr->numTags)) { continue;
}
charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
}
}
}
#ifdef MOZ_WIDGET_ANDROID // Hack for the SamsungDevanagari font, bug 1012365: // pretend the font supports U+0972. if (!charmap->test(0x0972) && charmap->test(0x0905) &&
charmap->test(0x0945)) {
charmap->set(0x0972);
} #endif
bool setCharMap = true; if (NS_SUCCEEDED(rv)) {
gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
fontlist::FontList* sharedFontList = pfl->SharedFontList(); if (!IsUserFont() && mShmemFace) {
mShmemFace->SetCharacterMap(sharedFontList, charmap, mShmemFamily); if (TrySetShmemCharacterMap()) {
setCharMap = false;
}
} else {
charmap = pfl->FindCharMap(charmap);
}
mHasCmapTable = true;
} else { // if error occurred, initialize to null cmap
charmap = new gfxCharacterMap();
mHasCmapTable = false;
} if (setCharMap) { if (mCharacterMap.compareExchange(nullptr, charmap.get())) { // We forget rather than addref because we don't use the charmap below.
Unused << charmap.forget();
}
}
return rv;
}
hb_face_t* FT2FontEntry::CreateHBFace() const {
hb_face_t* result = nullptr;
if (mFilename[0] == '/') { // An absolute path means a normal file in the filesystem, so we can use // hb_blob_create_from_file to read it.
gfxFontUtils::AutoHBBlob fileBlob(
hb_blob_create_from_file(mFilename.get())); if (hb_blob_get_length(fileBlob) > 0) {
result = hb_face_create(fileBlob, mFTFontIndex);
}
} else { // A relative path means an omnijar resource, which we may need to // decompress to a temporary buffer.
RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::Type::GRE);
nsZipItem* item = reader->GetItem(mFilename);
MOZ_ASSERT(item, "failed to find zip entry"); if (item) { // TODO(jfkthame): // Check whether the item is compressed; if not, we could just get a // pointer without needing to allocate a buffer and copy the data. // (Currently this configuration isn't used for Gecko on Android.)
uint32_t length = item->RealSize();
uint8_t* buffer = static_cast<uint8_t*>(malloc(length)); if (buffer) {
nsZipCursor cursor(item, reader, buffer, length);
cursor.Copy(&length);
MOZ_ASSERT(length == item->RealSize(), "error reading font"); if (length == item->RealSize()) {
gfxFontUtils::AutoHBBlob blob(
hb_blob_create((constchar*)buffer, length,
HB_MEMORY_MODE_READONLY, buffer, free));
result = hb_face_create(blob, mFTFontIndex);
}
}
}
}
return result;
}
bool FT2FontEntry::HasFontTable(uint32_t aTableTag) { // If we already have a FreeType face, we can just use that. if (mFTFace) {
RefPtr<SharedFTFace> face = GetFTFace(); return gfxFT2FontEntryBase::FaceHasTable(face, aTableTag);
}
{ // If we have a cached set of tables, query that.
AutoReadLock lock(mLock); if (mAvailableTables.Count() > 0) { return mAvailableTables.Contains(aTableTag);
}
}
// If we haven't created a FreeType face already, try to avoid that by // reading the available table tags via harfbuzz and caching in a hashset.
AutoWriteLock lock(mLock); if (!mFTFace && !mFilename.IsEmpty()) {
hb_face_t* face = CreateHBFace(); if (face) { // Read table tags in batches; 32 should be enough for most fonts in a // single operation. constunsigned TAG_BUF_LENGTH = 32;
hb_tag_t tags[TAG_BUF_LENGTH]; unsignedint startOffset = 0; unsignedint totalTables = 0; do { unsignedint count = TAG_BUF_LENGTH; // Updates count to the number of table tags actually retrieved
totalTables = hb_face_get_table_tags(face, startOffset, &count, tags);
startOffset += count; while (count-- > 0) {
mAvailableTables.Insert(tags[count]);
}
} while (startOffset < totalTables);
hb_face_destroy(face);
} else { // Failed to create the HarfBuzz face! The font is probably broken. // Put a dummy entry in mAvailableTables so that we don't bother // re-trying here.
mAvailableTables.Insert(uint32_t(-1));
} return mAvailableTables.Contains(aTableTag);
}
// Last resort: we'll have to create a (temporary) FreeType face to query // for table presence.
RefPtr<SharedFTFace> face = GetFTFace(); return gfxFT2FontEntryBase::FaceHasTable(face, aTableTag);
}
hb_blob_t* FT2FontEntry::GetFontTable(uint32_t aTableTag) { if (FTUserFontData* userFontData = GetUserFontData()) { // If there's a cairo font face, we may be able to return a blob // that just wraps a range of the attached user font data if (userFontData->FontData()) { return gfxFontUtils::GetTableFromFontData(userFontData->FontData(),
aTableTag);
}
}
// If the FT_Face hasn't been instantiated, try to read table directly // via harfbuzz API to avoid expensive FT_Face creation. if (!mFTFace && !mFilename.IsEmpty()) {
hb_face_t* face = CreateHBFace(); if (face) {
hb_blob_t* result = hb_face_reference_table(face, aTableTag);
hb_face_destroy(face); return result;
}
}
// Otherwise, use the default method (which in turn will call our // implementation of CopyFontTable). return gfxFontEntry::GetFontTable(aTableTag);
}
bool FT2FontEntry::HasVariations() { switch (mHasVariations) { case HasVariationsState::No: returnfalse; case HasVariationsState::Yes: returntrue; case HasVariationsState::Uninitialized: break;
}
/* * FT2FontFamily * A standard gfxFontFamily; just adds a method used to support sending * the font list from chrome to content via IPC.
*/
void FT2FontFamily::AddFacesToFontList(nsTArray<FontListEntry>* aFontList) {
AutoReadLock lock(mLock); for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) { const FT2FontEntry* fe = static_cast<const FT2FontEntry*>(mAvailableFonts[i].get()); if (!fe) { continue;
}
/* * Startup cache support for the font list: * We store the list of families and faces, with their style attributes and the * corresponding font files, in the startup cache. * This allows us to recreate the gfxFT2FontList collection of families and * faces without instantiating Freetype faces for each font file (in order to * find their attributes), leading to significantly quicker startup.
*/
class FontNameCache { public: // Delimiters used in the cached font-list records we store in startupCache staticconstchar kFileSep = 0x1c; staticconstchar kGroupSep = 0x1d; staticconstchar kRecordSep = 0x1e; staticconstchar kFieldSep = 0x1f;
// Separator for font property ranges; we only look for this within a // field that holds a serialized FontPropertyValue or Range, so there's no // risk of conflicting with printable characters in font names. // Note that this must be a character that will terminate strtof() parsing // of a number. staticconstchar kRangeSep = ':';
// Creates the object but does NOT load the cached data from the startup // cache; call Init() after creation to do that.
FontNameCache() : mMap(&mOps, sizeof(FNCMapEntry), 0), mWriteNeeded(false) { // HACK ALERT: it's weird to assign |mOps| after we passed a pointer to // it to |mMap|'s constructor. A more normal approach here would be to // have a static |sOps| member. Unfortunately, this mysteriously but // consistently makes Fennec start-up slower, so we take this // unorthodox approach instead. It's safe because PLDHashTable's // constructor doesn't dereference the pointer; it just makes a copy of // it.
mOps = (PLDHashTableOps){StringHash, HashMatchEntry, MoveEntry,
PLDHashTable::ClearEntryStub, nullptr};
MOZ_ASSERT(XRE_IsParentProcess(), "FontNameCache should only be used in chrome process");
mCache = mozilla::scache::StartupCache::GetSingleton();
}
// This may be called more than once (if we re-load the font list). void Init() { if (!mCache) { return;
}
uint32_t size; constchar* cur; if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &cur, &size))) {
LOG(("no cache of " CACHE_KEY)); return;
}
LOG(("got: %u bytes from the cache " CACHE_KEY, size));
mMap.Clear();
mWriteNeeded = false;
while (constchar* fileEnd = strchr(cur, kFileSep)) { // The cached record for one file is at [cur, fileEnd].
// Find end of field that starts at aStart, terminated by kGroupSep or // end of record. auto endOfField = [=](constchar* aStart) -> constchar* {
MOZ_ASSERT(aStart <= fileEnd); constchar* end = static_cast<constchar*>(
memchr(aStart, kGroupSep, fileEnd - aStart)); if (end) { return end;
} return fileEnd;
};
// Advance aStart and aEnd to indicate the range of the next field and // return true, or just return false if already at end of record. auto nextField = [=](constchar*& aStart, constchar*& aEnd) -> bool { if (aEnd < fileEnd) {
aStart = aEnd + 1;
aEnd = endOfField(aStart); returntrue;
} returnfalse;
};
constchar* end = endOfField(cur);
nsCString filename(cur, end - cur); if (!nextField(cur, end)) { break;
}
nsCString faceList(cur, end - cur); if (!nextField(cur, end)) { break;
}
uint32_t timestamp = strtoul(cur, nullptr, 10); if (!nextField(cur, end)) { break;
}
uint32_t filesize = strtoul(cur, nullptr, 10);
auto mapEntry = static_cast<FNCMapEntry*>(mMap.Add(filename.get(), fallible)); if (mapEntry) {
mapEntry->mFilename.Assign(filename);
mapEntry->mTimestamp = timestamp;
mapEntry->mFilesize = filesize;
mapEntry->mFaces.Assign(faceList); // entries from the startupcache are marked "non-existing" // until we have confirmed that the file still exists
mapEntry->mFileExists = false;
}
cur = fileEnd + 1;
}
}
void GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList,
uint32_t* aTimestamp, uint32_t* aFilesize) { auto entry = static_cast<FNCMapEntry*>(mMap.Search(aFileName.get())); if (entry) {
*aTimestamp = entry->mTimestamp;
*aFilesize = entry->mFilesize;
aFaceList.Assign(entry->mFaces); // this entry does correspond to an existing file // (although it might not be up-to-date, in which case // it will get overwritten via CacheFileInfo)
entry->mFileExists = true;
}
}
void FT2FontEntry::CheckForBrokenFont(gfxFontFamily* aFamily) { // note if the family is in the "bad underline" blocklist if (aFamily->IsBadUnderlineFamily()) {
mIsBadUnderlineFont = true;
}
nsAutoCString familyKey(aFamily->Name());
BuildKeyNameFromFontName(familyKey);
CheckForBrokenFont(familyKey);
}
void FT2FontEntry::CheckForBrokenFont(const nsACString& aFamilyKey) { // bug 721719 - set the IgnoreGSUB flag on entries for Roboto // because of unwanted on-by-default "ae" ligature. // (See also AppendFaceFromFontListEntry.) if (aFamilyKey.EqualsLiteral("roboto")) {
mIgnoreGSUB = true; return;
}
// bug 706888 - set the IgnoreGSUB flag on the broken version of // Droid Sans Arabic from certain phones, as identified by the // font checksum in the 'head' table if (aFamilyKey.EqualsLiteral("droid sans arabic")) {
RefPtr<SharedFTFace> face = GetFTFace(); if (face) { const TT_Header* head = static_cast<const TT_Header*>(
FT_Get_Sfnt_Table(face->GetFace(), ft_sfnt_head)); if (head && head->CheckSum_Adjust == 0xe445242) {
mIgnoreGSUB = true;
}
}
}
}
void gfxFT2FontList::AppendFacesFromBlob( const nsCString& aFileName, StandardFile aStdFile, hb_blob_t* aBlob,
FontNameCache* aCache, uint32_t aTimestamp, uint32_t aFilesize) {
nsCString newFaceList;
uint32_t numFaces = 1; unsignedint length; constchar* data = hb_blob_get_data(aBlob, &length); // Check for a possible TrueType Collection if (length >= sizeof(TTCHeader)) { const TTCHeader* ttc = reinterpret_cast<const TTCHeader*>(data); if (ttc->ttcTag == TRUETYPE_TAG('t', 't', 'c', 'f')) {
numFaces = ttc->numFonts;
}
} for (unsignedint index = 0; index < numFaces; index++) {
hb_face_t* face = hb_face_create(aBlob, index); if (face != hb_face_get_empty()) {
AddFaceToList(aFileName, index, aStdFile, face, newFaceList);
}
hb_face_destroy(face);
} if (aCache && !newFaceList.IsEmpty()) {
aCache->CacheFileInfo(aFileName, newFaceList, aTimestamp, aFilesize);
}
}
staticvoid GetName(hb_face_t* aFace, hb_ot_name_id_t aNameID,
nsACString& aName) { unsignedint n = 0;
n = hb_ot_name_get_utf8(aFace, aNameID, HB_LANGUAGE_INVALID, &n, nullptr); if (n) {
aName.SetLength(n++); // increment n to account for NUL terminator
n = hb_ot_name_get_utf8(aFace, aNameID, HB_LANGUAGE_INVALID, &n,
aName.BeginWriting());
}
}
// Given the harfbuzz face corresponding to an entryName and face index, // add the face to the available font list and to the faceList string void gfxFT2FontList::AddFaceToList(const nsCString& aEntryName, uint32_t aIndex,
StandardFile aStdFile, hb_face_t* aFace,
nsCString& aFaceList) {
nsAutoCString familyName; bool preferTypographicNames = true;
GetName(aFace, HB_OT_NAME_ID_TYPOGRAPHIC_FAMILY, familyName); if (familyName.IsEmpty()) {
preferTypographicNames = false;
GetName(aFace, HB_OT_NAME_ID_FONT_FAMILY, familyName);
} if (familyName.IsEmpty()) { return;
}
nsAutoCString fullname;
GetName(aFace, HB_OT_NAME_ID_FULL_NAME, fullname); if (fullname.IsEmpty()) { // Construct fullname from family + style
fullname = familyName;
nsAutoCString styleName; if (preferTypographicNames) {
GetName(aFace, HB_OT_NAME_ID_TYPOGRAPHIC_SUBFAMILY, styleName);
} if (styleName.IsEmpty()) {
GetName(aFace, HB_OT_NAME_ID_FONT_SUBFAMILY, styleName);
} if (!styleName.IsEmpty() && !styleName.EqualsLiteral("Regular")) {
fullname.Append(' ');
fullname.Append(styleName);
}
}
// Build the font entry name and create an FT2FontEntry, // but do -not- keep a reference to the FT_Face. // (When using the shared font list, this entry will not be retained, // it is used only to call AppendToFaceList.)
RefPtr<FT2FontEntry> fe =
FT2FontEntry::CreateFontEntry(fullname, aEntryName.get(), aIndex, aFace);
nsZipItem* item = aArchive->GetItem(aEntryName);
NS_ASSERTION(item, "failed to find zip entry");
uint32_t bufSize = item->RealSize();
// We use fallible allocation here; if there's not enough RAM, we'll simply // ignore the bundled fonts and fall back to the device's installed fonts. char* buffer = static_cast<char*>(malloc(bufSize)); if (!buffer) { return;
}
// Called on each family after all fonts are added to the list; // if aSortFaces is true this will sort faces to give priority to "standard" // font files. void FT2FontFamily::FinalizeMemberList(bool aSortFaces) {
AutoWriteLock lock(mLock);
SetHasStyles(true);
if (aSortFaces) {
SortAvailableFonts();
}
CheckForSimpleFamily();
}
// Chrome process: get the cached list (if any) if (!mFontNameCache) {
mFontNameCache = MakeUnique<FontNameCache>();
}
mFontNameCache->Init();
#ifdefined(MOZ_WIDGET_ANDROID) // Android API 29+ provides system font and font matcher API for native code.
nsAutoCString androidFontsRoot = [&] { // ANDROID_ROOT is the root of the android system, typically /system; // font files are in /$ANDROID_ROOT/fonts/
nsAutoCString root; char* androidRoot = PR_GetEnv("ANDROID_ROOT"); if (androidRoot) {
root = androidRoot;
} else {
root = "/system"_ns;
}
root.AppendLiteral("/fonts"); return root;
}();
if (useSystemFontAPI) {
gfxAndroidPlatform::WaitForInitializeFontAPI();
bool noFontByFontAPI = true;
AndroidSystemFontIterator iter; if (iter.Init()) { while (Maybe<AndroidFont> androidFont = iter.Next()) { if (constchar* fontPath = androidFont->GetFontFilePath()) {
noFontByFontAPI = false;
AppendFacesFromFontFile(nsDependentCString(fontPath),
mFontNameCache.get(), kStandard);
}
}
}
if (noFontByFontAPI) { // Font API doesn't seem to work. Use legacy way.
useSystemFontAPI = false;
}
if (!StaticPrefs::gfx_font_rendering_colr_v1_enabled()) { // We turn off COLRv1 fonts support. Newer android versions have // COLRv1 emoji font, and a legacy and hidden CBDT font we understand, // so try to find NotoColorEmojiLegacy.ttf explicitly for now.
nsAutoCString legacyEmojiFont(androidFontsRoot);
legacyEmojiFont.Append("/NotoColorEmojiLegacy.ttf");
AppendFacesFromFontFile(legacyEmojiFont, mFontNameCache.get(), kStandard);
}
}
if (!useSystemFontAPI) #endif
{
FindFontsInDir(androidFontsRoot, mFontNameCache.get());
}
// Look for fonts stored in omnijar, unless we're on a low-memory // device where we don't want to spend the RAM to decompress them. // (Prefs may disable this, or force-enable it even with low memory.) if (StaticPrefs::gfx_bundled_fonts_activate_AtStartup() > 0 ||
(StaticPrefs::gfx_bundled_fonts_activate_AtStartup() < 0 &&
!nsMemory::IsLowMemoryPlatform())) { auto timer = glean::fontlist::bundledfonts_activate.Measure();
FindFontsInOmnijar(mFontNameCache.get());
}
// Look for downloaded fonts in a profile-agnostic "fonts" directory.
nsCOMPtr<nsIProperties> dirSvc =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); if (dirSvc) {
nsCOMPtr<nsIFile> appDir;
nsresult rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(appDir)); if (NS_SUCCEEDED(rv)) {
appDir->AppendNative("fonts"_ns);
nsCString localPath; if (NS_SUCCEEDED(appDir->GetNativePath(localPath))) {
FindFontsInDir(localPath, mFontNameCache.get());
}
}
}
// look for locally-added fonts in a "fonts" subdir of the profile
nsCOMPtr<nsIFile> localDir;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
getter_AddRefs(localDir)); if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(localDir->Append(u"fonts"_ns))) {
nsCString localPath;
rv = localDir->GetNativePath(localPath); if (NS_SUCCEEDED(rv)) {
FindFontsInDir(localPath, mFontNameCache.get());
}
}
mFontNameCache->DropStaleEntries(); if (!mFontNameCache->EntryCount()) { // if we can't find any usable fonts, we are doomed!
MOZ_CRASH("No font files found");
}
// Write out FontCache data if needed
WriteCache();
}
// Add the face(s) from this file to our font list; // note that if we have cached info for this file in fnc, // and the file is unchanged, we won't actually need to read it. // If the file is new/changed, this will update the FontNameCache.
AppendFacesFromFontFile(s, aFNC, isStdFont ? kStandard : kUnknown);
}
}
closedir(d);
}
void gfxFT2FontList::AppendFaceFromFontListEntry(const FontListEntry& aFLE,
StandardFile aStdFile) {
FT2FontEntry* fe = FT2FontEntry::CreateFontEntry(aFLE); if (fe) {
nsAutoCString key(aFLE.familyName());
BuildKeyNameFromFontName(key);
fe->mStandardFace = (aStdFile == kStandard);
RefPtr<gfxFontFamily> family = mFontFamilies.LookupOrInsertWith(key, [&] { auto family =
MakeRefPtr<FT2FontFamily>(aFLE.familyName(), aFLE.visibility()); if (mSkipSpaceLookupCheckFamilies.Contains(key)) {
family->SetSkipSpaceFeatureCheck(true);
} if (mBadUnderlineFamilyNames.ContainsSorted(key)) {
family->SetBadUnderlineFamily();
} return family;
});
family->AddFontEntry(fe);
fe->CheckForBrokenFont(family);
}
}
void gfxFT2FontList::ReadSystemFontList(dom::SystemFontList* aList) {
AutoLock lock(mLock); for (constauto& entry : mFontFamilies) { auto family = static_cast<FT2FontFamily*>(entry.GetData().get());
family->AddFacesToFontList(&aList->entries());
}
}
staticvoid LoadSkipSpaceLookupCheck(
nsTHashSet<nsCString>& aSkipSpaceLookupCheck) {
AutoTArray<nsCString, 5> skiplist;
gfxFontUtils::GetPrefsFontList( "font.whitelist.skip_default_features_space_check", skiplist);
uint32_t numFonts = skiplist.Length(); for (uint32_t i = 0; i < numFonts; i++) {
ToLowerCase(skiplist[i]);
aSkipSpaceLookupCheck.Insert(skiplist[i]);
}
}
if (XRE_IsParentProcess()) { // This will populate/update mFontNameCache and store it in the // startupCache for future startups.
FindFonts();
// Finalize the families by sorting faces into standard order // and marking "simple" families. for (constauto& entry : mFontFamilies) { auto* family = static_cast<FT2FontFamily*>(entry.GetData().get());
family->FinalizeMemberList(/* aSortFaces */ true);
}
return NS_OK;
}
// Content process: use font list passed from the chrome process via // the GetXPCOMProcessAttributes message. auto& fontList = dom::ContentChild::GetSingleton()->SystemFontList(); for (FontListEntry& fle : fontList.entries()) { // We don't need to identify "standard" font files here, // as the faces are already sorted.
AppendFaceFromFontListEntry(fle, kUnknown);
}
// We don't need to sort faces (because they were already sorted by the // chrome process, so we just maintain the existing order) for (constauto& entry : mFontFamilies) { auto* family = static_cast<FT2FontFamily*>(entry.GetData().get());
family->FinalizeMemberList(/* aSortFaces */ false);
}
LOG(("got font list from chrome process: %" PRIdPTR " faces in %" PRIu32 " families",
fontList.entries().Length(), mFontFamilies.Count()));
fontList.entries().Clear();
return NS_OK;
}
void gfxFT2FontList::InitSharedFontListForPlatform() { if (!XRE_IsParentProcess()) { // Content processes will access the shared-memory data created by the // parent, so don't need to scan for available fonts themselves. return;
}
// This will populate mFontNameCache with entries for all the available font // files, and record them in mFamilies (unshared list) or mFamilyInitData and // mFaceInitData (shared font list).
FindFonts();
mozilla::fontlist::FontList* list = SharedFontList();
list->SetFamilyNames(mFamilyInitData);
auto families = list->Families(); for (uint32_t i = 0; i < mFamilyInitData.Length(); i++) { auto faceList = mFaceInitData.Get(mFamilyInitData[i].mKey);
MOZ_ASSERT(faceList);
families[i].AddFaces(list, *faceList);
}
if (SharedFontList()) { return LookupInSharedFaceNameList(aPresContext, aFontName, aWeightForEntry,
aStretchForEntry, aStyleForEntry);
}
// walk over list of names
FT2FontEntry* fontEntry = nullptr;
FontVisibility level =
aPresContext ? aPresContext->GetFontVisibility() : FontVisibility::User;
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.37 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.