FontCacheMac.mm   [plain text]


/*
 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
 *
 * 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 "CharacterNames.h"

#ifdef BUILDING_ON_TIGER
typedef int NSInteger;
#endif

namespace WebCore {


#define MINIMUM_GLYPH_CACHE_SIZE 1536 * 1024

void FontCache::platformInit()
{
	size_t s = 1024*1024;

    wkSetUpFontCache(s);
}

const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length)
{
    //printf("Purple looks for font for unicode char %u\n", *characters);
    // Unlike OS X, our fallback font on Purple is Arial Unicode, which doesn't have some apple-specific glyphs like F8FF.
    // Fall back to the Apple Fallback font in this case.
    if (length > 0 && requiresCustomFallbackFont(*characters))
        return getCachedFontData(getCustomFallbackFont(*characters, font));

    UChar32 c = *characters;
    if (length > 1 && U16_IS_LEAD(c) && U16_IS_TRAIL(characters[1]))
        c = U16_GET_SUPPLEMENTARY(c, characters[1]);

    bool useCJFont = false;
    bool useKoreanFont = false;
    bool useCyrillicFont = false;
    if (length > 0) {
        do {
            // This isn't a loop but a way to efficiently check for ranges of characters.

            // The following ranges are Korean Hangul and should be rendered by Apple Gothic
            // U+1100 - U+11FF
            // U+3130 - U+318F
            // U+AC00 - U+D7A3

            // These are Cyrillic and should be rendered by Helvetica Neue
            // U+0400 - U+052F
            
            if (c < 0x400)
                break;
            if (c <= 0x52F)
                useCyrillicFont = true;
            if (c < 0x1100)
                break;
            if (c <= 0x11FF) {
                useKoreanFont = true;
                break;
            }
            if (c < 0x2E80)
                break;
            if (c < 0x3130) {
                useCJFont = true;
                break;
            }
            if (c <= 0x318F) {
                useKoreanFont = true;
                break;
            }
            if (c < 0xAC00) {
                useCJFont = true;
                break;
            }
            if (c <= 0xD7A3) {
                useKoreanFont = true;
                break;
            }
            if ( c <= 0xDFFF) {
                useCJFont = true;
                break;
            }
            if (c <= 0xF8FF)
                break;
            if (c < 0xFFF0) {
                useCJFont = true;
                break;
            }
            if (c >=0x20000 && c <= 0x2FFFF)
                useCJFont = true;
        } while (0);
    }
        
	FontPlatformData* platformFont = NULL;
    
	if (useCJFont) {
		// by default, Chinese font is preferred,  fall back on Japanese
		
		enum {
			kCJKFontUseHiragino	= 0,
			kCJKFontUseSTHeitiSC,
			kCJKFontUseSTHeitiTC,
			kCJKFontUseSTHeitiJ,
			kCJKFontUseSTHeitiK,
			kCJKFontsUseHKGPW3UI
		};
		static AtomicString cjkPlain[] = { 	
            AtomicString("HiraKakuProN-W3"),
            AtomicString("STHeitiSC-Light"),
            AtomicString("STHeitiTC-Light"),
            AtomicString("STHeitiJ-Light"),
            AtomicString("STHeitiK-Light"),
            AtomicString("HKGPW3UI")};
        
		static AtomicString cjkBold[] = { 	
            AtomicString("HiraKakuProN-W6"),
            AtomicString("STHeitiSC-Medium"),
            AtomicString("STHeitiTC-Medium"),
            AtomicString("STHeitiJ-Medium"),
            AtomicString("STHeitiK-Medium"),
            AtomicString("HKGPW3UI")};
		
		//default below is for Simplified Chinese user: zh-Hans - note that Hiragino is the
		//the secondary font as we want its for Hiragana and Katakana. The other CJK fonts
		//do not, and should not, contain Hiragana or Katakana glyphs.
		static int	preferredCJKFont=kCJKFontUseSTHeitiSC;
		static int	secondaryCJKFont=kCJKFontsUseHKGPW3UI;
		
		static int CJKFontInitialized = 0;
		if ( CJKFontInitialized == 0 ) {
			CJKFontInitialized = 1;
			// Testing: languageName = (CFStringRef)@"ja";
			NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
			NSArray *languages = [defaults stringArrayForKey:@"AppleLanguages"];
			
            if( languages ) {
                CFStringRef languageName = NULL;
                for( NSString *language in languages) {
                    languageName = CFLocaleCreateCanonicalLanguageIdentifierFromString(NULL, (CFStringRef)language);
                    if ( CFEqual(languageName, CFSTR("zh-Hans")) )
                        break;                                  //Simplified Chinese - default settings
                    else if ( CFEqual(languageName, CFSTR("ja")) ) {	
                        preferredCJKFont=kCJKFontUseHiragino;	//japanese - prefer Hiragino and STHeiti Japanse Variant
                        secondaryCJKFont=kCJKFontUseSTHeitiJ;
                        break;
                    } else if ( CFEqual(languageName, CFSTR("ko")) ) {	
                        preferredCJKFont=kCJKFontUseSTHeitiK;	//korean - prefer STHeiti Korean Variant 
                        break;
                    } else if ( CFEqual(languageName, CFSTR("zh-Hant")) ) {
                        preferredCJKFont=kCJKFontUseSTHeitiTC;	//Traditional Chinese - prefer STHeiti Traditional Variant
                        break;
                    }
                    CFRelease(languageName);
                    languageName = NULL;
                }
                if( languageName )
                    CFRelease(languageName);
            }
		}
        
		platformFont = getCachedFontPlatformData(font.fontDescription(), font.fontDescription().bold() ? cjkBold[preferredCJKFont] : cjkPlain[preferredCJKFont]);
		
		CGGlyph glyphs[2];
		// CGFontGetGlyphsForUnichars takes UTF-16 buffer. Should only be 1 codepoint but since we may pass in two UTF-16 characters,
		// make room for 2 glyphs just to be safe.
		GSFontGetGlyphsForUnichars(platformFont->font(), characters, glyphs, length);
		
		if ( glyphs[0] == 0 )
			platformFont = getCachedFontPlatformData(font.fontDescription(), font.fontDescription().bold() ? cjkBold[secondaryCJKFont] : cjkPlain[secondaryCJKFont]);
    } else if (useKoreanFont) {
        static AtomicString koreanFont("AppleGothic");
        platformFont = getCachedFontPlatformData(font.fontDescription(), koreanFont);    
    } else if (useCyrillicFont) {
        static AtomicString cyrillicPlain("HelveticaNeue");
        static AtomicString cyrillicBold("HelveticaNeue-Bold");
        platformFont = getCachedFontPlatformData(font.fontDescription(), font.fontDescription().bold() ? cyrillicBold : cyrillicPlain);
    }

	if ( platformFont != NULL )
		return getCachedFontData(platformFont);

    return getCachedFontData(getLastResortFallbackFont(font.fontDescription()));
}

FontPlatformData* 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.
    FontPlatformData* platformData = 0;
    const FontFamily* currFamily = &font.fontDescription().family();
    while (currFamily && !platformData) {
        if (currFamily->family().length()) {
            static String matchWords[3] = { String("Arabic"), String("Pashto"), String("Urdu") };
            static AtomicString geezaStr("Geeza Pro");
            for (int j = 0; j < 3 && !platformData; ++j)
                if (currFamily->family().contains(matchWords[j], false))
                    platformData = getCachedFontPlatformData(font.fontDescription(), geezaStr);
        }
        currFamily = currFamily->next();
    }

    return platformData;
}

FontPlatformData* FontCache::getLastResortFallbackFont(const FontDescription& fontDescription)
{
    static AtomicString arialUnicodeMSStr("ArialUnicodeMS");
    FontPlatformData* platformFont = getCachedFontPlatformData(fontDescription, arialUnicodeMSStr);    
    return platformFont;
}

    
FontPlatformData* FontCache::getCustomFallbackFont(const UInt32 c, const Font& font)
{
    assert(requiresCustomFallbackFont(c));
    
    if (c == AppleLogo) {
        static AtomicString helveticaStr("Helvetica");
        FontPlatformData* platformFont = getCachedFontPlatformData(font.fontDescription(), helveticaStr);
        return platformFont;
    } else if (c == BigDot) {
        static AtomicString lockClockStr("LockClock-Light");
        FontPlatformData* platformFont = getCachedFontPlatformData(font.fontDescription(), lockClockStr);
        return platformFont;
    }
    return NULL;
}
    
    
inline bool FontCache::requiresCustomFallbackFont(const UInt32 c)
{
    return (c == AppleLogo ||
            c == BigDot);
}

    

bool FontCache::fontExists(const FontDescription& fontDescription, const AtomicString& family)
{
    GSFontTraitMask traits = 0;
    if (fontDescription.italic())
        traits |= GSItalicFontMask;
    if (fontDescription.bold())
        traits |= GSBoldFontMask;
    float size = fontDescription.computedPixelSize();
    
    GSFontRef gsFont= [WebFontCache createFontWithFamily:family traits:traits size:size];
    return gsFont != 0;
}

FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
{
    // Special case for "Courier" font. We have only an oblique variant on Purple,
    // so disallow its use here. We'll fall back on "Courier New". 5116477.
    static AtomicString courier("Courier");
    if (equalIgnoringCase(family, courier))
        return 0;    
    
    GSFontTraitMask traits = 0;
    if (fontDescription.italic())
        traits |= GSItalicFontMask;
    if (fontDescription.bold())
        traits |= GSBoldFontMask;
    float size = fontDescription.computedPixelSize();
	
	GSFontRef gsFont= [WebFontCache createFontWithFamily: family traits: traits size: size];
	if (!gsFont)
		return 0;
	
    GSFontTraitMask actualTraits = 0;
    if (fontDescription.bold() || fontDescription.italic())
        actualTraits = GSFontGetTraits(gsFont);
    
    FontPlatformData* result = new FontPlatformData;
    
    // Use the correct font for print vs. screen.
    result->setFont(gsFont);
    result->m_syntheticBold = (traits & GSBoldFontMask) && !(actualTraits & GSBoldFontMask);
    result->m_syntheticOblique = (traits & GSItalicFontMask) && !(actualTraits & GSItalicFontMask);	
    return result;
}

} // namespace WebCore