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 | |
45 | using namespace std; |
46 | |
47 | static int point2Pixel(double point) |
48 | { |
49 | return (int)(((point*QX11Info::appDpiY())/72.0)+0.5); |
50 | } |
51 | |
52 | static int pixel2Point(double pixel) |
53 | { |
54 | return (int)(((pixel*72.0)/(double)QX11Info::appDpiY())+0.5); |
55 | } |
56 | |
57 | static bool equal(double d1, double d2) |
58 | { |
59 | return (fabs(d1 - d2) < 0.0001); |
60 | } |
61 | |
62 | static 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 | |
81 | static 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 | |
90 | inline bool fExists(const QString &p) |
91 | { |
92 | return check(p, S_IFREG, false); |
93 | } |
94 | |
95 | inline bool dWritable(const QString &p) |
96 | { |
97 | return check(p, S_IFDIR, true); |
98 | } |
99 | |
100 | static 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 | |
112 | static 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. |
121 | QString 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 | |
164 | static 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 | |
202 | static 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 | |
216 | static 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 | |
228 | KXftConfig::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 | |
237 | KXftConfig::~KXftConfig() |
238 | { |
239 | } |
240 | |
241 | bool 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 | |
301 | bool 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 [] = "<?xml version = '1.0'?>" ; |
351 | static const char [] = "<?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 | |
390 | bool KXftConfig::getSubPixelType(SubPixel::Type &type) |
391 | { |
392 | type=m_subPixel.type; |
393 | return SubPixel::None!=m_subPixel.type; |
394 | } |
395 | |
396 | void 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 | |
405 | bool 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 | |
416 | void 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 | |
430 | void KXftConfig::setHinting(bool set) |
431 | { |
432 | if(set!=m_hinting.set) |
433 | { |
434 | m_hinting.set=set; |
435 | m_madeChanges=true; |
436 | } |
437 | } |
438 | |
439 | bool 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 | |
452 | void 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 | |
465 | QString 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 | |
483 | const 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 | |
501 | QString 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 | |
519 | const 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 | |
535 | void 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 | |
666 | void 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 | |
686 | void 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 | |
719 | void 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 | |
739 | void 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 | |
796 | bool KXftConfig::getAntiAliasing() const |
797 | { |
798 | return m_antiAliasing.set; |
799 | } |
800 | |
801 | void KXftConfig::setAntiAliasing( bool set ) |
802 | { |
803 | if(set!=m_antiAliasing.set) |
804 | { |
805 | m_antiAliasing.set = set; |
806 | m_madeChanges = true; |
807 | } |
808 | } |
809 | |
810 | void 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*. |
833 | bool 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 | |