1 /****************************************************************************
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the plugins of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qfontconfigdatabase.h"
44 #include <QtCore/QList>
45 #include <QtGui/private/qfont_p.h>
47 #include <QtCore/QElapsedTimer>
49 #include <QtGui/private/qapplication_p.h>
50 #include <QtGui/QPlatformScreen>
52 #include <QtGui/private/qfontengine_ft_p.h>
53 #include <QtGui/private/qfontengine_p.h>
58 #include FT_TRUETYPE_TABLES_H
60 #include <fontconfig/fontconfig.h>
62 #define SimplifiedChineseCsbBit 18
63 #define TraditionalChineseCsbBit 20
64 #define JapaneseCsbBit 17
65 #define KoreanCsbBit 21
67 static inline bool requiresOpenType(int writingSystem)
69 return ((writingSystem >= QFontDatabase::Syriac && writingSystem <= QFontDatabase::Sinhala)
70 || writingSystem == QFontDatabase::Khmer || writingSystem == QFontDatabase::Nko);
72 static inline bool scriptRequiresOpenType(int script)
74 return ((script >= QUnicodeTables::Syriac && script <= QUnicodeTables::Sinhala)
75 || script == QUnicodeTables::Khmer || script == QUnicodeTables::Nko);
78 static int getFCWeight(int fc_weight)
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;
93 static const char *specialLanguages[] = {
123 enum { SpecialLanguageCount = sizeof(specialLanguages) / sizeof(const char *) };
125 static const ushort specialChars[] = {
155 enum { SpecialCharCount = sizeof(specialChars) / sizeof(ushort) };
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[] = {
185 "zh-cn", // SimplifiedChinese
186 "zh-tw", // TraditionalChinese
195 enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) };
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[] = {
225 0, // SimplifiedChinese
226 0, // TraditionalChinese
235 enum { SampleCharCount = sizeof(sampleCharForWritingSystem) / sizeof(ushort) };
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[] = {
250 "deva", // Devanagari
266 0, // SimplifiedChinese
267 0, // TraditionalChinese
277 static const char *getFcFamilyForStyleHint(const QFont::StyleHint style)
279 const char *stylehint = 0;
281 case QFont::SansSerif:
282 stylehint = "sans-serif";
287 case QFont::TypeWriter:
288 stylehint = "monospace";
296 void QFontconfigDatabase::populateFontDatabase()
307 FcChar8 *foundry_value;
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,
319 #if FC_VERSION >= 20297
324 const char **p = properties;
326 FcObjectSetAdd(os, *p);
329 fonts = FcFontList(0, pattern, os);
330 FcObjectSetDestroy(os);
331 FcPatternDestroy(pattern);
334 for (int i = 0; i < fonts->nfont; i++) {
335 if (FcPatternGetString(fonts->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
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;
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)
355 if (FcPatternGetInteger (fonts->fonts[i], FC_INDEX, 0, &indexValue) != FcResultMatch)
357 if (FcPatternGetBool(fonts->fonts[i], FC_SCALABLE, 0, &scalable) != FcResultMatch)
359 if (FcPatternGetString(fonts->fonts[i], FC_FOUNDRY, 0, &foundry_value) != FcResultMatch)
361 if(FcPatternGetBool(fonts->fonts[i],FC_ANTIALIAS,0,&antialias) != FcResultMatch)
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];
371 FcLangResult langRes = FcLangSetHasLang(langset, lang);
372 if (langRes != FcLangDifferentLang)
373 writingSystems.setSupported(QFontDatabase::WritingSystem(i));
377 // we set Other to supported for symbol fonts. It makes no
378 // sense to merge these with other ones, as they are
380 writingSystems.setSupported(QFontDatabase::Other);
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])
391 if (FcCharSetHasChar(cs, sampleCharForWritingSystem[i]))
392 writingSystems.setSupported(QFontDatabase::WritingSystem(i));
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]) {
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);
408 FontFile *fontFile = new FontFile;
409 fontFile->fileName = QLatin1String((const char *)file_value);
410 fontFile->indexValue = indexValue;
412 QFont::Style style = (slant_value == FC_SLANT_ITALIC)
414 : ((slant_value == FC_SLANT_OBLIQUE)
415 ? QFont::StyleOblique
416 : QFont::StyleNormal);
417 QFont::Weight weight = QFont::Weight(getFCWeight(weight_value));
419 double pixel_size = 0;
421 FcPatternGetDouble (fonts->fonts[i], FC_PIXEL_SIZE, 0, &pixel_size);
423 int width = FC_WIDTH_NORMAL;
424 FcPatternGetInteger(fonts->fonts[i], FC_WIDTH, 0, &width);
426 QFont::Stretch stretch;
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;
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;
444 FcFontSetDestroy (fonts);
446 struct FcDefaultFont {
451 const FcDefaultFont defaults[] = {
452 { "Serif", "serif", false },
453 { "Sans Serif", "sans-serif", false },
454 { "Monospace", "monospace", true },
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);
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);
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);
479 QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, QUnicodeTables::Script script, void *usrPtr)
483 QFontDef fontDef = f;
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;
491 //try and get the pattern
492 FcPattern *pattern = FcPatternCreate();
494 bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
495 QFontEngineFT::GlyphFormat format = antialias? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono;
497 engine = new QFontEngineFT(fontDef);
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);
506 value.u.s = (const FcChar8 *)fid.filename.data();
507 FcPatternAdd(pattern,FC_FILE,value,true);
509 value.type = FcTypeInteger;
510 value.u.i = fid.index;
511 FcPatternAdd(pattern,FC_INDEX,value,true);
513 QFontEngineFT::HintStyle default_hint_style;
515 if (FcConfigSubstitute(0,pattern,FcMatchPattern)) {
519 if (FcPatternGetInteger (pattern, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch)
520 hint_style = QFontEngineFT::HintFull;
521 switch (hint_style) {
523 default_hint_style = QFontEngineFT::HintNone;
526 default_hint_style = QFontEngineFT::HintLight;
529 default_hint_style = QFontEngineFT::HintMedium;
532 default_hint_style = QFontEngineFT::HintFull;
537 engine->setDefaultHintStyle(default_hint_style);
538 if (!engine->init(fid,antialias,format)) {
543 if (engine->invalid()) {
546 } else if (scriptRequiresOpenType(script)) {
547 HB_Face hbFace = engine->harfbuzzFace();
548 if (!hbFace || !hbFace->supported_scripts[script]) {
557 QStringList QFontconfigDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const
559 QStringList fallbackFamilies;
560 FcPattern *pattern = FcPatternCreate();
562 return fallbackFamilies;
565 value.type = FcTypeString;
566 QByteArray cs = family.toUtf8();
567 value.u.s = (const FcChar8 *)cs.data();
568 FcPatternAdd(pattern,FC_FAMILY,value,true);
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);
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);
585 const char *stylehint = getFcFamilyForStyleHint(styleHint);
587 value.u.s = (const FcChar8 *)stylehint;
588 FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
591 FcConfigSubstitute(0, pattern, FcMatchPattern);
592 FcDefaultSubstitute(pattern);
594 FcResult result = FcResultMatch;
595 FcFontSet *fontSet = FcFontSort(0,pattern,FcFalse,0,&result);
596 FcPatternDestroy(pattern);
600 for (int i = 0; i < fontSet->nfont; i++) {
602 if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
604 // capitalize(value);
605 QString familyName = QString::fromUtf8((const char *)value);
606 if (!fallbackFamilies.contains(familyName,Qt::CaseInsensitive)) {
607 fallbackFamilies << familyName;
610 FcFontSetDestroy(fontSet);
612 // qDebug() << "fallbackFamilies for:" << family << fallbackFamilies;
614 return fallbackFamilies;