1 /* $Id: fontdetection.cpp 25676 2013-08-05 20:36:55Z michi_cc $ */
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/>.
10 /** @file fontdetection.cpp Detection of the right font. */
16 #include "fontdetection.h"
17 #include "string_func.h"
18 #include "strings_func.h"
20 extern FT_Library _library
;
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 /* ========================================================================================
29 * ======================================================================================== */
32 #include "core/alloc_func.hpp"
33 #include "core/math_func.hpp"
35 #include <shlobj.h> /* SHGetFolderPath */
36 #include "os/windows/win32.h"
38 #include "safeguards.h"
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
];
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);
58 /* Technically not needed, but do it for consistency. */
59 GetShortPathName(long_path
, short_path
, lengthof(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
;
79 TCHAR vbuffer
[MAX_PATH
], dbuffer
[256];
81 const char *font_path
;
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");
96 /* Convert font name to file system encoding. */
97 TCHAR
*font_namep
= _tcsdup(OTTD2FS(font_name
));
99 for (index
= 0;; index
++) {
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:
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;
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");
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
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
);
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
);
156 registry_no_font_found
:
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;
184 uint16 format
, count
, stringOffset
, platformId
, encodingId
, languageId
, nameId
, length
, offset
;
186 HFONT font
= CreateFontIndirect(&logfont
->elfLogFont
);
187 if (font
== nullptr) goto err1
;
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
++];
201 count
= buf
[pos
++] << 8;
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
++];
215 pos
+= 4; // skip length and offset
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
;
238 SelectObject(dc
, oldfont
);
239 ReleaseDC(nullptr, dc
);
242 return ret_font_name
== nullptr ? WIDE_TO_MB((const TCHAR
*)logfont
->elfFullName
) : ret_font_name
;
252 FontList() : fonts(nullptr), items(0), capacity(0) { };
255 if (this->fonts
== nullptr) return;
257 for (uint i
= 0; i
< this->items
; i
++) {
258 free(this->fonts
[i
]);
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
);
281 FreeTypeSettings
*settings
;
282 LOCALESIGNATURE locale
;
283 MissingGlyphSearcher
*callback
;
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. */
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
);
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;
328 /* Init FreeType if needed. */
329 if ((ft_init
|| FT_Init_FreeType(&_library
) == FT_Err_Ok
) && GetFontByFaceName(font_name
, &face
) == FT_Err_Ok
) {
334 /* Uninit FreeType if we did the init. */
335 FT_Done_FreeType(_library
);
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");
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
);
356 langInfo
.settings
= settings
;
357 langInfo
.callback
= callback
;
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
);
371 #elif defined(__APPLE__) /* end ifdef Win32 */
372 /* ========================================================================================
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
);
388 if (font
== kInvalidFont
) return err
;
390 /* Get a file system reference for the font. */
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
);
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
405 os_err
= ATSFontGetFileSpecification(font
, (ATSFSSpec
*)&spec
);
406 if (os_err
== noErr
) os_err
= FSpMakeFSRef(&spec
, &ref
);
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
);
422 bool SetFallbackFont(FreeTypeSettings
*settings
, const char *language_isocode
, int winlangid
, MissingGlyphSearcher
*callback
)
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. */
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
));
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
);
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
);
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;
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;
488 callback
->SetFontNames(settings
, name
);
489 if (!callback
->FindMissingGlyphs(nullptr)) {
490 DEBUG(freetype
, 2, "CT-Font for %s: %s", language_isocode
, name
);
495 if (descs
!= nullptr) CFRelease(descs
);
499 /* Create a font iterator and iterate over all fonts that
500 * are available to the application. */
503 ATSFontIteratorCreate(kATSFontContextLocal
, nullptr, nullptr, kATSOptionFlagsDefaultScope
, &itr
);
504 while (!result
&& ATSFontIteratorNext(itr
, &font
) == noErr
) {
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;
524 callback
->SetFontNames(settings
, name
);
525 if (!callback
->FindMissingGlyphs(nullptr)) {
526 DEBUG(freetype
, 2, "ATS-Font for %s: %s", language_isocode
, name
);
531 ATSFontIteratorRelease(&itr
);
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);
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
;
559 ShowInfoF("Unable to load font configuration");
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';
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) {
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
);
612 FcPatternDestroy(pat
);
613 FcFontSetDestroy(fs
);
620 bool SetFallbackFont(FreeTypeSettings
*settings
, const char *language_isocode
, int winlangid
, MissingGlyphSearcher
*callback
)
622 if (!FcInit()) return 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
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
);
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) {
658 /* Get a font with the right spacing .*/
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");
678 best_font
= (const char *)file
;
682 if (best_font
!= nullptr) {
684 callback
->SetFontNames(settings
, best_font
);
685 InitFreeType(callback
->Monospace());
688 /* Clean up the list of filenames. */
689 FcFontSetDestroy(fs
);
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 */