/**************************************************************************** ** ** 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 #include #include #include #include #include #include #include #include #include #include #include #include #if QT_CONFIG(directwrite) # if QT_CONFIG(directwrite3) # include "qwindowsdirectwritefontdatabase_p.h" # endif # include # include # include "qwindowsfontenginedirectwrite_p.h" #endif QT_BEGIN_NAMESPACE #if QT_CONFIG(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 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 /*! \class QWindowsFontEngineData \brief Static constant data shared by the font engines. \internal */ 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; } 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; } /*! \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.families.first() << '"'; 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; } void QWindowsFontDatabase::debugFormat(QDebug &d, const LOGFONT &lf) { d << "LOGFONT(\"" << QString::fromWCharArray(lf.lfFaceName) << "\", lfWidth=" << lf.lfWidth << ", lfHeight=" << lf.lfHeight << ')'; } QDebug operator<<(QDebug d, const LOGFONT &lf) { QDebugStateSaver saver(d); d.nospace(); d.noquote(); QWindowsFontDatabase::debugFormat(d, lf); 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(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(faceNamePtr), faceName.utf16(), sizeof(wchar_t) * nameLength); faceNamePtr[nameLength] = u'\0'; } return faceNamePtr; } namespace { struct StoreFontPayload { StoreFontPayload(const QString &family, QWindowsFontDatabase *fontDatabase) : populatedFontFamily(family) , windowsFontDatabase(fontDatabase) {} QString populatedFontFamily; QDuplicateTracker 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 unreliableTextMetrics = type == 0; const bool scalable = (textmetric->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE)) && !unreliableTextMetrics; 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 = static_cast(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(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(lparam); Q_ASSERT(sfp != nullptr); if (type & TRUETYPE_FONTTYPE) { signature = &reinterpret_cast(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(). if (sfp->foundFontAndStyles.hasSeen({familyName, styleName})) return 1; } addFontToDatabase(familyName, styleName, *logFont, textmetric, signature, type, sfp); // keep on enumerating return 1; } bool QWindowsFontDatabase::populateFamilyAliases(const QString &missingFamily) { Q_UNUSED(missingFamily); if (m_hasPopulatedAliases) return false; QStringList families = QFontDatabase::families(); for (const QString &family : families) populateFamily(family); m_hasPopulatedAliases = true; return true; } void QWindowsFontDatabase::populateFamily(const QString &familyName) { qCDebug(lcQpaFonts) << familyName; if (familyName.size() >= LF_FACESIZE) { // Field length of LOGFONT::lfFaceName qCDebug(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(&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(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. const QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().families().first(); if (QPlatformFontDatabase::resolveFontFamilyAlias(systemDefaultFamily) == systemDefaultFamily) QPlatformFontDatabase::registerFontFamily(systemDefaultFamily); addDefaultEUDCFont(); } QWindowsFontDatabase::QWindowsFontDatabase() { // Properties accessed by QWin32PrintEngine (Qt Print Support) static const int hfontMetaTypeId = qRegisterMetaType(); static const int logFontMetaTypeId = qRegisterMetaType(); Q_UNUSED(hfontMetaTypeId); Q_UNUSED(logFontMetaTypeId); if (lcQpaFonts().isDebugEnabled()) { QSharedPointer d = data(); qCDebug(lcQpaFonts) << __FUNCTION__ << "Clear type: " << d->clearTypeEnabled << "gamma: " << d->fontSmoothingGamma; } } QWindowsFontDatabase::~QWindowsFontDatabase() { removeApplicationFonts(); } QFontEngine * QWindowsFontDatabase::fontEngine(const QFontDef &fontDef, void *handle) { const QString faceName(static_cast(handle)); QFontEngine *fe = QWindowsFontDatabase::createEngine(fontDef, faceName, defaultVerticalDPI(), data()); 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 QT_CONFIG(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(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(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.families = QStringList(uniqueFamilyName); request.pixelSize = pixelSize; request.styleStrategy = QFont::PreferMatch; request.hintingPreference = hintingPreference; request.stretch = QFont::Unstretched; fontEngine = QWindowsFontDatabase::createEngine(request, QString(), defaultVerticalDPI(), data()); if (fontEngine) { if (request.families != fontEngine->fontDef.families) { qWarning("%s: Failed to load font. Got fallback instead: %s", __FUNCTION__, qPrintable(fontEngine->fontDef.families.first())); 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(fontEngine)->setUniqueFamilyName(uniqueFamilyName); fontEngine->fontDef.families = QStringList(actualFontName); break; #if QT_CONFIG(directwrite) && QT_CONFIG(direct2d) case QFontEngine::DirectWrite: static_cast(fontEngine)->setUniqueFamilyName(uniqueFamilyName); fontEngine->fontDef.families = QStringList(actualFontName); break; #endif // directwrite && direct2d 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); } } // Get style and weight info if (fontEngine != nullptr) font.updateFromOS2Table(fontEngine); } #if QT_CONFIG(directwrite) && QT_CONFIG(direct2d) else { fontEngine = QWindowsFontDatabaseBase::fontEngine(fontData, pixelSize, hintingPreference); } #endif // directwrite && direct2d qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDATA" << fontData << pixelSize << hintingPreference << fontEngine; return fontEngine; } static QList getTrueTypeFontOffsets(const uchar *fontData) { QList offsets; const quint32 headerTag = *reinterpret_cast(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(fontData + 8); for (uint i = 0; i < numFonts; ++i) { offsets << qFromBigEndian(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(data + 4); for (uint i = 0; i < numTables; ++i) { const quint32 offset = 12 + 16 * i; if (*reinterpret_cast(data + offset) == tag) { *table = fileBegin + qFromBigEndian(data + offset + 8); *length = qFromBigEndian(data + offset + 12); return; } } *table = 0; *length = 0; return; } static void getFamiliesAndSignatures(const QByteArray &fontData, QList *families, QList *signatures, QList *values) { const uchar *data = reinterpret_cast(fontData.constData()); QList 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(table + 4); quint16 fsSelection = qFromBigEndian(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(table + 42); signature.fsUsb[1] = qFromBigEndian(table + 46); signature.fsUsb[2] = qFromBigEndian(table + 50); signature.fsUsb[3] = qFromBigEndian(table + 54); signature.fsCsb[0] = qFromBigEndian(table + 78); signature.fsCsb[1] = qFromBigEndian(table + 82); } else { memset(&signature, 0, sizeof(signature)); } signatures->append(signature); } } } QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont) { WinApplicationFont font; font.fileName = fileName; QList signatures; QList fontValues; QList families; QStringList familyNames; if (!fontData.isEmpty()) { getFamiliesAndSignatures(fontData, &families, &signatures, &fontValues); if (families.isEmpty()) return familyNames; DWORD dummy = 0; font.handle = AddFontMemResourceEx(const_cast(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); if (applicationFont != nullptr) { QFontDatabasePrivate::ApplicationFont::Properties properties; properties.style = values.isItalic ? QFont::StyleItalic : QFont::StyleNormal; properties.weight = static_cast(values.weight); properties.familyName = familyName; properties.styleName = styleName; applicationFont->properties.append(properties); } 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, applicationFont != nullptr ? &fontValues : 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; if (applicationFont != nullptr) { const QString &styleName = families.at(j).style; const QFontValues &values = fontValues.at(j); QFontDatabasePrivate::ApplicationFont::Properties properties; properties.style = values.isItalic ? QFont::StyleItalic : QFont::StyleNormal; properties.weight = static_cast(values.weight); properties.familyName = familyName; properties.styleName = styleName; applicationFont->properties.append(properties); } 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(font.fileName.utf16()), FR_PRIVATE, nullptr); } } m_applicationFonts.clear(); m_eudcFonts.clear(); } void QWindowsFontDatabase::releaseHandle(void *handle) { const QChar *faceName = reinterpret_cast(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(); } QStringList QWindowsFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const { QStringList result; result.append(familyForStyleHint(styleHint)); result.append(m_eudcFonts); result.append(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 &data) { QFontEngine *fe = nullptr; 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 QT_CONFIG(directwrite) && QT_CONFIG(direct2d) if (data->directWriteFactory != nullptr) { 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(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 QT_CONFIG(direct2d) IDWriteFontFace2 *directWriteFontFace2 = nullptr; if (SUCCEEDED(directWriteFontFace->QueryInterface(__uuidof(IDWriteFontFace2), reinterpret_cast(&directWriteFontFace2)))) { if (directWriteFontFace2->IsColorFont()) isColorFont = directWriteFontFace2->GetPaletteEntryCount() > 0; directWriteFontFace2->Release(); } #endif // direct2d useDw = useDw || useDirectWrite(hintingPreference, fam, isColorFont); qCDebug(lcQpaFonts) << __FUNCTION__ << request.families.first() << 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.families = QStringList(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 // directwrite direct2d if (!fe) { QWindowsFontEngine *few = new QWindowsFontEngine(request.families.first(), lf, data); if (preferClearTypeAA) few->glyphFormat = QFontEngine::Format_A32; few->initFontInfo(request, dpi); fe = few; } return fe; } bool QWindowsFontDatabase::isPrivateFontFamily(const QString &family) const { return m_eudcFonts.contains(family) || QPlatformFontDatabase::isPrivateFontFamily(family); } QT_END_NAMESPACE