1 /****************************************************************************
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
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 #if !defined(SYMBIAN_VERSION_9_4) && !defined(SYMBIAN_VERSION_9_3) && !defined(SYMBIAN_VERSION_9_2)
62 #define SYMBIAN_LINKEDFONTS_SUPPORTED
63 #endif // !SYMBIAN_VERSION_9_4
67 bool qt_symbian_isLinkedFont(const TDesC &typefaceName) // Also used in qfont_s60.cpp
69 bool isLinkedFont = false;
70 #ifdef SYMBIAN_LINKEDFONTS_SUPPORTED
71 const QString name((const QChar*)typefaceName.Ptr(), typefaceName.Length());
72 isLinkedFont = name.endsWith(QLatin1String("LF")) && name == name.toUpper();
73 #endif // SYMBIAN_LINKEDFONTS_SUPPORTED
77 QStringList qt_symbian_fontFamiliesOnFontServer() // Also used in qfont_s60.cpp
80 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
81 const int numTypeFaces = S60->screenDevice()->NumTypefaces();
82 for (int i = 0; i < numTypeFaces; i++) {
83 TTypefaceSupport typefaceSupport;
84 S60->screenDevice()->TypefaceSupport(typefaceSupport, i);
85 const QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
86 result.append(familyName);
92 QFileInfoList alternativeFilePaths(const QString &path, const QStringList &nameFilters,
93 QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort,
94 bool uniqueFileNames = true)
98 // Prepare a 'soft to hard' drive list: W:, X: ... A:, Z:
99 QStringList driveStrings;
100 foreach (const QFileInfo &drive, QDir::drives())
101 driveStrings.append(drive.absolutePath());
103 const QString zDriveString(QLatin1String("Z:/"));
104 driveStrings.removeAll(zDriveString);
105 driveStrings.prepend(zDriveString);
107 QStringList uniqueFileNameList;
108 for (int i = driveStrings.count() - 1; i >= 0; --i) {
109 const QDir dirOnDrive(driveStrings.at(i) + path);
110 const QFileInfoList entriesOnDrive = dirOnDrive.entryInfoList(nameFilters, filters, sort);
111 if (uniqueFileNames) {
112 foreach(const QFileInfo &entry, entriesOnDrive) {
113 if (!uniqueFileNameList.contains(entry.fileName())) {
114 uniqueFileNameList.append(entry.fileName());
115 result.append(entry);
119 result.append(entriesOnDrive);
125 #ifdef QT_NO_FREETYPE
126 class QSymbianFontDatabaseExtrasImplementation : public QSymbianFontDatabaseExtras
129 QSymbianFontDatabaseExtrasImplementation();
130 ~QSymbianFontDatabaseExtrasImplementation();
132 const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const;
133 void removeAppFontData(QFontDatabasePrivate::ApplicationFont *fnt);
134 static inline bool appFontLimitReached();
135 TUid addFontFileToFontStore(const QFileInfo &fontFileInfo);
138 static inline QString tempAppFontFolder();
139 static const QString appFontMarkerPrefix;
140 static QString appFontMarker(); // 'qaf<shortUid[+shortPid]>'
142 struct CFontFromFontStoreReleaser {
143 static inline void cleanup(CFont *font)
147 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
148 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras);
149 dbExtras->m_store->ReleaseFont(font);
153 struct CFontFromScreenDeviceReleaser {
154 static inline void cleanup(CFont *font)
158 S60->screenDevice()->ReleaseFont(font);
162 // m_heap, m_store, m_rasterizer and m_extras are used if Symbian
163 // does not provide the Font Table API
166 COpenFontRasterizer *m_rasterizer;
167 mutable QList<const QSymbianTypeFaceExtras *> m_extras;
169 mutable QSet<QString> m_applicationFontFamilies;
172 const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix =
175 inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
177 return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\');
180 QString QSymbianFontDatabaseExtrasImplementation::appFontMarker()
182 static QString result;
183 if (result.isEmpty()) {
185 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
186 // We are allowed to load app fonts even from previous, crashed runs
187 // of this application, since we can access the font tables.
188 const quint32 uid = RProcess().Type().MostDerived().iUid;
189 id = static_cast<quint16>(uid + (uid >> 16));
191 // If no font table Api is available, we must not even load a font
192 // from a previous (crashed) run of this application. Reason: we
193 // won't get the font tables, they are not in the CFontStore.
194 // So, we use the pid, for more uniqueness.
195 id = static_cast<quint16>(RProcess().Id().Id());
197 result = appFontMarkerPrefix + QString::fromLatin1("%1").arg(id & 0x7fff, 3, 32, QLatin1Char('0'));
198 Q_ASSERT(appFontMarkerPrefix.length() == 1 && result.length() == 4);
203 static inline bool qt_symbian_fontNameHasAppFontMarker(const QString &fontName)
205 const int idLength = 3; // Keep in sync with id length in appFontMarker().
206 const QString &prefix = QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix;
207 if (fontName.length() < prefix.length() + idLength
208 || fontName.mid(fontName.length() - idLength - prefix.length(), prefix.length()) != prefix)
210 // Testing if the the id is base32 data
211 for (int i = fontName.length() - idLength; i < fontName.length(); ++i) {
212 const QChar &c = fontName.at(i);
213 if (!(c >= QLatin1Char('0') && c <= QLatin1Char('9')
214 || c >= QLatin1Char('a') && c <= QLatin1Char('v')))
220 // If fontName is an application font of this app, prepend the app font marker
221 QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName)
223 QFontDatabasePrivate *db = privateDb();
225 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
226 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
227 return dbExtras->m_applicationFontFamilies.contains(fontName) ?
228 fontName + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
232 static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName)
234 return markedFontName.left(markedFontName.length()
235 - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length());
238 QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation()
240 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
242 filters.append(QLatin1String("*.ttf"));
243 filters.append(QLatin1String("*.ccc"));
244 filters.append(QLatin1String("*.ltt"));
245 const QFileInfoList fontFiles = alternativeFilePaths(QLatin1String("resource\\Fonts"), filters);
247 const TInt heapMinLength = 0x1000;
248 const TInt heapMaxLength = qMax(0x20000 * fontFiles.count(), heapMinLength);
249 m_heap = User::ChunkHeap(NULL, heapMinLength, heapMaxLength);
251 m_store = CFontStore::NewL(m_heap);
252 m_rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
253 CleanupStack::PushL(m_rasterizer);
254 m_store->InstallRasterizerL(m_rasterizer);
255 CleanupStack::Pop(m_rasterizer););
257 foreach (const QFileInfo &fontFileInfo, fontFiles)
258 addFontFileToFontStore(fontFileInfo);
262 void QSymbianFontDatabaseExtrasImplementation::clear()
264 QFontDatabasePrivate *db = privateDb();
267 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
268 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
270 return; // initializeDb() has never been called
271 QSymbianTypeFaceExtrasHash &extrasHash = S60->fontData();
272 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
273 qDeleteAll(extrasHash);
275 typedef QList<const QSymbianTypeFaceExtras *>::iterator iterator;
276 for (iterator p = dbExtras->m_extras.begin(); p != dbExtras->m_extras.end(); ++p) {
277 dbExtras->m_store->ReleaseFont((*p)->fontOwner());
280 dbExtras->m_extras.clear();
285 void qt_cleanup_symbianFontDatabase()
287 static bool cleanupDone = false;
292 QFontDatabasePrivate *db = privateDb();
296 QSymbianFontDatabaseExtrasImplementation::clear();
298 if (!db->applicationFonts.isEmpty()) {
299 QFontDatabase::removeAllApplicationFonts();
300 // We remove the left over temporary font files of Qt application.
301 // Active fonts are undeletable since the font server holds a handle
302 // on them, so we do not need to worry to delete other running
303 // applications' fonts.
304 const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder());
305 const QStringList filter(
306 QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf"));
307 foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter))
308 QFile(ttfFile.absoluteFilePath()).remove();
309 db->applicationFonts.clear();
313 QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation()
315 qt_cleanup_symbianFontDatabase();
316 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
322 #ifndef FNTSTORE_H_INLINES_SUPPORT_FMM
324 Workaround: fntstore.h has an inlined function 'COpenFont* CBitmapFont::OpenFont()'
325 that returns a private data member. The header will change between SDKs. But Qt has
326 to build on any SDK version and run on other versions of Symbian OS.
327 This function performs the needed pointer arithmetic to get the right COpenFont*
329 COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont)
331 const TInt offsetIOpenFont = 92; // '_FOFF(CBitmapFont, iOpenFont)' ..if iOpenFont weren't private
332 const TUint valueIOpenFont = *(TUint*)PtrAdd(aBitmapFont, offsetIOpenFont);
333 return (valueIOpenFont & 1) ?
334 (COpenFont*)PtrAdd(aBitmapFont, valueIOpenFont & ~1) : // New behavior: iOpenFont is offset
335 (COpenFont*)valueIOpenFont; // Old behavior: iOpenFont is pointer
337 #endif // FNTSTORE_H_INLINES_SUPPORT_FMM
339 const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &aTypeface,
340 bool bold, bool italic) const
342 QSymbianTypeFaceExtrasHash &extrasHash = S60->fontData();
343 if (extrasHash.isEmpty() && QThread::currentThread() != QApplication::instance()->thread())
344 S60->addThreadLocalReleaseFunc(clear);
345 const QString typeface = qt_symbian_fontNameWithAppFontMarker(aTypeface);
346 const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic));
347 if (!extrasHash.contains(searchKey)) {
348 TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1);
350 searchSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold);
352 searchSpec.iFontStyle.SetPosture(EPostureItalic);
355 if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
356 const TInt err = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(font, searchSpec);
357 Q_ASSERT(err == KErrNone && font);
358 QScopedPointer<CFont, CFontFromScreenDeviceReleaser> sFont(font);
359 QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font);
361 extrasHash.insert(searchKey, extras);
363 const TInt err = m_store->GetNearestFontToDesignHeightInPixels(font, searchSpec);
364 Q_ASSERT(err == KErrNone && font);
365 const CBitmapFont *bitmapFont = static_cast<CBitmapFont*>(font);
366 COpenFont *openFont =
367 #ifdef FNTSTORE_H_INLINES_SUPPORT_FMM
368 bitmapFont->OpenFont();
369 #else // FNTSTORE_H_INLINES_SUPPORT_FMM
370 OpenFontFromBitmapFont(bitmapFont);
371 #endif // FNTSTORE_H_INLINES_SUPPORT_FMM
372 const TOpenFontFaceAttrib* const attrib = openFont->FaceAttrib();
373 const QString foundKey =
374 QString((const QChar*)attrib->FullName().Ptr(), attrib->FullName().Length());
375 if (!extrasHash.contains(foundKey)) {
376 QScopedPointer<CFont, CFontFromFontStoreReleaser> sFont(font);
377 QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font, openFont);
379 m_extras.append(extras);
380 extrasHash.insert(searchKey, extras);
381 extrasHash.insert(foundKey, extras);
383 m_store->ReleaseFont(font);
384 extrasHash.insert(searchKey, extrasHash.value(foundKey));
388 return extrasHash.value(searchKey);
391 void QSymbianFontDatabaseExtrasImplementation::removeAppFontData(
392 QFontDatabasePrivate::ApplicationFont *fnt)
395 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()
396 && fnt->fontStoreFontFileUid.iUid != 0)
397 m_store->RemoveFile(fnt->fontStoreFontFileUid);
398 if (!fnt->families.isEmpty())
399 m_applicationFontFamilies.remove(fnt->families.first());
400 if (fnt->screenDeviceFontFileId != 0)
401 S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId);
402 QFile::remove(fnt->temporaryFileName);
403 *fnt = QFontDatabasePrivate::ApplicationFont();
406 bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
408 QFontDatabasePrivate *db = privateDb();
411 const int maxAppFonts = 5;
412 int registeredAppFonts = 0;
413 foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts)
414 if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts)
419 TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo)
421 Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable());
422 const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath());
423 const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile));
425 TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr));
429 #else // QT_NO_FREETYPE
430 class QFontEngineFTS60 : public QFontEngineFT
433 QFontEngineFTS60(const QFontDef &fd);
436 QFontEngineFTS60::QFontEngineFTS60(const QFontDef &fd)
439 default_hint_style = HintFull;
441 #endif // QT_NO_FREETYPE
444 QFontEngineS60::pixelsToPoints, QFontEngineS60::pointsToPixels, QFontEngineMultiS60::QFontEngineMultiS60
445 and QFontEngineMultiS60::QFontEngineMultiS60 should be in qfontengine_s60.cpp. But since also the
446 Freetype based font rendering need them, they are here.
448 qreal QFontEngineS60::pixelsToPoints(qreal pixels, Qt::Orientation orientation)
450 CWsScreenDevice* device = S60->screenDevice();
451 return (orientation == Qt::Horizontal?
452 device->HorizontalPixelsToTwips(pixels)
453 :device->VerticalPixelsToTwips(pixels)) / KTwipsPerPoint;
456 qreal QFontEngineS60::pointsToPixels(qreal points, Qt::Orientation orientation)
458 CWsScreenDevice* device = S60->screenDevice();
459 const int twips = points * KTwipsPerPoint;
460 return orientation == Qt::Horizontal?
461 device->HorizontalTwipsToPixels(twips)
462 :device->VerticalTwipsToPixels(twips);
465 QFontEngineMultiS60::QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies)
466 : QFontEngineMulti(fallbackFamilies.size() + 1)
468 , m_fallbackFamilies(fallbackFamilies)
472 fontDef = engines[0]->fontDef;
475 void QFontEngineMultiS60::loadEngine(int at)
477 Q_ASSERT(at < engines.size());
478 Q_ASSERT(engines.at(at) == 0);
480 QFontDef request = fontDef;
481 request.styleStrategy |= QFont::NoFontMerging;
482 request.family = m_fallbackFamilies.at(at-1);
483 engines[at] = QFontDatabase::findFont(m_script,
486 Q_ASSERT(engines[at]);
489 #ifdef QT_NO_FREETYPE
490 static bool registerScreenDeviceFont(int screenDeviceFontIndex,
491 const QSymbianFontDatabaseExtrasImplementation *dbExtras)
493 TTypefaceSupport typefaceSupport;
494 S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex);
496 if (qt_symbian_isLinkedFont(typefaceSupport.iTypeface.iName))
499 QString familyName((const QChar*)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length());
500 if (qt_symbian_fontNameHasAppFontMarker(familyName)) {
501 const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
502 if (familyName.endsWith(marker)) {
503 familyName = qt_symbian_appFontNameWithoutMarker(familyName);
504 dbExtras->m_applicationFontFamilies.insert(familyName);
506 return false; // This was somebody else's application font. Skip it.
510 CFont *font; // We have to get a font instance in order to know all the details
511 TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11);
512 if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone)
514 QScopedPointer<CFont, QSymbianFontDatabaseExtrasImplementation::CFontFromScreenDeviceReleaser> sFont(font);
515 if (font->TypeUid() != KCFbsFontUid)
517 TOpenFontFaceAttrib faceAttrib;
518 const CFbsFont *cfbsFont = static_cast<const CFbsFont *>(font);
519 cfbsFont->GetFaceAttrib(faceAttrib);
521 QtFontStyle::Key styleKey;
522 styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal;
523 styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal;
525 QtFontFamily *family = privateDb()->family(familyName, true);
526 family->fixedPitch = faceAttrib.IsMonoWidth();
527 QtFontFoundry *foundry = family->foundry(QString(), true);
528 QtFontStyle *style = foundry->style(styleKey, QString(), true);
529 style->smoothScalable = typefaceSupport.iIsScalable;
530 style->pixelSize(0, true);
532 const QSymbianTypeFaceExtras *typeFaceExtras =
533 dbExtras->extras(familyName, faceAttrib.IsBold(), faceAttrib.IsItalic());
534 const QByteArray os2Table = typeFaceExtras->getSfntTable(MAKE_TAG('O', 'S', '/', '2'));
535 const unsigned char* data = reinterpret_cast<const unsigned char*>(os2Table.constData());
536 const unsigned char* ulUnicodeRange = data + 42;
537 quint32 unicodeRange[4] = {
538 qFromBigEndian<quint32>(ulUnicodeRange),
539 qFromBigEndian<quint32>(ulUnicodeRange + 4),
540 qFromBigEndian<quint32>(ulUnicodeRange + 8),
541 qFromBigEndian<quint32>(ulUnicodeRange + 12)
543 const unsigned char* ulCodePageRange = data + 78;
544 quint32 codePageRange[2] = {
545 qFromBigEndian<quint32>(ulCodePageRange),
546 qFromBigEndian<quint32>(ulCodePageRange + 4)
548 const QList<QFontDatabase::WritingSystem> writingSystems =
549 qt_determine_writing_systems_from_truetype_bits(unicodeRange, codePageRange);
550 foreach (const QFontDatabase::WritingSystem system, writingSystems)
551 family->writingSystems[system] = QtFontFamily::Supported;
556 static void initializeDb()
558 QFontDatabasePrivate *db = privateDb();
562 #ifdef QT_NO_FREETYPE
563 if (!db->symbianExtras)
564 db->symbianExtras = new QSymbianFontDatabaseExtrasImplementation;
566 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
568 const int numTypeFaces = S60->screenDevice()->NumTypefaces();
569 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
570 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
571 for (int i = 0; i < numTypeFaces; i++)
572 registerScreenDeviceFont(i, dbExtras);
574 // We have to clear/release all CFonts, here, in case one of the fonts is
575 // an application font of another running Qt app. Otherwise the other Qt app
576 // cannot remove it's application font, anymore -> "Zombie Font".
577 QSymbianFontDatabaseExtrasImplementation::clear();
581 #else // QT_NO_FREETYPE
582 QDir dir(QDesktopServices::storageLocation(QDesktopServices::FontsLocation));
583 dir.setNameFilters(QStringList() << QLatin1String("*.ttf")
584 << QLatin1String("*.ttc") << QLatin1String("*.pfa")
585 << QLatin1String("*.pfb"));
586 for (int i = 0; i < int(dir.count()); ++i) {
587 const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i]));
590 #endif // QT_NO_FREETYPE
593 static inline void load(const QString &family = QString(), int script = -1)
602 quint16 numTables, searchRange, entrySelector, rangeShift;
606 quint32 tag, checkSum, offset, length;
609 struct NameTableHead {
610 quint16 format, count, stringOffset;
614 quint16 platformID, encodingID, languageID, nameID, length, offset;
617 static quint32 ttfCalcChecksum(const char *data, quint32 bytesCount)
620 const quint32 *ptr = reinterpret_cast<const quint32*>(data);
621 const quint32 *endPtr =
622 ptr + (bytesCount + sizeof(quint32) - 1) / sizeof(quint32);
623 while (ptr < endPtr) {
624 const quint32 unit32Value = *ptr++;
625 result += qFromBigEndian(unit32Value);
630 static inline quint32 toDWordBoundary(quint32 value)
632 return (value + 3) & ~3;
635 static inline quint32 dWordPadding(quint32 value)
637 return (4 - (value & 3)) & 3;
640 static inline bool ttfMarkNameTable(QByteArray &table, const QString &marker)
642 const quint32 tableLength = static_cast<quint32>(table.size());
644 if (tableLength > 50000 // hard limit
645 || tableLength < sizeof(NameTableHead)) // corrupt name table
648 const NameTableHead *head = reinterpret_cast<const NameTableHead*>(table.constData());
649 const quint16 count = qFromBigEndian(head->count);
650 const quint16 stringOffset = qFromBigEndian(head->stringOffset);
651 if (count > 200 // hard limit
652 || stringOffset >= tableLength // corrupt name table
653 || sizeof(NameTableHead) + count * sizeof(NameRecord) >= tableLength) // corrupt name table
656 QTextEncoder encoder(QTextCodec::codecForName("UTF-16BE"), QTextCodec::IgnoreHeader);
657 const QByteArray markerUtf16BE = encoder.fromUnicode(marker);
658 const QByteArray markerAscii = marker.toAscii();
660 QByteArray markedTable;
661 markedTable.reserve(tableLength + marker.length() * 20); // Original size plus some extra
662 markedTable.append(table, stringOffset);
663 QByteArray markedStrings;
664 quint32 stringDataCount = stringOffset;
665 for (quint16 i = 0; i < count; ++i) {
666 const quint32 nameRecordOffset = sizeof(NameTableHead) + sizeof(NameRecord) * i;
667 NameRecord *nameRecord =
668 reinterpret_cast<NameRecord*>(markedTable.data() + nameRecordOffset);
669 const quint16 nameID = qFromBigEndian(nameRecord->nameID);
670 const quint16 platformID = qFromBigEndian(nameRecord->platformID);
671 const quint16 encodingID = qFromBigEndian(nameRecord->encodingID);
672 const quint16 offset = qFromBigEndian(nameRecord->offset);
673 const quint16 length = qFromBigEndian(nameRecord->length);
674 stringDataCount += length;
675 if (stringDataCount > 80000 // hard limit. String data may be > name table size. Multiple records can reference the same string.
676 || static_cast<quint32>(stringOffset + offset + length) > tableLength) // String outside bounds
678 const bool needsMarker =
679 nameID == 1 || nameID == 3 || nameID == 4 || nameID == 16 || nameID == 21;
680 const bool isUnicode =
681 platformID == 0 || platformID == 3 && encodingID == 1;
682 const QByteArray originalString =
683 QByteArray::fromRawData(table.constData() + stringOffset + offset, length);
684 QByteArray markedString;
686 const int maxBytesLength = (KMaxTypefaceNameLength - marker.length()) * (isUnicode ? 2 : 1);
687 markedString = originalString.left(maxBytesLength) + (isUnicode ? markerUtf16BE : markerAscii);
689 markedString = originalString;
691 nameRecord->offset = qToBigEndian(static_cast<quint16>(markedStrings.length()));
692 nameRecord->length = qToBigEndian(static_cast<quint16>(markedString.length()));
693 markedStrings.append(markedString);
695 markedTable.append(markedStrings);
700 const quint32 ttfMaxFileSize = 3500000;
702 static inline bool ttfMarkAppFont(QByteArray &ttf, const QString &marker)
704 const quint32 ttfChecksumNumber = 0xb1b0afba;
705 const quint32 alignment = 4;
706 const quint32 ttfLength = static_cast<quint32>(ttf.size());
707 if (ttfLength > ttfMaxFileSize // hard limit
708 || ttfLength % alignment != 0 // ttf sizes are always factors of 4
709 || ttfLength <= sizeof(OffsetTable) // ttf too short
710 || ttfCalcChecksum(ttf.constData(), ttf.size()) != ttfChecksumNumber) // ttf checksum is invalid
713 const OffsetTable *offsetTable = reinterpret_cast<const OffsetTable*>(ttf.constData());
714 const quint16 numTables = qFromBigEndian(offsetTable->numTables);
715 const quint32 recordsLength =
716 toDWordBoundary(sizeof(OffsetTable) + numTables * sizeof(TableRecord));
717 if (numTables > 30 // hard limit
718 || recordsLength + numTables * alignment > ttfLength) // Corrupt ttf. Tables would not fit, even if empty.
721 QByteArray markedTtf;
722 markedTtf.reserve(ttfLength + marker.length() * 20); // Original size plus some extra
723 markedTtf.append(ttf.constData(), recordsLength);
725 const quint32 ttfCheckSumAdjustmentOffset = 8; // Offset from the start of 'head'
726 int indexOfHeadTable = -1;
727 quint32 ttfDataSize = recordsLength;
728 typedef QPair<quint32, quint32> Range;
729 QList<Range> memoryRanges;
730 memoryRanges.reserve(numTables);
731 for (int i = 0; i < numTables; ++i) {
732 TableRecord *tableRecord =
733 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
734 const quint32 offset = qFromBigEndian(tableRecord->offset);
735 const quint32 length = qFromBigEndian(tableRecord->length);
736 const quint32 lengthAligned = toDWordBoundary(length);
737 ttfDataSize += lengthAligned;
738 if (offset < recordsLength // must not intersect ttf header/records
739 || offset % alignment != 0 // must be aligned
740 || offset > ttfLength - alignment // table out of bounds
741 || offset + lengthAligned > ttfLength // table out of bounds
742 || ttfDataSize > ttfLength) // tables would not fit into the ttf
745 foreach (const Range &range, memoryRanges)
746 if (offset < range.first + range.second && offset + lengthAligned > range.first)
747 return false; // Overlaps with another table
748 memoryRanges.append(Range(offset, lengthAligned));
750 quint32 checkSum = qFromBigEndian(tableRecord->checkSum);
751 if (tableRecord->tag == qToBigEndian(static_cast<quint32>('head'))) {
752 if (length < ttfCheckSumAdjustmentOffset + sizeof(quint32))
753 return false; // Invalid 'head' table
754 const quint32 *checkSumAdjustmentTag =
755 reinterpret_cast<const quint32*>(ttf.constData() + offset + ttfCheckSumAdjustmentOffset);
756 const quint32 checkSumAdjustment = qFromBigEndian(*checkSumAdjustmentTag);
757 checkSum += checkSumAdjustment;
758 indexOfHeadTable = i; // For the ttf checksum re-calculation, later
760 if (checkSum != ttfCalcChecksum(ttf.constData() + offset, length))
761 return false; // Table checksum is invalid
763 bool updateTableChecksum = false;
765 if (tableRecord->tag == qToBigEndian(static_cast<quint32>('name'))) {
766 table = QByteArray(ttf.constData() + offset, length);
767 if (!ttfMarkNameTable(table, marker))
768 return false; // Name table was not markable.
769 updateTableChecksum = true;
771 table = QByteArray::fromRawData(ttf.constData() + offset, length);
774 tableRecord->offset = qToBigEndian(markedTtf.size());
775 tableRecord->length = qToBigEndian(table.size());
776 markedTtf.append(table);
777 markedTtf.append(QByteArray(dWordPadding(table.size()), 0)); // 0-padding
778 if (updateTableChecksum) {
779 TableRecord *tableRecord = // Need to recalculate, since markedTtf changed
780 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord));
781 const quint32 offset = qFromBigEndian(tableRecord->offset);
782 const quint32 length = qFromBigEndian(tableRecord->length);
783 tableRecord->checkSum = qToBigEndian(ttfCalcChecksum(markedTtf.constData() + offset, length));
786 if (indexOfHeadTable == -1 // 'head' table is mandatory
787 || ttfDataSize != ttfLength) // We do not allow ttf data "holes". Neither does Symbian.
789 TableRecord *headRecord =
790 reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + indexOfHeadTable * sizeof(TableRecord));
791 quint32 *checkSumAdjustmentTag =
792 reinterpret_cast<quint32*>(markedTtf.data() + qFromBigEndian(headRecord->offset) + ttfCheckSumAdjustmentOffset);
793 *checkSumAdjustmentTag = 0;
794 const quint32 ttfChecksum = ttfCalcChecksum(markedTtf.constData(), markedTtf.count());
795 *checkSumAdjustmentTag = qToBigEndian(ttfChecksumNumber - ttfChecksum);
800 static inline bool ttfCanSymbianLoadFont(const QByteArray &data, const QString &fileName)
805 QFileInfo info(fileName);
806 if (!data.isEmpty()) {
807 QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
808 + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
809 + QLatin1String("XXXXXX.ttf"));
810 if (!tempfile.open() || tempfile.write(data) == -1)
812 ttfFileName = QDir::toNativeSeparators(QFileInfo(tempfile).canonicalFilePath());
813 tempfile.setAutoRemove(false);
815 tempFileGuard.setFileName(ttfFileName);
816 if (!tempFileGuard.open(QIODevice::ReadOnly))
818 } else if (info.isFile()) {
819 ttfFileName = QDir::toNativeSeparators(info.canonicalFilePath());
824 CFontStore *store = 0;
825 RHeap* heap = User::ChunkHeap(NULL, 0x1000, 0x20000);
828 CleanupClosePushL(*heap);
829 store = CFontStore::NewL(heap);
830 CleanupStack::PushL(store);
831 COpenFontRasterizer *rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E));
832 CleanupStack::PushL(rasterizer);
833 store->InstallRasterizerL(rasterizer);
834 CleanupStack::Pop(rasterizer);
836 TRAP_IGNORE(fontUid = store->AddFileL(qt_QString2TPtrC(ttfFileName)));
837 if (fontUid.iUid != -1)
839 CleanupStack::PopAndDestroy(2, heap); // heap, store
843 if (tempFileGuard.isOpen())
844 tempFileGuard.remove();
849 static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
851 if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached()
852 || fnt->data.size() > ttfMaxFileSize // hard limit
853 || fnt->data.isEmpty() && (!fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive) // Only buffer or .ttf
854 || QFileInfo(fnt->fileName).size() > ttfMaxFileSize)) // hard limit
857 // Using ttfCanSymbianLoadFont() causes crashes on app destruction (Symbian^3|PR1 and lower).
858 // Therefore, not using it for now, but eventually in a later version.
859 // if (!ttfCanSymbianLoadFont(fnt->data, fnt->fileName))
862 QFontDatabasePrivate *db = privateDb();
869 QSymbianFontDatabaseExtrasImplementation *dbExtras =
870 static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
874 const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker();
876 // The QTemporaryFile object being used in the following section must be
877 // destructed before letting Symbian load the TTF file. Symbian would not
878 // load it otherwise, because QTemporaryFile will still keep some handle
879 // on it. The scope is used to reduce the life time of the QTemporaryFile.
880 // In order to prevent other processes from modifying the file between the
881 // moment where the QTemporaryFile is destructed and the file is loaded by
882 // Symbian, we have a QFile "tempFileGuard" outside the scope which opens
883 // the file in ReadOnly mode while the QTemporaryFile is still alive.
886 QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
887 + marker + QLatin1String("XXXXXX.ttf"));
888 if (!tempfile.open())
890 const QString tempFileName = QFileInfo(tempfile).canonicalFilePath();
891 if (fnt->data.isEmpty()) {
892 QFile sourceFile(fnt->fileName);
893 if (!sourceFile.open(QIODevice::ReadOnly))
895 fnt->data = sourceFile.readAll();
897 if (!ttfMarkAppFont(fnt->data, marker) || tempfile.write(fnt->data) == -1)
899 tempfile.setAutoRemove(false);
900 tempfile.close(); // Tempfile still keeps a file handle, forbidding write access
901 fnt->data.clear(); // The TTF data was marked and saved. Not needed in memory, anymore.
902 tempFileGuard.setFileName(tempFileName);
903 if (!tempFileGuard.open(QIODevice::ReadOnly))
905 fnt->temporaryFileName = tempFileName;
908 const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName);
909 QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock);
910 const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer();
912 S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId);
913 tempFileGuard.close(); // Did its job
914 const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer();
915 if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device?
916 int fontOnServerIndex = fontsOnServerAfter.count() - 1;
917 for (int i = 0; i < fontsOnServerBefore.count(); i++) {
918 if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) {
919 fontOnServerIndex = i;
924 // Must remove all font engines with their CFonts, first.
925 QFontCache::instance()->clear();
927 QSymbianFontDatabaseExtrasImplementation::clear();
929 if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable())
930 fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName));
932 const QString &appFontName = fontsOnServerAfter.at(fontOnServerIndex);
933 fnt->families.append(qt_symbian_appFontNameWithoutMarker(appFontName));
934 if (!qt_symbian_fontNameHasAppFontMarker(appFontName)
935 || !registerScreenDeviceFont(fontOnServerIndex, dbExtras))
936 dbExtras->removeAppFontData(fnt);
938 if (fnt->screenDeviceFontFileId > 0)
939 S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); // May still have the file open!
940 QFile::remove(fnt->temporaryFileName);
941 *fnt = QFontDatabasePrivate::ApplicationFont();
946 bool QFontDatabase::removeApplicationFont(int handle)
948 QMutexLocker locker(fontDatabaseMutex());
950 QFontDatabasePrivate *db = privateDb();
951 if (!db || handle < 0 || handle >= db->applicationFonts.count())
953 QSymbianFontDatabaseExtrasImplementation *dbExtras =
954 static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
958 QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle];
959 if (fnt->families.isEmpty())
960 return true; // Nothing to remove. Return peacefully.
962 // Must remove all font engines with their CFonts, first
963 QFontCache::instance()->clear();
965 dbExtras->removeAppFontData(fnt);
967 db->invalidate(); // This will just emit 'fontDatabaseChanged()'
971 bool QFontDatabase::removeAllApplicationFonts()
973 QMutexLocker locker(fontDatabaseMutex());
975 const int applicationFontsCount = privateDb()->applicationFonts.count();
976 for (int i = 0; i < applicationFontsCount; ++i)
977 if (!removeApplicationFont(i))
982 bool QFontDatabase::supportsThreadedFontRendering()
984 return QSymbianTypeFaceExtras::symbianFontTableApiAvailable();
988 QFontDef cleanedFontDef(const QFontDef &req)
990 QFontDef result = req;
991 if (result.pixelSize <= 0) {
992 result.pixelSize = QFontEngineS60::pointsToPixels(qMax(qreal(1.0), result.pointSize));
993 result.pointSize = 0;
998 QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QFontDef &req)
1000 const QFontCache::Key key(cleanedFontDef(req), script);
1002 if (!privateDb()->count)
1005 QFontEngine *fe = QFontCache::instance()->findEngine(key);
1007 // Making sure that fe->fontDef.family will be an existing font.
1009 QFontDatabasePrivate *db = privateDb();
1011 QList<int> blacklistedFamilies;
1012 match(script, key.def, key.def.family, QString(), -1, &desc, blacklistedFamilies);
1013 if (!desc.family) // falling back to application font
1014 desc.family = db->family(QApplication::font().defaultFamily());
1015 Q_ASSERT(desc.family);
1017 // Making sure that desc.family supports the requested script
1018 QtFontDesc mappedDesc;
1019 bool supportsScript = false;
1021 match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
1022 if (mappedDesc.family == desc.family) {
1023 supportsScript = true;
1026 blacklistedFamilies.append(mappedDesc.familyIndex);
1027 } while (mappedDesc.family);
1028 if (!supportsScript) {
1029 blacklistedFamilies.clear();
1030 match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies);
1031 if (mappedDesc.family)
1035 const QString fontFamily = desc.family->name;
1036 QFontDef request = req;
1037 request.family = fontFamily;
1038 #ifdef QT_NO_FREETYPE
1039 const QSymbianFontDatabaseExtrasImplementation *dbExtras =
1040 static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
1041 const QSymbianTypeFaceExtras *typeFaceExtras =
1042 dbExtras->extras(fontFamily, request.weight > QFont::Normal, request.style != QFont::StyleNormal);
1044 // We need a valid pixelSize, e.g. for lineThickness()
1045 if (request.pixelSize < 0)
1046 request.pixelSize = request.pointSize * d->dpi / 72;
1048 fe = new QFontEngineS60(request, typeFaceExtras);
1049 #else // QT_NO_FREETYPE
1051 QFontEngine::FaceId faceId;
1052 const QtFontFamily * const reqQtFontFamily = db->family(fontFamily);
1053 faceId.filename = reqQtFontFamily->fontFilename;
1054 faceId.index = reqQtFontFamily->fontFileIndex;
1056 QFontEngineFTS60 *fte = new QFontEngineFTS60(cleanedFontDef(request));
1057 if (fte->init(faceId, true, QFontEngineFT::Format_A8))
1061 #endif // QT_NO_FREETYPE
1064 if (script == QUnicodeTables::Common
1065 && !(req.styleStrategy & QFont::NoFontMerging)
1068 QStringList commonFonts;
1069 for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) {
1070 if (scriptForWritingSystem[ws] != script)
1072 for (int i = 0; i < db->count; ++i) {
1073 if (db->families[i]->writingSystems[ws] & QtFontFamily::Supported)
1074 commonFonts.append(db->families[i]->name);
1078 // Hack: Prioritize .ccc fonts
1079 const QString niceEastAsianFont(QLatin1String("Sans MT 936_S60"));
1080 if (commonFonts.removeAll(niceEastAsianFont) > 0)
1081 commonFonts.prepend(niceEastAsianFont);
1083 fe = new QFontEngineMultiS60(fe, script, commonFonts);
1087 QFontCache::instance()->insertEngine(key, fe);
1091 void QFontDatabase::load(const QFontPrivate *d, int script)
1093 QFontEngine *fe = 0;
1094 QFontDef req = d->request;
1096 if (!d->engineData) {
1097 const QFontCache::Key key(cleanedFontDef(req), script);
1098 getEngineData(d, key);
1101 // the cached engineData could have already loaded the engine we want
1102 if (d->engineData->engines[script])
1103 fe = d->engineData->engines[script];
1106 if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) {
1107 fe = new QTestFontEngine(req.pixelSize);
1110 fe = findFont(script, d, req);
1112 d->engineData->engines[script] = fe;