Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
19Q_GUI_EXPORT QFontDatabase::WritingSystem qt_writing_system_for_script(int script);
20
21// MinGW does not always define this
22#ifndef E_NOT_SUFFICIENT_BUFFER
23# define E_NOT_SUFFICIENT_BUFFER HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)
24#endif
25
26template<typename T>
28 DirectWriteScope(T *res = nullptr) : m_res(res) {}
30 if (m_res != nullptr)
31 m_res->Release();
32 }
33
34 T **operator&()
35 {
36 return &m_res;
37 }
38
39 T *operator->()
40 {
41 return m_res;
42 }
43
44 T *operator*() {
45 return m_res;
46 }
47
48private:
49 T *m_res;
50};
51
52QWindowsDirectWriteFontDatabase::QWindowsDirectWriteFontDatabase()
53{
54 qCDebug(lcQpaFonts) << "Creating DirectWrite database";
55}
56
57QWindowsDirectWriteFontDatabase::~QWindowsDirectWriteFontDatabase()
58{
59 for (auto it = m_populatedFonts.begin(); it != m_populatedFonts.end(); ++it)
60 it.value()->Release();
61}
62
63QString QWindowsDirectWriteFontDatabase::localeString(IDWriteLocalizedStrings *names,
64 wchar_t localeName[])
65{
66 uint index;
67 BOOL exists;
68 if (SUCCEEDED(names->FindLocaleName(localeName, &index, &exists)) && exists) {
69 uint length;
70 if (SUCCEEDED(names->GetStringLength(index, &length)) && length > 0) {
71 QVarLengthArray<wchar_t> buffer(int(length) + 1);
72 if (SUCCEEDED(names->GetString(index, buffer.data(), length + 1)))
73 return QString::fromWCharArray(buffer.data());
74 }
75 }
76
77 return QString();
78}
79
80QFont::Stretch QWindowsDirectWriteFontDatabase::fromDirectWriteStretch(DWRITE_FONT_STRETCH stretch)
81{
82 switch (stretch) {
83 case DWRITE_FONT_STRETCH_ULTRA_CONDENSED: return QFont::UltraCondensed;
84 case DWRITE_FONT_STRETCH_EXTRA_CONDENSED: return QFont::ExtraCondensed;
85 case DWRITE_FONT_STRETCH_CONDENSED: return QFont::Condensed;
86 case DWRITE_FONT_STRETCH_SEMI_CONDENSED: return QFont::SemiCondensed;
87 case DWRITE_FONT_STRETCH_NORMAL: return QFont::Unstretched;
88 case DWRITE_FONT_STRETCH_SEMI_EXPANDED: return QFont::SemiExpanded;
89 case DWRITE_FONT_STRETCH_EXPANDED: return QFont::Expanded;
90 case DWRITE_FONT_STRETCH_EXTRA_EXPANDED: return QFont::ExtraExpanded;
91 case DWRITE_FONT_STRETCH_ULTRA_EXPANDED: return QFont::UltraExpanded;
92 default: return QFont::AnyStretch;
93 }
94}
95
96QFont::Weight QWindowsDirectWriteFontDatabase::fromDirectWriteWeight(DWRITE_FONT_WEIGHT weight)
97{
98 return static_cast<QFont::Weight>(weight);
99}
100
101QFont::Style QWindowsDirectWriteFontDatabase::fromDirectWriteStyle(DWRITE_FONT_STYLE style)
102{
103 switch (style) {
104 case DWRITE_FONT_STYLE_NORMAL: return QFont::StyleNormal;
105 case DWRITE_FONT_STYLE_OBLIQUE: return QFont::StyleOblique;
106 case DWRITE_FONT_STYLE_ITALIC: return QFont::StyleItalic;
107 default: return QFont::StyleNormal;
108 }
109}
110
111void QWindowsDirectWriteFontDatabase::populateFamily(const QString &familyName)
112{
113 auto it = m_populatedFonts.find(familyName);
114 if (it == m_populatedFonts.end() && m_populatedBitmapFonts.contains(familyName)) {
115 qCDebug(lcQpaFonts) << "Populating bitmap font" << familyName;
116 QWindowsFontDatabase::populateFamily(familyName);
117 return;
118 }
119
120 IDWriteFontFamily *fontFamily = it != m_populatedFonts.end() ? it.value() : nullptr;
121 if (fontFamily == nullptr) {
122 qCWarning(lcQpaFonts) << "Cannot find" << familyName << "in list of fonts";
123 return;
124 }
125
126 qCDebug(lcQpaFonts) << "Populate family:" << familyName;
127
128 wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
129 bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
130 wchar_t englishLocale[] = L"en-us";
131
132 static const int SMOOTH_SCALABLE = 0xffff;
133 const QString foundryName; // No such concept.
134 const bool scalable = true;
135 const bool antialias = false;
136 const int size = SMOOTH_SCALABLE;
137
138 DirectWriteScope<IDWriteFontList> matchingFonts;
139 if (SUCCEEDED(fontFamily->GetMatchingFonts(DWRITE_FONT_WEIGHT_REGULAR,
140 DWRITE_FONT_STRETCH_NORMAL,
141 DWRITE_FONT_STYLE_NORMAL,
142 &matchingFonts))) {
143 for (uint j = 0; j < matchingFonts->GetFontCount(); ++j) {
144 DirectWriteScope<IDWriteFont> font;
145 if (SUCCEEDED(matchingFonts->GetFont(j, &font))) {
146 DirectWriteScope<IDWriteFont2> font2;
147 if (!SUCCEEDED(font->QueryInterface(__uuidof(IDWriteFont2),
148 reinterpret_cast<void **>(&font2)))) {
149 qCWarning(lcQpaFonts) << "COM object does not support IDWriteFont1";
150 continue;
151 }
152
153 QString defaultLocaleFamilyName;
154 QString englishLocaleFamilyName;
155 QString defaultLocaleGdiCompatibleFamilyName;
156 QString englishLocaleGdiCompatibleFamilyName;
157
158 DirectWriteScope<IDWriteFontFamily> fontFamily2;
159 if (SUCCEEDED(font2->GetFontFamily(&fontFamily2))) {
160 DirectWriteScope<IDWriteLocalizedStrings> names;
161 if (SUCCEEDED(fontFamily2->GetFamilyNames(&names))) {
162 defaultLocaleFamilyName = hasDefaultLocale ? localeString(*names, defaultLocale) : QString();
163 englishLocaleFamilyName = localeString(*names, englishLocale);
164 }
165
166 BOOL ok;
167 if (SUCCEEDED(font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &names, &ok)) && ok) {
168 defaultLocaleGdiCompatibleFamilyName = hasDefaultLocale ? localeString(*names, defaultLocale) : QString();
169 englishLocaleGdiCompatibleFamilyName = localeString(*names, englishLocale);
170 }
171 }
172
173 if (defaultLocaleFamilyName.isEmpty()
174 && englishLocaleFamilyName.isEmpty()
175 && englishLocaleGdiCompatibleFamilyName.isEmpty()
176 && defaultLocaleGdiCompatibleFamilyName.isEmpty()) {
177 englishLocaleFamilyName = familyName;
178 }
179
180 QFont::Stretch stretch = fromDirectWriteStretch(font2->GetStretch());
181 QFont::Style style = fromDirectWriteStyle(font2->GetStyle());
182 QFont::Weight weight = fromDirectWriteWeight(font2->GetWeight());
183 bool fixed = font2->IsMonospacedFont();
184 bool color = font2->IsColorFont();
185
186 DirectWriteScope<IDWriteFontFace> face;
187 if (SUCCEEDED(font->CreateFontFace(&face))) {
188 QSupportedWritingSystems writingSystems = supportedWritingSystems(*face);
189
190 auto registerFontName = [&](const QString &rFamilyName,
191 const QString &rStyleName) {
192 qCDebug(lcQpaFonts) << "Family" << familyName << "has variant"
193 << rFamilyName << ", "
194 << rStyleName << ", "
195 << stretch << ", "
196 << style << ", "
197 << weight << ", "
198 << fixed << ", "
199 << writingSystems;
200 QPlatformFontDatabase::registerFont(rFamilyName,
201 rStyleName,
202 QString(),
203 weight,
204 style,
205 stretch,
206 antialias,
207 scalable,
208 size,
209 fixed,
210 color,
211 writingSystems,
212 new FontHandle(*face, rFamilyName));
213 };
214
215 // Register the standard face names
216 DirectWriteScope<IDWriteLocalizedStrings> names;
217 if (SUCCEEDED(font2->GetFaceNames(&names))) {
218 QString defaultLocaleStyleName = hasDefaultLocale ? localeString(*names, defaultLocale) : QString();
219 QString englishLocaleStyleName = localeString(*names, englishLocale);
220 qCDebug(lcQpaFonts) << ">>> Storing English locale names";
221 registerFontName(englishLocaleFamilyName, englishLocaleStyleName);
222
223 // If the font has another name in the current default locale, register
224 // this as well
225 if (!defaultLocaleFamilyName.isEmpty()
226 && defaultLocaleFamilyName != englishLocaleFamilyName) {
227 qCDebug(lcQpaFonts) << ">>> Storing default locale names";
228 registerFontName(defaultLocaleFamilyName, defaultLocaleStyleName);
229 }
230 }
231
232 // Register GDI compatible names if they differ from the main face names
233 if (!defaultLocaleGdiCompatibleFamilyName.isEmpty()
234 || !englishLocaleGdiCompatibleFamilyName.isEmpty()) {
235 QString defaultLocaleGdiCompatibleStyleName;
236 QString englishLocaleGdiCompatibleStyleName;
237
238 DirectWriteScope<IDWriteLocalizedStrings> names;
239 BOOL ok;
240 if (SUCCEEDED(font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES, &names, &ok)) && ok) {
241 defaultLocaleGdiCompatibleStyleName = hasDefaultLocale ? localeString(*names, defaultLocale) : QString();
242 englishLocaleGdiCompatibleStyleName = localeString(*names, englishLocale);
243
244 if (!englishLocaleGdiCompatibleFamilyName.isEmpty()
245 && englishLocaleGdiCompatibleFamilyName != englishLocaleFamilyName) {
246 qCDebug(lcQpaFonts) << ">>> Storing English GDI compatible names";
247 registerFontName(englishLocaleGdiCompatibleFamilyName,
248 englishLocaleGdiCompatibleStyleName);
249 }
250
251 if (!defaultLocaleGdiCompatibleFamilyName.isEmpty()
252 && defaultLocaleGdiCompatibleFamilyName != englishLocaleGdiCompatibleFamilyName
253 && defaultLocaleGdiCompatibleFamilyName != defaultLocaleFamilyName) {
254 qCDebug(lcQpaFonts) << ">>> Storing Default locale GDI compatible names";
255 registerFontName(defaultLocaleGdiCompatibleFamilyName,
256 defaultLocaleGdiCompatibleStyleName);
257 }
258 }
259 }
260 }
261 }
262 }
263 }
264}
265
266QSupportedWritingSystems QWindowsDirectWriteFontDatabase::supportedWritingSystems(IDWriteFontFace *face) const
267{
268 QSupportedWritingSystems writingSystems;
269 writingSystems.setSupported(QFontDatabase::Any);
270
271 DirectWriteScope<IDWriteFontFace1> face1;
272 if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace1),
273 reinterpret_cast<void **>(&face1)))) {
274 const void *tableData = nullptr;
275 UINT32 tableSize;
276 void *tableContext = nullptr;
277 BOOL exists;
278 HRESULT hr = face->TryGetFontTable(qFromBigEndian(QFont::Tag("OS/2").value()),
279 &tableData,
280 &tableSize,
281 &tableContext,
282 &exists);
283 if (SUCCEEDED(hr) && exists) {
284 writingSystems = QPlatformFontDatabase::writingSystemsFromOS2Table(reinterpret_cast<const char *>(tableData), tableSize);
285 face->ReleaseFontTable(tableContext);
286 } else { // Fall back to checking first character of each Unicode range in font (may include too many writing systems)
287 quint32 rangeCount;
288 hr = face1->GetUnicodeRanges(0, nullptr, &rangeCount);
289
290 // Documented to return E_NO_SUFFICIENT_BUFFER when provided maxRangeCount is too
291 // small
292 if (hr == E_NOT_SUFFICIENT_BUFFER && rangeCount > 0) {
293 QVarLengthArray<DWRITE_UNICODE_RANGE, QChar::ScriptCount> ranges(rangeCount);
294
295 hr = face1->GetUnicodeRanges(rangeCount, ranges.data(), &rangeCount);
296 if (SUCCEEDED(hr)) {
297 for (uint i = 0; i < rangeCount; ++i) {
298 QChar::Script script = QChar::script(ranges.at(i).first);
299
300 QFontDatabase::WritingSystem writingSystem = qt_writing_system_for_script(script);
301
302 if (writingSystem > QFontDatabase::Any && writingSystem < QFontDatabase::WritingSystemsCount)
303 writingSystems.setSupported(writingSystem);
304 }
305 } else {
306 const QString errorString = qt_error_string(int(hr));
307 qCWarning(lcQpaFonts) << "Failed to get unicode ranges for font:" << errorString;
308 }
309 }
310 }
311 }
312
313 return writingSystems;
314}
315
316bool QWindowsDirectWriteFontDatabase::populateFamilyAliases(const QString &missingFamily)
317{
318 // Skip over implementation in QWindowsFontDatabase
319 return QWindowsFontDatabaseBase::populateFamilyAliases(missingFamily);
320}
321
322QFontEngine *QWindowsDirectWriteFontDatabase::fontEngine(const QByteArray &fontData,
323 qreal pixelSize,
324 QFont::HintingPreference hintingPreference)
325{
326 // Skip over implementation in QWindowsFontDatabase
327 return QWindowsFontDatabaseBase::fontEngine(fontData, pixelSize, hintingPreference);
328}
329
330QFontEngine *QWindowsDirectWriteFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
331{
332 const FontHandle *fontHandle = static_cast<const FontHandle *>(handle);
333 IDWriteFontFace *face = fontHandle->fontFace;
334 if (face == nullptr) {
335 qCDebug(lcQpaFonts) << "Falling back to GDI";
336 return QWindowsFontDatabase::fontEngine(fontDef, handle);
337 }
338
339 DWRITE_FONT_SIMULATIONS simulations = DWRITE_FONT_SIMULATIONS_NONE;
340 if (fontDef.weight >= QFont::DemiBold || fontDef.style != QFont::StyleNormal) {
341 DirectWriteScope<IDWriteFontFace3> face3;
342 if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
343 reinterpret_cast<void **>(&face3)))) {
344 if (fontDef.weight >= QFont::DemiBold && face3->GetWeight() < DWRITE_FONT_WEIGHT_DEMI_BOLD)
345 simulations |= DWRITE_FONT_SIMULATIONS_BOLD;
346
347 if (fontDef.style != QFont::StyleNormal && face3->GetStyle() == DWRITE_FONT_STYLE_NORMAL)
348 simulations |= DWRITE_FONT_SIMULATIONS_OBLIQUE;
349 }
350 }
351
352 DirectWriteScope<IDWriteFontFace5> newFace;
353 if (!fontDef.variableAxisValues.isEmpty() || simulations != DWRITE_FONT_SIMULATIONS_NONE) {
354 DirectWriteScope<IDWriteFontFace5> face5;
355 if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace5),
356 reinterpret_cast<void **>(&face5)))) {
357 DirectWriteScope<IDWriteFontResource> font;
358 if (SUCCEEDED(face5->GetFontResource(&font))) {
359 UINT32 fontAxisCount = font->GetFontAxisCount();
360 QVarLengthArray<DWRITE_FONT_AXIS_VALUE, 8> fontAxisValues(fontAxisCount);
361
362 if (!fontDef.variableAxisValues.isEmpty()) {
363 if (SUCCEEDED(face5->GetFontAxisValues(fontAxisValues.data(), fontAxisCount))) {
364 for (UINT32 i = 0; i < fontAxisCount; ++i) {
365 if (auto maybeTag = QFont::Tag::fromValue(qToBigEndian<UINT32>(fontAxisValues[i].axisTag))) {
366 if (fontDef.variableAxisValues.contains(*maybeTag))
367 fontAxisValues[i].value = fontDef.variableAxisValues.value(*maybeTag);
368 }
369 }
370 }
371 }
372
373 if (SUCCEEDED(font->CreateFontFace(simulations,
374 !fontDef.variableAxisValues.isEmpty() ? fontAxisValues.data() : nullptr,
375 !fontDef.variableAxisValues.isEmpty() ? fontAxisCount : 0,
376 &newFace))) {
377 face = *newFace;
378 } else {
379 qCWarning(lcQpaFonts) << "DirectWrite: Can't create font face for variable axis values";
380 }
381 }
382 }
383 }
384
385 QWindowsFontEngineDirectWrite *fontEngine = new QWindowsFontEngineDirectWrite(face, fontDef.pixelSize, data());
386 fontEngine->initFontInfo(fontDef, defaultVerticalDPI());
387
388 return fontEngine;
389}
390
391QStringList QWindowsDirectWriteFontDatabase::fallbacksForFamily(const QString &family,
392 QFont::Style style,
393 QFont::StyleHint styleHint,
394 QFontDatabasePrivate::ExtendedScript script) const
395{
396 QStringList result;
397 result.append(QWindowsFontDatabaseBase::familiesForScript(script));
398 result.append(familyForStyleHint(styleHint));
399 result.append(extraTryFontsForFamily(family));
400 result.append(QPlatformFontDatabase::fallbacksForFamily(family, style, styleHint, script));
401
402 qCDebug(lcQpaFonts) << __FUNCTION__ << family << style << styleHint
403 << script << result;
404 return result;
405}
406
407template<typename T>
408void QWindowsDirectWriteFontDatabase::collectAdditionalNames(T *font,
409 wchar_t *defaultLocale,
410 wchar_t *englishLocale,
411 std::function<void(const QString &, const QString &)> registerFamily)
412{
413 BOOL ok;
414 QString defaultLocaleGdiCompatibleFamilyName;
415 QString englishLocaleGdiCompatibleFamilyName;
416
417 const bool hasDefaultLocale = defaultLocale != nullptr;
418 Q_ASSERT(englishLocale != nullptr);
419
420 IDWriteLocalizedStrings *names = nullptr;
421 if (SUCCEEDED(font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &names, &ok)) && ok) {
422 defaultLocaleGdiCompatibleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
423 englishLocaleGdiCompatibleFamilyName = localeString(names, englishLocale);
424
425 names->Release();
426 }
427
428 QString defaultLocaleGdiCompatibleStyleName;
429 QString englishLocaleGdiCompatibleStyleName;
430 if (SUCCEEDED(font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES, &names, &ok)) && ok) {
431 defaultLocaleGdiCompatibleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
432 englishLocaleGdiCompatibleStyleName = localeString(names, englishLocale);
433
434 names->Release();
435 }
436
437 QString defaultLocaleTypographicFamilyName;
438 QString englishLocaleTypographicFamilyName;
439 if (SUCCEEDED(font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_FAMILY_NAMES, &names, &ok)) && ok) {
440 defaultLocaleTypographicFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
441 englishLocaleTypographicFamilyName = localeString(names, englishLocale);
442
443 names->Release();
444 }
445
446 QString defaultLocaleTypographicStyleName;
447 QString englishLocaleTypographicStyleName;
448 if (SUCCEEDED(font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_SUBFAMILY_NAMES, &names, &ok)) && ok) {
449 defaultLocaleTypographicStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
450 englishLocaleTypographicStyleName = localeString(names, englishLocale);
451
452 names->Release();
453 }
454
455 if (!englishLocaleGdiCompatibleFamilyName.isEmpty())
456 registerFamily(englishLocaleGdiCompatibleFamilyName, englishLocaleGdiCompatibleStyleName);
457
458 if (!defaultLocaleGdiCompatibleFamilyName.isEmpty())
459 registerFamily(defaultLocaleGdiCompatibleFamilyName, defaultLocaleGdiCompatibleStyleName);
460
461 if (!englishLocaleTypographicFamilyName.isEmpty())
462 registerFamily(englishLocaleTypographicFamilyName, englishLocaleTypographicStyleName);
463
464 if (!defaultLocaleTypographicFamilyName.isEmpty())
465 registerFamily(defaultLocaleTypographicFamilyName, defaultLocaleTypographicStyleName);
466}
467
468namespace {
469 struct FontInfo {
470 QFont::Stretch stretch;
471 QFont::Style style;
472 QFont::Weight weight;
473 QString familyName;
474 QString styleName;
475
476 bool operator==(const FontInfo &other) const
477 {
478 return stretch == other.stretch
479 && style == other.style
480 && weight == other.weight
481 && familyName == other.familyName
482 && styleName == other.styleName;
483 }
484
485 friend size_t qHash(const FontInfo &key, size_t seed = 0) noexcept
486 {
487 return qHashMulti(seed,
488 key.stretch,
489 key.style,
490 key.weight,
491 key.familyName,
492 key.styleName);
493 }
494 };
495
496}
497
498QStringList QWindowsDirectWriteFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont)
499{
500 qCDebug(lcQpaFonts) << "Adding application font" << fileName;
501
502 QByteArray loadedData = fontData;
503 if (loadedData.isEmpty()) {
504 QFile file(fileName);
505 if (!file.open(QIODevice::ReadOnly)) {
506 qCWarning(lcQpaFonts) << "Cannot open" << fileName << "for reading.";
507 return QStringList();
508 }
509 loadedData = file.readAll();
510 }
511
512 QList<IDWriteFontFace *> faces = createDirectWriteFaces(loadedData, fileName);
513 if (faces.isEmpty()) {
514 qCWarning(lcQpaFonts) << "Failed to create DirectWrite face from font data. Font may be unsupported.";
515 return QStringList();
516 }
517
518 QSet<FontInfo> registeredFonts;
519 QSet<QString> ret;
520 for (int i = 0; i < faces.size(); ++i) {
521 IDWriteFontFace *face = faces.at(i);
522 wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
523 bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
524 wchar_t englishLocale[] = L"en-us";
525
526 constexpr int SMOOTH_SCALABLE = 0xffff;
527 constexpr bool scalable = true;
528 constexpr bool antialias = false;
529 constexpr int size = SMOOTH_SCALABLE;
530
531 QSupportedWritingSystems writingSystems = supportedWritingSystems(face);
532 DirectWriteScope<IDWriteFontFace3> face3;
533 if (SUCCEEDED(face->QueryInterface(__uuidof(IDWriteFontFace3),
534 reinterpret_cast<void **>(&face3)))) {
535
536 QFont::Stretch stretch = fromDirectWriteStretch(face3->GetStretch());
537 QFont::Style style = fromDirectWriteStyle(face3->GetStyle());
538 QFont::Weight weight = fromDirectWriteWeight(face3->GetWeight());
539 bool fixed = face3->IsMonospacedFont();
540 bool color = face3->IsColorFont();
541
542 auto registerFamilyAndStyle = [&registeredFonts,
543 &ret,
544 applicationFont,
545 face,
546 writingSystems,
547 stretch,
548 style,
549 weight,
550 fixed,
551 color](const QString &familyName,
552 const QString &styleName)
553 {
554 FontInfo fontInfo = { stretch, style, weight, familyName, styleName };
555 if (registeredFonts.contains(fontInfo))
556 return;
557 registeredFonts.insert(fontInfo);
558 ret.insert(familyName);
559
560 qCDebug(lcQpaFonts) << "\tRegistering alternative:" << familyName
561 << ":" << styleName
562 << ", stretch =" << stretch
563 << ", style =" << style
564 << ", weight =" << weight
565 << ", fixed =" << fixed
566 << ", color =" << color;
567 if (applicationFont != nullptr) {
568 QFontDatabasePrivate::ApplicationFont::Properties properties;
569 properties.style = style;
570 properties.weight = weight;
571 properties.familyName = familyName;
572 properties.styleName = styleName;
573 properties.stretch = stretch;
574 applicationFont->properties.append(properties);
575 }
576
577 QPlatformFontDatabase::registerFont(familyName,
578 styleName,
579 QString(),
580 weight,
581 style,
582 stretch,
583 antialias,
584 scalable,
585 size,
586 fixed,
587 color,
588 writingSystems,
589 new FontHandle(face, familyName));
590 };
591
592 QString defaultLocaleFamilyName;
593 QString englishLocaleFamilyName;
594
595 IDWriteLocalizedStrings *names = nullptr;
596 if (SUCCEEDED(face3->GetFamilyNames(&names))) {
597 defaultLocaleFamilyName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
598 englishLocaleFamilyName = localeString(names, englishLocale);
599
600 names->Release();
601 }
602
603 QString defaultLocaleStyleName;
604 QString englishLocaleStyleName;
605 if (SUCCEEDED(face3->GetFaceNames(&names))) {
606 defaultLocaleStyleName = hasDefaultLocale ? localeString(names, defaultLocale) : QString();
607 englishLocaleStyleName = localeString(names, englishLocale);
608
609 names->Release();
610 }
611
612 qCDebug(lcQpaFonts) << "\tFont names:" << englishLocaleFamilyName << ", " << defaultLocaleFamilyName
613 << ", style names:" << englishLocaleStyleName << ", " << defaultLocaleStyleName
614 << ", stretch:" << stretch
615 << ", style:" << style
616 << ", weight:" << weight
617 << ", fixed:" << fixed;
618
619 if (!englishLocaleFamilyName.isEmpty())
620 registerFamilyAndStyle(englishLocaleFamilyName, englishLocaleStyleName);
621
622 if (!defaultLocaleFamilyName.isEmpty())
623 registerFamilyAndStyle(defaultLocaleFamilyName, defaultLocaleStyleName);
624
625 collectAdditionalNames(*face3,
626 hasDefaultLocale ? defaultLocale : nullptr,
627 englishLocale,
628 registerFamilyAndStyle);
629
630 } else {
631 qCWarning(lcQpaFonts) << "Unable to query IDWriteFontFace3 interface from font face.";
632 }
633
634 face->Release();
635 }
636
637 return ret.values();
638}
639
640bool QWindowsDirectWriteFontDatabase::isPrivateFontFamily(const QString &family) const
641{
642 Q_UNUSED(family);
643 return false;
644}
645
646static int QT_WIN_CALLBACK populateBitmapFonts(const LOGFONT *logFont,
647 const TEXTMETRIC *textmetric,
648 DWORD type,
649 LPARAM lparam)
650{
651 Q_UNUSED(textmetric);
652
653 // the "@family" fonts are just the same as "family". Ignore them.
654 const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont);
655 const wchar_t *faceNameW = f->elfLogFont.lfFaceName;
656 if (faceNameW[0] && faceNameW[0] != L'@' && wcsncmp(faceNameW, L"WST_", 4)) {
657 const QString faceName = QString::fromWCharArray(faceNameW);
658 if (type & RASTER_FONTTYPE || type == 0) {
659 QWindowsDirectWriteFontDatabase *db = reinterpret_cast<QWindowsDirectWriteFontDatabase *>(lparam);
660 if (!db->hasPopulatedFont(faceName)) {
661 db->registerFontFamily(faceName);
662 db->registerBitmapFont(faceName);
663 }
664 }
665 }
666 return 1; // continue
667}
668
669void QWindowsDirectWriteFontDatabase::populateFontDatabase()
670{
671 wchar_t defaultLocale[LOCALE_NAME_MAX_LENGTH];
672 bool hasDefaultLocale = GetUserDefaultLocaleName(defaultLocale, LOCALE_NAME_MAX_LENGTH) != 0;
673 wchar_t englishLocale[] = L"en-us";
674
675 const QString defaultFontName = defaultFont().family();
676 const QString systemDefaultFontName = systemDefaultFont().family();
677
678 DirectWriteScope<IDWriteFontCollection2> fontCollection;
679 DirectWriteScope<IDWriteFactory6> factory6;
680 if (FAILED(data()->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory6),
681 reinterpret_cast<void **>(&factory6)))) {
682 qCWarning(lcQpaFonts) << "Can't initialize IDWriteFactory6. Use GDI font engine instead.";
683 return;
684 }
685
686 if (SUCCEEDED(factory6->GetSystemFontCollection(false,
687 DWRITE_FONT_FAMILY_MODEL_TYPOGRAPHIC,
688 &fontCollection))) {
689 QSet<QString> registeredFamilies;
690 for (uint i = 0; i < fontCollection->GetFontFamilyCount(); ++i) {
691 DirectWriteScope<IDWriteFontFamily2> fontFamily;
692 if (SUCCEEDED(fontCollection->GetFontFamily(i, &fontFamily))) {
693 auto registerFamily = [&](const QString &familyName, const QString &styleName) {
694 Q_UNUSED(styleName);
695 if (registeredFamilies.contains(familyName))
696 return;
697 registeredFamilies.insert(familyName);
698
699 qCDebug(lcQpaFonts) << "Registering font family" << familyName;
700 registerFontFamily(familyName);
701 m_populatedFonts.insert(familyName, *fontFamily);
702 fontFamily->AddRef();
703
704 if (familyName == defaultFontName
705 && defaultFontName != systemDefaultFontName) {
706 qCDebug(lcQpaFonts) << "Adding default font" << systemDefaultFontName
707 << "as alternative to" << familyName;
708
709 m_populatedFonts.insert(systemDefaultFontName, *fontFamily);
710 fontFamily->AddRef();
711 }
712 };
713
714 QString defaultLocaleName;
715 QString englishLocaleName;
716 DirectWriteScope<IDWriteLocalizedStrings> names;
717 if (SUCCEEDED(fontFamily->GetFamilyNames(&names))) {
718 if (hasDefaultLocale)
719 defaultLocaleName = localeString(*names, defaultLocale);
720 englishLocaleName = localeString(*names, englishLocale);
721 }
722
723 {
724 if (!defaultLocaleName.isEmpty())
725 registerFamily(defaultLocaleName, QString{});
726 }
727
728 {
729 if (!englishLocaleName.isEmpty())
730 registerFamily(englishLocaleName, QString{});
731 }
732
733 for (uint j = 0; j < fontFamily->GetFontCount(); ++j) {
734 DirectWriteScope<IDWriteFont3> font;
735 if (SUCCEEDED(fontFamily->GetFont(j, &font))) {
736 collectAdditionalNames(*font,
737 hasDefaultLocale ? defaultLocale : nullptr,
738 englishLocale,
739 registerFamily);
740 }
741 }
742 }
743 }
744 }
745
746 // Since bitmap fonts are not supported by DirectWrite, we need to populate these as well
747 {
748 HDC dummy = GetDC(0);
749 LOGFONT lf;
750 lf.lfCharSet = DEFAULT_CHARSET;
751 lf.lfFaceName[0] = 0;
752 lf.lfPitchAndFamily = 0;
753 EnumFontFamiliesEx(dummy, &lf, populateBitmapFonts, reinterpret_cast<intptr_t>(this), 0);
754 ReleaseDC(0, dummy);
755 }
756}
757
758bool QWindowsDirectWriteFontDatabase::supportsVariableApplicationFonts() const
759{
760 QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
761 DirectWriteScope<IDWriteFactory5> factory5;
762 if (SUCCEEDED(fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory5),
763 reinterpret_cast<void **>(&factory5)))) {
764 return true;
765 }
766
767 return false;
768}
769
770void QWindowsDirectWriteFontDatabase::invalidate()
771{
772 QWindowsFontDatabase::invalidate();
773
774 for (IDWriteFontFamily *value : m_populatedFonts)
775 value->Release();
776 m_populatedFonts.clear();
777 m_populatedFonts.squeeze();
778
779 m_populatedBitmapFonts.clear();
780 m_populatedBitmapFonts.squeeze();
781}
782
783QT_END_NAMESPACE
Combined button and popup list for selecting options.
#define E_NOT_SUFFICIENT_BUFFER