File indexing completed on 2024-06-02 05:43:46

0001 /*
0002     SPDX-FileCopyrightText: 2003-2009 Craig Drummond <craig@kde.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "FcConfig.h"
0007 #include "Misc.h"
0008 #include <QByteArray>
0009 #include <QDebug>
0010 #include <QDir>
0011 #include <QDomDocument>
0012 #include <QDomElement>
0013 #include <QDomNode>
0014 #include <QDomText>
0015 #include <QFile>
0016 #include <QRegularExpression>
0017 #include <fontconfig/fontconfig.h>
0018 #include <stdio.h>
0019 
0020 namespace KFI
0021 {
0022 namespace FcConfig
0023 {
0024 inline QString xDirSyntax(const QString &d)
0025 {
0026     return Misc::fileSyntax(d);
0027 }
0028 
0029 //
0030 // Obtain location of config file to use.
0031 //
0032 // For system, prefer the following:
0033 //
0034 //     <...>/config.d/00kde.conf   = preferred method from FCConfig >= 2.3
0035 //     <...>/local.conf
0036 //
0037 // Non-system, prefer:
0038 //
0039 //     $HOME/<...>/.fonts.conf
0040 //     $HOME/<...>/fonts.conf
0041 //
0042 QString getConfigFile(bool system)
0043 {
0044 #if (FC_VERSION >= 20300)
0045     static const char constKdeRootFcFile[] = "00kde.conf";
0046 #endif
0047 
0048     FcStrList *list = FcConfigGetConfigFiles(FcConfigGetCurrent());
0049     QStringList files;
0050     FcChar8 *file;
0051     QString home(Misc::dirSyntax(QDir::homePath()));
0052 
0053     while ((file = FcStrListNext(list))) {
0054         QString f((const char *)file);
0055 
0056         if (Misc::fExists(f)) {
0057             // For nonsystem, only consider file within $HOME
0058             if (system || 0 == Misc::fileSyntax(f).indexOf(home)) {
0059                 files.append(f);
0060             }
0061         }
0062 #if (FC_VERSION >= 20300)
0063         if (system && Misc::dExists(f) && (f.contains(QRegularExpression("/conf\\.d/?$")) || f.contains(QRegularExpression("/conf\\.d?$")))) {
0064             return Misc::dirSyntax(f) + constKdeRootFcFile; // This ones good enough for me!
0065         }
0066 #endif
0067     }
0068 
0069     //
0070     // Go through list of files, looking for the preferred one...
0071     if (!files.isEmpty()) {
0072         QStringList::const_iterator it(files.begin()), end(files.end());
0073 
0074         for (; it != end; ++it) {
0075             if (-1 != (*it).indexOf(QRegularExpression(system ? "/local\\.conf$" : "/\\.?fonts\\.conf$"))) {
0076                 return *it;
0077             }
0078         }
0079         return files.front(); // Just return the 1st one...
0080     } else { // Hmmm... no known files?
0081         return system ? "/etc/fonts/local.conf" : Misc::fileSyntax(home + "/.fonts.conf");
0082     }
0083 }
0084 
0085 void addDir(const QString &dir, bool system)
0086 {
0087     QDomDocument doc("fontconfig");
0088     QString fileName = getConfigFile(system);
0089     QFile f(fileName);
0090     bool hasDir(false);
0091 
0092     // qDebug() << "Using fontconfig file:" << fileName;
0093 
0094     // Load existing file - and check to see whether it has the dir...
0095     if (f.open(QIODevice::ReadOnly)) {
0096         doc.clear();
0097 
0098         if (doc.setContent(&f)) {
0099             QDomNode n = doc.documentElement().firstChild();
0100 
0101             while (!n.isNull() && !hasDir) {
0102                 QDomElement e = n.toElement();
0103 
0104                 if (!e.isNull() && "dir" == e.tagName()) {
0105                     if (0 == Misc::expandHome(Misc::dirSyntax(e.text())).indexOf(dir)) {
0106                         hasDir = true;
0107                     }
0108                 }
0109                 n = n.nextSibling();
0110             }
0111         }
0112         f.close();
0113     }
0114 
0115     // Add dir, and save, if config does not already have this dir.
0116     if (!hasDir) {
0117         if (doc.documentElement().isNull()) {
0118             doc.appendChild(doc.createElement("fontconfig"));
0119         }
0120 
0121         QDomElement newNode = doc.createElement("dir");
0122         QDomText text = doc.createTextNode(Misc::contractHome(xDirSyntax(dir)));
0123 
0124         newNode.appendChild(text);
0125         doc.documentElement().appendChild(newNode);
0126 
0127         FcAtomic *atomic = FcAtomicCreate((const unsigned char *)(QFile::encodeName(fileName).data()));
0128 
0129         if (atomic) {
0130             if (FcAtomicLock(atomic)) {
0131                 FILE *f = fopen((char *)FcAtomicNewFile(atomic), "w");
0132 
0133                 if (f) {
0134                     //
0135                     // Check document syntax...
0136                     static const char qtXmlHeader[] = "<?xml version = '1.0'?>";
0137                     static const char xmlHeader[] = "<?xml version=\"1.0\"?>";
0138                     static const char qtDocTypeLine[] = "<!DOCTYPE fontconfig>";
0139                     static const char docTypeLine[] =
0140                         "<!DOCTYPE fontconfig SYSTEM "
0141                         "\"fonts.dtd\">";
0142 
0143                     QString str(doc.toString());
0144                     int idx;
0145 
0146                     if (0 != str.indexOf("<?xml")) {
0147                         str.insert(0, xmlHeader);
0148                     } else if (0 == str.indexOf(qtXmlHeader)) {
0149                         str.replace(0, strlen(qtXmlHeader), xmlHeader);
0150                     }
0151 
0152                     if (-1 != (idx = str.indexOf(qtDocTypeLine))) {
0153                         str.replace(idx, strlen(qtDocTypeLine), docTypeLine);
0154                     }
0155 
0156                     //
0157                     // Write to file...
0158                     fputs(str.toUtf8(), f);
0159                     fclose(f);
0160 
0161                     if (!FcAtomicReplaceOrig(atomic)) {
0162                         FcAtomicDeleteNew(atomic);
0163                     }
0164                 }
0165                 FcAtomicUnlock(atomic);
0166             }
0167             FcAtomicDestroy(atomic);
0168         }
0169     }
0170 }
0171 
0172 }
0173 
0174 }