Extend object variable 0x60 to also return the view
[openttd/fttd.git] / src / fontdetection.cpp
blob82c4f354f627e6e76a86286de2e539cbf1e606da
1 /* $Id$ */
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 /**
39 * Get the short DOS 8.3 format for paths.
40 * FreeType doesn't support Unicode filenames and Windows' fopen (as used
41 * by FreeType) doesn't support UTF-8 filenames. So we have to convert the
42 * filename into something that isn't UTF-8 but represents the Unicode file
43 * name. This is the short DOS 8.3 format. This does not contain any
44 * characters that fopen doesn't support.
45 * @param long_path the path in system encoding.
46 * @return the short path in ANSI (ASCII).
48 const char *GetShortPath(const TCHAR *long_path)
50 static char short_path[MAX_PATH];
51 #ifdef UNICODE
52 WCHAR short_path_w[MAX_PATH];
53 GetShortPathName(long_path, short_path_w, lengthof(short_path_w));
54 WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path, lengthof(short_path), NULL, NULL);
55 #else
56 /* Technically not needed, but do it for consistency. */
57 GetShortPathName(long_path, short_path, lengthof(short_path));
58 #endif
59 return short_path;
62 /* Get the font file to be loaded into Freetype by looping the registry
63 * location where windows lists all installed fonts. Not very nice, will
64 * surely break if the registry path changes, but it works. Much better
65 * solution would be to use CreateFont, and extract the font data from it
66 * by GetFontData. The problem with this is that the font file needs to be
67 * kept in memory then until the font is no longer needed. This could mean
68 * an additional memory usage of 30MB (just for fonts!) when using an eastern
69 * font for all font sizes */
70 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
71 #define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts"
72 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
74 FT_Error err = FT_Err_Cannot_Open_Resource;
75 HKEY hKey;
76 LONG ret;
77 TCHAR vbuffer[MAX_PATH], dbuffer[256];
78 TCHAR *pathbuf;
79 const char *font_path;
80 uint index;
81 size_t path_len;
83 /* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
84 * "Windows NT" key, on Windows 9x in the Windows key. To save us having
85 * to retrieve the windows version, we'll just query both */
86 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
87 if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
89 if (ret != ERROR_SUCCESS) {
90 DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
91 return err;
94 /* Convert font name to file system encoding. */
95 TCHAR *font_namep = _tcsdup(OTTD2FS(font_name));
97 for (index = 0;; index++) {
98 TCHAR *s;
99 DWORD vbuflen = lengthof(vbuffer);
100 DWORD dbuflen = lengthof(dbuffer);
102 ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, NULL, NULL, (byte*)dbuffer, &dbuflen);
103 if (ret != ERROR_SUCCESS) goto registry_no_font_found;
105 /* The font names in the registry are of the following 3 forms:
106 * - ADMUI3.fon
107 * - Book Antiqua Bold (TrueType)
108 * - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
109 * We will strip the font-type '()' if any and work with the font name
110 * itself, which must match exactly; if...
111 * TTC files, font files which contain more than one font are separated
112 * by '&'. Our best bet will be to do substr match for the fontname
113 * and then let FreeType figure out which index to load */
114 s = _tcschr(vbuffer, _T('('));
115 if (s != NULL) s[-1] = '\0';
117 if (_tcschr(vbuffer, _T('&')) == NULL) {
118 if (_tcsicmp(vbuffer, font_namep) == 0) break;
119 } else {
120 if (_tcsstr(vbuffer, font_namep) != NULL) break;
124 if (!SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, vbuffer))) {
125 DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory");
126 goto folder_error;
129 /* Some fonts are contained in .ttc files, TrueType Collection fonts. These
130 * contain multiple fonts inside this single file. GetFontData however
131 * returns the whole file, so we need to check each font inside to get the
132 * proper font. */
133 path_len = _tcslen(vbuffer) + _tcslen(dbuffer) + 2; // '\' and terminating nul.
134 pathbuf = AllocaM(TCHAR, path_len);
135 _sntprintf(pathbuf, path_len, _T("%s\\%s"), vbuffer, dbuffer);
137 /* Convert the path into something that FreeType understands. */
138 font_path = GetShortPath(pathbuf);
140 index = 0;
141 do {
142 err = FT_New_Face(_library, font_path, index, face);
143 if (err != FT_Err_Ok) break;
145 if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
146 /* Try english name if font name failed */
147 if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
148 err = FT_Err_Cannot_Open_Resource;
150 } while ((FT_Long)++index != (*face)->num_faces);
153 folder_error:
154 registry_no_font_found:
155 free(font_namep);
156 RegCloseKey(hKey);
157 return err;
161 * Fonts can have localised names and when the system locale is the same as
162 * one of those localised names Windows will always return that localised name
163 * instead of allowing to get the non-localised (English US) name of the font.
164 * This will later on give problems as freetype uses the non-localised name of
165 * the font and we need to compare based on that name.
166 * Windows furthermore DOES NOT have an API to get the non-localised name nor
167 * can we override the system locale. This means that we have to actually read
168 * the font itself to gather the font name we want.
169 * Based on: http://blogs.msdn.com/michkap/archive/2006/02/13/530814.aspx
170 * @param logfont the font information to get the english name of.
171 * @return the English name (if it could be found).
173 static const char *GetEnglishFontName(const ENUMLOGFONTEX *logfont)
175 static char font_name[MAX_PATH];
176 const char *ret_font_name = NULL;
177 uint pos = 0;
178 HDC dc;
179 HGDIOBJ oldfont;
180 byte *buf;
181 DWORD dw;
182 uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset;
184 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
185 if (font == NULL) goto err1;
187 dc = GetDC(NULL);
188 oldfont = SelectObject(dc, font);
189 dw = GetFontData(dc, 'eman', 0, NULL, 0);
190 if (dw == GDI_ERROR) goto err2;
192 buf = MallocT<byte>(dw);
193 dw = GetFontData(dc, 'eman', 0, buf, dw);
194 if (dw == GDI_ERROR) goto err3;
196 format = buf[pos++] << 8;
197 format += buf[pos++];
198 assert(format == 0);
199 count = buf[pos++] << 8;
200 count += buf[pos++];
201 stringOffset = buf[pos++] << 8;
202 stringOffset += buf[pos++];
203 for (uint i = 0; i < count; i++) {
204 platformId = buf[pos++] << 8;
205 platformId += buf[pos++];
206 encodingId = buf[pos++] << 8;
207 encodingId += buf[pos++];
208 languageId = buf[pos++] << 8;
209 languageId += buf[pos++];
210 nameId = buf[pos++] << 8;
211 nameId += buf[pos++];
212 if (nameId != 1) {
213 pos += 4; // skip length and offset
214 continue;
216 length = buf[pos++] << 8;
217 length += buf[pos++];
218 offset = buf[pos++] << 8;
219 offset += buf[pos++];
221 /* Don't buffer overflow */
222 length = min(length, MAX_PATH - 1);
223 for (uint j = 0; j < length; j++) font_name[j] = buf[stringOffset + offset + j];
224 font_name[length] = '\0';
226 if ((platformId == 1 && languageId == 0) || // Macintosh English
227 (platformId == 3 && languageId == 0x0409)) { // Microsoft English (US)
228 ret_font_name = font_name;
229 break;
233 err3:
234 free(buf);
235 err2:
236 SelectObject(dc, oldfont);
237 ReleaseDC(NULL, dc);
238 DeleteObject(font);
239 err1:
240 return ret_font_name == NULL ? WIDE_TO_MB((const TCHAR*)logfont->elfFullName) : ret_font_name;
243 class FontList {
244 protected:
245 TCHAR **fonts;
246 uint items;
247 uint capacity;
249 public:
250 FontList() : fonts(NULL), items(0), capacity(0) { };
252 ~FontList() {
253 if (this->fonts == NULL) return;
255 for (uint i = 0; i < this->items; i++) {
256 free(this->fonts[i]);
259 free(this->fonts);
262 bool Add(const TCHAR *font) {
263 for (uint i = 0; i < this->items; i++) {
264 if (_tcscmp(this->fonts[i], font) == 0) return false;
267 if (this->items == this->capacity) {
268 this->capacity += 10;
269 this->fonts = ReallocT(this->fonts, this->capacity);
272 this->fonts[this->items++] = _tcsdup(font);
274 return true;
278 struct EFCParam {
279 FreeTypeSettings *settings;
280 LOCALESIGNATURE locale;
281 MissingGlyphSearcher *callback;
282 FontList fonts;
285 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
287 EFCParam *info = (EFCParam *)lParam;
289 /* Skip duplicates */
290 if (!info->fonts.Add((const TCHAR*)logfont->elfFullName)) return 1;
291 /* Only use TrueType fonts */
292 if (!(type & TRUETYPE_FONTTYPE)) return 1;
293 /* Don't use SYMBOL fonts */
294 if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET) return 1;
295 /* Use monospaced fonts when asked for it. */
296 if (info->callback->Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH)) return 1;
298 /* The font has to have at least one of the supported locales to be usable. */
299 if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) {
300 /* On win9x metric->ntmFontSig seems to contain garbage. */
301 FONTSIGNATURE fs;
302 memset(&fs, 0, sizeof(fs));
303 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
304 if (font != NULL) {
305 HDC dc = GetDC(NULL);
306 HGDIOBJ oldfont = SelectObject(dc, font);
307 GetTextCharsetInfo(dc, &fs, 0);
308 SelectObject(dc, oldfont);
309 ReleaseDC(NULL, dc);
310 DeleteObject(font);
312 if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) return 1;
315 char font_name[MAX_PATH];
316 convert_from_fs((const TCHAR *)logfont->elfFullName, font_name, lengthof(font_name));
318 /* Add english name after font name */
319 const char *english_name = GetEnglishFontName(logfont);
320 strecpy(font_name + strlen(font_name) + 1, english_name, lastof(font_name));
322 /* Check whether we can actually load the font. */
323 bool ft_init = _library != NULL;
324 bool found = false;
325 FT_Face face;
326 /* Init FreeType if needed. */
327 if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) && GetFontByFaceName(font_name, &face) == FT_Err_Ok) {
328 FT_Done_Face(face);
329 found = true;
331 if (!ft_init) {
332 /* Uninit FreeType if we did the init. */
333 FT_Done_FreeType(_library);
334 _library = NULL;
337 if (!found) return 1;
339 info->callback->SetFontNames(info->settings, font_name);
340 if (info->callback->FindMissingGlyphs(NULL)) return 1;
341 DEBUG(freetype, 1, "Fallback font: %s (%s)", font_name, english_name);
342 return 0; // stop enumerating
345 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
347 DEBUG(freetype, 1, "Trying fallback fonts");
348 EFCParam langInfo;
349 if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPTSTR)&langInfo.locale, sizeof(langInfo.locale) / sizeof(TCHAR)) == 0) {
350 /* Invalid langid or some other mysterious error, can't determine fallback font. */
351 DEBUG(freetype, 1, "Can't get locale info for fallback font (langid=0x%x)", winlangid);
352 return false;
354 langInfo.settings = settings;
355 langInfo.callback = callback;
357 LOGFONT font;
358 /* Enumerate all fonts. */
359 font.lfCharSet = DEFAULT_CHARSET;
360 font.lfFaceName[0] = '\0';
361 font.lfPitchAndFamily = 0;
363 HDC dc = GetDC(NULL);
364 int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
365 ReleaseDC(NULL, dc);
366 return ret == 0;
369 #elif defined(__APPLE__) /* end ifdef Win32 */
370 /* ========================================================================================
371 * OSX support
372 * ======================================================================================== */
374 #include "os/macosx/macos.h"
376 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
378 FT_Error err = FT_Err_Cannot_Open_Resource;
380 /* Get font reference from name. */
381 CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8);
382 ATSFontRef font = ATSFontFindFromName(name, kATSOptionFlagsDefault);
383 CFRelease(name);
384 if (font == kInvalidFont) return err;
386 /* Get a file system reference for the font. */
387 FSRef ref;
388 OSStatus os_err = -1;
389 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
390 if (MacOSVersionIsAtLeast(10, 5, 0)) {
391 os_err = ATSFontGetFileReference(font, &ref);
392 } else
393 #endif
395 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !__LP64__
396 /* This type was introduced with the 10.5 SDK. */
397 #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
398 #define ATSFSSpec FSSpec
399 #endif
400 FSSpec spec;
401 os_err = ATSFontGetFileSpecification(font, (ATSFSSpec *)&spec);
402 if (os_err == noErr) os_err = FSpMakeFSRef(&spec, &ref);
403 #endif
406 if (os_err == noErr) {
407 /* Get unix path for file. */
408 UInt8 file_path[PATH_MAX];
409 if (FSRefMakePath(&ref, file_path, sizeof(file_path)) == noErr) {
410 DEBUG(freetype, 3, "Font path for %s: %s", font_name, file_path);
411 err = FT_New_Face(_library, (const char *)file_path, 0, face);
415 return err;
418 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
420 bool result = false;
422 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
423 if (MacOSVersionIsAtLeast(10, 5, 0)) {
424 /* Determine fallback font using CoreText. This uses the language isocode
425 * to find a suitable font. CoreText is available from 10.5 onwards. */
426 char lang[16];
427 if (strcmp(language_isocode, "zh_TW") == 0) {
428 /* Traditional Chinese */
429 strecpy(lang, "zh-Hant", lastof(lang));
430 } else if (strcmp(language_isocode, "zh_CN") == 0) {
431 /* Simplified Chinese */
432 strecpy(lang, "zh-Hans", lastof(lang));
433 } else {
434 /* Just copy the first part of the isocode. */
435 strecpy(lang, language_isocode, lastof(lang));
436 char *sep = strchr(lang, '_');
437 if (sep != NULL) *sep = '\0';
440 /* Create a font descriptor matching the wanted language and latin (english) glyphs. */
441 CFStringRef lang_codes[2];
442 lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8);
443 lang_codes[1] = CFSTR("en");
444 CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks);
445 CFDictionaryRef lang_attribs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&kCTFontLanguagesAttribute, (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
446 CTFontDescriptorRef lang_desc = CTFontDescriptorCreateWithAttributes(lang_attribs);
447 CFRelease(lang_arr);
448 CFRelease(lang_attribs);
449 CFRelease(lang_codes[0]);
451 /* Get array of all font descriptors for the wanted language. */
452 CFSetRef mandatory_attribs = CFSetCreate(kCFAllocatorDefault, (const void **)&kCTFontLanguagesAttribute, 1, &kCFTypeSetCallBacks);
453 CFArrayRef descs = CTFontDescriptorCreateMatchingFontDescriptors(lang_desc, mandatory_attribs);
454 CFRelease(mandatory_attribs);
455 CFRelease(lang_desc);
457 for (CFIndex i = 0; descs != NULL && i < CFArrayGetCount(descs); i++) {
458 CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs, i);
460 /* Get font traits. */
461 CFDictionaryRef traits = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute);
462 CTFontSymbolicTraits symbolic_traits;
463 CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits, kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
464 CFRelease(traits);
466 /* Skip symbol fonts and vertical fonts. */
467 if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue;
468 /* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
469 if (symbolic_traits & kCTFontBoldTrait) continue;
470 /* Select monospaced fonts if asked for. */
471 if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
473 /* Get font name. */
474 char name[128];
475 CFStringRef font_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute);
476 CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
477 CFRelease(font_name);
479 /* There are some special fonts starting with an '.' and the last
480 * resort font that aren't usable. Skip them. */
481 if (name[0] == '.' || strncmp(name, "LastResort", 10) == 0) continue;
483 /* Save result. */
484 callback->SetFontNames(settings, name);
485 if (!callback->FindMissingGlyphs(NULL)) {
486 DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name);
487 result = true;
488 break;
491 if (descs != NULL) CFRelease(descs);
492 } else
493 #endif
495 /* Create a font iterator and iterate over all fonts that
496 * are available to the application. */
497 ATSFontIterator itr;
498 ATSFontRef font;
499 ATSFontIteratorCreate(kATSFontContextLocal, NULL, NULL, kATSOptionFlagsDefaultScope, &itr);
500 while (!result && ATSFontIteratorNext(itr, &font) == noErr) {
501 /* Get font name. */
502 char name[128];
503 CFStringRef font_name;
504 ATSFontGetName(font, kATSOptionFlagsDefault, &font_name);
505 CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
507 bool monospace = IsMonospaceFont(font_name);
508 CFRelease(font_name);
510 /* Select monospaced fonts if asked for. */
511 if (monospace != callback->Monospace()) continue;
513 /* We only want the base font and not bold or italic variants. */
514 if (strstr(name, "Italic") != NULL || strstr(name, "Bold")) continue;
516 /* Skip some inappropriate or ugly looking fonts that have better alternatives. */
517 if (name[0] == '.' || strncmp(name, "Apple Symbols", 13) == 0 || strncmp(name, "LastResort", 10) == 0) continue;
519 /* Save result. */
520 callback->SetFontNames(settings, name);
521 if (!callback->FindMissingGlyphs(NULL)) {
522 DEBUG(freetype, 2, "ATS-Font for %s: %s", language_isocode, name);
523 result = true;
524 break;
527 ATSFontIteratorRelease(&itr);
530 if (!result) {
531 /* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
532 * supports. If we didn't find any other font, just try it, maybe we get lucky. */
533 callback->SetFontNames(settings, "Arial Unicode MS");
534 result = !callback->FindMissingGlyphs(NULL);
537 callback->FindMissingGlyphs(NULL);
538 return result;
541 #elif defined(WITH_FONTCONFIG) /* end ifdef __APPLE__ */
543 #include <fontconfig/fontconfig.h>
545 /* ========================================================================================
546 * FontConfig (unix) support
547 * ======================================================================================== */
548 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
550 FT_Error err = FT_Err_Cannot_Open_Resource;
552 if (!FcInit()) {
553 ShowInfoF("Unable to load font configuration");
554 } else {
555 FcPattern *match;
556 FcPattern *pat;
557 FcFontSet *fs;
558 FcResult result;
559 char *font_style;
560 char *font_family;
562 /* Split & strip the font's style */
563 font_family = strdup(font_name);
564 font_style = strchr(font_family, ',');
565 if (font_style != NULL) {
566 font_style[0] = '\0';
567 font_style++;
568 while (*font_style == ' ' || *font_style == '\t') font_style++;
571 /* Resolve the name and populate the information structure */
572 pat = FcNameParse((FcChar8*)font_family);
573 if (font_style != NULL) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
574 FcConfigSubstitute(0, pat, FcMatchPattern);
575 FcDefaultSubstitute(pat);
576 fs = FcFontSetCreate();
577 match = FcFontMatch(0, pat, &result);
579 if (fs != NULL && match != NULL) {
580 int i;
581 FcChar8 *family;
582 FcChar8 *style;
583 FcChar8 *file;
584 FcFontSetAdd(fs, match);
586 for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
587 /* Try the new filename */
588 if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch &&
589 FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
590 FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) {
592 /* The correct style? */
593 if (font_style != NULL && strcasecmp(font_style, (char*)style) != 0) continue;
595 /* Font config takes the best shot, which, if the family name is spelled
596 * wrongly a 'random' font, so check whether the family name is the
597 * same as the supplied name */
598 if (strcasecmp(font_family, (char*)family) == 0) {
599 err = FT_New_Face(_library, (char *)file, 0, face);
605 free(font_family);
606 FcPatternDestroy(pat);
607 FcFontSetDestroy(fs);
608 FcFini();
611 return err;
614 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
616 if (!FcInit()) return false;
618 bool ret = false;
620 /* Fontconfig doesn't handle full language isocodes, only the part
621 * before the _ of e.g. en_GB is used, so "remove" everything after
622 * the _. */
623 char lang[16];
624 seprintf(lang, lastof(lang), ":lang=%s", language_isocode);
625 char *split = strchr(lang, '_');
626 if (split != NULL) *split = '\0';
628 /* First create a pattern to match the wanted language. */
629 FcPattern *pat = FcNameParse((FcChar8*)lang);
630 /* We only want to know the filename. */
631 FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT, NULL);
632 /* Get the list of filenames matching the wanted language. */
633 FcFontSet *fs = FcFontList(NULL, pat, os);
635 /* We don't need these anymore. */
636 FcObjectSetDestroy(os);
637 FcPatternDestroy(pat);
639 if (fs != NULL) {
640 int best_weight = -1;
641 const char *best_font = NULL;
643 for (int i = 0; i < fs->nfont; i++) {
644 FcPattern *font = fs->fonts[i];
646 FcChar8 *file = NULL;
647 FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
648 if (res != FcResultMatch || file == NULL) {
649 continue;
652 /* Get a font with the right spacing .*/
653 int value = 0;
654 FcPatternGetInteger(font, FC_SPACING, 0, &value);
655 if (callback->Monospace() != (value == FC_MONO) && value != FC_DUAL) continue;
657 /* Do not use those that explicitly say they're slanted. */
658 FcPatternGetInteger(font, FC_SLANT, 0, &value);
659 if (value != 0) continue;
661 /* We want the fatter font as they look better at small sizes. */
662 FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
663 if (value <= best_weight) continue;
665 callback->SetFontNames(settings, (const char*)file);
667 bool missing = callback->FindMissingGlyphs(NULL);
668 DEBUG(freetype, 1, "Font \"%s\" misses%s glyphs", file, missing ? "" : " no");
670 if (!missing) {
671 best_weight = value;
672 best_font = (const char *)file;
676 if (best_font != NULL) {
677 ret = true;
678 callback->SetFontNames(settings, best_font);
679 InitFreeType(callback->Monospace());
682 /* Clean up the list of filenames. */
683 FcFontSetDestroy(fs);
686 FcFini();
687 return ret;
690 #else /* without WITH_FONTCONFIG */
691 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;}
692 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) { return false; }
693 #endif /* WITH_FONTCONFIG */
695 #endif /* WITH_FREETYPE */