/* * Copyright (C) 2007-2009 Torch Mobile Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "config.h" #include "FontPlatformData.h" #include "Font.h" #include "FontCache.h" #include "FontData.h" #include "PlatformString.h" #include "SimpleFontData.h" #include "UnicodeRange.h" #include "wtf/OwnPtr.h" #include #include #include #include namespace WebCore { extern HDC g_screenDC; static wchar_t songTiStr[] = { 0x5b8b, 0x4f53, 0 }; static wchar_t heiTiStr[] = { 0x9ed1, 0x4f53, 0 }; class FontFamilyCodePageInfo { public: FontFamilyCodePageInfo() : m_codePage(0), m_codePages(0) { } FontFamilyCodePageInfo(const wchar_t* family, UINT codePage) : m_family(family), m_codePage(codePage), m_codePages(0) { } DWORD codePages() const { if (!m_codePages) { #if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) if (IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface()) langFontLink->CodePageToCodePages(m_codePage, &m_codePages); #else if (IMLangFontLink* langFontLink = fontCache()->getFontLinkInterface()) langFontLink->CodePageToCodePages(m_codePage, &m_codePages); #endif } return m_codePages; } String m_family; UINT m_codePage; private: mutable DWORD m_codePages; }; class FontFamilyChecker { public: FontFamilyChecker(const wchar_t* family) : m_exists(false) { EnumFontFamilies(g_screenDC, family, enumFontFamProc, (LPARAM)this); } bool isSupported() const { return m_exists; } private: bool m_exists; static int CALLBACK enumFontFamProc(const LOGFONT FAR* lpelf, const TEXTMETRIC FAR* lpntm, DWORD FontType, LPARAM lParam); }; class ValidFontFamilyFinder { public: ValidFontFamilyFinder() { EnumFontFamilies(g_screenDC, 0, enumFontFamProc, (LPARAM)this); } const String& family() const { return m_family; } private: String m_family; static int CALLBACK enumFontFamProc(const LOGFONT FAR* lpelf, const TEXTMETRIC FAR* lpntm, DWORD FontType, LPARAM lParam); }; class FixedSizeFontData: public RefCounted { public: LOGFONT m_font; OwnPtr m_hfont; TEXTMETRIC m_metrics; DWORD m_codePages; unsigned m_weight; bool m_italic; static PassRefPtr create(const AtomicString& family, unsigned weight, bool italic); private: FixedSizeFontData() : m_codePages(0) , m_weight(0) , m_italic(false) { memset(&m_font, 0, sizeof(m_font)); memset(&m_metrics, 0, sizeof(m_metrics)); } }; struct FixedSizeFontDataKey { FixedSizeFontDataKey(const AtomicString& family = AtomicString(), unsigned weight = 0, bool italic = false) : m_family(family) , m_weight(weight) , m_italic(italic) { } FixedSizeFontDataKey(WTF::HashTableDeletedValueType) : m_weight(-2) { } bool isHashTableDeletedValue() const { return m_weight == -2; } bool operator==(const FixedSizeFontDataKey& other) const { return equalIgnoringCase(m_family, other.m_family) && m_weight == other.m_weight && m_italic == other.m_italic; } AtomicString m_family; unsigned m_weight; bool m_italic; }; struct FixedSizeFontDataKeyHash { static unsigned hash(const FixedSizeFontDataKey& font) { unsigned hashCodes[] = { CaseFoldingHash::hash(font.m_family), font.m_weight, // static_cast(font.m_italic); }; return StringHasher::hashMemory(hashCodes); } static bool equal(const FixedSizeFontDataKey& a, const FixedSizeFontDataKey& b) { return a == b; } static const bool safeToCompareToEmptyOrDeleted = true; }; struct FixedSizeFontDataKeyTraits : WTF::GenericHashTraits { static const bool emptyValueIsZero = true; static const FixedSizeFontDataKey& emptyValue() { DEFINE_STATIC_LOCAL(FixedSizeFontDataKey, key, (nullAtom)); return key; } static void constructDeletedValue(FixedSizeFontDataKey& slot) { new (&slot) FixedSizeFontDataKey(WTF::HashTableDeletedValue); } static bool isDeletedValue(const FixedSizeFontDataKey& value) { return value.isHashTableDeletedValue(); } }; int CALLBACK FontFamilyChecker::enumFontFamProc(const LOGFONT FAR* lpelf, const TEXTMETRIC FAR* lpntm, DWORD FontType, LPARAM lParam) { ((FontFamilyChecker*)lParam)->m_exists = true; return 0; } int CALLBACK ValidFontFamilyFinder::enumFontFamProc(const LOGFONT FAR* lpelf, const TEXTMETRIC FAR* lpntm, DWORD FontType, LPARAM lParam) { if (lpelf->lfCharSet != SYMBOL_CHARSET) { ((ValidFontFamilyFinder*)lParam)->m_family = String(lpelf->lfFaceName); return 0; } return 1; } typedef Vector KnownFonts; static KnownFonts& knownFonts() { static KnownFonts fonts; static bool firstTime = true; if (firstTime) { firstTime = false; if (FontPlatformData::isSongTiSupported()) fonts.append(FontFamilyCodePageInfo(songTiStr, 936)); } return fonts; } static String getDefaultFontFamily() { if (FontFamilyChecker(L"Tahoma").isSupported()) return String(L"Tahoma"); bool good = false; String family; HKEY key; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\GDI\\SysFnt", 0, 0, &key) == ERROR_SUCCESS) { DWORD maxlen, type; if (RegQueryValueEx(key, L"Nm", 0, &type, 0, &maxlen) == ERROR_SUCCESS && type == REG_SZ) { ++maxlen; if (wchar_t* buffer = new wchar_t[maxlen]) { if (RegQueryValueEx(key, L"Nm", 0, &type, (LPBYTE)buffer, &maxlen) == ERROR_SUCCESS) { family = String(buffer, maxlen); good = true; } delete[] buffer; } } RegCloseKey(key); } if (good) return family; return ValidFontFamilyFinder().family(); } typedef HashMap, FixedSizeFontDataKeyHash, FixedSizeFontDataKeyTraits> FixedSizeFontCache; FixedSizeFontCache g_fixedSizeFontCache; PassRefPtr FixedSizeFontData::create(const AtomicString& family, unsigned weight, bool italic) { FixedSizeFontData* fontData = new FixedSizeFontData(); fontData->m_weight = weight; fontData->m_italic = italic; LOGFONT& winFont = fontData->m_font; // The size here looks unusual. The negative number is intentional. winFont.lfHeight = -72; winFont.lfWidth = 0; winFont.lfEscapement = 0; winFont.lfOrientation = 0; winFont.lfUnderline = false; winFont.lfStrikeOut = false; winFont.lfCharSet = DEFAULT_CHARSET; winFont.lfOutPrecision = OUT_DEFAULT_PRECIS; winFont.lfQuality = CLEARTYPE_QUALITY; //DEFAULT_QUALITY; winFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; winFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; winFont.lfItalic = italic; winFont.lfWeight = FontPlatformData::adjustedGDIFontWeight(weight, family); int len = std::min(family.length(), (unsigned int)LF_FACESIZE - 1); wmemcpy(winFont.lfFaceName, family.characters(), len); winFont.lfFaceName[len] = L'\0'; fontData->m_hfont.set(CreateFontIndirect(&winFont)); HGDIOBJ oldFont = SelectObject(g_screenDC, fontData->m_hfont.get()); GetTextMetrics(g_screenDC, &fontData->m_metrics); #if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) if (IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface()) { #else if (IMLangFontLink* langFontLink = fontCache()->getFontLinkInterface()) { #endif langFontLink->GetFontCodePages(g_screenDC, fontData->m_hfont.get(), &fontData->m_codePages); fontData->m_codePages |= FontPlatformData::getKnownFontCodePages(winFont.lfFaceName); } SelectObject(g_screenDC, oldFont); return adoptRef(fontData); } static PassRefPtr createFixedSizeFontData(const AtomicString& family, unsigned weight, bool italic) { FixedSizeFontDataKey key(family, weight, italic); pair result = g_fixedSizeFontCache.add(key, RefPtr()); if (result.second) result.first->second = FixedSizeFontData::create(family, weight, italic); return result.first->second; } static LONG toGDIFontWeight(FontWeight fontWeight) { static LONG gdiFontWeights[] = { FW_THIN, // FontWeight100 FW_EXTRALIGHT, // FontWeight200 FW_LIGHT, // FontWeight300 FW_NORMAL, // FontWeight400 FW_MEDIUM, // FontWeight500 FW_SEMIBOLD, // FontWeight600 FW_BOLD, // FontWeight700 FW_EXTRABOLD, // FontWeight800 FW_HEAVY // FontWeight900 }; return gdiFontWeights[fontWeight]; } class FontPlatformPrivateData { public: int m_reference; RefPtr m_rootFontData; AtomicString m_family; FontDescription m_fontDescription; OwnPtr m_hfontScaled; int m_size; long m_fontScaledWidth; long m_fontScaledHeight; bool m_disabled; FontPlatformPrivateData(int size, unsigned weight) : m_reference(1) , m_family(FontPlatformData::defaultFontFamily()) , m_size(size) , m_fontScaledWidth(0) , m_fontScaledHeight(0) , m_disabled(false) { m_rootFontData = createFixedSizeFontData(m_family, weight, false); } FontPlatformPrivateData(const FontDescription& fontDescription, const AtomicString& family) : m_reference(1) , m_size(fontDescription.computedPixelSize()) , m_fontDescription(fontDescription) , m_family(family) , m_fontScaledWidth(0) , m_fontScaledHeight(0) , m_disabled(!fontDescription.specifiedSize()) { m_rootFontData = FixedSizeFontData::create(family, toGDIFontWeight(fontDescription.weight()), fontDescription.italic()); } }; FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const AtomicString& desiredFamily, bool useDefaultFontIfNotPresent) { String family(desiredFamily); if (!equalIgnoringCase(family, defaultFontFamily()) && !FontFamilyChecker(family.charactersWithNullTermination()).isSupported()) { if (equalIgnoringCase(family, String(heiTiStr)) && isSongTiSupported()) family = String(songTiStr); else if (useDefaultFontIfNotPresent) family = defaultFontFamily(); } m_private = new FontPlatformPrivateData(fontDescription, family); } FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) { if (!size) m_private = 0; else m_private = new FontPlatformPrivateData((int)(size + 0.5), bold ? FW_BOLD : FW_NORMAL); } FontPlatformData::~FontPlatformData() { if (isValid() && !--m_private->m_reference) { if (m_private->m_rootFontData->refCount() == 2) { FixedSizeFontDataKey key(m_private->m_family, m_private->m_rootFontData->m_weight, m_private->m_rootFontData->m_italic); g_fixedSizeFontCache.remove(key); } delete m_private; } } FontPlatformData& FontPlatformData::operator=(const FontPlatformData& o) { if (isValid() && !--m_private->m_reference) delete m_private; if (m_private = o.m_private) ++m_private->m_reference; return *this; } HFONT FontPlatformData::hfont() const { if (!isValid()) return 0; if (m_private->m_disabled) return 0; if (!m_private->m_rootFontData->m_hfont) m_private->m_rootFontData->m_hfont.set(CreateFontIndirect(&m_private->m_rootFontData->m_font)); return m_private->m_rootFontData->m_hfont.get(); } HFONT FontPlatformData::getScaledFontHandle(int height, int width) const { if (!isValid() || m_private->m_disabled) return 0; if (!m_private->m_hfontScaled || m_private->m_fontScaledHeight != height || m_private->m_fontScaledWidth != width) { m_private->m_fontScaledHeight = height; m_private->m_fontScaledWidth = width; LOGFONT font = m_private->m_rootFontData->m_font; font.lfHeight = -height; font.lfWidth = width; m_private->m_hfontScaled.set(CreateFontIndirect(&font)); } return m_private->m_hfontScaled.get(); } bool FontPlatformData::discardFontHandle() { if (!isValid()) return false; if (m_private->m_rootFontData->m_hfont) { m_private->m_rootFontData->m_hfont.set(0); return true; } if (m_private->m_hfontScaled) { m_private->m_hfontScaled.set(0); return true; } return false; } const TEXTMETRIC& FontPlatformData::metrics() const { return m_private->m_rootFontData->m_metrics; } bool FontPlatformData::isSystemFont() const { return false; } int FontPlatformData::size() const { return m_private->m_size; } const FontDescription& FontPlatformData::fontDescription() const { return m_private->m_fontDescription; } const AtomicString& FontPlatformData::family() const { return m_private->m_family; } const LOGFONT& FontPlatformData::logFont() const { return m_private->m_rootFontData->m_font; } int FontPlatformData::averageCharWidth() const { return (m_private->m_rootFontData->m_metrics.tmAveCharWidth * size() + 36) / 72; } bool FontPlatformData::isDisabled() const { return !isValid() || m_private->m_disabled; } DWORD FontPlatformData::codePages() const { return m_private->m_rootFontData->m_codePages; } bool FontPlatformData::isSongTiSupported() { static bool exists = FontFamilyChecker(songTiStr).isSupported(); return exists; } bool FontPlatformData::mapKnownFont(DWORD codePages, String& family) { KnownFonts& fonts = knownFonts(); for (KnownFonts::iterator i = fonts.begin(); i != fonts.end(); ++i) { if (i->codePages() & codePages) { family = i->m_family; return true; } } return false; } DWORD FontPlatformData::getKnownFontCodePages(const wchar_t* family) { KnownFonts& fonts = knownFonts(); for (KnownFonts::iterator i = fonts.begin(); i != fonts.end(); ++i) { if (equalIgnoringCase(i->m_family, String(family))) return i->codePages(); } return 0; } const String& FontPlatformData::defaultFontFamily() { static String family(getDefaultFontFamily()); return family; } LONG FontPlatformData::adjustedGDIFontWeight(LONG gdiFontWeight, const String& family) { static AtomicString lucidaStr("Lucida Grande"); if (equalIgnoringCase(family, lucidaStr)) { if (gdiFontWeight == FW_NORMAL) return FW_MEDIUM; if (gdiFontWeight == FW_BOLD) return FW_SEMIBOLD; } return gdiFontWeight; } #ifndef NDEBUG String FontPlatformData::description() const { return String(); } #endif }