Implement missing features in DirectWrite font backend

The DirectWrite font backend is an optional backend which
is planned to take over as the default on Windows. In order
to do this, though, a few gaps need to be filled in order
for it to pass all autotests.

The following things are covered by this:

1. Bitmap fonts are unsupported in DirectWrite. We enumerate
   these using GDI and fall back to the GDI font engine when
   loading them. As part of this, we introduce a new handle
   type for fonts on Windows which can represent both
   the DirectWrite and GDI engines.
2. "Legacy font names" where sub-family is embedded in the
   family name is now enumerated together with the
   typographic font name.
3. The DirectWrite font engine was not loading kerning pairs
   from the font, like the other engines (omission which was
   detected by the test)
4. Turning off antialiasing does not work with DirectWrite, so
   we fall back to GDI for this.
5. Loading supported writing systems from application fonts
   was not supported.

Task-number: QTBUG-119420
Change-Id: Icf6c351afb0d7487b2f4634199088d701a324aae
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
bb10
Eskil Abrahamsen Blomfeldt 2023-12-07 13:09:33 +01:00
parent 16bcdba8e7
commit 0916415ef7
8 changed files with 457 additions and 110 deletions

View File

@ -580,7 +580,7 @@ qt_config_compile_test(directwrite3
int main(int, char **)
{
IUnknown *factory = nullptr;
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3),
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory6),
&factory);
return 0;
}

View File

@ -106,6 +106,12 @@ static QFont::Style fromDirectWriteStyle(DWRITE_FONT_STYLE style)
void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
{
auto it = m_populatedFonts.find(familyName);
if (it == m_populatedFonts.end() && m_populatedBitmapFonts.contains(familyName)) {
qCDebug(lcQpaFonts) << "Populating bitmap font" << familyName;
QWindowsFontDatabase::populateFamily(familyName);
return;
}
IDWriteFontFamily *fontFamily = it != m_populatedFonts.end() ? it.value() : nullptr;
if (fontFamily == nullptr) {
qCWarning(lcQpaFonts) << "Cannot find" << familyName << "in list of fonts";
@ -173,53 +179,38 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
IDWriteFontFace *face = nullptr;
if (SUCCEEDED(font->CreateFontFace(&face))) {
QSupportedWritingSystems writingSystems;
const void *tableData = nullptr;
UINT32 tableSize;
void *tableContext = nullptr;
BOOL exists;
HRESULT hr = face->TryGetFontTable(qFromBigEndian(QFont::Tag("OS/2").value()),
&tableData,
&tableSize,
&tableContext,
&exists);
if (SUCCEEDED(hr) && exists) {
writingSystems = QPlatformFontDatabase::writingSystemsFromOS2Table(reinterpret_cast<const char *>(tableData), tableSize);
} else { // Fall back to checking first character of each Unicode range in font (may include too many writing systems)
quint32 rangeCount;
hr = font1->GetUnicodeRanges(0, nullptr, &rangeCount);
if (rangeCount > 0) {
QVarLengthArray<DWRITE_UNICODE_RANGE, QChar::ScriptCount> ranges(rangeCount);
hr = font1->GetUnicodeRanges(rangeCount, ranges.data(), &rangeCount);
if (SUCCEEDED(hr)) {
for (uint i = 0; i < rangeCount; ++i) {
QChar::Script script = QChar::script(ranges.at(i).first);
QFontDatabase::WritingSystem writingSystem = qt_writing_system_for_script(script);
if (writingSystem > QFontDatabase::Any && writingSystem < QFontDatabase::WritingSystemsCount)
writingSystems.setSupported(writingSystem);
}
} else {
const QString errorString = qt_error_string(int(hr));
qCWarning(lcQpaFonts) << "Failed to get unicode ranges for font" << englishLocaleFamilyName << englishLocaleStyleName << ":" << errorString;
}
}
}
QSupportedWritingSystems writingSystems = supportedWritingSystems(face);
if (!englishLocaleStyleName.isEmpty() || defaultLocaleStyleName.isEmpty()) {
qCDebug(lcQpaFonts) << "Font" << englishLocaleFamilyName << englishLocaleStyleName << "supports writing systems:" << writingSystems;
QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
face->AddRef();
QPlatformFontDatabase::registerFont(englishLocaleFamilyName,
englishLocaleStyleName,
QString(),
weight,
style,
stretch,
antialias,
scalable,
size,
fixed,
writingSystems,
new FontHandle(face, englishLocaleFamilyName));
}
if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) {
QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
face->AddRef();
QPlatformFontDatabase::registerFont(defaultLocaleFamilyName,
defaultLocaleStyleName,
QString(),
weight,
style,
stretch,
antialias,
scalable,
size,
fixed,
writingSystems,
new FontHandle(face, defaultLocaleFamilyName));
}
face->Release();
@ -238,10 +229,144 @@ void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
}
}
QSupportedWritingSystems QWindowsDirectWriteFontDatabase::supportedWritingSystems(IDWriteFontFace *face) const
{
QSupportedWritingSystems writingSystems;
writingSystems.setSupported(QFontDatabase::Any);
DirectWriteScope<IDWriteFontFace1> face1;
if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace1),
reinterpret_cast<void **>(&face1)))) {
const void *tableData = nullptr;
UINT32 tableSize;
void *tableContext = nullptr;
BOOL exists;
HRESULT hr = face->TryGetFontTable(qFromBigEndian(QFont::Tag("OS/2").value()),
&tableData,
&tableSize,
&tableContext,
&exists);
if (SUCCEEDED(hr) && exists) {
writingSystems = QPlatformFontDatabase::writingSystemsFromOS2Table(reinterpret_cast<const char *>(tableData), tableSize);
} else { // Fall back to checking first character of each Unicode range in font (may include too many writing systems)
quint32 rangeCount;
hr = face1->GetUnicodeRanges(0, nullptr, &rangeCount);
if (rangeCount > 0) {
QVarLengthArray<DWRITE_UNICODE_RANGE, QChar::ScriptCount> ranges(rangeCount);
hr = face1->GetUnicodeRanges(rangeCount, ranges.data(), &rangeCount);
if (SUCCEEDED(hr)) {
for (uint i = 0; i < rangeCount; ++i) {
QChar::Script script = QChar::script(ranges.at(i).first);
QFontDatabase::WritingSystem writingSystem = qt_writing_system_for_script(script);
if (writingSystem > QFontDatabase::Any && writingSystem < QFontDatabase::WritingSystemsCount)
writingSystems.setSupported(writingSystem);
}
} else {
const QString errorString = qt_error_string(int(hr));
qCWarning(lcQpaFonts) << "Failed to get unicode ranges for font:" << errorString;
}
}
}
}
return writingSystems;
}
bool QWindowsDirectWriteFontDatabase::populateFamilyAliases(const QString &missingFamily)
{
// If the font has not been populated, it is possible this is a legacy font family supported
// by GDI. We make an attempt at loading it via GDI and then add this face directly to the
// database.
if (!missingFamily.isEmpty()
&& missingFamily.size() < LF_FACESIZE
&& !m_populatedFonts.contains(missingFamily)
&& !m_populatedBitmapFonts.contains(missingFamily)) {
qCDebug(lcQpaFonts) << "Loading unpopulated" << missingFamily << ". Trying GDI.";
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
memcpy(lf.lfFaceName, missingFamily.utf16(), missingFamily.size() * sizeof(wchar_t));
HFONT hfont = CreateFontIndirect(&lf);
if (hfont) {
HDC dummy = GetDC(0);
HGDIOBJ oldFont = SelectObject(dummy, hfont);
DirectWriteScope<IDWriteFontFace> directWriteFontFace;
if (SUCCEEDED(data()->directWriteGdiInterop->CreateFontFaceFromHdc(dummy, &directWriteFontFace))) {
DirectWriteScope<IDWriteFontCollection> fontCollection;
if (SUCCEEDED(data()->directWriteFactory->GetSystemFontCollection(&fontCollection))) {
DirectWriteScope<IDWriteFont> font;
if (SUCCEEDED(fontCollection->GetFontFromFontFace(*directWriteFontFace, &font))) {
DirectWriteScope<IDWriteFont1> font1;
if (SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont1),
reinterpret_cast<void **>(&font1)))) {
IDWriteLocalizedStrings *names;
if (SUCCEEDED(font1->GetFaceNames(&names))) {
wchar_t englishLocale[] = L"en-us";
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();
QSupportedWritingSystems writingSystems = supportedWritingSystems(*directWriteFontFace);
qCDebug(lcQpaFonts) << "Registering legacy font family" << missingFamily;
QPlatformFontDatabase::registerFont(missingFamily,
englishLocaleStyleName,
QString(),
weight,
style,
stretch,
false,
true,
0xffff,
fixed,
writingSystems,
new FontHandle(*directWriteFontFace, missingFamily));
SelectObject(dummy, oldFont);
DeleteObject(hfont);
return true;
}
}
}
}
}
SelectObject(dummy, oldFont);
DeleteObject(hfont);
}
}
// Skip over implementation in QWindowsFontDatabase
return QWindowsFontDatabaseBase::populateFamilyAliases(missingFamily);
}
QFontEngine *QWindowsDirectWriteFontDatabase::fontEngine(const QByteArray &fontData,
qreal pixelSize,
QFont::HintingPreference hintingPreference)
{
// Skip over implementation in QWindowsFontDatabase
return QWindowsFontDatabaseBase::fontEngine(fontData, pixelSize, hintingPreference);
}
QFontEngine *QWindowsDirectWriteFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
{
IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle);
Q_ASSERT(face != nullptr);
const FontHandle *fontHandle = static_cast<const FontHandle *>(handle);
IDWriteFontFace *face = fontHandle->fontFace;
if (face == nullptr || fontDef.styleStrategy & QFont::NoAntialias) {
qCDebug(lcQpaFonts) << "Falling back to GDI";
return QWindowsFontDatabase::fontEngine(fontDef, handle);
}
DWRITE_FONT_SIMULATIONS simulations = DWRITE_FONT_SIMULATIONS_NONE;
if (fontDef.weight >= QFont::DemiBold || fontDef.style != QFont::StyleNormal) {
@ -327,7 +452,7 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
return QStringList();
}
QStringList ret;
QSet<QString> ret;
for (int i = 0; i < faces.size(); ++i) {
IDWriteFontFace *face = faces.at(i);
wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
@ -335,22 +460,18 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
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);
IDWriteFontFace3 *face3 = nullptr;
QSupportedWritingSystems writingSystems = supportedWritingSystems(face);
DirectWriteScope<IDWriteFontFace3> face3;
if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
reinterpret_cast<void **>(&face3)))) {
reinterpret_cast<void **>(&face3)))) {
QString defaultLocaleFamilyName;
QString englishLocaleFamilyName;
IDWriteLocalizedStrings *names;
IDWriteLocalizedStrings *names = nullptr;
if (SUCCEEDED(face3->GetFamilyNames(&names))) {
defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
englishLocaleFamilyName = localeString(names, englishLocale);
@ -367,6 +488,43 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
names->Release();
}
BOOL ok;
QString defaultLocaleGdiCompatibleFamilyName;
QString englishLocaleGdiCompatibleFamilyName;
if (SUCCEEDED(face3->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &names, &ok)) && ok) {
defaultLocaleGdiCompatibleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
englishLocaleGdiCompatibleFamilyName = localeString(names, englishLocale);
names->Release();
}
QString defaultLocaleGdiCompatibleStyleName;
QString englishLocaleGdiCompatibleStyleName;
if (SUCCEEDED(face3->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES, &names, &ok)) && ok) {
defaultLocaleGdiCompatibleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
englishLocaleGdiCompatibleStyleName = localeString(names, englishLocale);
names->Release();
}
QString defaultLocaleTypographicFamilyName;
QString englishLocaleTypographicFamilyName;
if (SUCCEEDED(face3->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_FAMILY_NAMES, &names, &ok)) && ok) {
defaultLocaleTypographicFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
englishLocaleTypographicFamilyName = localeString(names, englishLocale);
names->Release();
}
QString defaultLocaleTypographicStyleName;
QString englishLocaleTypographicStyleName;
if (SUCCEEDED(face3->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_SUBFAMILY_NAMES, &names, &ok)) && ok) {
defaultLocaleTypographicStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
englishLocaleTypographicStyleName = localeString(names, englishLocale);
names->Release();
}
QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch());
QFont::Style style = fromDirectWriteStyle(face3->GetStyle());
QFont::Weight weight = fromDirectWriteWeight(face3->GetWeight());
@ -389,12 +547,22 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
applicationFont->properties.append(properties);
}
ret.append(englishLocaleFamilyName);
QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
face->AddRef();
ret.insert(englishLocaleFamilyName);
QPlatformFontDatabase::registerFont(englishLocaleFamilyName,
englishLocaleStyleName,
QString(),
weight,
style,
stretch,
antialias,
scalable,
size,
fixed,
writingSystems,
new FontHandle(face, englishLocaleFamilyName));
}
if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) {
if (!defaultLocaleFamilyName.isEmpty() && !ret.contains(defaultLocaleFamilyName)) {
if (applicationFont != nullptr) {
QFontDatabasePrivate::ApplicationFont::Properties properties;
properties.style = style;
@ -404,12 +572,121 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
applicationFont->properties.append(properties);
}
ret.append(defaultLocaleFamilyName);
QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
face->AddRef();
ret.insert(defaultLocaleFamilyName);
QPlatformFontDatabase::registerFont(defaultLocaleFamilyName,
defaultLocaleStyleName,
QString(),
weight,
style,
stretch,
antialias,
scalable,
size,
fixed,
writingSystems,
new FontHandle(face, defaultLocaleFamilyName));
}
if (!englishLocaleGdiCompatibleFamilyName.isEmpty() &&
!ret.contains(englishLocaleGdiCompatibleFamilyName)) {
if (applicationFont != nullptr) {
QFontDatabasePrivate::ApplicationFont::Properties properties;
properties.style = style;
properties.weight = weight;
properties.familyName = englishLocaleGdiCompatibleFamilyName;
applicationFont->properties.append(properties);
}
ret.insert(englishLocaleGdiCompatibleFamilyName);
QPlatformFontDatabase::registerFont(englishLocaleGdiCompatibleFamilyName,
englishLocaleGdiCompatibleStyleName,
QString(),
weight,
style,
stretch,
antialias,
scalable,
size,
fixed,
writingSystems,
new FontHandle(face, englishLocaleGdiCompatibleFamilyName));
}
if (!defaultLocaleGdiCompatibleFamilyName.isEmpty()
&& !ret.contains(defaultLocaleGdiCompatibleFamilyName)) {
if (applicationFont != nullptr) {
QFontDatabasePrivate::ApplicationFont::Properties properties;
properties.style = style;
properties.weight = weight;
properties.familyName = defaultLocaleGdiCompatibleFamilyName;
applicationFont->properties.append(properties);
}
ret.insert(defaultLocaleGdiCompatibleFamilyName);
QPlatformFontDatabase::registerFont(defaultLocaleGdiCompatibleFamilyName,
defaultLocaleGdiCompatibleStyleName,
QString(),
weight,
style,
stretch,
antialias,
scalable,
size,
fixed,
writingSystems,
new FontHandle(face, defaultLocaleGdiCompatibleFamilyName));
}
if (!englishLocaleTypographicFamilyName.isEmpty()
&& !ret.contains(englishLocaleTypographicFamilyName)) {
if (applicationFont != nullptr) {
QFontDatabasePrivate::ApplicationFont::Properties properties;
properties.style = style;
properties.weight = weight;
properties.familyName = englishLocaleTypographicFamilyName;
applicationFont->properties.append(properties);
}
ret.insert(englishLocaleTypographicFamilyName);
QPlatformFontDatabase::registerFont(englishLocaleTypographicFamilyName,
englishLocaleTypographicStyleName,
QString(),
weight,
style,
stretch,
antialias,
scalable,
size,
fixed,
writingSystems,
new FontHandle(face, englishLocaleTypographicFamilyName));
}
if (!defaultLocaleTypographicFamilyName.isEmpty()
&& !ret.contains(defaultLocaleTypographicFamilyName)) {
if (applicationFont != nullptr) {
QFontDatabasePrivate::ApplicationFont::Properties properties;
properties.style = style;
properties.weight = weight;
properties.familyName = defaultLocaleTypographicFamilyName;
applicationFont->properties.append(properties);
}
ret.insert(defaultLocaleTypographicFamilyName);
QPlatformFontDatabase::registerFont(defaultLocaleTypographicFamilyName,
defaultLocaleTypographicStyleName,
QString(),
weight,
style,
stretch,
antialias,
scalable,
size,
fixed,
writingSystems,
new FontHandle(face, defaultLocaleTypographicFamilyName));
}
face3->Release();
} else {
qCWarning(lcQpaFonts) << "Unable to query IDWriteFontFace3 interface from font face.";
}
@ -417,18 +694,7 @@ QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray
face->Release();
}
return ret;
}
void QWindowsDirectWriteFontDatabase::releaseHandle(void *handle)
{
IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle);
face->Release();
}
bool QWindowsDirectWriteFontDatabase::fontsAlwaysScalable() const
{
return true;
return ret.values();
}
bool QWindowsDirectWriteFontDatabase::isPrivateFontFamily(const QString &family) const
@ -437,6 +703,29 @@ bool QWindowsDirectWriteFontDatabase::isPrivateFontFamily(const QString &family)
return false;
}
static int QT_WIN_CALLBACK populateBitmapFonts(const LOGFONT *logFont,
const TEXTMETRIC *textmetric,
DWORD type,
LPARAM lparam)
{
Q_UNUSED(textmetric);
// the "@family" fonts are just the same as "family". Ignore them.
const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont);
const wchar_t *faceNameW = f->elfLogFont.lfFaceName;
if (faceNameW[0] && faceNameW[0] != L'@' && wcsncmp(faceNameW, L"WST_", 4)) {
const QString faceName = QString::fromWCharArray(faceNameW);
if (type & RASTER_FONTTYPE || type == 0) {
QWindowsDirectWriteFontDatabase *db = reinterpret_cast<QWindowsDirectWriteFontDatabase *>(lparam);
if (!db->hasPopulatedFont(faceName)) {
db->registerFontFamily(faceName);
db->registerBitmapFont(faceName);
}
}
}
return 1; // continue
}
void QWindowsDirectWriteFontDatabase::populateFontDatabase()
{
wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
@ -446,10 +735,19 @@ void QWindowsDirectWriteFontDatabase::populateFontDatabase()
const QString defaultFontName = defaultFont().families().constFirst();
const QString systemDefaultFontName = systemDefaultFont().families().constFirst();
IDWriteFontCollection *fontCollection;
if (SUCCEEDED(data()->directWriteFactory->GetSystemFontCollection(&fontCollection))) {
DirectWriteScope<IDWriteFontCollection2> fontCollection;
DirectWriteScope<IDWriteFactory6> factory6;
if (FAILED(data()->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory6),
reinterpret_cast<void **>(&factory6)))) {
qCWarning(lcQpaFonts) << "Can't initialize IDWriteFactory6. Use GDI font engine instead.";
return;
}
if (SUCCEEDED(factory6->GetSystemFontCollection(false,
DWRITE_FONT_FAMILY_MODEL_TYPOGRAPHIC,
&fontCollection))) {
for (uint i = 0; i < fontCollection->GetFontFamilyCount(); ++i) {
IDWriteFontFamily *fontFamily;
IDWriteFontFamily2 *fontFamily;
if (SUCCEEDED(fontCollection->GetFontFamily(i, &fontFamily))) {
QString defaultLocaleName;
QString englishLocaleName;
@ -493,6 +791,17 @@ void QWindowsDirectWriteFontDatabase::populateFontDatabase()
}
}
}
// Since bitmap fonts are not supported by DirectWrite, we need to populate these as well
{
HDC dummy = GetDC(0);
LOGFONT lf;
lf.lfCharSet = DEFAULT_CHARSET;
lf.lfFaceName[0] = 0;
lf.lfPitchAndFamily = 0;
EnumFontFamiliesEx(dummy, &lf, populateBitmapFonts, reinterpret_cast<intptr_t>(this), 0);
ReleaseDC(0, dummy);
}
}
QFont QWindowsDirectWriteFontDatabase::defaultFont() const

View File

@ -20,17 +20,18 @@
QT_REQUIRE_CONFIG(directwrite3);
#include "qwindowsfontdatabasebase_p.h"
#include "qwindowsfontdatabase_p.h"
#include <QtCore/qloggingcategory.h>
struct IDWriteFactory;
struct IDWriteFont;
struct IDWriteFont1;
struct IDWriteFontFamily;
struct IDWriteLocalizedStrings;
QT_BEGIN_NAMESPACE
class Q_GUI_EXPORT QWindowsDirectWriteFontDatabase : public QWindowsFontDatabaseBase
class Q_GUI_EXPORT QWindowsDirectWriteFontDatabase : public QWindowsFontDatabase
{
Q_DISABLE_COPY_MOVE(QWindowsDirectWriteFontDatabase)
public:
@ -39,21 +40,34 @@ public:
void populateFontDatabase() override;
void populateFamily(const QString &familyName) override;
bool populateFamilyAliases(const QString &missingFamily) override;
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) 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, QFontDatabasePrivate::ApplicationFont *font = nullptr) override;
void releaseHandle(void *handle) override;
QFont defaultFont() const override;
bool fontsAlwaysScalable() const override;
bool isPrivateFontFamily(const QString &family) const override;
bool supportsVariableApplicationFonts() const override;
void registerBitmapFont(const QString &bitmapFont)
{
m_populatedBitmapFonts.insert(bitmapFont);
}
bool hasPopulatedFont(const QString &fontFamily) const
{
return m_populatedFonts.contains(fontFamily);
}
private:
friend class QWindowsFontEngineDirectWrite;
static QString localeString(IDWriteLocalizedStrings *names, wchar_t localeName[]);
QSupportedWritingSystems supportedWritingSystems(IDWriteFontFace *face) const;
QHash<QString, IDWriteFontFamily *> m_populatedFonts;
QSet<QString> m_populatedBitmapFonts;
};
QT_END_NAMESPACE

View File

@ -434,18 +434,6 @@ QFontNames qt_getCanonicalFontNames(const LOGFONT &lf)
return fontNames;
}
static QChar *createFontFile(const QString &faceName)
{
QChar *faceNamePtr = nullptr;
if (!faceName.isEmpty()) {
const int nameLength = qMin(faceName.length(), LF_FACESIZE - 1);
faceNamePtr = new QChar[nameLength + 1];
memcpy(static_cast<void *>(faceNamePtr), faceName.data(), sizeof(wchar_t) * nameLength);
faceNamePtr[nameLength] = u'\0';
}
return faceNamePtr;
}
namespace {
struct StoreFontPayload {
StoreFontPayload(const QString &family,
@ -554,19 +542,19 @@ static bool addFontToDatabase(QString familyName,
const bool wasPopulated = QPlatformFontDatabase::isFamilyPopulated(familyName);
QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight,
style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
// add fonts windows can generate for us:
if (weight <= QFont::DemiBold && styleName.isEmpty())
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold,
style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
if (style != QFont::StyleItalic && styleName.isEmpty())
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight,
QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
if (weight <= QFont::DemiBold && style != QFont::StyleItalic && styleName.isEmpty())
QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold,
QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
// We came here from populating a different font family, so we have
// to ensure the entire typographic family is populated before we
@ -580,7 +568,7 @@ static bool addFontToDatabase(QString familyName,
if (!subFamilyName.isEmpty() && familyName != subFamilyName) {
QPlatformFontDatabase::registerFont(subFamilyName, subFamilyStyle, foundryName, weight,
style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName));
style, stretch, antialias, scalable, size, fixed, writingSystems, new QWindowsFontDatabase::FontHandle(faceName));
}
if (!englishName.isEmpty() && englishName != familyName)
@ -752,7 +740,8 @@ QWindowsFontDatabase::~QWindowsFontDatabase()
QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
{
const QString faceName(static_cast<const QChar*>(handle));
FontHandle *fontHandle = static_cast<FontHandle *>(handle);
const QString faceName = fontHandle->faceName.left(LF_FACESIZE - 1);
QFontEngine *fe = QWindowsFontDatabase::createEngine(fontDef, faceName,
defaultVerticalDPI(),
data());
@ -1124,10 +1113,22 @@ void QWindowsFontDatabase::removeApplicationFonts()
m_eudcFonts.clear();
}
QWindowsFontDatabase::FontHandle::FontHandle(IDWriteFontFace *face, const QString &name)
: fontFace(face), faceName(name)
{
fontFace->AddRef();
}
QWindowsFontDatabase::FontHandle::~FontHandle()
{
if (fontFace != nullptr)
fontFace->Release();
}
void QWindowsFontDatabase::releaseHandle(void *handle)
{
const QChar *faceName = reinterpret_cast<const QChar *>(handle);
delete[] faceName;
delete static_cast<FontHandle *>(handle);
}
QString QWindowsFontDatabase::fontDir() const

View File

@ -75,9 +75,16 @@ public:
static void debugFormat(QDebug &d, const LOGFONT &lf);
#endif // !QT_NO_DEBUG_STREAM
struct FontHandle {
FontHandle(const QString &name) : faceName(name) {}
FontHandle(IDWriteFontFace *face, const QString &name);
~FontHandle();
IDWriteFontFace *fontFace = nullptr;
QString faceName;
};
private:
void addDefaultEUDCFont();
struct WinApplicationFont {

View File

@ -565,16 +565,27 @@ void QWindowsFontDatabaseBase::createDirectWriteFactory(IDWriteFactory **factory
IUnknown *result = nullptr;
# if QT_CONFIG(directwrite3)
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory5), &result);
if (result == nullptr)
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result);
# endif
if (result == nullptr)
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result);
qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory6";
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory6), &result);
if (result == nullptr) {
qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory5";
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory5), &result);
}
if (result == nullptr) {
qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory3";
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result);
}
# endif
if (result == nullptr) {
qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory2";
DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result);
}
if (result == nullptr) {
qCDebug(lcQpaFonts) << "Trying to create plain IDWriteFactory";
if (FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &result))) {
qErrnoWarning("DWriteCreateFactory failed");
return;

View File

@ -365,6 +365,8 @@ void QWindowsFontEngineDirectWrite::collectMetrics()
quint16 advanceWidthMax = qFromBigEndian<quint16>(table.constData() + advanceWidthMaxLocation);
m_maxAdvanceWidth = DESIGN_TO_LOGICAL(advanceWidthMax);
}
loadKerningPairs(emSquareSize() / QFixed::fromReal(fontDef.pixelSize));
}
QFixed QWindowsFontEngineDirectWrite::underlinePosition() const
@ -672,7 +674,8 @@ QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph,
bool QWindowsFontEngineDirectWrite::supportsHorizontalSubPixelPositions() const
{
return true;
DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef);
return (renderMode != DWRITE_RENDERING_MODE_GDI_CLASSIC && renderMode != DWRITE_RENDERING_MODE_GDI_NATURAL);
}
QFontEngine::Properties QWindowsFontEngineDirectWrite::properties() const

View File

@ -423,8 +423,10 @@ void tst_QFontDatabase::condensedFontMatching()
QFont f;
f.setStyleStrategy(QFont::NoFontMerging);
QFontPrivate *font_d = QFontPrivate::get(f);
if (font_d->engineForScript(QChar::Script_Common)->type() != QFontEngine::Freetype)
if (font_d->engineForScript(QChar::Script_Common)->type() != QFontEngine::Freetype
&& font_d->engineForScript(QChar::Script_Common)->type() != QFontEngine::DirectWrite) {
QEXPECT_FAIL("","No matching of sub-family by stretch on Windows", Continue);
}
#endif
QCOMPARE(QFontMetrics(tfcByStretch).horizontalAdvance(testString()),