1/* 2* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. 3* Copyright (C) 2007-2009 Torch Mobile, Inc. 4* 5* Redistribution and use in source and binary forms, with or without 6* modification, are permitted provided that the following conditions 7* are met: 8* 9* 1. Redistributions of source code must retain the above copyright 10* notice, this list of conditions and the following disclaimer. 11* 2. Redistributions in binary form must reproduce the above copyright 12* notice, this list of conditions and the following disclaimer in the 13* documentation and/or other materials provided with the distribution. 14* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15* its contributors may be used to endorse or promote products derived 16* from this software without specific prior written permission. 17* 18* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28*/ 29 30#include "config.h" 31#include "FontCache.h" 32 33#include "Font.h" 34#include "FontData.h" 35#include "SimpleFontData.h" 36#include "UnicodeRange.h" 37#include "wtf/OwnPtr.h" 38 39#include <windows.h> 40#include <mlang.h> 41 42namespace WebCore { 43 44extern HDC g_screenDC; 45 46static IMultiLanguage *multiLanguage = 0; 47 48#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) 49static IMLangFontLink2* langFontLink = 0; 50#else 51static IMLangFontLink* langFontLink = 0; 52#endif 53 54IMultiLanguage* FontCache::getMultiLanguageInterface() 55{ 56 if (!multiLanguage) 57 CoCreateInstance(CLSID_CMultiLanguage, 0, CLSCTX_INPROC_SERVER, IID_IMultiLanguage, (void**)&multiLanguage); 58 59 return multiLanguage; 60} 61 62#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) 63IMLangFontLink2* FontCache::getFontLinkInterface() 64#else 65IMLangFontLink* FontCache::getFontLinkInterface() 66#endif 67{ 68 if (!langFontLink) { 69 if (IMultiLanguage* mli = getMultiLanguageInterface()) 70 mli->QueryInterface(&langFontLink); 71 } 72 73 return langFontLink; 74} 75 76#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) 77static bool currentFontContainsCharacter(IMLangFontLink2* langFontLink, HDC hdc, UChar character) 78{ 79 UINT unicodeRanges; 80 if (S_OK != langFontLink->GetFontUnicodeRanges(hdc, &unicodeRanges, 0)) 81 return false; 82 83 static Vector<UNICODERANGE, 64> glyphsetBuffer; 84 glyphsetBuffer.resize(unicodeRanges); 85 86 if (S_OK != langFontLink->GetFontUnicodeRanges(hdc, &unicodeRanges, glyphsetBuffer.data())) 87 return false; 88 89 // FIXME: Change this to a binary search. (Yong Li: That's easy. But, is it guaranteed that the ranges are sorted?) 90 for (Vector<UNICODERANGE, 64>::const_iterator i = glyphsetBuffer.begin(); i != glyphsetBuffer.end(); ++i) { 91 if (i->wcTo >= character) 92 return i->wcFrom <= character; 93 } 94 95 return false; 96} 97#else 98static bool currentFontContainsCharacter(IMLangFontLink* langFontLink, HDC hdc, HFONT hfont, UChar character, const wchar_t* faceName) 99{ 100 DWORD fontCodePages = 0, charCodePages = 0; 101 HRESULT result = langFontLink->GetFontCodePages(hdc, hfont, &fontCodePages); 102 if (result != S_OK) 103 return false; 104 result = langFontLink->GetCharCodePages(character, &charCodePages); 105 if (result != S_OK) 106 return false; 107 108 fontCodePages |= FontPlatformData::getKnownFontCodePages(faceName); 109 if (fontCodePages & charCodePages) 110 return true; 111 112 return false; 113} 114#endif 115 116#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) 117static HFONT createMLangFont(IMLangFontLink2* langFontLink, HDC hdc, DWORD codePageMask, UChar character = 0) 118{ 119 HFONT mlangFont; 120 if (SUCCEEDED(langFontLink->MapFont(hdc, codePageMask, character, &mlangFont))) 121 return mlangFont; 122 123 return 0; 124} 125#else 126static HFONT createMLangFont(IMLangFontLink* langFontLink, HDC hdc, const FontPlatformData& refFont, DWORD codePageMask) 127{ 128 HFONT mlangFont; 129 LRESULT result = langFontLink->MapFont(hdc, codePageMask, refFont.hfont(), &mlangFont); 130 131 return result == S_OK ? mlangFont : 0; 132} 133#endif 134 135static const Vector<DWORD, 4>& getCJKCodePageMasks() 136{ 137 // The default order in which we look for a font for a CJK character. If the user's default code page is 138 // one of these, we will use it first. 139 static const UINT CJKCodePages[] = { 140 932, /* Japanese */ 141 936, /* Simplified Chinese */ 142 950, /* Traditional Chinese */ 143 949 /* Korean */ 144 }; 145 146 static Vector<DWORD, 4> codePageMasks; 147 static bool initialized; 148 if (!initialized) { 149 initialized = true; 150#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) 151 IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface(); 152#else 153 IMLangFontLink* langFontLink = fontCache()->getFontLinkInterface(); 154#endif 155 if (!langFontLink) 156 return codePageMasks; 157 158 UINT defaultCodePage; 159 DWORD defaultCodePageMask = 0; 160 if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE, reinterpret_cast<LPWSTR>(&defaultCodePage), sizeof(defaultCodePage))) 161 langFontLink->CodePageToCodePages(defaultCodePage, &defaultCodePageMask); 162 163 if (defaultCodePage == CJKCodePages[0] || defaultCodePage == CJKCodePages[1] || defaultCodePage == CJKCodePages[2] || defaultCodePage == CJKCodePages[3]) 164 codePageMasks.append(defaultCodePageMask); 165 for (unsigned i = 0; i < 4; ++i) { 166 if (defaultCodePage != CJKCodePages[i]) { 167 DWORD codePageMask; 168 langFontLink->CodePageToCodePages(CJKCodePages[i], &codePageMask); 169 codePageMasks.append(codePageMask); 170 } 171 } 172 } 173 return codePageMasks; 174} 175 176 177struct TraitsInFamilyProcData { 178 TraitsInFamilyProcData(const AtomicString& familyName) 179 : m_familyName(familyName) 180 { 181 } 182 183 const AtomicString& m_familyName; 184 HashSet<unsigned> m_traitsMasks; 185}; 186 187static int CALLBACK traitsInFamilyEnumProc(CONST LOGFONT* logFont, CONST TEXTMETRIC* metrics, DWORD fontType, LPARAM lParam) 188{ 189 TraitsInFamilyProcData* procData = reinterpret_cast<TraitsInFamilyProcData*>(lParam); 190 191 unsigned traitsMask = 0; 192 traitsMask |= logFont->lfItalic ? FontStyleItalicMask : FontStyleNormalMask; 193 traitsMask |= FontVariantNormalMask; 194 LONG weight = FontPlatformData::adjustedGDIFontWeight(logFont->lfWeight, procData->m_familyName); 195 traitsMask |= weight == FW_THIN ? FontWeight100Mask : 196 weight == FW_EXTRALIGHT ? FontWeight200Mask : 197 weight == FW_LIGHT ? FontWeight300Mask : 198 weight == FW_NORMAL ? FontWeight400Mask : 199 weight == FW_MEDIUM ? FontWeight500Mask : 200 weight == FW_SEMIBOLD ? FontWeight600Mask : 201 weight == FW_BOLD ? FontWeight700Mask : 202 weight == FW_EXTRABOLD ? FontWeight800Mask : 203 FontWeight900Mask; 204 procData->m_traitsMasks.add(traitsMask); 205 return 1; 206} 207 208void FontCache::platformInit() 209{ 210} 211 212void FontCache::comInitialize() 213{ 214} 215 216void FontCache::comUninitialize() 217{ 218 if (langFontLink) { 219 langFontLink->Release(); 220 langFontLink = 0; 221 } 222 if (multiLanguage) { 223 multiLanguage->Release(); 224 multiLanguage = 0; 225 } 226} 227 228const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, const UChar* characters, int length) 229{ 230 String familyName; 231 WCHAR name[LF_FACESIZE]; 232 233 UChar character = characters[0]; 234 const FontPlatformData& origFont = font.primaryFont()->fontDataForCharacter(character)->platformData(); 235 unsigned unicodeRange = findCharUnicodeRange(character); 236 237#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) 238 if (IMLangFontLink2* langFontLink = getFontLinkInterface()) { 239#else 240 if (IMLangFontLink* langFontLink = getFontLinkInterface()) { 241#endif 242 HGDIOBJ oldFont = GetCurrentObject(g_screenDC, OBJ_FONT); 243 HFONT hfont = 0; 244 DWORD codePages = 0; 245 UINT codePage = 0; 246 // Try MLang font linking first. 247 langFontLink->GetCharCodePages(character, &codePages); 248 if (codePages && unicodeRange == cRangeSetCJK) { 249 // The CJK character may belong to multiple code pages. We want to 250 // do font linking against a single one of them, preferring the default 251 // code page for the user's locale. 252 const Vector<DWORD, 4>& CJKCodePageMasks = getCJKCodePageMasks(); 253 unsigned numCodePages = CJKCodePageMasks.size(); 254 for (unsigned i = 0; i < numCodePages; ++i) { 255#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) 256 hfont = createMLangFont(langFontLink, g_screenDC, CJKCodePageMasks[i]); 257#else 258 hfont = createMLangFont(langFontLink, g_screenDC, origFont, CJKCodePageMasks[i]); 259#endif 260 if (!hfont) 261 continue; 262 263 SelectObject(g_screenDC, hfont); 264 GetTextFace(g_screenDC, LF_FACESIZE, name); 265 266 if (hfont && !(codePages & CJKCodePageMasks[i])) { 267 // We asked about a code page that is not one of the code pages 268 // returned by MLang, so the font might not contain the character. 269#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) 270 if (!currentFontContainsCharacter(langFontLink, g_screenDC, character)) { 271#else 272 if (!currentFontContainsCharacter(langFontLink, g_screenDC, hfont, character, name)) { 273#endif 274 SelectObject(g_screenDC, oldFont); 275 langFontLink->ReleaseFont(hfont); 276 hfont = 0; 277 continue; 278 } 279 } 280 break; 281 } 282 } else { 283#if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2) 284 hfont = createMLangFont(langFontLink, g_screenDC, codePages, character); 285#else 286 hfont = createMLangFont(langFontLink, g_screenDC, origFont, codePages); 287#endif 288 SelectObject(g_screenDC, hfont); 289 GetTextFace(g_screenDC, LF_FACESIZE, name); 290 } 291 SelectObject(g_screenDC, oldFont); 292 293 if (hfont) { 294 familyName = name; 295 langFontLink->ReleaseFont(hfont); 296 } else 297 FontPlatformData::mapKnownFont(codePages, familyName); 298 } 299 300 if (familyName.isEmpty()) 301 familyName = FontPlatformData::defaultFontFamily(); 302 303 if (!familyName.isEmpty()) { 304 // FIXME: temporary workaround for Thai font problem 305 FontDescription fontDescription(font.fontDescription()); 306 if (unicodeRange == cRangeThai && fontDescription.weight() > FontWeightNormal) 307 fontDescription.setWeight(FontWeightNormal); 308 309 FontPlatformData* result = getCachedFontPlatformData(fontDescription, familyName); 310 if (result && result->hash() != origFont.hash()) { 311 if (SimpleFontData* fontData = getCachedFontData(result)) 312 return fontData; 313 } 314 } 315 316 return 0; 317} 318 319SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font) 320{ 321 return 0; 322} 323 324SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& fontDesc) 325{ 326 // FIXME: Would be even better to somehow get the user's default font here. For now we'll pick 327 // the default that the user would get without changing any prefs. 328 return getCachedFontData(fontDesc, FontPlatformData::defaultFontFamily()); 329} 330 331FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) 332{ 333 FontPlatformData* result = new FontPlatformData(fontDescription, family); 334 return result; 335} 336 337void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) 338{ 339 LOGFONT logFont; 340 logFont.lfCharSet = DEFAULT_CHARSET; 341 unsigned familyLength = std::min(familyName.length(), static_cast<unsigned>(LF_FACESIZE - 1)); 342 memcpy(logFont.lfFaceName, familyName.characters(), familyLength * sizeof(UChar)); 343 logFont.lfFaceName[familyLength] = 0; 344 logFont.lfPitchAndFamily = 0; 345 346 TraitsInFamilyProcData procData(familyName); 347 EnumFontFamiliesEx(g_screenDC, &logFont, traitsInFamilyEnumProc, reinterpret_cast<LPARAM>(&procData), 0); 348 copyToVector(procData.m_traitsMasks, traitsMasks); 349} 350 351} // namespace WebCore 352