| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. | |
| 3 * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> | |
| 4 * | |
| 5 * Redistribution and use in source and binary forms, with or without | |
| 6 * modification, are permitted provided that the following conditions | |
| 7 * are met: | |
| 8 * | |
| 9 * 1. Redistributions of source code must retain the above copyright | |
| 10 * notice, this list of conditions and the following disclaimer. | |
| 11 * 2. Redistributions in binary form must reproduce the above copyright | |
| 12 * notice, this list of conditions and the following disclaimer in the | |
| 13 * documentation and/or other materials provided with the distribution. | |
| 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
| 15 * its contributors may be used to endorse or promote products derived | |
| 16 * from this software without specific prior written permission. | |
| 17 * | |
| 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
| 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
| 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
| 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
| 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
| 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
| 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 28 */ | |
| 29 | |
| 30 #import "config.h" | |
| 31 #import "core/platform/mac/WebFontCache.h" | |
| 32 | |
| 33 #import <AppKit/AppKit.h> | |
| 34 #import <Foundation/Foundation.h> | |
| 35 #import <math.h> | |
| 36 #import "platform/fonts/FontTraitsMask.h" | |
| 37 #import <wtf/UnusedParam.h> | |
| 38 | |
| 39 using namespace WebCore; | |
| 40 | |
| 41 | |
| 42 #define SYNTHESIZED_FONT_TRAITS (NSBoldFontMask | NSItalicFontMask) | |
| 43 | |
| 44 #define IMPORTANT_FONT_TRAITS (0 \ | |
| 45 | NSCompressedFontMask \ | |
| 46 | NSCondensedFontMask \ | |
| 47 | NSExpandedFontMask \ | |
| 48 | NSItalicFontMask \ | |
| 49 | NSNarrowFontMask \ | |
| 50 | NSPosterFontMask \ | |
| 51 | NSSmallCapsFontMask \ | |
| 52 ) | |
| 53 | |
| 54 static BOOL acceptableChoice(NSFontTraitMask desiredTraits, NSFontTraitMask cand
idateTraits) | |
| 55 { | |
| 56 desiredTraits &= ~SYNTHESIZED_FONT_TRAITS; | |
| 57 return (candidateTraits & desiredTraits) == desiredTraits; | |
| 58 } | |
| 59 | |
| 60 static BOOL betterChoice(NSFontTraitMask desiredTraits, int desiredWeight, | |
| 61 NSFontTraitMask chosenTraits, int chosenWeight, | |
| 62 NSFontTraitMask candidateTraits, int candidateWeight) | |
| 63 { | |
| 64 if (!acceptableChoice(desiredTraits, candidateTraits)) | |
| 65 return NO; | |
| 66 | |
| 67 // A list of the traits we care about. | |
| 68 // The top item in the list is the worst trait to mismatch; if a font has th
is | |
| 69 // and we didn't ask for it, we'd prefer any other font in the family. | |
| 70 const NSFontTraitMask masks[] = { | |
| 71 NSPosterFontMask, | |
| 72 NSSmallCapsFontMask, | |
| 73 NSItalicFontMask, | |
| 74 NSCompressedFontMask, | |
| 75 NSCondensedFontMask, | |
| 76 NSExpandedFontMask, | |
| 77 NSNarrowFontMask, | |
| 78 0 | |
| 79 }; | |
| 80 | |
| 81 int i = 0; | |
| 82 NSFontTraitMask mask; | |
| 83 while ((mask = masks[i++])) { | |
| 84 BOOL desired = (desiredTraits & mask) != 0; | |
| 85 BOOL chosenHasUnwantedTrait = desired != ((chosenTraits & mask) != 0); | |
| 86 BOOL candidateHasUnwantedTrait = desired != ((candidateTraits & mask) !=
0); | |
| 87 if (!candidateHasUnwantedTrait && chosenHasUnwantedTrait) | |
| 88 return YES; | |
| 89 if (!chosenHasUnwantedTrait && candidateHasUnwantedTrait) | |
| 90 return NO; | |
| 91 } | |
| 92 | |
| 93 int chosenWeightDeltaMagnitude = abs(chosenWeight - desiredWeight); | |
| 94 int candidateWeightDeltaMagnitude = abs(candidateWeight - desiredWeight); | |
| 95 | |
| 96 // If both are the same distance from the desired weight, prefer the candida
te if it is further from medium. | |
| 97 if (chosenWeightDeltaMagnitude == candidateWeightDeltaMagnitude) | |
| 98 return abs(candidateWeight - 6) > abs(chosenWeight - 6); | |
| 99 | |
| 100 // Otherwise, prefer the one closer to the desired weight. | |
| 101 return candidateWeightDeltaMagnitude < chosenWeightDeltaMagnitude; | |
| 102 } | |
| 103 | |
| 104 static inline FontTraitsMask toTraitsMask(NSFontTraitMask appKitTraits, NSIntege
r appKitWeight) | |
| 105 { | |
| 106 return static_cast<FontTraitsMask>(((appKitTraits & NSFontItalicTrait) ? Fon
tStyleItalicMask : FontStyleNormalMask) | |
| 107 | FontVariantNormalMask | |
| 108 | (appKitWeight == 1 ? FontWeight100Mask : | |
| 109 appKitWeight == 2 ? FontWeight200Mask : | |
| 110 appKitWeight <= 4 ? FontWeight300Mask : | |
| 111 appKitWeight == 5 ? FontWeight400Mask : | |
| 112 appKitWeight == 6 ? FontWeight500Mask : | |
| 113 appKitWeight <= 8 ? FontWeight600Mask : | |
| 114 appKitWeight == 9 ? FontWeight700Mask : | |
| 115 appKitWeight <= 11 ? FontWeight800Mask : | |
| 116 FontWeight900Mask)); | |
| 117 } | |
| 118 | |
| 119 @implementation WebFontCache | |
| 120 | |
| 121 + (void)getTraits:(Vector<unsigned>&)traitsMasks inFamily:(NSString *)desiredFam
ily | |
| 122 { | |
| 123 NSFontManager *fontManager = [NSFontManager sharedFontManager]; | |
| 124 | |
| 125 NSEnumerator *e = [[fontManager availableFontFamilies] objectEnumerator]; | |
| 126 NSString *availableFamily; | |
| 127 while ((availableFamily = [e nextObject])) { | |
| 128 if ([desiredFamily caseInsensitiveCompare:availableFamily] == NSOrderedS
ame) | |
| 129 break; | |
| 130 } | |
| 131 | |
| 132 if (!availableFamily) { | |
| 133 // Match by PostScript name. | |
| 134 NSEnumerator *availableFonts = [[fontManager availableFonts] objectEnume
rator]; | |
| 135 NSString *availableFont; | |
| 136 while ((availableFont = [availableFonts nextObject])) { | |
| 137 if ([desiredFamily caseInsensitiveCompare:availableFont] == NSOrdere
dSame) { | |
| 138 NSFont *font = [NSFont fontWithName:availableFont size:10]; | |
| 139 NSInteger weight = [fontManager weightOfFont:font]; | |
| 140 traitsMasks.append(toTraitsMask([fontManager traitsOfFont:font],
weight)); | |
| 141 break; | |
| 142 } | |
| 143 } | |
| 144 return; | |
| 145 } | |
| 146 | |
| 147 NSArray *fonts = [fontManager availableMembersOfFontFamily:availableFamily];
| |
| 148 unsigned n = [fonts count]; | |
| 149 unsigned i; | |
| 150 for (i = 0; i < n; i++) { | |
| 151 NSArray *fontInfo = [fonts objectAtIndex:i]; | |
| 152 // Array indices must be hard coded because of lame AppKit API. | |
| 153 NSInteger fontWeight = [[fontInfo objectAtIndex:2] intValue]; | |
| 154 | |
| 155 NSFontTraitMask fontTraits = [[fontInfo objectAtIndex:3] unsignedIntValu
e]; | |
| 156 traitsMasks.append(toTraitsMask(fontTraits, fontWeight)); | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 // Family name is somewhat of a misnomer here. We first attempt to find an exac
t match | |
| 161 // comparing the desiredFamily to the PostScript name of the installed fonts. I
f that fails | |
| 162 // we then do a search based on the family names of the installed fonts. | |
| 163 + (NSFont *)internalFontWithFamily:(NSString *)desiredFamily traits:(NSFontTrait
Mask)desiredTraits weight:(int)desiredWeight size:(float)size | |
| 164 { | |
| 165 NSFontManager *fontManager = [NSFontManager sharedFontManager]; | |
| 166 | |
| 167 // Do a simple case insensitive search for a matching font family. | |
| 168 // NSFontManager requires exact name matches. | |
| 169 // This addresses the problem of matching arial to Arial, etc., but perhaps
not all the issues. | |
| 170 NSEnumerator *e = [[fontManager availableFontFamilies] objectEnumerator]; | |
| 171 NSString *availableFamily; | |
| 172 while ((availableFamily = [e nextObject])) { | |
| 173 if ([desiredFamily caseInsensitiveCompare:availableFamily] == NSOrderedS
ame) | |
| 174 break; | |
| 175 } | |
| 176 | |
| 177 if (!availableFamily) { | |
| 178 // Match by PostScript name. | |
| 179 NSEnumerator *availableFonts = [[fontManager availableFonts] objectEnume
rator]; | |
| 180 NSString *availableFont; | |
| 181 NSFont *nameMatchedFont = nil; | |
| 182 NSFontTraitMask desiredTraitsForNameMatch = desiredTraits | (desiredWeig
ht >= 7 ? NSBoldFontMask : 0); | |
| 183 while ((availableFont = [availableFonts nextObject])) { | |
| 184 if ([desiredFamily caseInsensitiveCompare:availableFont] == NSOrdere
dSame) { | |
| 185 nameMatchedFont = [NSFont fontWithName:availableFont size:size]; | |
| 186 | |
| 187 // Special case Osaka-Mono. According to <rdar://problem/399946
7>, we need to | |
| 188 // treat Osaka-Mono as fixed pitch. | |
| 189 if ([desiredFamily caseInsensitiveCompare:@"Osaka-Mono"] == NSOr
deredSame && desiredTraitsForNameMatch == 0) | |
| 190 return nameMatchedFont; | |
| 191 | |
| 192 NSFontTraitMask traits = [fontManager traitsOfFont:nameMatchedFo
nt]; | |
| 193 if ((traits & desiredTraitsForNameMatch) == desiredTraitsForName
Match) | |
| 194 return [fontManager convertFont:nameMatchedFont toHaveTrait:
desiredTraitsForNameMatch]; | |
| 195 | |
| 196 availableFamily = [nameMatchedFont familyName]; | |
| 197 break; | |
| 198 } | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 // Found a family, now figure out what weight and traits to use. | |
| 203 BOOL choseFont = false; | |
| 204 int chosenWeight = 0; | |
| 205 NSFontTraitMask chosenTraits = 0; | |
| 206 NSString *chosenFullName = 0; | |
| 207 | |
| 208 NSArray *fonts = [fontManager availableMembersOfFontFamily:availableFamily];
| |
| 209 unsigned n = [fonts count]; | |
| 210 unsigned i; | |
| 211 for (i = 0; i < n; i++) { | |
| 212 NSArray *fontInfo = [fonts objectAtIndex:i]; | |
| 213 | |
| 214 // Array indices must be hard coded because of lame AppKit API. | |
| 215 NSString *fontFullName = [fontInfo objectAtIndex:0]; | |
| 216 NSInteger fontWeight = [[fontInfo objectAtIndex:2] intValue]; | |
| 217 | |
| 218 NSFontTraitMask fontTraits = [[fontInfo objectAtIndex:3] unsignedIntValu
e]; | |
| 219 | |
| 220 BOOL newWinner; | |
| 221 if (!choseFont) | |
| 222 newWinner = acceptableChoice(desiredTraits, fontTraits); | |
| 223 else | |
| 224 newWinner = betterChoice(desiredTraits, desiredWeight, chosenTraits,
chosenWeight, fontTraits, fontWeight); | |
| 225 | |
| 226 if (newWinner) { | |
| 227 choseFont = YES; | |
| 228 chosenWeight = fontWeight; | |
| 229 chosenTraits = fontTraits; | |
| 230 chosenFullName = fontFullName; | |
| 231 | |
| 232 if (chosenWeight == desiredWeight && (chosenTraits & IMPORTANT_FONT_
TRAITS) == (desiredTraits & IMPORTANT_FONT_TRAITS)) | |
| 233 break; | |
| 234 } | |
| 235 } | |
| 236 | |
| 237 if (!choseFont) | |
| 238 return nil; | |
| 239 | |
| 240 NSFont *font = [NSFont fontWithName:chosenFullName size:size]; | |
| 241 | |
| 242 if (!font) | |
| 243 return nil; | |
| 244 | |
| 245 NSFontTraitMask actualTraits = 0; | |
| 246 if (desiredTraits & NSFontItalicTrait) | |
| 247 actualTraits = [fontManager traitsOfFont:font]; | |
| 248 int actualWeight = [fontManager weightOfFont:font]; | |
| 249 | |
| 250 bool syntheticBold = desiredWeight >= 7 && actualWeight < 7; | |
| 251 bool syntheticOblique = (desiredTraits & NSFontItalicTrait) && !(actualTrait
s & NSFontItalicTrait); | |
| 252 | |
| 253 // There are some malformed fonts that will be correctly returned by -fontWi
thFamily:traits:weight:size: as a match for a particular trait, | |
| 254 // though -[NSFontManager traitsOfFont:] incorrectly claims the font does no
t have the specified trait. This could result in applying | |
| 255 // synthetic bold on top of an already-bold font, as reported in <http://bug
s.webkit.org/show_bug.cgi?id=6146>. To work around this | |
| 256 // 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 | |
| 257 // the same family without those traits (to apply the synthetic traits to la
ter). | |
| 258 NSFontTraitMask nonSyntheticTraits = desiredTraits; | |
| 259 | |
| 260 if (syntheticBold) | |
| 261 nonSyntheticTraits &= ~NSBoldFontMask; | |
| 262 | |
| 263 if (syntheticOblique) | |
| 264 nonSyntheticTraits &= ~NSItalicFontMask; | |
| 265 | |
| 266 if (nonSyntheticTraits != desiredTraits) { | |
| 267 NSFont *fontWithoutSyntheticTraits = [fontManager fontWithFamily:availab
leFamily traits:nonSyntheticTraits weight:chosenWeight size:size]; | |
| 268 if (fontWithoutSyntheticTraits) | |
| 269 font = fontWithoutSyntheticTraits; | |
| 270 } | |
| 271 | |
| 272 return font; | |
| 273 } | |
| 274 | |
| 275 + (NSFont *)fontWithFamily:(NSString *)desiredFamily traits:(NSFontTraitMask)des
iredTraits weight:(int)desiredWeight size:(float)size | |
| 276 { | |
| 277 NSFont *font = [self internalFontWithFamily:desiredFamily traits:desiredTrai
ts weight:desiredWeight size:size]; | |
| 278 if (font) | |
| 279 return font; | |
| 280 | |
| 281 // Auto activate the font before looking for it a second time. | |
| 282 // Ignore the result because we want to use our own algorithm to actually fi
nd the font. | |
| 283 [NSFont fontWithName:desiredFamily size:size]; | |
| 284 | |
| 285 return [self internalFontWithFamily:desiredFamily traits:desiredTraits weigh
t:desiredWeight size:size]; | |
| 286 } | |
| 287 | |
| 288 + (NSFont *)fontWithFamily:(NSString *)desiredFamily traits:(NSFontTraitMask)des
iredTraits size:(float)size | |
| 289 { | |
| 290 int desiredWeight = (desiredTraits & NSBoldFontMask) ? 9 : 5; | |
| 291 return [self fontWithFamily:desiredFamily traits:desiredTraits weight:desire
dWeight size:size]; | |
| 292 } | |
| 293 | |
| 294 @end | |
| OLD | NEW |