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