1 /****************************************************************************
3 ** Copyright (C) 2011 Takumi Asaki
4 ** All rights reserved.
5 ** Contact: Takumi Asaki (takumi.asaki@gmail.com)
7 ** This file is part of the fontmanager application.
9 ** You may use this file under the terms of the BSD license as follows:
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
14 ** * Redistributions of source code must retain the above copyright
15 ** notice, this list of conditions and the following disclaimer.
16 ** * Redistributions in binary form must reproduce the above copyright
17 ** notice, this list of conditions and the following disclaimer in
18 ** the documentation and/or other materials provided with the
20 ** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
21 ** the names of its contributors may be used to endorse or promote
22 ** products derived from this software without specific prior written
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
37 ****************************************************************************/
39 #include "fontconfigmanager.h"
41 #include "fontconfigdefs.h"
42 #include "installedfontinfo.h"
43 #include "fontsconfigproperties.h"
45 #include "fontsconf.h"
50 #include <QXmlStreamReader>
53 #include <QFontMetrics>
58 #define FCBIN_PATH "/usr/bin/"
59 #define FCCACHE_COMMAND "fc-cache"
60 #define FCCACHE_OPTION "-v"
61 #define FCLIST_COMMAND "fc-list"
62 #define FCLIST_OPTION "-v"
64 //static QStringList configKeys;
66 FontConfigManager::FontConfigManager(QObject *parent) :
67 QObject(parent), mLang("en"), mLocalFontsConf(0)
69 connect(this, SIGNAL(localFontsConfPathChanged()), SIGNAL(localFontsConfExistsChanged()));
70 // connect(this, SIGNAL(localFontsConfPathChanged()), SIGNAL(fontsConfUpdated()));
72 mLocalFontsConf = new FontsConf(this);
73 mSystemLocalConf = new FontsConf(this);
75 QStringList cjkConflicts;
76 cjkConflicts << "ja" << "ko" << "zh-cn" << "zh-hk" << "zh-mo" << "zh-sg" << "zh-tw";
77 mConflictFonts << cjkConflicts;
79 mSystemLocalConf->load("/etc/fonts/local.conf");
83 QString FontConfigManager::currentLanguage() const
88 void FontConfigManager::setCurrentLanguage(const QString &lang)
93 QString FontConfigManager::localFontPath() const
95 return mLocalFontPath;
98 void FontConfigManager::setLocalFontPath(const QString &path)
100 mLocalFontPath = path;
103 QString FontConfigManager::localFontsConfPath() const
105 return mLocalFontsConfPath;
108 void FontConfigManager::setLocalFontsConfPath(const QString &path)
110 if (mLocalFontsConfPath != path) {
111 mLocalFontsConfPath = path;
112 emit localFontsConfPathChanged();
116 bool FontConfigManager::localFontsConfExists() const
118 return QFile::exists(mLocalFontsConfPath);
121 bool FontConfigManager::hasUnknownConfig() const
123 if (!mLocalFontsConf)
125 return mLocalFontsConf->hasUnknownConfig();
128 bool FontConfigManager::fontsConfModified() const
130 if (!mLocalFontsConf)
132 return mLocalFontsConf->modified();
135 bool FontConfigManager::isEmptyLocalFontsConf() const
137 return !mLocalFontsConf || mLocalFontsConf->isEmpty();
140 QStringList FontConfigManager::fontCount(const QString &fontpath) const
142 QStringList fontList;
143 foreach (InstalledFontInfo *info, mFcListInfo) {
144 if (info->file() == fontpath)
145 fontList << info->localefamily();
150 InstalledFontInfo *FontConfigManager::fontInfo(const QString &family, const QString &fullname) const
152 InstalledFontInfo *info = 0;
153 foreach (info, mFcListInfo) {
154 if (info->family().contains(family)) {
155 if (fullname.isEmpty())
157 if (info->fullname().contains(fullname))
164 InstalledFontInfo *FontConfigManager::fontInfo(int index) const
166 if (index >= 0 && index < mFcListInfo.count())
167 return mFcListInfo.at(index);
171 int FontConfigManager::count() const
173 return mFcListInfo.count();
176 void FontConfigManager::appendFontProperty(FontsConfigProperties *prop, const QStringList &familyList)
178 if (!mLocalFontsConf)
181 QStringList preferedFamilies(mLocalFontsConf->preferFamily());
182 preferedFamilies << prop->preferFamilies();
183 preferedFamilies.removeDuplicates();
184 foreach (const QString &family, preferedFamilies) {
185 if (prop->preferFamilies().contains(family)) {
186 if (familyList.isEmpty())
187 mLocalFontsConf->appendPreferFamilyFor(family, prop->family());
189 foreach (const QString &f, familyList)
190 mLocalFontsConf->appendPreferFamilyFor(family, f);
192 if (familyList.isEmpty())
193 mLocalFontsConf->removePreferFamilyFor(family, prop->family());
195 foreach (const QString &f, familyList)
196 mLocalFontsConf->removePreferFamilyFor(family, f);
200 QStringList keys = FontsConf::configKeys();
201 foreach (const QString &key, keys) {
202 if (prop->configValue(key) == FontsConfigProperties::Default) {
203 if (familyList.isEmpty())
204 mLocalFontsConf->unsetMatchEditValueFor(key, prop->family());
206 foreach (const QString &f, familyList)
207 mLocalFontsConf->unsetMatchEditValueFor(key, f);
209 if (familyList.isEmpty())
210 mLocalFontsConf->setMatchEditValueFor(key, prop->family(), (prop->configValue(key) == FontsConfigProperties::True));
212 foreach (const QString &f, familyList)
213 mLocalFontsConf->setMatchEditValueFor(key, f, (prop->configValue(key) == FontsConfigProperties::True));
217 emit fontsConfUpdated();
220 void FontConfigManager::appendFontProperty(InstalledFontInfo *fontInfo)
222 QStringList familyList;
223 familyList << fontInfo->enfamily();
224 appendFontProperty(fontInfo->fontProperty(), familyList);
227 FontsConfigProperties *FontConfigManager::fontProperty(const QString &family, const QStringList &familyList) const
229 FontsConfigProperties *prop = new FontsConfigProperties(family);
230 QStringList families;
231 QStringList preferedFamilies = mLocalFontsConf->preferFamily();
232 foreach (const QString &key, preferedFamilies) {
233 families = mLocalFontsConf->preferFamilyFor(key);
234 if (families.contains(family))
235 prop->addPreferFamily(key);
236 foreach (const QString &f, familyList) {
237 if (families.contains(f))
238 prop->addPreferFamily(key);
242 QStringList keys = FontsConf::configKeys();
243 foreach (const QString &key, keys) {
244 QString val = mLocalFontsConf->matchEditValueFor(key, prop->family());
245 if (!val.isEmpty()) {
246 prop->setConfigValue(key, (QVariant(val).toBool() ? FontsConfigProperties::True : FontsConfigProperties::False));
249 foreach (const QString &f, familyList) {
250 mLocalFontsConf->matchEditValueFor(key, f);
251 if (!val.isEmpty()) {
252 prop->setConfigValue(key, (QVariant(val).toBool() ? FontsConfigProperties::True : FontsConfigProperties::False));
261 FontsConfigProperties *FontConfigManager::fontProperty(InstalledFontInfo *fontInfo) const
263 return fontProperty(fontInfo->enfamily(), fontInfo->family());
266 QString FontConfigManager::localFontsConf() const
268 QFile fp(mLocalFontsConfPath);
273 if (!fp.open(QIODevice::ReadOnly))
277 ts.setCodec("UTF-8");
278 QString buf = ts.readAll();
285 QStringList FontConfigManager::preferFamilyFor(const QString &family) const
287 return mLocalFontsConf->preferFamilyFor(family);
290 QStringList FontConfigManager::acceptFamilyFor(const QString &family) const
292 return mLocalFontsConf->acceptFamilyFor(family);
295 QStringList FontConfigManager::prependFamilyFor(const QString &family) const
297 return mLocalFontsConf->prependFamilyFor(family);
300 QStringList FontConfigManager::appendFamilyFor(const QString &family) const
302 return mLocalFontsConf->appendFamilyFor(family);
305 bool FontConfigManager::isConflict(const QString &family, const QStringList &lang) const
307 InstalledFontInfo *info = fontInfo(family);
311 QList<int> confListIndex;
312 for (int i = 0; i < mConflictFonts.count(); i++) {
313 const QStringList list(mConflictFonts.at(i));
314 foreach (const QString &f, list) {
315 if (lang.contains(f)) {
316 if (!confListIndex.contains(i))
323 if (confListIndex.isEmpty())
326 foreach (int i, confListIndex) {
327 QStringList confList = mConflictFonts.at(confListIndex.at(i));
328 foreach (const QString &f, confList) {
329 if (info->lang().contains(f)) {
338 QStringList FontConfigManager::systemPreferFamilyFor(const QString &family) const
340 return mSystemLocalConf->preferFamilyFor(family);
343 void FontConfigManager::importSystemSettings(const QString &family)
345 QStringList systemFamilyList = systemPreferFamilyFor(family);
347 QStringList installedFontLang;
348 foreach (InstalledFontInfo *info, mFcListInfo) {
349 if (info->systemFont())
351 installedFontLang << info->lang();
353 installedFontLang.sort();
354 installedFontLang.removeDuplicates();
356 emit startUpdateFontsConfig();
357 foreach (const QString &f, systemFamilyList) {
358 InstalledFontInfo *info = fontInfo(f);
362 if (isConflict(f, installedFontLang))
363 mLocalFontsConf->appendAcceptFamilyFor(family, f);
365 mLocalFontsConf->appendPrependFamilyFor(family, f);
367 emit endUpdateFontsConfig();
369 if (mLocalFontsConf->modified()) {
370 emit fontsConfUpdated();
374 void FontConfigManager::createRecommendedSettings()
376 QList<InstalledFontInfo*> sansSerifFonts;
377 QList<InstalledFontInfo*> serifFonts;
378 QList<InstalledFontInfo*> monospaceFonts;
379 QList<InstalledFontInfo*> monospaceSansSerifFonts;
380 QList<InstalledFontInfo*> monospaceSerifFonts;
381 QList<InstalledFontInfo*> unknownFonts;
383 foreach (InstalledFontInfo *info, mFcListInfo) {
384 if (info->systemFont())
386 if (maybeMonospaceFont(info)) {
387 if (maybeSansSerifFont(info))
388 monospaceSansSerifFonts << info;
389 else if (maybeSerifFont(info))
390 monospaceSerifFonts << info;
392 monospaceFonts << info;
393 } else if (maybeSansSerifFont(info))
394 sansSerifFonts << info;
395 else if (maybeSerifFont(info))
398 unknownFonts << info;
401 emit startUpdateFontsConfig();
406 foreach (InstalledFontInfo *info, monospaceFonts)
407 f << info->localefamily();
408 qDebug() << "Mono:" << f;
410 foreach (InstalledFontInfo *info, monospaceSansSerifFonts)
411 f << info->localefamily();
412 qDebug() << "Mono(Sans Serif):" << f;
414 foreach (InstalledFontInfo *info, monospaceSerifFonts)
415 f << info->localefamily();
416 qDebug() << "Mono(Serif):" << f;
418 foreach (InstalledFontInfo *info, sansSerifFonts)
419 f << info->localefamily();
420 qDebug() << "Sans Serif:" << f;
422 foreach (InstalledFontInfo *info, serifFonts)
423 f << info->localefamily();
424 qDebug() << "Serif:" << f;
426 foreach (InstalledFontInfo *info, unknownFonts)
427 f << info->localefamily();
428 qDebug() << "Unknown:" << f;
431 if (monospaceFonts.count())
432 foreach (InstalledFontInfo *info, monospaceFonts) {
433 addPreferFamily(MONOSPACE_DEF, info->enfamily());
434 addPreferFamily(MONOSPACE_DEF, info->localefamily());
436 else if (monospaceSansSerifFonts.count())
437 foreach (InstalledFontInfo *info, monospaceSansSerifFonts) {
438 addPreferFamily(MONOSPACE_DEF, info->enfamily());
439 addPreferFamily(MONOSPACE_DEF, info->localefamily());
441 else if (monospaceSerifFonts.count())
442 foreach (InstalledFontInfo *info, monospaceSerifFonts) {
443 addPreferFamily(MONOSPACE_DEF, info->enfamily());
444 addPreferFamily(MONOSPACE_DEF, info->localefamily());
447 if (sansSerifFonts.count())
448 foreach (InstalledFontInfo *info, sansSerifFonts) {
449 addPreferFamily(SANSSERIF_DEF, info->enfamily());
450 addPreferFamily(SANSSERIF_DEF, info->localefamily());
452 else if (monospaceSansSerifFonts.count())
453 foreach (InstalledFontInfo *info, monospaceSansSerifFonts) {
454 addPreferFamily(SANSSERIF_DEF, info->enfamily());
455 addPreferFamily(SANSSERIF_DEF, info->localefamily());
457 else if (unknownFonts.count())
458 foreach (InstalledFontInfo *info, unknownFonts) {
459 addPreferFamily(SANSSERIF_DEF, info->enfamily());
460 addPreferFamily(SANSSERIF_DEF, info->localefamily());
462 else if (monospaceFonts.count())
463 foreach (InstalledFontInfo *info, monospaceFonts) {
464 addPreferFamily(SANSSERIF_DEF, info->enfamily());
465 addPreferFamily(SANSSERIF_DEF, info->localefamily());
468 if (serifFonts.count())
469 foreach (InstalledFontInfo *info, serifFonts) {
470 addPreferFamily(SERIF_DEF, info->enfamily());
471 addPreferFamily(SERIF_DEF, info->localefamily());
473 else if (monospaceSerifFonts.count())
474 foreach (InstalledFontInfo *info, monospaceSerifFonts) {
475 addPreferFamily(SANSSERIF_DEF, info->enfamily());
476 addPreferFamily(SANSSERIF_DEF, info->localefamily());
478 else if (unknownFonts.count())
479 foreach (InstalledFontInfo *info, unknownFonts) {
480 addPreferFamily(SERIF_DEF, info->enfamily());
481 addPreferFamily(SERIF_DEF, info->localefamily());
483 else if (monospaceFonts.count())
484 foreach (InstalledFontInfo *info, monospaceFonts) {
485 addPreferFamily(SERIF_DEF, info->enfamily());
486 addPreferFamily(SERIF_DEF, info->localefamily());
489 foreach (const QString &f, FontsConf::genericFamilies())
490 importSystemSettings(f);
491 emit endUpdateFontsConfig();
493 emit fontsConfUpdated();
496 QStringList FontConfigManager::installableFamily(const QString &family, bool localOnly)
498 QStringList installedFont;
499 installedFont << prependFamilyFor(family);
500 installedFont << appendFamilyFor(family);
501 installedFont << preferFamilyFor(family);
502 installedFont << acceptFamilyFor(family);
504 QStringList familyList;
505 foreach (InstalledFontInfo *info, mFcListInfo) {
506 if (localOnly && info->systemFont())
509 foreach (const QString &f, info->family()) {
510 if (installedFont.contains(f)) {
516 familyList << info->localefamily();
519 familyList.removeDuplicates();
524 QString FontConfigManager::localeFamily(const QString &family) const
526 if (mEnLocaleFontMap.contains(family))
527 return mEnLocaleFontMap.value(family);
531 bool FontConfigManager::maybeSansSerifFont(InstalledFontInfo *info) const
533 foreach (const QString &f, info->family()) {
534 if (f.contains("gothic", Qt::CaseInsensitive) ||
535 f.contains("arial", Qt::CaseInsensitive) ||
536 f.contains("helvetica", Qt::CaseInsensitive) ||
537 f.contains("verdana", Qt::CaseInsensitive) ||
538 f.contains("sans", Qt::CaseInsensitive))
544 bool FontConfigManager::maybeSerifFont(InstalledFontInfo *info) const
546 foreach (const QString &f, info->family()) {
547 if (f.contains("mincho", Qt::CaseInsensitive) ||
548 f.contains("times", Qt::CaseInsensitive) ||
549 (f.contains("serif", Qt::CaseInsensitive) && !f.contains("sans", Qt::CaseInsensitive)))
555 bool FontConfigManager::maybeMonospaceFont(InstalledFontInfo *info) const
557 QFont font(info->enfamily());
558 if (font.exactMatch()) {
562 QFontMetrics fm(font);
563 int w = fm.width(QLatin1Char('A'));
564 if (fm.width(QLatin1Char('i')) == w && fm.width(QLatin1Char('X')) == w)
567 foreach (const QString &f, info->family()) {
568 if (f.contains("courier", Qt::CaseInsensitive) ||
569 f.contains("mono", Qt::CaseInsensitive))
575 void FontConfigManager::runFcCache()
577 QProcess *proc = new QProcess(this);
578 connect(proc, SIGNAL(finished(int)), SIGNAL(fcCacheFinished()));
579 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
580 proc->start(FCBIN_PATH FCCACHE_COMMAND, QStringList() << FCCACHE_OPTION);
583 void FontConfigManager::readFcList()
585 QProcess *proc = new QProcess(this);
586 proc->start(FCBIN_PATH FCLIST_COMMAND, QStringList() << FCLIST_OPTION);
587 if (!proc->waitForStarted())
590 if (!proc->waitForFinished())
593 qDeleteAll(mFcListInfo);
595 mEnLocaleFontMap.clear();
597 QByteArray buf = proc->readAllStandardOutput();
598 QByteArray errbuf = proc->readAllStandardError();
599 if (!errbuf.isEmpty())
600 qWarning() << errbuf;
601 Q_ASSERT(errbuf.isEmpty());
603 static QByteArray emptyLine("\n");
604 QBuffer buffer(&buf);
606 buffer.open(QIODevice::ReadOnly);
607 Q_ASSERT(buffer.isOpen());
608 while (!buffer.atEnd()) {
609 QByteArray linebuf = buffer.readLine();
610 if (linebuf.isEmpty() || linebuf == emptyLine) {
611 if (!fibuf.isEmpty()) {
612 InstalledFontInfo *info = new InstalledFontInfo(fibuf, mLocalFontPath, this);
613 info->setLocale(mLang);
614 if (info->isValid()) {
615 mFcListInfo.append(info);
616 mEnLocaleFontMap.insert(info->enfamily(), info->localefamily());
617 info->setFontProperty(fontProperty(info));
626 Q_ASSERT(fibuf.isEmpty());
629 qSort(mFcListInfo.begin(), mFcListInfo.end(), InstalledFontInfo::compare);
631 emit fontListUpdated();
636 void FontConfigManager::readFontsConf()
638 mLocalFontsConf->load(mLocalFontsConfPath);
639 emit fontsConfUpdated();
642 void FontConfigManager::saveFontsConf()
644 if (!mLocalFontsConf || mLocalFontsConfPath.isEmpty())
647 bool check = localFontsConfExists();
648 if (isEmptyLocalFontsConf()) {
650 QFile::remove(mLocalFontsConfPath);
651 emit localFontsConfExistsChanged();
654 mLocalFontsConf->save(mLocalFontsConfPath);
656 emit localFontsConfExistsChanged();
661 void FontConfigManager::backupFontsConf(const QString &filepath)
663 if (filepath.isEmpty() || isEmptyLocalFontsConf())
665 mLocalFontsConf->save(filepath);
668 void FontConfigManager::restoreFontsConf(const QUrl &fileUrl)
670 QString filepath = fileUrl.toLocalFile();
671 if (filepath.isEmpty() || !QFile::exists(filepath))
673 FontsConf *restoredConf = new FontsConf(this);
674 restoredConf->load(filepath);
675 if (restoredConf->isValid()) {
676 mLocalFontsConf->copy(restoredConf);
677 emit fontsConfUpdated();
678 restoredConf->deleteLater();
682 void FontConfigManager::resetFontsConf()
684 if (!mLocalFontsConf)
686 mLocalFontsConf->initFontsConf();
687 emit fontsConfUpdated();
690 void FontConfigManager::addPreferFamily(const QString &family, const QString &value)
692 mLocalFontsConf->appendPreferFamilyFor(family, value);
693 emit fontsConfUpdated();
696 void FontConfigManager::addAcceptFamily(const QString &family, const QString &value)
698 mLocalFontsConf->appendAcceptFamilyFor(family, value);
699 emit fontsConfUpdated();
702 void FontConfigManager::addPrependFamily(const QString &family, const QString &value)
704 mLocalFontsConf->appendPrependFamilyFor(family, value);
705 emit fontsConfUpdated();
708 void FontConfigManager::addAppendFamily(const QString &family, const QString &value)
710 mLocalFontsConf->appendAppendFamilyFor(family, value);
711 emit fontsConfUpdated();
714 void FontConfigManager::insertPreferFamily(const QString &family, const QString &value, int index)
716 mLocalFontsConf->insertPreferFamilyFor(family, value, index);
717 emit fontsConfUpdated();
720 void FontConfigManager::insertAcceptFamily(const QString &family, const QString &value, int index)
722 mLocalFontsConf->insertAcceptFamilyFor(family, value, index);
723 emit fontsConfUpdated();
726 void FontConfigManager::insertPrependFamily(const QString &family, const QString &value, int index)
728 mLocalFontsConf->insertPrependFamilyFor(family, value, index);
729 emit fontsConfUpdated();
732 void FontConfigManager::insertAppendFamily(const QString &family, const QString &value, int index)
734 mLocalFontsConf->insertAppendFamilyFor(family, value, index);
735 emit fontsConfUpdated();
738 void FontConfigManager::removePreferFamily(const QString &family, const QString &value)
740 mLocalFontsConf->removePreferFamilyFor(family, value);
741 emit fontsConfUpdated();
744 void FontConfigManager::removeAcceptFamily(const QString &family, const QString &value)
746 mLocalFontsConf->removeAcceptFamilyFor(family, value);
747 emit fontsConfUpdated();
750 void FontConfigManager::removePrependFamily(const QString &family, const QString &value)
752 mLocalFontsConf->removePrependFamilyFor(family, value);
753 emit fontsConfUpdated();
756 void FontConfigManager::removeAppendFamily(const QString &family, const QString &value)
758 mLocalFontsConf->removeAppendFamilyFor(family, value);
759 emit fontsConfUpdated();