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
42 namespace WebCore {
43
44 extern HDC g_screenDC;
45
46 static IMultiLanguage *multiLanguage = 0;
47
48 #if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2)
49 static IMLangFontLink2* langFontLink = 0;
50 #else
51 static IMLangFontLink* langFontLink = 0;
52 #endif
53
getMultiLanguageInterface()54 IMultiLanguage* 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)
getFontLinkInterface()63 IMLangFontLink2* FontCache::getFontLinkInterface()
64 #else
65 IMLangFontLink* 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)
currentFontContainsCharacter(IMLangFontLink2 * langFontLink,HDC hdc,UChar character)77 static 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
currentFontContainsCharacter(IMLangFontLink * langFontLink,HDC hdc,HFONT hfont,UChar character,const wchar_t * faceName)98 static 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)
createMLangFont(IMLangFontLink2 * langFontLink,HDC hdc,DWORD codePageMask,UChar character=0)117 static 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
createMLangFont(IMLangFontLink * langFontLink,HDC hdc,const FontPlatformData & refFont,DWORD codePageMask)126 static 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
getCJKCodePageMasks()135 static 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
177 struct TraitsInFamilyProcData {
TraitsInFamilyProcDataWebCore::TraitsInFamilyProcData178 TraitsInFamilyProcData(const AtomicString& familyName)
179 : m_familyName(familyName)
180 {
181 }
182
183 const AtomicString& m_familyName;
184 HashSet<unsigned> m_traitsMasks;
185 };
186
traitsInFamilyEnumProc(CONST LOGFONT * logFont,CONST TEXTMETRIC * metrics,DWORD fontType,LPARAM lParam)187 static 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
platformInit()208 void FontCache::platformInit()
209 {
210 }
211
comInitialize()212 void FontCache::comInitialize()
213 {
214 }
215
comUninitialize()216 void 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
getFontDataForCharacters(const Font & font,const UChar * characters,int length)228 const 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
319 SimpleFontData* FontCache::getSimilarFontPlatformData(const Font& font)
320 {
321 return 0;
322 }
323
324 SimpleFontData* 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
331 FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family)
332 {
333 FontPlatformData* result = new FontPlatformData(fontDescription, family);
334 return result;
335 }
336
337 void 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