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"
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
];
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
);
56 /* Technically not needed, but do it for consistency. */
57 GetShortPathName(long_path
, short_path
, lengthof(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
;
77 TCHAR vbuffer
[MAX_PATH
], dbuffer
[256];
79 const char *font_path
;
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");
94 /* Convert font name to file system encoding. */
95 TCHAR
*font_namep
= _tcsdup(OTTD2FS(font_name
));
97 for (index
= 0;; index
++) {
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:
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;
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");
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
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
);
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
);
154 registry_no_font_found
:
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
;
182 uint16 format
, count
, stringOffset
, platformId
, encodingId
, languageId
, nameId
, length
, offset
;
184 HFONT font
= CreateFontIndirect(&logfont
->elfLogFont
);
185 if (font
== NULL
) goto err1
;
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
++];
199 count
= buf
[pos
++] << 8;
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
++];
213 pos
+= 4; // skip length and offset
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
;
236 SelectObject(dc
, oldfont
);
240 return ret_font_name
== NULL
? WIDE_TO_MB((const TCHAR
*)logfont
->elfFullName
) : ret_font_name
;
250 FontList() : fonts(NULL
), items(0), capacity(0) { };
253 if (this->fonts
== NULL
) return;
255 for (uint i
= 0; i
< this->items
; i
++) {
256 free(this->fonts
[i
]);
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
);
279 FreeTypeSettings
*settings
;
280 LOCALESIGNATURE locale
;
281 MissingGlyphSearcher
*callback
;
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. */
302 memset(&fs
, 0, sizeof(fs
));
303 HFONT font
= CreateFontIndirect(&logfont
->elfLogFont
);
305 HDC dc
= GetDC(NULL
);
306 HGDIOBJ oldfont
= SelectObject(dc
, font
);
307 GetTextCharsetInfo(dc
, &fs
, 0);
308 SelectObject(dc
, oldfont
);
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
;
326 /* Init FreeType if needed. */
327 if ((ft_init
|| FT_Init_FreeType(&_library
) == FT_Err_Ok
) && GetFontByFaceName(font_name
, &face
) == FT_Err_Ok
) {
332 /* Uninit FreeType if we did the init. */
333 FT_Done_FreeType(_library
);
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");
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
);
354 langInfo
.settings
= settings
;
355 langInfo
.callback
= callback
;
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);
369 #elif defined(__APPLE__) /* end ifdef Win32 */
370 /* ========================================================================================
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
);
384 if (font
== kInvalidFont
) return err
;
386 /* Get a file system reference for the font. */
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
);
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
401 os_err
= ATSFontGetFileSpecification(font
, (ATSFSSpec
*)&spec
);
402 if (os_err
== noErr
) os_err
= FSpMakeFSRef(&spec
, &ref
);
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
);
418 bool SetFallbackFont(FreeTypeSettings
*settings
, const char *language_isocode
, int winlangid
, MissingGlyphSearcher
*callback
)
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. */
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
));
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
);
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
);
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;
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;
484 callback
->SetFontNames(settings
, name
);
485 if (!callback
->FindMissingGlyphs(NULL
)) {
486 DEBUG(freetype
, 2, "CT-Font for %s: %s", language_isocode
, name
);
491 if (descs
!= NULL
) CFRelease(descs
);
495 /* Create a font iterator and iterate over all fonts that
496 * are available to the application. */
499 ATSFontIteratorCreate(kATSFontContextLocal
, NULL
, NULL
, kATSOptionFlagsDefaultScope
, &itr
);
500 while (!result
&& ATSFontIteratorNext(itr
, &font
) == noErr
) {
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;
520 callback
->SetFontNames(settings
, name
);
521 if (!callback
->FindMissingGlyphs(NULL
)) {
522 DEBUG(freetype
, 2, "ATS-Font for %s: %s", language_isocode
, name
);
527 ATSFontIteratorRelease(&itr
);
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
);
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
;
553 ShowInfoF("Unable to load font configuration");
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';
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
) {
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
);
606 FcPatternDestroy(pat
);
607 FcFontSetDestroy(fs
);
614 bool SetFallbackFont(FreeTypeSettings
*settings
, const char *language_isocode
, int winlangid
, MissingGlyphSearcher
*callback
)
616 if (!FcInit()) return 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
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
);
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
) {
652 /* Get a font with the right spacing .*/
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");
672 best_font
= (const char *)file
;
676 if (best_font
!= NULL
) {
678 callback
->SetFontNames(settings
, best_font
);
679 InitFreeType(callback
->Monospace());
682 /* Clean up the list of filenames. */
683 FcFontSetDestroy(fs
);
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 */