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