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