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 |