Supporting Qt application fonts on Symbian
[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 (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached())
521         return;
522
523     QFontDatabasePrivate *db = privateDb();
524     if (!db)
525         return;
526
527     if (!db->count)
528         initializeDb();
529
530     if (fnt->data.isEmpty() && !fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive))
531         return; // Only buffer or .ttf
532     QSymbianFontDatabaseExtrasImplementation *dbExtras =
533             static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
534     if (!dbExtras)
535         return;
536
537     // The QTemporaryFile object being used in the following section must be
538     // destructed before letting Symbian load the TTF file. Symbian would not
539     // load it otherwise, because QTemporaryFile will still keep some handle
540     // on it. The scope is used to reduce the life time of the QTemporaryFile.
541     // In order to prevent other processes from modifying the file between the
542     // moment where the QTemporaryFile is destructed and the file is loaded by
543     // Symbian, we have a QFile "tempFileGuard" outside the scope which opens
544     // the file in ReadOnly mode while the QTemporaryFile is still alive.
545     QFile tempFileGuard;
546     {
547         QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
548                                 + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
549                                 + QLatin1String("XXXXXX.ttf"));
550         if (!tempfile.open())
551             return;
552         const QString tempFileName = QFileInfo(tempfile).canonicalFilePath();
553         if (fnt->data.isEmpty()) {
554             QFile sourceFile(fnt->fileName);
555             if (!sourceFile.open(QIODevice::ReadOnly))
556                 return;
557             fnt->data = sourceFile.readAll();
558         }
559         if (tempfile.write(fnt->data) == -1)
560             return;
561         tempfile.setAutoRemove(false);
562         tempfile.close(); // Tempfile still keeps a file handle, forbidding write access
563         tempFileGuard.setFileName(tempFileName);
564         if (!tempFileGuard.open(QIODevice::ReadOnly))
565             return;
566         fnt->temporaryFileName = tempFileName;
567     }
568
569     const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName);
570     QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
571     const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer();
572     const TInt err =
573             S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId);
574     tempFileGuard.close(); // Did its job
575     const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer();
576     if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device?
577         int fontOnServerIndex = fontsOnServerAfter.count() - 1;
578         for (int i = 0; i < fontsOnServerBefore.count(); i++) {
579             if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) {
580                 fontOnServerIndex = i;
581                 break;
582             }
583         }
584
585         // Must remove all font engines with their CFonts, first.
586         QFontCache::instance()->clear();
587         db->free();
588         QSymbianFontDatabaseExtrasImplementation::clear();
589
590         if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable())
591             fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName));
592
593         fnt->families.append(fontsOnServerAfter.at(fontOnServerIndex));
594         if (!registerScreenDeviceFont(fontOnServerIndex, dbExtras))
595             dbExtras->removeAppFontData(fnt);
596     } else {
597         QFile::remove(fnt->temporaryFileName);
598         *fnt = QFontDatabasePrivate::ApplicationFont();
599     }
600     lock.relock();
601 }
602
603 bool QFontDatabase::removeApplicationFont(int handle)
604 {
605     QMutexLocker locker(fontDatabaseMutex());
606
607     QFontDatabasePrivate *db = privateDb();
608     if (!db || handle < 0 || handle >= db->applicationFonts.count())
609         return false;
610     QSymbianFontDatabaseExtrasImplementation *dbExtras =
611             static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
612     if (!dbExtras)
613         return false;
614
615     QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle];
616     if (fnt->families.isEmpty())
617         return true; // Nothing to remove. Return peacefully.
618
619     // Must remove all font engines with their CFonts, first
620     QFontCache::instance()->clear();
621     db->free();
622     dbExtras->removeAppFontData(fnt);
623
624     db->invalidate(); // This will just emit 'fontDatabaseChanged()'
625     return true;
626 }
627
628 bool QFontDatabase::removeAllApplicationFonts()
629 {
630     QMutexLocker locker(fontDatabaseMutex());
631
632     const int applicationFontsCount = privateDb()->applicationFonts.count();
633     for (int i = 0; i < applicationFontsCount; ++i)
634         if (!removeApplicationFont(i))
635             return false;
636     return true;
637 }
638
639 bool QFontDatabase::supportsThreadedFontRendering()
640 {
641     return false;
642 }
643
644 static
645 QFontDef cleanedFontDef(const QFontDef &req)
646 {
647     QFontDef result = req;
648     if (result.pixelSize <= 0) {
649         result.pixelSize = QFontEngineS60::pointsToPixels(qMax(qreal(1.0), result.pointSize));
650         result.pointSize = 0;
651     }
652     return result;
653 }
654
655 QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QFontDef &req)
656 {
657     const QFontCache::Key key(cleanedFontDef(req), script);
658
659     if (!privateDb()->count)
660         initializeDb();
661
662     QFontEngine *fe = QFontCache::instance()->findEngine(key);
663     if (!fe) {
664         // Making sure that fe->fontDef.family will be an existing font.
665         initializeDb();
666         QFontDatabasePrivate *db = privateDb();
667         QtFontDesc desc;
668         QList<int> blacklistedFamilies;
669         match(script, req, req.family, QString(), -1, &desc, blacklistedFamilies);
670         if (!desc.family) // falling back to application font
671             desc.family = db->family(QApplication::font().defaultFamily());
672         Q_ASSERT(desc.family);
673
674         // Making sure that desc.family supports the requested script
675         QtFontDesc mappedDesc;
676         bool supportsScript = false;
677         do {
678             match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
679             if (mappedDesc.family == desc.family) {
680                 supportsScript = true;
681                 break;
682             }
683             blacklistedFamilies.append(mappedDesc.familyIndex);
684         } while (mappedDesc.family);
685         if (!supportsScript) {
686             blacklistedFamilies.clear();
687             match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
688             if (mappedDesc.family)
689                 desc = mappedDesc;
690         }
691
692         const QString fontFamily = desc.family->name;
693         QFontDef request = req;
694         request.family = fontFamily;
695 #ifdef QT_NO_FREETYPE
696         const QSymbianFontDatabaseExtrasImplementation *dbExtras =
697                 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
698         const QSymbianTypeFaceExtras *typeFaceExtras =
699                 dbExtras->extras(fontFamily, request.weight > QFont::Normal, request.style != QFont::StyleNormal);
700
701         // We need a valid pixelSize, e.g. for lineThickness()
702         if (request.pixelSize < 0)
703             request.pixelSize = request.pointSize * d->dpi / 72;
704
705         fe = new QFontEngineS60(request, typeFaceExtras);
706 #else // QT_NO_FREETYPE
707         Q_UNUSED(d)
708         QFontEngine::FaceId faceId;
709         const QtFontFamily * const reqQtFontFamily = db->family(fontFamily);
710         faceId.filename = reqQtFontFamily->fontFilename;
711         faceId.index = reqQtFontFamily->fontFileIndex;
712
713         QFontEngineFTS60 *fte = new QFontEngineFTS60(cleanedFontDef(request));
714         if (fte->init(faceId, true, QFontEngineFT::Format_A8))
715             fe = fte;
716         else
717             delete fte;
718 #endif // QT_NO_FREETYPE
719
720         Q_ASSERT(fe);
721         if (script == QUnicodeTables::Common
722             && !(req.styleStrategy & QFont::NoFontMerging)
723             && !fe->symbol) {
724
725             QStringList commonFonts;
726             for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
727                 if (scriptForWritingSystem[ws] != script)
728                     continue;
729                 for (int i = 0; i < db->count; ++i) {
730                     if (db->families[i]->writingSystems[ws] & QtFontFamily::Supported)
731                         commonFonts.append(db->families[i]->name);
732                 }
733             }
734
735             // Hack: Prioritize .ccc fonts
736             const QString niceEastAsianFont(QLatin1String("Sans MT 936_S60"));
737             if (commonFonts.removeAll(niceEastAsianFont) > 0)
738                 commonFonts.prepend(niceEastAsianFont);
739
740             fe = new QFontEngineMultiS60(fe, script, commonFonts);
741         }
742     }
743     fe->ref.ref();
744     QFontCache::instance()->insertEngine(key, fe);
745     return fe;
746 }
747
748 void QFontDatabase::load(const QFontPrivate *d, int script)
749 {
750     QFontEngine *fe = 0;
751     QFontDef req = d->request;
752
753     if (!d->engineData) {
754         const QFontCache::Key key(cleanedFontDef(req), script);
755         getEngineData(d, key);
756     }
757
758     // the cached engineData could have already loaded the engine we want
759     if (d->engineData->engines[script])
760         fe = d->engineData->engines[script];
761
762     if (!fe) {
763         if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
764             fe = new QTestFontEngine(req.pixelSize);
765             fe->fontDef = req;
766         } else {
767             fe = findFont(script, d, req);
768         }
769         d->engineData->engines[script] = fe;
770     }
771 }
772
773 QT_END_NAMESPACE