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 ** 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
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
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_s60_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
63 QStringList qt_symbian_fontFamiliesOnFontServer() // Also used in qfont_s60.cpp
66 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
67 const int numTypeFaces = S60->screenDevice()->NumTypefaces();
68 for (int i = 0; i < numTypeFaces; i++) {
69 TTypefaceSupport typefaceSupport;
70 S60->screenDevice()->TypefaceSupport(typefaceSupport, i);
71 const QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
72 result.append(familyName);
78 QFileInfoList alternativeFilePaths(const QString &path, const QStringList &nameFilters,
79 QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort,
80 bool uniqueFileNames = true)
84 // Prepare a 'soft to hard' drive list: W:, X: ... A:, Z:
85 QStringList driveStrings;
86 foreach (const QFileInfo &drive, QDir::drives())
87 driveStrings.append(drive.absolutePath());
89 const QString zDriveString(QLatin1String("Z:/"));
90 driveStrings.removeAll(zDriveString);
91 driveStrings.prepend(zDriveString);
93 QStringList uniqueFileNameList;
94 for (int i = driveStrings.count() - 1; i >= 0; --i) {
95 const QDir dirOnDrive(driveStrings.at(i) + path);
96 const QFileInfoList entriesOnDrive = dirOnDrive.entryInfoList(nameFilters, filters, sort);
97 if (uniqueFileNames) {
98 foreach(const QFileInfo &entry, entriesOnDrive) {
99 if (!uniqueFileNameList.contains(entry.fileName())) {
100 uniqueFileNameList.append(entry.fileName());
101 result.append(entry);
105 result.append(entriesOnDrive);
111 #ifdef QT_NO_FREETYPE
112 class QSymbianFontDatabaseExtrasImplementation : public QSymbianFontDatabaseExtras
115 QSymbianFontDatabaseExtrasImplementation();
116 ~QSymbianFontDatabaseExtrasImplementation();
118 const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const;
119 void removeAppFontData(QFontDatabasePrivate::ApplicationFont *fnt);
120 static inline bool appFontLimitReached();
121 TUid addFontFileToFontStore(const QFileInfo &fontFileInfo);
124 static inline QString tempAppFontFolder();
125 static const QString appFontMarkerPrefix;
126 static QString appFontMarker(); // 'qaf<shortUid[+shortPid]>'
128 struct CFontFromFontStoreReleaser {
129 static inline void cleanup(CFont *font)
133 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
134 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras);
135 dbExtras->m_store->ReleaseFont(font);
139 struct CFontFromScreenDeviceReleaser {
140 static inline void cleanup(CFont *font)
144 S60->screenDevice()->ReleaseFont(font);
148 // m_heap, m_store, m_rasterizer and m_extras are used if Symbian
149 // does not provide the Font Table API
152 COpenFontRasterizer *m_rasterizer;
153 mutable QList<const QSymbianTypeFaceExtras *> m_extras;
155 mutable QHash<QString, const QSymbianTypeFaceExtras *> m_extrasHash;
156 mutable QSet<QString> m_applicationFontFamilies;
159 const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix =
162 inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
164 return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\');
167 QString QSymbianFontDatabaseExtrasImplementation::appFontMarker()
169 static QString result;
170 if (result.isEmpty()) {
172 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
173 // We are allowed to load app fonts even from previous, crashed runs
174 // of this application, since we can access the font tables.
175 const quint32 uid = RProcess().Type().MostDerived().iUid;
176 id = static_cast<quint16>(uid + (uid >> 16));
178 // If no font table Api is available, we must not even load a font
179 // from a previous (crashed) run of this application. Reason: we
180 // won't get the font tables, they are not in the CFontStore.
181 // So, we use the pid, for more uniqueness.
182 id = static_cast<quint16>(RProcess().Id().Id());
184 result = appFontMarkerPrefix + QString::fromLatin1("%1").arg(id & 0x7fff, 3, 32, QLatin1Char('0'));
185 Q_ASSERT(appFontMarkerPrefix.length() == 1 && result.length() == 4);
190 static inline bool qt_symbian_fontNameHasAppFontMarker(const QString &fontName)
192 const int idLength = 3; // Keep in sync with id length in appFontMarker().
193 const QString &prefix = QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix;
194 if (fontName.length() < prefix.length() + idLength
195 || fontName.mid(fontName.length() - idLength - prefix.length(), prefix.length()) != prefix)
197 // Testing if the the id is base32 data
198 for (int i = fontName.length() - idLength; i < fontName.length(); ++i) {
199 const QChar &c = fontName.at(i);
200 if (!(c >= QLatin1Char('0') && c <= QLatin1Char('9')
201 || c >= QLatin1Char('a') && c <= QLatin1Char('v')))
207 // If fontName is an application font of this app, prepend the app font marker
208 QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName)
210 QFontDatabasePrivate *db = privateDb();
212 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
213 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
214 return dbExtras->m_applicationFontFamilies.contains(fontName) ?
215 fontName + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
219 static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName)
221 return markedFontName.left(markedFontName.length()
222 - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length());
225 QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation()
227 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
229 filters.append(QLatin1String("*.ttf"));
230 filters.append(QLatin1String("*.ccc"));
231 filters.append(QLatin1String("*.ltt"));
232 const QFileInfoList fontFiles = alternativeFilePaths(QLatin1String("resource\\Fonts"), filters);
234 const TInt heapMinLength = 0x1000;
235 const TInt heapMaxLength = qMax(0x20000 * fontFiles.count(), heapMinLength);
236 m_heap = User::ChunkHeap(NULL, heapMinLength, heapMaxLength);
238 m_store = CFontStore::NewL(m_heap);
239 m_rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
240 CleanupStack::PushL(m_rasterizer);
241 m_store->InstallRasterizerL(m_rasterizer);
242 CleanupStack::Pop(m_rasterizer););
244 foreach (const QFileInfo &fontFileInfo, fontFiles)
245 addFontFileToFontStore(fontFileInfo);
249 void QSymbianFontDatabaseExtrasImplementation::clear()
251 QFontDatabasePrivate *db = privateDb();
254 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
255 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
257 return; // initializeDb() has never been called
258 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
259 qDeleteAll(dbExtras->m_extrasHash);
261 typedef QList<const QSymbianTypeFaceExtras *>::iterator iterator;
262 for (iterator p = dbExtras->m_extras.begin(); p != dbExtras->m_extras.end(); ++p) {
263 dbExtras->m_store->ReleaseFont((*p)->fontOwner());
266 dbExtras->m_extras.clear();
268 dbExtras->m_extrasHash.clear();
271 void qt_cleanup_symbianFontDatabase()
273 QFontDatabasePrivate *db = privateDb();
277 QSymbianFontDatabaseExtrasImplementation::clear();
279 if (!db->applicationFonts.isEmpty()) {
280 QFontDatabase::removeAllApplicationFonts();
281 // We remove the left over temporary font files of Qt application.
282 // Active fonts are undeletable since the font server holds a handle
283 // on them, so we do not need to worry to delete other running
284 // applications' fonts.
285 const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder());
286 const QStringList filter(
287 QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf"));
288 foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter))
289 QFile(ttfFile.absoluteFilePath()).remove();
290 db->applicationFonts.clear();
294 QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation()
296 qt_cleanup_symbianFontDatabase();
297 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
303 #ifndef FNTSTORE_H_INLINES_SUPPORT_FMM
305 Workaround: fntstore.h has an inlined function 'COpenFont* CBitmapFont::OpenFont()'
306 that returns a private data member. The header will change between SDKs. But Qt has
307 to build on any SDK version and run on other versions of Symbian OS.
308 This function performs the needed pointer arithmetic to get the right COpenFont*
310 COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont)
312 const TInt offsetIOpenFont = 92; // '_FOFF(CBitmapFont, iOpenFont)' ..if iOpenFont weren't private
313 const TUint valueIOpenFont = *(TUint*)PtrAdd(aBitmapFont, offsetIOpenFont);
314 return (valueIOpenFont & 1) ?
315 (COpenFont*)PtrAdd(aBitmapFont, valueIOpenFont & ~1) : // New behavior: iOpenFont is offset
316 (COpenFont*)valueIOpenFont; // Old behavior: iOpenFont is pointer
318 #endif // FNTSTORE_H_INLINES_SUPPORT_FMM
320 const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &aTypeface,
321 bool bold, bool italic) const
323 const QString typeface = qt_symbian_fontNameWithAppFontMarker(aTypeface);
324 const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic));
325 if (!m_extrasHash.contains(searchKey)) {
326 TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1);
328 searchSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold);
330 searchSpec.iFontStyle.SetPosture(EPostureItalic);
333 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
334 const TInt err = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(font, searchSpec);
335 Q_ASSERT(err == KErrNone && font);
336 QScopedPointer<CFont, CFontFromScreenDeviceReleaser> sFont(font);
337 QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font);
339 m_extrasHash.insert(searchKey, extras);
341 const TInt err = m_store->GetNearestFontToDesignHeightInPixels(font, searchSpec);
342 Q_ASSERT(err == KErrNone && font);
343 const CBitmapFont *bitmapFont = static_cast<CBitmapFont*>(font);
344 COpenFont *openFont =
345 #ifdef FNTSTORE_H_INLINES_SUPPORT_FMM
346 bitmapFont->OpenFont();
347 #else // FNTSTORE_H_INLINES_SUPPORT_FMM
348 OpenFontFromBitmapFont(bitmapFont);
349 #endif // FNTSTORE_H_INLINES_SUPPORT_FMM
350 const TOpenFontFaceAttrib* const attrib = openFont->FaceAttrib();
351 const QString foundKey =
352 QString((const QChar*)attrib->FullName().Ptr(), attrib->FullName().Length());
353 if (!m_extrasHash.contains(foundKey)) {
354 QScopedPointer<CFont, CFontFromFontStoreReleaser> sFont(font);
355 QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font, openFont);
357 m_extras.append(extras);
358 m_extrasHash.insert(searchKey, extras);
359 m_extrasHash.insert(foundKey, extras);
361 m_store->ReleaseFont(font);
362 m_extrasHash.insert(searchKey, m_extrasHash.value(foundKey));
366 return m_extrasHash.value(searchKey);
369 void QSymbianFontDatabaseExtrasImplementation::removeAppFontData(
370 QFontDatabasePrivate::ApplicationFont *fnt)
373 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()
374 && fnt->fontStoreFontFileUid.iUid != 0)
375 m_store->RemoveFile(fnt->fontStoreFontFileUid);
376 if (!fnt->families.isEmpty())
377 m_applicationFontFamilies.remove(fnt->families.first());
378 if (fnt->screenDeviceFontFileId != 0)
379 S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId);
380 QFile::remove(fnt->temporaryFileName);
381 *fnt = QFontDatabasePrivate::ApplicationFont();
384 bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
386 QFontDatabasePrivate *db = privateDb();
389 const int maxAppFonts = 5;
390 int registeredAppFonts = 0;
391 foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts)
392 if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts)
397 TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo)
399 Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable());
400 const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath());
401 const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile));
403 TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr));
407 #else // QT_NO_FREETYPE
408 class QFontEngineFTS60 : public QFontEngineFT
411 QFontEngineFTS60(const QFontDef &fd);
414 QFontEngineFTS60::QFontEngineFTS60(const QFontDef &fd)
417 default_hint_style = HintFull;
419 #endif // QT_NO_FREETYPE
422 QFontEngineS60::pixelsToPoints, QFontEngineS60::pointsToPixels, QFontEngineMultiS60::QFontEngineMultiS60
423 and QFontEngineMultiS60::QFontEngineMultiS60 should be in qfontengine_s60.cpp. But since also the
424 Freetype based font rendering need them, they are here.
426 qreal QFontEngineS60::pixelsToPoints(qreal pixels, Qt::Orientation orientation)
428 CWsScreenDevice* device = S60->screenDevice();
429 return (orientation == Qt::Horizontal?
430 device->HorizontalPixelsToTwips(pixels)
431 :device->VerticalPixelsToTwips(pixels)) / KTwipsPerPoint;
434 qreal QFontEngineS60::pointsToPixels(qreal points, Qt::Orientation orientation)
436 CWsScreenDevice* device = S60->screenDevice();
437 const int twips = points * KTwipsPerPoint;
438 return orientation == Qt::Horizontal?
439 device->HorizontalTwipsToPixels(twips)
440 :device->VerticalTwipsToPixels(twips);
443 QFontEngineMultiS60::QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies)
444 : QFontEngineMulti(fallbackFamilies.size() + 1)
446 , m_fallbackFamilies(fallbackFamilies)
450 fontDef = engines[0]->fontDef;
453 void QFontEngineMultiS60::loadEngine(int at)
455 Q_ASSERT(at < engines.size());
456 Q_ASSERT(engines.at(at) == 0);
458 QFontDef request = fontDef;
459 request.styleStrategy |= QFont::NoFontMerging;
460 request.family = m_fallbackFamilies.at(at-1);
461 engines[at] = QFontDatabase::findFont(m_script,
464 Q_ASSERT(engines[at]);
467 static bool registerScreenDeviceFont(int screenDeviceFontIndex,
468 const QSymbianFontDatabaseExtrasImplementation *dbExtras)
470 TTypefaceSupport typefaceSupport;
471 S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex);
473 QString familyName((const QChar*)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
474 if (qt_symbian_fontNameHasAppFontMarker(familyName)) {
475 const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
476 if (familyName.endsWith(marker)) {
477 familyName = qt_symbian_appFontNameWithoutMarker(familyName);
478 dbExtras->m_applicationFontFamilies.insert(familyName);
480 return false; // This was somebody else's application font. Skip it.
484 CFont *font; // We have to get a font instance in order to know all the details
485 TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11);
486 if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone)
488 QScopedPointer<CFont, QSymbianFontDatabaseExtrasImplementation::CFontFromScreenDeviceReleaser> sFont(font);
489 if (font->TypeUid() != KCFbsFontUid)
491 TOpenFontFaceAttrib faceAttrib;
492 const CFbsFont *cfbsFont = static_cast<const CFbsFont *>(font);
493 cfbsFont->GetFaceAttrib(faceAttrib);
495 QtFontStyle::Key styleKey;
496 styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal;
497 styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal;
499 QtFontFamily *family = privateDb()->family(familyName, true);
500 family->fixedPitch = faceAttrib.IsMonoWidth();
501 QtFontFoundry *foundry = family->foundry(QString(), true);
502 QtFontStyle *style = foundry->style(styleKey, true);
503 style->smoothScalable = typefaceSupport.iIsScalable;
504 style->pixelSize(0, true);
506 const QSymbianTypeFaceExtras *typeFaceExtras =
507 dbExtras->extras(familyName, faceAttrib.IsBold(), faceAttrib.IsItalic());
508 const QByteArray os2Table = typeFaceExtras->getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
509 const unsigned char* data = reinterpret_cast<const unsigned char*>(os2Table.constData());
510 const unsigned char* ulUnicodeRange = data + 42;
511 quint32 unicodeRange[4] = {
512 qFromBigEndian<quint32>(ulUnicodeRange),
513 qFromBigEndian<quint32>(ulUnicodeRange + 4),
514 qFromBigEndian<quint32>(ulUnicodeRange + 8),
515 qFromBigEndian<quint32>(ulUnicodeRange + 12)
517 const unsigned char* ulCodePageRange = data + 78;
518 quint32 codePageRange[2] = {
519 qFromBigEndian<quint32>(ulCodePageRange),
520 qFromBigEndian<quint32>(ulCodePageRange + 4)
522 const QList<QFontDatabase::WritingSystem> writingSystems =
523 determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
524 foreach (const QFontDatabase::WritingSystem system, writingSystems)
525 family->writingSystems[system] = QtFontFamily::Supported;
529 static void initializeDb()
531 QFontDatabasePrivate *db = privateDb();
535 #ifdef QT_NO_FREETYPE
536 if (!db->symbianExtras)
537 db->symbianExtras = new QSymbianFontDatabaseExtrasImplementation;
539 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
541 const int numTypeFaces = S60->screenDevice()->NumTypefaces();
542 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
543 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
544 for (int i = 0; i < numTypeFaces; i++)
545 registerScreenDeviceFont(i, dbExtras);
547 // We have to clear/release all CFonts, here, in case one of the fonts is
548 // an application font of another running Qt app. Otherwise the other Qt app
549 // cannot remove it's application font, anymore -> "Zombie Font".
550 QSymbianFontDatabaseExtrasImplementation::clear();
554 #else // QT_NO_FREETYPE
555 QDir dir(QDesktopServices::storageLocation(QDesktopServices::FontsLocation));
556 dir.setNameFilters(QStringList() << QLatin1String("*.ttf")
557 << QLatin1String("*.ttc") << QLatin1String("*.pfa")
558 << QLatin1String("*.pfb"));
559 for (int i = 0; i < int(dir.count()); ++i) {
560 const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i]));
563 #endif // QT_NO_FREETYPE
566 static inline void load(const QString &family = QString(), int script = -1)
575 quint16 numTables, searchRange, entrySelector, rangeShift;
579 quint32 tag, checkSum, offset, length;
582 struct NameTableHead {
583 quint16 format, count, stringOffset;
587 quint16 platformID, encodingID, languageID, nameID, length, offset;
590 static quint32 ttfCalcChecksum(const char *data, quint32 bytesCount)
593 const quint32 *ptr = reinterpret_cast<const quint32*>(data);
594 const quint32 *endPtr =
595 ptr + (bytesCount + sizeof(quint32) - 1) / sizeof(quint32);
596 while (ptr < endPtr) {
597 const quint32 unit32Value = *ptr++;
598 result += qFromBigEndian(unit32Value);
603 static inline quint32 toDWordBoundary(quint32 value)
605 return (value + 3) & ~3;
608 static inline quint32 dWordPadding(quint32 value)
610 return (4 - (value & 3)) & 3;
613 static inline bool ttfMarkNameTable(QByteArray &table, const QString &marker)
615 const quint32 tableLength = static_cast<quint32>(table.size());
617 if (tableLength > 50000 // hard limit
618 || tableLength < sizeof(NameTableHead)) // corrupt name table
621 const NameTableHead *head = reinterpret_cast<const NameTableHead*>(table.constData());
622 const quint16 count = qFromBigEndian(head->count);
623 const quint16 stringOffset = qFromBigEndian(head->stringOffset);
624 if (count > 200 // hard limit
625 || stringOffset >= tableLength // corrupt name table
626 || sizeof(NameTableHead) + count * sizeof(NameRecord) >= tableLength) // corrupt name table
629 QTextEncoder encoder(QTextCodec::codecForName("UTF-16BE"), QTextCodec::IgnoreHeader);
630 const QByteArray markerUtf16BE = encoder.fromUnicode(marker);
631 const QByteArray markerAscii = marker.toAscii();
633 QByteArray markedTable;
634 markedTable.reserve(tableLength + marker.length() * 20); // Original size plus some extra
635 markedTable.append(table, stringOffset);
636 QByteArray markedStrings;
637 quint32 stringDataCount = stringOffset;
638 for (quint16 i = 0; i < count; ++i) {
639 const quint32 nameRecordOffset = sizeof(NameTableHead) + sizeof(NameRecord) * i;
640 NameRecord *nameRecord =
641 reinterpret_cast<NameRecord*>(markedTable.data() + nameRecordOffset);
642 const quint16 nameID = qFromBigEndian(nameRecord->nameID);
643 const quint16 platformID = qFromBigEndian(nameRecord->platformID);
644 const quint16 encodingID = qFromBigEndian(nameRecord->encodingID);
645 const quint16 offset = qFromBigEndian(nameRecord->offset);
646 const quint16 length = qFromBigEndian(nameRecord->length);
647 stringDataCount += length;
648 if (stringDataCount > 80000 // hard limit. String data may be > name table size. Multiple records can reference the same string.
649 || static_cast<quint32>(stringOffset + offset + length) > tableLength) // String outside bounds
651 const bool needsMarker =
652 nameID == 1 || nameID == 3 || nameID == 4 || nameID == 16 || nameID == 21;
653 const bool isUnicode =
654 platformID == 0 || platformID == 3 && encodingID == 1;
655 const QByteArray originalString =
656 QByteArray::fromRawData(table.constData() + stringOffset + offset, length);
657 QByteArray markedString;
659 const int maxBytesLength = (KMaxTypefaceNameLength - marker.length()) * (isUnicode ? 2 : 1);
660 markedString = originalString.left(maxBytesLength) + (isUnicode ? markerUtf16BE : markerAscii);
662 markedString = originalString;
664 nameRecord->offset = qToBigEndian(static_cast<quint16>(markedStrings.length()));
665 nameRecord->length = qToBigEndian(static_cast<quint16>(markedString.length()));
666 markedStrings.append(markedString);
668 markedTable.append(markedStrings);
673 const quint32 ttfMaxFileSize = 3500000;
675 static inline bool ttfMarkAppFont(QByteArray &ttf, const QString &marker)
677 const quint32 ttfChecksumNumber = 0xb1b0afba;
678 const quint32 alignment = 4;
679 const quint32 ttfLength = static_cast<quint32>(ttf.size());
680 if (ttfLength > ttfMaxFileSize // hard limit
681 || ttfLength % alignment != 0 // ttf sizes are always factors of 4
682 || ttfLength <= sizeof(OffsetTable) // ttf too short
683 || ttfCalcChecksum(ttf.constData(), ttf.size()) != ttfChecksumNumber) // ttf checksum is invalid
686 const OffsetTable *offsetTable = reinterpret_cast<const OffsetTable*>(ttf.constData());
687 const quint16 numTables = qFromBigEndian(offsetTable->numTables);
688 const quint32 recordsLength =
689 toDWordBoundary(sizeof(OffsetTable) + numTables * sizeof(TableRecord));
690 if (numTables > 30 // hard limit
691 || recordsLength + numTables * alignment > ttfLength) // Corrupt ttf. Tables would not fit, even if empty.
694 QByteArray markedTtf;
695 markedTtf.reserve(ttfLength + marker.length() * 20); // Original size plus some extra
696 markedTtf.append(ttf.constData(), recordsLength);
698 const quint32 ttfCheckSumAdjustmentOffset = 8; // Offset from the start of 'head'
699 int indexOfHeadTable = -1;
700 quint32 ttfDataSize = recordsLength;
701 typedef QPair<quint32, quint32> Range;
702 QList<Range> memoryRanges;
703 memoryRanges.reserve(numTables);
704 for (int i = 0; i < numTables; ++i) {
705 TableRecord *tableRecord =
706 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
707 const quint32 offset = qFromBigEndian(tableRecord->offset);
708 const quint32 length = qFromBigEndian(tableRecord->length);
709 const quint32 lengthAligned = toDWordBoundary(length);
710 ttfDataSize += lengthAligned;
711 if (offset < recordsLength // must not intersect ttf header/records
712 || offset % alignment != 0 // must be aligned
713 || offset > ttfLength - alignment // table out of bounds
714 || offset + lengthAligned > ttfLength // table out of bounds
715 || ttfDataSize > ttfLength) // tables would not fit into the ttf
718 foreach (const Range &range, memoryRanges)
719 if (offset < range.first + range.second && offset + lengthAligned > range.first)
720 return false; // Overlaps with another table
721 memoryRanges.append(Range(offset, lengthAligned));
723 quint32 checkSum = qFromBigEndian(tableRecord->checkSum);
724 if (tableRecord->tag == qToBigEndian(static_cast<quint32>('head'))) {
725 if (length < ttfCheckSumAdjustmentOffset + sizeof(quint32))
726 return false; // Invalid 'head' table
727 const quint32 *checkSumAdjustmentTag =
728 reinterpret_cast<const quint32*>(ttf.constData() + offset + ttfCheckSumAdjustmentOffset);
729 const quint32 checkSumAdjustment = qFromBigEndian(*checkSumAdjustmentTag);
730 checkSum += checkSumAdjustment;
731 indexOfHeadTable = i; // For the ttf checksum re-calculation, later
733 if (checkSum != ttfCalcChecksum(ttf.constData() + offset, length))
734 return false; // Table checksum is invalid
736 bool updateTableChecksum = false;
738 if (tableRecord->tag == qToBigEndian(static_cast<quint32>('name'))) {
739 table = QByteArray(ttf.constData() + offset, length);
740 if (!ttfMarkNameTable(table, marker))
741 return false; // Name table was not markable.
742 updateTableChecksum = true;
744 table = QByteArray::fromRawData(ttf.constData() + offset, length);
747 tableRecord->offset = qToBigEndian(markedTtf.size());
748 tableRecord->length = qToBigEndian(table.size());
749 markedTtf.append(table);
750 markedTtf.append(QByteArray(dWordPadding(table.size()), 0)); // 0-padding
751 if (updateTableChecksum) {
752 TableRecord *tableRecord = // Need to recalculate, since markedTtf changed
753 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
754 const quint32 offset = qFromBigEndian(tableRecord->offset);
755 const quint32 length = qFromBigEndian(tableRecord->length);
756 tableRecord->checkSum = qToBigEndian(ttfCalcChecksum(markedTtf.constData() + offset, length));
759 if (indexOfHeadTable == -1 // 'head' table is mandatory
760 || ttfDataSize != ttfLength) // We do not allow ttf data "holes". Neither does Symbian.
762 TableRecord *headRecord =
763 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + indexOfHeadTable * sizeof(TableRecord));
764 quint32 *checkSumAdjustmentTag =
765 reinterpret_cast<quint32*>(markedTtf.data() + qFromBigEndian(headRecord->offset) + ttfCheckSumAdjustmentOffset);
766 *checkSumAdjustmentTag = 0;
767 const quint32 ttfChecksum = ttfCalcChecksum(markedTtf.constData(), markedTtf.count());
768 *checkSumAdjustmentTag = qToBigEndian(ttfChecksumNumber - ttfChecksum);
773 static inline bool ttfCanSymbianLoadFont(const QByteArray &data, const QString &fileName)
778 QFileInfo info(fileName);
779 if (!data.isEmpty()) {
780 QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
781 + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
782 + QLatin1String("XXXXXX.ttf"));
783 if (!tempfile.open() || tempfile.write(data) == -1)
785 ttfFileName = QDir::toNativeSeparators(QFileInfo(tempfile).canonicalFilePath());
786 tempfile.setAutoRemove(false);
788 tempFileGuard.setFileName(ttfFileName);
789 if (!tempFileGuard.open(QIODevice::ReadOnly))
791 } else if (info.isFile()) {
792 ttfFileName = QDir::toNativeSeparators(info.canonicalFilePath());
797 CFontStore *store = 0;
798 RHeap* heap = User::ChunkHeap(NULL, 0x1000, 0x20000);
801 CleanupClosePushL(*heap);
802 store = CFontStore::NewL(heap);
803 CleanupStack::PushL(store);
804 COpenFontRasterizer *rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
805 CleanupStack::PushL(rasterizer);
806 store->InstallRasterizerL(rasterizer);
807 CleanupStack::Pop(rasterizer);
809 TRAP_IGNORE(fontUid = store->AddFileL(qt_QString2TPtrC(ttfFileName)));
810 if (fontUid.iUid != -1)
812 CleanupStack::PopAndDestroy(2, heap); // heap, store
816 if (tempFileGuard.isOpen())
817 tempFileGuard.remove();
822 static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
824 if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
825 || fnt->data.size() > ttfMaxFileSize // hard limit
826 || fnt->data.isEmpty() && (!fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive) // Only buffer or .ttf
827 || QFileInfo(fnt->fileName).size() > ttfMaxFileSize)) // hard limit
830 // Using ttfCanSymbianLoadFont() causes crashes on app destruction (Symbian^3|PR1 and lower).
831 // Therefore, not using it for now, but eventually in a later version.
832 // if (!ttfCanSymbianLoadFont(fnt->data, fnt->fileName))
835 QFontDatabasePrivate *db = privateDb();
842 QSymbianFontDatabaseExtrasImplementation *dbExtras =
843 static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
847 const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
849 // The QTemporaryFile object being used in the following section must be
850 // destructed before letting Symbian load the TTF file. Symbian would not
851 // load it otherwise, because QTemporaryFile will still keep some handle
852 // on it. The scope is used to reduce the life time of the QTemporaryFile.
853 // In order to prevent other processes from modifying the file between the
854 // moment where the QTemporaryFile is destructed and the file is loaded by
855 // Symbian, we have a QFile "tempFileGuard" outside the scope which opens
856 // the file in ReadOnly mode while the QTemporaryFile is still alive.
859 QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
860 + marker + QLatin1String("XXXXXX.ttf"));
861 if (!tempfile.open())
863 const QString tempFileName = QFileInfo(tempfile).canonicalFilePath();
864 if (fnt->data.isEmpty()) {
865 QFile sourceFile(fnt->fileName);
866 if (!sourceFile.open(QIODevice::ReadOnly))
868 fnt->data = sourceFile.readAll();
870 if (!ttfMarkAppFont(fnt->data, marker) || tempfile.write(fnt->data) == -1)
872 tempfile.setAutoRemove(false);
873 tempfile.close(); // Tempfile still keeps a file handle, forbidding write access
874 fnt->data.clear(); // The TTF data was marked and saved. Not needed in memory, anymore.
875 tempFileGuard.setFileName(tempFileName);
876 if (!tempFileGuard.open(QIODevice::ReadOnly))
878 fnt->temporaryFileName = tempFileName;
881 const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName);
882 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
883 const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer();
885 S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId);
886 tempFileGuard.close(); // Did its job
887 const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer();
888 if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device?
889 int fontOnServerIndex = fontsOnServerAfter.count() - 1;
890 for (int i = 0; i < fontsOnServerBefore.count(); i++) {
891 if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) {
892 fontOnServerIndex = i;
897 // Must remove all font engines with their CFonts, first.
898 QFontCache::instance()->clear();
900 QSymbianFontDatabaseExtrasImplementation::clear();
902 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable())
903 fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName));
905 const QString &appFontName = fontsOnServerAfter.at(fontOnServerIndex);
906 fnt->families.append(qt_symbian_appFontNameWithoutMarker(appFontName));
907 if (!qt_symbian_fontNameHasAppFontMarker(appFontName)
908 || !registerScreenDeviceFont(fontOnServerIndex, dbExtras))
909 dbExtras->removeAppFontData(fnt);
911 if (fnt->screenDeviceFontFileId > 0)
912 S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); // May still have the file open!
913 QFile::remove(fnt->temporaryFileName);
914 *fnt = QFontDatabasePrivate::ApplicationFont();
919 bool QFontDatabase::removeApplicationFont(int handle)
921 QMutexLocker locker(fontDatabaseMutex());
923 QFontDatabasePrivate *db = privateDb();
924 if (!db || handle < 0 || handle >= db->applicationFonts.count())
926 QSymbianFontDatabaseExtrasImplementation *dbExtras =
927 static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
931 QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle];
932 if (fnt->families.isEmpty())
933 return true; // Nothing to remove. Return peacefully.
935 // Must remove all font engines with their CFonts, first
936 QFontCache::instance()->clear();
938 dbExtras->removeAppFontData(fnt);
940 db->invalidate(); // This will just emit 'fontDatabaseChanged()'
944 bool QFontDatabase::removeAllApplicationFonts()
946 QMutexLocker locker(fontDatabaseMutex());
948 const int applicationFontsCount = privateDb()->applicationFonts.count();
949 for (int i = 0; i < applicationFontsCount; ++i)
950 if (!removeApplicationFont(i))
955 bool QFontDatabase::supportsThreadedFontRendering()
961 QFontDef cleanedFontDef(const QFontDef &req)
963 QFontDef result = req;
964 if (result.pixelSize <= 0) {
965 result.pixelSize = QFontEngineS60::pointsToPixels(qMax(qreal(1.0), result.pointSize));
966 result.pointSize = 0;
971 QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QFontDef &req)
973 const QFontCache::Key key(cleanedFontDef(req), script);
975 if (!privateDb()->count)
978 QFontEngine *fe = QFontCache::instance()->findEngine(key);
980 // Making sure that fe->fontDef.family will be an existing font.
982 QFontDatabasePrivate *db = privateDb();
984 QList<int> blacklistedFamilies;
985 match(script, key.def, key.def.family, QString(), -1, &desc, blacklistedFamilies);
986 if (!desc.family) // falling back to application font
987 desc.family = db->family(QApplication::font().defaultFamily());
988 Q_ASSERT(desc.family);
990 // Making sure that desc.family supports the requested script
991 QtFontDesc mappedDesc;
992 bool supportsScript = false;
994 match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
995 if (mappedDesc.family == desc.family) {
996 supportsScript = true;
999 blacklistedFamilies.append(mappedDesc.familyIndex);
1000 } while (mappedDesc.family);
1001 if (!supportsScript) {
1002 blacklistedFamilies.clear();
1003 match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
1004 if (mappedDesc.family)
1008 const QString fontFamily = desc.family->name;
1009 QFontDef request = req;
1010 request.family = fontFamily;
1011 #ifdef QT_NO_FREETYPE
1012 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
1013 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
1014 const QSymbianTypeFaceExtras *typeFaceExtras =
1015 dbExtras->extras(fontFamily, request.weight > QFont::Normal, request.style != QFont::StyleNormal);
1017 // We need a valid pixelSize, e.g. for lineThickness()
1018 if (request.pixelSize < 0)
1019 request.pixelSize = request.pointSize * d->dpi / 72;
1021 fe = new QFontEngineS60(request, typeFaceExtras);
1022 #else // QT_NO_FREETYPE
1024 QFontEngine::FaceId faceId;
1025 const QtFontFamily * const reqQtFontFamily = db->family(fontFamily);
1026 faceId.filename = reqQtFontFamily->fontFilename;
1027 faceId.index = reqQtFontFamily->fontFileIndex;
1029 QFontEngineFTS60 *fte = new QFontEngineFTS60(cleanedFontDef(request));
1030 if (fte->init(faceId, true, QFontEngineFT::Format_A8))
1034 #endif // QT_NO_FREETYPE
1037 if (script == QUnicodeTables::Common
1038 && !(req.styleStrategy & QFont::NoFontMerging)
1041 QStringList commonFonts;
1042 for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
1043 if (scriptForWritingSystem[ws] != script)
1045 for (int i = 0; i < db->count; ++i) {
1046 if (db->families[i]->writingSystems[ws] & QtFontFamily::Supported)
1047 commonFonts.append(db->families[i]->name);
1051 // Hack: Prioritize .ccc fonts
1052 const QString niceEastAsianFont(QLatin1String("Sans MT 936_S60"));
1053 if (commonFonts.removeAll(niceEastAsianFont) > 0)
1054 commonFonts.prepend(niceEastAsianFont);
1056 fe = new QFontEngineMultiS60(fe, script, commonFonts);
1060 QFontCache::instance()->insertEngine(key, fe);
1064 void QFontDatabase::load(const QFontPrivate *d, int script)
1066 QFontEngine *fe = 0;
1067 QFontDef req = d->request;
1069 if (!d->engineData) {
1070 const QFontCache::Key key(cleanedFontDef(req), script);
1071 getEngineData(d, key);
1074 // the cached engineData could have already loaded the engine we want
1075 if (d->engineData->engines[script])
1076 fe = d->engineData->engines[script];
1079 if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
1080 fe = new QTestFontEngine(req.pixelSize);
1083 fe = findFont(script, d, req);
1085 d->engineData->engines[script] = fe;