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 "fontsconf.h"
41 #include "fontconfigdefs.h"
42 #include "fontsconfelement.h"
45 #include <QTextStream>
46 #include <QXmlStreamReader>
49 Initialized, Ready, InvalidConfig, UnknownState
52 static inline QString boolString(bool val)
54 return val ? TRUE_DEF : FALSE_DEF;
57 FontsConf::FontsConf(QObject *parent) :
58 QObject(parent), mModified(false), mValid(true), mHasUnknownConfig(false)
63 bool FontsConf::isEmpty() const
65 return !mElements || (mElements->count() == 0);
68 bool FontsConf::modified() const
73 void FontsConf::setModified(bool value)
78 bool FontsConf::hasUnknownConfig() const
80 return mHasUnknownConfig;
83 bool FontsConf::isValid() const
88 void FontsConf::initFontsConf()
92 FontsConfElementPointer rootElem(new FontsConfElement(FONTCONFIG_DEF, this));
96 bool FontsConf::parse(const QByteArray &buf)
101 QXmlStreamReader xml(buf);
103 FCState state = Initialized;
105 bool hasUnknownConfig = false;
107 FontsConfElementPointer elem;
108 while (!xml.atEnd()) {
109 QXmlStreamReader::TokenType token = xml.readNext();
110 if (token == QXmlStreamReader::DTD) {
111 if (xml.dtdName() != FONTCONFIG_DEF || xml.dtdSystemId() != "fonts.dtd") {
112 state = InvalidConfig;
114 } else if (token == QXmlStreamReader::StartElement) {
115 QString elemName(xml.name().toString());
118 if (elemName == FONTCONFIG_DEF) {
120 elem = FontsConfElementPointer(new FontsConfElement(elemName, this));
121 bool check = elem->parse(xml);
123 state = UnknownState;
125 state = InvalidConfig;
131 } else if (token == QXmlStreamReader::EndElement) {
132 QString elemName(xml.name().toString());
135 if (state == InvalidConfig)
136 qDebug("Invalid Config!!!");
138 if (state == InvalidConfig)
140 else if (state == UnknownState)
141 hasUnknownConfig = true;
144 if (xml.error() != QXmlStreamReader::NoError)
145 state = InvalidConfig;
147 if (state == Ready) {
150 mHasUnknownConfig = hasUnknownConfig;
153 return !(state == InvalidConfig);
156 QStringList FontsConf::preferFamily() const
159 return QStringList();
161 QStringList familyList;
164 while ((index = mElements->indexOf(ALIAS_DEF, index)) >= 0) {
165 FontsConfElementPointer aliasElem = mElements->childElementAt(index++);
166 FontsConfElementPointer familyElem = aliasElem->childElementOf(FAMILY_DEF);
167 if (!familyElem || familyElem->text().isEmpty())
169 familyList << familyElem->text();
171 // familyList.removeDuplicates();
175 QStringList FontsConf::aliasFamilyFor(const QString &family, const QString &mode) const
178 while ((index = mElements->indexOf(ALIAS_DEF, index)) >= 0) {
179 FontsConfElementPointer aliasElem = mElements->childElementAt(index++);
180 FontsConfElementPointer familyElem = aliasElem->childElementOf(FAMILY_DEF);
183 if (familyElem->text() != family)
185 FontsConfElementPointer targetElem = aliasElem->childElementOf(mode);
187 return QStringList();
188 QStringList familyList = textElementList(targetElem, FAMILY_DEF);
192 return QStringList();
195 void FontsConf::appendAliasFamilyFor(const QString &family, const QString &mode, const QString &value)
198 while ((index = mElements->indexOf(ALIAS_DEF, index)) >= 0) {
199 FontsConfElementPointer aliasElem = mElements->childElementAt(index++);
200 bool check = appendFamilyToAlias(aliasElem, family, mode, value);
205 FontsConfElementPointer aliasElem = aliasElementFor(family);
206 mElements->addChildElement(aliasElem);
207 bool check = appendFamilyToAlias(aliasElem, family, mode, value);
211 void FontsConf::insertAliasFamilyFor(const QString &family, const QString &mode, const QString &value, int idx)
214 while ((index = mElements->indexOf(ALIAS_DEF, index)) >= 0) {
215 FontsConfElementPointer aliasElem = mElements->childElementAt(index++);
216 bool check = insertFamilyToAlias(aliasElem, family, mode, value, idx);
221 FontsConfElementPointer aliasElem = aliasElementFor(family);
222 mElements->addChildElement(aliasElem);
223 bool check = appendFamilyToAlias(aliasElem, family, mode, value);
227 void FontsConf::removeAliasFamilyFor(const QString &family, const QString &mode, const QString &value)
230 while ((index = mElements->indexOf(ALIAS_DEF, index)) >= 0) {
231 FontsConfElementPointer aliasElem = mElements->childElementAt(index++);
233 FontsConfElementPointer familyElem = aliasElem->childElementOf(FAMILY_DEF);
234 if (!familyElem || familyElem->text() != family)
237 FontsConfElementPointer targetElem = aliasElem->childElementOf(mode);
238 if (!targetElem || targetElem->count() == 0)
242 while ((findex = targetElem->indexOf(FAMILY_DEF, findex)) >= 0) {
243 if (targetElem->childElementAt(findex)->text() == value) {
244 targetElem->removeAt(findex);
251 if (targetElem->count() == 0) {
252 aliasElem->removeOne(targetElem);
253 if (aliasElem->count() == 1) {
254 mElements->removeOne(aliasElem);
260 QStringList FontsConf::preferFamilyFor(const QString &family) const
262 return aliasFamilyFor(family, PREFER_DEF);
265 void FontsConf::appendPreferFamilyFor(const QString &family, const QString &value)
267 appendAliasFamilyFor(family, PREFER_DEF, value);
270 void FontsConf::insertPreferFamilyFor(const QString &family, const QString &value, int index)
272 insertAliasFamilyFor(family, PREFER_DEF, value, index);
275 void FontsConf::removePreferFamilyFor(const QString &family, const QString &value)
277 removeAliasFamilyFor(family, PREFER_DEF, value);
280 QStringList FontsConf::acceptFamilyFor(const QString &family) const
282 return aliasFamilyFor(family, ACCEPT_DEF);
285 void FontsConf::appendAcceptFamilyFor(const QString &family, const QString &value)
287 appendAliasFamilyFor(family, ACCEPT_DEF, value);
290 void FontsConf::insertAcceptFamilyFor(const QString &family, const QString &value, int index)
292 insertAliasFamilyFor(family, ACCEPT_DEF, value, index);
295 void FontsConf::removeAcceptFamilyFor(const QString &family, const QString &value)
297 removeAliasFamilyFor(family, ACCEPT_DEF, value);
300 QStringList FontsConf::matchFamilyFor(const QString &config, bool val) const
303 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
304 FontsConfElementPointer matchElem = mElements->childElementAt(index++);
306 bool check = isMatchElementFor(matchElem, config, QString(), boolString(val));
310 FontsConfElementPointer testElem = matchElem->childElementOf(TEST_DEF);
314 QStringList familyList = textElementList(testElem, STRING_DEF);
318 return QStringList();
321 QString FontsConf::matchEditValueFor(const QString &config, const QString &family) const
324 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
325 FontsConfElementPointer matchElem = mElements->childElementAt(index++);
327 bool check = isMatchElementFor(matchElem, config, family);
331 FontsConfElementPointer editElem = matchElem->childElementOf(EDIT_DEF);
335 FontsConfElementPointer boolElem = editElem->childElementOf(BOOL_DEF);
339 return boolElem->text();
345 void FontsConf::setMatchEditValueFor(const QString &config, const QString &family, bool val)
347 FontsConfElementPointer matchElem;
349 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
350 FontsConfElementPointer elem = mElements->childElementAt(index++);
351 bool check = isMatchElementFor(elem, config, QString(), boolString(val));
359 matchElem = matchElementFor(config, val);
360 mElements->addChildElement(matchElem);
364 FontsConfElementPointer testElem = matchElem->childElementOf(TEST_DEF);
365 QStringList familyList = textElementList(testElem, STRING_DEF);
366 if (familyList.contains(family))
369 FontsConfElementPointer stringElem(new FontsConfElement(STRING_DEF));
370 stringElem->setText(family);
371 testElem->addChildElement(stringElem);
375 void FontsConf::unsetMatchEditValueFor(const QString &config, const QString &family)
378 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
379 FontsConfElementPointer matchElem = mElements->childElementAt(index++);
381 bool check = isMatchElementFor(matchElem, config, family);
385 FontsConfElementPointer testElem = matchElem->childElementOf(TEST_DEF);
387 while ((sindex = testElem->indexOf(STRING_DEF, sindex)) >= 0) {
388 FontsConfElementPointer stringElem = testElem->childElementAt(sindex++);
389 if (stringElem->text() == family) {
390 testElem->removeAt(--sindex);
395 if (testElem->count() == 0) {
396 mElements->removeAt(--index);
402 QStringList FontsConf::patternFamilyFor(const QString &family, const QString &mode) const
405 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
406 FontsConfElementPointer matchElem = mElements->childElementAt(index++);
407 bool check = isPatternElementFor(matchElem, family, mode);
410 FontsConfElementPointer editElem = getEditPatternElementFor(matchElem, family, mode);
411 return textElementList(editElem, STRING_DEF);
413 return QStringList();
416 void FontsConf::appendPatternFamilyFor(const QString &family, const QString &mode, const QString &value)
418 insertPatternFamilyFor(family, mode, value, -1);
421 void FontsConf::insertPatternFamilyFor(const QString &family, const QString &mode, const QString &value, int idx)
423 FontsConfElementPointer matchElem;
426 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
427 FontsConfElementPointer elem = mElements->childElementAt(index++);
428 bool check = isPatternElementFor(elem, family, mode);
437 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
438 FontsConfElementPointer elem = mElements->childElementAt(index++);
439 bool check = isPatternElementFor(elem, family);
448 matchElem = patternElementFor(family);
449 mElements->addChildElement(matchElem);
453 FontsConfElementPointer editElem = getEditPatternElementFor(matchElem, family, mode);
455 editElem = FontsConfElementPointer(new FontsConfElement(EDIT_DEF));
456 editElem->setAttribute(NAME_DEF, FAMILY_DEF);
457 editElem->setAttribute(MODE_DEF, mode);
458 editElem->setAttribute(BINDING_DEF, STRONG_DEF);
459 matchElem->addChildElement(editElem);
463 QStringList familyList = textElementList(editElem, STRING_DEF);
464 if (familyList.contains(value))
467 FontsConfElementPointer stringElem(new FontsConfElement(STRING_DEF));
468 stringElem->setText(value);
470 editElem->addChildElement(stringElem);
472 editElem->insertChildElement(stringElem, idx);
476 void FontsConf::removePatternFamilyFor(const QString &family, const QString &mode, const QString &value)
478 FontsConfElementPointer matchElem;
481 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
482 FontsConfElementPointer elem = mElements->childElementAt(index++);
483 bool check = isPatternElementFor(elem, family, mode);
493 FontsConfElementPointer editElem = getEditPatternElementFor(matchElem, family, mode);
498 while ((sindex = editElem->indexOf(STRING_DEF, sindex)) >= 0) {
499 FontsConfElementPointer stringElem = editElem->childElementAt(sindex++);
500 if (stringElem->text() == value) {
501 editElem->removeAt(--sindex);
506 if (editElem->count() == 0) {
508 matchElem->removeOne(editElem);
509 FontsConfElementList editElemList = matchElem->findChildrenElements(EDIT_DEF);
510 if (editElemList.isEmpty())
511 mElements->removeOne(matchElem);
516 QStringList FontsConf::prependFamilyFor(const QString &family) const
518 return patternFamilyFor(family, PREPEND_DEF);
521 void FontsConf::appendPrependFamilyFor(const QString &family, const QString &value)
523 appendPatternFamilyFor(family, PREPEND_DEF, value);
526 void FontsConf::insertPrependFamilyFor(const QString &family, const QString &value, int index)
528 insertPatternFamilyFor(family, PREPEND_DEF, value, index);
531 void FontsConf::removePrependFamilyFor(const QString &family, const QString &value)
533 removePatternFamilyFor(family, PREPEND_DEF, value);
536 QStringList FontsConf::appendFamilyFor(const QString &family) const
538 return patternFamilyFor(family, APPEND_DEF);
541 void FontsConf::appendAppendFamilyFor(const QString &family, const QString &value)
543 appendPatternFamilyFor(family, APPEND_DEF, value);
546 void FontsConf::insertAppendFamilyFor(const QString &family, const QString &value, int index)
548 insertPatternFamilyFor(family, APPEND_DEF, value, index);
551 void FontsConf::removeAppendFamilyFor(const QString &family, const QString &value)
553 removePatternFamilyFor(family, APPEND_DEF, value);
556 void FontsConf::copy(FontsConf *src)
559 mValid = src->mValid;
560 mHasUnknownConfig = src->mHasUnknownConfig;
561 mElements = src->mElements;
564 QStringList FontsConf::genericFamilies()
566 static QStringList families;
567 if (families.isEmpty())
568 families << SANSSERIF_DEF << SERIF_DEF << MONOSPACE_DEF << SYSTEM_DEF;
572 QStringList FontsConf::configKeys()
574 static QStringList keys;
576 keys << EMBEDDEDBITMAP_DEF << HINTING_DEF << ANTIALIAS_DEF << AUTOHINT_DEF;
580 void FontsConf::load(const QString &path)
586 fp.open(QIODevice::ReadOnly);
587 if (!fp.isOpen() || fp.error() != QFile::NoError)
590 QByteArray buf = fp.readAll();
597 void FontsConf::save(const QString &path)
600 if (!mElements && fp.exists()) {
607 QXmlStreamWriter xml(&buf);
609 xml.setAutoFormatting(true);
610 xml.setAutoFormattingIndent(8);
612 xml.writeStartDocument();
614 xml.writeDTD("<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>");
617 mElements->save(xml);
619 xml.writeEndDocument();
622 fp.open(QIODevice::WriteOnly);
627 ts.setCodec("UTF-8");
635 bool FontsConf::appendFamilyToAlias(FontsConfElementPointer aliasElem, const QString &family, const QString &mode, const QString &value)
637 return insertFamilyToAlias(aliasElem, family, mode, value, -1);
640 bool FontsConf::insertFamilyToAlias(FontsConfElementPointer aliasElem, const QString &family, const QString &mode, const QString &value, int index)
642 if (aliasElem->type() != ALIAS_DEF)
645 FontsConfElementPointer familyElem = aliasElem->childElementOf(FAMILY_DEF);
646 if (!familyElem || familyElem->text() != family)
649 FontsConfElementPointer targetElem = aliasElem->childElementOf(mode);
651 targetElem = FontsConfElementPointer(new FontsConfElement(mode));
652 aliasElem->addChildElement(targetElem);
657 while ((findex = targetElem->indexOf(FAMILY_DEF, findex)) >= 0) {
658 if (targetElem->childElementAt(findex)->text() == value)
663 FontsConfElementPointer valueElem(new FontsConfElement(FAMILY_DEF));
664 valueElem->setText(value);
666 targetElem->addChildElement(valueElem);
668 targetElem->insertChildElement(valueElem, index);
674 bool FontsConf::isMatchElementFor(FontsConfElementPointer matchElem, const QString &config, const QString &family, const QString &val) const
676 if (!matchElem->hasAttribute(TARGET_DEF) || matchElem->value(TARGET_DEF) != FONT_DEF)
679 FontsConfElementList testElemList = matchElem->findChildrenElements(TEST_DEF);
680 if (testElemList.count() != 1)
683 FontsConfElementPointer testElem = testElemList.at(0);
684 if (!testElem->hasAttribute(NAME_DEF) || testElem->value(NAME_DEF) != FAMILY_DEF)
686 if ((testElem->hasAttribute(QUAL_DEF) && testElem->value(QUAL_DEF) != ANY_DEF) ||
687 (testElem->hasAttribute(COMPARE_DEF) && testElem->value(COMPARE_DEF) != EQ_DEF) ||
688 (testElem->hasAttribute(TARGET_DEF) && testElem->value(TARGET_DEF) != DEFAULT_DEF))
691 if (!family.isEmpty()) {
692 if (testElem->count() == 0 && testElem->text() != family)
694 QStringList familyList = textElementList(testElem, STRING_DEF);
695 if (!familyList.contains(family))
699 FontsConfElementList editElemList = matchElem->findChildrenElements(EDIT_DEF);
700 if (editElemList.count() != 1)
703 FontsConfElementPointer editElem = editElemList.at(0);
704 if ((!editElem->hasAttribute(NAME_DEF) || editElem->value(NAME_DEF) != config) ||
705 (editElem->hasAttribute(MODE_DEF) && editElem->value(MODE_DEF) != ASSIGN_DEF))
708 FontsConfElementList boolElemList = editElem->findChildrenElements(BOOL_DEF);
709 if (boolElemList.count() != 1) {
713 if (!val.isEmpty()) {
714 FontsConfElementPointer boolElem = boolElemList.at(0);
715 if (boolElem->text() != val)
722 bool FontsConf::isPatternElementFor(FontsConfElementPointer matchElem, const QString &family, const QString &mode) const
724 if (matchElem->hasAttribute(TARGET_DEF) && matchElem->value(TARGET_DEF) != PATTERN_DEF)
727 FontsConfElementList testElemList = matchElem->findChildrenElements(TEST_DEF);
728 if (testElemList.count() != 1)
731 FontsConfElementPointer testElem = testElemList.at(0);
732 if (testElem->hasAttribute(NAME_DEF) && testElem->value(NAME_DEF) != FAMILY_DEF)
734 if ((testElem->hasAttribute(COMPARE_DEF) && testElem->value(COMPARE_DEF) != EQ_DEF) ||
735 (testElem->hasAttribute(TARGET_DEF) && testElem->value(TARGET_DEF) != DEFAULT_DEF))
738 if (testElem->count() == 0) {
739 if (testElem->text().isEmpty() || testElem->text() != family)
742 QStringList familyList = textElementList(testElem, STRING_DEF);
743 if (familyList.count() != 1 || !familyList.contains(family))
751 while ((eindex = matchElem->indexOf(EDIT_DEF, eindex)) >= 0) {
752 FontsConfElementPointer editElem = matchElem->childElementAt(eindex++);
754 if ((editElem->hasAttribute(NAME_DEF) && editElem->value(NAME_DEF) == FAMILY_DEF) &&
755 (editElem->hasAttribute(MODE_DEF) && editElem->value(MODE_DEF) == mode))
763 FontsConfElementPointer FontsConf::getEditPatternElementFor(FontsConfElementPointer matchElem, const QString &family, const QString &mode) const
767 while ((eindex = matchElem->indexOf(EDIT_DEF, eindex)) >= 0) {
768 FontsConfElementPointer editElem = matchElem->childElementAt(eindex++);
770 if ((editElem->hasAttribute(NAME_DEF) && editElem->value(NAME_DEF) == FAMILY_DEF) &&
771 (editElem->hasAttribute(MODE_DEF) && editElem->value(MODE_DEF) == mode))
774 return FontsConfElementPointer();
777 bool FontsConf::containsTextElement(FontsConfElementPointer elem, const QString &tag, const QString &text) const
780 while ((index = elem->indexOf(tag, index)) >= 0) {
781 FontsConfElementPointer e = elem->childElementAt(index++);
782 if (e->text() == text)
788 QStringList FontsConf::textElementList(FontsConfElementPointer elem, const QString &tag) const
791 return QStringList();
794 while ((index = elem->indexOf(tag, index)) >= 0) {
795 FontsConfElementPointer e = elem->childElementAt(index++);
796 if (!e->text().isEmpty())
802 FontsConfElementPointer FontsConf::aliasElementFor(const QString &family) const
804 FontsConfElementPointer aliasElem(new FontsConfElement(ALIAS_DEF));
805 FontsConfElementPointer familyElem(new FontsConfElement(FAMILY_DEF));
806 familyElem->setText(family);
807 aliasElem->addChildElement(familyElem);
811 FontsConfElementPointer FontsConf::matchElementFor(const QString &config, bool val) const
813 FontsConfElementPointer matchElem = FontsConfElementPointer(new FontsConfElement(MATCH_DEF));
814 matchElem->setAttribute(TARGET_DEF, FONT_DEF);
816 FontsConfElementPointer testElem(new FontsConfElement(TEST_DEF));
817 testElem->setAttribute(QUAL_DEF, ANY_DEF);
818 testElem->setAttribute(NAME_DEF, FAMILY_DEF);
819 matchElem->addChildElement(testElem);
821 FontsConfElementPointer editElem(new FontsConfElement(EDIT_DEF));
822 editElem->setAttribute(NAME_DEF, config);
823 editElem->setAttribute(MODE_DEF, ASSIGN_DEF);
824 matchElem->addChildElement(editElem);
826 FontsConfElementPointer boolElem(new FontsConfElement(BOOL_DEF));
827 boolElem->setText(boolString(val));
828 editElem->addChildElement(boolElem);
833 FontsConfElementPointer FontsConf::patternElementFor(const QString &family) const
835 FontsConfElementPointer matchElem = FontsConfElementPointer(new FontsConfElement(MATCH_DEF));
836 matchElem->setAttribute(TARGET_DEF, PATTERN_DEF);
838 FontsConfElementPointer testElem(new FontsConfElement(TEST_DEF));
839 testElem->setAttribute(QUAL_DEF, ANY_DEF);
840 testElem->setAttribute(NAME_DEF, FAMILY_DEF);
841 matchElem->addChildElement(testElem);
843 FontsConfElementPointer stringElem(new FontsConfElement(STRING_DEF));
844 stringElem->setText(family);
845 testElem->addChildElement(stringElem);