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