|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qdir.h" |
|
43 #include "qscreen_qws.h" //so we can check for rotation |
|
44 #include "qwindowsystem_qws.h" |
|
45 #include "qlibraryinfo.h" |
|
46 #include "qabstractfileengine.h" |
|
47 #include <QtCore/qsettings.h> |
|
48 #if !defined(QT_NO_FREETYPE) |
|
49 #include "qfontengine_ft_p.h" |
|
50 |
|
51 #include <ft2build.h> |
|
52 #include FT_FREETYPE_H |
|
53 |
|
54 #endif |
|
55 #include "qfontengine_qpf_p.h" |
|
56 #include "private/qfactoryloader_p.h" |
|
57 #include "private/qcore_unix_p.h" // overrides QT_OPEN |
|
58 #include "qabstractfontengine_qws.h" |
|
59 #include "qabstractfontengine_p.h" |
|
60 #include <qdatetime.h> |
|
61 #include "qplatformdefs.h" |
|
62 |
|
63 // for mmap |
|
64 #include <stdlib.h> |
|
65 #include <unistd.h> |
|
66 #include <sys/types.h> |
|
67 #include <sys/stat.h> |
|
68 #include <sys/mman.h> |
|
69 #include <fcntl.h> |
|
70 #include <errno.h> |
|
71 |
|
72 #ifdef QT_FONTS_ARE_RESOURCES |
|
73 #include <qresource.h> |
|
74 #endif |
|
75 |
|
76 QT_BEGIN_NAMESPACE |
|
77 |
|
78 #ifndef QT_NO_LIBRARY |
|
79 Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, |
|
80 (QFontEngineFactoryInterface_iid, QLatin1String("/fontengines"), Qt::CaseInsensitive)) |
|
81 #endif |
|
82 |
|
83 const quint8 DatabaseVersion = 4; |
|
84 |
|
85 // QFontDatabasePrivate::addFont() went into qfontdatabase.cpp |
|
86 |
|
87 #ifndef QT_NO_QWS_QPF2 |
|
88 void QFontDatabasePrivate::addQPF2File(const QByteArray &file) |
|
89 { |
|
90 #ifndef QT_FONTS_ARE_RESOURCES |
|
91 struct stat st; |
|
92 if (stat(file.constData(), &st)) |
|
93 return; |
|
94 int f = QT_OPEN(file, O_RDONLY, 0); |
|
95 if (f < 0) |
|
96 return; |
|
97 const uchar *data = (const uchar *)mmap(0, st.st_size, PROT_READ, MAP_SHARED, f, 0); |
|
98 const int dataSize = st.st_size; |
|
99 #else |
|
100 QResource res(QLatin1String(file.constData())); |
|
101 const uchar *data = res.data(); |
|
102 const int dataSize = res.size(); |
|
103 //qDebug() << "addQPF2File" << file << data; |
|
104 #endif |
|
105 if (data && data != (const uchar *)MAP_FAILED) { |
|
106 if (QFontEngineQPF::verifyHeader(data, dataSize)) { |
|
107 QString fontName = QFontEngineQPF::extractHeaderField(data, QFontEngineQPF::Tag_FontName).toString(); |
|
108 int pixelSize = QFontEngineQPF::extractHeaderField(data, QFontEngineQPF::Tag_PixelSize).toInt(); |
|
109 QVariant weight = QFontEngineQPF::extractHeaderField(data, QFontEngineQPF::Tag_Weight); |
|
110 QVariant style = QFontEngineQPF::extractHeaderField(data, QFontEngineQPF::Tag_Style); |
|
111 QByteArray writingSystemBits = QFontEngineQPF::extractHeaderField(data, QFontEngineQPF::Tag_WritingSystems).toByteArray(); |
|
112 |
|
113 if (!fontName.isEmpty() && pixelSize) { |
|
114 int fontWeight = 50; |
|
115 if (weight.type() == QVariant::Int || weight.type() == QVariant::UInt) |
|
116 fontWeight = weight.toInt(); |
|
117 |
|
118 bool italic = static_cast<QFont::Style>(style.toInt()) & QFont::StyleItalic; |
|
119 |
|
120 QList<QFontDatabase::WritingSystem> writingSystems; |
|
121 for (int i = 0; i < writingSystemBits.count(); ++i) { |
|
122 uchar currentByte = writingSystemBits.at(i); |
|
123 for (int j = 0; j < 8; ++j) { |
|
124 if (currentByte & 1) |
|
125 writingSystems << QFontDatabase::WritingSystem(i * 8 + j); |
|
126 currentByte >>= 1; |
|
127 } |
|
128 } |
|
129 |
|
130 addFont(fontName, /*foundry*/ "prerendered", fontWeight, italic, |
|
131 pixelSize, file, /*fileIndex*/ 0, |
|
132 /*antialiased*/ true, writingSystems); |
|
133 } |
|
134 } else { |
|
135 qDebug() << "header verification of QPF2 font" << file << "failed. maybe it is corrupt?"; |
|
136 } |
|
137 #ifndef QT_FONTS_ARE_RESOURCES |
|
138 munmap((void *)data, st.st_size); |
|
139 #endif |
|
140 } |
|
141 #ifndef QT_FONTS_ARE_RESOURCES |
|
142 QT_CLOSE(f); |
|
143 #endif |
|
144 } |
|
145 #endif // QT_NO_QWS_QPF2 |
|
146 |
|
147 // QFontDatabasePrivate::addTTFile() went into qfontdatabase.cpp |
|
148 |
|
149 static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt); |
|
150 |
|
151 extern QString qws_fontCacheDir(); |
|
152 |
|
153 #ifndef QT_FONTS_ARE_RESOURCES |
|
154 bool QFontDatabasePrivate::loadFromCache(const QString &fontPath) |
|
155 { |
|
156 const bool weAreTheServer = QWSServer::instance(); |
|
157 |
|
158 QString fontDirFile = fontPath + QLatin1String("/fontdir"); |
|
159 |
|
160 QFile binaryDb(qws_fontCacheDir() + QLatin1String("/fontdb")); |
|
161 |
|
162 if (weAreTheServer) { |
|
163 QDateTime dbTimeStamp = QFileInfo(binaryDb.fileName()).lastModified(); |
|
164 |
|
165 QDateTime fontPathTimeStamp = QFileInfo(fontPath).lastModified(); |
|
166 if (dbTimeStamp < fontPathTimeStamp) |
|
167 return false; // let the caller create the cache |
|
168 |
|
169 if (QFile::exists(fontDirFile)) { |
|
170 QDateTime fontDirTimeStamp = QFileInfo(fontDirFile).lastModified(); |
|
171 if (dbTimeStamp < fontDirTimeStamp) |
|
172 return false; |
|
173 } |
|
174 } |
|
175 |
|
176 if (!binaryDb.open(QIODevice::ReadOnly)) { |
|
177 if (weAreTheServer) |
|
178 return false; // let the caller create the cache |
|
179 qFatal("QFontDatabase::loadFromCache: Could not open font database cache!"); |
|
180 } |
|
181 |
|
182 QDataStream stream(&binaryDb); |
|
183 quint8 version = 0; |
|
184 quint8 dataStreamVersion = 0; |
|
185 stream >> version >> dataStreamVersion; |
|
186 if (version != DatabaseVersion || dataStreamVersion != stream.version()) { |
|
187 if (weAreTheServer) |
|
188 return false; // let the caller create the cache |
|
189 qFatal("QFontDatabase::loadFromCache: Wrong version of the font database cache detected. Found %d/%d expected %d/%d", |
|
190 version, dataStreamVersion, DatabaseVersion, stream.version()); |
|
191 } |
|
192 |
|
193 QString originalFontPath; |
|
194 stream >> originalFontPath; |
|
195 if (originalFontPath != fontPath) { |
|
196 if (weAreTheServer) |
|
197 return false; // let the caller create the cache |
|
198 qFatal("QFontDatabase::loadFromCache: Font path doesn't match. Found %s in database, expected %s", qPrintable(originalFontPath), qPrintable(fontPath)); |
|
199 } |
|
200 |
|
201 QString familyname; |
|
202 stream >> familyname; |
|
203 //qDebug() << "populating database from" << binaryDb.fileName(); |
|
204 while (!familyname.isEmpty() && !stream.atEnd()) { |
|
205 QString foundryname; |
|
206 int weight; |
|
207 quint8 italic; |
|
208 int pixelSize; |
|
209 QByteArray file; |
|
210 int fileIndex; |
|
211 quint8 antialiased; |
|
212 quint8 writingSystemCount; |
|
213 |
|
214 QList<QFontDatabase::WritingSystem> writingSystems; |
|
215 |
|
216 stream >> foundryname >> weight >> italic >> pixelSize |
|
217 >> file >> fileIndex >> antialiased >> writingSystemCount; |
|
218 |
|
219 for (quint8 i = 0; i < writingSystemCount; ++i) { |
|
220 quint8 ws; |
|
221 stream >> ws; |
|
222 writingSystems.append(QFontDatabase::WritingSystem(ws)); |
|
223 } |
|
224 |
|
225 addFont(familyname, foundryname.toLatin1().constData(), weight, italic, pixelSize, file, fileIndex, antialiased, |
|
226 writingSystems); |
|
227 |
|
228 stream >> familyname; |
|
229 } |
|
230 |
|
231 stream >> fallbackFamilies; |
|
232 //qDebug() << "fallback families from cache:" << fallbackFamilies; |
|
233 return true; |
|
234 } |
|
235 #endif // QT_FONTS_ARE_RESOURCES |
|
236 |
|
237 /*! |
|
238 \internal |
|
239 */ |
|
240 |
|
241 static QString qwsFontPath() |
|
242 { |
|
243 QString fontpath = QString::fromLocal8Bit(qgetenv("QT_QWS_FONTDIR")); |
|
244 if (fontpath.isEmpty()) { |
|
245 #ifdef QT_FONTS_ARE_RESOURCES |
|
246 fontpath = QLatin1String(":/qt/fonts"); |
|
247 #else |
|
248 #ifndef QT_NO_SETTINGS |
|
249 fontpath = QLibraryInfo::location(QLibraryInfo::LibrariesPath); |
|
250 fontpath += QLatin1String("/fonts"); |
|
251 #else |
|
252 fontpath = QLatin1String("/lib/fonts"); |
|
253 #endif |
|
254 #endif //QT_FONTS_ARE_RESOURCES |
|
255 } |
|
256 |
|
257 return fontpath; |
|
258 } |
|
259 |
|
260 #if defined(QFONTDATABASE_DEBUG) && defined(QT_FONTS_ARE_RESOURCES) |
|
261 class FriendlyResource : public QResource |
|
262 { |
|
263 public: |
|
264 bool isDir () const { return QResource::isDir(); } |
|
265 bool isFile () const { return QResource::isFile(); } |
|
266 QStringList children () const { return QResource::children(); } |
|
267 }; |
|
268 #endif |
|
269 /*! |
|
270 \internal |
|
271 */ |
|
272 static void initializeDb() |
|
273 { |
|
274 QFontDatabasePrivate *db = privateDb(); |
|
275 if (!db || db->count) |
|
276 return; |
|
277 |
|
278 QString fontpath = qwsFontPath(); |
|
279 #ifndef QT_FONTS_ARE_RESOURCES |
|
280 QString fontDirFile = fontpath + QLatin1String("/fontdir"); |
|
281 |
|
282 if(!QFile::exists(fontpath)) { |
|
283 qFatal("QFontDatabase: Cannot find font directory %s - is Qt installed correctly?", |
|
284 fontpath.toLocal8Bit().constData()); |
|
285 } |
|
286 |
|
287 const bool loaded = db->loadFromCache(fontpath); |
|
288 |
|
289 if (db->reregisterAppFonts) { |
|
290 db->reregisterAppFonts = false; |
|
291 for (int i = 0; i < db->applicationFonts.count(); ++i) |
|
292 if (!db->applicationFonts.at(i).families.isEmpty()) { |
|
293 registerFont(&db->applicationFonts[i]); |
|
294 } |
|
295 } |
|
296 |
|
297 if (loaded) |
|
298 return; |
|
299 |
|
300 QString dbFileName = qws_fontCacheDir() + QLatin1String("/fontdb"); |
|
301 |
|
302 QFile binaryDb(dbFileName + QLatin1String(".tmp")); |
|
303 binaryDb.open(QIODevice::WriteOnly | QIODevice::Truncate); |
|
304 db->stream = new QDataStream(&binaryDb); |
|
305 *db->stream << DatabaseVersion << quint8(db->stream->version()) << fontpath; |
|
306 // qDebug() << "creating binary database at" << binaryDb.fileName(); |
|
307 |
|
308 // Load in font definition file |
|
309 FILE* fontdef=fopen(fontDirFile.toLocal8Bit().constData(),"r"); |
|
310 if (fontdef) { |
|
311 char buf[200]=""; |
|
312 char name[200]=""; |
|
313 char render[200]=""; |
|
314 char file[200]=""; |
|
315 char isitalic[10]=""; |
|
316 char flags[10]=""; |
|
317 do { |
|
318 fgets(buf,200,fontdef); |
|
319 if (buf[0] != '#') { |
|
320 int weight=50; |
|
321 int size=0; |
|
322 sscanf(buf,"%s %s %s %s %d %d %s",name,file,render,isitalic,&weight,&size,flags); |
|
323 QString filename; |
|
324 if (file[0] != '/') |
|
325 filename.append(fontpath).append(QLatin1Char('/')); |
|
326 filename += QLatin1String(file); |
|
327 bool italic = isitalic[0] == 'y'; |
|
328 bool smooth = QByteArray(flags).contains('s'); |
|
329 if (file[0] && QFile::exists(filename)) |
|
330 db->addFont(QString::fromUtf8(name), /*foundry*/"", weight, italic, size/10, QFile::encodeName(filename), /*fileIndex*/ 0, smooth); |
|
331 } |
|
332 } while (!feof(fontdef)); |
|
333 fclose(fontdef); |
|
334 } |
|
335 |
|
336 |
|
337 QDir dir(fontpath, QLatin1String("*.qpf")); |
|
338 for (int i=0; i<int(dir.count()); i++) { |
|
339 int u0 = dir[i].indexOf(QLatin1Char('_')); |
|
340 int u1 = dir[i].indexOf(QLatin1Char('_'), u0+1); |
|
341 int u2 = dir[i].indexOf(QLatin1Char('_'), u1+1); |
|
342 int u3 = dir[i].indexOf(QLatin1Char('.'), u1+1); |
|
343 if (u2 < 0) u2 = u3; |
|
344 |
|
345 QString familyname = dir[i].left(u0); |
|
346 int pixelSize = dir[i].mid(u0+1,u1-u0-1).toInt()/10; |
|
347 bool italic = dir[i].mid(u2-1,1) == QLatin1String("i"); |
|
348 int weight = dir[i].mid(u1+1,u2-u1-1-(italic?1:0)).toInt(); |
|
349 |
|
350 db->addFont(familyname, /*foundry*/ "qt", weight, italic, pixelSize, QFile::encodeName(dir.absoluteFilePath(dir[i])), |
|
351 /*fileIndex*/ 0, /*antialiased*/ true); |
|
352 } |
|
353 |
|
354 #ifndef QT_NO_FREETYPE |
|
355 dir.setNameFilters(QStringList() << QLatin1String("*.ttf") |
|
356 << QLatin1String("*.ttc") << QLatin1String("*.pfa") |
|
357 << QLatin1String("*.pfb")); |
|
358 dir.refresh(); |
|
359 for (int i = 0; i < int(dir.count()); ++i) { |
|
360 const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); |
|
361 // qDebug() << "looking at" << file; |
|
362 db->addTTFile(file); |
|
363 } |
|
364 #endif |
|
365 |
|
366 #ifndef QT_NO_QWS_QPF2 |
|
367 dir.setNameFilters(QStringList() << QLatin1String("*.qpf2")); |
|
368 dir.refresh(); |
|
369 for (int i = 0; i < int(dir.count()); ++i) { |
|
370 const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); |
|
371 // qDebug() << "looking at" << file; |
|
372 db->addQPF2File(file); |
|
373 } |
|
374 #endif |
|
375 |
|
376 #else //QT_FONTS_ARE_RESOURCES |
|
377 #ifdef QFONTDATABASE_DEBUG |
|
378 { |
|
379 QResource fontdir(fontpath); |
|
380 FriendlyResource *fr = static_cast<FriendlyResource*>(&fontdir); |
|
381 qDebug() << "fontdir" << fr->isValid() << fr->isDir() << fr->children(); |
|
382 |
|
383 } |
|
384 #endif |
|
385 QDir dir(fontpath, QLatin1String("*.qpf2")); |
|
386 for (int i = 0; i < int(dir.count()); ++i) { |
|
387 const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); |
|
388 //qDebug() << "looking at" << file; |
|
389 db->addQPF2File(file); |
|
390 } |
|
391 #endif //QT_FONTS_ARE_RESOURCES |
|
392 |
|
393 |
|
394 #ifdef QFONTDATABASE_DEBUG |
|
395 // print the database |
|
396 for (int f = 0; f < db->count; f++) { |
|
397 QtFontFamily *family = db->families[f]; |
|
398 FD_DEBUG("'%s' %s", qPrintable(family->name), (family->fixedPitch ? "fixed" : "")); |
|
399 #if 0 |
|
400 for (int i = 0; i < QFont::LastPrivateScript; ++i) { |
|
401 FD_DEBUG("\t%s: %s", qPrintable(QFontDatabase::scriptName((QFont::Script) i)), |
|
402 ((family->scripts[i] & QtFontFamily::Supported) ? "Supported" : |
|
403 (family->scripts[i] & QtFontFamily::UnSupported) == QtFontFamily::UnSupported ? |
|
404 "UnSupported" : "Unknown")); |
|
405 } |
|
406 #endif |
|
407 |
|
408 for (int fd = 0; fd < family->count; fd++) { |
|
409 QtFontFoundry *foundry = family->foundries[fd]; |
|
410 FD_DEBUG("\t\t'%s'", qPrintable(foundry->name)); |
|
411 for (int s = 0; s < foundry->count; s++) { |
|
412 QtFontStyle *style = foundry->styles[s]; |
|
413 FD_DEBUG("\t\t\tstyle: style=%d weight=%d\n" |
|
414 "\t\t\tstretch=%d", |
|
415 style->key.style, style->key.weight, |
|
416 style->key.stretch); |
|
417 if (style->smoothScalable) |
|
418 FD_DEBUG("\t\t\t\tsmooth scalable"); |
|
419 else if (style->bitmapScalable) |
|
420 FD_DEBUG("\t\t\t\tbitmap scalable"); |
|
421 if (style->pixelSizes) { |
|
422 FD_DEBUG("\t\t\t\t%d pixel sizes", style->count); |
|
423 for (int z = 0; z < style->count; ++z) { |
|
424 QtFontSize *size = style->pixelSizes + z; |
|
425 FD_DEBUG("\t\t\t\t size %5d", |
|
426 size->pixelSize); |
|
427 } |
|
428 } |
|
429 } |
|
430 } |
|
431 } |
|
432 #endif // QFONTDATABASE_DEBUG |
|
433 |
|
434 #ifndef QT_NO_LIBRARY |
|
435 QStringList pluginFoundries = loader()->keys(); |
|
436 // qDebug() << "plugin foundries:" << pluginFoundries; |
|
437 for (int i = 0; i < pluginFoundries.count(); ++i) { |
|
438 const QString foundry(pluginFoundries.at(i)); |
|
439 |
|
440 QFontEngineFactoryInterface *factory = qobject_cast<QFontEngineFactoryInterface *>(loader()->instance(foundry)); |
|
441 if (!factory) { |
|
442 qDebug() << "Could not load plugin for foundry" << foundry; |
|
443 continue; |
|
444 } |
|
445 |
|
446 QList<QFontEngineInfo> fonts = factory->availableFontEngines(); |
|
447 for (int i = 0; i < fonts.count(); ++i) { |
|
448 QFontEngineInfo info = fonts.at(i); |
|
449 |
|
450 int weight = info.weight(); |
|
451 if (weight <= 0) |
|
452 weight = QFont::Normal; |
|
453 |
|
454 db->addFont(info.family(), foundry.toLatin1().constData(), |
|
455 weight, info.style() != QFont::StyleNormal, |
|
456 qRound(info.pixelSize()), /*file*/QByteArray(), |
|
457 /*fileIndex*/0, /*antiAliased*/true, |
|
458 info.writingSystems()); |
|
459 } |
|
460 } |
|
461 #endif |
|
462 |
|
463 #ifndef QT_FONTS_ARE_RESOURCES |
|
464 // the empty string/familyname signifies the end of the font list. |
|
465 *db->stream << QString(); |
|
466 #endif |
|
467 { |
|
468 bool coveredWritingSystems[QFontDatabase::WritingSystemsCount] = { 0 }; |
|
469 |
|
470 db->fallbackFamilies.clear(); |
|
471 |
|
472 for (int i = 0; i < db->count; ++i) { |
|
473 QtFontFamily *family = db->families[i]; |
|
474 bool add = false; |
|
475 if (family->count == 0) |
|
476 continue; |
|
477 if (family->bogusWritingSystems) |
|
478 continue; |
|
479 for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) { |
|
480 if (coveredWritingSystems[ws]) |
|
481 continue; |
|
482 if (family->writingSystems[ws] & QtFontFamily::Supported) { |
|
483 coveredWritingSystems[ws] = true; |
|
484 add = true; |
|
485 } |
|
486 } |
|
487 if (add) |
|
488 db->fallbackFamilies << family->name; |
|
489 } |
|
490 //qDebug() << "fallbacks on the server:" << db->fallbackFamilies; |
|
491 #ifndef QT_FONTS_ARE_RESOURCES |
|
492 *db->stream << db->fallbackFamilies; |
|
493 #endif |
|
494 } |
|
495 #ifndef QT_FONTS_ARE_RESOURCES |
|
496 delete db->stream; |
|
497 db->stream = 0; |
|
498 QFile::remove(dbFileName); |
|
499 binaryDb.rename(dbFileName); |
|
500 #endif |
|
501 } |
|
502 |
|
503 // called from qwindowsystem_qws.cpp |
|
504 void qt_qws_init_fontdb() |
|
505 { |
|
506 initializeDb(); |
|
507 } |
|
508 |
|
509 #ifndef QT_NO_SETTINGS |
|
510 // called from qapplication_qws.cpp |
|
511 void qt_applyFontDatabaseSettings(const QSettings &settings) |
|
512 { |
|
513 initializeDb(); |
|
514 QFontDatabasePrivate *db = privateDb(); |
|
515 for (int i = 0; i < db->count; ++i) { |
|
516 QtFontFamily *family = db->families[i]; |
|
517 if (settings.contains(family->name)) |
|
518 family->fallbackFamilies = settings.value(family->name).toStringList(); |
|
519 } |
|
520 |
|
521 if (settings.contains(QLatin1String("Global Fallbacks"))) |
|
522 db->fallbackFamilies = settings.value(QLatin1String("Global Fallbacks")).toStringList(); |
|
523 } |
|
524 #endif // QT_NO_SETTINGS |
|
525 |
|
526 static inline void load(const QString & = QString(), int = -1) |
|
527 { |
|
528 initializeDb(); |
|
529 } |
|
530 |
|
531 #ifndef QT_NO_FREETYPE |
|
532 |
|
533 #if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20105 |
|
534 #define X_SIZE(face,i) ((face)->available_sizes[i].x_ppem) |
|
535 #define Y_SIZE(face,i) ((face)->available_sizes[i].y_ppem) |
|
536 #else |
|
537 #define X_SIZE(face,i) ((face)->available_sizes[i].width << 6) |
|
538 #define Y_SIZE(face,i) ((face)->available_sizes[i].height << 6) |
|
539 #endif |
|
540 |
|
541 #endif // QT_NO_FREETYPE |
|
542 |
|
543 static |
|
544 QFontEngine *loadSingleEngine(int script, const QFontPrivate *fp, |
|
545 const QFontDef &request, |
|
546 QtFontFamily *family, QtFontFoundry *foundry, |
|
547 QtFontStyle *style, QtFontSize *size) |
|
548 { |
|
549 Q_UNUSED(script); |
|
550 Q_UNUSED(fp); |
|
551 #ifdef QT_NO_FREETYPE |
|
552 Q_UNUSED(foundry); |
|
553 #endif |
|
554 #ifdef QT_NO_QWS_QPF |
|
555 Q_UNUSED(family); |
|
556 #endif |
|
557 Q_ASSERT(size); |
|
558 |
|
559 int pixelSize = size->pixelSize; |
|
560 if (!pixelSize || (style->smoothScalable && pixelSize == SMOOTH_SCALABLE)) |
|
561 pixelSize = request.pixelSize; |
|
562 |
|
563 #ifndef QT_NO_QWS_QPF2 |
|
564 if (foundry->name == QLatin1String("prerendered")) { |
|
565 #ifdef QT_FONTS_ARE_RESOURCES |
|
566 QResource res(QLatin1String(size->fileName.constData())); |
|
567 if (res.isValid()) { |
|
568 QFontEngineQPF *fe = new QFontEngineQPF(request, res.data(), res.size()); |
|
569 if (fe->isValid()) |
|
570 return fe; |
|
571 delete fe; |
|
572 qDebug() << "fontengine is not valid! " << size->fileName; |
|
573 } else { |
|
574 qDebug() << "Resource not valid" << size->fileName; |
|
575 } |
|
576 #else |
|
577 int f = ::open(size->fileName, O_RDONLY, 0); |
|
578 if (f >= 0) { |
|
579 QFontEngineQPF *fe = new QFontEngineQPF(request, f); |
|
580 if (fe->isValid()) |
|
581 return fe; |
|
582 delete fe; // will close f |
|
583 qDebug() << "fontengine is not valid!"; |
|
584 } |
|
585 #endif |
|
586 } else |
|
587 #endif |
|
588 if ( foundry->name != QLatin1String("qt") ) { ///#### is this the best way???? |
|
589 QString file = QFile::decodeName(size->fileName); |
|
590 |
|
591 QFontDef def = request; |
|
592 def.pixelSize = pixelSize; |
|
593 |
|
594 #ifdef QT_NO_QWS_SHARE_FONTS |
|
595 bool shareFonts = false; |
|
596 #else |
|
597 static bool dontShareFonts = !qgetenv("QWS_NO_SHARE_FONTS").isEmpty(); |
|
598 bool shareFonts = !dontShareFonts; |
|
599 #endif |
|
600 |
|
601 QScopedPointer<QFontEngine> engine; |
|
602 |
|
603 #ifndef QT_NO_LIBRARY |
|
604 QFontEngineFactoryInterface *factory = qobject_cast<QFontEngineFactoryInterface *>(loader()->instance(foundry->name)); |
|
605 if (factory) { |
|
606 QFontEngineInfo info; |
|
607 info.setFamily(request.family); |
|
608 info.setPixelSize(request.pixelSize); |
|
609 info.setStyle(QFont::Style(request.style)); |
|
610 info.setWeight(request.weight); |
|
611 // #### antialiased |
|
612 |
|
613 QAbstractFontEngine *customEngine = factory->create(info); |
|
614 if (customEngine) { |
|
615 engine.reset(new QProxyFontEngine(customEngine, def)); |
|
616 |
|
617 if (shareFonts) { |
|
618 QVariant hint = customEngine->fontProperty(QAbstractFontEngine::CacheGlyphsHint); |
|
619 if (hint.isValid()) |
|
620 shareFonts = hint.toBool(); |
|
621 else |
|
622 shareFonts = (pixelSize < 64); |
|
623 } |
|
624 } |
|
625 } |
|
626 #endif // QT_NO_LIBRARY |
|
627 if ((engine.isNull() && !file.isEmpty() && QFile::exists(file)) || privateDb()->isApplicationFont(file)) { |
|
628 QFontEngine::FaceId faceId; |
|
629 faceId.filename = file.toLocal8Bit(); |
|
630 faceId.index = size->fileIndex; |
|
631 |
|
632 #ifndef QT_NO_FREETYPE |
|
633 |
|
634 QScopedPointer<QFontEngineFT> fte(new QFontEngineFT(def)); |
|
635 if (fte->init(faceId, style->antialiased, |
|
636 style->antialiased ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono)) { |
|
637 #ifdef QT_NO_QWS_QPF2 |
|
638 return fte.take(); |
|
639 #else |
|
640 // try to distinguish between bdf and ttf fonts we can pre-render |
|
641 // and don't try to share outline fonts |
|
642 shareFonts = shareFonts |
|
643 && !fte->defaultGlyphs()->outline_drawing |
|
644 && !fte->getSfntTable(MAKE_TAG('h', 'e', 'a', 'd')).isEmpty(); |
|
645 engine.reset(fte.take()); |
|
646 #endif |
|
647 } |
|
648 #endif // QT_NO_FREETYPE |
|
649 } |
|
650 if (!engine.isNull()) { |
|
651 #if !defined(QT_NO_QWS_QPF2) && !defined(QT_FONTS_ARE_RESOURCES) |
|
652 if (shareFonts) { |
|
653 QScopedPointer<QFontEngineQPF> fe(new QFontEngineQPF(def, -1, engine.data())); |
|
654 engine.take(); |
|
655 if (fe->isValid()) |
|
656 return fe.take(); |
|
657 qWarning("Initializing QFontEngineQPF failed for %s", qPrintable(file)); |
|
658 engine.reset(fe->takeRenderingEngine()); |
|
659 } |
|
660 #endif |
|
661 return engine.take(); |
|
662 } |
|
663 } else |
|
664 { |
|
665 #ifndef QT_NO_QWS_QPF |
|
666 QString fn = qwsFontPath(); |
|
667 fn += QLatin1Char('/'); |
|
668 fn += family->name.toLower() |
|
669 + QLatin1Char('_') + QString::number(pixelSize*10) |
|
670 + QLatin1Char('_') + QString::number(style->key.weight) |
|
671 + (style->key.style == QFont::StyleItalic ? |
|
672 QLatin1String("i.qpf") : QLatin1String(".qpf")); |
|
673 //###rotation ### |
|
674 |
|
675 QFontEngine *fe = new QFontEngineQPF1(request, fn); |
|
676 return fe; |
|
677 #endif // QT_NO_QWS_QPF |
|
678 } |
|
679 return new QFontEngineBox(pixelSize); |
|
680 } |
|
681 |
|
682 static |
|
683 QFontEngine *loadEngine(int script, const QFontPrivate *fp, |
|
684 const QFontDef &request, |
|
685 QtFontFamily *family, QtFontFoundry *foundry, |
|
686 QtFontStyle *style, QtFontSize *size) |
|
687 { |
|
688 QScopedPointer<QFontEngine> engine(loadSingleEngine(script, fp, request, family, foundry, |
|
689 style, size)); |
|
690 if (!engine.isNull() |
|
691 && script == QUnicodeTables::Common |
|
692 && !(request.styleStrategy & QFont::NoFontMerging) && !engine->symbol) { |
|
693 |
|
694 QStringList fallbacks = privateDb()->fallbackFamilies; |
|
695 |
|
696 if (family && !family->fallbackFamilies.isEmpty()) |
|
697 fallbacks = family->fallbackFamilies; |
|
698 |
|
699 QFontEngine *fe = new QFontEngineMultiQWS(engine.data(), script, fallbacks); |
|
700 engine.take(); |
|
701 engine.reset(fe); |
|
702 } |
|
703 return engine.take(); |
|
704 } |
|
705 |
|
706 static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) |
|
707 { |
|
708 QFontDatabasePrivate *db = privateDb(); |
|
709 #ifdef QT_NO_FREETYPE |
|
710 Q_UNUSED(fnt); |
|
711 #else |
|
712 fnt->families = db->addTTFile(QFile::encodeName(fnt->fileName), fnt->data); |
|
713 db->fallbackFamilies += fnt->families; |
|
714 #endif |
|
715 db->reregisterAppFonts = true; |
|
716 } |
|
717 |
|
718 bool QFontDatabase::removeApplicationFont(int handle) |
|
719 { |
|
720 QMutexLocker locker(fontDatabaseMutex()); |
|
721 |
|
722 QFontDatabasePrivate *db = privateDb(); |
|
723 if (handle < 0 || handle >= db->applicationFonts.count()) |
|
724 return false; |
|
725 |
|
726 db->applicationFonts[handle] = QFontDatabasePrivate::ApplicationFont(); |
|
727 |
|
728 db->reregisterAppFonts = true; |
|
729 db->invalidate(); |
|
730 return true; |
|
731 } |
|
732 |
|
733 bool QFontDatabase::removeAllApplicationFonts() |
|
734 { |
|
735 QMutexLocker locker(fontDatabaseMutex()); |
|
736 |
|
737 QFontDatabasePrivate *db = privateDb(); |
|
738 if (db->applicationFonts.isEmpty()) |
|
739 return false; |
|
740 |
|
741 db->applicationFonts.clear(); |
|
742 db->invalidate(); |
|
743 return true; |
|
744 } |
|
745 |
|
746 bool QFontDatabase::supportsThreadedFontRendering() |
|
747 { |
|
748 return true; |
|
749 } |
|
750 |
|
751 /*! |
|
752 \internal |
|
753 */ |
|
754 QFontEngine * |
|
755 QFontDatabase::findFont(int script, const QFontPrivate *fp, |
|
756 const QFontDef &request) |
|
757 { |
|
758 QMutexLocker locker(fontDatabaseMutex()); |
|
759 |
|
760 const int force_encoding_id = -1; |
|
761 |
|
762 if (!privateDb()->count) |
|
763 initializeDb(); |
|
764 |
|
765 QScopedPointer<QFontEngine> fe; |
|
766 if (fp) { |
|
767 if (fp->rawMode) { |
|
768 fe.reset(loadEngine(script, fp, request, 0, 0, 0, 0)); |
|
769 |
|
770 // if we fail to load the rawmode font, use a 12pixel box engine instead |
|
771 if (fe.isNull()) |
|
772 fe.reset(new QFontEngineBox(12)); |
|
773 return fe.take(); |
|
774 } |
|
775 |
|
776 QFontCache::Key key(request, script); |
|
777 fe.reset(QFontCache::instance()->findEngine(key)); |
|
778 if (! fe.isNull()) |
|
779 return fe.take(); |
|
780 } |
|
781 |
|
782 QString family_name, foundry_name; |
|
783 QtFontStyle::Key styleKey; |
|
784 styleKey.style = request.style; |
|
785 styleKey.weight = request.weight; |
|
786 styleKey.stretch = request.stretch; |
|
787 char pitch = request.ignorePitch ? '*' : request.fixedPitch ? 'm' : 'p'; |
|
788 |
|
789 parseFontName(request.family, foundry_name, family_name); |
|
790 |
|
791 FM_DEBUG("QFontDatabase::findFont\n" |
|
792 " request:\n" |
|
793 " family: %s [%s], script: %d\n" |
|
794 " weight: %d, style: %d\n" |
|
795 " stretch: %d\n" |
|
796 " pixelSize: %d\n" |
|
797 " pitch: %c", |
|
798 family_name.isEmpty() ? "-- first in script --" : family_name.toLatin1().constData(), |
|
799 foundry_name.isEmpty() ? "-- any --" : foundry_name.toLatin1().constData(), |
|
800 script, request.weight, request.style, request.stretch, request.pixelSize, pitch); |
|
801 |
|
802 if (qt_enable_test_font && request.family == QLatin1String("__Qt__Box__Engine__")) { |
|
803 fe.reset(new QTestFontEngine(request.pixelSize)); |
|
804 fe->fontDef = request; |
|
805 } |
|
806 |
|
807 if (fe.isNull()) |
|
808 { |
|
809 QtFontDesc desc; |
|
810 match(script, request, family_name, foundry_name, force_encoding_id, &desc); |
|
811 |
|
812 if (desc.family != 0 && desc.foundry != 0 && desc.style != 0 |
|
813 ) { |
|
814 FM_DEBUG(" BEST:\n" |
|
815 " family: %s [%s]\n" |
|
816 " weight: %d, style: %d\n" |
|
817 " stretch: %d\n" |
|
818 " pixelSize: %d\n" |
|
819 " pitch: %c\n" |
|
820 " encoding: %d\n", |
|
821 desc.family->name.toLatin1().constData(), |
|
822 desc.foundry->name.isEmpty() ? "-- none --" : desc.foundry->name.toLatin1().constData(), |
|
823 desc.style->key.weight, desc.style->key.style, |
|
824 desc.style->key.stretch, desc.size ? desc.size->pixelSize : 0xffff, |
|
825 'p', 0 |
|
826 ); |
|
827 |
|
828 fe.reset(loadEngine(script, fp, request, desc.family, desc.foundry, desc.style, desc.size |
|
829 )); |
|
830 } else { |
|
831 FM_DEBUG(" NO MATCH FOUND\n"); |
|
832 } |
|
833 if (! fe.isNull()) |
|
834 initFontDef(desc, request, &fe->fontDef); |
|
835 } |
|
836 |
|
837 #ifndef QT_NO_FREETYPE |
|
838 if (! fe.isNull()) { |
|
839 if (scriptRequiresOpenType(script) && fe->type() == QFontEngine::Freetype) { |
|
840 HB_Face hbFace = static_cast<QFontEngineFT *>(fe.data())->harfbuzzFace(); |
|
841 if (!hbFace || !hbFace->supported_scripts[script]) { |
|
842 FM_DEBUG(" OpenType support missing for script\n"); |
|
843 fe.reset(0); |
|
844 } |
|
845 } |
|
846 } |
|
847 #endif |
|
848 |
|
849 if (! fe.isNull()) { |
|
850 if (fp) { |
|
851 QFontDef def = request; |
|
852 if (def.family.isEmpty()) { |
|
853 def.family = fp->request.family; |
|
854 def.family = def.family.left(def.family.indexOf(QLatin1Char(','))); |
|
855 } |
|
856 QFontCache::Key key(def, script); |
|
857 QFontCache::instance()->insertEngine(key, fe.data()); |
|
858 } |
|
859 } |
|
860 |
|
861 if (fe.isNull()) { |
|
862 if (!request.family.isEmpty()) |
|
863 return 0; |
|
864 |
|
865 FM_DEBUG("returning box engine"); |
|
866 |
|
867 fe.reset(new QFontEngineBox(request.pixelSize)); |
|
868 |
|
869 if (fp) { |
|
870 QFontCache::Key key(request, script); |
|
871 QFontCache::instance()->insertEngine(key, fe.data()); |
|
872 } |
|
873 } |
|
874 |
|
875 if (fp && fp->dpi > 0) { |
|
876 fe->fontDef.pointSize = qreal(double((fe->fontDef.pixelSize * 72) / fp->dpi)); |
|
877 } else { |
|
878 fe->fontDef.pointSize = request.pointSize; |
|
879 } |
|
880 |
|
881 return fe.take(); |
|
882 } |
|
883 |
|
884 void QFontDatabase::load(const QFontPrivate *d, int script) |
|
885 { |
|
886 QFontDef req = d->request; |
|
887 |
|
888 if (req.pixelSize == -1) |
|
889 req.pixelSize = qRound(req.pointSize*d->dpi/72); |
|
890 if (req.pointSize < 0) |
|
891 req.pointSize = req.pixelSize*72.0/d->dpi; |
|
892 |
|
893 if (!d->engineData) { |
|
894 QFontCache::Key key(req, script); |
|
895 |
|
896 // look for the requested font in the engine data cache |
|
897 d->engineData = QFontCache::instance()->findEngineData(key); |
|
898 |
|
899 if (!d->engineData) { |
|
900 // create a new one |
|
901 d->engineData = new QFontEngineData; |
|
902 QT_TRY { |
|
903 QFontCache::instance()->insertEngineData(key, d->engineData); |
|
904 } QT_CATCH(...) { |
|
905 delete d->engineData; |
|
906 d->engineData = 0; |
|
907 QT_RETHROW; |
|
908 } |
|
909 } else { |
|
910 d->engineData->ref.ref(); |
|
911 } |
|
912 } |
|
913 |
|
914 // the cached engineData could have already loaded the engine we want |
|
915 if (d->engineData->engines[script]) return; |
|
916 |
|
917 // double scale = 1.0; // ### TODO: fix the scale calculations |
|
918 |
|
919 // list of families to try |
|
920 QStringList family_list; |
|
921 |
|
922 if (!req.family.isEmpty()) { |
|
923 family_list = req.family.split(QLatin1Char(',')); |
|
924 |
|
925 // append the substitute list for each family in family_list |
|
926 QStringList subs_list; |
|
927 QStringList::ConstIterator it = family_list.constBegin(), end = family_list.constEnd(); |
|
928 for (; it != end; ++it) |
|
929 subs_list += QFont::substitutes(*it); |
|
930 family_list += subs_list; |
|
931 |
|
932 // append the default fallback font for the specified script |
|
933 // family_list << ... ; ########### |
|
934 |
|
935 // add the default family |
|
936 QString defaultFamily = QApplication::font().family(); |
|
937 if (! family_list.contains(defaultFamily)) |
|
938 family_list << defaultFamily; |
|
939 |
|
940 // add QFont::defaultFamily() to the list, for compatibility with |
|
941 // previous versions |
|
942 family_list << QApplication::font().defaultFamily(); |
|
943 } |
|
944 |
|
945 // null family means find the first font matching the specified script |
|
946 family_list << QString(); |
|
947 |
|
948 // load the font |
|
949 QFontEngine *engine = 0; |
|
950 QStringList::ConstIterator it = family_list.constBegin(), end = family_list.constEnd(); |
|
951 for (; !engine && it != end; ++it) { |
|
952 req.family = *it; |
|
953 |
|
954 engine = QFontDatabase::findFont(script, d, req); |
|
955 if (engine && (engine->type()==QFontEngine::Box) && !req.family.isEmpty()) |
|
956 engine = 0; |
|
957 } |
|
958 |
|
959 engine->ref.ref(); |
|
960 d->engineData->engines[script] = engine; |
|
961 } |
|
962 |
|
963 |
|
964 QT_END_NAMESPACE |