| 140 | } |
| 141 | |
| 142 | bool FontConfigFindFallbackFont(FontCacheSettings *settings, const std::string &language_isocode, MissingGlyphSearcher *callback) |
| 143 | { |
| 144 | bool ret = false; |
| 145 | |
| 146 | if (!FcInit()) return ret; |
| 147 | |
| 148 | auto fc_instance = AutoRelease<FcConfig, FcConfigDestroy>(FcConfigReference(nullptr)); |
| 149 | assert(fc_instance != nullptr); |
| 150 | |
| 151 | /* Fontconfig doesn't handle full language isocodes, only the part |
| 152 | * before the _ of e.g. en_GB is used, so "remove" everything after |
| 153 | * the _. */ |
| 154 | std::string lang = fmt::format(":lang={}", language_isocode.substr(0, language_isocode.find('_'))); |
| 155 | |
| 156 | /* First create a pattern to match the wanted language. */ |
| 157 | auto pat = AutoRelease<FcPattern, FcPatternDestroy>(FcNameParse(ToFcString(lang))); |
| 158 | /* We only want to know these attributes. */ |
| 159 | auto os = AutoRelease<FcObjectSet, FcObjectSetDestroy>(FcObjectSetBuild(FC_FILE, FC_INDEX, FC_SPACING, FC_SLANT, FC_WEIGHT, nullptr)); |
| 160 | /* Get the list of filenames matching the wanted language. */ |
| 161 | auto fs = AutoRelease<FcFontSet, FcFontSetDestroy>(FcFontList(nullptr, pat.get(), os.get())); |
| 162 | |
| 163 | if (fs == nullptr) return ret; |
| 164 | |
| 165 | int best_weight = -1; |
| 166 | const char *best_font = nullptr; |
| 167 | int best_index = 0; |
| 168 | |
| 169 | for (FcPattern *font : std::span(fs->fonts, fs->nfont)) { |
| 170 | FcChar8 *file = nullptr; |
| 171 | FcResult res = FcPatternGetString(font, FC_FILE, 0, &file); |
| 172 | if (res != FcResultMatch || file == nullptr) continue; |
| 173 | |
| 174 | /* Get a font with the right spacing .*/ |
| 175 | int value = 0; |
| 176 | FcPatternGetInteger(font, FC_SPACING, 0, &value); |
| 177 | if (callback->Monospace() != (value == FC_MONO) && value != FC_DUAL) continue; |
| 178 | |
| 179 | /* Do not use those that explicitly say they're slanted. */ |
| 180 | FcPatternGetInteger(font, FC_SLANT, 0, &value); |
| 181 | if (value != 0) continue; |
| 182 | |
| 183 | /* We want a font near to medium weight. */ |
| 184 | FcPatternGetInteger(font, FC_WEIGHT, 0, &value); |
| 185 | int weight = GetPreferredWeightDistance(value); |
| 186 | if (best_weight != -1 && weight > best_weight) continue; |
| 187 | |
| 188 | /* Possible match based on attributes, get index. */ |
| 189 | int32_t index; |
| 190 | res = FcPatternGetInteger(font, FC_INDEX, 0, &index); |
| 191 | if (res != FcResultMatch) continue; |
| 192 | |
| 193 | callback->SetFontNames(settings, FromFcString(file), &index); |
| 194 | |
| 195 | bool missing = callback->FindMissingGlyphs(); |
| 196 | Debug(fontcache, 1, "Font \"{}\" misses{} glyphs", FromFcString(file), missing ? "" : " no"); |
| 197 | |
| 198 | if (!missing) { |
| 199 | best_weight = weight; |
no test coverage detected