diff options
Diffstat (limited to 'WebCore/platform/mac/WebFontCache.mm')
-rw-r--r-- | WebCore/platform/mac/WebFontCache.mm | 194 |
1 files changed, 131 insertions, 63 deletions
diff --git a/WebCore/platform/mac/WebFontCache.mm b/WebCore/platform/mac/WebFontCache.mm index 1fcceb5..84f60b5 100644 --- a/WebCore/platform/mac/WebFontCache.mm +++ b/WebCore/platform/mac/WebFontCache.mm @@ -1,5 +1,6 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,14 +28,20 @@ */ #import "config.h" +#import "FontTraitsMask.h" #import "WebFontCache.h" #import <math.h> +using namespace WebCore; + +#ifdef BUILDING_ON_TIGER +typedef int NSInteger; +#endif + #define SYNTHESIZED_FONT_TRAITS (NSBoldFontMask | NSItalicFontMask) #define IMPORTANT_FONT_TRAITS (0 \ - | NSBoldFontMask \ | NSCompressedFontMask \ | NSCondensedFontMask \ | NSExpandedFontMask \ @@ -44,10 +51,7 @@ | NSSmallCapsFontMask \ ) -#define DESIRED_WEIGHT 5 - -static BOOL acceptableChoice(NSFontTraitMask desiredTraits, int desiredWeight, - NSFontTraitMask candidateTraits, int candidateWeight) +static BOOL acceptableChoice(NSFontTraitMask desiredTraits, int desiredWeight, NSFontTraitMask candidateTraits, int candidateWeight) { desiredTraits &= ~SYNTHESIZED_FONT_TRAITS; return (candidateTraits & desiredTraits) == desiredTraits; @@ -57,10 +61,9 @@ static BOOL betterChoice(NSFontTraitMask desiredTraits, int desiredWeight, NSFontTraitMask chosenTraits, int chosenWeight, NSFontTraitMask candidateTraits, int candidateWeight) { - if (!acceptableChoice(desiredTraits, desiredWeight, candidateTraits, candidateWeight)) { + if (!acceptableChoice(desiredTraits, desiredWeight, candidateTraits, candidateWeight)) return NO; - } - + // 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. @@ -72,8 +75,9 @@ static BOOL betterChoice(NSFontTraitMask desiredTraits, int desiredWeight, NSCondensedFontMask, NSExpandedFontMask, NSNarrowFontMask, - NSBoldFontMask, - 0 }; + 0 + }; + int i = 0; NSFontTraitMask mask; while ((mask = masks[i++])) { @@ -85,55 +89,91 @@ static BOOL betterChoice(NSFontTraitMask desiredTraits, int desiredWeight, if (!chosenHasUnwantedTrait && candidateHasUnwantedTrait) return NO; } - - int chosenWeightDelta = chosenWeight - desiredWeight; - int candidateWeightDelta = candidateWeight - desiredWeight; - - int chosenWeightDeltaMagnitude = abs(chosenWeightDelta); - int candidateWeightDeltaMagnitude = abs(candidateWeightDelta); - - // 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 YES; - } - if (candidateWeightDeltaMagnitude == chosenWeightDeltaMagnitude && candidateWeight < chosenWeight) { - return YES; - } - - return NO; + + int chosenWeightDeltaMagnitude = abs(chosenWeight - desiredWeight); + int candidateWeightDeltaMagnitude = abs(candidateWeight - desiredWeight); + + // If both are the same distance from the desired weight, prefer the candidate if it is further from medium. + if (chosenWeightDeltaMagnitude == candidateWeightDeltaMagnitude) + return abs(candidateWeight - 6) > abs(chosenWeight - 6); + + // Otherwise, prefer the one closer to the desired weight. + return candidateWeightDeltaMagnitude < chosenWeightDeltaMagnitude; +} + +// Workaround for <rdar://problem/5781372>. +static inline void fixUpWeight(NSInteger& weight, NSString *fontName) +{ + if (weight == 3 && [fontName rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch | NSBackwardsSearch | NSLiteralSearch].location != NSNotFound) + weight = 2; +} + +static inline FontTraitsMask toTraitsMask(NSFontTraitMask appKitTraits, NSInteger appKitWeight) +{ + return static_cast<FontTraitsMask>(((appKitTraits & NSFontItalicTrait) ? FontStyleItalicMask : FontStyleNormalMask) + | FontVariantNormalMask + | (appKitWeight == 1 ? FontWeight100Mask : + appKitWeight == 2 ? FontWeight200Mask : + appKitWeight <= 4 ? FontWeight300Mask : + appKitWeight == 5 ? FontWeight400Mask : + appKitWeight == 6 ? FontWeight500Mask : + appKitWeight <= 8 ? FontWeight600Mask : + appKitWeight == 9 ? FontWeight700Mask : + appKitWeight <= 11 ? FontWeight800Mask : + FontWeight900Mask)); } @implementation WebFontCache -// Family name is somewhat of a misnomer here. We first attempt to find an exact match -// comparing the desiredFamily to the PostScript name of the installed fonts. If that fails -// we then do a search based on the family names of the installed fonts. -+ (NSFont *)internalFontWithFamily:(NSString *)desiredFamily traits:(NSFontTraitMask)desiredTraits size:(float)size ++ (void)getTraits:(Vector<unsigned>&)traitsMasks inFamily:(NSString *)desiredFamily { NSFontManager *fontManager = [NSFontManager sharedFontManager]; - // Look for an exact match first. - NSEnumerator *availableFonts = [[fontManager availableFonts] objectEnumerator]; - NSString *availableFont; - NSFont *nameMatchedFont = nil; - while ((availableFont = [availableFonts nextObject])) { - if ([desiredFamily caseInsensitiveCompare:availableFont] == NSOrderedSame) { - nameMatchedFont = [NSFont fontWithName:availableFont size:size]; - - // Special case Osaka-Mono. According to <rdar://problem/3999467>, we need to - // treat Osaka-Mono as fixed pitch. - if ([desiredFamily caseInsensitiveCompare:@"Osaka-Mono"] == NSOrderedSame && desiredTraits == 0) - return nameMatchedFont; - - NSFontTraitMask traits = [fontManager traitsOfFont:nameMatchedFont]; - if ((traits & desiredTraits) == desiredTraits) - return [fontManager convertFont:nameMatchedFont toHaveTrait:desiredTraits]; + NSEnumerator *e = [[fontManager availableFontFamilies] objectEnumerator]; + NSString *availableFamily; + while ((availableFamily = [e nextObject])) { + if ([desiredFamily caseInsensitiveCompare:availableFamily] == NSOrderedSame) break; + } + + if (!availableFamily) { + // Match by PostScript name. + NSEnumerator *availableFonts = [[fontManager availableFonts] objectEnumerator]; + NSString *availableFont; + while ((availableFont = [availableFonts nextObject])) { + if ([desiredFamily caseInsensitiveCompare:availableFont] == NSOrderedSame) { + NSFont *font = [NSFont fontWithName:availableFont size:10]; + NSInteger weight = [fontManager weightOfFont:font]; + fixUpWeight(weight, desiredFamily); + traitsMasks.append(toTraitsMask([fontManager traitsOfFont:font], weight)); + break; + } } + return; } + 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. + NSString *fontFullName = [fontInfo objectAtIndex:0]; + NSInteger fontWeight = [[fontInfo objectAtIndex:2] intValue]; + fixUpWeight(fontWeight, fontFullName); + + NSFontTraitMask fontTraits = [[fontInfo objectAtIndex:3] unsignedIntValue]; + traitsMasks.append(toTraitsMask(fontTraits, fontWeight)); + } +} + +// Family name is somewhat of a misnomer here. We first attempt to find an exact match +// comparing the desiredFamily to the PostScript name of the installed fonts. If that fails +// we then do a search based on the family names of the installed fonts. ++ (NSFont *)internalFontWithFamily:(NSString *)desiredFamily traits:(NSFontTraitMask)desiredTraits weight:(int)desiredWeight size:(float)size +{ + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + // 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. @@ -144,13 +184,36 @@ static BOOL betterChoice(NSFontTraitMask desiredTraits, int desiredWeight, break; } - if (!availableFamily) - availableFamily = [nameMatchedFont familyName]; + if (!availableFamily) { + // Match by PostScript name. + NSEnumerator *availableFonts = [[fontManager availableFonts] objectEnumerator]; + NSString *availableFont; + NSFont *nameMatchedFont = nil; + NSFontTraitMask desiredTraitsForNameMatch = desiredTraits | (desiredWeight >= 7 ? NSBoldFontMask : 0); + while ((availableFont = [availableFonts nextObject])) { + if ([desiredFamily caseInsensitiveCompare:availableFont] == NSOrderedSame) { + nameMatchedFont = [NSFont fontWithName:availableFont size:size]; + + // Special case Osaka-Mono. According to <rdar://problem/3999467>, we need to + // treat Osaka-Mono as fixed pitch. + if ([desiredFamily caseInsensitiveCompare:@"Osaka-Mono"] == NSOrderedSame && desiredTraitsForNameMatch == 0) + return nameMatchedFont; + + NSFontTraitMask traits = [fontManager traitsOfFont:nameMatchedFont]; + if ((traits & desiredTraitsForNameMatch) == desiredTraitsForNameMatch) + return [fontManager convertFont:nameMatchedFont toHaveTrait:desiredTraitsForNameMatch]; + + availableFamily = [nameMatchedFont familyName]; + break; + } + } + } // Found a family, now figure out what weight and traits to use. BOOL choseFont = false; int chosenWeight = 0; NSFontTraitMask chosenTraits = 0; + NSString *chosenFullName = 0; NSArray *fonts = [fontManager availableMembersOfFontFamily:availableFamily]; unsigned n = [fonts count]; @@ -159,21 +222,25 @@ static BOOL betterChoice(NSFontTraitMask desiredTraits, int desiredWeight, NSArray *fontInfo = [fonts objectAtIndex:i]; // Array indices must be hard coded because of lame AppKit API. - int fontWeight = [[fontInfo objectAtIndex:2] intValue]; + NSString *fontFullName = [fontInfo objectAtIndex:0]; + NSInteger fontWeight = [[fontInfo objectAtIndex:2] intValue]; + fixUpWeight(fontWeight, fontFullName); + NSFontTraitMask fontTraits = [[fontInfo objectAtIndex:3] unsignedIntValue]; BOOL newWinner; if (!choseFont) - newWinner = acceptableChoice(desiredTraits, DESIRED_WEIGHT, fontTraits, fontWeight); + newWinner = acceptableChoice(desiredTraits, desiredWeight, fontTraits, fontWeight); else - newWinner = betterChoice(desiredTraits, DESIRED_WEIGHT, chosenTraits, chosenWeight, fontTraits, fontWeight); + newWinner = betterChoice(desiredTraits, desiredWeight, chosenTraits, chosenWeight, fontTraits, fontWeight); if (newWinner) { choseFont = YES; chosenWeight = fontWeight; chosenTraits = fontTraits; + chosenFullName = fontFullName; - if (chosenWeight == DESIRED_WEIGHT && (chosenTraits & IMPORTANT_FONT_TRAITS) == (desiredTraits & IMPORTANT_FONT_TRAITS)) + if (chosenWeight == desiredWeight && (chosenTraits & IMPORTANT_FONT_TRAITS) == (desiredTraits & IMPORTANT_FONT_TRAITS)) break; } } @@ -181,17 +248,18 @@ static BOOL betterChoice(NSFontTraitMask desiredTraits, int desiredWeight, if (!choseFont) return nil; - NSFont *font = [fontManager fontWithFamily:availableFamily traits:chosenTraits weight:chosenWeight size:size]; + NSFont *font = [NSFont fontWithName:chosenFullName size:size]; if (!font) return nil; NSFontTraitMask actualTraits = 0; - if (desiredTraits & (NSItalicFontMask | NSBoldFontMask)) - actualTraits = [[NSFontManager sharedFontManager] traitsOfFont:font]; + if (desiredTraits & NSFontItalicTrait) + actualTraits = [fontManager traitsOfFont:font]; + int actualWeight = [fontManager weightOfFont:font]; - bool syntheticBold = (desiredTraits & NSBoldFontMask) && !(actualTraits & NSBoldFontMask); - bool syntheticOblique = (desiredTraits & NSItalicFontMask) && !(actualTraits & NSItalicFontMask); + bool syntheticBold = desiredWeight >= 7 && actualWeight < 7; + bool syntheticOblique = (desiredTraits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait); // 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 @@ -215,10 +283,10 @@ static BOOL betterChoice(NSFontTraitMask desiredTraits, int desiredWeight, return font; } -+ (NSFont *)fontWithFamily:(NSString *)desiredFamily traits:(NSFontTraitMask)desiredTraits size:(float)size ++ (NSFont *)fontWithFamily:(NSString *)desiredFamily traits:(NSFontTraitMask)desiredTraits weight:(int)desiredWeight size:(float)size { #ifndef BUILDING_ON_TIGER - NSFont *font = [self internalFontWithFamily:desiredFamily traits:desiredTraits size:size]; + NSFont *font = [self internalFontWithFamily:desiredFamily traits:desiredTraits weight:desiredWeight size:size]; if (font) return font; @@ -227,7 +295,7 @@ static BOOL betterChoice(NSFontTraitMask desiredTraits, int desiredWeight, [NSFont fontWithName:desiredFamily size:size]; #endif - return [self internalFontWithFamily:desiredFamily traits:desiredTraits size:size]; + return [self internalFontWithFamily:desiredFamily traits:desiredTraits weight:desiredWeight size:size]; } @end |