Bug 463806 - [PATCH][@font-face] Downloaded font activation on Mac may fail due to...
[wine-gecko.git] / gfx / thebes / src / gfxQuartzFontCache.mm
bloba47eeacb7f66457b79e55154cfc3b22310982757
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-2008 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  *   Jonathan Kew <jfkthame@gmail.com>
12  * 
13  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
14  *
15  * Redistribution and use in source and binary forms, with or without
16  * modification, are permitted provided that the following conditions
17  * are met:
18  *
19  * 1.  Redistributions of source code must retain the above copyright
20  *     notice, this list of conditions and the following disclaimer.
21  * 2.  Redistributions in binary form must reproduce the above copyright
22  *     notice, this list of conditions and the following disclaimer in the
23  *     documentation and/or other materials provided with the distribution.
24  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
25  *     its contributors may be used to endorse or promote products derived
26  *     from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
29  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
30  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
31  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
32  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
33  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
34  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
35  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
37  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38  *
39  * ***** END LICENSE BLOCK ***** */
41 #include <Carbon.h>
43 #import <AppKit/AppKit.h>
45 #include "gfxPlatformMac.h"
46 #include "gfxQuartzFontCache.h"
47 #include "gfxAtsuiFonts.h"
48 #include "gfxUserFontSet.h"
50 #include "nsIPref.h"  // for pref changes callback notification
51 #include "nsServiceManagerUtils.h"
53 #include "nsDirectoryServiceUtils.h"
54 #include "nsDirectoryServiceDefs.h"
55 #include "nsISimpleEnumerator.h"
57 #include <unistd.h>
58 #include <time.h>
60 // _atsFontID is private; add it in our new category to NSFont
61 @interface NSFont (MozillaCategory)
62 - (ATSUFontID)_atsFontID;
63 @end
65 // font info loader constants
66 static const PRUint32 kDelayBeforeLoadingCmaps = 8 * 1000; // 8secs
67 static const PRUint32 kIntervalBetweenLoadingCmaps = 150; // 150ms
68 static const PRUint32 kNumFontsPerSlice = 10; // read in info 10 fonts at a time
70 #define INDEX_FONT_POSTSCRIPT_NAME 0
71 #define INDEX_FONT_FACE_NAME 1
72 #define INDEX_FONT_WEIGHT 2
73 #define INDEX_FONT_TRAITS 3
75 static const int kAppleMaxWeight = 14;
77 static const int gAppleWeightToCSSWeight[] = {
78     0,
79     1, // 1. 
80     1, // 2.  W1, ultralight
81     2, // 3.  W2, extralight
82     3, // 4.  W3, light
83     4, // 5.  W4, semilight
84     5, // 6.  W5, medium
85     6, // 7.
86     6, // 8.  W6, semibold
87     7, // 9.  W7, bold
88     8, // 10. W8, extrabold
89     8, // 11.
90     9, // 12. W9, ultrabold
91     9, // 13
92     9  // 14
96 static void GetStringForNSString(const NSString *aSrc, nsAString& aDist)
98     aDist.SetLength([aSrc length]);
99     [aSrc getCharacters:aDist.BeginWriting()];
102 static NSString* GetNSStringForString(const nsAString& aSrc)
104     return [NSString stringWithCharacters:aSrc.BeginReading()
105                      length:aSrc.Length()];
108 static PRLogModuleInfo *gFontInfoLog = PR_NewLogModule("fontInfoLog");
110 void
111 gfxQuartzFontCache::GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult)
113     aResult = aKeyName;
114     ToLowerCase(aResult);
117 /* MacOSFontEntry */
118 #pragma mark-
120 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, 
121                                PRInt32 aAppleWeight, PRUint32 aTraits, MacOSFamilyEntry *aFamily)
122     : gfxFontEntry(aPostscriptName), mTraits(aTraits), mFamily(aFamily), mATSUFontID(0),
123         mATSUIDInitialized(0)
125     mWeight = gfxQuartzFontCache::AppleWeightToCSSWeight(aAppleWeight) * 100;
127     mItalic = (mTraits & NSItalicFontMask ? 1 : 0);
128     mFixedPitch = (mTraits & NSFixedPitchFontMask ? 1 : 0);
131 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, ATSUFontID aFontID,
132                                PRUint16 aWeight, PRUint16 aStretch, PRUint32 aItalicStyle,
133                                gfxUserFontData *aUserFontData)
135     // xxx - stretch is basically ignored for now
137     mATSUIDInitialized = PR_TRUE;
138     mATSUFontID = aFontID;
139     mUserFontData = aUserFontData;
140     mWeight = aWeight;
141     mStretch = aStretch;
142     mFixedPitch = PR_FALSE; // xxx - do we need this for downloaded fonts?
143     mItalic = (aItalicStyle & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0;
144     
145     mTraits = (mItalic ? NSItalicFontMask : NSUnitalicFontMask) |
146               (mFixedPitch ? NSFixedPitchFontMask : 0) |
147               (mWeight >= 600 ? NSBoldFontMask : NSUnboldFontMask);
149     mName = aPostscriptName;
152 const nsString& 
153 MacOSFontEntry::FamilyName()
155     return mFamily->Name();
158 ATSUFontID MacOSFontEntry::GetFontID() 
160     if (!mATSUIDInitialized) {
161         mATSUIDInitialized = PR_TRUE;
162         NSString *psname = GetNSStringForString(mName);
163         NSFont *font = [NSFont fontWithName:psname size:0.0];
164         if (font) mATSUFontID = [font _atsFontID];
165     }
166     return mATSUFontID; 
169 // ATSUI requires AAT-enabled fonts to render complex scripts correctly.
170 // For now, simple clear out the cmap codepoints for fonts that have
171 // codepoints for complex scripts. (Bug 361986)
173 enum eComplexScript {
174     eComplexScriptArabic,
175     eComplexScriptIndic,
176     eComplexScriptTibetan
179 struct ScriptRange {
180     eComplexScript   script;
181     PRUint32         rangeStart;
182     PRUint32         rangeEnd;
185 const ScriptRange gScriptsThatRequireShaping[] = {
186     { eComplexScriptArabic, 0x0600, 0x077F },   // Basic Arabic and Arabic Supplement
187     { eComplexScriptIndic, 0x0900, 0x0D7F },     // Indic scripts - Devanagari, Bengali, ..., Malayalam
188     { eComplexScriptTibetan, 0x0F00, 0x0FFF }     // Tibetan
189     // Thai seems to be "renderable" without AAT morphing tables
190     // xxx - Lao, Khmer?
193 nsresult
194 MacOSFontEntry::ReadCMAP()
196     OSStatus status;
197     ByteCount size, cmapSize;
199     if (mCmapInitialized) return NS_OK;
200     ATSUFontID fontID = GetFontID();
202     // attempt this once, if errors occur leave a blank cmap
203     mCmapInitialized = PR_TRUE;
205     status = ATSFontGetTable(fontID, 'cmap', 0, 0, 0, &size);
206     cmapSize = size;
207     //printf( "cmap size: %s %d", NS_ConvertUTF16toUTF8(mName).get(), size );
208 #if DEBUG
209     if (status != noErr) {
210         char warnBuf[1024];
211         sprintf(warnBuf, "ATSFontGetTable returned %d for (%s)", (PRInt32)status, NS_ConvertUTF16toUTF8(mName).get());
212         NS_WARNING(warnBuf);
213     }   
214 #endif    
215     NS_ENSURE_TRUE(status == noErr, NS_ERROR_FAILURE);
217     nsAutoTArray<PRUint8,16384> buffer;
218     if (!buffer.AppendElements(size))
219         return NS_ERROR_OUT_OF_MEMORY;
220     PRUint8 *cmap = buffer.Elements();
222     status = ATSFontGetTable(fontID, 'cmap', 0, size, cmap, &size);
223     NS_ENSURE_TRUE(status == noErr, NS_ERROR_FAILURE);
225     nsresult rv = NS_ERROR_FAILURE;
226     PRPackedBool  unicodeFont, symbolFont; // currently ignored
227     rv = gfxFontUtils::ReadCMAP(cmap, size, mCharacterMap, unicodeFont, symbolFont);
229     // for complex scripts, check for the presence of mort/morx
230     PRBool checkedForMorphTable = PR_FALSE, hasMorphTable = PR_FALSE;
232     PRUint32 s, numScripts = sizeof(gScriptsThatRequireShaping) / sizeof(ScriptRange);
234     for (s = 0; s < numScripts; s++) {
235         eComplexScript  whichScript = gScriptsThatRequireShaping[s].script;
236         
237         // check to see if the cmap includes complex script codepoints
238         if (mCharacterMap.TestRange(gScriptsThatRequireShaping[s].rangeStart, gScriptsThatRequireShaping[s].rangeEnd)) {
239             
240             // check for mort/morx table, if haven't already
241             if (!checkedForMorphTable) {
242                 status = ATSFontGetTable(fontID, 'morx', 0, 0, 0, &size);
243                 if (status == noErr) {
244                     checkedForMorphTable = PR_TRUE;
245                     hasMorphTable = PR_TRUE;
246                 } else {
247                     // check for a mort table
248                     status = ATSFontGetTable(fontID, 'mort', 0, 0, 0, &size);
249                     checkedForMorphTable = PR_TRUE;
250                     if (status == noErr) {
251                         hasMorphTable = PR_TRUE;
252                     }
253                 }
254             }
255             
256             // rude hack - the Chinese STxxx fonts on 10.4 contain morx tables and Arabic glyphs but 
257             // lack the proper info for shaping Arabic, so exclude explicitly, ick
258             if (whichScript == eComplexScriptArabic && hasMorphTable) {
259                 if (mName.CharAt(0) == 'S' && mName.CharAt(1) == 'T') {
260                     mCharacterMap.ClearRange(gScriptsThatRequireShaping[s].rangeStart, gScriptsThatRequireShaping[s].rangeEnd);
261                 }
262             }
264             // general exclusion - if no morph table, exclude codepoints
265             if (!hasMorphTable) {
266                 mCharacterMap.ClearRange(gScriptsThatRequireShaping[s].rangeStart, gScriptsThatRequireShaping[s].rangeEnd);
267             }
268         }
269     }
271     PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit-cmap) psname: %s, size: %d\n", 
272                                         NS_ConvertUTF16toUTF8(mName).get(), mCharacterMap.GetSize()));
273                                         
274     return rv;
278 /* MacOSFamilyEntry */
279 #pragma mark-
281 // helper class for adding other family names back into font cache
282 class AddOtherFamilyNameFunctor 
284 public:
285     AddOtherFamilyNameFunctor(gfxQuartzFontCache *aFontCache) :
286         mFontCache(aFontCache)
287     {}
289     void operator() (MacOSFamilyEntry *aFamilyEntry, nsAString& aOtherName) {
290         mFontCache->AddOtherFamilyName(aFamilyEntry, aOtherName);
291     }
293     gfxQuartzFontCache *mFontCache;
296 void MacOSFamilyEntry::LocalizedName(nsAString& aLocalizedName)
298     // no other names ==> only one name, just return it
299     if (!HasOtherFamilyNames()) {
300         aLocalizedName = mName;
301         return;
302     }
304     NSFontManager *fontManager = [NSFontManager sharedFontManager];
306     // dig out the localized family name
307     NSString *family = GetNSStringForString(mName);
308     NSString *localizedFamily = [fontManager localizedNameForFamily:family face:nil];
310     if (localizedFamily) {
311         GetStringForNSString(localizedFamily, aLocalizedName);
312     } else {
313         // failed to get a localized name, just use the canonical name
314         aLocalizedName = mName;
315     }
318 PRBool MacOSFamilyEntry::HasOtherFamilyNames()
320     // need to read in other family names to determine this
321     if (!mOtherFamilyNamesInitialized) {
322         AddOtherFamilyNameFunctor addOtherNames(gfxQuartzFontCache::SharedFontCache());
323         ReadOtherFamilyNames(addOtherNames);  // sets mHasOtherFamilyNames
324     }
325     return mHasOtherFamilyNames;
328 static const PRUint32 kTraits_NonNormalWidthMask = NSNarrowFontMask | NSExpandedFontMask | 
329                             NSCondensedFontMask | NSCompressedFontMask | NSFixedPitchFontMask;
331 MacOSFontEntry*
332 MacOSFamilyEntry::FindFont(const gfxFontStyle* aStyle, PRBool& aNeedsBold)
334     return static_cast<MacOSFontEntry*> (FindFontForStyle(*aStyle, aNeedsBold));
337 MacOSFontEntry*
338 MacOSFamilyEntry::FindFont(const nsAString& aPostscriptName)
340     // find the font using a simple linear search
341     PRUint32 numFonts = mAvailableFonts.Length();
342     for (PRUint32 i = 0; i < numFonts; i++) {
343         MacOSFontEntry *fe = mAvailableFonts[i];
344         if (fe->Name() == aPostscriptName)
345             return fe;
346     }
347     return nsnull;
350 void
351 MacOSFamilyEntry::FindFontForChar(FontSearch *aMatchData)
353     // xxx - optimization point - keep a bit vector with the union of supported unicode ranges
354     // by all fonts for this family and bail immediately if the character is not in any of
355     // this family's cmaps
357     // iterate over fonts
358     PRUint32 numFonts = mAvailableFonts.Length();
359     for (PRUint32 i = 0; i < numFonts; i++) {
360         MacOSFontEntry *fe = mAvailableFonts[i];
361         PRInt32 rank = 0;
363         if (fe->TestCharacterMap(aMatchData->ch)) {
364             rank += 20;
365         }
367         // if we didn't match any characters don't bother wasting more time with this face.
368         if (rank == 0)
369             continue;
370             
371         // omitting from original windows code -- family name, lang group, pitch
372         // not available in current FontEntry implementation
374         if (aMatchData->fontToMatch) { 
375             const gfxFontStyle *style = aMatchData->fontToMatch->GetStyle();
376             
377             // italics
378             if (fe->IsItalic() && 
379                     (style->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0) {
380                 rank += 5;
381             }
382             
383             // weight
384             PRInt8 baseWeight, weightDistance;
385             style->ComputeWeightAndOffset(&baseWeight, &weightDistance);
387             // xxx - not entirely correct, the one unit of weight distance reflects 
388             // the "next bolder/lighter face"
389             PRUint32 targetWeight = (baseWeight * 100) + (weightDistance * 100);
391             PRUint32 entryWeight = fe->Weight();
392             if (entryWeight == targetWeight) {
393                 rank += 5;
394             } else {
395                 PRUint32 diffWeight = abs(entryWeight - targetWeight);
396                 if (diffWeight <= 100)  // favor faces close in weight
397                     rank += 2;
398             }
399         } else {
400             // if no font to match, prefer non-bold, non-italic fonts
401             if (!fe->IsItalic() && !fe->IsBold())
402                 rank += 5;
403         }
404         
405         // xxx - add whether AAT font with morphing info for specific lang groups
406         
407         if (rank > aMatchData->matchRank
408             || (rank == aMatchData->matchRank && Compare(fe->Name(), aMatchData->bestMatch->Name()) > 0)) 
409         {
410             aMatchData->bestMatch = fe;
411             aMatchData->matchRank = rank;
412         }
414     }
417 PRBool
418 MacOSFamilyEntry::FindFontsWithTraits(gfxFontEntry* aFontsForWeights[], PRUint32 aPosTraitsMask, 
419                                         PRUint32 aNegTraitsMask)
421     PRBool found = PR_FALSE;
423     // iterate over fonts
424     PRUint32 numFonts = mAvailableFonts.Length();
425     for (PRUint32 i = 0; i < numFonts; i++) {
426         MacOSFontEntry *fe = mAvailableFonts[i];
427         
428         // if traits match, add to list of fonts
429         PRUint32 traits = fe->Traits();
430         
431         // aPosTraitsMask == 0 ==> match all
432         if ((!aPosTraitsMask || (traits & aPosTraitsMask)) && !(traits & aNegTraitsMask)) {
433             PRInt32 weight = fe->Weight() / 100;
434             NS_ASSERTION(weight >= 1 && weight <= 9, "bogus font weight value!");
435             
436             // always prefer the first font for a given weight, helps deal a bit with 
437             // families with lots of faces (e.g. Minion Pro)
438             if (!aFontsForWeights[weight]) {
439                 aFontsForWeights[weight] = fe;
440                 found = PR_TRUE;
441             }
442         }
443     }
444     return found;
447 PRBool 
448 MacOSFamilyEntry::FindWeightsForStyle(gfxFontEntry* aFontsForWeights[], const gfxFontStyle& aFontStyle)
450     // short-circuit the single face per family case
451     if (mAvailableFonts.Length() == 1) {
452         MacOSFontEntry *fe = mAvailableFonts[0];
453         PRUint32 weight = fe->Weight() / 100;
454         NS_ASSERTION(weight >= 1 && weight <= 9, "bogus font weight value!");
455         aFontsForWeights[weight] = fe;
456         return PR_TRUE;
457     }
459     PRBool found = PR_FALSE;
460     PRBool isItalic = (aFontStyle.style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0;
462     // match italic faces
463     if (isItalic) {    
464         // first search for italic normal width fonts
465         found = FindFontsWithTraits(aFontsForWeights, NSItalicFontMask, kTraits_NonNormalWidthMask);
466         
467         // if not found, italic any width ones
468         if (!found) {
469             found = FindFontsWithTraits(aFontsForWeights, NSItalicFontMask, 0);        
470         }
471     }
473     // match non-italic faces, if no italic faces fall through here
474     if (!found) {
475         // look for normal width fonts
476         found = FindFontsWithTraits(aFontsForWeights, NSUnitalicFontMask, kTraits_NonNormalWidthMask);
477         
478         // if not found, any face will do
479         if (!found) {
480             found = FindFontsWithTraits(aFontsForWeights, NSUnitalicFontMask, 0);        
481         } 
482     }
484     // still not found?!? family must only contain italic fonts when looking for a normal 
485     // face, just use the whole list
486     if (!found) {
487         found = FindFontsWithTraits(aFontsForWeights, 0, 0);
488     }
489     NS_ASSERTION(found, "Font family containing no faces");
491     return found;
494 static NSString* CreateNameFromBuffer(const UInt8 *aBuf, ByteCount aLength, 
495         FontPlatformCode aPlatformCode, FontScriptCode aScriptCode, FontLanguageCode aLangCode)
497     CFStringRef outName = NULL;
499     if (aPlatformCode == kFontMacintoshPlatform) {
500         TextEncoding encoding;
501         OSStatus err = GetTextEncodingFromScriptInfo(aScriptCode, aLangCode, 
502                                                         kTextRegionDontCare, &encoding);
503         if (err) {
504             // some fonts are sloppy about the language code (e.g Arial Hebrew, Corsiva Hebrew)
505             // try again without the lang code to avoid bad combinations
506             OSStatus err = GetTextEncodingFromScriptInfo(aScriptCode, kTextLanguageDontCare, 
507                                                         kTextRegionDontCare, &encoding);
508             if (err) return nil;
509         }
510         outName = CFStringCreateWithBytes(kCFAllocatorDefault, aBuf, 
511                                             aLength, (CFStringEncoding) encoding, false);
512     } else if (aPlatformCode == kFontUnicodePlatform) {
513         outName = CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)aBuf, aLength/2);    
514     } else if (aPlatformCode == kFontMicrosoftPlatform) {
515         if (aScriptCode == 0) {
516             outName = CFStringCreateWithBytes(kCFAllocatorDefault, aBuf, 
517                                                 aLength, kCFStringEncodingUTF16BE, false);
518         } else {
519             outName = CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)aBuf, aLength/2);    
520         }
521     }
523     return (NSString*) outName;
526 // 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
527 enum {
528   kMozillaFontPreferredFamilyName            = 16,
531 // xxx - rather than use ATSUI calls, probably faster to load name table directly, 
532 // this avoids copying around strings that are of no interest
534 // returns true if other names were found, false otherwise
535 static PRBool ReadOtherFamilyNamesForFace(AddOtherFamilyNameFunctor& aOtherFamilyFunctor, MacOSFamilyEntry *aFamilyEntry,
536                                         NSString *familyName, ATSUFontID fontID, bool useFullName = false)
538     OSStatus err;
539     ItemCount i, nameCount;
540     PRBool foundNames = PR_FALSE;
542     if (fontID == kATSUInvalidFontID)
543         return foundNames;
545     err = ATSUCountFontNames(fontID, &nameCount);
546     if (err != noErr) 
547         return foundNames;
549     for (i = 0; i < nameCount; i++) {
551         FontNameCode nameCode;
552         FontPlatformCode platformCode;
553         FontScriptCode scriptCode;
554         FontLanguageCode langCode;
555         const ByteCount kBufLength = 2048;
556         char buf[kBufLength];
557         ByteCount len;
559         err = ATSUGetIndFontName(fontID, i, kBufLength, buf, &len, &nameCode, &platformCode, &scriptCode, &langCode);
560         // couldn't find a font name? just continue to the next name table entry
561         if (err == kATSUNoFontNameErr) 
562             continue;
563         // any other error, bail
564         if (err != noErr) 
565             return foundNames;
567         if (useFullName) {
568             if (nameCode != kFontFullName)
569                 continue;
570         } else {
571             if (!(nameCode == kFontFamilyName || nameCode == kMozillaFontPreferredFamilyName)) 
572                 continue; 
573         }
574         if (len >= kBufLength) continue; 
575         buf[len] = 0;
577         NSString *name = CreateNameFromBuffer((UInt8*)buf, len, platformCode, scriptCode, langCode);
579         // add if not same as canonical family name or already in list of names
580         if (name) {
582             if (![name isEqualToString:familyName]) {
583                 nsAutoString otherFamilyName;
584                 GetStringForNSString(name, otherFamilyName);
585                 aOtherFamilyFunctor(aFamilyEntry, otherFamilyName);
586                 foundNames = PR_TRUE;
587             }
589             [name release];
590         }
591     }
593     return foundNames;
596 void
597 MacOSFamilyEntry::ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor)
599     if (mOtherFamilyNamesInitialized) 
600         return;
601     mOtherFamilyNamesInitialized = PR_TRUE;
603     NSString *familyName = GetNSStringForString(mName);
605     // read in other family names for the first face in the list
606     MacOSFontEntry *fe = mAvailableFonts[0];
608     mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aOtherFamilyFunctor, this, familyName, fe->GetFontID());
610     // read in other names for the first face in the list with the assumption
611     // that if extra names don't exist in that face then they don't exist in
612     // other faces for the same font
613     if (mHasOtherFamilyNames) {
614         PRUint32 numFonts, i;
615         
616         // read in names for all faces, needed to catch cases where
617         // fonts all family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
618         numFonts = mAvailableFonts.Length();
619         for (i = 1; i < numFonts; i++) {
620             fe = mAvailableFonts[i];
621             ReadOtherFamilyNamesForFace(aOtherFamilyFunctor, this, familyName, fe->GetFontID());
622         }
623     }
626 /* SingleFaceFamily */
627 #pragma mark-
629 void SingleFaceFamily::LocalizedName(nsAString& aLocalizedName)
631     MacOSFontEntry *fontEntry;
633     // use the display name of the single face
634     fontEntry = mAvailableFonts[0];
635     if (!fontEntry) 
636         return;
637         
638     NSFont *font = [NSFont fontWithName:GetNSStringForString(fontEntry->Name()) size:0.0];
639     if (!font)
640         return;
642     NSString *fullname = [font displayName];
643     if (fullname) {
644         GetStringForNSString(fullname, aLocalizedName);
645     }
648 void SingleFaceFamily::ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor)
650     if (mOtherFamilyNamesInitialized) 
651         return;
652     mOtherFamilyNamesInitialized = PR_TRUE;
654     NSString *familyName = GetNSStringForString(mName);
656     // read in other family names for the first face in the list
657     MacOSFontEntry *fe = mAvailableFonts[0];
659     // read in other names, using the full font names as the family names
660     mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aOtherFamilyFunctor, this, familyName, fe->GetFontID(), true);    
663 /* gfxQuartzFontCache */
664 #pragma mark-
666 gfxQuartzFontCache *gfxQuartzFontCache::sSharedFontCache = nsnull;
668 gfxQuartzFontCache::gfxQuartzFontCache()
669     : mStartIndex(0), mIncrement(kNumFontsPerSlice), mNumFamilies(0)
671     mATSGeneration = PRUint32(kATSGenerationInitial);
673     mFontFamilies.Init(100);
674     mOtherFamilyNames.Init(30);
675     mOtherFamilyNamesInitialized = PR_FALSE;
676     mPrefFonts.Init(10);
678     InitFontList();
679     ::ATSFontNotificationSubscribe(ATSNotification,
680                                    kATSFontNotifyOptionDefault,
681                                    (void*)this, nsnull);
683     // pref changes notification setup
684     nsCOMPtr<nsIPref> pref = do_GetService(NS_PREF_CONTRACTID);
685     pref->RegisterCallback("font.", PrefChangedCallback, this);
686     pref->RegisterCallback("font.name-list.", PrefChangedCallback, this);
687     pref->RegisterCallback("intl.accept_languages", PrefChangedCallback, this);  // hmmmm...
691 const PRUint32 kNonNormalTraits = NSItalicFontMask | NSBoldFontMask | NSNarrowFontMask | NSExpandedFontMask | NSCondensedFontMask | NSCompressedFontMask;
693 void
694 gfxQuartzFontCache::InitFontList()
696     ATSGeneration currentGeneration = ATSGetGeneration();
697     
698     // need to ignore notifications after adding each font
699     if (mATSGeneration == currentGeneration)
700         return;
702     mATSGeneration = currentGeneration;
703     PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit) updating to generation: %d", mATSGeneration));                                         
704     
705     mFontFamilies.Clear();
706     mOtherFamilyNames.Clear();
707     mOtherFamilyNamesInitialized = PR_FALSE;
708     mPrefFonts.Clear();
709     mCodepointsWithNoFonts.reset();
710     CancelLoader();
712     // iterate over available families
713     NSFontManager *fontManager = [NSFontManager sharedFontManager];
714     NSEnumerator *families = [[fontManager availableFontFamilies] objectEnumerator];  // returns "canonical", non-localized family name
716     nsAutoString availableFamilyName, postscriptFontName;
717    
718     NSString *availableFamily = nil;
719     while ((availableFamily = [families nextObject])) {
721         // make a nsString
722         GetStringForNSString(availableFamily, availableFamilyName);
723         
724         // create a family entry
725         MacOSFamilyEntry *familyEntry = new MacOSFamilyEntry(availableFamilyName);
726         if (!familyEntry) break;
727         
728         // create a font entry for each face
729         NSArray *fontfaces = [fontManager availableMembersOfFontFamily:availableFamily];  // returns an array of [psname, style name, weight, traits] elements, goofy api
730         int faceCount = [fontfaces count];
731         int faceIndex;
733         for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
734             NSArray *face = [fontfaces objectAtIndex:faceIndex];
735             NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
736             PRInt32 weight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
737             PRUint32 traits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
738             
739             // 10.5 doesn't set NSUnitalicFontMask and NSUnboldFontMask - manually set these for consistency 
740             if (!(traits & NSBoldFontMask))
741                 traits |= NSUnboldFontMask;
742             if (!(traits & NSItalicFontMask))
743                 traits |= NSUnitalicFontMask;
744             
745             PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit) family: %s, psname: %s, face: %s, apple-weight: %d, css-weight: %d, traits: %8.8x\n", 
746                 [availableFamily UTF8String], [psname UTF8String], [[face objectAtIndex:INDEX_FONT_FACE_NAME] UTF8String], weight, gfxQuartzFontCache::AppleWeightToCSSWeight(weight), traits));
748             // make a nsString
749             GetStringForNSString(psname, postscriptFontName);
750         
751             // create a font entry
752             MacOSFontEntry *fontEntry = new MacOSFontEntry(postscriptFontName, weight, traits, familyEntry);
753             if (!fontEntry) break;            
754             
755             // insert into font entry array of family
756             familyEntry->AddFontEntry(fontEntry);
757         }
758         
759         // add the family entry to the hash table
760         ToLowerCase(availableFamilyName);
761         mFontFamilies.Put(availableFamilyName, familyEntry);
762     }
764     InitSingleFaceList();
766     // to avoid full search of font name tables, seed the other names table with localized names from 
767     // some of the prefs fonts which are accessed via their localized names.  changes in the pref fonts will only cause
768     // a font lookup miss earlier. this is a simple optimization, it's not required for correctness
769     PreloadNamesList();
771     // clean up various minor 10.4 font problems for specific fonts
772     if (gfxPlatformMac::GetPlatform()->OSXVersion() < MAC_OS_X_VERSION_10_5_HEX) {
773         // Cocoa calls report that italic faces exist for Courier and Helvetica,
774         // even though only bold faces exist so test for this using ATSUI id's (10.5 has proper faces)
775         EliminateDuplicateFaces(NS_LITERAL_STRING("Courier"));
776         EliminateDuplicateFaces(NS_LITERAL_STRING("Helvetica"));
777         
778         // Cocoa reports that Courier and Monaco are not fixed-pitch fonts
779         // so explicitly tweak these settings
780         SetFixedPitch(NS_LITERAL_STRING("Courier"));
781         SetFixedPitch(NS_LITERAL_STRING("Monaco"));
782     }
784     // initialize ranges of characters for which system-wide font search should be skipped
785     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
786     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
788     InitBadUnderlineList();
790     // start the delayed cmap loader
791     StartLoader(kDelayBeforeLoadingCmaps, kIntervalBetweenLoadingCmaps); 
795 void 
796 gfxQuartzFontCache::InitOtherFamilyNames()
798     mOtherFamilyNamesInitialized = PR_TRUE;
800     // iterate over all font families and read in other family names
801     mFontFamilies.Enumerate(gfxQuartzFontCache::InitOtherFamilyNamesProc, this);
803                                                          
804 PLDHashOperator PR_CALLBACK gfxQuartzFontCache::InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
805                                                          nsRefPtr<MacOSFamilyEntry>& aFamilyEntry,
806                                                          void* userArg)
808     gfxQuartzFontCache *fc = (gfxQuartzFontCache*) userArg;
809     AddOtherFamilyNameFunctor addOtherNames(fc);
810     aFamilyEntry->ReadOtherFamilyNames(addOtherNames);
811     return PL_DHASH_NEXT;
814 void
815 gfxQuartzFontCache::ReadOtherFamilyNamesForFamily(const nsAString& aFamilyName)
817     MacOSFamilyEntry *familyEntry = FindFamily(aFamilyName);
819     if (familyEntry) {
820         AddOtherFamilyNameFunctor addOtherNames(this);
821         familyEntry->ReadOtherFamilyNames(addOtherNames);
822     }
825 void
826 gfxQuartzFontCache::InitSingleFaceList()
828     nsAutoTArray<nsString, 10> singleFaceFonts;
829     gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts);
831     PRUint32 numFonts = singleFaceFonts.Length();
832     for (PRUint32 i = 0; i < numFonts; i++) {
833         nsAutoString availableFamilyName;
835         // lookup the font using NSFont    
836         NSString *faceName = GetNSStringForString(singleFaceFonts[i]);
837         NSFont *font = [NSFont fontWithName:faceName size:0.0];
838         if (font) {
839             NSString *availableFamily = [font familyName];
840             GetStringForNSString(availableFamily, availableFamilyName);
842             MacOSFamilyEntry *familyEntry = FindFamily(availableFamilyName);
843             if (familyEntry) {
844                 MacOSFontEntry *fontEntry = familyEntry->FindFont(singleFaceFonts[i]);
845                 if (fontEntry) {
846                     PRBool found;
847                     nsAutoString displayName, key;
848                     
849                     // use the display name the canonical name
850                     NSString *display = [font displayName];
851                     GetStringForNSString(display, displayName);
852                     GenerateFontListKey(displayName, key);
854                     // add only if doesn't exist already
855                     if (!(familyEntry = mFontFamilies.GetWeak(key, &found))) {
856                         familyEntry = new SingleFaceFamily(displayName);
857                         familyEntry->AddFontEntry(fontEntry);
858                         mFontFamilies.Put(key, familyEntry);
859                         PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit-singleface) family: %s, psname: %s\n", [display UTF8String], [faceName UTF8String]));
860                     }
861                     fontEntry->mFamily = familyEntry;
862                 }
863             }
864         }
865         
866     }
867        
870 void
871 gfxQuartzFontCache::PreloadNamesList()
873     nsAutoTArray<nsString, 10> preloadFonts;
874     gfxFontUtils::GetPrefsFontList("font.preload-names-list", preloadFonts);
876     PRUint32 numFonts = preloadFonts.Length();
877     for (PRUint32 i = 0; i < numFonts; i++) {
878         PRBool found;
879         nsAutoString key;
880         GenerateFontListKey(preloadFonts[i], key);
881         
882         // only search canonical names!
883         MacOSFamilyEntry *familyEntry = mFontFamilies.GetWeak(key, &found);
884         if (familyEntry) {
885             AddOtherFamilyNameFunctor addOtherNames(this);
886             familyEntry->ReadOtherFamilyNames(addOtherNames);
887         }
888     }
892 void 
893 gfxQuartzFontCache::EliminateDuplicateFaces(const nsAString& aFamilyName)
895     MacOSFamilyEntry *family = FindFamily(aFamilyName);
896     if (!family) return;
898     nsTArray<nsRefPtr<MacOSFontEntry> >& fontlist = family->GetFontList();
900     PRUint32 i, bold, numFonts, italicIndex;
901     MacOSFontEntry *italic, *nonitalic;
902     PRUint32 boldtraits[2] = { 0, NSBoldFontMask };
904     // if normal and italic have the same ATSUI id, delete italic
905     // if bold and bold-italic have the same ATSUI id, delete bold-italic
907     // two iterations, one for normal, one for bold
908     for (bold = 0; bold < 2; bold++) {
909         numFonts = fontlist.Length();
911         // find the non-italic face
912         nonitalic = nsnull;
913         for (i = 0; i < numFonts; i++) {
914             PRUint32 traits = fontlist[i]->Traits();
915             if (((traits & NSBoldFontMask) == boldtraits[bold]) && !(traits & NSItalicFontMask)) {
916                 nonitalic = fontlist[i];
917                 break;
918             }
919         }
921         // find the italic face
922         if (nonitalic) {
923             italic = nsnull;
924             for (i = 0; i < numFonts; i++) {
925                 PRUint32 traits = fontlist[i]->Traits();
926                 if (((traits & NSBoldFontMask) == boldtraits[bold]) && (traits & NSItalicFontMask)) {
927                     italic = fontlist[i];
928                     italicIndex = i;
929                     break;
930                 }
931             }
933             // if italic face and non-italic face have matching ATSUI id's, 
934             // the italic face is bogus so remove it
935             if (italic && italic->GetFontID() == nonitalic->GetFontID()) {
936                 fontlist.RemoveElementAt(italicIndex);
937             }
938         }
939     }
942 void 
943 gfxQuartzFontCache::SetFixedPitch(const nsAString& aFamilyName)
945     MacOSFamilyEntry *family = FindFamily(aFamilyName);
946     if (!family) return;
948     nsTArray<nsRefPtr<MacOSFontEntry> >& fontlist = family->GetFontList();
950     PRUint32 i, numFonts = fontlist.Length();
952     for (i = 0; i < numFonts; i++) {
953         fontlist[i]->mTraits |= NSFixedPitchFontMask;
954         fontlist[i]->mFixedPitch = 1;
955     }
958 void
959 gfxQuartzFontCache::InitBadUnderlineList()
961     nsAutoTArray<nsString, 10> blacklist;
962     gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist);
963     PRUint32 numFonts = blacklist.Length();
964     for (PRUint32 i = 0; i < numFonts; i++) {
965         PRBool found;
966         nsAutoString key;
967         GenerateFontListKey(blacklist[i], key);
969         MacOSFamilyEntry *familyEntry = mFontFamilies.GetWeak(key, &found);
970         if (familyEntry)
971             familyEntry->SetBadUnderlineFont(PR_TRUE);
972     }
975 PRBool 
976 gfxQuartzFontCache::ResolveFontName(const nsAString& aFontName, nsAString& aResolvedFontName)
978     MacOSFamilyEntry *family = FindFamily(aFontName);
979     if (family) {
980         aResolvedFontName = family->Name();
981         return PR_TRUE;
982     }
983     return PR_FALSE;
986 PRBool
987 gfxQuartzFontCache::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
989     MacOSFamilyEntry *family = FindFamily(aFontName);
990     if (family) {
991         family->LocalizedName(aFamilyName);
992         return PR_TRUE;
993     }
995     // Gecko 1.8 used Quickdraw font api's which produce a slightly different set of "family"
996     // names.  Try to resolve based on these names, in case this is stored in an old profile
997     // 1.8: "Futura", "Futura Condensed" ==> 1.9: "Futura
998     FMFont fmFont;
1000     // convert of a NSString
1001     NSString *fontName = GetNSStringForString(aFontName);
1003     // name ==> family id ==> old-style FMFont
1004     ATSFontFamilyRef fmFontFamily = ATSFontFamilyFindFromName((CFStringRef)fontName, kATSOptionFlagsDefault);
1005     OSStatus err = FMGetFontFromFontFamilyInstance(fmFontFamily, 0, &fmFont, nsnull);
1006     if (err != noErr || fmFont == kInvalidFont)
1007         return PR_FALSE;
1009     ATSFontRef atsFont = FMGetATSFontRefFromFont(fmFont);
1010     if (!atsFont)
1011         return PR_FALSE;
1013     NSString *psname;
1015     // now lookup the Postscript name
1016     err = ATSFontGetPostScriptName(atsFont, kATSOptionFlagsDefault, (CFStringRef*) (&psname));
1017     if (err != noErr)
1018         return PR_FALSE;
1020     // given an NSFont instance, Cocoa api's return the canonical family name
1021     NSString *canonicalfamily = [[NSFont fontWithName:psname size:0.0] familyName];
1022     [psname release];
1024     nsAutoString familyName;
1026     // lookup again using the canonical family name
1027     GetStringForNSString(canonicalfamily, familyName);
1028     family = FindFamily(familyName);
1029     if (family) {
1030         family->LocalizedName(aFamilyName);
1031         return PR_TRUE;
1032     }
1034     return PR_FALSE;
1037 void
1038 gfxQuartzFontCache::ATSNotification(ATSFontNotificationInfoRef aInfo,
1039                                     void* aUserArg)
1041     // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
1042     gfxQuartzFontCache *qfc = (gfxQuartzFontCache*)aUserArg;
1043     qfc->UpdateFontList();
1046 int PR_CALLBACK
1047 gfxQuartzFontCache::PrefChangedCallback(const char *aPrefName, void *closure)
1049     // XXX this could be made to only clear out the cache for the prefs that were changed
1050     // but it probably isn't that big a deal.
1051     gfxQuartzFontCache *qfc = static_cast<gfxQuartzFontCache *>(closure);
1052     qfc->mPrefFonts.Clear();
1053     return 0;
1056 MacOSFontEntry*
1057 gfxQuartzFontCache::GetDefaultFont(const gfxFontStyle* aStyle, PRBool& aNeedsBold)
1059     NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
1060     nsAutoString familyName;
1062     GetStringForNSString(defaultFamily, familyName);
1063     return FindFontForFamily(familyName, aStyle, aNeedsBold);
1066 struct FontListData {
1067     FontListData(const nsACString& aLangGroup,
1068                  const nsACString& aGenericFamily,
1069                  nsStringArray& aListOfFonts) :
1070         mLangGroup(aLangGroup), mGenericFamily(aGenericFamily),
1071         mListOfFonts(aListOfFonts) {}
1072     const nsACString& mLangGroup;
1073     const nsACString& mGenericFamily;
1074     nsStringArray& mListOfFonts;
1077 PLDHashOperator PR_CALLBACK
1078 gfxQuartzFontCache::HashEnumFuncForFamilies(nsStringHashKey::KeyType aKey,
1079                                             nsRefPtr<MacOSFamilyEntry>& aFamilyEntry,
1080                                             void *aUserArg)
1082     FontListData *data = (FontListData*)aUserArg;
1084     nsAutoString localizedFamilyName;
1085     aFamilyEntry->LocalizedName(localizedFamilyName);
1086     data->mListOfFonts.AppendString(localizedFamilyName);
1087     return PL_DHASH_NEXT;
1090 void
1091 gfxQuartzFontCache::GetFontList (const nsACString& aLangGroup,
1092                                  const nsACString& aGenericFamily,
1093                                  nsStringArray& aListOfFonts)
1095     FontListData data(aLangGroup, aGenericFamily, aListOfFonts);
1097     mFontFamilies.Enumerate(gfxQuartzFontCache::HashEnumFuncForFamilies, &data);
1099     aListOfFonts.Sort();
1100     aListOfFonts.Compact();
1103 struct FontFamilyListData {
1104     FontFamilyListData(nsTArray<nsRefPtr<MacOSFamilyEntry> >& aFamilyArray) 
1105         : mFamilyArray(aFamilyArray)
1106     {}
1108     static PLDHashOperator PR_CALLBACK AppendFamily(nsStringHashKey::KeyType aKey,
1109                                                     nsRefPtr<MacOSFamilyEntry>& aFamilyEntry,
1110                                                     void *aUserArg)
1111     {
1112         FontFamilyListData *data = (FontFamilyListData*)aUserArg;
1113         data->mFamilyArray.AppendElement(aFamilyEntry);
1114         return PL_DHASH_NEXT;
1115     }
1117     nsTArray<nsRefPtr<MacOSFamilyEntry> >& mFamilyArray;
1120 void
1121 gfxQuartzFontCache::GetFontFamilyList(nsTArray<nsRefPtr<MacOSFamilyEntry> >& aFamilyArray)
1123     FontFamilyListData data(aFamilyArray);
1124     mFontFamilies.Enumerate(FontFamilyListData::AppendFamily, &data);
1127 MacOSFontEntry*  
1128 gfxQuartzFontCache::FindFontForChar(const PRUint32 aCh, gfxFont *aPrevFont)
1130     // is codepoint with no matching font? return null immediately
1131     if (mCodepointsWithNoFonts.test(aCh)) {
1132         return nsnull;
1133     }
1135     // short-circuit system font fallback for U+FFFD, used to represent encoding errors
1136     // just use Lucida Grande (system font, guaranteed to be around)
1137     // this helps speed up pages with lots of encoding errors, binary-as-text, etc.
1138     if (aCh == 0xFFFD) {
1139         MacOSFontEntry* fontEntry;
1140         PRBool needsBold;  // ignored in the system fallback case
1141         
1142         if (aPrevFont) {
1143             fontEntry = FindFontForFamily(NS_LITERAL_STRING("Lucida Grande"), aPrevFont->GetStyle(), needsBold);
1144         } else {
1145             gfxFontStyle normalStyle;
1146             fontEntry = FindFontForFamily(NS_LITERAL_STRING("Lucida Grande"), &normalStyle, needsBold);
1147         }
1149         if (fontEntry && fontEntry->TestCharacterMap(aCh))
1150             return fontEntry;
1151     }
1153     FontSearch data(aCh, aPrevFont);
1155     // iterate over all font families to find a font that support the character
1156     mFontFamilies.Enumerate(gfxQuartzFontCache::FindFontForCharProc, &data);
1158     // no match? add to set of non-matching codepoints
1159     if (!data.bestMatch) {
1160         mCodepointsWithNoFonts.set(aCh);
1161     }
1163     return data.bestMatch;
1166 PLDHashOperator PR_CALLBACK 
1167 gfxQuartzFontCache::FindFontForCharProc(nsStringHashKey::KeyType aKey, nsRefPtr<MacOSFamilyEntry>& aFamilyEntry,
1168      void *userArg)
1170     FontSearch *data = (FontSearch*)userArg;
1172     // evaluate all fonts in this family for a match
1173     aFamilyEntry->FindFontForChar(data);
1174     return PL_DHASH_NEXT;
1177 MacOSFamilyEntry* 
1178 gfxQuartzFontCache::FindFamily(const nsAString& aFamily)
1180     nsAutoString key;
1181     MacOSFamilyEntry *familyEntry;
1182     PRBool found;
1183     GenerateFontListKey(aFamily, key);
1185     // lookup in canonical (i.e. English) family name list
1186     if ((familyEntry = mFontFamilies.GetWeak(key, &found))) {
1187         return familyEntry;
1188     }
1190     // lookup in other family names list (mostly localized names)
1191     if ((familyEntry = mOtherFamilyNames.GetWeak(key, &found))) {
1192         return familyEntry;
1193     }
1195     // name not found and other family names not yet fully initialized so
1196     // initialize the rest of the list and try again.  this is done lazily
1197     // since reading name table entries is expensive
1198     if (!mOtherFamilyNamesInitialized) {
1199         InitOtherFamilyNames();
1200         if ((familyEntry = mOtherFamilyNames.GetWeak(key, &found))) {
1201             return familyEntry;
1202         }
1203     }
1205     return nsnull;
1208 MacOSFontEntry*
1209 gfxQuartzFontCache::FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, PRBool& aNeedsBold)
1211     MacOSFamilyEntry *familyEntry = FindFamily(aFamily);
1213     aNeedsBold = PR_FALSE;
1215     if (familyEntry)
1216         return familyEntry->FindFont(aStyle, aNeedsBold);
1218     return nsnull;
1221 PRInt32 
1222 gfxQuartzFontCache::AppleWeightToCSSWeight(PRInt32 aAppleWeight)
1224     if (aAppleWeight < 1)
1225         aAppleWeight = 1;
1226     else if (aAppleWeight > kAppleMaxWeight)
1227         aAppleWeight = kAppleMaxWeight;
1228     return gAppleWeightToCSSWeight[aAppleWeight];
1231 PRBool
1232 gfxQuartzFontCache::GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<MacOSFamilyEntry> > *array)
1234     return mPrefFonts.Get(PRUint32(aLangGroup), array);
1237 void
1238 gfxQuartzFontCache::SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<MacOSFamilyEntry> >& array)
1240     mPrefFonts.Put(PRUint32(aLangGroup), array);
1243 void 
1244 gfxQuartzFontCache::AddOtherFamilyName(MacOSFamilyEntry *aFamilyEntry, nsAString& aOtherFamilyName)
1246     nsAutoString key;
1247     PRBool found;
1248     GenerateFontListKey(aOtherFamilyName, key);
1250     if (!mOtherFamilyNames.GetWeak(key, &found)) {
1251         mOtherFamilyNames.Put(key, aFamilyEntry);
1252         PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit-otherfamily) canonical family: %s, other family: %s\n", 
1253                                             NS_ConvertUTF16toUTF8(aFamilyEntry->Name()).get(), 
1254                                             NS_ConvertUTF16toUTF8(aOtherFamilyName).get()));
1255     }
1258 gfxFontEntry* 
1259 gfxQuartzFontCache::LookupLocalFont(const nsAString& aFontName)
1261     NSString *faceName = GetNSStringForString(aFontName);
1262     NSFont *font = [NSFont fontWithName:faceName size:0.0];
1264     if (font) {
1265         nsAutoString availableFamilyName;
1266         NSString *availableFamily = [font familyName];
1267         GetStringForNSString(availableFamily, availableFamilyName);
1269         MacOSFamilyEntry *familyEntry = FindFamily(availableFamilyName);
1270         if (familyEntry) {
1271             MacOSFontEntry *fontEntry = familyEntry->FindFont(aFontName);
1272             return fontEntry;
1273         }
1274     }
1276     // didn't find the font
1277     return nsnull;
1280 // grumble, another non-publised Apple API dependency (found in Webkit code)
1281 // activated with this value, font will not be found via system lookup routines
1282 // it can only be used via the created ATSUFontID
1283 // needed to prevent one doc from finding a font used in a separate doc
1285 enum {
1286     kPrivateATSFontContextPrivate = 3
1289 class MacOSUserFontData : public gfxUserFontData {
1290 public:
1291     MacOSUserFontData(ATSFontContainerRef aContainerRef)
1292         : mContainerRef(aContainerRef)
1293     { }
1295     virtual ~MacOSUserFontData()
1296     {
1297         // deactivate font
1298         if (mContainerRef)
1299             ATSFontDeactivate(mContainerRef, NULL, kATSOptionFlagsDefault);
1300     }
1302     ATSFontContainerRef     mContainerRef;
1305 gfxFontEntry* 
1306 gfxQuartzFontCache::MakePlatformFont(const gfxFontEntry *aProxyEntry, 
1307                                      const PRUint8 *aFontData, PRUint32 aLength)
1309     OSStatus err;
1310     
1311     NS_ASSERTION(aFontData && aLength != 0, 
1312                  "MakePlatformFont called with null data ptr");
1313                  
1314     // do simple validation check on font data before 
1315     // attempting to activate it
1316     if (!gfxFontUtils::ValidateSFNTHeaders(aFontData, aLength)) {
1317 #if DEBUG
1318         char warnBuf[1024];
1319         const gfxProxyFontEntry *proxyEntry = 
1320             static_cast<const gfxProxyFontEntry*> (aProxyEntry);
1321         sprintf(warnBuf, "downloaded font error, invalid font data for (%s)",
1322                 NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get());
1323         NS_WARNING(warnBuf);
1324 #endif    
1325         return nsnull;
1326     }
1328     ATSFontRef fontRef;
1329     ATSFontContainerRef containerRef;
1331     // we get occasional failures when multiple fonts are activated in quick succession
1332     // if the ATS font cache is damaged; to work around this, we can retry the activation
1333     const PRUint32 kMaxRetries = 3;
1334     PRUint32 retryCount = 0;
1335     while (retryCount++ < kMaxRetries) {
1336         err = ATSFontActivateFromMemory(const_cast<PRUint8*>(aFontData), aLength, 
1337                                         kPrivateATSFontContextPrivate,
1338                                         kATSFontFormatUnspecified,
1339                                         NULL, 
1340                                         kATSOptionFlagsDoNotNotify, 
1341                                         &containerRef);
1342         mATSGeneration = ATSGetGeneration();
1344         if (err != noErr) {
1345 #if DEBUG
1346             char warnBuf[1024];
1347             const gfxProxyFontEntry *proxyEntry = 
1348                 static_cast<const gfxProxyFontEntry*> (aProxyEntry);
1349             sprintf(warnBuf, "downloaded font error, ATSFontActivateFromMemory err: %d for (%s)",
1350                     PRInt32(err),
1351                     NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get());
1352             NS_WARNING(warnBuf);
1353 #endif    
1354             return nsnull;
1355         }
1357         // ignoring containers with multiple fonts, use the first face only for now
1358         err = ATSFontFindFromContainer(containerRef, kATSOptionFlagsDefault, 1, 
1359                                        &fontRef, NULL);
1360         if (err != noErr) {
1361 #if DEBUG
1362             char warnBuf[1024];
1363             const gfxProxyFontEntry *proxyEntry = 
1364                 static_cast<const gfxProxyFontEntry*> (aProxyEntry);
1365             sprintf(warnBuf, "downloaded font error, ATSFontFindFromContainer err: %d for (%s)",
1366                     PRInt32(err),
1367                     NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get());
1368             NS_WARNING(warnBuf);
1369 #endif  
1370             ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
1371             return nsnull;
1372         }
1373     
1374         // now lookup the Postscript name; this may fail if the font cache is bad
1375         OSStatus err;
1376         NSString *psname = NULL;
1377         nsAutoString postscriptName;
1378         err = ATSFontGetPostScriptName(fontRef, kATSOptionFlagsDefault, (CFStringRef*) (&psname));
1379         if (err == noErr) {
1380             GetStringForNSString(psname, postscriptName);
1381             [psname release];
1382         } else {
1383 #ifdef DEBUG
1384             char warnBuf[1024];
1385             const gfxProxyFontEntry *proxyEntry = 
1386                 static_cast<const gfxProxyFontEntry*> (aProxyEntry);
1387             sprintf(warnBuf, "ATSFontGetPostScriptName err = %d for (%s), retries = %d", (PRInt32)err,
1388                     NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get(), retryCount);
1389             NS_WARNING(warnBuf);
1390 #endif
1391             ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
1392             // retry the activation a couple of times if this fails
1393             // (may be a transient failure due to ATS font cache issues)
1394             continue;
1395         }
1397         // font entry will own this
1398         MacOSUserFontData *userFontData = new MacOSUserFontData(containerRef);
1400         if (!userFontData) {
1401             ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
1402             return nsnull;
1403         }
1405         PRUint16 w = aProxyEntry->mWeight;
1406         NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
1408         // create the font entry
1409         MacOSFontEntry *newFontEntry = 
1410             new MacOSFontEntry(postscriptName,
1411                                FMGetFontFromATSFontRef(fontRef),
1412                                w, aProxyEntry->mStretch, 
1413                                (PRUint32(aProxyEntry->mItalic) ? 
1414                                            FONT_STYLE_ITALIC : 
1415                                            FONT_STYLE_NORMAL), 
1416                                userFontData);
1418         if (!newFontEntry) {
1419             delete userFontData;
1420             return nsnull;
1421         }
1423         // if we succeeded (which should always be the case), return the new font
1424         if (newFontEntry->mIsValid)
1425             return newFontEntry;
1427         // if something is funky about this font, delete immediately
1428 #if DEBUG
1429         char warnBuf[1024];
1430         const gfxProxyFontEntry *proxyEntry = 
1431             static_cast<const gfxProxyFontEntry*> (aProxyEntry);
1432         sprintf(warnBuf, "downloaded font not loaded properly, removed face for (%s)", 
1433                 NS_ConvertUTF16toUTF8(proxyEntry->mFamily->Name()).get());
1434         NS_WARNING(warnBuf);
1435 #endif    
1436         delete newFontEntry;
1438         // We don't retry from here; the ATS font cache issue would have caused failure earlier
1439         // so if we get here, there's something else bad going on within our font data structures.
1440         // Currently, there should be no way to reach here, as fontentry creation cannot fail
1441         // except by memory allocation failure.
1442         NS_WARNING("invalid font entry for a newly activated font");
1443         break;
1444     }
1446     // if we get here, the activation failed (even with possible retries); can't use this font
1447     return nsnull;
1451 void 
1452 gfxQuartzFontCache::InitLoader()
1454     GetFontFamilyList(mFontFamiliesToLoad);
1455     mStartIndex = 0;
1456     mNumFamilies = mFontFamiliesToLoad.Length();
1459 PRBool 
1460 gfxQuartzFontCache::RunLoader()
1462     PRUint32 i, endIndex = (mStartIndex + mIncrement < mNumFamilies ? mStartIndex + mIncrement : mNumFamilies);
1464     // for each font family, load in various font info
1465     for (i = mStartIndex; i < endIndex; i++) {
1466         AddOtherFamilyNameFunctor addOtherNames(this);
1468         // load the cmap
1469         mFontFamiliesToLoad[i]->ReadCMAP();
1471         // read in other family names
1472         mFontFamiliesToLoad[i]->ReadOtherFamilyNames(addOtherNames);
1473     }
1475     mStartIndex += mIncrement;
1476     if (mStartIndex < mNumFamilies)
1477         return PR_FALSE;
1478     return PR_TRUE;
1481 void 
1482 gfxQuartzFontCache::FinishLoader()
1484     mFontFamiliesToLoad.Clear();
1485     mNumFamilies = 0;