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;}