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 = std::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())
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;
393 UInt8 file_path[PATH_MAX];
394 OSStatus os_err = -1;
402 CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault,
const_cast<const void **
>(
reinterpret_cast<const void *
const *
>(&kCTFontNameAttribute)), 1, &kCFTypeSetCallBacks));
403 CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get()));
406 for (CFIndex i = 0; descs.get() !=
nullptr && i < CFArrayGetCount(descs.get()) && os_err != noErr; i++) {
407 CFAutoRelease<CTFontRef> font(CTFontCreateWithFontDescriptor((CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i), 0.0,
nullptr));
409 if (CFURLGetFileSystemRepresentation(fontURL.get(),
true, file_path,
lengthof(file_path))) os_err = noErr;
412 if (os_err == noErr) {
413 DEBUG(freetype, 3,
"Font path for %s: %s", font_name, file_path);
414 err = FT_New_Face(_library, (
const char *)file_path, 0, face);
425 if (strcmp(language_isocode,
"zh_TW") == 0) {
428 }
else if (strcmp(language_isocode,
"zh_CN") == 0) {
434 char *sep = strchr(lang,
'_');
435 if (sep !=
nullptr) *sep =
'\0';
440 CFStringRef lang_codes[2];
441 lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8);
442 lang_codes[1] = CFSTR(
"en");
443 CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (
const void **)lang_codes,
lengthof(lang_codes), &kCFTypeArrayCallBacks);
444 CFAutoRelease<CFDictionaryRef> lang_attribs(CFDictionaryCreate(kCFAllocatorDefault,
const_cast<const void **
>(
reinterpret_cast<const void *
const *
>(&kCTFontLanguagesAttribute)), (
const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
447 CFRelease(lang_codes[0]);
450 CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault,
const_cast<const void **
>(
reinterpret_cast<const void *
const *
>(&kCTFontLanguagesAttribute)), 1, &kCFTypeSetCallBacks));
451 CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(lang_desc.get(), mandatory_attribs.get()));
454 for (CFIndex i = 0; descs.get() !=
nullptr && i < CFArrayGetCount(descs.get()); i++) {
455 CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i);
459 CTFontSymbolicTraits symbolic_traits;
460 CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
463 if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait))
continue;
465 if (symbolic_traits & kCTFontBoldTrait)
continue;
467 if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->
Monospace())
continue;
472 CFStringGetCString(font_name.get(), name,
lengthof(name), kCFStringEncodingUTF8);
476 if (name[0] ==
'.' || strncmp(name,
"LastResort", 10) == 0)
continue;
481 DEBUG(freetype, 2,
"CT-Font for %s: %s", language_isocode, name);
498 #elif defined(WITH_FONTCONFIG)
500 #include <fontconfig/fontconfig.h>
509 FT_Error err = FT_Err_Cannot_Open_Resource;
512 ShowInfoF(
"Unable to load font configuration");
522 font_family =
stredup(font_name);
523 font_style = strchr(font_family,
',');
524 if (font_style !=
nullptr) {
525 font_style[0] =
'\0';
527 while (*font_style ==
' ' || *font_style ==
'\t') font_style++;
531 pat = FcNameParse((FcChar8*)font_family);
532 if (font_style !=
nullptr) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
533 FcConfigSubstitute(0, pat, FcMatchPattern);
534 FcDefaultSubstitute(pat);
535 fs = FcFontSetCreate();
536 match = FcFontMatch(0, pat, &result);
538 if (fs !=
nullptr && match !=
nullptr) {
543 FcFontSetAdd(fs, match);
545 for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
547 if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch &&
548 FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
549 FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) {
552 if (font_style !=
nullptr && strcasecmp(font_style, (
char*)style) != 0)
continue;
557 if (strcasecmp(font_family, (
char*)family) == 0) {
558 err = FT_New_Face(_library, (
char *)file, 0, face);
565 FcPatternDestroy(pat);
566 FcFontSetDestroy(fs);
575 if (!FcInit())
return false;
584 char *split = strchr(lang,
'_');
585 if (split !=
nullptr) *split =
'\0';
588 FcPattern *pat = FcNameParse((FcChar8*)lang);
590 FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT,
nullptr);
592 FcFontSet *fs = FcFontList(
nullptr, pat, os);
595 FcObjectSetDestroy(os);
596 FcPatternDestroy(pat);
599 int best_weight = -1;
600 const char *best_font =
nullptr;
602 for (
int i = 0; i < fs->nfont; i++) {
603 FcPattern *font = fs->fonts[i];
605 FcChar8 *file =
nullptr;
606 FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
607 if (res != FcResultMatch || file ==
nullptr) {
613 FcPatternGetInteger(font, FC_SPACING, 0, &value);
614 if (callback->
Monospace() != (value == FC_MONO) && value != FC_DUAL)
continue;
617 FcPatternGetInteger(font, FC_SLANT, 0, &value);
618 if (value != 0)
continue;
621 FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
622 if (value <= best_weight)
continue;
627 DEBUG(freetype, 1,
"Font \"%s\" misses%s glyphs", file, missing ?
"" :
" no");
631 best_font = (
const char *)file;
635 if (best_font !=
nullptr) {
642 FcFontSetDestroy(fs);
650 FT_Error
GetFontByFaceName(
const char *font_name, FT_Face *face) {
return FT_Err_Cannot_Open_Resource;}