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_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 QSet<QString> m_applicationFontFamilies;
158 const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix =
161 inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
163 return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\');
166 QString QSymbianFontDatabaseExtrasImplementation::appFontMarker()
168 static QString result;
169 if (result.isEmpty()) {
171 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
172 // We are allowed to load app fonts even from previous, crashed runs
173 // of this application, since we can access the font tables.
174 const quint32 uid = RProcess().Type().MostDerived().iUid;
175 id = static_cast<quint16>(uid + (uid >> 16));
177 // If no font table Api is available, we must not even load a font
178 // from a previous (crashed) run of this application. Reason: we
179 // won't get the font tables, they are not in the CFontStore.
180 // So, we use the pid, for more uniqueness.
181 id = static_cast<quint16>(RProcess().Id().Id());
183 result = appFontMarkerPrefix + QString::fromLatin1("%1").arg(id & 0x7fff, 3, 32, QLatin1Char('0'));
184 Q_ASSERT(appFontMarkerPrefix.length() == 1 && result.length() == 4);
189 static inline bool qt_symbian_fontNameHasAppFontMarker(const QString &fontName)
191 const int idLength = 3; // Keep in sync with id length in appFontMarker().
192 const QString &prefix = QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix;
193 if (fontName.length() < prefix.length() + idLength
194 || fontName.mid(fontName.length() - idLength - prefix.length(), prefix.length()) != prefix)
196 // Testing if the the id is base32 data
197 for (int i = fontName.length() - idLength; i < fontName.length(); ++i) {
198 const QChar &c = fontName.at(i);
199 if (!(c >= QLatin1Char('0') && c <= QLatin1Char('9')
200 || c >= QLatin1Char('a') && c <= QLatin1Char('v')))
206 // If fontName is an application font of this app, prepend the app font marker
207 QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName)
209 QFontDatabasePrivate *db = privateDb();
211 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
212 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
213 return dbExtras->m_applicationFontFamilies.contains(fontName) ?
214 fontName + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
218 static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName)
220 return markedFontName.left(markedFontName.length()
221 - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length());
224 QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation()
226 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
228 filters.append(QLatin1String("*.ttf"));
229 filters.append(QLatin1String("*.ccc"));
230 filters.append(QLatin1String("*.ltt"));
231 const QFileInfoList fontFiles = alternativeFilePaths(QLatin1String("resource\\Fonts"), filters);
233 const TInt heapMinLength = 0x1000;
234 const TInt heapMaxLength = qMax(0x20000 * fontFiles.count(), heapMinLength);
235 m_heap = User::ChunkHeap(NULL, heapMinLength, heapMaxLength);
237 m_store = CFontStore::NewL(m_heap);
238 m_rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
239 CleanupStack::PushL(m_rasterizer);
240 m_store->InstallRasterizerL(m_rasterizer);
241 CleanupStack::Pop(m_rasterizer););
243 foreach (const QFileInfo &fontFileInfo, fontFiles)
244 addFontFileToFontStore(fontFileInfo);
248 void QSymbianFontDatabaseExtrasImplementation::clear()
250 QFontDatabasePrivate *db = privateDb();
253 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
254 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
256 return; // initializeDb() has never been called
257 QSymbianTypeFaceExtrasHash &extrasHash = S60->fontData();
258 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
259 qDeleteAll(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();
271 void qt_cleanup_symbianFontDatabase()
273 static bool cleanupDone = false;
278 QFontDatabasePrivate *db = privateDb();
282 QSymbianFontDatabaseExtrasImplementation::clear();
284 if (!db->applicationFonts.isEmpty()) {
285 QFontDatabase::removeAllApplicationFonts();
286 // We remove the left over temporary font files of Qt application.
287 // Active fonts are undeletable since the font server holds a handle
288 // on them, so we do not need to worry to delete other running
289 // applications' fonts.
290 const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder());
291 const QStringList filter(
292 QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf"));
293 foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter))
294 QFile(ttfFile.absoluteFilePath()).remove();
295 db->applicationFonts.clear();
299 QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation()
301 qt_cleanup_symbianFontDatabase();
302 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
308 #ifndef FNTSTORE_H_INLINES_SUPPORT_FMM
310 Workaround: fntstore.h has an inlined function 'COpenFont* CBitmapFont::OpenFont()'
311 that returns a private data member. The header will change between SDKs. But Qt has
312 to build on any SDK version and run on other versions of Symbian OS.
313 This function performs the needed pointer arithmetic to get the right COpenFont*
315 COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont)
317 const TInt offsetIOpenFont = 92; // '_FOFF(CBitmapFont, iOpenFont)' ..if iOpenFont weren't private
318 const TUint valueIOpenFont = *(TUint*)PtrAdd(aBitmapFont, offsetIOpenFont);
319 return (valueIOpenFont & 1) ?
320 (COpenFont*)PtrAdd(aBitmapFont, valueIOpenFont & ~1) : // New behavior: iOpenFont is offset
321 (COpenFont*)valueIOpenFont; // Old behavior: iOpenFont is pointer
323 #endif // FNTSTORE_H_INLINES_SUPPORT_FMM
325 const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &aTypeface,
326 bool bold, bool italic) const
328 QSymbianTypeFaceExtrasHash &extrasHash = S60->fontData();
329 if (extrasHash.isEmpty() && QThread::currentThread() != QApplication::instance()->thread())
330 S60->addThreadLocalReleaseFunc(clear);
331 const QString typeface = qt_symbian_fontNameWithAppFontMarker(aTypeface);
332 const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic));
333 if (!extrasHash.contains(searchKey)) {
334 TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1);
336 searchSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold);
338 searchSpec.iFontStyle.SetPosture(EPostureItalic);
341 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
342 const TInt err = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(font, searchSpec);
343 Q_ASSERT(err == KErrNone && font);
344 QScopedPointer<CFont, CFontFromScreenDeviceReleaser> sFont(font);
345 QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font);
347 extrasHash.insert(searchKey, extras);
349 const TInt err = m_store->GetNearestFontToDesignHeightInPixels(font, searchSpec);
350 Q_ASSERT(err == KErrNone && font);
351 const CBitmapFont *bitmapFont = static_cast<CBitmapFont*>(font);
352 COpenFont *openFont =
353 #ifdef FNTSTORE_H_INLINES_SUPPORT_FMM
354 bitmapFont->OpenFont();
355 #else // FNTSTORE_H_INLINES_SUPPORT_FMM
356 OpenFontFromBitmapFont(bitmapFont);
357 #endif // FNTSTORE_H_INLINES_SUPPORT_FMM
358 const TOpenFontFaceAttrib* const attrib = openFont->FaceAttrib();
359 const QString foundKey =
360 QString((const QChar*)attrib->FullName().Ptr(), attrib->FullName().Length());
361 if (!extrasHash.contains(foundKey)) {
362 QScopedPointer<CFont, CFontFromFontStoreReleaser> sFont(font);
363 QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font, openFont);
365 m_extras.append(extras);
366 extrasHash.insert(searchKey, extras);
367 extrasHash.insert(foundKey, extras);
369 m_store->ReleaseFont(font);
370 extrasHash.insert(searchKey, extrasHash.value(foundKey));
374 return extrasHash.value(searchKey);
377 void QSymbianFontDatabaseExtrasImplementation::removeAppFontData(
378 QFontDatabasePrivate::ApplicationFont *fnt)
381 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()
382 && fnt->fontStoreFontFileUid.iUid != 0)
383 m_store->RemoveFile(fnt->fontStoreFontFileUid);
384 if (!fnt->families.isEmpty())
385 m_applicationFontFamilies.remove(fnt->families.first());
386 if (fnt->screenDeviceFontFileId != 0)
387 S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId);
388 QFile::remove(fnt->temporaryFileName);
389 *fnt = QFontDatabasePrivate::ApplicationFont();
392 bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
394 QFontDatabasePrivate *db = privateDb();
397 const int maxAppFonts = 5;
398 int registeredAppFonts = 0;
399 foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts)
400 if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts)
405 TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo)
407 Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable());
408 const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath());
409 const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile));
411 TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr));
415 #else // QT_NO_FREETYPE
416 class QFontEngineFTS60 : public QFontEngineFT
419 QFontEngineFTS60(const QFontDef &fd);
422 QFontEngineFTS60::QFontEngineFTS60(const QFontDef &fd)
425 default_hint_style = HintFull;
427 #endif // QT_NO_FREETYPE
430 QFontEngineS60::pixelsToPoints, QFontEngineS60::pointsToPixels, QFontEngineMultiS60::QFontEngineMultiS60
431 and QFontEngineMultiS60::QFontEngineMultiS60 should be in qfontengine_s60.cpp. But since also the
432 Freetype based font rendering need them, they are here.
434 qreal QFontEngineS60::pixelsToPoints(qreal pixels, Qt::Orientation orientation)
436 CWsScreenDevice* device = S60->screenDevice();
437 return (orientation == Qt::Horizontal?
438 device->HorizontalPixelsToTwips(pixels)
439 :device->VerticalPixelsToTwips(pixels)) / KTwipsPerPoint;
442 qreal QFontEngineS60::pointsToPixels(qreal points, Qt::Orientation orientation)
444 CWsScreenDevice* device = S60->screenDevice();
445 const int twips = points * KTwipsPerPoint;
446 return orientation == Qt::Horizontal?
447 device->HorizontalTwipsToPixels(twips)
448 :device->VerticalTwipsToPixels(twips);
451 QFontEngineMultiS60::QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies)
452 : QFontEngineMulti(fallbackFamilies.size() + 1)
454 , m_fallbackFamilies(fallbackFamilies)
458 fontDef = engines[0]->fontDef;
461 void QFontEngineMultiS60::loadEngine(int at)
463 Q_ASSERT(at < engines.size());
464 Q_ASSERT(engines.at(at) == 0);
466 QFontDef request = fontDef;
467 request.styleStrategy |= QFont::NoFontMerging;
468 request.family = m_fallbackFamilies.at(at-1);
469 engines[at] = QFontDatabase::findFont(m_script,
472 Q_ASSERT(engines[at]);
475 #ifdef QT_NO_FREETYPE
476 static bool registerScreenDeviceFont(int screenDeviceFontIndex,
477 const QSymbianFontDatabaseExtrasImplementation *dbExtras)
479 TTypefaceSupport typefaceSupport;
480 S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex);
482 QString familyName((const QChar*)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
483 if (qt_symbian_fontNameHasAppFontMarker(familyName)) {
484 const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
485 if (familyName.endsWith(marker)) {
486 familyName = qt_symbian_appFontNameWithoutMarker(familyName);
487 dbExtras->m_applicationFontFamilies.insert(familyName);
489 return false; // This was somebody else's application font. Skip it.
493 CFont *font; // We have to get a font instance in order to know all the details
494 TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11);
495 if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone)
497 QScopedPointer<CFont, QSymbianFontDatabaseExtrasImplementation::CFontFromScreenDeviceReleaser> sFont(font);
498 if (font->TypeUid() != KCFbsFontUid)
500 TOpenFontFaceAttrib faceAttrib;
501 const CFbsFont *cfbsFont = static_cast<const CFbsFont *>(font);
502 cfbsFont->GetFaceAttrib(faceAttrib);
504 QtFontStyle::Key styleKey;
505 styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal;
506 styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal;
508 QtFontFamily *family = privateDb()->family(familyName, true);
509 family->fixedPitch = faceAttrib.IsMonoWidth();
510 QtFontFoundry *foundry = family->foundry(QString(), true);
511 QtFontStyle *style = foundry->style(styleKey, QString(), true);
512 style->smoothScalable = typefaceSupport.iIsScalable;
513 style->pixelSize(0, true);
515 const QSymbianTypeFaceExtras *typeFaceExtras =
516 dbExtras->extras(familyName, faceAttrib.IsBold(), faceAttrib.IsItalic());
517 const QByteArray os2Table = typeFaceExtras->getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
518 const unsigned char* data = reinterpret_cast<const unsigned char*>(os2Table.constData());
519 const unsigned char* ulUnicodeRange = data + 42;
520 quint32 unicodeRange[4] = {
521 qFromBigEndian<quint32>(ulUnicodeRange),
522 qFromBigEndian<quint32>(ulUnicodeRange + 4),
523 qFromBigEndian<quint32>(ulUnicodeRange + 8),
524 qFromBigEndian<quint32>(ulUnicodeRange + 12)
526 const unsigned char* ulCodePageRange = data + 78;
527 quint32 codePageRange[2] = {
528 qFromBigEndian<quint32>(ulCodePageRange),
529 qFromBigEndian<quint32>(ulCodePageRange + 4)
531 const QList<QFontDatabase::WritingSystem> writingSystems =
532 qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange);
533 foreach (const QFontDatabase::WritingSystem system, writingSystems)
534 family->writingSystems[system] = QtFontFamily::Supported;
539 static void initializeDb()
541 QFontDatabasePrivate *db = privateDb();
545 #ifdef QT_NO_FREETYPE
546 if (!db->symbianExtras)
547 db->symbianExtras = new QSymbianFontDatabaseExtrasImplementation;
549 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
551 const int numTypeFaces = S60->screenDevice()->NumTypefaces();
552 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
553 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
554 for (int i = 0; i < numTypeFaces; i++)
555 registerScreenDeviceFont(i, dbExtras);
557 // We have to clear/release all CFonts, here, in case one of the fonts is
558 // an application font of another running Qt app. Otherwise the other Qt app
559 // cannot remove it's application font, anymore -> "Zombie Font".
560 QSymbianFontDatabaseExtrasImplementation::clear();
564 #else // QT_NO_FREETYPE
565 QDir dir(QDesktopServices::storageLocation(QDesktopServices::FontsLocation));
566 dir.setNameFilters(QStringList() << QLatin1String("*.ttf")
567 << QLatin1String("*.ttc") << QLatin1String("*.pfa")
568 << QLatin1String("*.pfb"));
569 for (int i = 0; i < int(dir.count()); ++i) {
570 const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i]));
573 #endif // QT_NO_FREETYPE
576 static inline void load(const QString &family = QString(), int script = -1)
585 quint16 numTables, searchRange, entrySelector, rangeShift;
589 quint32 tag, checkSum, offset, length;
592 struct NameTableHead {
593 quint16 format, count, stringOffset;
597 quint16 platformID, encodingID, languageID, nameID, length, offset;
600 static quint32 ttfCalcChecksum(const char *data, quint32 bytesCount)
603 const quint32 *ptr = reinterpret_cast<const quint32*>(data);
604 const quint32 *endPtr =
605 ptr + (bytesCount + sizeof(quint32) - 1) / sizeof(quint32);
606 while (ptr < endPtr) {
607 const quint32 unit32Value = *ptr++;
608 result += qFromBigEndian(unit32Value);
613 static inline quint32 toDWordBoundary(quint32 value)
615 return (value + 3) & ~3;
618 static inline quint32 dWordPadding(quint32 value)
620 return (4 - (value & 3)) & 3;
623 static inline bool ttfMarkNameTable(QByteArray &table, const QString &marker)
625 const quint32 tableLength = static_cast<quint32>(table.size());
627 if (tableLength > 50000 // hard limit
628 || tableLength < sizeof(NameTableHead)) // corrupt name table
631 const NameTableHead *head = reinterpret_cast<const NameTableHead*>(table.constData());
632 const quint16 count = qFromBigEndian(head->count);
633 const quint16 stringOffset = qFromBigEndian(head->stringOffset);
634 if (count > 200 // hard limit
635 || stringOffset >= tableLength // corrupt name table
636 || sizeof(NameTableHead) + count * sizeof(NameRecord) >= tableLength) // corrupt name table
639 QTextEncoder encoder(QTextCodec::codecForName("UTF-16BE"), QTextCodec::IgnoreHeader);
640 const QByteArray markerUtf16BE = encoder.fromUnicode(marker);
641 const QByteArray markerAscii = marker.toAscii();
643 QByteArray markedTable;
644 markedTable.reserve(tableLength + marker.length() * 20); // Original size plus some extra
645 markedTable.append(table, stringOffset);
646 QByteArray markedStrings;
647 quint32 stringDataCount = stringOffset;
648 for (quint16 i = 0; i < count; ++i) {
649 const quint32 nameRecordOffset = sizeof(NameTableHead) + sizeof(NameRecord) * i;
650 NameRecord *nameRecord =
651 reinterpret_cast<NameRecord*>(markedTable.data() + nameRecordOffset);
652 const quint16 nameID = qFromBigEndian(nameRecord->nameID);
653 const quint16 platformID = qFromBigEndian(nameRecord->platformID);
654 const quint16 encodingID = qFromBigEndian(nameRecord->encodingID);
655 const quint16 offset = qFromBigEndian(nameRecord->offset);
656 const quint16 length = qFromBigEndian(nameRecord->length);
657 stringDataCount += length;
658 if (stringDataCount > 80000 // hard limit. String data may be > name table size. Multiple records can reference the same string.
659 || static_cast<quint32>(stringOffset + offset + length) > tableLength) // String outside bounds
661 const bool needsMarker =
662 nameID == 1 || nameID == 3 || nameID == 4 || nameID == 16 || nameID == 21;
663 const bool isUnicode =
664 platformID == 0 || platformID == 3 && encodingID == 1;
665 const QByteArray originalString =
666 QByteArray::fromRawData(table.constData() + stringOffset + offset, length);
667 QByteArray markedString;
669 const int maxBytesLength = (KMaxTypefaceNameLength - marker.length()) * (isUnicode ? 2 : 1);
670 markedString = originalString.left(maxBytesLength) + (isUnicode ? markerUtf16BE : markerAscii);
672 markedString = originalString;
674 nameRecord->offset = qToBigEndian(static_cast<quint16>(markedStrings.length()));
675 nameRecord->length = qToBigEndian(static_cast<quint16>(markedString.length()));
676 markedStrings.append(markedString);
678 markedTable.append(markedStrings);
683 const quint32 ttfMaxFileSize = 3500000;
685 static inline bool ttfMarkAppFont(QByteArray &ttf, const QString &marker)
687 const quint32 ttfChecksumNumber = 0xb1b0afba;
688 const quint32 alignment = 4;
689 const quint32 ttfLength = static_cast<quint32>(ttf.size());
690 if (ttfLength > ttfMaxFileSize // hard limit
691 || ttfLength % alignment != 0 // ttf sizes are always factors of 4
692 || ttfLength <= sizeof(OffsetTable) // ttf too short
693 || ttfCalcChecksum(ttf.constData(), ttf.size()) != ttfChecksumNumber) // ttf checksum is invalid
696 const OffsetTable *offsetTable = reinterpret_cast<const OffsetTable*>(ttf.constData());
697 const quint16 numTables = qFromBigEndian(offsetTable->numTables);
698 const quint32 recordsLength =
699 toDWordBoundary(sizeof(OffsetTable) + numTables * sizeof(TableRecord));
700 if (numTables > 30 // hard limit
701 || recordsLength + numTables * alignment > ttfLength) // Corrupt ttf. Tables would not fit, even if empty.
704 QByteArray markedTtf;
705 markedTtf.reserve(ttfLength + marker.length() * 20); // Original size plus some extra
706 markedTtf.append(ttf.constData(), recordsLength);
708 const quint32 ttfCheckSumAdjustmentOffset = 8; // Offset from the start of 'head'
709 int indexOfHeadTable = -1;
710 quint32 ttfDataSize = recordsLength;
711 typedef QPair<quint32, quint32> Range;
712 QList<Range> memoryRanges;
713 memoryRanges.reserve(numTables);
714 for (int i = 0; i < numTables; ++i) {
715 TableRecord *tableRecord =
716 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
717 const quint32 offset = qFromBigEndian(tableRecord->offset);
718 const quint32 length = qFromBigEndian(tableRecord->length);
719 const quint32 lengthAligned = toDWordBoundary(length);
720 ttfDataSize += lengthAligned;
721 if (offset < recordsLength // must not intersect ttf header/records
722 || offset % alignment != 0 // must be aligned
723 || offset > ttfLength - alignment // table out of bounds
724 || offset + lengthAligned > ttfLength // table out of bounds
725 || ttfDataSize > ttfLength) // tables would not fit into the ttf
728 foreach (const Range &range, memoryRanges)
729 if (offset < range.first + range.second && offset + lengthAligned > range.first)
730 return false; // Overlaps with another table
731 memoryRanges.append(Range(offset, lengthAligned));
733 quint32 checkSum = qFromBigEndian(tableRecord->checkSum);
734 if (tableRecord->tag == qToBigEndian(static_cast<quint32>('head'))) {
735 if (length < ttfCheckSumAdjustmentOffset + sizeof(quint32))
736 return false; // Invalid 'head' table
737 const quint32 *checkSumAdjustmentTag =
738 reinterpret_cast<const quint32*>(ttf.constData() + offset + ttfCheckSumAdjustmentOffset);
739 const quint32 checkSumAdjustment = qFromBigEndian(*checkSumAdjustmentTag);
740 checkSum += checkSumAdjustment;
741 indexOfHeadTable = i; // For the ttf checksum re-calculation, later
743 if (checkSum != ttfCalcChecksum(ttf.constData() + offset, length))
744 return false; // Table checksum is invalid
746 bool updateTableChecksum = false;
748 if (tableRecord->tag == qToBigEndian(static_cast<quint32>('name'))) {
749 table = QByteArray(ttf.constData() + offset, length);
750 if (!ttfMarkNameTable(table, marker))
751 return false; // Name table was not markable.
752 updateTableChecksum = true;
754 table = QByteArray::fromRawData(ttf.constData() + offset, length);
757 tableRecord->offset = qToBigEndian(markedTtf.size());
758 tableRecord->length = qToBigEndian(table.size());
759 markedTtf.append(table);
760 markedTtf.append(QByteArray(dWordPadding(table.size()), 0)); // 0-padding
761 if (updateTableChecksum) {
762 TableRecord *tableRecord = // Need to recalculate, since markedTtf changed
763 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
764 const quint32 offset = qFromBigEndian(tableRecord->offset);
765 const quint32 length = qFromBigEndian(tableRecord->length);
766 tableRecord->checkSum = qToBigEndian(ttfCalcChecksum(markedTtf.constData() + offset, length));
769 if (indexOfHeadTable == -1 // 'head' table is mandatory
770 || ttfDataSize != ttfLength) // We do not allow ttf data "holes". Neither does Symbian.
772 TableRecord *headRecord =
773 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + indexOfHeadTable * sizeof(TableRecord));
774 quint32 *checkSumAdjustmentTag =
775 reinterpret_cast<quint32*>(markedTtf.data() + qFromBigEndian(headRecord->offset) + ttfCheckSumAdjustmentOffset);
776 *checkSumAdjustmentTag = 0;
777 const quint32 ttfChecksum = ttfCalcChecksum(markedTtf.constData(), markedTtf.count());
778 *checkSumAdjustmentTag = qToBigEndian(ttfChecksumNumber - ttfChecksum);
783 static inline bool ttfCanSymbianLoadFont(const QByteArray &data, const QString &fileName)
788 QFileInfo info(fileName);
789 if (!data.isEmpty()) {
790 QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
791 + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
792 + QLatin1String("XXXXXX.ttf"));
793 if (!tempfile.open() || tempfile.write(data) == -1)
795 ttfFileName = QDir::toNativeSeparators(QFileInfo(tempfile).canonicalFilePath());
796 tempfile.setAutoRemove(false);
798 tempFileGuard.setFileName(ttfFileName);
799 if (!tempFileGuard.open(QIODevice::ReadOnly))
801 } else if (info.isFile()) {
802 ttfFileName = QDir::toNativeSeparators(info.canonicalFilePath());
807 CFontStore *store = 0;
808 RHeap* heap = User::ChunkHeap(NULL, 0x1000, 0x20000);
811 CleanupClosePushL(*heap);
812 store = CFontStore::NewL(heap);
813 CleanupStack::PushL(store);
814 COpenFontRasterizer *rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
815 CleanupStack::PushL(rasterizer);
816 store->InstallRasterizerL(rasterizer);
817 CleanupStack::Pop(rasterizer);
819 TRAP_IGNORE(fontUid = store->AddFileL(qt_QString2TPtrC(ttfFileName)));
820 if (fontUid.iUid != -1)
822 CleanupStack::PopAndDestroy(2, heap); // heap, store
826 if (tempFileGuard.isOpen())
827 tempFileGuard.remove();
832 static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
834 if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
835 || fnt->data.size() > ttfMaxFileSize // hard limit
836 || fnt->data.isEmpty() && (!fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive) // Only buffer or .ttf
837 || QFileInfo(fnt->fileName).size() > ttfMaxFileSize)) // hard limit
840 // Using ttfCanSymbianLoadFont() causes crashes on app destruction (Symbian^3|PR1 and lower).
841 // Therefore, not using it for now, but eventually in a later version.
842 // if (!ttfCanSymbianLoadFont(fnt->data, fnt->fileName))
845 QFontDatabasePrivate *db = privateDb();
852 QSymbianFontDatabaseExtrasImplementation *dbExtras =
853 static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
857 const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
859 // The QTemporaryFile object being used in the following section must be
860 // destructed before letting Symbian load the TTF file. Symbian would not
861 // load it otherwise, because QTemporaryFile will still keep some handle
862 // on it. The scope is used to reduce the life time of the QTemporaryFile.
863 // In order to prevent other processes from modifying the file between the
864 // moment where the QTemporaryFile is destructed and the file is loaded by
865 // Symbian, we have a QFile "tempFileGuard" outside the scope which opens
866 // the file in ReadOnly mode while the QTemporaryFile is still alive.
869 QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
870 + marker + QLatin1String("XXXXXX.ttf"));
871 if (!tempfile.open())
873 const QString tempFileName = QFileInfo(tempfile).canonicalFilePath();
874 if (fnt->data.isEmpty()) {
875 QFile sourceFile(fnt->fileName);
876 if (!sourceFile.open(QIODevice::ReadOnly))
878 fnt->data = sourceFile.readAll();
880 if (!ttfMarkAppFont(fnt->data, marker) || tempfile.write(fnt->data) == -1)
882 tempfile.setAutoRemove(false);
883 tempfile.close(); // Tempfile still keeps a file handle, forbidding write access
884 fnt->data.clear(); // The TTF data was marked and saved. Not needed in memory, anymore.
885 tempFileGuard.setFileName(tempFileName);
886 if (!tempFileGuard.open(QIODevice::ReadOnly))
888 fnt->temporaryFileName = tempFileName;
891 const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName);
892 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
893 const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer();
895 S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId);
896 tempFileGuard.close(); // Did its job
897 const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer();
898 if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device?
899 int fontOnServerIndex = fontsOnServerAfter.count() - 1;
900 for (int i = 0; i < fontsOnServerBefore.count(); i++) {
901 if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) {
902 fontOnServerIndex = i;
907 // Must remove all font engines with their CFonts, first.
908 QFontCache::instance()->clear();
910 QSymbianFontDatabaseExtrasImplementation::clear();
912 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable())
913 fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName));
915 const QString &appFontName = fontsOnServerAfter.at(fontOnServerIndex);
916 fnt->families.append(qt_symbian_appFontNameWithoutMarker(appFontName));
917 if (!qt_symbian_fontNameHasAppFontMarker(appFontName)
918 || !registerScreenDeviceFont(fontOnServerIndex, dbExtras))
919 dbExtras->removeAppFontData(fnt);
921 if (fnt->screenDeviceFontFileId > 0)
922 S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); // May still have the file open!
923 QFile::remove(fnt->temporaryFileName);
924 *fnt = QFontDatabasePrivate::ApplicationFont();
929 bool QFontDatabase::removeApplicationFont(int handle)
931 QMutexLocker locker(fontDatabaseMutex());
933 QFontDatabasePrivate *db = privateDb();
934 if (!db || handle < 0 || handle >= db->applicationFonts.count())
936 QSymbianFontDatabaseExtrasImplementation *dbExtras =
937 static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
941 QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle];
942 if (fnt->families.isEmpty())
943 return true; // Nothing to remove. Return peacefully.
945 // Must remove all font engines with their CFonts, first
946 QFontCache::instance()->clear();
948 dbExtras->removeAppFontData(fnt);
950 db->invalidate(); // This will just emit 'fontDatabaseChanged()'
954 bool QFontDatabase::removeAllApplicationFonts()
956 QMutexLocker locker(fontDatabaseMutex());
958 const int applicationFontsCount = privateDb()->applicationFonts.count();
959 for (int i = 0; i < applicationFontsCount; ++i)
960 if (!removeApplicationFont(i))
965 bool QFontDatabase::supportsThreadedFontRendering()
967 return QSymbianTypeFaceExtras::symbianFontTableApiAvailable();
971 QFontDef cleanedFontDef(const QFontDef &req)
973 QFontDef result = req;
974 if (result.pixelSize <= 0) {
975 result.pixelSize = QFontEngineS60::pointsToPixels(qMax(qreal(1.0), result.pointSize));
976 result.pointSize = 0;
981 QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QFontDef &req)
983 const QFontCache::Key key(cleanedFontDef(req), script);
985 if (!privateDb()->count)
988 QFontEngine *fe = QFontCache::instance()->findEngine(key);
990 // Making sure that fe->fontDef.family will be an existing font.
992 QFontDatabasePrivate *db = privateDb();
994 QList<int> blacklistedFamilies;
995 match(script, key.def, key.def.family, QString(), -1, &desc, blacklistedFamilies);
996 if (!desc.family) // falling back to application font
997 desc.family = db->family(QApplication::font().defaultFamily());
998 Q_ASSERT(desc.family);
1000 // Making sure that desc.family supports the requested script
1001 QtFontDesc mappedDesc;
1002 bool supportsScript = false;
1004 match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
1005 if (mappedDesc.family == desc.family) {
1006 supportsScript = true;
1009 blacklistedFamilies.append(mappedDesc.familyIndex);
1010 } while (mappedDesc.family);
1011 if (!supportsScript) {
1012 blacklistedFamilies.clear();
1013 match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
1014 if (mappedDesc.family)
1018 const QString fontFamily = desc.family->name;
1019 QFontDef request = req;
1020 request.family = fontFamily;
1021 #ifdef QT_NO_FREETYPE
1022 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
1023 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
1024 const QSymbianTypeFaceExtras *typeFaceExtras =
1025 dbExtras->extras(fontFamily, request.weight > QFont::Normal, request.style != QFont::StyleNormal);
1027 // We need a valid pixelSize, e.g. for lineThickness()
1028 if (request.pixelSize < 0)
1029 request.pixelSize = request.pointSize * d->dpi / 72;
1031 fe = new QFontEngineS60(request, typeFaceExtras);
1032 #else // QT_NO_FREETYPE
1034 QFontEngine::FaceId faceId;
1035 const QtFontFamily * const reqQtFontFamily = db->family(fontFamily);
1036 faceId.filename = reqQtFontFamily->fontFilename;
1037 faceId.index = reqQtFontFamily->fontFileIndex;
1039 QFontEngineFTS60 *fte = new QFontEngineFTS60(cleanedFontDef(request));
1040 if (fte->init(faceId, true, QFontEngineFT::Format_A8))
1044 #endif // QT_NO_FREETYPE
1047 if (script == QUnicodeTables::Common
1048 && !(req.styleStrategy & QFont::NoFontMerging)
1051 QStringList commonFonts;
1052 for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
1053 if (scriptForWritingSystem[ws] != script)
1055 for (int i = 0; i < db->count; ++i) {
1056 if (db->families[i]->writingSystems[ws] & QtFontFamily::Supported)
1057 commonFonts.append(db->families[i]->name);
1061 // Hack: Prioritize .ccc fonts
1062 const QString niceEastAsianFont(QLatin1String("Sans MT 936_S60"));
1063 if (commonFonts.removeAll(niceEastAsianFont) > 0)
1064 commonFonts.prepend(niceEastAsianFont);
1066 fe = new QFontEngineMultiS60(fe, script, commonFonts);
1070 QFontCache::instance()->insertEngine(key, fe);
1074 void QFontDatabase::load(const QFontPrivate *d, int script)
1076 QFontEngine *fe = 0;
1077 QFontDef req = d->request;
1079 if (!d->engineData) {
1080 const QFontCache::Key key(cleanedFontDef(req), script);
1081 getEngineData(d, key);
1084 // the cached engineData could have already loaded the engine we want
1085 if (d->engineData->engines[script])
1086 fe = d->engineData->engines[script];
1089 if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
1090 fe = new QTestFontEngine(req.pixelSize);
1093 fe = findFont(script, d, req);
1095 d->engineData->engines[script] = fe;