# fontdetection.cpp

Go to the documentation of this file.

    00001 /* $Id: fontdetection.cpp 25676 2013-08-05 20:36:55Z michi_cc $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * 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.
    00006  * 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.
    00007  * 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/>.
    00008  */
    00009
    00012 #ifdef WITH_FREETYPE
    00013
    00014 #include "stdafx.h"
    00015 #include "debug.h"
    00016 #include "fontdetection.h"
    00017 #include "string_func.h"
    00018 #include "strings_func.h"
    00019
    00020 extern FT_Library _library;
    00021
    00027 /* ========================================================================================
    00028  * Windows support
    00029  * ======================================================================================== */
    00030
    00031 #ifdef WIN32
    00032 #include "core/alloc_func.hpp"
    00033 #include "core/math_func.hpp"
    00034 #include <windows.h>
    00035 #include <shlobj.h> /* SHGetFolderPath */
    00036 #include "os/windows/win32.h"
    00037
    00048 const char *GetShortPath(const TCHAR *long_path)
    00049 {
    00050   static char short_path[MAX_PATH];
    00051 #ifdef UNICODE
    00052   WCHAR short_path_w[MAX_PATH];
    00053   GetShortPathName(long_path, short_path_w, lengthof(short_path_w));
    00054   WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path, lengthof(short_path), NULL, NULL);
    00055 #else
    00056   /* Technically not needed, but do it for consistency. */
    00057   GetShortPathName(long_path, short_path, lengthof(short_path));
    00058 #endif
    00059   return short_path;
    00060 }
    00061
    00062 /* Get the font file to be loaded into Freetype by looping the registry
    00063  * location where windows lists all installed fonts. Not very nice, will
    00064  * surely break if the registry path changes, but it works. Much better
    00065  * solution would be to use CreateFont, and extract the font data from it
    00066  * by GetFontData. The problem with this is that the font file needs to be
    00067  * kept in memory then until the font is no longer needed. This could mean
    00068  * an additional memory usage of 30MB (just for fonts!) when using an eastern
    00069  * font for all font sizes */
    00070 #define FONT_DIR_NT "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts"
    00071 #define FONT_DIR_9X "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Fonts"
    00072 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
    00073 {
    00074   FT_Error err = FT_Err_Cannot_Open_Resource;
    00075   HKEY hKey;
    00076   LONG ret;
    00077   TCHAR vbuffer[MAX_PATH], dbuffer[256];
    00078   TCHAR *pathbuf;
    00079   const char *font_path;
    00080   uint index;
    00081   size_t path_len;
    00082
    00083   /* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
    00084    * "Windows NT" key, on Windows 9x in the Windows key. To save us having
    00085    * to retrieve the windows version, we'll just query both */
    00086   ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
    00087   if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);
    00088
    00089   if (ret != ERROR_SUCCESS) {
    00090     DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
    00091     return err;
    00092   }
    00093
    00094   /* Convert font name to file system encoding. */
    00095   TCHAR *font_namep = _tcsdup(OTTD2FS(font_name));
    00096
    00097   for (index = 0;; index++) {
    00098     TCHAR *s;
    00099     DWORD vbuflen = lengthof(vbuffer);
    00100     DWORD dbuflen = lengthof(dbuffer);
    00101
    00102     ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, NULL, NULL, (byte*)dbuffer, &dbuflen);
    00103     if (ret != ERROR_SUCCESS) goto registry_no_font_found;
    00104
    00105     /* The font names in the registry are of the following 3 forms:
    00106      * - ADMUI3.fon
    00107      * - Book Antiqua Bold (TrueType)
    00108      * - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
    00109      * We will strip the font-type '()' if any and work with the font name
    00110      * itself, which must match exactly; if...
    00111      * TTC files, font files which contain more than one font are separated
    00112      * by '&'. Our best bet will be to do substr match for the fontname
    00113      * and then let FreeType figure out which index to load */
    00114     s = _tcschr(vbuffer, _T('('));
    00115     if (s != NULL) s[-1] = '\0';
    00116
    00117     if (_tcschr(vbuffer, _T('&')) == NULL) {
    00118       if (_tcsicmp(vbuffer, font_namep) == 0) break;
    00119     } else {
    00120       if (_tcsstr(vbuffer, font_namep) != NULL) break;
    00121     }
    00122   }
    00123
    00124   if (!SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, vbuffer))) {
    00125     DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory");
    00126     goto folder_error;
    00127   }
    00128
    00129   /* Some fonts are contained in .ttc files, TrueType Collection fonts. These
    00130    * contain multiple fonts inside this single file. GetFontData however
    00131    * returns the whole file, so we need to check each font inside to get the
    00132    * proper font. */
    00133   path_len = _tcslen(vbuffer) + _tcslen(dbuffer) + 2; // '\' and terminating nul.
    00134   pathbuf = AllocaM(TCHAR, path_len);
    00135   _sntprintf(pathbuf, path_len, _T("%s\\%s"), vbuffer, dbuffer);
    00136
    00137   /* Convert the path into something that FreeType understands. */
    00138   font_path = GetShortPath(pathbuf);
    00139
    00140   index = 0;
    00141   do {
    00142     err = FT_New_Face(_library, font_path, index, face);
    00143     if (err != FT_Err_Ok) break;
    00144
    00145     if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
    00146     /* Try english name if font name failed */
    00147     if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
    00148     err = FT_Err_Cannot_Open_Resource;
    00149
    00150   } while ((FT_Long)++index != (*face)->num_faces);
    00151
    00152
    00153 folder_error:
    00154 registry_no_font_found:
    00155   free(font_namep);
    00156   RegCloseKey(hKey);
    00157   return err;
    00158 }
    00159
    00173 static const char *GetEnglishFontName(const ENUMLOGFONTEX *logfont)
    00174 {
    00175   static char font_name[MAX_PATH];
    00176   const char *ret_font_name = NULL;
    00177   uint pos = 0;
    00178   HDC dc;
    00179   HGDIOBJ oldfont;
    00180   byte *buf;
    00181   DWORD dw;
    00182   uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset;
    00183
    00184   HFONT font = CreateFontIndirect(&logfont->elfLogFont);
    00185   if (font == NULL) goto err1;
    00186
    00187   dc = GetDC(NULL);
    00188   oldfont = SelectObject(dc, font);
    00189   dw = GetFontData(dc, 'eman', 0, NULL, 0);
    00190   if (dw == GDI_ERROR) goto err2;
    00191
    00192   buf = MallocT<byte>(dw);
    00193   dw = GetFontData(dc, 'eman', 0, buf, dw);
    00194   if (dw == GDI_ERROR) goto err3;
    00195
    00196   format = buf[pos++] << 8;
    00197   format += buf[pos++];
    00198   assert(format == 0);
    00199   count = buf[pos++] << 8;
    00200   count += buf[pos++];
    00201   stringOffset = buf[pos++] << 8;
    00202   stringOffset += buf[pos++];
    00203   for (uint i = 0; i < count; i++) {
    00204     platformId = buf[pos++] << 8;
    00205     platformId += buf[pos++];
    00206     encodingId = buf[pos++] << 8;
    00207     encodingId += buf[pos++];
    00208     languageId = buf[pos++] << 8;
    00209     languageId += buf[pos++];
    00210     nameId = buf[pos++] << 8;
    00211     nameId += buf[pos++];
    00212     if (nameId != 1) {
    00213       pos += 4; // skip length and offset
    00214       continue;
    00215     }
    00216     length = buf[pos++] << 8;
    00217     length += buf[pos++];
    00218     offset = buf[pos++] << 8;
    00219     offset += buf[pos++];
    00220
    00221     /* Don't buffer overflow */
    00222     length = min(length, MAX_PATH - 1);
    00223     for (uint j = 0; j < length; j++) font_name[j] = buf[stringOffset + offset + j];
    00224     font_name[length] = '\0';
    00225
    00226     if ((platformId == 1 && languageId == 0) ||      // Macintosh English
    00227         (platformId == 3 && languageId == 0x0409)) { // Microsoft English (US)
    00228       ret_font_name = font_name;
    00229       break;
    00230     }
    00231   }
    00232
    00233 err3:
    00234   free(buf);
    00235 err2:
    00236   SelectObject(dc, oldfont);
    00237   ReleaseDC(NULL, dc);
    00238   DeleteObject(font);
    00239 err1:
    00240   return ret_font_name == NULL ? WIDE_TO_MB((const TCHAR*)logfont->elfFullName) : ret_font_name;
    00241 }
    00242
    00243 class FontList {
    00244 protected:
    00245   TCHAR **fonts;
    00246   uint items;
    00247   uint capacity;
    00248
    00249 public:
    00250   FontList() : fonts(NULL), items(0), capacity(0) { };
    00251
    00252   ~FontList() {
    00253     if (this->fonts == NULL) return;
    00254
    00255     for (uint i = 0; i < this->items; i++) {
    00256       free(this->fonts[i]);
    00257     }
    00258
    00259     free(this->fonts);
    00260   }
    00261
    00262   bool Add(const TCHAR *font) {
    00263     for (uint i = 0; i < this->items; i++) {
    00264       if (_tcscmp(this->fonts[i], font) == 0) return false;
    00265     }
    00266
    00267     if (this->items == this->capacity) {
    00268       this->capacity += 10;
    00269       this->fonts = ReallocT(this->fonts, this->capacity);
    00270     }
    00271
    00272     this->fonts[this->items++] = _tcsdup(font);
    00273
    00274     return true;
    00275   }
    00276 };
    00277
    00278 struct EFCParam {
    00279   FreeTypeSettings *settings;
    00280   LOCALESIGNATURE  locale;
    00281   MissingGlyphSearcher *callback;
    00282   FontList fonts;
    00283 };
    00284
    00285 static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
    00286 {
    00287   EFCParam *info = (EFCParam *)lParam;
    00288
    00289   /* Skip duplicates */
    00290   if (!info->fonts.Add((const TCHAR*)logfont->elfFullName)) return 1;
    00291   /* Only use TrueType fonts */
    00292   if (!(type & TRUETYPE_FONTTYPE)) return 1;
    00293   /* Don't use SYMBOL fonts */
    00294   if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET) return 1;
    00295   /* Use monospaced fonts when asked for it. */
    00296   if (info->callback->Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH)) return 1;
    00297
    00298   /* The font has to have at least one of the supported locales to be usable. */
    00299   if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) {
    00300     /* On win9x metric->ntmFontSig seems to contain garbage. */
    00301     FONTSIGNATURE fs;
    00302     memset(&fs, 0, sizeof(fs));
    00303     HFONT font = CreateFontIndirect(&logfont->elfLogFont);
    00304     if (font != NULL) {
    00305       HDC dc = GetDC(NULL);
    00306       HGDIOBJ oldfont = SelectObject(dc, font);
    00307       GetTextCharsetInfo(dc, &fs, 0);
    00308       SelectObject(dc, oldfont);
    00309       ReleaseDC(NULL, dc);
    00310       DeleteObject(font);
    00311     }
    00312     if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) return 1;
    00313   }
    00314
    00315   char font_name[MAX_PATH];
    00316   convert_from_fs((const TCHAR *)logfont->elfFullName, font_name, lengthof(font_name));
    00317
    00318   /* Add english name after font name */
    00319   const char *english_name = GetEnglishFontName(logfont);
    00320   strecpy(font_name + strlen(font_name) + 1, english_name, lastof(font_name));
    00321
    00322   /* Check whether we can actually load the font. */
    00323   bool ft_init = _library != NULL;
    00324   bool found = false;
    00325   FT_Face face;
    00326   /* Init FreeType if needed. */
    00327   if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) && GetFontByFaceName(font_name, &face) == FT_Err_Ok) {
    00328     FT_Done_Face(face);
    00329     found = true;
    00330   }
    00331   if (!ft_init) {
    00332     /* Uninit FreeType if we did the init. */
    00333     FT_Done_FreeType(_library);
    00334     _library = NULL;
    00335   }
    00336
    00337   if (!found) return 1;
    00338
    00339   info->callback->SetFontNames(info->settings, font_name);
    00340   if (info->callback->FindMissingGlyphs(NULL)) return 1;
    00341   DEBUG(freetype, 1, "Fallback font: %s (%s)", font_name, english_name);
    00342   return 0; // stop enumerating
    00343 }
    00344
    00345 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
    00346 {
    00347   DEBUG(freetype, 1, "Trying fallback fonts");
    00348   EFCParam langInfo;
    00349   if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPTSTR)&langInfo.locale, sizeof(langInfo.locale) / sizeof(TCHAR)) == 0) {
    00350     /* Invalid langid or some other mysterious error, can't determine fallback font. */
    00351     DEBUG(freetype, 1, "Can't get locale info for fallback font (langid=0x%x)", winlangid);
    00352     return false;
    00353   }
    00354   langInfo.settings = settings;
    00355   langInfo.callback = callback;
    00356
    00357   LOGFONT font;
    00358   /* Enumerate all fonts. */
    00359   font.lfCharSet = DEFAULT_CHARSET;
    00360   font.lfFaceName[0] = '\0';
    00361   font.lfPitchAndFamily = 0;
    00362
    00363   HDC dc = GetDC(NULL);
    00364   int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0);
    00365   ReleaseDC(NULL, dc);
    00366   return ret == 0;
    00367 }
    00368
    00369 #elif defined(__APPLE__) /* end ifdef Win32 */
    00370 /* ========================================================================================
    00371  * OSX support
    00372  * ======================================================================================== */
    00373
    00374 #include "os/macosx/macos.h"
    00375
    00376 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
    00377 {
    00378   FT_Error err = FT_Err_Cannot_Open_Resource;
    00379
    00380   /* Get font reference from name. */
    00381   CFStringRef name = CFStringCreateWithCString(kCFAllocatorDefault, font_name, kCFStringEncodingUTF8);
    00382   ATSFontRef font = ATSFontFindFromName(name, kATSOptionFlagsDefault);
    00383   CFRelease(name);
    00384   if (font == kInvalidFont) return err;
    00385
    00386   /* Get a file system reference for the font. */
    00387   FSRef ref;
    00388   OSStatus os_err = -1;
    00389 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
    00390   if (MacOSVersionIsAtLeast(10, 5, 0)) {
    00391     os_err = ATSFontGetFileReference(font, &ref);
    00392   } else
    00393 #endif
    00394   {
    00395 #if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !__LP64__
    00396     /* This type was introduced with the 10.5 SDK. */
    00397 #if (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5)
    00398   #define ATSFSSpec FSSpec
    00399 #endif
    00400     FSSpec spec;
    00401     os_err = ATSFontGetFileSpecification(font, (ATSFSSpec *)&spec);
    00402     if (os_err == noErr) os_err = FSpMakeFSRef(&spec, &ref);
    00403 #endif
    00404   }
    00405
    00406   if (os_err == noErr) {
    00407     /* Get unix path for file. */
    00408     UInt8 file_path[PATH_MAX];
    00409     if (FSRefMakePath(&ref, file_path, sizeof(file_path)) == noErr) {
    00410       DEBUG(freetype, 3, "Font path for %s: %s", font_name, file_path);
    00411       err = FT_New_Face(_library, (const char *)file_path, 0, face);
    00412     }
    00413   }
    00414
    00415   return err;
    00416 }
    00417
    00418 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
    00419 {
    00420   bool result = false;
    00421
    00422 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
    00423   if (MacOSVersionIsAtLeast(10, 5, 0)) {
    00424     /* Determine fallback font using CoreText. This uses the language isocode
    00425      * to find a suitable font. CoreText is available from 10.5 onwards. */
    00426     char lang[16];
    00427     if (strcmp(language_isocode, "zh_TW") == 0) {
    00428       /* Traditional Chinese */
    00429       strecpy(lang, "zh-Hant", lastof(lang));
    00430     } else if (strcmp(language_isocode, "zh_CN") == 0) {
    00431       /* Simplified Chinese */
    00432       strecpy(lang, "zh-Hans", lastof(lang));
    00433     } else {
    00434       /* Just copy the first part of the isocode. */
    00435       strecpy(lang, language_isocode, lastof(lang));
    00436       char *sep = strchr(lang, '_');
    00437       if (sep != NULL) *sep = '\0';
    00438     }
    00439
    00440     /* Create a font descriptor matching the wanted language and latin (english) glyphs. */
    00441     CFStringRef lang_codes[2];
    00442     lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8);
    00443     lang_codes[1] = CFSTR("en");
    00444     CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks);
    00445     CFDictionaryRef lang_attribs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&kCTFontLanguagesAttribute, (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    00446     CTFontDescriptorRef lang_desc = CTFontDescriptorCreateWithAttributes(lang_attribs);
    00447     CFRelease(lang_arr);
    00448     CFRelease(lang_attribs);
    00449     CFRelease(lang_codes[0]);
    00450
    00451     /* Get array of all font descriptors for the wanted language. */
    00452     CFSetRef mandatory_attribs = CFSetCreate(kCFAllocatorDefault, (const void **)&kCTFontLanguagesAttribute, 1, &kCFTypeSetCallBacks);
    00453     CFArrayRef descs = CTFontDescriptorCreateMatchingFontDescriptors(lang_desc, mandatory_attribs);
    00454     CFRelease(mandatory_attribs);
    00455     CFRelease(lang_desc);
    00456
    00457     for (CFIndex i = 0; descs != NULL && i < CFArrayGetCount(descs); i++) {
    00458       CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs, i);
    00459
    00460       /* Get font traits. */
    00461       CFDictionaryRef traits = (CFDictionaryRef)CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute);
    00462       CTFontSymbolicTraits symbolic_traits;
    00463       CFNumberGetValue((CFNumberRef)CFDictionaryGetValue(traits, kCTFontSymbolicTrait), kCFNumberIntType, &symbolic_traits);
    00464       CFRelease(traits);
    00465
    00466       /* Skip symbol fonts and vertical fonts. */
    00467       if ((symbolic_traits & kCTFontClassMaskTrait) == (CTFontStylisticClass)kCTFontSymbolicClass || (symbolic_traits & kCTFontVerticalTrait)) continue;
    00468       /* Skip bold fonts (especially Arial Bold, which looks worse than regular Arial). */
    00469       if (symbolic_traits & kCTFontBoldTrait) continue;
    00470       /* Select monospaced fonts if asked for. */
    00471       if (((symbolic_traits & kCTFontMonoSpaceTrait) == kCTFontMonoSpaceTrait) != callback->Monospace()) continue;
    00472
    00473       /* Get font name. */
    00474       char name[128];
    00475       CFStringRef font_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute);
    00476       CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
    00477       CFRelease(font_name);
    00478
    00479       /* There are some special fonts starting with an '.' and the last
    00480        * resort font that aren't usable. Skip them. */
    00481       if (name[0] == '.' || strncmp(name, "LastResort", 10) == 0) continue;
    00482
    00483       /* Save result. */
    00484       callback->SetFontNames(settings, name);
    00485       if (!callback->FindMissingGlyphs(NULL)) {
    00486         DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name);
    00487         result = true;
    00488         break;
    00489       }
    00490     }
    00491     if (descs != NULL) CFRelease(descs);
    00492   } else
    00493 #endif
    00494   {
    00495     /* Create a font iterator and iterate over all fonts that
    00496      * are available to the application. */
    00497     ATSFontIterator itr;
    00498     ATSFontRef font;
    00499     ATSFontIteratorCreate(kATSFontContextLocal, NULL, NULL, kATSOptionFlagsDefaultScope, &itr);
    00500     while (!result && ATSFontIteratorNext(itr, &font) == noErr) {
    00501       /* Get font name. */
    00502       char name[128];
    00503       CFStringRef font_name;
    00504       ATSFontGetName(font, kATSOptionFlagsDefault, &font_name);
    00505       CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8);
    00506
    00507       bool monospace = IsMonospaceFont(font_name);
    00508       CFRelease(font_name);
    00509
    00510       /* Select monospaced fonts if asked for. */
    00511       if (monospace != callback->Monospace()) continue;
    00512
    00513       /* We only want the base font and not bold or italic variants. */
    00514       if (strstr(name, "Italic") != NULL || strstr(name, "Bold")) continue;
    00515
    00516       /* Skip some inappropriate or ugly looking fonts that have better alternatives. */
    00517       if (name[0] == '.' || strncmp(name, "Apple Symbols", 13) == 0 || strncmp(name, "LastResort", 10) == 0) continue;
    00518
    00519       /* Save result. */
    00520       callback->SetFontNames(settings, name);
    00521       if (!callback->FindMissingGlyphs(NULL)) {
    00522         DEBUG(freetype, 2, "ATS-Font for %s: %s", language_isocode, name);
    00523         result = true;
    00524         break;
    00525       }
    00526     }
    00527     ATSFontIteratorRelease(&itr);
    00528   }
    00529
    00530   if (!result) {
    00531     /* For some OS versions, the font 'Arial Unicode MS' does not report all languages it
    00532      * supports. If we didn't find any other font, just try it, maybe we get lucky. */
    00533     callback->SetFontNames(settings, "Arial Unicode MS");
    00534     result = !callback->FindMissingGlyphs(NULL);
    00535   }
    00536
    00537   callback->FindMissingGlyphs(NULL);
    00538   return result;
    00539 }
    00540
    00541 #elif defined(WITH_FONTCONFIG) /* end ifdef __APPLE__ */
    00542
    00543 #include <fontconfig/fontconfig.h>
    00544
    00545 /* ========================================================================================
    00546  * FontConfig (unix) support
    00547  * ======================================================================================== */
    00548 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
    00549 {
    00550   FT_Error err = FT_Err_Cannot_Open_Resource;
    00551
    00552   if (!FcInit()) {
    00553     ShowInfoF("Unable to load font configuration");
    00554   } else {
    00555     FcPattern *match;
    00556     FcPattern *pat;
    00557     FcFontSet *fs;
    00558     FcResult  result;
    00559     char *font_style;
    00560     char *font_family;
    00561
    00562     /* Split & strip the font's style */
    00563     font_family = strdup(font_name);
    00564     font_style = strchr(font_family, ',');
    00565     if (font_style != NULL) {
    00566       font_style[0] = '\0';
    00567       font_style++;
    00568       while (*font_style == ' ' || *font_style == '\t') font_style++;
    00569     }
    00570
    00571     /* Resolve the name and populate the information structure */
    00572     pat = FcNameParse((FcChar8*)font_family);
    00573     if (font_style != NULL) FcPatternAddString(pat, FC_STYLE, (FcChar8*)font_style);
    00574     FcConfigSubstitute(0, pat, FcMatchPattern);
    00575     FcDefaultSubstitute(pat);
    00576     fs = FcFontSetCreate();
    00577     match = FcFontMatch(0, pat, &result);
    00578
    00579     if (fs != NULL && match != NULL) {
    00580       int i;
    00581       FcChar8 *family;
    00582       FcChar8 *style;
    00583       FcChar8 *file;
    00584       FcFontSetAdd(fs, match);
    00585
    00586       for (i = 0; err != FT_Err_Ok && i < fs->nfont; i++) {
    00587         /* Try the new filename */
    00588         if (FcPatternGetString(fs->fonts[i], FC_FILE,   0, &file)   == FcResultMatch &&
    00589             FcPatternGetString(fs->fonts[i], FC_FAMILY, 0, &family) == FcResultMatch &&
    00590             FcPatternGetString(fs->fonts[i], FC_STYLE,  0, &style)  == FcResultMatch) {
    00591
    00592           /* The correct style? */
    00593           if (font_style != NULL && strcasecmp(font_style, (char*)style) != 0) continue;
    00594
    00595           /* Font config takes the best shot, which, if the family name is spelled
    00596            * wrongly a 'random' font, so check whether the family name is the
    00597            * same as the supplied name */
    00598           if (strcasecmp(font_family, (char*)family) == 0) {
    00599             err = FT_New_Face(_library, (char *)file, 0, face);
    00600           }
    00601         }
    00602       }
    00603     }
    00604
    00605     free(font_family);
    00606     FcPatternDestroy(pat);
    00607     FcFontSetDestroy(fs);
    00608     FcFini();
    00609   }
    00610
    00611   return err;
    00612 }
    00613
    00614 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback)
    00615 {
    00616   if (!FcInit()) return false;
    00617
    00618   bool ret = false;
    00619
    00620   /* Fontconfig doesn't handle full language isocodes, only the part
    00621    * before the _ of e.g. en_GB is used, so "remove" everything after
    00622    * the _. */
    00623   char lang[16];
    00624   seprintf(lang, lastof(lang), ":lang=%s", language_isocode);
    00625   char *split = strchr(lang, '_');
    00626   if (split != NULL) *split = '\0';
    00627
    00628   /* First create a pattern to match the wanted language. */
    00629   FcPattern *pat = FcNameParse((FcChar8*)lang);
    00630   /* We only want to know the filename. */
    00631   FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SPACING, FC_SLANT, FC_WEIGHT, NULL);
    00632   /* Get the list of filenames matching the wanted language. */
    00633   FcFontSet *fs = FcFontList(NULL, pat, os);
    00634
    00635   /* We don't need these anymore. */
    00636   FcObjectSetDestroy(os);
    00637   FcPatternDestroy(pat);
    00638
    00639   if (fs != NULL) {
    00640     int best_weight = -1;
    00641     const char *best_font = NULL;
    00642
    00643     for (int i = 0; i < fs->nfont; i++) {
    00644       FcPattern *font = fs->fonts[i];
    00645
    00646       FcChar8 *file = NULL;
    00647       FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
    00648       if (res != FcResultMatch || file == NULL) {
    00649         continue;
    00650       }
    00651
    00652       /* Get a font with the right spacing .*/
    00653       int value = 0;
    00654       FcPatternGetInteger(font, FC_SPACING, 0, &value);
    00655       if (callback->Monospace() != (value == FC_MONO) && value != FC_DUAL) continue;
    00656
    00657       /* Do not use those that explicitly say they're slanted. */
    00658       FcPatternGetInteger(font, FC_SLANT, 0, &value);
    00659       if (value != 0) continue;
    00660
    00661       /* We want the fatter font as they look better at small sizes. */
    00662       FcPatternGetInteger(font, FC_WEIGHT, 0, &value);
    00663       if (value <= best_weight) continue;
    00664
    00665       callback->SetFontNames(settings, (const char*)file);
    00666
    00667       bool missing = callback->FindMissingGlyphs(NULL);
    00668       DEBUG(freetype, 1, "Font \"%s\" misses%s glyphs", file, missing ? "" : " no");
    00669
    00670       if (!missing) {
    00671         best_weight = value;
    00672         best_font   = (const char *)file;
    00673       }
    00674     }
    00675
    00676     if (best_font != NULL) {
    00677       ret = true;
    00678       callback->SetFontNames(settings, best_font);
    00679       InitFreeType(callback->Monospace());
    00680     }
    00681
    00682     /* Clean up the list of filenames. */
    00683     FcFontSetDestroy(fs);
    00684   }
    00685
    00686   FcFini();
    00687   return ret;
    00688 }
    00689
    00690 #else /* without WITH_FONTCONFIG */
    00691 FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;}
    00692 bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) { return false; }
    00693 #endif /* WITH_FONTCONFIG */
    00694
    00695 #endif /* WITH_FREETYPE */