1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the plugins of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qfontconfigdatabase_p.h"
43 #include "qfontenginemultifontconfig_p.h"
45 #include <QtCore/QList>
46 #include <QtGui/private/qfont_p.h>
48 #include <QtCore/QElapsedTimer>
50 #include <QtGui/QPlatformScreen>
52 #include <QtGui/private/qfontengine_ft_p.h>
53 #include <QtGui/private/qfontengine_p.h>
54 #include <QtGui/private/qfontengine_qpa_p.h>
57 #include FT_TRUETYPE_TABLES_H
59 #include <fontconfig/fontconfig.h>
60 #include FT_FREETYPE_H
62 #if FC_VERSION >= 20402
63 #include <fontconfig/fcfreetype.h>
66 #define SimplifiedChineseCsbBit 18
67 #define TraditionalChineseCsbBit 20
68 #define JapaneseCsbBit 17
69 #define KoreanCsbBit 21
73 static inline bool requiresOpenType(int writingSystem)
75 return ((writingSystem >= QFontDatabase::Syriac && writingSystem <= QFontDatabase::Sinhala)
76 || writingSystem == QFontDatabase::Khmer || writingSystem == QFontDatabase::Nko);
78 static inline bool scriptRequiresOpenType(int script)
80 return ((script >= QUnicodeTables::Syriac && script <= QUnicodeTables::Sinhala)
81 || script == QUnicodeTables::Khmer || script == QUnicodeTables::Nko);
84 static int getFCWeight(int fc_weight)
86 int qtweight = QFont::Black;
87 if (fc_weight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_MEDIUM) / 2)
88 qtweight = QFont::Light;
89 else if (fc_weight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2)
90 qtweight = QFont::Normal;
91 else if (fc_weight <= (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2)
92 qtweight = QFont::DemiBold;
93 else if (fc_weight <= (FC_WEIGHT_BOLD + FC_WEIGHT_BLACK) / 2)
94 qtweight = QFont::Bold;
99 static const char *specialLanguages[] = {
129 enum { SpecialLanguageCount = sizeof(specialLanguages) / sizeof(const char *) };
131 static const ushort specialChars[] = {
161 enum { SpecialCharCount = sizeof(specialChars) / sizeof(ushort) };
163 // this could become a list of all languages used for each writing
164 // system, instead of using the single most common language.
165 static const char *languageForWritingSystem[] = {
191 "zh-cn", // SimplifiedChinese
192 "zh-tw", // TraditionalChinese
201 enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) };
203 // Unfortunately FontConfig doesn't know about some languages. We have to test these through the
204 // charset. The lists below contain the systems where we need to do this.
205 static const ushort sampleCharForWritingSystem[] = {
231 0, // SimplifiedChinese
232 0, // TraditionalChinese
241 enum { SampleCharCount = sizeof(sampleCharForWritingSystem) / sizeof(ushort) };
243 // Newer FontConfig let's us sort out fonts that contain certain glyphs, but no
244 // open type tables for is directly. Do this so we don't pick some strange
245 // pseudo unicode font
246 static const char *openType[] = {
256 "deva", // Devanagari
272 0, // SimplifiedChinese
273 0, // TraditionalChinese
283 static const char *getFcFamilyForStyleHint(const QFont::StyleHint style)
285 const char *stylehint = 0;
287 case QFont::SansSerif:
288 stylehint = "sans-serif";
293 case QFont::TypeWriter:
294 stylehint = "monospace";
302 void QFontconfigDatabase::populateFontDatabase()
313 FcChar8 *foundry_value;
318 FcObjectSet *os = FcObjectSetCreate();
319 FcPattern *pattern = FcPatternCreate();
320 const char *properties [] = {
321 FC_FAMILY, FC_WEIGHT, FC_SLANT,
322 FC_SPACING, FC_FILE, FC_INDEX,
323 FC_LANG, FC_CHARSET, FC_FOUNDRY, FC_SCALABLE, FC_PIXEL_SIZE, FC_WEIGHT,
325 #if FC_VERSION >= 20297
330 const char **p = properties;
332 FcObjectSetAdd(os, *p);
335 fonts = FcFontList(0, pattern, os);
336 FcObjectSetDestroy(os);
337 FcPatternDestroy(pattern);
340 for (int i = 0; i < fonts->nfont; i++) {
341 if (FcPatternGetString(fonts->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
343 // capitalize(value);
344 familyName = QString::fromUtf8((const char *)value);
345 slant_value = FC_SLANT_ROMAN;
346 weight_value = FC_WEIGHT_MEDIUM;
347 spacing_value = FC_PROPORTIONAL;
353 if (FcPatternGetInteger (fonts->fonts[i], FC_SLANT, 0, &slant_value) != FcResultMatch)
354 slant_value = FC_SLANT_ROMAN;
355 if (FcPatternGetInteger (fonts->fonts[i], FC_WEIGHT, 0, &weight_value) != FcResultMatch)
356 weight_value = FC_WEIGHT_MEDIUM;
357 if (FcPatternGetInteger (fonts->fonts[i], FC_SPACING, 0, &spacing_value) != FcResultMatch)
358 spacing_value = FC_PROPORTIONAL;
359 if (FcPatternGetString (fonts->fonts[i], FC_FILE, 0, &file_value) != FcResultMatch)
361 if (FcPatternGetInteger (fonts->fonts[i], FC_INDEX, 0, &indexValue) != FcResultMatch)
363 if (FcPatternGetBool(fonts->fonts[i], FC_SCALABLE, 0, &scalable) != FcResultMatch)
365 if (FcPatternGetString(fonts->fonts[i], FC_FOUNDRY, 0, &foundry_value) != FcResultMatch)
367 if(FcPatternGetBool(fonts->fonts[i],FC_ANTIALIAS,0,&antialias) != FcResultMatch)
370 QSupportedWritingSystems writingSystems;
371 FcLangSet *langset = 0;
372 FcResult res = FcPatternGetLangSet(fonts->fonts[i], FC_LANG, 0, &langset);
373 if (res == FcResultMatch) {
374 for (int i = 1; i < LanguageCount; ++i) {
375 const FcChar8 *lang = (const FcChar8*) languageForWritingSystem[i];
377 FcLangResult langRes = FcLangSetHasLang(langset, lang);
378 if (langRes != FcLangDifferentLang)
379 writingSystems.setSupported(QFontDatabase::WritingSystem(i));
383 // we set Other to supported for symbol fonts. It makes no
384 // sense to merge these with other ones, as they are
386 writingSystems.setSupported(QFontDatabase::Other);
390 res = FcPatternGetCharSet(fonts->fonts[i], FC_CHARSET, 0, &cs);
391 if (res == FcResultMatch) {
392 // some languages are not supported by FontConfig, we rather check the
393 // charset to detect these
394 for (int i = 1; i < SampleCharCount; ++i) {
395 if (!sampleCharForWritingSystem[i])
397 if (FcCharSetHasChar(cs, sampleCharForWritingSystem[i]))
398 writingSystems.setSupported(QFontDatabase::WritingSystem(i));
402 #if FC_VERSION >= 20297
403 for (int j = 1; j < LanguageCount; ++j) {
404 if (writingSystems.supported(QFontDatabase::WritingSystem(j))
405 && requiresOpenType(j) && openType[j]) {
407 res = FcPatternGetString (fonts->fonts[i], FC_CAPABILITY, 0, &cap);
408 if (res != FcResultMatch || !strstr((const char *)cap, openType[j]))
409 writingSystems.setSupported(QFontDatabase::WritingSystem(j),false);
414 FontFile *fontFile = new FontFile;
415 fontFile->fileName = QLatin1String((const char *)file_value);
416 fontFile->indexValue = indexValue;
418 QFont::Style style = (slant_value == FC_SLANT_ITALIC)
420 : ((slant_value == FC_SLANT_OBLIQUE)
421 ? QFont::StyleOblique
422 : QFont::StyleNormal);
423 QFont::Weight weight = QFont::Weight(getFCWeight(weight_value));
425 double pixel_size = 0;
428 FcPatternGetInteger (fonts->fonts[i], FC_WIDTH, 0, &width);
429 FcPatternGetDouble (fonts->fonts[i], FC_PIXEL_SIZE, 0, &pixel_size);
432 bool fixedPitch = spacing_value >= FC_MONO;
433 QFont::Stretch stretch = QFont::Unstretched;
434 QPlatformFontDatabase::registerFont(familyName,QLatin1String((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,writingSystems,fontFile);
435 // qDebug() << familyName << (const char *)foundry_value << weight << style << &writingSystems << scalable << true << pixel_size;
438 FcFontSetDestroy (fonts);
440 struct FcDefaultFont {
445 const FcDefaultFont defaults[] = {
446 { "Serif", "serif", false },
447 { "Sans Serif", "sans-serif", false },
448 { "Monospace", "monospace", true },
451 const FcDefaultFont *f = defaults;
452 // aliases only make sense for 'common', not for any of the specials
453 QSupportedWritingSystems ws;
454 ws.setSupported(QFontDatabase::Latin);
457 QString familyQtName = QString::fromLatin1(f->qtname);
459 registerFont(familyQtName,QString(),QFont::Normal,QFont::StyleNormal,QFont::Unstretched,true,true,0,f->fixed,ws,0);
460 registerFont(familyQtName,QString(),QFont::Normal,QFont::StyleItalic,QFont::Unstretched,true,true,0,f->fixed,ws,0);
461 registerFont(familyQtName,QString(),QFont::Normal,QFont::StyleOblique,QFont::Unstretched,true,true,0,f->fixed,ws,0);
465 //Lighthouse has very lazy population of the font db. We want it to be initialized when
466 //QApplication is constructed, so that the population procedure can do something like this to
467 //set the default font
468 // const FcDefaultFont *s = defaults;
469 // QFont font("Sans Serif");
470 // font.setPointSize(9);
471 // QApplication::setFont(font);
474 QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine,
475 QUnicodeTables::Script script)
477 return new QFontEngineMultiFontConfig(fontEngine, script);
480 QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, QUnicodeTables::Script script, void *usrPtr)
484 QFontDef fontDef = f;
486 QFontEngineFT *engine;
487 FontFile *fontfile = static_cast<FontFile *> (usrPtr);
488 QFontEngine::FaceId fid;
489 fid.filename = fontfile->fileName.toLocal8Bit();
490 fid.index = fontfile->indexValue;
492 bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
493 engine = new QFontEngineFT(fontDef);
495 QFontEngineFT::GlyphFormat format;
496 // try and get the pattern
497 FcPattern *pattern = FcPatternCreate();
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);
505 value.u.s = (const FcChar8 *)fid.filename.data();
506 FcPatternAdd(pattern,FC_FILE,value,true);
508 value.type = FcTypeInteger;
509 value.u.i = fid.index;
510 FcPatternAdd(pattern,FC_INDEX,value,true);
513 FcPattern *match = FcFontMatch(0, pattern, &result);
516 QFontEngineFT::HintStyle default_hint_style;
517 if (f.hintingPreference != QFont::PreferDefaultHinting) {
518 switch (f.hintingPreference) {
519 case QFont::PreferNoHinting:
520 default_hint_style = QFontEngineFT::HintNone;
522 case QFont::PreferVerticalHinting:
523 default_hint_style = QFontEngineFT::HintLight;
525 case QFont::PreferFullHinting:
527 default_hint_style = QFontEngineFT::HintFull;
532 if (FcPatternGetInteger (match, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch)
533 hint_style = QFontEngineFT::HintFull;
534 switch (hint_style) {
536 default_hint_style = QFontEngineFT::HintNone;
539 default_hint_style = QFontEngineFT::HintLight;
542 default_hint_style = QFontEngineFT::HintMedium;
545 default_hint_style = QFontEngineFT::HintFull;
549 engine->setDefaultHintStyle(default_hint_style);
552 QFontEngineFT::SubpixelAntialiasingType subpixelType = QFontEngineFT::Subpixel_None;
553 int subpixel = FC_RGBA_NONE;
555 FcPatternGetInteger(match, FC_RGBA, 0, &subpixel);
556 if (subpixel == FC_RGBA_UNKNOWN)
557 subpixel = FC_RGBA_NONE;
560 case FC_RGBA_NONE: subpixelType = QFontEngineFT::Subpixel_None; break;
561 case FC_RGBA_RGB: subpixelType = QFontEngineFT::Subpixel_RGB; break;
562 case FC_RGBA_BGR: subpixelType = QFontEngineFT::Subpixel_BGR; break;
563 case FC_RGBA_VRGB: subpixelType = QFontEngineFT::Subpixel_VRGB; break;
564 case FC_RGBA_VBGR: subpixelType = QFontEngineFT::Subpixel_VBGR; break;
568 format = subpixelType == QFontEngineFT::Subpixel_None
569 ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_A32;
570 engine->subpixelType = subpixelType;
572 format = QFontEngineFT::Format_Mono;
574 FcPatternGetCharSet(match, FC_CHARSET, 0, &charset);
575 FcPatternDestroy(match);
577 format = antialias ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono;
579 FcPatternDestroy(pattern);
581 if (!engine->init(fid,antialias,format)) {
586 if (engine->invalid()) {
589 } else if (scriptRequiresOpenType(script)) {
590 HB_Face hbFace = engine->initializedHarfbuzzFace();
591 if (!hbFace || !hbFace->supported_scripts[script]) {
597 if (engine && engine->freetype && !engine->freetype->charset)
598 engine->freetype->charset = FcCharSetCopy(charset);
603 QStringList QFontconfigDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const
605 QStringList fallbackFamilies;
606 FcPattern *pattern = FcPatternCreate();
608 return fallbackFamilies;
611 value.type = FcTypeString;
612 QByteArray cs = family.toUtf8();
613 value.u.s = (const FcChar8 *)cs.data();
614 FcPatternAdd(pattern,FC_FAMILY,value,true);
616 int slant_value = FC_SLANT_ROMAN;
617 if (style == QFont::StyleItalic)
618 slant_value = FC_SLANT_ITALIC;
619 else if (style == QFont::StyleOblique)
620 slant_value = FC_SLANT_OBLIQUE;
621 FcPatternAddInteger(pattern, FC_SLANT, slant_value);
623 if (script != QUnicodeTables::Common && *specialLanguages[script] != '\0') {
624 Q_ASSERT(script < QUnicodeTables::ScriptCount);
625 FcLangSet *ls = FcLangSetCreate();
626 FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]);
627 FcPatternAddLangSet(pattern, FC_LANG, ls);
628 FcLangSetDestroy(ls);
631 const char *stylehint = getFcFamilyForStyleHint(styleHint);
633 value.u.s = (const FcChar8 *)stylehint;
634 FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
637 FcConfigSubstitute(0, pattern, FcMatchPattern);
638 FcDefaultSubstitute(pattern);
640 FcResult result = FcResultMatch;
641 FcFontSet *fontSet = FcFontSort(0,pattern,FcFalse,0,&result);
642 FcPatternDestroy(pattern);
645 if (result == FcResultMatch) {
646 for (int i = 0; i < fontSet->nfont; i++) {
648 if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
650 // capitalize(value);
651 QString familyName = QString::fromUtf8((const char *)value);
652 if (!fallbackFamilies.contains(familyName,Qt::CaseInsensitive) &&
653 familyName.compare(family, Qt::CaseInsensitive)) {
654 fallbackFamilies << familyName;
658 FcFontSetDestroy(fontSet);
660 // qDebug() << "fallbackFamilies for:" << family << style << styleHint << script << fallbackFamilies;
662 return fallbackFamilies;
665 static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count)
667 #if FC_VERSION < 20402
669 return FcFreeTypeQuery(file, id, blanks, count);
672 return FcFreeTypeQuery(file, id, blanks, count);
674 extern FT_Library qt_getFreetype();
675 FT_Library lib = qt_getFreetype();
677 FcPattern *pattern = 0;
680 if (!FT_New_Memory_Face(lib, (const FT_Byte *)data.constData(), data.size(), id, &face)) {
681 *count = face->num_faces;
683 pattern = FcFreeTypeQueryFace(face, file, id, blanks);
692 QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName)
694 QStringList families;
695 FcFontSet *set = FcConfigGetFonts(0, FcSetApplication);
697 FcConfigAppFontAddFile(0, (const FcChar8 *)":/non-existent");
698 set = FcConfigGetFonts(0, FcSetApplication); // try again
704 FcBlanks *blanks = FcConfigGetBlanks(0);
707 FcPattern *pattern = 0;
709 pattern = queryFont((const FcChar8 *)QFile::encodeName(fileName).constData(),
710 fontData, id, blanks, &count);
714 FcPatternDel(pattern, FC_FILE);
715 QByteArray cs = fileName.toUtf8();
716 FcPatternAddString(pattern, FC_FILE, (const FcChar8 *) cs.constData());
719 if (FcPatternGetString(pattern, FC_FAMILY, 0, &fam) == FcResultMatch) {
720 QString family = QString::fromUtf8(reinterpret_cast<const char *>(fam));
724 if (!FcFontSetAdd(set, pattern))
728 } while (pattern && id < count);
733 QString QFontconfigDatabase::resolveFontFamilyAlias(const QString &family) const
735 FcPattern *pattern = FcPatternCreate();
739 if (!family.isEmpty()) {
740 QByteArray cs = family.toUtf8();
741 FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) cs.constData());
743 FcConfigSubstitute(0, pattern, FcMatchPattern);
744 FcDefaultSubstitute(pattern);
746 FcChar8 *familyAfterSubstitution = 0;
747 FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution);
748 QString resolved = QString::fromUtf8((const char *) familyAfterSubstitution);
749 FcPatternDestroy(pattern);
754 QFont QFontconfigDatabase::defaultFont() const
756 return QFont(resolveFontFamilyAlias(QString()));