source: trunk/src/gui/text/qfontdatabase_pm.cpp@ 659

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

global: Updated year to 2010 in OS/2-specific headers.

File size: 35.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** Copyright (C) 2010 netlabs.org. OS/2 parts.
8**
9** This file is part of the QtGui module of the Qt Toolkit.
10**
11** $QT_BEGIN_LICENSE:LGPL$
12** Commercial Usage
13** Licensees holding valid Qt Commercial licenses may use this file in
14** accordance with the Qt Commercial License Agreement provided with the
15** Software or, alternatively, in accordance with the terms contained in
16** a written agreement between you and Nokia.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 2.1 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 2.1 requirements
24** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** In addition, as a special exception, Nokia gives you certain additional
27** rights. These rights are described in the Nokia Qt LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** GNU General Public License Usage
31** Alternatively, this file may be used under the terms of the GNU
32** General Public License version 3.0 as published by the Free Software
33** Foundation and appearing in the file LICENSE.GPL included in the
34** packaging of this file. Please review the following information to
35** ensure the GNU General Public License version 3.0 requirements will be
36** met: http://www.gnu.org/copyleft/gpl.html.
37**
38** If you have questions regarding the use of this file, please contact
39** Nokia at [email protected].
40** $QT_END_LICENSE$
41**
42****************************************************************************/
43
44#include "qfontengine_pm_p.h"
45
46#include "qabstractfileengine.h"
47#include "qsettings.h"
48#include "qfileinfo.h"
49#include "qdatetime.h"
50#include "qhash.h"
51#include "qtextcodec.h"
52
53#include <ft2build.h>
54#include FT_FREETYPE_H
55#include FT_TYPES_H
56#include FT_TRUETYPE_TABLES_H
57#include FT_LCD_FILTER_H
58#include FT_SFNT_NAMES_H
59#include FT_TRUETYPE_IDS_H
60
61QT_BEGIN_NAMESPACE
62
63extern FT_Library qt_getFreetype(); // qfontengine_ft.cpp
64
65struct FaceData
66{
67 QByteArray file;
68 int index;
69 QString familyName;
70 QtFontStyle::Key styleKey;
71 QList<QFontDatabase::WritingSystem> systems;
72 bool fixedPitch;
73 bool smoothScalable;
74 QList<unsigned short> pixelSizes;
75};
76
77static QDataStream &operator<<(QDataStream &data, const QFontDatabase::WritingSystem &ws)
78{
79 data << (int)ws;
80 return data;
81}
82
83static QDataStream &operator>>(QDataStream &data, QFontDatabase::WritingSystem &ws)
84{
85 data >> (int&)ws;
86 return data;
87}
88
89static QDataStream &operator<<(QDataStream &data, const FaceData &cached)
90{
91 data << cached.familyName;
92 data << cached.styleKey.style << cached.styleKey.weight
93 << cached.styleKey.stretch;
94 data << cached.systems;
95 data << cached.fixedPitch << cached.smoothScalable;
96 data << cached.pixelSizes;
97 return data;
98}
99
100static QDataStream &operator>>(QDataStream &data, FaceData &cached)
101{
102 data >> cached.familyName;
103 uint style;
104 int weight, stretch;
105 data >> style; cached.styleKey.style = style;
106 data >> weight; cached.styleKey.weight = weight;
107 data >> stretch; cached.styleKey.stretch = stretch;
108 data >> cached.systems;
109 data >> cached.fixedPitch >> cached.smoothScalable;
110 data >> cached.pixelSizes;
111 return data;
112}
113
114struct FileData
115{
116 FileData() : seen(false) {}
117 FileData(const QFileInfo &fi, bool s) : fileInfo(fi), seen(s) {}
118
119 QFileInfo fileInfo;
120 bool seen;
121};
122
123typedef QHash<QString, FileData> FontFileHash;
124static FontFileHash knownFontFiles;
125
126static bool lookupFamilyName(FT_Face ftface, QString &familyName)
127{
128 FT_UInt nNameCount;
129 FT_UInt i;
130 FT_SfntName sfntName;
131 FT_UInt found, best;
132
133 nNameCount = FT_Get_Sfnt_Name_Count(ftface);
134
135 if (nNameCount == 0)
136 return false;
137
138#ifndef QT_NO_TEXTCODEC
139 QTextCodec *codec = QTextCodec::codecForName("UTF-16BE");
140
141 // find a unicode name at first
142 if (codec) {
143 found = (FT_UInt)-1;
144 best = (FT_UInt)-1;
145
146 // try to find the unicode name matching to the locale
147 for (i = 0; found == (FT_UInt)-1 && i < nNameCount; i++) {
148 FT_Get_Sfnt_Name(ftface, i, &sfntName);
149
150 if (sfntName.name_id == TT_NAME_ID_FONT_FAMILY &&
151 sfntName.platform_id == TT_PLATFORM_MICROSOFT &&
152 sfntName.encoding_id == TT_MS_ID_UNICODE_CS) {
153 if (best == (FT_UInt)-1 || sfntName.language_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)
154 best = i;
155
156 QLocale sysLocale = QLocale::system();
157 switch (sfntName.language_id) {
158 case TT_MS_LANGID_KOREAN_EXTENDED_WANSUNG_KOREA :
159 if (sysLocale.language() == QLocale::Korean)
160 found = i;
161 break;
162
163 case TT_MS_LANGID_JAPANESE_JAPAN :
164 if (sysLocale.language() == QLocale::Japanese)
165 found = i;
166 break;
167
168 case TT_MS_LANGID_CHINESE_PRC :
169 if (sysLocale.country() == QLocale::China &&
170 sysLocale.language() == QLocale::Chinese)
171 found = i;
172 break;
173
174 case TT_MS_LANGID_CHINESE_TAIWAN :
175 if (sysLocale.country() == QLocale::Taiwan &&
176 sysLocale.language() == QLocale::Chinese)
177 found = i;
178 break;
179 }
180 }
181 }
182
183 if (found == (FT_UInt)-1)
184 found = best;
185
186 if (found != (FT_UInt)-1) {
187 FT_Get_Sfnt_Name(ftface, found, &sfntName);
188
189 familyName = codec->toUnicode((const char *)sfntName.string, sfntName.string_len);
190
191 return true;
192 }
193 }
194#endif
195
196 found = (FT_UInt)-1;
197 best = (FT_UInt)-1;
198
199 // unicode name is not available, try the NLS encoded name
200 for (i = 0; found == (FT_UInt)-1 && i < nNameCount; i++) {
201 FT_Get_Sfnt_Name(ftface, i, &sfntName);
202
203 if (sfntName.name_id == TT_NAME_ID_FONT_FAMILY &&
204 sfntName.platform_id == TT_PLATFORM_MICROSOFT) {
205 if (best == (FT_UInt)-1)
206 best = i;
207
208 QLocale sysLocale = QLocale::system();
209 switch (sfntName.encoding_id) {
210 case TT_MS_ID_WANSUNG :
211 if (sysLocale.language() == QLocale::Korean)
212 found = i;
213 break;
214
215 case TT_MS_ID_SJIS :
216 if (sysLocale.language() == QLocale::Japanese)
217 found = i;
218 break;
219
220 case TT_MS_ID_BIG_5 :
221 if (sysLocale.country() == QLocale::Taiwan &&
222 sysLocale.language() == QLocale::Chinese)
223 found = i;
224 break;
225
226 case TT_MS_ID_GB2312 :
227 if (sysLocale.country() == QLocale::China &&
228 sysLocale.language() == QLocale::Chinese)
229 found = i;
230 break;
231
232 case TT_MS_ID_SYMBOL_CS :
233 found = i;
234 break;
235 }
236 }
237 }
238
239 if (found == (FT_UInt)-1)
240 found = best;
241
242 if (found != (FT_UInt)-1)
243 {
244 char *name;
245 FT_UInt name_len = 0;
246
247 FT_Get_Sfnt_Name(ftface, found, &sfntName);
248
249 name = (char *)alloca(sfntName.string_len);
250
251 for (FT_UInt j = 0; j < sfntName.string_len; j++)
252 if (sfntName.string[j])
253 name[name_len++] = sfntName.string[j];
254
255#ifndef QT_NO_TEXTCODEC
256 switch (sfntName.encoding_id) {
257 case TT_MS_ID_WANSUNG :
258 codec = QTextCodec::codecForName("cp949");
259 break;
260
261 case TT_MS_ID_SJIS :
262 codec = QTextCodec::codecForName("SJIS");
263 break;
264
265 case TT_MS_ID_BIG_5 :
266 codec = QTextCodec::codecForName("Big5");
267 break;
268
269 case TT_MS_ID_GB2312 :
270 codec = QTextCodec::codecForName("GB2312");
271 break;
272
273 case TT_MS_ID_SYMBOL_CS :
274 default :
275 codec = NULL;
276 break;
277 }
278
279 if (codec)
280 familyName = codec->toUnicode(name, name_len);
281 else
282#endif
283 familyName = QString::fromLocal8Bit(name, name_len);
284
285 return true;
286 }
287
288 return false;
289}
290
291static QList<FaceData> readFreeTypeFont(FT_Library lib, const QByteArray &file,
292 const QByteArray *data = 0)
293{
294 QList<FaceData> faces;
295
296 FT_Long numFaces = 0;
297 FT_Face face;
298
299 bool isMemFont = file.startsWith(":qmemoryfonts/");
300 Q_ASSERT(!isMemFont || data);
301
302 FT_Error rc = isMemFont ?
303 FT_New_Memory_Face(lib, (const FT_Byte *)data->constData(),
304 data->size(), -1, &face) :
305 FT_New_Face(lib, file, -1, &face);
306
307 if (rc == 0) {
308 numFaces = face->num_faces;
309 FT_Done_Face(face);
310 } else {
311 // note: for invalid/unsupported font file, numFaces is left 0 so that
312 // this function will return an empty list
313 }
314
315 FD_DEBUG("readFreeTypeFont: Font file %s: FT error %d, has %ld faces",
316 file.constData(), (int) rc, numFaces);
317
318 // go throuhg each face
319 for (FT_Long idx = 0; idx < numFaces; ++idx) {
320 rc = isMemFont ?
321 FT_New_Memory_Face(lib, (const FT_Byte *)data->constData(),
322 data->size(), idx, &face) :
323 FT_New_Face(lib, file, idx, &face);
324 if (rc != 0)
325 continue;
326
327 FaceData faceData;
328
329 faceData.file = file;
330 faceData.index = idx;
331
332 if (!lookupFamilyName(face, faceData.familyName))
333 faceData.familyName = QString::fromLocal8Bit(face->family_name);
334
335 // familyName may contain extra spaces (at least this is true for
336 // TNR.PFB that is reported as "Times New Roman ". Trim them.
337 faceData.familyName = faceData.familyName.trimmed();
338
339 faceData.styleKey.style = face->style_flags & FT_STYLE_FLAG_ITALIC ?
340 QFont::StyleItalic : QFont::StyleNormal;
341
342 TT_OS2 *os2_table = 0;
343 if (face->face_flags & FT_FACE_FLAG_SFNT) {
344 os2_table = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
345 }
346 if (os2_table) {
347 // map weight and width values
348 if (os2_table->usWeightClass < 400)
349 faceData.styleKey.weight = QFont::Light;
350 else if (os2_table->usWeightClass < 600)
351 faceData.styleKey.weight = QFont::Normal;
352 else if (os2_table->usWeightClass < 700)
353 faceData.styleKey.weight = QFont::DemiBold;
354 else if (os2_table->usWeightClass < 800)
355 faceData.styleKey.weight = QFont::Bold;
356 else
357 faceData.styleKey.weight = QFont::Black;
358
359 switch (os2_table->usWidthClass) {
360 case 1: faceData.styleKey.stretch = QFont::UltraCondensed; break;
361 case 2: faceData.styleKey.stretch = QFont::ExtraCondensed; break;
362 case 3: faceData.styleKey.stretch = QFont::Condensed; break;
363 case 4: faceData.styleKey.stretch = QFont::SemiCondensed; break;
364 case 5: faceData.styleKey.stretch = QFont::Unstretched; break;
365 case 6: faceData.styleKey.stretch = QFont::SemiExpanded; break;
366 case 7: faceData.styleKey.stretch = QFont::Expanded; break;
367 case 8: faceData.styleKey.stretch = QFont::ExtraExpanded; break;
368 case 9: faceData.styleKey.stretch = QFont::UltraExpanded; break;
369 default: faceData.styleKey.stretch = QFont::Unstretched; break;
370 }
371
372 quint32 unicodeRange[4] = {
373 os2_table->ulUnicodeRange1, os2_table->ulUnicodeRange2,
374 os2_table->ulUnicodeRange3, os2_table->ulUnicodeRange4
375 };
376 quint32 codePageRange[2] = {
377 os2_table->ulCodePageRange1, os2_table->ulCodePageRange2
378 };
379 faceData.systems =
380 determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
381 } else {
382 // we've only got simple weight information and no stretch
383 faceData.styleKey.weight = face->style_flags & FT_STYLE_FLAG_BOLD ?
384 QFont::Bold : QFont::Normal;
385 faceData.styleKey.stretch = QFont::Unstretched;
386 }
387
388 faceData.fixedPitch = face->face_flags & FT_FACE_FLAG_FIXED_WIDTH;
389
390 faceData.smoothScalable = face->face_flags & FT_FACE_FLAG_SCALABLE;
391
392 // the font may both be scalable and contain fixed size bitmaps
393 if (face->face_flags & FT_FACE_FLAG_FIXED_SIZES) {
394 for (FT_Int i = 0; i < face->num_fixed_sizes; ++i) {
395 faceData.pixelSizes << face->available_sizes[i].height;
396 }
397 }
398
399 faces << faceData;
400
401 FT_Done_Face(face);
402 }
403
404 return faces;
405}
406
407static void populateDatabase(const QString& fam)
408{
409 QFontDatabasePrivate *db = privateDb();
410 if (!db)
411 return;
412
413 QtFontFamily *family = 0;
414 if(!fam.isEmpty()) {
415 family = db->family(fam);
416 if(family)
417 return;
418 } else if (db->count) {
419 return;
420 }
421
422 // we don't recognize foundries on OS/2, use an empty one
423 const QString foundryName;
424
425#ifdef QFONTDATABASE_DEBUG
426 QTime timer;
427 timer.start();
428#endif
429
430 QSettings fontCache(QSettings::UserScope, QLatin1String("Trolltech"));
431 fontCache.beginGroup(QLatin1String("Qt/Fonts/Cache 1.0"));
432
433 if (!db->valid) {
434 // Obtain the initial list of known font files from the font cache in
435 // the registry. This list is used as an in-process cache which speeds
436 // speed up populating by eliminating the need to access the registry
437 // each time an unknown family is requested. This list is also necessary
438 // to detect deleted font files.
439 FD_DEBUG("populateDatabase: INVALID, getting font list from the cache");
440 db->valid = true;
441 knownFontFiles.clear();
442 QStringList files = fontCache.childGroups();
443 foreach(QString file, files) {
444 file.replace(QLatin1Char('|'), QLatin1Char('/'));
445 knownFontFiles.insert(file, FileData());
446 // note that QFileInfo is empty so the file will be considered as
447 // NEW which is necessary for the font to get into the database
448 }
449 } else {
450 // reset the 'seen' flag
451 for (FontFileHash::iterator it = knownFontFiles.begin();
452 it != knownFontFiles.end(); ++it)
453 it.value().seen = false;
454 }
455
456 QList<QFileInfo> fontFiles;
457
458 // take the font files from HINI_USERPROFILE\PM_Fonts
459 ULONG bufSize = 0;
460 BOOL ok = PrfQueryProfileSize(HINI_USERPROFILE, "PM_Fonts", 0, &bufSize);
461 Q_ASSERT(ok);
462 if (ok) {
463 char *buffer = new char[bufSize + 1 /*terminating NULL*/];
464 Q_ASSERT(buffer);
465 if (buffer) {
466 ULONG bufLen = PrfQueryProfileString(HINI_USERPROFILE, "PM_Fonts", 0, 0,
467 buffer, bufSize);
468 if (bufLen) {
469 char *key = buffer;
470 while (*key) {
471 ULONG keySize = 0;
472 ok = PrfQueryProfileSize(HINI_USERPROFILE, "PM_Fonts", key,
473 &keySize);
474 if (ok) {
475 QByteArray file(keySize, 0);
476 ULONG keyLen =
477 PrfQueryProfileString(HINI_USERPROFILE, "PM_Fonts", key, 0,
478 file.data(), file.size());
479 file.truncate(keyLen - 1 /*terminating NULL*/);
480 if (!file.isEmpty()) {
481 // FreeType doesn't understand .OFM but understands .PFB
482 if (file.toUpper().endsWith(".OFM")) {
483 file.chop(4);
484 file.append(".PFB");
485 }
486 fontFiles << QFileInfo(QFile::decodeName(file));
487 }
488 }
489 key += strlen(key) + 1;
490 }
491 }
492 delete buffer;
493 }
494 }
495
496 // add the application-defined fonts (only file-based)
497 foreach(const QFontDatabasePrivate::ApplicationFont &font, db->applicationFonts) {
498 if (!font.fileName.startsWith(QLatin1String(":qmemoryfonts/")))
499 fontFiles << QFileInfo(font.fileName);
500 }
501
502 // go through each font file and check if we have a valid cahce for it
503 for (QList<QFileInfo>::iterator it = fontFiles.begin(); it != fontFiles.end();) {
504 QFileInfo fileInfo = *it;
505 QString fileName = fileInfo.canonicalFilePath().toLower();
506 if (!fileName.isEmpty()) { // file may have been deleted
507 fileInfo.setFile(fileName);
508 // check the in-process file name cache
509 FileData &cached = knownFontFiles[fileName];
510 if (cached.fileInfo.filePath().isEmpty() ||
511 cached.fileInfo.lastModified() != fileInfo.lastModified() ||
512 cached.fileInfo.size() != fileInfo.size()) {
513 // no cache entry or outdated
514 FD_DEBUG("populateDatabase: NEW/UPDATED font file %s",
515 qPrintable(fileName));
516 cached.fileInfo = fileInfo;
517 cached.seen = true;
518 // keep it in the list for further inspection
519 ++it;
520 continue;
521 } else {
522 // just set the 'seen' flag and skip this font
523 // (it's already in the database)
524 FD_DEBUG("populateDatabase: UNCHANGED font file %s",
525 qPrintable(fileName));
526 cached.seen = true;
527 }
528 }
529 // remove from the list, nothing to do with it
530 it = fontFiles.erase(it);
531 }
532
533 FT_Library lib = qt_getFreetype();
534
535 QList<FaceData> foundFaces;
536
537 // go through each new/outdated font file and get available faces
538 foreach(const QFileInfo &fileInfo, fontFiles) {
539 QString fileKey = fileInfo.canonicalFilePath().toLower();
540 QByteArray file = QFile::encodeName(fileKey);
541
542 // QSettings uses / for splitting into groups, suppress it
543 fileKey.replace(QLatin1Char('/'), QLatin1Char('|'));
544
545 QList<FaceData> cachedFaces;
546
547 // first, look up the cached data
548 fontCache.beginGroup(fileKey);
549
550 if (fontCache.value(QLatin1String("DateTime")).toDateTime() != fileInfo.lastModified() ||
551 fontCache.value(QLatin1String("Size")).toUInt() != fileInfo.size()) {
552 // the cache is outdated or doesn't exist, query the font file
553 cachedFaces = readFreeTypeFont(lib, file);
554
555 // store the data into the cache
556 fontCache.setValue(QLatin1String("DateTime"), fileInfo.lastModified());
557 fontCache.setValue(QLatin1String("Size"), fileInfo.size());
558
559 // note: for an invalid/unsupported font file, cachedFaces is empty,
560 // so only DateTime and Size will be cached indicating hat this
561 // file is not recognized
562 foreach(FaceData cached, cachedFaces) {
563 QByteArray rawData;
564 QDataStream data(&rawData, QIODevice::WriteOnly);
565 data << cached;
566
567 QString face = QString::number(cached.index);
568 fontCache.beginGroup(face);
569 fontCache.setValue(QLatin1String("Info"), rawData);
570 fontCache.endGroup();
571 }
572 } else {
573 // take the face data from the cache
574 QStringList faces = fontCache.childGroups();
575
576 FD_DEBUG("populateDatabase: Font file %s: IN CACHE, has %d faces",
577 file.constData(), faces.count());
578
579 foreach(QString face, faces) {
580 bool ok = false;
581 FaceData cached;
582 cached.file = file;
583 cached.index = face.toInt(&ok);
584 if (!ok || cached.index < 0) // not a valid index
585 continue;
586
587 fontCache.beginGroup(face);
588 QByteArray rawData =
589 fontCache.value(QLatin1String("Info")).toByteArray();
590 QDataStream data(rawData);
591 data >> cached;
592 fontCache.endGroup();
593
594 cachedFaces << cached;
595 }
596 }
597
598 foundFaces << cachedFaces;
599
600 fontCache.endGroup();
601 }
602
603 // get available faces of the application-defined fonts (memory-based)
604 foreach(const QFontDatabasePrivate::ApplicationFont &font, db->applicationFonts) {
605 if (font.fileName.startsWith(QLatin1String(":qmemoryfonts/"))) {
606 QList<FaceData> faces = readFreeTypeFont(lib, font.fileName.toLatin1(),
607 &font.data);
608 foundFaces << faces;
609 }
610 }
611
612 // go throuhg each found face and add it to the database
613 foreach(const FaceData &face, foundFaces) {
614
615 QtFontFamily *family = privateDb()->family(face.familyName, true);
616
617 // @todo is it possible that the same family is both fixed and not?
618 Q_ASSERT(!family->fixedPitch || face.fixedPitch);
619 family->fixedPitch = face.fixedPitch;
620
621 if (face.systems.isEmpty()) {
622 // it was hard or impossible to determine the actual writing system
623 // of the font (as in case of OS/2 bitmap and PFB fonts for which it is
624 // usually simply reported that they support standard/system codepages).
625 // Pretend that we support all writing systems to not miss the one.
626 //
627 // @todo find a proper way to detect actual supported scripts to make
628 // sure these fonts are not matched for scripts they don't support.
629 for (int ws = 0; ws < QFontDatabase::WritingSystemsCount; ++ws)
630 family->writingSystems[ws] = QtFontFamily::Supported;
631 } else {
632 for (int i = 0; i < face.systems.count(); ++i)
633 family->writingSystems[face.systems.at(i)] = QtFontFamily::Supported;
634 }
635
636 QtFontFoundry *foundry = family->foundry(foundryName, true);
637 QtFontStyle *style = foundry->style(face.styleKey, true);
638
639 // so far, all recognized fonts are antialiased
640 style->antialiased = true;
641
642 if (face.smoothScalable && !style->smoothScalable) {
643 // add new scalable style only if it hasn't been already added --
644 // the first one of two duplicate (in Qt terms) non-bitmap font
645 // styles wins.
646 style->smoothScalable = true;
647 QtFontSize *size =
648 style->pixelSize(SMOOTH_SCALABLE, true);
649 size->fileName = face.file;
650 size->fileIndex = face.index;
651 size->systems = face.systems;
652 }
653
654 foreach(unsigned short pixelSize, face.pixelSizes) {
655 QtFontSize *size = style->pixelSize(pixelSize, true);
656 // the first bitmap style with a given pixel and point size wins
657 if (!size->fileName.isEmpty())
658 continue;
659 size->fileName = face.file;
660 size->fileIndex = face.index;
661 size->systems = face.systems;
662 }
663 }
664
665 // go through the known file list to detect what files have been removed
666 for (FontFileHash::iterator it = knownFontFiles.begin();
667 it != knownFontFiles.end();) {
668 if (!it.value().seen) {
669 FD_DEBUG("populateDatabase: DELETED font file %s",
670 qPrintable(it.key()));
671 // remove from the both caches
672 QString fileKey = it.key();
673 fileKey.replace(QLatin1Char('/'), QLatin1Char('|'));
674 fontCache.remove(fileKey);
675 it = knownFontFiles.erase(it);
676 // @todo should we remove all references to this file from the
677 // font database? My concern is that this font may be in use by Qt
678 // and its glyphs may be still cached when file deletion happens
679 } else {
680 ++it;
681 }
682 }
683
684#ifdef QFONTDATABASE_DEBUG
685 FD_DEBUG("populateDatabase: took %d ms", timer.elapsed());
686#endif
687}
688
689static void initializeDb()
690{
691 QFontDatabasePrivate *db = privateDb();
692 if (!db || db->count)
693 return;
694
695 populateDatabase(QString());
696
697#ifdef QFONTDATABASE_DEBUG
698 // print the database
699 qDebug("initializeDb:");
700 for (int f = 0; f < db->count; f++) {
701 QtFontFamily *family = db->families[f];
702 qDebug(" %s: %p", qPrintable(family->name), family);
703 populateDatabase(family->name);
704#if 1
705 qDebug(" writing systems supported:");
706 QStringList systems;
707 for (int ws = 0; ws < QFontDatabase::WritingSystemsCount; ++ws)
708 if (family->writingSystems[ws] & QtFontFamily::Supported)
709 systems << QFontDatabase::writingSystemName((QFontDatabase::WritingSystem)ws);
710 qDebug() << " " << systems;
711 for (int fd = 0; fd < family->count; fd++) {
712 QtFontFoundry *foundry = family->foundries[fd];
713 qDebug(" %s", foundry->name.isEmpty() ? "(empty foundry)" :
714 qPrintable(foundry->name));
715 for (int s = 0; s < foundry->count; s++) {
716 QtFontStyle *style = foundry->styles[s];
717 qDebug(" style: style=%d weight=%d smooth=%d", style->key.style,
718 style->key.weight, style->smoothScalable);
719 for(int i = 0; i < style->count; ++i) {
720 if (style->pixelSizes[i].pixelSize == SMOOTH_SCALABLE)
721 qDebug(" smooth %s:%d",
722 style->pixelSizes[i].fileName.constData(),
723 style->pixelSizes[i].fileIndex);
724 else
725 qDebug(" %d px %s:%d", style->pixelSizes[i].pixelSize,
726 style->pixelSizes[i].fileName.constData(),
727 style->pixelSizes[i].fileIndex);
728 }
729 }
730 }
731#endif
732 }
733#endif // QFONTDATABASE_DEBUG
734}
735
736static inline void load(const QString &family = QString(), int = -1)
737{
738 populateDatabase(family);
739}
740
741static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
742{
743 Q_ASSERT(fnt);
744 if (!fnt)
745 return;
746
747 QByteArray file =
748 QFile::encodeName(QDir::current().absoluteFilePath(fnt->fileName));
749
750 FT_Library lib = qt_getFreetype();
751 QList<FaceData> faces = readFreeTypeFont(lib, file, &fnt->data);
752
753 QStringList families;
754
755 foreach(const FaceData &face, faces)
756 if (!families.contains(face.familyName))
757 families << face.familyName;
758
759 fnt->families = families;
760}
761
762static QFontDef fontDescToFontDef(const QFontDef &req, const QtFontDesc &desc)
763{
764 static LONG dpi = -1;
765 if (dpi == -1) {
766 // PM cannot change resolutions on the fly so cache it
767 int hps = qt_display_ps();
768 DevQueryCaps(GpiQueryDevice(hps), CAPS_HORIZONTAL_FONT_RES, 1, &dpi);
769 }
770
771 QFontDef fontDef;
772
773 fontDef.family = desc.family->name;
774
775 if (desc.size->pixelSize == SMOOTH_SCALABLE) {
776 // scalable font matched, calculate the missing size (points or pixels)
777 fontDef.pointSize = req.pointSize;
778 fontDef.pixelSize = req.pixelSize;
779 if (req.pointSize < 0) {
780 fontDef.pointSize = req.pixelSize * 72. / dpi;
781 } else if (req.pixelSize == -1) {
782 fontDef.pixelSize = qRound(req.pointSize * dpi / 72.);
783 }
784 } else {
785 // non-scalable font matched, calculate both point and pixel size
786 fontDef.pixelSize = desc.size->pixelSize;
787 fontDef.pointSize = desc.size->pixelSize * 72. / dpi;
788 }
789
790 fontDef.styleStrategy = req.styleStrategy;
791 fontDef.styleHint = req.styleHint;
792
793 fontDef.weight = desc.style->key.weight;
794 fontDef.fixedPitch = desc.family->fixedPitch;
795 fontDef.style = desc.style->key.style;
796 fontDef.stretch = desc.style->key.stretch;
797
798 return fontDef;
799}
800
801static QFontEngine *loadEngine(const QFontDef &req, const QtFontDesc &desc)
802{
803 // @todo all these fixed so far; make configurable through the Registry
804 // on per-family basis
805 QFontEnginePMFT::HintStyle hintStyle = QFontEnginePMFT::HintFull;
806 bool autoHint = true;
807 QFontEngineFT::SubpixelAntialiasingType subPixel = QFontEngineFT::Subpixel_None;
808 int lcdFilter = FT_LCD_FILTER_DEFAULT;
809 bool useEmbeddedBitmap = true;
810
811 QFontEngine::FaceId faceId;
812 faceId.filename = desc.size->fileName;
813 faceId.index = desc.size->fileIndex;
814
815 QFontEngineFT *fe = new QFontEnginePMFT(fontDescToFontDef(req, desc), faceId,
816 desc.style->antialiased, hintStyle,
817 autoHint, subPixel, lcdFilter,
818 useEmbeddedBitmap);
819 Q_ASSERT(fe);
820 if (fe && fe->invalid()) {
821 FM_DEBUG(" --> invalid!\n");
822 delete fe;
823 fe = 0;
824 }
825 return fe;
826}
827
828static QString getAssociateFont()
829{
830 static char szFontName[FACESIZE + 1] = {0,};
831
832 // already queried ?
833 if (szFontName[0] == '\0') {
834 // query the associated font
835 if (PrfQueryProfileString(HINI_USERPROFILE,
836 "PM_SystemFonts", "PM_AssociateFont", 0,
837 szFontName, sizeof(szFontName))) {
838 szFontName[FACESIZE] = '\0';
839
840 // the format of the associated font is face_name;point_size
841 // so remove ';' and later
842 for (PSZ pch = szFontName; *pch; pch++)
843 if (*pch == ';') {
844 *pch = '\0';
845 break;
846 }
847 }
848
849 if (szFontName[0] == '\0')
850 szFontName[0] = ';';
851 }
852
853 if (szFontName[0] != ';')
854 return QString::fromLocal8Bit(szFontName);
855
856 return QString::null;
857}
858
859static QFontEngine *loadPM(const QFontPrivate *d, int script, const QFontDef &req)
860{
861 // list of families to try
862 QStringList families = familyList(req);
863
864 const char *styleHint = qt_fontFamilyFromStyleHint(d->request);
865 if (styleHint)
866 families << QLatin1String(styleHint);
867
868 // add the default family
869 QString defaultFamily = QApplication::font().family();
870 if (!families.contains(defaultFamily))
871 families << defaultFamily;
872
873 // add QFont::defaultFamily() to the list, for compatibility with
874 // previous versions
875 families << QApplication::font().defaultFamily();
876
877 // add PM_AssociateFont to the list (used on DBCS systems to take the
878 // missing glyphs from)
879 QString associateFont = getAssociateFont();
880 if (!associateFont.isEmpty() && !families.contains(associateFont))
881 families << associateFont;
882
883 // null family means find the first font matching the specified script
884 families << QString();
885
886 QtFontDesc desc;
887 QFontEngine *fe = 0;
888 QList<int> blacklistedFamilies;
889
890 while (!fe) {
891 for (int i = 0; i < families.size(); ++i) {
892 QString family, foundry;
893 parseFontName(families.at(i), foundry, family);
894 FM_DEBUG("loadPM: >>>>>>>>>>>>>> trying to match '%s'", qPrintable(family));
895 QT_PREPEND_NAMESPACE(match)(script, req, family, foundry, -1, &desc, blacklistedFamilies);
896 if (desc.family)
897 break;
898 }
899 if (!desc.family)
900 break;
901 FM_DEBUG("loadPM: ============== matched '%s'", qPrintable(desc.family->name));
902 fe = loadEngine(req, desc);
903 if (!fe)
904 blacklistedFamilies.append(desc.familyIndex);
905 }
906 return fe;
907}
908
909void QFontDatabase::load(const QFontPrivate *d, int script)
910{
911 Q_ASSERT(script >= 0 && script < QUnicodeTables::ScriptCount);
912
913 // normalize the request to get better caching
914 QFontDef req = d->request;
915 if (req.pixelSize <= 0)
916 req.pixelSize = qMax(1, qRound(req.pointSize * d->dpi / 72.));
917 req.pointSize = 0;
918 if (req.weight == 0)
919 req.weight = QFont::Normal;
920 if (req.stretch == 0)
921 req.stretch = 100;
922
923 // @todo a hack to substitute "WarpSans" with "Workplace Sans". Remove this
924 // when we start supporting OS/2 bitmap fonts.
925 if (req.family == QLatin1String("WarpSans"))
926 req.family = QLatin1String("Workplace Sans");
927
928 QFontCache::Key key(req, d->rawMode ? QUnicodeTables::Common : script, d->screen);
929 if (!d->engineData)
930 getEngineData(d, key);
931
932 // the cached engineData could have already loaded the engine we want
933 if (d->engineData->engines[script])
934 return;
935
936 // set it to the actual pointsize, so QFontInfo will do the right thing
937 req.pointSize = req.pixelSize * 72. / d->dpi;
938
939 QFontEngine *fe = QFontCache::instance()->findEngine(key);
940
941 if (!fe) {
942 if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
943 fe = new QTestFontEngine(req.pixelSize);
944 fe->fontDef = req;
945 } else {
946 QMutexLocker locker(fontDatabaseMutex());
947 if (!privateDb()->count)
948 initializeDb();
949 fe = loadPM(d, script, req);
950 }
951 if (!fe) {
952 fe = new QFontEngineBox(req.pixelSize);
953 fe->fontDef = QFontDef();
954 }
955 }
956 if (fe->symbol || (d->request.styleStrategy & QFont::NoFontMerging)) {
957 for (int i = 0; i < QUnicodeTables::ScriptCount; ++i) {
958 if (!d->engineData->engines[i]) {
959 d->engineData->engines[i] = fe;
960 fe->ref.ref();
961 }
962 }
963 } else {
964 d->engineData->engines[script] = fe;
965 fe->ref.ref();
966 }
967 QFontCache::instance()->insertEngine(key, fe);
968}
969
970bool QFontDatabase::removeApplicationFont(int handle)
971{
972 // nothing special to do here; just empty the given ApplicationFont entry
973
974 QMutexLocker locker(fontDatabaseMutex());
975
976 QFontDatabasePrivate *db = privateDb();
977 if (handle < 0 || handle >= db->applicationFonts.count())
978 return false;
979
980 db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont();
981
982 db->invalidate();
983 return true;
984}
985
986bool QFontDatabase::removeAllApplicationFonts()
987{
988 // nothing special to do here; just empty all ApplicationFont entries
989
990 QMutexLocker locker(fontDatabaseMutex());
991
992 QFontDatabasePrivate *db = privateDb();
993 if (db->applicationFonts.isEmpty())
994 return false;
995
996 db->applicationFonts.clear();
997 db->invalidate();
998 return true;
999}
1000
1001bool QFontDatabase::supportsThreadedFontRendering()
1002{
1003 // qt_getFreetype() returns a global static FT_Library object but
1004 // FreeType2 docs say that each thread should use its own FT_Library
1005 // object. For this reason, we return false here to prevent apps from
1006 // rendering fonts on threads other than the main GUI thread.
1007 return false;
1008}
1009
1010QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.