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