| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 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 "qwindowsfontdatabase_p.h" |
| #include "qwindowsfontdatabase_ft_p.h" // for default font |
| #include "qwindowsfontengine_p.h" |
| #include "qwindowsfontenginedirectwrite_p.h" |
| #include <QtCore/qt_windows.h> |
| |
| #include <QtGui/QFont> |
| #include <QtGui/QGuiApplication> |
| #include <QtGui/private/qhighdpiscaling_p.h> |
| |
| #include <QtCore/qmath.h> |
| #include <QtCore/QDebug> |
| #include <QtCore/QFile> |
| #include <QtCore/QtEndian> |
| #include <QtCore/QThreadStorage> |
| #include <QtCore/private/qsystemlibrary_p.h> |
| #include <QtCore/private/qwinregistry_p.h> |
| |
| #include <wchar.h> |
| |
| #if !defined(QT_NO_DIRECTWRITE) |
| # if defined(QT_USE_DIRECTWRITE2) |
| # include <dwrite_2.h> |
| # else |
| # include <dwrite.h> |
| # endif |
| # include <d2d1.h> |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| Q_LOGGING_CATEGORY(lcQpaFonts, "qt.qpa.fonts") |
| |
| #ifndef QT_NO_DIRECTWRITE |
| // ### fixme: Consider direct linking of dwrite.dll once Windows Vista pre SP2 is dropped (QTBUG-49711) |
| |
| 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); |
| } |
| |
| static void createDirectWriteFactory(IDWriteFactory **factory) |
| { |
| *factory = nullptr; |
| |
| static const DWriteCreateFactoryType dWriteCreateFactory = resolveDWriteCreateFactory(); |
| if (!dWriteCreateFactory) |
| return; |
| |
| IUnknown *result = NULL; |
| #if defined(QT_USE_DIRECTWRITE2) |
| dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory2), &result); |
| #endif |
| |
| if (result == NULL) { |
| if (FAILED(dWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &result))) { |
| qErrnoWarning("DWriteCreateFactory failed"); |
| return; |
| } |
| } |
| |
| *factory = static_cast<IDWriteFactory *>(result); |
| } |
| |
| static inline bool useDirectWrite(QFont::HintingPreference hintingPreference, |
| const QString &familyName = QString(), |
| bool isColorFont = false) |
| { |
| const unsigned options = QWindowsFontDatabase::fontOptions(); |
| if (Q_UNLIKELY(options & QWindowsFontDatabase::DontUseDirectWriteFonts)) |
| return false; |
| |
| // At some scales, GDI will misrender the MingLiU font, so we force use of |
| // DirectWrite to work around the issue. |
| if (Q_UNLIKELY(familyName.startsWith(QLatin1String("MingLiU")))) |
| return true; |
| |
| if (isColorFont) |
| return (options & QWindowsFontDatabase::DontUseColorFonts) == 0; |
| |
| return hintingPreference == QFont::PreferNoHinting |
| || hintingPreference == QFont::PreferVerticalHinting |
| || (QHighDpiScaling::isActive() && hintingPreference == QFont::PreferDefaultHinting); |
| } |
| #endif // !QT_NO_DIRECTWRITE |
| |
| // 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 |
| { |
| 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() |
| |
| class EmbeddedFont |
| { |
| public: |
| EmbeddedFont(const QByteArray &fontData) : m_fontData(fontData) {} |
| |
| QString changeFamilyName(const QString &newFamilyName); |
| QByteArray data() const { return m_fontData; } |
| TableDirectory *tableDirectoryEntry(const QByteArray &tagName); |
| QString familyName(TableDirectory *nameTableDirectory = 0); |
| |
| private: |
| QByteArray m_fontData; |
| }; |
| |
| TableDirectory *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 0; |
| |
| 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 0; |
| |
| TableDirectory *tableDirectoryEnd = tableDirectory + tableCount; |
| for (TableDirectory *entry = tableDirectory; entry < tableDirectoryEnd; ++entry) { |
| if (entry->identifier == tagId) |
| return entry; |
| } |
| |
| return 0; |
| } |
| |
| QString EmbeddedFont::familyName(TableDirectory *nameTableDirectoryEntry) |
| { |
| QString name; |
| |
| if (nameTableDirectoryEntry == 0) |
| nameTableDirectoryEntry = tableDirectoryEntry("name"); |
| |
| if (nameTableDirectoryEntry != 0) { |
| 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; |
| } |
| |
| QString EmbeddedFont::changeFamilyName(const QString &newFamilyName) |
| { |
| TableDirectory *nameTableDirectoryEntry = tableDirectoryEntry("name"); |
| if (nameTableDirectoryEntry == 0) |
| 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) |
| |
| 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() : m_directWriteFontFileLoader(nullptr) |
| { |
| createDirectWriteFactory(&m_directWriteFactory); |
| |
| if (m_directWriteFactory) { |
| m_directWriteFontFileLoader = new DirectWriteFontFileLoader(); |
| m_directWriteFactory->RegisterFontFileLoader(m_directWriteFontFileLoader); |
| } |
| } |
| |
| ~CustomFontFileLoader() |
| { |
| if (m_directWriteFactory != 0 && m_directWriteFontFileLoader != 0) |
| m_directWriteFactory->UnregisterFontFileLoader(m_directWriteFontFileLoader); |
| |
| if (m_directWriteFactory != 0) |
| m_directWriteFactory->Release(); |
| } |
| |
| void addKey(const void *key, const QByteArray &fontData) |
| { |
| if (m_directWriteFontFileLoader != 0) |
| m_directWriteFontFileLoader->addKey(key, fontData); |
| } |
| |
| void removeKey(const void *key) |
| { |
| if (m_directWriteFontFileLoader != 0) |
| m_directWriteFontFileLoader->removeKey(key); |
| } |
| |
| IDWriteFontFileLoader *loader() const |
| { |
| return m_directWriteFontFileLoader; |
| } |
| |
| private: |
| IDWriteFactory *m_directWriteFactory; |
| DirectWriteFontFileLoader *m_directWriteFontFileLoader; |
| }; |
| |
| #endif |
| |
| } // Anonymous namespace |
| |
| /*! |
| \struct QWindowsFontEngineData |
| \brief Static constant data shared by the font engines. |
| */ |
| |
| QWindowsFontEngineData::QWindowsFontEngineData() |
| : fontSmoothingGamma(QWindowsFontDatabase::fontSmoothingGamma()) |
| { |
| // from qapplication_win.cpp |
| UINT result = 0; |
| if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &result, 0)) |
| clearTypeEnabled = (result == FE_FONTSMOOTHINGCLEARTYPE); |
| |
| const qreal gray_gamma = 2.31; |
| for (int i=0; i<256; ++i) |
| pow_gamma[i] = uint(qRound(qPow(i / qreal(255.), gray_gamma) * 2047)); |
| |
| HDC displayDC = GetDC(0); |
| hdc = CreateCompatibleDC(displayDC); |
| ReleaseDC(0, displayDC); |
| } |
| |
| unsigned QWindowsFontDatabase::m_fontOptions = 0; |
| |
| void QWindowsFontDatabase::setFontOptions(unsigned options) |
| { |
| m_fontOptions = options & (QWindowsFontDatabase::DontUseDirectWriteFonts | |
| QWindowsFontDatabase::DontUseColorFonts); |
| } |
| |
| unsigned QWindowsFontDatabase::fontOptions() |
| { |
| return m_fontOptions; |
| } |
| |
| QWindowsFontEngineData::~QWindowsFontEngineData() |
| { |
| if (hdc) |
| DeleteDC(hdc); |
| #if !defined(QT_NO_DIRECTWRITE) |
| if (directWriteGdiInterop) |
| directWriteGdiInterop->Release(); |
| if (directWriteFactory) |
| directWriteFactory->Release(); |
| #endif |
| } |
| |
| qreal QWindowsFontDatabase::fontSmoothingGamma() |
| { |
| int winSmooth; |
| qreal result = 1; |
| if (SystemParametersInfo(0x200C /* SPI_GETFONTSMOOTHINGCONTRAST */, 0, &winSmooth, 0)) |
| result = qreal(winSmooth) / qreal(1000.0); |
| |
| // Safeguard ourselves against corrupt registry values... |
| if (result > 5 || result < 1) |
| result = qreal(1.4); |
| return result; |
| } |
| |
| #if !defined(QT_NO_DIRECTWRITE) |
| static inline bool initDirectWrite(QWindowsFontEngineData *d) |
| { |
| 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; |
| } |
| } |
| return true; |
| } |
| |
| #endif // !defined(QT_NO_DIRECTWRITE) |
| |
| /*! |
| \class QWindowsFontDatabase |
| \brief Font database for Windows |
| |
| \note The Qt 4.8 WIndows font database employed a mechanism of |
| delayed population of the database again passing a font name |
| to EnumFontFamiliesEx(), working around the fact that |
| EnumFontFamiliesEx() does not list all fonts by default. |
| This should be introduced to QPA as well? |
| |
| \internal |
| */ |
| |
| #ifndef QT_NO_DEBUG_STREAM |
| QDebug operator<<(QDebug d, const QFontDef &def) |
| { |
| QDebugStateSaver saver(d); |
| d.nospace(); |
| d.noquote(); |
| d << "QFontDef(Family=\"" << def.family << '"'; |
| if (!def.styleName.isEmpty()) |
| d << ", stylename=" << def.styleName; |
| d << ", pointsize=" << def.pointSize << ", pixelsize=" << def.pixelSize |
| << ", styleHint=" << def.styleHint << ", weight=" << def.weight |
| << ", stretch=" << def.stretch << ", hintingPreference=" |
| << def.hintingPreference << ')'; |
| return d; |
| } |
| |
| QDebug operator<<(QDebug d, const LOGFONT &lf) |
| { |
| QDebugStateSaver saver(d); |
| d.nospace(); |
| d.noquote(); |
| d << "LOGFONT(\"" << QString::fromWCharArray(lf.lfFaceName) |
| << "\", lfWidth=" << lf.lfWidth << ", lfHeight=" << lf.lfHeight << ')'; |
| return d; |
| } |
| #endif // !QT_NO_DEBUG_STREAM |
| |
| static inline QFontDatabase::WritingSystem writingSystemFromCharSet(uchar charSet) |
| { |
| switch (charSet) { |
| case ANSI_CHARSET: |
| case EASTEUROPE_CHARSET: |
| case BALTIC_CHARSET: |
| case TURKISH_CHARSET: |
| return QFontDatabase::Latin; |
| case GREEK_CHARSET: |
| return QFontDatabase::Greek; |
| case RUSSIAN_CHARSET: |
| return QFontDatabase::Cyrillic; |
| case HEBREW_CHARSET: |
| return QFontDatabase::Hebrew; |
| case ARABIC_CHARSET: |
| return QFontDatabase::Arabic; |
| case THAI_CHARSET: |
| return QFontDatabase::Thai; |
| case GB2312_CHARSET: |
| return QFontDatabase::SimplifiedChinese; |
| case CHINESEBIG5_CHARSET: |
| return QFontDatabase::TraditionalChinese; |
| case SHIFTJIS_CHARSET: |
| return QFontDatabase::Japanese; |
| case HANGUL_CHARSET: |
| case JOHAB_CHARSET: |
| return QFontDatabase::Korean; |
| case VIETNAMESE_CHARSET: |
| return QFontDatabase::Vietnamese; |
| case SYMBOL_CHARSET: |
| return QFontDatabase::Symbol; |
| default: |
| break; |
| } |
| return QFontDatabase::Any; |
| } |
| |
| #ifdef MAKE_TAG |
| #undef MAKE_TAG |
| #endif |
| // GetFontData expects the tags in little endian ;( |
| #define MAKE_TAG(ch1, ch2, ch3, ch4) (\ |
| (((quint32)(ch4)) << 24) | \ |
| (((quint32)(ch3)) << 16) | \ |
| (((quint32)(ch2)) << 8) | \ |
| ((quint32)(ch1)) \ |
| ) |
| |
| bool qt_localizedName(const QString &name) |
| { |
| const QChar *c = name.unicode(); |
| for (int i = 0; i < name.length(); ++i) { |
| if (c[i].unicode() >= 0x100) |
| return true; |
| } |
| return false; |
| } |
| |
| namespace { |
| |
| static QString readName(bool unicode, const uchar *string, int length) |
| { |
| QString out; |
| if (unicode) { |
| // utf16 |
| |
| length /= 2; |
| out.resize(length); |
| QChar *uc = out.data(); |
| for (int i = 0; i < length; ++i) |
| uc[i] = qt_getUShort(string + 2*i); |
| } else { |
| // Apple Roman |
| |
| out.resize(length); |
| QChar *uc = out.data(); |
| for (int i = 0; i < length; ++i) |
| uc[i] = QLatin1Char(char(string[i])); |
| } |
| return out; |
| } |
| |
| enum FieldTypeValue { |
| FamilyId = 1, |
| StyleId = 2, |
| PreferredFamilyId = 16, |
| PreferredStyleId = 17, |
| }; |
| |
| enum PlatformFieldValue { |
| PlatformId_Unicode = 0, |
| PlatformId_Apple = 1, |
| PlatformId_Microsoft = 3 |
| }; |
| |
| QFontNames qt_getCanonicalFontNames(const uchar *table, quint32 bytes) |
| { |
| QFontNames out; |
| const int NameRecordSize = 12; |
| const int MS_LangIdEnglish = 0x009; |
| |
| // get the name table |
| quint16 count; |
| quint16 string_offset; |
| const unsigned char *names; |
| |
| if (bytes < 8) |
| return out; |
| |
| if (qt_getUShort(table) != 0) |
| return out; |
| |
| count = qt_getUShort(table + 2); |
| string_offset = qt_getUShort(table + 4); |
| names = table + 6; |
| |
| if (string_offset >= bytes || 6 + count*NameRecordSize > string_offset) |
| return out; |
| |
| enum PlatformIdType { |
| NotFound = 0, |
| Unicode = 1, |
| Apple = 2, |
| Microsoft = 3 |
| }; |
| |
| PlatformIdType idStatus[4] = { NotFound, NotFound, NotFound, NotFound }; |
| int ids[4] = { -1, -1, -1, -1 }; |
| |
| for (int i = 0; i < count; ++i) { |
| // search for the correct name entries |
| |
| quint16 platform_id = qt_getUShort(names + i*NameRecordSize); |
| quint16 encoding_id = qt_getUShort(names + 2 + i*NameRecordSize); |
| quint16 language_id = qt_getUShort(names + 4 + i*NameRecordSize); |
| quint16 name_id = qt_getUShort(names + 6 + i*NameRecordSize); |
| |
| PlatformIdType *idType = nullptr; |
| int *id = nullptr; |
| |
| switch (name_id) { |
| case FamilyId: |
| idType = &idStatus[0]; |
| id = &ids[0]; |
| break; |
| case StyleId: |
| idType = &idStatus[1]; |
| id = &ids[1]; |
| break; |
| case PreferredFamilyId: |
| idType = &idStatus[2]; |
| id = &ids[2]; |
| break; |
| case PreferredStyleId: |
| idType = &idStatus[3]; |
| id = &ids[3]; |
| break; |
| default: |
| continue; |
| } |
| |
| quint16 length = qt_getUShort(names + 8 + i*NameRecordSize); |
| quint16 offset = qt_getUShort(names + 10 + i*NameRecordSize); |
| if (DWORD(string_offset + offset + length) > bytes) |
| continue; |
| |
| if ((platform_id == PlatformId_Microsoft |
| && (encoding_id == 0 || encoding_id == 1)) |
| && ((language_id & 0x3ff) == MS_LangIdEnglish |
| || *idType < Microsoft)) { |
| *id = i; |
| *idType = Microsoft; |
| } |
| // not sure if encoding id 4 for Unicode is utf16 or ucs4... |
| else if (platform_id == PlatformId_Unicode && encoding_id < 4 && *idType < Unicode) { |
| *id = i; |
| *idType = Unicode; |
| } |
| else if (platform_id == PlatformId_Apple && encoding_id == 0 && language_id == 0 && *idType < Apple) { |
| *id = i; |
| *idType = Apple; |
| } |
| } |
| |
| QString strings[4]; |
| for (int i = 0; i < 4; ++i) { |
| if (idStatus[i] == NotFound) |
| continue; |
| int id = ids[i]; |
| quint16 length = qt_getUShort(names + 8 + id * NameRecordSize); |
| quint16 offset = qt_getUShort(names + 10 + id * NameRecordSize); |
| const unsigned char *string = table + string_offset + offset; |
| strings[i] = readName(idStatus[i] != Apple, string, length); |
| } |
| |
| out.name = strings[0]; |
| out.style = strings[1]; |
| out.preferredName = strings[2]; |
| out.preferredStyle = strings[3]; |
| return out; |
| } |
| |
| } // namespace |
| |
| QString qt_getEnglishName(const QString &familyName, bool includeStyle) |
| { |
| QString i18n_name; |
| QString faceName = familyName; |
| faceName.truncate(LF_FACESIZE - 1); |
| |
| HDC hdc = GetDC( 0 ); |
| LOGFONT lf; |
| memset(&lf, 0, sizeof(LOGFONT)); |
| faceName.toWCharArray(lf.lfFaceName); |
| lf.lfFaceName[faceName.size()] = 0; |
| lf.lfCharSet = DEFAULT_CHARSET; |
| HFONT hfont = CreateFontIndirect(&lf); |
| |
| if (!hfont) { |
| ReleaseDC(0, hdc); |
| return QString(); |
| } |
| |
| HGDIOBJ oldobj = SelectObject( hdc, hfont ); |
| |
| const DWORD name_tag = MAKE_TAG( 'n', 'a', 'm', 'e' ); |
| |
| // get the name table |
| unsigned char *table = 0; |
| |
| DWORD bytes = GetFontData( hdc, name_tag, 0, 0, 0 ); |
| if ( bytes == GDI_ERROR ) { |
| // ### Unused variable |
| // int err = GetLastError(); |
| goto error; |
| } |
| |
| table = new unsigned char[bytes]; |
| GetFontData(hdc, name_tag, 0, table, bytes); |
| if ( bytes == GDI_ERROR ) |
| goto error; |
| |
| { |
| const QFontNames names = qt_getCanonicalFontNames(table, bytes); |
| i18n_name = names.name; |
| if (includeStyle) |
| i18n_name += QLatin1Char(' ') + names.style; |
| } |
| error: |
| delete [] table; |
| SelectObject( hdc, oldobj ); |
| DeleteObject( hfont ); |
| ReleaseDC( 0, hdc ); |
| |
| //qDebug("got i18n name of '%s' for font '%s'", i18n_name.latin1(), familyName.toLocal8Bit().data()); |
| return i18n_name; |
| } |
| |
| // Note this duplicates parts of qt_getEnglishName, we should try to unify the two functions. |
| QFontNames qt_getCanonicalFontNames(const LOGFONT &lf) |
| { |
| QFontNames fontNames; |
| HDC hdc = GetDC(0); |
| HFONT hfont = CreateFontIndirect(&lf); |
| |
| if (!hfont) { |
| ReleaseDC(0, hdc); |
| return fontNames; |
| } |
| |
| HGDIOBJ oldobj = SelectObject(hdc, hfont); |
| |
| // get the name table |
| QByteArray table; |
| const DWORD name_tag = MAKE_TAG('n', 'a', 'm', 'e'); |
| DWORD bytes = GetFontData(hdc, name_tag, 0, 0, 0); |
| if (bytes != GDI_ERROR) { |
| table.resize(bytes); |
| |
| if (GetFontData(hdc, name_tag, 0, table.data(), bytes) != GDI_ERROR) |
| fontNames = qt_getCanonicalFontNames(reinterpret_cast<const uchar*>(table.constData()), bytes); |
| } |
| |
| SelectObject(hdc, oldobj); |
| DeleteObject(hfont); |
| ReleaseDC(0, hdc); |
| |
| return fontNames; |
| } |
| |
| static QChar *createFontFile(const QString &faceName) |
| { |
| QChar *faceNamePtr = nullptr; |
| if (!faceName.isEmpty()) { |
| const int nameLength = qMin(faceName.length(), LF_FACESIZE - 1); |
| faceNamePtr = new QChar[nameLength + 1]; |
| memcpy(static_cast<void *>(faceNamePtr), faceName.utf16(), sizeof(wchar_t) * nameLength); |
| faceNamePtr[nameLength] = 0; |
| } |
| return faceNamePtr; |
| } |
| |
| namespace { |
| struct StoreFontPayload { |
| StoreFontPayload(const QString &family, |
| QWindowsFontDatabase *fontDatabase) |
| : populatedFontFamily(family) |
| , windowsFontDatabase(fontDatabase) |
| {} |
| |
| QString populatedFontFamily; |
| QSet<QPair<QString,QString> > foundFontAndStyles; |
| QWindowsFontDatabase *windowsFontDatabase; |
| }; |
| } |
| |
| static bool addFontToDatabase(QString familyName, |
| QString styleName, |
| const LOGFONT &logFont, |
| const TEXTMETRIC *textmetric, |
| const FONTSIGNATURE *signature, |
| int type, |
| StoreFontPayload *sfp) |
| { |
| // the "@family" fonts are just the same as "family". Ignore them. |
| if (familyName.isEmpty() || familyName.at(0) == QLatin1Char('@') || familyName.startsWith(QLatin1String("WST_"))) |
| return false; |
| |
| uchar charSet = logFont.lfCharSet; |
| |
| static const int SMOOTH_SCALABLE = 0xffff; |
| const QString foundryName; // No such concept. |
| const bool fixed = !(textmetric->tmPitchAndFamily & TMPF_FIXED_PITCH); |
| const bool ttf = (textmetric->tmPitchAndFamily & TMPF_TRUETYPE); |
| const bool scalable = textmetric->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE); |
| const int size = scalable ? SMOOTH_SCALABLE : textmetric->tmHeight; |
| const QFont::Style style = textmetric->tmItalic ? QFont::StyleItalic : QFont::StyleNormal; |
| const bool antialias = false; |
| const QFont::Weight weight = QPlatformFontDatabase::weightFromInteger(textmetric->tmWeight); |
| const QFont::Stretch stretch = QFont::Unstretched; |
| |
| #ifndef QT_NO_DEBUG_OUTPUT |
| if (lcQpaFonts().isDebugEnabled()) { |
| QString message; |
| QTextStream str(&message); |
| str << __FUNCTION__ << ' ' << familyName << ' ' << charSet << " TTF=" << ttf; |
| if (type & DEVICE_FONTTYPE) |
| str << " DEVICE"; |
| if (type & RASTER_FONTTYPE) |
| str << " RASTER"; |
| if (type & TRUETYPE_FONTTYPE) |
| str << " TRUETYPE"; |
| str << " scalable=" << scalable << " Size=" << size |
| << " Style=" << style << " Weight=" << weight |
| << " stretch=" << stretch; |
| qCDebug(lcQpaFonts) << message; |
| } |
| #endif |
| QString englishName; |
| QString faceName; |
| |
| QString subFamilyName; |
| QString subFamilyStyle; |
| // Look-up names registered in the font |
| QFontNames canonicalNames = qt_getCanonicalFontNames(logFont); |
| if (qt_localizedName(familyName) && !canonicalNames.name.isEmpty()) |
| englishName = canonicalNames.name; |
| if (!canonicalNames.preferredName.isEmpty()) { |
| subFamilyName = familyName; |
| subFamilyStyle = styleName; |
| faceName = familyName; // Remember the original name for later lookups |
| familyName = canonicalNames.preferredName; |
| styleName = canonicalNames.preferredStyle; |
| } |
| |
| QSupportedWritingSystems writingSystems; |
| if (type & TRUETYPE_FONTTYPE) { |
| Q_ASSERT(signature); |
| quint32 unicodeRange[4] = { |
| signature->fsUsb[0], signature->fsUsb[1], |
| signature->fsUsb[2], signature->fsUsb[3] |
| }; |
| quint32 codePageRange[2] = { |
| signature->fsCsb[0], signature->fsCsb[1] |
| }; |
| writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); |
| // ### Hack to work around problem with Thai text on Windows 7. Segoe UI contains |
| // the symbol for Baht, and Windows thus reports that it supports the Thai script. |
| // Since it's the default UI font on this platform, most widgets will be unable to |
| // display Thai text by default. As a temporary work around, we special case Segoe UI |
| // and remove the Thai script from its list of supported writing systems. |
| if (writingSystems.supported(QFontDatabase::Thai) && |
| familyName == QLatin1String("Segoe UI")) |
| writingSystems.setSupported(QFontDatabase::Thai, false); |
| } else { |
| const QFontDatabase::WritingSystem ws = writingSystemFromCharSet(charSet); |
| if (ws != QFontDatabase::Any) |
| writingSystems.setSupported(ws); |
| } |
| |
| // We came here from populating a different font family, so we have |
| // to ensure the entire typographic family is populated before we |
| // mark it as such inside registerFont() |
| if (!subFamilyName.isEmpty() |
| && familyName != subFamilyName |
| && sfp->populatedFontFamily != familyName |
| && !QPlatformFontDatabase::isFamilyPopulated(familyName)) { |
| sfp->windowsFontDatabase->populateFamily(familyName); |
| } |
| |
| QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight, |
| style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName)); |
| |
| // add fonts windows can generate for us: |
| if (weight <= QFont::DemiBold && styleName.isEmpty()) |
| QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, |
| style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName)); |
| if (style != QFont::StyleItalic && styleName.isEmpty()) |
| QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight, |
| QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName)); |
| if (weight <= QFont::DemiBold && style != QFont::StyleItalic && styleName.isEmpty()) |
| QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, |
| QFont::StyleItalic, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName)); |
| |
| if (!subFamilyName.isEmpty() && familyName != subFamilyName) { |
| QPlatformFontDatabase::registerFont(subFamilyName, subFamilyStyle, foundryName, weight, |
| style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(faceName)); |
| } |
| |
| if (!englishName.isEmpty() && englishName != familyName) |
| QPlatformFontDatabase::registerAliasToFontFamily(familyName, englishName); |
| |
| return true; |
| } |
| |
| static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *textmetric, |
| DWORD type, LPARAM lparam) |
| { |
| const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont); |
| const QString familyName = QString::fromWCharArray(f->elfLogFont.lfFaceName); |
| const QString styleName = QString::fromWCharArray(f->elfStyle); |
| |
| // NEWTEXTMETRICEX (passed for TT fonts) is a NEWTEXTMETRIC, which according |
| // to the documentation is identical to a TEXTMETRIC except for the last four |
| // members, which we don't use anyway |
| const FONTSIGNATURE *signature = nullptr; |
| StoreFontPayload *sfp = reinterpret_cast<StoreFontPayload *>(lparam); |
| Q_ASSERT(sfp != nullptr); |
| if (type & TRUETYPE_FONTTYPE) { |
| signature = &reinterpret_cast<const NEWTEXTMETRICEX *>(textmetric)->ntmFontSig; |
| // We get a callback for each script-type supported, but we register them all |
| // at once using the signature, so we only need one call to addFontToDatabase(). |
| QPair<QString,QString> fontAndStyle(familyName, styleName); |
| if (sfp->foundFontAndStyles.contains(fontAndStyle)) |
| return 1; |
| sfp->foundFontAndStyles.insert(fontAndStyle); |
| } |
| addFontToDatabase(familyName, styleName, *logFont, textmetric, signature, type, sfp); |
| |
| // keep on enumerating |
| return 1; |
| } |
| |
| void QWindowsFontDatabase::populateFamily(const QString &familyName) |
| { |
| qCDebug(lcQpaFonts) << familyName; |
| if (familyName.size() >= LF_FACESIZE) { |
| qCWarning(lcQpaFonts) << "Unable to enumerate family '" << familyName << '\''; |
| return; |
| } |
| HDC dummy = GetDC(0); |
| LOGFONT lf; |
| lf.lfCharSet = DEFAULT_CHARSET; |
| familyName.toWCharArray(lf.lfFaceName); |
| lf.lfFaceName[familyName.size()] = 0; |
| lf.lfPitchAndFamily = 0; |
| StoreFontPayload sfp(familyName, this); |
| EnumFontFamiliesEx(dummy, &lf, storeFont, reinterpret_cast<intptr_t>(&sfp), 0); |
| ReleaseDC(0, dummy); |
| } |
| |
| static int QT_WIN_CALLBACK populateFontFamilies(const LOGFONT *logFont, const TEXTMETRIC *textmetric, |
| DWORD, LPARAM) |
| { |
| // the "@family" fonts are just the same as "family". Ignore them. |
| const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont); |
| const wchar_t *faceNameW = f->elfLogFont.lfFaceName; |
| if (faceNameW[0] && faceNameW[0] != L'@' && wcsncmp(faceNameW, L"WST_", 4)) { |
| const QString faceName = QString::fromWCharArray(faceNameW); |
| QPlatformFontDatabase::registerFontFamily(faceName); |
| // Register current font's english name as alias |
| const bool ttf = (textmetric->tmPitchAndFamily & TMPF_TRUETYPE); |
| if (ttf && qt_localizedName(faceName)) { |
| const QString englishName = qt_getEnglishName(faceName); |
| if (!englishName.isEmpty()) |
| QPlatformFontDatabase::registerAliasToFontFamily(faceName, englishName); |
| } |
| } |
| return 1; // continue |
| } |
| |
| void QWindowsFontDatabase::addDefaultEUDCFont() |
| { |
| const QString path = QWinRegistryKey(HKEY_CURRENT_USER, LR"(EUDC\1252)") |
| .stringValue(L"SystemDefaultEUDCFont"); |
| if (!path.isEmpty()) { |
| QFile file(path); |
| if (!file.open(QIODevice::ReadOnly)) { |
| qCWarning(lcQpaFonts) << "Unable to open default EUDC font:" << path; |
| return; |
| } |
| |
| m_eudcFonts = addApplicationFont(file.readAll(), path); |
| } |
| } |
| |
| void QWindowsFontDatabase::populateFontDatabase() |
| { |
| removeApplicationFonts(); |
| HDC dummy = GetDC(0); |
| LOGFONT lf; |
| lf.lfCharSet = DEFAULT_CHARSET; |
| lf.lfFaceName[0] = 0; |
| lf.lfPitchAndFamily = 0; |
| EnumFontFamiliesEx(dummy, &lf, populateFontFamilies, 0, 0); |
| ReleaseDC(0, dummy); |
| // Work around EnumFontFamiliesEx() not listing the system font. |
| QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().family(); |
| if (QPlatformFontDatabase::resolveFontFamilyAlias(systemDefaultFamily) == systemDefaultFamily) |
| QPlatformFontDatabase::registerFontFamily(systemDefaultFamily); |
| addDefaultEUDCFont(); |
| } |
| |
| typedef QSharedPointer<QWindowsFontEngineData> QWindowsFontEngineDataPtr; |
| |
| typedef QThreadStorage<QWindowsFontEngineDataPtr> FontEngineThreadLocalData; |
| |
| Q_GLOBAL_STATIC(FontEngineThreadLocalData, fontEngineThreadLocalData) |
| |
| QSharedPointer<QWindowsFontEngineData> sharedFontData() |
| { |
| FontEngineThreadLocalData *data = fontEngineThreadLocalData(); |
| if (!data->hasLocalData()) |
| data->setLocalData(QSharedPointer<QWindowsFontEngineData>::create()); |
| return data->localData(); |
| } |
| |
| QWindowsFontDatabase::QWindowsFontDatabase() |
| { |
| // Properties accessed by QWin32PrintEngine (Qt Print Support) |
| static const int hfontMetaTypeId = qRegisterMetaType<HFONT>(); |
| static const int logFontMetaTypeId = qRegisterMetaType<LOGFONT>(); |
| Q_UNUSED(hfontMetaTypeId) |
| Q_UNUSED(logFontMetaTypeId) |
| |
| if (lcQpaFonts().isDebugEnabled()) { |
| const QWindowsFontEngineDataPtr data = sharedFontData(); |
| qCDebug(lcQpaFonts) << __FUNCTION__ << "Clear type: " |
| << data->clearTypeEnabled << "gamma: " << data->fontSmoothingGamma; |
| } |
| } |
| |
| QWindowsFontDatabase::~QWindowsFontDatabase() |
| { |
| removeApplicationFonts(); |
| } |
| |
| QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, void *handle) |
| { |
| const QString faceName(static_cast<const QChar*>(handle)); |
| QFontEngine *fe = QWindowsFontDatabase::createEngine(fontDef, faceName, |
| defaultVerticalDPI(), |
| sharedFontData()); |
| qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef << fe << handle; |
| return fe; |
| } |
| |
| QFontEngine *QWindowsFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) |
| { |
| EmbeddedFont font(fontData); |
| QFontEngine *fontEngine = 0; |
| |
| #if !defined(QT_NO_DIRECTWRITE) |
| if (!useDirectWrite(hintingPreference)) |
| #endif |
| { |
| GUID guid; |
| CoCreateGuid(&guid); |
| |
| QT_WARNING_PUSH |
| QT_WARNING_DISABLE_GCC("-Wstrict-aliasing") |
| QString uniqueFamilyName = QLatin1Char('f') |
| + QString::number(guid.Data1, 36) + QLatin1Char('-') |
| + QString::number(guid.Data2, 36) + QLatin1Char('-') |
| + QString::number(guid.Data3, 36) + QLatin1Char('-') |
| + QString::number(*reinterpret_cast<quint64 *>(guid.Data4), 36); |
| QT_WARNING_POP |
| |
| QString actualFontName = font.changeFamilyName(uniqueFamilyName); |
| if (actualFontName.isEmpty()) { |
| qWarning("%s: Can't change family name of font", __FUNCTION__); |
| return 0; |
| } |
| |
| DWORD count = 0; |
| QByteArray newFontData = font.data(); |
| HANDLE fontHandle = |
| AddFontMemResourceEx(const_cast<char *>(newFontData.constData()), |
| DWORD(newFontData.size()), 0, &count); |
| if (count == 0 && fontHandle != 0) { |
| RemoveFontMemResourceEx(fontHandle); |
| fontHandle = 0; |
| } |
| |
| if (fontHandle == 0) { |
| qWarning("%s: AddFontMemResourceEx failed", __FUNCTION__); |
| } else { |
| QFontDef request; |
| request.family = uniqueFamilyName; |
| request.pixelSize = pixelSize; |
| request.styleStrategy = QFont::PreferMatch; |
| request.hintingPreference = hintingPreference; |
| request.stretch = QFont::Unstretched; |
| |
| fontEngine = QWindowsFontDatabase::createEngine(request, QString(), |
| defaultVerticalDPI(), |
| sharedFontData()); |
| |
| if (fontEngine) { |
| if (request.family != fontEngine->fontDef.family) { |
| qWarning("%s: Failed to load font. Got fallback instead: %s", |
| __FUNCTION__, qPrintable(fontEngine->fontDef.family)); |
| if (fontEngine->ref.loadRelaxed() == 0) |
| delete fontEngine; |
| fontEngine = 0; |
| } else { |
| Q_ASSERT(fontEngine->ref.loadRelaxed() == 0); |
| |
| // Override the generated font name |
| switch (fontEngine->type()) { |
| case QFontEngine::Win: |
| static_cast<QWindowsFontEngine *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName); |
| fontEngine->fontDef.family = actualFontName; |
| break; |
| |
| #if !defined(QT_NO_DIRECTWRITE) |
| case QFontEngine::DirectWrite: |
| static_cast<QWindowsFontEngineDirectWrite *>(fontEngine)->setUniqueFamilyName(uniqueFamilyName); |
| fontEngine->fontDef.family = actualFontName; |
| break; |
| #endif // !QT_NO_DIRECTWRITE |
| |
| default: |
| Q_ASSERT_X(false, Q_FUNC_INFO, "Unhandled font engine."); |
| } |
| |
| UniqueFontData uniqueData; |
| uniqueData.handle = fontHandle; |
| uniqueData.refCount.ref(); |
| m_uniqueFontData[uniqueFamilyName] = uniqueData; |
| } |
| } else { |
| RemoveFontMemResourceEx(fontHandle); |
| } |
| } |
| } |
| #if !defined(QT_NO_DIRECTWRITE) |
| else { |
| CustomFontFileLoader fontFileLoader; |
| fontFileLoader.addKey(this, fontData); |
| |
| QSharedPointer<QWindowsFontEngineData> fontEngineData = sharedFontData(); |
| if (!initDirectWrite(fontEngineData.data())) |
| return 0; |
| |
| IDWriteFontFile *fontFile = 0; |
| void *key = this; |
| |
| HRESULT hres = fontEngineData->directWriteFactory->CreateCustomFontFileReference(&key, |
| sizeof(void *), |
| fontFileLoader.loader(), |
| &fontFile); |
| if (FAILED(hres)) { |
| qErrnoWarning(hres, "%s: CreateCustomFontFileReference failed", __FUNCTION__); |
| return 0; |
| } |
| |
| 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 0; |
| } |
| |
| IDWriteFontFace *directWriteFontFace = 0; |
| 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 0; |
| } |
| |
| fontFile->Release(); |
| |
| fontEngine = new QWindowsFontEngineDirectWrite(directWriteFontFace, |
| pixelSize, |
| fontEngineData); |
| |
| // Get font family from font data |
| fontEngine->fontDef.family = font.familyName(); |
| fontEngine->fontDef.hintingPreference = hintingPreference; |
| |
| directWriteFontFace->Release(); |
| } |
| #endif |
| |
| // Get style and weight info |
| if (fontEngine != 0) { |
| TableDirectory *os2TableEntry = font.tableDirectoryEntry("OS/2"); |
| if (os2TableEntry != 0) { |
| const OS2Table *os2Table = |
| reinterpret_cast<const OS2Table *>(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)); |
| } |
| } |
| |
| qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDATA" << fontData << pixelSize << hintingPreference << fontEngine; |
| return fontEngine; |
| } |
| |
| static QList<quint32> getTrueTypeFontOffsets(const uchar *fontData) |
| { |
| QList<quint32> offsets; |
| const quint32 headerTag = *reinterpret_cast<const quint32 *>(fontData); |
| if (headerTag != MAKE_TAG('t', 't', 'c', 'f')) { |
| if (headerTag != MAKE_TAG(0, 1, 0, 0) |
| && headerTag != MAKE_TAG('O', 'T', 'T', 'O') |
| && headerTag != MAKE_TAG('t', 'r', 'u', 'e') |
| && headerTag != MAKE_TAG('t', 'y', 'p', '1')) |
| return offsets; |
| offsets << 0; |
| return offsets; |
| } |
| const quint32 numFonts = qFromBigEndian<quint32>(fontData + 8); |
| for (uint i = 0; i < numFonts; ++i) { |
| offsets << qFromBigEndian<quint32>(fontData + 12 + i * 4); |
| } |
| return offsets; |
| } |
| |
| static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag, const uchar **table, quint32 *length) |
| { |
| const quint16 numTables = qFromBigEndian<quint16>(data + 4); |
| for (uint i = 0; i < numTables; ++i) { |
| const quint32 offset = 12 + 16 * i; |
| if (*reinterpret_cast<const quint32 *>(data + offset) == tag) { |
| *table = fileBegin + qFromBigEndian<quint32>(data + offset + 8); |
| *length = qFromBigEndian<quint32>(data + offset + 12); |
| return; |
| } |
| } |
| *table = 0; |
| *length = 0; |
| return; |
| } |
| |
| static void getFamiliesAndSignatures(const QByteArray &fontData, |
| QList<QFontNames> *families, |
| QVector<FONTSIGNATURE> *signatures, |
| QVector<QFontValues> *values) |
| { |
| const uchar *data = reinterpret_cast<const uchar *>(fontData.constData()); |
| |
| QList<quint32> offsets = getTrueTypeFontOffsets(data); |
| if (offsets.isEmpty()) |
| return; |
| |
| for (int i = 0; i < offsets.count(); ++i) { |
| const uchar *font = data + offsets.at(i); |
| const uchar *table; |
| quint32 length; |
| getFontTable(data, font, MAKE_TAG('n', 'a', 'm', 'e'), &table, &length); |
| if (!table) |
| continue; |
| QFontNames names = qt_getCanonicalFontNames(table, length); |
| if (names.name.isEmpty()) |
| continue; |
| |
| families->append(std::move(names)); |
| |
| if (values || signatures) |
| getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length); |
| |
| if (values) { |
| QFontValues fontValues; |
| if (table && length >= 64) { |
| // Read in some details about the font, offset calculated based on the specification |
| fontValues.weight = qFromBigEndian<quint16>(table + 4); |
| |
| quint16 fsSelection = qFromBigEndian<quint16>(table + 62); |
| fontValues.isItalic = (fsSelection & 1) != 0; |
| fontValues.isUnderlined = (fsSelection & (1 << 1)) != 0; |
| fontValues.isOverstruck = (fsSelection & (1 << 4)) != 0; |
| } |
| values->append(std::move(fontValues)); |
| } |
| |
| if (signatures) { |
| FONTSIGNATURE signature; |
| if (table && length >= 86) { |
| // Offsets taken from OS/2 table in the TrueType spec |
| signature.fsUsb[0] = qFromBigEndian<quint32>(table + 42); |
| signature.fsUsb[1] = qFromBigEndian<quint32>(table + 46); |
| signature.fsUsb[2] = qFromBigEndian<quint32>(table + 50); |
| signature.fsUsb[3] = qFromBigEndian<quint32>(table + 54); |
| |
| signature.fsCsb[0] = qFromBigEndian<quint32>(table + 78); |
| signature.fsCsb[1] = qFromBigEndian<quint32>(table + 82); |
| } else { |
| memset(&signature, 0, sizeof(signature)); |
| } |
| signatures->append(signature); |
| } |
| } |
| } |
| |
| QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) |
| { |
| WinApplicationFont font; |
| font.fileName = fileName; |
| QVector<FONTSIGNATURE> signatures; |
| QVector<QFontValues> fontValues; |
| QList<QFontNames> families; |
| QStringList familyNames; |
| |
| if (!fontData.isEmpty()) { |
| getFamiliesAndSignatures(fontData, &families, &signatures, &fontValues); |
| if (families.isEmpty()) |
| return familyNames; |
| |
| DWORD dummy = 0; |
| font.handle = |
| AddFontMemResourceEx(const_cast<char *>(fontData.constData()), |
| DWORD(fontData.size()), 0, &dummy); |
| if (font.handle == 0) |
| return QStringList(); |
| |
| // Memory fonts won't show up in enumeration, so do add them the hard way. |
| for (int j = 0; j < families.count(); ++j) { |
| const auto &family = families.at(j); |
| const QString &familyName = family.name; |
| const QString &styleName = family.style; |
| familyNames << familyName; |
| HDC hdc = GetDC(0); |
| LOGFONT lf; |
| memset(&lf, 0, sizeof(LOGFONT)); |
| memcpy(lf.lfFaceName, familyName.utf16(), sizeof(wchar_t) * qMin(LF_FACESIZE - 1, familyName.size())); |
| lf.lfCharSet = DEFAULT_CHARSET; |
| const QFontValues &values = fontValues.at(j); |
| lf.lfWeight = values.weight; |
| if (values.isItalic) |
| lf.lfItalic = TRUE; |
| if (values.isOverstruck) |
| lf.lfStrikeOut = TRUE; |
| if (values.isUnderlined) |
| lf.lfUnderline = TRUE; |
| HFONT hfont = CreateFontIndirect(&lf); |
| HGDIOBJ oldobj = SelectObject(hdc, hfont); |
| |
| TEXTMETRIC textMetrics; |
| GetTextMetrics(hdc, &textMetrics); |
| |
| StoreFontPayload sfp(familyName, this); |
| addFontToDatabase(familyName, styleName, lf, &textMetrics, &signatures.at(j), |
| TRUETYPE_FONTTYPE, &sfp); |
| |
| SelectObject(hdc, oldobj); |
| DeleteObject(hfont); |
| ReleaseDC(0, hdc); |
| } |
| } else { |
| QFile f(fileName); |
| if (!f.open(QIODevice::ReadOnly)) |
| return QStringList(); |
| QByteArray data = f.readAll(); |
| f.close(); |
| |
| getFamiliesAndSignatures(data, &families, nullptr, nullptr); |
| if (families.isEmpty()) |
| return QStringList(); |
| |
| if (AddFontResourceExW((wchar_t*)fileName.utf16(), FR_PRIVATE, 0) == 0) |
| return QStringList(); |
| |
| font.handle = 0; |
| |
| // Fonts based on files are added via populate, as they will show up in font enumeration. |
| for (int j = 0; j < families.count(); ++j) { |
| const QString familyName = families.at(j).name; |
| familyNames << familyName; |
| populateFamily(familyName); |
| } |
| } |
| |
| m_applicationFonts << font; |
| |
| return familyNames; |
| } |
| |
| void QWindowsFontDatabase::removeApplicationFonts() |
| { |
| for (const WinApplicationFont &font : qAsConst(m_applicationFonts)) { |
| if (font.handle) { |
| RemoveFontMemResourceEx(font.handle); |
| } else { |
| RemoveFontResourceExW(reinterpret_cast<LPCWSTR>(font.fileName.utf16()), |
| FR_PRIVATE, nullptr); |
| } |
| } |
| m_applicationFonts.clear(); |
| m_eudcFonts.clear(); |
| } |
| |
| void QWindowsFontDatabase::releaseHandle(void *handle) |
| { |
| const QChar *faceName = reinterpret_cast<const QChar *>(handle); |
| delete[] faceName; |
| } |
| |
| QString QWindowsFontDatabase::fontDir() const |
| { |
| const QString result = QPlatformFontDatabase::fontDir(); |
| qCDebug(lcQpaFonts) << __FUNCTION__ << result; |
| return result; |
| } |
| |
| bool QWindowsFontDatabase::fontsAlwaysScalable() const |
| { |
| return false; |
| } |
| |
| void QWindowsFontDatabase::derefUniqueFont(const QString &uniqueFont) |
| { |
| if (m_uniqueFontData.contains(uniqueFont)) { |
| if (!m_uniqueFontData[uniqueFont].refCount.deref()) { |
| RemoveFontMemResourceEx(m_uniqueFontData[uniqueFont].handle); |
| m_uniqueFontData.remove(uniqueFont); |
| } |
| } |
| } |
| |
| void QWindowsFontDatabase::refUniqueFont(const QString &uniqueFont) |
| { |
| if (m_uniqueFontData.contains(uniqueFont)) |
| m_uniqueFontData[uniqueFont].refCount.ref(); |
| } |
| |
| // ### fixme Qt 6 (QTBUG-58610): See comment at QWindowsFontDatabase::systemDefaultFont() |
| HFONT QWindowsFontDatabase::systemFont() |
| { |
| static const auto stock_sysfont = |
| reinterpret_cast<HFONT>(GetStockObject(DEFAULT_GUI_FONT)); |
| return stock_sysfont; |
| } |
| |
| // Creation functions |
| |
| static const char *other_tryFonts[] = { |
| "Arial", |
| "MS UI Gothic", |
| "Gulim", |
| "SimSun", |
| "PMingLiU", |
| "Arial Unicode MS", |
| 0 |
| }; |
| |
| static const char *jp_tryFonts [] = { |
| "MS UI Gothic", |
| "Arial", |
| "Gulim", |
| "SimSun", |
| "PMingLiU", |
| "Arial Unicode MS", |
| 0 |
| }; |
| |
| static const char *ch_CN_tryFonts [] = { |
| "SimSun", |
| "Arial", |
| "PMingLiU", |
| "Gulim", |
| "MS UI Gothic", |
| "Arial Unicode MS", |
| 0 |
| }; |
| |
| static const char *ch_TW_tryFonts [] = { |
| "PMingLiU", |
| "Arial", |
| "SimSun", |
| "Gulim", |
| "MS UI Gothic", |
| "Arial Unicode MS", |
| 0 |
| }; |
| |
| static const char *kr_tryFonts[] = { |
| "Gulim", |
| "Arial", |
| "PMingLiU", |
| "SimSun", |
| "MS UI Gothic", |
| "Arial Unicode MS", |
| 0 |
| }; |
| |
| static const char **tryFonts = 0; |
| |
| LOGFONT QWindowsFontDatabase::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) && sharedFontData()->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; |
| } |
| |
| QStringList QWindowsFontDatabase::extraTryFontsForFamily(const QString &family) |
| { |
| QStringList result; |
| QFontDatabase db; |
| if (!db.writingSystems(family).contains(QFontDatabase::Symbol)) { |
| if (!tryFonts) { |
| LANGID lid = GetUserDefaultLangID(); |
| switch (lid&0xff) { |
| case LANG_CHINESE: // Chinese |
| if ( lid == 0x0804 || lid == 0x1004) // China mainland and Singapore |
| tryFonts = ch_CN_tryFonts; |
| else |
| tryFonts = ch_TW_tryFonts; // Taiwan, Hong Kong and Macau |
| break; |
| case LANG_JAPANESE: |
| tryFonts = jp_tryFonts; |
| break; |
| case LANG_KOREAN: |
| tryFonts = kr_tryFonts; |
| break; |
| default: |
| tryFonts = other_tryFonts; |
| break; |
| } |
| } |
| QFontDatabase db; |
| const QStringList families = db.families(); |
| const char **tf = tryFonts; |
| while (tf && *tf) { |
| // QTBUG-31689, family might be an English alias for a localized font name. |
| const QString family = QString::fromLatin1(*tf); |
| if (families.contains(family) || db.hasFamily(family)) |
| result << family; |
| ++tf; |
| } |
| } |
| result.append(QStringLiteral("Segoe UI Emoji")); |
| result.append(QStringLiteral("Segoe UI Symbol")); |
| return result; |
| } |
| |
| QString QWindowsFontDatabase::familyForStyleHint(QFont::StyleHint styleHint) |
| { |
| switch (styleHint) { |
| case QFont::Times: |
| return QStringLiteral("Times New Roman"); |
| case QFont::Courier: |
| return QStringLiteral("Courier New"); |
| case QFont::Monospace: |
| return QStringLiteral("Courier New"); |
| case QFont::Cursive: |
| return QStringLiteral("Comic Sans MS"); |
| case QFont::Fantasy: |
| return QStringLiteral("Impact"); |
| case QFont::Decorative: |
| return QStringLiteral("Old English"); |
| case QFont::Helvetica: |
| return QStringLiteral("Arial"); |
| case QFont::System: |
| default: |
| break; |
| } |
| return QStringLiteral("MS Shell Dlg 2"); |
| } |
| |
| QStringList QWindowsFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const |
| { |
| QStringList result; |
| result.append(QWindowsFontDatabase::familyForStyleHint(styleHint)); |
| result.append(m_eudcFonts); |
| result.append(QWindowsFontDatabase::extraTryFontsForFamily(family)); |
| result.append(QPlatformFontDatabase::fallbacksForFamily(family, style, styleHint, script)); |
| |
| qCDebug(lcQpaFonts) << __FUNCTION__ << family << style << styleHint |
| << script << result; |
| return result; |
| } |
| |
| |
| QFontEngine *QWindowsFontDatabase::createEngine(const QFontDef &request, const QString &faceName, |
| int dpi, |
| const QSharedPointer<QWindowsFontEngineData> &data) |
| { |
| QFontEngine *fe = 0; |
| |
| LOGFONT lf = fontDefToLOGFONT(request, faceName); |
| const bool preferClearTypeAA = lf.lfQuality == CLEARTYPE_QUALITY; |
| |
| if (request.stretch != 100) { |
| HFONT hfont = CreateFontIndirect(&lf); |
| if (!hfont) { |
| qErrnoWarning("%s: CreateFontIndirect failed", __FUNCTION__); |
| hfont = QWindowsFontDatabase::systemFont(); |
| } |
| |
| HGDIOBJ oldObj = SelectObject(data->hdc, hfont); |
| TEXTMETRIC tm; |
| if (!GetTextMetrics(data->hdc, &tm)) |
| qErrnoWarning("%s: GetTextMetrics failed", __FUNCTION__); |
| else |
| lf.lfWidth = tm.tmAveCharWidth * request.stretch / 100; |
| SelectObject(data->hdc, oldObj); |
| |
| DeleteObject(hfont); |
| } |
| |
| #if !defined(QT_NO_DIRECTWRITE) |
| if (initDirectWrite(data.data())) { |
| const QString fam = QString::fromWCharArray(lf.lfFaceName); |
| const QString nameSubstitute = QWindowsFontEngineDirectWrite::fontNameSubstitute(fam); |
| if (nameSubstitute != fam) { |
| const int nameSubstituteLength = qMin(nameSubstitute.length(), LF_FACESIZE - 1); |
| memcpy(lf.lfFaceName, nameSubstitute.utf16(), nameSubstituteLength * sizeof(wchar_t)); |
| lf.lfFaceName[nameSubstituteLength] = 0; |
| } |
| |
| HFONT hfont = CreateFontIndirect(&lf); |
| if (!hfont) { |
| qErrnoWarning("%s: CreateFontIndirect failed", __FUNCTION__); |
| } else { |
| HGDIOBJ oldFont = SelectObject(data->hdc, hfont); |
| |
| const QFont::HintingPreference hintingPreference = |
| static_cast<QFont::HintingPreference>(request.hintingPreference); |
| bool useDw = useDirectWrite(hintingPreference, fam); |
| |
| IDWriteFontFace *directWriteFontFace = NULL; |
| HRESULT hr = data->directWriteGdiInterop->CreateFontFaceFromHdc(data->hdc, &directWriteFontFace); |
| if (SUCCEEDED(hr)) { |
| bool isColorFont = false; |
| #if defined(QT_USE_DIRECTWRITE2) |
| IDWriteFontFace2 *directWriteFontFace2 = nullptr; |
| if (SUCCEEDED(directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace2), |
| reinterpret_cast<void **>(&directWriteFontFace2)))) { |
| if (directWriteFontFace2->IsColorFont()) |
| isColorFont = directWriteFontFace2->GetPaletteEntryCount() > 0; |
| |
| directWriteFontFace2->Release(); |
| } |
| #endif |
| useDw = useDw || useDirectWrite(hintingPreference, fam, isColorFont); |
| qCDebug(lcQpaFonts) << __FUNCTION__ << request.family << request.pointSize |
| << "pt" << "hintingPreference=" << hintingPreference << "color=" << isColorFont |
| << dpi << "dpi" << "useDirectWrite=" << useDw; |
| if (useDw) { |
| QWindowsFontEngineDirectWrite *fedw = new QWindowsFontEngineDirectWrite(directWriteFontFace, |
| request.pixelSize, |
| data); |
| |
| wchar_t n[64]; |
| GetTextFace(data->hdc, 64, n); |
| |
| QFontDef fontDef = request; |
| fontDef.family = QString::fromWCharArray(n); |
| |
| if (isColorFont) |
| fedw->glyphFormat = QFontEngine::Format_ARGB; |
| fedw->initFontInfo(fontDef, dpi); |
| fe = fedw; |
| } |
| directWriteFontFace->Release(); |
| } else if (useDw) { |
| const QString errorString = qt_error_string(int(hr)); |
| qWarning().noquote().nospace() << "DirectWrite: CreateFontFaceFromHDC() failed (" |
| << errorString << ") for " << request << ' ' << lf << " dpi=" << dpi; |
| } |
| |
| SelectObject(data->hdc, oldFont); |
| DeleteObject(hfont); |
| } |
| } |
| #endif // QT_NO_DIRECTWRITE |
| |
| if (!fe) { |
| QWindowsFontEngine *few = new QWindowsFontEngine(request.family, lf, data); |
| if (preferClearTypeAA) |
| few->glyphFormat = QFontEngine::Format_A32; |
| few->initFontInfo(request, dpi); |
| fe = few; |
| } |
| |
| return fe; |
| } |
| |
| QFont QWindowsFontDatabase::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(QWindowsFontDatabase::systemFont(), sizeof(lf), &lf); |
| QFont systemFont = QWindowsFontDatabase::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; |
| } |
| |
| QFont QWindowsFontDatabase::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; |
| } |
| |
| static int s_defaultVerticalDPI = 96; // Native Pixels |
| |
| int QWindowsFontDatabase::defaultVerticalDPI() |
| { |
| return s_defaultVerticalDPI; |
| } |
| |
| void QWindowsFontDatabase::setDefaultVerticalDPI(int d) |
| { |
| s_defaultVerticalDPI = d; |
| } |
| |
| bool QWindowsFontDatabase::isPrivateFontFamily(const QString &family) const |
| { |
| return m_eudcFonts.contains(family) || QPlatformFontDatabase::isPrivateFontFamily(family); |
| } |
| |
| QT_END_NAMESPACE |