Do not load aplication fonts on Symbian^1 and below
[qt:qt.git] / src / gui / text / qfontdatabase_s60.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <private/qapplication_p.h>
43 #include "qdir.h"
44 #include "qfont_p.h"
45 #include "qfontengine_s60_p.h"
46 #include "qabstractfileengine.h"
47 #include "qdesktopservices.h"
48 #include "qtemporaryfile.h"
49 #include <private/qpixmap_s60_p.h>
50 #include <private/qt_s60_p.h>
51 #include "qendian.h"
52 #include <private/qcore_symbian_p.h>
53 #ifdef QT_NO_FREETYPE
54 #include <openfont.h>
55 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
56 #include <graphics/openfontrasterizer.h> // COpenFontRasterizer has moved to a new header file
57 #endif // SYMBIAN_ENABLE_SPLIT_HEADERS
58 #endif // QT_NO_FREETYPE
59
60 QT_BEGIN_NAMESPACE
61
62 QStringList qt_symbian_fontFamiliesOnFontServer() // Also used in qfont_s60.cpp
63 {
64     QStringList result;
65     QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
66     const int numTypeFaces = S60->screenDevice()->NumTypefaces();
67     for (int i = 0; i < numTypeFaces; i++) {
68         TTypefaceSupport typefaceSupport;
69         S60->screenDevice()->TypefaceSupport(typefaceSupport, i);
70         const QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
71         result.append(familyName);
72     }
73     lock.relock();
74     return result;
75 }
76
77 QFileInfoList alternativeFilePaths(const QString &path, const QStringList &nameFilters,
78     QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort,
79     bool uniqueFileNames = true)
80 {
81     QFileInfoList result;
82
83     // Prepare a 'soft to hard' drive list: W:, X: ... A:, Z:
84     QStringList driveStrings;
85     foreach (const QFileInfo &drive, QDir::drives())
86         driveStrings.append(drive.absolutePath());
87     driveStrings.sort();
88     const QString zDriveString(QLatin1String("Z:/"));
89     driveStrings.removeAll(zDriveString);
90     driveStrings.prepend(zDriveString);
91
92     QStringList uniqueFileNameList;
93     for (int i = driveStrings.count() - 1; i >= 0; --i) {
94         const QDir dirOnDrive(driveStrings.at(i) + path);
95         const QFileInfoList entriesOnDrive = dirOnDrive.entryInfoList(nameFilters, filters, sort);
96         if (uniqueFileNames) {
97             foreach(const QFileInfo &entry, entriesOnDrive) {
98                 if (!uniqueFileNameList.contains(entry.fileName())) {
99                     uniqueFileNameList.append(entry.fileName());
100                     result.append(entry);
101                 }
102             }
103         } else {
104             result.append(entriesOnDrive);
105         }
106     }
107     return result;
108 }
109
110 #ifdef QT_NO_FREETYPE
111 class QSymbianFontDatabaseExtrasImplementation : public QSymbianFontDatabaseExtras
112 {
113 public:
114     QSymbianFontDatabaseExtrasImplementation();
115     ~QSymbianFontDatabaseExtrasImplementation();
116
117     const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const;
118     void removeAppFontData(QFontDatabasePrivate::ApplicationFont *fnt);
119     static inline bool appFontLimitReached();
120     TUid addFontFileToFontStore(const QFileInfo &fontFileInfo);
121     static void clear();
122
123     static inline QString tempAppFontFolder();
124     static const QString appFontMarkerPrefix;
125     static QString appFontMarker(); // 'qaf<shortUid[+shortPid]>'
126
127     struct CFontFromFontStoreReleaser {
128         static inline void cleanup(CFont *font)
129         {
130             if (!font)
131                 return;
132             const QSymbianFontDatabaseExtrasImplementation *dbExtras =
133                     static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras);
134             dbExtras->m_store->ReleaseFont(font);
135         }
136     };
137
138     struct CFontFromScreenDeviceReleaser {
139         static inline void cleanup(CFont *font)
140         {
141             if (!font)
142                 return;
143             S60->screenDevice()->ReleaseFont(font);
144         }
145     };
146
147 // m_heap, m_store, m_rasterizer and m_extras are used if Symbian
148 // does not provide the Font Table API
149     RHeap* m_heap;
150     CFontStore *m_store;
151     COpenFontRasterizer *m_rasterizer;
152     mutable QList<const QSymbianTypeFaceExtras *> m_extras;
153
154     mutable QHash<QString, const QSymbianTypeFaceExtras *> m_extrasHash;
155 };
156
157 const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix =
158         QLatin1String("qaf");
159
160 inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
161 {
162     return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\');
163 }
164
165 QString QSymbianFontDatabaseExtrasImplementation::appFontMarker()
166 {
167     static QString result;
168     if (result.isEmpty()) {
169         const quint32 uid = RProcess().Type().MostDerived().iUid;
170         quint16 crossSum = static_cast<quint16>(uid + (uid >> 16));
171         if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
172             // If no font table Api is available, we must not even load a font
173             // from a previous (crashed) run of this application. Reason: we
174             // won't get the font tables, they are not in the CFontStore.
175             // So, we add the pid to the uniqueness of the marker.
176             const quint32 pid = static_cast<quint32>(RProcess().Id().Id());
177             crossSum += static_cast<quint16>(pid + (pid >> 16));
178         }
179         result = appFontMarkerPrefix + QString::number(crossSum, 16);
180     }
181     return result;
182 }
183
184 QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation()
185 {
186     if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
187         QStringList filters;
188         filters.append(QLatin1String("*.ttf"));
189         filters.append(QLatin1String("*.ccc"));
190         filters.append(QLatin1String("*.ltt"));
191         const QFileInfoList fontFiles = alternativeFilePaths(QLatin1String("resource\\Fonts"), filters);
192
193         const TInt heapMinLength = 0x1000;
194         const TInt heapMaxLength = qMax(0x20000 * fontFiles.count(), heapMinLength);
195         m_heap = User::ChunkHeap(NULL, heapMinLength, heapMaxLength);
196         QT_TRAP_THROWING(
197             m_store = CFontStore::NewL(m_heap);
198             m_rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
199             CleanupStack::PushL(m_rasterizer);
200             m_store->InstallRasterizerL(m_rasterizer);
201             CleanupStack::Pop(m_rasterizer););
202
203         foreach (const QFileInfo &fontFileInfo, fontFiles)
204             addFontFileToFontStore(fontFileInfo);
205     }
206 }
207
208 void QSymbianFontDatabaseExtrasImplementation::clear()
209 {
210     QFontDatabasePrivate *db = privateDb();
211     if (!db)
212         return;
213     const QSymbianFontDatabaseExtrasImplementation *dbExtras =
214             static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
215     if (!dbExtras)
216         return; // initializeDb() has never been called
217     if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
218         qDeleteAll(dbExtras->m_extrasHash);
219     } else {
220         typedef QList<const QSymbianTypeFaceExtras *>::iterator iterator;
221         for (iterator p = dbExtras->m_extras.begin(); p != dbExtras->m_extras.end(); ++p) {
222             dbExtras->m_store->ReleaseFont((*p)->fontOwner());
223             delete *p;
224         }
225         dbExtras->m_extras.clear();
226     }
227     dbExtras->m_extrasHash.clear();
228 }
229
230 void qt_cleanup_symbianFontDatabase()
231 {
232     QFontDatabasePrivate *db = privateDb();
233     if (!db)
234         return;
235
236     QSymbianFontDatabaseExtrasImplementation::clear();
237
238     if (!db->applicationFonts.isEmpty()) {
239         QFontDatabase::removeAllApplicationFonts();
240         // We remove the left over temporary font files of Qt application.
241         // Active fonts are undeletable since the font server holds a handle
242         // on them, so we do not need to worry to delete other running
243         // applications' fonts.
244         const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder());
245         const QStringList filter(
246                 QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf"));
247         foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter))
248             QFile(ttfFile.absoluteFilePath()).remove();
249         db->applicationFonts.clear();
250     }
251 }
252
253 QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation()
254 {
255     qt_cleanup_symbianFontDatabase();
256     if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
257         delete m_store;
258         m_heap->Close();
259     }
260 }
261
262 #ifndef FNTSTORE_H_INLINES_SUPPORT_FMM
263 /*
264  Workaround: fntstore.h has an inlined function 'COpenFont* CBitmapFont::OpenFont()'
265  that returns a private data member. The header will change between SDKs. But Qt has
266  to build on any SDK version and run on other versions of Symbian OS.
267  This function performs the needed pointer arithmetic to get the right COpenFont*
268 */
269 COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont)
270 {
271     const TInt offsetIOpenFont = 92; // '_FOFF(CBitmapFont, iOpenFont)' ..if iOpenFont weren't private
272     const TUint valueIOpenFont = *(TUint*)PtrAdd(aBitmapFont, offsetIOpenFont);
273     return (valueIOpenFont & 1) ?
274             (COpenFont*)PtrAdd(aBitmapFont, valueIOpenFont & ~1) : // New behavior: iOpenFont is offset
275             (COpenFont*)valueIOpenFont; // Old behavior: iOpenFont is pointer
276 }
277 #endif // FNTSTORE_H_INLINES_SUPPORT_FMM
278
279 const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &typeface,
280                                                                                bool bold, bool italic) const
281 {
282     const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic));
283     if (!m_extrasHash.contains(searchKey)) {
284         TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1);
285         if (bold)
286             searchSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold);
287         if (italic)
288             searchSpec.iFontStyle.SetPosture(EPostureItalic);
289
290         CFont* font = NULL;
291         if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
292             const TInt err = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(font, searchSpec);
293             Q_ASSERT(err == KErrNone && font);
294             QScopedPointer<CFont, CFontFromScreenDeviceReleaser> sFont(font);
295             QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font);
296             sFont.take();
297             m_extrasHash.insert(searchKey, extras);
298         } else {
299             const TInt err = m_store->GetNearestFontToDesignHeightInPixels(font, searchSpec);
300             Q_ASSERT(err == KErrNone && font);
301             const CBitmapFont *bitmapFont = static_cast<CBitmapFont*>(font);
302             COpenFont *openFont =
303 #ifdef FNTSTORE_H_INLINES_SUPPORT_FMM
304                 bitmapFont->OpenFont();
305 #else // FNTSTORE_H_INLINES_SUPPORT_FMM
306                 OpenFontFromBitmapFont(bitmapFont);
307 #endif // FNTSTORE_H_INLINES_SUPPORT_FMM
308             const TOpenFontFaceAttrib* const attrib = openFont->FaceAttrib();
309             const QString foundKey =
310                     QString((const QChar*)attrib->FullName().Ptr(), attrib->FullName().Length());
311             if (!m_extrasHash.contains(foundKey)) {
312                 QScopedPointer<CFont, CFontFromFontStoreReleaser> sFont(font);
313                 QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font, openFont);
314                 sFont.take();
315                 m_extras.append(extras);
316                 m_extrasHash.insert(searchKey, extras);
317                 m_extrasHash.insert(foundKey, extras);
318             } else {
319                 m_store->ReleaseFont(font);
320                 m_extrasHash.insert(searchKey, m_extrasHash.value(foundKey));
321             }
322         }
323     }
324     return m_extrasHash.value(searchKey);
325 }
326
327 void QSymbianFontDatabaseExtrasImplementation::removeAppFontData(
328     QFontDatabasePrivate::ApplicationFont *fnt)
329 {
330     clear();
331     if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()
332             && fnt->fontStoreFontFileUid.iUid != 0)
333         m_store->RemoveFile(fnt->fontStoreFontFileUid);
334     if (fnt->screenDeviceFontFileId != 0)
335         S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId);
336     QFile::remove(fnt->temporaryFileName);
337     *fnt = QFontDatabasePrivate::ApplicationFont();
338 }
339
340 bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
341 {
342     QFontDatabasePrivate *db = privateDb();
343     if (!db)
344         return false;
345     const int maxAppFonts = 5;
346     int registeredAppFonts = 0;
347     foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts)
348         if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts)
349             return true;
350     return false;
351 }
352
353 TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo)
354 {
355     Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable());
356     const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath());
357     const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile));
358     TUid fontUid = {0};
359     TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr));
360     return fontUid;
361 }
362
363 #else // QT_NO_FREETYPE
364 class QFontEngineFTS60 : public QFontEngineFT
365 {
366 public:
367     QFontEngineFTS60(const QFontDef &fd);
368 };
369
370 QFontEngineFTS60::QFontEngineFTS60(const QFontDef &fd)
371     : QFontEngineFT(fd)
372 {
373     default_hint_style = HintFull;
374 }
375 #endif // QT_NO_FREETYPE
376
377 /*
378  QFontEngineS60::pixelsToPoints, QFontEngineS60::pointsToPixels, QFontEngineMultiS60::QFontEngineMultiS60
379  and QFontEngineMultiS60::QFontEngineMultiS60 should be in qfontengine_s60.cpp. But since also the
380  Freetype based font rendering need them, they are here.
381 */
382 qreal QFontEngineS60::pixelsToPoints(qreal pixels, Qt::Orientation orientation)
383 {
384     CWsScreenDevice* device = S60->screenDevice();
385     return (orientation == Qt::Horizontal?
386         device->HorizontalPixelsToTwips(pixels)
387         :device->VerticalPixelsToTwips(pixels)) / KTwipsPerPoint;
388 }
389
390 qreal QFontEngineS60::pointsToPixels(qreal points, Qt::Orientation orientation)
391 {
392     CWsScreenDevice* device = S60->screenDevice();
393     const int twips = points * KTwipsPerPoint;
394     return orientation == Qt::Horizontal?
395         device->HorizontalTwipsToPixels(twips)
396         :device->VerticalTwipsToPixels(twips);
397 }
398
399 QFontEngineMultiS60::QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies)
400     : QFontEngineMulti(fallbackFamilies.size() + 1)
401     , m_script(script)
402     , m_fallbackFamilies(fallbackFamilies)
403 {
404     engines[0] = first;
405     first->ref.ref();
406     fontDef = engines[0]->fontDef;
407 }
408
409 void QFontEngineMultiS60::loadEngine(int at)
410 {
411     Q_ASSERT(at < engines.size());
412     Q_ASSERT(engines.at(at) == 0);
413
414     QFontDef request = fontDef;
415     request.styleStrategy |= QFont::NoFontMerging;
416     request.family = m_fallbackFamilies.at(at-1);
417     engines[at] = QFontDatabase::findFont(m_script,
418                                           /*fontprivate*/0,
419                                           request);
420     Q_ASSERT(engines[at]);
421 }
422
423 static bool registerScreenDeviceFont(int screenDeviceFontIndex,
424                                      const QSymbianFontDatabaseExtrasImplementation *dbExtras)
425 {
426     TTypefaceSupport typefaceSupport;
427         S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex);
428     CFont *font; // We have to get a font instance in order to know all the details
429     TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11);
430     if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone)
431         return false;
432     QScopedPointer<CFont, QSymbianFontDatabaseExtrasImplementation::CFontFromScreenDeviceReleaser> sFont(font);
433     if (font->TypeUid() != KCFbsFontUid)
434         return false;
435     TOpenFontFaceAttrib faceAttrib;
436     const CFbsFont *cfbsFont = static_cast<const CFbsFont *>(font);
437     cfbsFont->GetFaceAttrib(faceAttrib);
438
439     QtFontStyle::Key styleKey;
440     styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal;
441     styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal;
442
443     QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
444     QtFontFamily *family = privateDb()->family(familyName, true);
445     family->fixedPitch = faceAttrib.IsMonoWidth();
446     QtFontFoundry *foundry = family->foundry(QString(), true);
447     QtFontStyle *style = foundry->style(styleKey, true);
448     style->smoothScalable = typefaceSupport.iIsScalable;
449     style->pixelSize(0, true);
450
451     const QSymbianTypeFaceExtras *typeFaceExtras =
452             dbExtras->extras(familyName, faceAttrib.IsBold(), faceAttrib.IsItalic());
453     const QByteArray os2Table = typeFaceExtras->getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
454     const unsigned char* data = reinterpret_cast<const unsigned char*>(os2Table.constData());
455     const unsigned char* ulUnicodeRange = data + 42;
456     quint32 unicodeRange[4] = {
457         qFromBigEndian<quint32>(ulUnicodeRange),
458         qFromBigEndian<quint32>(ulUnicodeRange + 4),
459         qFromBigEndian<quint32>(ulUnicodeRange + 8),
460         qFromBigEndian<quint32>(ulUnicodeRange + 12)
461     };
462     const unsigned char* ulCodePageRange = data + 78;
463     quint32 codePageRange[2] = {
464         qFromBigEndian<quint32>(ulCodePageRange),
465         qFromBigEndian<quint32>(ulCodePageRange + 4)
466     };
467     const QList<QFontDatabase::WritingSystem> writingSystems =
468         determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
469     foreach (const QFontDatabase::WritingSystem system, writingSystems)
470         family->writingSystems[system] = QtFontFamily::Supported;
471     return true;
472 }
473
474 static void initializeDb()
475 {
476     QFontDatabasePrivate *db = privateDb();
477     if(!db || db->count)
478         return;
479
480 #ifdef QT_NO_FREETYPE
481     if (!db->symbianExtras)
482         db->symbianExtras = new QSymbianFontDatabaseExtrasImplementation;
483
484     QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
485
486     const int numTypeFaces = S60->screenDevice()->NumTypefaces();
487     const QSymbianFontDatabaseExtrasImplementation *dbExtras =
488             static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
489     for (int i = 0; i < numTypeFaces; i++)
490         registerScreenDeviceFont(i, dbExtras);
491
492     // We have to clear/release all CFonts, here, in case one of the fonts is
493     // an application font of another running Qt app. Otherwise the other Qt app
494     // cannot remove it's application font, anymore -> "Zombie Font".
495     QSymbianFontDatabaseExtrasImplementation::clear();
496
497     lock.relock();
498
499 #else // QT_NO_FREETYPE
500     QDir dir(QDesktopServices::storageLocation(QDesktopServices::FontsLocation));
501     dir.setNameFilters(QStringList() << QLatin1String("*.ttf")
502                        << QLatin1String("*.ttc") << QLatin1String("*.pfa")
503                        << QLatin1String("*.pfb"));
504     for (int i = 0; i < int(dir.count()); ++i) {
505         const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i]));
506         db->addTTFile(file);
507     }
508 #endif // QT_NO_FREETYPE
509 }
510
511 static inline void load(const QString &family = QString(), int script = -1)
512 {
513     Q_UNUSED(family)
514     Q_UNUSED(script)
515     initializeDb();
516 }
517
518 static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
519 {
520     if (QSysInfo::symbianVersion() <= QSysInfo::SV_SF_2)
521         return; // See QTBUG-16514 for what 'font collisions' can cause in Symbian^1 and lower
522
523     if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached())
524         return;
525
526     QFontDatabasePrivate *db = privateDb();
527     if (!db)
528         return;
529
530     if (!db->count)
531         initializeDb();
532
533     if (fnt->data.isEmpty() && !fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive))
534         return; // Only buffer or .ttf
535     QSymbianFontDatabaseExtrasImplementation *dbExtras =
536             static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
537     if (!dbExtras)
538         return;
539
540     // The QTemporaryFile object being used in the following section must be
541     // destructed before letting Symbian load the TTF file. Symbian would not
542     // load it otherwise, because QTemporaryFile will still keep some handle
543     // on it. The scope is used to reduce the life time of the QTemporaryFile.
544     // In order to prevent other processes from modifying the file between the
545     // moment where the QTemporaryFile is destructed and the file is loaded by
546     // Symbian, we have a QFile "tempFileGuard" outside the scope which opens
547     // the file in ReadOnly mode while the QTemporaryFile is still alive.
548     QFile tempFileGuard;
549     {
550         QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
551                                 + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
552                                 + QLatin1String("XXXXXX.ttf"));
553         if (!tempfile.open())
554             return;
555         const QString tempFileName = QFileInfo(tempfile).canonicalFilePath();
556         if (fnt->data.isEmpty()) {
557             QFile sourceFile(fnt->fileName);
558             if (!sourceFile.open(QIODevice::ReadOnly))
559                 return;
560             fnt->data = sourceFile.readAll();
561         }
562         if (tempfile.write(fnt->data) == -1)
563             return;
564         tempfile.setAutoRemove(false);
565         tempfile.close(); // Tempfile still keeps a file handle, forbidding write access
566         tempFileGuard.setFileName(tempFileName);
567         if (!tempFileGuard.open(QIODevice::ReadOnly))
568             return;
569         fnt->temporaryFileName = tempFileName;
570     }
571
572     const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName);
573     QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
574     const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer();
575     const TInt err =
576             S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId);
577     tempFileGuard.close(); // Did its job
578     const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer();
579     if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device?
580         int fontOnServerIndex = fontsOnServerAfter.count() - 1;
581         for (int i = 0; i < fontsOnServerBefore.count(); i++) {
582             if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) {
583                 fontOnServerIndex = i;
584                 break;
585             }
586         }
587
588         // Must remove all font engines with their CFonts, first.
589         QFontCache::instance()->clear();
590         db->free();
591         QSymbianFontDatabaseExtrasImplementation::clear();
592
593         if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable())
594             fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName));
595
596         fnt->families.append(fontsOnServerAfter.at(fontOnServerIndex));
597         if (!registerScreenDeviceFont(fontOnServerIndex, dbExtras))
598             dbExtras->removeAppFontData(fnt);
599     } else {
600         QFile::remove(fnt->temporaryFileName);
601         *fnt = QFontDatabasePrivate::ApplicationFont();
602     }
603     lock.relock();
604 }
605
606 bool QFontDatabase::removeApplicationFont(int handle)
607 {
608     QMutexLocker locker(fontDatabaseMutex());
609
610     QFontDatabasePrivate *db = privateDb();
611     if (!db || handle < 0 || handle >= db->applicationFonts.count())
612         return false;
613     QSymbianFontDatabaseExtrasImplementation *dbExtras =
614             static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
615     if (!dbExtras)
616         return false;
617
618     QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle];
619     if (fnt->families.isEmpty())
620         return true; // Nothing to remove. Return peacefully.
621
622     // Must remove all font engines with their CFonts, first
623     QFontCache::instance()->clear();
624     db->free();
625     dbExtras->removeAppFontData(fnt);
626
627     db->invalidate(); // This will just emit 'fontDatabaseChanged()'
628     return true;
629 }
630
631 bool QFontDatabase::removeAllApplicationFonts()
632 {
633     QMutexLocker locker(fontDatabaseMutex());
634
635     const int applicationFontsCount = privateDb()->applicationFonts.count();
636     for (int i = 0; i < applicationFontsCount; ++i)
637         if (!removeApplicationFont(i))
638             return false;
639     return true;
640 }
641
642 bool QFontDatabase::supportsThreadedFontRendering()
643 {
644     return false;
645 }
646
647 static
648 QFontDef cleanedFontDef(const QFontDef &req)
649 {
650     QFontDef result = req;
651     if (result.pixelSize <= 0) {
652         result.pixelSize = QFontEngineS60::pointsToPixels(qMax(qreal(1.0), result.pointSize));
653         result.pointSize = 0;
654     }
655     return result;
656 }
657
658 QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QFontDef &req)
659 {
660     const QFontCache::Key key(cleanedFontDef(req), script);
661
662     if (!privateDb()->count)
663         initializeDb();
664
665     QFontEngine *fe = QFontCache::instance()->findEngine(key);
666     if (!fe) {
667         // Making sure that fe->fontDef.family will be an existing font.
668         initializeDb();
669         QFontDatabasePrivate *db = privateDb();
670         QtFontDesc desc;
671         QList<int> blacklistedFamilies;
672         match(script, req, req.family, QString(), -1, &desc, blacklistedFamilies);
673         if (!desc.family) // falling back to application font
674             desc.family = db->family(QApplication::font().defaultFamily());
675         Q_ASSERT(desc.family);
676
677         // Making sure that desc.family supports the requested script
678         QtFontDesc mappedDesc;
679         bool supportsScript = false;
680         do {
681             match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
682             if (mappedDesc.family == desc.family) {
683                 supportsScript = true;
684                 break;
685             }
686             blacklistedFamilies.append(mappedDesc.familyIndex);
687         } while (mappedDesc.family);
688         if (!supportsScript) {
689             blacklistedFamilies.clear();
690             match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
691             if (mappedDesc.family)
692                 desc = mappedDesc;
693         }
694
695         const QString fontFamily = desc.family->name;
696         QFontDef request = req;
697         request.family = fontFamily;
698 #ifdef QT_NO_FREETYPE
699         const QSymbianFontDatabaseExtrasImplementation *dbExtras =
700                 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
701         const QSymbianTypeFaceExtras *typeFaceExtras =
702                 dbExtras->extras(fontFamily, request.weight > QFont::Normal, request.style != QFont::StyleNormal);
703
704         // We need a valid pixelSize, e.g. for lineThickness()
705         if (request.pixelSize < 0)
706             request.pixelSize = request.pointSize * d->dpi / 72;
707
708         fe = new QFontEngineS60(request, typeFaceExtras);
709 #else // QT_NO_FREETYPE
710         Q_UNUSED(d)
711         QFontEngine::FaceId faceId;
712         const QtFontFamily * const reqQtFontFamily = db->family(fontFamily);
713         faceId.filename = reqQtFontFamily->fontFilename;
714         faceId.index = reqQtFontFamily->fontFileIndex;
715
716         QFontEngineFTS60 *fte = new QFontEngineFTS60(cleanedFontDef(request));
717         if (fte->init(faceId, true, QFontEngineFT::Format_A8))
718             fe = fte;
719         else
720             delete fte;
721 #endif // QT_NO_FREETYPE
722
723         Q_ASSERT(fe);
724         if (script == QUnicodeTables::Common
725             && !(req.styleStrategy & QFont::NoFontMerging)
726             && !fe->symbol) {
727
728             QStringList commonFonts;
729             for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
730                 if (scriptForWritingSystem[ws] != script)
731                     continue;
732                 for (int i = 0; i < db->count; ++i) {
733                     if (db->families[i]->writingSystems[ws] & QtFontFamily::Supported)
734                         commonFonts.append(db->families[i]->name);
735                 }
736             }
737
738             // Hack: Prioritize .ccc fonts
739             const QString niceEastAsianFont(QLatin1String("Sans MT 936_S60"));
740             if (commonFonts.removeAll(niceEastAsianFont) > 0)
741                 commonFonts.prepend(niceEastAsianFont);
742
743             fe = new QFontEngineMultiS60(fe, script, commonFonts);
744         }
745     }
746     fe->ref.ref();
747     QFontCache::instance()->insertEngine(key, fe);
748     return fe;
749 }
750
751 void QFontDatabase::load(const QFontPrivate *d, int script)
752 {
753     QFontEngine *fe = 0;
754     QFontDef req = d->request;
755
756     if (!d->engineData) {
757         const QFontCache::Key key(cleanedFontDef(req), script);
758         getEngineData(d, key);
759     }
760
761     // the cached engineData could have already loaded the engine we want
762     if (d->engineData->engines[script])
763         fe = d->engineData->engines[script];
764
765     if (!fe) {
766         if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
767             fe = new QTestFontEngine(req.pixelSize);
768             fe->fontDef = req;
769         } else {
770             fe = findFont(script, d, req);
771         }
772         d->engineData->engines[script] = fe;
773     }
774 }
775
776 QT_END_NAMESPACE