From f54128e59f0572978c6684b2a85a6360b4e3a00e Mon Sep 17 00:00:00 2001 From: "jdaggett@mozilla.com" Date: Wed, 20 Feb 2008 15:57:32 -0800 Subject: [PATCH] Bug 417444. Load in other font family names lazily to avoid doing expensive name table reads at startup. r+sr=roc --- gfx/thebes/src/gfxQuartzFontCache.h | 32 ++++- gfx/thebes/src/gfxQuartzFontCache.mm | 264 +++++++++++++++++++++-------------- 2 files changed, 191 insertions(+), 105 deletions(-) diff --git a/gfx/thebes/src/gfxQuartzFontCache.h b/gfx/thebes/src/gfxQuartzFontCache.h index e2e87da3a..7d5137001 100644 --- a/gfx/thebes/src/gfxQuartzFontCache.h +++ b/gfx/thebes/src/gfxQuartzFontCache.h @@ -41,6 +41,7 @@ #define GFXQUARTZFONTCACHE_H_ #include "nsDataHashtable.h" +#include "nsRefPtrHashtable.h" #include "gfxFontUtils.h" #include "gfxAtsuiFonts.h" @@ -63,6 +64,7 @@ struct FontSearch { }; class MacOSFamilyEntry; +class gfxQuartzFontCache; // a single member of a font family (i.e. a single face, such as Times Italic) class MacOSFontEntry @@ -104,6 +106,9 @@ protected: PRPackedBool mATSUIDInitialized; }; +// helper class for adding other family names back into font cache +class AddOtherFamilyNameFunctor; + // a single font family, referencing one or more faces class MacOSFamilyEntry { @@ -111,7 +116,7 @@ public: THEBES_INLINE_DECL_REFCOUNTING(MacOSFamilyEntry) MacOSFamilyEntry(nsString &aName) : - mName(aName) + mName(aName), mOtherFamilyNamesInitialized(PR_FALSE) { } @@ -128,6 +133,9 @@ public: // used as part of the font fallback process void FindFontForChar(FontSearch *aMatchData); + // read in other family names, if any, and use functor to add each into cache + void ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor); + protected: // add font entries into array that match specified traits, returned in array listed by weight @@ -141,6 +149,7 @@ protected: nsString mName; // canonical font family name returned from NSFontManager nsTArray > mAvailableFonts; + PRPackedBool mOtherFamilyNamesInitialized; }; @@ -184,16 +193,28 @@ public: PRBool GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray > *array); void SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray >& array); + + void AddOtherFamilyName(MacOSFamilyEntry *aFamilyEntry, nsAString& aOtherFamilyName); private: static PLDHashOperator PR_CALLBACK FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr& aFamilyEntry, void* userArg); + static gfxQuartzFontCache *sSharedFontCache; gfxQuartzFontCache(); + // initialize font lists void InitFontList(); + void ReadOtherFamilyNamesForFamily(const nsAString& aFamilyName); + + // separate initialization for reading in name tables, since this is expensive + void InitOtherFamilyNames(); + + static PLDHashOperator PR_CALLBACK InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey, + nsRefPtr& aFamilyEntry, + void* userArg); void GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult); static void ATSNotification(ATSFontNotificationInfoRef aInfo, void* aUserArg); @@ -205,11 +226,11 @@ private: void* aUserArg); // canonical family name ==> family entry (unique, one name per family entry) - nsDataHashtable > mFontFamilies; + nsRefPtrHashtable mFontFamilies; - // localized family name ==> family entry (not unique, can have multiple names per + // other family name ==> family entry (not unique, can have multiple names per // family entry, only names *other* than the canonical names are stored here) - nsDataHashtable > mLocalizedFamilies; + nsRefPtrHashtable mOtherFamilyNames; // cached pref font lists // maps list of family names ==> array of family entries, one per lang group @@ -217,6 +238,9 @@ private: // when system-wide font lookup fails for a character, cache it to skip future searches gfxSparseBitSet mCodepointsWithNoFonts; + + // flag set after InitOtherFamilyNames is called upon first name lookup miss + PRPackedBool mOtherFamilyNamesInitialized; }; diff --git a/gfx/thebes/src/gfxQuartzFontCache.mm b/gfx/thebes/src/gfxQuartzFontCache.mm index 562eca0f6..29603208d 100644 --- a/gfx/thebes/src/gfxQuartzFontCache.mm +++ b/gfx/thebes/src/gfxQuartzFontCache.mm @@ -241,6 +241,21 @@ MacOSFontEntry::ReadCMAP() /* MacOSFamilyEntry */ #pragma mark- +// helper class for adding other family names back into font cache +class AddOtherFamilyNameFunctor +{ +public: + AddOtherFamilyNameFunctor(gfxQuartzFontCache *aFontCache) : + mFontCache(aFontCache) + {} + + void operator() (MacOSFamilyEntry *aFamilyEntry, nsAString& aOtherName) { + mFontCache->AddOtherFamilyName(aFamilyEntry, aOtherName); + } + + gfxQuartzFontCache *mFontCache; +}; + static const PRUint32 kTraits_NonNormalWidthMask = NSNarrowFontMask | NSExpandedFontMask | NSCondensedFontMask | NSCompressedFontMask | NSFixedPitchFontMask; @@ -310,7 +325,7 @@ MacOSFamilyEntry::FindFontForChar(FontSearch *aMatchData) rank += 20; } - // if we didn't match any characters don't bother wasting more time. + // if we didn't match any characters don't bother wasting more time with this face. if (rank == 0) continue; @@ -466,30 +481,6 @@ MacOSFamilyEntry::FindFontWeight(MacOSFontEntry* aFontsForWeights[], const gfxFo return aFontsForWeights[baseMatch]; } -/* gfxQuartzFontCache */ -#pragma mark- - -gfxQuartzFontCache *gfxQuartzFontCache::sSharedFontCache = nsnull; - -gfxQuartzFontCache::gfxQuartzFontCache() -{ - mFontFamilies.Init(100); - mLocalizedFamilies.Init(30); - mPrefFonts.Init(10); - - InitFontList(); - ::ATSFontNotificationSubscribe(ATSNotification, - kATSFontNotifyOptionDefault, - (void*)this, nsnull); - - // pref changes notification setup - nsCOMPtr pref = do_GetService(NS_PREF_CONTRACTID); - pref->RegisterCallback("font.", PrefChangedCallback, this); - pref->RegisterCallback("font.name-list.", PrefChangedCallback, this); - pref->RegisterCallback("intl.accept_languages", PrefChangedCallback, this); // hmmmm... - -} - static NSString* CreateNameFromBuffer(const UInt8 *aBuf, ByteCount aLength, FontPlatformCode aPlatformCode, FontScriptCode aScriptCode, FontLanguageCode aLangCode ) { @@ -522,7 +513,6 @@ static NSString* CreateNameFromBuffer(const UInt8 *aBuf, ByteCount aLength, return (NSString*) outName; } - // 10.4 headers only define TT/OT name table id's up to the license id (14) but 10.5 does, so use our own enum enum { kMozillaFontPreferredFamilyName = 16, @@ -531,25 +521,20 @@ enum { // xxx - rather than use ATSUI calls, probably faster to load name table directly, // this avoids copying around strings that are of no interest -// read in family names other than the canonical family name, including localized family names -static void ReadOtherFamilyNames(NSString *familyName, NSArray *faceArray, - int faceIndex, NSMutableArray *localizedNames) +// returns true if other names were found, false otherwise +static PRBool ReadOtherFamilyNamesForFace(AddOtherFamilyNameFunctor& aOtherFamilyFunctor, MacOSFamilyEntry *aFamilyEntry, + NSString *familyName, ATSUFontID fontID) { OSStatus err; ItemCount i, nameCount; - ATSUFontID fontID; - - NSArray *face = [faceArray objectAtIndex:faceIndex]; - NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME]; - NSFont *font = [NSFont fontWithName:psname size:0.0]; - fontID = [font _atsFontID]; + PRBool foundNames = PR_FALSE; if (fontID == kATSUInvalidFontID) - return; + return foundNames; err = ATSUCountFontNames(fontID, &nameCount); if (err != noErr) - return; + return foundNames; for (i = 0; i < nameCount; i++) { @@ -567,52 +552,94 @@ static void ReadOtherFamilyNames(NSString *familyName, NSArray *faceArray, continue; // any other error, bail if (err != noErr) - return; + return foundNames; if (!(nameCode == kFontFamilyName || nameCode == kMozillaFontPreferredFamilyName)) continue; if (len >= kBufLength) continue; buf[len] = 0; NSString *name = CreateNameFromBuffer((UInt8*)buf, len, platformCode, scriptCode, langCode); - NSString *foundName = nil; // add if not same as canonical family name or already in list of names if (name) { if (![name isEqualToString:familyName]) { - - // search other localized names - int j, lnameCount = [localizedNames count]; - NSString *lname; - for (j = 0; j < lnameCount; j++) { - lname = [localizedNames objectAtIndex:j]; - if ([lname isEqualToString:name]) { - foundName = lname; - break; - } - } - - // didn't find it in the list? add it - if (!foundName) { - [localizedNames addObject:name]; - PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(init-otherfamily) canonical family: %s, other family: %s\n", [familyName UTF8String], [name UTF8String])); - } + nsAutoString otherFamilyName; + GetStringForNSString(name, otherFamilyName); + aOtherFamilyFunctor(aFamilyEntry, otherFamilyName); + foundNames = PR_TRUE; } [name release]; } } + return foundNames; +} + +void +MacOSFamilyEntry::ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor) +{ + if (mOtherFamilyNamesInitialized) + return; + mOtherFamilyNamesInitialized = PR_TRUE; + + NSString *familyName = GetNSStringForString(mName); + + // read in other family names for the first face in the list + MacOSFontEntry *fe = mAvailableFonts[0]; + + PRBool foundNames = ReadOtherFamilyNamesForFace(aOtherFamilyFunctor, this, familyName, fe->GetFontID()); + + // read in other names for the first face in the list with the assumption + // that if extra names don't exist in that face then they don't exist in + // other faces for the same font + if (foundNames) { + PRUint32 numFonts, i; + + // read in names for all faces, needed to catch cases where + // fonts all family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6) + numFonts = mAvailableFonts.Length(); + for (i = 1; i < numFonts; i++) { + fe = mAvailableFonts[i]; + ReadOtherFamilyNamesForFace(aOtherFamilyFunctor, this, familyName, fe->GetFontID()); + } + } +} + +/* gfxQuartzFontCache */ +#pragma mark- + +gfxQuartzFontCache *gfxQuartzFontCache::sSharedFontCache = nsnull; + +gfxQuartzFontCache::gfxQuartzFontCache() +{ + mFontFamilies.Init(100); + mOtherFamilyNames.Init(30); + mOtherFamilyNamesInitialized = PR_FALSE; + mPrefFonts.Init(10); + + InitFontList(); + ::ATSFontNotificationSubscribe(ATSNotification, + kATSFontNotifyOptionDefault, + (void*)this, nsnull); + + // pref changes notification setup + nsCOMPtr pref = do_GetService(NS_PREF_CONTRACTID); + pref->RegisterCallback("font.", PrefChangedCallback, this); + pref->RegisterCallback("font.name-list.", PrefChangedCallback, this); + pref->RegisterCallback("intl.accept_languages", PrefChangedCallback, this); // hmmmm... + } -const int kIndexNormalFace_NotFound = -1; const PRUint32 kNonNormalTraits = NSItalicFontMask | NSBoldFontMask | NSNarrowFontMask | NSExpandedFontMask | NSCondensedFontMask | NSCompressedFontMask; void gfxQuartzFontCache::InitFontList() { mFontFamilies.Clear(); - mLocalizedFamilies.Clear(); + mOtherFamilyNames.Clear(); + mOtherFamilyNamesInitialized = PR_FALSE; mPrefFonts.Clear(); mCodepointsWithNoFonts.reset(); @@ -620,8 +647,7 @@ gfxQuartzFontCache::InitFontList() NSFontManager *fontManager = [NSFontManager sharedFontManager]; NSEnumerator *families = [[fontManager availableFontFamilies] objectEnumerator]; // returns "canonical", non-localized family name - NSMutableArray* localizedNames = [[NSMutableArray alloc] init]; - nsAutoString availableFamilyName, postscriptFontName, localizedName; + nsAutoString availableFamilyName, postscriptFontName; NSString *availableFamily = nil; while ((availableFamily = [families nextObject])) { @@ -630,13 +656,12 @@ gfxQuartzFontCache::InitFontList() GetStringForNSString(availableFamily, availableFamilyName); // create a family entry - nsRefPtr familyEntry = new MacOSFamilyEntry(availableFamilyName); + MacOSFamilyEntry *familyEntry = new MacOSFamilyEntry(availableFamilyName); if (!familyEntry) break; // create a font entry for each face NSArray *fontfaces = [fontManager availableMembersOfFontFamily:availableFamily]; // returns an array of [psname, style name, weight, traits] elements, goofy api int faceCount = [fontfaces count]; - int normalFaceIndex = kIndexNormalFace_NotFound; int faceIndex; for (faceIndex = 0; faceIndex < faceCount; faceIndex++) { @@ -651,52 +676,32 @@ gfxQuartzFontCache::InitFontList() if (!(traits & NSItalicFontMask)) traits |= NSUnitalicFontMask; - PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(init) family: %s, psname: %s, face: %s, apple-weight: %d, css-weight: %d, traits: %8.8x\n", + PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit) family: %s, psname: %s, face: %s, apple-weight: %d, css-weight: %d, traits: %8.8x\n", [availableFamily UTF8String], [psname UTF8String], [[face objectAtIndex:INDEX_FONT_FACE_NAME] UTF8String], weight, gfxQuartzFontCache::AppleWeightToCSSWeight(weight), traits )); // make a nsString GetStringForNSString(psname, postscriptFontName); // create a font entry - nsRefPtr fontEntry = new MacOSFontEntry(postscriptFontName, weight, traits, familyEntry); + MacOSFontEntry *fontEntry = new MacOSFontEntry(postscriptFontName, weight, traits, familyEntry); if (!fontEntry) break; // insert into font entry array of family familyEntry->AddFontEntry(fontEntry); - - // if normal face not found yet, check the traits for this one - if ( normalFaceIndex == kIndexNormalFace_NotFound && !(traits & kNonNormalTraits)) - normalFaceIndex = faceIndex; - } - - // if no normal face, just use the first face in the array of available ones - if ( normalFaceIndex == kIndexNormalFace_NotFound ) - normalFaceIndex = 0; - - // read the name table for the normal face; if localized names exist for that face, scan all font entries for more localized names - [localizedNames removeAllObjects]; - ReadOtherFamilyNames(availableFamily, fontfaces, normalFaceIndex, localizedNames); - if ([localizedNames count] != 0) { - for (faceIndex = 0; faceIndex < faceCount; faceIndex++) { - if (faceIndex == normalFaceIndex) continue; - ReadOtherFamilyNames(availableFamily, fontfaces, faceIndex, localizedNames); - } } // add the family entry to the hash table ToLowerCase(availableFamilyName); mFontFamilies.Put(availableFamilyName, familyEntry); - - // add in an entry for each of the localized names - int lnameIndex, lnameCount = [localizedNames count]; - for (lnameIndex = 0; lnameIndex < lnameCount; lnameIndex++) { - GetStringForNSString([localizedNames objectAtIndex:lnameIndex], localizedName); - ToLowerCase(localizedName); - mLocalizedFamilies.Put(localizedName, familyEntry); - } - } + // to avoid full search of font name tables, seed the other names table with localized names from + // some of the prefs fonts which are accessed via their localized names. changes in the pref fonts will only cause + // a font lookup miss earlier. this is a simple optimization, it's not required for correctness + ReadOtherFamilyNamesForFamily(NS_LITERAL_STRING("Hiragino Kaku Gothic Pro")); + ReadOtherFamilyNamesForFamily(NS_LITERAL_STRING("Hiragino Mincho Pro")); + ReadOtherFamilyNamesForFamily(NS_LITERAL_STRING("STSong")); + // xxx - deal with quirks (e.g. Osaka-Mono) // xxx - need to remove bogus Helvetica/Courier italic faces (Cocoa inanity!) @@ -705,19 +710,45 @@ gfxQuartzFontCache::InitFontList() mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls mCodepointsWithNoFonts.set(0xfffd); // unknown - - [localizedNames release]; + +} + +void +gfxQuartzFontCache::InitOtherFamilyNames() +{ + mOtherFamilyNamesInitialized = PR_TRUE; + + // iterate over all font families and read in other family names + mFontFamilies.Enumerate(gfxQuartzFontCache::InitOtherFamilyNamesProc, this); +} + +PLDHashOperator PR_CALLBACK gfxQuartzFontCache::InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey, + nsRefPtr& aFamilyEntry, + void* userArg) +{ + gfxQuartzFontCache *fc = (gfxQuartzFontCache*) userArg; + AddOtherFamilyNameFunctor addOtherNames(fc); + aFamilyEntry->ReadOtherFamilyNames(addOtherNames); + return PL_DHASH_NEXT; +} + +void +gfxQuartzFontCache::ReadOtherFamilyNamesForFamily(const nsAString& aFamilyName) +{ + MacOSFamilyEntry *familyEntry = FindFamily(aFamilyName); + + if (familyEntry) { + AddOtherFamilyNameFunctor addOtherNames(this); + familyEntry->ReadOtherFamilyNames(addOtherNames); + } } PRBool gfxQuartzFontCache::ResolveFontName(const nsAString& aFontName, nsAString& aResolvedFontName) { - nsAutoString key; - nsRefPtr familyEntry; - GenerateFontListKey(aFontName, key); - - if (mFontFamilies.Get(key, &familyEntry) || mLocalizedFamilies.Get(key, &familyEntry)) { - aResolvedFontName = familyEntry->Name(); + MacOSFamilyEntry *family = FindFamily(aFontName); + if (family) { + aResolvedFontName = family->Name(); return PR_TRUE; } return PR_FALSE; @@ -822,12 +853,29 @@ MacOSFamilyEntry* gfxQuartzFontCache::FindFamily(const nsAString& aFamily) { nsAutoString key; - nsRefPtr familyEntry; + MacOSFamilyEntry *familyEntry; GenerateFontListKey(aFamily, key); - if (mFontFamilies.Get(key, &familyEntry) || mLocalizedFamilies.Get(key, &familyEntry)) { + // lookup in canonical (i.e. English) family name list + if (mFontFamilies.Get(key, &familyEntry)) { return familyEntry; } + + // lookup in other family names list (mostly localized names) + if (mOtherFamilyNames.Get(key, &familyEntry)) { + return familyEntry; + } + + // name not found and other family names not yet fully initialized so + // initialize the rest of the list and try again. this is done lazily + // since reading name table entries is expensive + if (!mOtherFamilyNamesInitialized) { + InitOtherFamilyNames(); + if (mOtherFamilyNames.Get(key, &familyEntry)) { + return familyEntry; + } + } + return nsnull; } @@ -864,3 +912,17 @@ gfxQuartzFontCache::SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray< mPrefFonts.Put(PRUint32(aLangGroup), array); } +void +gfxQuartzFontCache::AddOtherFamilyName(MacOSFamilyEntry *aFamilyEntry, nsAString& aOtherFamilyName) +{ + nsAutoString key; + MacOSFamilyEntry *familyEntry; + GenerateFontListKey(aOtherFamilyName, key); + + if (!mOtherFamilyNames.Get(key, &familyEntry)) { + mOtherFamilyNames.Put(key, aFamilyEntry); + PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit-otherfamily) canonical family: %s, other family: %s\n", + NS_ConvertUTF16toUTF8(aFamilyEntry->Name()).get(), + NS_ConvertUTF16toUTF8(aOtherFamilyName).get())); + } +} -- 2.11.4.GIT