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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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