diff --git a/gfx/thebes/public/gfxPlatformMac.h b/gfx/thebes/public/gfxPlatformMac.h index eb210cfb93e4..81af2d96536b 100644 --- a/gfx/thebes/public/gfxPlatformMac.h +++ b/gfx/thebes/public/gfxPlatformMac.h @@ -61,6 +61,7 @@ public: nsresult GetFontList(const nsACString& aLangGroup, const nsACString& aGenericFamily, nsStringArray& aListOfFonts); + nsresult UpdateFontList(); }; #endif /* GFX_PLATFORM_MAC_H */ diff --git a/gfx/thebes/src/gfxAtsuiFonts.cpp b/gfx/thebes/src/gfxAtsuiFonts.cpp index 0d1c8bb8b665..70c92b36109a 100644 --- a/gfx/thebes/src/gfxAtsuiFonts.cpp +++ b/gfx/thebes/src/gfxAtsuiFonts.cpp @@ -139,9 +139,10 @@ gfxAtsuiFont::gfxAtsuiFont(ATSUFontID fontID, mMetrics.xHeight = atsMetrics.xHeight * size; if (atsMetrics.avgAdvanceWidth != 0.0) - mMetrics.aveCharWidth = atsMetrics.avgAdvanceWidth * size; + mMetrics.aveCharWidth = + PR_MIN(atsMetrics.avgAdvanceWidth * size, GetCharWidth('x')); else - mMetrics.aveCharWidth = GetCharWidth('a'); + mMetrics.aveCharWidth = GetCharWidth('x'); mMetrics.underlineOffset = atsMetrics.underlinePosition * size; mMetrics.underlineSize = atsMetrics.underlineThickness * size; diff --git a/gfx/thebes/src/gfxPlatformMac.cpp b/gfx/thebes/src/gfxPlatformMac.cpp index f7a61670af14..1d37b6b4294c 100644 --- a/gfx/thebes/src/gfxPlatformMac.cpp +++ b/gfx/thebes/src/gfxPlatformMac.cpp @@ -141,8 +141,13 @@ gfxPlatformMac::ResolveFontName(const nsAString& aFontName, FontResolverCallback aCallback, void *aClosure, PRBool& aAborted) { - // XXX Implement me with GetFontList! - aAborted = !(*aCallback)(aFontName, aClosure); + nsAutoString resolvedName; + if (!gfxQuartzFontCache::SharedFontCache()-> + ResolveFontName(aFontName, resolvedName)) { + aAborted = PR_FALSE; + return NS_OK; + } + aAborted = !(*aCallback)(resolvedName, aClosure); return NS_OK; } @@ -151,6 +156,14 @@ gfxPlatformMac::GetFontList(const nsACString& aLangGroup, const nsACString& aGenericFamily, nsStringArray& aListOfFonts) { - gfxQuartzFontCache::SharedFontCache()->GetFontList(aLangGroup, aGenericFamily, aListOfFonts); + gfxQuartzFontCache::SharedFontCache()-> + GetFontList(aLangGroup, aGenericFamily, aListOfFonts); return NS_OK; } + +nsresult +gfxPlatformMac::UpdateFontList() +{ + gfxQuartzFontCache::SharedFontCache()->UpdateFontList(); + return NS_OK; +} \ No newline at end of file diff --git a/gfx/thebes/src/gfxQuartzFontCache.h b/gfx/thebes/src/gfxQuartzFontCache.h index 271a53abd8c5..537e45bac8f7 100644 --- a/gfx/thebes/src/gfxQuartzFontCache.h +++ b/gfx/thebes/src/gfxQuartzFontCache.h @@ -20,6 +20,7 @@ * * Contributor(s): * Vladimir Vukicevic + * Masayuki Nakano * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -45,6 +46,55 @@ #include "nsUnicharUtils.h" #include "nsVoidArray.h" +class NSFontManager; +class NSString; +class NSFont; + +class FamilyEntry +{ +public: + THEBES_INLINE_DECL_REFCOUNTING(FamilyEntry) + + FamilyEntry(nsString &aName) : + mName(aName) + { + } + + const nsString& Name() { return mName; } +protected: + nsString mName; + // XXX we need to add the variables for generic family and lang group. +}; + +class FontEntry +{ +public: + THEBES_INLINE_DECL_REFCOUNTING(FontEntry) + + FontEntry(nsString &aName) : + mName(aName) + { + Init(); + } + + const nsString& Name() { return mName; } + PRInt32 Weight() { return mWeight; } + PRUint32 Traits() { return mTraits; } + PRBool IsFixedPitch(); + PRBool IsItalicStyle(); + PRBool IsBold(); + NSFont* GetNSFont(float aSize); + +protected: + void Init(); + void GetStringForNSString(const NSString *aSrc, nsAString& aDist); + NSString* GetNSStringForString(const nsAString& aSrc); + + nsString mName; + PRInt32 mWeight; + PRUint32 mTraits; +}; + class gfxQuartzFontCache { public: static gfxQuartzFontCache* SharedFontCache() { @@ -61,11 +111,35 @@ public: void GetFontList (const nsACString& aLangGroup, const nsACString& aGenericFamily, nsStringArray& aListOfFonts); + PRBool ResolveFontName(const nsAString& aFontName, + nsAString& aResolvedFontName); + void UpdateFontList() { InitFontList(); } private: static gfxQuartzFontCache *sSharedFontCache; gfxQuartzFontCache(); + void InitFontList(); + PRBool AppendFontFamily(NSFontManager *aFontManager, + NSString *aName, PRBool aNameIsPostscriptName); + NSFont* FindFontWeight(NSFontManager *aFontManager, + FontEntry *aOriginalFont, + NSFont *aFont, + const gfxFontStyle *aStyle); + NSFont* FindAnotherWeightMemberFont(NSFontManager *aFontManager, + FontEntry *aOriginalFont, + NSFont *aFont, + const gfxFontStyle *aStyle, + PRBool aBolder); + void GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult); + static void ATSNotification(ATSFontNotificationInfoRef aInfo, + void* aUserArg); + + static PLDHashOperator PR_CALLBACK + HashEnumFuncForFamilies(nsStringHashKey::KeyType aKey, + nsRefPtr& aFamilyEntry, + void* aUserArg); + ATSUFontID FindFromSystem (const nsAString& aFamily, const gfxFontStyle* aStyle); @@ -111,6 +185,9 @@ private: }; nsDataHashtable mCache; + nsDataHashtable > mFamilies; + nsDataHashtable > mPostscriptFonts; + nsDataHashtable mAllFontNames; }; #endif /* GFXQUARTZFONTCACHE_H_ */ diff --git a/gfx/thebes/src/gfxQuartzFontCache.mm b/gfx/thebes/src/gfxQuartzFontCache.mm index 596b78d56108..b5001a5622bd 100644 --- a/gfx/thebes/src/gfxQuartzFontCache.mm +++ b/gfx/thebes/src/gfxQuartzFontCache.mm @@ -6,6 +6,7 @@ * * Contributor(s): * Vladimir Vukicevic + * Masayuki Nakano * * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. * @@ -47,17 +48,7 @@ - (ATSUFontID)_atsFontID; @end -gfxQuartzFontCache *gfxQuartzFontCache::sSharedFontCache = nsnull; - -gfxQuartzFontCache::gfxQuartzFontCache() { - mCache.Init(); -} - -#define SYNTHESIZED_FONT_TRAITS (NSBoldFontMask | NSItalicFontMask) - - -#define IMPORTANT_FONT_TRAITS (0 \ - | NSBoldFontMask \ +#define NON_WEIGHT_TRAITS_MASK (0 \ | NSCompressedFontMask \ | NSCondensedFontMask \ | NSExpandedFontMask \ @@ -65,8 +56,13 @@ gfxQuartzFontCache::gfxQuartzFontCache() { | NSNarrowFontMask \ | NSPosterFontMask \ | NSSmallCapsFontMask \ + | NSFixedPitchFontMask \ ) +#define IMPORTANT_TRAITS_MASK (NON_WEIGHT_TRAITS_MASK | NSBoldFontMask) + +#define SAME_TRAITS(a,b,mask) (((a) & mask) == ((b) & mask)) + #define DESIRED_WEIGHT 5 #define APPLE_BOLD_WEIGHT 9 @@ -79,6 +75,281 @@ gfxQuartzFontCache::gfxQuartzFontCache() { #define INDEX_FONT_WEIGHT 2 #define INDEX_FONT_TRAITS 3 +/* FontEntry */ + +NSFont* +FontEntry::GetNSFont(float aSize) +{ + NSString *name = GetNSStringForString(mName); + return [NSFont fontWithName:name size:aSize]; +} + +PRBool +FontEntry::IsFixedPitch() +{ + return mTraits & NSFixedPitchFontMask ? PR_TRUE : PR_FALSE; +} + +PRBool +FontEntry::IsItalicStyle() +{ + return mTraits & NSItalicFontMask ? PR_TRUE : PR_FALSE; +} + +PRBool +FontEntry::IsBold() +{ + return mWeight >= APPLE_BOLD_WEIGHT ? PR_TRUE : PR_FALSE; +} + +void +FontEntry::Init() +{ + NSFont *font = GetNSFont(10.0); + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + mWeight = [fontManager weightOfFont:font]; + mTraits = [fontManager traitsOfFont:font]; +} + +void +FontEntry::GetStringForNSString(const NSString *aSrc, nsAString& aDist) +{ + aDist.SetLength([aSrc length]); + [aSrc getCharacters:aDist.BeginWriting()]; +} + +NSString* +FontEntry::GetNSStringForString(const nsAString& aSrc) +{ + return [NSString stringWithCharacters:aSrc.BeginReading() + length:aSrc.Length()]; +} + +/* gfxQuartzFontCache */ + +gfxQuartzFontCache *gfxQuartzFontCache::sSharedFontCache = nsnull; + +gfxQuartzFontCache::gfxQuartzFontCache() +{ + mCache.Init(); + mFamilies.Init(30); + mPostscriptFonts.Init(60); + mAllFontNames.Init(100); + InitFontList(); + ::ATSFontNotificationSubscribe(ATSNotification, + kATSFontNotifyOptionDefault, + (void*)this, nsnull); +} + +void +gfxQuartzFontCache::GenerateFontListKey(const nsAString& aKeyName, + nsAString& aResult) +{ + aResult = aKeyName; + ToLowerCase(aResult); +} + +static TextEncoding sUTF8Encoding = + ::CreateTextEncoding(kTextEncodingUnicodeDefault, + kTextEncodingDefaultVariant, + kUnicodeUTF8Format); + +PRBool +gfxQuartzFontCache::AppendFontFamily(NSFontManager *aFontManager, + NSString *aName, + PRBool aNameIsPostscriptName) +{ + NSString *name; + if (aNameIsPostscriptName) { + // we should use display name that is localized the family name and + // extra name. (e.g., "mono") + NSFont *font = [NSFont fontWithName:aName size:10.0]; + name = [font displayName]; + } else { + // we should use localzed simple family name. + name = [aFontManager localizedNameForFamily:aName face:nil]; + } + nsAutoString str; + str.SetLength([name length]); + [name getCharacters:str.BeginWriting()]; + nsAutoString key; + GenerateFontListKey(str, key); + nsRefPtr fe; + if (mFamilies.Get(key, &fe)) + return PR_FALSE; + fe = new FamilyEntry(str); + mFamilies.Put(key, fe); + return PR_TRUE; +} + +void +gfxQuartzFontCache::InitFontList() +{ + mCache.Clear(); + mFamilies.Clear(); + mPostscriptFonts.Clear(); + mAllFontNames.Clear(); + + nsAutoString key; + + // We should use cocoa for listing up the font families. + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + NSArray *families = [fontManager availableFontFamilies]; + PRUint32 familiesCount = [families count]; + for (PRUint32 i = 0; i < familiesCount; i++) { + NSString *name = [families objectAtIndex:i]; + if (!AppendFontFamily(fontManager, name, PR_FALSE)) + continue; + // If this family has both variable pitch font and + // fixed pitch font, we should append the font to family. + // Because CSS cannot specify the pitch in the same family. + NSFont *font = [NSFont fontWithName:name size:10.0]; + PRBool isFixedPitch = + [fontManager traitsOfFont:font] & NSFixedPitchFontMask ? PR_TRUE : + PR_FALSE; + NSArray *members = [fontManager availableMembersOfFontFamily:name]; + PRUint32 membersCount = [members count]; + for (PRUint32 j = 0; j < membersCount; j++) { + NSArray *member = [members objectAtIndex:j]; + PRUint32 newTraits = + [[member objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue]; + if ((isFixedPitch && !(newTraits & NSFixedPitchFontMask)) || + (!isFixedPitch && (newTraits & NSFixedPitchFontMask))) { + AppendFontFamily(fontManager, + [member objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME], PR_TRUE); + break; + } + } + } + + // We should cache for all alias for all families. + // We will use this for resolving the font names to actual fonts. + // We should use ATSUI for this, because cocoa cannot show the all aliases. + ItemCount fontCount; + OSStatus err = ::ATSUFontCount(&fontCount); + if (err != noErr) + return; + ATSUFontID fontIDs[fontCount]; + ItemCount arraySize = fontCount; + err = ::ATSUGetFontIDs(fontIDs, arraySize, &fontCount); + if (err != noErr) + return; + for (ItemCount i = 0; i < fontCount; i++) { + struct FontNames { + FontNames() { + mShouldProgress = PR_FALSE; + } + nsStringArray mNames; + nsString mPostscriptName; + PRBool mShouldProgress; + }; + FontNames names; + + ItemCount nameCount; + err = ::ATSUCountFontNames(fontIDs[i], &nameCount); + if (err != noErr || nameCount == 0) + continue; + for (ItemCount j = 0; j < nameCount; j++) { + ByteCount len; + err = ::ATSUGetIndFontName(fontIDs[i], j, 0, nsnull, &len, + nsnull, nsnull, nsnull, nsnull); + if (err != noErr || len == 0) + continue; + char buf[len]; + FontNameCode nameCode; + FontPlatformCode platformCode; + FontScriptCode scriptCode; + FontLanguageCode langCode; + err = ::ATSUGetIndFontName(fontIDs[i], j, len, + buf, &len, + &nameCode, &platformCode, + &scriptCode, &langCode); + if (err != noErr || len == 0) + continue; + switch (nameCode) { + case kFontFamilyName: + case kFontFullName: + case kFontPostscriptName: + break; + default: + continue; + } + nsAutoString fontName; + if (platformCode == kFontMacintoshPlatform) { + CFStringEncoding encoding; + err = ::GetTextEncodingFromScriptInfo(scriptCode, langCode, + kTextRegionDontCare, + &encoding); + if (err != noErr) + continue; + CFStringRef cfName = + ::CFStringCreateWithBytes(nsnull, (UInt8*)buf, len, + encoding, false); + fontName.Assign(::CFStringGetCharactersPtr(cfName), + ::CFStringGetLength(cfName)); + // If the font name is not east asian name, + // the fontName is empty. We should retry with UTF8 string. + // Don't swap this order. + if (fontName.IsEmpty()) { + nsCAutoString cName(::CFStringGetCStringPtr(cfName, + sUTF8Encoding)); + fontName = NS_ConvertUTF8toUTF16(cName); + } + ::CFRelease(cfName); + } else if (platformCode == kFontUnicodePlatform || + platformCode == kFontMicrosoftPlatform) { + fontName.Assign((PRUnichar*)buf, len / sizeof(PRUnichar)); + } else { + continue; + } + + if (fontName[0] == PRUnichar('.') || + fontName[0] == PRUnichar('%')) { + // We should ignore this font. + names.mShouldProgress = PR_FALSE; + break; + } + names.mShouldProgress = PR_TRUE; + + switch (nameCode) { + case kFontFamilyName: + case kFontFullName: + names.mNames.AppendString(fontName); + break; + case kFontPostscriptName: + NS_ASSERTION(names.mPostscriptName.IsEmpty(), + "A font id has two or more postscript name!"); + names.mPostscriptName = fontName; + break; + } + } + + if (!names.mShouldProgress) + continue; + if (names.mPostscriptName.IsEmpty()) + continue; + nsRefPtr psFont = new FontEntry(names.mPostscriptName); + GenerateFontListKey(names.mPostscriptName, key); + mPostscriptFonts.Put(key, psFont); + + for (PRInt32 i = 0; i < names.mNames.Count(); i++) { + GenerateFontListKey(*names.mNames[i], key); + nsAutoString str; + if (mAllFontNames.Get(key, &str)) + continue; + mAllFontNames.Put(key, psFont->Name()); + } + } +} + +void +gfxQuartzFontCache::ATSNotification(ATSFontNotificationInfoRef aInfo, + void* aUserArg) +{ + gfxQuartzFontCache *qfc = (gfxQuartzFontCache*)aUserArg; + qfc->UpdateFontList(); +} + // see docs for NSFontManager-availableMembersOfFontFamily: static const int CSSWeightToAppleWeight[] = { // 0 invalid @@ -94,6 +365,24 @@ static const int CSSWeightToAppleWeight[] = { 12 }; +static const int AppleWeightToCSSWeight[] = { + 0, + 1, // 1. + 1, // 2. W1, ultralight + 2, // 3. W2, extralight + 3, // 4. W3, light + 4, // 5. W4, semilight + 5, // 6. W5, medium + 6, // 7. + 6, // 8. W6, semibold + 7, // 9. W7, bold + 8, // 10. W8, extrabold + 8, // 11. + 9, // 12. W9, ultrabold + 9, // 13 + 9 // 14 +}; + // from the given CSS Weight, how many steps do we need to take in Apple Weights // to get to the next CSS weight static const int AppleWeightStepsToNextCSSWeight[] = { @@ -110,112 +399,121 @@ static const int AppleWeightStepsToNextCSSWeight[] = { 2 }; -/* - * Return PR_TRUE if the candidate font is acceptable for - * the given desired traits. - */ -static PRBool -acceptableChoice (NSFontTraitMask desiredTraits, int desiredWeight, - NSFontTraitMask candidateTraits, int candidateWeight) +NSFont* +gfxQuartzFontCache::FindFontWeight(NSFontManager *aFontManager, + FontEntry *aOriginalFont, + NSFont *aCurrentFont, + const gfxFontStyle *aStyle) { - // remove the traits we can synthesize, and compare the others - desiredTraits &= ~SYNTHESIZED_FONT_TRAITS; - return (candidateTraits & desiredTraits) == desiredTraits; -} + // Assume that the weight of gfxFontStyle is a relative weight for the + // specified font, e.g., if a font has three font they are light, normal, + // and bold, when the light font is specified, we should use normal font + // for the bold style of light font insted of the bold font. + PRInt8 baseCSSWeight, weightOffset; + aStyle->ComputeWeightAndOffset(&baseCSSWeight, &weightOffset); + PRInt32 fontBaseCSSWeight = + PR_MIN(PR_MAX(AppleWeightToCSSWeight[aOriginalFont->Weight()], 1), 9); + PRInt32 desiredCSSWeight = + PR_MIN(PR_MAX(fontBaseCSSWeight - CSS_NORMAL_WEIGHT_BASE + baseCSSWeight, 1), 9); + PRInt32 desiredWeight = CSSWeightToAppleWeight[desiredCSSWeight]; -/* - * Return PR_TRUE if the candidate font is better than the chosen font, - * for the given desired traits - */ -static PRBool -betterChoice(NSFontTraitMask desiredTraits, int desiredWeight, - NSFontTraitMask chosenTraits, int chosenWeight, - NSFontTraitMask candidateTraits, int candidateWeight) -{ - if (!acceptableChoice(desiredTraits, desiredWeight, candidateTraits, candidateWeight)) - return PR_FALSE; + PRInt32 currentWeight = [aFontManager weightOfFont:aCurrentFont]; + if (currentWeight == desiredWeight && weightOffset == 0) + return aCurrentFont; - // A list of the traits we care about. - // The top item in the list is the worst trait to mismatch; if a font has this - // and we didn't ask for it, we'd prefer any other font in the family. - const NSFontTraitMask masks[] = { - NSPosterFontMask, - NSSmallCapsFontMask, - NSItalicFontMask, - NSCompressedFontMask, - NSCondensedFontMask, - NSExpandedFontMask, - NSNarrowFontMask, - NSBoldFontMask, - 0 }; - - int i = 0; - NSFontTraitMask mask; - while ((mask = masks[i++])) { - BOOL desired = (desiredTraits & mask) != 0; - BOOL chosenHasUnwantedTrait = (desired != ((chosenTraits & mask) != 0)); - BOOL candidateHasUnwantedTrait = (desired != ((candidateTraits & mask) != 0)); - if (!candidateHasUnwantedTrait && chosenHasUnwantedTrait) - return PR_TRUE; - if (!chosenHasUnwantedTrait && candidateHasUnwantedTrait) - return PR_FALSE; + PRUint32 traits = [aFontManager traitsOfFont:aCurrentFont]; + NSFont *newFont = aCurrentFont; + if (currentWeight != desiredWeight) { + NSFont *newFont = [aFontManager fontWithFamily:[aCurrentFont familyName] + traits:traits weight:desiredWeight + size:aStyle->size]; + if (!SAME_TRAITS(traits, [aFontManager traitsOfFont:newFont], + NON_WEIGHT_TRAITS_MASK)) + newFont = aCurrentFont; } - int chosenWeightDelta = chosenWeight - desiredWeight; - int candidateWeightDelta = candidateWeight - desiredWeight; + newFont = aCurrentFont; + if (weightOffset < 0) { + while (weightOffset != 0 && + [aFontManager weightOfFont:newFont] > desiredWeight) { + NSFont *font = [aFontManager convertWeight:NO ofFont:newFont]; + if (newFont == font) + break; + if (!SAME_TRAITS(traits, [aFontManager traitsOfFont:font], + NON_WEIGHT_TRAITS_MASK)) + break; + newFont = font; + weightOffset++; + } + } else if (weightOffset > 0) { + while (weightOffset != 0 && + [aFontManager weightOfFont:newFont] < desiredWeight) { + NSFont *font = [aFontManager convertWeight:YES ofFont:newFont]; + if (newFont == font) + break; + if (!SAME_TRAITS(traits, [aFontManager traitsOfFont:font], + NON_WEIGHT_TRAITS_MASK)) + break; + newFont = font; + weightOffset--; + } + } - int chosenWeightDeltaMagnitude = ABS(chosenWeightDelta); - int candidateWeightDeltaMagnitude = ABS(candidateWeightDelta); + // The last resort. Some fonts cannot be bold by "font-weight: bold;". + // E.g., "Hiragino Kaku Gothic Pro". + // Maybe, we can remove this, if cairo supports the bold and italic font + // dinamic generating. + if (aStyle->weight < CSS_NORMAL_WEIGHT_BASE && + [aFontManager weightOfFont:newFont] >= aOriginalFont->Weight()) { + newFont = FindAnotherWeightMemberFont(aFontManager, aOriginalFont, + aCurrentFont, aStyle, PR_FALSE); + } else if (aStyle->weight >= CSS_BOLD_WEIGHT_BASE && + [aFontManager weightOfFont:newFont] <= aOriginalFont->Weight()) { + newFont = FindAnotherWeightMemberFont(aFontManager, aOriginalFont, + aCurrentFont, aStyle, PR_TRUE); + } - // Smaller magnitude wins. - // If both have same magnitude, tie breaker is that the smaller weight wins. - // Otherwise, first font in the array wins (should almost never happen). - if (candidateWeightDeltaMagnitude < chosenWeightDeltaMagnitude) - return PR_TRUE; - - if (candidateWeightDeltaMagnitude == chosenWeightDeltaMagnitude && candidateWeight < chosenWeight) - return PR_TRUE; - - return PR_FALSE; + return newFont; } NSFont* -FindFontWeight (NSFontManager *fontManager, NSFont *font, int desiredWeight, int weightSteps, float size) +gfxQuartzFontCache::FindAnotherWeightMemberFont(NSFontManager *aFontManager, + FontEntry *aOriginalFont, + NSFont *aCurrentFont, + const gfxFontStyle *aStyle, + PRBool aBolder) { - NSFont *newFont; + NSArray *fonts = + [aFontManager availableMembersOfFontFamily:[aCurrentFont familyName]]; + PRUint32 count = [fonts count]; + PRInt32 baseWeight = aOriginalFont->Weight(); + PRUint32 baseTraits = [aFontManager traitsOfFont:aCurrentFont]; - int currentWeight = [fontManager weightOfFont:font]; - if (currentWeight != desiredWeight) { - //fprintf (stderr, "desiredWeight: %d currentWeight: %d\n", desiredWeight, currentWeight); - newFont = [fontManager fontWithFamily:[font familyName] traits:[fontManager traitsOfFont:font] weight:desiredWeight size:size]; - if (newFont) { - font = newFont; - currentWeight = [fontManager weightOfFont:font]; - //fprintf (stderr, "picked new font, weight: %d\n", currentWeight); + PRInt32 newWeight = 0; + NSString *newFont; + for (PRUint32 i = 0; i < count; i++) { + NSArray *member = [fonts objectAtIndex:i]; + PRInt32 weight = [[member objectAtIndex:INDEX_FONT_WEIGHT] intValue]; + if ((aBolder && weight <= baseWeight) || (!aBolder && weight >= baseWeight)) + continue; + + PRUint32 traits = + [[member objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue]; + if (!SAME_TRAITS(traits, baseTraits, NON_WEIGHT_TRAITS_MASK)) + continue; + + // we should use lightest weight font at finding the bolder font, + // otherwise, we should use boldest weight font. + if (!newWeight || + (aBolder && newWeight > weight) || + (!aBolder && newWeight < weight)) { + newFont = [member objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME]; + newWeight = weight; } } - - if (weightSteps == 0) { - return font; - } else if (weightSteps < 0) { - while (weightSteps != 0) { - newFont = [fontManager convertWeight:NO ofFont:font]; - if (newFont == font) - return font; - font = newFont; - weightSteps++; - } - } else if (weightSteps > 0) { - while (weightSteps != 0) { - newFont = [fontManager convertWeight:YES ofFont:font]; - if (newFont == font) - return font; - font = newFont; - weightSteps--; - } - } - - return font; + if (!newWeight) + return aCurrentFont; + return [NSFont fontWithName:newFont size:aStyle->size]; } ATSUFontID @@ -243,145 +541,44 @@ ATSUFontID gfxQuartzFontCache::FindFromSystem (const nsAString& aFamily, const gfxFontStyle *aStyle) { - NSString *desiredFamily = [NSString stringWithCharacters:aFamily.BeginReading() length:aFamily.Length()]; - NSFontTraitMask desiredTraits = 0; - int desiredWeight; - PRInt8 baseCSSWeight, weightOffset; - - //printf ("FindATSUFontIDForFamilyAndStyle: %s\n", [desiredFamily cString]); - - aStyle->ComputeWeightAndOffset(&baseCSSWeight, &weightOffset); - desiredWeight = CSSWeightToAppleWeight[PR_MIN(PR_MAX(baseCSSWeight, 1), 9)]; + nsAutoString key, fontName; + GenerateFontListKey(aFamily, key); + if (!mAllFontNames.Get(key, &fontName)) + return kATSUInvalidFontID; + nsRefPtr fe; + GenerateFontListKey(fontName, key); + if (!mPostscriptFonts.Get(key, &fe)) + return kATSUInvalidFontID; + NSFont *font = fe->GetNSFont(aStyle->size); + PRUint32 desiredTraits = 0; // Oblique should really be synthesized italic; fix that later. - if (aStyle->style & FONT_STYLE_ITALIC || aStyle->style & FONT_STYLE_OBLIQUE) + if (fe->IsItalicStyle() || aStyle->style & FONT_STYLE_ITALIC || + aStyle->style & FONT_STYLE_OBLIQUE) desiredTraits |= NSItalicFontMask; - // we should really do the right thing with the offsets here, but that's harder. - if (desiredWeight >= APPLE_BOLD_WEIGHT) - desiredTraits |= NSBoldFontMask; + if (fe->IsFixedPitch()) + desiredTraits |= NSFixedPitchFontMask; NSFontManager *fontManager = [NSFontManager sharedFontManager]; - NSFont *font = nsnull; - // Look for an exact match first. - NSEnumerator *availableFonts = [[fontManager availableFonts] objectEnumerator]; - NSString *availableFont; - while ((availableFont = [availableFonts nextObject])) { - if ([desiredFamily caseInsensitiveCompare:availableFont] == NSOrderedSame) { - NSFont *nameMatchedFont = [NSFont fontWithName:availableFont size:aStyle->size]; - NSFontTraitMask traits = [fontManager traitsOfFont:nameMatchedFont]; - - if ((traits & desiredTraits) == desiredTraits) { - font = [fontManager convertFont:nameMatchedFont toHaveTrait:desiredTraits]; - font = FindFontWeight (fontManager, font, desiredWeight, weightOffset, aStyle->size); - //fprintf (stderr, "Exact match found; weight: %d\n", [fontManager weightOfFont:font]); - return [font _atsFontID]; - } + NSFont *newFont = font; + while (desiredTraits) { + newFont = [fontManager convertFont:font toHaveTrait:desiredTraits]; + PRInt32 newTraits = [fontManager traitsOfFont:newFont]; + if (newTraits & desiredTraits == desiredTraits) break; - } - } - // Do a simple case insensitive search for a matching font family. - // NSFontManager requires exact name matches. - // This addresses the problem of matching arial to Arial, etc., but perhaps not all the issues. - NSEnumerator *e = [[fontManager availableFontFamilies] objectEnumerator]; - NSString *availableFamily; - while ((availableFamily = [e nextObject])) { - if ([desiredFamily caseInsensitiveCompare:availableFamily] == NSOrderedSame) { - break; - } - } + newFont = font; - // If we can't find a match for this at all, then bail - if (availableFamily == nsnull) - return kATSUInvalidFontID; - - // Found a family, now figure out what weight and traits to use. - BOOL choseFont = false; - int chosenWeight = 0; - NSFontTraitMask chosenTraits = 0; - - NSArray *fonts = [fontManager availableMembersOfFontFamily:availableFamily]; - unsigned n = [fonts count]; - unsigned i; - for (i = 0; i < n; i++) { - NSArray *fontInfo = [fonts objectAtIndex:i]; - - // Array indices must be hard coded because of lame AppKit API. - int fontWeight = [[fontInfo objectAtIndex:INDEX_FONT_WEIGHT] intValue]; - NSFontTraitMask fontTraits = [[fontInfo objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue]; - - BOOL newWinner; - if (!choseFont) - newWinner = acceptableChoice(desiredTraits, desiredWeight, fontTraits, fontWeight); + if (desiredTraits & NSItalicFontMask) + desiredTraits &= ~NSItalicFontMask; else - newWinner = betterChoice(desiredTraits, desiredWeight, chosenTraits, chosenWeight, fontTraits, fontWeight); - - if (newWinner) { - choseFont = YES; - chosenWeight = fontWeight; - chosenTraits = fontTraits; - - if (chosenWeight == desiredWeight && - (chosenTraits & IMPORTANT_FONT_TRAITS) == (desiredTraits & IMPORTANT_FONT_TRAITS)) - { - // this one is good enough; don't bother looking for something better - break; - } - } + break; } - - // we couldn't find anything that was valid in the family - if (!choseFont) - return kATSUInvalidFontID; - - // grab the actual font - font = [fontManager fontWithFamily:availableFamily traits:chosenTraits weight:chosenWeight size:aStyle->size]; - if (!font) - return kATSUInvalidFontID; - - //fprintf (stderr, "No exact match found; basic match weight: %d\n", [fontManager weightOfFont:font]); - font = FindFontWeight (fontManager, font, desiredWeight, weightOffset, aStyle->size); - //fprintf (stderr, " after FindFontWeight: %d\n", [fontManager weightOfFont:font]); - chosenWeight = [fontManager weightOfFont:font]; - - // Figure out whether we need to synthesize - NSFontTraitMask actualTraits = 0; - if (desiredTraits & (NSItalicFontMask | NSBoldFontMask)) - actualTraits = [fontManager traitsOfFont:font]; - - bool syntheticBold = (baseCSSWeight >= CSS_BOLD_WEIGHT_BASE && weightOffset <= 0) && (chosenWeight < APPLE_BOLD_WEIGHT); - bool syntheticOblique = (desiredTraits & NSItalicFontMask) && !(actualTraits & NSItalicFontMask); - - // There are some malformed fonts that will be correctly returned - // by -fontWithFamily:traits:weight:size: as a match for a - // particular trait, though -[NSFontManager traitsOfFont:] - // incorrectly claims the font does not have the specified - // trait. This could result in applying synthetic bold on top of - // an already-bold font, as reported in - // . To work - // around this problem, if we got an apparent exact match, but the - // requested traits aren't present in the matched font, we'll try - // to get a font from the same family without those traits (to - // apply the synthetic traits to later). - NSFontTraitMask nonSyntheticTraits = desiredTraits; - - if (syntheticBold) - nonSyntheticTraits &= ~NSBoldFontMask; - - if (syntheticOblique) - nonSyntheticTraits &= ~NSItalicFontMask; - - if (nonSyntheticTraits != desiredTraits) { - NSFont *fontWithoutSyntheticTraits = [fontManager fontWithFamily:availableFamily traits:nonSyntheticTraits weight:chosenWeight size:aStyle->size]; - if (fontWithoutSyntheticTraits) - font = fontWithoutSyntheticTraits; - } - - //printf ("Font: %s -> %d\n", [availableFamily cString], GetNSFontATSUFontId(font)); - return [font _atsFontID]; + newFont = FindFontWeight(fontManager, fe, newFont, aStyle); + return [newFont _atsFontID]; } ATSUFontID @@ -390,23 +587,48 @@ gfxQuartzFontCache::GetDefaultATSUFontID (const gfxFontStyle* aStyle) return [[NSFont userFontOfSize:aStyle->size] _atsFontID]; } +struct FontListData { + FontListData(const nsACString& aLangGroup, + const nsACString& aGenericFamily, + nsStringArray& aListOfFonts) : + mLangGroup(aLangGroup), mGenericFamily(aGenericFamily), + mListOfFonts(aListOfFonts) {} + const nsACString& mLangGroup; + const nsACString& mGenericFamily; + nsStringArray& mListOfFonts; +}; + +PLDHashOperator PR_CALLBACK +gfxQuartzFontCache::HashEnumFuncForFamilies(nsStringHashKey::KeyType aKey, + nsRefPtr& aFamilyEntry, + void* aUserArg) +{ + FontListData *data = (FontListData*)aUserArg; + data->mListOfFonts.AppendString(aFamilyEntry->Name()); + return PL_DHASH_NEXT; +} + void gfxQuartzFontCache::GetFontList (const nsACString& aLangGroup, const nsACString& aGenericFamily, nsStringArray& aListOfFonts) { - NSFontManager *fontManager = [NSFontManager sharedFontManager]; - NSArray *familyArray = [fontManager availableFontFamilies]; + FontListData data(aLangGroup, aGenericFamily, aListOfFonts); - unsigned int nFamilies = [familyArray count]; - for (unsigned int i = 0; i < nFamilies; i++) { - NSString *family = [familyArray objectAtIndex:i]; - nsString str; - str.SetLength([family length]); - [family getCharacters:(str.BeginWriting())]; - aListOfFonts.AppendString(str); - } + mFamilies.Enumerate(gfxQuartzFontCache::HashEnumFuncForFamilies, &data); aListOfFonts.Sort(); + aListOfFonts.Compact(); } +PRBool +gfxQuartzFontCache::ResolveFontName(const nsAString& aFontName, + nsAString& aResolvedFontName) +{ + nsAutoString name, key; + GenerateFontListKey(aFontName, key); + if (!mAllFontNames.Get(key, &name)) + return PR_FALSE; + aResolvedFontName = name; + return PR_TRUE; +}