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