Implement application font loading for Windows

For FreeType Windows font database, use the application font loading
implementation of the parent class.
For native Windows font database, the implementation was adapted from
Qt 4.8.

Also fixed a bug in font database population, where old data was not
destroyed before population, resulting in fonts not getting properly
repopulated when a new font was added.

Task-number: QTBUG-24193
Task-number: QTBUG-24195
Task-number: QTBUG-24196
Change-Id: Ie224a87baf7500b9867aaafa807f62dd4503dee8
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@nokia.com>
This commit is contained in:
Miikka Heikkinen 2012-03-23 14:17:23 +02:00 committed by Qt by Nokia
parent d41e0564f5
commit 4f1820e3a7
4 changed files with 177 additions and 28 deletions

View File

@ -51,6 +51,7 @@
#include <QtCore/qmath.h> #include <QtCore/qmath.h>
#include <QtCore/QDebug> #include <QtCore/QDebug>
#include <QtCore/QtEndian>
#include <wchar.h> #include <wchar.h>
@ -618,14 +619,14 @@ static int CALLBACK storeFont(ENUMLOGFONTEX* f, NEWTEXTMETRICEX *textmetric,
void QWindowsFontDatabase::populateFontDatabase() void QWindowsFontDatabase::populateFontDatabase()
{ {
if (m_families.isEmpty()) { m_families.clear();
QPlatformFontDatabase::populateFontDatabase(); removeApplicationFonts();
populate(); // Called multiple times. QPlatformFontDatabase::populateFontDatabase();
// Work around EnumFontFamiliesEx() not listing the system font, see below. populate(); // Called multiple times.
const QString sysFontFamily = QGuiApplication::font().family(); // Work around EnumFontFamiliesEx() not listing the system font, see below.
if (!m_families.contains(sysFontFamily)) const QString sysFontFamily = QGuiApplication::font().family();
populate(sysFontFamily); if (!m_families.contains(sysFontFamily))
} populate(sysFontFamily);
} }
/*! /*!
@ -669,6 +670,7 @@ QWindowsFontDatabase::QWindowsFontDatabase() :
QWindowsFontDatabase::~QWindowsFontDatabase() QWindowsFontDatabase::~QWindowsFontDatabase()
{ {
removeApplicationFonts();
} }
QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef,
@ -702,11 +704,160 @@ QStringList QWindowsFontDatabase::fallbacksForFamily(const QString family, const
return result; return result;
} }
static QList<quint32> getTrueTypeFontOffsets(const uchar *fontData)
{
QList<quint32> offsets;
const quint32 headerTag = *reinterpret_cast<const quint32 *>(fontData);
if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) {
if (headerTag != MAKE_TAG(0, 1, 0, 0)
&& headerTag != MAKE_TAG('O', 'T', 'T', 'O')
&& headerTag != MAKE_TAG('t', 'r', 'u', 'e')
&& headerTag != MAKE_TAG('t', 'y', 'p', '1'))
return offsets;
offsets << 0;
return offsets;
}
const quint32 numFonts = qFromBigEndian<quint32>(fontData + 8);
for (uint i = 0; i < numFonts; ++i) {
offsets << qFromBigEndian<quint32>(fontData + 12 + i * 4);
}
return offsets;
}
static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag, const uchar **table, quint32 *length)
{
const quint16 numTables = qFromBigEndian<quint16>(data + 4);
for (uint i = 0; i < numTables; ++i) {
const quint32 offset = 12 + 16 * i;
if (*reinterpret_cast<const quint32 *>(data + offset) == tag) {
*table = fileBegin + qFromBigEndian<quint32>(data + offset + 8);
*length = qFromBigEndian<quint32>(data + offset + 12);
return;
}
}
*table = 0;
*length = 0;
return;
}
static void getFamiliesAndSignatures(const QByteArray &fontData,
QStringList *families,
QVector<FONTSIGNATURE> *signatures)
{
const uchar *data = reinterpret_cast<const uchar *>(fontData.constData());
QList<quint32> offsets = getTrueTypeFontOffsets(data);
if (offsets.isEmpty())
return;
for (int i = 0; i < offsets.count(); ++i) {
const uchar *font = data + offsets.at(i);
const uchar *table;
quint32 length;
getFontTable(data, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length);
if (!table)
continue;
QString name = getEnglishName(table, length);
if (name.isEmpty())
continue;
families->append(name);
if (signatures) {
FONTSIGNATURE signature;
getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length);
if (table && length >= 86) {
// Offsets taken from OS/2 table in the TrueType spec
signature.fsUsb[0] = qFromBigEndian<quint32>(table + 42);
signature.fsUsb[1] = qFromBigEndian<quint32>(table + 46);
signature.fsUsb[2] = qFromBigEndian<quint32>(table + 50);
signature.fsUsb[3] = qFromBigEndian<quint32>(table + 54);
signature.fsCsb[0] = qFromBigEndian<quint32>(table + 78);
signature.fsCsb[1] = qFromBigEndian<quint32>(table + 82);
} else {
memset(&signature, 0, sizeof(signature));
}
signatures->append(signature);
}
}
}
QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName)
{ {
const QStringList result = QPlatformFontDatabase::addApplicationFont(fontData, fileName); WinApplicationFont font;
Q_UNIMPLEMENTED(); font.fileName = fileName;
return result; QVector<FONTSIGNATURE> signatures;
QStringList families;
if (!fontData.isEmpty()) {
getFamiliesAndSignatures(fontData, &families, &signatures);
if (families.isEmpty())
return families;
DWORD dummy = 0;
font.handle = AddFontMemResourceEx((void *)fontData.constData(), fontData.size(), 0,
&dummy);
if (font.handle == 0)
return QStringList();
// Memory fonts won't show up in enumeration, so do add them the hard way.
for (int j = 0; j < families.count(); ++j) {
const QString familyName = families.at(j);
HDC hdc = GetDC(0);
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
memcpy(lf.lfFaceName, familyName.utf16(), sizeof(wchar_t) * qMin(LF_FACESIZE, familyName.size()));
lf.lfCharSet = DEFAULT_CHARSET;
HFONT hfont = CreateFontIndirect(&lf);
HGDIOBJ oldobj = SelectObject(hdc, hfont);
TEXTMETRIC textMetrics;
GetTextMetrics(hdc, &textMetrics);
addFontToDatabase(familyName, QString(), &textMetrics, &signatures.at(j),
TRUETYPE_FONTTYPE);
SelectObject(hdc, oldobj);
DeleteObject(hfont);
ReleaseDC(0, hdc);
}
} else {
QFile f(fileName);
if (!f.open(QIODevice::ReadOnly))
return families;
QByteArray data = f.readAll();
f.close();
getFamiliesAndSignatures(data, &families, 0);
if (families.isEmpty())
return families;
if (AddFontResourceExW((wchar_t*)fileName.utf16(), FR_PRIVATE, 0) == 0)
return QStringList();
font.handle = 0;
// Fonts based on files are added via populate, as they will show up in font enumeration.
for (int j = 0; j < families.count(); ++j)
populate(families.at(j));
}
m_applicationFonts << font;
return families;
}
void QWindowsFontDatabase::removeApplicationFonts()
{
foreach (const WinApplicationFont &font, m_applicationFonts) {
if (font.handle) {
RemoveFontMemResourceEx(font.handle);
} else {
RemoveFontResourceExW((LPCWSTR)font.fileName.utf16(), FR_PRIVATE, 0);
}
}
m_applicationFonts.clear();
} }
void QWindowsFontDatabase::releaseHandle(void *handle) void QWindowsFontDatabase::releaseHandle(void *handle)

View File

@ -100,8 +100,16 @@ public:
private: private:
void populate(const QString &family = QString()); void populate(const QString &family = QString());
void removeApplicationFonts();
QSharedPointer<QWindowsFontEngineData> m_fontEngineData; QSharedPointer<QWindowsFontEngineData> m_fontEngineData;
QSet<QString> m_families; QSet<QString> m_families;
struct WinApplicationFont {
HANDLE handle;
QString fileName;
};
QList<WinApplicationFont> m_applicationFonts;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -273,14 +273,13 @@ static int CALLBACK storeFont(ENUMLOGFONTEX* f, NEWTEXTMETRICEX *textmetric,
void QWindowsFontDatabaseFT::populateFontDatabase() void QWindowsFontDatabaseFT::populateFontDatabase()
{ {
if (m_families.isEmpty()) { m_families.clear();
QPlatformFontDatabase::populateFontDatabase(); QPlatformFontDatabase::populateFontDatabase();
populate(); // Called multiple times. populate(); // Called multiple times.
// Work around EnumFontFamiliesEx() not listing the system font, see below. // Work around EnumFontFamiliesEx() not listing the system font, see below.
const QString sysFontFamily = QGuiApplication::font().family(); const QString sysFontFamily = QGuiApplication::font().family();
if (!m_families.contains(sysFontFamily)) if (!m_families.contains(sysFontFamily))
populate(sysFontFamily); populate(sysFontFamily);
}
} }
/*! /*!
@ -426,14 +425,6 @@ QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString family, con
<< script << result << m_families; << script << result << m_families;
return result; return result;
} }
QStringList QWindowsFontDatabaseFT::addApplicationFont(const QByteArray &fontData, const QString &fileName)
{
const QStringList result = QPlatformFontDatabase::addApplicationFont(fontData, fileName);
Q_UNIMPLEMENTED();
return result;
}
QString QWindowsFontDatabaseFT::fontDir() const QString QWindowsFontDatabaseFT::fontDir() const
{ {
const QString result = QLatin1String(qgetenv("windir")) + QLatin1String("/Fonts");//QPlatformFontDatabase::fontDir(); const QString result = QLatin1String(qgetenv("windir")) + QLatin1String("/Fonts");//QPlatformFontDatabase::fontDir();

View File

@ -56,7 +56,6 @@ public:
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference); QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference);
QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const; QStringList fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const;
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName);
virtual QString fontDir() const; virtual QString fontDir() const;
virtual QFont defaultFont() const; virtual QFont defaultFont() const;