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