Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwindowsfontdatabase_ft.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7#include <QtGui/private/qfontengine_ft_p.h>
8
9#include <ft2build.h>
10#include FT_TRUETYPE_TABLES_H
11
12#include <QtCore/QDir>
13#include <QtCore/QDirIterator>
14#include <QtCore/QSettings>
15#if QT_CONFIG(regularexpression)
16#include <QtCore/QRegularExpression>
17#endif
18#include <QtCore/private/qduplicatetracker_p.h>
19
20#include <QtGui/QGuiApplication>
21#include <QtGui/QFontDatabase>
22
23#include <wchar.h>
24
25QT_BEGIN_NAMESPACE
26
27using namespace Qt::StringLiterals;
28
30{
31 switch (charSet) {
32 case ANSI_CHARSET:
33 case EASTEUROPE_CHARSET:
34 case BALTIC_CHARSET:
35 case TURKISH_CHARSET:
36 return QFontDatabase::Latin;
37 case GREEK_CHARSET:
38 return QFontDatabase::Greek;
39 case RUSSIAN_CHARSET:
40 return QFontDatabase::Cyrillic;
41 case HEBREW_CHARSET:
42 return QFontDatabase::Hebrew;
43 case ARABIC_CHARSET:
44 return QFontDatabase::Arabic;
45 case THAI_CHARSET:
46 return QFontDatabase::Thai;
47 case GB2312_CHARSET:
48 return QFontDatabase::SimplifiedChinese;
49 case CHINESEBIG5_CHARSET:
50 return QFontDatabase::TraditionalChinese;
51 case SHIFTJIS_CHARSET:
52 return QFontDatabase::Japanese;
53 case HANGUL_CHARSET:
54 case JOHAB_CHARSET:
55 return QFontDatabase::Korean;
56 case VIETNAMESE_CHARSET:
57 return QFontDatabase::Vietnamese;
58 case SYMBOL_CHARSET:
59 return QFontDatabase::Symbol;
60 default:
61 break;
62 }
63 return QFontDatabase::Any;
64}
65
66static FontFile * createFontFile(const QString &fileName, int index)
67{
68 FontFile *fontFile = new FontFile;
69 fontFile->fileName = fileName;
70 fontFile->indexValue = index;
71 return fontFile;
72}
73
74namespace {
75struct FontKey
76{
77 QString fileName;
78 QStringList fontNames;
79};
80} // namespace
81
82using FontKeys = QList<FontKey>;
83
85{
86 static FontKeys result;
87 if (result.isEmpty()) {
88 const QStringList keys = { QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"),
89 QStringLiteral("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts") };
90 for (const auto &key : keys) {
91 const QSettings fontRegistry(key, QSettings::NativeFormat);
92 const QStringList allKeys = fontRegistry.allKeys();
93 const QString trueType = QStringLiteral("(TrueType)");
94#if QT_CONFIG(regularexpression)
95 const QRegularExpression sizeListMatch(QStringLiteral("\\s(\\d+,)+\\d+"));
96 Q_ASSERT(sizeListMatch.isValid());
97#endif
98 const int size = allKeys.size();
99 result.reserve(result.size() + size);
100 for (int i = 0; i < size; ++i) {
101 FontKey fontKey;
102 const QString &registryFontKey = allKeys.at(i);
103 fontKey.fileName = fontRegistry.value(registryFontKey).toString();
104 QString realKey = registryFontKey;
105 realKey.remove(trueType);
106#if QT_CONFIG(regularexpression)
107 realKey.remove(sizeListMatch);
108#endif
109 const auto fontNames = QStringView(realKey).trimmed().split(u'&');
110 fontKey.fontNames.reserve(fontNames.size());
111 for (const auto &fontName : fontNames)
112 fontKey.fontNames.append(fontName.trimmed().toString());
113 result.append(fontKey);
114 }
115 }
116 }
117 return result;
118}
119
120static const FontKey *findFontKey(const QString &name, int *indexIn = nullptr)
121{
122 const FontKeys &keys = fontKeys();
123 for (auto it = keys.constBegin(), cend = keys.constEnd(); it != cend; ++it) {
124 const int index = it->fontNames.indexOf(name);
125 if (index >= 0) {
126 if (indexIn)
127 *indexIn = index;
128 return &(*it);
129 }
130 }
131 if (indexIn)
132 *indexIn = -1;
133 return nullptr;
134}
135
136static bool detectColorFont(const QByteArray &filename)
137{
138#if defined(FT_HAS_COLOR)
139 FT_Library library = qt_getFreetype();
140
141 FT_Face face;
142 if (FT_New_Face(library, filename.constData(), 0, &face) == FT_Err_Ok) {
143 bool hasColor = FT_HAS_COLOR(face);
144 FT_Done_Face(face);
145 return hasColor;
146 }
147#else
148 Q_UNUSED(filename);
149#endif
150 return false;
151}
152
153static bool addFontToDatabase(QString familyName,
154 QString styleName,
155 const QString &fullName,
156 const LOGFONT &logFont,
157 const TEXTMETRIC *textmetric,
158 const FONTSIGNATURE *signature,
159 int type)
160{
161 // the "@family" fonts are just the same as "family". Ignore them.
162 if (familyName.isEmpty() || familyName.at(0) == u'@' || familyName.startsWith("WST_"_L1))
163 return false;
164
165 uchar charSet = logFont.lfCharSet;
166
167 static const int SMOOTH_SCALABLE = 0xffff;
168 const QString foundryName; // No such concept.
169 const bool fixed = !(textmetric->tmPitchAndFamily & TMPF_FIXED_PITCH);
170 const bool ttf = (textmetric->tmPitchAndFamily & TMPF_TRUETYPE);
171 const bool scalable = textmetric->tmPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE);
172 const int size = scalable ? SMOOTH_SCALABLE : textmetric->tmHeight;
173 const QFont::Style style = textmetric->tmItalic ? QFont::StyleItalic : QFont::StyleNormal;
174 const bool antialias = false;
175 const QFont::Weight weight = static_cast<QFont::Weight>(textmetric->tmWeight);
176 const QFont::Stretch stretch = QFont::Unstretched;
177
178#ifndef QT_NO_DEBUG_STREAM
179 if (lcQpaFonts().isDebugEnabled()) {
180 QString message;
181 QTextStream str(&message);
182 str << __FUNCTION__ << ' ' << familyName << "::" << fullName << ' ' << charSet << " TTF=" << ttf;
183 if (type & DEVICE_FONTTYPE)
184 str << " DEVICE";
185 if (type & RASTER_FONTTYPE)
186 str << " RASTER";
187 if (type & TRUETYPE_FONTTYPE)
188 str << " TRUETYPE";
189 str << " scalable=" << scalable << " Size=" << size
190 << " Style=" << style << " Weight=" << weight
191 << " stretch=" << stretch;
192 qCDebug(lcQpaFonts) << message;
193 }
194#endif
195
196 QString englishName;
197 QString faceName = familyName;
198
199 QString subFamilyName;
200 QString subFamilyStyle;
201 // Look-up names registered in the font
202 QFontNames canonicalNames = qt_getCanonicalFontNames(logFont);
203 if (qt_localizedName(familyName) && !canonicalNames.name.isEmpty())
204 englishName = canonicalNames.name;
205 if (!canonicalNames.preferredName.isEmpty()) {
206 subFamilyName = familyName;
207 subFamilyStyle = styleName;
208 familyName = canonicalNames.preferredName;
209 styleName = canonicalNames.preferredStyle;
210 }
211
212 QSupportedWritingSystems writingSystems;
213 if (type & TRUETYPE_FONTTYPE) {
214 Q_ASSERT(signature);
215 quint32 unicodeRange[4] = {
216 signature->fsUsb[0], signature->fsUsb[1],
217 signature->fsUsb[2], signature->fsUsb[3]
218 };
219 quint32 codePageRange[2] = {
220 signature->fsCsb[0], signature->fsCsb[1]
221 };
222 writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
223 // ### Hack to work around problem with Thai text on Windows 7. Segoe UI contains
224 // the symbol for Baht, and Windows thus reports that it supports the Thai script.
225 // Since it's the default UI font on this platform, most widgets will be unable to
226 // display Thai text by default. As a temporary work around, we special case Segoe UI
227 // and remove the Thai script from its list of supported writing systems.
228 if (writingSystems.supported(QFontDatabase::Thai) && faceName == "Segoe UI"_L1)
229 writingSystems.setSupported(QFontDatabase::Thai, false);
230 } else {
231 const QFontDatabase::WritingSystem ws = writingSystemFromCharSet(charSet);
232 if (ws != QFontDatabase::Any)
233 writingSystems.setSupported(ws);
234 }
235
236 int index = 0;
237 const FontKey *key = findFontKey(fullName, &index);
238 if (!key) {
239 // On non-English locales, the styles of the font may be localized in enumeration, but
240 // not in the registry.
241 QLocale systemLocale = QLocale::system();
242 if (systemLocale.language() != QLocale::C
243 && systemLocale.language() != QLocale::English
244 && styleName != "Italic"_L1
245 && styleName != "Bold"_L1) {
246 key = findFontKey(qt_getEnglishName(fullName, true), &index);
247 }
248 if (!key)
249 key = findFontKey(faceName, &index);
250 if (!key && !englishName.isEmpty())
251 key = findFontKey(englishName, &index);
252 if (!key)
253 return false;
254 }
255 QString value = key->fileName;
256 if (value.isEmpty())
257 return false;
258
259 if (!QDir::isAbsolutePath(value))
260 value.prepend(QFile::decodeName(qgetenv("windir") + "\\Fonts\\"));
261
262 const bool color = detectColorFont(value.toLocal8Bit());
263
264 QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight, style, stretch,
265 antialias, scalable, size, fixed, color, writingSystems, createFontFile(value, index));
266
267 // add fonts windows can generate for us:
268 if (weight <= QFont::DemiBold && styleName.isEmpty())
269 QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, style, stretch,
270 antialias, scalable, size, fixed, color, writingSystems, createFontFile(value, index));
271
272 if (style != QFont::StyleItalic && styleName.isEmpty())
273 QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, weight, QFont::StyleItalic, stretch,
274 antialias, scalable, size, fixed, color, writingSystems, createFontFile(value, index));
275
276 if (weight <= QFont::DemiBold && style != QFont::StyleItalic && styleName.isEmpty())
277 QPlatformFontDatabase::registerFont(familyName, QString(), foundryName, QFont::Bold, QFont::StyleItalic, stretch,
278 antialias, scalable, size, fixed, color, writingSystems, createFontFile(value, index));
279
280 if (!subFamilyName.isEmpty() && familyName != subFamilyName) {
281 QPlatformFontDatabase::registerFont(subFamilyName, subFamilyStyle, foundryName, weight,
282 style, stretch, antialias, scalable, size, fixed, color, writingSystems, createFontFile(value, index));
283 }
284
285 if (!englishName.isEmpty() && englishName != familyName)
286 QPlatformFontDatabase::registerAliasToFontFamily(familyName, englishName);
287
288 return true;
289}
290
291static int QT_WIN_CALLBACK storeFont(const LOGFONT *logFont, const TEXTMETRIC *textmetric,
292 DWORD type, LPARAM lparam)
293{
294 const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont);
295 const QString faceName = QString::fromWCharArray(f->elfLogFont.lfFaceName);
296 const QString styleName = QString::fromWCharArray(f->elfStyle);
297 const QString fullName = QString::fromWCharArray(f->elfFullName);
298
299 // NEWTEXTMETRICEX (passed for TT fonts) is a NEWTEXTMETRIC, which according
300 // to the documentation is identical to a TEXTMETRIC except for the last four
301 // members, which we don't use anyway
302 const FONTSIGNATURE *signature = nullptr;
303 if (type & TRUETYPE_FONTTYPE) {
304 signature = &reinterpret_cast<const NEWTEXTMETRICEX *>(textmetric)->ntmFontSig;
305 // We get a callback for each script-type supported, but we register them all
306 // at once using the signature, so we only need one call to addFontToDatabase().
307 auto foundFontAndStyles = reinterpret_cast<QDuplicateTracker<FontAndStyle> *>(lparam);
308 if (foundFontAndStyles->hasSeen({faceName, styleName}))
309 return 1;
310 }
311 addFontToDatabase(faceName, styleName, fullName, *logFont, textmetric, signature, type);
312
313 // keep on enumerating
314 return 1;
315}
316
317bool QWindowsFontDatabaseFT::populateFamilyAliases(const QString &missingFamily)
318{
319 Q_UNUSED(missingFamily);
320
321 if (m_hasPopulatedAliases)
322 return false;
323
324 QStringList families = QFontDatabase::families();
325 for (const QString &family : families)
326 populateFamily(family);
327 m_hasPopulatedAliases = true;
328
329 return true;
330}
331
332/*
333 \brief Populates the font database using EnumFontFamiliesEx().
334
335 Normally, leaving the name empty should enumerate
336 all fonts, however, system fonts like "MS Shell Dlg 2"
337 are only found when specifying the name explicitly.
338*/
339
340void QWindowsFontDatabaseFT::populateFamily(const QString &familyName)
341{
342 qCDebug(lcQpaFonts) << familyName;
343 if (familyName.size() >= LF_FACESIZE) {
344 qCWarning(lcQpaFonts) << "Unable to enumerate family '" << familyName << '\'';
345 return;
346 }
347 HDC dummy = GetDC(0);
348 LOGFONT lf;
349 memset(&lf, 0, sizeof(LOGFONT));
350 familyName.toWCharArray(lf.lfFaceName);
351 lf.lfFaceName[familyName.size()] = 0;
352 lf.lfCharSet = DEFAULT_CHARSET;
353 lf.lfPitchAndFamily = 0;
354 QDuplicateTracker<FontAndStyle> foundFontAndStyles;
355 EnumFontFamiliesEx(dummy, &lf, storeFont, reinterpret_cast<intptr_t>(&foundFontAndStyles), 0);
356 ReleaseDC(0, dummy);
357}
358
359// Delayed population of font families
360
361static int QT_WIN_CALLBACK populateFontFamilies(const LOGFONT *logFont, const TEXTMETRIC *textmetric,
362 DWORD, LPARAM)
363{
364 const ENUMLOGFONTEX *f = reinterpret_cast<const ENUMLOGFONTEX *>(logFont);
365 // the "@family" fonts are just the same as "family". Ignore them.
366 const wchar_t *faceNameW = f->elfLogFont.lfFaceName;
367 if (faceNameW[0] && faceNameW[0] != L'@' && wcsncmp(faceNameW, L"WST_", 4)) {
368 // Register only font families for which a font file exists for delayed population
369 const bool ttf = textmetric->tmPitchAndFamily & TMPF_TRUETYPE;
370 const QString faceName = QString::fromWCharArray(faceNameW);
371 const FontKey *key = findFontKey(faceName);
372 if (!key) {
373 key = findFontKey(QString::fromWCharArray(f->elfFullName));
374 if (!key && ttf && qt_localizedName(faceName))
375 key = findFontKey(qt_getEnglishName(faceName));
376 }
377 if (key) {
378 QPlatformFontDatabase::registerFontFamily(faceName);
379 // Register current font's english name as alias
380 if (ttf && qt_localizedName(faceName)) {
381 const QString englishName = qt_getEnglishName(faceName);
382 if (!englishName.isEmpty())
383 QPlatformFontDatabase::registerAliasToFontFamily(faceName, englishName);
384 }
385 }
386 }
387 return 1; // continue
388}
389
390void QWindowsFontDatabaseFT::populateFontDatabase()
391{
392 HDC dummy = GetDC(0);
393 LOGFONT lf;
394 lf.lfCharSet = DEFAULT_CHARSET;
395 lf.lfFaceName[0] = 0;
396 lf.lfPitchAndFamily = 0;
397 EnumFontFamiliesEx(dummy, &lf, populateFontFamilies, 0, 0);
398 ReleaseDC(0, dummy);
399 // Work around EnumFontFamiliesEx() not listing the system font
400 const QString systemDefaultFamily = QWindowsFontDatabase::systemDefaultFont().families().constFirst();
401 if (QPlatformFontDatabase::resolveFontFamilyAlias(systemDefaultFamily) == systemDefaultFamily)
402 QPlatformFontDatabase::registerFontFamily(systemDefaultFamily);
403}
404
405QFontEngine * QWindowsFontDatabaseFT::fontEngine(const QFontDef &fontDef, void *handle)
406{
407 QFontEngine *fe = QFreeTypeFontDatabase::fontEngine(fontDef, handle);
408 qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef.families.constFirst() << fe << handle;
409 return fe;
410}
411
412QFontEngine *QWindowsFontDatabaseFT::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
413{
414 QFontEngine *fe = QFreeTypeFontDatabase::fontEngine(fontData, pixelSize, hintingPreference);
415 qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDATA" << fontData << pixelSize << hintingPreference << fe;
416 return fe;
417}
418
419QStringList QWindowsFontDatabaseFT::fallbacksForFamily(const QString &family,
420 QFont::Style style,
421 QFont::StyleHint styleHint,
422 QFontDatabasePrivate::ExtendedScript script) const
423{
424 QStringList result;
425 result.append(QWindowsFontDatabaseBase::familiesForScript(script));
426 result.append(QWindowsFontDatabaseBase::familyForStyleHint(styleHint));
427 result.append(QWindowsFontDatabaseBase::extraTryFontsForFamily(family));
428 result.append(QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script));
429
430 qCDebug(lcQpaFonts) << __FUNCTION__ << family << style << styleHint
431 << script << result;
432
433 return result;
434}
435QString QWindowsFontDatabaseFT::fontDir() const
436{
437 const QString result = QLatin1StringView(qgetenv("windir")) + "/Fonts"_L1;//QPlatformFontDatabase::fontDir();
438 qCDebug(lcQpaFonts) << __FUNCTION__ << result;
439 return result;
440}
441
442QFont QWindowsFontDatabaseFT::defaultFont() const
443{
444 return QWindowsFontDatabase::systemDefaultFont();
445}
446
447QT_END_NAMESPACE
static FontKeys & fontKeys()
static bool detectColorFont(const QByteArray &filename)
static FontFile * createFontFile(const QString &fileName, int index)
static QFontDatabase::WritingSystem writingSystemFromCharSet(uchar charSet)
static bool addFontToDatabase(QString familyName, QString styleName, const QString &fullName, const LOGFONT &logFont, const TEXTMETRIC *textmetric, const FONTSIGNATURE *signature, int type)
static const FontKey * findFontKey(const QString &name, int *indexIn=nullptr)