Bug 460926 A11y hierachy is broken on Ubuntu 8.10 (GNOME 2.24), r=Evan.Yan sr=roc
[wine-gecko.git] / gfx / thebes / src / gfxQuartzFontCache.mm
blob2461a91b79db14efda7ea951a38e3057168497ba
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"
47 #include "gfxUserFontSet.h"
49 #include "nsIPref.h"  // for pref changes callback notification
50 #include "nsServiceManagerUtils.h"
52 #include "nsDirectoryServiceUtils.h"
53 #include "nsDirectoryServiceDefs.h"
54 #include "nsISimpleEnumerator.h"
56 #include <unistd.h>
57 #include <time.h>
59 // _atsFontID is private; add it in our new category to NSFont
60 @interface NSFont (MozillaCategory)
61 - (ATSUFontID)_atsFontID;
62 @end
64 // font info loader constants
65 static const PRUint32 kDelayBeforeLoadingCmaps = 8 * 1000; // 8secs
66 static const PRUint32 kIntervalBetweenLoadingCmaps = 150; // 150ms
67 static const PRUint32 kNumFontsPerSlice = 10; // read in info 10 fonts at a time
69 #define INDEX_FONT_POSTSCRIPT_NAME 0
70 #define INDEX_FONT_FACE_NAME 1
71 #define INDEX_FONT_WEIGHT 2
72 #define INDEX_FONT_TRAITS 3
74 static const int kAppleMaxWeight = 14;
76 static const int gAppleWeightToCSSWeight[] = {
77     0,
78     1, // 1. 
79     1, // 2.  W1, ultralight
80     2, // 3.  W2, extralight
81     3, // 4.  W3, light
82     4, // 5.  W4, semilight
83     5, // 6.  W5, medium
84     6, // 7.
85     6, // 8.  W6, semibold
86     7, // 9.  W7, bold
87     8, // 10. W8, extrabold
88     8, // 11.
89     9, // 12. W9, ultrabold
90     9, // 13
91     9  // 14
95 static void GetStringForNSString(const NSString *aSrc, nsAString& aDist)
97     aDist.SetLength([aSrc length]);
98     [aSrc getCharacters:aDist.BeginWriting()];
101 static NSString* GetNSStringForString(const nsAString& aSrc)
103     return [NSString stringWithCharacters:aSrc.BeginReading()
104                      length:aSrc.Length()];
107 static PRLogModuleInfo *gFontInfoLog = PR_NewLogModule("fontInfoLog");
109 void
110 gfxQuartzFontCache::GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult)
112     aResult = aKeyName;
113     ToLowerCase(aResult);
116 /* MacOSFontEntry */
117 #pragma mark-
119 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, 
120                                 PRInt32 aAppleWeight, PRUint32 aTraits, MacOSFamilyEntry *aFamily)
121     : gfxFontEntry(aPostscriptName), mTraits(aTraits), mFamily(aFamily), mATSUFontID(0),
122         mATSUIDInitialized(0)
124     mWeight = gfxQuartzFontCache::AppleWeightToCSSWeight(aAppleWeight) * 100;
126     mItalic = (mTraits & NSItalicFontMask ? 1 : 0);
127     mFixedPitch = (mTraits & NSFixedPitchFontMask ? 1 : 0);
130 MacOSFontEntry::MacOSFontEntry(ATSUFontID aFontID, PRUint16 aWeight, PRUint16 aStretch, PRUint32 aItalicStyle, gfxUserFontData *aUserFontData)
132     // xxx - stretch is basically ignored for now
133     
134     mATSUIDInitialized = PR_TRUE;
135     mATSUFontID = aFontID;
136     mUserFontData = aUserFontData;
137     mWeight = aWeight;
138     mStretch = aStretch;
139     mFixedPitch = PR_FALSE; // xxx - do we need this for downloaded fonts?
140     mItalic = (aItalicStyle & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0;
141     
142     mTraits = (mItalic ? NSItalicFontMask : NSUnitalicFontMask) |
143               (mFixedPitch ? NSFixedPitchFontMask : 0) |
144               (mWeight >= 600 ? NSBoldFontMask : NSUnboldFontMask);
146     // get the postscript name
147     OSStatus err;
148     NSString *psname = NULL;
150     // now lookup the Postscript name
151     err = ATSFontGetPostScriptName((ATSFontRef) aFontID, kATSOptionFlagsDefault, (CFStringRef*) (&psname));
152     if (err == noErr) {
153         GetStringForNSString(psname, mName);
154         [psname release];
155     } else {
156         mIsValid = PR_FALSE;
157 #ifdef DEBUG
158         char warnBuf[1024];
159         sprintf(warnBuf, "ATSFontGetPostScriptName err = %d", (PRInt32)err);
160         NS_WARNING(warnBuf);
161 #endif        
162     }
165 const nsString& 
166 MacOSFontEntry::FamilyName()
168     return mFamily->Name();
171 ATSUFontID MacOSFontEntry::GetFontID() 
173     if (!mATSUIDInitialized) {
174         mATSUIDInitialized = PR_TRUE;
175         NSString *psname = GetNSStringForString(mName);
176         NSFont *font = [NSFont fontWithName:psname size:0.0];
177         if (font) mATSUFontID = [font _atsFontID];
178     }
179     return mATSUFontID; 
182 // ATSUI requires AAT-enabled fonts to render complex scripts correctly.
183 // For now, simple clear out the cmap codepoints for fonts that have
184 // codepoints for complex scripts. (Bug 361986)
186 enum eComplexScript {
187     eComplexScriptArabic,
188     eComplexScriptIndic,
189     eComplexScriptTibetan
192 struct ScriptRange {
193     eComplexScript   script;
194     PRUint32         rangeStart;
195     PRUint32         rangeEnd;
198 const ScriptRange gScriptsThatRequireShaping[] = {
199     { eComplexScriptArabic, 0x0600, 0x077F },   // Basic Arabic and Arabic Supplement
200     { eComplexScriptIndic, 0x0900, 0x0D7F },     // Indic scripts - Devanagari, Bengali, ..., Malayalam
201     { eComplexScriptTibetan, 0x0F00, 0x0FFF }     // Tibetan
202     // Thai seems to be "renderable" without AAT morphing tables
203     // xxx - Lao, Khmer?
206 nsresult
207 MacOSFontEntry::ReadCMAP()
209     OSStatus status;
210     ByteCount size, cmapSize;
212     if (mCmapInitialized) return NS_OK;
213     ATSUFontID fontID = GetFontID();
215     // attempt this once, if errors occur leave a blank cmap
216     mCmapInitialized = PR_TRUE;
218     status = ATSFontGetTable(fontID, 'cmap', 0, 0, 0, &size);
219     cmapSize = size;
220     //printf( "cmap size: %s %d", NS_ConvertUTF16toUTF8(mName).get(), size );
221 #if DEBUG
222     if (status != noErr) {
223         char warnBuf[1024];
224         sprintf(warnBuf, "ATSFontGetTable returned %d for (%s)", (PRInt32)status, NS_ConvertUTF16toUTF8(mName).get());
225         NS_WARNING(warnBuf);
226     }   
227 #endif    
228     NS_ENSURE_TRUE(status == noErr, NS_ERROR_FAILURE);
230     nsAutoTArray<PRUint8,16384> buffer;
231     if (!buffer.AppendElements(size))
232         return NS_ERROR_OUT_OF_MEMORY;
233     PRUint8 *cmap = buffer.Elements();
235     status = ATSFontGetTable(fontID, 'cmap', 0, size, cmap, &size);
236     NS_ENSURE_TRUE(status == noErr, NS_ERROR_FAILURE);
238     nsresult rv = NS_ERROR_FAILURE;
239     PRPackedBool  unicodeFont, symbolFont; // currently ignored
240     rv = gfxFontUtils::ReadCMAP(cmap, size, mCharacterMap, unicodeFont, symbolFont);
242     // for complex scripts, check for the presence of mort/morx
243     PRBool checkedForMorphTable = PR_FALSE, hasMorphTable = PR_FALSE;
245     PRUint32 s, numScripts = sizeof(gScriptsThatRequireShaping) / sizeof(ScriptRange);
247     for (s = 0; s < numScripts; s++) {
248         eComplexScript  whichScript = gScriptsThatRequireShaping[s].script;
249         
250         // check to see if the cmap includes complex script codepoints
251         if (mCharacterMap.TestRange(gScriptsThatRequireShaping[s].rangeStart, gScriptsThatRequireShaping[s].rangeEnd)) {
252             
253             // check for mort/morx table, if haven't already
254             if (!checkedForMorphTable) {
255                 status = ATSFontGetTable(fontID, 'morx', 0, 0, 0, &size);
256                 if (status == noErr) {
257                     checkedForMorphTable = PR_TRUE;
258                     hasMorphTable = PR_TRUE;
259                 } else {
260                     // check for a mort table
261                     status = ATSFontGetTable(fontID, 'mort', 0, 0, 0, &size);
262                     checkedForMorphTable = PR_TRUE;
263                     if (status == noErr) {
264                         hasMorphTable = PR_TRUE;
265                     }
266                 }
267             }
268             
269             // rude hack - the Chinese STxxx fonts on 10.4 contain morx tables and Arabic glyphs but 
270             // lack the proper info for shaping Arabic, so exclude explicitly, ick
271             if (whichScript == eComplexScriptArabic && hasMorphTable) {
272                 if (mName.CharAt(0) == 'S' && mName.CharAt(1) == 'T') {
273                     mCharacterMap.ClearRange(gScriptsThatRequireShaping[s].rangeStart, gScriptsThatRequireShaping[s].rangeEnd);
274                 }
275             }
277             // general exclusion - if no morph table, exclude codepoints
278             if (!hasMorphTable) {
279                 mCharacterMap.ClearRange(gScriptsThatRequireShaping[s].rangeStart, gScriptsThatRequireShaping[s].rangeEnd);
280             }
281         }
282     }
284     PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit-cmap) psname: %s, size: %d\n", 
285                                         NS_ConvertUTF16toUTF8(mName).get(), mCharacterMap.GetSize()));
286                                         
287     return rv;
291 /* MacOSFamilyEntry */
292 #pragma mark-
294 // helper class for adding other family names back into font cache
295 class AddOtherFamilyNameFunctor 
297 public:
298     AddOtherFamilyNameFunctor(gfxQuartzFontCache *aFontCache) :
299         mFontCache(aFontCache)
300     {}
302     void operator() (MacOSFamilyEntry *aFamilyEntry, nsAString& aOtherName) {
303         mFontCache->AddOtherFamilyName(aFamilyEntry, aOtherName);
304     }
306     gfxQuartzFontCache *mFontCache;
309 void MacOSFamilyEntry::LocalizedName(nsAString& aLocalizedName)
311     // no other names ==> only one name, just return it
312     if (!HasOtherFamilyNames()) {
313         aLocalizedName = mName;
314         return;
315     }
317     NSFontManager *fontManager = [NSFontManager sharedFontManager];
319     // dig out the localized family name
320     NSString *family = GetNSStringForString(mName);
321     NSString *localizedFamily = [fontManager localizedNameForFamily:family face:nil];
323     if (localizedFamily) {
324         GetStringForNSString(localizedFamily, aLocalizedName);
325     } else {
326         // failed to get a localized name, just use the canonical name
327         aLocalizedName = mName;
328     }
331 PRBool MacOSFamilyEntry::HasOtherFamilyNames()
333     // need to read in other family names to determine this
334     if (!mOtherFamilyNamesInitialized) {
335         AddOtherFamilyNameFunctor addOtherNames(gfxQuartzFontCache::SharedFontCache());
336         ReadOtherFamilyNames(addOtherNames);  // sets mHasOtherFamilyNames
337     }
338     return mHasOtherFamilyNames;
341 static const PRUint32 kTraits_NonNormalWidthMask = NSNarrowFontMask | NSExpandedFontMask | 
342                             NSCondensedFontMask | NSCompressedFontMask | NSFixedPitchFontMask;
344 MacOSFontEntry*
345 MacOSFamilyEntry::FindFont(const gfxFontStyle* aStyle, PRBool& aNeedsBold)
347     return static_cast<MacOSFontEntry*> (FindFontForStyle(*aStyle, aNeedsBold));
350 MacOSFontEntry*
351 MacOSFamilyEntry::FindFont(const nsAString& aPostscriptName)
353     // find the font using a simple linear search
354     PRUint32 numFonts = mAvailableFonts.Length();
355     for (PRUint32 i = 0; i < numFonts; i++) {
356         MacOSFontEntry *fe = mAvailableFonts[i];
357         if (fe->Name() == aPostscriptName)
358             return fe;
359     }
360     return nsnull;
363 void
364 MacOSFamilyEntry::FindFontForChar(FontSearch *aMatchData)
366     // xxx - optimization point - keep a bit vector with the union of supported unicode ranges
367     // by all fonts for this family and bail immediately if the character is not in any of
368     // this family's cmaps
370     // iterate over fonts
371     PRUint32 numFonts = mAvailableFonts.Length();
372     for (PRUint32 i = 0; i < numFonts; i++) {
373         MacOSFontEntry *fe = mAvailableFonts[i];
374         PRInt32 rank = 0;
376         if (fe->TestCharacterMap(aMatchData->ch)) {
377             rank += 20;
378         }
380         // if we didn't match any characters don't bother wasting more time with this face.
381         if (rank == 0)
382             continue;
383             
384         // omitting from original windows code -- family name, lang group, pitch
385         // not available in current FontEntry implementation
387         if (aMatchData->fontToMatch) { 
388             const gfxFontStyle *style = aMatchData->fontToMatch->GetStyle();
389             
390             // italics
391             if (fe->IsItalic() && 
392                     (style->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0) {
393                 rank += 5;
394             }
395             
396             // weight
397             PRInt8 baseWeight, weightDistance;
398             style->ComputeWeightAndOffset(&baseWeight, &weightDistance);
400             // xxx - not entirely correct, the one unit of weight distance reflects 
401             // the "next bolder/lighter face"
402             PRUint32 targetWeight = (baseWeight * 100) + (weightDistance * 100);
404             PRUint32 entryWeight = fe->Weight();
405             if (entryWeight == targetWeight) {
406                 rank += 5;
407             } else {
408                 PRUint32 diffWeight = abs(entryWeight - targetWeight);
409                 if (diffWeight <= 100)  // favor faces close in weight
410                     rank += 2;
411             }
412         } else {
413             // if no font to match, prefer non-bold, non-italic fonts
414             if (!fe->IsItalic() && !fe->IsBold())
415                 rank += 5;
416         }
417         
418         // xxx - add whether AAT font with morphing info for specific lang groups
419         
420         if (rank > aMatchData->matchRank
421             || (rank == aMatchData->matchRank && Compare(fe->Name(), aMatchData->bestMatch->Name()) > 0)) 
422         {
423             aMatchData->bestMatch = fe;
424             aMatchData->matchRank = rank;
425         }
427     }
430 PRBool
431 MacOSFamilyEntry::FindFontsWithTraits(gfxFontEntry* aFontsForWeights[], PRUint32 aPosTraitsMask, 
432                                         PRUint32 aNegTraitsMask)
434     PRBool found = PR_FALSE;
436     // iterate over fonts
437     PRUint32 numFonts = mAvailableFonts.Length();
438     for (PRUint32 i = 0; i < numFonts; i++) {
439         MacOSFontEntry *fe = mAvailableFonts[i];
440         
441         // if traits match, add to list of fonts
442         PRUint32 traits = fe->Traits();
443         
444         // aPosTraitsMask == 0 ==> match all
445         if ((!aPosTraitsMask || (traits & aPosTraitsMask)) && !(traits & aNegTraitsMask)) {
446             PRInt32 weight = fe->Weight() / 100;
447             NS_ASSERTION(weight >= 1 && weight <= 9, "bogus font weight value!");
448             
449             // always prefer the first font for a given weight, helps deal a bit with 
450             // families with lots of faces (e.g. Minion Pro)
451             if (!aFontsForWeights[weight]) {
452                 aFontsForWeights[weight] = fe;
453                 found = PR_TRUE;
454             }
455         }
456     }
457     return found;
460 PRBool 
461 MacOSFamilyEntry::FindWeightsForStyle(gfxFontEntry* aFontsForWeights[], const gfxFontStyle& aFontStyle)
463     // short-circuit the single face per family case
464     if (mAvailableFonts.Length() == 1) {
465         MacOSFontEntry *fe = mAvailableFonts[0];
466         PRUint32 weight = fe->Weight() / 100;
467         NS_ASSERTION(weight >= 1 && weight <= 9, "bogus font weight value!");
468         aFontsForWeights[weight] = fe;
469         return PR_TRUE;
470     }
472     PRBool found = PR_FALSE;
473     PRBool isItalic = (aFontStyle.style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0;
475     // match italic faces
476     if (isItalic) {    
477         // first search for italic normal width fonts
478         found = FindFontsWithTraits(aFontsForWeights, NSItalicFontMask, kTraits_NonNormalWidthMask);
479         
480         // if not found, italic any width ones
481         if (!found) {
482             found = FindFontsWithTraits(aFontsForWeights, NSItalicFontMask, 0);        
483         }
484     }
486     // match non-italic faces, if no italic faces fall through here
487     if (!found) {
488         // look for normal width fonts
489         found = FindFontsWithTraits(aFontsForWeights, NSUnitalicFontMask, kTraits_NonNormalWidthMask);
490         
491         // if not found, any face will do
492         if (!found) {
493             found = FindFontsWithTraits(aFontsForWeights, NSUnitalicFontMask, 0);        
494         } 
495     }
497     // still not found?!? family must only contain italic fonts when looking for a normal 
498     // face, just use the whole list
499     if (!found) {
500         found = FindFontsWithTraits(aFontsForWeights, 0, 0);
501     }
502     NS_ASSERTION(found, "Font family containing no faces");
504     return found;
507 static NSString* CreateNameFromBuffer(const UInt8 *aBuf, ByteCount aLength, 
508         FontPlatformCode aPlatformCode, FontScriptCode aScriptCode, FontLanguageCode aLangCode)
510     CFStringRef outName = NULL;
512     if (aPlatformCode == kFontMacintoshPlatform) {
513         TextEncoding encoding;
514         OSStatus err = GetTextEncodingFromScriptInfo(aScriptCode, aLangCode, 
515                                                         kTextRegionDontCare, &encoding);
516         if (err) {
517             // some fonts are sloppy about the language code (e.g Arial Hebrew, Corsiva Hebrew)
518             // try again without the lang code to avoid bad combinations
519             OSStatus err = GetTextEncodingFromScriptInfo(aScriptCode, kTextLanguageDontCare, 
520                                                         kTextRegionDontCare, &encoding);
521             if (err) return nil;
522         }
523         outName = CFStringCreateWithBytes(kCFAllocatorDefault, aBuf, 
524                                             aLength, (CFStringEncoding) encoding, false);
525     } else if (aPlatformCode == kFontUnicodePlatform) {
526         outName = CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)aBuf, aLength/2);    
527     } else if (aPlatformCode == kFontMicrosoftPlatform) {
528         if (aScriptCode == 0) {
529             outName = CFStringCreateWithBytes(kCFAllocatorDefault, aBuf, 
530                                                 aLength, kCFStringEncodingUTF16BE, false);
531         } else {
532             outName = CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)aBuf, aLength/2);    
533         }
534     }
536     return (NSString*) outName;
539 // 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
540 enum {
541   kMozillaFontPreferredFamilyName            = 16,
544 // xxx - rather than use ATSUI calls, probably faster to load name table directly, 
545 // this avoids copying around strings that are of no interest
547 // returns true if other names were found, false otherwise
548 static PRBool ReadOtherFamilyNamesForFace(AddOtherFamilyNameFunctor& aOtherFamilyFunctor, MacOSFamilyEntry *aFamilyEntry,
549                                         NSString *familyName, ATSUFontID fontID, bool useFullName = false)
551     OSStatus err;
552     ItemCount i, nameCount;
553     PRBool foundNames = PR_FALSE;
555     if (fontID == kATSUInvalidFontID)
556         return foundNames;
558     err = ATSUCountFontNames(fontID, &nameCount);
559     if (err != noErr) 
560         return foundNames;
562     for (i = 0; i < nameCount; i++) {
564         FontNameCode nameCode;
565         FontPlatformCode platformCode;
566         FontScriptCode scriptCode;
567         FontLanguageCode langCode;
568         const ByteCount kBufLength = 2048;
569         char buf[kBufLength];
570         ByteCount len;
572         err = ATSUGetIndFontName(fontID, i, kBufLength, buf, &len, &nameCode, &platformCode, &scriptCode, &langCode);
573         // couldn't find a font name? just continue to the next name table entry
574         if (err == kATSUNoFontNameErr) 
575             continue;
576         // any other error, bail
577         if (err != noErr) 
578             return foundNames;
580         if (useFullName) {
581             if (nameCode != kFontFullName)
582                 continue;
583         } else {
584             if (!(nameCode == kFontFamilyName || nameCode == kMozillaFontPreferredFamilyName)) 
585                 continue; 
586         }
587         if (len >= kBufLength) continue; 
588         buf[len] = 0;
590         NSString *name = CreateNameFromBuffer((UInt8*)buf, len, platformCode, scriptCode, langCode);
592         // add if not same as canonical family name or already in list of names
593         if (name) {
595             if (![name isEqualToString:familyName]) {
596                 nsAutoString otherFamilyName;
597                 GetStringForNSString(name, otherFamilyName);
598                 aOtherFamilyFunctor(aFamilyEntry, otherFamilyName);
599                 foundNames = PR_TRUE;
600             }
602             [name release];
603         }
604     }
606     return foundNames;
609 void
610 MacOSFamilyEntry::ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor)
612     if (mOtherFamilyNamesInitialized) 
613         return;
614     mOtherFamilyNamesInitialized = PR_TRUE;
616     NSString *familyName = GetNSStringForString(mName);
618     // read in other family names for the first face in the list
619     MacOSFontEntry *fe = mAvailableFonts[0];
621     mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aOtherFamilyFunctor, this, familyName, fe->GetFontID());
623     // read in other names for the first face in the list with the assumption
624     // that if extra names don't exist in that face then they don't exist in
625     // other faces for the same font
626     if (mHasOtherFamilyNames) {
627         PRUint32 numFonts, i;
628         
629         // read in names for all faces, needed to catch cases where
630         // fonts all family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
631         numFonts = mAvailableFonts.Length();
632         for (i = 1; i < numFonts; i++) {
633             fe = mAvailableFonts[i];
634             ReadOtherFamilyNamesForFace(aOtherFamilyFunctor, this, familyName, fe->GetFontID());
635         }
636     }
639 /* SingleFaceFamily */
640 #pragma mark-
642 void SingleFaceFamily::LocalizedName(nsAString& aLocalizedName)
644     MacOSFontEntry *fontEntry;
646     // use the display name of the single face
647     fontEntry = mAvailableFonts[0];
648     if (!fontEntry) 
649         return;
650         
651     NSFont *font = [NSFont fontWithName:GetNSStringForString(fontEntry->Name()) size:0.0];
652     if (!font)
653         return;
655     NSString *fullname = [font displayName];
656     if (fullname) {
657         GetStringForNSString(fullname, aLocalizedName);
658     }
661 void SingleFaceFamily::ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor)
663     if (mOtherFamilyNamesInitialized) 
664         return;
665     mOtherFamilyNamesInitialized = PR_TRUE;
667     NSString *familyName = GetNSStringForString(mName);
669     // read in other family names for the first face in the list
670     MacOSFontEntry *fe = mAvailableFonts[0];
672     // read in other names, using the full font names as the family names
673     mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aOtherFamilyFunctor, this, familyName, fe->GetFontID(), true);    
676 /* gfxQuartzFontCache */
677 #pragma mark-
679 gfxQuartzFontCache *gfxQuartzFontCache::sSharedFontCache = nsnull;
681 gfxQuartzFontCache::gfxQuartzFontCache()
682     : mStartIndex(0), mIncrement(kNumFontsPerSlice), mNumFamilies(0)
684     mATSGeneration = PRUint32(kATSGenerationInitial);
686     mFontFamilies.Init(100);
687     mOtherFamilyNames.Init(30);
688     mOtherFamilyNamesInitialized = PR_FALSE;
689     mPrefFonts.Init(10);
691     InitFontList();
692     ::ATSFontNotificationSubscribe(ATSNotification,
693                                    kATSFontNotifyOptionDefault,
694                                    (void*)this, nsnull);
696     // pref changes notification setup
697     nsCOMPtr<nsIPref> pref = do_GetService(NS_PREF_CONTRACTID);
698     pref->RegisterCallback("font.", PrefChangedCallback, this);
699     pref->RegisterCallback("font.name-list.", PrefChangedCallback, this);
700     pref->RegisterCallback("intl.accept_languages", PrefChangedCallback, this);  // hmmmm...
704 const PRUint32 kNonNormalTraits = NSItalicFontMask | NSBoldFontMask | NSNarrowFontMask | NSExpandedFontMask | NSCondensedFontMask | NSCompressedFontMask;
706 void
707 gfxQuartzFontCache::InitFontList()
709     ATSGeneration currentGeneration = ATSGeneration();
710     
711     // need to ignore notifications after adding each font
712     if (mATSGeneration == currentGeneration)
713         return;
715     mATSGeneration = currentGeneration;
716     PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit) updating to generation: %d", mATSGeneration));                                         
717     
718     mFontFamilies.Clear();
719     mOtherFamilyNames.Clear();
720     mOtherFamilyNamesInitialized = PR_FALSE;
721     mPrefFonts.Clear();
722     mCodepointsWithNoFonts.reset();
723     CancelLoader();
725     // iterate over available families
726     NSFontManager *fontManager = [NSFontManager sharedFontManager];
727     NSEnumerator *families = [[fontManager availableFontFamilies] objectEnumerator];  // returns "canonical", non-localized family name
729     nsAutoString availableFamilyName, postscriptFontName;
730    
731     NSString *availableFamily = nil;
732     while ((availableFamily = [families nextObject])) {
734         // make a nsString
735         GetStringForNSString(availableFamily, availableFamilyName);
736         
737         // create a family entry
738         MacOSFamilyEntry *familyEntry = new MacOSFamilyEntry(availableFamilyName);
739         if (!familyEntry) break;
740         
741         // create a font entry for each face
742         NSArray *fontfaces = [fontManager availableMembersOfFontFamily:availableFamily];  // returns an array of [psname, style name, weight, traits] elements, goofy api
743         int faceCount = [fontfaces count];
744         int faceIndex;
746         for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
747             NSArray *face = [fontfaces objectAtIndex:faceIndex];
748             NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
749             PRInt32 weight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
750             PRUint32 traits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
751             
752             // 10.5 doesn't set NSUnitalicFontMask and NSUnboldFontMask - manually set these for consistency 
753             if (!(traits & NSBoldFontMask))
754                 traits |= NSUnboldFontMask;
755             if (!(traits & NSItalicFontMask))
756                 traits |= NSUnitalicFontMask;
757             
758             PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit) family: %s, psname: %s, face: %s, apple-weight: %d, css-weight: %d, traits: %8.8x\n", 
759                 [availableFamily UTF8String], [psname UTF8String], [[face objectAtIndex:INDEX_FONT_FACE_NAME] UTF8String], weight, gfxQuartzFontCache::AppleWeightToCSSWeight(weight), traits));
761             // make a nsString
762             GetStringForNSString(psname, postscriptFontName);
763         
764             // create a font entry
765             MacOSFontEntry *fontEntry = new MacOSFontEntry(postscriptFontName, weight, traits, familyEntry);
766             if (!fontEntry) break;            
767             
768             // insert into font entry array of family
769             familyEntry->AddFontEntry(fontEntry);
770         }
771         
772         // add the family entry to the hash table
773         ToLowerCase(availableFamilyName);
774         mFontFamilies.Put(availableFamilyName, familyEntry);
775     }
777     InitSingleFaceList();
779     // to avoid full search of font name tables, seed the other names table with localized names from 
780     // some of the prefs fonts which are accessed via their localized names.  changes in the pref fonts will only cause
781     // a font lookup miss earlier. this is a simple optimization, it's not required for correctness
782     PreloadNamesList();
784     // clean up various minor 10.4 font problems for specific fonts
785     if (gfxPlatformMac::GetPlatform()->OSXVersion() < MAC_OS_X_VERSION_10_5_HEX) {
786         // Cocoa calls report that italic faces exist for Courier and Helvetica,
787         // even though only bold faces exist so test for this using ATSUI id's (10.5 has proper faces)
788         EliminateDuplicateFaces(NS_LITERAL_STRING("Courier"));
789         EliminateDuplicateFaces(NS_LITERAL_STRING("Helvetica"));
790         
791         // Cocoa reports that Courier and Monaco are not fixed-pitch fonts
792         // so explicitly tweak these settings
793         SetFixedPitch(NS_LITERAL_STRING("Courier"));
794         SetFixedPitch(NS_LITERAL_STRING("Monaco"));
795     }
797     // initialize ranges of characters for which system-wide font search should be skipped
798     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
799     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
801     InitBadUnderlineList();
803     // start the delayed cmap loader
804     StartLoader(kDelayBeforeLoadingCmaps, kIntervalBetweenLoadingCmaps); 
808 void 
809 gfxQuartzFontCache::InitOtherFamilyNames()
811     mOtherFamilyNamesInitialized = PR_TRUE;
813     // iterate over all font families and read in other family names
814     mFontFamilies.Enumerate(gfxQuartzFontCache::InitOtherFamilyNamesProc, this);
816                                                          
817 PLDHashOperator PR_CALLBACK gfxQuartzFontCache::InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
818                                                          nsRefPtr<MacOSFamilyEntry>& aFamilyEntry,
819                                                          void* userArg)
821     gfxQuartzFontCache *fc = (gfxQuartzFontCache*) userArg;
822     AddOtherFamilyNameFunctor addOtherNames(fc);
823     aFamilyEntry->ReadOtherFamilyNames(addOtherNames);
824     return PL_DHASH_NEXT;
827 void
828 gfxQuartzFontCache::ReadOtherFamilyNamesForFamily(const nsAString& aFamilyName)
830     MacOSFamilyEntry *familyEntry = FindFamily(aFamilyName);
832     if (familyEntry) {
833         AddOtherFamilyNameFunctor addOtherNames(this);
834         familyEntry->ReadOtherFamilyNames(addOtherNames);
835     }
838 void
839 gfxQuartzFontCache::InitSingleFaceList()
841     nsAutoTArray<nsString, 10> singleFaceFonts;
842     gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts);
844     PRUint32 numFonts = singleFaceFonts.Length();
845     for (PRUint32 i = 0; i < numFonts; i++) {
846         nsAutoString availableFamilyName;
848         // lookup the font using NSFont    
849         NSString *faceName = GetNSStringForString(singleFaceFonts[i]);
850         NSFont *font = [NSFont fontWithName:faceName size:0.0];
851         if (font) {
852             NSString *availableFamily = [font familyName];
853             GetStringForNSString(availableFamily, availableFamilyName);
855             MacOSFamilyEntry *familyEntry = FindFamily(availableFamilyName);
856             if (familyEntry) {
857                 MacOSFontEntry *fontEntry = familyEntry->FindFont(singleFaceFonts[i]);
858                 if (fontEntry) {
859                     PRBool found;
860                     nsAutoString displayName, key;
861                     
862                     // use the display name the canonical name
863                     NSString *display = [font displayName];
864                     GetStringForNSString(display, displayName);
865                     GenerateFontListKey(displayName, key);
867                     // add only if doesn't exist already
868                     if (!(familyEntry = mFontFamilies.GetWeak(key, &found))) {
869                         familyEntry = new SingleFaceFamily(displayName);
870                         familyEntry->AddFontEntry(fontEntry);
871                         mFontFamilies.Put(key, familyEntry);
872                         PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit-singleface) family: %s, psname: %s\n", [display UTF8String], [faceName UTF8String]));
873                     }
874                     fontEntry->mFamily = familyEntry;
875                 }
876             }
877         }
878         
879     }
880        
883 void
884 gfxQuartzFontCache::PreloadNamesList()
886     nsAutoTArray<nsString, 10> preloadFonts;
887     gfxFontUtils::GetPrefsFontList("font.preload-names-list", preloadFonts);
889     PRUint32 numFonts = preloadFonts.Length();
890     for (PRUint32 i = 0; i < numFonts; i++) {
891         PRBool found;
892         nsAutoString key;
893         GenerateFontListKey(preloadFonts[i], key);
894         
895         // only search canonical names!
896         MacOSFamilyEntry *familyEntry = mFontFamilies.GetWeak(key, &found);
897         if (familyEntry) {
898             AddOtherFamilyNameFunctor addOtherNames(this);
899             familyEntry->ReadOtherFamilyNames(addOtherNames);
900         }
901     }
905 void 
906 gfxQuartzFontCache::EliminateDuplicateFaces(const nsAString& aFamilyName)
908     MacOSFamilyEntry *family = FindFamily(aFamilyName);
909     if (!family) return;
911     nsTArray<nsRefPtr<MacOSFontEntry> >& fontlist = family->GetFontList();
913     PRUint32 i, bold, numFonts, italicIndex;
914     MacOSFontEntry *italic, *nonitalic;
915     PRUint32 boldtraits[2] = { 0, NSBoldFontMask };
917     // if normal and italic have the same ATSUI id, delete italic
918     // if bold and bold-italic have the same ATSUI id, delete bold-italic
920     // two iterations, one for normal, one for bold
921     for (bold = 0; bold < 2; bold++) {
922         numFonts = fontlist.Length();
924         // find the non-italic face
925         nonitalic = nsnull;
926         for (i = 0; i < numFonts; i++) {
927             PRUint32 traits = fontlist[i]->Traits();
928             if (((traits & NSBoldFontMask) == boldtraits[bold]) && !(traits & NSItalicFontMask)) {
929                 nonitalic = fontlist[i];
930                 break;
931             }
932         }
934         // find the italic face
935         if (nonitalic) {
936             italic = nsnull;
937             for (i = 0; i < numFonts; i++) {
938                 PRUint32 traits = fontlist[i]->Traits();
939                 if (((traits & NSBoldFontMask) == boldtraits[bold]) && (traits & NSItalicFontMask)) {
940                     italic = fontlist[i];
941                     italicIndex = i;
942                     break;
943                 }
944             }
946             // if italic face and non-italic face have matching ATSUI id's, 
947             // the italic face is bogus so remove it
948             if (italic && italic->GetFontID() == nonitalic->GetFontID()) {
949                 fontlist.RemoveElementAt(italicIndex);
950             }
951         }
952     }
955 void 
956 gfxQuartzFontCache::SetFixedPitch(const nsAString& aFamilyName)
958     MacOSFamilyEntry *family = FindFamily(aFamilyName);
959     if (!family) return;
961     nsTArray<nsRefPtr<MacOSFontEntry> >& fontlist = family->GetFontList();
963     PRUint32 i, numFonts = fontlist.Length();
965     for (i = 0; i < numFonts; i++) {
966         fontlist[i]->mTraits |= NSFixedPitchFontMask;
967         fontlist[i]->mFixedPitch = 1;
968     }
971 void
972 gfxQuartzFontCache::InitBadUnderlineList()
974     nsAutoTArray<nsString, 10> blacklist;
975     gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist);
976     PRUint32 numFonts = blacklist.Length();
977     for (PRUint32 i = 0; i < numFonts; i++) {
978         PRBool found;
979         nsAutoString key;
980         GenerateFontListKey(blacklist[i], key);
982         MacOSFamilyEntry *familyEntry = mFontFamilies.GetWeak(key, &found);
983         if (familyEntry)
984             familyEntry->SetBadUnderlineFont(PR_TRUE);
985     }
988 PRBool 
989 gfxQuartzFontCache::ResolveFontName(const nsAString& aFontName, nsAString& aResolvedFontName)
991     MacOSFamilyEntry *family = FindFamily(aFontName);
992     if (family) {
993         aResolvedFontName = family->Name();
994         return PR_TRUE;
995     }
996     return PR_FALSE;
999 PRBool
1000 gfxQuartzFontCache::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
1002     MacOSFamilyEntry *family = FindFamily(aFontName);
1003     if (family) {
1004         family->LocalizedName(aFamilyName);
1005         return PR_TRUE;
1006     }
1008     // Gecko 1.8 used Quickdraw font api's which produce a slightly different set of "family"
1009     // names.  Try to resolve based on these names, in case this is stored in an old profile
1010     // 1.8: "Futura", "Futura Condensed" ==> 1.9: "Futura
1011     FMFont fmFont;
1013     // convert of a NSString
1014     NSString *fontName = GetNSStringForString(aFontName);
1016     // name ==> family id ==> old-style FMFont
1017     ATSFontFamilyRef fmFontFamily = ATSFontFamilyFindFromName((CFStringRef)fontName, kATSOptionFlagsDefault);
1018     OSStatus err = FMGetFontFromFontFamilyInstance(fmFontFamily, 0, &fmFont, nsnull);
1019     if (err != noErr || fmFont == kInvalidFont)
1020         return PR_FALSE;
1022     ATSFontRef atsFont = FMGetATSFontRefFromFont(fmFont);
1023     if (!atsFont)
1024         return PR_FALSE;
1026     NSString *psname;
1028     // now lookup the Postscript name
1029     err = ATSFontGetPostScriptName(atsFont, kATSOptionFlagsDefault, (CFStringRef*) (&psname));
1030     if (err != noErr)
1031         return PR_FALSE;
1033     // given an NSFont instance, Cocoa api's return the canonical family name
1034     NSString *canonicalfamily = [[NSFont fontWithName:psname size:0.0] familyName];
1035     [psname release];
1037     nsAutoString familyName;
1039     // lookup again using the canonical family name
1040     GetStringForNSString(canonicalfamily, familyName);
1041     family = FindFamily(familyName);
1042     if (family) {
1043         family->LocalizedName(aFamilyName);
1044         return PR_TRUE;
1045     }
1047     return PR_FALSE;
1050 void
1051 gfxQuartzFontCache::ATSNotification(ATSFontNotificationInfoRef aInfo,
1052                                     void* aUserArg)
1054     // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
1055     gfxQuartzFontCache *qfc = (gfxQuartzFontCache*)aUserArg;
1056     qfc->UpdateFontList();
1059 int PR_CALLBACK
1060 gfxQuartzFontCache::PrefChangedCallback(const char *aPrefName, void *closure)
1062     // XXX this could be made to only clear out the cache for the prefs that were changed
1063     // but it probably isn't that big a deal.
1064     gfxQuartzFontCache *qfc = static_cast<gfxQuartzFontCache *>(closure);
1065     qfc->mPrefFonts.Clear();
1066     return 0;
1069 MacOSFontEntry*
1070 gfxQuartzFontCache::GetDefaultFont(const gfxFontStyle* aStyle, PRBool& aNeedsBold)
1072     NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
1073     nsAutoString familyName;
1075     GetStringForNSString(defaultFamily, familyName);
1076     return FindFontForFamily(familyName, aStyle, aNeedsBold);
1079 struct FontListData {
1080     FontListData(const nsACString& aLangGroup,
1081                  const nsACString& aGenericFamily,
1082                  nsStringArray& aListOfFonts) :
1083         mLangGroup(aLangGroup), mGenericFamily(aGenericFamily),
1084         mListOfFonts(aListOfFonts) {}
1085     const nsACString& mLangGroup;
1086     const nsACString& mGenericFamily;
1087     nsStringArray& mListOfFonts;
1090 PLDHashOperator PR_CALLBACK
1091 gfxQuartzFontCache::HashEnumFuncForFamilies(nsStringHashKey::KeyType aKey,
1092                                             nsRefPtr<MacOSFamilyEntry>& aFamilyEntry,
1093                                             void *aUserArg)
1095     FontListData *data = (FontListData*)aUserArg;
1097     nsAutoString localizedFamilyName;
1098     aFamilyEntry->LocalizedName(localizedFamilyName);
1099     data->mListOfFonts.AppendString(localizedFamilyName);
1100     return PL_DHASH_NEXT;
1103 void
1104 gfxQuartzFontCache::GetFontList (const nsACString& aLangGroup,
1105                                  const nsACString& aGenericFamily,
1106                                  nsStringArray& aListOfFonts)
1108     FontListData data(aLangGroup, aGenericFamily, aListOfFonts);
1110     mFontFamilies.Enumerate(gfxQuartzFontCache::HashEnumFuncForFamilies, &data);
1112     aListOfFonts.Sort();
1113     aListOfFonts.Compact();
1116 struct FontFamilyListData {
1117     FontFamilyListData(nsTArray<nsRefPtr<MacOSFamilyEntry> >& aFamilyArray) 
1118         : mFamilyArray(aFamilyArray)
1119     {}
1121     static PLDHashOperator PR_CALLBACK AppendFamily(nsStringHashKey::KeyType aKey,
1122                                                     nsRefPtr<MacOSFamilyEntry>& aFamilyEntry,
1123                                                     void *aUserArg)
1124     {
1125         FontFamilyListData *data = (FontFamilyListData*)aUserArg;
1126         data->mFamilyArray.AppendElement(aFamilyEntry);
1127         return PL_DHASH_NEXT;
1128     }
1130     nsTArray<nsRefPtr<MacOSFamilyEntry> >& mFamilyArray;
1133 void
1134 gfxQuartzFontCache::GetFontFamilyList(nsTArray<nsRefPtr<MacOSFamilyEntry> >& aFamilyArray)
1136     FontFamilyListData data(aFamilyArray);
1137     mFontFamilies.Enumerate(FontFamilyListData::AppendFamily, &data);
1140 MacOSFontEntry*  
1141 gfxQuartzFontCache::FindFontForChar(const PRUint32 aCh, gfxFont *aPrevFont)
1143     // is codepoint with no matching font? return null immediately
1144     if (mCodepointsWithNoFonts.test(aCh)) {
1145         return nsnull;
1146     }
1148     // short-circuit system font fallback for U+FFFD, used to represent encoding errors
1149     // just use Lucida Grande (system font, guaranteed to be around)
1150     // this helps speed up pages with lots of encoding errors, binary-as-text, etc.
1151     if (aCh == 0xFFFD) {
1152         MacOSFontEntry* fontEntry;
1153         PRBool needsBold;  // ignored in the system fallback case
1154         
1155         if (aPrevFont) {
1156             fontEntry = FindFontForFamily(NS_LITERAL_STRING("Lucida Grande"), aPrevFont->GetStyle(), needsBold);
1157         } else {
1158             gfxFontStyle normalStyle;
1159             fontEntry = FindFontForFamily(NS_LITERAL_STRING("Lucida Grande"), &normalStyle, needsBold);
1160         }
1162         if (fontEntry && fontEntry->TestCharacterMap(aCh))
1163             return fontEntry;
1164     }
1166     FontSearch data(aCh, aPrevFont);
1168     // iterate over all font families to find a font that support the character
1169     mFontFamilies.Enumerate(gfxQuartzFontCache::FindFontForCharProc, &data);
1171     // no match? add to set of non-matching codepoints
1172     if (!data.bestMatch) {
1173         mCodepointsWithNoFonts.set(aCh);
1174     }
1176     return data.bestMatch;
1179 PLDHashOperator PR_CALLBACK 
1180 gfxQuartzFontCache::FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr<MacOSFamilyEntry>& aFamilyEntry,
1181      void *userArg)
1183     FontSearch *data = (FontSearch*)userArg;
1185     // evaluate all fonts in this family for a match
1186     aFamilyEntry->FindFontForChar(data);
1187     return PL_DHASH_NEXT;
1190 MacOSFamilyEntry* 
1191 gfxQuartzFontCache::FindFamily(const nsAString& aFamily)
1193     nsAutoString key;
1194     MacOSFamilyEntry *familyEntry;
1195     PRBool found;
1196     GenerateFontListKey(aFamily, key);
1198     // lookup in canonical (i.e. English) family name list
1199     if ((familyEntry = mFontFamilies.GetWeak(key, &found))) {
1200         return familyEntry;
1201     }
1203     // lookup in other family names list (mostly localized names)
1204     if ((familyEntry = mOtherFamilyNames.GetWeak(key, &found))) {
1205         return familyEntry;
1206     }
1208     // name not found and other family names not yet fully initialized so
1209     // initialize the rest of the list and try again.  this is done lazily
1210     // since reading name table entries is expensive
1211     if (!mOtherFamilyNamesInitialized) {
1212         InitOtherFamilyNames();
1213         if ((familyEntry = mOtherFamilyNames.GetWeak(key, &found))) {
1214             return familyEntry;
1215         }
1216     }
1218     return nsnull;
1221 MacOSFontEntry*
1222 gfxQuartzFontCache::FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, PRBool& aNeedsBold)
1224     MacOSFamilyEntry *familyEntry = FindFamily(aFamily);
1226     aNeedsBold = PR_FALSE;
1228     if (familyEntry)
1229         return familyEntry->FindFont(aStyle, aNeedsBold);
1231     return nsnull;
1234 PRInt32 
1235 gfxQuartzFontCache::AppleWeightToCSSWeight(PRInt32 aAppleWeight)
1237     if (aAppleWeight < 1)
1238         aAppleWeight = 1;
1239     else if (aAppleWeight > kAppleMaxWeight)
1240         aAppleWeight = kAppleMaxWeight;
1241     return gAppleWeightToCSSWeight[aAppleWeight];
1244 PRBool
1245 gfxQuartzFontCache::GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<MacOSFamilyEntry> > *array)
1247     return mPrefFonts.Get(PRUint32(aLangGroup), array);
1250 void
1251 gfxQuartzFontCache::SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<MacOSFamilyEntry> >& array)
1253     mPrefFonts.Put(PRUint32(aLangGroup), array);
1256 void 
1257 gfxQuartzFontCache::AddOtherFamilyName(MacOSFamilyEntry *aFamilyEntry, nsAString& aOtherFamilyName)
1259     nsAutoString key;
1260     PRBool found;
1261     GenerateFontListKey(aOtherFamilyName, key);
1263     if (!mOtherFamilyNames.GetWeak(key, &found)) {
1264         mOtherFamilyNames.Put(key, aFamilyEntry);
1265         PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit-otherfamily) canonical family: %s, other family: %s\n", 
1266                                             NS_ConvertUTF16toUTF8(aFamilyEntry->Name()).get(), 
1267                                             NS_ConvertUTF16toUTF8(aOtherFamilyName).get()));
1268     }
1271 gfxFontEntry* 
1272 gfxQuartzFontCache::LookupLocalFont(const nsAString& aFontName)
1274     NSString *faceName = GetNSStringForString(aFontName);
1275     NSFont *font = [NSFont fontWithName:faceName size:0.0];
1277     if (font) {
1278         nsAutoString availableFamilyName;
1279         NSString *availableFamily = [font familyName];
1280         GetStringForNSString(availableFamily, availableFamilyName);
1282         MacOSFamilyEntry *familyEntry = FindFamily(availableFamilyName);
1283         if (familyEntry) {
1284             MacOSFontEntry *fontEntry = familyEntry->FindFont(aFontName);
1285             return fontEntry;
1286         }
1287     }
1289     // didn't find the font
1290     return nsnull;
1293 // grumble, another non-publised Apple API dependency (found in Webkit code)
1294 // activated with this value, font will not be found via system lookup routines
1295 // it can only be used via the created ATSUFontID
1296 // needed to prevent one doc from finding a font used in a separate doc
1298 enum {
1299     kPrivateATSFontContextPrivate = 3
1302 class MacOSUserFontData : public gfxUserFontData {
1303 public:
1304     MacOSUserFontData(ATSFontContainerRef aContainerRef)
1305         : mContainerRef(aContainerRef)
1306     { }
1308     virtual ~MacOSUserFontData()
1309     {
1310         // deactivate font
1311         if (mContainerRef)
1312             ATSFontDeactivate(mContainerRef, NULL, kATSOptionFlagsDefault);
1313     }
1315     ATSFontContainerRef     mContainerRef;
1318 gfxFontEntry* 
1319 gfxQuartzFontCache::MakePlatformFont(const gfxFontEntry *aProxyEntry, 
1320                                      const PRUint8 *aFontData, PRUint32 aLength)
1322     OSStatus err;
1323     
1324     NS_ASSERTION(aFontData && aLength != 0, 
1325                  "MakePlatformFont called with null data ptr");
1326                  
1327     // do simple validation check on font data before 
1328     // attempting to activate it
1329     if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) {
1330 #if DEBUG
1331         char warnBuf[1024];
1332         const gfxProxyFontEntry *proxyEntry = 
1333             static_cast<const gfxProxyFontEntry*> (aProxyEntry);
1334         sprintf(warnBuf, "downloaded font error, invalid font data for (%s)",
1335                 NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get());
1336         NS_WARNING(warnBuf);
1337 #endif    
1338         return nsnull;
1339     }
1341     ATSUFontID fontID;
1342     ATSFontContainerRef containerRef;
1344     err = ATSFontActivateFromMemory(const_cast<PRUint8*>(aFontData), aLength, 
1345                                     kPrivateATSFontContextPrivate,
1346                                     kATSFontFormatUnspecified,
1347                                     NULL, 
1348                                     kATSOptionFlagsDoNotNotify, 
1349                                     &containerRef);
1351     if (err != noErr) {
1352 #if DEBUG
1353         char warnBuf[1024];
1354         const gfxProxyFontEntry *proxyEntry = 
1355             static_cast<const gfxProxyFontEntry*> (aProxyEntry);
1356         sprintf(warnBuf, "downloaded font error, ATSFontActivateFromMemory err: %d for (%s)",
1357                 PRInt32(err),
1358                 NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get());
1359         NS_WARNING(warnBuf);
1360 #endif    
1361         return nsnull;
1362     }
1364     mATSGeneration = ATSGeneration();
1366     // ignoring containers with multiple fonts, use the first face only for now
1367     err = ATSFontFindFromContainer(containerRef, kATSOptionFlagsDefault, 1, 
1368                                    (ATSFontRef*)&fontID, NULL);
1369     if (err != noErr) {
1370 #if DEBUG
1371         char warnBuf[1024];
1372         const gfxProxyFontEntry *proxyEntry = 
1373             static_cast<const gfxProxyFontEntry*> (aProxyEntry);
1374         sprintf(warnBuf, "downloaded font error, ATSFontFindFromContainer err: %d for (%s)",
1375                 PRInt32(err),
1376                 NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get());
1377         NS_WARNING(warnBuf);
1378 #endif  
1379         ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
1380         return nsnull;
1381     }
1382     
1383     // font entry will own this
1384     MacOSUserFontData *userFontData = new MacOSUserFontData(containerRef);
1385     
1386     if (!userFontData) {
1387         ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
1388         return nsnull;
1389     }
1391     PRUint16 w = aProxyEntry->mWeight;
1392     NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
1394     MacOSFontEntry *newFontEntry = 
1395         new MacOSFontEntry(fontID, w, aProxyEntry->mStretch, 
1396                            (PRUint32(aProxyEntry->mItalic) ? 
1397                                        FONT_STYLE_ITALIC : 
1398                                        FONT_STYLE_NORMAL), 
1399                            userFontData);
1401     if (!newFontEntry) {
1402         delete userFontData;
1403         return nsnull;
1404     }
1405     
1406     // if something is funky about this font, delete immediately
1407     if (newFontEntry && !newFontEntry->mIsValid) {
1408 #if DEBUG
1409         char warnBuf[1024];
1410         const gfxProxyFontEntry *proxyEntry = 
1411             static_cast<const gfxProxyFontEntry*> (aProxyEntry);
1412         sprintf(warnBuf, "downloaded font not loaded properly, removed face for (%s)", 
1413                 NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get());
1414         NS_WARNING(warnBuf);
1415 #endif    
1416         delete newFontEntry;
1417         return nsnull;
1418     }
1420     return newFontEntry;
1424 void 
1425 gfxQuartzFontCache::InitLoader()
1427     GetFontFamilyList(mFontFamiliesToLoad);
1428     mStartIndex = 0;
1429     mNumFamilies = mFontFamiliesToLoad.Length();
1432 PRBool 
1433 gfxQuartzFontCache::RunLoader()
1435     PRUint32 i, endIndex = (mStartIndex + mIncrement < mNumFamilies ? mStartIndex + mIncrement : mNumFamilies);
1437     // for each font family, load in various font info
1438     for (i = mStartIndex; i < endIndex; i++) {
1439         AddOtherFamilyNameFunctor addOtherNames(this);
1441         // load the cmap
1442         mFontFamiliesToLoad[i]->ReadCMAP();
1444         // read in other family names
1445         mFontFamiliesToLoad[i]->ReadOtherFamilyNames(addOtherNames);
1446     }
1448     mStartIndex += mIncrement;
1449     if (mStartIndex < mNumFamilies)
1450         return PR_FALSE;
1451     return PR_TRUE;
1454 void 
1455 gfxQuartzFontCache::FinishLoader()
1457     mFontFamiliesToLoad.Clear();
1458     mNumFamilies = 0;