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

source code of qtbase/src/gui/text/unix/qfontconfigdatabase.cpp