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 void *hintStyleResource =
719 QGuiApplication::platformNativeInterface()->nativeResourceForScreen("hintstyle",
720 QGuiApplication::primaryScreen());
721 int xftHintStyle = int(reinterpret_cast<qintptr>(hintStyleResource));
722 if (preferXftConf && xftHintStyle > 0)
723 return QFontEngine::HintStyle(xftHintStyle - 1);
724
725 int hint_style = 0;
726 if (FcPatternGetInteger (match, FC_HINT_STYLE, 0, &hint_style) == FcResultMatch) {
727 switch (hint_style) {
728 case FC_HINT_NONE:
729 return QFontEngine::HintNone;
730 case FC_HINT_SLIGHT:
731 return QFontEngine::HintLight;
732 case FC_HINT_MEDIUM:
733 return QFontEngine::HintMedium;
734 case FC_HINT_FULL:
735 return QFontEngine::HintFull;
736 default:
737 Q_UNREACHABLE();
738 break;
739 }
740 }
741 if (xftHintStyle > 0)
742 return QFontEngine::HintStyle(xftHintStyle - 1);
743
744 return QFontEngine::HintFull;
745}
746
747QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match, bool preferXftConf)
748{
749 void *subpixelTypeResource =
750 QGuiApplication::platformNativeInterface()->nativeResourceForScreen("subpixeltype",
751 QGuiApplication::primaryScreen());
752 int xftSubpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource));
753 if (preferXftConf && xftSubpixelType > 0)
754 return QFontEngine::SubpixelAntialiasingType(xftSubpixelType - 1);
755
756 int subpixel = FC_RGBA_UNKNOWN;
757 if (FcPatternGetInteger(match, FC_RGBA, 0, &subpixel) == FcResultMatch) {
758 switch (subpixel) {
759 case FC_RGBA_UNKNOWN:
760 case FC_RGBA_NONE:
761 return QFontEngine::Subpixel_None;
762 case FC_RGBA_RGB:
763 return QFontEngine::Subpixel_RGB;
764 case FC_RGBA_BGR:
765 return QFontEngine::Subpixel_BGR;
766 case FC_RGBA_VRGB:
767 return QFontEngine::Subpixel_VRGB;
768 case FC_RGBA_VBGR:
769 return QFontEngine::Subpixel_VBGR;
770 default:
771 Q_UNREACHABLE();
772 break;
773 }
774 }
775
776 if (xftSubpixelType > 0)
777 return QFontEngine::SubpixelAntialiasingType(xftSubpixelType - 1);
778
779 return QFontEngine::Subpixel_None;
780}
781} // namespace
782
783QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr)
784{
785 if (!usrPtr)
786 return nullptr;
787
788 FontFile *fontfile = static_cast<FontFile *> (usrPtr);
789 QFontEngine::FaceId fid;
790 fid.filename = QFile::encodeName(fontfile->fileName);
791 fid.index = fontfile->indexValue;
792 fid.instanceIndex = fontfile->instanceIndex;
793 fid.variableAxes = f.variableAxisValues;
794
795 // FIXME: Unify with logic in QFontEngineFT::create()
796 QFontEngineFT *engine = new QFontEngineFT(f);
797 engine->face_id = fid;
798
799 setupFontEngine(engine, f);
800
801 if (!engine->init(fid, engine->antialias, engine->defaultFormat) || engine->invalid()) {
802 delete engine;
803 engine = nullptr;
804 }
805
806 return engine;
807}
808
809QFontEngine *QFontconfigDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
810{
811 QFontEngineFT *engine = static_cast<QFontEngineFT*>(QFreeTypeFontDatabase::fontEngine(fontData, pixelSize, hintingPreference));
812 if (engine == nullptr)
813 return nullptr;
814
815 setupFontEngine(engine, engine->fontDef);
816
817 return engine;
818}
819
820QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family,
821 QFont::Style style,
822 QFont::StyleHint styleHint,
823 QFontDatabasePrivate::ExtendedScript script) const
824{
825 QStringList fallbackFamilies;
826 FcPattern *pattern = FcPatternCreate();
827 if (!pattern)
828 return fallbackFamilies;
829
830 FcValue value;
831 value.type = FcTypeString;
832 const QByteArray cs = family.toUtf8();
833 value.u.s = (const FcChar8 *)cs.data();
834 FcPatternAdd(pattern,FC_FAMILY,value,true);
835
836#ifdef FC_COLOR
837 if (script == QFontDatabasePrivate::Script_Emoji) {
838 FcPatternAddBool(pattern, FC_COLOR, true);
839 value.u.s = (const FcChar8 *)"emoji";
840 FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
841 }
842#endif
843
844 int slant_value = FC_SLANT_ROMAN;
845 if (style == QFont::StyleItalic)
846 slant_value = FC_SLANT_ITALIC;
847 else if (style == QFont::StyleOblique)
848 slant_value = FC_SLANT_OBLIQUE;
849 FcPatternAddInteger(pattern, FC_SLANT, slant_value);
850
851 Q_ASSERT(uint(script) < QFontDatabasePrivate::ScriptCount);
852 if (uint(script) < QChar::ScriptCount && *specialLanguages[script] != '\0') {
853 FcLangSet *ls = FcLangSetCreate();
854 FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]);
855 FcPatternAddLangSet(pattern, FC_LANG, ls);
856 FcLangSetDestroy(ls);
857 } else if (!family.isEmpty()) {
858 // If script is Common or Han, then it may include languages like CJK,
859 // we should attach system default language set to the pattern
860 // to obtain correct font fallback list (i.e. if LANG=zh_CN
861 // then we normally want to use a Chinese font for CJK text;
862 // while a Japanese font should be used for that if LANG=ja)
863 FcPattern *dummy = FcPatternCreate();
864 FcDefaultSubstitute(dummy);
865 FcChar8 *lang = nullptr;
866 FcResult res = FcPatternGetString(dummy, FC_LANG, 0, &lang);
867 if (res == FcResultMatch)
868 FcPatternAddString(pattern, FC_LANG, lang);
869 FcPatternDestroy(dummy);
870 }
871
872 const char *stylehint = getFcFamilyForStyleHint(styleHint);
873 if (stylehint) {
874 value.u.s = (const FcChar8 *)stylehint;
875 FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
876 }
877
878 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
879 FcDefaultSubstitute(pattern);
880
881 FcResult result = FcResultMatch;
882 FcFontSet *fontSet = FcFontSort(nullptr,pattern,FcFalse,nullptr,&result);
883 FcPatternDestroy(pattern);
884
885 if (fontSet) {
886 QDuplicateTracker<QString> duplicates(fontSet->nfont + 1);
887 (void)duplicates.hasSeen(family.toCaseFolded());
888 for (int i = 0; i < fontSet->nfont; i++) {
889 FcChar8 *value = nullptr;
890 if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
891 continue;
892 // capitalize(value);
893 const QString familyName = QString::fromUtf8((const char *)value);
894 const QString familyNameCF = familyName.toCaseFolded();
895 if (!duplicates.hasSeen(familyNameCF)) {
896 fallbackFamilies << familyName;
897 }
898 }
899 FcFontSetDestroy(fontSet);
900 }
901// qDebug() << "fallbackFamilies for:" << family << style << styleHint << script << fallbackFamilies;
902
903 return fallbackFamilies;
904}
905
906static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count, FT_Face *face)
907{
908#if FC_VERSION < 20402
909 Q_UNUSED(data);
910 *face = nullptr;
911 return FcFreeTypeQuery(file, id, blanks, count);
912#else
913 if (data.isEmpty()) {
914 *face = nullptr;
915 return FcFreeTypeQuery(file, id, blanks, count);
916 }
917
918 FT_Library lib = qt_getFreetype();
919
920 FcPattern *pattern = nullptr;
921
922 if (!FT_New_Memory_Face(lib, (const FT_Byte *)data.constData(), data.size(), id, face)) {
923 *count = (*face)->num_faces;
924
925 pattern = FcFreeTypeQueryFace(*face, file, id, blanks);
926 } else {
927 *face = nullptr;
928 }
929
930 return pattern;
931#endif
932}
933
934QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont)
935{
936 QStringList families;
937
938 if (applicationFont != nullptr)
939 applicationFont->properties.clear();
940
941 FcFontSet *set = FcConfigGetFonts(nullptr, FcSetApplication);
942 if (!set) {
943 FcConfigAppFontAddFile(nullptr, (const FcChar8 *)":/non-existent");
944 set = FcConfigGetFonts(nullptr, FcSetApplication); // try again
945 if (!set)
946 return families;
947 }
948
949 int id = 0;
950 FcBlanks *blanks = FcConfigGetBlanks(nullptr);
951 int count = 0;
952
953 FcPattern *pattern;
954 do {
955 FT_Face face;
956 pattern = queryFont((const FcChar8 *)QFile::encodeName(fileName).constData(),
957 fontData, id, blanks, &count, &face);
958 if (!pattern)
959 return families;
960
961 FcChar8 *fam = nullptr;
962 if (FcPatternGetString(pattern, FC_FAMILY, 0, &fam) == FcResultMatch) {
963 QString family = QString::fromUtf8(reinterpret_cast<const char *>(fam));
964 families << family;
965 }
966 populateFromPattern(pattern, applicationFont, face, this);
967
968 if (face)
969 FT_Done_Face(face);
970
971 FcFontSetAdd(set, pattern);
972
973 ++id;
974 } while (id < count);
975
976 return families;
977}
978
979QString QFontconfigDatabase::resolveFontFamilyAlias(const QString &family) const
980{
981 QString resolved = QFreeTypeFontDatabase::resolveFontFamilyAlias(family);
982 if (!resolved.isEmpty() && resolved != family)
983 return resolved;
984 FcPattern *pattern = FcPatternCreate();
985 if (!pattern)
986 return family;
987
988 if (!family.isEmpty()) {
989 const QByteArray cs = family.toUtf8();
990 FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) cs.constData());
991 }
992 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
993 FcDefaultSubstitute(pattern);
994
995 FcChar8 *familyAfterSubstitution = nullptr;
996 FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution);
997 resolved = QString::fromUtf8((const char *) familyAfterSubstitution);
998 FcPatternDestroy(pattern);
999
1000 return resolved;
1001}
1002
1003QFont QFontconfigDatabase::defaultFont() const
1004{
1005 // Hack to get system default language until FcGetDefaultLangs()
1006 // is exported (https://bugs.freedesktop.org/show_bug.cgi?id=32853)
1007 // or https://bugs.freedesktop.org/show_bug.cgi?id=35482 is fixed
1008 FcPattern *dummy = FcPatternCreate();
1009 FcDefaultSubstitute(dummy);
1010 FcChar8 *lang = nullptr;
1011 FcResult res = FcPatternGetString(dummy, FC_LANG, 0, &lang);
1012
1013 FcPattern *pattern = FcPatternCreate();
1014 if (res == FcResultMatch) {
1015 // Make defaultFont pattern matching locale language aware, because
1016 // certain FC_LANG based custom rules may happen in FcConfigSubstitute()
1017 FcPatternAddString(pattern, FC_LANG, lang);
1018 }
1019 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
1020 FcDefaultSubstitute(pattern);
1021
1022 FcChar8 *familyAfterSubstitution = nullptr;
1023 FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution);
1024 QString resolved = QString::fromUtf8((const char *) familyAfterSubstitution);
1025 FcPatternDestroy(pattern);
1026 FcPatternDestroy(dummy);
1027
1028 return QFont(resolved);
1029}
1030
1031void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef &fontDef) const
1032{
1033 bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
1034 bool forcedAntialiasSetting = !antialias || isDprScaling();
1035
1036 const QPlatformServices *services = QGuiApplicationPrivate::platformIntegration()->services();
1037 bool preferXftConf = false;
1038
1039 if (services) {
1040 const QList<QByteArray> desktopEnv = services->desktopEnvironment().split(':');
1041 preferXftConf = !(desktopEnv.contains("KDE") || desktopEnv.contains("LXQT") || desktopEnv.contains("UKUI"));
1042 }
1043
1044 QFontEngine::GlyphFormat format;
1045 // try and get the pattern
1046 FcPattern *pattern = FcPatternCreate();
1047 FcPattern *match = nullptr;
1048
1049 FcValue value;
1050 value.type = FcTypeString;
1051 QByteArray cs = fontDef.family().toUtf8();
1052 value.u.s = (const FcChar8 *)cs.data();
1053 FcPatternAdd(pattern,FC_FAMILY,value,true);
1054
1055 QFontEngine::FaceId fid = engine->faceId();
1056
1057 if (!fid.filename.isEmpty()) {
1058 value.u.s = (const FcChar8 *)fid.filename.data();
1059 FcPatternAdd(pattern,FC_FILE,value,true);
1060
1061 value.type = FcTypeInteger;
1062 value.u.i = fid.index;
1063 FcPatternAdd(pattern,FC_INDEX,value,true);
1064 }
1065
1066 if (!qFuzzyIsNull(fontDef.pixelSize))
1067 FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontDef.pixelSize);
1068
1069 FcResult result;
1070
1071 FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
1072 FcDefaultSubstitute(pattern);
1073
1074#ifdef FC_VARIABLE
1075 if (!fid.filename.isEmpty()) {
1076 // FC_INDEX is ignored during processing in FcFontMatch.
1077 // So iterate FcPatterns directly and find it out.
1078 FcFontSet *fcsets[2], *fcfs;
1079
1080 fcsets[0] = FcConfigGetFonts(nullptr, FcSetSystem);
1081 fcsets[1] = FcConfigGetFonts(nullptr, FcSetApplication);
1082 for (int nset = 0; nset < 2; nset++) {
1083 fcfs = fcsets[nset];
1084 if (fcfs == nullptr)
1085 continue;
1086 for (int fnum = 0; fnum < fcfs->nfont; fnum++) {
1087 FcPattern *fcpat = fcfs->fonts[fnum];
1088 FcChar8 *fcfile;
1089 FcBool variable;
1090 double fcpixelsize;
1091 int fcindex;
1092
1093 // Skip the variable font itself, only to use the named instances and normal fonts here
1094 if (FcPatternGetBool(fcpat, FC_VARIABLE, 0, &variable) == FcResultMatch &&
1095 variable == FcTrue)
1096 continue;
1097
1098 if (!qFuzzyIsNull(fontDef.pixelSize)) {
1099 if (FcPatternGetDouble(fcpat, FC_PIXEL_SIZE, 0, &fcpixelsize) == FcResultMatch &&
1100 fontDef.pixelSize != fcpixelsize)
1101 continue;
1102 }
1103
1104 if (FcPatternGetString(fcpat, FC_FILE, 0, &fcfile) == FcResultMatch &&
1105 FcPatternGetInteger(fcpat, FC_INDEX, 0, &fcindex) == FcResultMatch) {
1106 QByteArray f = QByteArray::fromRawData((const char *)fcfile,
1107 qstrlen((const char *)fcfile));
1108 if (f == fid.filename && fcindex == fid.index) {
1109 // We found it.
1110 match = FcFontRenderPrepare(nullptr, pattern, fcpat);
1111 goto bail;
1112 }
1113 }
1114 }
1115 }
1116 }
1117bail:
1118#endif
1119
1120 if (!match)
1121 match = FcFontMatch(nullptr, pattern, &result);
1122
1123 int xftAntialias = 0;
1124 if (!forcedAntialiasSetting) {
1125 void *antialiasResource =
1126 QGuiApplication::platformNativeInterface()->nativeResourceForScreen("antialiasingEnabled",
1127 QGuiApplication::primaryScreen());
1128 xftAntialias = int(reinterpret_cast<qintptr>(antialiasResource));
1129 if ((preferXftConf || !match) && xftAntialias > 0) {
1130 antialias = xftAntialias - 1;
1131 forcedAntialiasSetting = true;
1132 }
1133 }
1134 if (match) {
1135 engine->setDefaultHintStyle(defaultHintStyleFromMatch((QFont::HintingPreference)fontDef.hintingPreference, match, preferXftConf));
1136
1137 FcBool fc_autohint;
1138 if (FcPatternGetBool(match, FC_AUTOHINT,0, &fc_autohint) == FcResultMatch)
1139 engine->forceAutoHint = fc_autohint;
1140
1141#if defined(FT_LCD_FILTER_H)
1142 int lcdFilter;
1143 if (FcPatternGetInteger(match, FC_LCD_FILTER, 0, &lcdFilter) == FcResultMatch)
1144 engine->lcdFilterType = lcdFilter;
1145#endif
1146
1147 if (!forcedAntialiasSetting) {
1148 FcBool fc_antialias;
1149 if (FcPatternGetBool(match, FC_ANTIALIAS,0, &fc_antialias) == FcResultMatch)
1150 antialias = fc_antialias;
1151 }
1152
1153 if (antialias) {
1154 QFontEngine::SubpixelAntialiasingType subpixelType = QFontEngine::Subpixel_None;
1155 if (!(fontDef.styleStrategy & QFont::NoSubpixelAntialias))
1156 subpixelType = subpixelTypeFromMatch(match, preferXftConf);
1157 engine->subpixelType = subpixelType;
1158 }
1159
1160 FcPatternDestroy(match);
1161 } else {
1162 void *hintStyleResource =
1163 QGuiApplication::platformNativeInterface()->nativeResourceForScreen("hintstyle",
1164 QGuiApplication::primaryScreen());
1165 int xftHintStyle = int(reinterpret_cast<qintptr>(hintStyleResource));
1166 if (xftHintStyle > 0)
1167 engine->setDefaultHintStyle(QFontEngine::HintStyle(xftHintStyle - 1));
1168 if (antialias) {
1169 engine->subpixelType = QFontEngine::Subpixel_None;
1170 if (!(fontDef.styleStrategy & QFont::NoSubpixelAntialias)) {
1171 void *subpixelTypeResource =
1172 QGuiApplication::platformNativeInterface()->nativeResourceForScreen("subpixeltype",
1173 QGuiApplication::primaryScreen());
1174 int xftSubpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource));
1175 if (xftSubpixelType > 1)
1176 engine->subpixelType = QFontEngine::SubpixelAntialiasingType(xftSubpixelType - 1);
1177 }
1178 }
1179 }
1180 if (antialias) {
1181 format = (engine->subpixelType == QFontEngine::Subpixel_None)
1182 ? QFontEngine::Format_A8
1183 : QFontEngine::Format_A32;
1184 } else {
1185 format = QFontEngine::Format_Mono;
1186 }
1187
1188 FcPatternDestroy(pattern);
1189
1190 engine->antialias = antialias;
1191 engine->defaultFormat = format;
1192 engine->glyphFormat = format;
1193
1194 // The device pixel ratio scales glyphs after the layout, so they can't be fitted horizontally
1195 if (isDprScaling() && fontDef.hintingPreference == QFont::PreferDefaultHinting) {
1196 static const bool disabled =
1197 qEnvironmentVariableIntValue("QT_DISABLE_DPR_FONT_HINTING") > 0;
1198 if (disabled || !engine->setVerticalHintingOnly())
1199 engine->setDefaultHintStyle(QFontEngine::HintNone);
1200 }
1201}
1202
1203bool QFontconfigDatabase::supportsVariableApplicationFonts() const
1204{
1205#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
1206 return true;
1207#else
1208 return false;
1209#endif
1210}
1211
1212QT_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)