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