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