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