OpenTTD
fontdetection.cpp
Go to the documentation of this file.
1 /* $Id: fontdetection.cpp 26709 2014-07-30 20:19:29Z planetmaker $ */
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 {
281  FreeTypeSettings *settings;
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  CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8);
386  ATSFontRef font = ATSFontFindFromName(name, kATSOptionFlagsDefault);
387  CFRelease(name);
388  if (font == kInvalidFont) return err;
389 
390  /* Get a file system reference for the font. */
391  FSRef ref;
392  OSStatus os_err = -1;
393 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
394  if (MacOSVersionIsAtLeast(10, 5, 0)) {
395  os_err = ATSFontGetFileReference(font, &ref);
396  } else
397 #endif
398  {
399 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !defined(__LP64__)
400  /* This type was introduced with the 10.5 SDK. */
401 #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
402  #define ATSFSSpec FSSpec
403 #endif
404  FSSpec spec;
405  os_err = ATSFontGetFileSpecification(font, (ATSFSSpec *)&spec);
406  if (os_err == noErr) os_err = FSpMakeFSRef(&spec, &ref);
407 #endif
408  }
409 
410  if (os_err == noErr) {
411  /* Get unix path for file. */
412  UInt8 file_path[PATH_MAX];
413  if (FSRefMakePath(&ref, file_path, sizeof(file_path)) == noErr) {
414  DEBUG(freetype, 3, "Font path for %s: %s", font_name, file_path);
415  err = FT_New_Face(_library, (const char *)file_path, 0, face);
416  }
417  }
418 
419  return err;
420 }
421 
422 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
423 {
424  bool result = false;
425 
426 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
427  if (MacOSVersionIsAtLeast(10, 5, 0)) {
428  /* Determine fallback font using CoreText. This uses the language isocode
429  * to find a suitable font. CoreText is available from 10.5 onwards. */
430  char lang[16];
431  if (strcmp(language_isocode, "zh_TW") == 0) {
432  /* Traditional Chinese */
433  strecpy(lang, "zh-Hant", lastof(lang));
434  } else if (strcmp(language_isocode, "zh_CN") == 0) {
435  /* Simplified Chinese */
436  strecpy(lang, "zh-Hans", lastof(lang));
437  } else {
438  /* Just copy the first part of the isocode. */
439  strecpy(lang, language_isocode, lastof(lang));
440  char *sep = strchr(lang, '_');
441  if (sep != NULL) *sep = '\0';
442  }
443 
444  /* Create a font descriptor matching the wanted language and latin (english) glyphs. */
445  CFStringRef lang_codes[2];
446  lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8);
447  lang_codes[1] = CFSTR("en");
448  CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks);
449  CFDictionaryRef lang_attribs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&kCTFontLanguagesAttribute, (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
450  CTFontDescriptorRef lang_desc = CTFontDescriptorCreateWithAttributes(lang_attribs);
451  CFRelease(lang_arr);
452  CFRelease(lang_attribs);
453  CFRelease(lang_codes[0]);
454 
455  /* Get array of all font descriptors for the wanted language. */
456  CFSetRef mandatory_attribs = CFSetCreate(kCFAllocatorDefault, (const void **)&kCTFontLanguagesAttribute, 1, &kCFTypeSetCallBacks);
457  CFArrayRef descs = CTFontDescriptorCreateMatchingFontDescriptors(lang_desc, mandatory_attribs);
458  CFRelease(mandatory_attribs);
459  CFRelease(lang_desc);
460 
461  for (CFIndex i = 0; descs != NULL && i < CFArrayGetCount(descs); i++) {
462  CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs, i);
463 
464  /* Get font traits. */
465  CFDictionaryRef traits = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute);
466  CTFontSymbolicTraits symbolic_traits;
467  CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits, kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
468  CFRelease(traits);
469 
470  /* Skip symbol fonts and vertical fonts. */
471  if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue;
472  /* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
473  if (symbolic_traits & kCTFontBoldTrait) continue;
474  /* Select monospaced fonts if asked for. */
475  if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
476 
477  /* Get font name. */
478  char name[128];
479  CFStringRef font_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute);
480  CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
481  CFRelease(font_name);
482 
483  /* There are some special fonts starting with an '.' and the last
484  * resort font that aren't usable. Skip them. */
485  if (name[0] == '.' || strncmp(name, "LastResort", 10) == 0) continue;
486 
487  /* Save result. */
488  callback->SetFontNames(settings, name);
489  if (!callback->FindMissingGlyphs(NULL)) {
490  DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name);
491  result = true;
492  break;
493  }
494  }
495  if (descs != NULL) CFRelease(descs);
496  } else
497 #endif
498  {
499  /* Create a font iterator and iterate over all fonts that
500  * are available to the application. */
501  ATSFontIterator itr;
502  ATSFontRef font;
503  ATSFontIteratorCreate(kATSFontContextLocal, NULL, NULL, kATSOptionFlagsDefaultScope, &itr);
504  while (!result && ATSFontIteratorNext(itr, &font) == noErr) {
505  /* Get font name. */
506  char name[128];
507  CFStringRef font_name;
508  ATSFontGetName(font, kATSOptionFlagsDefault, &font_name);
509  CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
510 
511  bool monospace = IsMonospaceFont(font_name);
512  CFRelease(font_name);
513 
514  /* Select monospaced fonts if asked for. */
515  if (monospace != callback->Monospace()) continue;
516 
517  /* We only want the base font and not bold or italic variants. */
518  if (strstr(name, "Italic") != NULL || strstr(name, "Bold")) continue;
519 
520  /* Skip some inappropriate or ugly looking fonts that have better alternatives. */
521  if (name[0] == '.' || strncmp(name, "Apple Symbols", 13) == 0 || strncmp(name, "LastResort", 10) == 0) continue;
522 
523  /* Save result. */
524  callback->SetFontNames(settings, name);
525  if (!callback->FindMissingGlyphs(NULL)) {
526  DEBUG(freetype, 2, "ATS-Font for %s: %s", language_isocode, name);
527  result = true;
528  break;
529  }
530  }
531  ATSFontIteratorRelease(&itr);
532  }
533 
534  if (!result) {
535  /* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
536  * supports. If we didn't find any other font, just try it, maybe we get lucky. */
537  callback->SetFontNames(settings, "Arial Unicode MS");
538  result = !callback->FindMissingGlyphs(NULL);
539  }
540 
541  callback->FindMissingGlyphs(NULL);
542  return result;
543 }
544 
545 #elif defined(WITH_FONTCONFIG) /* end ifdef __APPLE__ */
546 
547 #include <fontconfig/fontconfig.h>
548 
549 #include "safeguards.h"
550 
551 /* ========================================================================================
552  * FontConfig (unix) support
553  * ======================================================================================== */
554 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
555 {
556  FT_Error err = FT_Err_Cannot_Open_Resource;
557 
558  if (!FcInit()) {
559  ShowInfoF("Unable to load font configuration");
560  } else {
561  FcPattern *match;
562  FcPattern *pat;
563  FcFontSet *fs;
564  FcResult result;
565  char *font_style;
566  char *font_family;
567 
568  /* Split & strip the font's style */
569  font_family = stredup(font_name);
570  font_style = strchr(font_family, ',');
571  if (font_style != NULL) {
572  font_style[0] = '\0';
573  font_style++;
574  while (*font_style == ' ' || *font_style == '\t') font_style++;
575  }
576 
577  /* Resolve the name and populate the information structure */
578  pat = FcNameParse((FcChar8*)font_family);
579  if (font_style != NULL) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
580  FcConfigSubstitute(0, pat, FcMatchPattern);
581  FcDefaultSubstitute(pat);
582  fs = FcFontSetCreate();
583  match = FcFontMatch(0, pat, &result);
584 
585  if (fs != NULL && match != NULL) {
586  int i;
587  FcChar8 *family;
588  FcChar8 *style;
589  FcChar8 *file;
590  FcFontSetAdd(fs, match);
591 
592  for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
593  /* Try the new filename */
594  if (FcPatternGetString(fs->fonts[i], FC_FILE, 0, &file) == FcResultMatch &&
595  FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
596  FcPatternGetString(fs->fonts[i], FC_STYLE, 0, &style) == FcResultMatch) {
597 
598  /* The correct style? */
599  if (font_style != NULL && strcasecmp(font_style, (char*)style) != 0) continue;
600 
601  /* Font config takes the best shot, which, if the family name is spelled
602  * wrongly a 'random' font, so check whether the family name is the
603  * same as the supplied name */
604  if (strcasecmp(font_family, (char*)family) == 0) {
605  err = FT_New_Face(_library, (char *)file, 0, face);
606  }
607  }
608  }
609  }
610 
611  free(font_family);
612  FcPatternDestroy(pat);
613  FcFontSetDestroy(fs);
614  FcFini();
615  }
616 
617  return err;
618 }
619 
620 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
621 {
622  if (!FcInit()) return false;
623 
624  bool ret = false;
625 
626  /* Fontconfig doesn't handle full language isocodes, only the part
627  * before the _ of e.g. en_GB is used, so "remove" everything after
628  * the _. */
629  char lang[16];
630  seprintf(lang, lastof(lang), ":lang=%s", language_isocode);
631  char *split = strchr(lang, '_');
632  if (split != NULL) *split = '\0';
633 
634  /* First create a pattern to match the wanted language. */
635  FcPattern *pat = FcNameParse((FcChar8*)lang);
636  /* We only want to know the filename. */
637  FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT, NULL);
638  /* Get the list of filenames matching the wanted language. */
639  FcFontSet *fs = FcFontList(NULL, pat, os);
640 
641  /* We don't need these anymore. */
642  FcObjectSetDestroy(os);
643  FcPatternDestroy(pat);
644 
645  if (fs != NULL) {
646  int best_weight = -1;
647  const char *best_font = NULL;
648 
649  for (int i = 0; i < fs->nfont; i++) {
650  FcPattern *font = fs->fonts[i];
651 
652  FcChar8 *file = NULL;
653  FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
654  if (res != FcResultMatch || file == NULL) {
655  continue;
656  }
657 
658  /* Get a font with the right spacing .*/
659  int value = 0;
660  FcPatternGetInteger(font, FC_SPACING, 0, &value);
661  if (callback->Monospace() != (value == FC_MONO) && value != FC_DUAL) continue;
662 
663  /* Do not use those that explicitly say they're slanted. */
664  FcPatternGetInteger(font, FC_SLANT, 0, &value);
665  if (value != 0) continue;
666 
667  /* We want the fatter font as they look better at small sizes. */
668  FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
669  if (value <= best_weight) continue;
670 
671  callback->SetFontNames(settings, (const char*)file);
672 
673  bool missing = callback->FindMissingGlyphs(NULL);
674  DEBUG(freetype, 1, "Font \"%s\" misses%s glyphs", file, missing ? "" : " no");
675 
676  if (!missing) {
677  best_weight = value;
678  best_font = (const char *)file;
679  }
680  }
681 
682  if (best_font != NULL) {
683  ret = true;
684  callback->SetFontNames(settings, best_font);
685  InitFreeType(callback->Monospace());
686  }
687 
688  /* Clean up the list of filenames. */
689  FcFontSetDestroy(fs);
690  }
691 
692  FcFini();
693  return ret;
694 }
695 
696 #else /* without WITH_FONTCONFIG */
697 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;}
698 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) { return false; }
699 #endif /* WITH_FONTCONFIG */
700 
701 #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:398
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:708
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:646
Settings for the freetype fonts.
Definition: fontcache.h:204
void InitFreeType(bool monospace)
(Re)initialize the freetype related things, i.e.
Definition: fontcache.cpp:643
void CDECL ShowInfoF(const char *str,...)
Shows some information on the console/a popup box depending on the OS.
Definition: openttd.cpp:130
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:126
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:2003
#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:631
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:39
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