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; 47static IMLangFontLinkType* langFontLink = 0; 48 49IMultiLanguage* FontCache::getMultiLanguageInterface() 50{ 51 if (!multiLanguage) 52 CoCreateInstance(CLSID_CMultiLanguage, 0, CLSCTX_INPROC_SERVER, IID_IMultiLanguage, (void**)&multiLanguage); 53 54 return multiLanguage; 55} 56 57IMLangFontLinkType* FontCache::getFontLinkInterface() 58{ 59 if (!langFontLink) { 60 if (IMultiLanguage* mli = getMultiLanguageInterface()) 61 mli->QueryInterface(&langFontLink); 62 } 63 64 return langFontLink; 65} 66 67static bool currentFontContainsCharacter(IMLangFontLinkType* langFontLink, HDC hdc, HFONT hfont, UChar character, const wchar_t* faceName) 68{ 69#if USE(IMLANG_FONT_LINK2) 70 UINT unicodeRanges; 71 if (S_OK != langFontLink->GetFontUnicodeRanges(hdc, &unicodeRanges, 0)) 72 return false; 73 74 static Vector<UNICODERANGE, 64> glyphsetBuffer; 75 glyphsetBuffer.resize(unicodeRanges); 76 77 if (S_OK != langFontLink->GetFontUnicodeRanges(hdc, &unicodeRanges, glyphsetBuffer.data())) 78 return false; 79 80 // FIXME: Change this to a binary search. (Yong Li: That's easy. But, is it guaranteed that the ranges are sorted?) 81 for (Vector<UNICODERANGE, 64>::const_iterator i = glyphsetBuffer.begin(); i != glyphsetBuffer.end(); ++i) { 82 if (i->wcTo >= character) 83 return i->wcFrom <= character; 84 } 85#else 86 DWORD fontCodePages = 0, charCodePages = 0; 87 HRESULT result = langFontLink->GetFontCodePages(hdc, hfont, &fontCodePages); 88 if (result != S_OK) 89 return false; 90 result = langFontLink->GetCharCodePages(character, &charCodePages); 91 if (result != S_OK) 92 return false; 93 94 fontCodePages |= FontPlatformData::getKnownFontCodePages(faceName); 95 if (fontCodePages & charCodePages) 96 return true; 97#endif 98 99 return false; 100} 101 102static HFONT createMLangFont(IMLangFontLinkType* langFontLink, HDC hdc, const FontPlatformData& refFont, DWORD codePageMask, UChar character = 0) 103{ 104 HFONT mlangFont; 105 106#if USE(IMLANG_FONT_LINK2) 107 HRESULT result = langFontLink->MapFont(hdc, codePageMask, character, &mlangFont); 108#else 109 HRESULT result = langFontLink->MapFont(hdc, codePageMask, refFont.hfont(), &mlangFont); 110#endif 111 112 if (SUCCEEDED(result)) 113 return mlangFont; 114 return 0; 115} 116 117static const Vector<DWORD, 4>& getCJKCodePageMasks() 118{ 119 // The default order in which we look for a font for a CJK character. If the user's default code page is 120 // one of these, we will use it first. 121 static const UINT CJKCodePages[] = { 122 932, /* Japanese */ 123 936, /* Simplified Chinese */ 124 950, /* Traditional Chinese */ 125 949 /* Korean */ 126 }; 127 128 static Vector<DWORD, 4> codePageMasks; 129 static bool initialized; 130 if (!initialized) { 131 initialized = true; 132 IMLangFontLinkType* langFontLink = fontCache()->getFontLinkInterface(); 133 if (!langFontLink) 134 return codePageMasks; 135 136 UINT defaultCodePage; 137 DWORD defaultCodePageMask = 0; 138 if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_RETURN_NUMBER | LOCALE_IDEFAULTANSICODEPAGE, reinterpret_cast<LPWSTR>(&defaultCodePage), sizeof(defaultCodePage))) 139 langFontLink->CodePageToCodePages(defaultCodePage, &defaultCodePageMask); 140 141 if (defaultCodePage == CJKCodePages[0] || defaultCodePage == CJKCodePages[1] || defaultCodePage == CJKCodePages[2] || defaultCodePage == CJKCodePages[3]) 142 codePageMasks.append(defaultCodePageMask); 143 for (unsigned i = 0; i < 4; ++i) { 144 if (defaultCodePage != CJKCodePages[i]) { 145 DWORD codePageMask; 146 langFontLink->CodePageToCodePages(CJKCodePages[i], &codePageMask); 147 codePageMasks.append(codePageMask); 148 } 149 } 150 } 151 return codePageMasks; 152} 153 154 155struct TraitsInFamilyProcData { 156 TraitsInFamilyProcData(const AtomicString& familyName) 157 : m_familyName(familyName) 158 { 159 } 160 161 const AtomicString& m_familyName; 162 HashSet<unsigned> m_traitsMasks; 163}; 164 165static int CALLBACK traitsInFamilyEnumProc(CONST LOGFONT* logFont, CONST TEXTMETRIC* metrics, DWORD fontType, LPARAM lParam) 166{ 167 TraitsInFamilyProcData* procData = reinterpret_cast<TraitsInFamilyProcData*>(lParam); 168 169 unsigned traitsMask = 0; 170 traitsMask |= logFont->lfItalic ? FontStyleItalicMask : FontStyleNormalMask; 171 traitsMask |= FontVariantNormalMask; 172 LONG weight = FontPlatformData::adjustedGDIFontWeight(logFont->lfWeight, procData->m_familyName); 173 traitsMask |= weight == FW_THIN ? FontWeight100Mask : 174 weight == FW_EXTRALIGHT ? FontWeight200Mask : 175 weight == FW_LIGHT ? FontWeight300Mask : 176 weight == FW_NORMAL ? FontWeight400Mask : 177 weight == FW_MEDIUM ? FontWeight500Mask : 178 weight == FW_SEMIBOLD ? FontWeight600Mask : 179 weight == FW_BOLD ? FontWeight700Mask : 180 weight == FW_EXTRABOLD ? FontWeight800Mask : 181 FontWeight900Mask; 182 procData->m_traitsMasks.add(traitsMask); 183 return 1; 184} 185 186void FontCache::platformInit() 187{ 188} 189 190void FontCache::comInitialize() 191{ 192} 193 194void FontCache::comUninitialize() 195{ 196 if (langFontLink) { 197 langFontLink->Release(); 198 langFontLink = 0; 199 } 200 if (multiLanguage) { 201 multiLanguage->Release(); 202 multiLanguage = 0; 203 } 204} 205 206PassRefPtr<SimpleFontData> FontCache::systemFallbackForCharacters(const FontDescription& description, const SimpleFontData* originalFontData, bool, const UChar* characters, int length) 207{ 208 String familyName; 209 WCHAR name[LF_FACESIZE]; 210 211 UChar character = characters[0]; 212 const FontPlatformData& origFont = originalFontData->platformData(); 213 unsigned unicodeRange = findCharUnicodeRange(character); 214 215 if (IMLangFontLinkType* langFontLink = getFontLinkInterface()) { 216 HGDIOBJ oldFont = GetCurrentObject(g_screenDC, OBJ_FONT); 217 HFONT hfont = 0; 218 DWORD codePages = 0; 219 UINT codePage = 0; 220 // Try MLang font linking first. 221 langFontLink->GetCharCodePages(character, &codePages); 222 if (codePages && unicodeRange == cRangeSetCJK) { 223 // The CJK character may belong to multiple code pages. We want to 224 // do font linking against a single one of them, preferring the default 225 // code page for the user's locale. 226 const Vector<DWORD, 4>& CJKCodePageMasks = getCJKCodePageMasks(); 227 unsigned numCodePages = CJKCodePageMasks.size(); 228 for (unsigned i = 0; i < numCodePages; ++i) { 229 hfont = createMLangFont(langFontLink, g_screenDC, origFont, CJKCodePageMasks[i]); 230 if (!hfont) 231 continue; 232 233 SelectObject(g_screenDC, hfont); 234 GetTextFace(g_screenDC, LF_FACESIZE, name); 235 236 if (hfont && !(codePages & CJKCodePageMasks[i])) { 237 // We asked about a code page that is not one of the code pages 238 // returned by MLang, so the font might not contain the character. 239 if (!currentFontContainsCharacter(langFontLink, g_screenDC, hfont, character, name)) 240 { 241 SelectObject(g_screenDC, oldFont); 242 langFontLink->ReleaseFont(hfont); 243 hfont = 0; 244 continue; 245 } 246 } 247 break; 248 } 249 } else { 250 hfont = createMLangFont(langFontLink, g_screenDC, origFont, codePages, character); 251 SelectObject(g_screenDC, hfont); 252 GetTextFace(g_screenDC, LF_FACESIZE, name); 253 } 254 SelectObject(g_screenDC, oldFont); 255 256 if (hfont) { 257 familyName = name; 258 langFontLink->ReleaseFont(hfont); 259 } else 260 FontPlatformData::mapKnownFont(codePages, familyName); 261 } 262 263 if (familyName.isEmpty()) 264 familyName = FontPlatformData::defaultFontFamily(); 265 266 if (!familyName.isEmpty()) { 267 // FIXME: temporary workaround for Thai font problem 268 FontDescription fontDescription(description); 269 if (unicodeRange == cRangeThai && fontDescription.weight() > FontWeightNormal) 270 fontDescription.setWeight(FontWeightNormal); 271 272 FontPlatformData* result = getCachedFontPlatformData(fontDescription, familyName); 273 if (result && result->hash() != origFont.hash()) { 274 if (RefPtr<SimpleFontData> fontData = getCachedFontData(result, DoNotRetain)) 275 return fontData.release(); 276 } 277 } 278 279 return 0; 280} 281 282PassRefPtr<SimpleFontData> FontCache::getLastResortFallbackFont(const FontDescription& fontDesc, ShouldRetain shouldRetain) 283{ 284 // FIXME: Would be even better to somehow get the user's default font here. For now we'll pick 285 // the default that the user would get without changing any prefs. 286 return getCachedFontData(fontDesc, FontPlatformData::defaultFontFamily(), false, shouldRetain); 287} 288 289PassOwnPtr<FontPlatformData> FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) 290{ 291 return adoptPtr(new FontPlatformData(fontDescription, family)); 292} 293 294void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigned>& traitsMasks) 295{ 296 LOGFONT logFont; 297 logFont.lfCharSet = DEFAULT_CHARSET; 298 unsigned familyLength = std::min(familyName.length(), static_cast<unsigned>(LF_FACESIZE - 1)); 299 memcpy(logFont.lfFaceName, familyName.characters(), familyLength * sizeof(UChar)); 300 logFont.lfFaceName[familyLength] = 0; 301 logFont.lfPitchAndFamily = 0; 302 303 TraitsInFamilyProcData procData(familyName); 304 EnumFontFamiliesEx(g_screenDC, &logFont, traitsInFamilyEnumProc, reinterpret_cast<LPARAM>(&procData), 0); 305 copyToVector(procData.m_traitsMasks, traitsMasks); 306} 307 308} // namespace WebCore 309