diff options
Diffstat (limited to 'src/gui/text/windows/qwindowsfontdatabase_ft.cpp')
-rw-r--r-- | src/gui/text/windows/qwindowsfontdatabase_ft.cpp | 444 |
1 files changed, 444 insertions, 0 deletions
diff --git a/src/gui/text/windows/qwindowsfontdatabase_ft.cpp b/src/gui/text/windows/qwindowsfontdatabase_ft.cpp new file mode 100644 index 0000000000..cde20eebc2 --- /dev/null +++ b/src/gui/text/windows/qwindowsfontdatabase_ft.cpp @@ -0,0 +1,444 @@ +/**************************************************************************** +** +** 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_ft_p.h" +#include "qwindowsfontdatabase_p.h" + +#include <QtGui/private/qfontengine_ft_p.h> + +#include <ft2build.h> +#include FT_TRUETYPE_TABLES_H + +#include <QtCore/QDir> +#include <QtCore/QDirIterator> +#include <QtCore/QSettings> +#if QT_CONFIG(regularexpression) +#include <QtCore/QRegularExpression> +#endif +#include <QtGui/QGuiApplication> +#include <QtGui/QFontDatabase> + +#include <wchar.h> + +QT_BEGIN_NAMESPACE + +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; +} + +static FontFile * createFontFile(const QString &fileName, int index) +{ + FontFile *fontFile = new FontFile; + fontFile->fileName = fileName; + fontFile->indexValue = index; + return fontFile; +} + +namespace { +struct FontKey +{ + QString fileName; + QStringList fontNames; +}; +} // namespace + +typedef QVector<FontKey> FontKeys; + +static FontKeys &fontKeys() +{ + static FontKeys result; + if (result.isEmpty()) { + const QStringList keys = { QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"), + QStringLiteral("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts") }; + for (const auto key : keys) { + const QSettings fontRegistry(key, QSettings::NativeFormat); + const QStringList allKeys = fontRegistry.allKeys(); + const QString trueType = QStringLiteral("(TrueType)"); +#if QT_CONFIG(regularexpression) + const QRegularExpression sizeListMatch(QStringLiteral("\\s(\\d+,)+\\d+")); + Q_ASSERT(sizeListMatch.isValid()); +#endif + const int size = allKeys.size(); + result.reserve(result.size() + size); + for (int i = 0; i < size; ++i) { + FontKey fontKey; + const QString ®istryFontKey = allKeys.at(i); + fontKey.fileName = fontRegistry.value(registryFontKey).toString(); + QString realKey = registryFontKey; + realKey.remove(trueType); +#if QT_CONFIG(regularexpression) + realKey.remove(sizeListMatch); +#endif + const auto fontNames = QStringRef(&realKey).trimmed().split(QLatin1Char('&')); + fontKey.fontNames.reserve(fontNames.size()); + for (const QStringRef &fontName : fontNames) + fontKey.fontNames.append(fontName.trimmed().toString()); + result.append(fontKey); + } + } + } + return result; +} + +static const FontKey *findFontKey(const QString &name, int *indexIn = nullptr) +{ + const FontKeys &keys = fontKeys(); + for (auto it = keys.constBegin(), cend = keys.constEnd(); it != cend; ++it) { + const int index = it->fontNames.indexOf(name); + if (index >= 0) { + if (indexIn) + *indexIn = index; + return &(*it); + } + } + if (indexIn) + *indexIn = -1; + return nullptr; +} + +static bool addFontToDatabase(QString familyName, + QString styleName, + const QString &fullName, + const LOGFONT &logFont, + const TEXTMETRIC *textmetric, + const FONTSIGNATURE *signature, + int type) +{ + // 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_STREAM + if (lcQpaFonts().isDebugEnabled()) { + QString message; + QTextStream str(&message); + str << __FUNCTION__ << ' ' << familyName << "::" << fullName << ' ' << 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 = familyName; + + 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; + 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) && + faceName == QLatin1String("Segoe UI")) + writingSystems.setSupported(QFontDatabase::Thai, false); + } else { + const QFontDatabase::WritingSystem ws = writingSystemFromCharSet(charSet); + if (ws != QFontDatabase::Any) + writingSystems.setSupported(ws); + } + + int index = 0; + const FontKey *key = findFontKey(fullName, &index); + if (!key) { + // On non-English locales, the styles of the font may be localized in enumeration, but + // not in the registry. + QLocale systemLocale = QLocale::system(); + if (systemLocale.language() != QLocale::C + && systemLocale.language() != QLocale::English + && styleName != QLatin1String("Italic") + && styleName != QLatin1String("Bold")) { + key = findFontKey(qt_getEnglishName(fullName, true), &index); + } + if (!key) + key = findFontKey(faceName, &index); + if (!key && !englishName.isEmpty()) + key = findFontKey(englishName, &index); + if (!key) + return false; + } + QString value = key->fileName; + if (value.isEmpty()) + return false; + + if (!QDir::isAbsolutePath(value)) + value.prepend(QFile::decodeName(qgetenv("windir") + "\\Fonts\\")); + + QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight, style, stretch, + antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); + + // 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(value, index)); + + if (style != QFont::StyleItalic && styleName.isEmpty()) + QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight, QFont::StyleItalic, stretch, + antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); + + 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(value, index)); + + if (!subFamilyName.isEmpty() && familyName != subFamilyName) { + QPlatformFontDatabase::registerFont(subFamilyName, subFamilyStyle, foundryName, weight, + style, stretch, antialias, scalable, size, fixed, writingSystems, createFontFile(value, index)); + } + + 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 faceName = QString::fromWCharArray(f->elfLogFont.lfFaceName); + const QString styleName = QString::fromWCharArray(f->elfStyle); + const QString fullName = QString::fromWCharArray(f->elfFullName); + + // 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; + 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(). + QSet<FontAndStyle> *foundFontAndStyles = reinterpret_cast<QSet<FontAndStyle> *>(lparam); + FontAndStyle fontAndStyle = {faceName, styleName}; + if (foundFontAndStyles->contains(fontAndStyle)) + return 1; + foundFontAndStyles->insert(fontAndStyle); + } + addFontToDatabase(faceName, styleName, fullName, *logFont, textmetric, signature, type); + + // keep on enumerating + return 1; +} + +/*! + \brief Populate font database using EnumFontFamiliesEx(). + + Normally, leaving the name empty should enumerate + all fonts, however, system fonts like "MS Shell Dlg 2" + are only found when specifying the name explicitly. +*/ + +void QWindowsFontDatabaseFT::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; + memset(&lf, 0, sizeof(LOGFONT)); + familyName.toWCharArray(lf.lfFaceName); + lf.lfFaceName[familyName.size()] = 0; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfPitchAndFamily = 0; + QSet<FontAndStyle> foundFontAndStyles; + EnumFontFamiliesEx(dummy, &lf, storeFont, reinterpret_cast<intptr_t>(&foundFontAndStyles), 0); + ReleaseDC(0, dummy); +} + +// Delayed population of font families + +static int QT_WIN_CALLBACK populateFontFamilies(const LOGFONT *logFont, const TEXTMETRIC *textmetric, + DWORD, LPARAM) +{ + const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont); + // the "@family" fonts are just the same as "family". Ignore them. + const wchar_t *faceNameW = f->elfLogFont.lfFaceName; + if (faceNameW[0] && faceNameW[0] != L'@' && wcsncmp(faceNameW, L"WST_", 4)) { + // Register only font families for which a font file exists for delayed population + const bool ttf = textmetric->tmPitchAndFamily & TMPF_TRUETYPE; + const QString faceName = QString::fromWCharArray(faceNameW); + const FontKey *key = findFontKey(faceName); + if (!key) { + key = findFontKey(QString::fromWCharArray(f->elfFullName)); + if (!key && ttf && qt_localizedName(faceName)) + key = findFontKey(qt_getEnglishName(faceName)); + } + if (key) { + QPlatformFontDatabase::registerFontFamily(faceName); + // Register current font's english name as alias + if (ttf && qt_localizedName(faceName)) { + const QString englishName = qt_getEnglishName(faceName); + if (!englishName.isEmpty()) + QPlatformFontDatabase::registerAliasToFontFamily(faceName, englishName); + } + } + } + return 1; // continue +} + +void QWindowsFontDatabaseFT::populateFontDatabase() +{ + 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); +} + +QFontEngine * QWindowsFontDatabaseFT::fontEngine(const QFontDef &fontDef, void *handle) +{ + QFontEngine *fe = QFreeTypeFontDatabase::fontEngine(fontDef, handle); + qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef.family << fe << handle; + return fe; +} + +QFontEngine *QWindowsFontDatabaseFT::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) +{ + QFontEngine *fe = QFreeTypeFontDatabase::fontEngine(fontData, pixelSize, hintingPreference); + qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDATA" << fontData << pixelSize << hintingPreference << fe; + return fe; +} + +QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const +{ + QStringList result; + result.append(QWindowsFontDatabaseBase::familyForStyleHint(styleHint)); + result.append(QWindowsFontDatabaseBase::extraTryFontsForFamily(family)); + result.append(QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script)); + + qCDebug(lcQpaFonts) << __FUNCTION__ << family << style << styleHint + << script << result; + + return result; +} +QString QWindowsFontDatabaseFT::fontDir() const +{ + const QString result = QLatin1String(qgetenv("windir")) + QLatin1String("/Fonts");//QPlatformFontDatabase::fontDir(); + qCDebug(lcQpaFonts) << __FUNCTION__ << result; + return result; +} + +QFont QWindowsFontDatabaseFT::defaultFont() const +{ + return QWindowsFontDatabase::systemDefaultFont(); +} + +QT_END_NAMESPACE |