Experimental DirectWrite font database

Adds an opt-in experimental DirectWrite-based font database.

This cannot be the 100% replacement for GDI unfortunately, since
quite a few font formats used on Windows are still unsupported.
But it would be good to have it as an opt-in experimental feature
since it should make it easier to solve multiple font selection
issues we have on Windows.

In order to still share the DirectWrite-specific code between
the old and new database, this introduces a common base class.

Note that the feature depends on DirectWrite 3 support (Windows 10).

Fixes: QTBUG-74917
Change-Id: Ida08ec7ef4fda9fc78622ca4297909a727390a64
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2019-04-05 11:48:29 +02:00
parent a13e8d6660
commit 35262678c3
12 changed files with 1656 additions and 827 deletions

View File

@ -189,6 +189,20 @@
"-ldwrite"
]
},
"dwrite_3": {
"label": "DirectWrite 3",
"test": {
"main": [
"IUnknown *factory = 0;",
"DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3),",
" &factory);"
]
},
"headers": "dwrite_3.h",
"sources": [
"-ldwrite"
]
},
"drm": {
"label": "KMS",
"test": {
@ -1145,6 +1159,12 @@
"condition": "libs.dwrite_1",
"output": [ "privateFeature" ]
},
"directwrite3": {
"label": "DirectWrite 3",
"emitIf": "config.win32",
"condition": "features.directwrite1 && libs.dwrite_3",
"output": [ "privateFeature" ]
},
"directwrite2": {
"label": "DirectWrite 2",
"emitIf": "config.win32",

View File

@ -613,6 +613,13 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME
\li \c {dpiawareness=[0|1|2} Sets the DPI awareness of the process
(see \l{High DPI Displays}, since Qt 5.4).
\li \c {fontengine=freetype}, uses the FreeType font engine.
\li \c {fontengine=directwrite}, uses the experimental DirectWrite
font database and defaults to using the DirectWrite font
engine (which is otherwise only used for some font types
or font properties.) This affects font selection and aims
to provide font naming more consistent with other platforms,
but does not support all font formats, such as Postscript
Type-1 or Microsoft FNT fonts.
\li \c {menus=[native|none]}, controls the use of native menus.
Native menus are implemented using Win32 API and are simpler than

View File

@ -0,0 +1,465 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwindowsdirectwritefontdatabase_p.h"
#include "qwindowsfontenginedirectwrite_p.h"
#include <QtCore/qstringbuilder.h>
#include <QtCore/qvarlengtharray.h>
#include <dwrite_3.h>
#include <d2d1.h>
QT_BEGIN_NAMESPACE
#ifdef QT_USE_DIRECTWRITE3
QWindowsDirectWriteFontDatabase::QWindowsDirectWriteFontDatabase()
{
qCDebug(lcQpaFonts) << "Creating DirectWrite database";
}
QWindowsDirectWriteFontDatabase::~QWindowsDirectWriteFontDatabase()
{
for (auto it = m_populatedFonts.begin(); it != m_populatedFonts.end(); ++it)
it.value()->Release();
}
QString QWindowsDirectWriteFontDatabase::localeString(IDWriteLocalizedStrings *names,
wchar_t localeName[])
{
uint index;
BOOL exists;
if (SUCCEEDED(names->FindLocaleName(localeName, &index, &exists)) && exists) {
uint length;
if (SUCCEEDED(names->GetStringLength(index, &length)) && length > 0) {
QVarLengthArray<wchar_t> buffer(int(length) + 1);
if (SUCCEEDED(names->GetString(index, buffer.data(), length + 1)))
return QString::fromWCharArray(buffer.data());
}
}
return QString();
}
static QFont::Stretch fromDirectWriteStretch(DWRITE_FONT_STRETCH stretch)
{
switch (stretch) {
case DWRITE_FONT_STRETCH_ULTRA_CONDENSED: return QFont::UltraCondensed;
case DWRITE_FONT_STRETCH_EXTRA_CONDENSED: return QFont::ExtraCondensed;
case DWRITE_FONT_STRETCH_CONDENSED: return QFont::Condensed;
case DWRITE_FONT_STRETCH_SEMI_CONDENSED: return QFont::SemiCondensed;
case DWRITE_FONT_STRETCH_NORMAL: return QFont::UltraCondensed;
case DWRITE_FONT_STRETCH_SEMI_EXPANDED: return QFont::SemiExpanded;
case DWRITE_FONT_STRETCH_EXPANDED: return QFont::Expanded;
case DWRITE_FONT_STRETCH_EXTRA_EXPANDED: return QFont::ExtraExpanded;
case DWRITE_FONT_STRETCH_ULTRA_EXPANDED: return QFont::UltraExpanded;
default: return QFont::AnyStretch;
}
}
static QFont::Weight fromDirectWriteWeight(DWRITE_FONT_WEIGHT weight)
{
return QPlatformFontDatabase::weightFromInteger(int(weight));
}
static DWRITE_FONT_STYLE toDirectWriteStyle(QFont::Style style)
{
switch (style) {
case QFont::StyleNormal: return DWRITE_FONT_STYLE_NORMAL;
case QFont::StyleOblique: return DWRITE_FONT_STYLE_OBLIQUE;
case QFont::StyleItalic: return DWRITE_FONT_STYLE_ITALIC;
default: return DWRITE_FONT_STYLE_NORMAL;
}
}
static QFont::Style fromDirectWriteStyle(DWRITE_FONT_STYLE style)
{
switch (style) {
case DWRITE_FONT_STYLE_NORMAL: return QFont::StyleNormal;
case DWRITE_FONT_STYLE_OBLIQUE: return QFont::StyleOblique;
case DWRITE_FONT_STYLE_ITALIC: return QFont::StyleItalic;
default: return QFont::StyleNormal;
}
}
void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
{
auto it = m_populatedFonts.find(familyName);
IDWriteFontFamily *fontFamily = it != m_populatedFonts.end() ? it.value() : nullptr;
if (fontFamily == nullptr) {
qCWarning(lcQpaFonts) << "Cannot find" << familyName << "in list of fonts";
return;
}
qCDebug(lcQpaFonts) << "Populate family:" << familyName;
wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
wchar_t englishLocale[] = L"en-us";
static const int SMOOTH_SCALABLE = 0xffff;
const QString foundryName; // No such concept.
const bool scalable = true;
const bool antialias = false;
const int size = SMOOTH_SCALABLE;
IDWriteFontList *matchingFonts;
if (SUCCEEDED(fontFamily->GetMatchingFonts(DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STRETCH_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
&matchingFonts))) {
for (uint j = 0; j < matchingFonts->GetFontCount(); ++j) {
IDWriteFont *font;
if (SUCCEEDED(matchingFonts->GetFont(j, &font))) {
IDWriteFont1 *font1 = nullptr;
if (!SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont1),
reinterpret_cast<void **>(&font1)))) {
qCWarning(lcQpaFonts) << "COM object does not support IDWriteFont1";
continue;
}
QString defaultLocaleFamilyName;
QString englishLocaleFamilyName;
IDWriteFontFamily *fontFamily2;
if (SUCCEEDED(font1->GetFontFamily(&fontFamily2))) {
IDWriteLocalizedStrings *names;
if (SUCCEEDED(fontFamily2->GetFamilyNames(&names))) {
defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
englishLocaleFamilyName = localeString(names, englishLocale);
names->Release();
}
fontFamily2->Release();
}
if (defaultLocaleFamilyName.isEmpty() && englishLocaleFamilyName.isEmpty())
englishLocaleFamilyName = familyName;
QVarLengthArray<DWRITE_UNICODE_RANGE, 64> ranges(QFontDatabase::WritingSystemsCount);
uint count = 0;
if (SUCCEEDED(font1->GetUnicodeRanges(QFontDatabase::WritingSystemsCount, ranges.data(), &count))) {
// ###
}
QSupportedWritingSystems writingSystems;
writingSystems.setSupported(QFontDatabase::Any);
writingSystems.setSupported(QFontDatabase::Latin);
{
IDWriteLocalizedStrings *names;
if (SUCCEEDED(font1->GetFaceNames(&names))) {
QString defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
QString englishLocaleStyleName = localeString(names, englishLocale);
QFont::Stretch stretch = fromDirectWriteStretch(font1->GetStretch());
QFont::Style style = fromDirectWriteStyle(font1->GetStyle());
QFont::Weight weight = fromDirectWriteWeight(font1->GetWeight());
bool fixed = font1->IsMonospacedFont();
qCDebug(lcQpaFonts) << "Family" << familyName << "has english variant" << englishLocaleStyleName << ", in default locale:" << defaultLocaleStyleName << stretch << style << weight << fixed;
IDWriteFontFace *face = nullptr;
if (SUCCEEDED(font->CreateFontFace(&face))) {
if (!englishLocaleStyleName.isEmpty() || defaultLocaleStyleName.isEmpty()) {
QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
face->AddRef();
}
if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) {
QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
face->AddRef();
}
face->Release();
}
names->Release();
}
}
font1->Release();
font->Release();
}
}
matchingFonts->Release();
}
}
QFontEngine *QWindowsDirectWriteFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
{
IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle);
Q_ASSERT(face != nullptr);
QWindowsFontEngineDirectWrite *fontEngine = new QWindowsFontEngineDirectWrite(face, fontDef.pixelSize, data());
fontEngine->initFontInfo(fontDef, defaultVerticalDPI());
return fontEngine;
}
QStringList QWindowsDirectWriteFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
{
Q_UNUSED(styleHint);
Q_UNUSED(script);
wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
wchar_t englishLocale[] = L"en-us";
QStringList ret;
auto it = m_populatedFonts.find(family);
IDWriteFontFamily *fontFamily = it != m_populatedFonts.end() ? it.value() : nullptr;
if (fontFamily != nullptr) {
IDWriteFontList *matchingFonts = nullptr;
if (SUCCEEDED(fontFamily->GetMatchingFonts(DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STRETCH_NORMAL,
toDirectWriteStyle(style),
&matchingFonts))) {
for (uint j = 0; j < matchingFonts->GetFontCount(); ++j) {
IDWriteFont *font = nullptr;
if (SUCCEEDED(matchingFonts->GetFont(j, &font))) {
IDWriteFontFamily *fontFamily2;
if (SUCCEEDED(font->GetFontFamily(&fontFamily2))) {
IDWriteLocalizedStrings *names;
if (SUCCEEDED(fontFamily2->GetFamilyNames(&names))) {
QString name = localeString(names, englishLocale);
if (name.isEmpty() && hasDefaultLocale)
name = localeString(names, defaultLocale);
if (!name.isEmpty() && m_populatedFonts.contains(name))
ret.append(name);
names->Release();
}
fontFamily2->Release();
}
font->Release();
}
}
matchingFonts->Release();
}
}
qDebug(lcQpaFonts) << "fallbacks for" << family << "is" << ret;
return ret;
}
QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName)
{
qCDebug(lcQpaFonts) << "Adding application font" << fileName;
QByteArray loadedData = fontData;
if (loadedData.isEmpty()) {
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
qCWarning(lcQpaFonts) << "Cannot open" << fileName << "for reading.";
return QStringList();
}
loadedData = file.readAll();
}
IDWriteFontFace *face = createDirectWriteFace(loadedData);
if (face == nullptr) {
qCWarning(lcQpaFonts) << "Failed to create DirectWrite face from font data. Font may be unsupported.";
return QStringList();
}
wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
wchar_t englishLocale[] = L"en-us";
static const int SMOOTH_SCALABLE = 0xffff;
const QString foundryName; // No such concept.
const bool scalable = true;
const bool antialias = false;
const int size = SMOOTH_SCALABLE;
QSupportedWritingSystems writingSystems;
writingSystems.setSupported(QFontDatabase::Any);
writingSystems.setSupported(QFontDatabase::Latin);
QStringList ret;
IDWriteFontFace3 *face3 = nullptr;
if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
reinterpret_cast<void **>(&face3)))) {
QString defaultLocaleFamilyName;
QString englishLocaleFamilyName;
IDWriteLocalizedStrings *names;
if (SUCCEEDED(face3->GetFamilyNames(&names))) {
defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
englishLocaleFamilyName = localeString(names, englishLocale);
names->Release();
}
QString defaultLocaleStyleName;
QString englishLocaleStyleName;
if (SUCCEEDED(face3->GetFaceNames(&names))) {
defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
englishLocaleStyleName = localeString(names, englishLocale);
names->Release();
}
QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch());
QFont::Style style = fromDirectWriteStyle(face3->GetStyle());
QFont::Weight weight = fromDirectWriteWeight(face3->GetWeight());
bool fixed = face3->IsMonospacedFont();
qCDebug(lcQpaFonts) << "\tFont names:" << englishLocaleFamilyName << ", " << defaultLocaleFamilyName
<< ", style names:" << englishLocaleStyleName << ", " << defaultLocaleStyleName
<< ", stretch:" << stretch
<< ", style:" << style
<< ", weight:" << weight
<< ", fixed:" << fixed;
if (!englishLocaleFamilyName.isEmpty()) {
ret.append(englishLocaleFamilyName);
QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
face->AddRef();
}
if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) {
ret.append(defaultLocaleFamilyName);
QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
face->AddRef();
}
face3->Release();
} else {
qCWarning(lcQpaFonts) << "Unable to query IDWriteFontFace3 interface from font face.";
}
face->Release();
return ret;
}
void QWindowsDirectWriteFontDatabase::releaseHandle(void *handle)
{
IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle);
face->Release();
}
bool QWindowsDirectWriteFontDatabase::fontsAlwaysScalable() const
{
return true;
}
bool QWindowsDirectWriteFontDatabase::isPrivateFontFamily(const QString &family) const
{
Q_UNUSED(family);
return false;
}
void QWindowsDirectWriteFontDatabase::populateFontDatabase()
{
wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
wchar_t englishLocale[] = L"en-us";
QString defaultFontName = defaultFont().family();
QString systemDefaultFontName = systemDefaultFont().family();
IDWriteFontCollection *fontCollection;
if (SUCCEEDED(data()->directWriteFactory->GetSystemFontCollection(&fontCollection))) {
for (uint i = 0; i < fontCollection->GetFontFamilyCount(); ++i) {
IDWriteFontFamily *fontFamily;
if (SUCCEEDED(fontCollection->GetFontFamily(i, &fontFamily))) {
QString defaultLocaleName;
QString englishLocaleName;
IDWriteLocalizedStrings *names;
if (SUCCEEDED(fontFamily->GetFamilyNames(&names))) {
if (hasDefaultLocale)
defaultLocaleName = localeString(names, defaultLocale);
englishLocaleName = localeString(names, englishLocale);
}
qCDebug(lcQpaFonts) << "Registering font, english name = " << englishLocaleName << ", name in current locale = " << defaultLocaleName;
if (!defaultLocaleName.isEmpty()) {
registerFontFamily(defaultLocaleName);
m_populatedFonts.insert(defaultLocaleName, fontFamily);
fontFamily->AddRef();
if (defaultLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) {
qDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << defaultLocaleName;
m_populatedFonts.insert(systemDefaultFontName, fontFamily);
fontFamily->AddRef();
}
}
if (!englishLocaleName.isEmpty() && englishLocaleName != defaultLocaleName) {
registerFontFamily(englishLocaleName);
m_populatedFonts.insert(englishLocaleName, fontFamily);
fontFamily->AddRef();
if (englishLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) {
qDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << englishLocaleName;
m_populatedFonts.insert(systemDefaultFontName, fontFamily);
fontFamily->AddRef();
}
}
fontFamily->Release();
}
}
}
}
QFont QWindowsDirectWriteFontDatabase::defaultFont() const
{
return QFont(QStringLiteral("Segoe UI"));
}
#endif // QT_USE_DIRECTWRITE3
QT_END_NAMESPACE

View File

@ -0,0 +1,96 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QWINDOWSDIRECTWRITEFONTDATABASE_P_H
#define QWINDOWSDIRECTWRITEFONTDATABASE_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "qwindowsfontdatabasebase_p.h"
#include <qpa/qplatformfontdatabase.h>
#include <QtCore/qloggingcategory.h>
struct IDWriteFactory;
struct IDWriteFont;
struct IDWriteFontFamily;
struct IDWriteLocalizedStrings;
QT_BEGIN_NAMESPACE
#ifdef QT_USE_DIRECTWRITE3
class QWindowsDirectWriteFontDatabase : public QWindowsFontDatabaseBase
{
Q_DISABLE_COPY_MOVE(QWindowsDirectWriteFontDatabase)
public:
QWindowsDirectWriteFontDatabase();
~QWindowsDirectWriteFontDatabase() override;
void populateFontDatabase() override;
void populateFamily(const QString &familyName) override;
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const override;
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName) override;
void releaseHandle(void *handle) override;
QFont defaultFont() const override;
bool fontsAlwaysScalable() const override;
bool isPrivateFontFamily(const QString &family) const override;
private:
static QString localeString(IDWriteLocalizedStrings *names, wchar_t localeName[]);
QHash<QString, IDWriteFontFamily *> m_populatedFonts;
};
#endif // QT_USE_DIRECTWRITE3
QT_END_NAMESPACE
#endif // QWINDOWSDIRECTWRITEFONTDATABASE_P_H

View File

@ -51,7 +51,6 @@
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QtEndian>
#include <QtCore/QThreadStorage>
#include <QtCore/private/qsystemlibrary_p.h>
#include <QtCore/private/qwinregistry_p.h>
@ -64,6 +63,7 @@
# include <dwrite.h>
# endif
# include <d2d1.h>
# include "qwindowsdirectwritefontdatabase_p.h"
#endif
QT_BEGIN_NAMESPACE
@ -75,40 +75,6 @@ Q_LOGGING_CATEGORY(lcQpaFonts, "qt.qpa.fonts")
typedef HRESULT (WINAPI *DWriteCreateFactoryType)(DWRITE_FACTORY_TYPE, const IID &, IUnknown **);
static inline DWriteCreateFactoryType resolveDWriteCreateFactory()
{
QSystemLibrary library(QStringLiteral("dwrite"));
QFunctionPointer result = library.resolve("DWriteCreateFactory");
if (Q_UNLIKELY(!result)) {
qWarning("Unable to load dwrite.dll");
return nullptr;
}
return reinterpret_cast<DWriteCreateFactoryType>(result);
}
static void createDirectWriteFactory(IDWriteFactory **factory)
{
*factory = nullptr;
static const DWriteCreateFactoryType dWriteCreateFactory = resolveDWriteCreateFactory();
if (!dWriteCreateFactory)
return;
IUnknown *result = NULL;
#if defined(QT_USE_DIRECTWRITE2)
dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result);
#endif
if (result == NULL) {
if (FAILED(dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &result))) {
qErrnoWarning("DWriteCreateFactory failed");
return;
}
}
*factory = static_cast<IDWriteFactory *>(result);
}
static inline bool useDirectWrite(QFont::HintingPreference hintingPreference,
const QString &familyName = QString(),
bool isColorFont = false)
@ -131,459 +97,6 @@ static inline bool useDirectWrite(QFont::HintingPreference hintingPreference,
}
#endif // !QT_NO_DIRECTWRITE
// Helper classes for creating font engines directly from font data
namespace {
# pragma pack(1)
// Common structure for all formats of the "name" table
struct NameTable
{
quint16 format;
quint16 count;
quint16 stringOffset;
};
struct NameRecord
{
quint16 platformID;
quint16 encodingID;
quint16 languageID;
quint16 nameID;
quint16 length;
quint16 offset;
};
struct OffsetSubTable
{
quint32 scalerType;
quint16 numTables;
quint16 searchRange;
quint16 entrySelector;
quint16 rangeShift;
};
struct TableDirectory
{
quint32 identifier;
quint32 checkSum;
quint32 offset;
quint32 length;
};
struct OS2Table
{
quint16 version;
qint16 avgCharWidth;
quint16 weightClass;
quint16 widthClass;
quint16 type;
qint16 subscriptXSize;
qint16 subscriptYSize;
qint16 subscriptXOffset;
qint16 subscriptYOffset;
qint16 superscriptXSize;
qint16 superscriptYSize;
qint16 superscriptXOffset;
qint16 superscriptYOffset;
qint16 strikeOutSize;
qint16 strikeOutPosition;
qint16 familyClass;
quint8 panose[10];
quint32 unicodeRanges[4];
quint8 vendorID[4];
quint16 selection;
quint16 firstCharIndex;
quint16 lastCharIndex;
qint16 typoAscender;
qint16 typoDescender;
qint16 typoLineGap;
quint16 winAscent;
quint16 winDescent;
quint32 codepageRanges[2];
qint16 height;
qint16 capHeight;
quint16 defaultChar;
quint16 breakChar;
quint16 maxContext;
};
# pragma pack()
class EmbeddedFont
{
public:
EmbeddedFont(const QByteArray &fontData) : m_fontData(fontData) {}
QString changeFamilyName(const QString &newFamilyName);
QByteArray data() const { return m_fontData; }
TableDirectory *tableDirectoryEntry(const QByteArray &tagName);
QString familyName(TableDirectory *nameTableDirectory = 0);
private:
QByteArray m_fontData;
};
TableDirectory *EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName)
{
Q_ASSERT(tagName.size() == 4);
quint32 tagId = *(reinterpret_cast<const quint32 *>(tagName.constData()));
const size_t fontDataSize = m_fontData.size();
if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable)))
return 0;
OffsetSubTable *offsetSubTable = reinterpret_cast<OffsetSubTable *>(m_fontData.data());
TableDirectory *tableDirectory = reinterpret_cast<TableDirectory *>(offsetSubTable + 1);
const size_t tableCount = qFromBigEndian<quint16>(offsetSubTable->numTables);
if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable) + sizeof(TableDirectory) * tableCount))
return 0;
TableDirectory *tableDirectoryEnd = tableDirectory + tableCount;
for (TableDirectory *entry = tableDirectory; entry < tableDirectoryEnd; ++entry) {
if (entry->identifier == tagId)
return entry;
}
return 0;
}
QString EmbeddedFont::familyName(TableDirectory *nameTableDirectoryEntry)
{
QString name;
if (nameTableDirectoryEntry == 0)
nameTableDirectoryEntry = tableDirectoryEntry("name");
if (nameTableDirectoryEntry != 0) {
quint32 offset = qFromBigEndian<quint32>(nameTableDirectoryEntry->offset);
if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameTable)))
return QString();
NameTable *nameTable = reinterpret_cast<NameTable *>(m_fontData.data() + offset);
NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1);
quint16 nameTableCount = qFromBigEndian<quint16>(nameTable->count);
if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameRecord) * nameTableCount))
return QString();
for (int i = 0; i < nameTableCount; ++i, ++nameRecord) {
if (qFromBigEndian<quint16>(nameRecord->nameID) == 1
&& qFromBigEndian<quint16>(nameRecord->platformID) == 3 // Windows
&& qFromBigEndian<quint16>(nameRecord->languageID) == 0x0409) { // US English
quint16 stringOffset = qFromBigEndian<quint16>(nameTable->stringOffset);
quint16 nameOffset = qFromBigEndian<quint16>(nameRecord->offset);
quint16 nameLength = qFromBigEndian<quint16>(nameRecord->length);
if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + stringOffset + nameOffset + nameLength))
return QString();
const void *ptr = reinterpret_cast<const quint8 *>(nameTable)
+ stringOffset
+ nameOffset;
const quint16 *s = reinterpret_cast<const quint16 *>(ptr);
const quint16 *e = s + nameLength / sizeof(quint16);
while (s != e)
name += QChar( qFromBigEndian<quint16>(*s++));
break;
}
}
}
return name;
}
QString EmbeddedFont::changeFamilyName(const QString &newFamilyName)
{
TableDirectory *nameTableDirectoryEntry = tableDirectoryEntry("name");
if (nameTableDirectoryEntry == 0)
return QString();
QString oldFamilyName = familyName(nameTableDirectoryEntry);
// Reserve size for name table header, five required name records and string
const int requiredRecordCount = 5;
quint16 nameIds[requiredRecordCount] = { 1, 2, 3, 4, 6 };
int sizeOfHeader = sizeof(NameTable) + sizeof(NameRecord) * requiredRecordCount;
int newFamilyNameSize = newFamilyName.size() * int(sizeof(quint16));
const QString regularString = QString::fromLatin1("Regular");
int regularStringSize = regularString.size() * int(sizeof(quint16));
// Align table size of table to 32 bits (pad with 0)
int fullSize = ((sizeOfHeader + newFamilyNameSize + regularStringSize) & ~3) + 4;
QByteArray newNameTable(fullSize, char(0));
{
NameTable *nameTable = reinterpret_cast<NameTable *>(newNameTable.data());
nameTable->count = qbswap<quint16>(requiredRecordCount);
nameTable->stringOffset = qbswap<quint16>(sizeOfHeader);
NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1);
for (int i = 0; i < requiredRecordCount; ++i, nameRecord++) {
nameRecord->nameID = qbswap<quint16>(nameIds[i]);
nameRecord->encodingID = qbswap<quint16>(1);
nameRecord->languageID = qbswap<quint16>(0x0409);
nameRecord->platformID = qbswap<quint16>(3);
nameRecord->length = qbswap<quint16>(newFamilyNameSize);
// Special case for sub-family
if (nameIds[i] == 4) {
nameRecord->offset = qbswap<quint16>(newFamilyNameSize);
nameRecord->length = qbswap<quint16>(regularStringSize);
}
}
// nameRecord now points to string data
quint16 *stringStorage = reinterpret_cast<quint16 *>(nameRecord);
const quint16 *sourceString = newFamilyName.utf16();
for (int i = 0; i < newFamilyName.size(); ++i)
stringStorage[i] = qbswap<quint16>(sourceString[i]);
stringStorage += newFamilyName.size();
sourceString = regularString.utf16();
for (int i = 0; i < regularString.size(); ++i)
stringStorage[i] = qbswap<quint16>(sourceString[i]);
}
quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data());
quint32 *tableEnd = reinterpret_cast<quint32 *>(newNameTable.data() + fullSize);
quint32 checkSum = 0;
while (p < tableEnd)
checkSum += qFromBigEndian<quint32>(*(p++));
nameTableDirectoryEntry->checkSum = qbswap<quint32>(checkSum);
nameTableDirectoryEntry->offset = qbswap<quint32>(m_fontData.size());
nameTableDirectoryEntry->length = qbswap<quint32>(fullSize);
m_fontData.append(newNameTable);
return oldFamilyName;
}
#if !defined(QT_NO_DIRECTWRITE)
class DirectWriteFontFileStream: public IDWriteFontFileStream
{
Q_DISABLE_COPY(DirectWriteFontFileStream)
public:
DirectWriteFontFileStream(const QByteArray &fontData)
: m_fontData(fontData)
, m_referenceCount(0)
{
}
virtual ~DirectWriteFontFileStream()
{
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object);
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();
HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset,
UINT64 fragmentSize, OUT void **fragmentContext);
void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext);
HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize);
HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime);
private:
QByteArray m_fontData;
ULONG m_referenceCount;
};
HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::QueryInterface(REFIID iid, void **object)
{
if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) {
*object = this;
AddRef();
return S_OK;
} else {
*object = NULL;
return E_NOINTERFACE;
}
}
ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::AddRef()
{
return InterlockedIncrement(&m_referenceCount);
}
ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::Release()
{
ULONG newCount = InterlockedDecrement(&m_referenceCount);
if (newCount == 0)
delete this;
return newCount;
}
HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::ReadFileFragment(
const void **fragmentStart,
UINT64 fileOffset,
UINT64 fragmentSize,
OUT void **fragmentContext)
{
*fragmentContext = NULL;
if (fileOffset + fragmentSize <= quint64(m_fontData.size())) {
*fragmentStart = m_fontData.data() + fileOffset;
return S_OK;
} else {
*fragmentStart = NULL;
return E_FAIL;
}
}
void STDMETHODCALLTYPE DirectWriteFontFileStream::ReleaseFileFragment(void *)
{
}
HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetFileSize(UINT64 *fileSize)
{
*fileSize = m_fontData.size();
return S_OK;
}
HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
{
*lastWriteTime = 0;
return E_NOTIMPL;
}
class DirectWriteFontFileLoader: public IDWriteFontFileLoader
{
public:
DirectWriteFontFileLoader() : m_referenceCount(0) {}
virtual ~DirectWriteFontFileLoader()
{
}
inline void addKey(const void *key, const QByteArray &fontData)
{
Q_ASSERT(!m_fontDatas.contains(key));
m_fontDatas.insert(key, fontData);
}
inline void removeKey(const void *key)
{
m_fontDatas.remove(key);
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object);
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();
HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey,
UINT32 fontFileReferenceKeySize,
OUT IDWriteFontFileStream **fontFileStream);
private:
ULONG m_referenceCount;
QHash<const void *, QByteArray> m_fontDatas;
};
HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid,
void **object)
{
if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) {
*object = this;
AddRef();
return S_OK;
} else {
*object = NULL;
return E_NOINTERFACE;
}
}
ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::AddRef()
{
return InterlockedIncrement(&m_referenceCount);
}
ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::Release()
{
ULONG newCount = InterlockedDecrement(&m_referenceCount);
if (newCount == 0)
delete this;
return newCount;
}
HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::CreateStreamFromKey(
void const *fontFileReferenceKey,
UINT32 fontFileReferenceKeySize,
IDWriteFontFileStream **fontFileStream)
{
Q_UNUSED(fontFileReferenceKeySize);
if (fontFileReferenceKeySize != sizeof(const void *)) {
qWarning("%s: Wrong key size", __FUNCTION__);
return E_FAIL;
}
const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey);
*fontFileStream = NULL;
auto it = m_fontDatas.constFind(key);
if (it == m_fontDatas.constEnd())
return E_FAIL;
QByteArray fontData = it.value();
DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData);
stream->AddRef();
*fontFileStream = stream;
return S_OK;
}
class CustomFontFileLoader
{
public:
CustomFontFileLoader() : m_directWriteFontFileLoader(nullptr)
{
createDirectWriteFactory(&m_directWriteFactory);
if (m_directWriteFactory) {
m_directWriteFontFileLoader = new DirectWriteFontFileLoader();
m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader);
}
}
~CustomFontFileLoader()
{
if (m_directWriteFactory != 0 && m_directWriteFontFileLoader != 0)
m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader);
if (m_directWriteFactory != 0)
m_directWriteFactory->Release();
}
void addKey(const void *key, const QByteArray &fontData)
{
if (m_directWriteFontFileLoader != 0)
m_directWriteFontFileLoader->addKey(key, fontData);
}
void removeKey(const void *key)
{
if (m_directWriteFontFileLoader != 0)
m_directWriteFontFileLoader->removeKey(key);
}
IDWriteFontFileLoader *loader() const
{
return m_directWriteFontFileLoader;
}
private:
IDWriteFactory *m_directWriteFactory;
DirectWriteFontFileLoader *m_directWriteFontFileLoader;
};
#endif
} // Anonymous namespace
/*!
\struct QWindowsFontEngineData
\brief Static constant data shared by the font engines.
@ -620,18 +133,6 @@ unsigned QWindowsFontDatabase::fontOptions()
return m_fontOptions;
}
QWindowsFontEngineData::~QWindowsFontEngineData()
{
if (hdc)
DeleteDC(hdc);
#if !defined(QT_NO_DIRECTWRITE)
if (directWriteGdiInterop)
directWriteGdiInterop->Release();
if (directWriteFactory)
directWriteFactory->Release();
#endif
}
qreal QWindowsFontDatabase::fontSmoothingGamma()
{
int winSmooth;
@ -645,26 +146,6 @@ qreal QWindowsFontDatabase::fontSmoothingGamma()
return result;
}
#if !defined(QT_NO_DIRECTWRITE)
static inline bool initDirectWrite(QWindowsFontEngineData *d)
{
if (!d->directWriteFactory) {
createDirectWriteFactory(&d->directWriteFactory);
if (!d->directWriteFactory)
return false;
}
if (!d->directWriteGdiInterop) {
const HRESULT hr = d->directWriteFactory->GetGdiInterop(&d->directWriteGdiInterop);
if (FAILED(hr)) {
qErrnoWarning("%s: GetGdiInterop failed", __FUNCTION__);
return false;
}
}
return true;
}
#endif // !defined(QT_NO_DIRECTWRITE)
/*!
\class QWindowsFontDatabase
\brief Font database for Windows
@ -1239,20 +720,6 @@ void QWindowsFontDatabase::populateFontDatabase()
addDefaultEUDCFont();
}
typedef QSharedPointer<QWindowsFontEngineData> QWindowsFontEngineDataPtr;
typedef QThreadStorage<QWindowsFontEngineDataPtr> FontEngineThreadLocalData;
Q_GLOBAL_STATIC(FontEngineThreadLocalData, fontEngineThreadLocalData)
QSharedPointer<QWindowsFontEngineData> sharedFontData()
{
FontEngineThreadLocalData *data = fontEngineThreadLocalData();
if (!data->hasLocalData())
data->setLocalData(QSharedPointer<QWindowsFontEngineData>::create());
return data->localData();
}
QWindowsFontDatabase::QWindowsFontDatabase()
{
// Properties accessed by QWin32PrintEngine (Qt Print Support)
@ -1262,9 +729,9 @@ QWindowsFontDatabase::QWindowsFontDatabase()
Q_UNUSED(logFontMetaTypeId)
if (lcQpaFonts().isDebugEnabled()) {
const QWindowsFontEngineDataPtr data = sharedFontData();
QSharedPointer<QWindowsFontEngineData> d = data();
qCDebug(lcQpaFonts) << __FUNCTION__ << "Clear type: "
<< data->clearTypeEnabled << "gamma: " << data->fontSmoothingGamma;
<< d->clearTypeEnabled << "gamma: " << d->fontSmoothingGamma;
}
}
@ -1278,7 +745,7 @@ QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, void *ha
const QString faceName(static_cast<const QChar*>(handle));
QFontEngine *fe = QWindowsFontDatabase::createEngine(fontDef, faceName,
defaultVerticalDPI(),
sharedFontData());
data());
qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef << fe << handle;
return fe;
}
@ -1332,7 +799,7 @@ QT_WARNING_POP
fontEngine = QWindowsFontDatabase::createEngine(request, QString(),
defaultVerticalDPI(),
sharedFontData());
data());
if (fontEngine) {
if (request.family != fontEngine->fontDef.family) {
@ -1371,87 +838,17 @@ QT_WARNING_POP
RemoveFontMemResourceEx(fontHandle);
}
}
// Get style and weight info
if (fontEngine != nullptr)
font.updateFromOS2Table(fontEngine);
}
#if !defined(QT_NO_DIRECTWRITE)
else {
CustomFontFileLoader fontFileLoader;
fontFileLoader.addKey(this, fontData);
QSharedPointer<QWindowsFontEngineData> fontEngineData = sharedFontData();
if (!initDirectWrite(fontEngineData.data()))
return 0;
IDWriteFontFile *fontFile = 0;
void *key = this;
HRESULT hres = fontEngineData->directWriteFactory->CreateCustomFontFileReference(&key,
sizeof(void *),
fontFileLoader.loader(),
&fontFile);
if (FAILED(hres)) {
qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__);
return 0;
}
BOOL isSupportedFontType;
DWRITE_FONT_FILE_TYPE fontFileType;
DWRITE_FONT_FACE_TYPE fontFaceType;
UINT32 numberOfFaces;
fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces);
if (!isSupportedFontType) {
fontFile->Release();
return 0;
}
IDWriteFontFace *directWriteFontFace = 0;
hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType,
1,
&fontFile,
0,
DWRITE_FONT_SIMULATIONS_NONE,
&directWriteFontFace);
if (FAILED(hres)) {
qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__);
fontFile->Release();
return 0;
}
fontFile->Release();
fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace,
pixelSize,
fontEngineData);
// Get font family from font data
fontEngine->fontDef.family = font.familyName();
fontEngine->fontDef.hintingPreference = hintingPreference;
directWriteFontFace->Release();
fontEngine = QWindowsFontDatabaseBase::fontEngine(fontData, pixelSize, hintingPreference);
}
#endif
// Get style and weight info
if (fontEngine != 0) {
TableDirectory *os2TableEntry = font.tableDirectoryEntry("OS/2");
if (os2TableEntry != 0) {
const OS2Table *os2Table =
reinterpret_cast<const OS2Table *>(fontData.constData()
+ qFromBigEndian<quint32>(os2TableEntry->offset));
bool italic = qFromBigEndian<quint16>(os2Table->selection) & (1 << 0);
bool oblique = qFromBigEndian<quint16>(os2Table->selection) & (1 << 9);
if (italic)
fontEngine->fontDef.style = QFont::StyleItalic;
else if (oblique)
fontEngine->fontDef.style = QFont::StyleOblique;
else
fontEngine->fontDef.style = QFont::StyleNormal;
fontEngine->fontDef.weight = QPlatformFontDatabase::weightFromInteger(qFromBigEndian<quint16>(os2Table->weightClass));
}
}
qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDATA" << fontData << pixelSize << hintingPreference << fontEngine;
return fontEngine;
}
@ -1683,14 +1080,6 @@ void QWindowsFontDatabase::refUniqueFont(const QString &uniqueFont)
m_uniqueFontData[uniqueFont].refCount.ref();
}
// ### fixme Qt 6 (QTBUG-58610): See comment at QWindowsFontDatabase::systemDefaultFont()
HFONT QWindowsFontDatabase::systemFont()
{
static const auto stock_sysfont =
reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT));
return stock_sysfont;
}
// Creation functions
static const char *other_tryFonts[] = {
@ -1745,101 +1134,6 @@ static const char *kr_tryFonts[] = {
static const char **tryFonts = 0;
LOGFONT QWindowsFontDatabase::fontDefToLOGFONT(const QFontDef &request, const QString &faceName)
{
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
lf.lfHeight = -qRound(request.pixelSize);
lf.lfWidth = 0;
lf.lfEscapement = 0;
lf.lfOrientation = 0;
if (request.weight == 50)
lf.lfWeight = FW_DONTCARE;
else
lf.lfWeight = (request.weight*900)/99;
lf.lfItalic = request.style != QFont::StyleNormal;
lf.lfCharSet = DEFAULT_CHARSET;
int strat = OUT_DEFAULT_PRECIS;
if (request.styleStrategy & QFont::PreferBitmap) {
strat = OUT_RASTER_PRECIS;
} else if (request.styleStrategy & QFont::PreferDevice) {
strat = OUT_DEVICE_PRECIS;
} else if (request.styleStrategy & QFont::PreferOutline) {
strat = OUT_OUTLINE_PRECIS;
} else if (request.styleStrategy & QFont::ForceOutline) {
strat = OUT_TT_ONLY_PRECIS;
}
lf.lfOutPrecision = strat;
int qual = DEFAULT_QUALITY;
if (request.styleStrategy & QFont::PreferMatch)
qual = DRAFT_QUALITY;
else if (request.styleStrategy & QFont::PreferQuality)
qual = PROOF_QUALITY;
if (request.styleStrategy & QFont::PreferAntialias) {
qual = (request.styleStrategy & QFont::NoSubpixelAntialias) == 0
? CLEARTYPE_QUALITY : ANTIALIASED_QUALITY;
} else if (request.styleStrategy & QFont::NoAntialias) {
qual = NONANTIALIASED_QUALITY;
} else if ((request.styleStrategy & QFont::NoSubpixelAntialias) && sharedFontData()->clearTypeEnabled) {
qual = ANTIALIASED_QUALITY;
}
lf.lfQuality = qual;
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
int hint = FF_DONTCARE;
switch (request.styleHint) {
case QFont::Helvetica:
hint = FF_SWISS;
break;
case QFont::Times:
hint = FF_ROMAN;
break;
case QFont::Courier:
hint = FF_MODERN;
break;
case QFont::OldEnglish:
hint = FF_DECORATIVE;
break;
case QFont::System:
hint = FF_MODERN;
break;
default:
break;
}
lf.lfPitchAndFamily = DEFAULT_PITCH | hint;
QString fam = faceName;
if (fam.isEmpty())
fam = request.families.size() > 0 ? request.families.at(0) : request.family;
if (Q_UNLIKELY(fam.size() >= LF_FACESIZE)) {
qCritical("%s: Family name '%s' is too long.", __FUNCTION__, qPrintable(fam));
fam.truncate(LF_FACESIZE - 1);
}
if (fam.isEmpty())
fam = QStringLiteral("MS Sans Serif");
if (fam == QLatin1String("MS Sans Serif")
&& (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) {
fam = QStringLiteral("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale
}
if (fam == QLatin1String("Courier") && !(request.styleStrategy & QFont::PreferBitmap))
fam = QStringLiteral("Courier New");
memcpy(lf.lfFaceName, fam.utf16(), fam.size() * sizeof(wchar_t));
return lf;
}
QStringList QWindowsFontDatabase::extraTryFontsForFamily(const QString &family)
{
QStringList result;
@ -1947,7 +1241,7 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q
}
#if !defined(QT_NO_DIRECTWRITE)
if (initDirectWrite(data.data())) {
if (data->directWriteFactory != nullptr) {
const QString fam = QString::fromWCharArray(lf.lfFaceName);
const QString nameSubstitute = QWindowsFontEngineDirectWrite::fontNameSubstitute(fam);
if (nameSubstitute != fam) {
@ -2023,62 +1317,6 @@ QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const Q
return fe;
}
QFont QWindowsFontDatabase::systemDefaultFont()
{
#if QT_VERSION >= 0x060000
// Qt 6: Obtain default GUI font (typically "Segoe UI, 9pt", see QTBUG-58610)
NONCLIENTMETRICS ncm;
ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0);
const QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont);
#else
LOGFONT lf;
GetObject(QWindowsFontDatabase::systemFont(), sizeof(lf), &lf);
QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(lf);
// "MS Shell Dlg 2" is the correct system font >= Win2k
if (systemFont.family() == QLatin1String("MS Shell Dlg"))
systemFont.setFamily(QStringLiteral("MS Shell Dlg 2"));
// Qt 5 by (Qt 4) legacy uses GetStockObject(DEFAULT_GUI_FONT) to
// obtain the default GUI font (typically "MS Shell Dlg 2, 8pt"). This has been
// long deprecated; the message font of the NONCLIENTMETRICS structure obtained by
// SystemParametersInfo(SPI_GETNONCLIENTMETRICS) should be used instead (see
// QWindowsTheme::refreshFonts(), typically "Segoe UI, 9pt"), which is larger.
#endif // Qt 5
qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont;
return systemFont;
}
QFont QWindowsFontDatabase::LOGFONT_to_QFont(const LOGFONT& logFont, int verticalDPI_In)
{
if (verticalDPI_In <= 0)
verticalDPI_In = defaultVerticalDPI();
QFont qFont(QString::fromWCharArray(logFont.lfFaceName));
qFont.setItalic(logFont.lfItalic);
if (logFont.lfWeight != FW_DONTCARE)
qFont.setWeight(QPlatformFontDatabase::weightFromInteger(logFont.lfWeight));
const qreal logFontHeight = qAbs(logFont.lfHeight);
qFont.setPointSizeF(logFontHeight * 72.0 / qreal(verticalDPI_In));
qFont.setUnderline(logFont.lfUnderline);
qFont.setOverline(false);
qFont.setStrikeOut(logFont.lfStrikeOut);
return qFont;
}
int QWindowsFontDatabase::defaultVerticalDPI()
{
static int vDPI = -1;
if (vDPI == -1) {
if (HDC defaultDC = GetDC(0)) {
vDPI = GetDeviceCaps(defaultDC, LOGPIXELSY);
ReleaseDC(0, defaultDC);
} else {
// FIXME: Resolve now or return 96 and keep unresolved?
vDPI = 96;
}
}
return vDPI;
}
bool QWindowsFontDatabase::isPrivateFontFamily(const QString &family) const
{
return m_eudcFonts.contains(family) || QPlatformFontDatabase::isPrivateFontFamily(family);

View File

@ -51,39 +51,16 @@
// We mean it.
//
#include "qwindowsfontdatabasebase_p.h"
#include <qpa/qplatformfontdatabase.h>
#include <QtCore/QSharedPointer>
#include <QtCore/QLoggingCategory>
#include <QtCore/qt_windows.h>
#if !defined(QT_NO_DIRECTWRITE)
struct IDWriteFactory;
struct IDWriteGdiInterop;
#endif
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQpaFonts)
class QWindowsFontEngineData
{
Q_DISABLE_COPY_MOVE(QWindowsFontEngineData)
public:
QWindowsFontEngineData();
~QWindowsFontEngineData();
uint pow_gamma[256];
bool clearTypeEnabled = false;
qreal fontSmoothingGamma;
HDC hdc = 0;
#if !defined(QT_NO_DIRECTWRITE)
IDWriteFactory *directWriteFactory = nullptr;
IDWriteGdiInterop *directWriteGdiInterop = nullptr;
#endif
};
class QWindowsFontDatabase : public QPlatformFontDatabase
class QWindowsFontDatabase : public QWindowsFontDatabaseBase
{
Q_DISABLE_COPY_MOVE(QWindowsFontDatabase)
public:
@ -113,23 +90,15 @@ public:
void refUniqueFont(const QString &uniqueFont);
bool isPrivateFontFamily(const QString &family) const override;
static QFont systemDefaultFont();
static QFontEngine *createEngine(const QFontDef &request, const QString &faceName,
int dpi,
const QSharedPointer<QWindowsFontEngineData> &data);
static HFONT systemFont();
static QFont LOGFONT_to_QFont(const LOGFONT& lf, int verticalDPI = 0);
static qreal fontSmoothingGamma();
static LOGFONT fontDefToLOGFONT(const QFontDef &fontDef, const QString &faceName);
static QStringList extraTryFontsForFamily(const QString &family);
static QString familyForStyleHint(QFont::StyleHint styleHint);
static int defaultVerticalDPI();
static void setFontOptions(unsigned options);
static unsigned fontOptions();

View File

@ -0,0 +1,861 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwindowsfontdatabasebase_p.h"
#include "qwindowsfontdatabase_p.h"
#include <QtCore/private/qsystemlibrary_p.h>
#include <QtCore/QThreadStorage>
#include <QtCore/QtEndian>
#if !defined(QT_NO_DIRECTWRITE)
# if defined(QT_USE_DIRECTWRITE3)
# include <dwrite_3.h>
# elif defined(QT_USE_DIRECTWRITE2)
# include <dwrite_2.h>
# else
# include <dwrite.h>
# endif
# include <d2d1.h>
# include "qwindowsfontenginedirectwrite_p.h"
#endif
QT_BEGIN_NAMESPACE
// Helper classes for creating font engines directly from font data
namespace {
# pragma pack(1)
// Common structure for all formats of the "name" table
struct NameTable
{
quint16 format;
quint16 count;
quint16 stringOffset;
};
struct NameRecord
{
quint16 platformID;
quint16 encodingID;
quint16 languageID;
quint16 nameID;
quint16 length;
quint16 offset;
};
struct OffsetSubTable
{
quint32 scalerType;
quint16 numTables;
quint16 searchRange;
quint16 entrySelector;
quint16 rangeShift;
};
struct TableDirectory : public QWindowsFontDatabaseBase::FontTable
{
quint32 identifier;
quint32 checkSum;
quint32 offset;
quint32 length;
};
struct OS2Table
{
quint16 version;
qint16 avgCharWidth;
quint16 weightClass;
quint16 widthClass;
quint16 type;
qint16 subscriptXSize;
qint16 subscriptYSize;
qint16 subscriptXOffset;
qint16 subscriptYOffset;
qint16 superscriptXSize;
qint16 superscriptYSize;
qint16 superscriptXOffset;
qint16 superscriptYOffset;
qint16 strikeOutSize;
qint16 strikeOutPosition;
qint16 familyClass;
quint8 panose[10];
quint32 unicodeRanges[4];
quint8 vendorID[4];
quint16 selection;
quint16 firstCharIndex;
quint16 lastCharIndex;
qint16 typoAscender;
qint16 typoDescender;
qint16 typoLineGap;
quint16 winAscent;
quint16 winDescent;
quint32 codepageRanges[2];
qint16 height;
qint16 capHeight;
quint16 defaultChar;
quint16 breakChar;
quint16 maxContext;
};
# pragma pack()
} // Anonymous namespace
QWindowsFontDatabaseBase::FontTable *QWindowsFontDatabaseBase::EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName)
{
Q_ASSERT(tagName.size() == 4);
quint32 tagId = *(reinterpret_cast<const quint32 *>(tagName.constData()));
const size_t fontDataSize = m_fontData.size();
if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable)))
return nullptr;
OffsetSubTable *offsetSubTable = reinterpret_cast<OffsetSubTable *>(m_fontData.data());
TableDirectory *tableDirectory = reinterpret_cast<TableDirectory *>(offsetSubTable + 1);
const size_t tableCount = qFromBigEndian<quint16>(offsetSubTable->numTables);
if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable) + sizeof(TableDirectory) * tableCount))
return nullptr;
TableDirectory *tableDirectoryEnd = tableDirectory + tableCount;
for (TableDirectory *entry = tableDirectory; entry < tableDirectoryEnd; ++entry) {
if (entry->identifier == tagId)
return entry;
}
return nullptr;
}
QString QWindowsFontDatabaseBase::EmbeddedFont::familyName(QWindowsFontDatabaseBase::FontTable *directoryEntry)
{
QString name;
TableDirectory *nameTableDirectoryEntry = static_cast<TableDirectory *>(directoryEntry);
if (nameTableDirectoryEntry == nullptr)
nameTableDirectoryEntry = static_cast<TableDirectory *>(tableDirectoryEntry("name"));
if (nameTableDirectoryEntry != nullptr) {
quint32 offset = qFromBigEndian<quint32>(nameTableDirectoryEntry->offset);
if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameTable)))
return QString();
NameTable *nameTable = reinterpret_cast<NameTable *>(m_fontData.data() + offset);
NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1);
quint16 nameTableCount = qFromBigEndian<quint16>(nameTable->count);
if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameRecord) * nameTableCount))
return QString();
for (int i = 0; i < nameTableCount; ++i, ++nameRecord) {
if (qFromBigEndian<quint16>(nameRecord->nameID) == 1
&& qFromBigEndian<quint16>(nameRecord->platformID) == 3 // Windows
&& qFromBigEndian<quint16>(nameRecord->languageID) == 0x0409) { // US English
quint16 stringOffset = qFromBigEndian<quint16>(nameTable->stringOffset);
quint16 nameOffset = qFromBigEndian<quint16>(nameRecord->offset);
quint16 nameLength = qFromBigEndian<quint16>(nameRecord->length);
if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + stringOffset + nameOffset + nameLength))
return QString();
const void *ptr = reinterpret_cast<const quint8 *>(nameTable)
+ stringOffset
+ nameOffset;
const quint16 *s = reinterpret_cast<const quint16 *>(ptr);
const quint16 *e = s + nameLength / sizeof(quint16);
while (s != e)
name += QChar( qFromBigEndian<quint16>(*s++));
break;
}
}
}
return name;
}
void QWindowsFontDatabaseBase::EmbeddedFont::updateFromOS2Table(QFontEngine *fontEngine)
{
TableDirectory *os2TableEntry = static_cast<TableDirectory *>(tableDirectoryEntry("OS/2"));
if (os2TableEntry != nullptr) {
const OS2Table *os2Table =
reinterpret_cast<const OS2Table *>(m_fontData.constData()
+ qFromBigEndian<quint32>(os2TableEntry->offset));
bool italic = qFromBigEndian<quint16>(os2Table->selection) & (1 << 0);
bool oblique = qFromBigEndian<quint16>(os2Table->selection) & (1 << 9);
if (italic)
fontEngine->fontDef.style = QFont::StyleItalic;
else if (oblique)
fontEngine->fontDef.style = QFont::StyleOblique;
else
fontEngine->fontDef.style = QFont::StyleNormal;
fontEngine->fontDef.weight = QPlatformFontDatabase::weightFromInteger(qFromBigEndian<quint16>(os2Table->weightClass));
}
}
QString QWindowsFontDatabaseBase::EmbeddedFont::changeFamilyName(const QString &newFamilyName)
{
TableDirectory *nameTableDirectoryEntry = static_cast<TableDirectory *>(tableDirectoryEntry("name"));
if (nameTableDirectoryEntry == nullptr)
return QString();
QString oldFamilyName = familyName(nameTableDirectoryEntry);
// Reserve size for name table header, five required name records and string
const int requiredRecordCount = 5;
quint16 nameIds[requiredRecordCount] = { 1, 2, 3, 4, 6 };
int sizeOfHeader = sizeof(NameTable) + sizeof(NameRecord) * requiredRecordCount;
int newFamilyNameSize = newFamilyName.size() * int(sizeof(quint16));
const QString regularString = QString::fromLatin1("Regular");
int regularStringSize = regularString.size() * int(sizeof(quint16));
// Align table size of table to 32 bits (pad with 0)
int fullSize = ((sizeOfHeader + newFamilyNameSize + regularStringSize) & ~3) + 4;
QByteArray newNameTable(fullSize, char(0));
{
NameTable *nameTable = reinterpret_cast<NameTable *>(newNameTable.data());
nameTable->count = qbswap<quint16>(requiredRecordCount);
nameTable->stringOffset = qbswap<quint16>(sizeOfHeader);
NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1);
for (int i = 0; i < requiredRecordCount; ++i, nameRecord++) {
nameRecord->nameID = qbswap<quint16>(nameIds[i]);
nameRecord->encodingID = qbswap<quint16>(1);
nameRecord->languageID = qbswap<quint16>(0x0409);
nameRecord->platformID = qbswap<quint16>(3);
nameRecord->length = qbswap<quint16>(newFamilyNameSize);
// Special case for sub-family
if (nameIds[i] == 4) {
nameRecord->offset = qbswap<quint16>(newFamilyNameSize);
nameRecord->length = qbswap<quint16>(regularStringSize);
}
}
// nameRecord now points to string data
quint16 *stringStorage = reinterpret_cast<quint16 *>(nameRecord);
const quint16 *sourceString = newFamilyName.utf16();
for (int i = 0; i < newFamilyName.size(); ++i)
stringStorage[i] = qbswap<quint16>(sourceString[i]);
stringStorage += newFamilyName.size();
sourceString = regularString.utf16();
for (int i = 0; i < regularString.size(); ++i)
stringStorage[i] = qbswap<quint16>(sourceString[i]);
}
quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data());
quint32 *tableEnd = reinterpret_cast<quint32 *>(newNameTable.data() + fullSize);
quint32 checkSum = 0;
while (p < tableEnd)
checkSum += qFromBigEndian<quint32>(*(p++));
nameTableDirectoryEntry->checkSum = qbswap<quint32>(checkSum);
nameTableDirectoryEntry->offset = qbswap<quint32>(m_fontData.size());
nameTableDirectoryEntry->length = qbswap<quint32>(fullSize);
m_fontData.append(newNameTable);
return oldFamilyName;
}
#if !defined(QT_NO_DIRECTWRITE)
namespace {
class DirectWriteFontFileStream: public IDWriteFontFileStream
{
Q_DISABLE_COPY(DirectWriteFontFileStream)
public:
DirectWriteFontFileStream(const QByteArray &fontData)
: m_fontData(fontData)
, m_referenceCount(0)
{
}
virtual ~DirectWriteFontFileStream()
{
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object);
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();
HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset,
UINT64 fragmentSize, OUT void **fragmentContext);
void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext);
HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize);
HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime);
private:
QByteArray m_fontData;
ULONG m_referenceCount;
};
HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::QueryInterface(REFIID iid, void **object)
{
if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) {
*object = this;
AddRef();
return S_OK;
} else {
*object = NULL;
return E_NOINTERFACE;
}
}
ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::AddRef()
{
return InterlockedIncrement(&m_referenceCount);
}
ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::Release()
{
ULONG newCount = InterlockedDecrement(&m_referenceCount);
if (newCount == 0)
delete this;
return newCount;
}
HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::ReadFileFragment(
const void **fragmentStart,
UINT64 fileOffset,
UINT64 fragmentSize,
OUT void **fragmentContext)
{
*fragmentContext = NULL;
if (fileOffset + fragmentSize <= quint64(m_fontData.size())) {
*fragmentStart = m_fontData.data() + fileOffset;
return S_OK;
} else {
*fragmentStart = NULL;
return E_FAIL;
}
}
void STDMETHODCALLTYPE DirectWriteFontFileStream::ReleaseFileFragment(void *)
{
}
HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetFileSize(UINT64 *fileSize)
{
*fileSize = m_fontData.size();
return S_OK;
}
HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
{
*lastWriteTime = 0;
return E_NOTIMPL;
}
class DirectWriteFontFileLoader: public IDWriteFontFileLoader
{
public:
DirectWriteFontFileLoader() : m_referenceCount(0) {}
virtual ~DirectWriteFontFileLoader()
{
}
inline void addKey(const void *key, const QByteArray &fontData)
{
Q_ASSERT(!m_fontDatas.contains(key));
m_fontDatas.insert(key, fontData);
}
inline void removeKey(const void *key)
{
m_fontDatas.remove(key);
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object);
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();
HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey,
UINT32 fontFileReferenceKeySize,
OUT IDWriteFontFileStream **fontFileStream);
private:
ULONG m_referenceCount;
QHash<const void *, QByteArray> m_fontDatas;
};
HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid,
void **object)
{
if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) {
*object = this;
AddRef();
return S_OK;
} else {
*object = NULL;
return E_NOINTERFACE;
}
}
ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::AddRef()
{
return InterlockedIncrement(&m_referenceCount);
}
ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::Release()
{
ULONG newCount = InterlockedDecrement(&m_referenceCount);
if (newCount == 0)
delete this;
return newCount;
}
HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::CreateStreamFromKey(
void const *fontFileReferenceKey,
UINT32 fontFileReferenceKeySize,
IDWriteFontFileStream **fontFileStream)
{
Q_UNUSED(fontFileReferenceKeySize);
if (fontFileReferenceKeySize != sizeof(const void *)) {
qWarning("%s: Wrong key size", __FUNCTION__);
return E_FAIL;
}
const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey);
*fontFileStream = NULL;
auto it = m_fontDatas.constFind(key);
if (it == m_fontDatas.constEnd())
return E_FAIL;
QByteArray fontData = it.value();
DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData);
stream->AddRef();
*fontFileStream = stream;
return S_OK;
}
class CustomFontFileLoader
{
public:
CustomFontFileLoader(IDWriteFactory *factory)
{
m_directWriteFactory = factory;
if (m_directWriteFactory) {
m_directWriteFactory->AddRef();
m_directWriteFontFileLoader = new DirectWriteFontFileLoader();
m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader);
}
}
~CustomFontFileLoader()
{
if (m_directWriteFactory != nullptr && m_directWriteFontFileLoader != nullptr)
m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader);
if (m_directWriteFactory != nullptr)
m_directWriteFactory->Release();
}
void addKey(const void *key, const QByteArray &fontData)
{
if (m_directWriteFontFileLoader != nullptr)
m_directWriteFontFileLoader->addKey(key, fontData);
}
void removeKey(const void *key)
{
if (m_directWriteFontFileLoader != nullptr)
m_directWriteFontFileLoader->removeKey(key);
}
IDWriteFontFileLoader *loader() const
{
return m_directWriteFontFileLoader;
}
private:
IDWriteFactory *m_directWriteFactory = nullptr;
DirectWriteFontFileLoader *m_directWriteFontFileLoader = nullptr;
};
} // Anonymous namespace
#endif // !defined(QT_NO_DIRECTWRITE)
QWindowsFontEngineData::~QWindowsFontEngineData()
{
if (hdc)
DeleteDC(hdc);
#if !defined(QT_NO_DIRECTWRITE)
if (directWriteGdiInterop)
directWriteGdiInterop->Release();
if (directWriteFactory)
directWriteFactory->Release();
#endif
}
QWindowsFontDatabaseBase::QWindowsFontDatabaseBase()
{
}
QWindowsFontDatabaseBase::~QWindowsFontDatabaseBase()
{
}
typedef QSharedPointer<QWindowsFontEngineData> QWindowsFontEngineDataPtr;
typedef QThreadStorage<QWindowsFontEngineDataPtr> FontEngineThreadLocalData;
Q_GLOBAL_STATIC(FontEngineThreadLocalData, fontEngineThreadLocalData)
QSharedPointer<QWindowsFontEngineData> QWindowsFontDatabaseBase::data()
{
FontEngineThreadLocalData *data = fontEngineThreadLocalData();
if (!data->hasLocalData())
data->setLocalData(QSharedPointer<QWindowsFontEngineData>::create());
if (!init(data->localData()))
qCWarning(lcQpaFonts) << "Cannot initialize common font database data";
return data->localData();
}
bool QWindowsFontDatabaseBase::init(QSharedPointer<QWindowsFontEngineData> d)
{
if (!d->directWriteFactory) {
createDirectWriteFactory(&d->directWriteFactory);
if (!d->directWriteFactory)
return false;
}
if (!d->directWriteGdiInterop) {
const HRESULT hr = d->directWriteFactory->GetGdiInterop(&d->directWriteGdiInterop);
if (FAILED(hr)) {
qErrnoWarning("%s: GetGdiInterop failed", __FUNCTION__);
return false;
}
}
return true;
}
// ### Qt 6: Link directly to dwrite instead
typedef HRESULT (WINAPI *DWriteCreateFactoryType)(DWRITE_FACTORY_TYPE, const IID &, IUnknown **);
static inline DWriteCreateFactoryType resolveDWriteCreateFactory()
{
QSystemLibrary library(QStringLiteral("dwrite"));
QFunctionPointer result = library.resolve("DWriteCreateFactory");
if (Q_UNLIKELY(!result)) {
qWarning("Unable to load dwrite.dll");
return nullptr;
}
return reinterpret_cast<DWriteCreateFactoryType>(result);
}
void QWindowsFontDatabaseBase::createDirectWriteFactory(IDWriteFactory **factory)
{
*factory = nullptr;
static const DWriteCreateFactoryType dWriteCreateFactory = resolveDWriteCreateFactory();
if (!dWriteCreateFactory)
return;
IUnknown *result = nullptr;
#if defined(QT_USE_DIRECTWRITE3)
dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result);
#endif
#if defined(QT_USE_DIRECTWRITE2)
if (result == nullptr)
dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result);
#endif
if (result == nullptr) {
if (FAILED(dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &result))) {
qErrnoWarning("DWriteCreateFactory failed");
return;
}
}
*factory = static_cast<IDWriteFactory *>(result);
}
int QWindowsFontDatabaseBase::defaultVerticalDPI()
{
static int vDPI = -1;
if (vDPI == -1) {
if (HDC defaultDC = GetDC(0)) {
vDPI = GetDeviceCaps(defaultDC, LOGPIXELSY);
ReleaseDC(0, defaultDC);
} else {
// FIXME: Resolve now or return 96 and keep unresolved?
vDPI = 96;
}
}
return vDPI;
}
LOGFONT QWindowsFontDatabaseBase::fontDefToLOGFONT(const QFontDef &request, const QString &faceName)
{
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
lf.lfHeight = -qRound(request.pixelSize);
lf.lfWidth = 0;
lf.lfEscapement = 0;
lf.lfOrientation = 0;
if (request.weight == 50)
lf.lfWeight = FW_DONTCARE;
else
lf.lfWeight = (request.weight*900)/99;
lf.lfItalic = request.style != QFont::StyleNormal;
lf.lfCharSet = DEFAULT_CHARSET;
int strat = OUT_DEFAULT_PRECIS;
if (request.styleStrategy & QFont::PreferBitmap) {
strat = OUT_RASTER_PRECIS;
} else if (request.styleStrategy & QFont::PreferDevice) {
strat = OUT_DEVICE_PRECIS;
} else if (request.styleStrategy & QFont::PreferOutline) {
strat = OUT_OUTLINE_PRECIS;
} else if (request.styleStrategy & QFont::ForceOutline) {
strat = OUT_TT_ONLY_PRECIS;
}
lf.lfOutPrecision = strat;
int qual = DEFAULT_QUALITY;
if (request.styleStrategy & QFont::PreferMatch)
qual = DRAFT_QUALITY;
else if (request.styleStrategy & QFont::PreferQuality)
qual = PROOF_QUALITY;
if (request.styleStrategy & QFont::PreferAntialias) {
qual = (request.styleStrategy & QFont::NoSubpixelAntialias) == 0
? CLEARTYPE_QUALITY : ANTIALIASED_QUALITY;
} else if (request.styleStrategy & QFont::NoAntialias) {
qual = NONANTIALIASED_QUALITY;
} else if ((request.styleStrategy & QFont::NoSubpixelAntialias) && data()->clearTypeEnabled) {
qual = ANTIALIASED_QUALITY;
}
lf.lfQuality = qual;
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
int hint = FF_DONTCARE;
switch (request.styleHint) {
case QFont::Helvetica:
hint = FF_SWISS;
break;
case QFont::Times:
hint = FF_ROMAN;
break;
case QFont::Courier:
hint = FF_MODERN;
break;
case QFont::OldEnglish:
hint = FF_DECORATIVE;
break;
case QFont::System:
hint = FF_MODERN;
break;
default:
break;
}
lf.lfPitchAndFamily = DEFAULT_PITCH | hint;
QString fam = faceName;
if (fam.isEmpty())
fam = request.families.size() > 0 ? request.families.at(0) : request.family;
if (Q_UNLIKELY(fam.size() >= LF_FACESIZE)) {
qCritical("%s: Family name '%s' is too long.", __FUNCTION__, qPrintable(fam));
fam.truncate(LF_FACESIZE - 1);
}
if (fam.isEmpty())
fam = QStringLiteral("MS Sans Serif");
if (fam == QLatin1String("MS Sans Serif")
&& (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) {
fam = QStringLiteral("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale
}
if (fam == QLatin1String("Courier") && !(request.styleStrategy & QFont::PreferBitmap))
fam = QStringLiteral("Courier New");
memcpy(lf.lfFaceName, fam.utf16(), fam.size() * sizeof(wchar_t));
return lf;
}
QFont QWindowsFontDatabaseBase::LOGFONT_to_QFont(const LOGFONT& logFont, int verticalDPI_In)
{
if (verticalDPI_In <= 0)
verticalDPI_In = defaultVerticalDPI();
QFont qFont(QString::fromWCharArray(logFont.lfFaceName));
qFont.setItalic(logFont.lfItalic);
if (logFont.lfWeight != FW_DONTCARE)
qFont.setWeight(QPlatformFontDatabase::weightFromInteger(logFont.lfWeight));
const qreal logFontHeight = qAbs(logFont.lfHeight);
qFont.setPointSizeF(logFontHeight * 72.0 / qreal(verticalDPI_In));
qFont.setUnderline(logFont.lfUnderline);
qFont.setOverline(false);
qFont.setStrikeOut(logFont.lfStrikeOut);
return qFont;
}
// ### fixme Qt 6 (QTBUG-58610): See comment at QWindowsFontDatabase::systemDefaultFont()
HFONT QWindowsFontDatabaseBase::systemFont()
{
static const auto stock_sysfont =
reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT));
return stock_sysfont;
}
QFont QWindowsFontDatabaseBase::systemDefaultFont()
{
#if QT_VERSION >= 0x060000
// Qt 6: Obtain default GUI font (typically "Segoe UI, 9pt", see QTBUG-58610)
NONCLIENTMETRICS ncm;
ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0);
const QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont);
#else
LOGFONT lf;
GetObject(systemFont(), sizeof(lf), &lf);
QFont systemFont = LOGFONT_to_QFont(lf);
// "MS Shell Dlg 2" is the correct system font >= Win2k
if (systemFont.family() == QLatin1String("MS Shell Dlg"))
systemFont.setFamily(QStringLiteral("MS Shell Dlg 2"));
// Qt 5 by (Qt 4) legacy uses GetStockObject(DEFAULT_GUI_FONT) to
// obtain the default GUI font (typically "MS Shell Dlg 2, 8pt"). This has been
// long deprecated; the message font of the NONCLIENTMETRICS structure obtained by
// SystemParametersInfo(SPI_GETNONCLIENTMETRICS) should be used instead (see
// QWindowsTheme::refreshFonts(), typically "Segoe UI, 9pt"), which is larger.
#endif // Qt 5
qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont;
return systemFont;
}
#if !defined(QT_NO_DIRECTWRITE)
IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArray &fontData) const
{
QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
if (fontEngineData->directWriteFactory == nullptr) {
qCWarning(lcQpaFonts) << "DirectWrite factory not created in QWindowsFontDatabaseBase::createDirectWriteFace()";
return nullptr;
}
CustomFontFileLoader fontFileLoader(fontEngineData->directWriteFactory);
fontFileLoader.addKey(this, fontData);
IDWriteFontFile *fontFile = nullptr;
const void *key = this;
HRESULT hres = fontEngineData->directWriteFactory->CreateCustomFontFileReference(&key,
sizeof(void *),
fontFileLoader.loader(),
&fontFile);
if (FAILED(hres)) {
qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__);
return nullptr;
}
BOOL isSupportedFontType;
DWRITE_FONT_FILE_TYPE fontFileType;
DWRITE_FONT_FACE_TYPE fontFaceType;
UINT32 numberOfFaces;
fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces);
if (!isSupportedFontType) {
fontFile->Release();
return nullptr;
}
// ### Currently no support for .ttc, but we could easily return a list here.
IDWriteFontFace *directWriteFontFace = nullptr;
hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType,
1,
&fontFile,
0,
DWRITE_FONT_SIMULATIONS_NONE,
&directWriteFontFace);
if (FAILED(hres)) {
qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__);
fontFile->Release();
return nullptr;
}
fontFile->Release();
return directWriteFontFace;
}
#endif // !defined(QT_NO_DIRECTWRITE)
QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
{
QFontEngine *fontEngine = nullptr;
#if !defined(QT_NO_DIRECTWRITE)
QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
if (fontEngineData->directWriteFactory == nullptr)
return nullptr;
IDWriteFontFace *directWriteFontFace = createDirectWriteFace(fontData);
fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace,
pixelSize,
fontEngineData);
// Get font family from font data
EmbeddedFont font(fontData);
font.updateFromOS2Table(fontEngine);
fontEngine->fontDef.family = font.familyName();
fontEngine->fontDef.hintingPreference = hintingPreference;
directWriteFontFace->Release();
#endif // !defined(QT_NO_DIRECTWRITE)
return fontEngine;
}
QT_END_NAMESPACE

View File

@ -0,0 +1,131 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QWINDOWSFONTDATABASEBASE_P_H
#define QWINDOWSFONTDATABASEBASE_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <qpa/qplatformfontdatabase.h>
#include <QtCore/QSharedPointer>
#include <QtCore/QLoggingCategory>
#include <QtCore/qt_windows.h>
#if !defined(QT_NO_DIRECTWRITE)
struct IDWriteFactory;
struct IDWriteGdiInterop;
struct IDWriteFontFace;
#endif
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQpaFonts);
class QWindowsFontEngineData
{
Q_DISABLE_COPY_MOVE(QWindowsFontEngineData)
public:
QWindowsFontEngineData();
~QWindowsFontEngineData();
uint pow_gamma[256];
bool clearTypeEnabled = false;
qreal fontSmoothingGamma;
HDC hdc = 0;
#if !defined(QT_NO_DIRECTWRITE)
IDWriteFactory *directWriteFactory = nullptr;
IDWriteGdiInterop *directWriteGdiInterop = nullptr;
#endif
};
class QWindowsFontDatabaseBase : public QPlatformFontDatabase
{
public:
QWindowsFontDatabaseBase();
~QWindowsFontDatabaseBase() override;
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) override;
static int defaultVerticalDPI();
static QSharedPointer<QWindowsFontEngineData> data();
static void createDirectWriteFactory(IDWriteFactory **factory);
static QFont systemDefaultFont();
static HFONT systemFont();
static LOGFONT fontDefToLOGFONT(const QFontDef &fontDef, const QString &faceName);
static QFont LOGFONT_to_QFont(const LOGFONT& lf, int verticalDPI = 0);
class FontTable{};
class EmbeddedFont
{
public:
EmbeddedFont(const QByteArray &fontData) : m_fontData(fontData) {}
QString changeFamilyName(const QString &newFamilyName);
QByteArray data() const { return m_fontData; }
void updateFromOS2Table(QFontEngine *fontEngine);
FontTable *tableDirectoryEntry(const QByteArray &tagName);
QString familyName(FontTable *nameTableDirectory = nullptr);
private:
QByteArray m_fontData;
};
protected:
#if !defined(QT_NO_DIRECTWRITE)
IDWriteFontFace *createDirectWriteFace(const QByteArray &fontData) const;
#endif
private:
static bool init(QSharedPointer<QWindowsFontEngineData> data);
};
QT_END_NAMESPACE
#endif // QWINDOWSFONTDATABASEBASE_P_H

View File

@ -187,6 +187,18 @@ namespace {
}
static DWRITE_MEASURING_MODE renderModeToMeasureMode(DWRITE_RENDERING_MODE renderMode)
{
switch (renderMode) {
case DWRITE_RENDERING_MODE_GDI_CLASSIC:
return DWRITE_MEASURING_MODE_GDI_CLASSIC;
case DWRITE_RENDERING_MODE_GDI_NATURAL:
return DWRITE_MEASURING_MODE_GDI_NATURAL;
default:
return DWRITE_MEASURING_MODE_NATURAL;
}
}
static DWRITE_RENDERING_MODE hintingPreferenceToRenderingMode(QFont::HintingPreference hintingPreference)
{
if (QHighDpiScaling::isActive() && hintingPreference == QFont::PreferDefaultHinting)
@ -209,13 +221,10 @@ static DWRITE_RENDERING_MODE hintingPreferenceToRenderingMode(QFont::HintingPref
\ingroup qt-lighthouse-win
Font engine for subpixel positioned text on Windows Vista
(with platform update) and Windows 7. If selected during
(with platform update) and later. If selected during
configuration, the engine will be selected only when the hinting
preference of a font is set to None or Vertical hinting. The font
database uses most of the same logic but creates a direct write
font based on the LOGFONT rather than a GDI handle.
Will probably be superseded by a common Free Type font engine in Qt 5.X.
preference of a font is set to None or Vertical hinting, or
when fontengine=directwrite is selected as platform option.
*/
QWindowsFontEngineDirectWrite::QWindowsFontEngineDirectWrite(IDWriteFontFace *directWriteFontFace,
@ -480,9 +489,22 @@ void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEn
glyphIndices[i] = UINT16(glyphs->glyphs[i]);
QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size());
HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(),
glyphIndices.size(),
glyphMetrics.data());
HRESULT hr;
DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference));
if (renderMode == DWRITE_RENDERING_MODE_GDI_CLASSIC || renderMode == DWRITE_RENDERING_MODE_GDI_NATURAL) {
hr = m_directWriteFontFace->GetGdiCompatibleGlyphMetrics(float(fontDef.pixelSize),
1.0f,
NULL,
TRUE,
glyphIndices.data(),
glyphIndices.size(),
glyphMetrics.data());
} else {
hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(),
glyphIndices.size(),
glyphMetrics.data());
}
if (SUCCEEDED(hr)) {
qreal stretch = fontDef.stretch != QFont::AnyStretch ? fontDef.stretch / 100.0 : 1.0;
for (int i = 0; i < glyphs->numGlyphs; ++i)
@ -688,6 +710,8 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
DWRITE_RENDERING_MODE renderMode =
hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference));
DWRITE_MEASURING_MODE measureMode =
renderModeToMeasureMode(renderMode);
IDWriteGlyphRunAnalysis *glyphAnalysis = NULL;
HRESULT hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
@ -695,7 +719,7 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
1.0f,
&transform,
renderMode,
DWRITE_MEASURING_MODE_NATURAL,
measureMode,
0.0, 0.0,
&glyphAnalysis
);
@ -725,7 +749,7 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
0.0f,
&glyphRun,
NULL,
DWRITE_MEASURING_MODE_NATURAL,
measureMode,
NULL,
0,
&enumerator);
@ -756,7 +780,7 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t,
1.0f,
&transform,
renderMode,
DWRITE_MEASURING_MODE_NATURAL,
measureMode,
0.0, 0.0,
&colorGlyphsAnalysis
);
@ -996,6 +1020,7 @@ glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph
DWRITE_RENDERING_MODE renderMode =
hintingPreferenceToRenderingMode(QFont::HintingPreference(fontDef.hintingPreference));
DWRITE_MEASURING_MODE measureMode = renderModeToMeasureMode(renderMode);
IDWriteGlyphRunAnalysis *glyphAnalysis = NULL;
HRESULT hr = m_fontEngineData->directWriteFactory->CreateGlyphRunAnalysis(
@ -1003,7 +1028,7 @@ glyph_metrics_t QWindowsFontEngineDirectWrite::alphaMapBoundingBox(glyph_t glyph
1.0f,
&transform,
renderMode,
DWRITE_MEASURING_MODE_NATURAL,
measureMode,
0.0, 0.0,
&glyphAnalysis
);

View File

@ -2,11 +2,13 @@ QT *= gui-private
SOURCES += \
$$PWD/qwindowsfontdatabase.cpp \
$$PWD/qwindowsfontdatabasebase.cpp \
$$PWD/qwindowsfontengine.cpp \
$$PWD/qwindowsnativeimage.cpp
HEADERS += \
$$PWD/qwindowsfontdatabase_p.h \
$$PWD/qwindowsfontdatabasebase_p.h \
$$PWD/qwindowsfontengine_p.h \
$$PWD/qwindowsnativeimage_p.h
@ -16,7 +18,13 @@ qtConfig(freetype) {
}
qtConfig(directwrite):qtConfig(direct2d) {
qtConfig(directwrite2) {
qtConfig(directwrite3) {
QMAKE_USE_PRIVATE += dwrite_3
DEFINES *= QT_USE_DIRECTWRITE3 QT_USE_DIRECTWRITE2
SOURCES += $$PWD/qwindowsdirectwritefontdatabase.cpp
HEADERS += $$PWD/qwindowsdirectwritefontdatabase_p.h
} else: qtConfig(directwrite2) {
QMAKE_USE_PRIVATE += dwrite_2
DEFINES *= QT_USE_DIRECTWRITE2
} else {

View File

@ -48,6 +48,9 @@
#include "qwindowsscreen.h"
#include "qwindowstheme.h"
#include "qwindowsservices.h"
#ifdef QT_USE_DIRECTWRITE3
#include <QtFontDatabaseSupport/private/qwindowsdirectwritefontdatabase_p.h>
#endif
#ifndef QT_NO_FREETYPE
# include <QtFontDatabaseSupport/private/qwindowsfontdatabase_ft_p.h>
#endif
@ -187,7 +190,9 @@ static inline unsigned parseOptions(const QStringList &paramList,
unsigned options = 0;
for (const QString &param : paramList) {
if (param.startsWith(u"fontengine=")) {
if (param.endsWith(u"freetype")) {
if (param.endsWith(u"directwrite")) {
options |= QWindowsIntegration::FontDatabaseDirectWrite;
} else if (param.endsWith(u"freetype")) {
options |= QWindowsIntegration::FontDatabaseFreeType;
} else if (param.endsWith(u"native")) {
options |= QWindowsIntegration::FontDatabaseNative;
@ -504,14 +509,17 @@ QWindowsStaticOpenGLContext *QWindowsIntegration::staticOpenGLContext()
QPlatformFontDatabase *QWindowsIntegration::fontDatabase() const
{
if (!d->m_fontDatabase) {
#ifdef QT_NO_FREETYPE
d->m_fontDatabase = new QWindowsFontDatabase();
#else // QT_NO_FREETYPE
#ifdef QT_USE_DIRECTWRITE3
if (d->m_options & QWindowsIntegration::FontDatabaseDirectWrite)
d->m_fontDatabase = new QWindowsDirectWriteFontDatabase;
else
#endif
#ifndef QT_NO_FREETYPE
if (d->m_options & QWindowsIntegration::FontDatabaseFreeType)
d->m_fontDatabase = new QWindowsFontDatabaseFT;
else
d->m_fontDatabase = new QWindowsFontDatabase;
#endif // QT_NO_FREETYPE
d->m_fontDatabase = new QWindowsFontDatabase();
}
return d->m_fontDatabase;
}

View File

@ -72,7 +72,8 @@ public:
DetectAltGrModifier = 0x800,
RtlEnabled = 0x1000,
DarkModeWindowFrames = 0x2000,
DarkModeStyle = 0x4000
DarkModeStyle = 0x4000,
FontDatabaseDirectWrite = 0x8000
};
explicit QWindowsIntegration(const QStringList &paramList);