Make it possible to stop road vehicles from slowing down in curves so "diagonal"...
[openttd-joker.git] / src / fontdetection.cpp
blobf22f0eadeb0608e2d61f48a3715a70e3b2efdefa
1 /* $Id: fontdetection.cpp 25676 2013-08-05 20:36:55Z michi_cc $ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /** @file fontdetection.cpp Detection of the right font. */
12 #ifdef WITH_FREETYPE
14 #include "stdafx.h"
15 #include "debug.h"
16 #include "fontdetection.h"
17 #include "string_func.h"
18 #include "strings_func.h"
20 extern FT_Library _library;
22 /**
23 * Get the font loaded into a Freetype face by using a font-name.
24 * If no appropriate font is found, the function returns an error
27 /* ========================================================================================
28 * Windows support
29 * ======================================================================================== */
31 #ifdef WIN32
32 #include "core/alloc_func.hpp"
33 #include "core/math_func.hpp"
34 #include <windows.h>
35 #include <shlobj.h> /* SHGetFolderPath */
36 #include "os/windows/win32.h"
38 #include "safeguards.h"
40 /**
41 * Get the short DOS 8.3 format for paths.
42 * FreeType doesn't support Unicode filenames and Windows' fopen (as used
43 * by FreeType) doesn't support UTF-8 filenames. So we have to convert the
44 * filename into something that isn't UTF-8 but represents the Unicode file
45 * name. This is the short DOS 8.3 format. This does not contain any
46 * characters that fopen doesn't support.
47 * @param long_path the path in system encoding.
48 * @return the short path in ANSI (ASCII).
50 const char *GetShortPath(const TCHAR *long_path)
52 static char short_path[MAX_PATH];
53 #ifdef UNICODE
54 WCHAR short_path_w[MAX_PATH];
55 GetShortPathName(long_path, short_path_w, lengthof(short_path_w));
56 WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path, lengthof(short_path), nullptr, nullptr);
57 #else
58 /* Technically not needed, but do it for consistency. */
59 GetShortPathName(long_path, short_path, lengthof(short_path));
60 #endif
61 return short_path;
64 /* Get the font file to be loaded into Freetype by looping the registry
65 * location where windows lists all installed fonts. Not very nice, will
66 * surely break if the registry path changes, but it works. Much better
67 * solution would be to use CreateFont, and extract the font data from it
68 * by GetFontData. The problem with this is that the font file needs to be
69 * kept in memory then until the font is no longer needed. This could mean
70 * an additional memory usage of 30MB (just for fonts!) when using an eastern
71 * font for all font sizes */
72 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
73 #define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts"
74 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
76 FT_Error err = FT_Err_Cannot_Open_Resource;
77 HKEY hKey;
78 LONG ret;
79 TCHAR vbuffer[MAX_PATH], dbuffer[256];
80 TCHAR *pathbuf;
81 const char *font_path;
82 uint index;
83 size_t path_len;
85 /* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
86 * "Windows NT" key, on Windows 9x in the Windows key. To save us having
87 * to retrieve the windows version, we'll just query both */
88 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
89 if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
91 if (ret != ERROR_SUCCESS) {
92 DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
93 return err;
96 /* Convert font name to file system encoding. */
97 TCHAR *font_namep = _tcsdup(OTTD2FS(font_name));
99 for (index = 0;; index++) {
100 TCHAR *s;
101 DWORD vbuflen = lengthof(vbuffer);
102 DWORD dbuflen = lengthof(dbuffer);
104 ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, nullptr, nullptr, (byte*)dbuffer, &dbuflen);
105 if (ret != ERROR_SUCCESS) goto registry_no_font_found;
107 /* The font names in the registry are of the following 3 forms:
108 * - ADMUI3.fon
109 * - Book Antiqua Bold (TrueType)
110 * - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
111 * We will strip the font-type '()' if any and work with the font name
112 * itself, which must match exactly; if...
113 * TTC files, font files which contain more than one font are separated
114 * by '&'. Our best bet will be to do substr match for the fontname
115 * and then let FreeType figure out which index to load */
116 s = _tcschr(vbuffer, _T('('));
117 if (s != nullptr) s[-1] = '\0';
119 if (_tcschr(vbuffer, _T('&')) == nullptr) {
120 if (_tcsicmp(vbuffer, font_namep) == 0) break;
121 } else {
122 if (_tcsstr(vbuffer, font_namep) != nullptr) break;
126 if (!SUCCEEDED(OTTDSHGetFolderPath(nullptr, CSIDL_FONTS, nullptr, SHGFP_TYPE_CURRENT, vbuffer))) {
127 DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory");
128 goto folder_error;
131 /* Some fonts are contained in .ttc files, TrueType Collection fonts. These
132 * contain multiple fonts inside this single file. GetFontData however
133 * returns the whole file, so we need to check each font inside to get the
134 * proper font. */
135 path_len = _tcslen(vbuffer) + _tcslen(dbuffer) + 2; // '\' and terminating nul.
136 pathbuf = AllocaM(TCHAR, path_len);
137 _sntprintf(pathbuf, path_len, _T("%s\\%s"), vbuffer, dbuffer);
139 /* Convert the path into something that FreeType understands. */
140 font_path = GetShortPath(pathbuf);
142 index = 0;
143 do {
144 err = FT_New_Face(_library, font_path, index, face);
145 if (err != FT_Err_Ok) break;
147 if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
148 /* Try english name if font name failed */
149 if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
150 err = FT_Err_Cannot_Open_Resource;
152 } while ((FT_Long)++index != (*face)->num_faces);
155 folder_error:
156 registry_no_font_found:
157 free(font_namep);
158 RegCloseKey(hKey);
159 return err;
163 * Fonts can have localised names and when the system locale is the same as
164 * one of those localised names Windows will always return that localised name
165 * instead of allowing to get the non-localised (English US) name of the font.
166 * This will later on give problems as freetype uses the non-localised name of
167 * the font and we need to compare based on that name.
168 * Windows furthermore DOES NOT have an API to get the non-localised name nor
169 * can we override the system locale. This means that we have to actually read
170 * the font itself to gather the font name we want.
171 * Based on: http://blogs.msdn.com/michkap/archive/2006/02/13/530814.aspx
172 * @param logfont the font information to get the english name of.
173 * @return the English name (if it could be found).
175 static const char *GetEnglishFontName(const ENUMLOGFONTEX *logfont)
177 static char font_name[MAX_PATH];
178 const char *ret_font_name = nullptr;
179 uint pos = 0;
180 HDC dc;
181 HGDIOBJ oldfont;
182 byte *buf;
183 DWORD dw;
184 uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset;
186 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
187 if (font == nullptr) goto err1;
189 dc = GetDC(nullptr);
190 oldfont = SelectObject(dc, font);
191 dw = GetFontData(dc, 'eman', 0, nullptr, 0);
192 if (dw == GDI_ERROR) goto err2;
194 buf = MallocT<byte>(dw);
195 dw = GetFontData(dc, 'eman', 0, buf, dw);
196 if (dw == GDI_ERROR) goto err3;
198 format = buf[pos++] << 8;
199 format += buf[pos++];
200 assert(format == 0);
201 count = buf[pos++] << 8;
202 count += buf[pos++];
203 stringOffset = buf[pos++] << 8;
204 stringOffset += buf[pos++];
205 for (uint i = 0; i < count; i++) {
206 platformId = buf[pos++] << 8;
207 platformId += buf[pos++];
208 encodingId = buf[pos++] << 8;
209 encodingId += buf[pos++];
210 languageId = buf[pos++] << 8;
211 languageId += buf[pos++];
212 nameId = buf[pos++] << 8;
213 nameId += buf[pos++];
214 if (nameId != 1) {
215 pos += 4; // skip length and offset
216 continue;
218 length = buf[pos++] << 8;
219 length += buf[pos++];
220 offset = buf[pos++] << 8;
221 offset += buf[pos++];
223 /* Don't buffer overflow */
224 length = min(length, MAX_PATH - 1);
225 for (uint j = 0; j < length; j++) font_name[j] = buf[stringOffset + offset + j];
226 font_name[length] = '\0';
228 if ((platformId == 1 && languageId == 0) || // Macintosh English
229 (platformId == 3 && languageId == 0x0409)) { // Microsoft English (US)
230 ret_font_name = font_name;
231 break;
235 err3:
236 free(buf);
237 err2:
238 SelectObject(dc, oldfont);
239 ReleaseDC(nullptr, dc);
240 DeleteObject(font);
241 err1:
242 return ret_font_name == nullptr ? WIDE_TO_MB((const TCHAR*)logfont->elfFullName) : ret_font_name;
245 class FontList {
246 protected:
247 TCHAR **fonts;
248 uint items;
249 uint capacity;
251 public:
252 FontList() : fonts(nullptr), items(0), capacity(0) { };
254 ~FontList() {
255 if (this->fonts == nullptr) return;
257 for (uint i = 0; i < this->items; i++) {
258 free(this->fonts[i]);
261 free(this->fonts);
264 bool Add(const TCHAR *font) {
265 for (uint i = 0; i < this->items; i++) {
266 if (_tcscmp(this->fonts[i], font) == 0) return false;
269 if (this->items == this->capacity) {
270 this->capacity += 10;
271 this->fonts = ReallocT(this->fonts, this->capacity);
274 this->fonts[this->items++] = _tcsdup(font);
276 return true;
280 struct EFCParam {
281 FreeTypeSettings *settings;
282 LOCALESIGNATURE locale;
283 MissingGlyphSearcher *callback;
284 FontList fonts;
287 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
289 EFCParam *info = (EFCParam *)lParam;
291 /* Skip duplicates */
292 if (!info->fonts.Add((const TCHAR*)logfont->elfFullName)) return 1;
293 /* Only use TrueType fonts */
294 if (!(type & TRUETYPE_FONTTYPE)) return 1;
295 /* Don't use SYMBOL fonts */
296 if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET) return 1;
297 /* Use monospaced fonts when asked for it. */
298 if (info->callback->Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH)) return 1;
300 /* The font has to have at least one of the supported locales to be usable. */
301 if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) {
302 /* On win9x metric->ntmFontSig seems to contain garbage. */
303 FONTSIGNATURE fs;
304 memset(&fs, 0, sizeof(fs));
305 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
306 if (font != nullptr) {
307 HDC dc = GetDC(nullptr);
308 HGDIOBJ oldfont = SelectObject(dc, font);
309 GetTextCharsetInfo(dc, &fs, 0);
310 SelectObject(dc, oldfont);
311 ReleaseDC(nullptr, dc);
312 DeleteObject(font);
314 if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) return 1;
317 char font_name[MAX_PATH];
318 convert_from_fs((const TCHAR *)logfont->elfFullName, font_name, lengthof(font_name));
320 /* Add english name after font name */
321 const char *english_name = GetEnglishFontName(logfont);
322 strecpy(font_name + strlen(font_name) + 1, english_name, lastof(font_name));
324 /* Check whether we can actually load the font. */
325 bool ft_init = _library != nullptr;
326 bool found = false;
327 FT_Face face;
328 /* Init FreeType if needed. */
329 if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) && GetFontByFaceName(font_name, &face) == FT_Err_Ok) {
330 FT_Done_Face(face);
331 found = true;
333 if (!ft_init) {
334 /* Uninit FreeType if we did the init. */
335 FT_Done_FreeType(_library);
336 _library = nullptr;
339 if (!found) return 1;
341 info->callback->SetFontNames(info->settings, font_name);
342 if (info->callback->FindMissingGlyphs(nullptr)) return 1;
343 DEBUG(freetype, 1, "Fallback font: %s (%s)", font_name, english_name);
344 return 0; // stop enumerating
347 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
349 DEBUG(freetype, 1, "Trying fallback fonts");
350 EFCParam langInfo;
351 if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPTSTR)&langInfo.locale, sizeof(langInfo.locale) / sizeof(TCHAR)) == 0) {
352 /* Invalid langid or some other mysterious error, can't determine fallback font. */
353 DEBUG(freetype, 1, "Can't get locale info for fallback font (langid=0x%x)", winlangid);
354 return false;
356 langInfo.settings = settings;
357 langInfo.callback = callback;
359 LOGFONT font;
360 /* Enumerate all fonts. */
361 font.lfCharSet = DEFAULT_CHARSET;
362 font.lfFaceName[0] = '\0';
363 font.lfPitchAndFamily = 0;
365 HDC dc = GetDC(nullptr);
366 int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
367 ReleaseDC(nullptr, dc);
368 return ret == 0;
371 #elif defined(__APPLE__) /* end ifdef Win32 */
372 /* ========================================================================================
373 * OSX support
374 * ======================================================================================== */
376 #include "os/macosx/macos.h"
378 #include "safeguards.h"
380 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
382 FT_Error err = FT_Err_Cannot_Open_Resource;
384 /* Get font reference from name. */
385 CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8);
386 ATSFontRef font = ATSFontFindFromName(name, kATSOptionFlagsDefault);
387 CFRelease(name);
388 if (font == kInvalidFont) return err;
390 /* Get a file system reference for the font. */
391 FSRef ref;
392 OSStatus os_err = -1;
393 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
394 if (MacOSVersionIsAtLeast(10, 5, 0)) {
395 os_err = ATSFontGetFileReference(font, &ref);
396 } else
397 #endif
399 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !defined(__LP64__)
400 /* This type was introduced with the 10.5 SDK. */
401 #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
402 #define ATSFSSpec FSSpec
403 #endif
404 FSSpec spec;
405 os_err = ATSFontGetFileSpecification(font, (ATSFSSpec *)&spec);
406 if (os_err == noErr) os_err = FSpMakeFSRef(&spec, &ref);
407 #endif
410 if (os_err == noErr) {
411 /* Get unix path for file. */
412 UInt8 file_path[PATH_MAX];
413 if (FSRefMakePath(&ref, file_path, sizeof(file_path)) == noErr) {
414 DEBUG(freetype, 3, "Font path for %s: %s", font_name, file_path);
415 err = FT_New_Face(_library, (const char *)file_path, 0, face);
419 return err;
422 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
424 bool result = false;
426 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
427 if (MacOSVersionIsAtLeast(10, 5, 0)) {
428 /* Determine fallback font using CoreText. This uses the language isocode
429 * to find a suitable font. CoreText is available from 10.5 onwards. */
430 char lang[16];
431 if (strcmp(language_isocode, "zh_TW") == 0) {
432 /* Traditional Chinese */
433 strecpy(lang, "zh-Hant", lastof(lang));
434 } else if (strcmp(language_isocode, "zh_CN") == 0) {
435 /* Simplified Chinese */
436 strecpy(lang, "zh-Hans", lastof(lang));
437 } else {
438 /* Just copy the first part of the isocode. */
439 strecpy(lang, language_isocode, lastof(lang));
440 char *sep = strchr(lang, '_');
441 if (sep != nullptr) *sep = '\0';
444 /* Create a font descriptor matching the wanted language and latin (english) glyphs. */
445 CFStringRef lang_codes[2];
446 lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8);
447 lang_codes[1] = CFSTR("en");
448 CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks);
449 CFDictionaryRef lang_attribs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&kCTFontLanguagesAttribute, (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
450 CTFontDescriptorRef lang_desc = CTFontDescriptorCreateWithAttributes(lang_attribs);
451 CFRelease(lang_arr);
452 CFRelease(lang_attribs);
453 CFRelease(lang_codes[0]);
455 /* Get array of all font descriptors for the wanted language. */
456 CFSetRef mandatory_attribs = CFSetCreate(kCFAllocatorDefault, (const void **)&kCTFontLanguagesAttribute, 1, &kCFTypeSetCallBacks);
457 CFArrayRef descs = CTFontDescriptorCreateMatchingFontDescriptors(lang_desc, mandatory_attribs);
458 CFRelease(mandatory_attribs);
459 CFRelease(lang_desc);
461 for (CFIndex i = 0; descs != nullptr && i < CFArrayGetCount(descs); i++) {
462 CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs, i);
464 /* Get font traits. */
465 CFDictionaryRef traits = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute);
466 CTFontSymbolicTraits symbolic_traits;
467 CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits, kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
468 CFRelease(traits);
470 /* Skip symbol fonts and vertical fonts. */
471 if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue;
472 /* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
473 if (symbolic_traits & kCTFontBoldTrait) continue;
474 /* Select monospaced fonts if asked for. */
475 if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
477 /* Get font name. */
478 char name[128];
479 CFStringRef font_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute);
480 CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
481 CFRelease(font_name);
483 /* There are some special fonts starting with an '.' and the last
484 * resort font that aren't usable. Skip them. */
485 if (name[0] == '.' || strncmp(name, "LastResort", 10) == 0) continue;
487 /* Save result. */
488 callback->SetFontNames(settings, name);
489 if (!callback->FindMissingGlyphs(nullptr)) {
490 DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name);
491 result = true;
492 break;
495 if (descs != nullptr) CFRelease(descs);
496 } else
497 #endif
499 /* Create a font iterator and iterate over all fonts that
500 * are available to the application. */
501 ATSFontIterator itr;
502 ATSFontRef font;
503 ATSFontIteratorCreate(kATSFontContextLocal, nullptr, nullptr, kATSOptionFlagsDefaultScope, &itr);
504 while (!result && ATSFontIteratorNext(itr, &font) == noErr) {
505 /* Get font name. */
506 char name[128];
507 CFStringRef font_name;
508 ATSFontGetName(font, kATSOptionFlagsDefault, &font_name);
509 CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
511 bool monospace = IsMonospaceFont(font_name);
512 CFRelease(font_name);
514 /* Select monospaced fonts if asked for. */
515 if (monospace != callback->Monospace()) continue;
517 /* We only want the base font and not bold or italic variants. */
518 if (strstr(name, "Italic") != nullptr || strstr(name, "Bold")) continue;
520 /* Skip some inappropriate or ugly looking fonts that have better alternatives. */
521 if (name[0] == '.' || strncmp(name, "Apple Symbols", 13) == 0 || strncmp(name, "LastResort", 10) == 0) continue;
523 /* Save result. */
524 callback->SetFontNames(settings, name);
525 if (!callback->FindMissingGlyphs(nullptr)) {
526 DEBUG(freetype, 2, "ATS-Font for %s: %s", language_isocode, name);
527 result = true;
528 break;
531 ATSFontIteratorRelease(&itr);
534 if (!result) {
535 /* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
536 * supports. If we didn't find any other font, just try it, maybe we get lucky. */
537 callback->SetFontNames(settings, "Arial Unicode MS");
538 result = !callback->FindMissingGlyphs(nullptr);
541 callback->FindMissingGlyphs(nullptr);
542 return result;
545 #elif defined(WITH_FONTCONFIG) /* end ifdef __APPLE__ */
547 #include <fontconfig/fontconfig.h>
549 #include "safeguards.h"
551 /* ========================================================================================
552 * FontConfig (unix) support
553 * ======================================================================================== */
554 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
556 FT_Error err = FT_Err_Cannot_Open_Resource;
558 if (!FcInit()) {
559 ShowInfoF("Unable to load font configuration");
560 } else {
561 FcPattern *match;
562 FcPattern *pat;
563 FcFontSet *fs;
564 FcResult result;
565 char *font_style;
566 char *font_family;
568 /* Split & strip the font's style */
569 font_family = stredup(font_name);
570 font_style = strchr(font_family, ',');
571 if (font_style != nullptr) {
572 font_style[0] = '\0';
573 font_style++;
574 while (*font_style == ' ' || *font_style == '\t') font_style++;
577 /* Resolve the name and populate the information structure */
578 pat = FcNameParse((FcChar8*)font_family);
579 if (font_style != nullptr) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
580 FcConfigSubstitute(0, pat, FcMatchPattern);
581 FcDefaultSubstitute(pat);
582 fs = FcFontSetCreate();
583 match = FcFontMatch(0, pat, &result);
585 if (fs != nullptr && match != nullptr) {
586 int i;
587 FcChar8 *family;
588 FcChar8 *style;
589 FcChar8 *file;
590 FcFontSetAdd(fs, match);
592 for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
593 /* Try the new filename */
594 if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch &&
595 FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
596 FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) {
598 /* The correct style? */
599 if (font_style != nullptr && strcasecmp(font_style, (char*)style) != 0) continue;
601 /* Font config takes the best shot, which, if the family name is spelled
602 * wrongly a 'random' font, so check whether the family name is the
603 * same as the supplied name */
604 if (strcasecmp(font_family, (char*)family) == 0) {
605 err = FT_New_Face(_library, (char *)file, 0, face);
611 free(font_family);
612 FcPatternDestroy(pat);
613 FcFontSetDestroy(fs);
614 FcFini();
617 return err;
620 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
622 if (!FcInit()) return false;
624 bool ret = false;
626 /* Fontconfig doesn't handle full language isocodes, only the part
627 * before the _ of e.g. en_GB is used, so "remove" everything after
628 * the _. */
629 char lang[16];
630 seprintf(lang, lastof(lang), ":lang=%s", language_isocode);
631 char *split = strchr(lang, '_');
632 if (split != nullptr) *split = '\0';
634 /* First create a pattern to match the wanted language. */
635 FcPattern *pat = FcNameParse((FcChar8*)lang);
636 /* We only want to know the filename. */
637 FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT, nullptr);
638 /* Get the list of filenames matching the wanted language. */
639 FcFontSet *fs = FcFontList(nullptr, pat, os);
641 /* We don't need these anymore. */
642 FcObjectSetDestroy(os);
643 FcPatternDestroy(pat);
645 if (fs != nullptr) {
646 int best_weight = -1;
647 const char *best_font = nullptr;
649 for (int i = 0; i < fs->nfont; i++) {
650 FcPattern *font = fs->fonts[i];
652 FcChar8 *file = nullptr;
653 FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
654 if (res != FcResultMatch || file == nullptr) {
655 continue;
658 /* Get a font with the right spacing .*/
659 int value = 0;
660 FcPatternGetInteger(font, FC_SPACING, 0, &value);
661 if (callback->Monospace() != (value == FC_MONO) && value != FC_DUAL) continue;
663 /* Do not use those that explicitly say they're slanted. */
664 FcPatternGetInteger(font, FC_SLANT, 0, &value);
665 if (value != 0) continue;
667 /* We want the fatter font as they look better at small sizes. */
668 FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
669 if (value <= best_weight) continue;
671 callback->SetFontNames(settings, (const char*)file);
673 bool missing = callback->FindMissingGlyphs(nullptr);
674 DEBUG(freetype, 1, "Font \"%s\" misses%s glyphs", file, missing ? "" : " no");
676 if (!missing) {
677 best_weight = value;
678 best_font = (const char *)file;
682 if (best_font != nullptr) {
683 ret = true;
684 callback->SetFontNames(settings, best_font);
685 InitFreeType(callback->Monospace());
688 /* Clean up the list of filenames. */
689 FcFontSetDestroy(fs);
692 FcFini();
693 return ret;
696 #else /* without WITH_FONTCONFIG */
697 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;}
698 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) { return false; }
699 #endif /* WITH_FONTCONFIG */
701 #endif /* WITH_FREETYPE */