Warning, file /plasma/plasma-workspace/kcms/fonts/kxftconfig.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2002 Craig Drummond <craig@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "kxftconfig.h"
0008 #ifdef HAVE_FONTCONFIG
0009 
0010 #include <ctype.h>
0011 #include <math.h>
0012 #include <stdarg.h>
0013 #include <stdio.h>
0014 #include <stdlib.h>
0015 #include <string.h>
0016 #include <sys/stat.h>
0017 
0018 #include <QByteArray>
0019 #include <QDebug>
0020 #include <QDir>
0021 #include <QFile>
0022 #include <QFileInfo>
0023 #include <QStandardPaths>
0024 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0025 #include <private/qtx11extras_p.h>
0026 #else
0027 #include <QX11Info>
0028 #endif
0029 
0030 #include <KLocalizedString>
0031 
0032 #include <fontconfig/fontconfig.h>
0033 
0034 using namespace std;
0035 
0036 static int point2Pixel(double point)
0037 {
0038     return (int)(((point * QX11Info::appDpiY()) / 72.0) + 0.5);
0039 }
0040 
0041 static int pixel2Point(double pixel)
0042 {
0043     return (int)(((pixel * 72.0) / (double)QX11Info::appDpiY()) + 0.5);
0044 }
0045 
0046 static bool equal(double d1, double d2)
0047 {
0048     return (fabs(d1 - d2) < 0.0001);
0049 }
0050 
0051 static QString dirSyntax(const QString &d)
0052 {
0053     if (d.isNull()) {
0054         return d;
0055     }
0056 
0057     QString ds(d);
0058     ds.replace(QLatin1String("//"), QLatin1String("/"));
0059     if (!ds.endsWith(QLatin1Char('/'))) {
0060         ds += QLatin1Char('/');
0061     }
0062 
0063     return ds;
0064 }
0065 
0066 inline bool fExists(const QString &p)
0067 {
0068     return QFileInfo(p).isFile();
0069 }
0070 
0071 inline bool dWritable(const QString &p)
0072 {
0073     QFileInfo info(p);
0074     return info.isDir() && info.isWritable();
0075 }
0076 
0077 static QString getDir(const QString &path)
0078 {
0079     QString str(path);
0080 
0081     const int slashPos = str.lastIndexOf(QLatin1Char('/'));
0082     if (slashPos != -1) {
0083         str.truncate(slashPos + 1);
0084     }
0085 
0086     return dirSyntax(str);
0087 }
0088 
0089 static QDateTime getTimeStamp(const QString &item)
0090 {
0091     return QFileInfo(item).lastModified();
0092 }
0093 
0094 static QString getEntry(QDomElement element, const char *type, unsigned int numAttributes, ...)
0095 {
0096     if (numAttributes == uint(element.attributes().length())) {
0097         va_list args;
0098         unsigned int arg;
0099         bool ok = true;
0100 
0101         va_start(args, numAttributes);
0102 
0103         for (arg = 0; arg < numAttributes && ok; ++arg) {
0104             const char *attr = va_arg(args, const char *);
0105             const char *val = va_arg(args, const char *);
0106 
0107             if (!attr || !val || val != element.attribute(attr)) {
0108                 ok = false;
0109             }
0110         }
0111 
0112         va_end(args);
0113 
0114         if (ok) {
0115             QDomNode n = element.firstChild();
0116 
0117             if (!n.isNull()) {
0118                 QDomElement e = n.toElement();
0119 
0120                 if (!e.isNull() && type == e.tagName()) {
0121                     return e.text();
0122                 }
0123             }
0124         }
0125     }
0126 
0127     return QString();
0128 }
0129 
0130 static KXftConfig::SubPixel::Type strToType(const char *str)
0131 {
0132     if (0 == strcmp(str, "rgb")) {
0133         return KXftConfig::SubPixel::Rgb;
0134     } else if (0 == strcmp(str, "bgr")) {
0135         return KXftConfig::SubPixel::Bgr;
0136     } else if (0 == strcmp(str, "vrgb")) {
0137         return KXftConfig::SubPixel::Vrgb;
0138     } else if (0 == strcmp(str, "vbgr")) {
0139         return KXftConfig::SubPixel::Vbgr;
0140     } else if (0 == strcmp(str, "none")) {
0141         return KXftConfig::SubPixel::None;
0142     } else {
0143         return KXftConfig::SubPixel::NotSet;
0144     }
0145 }
0146 
0147 static KXftConfig::Hint::Style strToStyle(const char *str)
0148 {
0149     if (0 == strcmp(str, "hintslight")) {
0150         return KXftConfig::Hint::Slight;
0151     } else if (0 == strcmp(str, "hintmedium")) {
0152         return KXftConfig::Hint::Medium;
0153     } else if (0 == strcmp(str, "hintfull")) {
0154         return KXftConfig::Hint::Full;
0155     } else {
0156         return KXftConfig::Hint::None;
0157     }
0158 }
0159 
0160 KXftConfig::KXftConfig(const QString &path)
0161     : m_doc("fontconfig")
0162     , m_file(path.isEmpty() ? getConfigFile() : path)
0163 {
0164     qDebug() << "Using fontconfig file:" << m_file;
0165     reset();
0166 }
0167 
0168 KXftConfig::~KXftConfig()
0169 {
0170 }
0171 
0172 //
0173 // Obtain location of config file to use.
0174 QString KXftConfig::getConfigFile()
0175 {
0176     FcStrList *list = FcConfigGetConfigFiles(FcConfigGetCurrent());
0177     QStringList localFiles;
0178     FcChar8 *file;
0179     QString home(dirSyntax(QDir::homePath()));
0180 
0181     m_globalFiles.clear();
0182 
0183     while ((file = FcStrListNext(list))) {
0184         QString f((const char *)file);
0185 
0186         if (fExists(f) && 0 == f.indexOf(home)) {
0187             localFiles.append(f);
0188         } else {
0189             m_globalFiles.append(f);
0190         }
0191     }
0192     FcStrListDone(list);
0193 
0194     //
0195     // Go through list of localFiles, looking for the preferred one...
0196     if (!localFiles.isEmpty()) {
0197         for (const QString &file : qAsConst(localFiles)) {
0198             if (file.endsWith(QLatin1String("/fonts.conf")) || file.endsWith(QLatin1String("/.fonts.conf"))) {
0199                 return file;
0200             }
0201         }
0202         return localFiles.front(); // Just return the 1st one...
0203     } else { // Hmmm... no known localFiles?
0204         if (FcGetVersion() >= 21000) {
0205             const QString targetPath(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + QLatin1String("fontconfig"));
0206             QDir target(targetPath);
0207             if (!target.exists()) {
0208                 target.mkpath(targetPath);
0209             }
0210             return targetPath + QLatin1String("/fonts.conf");
0211         } else {
0212             return home + QLatin1String("/.fonts.conf");
0213         }
0214     }
0215 }
0216 
0217 bool KXftConfig::reset()
0218 {
0219     m_madeChanges = false;
0220     m_hint.reset();
0221     m_hinting.reset();
0222     m_excludeRange.reset();
0223     m_excludePixelRange.reset();
0224     m_subPixel.reset();
0225     m_antiAliasing.reset();
0226     m_antiAliasingHasLocalConfig = false;
0227     m_subPixelHasLocalConfig = false;
0228     m_hintHasLocalConfig = false;
0229 
0230     bool ok = false;
0231     std::for_each(m_globalFiles.cbegin(), m_globalFiles.cend(), [this, &ok](const QString &file) {
0232         ok |= parseConfigFile(file);
0233     });
0234 
0235     AntiAliasing globalAntialiasing;
0236     globalAntialiasing.state = m_antiAliasing.state;
0237     SubPixel globalSubPixel;
0238     globalSubPixel.type = m_subPixel.type;
0239     Hint globalHint;
0240     globalHint.style = m_hint.style;
0241     Exclude globalExcludeRange;
0242     globalExcludeRange.from = m_excludeRange.from;
0243     globalExcludeRange.to = m_excludePixelRange.to;
0244     Exclude globalExcludePixelRange;
0245     globalExcludePixelRange.from = m_excludePixelRange.from;
0246     globalExcludePixelRange.to = m_excludePixelRange.to;
0247     Hinting globalHinting;
0248     globalHinting.set = m_hinting.set;
0249 
0250     m_antiAliasing.reset();
0251     m_subPixel.reset();
0252     m_hint.reset();
0253     m_hinting.reset();
0254     m_excludeRange.reset();
0255     m_excludePixelRange.reset();
0256 
0257     ok |= parseConfigFile(m_file);
0258 
0259     if (m_antiAliasing.node.isNull()) {
0260         m_antiAliasing = globalAntialiasing;
0261     } else {
0262         m_antiAliasingHasLocalConfig = true;
0263     }
0264 
0265     if (m_subPixel.node.isNull()) {
0266         m_subPixel = globalSubPixel;
0267     } else {
0268         m_subPixelHasLocalConfig = true;
0269     }
0270 
0271     if (m_hint.node.isNull()) {
0272         m_hint = globalHint;
0273     } else {
0274         m_hintHasLocalConfig = true;
0275     }
0276 
0277     if (m_hinting.node.isNull()) {
0278         m_hinting = globalHinting;
0279     }
0280     if (m_excludeRange.node.isNull()) {
0281         m_excludeRange = globalExcludeRange;
0282     }
0283     if (m_excludePixelRange.node.isNull()) {
0284         m_excludePixelRange = globalExcludePixelRange;
0285     }
0286 
0287     return ok;
0288 }
0289 
0290 bool KXftConfig::apply()
0291 {
0292     bool ok = true;
0293 
0294     if (m_madeChanges) {
0295         //
0296         // Check if file has been written since we last read it. If it has, then re-read and add any
0297         // of our changes...
0298         if (fExists(m_file) && getTimeStamp(m_file) != m_time) {
0299             KXftConfig newConfig;
0300 
0301             newConfig.setExcludeRange(m_excludeRange.from, m_excludeRange.to);
0302             newConfig.setSubPixelType(m_subPixel.type);
0303             newConfig.setHintStyle(m_hint.style);
0304             newConfig.setAntiAliasing(m_antiAliasing.state);
0305 
0306             ok = newConfig.changed() ? newConfig.apply() : true;
0307             if (ok) {
0308                 reset();
0309             } else {
0310                 m_time = getTimeStamp(m_file);
0311             }
0312         } else {
0313             // Ensure these are always equal...
0314             m_excludePixelRange.from = (int)point2Pixel(m_excludeRange.from);
0315             m_excludePixelRange.to = (int)point2Pixel(m_excludeRange.to);
0316 
0317             FcAtomic *atomic = FcAtomicCreate((const unsigned char *)(QFile::encodeName(m_file).data()));
0318 
0319             ok = false;
0320             if (atomic) {
0321                 if (FcAtomicLock(atomic)) {
0322                     FILE *f = fopen((char *)FcAtomicNewFile(atomic), "w");
0323 
0324                     if (f) {
0325                         applySubPixelType();
0326                         applyHintStyle();
0327                         applyAntiAliasing();
0328                         applyExcludeRange(false);
0329                         applyExcludeRange(true);
0330 
0331                         //
0332                         // Check document syntax...
0333                         static const char qtXmlHeader[] = "<?xml version = '1.0'?>";
0334                         static const char xmlHeader[] = "<?xml version=\"1.0\"?>";
0335                         static const char qtDocTypeLine[] = "<!DOCTYPE fontconfig>";
0336                         static const char docTypeLine[] =
0337                             "<!DOCTYPE fontconfig SYSTEM "
0338                             "\"fonts.dtd\">";
0339 
0340                         QString str(m_doc.toString());
0341                         int idx;
0342 
0343                         if (0 != str.indexOf("<?xml")) {
0344                             str.insert(0, xmlHeader);
0345                         } else if (0 == str.indexOf(qtXmlHeader)) {
0346                             str.replace(0, strlen(qtXmlHeader), xmlHeader);
0347                         }
0348 
0349                         if (-1 != (idx = str.indexOf(qtDocTypeLine))) {
0350                             str.replace(idx, strlen(qtDocTypeLine), docTypeLine);
0351                         }
0352 
0353                         //
0354                         // Write to file...
0355                         fputs(str.toUtf8(), f);
0356                         fclose(f);
0357 
0358                         if (FcAtomicReplaceOrig(atomic)) {
0359                             ok = true;
0360                             reset(); // Re-read contents..
0361                         } else {
0362                             FcAtomicDeleteNew(atomic);
0363                         }
0364                     }
0365                     FcAtomicUnlock(atomic);
0366                 }
0367                 FcAtomicDestroy(atomic);
0368             }
0369         }
0370     }
0371 
0372     return ok;
0373 }
0374 
0375 bool KXftConfig::subPixelTypeHasLocalConfig() const
0376 {
0377     return m_subPixelHasLocalConfig;
0378 }
0379 
0380 bool KXftConfig::getSubPixelType(SubPixel::Type &type)
0381 {
0382     type = m_subPixel.type;
0383     return SubPixel::None != m_subPixel.type;
0384 }
0385 
0386 void KXftConfig::setSubPixelType(SubPixel::Type type)
0387 {
0388     if (type != m_subPixel.type) {
0389         m_subPixel.type = type;
0390         m_madeChanges = true;
0391     }
0392 }
0393 
0394 bool KXftConfig::hintStyleHasLocalConfig() const
0395 {
0396     return m_hintHasLocalConfig;
0397 }
0398 
0399 bool KXftConfig::getHintStyle(Hint::Style &style)
0400 {
0401     if (Hint::NotSet != m_hint.style && !m_hint.toBeRemoved) {
0402         style = m_hint.style;
0403         return true;
0404     } else {
0405         return false;
0406     }
0407 }
0408 
0409 void KXftConfig::setHintStyle(Hint::Style style)
0410 {
0411     if ((Hint::NotSet == style && Hint::NotSet != m_hint.style && !m_hint.toBeRemoved)
0412         || (Hint::NotSet != style && (style != m_hint.style || m_hint.toBeRemoved))) {
0413         m_hint.toBeRemoved = (Hint::NotSet == style);
0414         m_hint.style = style;
0415         m_madeChanges = true;
0416     }
0417 
0418     if (Hint::NotSet != style) {
0419         setHinting(Hint::None != m_hint.style);
0420     }
0421 }
0422 
0423 void KXftConfig::setHinting(bool set)
0424 {
0425     if (set != m_hinting.set) {
0426         m_hinting.set = set;
0427         m_madeChanges = true;
0428     }
0429 }
0430 
0431 bool KXftConfig::getExcludeRange(double &from, double &to)
0432 {
0433     if (!equal(0, m_excludeRange.from) || !equal(0, m_excludeRange.to)) {
0434         from = m_excludeRange.from;
0435         to = m_excludeRange.to;
0436         return true;
0437     } else {
0438         return false;
0439     }
0440 }
0441 
0442 void KXftConfig::setExcludeRange(double from, double to)
0443 {
0444     double f = from < to ? from : to, t = from < to ? to : from;
0445 
0446     if (!equal(f, m_excludeRange.from) || !equal(t, m_excludeRange.to)) {
0447         m_excludeRange.from = f;
0448         m_excludeRange.to = t;
0449         m_madeChanges = true;
0450     }
0451 }
0452 
0453 QString KXftConfig::description(SubPixel::Type t)
0454 {
0455     switch (t) {
0456     default:
0457     case SubPixel::NotSet:
0458         return i18nc("use system subpixel setting", "Vendor default");
0459     case SubPixel::None:
0460         return i18nc("no subpixel rendering", "None");
0461     case SubPixel::Rgb:
0462         return i18n("RGB");
0463     case SubPixel::Bgr:
0464         return i18n("BGR");
0465     case SubPixel::Vrgb:
0466         return i18n("Vertical RGB");
0467     case SubPixel::Vbgr:
0468         return i18n("Vertical BGR");
0469     }
0470 }
0471 
0472 const char *KXftConfig::toStr(SubPixel::Type t)
0473 {
0474     switch (t) {
0475     default:
0476     case SubPixel::NotSet:
0477         return "";
0478     case SubPixel::None:
0479         return "none";
0480     case SubPixel::Rgb:
0481         return "rgb";
0482     case SubPixel::Bgr:
0483         return "bgr";
0484     case SubPixel::Vrgb:
0485         return "vrgb";
0486     case SubPixel::Vbgr:
0487         return "vbgr";
0488     }
0489 }
0490 
0491 QString KXftConfig::description(Hint::Style s)
0492 {
0493     switch (s) {
0494     default:
0495     case Hint::NotSet:
0496         return i18nc("use system hinting settings", "Vendor default");
0497     case Hint::Medium:
0498         return i18nc("medium hinting", "Medium");
0499     case Hint::None:
0500         return i18nc("no hinting", "None");
0501     case Hint::Slight:
0502         return i18nc("slight hinting", "Slight");
0503     case Hint::Full:
0504         return i18nc("full hinting", "Full");
0505     }
0506 }
0507 
0508 const char *KXftConfig::toStr(Hint::Style s)
0509 {
0510     switch (s) {
0511     default:
0512     case Hint::NotSet:
0513         return "";
0514     case Hint::Medium:
0515         return "hintmedium";
0516     case Hint::None:
0517         return "hintnone";
0518     case Hint::Slight:
0519         return "hintslight";
0520     case Hint::Full:
0521         return "hintfull";
0522     }
0523 }
0524 
0525 bool KXftConfig::parseConfigFile(const QString &filename)
0526 {
0527     bool ok = false;
0528 
0529     QFile f(filename);
0530 
0531     if (f.open(QIODevice::ReadOnly)) {
0532         m_time = getTimeStamp(filename);
0533         ok = true;
0534         m_doc.clear();
0535 
0536         if (m_doc.setContent(&f)) {
0537             readContents();
0538         }
0539         f.close();
0540     } else {
0541         ok = !fExists(filename) && dWritable(getDir(filename));
0542     }
0543 
0544     if (m_doc.documentElement().isNull()) {
0545         m_doc.appendChild(m_doc.createElement("fontconfig"));
0546     }
0547 
0548     if (ok) {
0549         //
0550         // Check exclude range values - i.e. size and pixel size...
0551         // If "size" range is set, ensure "pixelsize" matches...
0552         if (!equal(0, m_excludeRange.from) || !equal(0, m_excludeRange.to)) {
0553             double pFrom = (double)point2Pixel(m_excludeRange.from), pTo = (double)point2Pixel(m_excludeRange.to);
0554 
0555             if (!equal(pFrom, m_excludePixelRange.from) || !equal(pTo, m_excludePixelRange.to)) {
0556                 m_excludePixelRange.from = pFrom;
0557                 m_excludePixelRange.to = pTo;
0558                 m_madeChanges = true;
0559             }
0560         } else if (!equal(0, m_excludePixelRange.from) || !equal(0, m_excludePixelRange.to)) {
0561             // "pixelsize" set, but not "size" !!!
0562             m_excludeRange.from = (int)pixel2Point(m_excludePixelRange.from);
0563             m_excludeRange.to = (int)pixel2Point(m_excludePixelRange.to);
0564             m_madeChanges = true;
0565         }
0566     }
0567 
0568     return ok;
0569 }
0570 
0571 void KXftConfig::readContents()
0572 {
0573     for (auto n = m_doc.documentElement().firstChild(); !n.isNull(); n = n.nextSibling()) {
0574         QDomElement e = n.toElement();
0575 
0576         if (e.isNull()) {
0577             continue;
0578         }
0579 
0580         if ("match" != e.tagName()) {
0581             continue;
0582         }
0583 
0584         int childNodesCount = e.childNodes().count();
0585         for (auto en = e.firstChild(); !en.isNull(); en = en.nextSibling()) {
0586             if (en.isComment()) {
0587                 childNodesCount--;
0588             }
0589         }
0590 
0591         QString str;
0592         if (childNodesCount == 1) {
0593             if ("font" != e.attribute("target") && "pattern" != e.attribute("target")) {
0594                 break;
0595             }
0596             for (auto en = e.firstChild(); !en.isNull(); en = en.nextSibling()) {
0597                 QDomElement ene = en.toElement();
0598                 while (ene.isComment()) {
0599                     ene = ene.nextSiblingElement();
0600                 }
0601 
0602                 if (ene.isNull() || "edit" != ene.tagName()) {
0603                     continue;
0604                 }
0605 
0606                 if (!(str = getEntry(ene, "const", 2, "name", "rgba", "mode", "assign")).isNull()
0607                     || (m_subPixel.type == SubPixel::NotSet && !(str = getEntry(ene, "const", 2, "name", "rgba", "mode", "append")).isNull())) {
0608                     m_subPixel.node = n;
0609                     m_subPixel.type = strToType(str.toLatin1());
0610                 } else if (!(str = getEntry(ene, "const", 2, "name", "hintstyle", "mode", "assign")).isNull()
0611                            || (m_hint.style == Hint::NotSet && !(str = getEntry(ene, "const", 2, "name", "hintstyle", "mode", "append")).isNull())) {
0612                     m_hint.node = n;
0613                     m_hint.style = strToStyle(str.toLatin1());
0614                 } else if (!(str = getEntry(ene, "bool", 2, "name", "hinting", "mode", "assign")).isNull()) {
0615                     m_hinting.node = n;
0616                     m_hinting.set = str.toLower() != "false";
0617                 } else if (!(str = getEntry(ene, "bool", 2, "name", "antialias", "mode", "assign")).isNull()
0618                            || (m_antiAliasing.state == AntiAliasing::NotSet
0619                                && !(str = getEntry(ene, "bool", 2, "name", "antialias", "mode", "append")).isNull())) {
0620                     m_antiAliasing.node = n;
0621                     m_antiAliasing.state = str.toLower() != "false" ? AntiAliasing::Enabled : AntiAliasing::Disabled;
0622                 }
0623             }
0624         } else if (childNodesCount == 3) { // CPD: Is target "font" or "pattern" ????
0625             if ("font" != e.attribute("target")) {
0626                 break;
0627             }
0628 
0629             bool foundFalse = false;
0630             double from = -1.0;
0631             double to = -1.0;
0632             double pixelFrom = -1.0;
0633             double pixelTo = -1.0;
0634 
0635             for (auto en = e.firstChild(); !en.isNull(); en = en.nextSibling()) {
0636                 if (en.isComment()) {
0637                     continue;
0638                 }
0639                 auto ene = en.toElement();
0640                 if ("test" == ene.tagName()) {
0641                     // kcmfonts used to write incorrectly more or less instead of
0642                     // more_eq and less_eq, so read both,
0643                     // first the old (wrong) one then the right one
0644                     if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "size", "compare", "more")).isNull()) {
0645                         from = str.toDouble();
0646                     }
0647                     if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "size", "compare", "more_eq")).isNull()) {
0648                         from = str.toDouble();
0649                     }
0650                     if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "size", "compare", "less")).isNull()) {
0651                         to = str.toDouble();
0652                     }
0653                     if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "size", "compare", "less_eq")).isNull()) {
0654                         to = str.toDouble();
0655                     }
0656                     if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "pixelsize", "compare", "more")).isNull()) {
0657                         pixelFrom = str.toDouble();
0658                     }
0659                     if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "pixelsize", "compare", "more_eq")).isNull()) {
0660                         pixelFrom = str.toDouble();
0661                     }
0662                     if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "pixelsize", "compare", "less")).isNull()) {
0663                         pixelTo = str.toDouble();
0664                     }
0665                     if (!(str = getEntry(ene, "double", 3, "qual", "any", "name", "pixelsize", "compare", "less_eq")).isNull()) {
0666                         pixelTo = str.toDouble();
0667                     }
0668                 } else if ("edit" == ene.tagName() && "false" == getEntry(ene, "bool", 2, "name", "antialias", "mode", "assign")) {
0669                     foundFalse = true;
0670                 }
0671             }
0672 
0673             if ((from >= 0 || to >= 0) && foundFalse) {
0674                 m_excludeRange.from = from < to ? from : to;
0675                 m_excludeRange.to = from < to ? to : from;
0676                 m_excludeRange.node = n;
0677             } else if ((pixelFrom >= 0 || pixelTo >= 0) && foundFalse) {
0678                 m_excludePixelRange.from = pixelFrom < pixelTo ? pixelFrom : pixelTo;
0679                 m_excludePixelRange.to = pixelFrom < pixelTo ? pixelTo : pixelFrom;
0680                 m_excludePixelRange.node = n;
0681             }
0682         }
0683     }
0684 }
0685 
0686 void KXftConfig::applySubPixelType()
0687 {
0688     if (SubPixel::NotSet == m_subPixel.type) {
0689         if (!m_subPixel.node.isNull()) {
0690             m_doc.documentElement().removeChild(m_subPixel.node);
0691             m_subPixel.node.clear();
0692         }
0693     } else {
0694         QDomElement matchNode = m_doc.createElement("match");
0695         QDomElement typeNode = m_doc.createElement("const");
0696         QDomElement editNode = m_doc.createElement("edit");
0697         QDomText typeText = m_doc.createTextNode(toStr(m_subPixel.type));
0698 
0699         matchNode.setAttribute("target", "font");
0700         editNode.setAttribute("mode", "assign");
0701         editNode.setAttribute("name", "rgba");
0702         editNode.appendChild(typeNode);
0703         typeNode.appendChild(typeText);
0704         matchNode.appendChild(editNode);
0705         if (m_subPixel.node.isNull()) {
0706             m_doc.documentElement().appendChild(matchNode);
0707         } else {
0708             m_doc.documentElement().replaceChild(matchNode, m_subPixel.node);
0709         }
0710         m_subPixel.node = matchNode;
0711     }
0712 }
0713 
0714 void KXftConfig::applyHintStyle()
0715 {
0716     applyHinting();
0717 
0718     if (Hint::NotSet == m_hint.style) {
0719         if (!m_hint.node.isNull()) {
0720             m_doc.documentElement().removeChild(m_hint.node);
0721             m_hint.node.clear();
0722         }
0723         if (!m_hinting.node.isNull()) {
0724             m_doc.documentElement().removeChild(m_hinting.node);
0725             m_hinting.node.clear();
0726         }
0727     } else {
0728         QDomElement matchNode = m_doc.createElement("match"), typeNode = m_doc.createElement("const"), editNode = m_doc.createElement("edit");
0729         QDomText typeText = m_doc.createTextNode(toStr(m_hint.style));
0730 
0731         matchNode.setAttribute("target", "font");
0732         editNode.setAttribute("mode", "assign");
0733         editNode.setAttribute("name", "hintstyle");
0734         editNode.appendChild(typeNode);
0735         typeNode.appendChild(typeText);
0736         matchNode.appendChild(editNode);
0737         if (m_hint.node.isNull()) {
0738             m_doc.documentElement().appendChild(matchNode);
0739         } else {
0740             m_doc.documentElement().replaceChild(matchNode, m_hint.node);
0741         }
0742         m_hint.node = matchNode;
0743     }
0744 }
0745 
0746 void KXftConfig::applyHinting()
0747 {
0748     QDomElement matchNode = m_doc.createElement("match"), typeNode = m_doc.createElement("bool"), editNode = m_doc.createElement("edit");
0749     QDomText typeText = m_doc.createTextNode(m_hinting.set ? "true" : "false");
0750 
0751     matchNode.setAttribute("target", "font");
0752     editNode.setAttribute("mode", "assign");
0753     editNode.setAttribute("name", "hinting");
0754     editNode.appendChild(typeNode);
0755     typeNode.appendChild(typeText);
0756     matchNode.appendChild(editNode);
0757     if (m_hinting.node.isNull()) {
0758         m_doc.documentElement().appendChild(matchNode);
0759     } else {
0760         m_doc.documentElement().replaceChild(matchNode, m_hinting.node);
0761     }
0762     m_hinting.node = matchNode;
0763 }
0764 
0765 void KXftConfig::applyExcludeRange(bool pixel)
0766 {
0767     Exclude &range = pixel ? m_excludePixelRange : m_excludeRange;
0768 
0769     if (equal(range.from, 0) && equal(range.to, 0)) {
0770         if (!range.node.isNull()) {
0771             m_doc.documentElement().removeChild(range.node);
0772             range.node.clear();
0773         }
0774     } else {
0775         QString fromString, toString;
0776 
0777         fromString.setNum(range.from);
0778         toString.setNum(range.to);
0779 
0780         QDomElement matchNode = m_doc.createElement("match"), fromTestNode = m_doc.createElement("test"), fromNode = m_doc.createElement("double"),
0781                     toTestNode = m_doc.createElement("test"), toNode = m_doc.createElement("double"), editNode = m_doc.createElement("edit"),
0782                     boolNode = m_doc.createElement("bool");
0783         QDomText fromText = m_doc.createTextNode(fromString), toText = m_doc.createTextNode(toString), boolText = m_doc.createTextNode("false");
0784 
0785         matchNode.setAttribute("target", "font"); // CPD: Is target "font" or "pattern" ????
0786         fromTestNode.setAttribute("qual", "any");
0787         fromTestNode.setAttribute("name", pixel ? "pixelsize" : "size");
0788         fromTestNode.setAttribute("compare", "more_eq");
0789         fromTestNode.appendChild(fromNode);
0790         fromNode.appendChild(fromText);
0791         toTestNode.setAttribute("qual", "any");
0792         toTestNode.setAttribute("name", pixel ? "pixelsize" : "size");
0793         toTestNode.setAttribute("compare", "less_eq");
0794         toTestNode.appendChild(toNode);
0795         toNode.appendChild(toText);
0796         editNode.setAttribute("mode", "assign");
0797         editNode.setAttribute("name", "antialias");
0798         editNode.appendChild(boolNode);
0799         boolNode.appendChild(boolText);
0800         matchNode.appendChild(fromTestNode);
0801         matchNode.appendChild(toTestNode);
0802         matchNode.appendChild(editNode);
0803 
0804         if (!m_antiAliasing.node.isNull()) {
0805             m_doc.documentElement().removeChild(range.node);
0806         }
0807         if (range.node.isNull()) {
0808             m_doc.documentElement().appendChild(matchNode);
0809         } else {
0810             m_doc.documentElement().replaceChild(matchNode, range.node);
0811         }
0812         range.node = matchNode;
0813     }
0814 }
0815 
0816 bool KXftConfig::antiAliasingHasLocalConfig() const
0817 {
0818     return m_antiAliasingHasLocalConfig;
0819 }
0820 
0821 KXftConfig::AntiAliasing::State KXftConfig::getAntiAliasing() const
0822 {
0823     return m_antiAliasing.state;
0824 }
0825 
0826 void KXftConfig::setAntiAliasing(AntiAliasing::State state)
0827 {
0828     if (state != m_antiAliasing.state) {
0829         m_antiAliasing.state = state;
0830         m_madeChanges = true;
0831     }
0832 }
0833 
0834 void KXftConfig::applyAntiAliasing()
0835 {
0836     if (AntiAliasing::NotSet == m_antiAliasing.state) {
0837         if (!m_antiAliasing.node.isNull()) {
0838             m_doc.documentElement().removeChild(m_antiAliasing.node);
0839             m_antiAliasing.node.clear();
0840         }
0841     } else {
0842         QDomElement matchNode = m_doc.createElement("match");
0843         QDomElement typeNode = m_doc.createElement("bool");
0844         QDomElement editNode = m_doc.createElement("edit");
0845         QDomText typeText = m_doc.createTextNode(m_antiAliasing.state == AntiAliasing::Enabled ? "true" : "false");
0846 
0847         matchNode.setAttribute("target", "font");
0848         editNode.setAttribute("mode", "assign");
0849         editNode.setAttribute("name", "antialias");
0850         editNode.appendChild(typeNode);
0851         typeNode.appendChild(typeText);
0852         matchNode.appendChild(editNode);
0853         if (!m_antiAliasing.node.isNull()) {
0854             m_doc.documentElement().removeChild(m_antiAliasing.node);
0855         }
0856         m_doc.documentElement().appendChild(matchNode);
0857         m_antiAliasing.node = matchNode;
0858     }
0859 }
0860 
0861 // KXftConfig only parses one config file, user's .fonts.conf usually.
0862 // If that one doesn't exist, then KXftConfig doesn't know if antialiasing
0863 // is enabled or not. So try to find out the default value from the default font.
0864 // Maybe there's a better way *shrug*.
0865 bool KXftConfig::aliasingEnabled()
0866 {
0867     FcPattern *pattern = FcPatternCreate();
0868     FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
0869     FcDefaultSubstitute(pattern);
0870     FcResult result;
0871     FcPattern *f = FcFontMatch(nullptr, pattern, &result);
0872     FcBool antialiased = FcTrue;
0873     FcPatternGetBool(f, FC_ANTIALIAS, 0, &antialiased);
0874     FcPatternDestroy(f);
0875     FcPatternDestroy(pattern);
0876     return antialiased == FcTrue;
0877 }
0878 
0879 #endif