summaryrefslogtreecommitdiffstats
path: root/src/platformsupport/fontdatabases/windows/qwindowsfontdatabasebase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/platformsupport/fontdatabases/windows/qwindowsfontdatabasebase.cpp')
-rw-r--r--src/platformsupport/fontdatabases/windows/qwindowsfontdatabasebase.cpp869
1 files changed, 869 insertions, 0 deletions
diff --git a/src/platformsupport/fontdatabases/windows/qwindowsfontdatabasebase.cpp b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabasebase.cpp
new file mode 100644
index 0000000000..5b8707cdec
--- /dev/null
+++ b/src/platformsupport/fontdatabases/windows/qwindowsfontdatabasebase.cpp
@@ -0,0 +1,869 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the plugins of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsfontdatabasebase_p.h"
+#include "qwindowsfontdatabase_p.h"
+
+#include <QtCore/private/qsystemlibrary_p.h>
+#include <QtCore/QThreadStorage>
+#include <QtCore/QtEndian>
+
+#if !defined(QT_NO_DIRECTWRITE)
+# if defined(QT_USE_DIRECTWRITE3)
+# include <dwrite_3.h>
+# elif defined(QT_USE_DIRECTWRITE2)
+# include <dwrite_2.h>
+# else
+# include <dwrite.h>
+# endif
+# include <d2d1.h>
+# include "qwindowsfontenginedirectwrite_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+// Helper classes for creating font engines directly from font data
+namespace {
+
+# pragma pack(1)
+
+ // Common structure for all formats of the "name" table
+ struct NameTable
+ {
+ quint16 format;
+ quint16 count;
+ quint16 stringOffset;
+ };
+
+ struct NameRecord
+ {
+ quint16 platformID;
+ quint16 encodingID;
+ quint16 languageID;
+ quint16 nameID;
+ quint16 length;
+ quint16 offset;
+ };
+
+ struct OffsetSubTable
+ {
+ quint32 scalerType;
+ quint16 numTables;
+ quint16 searchRange;
+ quint16 entrySelector;
+ quint16 rangeShift;
+ };
+
+ struct TableDirectory : public QWindowsFontDatabaseBase::FontTable
+ {
+ quint32 identifier;
+ quint32 checkSum;
+ quint32 offset;
+ quint32 length;
+ };
+
+ struct OS2Table
+ {
+ quint16 version;
+ qint16 avgCharWidth;
+ quint16 weightClass;
+ quint16 widthClass;
+ quint16 type;
+ qint16 subscriptXSize;
+ qint16 subscriptYSize;
+ qint16 subscriptXOffset;
+ qint16 subscriptYOffset;
+ qint16 superscriptXSize;
+ qint16 superscriptYSize;
+ qint16 superscriptXOffset;
+ qint16 superscriptYOffset;
+ qint16 strikeOutSize;
+ qint16 strikeOutPosition;
+ qint16 familyClass;
+ quint8 panose[10];
+ quint32 unicodeRanges[4];
+ quint8 vendorID[4];
+ quint16 selection;
+ quint16 firstCharIndex;
+ quint16 lastCharIndex;
+ qint16 typoAscender;
+ qint16 typoDescender;
+ qint16 typoLineGap;
+ quint16 winAscent;
+ quint16 winDescent;
+ quint32 codepageRanges[2];
+ qint16 height;
+ qint16 capHeight;
+ quint16 defaultChar;
+ quint16 breakChar;
+ quint16 maxContext;
+ };
+
+# pragma pack()
+
+} // Anonymous namespace
+
+QWindowsFontDatabaseBase::FontTable *QWindowsFontDatabaseBase::EmbeddedFont::tableDirectoryEntry(const QByteArray &tagName)
+{
+ Q_ASSERT(tagName.size() == 4);
+ quint32 tagId = *(reinterpret_cast<const quint32 *>(tagName.constData()));
+ const size_t fontDataSize = m_fontData.size();
+ if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable)))
+ return nullptr;
+
+ OffsetSubTable *offsetSubTable = reinterpret_cast<OffsetSubTable *>(m_fontData.data());
+ TableDirectory *tableDirectory = reinterpret_cast<TableDirectory *>(offsetSubTable + 1);
+
+ const size_t tableCount = qFromBigEndian<quint16>(offsetSubTable->numTables);
+ if (Q_UNLIKELY(fontDataSize < sizeof(OffsetSubTable) + sizeof(TableDirectory) * tableCount))
+ return nullptr;
+
+ TableDirectory *tableDirectoryEnd = tableDirectory + tableCount;
+ for (TableDirectory *entry = tableDirectory; entry < tableDirectoryEnd; ++entry) {
+ if (entry->identifier == tagId)
+ return entry;
+ }
+
+ return nullptr;
+}
+
+QString QWindowsFontDatabaseBase::EmbeddedFont::familyName(QWindowsFontDatabaseBase::FontTable *directoryEntry)
+{
+ QString name;
+
+ TableDirectory *nameTableDirectoryEntry = static_cast<TableDirectory *>(directoryEntry);
+ if (nameTableDirectoryEntry == nullptr)
+ nameTableDirectoryEntry = static_cast<TableDirectory *>(tableDirectoryEntry("name"));
+
+ if (nameTableDirectoryEntry != nullptr) {
+ quint32 offset = qFromBigEndian<quint32>(nameTableDirectoryEntry->offset);
+ if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameTable)))
+ return QString();
+
+ NameTable *nameTable = reinterpret_cast<NameTable *>(m_fontData.data() + offset);
+ NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1);
+
+ quint16 nameTableCount = qFromBigEndian<quint16>(nameTable->count);
+ if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + sizeof(NameRecord) * nameTableCount))
+ return QString();
+
+ for (int i = 0; i < nameTableCount; ++i, ++nameRecord) {
+ if (qFromBigEndian<quint16>(nameRecord->nameID) == 1
+ && qFromBigEndian<quint16>(nameRecord->platformID) == 3 // Windows
+ && qFromBigEndian<quint16>(nameRecord->languageID) == 0x0409) { // US English
+ quint16 stringOffset = qFromBigEndian<quint16>(nameTable->stringOffset);
+ quint16 nameOffset = qFromBigEndian<quint16>(nameRecord->offset);
+ quint16 nameLength = qFromBigEndian<quint16>(nameRecord->length);
+
+ if (Q_UNLIKELY(quint32(m_fontData.size()) < offset + stringOffset + nameOffset + nameLength))
+ return QString();
+
+ const void *ptr = reinterpret_cast<const quint8 *>(nameTable)
+ + stringOffset
+ + nameOffset;
+
+ const quint16 *s = reinterpret_cast<const quint16 *>(ptr);
+ const quint16 *e = s + nameLength / sizeof(quint16);
+ while (s != e)
+ name += QChar( qFromBigEndian<quint16>(*s++));
+ break;
+ }
+ }
+ }
+
+ return name;
+}
+
+void QWindowsFontDatabaseBase::EmbeddedFont::updateFromOS2Table(QFontEngine *fontEngine)
+{
+ TableDirectory *os2TableEntry = static_cast<TableDirectory *>(tableDirectoryEntry("OS/2"));
+ if (os2TableEntry != nullptr) {
+ const OS2Table *os2Table =
+ reinterpret_cast<const OS2Table *>(m_fontData.constData()
+ + qFromBigEndian<quint32>(os2TableEntry->offset));
+
+ bool italic = qFromBigEndian<quint16>(os2Table->selection) & (1 << 0);
+ bool oblique = qFromBigEndian<quint16>(os2Table->selection) & (1 << 9);
+
+ if (italic)
+ fontEngine->fontDef.style = QFont::StyleItalic;
+ else if (oblique)
+ fontEngine->fontDef.style = QFont::StyleOblique;
+ else
+ fontEngine->fontDef.style = QFont::StyleNormal;
+
+ fontEngine->fontDef.weight = QPlatformFontDatabase::weightFromInteger(qFromBigEndian<quint16>(os2Table->weightClass));
+ }
+}
+
+QString QWindowsFontDatabaseBase::EmbeddedFont::changeFamilyName(const QString &newFamilyName)
+{
+ TableDirectory *nameTableDirectoryEntry = static_cast<TableDirectory *>(tableDirectoryEntry("name"));
+ if (nameTableDirectoryEntry == nullptr)
+ return QString();
+
+ QString oldFamilyName = familyName(nameTableDirectoryEntry);
+
+ // Reserve size for name table header, five required name records and string
+ const int requiredRecordCount = 5;
+ quint16 nameIds[requiredRecordCount] = { 1, 2, 3, 4, 6 };
+
+ int sizeOfHeader = sizeof(NameTable) + sizeof(NameRecord) * requiredRecordCount;
+ int newFamilyNameSize = newFamilyName.size() * int(sizeof(quint16));
+
+ const QString regularString = QString::fromLatin1("Regular");
+ int regularStringSize = regularString.size() * int(sizeof(quint16));
+
+ // Align table size of table to 32 bits (pad with 0)
+ int fullSize = ((sizeOfHeader + newFamilyNameSize + regularStringSize) & ~3) + 4;
+
+ QByteArray newNameTable(fullSize, char(0));
+
+ {
+ NameTable *nameTable = reinterpret_cast<NameTable *>(newNameTable.data());
+ nameTable->count = qbswap<quint16>(requiredRecordCount);
+ nameTable->stringOffset = qbswap<quint16>(sizeOfHeader);
+
+ NameRecord *nameRecord = reinterpret_cast<NameRecord *>(nameTable + 1);
+ for (int i = 0; i < requiredRecordCount; ++i, nameRecord++) {
+ nameRecord->nameID = qbswap<quint16>(nameIds[i]);
+ nameRecord->encodingID = qbswap<quint16>(1);
+ nameRecord->languageID = qbswap<quint16>(0x0409);
+ nameRecord->platformID = qbswap<quint16>(3);
+ nameRecord->length = qbswap<quint16>(newFamilyNameSize);
+
+ // Special case for sub-family
+ if (nameIds[i] == 4) {
+ nameRecord->offset = qbswap<quint16>(newFamilyNameSize);
+ nameRecord->length = qbswap<quint16>(regularStringSize);
+ }
+ }
+
+ // nameRecord now points to string data
+ quint16 *stringStorage = reinterpret_cast<quint16 *>(nameRecord);
+ const quint16 *sourceString = newFamilyName.utf16();
+ for (int i = 0; i < newFamilyName.size(); ++i)
+ stringStorage[i] = qbswap<quint16>(sourceString[i]);
+ stringStorage += newFamilyName.size();
+
+ sourceString = regularString.utf16();
+ for (int i = 0; i < regularString.size(); ++i)
+ stringStorage[i] = qbswap<quint16>(sourceString[i]);
+ }
+
+ quint32 *p = reinterpret_cast<quint32 *>(newNameTable.data());
+ quint32 *tableEnd = reinterpret_cast<quint32 *>(newNameTable.data() + fullSize);
+
+ quint32 checkSum = 0;
+ while (p < tableEnd)
+ checkSum += qFromBigEndian<quint32>(*(p++));
+
+ nameTableDirectoryEntry->checkSum = qbswap<quint32>(checkSum);
+ nameTableDirectoryEntry->offset = qbswap<quint32>(m_fontData.size());
+ nameTableDirectoryEntry->length = qbswap<quint32>(fullSize);
+
+ m_fontData.append(newNameTable);
+
+ return oldFamilyName;
+}
+
+#if !defined(QT_NO_DIRECTWRITE)
+
+namespace {
+ class DirectWriteFontFileStream: public IDWriteFontFileStream
+ {
+ Q_DISABLE_COPY(DirectWriteFontFileStream)
+ public:
+ DirectWriteFontFileStream(const QByteArray &fontData)
+ : m_fontData(fontData)
+ , m_referenceCount(0)
+ {
+ }
+ virtual ~DirectWriteFontFileStream()
+ {
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ HRESULT STDMETHODCALLTYPE ReadFileFragment(const void **fragmentStart, UINT64 fileOffset,
+ UINT64 fragmentSize, OUT void **fragmentContext);
+ void STDMETHODCALLTYPE ReleaseFileFragment(void *fragmentContext);
+ HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64 *fileSize);
+ HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64 *lastWriteTime);
+
+ private:
+ QByteArray m_fontData;
+ ULONG m_referenceCount;
+ };
+
+ HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::QueryInterface(REFIID iid, void **object)
+ {
+ if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) {
+ *object = this;
+ AddRef();
+ return S_OK;
+ } else {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ }
+
+ ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::AddRef()
+ {
+ return InterlockedIncrement(&m_referenceCount);
+ }
+
+ ULONG STDMETHODCALLTYPE DirectWriteFontFileStream::Release()
+ {
+ ULONG newCount = InterlockedDecrement(&m_referenceCount);
+ if (newCount == 0)
+ delete this;
+ return newCount;
+ }
+
+ HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::ReadFileFragment(
+ const void **fragmentStart,
+ UINT64 fileOffset,
+ UINT64 fragmentSize,
+ OUT void **fragmentContext)
+ {
+ *fragmentContext = NULL;
+ if (fileOffset + fragmentSize <= quint64(m_fontData.size())) {
+ *fragmentStart = m_fontData.data() + fileOffset;
+ return S_OK;
+ } else {
+ *fragmentStart = NULL;
+ return E_FAIL;
+ }
+ }
+
+ void STDMETHODCALLTYPE DirectWriteFontFileStream::ReleaseFileFragment(void *)
+ {
+ }
+
+ HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetFileSize(UINT64 *fileSize)
+ {
+ *fileSize = m_fontData.size();
+ return S_OK;
+ }
+
+ HRESULT STDMETHODCALLTYPE DirectWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
+ {
+ *lastWriteTime = 0;
+ return E_NOTIMPL;
+ }
+
+ class DirectWriteFontFileLoader: public IDWriteFontFileLoader
+ {
+ public:
+ DirectWriteFontFileLoader() : m_referenceCount(0) {}
+ virtual ~DirectWriteFontFileLoader()
+ {
+ }
+
+ inline void addKey(const void *key, const QByteArray &fontData)
+ {
+ Q_ASSERT(!m_fontDatas.contains(key));
+ m_fontDatas.insert(key, fontData);
+ }
+
+ inline void removeKey(const void *key)
+ {
+ m_fontDatas.remove(key);
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object);
+ ULONG STDMETHODCALLTYPE AddRef();
+ ULONG STDMETHODCALLTYPE Release();
+
+ HRESULT STDMETHODCALLTYPE CreateStreamFromKey(void const *fontFileReferenceKey,
+ UINT32 fontFileReferenceKeySize,
+ OUT IDWriteFontFileStream **fontFileStream);
+
+ private:
+ ULONG m_referenceCount;
+ QHash<const void *, QByteArray> m_fontDatas;
+ };
+
+ HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid,
+ void **object)
+ {
+ if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) {
+ *object = this;
+ AddRef();
+ return S_OK;
+ } else {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+ }
+
+ ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::AddRef()
+ {
+ return InterlockedIncrement(&m_referenceCount);
+ }
+
+ ULONG STDMETHODCALLTYPE DirectWriteFontFileLoader::Release()
+ {
+ ULONG newCount = InterlockedDecrement(&m_referenceCount);
+ if (newCount == 0)
+ delete this;
+ return newCount;
+ }
+
+ HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::CreateStreamFromKey(
+ void const *fontFileReferenceKey,
+ UINT32 fontFileReferenceKeySize,
+ IDWriteFontFileStream **fontFileStream)
+ {
+ Q_UNUSED(fontFileReferenceKeySize);
+
+ if (fontFileReferenceKeySize != sizeof(const void *)) {
+ qWarning("%s: Wrong key size", __FUNCTION__);
+ return E_FAIL;
+ }
+
+ const void *key = *reinterpret_cast<void * const *>(fontFileReferenceKey);
+ *fontFileStream = NULL;
+ auto it = m_fontDatas.constFind(key);
+ if (it == m_fontDatas.constEnd())
+ return E_FAIL;
+
+ QByteArray fontData = it.value();
+ DirectWriteFontFileStream *stream = new DirectWriteFontFileStream(fontData);
+ stream->AddRef();
+ *fontFileStream = stream;
+
+ return S_OK;
+ }
+
+ class CustomFontFileLoader
+ {
+ public:
+ CustomFontFileLoader(IDWriteFactory *factory)
+ {
+ m_directWriteFactory = factory;
+
+ if (m_directWriteFactory) {
+ m_directWriteFactory->AddRef();
+
+ m_directWriteFontFileLoader = new DirectWriteFontFileLoader();
+ m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader);
+ }
+ }
+
+ ~CustomFontFileLoader()
+ {
+ if (m_directWriteFactory != nullptr && m_directWriteFontFileLoader != nullptr)
+ m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader);
+
+ if (m_directWriteFactory != nullptr)
+ m_directWriteFactory->Release();
+ }
+
+ void addKey(const void *key, const QByteArray &fontData)
+ {
+ if (m_directWriteFontFileLoader != nullptr)
+ m_directWriteFontFileLoader->addKey(key, fontData);
+ }
+
+ void removeKey(const void *key)
+ {
+ if (m_directWriteFontFileLoader != nullptr)
+ m_directWriteFontFileLoader->removeKey(key);
+ }
+
+ IDWriteFontFileLoader *loader() const
+ {
+ return m_directWriteFontFileLoader;
+ }
+
+ private:
+ IDWriteFactory *m_directWriteFactory = nullptr;
+ DirectWriteFontFileLoader *m_directWriteFontFileLoader = nullptr;
+ };
+} // Anonymous namespace
+
+#endif // !defined(QT_NO_DIRECTWRITE)
+
+
+QWindowsFontEngineData::~QWindowsFontEngineData()
+{
+ if (hdc)
+ DeleteDC(hdc);
+
+#if !defined(QT_NO_DIRECTWRITE)
+ if (directWriteGdiInterop)
+ directWriteGdiInterop->Release();
+ if (directWriteFactory)
+ directWriteFactory->Release();
+#endif
+}
+
+QWindowsFontDatabaseBase::QWindowsFontDatabaseBase()
+{
+}
+
+QWindowsFontDatabaseBase::~QWindowsFontDatabaseBase()
+{
+}
+
+typedef QSharedPointer<QWindowsFontEngineData> QWindowsFontEngineDataPtr;
+typedef QThreadStorage<QWindowsFontEngineDataPtr> FontEngineThreadLocalData;
+Q_GLOBAL_STATIC(FontEngineThreadLocalData, fontEngineThreadLocalData)
+
+QSharedPointer<QWindowsFontEngineData> QWindowsFontDatabaseBase::data()
+{
+ FontEngineThreadLocalData *data = fontEngineThreadLocalData();
+ if (!data->hasLocalData())
+ data->setLocalData(QSharedPointer<QWindowsFontEngineData>::create());
+
+ if (!init(data->localData()))
+ qCWarning(lcQpaFonts) << "Cannot initialize common font database data";
+
+ return data->localData();
+}
+
+bool QWindowsFontDatabaseBase::init(QSharedPointer<QWindowsFontEngineData> d)
+{
+#if !defined(QT_NO_DIRECTWRITE)
+ if (!d->directWriteFactory) {
+ createDirectWriteFactory(&d->directWriteFactory);
+ if (!d->directWriteFactory)
+ return false;
+ }
+ if (!d->directWriteGdiInterop) {
+ const HRESULT hr = d->directWriteFactory->GetGdiInterop(&d->directWriteGdiInterop);
+ if (FAILED(hr)) {
+ qErrnoWarning("%s: GetGdiInterop failed", __FUNCTION__);
+ return false;
+ }
+ }
+#endif
+ return true;
+}
+
+#if !defined(QT_NO_DIRECTWRITE)
+// ### Qt 6: Link directly to dwrite instead
+typedef HRESULT (WINAPI *DWriteCreateFactoryType)(DWRITE_FACTORY_TYPE, const IID &, IUnknown **);
+static inline DWriteCreateFactoryType resolveDWriteCreateFactory()
+{
+ QSystemLibrary library(QStringLiteral("dwrite"));
+ QFunctionPointer result = library.resolve("DWriteCreateFactory");
+ if (Q_UNLIKELY(!result)) {
+ qWarning("Unable to load dwrite.dll");
+ return nullptr;
+ }
+ return reinterpret_cast<DWriteCreateFactoryType>(result);
+}
+
+void QWindowsFontDatabaseBase::createDirectWriteFactory(IDWriteFactory **factory)
+{
+ *factory = nullptr;
+
+ static const DWriteCreateFactoryType dWriteCreateFactory = resolveDWriteCreateFactory();
+ if (!dWriteCreateFactory)
+ return;
+
+ IUnknown *result = nullptr;
+# if defined(QT_USE_DIRECTWRITE3)
+ dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory3), &result);
+# endif
+
+# if defined(QT_USE_DIRECTWRITE2)
+ if (result == nullptr)
+ dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result);
+# endif
+
+ if (result == nullptr) {
+ if (FAILED(dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &result))) {
+ qErrnoWarning("DWriteCreateFactory failed");
+ return;
+ }
+ }
+
+ *factory = static_cast<IDWriteFactory *>(result);
+}
+#endif // !defined(QT_NO_DIRECTWRITE)
+
+int QWindowsFontDatabaseBase::defaultVerticalDPI()
+{
+ static int vDPI = -1;
+ if (vDPI == -1) {
+ if (HDC defaultDC = GetDC(0)) {
+ vDPI = GetDeviceCaps(defaultDC, LOGPIXELSY);
+ ReleaseDC(0, defaultDC);
+ } else {
+ // FIXME: Resolve now or return 96 and keep unresolved?
+ vDPI = 96;
+ }
+ }
+ return vDPI;
+}
+
+LOGFONT QWindowsFontDatabaseBase::fontDefToLOGFONT(const QFontDef &request, const QString &faceName)
+{
+ LOGFONT lf;
+ memset(&lf, 0, sizeof(LOGFONT));
+
+ lf.lfHeight = -qRound(request.pixelSize);
+ lf.lfWidth = 0;
+ lf.lfEscapement = 0;
+ lf.lfOrientation = 0;
+ if (request.weight == 50)
+ lf.lfWeight = FW_DONTCARE;
+ else
+ lf.lfWeight = (request.weight*900)/99;
+ lf.lfItalic = request.style != QFont::StyleNormal;
+ lf.lfCharSet = DEFAULT_CHARSET;
+
+ int strat = OUT_DEFAULT_PRECIS;
+ if (request.styleStrategy & QFont::PreferBitmap) {
+ strat = OUT_RASTER_PRECIS;
+ } else if (request.styleStrategy & QFont::PreferDevice) {
+ strat = OUT_DEVICE_PRECIS;
+ } else if (request.styleStrategy & QFont::PreferOutline) {
+ strat = OUT_OUTLINE_PRECIS;
+ } else if (request.styleStrategy & QFont::ForceOutline) {
+ strat = OUT_TT_ONLY_PRECIS;
+ }
+
+ lf.lfOutPrecision = strat;
+
+ int qual = DEFAULT_QUALITY;
+
+ if (request.styleStrategy & QFont::PreferMatch)
+ qual = DRAFT_QUALITY;
+ else if (request.styleStrategy & QFont::PreferQuality)
+ qual = PROOF_QUALITY;
+
+ if (request.styleStrategy & QFont::PreferAntialias) {
+ qual = (request.styleStrategy & QFont::NoSubpixelAntialias) == 0
+ ? CLEARTYPE_QUALITY : ANTIALIASED_QUALITY;
+ } else if (request.styleStrategy & QFont::NoAntialias) {
+ qual = NONANTIALIASED_QUALITY;
+ } else if ((request.styleStrategy & QFont::NoSubpixelAntialias) && data()->clearTypeEnabled) {
+ qual = ANTIALIASED_QUALITY;
+ }
+
+ lf.lfQuality = qual;
+
+ lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+
+ int hint = FF_DONTCARE;
+ switch (request.styleHint) {
+ case QFont::Helvetica:
+ hint = FF_SWISS;
+ break;
+ case QFont::Times:
+ hint = FF_ROMAN;
+ break;
+ case QFont::Courier:
+ hint = FF_MODERN;
+ break;
+ case QFont::OldEnglish:
+ hint = FF_DECORATIVE;
+ break;
+ case QFont::System:
+ hint = FF_MODERN;
+ break;
+ default:
+ break;
+ }
+
+ lf.lfPitchAndFamily = DEFAULT_PITCH | hint;
+
+ QString fam = faceName;
+ if (fam.isEmpty())
+ fam = request.families.size() > 0 ? request.families.at(0) : request.family;
+ if (Q_UNLIKELY(fam.size() >= LF_FACESIZE)) {
+ qCritical("%s: Family name '%s' is too long.", __FUNCTION__, qPrintable(fam));
+ fam.truncate(LF_FACESIZE - 1);
+ }
+
+ if (fam.isEmpty())
+ fam = QStringLiteral("MS Sans Serif");
+
+ if (fam == QLatin1String("MS Sans Serif")
+ && (request.style == QFont::StyleItalic || (-lf.lfHeight > 18 && -lf.lfHeight != 24))) {
+ fam = QStringLiteral("Arial"); // MS Sans Serif has bearing problems in italic, and does not scale
+ }
+ if (fam == QLatin1String("Courier") && !(request.styleStrategy & QFont::PreferBitmap))
+ fam = QStringLiteral("Courier New");
+
+ memcpy(lf.lfFaceName, fam.utf16(), fam.size() * sizeof(wchar_t));
+
+ return lf;
+}
+
+QFont QWindowsFontDatabaseBase::LOGFONT_to_QFont(const LOGFONT& logFont, int verticalDPI_In)
+{
+ if (verticalDPI_In <= 0)
+ verticalDPI_In = defaultVerticalDPI();
+ QFont qFont(QString::fromWCharArray(logFont.lfFaceName));
+ qFont.setItalic(logFont.lfItalic);
+ if (logFont.lfWeight != FW_DONTCARE)
+ qFont.setWeight(QPlatformFontDatabase::weightFromInteger(logFont.lfWeight));
+ const qreal logFontHeight = qAbs(logFont.lfHeight);
+ qFont.setPointSizeF(logFontHeight * 72.0 / qreal(verticalDPI_In));
+ qFont.setUnderline(logFont.lfUnderline);
+ qFont.setOverline(false);
+ qFont.setStrikeOut(logFont.lfStrikeOut);
+ return qFont;
+}
+
+// ### fixme Qt 6 (QTBUG-58610): See comment at QWindowsFontDatabase::systemDefaultFont()
+HFONT QWindowsFontDatabaseBase::systemFont()
+{
+ static const auto stock_sysfont =
+ reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT));
+ return stock_sysfont;
+}
+
+QFont QWindowsFontDatabaseBase::systemDefaultFont()
+{
+#if QT_VERSION >= 0x060000
+ // Qt 6: Obtain default GUI font (typically "Segoe UI, 9pt", see QTBUG-58610)
+ NONCLIENTMETRICS ncm;
+ ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT);
+ SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0);
+ const QFont systemFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont);
+#else
+ LOGFONT lf;
+ GetObject(systemFont(), sizeof(lf), &lf);
+ QFont systemFont = LOGFONT_to_QFont(lf);
+ // "MS Shell Dlg 2" is the correct system font >= Win2k
+ if (systemFont.family() == QLatin1String("MS Shell Dlg"))
+ systemFont.setFamily(QStringLiteral("MS Shell Dlg 2"));
+ // Qt 5 by (Qt 4) legacy uses GetStockObject(DEFAULT_GUI_FONT) to
+ // obtain the default GUI font (typically "MS Shell Dlg 2, 8pt"). This has been
+ // long deprecated; the message font of the NONCLIENTMETRICS structure obtained by
+ // SystemParametersInfo(SPI_GETNONCLIENTMETRICS) should be used instead (see
+ // QWindowsTheme::refreshFonts(), typically "Segoe UI, 9pt"), which is larger.
+#endif // Qt 5
+ qCDebug(lcQpaFonts) << __FUNCTION__ << systemFont;
+ return systemFont;
+}
+
+#if !defined(QT_NO_DIRECTWRITE)
+IDWriteFontFace *QWindowsFontDatabaseBase::createDirectWriteFace(const QByteArray &fontData) const
+{
+ QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
+ if (fontEngineData->directWriteFactory == nullptr) {
+ qCWarning(lcQpaFonts) << "DirectWrite factory not created in QWindowsFontDatabaseBase::createDirectWriteFace()";
+ return nullptr;
+ }
+
+ CustomFontFileLoader fontFileLoader(fontEngineData->directWriteFactory);
+ fontFileLoader.addKey(this, fontData);
+
+ IDWriteFontFile *fontFile = nullptr;
+ const void *key = this;
+
+ HRESULT hres = fontEngineData->directWriteFactory->CreateCustomFontFileReference(&key,
+ sizeof(void *),
+ fontFileLoader.loader(),
+ &fontFile);
+ if (FAILED(hres)) {
+ qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__);
+ return nullptr;
+ }
+
+ BOOL isSupportedFontType;
+ DWRITE_FONT_FILE_TYPE fontFileType;
+ DWRITE_FONT_FACE_TYPE fontFaceType;
+ UINT32 numberOfFaces;
+ fontFile->Analyze(&isSupportedFontType, &fontFileType, &fontFaceType, &numberOfFaces);
+ if (!isSupportedFontType) {
+ fontFile->Release();
+ return nullptr;
+ }
+
+ // ### Currently no support for .ttc, but we could easily return a list here.
+ IDWriteFontFace *directWriteFontFace = nullptr;
+ hres = fontEngineData->directWriteFactory->CreateFontFace(fontFaceType,
+ 1,
+ &fontFile,
+ 0,
+ DWRITE_FONT_SIMULATIONS_NONE,
+ &directWriteFontFace);
+ if (FAILED(hres)) {
+ qErrnoWarning(hres, "%s: CreateFontFace failed", __FUNCTION__);
+ fontFile->Release();
+ return nullptr;
+ }
+
+ fontFile->Release();
+ return directWriteFontFace;
+}
+#endif // !defined(QT_NO_DIRECTWRITE)
+
+QFontEngine *QWindowsFontDatabaseBase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
+{
+ QFontEngine *fontEngine = nullptr;
+
+#if !defined(QT_NO_DIRECTWRITE)
+ QSharedPointer<QWindowsFontEngineData> fontEngineData = data();
+ if (fontEngineData->directWriteFactory == nullptr)
+ return nullptr;
+
+ IDWriteFontFace *directWriteFontFace = createDirectWriteFace(fontData);
+ fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace,
+ pixelSize,
+ fontEngineData);
+
+ // Get font family from font data
+ EmbeddedFont font(fontData);
+ font.updateFromOS2Table(fontEngine);
+ fontEngine->fontDef.family = font.familyName();
+ fontEngine->fontDef.hintingPreference = hintingPreference;
+
+ directWriteFontFace->Release();
+#else // !defined(QT_NO_DIRECTWRITE)
+ Q_UNUSED(fontData);
+ Q_UNUSED(pixelSize);
+ Q_UNUSED(hintingPreference);
+#endif
+
+ return fontEngine;
+}
+
+QT_END_NAMESPACE