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