1 /*
2 * Copyright (C) 2007-2009 Torch Mobile Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21 #include "config.h"
22 #include "FontPlatformData.h"
23
24 #include "Font.h"
25 #include "FontCache.h"
26 #include "FontData.h"
27 #include "PlatformString.h"
28 #include "SimpleFontData.h"
29 #include "UnicodeRange.h"
30 #include "wtf/OwnPtr.h"
31
32 #include <windows.h>
33 #include <mlang.h>
34
35 namespace WebCore {
36
37 extern HDC g_screenDC;
38
39 static wchar_t songTiStr[] = { 0x5b8b, 0x4f53, 0 };
40 static wchar_t heiTiStr[] = { 0x9ed1, 0x4f53, 0 };
41
42 class FontFamilyCodePageInfo {
43 public:
FontFamilyCodePageInfo()44 FontFamilyCodePageInfo()
45 : m_codePage(0), m_codePages(0)
46 {
47 }
FontFamilyCodePageInfo(const wchar_t * family,UINT codePage)48 FontFamilyCodePageInfo(const wchar_t* family, UINT codePage)
49 : m_family(family), m_codePage(codePage), m_codePages(0)
50 {
51 }
codePages() const52 DWORD codePages() const
53 {
54 if (!m_codePages) {
55 #if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2)
56 if (IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface())
57 langFontLink->CodePageToCodePages(m_codePage, &m_codePages);
58 #else
59 if (IMLangFontLink* langFontLink = fontCache()->getFontLinkInterface())
60 langFontLink->CodePageToCodePages(m_codePage, &m_codePages);
61 #endif
62 }
63 return m_codePages;
64 }
65
66 String m_family;
67 UINT m_codePage;
68 private:
69 mutable DWORD m_codePages;
70 };
71
72 class FontFamilyChecker {
73 public:
FontFamilyChecker(const wchar_t * family)74 FontFamilyChecker(const wchar_t* family)
75 : m_exists(false)
76 {
77 EnumFontFamilies(g_screenDC, family, enumFontFamProc, (LPARAM)this);
78 }
isSupported() const79 bool isSupported() const { return m_exists; }
80 private:
81 bool m_exists;
82 static int CALLBACK enumFontFamProc(const LOGFONT FAR* lpelf, const TEXTMETRIC FAR* lpntm, DWORD FontType, LPARAM lParam);
83 };
84
85 class ValidFontFamilyFinder {
86 public:
ValidFontFamilyFinder()87 ValidFontFamilyFinder()
88 {
89 EnumFontFamilies(g_screenDC, 0, enumFontFamProc, (LPARAM)this);
90 }
family() const91 const String& family() const { return m_family; }
92 private:
93 String m_family;
94 static int CALLBACK enumFontFamProc(const LOGFONT FAR* lpelf, const TEXTMETRIC FAR* lpntm, DWORD FontType, LPARAM lParam);
95 };
96
97 class FixedSizeFontData: public RefCounted<FixedSizeFontData> {
98 public:
99 LOGFONT m_font;
100 OwnPtr<HFONT> m_hfont;
101 TEXTMETRIC m_metrics;
102 DWORD m_codePages;
103 unsigned m_weight;
104 bool m_italic;
105
106 static PassRefPtr<FixedSizeFontData> create(const AtomicString& family, unsigned weight, bool italic);
107 private:
FixedSizeFontData()108 FixedSizeFontData()
109 : m_codePages(0)
110 , m_weight(0)
111 , m_italic(false)
112 {
113 memset(&m_font, 0, sizeof(m_font));
114 memset(&m_metrics, 0, sizeof(m_metrics));
115 }
116 };
117
118 struct FixedSizeFontDataKey {
FixedSizeFontDataKeyWebCore::FixedSizeFontDataKey119 FixedSizeFontDataKey(const AtomicString& family = AtomicString(), unsigned weight = 0, bool italic = false)
120 : m_family(family)
121 , m_weight(weight)
122 , m_italic(italic)
123 {
124 }
125
FixedSizeFontDataKeyWebCore::FixedSizeFontDataKey126 FixedSizeFontDataKey(WTF::HashTableDeletedValueType) : m_weight(-2) { }
isHashTableDeletedValueWebCore::FixedSizeFontDataKey127 bool isHashTableDeletedValue() const { return m_weight == -2; }
128
operator ==WebCore::FixedSizeFontDataKey129 bool operator==(const FixedSizeFontDataKey& other) const
130 {
131 return equalIgnoringCase(m_family, other.m_family)
132 && m_weight == other.m_weight
133 && m_italic == other.m_italic;
134 }
135
136 AtomicString m_family;
137 unsigned m_weight;
138 bool m_italic;
139 };
140
141 struct FixedSizeFontDataKeyHash {
hashWebCore::FixedSizeFontDataKeyHash142 static unsigned hash(const FixedSizeFontDataKey& font)
143 {
144 unsigned hashCodes[] = {
145 CaseFoldingHash::hash(font.m_family),
146 font.m_weight,
147 // static_cast<unsigned>(font.m_italic);
148 };
149 return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar));
150 }
151
equalWebCore::FixedSizeFontDataKeyHash152 static bool equal(const FixedSizeFontDataKey& a, const FixedSizeFontDataKey& b)
153 {
154 return a == b;
155 }
156
157 static const bool safeToCompareToEmptyOrDeleted = true;
158 };
159
160 struct FixedSizeFontDataKeyTraits : WTF::GenericHashTraits<FixedSizeFontDataKey> {
161 static const bool emptyValueIsZero = true;
emptyValueWebCore::FixedSizeFontDataKeyTraits162 static const FixedSizeFontDataKey& emptyValue()
163 {
164 DEFINE_STATIC_LOCAL(FixedSizeFontDataKey, key, (nullAtom));
165 return key;
166 }
constructDeletedValueWebCore::FixedSizeFontDataKeyTraits167 static void constructDeletedValue(FixedSizeFontDataKey& slot)
168 {
169 new (&slot) FixedSizeFontDataKey(WTF::HashTableDeletedValue);
170 }
isDeletedValueWebCore::FixedSizeFontDataKeyTraits171 static bool isDeletedValue(const FixedSizeFontDataKey& value)
172 {
173 return value.isHashTableDeletedValue();
174 }
175 };
176
enumFontFamProc(const LOGFONT FAR * lpelf,const TEXTMETRIC FAR * lpntm,DWORD FontType,LPARAM lParam)177 int CALLBACK FontFamilyChecker::enumFontFamProc(const LOGFONT FAR* lpelf, const TEXTMETRIC FAR* lpntm, DWORD FontType, LPARAM lParam)
178 {
179 ((FontFamilyChecker*)lParam)->m_exists = true;
180 return 0;
181 }
182
enumFontFamProc(const LOGFONT FAR * lpelf,const TEXTMETRIC FAR * lpntm,DWORD FontType,LPARAM lParam)183 int CALLBACK ValidFontFamilyFinder::enumFontFamProc(const LOGFONT FAR* lpelf, const TEXTMETRIC FAR* lpntm, DWORD FontType, LPARAM lParam)
184 {
185 if (lpelf->lfCharSet != SYMBOL_CHARSET) {
186 ((ValidFontFamilyFinder*)lParam)->m_family = String(lpelf->lfFaceName);
187 return 0;
188 }
189 return 1;
190 }
191
192 typedef Vector<FontFamilyCodePageInfo> KnownFonts;
knownFonts()193 static KnownFonts& knownFonts()
194 {
195 static KnownFonts fonts;
196 static bool firstTime = true;
197 if (firstTime) {
198 firstTime = false;
199 if (FontPlatformData::isSongTiSupported())
200 fonts.append(FontFamilyCodePageInfo(songTiStr, 936));
201 }
202 return fonts;
203 }
204
getDefaultFontFamily()205 static String getDefaultFontFamily()
206 {
207 if (FontFamilyChecker(L"Tahoma").isSupported())
208 return String(L"Tahoma");
209
210 bool good = false;
211 String family;
212 HKEY key;
213 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\GDI\\SysFnt", 0, 0, &key) == ERROR_SUCCESS) {
214 DWORD maxlen, type;
215 if (RegQueryValueEx(key, L"Nm", 0, &type, 0, &maxlen) == ERROR_SUCCESS && type == REG_SZ) {
216 ++maxlen;
217 if (wchar_t* buffer = new wchar_t[maxlen]) {
218 if (RegQueryValueEx(key, L"Nm", 0, &type, (LPBYTE)buffer, &maxlen) == ERROR_SUCCESS) {
219 family = String(buffer, maxlen);
220 good = true;
221 }
222 delete[] buffer;
223 }
224 }
225 RegCloseKey(key);
226 }
227 if (good)
228 return family;
229
230 return ValidFontFamilyFinder().family();
231 }
232
233 typedef HashMap<FixedSizeFontDataKey, RefPtr<FixedSizeFontData>, FixedSizeFontDataKeyHash, FixedSizeFontDataKeyTraits> FixedSizeFontCache;
234 FixedSizeFontCache g_fixedSizeFontCache;
235
create(const AtomicString & family,unsigned weight,bool italic)236 PassRefPtr<FixedSizeFontData> FixedSizeFontData::create(const AtomicString& family, unsigned weight, bool italic)
237 {
238 FixedSizeFontData* fontData = new FixedSizeFontData();
239
240 fontData->m_weight = weight;
241 fontData->m_italic = italic;
242
243 LOGFONT& winFont = fontData->m_font;
244 // The size here looks unusual. The negative number is intentional.
245 winFont.lfHeight = -72;
246 winFont.lfWidth = 0;
247 winFont.lfEscapement = 0;
248 winFont.lfOrientation = 0;
249 winFont.lfUnderline = false;
250 winFont.lfStrikeOut = false;
251 winFont.lfCharSet = DEFAULT_CHARSET;
252 winFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
253 winFont.lfQuality = CLEARTYPE_QUALITY; //DEFAULT_QUALITY;
254 winFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
255 winFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
256 winFont.lfItalic = italic;
257 winFont.lfWeight = FontPlatformData::adjustedGDIFontWeight(weight, family);
258
259 int len = std::min(family.length(), (unsigned int)LF_FACESIZE - 1);
260 wmemcpy(winFont.lfFaceName, family.characters(), len);
261 winFont.lfFaceName[len] = L'\0';
262
263 fontData->m_hfont.set(CreateFontIndirect(&winFont));
264
265 HGDIOBJ oldFont = SelectObject(g_screenDC, fontData->m_hfont.get());
266
267 GetTextMetrics(g_screenDC, &fontData->m_metrics);
268
269 #if defined(IMLANG_FONT_LINK) && (IMLANG_FONT_LINK == 2)
270 if (IMLangFontLink2* langFontLink = fontCache()->getFontLinkInterface()) {
271 #else
272 if (IMLangFontLink* langFontLink = fontCache()->getFontLinkInterface()) {
273 #endif
274 langFontLink->GetFontCodePages(g_screenDC, fontData->m_hfont.get(), &fontData->m_codePages);
275 fontData->m_codePages |= FontPlatformData::getKnownFontCodePages(winFont.lfFaceName);
276 }
277
278 SelectObject(g_screenDC, oldFont);
279
280 return adoptRef(fontData);
281 }
282
283 static PassRefPtr<FixedSizeFontData> createFixedSizeFontData(const AtomicString& family, unsigned weight, bool italic)
284 {
285 FixedSizeFontDataKey key(family, weight, italic);
286 pair<FixedSizeFontCache::iterator, bool> result = g_fixedSizeFontCache.add(key, RefPtr<FixedSizeFontData>());
287 if (result.second)
288 result.first->second = FixedSizeFontData::create(family, weight, italic);
289
290 return result.first->second;
291 }
292
293 static LONG toGDIFontWeight(FontWeight fontWeight)
294 {
295 static LONG gdiFontWeights[] = {
296 FW_THIN, // FontWeight100
297 FW_EXTRALIGHT, // FontWeight200
298 FW_LIGHT, // FontWeight300
299 FW_NORMAL, // FontWeight400
300 FW_MEDIUM, // FontWeight500
301 FW_SEMIBOLD, // FontWeight600
302 FW_BOLD, // FontWeight700
303 FW_EXTRABOLD, // FontWeight800
304 FW_HEAVY // FontWeight900
305 };
306 return gdiFontWeights[fontWeight];
307 }
308
309 class FontPlatformPrivateData {
310 public:
311 int m_reference;
312 RefPtr<FixedSizeFontData> m_rootFontData;
313 AtomicString m_family;
314 FontDescription m_fontDescription;
315 OwnPtr<HFONT> m_hfontScaled;
316 int m_size;
317 long m_fontScaledWidth;
318 long m_fontScaledHeight;
319 bool m_disabled;
320 FontPlatformPrivateData(int size, unsigned weight)
321 : m_reference(1)
322 , m_family(FontPlatformData::defaultFontFamily())
323 , m_size(size)
324 , m_fontScaledWidth(0)
325 , m_fontScaledHeight(0)
326 , m_disabled(false)
327 {
328 m_rootFontData = createFixedSizeFontData(m_family, weight, false);
329 }
330 FontPlatformPrivateData(const FontDescription& fontDescription, const AtomicString& family)
331 : m_reference(1)
332 , m_size(fontDescription.computedPixelSize())
333 , m_fontDescription(fontDescription)
334 , m_family(family)
335 , m_fontScaledWidth(0)
336 , m_fontScaledHeight(0)
337 , m_disabled(!fontDescription.specifiedSize())
338 {
339 m_rootFontData = FixedSizeFontData::create(family, toGDIFontWeight(fontDescription.weight()), fontDescription.italic());
340 }
341 };
342
343 FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const AtomicString& desiredFamily, bool useDefaultFontIfNotPresent)
344 {
345 String family(desiredFamily);
346 if (!equalIgnoringCase(family, defaultFontFamily()) && !FontFamilyChecker(family.charactersWithNullTermination()).isSupported()) {
347 if (equalIgnoringCase(family, String(heiTiStr)) && isSongTiSupported())
348 family = String(songTiStr);
349 else if (useDefaultFontIfNotPresent)
350 family = defaultFontFamily();
351 }
352
353 m_private = new FontPlatformPrivateData(fontDescription, family);
354 }
355
356 FontPlatformData::FontPlatformData(float size, bool bold, bool oblique)
357 {
358 if (!size)
359 m_private = 0;
360 else
361 m_private = new FontPlatformPrivateData((int)(size + 0.5), bold ? FW_BOLD : FW_NORMAL);
362 }
363
364 FontPlatformData::~FontPlatformData()
365 {
366 if (isValid() && !--m_private->m_reference) {
367 if (m_private->m_rootFontData->refCount() == 2) {
368 FixedSizeFontDataKey key(m_private->m_family, m_private->m_rootFontData->m_weight, m_private->m_rootFontData->m_italic);
369 g_fixedSizeFontCache.remove(key);
370 }
371 delete m_private;
372 }
373 }
374
375 FontPlatformData& FontPlatformData::operator=(const FontPlatformData& o)
376 {
377 if (isValid() && !--m_private->m_reference)
378 delete m_private;
379
380 if (m_private = o.m_private)
381 ++m_private->m_reference;
382
383 return *this;
384 }
385
386 HFONT FontPlatformData::hfont() const
387 {
388 if (!isValid())
389 return 0;
390
391 if (m_private->m_disabled)
392 return 0;
393
394 if (!m_private->m_rootFontData->m_hfont)
395 m_private->m_rootFontData->m_hfont.set(CreateFontIndirect(&m_private->m_rootFontData->m_font));
396
397 return m_private->m_rootFontData->m_hfont.get();
398 }
399
400 HFONT FontPlatformData::getScaledFontHandle(int height, int width) const
401 {
402 if (!isValid() || m_private->m_disabled)
403 return 0;
404
405 if (!m_private->m_hfontScaled || m_private->m_fontScaledHeight != height || m_private->m_fontScaledWidth != width) {
406 m_private->m_fontScaledHeight = height;
407 m_private->m_fontScaledWidth = width;
408 LOGFONT font = m_private->m_rootFontData->m_font;
409 font.lfHeight = -height;
410 font.lfWidth = width;
411 m_private->m_hfontScaled.set(CreateFontIndirect(&font));
412 }
413
414 return m_private->m_hfontScaled.get();
415 }
416
417 bool FontPlatformData::discardFontHandle()
418 {
419 if (!isValid())
420 return false;
421
422 if (m_private->m_rootFontData->m_hfont) {
423 m_private->m_rootFontData->m_hfont.set(0);
424 return true;
425 }
426
427 if (m_private->m_hfontScaled) {
428 m_private->m_hfontScaled.set(0);
429 return true;
430 }
431 return false;
432 }
433
434 const TEXTMETRIC& FontPlatformData::metrics() const
435 {
436 return m_private->m_rootFontData->m_metrics;
437 }
438
439 bool FontPlatformData::isSystemFont() const
440 {
441 return false;
442 }
443
444 int FontPlatformData::size() const
445 {
446 return m_private->m_size;
447 }
448
449 const FontDescription& FontPlatformData::fontDescription() const
450 {
451 return m_private->m_fontDescription;
452 }
453
454 const AtomicString& FontPlatformData::family() const
455 {
456 return m_private->m_family;
457 }
458
459 const LOGFONT& FontPlatformData::logFont() const
460 {
461 return m_private->m_rootFontData->m_font;
462 }
463
464 int FontPlatformData::averageCharWidth() const
465 {
466 return (m_private->m_rootFontData->m_metrics.tmAveCharWidth * size() + 36) / 72;
467 }
468
469 bool FontPlatformData::isDisabled() const
470 {
471 return !isValid() || m_private->m_disabled;
472 }
473
474 DWORD FontPlatformData::codePages() const
475 {
476 return m_private->m_rootFontData->m_codePages;
477 }
478
479 bool FontPlatformData::isSongTiSupported()
480 {
481 static bool exists = FontFamilyChecker(songTiStr).isSupported();
482 return exists;
483 }
484
485 bool FontPlatformData::mapKnownFont(DWORD codePages, String& family)
486 {
487 KnownFonts& fonts = knownFonts();
488 for (KnownFonts::iterator i = fonts.begin(); i != fonts.end(); ++i) {
489 if (i->codePages() & codePages) {
490 family = i->m_family;
491 return true;
492 }
493 }
494 return false;
495 }
496
497 DWORD FontPlatformData::getKnownFontCodePages(const wchar_t* family)
498 {
499 KnownFonts& fonts = knownFonts();
500 for (KnownFonts::iterator i = fonts.begin(); i != fonts.end(); ++i) {
501 if (equalIgnoringCase(i->m_family, String(family)))
502 return i->codePages();
503 }
504 return 0;
505 }
506
507 const String& FontPlatformData::defaultFontFamily()
508 {
509 static String family(getDefaultFontFamily());
510 return family;
511 }
512
513 LONG FontPlatformData::adjustedGDIFontWeight(LONG gdiFontWeight, const String& family)
514 {
515 static AtomicString lucidaStr("Lucida Grande");
516 if (equalIgnoringCase(family, lucidaStr)) {
517 if (gdiFontWeight == FW_NORMAL)
518 return FW_MEDIUM;
519 if (gdiFontWeight == FW_BOLD)
520 return FW_SEMIBOLD;
521 }
522 return gdiFontWeight;
523 }
524
525 #ifndef NDEBUG
526 String FontPlatformData::description() const
527 {
528 return String();
529 }
530 #endif
531
532 }
533