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 }