10 #if defined(WITH_FREETYPE) || defined(_WIN32) 19 extern FT_Library _library;
51 const char *GetShortPath(
const TCHAR *long_path)
53 static char short_path[MAX_PATH];
55 WCHAR short_path_w[MAX_PATH];
56 GetShortPathName(long_path, short_path_w,
lengthof(short_path_w));
57 WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path,
lengthof(short_path),
nullptr,
nullptr);
60 GetShortPathName(long_path, short_path,
lengthof(short_path));
73 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" 74 #define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts" 77 FT_Error err = FT_Err_Cannot_Open_Resource;
80 TCHAR vbuffer[MAX_PATH], dbuffer[256];
82 const char *font_path;
89 ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
90 if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
92 if (ret != ERROR_SUCCESS) {
93 DEBUG(freetype, 0,
"Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
98 TCHAR *font_namep = _tcsdup(
OTTD2FS(font_name));
100 for (index = 0;; index++) {
105 ret = RegEnumValue(hKey, index, vbuffer, &vbuflen,
nullptr,
nullptr, (byte*)dbuffer, &dbuflen);
106 if (ret != ERROR_SUCCESS)
goto registry_no_font_found;
117 s = _tcschr(vbuffer, _T(
'('));
118 if (s !=
nullptr) s[-1] =
'\0';
120 if (_tcschr(vbuffer, _T(
'&')) ==
nullptr) {
121 if (_tcsicmp(vbuffer, font_namep) == 0)
break;
123 if (_tcsstr(vbuffer, font_namep) !=
nullptr)
break;
127 if (!SUCCEEDED(
OTTDSHGetFolderPath(
nullptr, CSIDL_FONTS,
nullptr, SHGFP_TYPE_CURRENT, vbuffer))) {
128 DEBUG(freetype, 0,
"SHGetFolderPath cannot return fonts directory");
136 path_len = _tcslen(vbuffer) + _tcslen(dbuffer) + 2;
137 pathbuf =
AllocaM(TCHAR, path_len);
138 _sntprintf(pathbuf, path_len, _T(
"%s\\%s"), vbuffer, dbuffer);
141 font_path = GetShortPath(pathbuf);
145 err = FT_New_Face(_library, font_path, index, face);
146 if (err != FT_Err_Ok)
break;
148 if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0)
break;
150 if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0)
break;
151 err = FT_Err_Cannot_Open_Resource;
153 }
while ((FT_Long)++index != (*face)->num_faces);
157 registry_no_font_found:
176 static const char *GetEnglishFontName(
const ENUMLOGFONTEX *logfont)
178 static char font_name[MAX_PATH];
179 const char *ret_font_name =
nullptr;
185 uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset;
187 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
188 if (font ==
nullptr)
goto err1;
191 oldfont = SelectObject(dc, font);
192 dw = GetFontData(dc,
'eman', 0,
nullptr, 0);
193 if (dw == GDI_ERROR)
goto err2;
195 buf = MallocT<byte>(dw);
196 dw = GetFontData(dc,
'eman', 0, buf, dw);
197 if (dw == GDI_ERROR)
goto err3;
199 format = buf[pos++] << 8;
200 format += buf[pos++];
202 count = buf[pos++] << 8;
204 stringOffset = buf[pos++] << 8;
205 stringOffset += buf[pos++];
206 for (uint i = 0; i < count; i++) {
207 platformId = buf[pos++] << 8;
208 platformId += buf[pos++];
209 encodingId = buf[pos++] << 8;
210 encodingId += buf[pos++];
211 languageId = buf[pos++] << 8;
212 languageId += buf[pos++];
213 nameId = buf[pos++] << 8;
214 nameId += buf[pos++];
219 length = buf[pos++] << 8;
220 length += buf[pos++];
221 offset = buf[pos++] << 8;
222 offset += buf[pos++];
225 length =
min(length, MAX_PATH - 1);
226 for (uint j = 0; j < length; j++) font_name[j] = buf[stringOffset + offset + j];
227 font_name[length] =
'\0';
229 if ((platformId == 1 && languageId == 0) ||
230 (platformId == 3 && languageId == 0x0409)) {
231 ret_font_name = font_name;
239 SelectObject(dc, oldfont);
240 ReleaseDC(
nullptr, dc);
243 return ret_font_name ==
nullptr ? WIDE_TO_MB((
const TCHAR*)logfont->elfFullName) : ret_font_name;
254 FontList() : fonts(nullptr), items(0), capacity(0) { };
257 if (this->fonts ==
nullptr)
return;
259 for (uint i = 0; i < this->items; i++) {
260 free(this->fonts[i]);
266 bool Add(
const TCHAR *font) {
267 for (uint i = 0; i < this->items; i++) {
268 if (_tcscmp(this->fonts[i], font) == 0)
return false;
271 if (this->items == this->capacity) {
272 this->capacity += 10;
273 this->fonts =
ReallocT(this->fonts, this->capacity);
276 this->fonts[this->items++] = _tcsdup(font);
284 LOCALESIGNATURE locale;
289 static int CALLBACK EnumFontCallback(
const ENUMLOGFONTEX *logfont,
const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
291 EFCParam *info = (EFCParam *)lParam;
294 if (!info->fonts.Add((
const TCHAR*)logfont->elfFullName))
return 1;
296 if (!(type & TRUETYPE_FONTTYPE))
return 1;
298 if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET)
return 1;
300 if (info->callback->Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH))
return 1;
303 if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) {
306 memset(&fs, 0,
sizeof(fs));
307 HFONT font = CreateFontIndirect(&logfont->elfLogFont);
308 if (font !=
nullptr) {
309 HDC dc = GetDC(
nullptr);
310 HGDIOBJ oldfont = SelectObject(dc, font);
311 GetTextCharsetInfo(dc, &fs, 0);
312 SelectObject(dc, oldfont);
313 ReleaseDC(
nullptr, dc);
316 if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0)
return 1;
319 char font_name[MAX_PATH];
324 const char *english_name = GetEnglishFontName(logfont);
325 strecpy(font_name + strlen(font_name) + 1, english_name,
lastof(font_name));
328 bool ft_init = _library !=
nullptr;
332 if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) &&
GetFontByFaceName(font_name, &face) == FT_Err_Ok) {
338 FT_Done_FreeType(_library);
342 if (!found)
return 1;
344 const char *english_name = font_name;
347 PLOGFONT os_data = MallocT<LOGFONT>(1);
348 *os_data = logfont->elfLogFont;
349 info->callback->SetFontNames(info->settings, font_name, os_data);
350 if (info->callback->FindMissingGlyphs(
nullptr))
return 1;
351 DEBUG(freetype, 1,
"Fallback font: %s (%s)", font_name, english_name);
357 DEBUG(freetype, 1,
"Trying fallback fonts");
359 if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPTSTR)&langInfo.locale,
sizeof(langInfo.locale) /
sizeof(TCHAR)) == 0) {
361 DEBUG(freetype, 1,
"Can't get locale info for fallback font (langid=0x%x)", winlangid);
365 langInfo.callback = callback;
369 font.lfCharSet = DEFAULT_CHARSET;
370 font.lfFaceName[0] =
'\0';
371 font.lfPitchAndFamily = 0;
373 HDC dc = GetDC(
nullptr);
374 int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
375 ReleaseDC(
nullptr, dc);
379 #elif defined(__APPLE__) 390 FT_Error err = FT_Err_Cannot_Open_Resource;
394 OSStatus os_err = -1;
397 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) 404 CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, (
const void **)&kCTFontNameAttribute, 1, &kCFTypeSetCallBacks));
405 CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get()));
408 for (CFIndex i = 0; descs.get() !=
nullptr && i < CFArrayGetCount(descs.get()) && os_err != noErr; i++) {
409 CFAutoRelease<CTFontRef> font(CTFontCreateWithFontDescriptor((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i), 0.0,
nullptr));
411 if (CFURLGetFileSystemRepresentation(fontURL.get(),
true, file_path,
lengthof(file_path))) os_err = noErr;
416 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6) 417 ATSFontRef font = ATSFontFindFromName(name.get(), kATSOptionFlagsDefault);
418 if (font == kInvalidFont)
return err;
422 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) 424 os_err = ATSFontGetFileReference(font, &ref);
428 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !defined(__LP64__) 430 #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5) 431 #define ATSFSSpec FSSpec 434 os_err = ATSFontGetFileSpecification(font, (ATSFSSpec *)&spec);
435 if (os_err == noErr) os_err = FSpMakeFSRef(&spec, &ref);
440 if (os_err == noErr) os_err = FSRefMakePath(&ref, file_path,
sizeof(file_path));
444 if (os_err == noErr) {
445 DEBUG(freetype, 3,
"Font path for %s: %s", font_name, file_path);
446 err = FT_New_Face(_library, (
const char *)file_path, 0, face);
456 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) 461 if (strcmp(language_isocode,
"zh_TW") == 0) {
464 }
else if (strcmp(language_isocode,
"zh_CN") == 0) {
470 char *sep = strchr(lang,
'_');
471 if (sep !=
nullptr) *sep =
'\0';
476 CFStringRef lang_codes[2];
477 lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8);
478 lang_codes[1] = CFSTR(
"en");
479 CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (
const void **)lang_codes,
lengthof(lang_codes), &kCFTypeArrayCallBacks);
480 CFAutoRelease<CFDictionaryRef> lang_attribs(CFDictionaryCreate(kCFAllocatorDefault, (
const void**)&kCTFontLanguagesAttribute, (
const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
483 CFRelease(lang_codes[0]);
486 CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, (
const void **)&kCTFontLanguagesAttribute, 1, &kCFTypeSetCallBacks));
487 CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(lang_desc.get(), mandatory_attribs.get()));
489 for (CFIndex i = 0; descs.get() !=
nullptr && i < CFArrayGetCount(descs.get()); i++) {
490 CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i);
494 CTFontSymbolicTraits symbolic_traits;
495 CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
498 if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait))
continue;
500 if (symbolic_traits & kCTFontBoldTrait)
continue;
502 if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->
Monospace())
continue;
507 CFStringGetCString(font_name.get(), name,
lengthof(name), kCFStringEncodingUTF8);
511 if (name[0] ==
'.' || strncmp(name,
"LastResort", 10) == 0)
continue;
516 DEBUG(freetype, 2,
"CT-Font for %s: %s", language_isocode, name);
524 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6) 529 ATSFontIteratorCreate(kATSFontContextLocal,
nullptr,
nullptr, kATSOptionFlagsDefaultScope, &itr);
530 while (!result && ATSFontIteratorNext(itr, &font) == noErr) {
533 CFStringRef font_name;
534 ATSFontGetName(font, kATSOptionFlagsDefault, &font_name);
535 CFStringGetCString(font_name, name,
lengthof(name), kCFStringEncodingUTF8);
537 bool monospace = IsMonospaceFont(font_name);
538 CFRelease(font_name);
541 if (monospace != callback->
Monospace())
continue;
544 if (strstr(name,
"Italic") !=
nullptr || strstr(name,
"Bold"))
continue;
547 if (name[0] ==
'.' || strncmp(name,
"Apple Symbols", 13) == 0 || strncmp(name,
"LastResort", 10) == 0)
continue;
552 DEBUG(freetype, 2,
"ATS-Font for %s: %s", language_isocode, name);
557 ATSFontIteratorRelease(&itr);
572 #elif defined(WITH_FONTCONFIG) 574 #include <fontconfig/fontconfig.h> 583 FT_Error err = FT_Err_Cannot_Open_Resource;
586 ShowInfoF(
"Unable to load font configuration");
596 font_family =
stredup(font_name);
597 font_style = strchr(font_family,
',');
598 if (font_style !=
nullptr) {
599 font_style[0] =
'\0';
601 while (*font_style ==
' ' || *font_style ==
'\t') font_style++;
605 pat = FcNameParse((FcChar8*)font_family);
606 if (font_style !=
nullptr) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
607 FcConfigSubstitute(0, pat, FcMatchPattern);
608 FcDefaultSubstitute(pat);
609 fs = FcFontSetCreate();
610 match = FcFontMatch(0, pat, &result);
612 if (fs !=
nullptr && match !=
nullptr) {
617 FcFontSetAdd(fs, match);
619 for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
621 if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch &&
622 FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
623 FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) {
626 if (font_style !=
nullptr && strcasecmp(font_style, (
char*)style) != 0)
continue;
631 if (strcasecmp(font_family, (
char*)family) == 0) {
632 err = FT_New_Face(_library, (
char *)file, 0, face);
639 FcPatternDestroy(pat);
640 FcFontSetDestroy(fs);
649 if (!FcInit())
return false;
658 char *split = strchr(lang,
'_');
659 if (split !=
nullptr) *split =
'\0';
662 FcPattern *pat = FcNameParse((FcChar8*)lang);
664 FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT,
nullptr);
666 FcFontSet *fs = FcFontList(
nullptr, pat, os);
669 FcObjectSetDestroy(os);
670 FcPatternDestroy(pat);
673 int best_weight = -1;
674 const char *best_font =
nullptr;
676 for (
int i = 0; i < fs->nfont; i++) {
677 FcPattern *font = fs->fonts[i];
679 FcChar8 *file =
nullptr;
680 FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
681 if (res != FcResultMatch || file ==
nullptr) {
687 FcPatternGetInteger(font, FC_SPACING, 0, &value);
688 if (callback->
Monospace() != (value == FC_MONO) && value != FC_DUAL)
continue;
691 FcPatternGetInteger(font, FC_SLANT, 0, &value);
692 if (value != 0)
continue;
695 FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
696 if (value <= best_weight)
continue;
701 DEBUG(freetype, 1,
"Font \"%s\" misses%s glyphs", file, missing ?
"" :
" no");
705 best_font = (
const char *)file;
709 if (best_font !=
nullptr) {
716 FcFontSetDestroy(fs);
724 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.
std::unique_ptr< typename std::remove_pointer< T >::type, CFDeleter< typename std::remove_pointer< T >::type > > CFAutoRelease
Specialisation of std::unique_ptr for CoreFoundation objects.
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