20 extern FT_Library _library;
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), NULL, NULL);
59 GetShortPathName(long_path, short_path,
lengthof(short_path));
72 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" 73 #define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts" 76 FT_Error err = FT_Err_Cannot_Open_Resource;
79 TCHAR vbuffer[MAX_PATH], dbuffer[256];
81 const char *font_path;
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");
97 TCHAR *font_namep = _tcsdup(
OTTD2FS(font_name));
99 for (index = 0;; index++) {
104 ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, NULL, NULL, (byte*)dbuffer, &dbuflen);
105 if (ret != ERROR_SUCCESS)
goto registry_no_font_found;
116 s = _tcschr(vbuffer, _T(
'('));
117 if (s != NULL) s[-1] =
'\0';
119 if (_tcschr(vbuffer, _T(
'&')) == NULL) {
120 if (_tcsicmp(vbuffer, font_namep) == 0)
break;
122 if (_tcsstr(vbuffer, font_namep) != NULL)
break;
126 if (!SUCCEEDED(
OTTDSHGetFolderPath(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, vbuffer))) {
127 DEBUG(freetype, 0,
"SHGetFolderPath cannot return fonts directory");
135 path_len = _tcslen(vbuffer) + _tcslen(dbuffer) + 2;
136 pathbuf =
AllocaM(TCHAR, path_len);
137 _sntprintf(pathbuf, path_len, _T(
"%s\\%s"), vbuffer, dbuffer);
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;
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:
175 static const char *GetEnglishFontName(
const ENUMLOGFONTEX *logfont)
177 static char font_name[MAX_PATH];
178 const char *ret_font_name = NULL;
184 uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset;
186 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
187 if (font == NULL)
goto err1;
190 oldfont = SelectObject(dc, font);
191 dw = GetFontData(dc,
'eman', 0, NULL, 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++];
218 length = buf[pos++] << 8;
219 length += buf[pos++];
220 offset = buf[pos++] << 8;
221 offset += buf[pos++];
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) ||
229 (platformId == 3 && languageId == 0x0409)) {
230 ret_font_name = font_name;
238 SelectObject(dc, oldfont);
242 return ret_font_name == NULL ? WIDE_TO_MB((
const TCHAR*)logfont->elfFullName) : ret_font_name;
252 FontList() : fonts(NULL), items(0), capacity(0) { };
255 if (this->fonts == NULL)
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);
282 LOCALESIGNATURE locale;
287 static int CALLBACK EnumFontCallback(
const ENUMLOGFONTEX *logfont,
const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
289 EFCParam *info = (EFCParam *)lParam;
292 if (!info->fonts.Add((
const TCHAR*)logfont->elfFullName))
return 1;
294 if (!(type & TRUETYPE_FONTTYPE))
return 1;
296 if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET)
return 1;
298 if (info->callback->Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH))
return 1;
301 if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) {
304 memset(&fs, 0,
sizeof(fs));
305 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
307 HDC dc = GetDC(NULL);
308 HGDIOBJ oldfont = SelectObject(dc, font);
309 GetTextCharsetInfo(dc, &fs, 0);
310 SelectObject(dc, oldfont);
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];
321 const char *english_name = GetEnglishFontName(logfont);
322 strecpy(font_name + strlen(font_name) + 1, english_name,
lastof(font_name));
325 bool ft_init = _library != NULL;
329 if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) &&
GetFontByFaceName(font_name, &face) == FT_Err_Ok) {
335 FT_Done_FreeType(_library);
339 if (!found)
return 1;
341 info->callback->SetFontNames(info->settings, font_name);
342 if (info->callback->FindMissingGlyphs(NULL))
return 1;
343 DEBUG(freetype, 1,
"Fallback font: %s (%s)", font_name, english_name);
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) {
353 DEBUG(freetype, 1,
"Can't get locale info for fallback font (langid=0x%x)", winlangid);
356 langInfo.settings = settings;
357 langInfo.callback = callback;
361 font.lfCharSet = DEFAULT_CHARSET;
362 font.lfFaceName[0] =
'\0';
363 font.lfPitchAndFamily = 0;
365 HDC dc = GetDC(NULL);
366 int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
371 #elif defined(__APPLE__) 382 FT_Error err = FT_Err_Cannot_Open_Resource;
385 CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8);
386 ATSFontRef font = ATSFontFindFromName(name, kATSOptionFlagsDefault);
388 if (font == kInvalidFont)
return err;
392 OSStatus os_err = -1;
393 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) 395 os_err = ATSFontGetFileReference(font, &ref);
399 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !defined(__LP64__) 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) {
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);
426 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) 431 if (strcmp(language_isocode,
"zh_TW") == 0) {
434 }
else if (strcmp(language_isocode,
"zh_CN") == 0) {
440 char *sep = strchr(lang,
'_');
441 if (sep != NULL) *sep =
'\0';
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]);
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 != NULL && i < CFArrayGetCount(descs); i++) {
462 CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs, i);
465 CFDictionaryRef traits = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute);
466 CTFontSymbolicTraits symbolic_traits;
467 CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits, kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
471 if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait))
continue;
473 if (symbolic_traits & kCTFontBoldTrait)
continue;
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);
485 if (name[0] ==
'.' || strncmp(name,
"LastResort", 10) == 0)
continue;
490 DEBUG(freetype, 2,
"CT-Font for %s: %s", language_isocode, name);
495 if (descs != NULL) CFRelease(descs);
503 ATSFontIteratorCreate(kATSFontContextLocal, NULL, NULL, 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);
515 if (monospace != callback->
Monospace())
continue;
518 if (strstr(name,
"Italic") != NULL || strstr(name,
"Bold"))
continue;
521 if (name[0] ==
'.' || strncmp(name,
"Apple Symbols", 13) == 0 || strncmp(name,
"LastResort", 10) == 0)
continue;
526 DEBUG(freetype, 2,
"ATS-Font for %s: %s", language_isocode, name);
531 ATSFontIteratorRelease(&itr);
545 #elif defined(WITH_FONTCONFIG) 547 #include <fontconfig/fontconfig.h> 556 FT_Error err = FT_Err_Cannot_Open_Resource;
559 ShowInfoF(
"Unable to load font configuration");
569 font_family =
stredup(font_name);
570 font_style = strchr(font_family,
',');
571 if (font_style != NULL) {
572 font_style[0] =
'\0';
574 while (*font_style ==
' ' || *font_style ==
'\t') font_style++;
578 pat = FcNameParse((FcChar8*)font_family);
579 if (font_style != NULL) 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 != NULL && match != NULL) {
590 FcFontSetAdd(fs, match);
592 for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
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) {
599 if (font_style != NULL && strcasecmp(font_style, (
char*)style) != 0)
continue;
604 if (strcasecmp(font_family, (
char*)family) == 0) {
605 err = FT_New_Face(_library, (
char *)file, 0, face);
612 FcPatternDestroy(pat);
613 FcFontSetDestroy(fs);
622 if (!FcInit())
return false;
631 char *split = strchr(lang,
'_');
632 if (split != NULL) *split =
'\0';
635 FcPattern *pat = FcNameParse((FcChar8*)lang);
637 FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT, NULL);
639 FcFontSet *fs = FcFontList(NULL, pat, os);
642 FcObjectSetDestroy(os);
643 FcPatternDestroy(pat);
646 int best_weight = -1;
647 const char *best_font = NULL;
649 for (
int i = 0; i < fs->nfont; i++) {
650 FcPattern *font = fs->fonts[i];
652 FcChar8 *file = NULL;
653 FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
654 if (res != FcResultMatch || file == NULL) {
660 FcPatternGetInteger(font, FC_SPACING, 0, &value);
661 if (callback->
Monospace() != (value == FC_MONO) && value != FC_DUAL)
continue;
664 FcPatternGetInteger(font, FC_SLANT, 0, &value);
665 if (value != 0)
continue;
668 FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
669 if (value <= best_weight)
continue;
674 DEBUG(freetype, 1,
"Font \"%s\" misses%s glyphs", file, missing ?
"" :
" no");
678 best_font = (
const char *)file;
682 if (best_font != NULL) {
689 FcFontSetDestroy(fs);
697 FT_Error
GetFontByFaceName(
const char *font_name, FT_Face *face) {
return FT_Err_Cannot_Open_Resource;}
Functions related to OTTD's strings.
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Functions related to debugging.
HRESULT OTTDSHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
Our very own SHGetFolderPath function for support of windows operating systems that don't have this f...
static bool MacOSVersionIsAtLeast(long major, long minor, long bugfix)
Check if we are at least running on the specified version of Mac OS.
Functions related to detecting/finding the right font.
virtual bool Monospace()=0
Whether to search for a monospace font or not.
#define lastof(x)
Get the last element of an fixed size array.
#define AllocaM(T, num_elements)
alloca() has to be called in the parent function, so define AllocaM() as a macro
char * convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen)
Convert to OpenTTD's encoding from that of the environment in UNICODE.
Settings for the freetype fonts.
void InitFreeType(bool monospace)
(Re)initialize the freetype related things, i.e.
void CDECL ShowInfoF(const char *str,...)
Shows some information on the console/a popup box depending on the OS.
Functions related to low-level strings.
Functions related to the allocation of memory.
A searcher for missing glyphs.
Definition of base types and functions in a cross-platform compatible way.
A number of safeguards to prevent using unsafe methods.
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
Get the font loaded into a Freetype face by using a font-name.
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
static T * ReallocT(T *t_ptr, size_t num_elements)
Simplified reallocation function that allocates the specified number of elements of the given type...
bool FindMissingGlyphs(const char **str)
Check whether there are glyphs missing in the current language.
#define lengthof(x)
Return the length of an fixed size array.
static T min(const T a, const T b)
Returns the minimum of two values.
const TCHAR * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD's encoding to that of the local environment.
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
We would like to have a fallback font as the current one doesn't contain all characters we need...
#define DEBUG(name, level,...)
Output a line of debugging information.
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
#define PATH_MAX
The maximum length of paths, if we don't know it.
virtual void SetFontNames(struct FreeTypeSettings *settings, const char *font_name)=0
Set the right font names.
Functions related to MacOS support.
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
declarations of functions for MS windows systems