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);
458 registerFont(familyQtName,QString(),QFont::Normal,QFont::StyleNormal,QFont::Unstretched,true,true,0,f->fixed,ws,0);
459 registerFont(familyQtName,QString(),QFont::Normal,QFont::StyleItalic,QFont::Unstretched,true,true,0,f->fixed,ws,0);
460 registerFont(familyQtName,QString(),QFont::Normal,QFont::StyleOblique,QFont::Unstretched,true,true,0,f->fixed,ws,0);
464 //Lighthouse has very lazy population of the font db. We want it to be initialized when
465 //QApplication is constructed, so that the population procedure can do something like this to
466 //set the default font
467 // const FcDefaultFont *s = defaults;
468 // QFont font("Sans Serif");
469 // font.setPointSize(9);
470 // QApplication::setFont(font);
473 QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine,
474 QUnicodeTables::Script script)
476 return new QFontEngineMultiFontConfig(fontEngine, script);
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 bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
492 engine = new QFontEngineFT(fontDef);
494 QFontEngineFT::GlyphFormat format;
495 // try and get the pattern
496 FcPattern *pattern = FcPatternCreate();
499 value.type = FcTypeString;
500 QByteArray cs = fontDef.family.toUtf8();
501 value.u.s = (const FcChar8 *)cs.data();
502 FcPatternAdd(pattern,FC_FAMILY,value,true);
504 value.u.s = (const FcChar8 *)fid.filename.data();
505 FcPatternAdd(pattern,FC_FILE,value,true);
507 value.type = FcTypeInteger;
508 value.u.i = fid.index;
509 FcPatternAdd(pattern,FC_INDEX,value,true);
512 FcPattern *match = FcFontMatch(0, pattern, &result);
515 QFontEngineFT::HintStyle default_hint_style;
516 if (f.hintingPreference != QFont::PreferDefaultHinting) {
517 switch (f.hintingPreference) {
518 case QFont::PreferNoHinting:
519 default_hint_style = QFontEngineFT::HintNone;
521 case QFont::PreferVerticalHinting:
522 default_hint_style = QFontEngineFT::HintLight;
524 case QFont::PreferFullHinting:
526 default_hint_style = QFontEngineFT::HintFull;
531 if (FcPatternGetInteger (match, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch)
532 hint_style = QFontEngineFT::HintFull;
533 switch (hint_style) {
535 default_hint_style = QFontEngineFT::HintNone;
538 default_hint_style = QFontEngineFT::HintLight;
541 default_hint_style = QFontEngineFT::HintMedium;
544 default_hint_style = QFontEngineFT::HintFull;
548 engine->setDefaultHintStyle(default_hint_style);
551 QFontEngineFT::SubpixelAntialiasingType subpixelType = QFontEngineFT::Subpixel_None;
552 int subpixel = FC_RGBA_NONE;
554 FcPatternGetInteger(match, FC_RGBA, 0, &subpixel);
555 if (subpixel == FC_RGBA_UNKNOWN)
556 subpixel = FC_RGBA_NONE;
559 case FC_RGBA_NONE: subpixelType = QFontEngineFT::Subpixel_None; break;
560 case FC_RGBA_RGB: subpixelType = QFontEngineFT::Subpixel_RGB; break;
561 case FC_RGBA_BGR: subpixelType = QFontEngineFT::Subpixel_BGR; break;
562 case FC_RGBA_VRGB: subpixelType = QFontEngineFT::Subpixel_VRGB; break;
563 case FC_RGBA_VBGR: subpixelType = QFontEngineFT::Subpixel_VBGR; break;
567 format = subpixelType == QFontEngineFT::Subpixel_None
568 ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_A32;
569 engine->subpixelType = subpixelType;
571 format = QFontEngineFT::Format_Mono;
573 FcPatternGetCharSet(match, FC_CHARSET, 0, &charset);
574 FcPatternDestroy(match);
576 format = antialias ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono;
578 FcPatternDestroy(pattern);
580 if (!engine->init(fid,antialias,format)) {
585 if (engine->invalid()) {
588 } else if (scriptRequiresOpenType(script)) {
589 HB_Face hbFace = engine->initializedHarfbuzzFace();
590 if (!hbFace || !hbFace->supported_scripts[script]) {
596 if (engine && engine->freetype && !engine->freetype->charset)
597 engine->freetype->charset = FcCharSetCopy(charset);
602 QStringList QFontconfigDatabase::fallbacksForFamily(const QString family, const QFont::Style &style, const QFont::StyleHint &styleHint, const QUnicodeTables::Script &script) const
604 QStringList fallbackFamilies;
605 FcPattern *pattern = FcPatternCreate();
607 return fallbackFamilies;
610 value.type = FcTypeString;
611 QByteArray cs = family.toUtf8();
612 value.u.s = (const FcChar8 *)cs.data();
613 FcPatternAdd(pattern,FC_FAMILY,value,true);
615 int slant_value = FC_SLANT_ROMAN;
616 if (style == QFont::StyleItalic)
617 slant_value = FC_SLANT_ITALIC;
618 else if (style == QFont::StyleOblique)
619 slant_value = FC_SLANT_OBLIQUE;
620 FcPatternAddInteger(pattern, FC_SLANT, slant_value);
622 if (script != QUnicodeTables::Common && *specialLanguages[script] != '\0') {
623 Q_ASSERT(script < QUnicodeTables::ScriptCount);
624 FcLangSet *ls = FcLangSetCreate();
625 FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]);
626 FcPatternAddLangSet(pattern, FC_LANG, ls);
627 FcLangSetDestroy(ls);
630 const char *stylehint = getFcFamilyForStyleHint(styleHint);
632 value.u.s = (const FcChar8 *)stylehint;
633 FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
636 FcConfigSubstitute(0, pattern, FcMatchPattern);
637 FcDefaultSubstitute(pattern);
639 FcResult result = FcResultMatch;
640 FcFontSet *fontSet = FcFontSort(0,pattern,FcFalse,0,&result);
641 FcPatternDestroy(pattern);
644 for (int i = 0; i < fontSet->nfont; i++) {
646 if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
648 // capitalize(value);
649 QString familyName = QString::fromUtf8((const char *)value);
650 if (!fallbackFamilies.contains(familyName,Qt::CaseInsensitive) &&
651 familyName.compare(family, Qt::CaseInsensitive)) {
652 fallbackFamilies << familyName;
655 FcFontSetDestroy(fontSet);
657 // qDebug() << "fallbackFamilies for:" << family << style << styleHint << script << fallbackFamilies;
659 return fallbackFamilies;
662 static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count)
664 #if FC_VERSION < 20402
666 return FcFreeTypeQuery(file, id, blanks, count);
669 return FcFreeTypeQuery(file, id, blanks, count);
671 FT_Library lib = qt_getFreetype();
673 FcPattern *pattern = 0;
676 if (!FT_New_Memory_Face(lib, (const FT_Byte *)data.constData(), data.size(), id, &face)) {
677 *count = face->num_faces;
679 pattern = FcFreeTypeQueryFace(face, file, id, blanks);
688 QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName)
690 QStringList families;
691 FcFontSet *set = FcConfigGetFonts(0, FcSetApplication);
693 FcConfigAppFontAddFile(0, (const FcChar8 *)":/non-existent");
694 set = FcConfigGetFonts(0, FcSetApplication); // try again
700 FcBlanks *blanks = FcConfigGetBlanks(0);
703 FcPattern *pattern = 0;
705 pattern = queryFont((const FcChar8 *)QFile::encodeName(fileName).constData(),
706 fontData, id, blanks, &count);
710 FcPatternDel(pattern, FC_FILE);
711 QByteArray cs = fileName.toUtf8();
712 FcPatternAddString(pattern, FC_FILE, (const FcChar8 *) cs.constData());
715 if (FcPatternGetString(pattern, FC_FAMILY, 0, &fam) == FcResultMatch) {
716 QString family = QString::fromUtf8(reinterpret_cast<const char *>(fam));
720 if (!FcFontSetAdd(set, pattern))
724 } while (pattern && id < count);
729 QString QFontconfigDatabase::resolveFontFamilyAlias(const QString &family) const
731 FcPattern *pattern = FcPatternCreate();
735 if (!family.isEmpty()) {
736 QByteArray cs = family.toUtf8();
737 FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) cs.constData());
739 FcConfigSubstitute(0, pattern, FcMatchPattern);
740 FcDefaultSubstitute(pattern);
742 FcChar8 *familyAfterSubstitution = 0;
743 FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution);
744 QString resolved = QString::fromUtf8((const char *) familyAfterSubstitution);
745 FcPatternDestroy(pattern);
750 QFont QFontconfigDatabase::defaultFont() const
752 return QFont(resolveFontFamilyAlias(QString()));