File indexing completed on 2024-05-19 05:38:06

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