blob: 0747dd73a5e6d5a1b3ffe5158bf0e38da9392a8c [file] [log] [blame]
/*
* Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
* Copyright (C) 2007 Nicholas Shanks <webkit@nickshanks.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "FontCache.h"
#import "Font.h"
#import "SimpleFontData.h"
#import "FontPlatformData.h"
#import "WebCoreSystemInterface.h"
#import "WebFontCache.h"
#import <AppKit/AppKit.h>
#import <wtf/StdLibExtras.h>
#ifdef BUILDING_ON_TIGER
typedef int NSInteger;
#endif
namespace WebCore {
#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
static void fontCacheRegisteredFontsChangedNotificationCallback(CFNotificationCenterRef, void* observer, CFStringRef name, const void *, CFDictionaryRef)
{
ASSERT_UNUSED(observer, observer == fontCache());
ASSERT_UNUSED(name, CFEqual(name, kCTFontManagerRegisteredFontsChangedNotification));
fontCache()->invalidate();
}
#else
static void fontCacheATSNotificationCallback(ATSFontNotificationInfoRef, void*)
{
fontCache()->invalidate();
}
#endif
void FontCache::platformInit()
{
wkSetUpFontCache();
#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, fontCacheRegisteredFontsChangedNotificationCallback, kCTFontManagerRegisteredFontsChangedNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
#else
// kCTFontManagerRegisteredFontsChangedNotification does not exist on Leopard and earlier.
// FIXME: Passing kATSFontNotifyOptionReceiveWhileSuspended may be an overkill and does not seem to work anyway.
ATSFontNotificationSubscribe(fontCacheATSNotificationCallback, kATSFontNotifyOptionReceiveWhileSuspended, 0, 0);
#endif
}
static int toAppKitFontWeight(FontWeight fontWeight)
{
static int appKitFontWeights[] = {
2, // FontWeight100
3, // FontWeight200
4, // FontWeight300
5, // FontWeight400
6, // FontWeight500
8, // FontWeight600
9, // FontWeight700
10, // FontWeight800
12, // FontWeight900
};
return appKitFontWeights[fontWeight];
}
static inline bool isAppKitFontWeightBold(NSInteger appKitFontWeight)
{
return appKitFontWeight >= 7;
}
const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length)
{
const FontPlatformData& platformData = font.fontDataAt(0)->fontDataForCharacter(characters[0])->platformData();
NSFont *nsFont = platformData.font();
NSString *string = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(characters) length:length freeWhenDone:NO];
NSFont *substituteFont = wkGetFontInLanguageForRange(nsFont, string, NSMakeRange(0, length));
[string release];
if (!substituteFont && length == 1)
substituteFont = wkGetFontInLanguageForCharacter(nsFont, characters[0]);
if (!substituteFont)
return 0;
// Use the family name from the AppKit-supplied substitute font, requesting the
// traits, weight, and size we want. One way this does better than the original
// AppKit request is that it takes synthetic bold and oblique into account.
// But it does create the possibility that we could end up with a font that
// doesn't actually cover the characters we need.
NSFontManager *fontManager = [NSFontManager sharedFontManager];
NSFontTraitMask traits;
NSInteger weight;
CGFloat size;
if (nsFont) {
traits = [fontManager traitsOfFont:nsFont];
if (platformData.m_syntheticBold)
traits |= NSBoldFontMask;
if (platformData.m_syntheticOblique)
traits |= NSFontItalicTrait;
weight = [fontManager weightOfFont:nsFont];
size = [nsFont pointSize];
} else {
// For custom fonts nsFont is nil.
traits = font.italic() ? NSFontItalicTrait : 0;
weight = toAppKitFontWeight(font.weight());
size = font.pixelSize();
}
if (NSFont *bestVariation = [fontManager fontWithFamily:[substituteFont familyName] traits:traits weight:weight size:size])
substituteFont = bestVariation;
substituteFont = font.fontDescription().usePrinterFont() ? [substituteFont printerFont] : [substituteFont screenFont];
NSFontTraitMask substituteFontTraits = [fontManager traitsOfFont:substituteFont];
NSInteger substituteFontWeight = [fontManager weightOfFont:substituteFont];
FontPlatformData alternateFont(substituteFont,
!font.isPlatformFont() && isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(substituteFontWeight),
!font.isPlatformFont() && (traits & NSFontItalicTrait) && !(substituteFontTraits & NSFontItalicTrait));
return getCachedFontData(&alternateFont);
}
SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font)
{
// Attempt to find an appropriate font using a match based on
// the presence of keywords in the the requested names. For example, we'll
// match any name that contains "Arabic" to Geeza Pro.
SimpleFontData* simpleFontData = 0;
const FontFamily* currFamily = &font.fontDescription().family();
while (currFamily && !simpleFontData) {
if (currFamily->family().length()) {
static String* matchWords[3] = { new String("Arabic"), new String("Pashto"), new String("Urdu") };
DEFINE_STATIC_LOCAL(AtomicString, geezaStr, ("Geeza Pro"));
for (int j = 0; j < 3 && !simpleFontData; ++j)
if (currFamily->family().contains(*matchWords[j], false))
simpleFontData = getCachedFontData(font.fontDescription(), geezaStr);
}
currFamily = currFamily->next();
}
return simpleFontData;
}
SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription)
{
DEFINE_STATIC_LOCAL(AtomicString, timesStr, ("Times"));
// FIXME: Would be even better to somehow get the user's default font here. For now we'll pick
// the default that the user would get without changing any prefs.
SimpleFontData* simpleFontData = getCachedFontData(fontDescription, timesStr);
if (simpleFontData)
return simpleFontData;
// The Times fallback will almost always work, but in the highly unusual case where
// the user doesn't have it, we fall back on Lucida Grande because that's
// guaranteed to be there, according to Nathan Taylor. This is good enough
// to avoid a crash at least.
DEFINE_STATIC_LOCAL(AtomicString, lucidaGrandeStr, ("Lucida Grande"));
return getCachedFontData(fontDescription, lucidaGrandeStr);
}
void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks)
{
[WebFontCache getTraits:traitsMasks inFamily:familyName];
}
FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
{
NSFontTraitMask traits = fontDescription.italic() ? NSFontItalicTrait : 0;
NSInteger weight = toAppKitFontWeight(fontDescription.weight());
float size = fontDescription.computedPixelSize();
NSFont *nsFont = [WebFontCache fontWithFamily:family traits:traits weight:weight size:size];
if (!nsFont)
return 0;
NSFontManager *fontManager = [NSFontManager sharedFontManager];
NSFontTraitMask actualTraits = 0;
if (fontDescription.italic())
actualTraits = [fontManager traitsOfFont:nsFont];
NSInteger actualWeight = [fontManager weightOfFont:nsFont];
NSFont *platformFont = fontDescription.usePrinterFont() ? [nsFont printerFont] : [nsFont screenFont];
bool syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight);
bool syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait);
return new FontPlatformData(platformFont, syntheticBold, syntheticOblique);
}
} // namespace WebCore