1/* 2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com> 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#import "config.h" 31#import "FontCache.h" 32 33#import "Font.h" 34#import "SimpleFontData.h" 35#import "FontPlatformData.h" 36#import "WebCoreSystemInterface.h" 37#import "WebFontCache.h" 38#import <AppKit/AppKit.h> 39#import <wtf/StdLibExtras.h> 40 41#ifdef BUILDING_ON_TIGER 42typedef int NSInteger; 43#endif 44 45namespace WebCore { 46 47#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 48static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef name, const void *, CFDictionaryRef) 49{ 50 ASSERT_UNUSED(observer, observer == fontCache()); 51 ASSERT_UNUSED(name, CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification)); 52 fontCache()->invalidate(); 53} 54#else 55static void fontCacheATSNotificationCallback(ATSFontNotificationInfoRef, void*) 56{ 57 fontCache()->invalidate(); 58} 59#endif 60 61void FontCache::platformInit() 62{ 63 wkSetUpFontCache(); 64#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 65 CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, fontCacheRegisteredFontsChangedNotificationCallback, kCTFontManagerRegisteredFontsChangedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately); 66#else 67 // kCTFontManagerRegisteredFontsChangedNotification does not exist on Leopard and earlier. 68 // FIXME: Passing kATSFontNotifyOptionReceiveWhileSuspended may be an overkill and does not seem to work anyway. 69 ATSFontNotificationSubscribe(fontCacheATSNotificationCallback, kATSFontNotifyOptionReceiveWhileSuspended, 0, 0); 70#endif 71} 72 73static int toAppKitFontWeight(FontWeight fontWeight) 74{ 75 static int appKitFontWeights[] = { 76 2, // FontWeight100 77 3, // FontWeight200 78 4, // FontWeight300 79 5, // FontWeight400 80 6, // FontWeight500 81 8, // FontWeight600 82 9, // FontWeight700 83 10, // FontWeight800 84 12, // FontWeight900 85 }; 86 return appKitFontWeights[fontWeight]; 87} 88 89static inline bool isAppKitFontWeightBold(NSInteger appKitFontWeight) 90{ 91 return appKitFontWeight >= 7; 92} 93 94const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) 95{ 96 const FontPlatformData& platformData = font.fontDataAt(0)->fontDataForCharacter(characters[0])->platformData(); 97 NSFont *nsFont = platformData.font(); 98 99 NSString *string = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(characters) length:length freeWhenDone:NO]; 100 NSFont *substituteFont = wkGetFontInLanguageForRange(nsFont, string, NSMakeRange(0, length)); 101 [string release]; 102 103 if (!substituteFont && length == 1) 104 substituteFont = wkGetFontInLanguageForCharacter(nsFont, characters[0]); 105 if (!substituteFont) 106 return 0; 107 108 // Use the family name from the AppKit-supplied substitute font, requesting the 109 // traits, weight, and size we want. One way this does better than the original 110 // AppKit request is that it takes synthetic bold and oblique into account. 111 // But it does create the possibility that we could end up with a font that 112 // doesn't actually cover the characters we need. 113 114 NSFontManager *fontManager = [NSFontManager sharedFontManager]; 115 116 NSFontTraitMask traits; 117 NSInteger weight; 118 CGFloat size; 119 120 if (nsFont) { 121 traits = [fontManager traitsOfFont:nsFont]; 122 if (platformData.m_syntheticBold) 123 traits |= NSBoldFontMask; 124 if (platformData.m_syntheticOblique) 125 traits |= NSFontItalicTrait; 126 weight = [fontManager weightOfFont:nsFont]; 127 size = [nsFont pointSize]; 128 } else { 129 // For custom fonts nsFont is nil. 130 traits = font.italic() ? NSFontItalicTrait : 0; 131 weight = toAppKitFontWeight(font.weight()); 132 size = font.pixelSize(); 133 } 134 135 if (NSFont *bestVariation = [fontManager fontWithFamily:[substituteFont familyName] traits:traits weight:weight size:size]) 136 substituteFont = bestVariation; 137 138 substituteFont = font.fontDescription().usePrinterFont() ? [substituteFont printerFont] : [substituteFont screenFont]; 139 140 NSFontTraitMask substituteFontTraits = [fontManager traitsOfFont:substituteFont]; 141 NSInteger substituteFontWeight = [fontManager weightOfFont:substituteFont]; 142 143 FontPlatformData alternateFont(substituteFont, platformData.size(), 144 !font.isPlatformFont() && isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(substituteFontWeight), 145 !font.isPlatformFont() && (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait), 146 platformData.m_orientation); 147 return getCachedFontData(&alternateFont); 148} 149 150SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font) 151{ 152 // Attempt to find an appropriate font using a match based on 153 // the presence of keywords in the the requested names. For example, we'll 154 // match any name that contains "Arabic" to Geeza Pro. 155 SimpleFontData* simpleFontData = 0; 156 const FontFamily* currFamily = &font.fontDescription().family(); 157 while (currFamily && !simpleFontData) { 158 if (currFamily->family().length()) { 159 static String* matchWords[3] = { new String("Arabic"), new String("Pashto"), new String("Urdu") }; 160 DEFINE_STATIC_LOCAL(AtomicString, geezaStr, ("Geeza Pro")); 161 for (int j = 0; j < 3 && !simpleFontData; ++j) 162 if (currFamily->family().contains(*matchWords[j], false)) 163 simpleFontData = getCachedFontData(font.fontDescription(), geezaStr); 164 } 165 currFamily = currFamily->next(); 166 } 167 168 return simpleFontData; 169} 170 171SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription) 172{ 173 DEFINE_STATIC_LOCAL(AtomicString, timesStr, ("Times")); 174 175 // FIXME: Would be even better to somehow get the user's default font here. For now we'll pick 176 // the default that the user would get without changing any prefs. 177 SimpleFontData* simpleFontData = getCachedFontData(fontDescription, timesStr); 178 if (simpleFontData) 179 return simpleFontData; 180 181 // The Times fallback will almost always work, but in the highly unusual case where 182 // the user doesn't have it, we fall back on Lucida Grande because that's 183 // guaranteed to be there, according to Nathan Taylor. This is good enough 184 // to avoid a crash at least. 185 DEFINE_STATIC_LOCAL(AtomicString, lucidaGrandeStr, ("Lucida Grande")); 186 return getCachedFontData(fontDescription, lucidaGrandeStr); 187} 188 189void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) 190{ 191 [WebFontCache getTraits:traitsMasks inFamily:familyName]; 192} 193 194FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) 195{ 196 NSFontTraitMask traits = fontDescription.italic() ? NSFontItalicTrait : 0; 197 NSInteger weight = toAppKitFontWeight(fontDescription.weight()); 198 float size = fontDescription.computedPixelSize(); 199 200 NSFont *nsFont = [WebFontCache fontWithFamily:family traits:traits weight:weight size:size]; 201 if (!nsFont) 202 return 0; 203 204 NSFontManager *fontManager = [NSFontManager sharedFontManager]; 205 NSFontTraitMask actualTraits = 0; 206 if (fontDescription.italic()) 207 actualTraits = [fontManager traitsOfFont:nsFont]; 208 NSInteger actualWeight = [fontManager weightOfFont:nsFont]; 209 210 NSFont *platformFont = fontDescription.usePrinterFont() ? [nsFont printerFont] : [nsFont screenFont]; 211 bool syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight); 212 bool syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait); 213 214 return new FontPlatformData(platformFont, size, syntheticBold, syntheticOblique, fontDescription.orientation(), fontDescription.textOrientation(), fontDescription.widthVariant()); 215} 216 217} // namespace WebCore 218