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};
258static_assert(sizeof specialLanguages / sizeof *specialLanguages == QChar::ScriptCount);
259
260// this could become a list of all languages used for each writing
261// system, instead of using the single most common language.
262static const char languageForWritingSystem[][6] = {
263 "", // Any
264 "en", // Latin
265 "el", // Greek
266 "ru", // Cyrillic
267 "hy", // Armenian
268 "he", // Hebrew
269 "ar", // Arabic
270 "syr", // Syriac
271 "div", // Thaana
272 "hi", // Devanagari
273 "bn", // Bengali
274 "pa", // Gurmukhi
275 "gu", // Gujarati
276 "or", // Oriya
277 "ta", // Tamil
278 "te", // Telugu
279 "kn", // Kannada
280 "ml", // Malayalam
281 "si", // Sinhala
282 "th", // Thai
283 "lo", // Lao
284 "bo", // Tibetan
285 "my", // Myanmar
286 "ka", // Georgian
287 "km", // Khmer
288 "zh-cn", // SimplifiedChinese
289 "zh-tw", // TraditionalChinese
290 "ja", // Japanese
291 "ko", // Korean
292 "vi", // Vietnamese
293 "", // Symbol
294 "sga", // Ogham
295 "non", // Runic
296 "man" // N'Ko
297};
298static_assert(sizeof languageForWritingSystem / sizeof *languageForWritingSystem == QFontDatabase::WritingSystemsCount);
299
300#if FC_VERSION >= 20297
301// Newer FontConfig let's us sort out fonts that report certain scripts support,
302// but no open type tables for handling them correctly.
303// Check the reported script presence in the FC_CAPABILITY's "otlayout:" section.
304static const char capabilityForWritingSystem[][5] = {
305 "", // Any
306 "", // Latin
307 "", // Greek
308 "", // Cyrillic
309 "", // Armenian
310 "", // Hebrew
311 "", // Arabic
312 "syrc", // Syriac
313 "thaa", // Thaana
314 "deva", // Devanagari
315 "beng", // Bengali
316 "guru", // Gurmukhi
317 "gujr", // Gujarati
318 "orya", // Oriya
319 "taml", // Tamil
320 "telu", // Telugu
321 "knda", // Kannada
322 "mlym", // Malayalam
323 "sinh", // Sinhala
324 "", // Thai
325 "", // Lao
326 "tibt", // Tibetan
327 "mymr", // Myanmar
328 "", // Georgian
329 "khmr", // Khmer
330 "", // SimplifiedChinese
331 "", // TraditionalChinese
332 "", // Japanese
333 "", // Korean
334 "", // Vietnamese
335 "", // Symbol
336 "", // Ogham
337 "", // Runic
338 "nko " // N'Ko
339};
340static_assert(sizeof(capabilityForWritingSystem) / sizeof(*capabilityForWritingSystem) == QFontDatabase::WritingSystemsCount);
341#endif
342
343static const char *getFcFamilyForStyleHint(const QFont::StyleHint style)
344{
345 const char *stylehint = nullptr;
346 switch (style) {
347 case QFont::SansSerif:
348 stylehint = "sans-serif";
349 break;
350 case QFont::Serif:
351 stylehint = "serif";
352 break;
353 case QFont::TypeWriter:
354 case QFont::Monospace:
355 stylehint = "monospace";
356 break;
357 case QFont::Cursive:
358 stylehint = "cursive";
359 break;
360 case QFont::Fantasy:
361 stylehint = "fantasy";
362 break;
363 default:
364 break;
365 }
366 return stylehint;
367}
368
369static inline bool requiresOpenType(int writingSystem)
370{
371 return ((writingSystem >= QFontDatabase::Syriac && writingSystem <= QFontDatabase::Sinhala)
372 || writingSystem == QFontDatabase::Khmer || writingSystem == QFontDatabase::Nko);
373}
374
375static void populateFromPattern(FcPattern *pattern,
376 QFontDatabasePrivate::ApplicationFont *applicationFont = nullptr,
377 FT_Face face = nullptr,
378 QFontconfigDatabase *db = nullptr)
379{
380 QString familyName;
381 QString familyNameLang;
382 FcChar8 *value = nullptr;
383 int weight_value;
384 int slant_value;
385 int spacing_value;
386 int width_value;
387 FcChar8 *file_value;
388 int indexValue;
389 FcChar8 *foundry_value;
390 FcChar8 *style_value;
391 FcBool scalable;
392 FcBool antialias;
393
394 if (FcPatternGetString(pattern, FC_FAMILY, 0, &value) != FcResultMatch)
395 return;
396
397 familyName = QString::fromUtf8((const char *)value);
398
399 if (FcPatternGetString(pattern, FC_FAMILYLANG, 0, &value) == FcResultMatch)
400 familyNameLang = QString::fromUtf8((const char *)value);
401
402 slant_value = FC_SLANT_ROMAN;
403 weight_value = FC_WEIGHT_REGULAR;
404 spacing_value = FC_PROPORTIONAL;
405 file_value = nullptr;
406 indexValue = 0;
407 scalable = FcTrue;
408
409
410 if (FcPatternGetInteger(pattern, FC_SLANT, 0, &slant_value) != FcResultMatch)
411 slant_value = FC_SLANT_ROMAN;
412 if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight_value) != FcResultMatch)
413 weight_value = FC_WEIGHT_REGULAR;
414 if (FcPatternGetInteger(pattern, FC_WIDTH, 0, &width_value) != FcResultMatch)
415 width_value = FC_WIDTH_NORMAL;
416 if (FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing_value) != FcResultMatch)
417 spacing_value = FC_PROPORTIONAL;
418 if (FcPatternGetString(pattern, FC_FILE, 0, &file_value) != FcResultMatch)
419 file_value = nullptr;
420 if (FcPatternGetInteger(pattern, FC_INDEX, 0, &indexValue) != FcResultMatch)
421 indexValue = 0;
422 if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &scalable) != FcResultMatch)
423 scalable = FcTrue;
424 if (FcPatternGetString(pattern, FC_FOUNDRY, 0, &foundry_value) != FcResultMatch)
425 foundry_value = nullptr;
426 if (FcPatternGetString(pattern, FC_STYLE, 0, &style_value) != FcResultMatch)
427 style_value = nullptr;
428 if (FcPatternGetBool(pattern,FC_ANTIALIAS,0,&antialias) != FcResultMatch)
429 antialias = true;
430
431 QSupportedWritingSystems writingSystems;
432 FcLangSet *langset = nullptr;
433 FcResult res = FcPatternGetLangSet(pattern, FC_LANG, 0, &langset);
434 if (res == FcResultMatch) {
435 bool hasLang = false;
436#if FC_VERSION >= 20297
437 FcChar8 *cap = nullptr;
438 FcResult capRes = FcResultNoMatch;
439#endif
440 for (int j = 1; j < QFontDatabase::WritingSystemsCount; ++j) {
441 const FcChar8 *lang = (const FcChar8*) languageForWritingSystem[j];
442 if (lang) {
443 FcLangResult langRes = FcLangSetHasLang(langset, lang);
444 if (langRes != FcLangDifferentLang) {
445#if FC_VERSION >= 20297
446 if (*capabilityForWritingSystem[j] && requiresOpenType(j)) {
447 if (cap == nullptr)
448 capRes = FcPatternGetString(pattern, FC_CAPABILITY, 0, &cap);
449 if (capRes == FcResultMatch && strstr(reinterpret_cast<const char *>(cap), capabilityForWritingSystem[j]) == nullptr)
450 continue;
451 }
452#endif
453 writingSystems.setSupported(QFontDatabase::WritingSystem(j));
454 hasLang = true;
455 }
456 }
457 }
458 if (!hasLang)
459 // none of our known languages, add it to the other set
460 writingSystems.setSupported(QFontDatabase::Other);
461 } else {
462 // we set Other to supported for symbol fonts. It makes no
463 // sense to merge these with other ones, as they are
464 // special in a way.
465 writingSystems.setSupported(QFontDatabase::Other);
466 }
467
468 QString fileName = QString::fromLocal8Bit((const char *)file_value);
469
470 QFont::Style style = (slant_value == FC_SLANT_ITALIC)
471 ? QFont::StyleItalic
472 : ((slant_value == FC_SLANT_OBLIQUE)
473 ? QFont::StyleOblique
474 : QFont::StyleNormal);
475 // Note: weight should really be an int but registerFont incorrectly uses an enum
476 QFont::Weight weight = QFont::Weight(weightFromFcWeight(weight_value));
477
478 double pixel_size = 0;
479 if (!scalable)
480 FcPatternGetDouble (pattern, FC_PIXEL_SIZE, 0, &pixel_size);
481
482 bool fixedPitch = spacing_value >= FC_MONO;
483
484 FcBool colorFont = false;
485#ifdef FC_COLOR
486 FcPatternGetBool(pattern, FC_COLOR, 0, &colorFont);
487#endif
488
489 // Note: stretch should really be an int but registerFont incorrectly uses an enum
490 QFont::Stretch stretch = QFont::Stretch(stretchFromFcWidth(width_value));
491 QString styleName = style_value ? QString::fromUtf8((const char *) style_value) : QString();
492
493 if (applicationFont != nullptr) {
494 QFontDatabasePrivate::ApplicationFont::Properties properties;
495 properties.familyName = familyName;
496 properties.styleName = styleName;
497 properties.weight = weight;
498 properties.style = style;
499 properties.stretch = stretch;
500
501 applicationFont->properties.append(properties);
502 }
503
504 {
505 FontFile *fontFile = new FontFile;
506 fontFile->fileName = fileName;
507 fontFile->indexValue = indexValue;
508 QPlatformFontDatabase::registerFont(familyName,
509 styleName,
510 QLatin1StringView((const char *)foundry_value),
511 weight,
512 style,
513 stretch,
514 antialias,
515 scalable,
516 pixel_size,
517 fixedPitch,
518 colorFont,
519 writingSystems,
520 fontFile);
521 }
522 if (applicationFont != nullptr && face != nullptr && db != nullptr) {
523 db->addNamedInstancesForFace(face,
524 indexValue,
525 familyName,
526 styleName,
527 weight,
528 stretch,
529 style,
530 fixedPitch,
531 colorFont,
532 writingSystems,
533 QByteArray((const char*)file_value),
534 applicationFont->data);
535 }
536
537// qDebug() << familyName << (const char *)foundry_value << weight << style << &writingSystems << scalable << true << pixel_size;
538
539 for (int k = 1; FcPatternGetString(pattern, FC_FAMILY, k, &value) == FcResultMatch; ++k) {
540 const QString altFamilyName = QString::fromUtf8((const char *)value);
541 // Extra family names can be aliases or subfamilies.
542 // If it is a subfamily, register it as a separate font, so only members of the subfamily are
543 // matched when the subfamily is requested.
544 QString altStyleName;
545 if (FcPatternGetString(pattern, FC_STYLE, k, &value) == FcResultMatch)
546 altStyleName = QString::fromUtf8((const char *)value);
547 else
548 altStyleName = styleName;
549
550 QString altFamilyNameLang;
551 if (FcPatternGetString(pattern, FC_FAMILYLANG, k, &value) == FcResultMatch)
552 altFamilyNameLang = QString::fromUtf8((const char *)value);
553 else
554 altFamilyNameLang = familyNameLang;
555
556 if (familyNameLang == altFamilyNameLang && altStyleName != styleName) {
557 if (applicationFont != nullptr) {
558 QFontDatabasePrivate::ApplicationFont::Properties properties;
559 properties.familyName = altFamilyName;
560 properties.styleName = altStyleName;
561 properties.weight = weight;
562 properties.style = style;
563 properties.stretch = stretch;
564
565 applicationFont->properties.append(properties);
566 }
567
568 {
569 FontFile *altFontFile = new FontFile;
570 altFontFile->fileName = fileName;
571 altFontFile->indexValue = indexValue;
572 QPlatformFontDatabase::registerFont(altFamilyName,
573 altStyleName,
574 QLatin1StringView((const char *)foundry_value),
575 weight,
576 style,
577 stretch,
578 antialias,
579 scalable,
580 pixel_size,
581 fixedPitch,
582 colorFont,
583 writingSystems,
584 altFontFile);
585 }
586 } else {
587 QPlatformFontDatabase::registerAliasToFontFamily(familyName, altFamilyName);
588 }
589 }
590
591}
592
593static bool isDprScaling()
594{
595 return !qFuzzyCompare(qApp->devicePixelRatio(), qreal(1.0));
596}
597
598QFontconfigDatabase::~QFontconfigDatabase()
599{
600 FcConfigDestroy(FcConfigGetCurrent());
601}
602
603void QFontconfigDatabase::populateFontDatabase()
604{
605 FcInit();
606 FcFontSet *fonts;
607
608 {
609 FcObjectSet *os = FcObjectSetCreate();
610 FcPattern *pattern = FcPatternCreate();
611 const char *properties [] = {
612 FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT,
613 FC_SPACING, FC_FILE, FC_INDEX,
614 FC_LANG, FC_CHARSET, FC_FOUNDRY, FC_SCALABLE, FC_PIXEL_SIZE,
615 FC_WIDTH, FC_FAMILYLANG,
616#if FC_VERSION >= 20297
617 FC_CAPABILITY,
618#endif
619#if defined(FC_COLOR)
620 FC_COLOR,
621#endif
622 (const char *)nullptr
623 };
624 const char **p = properties;
625 while (*p) {
626 FcObjectSetAdd(os, *p);
627 ++p;
628 }
629
630#ifdef FC_VARIABLE
631 /* Support the named instance of Variable Fonts. */
632 FcPatternAddBool(pattern, FC_VARIABLE, FcFalse);
633#endif
634
635 fonts = FcFontList(nullptr, pattern, os);
636 FcObjectSetDestroy(os);
637 FcPatternDestroy(pattern);
638 if (!fonts)
639 return;
640 }
641
642 for (int i = 0; i < fonts->nfont; i++)
643 populateFromPattern(fonts->fonts[i]);
644
645 FcFontSetDestroy (fonts);
646
647 struct FcDefaultFont {
648 const char *qtname;
649 const char *rawname;
650 bool fixed;
651 };
652 const FcDefaultFont defaults[] = {
653 { "Serif", "serif", false },
654 { "Sans Serif", "sans-serif", false },
655 { "Monospace", "monospace", true },
656 { nullptr, nullptr, false }
657 };
658 const FcDefaultFont *f = defaults;
659 // aliases only make sense for 'common', not for any of the specials
660 QSupportedWritingSystems ws;
661 ws.setSupported(QFontDatabase::Latin);
662
663 while (f->qtname) {
664 QString familyQtName = QString::fromLatin1(f->qtname);
665 registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleNormal,QFont::Unstretched,true,true,0,f->fixed,false,ws,nullptr);
666 registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleItalic,QFont::Unstretched,true,true,0,f->fixed,false,ws,nullptr);
667 registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleOblique,QFont::Unstretched,true,true,0,f->fixed,false,ws,nullptr);
668 ++f;
669 }
670
671 //QPA has very lazy population of the font db. We want it to be initialized when
672 //QApplication is constructed, so that the population procedure can do something like this to
673 //set the default font
674// const FcDefaultFont *s = defaults;
675// QFont font("Sans Serif");
676// font.setPointSize(9);
677// QApplication::setFont(font);
678}
679
680void QFontconfigDatabase::invalidate()
681{
682 // Clear app fonts.
683 FcConfigAppFontClear(nullptr);
684}
685
686QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine, QFontDatabasePrivate::ExtendedScript script)
687{
688 return new QFontEngineMultiFontConfig(fontEngine, script);
689}
690
691namespace {
692QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintingPreference, FcPattern *match, bool preferXftConf)
693{
694 switch (hintingPreference) {
695 case QFont::PreferNoHinting:
696 return QFontEngine::HintNone;
697 case QFont::PreferVerticalHinting:
698 return QFontEngine::HintLight;
699 case QFont::PreferFullHinting:
700 return QFontEngine::HintFull;
701 case QFont::PreferDefaultHinting:
702 break;
703 }
704
705 if (isDprScaling())
706 return QFontEngine::HintNone;
707
708 void *hintStyleResource =
709 QGuiApplication::platformNativeInterface()->nativeResourceForScreen("hintstyle",
710 QGuiApplication::primaryScreen());
711 int xftHintStyle = int(reinterpret_cast<qintptr>(hintStyleResource));
712 if (preferXftConf && xftHintStyle > 0)
713 return QFontEngine::HintStyle(xftHintStyle - 1);
714
715 int hint_style = 0;
716 if (FcPatternGetInteger (match, FC_HINT_STYLE, 0, &hint_style) == FcResultMatch) {
717 switch (hint_style) {
718 case FC_HINT_NONE:
719 return QFontEngine::HintNone;
720 case FC_HINT_SLIGHT:
721 return QFontEngine::HintLight;
722 case FC_HINT_MEDIUM:
723 return QFontEngine::HintMedium;
724 case FC_HINT_FULL:
725 return QFontEngine::HintFull;
726 default:
727 Q_UNREACHABLE();
728 break;
729 }
730 }
731 if (xftHintStyle > 0)
732 return QFontEngine::HintStyle(xftHintStyle - 1);
733
734 return QFontEngine::HintFull;
735}
736
737QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match, bool preferXftConf)
738{
739 void *subpixelTypeResource =
740 QGuiApplication::platformNativeInterface()->nativeResourceForScreen("subpixeltype",
741 QGuiApplication::primaryScreen());
742 int xftSubpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource));
743 if (preferXftConf && xftSubpixelType > 0)
744 return QFontEngine::SubpixelAntialiasingType(xftSubpixelType - 1);
745
746 int subpixel = FC_RGBA_UNKNOWN;
747 if (FcPatternGetInteger(match, FC_RGBA, 0, &subpixel) == FcResultMatch) {
748 switch (subpixel) {
749 case FC_RGBA_UNKNOWN:
750 case FC_RGBA_NONE:
751 return QFontEngine::Subpixel_None;
752 case FC_RGBA_RGB:
753 return QFontEngine::Subpixel_RGB;
754 case FC_RGBA_BGR:
755 return QFontEngine::Subpixel_BGR;
756 case FC_RGBA_VRGB:
757 return QFontEngine::Subpixel_VRGB;
758 case FC_RGBA_VBGR:
759 return QFontEngine::Subpixel_VBGR;
760 default:
761 Q_UNREACHABLE();
762 break;
763 }
764 }
765
766 if (xftSubpixelType > 0)
767 return QFontEngine::SubpixelAntialiasingType(xftSubpixelType - 1);
768
769 return QFontEngine::Subpixel_None;
770}
771} // namespace
772
773QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr)
774{
775 if (!usrPtr)
776 return nullptr;
777
778 FontFile *fontfile = static_cast<FontFile *> (usrPtr);
779 QFontEngine::FaceId fid;
780 fid.filename = QFile::encodeName(fontfile->fileName);
781 fid.index = fontfile->indexValue;
782 fid.instanceIndex = fontfile->instanceIndex;
783 fid.variableAxes = f.variableAxisValues;
784
785 // FIXME: Unify with logic in QFontEngineFT::create()
786 QFontEngineFT *engine = new QFontEngineFT(f);
787 engine->face_id = fid;
788
789 setupFontEngine(engine, f);
790
791 if (!engine->init(fid, engine->antialias, engine->defaultFormat) || engine->invalid()) {
792 delete engine;
793 engine = nullptr;
794 }
795
796 return engine;
797}
798
799QFontEngine *QFontconfigDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
800{
801 QFontEngineFT *engine = static_cast<QFontEngineFT*>(QFreeTypeFontDatabase::fontEngine(fontData, pixelSize, hintingPreference));
802 if (engine == nullptr)
803 return nullptr;
804
805 setupFontEngine(engine, engine->fontDef);
806
807 return engine;
808}
809
810QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family,
811 QFont::Style style,
812 QFont::StyleHint styleHint,
813 QFontDatabasePrivate::ExtendedScript script) const
814{
815 QStringList fallbackFamilies;
816 FcPattern *pattern = FcPatternCreate();
817 if (!pattern)
818 return fallbackFamilies;
819
820 FcValue value;
821 value.type = FcTypeString;
822 const QByteArray cs = family.toUtf8();
823 value.u.s = (const FcChar8 *)cs.data();
824 FcPatternAdd(pattern,FC_FAMILY,value,true);
825
826#ifdef FC_COLOR
827 if (script == QFontDatabasePrivate::Script_Emoji) {
828 FcPatternAddBool(pattern, FC_COLOR, true);
829 value.u.s = (const FcChar8 *)"emoji";
830 FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
831 }
832#endif
833
834 int slant_value = FC_SLANT_ROMAN;
835 if (style == QFont::StyleItalic)
836 slant_value = FC_SLANT_ITALIC;
837 else if (style == QFont::StyleOblique)
838 slant_value = FC_SLANT_OBLIQUE;
839 FcPatternAddInteger(pattern, FC_SLANT, slant_value);
840
841 Q_ASSERT(uint(script) < QFontDatabasePrivate::ScriptCount);
842 if (uint(script) < QChar::ScriptCount && *specialLanguages[script] != '\0') {
843 FcLangSet *ls = FcLangSetCreate();
844 FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]);
845 FcPatternAddLangSet(pattern, FC_LANG, ls);
846 FcLangSetDestroy(ls);
847 } else if (!family.isEmpty()) {
848 // If script is Common or Han, then it may include languages like CJK,
849 // we should attach system default language set to the pattern
850 // to obtain correct font fallback list (i.e. if LANG=zh_CN
851 // then we normally want to use a Chinese font for CJK text;
852 // while a Japanese font should be used for that if LANG=ja)
853 FcPattern *dummy = FcPatternCreate();
854 FcDefaultSubstitute(dummy);
855 FcChar8 *lang = nullptr;
856 FcResult res = FcPatternGetString(dummy, FC_LANG, 0, &lang);
857 if (res == FcResultMatch)
858 FcPatternAddString(pattern, FC_LANG, lang);
859 FcPatternDestroy(dummy);
860 }
861
862 const char *stylehint = getFcFamilyForStyleHint(styleHint);
863 if (stylehint) {
864 value.u.s = (const FcChar8 *)stylehint;
865 FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
866 }
867
868 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
869 FcDefaultSubstitute(pattern);
870
871 FcResult result = FcResultMatch;
872 FcFontSet *fontSet = FcFontSort(nullptr,pattern,FcFalse,nullptr,&result);
873 FcPatternDestroy(pattern);
874
875 if (fontSet) {
876 QDuplicateTracker<QString> duplicates(fontSet->nfont + 1);
877 (void)duplicates.hasSeen(family.toCaseFolded());
878 for (int i = 0; i < fontSet->nfont; i++) {
879 FcChar8 *value = nullptr;
880 if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
881 continue;
882 // capitalize(value);
883 const QString familyName = QString::fromUtf8((const char *)value);
884 const QString familyNameCF = familyName.toCaseFolded();
885 if (!duplicates.hasSeen(familyNameCF)) {
886 fallbackFamilies << familyName;
887 }
888 }
889 FcFontSetDestroy(fontSet);
890 }
891// qDebug() << "fallbackFamilies for:" << family << style << styleHint << script << fallbackFamilies;
892
893 return fallbackFamilies;
894}
895
896static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count, FT_Face *face)
897{
898#if FC_VERSION < 20402
899 Q_UNUSED(data);
900 *face = nullptr;
901 return FcFreeTypeQuery(file, id, blanks, count);
902#else
903 if (data.isEmpty()) {
904 *face = nullptr;
905 return FcFreeTypeQuery(file, id, blanks, count);
906 }
907
908 FT_Library lib = qt_getFreetype();
909
910 FcPattern *pattern = nullptr;
911
912 if (!FT_New_Memory_Face(lib, (const FT_Byte *)data.constData(), data.size(), id, face)) {
913 *count = (*face)->num_faces;
914
915 pattern = FcFreeTypeQueryFace(*face, file, id, blanks);
916 } else {
917 *face = nullptr;
918 }
919
920 return pattern;
921#endif
922}
923
924QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont)
925{
926 QStringList families;
927
928 if (applicationFont != nullptr)
929 applicationFont->properties.clear();
930
931 FcFontSet *set = FcConfigGetFonts(nullptr, FcSetApplication);
932 if (!set) {
933 FcConfigAppFontAddFile(nullptr, (const FcChar8 *)":/non-existent");
934 set = FcConfigGetFonts(nullptr, FcSetApplication); // try again
935 if (!set)
936 return families;
937 }
938
939 int id = 0;
940 FcBlanks *blanks = FcConfigGetBlanks(nullptr);
941 int count = 0;
942
943 FcPattern *pattern;
944 do {
945 FT_Face face;
946 pattern = queryFont((const FcChar8 *)QFile::encodeName(fileName).constData(),
947 fontData, id, blanks, &count, &face);
948 if (!pattern)
949 return families;
950
951 FcChar8 *fam = nullptr;
952 if (FcPatternGetString(pattern, FC_FAMILY, 0, &fam) == FcResultMatch) {
953 QString family = QString::fromUtf8(reinterpret_cast<const char *>(fam));
954 families << family;
955 }
956 populateFromPattern(pattern, applicationFont, face, this);
957
958 if (face)
959 FT_Done_Face(face);
960
961 FcFontSetAdd(set, pattern);
962
963 ++id;
964 } while (id < count);
965
966 return families;
967}
968
969QString QFontconfigDatabase::resolveFontFamilyAlias(const QString &family) const
970{
971 QString resolved = QFreeTypeFontDatabase::resolveFontFamilyAlias(family);
972 if (!resolved.isEmpty() && resolved != family)
973 return resolved;
974 FcPattern *pattern = FcPatternCreate();
975 if (!pattern)
976 return family;
977
978 if (!family.isEmpty()) {
979 const QByteArray cs = family.toUtf8();
980 FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) cs.constData());
981 }
982 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
983 FcDefaultSubstitute(pattern);
984
985 FcChar8 *familyAfterSubstitution = nullptr;
986 FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution);
987 resolved = QString::fromUtf8((const char *) familyAfterSubstitution);
988 FcPatternDestroy(pattern);
989
990 return resolved;
991}
992
993QFont QFontconfigDatabase::defaultFont() const
994{
995 // Hack to get system default language until FcGetDefaultLangs()
996 // is exported (https://bugs.freedesktop.org/show_bug.cgi?id=32853)
997 // or https://bugs.freedesktop.org/show_bug.cgi?id=35482 is fixed
998 FcPattern *dummy = FcPatternCreate();
999 FcDefaultSubstitute(dummy);
1000 FcChar8 *lang = nullptr;
1001 FcResult res = FcPatternGetString(dummy, FC_LANG, 0, &lang);
1002
1003 FcPattern *pattern = FcPatternCreate();
1004 if (res == FcResultMatch) {
1005 // Make defaultFont pattern matching locale language aware, because
1006 // certain FC_LANG based custom rules may happen in FcConfigSubstitute()
1007 FcPatternAddString(pattern, FC_LANG, lang);
1008 }
1009 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
1010 FcDefaultSubstitute(pattern);
1011
1012 FcChar8 *familyAfterSubstitution = nullptr;
1013 FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution);
1014 QString resolved = QString::fromUtf8((const char *) familyAfterSubstitution);
1015 FcPatternDestroy(pattern);
1016 FcPatternDestroy(dummy);
1017
1018 return QFont(resolved);
1019}
1020
1021void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef &fontDef) const
1022{
1023 bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
1024 bool forcedAntialiasSetting = !antialias || isDprScaling();
1025
1026 const QPlatformServices *services = QGuiApplicationPrivate::platformIntegration()->services();
1027 bool preferXftConf = false;
1028
1029 if (services) {
1030 const QList<QByteArray> desktopEnv = services->desktopEnvironment().split(':');
1031 preferXftConf = !(desktopEnv.contains("KDE") || desktopEnv.contains("LXQT") || desktopEnv.contains("UKUI"));
1032 }
1033
1034 QFontEngine::GlyphFormat format;
1035 // try and get the pattern
1036 FcPattern *pattern = FcPatternCreate();
1037 FcPattern *match = nullptr;
1038
1039 FcValue value;
1040 value.type = FcTypeString;
1041 QByteArray cs = fontDef.families.first().toUtf8();
1042 value.u.s = (const FcChar8 *)cs.data();
1043 FcPatternAdd(pattern,FC_FAMILY,value,true);
1044
1045 QFontEngine::FaceId fid = engine->faceId();
1046
1047 if (!fid.filename.isEmpty()) {
1048 value.u.s = (const FcChar8 *)fid.filename.data();
1049 FcPatternAdd(pattern,FC_FILE,value,true);
1050
1051 value.type = FcTypeInteger;
1052 value.u.i = fid.index;
1053 FcPatternAdd(pattern,FC_INDEX,value,true);
1054 }
1055
1056 if (!qFuzzyIsNull(fontDef.pixelSize))
1057 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontDef.pixelSize);
1058
1059 FcResult result;
1060
1061 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
1062 FcDefaultSubstitute(pattern);
1063
1064#ifdef FC_VARIABLE
1065 if (!fid.filename.isEmpty()) {
1066 // FC_INDEX is ignored during processing in FcFontMatch.
1067 // So iterate FcPatterns directly and find it out.
1068 FcFontSet *fcsets[2], *fcfs;
1069
1070 fcsets[0] = FcConfigGetFonts(nullptr, FcSetSystem);
1071 fcsets[1] = FcConfigGetFonts(nullptr, FcSetApplication);
1072 for (int nset = 0; nset < 2; nset++) {
1073 fcfs = fcsets[nset];
1074 if (fcfs == nullptr)
1075 continue;
1076 for (int fnum = 0; fnum < fcfs->nfont; fnum++) {
1077 FcPattern *fcpat = fcfs->fonts[fnum];
1078 FcChar8 *fcfile;
1079 FcBool variable;
1080 double fcpixelsize;
1081 int fcindex;
1082
1083 // Skip the variable font itself, only to use the named instances and normal fonts here
1084 if (FcPatternGetBool(fcpat, FC_VARIABLE, 0, &variable) == FcResultMatch &&
1085 variable == FcTrue)
1086 continue;
1087
1088 if (!qFuzzyIsNull(fontDef.pixelSize)) {
1089 if (FcPatternGetDouble(fcpat, FC_PIXEL_SIZE, 0, &fcpixelsize) == FcResultMatch &&
1090 fontDef.pixelSize != fcpixelsize)
1091 continue;
1092 }
1093
1094 if (FcPatternGetString(fcpat, FC_FILE, 0, &fcfile) == FcResultMatch &&
1095 FcPatternGetInteger(fcpat, FC_INDEX, 0, &fcindex) == FcResultMatch) {
1096 QByteArray f = QByteArray::fromRawData((const char *)fcfile,
1097 qstrlen((const char *)fcfile));
1098 if (f == fid.filename && fcindex == fid.index) {
1099 // We found it.
1100 match = FcFontRenderPrepare(nullptr, pattern, fcpat);
1101 goto bail;
1102 }
1103 }
1104 }
1105 }
1106 }
1107bail:
1108#endif
1109
1110 if (!match)
1111 match = FcFontMatch(nullptr, pattern, &result);
1112
1113 int xftAntialias = 0;
1114 if (!forcedAntialiasSetting) {
1115 void *antialiasResource =
1116 QGuiApplication::platformNativeInterface()->nativeResourceForScreen("antialiasingEnabled",
1117 QGuiApplication::primaryScreen());
1118 xftAntialias = int(reinterpret_cast<qintptr>(antialiasResource));
1119 if ((preferXftConf || !match) && xftAntialias > 0) {
1120 antialias = xftAntialias - 1;
1121 forcedAntialiasSetting = true;
1122 }
1123 }
1124 if (match) {
1125 engine->setDefaultHintStyle(defaultHintStyleFromMatch((QFont::HintingPreference)fontDef.hintingPreference, match, preferXftConf));
1126
1127 FcBool fc_autohint;
1128 if (FcPatternGetBool(match, FC_AUTOHINT,0, &fc_autohint) == FcResultMatch)
1129 engine->forceAutoHint = fc_autohint;
1130
1131#if defined(FT_LCD_FILTER_H)
1132 int lcdFilter;
1133 if (FcPatternGetInteger(match, FC_LCD_FILTER, 0, &lcdFilter) == FcResultMatch)
1134 engine->lcdFilterType = lcdFilter;
1135#endif
1136
1137 if (!forcedAntialiasSetting) {
1138 FcBool fc_antialias;
1139 if (FcPatternGetBool(match, FC_ANTIALIAS,0, &fc_antialias) == FcResultMatch)
1140 antialias = fc_antialias;
1141 }
1142
1143 if (antialias) {
1144 QFontEngine::SubpixelAntialiasingType subpixelType = QFontEngine::Subpixel_None;
1145 if (!(fontDef.styleStrategy & QFont::NoSubpixelAntialias))
1146 subpixelType = subpixelTypeFromMatch(match, preferXftConf);
1147 engine->subpixelType = subpixelType;
1148 }
1149
1150 FcPatternDestroy(match);
1151 } else {
1152 void *hintStyleResource =
1153 QGuiApplication::platformNativeInterface()->nativeResourceForScreen("hintstyle",
1154 QGuiApplication::primaryScreen());
1155 int xftHintStyle = int(reinterpret_cast<qintptr>(hintStyleResource));
1156 if (xftHintStyle > 0)
1157 engine->setDefaultHintStyle(QFontEngine::HintStyle(xftHintStyle - 1));
1158 if (antialias) {
1159 engine->subpixelType = QFontEngine::Subpixel_None;
1160 if (!(fontDef.styleStrategy & QFont::NoSubpixelAntialias)) {
1161 void *subpixelTypeResource =
1162 QGuiApplication::platformNativeInterface()->nativeResourceForScreen("subpixeltype",
1163 QGuiApplication::primaryScreen());
1164 int xftSubpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource));
1165 if (xftSubpixelType > 1)
1166 engine->subpixelType = QFontEngine::SubpixelAntialiasingType(xftSubpixelType - 1);
1167 }
1168 }
1169 }
1170 if (antialias) {
1171 format = (engine->subpixelType == QFontEngine::Subpixel_None)
1172 ? QFontEngine::Format_A8
1173 : QFontEngine::Format_A32;
1174 } else {
1175 format = QFontEngine::Format_Mono;
1176 }
1177
1178 FcPatternDestroy(pattern);
1179
1180 engine->antialias = antialias;
1181 engine->defaultFormat = format;
1182 engine->glyphFormat = format;
1183}
1184
1185bool QFontconfigDatabase::supportsVariableApplicationFonts() const
1186{
1187#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
1188 return true;
1189#else
1190 return false;
1191#endif
1192}
1193
1194QT_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)