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>
57 #define FCBIN_PATH "/usr/bin/"
58 #define FCCACHE_COMMAND "fc-cache"
59 #define FCCACHE_OPTION "-v"
60 #define FCLIST_COMMAND "fc-list"
61 #define FCLIST_OPTION "-v"
63 //static QStringList configKeys;
65 FontConfigManager::FontConfigManager(QObject *parent) :
66 QObject(parent), mLang("en"), mLocalFontsConf(0)
68 connect(this, SIGNAL(localFontsConfPathChanged()), SIGNAL(localFontsConfExistsChanged()));
69 connect(this, SIGNAL(localFontsConfPathChanged()), SIGNAL(fontsConfUpdated()));
71 mLocalFontsConf = new FontsConf(this);
72 mSystemLocalConf = new FontsConf(this);
74 QStringList cjkConflicts;
75 cjkConflicts << "ja" << "ko" << "zh-cn" << "zh-hk" << "zh-mo" << "zh-sg" << "zh-tw";
76 mConflictFonts << cjkConflicts;
78 mSystemLocalConf->load("/etc/fonts/local.conf");
82 QString FontConfigManager::currentLanguage() const
87 void FontConfigManager::setCurrentLanguage(const QString &lang)
92 QString FontConfigManager::localFontPath() const
94 return mLocalFontPath;
97 void FontConfigManager::setLocalFontPath(const QString &path)
99 mLocalFontPath = path;
102 QString FontConfigManager::localFontsConfPath() const
104 return mLocalFontsConfPath;
107 void FontConfigManager::setLocalFontsConfPath(const QString &path)
109 if (mLocalFontsConfPath != path) {
110 mLocalFontsConfPath = path;
111 emit localFontsConfPathChanged();
115 bool FontConfigManager::localFontsConfExists() const
117 return QFile::exists(mLocalFontsConfPath);
120 bool FontConfigManager::hasUnknownConfig() const
122 if (!mLocalFontsConf)
124 return mLocalFontsConf->hasUnknownConfig();
127 bool FontConfigManager::fontsConfModified() const
129 if (!mLocalFontsConf)
131 return mLocalFontsConf->modified();
134 bool FontConfigManager::isEmptyLocalFontsConf() const
136 return !mLocalFontsConf || mLocalFontsConf->isEmpty();
139 QStringList FontConfigManager::fontCount(const QString &fontpath) const
141 QStringList fontList;
142 foreach (InstalledFontInfo *info, mFcListInfo) {
143 if (info->file() == fontpath)
144 fontList << info->localefamily();
149 InstalledFontInfo *FontConfigManager::fontInfo(const QString &family, const QString &fullname) const
151 InstalledFontInfo *info = 0;
152 foreach (info, mFcListInfo) {
153 if (info->family().contains(family)) {
154 if (fullname.isEmpty())
156 if (info->fullname().contains(fullname))
163 InstalledFontInfo *FontConfigManager::fontInfo(int index) const
165 if (index >= 0 && index < mFcListInfo.count())
166 return mFcListInfo.at(index);
170 int FontConfigManager::count() const
172 return mFcListInfo.count();
175 void FontConfigManager::appendFontProperty(FontsConfigProperties *prop, const QStringList &familyList)
177 if (!mLocalFontsConf)
180 QStringList preferedFamilies(mLocalFontsConf->preferFamily());
181 preferedFamilies << prop->preferFamilies();
182 preferedFamilies.removeDuplicates();
183 foreach (const QString &family, preferedFamilies) {
184 if (prop->preferFamilies().contains(family)) {
185 mLocalFontsConf->appendPreferFamilyFor(family, prop->family());
186 foreach (const QString &f, familyList) {
187 mLocalFontsConf->appendPreferFamilyFor(family, f);
190 mLocalFontsConf->removePreferFamilyFor(family, prop->family());
191 foreach (const QString &f, familyList) {
192 mLocalFontsConf->removePreferFamilyFor(family, f);
197 QStringList keys = FontsConf::configKeys();
198 foreach (const QString &key, keys) {
199 if (prop->configValue(key) == FontsConfigProperties::Default) {
200 mLocalFontsConf->unsetMatchEditValueFor(key, prop->family());
201 foreach (const QString &f, familyList) {
202 mLocalFontsConf->unsetMatchEditValueFor(key, f);
205 mLocalFontsConf->setMatchEditValueFor(key, prop->family(), (prop->configValue(key) == FontsConfigProperties::True));
206 foreach (const QString &f, familyList) {
207 mLocalFontsConf->setMatchEditValueFor(key, f, (prop->configValue(key) == FontsConfigProperties::True));
212 emit fontsConfUpdated();
215 void FontConfigManager::appendFontProperty(InstalledFontInfo *fontInfo)
217 appendFontProperty(fontInfo->fontProperty(), fontInfo->family());
220 FontsConfigProperties *FontConfigManager::fontProperty(const QString &family, const QStringList &familyList) const
222 FontsConfigProperties *prop = new FontsConfigProperties(family);
223 QStringList families;
224 QStringList preferedFamilies = mLocalFontsConf->preferFamily();
225 foreach (const QString &key, preferedFamilies) {
226 families = mLocalFontsConf->preferFamilyFor(key);
227 if (families.contains(family))
228 prop->addPreferFamily(key);
229 foreach (const QString &f, familyList) {
230 if (families.contains(f))
231 prop->addPreferFamily(key);
235 QStringList keys = FontsConf::configKeys();
236 foreach (const QString &key, keys) {
237 QString val = mLocalFontsConf->matchEditValueFor(key, prop->family());
238 if (!val.isEmpty()) {
239 prop->setConfigValue(key, (QVariant(val).toBool() ? FontsConfigProperties::True : FontsConfigProperties::False));
242 foreach (const QString &f, familyList) {
243 mLocalFontsConf->matchEditValueFor(key, f);
244 if (!val.isEmpty()) {
245 prop->setConfigValue(key, (QVariant(val).toBool() ? FontsConfigProperties::True : FontsConfigProperties::False));
254 FontsConfigProperties *FontConfigManager::fontProperty(InstalledFontInfo *fontInfo) const
256 return fontProperty(fontInfo->enfamily(), fontInfo->family());
259 QString FontConfigManager::localFontsConf() const
261 QFile fp(mLocalFontsConfPath);
266 if (!fp.open(QIODevice::ReadOnly))
270 ts.setCodec("UTF-8");
271 QString buf = ts.readAll();
278 QStringList FontConfigManager::preferFamilyFor(const QString &family) const
280 return mLocalFontsConf->preferFamilyFor(family);
283 QStringList FontConfigManager::acceptFamilyFor(const QString &family) const
285 return mLocalFontsConf->acceptFamilyFor(family);
288 QStringList FontConfigManager::prependFamilyFor(const QString &family) const
290 return mLocalFontsConf->prependFamilyFor(family);
293 QStringList FontConfigManager::appendFamilyFor(const QString &family) const
295 return mLocalFontsConf->appendFamilyFor(family);
298 bool FontConfigManager::isConflict(const QString &family, const QStringList &lang) const
300 InstalledFontInfo *info = fontInfo(family);
304 QList<int> confListIndex;
305 for (int i = 0; i < mConflictFonts.count(); i++) {
306 const QStringList list(mConflictFonts.at(i));
307 foreach (const QString &f, list) {
308 if (lang.contains(f)) {
309 if (!confListIndex.contains(i))
316 if (confListIndex.isEmpty())
319 foreach (int i, confListIndex) {
320 QStringList confList = mConflictFonts.at(confListIndex.at(i));
321 foreach (const QString &f, confList) {
322 if (info->lang().contains(f)) {
331 QStringList FontConfigManager::systemPreferFamilyFor(const QString &family) const
333 return mSystemLocalConf->preferFamilyFor(family);
336 void FontConfigManager::importSystemSettings(const QString &family)
338 QStringList systemFamilyList = systemPreferFamilyFor(family);
340 QStringList installedFontLang;
341 foreach (InstalledFontInfo *info, mFcListInfo) {
342 if (info->systemFont())
344 installedFontLang << info->lang();
346 installedFontLang.sort();
347 installedFontLang.removeDuplicates();
349 emit startUpdateFontsConfig();
350 foreach (const QString &f, systemFamilyList) {
351 InstalledFontInfo *info = fontInfo(f);
355 if (isConflict(f, installedFontLang))
356 mLocalFontsConf->appendAcceptFamilyFor(family, f);
358 mLocalFontsConf->appendPrependFamilyFor(family, f);
360 emit endUpdateFontsConfig();
362 if (mLocalFontsConf->modified()) {
363 emit fontsConfUpdated();
367 void FontConfigManager::createRecommendedSettings()
369 QList<InstalledFontInfo*> sansSerifFonts;
370 QList<InstalledFontInfo*> serifFonts;
371 QList<InstalledFontInfo*> monospaceFonts;
372 QList<InstalledFontInfo*> monospaceSansSerifFonts;
373 QList<InstalledFontInfo*> monospaceSerifFonts;
374 QList<InstalledFontInfo*> unknownFonts;
376 foreach (InstalledFontInfo *info, mFcListInfo) {
377 if (info->systemFont())
379 if (maybeMonospaceFont(info)) {
380 if (maybeSansSerifFont(info))
381 monospaceSansSerifFonts << info;
382 else if (maybeSerifFont(info))
383 monospaceSerifFonts << info;
385 monospaceFonts << info;
386 } else if (maybeSansSerifFont(info))
387 sansSerifFonts << info;
388 else if (maybeSerifFont(info))
391 unknownFonts << info;
394 emit startUpdateFontsConfig();
399 foreach (InstalledFontInfo *info, monospaceFonts)
400 f << info->localefamily();
401 qDebug() << "Mono:" << f;
403 foreach (InstalledFontInfo *info, monospaceSansSerifFonts)
404 f << info->localefamily();
405 qDebug() << "Mono(Sans Serif):" << f;
407 foreach (InstalledFontInfo *info, monospaceSerifFonts)
408 f << info->localefamily();
409 qDebug() << "Mono(Serif):" << f;
411 foreach (InstalledFontInfo *info, sansSerifFonts)
412 f << info->localefamily();
413 qDebug() << "Sans Serif:" << f;
415 foreach (InstalledFontInfo *info, serifFonts)
416 f << info->localefamily();
417 qDebug() << "Serif:" << f;
419 foreach (InstalledFontInfo *info, unknownFonts)
420 f << info->localefamily();
421 qDebug() << "Unknown:" << f;
424 if (monospaceFonts.count())
425 foreach (InstalledFontInfo *info, monospaceFonts) {
426 addPreferFamily(MONOSPACE_DEF, info->enfamily());
427 addPreferFamily(MONOSPACE_DEF, info->localefamily());
429 else if (monospaceSansSerifFonts.count())
430 foreach (InstalledFontInfo *info, monospaceSansSerifFonts) {
431 addPreferFamily(MONOSPACE_DEF, info->enfamily());
432 addPreferFamily(MONOSPACE_DEF, info->localefamily());
434 else if (monospaceSerifFonts.count())
435 foreach (InstalledFontInfo *info, monospaceSerifFonts) {
436 addPreferFamily(MONOSPACE_DEF, info->enfamily());
437 addPreferFamily(MONOSPACE_DEF, info->localefamily());
440 if (sansSerifFonts.count())
441 foreach (InstalledFontInfo *info, sansSerifFonts) {
442 addPreferFamily(SANSSERIF_DEF, info->enfamily());
443 addPreferFamily(SANSSERIF_DEF, info->localefamily());
445 else if (monospaceSansSerifFonts.count())
446 foreach (InstalledFontInfo *info, monospaceSansSerifFonts) {
447 addPreferFamily(SANSSERIF_DEF, info->enfamily());
448 addPreferFamily(SANSSERIF_DEF, info->localefamily());
450 else if (unknownFonts.count())
451 foreach (InstalledFontInfo *info, unknownFonts) {
452 addPreferFamily(SANSSERIF_DEF, info->enfamily());
453 addPreferFamily(SANSSERIF_DEF, info->localefamily());
455 else if (monospaceFonts.count())
456 foreach (InstalledFontInfo *info, monospaceFonts) {
457 addPreferFamily(SANSSERIF_DEF, info->enfamily());
458 addPreferFamily(SANSSERIF_DEF, info->localefamily());
461 if (serifFonts.count())
462 foreach (InstalledFontInfo *info, serifFonts) {
463 addPreferFamily(SERIF_DEF, info->enfamily());
464 addPreferFamily(SERIF_DEF, info->localefamily());
466 else if (monospaceSerifFonts.count())
467 foreach (InstalledFontInfo *info, monospaceSerifFonts) {
468 addPreferFamily(SANSSERIF_DEF, info->enfamily());
469 addPreferFamily(SANSSERIF_DEF, info->localefamily());
471 else if (unknownFonts.count())
472 foreach (InstalledFontInfo *info, unknownFonts) {
473 addPreferFamily(SERIF_DEF, info->enfamily());
474 addPreferFamily(SERIF_DEF, info->localefamily());
476 else if (monospaceFonts.count())
477 foreach (InstalledFontInfo *info, monospaceFonts) {
478 addPreferFamily(SERIF_DEF, info->enfamily());
479 addPreferFamily(SERIF_DEF, info->localefamily());
482 foreach (const QString &f, FontsConf::genericFamilies())
483 importSystemSettings(f);
484 emit endUpdateFontsConfig();
486 emit fontsConfUpdated();
489 QStringList FontConfigManager::installableFamily(const QString &family, bool localOnly)
491 QStringList installedFont;
492 installedFont << prependFamilyFor(family);
493 installedFont << appendFamilyFor(family);
494 installedFont << preferFamilyFor(family);
495 installedFont << acceptFamilyFor(family);
497 QStringList familyList;
498 foreach (InstalledFontInfo *info, mFcListInfo) {
499 if (localOnly && info->systemFont())
502 foreach (const QString &f, info->family()) {
503 if (installedFont.contains(f)) {
509 familyList << info->localefamily();
512 familyList.removeDuplicates();
517 bool FontConfigManager::maybeSansSerifFont(InstalledFontInfo *info) const
519 foreach (const QString &f, info->family()) {
520 if (f.contains("gothic", Qt::CaseInsensitive) ||
521 f.contains("arial", Qt::CaseInsensitive) ||
522 f.contains("helvetica", Qt::CaseInsensitive) ||
523 f.contains("verdana", Qt::CaseInsensitive) ||
524 f.contains("sans", Qt::CaseInsensitive))
530 bool FontConfigManager::maybeSerifFont(InstalledFontInfo *info) const
532 foreach (const QString &f, info->family()) {
533 if (f.contains("mincho", Qt::CaseInsensitive) ||
534 f.contains("times", Qt::CaseInsensitive) ||
535 (f.contains("serif", Qt::CaseInsensitive) && !f.contains("sans", Qt::CaseInsensitive)))
541 bool FontConfigManager::maybeMonospaceFont(InstalledFontInfo *info) const
543 QFont font(info->enfamily());
544 if (font.exactMatch()) {
548 QFontMetrics fm(font);
549 int w = fm.width(QLatin1Char('A'));
550 if (fm.width(QLatin1Char('i')) == w && fm.width(QLatin1Char('X')) == w)
553 foreach (const QString &f, info->family()) {
554 if (f.contains("courier", Qt::CaseInsensitive) ||
555 f.contains("mono", Qt::CaseInsensitive))
561 void FontConfigManager::runFcCache()
563 QProcess *proc = new QProcess(this);
564 connect(proc, SIGNAL(finished(int)), SIGNAL(fcCacheFinished()));
565 connect(proc, SIGNAL(finished(int)), proc, SLOT(deleteLater()));
566 proc->start(FCBIN_PATH FCCACHE_COMMAND, QStringList() << FCCACHE_OPTION);
569 void FontConfigManager::readFcList()
571 QProcess *proc = new QProcess(this);
572 proc->start(FCBIN_PATH FCLIST_COMMAND, QStringList() << FCLIST_OPTION);
573 if (!proc->waitForStarted())
576 if (!proc->waitForFinished())
579 qDeleteAll(mFcListInfo);
582 QByteArray buf = proc->readAllStandardOutput();
583 QByteArray errbuf = proc->readAllStandardError();
584 if (!errbuf.isEmpty())
585 qWarning() << errbuf;
586 Q_ASSERT(errbuf.isEmpty());
588 static QByteArray emptyLine("\n");
589 QBuffer buffer(&buf);
591 buffer.open(QIODevice::ReadOnly);
592 Q_ASSERT(buffer.isOpen());
593 while (!buffer.atEnd()) {
594 QByteArray linebuf = buffer.readLine();
595 if (linebuf.isEmpty() || linebuf == emptyLine) {
596 if (!fibuf.isEmpty()) {
597 InstalledFontInfo *info = new InstalledFontInfo(fibuf, mLocalFontPath, this);
598 info->setLocale(mLang);
600 mFcListInfo.append(info);
603 info->setFontProperty(fontProperty(info));
610 Q_ASSERT(fibuf.isEmpty());
613 qSort(mFcListInfo.begin(), mFcListInfo.end(), InstalledFontInfo::compare);
615 emit fontListUpdated();
620 void FontConfigManager::readFontsConf()
622 mLocalFontsConf->load(mLocalFontsConfPath);
623 emit fontsConfUpdated();
626 void FontConfigManager::saveFontsConf()
628 if (!mLocalFontsConf || mLocalFontsConfPath.isEmpty())
631 bool check = localFontsConfExists();
632 if (isEmptyLocalFontsConf()) {
634 QFile::remove(mLocalFontsConfPath);
635 emit localFontsConfExistsChanged();
638 mLocalFontsConf->save(mLocalFontsConfPath);
640 emit localFontsConfExistsChanged();
645 void FontConfigManager::resetFontsConf()
647 if (!mLocalFontsConf)
649 mLocalFontsConf->initFontsConf();
650 emit fontsConfUpdated();
653 void FontConfigManager::addPreferFamily(const QString &family, const QString &value)
655 mLocalFontsConf->appendPreferFamilyFor(family, value);
656 emit fontsConfUpdated();
659 void FontConfigManager::addAcceptFamily(const QString &family, const QString &value)
661 mLocalFontsConf->appendAcceptFamilyFor(family, value);
662 emit fontsConfUpdated();
665 void FontConfigManager::addPrependFamily(const QString &family, const QString &value)
667 mLocalFontsConf->appendPrependFamilyFor(family, value);
668 emit fontsConfUpdated();
671 void FontConfigManager::addAppendFamily(const QString &family, const QString &value)
673 mLocalFontsConf->appendAppendFamilyFor(family, value);
674 emit fontsConfUpdated();
677 void FontConfigManager::removePreferFamily(const QString &family, const QString &value)
679 mLocalFontsConf->removePreferFamilyFor(family, value);
680 emit fontsConfUpdated();
683 void FontConfigManager::removeAcceptFamily(const QString &family, const QString &value)
685 mLocalFontsConf->removeAcceptFamilyFor(family, value);
686 emit fontsConfUpdated();
689 void FontConfigManager::removePrependFamily(const QString &family, const QString &value)
691 mLocalFontsConf->removePrependFamilyFor(family, value);
692 emit fontsConfUpdated();
695 void FontConfigManager::removeAppendFamily(const QString &family, const QString &value)
697 mLocalFontsConf->removeAppendFamilyFor(family, value);
698 emit fontsConfUpdated();