Bug 396732. Cache codepoints with no fonts to avoid repeated system-wide font lookup...
[wine-gecko.git] / gfx / thebes / src / gfxQuartzFontCache.mm
blobc55213393d2de24da2046cac25c5bf808d971ed7
1 /* -*- Mode: ObjC; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * ***** BEGIN LICENSE BLOCK *****
3  * Version: BSD
4  *
5  * Copyright (C) 2006 Mozilla Corporation.  All rights reserved.
6  *
7  * Contributor(s):
8  *   Vladimir Vukicevic <vladimir@pobox.com>
9  *   Masayuki Nakano <masayuki@d-toybox.com>
10  *   John Daggett <jdaggett@mozilla.com>
11  * 
12  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  *
18  * 1.  Redistributions of source code must retain the above copyright
19  *     notice, this list of conditions and the following disclaimer.
20  * 2.  Redistributions in binary form must reproduce the above copyright
21  *     notice, this list of conditions and the following disclaimer in the
22  *     documentation and/or other materials provided with the distribution.
23  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
24  *     its contributors may be used to endorse or promote products derived
25  *     from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
28  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
29  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
31  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
34  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
36  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37  *
38  * ***** END LICENSE BLOCK ***** */
40 #include <Carbon.h>
42 #import <AppKit/AppKit.h>
44 #include "gfxPlatformMac.h"
45 #include "gfxQuartzFontCache.h"
46 #include "gfxAtsuiFonts.h"
48 #include "nsIPref.h"  // for pref changes callback notification
49 #include "nsServiceManagerUtils.h"
51 // _atsFontID is private; add it in our new category to NSFont
52 @interface NSFont (MozillaCategory)
53 - (ATSUFontID)_atsFontID;
54 @end
56 #define INDEX_FONT_POSTSCRIPT_NAME 0
57 #define INDEX_FONT_FACE_NAME 1
58 #define INDEX_FONT_WEIGHT 2
59 #define INDEX_FONT_TRAITS 3
61 static const int kAppleMaxWeight = 14;
63 static const int gAppleWeightToCSSWeight[] = {
64     0,
65     1, // 1. 
66     1, // 2.  W1, ultralight
67     2, // 3.  W2, extralight
68     3, // 4.  W3, light
69     4, // 5.  W4, semilight
70     5, // 6.  W5, medium
71     6, // 7.
72     6, // 8.  W6, semibold
73     7, // 9.  W7, bold
74     8, // 10. W8, extrabold
75     8, // 11.
76     9, // 12. W9, ultrabold
77     9, // 13
78     9  // 14
82 static void GetStringForNSString(const NSString *aSrc, nsAString& aDist)
84     aDist.SetLength([aSrc length]);
85     [aSrc getCharacters:aDist.BeginWriting()];
88 static NSString* GetNSStringForString(const nsAString& aSrc)
90     return [NSString stringWithCharacters:aSrc.BeginReading()
91                      length:aSrc.Length()];
94 static PRLogModuleInfo *gFontInfoLog = PR_NewLogModule("fontInfoLog");
96 void
97 gfxQuartzFontCache::GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult)
99     aResult = aKeyName;
100     ToLowerCase(aResult);
103 /* MacOSFontEntry */
104 #pragma mark-
106 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, 
107                                 PRInt32 aAppleWeight, PRUint32 aTraits, MacOSFamilyEntry *aFamily)
108     : mPostscriptName(aPostscriptName), mTraits(aTraits), mFamily(aFamily), mATSUFontID(0),
109         mCmapInitialized(0), mATSUIDInitialized(0)
111     mWeight = gfxQuartzFontCache::AppleWeightToCSSWeight(aAppleWeight);
114 const nsString& 
115 MacOSFontEntry::FamilyName()
117     return mFamily->Name();
120 PRBool MacOSFontEntry::IsFixedPitch() {
121     return mTraits & NSFixedPitchFontMask;
124 PRBool MacOSFontEntry::IsItalicStyle() {
125     return mTraits & NSItalicFontMask;
128 PRBool MacOSFontEntry::IsBold() {
129     return mTraits & NSBoldFontMask;
132 ATSUFontID MacOSFontEntry::GetFontID() 
134     if (!mATSUIDInitialized) {
135         mATSUIDInitialized = PR_TRUE;
136         NSString *psname = GetNSStringForString(mPostscriptName);
137         NSFont *font = [NSFont fontWithName:psname size:0.0];
138         if (font) mATSUFontID = [font _atsFontID];
139     }
140     return mATSUFontID; 
143 nsresult
144 MacOSFontEntry::ReadCMAP()
146     OSStatus status;
147     ByteCount size;
148     
149     if (mCmapInitialized) return NS_OK;
150     ATSUFontID fontID = GetFontID();
151     
152     // attempt this once, if errors occur leave a blank cmap
153     mCmapInitialized = PR_TRUE;
154     
155     status = ATSFontGetTable(fontID, 'cmap', 0, 0, 0, &size);
156     //printf( "cmap size: %s %d\n", NS_ConvertUTF16toUTF8(mName).get(), size );
157     NS_ENSURE_TRUE(status == noErr, NS_ERROR_FAILURE);
159     nsAutoTArray<PRUint8,16384> buffer;
160     if (!buffer.AppendElements(size))
161         return NS_ERROR_OUT_OF_MEMORY;
162     PRUint8 *cmap = buffer.Elements();
164     status = ATSFontGetTable(fontID, 'cmap', 0, size, cmap, &size);
165     NS_ENSURE_TRUE(status == noErr, NS_ERROR_FAILURE);
167     nsresult rv = NS_ERROR_FAILURE;
168     PRPackedBool  unicodeFont, symbolFont; // currently ignored
169     rv = gfxFontUtils::ReadCMAP(cmap, size, mCharacterMap, mUnicodeRanges, unicodeFont, symbolFont);
170     
171     return rv;
175 /* MacOSFamilyEntry */
176 #pragma mark-
178 static const PRUint32 kTraits_NonNormalWidthMask = NSNarrowFontMask | NSExpandedFontMask | 
179                             NSCondensedFontMask | NSCompressedFontMask | NSFixedPitchFontMask;
181 MacOSFontEntry*
182 MacOSFamilyEntry::FindFont(const gfxFontStyle* aStyle)
184     // short-circuit the single face per family case
185     if (mAvailableFonts.Length() == 1) return mAvailableFonts[0];
186     
187     PRBool found = PR_FALSE;
188     PRBool isItalic = (aStyle->style == FONT_STYLE_ITALIC || aStyle->style == FONT_STYLE_OBLIQUE);
189     MacOSFontEntry* fontsWithTraits[10];
190     
191     memset(fontsWithTraits, 0, sizeof(fontsWithTraits));
192     
193     // match italic faces
194     if ( isItalic ) {    
195         // first search for italic normal width fonts
196         found = FindFontsWithTraits(fontsWithTraits, NSItalicFontMask, kTraits_NonNormalWidthMask);
197         
198         // if not found, italic any width ones
199         if (!found) {
200             found = FindFontsWithTraits(fontsWithTraits, NSItalicFontMask, 0);        
201         }
202     }
203     
204     // match non-italic faces, if no italic faces fall through here
205     if (!found) {
206         // look for normal width fonts
207         found = FindFontsWithTraits(fontsWithTraits, NSUnitalicFontMask, kTraits_NonNormalWidthMask);
208         
209         // if not found, any face will do
210         if (!found) {
211             found = FindFontsWithTraits(fontsWithTraits, NSUnitalicFontMask, 0);        
212         } 
213     }
214     
215     // still not found?!? family must only contain italic fonts when looking for a normal 
216     // face, just use the whole list
217     if (!found) {
218         found = FindFontsWithTraits(fontsWithTraits, 0, 0);
219     }
220     NS_ASSERTION(found, "Font family containing no faces");
221     if (!found) return nsnull;
222     
223     MacOSFontEntry* chosenFont = FindFontWeight(fontsWithTraits, aStyle);
224     NS_ASSERTION(chosenFont, "Somehow selected a null font entry when choosing based on font weight");
225     return chosenFont;
228 void
229 MacOSFamilyEntry::FindFontForChar(FontSearch *aMatchData)
231     PRUint32 numFonts, i;
232     
233     // xxx - optimization point - keep a bit vector with the union of supported unicode ranges
234     // by all fonts for this family and bail immediately if the character is not in any of
235     // this family's cmaps
236     
237     // iterate over fonts
238     numFonts = mAvailableFonts.Length();
239     for (i = 0; i < numFonts; i++) {
240         MacOSFontEntry *fe = mAvailableFonts[i];
241         PRInt32 rank = 0;
242     
243         if (fe->TestCharacterMap(aMatchData->ch)) {
244             rank += 20;
245         }
246     
247         // if we didn't match any characters don't bother wasting more time.
248         if (rank == 0)
249             return;
250             
251         // omitting from original windows code -- family name, lang group, pitch
252         // not available in current FontEntry implementation
253     
254         if (aMatchData->fontToMatch) { 
255             const gfxFontStyle *style = aMatchData->fontToMatch->GetStyle();
256             
257             // italics
258             if (fe->IsItalicStyle() && 
259                     (style->style == FONT_STYLE_ITALIC || style->style == FONT_STYLE_ITALIC)) {
260                 rank += 5;
261             }
262             
263             // weight
264             PRInt8 baseWeight, weightDistance;
265             style->ComputeWeightAndOffset(&baseWeight, &weightDistance);
266     
267             // xxx - not entirely correct, the one unit of weight distance reflects 
268             // the "next bolder/lighter face"
269             PRUint32 targetWeight = (baseWeight * 100) + (weightDistance * 100);
270     
271             PRUint32 entryWeight = fe->Weight() * 100;
272             if (entryWeight == targetWeight) {
273                 rank += 5;
274             } else {
275                 PRUint32 diffWeight = abs(entryWeight - targetWeight);
276                 if (diffWeight <= 100)  // favor faces close in weight
277                     rank += 2;
278             }
279         } else {
280             // if no font to match, prefer non-bold, non-italic fonts
281             if (!fe->IsItalicStyle() && !fe->IsBold())
282                 rank += 5;
283         }
284         
285         // xxx - add whether AAT font with morphing info for specific lang groups
286         
287         if (rank > aMatchData->matchRank
288             || (rank == aMatchData->matchRank && Compare(fe->Name(), aMatchData->bestMatch->Name()) > 0)) 
289         {
290             aMatchData->bestMatch = fe;
291             aMatchData->matchRank = rank;
292         }
293     
294     }
297 PRBool
298 MacOSFamilyEntry::FindFontsWithTraits(MacOSFontEntry* aFontsForWeights[], PRUint32 aPosTraitsMask, 
299                                         PRUint32 aNegTraitsMask)
301     PRUint32 numFonts, i;
302     PRBool found = PR_FALSE;
303     
304     // iterate over fonts
305     numFonts = mAvailableFonts.Length();
306     for (i = 0; i < numFonts; i++) {
307         MacOSFontEntry *fe = mAvailableFonts[i];
308         
309         // if traits match, add to list of fonts
310         PRUint32 traits = fe->Traits();
311         
312         // aPosTraitsMask == 0 ==> match all
313         if ( (!aPosTraitsMask || (traits & aPosTraitsMask)) && !(traits & aNegTraitsMask)) {
314             PRInt32 weight = fe->Weight();
315             
316             // always prefer the first font for a given weight, helps deal a bit with 
317             // families with lots of faces (e.g. Minion Pro)
318             if (!aFontsForWeights[weight]) {
319                 aFontsForWeights[weight] = fe;
320                 found = PR_TRUE;
321             }
322         }
323     }
324     return found;
327 MacOSFontEntry* 
328 MacOSFamilyEntry::FindFontWeight(MacOSFontEntry* aFontsForWeights[], const gfxFontStyle* aStyle)
330     // calculate the desired weight from the style
331     PRInt32 w, direction, offset, baseMatch;
332     PRInt8 baseWeight, weightDistance;
334     aStyle->ComputeWeightAndOffset(&baseWeight, &weightDistance);
335     NS_ASSERTION(baseWeight != 0, "Style with font weight 0 (whacked)");
336     
337     // choose the weight that matches the base weight using CSS Fonts spec rules
338     
339     // have the desired weight ==> use it
340     baseMatch = 0;
341     if (aFontsForWeights[baseWeight]) {
342     
343         baseMatch = baseWeight;
344         
345     } else {
346     
347         // CSS2.1 and draft CSS3 Fonts specs are ambiguous about how to handle missing 400 weight face
348         // substitute 400 and 500 for each other (example: Futura family that ships with Mac OS X)
349         if (baseWeight == 4 && aFontsForWeights[5]) {
350             baseMatch = 5;
351         } else if (baseWeight == 5 && aFontsForWeights[4]) {
352             baseMatch = 4;
353         } else {
354         
355             // otherwise, use explicit CSS rules
356             // weights above 400 ==> look up in weights, then down, otherwise look down, then up
357             direction = (baseWeight > 4 ? 1 : -1);
358             
359             // search in one direction
360             for (w = baseWeight + direction; w >= 1 && w <= 9; w += direction) {
361                 if (aFontsForWeights[w]) {
362                     baseMatch = w;
363                     break;
364                 }
365             }
366             
367             // not found? switch direction and search the remaining entries
368             if (!baseMatch) {
369                 direction = -direction;
370                 for (w = baseWeight + direction; w >= 1 && w <= 9; w += direction) {
371                     if (aFontsForWeights[w]) {
372                         baseMatch = w;
373                         break;
374                     }
375                 }
376             }
377         }
378     }
379     
380     // at this point, should have found an entry matching the base weight
381     NS_ASSERTION(baseMatch, "Somehow didn't find matching weight");
382                 
383     // handle weight offsets
384     if (weightDistance) {
385         direction = (weightDistance < 0 ? -1 : 1);
386         offset = abs(weightDistance);
387         
388         // look for bolder/lighter face [offset] number of faces away from the base face
389         // e.g. weight = 698 with Helvetica Neue ==> offset = 2, direction = -1, 
390         //      baseMatch starts at 7 (Bold), then 4 (Regular), then 2 (Light)
391         for (w = baseMatch + direction; w >= 1 && w <= 9 && offset; w += direction) {
392             if (aFontsForWeights[w]) {
393                 baseMatch = w;
394                 offset--;
395             }
396         }
397     }
398     
399     NS_ASSERTION(aFontsForWeights[baseMatch], "Chose a weight without a corresponding face");
400     return aFontsForWeights[baseMatch];
403 /* gfxQuartzFontCache */
404 #pragma mark-
406 gfxQuartzFontCache *gfxQuartzFontCache::sSharedFontCache = nsnull;
408 gfxQuartzFontCache::gfxQuartzFontCache()
410     mFontFamilies.Init(100);
411     mLocalizedFamilies.Init(30);
412     mPrefFonts.Init(10);
414     InitFontList();
415     ::ATSFontNotificationSubscribe(ATSNotification,
416                                    kATSFontNotifyOptionDefault,
417                                    (void*)this, nsnull);
419     // pref changes notification setup
420     nsCOMPtr<nsIPref> pref = do_GetService(NS_PREF_CONTRACTID);
421     pref->RegisterCallback("font.", PrefChangedCallback, this);
422     pref->RegisterCallback("font.name-list.", PrefChangedCallback, this);
423     pref->RegisterCallback("intl.accept_languages", PrefChangedCallback, this);  // hmmmm...
424     
427 static NSString* CreateNameFromBuffer(const UInt8 *aBuf, ByteCount aLength, 
428         FontPlatformCode aPlatformCode, FontScriptCode aScriptCode, FontLanguageCode aLangCode )
430     CFStringRef outName = NULL;
431     
432     if (aPlatformCode == kFontMacintoshPlatform) {
433         TextEncoding encoding;
434         OSStatus err = GetTextEncodingFromScriptInfo(aScriptCode, aLangCode, 
435                                                         kTextRegionDontCare, &encoding);
436         if (err) {
437             // some fonts are sloppy about the language code (e.g Arial Hebrew, Corsiva Hebrew)
438             // try again without the lang code to avoid bad combinations
439             OSStatus err = GetTextEncodingFromScriptInfo(aScriptCode, kTextLanguageDontCare, 
440                                                         kTextRegionDontCare, &encoding);
441             if (err) return nil;
442         }
443         outName = CFStringCreateWithBytes(kCFAllocatorDefault, aBuf, 
444                                             aLength, (CFStringEncoding) encoding, false);
445     } else if (aPlatformCode == kFontUnicodePlatform) {
446         outName = CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)aBuf, aLength/2);    
447     } else if (aPlatformCode == kFontMicrosoftPlatform) {
448         if ( aScriptCode == 0 ) {
449             outName = CFStringCreateWithBytes(kCFAllocatorDefault, aBuf, 
450                                                 aLength, kCFStringEncodingUTF16BE, false);
451         } else {
452             outName = CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)aBuf, aLength/2);    
453         }
454     }
456     return (NSString*) outName;
460 // 10.4 headers only define TT/OT name table id's up to the license id (14) but 10.5 does, so use our own enum
461 enum {
462   kMozillaFontPreferredFamilyName            = 16,
465 // xxx - rather than use ATSUI calls, probably faster to load name table directly, 
466 // this avoids copying around strings that are of no interest
468 static void ReadLocalizedNames(NSString *familyName, NSArray *faceArray, 
469                                 int faceIndex, NSMutableArray *localizedNames)
471     OSStatus err;
472     ItemCount i, nameCount;
473     ATSUFontID fontID;
474     
475     NSArray *face = [faceArray objectAtIndex:faceIndex];
476     NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
477     NSFont *font = [NSFont fontWithName:psname size:0.0];
478     fontID = [font _atsFontID];
479     
480     err = ATSUCountFontNames(fontID, &nameCount);
481     for (i = 0; i < nameCount; i++) {
483         FontNameCode nameCode;
484         FontPlatformCode platformCode;
485         FontScriptCode scriptCode;
486         FontLanguageCode langCode;
487         const ByteCount kBufLength = 2048;
488         char buf[kBufLength];
489         ByteCount len;
491         err = ATSUGetIndFontName(fontID, i, kBufLength, buf, &len, &nameCode, &platformCode, &scriptCode, &langCode);
492         if (!(nameCode == kFontFamilyName || nameCode == kMozillaFontPreferredFamilyName)) continue; 
493         if (len >= kBufLength) continue; 
494         buf[len] = 0;
495         
496         NSString *name = CreateNameFromBuffer((UInt8*)buf, len, platformCode, scriptCode, langCode);
497         NSString *foundName = nil;
498         
499         // add if not same as canonical family name or already in list of names
500         if (name) {
502             if (![name isEqualToString:familyName]) {
503                 
504                 // search other localized names
505                 int j, lnameCount = [localizedNames count];
506                 NSString *lname;
507                 for (j = 0; j < lnameCount; j++) {
508                     lname = [localizedNames objectAtIndex:j];
509                     if ([lname isEqualToString:name]) {
510                         foundName = lname;
511                         break;
512                     }
513                 }
514                 
515                 // didn't find it in the list? add it
516                 if (!foundName)
517                     [localizedNames addObject:name];
518             }
519     
520             [name release];
521         }
522     }
526 const int kIndexNormalFace_NotFound = -1;
527 const PRUint32 kNonNormalTraits = NSItalicFontMask | NSBoldFontMask | NSNarrowFontMask | NSExpandedFontMask | NSCondensedFontMask | NSCompressedFontMask;
529 void
530 gfxQuartzFontCache::InitFontList()
532     mFontFamilies.Clear();
533     mLocalizedFamilies.Clear();
534     mPrefFonts.Clear();
535     mCodepointsWithNoFonts.reset();
536     
537     // iterate over available families
538     NSFontManager *fontManager = [NSFontManager sharedFontManager];
539     NSEnumerator *families = [[fontManager availableFontFamilies] objectEnumerator];  // returns "canonical", non-localized family name
540     
541     NSMutableArray* localizedNames = [[NSMutableArray alloc] init];
542     nsAutoString availableFamilyName, postscriptFontName, localizedName;
543    
544     NSString *availableFamily = nil;
545     while ((availableFamily = [families nextObject])) {
547         // make a nsString
548         GetStringForNSString(availableFamily, availableFamilyName);
549         
550         // create a family entry
551         nsRefPtr<MacOSFamilyEntry> familyEntry = new MacOSFamilyEntry(availableFamilyName);
552         if (!familyEntry) break;
553         
554         // create a font entry for each face
555         NSArray *fontfaces = [fontManager availableMembersOfFontFamily:availableFamily];  // returns an array of [psname, style name, weight, traits] elements, goofy api
556         int faceCount = [fontfaces count];
557         int normalFaceIndex = kIndexNormalFace_NotFound;
558         int faceIndex;
560         for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
561             NSArray *face = [fontfaces objectAtIndex:faceIndex];
562             NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
563             PRInt32 weight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
564             PRUint32 traits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
565             
566             // 10.5 doesn't set NSUnitalicFontMask and NSUnboldFontMask - manually set these for consistency 
567             if (!(traits & NSBoldFontMask))
568                 traits |= NSUnboldFontMask;
569             if (!(traits & NSItalicFontMask))
570                 traits |= NSUnitalicFontMask;
571             
572             PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(init) family: %s, psname: %s, face: %s, apple-weight: %d, css-weight: %d, traits: %8.8x\n", 
573                 [availableFamily UTF8String], [psname UTF8String], [[face objectAtIndex:INDEX_FONT_FACE_NAME] UTF8String], weight, gfxQuartzFontCache::AppleWeightToCSSWeight(weight), traits ));
575             // make a nsString
576             GetStringForNSString(psname, postscriptFontName);
577         
578             // create a font entry
579             nsRefPtr<MacOSFontEntry> fontEntry = new MacOSFontEntry(postscriptFontName, weight, traits, familyEntry);
580             if (!fontEntry) break;            
581             
582             // insert into font entry array of family
583             familyEntry->AddFontEntry(fontEntry);
584             
585             // if normal face not found yet, check the traits for this one
586             if ( normalFaceIndex == kIndexNormalFace_NotFound && !(traits & kNonNormalTraits))
587                 normalFaceIndex = faceIndex;
588         }
589         
590         // if no normal face, just use the first face in the array of available ones
591         if ( normalFaceIndex == kIndexNormalFace_NotFound )
592             normalFaceIndex = 0;
593         
594         // read the name table for the normal face; if localized names exist for that face, scan all font entries for more localized names
595         [localizedNames removeAllObjects];
596         ReadLocalizedNames(availableFamily, fontfaces, normalFaceIndex, localizedNames);
597         if ([localizedNames count] != 0) {
598             for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
599                 if (faceIndex == normalFaceIndex) continue;
600                 ReadLocalizedNames(availableFamily, fontfaces, faceIndex, localizedNames);
601             }
602         }
603         
604         // add the family entry to the hash table
605         ToLowerCase(availableFamilyName);
606         mFontFamilies.Put(availableFamilyName, familyEntry);
607         
608         // add in an entry for each of the localized names
609         int lnameIndex, lnameCount = [localizedNames count];
610         for (lnameIndex = 0; lnameIndex < lnameCount; lnameIndex++) {
611             GetStringForNSString([localizedNames objectAtIndex:lnameIndex], localizedName);
612             ToLowerCase(localizedName);
613             mLocalizedFamilies.Put(localizedName, familyEntry);
614         }
615         
616     }
617     
618     // xxx - deal with quirks (e.g. Osaka-Mono)
619     
620     // xxx - need to remove bogus Helvetica/Courier italic faces (Cocoa inanity!)
621     
622     // initialize ranges of characters for which system-wide font search should be skipped
623     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
624     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
625     mCodepointsWithNoFonts.set(0xfffd);          // unknown
626                                
627     [localizedNames release];
630 PRBool 
631 gfxQuartzFontCache::ResolveFontName(const nsAString& aFontName, nsAString& aResolvedFontName)
633     nsAutoString key;
634     nsRefPtr<MacOSFamilyEntry> familyEntry;
635     GenerateFontListKey(aFontName, key);
636     
637     if (mFontFamilies.Get(key, &familyEntry) || mLocalizedFamilies.Get(key, &familyEntry)) {
638         aResolvedFontName = familyEntry->Name();
639         return PR_TRUE;
640     }
641     return PR_FALSE;
644 void
645 gfxQuartzFontCache::ATSNotification(ATSFontNotificationInfoRef aInfo,
646                                     void* aUserArg)
648     // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
649     gfxQuartzFontCache *qfc = (gfxQuartzFontCache*)aUserArg;
650     qfc->UpdateFontList();
653 int PR_CALLBACK
654 gfxQuartzFontCache::PrefChangedCallback(const char *aPrefName, void *closure)
656     // XXX this could be made to only clear out the cache for the prefs that were changed
657     // but it probably isn't that big a deal.
658     gfxQuartzFontCache *qfc = static_cast<gfxQuartzFontCache *>(closure);
659     qfc->mPrefFonts.Clear();
660     return 0;
663 MacOSFontEntry*
664 gfxQuartzFontCache::GetDefaultFont(const gfxFontStyle* aStyle)
666     NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
667     nsAutoString familyName;
668     
669     GetStringForNSString(defaultFamily, familyName);
670     return FindFontForFamily(familyName, aStyle);
673 struct FontListData {
674     FontListData(const nsACString& aLangGroup,
675                  const nsACString& aGenericFamily,
676                  nsStringArray& aListOfFonts) :
677         mLangGroup(aLangGroup), mGenericFamily(aGenericFamily),
678         mListOfFonts(aListOfFonts) {}
679     const nsACString& mLangGroup;
680     const nsACString& mGenericFamily;
681     nsStringArray& mListOfFonts;
684 PLDHashOperator PR_CALLBACK
685 gfxQuartzFontCache::HashEnumFuncForFamilies(nsStringHashKey::KeyType aKey,
686                                             nsRefPtr<MacOSFamilyEntry>& aFamilyEntry,
687                                             void* aUserArg)
689     FontListData *data = (FontListData*)aUserArg;
690     data->mListOfFonts.AppendString(aFamilyEntry->Name());
691     return PL_DHASH_NEXT;
694 void
695 gfxQuartzFontCache::GetFontList (const nsACString& aLangGroup,
696                                  const nsACString& aGenericFamily,
697                                  nsStringArray& aListOfFonts)
699     FontListData data(aLangGroup, aGenericFamily, aListOfFonts);
701     mFontFamilies.Enumerate(gfxQuartzFontCache::HashEnumFuncForFamilies, &data);
703     aListOfFonts.Sort();
704     aListOfFonts.Compact();
707 MacOSFontEntry*  
708 gfxQuartzFontCache::FindFontForChar(const PRUint32 aCh, gfxAtsuiFont *aPrevFont)
710     // is codepoint with no matching font? return null immediately
711     if (mCodepointsWithNoFonts.test(aCh)) {
712         return nsnull;
713     }
715     FontSearch data(aCh, aPrevFont);
717     // iterate over all font families to find a font that support the character
718     mFontFamilies.Enumerate(gfxQuartzFontCache::FindFontForCharProc, &data);
720     // no match? add to set of non-matching codepoints
721     if (!data.bestMatch) {
722         mCodepointsWithNoFonts.set(aCh);
723     }
724     
725     return data.bestMatch;
728 PLDHashOperator PR_CALLBACK 
729 gfxQuartzFontCache::FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr<MacOSFamilyEntry>& aFamilyEntry,
730      void* userArg)
732     FontSearch *data = (FontSearch*)userArg;
734     // evaluate all fonts in this family for a match
735     aFamilyEntry->FindFontForChar(data);
736     return PL_DHASH_NEXT;
739 MacOSFamilyEntry* 
740 gfxQuartzFontCache::FindFamily(const nsAString& aFamily)
742     nsAutoString key;
743     nsRefPtr<MacOSFamilyEntry> familyEntry;
744     GenerateFontListKey(aFamily, key);
745     
746     if (mFontFamilies.Get(key, &familyEntry) || mLocalizedFamilies.Get(key, &familyEntry)) {
747         return familyEntry;
748     }
749     return nsnull;
751     
752 MacOSFontEntry*
753 gfxQuartzFontCache::FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle)
755     MacOSFamilyEntry *familyEntry = FindFamily(aFamily);
756     
757     if (familyEntry)
758         return familyEntry->FindFont(aStyle);
760     return nsnull;
763 PRInt32 
764 gfxQuartzFontCache::AppleWeightToCSSWeight(PRInt32 aAppleWeight)
766     if (aAppleWeight < 1)
767         aAppleWeight = 1;
768     else if (aAppleWeight > kAppleMaxWeight)
769         aAppleWeight = kAppleMaxWeight;
770     return gAppleWeightToCSSWeight[aAppleWeight];
773 PRBool
774 gfxQuartzFontCache::GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<MacOSFamilyEntry> > *array)
776     return mPrefFonts.Get(PRUint32(aLangGroup), array);
779 void
780 gfxQuartzFontCache::SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<MacOSFamilyEntry> >& array)
782     mPrefFonts.Put(PRUint32(aLangGroup), array);