| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the QtGui module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <private/qapplication_p.h> |
| #include "qdir.h" |
| #include "qfont_p.h" |
| #include "qfontengine_s60_p.h" |
| #include "qabstractfileengine.h" |
| #include "qdesktopservices.h" |
| #include "qtemporaryfile.h" |
| #include "qtextcodec.h" |
| #include <private/qpixmap_raster_symbian_p.h> |
| #include <private/qt_s60_p.h> |
| #include "qendian.h" |
| #include <private/qcore_symbian_p.h> |
| #ifdef QT_NO_FREETYPE |
| #include <openfont.h> |
| #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS |
| #include <graphics/openfontrasterizer.h> // COpenFontRasterizer has moved to a new header file |
| #endif // SYMBIAN_ENABLE_SPLIT_HEADERS |
| #endif // QT_NO_FREETYPE |
| |
| #if !defined(SYMBIAN_VERSION_9_4) && !defined(SYMBIAN_VERSION_9_3) && !defined(SYMBIAN_VERSION_9_2) |
| #define SYMBIAN_LINKEDFONTS_SUPPORTED |
| #endif // !SYMBIAN_VERSION_9_4 |
| |
| QT_BEGIN_NAMESPACE |
| |
| bool qt_symbian_isLinkedFont(const TDesC &typefaceName) // Also used in qfont_s60.cpp |
| { |
| bool isLinkedFont = false; |
| #ifdef SYMBIAN_LINKEDFONTS_SUPPORTED |
| const QString name((const QChar*)typefaceName.Ptr(), typefaceName.Length()); |
| isLinkedFont = name.endsWith(QLatin1String("LF")) && name == name.toUpper(); |
| #endif // SYMBIAN_LINKEDFONTS_SUPPORTED |
| return isLinkedFont; |
| } |
| |
| QStringList qt_symbian_fontFamiliesOnFontServer() // Also used in qfont_s60.cpp |
| { |
| QStringList result; |
| QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); |
| const int numTypeFaces = S60->screenDevice()->NumTypefaces(); |
| for (int i = 0; i < numTypeFaces; i++) { |
| TTypefaceSupport typefaceSupport; |
| S60->screenDevice()->TypefaceSupport(typefaceSupport, i); |
| const QString familyName((const QChar *)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length()); |
| result.append(familyName); |
| } |
| lock.relock(); |
| return result; |
| } |
| |
| QFileInfoList alternativeFilePaths(const QString &path, const QStringList &nameFilters, |
| QDir::Filters filters = QDir::NoFilter, QDir::SortFlags sort = QDir::NoSort, |
| bool uniqueFileNames = true) |
| { |
| QFileInfoList result; |
| |
| // Prepare a 'soft to hard' drive list: W:, X: ... A:, Z: |
| QStringList driveStrings; |
| foreach (const QFileInfo &drive, QDir::drives()) |
| driveStrings.append(drive.absolutePath()); |
| driveStrings.sort(); |
| const QString zDriveString(QLatin1String("Z:/")); |
| driveStrings.removeAll(zDriveString); |
| driveStrings.prepend(zDriveString); |
| |
| QStringList uniqueFileNameList; |
| for (int i = driveStrings.count() - 1; i >= 0; --i) { |
| const QDir dirOnDrive(driveStrings.at(i) + path); |
| const QFileInfoList entriesOnDrive = dirOnDrive.entryInfoList(nameFilters, filters, sort); |
| if (uniqueFileNames) { |
| foreach(const QFileInfo &entry, entriesOnDrive) { |
| if (!uniqueFileNameList.contains(entry.fileName())) { |
| uniqueFileNameList.append(entry.fileName()); |
| result.append(entry); |
| } |
| } |
| } else { |
| result.append(entriesOnDrive); |
| } |
| } |
| return result; |
| } |
| |
| #ifdef QT_NO_FREETYPE |
| class QSymbianFontDatabaseExtrasImplementation : public QSymbianFontDatabaseExtras |
| { |
| public: |
| QSymbianFontDatabaseExtrasImplementation(); |
| ~QSymbianFontDatabaseExtrasImplementation(); |
| |
| const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const; |
| void removeAppFontData(QFontDatabasePrivate::ApplicationFont *fnt); |
| static inline bool appFontLimitReached(); |
| TUid addFontFileToFontStore(const QFileInfo &fontFileInfo); |
| static void clear(); |
| |
| static inline QString tempAppFontFolder(); |
| static const QString appFontMarkerPrefix; |
| static QString appFontMarker(); // 'qaf<shortUid[+shortPid]>' |
| |
| struct CFontFromFontStoreReleaser { |
| static inline void cleanup(CFont *font) |
| { |
| if (!font) |
| return; |
| const QSymbianFontDatabaseExtrasImplementation *dbExtras = |
| static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras); |
| dbExtras->m_store->ReleaseFont(font); |
| } |
| }; |
| |
| struct CFontFromScreenDeviceReleaser { |
| static inline void cleanup(CFont *font) |
| { |
| if (!font) |
| return; |
| S60->screenDevice()->ReleaseFont(font); |
| } |
| }; |
| |
| // m_heap, m_store, m_rasterizer and m_extras are used if Symbian |
| // does not provide the Font Table API |
| RHeap* m_heap; |
| CFontStore *m_store; |
| COpenFontRasterizer *m_rasterizer; |
| mutable QList<const QSymbianTypeFaceExtras *> m_extras; |
| |
| mutable QSet<QString> m_applicationFontFamilies; |
| }; |
| |
| const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix = |
| QLatin1String("Q"); |
| |
| inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() |
| { |
| return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\'); |
| } |
| |
| QString QSymbianFontDatabaseExtrasImplementation::appFontMarker() |
| { |
| static QString result; |
| if (result.isEmpty()) { |
| quint16 id = 0; |
| if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { |
| // We are allowed to load app fonts even from previous, crashed runs |
| // of this application, since we can access the font tables. |
| const quint32 uid = RProcess().Type().MostDerived().iUid; |
| id = static_cast<quint16>(uid + (uid >> 16)); |
| } else { |
| // If no font table Api is available, we must not even load a font |
| // from a previous (crashed) run of this application. Reason: we |
| // won't get the font tables, they are not in the CFontStore. |
| // So, we use the pid, for more uniqueness. |
| id = static_cast<quint16>(RProcess().Id().Id()); |
| } |
| result = appFontMarkerPrefix + QString::fromLatin1("%1").arg(id & 0x7fff, 3, 32, QLatin1Char('0')); |
| Q_ASSERT(appFontMarkerPrefix.length() == 1 && result.length() == 4); |
| } |
| return result; |
| } |
| |
| static inline bool qt_symbian_fontNameHasAppFontMarker(const QString &fontName) |
| { |
| const int idLength = 3; // Keep in sync with id length in appFontMarker(). |
| const QString &prefix = QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix; |
| if (fontName.length() < prefix.length() + idLength |
| || fontName.mid(fontName.length() - idLength - prefix.length(), prefix.length()) != prefix) |
| return false; |
| // Testing if the the id is base32 data |
| for (int i = fontName.length() - idLength; i < fontName.length(); ++i) { |
| const QChar &c = fontName.at(i); |
| if (!(c >= QLatin1Char('0') && c <= QLatin1Char('9') |
| || c >= QLatin1Char('a') && c <= QLatin1Char('v'))) |
| return false; |
| } |
| return true; |
| } |
| |
| // If fontName is an application font of this app, prepend the app font marker |
| QString qt_symbian_fontNameWithAppFontMarker(const QString &fontName) |
| { |
| QFontDatabasePrivate *db = privateDb(); |
| Q_ASSERT(db); |
| const QSymbianFontDatabaseExtrasImplementation *dbExtras = |
| static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); |
| return dbExtras->m_applicationFontFamilies.contains(fontName) ? |
| fontName + QSymbianFontDatabaseExtrasImplementation::appFontMarker() |
| : fontName; |
| } |
| |
| static inline QString qt_symbian_appFontNameWithoutMarker(const QString &markedFontName) |
| { |
| return markedFontName.left(markedFontName.length() |
| - QSymbianFontDatabaseExtrasImplementation::appFontMarker().length()); |
| } |
| |
| QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation() |
| { |
| if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { |
| QStringList filters; |
| filters.append(QLatin1String("*.ttf")); |
| filters.append(QLatin1String("*.ccc")); |
| filters.append(QLatin1String("*.ltt")); |
| const QFileInfoList fontFiles = alternativeFilePaths(QLatin1String("resource\\Fonts"), filters); |
| |
| const TInt heapMinLength = 0x1000; |
| const TInt heapMaxLength = qMax(0x20000 * fontFiles.count(), heapMinLength); |
| m_heap = User::ChunkHeap(NULL, heapMinLength, heapMaxLength); |
| QT_TRAP_THROWING( |
| m_store = CFontStore::NewL(m_heap); |
| m_rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E)); |
| CleanupStack::PushL(m_rasterizer); |
| m_store->InstallRasterizerL(m_rasterizer); |
| CleanupStack::Pop(m_rasterizer);); |
| |
| foreach (const QFileInfo &fontFileInfo, fontFiles) |
| addFontFileToFontStore(fontFileInfo); |
| } |
| } |
| |
| void QSymbianFontDatabaseExtrasImplementation::clear() |
| { |
| QFontDatabasePrivate *db = privateDb(); |
| if (!db) |
| return; |
| const QSymbianFontDatabaseExtrasImplementation *dbExtras = |
| static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); |
| if (!dbExtras) |
| return; // initializeDb() has never been called |
| QSymbianTypeFaceExtrasHash &extrasHash = S60->fontData(); |
| if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { |
| qDeleteAll(extrasHash); |
| } else { |
| typedef QList<const QSymbianTypeFaceExtras *>::iterator iterator; |
| for (iterator p = dbExtras->m_extras.begin(); p != dbExtras->m_extras.end(); ++p) { |
| dbExtras->m_store->ReleaseFont((*p)->fontOwner()); |
| delete *p; |
| } |
| dbExtras->m_extras.clear(); |
| } |
| extrasHash.clear(); |
| } |
| |
| void qt_cleanup_symbianFontDatabase() |
| { |
| static bool cleanupDone = false; |
| if (cleanupDone) |
| return; |
| cleanupDone = true; |
| |
| QFontDatabasePrivate *db = privateDb(); |
| if (!db) |
| return; |
| |
| QSymbianFontDatabaseExtrasImplementation::clear(); |
| |
| if (!db->applicationFonts.isEmpty()) { |
| QFontDatabase::removeAllApplicationFonts(); |
| // We remove the left over temporary font files of Qt application. |
| // Active fonts are undeletable since the font server holds a handle |
| // on them, so we do not need to worry to delete other running |
| // applications' fonts. |
| const QDir dir(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()); |
| const QStringList filter( |
| QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix + QLatin1String("*.ttf")); |
| foreach (const QFileInfo &ttfFile, dir.entryInfoList(filter)) |
| QFile(ttfFile.absoluteFilePath()).remove(); |
| db->applicationFonts.clear(); |
| } |
| } |
| |
| QSymbianFontDatabaseExtrasImplementation::~QSymbianFontDatabaseExtrasImplementation() |
| { |
| qt_cleanup_symbianFontDatabase(); |
| if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { |
| delete m_store; |
| m_heap->Close(); |
| } |
| } |
| |
| #ifndef FNTSTORE_H_INLINES_SUPPORT_FMM |
| /* |
| Workaround: fntstore.h has an inlined function 'COpenFont* CBitmapFont::OpenFont()' |
| that returns a private data member. The header will change between SDKs. But Qt has |
| to build on any SDK version and run on other versions of Symbian OS. |
| This function performs the needed pointer arithmetic to get the right COpenFont* |
| */ |
| COpenFont* OpenFontFromBitmapFont(const CBitmapFont* aBitmapFont) |
| { |
| const TInt offsetIOpenFont = 92; // '_FOFF(CBitmapFont, iOpenFont)' ..if iOpenFont weren't private |
| const TUint valueIOpenFont = *(TUint*)PtrAdd(aBitmapFont, offsetIOpenFont); |
| return (valueIOpenFont & 1) ? |
| (COpenFont*)PtrAdd(aBitmapFont, valueIOpenFont & ~1) : // New behavior: iOpenFont is offset |
| (COpenFont*)valueIOpenFont; // Old behavior: iOpenFont is pointer |
| } |
| #endif // FNTSTORE_H_INLINES_SUPPORT_FMM |
| |
| const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(const QString &aTypeface, |
| bool bold, bool italic) const |
| { |
| QSymbianTypeFaceExtrasHash &extrasHash = S60->fontData(); |
| if (extrasHash.isEmpty() && QThread::currentThread() != QApplication::instance()->thread()) |
| S60->addThreadLocalReleaseFunc(clear); |
| const QString typeface = qt_symbian_fontNameWithAppFontMarker(aTypeface); |
| const QString searchKey = typeface + QString::number(int(bold)) + QString::number(int(italic)); |
| if (!extrasHash.contains(searchKey)) { |
| TFontSpec searchSpec(qt_QString2TPtrC(typeface), 1); |
| if (bold) |
| searchSpec.iFontStyle.SetStrokeWeight(EStrokeWeightBold); |
| if (italic) |
| searchSpec.iFontStyle.SetPosture(EPostureItalic); |
| |
| CFont* font = NULL; |
| if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) { |
| const TInt err = S60->screenDevice()->GetNearestFontToDesignHeightInPixels(font, searchSpec); |
| Q_ASSERT(err == KErrNone && font); |
| QScopedPointer<CFont, CFontFromScreenDeviceReleaser> sFont(font); |
| QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font); |
| sFont.take(); |
| extrasHash.insert(searchKey, extras); |
| } else { |
| const TInt err = m_store->GetNearestFontToDesignHeightInPixels(font, searchSpec); |
| Q_ASSERT(err == KErrNone && font); |
| const CBitmapFont *bitmapFont = static_cast<CBitmapFont*>(font); |
| COpenFont *openFont = |
| #ifdef FNTSTORE_H_INLINES_SUPPORT_FMM |
| bitmapFont->OpenFont(); |
| #else // FNTSTORE_H_INLINES_SUPPORT_FMM |
| OpenFontFromBitmapFont(bitmapFont); |
| #endif // FNTSTORE_H_INLINES_SUPPORT_FMM |
| const TOpenFontFaceAttrib* const attrib = openFont->FaceAttrib(); |
| const QString foundKey = |
| QString((const QChar*)attrib->FullName().Ptr(), attrib->FullName().Length()); |
| if (!extrasHash.contains(foundKey)) { |
| QScopedPointer<CFont, CFontFromFontStoreReleaser> sFont(font); |
| QSymbianTypeFaceExtras *extras = new QSymbianTypeFaceExtras(font, openFont); |
| sFont.take(); |
| m_extras.append(extras); |
| extrasHash.insert(searchKey, extras); |
| extrasHash.insert(foundKey, extras); |
| } else { |
| m_store->ReleaseFont(font); |
| extrasHash.insert(searchKey, extrasHash.value(foundKey)); |
| } |
| } |
| } |
| return extrasHash.value(searchKey); |
| } |
| |
| void QSymbianFontDatabaseExtrasImplementation::removeAppFontData( |
| QFontDatabasePrivate::ApplicationFont *fnt) |
| { |
| clear(); |
| if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable() |
| && fnt->fontStoreFontFileUid.iUid != 0) |
| m_store->RemoveFile(fnt->fontStoreFontFileUid); |
| if (!fnt->families.isEmpty()) |
| m_applicationFontFamilies.remove(fnt->families.first()); |
| if (fnt->screenDeviceFontFileId != 0) |
| S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); |
| QFile::remove(fnt->temporaryFileName); |
| *fnt = QFontDatabasePrivate::ApplicationFont(); |
| } |
| |
| bool QSymbianFontDatabaseExtrasImplementation::appFontLimitReached() |
| { |
| QFontDatabasePrivate *db = privateDb(); |
| if (!db) |
| return false; |
| const int maxAppFonts = 5; |
| int registeredAppFonts = 0; |
| foreach (const QFontDatabasePrivate::ApplicationFont &appFont, db->applicationFonts) |
| if (!appFont.families.isEmpty() && ++registeredAppFonts == maxAppFonts) |
| return true; |
| return false; |
| } |
| |
| TUid QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo) |
| { |
| Q_ASSERT(!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()); |
| const QString fontFile = QDir::toNativeSeparators(fontFileInfo.absoluteFilePath()); |
| const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile)); |
| TUid fontUid = {0}; |
| TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr)); |
| return fontUid; |
| } |
| |
| #else // QT_NO_FREETYPE |
| class QFontEngineFTS60 : public QFontEngineFT |
| { |
| public: |
| QFontEngineFTS60(const QFontDef &fd); |
| }; |
| |
| QFontEngineFTS60::QFontEngineFTS60(const QFontDef &fd) |
| : QFontEngineFT(fd) |
| { |
| default_hint_style = HintFull; |
| } |
| #endif // QT_NO_FREETYPE |
| |
| /* |
| QFontEngineS60::pixelsToPoints, QFontEngineS60::pointsToPixels, QFontEngineMultiS60::QFontEngineMultiS60 |
| and QFontEngineMultiS60::QFontEngineMultiS60 should be in qfontengine_s60.cpp. But since also the |
| Freetype based font rendering need them, they are here. |
| */ |
| qreal QFontEngineS60::pixelsToPoints(qreal pixels, Qt::Orientation orientation) |
| { |
| CWsScreenDevice* device = S60->screenDevice(); |
| return (orientation == Qt::Horizontal? |
| device->HorizontalPixelsToTwips(pixels) |
| :device->VerticalPixelsToTwips(pixels)) / KTwipsPerPoint; |
| } |
| |
| qreal QFontEngineS60::pointsToPixels(qreal points, Qt::Orientation orientation) |
| { |
| CWsScreenDevice* device = S60->screenDevice(); |
| const int twips = points * KTwipsPerPoint; |
| return orientation == Qt::Horizontal? |
| device->HorizontalTwipsToPixels(twips) |
| :device->VerticalTwipsToPixels(twips); |
| } |
| |
| QFontEngineMultiS60::QFontEngineMultiS60(QFontEngine *first, int script, const QStringList &fallbackFamilies) |
| : QFontEngineMulti(fallbackFamilies.size() + 1) |
| , m_script(script) |
| , m_fallbackFamilies(fallbackFamilies) |
| { |
| engines[0] = first; |
| first->ref.ref(); |
| fontDef = engines[0]->fontDef; |
| } |
| |
| void QFontEngineMultiS60::loadEngine(int at) |
| { |
| Q_ASSERT(at < engines.size()); |
| Q_ASSERT(engines.at(at) == 0); |
| |
| QFontDef request = fontDef; |
| request.styleStrategy |= QFont::NoFontMerging; |
| request.family = m_fallbackFamilies.at(at-1); |
| engines[at] = QFontDatabase::findFont(m_script, |
| /*fontprivate*/0, |
| request); |
| Q_ASSERT(engines[at]); |
| } |
| |
| static bool registerScreenDeviceFont(int screenDeviceFontIndex, |
| const QSymbianFontDatabaseExtrasImplementation *dbExtras) |
| { |
| TTypefaceSupport typefaceSupport; |
| S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex); |
| |
| if (qt_symbian_isLinkedFont(typefaceSupport.iTypeface.iName)) |
| return false; |
| |
| QString familyName((const QChar*)typefaceSupport.iTypeface.iName.Ptr(), typefaceSupport.iTypeface.iName.Length()); |
| if (qt_symbian_fontNameHasAppFontMarker(familyName)) { |
| const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker(); |
| if (familyName.endsWith(marker)) { |
| familyName = qt_symbian_appFontNameWithoutMarker(familyName); |
| dbExtras->m_applicationFontFamilies.insert(familyName); |
| } else { |
| return false; // This was somebody else's application font. Skip it. |
| } |
| } |
| |
| CFont *font; // We have to get a font instance in order to know all the details |
| TFontSpec fontSpec(typefaceSupport.iTypeface.iName, 11); |
| if (S60->screenDevice()->GetNearestFontInPixels(font, fontSpec) != KErrNone) |
| return false; |
| QScopedPointer<CFont, QSymbianFontDatabaseExtrasImplementation::CFontFromScreenDeviceReleaser> sFont(font); |
| if (font->TypeUid() != KCFbsFontUid) |
| return false; |
| TOpenFontFaceAttrib faceAttrib; |
| const CFbsFont *cfbsFont = static_cast<const CFbsFont *>(font); |
| cfbsFont->GetFaceAttrib(faceAttrib); |
| |
| QtFontStyle::Key styleKey; |
| styleKey.style = faceAttrib.IsItalic()?QFont::StyleItalic:QFont::StyleNormal; |
| styleKey.weight = faceAttrib.IsBold()?QFont::Bold:QFont::Normal; |
| |
| QtFontFamily *family = privateDb()->family(familyName, true); |
| family->fixedPitch = faceAttrib.IsMonoWidth(); |
| QtFontFoundry *foundry = family->foundry(QString(), true); |
| QtFontStyle *style = foundry->style(styleKey, true); |
| style->smoothScalable = typefaceSupport.iIsScalable; |
| style->pixelSize(0, true); |
| |
| const QSymbianTypeFaceExtras *typeFaceExtras = |
| dbExtras->extras(familyName, faceAttrib.IsBold(), faceAttrib.IsItalic()); |
| const QByteArray os2Table = typeFaceExtras->getSfntTable(MAKE_TAG('O', 'S', '/', '2')); |
| const unsigned char* data = reinterpret_cast<const unsigned char*>(os2Table.constData()); |
| const unsigned char* ulUnicodeRange = data + 42; |
| quint32 unicodeRange[4] = { |
| qFromBigEndian<quint32>(ulUnicodeRange), |
| qFromBigEndian<quint32>(ulUnicodeRange + 4), |
| qFromBigEndian<quint32>(ulUnicodeRange + 8), |
| qFromBigEndian<quint32>(ulUnicodeRange + 12) |
| }; |
| const unsigned char* ulCodePageRange = data + 78; |
| quint32 codePageRange[2] = { |
| qFromBigEndian<quint32>(ulCodePageRange), |
| qFromBigEndian<quint32>(ulCodePageRange + 4) |
| }; |
| const QList<QFontDatabase::WritingSystem> writingSystems = |
| determineWritingSystemsFromTrueTypeBits(unicodeRange, codePageRange); |
| foreach (const QFontDatabase::WritingSystem system, writingSystems) |
| family->writingSystems[system] = QtFontFamily::Supported; |
| return true; |
| } |
| |
| static void initializeDb() |
| { |
| QFontDatabasePrivate *db = privateDb(); |
| if(!db || db->count) |
| return; |
| |
| #ifdef QT_NO_FREETYPE |
| if (!db->symbianExtras) |
| db->symbianExtras = new QSymbianFontDatabaseExtrasImplementation; |
| |
| QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); |
| |
| const int numTypeFaces = S60->screenDevice()->NumTypefaces(); |
| const QSymbianFontDatabaseExtrasImplementation *dbExtras = |
| static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); |
| for (int i = 0; i < numTypeFaces; i++) |
| registerScreenDeviceFont(i, dbExtras); |
| |
| // We have to clear/release all CFonts, here, in case one of the fonts is |
| // an application font of another running Qt app. Otherwise the other Qt app |
| // cannot remove it's application font, anymore -> "Zombie Font". |
| QSymbianFontDatabaseExtrasImplementation::clear(); |
| |
| lock.relock(); |
| |
| #else // QT_NO_FREETYPE |
| QDir dir(QDesktopServices::storageLocation(QDesktopServices::FontsLocation)); |
| dir.setNameFilters(QStringList() << QLatin1String("*.ttf") |
| << QLatin1String("*.ttc") << QLatin1String("*.pfa") |
| << QLatin1String("*.pfb")); |
| for (int i = 0; i < int(dir.count()); ++i) { |
| const QByteArray file = QFile::encodeName(dir.absoluteFilePath(dir[i])); |
| db->addTTFile(file); |
| } |
| #endif // QT_NO_FREETYPE |
| } |
| |
| static inline void load(const QString &family = QString(), int script = -1) |
| { |
| Q_UNUSED(family) |
| Q_UNUSED(script) |
| initializeDb(); |
| } |
| |
| struct OffsetTable { |
| quint32 sfntVersion; |
| quint16 numTables, searchRange, entrySelector, rangeShift; |
| }; |
| |
| struct TableRecord { |
| quint32 tag, checkSum, offset, length; |
| }; |
| |
| struct NameTableHead { |
| quint16 format, count, stringOffset; |
| }; |
| |
| struct NameRecord { |
| quint16 platformID, encodingID, languageID, nameID, length, offset; |
| }; |
| |
| static quint32 ttfCalcChecksum(const char *data, quint32 bytesCount) |
| { |
| quint32 result = 0; |
| const quint32 *ptr = reinterpret_cast<const quint32*>(data); |
| const quint32 *endPtr = |
| ptr + (bytesCount + sizeof(quint32) - 1) / sizeof(quint32); |
| while (ptr < endPtr) { |
| const quint32 unit32Value = *ptr++; |
| result += qFromBigEndian(unit32Value); |
| } |
| return result; |
| } |
| |
| static inline quint32 toDWordBoundary(quint32 value) |
| { |
| return (value + 3) & ~3; |
| } |
| |
| static inline quint32 dWordPadding(quint32 value) |
| { |
| return (4 - (value & 3)) & 3; |
| } |
| |
| static inline bool ttfMarkNameTable(QByteArray &table, const QString &marker) |
| { |
| const quint32 tableLength = static_cast<quint32>(table.size()); |
| |
| if (tableLength > 50000 // hard limit |
| || tableLength < sizeof(NameTableHead)) // corrupt name table |
| return false; |
| |
| const NameTableHead *head = reinterpret_cast<const NameTableHead*>(table.constData()); |
| const quint16 count = qFromBigEndian(head->count); |
| const quint16 stringOffset = qFromBigEndian(head->stringOffset); |
| if (count > 200 // hard limit |
| || stringOffset >= tableLength // corrupt name table |
| || sizeof(NameTableHead) + count * sizeof(NameRecord) >= tableLength) // corrupt name table |
| return false; |
| |
| QTextEncoder encoder(QTextCodec::codecForName("UTF-16BE"), QTextCodec::IgnoreHeader); |
| const QByteArray markerUtf16BE = encoder.fromUnicode(marker); |
| const QByteArray markerAscii = marker.toAscii(); |
| |
| QByteArray markedTable; |
| markedTable.reserve(tableLength + marker.length() * 20); // Original size plus some extra |
| markedTable.append(table, stringOffset); |
| QByteArray markedStrings; |
| quint32 stringDataCount = stringOffset; |
| for (quint16 i = 0; i < count; ++i) { |
| const quint32 nameRecordOffset = sizeof(NameTableHead) + sizeof(NameRecord) * i; |
| NameRecord *nameRecord = |
| reinterpret_cast<NameRecord*>(markedTable.data() + nameRecordOffset); |
| const quint16 nameID = qFromBigEndian(nameRecord->nameID); |
| const quint16 platformID = qFromBigEndian(nameRecord->platformID); |
| const quint16 encodingID = qFromBigEndian(nameRecord->encodingID); |
| const quint16 offset = qFromBigEndian(nameRecord->offset); |
| const quint16 length = qFromBigEndian(nameRecord->length); |
| stringDataCount += length; |
| if (stringDataCount > 80000 // hard limit. String data may be > name table size. Multiple records can reference the same string. |
| || static_cast<quint32>(stringOffset + offset + length) > tableLength) // String outside bounds |
| return false; |
| const bool needsMarker = |
| nameID == 1 || nameID == 3 || nameID == 4 || nameID == 16 || nameID == 21; |
| const bool isUnicode = |
| platformID == 0 || platformID == 3 && encodingID == 1; |
| const QByteArray originalString = |
| QByteArray::fromRawData(table.constData() + stringOffset + offset, length); |
| QByteArray markedString; |
| if (needsMarker) { |
| const int maxBytesLength = (KMaxTypefaceNameLength - marker.length()) * (isUnicode ? 2 : 1); |
| markedString = originalString.left(maxBytesLength) + (isUnicode ? markerUtf16BE : markerAscii); |
| } else { |
| markedString = originalString; |
| } |
| nameRecord->offset = qToBigEndian(static_cast<quint16>(markedStrings.length())); |
| nameRecord->length = qToBigEndian(static_cast<quint16>(markedString.length())); |
| markedStrings.append(markedString); |
| } |
| markedTable.append(markedStrings); |
| table = markedTable; |
| return true; |
| } |
| |
| const quint32 ttfMaxFileSize = 3500000; |
| |
| static inline bool ttfMarkAppFont(QByteArray &ttf, const QString &marker) |
| { |
| const quint32 ttfChecksumNumber = 0xb1b0afba; |
| const quint32 alignment = 4; |
| const quint32 ttfLength = static_cast<quint32>(ttf.size()); |
| if (ttfLength > ttfMaxFileSize // hard limit |
| || ttfLength % alignment != 0 // ttf sizes are always factors of 4 |
| || ttfLength <= sizeof(OffsetTable) // ttf too short |
| || ttfCalcChecksum(ttf.constData(), ttf.size()) != ttfChecksumNumber) // ttf checksum is invalid |
| return false; |
| |
| const OffsetTable *offsetTable = reinterpret_cast<const OffsetTable*>(ttf.constData()); |
| const quint16 numTables = qFromBigEndian(offsetTable->numTables); |
| const quint32 recordsLength = |
| toDWordBoundary(sizeof(OffsetTable) + numTables * sizeof(TableRecord)); |
| if (numTables > 30 // hard limit |
| || recordsLength + numTables * alignment > ttfLength) // Corrupt ttf. Tables would not fit, even if empty. |
| return false; |
| |
| QByteArray markedTtf; |
| markedTtf.reserve(ttfLength + marker.length() * 20); // Original size plus some extra |
| markedTtf.append(ttf.constData(), recordsLength); |
| |
| const quint32 ttfCheckSumAdjustmentOffset = 8; // Offset from the start of 'head' |
| int indexOfHeadTable = -1; |
| quint32 ttfDataSize = recordsLength; |
| typedef QPair<quint32, quint32> Range; |
| QList<Range> memoryRanges; |
| memoryRanges.reserve(numTables); |
| for (int i = 0; i < numTables; ++i) { |
| TableRecord *tableRecord = |
| reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord)); |
| const quint32 offset = qFromBigEndian(tableRecord->offset); |
| const quint32 length = qFromBigEndian(tableRecord->length); |
| const quint32 lengthAligned = toDWordBoundary(length); |
| ttfDataSize += lengthAligned; |
| if (offset < recordsLength // must not intersect ttf header/records |
| || offset % alignment != 0 // must be aligned |
| || offset > ttfLength - alignment // table out of bounds |
| || offset + lengthAligned > ttfLength // table out of bounds |
| || ttfDataSize > ttfLength) // tables would not fit into the ttf |
| return false; |
| |
| foreach (const Range &range, memoryRanges) |
| if (offset < range.first + range.second && offset + lengthAligned > range.first) |
| return false; // Overlaps with another table |
| memoryRanges.append(Range(offset, lengthAligned)); |
| |
| quint32 checkSum = qFromBigEndian(tableRecord->checkSum); |
| if (tableRecord->tag == qToBigEndian(static_cast<quint32>('head'))) { |
| if (length < ttfCheckSumAdjustmentOffset + sizeof(quint32)) |
| return false; // Invalid 'head' table |
| const quint32 *checkSumAdjustmentTag = |
| reinterpret_cast<const quint32*>(ttf.constData() + offset + ttfCheckSumAdjustmentOffset); |
| const quint32 checkSumAdjustment = qFromBigEndian(*checkSumAdjustmentTag); |
| checkSum += checkSumAdjustment; |
| indexOfHeadTable = i; // For the ttf checksum re-calculation, later |
| } |
| if (checkSum != ttfCalcChecksum(ttf.constData() + offset, length)) |
| return false; // Table checksum is invalid |
| |
| bool updateTableChecksum = false; |
| QByteArray table; |
| if (tableRecord->tag == qToBigEndian(static_cast<quint32>('name'))) { |
| table = QByteArray(ttf.constData() + offset, length); |
| if (!ttfMarkNameTable(table, marker)) |
| return false; // Name table was not markable. |
| updateTableChecksum = true; |
| } else { |
| table = QByteArray::fromRawData(ttf.constData() + offset, length); |
| } |
| |
| tableRecord->offset = qToBigEndian(markedTtf.size()); |
| tableRecord->length = qToBigEndian(table.size()); |
| markedTtf.append(table); |
| markedTtf.append(QByteArray(dWordPadding(table.size()), 0)); // 0-padding |
| if (updateTableChecksum) { |
| TableRecord *tableRecord = // Need to recalculate, since markedTtf changed |
| reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + i * sizeof(TableRecord)); |
| const quint32 offset = qFromBigEndian(tableRecord->offset); |
| const quint32 length = qFromBigEndian(tableRecord->length); |
| tableRecord->checkSum = qToBigEndian(ttfCalcChecksum(markedTtf.constData() + offset, length)); |
| } |
| } |
| if (indexOfHeadTable == -1 // 'head' table is mandatory |
| || ttfDataSize != ttfLength) // We do not allow ttf data "holes". Neither does Symbian. |
| return false; |
| TableRecord *headRecord = |
| reinterpret_cast<TableRecord*>(markedTtf.data() + sizeof(OffsetTable) + indexOfHeadTable * sizeof(TableRecord)); |
| quint32 *checkSumAdjustmentTag = |
| reinterpret_cast<quint32*>(markedTtf.data() + qFromBigEndian(headRecord->offset) + ttfCheckSumAdjustmentOffset); |
| *checkSumAdjustmentTag = 0; |
| const quint32 ttfChecksum = ttfCalcChecksum(markedTtf.constData(), markedTtf.count()); |
| *checkSumAdjustmentTag = qToBigEndian(ttfChecksumNumber - ttfChecksum); |
| ttf = markedTtf; |
| return true; |
| } |
| |
| static inline bool ttfCanSymbianLoadFont(const QByteArray &data, const QString &fileName) |
| { |
| bool result = false; |
| QString ttfFileName; |
| QFile tempFileGuard; |
| QFileInfo info(fileName); |
| if (!data.isEmpty()) { |
| QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() |
| + QSymbianFontDatabaseExtrasImplementation::appFontMarker() |
| + QLatin1String("XXXXXX.ttf")); |
| if (!tempfile.open() || tempfile.write(data) == -1) |
| return false; |
| ttfFileName = QDir::toNativeSeparators(QFileInfo(tempfile).canonicalFilePath()); |
| tempfile.setAutoRemove(false); |
| tempfile.close(); |
| tempFileGuard.setFileName(ttfFileName); |
| if (!tempFileGuard.open(QIODevice::ReadOnly)) |
| return false; |
| } else if (info.isFile()) { |
| ttfFileName = QDir::toNativeSeparators(info.canonicalFilePath()); |
| } else { |
| return false; |
| } |
| |
| CFontStore *store = 0; |
| RHeap* heap = User::ChunkHeap(NULL, 0x1000, 0x20000); |
| if (heap) { |
| QT_TRAP_THROWING( |
| CleanupClosePushL(*heap); |
| store = CFontStore::NewL(heap); |
| CleanupStack::PushL(store); |
| COpenFontRasterizer *rasterizer = COpenFontRasterizer::NewL(TUid::Uid(0x101F7F5E)); |
| CleanupStack::PushL(rasterizer); |
| store->InstallRasterizerL(rasterizer); |
| CleanupStack::Pop(rasterizer); |
| TUid fontUid = {-1}; |
| TRAP_IGNORE(fontUid = store->AddFileL(qt_QString2TPtrC(ttfFileName))); |
| if (fontUid.iUid != -1) |
| result = true; |
| CleanupStack::PopAndDestroy(2, heap); // heap, store |
| ); |
| } |
| |
| if (tempFileGuard.isOpen()) |
| tempFileGuard.remove(); |
| |
| return result; |
| } |
| |
| static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt) |
| { |
| if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached() |
| || fnt->data.size() > ttfMaxFileSize // hard limit |
| || fnt->data.isEmpty() && (!fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive) // Only buffer or .ttf |
| || QFileInfo(fnt->fileName).size() > ttfMaxFileSize)) // hard limit |
| return; |
| |
| // Using ttfCanSymbianLoadFont() causes crashes on app destruction (Symbian^3|PR1 and lower). |
| // Therefore, not using it for now, but eventually in a later version. |
| // if (!ttfCanSymbianLoadFont(fnt->data, fnt->fileName)) |
| // return; |
| |
| QFontDatabasePrivate *db = privateDb(); |
| if (!db) |
| return; |
| |
| if (!db->count) |
| initializeDb(); |
| |
| QSymbianFontDatabaseExtrasImplementation *dbExtras = |
| static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); |
| if (!dbExtras) |
| return; |
| |
| const QString &marker = QSymbianFontDatabaseExtrasImplementation::appFontMarker(); |
| |
| // The QTemporaryFile object being used in the following section must be |
| // destructed before letting Symbian load the TTF file. Symbian would not |
| // load it otherwise, because QTemporaryFile will still keep some handle |
| // on it. The scope is used to reduce the life time of the QTemporaryFile. |
| // In order to prevent other processes from modifying the file between the |
| // moment where the QTemporaryFile is destructed and the file is loaded by |
| // Symbian, we have a QFile "tempFileGuard" outside the scope which opens |
| // the file in ReadOnly mode while the QTemporaryFile is still alive. |
| QFile tempFileGuard; |
| { |
| QTemporaryFile tempfile(QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder() |
| + marker + QLatin1String("XXXXXX.ttf")); |
| if (!tempfile.open()) |
| return; |
| const QString tempFileName = QFileInfo(tempfile).canonicalFilePath(); |
| if (fnt->data.isEmpty()) { |
| QFile sourceFile(fnt->fileName); |
| if (!sourceFile.open(QIODevice::ReadOnly)) |
| return; |
| fnt->data = sourceFile.readAll(); |
| } |
| if (!ttfMarkAppFont(fnt->data, marker) || tempfile.write(fnt->data) == -1) |
| return; |
| tempfile.setAutoRemove(false); |
| tempfile.close(); // Tempfile still keeps a file handle, forbidding write access |
| fnt->data.clear(); // The TTF data was marked and saved. Not needed in memory, anymore. |
| tempFileGuard.setFileName(tempFileName); |
| if (!tempFileGuard.open(QIODevice::ReadOnly)) |
| return; |
| fnt->temporaryFileName = tempFileName; |
| } |
| |
| const QString fullFileName = QDir::toNativeSeparators(fnt->temporaryFileName); |
| QSymbianFbsHeapLock lock(QSymbianFbsHeapLock::Unlock); |
| const QStringList fontsOnServerBefore = qt_symbian_fontFamiliesOnFontServer(); |
| const TInt err = |
| S60->screenDevice()->AddFile(qt_QString2TPtrC(fullFileName), fnt->screenDeviceFontFileId); |
| tempFileGuard.close(); // Did its job |
| const QStringList fontsOnServerAfter = qt_symbian_fontFamiliesOnFontServer(); |
| if (err == KErrNone && fontsOnServerBefore.count() < fontsOnServerAfter.count()) { // Added to screen device? |
| int fontOnServerIndex = fontsOnServerAfter.count() - 1; |
| for (int i = 0; i < fontsOnServerBefore.count(); i++) { |
| if (fontsOnServerBefore.at(i) != fontsOnServerAfter.at(i)) { |
| fontOnServerIndex = i; |
| break; |
| } |
| } |
| |
| // Must remove all font engines with their CFonts, first. |
| QFontCache::instance()->clear(); |
| db->free(); |
| QSymbianFontDatabaseExtrasImplementation::clear(); |
| |
| if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) |
| fnt->fontStoreFontFileUid = dbExtras->addFontFileToFontStore(QFileInfo(fullFileName)); |
| |
| const QString &appFontName = fontsOnServerAfter.at(fontOnServerIndex); |
| fnt->families.append(qt_symbian_appFontNameWithoutMarker(appFontName)); |
| if (!qt_symbian_fontNameHasAppFontMarker(appFontName) |
| || !registerScreenDeviceFont(fontOnServerIndex, dbExtras)) |
| dbExtras->removeAppFontData(fnt); |
| } else { |
| if (fnt->screenDeviceFontFileId > 0) |
| S60->screenDevice()->RemoveFile(fnt->screenDeviceFontFileId); // May still have the file open! |
| QFile::remove(fnt->temporaryFileName); |
| *fnt = QFontDatabasePrivate::ApplicationFont(); |
| } |
| lock.relock(); |
| } |
| |
| bool QFontDatabase::removeApplicationFont(int handle) |
| { |
| QMutexLocker locker(fontDatabaseMutex()); |
| |
| QFontDatabasePrivate *db = privateDb(); |
| if (!db || handle < 0 || handle >= db->applicationFonts.count()) |
| return false; |
| QSymbianFontDatabaseExtrasImplementation *dbExtras = |
| static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); |
| if (!dbExtras) |
| return false; |
| |
| QFontDatabasePrivate::ApplicationFont *fnt = &db->applicationFonts[handle]; |
| if (fnt->families.isEmpty()) |
| return true; // Nothing to remove. Return peacefully. |
| |
| // Must remove all font engines with their CFonts, first |
| QFontCache::instance()->clear(); |
| db->free(); |
| dbExtras->removeAppFontData(fnt); |
| |
| db->invalidate(); // This will just emit 'fontDatabaseChanged()' |
| return true; |
| } |
| |
| bool QFontDatabase::removeAllApplicationFonts() |
| { |
| QMutexLocker locker(fontDatabaseMutex()); |
| |
| const int applicationFontsCount = privateDb()->applicationFonts.count(); |
| for (int i = 0; i < applicationFontsCount; ++i) |
| if (!removeApplicationFont(i)) |
| return false; |
| return true; |
| } |
| |
| bool QFontDatabase::supportsThreadedFontRendering() |
| { |
| return QSymbianTypeFaceExtras::symbianFontTableApiAvailable(); |
| } |
| |
| static |
| QFontDef cleanedFontDef(const QFontDef &req) |
| { |
| QFontDef result = req; |
| if (result.pixelSize <= 0) { |
| result.pixelSize = QFontEngineS60::pointsToPixels(qMax(qreal(1.0), result.pointSize)); |
| result.pointSize = 0; |
| } |
| return result; |
| } |
| |
| QFontEngine *QFontDatabase::findFont(int script, const QFontPrivate *d, const QFontDef &req) |
| { |
| const QFontCache::Key key(cleanedFontDef(req), script); |
| |
| if (!privateDb()->count) |
| initializeDb(); |
| |
| QFontEngine *fe = QFontCache::instance()->findEngine(key); |
| if (!fe) { |
| // Making sure that fe->fontDef.family will be an existing font. |
| initializeDb(); |
| QFontDatabasePrivate *db = privateDb(); |
| QtFontDesc desc; |
| QList<int> blacklistedFamilies; |
| match(script, key.def, key.def.family, QString(), -1, &desc, blacklistedFamilies); |
| if (!desc.family) // falling back to application font |
| desc.family = db->family(QApplication::font().defaultFamily()); |
| Q_ASSERT(desc.family); |
| |
| // Making sure that desc.family supports the requested script |
| QtFontDesc mappedDesc; |
| bool supportsScript = false; |
| do { |
| match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies); |
| if (mappedDesc.family == desc.family) { |
| supportsScript = true; |
| break; |
| } |
| blacklistedFamilies.append(mappedDesc.familyIndex); |
| } while (mappedDesc.family); |
| if (!supportsScript) { |
| blacklistedFamilies.clear(); |
| match(script, req, QString(), QString(), -1, &mappedDesc, blacklistedFamilies); |
| if (mappedDesc.family) |
| desc = mappedDesc; |
| } |
| |
| const QString fontFamily = desc.family->name; |
| QFontDef request = req; |
| request.family = fontFamily; |
| #ifdef QT_NO_FREETYPE |
| const QSymbianFontDatabaseExtrasImplementation *dbExtras = |
| static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras); |
| const QSymbianTypeFaceExtras *typeFaceExtras = |
| dbExtras->extras(fontFamily, request.weight > QFont::Normal, request.style != QFont::StyleNormal); |
| |
| // We need a valid pixelSize, e.g. for lineThickness() |
| if (request.pixelSize < 0) |
| request.pixelSize = request.pointSize * d->dpi / 72; |
| |
| fe = new QFontEngineS60(request, typeFaceExtras); |
| #else // QT_NO_FREETYPE |
| Q_UNUSED(d) |
| QFontEngine::FaceId faceId; |
| const QtFontFamily * const reqQtFontFamily = db->family(fontFamily); |
| faceId.filename = reqQtFontFamily->fontFilename; |
| faceId.index = reqQtFontFamily->fontFileIndex; |
| |
| QFontEngineFTS60 *fte = new QFontEngineFTS60(cleanedFontDef(request)); |
| if (fte->init(faceId, true, QFontEngineFT::Format_A8)) |
| fe = fte; |
| else |
| delete fte; |
| #endif // QT_NO_FREETYPE |
| |
| Q_ASSERT(fe); |
| if (script == QUnicodeTables::Common |
| && !(req.styleStrategy & QFont::NoFontMerging) |
| && !fe->symbol) { |
| |
| QStringList commonFonts; |
| for (int ws = 1; ws < QFontDatabase::WritingSystemsCount; ++ws) { |
| if (scriptForWritingSystem[ws] != script) |
| continue; |
| for (int i = 0; i < db->count; ++i) { |
| if (db->families[i]->writingSystems[ws] & QtFontFamily::Supported) |
| commonFonts.append(db->families[i]->name); |
| } |
| } |
| |
| // Hack: Prioritize .ccc fonts |
| const QString niceEastAsianFont(QLatin1String("Sans MT 936_S60")); |
| if (commonFonts.removeAll(niceEastAsianFont) > 0) |
| commonFonts.prepend(niceEastAsianFont); |
| |
| fe = new QFontEngineMultiS60(fe, script, commonFonts); |
| } |
| } |
| fe->ref.ref(); |
| QFontCache::instance()->insertEngine(key, fe); |
| return fe; |
| } |
| |
| void QFontDatabase::load(const QFontPrivate *d, int script) |
| { |
| QFontEngine *fe = 0; |
| QFontDef req = d->request; |
| |
| if (!d->engineData) { |
| const QFontCache::Key key(cleanedFontDef(req), script); |
| getEngineData(d, key); |
| } |
| |
| // the cached engineData could have already loaded the engine we want |
| if (d->engineData->engines[script]) |
| fe = d->engineData->engines[script]; |
| |
| if (!fe) { |
| if (qt_enable_test_font && req.family == QLatin1String("__Qt__Box__Engine__")) { |
| fe = new QTestFontEngine(req.pixelSize); |
| fe->fontDef = req; |
| } else { |
| fe = findFont(script, d, req); |
| } |
| d->engineData->engines[script] = fe; |
| } |
| } |
| |
| QT_END_NAMESPACE |