1/****************************************************************************
2**
3** Copyright (C) 2019 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 "qfontconfigdatabase_p.h"
41#include "qfontenginemultifontconfig_p.h"
42
43#include <QtFontDatabaseSupport/private/qfontengine_ft_p.h>
44
45#include <QtCore/QList>
46#include <QtCore/QElapsedTimer>
47#include <QtCore/QFile>
48
49#include <qpa/qplatformnativeinterface.h>
50#include <qpa/qplatformscreen.h>
51#include <qpa/qplatformintegration.h>
52#include <qpa/qplatformservices.h>
53
54#include <QtGui/private/qguiapplication_p.h>
55#include <QtGui/private/qhighdpiscaling_p.h>
56
57#include <QtGui/qguiapplication.h>
58
59#include <QtCore/private/qduplicatetracker_p.h>
60
61#include <fontconfig/fontconfig.h>
62#if FC_VERSION >= 20402
63#include <fontconfig/fcfreetype.h>
64#endif
65
66QT_BEGIN_NAMESPACE
67
68static const int maxWeight = 99;
69
70static inline int mapToQtWeightForRange(int fcweight, int fcLower, int fcUpper, int qtLower, int qtUpper)
71{
72 return qtLower + ((fcweight - fcLower) * (qtUpper - qtLower)) / (fcUpper - fcLower);
73}
74
75static inline int weightFromFcWeight(int fcweight)
76{
77 // Font Config uses weights from 0 to 215 (the highest enum value) while QFont ranges from
78 // 0 to 99. The spacing between the values for the enums are uneven so a linear mapping from
79 // Font Config values to Qt would give surprising results. So, we do a piecewise linear
80 // mapping. This ensures that where there is a corresponding enum on both sides (for example
81 // FC_WEIGHT_DEMIBOLD and QFont::DemiBold) we map one to the other but other values map
82 // to intermediate Qt weights.
83
84 if (fcweight <= FC_WEIGHT_THIN)
85 return QFont::Thin;
86 if (fcweight <= FC_WEIGHT_ULTRALIGHT)
87 return mapToQtWeightForRange(fcweight, FC_WEIGHT_THIN, FC_WEIGHT_ULTRALIGHT, qtLower: QFont::Thin, qtUpper: QFont::ExtraLight);
88 if (fcweight <= FC_WEIGHT_LIGHT)
89 return mapToQtWeightForRange(fcweight, FC_WEIGHT_ULTRALIGHT, FC_WEIGHT_LIGHT, qtLower: QFont::ExtraLight, qtUpper: QFont::Light);
90 if (fcweight <= FC_WEIGHT_NORMAL)
91 return mapToQtWeightForRange(fcweight, FC_WEIGHT_LIGHT, FC_WEIGHT_NORMAL, qtLower: QFont::Light, qtUpper: QFont::Normal);
92 if (fcweight <= FC_WEIGHT_MEDIUM)
93 return mapToQtWeightForRange(fcweight, FC_WEIGHT_NORMAL, FC_WEIGHT_MEDIUM, qtLower: QFont::Normal, qtUpper: QFont::Medium);
94 if (fcweight <= FC_WEIGHT_DEMIBOLD)
95 return mapToQtWeightForRange(fcweight, FC_WEIGHT_MEDIUM, FC_WEIGHT_DEMIBOLD, qtLower: QFont::Medium, qtUpper: QFont::DemiBold);
96 if (fcweight <= FC_WEIGHT_BOLD)
97 return mapToQtWeightForRange(fcweight, FC_WEIGHT_DEMIBOLD, FC_WEIGHT_BOLD, qtLower: QFont::DemiBold, qtUpper: QFont::Bold);
98 if (fcweight <= FC_WEIGHT_ULTRABOLD)
99 return mapToQtWeightForRange(fcweight, FC_WEIGHT_BOLD, FC_WEIGHT_ULTRABOLD, qtLower: QFont::Bold, qtUpper: QFont::ExtraBold);
100 if (fcweight <= FC_WEIGHT_BLACK)
101 return mapToQtWeightForRange(fcweight, FC_WEIGHT_ULTRABOLD, FC_WEIGHT_BLACK, qtLower: QFont::ExtraBold, qtUpper: QFont::Black);
102 if (fcweight <= FC_WEIGHT_ULTRABLACK)
103 return mapToQtWeightForRange(fcweight, FC_WEIGHT_BLACK, FC_WEIGHT_ULTRABLACK, qtLower: QFont::Black, qtUpper: maxWeight);
104 return maxWeight;
105}
106
107static inline int stretchFromFcWidth(int fcwidth)
108{
109 // Font Config enums for width match pretty closely with those used by Qt so just use
110 // Font Config values directly while enforcing the same limits imposed by QFont.
111 const int maxStretch = 4000;
112 int qtstretch;
113 if (fcwidth < 1)
114 qtstretch = 1;
115 else if (fcwidth > maxStretch)
116 qtstretch = maxStretch;
117 else
118 qtstretch = fcwidth;
119
120 return qtstretch;
121}
122
123static const char specialLanguages[][6] = {
124 "", // Unknown
125 "", // Inherited
126 "", // Common
127 "en", // Latin
128 "el", // Greek
129 "ru", // Cyrillic
130 "hy", // Armenian
131 "he", // Hebrew
132 "ar", // Arabic
133 "syr", // Syriac
134 "dv", // Thaana
135 "hi", // Devanagari
136 "bn", // Bengali
137 "pa", // Gurmukhi
138 "gu", // Gujarati
139 "or", // Oriya
140 "ta", // Tamil
141 "te", // Telugu
142 "kn", // Kannada
143 "ml", // Malayalam
144 "si", // Sinhala
145 "th", // Thai
146 "lo", // Lao
147 "bo", // Tibetan
148 "my", // Myanmar
149 "ka", // Georgian
150 "ko", // Hangul
151 "am", // Ethiopic
152 "chr", // Cherokee
153 "cr", // CanadianAboriginal
154 "sga", // Ogham
155 "non", // Runic
156 "km", // Khmer
157 "mn", // Mongolian
158 "ja", // Hiragana
159 "ja", // Katakana
160 "zh-TW", // Bopomofo
161 "", // Han
162 "ii", // Yi
163 "ett", // OldItalic
164 "got", // Gothic
165 "en", // Deseret
166 "fil", // Tagalog
167 "hnn", // Hanunoo
168 "bku", // Buhid
169 "tbw", // Tagbanwa
170 "cop", // Coptic
171 "lif", // Limbu
172 "tdd", // TaiLe
173 "grc", // LinearB
174 "uga", // Ugaritic
175 "en", // Shavian
176 "so", // Osmanya
177 "grc", // Cypriot
178 "", // Braille
179 "bug", // Buginese
180 "khb", // NewTaiLue
181 "cu", // Glagolitic
182 "shi", // Tifinagh
183 "syl", // SylotiNagri
184 "peo", // OldPersian
185 "pra", // Kharoshthi
186 "ban", // Balinese
187 "akk", // Cuneiform
188 "phn", // Phoenician
189 "lzh", // PhagsPa
190 "man", // Nko
191 "su", // Sundanese
192 "lep", // Lepcha
193 "sat", // OlChiki
194 "vai", // Vai
195 "saz", // Saurashtra
196 "eky", // KayahLi
197 "rej", // Rejang
198 "xlc", // Lycian
199 "xcr", // Carian
200 "xld", // Lydian
201 "cjm", // Cham
202 "nod", // TaiTham
203 "blt", // TaiViet
204 "ae", // Avestan
205 "egy", // EgyptianHieroglyphs
206 "smp", // Samaritan
207 "lis", // Lisu
208 "bax", // Bamum
209 "jv", // Javanese
210 "mni", // MeeteiMayek
211 "arc", // ImperialAramaic
212 "xsa", // OldSouthArabian
213 "xpr", // InscriptionalParthian
214 "pal", // InscriptionalPahlavi
215 "otk", // OldTurkic
216 "bh", // Kaithi
217 "bbc", // Batak
218 "pra", // Brahmi
219 "myz", // Mandaic
220 "ccp", // Chakma
221 "xmr", // MeroiticCursive
222 "xmr", // MeroiticHieroglyphs
223 "hmd", // Miao
224 "sa", // Sharada
225 "srb", // SoraSompeng
226 "doi", // Takri
227 "lez", // CaucasianAlbanian
228 "bsq", // BassaVah
229 "fr", // Duployan
230 "sq", // Elbasan
231 "sa", // Grantha
232 "hnj", // PahawhHmong
233 "sd", // Khojki
234 "lab", // LinearA
235 "hi", // Mahajani
236 "xmn", // Manichaean
237 "men", // MendeKikakui
238 "mr", // Modi
239 "mru", // Mro
240 "xna", // OldNorthArabian
241 "arc", // Nabataean
242 "arc", // Palmyrene
243 "ctd", // PauCinHau
244 "kv", // OldPermic
245 "pal", // PsalterPahlavi
246 "sa", // Siddham
247 "sd", // Khudawadi
248 "mai", // Tirhuta
249 "hoc", // WarangCiti
250 "", // Ahom
251 "", // AnatolianHieroglyphs
252 "", // Hatran
253 "", // Multani
254 "", // OldHungarian
255 "", // SignWriting
256 "", // Adlam
257 "", // Bhaiksuki
258 "", // Marchen
259 "", // Newa
260 "", // Osage
261 "", // Tangut
262 "", // MasaramGondi
263 "", // Nushu
264 "", // Soyombo
265 "", // ZanabazarSquare
266 "", // Dogra
267 "", // GunjalaGondi
268 "", // HanifiRohingya
269 "", // Makasar
270 "", // Medefaidrin
271 "", // OldSogdian
272 "", // Sogdian
273 "", // Elymaic
274 "", // Nandinagari
275 "", // NyiakengPuachueHmong
276 "", // Wancho
277 "", // Chorasmian
278 "", // DivesAkuru
279 "", // KhitanSmallScript
280 "" // Yezidi
281};
282Q_STATIC_ASSERT(sizeof specialLanguages / sizeof *specialLanguages == QChar::ScriptCount);
283
284// this could become a list of all languages used for each writing
285// system, instead of using the single most common language.
286static const char languageForWritingSystem[][6] = {
287 "", // Any
288 "en", // Latin
289 "el", // Greek
290 "ru", // Cyrillic
291 "hy", // Armenian
292 "he", // Hebrew
293 "ar", // Arabic
294 "syr", // Syriac
295 "div", // Thaana
296 "hi", // Devanagari
297 "bn", // Bengali
298 "pa", // Gurmukhi
299 "gu", // Gujarati
300 "or", // Oriya
301 "ta", // Tamil
302 "te", // Telugu
303 "kn", // Kannada
304 "ml", // Malayalam
305 "si", // Sinhala
306 "th", // Thai
307 "lo", // Lao
308 "bo", // Tibetan
309 "my", // Myanmar
310 "ka", // Georgian
311 "km", // Khmer
312 "zh-cn", // SimplifiedChinese
313 "zh-tw", // TraditionalChinese
314 "ja", // Japanese
315 "ko", // Korean
316 "vi", // Vietnamese
317 "", // Symbol
318 "sga", // Ogham
319 "non", // Runic
320 "man" // N'Ko
321};
322Q_STATIC_ASSERT(sizeof languageForWritingSystem / sizeof *languageForWritingSystem == QFontDatabase::WritingSystemsCount);
323
324#if FC_VERSION >= 20297
325// Newer FontConfig let's us sort out fonts that report certain scripts support,
326// but no open type tables for handling them correctly.
327// Check the reported script presence in the FC_CAPABILITY's "otlayout:" section.
328static const char capabilityForWritingSystem[][5] = {
329 "", // Any
330 "", // Latin
331 "", // Greek
332 "", // Cyrillic
333 "", // Armenian
334 "", // Hebrew
335 "", // Arabic
336 "syrc", // Syriac
337 "thaa", // Thaana
338 "deva", // Devanagari
339 "beng", // Bengali
340 "guru", // Gurmukhi
341 "gujr", // Gujarati
342 "orya", // Oriya
343 "taml", // Tamil
344 "telu", // Telugu
345 "knda", // Kannada
346 "mlym", // Malayalam
347 "sinh", // Sinhala
348 "", // Thai
349 "", // Lao
350 "tibt", // Tibetan
351 "mymr", // Myanmar
352 "", // Georgian
353 "khmr", // Khmer
354 "", // SimplifiedChinese
355 "", // TraditionalChinese
356 "", // Japanese
357 "", // Korean
358 "", // Vietnamese
359 "", // Symbol
360 "", // Ogham
361 "", // Runic
362 "nko " // N'Ko
363};
364Q_STATIC_ASSERT(sizeof(capabilityForWritingSystem) / sizeof(*capabilityForWritingSystem) == QFontDatabase::WritingSystemsCount);
365#endif
366
367static const char *getFcFamilyForStyleHint(const QFont::StyleHint style)
368{
369 const char *stylehint = nullptr;
370 switch (style) {
371 case QFont::SansSerif:
372 stylehint = "sans-serif";
373 break;
374 case QFont::Serif:
375 stylehint = "serif";
376 break;
377 case QFont::TypeWriter:
378 case QFont::Monospace:
379 stylehint = "monospace";
380 break;
381 case QFont::Cursive:
382 stylehint = "cursive";
383 break;
384 case QFont::Fantasy:
385 stylehint = "fantasy";
386 break;
387 default:
388 break;
389 }
390 return stylehint;
391}
392
393static inline bool requiresOpenType(int writingSystem)
394{
395 return ((writingSystem >= QFontDatabase::Syriac && writingSystem <= QFontDatabase::Sinhala)
396 || writingSystem == QFontDatabase::Khmer || writingSystem == QFontDatabase::Nko);
397}
398
399static void populateFromPattern(FcPattern *pattern)
400{
401 QString familyName;
402 QString familyNameLang;
403 FcChar8 *value = nullptr;
404 int weight_value;
405 int slant_value;
406 int spacing_value;
407 int width_value;
408 FcChar8 *file_value;
409 int indexValue;
410 FcChar8 *foundry_value;
411 FcChar8 *style_value;
412 FcBool scalable;
413 FcBool antialias;
414
415 if (FcPatternGetString(p: pattern, FC_FAMILY, n: 0, s: &value) != FcResultMatch)
416 return;
417
418 familyName = QString::fromUtf8(str: (const char *)value);
419
420 if (FcPatternGetString(p: pattern, FC_FAMILYLANG, n: 0, s: &value) == FcResultMatch)
421 familyNameLang = QString::fromUtf8(str: (const char *)value);
422
423 slant_value = FC_SLANT_ROMAN;
424 weight_value = FC_WEIGHT_REGULAR;
425 spacing_value = FC_PROPORTIONAL;
426 file_value = nullptr;
427 indexValue = 0;
428 scalable = FcTrue;
429
430
431 if (FcPatternGetInteger(p: pattern, FC_SLANT, n: 0, i: &slant_value) != FcResultMatch)
432 slant_value = FC_SLANT_ROMAN;
433 if (FcPatternGetInteger(p: pattern, FC_WEIGHT, n: 0, i: &weight_value) != FcResultMatch)
434 weight_value = FC_WEIGHT_REGULAR;
435 if (FcPatternGetInteger(p: pattern, FC_WIDTH, n: 0, i: &width_value) != FcResultMatch)
436 width_value = FC_WIDTH_NORMAL;
437 if (FcPatternGetInteger(p: pattern, FC_SPACING, n: 0, i: &spacing_value) != FcResultMatch)
438 spacing_value = FC_PROPORTIONAL;
439 if (FcPatternGetString(p: pattern, FC_FILE, n: 0, s: &file_value) != FcResultMatch)
440 file_value = nullptr;
441 if (FcPatternGetInteger(p: pattern, FC_INDEX, n: 0, i: &indexValue) != FcResultMatch)
442 indexValue = 0;
443 if (FcPatternGetBool(p: pattern, FC_SCALABLE, n: 0, b: &scalable) != FcResultMatch)
444 scalable = FcTrue;
445 if (FcPatternGetString(p: pattern, FC_FOUNDRY, n: 0, s: &foundry_value) != FcResultMatch)
446 foundry_value = nullptr;
447 if (FcPatternGetString(p: pattern, FC_STYLE, n: 0, s: &style_value) != FcResultMatch)
448 style_value = nullptr;
449 if (FcPatternGetBool(p: pattern,FC_ANTIALIAS,n: 0,b: &antialias) != FcResultMatch)
450 antialias = true;
451
452 QSupportedWritingSystems writingSystems;
453 FcLangSet *langset = nullptr;
454 FcResult res = FcPatternGetLangSet(p: pattern, FC_LANG, n: 0, ls: &langset);
455 if (res == FcResultMatch) {
456 bool hasLang = false;
457#if FC_VERSION >= 20297
458 FcChar8 *cap = nullptr;
459 FcResult capRes = FcResultNoMatch;
460#endif
461 for (int j = 1; j < QFontDatabase::WritingSystemsCount; ++j) {
462 const FcChar8 *lang = (const FcChar8*) languageForWritingSystem[j];
463 if (lang) {
464 FcLangResult langRes = FcLangSetHasLang(ls: langset, lang);
465 if (langRes != FcLangDifferentLang) {
466#if FC_VERSION >= 20297
467 if (*capabilityForWritingSystem[j] && requiresOpenType(writingSystem: j)) {
468 if (cap == nullptr)
469 capRes = FcPatternGetString(p: pattern, FC_CAPABILITY, n: 0, s: &cap);
470 if (capRes == FcResultMatch && strstr(haystack: reinterpret_cast<const char *>(cap), needle: capabilityForWritingSystem[j]) == nullptr)
471 continue;
472 }
473#endif
474 writingSystems.setSupported(QFontDatabase::WritingSystem(j));
475 hasLang = true;
476 }
477 }
478 }
479 if (!hasLang)
480 // none of our known languages, add it to the other set
481 writingSystems.setSupported(QFontDatabase::Other);
482 } else {
483 // we set Other to supported for symbol fonts. It makes no
484 // sense to merge these with other ones, as they are
485 // special in a way.
486 writingSystems.setSupported(QFontDatabase::Other);
487 }
488
489 FontFile *fontFile = new FontFile;
490 fontFile->fileName = QString::fromLocal8Bit(str: (const char *)file_value);
491 fontFile->indexValue = indexValue;
492
493 QFont::Style style = (slant_value == FC_SLANT_ITALIC)
494 ? QFont::StyleItalic
495 : ((slant_value == FC_SLANT_OBLIQUE)
496 ? QFont::StyleOblique
497 : QFont::StyleNormal);
498 // Note: weight should really be an int but registerFont incorrectly uses an enum
499 QFont::Weight weight = QFont::Weight(weightFromFcWeight(fcweight: weight_value));
500
501 double pixel_size = 0;
502 if (!scalable)
503 FcPatternGetDouble (p: pattern, FC_PIXEL_SIZE, n: 0, d: &pixel_size);
504
505 bool fixedPitch = spacing_value >= FC_MONO;
506 // Note: stretch should really be an int but registerFont incorrectly uses an enum
507 QFont::Stretch stretch = QFont::Stretch(stretchFromFcWidth(fcwidth: width_value));
508 QString styleName = style_value ? QString::fromUtf8(str: (const char *) style_value) : QString();
509 QPlatformFontDatabase::registerFont(familyname: familyName,stylename: styleName,foundryname: QLatin1String((const char *)foundry_value),weight,style,stretch,antialiased: antialias,scalable,pixelSize: pixel_size,fixedPitch,writingSystems,handle: fontFile);
510// qDebug() << familyName << (const char *)foundry_value << weight << style << &writingSystems << scalable << true << pixel_size;
511
512 for (int k = 1; FcPatternGetString(p: pattern, FC_FAMILY, n: k, s: &value) == FcResultMatch; ++k) {
513 const QString altFamilyName = QString::fromUtf8(str: (const char *)value);
514 // Extra family names can be aliases or subfamilies.
515 // If it is a subfamily, register it as a separate font, so only members of the subfamily are
516 // matched when the subfamily is requested.
517 QString altStyleName;
518 if (FcPatternGetString(p: pattern, FC_STYLE, n: k, s: &value) == FcResultMatch)
519 altStyleName = QString::fromUtf8(str: (const char *)value);
520 else
521 altStyleName = styleName;
522
523 QString altFamilyNameLang;
524 if (FcPatternGetString(p: pattern, FC_FAMILYLANG, n: k, s: &value) == FcResultMatch)
525 altFamilyNameLang = QString::fromUtf8(str: (const char *)value);
526 else
527 altFamilyNameLang = familyNameLang;
528
529 if (familyNameLang == altFamilyNameLang && altStyleName != styleName) {
530 FontFile *altFontFile = new FontFile(*fontFile);
531 QPlatformFontDatabase::registerFont(familyname: altFamilyName, stylename: altStyleName, foundryname: QLatin1String((const char *)foundry_value),weight,style,stretch,antialiased: antialias,scalable,pixelSize: pixel_size,fixedPitch,writingSystems,handle: altFontFile);
532 } else {
533 QPlatformFontDatabase::registerAliasToFontFamily(familyName, alias: altFamilyName);
534 }
535 }
536
537}
538
539QFontconfigDatabase::~QFontconfigDatabase()
540{
541 FcConfigDestroy(config: FcConfigGetCurrent());
542}
543
544void QFontconfigDatabase::populateFontDatabase()
545{
546 FcInit();
547 FcFontSet *fonts;
548
549 {
550 FcObjectSet *os = FcObjectSetCreate();
551 FcPattern *pattern = FcPatternCreate();
552 const char *properties [] = {
553 FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT,
554 FC_SPACING, FC_FILE, FC_INDEX,
555 FC_LANG, FC_CHARSET, FC_FOUNDRY, FC_SCALABLE, FC_PIXEL_SIZE,
556 FC_WIDTH, FC_FAMILYLANG,
557#if FC_VERSION >= 20297
558 FC_CAPABILITY,
559#endif
560 (const char *)nullptr
561 };
562 const char **p = properties;
563 while (*p) {
564 FcObjectSetAdd(os, object: *p);
565 ++p;
566 }
567 fonts = FcFontList(config: nullptr, p: pattern, os);
568 FcObjectSetDestroy(os);
569 FcPatternDestroy(p: pattern);
570 }
571
572 for (int i = 0; i < fonts->nfont; i++)
573 populateFromPattern(pattern: fonts->fonts[i]);
574
575 FcFontSetDestroy (s: fonts);
576
577 struct FcDefaultFont {
578 const char *qtname;
579 const char *rawname;
580 bool fixed;
581 };
582 const FcDefaultFont defaults[] = {
583 { .qtname: "Serif", .rawname: "serif", .fixed: false },
584 { .qtname: "Sans Serif", .rawname: "sans-serif", .fixed: false },
585 { .qtname: "Monospace", .rawname: "monospace", .fixed: true },
586 { .qtname: nullptr, .rawname: nullptr, .fixed: false }
587 };
588 const FcDefaultFont *f = defaults;
589 // aliases only make sense for 'common', not for any of the specials
590 QSupportedWritingSystems ws;
591 ws.setSupported(QFontDatabase::Latin);
592
593 while (f->qtname) {
594 QString familyQtName = QString::fromLatin1(str: f->qtname);
595 registerFont(familyname: familyQtName,stylename: QString(),foundryname: QString(),weight: QFont::Normal,style: QFont::StyleNormal,stretch: QFont::Unstretched,antialiased: true,scalable: true,pixelSize: 0,fixedPitch: f->fixed,writingSystems: ws,handle: nullptr);
596 registerFont(familyname: familyQtName,stylename: QString(),foundryname: QString(),weight: QFont::Normal,style: QFont::StyleItalic,stretch: QFont::Unstretched,antialiased: true,scalable: true,pixelSize: 0,fixedPitch: f->fixed,writingSystems: ws,handle: nullptr);
597 registerFont(familyname: familyQtName,stylename: QString(),foundryname: QString(),weight: QFont::Normal,style: QFont::StyleOblique,stretch: QFont::Unstretched,antialiased: true,scalable: true,pixelSize: 0,fixedPitch: f->fixed,writingSystems: ws,handle: nullptr);
598 ++f;
599 }
600
601 //QPA has very lazy population of the font db. We want it to be initialized when
602 //QApplication is constructed, so that the population procedure can do something like this to
603 //set the default font
604// const FcDefaultFont *s = defaults;
605// QFont font("Sans Serif");
606// font.setPointSize(9);
607// QApplication::setFont(font);
608}
609
610void QFontconfigDatabase::invalidate()
611{
612 // Clear app fonts.
613 FcConfigAppFontClear(config: nullptr);
614}
615
616QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine, QChar::Script script)
617{
618 return new QFontEngineMultiFontConfig(fontEngine, script);
619}
620
621namespace {
622QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintingPreference, FcPattern *match, bool useXftConf)
623{
624 switch (hintingPreference) {
625 case QFont::PreferNoHinting:
626 return QFontEngine::HintNone;
627 case QFont::PreferVerticalHinting:
628 return QFontEngine::HintLight;
629 case QFont::PreferFullHinting:
630 return QFontEngine::HintFull;
631 case QFont::PreferDefaultHinting:
632 break;
633 }
634
635 if (QHighDpiScaling::isActive())
636 return QFontEngine::HintNone;
637
638 int hint_style = 0;
639 if (FcPatternGetInteger (p: match, FC_HINT_STYLE, n: 0, i: &hint_style) == FcResultMatch) {
640 switch (hint_style) {
641 case FC_HINT_NONE:
642 return QFontEngine::HintNone;
643 case FC_HINT_SLIGHT:
644 return QFontEngine::HintLight;
645 case FC_HINT_MEDIUM:
646 return QFontEngine::HintMedium;
647 case FC_HINT_FULL:
648 return QFontEngine::HintFull;
649 default:
650 Q_UNREACHABLE();
651 break;
652 }
653 }
654
655 if (useXftConf) {
656 void *hintStyleResource =
657 QGuiApplication::platformNativeInterface()->nativeResourceForScreen(resource: "hintstyle",
658 screen: QGuiApplication::primaryScreen());
659 int hintStyle = int(reinterpret_cast<qintptr>(hintStyleResource));
660 if (hintStyle > 0)
661 return QFontEngine::HintStyle(hintStyle - 1);
662 }
663
664 return QFontEngine::HintFull;
665}
666
667QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match, bool useXftConf)
668{
669 int subpixel = FC_RGBA_UNKNOWN;
670 if (FcPatternGetInteger(p: match, FC_RGBA, n: 0, i: &subpixel) == FcResultMatch) {
671 switch (subpixel) {
672 case FC_RGBA_UNKNOWN:
673 case FC_RGBA_NONE:
674 return QFontEngine::Subpixel_None;
675 case FC_RGBA_RGB:
676 return QFontEngine::Subpixel_RGB;
677 case FC_RGBA_BGR:
678 return QFontEngine::Subpixel_BGR;
679 case FC_RGBA_VRGB:
680 return QFontEngine::Subpixel_VRGB;
681 case FC_RGBA_VBGR:
682 return QFontEngine::Subpixel_VBGR;
683 default:
684 Q_UNREACHABLE();
685 break;
686 }
687 }
688
689 if (useXftConf) {
690 void *subpixelTypeResource =
691 QGuiApplication::platformNativeInterface()->nativeResourceForScreen(resource: "subpixeltype",
692 screen: QGuiApplication::primaryScreen());
693 int subpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource));
694 if (subpixelType > 0)
695 return QFontEngine::SubpixelAntialiasingType(subpixelType - 1);
696 }
697
698 return QFontEngine::Subpixel_None;
699}
700} // namespace
701
702QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr)
703{
704 if (!usrPtr)
705 return nullptr;
706
707 FontFile *fontfile = static_cast<FontFile *> (usrPtr);
708 QFontEngine::FaceId fid;
709 fid.filename = QFile::encodeName(fileName: fontfile->fileName);
710 fid.index = fontfile->indexValue;
711
712 // FIXME: Unify with logic in QFontEngineFT::create()
713 QFontEngineFT *engine = new QFontEngineFT(f);
714 engine->face_id = fid;
715
716 setupFontEngine(engine, fontDef: f);
717
718 if (!engine->init(faceId: fid, antiaalias: engine->antialias, defaultFormat: engine->defaultFormat) || engine->invalid()) {
719 delete engine;
720 engine = nullptr;
721 }
722
723 return engine;
724}
725
726QFontEngine *QFontconfigDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
727{
728 QFontEngineFT *engine = static_cast<QFontEngineFT*>(QFreeTypeFontDatabase::fontEngine(fontData, pixelSize, hintingPreference));
729 if (engine == nullptr)
730 return nullptr;
731
732 setupFontEngine(engine, fontDef: engine->fontDef);
733
734 return engine;
735}
736
737QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
738{
739 QStringList fallbackFamilies;
740 FcPattern *pattern = FcPatternCreate();
741 if (!pattern)
742 return fallbackFamilies;
743
744 FcValue value;
745 value.type = FcTypeString;
746 const QByteArray cs = family.toUtf8();
747 value.u.s = (const FcChar8 *)cs.data();
748 FcPatternAdd(p: pattern,FC_FAMILY,value,append: true);
749
750 int slant_value = FC_SLANT_ROMAN;
751 if (style == QFont::StyleItalic)
752 slant_value = FC_SLANT_ITALIC;
753 else if (style == QFont::StyleOblique)
754 slant_value = FC_SLANT_OBLIQUE;
755 FcPatternAddInteger(p: pattern, FC_SLANT, i: slant_value);
756
757 Q_ASSERT(uint(script) < QChar::ScriptCount);
758 if (*specialLanguages[script] != '\0') {
759 FcLangSet *ls = FcLangSetCreate();
760 FcLangSetAdd(ls, lang: (const FcChar8*)specialLanguages[script]);
761 FcPatternAddLangSet(p: pattern, FC_LANG, ls);
762 FcLangSetDestroy(ls);
763 } else if (!family.isEmpty()) {
764 // If script is Common or Han, then it may include languages like CJK,
765 // we should attach system default language set to the pattern
766 // to obtain correct font fallback list (i.e. if LANG=zh_CN
767 // then we normally want to use a Chinese font for CJK text;
768 // while a Japanese font should be used for that if LANG=ja)
769 FcPattern *dummy = FcPatternCreate();
770 FcDefaultSubstitute(pattern: dummy);
771 FcChar8 *lang = nullptr;
772 FcResult res = FcPatternGetString(p: dummy, FC_LANG, n: 0, s: &lang);
773 if (res == FcResultMatch)
774 FcPatternAddString(p: pattern, FC_LANG, s: lang);
775 FcPatternDestroy(p: dummy);
776 }
777
778 const char *stylehint = getFcFamilyForStyleHint(style: styleHint);
779 if (stylehint) {
780 value.u.s = (const FcChar8 *)stylehint;
781 FcPatternAddWeak(p: pattern, FC_FAMILY, value, FcTrue);
782 }
783
784 FcConfigSubstitute(config: nullptr, p: pattern, kind: FcMatchPattern);
785 FcDefaultSubstitute(pattern);
786
787 FcResult result = FcResultMatch;
788 FcFontSet *fontSet = FcFontSort(config: nullptr,p: pattern,FcFalse,csp: nullptr,result: &result);
789 FcPatternDestroy(p: pattern);
790
791 if (fontSet) {
792 QDuplicateTracker<QString> duplicates;
793 duplicates.reserve(n: fontSet->nfont + 1);
794 (void)duplicates.hasSeen(s: family.toCaseFolded());
795 for (int i = 0; i < fontSet->nfont; i++) {
796 FcChar8 *value = nullptr;
797 if (FcPatternGetString(p: fontSet->fonts[i], FC_FAMILY, n: 0, s: &value) != FcResultMatch)
798 continue;
799 // capitalize(value);
800 const QString familyName = QString::fromUtf8(str: (const char *)value);
801 const QString familyNameCF = familyName.toCaseFolded();
802 if (!duplicates.hasSeen(s: familyNameCF)) {
803 fallbackFamilies << familyName;
804 }
805 }
806 FcFontSetDestroy(s: fontSet);
807 }
808// qDebug() << "fallbackFamilies for:" << family << style << styleHint << script << fallbackFamilies;
809
810 return fallbackFamilies;
811}
812
813static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count)
814{
815#if FC_VERSION < 20402
816 Q_UNUSED(data)
817 return FcFreeTypeQuery(file, id, blanks, count);
818#else
819 if (data.isEmpty())
820 return FcFreeTypeQuery(file, id, blanks, count);
821
822 FT_Library lib = qt_getFreetype();
823
824 FcPattern *pattern = nullptr;
825
826 FT_Face face;
827 if (!FT_New_Memory_Face(library: lib, file_base: (const FT_Byte *)data.constData(), file_size: data.size(), face_index: id, aface: &face)) {
828 *count = face->num_faces;
829
830 pattern = FcFreeTypeQueryFace(face, file, id, blanks);
831
832 FT_Done_Face(face);
833 }
834
835 return pattern;
836#endif
837}
838
839QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName)
840{
841 QStringList families;
842
843 FcFontSet *set = FcConfigGetFonts(config: nullptr, set: FcSetApplication);
844 if (!set) {
845 FcConfigAppFontAddFile(config: nullptr, file: (const FcChar8 *)":/non-existent");
846 set = FcConfigGetFonts(config: nullptr, set: FcSetApplication); // try again
847 if (!set)
848 return families;
849 }
850
851 int id = 0;
852 FcBlanks *blanks = FcConfigGetBlanks(config: nullptr);
853 int count = 0;
854
855 FcPattern *pattern;
856 do {
857 pattern = queryFont(file: (const FcChar8 *)QFile::encodeName(fileName).constData(),
858 data: fontData, id, blanks, count: &count);
859 if (!pattern)
860 return families;
861
862 FcChar8 *fam = nullptr;
863 if (FcPatternGetString(p: pattern, FC_FAMILY, n: 0, s: &fam) == FcResultMatch) {
864 QString family = QString::fromUtf8(str: reinterpret_cast<const char *>(fam));
865 families << family;
866 }
867 populateFromPattern(pattern);
868
869 FcFontSetAdd(s: set, font: pattern);
870
871 ++id;
872 } while (id < count);
873
874 return families;
875}
876
877QString QFontconfigDatabase::resolveFontFamilyAlias(const QString &family) const
878{
879 QString resolved = QFreeTypeFontDatabase::resolveFontFamilyAlias(family);
880 if (!resolved.isEmpty() && resolved != family)
881 return resolved;
882 FcPattern *pattern = FcPatternCreate();
883 if (!pattern)
884 return family;
885
886 if (!family.isEmpty()) {
887 const QByteArray cs = family.toUtf8();
888 FcPatternAddString(p: pattern, FC_FAMILY, s: (const FcChar8 *) cs.constData());
889 }
890 FcConfigSubstitute(config: nullptr, p: pattern, kind: FcMatchPattern);
891 FcDefaultSubstitute(pattern);
892
893 FcChar8 *familyAfterSubstitution = nullptr;
894 FcPatternGetString(p: pattern, FC_FAMILY, n: 0, s: &familyAfterSubstitution);
895 resolved = QString::fromUtf8(str: (const char *) familyAfterSubstitution);
896 FcPatternDestroy(p: pattern);
897
898 return resolved;
899}
900
901QFont QFontconfigDatabase::defaultFont() const
902{
903 // Hack to get system default language until FcGetDefaultLangs()
904 // is exported (https://bugs.freedesktop.org/show_bug.cgi?id=32853)
905 // or https://bugs.freedesktop.org/show_bug.cgi?id=35482 is fixed
906 FcPattern *dummy = FcPatternCreate();
907 FcDefaultSubstitute(pattern: dummy);
908 FcChar8 *lang = nullptr;
909 FcResult res = FcPatternGetString(p: dummy, FC_LANG, n: 0, s: &lang);
910
911 FcPattern *pattern = FcPatternCreate();
912 if (res == FcResultMatch) {
913 // Make defaultFont pattern matching locale language aware, because
914 // certain FC_LANG based custom rules may happen in FcConfigSubstitute()
915 FcPatternAddString(p: pattern, FC_LANG, s: lang);
916 }
917 FcConfigSubstitute(config: nullptr, p: pattern, kind: FcMatchPattern);
918 FcDefaultSubstitute(pattern);
919
920 FcChar8 *familyAfterSubstitution = nullptr;
921 FcPatternGetString(p: pattern, FC_FAMILY, n: 0, s: &familyAfterSubstitution);
922 QString resolved = QString::fromUtf8(str: (const char *) familyAfterSubstitution);
923 FcPatternDestroy(p: pattern);
924 FcPatternDestroy(p: dummy);
925
926 return QFont(resolved);
927}
928
929void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef &fontDef) const
930{
931 bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
932 bool forcedAntialiasSetting = !antialias || QHighDpiScaling::isActive();
933
934 const QPlatformServices *services = QGuiApplicationPrivate::platformIntegration()->services();
935 bool useXftConf = false;
936
937 if (services) {
938 const QList<QByteArray> desktopEnv = services->desktopEnvironment().split(sep: ':');
939 useXftConf = desktopEnv.contains(t: "GNOME") || desktopEnv.contains(t: "UNITY") || desktopEnv.contains(t: "XFCE");
940 }
941
942 if (useXftConf && !forcedAntialiasSetting) {
943 void *antialiasResource =
944 QGuiApplication::platformNativeInterface()->nativeResourceForScreen(resource: "antialiasingEnabled",
945 screen: QGuiApplication::primaryScreen());
946 int antialiasingEnabled = int(reinterpret_cast<qintptr>(antialiasResource));
947 if (antialiasingEnabled > 0)
948 antialias = antialiasingEnabled - 1;
949 }
950
951 QFontEngine::GlyphFormat format;
952 // try and get the pattern
953 FcPattern *pattern = FcPatternCreate();
954
955 FcValue value;
956 value.type = FcTypeString;
957 QByteArray cs = fontDef.family.toUtf8();
958 value.u.s = (const FcChar8 *)cs.data();
959 FcPatternAdd(p: pattern,FC_FAMILY,value,append: true);
960
961 QFontEngine::FaceId fid = engine->faceId();
962
963 if (!fid.filename.isEmpty()) {
964 value.u.s = (const FcChar8 *)fid.filename.data();
965 FcPatternAdd(p: pattern,FC_FILE,value,append: true);
966
967 value.type = FcTypeInteger;
968 value.u.i = fid.index;
969 FcPatternAdd(p: pattern,FC_INDEX,value,append: true);
970 }
971
972 if (fontDef.pixelSize > 0.1)
973 FcPatternAddDouble(p: pattern, FC_PIXEL_SIZE, d: fontDef.pixelSize);
974
975 FcResult result;
976
977 FcConfigSubstitute(config: nullptr, p: pattern, kind: FcMatchPattern);
978 FcDefaultSubstitute(pattern);
979
980 FcPattern *match = FcFontMatch(config: nullptr, p: pattern, result: &result);
981 if (match) {
982 engine->setDefaultHintStyle(defaultHintStyleFromMatch(hintingPreference: (QFont::HintingPreference)fontDef.hintingPreference, match, useXftConf));
983
984 FcBool fc_autohint;
985 if (FcPatternGetBool(p: match, FC_AUTOHINT,n: 0, b: &fc_autohint) == FcResultMatch)
986 engine->forceAutoHint = fc_autohint;
987
988#if defined(FT_LCD_FILTER_H)
989 int lcdFilter;
990 if (FcPatternGetInteger(p: match, FC_LCD_FILTER, n: 0, i: &lcdFilter) == FcResultMatch)
991 engine->lcdFilterType = lcdFilter;
992#endif
993
994 if (!forcedAntialiasSetting) {
995 FcBool fc_antialias;
996 if (FcPatternGetBool(p: match, FC_ANTIALIAS,n: 0, b: &fc_antialias) == FcResultMatch)
997 antialias = fc_antialias;
998 }
999
1000 if (antialias) {
1001 QFontEngine::SubpixelAntialiasingType subpixelType = QFontEngine::Subpixel_None;
1002 if (!(fontDef.styleStrategy & QFont::NoSubpixelAntialias))
1003 subpixelType = subpixelTypeFromMatch(match, useXftConf);
1004 engine->subpixelType = subpixelType;
1005
1006 format = (subpixelType == QFontEngine::Subpixel_None)
1007 ? QFontEngine::Format_A8
1008 : QFontEngine::Format_A32;
1009 } else
1010 format = QFontEngine::Format_Mono;
1011
1012 FcPatternDestroy(p: match);
1013 } else
1014 format = antialias ? QFontEngine::Format_A8 : QFontEngine::Format_Mono;
1015
1016 FcPatternDestroy(p: pattern);
1017
1018 engine->antialias = antialias;
1019 engine->defaultFormat = format;
1020 engine->glyphFormat = format;
1021}
1022
1023QT_END_NAMESPACE
1024

source code of qtbase/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp