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