12 #if defined(WITH_FREETYPE) || defined(_WIN32) 21 extern FT_Library _library;
53 const char *GetShortPath(
const TCHAR *long_path)
55 static char short_path[MAX_PATH];
57 WCHAR short_path_w[MAX_PATH];
58 GetShortPathName(long_path, short_path_w,
lengthof(short_path_w));
59 WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path,
lengthof(short_path),
nullptr,
nullptr);
62 GetShortPathName(long_path, short_path,
lengthof(short_path));
75 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" 76 #define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts" 79 FT_Error err = FT_Err_Cannot_Open_Resource;
82 TCHAR vbuffer[MAX_PATH], dbuffer[256];
84 const char *font_path;
91 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
92 if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
94 if (ret != ERROR_SUCCESS) {
95 DEBUG(freetype, 0,
"Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
100 TCHAR *font_namep = _tcsdup(
OTTD2FS(font_name));
102 for (index = 0;; index++) {
107 ret = RegEnumValue(hKey, index, vbuffer, &vbuflen,
nullptr,
nullptr, (byte*)dbuffer, &dbuflen);
108 if (ret != ERROR_SUCCESS)
goto registry_no_font_found;
119 s = _tcschr(vbuffer, _T(
'('));
120 if (s !=
nullptr) s[-1] =
'\0';
122 if (_tcschr(vbuffer, _T(
'&')) ==
nullptr) {
123 if (_tcsicmp(vbuffer, font_namep) == 0)
break;
125 if (_tcsstr(vbuffer, font_namep) !=
nullptr)
break;
129 if (!SUCCEEDED(
OTTDSHGetFolderPath(
nullptr, CSIDL_FONTS,
nullptr, SHGFP_TYPE_CURRENT, vbuffer))) {
130 DEBUG(freetype, 0,
"SHGetFolderPath cannot return fonts directory");
138 path_len = _tcslen(vbuffer) + _tcslen(dbuffer) + 2;
139 pathbuf =
AllocaM(TCHAR, path_len);
140 _sntprintf(pathbuf, path_len, _T(
"%s\\%s"), vbuffer, dbuffer);
143 font_path = GetShortPath(pathbuf);
147 err = FT_New_Face(_library, font_path, index, face);
148 if (err != FT_Err_Ok)
break;
150 if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0)
break;
152 if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0)
break;
153 err = FT_Err_Cannot_Open_Resource;
155 }
while ((FT_Long)++index != (*face)->num_faces);
159 registry_no_font_found:
178 static const char *GetEnglishFontName(
const ENUMLOGFONTEX *logfont)
180 static char font_name[MAX_PATH];
181 const char *ret_font_name =
nullptr;
187 uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset;
189 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
190 if (font ==
nullptr)
goto err1;
193 oldfont = SelectObject(dc, font);
194 dw = GetFontData(dc,
'eman', 0,
nullptr, 0);
195 if (dw == GDI_ERROR)
goto err2;
197 buf = MallocT<byte>(dw);
198 dw = GetFontData(dc,
'eman', 0, buf, dw);
199 if (dw == GDI_ERROR)
goto err3;
201 format = buf[pos++] << 8;
202 format += buf[pos++];
204 count = buf[pos++] << 8;
206 stringOffset = buf[pos++] << 8;
207 stringOffset += buf[pos++];
208 for (uint i = 0; i < count; i++) {
209 platformId = buf[pos++] << 8;
210 platformId += buf[pos++];
211 encodingId = buf[pos++] << 8;
212 encodingId += buf[pos++];
213 languageId = buf[pos++] << 8;
214 languageId += buf[pos++];
215 nameId = buf[pos++] << 8;
216 nameId += buf[pos++];
221 length = buf[pos++] << 8;
222 length += buf[pos++];
223 offset = buf[pos++] << 8;
224 offset += buf[pos++];
227 length =
min(length, MAX_PATH - 1);
228 for (uint j = 0; j < length; j++) font_name[j] = buf[stringOffset + offset + j];
229 font_name[length] =
'\0';
231 if ((platformId == 1 && languageId == 0) ||
232 (platformId == 3 && languageId == 0x0409)) {
233 ret_font_name = font_name;
241 SelectObject(dc, oldfont);
242 ReleaseDC(
nullptr, dc);
245 return ret_font_name ==
nullptr ? WIDE_TO_MB((
const TCHAR*)logfont->elfFullName) : ret_font_name;
256 FontList() : fonts(nullptr), items(0), capacity(0) { };
259 if (this->fonts ==
nullptr)
return;
261 for (uint i = 0; i < this->items; i++) {
262 free(this->fonts[i]);
268 bool Add(
const TCHAR *font) {
269 for (uint i = 0; i < this->items; i++) {
270 if (_tcscmp(this->fonts[i], font) == 0)
return false;
273 if (this->items == this->capacity) {
274 this->capacity += 10;
275 this->fonts =
ReallocT(this->fonts, this->capacity);
278 this->fonts[this->items++] = _tcsdup(font);
286 LOCALESIGNATURE locale;
291 static int CALLBACK EnumFontCallback(
const ENUMLOGFONTEX *logfont,
const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
293 EFCParam *info = (EFCParam *)lParam;
296 if (!info->fonts.Add((
const TCHAR*)logfont->elfFullName))
return 1;
298 if (!(type & TRUETYPE_FONTTYPE))
return 1;
300 if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET)
return 1;
302 if (info->callback->Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH))
return 1;
305 if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) {
308 memset(&fs, 0,
sizeof(fs));
309 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
310 if (font !=
nullptr) {
311 HDC dc = GetDC(
nullptr);
312 HGDIOBJ oldfont = SelectObject(dc, font);
313 GetTextCharsetInfo(dc, &fs, 0);
314 SelectObject(dc, oldfont);
315 ReleaseDC(
nullptr, dc);
318 if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0)
return 1;
321 char font_name[MAX_PATH];
326 const char *english_name = GetEnglishFontName(logfont);
327 strecpy(font_name + strlen(font_name) + 1, english_name,
lastof(font_name));
330 bool ft_init = _library !=
nullptr;
334 if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) &&
GetFontByFaceName(font_name, &face) == FT_Err_Ok) {
340 FT_Done_FreeType(_library);
344 if (!found)
return 1;
346 const char *english_name = font_name;
349 PLOGFONT os_data = MallocT<LOGFONT>(1);
350 *os_data = logfont->elfLogFont;
351 info->callback->SetFontNames(info->settings, font_name, os_data);
352 if (info->callback->FindMissingGlyphs(
nullptr))
return 1;
353 DEBUG(freetype, 1,
"Fallback font: %s (%s)", font_name, english_name);
359 DEBUG(freetype, 1,
"Trying fallback fonts");
361 if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPTSTR)&langInfo.locale,
sizeof(langInfo.locale) /
sizeof(TCHAR)) == 0) {
363 DEBUG(freetype, 1,
"Can't get locale info for fallback font (langid=0x%x)", winlangid);
367 langInfo.callback = callback;
371 font.lfCharSet = DEFAULT_CHARSET;
372 font.lfFaceName[0] =
'\0';
373 font.lfPitchAndFamily = 0;
375 HDC dc = GetDC(
nullptr);
376 int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
377 ReleaseDC(
nullptr, dc);
381 #elif defined(__APPLE__) 392 FT_Error err = FT_Err_Cannot_Open_Resource;
396 OSStatus os_err = -1;
397 CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8);
399 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) 405 CTFontDescriptorRef name_desc = CTFontDescriptorCreateWithNameAndSize(name, 0.0);
406 CFSetRef mandatory_attribs = CFSetCreate(kCFAllocatorDefault, (
const void **)&kCTFontNameAttribute, 1, &kCFTypeSetCallBacks);
407 CFArrayRef descs = CTFontDescriptorCreateMatchingFontDescriptors(name_desc, mandatory_attribs);
408 CFRelease(mandatory_attribs);
409 CFRelease(name_desc);
413 for (CFIndex i = 0; descs !=
nullptr && i < CFArrayGetCount(descs) && os_err != noErr; i++) {
414 CTFontRef font = CTFontCreateWithFontDescriptor((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs, i), 0.0,
nullptr);
415 CFURLRef fontURL = (CFURLRef)CTFontCopyAttribute(font, kCTFontURLAttribute);
416 if (CFURLGetFileSystemRepresentation(fontURL,
true, file_path,
lengthof(file_path))) os_err = noErr;
420 if (descs !=
nullptr) CFRelease(descs);
424 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6) 425 ATSFontRef font = ATSFontFindFromName(name, kATSOptionFlagsDefault);
427 if (font == kInvalidFont)
return err;
431 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) 433 os_err = ATSFontGetFileReference(font, &ref);
437 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !defined(__LP64__) 439 #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5) 440 #define ATSFSSpec FSSpec 443 os_err = ATSFontGetFileSpecification(font, (ATSFSSpec *)&spec);
444 if (os_err == noErr) os_err = FSpMakeFSRef(&spec, &ref);
449 if (os_err == noErr) os_err = FSRefMakePath(&ref, file_path,
sizeof(file_path));
453 if (os_err == noErr) {
454 DEBUG(freetype, 3,
"Font path for %s: %s", font_name, file_path);
455 err = FT_New_Face(_library, (
const char *)file_path, 0, face);
465 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) 470 if (strcmp(language_isocode,
"zh_TW") == 0) {
473 }
else if (strcmp(language_isocode,
"zh_CN") == 0) {
479 char *sep = strchr(lang,
'_');
480 if (sep !=
nullptr) *sep =
'\0';
484 CFStringRef lang_codes[2];
485 lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8);
486 lang_codes[1] = CFSTR(
"en");
487 CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (
const void **)lang_codes,
lengthof(lang_codes), &kCFTypeArrayCallBacks);
488 CFDictionaryRef lang_attribs = CFDictionaryCreate(kCFAllocatorDefault, (
const void**)&kCTFontLanguagesAttribute, (
const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
489 CTFontDescriptorRef lang_desc = CTFontDescriptorCreateWithAttributes(lang_attribs);
491 CFRelease(lang_attribs);
492 CFRelease(lang_codes[0]);
495 CFSetRef mandatory_attribs = CFSetCreate(kCFAllocatorDefault, (
const void **)&kCTFontLanguagesAttribute, 1, &kCFTypeSetCallBacks);
496 CFArrayRef descs = CTFontDescriptorCreateMatchingFontDescriptors(lang_desc, mandatory_attribs);
497 CFRelease(mandatory_attribs);
498 CFRelease(lang_desc);
500 for (CFIndex i = 0; descs !=
nullptr && i < CFArrayGetCount(descs); i++) {
501 CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs, i);
504 CFDictionaryRef traits = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute);
505 CTFontSymbolicTraits symbolic_traits;
506 CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits, kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
510 if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait))
continue;
512 if (symbolic_traits & kCTFontBoldTrait)
continue;
514 if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->
Monospace())
continue;
518 CFStringRef font_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute);
519 CFStringGetCString(font_name, name,
lengthof(name), kCFStringEncodingUTF8);
520 CFRelease(font_name);
524 if (name[0] ==
'.' || strncmp(name,
"LastResort", 10) == 0)
continue;
529 DEBUG(freetype, 2,
"CT-Font for %s: %s", language_isocode, name);
534 if (descs !=
nullptr) CFRelease(descs);
538 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6) 543 ATSFontIteratorCreate(kATSFontContextLocal,
nullptr,
nullptr, kATSOptionFlagsDefaultScope, &itr);
544 while (!result && ATSFontIteratorNext(itr, &font) == noErr) {
547 CFStringRef font_name;
548 ATSFontGetName(font, kATSOptionFlagsDefault, &font_name);
549 CFStringGetCString(font_name, name,
lengthof(name), kCFStringEncodingUTF8);
551 bool monospace = IsMonospaceFont(font_name);
552 CFRelease(font_name);
555 if (monospace != callback->
Monospace())
continue;
558 if (strstr(name,
"Italic") !=
nullptr || strstr(name,
"Bold"))
continue;
561 if (name[0] ==
'.' || strncmp(name,
"Apple Symbols", 13) == 0 || strncmp(name,
"LastResort", 10) == 0)
continue;
566 DEBUG(freetype, 2,
"ATS-Font for %s: %s", language_isocode, name);
571 ATSFontIteratorRelease(&itr);
586 #elif defined(WITH_FONTCONFIG) 588 #include <fontconfig/fontconfig.h> 597 FT_Error err = FT_Err_Cannot_Open_Resource;
600 ShowInfoF(
"Unable to load font configuration");
610 font_family =
stredup(font_name);
611 font_style = strchr(font_family,
',');
612 if (font_style !=
nullptr) {
613 font_style[0] =
'\0';
615 while (*font_style ==
' ' || *font_style ==
'\t') font_style++;
619 pat = FcNameParse((FcChar8*)font_family);
620 if (font_style !=
nullptr) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
621 FcConfigSubstitute(0, pat, FcMatchPattern);
622 FcDefaultSubstitute(pat);
623 fs = FcFontSetCreate();
624 match = FcFontMatch(0, pat, &result);
626 if (fs !=
nullptr && match !=
nullptr) {
631 FcFontSetAdd(fs, match);
633 for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
635 if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch &&
636 FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
637 FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) {
640 if (font_style !=
nullptr && strcasecmp(font_style, (
char*)style) != 0)
continue;
645 if (strcasecmp(font_family, (
char*)family) == 0) {
646 err = FT_New_Face(_library, (
char *)file, 0, face);
653 FcPatternDestroy(pat);
654 FcFontSetDestroy(fs);
663 if (!FcInit())
return false;
672 char *split = strchr(lang,
'_');
673 if (split !=
nullptr) *split =
'\0';
676 FcPattern *pat = FcNameParse((FcChar8*)lang);
678 FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT,
nullptr);
680 FcFontSet *fs = FcFontList(
nullptr, pat, os);
683 FcObjectSetDestroy(os);
684 FcPatternDestroy(pat);
687 int best_weight = -1;
688 const char *best_font =
nullptr;
690 for (
int i = 0; i < fs->nfont; i++) {
691 FcPattern *font = fs->fonts[i];
693 FcChar8 *file =
nullptr;
694 FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
695 if (res != FcResultMatch || file ==
nullptr) {
701 FcPatternGetInteger(font, FC_SPACING, 0, &value);
702 if (callback->
Monospace() != (value == FC_MONO) && value != FC_DUAL)
continue;
705 FcPatternGetInteger(font, FC_SLANT, 0, &value);
706 if (value != 0)
continue;
709 FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
710 if (value <= best_weight)
continue;
715 DEBUG(freetype, 1,
"Font \"%s\" misses%s glyphs", file, missing ?
"" :
" no");
719 best_font = (
const char *)file;
723 if (best_font !=
nullptr) {
730 FcFontSetDestroy(fs);
738 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...
fluid_settings_t * settings
FluidSynth settings handle.
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.
virtual void SetFontNames(struct FreeTypeSettings *settings, const char *font_name, const void *os_data=nullptr)=0
Set the right font names.
#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.
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