Correct typo in the Gujarati openType identififer.
[qt:qt.git] / src / plugins / platforms / fontdatabases / fontconfig / qfontconfigdatabase.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qfontconfigdatabase.h"
43
44 #include <QtCore/QList>
45 #include <QtGui/private/qfont_p.h>
46
47 #include <QtCore/QElapsedTimer>
48
49 #include <QtGui/private/qapplication_p.h>
50 #include <QtGui/QPlatformScreen>
51
52 #include <QtGui/private/qfontengine_ft_p.h>
53 #include <QtGui/private/qfontengine_p.h>
54
55
56
57 #include <ft2build.h>
58 #include FT_TRUETYPE_TABLES_H
59
60 #include <fontconfig/fontconfig.h>
61
62 #define SimplifiedChineseCsbBit 18
63 #define TraditionalChineseCsbBit 20
64 #define JapaneseCsbBit 17
65 #define KoreanCsbBit 21
66
67 static inline bool requiresOpenType(int writingSystem)
68 {
69     return ((writingSystem >= QFontDatabase::Syriac && writingSystem <= QFontDatabase::Sinhala)
70             || writingSystem == QFontDatabase::Khmer || writingSystem == QFontDatabase::Nko);
71 }
72 static inline bool scriptRequiresOpenType(int script)
73 {
74     return ((script >= QUnicodeTables::Syriac && script <= QUnicodeTables::Sinhala)
75             || script == QUnicodeTables::Khmer || script == QUnicodeTables::Nko);
76 }
77
78 static int getFCWeight(int fc_weight)
79 {
80     int qtweight = QFont::Black;
81     if (fc_weight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_MEDIUM) / 2)
82         qtweight = QFont::Light;
83     else if (fc_weight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2)
84         qtweight = QFont::Normal;
85     else if (fc_weight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2)
86         qtweight = QFont::DemiBold;
87     else if (fc_weight <= (FC_WEIGHT_BOLD + FC_WEIGHT_BLACK) / 2)
88         qtweight = QFont::Bold;
89
90     return qtweight;
91 }
92
93 static const char *specialLanguages[] = {
94     "en", // Common
95     "el", // Greek
96     "ru", // Cyrillic
97     "hy", // Armenian
98     "he", // Hebrew
99     "ar", // Arabic
100     "syr", // Syriac
101     "div", // Thaana
102     "hi", // Devanagari
103     "bn", // Bengali
104     "pa", // Gurmukhi
105     "gu", // Gujarati
106     "or", // Oriya
107     "ta", // Tamil
108     "te", // Telugu
109     "kn", // Kannada
110     "ml", // Malayalam
111     "si", // Sinhala
112     "th", // Thai
113     "lo", // Lao
114     "bo", // Tibetan
115     "my", // Myanmar
116     "ka", // Georgian
117     "ko", // Hangul
118     "", // Ogham
119     "", // Runic
120     "km", // Khmer
121     "" // N'Ko
122 };
123 enum { SpecialLanguageCount = sizeof(specialLanguages) / sizeof(const char *) };
124
125 static const ushort specialChars[] = {
126     0, // English
127     0, // Greek
128     0, // Cyrillic
129     0, // Armenian
130     0, // Hebrew
131     0, // Arabic
132     0, // Syriac
133     0, // Thaana
134     0, // Devanagari
135     0, // Bengali
136     0, // Gurmukhi
137     0, // Gujarati
138     0, // Oriya
139     0, // Tamil
140     0xc15, // Telugu
141     0xc95, // Kannada
142     0xd15, // Malayalam
143     0xd9a, // Sinhala
144     0, // Thai
145     0, // Lao
146     0, // Tibetan
147     0x1000, // Myanmar
148     0, // Georgian
149     0, // Hangul
150     0x1681, // Ogham
151     0x16a0, // Runic
152     0,  // Khmer
153     0x7ca // N'Ko
154 };
155 enum { SpecialCharCount = sizeof(specialChars) / sizeof(ushort) };
156
157 // this could become a list of all languages used for each writing
158 // system, instead of using the single most common language.
159 static const char *languageForWritingSystem[] = {
160     0,     // Any
161     "en",  // Latin
162     "el",  // Greek
163     "ru",  // Cyrillic
164     "hy",  // Armenian
165     "he",  // Hebrew
166     "ar",  // Arabic
167     "syr", // Syriac
168     "div", // Thaana
169     "hi",  // Devanagari
170     "bn",  // Bengali
171     "pa",  // Gurmukhi
172     "gu",  // Gujarati
173     "or",  // Oriya
174     "ta",  // Tamil
175     "te",  // Telugu
176     "kn",  // Kannada
177     "ml",  // Malayalam
178     "si",  // Sinhala
179     "th",  // Thai
180     "lo",  // Lao
181     "bo",  // Tibetan
182     "my",  // Myanmar
183     "ka",  // Georgian
184     "km",  // Khmer
185     "zh-cn", // SimplifiedChinese
186     "zh-tw", // TraditionalChinese
187     "ja",  // Japanese
188     "ko",  // Korean
189     "vi",  // Vietnamese
190     0, // Symbol
191     0, // Ogham
192     0, // Runic
193     0 // N'Ko
194 };
195 enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) };
196
197 // Unfortunately FontConfig doesn't know about some languages. We have to test these through the
198 // charset. The lists below contain the systems where we need to do this.
199 static const ushort sampleCharForWritingSystem[] = {
200     0,     // Any
201     0,  // Latin
202     0,  // Greek
203     0,  // Cyrillic
204     0,  // Armenian
205     0,  // Hebrew
206     0,  // Arabic
207     0, // Syriac
208     0, // Thaana
209     0,  // Devanagari
210     0,  // Bengali
211     0,  // Gurmukhi
212     0,  // Gujarati
213     0,  // Oriya
214     0,  // Tamil
215     0xc15,  // Telugu
216     0xc95,  // Kannada
217     0xd15,  // Malayalam
218     0xd9a,  // Sinhala
219     0,  // Thai
220     0,  // Lao
221     0,  // Tibetan
222     0x1000,  // Myanmar
223     0,  // Georgian
224     0,  // Khmer
225     0, // SimplifiedChinese
226     0, // TraditionalChinese
227     0,  // Japanese
228     0,  // Korean
229     0,  // Vietnamese
230     0, // Symbol
231     0x1681, // Ogham
232     0x16a0, // Runic
233     0x7ca // N'Ko
234 };
235 enum { SampleCharCount = sizeof(sampleCharForWritingSystem) / sizeof(ushort) };
236
237 // Newer FontConfig let's us sort out fonts that contain certain glyphs, but no
238 // open type tables for is directly. Do this so we don't pick some strange
239 // pseudo unicode font
240 static const char *openType[] = {
241     0,     // Any
242     0,  // Latin
243     0,  // Greek
244     0,  // Cyrillic
245     0,  // Armenian
246     0,  // Hebrew
247     0,  // Arabic
248     "syrc",  // Syriac
249     "thaa",  // Thaana
250     "deva",  // Devanagari
251     "beng",  // Bengali
252     "guru",  // Gurmukhi
253     "gujr",  // Gujarati
254     "orya",  // Oriya
255     "taml",  // Tamil
256     "telu",  // Telugu
257     "knda",  // Kannada
258     "mlym",  // Malayalam
259     "sinh",  // Sinhala
260     0,  // Thai
261     0,  // Lao
262     "tibt",  // Tibetan
263     "mymr",  // Myanmar
264     0,  // Georgian
265     "khmr",  // Khmer
266     0, // SimplifiedChinese
267     0, // TraditionalChinese
268     0,  // Japanese
269     0,  // Korean
270     0,  // Vietnamese
271     0, // Symbol
272     0, // Ogham
273     0, // Runic
274     "nko " // N'Ko
275 };
276
277 static const char *getFcFamilyForStyleHint(const QFont::StyleHint style)
278 {
279     const char *stylehint = 0;
280     switch (style) {
281     case QFont::SansSerif:
282         stylehint = "sans-serif";
283         break;
284     case QFont::Serif:
285         stylehint = "serif";
286         break;
287     case QFont::TypeWriter:
288         stylehint = "monospace";
289         break;
290     default:
291         break;
292     }
293     return stylehint;
294 }
295
296 void QFontconfigDatabase::populateFontDatabase()
297 {
298     FcFontSet  *fonts;
299
300     QString familyName;
301     FcChar8 *value = 0;
302     int weight_value;
303     int slant_value;
304     int spacing_value;
305     FcChar8 *file_value;
306     int indexValue;
307     FcChar8 *foundry_value;
308     FcBool scalable;
309     FcBool antialias;
310
311     {
312         FcObjectSet *os = FcObjectSetCreate();
313         FcPattern *pattern = FcPatternCreate();
314         const char *properties [] = {
315             FC_FAMILY, FC_WEIGHT, FC_SLANT,
316             FC_SPACING, FC_FILE, FC_INDEX,
317             FC_LANG, FC_CHARSET, FC_FOUNDRY, FC_SCALABLE, FC_PIXEL_SIZE,
318             FC_WIDTH,
319 #if FC_VERSION >= 20297
320             FC_CAPABILITY,
321 #endif
322             (const char *)0
323         };
324         const char **p = properties;
325         while (*p) {
326             FcObjectSetAdd(os, *p);
327             ++p;
328         }
329         fonts = FcFontList(0, pattern, os);
330         FcObjectSetDestroy(os);
331         FcPatternDestroy(pattern);
332     }
333
334     for (int i = 0; i < fonts->nfont; i++) {
335         if (FcPatternGetString(fonts->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
336             continue;
337         //         capitalize(value);
338         familyName = QString::fromUtf8((const char *)value);
339         slant_value = FC_SLANT_ROMAN;
340         weight_value = FC_WEIGHT_MEDIUM;
341         spacing_value = FC_PROPORTIONAL;
342         file_value = 0;
343         indexValue = 0;
344         scalable = FcTrue;
345
346
347         if (FcPatternGetInteger (fonts->fonts[i], FC_SLANT, 0, &slant_value) != FcResultMatch)
348             slant_value = FC_SLANT_ROMAN;
349         if (FcPatternGetInteger (fonts->fonts[i], FC_WEIGHT, 0, &weight_value) != FcResultMatch)
350             weight_value = FC_WEIGHT_MEDIUM;
351         if (FcPatternGetInteger (fonts->fonts[i], FC_SPACING, 0, &spacing_value) != FcResultMatch)
352             spacing_value = FC_PROPORTIONAL;
353         if (FcPatternGetString (fonts->fonts[i], FC_FILE, 0, &file_value) != FcResultMatch)
354             file_value = 0;
355         if (FcPatternGetInteger (fonts->fonts[i], FC_INDEX, 0, &indexValue) != FcResultMatch)
356             indexValue = 0;
357         if (FcPatternGetBool(fonts->fonts[i], FC_SCALABLE, 0, &scalable) != FcResultMatch)
358             scalable = FcTrue;
359         if (FcPatternGetString(fonts->fonts[i], FC_FOUNDRY, 0, &foundry_value) != FcResultMatch)
360             foundry_value = 0;
361         if(FcPatternGetBool(fonts->fonts[i],FC_ANTIALIAS,0,&antialias) != FcResultMatch)
362             antialias = true;
363
364         QSupportedWritingSystems writingSystems;
365         FcLangSet *langset = 0;
366         FcResult res = FcPatternGetLangSet(fonts->fonts[i], FC_LANG, 0, &langset);
367         if (res == FcResultMatch) {
368             for (int i = 1; i < LanguageCount; ++i) {
369                 const FcChar8 *lang = (const FcChar8*) languageForWritingSystem[i];
370                 if (lang) {
371                     FcLangResult langRes = FcLangSetHasLang(langset, lang);
372                     if (langRes != FcLangDifferentLang)
373                         writingSystems.setSupported(QFontDatabase::WritingSystem(i));
374                 }
375             }
376         } else {
377             // we set Other to supported for symbol fonts. It makes no
378             // sense to merge these with other ones, as they are
379             // special in a way.
380             writingSystems.setSupported(QFontDatabase::Other);
381         }
382
383         FcCharSet *cs = 0;
384         res = FcPatternGetCharSet(fonts->fonts[i], FC_CHARSET, 0, &cs);
385         if (res == FcResultMatch) {
386             // some languages are not supported by FontConfig, we rather check the
387             // charset to detect these
388             for (int i = 1; i < SampleCharCount; ++i) {
389                 if (!sampleCharForWritingSystem[i])
390                     continue;
391                 if (FcCharSetHasChar(cs, sampleCharForWritingSystem[i]))
392                     writingSystems.setSupported(QFontDatabase::WritingSystem(i));
393             }
394         }
395
396 #if FC_VERSION >= 20297
397         for (int j = 1; j < LanguageCount; ++j) {
398             if (writingSystems.supported(QFontDatabase::WritingSystem(j))
399                 && requiresOpenType(j) && openType[j]) {
400                 FcChar8 *cap;
401                 res = FcPatternGetString (fonts->fonts[i], FC_CAPABILITY, 0, &cap);
402                 if (res != FcResultMatch || !strstr((const char *)cap, openType[j]))
403                     writingSystems.setSupported(QFontDatabase::WritingSystem(j),false);
404             }
405         }
406 #endif
407
408         FontFile *fontFile = new FontFile;
409         fontFile->fileName = QLatin1String((const char *)file_value);
410         fontFile->indexValue = indexValue;
411
412         QFont::Style style = (slant_value == FC_SLANT_ITALIC)
413                          ? QFont::StyleItalic
414                          : ((slant_value == FC_SLANT_OBLIQUE)
415                             ? QFont::StyleOblique
416                             : QFont::StyleNormal);
417         QFont::Weight weight = QFont::Weight(getFCWeight(weight_value));
418
419         double pixel_size = 0;
420         if (!scalable)
421             FcPatternGetDouble (fonts->fonts[i], FC_PIXEL_SIZE, 0, &pixel_size);
422
423         int width = FC_WIDTH_NORMAL;
424         FcPatternGetInteger(fonts->fonts[i], FC_WIDTH, 0, &width);
425
426         QFont::Stretch stretch;
427         switch (width) {
428         case FC_WIDTH_ULTRACONDENSED: stretch = QFont::UltraCondensed; break;
429         case FC_WIDTH_EXTRACONDENSED: stretch = QFont::ExtraCondensed; break;
430         case FC_WIDTH_CONDENSED:      stretch = QFont::Condensed;      break;
431         case FC_WIDTH_SEMICONDENSED:  stretch = QFont::SemiCondensed;  break;
432         case FC_WIDTH_NORMAL:         stretch = QFont::Unstretched;    break;
433         case FC_WIDTH_SEMIEXPANDED:   stretch = QFont::SemiExpanded;   break;
434         case FC_WIDTH_EXPANDED:       stretch = QFont::Expanded;       break;
435         case FC_WIDTH_EXTRAEXPANDED:  stretch = QFont::ExtraExpanded;  break;
436         case FC_WIDTH_ULTRAEXPANDED:  stretch = QFont::UltraExpanded;  break;
437         default:                      stretch = QFont::Unstretched;    break;
438         }
439
440         QPlatformFontDatabase::registerFont(familyName,QLatin1String((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,writingSystems,fontFile);
441 //        qDebug() << familyName << (const char *)foundry_value << weight << style << &writingSystems << scalable << true << pixel_size;
442     }
443
444     FcFontSetDestroy (fonts);
445
446     struct FcDefaultFont {
447         const char *qtname;
448         const char *rawname;
449         bool fixed;
450     };
451     const FcDefaultFont defaults[] = {
452         { "Serif", "serif", false },
453         { "Sans Serif", "sans-serif", false },
454         { "Monospace", "monospace", true },
455         { 0, 0, false }
456     };
457     const FcDefaultFont *f = defaults;
458     // aliases only make sense for 'common', not for any of the specials
459     QSupportedWritingSystems ws;
460     ws.setSupported(QFontDatabase::Latin);
461
462
463     while (f->qtname) {
464         registerFont(f->qtname,QLatin1String(""),QFont::Normal,QFont::StyleNormal,QFont::Unstretched,true,true,0,ws,0);
465         registerFont(f->qtname,QLatin1String(""),QFont::Normal,QFont::StyleItalic,QFont::Unstretched,true,true,0,ws,0);
466         registerFont(f->qtname,QLatin1String(""),QFont::Normal,QFont::StyleOblique,QFont::Unstretched,true,true,0,ws,0);
467         ++f;
468     }
469
470     //Lighthouse has very lazy population of the font db. We want it to be initialized when
471     //QApplication is constructed, so that the population procedure can do something like this to
472     //set the default font
473 //    const FcDefaultFont *s = defaults;
474 //    QFont font("Sans Serif");
475 //    font.setPointSize(9);
476 //    QApplication::setFont(font);
477 }
478
479 QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, QUnicodeTables::Script script, void *usrPtr)
480 {
481     if (!usrPtr)
482         return 0;
483     QFontDef fontDef = f;
484
485     QFontEngineFT *engine;
486     FontFile *fontfile = static_cast<FontFile *> (usrPtr);
487     QFontEngine::FaceId fid;
488     fid.filename = fontfile->fileName.toLocal8Bit();
489     fid.index = fontfile->indexValue;
490
491     //try and get the pattern
492     FcPattern *pattern = FcPatternCreate();
493
494     bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
495     QFontEngineFT::GlyphFormat format = antialias? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono;
496
497     engine = new QFontEngineFT(fontDef);
498
499     FcValue value;
500     value.type = FcTypeString;
501         QByteArray cs = fontDef.family.toUtf8();
502     value.u.s = (const FcChar8 *)cs.data();
503     FcPatternAdd(pattern,FC_FAMILY,value,true);
504
505
506     value.u.s = (const FcChar8 *)fid.filename.data();
507     FcPatternAdd(pattern,FC_FILE,value,true);
508
509     value.type = FcTypeInteger;
510     value.u.i = fid.index;
511     FcPatternAdd(pattern,FC_INDEX,value,true);
512
513     QFontEngineFT::HintStyle default_hint_style;
514
515     if (FcConfigSubstitute(0,pattern,FcMatchPattern)) {
516
517         //hinting
518         int hint_style = 0;
519         if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch)
520             hint_style = QFontEngineFT::HintFull;
521         switch (hint_style) {
522         case FC_HINT_NONE:
523             default_hint_style = QFontEngineFT::HintNone;
524             break;
525         case FC_HINT_SLIGHT:
526             default_hint_style = QFontEngineFT::HintLight;
527             break;
528         case FC_HINT_MEDIUM:
529             default_hint_style = QFontEngineFT::HintMedium;
530             break;
531         default:
532             default_hint_style = QFontEngineFT::HintFull;
533             break;
534         }
535     }
536
537     engine->setDefaultHintStyle(default_hint_style);
538     if (!engine->init(fid,antialias,format)) {
539         delete engine;
540         engine = 0;
541         return engine;
542     }
543     if (engine->invalid()) {
544         delete engine;
545         engine = 0;
546     } else if (scriptRequiresOpenType(script)) {
547         HB_Face hbFace = engine->harfbuzzFace();
548         if (!hbFace || !hbFace->supported_scripts[script]) {
549             delete engine;
550             engine = 0;
551         }
552     }
553
554     return engine;
555 }
556
557 QStringList QFontconfigDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const
558 {
559     QStringList fallbackFamilies;
560     FcPattern *pattern = FcPatternCreate();
561     if (!pattern)
562         return fallbackFamilies;
563
564     FcValue value;
565     value.type = FcTypeString;
566     QByteArray cs = family.toUtf8();
567     value.u.s = (const FcChar8 *)cs.data();
568     FcPatternAdd(pattern,FC_FAMILY,value,true);
569
570     int slant_value = FC_SLANT_ROMAN;
571     if (style == QFont::StyleItalic)
572         slant_value = FC_SLANT_ITALIC;
573     else if (style == QFont::StyleOblique)
574         slant_value = FC_SLANT_OBLIQUE;
575     FcPatternAddInteger(pattern, FC_SLANT, slant_value);
576
577     if (script != QUnicodeTables::Common && *specialLanguages[script] != '\0') {
578         Q_ASSERT(script < QUnicodeTables::ScriptCount);
579         FcLangSet *ls = FcLangSetCreate();
580         FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]);
581         FcPatternAddLangSet(pattern, FC_LANG, ls);
582         FcLangSetDestroy(ls);
583     }
584
585     const char *stylehint = getFcFamilyForStyleHint(styleHint);
586     if (stylehint) {
587         value.u.s = (const FcChar8 *)stylehint;
588         FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
589     }
590
591     FcConfigSubstitute(0, pattern, FcMatchPattern);
592     FcDefaultSubstitute(pattern);
593
594     FcResult result = FcResultMatch;
595     FcFontSet *fontSet = FcFontSort(0,pattern,FcFalse,0,&result);
596     FcPatternDestroy(pattern);
597
598     if (fontSet)
599     {
600         for (int i = 0; i < fontSet->nfont; i++) {
601             FcChar8 *value = 0;
602             if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
603                 continue;
604             //         capitalize(value);
605             QString familyName = QString::fromUtf8((const char *)value);
606             if (!fallbackFamilies.contains(familyName,Qt::CaseInsensitive)) {
607                 fallbackFamilies << familyName;
608             }
609         }
610         FcFontSetDestroy(fontSet);
611     }
612 //    qDebug() << "fallbackFamilies for:" << family << fallbackFamilies;
613
614     return fallbackFamilies;
615 }