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