Temporary fix for ambiguous cast from four letter char constant
[qt:qt.git] / src / gui / text / qfontdatabase_s60.cpp
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 ** 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 <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
61 QT_BEGIN_NAMESPACE
62
63 QStringList 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
78 QFileInfoList 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
112 class QSymbianFontDatabaseExtrasImplementation : public QSymbianFontDatabaseExtras
113 {
114 public:
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
159 const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix =
160         QLatin1String("Q");
161
162 inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
163 {
164     return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\');
165 }
166
167 QString 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
190 static 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
208 QString 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
219 static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName)
220 {
221     return markedFontName.left(markedFontName.length()
222                                - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length());
223 }
224
225 QSymbianFontDatabaseExtrasImplementation::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
249 void 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
271 void 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
294 QSymbianFontDatabaseExtrasImplementation::~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 */
310 COpenFont* 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
320 const 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
369 void 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
384 bool 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
397 TUid 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
408 class QFontEngineFTS60 : public QFontEngineFT
409 {
410 public:
411     QFontEngineFTS60(const QFontDef &fd);
412 };
413
414 QFontEngineFTS60::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 */
426 qreal 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
434 qreal 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
443 QFontEngineMultiS60::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
453 void 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
467 static 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
529 static 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
566 static inline void load(const QString &family = QString(), int script = -1)
567 {
568     Q_UNUSED(family)
569     Q_UNUSED(script)
570     initializeDb();
571 }
572
573 struct OffsetTable {
574     quint32 sfntVersion;
575     quint16 numTables, searchRange, entrySelector, rangeShift;
576 };
577
578 struct TableRecord {
579     quint32 tag, checkSum, offset, length;
580 };
581
582 struct NameTableHead {
583     quint16 format, count, stringOffset;
584 };
585
586 struct NameRecord {
587     quint16 platformID, encodingID, languageID, nameID, length, offset;
588 };
589
590 static 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
603 static inline quint32 toDWordBoundary(quint32 value)
604 {
605     return (value + 3) & ~3;
606 }
607
608 static inline quint32 dWordPadding(quint32 value)
609 {
610     return (4 - (value & 3)) & 3;
611 }
612
613 static 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
673 const quint32 ttfMaxFileSize = 3500000;
674
675 static 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
773 static 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
822 static 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
919 bool 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
944 bool 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
955 bool QFontDatabase::supportsThreadedFontRendering()
956 {
957     return false;
958 }
959
960 static
961 QFontDef 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
971 QFontEngine *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
1064 void 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
1089 QT_END_NAMESPACE