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
qwindowsfontdatabasebase.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// Qt-Security score:critical reason:data-parser
4
7
8#include <QtCore/QThreadStorage>
9#include <QtCore/QtEndian>
10
11#if QT_CONFIG(directwrite)
12# if QT_CONFIG(directwrite3)
13# include <dwrite_3.h>
14# else
15# include <dwrite_2.h>
16# endif
17# include <d2d1.h>
18# include "qwindowsfontenginedirectwrite_p.h"
19#endif
20
21#include <utility> // for std::pair
22
23QT_BEGIN_NAMESPACE
24
25using namespace Qt::StringLiterals;
26
27// Helper classes for creating font engines directly from font data
28namespace {
29
30# pragma pack(1)
31
32 // Common structure for all formats of the "name" table
33 struct NameTable
34 {
35 quint16 format;
36 quint16 count;
37 quint16 stringOffset;
38 };
39
40 struct NameRecord
41 {
42 quint16 platformID;
43 quint16 encodingID;
44 quint16 languageID;
45 quint16 nameID;
46 quint16 length;
47 quint16 offset;
48 };
49
50 struct OffsetSubTable
51 {
52 quint32 scalerType;
53 quint16 numTables;
54 quint16 searchRange;
55 quint16 entrySelector;
56 quint16 rangeShift;
57 };
58
59 struct TableDirectory : public QWindowsFontDatabaseBase::FontTable
60 {
61 quint32 identifier;
62 quint32 checkSum;
63 quint32 offset;
64 quint32 length;
65 };
66
67 struct OS2Table
68 {
69 quint16 version;
70 qint16 avgCharWidth;
71 quint16 weightClass;
72 quint16 widthClass;
73 quint16 type;
74 qint16 subscriptXSize;
75 qint16 subscriptYSize;
76 qint16 subscriptXOffset;
77 qint16 subscriptYOffset;
78 qint16 superscriptXSize;
79 qint16 superscriptYSize;
80 qint16 superscriptXOffset;
81 qint16 superscriptYOffset;
82 qint16 strikeOutSize;
83 qint16 strikeOutPosition;
84 qint16 familyClass;
85 quint8 panose[10];
86 quint32 unicodeRanges[4];
87 quint8 vendorID[4];
88 quint16 selection;
89 quint16 firstCharIndex;
90 quint16 lastCharIndex;
91 qint16 typoAscender;
92 qint16 typoDescender;
93 qint16 typoLineGap;
94 quint16 winAscent;
95 quint16 winDescent;
96 quint32 codepageRanges[2];
97 qint16 height;
98 qint16 capHeight;
99 quint16 defaultChar;
100 quint16 breakChar;
101 quint16 maxContext;
102 };
103
104# pragma pack()
105
106} // Anonymous namespace
107
108QWindowsFontDatabaseBase::FontTable *QWindowsFontDatabaseBase::EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName)
109{
110 Q_ASSERT(tagName.size() == 4);
111 quint32 tagId = *(reinterpret_cast<const quint32 *>(tagName.constData()));
112 const size_t fontDataSize = m_fontData.size();
113 if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable)))
114 return nullptr;
115
116 OffsetSubTable *offsetSubTable = reinterpret_cast<OffsetSubTable *>(m_fontData.data());
117 TableDirectory *tableDirectory = reinterpret_cast<TableDirectory *>(offsetSubTable + 1);
118
119 const size_t tableCount = qFromBigEndian<quint16>(offsetSubTable->numTables);
120 if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable) + sizeof(TableDirectory) * tableCount))
121 return nullptr;
122
123 TableDirectory *tableDirectoryEnd = tableDirectory + tableCount;
124 for (TableDirectory *entry = tableDirectory; entry < tableDirectoryEnd; ++entry) {
125 if (entry->identifier == tagId)
126 return entry;
127 }
128
129 return nullptr;
130}
131
132QString QWindowsFontDatabaseBase::EmbeddedFont::familyName(QWindowsFontDatabaseBase::FontTable *directoryEntry)
133{
134 QString name;
135
136 TableDirectory *nameTableDirectoryEntry = static_cast<TableDirectory *>(directoryEntry);
137 if (nameTableDirectoryEntry == nullptr)
138 nameTableDirectoryEntry = static_cast<TableDirectory *>(tableDirectoryEntry("name"));
139
140 if (nameTableDirectoryEntry != nullptr) {
141 quint32 offset = qFromBigEndian<quint32>(nameTableDirectoryEntry->offset);
142 if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameTable)))
143 return QString();
144
145 NameTable *nameTable = reinterpret_cast<NameTable *>(m_fontData.data() + offset);
146 NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1);
147
148 quint16 nameTableCount = qFromBigEndian<quint16>(nameTable->count);
149 if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameRecord) * nameTableCount))
150 return QString();
151
152 for (int i = 0; i < nameTableCount; ++i, ++nameRecord) {
153 if (qFromBigEndian<quint16>(nameRecord->nameID) == 1
154 && qFromBigEndian<quint16>(nameRecord->platformID) == 3 // Windows
155 && qFromBigEndian<quint16>(nameRecord->languageID) == 0x0409) { // US English
156 quint16 stringOffset = qFromBigEndian<quint16>(nameTable->stringOffset);
157 quint16 nameOffset = qFromBigEndian<quint16>(nameRecord->offset);
158 quint16 nameLength = qFromBigEndian<quint16>(nameRecord->length);
159
160 if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + stringOffset + nameOffset + nameLength))
161 return QString();
162
163 const void *ptr = reinterpret_cast<const quint8 *>(nameTable)
164 + stringOffset
165 + nameOffset;
166
167 const quint16 *s = reinterpret_cast<const quint16 *>(ptr);
168 const quint16 *e = s + nameLength / sizeof(quint16);
169 while (s != e)
170 name += QChar( qFromBigEndian<quint16>(*s++));
171 break;
172 }
173 }
174 }
175
176 return name;
177}
178
179void QWindowsFontDatabaseBase::EmbeddedFont::updateFromOS2Table(QFontEngine *fontEngine)
180{
181 TableDirectory *os2TableEntry = static_cast<TableDirectory *>(tableDirectoryEntry("OS/2"));
182 if (os2TableEntry != nullptr) {
183 const quint32 os2TableOffset = qFromBigEndian<quint32>(os2TableEntry->offset);
184 if (Q_UNLIKELY(qsizetype(os2TableOffset) + qsizetype(sizeof(OS2Table)) > m_fontData.size())) {
185 qCWarning(lcQpaFonts) << "QWindowsFontDatabaseBase::EmbeddedFont::updateFromOS2Table: "
186 << "Invalid OS/2 table offset:" << os2TableOffset;
187 return;
188 }
189
190 const OS2Table *os2Table =
191 reinterpret_cast<const OS2Table *>(m_fontData.constData() + os2TableOffset);
192
193 bool italic = qFromBigEndian<quint16>(os2Table->selection) & (1 << 0);
194 bool oblique = qFromBigEndian<quint16>(os2Table->selection) & (1 << 9);
195
196 if (italic)
197 fontEngine->fontDef.style = QFont::StyleItalic;
198 else if (oblique)
199 fontEngine->fontDef.style = QFont::StyleOblique;
200 else
201 fontEngine->fontDef.style = QFont::StyleNormal;
202
203 fontEngine->fontDef.weight = qFromBigEndian<quint16>(os2Table->weightClass);
204 }
205}
206
207QString QWindowsFontDatabaseBase::EmbeddedFont::changeFamilyName(const QString &newFamilyName)
208{
209 TableDirectory *nameTableDirectoryEntry = static_cast<TableDirectory *>(tableDirectoryEntry("name"));
210 if (nameTableDirectoryEntry == nullptr)
211 return QString();
212
213 QString oldFamilyName = familyName(nameTableDirectoryEntry);
214
215 // Reserve size for name table header, five required name records and string
216 const int requiredRecordCount = 5;
217 quint16 nameIds[requiredRecordCount] = { 1, 2, 3, 4, 6 };
218
219 int sizeOfHeader = sizeof(NameTable) + sizeof(NameRecord) * requiredRecordCount;
220 int newFamilyNameSize = newFamilyName.size() * int(sizeof(quint16));
221
222 const QString regularString = QString::fromLatin1("Regular");
223 int regularStringSize = regularString.size() * int(sizeof(quint16));
224
225 // Align table size of table to 32 bits (pad with 0)
226 int fullSize = ((sizeOfHeader + newFamilyNameSize + regularStringSize) & ~3) + 4;
227
228 QByteArray newNameTable(fullSize, char(0));
229
230 {
231 NameTable *nameTable = reinterpret_cast<NameTable *>(newNameTable.data());
232 nameTable->count = qbswap<quint16>(requiredRecordCount);
233 nameTable->stringOffset = qbswap<quint16>(sizeOfHeader);
234
235 NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1);
236 for (int i = 0; i < requiredRecordCount; ++i, nameRecord++) {
237 nameRecord->nameID = qbswap<quint16>(nameIds[i]);
238 nameRecord->encodingID = qbswap<quint16>(1);
239 nameRecord->languageID = qbswap<quint16>(0x0409);
240 nameRecord->platformID = qbswap<quint16>(3);
241 nameRecord->length = qbswap<quint16>(newFamilyNameSize);
242
243 // Special case for sub-family
244 if (nameIds[i] == 4) {
245 nameRecord->offset = qbswap<quint16>(newFamilyNameSize);
246 nameRecord->length = qbswap<quint16>(regularStringSize);
247 }
248 }
249
250 // nameRecord now points to string data
251 quint16 *stringStorage = reinterpret_cast<quint16 *>(nameRecord);
252 for (QChar ch : newFamilyName)
253 *stringStorage++ = qbswap<quint16>(quint16(ch.unicode()));
254
255 for (QChar ch : regularString)
256 *stringStorage++ = qbswap<quint16>(quint16(ch.unicode()));
257 }
258
259 quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data());
260 quint32 *tableEnd = reinterpret_cast<quint32 *>(newNameTable.data() + fullSize);
261
262 quint32 checkSum = 0;
263 while (p < tableEnd)
264 checkSum += qFromBigEndian<quint32>(*(p++));
265
266 nameTableDirectoryEntry->checkSum = qbswap<quint32>(checkSum);
267 nameTableDirectoryEntry->offset = qbswap<quint32>(m_fontData.size());
268 nameTableDirectoryEntry->length = qbswap<quint32>(fullSize);
269
270 m_fontData.append(newNameTable);
271
272 return oldFamilyName;
273}
274
275#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d)
276
277namespace {
278 class DirectWriteFontFileStream: public IDWriteFontFileStream
279 {
280 Q_DISABLE_COPY(DirectWriteFontFileStream)
281 public:
282 DirectWriteFontFileStream(const QByteArray &fontData)
283 : m_fontData(fontData)
284 , m_referenceCount(0)
285 {
286 }
287 virtual ~DirectWriteFontFileStream()
288 {
289 }
290
291 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object) override;
292 ULONG STDMETHODCALLTYPE AddRef() override;
293 ULONG STDMETHODCALLTYPE Release() override;
294
295 HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset,
296 UINT64 fragmentSize, OUT void **fragmentContext) override;
297 void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext) override;
298 HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize) override;
299 HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime) override;
300
301 private:
302 QByteArray m_fontData;
303 ULONG m_referenceCount;
304 };
305
306 HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::QueryInterface(REFIID iid, void **object)
307 {
308 if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) {
309 *object = this;
310 AddRef();
311 return S_OK;
312 } else {
313 *object = NULL;
314 return E_NOINTERFACE;
315 }
316 }
317
318 ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::AddRef()
319 {
320 return InterlockedIncrement(&m_referenceCount);
321 }
322
323 ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::Release()
324 {
325 ULONG newCount = InterlockedDecrement(&m_referenceCount);
326 if (newCount == 0)
327 delete this;
328 return newCount;
329 }
330
331 HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::ReadFileFragment(
332 const void **fragmentStart,
333 UINT64 fileOffset,
334 UINT64 fragmentSize,
335 OUT void **fragmentContext)
336 {
337 *fragmentContext = NULL;
338 const UINT64 size = UINT64(m_fontData.size());
339 if (fileOffset <= size && fragmentSize <= size - fileOffset) {
340 *fragmentStart = m_fontData.data() + fileOffset;
341 return S_OK;
342 } else {
343 *fragmentStart = NULL;
344 return E_FAIL;
345 }
346 }
347
348 void STDMETHODCALLTYPE DirectWriteFontFileStream::ReleaseFileFragment(void *)
349 {
350 }
351
352 HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetFileSize(UINT64 *fileSize)
353 {
354 *fileSize = m_fontData.size();
355 return S_OK;
356 }
357
358 HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
359 {
360 *lastWriteTime = 0;
361 return E_NOTIMPL;
362 }
363
364 class DirectWriteFontFileLoader: public IDWriteLocalFontFileLoader
365 {
366 public:
367 DirectWriteFontFileLoader() : m_referenceCount(0) {}
368 virtual ~DirectWriteFontFileLoader()
369 {
370 }
371
372 inline void addKey(const QByteArray &fontData, const QString &filename)
373 {
374 if (!m_fontDatas.contains(fontData.data()))
375 m_fontDatas.insert(fontData.data(), {fontData, filename});
376 }
377
378 HRESULT STDMETHODCALLTYPE GetFilePathLengthFromKey(void const* fontFileReferenceKey,
379 UINT32 fontFileReferenceKeySize,
380 UINT32* filePathLength) override
381 {
382 Q_UNUSED(fontFileReferenceKeySize);
383 const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey);
384 auto it = m_fontDatas.constFind(key);
385 if (it == m_fontDatas.constEnd())
386 return E_FAIL;
387
388 *filePathLength = it.value().second.size();
389 return 0;
390 }
391
392 HRESULT STDMETHODCALLTYPE GetFilePathFromKey(void const* fontFileReferenceKey,
393 UINT32 fontFileReferenceKeySize,
394 WCHAR* filePath,
395 UINT32 filePathSize) override
396 {
397 Q_UNUSED(fontFileReferenceKeySize);
398 const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey);
399 const auto it = m_fontDatas.constFind(key);
400 if (it == m_fontDatas.constEnd())
401 return E_FAIL;
402
403 const QString &path = it.value().second;
404 if (filePathSize < path.size() + 1)
405 return E_FAIL;
406
407 const qsizetype length = path.toWCharArray(filePath);
408 filePath[length] = '\0';
409
410 return 0;
411 }
412
413 HRESULT STDMETHODCALLTYPE GetLastWriteTimeFromKey(void const* fontFileReferenceKey,
414 UINT32 fontFileReferenceKeySize,
415 FILETIME* lastWriteTime) override
416 {
417 Q_UNUSED(fontFileReferenceKey);
418 Q_UNUSED(fontFileReferenceKeySize);
419 Q_UNUSED(lastWriteTime);
420 // We never call this, so just fail
421 return E_FAIL;
422 }
423
424
425 inline void removeKey(const void *key)
426 {
427 m_fontDatas.remove(key);
428 }
429
430 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object) override;
431 ULONG STDMETHODCALLTYPE AddRef() override;
432 ULONG STDMETHODCALLTYPE Release() override;
433
434 HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey,
435 UINT32 fontFileReferenceKeySize,
436 OUT IDWriteFontFileStream **fontFileStream) override;
437
438 void clear()
439 {
440 m_fontDatas.clear();
441 }
442
443 private:
444 ULONG m_referenceCount;
445 QHash<const void *, std::pair<QByteArray, QString> > m_fontDatas;
446 };
447
448 HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid,
449 void **object)
450 {
451 if (iid == IID_IUnknown
452 || iid == __uuidof(IDWriteFontFileLoader)
453 || iid == __uuidof(IDWriteLocalFontFileLoader)) {
454 *object = this;
455 AddRef();
456 return S_OK;
457 } else {
458 *object = NULL;
459 return E_NOINTERFACE;
460 }
461 }
462
463 ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::AddRef()
464 {
465 return InterlockedIncrement(&m_referenceCount);
466 }
467
468 ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::Release()
469 {
470 ULONG newCount = InterlockedDecrement(&m_referenceCount);
471 if (newCount == 0)
472 delete this;
473 return newCount;
474 }
475
476 HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::CreateStreamFromKey(
477 void const *fontFileReferenceKey,
478 UINT32 fontFileReferenceKeySize,
479 IDWriteFontFileStream **fontFileStream)
480 {
481 Q_UNUSED(fontFileReferenceKeySize);
482
483 if (fontFileReferenceKeySize != sizeof(const void *)) {
484 qWarning("%s: Wrong key size", __FUNCTION__);
485 return E_FAIL;
486 }
487
488 const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey);
489 *fontFileStream = NULL;
490 auto it = m_fontDatas.constFind(key);
491 if (it == m_fontDatas.constEnd())
492 return E_FAIL;
493
494 QByteArray fontData = it.value().first;
495 DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData);
496 stream->AddRef();
497 *fontFileStream = stream;
498
499 return S_OK;
500 }
501
502} // Anonymous namespace
503
504class QCustomFontFileLoader
505{
506public:
507 QCustomFontFileLoader(IDWriteFactory *factory)
508 {
509 m_directWriteFactory = factory;
510
511 if (m_directWriteFactory) {
512 m_directWriteFactory->AddRef();
513
514 m_directWriteFontFileLoader = new DirectWriteFontFileLoader();
515 m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader);
516 }
517 }
518
519 ~QCustomFontFileLoader()
520 {
521 clear();
522
523 if (m_directWriteFactory != nullptr && m_directWriteFontFileLoader != nullptr)
524 m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader);
525
526 if (m_directWriteFactory != nullptr)
527 m_directWriteFactory->Release();
528 }
529
530 void addKey(const QByteArray &fontData, const QString &filename)
531 {
532 if (m_directWriteFontFileLoader != nullptr)
533 m_directWriteFontFileLoader->addKey(fontData, filename);
534 }
535
536 void removeKey(const void *key)
537 {
538 if (m_directWriteFontFileLoader != nullptr)
539 m_directWriteFontFileLoader->removeKey(key);
540 }
541
542 IDWriteFontFileLoader *loader() const
543 {
544 return m_directWriteFontFileLoader;
545 }
546
547 void clear()
548 {
549 if (m_directWriteFontFileLoader != nullptr)
550 m_directWriteFontFileLoader->clear();
551 }
552
553private:
554 IDWriteFactory *m_directWriteFactory = nullptr;
555 DirectWriteFontFileLoader *m_directWriteFontFileLoader = nullptr;
556};
557
558
559#endif // directwrite && direct2d
560
561
562QWindowsFontEngineData::~QWindowsFontEngineData()
563{
564 if (hdc)
565 DeleteDC(hdc);
566
567#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d)
568 if (directWriteGdiInterop)
569 directWriteGdiInterop->Release();
570 if (directWriteFactory)
571 directWriteFactory->Release();
572#endif
573}
574
575QWindowsFontDatabaseBase::QWindowsFontDatabaseBase()
576{
577}
578
579QWindowsFontDatabaseBase::~QWindowsFontDatabaseBase()
580{
581}
582
585Q_GLOBAL_STATIC(FontEngineThreadLocalData, fontEngineThreadLocalData)
586
587QSharedPointer<QWindowsFontEngineData> QWindowsFontDatabaseBase::data()
588{
589 FontEngineThreadLocalData *data = fontEngineThreadLocalData();
590 if (!data->hasLocalData())
591 data->setLocalData(QSharedPointer<QWindowsFontEngineData>::create());
592
593 if (!init(data->localData()))
594 qCWarning(lcQpaFonts) << "Cannot initialize common font database data";
595
596 return data->localData();
597}
598
599bool QWindowsFontDatabaseBase::init(QSharedPointer<QWindowsFontEngineData> d)
600{
601#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d)
602 if (!d->directWriteFactory) {
603 createDirectWriteFactory(&d->directWriteFactory);
604 if (!d->directWriteFactory)
605 return false;
606 }
607 if (!d->directWriteGdiInterop) {
608 const HRESULT hr = d->directWriteFactory->GetGdiInterop(&d->directWriteGdiInterop);
609 if (FAILED(hr)) {
610 qErrnoWarning("%s: GetGdiInterop failed", __FUNCTION__);
611 return false;
612 }
613 }
614#else
615 Q_UNUSED(d);
616#endif // directwrite && direct2d
617 return true;
618}
619
620#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d)
621void QWindowsFontDatabaseBase::createDirectWriteFactory(IDWriteFactory **factory)
622{
623 *factory = nullptr;
624 IUnknown *result = nullptr;
625
626# if QT_CONFIG(directwrite3)
627 qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory6";
628 DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory6), &result);
629
630 if (result == nullptr) {
631 qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory5";
632 DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory5), &result);
633 }
634
635 if (result == nullptr) {
636 qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory4";
637 DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory4), &result);
638 }
639
640 if (result == nullptr) {
641 qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory3";
642 DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result);
643 }
644# endif
645
646 if (result == nullptr) {
647 qCDebug(lcQpaFonts) << "Trying to create IDWriteFactory2";
648 DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result);
649 }
650
651 if (result == nullptr) {
652 qCDebug(lcQpaFonts) << "Trying to create plain IDWriteFactory";
653 if (FAILED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &result))) {
654 qErrnoWarning("DWriteCreateFactory failed");
655 return;
656 }
657 }
658
659 *factory = static_cast<IDWriteFactory *>(result);
660}
661#endif // directwrite && direct2d
662
663int QWindowsFontDatabaseBase::defaultVerticalDPI()
664{
665 return 96;
666}
667
668LOGFONT QWindowsFontDatabaseBase::fontDefToLOGFONT(const QFontDef &request, const QString &faceName)
669{
670 LOGFONT lf;
671 memset(&lf, 0, sizeof(LOGFONT));
672
673 lf.lfHeight = -qRound(request.pixelSize);
674 lf.lfWidth = 0;
675 lf.lfEscapement = 0;
676 lf.lfOrientation = 0;
677 if (request.weight == QFont::Normal)
678 lf.lfWeight = FW_DONTCARE;
679 else
680 lf.lfWeight = request.weight;
681 lf.lfItalic = request.style != QFont::StyleNormal;
682 lf.lfCharSet = DEFAULT_CHARSET;
683
684 int strat = OUT_DEFAULT_PRECIS;
685 if (request.styleStrategy & QFont::PreferBitmap) {
686 strat = OUT_RASTER_PRECIS;
687 } else if (request.styleStrategy & QFont::PreferDevice) {
688 strat = OUT_DEVICE_PRECIS;
689 } else if (request.styleStrategy & QFont::PreferOutline) {
690 strat = OUT_OUTLINE_PRECIS;
691 } else if (request.styleStrategy & QFont::ForceOutline) {
692 strat = OUT_TT_ONLY_PRECIS;
693 }
694
695 lf.lfOutPrecision = strat;
696
697 int qual = DEFAULT_QUALITY;
698
699 if (request.styleStrategy & QFont::PreferMatch)
700 qual = DRAFT_QUALITY;
701 else if (request.styleStrategy & QFont::PreferQuality)
702 qual = PROOF_QUALITY;
703
704 if (request.styleStrategy & QFont::PreferAntialias) {
705 qual = (request.styleStrategy & QFont::NoSubpixelAntialias) == 0
706 ? CLEARTYPE_QUALITY : ANTIALIASED_QUALITY;
707 } else if (request.styleStrategy & QFont::NoAntialias) {
708 qual = NONANTIALIASED_QUALITY;
709 } else if ((request.styleStrategy & QFont::NoSubpixelAntialias) && data()->clearTypeEnabled) {
710 qual = ANTIALIASED_QUALITY;
711 }
712
713 lf.lfQuality = qual;
714
715 lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
716
717 int hint = FF_DONTCARE;
718 switch (request.styleHint) {
719 case QFont::Helvetica:
720 hint = FF_SWISS;
721 break;
722 case QFont::Times:
723 hint = FF_ROMAN;
724 break;
725 case QFont::Courier:
726 hint = FF_MODERN;
727 break;
728 case QFont::OldEnglish:
729 hint = FF_DECORATIVE;
730 break;
731 case QFont::System:
732 hint = FF_MODERN;
733 break;
734 default:
735 break;
736 }
737
738 lf.lfPitchAndFamily = DEFAULT_PITCH | hint;
739
740 QString fam = faceName;
741 if (fam.isEmpty())
742 fam = request.family();
743 if (fam.isEmpty())
744 return lf;
745 if (Q_UNLIKELY(fam.size() >= LF_FACESIZE)) {
746 qCritical("%s: Family name '%s' is too long.", __FUNCTION__, qPrintable(fam));
747 fam.truncate(LF_FACESIZE - 1);
748 }
749
750 memcpy(lf.lfFaceName, fam.utf16(), fam.size() * sizeof(wchar_t));
751
752 return lf;
753}
754
755QFont QWindowsFontDatabaseBase::LOGFONT_to_QFont(const LOGFONT& logFont, int verticalDPI_In)
756{
757 if (verticalDPI_In <= 0)
758 verticalDPI_In = defaultVerticalDPI();
759 QFont qFont(QString::fromWCharArray(logFont.lfFaceName));
760 qFont.setItalic(logFont.lfItalic);
761 if (logFont.lfWeight != FW_DONTCARE)
762 qFont.setWeight(QFont::Weight(logFont.lfWeight));
763 const qreal logFontHeight = qAbs(logFont.lfHeight);
764 qFont.setPointSizeF(logFontHeight * 72.0 / qreal(verticalDPI_In));
765 qFont.setUnderline(logFont.lfUnderline);
766 qFont.setOverline(false);
767 qFont.setStrikeOut(logFont.lfStrikeOut);
768 return qFont;
769}
770
771// ### fixme Qt 6 (QTBUG-58610): See comment at QWindowsFontDatabase::systemDefaultFont()
772HFONT QWindowsFontDatabaseBase::systemFont()
773{
774 static const auto stock_sysfont =
775 reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT));
776 return stock_sysfont;
777}
778
779QFont QWindowsFontDatabaseBase::systemDefaultFont()
780{
781 // Qt 6: Obtain default GUI font (typically "Segoe UI, 9pt", see QTBUG-58610)
782 NONCLIENTMETRICS ncm = {};
783 ncm.cbSize = sizeof(ncm);
784 SystemParametersInfoForDpi(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0, defaultVerticalDPI());
785 const QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont);
786 qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont;
787 return systemFont;
788}
789
790void QWindowsFontDatabaseBase::invalidate()
791{
792#if QT_CONFIG(directwrite)
793 m_fontFileLoader.reset(nullptr);
794#endif
795}
796
797#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d)
798IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArray &fontData)
799{
800 QList<IDWriteFontFace *> faces = createDirectWriteFaces(fontData, QString{}, false);
801 Q_ASSERT(faces.size() <= 1);
802
803 return faces.isEmpty() ? nullptr : faces.first();
804}
805
806QList<IDWriteFontFace *> QWindowsFontDatabaseBase::createDirectWriteFaces(const QByteArray &fontData,
807 const QString &filename,
808 bool queryVariations) const
809{
810 QList<IDWriteFontFace *> ret;
811 QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
812 if (fontEngineData->directWriteFactory == nullptr) {
813 qCWarning(lcQpaFonts) << "DirectWrite factory not created in QWindowsFontDatabaseBase::createDirectWriteFace()";
814 return ret;
815 }
816
817 if (m_fontFileLoader == nullptr)
818 m_fontFileLoader.reset(new QCustomFontFileLoader(fontEngineData->directWriteFactory));
819
820 m_fontFileLoader->addKey(fontData, filename);
821
822 IDWriteFontFile *fontFile = nullptr;
823 const void *key = fontData.data();
824
825 HRESULT hres = fontEngineData->directWriteFactory->CreateCustomFontFileReference(&key,
826 sizeof(void *),
827 m_fontFileLoader->loader(),
828 &fontFile);
829 if (FAILED(hres)) {
830 qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__);
831 return ret;
832 }
833
834 BOOL isSupportedFontType;
835 DWRITE_FONT_FILE_TYPE fontFileType;
836 DWRITE_FONT_FACE_TYPE fontFaceType;
837 UINT32 numberOfFaces;
838 if (FAILED(fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces))
839 || !isSupportedFontType) {
840 fontFile->Release();
841 return ret;
842 }
843
844#if QT_CONFIG(directwrite3)
845 IDWriteFactory5 *factory5 = nullptr;
846 if (queryVariations && SUCCEEDED(fontEngineData->directWriteFactory->QueryInterface(__uuidof(IDWriteFactory5),
847 reinterpret_cast<void **>(&factory5)))) {
848
849 IDWriteFontSetBuilder1 *builder;
850 if (SUCCEEDED(factory5->CreateFontSetBuilder(&builder))) {
851 if (SUCCEEDED(builder->AddFontFile(fontFile))) {
852 IDWriteFontSet *fontSet;
853 if (SUCCEEDED(builder->CreateFontSet(&fontSet))) {
854 int count = fontSet->GetFontCount();
855 qCDebug(lcQpaFonts) << "Found" << count << "variations in font file";
856 for (int i = 0; i < count; ++i) {
857 IDWriteFontFaceReference *ref;
858 if (SUCCEEDED(fontSet->GetFontFaceReference(i, &ref))) {
859 IDWriteFontFace3 *face;
860 if (SUCCEEDED(ref->CreateFontFace(&face))) {
861 ret.append(face);
862 }
863 ref->Release();
864 }
865 }
866 fontSet->Release();
867 }
868 }
869
870 builder->Release();
871 }
872
873 factory5->Release();
874 }
875#else
876 Q_UNUSED(queryVariations);
877#endif
878
879 // ### Currently no support for .ttc, but we could easily return a list here.
880 if (ret.isEmpty()) {
881 IDWriteFontFace *directWriteFontFace = nullptr;
882 hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType,
883 1,
884 &fontFile,
885 0,
886 DWRITE_FONT_SIMULATIONS_NONE,
887 &directWriteFontFace);
888 if (FAILED(hres)) {
889 qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__);
890 fontFile->Release();
891 return ret;
892 } else {
893 ret.append(directWriteFontFace);
894 }
895 }
896
897 fontFile->Release();
898
899 return ret;
900}
901#endif // directwrite && direct2d
902
903QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QFontDef &fontDef, void *handle)
904{
905 // This function was apparently not used before, and probably isn't now either,
906 // call the base implementation which just prints that it's not supported.
907 return QPlatformFontDatabase::fontEngine(fontDef, handle);
908}
909
910QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
911{
912 QFontEngine *fontEngine = nullptr;
913
914#if QT_CONFIG(directwrite) && QT_CONFIG(direct2d)
915 QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
916 if (fontEngineData->directWriteFactory == nullptr)
917 return nullptr;
918
919 IDWriteFontFace * directWriteFontFace = createDirectWriteFace(fontData);
920 if (directWriteFontFace == nullptr)
921 return nullptr;
922
923 fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace,
924 pixelSize,
925 fontEngineData);
926
927 // Get font family from font data
928 EmbeddedFont font(fontData);
929 font.updateFromOS2Table(fontEngine);
930 fontEngine->fontDef.families = QStringList(font.familyName());
931 fontEngine->fontDef.hintingPreference = hintingPreference;
932
933 directWriteFontFace->Release();
934#else // directwrite && direct2d
935 Q_UNUSED(fontData);
936 Q_UNUSED(pixelSize);
937 Q_UNUSED(hintingPreference);
938#endif
939
940 return fontEngine;
941}
942
943QStringList QWindowsFontDatabaseBase::familiesForScript(QFontDatabasePrivate::ExtendedScript script)
944{
945 if (script == QFontDatabasePrivate::Script_Emoji)
946 return QStringList{} << QStringLiteral("Segoe UI Emoji");
947 else
948 return QStringList{};
949}
950
951QString QWindowsFontDatabaseBase::familyForStyleHint(QFont::StyleHint styleHint)
952{
953 switch (styleHint) {
954 case QFont::Times:
955 return QStringLiteral("Times New Roman");
956 case QFont::Courier:
957 return QStringLiteral("Courier New");
958 case QFont::Monospace:
959 return QStringLiteral("Courier New");
960 case QFont::Cursive:
961 return QStringLiteral("Comic Sans MS");
962 case QFont::Fantasy:
963 return QStringLiteral("Impact");
964 case QFont::Decorative:
965 return QStringLiteral("Old English");
966 case QFont::Helvetica:
967 return QStringLiteral("Arial");
968 case QFont::System:
969 default:
970 break;
971 }
972 return QStringLiteral("Tahoma");
973}
974
975// Creation functions
976
977static const char *other_tryFonts[] = {
978 "Arial",
979 "MS UI Gothic",
980 "Gulim",
981 "SimSun",
982 "PMingLiU",
983 "Arial Unicode MS",
984 0
985};
986
987static const char *jp_tryFonts [] = {
988 "Yu Gothic UI",
989 "MS UI Gothic",
990 "Arial",
991 "Gulim",
992 "SimSun",
993 "PMingLiU",
994 "Arial Unicode MS",
995 0
996};
997
998static const char *ch_CN_tryFonts [] = {
999 "SimSun",
1000 "Arial",
1001 "PMingLiU",
1002 "Gulim",
1003 "MS UI Gothic",
1004 "Arial Unicode MS",
1005 0
1006};
1007
1008static const char *ch_TW_tryFonts [] = {
1009 "PMingLiU",
1010 "Arial",
1011 "SimSun",
1012 "Gulim",
1013 "MS UI Gothic",
1014 "Arial Unicode MS",
1015 0
1016};
1017
1018static const char *kr_tryFonts[] = {
1019 "Malgun Gothic",
1020 "Gulim",
1021 "Arial",
1022 "PMingLiU",
1023 "SimSun",
1024 "MS UI Gothic",
1025 "Arial Unicode MS",
1026 0
1027};
1028
1029static const char **tryFonts = nullptr;
1030
1031QStringList QWindowsFontDatabaseBase::extraTryFontsForFamily(const QString &family)
1032{
1033 QStringList result;
1034 if (!QFontDatabase::writingSystems(family).contains(QFontDatabase::Symbol)) {
1035 if (!tryFonts) {
1036 LANGID lid = GetUserDefaultLangID();
1037 switch (lid&0xff) {
1038 case LANG_CHINESE: // Chinese
1039 if ( lid == 0x0804 || lid == 0x1004) // China mainland and Singapore
1040 tryFonts = ch_CN_tryFonts;
1041 else
1042 tryFonts = ch_TW_tryFonts; // Taiwan, Hong Kong and Macau
1043 break;
1044 case LANG_JAPANESE:
1045 tryFonts = jp_tryFonts;
1046 break;
1047 case LANG_KOREAN:
1048 tryFonts = kr_tryFonts;
1049 break;
1050 default:
1051 tryFonts = other_tryFonts;
1052 break;
1053 }
1054 }
1055 const QStringList families = QFontDatabase::families();
1056 const char **tf = tryFonts;
1057 while (tf && *tf) {
1058 // QTBUG-31689, family might be an English alias for a localized font name.
1059 const QString family = QString::fromLatin1(*tf);
1060 if (families.contains(family) || QFontDatabase::hasFamily(family))
1061 result << family;
1062 ++tf;
1063 }
1064 }
1065 result.append(QStringLiteral("Segoe UI Emoji"));
1066 result.append(QStringLiteral("Segoe UI Symbol"));
1067 return result;
1068}
1069
1070QFontDef QWindowsFontDatabaseBase::sanitizeRequest(QFontDef request) const
1071{
1072 QFontDef req = request;
1073 const QString fam = request.families.front();
1074 if (fam.isEmpty())
1075 req.families[0] = QStringLiteral("MS Sans Serif");
1076
1077 if (fam == "MS Sans Serif"_L1) {
1078 int height = -qRound(request.pixelSize);
1079 // MS Sans Serif has bearing problems in italic, and does not scale
1080 if (request.style == QFont::StyleItalic || (height > 18 && height != 24))
1081 req.families[0] = QStringLiteral("Arial");
1082 }
1083
1084 if (!(request.styleStrategy & QFont::StyleStrategy::PreferBitmap) && fam == u"Courier")
1085 req.families[0] = QStringLiteral("Courier New");
1086 return req;
1087}
1088
1089QT_END_NAMESPACE
static const char * ch_CN_tryFonts[]
static const char * jp_tryFonts[]
static const char * kr_tryFonts[]
static const char ** tryFonts
QThreadStorage< QWindowsFontEngineDataPtr > FontEngineThreadLocalData
static const char * ch_TW_tryFonts[]
QSharedPointer< QWindowsFontEngineData > QWindowsFontEngineDataPtr