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