OpenTTD
fontdetection.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
10 #if defined(WITH_FREETYPE) || defined(_WIN32)
11 
12 #include "stdafx.h"
13 #include "debug.h"
14 #include "fontdetection.h"
15 #include "string_func.h"
16 #include "strings_func.h"
17 
18 #ifdef WITH_FREETYPE
19 extern FT_Library _library;
20 #endif /* WITH_FREETYPE */
21 
27 /* ========================================================================================
28  * Windows support
29  * ======================================================================================== */
30 
31 #ifdef _WIN32
32 #include "core/alloc_func.hpp"
33 #include "core/math_func.hpp"
34 #include <windows.h>
35 #include <shlobj.h> /* SHGetFolderPath */
36 #include "os/windows/win32.h"
37 
38 #include "safeguards.h"
39 
40 #ifdef WITH_FREETYPE
41 
51 const char *GetShortPath(const TCHAR *long_path)
52 {
53  static char short_path[MAX_PATH];
54 #ifdef UNICODE
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);
58 #else
59  /* Technically not needed, but do it for consistency. */
60  GetShortPathName(long_path, short_path, lengthof(short_path));
61 #endif
62  return short_path;
63 }
64 
65 /* Get the font file to be loaded into Freetype by looping the registry
66  * location where windows lists all installed fonts. Not very nice, will
67  * surely break if the registry path changes, but it works. Much better
68  * solution would be to use CreateFont, and extract the font data from it
69  * by GetFontData. The problem with this is that the font file needs to be
70  * kept in memory then until the font is no longer needed. This could mean
71  * an additional memory usage of 30MB (just for fonts!) when using an eastern
72  * font for all font sizes */
73 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
74 #define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts"
75 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
76 {
77  FT_Error err = FT_Err_Cannot_Open_Resource;
78  HKEY hKey;
79  LONG ret;
80  TCHAR vbuffer[MAX_PATH], dbuffer[256];
81  TCHAR *pathbuf;
82  const char *font_path;
83  uint index;
84  size_t path_len;
85 
86  /* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
87  * "Windows NT" key, on Windows 9x in the Windows key. To save us having
88  * to retrieve the windows version, we'll just query both */
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);
91 
92  if (ret != ERROR_SUCCESS) {
93  DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
94  return err;
95  }
96 
97  /* Convert font name to file system encoding. */
98  TCHAR *font_namep = _tcsdup(OTTD2FS(font_name));
99 
100  for (index = 0;; index++) {
101  TCHAR *s;
102  DWORD vbuflen = lengthof(vbuffer);
103  DWORD dbuflen = lengthof(dbuffer);
104 
105  ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, nullptr, nullptr, (byte*)dbuffer, &dbuflen);
106  if (ret != ERROR_SUCCESS) goto registry_no_font_found;
107 
108  /* The font names in the registry are of the following 3 forms:
109  * - ADMUI3.fon
110  * - Book Antiqua Bold (TrueType)
111  * - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
112  * We will strip the font-type '()' if any and work with the font name
113  * itself, which must match exactly; if...
114  * TTC files, font files which contain more than one font are separated
115  * by '&'. Our best bet will be to do substr match for the fontname
116  * and then let FreeType figure out which index to load */
117  s = _tcschr(vbuffer, _T('('));
118  if (s != nullptr) s[-1] = '\0';
119 
120  if (_tcschr(vbuffer, _T('&')) == nullptr) {
121  if (_tcsicmp(vbuffer, font_namep) == 0) break;
122  } else {
123  if (_tcsstr(vbuffer, font_namep) != nullptr) break;
124  }
125  }
126 
127  if (!SUCCEEDED(OTTDSHGetFolderPath(nullptr, CSIDL_FONTS, nullptr, SHGFP_TYPE_CURRENT, vbuffer))) {
128  DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory");
129  goto folder_error;
130  }
131 
132  /* Some fonts are contained in .ttc files, TrueType Collection fonts. These
133  * contain multiple fonts inside this single file. GetFontData however
134  * returns the whole file, so we need to check each font inside to get the
135  * proper font. */
136  path_len = _tcslen(vbuffer) + _tcslen(dbuffer) + 2; // '\' and terminating nul.
137  pathbuf = AllocaM(TCHAR, path_len);
138  _sntprintf(pathbuf, path_len, _T("%s\\%s"), vbuffer, dbuffer);
139 
140  /* Convert the path into something that FreeType understands. */
141  font_path = GetShortPath(pathbuf);
142 
143  index = 0;
144  do {
145  err = FT_New_Face(_library, font_path, index, face);
146  if (err != FT_Err_Ok) break;
147 
148  if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
149  /* Try english name if font name failed */
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;
152 
153  } while ((FT_Long)++index != (*face)->num_faces);
154 
155 
156 folder_error:
157 registry_no_font_found:
158  free(font_namep);
159  RegCloseKey(hKey);
160  return err;
161 }
162 
176 static const char *GetEnglishFontName(const ENUMLOGFONTEX *logfont)
177 {
178  static char font_name[MAX_PATH];
179  const char *ret_font_name = nullptr;
180  uint pos = 0;
181  HDC dc;
182  HGDIOBJ oldfont;
183  byte *buf;
184  DWORD dw;
185  uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset;
186 
187  HFONT font = CreateFontIndirect(&logfont->elfLogFont);
188  if (font == nullptr) goto err1;
189 
190  dc = GetDC(nullptr);
191  oldfont = SelectObject(dc, font);
192  dw = GetFontData(dc, 'eman', 0, nullptr, 0);
193  if (dw == GDI_ERROR) goto err2;
194 
195  buf = MallocT<byte>(dw);
196  dw = GetFontData(dc, 'eman', 0, buf, dw);
197  if (dw == GDI_ERROR) goto err3;
198 
199  format = buf[pos++] << 8;
200  format += buf[pos++];
201  assert(format == 0);
202  count = buf[pos++] << 8;
203  count += buf[pos++];
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++];
215  if (nameId != 1) {
216  pos += 4; // skip length and offset
217  continue;
218  }
219  length = buf[pos++] << 8;
220  length += buf[pos++];
221  offset = buf[pos++] << 8;
222  offset += buf[pos++];
223 
224  /* Don't buffer overflow */
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';
228 
229  if ((platformId == 1 && languageId == 0) || // Macintosh English
230  (platformId == 3 && languageId == 0x0409)) { // Microsoft English (US)
231  ret_font_name = font_name;
232  break;
233  }
234  }
235 
236 err3:
237  free(buf);
238 err2:
239  SelectObject(dc, oldfont);
240  ReleaseDC(nullptr, dc);
241  DeleteObject(font);
242 err1:
243  return ret_font_name == nullptr ? WIDE_TO_MB((const TCHAR*)logfont->elfFullName) : ret_font_name;
244 }
245 #endif /* WITH_FREETYPE */
246 
247 class FontList {
248 protected:
249  TCHAR **fonts;
250  uint items;
251  uint capacity;
252 
253 public:
254  FontList() : fonts(nullptr), items(0), capacity(0) { };
255 
256  ~FontList() {
257  if (this->fonts == nullptr) return;
258 
259  for (uint i = 0; i < this->items; i++) {
260  free(this->fonts[i]);
261  }
262 
263  free(this->fonts);
264  }
265 
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;
269  }
270 
271  if (this->items == this->capacity) {
272  this->capacity += 10;
273  this->fonts = ReallocT(this->fonts, this->capacity);
274  }
275 
276  this->fonts[this->items++] = _tcsdup(font);
277 
278  return true;
279  }
280 };
281 
282 struct EFCParam {
284  LOCALESIGNATURE locale;
285  MissingGlyphSearcher *callback;
286  FontList fonts;
287 };
288 
289 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
290 {
291  EFCParam *info = (EFCParam *)lParam;
292 
293  /* Skip duplicates */
294  if (!info->fonts.Add((const TCHAR*)logfont->elfFullName)) return 1;
295  /* Only use TrueType fonts */
296  if (!(type & TRUETYPE_FONTTYPE)) return 1;
297  /* Don't use SYMBOL fonts */
298  if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET) return 1;
299  /* Use monospaced fonts when asked for it. */
300  if (info->callback->Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH)) return 1;
301 
302  /* The font has to have at least one of the supported locales to be usable. */
303  if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) {
304  /* On win9x metric->ntmFontSig seems to contain garbage. */
305  FONTSIGNATURE fs;
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);
314  DeleteObject(font);
315  }
316  if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) return 1;
317  }
318 
319  char font_name[MAX_PATH];
320  convert_from_fs((const TCHAR *)logfont->elfFullName, font_name, lengthof(font_name));
321 
322 #ifdef WITH_FREETYPE
323  /* Add english name after font name */
324  const char *english_name = GetEnglishFontName(logfont);
325  strecpy(font_name + strlen(font_name) + 1, english_name, lastof(font_name));
326 
327  /* Check whether we can actually load the font. */
328  bool ft_init = _library != nullptr;
329  bool found = false;
330  FT_Face face;
331  /* Init FreeType if needed. */
332  if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) && GetFontByFaceName(font_name, &face) == FT_Err_Ok) {
333  FT_Done_Face(face);
334  found = true;
335  }
336  if (!ft_init) {
337  /* Uninit FreeType if we did the init. */
338  FT_Done_FreeType(_library);
339  _library = nullptr;
340  }
341 
342  if (!found) return 1;
343 #else
344  const char *english_name = font_name;
345 #endif /* WITH_FREETYPE */
346 
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);
352  return 0; // stop enumerating
353 }
354 
355 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
356 {
357  DEBUG(freetype, 1, "Trying fallback fonts");
358  EFCParam langInfo;
359  if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPTSTR)&langInfo.locale, sizeof(langInfo.locale) / sizeof(TCHAR)) == 0) {
360  /* Invalid langid or some other mysterious error, can't determine fallback font. */
361  DEBUG(freetype, 1, "Can't get locale info for fallback font (langid=0x%x)", winlangid);
362  return false;
363  }
364  langInfo.settings = settings;
365  langInfo.callback = callback;
366 
367  LOGFONT font;
368  /* Enumerate all fonts. */
369  font.lfCharSet = DEFAULT_CHARSET;
370  font.lfFaceName[0] = '\0';
371  font.lfPitchAndFamily = 0;
372 
373  HDC dc = GetDC(nullptr);
374  int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
375  ReleaseDC(nullptr, dc);
376  return ret == 0;
377 }
378 
379 #elif defined(__APPLE__) /* end ifdef Win32 */
380 /* ========================================================================================
381  * OSX support
382  * ======================================================================================== */
383 
384 #include "os/macosx/macos.h"
385 
386 #include "safeguards.h"
387 
388 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
389 {
390  FT_Error err = FT_Err_Cannot_Open_Resource;
391 
392  /* Get font reference from name. */
393  UInt8 file_path[PATH_MAX];
394  OSStatus os_err = -1;
395  CFAutoRelease<CFStringRef> name(CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8));
396 
397 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
398  if (MacOSVersionIsAtLeast(10, 6, 0)) {
399  /* Simply creating the font using CTFontCreateWithNameAndSize will *always* return
400  * something, no matter the name. As such, we can't use it to check for existence.
401  * We instead query the list of all font descriptors that match the given name which
402  * does not do this stupid name fallback. */
403  CFAutoRelease<CTFontDescriptorRef> name_desc(CTFontDescriptorCreateWithNameAndSize(name.get(), 0.0));
404  CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, (const void **)&kCTFontNameAttribute, 1, &kCFTypeSetCallBacks));
405  CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(name_desc.get(), mandatory_attribs.get()));
406 
407  /* Loop over all matches until we can get a path for one of them. */
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));
410  CFAutoRelease<CFURLRef> fontURL((CFURLRef)CTFontCopyAttribute(font.get(), kCTFontURLAttribute));
411  if (CFURLGetFileSystemRepresentation(fontURL.get(), true, file_path, lengthof(file_path))) os_err = noErr;
412  }
413  } else
414 #endif
415  {
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;
419 
420  /* Get a file system reference for the font. */
421  FSRef ref;
422 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
423  if (MacOSVersionIsAtLeast(10, 5, 0)) {
424  os_err = ATSFontGetFileReference(font, &ref);
425  } else
426 #endif
427  {
428 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !defined(__LP64__)
429  /* This type was introduced with the 10.5 SDK. */
430 #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
431 #define ATSFSSpec FSSpec
432 #endif
433  FSSpec spec;
434  os_err = ATSFontGetFileSpecification(font, (ATSFSSpec *)&spec);
435  if (os_err == noErr) os_err = FSpMakeFSRef(&spec, &ref);
436 #endif
437  }
438 
439  /* Get unix path for file. */
440  if (os_err == noErr) os_err = FSRefMakePath(&ref, file_path, sizeof(file_path));
441 #endif
442  }
443 
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);
447  }
448 
449  return err;
450 }
451 
452 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
453 {
454  bool result = false;
455 
456 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
457  if (MacOSVersionIsAtLeast(10, 5, 0)) {
458  /* Determine fallback font using CoreText. This uses the language isocode
459  * to find a suitable font. CoreText is available from 10.5 onwards. */
460  char lang[16];
461  if (strcmp(language_isocode, "zh_TW") == 0) {
462  /* Traditional Chinese */
463  strecpy(lang, "zh-Hant", lastof(lang));
464  } else if (strcmp(language_isocode, "zh_CN") == 0) {
465  /* Simplified Chinese */
466  strecpy(lang, "zh-Hans", lastof(lang));
467  } else {
468  /* Just copy the first part of the isocode. */
469  strecpy(lang, language_isocode, lastof(lang));
470  char *sep = strchr(lang, '_');
471  if (sep != nullptr) *sep = '\0';
472  }
473 
474  /* Create a font descriptor matching the wanted language and latin (english) glyphs.
475  * Can't use CFAutoRelease here for everything due to the way the dictionary has to be created. */
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));
481  CFAutoRelease<CTFontDescriptorRef> lang_desc(CTFontDescriptorCreateWithAttributes(lang_attribs.get()));
482  CFRelease(lang_arr);
483  CFRelease(lang_codes[0]);
484 
485  /* Get array of all font descriptors for the wanted language. */
486  CFAutoRelease<CFSetRef> mandatory_attribs(CFSetCreate(kCFAllocatorDefault, (const void **)&kCTFontLanguagesAttribute, 1, &kCFTypeSetCallBacks));
487  CFAutoRelease<CFArrayRef> descs(CTFontDescriptorCreateMatchingFontDescriptors(lang_desc.get(), mandatory_attribs.get()));
488 
489  for (CFIndex i = 0; descs.get() != nullptr && i < CFArrayGetCount(descs.get()); i++) {
490  CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs.get(), i);
491 
492  /* Get font traits. */
493  CFAutoRelease<CFDictionaryRef> traits((CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute));
494  CTFontSymbolicTraits symbolic_traits;
495  CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
496 
497  /* Skip symbol fonts and vertical fonts. */
498  if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue;
499  /* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
500  if (symbolic_traits & kCTFontBoldTrait) continue;
501  /* Select monospaced fonts if asked for. */
502  if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
503 
504  /* Get font name. */
505  char name[128];
506  CFAutoRelease<CFStringRef> font_name((CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute));
507  CFStringGetCString(font_name.get(), name, lengthof(name), kCFStringEncodingUTF8);
508 
509  /* There are some special fonts starting with an '.' and the last
510  * resort font that aren't usable. Skip them. */
511  if (name[0] == '.' || strncmp(name, "LastResort", 10) == 0) continue;
512 
513  /* Save result. */
514  callback->SetFontNames(settings, name);
515  if (!callback->FindMissingGlyphs(nullptr)) {
516  DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name);
517  result = true;
518  break;
519  }
520  }
521  } else
522 #endif
523  {
524 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6)
525  /* Create a font iterator and iterate over all fonts that
526  * are available to the application. */
527  ATSFontIterator itr;
528  ATSFontRef font;
529  ATSFontIteratorCreate(kATSFontContextLocal, nullptr, nullptr, kATSOptionFlagsDefaultScope, &itr);
530  while (!result && ATSFontIteratorNext(itr, &font) == noErr) {
531  /* Get font name. */
532  char name[128];
533  CFStringRef font_name;
534  ATSFontGetName(font, kATSOptionFlagsDefault, &font_name);
535  CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
536 
537  bool monospace = IsMonospaceFont(font_name);
538  CFRelease(font_name);
539 
540  /* Select monospaced fonts if asked for. */
541  if (monospace != callback->Monospace()) continue;
542 
543  /* We only want the base font and not bold or italic variants. */
544  if (strstr(name, "Italic") != nullptr || strstr(name, "Bold")) continue;
545 
546  /* Skip some inappropriate or ugly looking fonts that have better alternatives. */
547  if (name[0] == '.' || strncmp(name, "Apple Symbols", 13) == 0 || strncmp(name, "LastResort", 10) == 0) continue;
548 
549  /* Save result. */
550  callback->SetFontNames(settings, name);
551  if (!callback->FindMissingGlyphs(nullptr)) {
552  DEBUG(freetype, 2, "ATS-Font for %s: %s", language_isocode, name);
553  result = true;
554  break;
555  }
556  }
557  ATSFontIteratorRelease(&itr);
558 #endif
559  }
560 
561  if (!result) {
562  /* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
563  * supports. If we didn't find any other font, just try it, maybe we get lucky. */
564  callback->SetFontNames(settings, "Arial Unicode MS");
565  result = !callback->FindMissingGlyphs(nullptr);
566  }
567 
568  callback->FindMissingGlyphs(nullptr);
569  return result;
570 }
571 
572 #elif defined(WITH_FONTCONFIG) /* end ifdef __APPLE__ */
573 
574 #include <fontconfig/fontconfig.h>
575 
576 #include "safeguards.h"
577 
578 /* ========================================================================================
579  * FontConfig (unix) support
580  * ======================================================================================== */
581 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
582 {
583  FT_Error err = FT_Err_Cannot_Open_Resource;
584 
585  if (!FcInit()) {
586  ShowInfoF("Unable to load font configuration");
587  } else {
588  FcPattern *match;
589  FcPattern *pat;
590  FcFontSet *fs;
591  FcResult result;
592  char *font_style;
593  char *font_family;
594 
595  /* Split & strip the font's style */
596  font_family = stredup(font_name);
597  font_style = strchr(font_family, ',');
598  if (font_style != nullptr) {
599  font_style[0] = '\0';
600  font_style++;
601  while (*font_style == ' ' || *font_style == '\t') font_style++;
602  }
603 
604  /* Resolve the name and populate the information structure */
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);
611 
612  if (fs != nullptr && match != nullptr) {
613  int i;
614  FcChar8 *family;
615  FcChar8 *style;
616  FcChar8 *file;
617  FcFontSetAdd(fs, match);
618 
619  for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
620  /* Try the new filename */
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) {
624 
625  /* The correct style? */
626  if (font_style != nullptr && strcasecmp(font_style, (char*)style) != 0) continue;
627 
628  /* Font config takes the best shot, which, if the family name is spelled
629  * wrongly a 'random' font, so check whether the family name is the
630  * same as the supplied name */
631  if (strcasecmp(font_family, (char*)family) == 0) {
632  err = FT_New_Face(_library, (char *)file, 0, face);
633  }
634  }
635  }
636  }
637 
638  free(font_family);
639  FcPatternDestroy(pat);
640  FcFontSetDestroy(fs);
641  FcFini();
642  }
643 
644  return err;
645 }
646 
647 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
648 {
649  if (!FcInit()) return false;
650 
651  bool ret = false;
652 
653  /* Fontconfig doesn't handle full language isocodes, only the part
654  * before the _ of e.g. en_GB is used, so "remove" everything after
655  * the _. */
656  char lang[16];
657  seprintf(lang, lastof(lang), ":lang=%s", language_isocode);
658  char *split = strchr(lang, '_');
659  if (split != nullptr) *split = '\0';
660 
661  /* First create a pattern to match the wanted language. */
662  FcPattern *pat = FcNameParse((FcChar8*)lang);
663  /* We only want to know the filename. */
664  FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT, nullptr);
665  /* Get the list of filenames matching the wanted language. */
666  FcFontSet *fs = FcFontList(nullptr, pat, os);
667 
668  /* We don't need these anymore. */
669  FcObjectSetDestroy(os);
670  FcPatternDestroy(pat);
671 
672  if (fs != nullptr) {
673  int best_weight = -1;
674  const char *best_font = nullptr;
675 
676  for (int i = 0; i < fs->nfont; i++) {
677  FcPattern *font = fs->fonts[i];
678 
679  FcChar8 *file = nullptr;
680  FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
681  if (res != FcResultMatch || file == nullptr) {
682  continue;
683  }
684 
685  /* Get a font with the right spacing .*/
686  int value = 0;
687  FcPatternGetInteger(font, FC_SPACING, 0, &value);
688  if (callback->Monospace() != (value == FC_MONO) && value != FC_DUAL) continue;
689 
690  /* Do not use those that explicitly say they're slanted. */
691  FcPatternGetInteger(font, FC_SLANT, 0, &value);
692  if (value != 0) continue;
693 
694  /* We want the fatter font as they look better at small sizes. */
695  FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
696  if (value <= best_weight) continue;
697 
698  callback->SetFontNames(settings, (const char*)file);
699 
700  bool missing = callback->FindMissingGlyphs(nullptr);
701  DEBUG(freetype, 1, "Font \"%s\" misses%s glyphs", file, missing ? "" : " no");
702 
703  if (!missing) {
704  best_weight = value;
705  best_font = (const char *)file;
706  }
707  }
708 
709  if (best_font != nullptr) {
710  ret = true;
711  callback->SetFontNames(settings, best_font);
712  InitFreeType(callback->Monospace());
713  }
714 
715  /* Clean up the list of filenames. */
716  FcFontSetDestroy(fs);
717  }
718 
719  FcFini();
720  return ret;
721 }
722 
723 #else /* without WITH_FONTCONFIG */
724 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;}
725 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) { return false; }
726 #endif /* WITH_FONTCONFIG */
727 
728 #endif /* WITH_FREETYPE */
Functions related to OTTD&#39;s strings.
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:407
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&#39;t have this f...
Definition: win32.cpp:653
fluid_settings_t * settings
FluidSynth settings handle.
Definition: fluidsynth.cpp:20
static bool MacOSVersionIsAtLeast(long major, long minor, long bugfix)
Check if we are at least running on the specified version of Mac OS.
Definition: macos.h:25
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.
Definition: depend.cpp:48
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
Definition: alloc_func.hpp:132
char * convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen)
Convert to OpenTTD&#39;s encoding from that of the environment in UNICODE.
Definition: win32.cpp:591
Settings for the freetype fonts.
Definition: fontcache.h:224
void InitFreeType(bool monospace)
(Re)initialize the freetype related things, i.e.
Definition: fontcache.cpp:1034
void CDECL ShowInfoF(const char *str,...)
Shows some information on the console/a popup box depending on the OS.
Definition: openttd.cpp:134
Functions related to low-level strings.
Functions related to the allocation of memory.
A searcher for missing glyphs.
Definition: strings_func.h:244
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.
Definition: string.cpp:136
static T * ReallocT(T *t_ptr, size_t num_elements)
Simplified reallocation function that allocates the specified number of elements of the given type...
Definition: alloc_func.hpp:111
bool FindMissingGlyphs(const char **str)
Check whether there are glyphs missing in the current language.
Definition: strings.cpp:2008
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:40
static T min(const T a, const T b)
Returns the minimum of two values.
Definition: math_func.hpp:40
const TCHAR * OTTD2FS(const char *name, bool console_cp)
Convert from OpenTTD&#39;s encoding to that of the local environment.
Definition: win32.cpp:576
Integer math functions.
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&#39;t contain all characters we need...
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:35
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.
Definition: macos.h:52
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
Definition: depend.cpp:66
#define PATH_MAX
The maximum length of paths, if we don&#39;t know it.
Definition: depend.cpp:136
Functions related to MacOS support.
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:129
declarations of functions for MS windows systems