FontPlatformData.cpp [plain text]
#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 <wtf/StdLibExtras.h>
#include <wtf/text/StringHash.h>
#include <windows.h>
#include <mlang.h>
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<FixedSizeFontData> {
public:
LOGFONT m_font;
OwnPtr<HFONT> m_hfont;
TEXTMETRIC m_metrics;
DWORD m_codePages;
unsigned m_weight;
bool m_italic;
static PassRefPtr<FixedSizeFontData> 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,
};
return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes);
}
static bool equal(const FixedSizeFontDataKey& a, const FixedSizeFontDataKey& b)
{
return a == b;
}
static const bool safeToCompareToEmptyOrDeleted = true;
};
struct FixedSizeFontDataKeyTraits : WTF::GenericHashTraits<FixedSizeFontDataKey> {
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<FontFamilyCodePageInfo> 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<FixedSizeFontDataKey, RefPtr<FixedSizeFontData>, FixedSizeFontDataKeyHash, FixedSizeFontDataKeyTraits> FixedSizeFontCache;
FixedSizeFontCache g_fixedSizeFontCache;
PassRefPtr<FixedSizeFontData> 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;
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; 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 = adoptPtr(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<FixedSizeFontData> createFixedSizeFontData(const AtomicString& family, unsigned weight, bool italic)
{
FixedSizeFontDataKey key(family, weight, italic);
pair<FixedSizeFontCache::iterator, bool> result = g_fixedSizeFontCache.add(key, RefPtr<FixedSizeFontData>());
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, FW_EXTRALIGHT, FW_LIGHT, FW_NORMAL, FW_MEDIUM, FW_SEMIBOLD, FW_BOLD, FW_EXTRABOLD, FW_HEAVY };
return gdiFontWeights[fontWeight];
}
class FontPlatformPrivateData {
public:
int m_reference;
RefPtr<FixedSizeFontData> m_rootFontData;
AtomicString m_family;
FontDescription m_fontDescription;
OwnPtr<HFONT> 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 = adoptPtr(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 = adoptPtr(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 = nullptr;
return true;
}
if (m_private->m_hfontScaled) {
m_private->m_hfontScaled = nullptr;
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
}