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