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