summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlessandro Portale <alessandro.portale@nokia.com>2011-01-19 12:16:13 (GMT)
committerAlessandro Portale <alessandro.portale@nokia.com>2011-01-19 12:27:14 (GMT)
commit770fb729929764a1f1c5fbd3d54714cf811c81e0 (patch)
treea7ac55a60b2d1763d99ae811faa088c92cf3423f
parent11815c63abeaf2b768e0cf59a83b53ff2b160b05 (diff)
downloadQt-770fb729929764a1f1c5fbd3d54714cf811c81e0.zip
Qt-770fb729929764a1f1c5fbd3d54714cf811c81e0.tar.gz
Qt-770fb729929764a1f1c5fbd3d54714cf811c81e0.tar.bz2
Supporting Qt application fonts on Symbian
This patch finally implements the missing application font support on Symbian. QFontDatabase's addApplicationFont[FromData], applicationFontFamilies and removeApplicationFont are now functional and allow an application to load, use and unload fonts at run-time. The underlying Symbian API comes with some restrictions/specialties. Most of them are worked around in this patch, the missing ones are left as part of QTBUG-16514: - The font file must be a file, not a memory buffer. Web fonts and qrc fonts come as memory buffers. These buffers are saved to a temporary .ttf file and the file is loaded by the underlying Symbian API. The temporary file can only be deleted after the font is unloaded. - The font file must be in a public location in order to be loadable by Symbian. It can for example not reside in the private application directory. Therefore, all application fonts (also those that are on the file system) become a temporary .ttf file in c:\temp\. - Symbian3/PR2 will come with a font table API which provides direct access to font tables. Symbian3/PR1 and below are missing this API, therefore, an own TFontStore is (ab)used to read font tables out of a font. This patch is considering both code paths in several occasions, making the Qt Symbian font implementation significantly less maintainable. - The fonts are loaded into Symbian's central font server. Loaded fonts from different processes can have colliding font typeface names (not file names) on that server. The server does not separate loaded fonts by their origin processes. Working around such collisions is part of QTBUG-16514. The number of fonts loadable at the same time by a Qt application is limited to the random value 5. Just to prevent abuse of the font server's memory. As usual, this patch was looked at by colleagues, and it was adjusted according to the feedback. But since the bus factor for the interaction of Qt's and Symbian's font systems is 1, I reviewed this patch, myself. Task-Number: QTBUG-6611 Autotest: tst_QFontDatabase::addAppFont
-rw-r--r--src/gui/kernel/qapplication_s60.cpp4
-rw-r--r--src/gui/text/qfontdatabase.cpp16
-rw-r--r--src/gui/text/qfontdatabase_s60.cpp229
-rw-r--r--tests/auto/qfontdatabase/tst_qfontdatabase.cpp4
4 files changed, 225 insertions, 28 deletions
diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp
index ba06312..40a7ec6 100644
--- a/src/gui/kernel/qapplication_s60.cpp
+++ b/src/gui/kernel/qapplication_s60.cpp
@@ -1616,7 +1616,7 @@ void qt_init(QApplicationPrivate * /* priv */, int)
qRegisterMetaType<WId>("WId");
}
-extern void qt_cleanup_symbianFontDatabaseExtras(); // qfontdatabase_s60.cpp
+extern void qt_cleanup_symbianFontDatabase(); // qfontdatabase_s60.cpp
/*****************************************************************************
qt_cleanup() - cleans up when the application is finished
@@ -1630,7 +1630,7 @@ void qt_cleanup()
QFontCache::cleanup(); // Has to happen now, since QFontEngineS60 has FBS handles
QPixmapCache::clear(); // Has to happen now, since QS60PixmapData has FBS handles
- qt_cleanup_symbianFontDatabaseExtras();
+ qt_cleanup_symbianFontDatabase();
// S60 structure and window server session are freed in eventdispatcher destructor as they are needed there
// It's important that this happens here, before the event dispatcher gets
diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp
index 5cecf08..637957d 100644
--- a/src/gui/text/qfontdatabase.cpp
+++ b/src/gui/text/qfontdatabase.cpp
@@ -624,6 +624,10 @@ public:
{ }
~QFontDatabasePrivate() {
free();
+#if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
+ if (symbianExtras)
+ delete symbianExtras;
+#endif
}
QtFontFamily *family(const QString &f, bool = false);
void free() {
@@ -632,12 +636,6 @@ public:
::free(families);
families = 0;
count = 0;
-#if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
- if (symbianExtras) {
- delete symbianExtras;
- symbianExtras = 0;
- }
-#endif
// don't clear the memory fonts!
}
@@ -653,6 +651,10 @@ public:
QVector<FONTSIGNATURE> signatures;
#elif defined(Q_WS_MAC)
ATSFontContainerRef handle;
+#elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
+ QString temporaryFileName;
+ TInt screenDeviceFontFileId;
+ TUid fontStoreFontFileUid;
#endif
QStringList families;
};
@@ -680,7 +682,7 @@ public:
QDataStream *stream;
QStringList fallbackFamilies;
#elif defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE)
- const QSymbianFontDatabaseExtras *symbianExtras;
+ QSymbianFontDatabaseExtras *symbianExtras;
#endif
};
diff --git a/src/gui/text/qfontdatabase_s60.cpp b/src/gui/text/qfontdatabase_s60.cpp
index 6ba035e..e508810 100644
--- a/src/gui/text/qfontdatabase_s60.cpp
+++ b/src/gui/text/qfontdatabase_s60.cpp
@@ -45,6 +45,7 @@
#include "qfontengine_s60_p.h"
#include "qabstractfileengine.h"
#include "qdesktopservices.h"
+#include "qtemporaryfile.h"
#include <private/qpixmap_s60_p.h>
#include <private/qt_s60_p.h>
#include "qendian.h"
@@ -114,7 +115,14 @@ public:
~QSymbianFontDatabaseExtrasImplementation();
const QSymbianTypeFaceExtras *extras(const QString &typeface, bool bold, bool italic) const;
- void addFontFileToFontStore(const QFileInfo &fontFileInfo);
+ 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)
@@ -146,6 +154,33 @@ public:
mutable QHash<QString, const QSymbianTypeFaceExtras *> m_extrasHash;
};
+const QString QSymbianFontDatabaseExtrasImplementation::appFontMarkerPrefix =
+ QLatin1String("qaf");
+
+inline QString QSymbianFontDatabaseExtrasImplementation::tempAppFontFolder()
+{
+ return QDir::toNativeSeparators(QDir::tempPath()) + QLatin1Char('\\');
+}
+
+QString QSymbianFontDatabaseExtrasImplementation::appFontMarker()
+{
+ static QString result;
+ if (result.isEmpty()) {
+ const quint32 uid = RProcess().Type().MostDerived().iUid;
+ quint16 crossSum = static_cast<quint16>(uid + (uid >> 16));
+ if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
+ // 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 add the pid to the uniqueness of the marker.
+ const quint32 pid = static_cast<quint32>(RProcess().Id().Id());
+ crossSum += static_cast<quint16>(pid + (pid >> 16));
+ }
+ result = appFontMarkerPrefix + QString::number(crossSum, 16);
+ }
+ return result;
+}
+
QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementation()
{
if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
@@ -170,10 +205,13 @@ QSymbianFontDatabaseExtrasImplementation::QSymbianFontDatabaseExtrasImplementati
}
}
-void qt_cleanup_symbianFontDatabaseExtras()
+void QSymbianFontDatabaseExtrasImplementation::clear()
{
+ QFontDatabasePrivate *db = privateDb();
+ if (!db)
+ return;
const QSymbianFontDatabaseExtrasImplementation *dbExtras =
- static_cast<const QSymbianFontDatabaseExtrasImplementation*>(privateDb()->symbianExtras);
+ static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
if (!dbExtras)
return; // initializeDb() has never been called
if (QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
@@ -189,9 +227,32 @@ void qt_cleanup_symbianFontDatabaseExtras()
dbExtras->m_extrasHash.clear();
}
+void qt_cleanup_symbianFontDatabase()
+{
+ 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_symbianFontDatabaseExtras();
+ qt_cleanup_symbianFontDatabase();
if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()) {
delete m_store;
m_heap->Close();
@@ -263,12 +324,40 @@ const QSymbianTypeFaceExtras *QSymbianFontDatabaseExtrasImplementation::extras(c
return m_extrasHash.value(searchKey);
}
-void QSymbianFontDatabaseExtrasImplementation::addFontFileToFontStore(const QFileInfo &fontFileInfo)
+void QSymbianFontDatabaseExtrasImplementation::removeAppFontData(
+ QFontDatabasePrivate::ApplicationFont *fnt)
+{
+ clear();
+ if (!QSymbianTypeFaceExtras::symbianFontTableApiAvailable()
+ && fnt->fontStoreFontFileUid.iUid != 0)
+ m_store->RemoveFile(fnt->fontStoreFontFileUid);
+ 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());
- TPtrC fontFilePtr(qt_QString2TPtrC(fontFile));
- QT_TRAP_THROWING(m_store->AddFileL(fontFilePtr));
+ const TPtrC fontFilePtr(qt_QString2TPtrC(fontFile));
+ TUid fontUid = {0};
+ TRAP_IGNORE(fontUid = m_store->AddFileL(fontFilePtr));
+ return fontUid;
}
#else // QT_NO_FREETYPE
@@ -331,9 +420,9 @@ void QFontEngineMultiS60::loadEngine(int at)
Q_ASSERT(engines[at]);
}
-static bool addFontToScreenDevice(int screenDeviceFontIndex,
- const QSymbianFontDatabaseExtrasImplementation *dbExtras)
-{
+static bool registerScreenDeviceFont(int screenDeviceFontIndex,
+ const QSymbianFontDatabaseExtrasImplementation *dbExtras)
+{
TTypefaceSupport typefaceSupport;
S60->screenDevice()->TypefaceSupport(typefaceSupport, screenDeviceFontIndex);
CFont *font; // We have to get a font instance in order to know all the details
@@ -398,7 +487,12 @@ static void initializeDb()
const QSymbianFontDatabaseExtrasImplementation *dbExtras =
static_cast<const QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
for (int i = 0; i < numTypeFaces; i++)
- addFontToScreenDevice(i, dbExtras);
+ 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();
@@ -423,18 +517,123 @@ static inline void load(const QString &family = QString(), int script = -1)
static void registerFont(QFontDatabasePrivate::ApplicationFont *fnt)
{
- Q_UNUSED(fnt);
+ if (QSymbianFontDatabaseExtrasImplementation::appFontLimitReached())
+ return;
+
+ QFontDatabasePrivate *db = privateDb();
+ if (!db)
+ return;
+
+ if (!db->count)
+ initializeDb();
+
+ if (fnt->data.isEmpty() && !fnt->fileName.endsWith(QLatin1String(".ttf"), Qt::CaseInsensitive))
+ return; // Only buffer or .ttf
+ QSymbianFontDatabaseExtrasImplementation *dbExtras =
+ static_cast<QSymbianFontDatabaseExtrasImplementation*>(db->symbianExtras);
+ if (!dbExtras)
+ return;
+
+ // 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()
+ + QSymbianFontDatabaseExtrasImplementation::appFontMarker()
+ + 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 (tempfile.write(fnt->data) == -1)
+ return;
+ tempfile.setAutoRemove(false);
+ tempfile.close(); // Tempfile still keeps a file handle, forbidding write access
+ 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));
+
+ fnt->families.append(fontsOnServerAfter.at(fontOnServerIndex));
+ if (!registerScreenDeviceFont(fontOnServerIndex, dbExtras))
+ dbExtras->removeAppFontData(fnt);
+ } else {
+ QFile::remove(fnt->temporaryFileName);
+ *fnt = QFontDatabasePrivate::ApplicationFont();
+ }
+ lock.relock();
}
bool QFontDatabase::removeApplicationFont(int handle)
{
- Q_UNUSED(handle);
- return false;
+ 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()
{
- return false;
+ 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()
diff --git a/tests/auto/qfontdatabase/tst_qfontdatabase.cpp b/tests/auto/qfontdatabase/tst_qfontdatabase.cpp
index ead000c..8b6f621 100644
--- a/tests/auto/qfontdatabase/tst_qfontdatabase.cpp
+++ b/tests/auto/qfontdatabase/tst_qfontdatabase.cpp
@@ -191,9 +191,6 @@ void tst_QFontDatabase::addAppFont_data()
void tst_QFontDatabase::addAppFont()
{
-#ifdef Q_OS_SYMBIAN
- QSKIP( "Symbian: Application fonts are not yet supported", SkipAll );
-#else
QFETCH(bool, useMemoryFont);
QSignalSpy fontDbChangedSpy(QApplication::instance(), SIGNAL(fontDatabaseChanged()));
@@ -243,7 +240,6 @@ void tst_QFontDatabase::addAppFont()
QCOMPARE(fontDbChangedSpy.count(), 2);
QVERIFY(db.families() == oldFamilies);
-#endif
}
QTEST_MAIN(tst_QFontDatabase)