FontUtilsChromiumWin.cpp [plain text]
#include "config.h"
#include "FontUtilsChromiumWin.h"
#include <limits>
#include "PlatformString.h"
#include "UniscribeHelper.h"
#include <unicode/locid.h>
#include <unicode/uchar.h>
#include <wtf/HashMap.h>
#include <wtf/text/StringHash.h>
namespace WebCore {
namespace {
bool isFontPresent(const UChar* fontName)
{
HFONT hfont = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
fontName);
if (!hfont)
return false;
HDC dc = GetDC(0);
HGDIOBJ oldFont = static_cast<HFONT>(SelectObject(dc, hfont));
WCHAR actualFontName[LF_FACESIZE];
GetTextFace(dc, LF_FACESIZE, actualFontName);
actualFontName[LF_FACESIZE - 1] = 0;
SelectObject(dc, oldFont);
DeleteObject(hfont);
ReleaseDC(0, dc);
return !wcscmp(fontName, actualFontName);
}
typedef const UChar* ScriptToFontMap[USCRIPT_CODE_LIMIT];
void initializeScriptFontMap(ScriptToFontMap& scriptFontMap)
{
struct FontMap {
UScriptCode script;
const UChar* family;
};
static const FontMap fontMap[] = {
{USCRIPT_LATIN, L"times new roman"},
{USCRIPT_GREEK, L"times new roman"},
{USCRIPT_CYRILLIC, L"times new roman"},
{USCRIPT_SIMPLIFIED_HAN, L"simsun"},
{USCRIPT_TRADITIONAL_HAN, L"pmingliu"},
{USCRIPT_HIRAGANA, L"ms pgothic"},
{USCRIPT_KATAKANA, L"ms pgothic"},
{USCRIPT_KATAKANA_OR_HIRAGANA, L"ms pgothic"},
{USCRIPT_HANGUL, L"gulim"},
{USCRIPT_THAI, L"tahoma"},
{USCRIPT_HEBREW, L"david"},
{USCRIPT_ARABIC, L"tahoma"},
{USCRIPT_DEVANAGARI, L"mangal"},
{USCRIPT_BENGALI, L"vrinda"},
{USCRIPT_GURMUKHI, L"raavi"},
{USCRIPT_GUJARATI, L"shruti"},
{USCRIPT_TAMIL, L"latha"},
{USCRIPT_TELUGU, L"gautami"},
{USCRIPT_KANNADA, L"tunga"},
{USCRIPT_GEORGIAN, L"sylfaen"},
{USCRIPT_ARMENIAN, L"sylfaen"},
{USCRIPT_THAANA, L"mv boli"},
{USCRIPT_CANADIAN_ABORIGINAL, L"euphemia"},
{USCRIPT_CHEROKEE, L"plantagenet cherokee"},
{USCRIPT_MONGOLIAN, L"mongolian balti"},
};
struct ScriptToFontFamilies {
UScriptCode script;
const UChar** families;
};
static const UChar* malayalamFonts[] = {L"AnjaliOldLipi", L"Lohit Malayalam", L"Kartika", L"Rachana", 0};
static const UChar* khmerFonts[] = {L"Khmer OS", L"MoolBoran", L"DaunPenh", L"Code2000", 0};
static const UChar* ethiopicFonts[] = {L"Nyala", L"Abyssinica SIL", L"Ethiopia Jiret", L"Visual Geez Unicode", L"GF Zemen Unicode", 0};
static const UChar* oriyaFonts[] = {L"Kalinga", L"ori1Uni", L"Lohit Oriya", 0};
static const UChar* laoFonts[] = {L"DokChampa", L"Saysettha OT", L"Phetsarath OT", L"Code2000", 0};
static const UChar* tibetanFonts[] = {L"Microsoft Himalaya", L"Jomolhari", L"Tibetan Machine Uni", 0};
static const UChar* sinhalaFonts[] = {L"Iskoola Pota", L"AksharUnicode", 0};
static const UChar* yiFonts[] = {L"Microsoft Yi Balti", L"Nuosu SIL", L"Code2000", 0};
static const UChar* syriacFonts[] = {L"Estrangelo Edessa", L"Estrangelo Nisibin", L"Code2000", 0};
static const UChar* myanmarFonts[] = {L"Padauk", L"Parabaik", L"Myanmar3", L"Code2000", 0};
static const ScriptToFontFamilies scriptToFontFamilies[] = {
{USCRIPT_MALAYALAM, malayalamFonts},
{USCRIPT_KHMER, khmerFonts},
{USCRIPT_ETHIOPIC, ethiopicFonts},
{USCRIPT_ORIYA, oriyaFonts},
{USCRIPT_LAO, laoFonts},
{USCRIPT_TIBETAN, tibetanFonts},
{USCRIPT_SINHALA, sinhalaFonts},
{USCRIPT_YI, yiFonts},
{USCRIPT_SYRIAC, syriacFonts},
{USCRIPT_MYANMAR, myanmarFonts},
};
for (size_t i = 0; i < WTF_ARRAY_LENGTH(fontMap); ++i)
scriptFontMap[fontMap[i].script] = fontMap[i].family;
for (size_t i = 0; i < WTF_ARRAY_LENGTH(scriptToFontFamilies); ++i) {
UScriptCode script = scriptToFontFamilies[i].script;
scriptFontMap[script] = 0;
const UChar** familyPtr = scriptToFontFamilies[i].families;
while (*familyPtr) {
if (isFontPresent(*familyPtr)) {
scriptFontMap[script] = *familyPtr;
break;
}
++familyPtr;
}
}
icu::Locale locale = icu::Locale::getDefault();
const UChar* localeFamily = 0;
if (locale == icu::Locale::getJapanese())
localeFamily = scriptFontMap[USCRIPT_HIRAGANA];
else if (locale == icu::Locale::getKorean())
localeFamily = scriptFontMap[USCRIPT_HANGUL];
else if (locale == icu::Locale::getTraditionalChinese())
localeFamily = scriptFontMap[USCRIPT_TRADITIONAL_HAN];
else {
localeFamily = scriptFontMap[USCRIPT_SIMPLIFIED_HAN];
}
if (localeFamily)
scriptFontMap[USCRIPT_HAN] = localeFamily;
}
UScriptCode getScriptBasedOnUnicodeBlock(int ucs4)
{
UBlockCode block = ublock_getCode(ucs4);
switch (block) {
case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION:
return USCRIPT_HAN;
case UBLOCK_HIRAGANA:
case UBLOCK_KATAKANA:
return USCRIPT_HIRAGANA;
case UBLOCK_ARABIC:
return USCRIPT_ARABIC;
case UBLOCK_THAI:
return USCRIPT_THAI;
case UBLOCK_GREEK:
return USCRIPT_GREEK;
case UBLOCK_DEVANAGARI:
return USCRIPT_DEVANAGARI;
case UBLOCK_ARMENIAN:
return USCRIPT_ARMENIAN;
case UBLOCK_GEORGIAN:
return USCRIPT_GEORGIAN;
case UBLOCK_KANNADA:
return USCRIPT_KANNADA;
default:
return USCRIPT_COMMON;
}
}
UScriptCode getScript(int ucs4)
{
UErrorCode err = U_ZERO_ERROR;
UScriptCode script = uscript_getScript(ucs4, &err);
if (script <= USCRIPT_INHERITED || U_FAILURE(err))
script = getScriptBasedOnUnicodeBlock(ucs4);
return script;
}
const int kUndefinedAscent = std::numeric_limits<int>::min();
int getAscent(HFONT hfont)
{
HDC dc = GetDC(0);
HGDIOBJ oldFont = SelectObject(dc, hfont);
TEXTMETRIC tm;
BOOL gotMetrics = GetTextMetrics(dc, &tm);
SelectObject(dc, oldFont);
ReleaseDC(0, dc);
return gotMetrics ? tm.tmAscent : kUndefinedAscent;
}
WORD getSpaceGlyph(HFONT hfont)
{
HDC dc = GetDC(0);
HGDIOBJ oldFont = SelectObject(dc, hfont);
WCHAR space = L' ';
WORD spaceGlyph = 0;
GetGlyphIndices(dc, &space, 1, &spaceGlyph, 0);
SelectObject(dc, oldFont);
ReleaseDC(0, dc);
return spaceGlyph;
}
struct FontData {
FontData()
: hfont(0)
, ascent(kUndefinedAscent)
, scriptCache(0)
, spaceGlyph(0)
{
}
HFONT hfont;
int ascent;
mutable SCRIPT_CACHE scriptCache;
WORD spaceGlyph;
};
typedef HashMap<String, FontData> FontDataCache;
}
const UChar* getFontFamilyForScript(UScriptCode script,
FontDescription::GenericFamilyType generic)
{
static ScriptToFontMap scriptFontMap;
static bool initialized = false;
if (!initialized) {
initializeScriptFontMap(scriptFontMap);
initialized = true;
}
if (script == USCRIPT_INVALID_CODE)
return 0;
ASSERT(script < USCRIPT_CODE_LIMIT);
return scriptFontMap[script];
}
const UChar* getFallbackFamily(const UChar* characters,
int length,
FontDescription::GenericFamilyType generic,
UChar32* charChecked,
UScriptCode* scriptChecked)
{
ASSERT(characters && characters[0] && length > 0);
UScriptCode script = USCRIPT_COMMON;
int i = 0;
UChar32 ucs4 = 0;
while (i < length && script == USCRIPT_COMMON) {
U16_NEXT(characters, i, length, ucs4);
script = getScript(ucs4);
}
if (0xFF00 < ucs4 && ucs4 < 0xFF5F)
script = USCRIPT_HAN;
if (script == USCRIPT_COMMON)
script = getScriptBasedOnUnicodeBlock(ucs4);
const UChar* family = getFontFamilyForScript(script, generic);
if (!family || ucs4 > 0xFFFF) {
int plane = ucs4 >> 16;
switch (plane) {
case 1:
family = L"code2001";
break;
case 2:
if (icu::Locale::getDefault() == icu::Locale::getTraditionalChinese())
family = L"pmingliu-extb";
else
family = L"simsun-extb";
break;
default:
family = L"lucida sans unicode";
}
}
if (charChecked)
*charChecked = ucs4;
if (scriptChecked)
*scriptChecked = script;
return family;
}
bool getDerivedFontData(const UChar* family,
int style,
LOGFONT* logfont,
int* ascent,
HFONT* hfont,
SCRIPT_CACHE** scriptCache,
WORD* spaceGlyph)
{
ASSERT(logfont);
ASSERT(family);
ASSERT(*family);
static FontDataCache fontDataCache;
String fontKey = String::format("%1d:%d:%ls", style, logfont->lfHeight, family);
FontDataCache::iterator iter = fontDataCache.find(fontKey);
FontData* derived;
if (iter == fontDataCache.end()) {
ASSERT(wcslen(family) < LF_FACESIZE);
wcscpy_s(logfont->lfFaceName, LF_FACESIZE, family);
pair<FontDataCache::iterator, bool> entry = fontDataCache.add(fontKey, FontData());
derived = &entry.first->second;
derived->hfont = CreateFontIndirect(logfont);
derived->ascent = getAscent(derived->hfont);
derived->spaceGlyph = getSpaceGlyph(derived->hfont);
} else {
derived = &iter->second;
if (kUndefinedAscent == derived->ascent)
derived->ascent = getAscent(derived->hfont);
}
*hfont = derived->hfont;
*ascent = derived->ascent;
*scriptCache = &(derived->scriptCache);
*spaceGlyph = derived->spaceGlyph;
return *ascent != kUndefinedAscent;
}
int getStyleFromLogfont(const LOGFONT* logfont)
{
if (!logfont) {
ASSERT_NOT_REACHED();
return FontStyleNormal;
}
return (logfont->lfItalic ? FontStyleItalic : FontStyleNormal) |
(logfont->lfUnderline ? FontStyleUnderlined : FontStyleNormal) |
(logfont->lfWeight >= 700 ? FontStyleBold : FontStyleNormal);
}
}