add more spacing
[personal-kdebase.git] / workspace / kcontrol / kfontinst / lib / Fc.cpp
blob4bafd3e154765642e3a0e36da4d66fbf650abec3
1 /*
2 * KFontInst - KDE Font Installer
4 * Copyright 2003-2007 Craig Drummond <craig@kde.org>
6 * ----
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
24 #include "Fc.h"
25 #include <QtCore/QTextStream>
28 // KDE font chooser always seems to use Italic - for both Oblique, and Italic. So I guees
29 // the fonts:/ should do too - so as to appear more unified.
31 // ditto with respect to Medium/Regular
32 #define KFI_HAVE_OBLIQUE // Do we differentiate between Italic and Oblique when comparing slants?
33 #define KFI_DISPLAY_OBLIQUE // Do we want to list "Oblique"? Or always use Italic?
34 #define KFI_HAVE_MEDIUM_WEIGHT // Do we differentiate between Medium and Regular weights when comparing weights?
35 #define KFI_DISPLAY_MEDIUM // Do we want to list "Medium"? Or always use Regular?
37 namespace KFI
40 namespace FC
43 int weight(int w)
45 if(KFI_NULL_SETTING==w)
46 #ifdef KFI_HAVE_MEDIUM_WEIGHT
47 return FC_WEIGHT_MEDIUM;
48 #else
49 return FC_WEIGHT_REGULAR;
50 #endif
52 if(w<FC_WEIGHT_EXTRALIGHT)
53 return FC_WEIGHT_THIN;
55 if(w<(FC_WEIGHT_EXTRALIGHT+FC_WEIGHT_LIGHT)/2)
56 return FC_WEIGHT_EXTRALIGHT;
58 if(w<(FC_WEIGHT_LIGHT+FC_WEIGHT_REGULAR)/2)
59 return FC_WEIGHT_LIGHT;
61 #ifdef KFI_HAVE_MEDIUM_WEIGHT
62 if(w<(FC_WEIGHT_REGULAR+FC_WEIGHT_MEDIUM)/2)
63 return FC_WEIGHT_REGULAR;
65 if(w<(FC_WEIGHT_MEDIUM+FC_WEIGHT_DEMIBOLD)/2)
66 return FC_WEIGHT_MEDIUM;
67 #else
68 if(w<(FC_WEIGHT_REGULAR+FC_WEIGHT_DEMIBOLD)/2)
69 return FC_WEIGHT_REGULAR;
70 #endif
72 if(w<(FC_WEIGHT_DEMIBOLD+FC_WEIGHT_BOLD)/2)
73 return FC_WEIGHT_DEMIBOLD;
75 if(w<(FC_WEIGHT_BOLD+FC_WEIGHT_EXTRABOLD)/2)
76 return FC_WEIGHT_BOLD;
78 if(w<(FC_WEIGHT_EXTRABOLD+FC_WEIGHT_BLACK)/2)
79 return FC_WEIGHT_EXTRABOLD;
81 return FC_WEIGHT_BLACK;
84 int width(int w)
86 if(KFI_NULL_SETTING==w)
87 return KFI_FC_WIDTH_NORMAL;
89 if(w<KFI_FC_WIDTH_EXTRACONDENSED)
90 return KFI_FC_WIDTH_EXTRACONDENSED;
92 if(w<(KFI_FC_WIDTH_EXTRACONDENSED+KFI_FC_WIDTH_CONDENSED)/2)
93 return KFI_FC_WIDTH_EXTRACONDENSED;
95 if(w<(KFI_FC_WIDTH_CONDENSED+KFI_FC_WIDTH_SEMICONDENSED)/2)
96 return KFI_FC_WIDTH_CONDENSED;
98 if(w<(KFI_FC_WIDTH_SEMICONDENSED+KFI_FC_WIDTH_NORMAL)/2)
99 return KFI_FC_WIDTH_SEMICONDENSED;
101 if(w<(KFI_FC_WIDTH_NORMAL+KFI_FC_WIDTH_SEMIEXPANDED)/2)
102 return KFI_FC_WIDTH_NORMAL;
104 if(w<(KFI_FC_WIDTH_SEMIEXPANDED+KFI_FC_WIDTH_EXPANDED)/2)
105 return KFI_FC_WIDTH_SEMIEXPANDED;
107 if(w<(KFI_FC_WIDTH_EXPANDED+KFI_FC_WIDTH_EXTRAEXPANDED)/2)
108 return KFI_FC_WIDTH_EXPANDED;
110 if(w<(KFI_FC_WIDTH_EXTRAEXPANDED+KFI_FC_WIDTH_ULTRAEXPANDED)/2)
111 return KFI_FC_WIDTH_EXTRAEXPANDED;
113 return KFI_FC_WIDTH_ULTRAEXPANDED;
116 int slant(int s)
118 if(KFI_NULL_SETTING==s || s<FC_SLANT_ITALIC)
119 return FC_SLANT_ROMAN;
121 #ifdef KFI_HAVE_OBLIQUE
122 if(s<(FC_SLANT_ITALIC+FC_SLANT_OBLIQUE)/2)
123 return FC_SLANT_ITALIC;
125 return FC_SLANT_OBLIQUE;
126 #else
127 return FC_SLANT_ITALIC;
128 #endif
131 int spacing(int s)
133 if(s<FC_MONO)
134 return FC_PROPORTIONAL;
136 if(s<(FC_MONO+FC_CHARCELL)/2)
137 return FC_MONO;
139 return FC_CHARCELL;
142 int strToWeight(const QString &str, QString &newStr)
144 if(0==str.indexOf(i18n(KFI_WEIGHT_THIN), 0, Qt::CaseInsensitive))
146 newStr=str.mid(i18n(KFI_WEIGHT_THIN).length());
147 return FC_WEIGHT_THIN;
149 if(0==str.indexOf(i18n(KFI_WEIGHT_EXTRALIGHT), 0, Qt::CaseInsensitive))
151 newStr=str.mid(i18n(KFI_WEIGHT_EXTRALIGHT).length());
152 return FC_WEIGHT_EXTRALIGHT;
154 //if(0==str.indexOf(i18n(KFI_WEIGHT_ULTRALIGHT), 0, Qt::CaseInsensitive))
156 // newStr=str.mid(i18n(KFI_WEIGHT_ULTRALIGHT).length());
157 // return FC_WEIGHT_ULTRALIGHT;
159 if(0==str.indexOf(i18n(KFI_WEIGHT_LIGHT), 0, Qt::CaseInsensitive))
161 newStr=str.mid(i18n(KFI_WEIGHT_LIGHT).length());
162 return FC_WEIGHT_LIGHT;
164 if(0==str.indexOf(i18n(KFI_WEIGHT_REGULAR), 0, Qt::CaseInsensitive))
166 newStr=str.mid(i18n(KFI_WEIGHT_REGULAR).length());
167 return FC_WEIGHT_REGULAR;
169 //if(0==str.indexOf(i18n(KFI_WEIGHT_NORMAL), 0, Qt::CaseInsensitive))
171 // newStr=str.mid(i18n(KFI_WEIGHT_NORMAL).length());
172 // return FC_WEIGHT_NORMAL;
174 if(0==str.indexOf(i18n(KFI_WEIGHT_MEDIUM), 0, Qt::CaseInsensitive))
176 newStr=str.mid(i18n(KFI_WEIGHT_MEDIUM).length());
177 return FC_WEIGHT_MEDIUM;
179 if(0==str.indexOf(i18n(KFI_WEIGHT_DEMIBOLD), 0, Qt::CaseInsensitive))
181 newStr=str.mid(i18n(KFI_WEIGHT_DEMIBOLD).length());
182 return FC_WEIGHT_DEMIBOLD;
184 //if(0==str.indexOf(i18n(KFI_WEIGHT_SEMIBOLD), 0, Qt::CaseInsensitive))
186 // newStr=str.mid(i18n(KFI_WEIGHT_SEMIBOLD).length());
187 // return FC_WEIGHT_SEMIBOLD;
189 if(0==str.indexOf(i18n(KFI_WEIGHT_BOLD), 0, Qt::CaseInsensitive))
191 newStr=str.mid(i18n(KFI_WEIGHT_BOLD).length());
192 return FC_WEIGHT_BOLD;
194 if(0==str.indexOf(i18n(KFI_WEIGHT_EXTRABOLD), 0, Qt::CaseInsensitive))
196 newStr=str.mid(i18n(KFI_WEIGHT_EXTRABOLD).length());
197 return FC_WEIGHT_EXTRABOLD;
199 //if(0==str.indexOf(i18n(KFI_WEIGHT_ULTRABOLD), 0, Qt::CaseInsensitive))
201 // newStr=str.mid(i18n(KFI_WEIGHT_ULTRABOLD).length());
202 // return FC_WEIGHT_ULTRABOLD;
204 if(0==str.indexOf(i18n(KFI_WEIGHT_BLACK), 0, Qt::CaseInsensitive))
206 newStr=str.mid(i18n(KFI_WEIGHT_BLACK).length());
207 return FC_WEIGHT_BLACK;
209 if(0==str.indexOf(i18n(KFI_WEIGHT_BLACK), 0, Qt::CaseInsensitive))
211 newStr=str.mid(i18n(KFI_WEIGHT_BLACK).length());
212 return FC_WEIGHT_BLACK;
215 newStr=str;
216 return FC_WEIGHT_REGULAR;
219 int strToWidth(const QString &str, QString &newStr)
221 if(0==str.indexOf(i18n(KFI_WIDTH_ULTRACONDENSED), 0, Qt::CaseInsensitive))
223 newStr=str.mid(i18n(KFI_WIDTH_ULTRACONDENSED).length());
224 return KFI_FC_WIDTH_ULTRACONDENSED;
226 if(0==str.indexOf(i18n(KFI_WIDTH_EXTRACONDENSED), 0, Qt::CaseInsensitive))
228 newStr=str.mid(i18n(KFI_WIDTH_EXTRACONDENSED).length());
229 return KFI_FC_WIDTH_EXTRACONDENSED;
231 if(0==str.indexOf(i18n(KFI_WIDTH_CONDENSED), 0, Qt::CaseInsensitive))
233 newStr=str.mid(i18n(KFI_WIDTH_CONDENSED).length());
234 return KFI_FC_WIDTH_CONDENSED;
236 if(0==str.indexOf(i18n(KFI_WIDTH_SEMICONDENSED), 0, Qt::CaseInsensitive))
238 newStr=str.mid(i18n(KFI_WIDTH_SEMICONDENSED).length());
239 return KFI_FC_WIDTH_SEMICONDENSED;
241 if(0==str.indexOf(i18n(KFI_WIDTH_NORMAL), 0, Qt::CaseInsensitive))
243 newStr=str.mid(i18n(KFI_WIDTH_NORMAL).length());
244 return KFI_FC_WIDTH_NORMAL;
246 if(0==str.indexOf(i18n(KFI_WIDTH_SEMIEXPANDED), 0, Qt::CaseInsensitive))
248 newStr=str.mid(i18n(KFI_WIDTH_SEMIEXPANDED).length());
249 return KFI_FC_WIDTH_SEMIEXPANDED;
251 if(0==str.indexOf(i18n(KFI_WIDTH_EXPANDED), 0, Qt::CaseInsensitive))
253 newStr=str.mid(i18n(KFI_WIDTH_EXPANDED).length());
254 return KFI_FC_WIDTH_EXPANDED;
256 if(0==str.indexOf(i18n(KFI_WIDTH_EXTRAEXPANDED), 0, Qt::CaseInsensitive))
258 newStr=str.mid(i18n(KFI_WIDTH_EXTRAEXPANDED).length());
259 return KFI_FC_WIDTH_EXTRAEXPANDED;
261 if(0==str.indexOf(i18n(KFI_WIDTH_ULTRAEXPANDED), 0, Qt::CaseInsensitive))
263 newStr=str.mid(i18n(KFI_WIDTH_ULTRAEXPANDED).length());
264 return KFI_FC_WIDTH_ULTRAEXPANDED;
267 newStr=str;
268 return KFI_FC_WIDTH_NORMAL;
271 int strToSlant(const QString &str)
273 if(-1!=str.indexOf(i18n(KFI_SLANT_ITALIC)))
274 return FC_SLANT_ITALIC;
275 if(-1!=str.indexOf(i18n(KFI_SLANT_OBLIQUE)))
276 return FC_SLANT_OBLIQUE;
277 return FC_SLANT_ROMAN;
280 quint32 createStyleVal(const QString &name)
282 int pos;
284 if(-1==(pos=name.indexOf(", "))) // No style information...
285 return createStyleVal(FC_WEIGHT_REGULAR,
286 #ifdef KFI_FC_NO_WIDTHS
287 KFI_NULL_SETTING
288 #else
289 KFI_FC_WIDTH_NORMAL
290 #endif
291 , FC_SLANT_ROMAN);
292 else
294 QString style(name.mid(pos+2));
296 return createStyleVal(strToWeight(style, style),
297 #ifdef KFI_FC_NO_WIDTHS
298 KFI_NULL_SETTING
299 #else
300 strToWidth(style, style)
301 #endif
302 , strToSlant(style));
306 QString styleValToStr(quint32 style)
308 QString str;
309 int weight, width, slant;
311 decomposeStyleVal(style, weight, width, slant);
312 str.sprintf("0X%02X%02X%02X\n", weight, width, slant);
313 return str;
316 void decomposeStyleVal(quint32 styleInfo, int &weight, int &width, int &slant)
318 weight=(styleInfo&0xFF0000)>>16;
319 width=(styleInfo&0x00FF00)>>8;
320 slant=(styleInfo&0x0000FF);
323 quint32 styleValFromStr(const QString &style)
325 if(style.isEmpty())
326 return KFI_NO_STYLE_INFO;
327 else
329 quint32 val;
331 QTextStream(const_cast<QString *>(&style), QIODevice::ReadOnly) >> val;
332 return val;
336 QString getFcString(FcPattern *pat, const char *val, int index)
338 QString rv;
339 FcChar8 *fcStr;
341 if(FcResultMatch==FcPatternGetString(pat, val, index, &fcStr))
342 rv=QString::fromUtf8((char *)fcStr);
344 return rv;
347 #ifdef KFI_USE_TRANSLATED_FAMILY_NAME
349 // Try to get the 'string' that matches the users KDE locale..
350 QString getFcLangString(FcPattern *pat, const char *val, const char *valLang)
352 QString rv;
353 QStringList kdeLangs=KGlobal::locale()->languageList(),
354 fontLangs;
355 QStringList::ConstIterator it(kdeLangs.begin()),
356 end(kdeLangs.end());
358 // Create list of langs that this font's 'val' is encoded in...
359 for(int i=0; true; ++i)
361 QString lang=getFcString(pat, valLang, i);
363 if(lang.isEmpty())
364 break;
365 else
366 fontLangs.append(lang);
369 // Now go through the user's KDE locale, and try to find a font match...
370 for(; it!=end; ++it)
372 int index=fontLangs.findIndex(*it);
374 if(-1!=index)
376 rv=getFcString(pat, val, index);
378 if(!rv.isEmpty())
379 break;
383 if(rv.isEmpty())
384 rv=getFcString(pat, val, 0);
385 return rv;
387 #endif
389 int getFcInt(FcPattern *pat, const char *val, int index, int def)
391 int rv;
393 if (FcResultMatch==FcPatternGetInteger(pat, val, index, &rv))
394 return rv;
395 return def;
398 void getDetails(FcPattern *pat, QString &name, quint32 &styleVal, int &index)
400 int weight=getFcInt(pat, FC_WEIGHT, 0, KFI_NULL_SETTING),
401 width=
402 #ifdef KFI_FC_NO_WIDTHS
403 KFI_NULL_SETTING,
404 #else
405 getFcInt(pat, FC_WIDTH, 0, KFI_NULL_SETTING),
406 #endif
407 slant=getFcInt(pat, FC_SLANT, 0, KFI_NULL_SETTING);
409 index=getFcInt(pat, FC_INDEX, 0, 0);
410 name=createName(pat, weight, width, slant);
411 styleVal=createStyleVal(weight, width, slant);
414 QString createName(FcPattern *pat)
416 return createName(pat, getFcInt(pat, FC_WEIGHT, 0),
417 #ifdef KFI_FC_NO_WIDTHS
418 KFI_NULL_SETTING,
419 #else
420 getFcInt(pat, FC_WIDTH, 0),
421 #endif
422 getFcInt(pat, FC_SLANT, 0));
425 QString createName(FcPattern *pat, int weight, int width, int slant)
427 #ifdef KFI_USE_TRANSLATED_FAMILY_NAME
428 QString family(getFcLangString(pat, FC_FAMILY, FC_FAMILYLANG));
429 #else
430 QString family(getFcString(pat, FC_FAMILY, 0));
431 #endif
433 return createName(family, weight, width, slant);
436 QString createName(const QString &family, quint32 styleInfo)
438 int weight, width, slant;
440 decomposeStyleVal(styleInfo, weight, width, slant);
441 return createName(family, weight, width, slant);
444 QString createName(const QString &family, int weight, int width, int slant)
447 //CPD: TODO: the names *need* to match up with kfontchooser's...
448 // : Removing KFI_DISPLAY_OBLIQUE and KFI_DISPLAY_MEDIUM help this.
449 // However, I have at least one bad font:
450 // Rockwell Extra Bold. Both fontconfig, and kcmshell fonts list family
451 // as "Rockwell Extra Bold" -- good (well at least they match). *But* fontconfig
452 // is returning the weight "Extra Bold", and kcmshell fonts is using "Bold" :-(
454 QString name(family),
455 weightString,
456 widthString,
457 slantString;
458 bool comma=false;
460 #ifndef KFI_FC_NO_WIDTHS
461 if(KFI_NULL_SETTING!=width)
462 widthString=widthStr(width);
463 #endif
465 if(KFI_NULL_SETTING!=slant)
466 slantString=slantStr(slant);
469 // If weight is "Regular", we only want to display it if slant and width are empty.
470 if(KFI_NULL_SETTING!=weight)
472 weightString=weightStr(weight, !slantString.isEmpty() || !widthString.isEmpty());
474 if(!weightString.isEmpty())
476 name+=QString(", ")+weightString;
477 comma=true;
481 #ifndef KFI_FC_NO_WIDTHS
482 if(!widthString.isEmpty())
484 if(!comma)
486 name+=QChar(',');
487 comma=true;
489 name+=QChar(' ')+widthString;
491 #endif
493 if(!slantString.isEmpty())
495 if(!comma)
497 name+=QChar(',');
498 comma=true;
500 name+=QChar(' ')+slantString;
503 return name;
506 QString weightStr(int w, bool emptyNormal)
508 switch(weight(w))
510 case FC_WEIGHT_THIN:
511 return i18n(KFI_WEIGHT_THIN);
512 case FC_WEIGHT_EXTRALIGHT:
513 return i18n(KFI_WEIGHT_EXTRALIGHT);
514 case FC_WEIGHT_LIGHT:
515 return i18n(KFI_WEIGHT_LIGHT);
516 case FC_WEIGHT_MEDIUM:
517 #ifdef KFI_DISPLAY_MEDIUM
518 return i18n(KFI_WEIGHT_MEDIUM);
519 #endif
520 case FC_WEIGHT_REGULAR:
521 return emptyNormal ? QString() : i18n(KFI_WEIGHT_REGULAR);
522 case FC_WEIGHT_DEMIBOLD:
523 return i18n(KFI_WEIGHT_DEMIBOLD);
524 case FC_WEIGHT_BOLD:
525 return i18n(KFI_WEIGHT_BOLD);
526 case FC_WEIGHT_EXTRABOLD:
527 return i18n(KFI_WEIGHT_EXTRABOLD);
528 default:
529 return i18n(KFI_WEIGHT_BLACK);
533 QString widthStr(int w, bool emptyNormal)
535 switch(width(w))
537 case KFI_FC_WIDTH_ULTRACONDENSED:
538 return i18n(KFI_WIDTH_ULTRACONDENSED);
539 case KFI_FC_WIDTH_EXTRACONDENSED:
540 return i18n(KFI_WIDTH_EXTRACONDENSED);
541 case KFI_FC_WIDTH_CONDENSED:
542 return i18n(KFI_WIDTH_CONDENSED);
543 case KFI_FC_WIDTH_SEMICONDENSED:
544 return i18n(KFI_WIDTH_SEMICONDENSED);
545 case KFI_FC_WIDTH_NORMAL:
546 return emptyNormal ? QString() : i18n(KFI_WIDTH_NORMAL);
547 case KFI_FC_WIDTH_SEMIEXPANDED:
548 return i18n(KFI_WIDTH_SEMIEXPANDED);
549 case KFI_FC_WIDTH_EXPANDED:
550 return i18n(KFI_WIDTH_EXPANDED);
551 case KFI_FC_WIDTH_EXTRAEXPANDED:
552 return i18n(KFI_WIDTH_EXTRAEXPANDED);
553 default:
554 return i18n(KFI_WIDTH_ULTRAEXPANDED);
558 QString slantStr(int s, bool emptyNormal)
560 switch(slant(s))
562 case FC_SLANT_OBLIQUE:
563 #ifdef KFI_DISPLAY_OBLIQUE
564 return i18n(KFI_SLANT_OBLIQUE);
565 #endif
566 case FC_SLANT_ITALIC:
567 return i18n(KFI_SLANT_ITALIC);
568 default:
569 return emptyNormal ? QString() : i18n(KFI_SLANT_ROMAN);
573 QString spacingStr(int s)
575 switch(spacing(s))
577 case FC_MONO:
578 return i18n(KFI_SPACING_MONO);
579 case FC_CHARCELL:
580 return i18n(KFI_SPACING_CHARCELL);
581 default:
582 return i18n(KFI_SPACING_PROPORTIONAL);
586 bool bitmapsEnabled()
589 // On some systems, such as KUbuntu, fontconfig is configured to ignore all bitmap fonts.
590 // The following check tries to get a list of installed bitmaps, if it an empty list is returned
591 // it is assumed that bitmaps are disabled.
593 static bool enabled(false);
594 static bool checked(false); // Do not keep on checking!
596 if(!checked)
598 FcObjectSet *os = FcObjectSetBuild(FC_FAMILY, (void *)0);
599 FcPattern *pat = FcPatternBuild(NULL, FC_SCALABLE, FcTypeBool, FcFalse, NULL);
600 FcFontSet *set = FcFontList(0, pat, os);
602 FcPatternDestroy(pat);
603 FcObjectSetDestroy(os);
605 if (set)
607 if(set->nfont)
608 enabled=true;
610 FcFontSetDestroy(set);
612 checked=true;
615 return enabled;