1 /* -*- Mode: ObjC; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * ***** BEGIN LICENSE BLOCK *****
5 * Copyright (C) 2006-2009 Mozilla Corporation. All rights reserved.
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>
13 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
15 * Redistribution and use in source and binary forms, with or without
16 * modification, are permitted provided that the following conditions
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.
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.
39 * ***** END LICENSE BLOCK ***** */
42 #define FORCE_PR_LOG /* Allow logging in the release build */
46 #include <Carbon/Carbon.h>
48 #import <AppKit/AppKit.h>
50 #include "gfxPlatformMac.h"
51 #include "gfxMacPlatformFontList.h"
52 #include "gfxMacFont.h"
53 #include "gfxUserFontSet.h"
55 #include "nsServiceManagerUtils.h"
58 #include "nsDirectoryServiceUtils.h"
59 #include "nsDirectoryServiceDefs.h"
60 #include "nsISimpleEnumerator.h"
65 class nsAutoreleasePool {
69 mLocalPool = [[NSAutoreleasePool alloc] init];
76 NSAutoreleasePool *mLocalPool;
79 // font info loader constants
80 static const PRUint32 kDelayBeforeLoadingCmaps = 8 * 1000; // 8secs
81 static const PRUint32 kIntervalBetweenLoadingCmaps = 150; // 150ms
82 static const PRUint32 kNumFontsPerSlice = 10; // read in info 10 fonts at a time
84 // indexes into the NSArray objects that the Cocoa font manager returns
85 // as the available members of a family
86 #define INDEX_FONT_POSTSCRIPT_NAME 0
87 #define INDEX_FONT_FACE_NAME 1
88 #define INDEX_FONT_WEIGHT 2
89 #define INDEX_FONT_TRAITS 3
91 static const int kAppleMaxWeight = 14;
92 static const int kAppleExtraLightWeight = 3;
93 static const int kAppleUltraLightWeight = 2;
95 static const int gAppleWeightToCSSWeight[] = {
98 1, // 2. W1, ultralight
99 2, // 3. W2, extralight
101 4, // 5. W4, semilight
104 6, // 8. W6, semibold
106 8, // 10. W8, extrabold
108 9, // 12. W9, ultrabold
113 // cache Cocoa's "shared font manager" for performance
114 static NSFontManager *sFontManager;
116 static void GetStringForNSString(const NSString *aSrc, nsAString& aDist)
118 aDist.SetLength([aSrc length]);
119 [aSrc getCharacters:aDist.BeginWriting()];
122 static NSString* GetNSStringForString(const nsAString& aSrc)
124 return [NSString stringWithCharacters:aSrc.BeginReading()
125 length:aSrc.Length()];
130 #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
132 #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \
133 gfxPlatform::GetLog(eGfxLog_fontlist), \
141 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
143 gfxFontFamily *aFamily,
144 PRBool aIsStandardFace)
145 : gfxFontEntry(aPostscriptName, aFamily, aIsStandardFace),
147 mATSFontRefInitialized(PR_FALSE),
148 mRequiresAAT(PR_FALSE),
150 mIsCFFInitialized(PR_FALSE)
155 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, ATSFontRef aFontRef,
156 PRUint16 aWeight, PRUint16 aStretch, PRUint32 aItalicStyle,
157 gfxUserFontData *aUserFontData)
158 : gfxFontEntry(aPostscriptName),
159 mATSFontRef(aFontRef),
160 mATSFontRefInitialized(PR_TRUE),
161 mRequiresAAT(PR_FALSE),
162 mIsCFFInitialized(PR_FALSE)
164 // xxx - stretch is basically ignored for now
166 mUserFontData = aUserFontData;
169 mFixedPitch = PR_FALSE; // xxx - do we need this for downloaded fonts?
170 mItalic = (aItalicStyle & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0;
171 mIsUserFont = aUserFontData != nsnull;
175 MacOSFontEntry::GetFontRef()
177 if (!mATSFontRefInitialized) {
178 mATSFontRefInitialized = PR_TRUE;
179 NSString *psname = GetNSStringForString(mName);
180 mATSFontRef = ::ATSFontFindFromPostScriptName(CFStringRef(psname),
181 kATSOptionFlagsDefault);
186 // ATSUI requires AAT-enabled fonts to render complex scripts correctly.
187 // For now, simple clear out the cmap codepoints for fonts that have
188 // codepoints for complex scripts. (Bug 361986)
189 // Core Text is similar, but can render Arabic using OpenType fonts as well.
191 enum eComplexScript {
192 eComplexScriptArabic,
194 eComplexScriptTibetan
198 eComplexScript script;
203 const ScriptRange gScriptsThatRequireShaping[] = {
204 { eComplexScriptArabic, 0x0600, 0x077F }, // Basic Arabic, Syriac, Arabic Supplement
205 { eComplexScriptIndic, 0x0900, 0x0D7F }, // Indic scripts - Devanagari, Bengali, ..., Malayalam
206 { eComplexScriptTibetan, 0x0F00, 0x0FFF } // Tibetan
207 // Thai seems to be "renderable" without AAT morphing tables
212 MacOSFontEntry::ReadCMAP()
216 // attempt this once, if errors occur leave a blank cmap
217 if (mCmapInitialized)
219 mCmapInitialized = PR_TRUE;
221 PRUint32 kCMAP = TRUETYPE_TAG('c','m','a','p');
223 AutoFallibleTArray<PRUint8,16384> cmap;
224 if (GetFontTable(kCMAP, cmap) != NS_OK)
225 return NS_ERROR_FAILURE;
227 PRPackedBool unicodeFont, symbolFont; // currently ignored
228 nsresult rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(),
229 mCharacterMap, mUVSOffset,
230 unicodeFont, symbolFont);
232 mCharacterMap.reset();
235 mHasCmapTable = PR_TRUE;
237 ATSFontRef fontRef = GetFontRef();
239 // for layout support, check for the presence of mort/morx and/or
240 // opentype layout tables
241 PRBool hasAATLayout =
242 (::ATSFontGetTable(fontRef, TRUETYPE_TAG('m','o','r','x'),
243 0, 0, 0, &size) == noErr) ||
244 (::ATSFontGetTable(fontRef, TRUETYPE_TAG('m','o','r','t'),
245 0, 0, 0, &size) == noErr);
248 (::ATSFontGetTable(fontRef, TRUETYPE_TAG('G','S','U','B'),
249 0, 0, 0, &size) == noErr);
251 (::ATSFontGetTable(fontRef, TRUETYPE_TAG('G','P','O','S'),
252 0, 0, 0, &size) == noErr);
254 if (hasAATLayout && !(hasGSUB || hasGPOS)) {
255 mRequiresAAT = PR_TRUE; // prefer CoreText if font has no OTL tables
258 PRUint32 numScripts =
259 sizeof(gScriptsThatRequireShaping) / sizeof(ScriptRange);
261 for (PRUint32 s = 0; s < numScripts; s++) {
262 eComplexScript whichScript = gScriptsThatRequireShaping[s].script;
264 // check to see if the cmap includes complex script codepoints
265 if (mCharacterMap.TestRange(gScriptsThatRequireShaping[s].rangeStart,
266 gScriptsThatRequireShaping[s].rangeEnd)) {
267 PRBool omitRange = PR_TRUE;
270 omitRange = PR_FALSE;
271 // prefer CoreText for Apple's complex-script fonts,
272 // even if they also have some OpenType tables
273 // (e.g. Geeza Pro Bold on 10.6; see bug 614903)
274 mRequiresAAT = PR_TRUE;
275 } else if (whichScript == eComplexScriptArabic) {
276 // special-case for Arabic:
277 // even if there's no morph table, CoreText can shape Arabic
278 // using OpenType layout; or if it's a downloaded font,
279 // assume the site knows what it's doing (as harfbuzz will
280 // be able to shape even though the font itself lacks tables
281 // stripped during sanitization).
282 // We check for GSUB here, as GPOS alone would not be ok
283 // for Arabic shaping.
284 if (hasGSUB || (mIsUserFont && !mIsLocalUserFont)) {
285 // TODO: to be really thorough, we could check that the
286 // GSUB table actually supports the 'arab' script tag.
287 omitRange = PR_FALSE;
292 mCharacterMap.ClearRange(gScriptsThatRequireShaping[s].rangeStart,
293 gScriptsThatRequireShaping[s].rangeEnd);
299 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n",
300 NS_ConvertUTF16toUTF8(mName).get(),
301 mCharacterMap.GetSize()));
308 MacOSFontEntry::GetFontTable(PRUint32 aTableTag, FallibleTArray<PRUint8>& aBuffer)
310 nsAutoreleasePool localPool;
312 ATSFontRef fontRef = GetFontRef();
313 if (fontRef == (ATSFontRef)kATSUInvalidFontID) {
314 return NS_ERROR_FAILURE;
317 ByteCount dataLength;
318 OSStatus status = ::ATSFontGetTable(fontRef, aTableTag, 0, 0, 0, &dataLength);
319 if (status != noErr) {
320 return NS_ERROR_FAILURE;
323 if (!aBuffer.AppendElements(dataLength)) {
324 return NS_ERROR_OUT_OF_MEMORY;
326 PRUint8 *dataPtr = aBuffer.Elements();
328 status = ::ATSFontGetTable(fontRef, aTableTag, 0, dataLength, dataPtr, &dataLength);
329 NS_ENSURE_TRUE(status == noErr, NS_ERROR_FAILURE);
335 MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, PRBool aNeedsBold)
337 return new gfxMacFont(this, aFontStyle, aNeedsBold);
341 MacOSFontEntry::IsCFF()
343 if (!mIsCFFInitialized) {
344 mIsCFFInitialized = PR_TRUE;
345 ATSFontRef fontRef = GetFontRef();
346 if (fontRef != (ATSFontRef)kATSUInvalidFontID) {
347 ByteCount dataLength;
348 OSStatus status = ::ATSFontGetTable(fontRef,
349 TRUETYPE_TAG('C','F','F',' '),
350 0, 0, 0, &dataLength);
351 if (status == noErr && dataLength > 0) {
361 /* gfxMacFontFamily */
364 class gfxMacFontFamily : public gfxFontFamily
367 gfxMacFontFamily(nsAString& aName) :
371 virtual ~gfxMacFontFamily() {}
373 virtual void LocalizedName(nsAString& aLocalizedName);
375 virtual void FindStyleVariations();
377 void EliminateDuplicateFaces();
381 gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName)
383 nsAutoreleasePool localPool;
385 if (!HasOtherFamilyNames()) {
386 aLocalizedName = mName;
390 NSString *family = GetNSStringForString(mName);
391 NSString *localized = [sFontManager
392 localizedNameForFamily:family
396 GetStringForNSString(localized, aLocalizedName);
400 // failed to get localized name, just use the canonical one
401 aLocalizedName = mName;
405 gfxMacFontFamily::FindStyleVariations()
410 nsAutoreleasePool localPool;
412 NSString *family = GetNSStringForString(mName);
414 // create a font entry for each face
415 NSArray *fontfaces = [sFontManager
416 availableMembersOfFontFamily:family]; // returns an array of [psname, style name, weight, traits] elements, goofy api
417 int faceCount = [fontfaces count];
420 // Bug 420981 - under 10.5, UltraLight and Light have the same weight value
421 PRBool needToCheckLightFaces =
422 (gfxPlatformMac::GetPlatform()->OSXVersion() >= MAC_OS_X_VERSION_10_5_HEX);
424 for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
425 NSArray *face = [fontfaces objectAtIndex:faceIndex];
426 NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
427 PRInt32 appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
428 PRUint32 macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
429 NSString *facename = [face objectAtIndex:INDEX_FONT_FACE_NAME];
430 PRBool isStandardFace = PR_FALSE;
432 if (needToCheckLightFaces && appKitWeight == kAppleExtraLightWeight) {
433 // if the facename contains UltraLight, set the weight to the ultralight weight value
434 NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch];
435 if (range.location != NSNotFound) {
436 appKitWeight = kAppleUltraLightWeight;
440 PRInt32 cssWeight = gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight) * 100;
443 nsAutoString postscriptFontName;
444 GetStringForNSString(psname, postscriptFontName);
446 if ([facename isEqualToString:@"Regular"] ||
447 [facename isEqualToString:@"Bold"] ||
448 [facename isEqualToString:@"Italic"] ||
449 [facename isEqualToString:@"Oblique"] ||
450 [facename isEqualToString:@"Bold Italic"] ||
451 [facename isEqualToString:@"Bold Oblique"])
453 isStandardFace = PR_TRUE;
456 // create a font entry
457 MacOSFontEntry *fontEntry = new MacOSFontEntry(postscriptFontName,
458 cssWeight, this, isStandardFace);
459 if (!fontEntry) break;
461 // set additional properties based on the traits reported by Cocoa
462 if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
463 fontEntry->mStretch = NS_FONT_STRETCH_CONDENSED;
464 } else if (macTraits & NSExpandedFontMask) {
465 fontEntry->mStretch = NS_FONT_STRETCH_EXPANDED;
467 // Cocoa fails to set the Italic traits bit for HelveticaLightItalic,
468 // at least (see bug 611855), so check for style name endings as well
469 if ((macTraits & NSItalicFontMask) ||
470 [facename hasSuffix:@"Italic"] ||
471 [facename hasSuffix:@"Oblique"])
473 fontEntry->mItalic = PR_TRUE;
475 if (macTraits & NSFixedPitchFontMask) {
476 fontEntry->mFixedPitch = PR_TRUE;
480 if (LOG_FONTLIST_ENABLED()) {
481 LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
482 " with style: %s weight: %d stretch: %d"
483 " (apple-weight: %d macTraits: %8.8x)",
484 NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
485 NS_ConvertUTF16toUTF8(Name()).get(),
486 fontEntry->IsItalic() ? "italic" : "normal",
487 cssWeight, fontEntry->Stretch(),
488 appKitWeight, macTraits));
492 // insert into font entry array of family
493 AddFontEntry(fontEntry);
496 SortAvailableFonts();
497 SetHasStyles(PR_TRUE);
499 if (mIsBadUnderlineFamily) {
500 SetBadUnderlineFonts();
505 gfxMacFontFamily::EliminateDuplicateFaces()
507 PRUint32 i, bold, numFonts, italicIndex;
508 MacOSFontEntry *italic, *nonitalic;
510 FindStyleVariations();
512 // if normal and italic have the same ATS font ref, delete italic
513 // if bold and bold-italic have the same ATS font ref, delete bold-italic
515 // two iterations, one for normal, one for bold
516 for (bold = 0; bold < 2; bold++) {
517 numFonts = mAvailableFonts.Length();
519 // find the non-italic face
521 for (i = 0; i < numFonts; i++) {
522 if ((mAvailableFonts[i]->IsBold() == (bold == 1)) &&
523 !mAvailableFonts[i]->IsItalic()) {
524 nonitalic = static_cast<MacOSFontEntry*>(mAvailableFonts[i].get());
529 // find the italic face
532 for (i = 0; i < numFonts; i++) {
533 if ((mAvailableFonts[i]->IsBold() == (bold == 1)) &&
534 mAvailableFonts[i]->IsItalic()) {
535 italic = static_cast<MacOSFontEntry*>(mAvailableFonts[i].get());
541 // if italic face and non-italic face have matching ATS refs,
542 // or if the italic returns 0 rather than an actual ATSFontRef,
543 // then the italic face is bogus so remove it
544 if (italic && (italic->GetFontRef() == 0 ||
545 italic->GetFontRef() == nonitalic->GetFontRef())) {
546 mAvailableFonts.RemoveElementAt(italicIndex);
553 /* gfxSingleFaceMacFontFamily */
556 class gfxSingleFaceMacFontFamily : public gfxFontFamily
559 gfxSingleFaceMacFontFamily(nsAString& aName) :
563 virtual ~gfxSingleFaceMacFontFamily() {}
565 virtual void LocalizedName(nsAString& aLocalizedName);
567 virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
571 gfxSingleFaceMacFontFamily::LocalizedName(nsAString& aLocalizedName)
573 nsAutoreleasePool localPool;
575 if (!HasOtherFamilyNames()) {
576 aLocalizedName = mName;
580 gfxFontEntry *fe = mAvailableFonts[0];
581 NSFont *font = [NSFont fontWithName:GetNSStringForString(fe->Name())
584 NSString *localized = [font displayName];
586 GetStringForNSString(localized, aLocalizedName);
591 // failed to get localized name, just use the canonical one
592 aLocalizedName = mName;
596 gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
598 if (mOtherFamilyNamesInitialized)
601 gfxFontEntry *fe = mAvailableFonts[0];
605 const PRUint32 kNAME = TRUETYPE_TAG('n','a','m','e');
606 AutoFallibleTArray<PRUint8,8192> buffer;
608 if (fe->GetFontTable(kNAME, buffer) != NS_OK)
611 mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
614 mOtherFamilyNamesInitialized = PR_TRUE;
618 /* gfxMacPlatformFontList */
621 gfxMacPlatformFontList::gfxMacPlatformFontList() :
622 gfxPlatformFontList(PR_FALSE), mATSGeneration(PRUint32(kATSGenerationInitial))
624 ::ATSFontNotificationSubscribe(ATSNotification,
625 kATSFontNotifyOptionDefault,
626 (void*)this, nsnull);
628 // this should always be available (though we won't actually fail if it's missing,
629 // we'll just end up doing a search and then caching the new result instead)
630 mReplacementCharFallbackFamily = NS_LITERAL_STRING("Lucida Grande");
632 // cache this in a static variable so that MacOSFontFamily objects
633 // don't have to repeatedly look it up
634 sFontManager = [NSFontManager sharedFontManager];
638 gfxMacPlatformFontList::InitFontList()
640 nsAutoreleasePool localPool;
642 ATSGeneration currentGeneration = ::ATSGetGeneration();
644 // need to ignore notifications after adding each font
645 if (mATSGeneration == currentGeneration)
648 mATSGeneration = currentGeneration;
650 LOG_FONTLIST(("(fontlist) updating to generation: %d", mATSGeneration));
654 gfxPlatformFontList::InitFontList();
656 // iterate over available families
657 NSEnumerator *families = [[sFontManager availableFontFamilies]
658 objectEnumerator]; // returns "canonical", non-localized family name
660 nsAutoString availableFamilyName;
662 NSString *availableFamily = nil;
663 while ((availableFamily = [families nextObject])) {
666 GetStringForNSString(availableFamily, availableFamilyName);
668 // create a family entry
669 gfxFontFamily *familyEntry = new gfxMacFontFamily(availableFamilyName);
670 if (!familyEntry) break;
672 // add the family entry to the hash table
673 ToLowerCase(availableFamilyName);
674 mFontFamilies.Put(availableFamilyName, familyEntry);
676 // check the bad underline blacklist
677 if (mBadUnderlineFamilyNames.Contains(availableFamilyName))
678 familyEntry->SetBadUnderlineFamily();
681 InitSingleFaceList();
683 // to avoid full search of font name tables, seed the other names table with localized names from
684 // some of the prefs fonts which are accessed via their localized names. changes in the pref fonts will only cause
685 // a font lookup miss earlier. this is a simple optimization, it's not required for correctness
688 // clean up various minor 10.4 font problems for specific fonts
689 if (gfxPlatformMac::GetPlatform()->OSXVersion() < MAC_OS_X_VERSION_10_5_HEX) {
690 // Cocoa calls report that italic faces exist for Courier and Helvetica,
691 // even though only bold faces exist so test for this using ATS font refs (10.5 has proper faces)
692 EliminateDuplicateFaces(NS_LITERAL_STRING("Courier"));
693 EliminateDuplicateFaces(NS_LITERAL_STRING("Helvetica"));
695 // Cocoa reports that Courier and Monaco are not fixed-pitch fonts
696 // so explicitly tweak these settings
697 SetFixedPitch(NS_LITERAL_STRING("Courier"));
698 SetFixedPitch(NS_LITERAL_STRING("Monaco"));
701 // start the delayed cmap loader
702 StartLoader(kDelayBeforeLoadingCmaps, kIntervalBetweenLoadingCmaps);
708 gfxMacPlatformFontList::InitSingleFaceList()
710 nsAutoTArray<nsString, 10> singleFaceFonts;
711 gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts);
713 PRUint32 numFonts = singleFaceFonts.Length();
714 for (PRUint32 i = 0; i < numFonts; i++) {
716 LOG_FONTLIST(("(fontlist-singleface) face name: %s\n",
717 NS_ConvertUTF16toUTF8(singleFaceFonts[i]).get()));
719 gfxFontEntry *fontEntry = LookupLocalFont(nsnull, singleFaceFonts[i]);
721 nsAutoString familyName, key;
722 familyName = singleFaceFonts[i];
723 GenerateFontListKey(familyName, key);
725 LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
726 NS_ConvertUTF16toUTF8(familyName).get(),
727 NS_ConvertUTF16toUTF8(key).get()));
730 // add only if doesn't exist already
732 gfxFontFamily *familyEntry;
733 if (!(familyEntry = mFontFamilies.GetWeak(key, &found))) {
734 familyEntry = new gfxSingleFaceMacFontFamily(familyName);
735 familyEntry->AddFontEntry(fontEntry);
736 familyEntry->SetHasStyles(PR_TRUE);
737 mFontFamilies.Put(key, familyEntry);
738 fontEntry->mFamily = familyEntry;
740 LOG_FONTLIST(("(fontlist-singleface) added new family\n",
741 NS_ConvertUTF16toUTF8(familyName).get(),
742 NS_ConvertUTF16toUTF8(key).get()));
750 gfxMacPlatformFontList::EliminateDuplicateFaces(const nsAString& aFamilyName)
752 gfxMacFontFamily *family =
753 static_cast<gfxMacFontFamily*>(FindFamily(aFamilyName));
756 family->EliminateDuplicateFaces();
760 gfxMacPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
762 gfxFontFamily *family = FindFamily(aFontName);
764 family->LocalizedName(aFamilyName);
768 // Gecko 1.8 used Quickdraw font api's which produce a slightly different set of "family"
769 // names. Try to resolve based on these names, in case this is stored in an old profile
770 // 1.8: "Futura", "Futura Condensed" ==> 1.9: "Futura"
772 // convert the name to a Pascal-style Str255 to try as Quickdraw name
774 NS_ConvertUTF16toUTF8 utf8name(aFontName);
775 qdname[0] = PR_MAX(255, strlen(utf8name.get()));
776 memcpy(&qdname[1], utf8name.get(), qdname[0]);
778 // look up the Quickdraw name
779 ATSFontFamilyRef atsFamily = ::ATSFontFamilyFindFromQuickDrawName(qdname);
780 if (atsFamily == (ATSFontFamilyRef)kInvalidFontFamily) {
784 // if we found a family, get its ATS name
786 OSStatus status = ::ATSFontFamilyGetName(atsFamily, kATSOptionFlagsDefault, &cfName);
787 if (status != noErr) {
791 // then use this to locate the family entry and retrieve its localized name
792 nsAutoString familyName;
793 GetStringForNSString((const NSString*)cfName, familyName);
796 family = FindFamily(familyName);
798 family->LocalizedName(aFamilyName);
806 gfxMacPlatformFontList::ATSNotification(ATSFontNotificationInfoRef aInfo,
809 // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
810 gfxMacPlatformFontList *qfc = (gfxMacPlatformFontList*)aUserArg;
811 qfc->UpdateFontList();
815 gfxMacPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle, PRBool& aNeedsBold)
817 nsAutoreleasePool localPool;
819 NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
820 nsAutoString familyName;
822 GetStringForNSString(defaultFamily, familyName);
823 return FindFontForFamily(familyName, aStyle, aNeedsBold);
827 gfxMacPlatformFontList::AppleWeightToCSSWeight(PRInt32 aAppleWeight)
829 if (aAppleWeight < 1)
831 else if (aAppleWeight > kAppleMaxWeight)
832 aAppleWeight = kAppleMaxWeight;
833 return gAppleWeightToCSSWeight[aAppleWeight];
837 gfxMacPlatformFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
838 const nsAString& aFontName)
840 nsAutoreleasePool localPool;
842 NSString *faceName = GetNSStringForString(aFontName);
844 // first lookup a single face based on postscript name
845 ATSFontRef fontRef = ::ATSFontFindFromPostScriptName(CFStringRef(faceName),
846 kATSOptionFlagsDefault);
848 // if not found, lookup using full font name
849 if (fontRef == kInvalidFont)
850 fontRef = ::ATSFontFindFromName(CFStringRef(faceName),
851 kATSOptionFlagsDefault);
854 if (fontRef == kInvalidFont)
857 MacOSFontEntry *newFontEntry;
859 PRUint16 w = aProxyEntry->mWeight;
860 NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
863 new MacOSFontEntry(aFontName, fontRef,
864 w, aProxyEntry->mStretch,
865 aProxyEntry->mItalic ?
866 FONT_STYLE_ITALIC : FONT_STYLE_NORMAL,
870 new MacOSFontEntry(aFontName, fontRef,
871 400, 0, FONT_STYLE_NORMAL, nsnull);
877 // grumble, another non-publised Apple API dependency (found in Webkit code)
878 // activated with this value, font will not be found via system lookup routines
879 // it can only be used via the created ATSFontRef
880 // needed to prevent one doc from finding a font used in a separate doc
883 kPrivateATSFontContextPrivate = 3
886 class MacOSUserFontData : public gfxUserFontData {
888 MacOSUserFontData(ATSFontContainerRef aContainerRef)
889 : mContainerRef(aContainerRef)
892 virtual ~MacOSUserFontData()
896 ::ATSFontDeactivate(mContainerRef, NULL, kATSOptionFlagsDefault);
899 ATSFontContainerRef mContainerRef;
903 gfxMacPlatformFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
904 const PRUint8 *aFontData,
909 NS_ASSERTION(aFontData, "MakePlatformFont called with null data");
911 // MakePlatformFont is responsible for deleting the font data with NS_Free
912 // so we set up a stack object to ensure it is freed even if we take an
914 struct FontDataDeleter {
915 FontDataDeleter(const PRUint8 *aFontData)
916 : mFontData(aFontData) { }
917 ~FontDataDeleter() { NS_Free((void*)mFontData); }
918 const PRUint8 *mFontData;
920 FontDataDeleter autoDelete(aFontData);
923 ATSFontContainerRef containerRef;
925 // we get occasional failures when multiple fonts are activated in quick succession
926 // if the ATS font cache is damaged; to work around this, we can retry the activation
927 const PRUint32 kMaxRetries = 3;
928 PRUint32 retryCount = 0;
929 while (retryCount++ < kMaxRetries) {
930 err = ::ATSFontActivateFromMemory(const_cast<PRUint8*>(aFontData), aLength,
931 kPrivateATSFontContextPrivate,
932 kATSFontFormatUnspecified,
934 kATSOptionFlagsDoNotNotify,
936 mATSGeneration = ::ATSGetGeneration();
941 sprintf(warnBuf, "downloaded font error, ATSFontActivateFromMemory err: %d for (%s)",
943 NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get());
949 // ignoring containers with multiple fonts, use the first face only for now
950 err = ::ATSFontFindFromContainer(containerRef, kATSOptionFlagsDefault, 1,
955 sprintf(warnBuf, "downloaded font error, ATSFontFindFromContainer err: %d for (%s)",
957 NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get());
960 ::ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
964 // now lookup the Postscript name; this may fail if the font cache is bad
966 NSString *psname = NULL;
967 err = ::ATSFontGetPostScriptName(fontRef, kATSOptionFlagsDefault, (CFStringRef*) (&psname));
973 sprintf(warnBuf, "ATSFontGetPostScriptName err = %d for (%s), retries = %d", (PRInt32)err,
974 NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get(), retryCount);
977 ::ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
978 // retry the activation a couple of times if this fails
979 // (may be a transient failure due to ATS font cache issues)
983 // font entry will own this
984 MacOSUserFontData *userFontData = new MacOSUserFontData(containerRef);
987 ::ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
991 PRUint16 w = aProxyEntry->mWeight;
992 NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
994 // create the font entry
995 nsAutoString uniqueName;
997 nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
1003 MacOSFontEntry *newFontEntry =
1004 new MacOSFontEntry(uniqueName,
1006 w, aProxyEntry->mStretch,
1007 aProxyEntry->mItalic ?
1008 FONT_STYLE_ITALIC : FONT_STYLE_NORMAL,
1011 if (!newFontEntry) {
1012 delete userFontData;
1016 // if succeeded and font cmap is good, return the new font
1017 if (newFontEntry->mIsValid && NS_SUCCEEDED(newFontEntry->ReadCMAP()))
1018 return newFontEntry;
1020 // if something is funky about this font, delete immediately
1023 sprintf(warnBuf, "downloaded font not loaded properly, removed face for (%s)",
1024 NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get());
1025 NS_WARNING(warnBuf);
1027 delete newFontEntry;
1029 // We don't retry from here; the ATS font cache issue would have caused failure earlier
1030 // so if we get here, there's something else bad going on within our font data structures.
1031 // Currently, there should be no way to reach here, as fontentry creation cannot fail
1032 // except by memory allocation failure.
1033 NS_WARNING("invalid font entry for a newly activated font");
1037 // if we get here, the activation failed (even with possible retries); can't use this font