1/*
2 Copyright (c) 2002 Craig Drummond <craig@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
18*/
19
20#include "kxftconfig.h"
21#ifdef HAVE_FONTCONFIG
22
23#include <stdarg.h>
24#include <stdio.h>
25#include <math.h>
26#include <string.h>
27#include <ctype.h>
28#include <stdlib.h>
29#include <sys/stat.h>
30
31#include <QRegExp>
32#include <QFile>
33#include <QDir>
34#include <QX11Info>
35#include <QByteArray>
36
37#include <KLocale>
38#include <kde_file.h>
39#include <KGlobal>
40#include <KStandardDirs>
41#include <KDebug>
42
43#include <fontconfig/fontconfig.h>
44
45using namespace std;
46
47static int point2Pixel(double point)
48{
49 return (int)(((point*QX11Info::appDpiY())/72.0)+0.5);
50}
51
52static int pixel2Point(double pixel)
53{
54 return (int)(((pixel*72.0)/(double)QX11Info::appDpiY())+0.5);
55}
56
57static bool equal(double d1, double d2)
58{
59 return (fabs(d1 - d2) < 0.0001);
60}
61
62static QString dirSyntax(const QString &d)
63{
64 if(!d.isNull())
65 {
66 QString ds(d);
67
68 ds.replace("//", "/");
69
70 int slashPos=ds.lastIndexOf('/');
71
72 if(slashPos!=(((int)ds.length())-1))
73 ds.append('/');
74
75 return ds;
76 }
77
78 return d;
79}
80
81static bool check(const QString &path, unsigned int fmt, bool checkW=false)
82{
83 KDE_struct_stat info;
84 QByteArray pathC(QFile::encodeName(path));
85
86 return 0==KDE_lstat(pathC, &info) && (info.st_mode&S_IFMT)==fmt &&
87 (!checkW || 0==::access(pathC, W_OK));
88}
89
90inline bool fExists(const QString &p)
91{
92 return check(p, S_IFREG, false);
93}
94
95inline bool dWritable(const QString &p)
96{
97 return check(p, S_IFDIR, true);
98}
99
100static QString getDir(const QString &f)
101{
102 QString d(f);
103
104 int slashPos=d.lastIndexOf('/');
105
106 if(-1!=slashPos)
107 d.remove(slashPos+1, d.length());
108
109 return dirSyntax(d);
110}
111
112static time_t getTimeStamp(const QString &item)
113{
114 KDE_struct_stat info;
115
116 return !item.isNull() && 0==KDE_lstat(QFile::encodeName(item), &info) ? info.st_mtime : 0;
117}
118
119//
120// Obtain location of config file to use.
121QString getConfigFile()
122{
123 FcStrList *list=FcConfigGetConfigFiles(FcConfigGetCurrent());
124 QStringList files;
125 FcChar8 *file;
126 QString home(dirSyntax(QDir::homePath()));
127
128 while((file=FcStrListNext(list)))
129 {
130 QString f((const char *)file);
131
132 if(fExists(f) && 0==f.indexOf(home))
133 files.append(f);
134 }
135 FcStrListDone(list);
136
137 //
138 // Go through list of files, looking for the preferred one...
139 if(files.count())
140 {
141 QStringList::const_iterator it(files.begin()),
142 end(files.end());
143
144 for(; it!=end; ++it)
145 if(-1!=(*it).indexOf(QRegExp("/\\.?fonts\\.conf$")))
146 return *it;
147 return files.front(); // Just return the 1st one...
148 }
149 else // Hmmm... no known files?
150 {
151 if(FcGetVersion() >= 21000)
152 {
153 QString targetPath(KGlobal::dirs()->localxdgconfdir()+"fontconfig");
154 QDir target(targetPath);
155 if(!target.exists())
156 target.mkpath(targetPath);
157 return targetPath+"/fonts.conf";
158 }
159 else
160 return home+"/.fonts.conf";
161 }
162}
163
164static QString getEntry(QDomElement element, const char *type, unsigned int numAttributes, ...)
165{
166 if(numAttributes==element.attributes().length())
167 {
168 va_list args;
169 unsigned int arg;
170 bool ok=true;
171
172 va_start(args, numAttributes);
173
174 for(arg=0; arg<numAttributes && ok; ++arg)
175 {
176 const char *attr=va_arg(args, const char *);
177 const char *val =va_arg(args, const char *);
178
179 if(!attr || !val || val!=element.attribute(attr))
180 ok=false;
181 }
182
183 va_end(args);
184
185 if(ok)
186 {
187 QDomNode n=element.firstChild();
188
189 if(!n.isNull())
190 {
191 QDomElement e = n.toElement();
192
193 if(!e.isNull() && type==e.tagName())
194 return e.text();
195 }
196 }
197 }
198
199 return QString();
200}
201
202static KXftConfig::SubPixel::Type strToType(const char *str)
203{
204 if(0==strcmp(str, "rgb"))
205 return KXftConfig::SubPixel::Rgb;
206 else if(0==strcmp(str, "bgr"))
207 return KXftConfig::SubPixel::Bgr;
208 else if(0==strcmp(str, "vrgb"))
209 return KXftConfig::SubPixel::Vrgb;
210 else if(0==strcmp(str, "vbgr"))
211 return KXftConfig::SubPixel::Vbgr;
212 else
213 return KXftConfig::SubPixel::None;
214}
215
216static KXftConfig::Hint::Style strToStyle(const char *str)
217{
218 if(0==strcmp(str, "hintslight"))
219 return KXftConfig::Hint::Slight;
220 else if(0==strcmp(str, "hintmedium"))
221 return KXftConfig::Hint::Medium;
222 else if(0==strcmp(str, "hintfull"))
223 return KXftConfig::Hint::Full;
224 else
225 return KXftConfig::Hint::None;
226}
227
228KXftConfig::KXftConfig()
229 : m_doc("fontconfig")
230 , m_file(getConfigFile())
231{
232 kDebug(1208) << "Using fontconfig file:" << m_file;
233 m_antiAliasing = aliasingEnabled();
234 reset();
235}
236
237KXftConfig::~KXftConfig()
238{
239}
240
241bool KXftConfig::reset()
242{
243 bool ok=false;
244
245 m_madeChanges=false;
246 m_hint.reset();
247 m_hinting.reset();
248 m_excludeRange.reset();
249 m_excludePixelRange.reset();
250 m_subPixel.reset();
251
252 QFile f(m_file);
253
254 if(f.open(QIODevice::ReadOnly))
255 {
256 m_time=getTimeStamp(m_file);
257 ok=true;
258 m_doc.clear();
259
260 if(m_doc.setContent(&f))
261 readContents();
262 f.close();
263 }
264 else
265 ok=!fExists(m_file) && dWritable(getDir(m_file));
266
267 if(m_doc.documentElement().isNull())
268 m_doc.appendChild(m_doc.createElement("fontconfig"));
269
270 if(ok)
271 {
272 //
273 // Check exclude range values - i.e. size and pixel size...
274 // If "size" range is set, ensure "pixelsize" matches...
275 if(!equal(0, m_excludeRange.from) || !equal(0, m_excludeRange.to))
276 {
277 double pFrom=(double)point2Pixel(m_excludeRange.from),
278 pTo=(double)point2Pixel(m_excludeRange.to);
279
280 if(!equal(pFrom, m_excludePixelRange.from) || !equal(pTo, m_excludePixelRange.to))
281 {
282 m_excludePixelRange.from=pFrom;
283 m_excludePixelRange.to=pTo;
284 m_madeChanges=true;
285 apply();
286 }
287 }
288 else if(!equal(0, m_excludePixelRange.from) || !equal(0, m_excludePixelRange.to))
289 {
290 // "pixelsize" set, but not "size" !!!
291 m_excludeRange.from=(int)pixel2Point(m_excludePixelRange.from);
292 m_excludeRange.to=(int)pixel2Point(m_excludePixelRange.to);
293 m_madeChanges=true;
294 apply();
295 }
296 }
297
298 return ok;
299}
300
301bool KXftConfig::apply()
302{
303 bool ok=true;
304
305 if(m_madeChanges)
306 {
307 //
308 // Check if file has been written since we last read it. If it has, then re-read and add any
309 // of our changes...
310 if(fExists(m_file) && getTimeStamp(m_file)!=m_time)
311 {
312 KXftConfig newConfig;
313
314 newConfig.setExcludeRange(m_excludeRange.from, m_excludeRange.to);
315 newConfig.setSubPixelType(m_subPixel.type);
316 newConfig.setHintStyle(m_hint.style);
317 newConfig.setAntiAliasing(m_antiAliasing.set);
318
319 ok=newConfig.changed() ? newConfig.apply() : true;
320 if(ok)
321 reset();
322 else
323 m_time=getTimeStamp(m_file);
324 }
325 else
326 {
327 // Ensure these are always equal...
328 m_excludePixelRange.from=(int)point2Pixel(m_excludeRange.from);
329 m_excludePixelRange.to=(int)point2Pixel(m_excludeRange.to);
330
331 FcAtomic *atomic=FcAtomicCreate((const unsigned char *)(QFile::encodeName(m_file).data()));
332
333 ok=false;
334 if(atomic)
335 {
336 if(FcAtomicLock(atomic))
337 {
338 FILE *f=fopen((char *)FcAtomicNewFile(atomic), "w");
339
340 if(f)
341 {
342 applySubPixelType();
343 applyHintStyle();
344 applyAntiAliasing();
345 applyExcludeRange(false);
346 applyExcludeRange(true);
347
348 //
349 // Check document syntax...
350 static const char qtXmlHeader[] = "<?xml version = '1.0'?>";
351 static const char xmlHeader[] = "<?xml version=\"1.0\"?>";
352 static const char qtDocTypeLine[] = "<!DOCTYPE fontconfig>";
353 static const char docTypeLine[] = "<!DOCTYPE fontconfig SYSTEM "
354 "\"fonts.dtd\">";
355
356 QString str(m_doc.toString());
357 int idx;
358
359 if(0!=str.indexOf("<?xml"))
360 str.insert(0, xmlHeader);
361 else if(0==str.indexOf(qtXmlHeader))
362 str.replace(0, strlen(qtXmlHeader), xmlHeader);
363
364 if(-1!=(idx=str.indexOf(qtDocTypeLine)))
365 str.replace(idx, strlen(qtDocTypeLine), docTypeLine);
366
367 //
368 // Write to file...
369 fputs(str.toUtf8(), f);
370 fclose(f);
371
372 if(FcAtomicReplaceOrig(atomic))
373 {
374 ok=true;
375 reset(); // Re-read contents..
376 }
377 else
378 FcAtomicDeleteNew(atomic);
379 }
380 FcAtomicUnlock(atomic);
381 }
382 FcAtomicDestroy(atomic);
383 }
384 }
385 }
386
387 return ok;
388}
389
390bool KXftConfig::getSubPixelType(SubPixel::Type &type)
391{
392 type=m_subPixel.type;
393 return SubPixel::None!=m_subPixel.type;
394}
395
396void KXftConfig::setSubPixelType(SubPixel::Type type)
397{
398 if(type!=m_subPixel.type)
399 {
400 m_subPixel.type=type;
401 m_madeChanges=true;
402 }
403}
404
405bool KXftConfig::getHintStyle(Hint::Style &style)
406{
407 if(Hint::NotSet!=m_hint.style && !m_hint.toBeRemoved)
408 {
409 style=m_hint.style;
410 return true;
411 }
412 else
413 return false;
414}
415
416void KXftConfig::setHintStyle(Hint::Style style)
417{
418 if((Hint::NotSet==style && Hint::NotSet!=m_hint.style && !m_hint.toBeRemoved) ||
419 (Hint::NotSet!=style && (style!=m_hint.style || m_hint.toBeRemoved)) )
420 {
421 m_hint.toBeRemoved=(Hint::NotSet==style);
422 m_hint.style=style;
423 m_madeChanges=true;
424 }
425
426 if(Hint::NotSet!=style)
427 setHinting(Hint::None!=m_hint.style);
428}
429
430void KXftConfig::setHinting(bool set)
431{
432 if(set!=m_hinting.set)
433 {
434 m_hinting.set=set;
435 m_madeChanges=true;
436 }
437}
438
439bool KXftConfig::getExcludeRange(double &from, double &to)
440{
441 if(!equal(0, m_excludeRange.from) || !equal(0,m_excludeRange.to))
442 {
443 from=m_excludeRange.from;
444 to=m_excludeRange.to;
445 return true;
446 }
447 else
448 return false;
449}
450
451
452void KXftConfig::setExcludeRange(double from, double to)
453{
454 double f=from<to ? from : to,
455 t=from<to ? to : from;
456
457 if(!equal(f, m_excludeRange.from) || !equal(t,m_excludeRange.to))
458 {
459 m_excludeRange.from=f;
460 m_excludeRange.to=t;
461 m_madeChanges=true;
462 }
463}
464
465QString KXftConfig::description(SubPixel::Type t)
466{
467 switch(t)
468 {
469 default:
470 case SubPixel::None:
471 return i18nc("no subpixel rendering", "None");
472 case SubPixel::Rgb:
473 return i18n("RGB");
474 case SubPixel::Bgr:
475 return i18n("BGR");
476 case SubPixel::Vrgb:
477 return i18n("Vertical RGB");
478 case SubPixel::Vbgr:
479 return i18n("Vertical BGR");
480 }
481}
482
483const char * KXftConfig::toStr(SubPixel::Type t)
484{
485 switch(t)
486 {
487 default:
488 case SubPixel::None:
489 return "none";
490 case SubPixel::Rgb:
491 return "rgb";
492 case SubPixel::Bgr:
493 return "bgr";
494 case SubPixel::Vrgb:
495 return "vrgb";
496 case SubPixel::Vbgr:
497 return "vbgr";
498 }
499}
500
501QString KXftConfig::description(Hint::Style s)
502{
503 switch(s)
504 {
505 default:
506 case Hint::Medium:
507 return i18nc("medium hinting", "Medium");
508 case Hint::NotSet:
509 return "";
510 case Hint::None:
511 return i18nc("no hinting", "None");
512 case Hint::Slight:
513 return i18nc("slight hinting", "Slight");
514 case Hint::Full:
515 return i18nc("full hinting", "Full");
516 }
517}
518
519const char * KXftConfig::toStr(Hint::Style s)
520{
521 switch(s)
522 {
523 default:
524 case Hint::Medium:
525 return "hintmedium";
526 case Hint::None:
527 return "hintnone";
528 case Hint::Slight:
529 return "hintslight";
530 case Hint::Full:
531 return "hintfull";
532 }
533}
534
535void KXftConfig::readContents()
536{
537 QDomNode n = m_doc.documentElement().firstChild();
538
539 while(!n.isNull())
540 {
541 QDomElement e = n.toElement();
542
543 if(!e.isNull())
544 {
545 if("match"==e.tagName())
546 {
547 QString str;
548
549 switch(e.childNodes().count())
550 {
551 case 1:
552 if("font"==e.attribute("target"))
553 {
554 QDomElement ene=e.firstChild().toElement();
555
556 if(!ene.isNull() && "edit"==ene.tagName())
557 {
558 if(!(str=getEntry(ene, "const", 2, "name", "rgba", "mode",
559 "assign")).isNull())
560 {
561 m_subPixel.node=n;
562 m_subPixel.type=strToType(str.toLatin1());
563 }
564 else if(!(str=getEntry(ene, "const", 2, "name", "hintstyle", "mode",
565 "assign")).isNull())
566 {
567 m_hint.node=n;
568 m_hint.style=strToStyle(str.toLatin1());
569 }
570 else if(!(str=getEntry(ene, "bool", 2, "name", "hinting", "mode",
571 "assign")).isNull())
572 {
573 m_hinting.node=n;
574 m_hinting.set=str.toLower()!="false";
575 }
576 else if(!(str=getEntry(ene, "bool", 2, "name", "antialias", "mode",
577 "assign")).isNull())
578 {
579 m_antiAliasing.node=n;
580 m_antiAliasing.set=str.toLower()!="false";
581 }
582 }
583 }
584 break;
585 case 3: // CPD: Is target "font" or "pattern" ????
586 if("font"==e.attribute("target"))
587 {
588 bool foundFalse=false;
589 QDomNode en=e.firstChild();
590 QString family;
591 double from=-1.0,
592 to=-1.0,
593 pixelFrom=-1.0,
594 pixelTo=-1.0;
595
596 while(!en.isNull())
597 {
598 QDomElement ene=en.toElement();
599
600 if(!ene.isNull())
601 {
602 if("test"==ene.tagName())
603 {
604 // kcmfonts used to write incorrectly more or less instead of
605 // more_eq and less_eq, so read both,
606 // first the old (wrong) one then the right one
607 if(!(str=getEntry(ene, "double", 3, "qual", "any", "name",
608 "size", "compare", "more")).isNull())
609 from=str.toDouble();
610 if(!(str=getEntry(ene, "double", 3, "qual", "any", "name",
611 "size", "compare", "more_eq")).isNull())
612 from=str.toDouble();
613 if(!(str=getEntry(ene, "double", 3, "qual", "any", "name",
614 "size", "compare", "less")).isNull())
615 to=str.toDouble();
616 if(!(str=getEntry(ene, "double", 3, "qual", "any", "name",
617 "size", "compare", "less_eq")).isNull())
618 to=str.toDouble();
619 if(!(str=getEntry(ene, "double", 3, "qual", "any", "name",
620 "pixelsize", "compare", "more")).isNull())
621 pixelFrom=str.toDouble();
622 if(!(str=getEntry(ene, "double", 3, "qual", "any", "name",
623 "pixelsize", "compare",
624 "more_eq")).isNull())
625 pixelFrom=str.toDouble();
626 if(!(str=getEntry(ene, "double", 3, "qual", "any", "name",
627 "pixelsize", "compare", "less")).isNull())
628 pixelTo=str.toDouble();
629 if(!(str=getEntry(ene, "double", 3, "qual", "any", "name",
630 "pixelsize", "compare",
631 "less_eq")).isNull())
632 pixelTo=str.toDouble();
633 }
634 else if("edit"==ene.tagName() &&
635 "false"==getEntry(ene, "bool", 2, "name", "antialias",
636 "mode", "assign"))
637 foundFalse=true;
638 }
639
640 en=en.nextSibling();
641 }
642
643 if((from>=0 || to>=0) && foundFalse)
644 {
645 m_excludeRange.from=from < to ? from : to;
646 m_excludeRange.to =from < to ? to : from;
647 m_excludeRange.node=n;
648 }
649 else if((pixelFrom>=0 || pixelTo>=0) && foundFalse)
650 {
651 m_excludePixelRange.from=pixelFrom < pixelTo ? pixelFrom : pixelTo;
652 m_excludePixelRange.to =pixelFrom < pixelTo ? pixelTo : pixelFrom;
653 m_excludePixelRange.node=n;
654 }
655 }
656 break;
657 default:
658 break;
659 }
660 }
661 }
662 n=n.nextSibling();
663 }
664}
665
666void KXftConfig::applySubPixelType()
667{
668 QDomElement matchNode = m_doc.createElement("match"),
669 typeNode = m_doc.createElement("const"),
670 editNode = m_doc.createElement("edit");
671 QDomText typeText = m_doc.createTextNode(toStr(m_subPixel.type));
672
673 matchNode.setAttribute("target", "font");
674 editNode.setAttribute("mode", "assign");
675 editNode.setAttribute("name", "rgba");
676 editNode.appendChild(typeNode);
677 typeNode.appendChild(typeText);
678 matchNode.appendChild(editNode);
679 if(m_subPixel.node.isNull())
680 m_doc.documentElement().appendChild(matchNode);
681 else
682 m_doc.documentElement().replaceChild(matchNode, m_subPixel.node);
683 m_subPixel.node=matchNode;
684}
685
686void KXftConfig::applyHintStyle()
687{
688 applyHinting();
689
690 if(Hint::NotSet==m_hint.style || m_hint.toBeRemoved)
691 {
692 if(!m_hint.node.isNull())
693 {
694 m_doc.documentElement().removeChild(m_hint.node);
695 m_hint.node.clear();
696 }
697 }
698 else
699 {
700 QDomElement matchNode = m_doc.createElement("match"),
701 typeNode = m_doc.createElement("const"),
702 editNode = m_doc.createElement("edit");
703 QDomText typeText = m_doc.createTextNode(toStr(m_hint.style));
704
705 matchNode.setAttribute("target", "font");
706 editNode.setAttribute("mode", "assign");
707 editNode.setAttribute("name", "hintstyle");
708 editNode.appendChild(typeNode);
709 typeNode.appendChild(typeText);
710 matchNode.appendChild(editNode);
711 if(m_hint.node.isNull())
712 m_doc.documentElement().appendChild(matchNode);
713 else
714 m_doc.documentElement().replaceChild(matchNode, m_hint.node);
715 m_hint.node=matchNode;
716 }
717}
718
719void KXftConfig::applyHinting()
720{
721 QDomElement matchNode = m_doc.createElement("match"),
722 typeNode = m_doc.createElement("bool"),
723 editNode = m_doc.createElement("edit");
724 QDomText typeText = m_doc.createTextNode(m_hinting.set ? "true" : "false");
725
726 matchNode.setAttribute("target", "font");
727 editNode.setAttribute("mode", "assign");
728 editNode.setAttribute("name", "hinting");
729 editNode.appendChild(typeNode);
730 typeNode.appendChild(typeText);
731 matchNode.appendChild(editNode);
732 if(m_hinting.node.isNull())
733 m_doc.documentElement().appendChild(matchNode);
734 else
735 m_doc.documentElement().replaceChild(matchNode, m_hinting.node);
736 m_hinting.node=matchNode;
737}
738
739void KXftConfig::applyExcludeRange(bool pixel)
740{
741 Exclude &range=pixel ? m_excludePixelRange : m_excludeRange;
742
743 if(equal(range.from, 0) && equal(range.to, 0))
744 {
745 if(!range.node.isNull())
746 {
747 m_doc.documentElement().removeChild(range.node);
748 range.node.clear();
749 }
750 }
751 else
752 {
753 QString fromString,
754 toString;
755
756 fromString.setNum(range.from);
757 toString.setNum(range.to);
758
759 QDomElement matchNode = m_doc.createElement("match"),
760 fromTestNode = m_doc.createElement("test"),
761 fromNode = m_doc.createElement("double"),
762 toTestNode = m_doc.createElement("test"),
763 toNode = m_doc.createElement("double"),
764 editNode = m_doc.createElement("edit"),
765 boolNode = m_doc.createElement("bool");
766 QDomText fromText = m_doc.createTextNode(fromString),
767 toText = m_doc.createTextNode(toString),
768 boolText = m_doc.createTextNode("false");
769
770 matchNode.setAttribute("target", "font"); // CPD: Is target "font" or "pattern" ????
771 fromTestNode.setAttribute("qual", "any");
772 fromTestNode.setAttribute("name", pixel ? "pixelsize" : "size");
773 fromTestNode.setAttribute("compare", "more_eq");
774 fromTestNode.appendChild(fromNode);
775 fromNode.appendChild(fromText);
776 toTestNode.setAttribute("qual", "any");
777 toTestNode.setAttribute("name", pixel ? "pixelsize" : "size");
778 toTestNode.setAttribute("compare", "less_eq");
779 toTestNode.appendChild(toNode);
780 toNode.appendChild(toText);
781 editNode.setAttribute("mode", "assign");
782 editNode.setAttribute("name", "antialias");
783 editNode.appendChild(boolNode);
784 boolNode.appendChild(boolText);
785 matchNode.appendChild(fromTestNode);
786 matchNode.appendChild(toTestNode);
787 matchNode.appendChild(editNode);
788
789 if(!m_antiAliasing.node.isNull())
790 m_doc.documentElement().removeChild(range.node);
791 m_doc.documentElement().appendChild(matchNode);
792 range.node=matchNode;
793 }
794}
795
796bool KXftConfig::getAntiAliasing() const
797{
798 return m_antiAliasing.set;
799}
800
801void KXftConfig::setAntiAliasing( bool set )
802{
803 if(set!=m_antiAliasing.set)
804 {
805 m_antiAliasing.set = set;
806 m_madeChanges = true;
807 }
808}
809
810void KXftConfig::applyAntiAliasing()
811{
812 QDomElement matchNode = m_doc.createElement("match"),
813 typeNode = m_doc.createElement("bool"),
814 editNode = m_doc.createElement("edit");
815 QDomText typeText = m_doc.createTextNode(m_antiAliasing.set ? "true" : "false");
816
817 matchNode.setAttribute("target", "font");
818 editNode.setAttribute("mode", "assign");
819 editNode.setAttribute("name", "antialias");
820 editNode.appendChild(typeNode);
821 typeNode.appendChild(typeText);
822 matchNode.appendChild(editNode);
823 if(!m_antiAliasing.node.isNull())
824 m_doc.documentElement().removeChild(m_antiAliasing.node);
825 m_doc.documentElement().appendChild(matchNode);
826 m_antiAliasing.node=matchNode;
827}
828
829// KXftConfig only parses one config file, user's .fonts.conf usually.
830// If that one doesn't exist, then KXftConfig doesn't know if antialiasing
831// is enabled or not. So try to find out the default value from the default font.
832// Maybe there's a better way *shrug*.
833bool KXftConfig::aliasingEnabled()
834{
835 FcPattern *pattern = FcPatternCreate();
836 FcConfigSubstitute(0, pattern, FcMatchPattern);
837 FcDefaultSubstitute(pattern);
838 FcResult result;
839 FcPattern *f = FcFontMatch( 0, pattern, &result );
840 FcBool antialiased = FcTrue;
841 FcPatternGetBool( f, FC_ANTIALIAS, 0, &antialiased );
842 FcPatternDestroy( f );
843 FcPatternDestroy( pattern );
844 return antialiased == FcTrue;
845}
846
847#endif
848