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