source: trunk/src/gui/text/qfontdatabase_win.cpp@ 605

Last change on this file since 605 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 38.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qt_windows.h"
43#include <qmath.h>
44#include <private/qapplication_p.h>
45#include "qfont_p.h"
46#include "qfontengine_p.h"
47#include "qpaintdevice.h"
48#include "qlibrary.h"
49#include "qabstractfileengine.h"
50#include "qendian.h"
51
52#ifdef Q_OS_WINCE
53# include <QTemporaryFile>
54#endif
55
56QT_BEGIN_NAMESPACE
57
58extern HDC shared_dc(); // common dc for all fonts
59
60#ifdef MAKE_TAG
61#undef MAKE_TAG
62#endif
63// GetFontData expects the tags in little endian ;(
64#define MAKE_TAG(ch1, ch2, ch3, ch4) (\
65 (((quint32)(ch4)) << 24) | \
66 (((quint32)(ch3)) << 16) | \
67 (((quint32)(ch2)) << 8) | \
68 ((quint32)(ch1)) \
69 )
70
71static HFONT stock_sysfont = 0;
72
73static bool localizedName(const QString &name)
74{
75 const QChar *c = name.unicode();
76 for(int i = 0; i < name.length(); ++i) {
77 if(c[i].unicode() >= 0x100)
78 return true;
79 }
80 return false;
81}
82
83static inline quint16 getUShort(const unsigned char *p)
84{
85 quint16 val;
86 val = *p++ << 8;
87 val |= *p;
88
89 return val;
90}
91
92static QString getEnglishName(const uchar *table, quint32 bytes)
93{
94 QString i18n_name;
95 enum {
96 NameRecordSize = 12,
97 FamilyId = 1,
98 MS_LangIdEnglish = 0x009
99 };
100
101 // get the name table
102 quint16 count;
103 quint16 string_offset;
104 const unsigned char *names;
105
106 int microsoft_id = -1;
107 int apple_id = -1;
108 int unicode_id = -1;
109
110 if(getUShort(table) != 0)
111 goto error;
112
113 count = getUShort(table+2);
114 string_offset = getUShort(table+4);
115 names = table + 6;
116
117 if(string_offset >= bytes || 6 + count*NameRecordSize > string_offset)
118 goto error;
119
120 for(int i = 0; i < count; ++i) {
121 // search for the correct name entry
122
123 quint16 platform_id = getUShort(names + i*NameRecordSize);
124 quint16 encoding_id = getUShort(names + 2 + i*NameRecordSize);
125 quint16 language_id = getUShort(names + 4 + i*NameRecordSize);
126 quint16 name_id = getUShort(names + 6 + i*NameRecordSize);
127
128 if(name_id != FamilyId)
129 continue;
130
131 enum {
132 PlatformId_Unicode = 0,
133 PlatformId_Apple = 1,
134 PlatformId_Microsoft = 3
135 };
136
137 quint16 length = getUShort(names + 8 + i*NameRecordSize);
138 quint16 offset = getUShort(names + 10 + i*NameRecordSize);
139 if(DWORD(string_offset + offset + length) >= bytes)
140 continue;
141
142 if ((platform_id == PlatformId_Microsoft
143 && (encoding_id == 0 || encoding_id == 1))
144 && (language_id & 0x3ff) == MS_LangIdEnglish
145 && microsoft_id == -1)
146 microsoft_id = i;
147 // not sure if encoding id 4 for Unicode is utf16 or ucs4...
148 else if(platform_id == PlatformId_Unicode && encoding_id < 4 && unicode_id == -1)
149 unicode_id = i;
150 else if(platform_id == PlatformId_Apple && encoding_id == 0 && language_id == 0)
151 apple_id = i;
152 }
153 {
154 bool unicode = false;
155 int id = -1;
156 if(microsoft_id != -1) {
157 id = microsoft_id;
158 unicode = true;
159 } else if(apple_id != -1) {
160 id = apple_id;
161 unicode = false;
162 } else if (unicode_id != -1) {
163 id = unicode_id;
164 unicode = true;
165 }
166 if(id != -1) {
167 quint16 length = getUShort(names + 8 + id*NameRecordSize);
168 quint16 offset = getUShort(names + 10 + id*NameRecordSize);
169 if(unicode) {
170 // utf16
171
172 length /= 2;
173 i18n_name.resize(length);
174 QChar *uc = (QChar *) i18n_name.unicode();
175 const unsigned char *string = table + string_offset + offset;
176 for(int i = 0; i < length; ++i)
177 uc[i] = getUShort(string + 2*i);
178 } else {
179 // Apple Roman
180
181 i18n_name.resize(length);
182 QChar *uc = (QChar *) i18n_name.unicode();
183 const unsigned char *string = table + string_offset + offset;
184 for(int i = 0; i < length; ++i)
185 uc[i] = QLatin1Char(string[i]);
186 }
187 }
188 }
189 error:
190 //qDebug("got i18n name of '%s' for font '%s'", i18n_name.latin1(), familyName.toLocal8Bit().data());
191 return i18n_name;
192}
193
194static QString getEnglishName(const QString &familyName)
195{
196 QString i18n_name;
197
198 HDC hdc = GetDC( 0 );
199 LOGFONT lf;
200 memset(&lf, 0, sizeof(LOGFONT));
201 memcpy(lf.lfFaceName, familyName.utf16(), qMin(LF_FACESIZE, familyName.length()) * sizeof(wchar_t));
202 lf.lfCharSet = DEFAULT_CHARSET;
203 HFONT hfont = CreateFontIndirect(&lf);
204
205 if(!hfont) {
206 ReleaseDC(0, hdc);
207 return QString();
208 }
209
210 HGDIOBJ oldobj = SelectObject( hdc, hfont );
211
212 const DWORD name_tag = MAKE_TAG( 'n', 'a', 'm', 'e' );
213
214 // get the name table
215 unsigned char *table = 0;
216
217 DWORD bytes = GetFontData( hdc, name_tag, 0, 0, 0 );
218 if ( bytes == GDI_ERROR ) {
219 // ### Unused variable
220 /* int err = GetLastError(); */
221 goto error;
222 }
223
224 table = new unsigned char[bytes];
225 GetFontData(hdc, name_tag, 0, table, bytes);
226 if ( bytes == GDI_ERROR )
227 goto error;
228
229 i18n_name = getEnglishName(table, bytes);
230error:
231 delete [] table;
232 SelectObject( hdc, oldobj );
233 DeleteObject( hfont );
234 ReleaseDC( 0, hdc );
235
236 //qDebug("got i18n name of '%s' for font '%s'", i18n_name.latin1(), familyName.toLocal8Bit().data());
237 return i18n_name;
238}
239
240static
241void addFontToDatabase(QString familyName, const QString &scriptName,
242 TEXTMETRIC *textmetric,
243 const FONTSIGNATURE *signature,
244 int type)
245{
246 const int script = -1;
247 const QString foundryName;
248 Q_UNUSED(script);
249
250 bool italic = false;
251 int weight;
252 bool fixed;
253 bool ttf;
254 bool scalable;
255 int size;
256
257// QString escript = QString::fromWCharArray(f->elfScript);
258// qDebug("script=%s", escript.latin1());
259
260 NEWTEXTMETRIC *tm = (NEWTEXTMETRIC *)textmetric;
261 fixed = !(tm->tmPitchAndFamily & TMPF_FIXED_PITCH);
262 ttf = (tm->tmPitchAndFamily & TMPF_TRUETYPE);
263 scalable = tm->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE);
264 size = scalable ? SMOOTH_SCALABLE : tm->tmHeight;
265 italic = tm->tmItalic;
266 weight = tm->tmWeight;
267
268 // the "@family" fonts are just the same as "family". Ignore them.
269 if (familyName[0] != QLatin1Char('@') && !familyName.startsWith(QLatin1String("WST_"))) {
270 QtFontStyle::Key styleKey;
271 styleKey.style = italic ? QFont::StyleItalic : QFont::StyleNormal;
272 if (weight < 400)
273 styleKey.weight = QFont::Light;
274 else if (weight < 600)
275 styleKey.weight = QFont::Normal;
276 else if (weight < 700)
277 styleKey.weight = QFont::DemiBold;
278 else if (weight < 800)
279 styleKey.weight = QFont::Bold;
280 else
281 styleKey.weight = QFont::Black;
282
283 QtFontFamily *family = privateDb()->family(familyName, true);
284
285 if(ttf && localizedName(familyName) && family->english_name.isEmpty())
286 family->english_name = getEnglishName(familyName);
287
288 QtFontFoundry *foundry = family->foundry(foundryName, true);
289 QtFontStyle *style = foundry->style(styleKey, true);
290 style->smoothScalable = scalable;
291 style->pixelSize( size, TRUE);
292
293 // add fonts windows can generate for us:
294 if (styleKey.weight <= QFont::DemiBold) {
295 QtFontStyle::Key key(styleKey);
296 key.weight = QFont::Bold;
297 QtFontStyle *style = foundry->style(key, true);
298 style->smoothScalable = scalable;
299 style->pixelSize( size, TRUE);
300 }
301 if (styleKey.style != QFont::StyleItalic) {
302 QtFontStyle::Key key(styleKey);
303 key.style = QFont::StyleItalic;
304 QtFontStyle *style = foundry->style(key, true);
305 style->smoothScalable = scalable;
306 style->pixelSize( size, TRUE);
307 }
308 if (styleKey.weight <= QFont::DemiBold && styleKey.style != QFont::StyleItalic) {
309 QtFontStyle::Key key(styleKey);
310 key.weight = QFont::Bold;
311 key.style = QFont::StyleItalic;
312 QtFontStyle *style = foundry->style(key, true);
313 style->smoothScalable = scalable;
314 style->pixelSize( size, TRUE);
315 }
316
317 family->fixedPitch = fixed;
318
319 if (!family->writingSystemCheck && type & TRUETYPE_FONTTYPE) {
320 quint32 unicodeRange[4] = {
321 signature->fsUsb[0], signature->fsUsb[1],
322 signature->fsUsb[2], signature->fsUsb[3]
323 };
324#ifdef Q_WS_WINCE
325 if (signature->fsUsb[0] == 0) {
326 // If the unicode ranges bit mask is zero then
327 // EnumFontFamiliesEx failed to determine it properly.
328 // In this case we just pretend that the font supports all languages.
329 unicodeRange[0] = 0xbfffffff; // second most significant bit must be zero
330 unicodeRange[1] = 0xffffffff;
331 unicodeRange[2] = 0xffffffff;
332 unicodeRange[3] = 0xffffffff;
333 }
334#endif
335 quint32 codePageRange[2] = {
336 signature->fsCsb[0], signature->fsCsb[1]
337 };
338 QList<QFontDatabase::WritingSystem> systems = determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
339 for (int i = 0; i < systems.count(); ++i)
340 family->writingSystems[systems.at(i)] = QtFontFamily::Supported;
341 } else if (!family->writingSystemCheck) {
342 //qDebug("family='%s' script=%s", family->name.latin1(), script.latin1());
343 if (scriptName == QLatin1String("Western")
344 || scriptName == QLatin1String("Baltic")
345 || scriptName == QLatin1String("Central European")
346 || scriptName == QLatin1String("Turkish")
347 || scriptName == QLatin1String("Vietnamese"))
348 family->writingSystems[QFontDatabase::Latin] = QtFontFamily::Supported;
349 else if (scriptName == QLatin1String("Thai"))
350 family->writingSystems[QFontDatabase::Thai] = QtFontFamily::Supported;
351 else if (scriptName == QLatin1String("Symbol")
352 || scriptName == QLatin1String("Other"))
353 family->writingSystems[QFontDatabase::Symbol] = QtFontFamily::Supported;
354 else if (scriptName == QLatin1String("OEM/Dos"))
355 family->writingSystems[QFontDatabase::Latin] = QtFontFamily::Supported;
356 else if (scriptName == QLatin1String("CHINESE_GB2312"))
357 family->writingSystems[QFontDatabase::SimplifiedChinese] = QtFontFamily::Supported;
358 else if (scriptName == QLatin1String("CHINESE_BIG5"))
359 family->writingSystems[QFontDatabase::TraditionalChinese] = QtFontFamily::Supported;
360 else if (scriptName == QLatin1String("Cyrillic"))
361 family->writingSystems[QFontDatabase::Cyrillic] = QtFontFamily::Supported;
362 else if (scriptName == QLatin1String("Hangul"))
363 family->writingSystems[QFontDatabase::Korean] = QtFontFamily::Supported;
364 else if (scriptName == QLatin1String("Hebrew"))
365 family->writingSystems[QFontDatabase::Hebrew] = QtFontFamily::Supported;
366 else if (scriptName == QLatin1String("Greek"))
367 family->writingSystems[QFontDatabase::Greek] = QtFontFamily::Supported;
368 else if (scriptName == QLatin1String("Japanese"))
369 family->writingSystems[QFontDatabase::Japanese] = QtFontFamily::Supported;
370 else if (scriptName == QLatin1String("Arabic"))
371 family->writingSystems[QFontDatabase::Arabic] = QtFontFamily::Supported;
372 }
373 }
374}
375
376static
377int CALLBACK
378storeFont(ENUMLOGFONTEX* f, NEWTEXTMETRICEX *textmetric, int type, LPARAM /*p*/)
379{
380 QString familyName = QString::fromWCharArray(f->elfLogFont.lfFaceName);
381 QString script = QString::fromWCharArray(f->elfScript);
382
383 FONTSIGNATURE signature = textmetric->ntmFontSig;
384
385 // NEWTEXTMETRICEX is a NEWTEXTMETRIC, which according to the documentation is
386 // identical to a TEXTMETRIC except for the last four members, which we don't use
387 // anyway
388 addFontToDatabase(familyName, script, (TEXTMETRIC *)textmetric, &signature, type);
389 // keep on enumerating
390 return 1;
391}
392
393static
394void populate_database(const QString& fam)
395{
396 QFontDatabasePrivate *d = privateDb();
397 if (!d)
398 return;
399
400 QtFontFamily *family = 0;
401 if(!fam.isEmpty()) {
402 family = d->family(fam);
403 if(family && family->loaded)
404 return;
405 } else if (d->count) {
406 return;
407 }
408
409 HDC dummy = GetDC(0);
410
411 LOGFONT lf;
412 lf.lfCharSet = DEFAULT_CHARSET;
413 if (fam.isNull()) {
414 lf.lfFaceName[0] = 0;
415 } else {
416 memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded
417 }
418 lf.lfPitchAndFamily = 0;
419
420 EnumFontFamiliesEx(dummy, &lf,
421 (FONTENUMPROC)storeFont, (LPARAM)privateDb(), 0);
422
423 ReleaseDC(0, dummy);
424
425 for (int i = 0; i < d->applicationFonts.count(); ++i) {
426 QFontDatabasePrivate::ApplicationFont fnt = d->applicationFonts.at(i);
427 if (!fnt.memoryFont)
428 continue;
429 for (int j = 0; j < fnt.families.count(); ++j) {
430 const QString familyName = fnt.families.at(j);
431 HDC hdc = GetDC(0);
432 LOGFONT lf;
433 memset(&lf, 0, sizeof(LOGFONT));
434 memcpy(lf.lfFaceName, familyName.utf16(), sizeof(wchar_t) * qMin(LF_FACESIZE, familyName.size()));
435 lf.lfCharSet = DEFAULT_CHARSET;
436 HFONT hfont = CreateFontIndirect(&lf);
437 HGDIOBJ oldobj = SelectObject(hdc, hfont);
438
439 TEXTMETRIC textMetrics;
440 GetTextMetrics(hdc, &textMetrics);
441
442 addFontToDatabase(familyName, QString(),
443 &textMetrics,
444 &fnt.signatures.at(j),
445 TRUETYPE_FONTTYPE);
446
447 SelectObject(hdc, oldobj);
448 DeleteObject(hfont);
449 ReleaseDC(0, hdc);
450 }
451 }
452
453 if(!fam.isEmpty()) {
454 family = d->family(fam);
455 if(family) {
456 if(!family->writingSystemCheck) {
457 }
458 family->loaded = true;
459 }
460 }
461}
462
463static void initializeDb()
464{
465 QFontDatabasePrivate *db = privateDb();
466 if (!db || db->count)
467 return;
468
469 populate_database(QString());
470
471#ifdef QFONTDATABASE_DEBUG
472 // print the database
473 for (int f = 0; f < db->count; f++) {
474 QtFontFamily *family = db->families[f];
475 qDebug(" %s: %p", qPrintable(family->name), family);
476 populate_database(family->name);
477
478#if 0
479 qDebug(" scripts supported:");
480 for (int i = 0; i < QUnicodeTables::ScriptCount; i++)
481 if(family->writingSystems[i] & QtFontFamily::Supported)
482 qDebug(" %d", i);
483 for (int fd = 0; fd < family->count; fd++) {
484 QtFontFoundry *foundry = family->foundries[fd];
485 qDebug(" %s", foundry->name.latin1());
486 for (int s = 0; s < foundry->count; s++) {
487 QtFontStyle *style = foundry->styles[s];
488 qDebug(" style: style=%d weight=%d smooth=%d", style->key.style,
489 style->key.weight, style->smoothScalable );
490 if(!style->smoothScalable) {
491 for(int i = 0; i < style->count; ++i) {
492 qDebug(" %d", style->pixelSizes[i].pixelSize);
493 }
494 }
495 }
496 }
497#endif
498 }
499#endif // QFONTDATABASE_DEBUG
500
501}
502
503static inline void load(const QString &family = QString(), int = -1)
504{
505 populate_database(family);
506}
507
508
509
510
511
512// --------------------------------------------------------------------------------------
513// font loader
514// --------------------------------------------------------------------------------------
515
516
517
518static void initFontInfo(QFontEngineWin *fe, const QFontDef &request, const QFontPrivate *fp)
519{
520 fe->fontDef = request; // most settings are equal
521
522 HDC dc = ((request.styleStrategy & QFont::PreferDevice) && fp->hdc) ? fp->hdc : shared_dc();
523 SelectObject(dc, fe->hfont);
524 wchar_t n[64];
525 GetTextFace(dc, 64, n);
526 fe->fontDef.family = QString::fromWCharArray(n);
527 fe->fontDef.fixedPitch = !(fe->tm.tmPitchAndFamily & TMPF_FIXED_PITCH);
528 if (fe->fontDef.pointSize < 0) {
529 fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / fp->dpi;
530 } else if (fe->fontDef.pixelSize == -1) {
531 fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * fp->dpi / 72.);
532 }
533}
534
535
536static const char *other_tryFonts[] = {
537 "Arial",
538 "MS UI Gothic",
539 "Gulim",
540 "SimSun",
541 "PMingLiU",
542 "Arial Unicode MS",
543 0
544};
545
546static const char *jp_tryFonts [] = {
547 "MS UI Gothic",
548 "Arial",
549 "Gulim",
550 "SimSun",
551 "PMingLiU",
552 "Arial Unicode MS",
553 0
554};
555
556static const char *ch_CN_tryFonts [] = {
557 "SimSun",
558 "Arial",
559 "PMingLiU",
560 "Gulim",
561 "MS UI Gothic",
562 "Arial Unicode MS",
563 0
564};
565
566static const char *ch_TW_tryFonts [] = {
567 "PMingLiU",
568 "Arial",
569 "SimSun",
570 "Gulim",
571 "MS UI Gothic",
572 "Arial Unicode MS",
573 0
574};
575
576static const char *kr_tryFonts[] = {
577 "Gulim",
578 "Arial",
579 "PMingLiU",
580 "SimSun",
581 "MS UI Gothic",
582 "Arial Unicode MS",
583 0
584};
585
586static const char **tryFonts = 0;
587
588
589static inline HFONT systemFont()
590{
591 if (stock_sysfont == 0)
592 stock_sysfont = (HFONT)GetStockObject(SYSTEM_FONT);
593 return stock_sysfont;
594}
595
596#if !defined(DEFAULT_GUI_FONT)
597#define DEFAULT_GUI_FONT 17
598#endif
599
600static
601QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &request, const QtFontDesc *desc,
602 const QStringList &family_list)
603{
604 LOGFONT lf;
605 memset(&lf, 0, sizeof(LOGFONT));
606
607 bool useDevice = (request.styleStrategy & QFont::PreferDevice) && fp->hdc;
608
609 HDC hdc = shared_dc();
610 QString font_name = desc->family->name;
611
612 if (useDevice) {
613 hdc = fp->hdc;
614 font_name = request.family;
615 }
616
617 bool stockFont = false;
618 bool preferClearTypeAA = false;
619
620 HFONT hfont = 0;
621
622 if (fp->rawMode) { // will choose a stock font
623 int f, deffnt = SYSTEM_FONT;
624 QString fam = desc->family->name.toLower();
625 if (fam == QLatin1String("default"))
626 f = deffnt;
627 else if (fam == QLatin1String("system"))
628 f = SYSTEM_FONT;
629#ifndef Q_WS_WINCE
630 else if (fam == QLatin1String("system_fixed"))
631 f = SYSTEM_FIXED_FONT;
632 else if (fam == QLatin1String("ansi_fixed"))
633 f = ANSI_FIXED_FONT;
634 else if (fam == QLatin1String("ansi_var"))
635 f = ANSI_VAR_FONT;
636 else if (fam == QLatin1String("device_default"))
637 f = DEVICE_DEFAULT_FONT;
638 else if (fam == QLatin1String("oem_fixed"))
639 f = OEM_FIXED_FONT;
640#endif
641 else if (fam[0] == QLatin1Char('#'))
642 f = fam.right(fam.length()-1).toInt();
643 else
644 f = deffnt;
645 hfont = (HFONT)GetStockObject(f);
646 if (!hfont) {
647 qErrnoWarning("QFontEngine::loadEngine: GetStockObject failed");
648 hfont = systemFont();
649 }
650 stockFont = true;
651 } else {
652
653 int hint = FF_DONTCARE;
654 switch (request.styleHint) {
655 case QFont::Helvetica:
656 hint = FF_SWISS;
657 break;
658 case QFont::Times:
659 hint = FF_ROMAN;
660 break;
661 case QFont::Courier:
662 hint = FF_MODERN;
663 break;
664 case QFont::OldEnglish:
665 hint = FF_DECORATIVE;
666 break;
667 case QFont::System:
668 hint = FF_MODERN;
669 break;
670 default:
671 break;
672 }
673
674 lf.lfHeight = -qRound(request.pixelSize);
675 lf.lfWidth = 0;
676 lf.lfEscapement = 0;
677 lf.lfOrientation = 0;
678 if (desc->style->key.weight == 50)
679 lf.lfWeight = FW_DONTCARE;
680 else
681 lf.lfWeight = (desc->style->key.weight*900)/99;
682 lf.lfItalic = (desc->style->key.style != QFont::StyleNormal);
683 lf.lfCharSet = DEFAULT_CHARSET;
684
685 int strat = OUT_DEFAULT_PRECIS;
686 if (request.styleStrategy & QFont::PreferBitmap) {
687 strat = OUT_RASTER_PRECIS;
688#ifndef Q_WS_WINCE
689 } else if (request.styleStrategy & QFont::PreferDevice) {
690 strat = OUT_DEVICE_PRECIS;
691 } else if (request.styleStrategy & QFont::PreferOutline) {
692 strat = OUT_OUTLINE_PRECIS;
693 } else if (request.styleStrategy & QFont::ForceOutline) {
694 strat = OUT_TT_ONLY_PRECIS;
695#endif
696 }
697
698 lf.lfOutPrecision = strat;
699
700 int qual = DEFAULT_QUALITY;
701
702 if (request.styleStrategy & QFont::PreferMatch)
703 qual = DRAFT_QUALITY;
704#ifndef Q_WS_WINCE
705 else if (request.styleStrategy & QFont::PreferQuality)
706 qual = PROOF_QUALITY;
707#endif
708
709 if (request.styleStrategy & QFont::PreferAntialias) {
710 if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP) {
711 qual = CLEARTYPE_QUALITY;
712 preferClearTypeAA = true;
713 } else {
714 qual = ANTIALIASED_QUALITY;
715 }
716 } else if (request.styleStrategy & QFont::NoAntialias) {
717 qual = NONANTIALIASED_QUALITY;
718 }
719
720 lf.lfQuality = qual;
721
722 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
723 lf.lfPitchAndFamily = DEFAULT_PITCH | hint;
724
725 QString fam = font_name;
726
727 if(fam.isEmpty())
728 fam = QLatin1String("MS Sans Serif");
729
730 if ((fam == QLatin1String("MS Sans Serif"))
731 && (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) {
732 fam = QLatin1String("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale
733 }
734 if (fam == QLatin1String("Courier") && !(request.styleStrategy & QFont::PreferBitmap))
735 fam = QLatin1String("Courier New");
736
737 memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded
738 hfont = CreateFontIndirect(&lf);
739 if (!hfont)
740 qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect failed");
741
742 stockFont = (hfont == 0);
743 bool ttf = false;
744 int avWidth = 0;
745 BOOL res;
746 HGDIOBJ oldObj = SelectObject(hdc, hfont);
747
748 TEXTMETRIC tm;
749 res = GetTextMetrics(hdc, &tm);
750 avWidth = tm.tmAveCharWidth;
751 ttf = tm.tmPitchAndFamily & TMPF_TRUETYPE;
752
753 SelectObject(hdc, oldObj);
754
755 if (hfont && (!ttf || request.stretch != 100)) {
756 DeleteObject(hfont);
757 if (!res)
758 qErrnoWarning("QFontEngine::loadEngine: GetTextMetrics failed");
759 lf.lfWidth = avWidth * request.stretch/100;
760 hfont = CreateFontIndirect(&lf);
761 if (!hfont)
762 qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect with stretch failed");
763 }
764
765#ifndef Q_WS_WINCE
766 if (hfont == 0) {
767 hfont = (HFONT)GetStockObject(ANSI_VAR_FONT);
768 stockFont = true;
769 }
770#else
771 if (hfont == 0) {
772 hfont = (HFONT)GetStockObject(SYSTEM_FONT);
773 stockFont = true;
774 }
775#endif
776
777 }
778 QFontEngineWin *few = new QFontEngineWin(font_name, hfont, stockFont, lf);
779
780 if (preferClearTypeAA)
781 few->glyphFormat = QFontEngineGlyphCache::Raster_RGBMask;
782
783 // Also check for OpenType tables when using complex scripts
784 // ### TODO: This only works for scripts that require OpenType. More generally
785 // for scripts that do not require OpenType we should just look at the list of
786 // supported writing systems in the font's OS/2 table.
787 if (scriptRequiresOpenType(script)) {
788 HB_Face hbFace = few->harfbuzzFace();
789 if (!hbFace || !hbFace->supported_scripts[script]) {
790 FM_DEBUG(" OpenType support missing for script\n");
791 delete few;
792 return 0;
793 }
794 }
795
796 QFontEngine *fe = few;
797 initFontInfo(few, request, fp);
798 if(script == QUnicodeTables::Common
799 && !(request.styleStrategy & QFont::NoFontMerging)
800 && !(desc->family->writingSystems[QFontDatabase::Symbol] & QtFontFamily::Supported)) {
801 if(!tryFonts) {
802 LANGID lid = GetUserDefaultLangID();
803 switch( lid&0xff ) {
804 case LANG_CHINESE: // Chinese (Taiwan)
805 if ( lid == 0x0804 ) // Taiwan
806 tryFonts = ch_TW_tryFonts;
807 else
808 tryFonts = ch_CN_tryFonts;
809 break;
810 case LANG_JAPANESE:
811 tryFonts = jp_tryFonts;
812 break;
813 case LANG_KOREAN:
814 tryFonts = kr_tryFonts;
815 break;
816 default:
817 tryFonts = other_tryFonts;
818 break;
819 }
820 }
821 QStringList fm = QFontDatabase().families();
822 QStringList list = family_list;
823 const char **tf = tryFonts;
824 while(tf && *tf) {
825 if(fm.contains(QLatin1String(*tf)))
826 list << QLatin1String(*tf);
827 ++tf;
828 }
829 QFontEngine *mfe = new QFontEngineMultiWin(few, list);
830 mfe->fontDef = fe->fontDef;
831 fe = mfe;
832 }
833 return fe;
834}
835
836const char *styleHint(const QFontDef &request)
837{
838 const char *stylehint = 0;
839 switch (request.styleHint) {
840 case QFont::SansSerif:
841 stylehint = "Arial";
842 break;
843 case QFont::Serif:
844 stylehint = "Times New Roman";
845 break;
846 case QFont::TypeWriter:
847 stylehint = "Courier New";
848 break;
849 default:
850 if (request.fixedPitch)
851 stylehint = "Courier New";
852 break;
853 }
854 return stylehint;
855}
856
857static QFontEngine *loadWin(const QFontPrivate *d, int script, const QFontDef &req)
858{
859 // list of families to try
860 QStringList family_list = familyList(req);
861
862 const char *stylehint = styleHint(d->request);
863 if (stylehint)
864 family_list << QLatin1String(stylehint);
865
866 // append the default fallback font for the specified script
867 // family_list << ... ; ###########
868
869 // add the default family
870 QString defaultFamily = QApplication::font().family();
871 if (! family_list.contains(defaultFamily))
872 family_list << defaultFamily;
873
874 // add QFont::defaultFamily() to the list, for compatibility with
875 // previous versions
876 family_list << QApplication::font().defaultFamily();
877
878 // null family means find the first font matching the specified script
879 family_list << QString();
880
881 QtFontDesc desc;
882 QFontEngine *fe = 0;
883 QList<int> blacklistedFamilies;
884
885 while (!fe) {
886 for (int i = 0; i < family_list.size(); ++i) {
887 QString family, foundry;
888 parseFontName(family_list.at(i), foundry, family);
889 FM_DEBUG("loadWin: >>>>>>>>>>>>>>trying to match '%s'", family.toLatin1().data());
890 QT_PREPEND_NAMESPACE(match)(script, req, family, foundry, -1, &desc, blacklistedFamilies);
891 if (desc.family)
892 break;
893 }
894 if (!desc.family)
895 break;
896 fe = loadEngine(script, d, req, &desc, family_list);
897 if (!fe)
898 blacklistedFamilies.append(desc.familyIndex);
899 }
900 return fe;
901}
902
903void QFontDatabase::load(const QFontPrivate *d, int script)
904{
905 // sanity checks
906 if (!qApp)
907 qWarning("QFontDatabase::load: Must construct QApplication first");
908 Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount);
909
910 // normalize the request to get better caching
911 QFontDef req = d->request;
912 if (req.pixelSize <= 0)
913 req.pixelSize = qreal((req.pointSize * d->dpi) / 72.);
914 if (req.pixelSize < 1)
915 req.pixelSize = 1;
916 if (req.weight == 0)
917 req.weight = QFont::Normal;
918 if (req.stretch == 0)
919 req.stretch = 100;
920
921 QFontCache::Key key(req, d->rawMode ? QUnicodeTables::Common : script, d->screen);
922 if (!d->engineData)
923 getEngineData(d, key);
924
925 // the cached engineData could have already loaded the engine we want
926 if (d->engineData->engines[script])
927 return;
928
929 QFontEngine *fe = QFontCache::instance()->findEngine(key);
930
931 // set it to the actual pointsize, so QFontInfo will do the right thing
932 if (req.pointSize < 0)
933 req.pointSize = req.pixelSize*72./d->dpi;
934
935 if (!fe) {
936 if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
937 fe = new QTestFontEngine(req.pixelSize);
938 fe->fontDef = req;
939 } else {
940 QMutexLocker locker(fontDatabaseMutex());
941 if (!privateDb()->count)
942 initializeDb();
943 fe = loadWin(d, script, req);
944 }
945 if (!fe) {
946 fe = new QFontEngineBox(req.pixelSize);
947 fe->fontDef = QFontDef();
948 }
949 }
950 d->engineData->engines[script] = fe;
951 fe->ref.ref();
952 QFontCache::instance()->insertEngine(key, fe);
953}
954
955#if !defined(FR_PRIVATE)
956#define FR_PRIVATE 0x10
957#endif
958
959typedef int (WINAPI *PtrAddFontResourceExW)(LPCWSTR, DWORD, PVOID);
960typedef HANDLE (WINAPI *PtrAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *);
961typedef BOOL (WINAPI *PtrRemoveFontResourceExW)(LPCWSTR, DWORD, PVOID);
962typedef BOOL (WINAPI *PtrRemoveFontMemResourceEx)(HANDLE);
963
964static QList<quint32> getTrueTypeFontOffsets(const uchar *fontData)
965{
966 QList<quint32> offsets;
967 const quint32 headerTag = *reinterpret_cast<const quint32 *>(fontData);
968 if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) {
969 if (headerTag != MAKE_TAG(0, 1, 0, 0)
970 && headerTag != MAKE_TAG('O', 'T', 'T', 'O')
971 && headerTag != MAKE_TAG('t', 'r', 'u', 'e')
972 && headerTag != MAKE_TAG('t', 'y', 'p', '1'))
973 return offsets;
974 offsets << 0;
975 return offsets;
976 }
977 const quint32 numFonts = qFromBigEndian<quint32>(fontData + 8);
978 for (uint i = 0; i < numFonts; ++i) {
979 offsets << qFromBigEndian<quint32>(fontData + 12 + i * 4);
980 }
981 return offsets;
982}
983
984static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag, const uchar **table, quint32 *length)
985{
986 const quint16 numTables = qFromBigEndian<quint16>(data + 4);
987 for (uint i = 0; i < numTables; ++i) {
988 const quint32 offset = 12 + 16 * i;
989 if (*reinterpret_cast<const quint32 *>(data + offset) == tag) {
990 *table = fileBegin + qFromBigEndian<quint32>(data + offset + 8);
991 *length = qFromBigEndian<quint32>(data + offset + 12);
992 return;
993 }
994 }
995 *table = 0;
996 *length = 0;
997 return;
998}
999
1000static void getFamiliesAndSignatures(const QByteArray &fontData, QFontDatabasePrivate::ApplicationFont *appFont)
1001{
1002 const uchar *data = reinterpret_cast<const uchar *>(fontData.constData());
1003
1004 QList<quint32> offsets = getTrueTypeFontOffsets(data);
1005 if (offsets.isEmpty())
1006 return;
1007
1008 for (int i = 0; i < offsets.count(); ++i) {
1009 const uchar *font = data + offsets.at(i);
1010 const uchar *table;
1011 quint32 length;
1012 getFontTable(data, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length);
1013 if (!table)
1014 continue;
1015 QString name = getEnglishName(table, length);
1016 if (name.isEmpty())
1017 continue;
1018
1019 appFont->families << name;
1020 FONTSIGNATURE signature;
1021 getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length);
1022 if (table && length >= 86) {
1023 // See also qfontdatabase_mac.cpp, offsets taken from OS/2 table in the TrueType spec
1024 signature.fsUsb[0] = qFromBigEndian<quint32>(table + 42);
1025 signature.fsUsb[1] = qFromBigEndian<quint32>(table + 46);
1026 signature.fsUsb[2] = qFromBigEndian<quint32>(table + 50);
1027 signature.fsUsb[3] = qFromBigEndian<quint32>(table + 54);
1028
1029 signature.fsCsb[0] = qFromBigEndian<quint32>(table + 78);
1030 signature.fsCsb[1] = qFromBigEndian<quint32>(table + 82);
1031 } else {
1032 memset(&signature, 0, sizeof(signature));
1033 }
1034 appFont->signatures << signature;
1035 }
1036}
1037
1038static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
1039{
1040 if(!fnt->data.isEmpty()) {
1041#ifndef Q_OS_WINCE
1042 PtrAddFontMemResourceEx ptrAddFontMemResourceEx = (PtrAddFontMemResourceEx)QLibrary::resolve(QLatin1String("gdi32"),
1043 "AddFontMemResourceEx");
1044 if (!ptrAddFontMemResourceEx)
1045 return;
1046#endif
1047 getFamiliesAndSignatures(fnt->data, fnt);
1048 if (fnt->families.isEmpty())
1049 return;
1050
1051#ifdef Q_OS_WINCE
1052 HANDLE handle = 0;
1053
1054 {
1055#ifdef QT_NO_TEMPORARYFILE
1056 wchar_t lpBuffer[MAX_PATH];
1057 GetTempPath(MAX_PATH, lpBuffer);
1058 QString s = QString::fromWCharArray(lpBuffer);
1059 QFile tempfile(s + QLatin1String("/font") + QString::number(GetTickCount()) + QLatin1String(".ttf"));
1060 if (!tempfile.open(QIODevice::ReadWrite))
1061#else
1062 QTemporaryFile tempfile(QLatin1String("XXXXXXXX.ttf"));
1063 if (!tempfile.open())
1064#endif // QT_NO_TEMPORARYFILE
1065 return;
1066 if (tempfile.write(fnt->data) == -1)
1067 return;
1068
1069#ifndef QT_NO_TEMPORARYFILE
1070 tempfile.setAutoRemove(false);
1071#endif
1072 fnt->fileName = QFileInfo(tempfile.fileName()).absoluteFilePath();
1073 }
1074
1075 if (AddFontResource((LPCWSTR)fnt->fileName.utf16()) == 0) {
1076 QFile(fnt->fileName).remove();
1077 return;
1078 }
1079#else
1080 DWORD dummy = 0;
1081 HANDLE handle = ptrAddFontMemResourceEx((void *)fnt->data.constData(), fnt->data.size(), 0,
1082 &dummy);
1083 if (handle == 0)
1084 return;
1085#endif // Q_OS_WINCE
1086
1087 fnt->handle = handle;
1088 fnt->data = QByteArray();
1089 fnt->memoryFont = true;
1090 } else {
1091 QFile f(fnt->fileName);
1092 if (!f.open(QIODevice::ReadOnly))
1093 return;
1094 QByteArray data = f.readAll();
1095 f.close();
1096 getFamiliesAndSignatures(data, fnt);
1097
1098#ifdef Q_OS_WINCE
1099 QFileInfo fileinfo(fnt->fileName);
1100 fnt->fileName = fileinfo.absoluteFilePath();
1101 if (AddFontResource((LPCWSTR)fnt->fileName.utf16()) == 0)
1102 return;
1103#else
1104 // supported from 2000 on, so no need to deal with the *A variant
1105 PtrAddFontResourceExW ptrAddFontResourceExW = (PtrAddFontResourceExW)QLibrary::resolve(QLatin1String("gdi32"),
1106 "AddFontResourceExW");
1107 if (!ptrAddFontResourceExW
1108 || ptrAddFontResourceExW((wchar_t*)fnt->fileName.utf16(), FR_PRIVATE, 0) == 0)
1109 return;
1110#endif // Q_OS_WINCE
1111
1112 fnt->memoryFont = false;
1113 }
1114}
1115
1116bool QFontDatabase::removeApplicationFont(int handle)
1117{
1118 QMutexLocker locker(fontDatabaseMutex());
1119
1120 QFontDatabasePrivate *db = privateDb();
1121 if (handle < 0 || handle >= db->applicationFonts.count())
1122 return false;
1123
1124 const QFontDatabasePrivate::ApplicationFont font = db->applicationFonts.at(handle);
1125 db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
1126 if (font.memoryFont) {
1127#ifdef Q_OS_WINCE
1128 bool removeSucceeded = RemoveFontResource((LPCWSTR)font.fileName.utf16());
1129 QFile tempfile(font.fileName);
1130 tempfile.remove();
1131 if (!removeSucceeded)
1132 return false;
1133#else
1134 PtrRemoveFontMemResourceEx ptrRemoveFontMemResourceEx = (PtrRemoveFontMemResourceEx)QLibrary::resolve(QLatin1String("gdi32"),
1135 "RemoveFontMemResourceEx");
1136 if (!ptrRemoveFontMemResourceEx
1137 || !ptrRemoveFontMemResourceEx(font.handle))
1138 return false;
1139#endif // Q_OS_WINCE
1140 } else {
1141#ifdef Q_OS_WINCE
1142 if (!RemoveFontResource((LPCWSTR)font.fileName.utf16()))
1143 return false;
1144#else
1145 PtrRemoveFontResourceExW ptrRemoveFontResourceExW = (PtrRemoveFontResourceExW)QLibrary::resolve(QLatin1String("gdi32"),
1146 "RemoveFontResourceExW");
1147 if (!ptrRemoveFontResourceExW
1148 || !ptrRemoveFontResourceExW((LPCWSTR)font.fileName.utf16(), FR_PRIVATE, 0))
1149 return false;
1150#endif // Q_OS_WINCE
1151 }
1152
1153 db->invalidate();
1154 return true;
1155}
1156
1157bool QFontDatabase::removeAllApplicationFonts()
1158{
1159 QMutexLocker locker(fontDatabaseMutex());
1160
1161 QFontDatabasePrivate *db = privateDb();
1162 for (int i = 0; i < db->applicationFonts.count(); ++i)
1163 if (!removeApplicationFont(i))
1164 return false;
1165 return true;
1166}
1167
1168bool QFontDatabase::supportsThreadedFontRendering()
1169{
1170 return true;
1171}
1172
1173QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.