Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qwindowsdirectwritefontdatabase.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
7
8#include <QtCore/qendian.h>
9#include <QtCore/qfile.h>
10#include <QtCore/qstringbuilder.h>
11#include <QtCore/qvarlengtharray.h>
12
13#include <dwrite_3.h>
14#include <d2d1.h>
15
17
18// Defined in gui/text/qfontdatabase.cpp
20
22{
23 qCDebug(lcQpaFonts) << "Creating DirectWrite database";
24}
25
27{
28 for (auto it = m_populatedFonts.begin(); it != m_populatedFonts.end(); ++it)
29 it.value()->Release();
30}
31
32QString QWindowsDirectWriteFontDatabase::localeString(IDWriteLocalizedStrings *names,
33 wchar_t localeName[])
34{
35 uint index;
36 BOOL exists;
37 if (SUCCEEDED(names->FindLocaleName(localeName, &index, &exists)) && exists) {
39 if (SUCCEEDED(names->GetStringLength(index, &length)) && length > 0) {
41 if (SUCCEEDED(names->GetString(index, buffer.data(), length + 1)))
42 return QString::fromWCharArray(buffer.data());
43 }
44 }
45
46 return QString();
47}
48
49static QFont::Stretch fromDirectWriteStretch(DWRITE_FONT_STRETCH stretch)
50{
51 switch (stretch) {
52 case DWRITE_FONT_STRETCH_ULTRA_CONDENSED: return QFont::UltraCondensed;
53 case DWRITE_FONT_STRETCH_EXTRA_CONDENSED: return QFont::ExtraCondensed;
54 case DWRITE_FONT_STRETCH_CONDENSED: return QFont::Condensed;
55 case DWRITE_FONT_STRETCH_SEMI_CONDENSED: return QFont::SemiCondensed;
56 case DWRITE_FONT_STRETCH_NORMAL: return QFont::Unstretched;
57 case DWRITE_FONT_STRETCH_SEMI_EXPANDED: return QFont::SemiExpanded;
58 case DWRITE_FONT_STRETCH_EXPANDED: return QFont::Expanded;
59 case DWRITE_FONT_STRETCH_EXTRA_EXPANDED: return QFont::ExtraExpanded;
60 case DWRITE_FONT_STRETCH_ULTRA_EXPANDED: return QFont::UltraExpanded;
61 default: return QFont::AnyStretch;
62 }
63}
64
65static QFont::Weight fromDirectWriteWeight(DWRITE_FONT_WEIGHT weight)
66{
67 return static_cast<QFont::Weight>(weight);
68}
69
70static QFont::Style fromDirectWriteStyle(DWRITE_FONT_STYLE style)
71{
72 switch (style) {
73 case DWRITE_FONT_STYLE_NORMAL: return QFont::StyleNormal;
74 case DWRITE_FONT_STYLE_OBLIQUE: return QFont::StyleOblique;
75 case DWRITE_FONT_STYLE_ITALIC: return QFont::StyleItalic;
76 default: return QFont::StyleNormal;
77 }
78}
79
81{
82 auto it = m_populatedFonts.find(familyName);
83 IDWriteFontFamily *fontFamily = it != m_populatedFonts.end() ? it.value() : nullptr;
84 if (fontFamily == nullptr) {
85 qCWarning(lcQpaFonts) << "Cannot find" << familyName << "in list of fonts";
86 return;
87 }
88
89 qCDebug(lcQpaFonts) << "Populate family:" << familyName;
90
91 wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
92 bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
93 wchar_t englishLocale[] = L"en-us";
94
95 static const int SMOOTH_SCALABLE = 0xffff;
96 const QString foundryName; // No such concept.
97 const bool scalable = true;
98 const bool antialias = false;
99 const int size = SMOOTH_SCALABLE;
100
101 IDWriteFontList *matchingFonts;
102 if (SUCCEEDED(fontFamily->GetMatchingFonts(DWRITE_FONT_WEIGHT_REGULAR,
103 DWRITE_FONT_STRETCH_NORMAL,
104 DWRITE_FONT_STYLE_NORMAL,
105 &matchingFonts))) {
106 for (uint j = 0; j < matchingFonts->GetFontCount(); ++j) {
107 IDWriteFont *font;
108 if (SUCCEEDED(matchingFonts->GetFont(j, &font))) {
109 IDWriteFont1 *font1 = nullptr;
110 if (!SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont1),
111 reinterpret_cast<void **>(&font1)))) {
112 qCWarning(lcQpaFonts) << "COM object does not support IDWriteFont1";
113 continue;
114 }
115
116 QString defaultLocaleFamilyName;
117 QString englishLocaleFamilyName;
118
119 IDWriteFontFamily *fontFamily2;
120 if (SUCCEEDED(font1->GetFontFamily(&fontFamily2))) {
121 IDWriteLocalizedStrings *names;
122 if (SUCCEEDED(fontFamily2->GetFamilyNames(&names))) {
123 defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
124 englishLocaleFamilyName = localeString(names, englishLocale);
125
126 names->Release();
127 }
128
129 fontFamily2->Release();
130 }
131
132 if (defaultLocaleFamilyName.isEmpty() && englishLocaleFamilyName.isEmpty())
133 englishLocaleFamilyName = familyName;
134
135 {
136 IDWriteLocalizedStrings *names;
137 if (SUCCEEDED(font1->GetFaceNames(&names))) {
138 QString defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
139 QString englishLocaleStyleName = localeString(names, englishLocale);
140
141 QFont::Stretch stretch = fromDirectWriteStretch(font1->GetStretch());
142 QFont::Style style = fromDirectWriteStyle(font1->GetStyle());
143 QFont::Weight weight = fromDirectWriteWeight(font1->GetWeight());
144 bool fixed = font1->IsMonospacedFont();
145
146 qCDebug(lcQpaFonts) << "Family" << familyName << "has english variant" << englishLocaleStyleName << ", in default locale:" << defaultLocaleStyleName << stretch << style << weight << fixed;
147
148 IDWriteFontFace *face = nullptr;
149 if (SUCCEEDED(font->CreateFontFace(&face))) {
150 QSupportedWritingSystems writingSystems;
151
152 const void *tableData = nullptr;
153 UINT32 tableSize;
154 void *tableContext = nullptr;
155 BOOL exists;
156 HRESULT hr = face->TryGetFontTable(qbswap<quint32>(MAKE_TAG('O','S','/','2')),
157 &tableData,
158 &tableSize,
159 &tableContext,
160 &exists);
161 if (SUCCEEDED(hr) && exists) {
162 writingSystems = QPlatformFontDatabase::writingSystemsFromOS2Table(reinterpret_cast<const char *>(tableData), tableSize);
163 } else { // Fall back to checking first character of each Unicode range in font (may include too many writing systems)
164 quint32 rangeCount;
165 hr = font1->GetUnicodeRanges(0, nullptr, &rangeCount);
166
167 if (rangeCount > 0) {
169
170 hr = font1->GetUnicodeRanges(rangeCount, ranges.data(), &rangeCount);
171 if (SUCCEEDED(hr)) {
172 for (uint i = 0; i < rangeCount; ++i) {
173 QChar::Script script = QChar::script(ranges.at(i).first);
174
176
177 if (writingSystem > QFontDatabase::Any && writingSystem < QFontDatabase::WritingSystemsCount)
178 writingSystems.setSupported(writingSystem);
179 }
180 } else {
181 const QString errorString = qt_error_string(int(hr));
182 qCWarning(lcQpaFonts) << "Failed to get unicode ranges for font" << englishLocaleFamilyName << englishLocaleStyleName << ":" << errorString;
183 }
184 }
185 }
186
187 if (!englishLocaleStyleName.isEmpty() || defaultLocaleStyleName.isEmpty()) {
188 qCDebug(lcQpaFonts) << "Font" << englishLocaleFamilyName << englishLocaleStyleName << "supports writing systems:" << writingSystems;
189
190 QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
191 face->AddRef();
192 }
193
194 if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) {
195 QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
196 face->AddRef();
197 }
198
199 face->Release();
200 }
201
202 names->Release();
203 }
204 }
205
206 font1->Release();
207 font->Release();
208 }
209 }
210
211 matchingFonts->Release();
212 }
213}
214
216{
217 IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle);
218 Q_ASSERT(face != nullptr);
219
221 fontEngine->initFontInfo(fontDef, defaultVerticalDPI());
222
223 return fontEngine;
224}
225
227{
229 result.append(familyForStyleHint(styleHint));
230 result.append(extraTryFontsForFamily(family));
231 result.append(QPlatformFontDatabase::fallbacksForFamily(family, style, styleHint, script));
232
233 qCDebug(lcQpaFonts) << __FUNCTION__ << family << style << styleHint
234 << script << result;
235 return result;
236}
237
239{
240 qCDebug(lcQpaFonts) << "Adding application font" << fileName;
241
242 QByteArray loadedData = fontData;
243 if (loadedData.isEmpty()) {
246 qCWarning(lcQpaFonts) << "Cannot open" << fileName << "for reading.";
247 return QStringList();
248 }
249 loadedData = file.readAll();
250 }
251
252 IDWriteFontFace *face = createDirectWriteFace(loadedData);
253 if (face == nullptr) {
254 qCWarning(lcQpaFonts) << "Failed to create DirectWrite face from font data. Font may be unsupported.";
255 return QStringList();
256 }
257
258 wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
259 bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
260 wchar_t englishLocale[] = L"en-us";
261
262 static const int SMOOTH_SCALABLE = 0xffff;
263 const QString foundryName; // No such concept.
264 const bool scalable = true;
265 const bool antialias = false;
266 const int size = SMOOTH_SCALABLE;
267
268 QSupportedWritingSystems writingSystems;
269 writingSystems.setSupported(QFontDatabase::Any);
270 writingSystems.setSupported(QFontDatabase::Latin);
271
273 IDWriteFontFace3 *face3 = nullptr;
274 if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
275 reinterpret_cast<void **>(&face3)))) {
276 QString defaultLocaleFamilyName;
277 QString englishLocaleFamilyName;
278
279 IDWriteLocalizedStrings *names;
280 if (SUCCEEDED(face3->GetFamilyNames(&names))) {
281 defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
282 englishLocaleFamilyName = localeString(names, englishLocale);
283
284 names->Release();
285 }
286
287 QString defaultLocaleStyleName;
288 QString englishLocaleStyleName;
289 if (SUCCEEDED(face3->GetFaceNames(&names))) {
290 defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
291 englishLocaleStyleName = localeString(names, englishLocale);
292
293 names->Release();
294 }
295
296 QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch());
297 QFont::Style style = fromDirectWriteStyle(face3->GetStyle());
298 QFont::Weight weight = fromDirectWriteWeight(face3->GetWeight());
299 bool fixed = face3->IsMonospacedFont();
300
301 qCDebug(lcQpaFonts) << "\tFont names:" << englishLocaleFamilyName << ", " << defaultLocaleFamilyName
302 << ", style names:" << englishLocaleStyleName << ", " << defaultLocaleStyleName
303 << ", stretch:" << stretch
304 << ", style:" << style
305 << ", weight:" << weight
306 << ", fixed:" << fixed;
307
308 if (!englishLocaleFamilyName.isEmpty()) {
309 if (applicationFont != nullptr) {
311 properties.style = style;
312 properties.weight = weight;
313 properties.familyName = englishLocaleFamilyName;
314 properties.styleName = englishLocaleStyleName;
315 applicationFont->properties.append(properties);
316 }
317
318 ret.append(englishLocaleFamilyName);
319 QPlatformFontDatabase::registerFont(englishLocaleFamilyName, englishLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
320 face->AddRef();
321 }
322
323 if (!defaultLocaleFamilyName.isEmpty() && defaultLocaleFamilyName != englishLocaleFamilyName) {
324 if (applicationFont != nullptr) {
326 properties.style = style;
327 properties.weight = weight;
328 properties.familyName = englishLocaleFamilyName;
329 properties.styleName = englishLocaleStyleName;
330 applicationFont->properties.append(properties);
331 }
332
333 ret.append(defaultLocaleFamilyName);
334 QPlatformFontDatabase::registerFont(defaultLocaleFamilyName, defaultLocaleStyleName, QString(), weight, style, stretch, antialias, scalable, size, fixed, writingSystems, face);
335 face->AddRef();
336 }
337
338 face3->Release();
339 } else {
340 qCWarning(lcQpaFonts) << "Unable to query IDWriteFontFace3 interface from font face.";
341 }
342
343 face->Release();
344
345 return ret;
346}
347
349{
350 IDWriteFontFace *face = reinterpret_cast<IDWriteFontFace *>(handle);
351 face->Release();
352}
353
355{
356 return true;
357}
358
360{
361 Q_UNUSED(family);
362 return false;
363}
364
366{
367 wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
368 bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
369 wchar_t englishLocale[] = L"en-us";
370
371 const QString defaultFontName = defaultFont().families().first();
372 const QString systemDefaultFontName = systemDefaultFont().families().first();
373
374 IDWriteFontCollection *fontCollection;
375 if (SUCCEEDED(data()->directWriteFactory->GetSystemFontCollection(&fontCollection))) {
376 for (uint i = 0; i < fontCollection->GetFontFamilyCount(); ++i) {
377 IDWriteFontFamily *fontFamily;
378 if (SUCCEEDED(fontCollection->GetFontFamily(i, &fontFamily))) {
379 QString defaultLocaleName;
380 QString englishLocaleName;
381
382 IDWriteLocalizedStrings *names;
383 if (SUCCEEDED(fontFamily->GetFamilyNames(&names))) {
384 if (hasDefaultLocale)
385 defaultLocaleName = localeString(names, defaultLocale);
386
387 englishLocaleName = localeString(names, englishLocale);
388 }
389
390 qCDebug(lcQpaFonts) << "Registering font, english name = " << englishLocaleName << ", name in current locale = " << defaultLocaleName;
391 if (!defaultLocaleName.isEmpty()) {
392 registerFontFamily(defaultLocaleName);
393 m_populatedFonts.insert(defaultLocaleName, fontFamily);
394 fontFamily->AddRef();
395
396 if (defaultLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) {
397 qDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << defaultLocaleName;
398
399 m_populatedFonts.insert(systemDefaultFontName, fontFamily);
400 fontFamily->AddRef();
401 }
402 }
403
404 if (!englishLocaleName.isEmpty() && englishLocaleName != defaultLocaleName) {
405 registerFontFamily(englishLocaleName);
406 m_populatedFonts.insert(englishLocaleName, fontFamily);
407 fontFamily->AddRef();
408
409 if (englishLocaleName == defaultFontName && defaultFontName != systemDefaultFontName) {
410 qDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName << "as alternative to" << englishLocaleName;
411
412 m_populatedFonts.insert(systemDefaultFontName, fontFamily);
413 fontFamily->AddRef();
414 }
415 }
416
417 fontFamily->Release();
418 }
419 }
420 }
421}
422
424{
425 return QFont(QStringLiteral("Segoe UI"));
426}
427
\inmodule QtCore
Definition qbytearray.h:57
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
Script script() const noexcept
Definition qchar.h:453
Script
Definition qchar.h:144
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
WritingSystem
\value Any \value Latin \value Greek \value Cyrillic \value Armenian \value Hebrew \value Arabic \val...
\reentrant
Definition qfont.h:20
StyleHint
Style hints are used by the \l{QFont}{font matching} algorithm to find an appropriate default family ...
Definition qfont.h:23
QStringList families() const
Definition qfont.cpp:2469
Stretch
Predefined stretch values that follow the CSS naming convention.
Definition qfont.h:80
@ AnyStretch
Definition qfont.h:81
@ ExtraCondensed
Definition qfont.h:83
@ Expanded
Definition qfont.h:88
@ Unstretched
Definition qfont.h:86
@ SemiCondensed
Definition qfont.h:85
@ SemiExpanded
Definition qfont.h:87
@ UltraCondensed
Definition qfont.h:82
@ ExtraExpanded
Definition qfont.h:89
@ Condensed
Definition qfont.h:84
@ UltraExpanded
Definition qfont.h:90
Weight
Qt uses a weighting scale from 1 to 1000 compatible with OpenType.
Definition qfont.h:60
Style
This enum describes the different styles of glyphs that are used to display text.
Definition qfont.h:73
@ StyleItalic
Definition qfont.h:75
@ StyleNormal
Definition qfont.h:74
@ StyleOblique
Definition qfont.h:76
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1202
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1258
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
static QSupportedWritingSystems writingSystemsFromOS2Table(const char *os2Table, size_t length)
Helper function that determines the writing system support based on the contents of the OS/2 table in...
virtual QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
Returns a list of alternative fonts for the specified family and style and script using the styleHint...
static void registerFontFamily(const QString &familyName)
Registers a font family with the font database.
static void registerFont(const QString &familyname, const QString &stylename, const QString &foundryname, QFont::Weight weight, QFont::Style style, QFont::Stretch stretch, bool antialiased, bool scalable, int pixelSize, bool fixedPitch, const QSupportedWritingSystems &writingSystems, void *handle)
Registers a font with the given set of attributes describing the font's foundry, family name,...
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1164
The QSupportedWritingSystems class is used when registering fonts with the internal Qt fontdatabase.
void setSupported(QFontDatabase::WritingSystem, bool supported=true)
Sets or clears support for the specified writingSystem based on the value given by support.
const T & at(qsizetype idx) const
T * data() noexcept
void populateFontDatabase() override
This function is called once at startup by Qt's internal font database.
QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *font=nullptr) override
Adds an application font described by the font contained supplied fontData or using the font containe...
void releaseHandle(void *handle) override
Releases the specified font handle.
QFontEngine * fontEngine(const QFontDef &fontDef, void *handle) override
Returns the font engine that can be used to render the font described by the font definition,...
QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const override
Returns a list of alternative fonts for the specified family and style and script using the styleHint...
QFont defaultFont() const override
Returns the default system font.
void populateFamily(const QString &familyName) override
This function is called whenever a lazily populated family, populated through registerFontFamily(),...
bool isPrivateFontFamily(const QString &family) const override
Returns true if the font family is private.
bool fontsAlwaysScalable() const override
Return true if all fonts are considered scalable when using this font database.
static QStringList extraTryFontsForFamily(const QString &family)
static QSharedPointer< QWindowsFontEngineData > data()
static QString familyForStyleHint(QFont::StyleHint styleHint)
Windows font engine using Direct Write.
QSet< QString >::iterator it
Combined button and popup list for selecting options.
static const QCssKnownValue properties[NumProperties - 1]
#define SMOOTH_SCALABLE
Q_GUI_EXPORT QFontDatabase::WritingSystem qt_writing_system_for_script(int script)
#define MAKE_TAG(ch1, ch2, ch3, ch4)
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
#define qDebug
[1]
Definition qlogging.h:160
#define qCWarning(category,...)
#define qCDebug(category,...)
return ret
GLuint64 GLenum void * handle
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLenum GLuint GLenum GLsizei length
GLenum face
GLuint GLuint GLfloat weight
GLenum GLuint buffer
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLsizeiptr const void * fontData
GLuint GLuint * names
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QStringLiteral(str)
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:45
unsigned int uint
Definition qtypes.h:29
long HRESULT
static QFont::Stretch fromDirectWriteStretch(DWRITE_FONT_STRETCH stretch)
static QFont::Weight fromDirectWriteWeight(DWRITE_FONT_WEIGHT weight)
QT_BEGIN_NAMESPACE Q_GUI_EXPORT QFontDatabase::WritingSystem qt_writing_system_for_script(int script)
static QFont::Style fromDirectWriteStyle(DWRITE_FONT_STYLE style)
QFile file
[0]
qreal pixelSize
Definition qfont_p.h:60