Home | History | Annotate | Download | only in mac
      1 /*
      2  * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
      3  * Copyright (C) 2007 Nicholas Shanks <webkit (at) 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 "core/platform/graphics/FontCache.h"
     32 
     33 #import <AppKit/AppKit.h>
     34 #import "core/platform/graphics/Font.h"
     35 #import "core/platform/graphics/FontPlatformData.h"
     36 #import "core/platform/graphics/SimpleFontData.h"
     37 #import "core/platform/mac/WebFontCache.h"
     38 #import <wtf/MainThread.h>
     39 #import <wtf/StdLibExtras.h>
     40 
     41 // Forward declare Mac SPIs.
     42 // Request for public API: rdar://13803570
     43 @interface NSFont (WebKitSPI)
     44 + (NSFont*)findFontLike:(NSFont*)font forString:(NSString*)string withRange:(NSRange)range inLanguage:(id)useNil;
     45 + (NSFont*)findFontLike:(NSFont*)font forCharacter:(UniChar)uc inLanguage:(id)useNil;
     46 @end
     47 
     48 namespace WebCore {
     49 
     50 // The "void*" parameter makes the function match the prototype for callbacks from callOnMainThread.
     51 static void invalidateFontCache(void*)
     52 {
     53     if (!isMainThread()) {
     54         callOnMainThread(&invalidateFontCache, 0);
     55         return;
     56     }
     57     fontCache()->invalidate();
     58 }
     59 
     60 static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef name, const void *, CFDictionaryRef)
     61 {
     62     ASSERT_UNUSED(observer, observer == fontCache());
     63     ASSERT_UNUSED(name, CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification));
     64     invalidateFontCache(0);
     65 }
     66 
     67 void FontCache::platformInit()
     68 {
     69     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, fontCacheRegisteredFontsChangedNotificationCallback, kCTFontManagerRegisteredFontsChangedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
     70 }
     71 
     72 static int toAppKitFontWeight(FontWeight fontWeight)
     73 {
     74     static int appKitFontWeights[] = {
     75         2,  // FontWeight100
     76         3,  // FontWeight200
     77         4,  // FontWeight300
     78         5,  // FontWeight400
     79         6,  // FontWeight500
     80         8,  // FontWeight600
     81         9,  // FontWeight700
     82         10, // FontWeight800
     83         12, // FontWeight900
     84     };
     85     return appKitFontWeights[fontWeight];
     86 }
     87 
     88 static inline bool isAppKitFontWeightBold(NSInteger appKitFontWeight)
     89 {
     90     return appKitFontWeight >= 7;
     91 }
     92 
     93 PassRefPtr<SimpleFontData> FontCache::getFontDataForCharacter(const Font& font, UChar32 character)
     94 {
     95     // FIXME: We should fix getFallbackFamily to take a UChar32
     96     // and remove this split-to-UChar16 code.
     97     UChar codeUnits[2];
     98     int codeUnitsLength;
     99     if (character <= 0xFFFF) {
    100         codeUnits[0] = character;
    101         codeUnitsLength = 1;
    102     } else {
    103         codeUnits[0] = U16_LEAD(character);
    104         codeUnits[1] = U16_TRAIL(character);
    105         codeUnitsLength = 2;
    106     }
    107 
    108     const FontPlatformData& platformData = font.fontDataAt(0)->fontDataForCharacter(character)->platformData();
    109     NSFont *nsFont = platformData.font();
    110 
    111     NSString *string = [[NSString alloc] initWithCharactersNoCopy:codeUnits length:codeUnitsLength freeWhenDone:NO];
    112     NSFont *substituteFont = [NSFont findFontLike:nsFont forString:string withRange:NSMakeRange(0, codeUnitsLength) inLanguage:nil];
    113     [string release];
    114 
    115     // FIXME: Remove this SPI usage: http://crbug.com/255122
    116     if (!substituteFont && codeUnitsLength == 1)
    117         substituteFont = [NSFont findFontLike:nsFont forCharacter:codeUnits[0] inLanguage:nil];
    118     if (!substituteFont)
    119         return 0;
    120 
    121     // Chromium can't render AppleColorEmoji.
    122     if ([[substituteFont familyName] isEqual:@"Apple Color Emoji"])
    123         return 0;
    124 
    125     // Use the family name from the AppKit-supplied substitute font, requesting the
    126     // traits, weight, and size we want. One way this does better than the original
    127     // AppKit request is that it takes synthetic bold and oblique into account.
    128     // But it does create the possibility that we could end up with a font that
    129     // doesn't actually cover the characters we need.
    130 
    131     NSFontManager *fontManager = [NSFontManager sharedFontManager];
    132 
    133     NSFontTraitMask traits;
    134     NSInteger weight;
    135     CGFloat size;
    136 
    137     if (nsFont) {
    138         traits = [fontManager traitsOfFont:nsFont];
    139         if (platformData.m_syntheticBold)
    140             traits |= NSBoldFontMask;
    141         if (platformData.m_syntheticOblique)
    142             traits |= NSFontItalicTrait;
    143         weight = [fontManager weightOfFont:nsFont];
    144         size = [nsFont pointSize];
    145     } else {
    146         // For custom fonts nsFont is nil.
    147         traits = font.italic() ? NSFontItalicTrait : 0;
    148         weight = toAppKitFontWeight(font.weight());
    149         size = font.pixelSize();
    150     }
    151 
    152     NSFontTraitMask substituteFontTraits = [fontManager traitsOfFont:substituteFont];
    153     NSInteger substituteFontWeight = [fontManager weightOfFont:substituteFont];
    154 
    155     if (traits != substituteFontTraits || weight != substituteFontWeight || !nsFont) {
    156         if (NSFont *bestVariation = [fontManager fontWithFamily:[substituteFont familyName] traits:traits weight:weight size:size]) {
    157             if (!nsFont || (([fontManager traitsOfFont:bestVariation] != substituteFontTraits || [fontManager weightOfFont:bestVariation] != substituteFontWeight)
    158                 && [[bestVariation coveredCharacterSet] longCharacterIsMember:character]))
    159                 substituteFont = bestVariation;
    160         }
    161     }
    162 
    163     substituteFont = font.fontDescription().usePrinterFont() ? [substituteFont printerFont] : [substituteFont screenFont];
    164 
    165     substituteFontTraits = [fontManager traitsOfFont:substituteFont];
    166     substituteFontWeight = [fontManager weightOfFont:substituteFont];
    167 
    168     FontPlatformData alternateFont(substituteFont, platformData.size(), platformData.isPrinterFont(),
    169         !font.isPlatformFont() && isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(substituteFontWeight),
    170         !font.isPlatformFont() && (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait),
    171         platformData.m_orientation);
    172 
    173     return getFontResourceData(&alternateFont, DoNotRetain);
    174 }
    175 
    176 PassRefPtr<SimpleFontData> FontCache::getSimilarFontPlatformData(const Font& font)
    177 {
    178     // Attempt to find an appropriate font using a match based on
    179     // the presence of keywords in the the requested names.  For example, we'll
    180     // match any name that contains "Arabic" to Geeza Pro.
    181     RefPtr<SimpleFontData> simpleFontData;
    182     const FontFamily* currFamily = &font.fontDescription().family();
    183     while (currFamily && !simpleFontData) {
    184         if (currFamily->family().length()) {
    185             static String* matchWords[3] = { new String("Arabic"), new String("Pashto"), new String("Urdu") };
    186             DEFINE_STATIC_LOCAL(AtomicString, geezaStr, ("Geeza Pro", AtomicString::ConstructFromLiteral));
    187             for (int j = 0; j < 3 && !simpleFontData; ++j)
    188                 if (currFamily->family().contains(*matchWords[j], false))
    189                     simpleFontData = getFontResourceData(font.fontDescription(), geezaStr);
    190         }
    191         currFamily = currFamily->next();
    192     }
    193 
    194     return simpleFontData.release();
    195 }
    196 
    197 PassRefPtr<SimpleFontData> FontCache::getLastResortFallbackFont(const FontDescription& fontDescription, ShouldRetain shouldRetain)
    198 {
    199     DEFINE_STATIC_LOCAL(AtomicString, timesStr, ("Times", AtomicString::ConstructFromLiteral));
    200 
    201     // FIXME: Would be even better to somehow get the user's default font here.  For now we'll pick
    202     // the default that the user would get without changing any prefs.
    203     RefPtr<SimpleFontData> simpleFontData = getFontResourceData(fontDescription, timesStr, false, shouldRetain);
    204     if (simpleFontData)
    205         return simpleFontData.release();
    206 
    207     // The Times fallback will almost always work, but in the highly unusual case where
    208     // the user doesn't have it, we fall back on Lucida Grande because that's
    209     // guaranteed to be there, according to Nathan Taylor. This is good enough
    210     // to avoid a crash at least.
    211     DEFINE_STATIC_LOCAL(AtomicString, lucidaGrandeStr, ("Lucida Grande", AtomicString::ConstructFromLiteral));
    212     return getFontResourceData(fontDescription, lucidaGrandeStr, false, shouldRetain);
    213 }
    214 
    215 void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks)
    216 {
    217     [WebFontCache getTraits:traitsMasks inFamily:familyName];
    218 }
    219 
    220 FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
    221 {
    222     NSFontTraitMask traits = fontDescription.italic() ? NSFontItalicTrait : 0;
    223     NSInteger weight = toAppKitFontWeight(fontDescription.weight());
    224     float size = fontDescription.computedPixelSize();
    225 
    226     NSFont *nsFont = [WebFontCache fontWithFamily:family traits:traits weight:weight size:size];
    227     if (!nsFont)
    228         return 0;
    229 
    230     NSFontManager *fontManager = [NSFontManager sharedFontManager];
    231     NSFontTraitMask actualTraits = 0;
    232     if (fontDescription.italic())
    233         actualTraits = [fontManager traitsOfFont:nsFont];
    234     NSInteger actualWeight = [fontManager weightOfFont:nsFont];
    235 
    236     NSFont *platformFont = fontDescription.usePrinterFont() ? [nsFont printerFont] : [nsFont screenFont];
    237     bool syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight);
    238     bool syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait);
    239 
    240     // FontPlatformData::font() can be null for the case of Chromium out-of-process font loading.
    241     // In that case, we don't want to use the platformData.
    242     OwnPtr<FontPlatformData> platformData = adoptPtr(new FontPlatformData(platformFont, size, fontDescription.usePrinterFont(), syntheticBold, syntheticOblique, fontDescription.orientation(), fontDescription.widthVariant()));
    243     if (!platformData->font())
    244         return 0;
    245     return platformData.leakPtr();
    246 }
    247 
    248 } // namespace WebCore
    249