From eae23e8bd3dd34c818db1f2d9512f676d23a695e Mon Sep 17 00:00:00 2001 From: "jdaggett%mozilla.com" Date: Tue, 26 Feb 2008 08:00:44 +0000 Subject: [PATCH] Bug 419370. Allow single face font families, specified in a pref setting. r+sr=stuart git-svn-id: svn://10.0.0.236/trunk@246510 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/gfx/thebes/public/gfxFontUtils.h | 3 + mozilla/gfx/thebes/src/gfxFontUtils.cpp | 49 ++++ mozilla/gfx/thebes/src/gfxQuartzFontCache.h | 44 +++- mozilla/gfx/thebes/src/gfxQuartzFontCache.mm | 242 +++++++++++++++++-- mozilla/modules/libpref/src/init/all.js | 8 + 5 files changed, 322 insertions(+), 24 deletions(-) diff --git a/mozilla/gfx/thebes/public/gfxFontUtils.h b/mozilla/gfx/thebes/public/gfxFontUtils.h index 6997063349d..a6c8f71a79e 100644 --- a/mozilla/gfx/thebes/public/gfxFontUtils.h +++ b/mozilla/gfx/thebes/public/gfxFontUtils.h @@ -316,6 +316,9 @@ public: } static PRUint8 CharRangeBit(PRUint32 ch); + + // for a given font list pref name, set up a list of font names + static void GetPrefsFontList(const char *aPrefName, nsTArray& aFontList); }; diff --git a/mozilla/gfx/thebes/src/gfxFontUtils.cpp b/mozilla/gfx/thebes/src/gfxFontUtils.cpp index 6c3be32e5d8..cca4841f0d2 100644 --- a/mozilla/gfx/thebes/src/gfxFontUtils.cpp +++ b/mozilla/gfx/thebes/src/gfxFontUtils.cpp @@ -38,6 +38,13 @@ #include "gfxFontUtils.h" +#include "nsIPref.h" // for pref handling code +#include "nsServiceManagerUtils.h" + +#include "nsIPrefBranch.h" +#include "nsIPrefService.h" +#include "nsIPrefLocalizedString.h" +#include "nsISupportsPrimitives.h" #define NO_RANGE_FOUND 126 // bit 126 in the font unicode ranges is required to be 0 @@ -412,3 +419,45 @@ PRUint8 gfxFontUtils::CharRangeBit(PRUint32 ch) { return NO_RANGE_FOUND; } +void gfxFontUtils::GetPrefsFontList(const char *aPrefName, nsTArray& aFontList) +{ + const PRUnichar kComma = PRUnichar(','); + + aFontList.Clear(); + + // get the list of single-face font families + nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); + + nsAutoString fontlistValue; + if (prefs) { + nsCOMPtr prefString; + prefs->GetComplexValue(aPrefName, NS_GET_IID(nsISupportsString), getter_AddRefs(prefString)); + if (!prefString) + return; + prefString->GetData(fontlistValue); + } + + // append each font name to the list + nsAutoString fontname; + nsPromiseFlatString fonts(fontlistValue); + const PRUnichar *p, *p_end; + fonts.BeginReading(p); + fonts.EndReading(p_end); + + while (p < p_end) { + const PRUnichar *nameStart = p; + while (++p != p_end && *p != kComma) + /* nothing */ ; + + // pull out a single name and clean out leading/trailing whitespace + fontname = Substring(nameStart, p); + fontname.CompressWhitespace(PR_TRUE, PR_TRUE); + + // append it to the list + aFontList.AppendElement(fontname); + ++p; + } + +} + + diff --git a/mozilla/gfx/thebes/src/gfxQuartzFontCache.h b/mozilla/gfx/thebes/src/gfxQuartzFontCache.h index 7d5137001f2..51b7a9cc634 100644 --- a/mozilla/gfx/thebes/src/gfxQuartzFontCache.h +++ b/mozilla/gfx/thebes/src/gfxQuartzFontCache.h @@ -116,11 +116,17 @@ public: THEBES_INLINE_DECL_REFCOUNTING(MacOSFamilyEntry) MacOSFamilyEntry(nsString &aName) : - mName(aName), mOtherFamilyNamesInitialized(PR_FALSE) - { - } - + mName(aName), mOtherFamilyNamesInitialized(PR_FALSE), mHasOtherFamilyNames(PR_FALSE) + {} + + virtual ~MacOSFamilyEntry() {} + const nsString& Name() { return mName; } + virtual void LocalizedName(nsString& aLocalizedName); + virtual PRBool HasOtherFamilyNames(); + + nsTArray >& GetFontList() { return mAvailableFonts; } + void AddFontEntry(nsRefPtr aFontEntry) { mAvailableFonts.AppendElement(aFontEntry); } @@ -134,7 +140,10 @@ public: void FindFontForChar(FontSearch *aMatchData); // read in other family names, if any, and use functor to add each into cache - void ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor); + virtual void ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor); + + // search for a specific face using the Postscript name + MacOSFontEntry* FindFont(const nsString& aPostscriptName); protected: @@ -150,8 +159,24 @@ protected: nsString mName; // canonical font family name returned from NSFontManager nsTArray > mAvailableFonts; PRPackedBool mOtherFamilyNamesInitialized; + PRPackedBool mHasOtherFamilyNames; }; +// special-case situation where specific faces need to be treated as separate font family +class SingleFaceFamily : public MacOSFamilyEntry +{ +public: + SingleFaceFamily(nsString &aName) : + MacOSFamilyEntry(aName) + {} + + virtual ~SingleFaceFamily() {} + + virtual void LocalizedName(nsString& aLocalizedName); + + // read in other family names, if any, and use functor to add each into cache + virtual void ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor); +}; class gfxQuartzFontCache { public: @@ -211,6 +236,15 @@ private: // separate initialization for reading in name tables, since this is expensive void InitOtherFamilyNames(); + + // special case font faces treated as font families (set via prefs) + void InitSingleFaceList(); + + // commonly used fonts for which the name table should be loaded at startup + void PreloadNamesList(); + + // eliminate faces which have the same ATSUI id + void EliminateDuplicateFaces(const nsAString& aFamilyName); static PLDHashOperator PR_CALLBACK InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey, nsRefPtr& aFamilyEntry, diff --git a/mozilla/gfx/thebes/src/gfxQuartzFontCache.mm b/mozilla/gfx/thebes/src/gfxQuartzFontCache.mm index 81f0611ce23..d6984a297a3 100644 --- a/mozilla/gfx/thebes/src/gfxQuartzFontCache.mm +++ b/mozilla/gfx/thebes/src/gfxQuartzFontCache.mm @@ -256,6 +256,38 @@ public: gfxQuartzFontCache *mFontCache; }; +void MacOSFamilyEntry::LocalizedName(nsString& aLocalizedName) +{ + // no other names ==> only one name, just return it + if (!HasOtherFamilyNames()) { + aLocalizedName = mName; + return; + } + + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + + // dig out the localized family name + NSString *family = GetNSStringForString(mName); + NSString *localizedFamily = [fontManager localizedNameForFamily:family face:nil]; + + if (localizedFamily) { + GetStringForNSString(localizedFamily, aLocalizedName); + } else { + // failed to get a localized name, just use the canonical name + aLocalizedName = mName; + } +} + +PRBool MacOSFamilyEntry::HasOtherFamilyNames() +{ + // need to read in other family names to determine this + if (!mOtherFamilyNamesInitialized) { + AddOtherFamilyNameFunctor addOtherNames(gfxQuartzFontCache::SharedFontCache()); + ReadOtherFamilyNames(addOtherNames); // sets mHasOtherFamilyNames + } + return mHasOtherFamilyNames; +} + static const PRUint32 kTraits_NonNormalWidthMask = NSNarrowFontMask | NSExpandedFontMask | NSCondensedFontMask | NSCompressedFontMask | NSFixedPitchFontMask; @@ -306,18 +338,29 @@ MacOSFamilyEntry::FindFont(const gfxFontStyle* aStyle) return chosenFont; } +MacOSFontEntry* +MacOSFamilyEntry::FindFont(const nsString& aPostscriptName) +{ + // find the font using a simple linear search + PRUint32 numFonts = mAvailableFonts.Length(); + for (PRUint32 i = 0; i < numFonts; i++) { + MacOSFontEntry *fe = mAvailableFonts[i]; + if (fe->Name() == aPostscriptName) + return fe; + } + return nsnull; +} + void MacOSFamilyEntry::FindFontForChar(FontSearch *aMatchData) { - PRUint32 numFonts, i; - // xxx - optimization point - keep a bit vector with the union of supported unicode ranges // by all fonts for this family and bail immediately if the character is not in any of // this family's cmaps // iterate over fonts - numFonts = mAvailableFonts.Length(); - for (i = 0; i < numFonts; i++) { + PRUint32 numFonts = mAvailableFonts.Length(); + for (PRUint32 i = 0; i < numFonts; i++) { MacOSFontEntry *fe = mAvailableFonts[i]; PRInt32 rank = 0; @@ -379,12 +422,11 @@ PRBool MacOSFamilyEntry::FindFontsWithTraits(MacOSFontEntry* aFontsForWeights[], PRUint32 aPosTraitsMask, PRUint32 aNegTraitsMask) { - PRUint32 numFonts, i; PRBool found = PR_FALSE; // iterate over fonts - numFonts = mAvailableFonts.Length(); - for (i = 0; i < numFonts; i++) { + PRUint32 numFonts = mAvailableFonts.Length(); + for (PRUint32 i = 0; i < numFonts; i++) { MacOSFontEntry *fe = mAvailableFonts[i]; // if traits match, add to list of fonts @@ -523,7 +565,7 @@ enum { // returns true if other names were found, false otherwise static PRBool ReadOtherFamilyNamesForFace(AddOtherFamilyNameFunctor& aOtherFamilyFunctor, MacOSFamilyEntry *aFamilyEntry, - NSString *familyName, ATSUFontID fontID) + NSString *familyName, ATSUFontID fontID, bool useFullName = false) { OSStatus err; ItemCount i, nameCount; @@ -554,7 +596,13 @@ static PRBool ReadOtherFamilyNamesForFace(AddOtherFamilyNameFunctor& aOtherFamil if (err != noErr) return foundNames; - if (!(nameCode == kFontFamilyName || nameCode == kMozillaFontPreferredFamilyName)) continue; + if (useFullName) { + if (nameCode != kFontFullName) + continue; + } else { + if (!(nameCode == kFontFamilyName || nameCode == kMozillaFontPreferredFamilyName)) + continue; + } if (len >= kBufLength) continue; buf[len] = 0; @@ -589,12 +637,12 @@ MacOSFamilyEntry::ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFu // read in other family names for the first face in the list MacOSFontEntry *fe = mAvailableFonts[0]; - PRBool foundNames = ReadOtherFamilyNamesForFace(aOtherFamilyFunctor, this, familyName, fe->GetFontID()); + mHasOtherFamilyNames = 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) { + if (mHasOtherFamilyNames) { PRUint32 numFonts, i; // read in names for all faces, needed to catch cases where @@ -607,6 +655,43 @@ MacOSFamilyEntry::ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFu } } +/* SingleFaceFamily */ +#pragma mark- + +void SingleFaceFamily::LocalizedName(nsString& aLocalizedName) +{ + MacOSFontEntry *fontEntry; + + // use the display name of the single face + fontEntry = mAvailableFonts[0]; + if (!fontEntry) + return; + + NSFont *font = [NSFont fontWithName:GetNSStringForString(fontEntry->Name()) size:0.0]; + if (!font) + return; + + NSString *fullname = [font displayName]; + if (fullname) { + GetStringForNSString(fullname, aLocalizedName); + } +} + +void SingleFaceFamily::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]; + + // read in other names, using the full font names as the family names + mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aOtherFamilyFunctor, this, familyName, fe->GetFontID(), true); +} + /* gfxQuartzFontCache */ #pragma mark- @@ -695,16 +780,17 @@ gfxQuartzFontCache::InitFontList() mFontFamilies.Put(availableFamilyName, familyEntry); } + InitSingleFaceList(); + // 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")); + PreloadNamesList(); - // xxx - deal with quirks (e.g. Osaka-Mono) - - // xxx - need to remove bogus Helvetica/Courier italic faces (Cocoa inanity!) + // under 10.4, Cocoa calls report that italic faces exist for Courier and Helvetica, + // even though only bold faces exist so test for this using ATSUI id's (10.5 has proper faces) + EliminateDuplicateFaces(NS_LITERAL_STRING("Courier")); + EliminateDuplicateFaces(NS_LITERAL_STRING("Helvetica")); // initialize ranges of characters for which system-wide font search should be skipped mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls @@ -743,6 +829,122 @@ gfxQuartzFontCache::ReadOtherFamilyNamesForFamily(const nsAString& aFamilyName) } } +void +gfxQuartzFontCache::InitSingleFaceList() +{ + nsAutoTArray singleFaceFonts; + gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts); + + PRUint32 numFonts = singleFaceFonts.Length(); + for (PRUint32 i = 0; i < numFonts; i++) { + nsAutoString availableFamilyName; + + // lookup the font using NSFont + NSString *faceName = GetNSStringForString(singleFaceFonts[i]); + NSFont *font = [NSFont fontWithName:faceName size:0.0]; + if (font) { + NSString *availableFamily = [font familyName]; + GetStringForNSString(availableFamily, availableFamilyName); + + MacOSFamilyEntry *familyEntry = FindFamily(availableFamilyName); + if (familyEntry) { + MacOSFontEntry *fontEntry = familyEntry->FindFont(singleFaceFonts[i]); + if (fontEntry) { + PRBool found; + nsAutoString displayName, key; + + // use the display name the canonical name + NSString *display = [font displayName]; + GetStringForNSString(display, displayName); + GenerateFontListKey(displayName, key); + + // add only if doesn't exist already + if (!mFontFamilies.GetWeak(key, &found)) { + familyEntry = new SingleFaceFamily(displayName); + familyEntry->AddFontEntry(fontEntry); + mFontFamilies.Put(key, familyEntry); + PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit-singleface) family: %s, psname: %s\n", [display UTF8String], [faceName UTF8String])); + } + } + } + } + + } + +} + +void +gfxQuartzFontCache::PreloadNamesList() +{ + nsAutoTArray preloadFonts; + gfxFontUtils::GetPrefsFontList("font.preload-names-list", preloadFonts); + + PRUint32 numFonts = preloadFonts.Length(); + for (PRUint32 i = 0; i < numFonts; i++) { + PRBool found; + nsAutoString key; + GenerateFontListKey(preloadFonts[i], key); + + // only search canonical names! + MacOSFamilyEntry *familyEntry = mFontFamilies.GetWeak(key, &found); + if (familyEntry) { + AddOtherFamilyNameFunctor addOtherNames(this); + familyEntry->ReadOtherFamilyNames(addOtherNames); + } + } + +} + +void +gfxQuartzFontCache::EliminateDuplicateFaces(const nsAString& aFamilyName) +{ + MacOSFamilyEntry *family = FindFamily(aFamilyName); + if (!family) return; + + nsTArray >& fontlist = family->GetFontList(); + + PRUint32 i, bold, numFonts, italicIndex; + MacOSFontEntry *italic, *nonitalic; + PRUint32 boldtraits[2] = { 0, NSBoldFontMask }; + + // if normal and italic have the same ATSUI id, delete italic + // if bold and bold-italic have the same ATSUI id, delete bold-italic + + // two iterations, one for normal, one for bold + for (bold = 0; bold < 2; bold++) { + numFonts = fontlist.Length(); + + // find the non-italic face + nonitalic = nsnull; + for (i = 0; i < numFonts; i++) { + PRUint32 traits = fontlist[i]->Traits(); + if (((traits & NSBoldFontMask) == boldtraits[bold]) && !(traits & NSItalicFontMask)) { + nonitalic = fontlist[i]; + break; + } + } + + // find the italic face + if (nonitalic) { + italic = nsnull; + for (i = 0; i < numFonts; i++) { + PRUint32 traits = fontlist[i]->Traits(); + if (((traits & NSBoldFontMask) == boldtraits[bold]) && (traits & NSItalicFontMask)) { + italic = fontlist[i]; + italicIndex = i; + break; + } + } + + // if italic face and non-italic face have matching ATSUI id's, + // the italic face is bogus so remove it + if (italic && italic->GetFontID() == nonitalic->GetFontID()) { + fontlist.RemoveElementAt(italicIndex); + } + } + } +} + PRBool gfxQuartzFontCache::ResolveFontName(const nsAString& aFontName, nsAString& aResolvedFontName) { @@ -800,7 +1002,10 @@ gfxQuartzFontCache::HashEnumFuncForFamilies(nsStringHashKey::KeyType aKey, void* aUserArg) { FontListData *data = (FontListData*)aUserArg; - data->mListOfFonts.AppendString(aFamilyEntry->Name()); + + nsAutoString localizedFamilyName; + aFamilyEntry->LocalizedName(localizedFamilyName); + data->mListOfFonts.AppendString(localizedFamilyName); return PL_DHASH_NEXT; } @@ -917,7 +1122,6 @@ void gfxQuartzFontCache::AddOtherFamilyName(MacOSFamilyEntry *aFamilyEntry, nsAString& aOtherFamilyName) { nsAutoString key; - MacOSFamilyEntry *familyEntry; PRBool found; GenerateFontListKey(aOtherFamilyName, key); diff --git a/mozilla/modules/libpref/src/init/all.js b/mozilla/modules/libpref/src/init/all.js index 15d92c32d8d..f93c8cf0619 100644 --- a/mozilla/modules/libpref/src/init/all.js +++ b/mozilla/modules/libpref/src/init/all.js @@ -1730,6 +1730,14 @@ pref("font.size.fixed.zh-HK", 16); // Apple's Symbol is Unicode so use it pref("font.mathfont-family", "STIXNonUnicode, STIXSize1, STIXGeneral, Cambria Math, Symbol, DejaVu Sans"); +// individual font faces to be treated as independent families +// names are Postscript names of each face +pref("font.single-face-list", "Osaka-Mono"); + +// optimization hint for fonts with localized names to be read in at startup, otherwise read in at lookup miss +// names are canonical family names (typically English names) +pref("font.preload-names-list", "Hiragino Kaku Gothic Pro,Hiragino Mincho Pro,STSong"); + pref("browser.urlbar.clickAtEndSelects", false); // Override the Windows settings: no menu key, meta accelerator key. ctrl for general access key in HTML/XUL