gfx/thebes/gfxMacPlatformFontList.mm

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:ca4144c7945c
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-2009 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 ***** */
40
41 #ifdef MOZ_LOGGING
42 #define FORCE_PR_LOG /* Allow logging in the release build */
43 #endif
44 #include "prlog.h"
45
46 #include <algorithm>
47
48 #import <AppKit/AppKit.h>
49
50 #include "gfxPlatformMac.h"
51 #include "gfxMacPlatformFontList.h"
52 #include "gfxMacFont.h"
53 #include "gfxUserFontSet.h"
54 #include "harfbuzz/hb.h"
55
56 #include "nsServiceManagerUtils.h"
57 #include "nsTArray.h"
58
59 #include "nsDirectoryServiceUtils.h"
60 #include "nsDirectoryServiceDefs.h"
61 #include "nsISimpleEnumerator.h"
62 #include "nsCharTraits.h"
63 #include "nsCocoaFeatures.h"
64 #include "gfxFontConstants.h"
65
66 #include "mozilla/MemoryReporting.h"
67 #include "mozilla/Preferences.h"
68 #include "mozilla/Telemetry.h"
69 #include "mozilla/gfx/2D.h"
70
71 #include <unistd.h>
72 #include <time.h>
73
74 using namespace mozilla;
75
76 class nsAutoreleasePool {
77 public:
78 nsAutoreleasePool()
79 {
80 mLocalPool = [[NSAutoreleasePool alloc] init];
81 }
82 ~nsAutoreleasePool()
83 {
84 [mLocalPool release];
85 }
86 private:
87 NSAutoreleasePool *mLocalPool;
88 };
89
90 // indexes into the NSArray objects that the Cocoa font manager returns
91 // as the available members of a family
92 #define INDEX_FONT_POSTSCRIPT_NAME 0
93 #define INDEX_FONT_FACE_NAME 1
94 #define INDEX_FONT_WEIGHT 2
95 #define INDEX_FONT_TRAITS 3
96
97 static const int kAppleMaxWeight = 14;
98 static const int kAppleExtraLightWeight = 3;
99 static const int kAppleUltraLightWeight = 2;
100
101 static const int gAppleWeightToCSSWeight[] = {
102 0,
103 1, // 1.
104 1, // 2. W1, ultralight
105 2, // 3. W2, extralight
106 3, // 4. W3, light
107 4, // 5. W4, semilight
108 5, // 6. W5, medium
109 6, // 7.
110 6, // 8. W6, semibold
111 7, // 9. W7, bold
112 8, // 10. W8, extrabold
113 8, // 11.
114 9, // 12. W9, ultrabold
115 9, // 13
116 9 // 14
117 };
118
119 // cache Cocoa's "shared font manager" for performance
120 static NSFontManager *sFontManager;
121
122 static void GetStringForNSString(const NSString *aSrc, nsAString& aDist)
123 {
124 aDist.SetLength([aSrc length]);
125 [aSrc getCharacters:reinterpret_cast<unichar*>(aDist.BeginWriting())];
126 }
127
128 static NSString* GetNSStringForString(const nsAString& aSrc)
129 {
130 return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(aSrc.BeginReading())
131 length:aSrc.Length()];
132 }
133
134 #ifdef PR_LOGGING
135
136 #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
137 PR_LOG_DEBUG, args)
138 #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \
139 gfxPlatform::GetLog(eGfxLog_fontlist), \
140 PR_LOG_DEBUG)
141 #define LOG_CMAPDATA_ENABLED() PR_LOG_TEST( \
142 gfxPlatform::GetLog(eGfxLog_cmapdata), \
143 PR_LOG_DEBUG)
144
145 #endif // PR_LOGGING
146
147 #pragma mark-
148
149 // Complex scripts will not render correctly unless appropriate AAT or OT
150 // layout tables are present.
151 // For OpenType, we also check that the GSUB table supports the relevant
152 // script tag, to avoid using things like Arial Unicode MS for Lao (it has
153 // the characters, but lacks OpenType support).
154
155 // TODO: consider whether we should move this to gfxFontEntry and do similar
156 // cmap-masking on other platforms to avoid using fonts that won't shape
157 // properly.
158
159 nsresult
160 MacOSFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
161 {
162 // attempt this once, if errors occur leave a blank cmap
163 if (mCharacterMap) {
164 return NS_OK;
165 }
166
167 nsRefPtr<gfxCharacterMap> charmap;
168 nsresult rv;
169 bool symbolFont;
170
171 if (aFontInfoData && (charmap = GetCMAPFromFontInfo(aFontInfoData,
172 mUVSOffset,
173 symbolFont))) {
174 rv = NS_OK;
175 } else {
176 uint32_t kCMAP = TRUETYPE_TAG('c','m','a','p');
177 charmap = new gfxCharacterMap();
178 AutoTable cmapTable(this, kCMAP);
179
180 if (cmapTable) {
181 bool unicodeFont = false, symbolFont = false; // currently ignored
182 uint32_t cmapLen;
183 const uint8_t* cmapData =
184 reinterpret_cast<const uint8_t*>(hb_blob_get_data(cmapTable,
185 &cmapLen));
186 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen,
187 *charmap, mUVSOffset,
188 unicodeFont, symbolFont);
189 } else {
190 rv = NS_ERROR_NOT_AVAILABLE;
191 }
192 }
193
194 if (NS_SUCCEEDED(rv) && !HasGraphiteTables()) {
195 // We assume a Graphite font knows what it's doing,
196 // and provides whatever shaping is needed for the
197 // characters it supports, so only check/clear the
198 // complex-script ranges for non-Graphite fonts
199
200 // for layout support, check for the presence of mort/morx and/or
201 // opentype layout tables
202 bool hasAATLayout = HasFontTable(TRUETYPE_TAG('m','o','r','x')) ||
203 HasFontTable(TRUETYPE_TAG('m','o','r','t'));
204 bool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
205 bool hasGPOS = HasFontTable(TRUETYPE_TAG('G','P','O','S'));
206 if (hasAATLayout && !(hasGSUB || hasGPOS)) {
207 mRequiresAAT = true; // prefer CoreText if font has no OTL tables
208 }
209
210 for (const ScriptRange* sr = gfxPlatformFontList::sComplexScriptRanges;
211 sr->rangeStart; sr++) {
212 // check to see if the cmap includes complex script codepoints
213 if (charmap->TestRange(sr->rangeStart, sr->rangeEnd)) {
214 if (hasAATLayout) {
215 // prefer CoreText for Apple's complex-script fonts,
216 // even if they also have some OpenType tables
217 // (e.g. Geeza Pro Bold on 10.6; see bug 614903)
218 mRequiresAAT = true;
219 // and don't mask off complex-script ranges, we assume
220 // the AAT tables will provide the necessary shaping
221 continue;
222 }
223
224 // We check for GSUB here, as GPOS alone would not be ok.
225 if (hasGSUB && SupportsScriptInGSUB(sr->tags)) {
226 continue;
227 }
228
229 charmap->ClearRange(sr->rangeStart, sr->rangeEnd);
230 }
231 }
232 }
233
234 mHasCmapTable = NS_SUCCEEDED(rv);
235 if (mHasCmapTable) {
236 gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
237 mCharacterMap = pfl->FindCharMap(charmap);
238 } else {
239 // if error occurred, initialize to null cmap
240 mCharacterMap = new gfxCharacterMap();
241 }
242
243 #ifdef PR_LOGGING
244 LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d hash: %8.8x%s\n",
245 NS_ConvertUTF16toUTF8(mName).get(),
246 charmap->SizeOfIncludingThis(moz_malloc_size_of),
247 charmap->mHash, mCharacterMap == charmap ? " new" : ""));
248 if (LOG_CMAPDATA_ENABLED()) {
249 char prefix[256];
250 sprintf(prefix, "(cmapdata) name: %.220s",
251 NS_ConvertUTF16toUTF8(mName).get());
252 charmap->Dump(prefix, eGfxLog_cmapdata);
253 }
254 #endif
255
256 return rv;
257 }
258
259 gfxFont*
260 MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
261 {
262 return new gfxMacFont(this, aFontStyle, aNeedsBold);
263 }
264
265 bool
266 MacOSFontEntry::IsCFF()
267 {
268 if (!mIsCFFInitialized) {
269 mIsCFFInitialized = true;
270 mIsCFF = HasFontTable(TRUETYPE_TAG('C','F','F',' '));
271 }
272
273 return mIsCFF;
274 }
275
276 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
277 int32_t aWeight,
278 bool aIsStandardFace)
279 : gfxFontEntry(aPostscriptName, aIsStandardFace),
280 mFontRef(NULL),
281 mFontRefInitialized(false),
282 mRequiresAAT(false),
283 mIsCFF(false),
284 mIsCFFInitialized(false)
285 {
286 mWeight = aWeight;
287 }
288
289 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
290 CGFontRef aFontRef,
291 uint16_t aWeight, uint16_t aStretch,
292 uint32_t aItalicStyle,
293 bool aIsUserFont, bool aIsLocal)
294 : gfxFontEntry(aPostscriptName, false),
295 mFontRef(NULL),
296 mFontRefInitialized(false),
297 mRequiresAAT(false),
298 mIsCFF(false),
299 mIsCFFInitialized(false)
300 {
301 mFontRef = aFontRef;
302 mFontRefInitialized = true;
303 ::CFRetain(mFontRef);
304
305 mWeight = aWeight;
306 mStretch = aStretch;
307 mFixedPitch = false; // xxx - do we need this for downloaded fonts?
308 mItalic = (aItalicStyle & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
309 mIsUserFont = aIsUserFont;
310 mIsLocalUserFont = aIsLocal;
311 }
312
313 CGFontRef
314 MacOSFontEntry::GetFontRef()
315 {
316 if (!mFontRefInitialized) {
317 mFontRefInitialized = true;
318 NSString *psname = GetNSStringForString(mName);
319 mFontRef = ::CGFontCreateWithFontName(CFStringRef(psname));
320 }
321 return mFontRef;
322 }
323
324 /*static*/ void
325 MacOSFontEntry::DestroyBlobFunc(void* aUserData)
326 {
327 ::CFRelease((CFDataRef)aUserData);
328 }
329
330 hb_blob_t *
331 MacOSFontEntry::GetFontTable(uint32_t aTag)
332 {
333 CGFontRef fontRef = GetFontRef();
334 if (!fontRef) {
335 return nullptr;
336 }
337
338 CFDataRef dataRef = ::CGFontCopyTableForTag(fontRef, aTag);
339 if (dataRef) {
340 return hb_blob_create((const char*)::CFDataGetBytePtr(dataRef),
341 ::CFDataGetLength(dataRef),
342 HB_MEMORY_MODE_READONLY,
343 (void*)dataRef, DestroyBlobFunc);
344 }
345
346 return nullptr;
347 }
348
349 bool
350 MacOSFontEntry::HasFontTable(uint32_t aTableTag)
351 {
352 nsAutoreleasePool localPool;
353
354 CGFontRef fontRef = GetFontRef();
355 if (!fontRef) {
356 return false;
357 }
358
359 CFDataRef tableData = ::CGFontCopyTableForTag(fontRef, aTableTag);
360 if (!tableData) {
361 return false;
362 }
363
364 ::CFRelease(tableData);
365 return true;
366 }
367
368 void
369 MacOSFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
370 FontListSizes* aSizes) const
371 {
372 aSizes->mFontListSize += aMallocSizeOf(this);
373 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
374 }
375
376 /* gfxMacFontFamily */
377 #pragma mark-
378
379 class gfxMacFontFamily : public gfxFontFamily
380 {
381 public:
382 gfxMacFontFamily(nsAString& aName) :
383 gfxFontFamily(aName)
384 {}
385
386 virtual ~gfxMacFontFamily() {}
387
388 virtual void LocalizedName(nsAString& aLocalizedName);
389
390 virtual void FindStyleVariations(FontInfoData *aFontInfoData = nullptr);
391 };
392
393 void
394 gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName)
395 {
396 nsAutoreleasePool localPool;
397
398 if (!HasOtherFamilyNames()) {
399 aLocalizedName = mName;
400 return;
401 }
402
403 NSString *family = GetNSStringForString(mName);
404 NSString *localized = [sFontManager
405 localizedNameForFamily:family
406 face:nil];
407
408 if (localized) {
409 GetStringForNSString(localized, aLocalizedName);
410 return;
411 }
412
413 // failed to get localized name, just use the canonical one
414 aLocalizedName = mName;
415 }
416
417 // Return the CSS weight value to use for the given face, overriding what
418 // AppKit gives us (used to adjust families with bad weight values, see
419 // bug 931426).
420 // A return value of 0 indicates no override - use the existing weight.
421 static inline int
422 GetWeightOverride(const nsAString& aPSName)
423 {
424 nsAutoCString prefName("font.weight-override.");
425 // The PostScript name is required to be ASCII; if it's not, the font is
426 // broken anyway, so we really don't care that this is lossy.
427 LossyAppendUTF16toASCII(aPSName, prefName);
428 return Preferences::GetInt(prefName.get(), 0);
429 }
430
431 void
432 gfxMacFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
433 {
434 if (mHasStyles)
435 return;
436
437 nsAutoreleasePool localPool;
438
439 NSString *family = GetNSStringForString(mName);
440
441 // create a font entry for each face
442 NSArray *fontfaces = [sFontManager
443 availableMembersOfFontFamily:family]; // returns an array of [psname, style name, weight, traits] elements, goofy api
444 int faceCount = [fontfaces count];
445 int faceIndex;
446
447 for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
448 NSArray *face = [fontfaces objectAtIndex:faceIndex];
449 NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
450 int32_t appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
451 uint32_t macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
452 NSString *facename = [face objectAtIndex:INDEX_FONT_FACE_NAME];
453 bool isStandardFace = false;
454
455 if (appKitWeight == kAppleExtraLightWeight) {
456 // if the facename contains UltraLight, set the weight to the ultralight weight value
457 NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch];
458 if (range.location != NSNotFound) {
459 appKitWeight = kAppleUltraLightWeight;
460 }
461 }
462
463 // make a nsString
464 nsAutoString postscriptFontName;
465 GetStringForNSString(psname, postscriptFontName);
466
467 int32_t cssWeight = GetWeightOverride(postscriptFontName);
468 if (cssWeight) {
469 // scale down and clamp, to get a value from 1..9
470 cssWeight = ((cssWeight + 50) / 100);
471 cssWeight = std::max(1, std::min(cssWeight, 9));
472 } else {
473 cssWeight =
474 gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight);
475 }
476 cssWeight *= 100; // scale up to CSS values
477
478 if ([facename isEqualToString:@"Regular"] ||
479 [facename isEqualToString:@"Bold"] ||
480 [facename isEqualToString:@"Italic"] ||
481 [facename isEqualToString:@"Oblique"] ||
482 [facename isEqualToString:@"Bold Italic"] ||
483 [facename isEqualToString:@"Bold Oblique"])
484 {
485 isStandardFace = true;
486 }
487
488 // create a font entry
489 MacOSFontEntry *fontEntry =
490 new MacOSFontEntry(postscriptFontName, cssWeight, isStandardFace);
491 if (!fontEntry) {
492 break;
493 }
494
495 // set additional properties based on the traits reported by Cocoa
496 if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
497 fontEntry->mStretch = NS_FONT_STRETCH_CONDENSED;
498 } else if (macTraits & NSExpandedFontMask) {
499 fontEntry->mStretch = NS_FONT_STRETCH_EXPANDED;
500 }
501 // Cocoa fails to set the Italic traits bit for HelveticaLightItalic,
502 // at least (see bug 611855), so check for style name endings as well
503 if ((macTraits & NSItalicFontMask) ||
504 [facename hasSuffix:@"Italic"] ||
505 [facename hasSuffix:@"Oblique"])
506 {
507 fontEntry->mItalic = true;
508 }
509 if (macTraits & NSFixedPitchFontMask) {
510 fontEntry->mFixedPitch = true;
511 }
512
513 #ifdef PR_LOGGING
514 if (LOG_FONTLIST_ENABLED()) {
515 LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
516 " with style: %s weight: %d stretch: %d"
517 " (apple-weight: %d macTraits: %8.8x)",
518 NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
519 NS_ConvertUTF16toUTF8(Name()).get(),
520 fontEntry->IsItalic() ? "italic" : "normal",
521 cssWeight, fontEntry->Stretch(),
522 appKitWeight, macTraits));
523 }
524 #endif
525
526 // insert into font entry array of family
527 AddFontEntry(fontEntry);
528 }
529
530 SortAvailableFonts();
531 SetHasStyles(true);
532
533 if (mIsBadUnderlineFamily) {
534 SetBadUnderlineFonts();
535 }
536 }
537
538
539 /* gfxSingleFaceMacFontFamily */
540 #pragma mark-
541
542 class gfxSingleFaceMacFontFamily : public gfxFontFamily
543 {
544 public:
545 gfxSingleFaceMacFontFamily(nsAString& aName) :
546 gfxFontFamily(aName)
547 {
548 mFaceNamesInitialized = true; // omit from face name lists
549 }
550
551 virtual ~gfxSingleFaceMacFontFamily() {}
552
553 virtual void LocalizedName(nsAString& aLocalizedName);
554
555 virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
556 };
557
558 void
559 gfxSingleFaceMacFontFamily::LocalizedName(nsAString& aLocalizedName)
560 {
561 nsAutoreleasePool localPool;
562
563 if (!HasOtherFamilyNames()) {
564 aLocalizedName = mName;
565 return;
566 }
567
568 gfxFontEntry *fe = mAvailableFonts[0];
569 NSFont *font = [NSFont fontWithName:GetNSStringForString(fe->Name())
570 size:0.0];
571 if (font) {
572 NSString *localized = [font displayName];
573 if (localized) {
574 GetStringForNSString(localized, aLocalizedName);
575 return;
576 }
577 }
578
579 // failed to get localized name, just use the canonical one
580 aLocalizedName = mName;
581 }
582
583 void
584 gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
585 {
586 if (mOtherFamilyNamesInitialized) {
587 return;
588 }
589
590 gfxFontEntry *fe = mAvailableFonts[0];
591 if (!fe) {
592 return;
593 }
594
595 const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
596
597 gfxFontEntry::AutoTable nameTable(fe, kNAME);
598 if (!nameTable) {
599 return;
600 }
601
602 mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
603 nameTable,
604 true);
605
606 mOtherFamilyNamesInitialized = true;
607 }
608
609
610 /* gfxMacPlatformFontList */
611 #pragma mark-
612
613 gfxMacPlatformFontList::gfxMacPlatformFontList() :
614 gfxPlatformFontList(false),
615 mDefaultFont(nullptr)
616 {
617 ::CFNotificationCenterAddObserver(::CFNotificationCenterGetLocalCenter(),
618 this,
619 RegisteredFontsChangedNotificationCallback,
620 kCTFontManagerRegisteredFontsChangedNotification,
621 0,
622 CFNotificationSuspensionBehaviorDeliverImmediately);
623
624 // cache this in a static variable so that MacOSFontFamily objects
625 // don't have to repeatedly look it up
626 sFontManager = [NSFontManager sharedFontManager];
627 }
628
629 gfxMacPlatformFontList::~gfxMacPlatformFontList()
630 {
631 if (mDefaultFont) {
632 ::CFRelease(mDefaultFont);
633 }
634 }
635
636 nsresult
637 gfxMacPlatformFontList::InitFontList()
638 {
639 nsAutoreleasePool localPool;
640
641 Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer;
642
643 // reset font lists
644 gfxPlatformFontList::InitFontList();
645
646 // iterate over available families
647
648 CFArrayRef familyNames = CTFontManagerCopyAvailableFontFamilyNames();
649
650 // iterate over families
651 uint32_t i, numFamilies;
652
653 numFamilies = CFArrayGetCount(familyNames);
654 for (i = 0; i < numFamilies; i++) {
655 CFStringRef family = (CFStringRef)CFArrayGetValueAtIndex(familyNames, i);
656
657 // CTFontManager includes weird internal family names and
658 // LastResort, skip over those
659 if (!family ||
660 ::CFStringHasPrefix(family, CFSTR(".")) ||
661 CFStringCompare(family, CFSTR("LastResort"),
662 kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
663 continue;
664 }
665
666 nsAutoTArray<UniChar, 1024> buffer;
667 CFIndex len = ::CFStringGetLength(family);
668 buffer.SetLength(len+1);
669 ::CFStringGetCharacters(family, ::CFRangeMake(0, len),
670 buffer.Elements());
671 buffer[len] = 0;
672 nsAutoString familyName(reinterpret_cast<char16_t*>(buffer.Elements()), len);
673
674 // create a family entry
675 gfxFontFamily *familyEntry = new gfxMacFontFamily(familyName);
676 if (!familyEntry) break;
677
678 // add the family entry to the hash table
679 ToLowerCase(familyName);
680 mFontFamilies.Put(familyName, familyEntry);
681
682 // check the bad underline blacklist
683 if (mBadUnderlineFamilyNames.Contains(familyName))
684 familyEntry->SetBadUnderlineFamily();
685 }
686
687 CFRelease(familyNames);
688
689 InitSingleFaceList();
690
691 // to avoid full search of font name tables, seed the other names table with localized names from
692 // some of the prefs fonts which are accessed via their localized names. changes in the pref fonts will only cause
693 // a font lookup miss earlier. this is a simple optimization, it's not required for correctness
694 PreloadNamesList();
695
696 // start the delayed cmap loader
697 GetPrefsAndStartLoader();
698
699 return NS_OK;
700 }
701
702 void
703 gfxMacPlatformFontList::InitSingleFaceList()
704 {
705 nsAutoTArray<nsString, 10> singleFaceFonts;
706 gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts);
707
708 uint32_t numFonts = singleFaceFonts.Length();
709 for (uint32_t i = 0; i < numFonts; i++) {
710 #ifdef PR_LOGGING
711 LOG_FONTLIST(("(fontlist-singleface) face name: %s\n",
712 NS_ConvertUTF16toUTF8(singleFaceFonts[i]).get()));
713 #endif
714 gfxFontEntry *fontEntry = LookupLocalFont(nullptr, singleFaceFonts[i]);
715 if (fontEntry) {
716 nsAutoString familyName, key;
717 familyName = singleFaceFonts[i];
718 GenerateFontListKey(familyName, key);
719 #ifdef PR_LOGGING
720 LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
721 NS_ConvertUTF16toUTF8(familyName).get(),
722 NS_ConvertUTF16toUTF8(key).get()));
723 #endif
724
725 // add only if doesn't exist already
726 if (!mFontFamilies.GetWeak(key)) {
727 gfxFontFamily *familyEntry =
728 new gfxSingleFaceMacFontFamily(familyName);
729 familyEntry->AddFontEntry(fontEntry);
730 familyEntry->SetHasStyles(true);
731 mFontFamilies.Put(key, familyEntry);
732 #ifdef PR_LOGGING
733 LOG_FONTLIST(("(fontlist-singleface) added new family\n",
734 NS_ConvertUTF16toUTF8(familyName).get(),
735 NS_ConvertUTF16toUTF8(key).get()));
736 #endif
737 }
738 }
739 }
740 }
741
742 bool
743 gfxMacPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
744 {
745 gfxFontFamily *family = FindFamily(aFontName);
746 if (family) {
747 family->LocalizedName(aFamilyName);
748 return true;
749 }
750
751 return false;
752 }
753
754 void
755 gfxMacPlatformFontList::RegisteredFontsChangedNotificationCallback(CFNotificationCenterRef center,
756 void *observer,
757 CFStringRef name,
758 const void *object,
759 CFDictionaryRef userInfo)
760 {
761 if (!::CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)) {
762 return;
763 }
764
765 gfxMacPlatformFontList* fl = static_cast<gfxMacPlatformFontList*>(observer);
766
767 // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
768 fl->UpdateFontList();
769
770 // modify a preference that will trigger reflow everywhere
771 fl->ForceGlobalReflow();
772 }
773
774 gfxFontEntry*
775 gfxMacPlatformFontList::GlobalFontFallback(const uint32_t aCh,
776 int32_t aRunScript,
777 const gfxFontStyle* aMatchStyle,
778 uint32_t& aCmapCount,
779 gfxFontFamily** aMatchedFamily)
780 {
781 bool useCmaps = gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
782
783 if (useCmaps) {
784 return gfxPlatformFontList::GlobalFontFallback(aCh,
785 aRunScript,
786 aMatchStyle,
787 aCmapCount,
788 aMatchedFamily);
789 }
790
791 CFStringRef str;
792 UniChar ch[2];
793 CFIndex len = 1;
794
795 if (IS_IN_BMP(aCh)) {
796 ch[0] = aCh;
797 str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 1,
798 kCFAllocatorNull);
799 } else {
800 ch[0] = H_SURROGATE(aCh);
801 ch[1] = L_SURROGATE(aCh);
802 str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, ch, 2,
803 kCFAllocatorNull);
804 if (!str) {
805 return nullptr;
806 }
807 len = 2;
808 }
809
810 // use CoreText to find the fallback family
811
812 gfxFontEntry *fontEntry = nullptr;
813 CTFontRef fallback;
814 bool cantUseFallbackFont = false;
815
816 if (!mDefaultFont) {
817 mDefaultFont = ::CTFontCreateWithName(CFSTR("LucidaGrande"), 12.f,
818 NULL);
819 }
820
821 fallback = ::CTFontCreateForString(mDefaultFont, str,
822 ::CFRangeMake(0, len));
823
824 if (fallback) {
825 CFStringRef familyName = ::CTFontCopyFamilyName(fallback);
826 ::CFRelease(fallback);
827
828 if (familyName &&
829 ::CFStringCompare(familyName, CFSTR("LastResort"),
830 kCFCompareCaseInsensitive) != kCFCompareEqualTo)
831 {
832 nsAutoTArray<UniChar, 1024> buffer;
833 CFIndex len = ::CFStringGetLength(familyName);
834 buffer.SetLength(len+1);
835 ::CFStringGetCharacters(familyName, ::CFRangeMake(0, len),
836 buffer.Elements());
837 buffer[len] = 0;
838 nsDependentString familyName(reinterpret_cast<char16_t*>(buffer.Elements()), len);
839
840 bool needsBold; // ignored in the system fallback case
841
842 gfxFontFamily *family = FindFamily(familyName);
843 if (family) {
844 fontEntry = family->FindFontForStyle(*aMatchStyle, needsBold);
845 if (fontEntry) {
846 if (fontEntry->TestCharacterMap(aCh)) {
847 *aMatchedFamily = family;
848 } else {
849 fontEntry = nullptr;
850 cantUseFallbackFont = true;
851 }
852 }
853 }
854 }
855
856 if (familyName) {
857 ::CFRelease(familyName);
858 }
859 }
860
861 if (cantUseFallbackFont) {
862 Telemetry::Accumulate(Telemetry::BAD_FALLBACK_FONT, cantUseFallbackFont);
863 }
864
865 ::CFRelease(str);
866
867 return fontEntry;
868 }
869
870 gfxFontFamily*
871 gfxMacPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle)
872 {
873 nsAutoreleasePool localPool;
874
875 NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
876 nsAutoString familyName;
877
878 GetStringForNSString(defaultFamily, familyName);
879 return FindFamily(familyName);
880 }
881
882 int32_t
883 gfxMacPlatformFontList::AppleWeightToCSSWeight(int32_t aAppleWeight)
884 {
885 if (aAppleWeight < 1)
886 aAppleWeight = 1;
887 else if (aAppleWeight > kAppleMaxWeight)
888 aAppleWeight = kAppleMaxWeight;
889 return gAppleWeightToCSSWeight[aAppleWeight];
890 }
891
892 gfxFontEntry*
893 gfxMacPlatformFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
894 const nsAString& aFontName)
895 {
896 nsAutoreleasePool localPool;
897
898 NSString *faceName = GetNSStringForString(aFontName);
899 MacOSFontEntry *newFontEntry;
900
901 // lookup face based on postscript or full name
902 CGFontRef fontRef = ::CGFontCreateWithFontName(CFStringRef(faceName));
903 if (!fontRef) {
904 return nullptr;
905 }
906
907 if (aProxyEntry) {
908 uint16_t w = aProxyEntry->mWeight;
909 NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
910
911 newFontEntry =
912 new MacOSFontEntry(aFontName, fontRef,
913 w, aProxyEntry->mStretch,
914 aProxyEntry->mItalic ?
915 NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL,
916 true, true);
917 } else {
918 newFontEntry =
919 new MacOSFontEntry(aFontName, fontRef,
920 400, 0, NS_FONT_STYLE_NORMAL,
921 false, false);
922 }
923 ::CFRelease(fontRef);
924
925 return newFontEntry;
926 }
927
928 static void ReleaseData(void *info, const void *data, size_t size)
929 {
930 NS_Free((void*)data);
931 }
932
933 gfxFontEntry*
934 gfxMacPlatformFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
935 const uint8_t *aFontData,
936 uint32_t aLength)
937 {
938 NS_ASSERTION(aFontData, "MakePlatformFont called with null data");
939
940 uint16_t w = aProxyEntry->mWeight;
941 NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
942
943 // create the font entry
944 nsAutoString uniqueName;
945
946 nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
947 if (NS_FAILED(rv)) {
948 return nullptr;
949 }
950
951 CGDataProviderRef provider =
952 ::CGDataProviderCreateWithData(nullptr, aFontData, aLength,
953 &ReleaseData);
954 CGFontRef fontRef = ::CGFontCreateWithDataProvider(provider);
955 ::CGDataProviderRelease(provider);
956
957 if (!fontRef) {
958 return nullptr;
959 }
960
961 nsAutoPtr<MacOSFontEntry>
962 newFontEntry(new MacOSFontEntry(uniqueName, fontRef, w,
963 aProxyEntry->mStretch,
964 aProxyEntry->mItalic ?
965 NS_FONT_STYLE_ITALIC :
966 NS_FONT_STYLE_NORMAL,
967 true, false));
968 ::CFRelease(fontRef);
969
970 // if succeeded and font cmap is good, return the new font
971 if (newFontEntry->mIsValid && NS_SUCCEEDED(newFontEntry->ReadCMAP())) {
972 return newFontEntry.forget();
973 }
974
975 // if something is funky about this font, delete immediately
976
977 #if DEBUG
978 NS_WARNING("downloaded font not loaded properly");
979 #endif
980
981 return nullptr;
982 }
983
984 // used to load system-wide font info on off-main thread
985 class MacFontInfo : public FontInfoData {
986 public:
987 MacFontInfo(bool aLoadOtherNames,
988 bool aLoadFaceNames,
989 bool aLoadCmaps) :
990 FontInfoData(aLoadOtherNames, aLoadFaceNames, aLoadCmaps)
991 {}
992
993 virtual ~MacFontInfo() {}
994
995 virtual void Load() {
996 nsAutoreleasePool localPool;
997 // bug 975460 - async font loader crashes sometimes under 10.6, disable
998 if (nsCocoaFeatures::OnLionOrLater()) {
999 FontInfoData::Load();
1000 }
1001 }
1002
1003 // loads font data for all members of a given family
1004 virtual void LoadFontFamilyData(const nsAString& aFamilyName);
1005 };
1006
1007 void
1008 MacFontInfo::LoadFontFamilyData(const nsAString& aFamilyName)
1009 {
1010 // family name ==> CTFontDescriptor
1011 NSString *famName = GetNSStringForString(aFamilyName);
1012 CFStringRef family = CFStringRef(famName);
1013
1014 CFMutableDictionaryRef attr =
1015 CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
1016 &kCFTypeDictionaryValueCallBacks);
1017 CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, family);
1018 CTFontDescriptorRef fd = CTFontDescriptorCreateWithAttributes(attr);
1019 CFRelease(attr);
1020 CFArrayRef matchingFonts =
1021 CTFontDescriptorCreateMatchingFontDescriptors(fd, NULL);
1022 CFRelease(fd);
1023 if (!matchingFonts) {
1024 return;
1025 }
1026
1027 nsTArray<nsString> otherFamilyNames;
1028 bool hasOtherFamilyNames = true;
1029
1030 // iterate over faces in the family
1031 int f, numFaces = (int) CFArrayGetCount(matchingFonts);
1032 for (f = 0; f < numFaces; f++) {
1033 mLoadStats.fonts++;
1034
1035 CTFontDescriptorRef faceDesc =
1036 (CTFontDescriptorRef)CFArrayGetValueAtIndex(matchingFonts, f);
1037 if (!faceDesc) {
1038 continue;
1039 }
1040 CTFontRef fontRef = CTFontCreateWithFontDescriptor(faceDesc,
1041 0.0, nullptr);
1042 if (!fontRef) {
1043 NS_WARNING("failed to create a CTFontRef");
1044 continue;
1045 }
1046
1047 if (mLoadCmaps) {
1048 // face name
1049 CFStringRef faceName = (CFStringRef)
1050 CTFontDescriptorCopyAttribute(faceDesc, kCTFontNameAttribute);
1051
1052 nsAutoTArray<UniChar, 1024> buffer;
1053 CFIndex len = CFStringGetLength(faceName);
1054 buffer.SetLength(len+1);
1055 CFStringGetCharacters(faceName, ::CFRangeMake(0, len),
1056 buffer.Elements());
1057 buffer[len] = 0;
1058 nsAutoString fontName(reinterpret_cast<char16_t*>(buffer.Elements()),
1059 len);
1060
1061 // load the cmap data
1062 FontFaceData fontData;
1063 CFDataRef cmapTable = CTFontCopyTable(fontRef, kCTFontTableCmap,
1064 kCTFontTableOptionNoOptions);
1065
1066 if (cmapTable) {
1067 bool unicodeFont = false, symbolFont = false; // ignored
1068 const uint8_t *cmapData =
1069 (const uint8_t*)CFDataGetBytePtr(cmapTable);
1070 uint32_t cmapLen = CFDataGetLength(cmapTable);
1071 nsRefPtr<gfxCharacterMap> charmap = new gfxCharacterMap();
1072 uint32_t offset;
1073 nsresult rv;
1074
1075 rv = gfxFontUtils::ReadCMAP(cmapData, cmapLen, *charmap, offset,
1076 unicodeFont, symbolFont);
1077 if (NS_SUCCEEDED(rv)) {
1078 fontData.mCharacterMap = charmap;
1079 fontData.mUVSOffset = offset;
1080 fontData.mSymbolFont = symbolFont;
1081 mLoadStats.cmaps++;
1082 }
1083 CFRelease(cmapTable);
1084 }
1085
1086 mFontFaceData.Put(fontName, fontData);
1087 CFRelease(faceName);
1088 }
1089
1090 if (mLoadOtherNames && hasOtherFamilyNames) {
1091 CFDataRef nameTable = CTFontCopyTable(fontRef, kCTFontTableName,
1092 kCTFontTableOptionNoOptions);
1093
1094 if (nameTable) {
1095 const char *nameData = (const char*)CFDataGetBytePtr(nameTable);
1096 uint32_t nameLen = CFDataGetLength(nameTable);
1097 gfxFontFamily::ReadOtherFamilyNamesForFace(aFamilyName,
1098 nameData, nameLen,
1099 otherFamilyNames,
1100 false);
1101 hasOtherFamilyNames = otherFamilyNames.Length() != 0;
1102 CFRelease(nameTable);
1103 }
1104 }
1105
1106 CFRelease(fontRef);
1107 }
1108 CFRelease(matchingFonts);
1109
1110 // if found other names, insert them in the hash table
1111 if (otherFamilyNames.Length() != 0) {
1112 mOtherFamilyNames.Put(aFamilyName, otherFamilyNames);
1113 mLoadStats.othernames += otherFamilyNames.Length();
1114 }
1115 }
1116
1117 already_AddRefed<FontInfoData>
1118 gfxMacPlatformFontList::CreateFontInfoData()
1119 {
1120 bool loadCmaps = !UsesSystemFallback() ||
1121 gfxPlatform::GetPlatform()->UseCmapsDuringSystemFallback();
1122
1123 nsRefPtr<MacFontInfo> fi =
1124 new MacFontInfo(true, NeedFullnamePostscriptNames(), loadCmaps);
1125 return fi.forget();
1126 }
1127

mercurial