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