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 <QXmlStreamReader>
50 Initialized, Ready, InvalidConfig, UnknownState
53 static inline QString boolString(bool val)
55 return val ? TRUE_DEF : FALSE_DEF;
58 FontsConf::FontsConf(QObject *parent) :
59 QObject(parent), mModified(false), mHasUnknownConfig(false)
64 bool FontsConf::isEmpty() const
66 return !mElements || (mElements->count() == 0);
69 bool FontsConf::modified() const
74 bool FontsConf::hasUnknownConfig() const
76 return mHasUnknownConfig;
79 void FontsConf::initFontsConf()
83 FontsConfElementPointer rootElem(new FontsConfElement(FONTCONFIG_DEF, this));
87 bool FontsConf::parse(const QByteArray &buf)
92 QXmlStreamReader xml(buf);
94 FCState state = Initialized;
96 bool hasUnknownConfig = false;
98 FontsConfElementPointer elem;
99 while (!xml.atEnd()) {
100 QXmlStreamReader::TokenType token = xml.readNext();
101 if (token == QXmlStreamReader::DTD) {
102 if (xml.dtdName() != FONTCONFIG_DEF || xml.dtdSystemId() != "fonts.dtd") {
103 state = InvalidConfig;
105 } else if (token == QXmlStreamReader::StartElement) {
106 QString elemName(xml.name().toString());
109 if (elemName == FONTCONFIG_DEF) {
111 elem = FontsConfElementPointer(new FontsConfElement(elemName, this));
112 bool check = elem->parse(xml);
114 state = UnknownState;
116 state = InvalidConfig;
122 } else if (token == QXmlStreamReader::EndElement) {
123 QString elemName(xml.name().toString());
126 if (state == InvalidConfig)
127 qDebug("Invalid Config!!!");
129 if (state == InvalidConfig)
131 else if (state == UnknownState)
132 hasUnknownConfig = true;
135 if (xml.error() != QXmlStreamReader::NoError)
136 state = InvalidConfig;
138 if (state == Ready) {
141 mHasUnknownConfig = hasUnknownConfig;
144 return !(state == InvalidConfig);
147 QStringList FontsConf::preferFamily() const
150 return QStringList();
152 QStringList familyList;
155 while ((index = mElements->indexOf(ALIAS_DEF, index)) >= 0) {
156 FontsConfElementPointer aliasElem = mElements->childElementAt(index++);
157 FontsConfElementPointer familyElem = aliasElem->childElementOf(FAMILY_DEF);
158 if (!familyElem || familyElem->text().isEmpty())
160 familyList << familyElem->text();
162 // familyList.removeDuplicates();
166 QStringList FontsConf::aliasFamilyFor(const QString &family, const QString &mode) const
169 while ((index = mElements->indexOf(ALIAS_DEF, index)) >= 0) {
170 FontsConfElementPointer aliasElem = mElements->childElementAt(index++);
171 FontsConfElementPointer familyElem = aliasElem->childElementOf(FAMILY_DEF);
174 if (familyElem->text() != family)
176 FontsConfElementPointer targetElem = aliasElem->childElementOf(mode);
178 return QStringList();
179 QStringList familyList = textElementList(targetElem, FAMILY_DEF);
183 return QStringList();
186 void FontsConf::appendAliasFamilyFor(const QString &family, const QString &mode, const QString &value)
189 while ((index = mElements->indexOf(ALIAS_DEF, index)) >= 0) {
190 FontsConfElementPointer aliasElem = mElements->childElementAt(index++);
191 bool check = appendFamilyToAlias(aliasElem, family, mode, value);
196 FontsConfElementPointer aliasElem = aliasElementFor(family);
197 mElements->addChildElement(aliasElem);
198 bool check = appendFamilyToAlias(aliasElem, family, mode, value);
202 void FontsConf::removeAliasFamilyFor(const QString &family, const QString &mode, const QString &value)
205 while ((index = mElements->indexOf(ALIAS_DEF, index)) >= 0) {
206 FontsConfElementPointer aliasElem = mElements->childElementAt(index++);
208 FontsConfElementPointer familyElem = aliasElem->childElementOf(FAMILY_DEF);
209 if (!familyElem || familyElem->text() != family)
212 FontsConfElementPointer targetElem = aliasElem->childElementOf(mode);
213 if (!targetElem || targetElem->count() == 0)
217 while ((findex = targetElem->indexOf(FAMILY_DEF, findex)) >= 0) {
218 if (targetElem->childElementAt(findex)->text() == value) {
219 targetElem->removeAt(findex);
226 if (targetElem->count() == 0) {
227 aliasElem->removeOne(targetElem);
228 if (aliasElem->count() == 1) {
229 mElements->removeOne(aliasElem);
235 QStringList FontsConf::preferFamilyFor(const QString &family) const
237 return aliasFamilyFor(family, PREFER_DEF);
240 void FontsConf::appendPreferFamilyFor(const QString &family, const QString &value)
242 appendAliasFamilyFor(family, PREFER_DEF, value);
245 void FontsConf::removePreferFamilyFor(const QString &family, const QString &value)
247 removeAliasFamilyFor(family, PREFER_DEF, value);
250 QStringList FontsConf::acceptFamilyFor(const QString &family) const
252 return aliasFamilyFor(family, ACCEPT_DEF);
255 void FontsConf::appendAcceptFamilyFor(const QString &family, const QString &value)
257 appendAliasFamilyFor(family, ACCEPT_DEF, value);
260 void FontsConf::removeAcceptFamilyFor(const QString &family, const QString &value)
262 removeAliasFamilyFor(family, ACCEPT_DEF, value);
265 QStringList FontsConf::matchFamilyFor(const QString &config, bool val) const
268 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
269 FontsConfElementPointer matchElem = mElements->childElementAt(index++);
271 bool check = isMatchElementFor(matchElem, config, QString(), boolString(val));
275 FontsConfElementPointer testElem = matchElem->childElementOf(TEST_DEF);
279 QStringList familyList = textElementList(testElem, STRING_DEF);
283 return QStringList();
286 QString FontsConf::matchEditValueFor(const QString &config, const QString &family) const
289 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
290 FontsConfElementPointer matchElem = mElements->childElementAt(index++);
292 bool check = isMatchElementFor(matchElem, config, family);
296 FontsConfElementPointer editElem = matchElem->childElementOf(EDIT_DEF);
300 FontsConfElementPointer boolElem = editElem->childElementOf(BOOL_DEF);
304 return boolElem->text();
310 void FontsConf::setMatchEditValueFor(const QString &config, const QString &family, bool val)
312 FontsConfElementPointer matchElem;
314 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
315 FontsConfElementPointer elem = mElements->childElementAt(index++);
316 bool check = isMatchElementFor(elem, config, QString(), boolString(val));
324 matchElem = matchElementFor(config, val);
325 mElements->addChildElement(matchElem);
329 FontsConfElementPointer testElem = matchElem->childElementOf(TEST_DEF);
330 QStringList familyList = textElementList(testElem, STRING_DEF);
331 if (familyList.contains(family))
334 FontsConfElementPointer stringElem(new FontsConfElement(STRING_DEF));
335 stringElem->setText(family);
336 testElem->addChildElement(stringElem);
340 void FontsConf::unsetMatchEditValueFor(const QString &config, const QString &family)
343 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
344 FontsConfElementPointer matchElem = mElements->childElementAt(index++);
346 bool check = isMatchElementFor(matchElem, config, family);
350 FontsConfElementPointer testElem = matchElem->childElementOf(TEST_DEF);
352 while ((sindex = testElem->indexOf(STRING_DEF, sindex)) >= 0) {
353 FontsConfElementPointer stringElem = testElem->childElementAt(sindex++);
354 if (stringElem->text() == family) {
355 testElem->removeAt(--sindex);
360 if (testElem->count() == 0) {
361 mElements->removeAt(--index);
367 QStringList FontsConf::patternFamilyFor(const QString &family, const QString &mode) const
370 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
371 FontsConfElementPointer matchElem = mElements->childElementAt(index++);
372 bool check = isPatternElementFor(matchElem, family, mode);
375 FontsConfElementPointer editElem = getEditPatternElementFor(matchElem, family, mode);
376 return textElementList(editElem, STRING_DEF);
378 return QStringList();
381 void FontsConf::appendPatternFamilyFor(const QString &family, const QString &mode, const QString &value)
383 FontsConfElementPointer matchElem;
386 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
387 FontsConfElementPointer elem = mElements->childElementAt(index++);
388 bool check = isPatternElementFor(elem, family, mode);
397 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
398 FontsConfElementPointer elem = mElements->childElementAt(index++);
399 bool check = isPatternElementFor(elem, family);
408 matchElem = patternElementFor(family);
409 mElements->addChildElement(matchElem);
413 FontsConfElementPointer editElem = getEditPatternElementFor(matchElem, family, mode);
415 editElem = FontsConfElementPointer(new FontsConfElement(EDIT_DEF));
416 editElem->setAttribute(NAME_DEF, FAMILY_DEF);
417 editElem->setAttribute(MODE_DEF, mode);
418 editElem->setAttribute(BINDING_DEF, STRONG_DEF);
419 matchElem->addChildElement(editElem);
423 QStringList familyList = textElementList(editElem, STRING_DEF);
424 if (familyList.contains(value))
427 FontsConfElementPointer stringElem(new FontsConfElement(STRING_DEF));
428 stringElem->setText(value);
429 editElem->addChildElement(stringElem);
433 void FontsConf::removePatternFamilyFor(const QString &family, const QString &mode, const QString &value)
435 FontsConfElementPointer matchElem;
438 while ((index = mElements->indexOf(MATCH_DEF, index)) >= 0) {
439 FontsConfElementPointer elem = mElements->childElementAt(index++);
440 bool check = isPatternElementFor(elem, family, mode);
450 FontsConfElementPointer editElem = getEditPatternElementFor(matchElem, family, mode);
455 while ((sindex = editElem->indexOf(STRING_DEF, sindex)) >= 0) {
456 FontsConfElementPointer stringElem = editElem->childElementAt(sindex++);
457 if (stringElem->text() == value) {
458 editElem->removeAt(--sindex);
463 if (editElem->count() == 0) {
465 matchElem->removeOne(editElem);
466 FontsConfElementList editElemList = matchElem->findChildrenElements(EDIT_DEF);
467 if (editElemList.isEmpty())
468 mElements->removeOne(matchElem);
473 QStringList FontsConf::prependFamilyFor(const QString &family) const
475 return patternFamilyFor(family, PREPEND_DEF);
478 void FontsConf::appendPrependFamilyFor(const QString &family, const QString &value)
480 appendPatternFamilyFor(family, PREPEND_DEF, value);
483 void FontsConf::removePrependFamilyFor(const QString &family, const QString &value)
485 removePatternFamilyFor(family, PREPEND_DEF, value);
488 QStringList FontsConf::appendFamilyFor(const QString &family) const
490 return patternFamilyFor(family, APPEND_DEF);
493 void FontsConf::appendAppendFamilyFor(const QString &family, const QString &value)
495 appendPatternFamilyFor(family, APPEND_DEF, value);
498 void FontsConf::removeAppendFamilyFor(const QString &family, const QString &value)
500 removePatternFamilyFor(family, APPEND_DEF, value);
503 QStringList FontsConf::genericFamilies()
505 static QStringList families;
506 if (families.isEmpty())
507 families << SANSSERIF_DEF << SERIF_DEF << MONOSPACE_DEF;
511 QStringList FontsConf::configKeys()
513 static QStringList keys;
515 keys << EMBEDDEDBITMAP_DEF << HINTING_DEF << ANTIALIAS_DEF << AUTOHINT_DEF;
519 void FontsConf::load(const QString &path)
525 fp.open(QIODevice::ReadOnly);
526 if (!fp.isOpen() || fp.error() != QFile::NoError)
529 QByteArray buf = fp.readAll();
533 bool check = parse(buf);
538 void FontsConf::save(const QString &path)
541 if (!mElements && fp.exists()) {
548 QXmlStreamWriter xml(&buf);
550 xml.setAutoFormatting(true);
552 xml.writeStartDocument();
554 xml.writeDTD("<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>");
557 mElements->save(xml);
559 xml.writeEndDocument();
562 fp.open(QIODevice::WriteOnly);
567 ts.setCodec("UTF-8");
575 bool FontsConf::appendFamilyToAlias(FontsConfElementPointer aliasElem, const QString &family, const QString &mode, const QString &value)
577 if (aliasElem->type() != ALIAS_DEF)
580 FontsConfElementPointer familyElem = aliasElem->childElementOf(FAMILY_DEF);
581 if (!familyElem || familyElem->text() != family)
584 FontsConfElementPointer targetElem = aliasElem->childElementOf(mode);
586 targetElem = FontsConfElementPointer(new FontsConfElement(mode));
587 aliasElem->addChildElement(targetElem);
592 while ((findex = targetElem->indexOf(FAMILY_DEF, findex)) >= 0) {
593 if (targetElem->childElementAt(findex)->text() == value)
598 FontsConfElementPointer valueElem(new FontsConfElement(FAMILY_DEF));
599 valueElem->setText(value);
600 targetElem->addChildElement(valueElem);
606 bool FontsConf::isMatchElementFor(FontsConfElementPointer matchElem, const QString &config, const QString &family, const QString &val) const
608 if (!matchElem->hasAttribute(TARGET_DEF) || matchElem->value(TARGET_DEF) != FONT_DEF)
611 FontsConfElementList testElemList = matchElem->findChildrenElements(TEST_DEF);
612 if (testElemList.count() != 1)
615 FontsConfElementPointer testElem = testElemList.at(0);
616 if (!testElem->hasAttribute(NAME_DEF) || testElem->value(NAME_DEF) != FAMILY_DEF)
618 if ((testElem->hasAttribute(QUAL_DEF) && testElem->value(QUAL_DEF) != ANY_DEF) ||
619 (testElem->hasAttribute(COMPARE_DEF) && testElem->value(COMPARE_DEF) != EQ_DEF) ||
620 (testElem->hasAttribute(TARGET_DEF) && testElem->value(TARGET_DEF) != DEFAULT_DEF))
623 if (!family.isEmpty()) {
624 if (testElem->count() == 0 && testElem->text() != family)
626 QStringList familyList = textElementList(testElem, STRING_DEF);
627 if (!familyList.contains(family))
631 FontsConfElementList editElemList = matchElem->findChildrenElements(EDIT_DEF);
632 if (editElemList.count() != 1)
635 FontsConfElementPointer editElem = editElemList.at(0);
636 if ((!editElem->hasAttribute(NAME_DEF) || editElem->value(NAME_DEF) != config) ||
637 (editElem->hasAttribute(MODE_DEF) && editElem->value(MODE_DEF) != ASSIGN_DEF))
640 FontsConfElementList boolElemList = editElem->findChildrenElements(BOOL_DEF);
641 if (boolElemList.count() != 1) {
645 if (!val.isEmpty()) {
646 FontsConfElementPointer boolElem = boolElemList.at(0);
647 if (boolElem->text() != val)
654 bool FontsConf::isPatternElementFor(FontsConfElementPointer matchElem, const QString &family, const QString &mode) const
656 if (matchElem->hasAttribute(TARGET_DEF) && matchElem->value(TARGET_DEF) != PATTERN_DEF)
659 FontsConfElementList testElemList = matchElem->findChildrenElements(TEST_DEF);
660 if (testElemList.count() != 1)
663 FontsConfElementPointer testElem = testElemList.at(0);
664 if (testElem->hasAttribute(NAME_DEF) && testElem->value(NAME_DEF) != FAMILY_DEF)
666 if ((testElem->hasAttribute(COMPARE_DEF) && testElem->value(COMPARE_DEF) != EQ_DEF) ||
667 (testElem->hasAttribute(TARGET_DEF) && testElem->value(TARGET_DEF) != DEFAULT_DEF))
670 if (testElem->count() == 0) {
671 if (testElem->text().isEmpty() || testElem->text() != family)
674 QStringList familyList = textElementList(testElem, STRING_DEF);
675 if (familyList.count() != 1 || !familyList.contains(family))
683 while ((eindex = matchElem->indexOf(EDIT_DEF, eindex)) >= 0) {
684 FontsConfElementPointer editElem = matchElem->childElementAt(eindex++);
686 if ((editElem->hasAttribute(NAME_DEF) && editElem->value(NAME_DEF) == FAMILY_DEF) &&
687 (editElem->hasAttribute(MODE_DEF) && editElem->value(MODE_DEF) == mode))
695 FontsConfElementPointer FontsConf::getEditPatternElementFor(FontsConfElementPointer matchElem, const QString &family, const QString &mode) const
699 while ((eindex = matchElem->indexOf(EDIT_DEF, eindex)) >= 0) {
700 FontsConfElementPointer editElem = matchElem->childElementAt(eindex++);
702 if ((editElem->hasAttribute(NAME_DEF) && editElem->value(NAME_DEF) == FAMILY_DEF) &&
703 (editElem->hasAttribute(MODE_DEF) && editElem->value(MODE_DEF) == mode))
706 return FontsConfElementPointer();
709 bool FontsConf::containsTextElement(FontsConfElementPointer elem, const QString &tag, const QString &text) const
712 while ((index = elem->indexOf(tag, index)) >= 0) {
713 FontsConfElementPointer e = elem->childElementAt(index++);
714 if (e->text() == text)
720 QStringList FontsConf::textElementList(FontsConfElementPointer elem, const QString &tag) const
723 return QStringList();
726 while ((index = elem->indexOf(tag, index)) >= 0) {
727 FontsConfElementPointer e = elem->childElementAt(index++);
728 if (!e->text().isEmpty())
734 FontsConfElementPointer FontsConf::aliasElementFor(const QString &family) const
736 FontsConfElementPointer aliasElem(new FontsConfElement(ALIAS_DEF));
737 FontsConfElementPointer familyElem(new FontsConfElement(FAMILY_DEF));
738 familyElem->setText(family);
739 aliasElem->addChildElement(familyElem);
743 FontsConfElementPointer FontsConf::matchElementFor(const QString &config, bool val) const
745 FontsConfElementPointer matchElem = FontsConfElementPointer(new FontsConfElement(MATCH_DEF));
746 matchElem->setAttribute(TARGET_DEF, FONT_DEF);
748 FontsConfElementPointer testElem(new FontsConfElement(TEST_DEF));
749 testElem->setAttribute(QUAL_DEF, ANY_DEF);
750 testElem->setAttribute(NAME_DEF, FAMILY_DEF);
751 matchElem->addChildElement(testElem);
753 FontsConfElementPointer editElem(new FontsConfElement(EDIT_DEF));
754 editElem->setAttribute(NAME_DEF, config);
755 editElem->setAttribute(MODE_DEF, ASSIGN_DEF);
756 matchElem->addChildElement(editElem);
758 FontsConfElementPointer boolElem(new FontsConfElement(BOOL_DEF));
759 boolElem->setText(boolString(val));
760 editElem->addChildElement(boolElem);
765 FontsConfElementPointer FontsConf::patternElementFor(const QString &family) const
767 FontsConfElementPointer matchElem = FontsConfElementPointer(new FontsConfElement(MATCH_DEF));
768 matchElem->setAttribute(TARGET_DEF, PATTERN_DEF);
770 FontsConfElementPointer testElem(new FontsConfElement(TEST_DEF));
771 testElem->setAttribute(QUAL_DEF, ANY_DEF);
772 testElem->setAttribute(NAME_DEF, FAMILY_DEF);
773 matchElem->addChildElement(testElem);
775 FontsConfElementPointer stringElem(new FontsConfElement(STRING_DEF));
776 stringElem->setText(family);
777 testElem->addChildElement(stringElem);