qt6-bb10/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp

868 lines
28 KiB
C++

/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qfontconfigdatabase_p.h"
#include "qfontenginemultifontconfig_p.h"
#include <QtCore/QList>
#include <QtGui/private/qfont_p.h>
#include <QtCore/QElapsedTimer>
#include <qpa/qplatformscreen.h>
#include <QtGui/private/qfontengine_ft_p.h>
#include <QtGui/private/qfontengine_p.h>
#include <QtGui/private/qfontengine_qpa_p.h>
#include <ft2build.h>
#include FT_TRUETYPE_TABLES_H
#include <fontconfig/fontconfig.h>
#include FT_FREETYPE_H
#if FC_VERSION >= 20402
#include <fontconfig/fcfreetype.h>
#endif
#define SimplifiedChineseCsbBit 18
#define TraditionalChineseCsbBit 20
#define JapaneseCsbBit 17
#define KoreanCsbBit 21
QT_BEGIN_NAMESPACE
static inline bool requiresOpenType(int writingSystem)
{
return ((writingSystem >= QFontDatabase::Syriac && writingSystem <= QFontDatabase::Sinhala)
|| writingSystem == QFontDatabase::Khmer || writingSystem == QFontDatabase::Nko);
}
static inline bool scriptRequiresOpenType(int script)
{
return ((script >= QChar::Script_Syriac && script <= QChar::Script_Sinhala)
|| script == QChar::Script_Khmer || script == QChar::Script_Nko);
}
static int getFCWeight(int fc_weight)
{
int qtweight = QFont::Black;
if (fc_weight <= (FC_WEIGHT_LIGHT + FC_WEIGHT_REGULAR) / 2)
qtweight = QFont::Light;
else if (fc_weight <= (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2)
qtweight = QFont::Normal;
else if (fc_weight <= (FC_WEIGHT_MEDIUM + FC_WEIGHT_BOLD) / 2)
qtweight = QFont::DemiBold;
else if (fc_weight <= (FC_WEIGHT_BOLD + FC_WEIGHT_BLACK) / 2)
qtweight = QFont::Bold;
return qtweight;
}
static const char *specialLanguages[] = {
"", // Unknown
"", // Inherited
"", // Common
"en", // Latin
"el", // Greek
"ru", // Cyrillic
"hy", // Armenian
"he", // Hebrew
"ar", // Arabic
"syr", // Syriac
"dv", // Thaana
"hi", // Devanagari
"bn", // Bengali
"pa", // Gurmukhi
"gu", // Gujarati
"or", // Oriya
"ta", // Tamil
"te", // Telugu
"kn", // Kannada
"ml", // Malayalam
"si", // Sinhala
"th", // Thai
"lo", // Lao
"bo", // Tibetan
"my", // Myanmar
"ka", // Georgian
"ko", // Hangul
"am", // Ethiopic
"chr", // Cherokee
"cr", // CanadianAboriginal
"sga", // Ogham
"non", // Runic
"km", // Khmer
"mn", // Mongolian
"ja", // Hiragana
"ja", // Katakana
"zh-TW", // Bopomofo
"", // Han
"ii", // Yi
"ett", // OldItalic
"got", // Gothic
"en", // Deseret
"fil", // Tagalog
"hnn", // Hanunoo
"bku", // Buhid
"tbw", // Tagbanwa
"cop", // Coptic
"lif", // Limbu
"tdd", // TaiLe
"grc", // LinearB
"uga", // Ugaritic
"en", // Shavian
"so", // Osmanya
"grc", // Cypriot
"", // Braille
"bug", // Buginese
"khb", // NewTaiLue
"cu", // Glagolitic
"shi", // Tifinagh
"syl", // SylotiNagri
"peo", // OldPersian
"pra", // Kharoshthi
"ban", // Balinese
"akk", // Cuneiform
"phn", // Phoenician
"lzh", // PhagsPa
"man", // Nko
"su", // Sundanese
"lep", // Lepcha
"sat", // OlChiki
"vai", // Vai
"saz", // Saurashtra
"eky", // KayahLi
"rej", // Rejang
"xlc", // Lycian
"xcr", // Carian
"xld", // Lydian
"cjm", // Cham
"nod", // TaiTham
"blt", // TaiViet
"ae", // Avestan
"egy", // EgyptianHieroglyphs
"smp", // Samaritan
"lis", // Lisu
"bax", // Bamum
"jv", // Javanese
"mni", // MeeteiMayek
"arc", // ImperialAramaic
"xsa", // OldSouthArabian
"xpr", // InscriptionalParthian
"pal", // InscriptionalPahlavi
"otk", // OldTurkic
"bh", // Kaithi
"bbc", // Batak
"pra", // Brahmi
"myz", // Mandaic
"ccp", // Chakma
"xmr", // MeroiticCursive
"xmr", // MeroiticHieroglyphs
"hmd", // Miao
"sa", // Sharada
"srb", // SoraSompeng
"doi" // Takri
};
enum { SpecialLanguageCount = sizeof(specialLanguages) / sizeof(const char *) };
// this could become a list of all languages used for each writing
// system, instead of using the single most common language.
static const char *languageForWritingSystem[] = {
0, // Any
"en", // Latin
"el", // Greek
"ru", // Cyrillic
"hy", // Armenian
"he", // Hebrew
"ar", // Arabic
"syr", // Syriac
"div", // Thaana
"hi", // Devanagari
"bn", // Bengali
"pa", // Gurmukhi
"gu", // Gujarati
"or", // Oriya
"ta", // Tamil
"te", // Telugu
"kn", // Kannada
"ml", // Malayalam
"si", // Sinhala
"th", // Thai
"lo", // Lao
"bo", // Tibetan
"my", // Myanmar
"ka", // Georgian
"km", // Khmer
"zh-cn", // SimplifiedChinese
"zh-tw", // TraditionalChinese
"ja", // Japanese
"ko", // Korean
"vi", // Vietnamese
0, // Symbol
"sga", // Ogham
"non", // Runic
"man" // N'Ko
};
enum { LanguageCount = sizeof(languageForWritingSystem) / sizeof(const char *) };
// Unfortunately FontConfig doesn't know about some languages. We have to test these through the
// charset. The lists below contain the systems where we need to do this.
static const ushort sampleCharForWritingSystem[] = {
0, // Any
0, // Latin
0, // Greek
0, // Cyrillic
0, // Armenian
0, // Hebrew
0, // Arabic
0, // Syriac
0, // Thaana
0, // Devanagari
0, // Bengali
0, // Gurmukhi
0, // Gujarati
0, // Oriya
0, // Tamil
0xc15, // Telugu
0xc95, // Kannada
0xd15, // Malayalam
0xd9a, // Sinhala
0, // Thai
0, // Lao
0, // Tibetan
0x1000, // Myanmar
0, // Georgian
0, // Khmer
0, // SimplifiedChinese
0, // TraditionalChinese
0, // Japanese
0, // Korean
0, // Vietnamese
0, // Symbol
0x1681, // Ogham
0x16a0, // Runic
0x7ca // N'Ko
};
enum { SampleCharCount = sizeof(sampleCharForWritingSystem) / sizeof(ushort) };
// Newer FontConfig let's us sort out fonts that contain certain glyphs, but no
// open type tables for is directly. Do this so we don't pick some strange
// pseudo unicode font
static const char *openType[] = {
0, // Any
0, // Latin
0, // Greek
0, // Cyrillic
0, // Armenian
0, // Hebrew
0, // Arabic
"syrc", // Syriac
"thaa", // Thaana
"deva", // Devanagari
"beng", // Bengali
"guru", // Gurmukhi
"gurj", // Gujarati
"orya", // Oriya
"taml", // Tamil
"telu", // Telugu
"knda", // Kannada
"mlym", // Malayalam
"sinh", // Sinhala
0, // Thai
0, // Lao
"tibt", // Tibetan
"mymr", // Myanmar
0, // Georgian
"khmr", // Khmer
0, // SimplifiedChinese
0, // TraditionalChinese
0, // Japanese
0, // Korean
0, // Vietnamese
0, // Symbol
0, // Ogham
0, // Runic
"nko " // N'Ko
};
static const char *getFcFamilyForStyleHint(const QFont::StyleHint style)
{
const char *stylehint = 0;
switch (style) {
case QFont::SansSerif:
stylehint = "sans-serif";
break;
case QFont::Serif:
stylehint = "serif";
break;
case QFont::TypeWriter:
case QFont::Monospace:
stylehint = "monospace";
break;
case QFont::Cursive:
stylehint = "cursive";
break;
case QFont::Fantasy:
stylehint = "fantasy";
break;
default:
break;
}
return stylehint;
}
static bool isSymbolFont(FontFile *fontFile)
{
if (fontFile == 0 || fontFile->fileName.isEmpty())
return false;
QFontEngine::FaceId id;
id.filename = fontFile->fileName.toLocal8Bit();
id.index = fontFile->indexValue;
QFreetypeFace *f = QFreetypeFace::getFace(id);
if (f == 0) {
qWarning("isSymbolFont: Couldn't open face %s/%d", id.filename.data(), id.index);
return false;
}
bool hasSymbolMap = f->symbol_map;
f->release(id);
return hasSymbolMap;
}
Q_GUI_EXPORT void qt_registerAliasToFontFamily(const QString &familyName, const QString &alias);
void QFontconfigDatabase::populateFontDatabase()
{
FcFontSet *fonts;
QString familyName;
FcChar8 *value = 0;
int weight_value;
int slant_value;
int spacing_value;
FcChar8 *file_value;
int indexValue;
FcChar8 *foundry_value;
FcChar8 *style_value;
FcBool scalable;
FcBool antialias;
{
FcObjectSet *os = FcObjectSetCreate();
FcPattern *pattern = FcPatternCreate();
const char *properties [] = {
FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT,
FC_SPACING, FC_FILE, FC_INDEX,
FC_LANG, FC_CHARSET, FC_FOUNDRY, FC_SCALABLE, FC_PIXEL_SIZE, FC_WEIGHT,
FC_WIDTH,
#if FC_VERSION >= 20297
FC_CAPABILITY,
#endif
(const char *)0
};
const char **p = properties;
while (*p) {
FcObjectSetAdd(os, *p);
++p;
}
fonts = FcFontList(0, pattern, os);
FcObjectSetDestroy(os);
FcPatternDestroy(pattern);
}
for (int i = 0; i < fonts->nfont; i++) {
if (FcPatternGetString(fonts->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
continue;
// capitalize(value);
familyName = QString::fromUtf8((const char *)value);
slant_value = FC_SLANT_ROMAN;
weight_value = FC_WEIGHT_REGULAR;
spacing_value = FC_PROPORTIONAL;
file_value = 0;
indexValue = 0;
scalable = FcTrue;
if (FcPatternGetInteger (fonts->fonts[i], FC_SLANT, 0, &slant_value) != FcResultMatch)
slant_value = FC_SLANT_ROMAN;
if (FcPatternGetInteger (fonts->fonts[i], FC_WEIGHT, 0, &weight_value) != FcResultMatch)
weight_value = FC_WEIGHT_REGULAR;
if (FcPatternGetInteger (fonts->fonts[i], FC_SPACING, 0, &spacing_value) != FcResultMatch)
spacing_value = FC_PROPORTIONAL;
if (FcPatternGetString (fonts->fonts[i], FC_FILE, 0, &file_value) != FcResultMatch)
file_value = 0;
if (FcPatternGetInteger (fonts->fonts[i], FC_INDEX, 0, &indexValue) != FcResultMatch)
indexValue = 0;
if (FcPatternGetBool(fonts->fonts[i], FC_SCALABLE, 0, &scalable) != FcResultMatch)
scalable = FcTrue;
if (FcPatternGetString(fonts->fonts[i], FC_FOUNDRY, 0, &foundry_value) != FcResultMatch)
foundry_value = 0;
if (FcPatternGetString(fonts->fonts[i], FC_STYLE, 0, &style_value) != FcResultMatch)
style_value = 0;
if(FcPatternGetBool(fonts->fonts[i],FC_ANTIALIAS,0,&antialias) != FcResultMatch)
antialias = true;
QSupportedWritingSystems writingSystems;
FcLangSet *langset = 0;
FcResult res = FcPatternGetLangSet(fonts->fonts[i], FC_LANG, 0, &langset);
if (res == FcResultMatch) {
for (int i = 1; i < LanguageCount; ++i) {
const FcChar8 *lang = (const FcChar8*) languageForWritingSystem[i];
if (lang) {
FcLangResult langRes = FcLangSetHasLang(langset, lang);
if (langRes != FcLangDifferentLang)
writingSystems.setSupported(QFontDatabase::WritingSystem(i));
}
}
} else {
// we set Other to supported for symbol fonts. It makes no
// sense to merge these with other ones, as they are
// special in a way.
writingSystems.setSupported(QFontDatabase::Other);
}
FcCharSet *cs = 0;
res = FcPatternGetCharSet(fonts->fonts[i], FC_CHARSET, 0, &cs);
if (res == FcResultMatch) {
// some languages are not supported by FontConfig, we rather check the
// charset to detect these
for (int i = 1; i < SampleCharCount; ++i) {
if (!sampleCharForWritingSystem[i] || writingSystems.supported(QFontDatabase::WritingSystem(i)))
continue;
if (FcCharSetHasChar(cs, sampleCharForWritingSystem[i]))
writingSystems.setSupported(QFontDatabase::WritingSystem(i));
}
}
#if FC_VERSION >= 20297
for (int j = 1; j < LanguageCount; ++j) {
if (writingSystems.supported(QFontDatabase::WritingSystem(j))
&& requiresOpenType(j) && openType[j]) {
FcChar8 *cap;
res = FcPatternGetString (fonts->fonts[i], FC_CAPABILITY, 0, &cap);
if (res != FcResultMatch || !strstr((const char *)cap, openType[j]))
writingSystems.setSupported(QFontDatabase::WritingSystem(j),false);
}
}
#endif
FontFile *fontFile = new FontFile;
fontFile->fileName = QLatin1String((const char *)file_value);
fontFile->indexValue = indexValue;
if (isSymbolFont(fontFile))
writingSystems.setSupported(QFontDatabase::Other);
QFont::Style style = (slant_value == FC_SLANT_ITALIC)
? QFont::StyleItalic
: ((slant_value == FC_SLANT_OBLIQUE)
? QFont::StyleOblique
: QFont::StyleNormal);
QFont::Weight weight = QFont::Weight(getFCWeight(weight_value));
double pixel_size = 0;
if (!scalable) {
int width = 100;
FcPatternGetInteger (fonts->fonts[i], FC_WIDTH, 0, &width);
FcPatternGetDouble (fonts->fonts[i], FC_PIXEL_SIZE, 0, &pixel_size);
}
bool fixedPitch = spacing_value >= FC_MONO;
QFont::Stretch stretch = QFont::Unstretched;
QString styleName = style_value ? QString::fromUtf8((const char *) style_value) : QString();
QPlatformFontDatabase::registerFont(familyName,styleName,QLatin1String((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,writingSystems,fontFile);
// qDebug() << familyName << (const char *)foundry_value << weight << style << &writingSystems << scalable << true << pixel_size;
for (int k = 1; FcPatternGetString(fonts->fonts[i], FC_FAMILY, k, &value) == FcResultMatch; ++k)
qt_registerAliasToFontFamily(familyName, QString::fromUtf8((const char *)value));
}
FcFontSetDestroy (fonts);
struct FcDefaultFont {
const char *qtname;
const char *rawname;
bool fixed;
};
const FcDefaultFont defaults[] = {
{ "Serif", "serif", false },
{ "Sans Serif", "sans-serif", false },
{ "Monospace", "monospace", true },
{ 0, 0, false }
};
const FcDefaultFont *f = defaults;
// aliases only make sense for 'common', not for any of the specials
QSupportedWritingSystems ws;
ws.setSupported(QFontDatabase::Latin);
while (f->qtname) {
QString familyQtName = QString::fromLatin1(f->qtname);
registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleNormal,QFont::Unstretched,true,true,0,f->fixed,ws,0);
registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleItalic,QFont::Unstretched,true,true,0,f->fixed,ws,0);
registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleOblique,QFont::Unstretched,true,true,0,f->fixed,ws,0);
++f;
}
//Lighthouse has very lazy population of the font db. We want it to be initialized when
//QApplication is constructed, so that the population procedure can do something like this to
//set the default font
// const FcDefaultFont *s = defaults;
// QFont font("Sans Serif");
// font.setPointSize(9);
// QApplication::setFont(font);
}
QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine, QChar::Script script)
{
return new QFontEngineMultiFontConfig(fontEngine, script);
}
QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, QChar::Script script, void *usrPtr)
{
if (!usrPtr)
return 0;
QFontDef fontDef = f;
QFontEngineFT *engine;
FontFile *fontfile = static_cast<FontFile *> (usrPtr);
QFontEngine::FaceId fid;
fid.filename = fontfile->fileName.toLocal8Bit();
fid.index = fontfile->indexValue;
bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
engine = new QFontEngineFT(fontDef);
QFontEngineFT::GlyphFormat format;
// try and get the pattern
FcPattern *pattern = FcPatternCreate();
FcValue value;
value.type = FcTypeString;
QByteArray cs = fontDef.family.toUtf8();
value.u.s = (const FcChar8 *)cs.data();
FcPatternAdd(pattern,FC_FAMILY,value,true);
value.u.s = (const FcChar8 *)fid.filename.data();
FcPatternAdd(pattern,FC_FILE,value,true);
value.type = FcTypeInteger;
value.u.i = fid.index;
FcPatternAdd(pattern,FC_INDEX,value,true);
FcResult result;
FcPattern *match = FcFontMatch(0, pattern, &result);
if (match) {
QFontEngineFT::HintStyle default_hint_style;
if (f.hintingPreference != QFont::PreferDefaultHinting) {
switch (f.hintingPreference) {
case QFont::PreferNoHinting:
default_hint_style = QFontEngineFT::HintNone;
break;
case QFont::PreferVerticalHinting:
default_hint_style = QFontEngineFT::HintLight;
break;
case QFont::PreferFullHinting:
default:
default_hint_style = QFontEngineFT::HintFull;
break;
}
} else {
int hint_style = 0;
if (FcPatternGetInteger (match, FC_HINT_STYLE, 0, &hint_style) == FcResultNoMatch)
hint_style = QFontEngineFT::HintFull;
switch (hint_style) {
case FC_HINT_NONE:
default_hint_style = QFontEngineFT::HintNone;
break;
case FC_HINT_SLIGHT:
default_hint_style = QFontEngineFT::HintLight;
break;
case FC_HINT_MEDIUM:
default_hint_style = QFontEngineFT::HintMedium;
break;
default:
default_hint_style = QFontEngineFT::HintFull;
break;
}
}
engine->setDefaultHintStyle(default_hint_style);
if (antialias) {
QFontEngineFT::SubpixelAntialiasingType subpixelType = QFontEngineFT::Subpixel_None;
int subpixel = FC_RGBA_NONE;
FcPatternGetInteger(match, FC_RGBA, 0, &subpixel);
if (subpixel == FC_RGBA_UNKNOWN)
subpixel = FC_RGBA_NONE;
switch (subpixel) {
case FC_RGBA_NONE: subpixelType = QFontEngineFT::Subpixel_None; break;
case FC_RGBA_RGB: subpixelType = QFontEngineFT::Subpixel_RGB; break;
case FC_RGBA_BGR: subpixelType = QFontEngineFT::Subpixel_BGR; break;
case FC_RGBA_VRGB: subpixelType = QFontEngineFT::Subpixel_VRGB; break;
case FC_RGBA_VBGR: subpixelType = QFontEngineFT::Subpixel_VBGR; break;
default: break;
}
format = subpixelType == QFontEngineFT::Subpixel_None
? QFontEngineFT::Format_A8 : QFontEngineFT::Format_A32;
engine->subpixelType = subpixelType;
} else
format = QFontEngineFT::Format_Mono;
FcPatternDestroy(match);
} else
format = antialias ? QFontEngineFT::Format_A8 : QFontEngineFT::Format_Mono;
FcPatternDestroy(pattern);
if (!engine->init(fid,antialias,format)) {
delete engine;
engine = 0;
return engine;
}
if (engine->invalid()) {
delete engine;
engine = 0;
} else if (scriptRequiresOpenType(script)) {
if (!engine->supportsScript(script)) {
delete engine;
engine = 0;
}
}
return engine;
}
QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
{
QStringList fallbackFamilies;
FcPattern *pattern = FcPatternCreate();
if (!pattern)
return fallbackFamilies;
FcValue value;
value.type = FcTypeString;
QByteArray cs = family.toUtf8();
value.u.s = (const FcChar8 *)cs.data();
FcPatternAdd(pattern,FC_FAMILY,value,true);
int slant_value = FC_SLANT_ROMAN;
if (style == QFont::StyleItalic)
slant_value = FC_SLANT_ITALIC;
else if (style == QFont::StyleOblique)
slant_value = FC_SLANT_OBLIQUE;
FcPatternAddInteger(pattern, FC_SLANT, slant_value);
Q_ASSERT(uint(script) < SpecialLanguageCount);
if (*specialLanguages[script] != '\0') {
FcLangSet *ls = FcLangSetCreate();
FcLangSetAdd(ls, (const FcChar8*)specialLanguages[script]);
FcPatternAddLangSet(pattern, FC_LANG, ls);
FcLangSetDestroy(ls);
} else if (!family.isEmpty()) {
// If script is Common or Han, then it may include languages like CJK,
// we should attach system default language set to the pattern
// to obtain correct font fallback list (i.e. if LANG=zh_CN
// then we normally want to use a Chinese font for CJK text;
// while a Japanese font should be used for that if LANG=ja)
FcPattern *dummy = FcPatternCreate();
FcDefaultSubstitute(dummy);
FcChar8 *lang = 0;
FcResult res = FcPatternGetString(dummy, FC_LANG, 0, &lang);
if (res == FcResultMatch)
FcPatternAddString(pattern, FC_LANG, lang);
FcPatternDestroy(dummy);
}
const char *stylehint = getFcFamilyForStyleHint(styleHint);
if (stylehint) {
value.u.s = (const FcChar8 *)stylehint;
FcPatternAddWeak(pattern, FC_FAMILY, value, FcTrue);
}
FcConfigSubstitute(0, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcResult result = FcResultMatch;
FcFontSet *fontSet = FcFontSort(0,pattern,FcFalse,0,&result);
FcPatternDestroy(pattern);
if (fontSet) {
for (int i = 0; i < fontSet->nfont; i++) {
FcChar8 *value = 0;
if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
continue;
// capitalize(value);
QString familyName = QString::fromUtf8((const char *)value);
if (!fallbackFamilies.contains(familyName,Qt::CaseInsensitive) &&
familyName.compare(family, Qt::CaseInsensitive)) {
fallbackFamilies << familyName;
}
}
FcFontSetDestroy(fontSet);
}
// qDebug() << "fallbackFamilies for:" << family << style << styleHint << script << fallbackFamilies;
return fallbackFamilies;
}
static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count)
{
#if FC_VERSION < 20402
Q_UNUSED(data)
return FcFreeTypeQuery(file, id, blanks, count);
#else
if (data.isEmpty())
return FcFreeTypeQuery(file, id, blanks, count);
FT_Library lib = qt_getFreetype();
FcPattern *pattern = 0;
FT_Face face;
if (!FT_New_Memory_Face(lib, (const FT_Byte *)data.constData(), data.size(), id, &face)) {
*count = face->num_faces;
pattern = FcFreeTypeQueryFace(face, file, id, blanks);
FT_Done_Face(face);
}
return pattern;
#endif
}
QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName)
{
QStringList families;
FcFontSet *set = FcConfigGetFonts(0, FcSetApplication);
if (!set) {
FcConfigAppFontAddFile(0, (const FcChar8 *)":/non-existent");
set = FcConfigGetFonts(0, FcSetApplication); // try again
if (!set)
return families;
}
int id = 0;
FcBlanks *blanks = FcConfigGetBlanks(0);
int count = 0;
FcPattern *pattern = 0;
do {
pattern = queryFont((const FcChar8 *)QFile::encodeName(fileName).constData(),
fontData, id, blanks, &count);
if (!pattern)
return families;
FcPatternDel(pattern, FC_FILE);
QByteArray cs = fileName.toUtf8();
FcPatternAddString(pattern, FC_FILE, (const FcChar8 *) cs.constData());
FcChar8 *fam = 0;
if (FcPatternGetString(pattern, FC_FAMILY, 0, &fam) == FcResultMatch) {
QString family = QString::fromUtf8(reinterpret_cast<const char *>(fam));
families << family;
}
if (!FcFontSetAdd(set, pattern))
return families;
++id;
} while (pattern && id < count);
return families;
}
QString QFontconfigDatabase::resolveFontFamilyAlias(const QString &family) const
{
FcPattern *pattern = FcPatternCreate();
if (!pattern)
return family;
if (!family.isEmpty()) {
QByteArray cs = family.toUtf8();
FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) cs.constData());
}
FcConfigSubstitute(0, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcChar8 *familyAfterSubstitution = 0;
FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution);
QString resolved = QString::fromUtf8((const char *) familyAfterSubstitution);
FcPatternDestroy(pattern);
return resolved;
}
QFont QFontconfigDatabase::defaultFont() const
{
// Hack to get system default language until FcGetDefaultLangs()
// is exported (https://bugs.freedesktop.org/show_bug.cgi?id=32853)
// or https://bugs.freedesktop.org/show_bug.cgi?id=35482 is fixed
FcPattern *dummy = FcPatternCreate();
FcDefaultSubstitute(dummy);
FcChar8 *lang = 0;
FcResult res = FcPatternGetString(dummy, FC_LANG, 0, &lang);
FcPattern *pattern = FcPatternCreate();
if (res == FcResultMatch) {
// Make defaultFont pattern matching locale language aware, because
// certain FC_LANG based custom rules may happen in FcConfigSubstitute()
FcPatternAddString(pattern, FC_LANG, lang);
}
FcConfigSubstitute(0, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcChar8 *familyAfterSubstitution = 0;
FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution);
QString resolved = QString::fromUtf8((const char *) familyAfterSubstitution);
FcPatternDestroy(pattern);
FcPatternDestroy(dummy);
return QFont(resolved);
}
QT_END_NAMESPACE