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