File indexing completed on 2025-01-12 05:02:06

0001 /*
0002     SPDX-FileCopyrightText: 2003-2007 Craig Drummond <craig@kde.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "Fc.h"
0007 #include <QHash>
0008 #include <QTextStream>
0009 #include <QUrlQuery>
0010 //
0011 // KDE font chooser always seems to use Italic - for both Oblique, and Italic. So I guess
0012 // the fonts:/ should do too - so as to appear more unified.
0013 //
0014 // ditto with respect to Medium/Regular
0015 #define KFI_HAVE_OBLIQUE // Do we differentiate between Italic and Oblique when comparing slants?
0016 #define KFI_DISPLAY_OBLIQUE // Do we want to list "Oblique"? Or always use Italic?
0017 #define KFI_HAVE_MEDIUM_WEIGHT // Do we differentiate between Medium and Regular weights when comparing weights?
0018 #define KFI_DISPLAY_MEDIUM // Do we want to list "Medium"? Or always use Regular?
0019 
0020 namespace KFI
0021 {
0022 namespace FC
0023 {
0024 #define FC_PROTOCOL QString::fromLatin1("fontconfig")
0025 #define FC_STYLE_QUERY QString::fromLatin1("style")
0026 #define FC_FILE_QUERY QString::fromLatin1("file")
0027 #define FC_INDEX_QUERY QString::fromLatin1("index")
0028 
0029 QUrl encode(const QString &name, quint32 style, const QString &file, int index)
0030 {
0031     QUrl url(QUrl::fromLocalFile(name));
0032     QUrlQuery query;
0033 
0034     url.setScheme(FC_PROTOCOL);
0035     query.addQueryItem(FC_STYLE_QUERY, QString::number(style));
0036     if (!file.isEmpty()) {
0037         query.addQueryItem(FC_FILE_QUERY, file);
0038     }
0039     if (index > 0) {
0040         query.addQueryItem(FC_INDEX_QUERY, QString::number(index));
0041     }
0042 
0043     url.setQuery(query);
0044     return url;
0045 }
0046 
0047 Misc::TFont decode(const QUrl &url)
0048 {
0049     QUrlQuery query(url);
0050     return FC_PROTOCOL == url.scheme() ? Misc::TFont(url.path(), query.queryItemValue(FC_STYLE_QUERY).toUInt()) : Misc::TFont(QString(), 0);
0051 }
0052 
0053 QString getFile(const QUrl &url)
0054 {
0055     QUrlQuery query(url);
0056     return FC_PROTOCOL == url.scheme() ? query.queryItemValue(FC_FILE_QUERY) : QString();
0057 }
0058 
0059 int getIndex(const QUrl &url)
0060 {
0061     QUrlQuery query(url);
0062     return FC_PROTOCOL == url.scheme() ? query.queryItemValue(FC_INDEX_QUERY).toInt() : 0;
0063 }
0064 
0065 int weight(int w)
0066 {
0067     if (KFI_NULL_SETTING == w) {
0068 #ifdef KFI_HAVE_MEDIUM_WEIGHT
0069         return FC_WEIGHT_MEDIUM;
0070     }
0071 #else
0072         return FC_WEIGHT_REGULAR;
0073 #endif
0074 
0075     if (w < FC_WEIGHT_EXTRALIGHT) {
0076         return FC_WEIGHT_THIN;
0077     }
0078 
0079     if (w < (FC_WEIGHT_EXTRALIGHT + FC_WEIGHT_LIGHT) / 2) {
0080         return FC_WEIGHT_EXTRALIGHT;
0081     }
0082 
0083     if (w < (FC_WEIGHT_LIGHT + FC_WEIGHT_REGULAR) / 2) {
0084         return FC_WEIGHT_LIGHT;
0085     }
0086 
0087 #ifdef KFI_HAVE_MEDIUM_WEIGHT
0088     if (w < (FC_WEIGHT_REGULAR + FC_WEIGHT_MEDIUM) / 2) {
0089         return FC_WEIGHT_REGULAR;
0090     }
0091 
0092     if (w < (FC_WEIGHT_MEDIUM + FC_WEIGHT_DEMIBOLD) / 2) {
0093         return FC_WEIGHT_MEDIUM;
0094     }
0095 #else
0096         if (w < (FC_WEIGHT_REGULAR + FC_WEIGHT_DEMIBOLD) / 2)
0097             return FC_WEIGHT_REGULAR;
0098 #endif
0099 
0100     if (w < (FC_WEIGHT_DEMIBOLD + FC_WEIGHT_BOLD) / 2) {
0101         return FC_WEIGHT_DEMIBOLD;
0102     }
0103 
0104     if (w < (FC_WEIGHT_BOLD + FC_WEIGHT_EXTRABOLD) / 2) {
0105         return FC_WEIGHT_BOLD;
0106     }
0107 
0108     if (w < (FC_WEIGHT_EXTRABOLD + FC_WEIGHT_BLACK) / 2) {
0109         return FC_WEIGHT_EXTRABOLD;
0110     }
0111 
0112     return FC_WEIGHT_BLACK;
0113 }
0114 
0115 int width(int w)
0116 {
0117     if (KFI_NULL_SETTING == w) {
0118         return KFI_FC_WIDTH_NORMAL;
0119     }
0120 
0121     if (w < KFI_FC_WIDTH_EXTRACONDENSED) {
0122         return KFI_FC_WIDTH_EXTRACONDENSED;
0123     }
0124 
0125     if (w < (KFI_FC_WIDTH_EXTRACONDENSED + KFI_FC_WIDTH_CONDENSED) / 2) {
0126         return KFI_FC_WIDTH_EXTRACONDENSED;
0127     }
0128 
0129     if (w < (KFI_FC_WIDTH_CONDENSED + KFI_FC_WIDTH_SEMICONDENSED) / 2) {
0130         return KFI_FC_WIDTH_CONDENSED;
0131     }
0132 
0133     if (w < (KFI_FC_WIDTH_SEMICONDENSED + KFI_FC_WIDTH_NORMAL) / 2) {
0134         return KFI_FC_WIDTH_SEMICONDENSED;
0135     }
0136 
0137     if (w < (KFI_FC_WIDTH_NORMAL + KFI_FC_WIDTH_SEMIEXPANDED) / 2) {
0138         return KFI_FC_WIDTH_NORMAL;
0139     }
0140 
0141     if (w < (KFI_FC_WIDTH_SEMIEXPANDED + KFI_FC_WIDTH_EXPANDED) / 2) {
0142         return KFI_FC_WIDTH_SEMIEXPANDED;
0143     }
0144 
0145     if (w < (KFI_FC_WIDTH_EXPANDED + KFI_FC_WIDTH_EXTRAEXPANDED) / 2) {
0146         return KFI_FC_WIDTH_EXPANDED;
0147     }
0148 
0149     if (w < (KFI_FC_WIDTH_EXTRAEXPANDED + KFI_FC_WIDTH_ULTRAEXPANDED) / 2) {
0150         return KFI_FC_WIDTH_EXTRAEXPANDED;
0151     }
0152 
0153     return KFI_FC_WIDTH_ULTRAEXPANDED;
0154 }
0155 
0156 int slant(int s)
0157 {
0158     if (KFI_NULL_SETTING == s || s < FC_SLANT_ITALIC) {
0159         return FC_SLANT_ROMAN;
0160     }
0161 
0162 #ifdef KFI_HAVE_OBLIQUE
0163     if (s < (FC_SLANT_ITALIC + FC_SLANT_OBLIQUE) / 2) {
0164         return FC_SLANT_ITALIC;
0165     }
0166 
0167     return FC_SLANT_OBLIQUE;
0168 #else
0169         return FC_SLANT_ITALIC;
0170 #endif
0171 }
0172 
0173 int spacing(int s)
0174 {
0175     if (s < FC_MONO) {
0176         return FC_PROPORTIONAL;
0177     }
0178 
0179     if (s < (FC_MONO + FC_CHARCELL) / 2) {
0180         return FC_MONO;
0181     }
0182 
0183     return FC_CHARCELL;
0184 }
0185 
0186 int strToWeight(const QString &str, QString &newStr)
0187 {
0188     if (0 == str.indexOf(KFI_WEIGHT_THIN.toString(), 0, Qt::CaseInsensitive)) {
0189         newStr = str.mid(KFI_WEIGHT_THIN.toString().length());
0190         return FC_WEIGHT_THIN;
0191     }
0192     if (0 == str.indexOf(KFI_WEIGHT_EXTRALIGHT.toString(), 0, Qt::CaseInsensitive)) {
0193         newStr = str.mid(KFI_WEIGHT_EXTRALIGHT.toString().length());
0194         return FC_WEIGHT_EXTRALIGHT;
0195     }
0196     // if(0==str.indexOf(i18n(KFI_WEIGHT_ULTRALIGHT), 0, Qt::CaseInsensitive))
0197     //{
0198     //    newStr=str.mid(i18n(KFI_WEIGHT_ULTRALIGHT).length());
0199     //    return FC_WEIGHT_ULTRALIGHT;
0200     //}
0201     if (0 == str.indexOf(KFI_WEIGHT_LIGHT.toString(), 0, Qt::CaseInsensitive)) {
0202         newStr = str.mid(KFI_WEIGHT_LIGHT.toString().length());
0203         return FC_WEIGHT_LIGHT;
0204     }
0205     if (0 == str.indexOf(KFI_WEIGHT_REGULAR.toString(), 0, Qt::CaseInsensitive)) {
0206         newStr = str.mid(KFI_WEIGHT_REGULAR.toString().length());
0207         return FC_WEIGHT_REGULAR;
0208     }
0209     // if(0==str.indexOf(i18n(KFI_WEIGHT_NORMAL), 0, Qt::CaseInsensitive))
0210     //{
0211     //    newStr=str.mid(i18n(KFI_WEIGHT_NORMAL).length());
0212     //    return FC_WEIGHT_NORMAL;
0213     //}
0214     if (0 == str.indexOf(KFI_WEIGHT_MEDIUM.toString(), 0, Qt::CaseInsensitive)) {
0215         newStr = str.mid(KFI_WEIGHT_MEDIUM.toString().length());
0216         return FC_WEIGHT_MEDIUM;
0217     }
0218     if (0 == str.indexOf(KFI_WEIGHT_DEMIBOLD.toString(), 0, Qt::CaseInsensitive)) {
0219         newStr = str.mid(KFI_WEIGHT_DEMIBOLD.toString().length());
0220         return FC_WEIGHT_DEMIBOLD;
0221     }
0222     // if(0==str.indexOf(i18n(KFI_WEIGHT_SEMIBOLD), 0, Qt::CaseInsensitive))
0223     //{
0224     //    newStr=str.mid(i18n(KFI_WEIGHT_SEMIBOLD).length());
0225     //    return FC_WEIGHT_SEMIBOLD;
0226     //}
0227     if (0 == str.indexOf(KFI_WEIGHT_BOLD.toString(), 0, Qt::CaseInsensitive)) {
0228         newStr = str.mid(KFI_WEIGHT_BOLD.toString().length());
0229         return FC_WEIGHT_BOLD;
0230     }
0231     if (0 == str.indexOf(KFI_WEIGHT_EXTRABOLD.toString(), 0, Qt::CaseInsensitive)) {
0232         newStr = str.mid(KFI_WEIGHT_EXTRABOLD.toString().length());
0233         return FC_WEIGHT_EXTRABOLD;
0234     }
0235     // if(0==str.indexOf(i18n(KFI_WEIGHT_ULTRABOLD), 0, Qt::CaseInsensitive))
0236     //{
0237     //    newStr=str.mid(i18n(KFI_WEIGHT_ULTRABOLD).length());
0238     //    return FC_WEIGHT_ULTRABOLD;
0239     //}
0240     if (0 == str.indexOf(KFI_WEIGHT_BLACK.toString(), 0, Qt::CaseInsensitive)) {
0241         newStr = str.mid(KFI_WEIGHT_BLACK.toString().length());
0242         return FC_WEIGHT_BLACK;
0243     }
0244     if (0 == str.indexOf(KFI_WEIGHT_BLACK.toString(), 0, Qt::CaseInsensitive)) {
0245         newStr = str.mid(KFI_WEIGHT_BLACK.toString().length());
0246         return FC_WEIGHT_BLACK;
0247     }
0248 
0249     newStr = str;
0250     return FC_WEIGHT_REGULAR;
0251 }
0252 
0253 int strToWidth(const QString &str, QString &newStr)
0254 {
0255     if (0 == str.indexOf(KFI_WIDTH_ULTRACONDENSED.toString(), 0, Qt::CaseInsensitive)) {
0256         newStr = str.mid(KFI_WIDTH_ULTRACONDENSED.toString().length());
0257         return KFI_FC_WIDTH_ULTRACONDENSED;
0258     }
0259     if (0 == str.indexOf(KFI_WIDTH_EXTRACONDENSED.toString(), 0, Qt::CaseInsensitive)) {
0260         newStr = str.mid(KFI_WIDTH_EXTRACONDENSED.toString().length());
0261         return KFI_FC_WIDTH_EXTRACONDENSED;
0262     }
0263     if (0 == str.indexOf(KFI_WIDTH_CONDENSED.toString(), 0, Qt::CaseInsensitive)) {
0264         newStr = str.mid(KFI_WIDTH_CONDENSED.toString().length());
0265         return KFI_FC_WIDTH_CONDENSED;
0266     }
0267     if (0 == str.indexOf(KFI_WIDTH_SEMICONDENSED.toString(), 0, Qt::CaseInsensitive)) {
0268         newStr = str.mid(KFI_WIDTH_SEMICONDENSED.toString().length());
0269         return KFI_FC_WIDTH_SEMICONDENSED;
0270     }
0271     if (0 == str.indexOf(KFI_WIDTH_NORMAL.toString(), 0, Qt::CaseInsensitive)) {
0272         newStr = str.mid(KFI_WIDTH_NORMAL.toString().length());
0273         return KFI_FC_WIDTH_NORMAL;
0274     }
0275     if (0 == str.indexOf(KFI_WIDTH_SEMIEXPANDED.toString(), 0, Qt::CaseInsensitive)) {
0276         newStr = str.mid(KFI_WIDTH_SEMIEXPANDED.toString().length());
0277         return KFI_FC_WIDTH_SEMIEXPANDED;
0278     }
0279     if (0 == str.indexOf(KFI_WIDTH_EXPANDED.toString(), 0, Qt::CaseInsensitive)) {
0280         newStr = str.mid(KFI_WIDTH_EXPANDED.toString().length());
0281         return KFI_FC_WIDTH_EXPANDED;
0282     }
0283     if (0 == str.indexOf(KFI_WIDTH_EXTRAEXPANDED.toString(), 0, Qt::CaseInsensitive)) {
0284         newStr = str.mid(KFI_WIDTH_EXTRAEXPANDED.toString().length());
0285         return KFI_FC_WIDTH_EXTRAEXPANDED;
0286     }
0287     if (0 == str.indexOf(KFI_WIDTH_ULTRAEXPANDED.toString(), 0, Qt::CaseInsensitive)) {
0288         newStr = str.mid(KFI_WIDTH_ULTRAEXPANDED.toString().length());
0289         return KFI_FC_WIDTH_ULTRAEXPANDED;
0290     }
0291 
0292     newStr = str;
0293     return KFI_FC_WIDTH_NORMAL;
0294 }
0295 
0296 int strToSlant(const QString &str)
0297 {
0298     if (-1 != str.indexOf(KFI_SLANT_ITALIC.toString())) {
0299         return FC_SLANT_ITALIC;
0300     }
0301     if (-1 != str.indexOf(KFI_SLANT_OBLIQUE.toString())) {
0302         return FC_SLANT_OBLIQUE;
0303     }
0304     return FC_SLANT_ROMAN;
0305 }
0306 
0307 quint32 createStyleVal(const QString &name)
0308 {
0309     int pos;
0310 
0311     if (-1 == (pos = name.indexOf(", "))) { // No style information...
0312         return createStyleVal(FC_WEIGHT_REGULAR,
0313 #ifdef KFI_FC_NO_WIDTHS
0314                               KFI_NULL_SETTING
0315 #else
0316                                   KFI_FC_WIDTH_NORMAL
0317 #endif
0318                               ,
0319                               FC_SLANT_ROMAN);
0320     } else {
0321         QString style(name.mid(pos + 2));
0322 
0323         return createStyleVal(strToWeight(style, style),
0324 #ifdef KFI_FC_NO_WIDTHS
0325                               KFI_NULL_SETTING
0326 #else
0327                                   strToWidth(style, style)
0328 #endif
0329                               ,
0330                               strToSlant(style));
0331     }
0332 }
0333 
0334 QString styleValToStr(quint32 style)
0335 {
0336     return QStringLiteral("0X%1\n").arg(style, 6, 16, QLatin1Char('0')).toUpper();
0337 }
0338 
0339 void decomposeStyleVal(quint32 styleInfo, int &weight, int &width, int &slant)
0340 {
0341     weight = (styleInfo & 0xFF0000) >> 16;
0342     width = (styleInfo & 0x00FF00) >> 8;
0343     slant = (styleInfo & 0x0000FF);
0344 }
0345 
0346 quint32 styleValFromStr(const QString &style)
0347 {
0348     if (style.isEmpty()) {
0349         return KFI_NO_STYLE_INFO;
0350     } else {
0351         quint32 val;
0352 
0353         QTextStream(const_cast<QString *>(&style), QIODevice::ReadOnly) >> val;
0354         return val;
0355     }
0356 }
0357 
0358 QString getFcString(FcPattern *pat, const char *val, int index)
0359 {
0360     QString rv;
0361     FcChar8 *fcStr;
0362 
0363     if (FcResultMatch == FcPatternGetString(pat, val, index, &fcStr)) {
0364         rv = QString::fromUtf8((char *)fcStr);
0365     }
0366 
0367     return rv;
0368 }
0369 
0370 //
0371 // TODO: How correct is this?
0372 // Qt/Gtk seem to prefer font family names with FAMILY_LANG=="en"
0373 // ...so if possible, use that. Else, use the first non "xx" lang.
0374 QString getFcLangString(FcPattern *pat, const char *val, const char *valLang)
0375 {
0376     int langIndex = -1;
0377 
0378     for (int i = 0; true; ++i) {
0379         QString lang = getFcString(pat, valLang, i);
0380 
0381         if (lang.isEmpty()) {
0382             break;
0383         } else if (QString::fromLatin1("en") == lang) {
0384             return getFcString(pat, val, i);
0385         } else if (QString::fromLatin1("xx") != lang && -1 == langIndex) {
0386             langIndex = i;
0387         }
0388     }
0389 
0390     return getFcString(pat, val, langIndex > 0 ? langIndex : 0);
0391 }
0392 
0393 int getFcInt(FcPattern *pat, const char *val, int index, int def)
0394 {
0395     int rv;
0396 
0397     if (FcResultMatch == FcPatternGetInteger(pat, val, index, &rv)) {
0398         return rv;
0399     }
0400     return def;
0401 }
0402 
0403 QString getName(const QString &file)
0404 {
0405     int count = 0;
0406     FcPattern *pat = FcFreeTypeQuery((const FcChar8 *)(QFile::encodeName(file).constData()), 0, nullptr, &count);
0407     QString name(i18n("Unknown"));
0408 
0409     if (pat) {
0410         name = FC::createName(pat);
0411         FcPatternDestroy(pat);
0412     }
0413 
0414     return name;
0415 }
0416 
0417 void getDetails(FcPattern *pat, QString &family, quint32 &styleVal, int &index, QString &foundry)
0418 {
0419     int weight = getFcInt(pat, FC_WEIGHT, 0, KFI_NULL_SETTING),
0420         width =
0421 #ifdef KFI_FC_NO_WIDTHS
0422             KFI_NULL_SETTING,
0423 #else
0424                 getFcInt(pat, FC_WIDTH, 0, KFI_NULL_SETTING),
0425 #endif
0426         slant = getFcInt(pat, FC_SLANT, 0, KFI_NULL_SETTING);
0427 
0428     index = getFcInt(pat, FC_INDEX, 0, 0);
0429     // #ifdef KFI_USE_TRANSLATED_FAMILY_NAME
0430     family = getFcLangString(pat, FC_FAMILY, FC_FAMILYLANG);
0431     // #else
0432     //     family=getFcString(pat, FC_FAMILY, 0);
0433     // #endif
0434     styleVal = createStyleVal(weight, width, slant);
0435     foundry = getFcString(pat, FC_FOUNDRY, 0);
0436 }
0437 
0438 QString createName(FcPattern *pat)
0439 {
0440     QString family, foundry;
0441     quint32 styleVal;
0442     int index;
0443 
0444     getDetails(pat, family, styleVal, index, foundry);
0445     return createName(family, styleVal);
0446 }
0447 
0448 QString createName(const QString &family, quint32 styleInfo)
0449 {
0450     int weight, width, slant;
0451 
0452     decomposeStyleVal(styleInfo, weight, width, slant);
0453     return createName(family, weight, width, slant);
0454 }
0455 
0456 QString createName(const QString &family, int weight, int width, int slant)
0457 {
0458     return family + QString::fromLatin1(", ") + createStyleName(weight, width, slant);
0459 }
0460 
0461 QString createStyleName(quint32 styleInfo)
0462 {
0463     int weight, width, slant;
0464 
0465     decomposeStyleVal(styleInfo, weight, width, slant);
0466     return createStyleName(weight, width, slant);
0467 }
0468 
0469 QString createStyleName(int weight, int width, int slant)
0470 {
0471     //
0472     // CPD: TODO: the names *need* to match up with kfontchooser's...
0473     //         : Removing KFI_DISPLAY_OBLIQUE and KFI_DISPLAY_MEDIUM help this.
0474     //           However, I have at least one bad font:
0475     //               Rockwell Extra Bold. Both fontconfig, and kcmshell fonts list family
0476     //               as "Rockwell Extra Bold" -- good (well at least they match). *But* fontconfig
0477     //               is returning the weight "Extra Bold", and kcmshell fonts is using "Bold" :-(
0478     //
0479     QString name, weightString, widthString, slantString;
0480 
0481 #ifndef KFI_FC_NO_WIDTHS
0482     if (KFI_NULL_SETTING != width) {
0483         widthString = widthStr(width);
0484     }
0485 #endif
0486 
0487     if (KFI_NULL_SETTING != slant) {
0488         slantString = slantStr(slant);
0489     }
0490 
0491     //
0492     // If weight is "Regular", we only want to display it if slant and width are empty.
0493     if (KFI_NULL_SETTING != weight) {
0494         weightString = weightStr(weight, !slantString.isEmpty() || !widthString.isEmpty());
0495 
0496         if (!weightString.isEmpty()) {
0497             name = weightString;
0498         }
0499     }
0500 
0501 #ifndef KFI_FC_NO_WIDTHS
0502     if (!widthString.isEmpty()) {
0503         if (!name.isEmpty()) {
0504             name += QChar(' ');
0505         }
0506         name += widthString;
0507     }
0508 #endif
0509 
0510     if (!slantString.isEmpty()) {
0511         if (!name.isEmpty()) {
0512             name += QChar(' ');
0513         }
0514         name += slantString;
0515     }
0516     return name;
0517 }
0518 
0519 QString weightStr(int w, bool emptyNormal)
0520 {
0521     switch (weight(w)) {
0522     case FC_WEIGHT_THIN:
0523         return KFI_WEIGHT_THIN.toString();
0524     case FC_WEIGHT_EXTRALIGHT:
0525         return KFI_WEIGHT_EXTRALIGHT.toString();
0526     case FC_WEIGHT_LIGHT:
0527         return KFI_WEIGHT_LIGHT.toString();
0528     case FC_WEIGHT_MEDIUM:
0529 #ifdef KFI_DISPLAY_MEDIUM
0530         return KFI_WEIGHT_MEDIUM.toString();
0531 #endif
0532     case FC_WEIGHT_REGULAR:
0533         return emptyNormal ? QString() : KFI_WEIGHT_REGULAR.toString();
0534     case FC_WEIGHT_DEMIBOLD:
0535         return KFI_WEIGHT_DEMIBOLD.toString();
0536     case FC_WEIGHT_BOLD:
0537         return KFI_WEIGHT_BOLD.toString();
0538     case FC_WEIGHT_EXTRABOLD:
0539         return KFI_WEIGHT_EXTRABOLD.toString();
0540     default:
0541         return KFI_WEIGHT_BLACK.toString();
0542     }
0543 }
0544 
0545 QString widthStr(int w, bool emptyNormal)
0546 {
0547     switch (width(w)) {
0548     case KFI_FC_WIDTH_ULTRACONDENSED:
0549         return KFI_WIDTH_ULTRACONDENSED.toString();
0550     case KFI_FC_WIDTH_EXTRACONDENSED:
0551         return KFI_WIDTH_EXTRACONDENSED.toString();
0552     case KFI_FC_WIDTH_CONDENSED:
0553         return KFI_WIDTH_CONDENSED.toString();
0554     case KFI_FC_WIDTH_SEMICONDENSED:
0555         return KFI_WIDTH_SEMICONDENSED.toString();
0556     case KFI_FC_WIDTH_NORMAL:
0557         return emptyNormal ? QString() : KFI_WIDTH_NORMAL.toString();
0558     case KFI_FC_WIDTH_SEMIEXPANDED:
0559         return KFI_WIDTH_SEMIEXPANDED.toString();
0560     case KFI_FC_WIDTH_EXPANDED:
0561         return KFI_WIDTH_EXPANDED.toString();
0562     case KFI_FC_WIDTH_EXTRAEXPANDED:
0563         return KFI_WIDTH_EXTRAEXPANDED.toString();
0564     default:
0565         return KFI_WIDTH_ULTRAEXPANDED.toString();
0566     }
0567 }
0568 
0569 QString slantStr(int s, bool emptyNormal)
0570 {
0571     switch (slant(s)) {
0572     case FC_SLANT_OBLIQUE:
0573 #ifdef KFI_DISPLAY_OBLIQUE
0574         return KFI_SLANT_OBLIQUE.toString();
0575 #endif
0576     case FC_SLANT_ITALIC:
0577         return KFI_SLANT_ITALIC.toString();
0578     default:
0579         return emptyNormal ? QString() : KFI_SLANT_ROMAN.toString();
0580     }
0581 }
0582 
0583 QString spacingStr(int s)
0584 {
0585     switch (spacing(s)) {
0586     case FC_MONO:
0587         return KFI_SPACING_MONO.toString();
0588     case FC_CHARCELL:
0589         return KFI_SPACING_CHARCELL.toString();
0590     default:
0591         return KFI_SPACING_PROPORTIONAL.toString();
0592     }
0593 }
0594 
0595 bool bitmapsEnabled()
0596 {
0597     //
0598     // On some systems, such as KUbuntu, fontconfig is configured to ignore all bitmap fonts.
0599     // The following check tries to get a list of installed bitmaps, if it an empty list is returned
0600     // it is assumed that bitmaps are disabled.
0601 
0602     static bool enabled(false);
0603     static bool checked(false); // Do not keep on checking!
0604 
0605     if (!checked) {
0606         FcObjectSet *os = FcObjectSetBuild(FC_FAMILY, (void *)nullptr);
0607         FcPattern *pat = FcPatternBuild(nullptr, FC_SCALABLE, FcTypeBool, FcFalse, NULL);
0608         FcFontSet *set = FcFontList(nullptr, pat, os);
0609 
0610         FcPatternDestroy(pat);
0611         FcObjectSetDestroy(os);
0612 
0613         if (set) {
0614             if (set->nfont) {
0615                 enabled = true;
0616             }
0617 
0618             FcFontSetDestroy(set);
0619         }
0620         checked = true;
0621     }
0622 
0623     return enabled;
0624 }
0625 
0626 }
0627 
0628 }