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