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