1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <private/qapplication_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>
53 #include <private/qcore_symbian_p.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
61 #ifndef SYMBIAN_VERSION_9_4
62 #define SYMBIAN_LINKEDFONTS_SUPPORTED
63 #endif // !SYMBIAN_VERSION_9_4
65 #ifdef SYMBIAN_LINKEDFONTS_SUPPORTED
66 #include <linkedfonts.h>
67 #endif // SYMBIAN_LINKEDFONTS_SUPPORTED
71 #ifdef SYMBIAN_LINKEDFONTS_SUPPORTED
72 static bool isLinkedFontL(const TDesC &aTypefaceName)
74 CLinkedTypefaceSpecification *linkedspec = CLinkedTypefaceSpecification::NewLC(aTypefaceName);
75 CFbsTypefaceStore *tfs = CFbsTypefaceStore::NewL(NULL);
76 CleanupStack::PushL(tfs);
77 linkedspec->FetchLinkedTypefaceSpecificationL(*tfs);
78 CleanupStack::PopAndDestroy(tfs);
79 CleanupStack::PopAndDestroy(linkedspec);
82 #endif // SYMBIAN_LINKEDFONTS_SUPPORTED
84 bool qt_symbian_isLinkedFont(const TDesC &typefaceName) // Also used in qfont_s60.cpp
86 bool isLinkedFont = false;
87 #ifdef SYMBIAN_LINKEDFONTS_SUPPORTED
88 if (RFbsSession::Connect() == KErrNone) {
89 TRAP_IGNORE(isLinkedFont = isLinkedFontL(typefaceName));
90 RFbsSession::Disconnect();
92 #endif // SYMBIAN_LINKEDFONTS_SUPPORTED
96 QStringList qt_symbian_fontFamiliesOnFontServer() // Also used in qfont_s60.cpp
99 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
100 const int numTypeFaces = S60->screenDevice()->NumTypefaces();
101 for (int i = 0; i < numTypeFaces; i++) {
102 TTypefaceSupport typefaceSupport;
103 S60->screenDevice()->TypefaceSupport(typefaceSupport, i);
104 const QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
105 result.append(familyName);
111 QFileInfoList alternativeFilePaths(const QString &path, const QStringList &nameFilters,
112 QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort,
113 bool uniqueFileNames = true)
115 QFileInfoList result;
117 // Prepare a 'soft to hard' drive list: W:, X: ... A:, Z:
118 QStringList driveStrings;
119 foreach (const QFileInfo &drive, QDir::drives())
120 driveStrings.append(drive.absolutePath());
122 const QString zDriveString(QLatin1String("Z:/"));
123 driveStrings.removeAll(zDriveString);
124 driveStrings.prepend(zDriveString);
126 QStringList uniqueFileNameList;
127 for (int i = driveStrings.count() - 1; i >= 0; --i) {
128 const QDir dirOnDrive(driveStrings.at(i) + path);
129 const QFileInfoList entriesOnDrive = dirOnDrive.entryInfoList(nameFilters, filters, sort);
130 if (uniqueFileNames) {
131 foreach(const QFileInfo &entry, entriesOnDrive) {
132 if (!uniqueFileNameList.contains(entry.fileName())) {
133 uniqueFileNameList.append(entry.fileName());
134 result.append(entry);
138 result.append(entriesOnDrive);
144 #ifdef QT_NO_FREETYPE
145 class QSymbianFontDatabaseExtrasImplementation : public QSymbianFontDatabaseExtras
148 QSymbianFontDatabaseExtrasImplementation();
149 ~QSymbianFontDatabaseExtrasImplementation();
151 const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const;
152 void removeAppFontData(QFontDatabasePrivate::ApplicationFont *fnt);
153 static inline bool appFontLimitReached();
154 TUid addFontFileToFontStore(const QFileInfo &fontFileInfo);
157 static inline QString tempAppFontFolder();
158 static const QString appFontMarkerPrefix;
159 static QString appFontMarker(); // 'qaf<shortUid[+shortPid]>'
161 struct CFontFromFontStoreReleaser {
162 static inline void cleanup(CFont *font)
166 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
167 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras);
168 dbExtras->m_store->ReleaseFont(font);
172 struct CFontFromScreenDeviceReleaser {
173 static inline void cleanup(CFont *font)
177 S60->screenDevice()->ReleaseFont(font);
181 // m_heap, m_store, m_rasterizer and m_extras are used if Symbian
182 // does not provide the Font Table API
185 COpenFontRasterizer *m_rasterizer;
186 mutable QList<const QSymbianTypeFaceExtras *> m_extras;
188 mutable QSet<QString> m_applicationFontFamilies;
191 const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix =
194 inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
196 return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\');
199 QString QSymbianFontDatabaseExtrasImplementation::appFontMarker()
201 static QString result;
202 if (result.isEmpty()) {
204 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
205 // We are allowed to load app fonts even from previous, crashed runs
206 // of this application, since we can access the font tables.
207 const quint32 uid = RProcess().Type().MostDerived().iUid;
208 id = static_cast<quint16>(uid + (uid >> 16));
210 // If no font table Api is available, we must not even load a font
211 // from a previous (crashed) run of this application. Reason: we
212 // won't get the font tables, they are not in the CFontStore.
213 // So, we use the pid, for more uniqueness.
214 id = static_cast<quint16>(RProcess().Id().Id());
216 result = appFontMarkerPrefix + QString::fromLatin1("%1").arg(id & 0x7fff, 3, 32, QLatin1Char('0'));
217 Q_ASSERT(appFontMarkerPrefix.length() == 1 && result.length() == 4);
222 static inline bool qt_symbian_fontNameHasAppFontMarker(const QString &fontName)
224 const int idLength = 3; // Keep in sync with id length in appFontMarker().
225 const QString &prefix = QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix;
226 if (fontName.length() < prefix.length() + idLength
227 || fontName.mid(fontName.length() - idLength - prefix.length(), prefix.length()) != prefix)
229 // Testing if the the id is base32 data
230 for (int i = fontName.length() - idLength; i < fontName.length(); ++i) {
231 const QChar &c = fontName.at(i);
232 if (!(c >= QLatin1Char('0') && c <= QLatin1Char('9')
233 || c >= QLatin1Char('a') && c <= QLatin1Char('v')))
239 // If fontName is an application font of this app, prepend the app font marker
240 QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName)
242 QFontDatabasePrivate *db = privateDb();
244 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
245 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
246 return dbExtras->m_applicationFontFamilies.contains(fontName) ?
247 fontName + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
251 static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName)
253 return markedFontName.left(markedFontName.length()
254 - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length());
257 QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation()
259 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
261 filters.append(QLatin1String("*.ttf"));
262 filters.append(QLatin1String("*.ccc"));
263 filters.append(QLatin1String("*.ltt"));
264 const QFileInfoList fontFiles = alternativeFilePaths(QLatin1String("resource\\Fonts"), filters);
266 const TInt heapMinLength = 0x1000;
267 const TInt heapMaxLength = qMax(0x20000 * fontFiles.count(), heapMinLength);
268 m_heap = User::ChunkHeap(NULL, heapMinLength, heapMaxLength);
270 m_store = CFontStore::NewL(m_heap);
271 m_rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
272 CleanupStack::PushL(m_rasterizer);
273 m_store->InstallRasterizerL(m_rasterizer);
274 CleanupStack::Pop(m_rasterizer););
276 foreach (const QFileInfo &fontFileInfo, fontFiles)
277 addFontFileToFontStore(fontFileInfo);
281 void QSymbianFontDatabaseExtrasImplementation::clear()
283 QFontDatabasePrivate *db = privateDb();
286 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
287 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
289 return; // initializeDb() has never been called
290 QSymbianTypeFaceExtrasHash &extrasHash = S60->fontData();
291 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
292 qDeleteAll(extrasHash);
294 typedef QList<const QSymbianTypeFaceExtras *>::iterator iterator;
295 for (iterator p = dbExtras->m_extras.begin(); p != dbExtras->m_extras.end(); ++p) {
296 dbExtras->m_store->ReleaseFont((*p)->fontOwner());
299 dbExtras->m_extras.clear();
304 void qt_cleanup_symbianFontDatabase()
306 static bool cleanupDone = false;
311 QFontDatabasePrivate *db = privateDb();
315 QSymbianFontDatabaseExtrasImplementation::clear();
317 if (!db->applicationFonts.isEmpty()) {
318 QFontDatabase::removeAllApplicationFonts();
319 // We remove the left over temporary font files of Qt application.
320 // Active fonts are undeletable since the font server holds a handle
321 // on them, so we do not need to worry to delete other running
322 // applications' fonts.
323 const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder());
324 const QStringList filter(
325 QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf"));
326 foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter))
327 QFile(ttfFile.absoluteFilePath()).remove();
328 db->applicationFonts.clear();
332 QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation()
334 qt_cleanup_symbianFontDatabase();
335 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
341 #ifndef FNTSTORE_H_INLINES_SUPPORT_FMM
343 Workaround: fntstore.h has an inlined function 'COpenFont* CBitmapFont::OpenFont()'
344 that returns a private data member. The header will change between SDKs. But Qt has
345 to build on any SDK version and run on other versions of Symbian OS.
346 This function performs the needed pointer arithmetic to get the right COpenFont*
348 COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont)
350 const TInt offsetIOpenFont = 92; // '_FOFF(CBitmapFont, iOpenFont)' ..if iOpenFont weren't private
351 const TUint valueIOpenFont = *(TUint*)PtrAdd(aBitmapFont, offsetIOpenFont);
352 return (valueIOpenFont & 1) ?
353 (COpenFont*)PtrAdd(aBitmapFont, valueIOpenFont & ~1) : // New behavior: iOpenFont is offset
354 (COpenFont*)valueIOpenFont; // Old behavior: iOpenFont is pointer
356 #endif // FNTSTORE_H_INLINES_SUPPORT_FMM
358 const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &aTypeface,
359 bool bold, bool italic) const
361 QSymbianTypeFaceExtrasHash &extrasHash = S60->fontData();
362 if (extrasHash.isEmpty() && QThread::currentThread() != QApplication::instance()->thread())
363 S60->addThreadLocalReleaseFunc(clear);
364 const QString typeface = qt_symbian_fontNameWithAppFontMarker(aTypeface);
365 const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic));
366 if (!extrasHash.contains(searchKey)) {
367 TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1);
369 searchSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold);
371 searchSpec.iFontStyle.SetPosture(EPostureItalic);
374 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
375 const TInt err = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(font, searchSpec);
376 Q_ASSERT(err == KErrNone && font);
377 QScopedPointer<CFont, CFontFromScreenDeviceReleaser> sFont(font);
378 QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font);
380 extrasHash.insert(searchKey, extras);
382 const TInt err = m_store->GetNearestFontToDesignHeightInPixels(font, searchSpec);
383 Q_ASSERT(err == KErrNone && font);
384 const CBitmapFont *bitmapFont = static_cast<CBitmapFont*>(font);
385 COpenFont *openFont =
386 #ifdef FNTSTORE_H_INLINES_SUPPORT_FMM
387 bitmapFont->OpenFont();
388 #else // FNTSTORE_H_INLINES_SUPPORT_FMM
389 OpenFontFromBitmapFont(bitmapFont);
390 #endif // FNTSTORE_H_INLINES_SUPPORT_FMM
391 const TOpenFontFaceAttrib* const attrib = openFont->FaceAttrib();
392 const QString foundKey =
393 QString((const QChar*)attrib->FullName().Ptr(), attrib->FullName().Length());
394 if (!extrasHash.contains(foundKey)) {
395 QScopedPointer<CFont, CFontFromFontStoreReleaser> sFont(font);
396 QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font, openFont);
398 m_extras.append(extras);
399 extrasHash.insert(searchKey, extras);
400 extrasHash.insert(foundKey, extras);
402 m_store->ReleaseFont(font);
403 extrasHash.insert(searchKey, extrasHash.value(foundKey));
407 return extrasHash.value(searchKey);
410 void QSymbianFontDatabaseExtrasImplementation::removeAppFontData(
411 QFontDatabasePrivate::ApplicationFont *fnt)
414 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()
415 && fnt->fontStoreFontFileUid.iUid != 0)
416 m_store->RemoveFile(fnt->fontStoreFontFileUid);
417 if (!fnt->families.isEmpty())
418 m_applicationFontFamilies.remove(fnt->families.first());
419 if (fnt->screenDeviceFontFileId != 0)
420 S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId);
421 QFile::remove(fnt->temporaryFileName);
422 *fnt = QFontDatabasePrivate::ApplicationFont();
425 bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
427 QFontDatabasePrivate *db = privateDb();
430 const int maxAppFonts = 5;
431 int registeredAppFonts = 0;
432 foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts)
433 if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts)
438 TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo)
440 Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable());
441 const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath());
442 const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile));
444 TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr));
448 #else // QT_NO_FREETYPE
449 class QFontEngineFTS60 : public QFontEngineFT
452 QFontEngineFTS60(const QFontDef &fd);
455 QFontEngineFTS60::QFontEngineFTS60(const QFontDef &fd)
458 default_hint_style = HintFull;
460 #endif // QT_NO_FREETYPE
463 QFontEngineS60::pixelsToPoints, QFontEngineS60::pointsToPixels, QFontEngineMultiS60::QFontEngineMultiS60
464 and QFontEngineMultiS60::QFontEngineMultiS60 should be in qfontengine_s60.cpp. But since also the
465 Freetype based font rendering need them, they are here.
467 qreal QFontEngineS60::pixelsToPoints(qreal pixels, Qt::Orientation orientation)
469 CWsScreenDevice* device = S60->screenDevice();
470 return (orientation == Qt::Horizontal?
471 device->HorizontalPixelsToTwips(pixels)
472 :device->VerticalPixelsToTwips(pixels)) / KTwipsPerPoint;
475 qreal QFontEngineS60::pointsToPixels(qreal points, Qt::Orientation orientation)
477 CWsScreenDevice* device = S60->screenDevice();
478 const int twips = points * KTwipsPerPoint;
479 return orientation == Qt::Horizontal?
480 device->HorizontalTwipsToPixels(twips)
481 :device->VerticalTwipsToPixels(twips);
484 QFontEngineMultiS60::QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies)
485 : QFontEngineMulti(fallbackFamilies.size() + 1)
487 , m_fallbackFamilies(fallbackFamilies)
491 fontDef = engines[0]->fontDef;
494 void QFontEngineMultiS60::loadEngine(int at)
496 Q_ASSERT(at < engines.size());
497 Q_ASSERT(engines.at(at) == 0);
499 QFontDef request = fontDef;
500 request.styleStrategy |= QFont::NoFontMerging;
501 request.family = m_fallbackFamilies.at(at-1);
502 engines[at] = QFontDatabase::findFont(m_script,
505 Q_ASSERT(engines[at]);
508 #ifdef QT_NO_FREETYPE
509 static bool registerScreenDeviceFont(int screenDeviceFontIndex,
510 const QSymbianFontDatabaseExtrasImplementation *dbExtras)
512 TTypefaceSupport typefaceSupport;
513 S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex);
515 if (qt_symbian_isLinkedFont(typefaceSupport.iTypeface.iName))
518 QString familyName((const QChar*)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
519 if (qt_symbian_fontNameHasAppFontMarker(familyName)) {
520 const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
521 if (familyName.endsWith(marker)) {
522 familyName = qt_symbian_appFontNameWithoutMarker(familyName);
523 dbExtras->m_applicationFontFamilies.insert(familyName);
525 return false; // This was somebody else's application font. Skip it.
529 CFont *font; // We have to get a font instance in order to know all the details
530 TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11);
531 if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone)
533 QScopedPointer<CFont, QSymbianFontDatabaseExtrasImplementation::CFontFromScreenDeviceReleaser> sFont(font);
534 if (font->TypeUid() != KCFbsFontUid)
536 TOpenFontFaceAttrib faceAttrib;
537 const CFbsFont *cfbsFont = static_cast<const CFbsFont *>(font);
538 cfbsFont->GetFaceAttrib(faceAttrib);
540 QtFontStyle::Key styleKey;
541 styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal;
542 styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal;
544 QtFontFamily *family = privateDb()->family(familyName, true);
545 family->fixedPitch = faceAttrib.IsMonoWidth();
546 QtFontFoundry *foundry = family->foundry(QString(), true);
547 QtFontStyle *style = foundry->style(styleKey, QString(), true);
548 style->smoothScalable = typefaceSupport.iIsScalable;
549 style->pixelSize(0, true);
551 const QSymbianTypeFaceExtras *typeFaceExtras =
552 dbExtras->extras(familyName, faceAttrib.IsBold(), faceAttrib.IsItalic());
553 const QByteArray os2Table = typeFaceExtras->getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
554 const unsigned char* data = reinterpret_cast<const unsigned char*>(os2Table.constData());
555 const unsigned char* ulUnicodeRange = data + 42;
556 quint32 unicodeRange[4] = {
557 qFromBigEndian<quint32>(ulUnicodeRange),
558 qFromBigEndian<quint32>(ulUnicodeRange + 4),
559 qFromBigEndian<quint32>(ulUnicodeRange + 8),
560 qFromBigEndian<quint32>(ulUnicodeRange + 12)
562 const unsigned char* ulCodePageRange = data + 78;
563 quint32 codePageRange[2] = {
564 qFromBigEndian<quint32>(ulCodePageRange),
565 qFromBigEndian<quint32>(ulCodePageRange + 4)
567 const QList<QFontDatabase::WritingSystem> writingSystems =
568 qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange);
569 foreach (const QFontDatabase::WritingSystem system, writingSystems)
570 family->writingSystems[system] = QtFontFamily::Supported;
575 static void initializeDb()
577 QFontDatabasePrivate *db = privateDb();
581 #ifdef QT_NO_FREETYPE
582 if (!db->symbianExtras)
583 db->symbianExtras = new QSymbianFontDatabaseExtrasImplementation;
585 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
587 const int numTypeFaces = S60->screenDevice()->NumTypefaces();
588 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
589 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
590 for (int i = 0; i < numTypeFaces; i++)
591 registerScreenDeviceFont(i, dbExtras);
593 // We have to clear/release all CFonts, here, in case one of the fonts is
594 // an application font of another running Qt app. Otherwise the other Qt app
595 // cannot remove it's application font, anymore -> "Zombie Font".
596 QSymbianFontDatabaseExtrasImplementation::clear();
600 #else // QT_NO_FREETYPE
601 QDir dir(QDesktopServices::storageLocation(QDesktopServices::FontsLocation));
602 dir.setNameFilters(QStringList() << QLatin1String("*.ttf")
603 << QLatin1String("*.ttc") << QLatin1String("*.pfa")
604 << QLatin1String("*.pfb"));
605 for (int i = 0; i < int(dir.count()); ++i) {
606 const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i]));
609 #endif // QT_NO_FREETYPE
612 static inline void load(const QString &family = QString(), int script = -1)
621 quint16 numTables, searchRange, entrySelector, rangeShift;
625 quint32 tag, checkSum, offset, length;
628 struct NameTableHead {
629 quint16 format, count, stringOffset;
633 quint16 platformID, encodingID, languageID, nameID, length, offset;
636 static quint32 ttfCalcChecksum(const char *data, quint32 bytesCount)
639 const quint32 *ptr = reinterpret_cast<const quint32*>(data);
640 const quint32 *endPtr =
641 ptr + (bytesCount + sizeof(quint32) - 1) / sizeof(quint32);
642 while (ptr < endPtr) {
643 const quint32 unit32Value = *ptr++;
644 result += qFromBigEndian(unit32Value);
649 static inline quint32 toDWordBoundary(quint32 value)
651 return (value + 3) & ~3;
654 static inline quint32 dWordPadding(quint32 value)
656 return (4 - (value & 3)) & 3;
659 static inline bool ttfMarkNameTable(QByteArray &table, const QString &marker)
661 const quint32 tableLength = static_cast<quint32>(table.size());
663 if (tableLength > 50000 // hard limit
664 || tableLength < sizeof(NameTableHead)) // corrupt name table
667 const NameTableHead *head = reinterpret_cast<const NameTableHead*>(table.constData());
668 const quint16 count = qFromBigEndian(head->count);
669 const quint16 stringOffset = qFromBigEndian(head->stringOffset);
670 if (count > 200 // hard limit
671 || stringOffset >= tableLength // corrupt name table
672 || sizeof(NameTableHead) + count * sizeof(NameRecord) >= tableLength) // corrupt name table
675 QTextEncoder encoder(QTextCodec::codecForName("UTF-16BE"), QTextCodec::IgnoreHeader);
676 const QByteArray markerUtf16BE = encoder.fromUnicode(marker);
677 const QByteArray markerAscii = marker.toAscii();
679 QByteArray markedTable;
680 markedTable.reserve(tableLength + marker.length() * 20); // Original size plus some extra
681 markedTable.append(table, stringOffset);
682 QByteArray markedStrings;
683 quint32 stringDataCount = stringOffset;
684 for (quint16 i = 0; i < count; ++i) {
685 const quint32 nameRecordOffset = sizeof(NameTableHead) + sizeof(NameRecord) * i;
686 NameRecord *nameRecord =
687 reinterpret_cast<NameRecord*>(markedTable.data() + nameRecordOffset);
688 const quint16 nameID = qFromBigEndian(nameRecord->nameID);
689 const quint16 platformID = qFromBigEndian(nameRecord->platformID);
690 const quint16 encodingID = qFromBigEndian(nameRecord->encodingID);
691 const quint16 offset = qFromBigEndian(nameRecord->offset);
692 const quint16 length = qFromBigEndian(nameRecord->length);
693 stringDataCount += length;
694 if (stringDataCount > 80000 // hard limit. String data may be > name table size. Multiple records can reference the same string.
695 || static_cast<quint32>(stringOffset + offset + length) > tableLength) // String outside bounds
697 const bool needsMarker =
698 nameID == 1 || nameID == 3 || nameID == 4 || nameID == 16 || nameID == 21;
699 const bool isUnicode =
700 platformID == 0 || platformID == 3 && encodingID == 1;
701 const QByteArray originalString =
702 QByteArray::fromRawData(table.constData() + stringOffset + offset, length);
703 QByteArray markedString;
705 const int maxBytesLength = (KMaxTypefaceNameLength - marker.length()) * (isUnicode ? 2 : 1);
706 markedString = originalString.left(maxBytesLength) + (isUnicode ? markerUtf16BE : markerAscii);
708 markedString = originalString;
710 nameRecord->offset = qToBigEndian(static_cast<quint16>(markedStrings.length()));
711 nameRecord->length = qToBigEndian(static_cast<quint16>(markedString.length()));
712 markedStrings.append(markedString);
714 markedTable.append(markedStrings);
719 const quint32 ttfMaxFileSize = 3500000;
721 static inline bool ttfMarkAppFont(QByteArray &ttf, const QString &marker)
723 const quint32 ttfChecksumNumber = 0xb1b0afba;
724 const quint32 alignment = 4;
725 const quint32 ttfLength = static_cast<quint32>(ttf.size());
726 if (ttfLength > ttfMaxFileSize // hard limit
727 || ttfLength % alignment != 0 // ttf sizes are always factors of 4
728 || ttfLength <= sizeof(OffsetTable) // ttf too short
729 || ttfCalcChecksum(ttf.constData(), ttf.size()) != ttfChecksumNumber) // ttf checksum is invalid
732 const OffsetTable *offsetTable = reinterpret_cast<const OffsetTable*>(ttf.constData());
733 const quint16 numTables = qFromBigEndian(offsetTable->numTables);
734 const quint32 recordsLength =
735 toDWordBoundary(sizeof(OffsetTable) + numTables * sizeof(TableRecord));
736 if (numTables > 30 // hard limit
737 || recordsLength + numTables * alignment > ttfLength) // Corrupt ttf. Tables would not fit, even if empty.
740 QByteArray markedTtf;
741 markedTtf.reserve(ttfLength + marker.length() * 20); // Original size plus some extra
742 markedTtf.append(ttf.constData(), recordsLength);
744 const quint32 ttfCheckSumAdjustmentOffset = 8; // Offset from the start of 'head'
745 int indexOfHeadTable = -1;
746 quint32 ttfDataSize = recordsLength;
747 typedef QPair<quint32, quint32> Range;
748 QList<Range> memoryRanges;
749 memoryRanges.reserve(numTables);
750 for (int i = 0; i < numTables; ++i) {
751 TableRecord *tableRecord =
752 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
753 const quint32 offset = qFromBigEndian(tableRecord->offset);
754 const quint32 length = qFromBigEndian(tableRecord->length);
755 const quint32 lengthAligned = toDWordBoundary(length);
756 ttfDataSize += lengthAligned;
757 if (offset < recordsLength // must not intersect ttf header/records
758 || offset % alignment != 0 // must be aligned
759 || offset > ttfLength - alignment // table out of bounds
760 || offset + lengthAligned > ttfLength // table out of bounds
761 || ttfDataSize > ttfLength) // tables would not fit into the ttf
764 foreach (const Range &range, memoryRanges)
765 if (offset < range.first + range.second && offset + lengthAligned > range.first)
766 return false; // Overlaps with another table
767 memoryRanges.append(Range(offset, lengthAligned));
769 quint32 checkSum = qFromBigEndian(tableRecord->checkSum);
770 if (tableRecord->tag == qToBigEndian(static_cast<quint32>('head'))) {
771 if (length < ttfCheckSumAdjustmentOffset + sizeof(quint32))
772 return false; // Invalid 'head' table
773 const quint32 *checkSumAdjustmentTag =
774 reinterpret_cast<const quint32*>(ttf.constData() + offset + ttfCheckSumAdjustmentOffset);
775 const quint32 checkSumAdjustment = qFromBigEndian(*checkSumAdjustmentTag);
776 checkSum += checkSumAdjustment;
777 indexOfHeadTable = i; // For the ttf checksum re-calculation, later
779 if (checkSum != ttfCalcChecksum(ttf.constData() + offset, length))
780 return false; // Table checksum is invalid
782 bool updateTableChecksum = false;
784 if (tableRecord->tag == qToBigEndian(static_cast<quint32>('name'))) {
785 table = QByteArray(ttf.constData() + offset, length);
786 if (!ttfMarkNameTable(table, marker))
787 return false; // Name table was not markable.
788 updateTableChecksum = true;
790 table = QByteArray::fromRawData(ttf.constData() + offset, length);
793 tableRecord->offset = qToBigEndian(markedTtf.size());
794 tableRecord->length = qToBigEndian(table.size());
795 markedTtf.append(table);
796 markedTtf.append(QByteArray(dWordPadding(table.size()), 0)); // 0-padding
797 if (updateTableChecksum) {
798 TableRecord *tableRecord = // Need to recalculate, since markedTtf changed
799 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
800 const quint32 offset = qFromBigEndian(tableRecord->offset);
801 const quint32 length = qFromBigEndian(tableRecord->length);
802 tableRecord->checkSum = qToBigEndian(ttfCalcChecksum(markedTtf.constData() + offset, length));
805 if (indexOfHeadTable == -1 // 'head' table is mandatory
806 || ttfDataSize != ttfLength) // We do not allow ttf data "holes". Neither does Symbian.
808 TableRecord *headRecord =
809 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + indexOfHeadTable * sizeof(TableRecord));
810 quint32 *checkSumAdjustmentTag =
811 reinterpret_cast<quint32*>(markedTtf.data() + qFromBigEndian(headRecord->offset) + ttfCheckSumAdjustmentOffset);
812 *checkSumAdjustmentTag = 0;
813 const quint32 ttfChecksum = ttfCalcChecksum(markedTtf.constData(), markedTtf.count());
814 *checkSumAdjustmentTag = qToBigEndian(ttfChecksumNumber - ttfChecksum);
819 static inline bool ttfCanSymbianLoadFont(const QByteArray &data, const QString &fileName)
824 QFileInfo info(fileName);
825 if (!data.isEmpty()) {
826 QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
827 + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
828 + QLatin1String("XXXXXX.ttf"));
829 if (!tempfile.open() || tempfile.write(data) == -1)
831 ttfFileName = QDir::toNativeSeparators(QFileInfo(tempfile).canonicalFilePath());
832 tempfile.setAutoRemove(false);
834 tempFileGuard.setFileName(ttfFileName);
835 if (!tempFileGuard.open(QIODevice::ReadOnly))
837 } else if (info.isFile()) {
838 ttfFileName = QDir::toNativeSeparators(info.canonicalFilePath());
843 CFontStore *store = 0;
844 RHeap* heap = User::ChunkHeap(NULL, 0x1000, 0x20000);
847 CleanupClosePushL(*heap);
848 store = CFontStore::NewL(heap);
849 CleanupStack::PushL(store);
850 COpenFontRasterizer *rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
851 CleanupStack::PushL(rasterizer);
852 store->InstallRasterizerL(rasterizer);
853 CleanupStack::Pop(rasterizer);
855 TRAP_IGNORE(fontUid = store->AddFileL(qt_QString2TPtrC(ttfFileName)));
856 if (fontUid.iUid != -1)
858 CleanupStack::PopAndDestroy(2, heap); // heap, store
862 if (tempFileGuard.isOpen())
863 tempFileGuard.remove();
868 static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
870 if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
871 || fnt->data.size() > ttfMaxFileSize // hard limit
872 || fnt->data.isEmpty() && (!fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive) // Only buffer or .ttf
873 || QFileInfo(fnt->fileName).size() > ttfMaxFileSize)) // hard limit
876 // Using ttfCanSymbianLoadFont() causes crashes on app destruction (Symbian^3|PR1 and lower).
877 // Therefore, not using it for now, but eventually in a later version.
878 // if (!ttfCanSymbianLoadFont(fnt->data, fnt->fileName))
881 QFontDatabasePrivate *db = privateDb();
888 QSymbianFontDatabaseExtrasImplementation *dbExtras =
889 static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
893 const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
895 // The QTemporaryFile object being used in the following section must be
896 // destructed before letting Symbian load the TTF file. Symbian would not
897 // load it otherwise, because QTemporaryFile will still keep some handle
898 // on it. The scope is used to reduce the life time of the QTemporaryFile.
899 // In order to prevent other processes from modifying the file between the
900 // moment where the QTemporaryFile is destructed and the file is loaded by
901 // Symbian, we have a QFile "tempFileGuard" outside the scope which opens
902 // the file in ReadOnly mode while the QTemporaryFile is still alive.
905 QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
906 + marker + QLatin1String("XXXXXX.ttf"));
907 if (!tempfile.open())
909 const QString tempFileName = QFileInfo(tempfile).canonicalFilePath();
910 if (fnt->data.isEmpty()) {
911 QFile sourceFile(fnt->fileName);
912 if (!sourceFile.open(QIODevice::ReadOnly))
914 fnt->data = sourceFile.readAll();
916 if (!ttfMarkAppFont(fnt->data, marker) || tempfile.write(fnt->data) == -1)
918 tempfile.setAutoRemove(false);
919 tempfile.close(); // Tempfile still keeps a file handle, forbidding write access
920 fnt->data.clear(); // The TTF data was marked and saved. Not needed in memory, anymore.
921 tempFileGuard.setFileName(tempFileName);
922 if (!tempFileGuard.open(QIODevice::ReadOnly))
924 fnt->temporaryFileName = tempFileName;
927 const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName);
928 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
929 const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer();
931 S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId);
932 tempFileGuard.close(); // Did its job
933 const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer();
934 if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device?
935 int fontOnServerIndex = fontsOnServerAfter.count() - 1;
936 for (int i = 0; i < fontsOnServerBefore.count(); i++) {
937 if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) {
938 fontOnServerIndex = i;
943 // Must remove all font engines with their CFonts, first.
944 QFontCache::instance()->clear();
946 QSymbianFontDatabaseExtrasImplementation::clear();
948 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable())
949 fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName));
951 const QString &appFontName = fontsOnServerAfter.at(fontOnServerIndex);
952 fnt->families.append(qt_symbian_appFontNameWithoutMarker(appFontName));
953 if (!qt_symbian_fontNameHasAppFontMarker(appFontName)
954 || !registerScreenDeviceFont(fontOnServerIndex, dbExtras))
955 dbExtras->removeAppFontData(fnt);
957 if (fnt->screenDeviceFontFileId > 0)
958 S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); // May still have the file open!
959 QFile::remove(fnt->temporaryFileName);
960 *fnt = QFontDatabasePrivate::ApplicationFont();
965 bool QFontDatabase::removeApplicationFont(int handle)
967 QMutexLocker locker(fontDatabaseMutex());
969 QFontDatabasePrivate *db = privateDb();
970 if (!db || handle < 0 || handle >= db->applicationFonts.count())
972 QSymbianFontDatabaseExtrasImplementation *dbExtras =
973 static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
977 QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle];
978 if (fnt->families.isEmpty())
979 return true; // Nothing to remove. Return peacefully.
981 // Must remove all font engines with their CFonts, first
982 QFontCache::instance()->clear();
984 dbExtras->removeAppFontData(fnt);
986 db->invalidate(); // This will just emit 'fontDatabaseChanged()'
990 bool QFontDatabase::removeAllApplicationFonts()
992 QMutexLocker locker(fontDatabaseMutex());
994 const int applicationFontsCount = privateDb()->applicationFonts.count();
995 for (int i = 0; i < applicationFontsCount; ++i)
996 if (!removeApplicationFont(i))
1001 bool QFontDatabase::supportsThreadedFontRendering()
1003 return QSymbianTypeFaceExtras::symbianFontTableApiAvailable();
1007 QFontDef cleanedFontDef(const QFontDef &req)
1009 QFontDef result = req;
1010 if (result.pixelSize <= 0) {
1011 result.pixelSize = QFontEngineS60::pointsToPixels(qMax(qreal(1.0), result.pointSize));
1012 result.pointSize = 0;
1017 QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QFontDef &req)
1019 const QFontCache::Key key(cleanedFontDef(req), script);
1021 if (!privateDb()->count)
1024 QFontEngine *fe = QFontCache::instance()->findEngine(key);
1026 // Making sure that fe->fontDef.family will be an existing font.
1028 QFontDatabasePrivate *db = privateDb();
1030 QList<int> blacklistedFamilies;
1031 match(script, key.def, key.def.family, QString(), -1, &desc, blacklistedFamilies);
1032 if (!desc.family) // falling back to application font
1033 desc.family = db->family(QApplication::font().defaultFamily());
1034 Q_ASSERT(desc.family);
1036 // Making sure that desc.family supports the requested script
1037 QtFontDesc mappedDesc;
1038 bool supportsScript = false;
1040 match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
1041 if (mappedDesc.family == desc.family) {
1042 supportsScript = true;
1045 blacklistedFamilies.append(mappedDesc.familyIndex);
1046 } while (mappedDesc.family);
1047 if (!supportsScript) {
1048 blacklistedFamilies.clear();
1049 match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
1050 if (mappedDesc.family)
1054 const QString fontFamily = desc.family->name;
1055 QFontDef request = req;
1056 request.family = fontFamily;
1057 #ifdef QT_NO_FREETYPE
1058 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
1059 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
1060 const QSymbianTypeFaceExtras *typeFaceExtras =
1061 dbExtras->extras(fontFamily, request.weight > QFont::Normal, request.style != QFont::StyleNormal);
1063 // We need a valid pixelSize, e.g. for lineThickness()
1064 if (request.pixelSize < 0)
1065 request.pixelSize = request.pointSize * d->dpi / 72;
1067 fe = new QFontEngineS60(request, typeFaceExtras);
1068 #else // QT_NO_FREETYPE
1070 QFontEngine::FaceId faceId;
1071 const QtFontFamily * const reqQtFontFamily = db->family(fontFamily);
1072 faceId.filename = reqQtFontFamily->fontFilename;
1073 faceId.index = reqQtFontFamily->fontFileIndex;
1075 QFontEngineFTS60 *fte = new QFontEngineFTS60(cleanedFontDef(request));
1076 if (fte->init(faceId, true, QFontEngineFT::Format_A8))
1080 #endif // QT_NO_FREETYPE
1083 if (script == QUnicodeTables::Common
1084 && !(req.styleStrategy & QFont::NoFontMerging)
1087 QStringList commonFonts;
1088 for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
1089 if (scriptForWritingSystem[ws] != script)
1091 for (int i = 0; i < db->count; ++i) {
1092 if (db->families[i]->writingSystems[ws] & QtFontFamily::Supported)
1093 commonFonts.append(db->families[i]->name);
1097 // Hack: Prioritize .ccc fonts
1098 const QString niceEastAsianFont(QLatin1String("Sans MT 936_S60"));
1099 if (commonFonts.removeAll(niceEastAsianFont) > 0)
1100 commonFonts.prepend(niceEastAsianFont);
1102 fe = new QFontEngineMultiS60(fe, script, commonFonts);
1106 QFontCache::instance()->insertEngine(key, fe);
1110 void QFontDatabase::load(const QFontPrivate *d, int script)
1112 QFontEngine *fe = 0;
1113 QFontDef req = d->request;
1115 if (!d->engineData) {
1116 const QFontCache::Key key(cleanedFontDef(req), script);
1117 getEngineData(d, key);
1120 // the cached engineData could have already loaded the engine we want
1121 if (d->engineData->engines[script])
1122 fe = d->engineData->engines[script];
1125 if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
1126 fe = new QTestFontEngine(req.pixelSize);
1129 fe = findFont(script, d, req);
1131 d->engineData->engines[script] = fe;