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