PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/gfx/thebes/gfxMacPlatformFontList.mm

https://bitbucket.org/MeeGoAdmin/mozilla-central/
Objective C++ | 1175 lines | 845 code | 194 blank | 136 comment | 117 complexity | b0f1714f833c709bcf642ecc85e0d6ab MD5 | raw file
Possible License(s): AGPL-1.0, MIT, BSD-3-Clause, Apache-2.0, LGPL-2.1, 0BSD, LGPL-3.0, MPL-2.0-no-copyleft-exception, GPL-2.0, JSON
  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. #ifdef MOZ_LOGGING
  41. #define FORCE_PR_LOG /* Allow logging in the release build */
  42. #endif
  43. #include "prlog.h"
  44. #include <Carbon/Carbon.h>
  45. #import <AppKit/AppKit.h>
  46. #include "gfxPlatformMac.h"
  47. #include "gfxMacPlatformFontList.h"
  48. #include "gfxMacFont.h"
  49. #include "gfxUserFontSet.h"
  50. #include "nsServiceManagerUtils.h"
  51. #include "nsTArray.h"
  52. #include "nsDirectoryServiceUtils.h"
  53. #include "nsDirectoryServiceDefs.h"
  54. #include "nsISimpleEnumerator.h"
  55. #include "mozilla/Telemetry.h"
  56. #include <unistd.h>
  57. #include <time.h>
  58. using namespace mozilla;
  59. class nsAutoreleasePool {
  60. public:
  61. nsAutoreleasePool()
  62. {
  63. mLocalPool = [[NSAutoreleasePool alloc] init];
  64. }
  65. ~nsAutoreleasePool()
  66. {
  67. [mLocalPool release];
  68. }
  69. private:
  70. NSAutoreleasePool *mLocalPool;
  71. };
  72. // font info loader constants
  73. static const PRUint32 kDelayBeforeLoadingCmaps = 8 * 1000; // 8secs
  74. static const PRUint32 kIntervalBetweenLoadingCmaps = 150; // 150ms
  75. static const PRUint32 kNumFontsPerSlice = 10; // read in info 10 fonts at a time
  76. // indexes into the NSArray objects that the Cocoa font manager returns
  77. // as the available members of a family
  78. #define INDEX_FONT_POSTSCRIPT_NAME 0
  79. #define INDEX_FONT_FACE_NAME 1
  80. #define INDEX_FONT_WEIGHT 2
  81. #define INDEX_FONT_TRAITS 3
  82. static const int kAppleMaxWeight = 14;
  83. static const int kAppleExtraLightWeight = 3;
  84. static const int kAppleUltraLightWeight = 2;
  85. static const int gAppleWeightToCSSWeight[] = {
  86. 0,
  87. 1, // 1.
  88. 1, // 2. W1, ultralight
  89. 2, // 3. W2, extralight
  90. 3, // 4. W3, light
  91. 4, // 5. W4, semilight
  92. 5, // 6. W5, medium
  93. 6, // 7.
  94. 6, // 8. W6, semibold
  95. 7, // 9. W7, bold
  96. 8, // 10. W8, extrabold
  97. 8, // 11.
  98. 9, // 12. W9, ultrabold
  99. 9, // 13
  100. 9 // 14
  101. };
  102. // cache Cocoa's "shared font manager" for performance
  103. static NSFontManager *sFontManager;
  104. static void GetStringForNSString(const NSString *aSrc, nsAString& aDist)
  105. {
  106. aDist.SetLength([aSrc length]);
  107. [aSrc getCharacters:aDist.BeginWriting()];
  108. }
  109. static NSString* GetNSStringForString(const nsAString& aSrc)
  110. {
  111. return [NSString stringWithCharacters:aSrc.BeginReading()
  112. length:aSrc.Length()];
  113. }
  114. #ifdef PR_LOGGING
  115. #define LOG_FONTLIST(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
  116. PR_LOG_DEBUG, args)
  117. #define LOG_FONTLIST_ENABLED() PR_LOG_TEST( \
  118. gfxPlatform::GetLog(eGfxLog_fontlist), \
  119. PR_LOG_DEBUG)
  120. #endif // PR_LOGGING
  121. /* MacOSFontEntry - abstract superclass for ATS and CG font entries */
  122. #pragma mark-
  123. MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
  124. PRInt32 aWeight,
  125. gfxFontFamily *aFamily,
  126. PRBool aIsStandardFace)
  127. : gfxFontEntry(aPostscriptName, aFamily, aIsStandardFace),
  128. mFontRef(NULL),
  129. mFontRefInitialized(PR_FALSE),
  130. mRequiresAAT(PR_FALSE),
  131. mIsCFF(PR_FALSE),
  132. mIsCFFInitialized(PR_FALSE)
  133. {
  134. mWeight = aWeight;
  135. }
  136. // ATSUI requires AAT-enabled fonts to render complex scripts correctly.
  137. // For now, simple clear out the cmap codepoints for fonts that have
  138. // codepoints for complex scripts. (Bug 361986)
  139. // Core Text is similar, but can render Arabic using OpenType fonts as well.
  140. enum eComplexScript {
  141. eComplexScriptArabic,
  142. eComplexScriptIndic,
  143. eComplexScriptTibetan
  144. };
  145. struct ScriptRange {
  146. eComplexScript script;
  147. PRUint32 rangeStart;
  148. PRUint32 rangeEnd;
  149. };
  150. const ScriptRange gScriptsThatRequireShaping[] = {
  151. { eComplexScriptArabic, 0x0600, 0x077F }, // Basic Arabic, Syriac, Arabic Supplement
  152. { eComplexScriptIndic, 0x0900, 0x0D7F }, // Indic scripts - Devanagari, Bengali, ..., Malayalam
  153. { eComplexScriptTibetan, 0x0F00, 0x0FFF } // Tibetan
  154. // Thai seems to be "renderable" without AAT morphing tables
  155. // xxx - Lao, Khmer?
  156. };
  157. nsresult
  158. MacOSFontEntry::ReadCMAP()
  159. {
  160. // attempt this once, if errors occur leave a blank cmap
  161. if (mCmapInitialized) {
  162. return NS_OK;
  163. }
  164. mCmapInitialized = PR_TRUE;
  165. PRUint32 kCMAP = TRUETYPE_TAG('c','m','a','p');
  166. AutoFallibleTArray<PRUint8,16384> cmap;
  167. if (GetFontTable(kCMAP, cmap) != NS_OK) {
  168. return NS_ERROR_FAILURE;
  169. }
  170. PRPackedBool unicodeFont, symbolFont; // currently ignored
  171. nsresult rv = gfxFontUtils::ReadCMAP(cmap.Elements(), cmap.Length(),
  172. mCharacterMap, mUVSOffset,
  173. unicodeFont, symbolFont);
  174. if (NS_FAILED(rv)) {
  175. mCharacterMap.reset();
  176. return rv;
  177. }
  178. mHasCmapTable = PR_TRUE;
  179. CGFontRef fontRef = GetFontRef();
  180. if (!fontRef) {
  181. return NS_ERROR_FAILURE;
  182. }
  183. // for layout support, check for the presence of mort/morx and/or
  184. // opentype layout tables
  185. PRBool hasAATLayout = HasFontTable(TRUETYPE_TAG('m','o','r','x')) ||
  186. HasFontTable(TRUETYPE_TAG('m','o','r','t'));
  187. PRBool hasGSUB = HasFontTable(TRUETYPE_TAG('G','S','U','B'));
  188. PRBool hasGPOS = HasFontTable(TRUETYPE_TAG('G','P','O','S'));
  189. if (hasAATLayout && !(hasGSUB || hasGPOS)) {
  190. mRequiresAAT = PR_TRUE; // prefer CoreText if font has no OTL tables
  191. }
  192. PRUint32 numScripts =
  193. sizeof(gScriptsThatRequireShaping) / sizeof(ScriptRange);
  194. for (PRUint32 s = 0; s < numScripts; s++) {
  195. eComplexScript whichScript = gScriptsThatRequireShaping[s].script;
  196. // check to see if the cmap includes complex script codepoints
  197. if (mCharacterMap.TestRange(gScriptsThatRequireShaping[s].rangeStart,
  198. gScriptsThatRequireShaping[s].rangeEnd)) {
  199. PRBool omitRange = PR_TRUE;
  200. if (hasAATLayout) {
  201. omitRange = PR_FALSE;
  202. // prefer CoreText for Apple's complex-script fonts,
  203. // even if they also have some OpenType tables
  204. // (e.g. Geeza Pro Bold on 10.6; see bug 614903)
  205. mRequiresAAT = PR_TRUE;
  206. } else if (whichScript == eComplexScriptArabic) {
  207. // special-case for Arabic:
  208. // even if there's no morph table, CoreText can shape Arabic
  209. // using OpenType layout; or if it's a downloaded font,
  210. // assume the site knows what it's doing (as harfbuzz will
  211. // be able to shape even though the font itself lacks tables
  212. // stripped during sanitization).
  213. // We check for GSUB here, as GPOS alone would not be ok
  214. // for Arabic shaping.
  215. if (hasGSUB || (mIsUserFont && !mIsLocalUserFont)) {
  216. // TODO: to be really thorough, we could check that the
  217. // GSUB table actually supports the 'arab' script tag.
  218. omitRange = PR_FALSE;
  219. }
  220. }
  221. if (omitRange) {
  222. mCharacterMap.ClearRange(gScriptsThatRequireShaping[s].rangeStart,
  223. gScriptsThatRequireShaping[s].rangeEnd);
  224. }
  225. }
  226. }
  227. #ifdef PR_LOGGING
  228. LOG_FONTLIST(("(fontlist-cmap) name: %s, size: %d\n",
  229. NS_ConvertUTF16toUTF8(mName).get(),
  230. mCharacterMap.GetSize()));
  231. #endif
  232. return rv;
  233. }
  234. gfxFont*
  235. MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, PRBool aNeedsBold)
  236. {
  237. return new gfxMacFont(this, aFontStyle, aNeedsBold);
  238. }
  239. PRBool
  240. MacOSFontEntry::IsCFF()
  241. {
  242. if (!mIsCFFInitialized) {
  243. mIsCFFInitialized = PR_TRUE;
  244. mIsCFF = HasFontTable(TRUETYPE_TAG('C','F','F',' '));
  245. }
  246. return mIsCFF;
  247. }
  248. /* ATSFontEntry - used on Mac OS X 10.5.x */
  249. #pragma mark-
  250. ATSFontEntry::ATSFontEntry(const nsAString& aPostscriptName,
  251. PRInt32 aWeight,
  252. gfxFontFamily *aFamily,
  253. PRBool aIsStandardFace)
  254. : MacOSFontEntry(aPostscriptName, aWeight, aFamily, aIsStandardFace),
  255. mATSFontRef(kInvalidFont),
  256. mATSFontRefInitialized(PR_FALSE)
  257. {
  258. }
  259. ATSFontEntry::ATSFontEntry(const nsAString& aPostscriptName,
  260. ATSFontRef aFontRef,
  261. PRUint16 aWeight, PRUint16 aStretch,
  262. PRUint32 aItalicStyle,
  263. gfxUserFontData *aUserFontData,
  264. PRBool aIsLocal)
  265. : MacOSFontEntry(aPostscriptName, aWeight, nsnull, PR_FALSE)
  266. {
  267. mATSFontRef = aFontRef;
  268. mATSFontRefInitialized = PR_TRUE;
  269. mWeight = aWeight;
  270. mStretch = aStretch;
  271. mFixedPitch = PR_FALSE; // xxx - do we need this for downloaded fonts?
  272. mItalic = (aItalicStyle & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0;
  273. mUserFontData = aUserFontData;
  274. mIsUserFont = (aUserFontData != nsnull) || aIsLocal;
  275. mIsLocalUserFont = aIsLocal;
  276. }
  277. ATSFontRef
  278. ATSFontEntry::GetATSFontRef()
  279. {
  280. if (!mATSFontRefInitialized) {
  281. mATSFontRefInitialized = PR_TRUE;
  282. NSString *psname = GetNSStringForString(mName);
  283. mATSFontRef = ::ATSFontFindFromPostScriptName(CFStringRef(psname),
  284. kATSOptionFlagsDefault);
  285. }
  286. return mATSFontRef;
  287. }
  288. CGFontRef
  289. ATSFontEntry::GetFontRef()
  290. {
  291. if (mFontRefInitialized) {
  292. return mFontRef;
  293. }
  294. // GetATSFontRef will initialize mATSFontRef
  295. if (GetATSFontRef() == kInvalidFont) {
  296. return nsnull;
  297. }
  298. mFontRef = ::CGFontCreateWithPlatformFont(&mATSFontRef);
  299. mFontRefInitialized = PR_TRUE;
  300. return mFontRef;
  301. }
  302. nsresult
  303. ATSFontEntry::GetFontTable(PRUint32 aTableTag, FallibleTArray<PRUint8>& aBuffer)
  304. {
  305. nsAutoreleasePool localPool;
  306. ATSFontRef fontRef = GetATSFontRef();
  307. if (fontRef == kInvalidFont) {
  308. return NS_ERROR_FAILURE;
  309. }
  310. ByteCount dataLength;
  311. OSStatus status = ::ATSFontGetTable(fontRef, aTableTag, 0, 0, 0, &dataLength);
  312. if (status != noErr) {
  313. return NS_ERROR_FAILURE;
  314. }
  315. if (!aBuffer.AppendElements(dataLength)) {
  316. return NS_ERROR_OUT_OF_MEMORY;
  317. }
  318. PRUint8 *dataPtr = aBuffer.Elements();
  319. status = ::ATSFontGetTable(fontRef, aTableTag, 0, dataLength, dataPtr, &dataLength);
  320. NS_ENSURE_TRUE(status == noErr, NS_ERROR_FAILURE);
  321. return NS_OK;
  322. }
  323. PRBool
  324. ATSFontEntry::HasFontTable(PRUint32 aTableTag)
  325. {
  326. ATSFontRef fontRef = GetATSFontRef();
  327. ByteCount size;
  328. return fontRef != kInvalidFont &&
  329. (::ATSFontGetTable(fontRef, aTableTag, 0, 0, 0, &size) == noErr);
  330. }
  331. /* CGFontEntry - used on Mac OS X 10.6+ */
  332. #pragma mark-
  333. CGFontEntry::CGFontEntry(const nsAString& aPostscriptName,
  334. PRInt32 aWeight,
  335. gfxFontFamily *aFamily,
  336. PRBool aIsStandardFace)
  337. : MacOSFontEntry(aPostscriptName, aWeight, aFamily, aIsStandardFace)
  338. {
  339. }
  340. CGFontEntry::CGFontEntry(const nsAString& aPostscriptName,
  341. CGFontRef aFontRef,
  342. PRUint16 aWeight, PRUint16 aStretch,
  343. PRUint32 aItalicStyle,
  344. PRBool aIsUserFont, PRBool aIsLocal)
  345. : MacOSFontEntry(aPostscriptName, aWeight, nsnull, PR_FALSE)
  346. {
  347. mFontRef = aFontRef;
  348. mFontRefInitialized = PR_TRUE;
  349. ::CFRetain(mFontRef);
  350. mWeight = aWeight;
  351. mStretch = aStretch;
  352. mFixedPitch = PR_FALSE; // xxx - do we need this for downloaded fonts?
  353. mItalic = (aItalicStyle & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0;
  354. mIsUserFont = aIsUserFont;
  355. mIsLocalUserFont = aIsLocal;
  356. }
  357. CGFontRef
  358. CGFontEntry::GetFontRef()
  359. {
  360. if (!mFontRefInitialized) {
  361. mFontRefInitialized = PR_TRUE;
  362. NSString *psname = GetNSStringForString(mName);
  363. mFontRef = ::CGFontCreateWithFontName(CFStringRef(psname));
  364. }
  365. return mFontRef;
  366. }
  367. nsresult
  368. CGFontEntry::GetFontTable(PRUint32 aTableTag, FallibleTArray<PRUint8>& aBuffer)
  369. {
  370. nsAutoreleasePool localPool;
  371. CGFontRef fontRef = GetFontRef();
  372. if (!fontRef) {
  373. return NS_ERROR_FAILURE;
  374. }
  375. CFDataRef tableData = ::CGFontCopyTableForTag(fontRef, aTableTag);
  376. if (!tableData) {
  377. return NS_ERROR_FAILURE;
  378. }
  379. nsresult rval = NS_OK;
  380. CFIndex dataLength = ::CFDataGetLength(tableData);
  381. if (aBuffer.AppendElements(dataLength)) {
  382. ::CFDataGetBytes(tableData, ::CFRangeMake(0, dataLength),
  383. aBuffer.Elements());
  384. } else {
  385. rval = NS_ERROR_OUT_OF_MEMORY;
  386. }
  387. ::CFRelease(tableData);
  388. return rval;
  389. }
  390. PRBool
  391. CGFontEntry::HasFontTable(PRUint32 aTableTag)
  392. {
  393. nsAutoreleasePool localPool;
  394. CGFontRef fontRef = GetFontRef();
  395. if (!fontRef) {
  396. return PR_FALSE;
  397. }
  398. CFDataRef tableData = ::CGFontCopyTableForTag(fontRef, aTableTag);
  399. if (!tableData) {
  400. return PR_FALSE;
  401. }
  402. ::CFRelease(tableData);
  403. return PR_TRUE;
  404. }
  405. /* gfxMacFontFamily */
  406. #pragma mark-
  407. class gfxMacFontFamily : public gfxFontFamily
  408. {
  409. public:
  410. gfxMacFontFamily(nsAString& aName) :
  411. gfxFontFamily(aName)
  412. {}
  413. virtual ~gfxMacFontFamily() {}
  414. virtual void LocalizedName(nsAString& aLocalizedName);
  415. virtual void FindStyleVariations();
  416. };
  417. void
  418. gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName)
  419. {
  420. nsAutoreleasePool localPool;
  421. if (!HasOtherFamilyNames()) {
  422. aLocalizedName = mName;
  423. return;
  424. }
  425. NSString *family = GetNSStringForString(mName);
  426. NSString *localized = [sFontManager
  427. localizedNameForFamily:family
  428. face:nil];
  429. if (localized) {
  430. GetStringForNSString(localized, aLocalizedName);
  431. return;
  432. }
  433. // failed to get localized name, just use the canonical one
  434. aLocalizedName = mName;
  435. }
  436. void
  437. gfxMacFontFamily::FindStyleVariations()
  438. {
  439. if (mHasStyles)
  440. return;
  441. nsAutoreleasePool localPool;
  442. NSString *family = GetNSStringForString(mName);
  443. // create a font entry for each face
  444. NSArray *fontfaces = [sFontManager
  445. availableMembersOfFontFamily:family]; // returns an array of [psname, style name, weight, traits] elements, goofy api
  446. int faceCount = [fontfaces count];
  447. int faceIndex;
  448. // Bug 420981 - under 10.5, UltraLight and Light have the same weight value
  449. PRBool needToCheckLightFaces =
  450. (gfxPlatformMac::GetPlatform()->OSXVersion() >= MAC_OS_X_VERSION_10_5_HEX);
  451. for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
  452. NSArray *face = [fontfaces objectAtIndex:faceIndex];
  453. NSString *psname = [face objectAtIndex:INDEX_FONT_POSTSCRIPT_NAME];
  454. PRInt32 appKitWeight = [[face objectAtIndex:INDEX_FONT_WEIGHT] unsignedIntValue];
  455. PRUint32 macTraits = [[face objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue];
  456. NSString *facename = [face objectAtIndex:INDEX_FONT_FACE_NAME];
  457. PRBool isStandardFace = PR_FALSE;
  458. if (needToCheckLightFaces && appKitWeight == kAppleExtraLightWeight) {
  459. // if the facename contains UltraLight, set the weight to the ultralight weight value
  460. NSRange range = [facename rangeOfString:@"ultralight" options:NSCaseInsensitiveSearch];
  461. if (range.location != NSNotFound) {
  462. appKitWeight = kAppleUltraLightWeight;
  463. }
  464. }
  465. PRInt32 cssWeight = gfxMacPlatformFontList::AppleWeightToCSSWeight(appKitWeight) * 100;
  466. // make a nsString
  467. nsAutoString postscriptFontName;
  468. GetStringForNSString(psname, postscriptFontName);
  469. if ([facename isEqualToString:@"Regular"] ||
  470. [facename isEqualToString:@"Bold"] ||
  471. [facename isEqualToString:@"Italic"] ||
  472. [facename isEqualToString:@"Oblique"] ||
  473. [facename isEqualToString:@"Bold Italic"] ||
  474. [facename isEqualToString:@"Bold Oblique"])
  475. {
  476. isStandardFace = PR_TRUE;
  477. }
  478. // create a font entry
  479. MacOSFontEntry *fontEntry;
  480. if (gfxMacPlatformFontList::UseATSFontEntry()) {
  481. fontEntry = new ATSFontEntry(postscriptFontName,
  482. cssWeight, this, isStandardFace);
  483. } else {
  484. fontEntry = new CGFontEntry(postscriptFontName,
  485. cssWeight, this, isStandardFace);
  486. }
  487. if (!fontEntry) {
  488. break;
  489. }
  490. // set additional properties based on the traits reported by Cocoa
  491. if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
  492. fontEntry->mStretch = NS_FONT_STRETCH_CONDENSED;
  493. } else if (macTraits & NSExpandedFontMask) {
  494. fontEntry->mStretch = NS_FONT_STRETCH_EXPANDED;
  495. }
  496. // Cocoa fails to set the Italic traits bit for HelveticaLightItalic,
  497. // at least (see bug 611855), so check for style name endings as well
  498. if ((macTraits & NSItalicFontMask) ||
  499. [facename hasSuffix:@"Italic"] ||
  500. [facename hasSuffix:@"Oblique"])
  501. {
  502. fontEntry->mItalic = PR_TRUE;
  503. }
  504. if (macTraits & NSFixedPitchFontMask) {
  505. fontEntry->mFixedPitch = PR_TRUE;
  506. }
  507. #ifdef PR_LOGGING
  508. if (LOG_FONTLIST_ENABLED()) {
  509. LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
  510. " with style: %s weight: %d stretch: %d"
  511. " (apple-weight: %d macTraits: %8.8x)",
  512. NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
  513. NS_ConvertUTF16toUTF8(Name()).get(),
  514. fontEntry->IsItalic() ? "italic" : "normal",
  515. cssWeight, fontEntry->Stretch(),
  516. appKitWeight, macTraits));
  517. }
  518. #endif
  519. // insert into font entry array of family
  520. AddFontEntry(fontEntry);
  521. }
  522. SortAvailableFonts();
  523. SetHasStyles(PR_TRUE);
  524. if (mIsBadUnderlineFamily) {
  525. SetBadUnderlineFonts();
  526. }
  527. }
  528. /* gfxSingleFaceMacFontFamily */
  529. #pragma mark-
  530. class gfxSingleFaceMacFontFamily : public gfxFontFamily
  531. {
  532. public:
  533. gfxSingleFaceMacFontFamily(nsAString& aName) :
  534. gfxFontFamily(aName)
  535. {}
  536. virtual ~gfxSingleFaceMacFontFamily() {}
  537. virtual void LocalizedName(nsAString& aLocalizedName);
  538. virtual void ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList);
  539. };
  540. void
  541. gfxSingleFaceMacFontFamily::LocalizedName(nsAString& aLocalizedName)
  542. {
  543. nsAutoreleasePool localPool;
  544. if (!HasOtherFamilyNames()) {
  545. aLocalizedName = mName;
  546. return;
  547. }
  548. gfxFontEntry *fe = mAvailableFonts[0];
  549. NSFont *font = [NSFont fontWithName:GetNSStringForString(fe->Name())
  550. size:0.0];
  551. if (font) {
  552. NSString *localized = [font displayName];
  553. if (localized) {
  554. GetStringForNSString(localized, aLocalizedName);
  555. return;
  556. }
  557. }
  558. // failed to get localized name, just use the canonical one
  559. aLocalizedName = mName;
  560. }
  561. void
  562. gfxSingleFaceMacFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
  563. {
  564. if (mOtherFamilyNamesInitialized)
  565. return;
  566. gfxFontEntry *fe = mAvailableFonts[0];
  567. if (!fe)
  568. return;
  569. const PRUint32 kNAME = TRUETYPE_TAG('n','a','m','e');
  570. AutoFallibleTArray<PRUint8,8192> buffer;
  571. if (fe->GetFontTable(kNAME, buffer) != NS_OK)
  572. return;
  573. mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
  574. buffer,
  575. PR_TRUE);
  576. mOtherFamilyNamesInitialized = PR_TRUE;
  577. }
  578. /* gfxMacPlatformFontList */
  579. #pragma mark-
  580. gfxMacPlatformFontList::gfxMacPlatformFontList() :
  581. gfxPlatformFontList(PR_FALSE), mATSGeneration(PRUint32(kATSGenerationInitial))
  582. {
  583. ::ATSFontNotificationSubscribe(ATSNotification,
  584. kATSFontNotifyOptionDefault,
  585. (void*)this, nsnull);
  586. // this should always be available (though we won't actually fail if it's missing,
  587. // we'll just end up doing a search and then caching the new result instead)
  588. mReplacementCharFallbackFamily = NS_LITERAL_STRING("Lucida Grande");
  589. // cache this in a static variable so that MacOSFontFamily objects
  590. // don't have to repeatedly look it up
  591. sFontManager = [NSFontManager sharedFontManager];
  592. }
  593. nsresult
  594. gfxMacPlatformFontList::InitFontList()
  595. {
  596. nsAutoreleasePool localPool;
  597. ATSGeneration currentGeneration = ::ATSGetGeneration();
  598. // need to ignore notifications after adding each font
  599. if (mATSGeneration == currentGeneration)
  600. return NS_OK;
  601. Telemetry::AutoTimer<Telemetry::MAC_INITFONTLIST_TOTAL> timer;
  602. mATSGeneration = currentGeneration;
  603. #ifdef PR_LOGGING
  604. LOG_FONTLIST(("(fontlist) updating to generation: %d", mATSGeneration));
  605. #endif
  606. // reset font lists
  607. gfxPlatformFontList::InitFontList();
  608. // iterate over available families
  609. NSEnumerator *families = [[sFontManager availableFontFamilies]
  610. objectEnumerator]; // returns "canonical", non-localized family name
  611. nsAutoString availableFamilyName;
  612. NSString *availableFamily = nil;
  613. while ((availableFamily = [families nextObject])) {
  614. // make a nsString
  615. GetStringForNSString(availableFamily, availableFamilyName);
  616. // create a family entry
  617. gfxFontFamily *familyEntry = new gfxMacFontFamily(availableFamilyName);
  618. if (!familyEntry) break;
  619. // add the family entry to the hash table
  620. ToLowerCase(availableFamilyName);
  621. mFontFamilies.Put(availableFamilyName, familyEntry);
  622. // check the bad underline blacklist
  623. if (mBadUnderlineFamilyNames.Contains(availableFamilyName))
  624. familyEntry->SetBadUnderlineFamily();
  625. }
  626. InitSingleFaceList();
  627. // to avoid full search of font name tables, seed the other names table with localized names from
  628. // some of the prefs fonts which are accessed via their localized names. changes in the pref fonts will only cause
  629. // a font lookup miss earlier. this is a simple optimization, it's not required for correctness
  630. PreloadNamesList();
  631. // start the delayed cmap loader
  632. StartLoader(kDelayBeforeLoadingCmaps, kIntervalBetweenLoadingCmaps);
  633. return NS_OK;
  634. }
  635. void
  636. gfxMacPlatformFontList::InitSingleFaceList()
  637. {
  638. nsAutoTArray<nsString, 10> singleFaceFonts;
  639. gfxFontUtils::GetPrefsFontList("font.single-face-list", singleFaceFonts);
  640. PRUint32 numFonts = singleFaceFonts.Length();
  641. for (PRUint32 i = 0; i < numFonts; i++) {
  642. #ifdef PR_LOGGING
  643. LOG_FONTLIST(("(fontlist-singleface) face name: %s\n",
  644. NS_ConvertUTF16toUTF8(singleFaceFonts[i]).get()));
  645. #endif
  646. gfxFontEntry *fontEntry = LookupLocalFont(nsnull, singleFaceFonts[i]);
  647. if (fontEntry) {
  648. nsAutoString familyName, key;
  649. familyName = singleFaceFonts[i];
  650. GenerateFontListKey(familyName, key);
  651. #ifdef PR_LOGGING
  652. LOG_FONTLIST(("(fontlist-singleface) family name: %s, key: %s\n",
  653. NS_ConvertUTF16toUTF8(familyName).get(),
  654. NS_ConvertUTF16toUTF8(key).get()));
  655. #endif
  656. // add only if doesn't exist already
  657. PRBool found;
  658. gfxFontFamily *familyEntry;
  659. if (!(familyEntry = mFontFamilies.GetWeak(key, &found))) {
  660. familyEntry = new gfxSingleFaceMacFontFamily(familyName);
  661. familyEntry->AddFontEntry(fontEntry);
  662. familyEntry->SetHasStyles(PR_TRUE);
  663. mFontFamilies.Put(key, familyEntry);
  664. fontEntry->mFamily = familyEntry;
  665. #ifdef PR_LOGGING
  666. LOG_FONTLIST(("(fontlist-singleface) added new family\n",
  667. NS_ConvertUTF16toUTF8(familyName).get(),
  668. NS_ConvertUTF16toUTF8(key).get()));
  669. #endif
  670. }
  671. }
  672. }
  673. }
  674. PRBool
  675. gfxMacPlatformFontList::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
  676. {
  677. gfxFontFamily *family = FindFamily(aFontName);
  678. if (family) {
  679. family->LocalizedName(aFamilyName);
  680. return PR_TRUE;
  681. }
  682. // Gecko 1.8 used Quickdraw font api's which produce a slightly different set of "family"
  683. // names. Try to resolve based on these names, in case this is stored in an old profile
  684. // 1.8: "Futura", "Futura Condensed" ==> 1.9: "Futura"
  685. // convert the name to a Pascal-style Str255 to try as Quickdraw name
  686. Str255 qdname;
  687. NS_ConvertUTF16toUTF8 utf8name(aFontName);
  688. qdname[0] = NS_MAX<size_t>(255, strlen(utf8name.get()));
  689. memcpy(&qdname[1], utf8name.get(), qdname[0]);
  690. // look up the Quickdraw name
  691. ATSFontFamilyRef atsFamily = ::ATSFontFamilyFindFromQuickDrawName(qdname);
  692. if (atsFamily == (ATSFontFamilyRef)kInvalidFontFamily) {
  693. return PR_FALSE;
  694. }
  695. // if we found a family, get its ATS name
  696. CFStringRef cfName;
  697. OSStatus status = ::ATSFontFamilyGetName(atsFamily, kATSOptionFlagsDefault, &cfName);
  698. if (status != noErr) {
  699. return PR_FALSE;
  700. }
  701. // then use this to locate the family entry and retrieve its localized name
  702. nsAutoString familyName;
  703. GetStringForNSString((const NSString*)cfName, familyName);
  704. ::CFRelease(cfName);
  705. family = FindFamily(familyName);
  706. if (family) {
  707. family->LocalizedName(aFamilyName);
  708. return PR_TRUE;
  709. }
  710. return PR_FALSE;
  711. }
  712. void
  713. gfxMacPlatformFontList::ATSNotification(ATSFontNotificationInfoRef aInfo,
  714. void* aUserArg)
  715. {
  716. // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
  717. gfxMacPlatformFontList *qfc = (gfxMacPlatformFontList*)aUserArg;
  718. qfc->UpdateFontList();
  719. }
  720. gfxFontEntry*
  721. gfxMacPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle, PRBool& aNeedsBold)
  722. {
  723. nsAutoreleasePool localPool;
  724. NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName];
  725. nsAutoString familyName;
  726. GetStringForNSString(defaultFamily, familyName);
  727. return FindFontForFamily(familyName, aStyle, aNeedsBold);
  728. }
  729. PRInt32
  730. gfxMacPlatformFontList::AppleWeightToCSSWeight(PRInt32 aAppleWeight)
  731. {
  732. if (aAppleWeight < 1)
  733. aAppleWeight = 1;
  734. else if (aAppleWeight > kAppleMaxWeight)
  735. aAppleWeight = kAppleMaxWeight;
  736. return gAppleWeightToCSSWeight[aAppleWeight];
  737. }
  738. gfxFontEntry*
  739. gfxMacPlatformFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
  740. const nsAString& aFontName)
  741. {
  742. nsAutoreleasePool localPool;
  743. NSString *faceName = GetNSStringForString(aFontName);
  744. MacOSFontEntry *newFontEntry;
  745. if (UseATSFontEntry()) {
  746. // first lookup a single face based on postscript name
  747. ATSFontRef fontRef = ::ATSFontFindFromPostScriptName(CFStringRef(faceName),
  748. kATSOptionFlagsDefault);
  749. // if not found, lookup using full font name
  750. if (fontRef == kInvalidFont) {
  751. fontRef = ::ATSFontFindFromName(CFStringRef(faceName),
  752. kATSOptionFlagsDefault);
  753. if (fontRef == kInvalidFont) {
  754. return nsnull;
  755. }
  756. }
  757. if (aProxyEntry) {
  758. PRUint16 w = aProxyEntry->mWeight;
  759. NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
  760. newFontEntry =
  761. new ATSFontEntry(aFontName, fontRef,
  762. w, aProxyEntry->mStretch,
  763. aProxyEntry->mItalic ?
  764. FONT_STYLE_ITALIC : FONT_STYLE_NORMAL,
  765. nsnull, PR_TRUE);
  766. } else {
  767. newFontEntry =
  768. new ATSFontEntry(aFontName, fontRef,
  769. 400, 0, FONT_STYLE_NORMAL, nsnull, PR_FALSE);
  770. }
  771. } else {
  772. // lookup face based on postscript or full name
  773. CGFontRef fontRef = ::CGFontCreateWithFontName(CFStringRef(faceName));
  774. if (!fontRef) {
  775. return nsnull;
  776. }
  777. if (aProxyEntry) {
  778. PRUint16 w = aProxyEntry->mWeight;
  779. NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
  780. newFontEntry =
  781. new CGFontEntry(aFontName, fontRef,
  782. w, aProxyEntry->mStretch,
  783. aProxyEntry->mItalic ?
  784. FONT_STYLE_ITALIC : FONT_STYLE_NORMAL,
  785. PR_TRUE, PR_TRUE);
  786. } else {
  787. newFontEntry =
  788. new CGFontEntry(aFontName, fontRef,
  789. 400, 0, FONT_STYLE_NORMAL,
  790. PR_FALSE, PR_FALSE);
  791. }
  792. ::CFRelease(fontRef);
  793. }
  794. return newFontEntry;
  795. }
  796. gfxFontEntry*
  797. gfxMacPlatformFontList::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
  798. const PRUint8 *aFontData,
  799. PRUint32 aLength)
  800. {
  801. return UseATSFontEntry()
  802. ? MakePlatformFontATS(aProxyEntry, aFontData, aLength)
  803. : MakePlatformFontCG(aProxyEntry, aFontData, aLength);
  804. }
  805. static void ReleaseData(void *info, const void *data, size_t size)
  806. {
  807. NS_Free((void*)data);
  808. }
  809. gfxFontEntry*
  810. gfxMacPlatformFontList::MakePlatformFontCG(const gfxProxyFontEntry *aProxyEntry,
  811. const PRUint8 *aFontData,
  812. PRUint32 aLength)
  813. {
  814. NS_ASSERTION(aFontData, "MakePlatformFont called with null data");
  815. PRUint16 w = aProxyEntry->mWeight;
  816. NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
  817. // create the font entry
  818. nsAutoString uniqueName;
  819. nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
  820. if (NS_FAILED(rv)) {
  821. return nsnull;
  822. }
  823. CGDataProviderRef provider =
  824. ::CGDataProviderCreateWithData(nsnull, aFontData, aLength,
  825. &ReleaseData);
  826. CGFontRef fontRef = ::CGFontCreateWithDataProvider(provider);
  827. ::CGDataProviderRelease(provider);
  828. if (!fontRef) {
  829. return nsnull;
  830. }
  831. nsAutoPtr<CGFontEntry>
  832. newFontEntry(new CGFontEntry(uniqueName, fontRef, w,
  833. aProxyEntry->mStretch,
  834. aProxyEntry->mItalic ?
  835. FONT_STYLE_ITALIC : FONT_STYLE_NORMAL,
  836. PR_TRUE, PR_FALSE));
  837. // if succeeded and font cmap is good, return the new font
  838. if (newFontEntry->mIsValid && NS_SUCCEEDED(newFontEntry->ReadCMAP())) {
  839. return newFontEntry.forget();
  840. }
  841. // if something is funky about this font, delete immediately
  842. #if DEBUG
  843. char warnBuf[1024];
  844. sprintf(warnBuf, "downloaded font not loaded properly, removed face for (%s)",
  845. NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get());
  846. NS_WARNING(warnBuf);
  847. #endif
  848. return nsnull;
  849. }
  850. // grumble, another non-publised Apple API dependency (found in Webkit code)
  851. // activated with this value, font will not be found via system lookup routines
  852. // it can only be used via the created ATSFontRef
  853. // needed to prevent one doc from finding a font used in a separate doc
  854. enum {
  855. kPrivateATSFontContextPrivate = 3
  856. };
  857. class ATSUserFontData : public gfxUserFontData {
  858. public:
  859. ATSUserFontData(ATSFontContainerRef aContainerRef)
  860. : mContainerRef(aContainerRef)
  861. { }
  862. virtual ~ATSUserFontData()
  863. {
  864. // deactivate font
  865. if (mContainerRef) {
  866. ::ATSFontDeactivate(mContainerRef, NULL, kATSOptionFlagsDefault);
  867. }
  868. }
  869. ATSFontContainerRef mContainerRef;
  870. };
  871. gfxFontEntry*
  872. gfxMacPlatformFontList::MakePlatformFontATS(const gfxProxyFontEntry *aProxyEntry,
  873. const PRUint8 *aFontData,
  874. PRUint32 aLength)
  875. {
  876. OSStatus err;
  877. NS_ASSERTION(aFontData, "MakePlatformFont called with null data");
  878. // MakePlatformFont is responsible for deleting the font data with NS_Free
  879. // so we set up a stack object to ensure it is freed even if we take an
  880. // early exit
  881. struct FontDataDeleter {
  882. FontDataDeleter(const PRUint8 *aFontData)
  883. : mFontData(aFontData) { }
  884. ~FontDataDeleter() { NS_Free((void*)mFontData); }
  885. const PRUint8 *mFontData;
  886. };
  887. FontDataDeleter autoDelete(aFontData);
  888. ATSFontRef fontRef;
  889. ATSFontContainerRef containerRef;
  890. // we get occasional failures when multiple fonts are activated in quick succession
  891. // if the ATS font cache is damaged; to work around this, we can retry the activation
  892. const PRUint32 kMaxRetries = 3;
  893. PRUint32 retryCount = 0;
  894. while (retryCount++ < kMaxRetries) {
  895. err = ::ATSFontActivateFromMemory(const_cast<PRUint8*>(aFontData), aLength,
  896. kPrivateATSFontContextPrivate,
  897. kATSFontFormatUnspecified,
  898. NULL,
  899. kATSOptionFlagsDoNotNotify,
  900. &containerRef);
  901. mATSGeneration = ::ATSGetGeneration();
  902. if (err != noErr) {
  903. #if DEBUG
  904. char warnBuf[1024];
  905. sprintf(warnBuf, "downloaded font error, ATSFontActivateFromMemory err: %d for (%s)",
  906. PRInt32(err),
  907. NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get());
  908. NS_WARNING(warnBuf);
  909. #endif
  910. return nsnull;
  911. }
  912. // ignoring containers with multiple fonts, use the first face only for now
  913. err = ::ATSFontFindFromContainer(containerRef, kATSOptionFlagsDefault, 1,
  914. &fontRef, NULL);
  915. if (err != noErr) {
  916. #if DEBUG
  917. char warnBuf[1024];
  918. sprintf(warnBuf, "downloaded font error, ATSFontFindFromContainer err: %d for (%s)",
  919. PRInt32(err),
  920. NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get());
  921. NS_WARNING(warnBuf);
  922. #endif
  923. ::ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
  924. return nsnull;
  925. }
  926. // now lookup the Postscript name; this may fail if the font cache is bad
  927. OSStatus err;
  928. NSString *psname = NULL;
  929. err = ::ATSFontGetPostScriptName(fontRef, kATSOptionFlagsDefault, (CFStringRef*) (&psname));
  930. if (err == noErr) {
  931. [psname release];
  932. } else {
  933. #ifdef DEBUG
  934. char warnBuf[1024];
  935. sprintf(warnBuf, "ATSFontGetPostScriptName err = %d for (%s), retries = %d", (PRInt32)err,
  936. NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get(), retryCount);
  937. NS_WARNING(warnBuf);
  938. #endif
  939. ::ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault);
  940. // retry the activation a couple of times if this fails
  941. // (may be a transient failure due to ATS font cache issues)
  942. continue;
  943. }
  944. PRUint16 w = aProxyEntry->mWeight;
  945. NS_ASSERTION(w >= 100 && w <= 900, "bogus font weight value!");
  946. nsAutoString uniqueName;
  947. nsresult rv = gfxFontUtils::MakeUniqueUserFontName(uniqueName);
  948. if (NS_FAILED(rv)) {
  949. return nsnull;
  950. }
  951. // font entry will own this
  952. ATSUserFontData *userFontData = new ATSUserFontData(containerRef);
  953. ATSFontEntry *newFontEntry =
  954. new ATSFontEntry(uniqueName,
  955. fontRef,
  956. w, aProxyEntry->mStretch,
  957. aProxyEntry->mItalic ?
  958. FONT_STYLE_ITALIC : FONT_STYLE_NORMAL,
  959. userFontData, PR_FALSE);
  960. // if succeeded and font cmap is good, return the new font
  961. if (newFontEntry->mIsValid && NS_SUCCEEDED(newFontEntry->ReadCMAP())) {
  962. return newFontEntry;
  963. }
  964. // if something is funky about this font, delete immediately
  965. #if DEBUG
  966. char warnBuf[1024];
  967. sprintf(warnBuf, "downloaded font not loaded properly, removed face for (%s)",
  968. NS_ConvertUTF16toUTF8(aProxyEntry->mFamily->Name()).get());
  969. NS_WARNING(warnBuf);
  970. #endif
  971. delete newFontEntry;
  972. // We don't retry from here; the ATS font cache issue would have caused failure earlier
  973. // so if we get here, there's something else bad going on within our font data structures.
  974. // Currently, there should be no way to reach here, as fontentry creation cannot fail
  975. // except by memory allocation failure.
  976. NS_WARNING("invalid font entry for a newly activated font");
  977. break;
  978. }
  979. // if we get here, the activation failed (even with possible retries); can't use this font
  980. return nsnull;
  981. }