File indexing completed on 2025-01-26 05:08:05

0001 /*
0002     SPDX-FileCopyrightText: 2003-2009 Craig Drummond <craig@kde.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "Folder.h"
0007 #include "FcConfig.h"
0008 #include "KfiConstants.h"
0009 #include "XmlStrings.h"
0010 #include "config-fontinst.h"
0011 #include <KShell>
0012 #include <QCoreApplication>
0013 #include <QDebug>
0014 #include <QDir>
0015 #include <QDomDocument>
0016 #include <QDomElement>
0017 #include <QDomNode>
0018 #include <QFile>
0019 #include <QProcess>
0020 #include <QSaveFile>
0021 #include <QStandardPaths>
0022 #include <QTextStream>
0023 #include <fontconfig/fontconfig.h>
0024 
0025 #define DISABLED_FONTS "disabledfonts"
0026 
0027 namespace KFI
0028 {
0029 bool Folder::CfgFile::modified()
0030 {
0031     return timestamp != Misc::getTimeStamp(name);
0032 }
0033 
0034 void Folder::CfgFile::updateTimeStamp()
0035 {
0036     timestamp = Misc::getTimeStamp(name);
0037 }
0038 
0039 Folder::~Folder()
0040 {
0041     saveDisabled();
0042 }
0043 
0044 void Folder::init(bool system, bool systemBus)
0045 {
0046     m_isSystem = system;
0047     if (!system) {
0048         FcStrList *list = FcConfigGetFontDirs(FcInitLoadConfigAndFonts());
0049         QStringList dirs;
0050         FcChar8 *fcDir;
0051 
0052         while ((fcDir = FcStrListNext(list))) {
0053             dirs.append(Misc::dirSyntax((const char *)fcDir));
0054         }
0055 
0056         m_location = Misc::getFolder(Misc::dirSyntax(QDir::homePath() + "/.fonts/"), Misc::dirSyntax(QDir::homePath()), dirs);
0057     } else {
0058         m_location = KFI_DEFAULT_SYS_FONTS_FOLDER;
0059     }
0060 
0061     if ((!system && !systemBus) || (system && systemBus)) {
0062         FcConfig::addDir(m_location, system);
0063     }
0064 
0065     m_disabledCfg.dirty = false;
0066     if (m_disabledCfg.name.isEmpty()) {
0067         QString fileName("/" DISABLED_FONTS ".xml");
0068 
0069         if (system) {
0070             m_disabledCfg.name = QString::fromLatin1(KFI_ROOT_CFG_DIR) + fileName;
0071         } else {
0072             QString path = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/');
0073 
0074             if (!Misc::dExists(path)) {
0075                 Misc::createDir(path);
0076             }
0077             m_disabledCfg.name = path + fileName;
0078         }
0079         m_disabledCfg.timestamp = 0;
0080     }
0081 }
0082 
0083 bool Folder::allowToggling() const
0084 {
0085     return Misc::fExists(m_disabledCfg.name) ? Misc::fWritable(m_disabledCfg.name) : Misc::dWritable(Misc::getDir(m_disabledCfg.name));
0086 }
0087 
0088 void Folder::loadDisabled()
0089 {
0090     if (m_disabledCfg.dirty) {
0091         saveDisabled();
0092     }
0093 
0094     QFile f(m_disabledCfg.name);
0095 
0096     // qDebug() << m_disabledCfg.name;
0097     m_disabledCfg.dirty = false;
0098     if (f.open(QIODevice::ReadOnly)) {
0099         QDomDocument doc;
0100 
0101         if (doc.setContent(&f)) {
0102             for (QDomNode n = doc.documentElement().firstChild(); !n.isNull(); n = n.nextSibling()) {
0103                 QDomElement e = n.toElement();
0104 
0105                 if (FONT_TAG == e.tagName()) {
0106                     Family fam(e, false);
0107 
0108                     if (!fam.name().isEmpty()) {
0109                         Style style(e, false);
0110 
0111                         if (KFI_NO_STYLE_INFO != style.value()) {
0112                             QList<File> files;
0113 
0114                             if (e.hasAttribute(PATH_ATTR)) {
0115                                 File file(e, true);
0116 
0117                                 if (!file.path().isEmpty()) {
0118                                     files.append(file);
0119                                 } else {
0120                                     m_disabledCfg.dirty = true;
0121                                 }
0122                             } else {
0123                                 for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
0124                                     QDomElement ent = n.toElement();
0125 
0126                                     if (FILE_TAG == ent.tagName()) {
0127                                         File file(ent, true);
0128 
0129                                         if (!file.path().isEmpty()) {
0130                                             files.append(file);
0131                                         } else {
0132                                             // qDebug() << "Set dirty from load";
0133                                             m_disabledCfg.dirty = true;
0134                                         }
0135                                     }
0136                                 }
0137                             }
0138 
0139                             if (files.count() > 0) {
0140                                 QList<File>::ConstIterator it(files.begin()), end(files.end());
0141 
0142                                 FamilyCont::ConstIterator f(m_fonts.insert(fam));
0143                                 StyleCont::ConstIterator s((*f).add(style));
0144 
0145                                 for (; it != end; ++it) {
0146                                     (*s).add(*it);
0147                                 }
0148                             }
0149                         }
0150                     }
0151                 }
0152             }
0153         }
0154 
0155         f.close();
0156         m_disabledCfg.updateTimeStamp();
0157     }
0158 
0159     saveDisabled();
0160 }
0161 
0162 void Folder::saveDisabled()
0163 {
0164     if (m_disabledCfg.dirty) {
0165         if (!m_isSystem || Misc::root()) {
0166             // qDebug() << m_disabledCfg.name;
0167 
0168             QSaveFile file;
0169 
0170             file.setFileName(m_disabledCfg.name);
0171 
0172             if (!file.open(QIODevice::WriteOnly)) {
0173                 // qDebug() << "Exit - cant open save file";
0174                 qApp->exit(0);
0175             }
0176 
0177             QTextStream str(&file);
0178 
0179             str << "<" DISABLED_FONTS ">" << Qt::endl;
0180 
0181             FamilyCont::ConstIterator it(m_fonts.begin()), end(m_fonts.end());
0182 
0183             for (; it != end; ++it) {
0184                 (*it).toXml(true, str);
0185             }
0186             str << "</" DISABLED_FONTS ">" << Qt::endl;
0187             str.flush();
0188 
0189             if (!file.commit()) {
0190                 // qDebug() << "Exit - cant finalize save file";
0191                 qApp->exit(0);
0192             }
0193         }
0194         m_disabledCfg.updateTimeStamp();
0195         m_disabledCfg.dirty = false;
0196     }
0197 }
0198 
0199 QStringList Folder::toXml(int max)
0200 {
0201     QStringList rv;
0202     FamilyCont::ConstIterator it(m_fonts.begin()), end(m_fonts.end());
0203     QString string;
0204     QTextStream str(&string);
0205 
0206     for (int i = 0; it != end; ++it, ++i) {
0207         if (0 == (i % max)) {
0208             if (i) {
0209                 str << "</" FONTLIST_TAG ">" << Qt::endl;
0210                 rv.append(string);
0211                 string = QString();
0212             }
0213             str << "<" FONTLIST_TAG " " << SYSTEM_ATTR "=\"" << (m_isSystem ? "true" : "false") << "\">" << Qt::endl;
0214         }
0215 
0216         (*it).toXml(false, str);
0217     }
0218 
0219     if (!string.isEmpty()) {
0220         str << "</" FONTLIST_TAG ">" << Qt::endl;
0221         rv.append(string);
0222     }
0223     return rv;
0224 }
0225 
0226 Families Folder::list()
0227 {
0228     Families fam(m_isSystem);
0229     FamilyCont::ConstIterator it(m_fonts.begin()), end(m_fonts.end());
0230 
0231     for (int i = 0; it != end; ++it, ++i) {
0232         fam.items.insert(*it);
0233     }
0234 
0235     return fam;
0236 }
0237 
0238 bool Folder::contains(const QString &family, quint32 style)
0239 {
0240     FamilyCont::ConstIterator fam = m_fonts.find(Family(family));
0241 
0242     if (fam == m_fonts.end()) {
0243         return false;
0244     }
0245 
0246     StyleCont::ConstIterator st = (*fam).styles().find(Style(style));
0247 
0248     return st != (*fam).styles().end();
0249 }
0250 
0251 void Folder::add(const Family &family)
0252 {
0253     FamilyCont::ConstIterator existingFamily = m_fonts.find(family);
0254 
0255     if (existingFamily == m_fonts.end()) {
0256         m_fonts.insert(family);
0257     } else {
0258         StyleCont::ConstIterator it(family.styles().begin()), end(family.styles().end());
0259 
0260         for (; it != end; ++it) {
0261             StyleCont::ConstIterator existingStyle = (*existingFamily).styles().find(*it);
0262 
0263             if (existingStyle == (*existingFamily).styles().end()) {
0264                 (*existingFamily).add(*it);
0265             } else {
0266                 FileCont::ConstIterator fit((*it).files().begin()), fend((*it).files().end());
0267 
0268                 for (; fit != fend; ++fit) {
0269                     FileCont::ConstIterator f = (*existingStyle).files().find(*fit);
0270 
0271                     if (f == (*existingStyle).files().end()) {
0272                         (*existingStyle).add(*fit);
0273                     }
0274                 }
0275 
0276                 (*existingStyle).setWritingSystems((*existingStyle).writingSystems() | (*it).writingSystems());
0277                 if (!(*existingStyle).scalable() && (*it).scalable()) {
0278                     (*existingStyle).setScalable(true);
0279                 }
0280             }
0281         }
0282     }
0283 }
0284 
0285 void Folder::configure(bool force)
0286 {
0287     // qDebug() << "EMPTY MODIFIED " << m_modifiedDirs.isEmpty();
0288 
0289     if (force || !m_modifiedDirs.isEmpty()) {
0290         saveDisabled();
0291 
0292         QSet<QString>::ConstIterator it(m_modifiedDirs.constBegin()), end(m_modifiedDirs.constEnd());
0293         QSet<QString> dirs;
0294 
0295         for (; it != end; ++it) {
0296             if (Misc::fExists((*it) + "fonts.dir")) {
0297                 dirs.insert(KShell::quoteArg(*it));
0298             }
0299         }
0300 
0301         if (!dirs.isEmpty()) {
0302             QProcess::startDetached(QStringLiteral(KFONTINST_LIB_EXEC_DIR "/fontinst_x11"), dirs.values());
0303         }
0304 
0305         m_modifiedDirs.clear();
0306 
0307         // qDebug() << "RUN FC";
0308         Misc::doCmd("fc-cache");
0309         // qDebug() << "DONE";
0310     }
0311 }
0312 
0313 Folder::Flat Folder::flatten() const
0314 {
0315     FamilyCont::ConstIterator fam = m_fonts.begin(), famEnd = m_fonts.end();
0316     Flat rv;
0317 
0318     for (; fam != famEnd; ++fam) {
0319         StyleCont::ConstIterator style((*fam).styles().begin()), styleEnd((*fam).styles().end());
0320 
0321         for (; style != styleEnd; ++style) {
0322             FileCont::ConstIterator file((*style).files().begin()), fileEnd((*style).files().end());
0323 
0324             for (; file != fileEnd; ++file) {
0325                 rv.insert(FlatFont(*fam, *style, *file));
0326             }
0327         }
0328     }
0329 
0330     return rv;
0331 }
0332 
0333 Families Folder::Flat::build(bool system) const
0334 {
0335     ConstIterator it(begin()), e(end());
0336     Families families(system);
0337 
0338     for (; it != e; ++it) {
0339         Family f((*it).family);
0340         Style s((*it).styleInfo, (*it).scalable, (*it).writingSystems);
0341         FamilyCont::ConstIterator fam = families.items.constFind(f);
0342 
0343         if (families.items.constEnd() == fam) {
0344             s.add((*it).file);
0345             f.add(s);
0346             families.items.insert(f);
0347         } else {
0348             StyleCont::ConstIterator st = (*fam).styles().constFind(s);
0349 
0350             if ((*fam).styles().constEnd() == st) {
0351                 s.add((*it).file);
0352                 (*fam).add(s);
0353             } else {
0354                 (*st).add((*it).file);
0355             }
0356         }
0357     }
0358 
0359     return families;
0360 }
0361 
0362 }