QortalOS Brooklyn for Raspberry Pi 4
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

884 lines
29 KiB

/*
SPDX-FileCopyrightText: 2002 Craig Drummond <[email protected]>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "kxftconfig.h"
#ifdef HAVE_FONTCONFIG
#include <ctype.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <QByteArray>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QStandardPaths>
#include <QX11Info>
#include <KLocalizedString>
#include <fontconfig/fontconfig.h>
using namespace std;
static int point2Pixel(double point)
{
return (int)(((point * QX11Info::appDpiY()) / 72.0) + 0.5);
}
static int pixel2Point(double pixel)
{
return (int)(((pixel * 72.0) / (double)QX11Info::appDpiY()) + 0.5);
}
static bool equal(double d1, double d2)
{
return (fabs(d1 - d2) < 0.0001);
}
static QString dirSyntax(const QString &d)
{
if (d.isNull()) {
return d;
}
QString ds(d);
ds.replace(QLatin1String("//"), QLatin1String("/"));
if (!ds.endsWith(QLatin1Char('/'))) {
ds += QLatin1Char('/');
}
return ds;
}
inline bool fExists(const QString &p)
{
return QFileInfo(p).isFile();
}
inline bool dWritable(const QString &p)
{
QFileInfo info(p);
return info.isDir() && info.isWritable();
}
static QString getDir(const QString &path)
{
QString str(path);
const int slashPos = str.lastIndexOf(QLatin1Char('/'));
if (slashPos != -1) {
str.truncate(slashPos + 1);
}
return dirSyntax(str);
}
static QDateTime getTimeStamp(const QString &item)
{
return QFileInfo(item).lastModified();
}
static QString getEntry(QDomElement element, const char *type, unsigned int numAttributes, ...)
{
if (numAttributes == uint(element.attributes().length())) {
va_list args;
unsigned int arg;
bool ok = true;
va_start(args, numAttributes);
for (arg = 0; arg < numAttributes && ok; ++arg) {
const char *attr = va_arg(args, const char *);
const char *val = va_arg(args, const char *);
if (!attr || !val || val != element.attribute(attr)) {
ok = false;
}
}
va_end(args);
if (ok) {
QDomNode n = element.firstChild();
if (!n.isNull()) {
QDomElement e = n.toElement();
if (!e.isNull() && type == e.tagName()) {
return e.text();
}
}
}
}
return QString();
}
static KXftConfig::SubPixel::Type strToType(const char *str)
{
if (0 == strcmp(str, "rgb")) {
return KXftConfig::SubPixel::Rgb;
} else if (0 == strcmp(str, "bgr")) {
return KXftConfig::SubPixel::Bgr;
} else if (0 == strcmp(str, "vrgb")) {
return KXftConfig::SubPixel::Vrgb;
} else if (0 == strcmp(str, "vbgr")) {
return KXftConfig::SubPixel::Vbgr;
} else if (0 == strcmp(str, "none")) {
return KXftConfig::SubPixel::None;
} else {
return KXftConfig::SubPixel::NotSet;
}
}
static KXftConfig::Hint::Style strToStyle(const char *str)
{
if (0 == strcmp(str, "hintslight")) {
return KXftConfig::Hint::Slight;
} else if (0 == strcmp(str, "hintmedium")) {
return KXftConfig::Hint::Medium;
} else if (0 == strcmp(str, "hintfull")) {
return KXftConfig::Hint::Full;
} else {
return KXftConfig::Hint::None;
}
}
KXftConfig::KXftConfig()
: m_doc("fontconfig")
, m_file(getConfigFile())
{
qDebug() << "Using fontconfig file:" << m_file;
reset();
}
KXftConfig::~KXftConfig()
{
}
//
// Obtain location of config file to use.
QString KXftConfig::getConfigFile()
{
FcStrList *list = FcConfigGetConfigFiles(FcConfigGetCurrent());
QStringList localFiles;
FcChar8 *file;
QString home(dirSyntax(QDir::homePath()));
m_globalFiles.clear();
while ((file = FcStrListNext(list))) {
QString f((const char *)file);
if (fExists(f) && 0 == f.indexOf(home)) {
localFiles.append(f);
} else {
m_globalFiles.append(f);
}
}
FcStrListDone(list);
//
// Go through list of localFiles, looking for the preferred one...
if (!localFiles.isEmpty()) {
for (const QString &file : qAsConst(localFiles)) {
if (file.endsWith(QLatin1String("/fonts.conf")) || file.endsWith(QLatin1String("/.fonts.conf"))) {
return file;
}
}
return localFiles.front(); // Just return the 1st one...
} else { // Hmmm... no known localFiles?
if (FcGetVersion() >= 21000) {
const QString targetPath(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + QLatin1String("fontconfig"));
QDir target(targetPath);
if (!target.exists()) {
target.mkpath(targetPath);
}
return targetPath + QLatin1String("/fonts.conf");
} else {
return home + QLatin1String("/.fonts.conf");
}
}
}
bool KXftConfig::reset()
{
m_madeChanges = false;
m_hint.reset();
m_hinting.reset();
m_excludeRange.reset();
m_excludePixelRange.reset();
m_subPixel.reset();
m_antiAliasing.reset();
m_antiAliasingHasLocalConfig = false;
m_subPixelHasLocalConfig = false;
m_hintHasLocalConfig = false;
bool ok = false;
std::for_each(m_globalFiles.cbegin(), m_globalFiles.cend(), [this, &ok](const QString &file) {
ok |= parseConfigFile(file);
});
AntiAliasing globalAntialiasing;
globalAntialiasing.state = m_antiAliasing.state;
SubPixel globalSubPixel;
globalSubPixel.type = m_subPixel.type;
Hint globalHint;
globalHint.style = m_hint.style;
Exclude globalExcludeRange;
globalExcludeRange.from = m_excludeRange.from;
globalExcludeRange.to = m_excludePixelRange.to;
Exclude globalExcludePixelRange;
globalExcludePixelRange.from = m_excludePixelRange.from;
globalExcludePixelRange.to = m_excludePixelRange.to;
Hinting globalHinting;
globalHinting.set = m_hinting.set;
m_antiAliasing.reset();
m_subPixel.reset();
m_hint.reset();
m_hinting.reset();
m_excludeRange.reset();
m_excludePixelRange.reset();
ok |= parseConfigFile(m_file);
if (m_antiAliasing.node.isNull()) {
m_antiAliasing = globalAntialiasing;
} else {
m_antiAliasingHasLocalConfig = true;
}
if (m_subPixel.node.isNull()) {
m_subPixel = globalSubPixel;
} else {
m_subPixelHasLocalConfig = true;
}
if (m_hint.node.isNull()) {
m_hint = globalHint;
} else {
m_hintHasLocalConfig = true;
}
if (m_hinting.node.isNull()) {
m_hinting = globalHinting;
}
if (m_excludeRange.node.isNull()) {
m_excludeRange = globalExcludeRange;
}
if (m_excludePixelRange.node.isNull()) {
m_excludePixelRange = globalExcludePixelRange;
}
return ok;
}
bool KXftConfig::apply()
{
bool ok = true;
if (m_madeChanges) {
//
// Check if file has been written since we last read it. If it has, then re-read and add any
// of our changes...
if (fExists(m_file) && getTimeStamp(m_file) != m_time) {
KXftConfig newConfig;
newConfig.setExcludeRange(m_excludeRange.from, m_excludeRange.to);
newConfig.setSubPixelType(m_subPixel.type);
newConfig.setHintStyle(m_hint.style);
newConfig.setAntiAliasing(m_antiAliasing.state);
ok = newConfig.changed() ? newConfig.apply() : true;
if (ok) {
reset();
} else {
m_time = getTimeStamp(m_file);
}
} else {
// Ensure these are always equal...
m_excludePixelRange.from = (int)point2Pixel(m_excludeRange.from);
m_excludePixelRange.to = (int)point2Pixel(m_excludeRange.to);
FcAtomic *atomic = FcAtomicCreate((const unsigned char *)(QFile::encodeName(m_file).data()));
ok = false;
if (atomic) {
if (FcAtomicLock(atomic)) {
FILE *f = fopen((char *)FcAtomicNewFile(atomic), "w");
if (f) {
applySubPixelType();
applyHintStyle();
applyAntiAliasing();
applyExcludeRange(false);
applyExcludeRange(true);
//
// Check document syntax...
static const char qtXmlHeader[] = "<?xml version = '1.0'?>";
static const char xmlHeader[] = "<?xml version=\"1.0\"?>";
static const char qtDocTypeLine[] = "<!DOCTYPE fontconfig>";
static const char docTypeLine[] =
"<!DOCTYPE fontconfig SYSTEM "
"\"fonts.dtd\">";
QString str(m_doc.toString());
int idx;
if (0 != str.indexOf("<?xml")) {
str.insert(0, xmlHeader);
} else if (0 == str.indexOf(qtXmlHeader)) {
str.replace(0, strlen(qtXmlHeader), xmlHeader);
}
if (-1 != (idx = str.indexOf(qtDocTypeLine))) {
str.replace(idx, strlen(qtDocTypeLine), docTypeLine);
}
//
// Write to file...
fputs(str.toUtf8(), f);
fclose(f);
if (FcAtomicReplaceOrig(atomic)) {
ok = true;
reset(); // Re-read contents..
} else {
FcAtomicDeleteNew(atomic);
}
}
FcAtomicUnlock(atomic);
}
FcAtomicDestroy(atomic);
}
}
}
return ok;
}
bool KXftConfig::subPixelTypeHasLocalConfig() const
{
return m_subPixelHasLocalConfig;
}
bool KXftConfig::getSubPixelType(SubPixel::Type &type)
{
type = m_subPixel.type;
return SubPixel::None != m_subPixel.type;
}
void KXftConfig::setSubPixelType(SubPixel::Type type)
{
if (type != m_subPixel.type) {
m_subPixel.type = type;
m_madeChanges = true;
}
}
bool KXftConfig::hintStyleHasLocalConfig() const
{
return m_hintHasLocalConfig;
}
bool KXftConfig::getHintStyle(Hint::Style &style)
{
if (Hint::NotSet != m_hint.style && !m_hint.toBeRemoved) {
style = m_hint.style;
return true;
} else {
return false;
}
}
void KXftConfig::setHintStyle(Hint::Style style)
{
if ((Hint::NotSet == style && Hint::NotSet != m_hint.style && !m_hint.toBeRemoved)
|| (Hint::NotSet != style && (style != m_hint.style || m_hint.toBeRemoved))) {
m_hint.toBeRemoved = (Hint::NotSet == style);
m_hint.style = style;
m_madeChanges = true;
}
if (Hint::NotSet != style) {
setHinting(Hint::None != m_hint.style);
}
}
void KXftConfig::setHinting(bool set)
{
if (set != m_hinting.set) {
m_hinting.set = set;
m_madeChanges = true;
}
}
bool KXftConfig::getExcludeRange(double &from, double &to)
{
if (!equal(0, m_excludeRange.from) || !equal(0, m_excludeRange.to)) {
from = m_excludeRange.from;
to = m_excludeRange.to;
return true;
} else {
return false;
}
}
void KXftConfig::setExcludeRange(double from, double to)
{
double f = from < to ? from : to, t = from < to ? to : from;
if (!equal(f, m_excludeRange.from) || !equal(t, m_excludeRange.to)) {
m_excludeRange.from = f;
m_excludeRange.to = t;
m_madeChanges = true;
}
}
QString KXftConfig::description(SubPixel::Type t)
{
switch (t) {
default:
case SubPixel::NotSet:
return i18nc("use system subpixel setting", "Vendor default");
case SubPixel::None:
return i18nc("no subpixel rendering", "None");
case SubPixel::Rgb:
return i18n("RGB");
case SubPixel::Bgr:
return i18n("BGR");
case SubPixel::Vrgb:
return i18n("Vertical RGB");
case SubPixel::Vbgr:
return i18n("Vertical BGR");
}
}
const char *KXftConfig::toStr(SubPixel::Type t)
{
switch (t) {
default:
case SubPixel::NotSet:
return "";
case SubPixel::None:
return "none";
case SubPixel::Rgb:
return "rgb";
case SubPixel::Bgr:
return "bgr";
case SubPixel::Vrgb:
return "vrgb";
case SubPixel::Vbgr:
return "vbgr";
}
}
QString KXftConfig::description(Hint::Style s)
{
switch (s) {
default:
case Hint::NotSet:
return i18nc("use system hinting settings", "Vendor default");
case Hint::Medium:
return i18nc("medium hinting", "Medium");
case Hint::None:
return i18nc("no hinting", "None");
case Hint::Slight:
return i18nc("slight hinting", "Slight");
case Hint::Full:
return i18nc("full hinting", "Full");
}
}
const char *KXftConfig::toStr(Hint::Style s)
{
switch (s) {
default:
case Hint::NotSet:
return "";
case Hint::Medium:
return "hintmedium";
case Hint::None:
return "hintnone";
case Hint::Slight:
return "hintslight";
case Hint::Full:
return "hintfull";
}
}
bool KXftConfig::parseConfigFile(const QString &filename)
{
bool ok = false;
QFile f(filename);
if (f.open(QIODevice::ReadOnly)) {
m_time = getTimeStamp(filename);
ok = true;
m_doc.clear();
if (m_doc.setContent(&f)) {
readContents();
}
f.close();
} else {
ok = !fExists(filename) && dWritable(getDir(filename));
}
if (m_doc.documentElement().isNull()) {
m_doc.appendChild(m_doc.createElement("fontconfig"));
}
if (ok) {
//
// Check exclude range values - i.e. size and pixel size...
// If "size" range is set, ensure "pixelsize" matches...
if (!equal(0, m_excludeRange.from) || !equal(0, m_excludeRange.to)) {
double pFrom = (double)point2Pixel(m_excludeRange.from), pTo = (double)point2Pixel(m_excludeRange.to);
if (!equal(pFrom, m_excludePixelRange.from) || !equal(pTo, m_excludePixelRange.to)) {
m_excludePixelRange.from = pFrom;
m_excludePixelRange.to = pTo;
m_madeChanges = true;
}
} else if (!equal(0, m_excludePixelRange.from) || !equal(0, m_excludePixelRange.to)) {
// "pixelsize" set, but not "size" !!!
m_excludeRange.from = (int)pixel2Point(m_excludePixelRange.from);
m_excludeRange.to = (int)pixel2Point(m_excludePixelRange.to);
m_madeChanges = true;
}
}
return ok;
}
void KXftConfig::readContents()
{
QDomNode n = m_doc.documentElement().firstChild();
while (!n.isNull()) {
QDomElement e = n.toElement();
if (!e.isNull()) {
if ("match" == e.tagName()) {
QString str;
int childNodesCount = e.childNodes().count();
QDomNode en = e.firstChild();
while (!en.isNull()) {
if (en.isComment()) {
childNodesCount--;
}
en = en.nextSibling();
}
switch (childNodesCount) {
case 1:
if ("font" == e.attribute("target") || "pattern" == e.attribute("target")) {
QDomNode en = e.firstChild();
while (!en.isNull()) {
QDomElement ene = en.toElement();
while (ene.isComment()) {
ene = ene.nextSiblingElement();
}
if (!ene.isNull() && "edit" == ene.tagName()) {
if (!(str = getEntry(ene, "const", 2, "name", "rgba", "mode", "assign")).isNull()
|| (m_subPixel.type == SubPixel::NotSet && !(str = getEntry(ene, "const", 2, "name", "rgba", "mode", "append")).isNull())) {
m_subPixel.node = n;
m_subPixel.type = strToType(str.toLatin1());
} else if (!(str = getEntry(ene, "const", 2, "name", "hintstyle", "mode", "assign")).isNull()
|| (m_hint.style == Hint::NotSet
&& !(str = getEntry(ene, "const", 2, "name", "hintstyle", "mode", "append")).isNull())) {
m_hint.node = n;
m_hint.style = strToStyle(str.toLatin1());
} else if (!(str = getEntry(ene, "bool", 2, "name", "hinting", "mode", "assign")).isNull()) {
m_hinting.node = n;
m_hinting.set = str.toLower() != "false";
} else if (!(str = getEntry(ene, "bool", 2, "name", "antialias", "mode", "assign")).isNull()
|| (m_antiAliasing.state == AntiAliasing::NotSet
&& !(str = getEntry(ene, "bool", 2, "name", "antialias", "mode", "append")).isNull())) {
m_antiAliasing.node = n;
m_antiAliasing.state = str.toLower() != "false" ? AntiAliasing::Enabled : AntiAliasing::Disabled;
}
}
en = en.nextSibling();
}
}
break;
case 3: // CPD: Is target "font" or "pattern" ????
if ("font" == e.attribute("target")) {
bool foundFalse = false;
QDomNode en = e.firstChild();
while (en.isComment()) {
en = en.nextSibling();
}
double from = -1.0, to = -1.0, pixelFrom = -1.0, pixelTo = -1.0;
while (!en.isNull()) {
QDomElement ene = en.toElement();
if (!ene.isNull()) {
if ("test" == ene.tagName()) {
// kcmfonts used to write incorrectly more or less instead of
// more_eq and less_eq, so read both,
// first the old (wrong) one then the right one
if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "size", "compare", "more")).isNull()) {
from = str.toDouble();
}
if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "size", "compare", "more_eq")).isNull()) {
from = str.toDouble();
}
if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "size", "compare", "less")).isNull()) {
to = str.toDouble();
}
if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "size", "compare", "less_eq")).isNull()) {
to = str.toDouble();
}
if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "pixelsize", "compare", "more")).isNull()) {
pixelFrom = str.toDouble();
}
if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "pixelsize", "compare", "more_eq")).isNull()) {
pixelFrom = str.toDouble();
}
if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "pixelsize", "compare", "less")).isNull()) {
pixelTo = str.toDouble();
}
if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "pixelsize", "compare", "less_eq")).isNull()) {
pixelTo = str.toDouble();
}
} else if ("edit" == ene.tagName() && "false" == getEntry(ene, "bool", 2, "name", "antialias", "mode", "assign")) {
foundFalse = true;
}
}
en = en.nextSibling();
}
if ((from >= 0 || to >= 0) && foundFalse) {
m_excludeRange.from = from < to ? from : to;
m_excludeRange.to = from < to ? to : from;
m_excludeRange.node = n;
} else if ((pixelFrom >= 0 || pixelTo >= 0) && foundFalse) {
m_excludePixelRange.from = pixelFrom < pixelTo ? pixelFrom : pixelTo;
m_excludePixelRange.to = pixelFrom < pixelTo ? pixelTo : pixelFrom;
m_excludePixelRange.node = n;
}
}
break;
default:
break;
}
}
}
n = n.nextSibling();
}
}
void KXftConfig::applySubPixelType()
{
if (SubPixel::NotSet == m_subPixel.type) {
if (!m_subPixel.node.isNull()) {
m_doc.documentElement().removeChild(m_subPixel.node);
m_subPixel.node.clear();
}
} else {
QDomElement matchNode = m_doc.createElement("match");
QDomElement typeNode = m_doc.createElement("const");
QDomElement editNode = m_doc.createElement("edit");
QDomText typeText = m_doc.createTextNode(toStr(m_subPixel.type));
matchNode.setAttribute("target", "font");
editNode.setAttribute("mode", "assign");
editNode.setAttribute("name", "rgba");
editNode.appendChild(typeNode);
typeNode.appendChild(typeText);
matchNode.appendChild(editNode);
if (m_subPixel.node.isNull()) {
m_doc.documentElement().appendChild(matchNode);
} else {
m_doc.documentElement().replaceChild(matchNode, m_subPixel.node);
}
m_subPixel.node = matchNode;
}
}
void KXftConfig::applyHintStyle()
{
applyHinting();
if (Hint::NotSet == m_hint.style) {
if (!m_hint.node.isNull()) {
m_doc.documentElement().removeChild(m_hint.node);
m_hint.node.clear();
}
if (!m_hinting.node.isNull()) {
m_doc.documentElement().removeChild(m_hinting.node);
m_hinting.node.clear();
}
} else {
QDomElement matchNode = m_doc.createElement("match"), typeNode = m_doc.createElement("const"), editNode = m_doc.createElement("edit");
QDomText typeText = m_doc.createTextNode(toStr(m_hint.style));
matchNode.setAttribute("target", "font");
editNode.setAttribute("mode", "assign");
editNode.setAttribute("name", "hintstyle");
editNode.appendChild(typeNode);
typeNode.appendChild(typeText);
matchNode.appendChild(editNode);
if (m_hint.node.isNull()) {
m_doc.documentElement().appendChild(matchNode);
} else {
m_doc.documentElement().replaceChild(matchNode, m_hint.node);
}
m_hint.node = matchNode;
}
}
void KXftConfig::applyHinting()
{
QDomElement matchNode = m_doc.createElement("match"), typeNode = m_doc.createElement("bool"), editNode = m_doc.createElement("edit");
QDomText typeText = m_doc.createTextNode(m_hinting.set ? "true" : "false");
matchNode.setAttribute("target", "font");
editNode.setAttribute("mode", "assign");
editNode.setAttribute("name", "hinting");
editNode.appendChild(typeNode);
typeNode.appendChild(typeText);
matchNode.appendChild(editNode);
if (m_hinting.node.isNull()) {
m_doc.documentElement().appendChild(matchNode);
} else {
m_doc.documentElement().replaceChild(matchNode, m_hinting.node);
}
m_hinting.node = matchNode;
}
void KXftConfig::applyExcludeRange(bool pixel)
{
Exclude &range = pixel ? m_excludePixelRange : m_excludeRange;
if (equal(range.from, 0) && equal(range.to, 0)) {
if (!range.node.isNull()) {
m_doc.documentElement().removeChild(range.node);
range.node.clear();
}
} else {
QString fromString, toString;
fromString.setNum(range.from);
toString.setNum(range.to);
QDomElement matchNode = m_doc.createElement("match"), fromTestNode = m_doc.createElement("test"), fromNode = m_doc.createElement("double"),
toTestNode = m_doc.createElement("test"), toNode = m_doc.createElement("double"), editNode = m_doc.createElement("edit"),
boolNode = m_doc.createElement("bool");
QDomText fromText = m_doc.createTextNode(fromString), toText = m_doc.createTextNode(toString), boolText = m_doc.createTextNode("false");
matchNode.setAttribute("target", "font"); // CPD: Is target "font" or "pattern" ????
fromTestNode.setAttribute("qual", "any");
fromTestNode.setAttribute("name", pixel ? "pixelsize" : "size");
fromTestNode.setAttribute("compare", "more_eq");
fromTestNode.appendChild(fromNode);
fromNode.appendChild(fromText);
toTestNode.setAttribute("qual", "any");
toTestNode.setAttribute("name", pixel ? "pixelsize" : "size");
toTestNode.setAttribute("compare", "less_eq");
toTestNode.appendChild(toNode);
toNode.appendChild(toText);
editNode.setAttribute("mode", "assign");
editNode.setAttribute("name", "antialias");
editNode.appendChild(boolNode);
boolNode.appendChild(boolText);
matchNode.appendChild(fromTestNode);
matchNode.appendChild(toTestNode);
matchNode.appendChild(editNode);
if (!m_antiAliasing.node.isNull()) {
m_doc.documentElement().removeChild(range.node);
}
if (range.node.isNull()) {
m_doc.documentElement().appendChild(matchNode);
} else {
m_doc.documentElement().replaceChild(matchNode, range.node);
}
range.node = matchNode;
}
}
bool KXftConfig::antiAliasingHasLocalConfig() const
{
return m_antiAliasingHasLocalConfig;
}
KXftConfig::AntiAliasing::State KXftConfig::getAntiAliasing() const
{
return m_antiAliasing.state;
}
void KXftConfig::setAntiAliasing(AntiAliasing::State state)
{
if (state != m_antiAliasing.state) {
m_antiAliasing.state = state;
m_madeChanges = true;
}
}
void KXftConfig::applyAntiAliasing()
{
if (AntiAliasing::NotSet == m_antiAliasing.state) {
if (!m_antiAliasing.node.isNull()) {
m_doc.documentElement().removeChild(m_antiAliasing.node);
m_antiAliasing.node.clear();
}
} else {
QDomElement matchNode = m_doc.createElement("match");
QDomElement typeNode = m_doc.createElement("bool");
QDomElement editNode = m_doc.createElement("edit");
QDomText typeText = m_doc.createTextNode(m_antiAliasing.state == AntiAliasing::Enabled ? "true" : "false");
matchNode.setAttribute("target", "font");
editNode.setAttribute("mode", "assign");
editNode.setAttribute("name", "antialias");
editNode.appendChild(typeNode);
typeNode.appendChild(typeText);
matchNode.appendChild(editNode);
if (!m_antiAliasing.node.isNull()) {
m_doc.documentElement().removeChild(m_antiAliasing.node);
}
m_doc.documentElement().appendChild(matchNode);
m_antiAliasing.node = matchNode;
}
}
// KXftConfig only parses one config file, user's .fonts.conf usually.
// If that one doesn't exist, then KXftConfig doesn't know if antialiasing
// is enabled or not. So try to find out the default value from the default font.
// Maybe there's a better way *shrug*.
bool KXftConfig::aliasingEnabled()
{
FcPattern *pattern = FcPatternCreate();
FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcResult result;
FcPattern *f = FcFontMatch(nullptr, pattern, &result);
FcBool antialiased = FcTrue;
FcPatternGetBool(f, FC_ANTIALIAS, 0, &antialiased);
FcPatternDestroy(f);
FcPatternDestroy(pattern);
return antialiased == FcTrue;
}
#endif