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