blob: 0d4bc288ea6e48f11a42bf9b02ea4cb405c93612 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qfontconfigdatabase_p.h"
#include "qfontenginemultifontconfig_p.h"
#include <QtFontDatabaseSupport/private/qfontengine_ft_p.h>
#include <QtCore/QList>
#include <QtCore/QElapsedTimer>
#include <QtCore/QFile>
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformscreen.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformservices.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtGui/qguiapplication.h>
#include <QtCore/private/qduplicatetracker_p.h>
#include <fontconfig/fontconfig.h>
#if FC_VERSION >= 20402
#include <fontconfig/fcfreetype.h>
#endif
QT_BEGIN_NAMESPACE
static const int maxWeight = 99;
static inline int mapToQtWeightForRange(int fcweight, int fcLower, int fcUpper, int qtLower, int qtUpper)
{
return qtLower + ((fcweight - fcLower) * (qtUpper - qtLower)) / (fcUpper - fcLower);
}
static inline int weightFromFcWeight(int fcweight)
{
// Font Config uses weights from 0 to 215 (the highest enum value) while QFont ranges from
// 0 to 99. The spacing between the values for the enums are uneven so a linear mapping from
// Font Config values to Qt would give surprising results. So, we do a piecewise linear
// mapping. This ensures that where there is a corresponding enum on both sides (for example
// FC_WEIGHT_DEMIBOLD and QFont::DemiBold) we map one to the other but other values map
// to intermediate Qt weights.
if (fcweight <= FC_WEIGHT_THIN)
return QFont::Thin;
if (fcweight <= FC_WEIGHT_ULTRALIGHT)
return mapToQtWeightForRange(fcweight, FC_WEIGHT_THIN, FC_WEIGHT_ULTRALIGHT, QFont::Thin, QFont::ExtraLight);
if (fcweight <= FC_WEIGHT_LIGHT)
return mapToQtWeightForRange(fcweight, FC_WEIGHT_ULTRALIGHT, FC_WEIGHT_LIGHT, QFont::ExtraLight, QFont::Light);
if (fcweight <= FC_WEIGHT_NORMAL)
return mapToQtWeightForRange(fcweight, FC_WEIGHT_LIGHT, FC_WEIGHT_NORMAL, QFont::Light, QFont::Normal);
if (fcweight <= FC_WEIGHT_MEDIUM)
return mapToQtWeightForRange(fcweight, FC_WEIGHT_NORMAL, FC_WEIGHT_MEDIUM, QFont::Normal, QFont::Medium);
if (fcweight <= FC_WEIGHT_DEMIBOLD)
return mapToQtWeightForRange(fcweight, FC_WEIGHT_MEDIUM, FC_WEIGHT_DEMIBOLD, QFont::Medium, QFont::DemiBold);
if (fcweight <= FC_WEIGHT_BOLD)
return mapToQtWeightForRange(fcweight, FC_WEIGHT_DEMIBOLD, FC_WEIGHT_BOLD, QFont::DemiBold, QFont::Bold);
if (fcweight <= FC_WEIGHT_ULTRABOLD)
return mapToQtWeightForRange(fcweight, FC_WEIGHT_BOLD, FC_WEIGHT_ULTRABOLD, QFont::Bold, QFont::ExtraBold);
if (fcweight <= FC_WEIGHT_BLACK)
return mapToQtWeightForRange(fcweight, FC_WEIGHT_ULTRABOLD, FC_WEIGHT_BLACK, QFont::ExtraBold, QFont::Black);
if (fcweight <= FC_WEIGHT_ULTRABLACK)
return mapToQtWeightForRange(fcweight, FC_WEIGHT_BLACK, FC_WEIGHT_ULTRABLACK, QFont::Black, maxWeight);
return maxWeight;
}
static inline int stretchFromFcWidth(int fcwidth)
{
// Font Config enums for width match pretty closely with those used by Qt so just use
// Font Config values directly while enforcing the same limits imposed by QFont.
const int maxStretch = 4000;
int qtstretch;
if (fcwidth < 1)
qtstretch = 1;
else if (fcwidth > maxStretch)
qtstretch = maxStretch;
else
qtstretch = fcwidth;
return qtstretch;
}
static const char specialLanguages[][6] = {
"", // 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
"lez", // CaucasianAlbanian
"bsq", // BassaVah
"fr", // Duployan
"sq", // Elbasan
"sa", // Grantha
"hnj", // PahawhHmong
"sd", // Khojki
"lab", // LinearA
"hi", // Mahajani
"xmn", // Manichaean
"men", // MendeKikakui
"mr", // Modi
"mru", // Mro
"xna", // OldNorthArabian
"arc", // Nabataean
"arc", // Palmyrene
"ctd", // PauCinHau
"kv", // OldPermic
"pal", // PsalterPahlavi
"sa", // Siddham
"sd", // Khudawadi
"mai", // Tirhuta
"hoc", // WarangCiti
"", // Ahom
"", // AnatolianHieroglyphs
"", // Hatran
"", // Multani
"", // OldHungarian
"", // SignWriting
"", // Adlam
"", // Bhaiksuki
"", // Marchen
"", // Newa
"", // Osage
"", // Tangut
"", // MasaramGondi
"", // Nushu
"", // Soyombo
"", // ZanabazarSquare
"", // Dogra
"", // GunjalaGondi
"", // HanifiRohingya
"", // Makasar
"", // Medefaidrin
"", // OldSogdian
"", // Sogdian
"", // Elymaic
"", // Nandinagari
"", // NyiakengPuachueHmong
"", // Wancho
"", // Chorasmian
"", // DivesAkuru
"", // KhitanSmallScript
"" // Yezidi
};
Q_STATIC_ASSERT(sizeof specialLanguages / sizeof *specialLanguages == QChar::ScriptCount);
// 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[][6] = {
"", // 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
"", // Symbol
"sga", // Ogham
"non", // Runic
"man" // N'Ko
};
Q_STATIC_ASSERT(sizeof languageForWritingSystem / sizeof *languageForWritingSystem == QFontDatabase::WritingSystemsCount);
#if FC_VERSION >= 20297
// Newer FontConfig let's us sort out fonts that report certain scripts support,
// but no open type tables for handling them correctly.
// Check the reported script presence in the FC_CAPABILITY's "otlayout:" section.
static const char capabilityForWritingSystem[][5] = {
"", // Any
"", // Latin
"", // Greek
"", // Cyrillic
"", // Armenian
"", // Hebrew
"", // Arabic
"syrc", // Syriac
"thaa", // Thaana
"deva", // Devanagari
"beng", // Bengali
"guru", // Gurmukhi
"gujr", // Gujarati
"orya", // Oriya
"taml", // Tamil
"telu", // Telugu
"knda", // Kannada
"mlym", // Malayalam
"sinh", // Sinhala
"", // Thai
"", // Lao
"tibt", // Tibetan
"mymr", // Myanmar
"", // Georgian
"khmr", // Khmer
"", // SimplifiedChinese
"", // TraditionalChinese
"", // Japanese
"", // Korean
"", // Vietnamese
"", // Symbol
"", // Ogham
"", // Runic
"nko " // N'Ko
};
Q_STATIC_ASSERT(sizeof(capabilityForWritingSystem) / sizeof(*capabilityForWritingSystem) == QFontDatabase::WritingSystemsCount);
#endif
static const char *getFcFamilyForStyleHint(const QFont::StyleHint style)
{
const char *stylehint = nullptr;
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 inline bool requiresOpenType(int writingSystem)
{
return ((writingSystem >= QFontDatabase::Syriac && writingSystem <= QFontDatabase::Sinhala)
|| writingSystem == QFontDatabase::Khmer || writingSystem == QFontDatabase::Nko);
}
static void populateFromPattern(FcPattern *pattern)
{
QString familyName;
QString familyNameLang;
FcChar8 *value = nullptr;
int weight_value;
int slant_value;
int spacing_value;
int width_value;
FcChar8 *file_value;
int indexValue;
FcChar8 *foundry_value;
FcChar8 *style_value;
FcBool scalable;
FcBool antialias;
if (FcPatternGetString(pattern, FC_FAMILY, 0, &value) != FcResultMatch)
return;
familyName = QString::fromUtf8((const char *)value);
if (FcPatternGetString(pattern, FC_FAMILYLANG, 0, &value) == FcResultMatch)
familyNameLang = QString::fromUtf8((const char *)value);
slant_value = FC_SLANT_ROMAN;
weight_value = FC_WEIGHT_REGULAR;
spacing_value = FC_PROPORTIONAL;
file_value = nullptr;
indexValue = 0;
scalable = FcTrue;
if (FcPatternGetInteger(pattern, FC_SLANT, 0, &slant_value) != FcResultMatch)
slant_value = FC_SLANT_ROMAN;
if (FcPatternGetInteger(pattern, FC_WEIGHT, 0, &weight_value) != FcResultMatch)
weight_value = FC_WEIGHT_REGULAR;
if (FcPatternGetInteger(pattern, FC_WIDTH, 0, &width_value) != FcResultMatch)
width_value = FC_WIDTH_NORMAL;
if (FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing_value) != FcResultMatch)
spacing_value = FC_PROPORTIONAL;
if (FcPatternGetString(pattern, FC_FILE, 0, &file_value) != FcResultMatch)
file_value = nullptr;
if (FcPatternGetInteger(pattern, FC_INDEX, 0, &indexValue) != FcResultMatch)
indexValue = 0;
if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &scalable) != FcResultMatch)
scalable = FcTrue;
if (FcPatternGetString(pattern, FC_FOUNDRY, 0, &foundry_value) != FcResultMatch)
foundry_value = nullptr;
if (FcPatternGetString(pattern, FC_STYLE, 0, &style_value) != FcResultMatch)
style_value = nullptr;
if (FcPatternGetBool(pattern,FC_ANTIALIAS,0,&antialias) != FcResultMatch)
antialias = true;
QSupportedWritingSystems writingSystems;
FcLangSet *langset = nullptr;
FcResult res = FcPatternGetLangSet(pattern, FC_LANG, 0, &langset);
if (res == FcResultMatch) {
bool hasLang = false;
#if FC_VERSION >= 20297
FcChar8 *cap = nullptr;
FcResult capRes = FcResultNoMatch;
#endif
for (int j = 1; j < QFontDatabase::WritingSystemsCount; ++j) {
const FcChar8 *lang = (const FcChar8*) languageForWritingSystem[j];
if (lang) {
FcLangResult langRes = FcLangSetHasLang(langset, lang);
if (langRes != FcLangDifferentLang) {
#if FC_VERSION >= 20297
if (*capabilityForWritingSystem[j] && requiresOpenType(j)) {
if (cap == nullptr)
capRes = FcPatternGetString(pattern, FC_CAPABILITY, 0, &cap);
if (capRes == FcResultMatch && strstr(reinterpret_cast<const char *>(cap), capabilityForWritingSystem[j]) == nullptr)
continue;
}
#endif
writingSystems.setSupported(QFontDatabase::WritingSystem(j));
hasLang = true;
}
}
}
if (!hasLang)
// none of our known languages, add it to the other set
writingSystems.setSupported(QFontDatabase::Other);
} 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);
}
FontFile *fontFile = new FontFile;
fontFile->fileName = QString::fromLocal8Bit((const char *)file_value);
fontFile->indexValue = indexValue;
QFont::Style style = (slant_value == FC_SLANT_ITALIC)
? QFont::StyleItalic
: ((slant_value == FC_SLANT_OBLIQUE)
? QFont::StyleOblique
: QFont::StyleNormal);
// Note: weight should really be an int but registerFont incorrectly uses an enum
QFont::Weight weight = QFont::Weight(weightFromFcWeight(weight_value));
double pixel_size = 0;
if (!scalable)
FcPatternGetDouble (pattern, FC_PIXEL_SIZE, 0, &pixel_size);
bool fixedPitch = spacing_value >= FC_MONO;
// Note: stretch should really be an int but registerFont incorrectly uses an enum
QFont::Stretch stretch = QFont::Stretch(stretchFromFcWidth(width_value));
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(pattern, FC_FAMILY, k, &value) == FcResultMatch; ++k) {
const QString altFamilyName = QString::fromUtf8((const char *)value);
// Extra family names can be aliases or subfamilies.
// If it is a subfamily, register it as a separate font, so only members of the subfamily are
// matched when the subfamily is requested.
QString altStyleName;
if (FcPatternGetString(pattern, FC_STYLE, k, &value) == FcResultMatch)
altStyleName = QString::fromUtf8((const char *)value);
else
altStyleName = styleName;
QString altFamilyNameLang;
if (FcPatternGetString(pattern, FC_FAMILYLANG, k, &value) == FcResultMatch)
altFamilyNameLang = QString::fromUtf8((const char *)value);
else
altFamilyNameLang = familyNameLang;
if (familyNameLang == altFamilyNameLang && altStyleName != styleName) {
FontFile *altFontFile = new FontFile(*fontFile);
QPlatformFontDatabase::registerFont(altFamilyName, altStyleName, QLatin1String((const char *)foundry_value),weight,style,stretch,antialias,scalable,pixel_size,fixedPitch,writingSystems,altFontFile);
} else {
QPlatformFontDatabase::registerAliasToFontFamily(familyName, altFamilyName);
}
}
}
void QFontconfigDatabase::populateFontDatabase()
{
FcInit();
FcFontSet *fonts;
{
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_WIDTH, FC_FAMILYLANG,
#if FC_VERSION >= 20297
FC_CAPABILITY,
#endif
(const char *)nullptr
};
const char **p = properties;
while (*p) {
FcObjectSetAdd(os, *p);
++p;
}
fonts = FcFontList(nullptr, pattern, os);
FcObjectSetDestroy(os);
FcPatternDestroy(pattern);
}
for (int i = 0; i < fonts->nfont; i++)
populateFromPattern(fonts->fonts[i]);
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 },
{ nullptr, nullptr, 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,nullptr);
registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleItalic,QFont::Unstretched,true,true,0,f->fixed,ws,nullptr);
registerFont(familyQtName,QString(),QString(),QFont::Normal,QFont::StyleOblique,QFont::Unstretched,true,true,0,f->fixed,ws,nullptr);
++f;
}
//QPA 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);
}
void QFontconfigDatabase::invalidate()
{
// Clear app fonts.
FcConfigAppFontClear(nullptr);
}
QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine, QChar::Script script)
{
return new QFontEngineMultiFontConfig(fontEngine, script);
}
namespace {
QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintingPreference, FcPattern *match, bool useXftConf)
{
switch (hintingPreference) {
case QFont::PreferNoHinting:
return QFontEngine::HintNone;
case QFont::PreferVerticalHinting:
return QFontEngine::HintLight;
case QFont::PreferFullHinting:
return QFontEngine::HintFull;
case QFont::PreferDefaultHinting:
break;
}
if (QHighDpiScaling::isActive())
return QFontEngine::HintNone;
int hint_style = 0;
if (FcPatternGetInteger (match, FC_HINT_STYLE, 0, &hint_style) == FcResultMatch) {
switch (hint_style) {
case FC_HINT_NONE:
return QFontEngine::HintNone;
case FC_HINT_SLIGHT:
return QFontEngine::HintLight;
case FC_HINT_MEDIUM:
return QFontEngine::HintMedium;
case FC_HINT_FULL:
return QFontEngine::HintFull;
default:
Q_UNREACHABLE();
break;
}
}
if (useXftConf) {
void *hintStyleResource =
QGuiApplication::platformNativeInterface()->nativeResourceForScreen("hintstyle",
QGuiApplication::primaryScreen());
int hintStyle = int(reinterpret_cast<qintptr>(hintStyleResource));
if (hintStyle > 0)
return QFontEngine::HintStyle(hintStyle - 1);
}
return QFontEngine::HintFull;
}
QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match, bool useXftConf)
{
int subpixel = FC_RGBA_UNKNOWN;
if (FcPatternGetInteger(match, FC_RGBA, 0, &subpixel) == FcResultMatch) {
switch (subpixel) {
case FC_RGBA_UNKNOWN:
case FC_RGBA_NONE:
return QFontEngine::Subpixel_None;
case FC_RGBA_RGB:
return QFontEngine::Subpixel_RGB;
case FC_RGBA_BGR:
return QFontEngine::Subpixel_BGR;
case FC_RGBA_VRGB:
return QFontEngine::Subpixel_VRGB;
case FC_RGBA_VBGR:
return QFontEngine::Subpixel_VBGR;
default:
Q_UNREACHABLE();
break;
}
}
if (useXftConf) {
void *subpixelTypeResource =
QGuiApplication::platformNativeInterface()->nativeResourceForScreen("subpixeltype",
QGuiApplication::primaryScreen());
int subpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource));
if (subpixelType > 0)
return QFontEngine::SubpixelAntialiasingType(subpixelType - 1);
}
return QFontEngine::Subpixel_None;
}
} // namespace
QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr)
{
if (!usrPtr)
return nullptr;
FontFile *fontfile = static_cast<FontFile *> (usrPtr);
QFontEngine::FaceId fid;
fid.filename = QFile::encodeName(fontfile->fileName);
fid.index = fontfile->indexValue;
// FIXME: Unify with logic in QFontEngineFT::create()
QFontEngineFT *engine = new QFontEngineFT(f);
engine->face_id = fid;
setupFontEngine(engine, f);
if (!engine->init(fid, engine->antialias, engine->defaultFormat) || engine->invalid()) {
delete engine;
engine = nullptr;
}
return engine;
}
QFontEngine *QFontconfigDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference)
{
QFontEngineFT *engine = static_cast<QFontEngineFT*>(QFreeTypeFontDatabase::fontEngine(fontData, pixelSize, hintingPreference));
if (engine == nullptr)
return nullptr;
setupFontEngine(engine, engine->fontDef);
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;
const 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) < QChar::ScriptCount);
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 = nullptr;
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(nullptr, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcResult result = FcResultMatch;
FcFontSet *fontSet = FcFontSort(nullptr,pattern,FcFalse,nullptr,&result);
FcPatternDestroy(pattern);
if (fontSet) {
QDuplicateTracker<QString> duplicates;
duplicates.reserve(fontSet->nfont + 1);
(void)duplicates.hasSeen(family.toCaseFolded());
for (int i = 0; i < fontSet->nfont; i++) {
FcChar8 *value = nullptr;
if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch)
continue;
// capitalize(value);
const QString familyName = QString::fromUtf8((const char *)value);
const QString familyNameCF = familyName.toCaseFolded();
if (!duplicates.hasSeen(familyNameCF)) {
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 = nullptr;
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(nullptr, FcSetApplication);
if (!set) {
FcConfigAppFontAddFile(nullptr, (const FcChar8 *)":/non-existent");
set = FcConfigGetFonts(nullptr, FcSetApplication); // try again
if (!set)
return families;
}
int id = 0;
FcBlanks *blanks = FcConfigGetBlanks(nullptr);
int count = 0;
FcPattern *pattern;
do {
pattern = queryFont((const FcChar8 *)QFile::encodeName(fileName).constData(),
fontData, id, blanks, &count);
if (!pattern)
return families;
FcChar8 *fam = nullptr;
if (FcPatternGetString(pattern, FC_FAMILY, 0, &fam) == FcResultMatch) {
QString family = QString::fromUtf8(reinterpret_cast<const char *>(fam));
families << family;
}
populateFromPattern(pattern);
FcFontSetAdd(set, pattern);
++id;
} while (id < count);
return families;
}
QString QFontconfigDatabase::resolveFontFamilyAlias(const QString &family) const
{
QString resolved = QFreeTypeFontDatabase::resolveFontFamilyAlias(family);
if (!resolved.isEmpty() && resolved != family)
return resolved;
FcPattern *pattern = FcPatternCreate();
if (!pattern)
return family;
if (!family.isEmpty()) {
const QByteArray cs = family.toUtf8();
FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *) cs.constData());
}
FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcChar8 *familyAfterSubstitution = nullptr;
FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution);
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 = nullptr;
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(nullptr, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcChar8 *familyAfterSubstitution = nullptr;
FcPatternGetString(pattern, FC_FAMILY, 0, &familyAfterSubstitution);
QString resolved = QString::fromUtf8((const char *) familyAfterSubstitution);
FcPatternDestroy(pattern);
FcPatternDestroy(dummy);
return QFont(resolved);
}
void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef &fontDef) const
{
bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias);
bool forcedAntialiasSetting = !antialias || QHighDpiScaling::isActive();
const QPlatformServices *services = QGuiApplicationPrivate::platformIntegration()->services();
bool useXftConf = false;
if (services) {
const QList<QByteArray> desktopEnv = services->desktopEnvironment().split(':');
useXftConf = desktopEnv.contains("GNOME") || desktopEnv.contains("UNITY") || desktopEnv.contains("XFCE");
}
if (useXftConf && !forcedAntialiasSetting) {
void *antialiasResource =
QGuiApplication::platformNativeInterface()->nativeResourceForScreen("antialiasingEnabled",
QGuiApplication::primaryScreen());
int antialiasingEnabled = int(reinterpret_cast<qintptr>(antialiasResource));
if (antialiasingEnabled > 0)
antialias = antialiasingEnabled - 1;
}
QFontEngine::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);
QFontEngine::FaceId fid = engine->faceId();
if (!fid.filename.isEmpty()) {
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);
}
if (fontDef.pixelSize > 0.1)
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, fontDef.pixelSize);
FcResult result;
FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcPattern *match = FcFontMatch(nullptr, pattern, &result);
if (match) {
engine->setDefaultHintStyle(defaultHintStyleFromMatch((QFont::HintingPreference)fontDef.hintingPreference, match, useXftConf));
FcBool fc_autohint;
if (FcPatternGetBool(match, FC_AUTOHINT,0, &fc_autohint) == FcResultMatch)
engine->forceAutoHint = fc_autohint;
#if defined(FT_LCD_FILTER_H)
int lcdFilter;
if (FcPatternGetInteger(match, FC_LCD_FILTER, 0, &lcdFilter) == FcResultMatch)
engine->lcdFilterType = lcdFilter;
#endif
if (!forcedAntialiasSetting) {
FcBool fc_antialias;
if (FcPatternGetBool(match, FC_ANTIALIAS,0, &fc_antialias) == FcResultMatch)
antialias = fc_antialias;
}
if (antialias) {
QFontEngine::SubpixelAntialiasingType subpixelType = QFontEngine::Subpixel_None;
if (!(fontDef.styleStrategy & QFont::NoSubpixelAntialias))
subpixelType = subpixelTypeFromMatch(match, useXftConf);
engine->subpixelType = subpixelType;
format = (subpixelType == QFontEngine::Subpixel_None)
? QFontEngine::Format_A8
: QFontEngine::Format_A32;
} else
format = QFontEngine::Format_Mono;
FcPatternDestroy(match);
} else
format = antialias ? QFontEngine::Format_A8 : QFontEngine::Format_Mono;
FcPatternDestroy(pattern);
engine->antialias = antialias;
engine->defaultFormat = format;
engine->glyphFormat = format;
}
QT_END_NAMESPACE