source: trunk/src/gui/text/qfontdatabase_s60.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 44.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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** 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 qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <private/qapplication_p.h>
43#include "qdir.h"
44#include "qfont_p.h"
45#include "qfontengine_s60_p.h"
46#include "qabstractfileengine.h"
47#include "qdesktopservices.h"
48#include "qtemporaryfile.h"
49#include "qtextcodec.h"
50#include <private/qpixmap_s60_p.h>
51#include <private/qt_s60_p.h>
52#include "qendian.h"
53#include <private/qcore_symbian_p.h>
54#ifdef QT_NO_FREETYPE
55#include <openfont.h>
56#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
57#include <graphics/openfontrasterizer.h> // COpenFontRasterizer has moved to a new header file
58#endif // SYMBIAN_ENABLE_SPLIT_HEADERS
59#endif // QT_NO_FREETYPE
60
61QT_BEGIN_NAMESPACE
62
63QStringList qt_symbian_fontFamiliesOnFontServer() // Also used in qfont_s60.cpp
64{
65 QStringList result;
66 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
67 const int numTypeFaces = S60->screenDevice()->NumTypefaces();
68 for (int i = 0; i < numTypeFaces; i++) {
69 TTypefaceSupport typefaceSupport;
70 S60->screenDevice()->TypefaceSupport(typefaceSupport, i);
71 const QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
72 result.append(familyName);
73 }
74 lock.relock();
75 return result;
76}
77
78QFileInfoList alternativeFilePaths(const QString &path, const QStringList &nameFilters,
79 QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort,
80 bool uniqueFileNames = true)
81{
82 QFileInfoList result;
83
84 // Prepare a 'soft to hard' drive list: W:, X: ... A:, Z:
85 QStringList driveStrings;
86 foreach (const QFileInfo &drive, QDir::drives())
87 driveStrings.append(drive.absolutePath());
88 driveStrings.sort();
89 const QString zDriveString(QLatin1String("Z:/"));
90 driveStrings.removeAll(zDriveString);
91 driveStrings.prepend(zDriveString);
92
93 QStringList uniqueFileNameList;
94 for (int i = driveStrings.count() - 1; i >= 0; --i) {
95 const QDir dirOnDrive(driveStrings.at(i) + path);
96 const QFileInfoList entriesOnDrive = dirOnDrive.entryInfoList(nameFilters, filters, sort);
97 if (uniqueFileNames) {
98 foreach(const QFileInfo &entry, entriesOnDrive) {
99 if (!uniqueFileNameList.contains(entry.fileName())) {
100 uniqueFileNameList.append(entry.fileName());
101 result.append(entry);
102 }
103 }
104 } else {
105 result.append(entriesOnDrive);
106 }
107 }
108 return result;
109}
110
111#ifdef QT_NO_FREETYPE
112class QSymbianFontDatabaseExtrasImplementation : public QSymbianFontDatabaseExtras
113{
114public:
115 QSymbianFontDatabaseExtrasImplementation();
116 ~QSymbianFontDatabaseExtrasImplementation();
117
118 const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const;
119 void removeAppFontData(QFontDatabasePrivate::ApplicationFont *fnt);
120 static inline bool appFontLimitReached();
121 TUid addFontFileToFontStore(const QFileInfo &fontFileInfo);
122 static void clear();
123
124 static inline QString tempAppFontFolder();
125 static const QString appFontMarkerPrefix;
126 static QString appFontMarker(); // 'qaf<shortUid[+shortPid]>'
127
128 struct CFontFromFontStoreReleaser {
129 static inline void cleanup(CFont *font)
130 {
131 if (!font)
132 return;
133 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
134 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras);
135 dbExtras->m_store->ReleaseFont(font);
136 }
137 };
138
139 struct CFontFromScreenDeviceReleaser {
140 static inline void cleanup(CFont *font)
141 {
142 if (!font)
143 return;
144 S60->screenDevice()->ReleaseFont(font);
145 }
146 };
147
148// m_heap, m_store, m_rasterizer and m_extras are used if Symbian
149// does not provide the Font Table API
150 RHeap* m_heap;
151 CFontStore *m_store;
152 COpenFontRasterizer *m_rasterizer;
153 mutable QList<const QSymbianTypeFaceExtras *> m_extras;
154
155 mutable QHash<QString, const QSymbianTypeFaceExtras *> m_extrasHash;
156 mutable QSet<QString> m_applicationFontFamilies;
157};
158
159const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix =
160 QLatin1String("Q");
161
162inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
163{
164 return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\');
165}
166
167QString QSymbianFontDatabaseExtrasImplementation::appFontMarker()
168{
169 static QString result;
170 if (result.isEmpty()) {
171 quint16 id = 0;
172 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
173 // We are allowed to load app fonts even from previous, crashed runs
174 // of this application, since we can access the font tables.
175 const quint32 uid = RProcess().Type().MostDerived().iUid;
176 id = static_cast<quint16>(uid + (uid >> 16));
177 } else {
178 // If no font table Api is available, we must not even load a font
179 // from a previous (crashed) run of this application. Reason: we
180 // won't get the font tables, they are not in the CFontStore.
181 // So, we use the pid, for more uniqueness.
182 id = static_cast<quint16>(RProcess().Id().Id());
183 }
184 result = appFontMarkerPrefix + QString::fromLatin1("%1").arg(id & 0x7fff, 3, 32, QLatin1Char('0'));
185 Q_ASSERT(appFontMarkerPrefix.length() == 1 && result.length() == 4);
186 }
187 return result;
188}
189
190static inline bool qt_symbian_fontNameHasAppFontMarker(const QString &fontName)
191{
192 const int idLength = 3; // Keep in sync with id length in appFontMarker().
193 const QString &prefix = QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix;
194 if (fontName.length() < prefix.length() + idLength
195 || fontName.mid(fontName.length() - idLength - prefix.length(), prefix.length()) != prefix)
196 return false;
197 // Testing if the the id is base32 data
198 for (int i = fontName.length() - idLength; i < fontName.length(); ++i) {
199 const QChar &c = fontName.at(i);
200 if (!(c >= QLatin1Char('0') && c <= QLatin1Char('9')
201 || c >= QLatin1Char('a') && c <= QLatin1Char('v')))
202 return false;
203 }
204 return true;
205}
206
207// If fontName is an application font of this app, prepend the app font marker
208QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName)
209{
210 QFontDatabasePrivate *db = privateDb();
211 Q_ASSERT(db);
212 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
213 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
214 return dbExtras->m_applicationFontFamilies.contains(fontName) ?
215 fontName + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
216 : fontName;
217}
218
219static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName)
220{
221 return markedFontName.left(markedFontName.length()
222 - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length());
223}
224
225QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation()
226{
227 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
228 QStringList filters;
229 filters.append(QLatin1String("*.ttf"));
230 filters.append(QLatin1String("*.ccc"));
231 filters.append(QLatin1String("*.ltt"));
232 const QFileInfoList fontFiles = alternativeFilePaths(QLatin1String("resource\\Fonts"), filters);
233
234 const TInt heapMinLength = 0x1000;
235 const TInt heapMaxLength = qMax(0x20000 * fontFiles.count(), heapMinLength);
236 m_heap = User::ChunkHeap(NULL, heapMinLength, heapMaxLength);
237 QT_TRAP_THROWING(
238 m_store = CFontStore::NewL(m_heap);
239 m_rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
240 CleanupStack::PushL(m_rasterizer);
241 m_store->InstallRasterizerL(m_rasterizer);
242 CleanupStack::Pop(m_rasterizer););
243
244 foreach (const QFileInfo &fontFileInfo, fontFiles)
245 addFontFileToFontStore(fontFileInfo);
246 }
247}
248
249void QSymbianFontDatabaseExtrasImplementation::clear()
250{
251 QFontDatabasePrivate *db = privateDb();
252 if (!db)
253 return;
254 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
255 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
256 if (!dbExtras)
257 return; // initializeDb() has never been called
258 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
259 qDeleteAll(dbExtras->m_extrasHash);
260 } else {
261 typedef QList<const QSymbianTypeFaceExtras *>::iterator iterator;
262 for (iterator p = dbExtras->m_extras.begin(); p != dbExtras->m_extras.end(); ++p) {
263 dbExtras->m_store->ReleaseFont((*p)->fontOwner());
264 delete *p;
265 }
266 dbExtras->m_extras.clear();
267 }
268 dbExtras->m_extrasHash.clear();
269}
270
271void qt_cleanup_symbianFontDatabase()
272{
273 QFontDatabasePrivate *db = privateDb();
274 if (!db)
275 return;
276
277 QSymbianFontDatabaseExtrasImplementation::clear();
278
279 if (!db->applicationFonts.isEmpty()) {
280 QFontDatabase::removeAllApplicationFonts();
281 // We remove the left over temporary font files of Qt application.
282 // Active fonts are undeletable since the font server holds a handle
283 // on them, so we do not need to worry to delete other running
284 // applications' fonts.
285 const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder());
286 const QStringList filter(
287 QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf"));
288 foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter))
289 QFile(ttfFile.absoluteFilePath()).remove();
290 db->applicationFonts.clear();
291 }
292}
293
294QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation()
295{
296 qt_cleanup_symbianFontDatabase();
297 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
298 delete m_store;
299 m_heap->Close();
300 }
301}
302
303#ifndef FNTSTORE_H_INLINES_SUPPORT_FMM
304/*
305 Workaround: fntstore.h has an inlined function 'COpenFont* CBitmapFont::OpenFont()'
306 that returns a private data member. The header will change between SDKs. But Qt has
307 to build on any SDK version and run on other versions of Symbian OS.
308 This function performs the needed pointer arithmetic to get the right COpenFont*
309*/
310COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont)
311{
312 const TInt offsetIOpenFont = 92; // '_FOFF(CBitmapFont, iOpenFont)' ..if iOpenFont weren't private
313 const TUint valueIOpenFont = *(TUint*)PtrAdd(aBitmapFont, offsetIOpenFont);
314 return (valueIOpenFont & 1) ?
315 (COpenFont*)PtrAdd(aBitmapFont, valueIOpenFont & ~1) : // New behavior: iOpenFont is offset
316 (COpenFont*)valueIOpenFont; // Old behavior: iOpenFont is pointer
317}
318#endif // FNTSTORE_H_INLINES_SUPPORT_FMM
319
320const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &aTypeface,
321 bool bold, bool italic) const
322{
323 const QString typeface = qt_symbian_fontNameWithAppFontMarker(aTypeface);
324 const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic));
325 if (!m_extrasHash.contains(searchKey)) {
326 TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1);
327 if (bold)
328 searchSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold);
329 if (italic)
330 searchSpec.iFontStyle.SetPosture(EPostureItalic);
331
332 CFont* font = NULL;
333 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
334 const TInt err = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(font, searchSpec);
335 Q_ASSERT(err == KErrNone && font);
336 QScopedPointer<CFont, CFontFromScreenDeviceReleaser> sFont(font);
337 QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font);
338 sFont.take();
339 m_extrasHash.insert(searchKey, extras);
340 } else {
341 const TInt err = m_store->GetNearestFontToDesignHeightInPixels(font, searchSpec);
342 Q_ASSERT(err == KErrNone && font);
343 const CBitmapFont *bitmapFont = static_cast<CBitmapFont*>(font);
344 COpenFont *openFont =
345#ifdef FNTSTORE_H_INLINES_SUPPORT_FMM
346 bitmapFont->OpenFont();
347#else // FNTSTORE_H_INLINES_SUPPORT_FMM
348 OpenFontFromBitmapFont(bitmapFont);
349#endif // FNTSTORE_H_INLINES_SUPPORT_FMM
350 const TOpenFontFaceAttrib* const attrib = openFont->FaceAttrib();
351 const QString foundKey =
352 QString((const QChar*)attrib->FullName().Ptr(), attrib->FullName().Length());
353 if (!m_extrasHash.contains(foundKey)) {
354 QScopedPointer<CFont, CFontFromFontStoreReleaser> sFont(font);
355 QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font, openFont);
356 sFont.take();
357 m_extras.append(extras);
358 m_extrasHash.insert(searchKey, extras);
359 m_extrasHash.insert(foundKey, extras);
360 } else {
361 m_store->ReleaseFont(font);
362 m_extrasHash.insert(searchKey, m_extrasHash.value(foundKey));
363 }
364 }
365 }
366 return m_extrasHash.value(searchKey);
367}
368
369void QSymbianFontDatabaseExtrasImplementation::removeAppFontData(
370 QFontDatabasePrivate::ApplicationFont *fnt)
371{
372 clear();
373 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()
374 && fnt->fontStoreFontFileUid.iUid != 0)
375 m_store->RemoveFile(fnt->fontStoreFontFileUid);
376 if (!fnt->families.isEmpty())
377 m_applicationFontFamilies.remove(fnt->families.first());
378 if (fnt->screenDeviceFontFileId != 0)
379 S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId);
380 QFile::remove(fnt->temporaryFileName);
381 *fnt = QFontDatabasePrivate::ApplicationFont();
382}
383
384bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
385{
386 QFontDatabasePrivate *db = privateDb();
387 if (!db)
388 return false;
389 const int maxAppFonts = 5;
390 int registeredAppFonts = 0;
391 foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts)
392 if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts)
393 return true;
394 return false;
395}
396
397TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo)
398{
399 Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable());
400 const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath());
401 const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile));
402 TUid fontUid = {0};
403 TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr));
404 return fontUid;
405}
406
407#else // QT_NO_FREETYPE
408class QFontEngineFTS60 : public QFontEngineFT
409{
410public:
411 QFontEngineFTS60(const QFontDef &fd);
412};
413
414QFontEngineFTS60::QFontEngineFTS60(const QFontDef &fd)
415 : QFontEngineFT(fd)
416{
417 default_hint_style = HintFull;
418}
419#endif // QT_NO_FREETYPE
420
421/*
422 QFontEngineS60::pixelsToPoints, QFontEngineS60::pointsToPixels, QFontEngineMultiS60::QFontEngineMultiS60
423 and QFontEngineMultiS60::QFontEngineMultiS60 should be in qfontengine_s60.cpp. But since also the
424 Freetype based font rendering need them, they are here.
425*/
426qreal QFontEngineS60::pixelsToPoints(qreal pixels, Qt::Orientation orientation)
427{
428 CWsScreenDevice* device = S60->screenDevice();
429 return (orientation == Qt::Horizontal?
430 device->HorizontalPixelsToTwips(pixels)
431 :device->VerticalPixelsToTwips(pixels)) / KTwipsPerPoint;
432}
433
434qreal QFontEngineS60::pointsToPixels(qreal points, Qt::Orientation orientation)
435{
436 CWsScreenDevice* device = S60->screenDevice();
437 const int twips = points * KTwipsPerPoint;
438 return orientation == Qt::Horizontal?
439 device->HorizontalTwipsToPixels(twips)
440 :device->VerticalTwipsToPixels(twips);
441}
442
443QFontEngineMultiS60::QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies)
444 : QFontEngineMulti(fallbackFamilies.size() + 1)
445 , m_script(script)
446 , m_fallbackFamilies(fallbackFamilies)
447{
448 engines[0] = first;
449 first->ref.ref();
450 fontDef = engines[0]->fontDef;
451}
452
453void QFontEngineMultiS60::loadEngine(int at)
454{
455 Q_ASSERT(at < engines.size());
456 Q_ASSERT(engines.at(at) == 0);
457
458 QFontDef request = fontDef;
459 request.styleStrategy |= QFont::NoFontMerging;
460 request.family = m_fallbackFamilies.at(at-1);
461 engines[at] = QFontDatabase::findFont(m_script,
462 /*fontprivate*/0,
463 request);
464 Q_ASSERT(engines[at]);
465}
466
467static bool registerScreenDeviceFont(int screenDeviceFontIndex,
468 const QSymbianFontDatabaseExtrasImplementation *dbExtras)
469{
470 TTypefaceSupport typefaceSupport;
471 S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex);
472
473 QString familyName((const QChar*)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
474 if (qt_symbian_fontNameHasAppFontMarker(familyName)) {
475 const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
476 if (familyName.endsWith(marker)) {
477 familyName = qt_symbian_appFontNameWithoutMarker(familyName);
478 dbExtras->m_applicationFontFamilies.insert(familyName);
479 } else {
480 return false; // This was somebody else's application font. Skip it.
481 }
482 }
483
484 CFont *font; // We have to get a font instance in order to know all the details
485 TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11);
486 if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone)
487 return false;
488 QScopedPointer<CFont, QSymbianFontDatabaseExtrasImplementation::CFontFromScreenDeviceReleaser> sFont(font);
489 if (font->TypeUid() != KCFbsFontUid)
490 return false;
491 TOpenFontFaceAttrib faceAttrib;
492 const CFbsFont *cfbsFont = static_cast<const CFbsFont *>(font);
493 cfbsFont->GetFaceAttrib(faceAttrib);
494
495 QtFontStyle::Key styleKey;
496 styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal;
497 styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal;
498
499 QtFontFamily *family = privateDb()->family(familyName, true);
500 family->fixedPitch = faceAttrib.IsMonoWidth();
501 QtFontFoundry *foundry = family->foundry(QString(), true);
502 QtFontStyle *style = foundry->style(styleKey, true);
503 style->smoothScalable = typefaceSupport.iIsScalable;
504 style->pixelSize(0, true);
505
506 const QSymbianTypeFaceExtras *typeFaceExtras =
507 dbExtras->extras(familyName, faceAttrib.IsBold(), faceAttrib.IsItalic());
508 const QByteArray os2Table = typeFaceExtras->getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
509 const unsigned char* data = reinterpret_cast<const unsigned char*>(os2Table.constData());
510 const unsigned char* ulUnicodeRange = data + 42;
511 quint32 unicodeRange[4] = {
512 qFromBigEndian<quint32>(ulUnicodeRange),
513 qFromBigEndian<quint32>(ulUnicodeRange + 4),
514 qFromBigEndian<quint32>(ulUnicodeRange + 8),
515 qFromBigEndian<quint32>(ulUnicodeRange + 12)
516 };
517 const unsigned char* ulCodePageRange = data + 78;
518 quint32 codePageRange[2] = {
519 qFromBigEndian<quint32>(ulCodePageRange),
520 qFromBigEndian<quint32>(ulCodePageRange + 4)
521 };
522 const QList<QFontDatabase::WritingSystem> writingSystems =
523 determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
524 foreach (const QFontDatabase::WritingSystem system, writingSystems)
525 family->writingSystems[system] = QtFontFamily::Supported;
526 return true;
527}
528
529static void initializeDb()
530{
531 QFontDatabasePrivate *db = privateDb();
532 if(!db || db->count)
533 return;
534
535#ifdef QT_NO_FREETYPE
536 if (!db->symbianExtras)
537 db->symbianExtras = new QSymbianFontDatabaseExtrasImplementation;
538
539 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
540
541 const int numTypeFaces = S60->screenDevice()->NumTypefaces();
542 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
543 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
544 for (int i = 0; i < numTypeFaces; i++)
545 registerScreenDeviceFont(i, dbExtras);
546
547 // We have to clear/release all CFonts, here, in case one of the fonts is
548 // an application font of another running Qt app. Otherwise the other Qt app
549 // cannot remove it's application font, anymore -> "Zombie Font".
550 QSymbianFontDatabaseExtrasImplementation::clear();
551
552 lock.relock();
553
554#else // QT_NO_FREETYPE
555 QDir dir(QDesktopServices::storageLocation(QDesktopServices::FontsLocation));
556 dir.setNameFilters(QStringList() << QLatin1String("*.ttf")
557 << QLatin1String("*.ttc") << QLatin1String("*.pfa")
558 << QLatin1String("*.pfb"));
559 for (int i = 0; i < int(dir.count()); ++i) {
560 const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i]));
561 db->addTTFile(file);
562 }
563#endif // QT_NO_FREETYPE
564}
565
566static inline void load(const QString &family = QString(), int script = -1)
567{
568 Q_UNUSED(family)
569 Q_UNUSED(script)
570 initializeDb();
571}
572
573struct OffsetTable {
574 quint32 sfntVersion;
575 quint16 numTables, searchRange, entrySelector, rangeShift;
576};
577
578struct TableRecord {
579 quint32 tag, checkSum, offset, length;
580};
581
582struct NameTableHead {
583 quint16 format, count, stringOffset;
584};
585
586struct NameRecord {
587 quint16 platformID, encodingID, languageID, nameID, length, offset;
588};
589
590static quint32 ttfCalcChecksum(const char *data, quint32 bytesCount)
591{
592 quint32 result = 0;
593 const quint32 *ptr = reinterpret_cast<const quint32*>(data);
594 const quint32 *endPtr =
595 ptr + (bytesCount + sizeof(quint32) - 1) / sizeof(quint32);
596 while (ptr < endPtr) {
597 const quint32 unit32Value = *ptr++;
598 result += qFromBigEndian(unit32Value);
599 }
600 return result;
601}
602
603static inline quint32 toDWordBoundary(quint32 value)
604{
605 return (value + 3) & ~3;
606}
607
608static inline quint32 dWordPadding(quint32 value)
609{
610 return (4 - (value & 3)) & 3;
611}
612
613static inline bool ttfMarkNameTable(QByteArray &table, const QString &marker)
614{
615 const quint32 tableLength = static_cast<quint32>(table.size());
616
617 if (tableLength > 50000 // hard limit
618 || tableLength < sizeof(NameTableHead)) // corrupt name table
619 return false;
620
621 const NameTableHead *head = reinterpret_cast<const NameTableHead*>(table.constData());
622 const quint16 count = qFromBigEndian(head->count);
623 const quint16 stringOffset = qFromBigEndian(head->stringOffset);
624 if (count > 200 // hard limit
625 || stringOffset >= tableLength // corrupt name table
626 || sizeof(NameTableHead) + count * sizeof(NameRecord) >= tableLength) // corrupt name table
627 return false;
628
629 QTextEncoder encoder(QTextCodec::codecForName("UTF-16BE"), QTextCodec::IgnoreHeader);
630 const QByteArray markerUtf16BE = encoder.fromUnicode(marker);
631 const QByteArray markerAscii = marker.toAscii();
632
633 QByteArray markedTable;
634 markedTable.reserve(tableLength + marker.length() * 20); // Original size plus some extra
635 markedTable.append(table, stringOffset);
636 QByteArray markedStrings;
637 quint32 stringDataCount = stringOffset;
638 for (quint16 i = 0; i < count; ++i) {
639 const quint32 nameRecordOffset = sizeof(NameTableHead) + sizeof(NameRecord) * i;
640 NameRecord *nameRecord =
641 reinterpret_cast<NameRecord*>(markedTable.data() + nameRecordOffset);
642 const quint16 nameID = qFromBigEndian(nameRecord->nameID);
643 const quint16 platformID = qFromBigEndian(nameRecord->platformID);
644 const quint16 encodingID = qFromBigEndian(nameRecord->encodingID);
645 const quint16 offset = qFromBigEndian(nameRecord->offset);
646 const quint16 length = qFromBigEndian(nameRecord->length);
647 stringDataCount += length;
648 if (stringDataCount > 80000 // hard limit. String data may be > name table size. Multiple records can reference the same string.
649 || static_cast<quint32>(stringOffset + offset + length) > tableLength) // String outside bounds
650 return false;
651 const bool needsMarker =
652 nameID == 1 || nameID == 3 || nameID == 4 || nameID == 16 || nameID == 21;
653 const bool isUnicode =
654 platformID == 0 || platformID == 3 && encodingID == 1;
655 const QByteArray originalString =
656 QByteArray::fromRawData(table.constData() + stringOffset + offset, length);
657 QByteArray markedString;
658 if (needsMarker) {
659 const int maxBytesLength = (KMaxTypefaceNameLength - marker.length()) * (isUnicode ? 2 : 1);
660 markedString = originalString.left(maxBytesLength) + (isUnicode ? markerUtf16BE : markerAscii);
661 } else {
662 markedString = originalString;
663 }
664 nameRecord->offset = qToBigEndian(static_cast<quint16>(markedStrings.length()));
665 nameRecord->length = qToBigEndian(static_cast<quint16>(markedString.length()));
666 markedStrings.append(markedString);
667 }
668 markedTable.append(markedStrings);
669 table = markedTable;
670 return true;
671}
672
673const quint32 ttfMaxFileSize = 3500000;
674
675static inline bool ttfMarkAppFont(QByteArray &ttf, const QString &marker)
676{
677 const quint32 ttfChecksumNumber = 0xb1b0afba;
678 const quint32 alignment = 4;
679 const quint32 ttfLength = static_cast<quint32>(ttf.size());
680 if (ttfLength > ttfMaxFileSize // hard limit
681 || ttfLength % alignment != 0 // ttf sizes are always factors of 4
682 || ttfLength <= sizeof(OffsetTable) // ttf too short
683 || ttfCalcChecksum(ttf.constData(), ttf.size()) != ttfChecksumNumber) // ttf checksum is invalid
684 return false;
685
686 const OffsetTable *offsetTable = reinterpret_cast<const OffsetTable*>(ttf.constData());
687 const quint16 numTables = qFromBigEndian(offsetTable->numTables);
688 const quint32 recordsLength =
689 toDWordBoundary(sizeof(OffsetTable) + numTables * sizeof(TableRecord));
690 if (numTables > 30 // hard limit
691 || recordsLength + numTables * alignment > ttfLength) // Corrupt ttf. Tables would not fit, even if empty.
692 return false;
693
694 QByteArray markedTtf;
695 markedTtf.reserve(ttfLength + marker.length() * 20); // Original size plus some extra
696 markedTtf.append(ttf.constData(), recordsLength);
697
698 const quint32 ttfCheckSumAdjustmentOffset = 8; // Offset from the start of 'head'
699 int indexOfHeadTable = -1;
700 quint32 ttfDataSize = recordsLength;
701 typedef QPair<quint32, quint32> Range;
702 QList<Range> memoryRanges;
703 memoryRanges.reserve(numTables);
704 for (int i = 0; i < numTables; ++i) {
705 TableRecord *tableRecord =
706 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
707 const quint32 offset = qFromBigEndian(tableRecord->offset);
708 const quint32 length = qFromBigEndian(tableRecord->length);
709 const quint32 lengthAligned = toDWordBoundary(length);
710 ttfDataSize += lengthAligned;
711 if (offset < recordsLength // must not intersect ttf header/records
712 || offset % alignment != 0 // must be aligned
713 || offset > ttfLength - alignment // table out of bounds
714 || offset + lengthAligned > ttfLength // table out of bounds
715 || ttfDataSize > ttfLength) // tables would not fit into the ttf
716 return false;
717
718 foreach (const Range &range, memoryRanges)
719 if (offset < range.first + range.second && offset + lengthAligned > range.first)
720 return false; // Overlaps with another table
721 memoryRanges.append(Range(offset, lengthAligned));
722
723 quint32 checkSum = qFromBigEndian(tableRecord->checkSum);
724 if (tableRecord->tag == qToBigEndian(static_cast<quint32>('head'))) {
725 if (length < ttfCheckSumAdjustmentOffset + sizeof(quint32))
726 return false; // Invalid 'head' table
727 const quint32 *checkSumAdjustmentTag =
728 reinterpret_cast<const quint32*>(ttf.constData() + offset + ttfCheckSumAdjustmentOffset);
729 const quint32 checkSumAdjustment = qFromBigEndian(*checkSumAdjustmentTag);
730 checkSum += checkSumAdjustment;
731 indexOfHeadTable = i; // For the ttf checksum re-calculation, later
732 }
733 if (checkSum != ttfCalcChecksum(ttf.constData() + offset, length))
734 return false; // Table checksum is invalid
735
736 bool updateTableChecksum = false;
737 QByteArray table;
738 if (tableRecord->tag == qToBigEndian(static_cast<quint32>('name'))) {
739 table = QByteArray(ttf.constData() + offset, length);
740 if (!ttfMarkNameTable(table, marker))
741 return false; // Name table was not markable.
742 updateTableChecksum = true;
743 } else {
744 table = QByteArray::fromRawData(ttf.constData() + offset, length);
745 }
746
747 tableRecord->offset = qToBigEndian(markedTtf.size());
748 tableRecord->length = qToBigEndian(table.size());
749 markedTtf.append(table);
750 markedTtf.append(QByteArray(dWordPadding(table.size()), 0)); // 0-padding
751 if (updateTableChecksum) {
752 TableRecord *tableRecord = // Need to recalculate, since markedTtf changed
753 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
754 const quint32 offset = qFromBigEndian(tableRecord->offset);
755 const quint32 length = qFromBigEndian(tableRecord->length);
756 tableRecord->checkSum = qToBigEndian(ttfCalcChecksum(markedTtf.constData() + offset, length));
757 }
758 }
759 if (indexOfHeadTable == -1 // 'head' table is mandatory
760 || ttfDataSize != ttfLength) // We do not allow ttf data "holes". Neither does Symbian.
761 return false;
762 TableRecord *headRecord =
763 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + indexOfHeadTable * sizeof(TableRecord));
764 quint32 *checkSumAdjustmentTag =
765 reinterpret_cast<quint32*>(markedTtf.data() + qFromBigEndian(headRecord->offset) + ttfCheckSumAdjustmentOffset);
766 *checkSumAdjustmentTag = 0;
767 const quint32 ttfChecksum = ttfCalcChecksum(markedTtf.constData(), markedTtf.count());
768 *checkSumAdjustmentTag = qToBigEndian(ttfChecksumNumber - ttfChecksum);
769 ttf = markedTtf;
770 return true;
771}
772
773static inline bool ttfCanSymbianLoadFont(const QByteArray &data, const QString &fileName)
774{
775 bool result = false;
776 QString ttfFileName;
777 QFile tempFileGuard;
778 QFileInfo info(fileName);
779 if (!data.isEmpty()) {
780 QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
781 + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
782 + QLatin1String("XXXXXX.ttf"));
783 if (!tempfile.open() || tempfile.write(data) == -1)
784 return false;
785 ttfFileName = QDir::toNativeSeparators(QFileInfo(tempfile).canonicalFilePath());
786 tempfile.setAutoRemove(false);
787 tempfile.close();
788 tempFileGuard.setFileName(ttfFileName);
789 if (!tempFileGuard.open(QIODevice::ReadOnly))
790 return false;
791 } else if (info.isFile()) {
792 ttfFileName = QDir::toNativeSeparators(info.canonicalFilePath());
793 } else {
794 return false;
795 }
796
797 CFontStore *store = 0;
798 RHeap* heap = User::ChunkHeap(NULL, 0x1000, 0x20000);
799 if (heap) {
800 QT_TRAP_THROWING(
801 CleanupClosePushL(*heap);
802 store = CFontStore::NewL(heap);
803 CleanupStack::PushL(store);
804 COpenFontRasterizer *rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
805 CleanupStack::PushL(rasterizer);
806 store->InstallRasterizerL(rasterizer);
807 CleanupStack::Pop(rasterizer);
808 TUid fontUid = {-1};
809 TRAP_IGNORE(fontUid = store->AddFileL(qt_QString2TPtrC(ttfFileName)));
810 if (fontUid.iUid != -1)
811 result = true;
812 CleanupStack::PopAndDestroy(2, heap); // heap, store
813 );
814 }
815
816 if (tempFileGuard.isOpen())
817 tempFileGuard.remove();
818
819 return result;
820}
821
822static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
823{
824 if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
825 || fnt->data.size() > ttfMaxFileSize // hard limit
826 || fnt->data.isEmpty() && (!fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive) // Only buffer or .ttf
827 || QFileInfo(fnt->fileName).size() > ttfMaxFileSize)) // hard limit
828 return;
829
830// Using ttfCanSymbianLoadFont() causes crashes on app destruction (Symbian^3|PR1 and lower).
831// Therefore, not using it for now, but eventually in a later version.
832// if (!ttfCanSymbianLoadFont(fnt->data, fnt->fileName))
833// return;
834
835 QFontDatabasePrivate *db = privateDb();
836 if (!db)
837 return;
838
839 if (!db->count)
840 initializeDb();
841
842 QSymbianFontDatabaseExtrasImplementation *dbExtras =
843 static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
844 if (!dbExtras)
845 return;
846
847 const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
848
849 // The QTemporaryFile object being used in the following section must be
850 // destructed before letting Symbian load the TTF file. Symbian would not
851 // load it otherwise, because QTemporaryFile will still keep some handle
852 // on it. The scope is used to reduce the life time of the QTemporaryFile.
853 // In order to prevent other processes from modifying the file between the
854 // moment where the QTemporaryFile is destructed and the file is loaded by
855 // Symbian, we have a QFile "tempFileGuard" outside the scope which opens
856 // the file in ReadOnly mode while the QTemporaryFile is still alive.
857 QFile tempFileGuard;
858 {
859 QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
860 + marker + QLatin1String("XXXXXX.ttf"));
861 if (!tempfile.open())
862 return;
863 const QString tempFileName = QFileInfo(tempfile).canonicalFilePath();
864 if (fnt->data.isEmpty()) {
865 QFile sourceFile(fnt->fileName);
866 if (!sourceFile.open(QIODevice::ReadOnly))
867 return;
868 fnt->data = sourceFile.readAll();
869 }
870 if (!ttfMarkAppFont(fnt->data, marker) || tempfile.write(fnt->data) == -1)
871 return;
872 tempfile.setAutoRemove(false);
873 tempfile.close(); // Tempfile still keeps a file handle, forbidding write access
874 fnt->data.clear(); // The TTF data was marked and saved. Not needed in memory, anymore.
875 tempFileGuard.setFileName(tempFileName);
876 if (!tempFileGuard.open(QIODevice::ReadOnly))
877 return;
878 fnt->temporaryFileName = tempFileName;
879 }
880
881 const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName);
882 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
883 const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer();
884 const TInt err =
885 S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId);
886 tempFileGuard.close(); // Did its job
887 const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer();
888 if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device?
889 int fontOnServerIndex = fontsOnServerAfter.count() - 1;
890 for (int i = 0; i < fontsOnServerBefore.count(); i++) {
891 if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) {
892 fontOnServerIndex = i;
893 break;
894 }
895 }
896
897 // Must remove all font engines with their CFonts, first.
898 QFontCache::instance()->clear();
899 db->free();
900 QSymbianFontDatabaseExtrasImplementation::clear();
901
902 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable())
903 fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName));
904
905 const QString &appFontName = fontsOnServerAfter.at(fontOnServerIndex);
906 fnt->families.append(qt_symbian_appFontNameWithoutMarker(appFontName));
907 if (!qt_symbian_fontNameHasAppFontMarker(appFontName)
908 || !registerScreenDeviceFont(fontOnServerIndex, dbExtras))
909 dbExtras->removeAppFontData(fnt);
910 } else {
911 if (fnt->screenDeviceFontFileId > 0)
912 S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); // May still have the file open!
913 QFile::remove(fnt->temporaryFileName);
914 *fnt = QFontDatabasePrivate::ApplicationFont();
915 }
916 lock.relock();
917}
918
919bool QFontDatabase::removeApplicationFont(int handle)
920{
921 QMutexLocker locker(fontDatabaseMutex());
922
923 QFontDatabasePrivate *db = privateDb();
924 if (!db || handle < 0 || handle >= db->applicationFonts.count())
925 return false;
926 QSymbianFontDatabaseExtrasImplementation *dbExtras =
927 static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
928 if (!dbExtras)
929 return false;
930
931 QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle];
932 if (fnt->families.isEmpty())
933 return true; // Nothing to remove. Return peacefully.
934
935 // Must remove all font engines with their CFonts, first
936 QFontCache::instance()->clear();
937 db->free();
938 dbExtras->removeAppFontData(fnt);
939
940 db->invalidate(); // This will just emit 'fontDatabaseChanged()'
941 return true;
942}
943
944bool QFontDatabase::removeAllApplicationFonts()
945{
946 QMutexLocker locker(fontDatabaseMutex());
947
948 const int applicationFontsCount = privateDb()->applicationFonts.count();
949 for (int i = 0; i < applicationFontsCount; ++i)
950 if (!removeApplicationFont(i))
951 return false;
952 return true;
953}
954
955bool QFontDatabase::supportsThreadedFontRendering()
956{
957 return false;
958}
959
960static
961QFontDef cleanedFontDef(const QFontDef &req)
962{
963 QFontDef result = req;
964 if (result.pixelSize <= 0) {
965 result.pixelSize = QFontEngineS60::pointsToPixels(qMax(qreal(1.0), result.pointSize));
966 result.pointSize = 0;
967 }
968 return result;
969}
970
971QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QFontDef &req)
972{
973 const QFontCache::Key key(cleanedFontDef(req), script);
974
975 if (!privateDb()->count)
976 initializeDb();
977
978 QFontEngine *fe = QFontCache::instance()->findEngine(key);
979 if (!fe) {
980 // Making sure that fe->fontDef.family will be an existing font.
981 initializeDb();
982 QFontDatabasePrivate *db = privateDb();
983 QtFontDesc desc;
984 QList<int> blacklistedFamilies;
985 match(script, key.def, key.def.family, QString(), -1, &desc, blacklistedFamilies);
986 if (!desc.family) // falling back to application font
987 desc.family = db->family(QApplication::font().defaultFamily());
988 Q_ASSERT(desc.family);
989
990 // Making sure that desc.family supports the requested script
991 QtFontDesc mappedDesc;
992 bool supportsScript = false;
993 do {
994 match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
995 if (mappedDesc.family == desc.family) {
996 supportsScript = true;
997 break;
998 }
999 blacklistedFamilies.append(mappedDesc.familyIndex);
1000 } while (mappedDesc.family);
1001 if (!supportsScript) {
1002 blacklistedFamilies.clear();
1003 match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
1004 if (mappedDesc.family)
1005 desc = mappedDesc;
1006 }
1007
1008 const QString fontFamily = desc.family->name;
1009 QFontDef request = req;
1010 request.family = fontFamily;
1011#ifdef QT_NO_FREETYPE
1012 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
1013 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
1014 const QSymbianTypeFaceExtras *typeFaceExtras =
1015 dbExtras->extras(fontFamily, request.weight > QFont::Normal, request.style != QFont::StyleNormal);
1016
1017 // We need a valid pixelSize, e.g. for lineThickness()
1018 if (request.pixelSize < 0)
1019 request.pixelSize = request.pointSize * d->dpi / 72;
1020
1021 fe = new QFontEngineS60(request, typeFaceExtras);
1022#else // QT_NO_FREETYPE
1023 Q_UNUSED(d)
1024 QFontEngine::FaceId faceId;
1025 const QtFontFamily * const reqQtFontFamily = db->family(fontFamily);
1026 faceId.filename = reqQtFontFamily->fontFilename;
1027 faceId.index = reqQtFontFamily->fontFileIndex;
1028
1029 QFontEngineFTS60 *fte = new QFontEngineFTS60(cleanedFontDef(request));
1030 if (fte->init(faceId, true, QFontEngineFT::Format_A8))
1031 fe = fte;
1032 else
1033 delete fte;
1034#endif // QT_NO_FREETYPE
1035
1036 Q_ASSERT(fe);
1037 if (script == QUnicodeTables::Common
1038 && !(req.styleStrategy & QFont::NoFontMerging)
1039 && !fe->symbol) {
1040
1041 QStringList commonFonts;
1042 for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
1043 if (scriptForWritingSystem[ws] != script)
1044 continue;
1045 for (int i = 0; i < db->count; ++i) {
1046 if (db->families[i]->writingSystems[ws] & QtFontFamily::Supported)
1047 commonFonts.append(db->families[i]->name);
1048 }
1049 }
1050
1051 // Hack: Prioritize .ccc fonts
1052 const QString niceEastAsianFont(QLatin1String("Sans MT 936_S60"));
1053 if (commonFonts.removeAll(niceEastAsianFont) > 0)
1054 commonFonts.prepend(niceEastAsianFont);
1055
1056 fe = new QFontEngineMultiS60(fe, script, commonFonts);
1057 }
1058 }
1059 fe->ref.ref();
1060 QFontCache::instance()->insertEngine(key, fe);
1061 return fe;
1062}
1063
1064void QFontDatabase::load(const QFontPrivate *d, int script)
1065{
1066 QFontEngine *fe = 0;
1067 QFontDef req = d->request;
1068
1069 if (!d->engineData) {
1070 const QFontCache::Key key(cleanedFontDef(req), script);
1071 getEngineData(d, key);
1072 }
1073
1074 // the cached engineData could have already loaded the engine we want
1075 if (d->engineData->engines[script])
1076 fe = d->engineData->engines[script];
1077
1078 if (!fe) {
1079 if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
1080 fe = new QTestFontEngine(req.pixelSize);
1081 fe->fontDef = req;
1082 } else {
1083 fe = findFont(script, d, req);
1084 }
1085 d->engineData->engines[script] = fe;
1086 }
1087}
1088
1089QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.