# HG changeset patch # User Michael Lutz # Date 1355593730 -3600 # Node ID 1593a72bcfaa5b7ffff58c6ed7866d43fb046083 # Parent e7954aa34d4780493e4e5131f915853d0477e19c -Codechange: [Win32] Create a list of all possible fallback fonts and use it to select one. diff --git a/src/fontcache.cpp b/src/fontcache.cpp --- a/src/fontcache.cpp +++ b/src/fontcache.cpp @@ -234,7 +234,7 @@ * @param logfont the font information to get the english name of. * @return the English name (if it could be found). */ -static const char *GetEnglishFontName(const ENUMLOGFONTEX *logfont) +static const char *GetEnglishFontName(const LOGFONT *logfont) { static char font_name[MAX_PATH]; const char *ret_font_name = NULL; @@ -245,7 +245,7 @@ DWORD dw; uint16 format, count, stringOffset, platformId, encodingId, languageId, nameId, length, offset; - HFONT font = CreateFontIndirect(&logfont->elfLogFont); + HFONT font = CreateFontIndirect(logfont); if (font == NULL) goto err1; dc = GetDC(NULL); @@ -301,67 +301,42 @@ ReleaseDC(NULL, dc); DeleteObject(font); err1: - return ret_font_name == NULL ? WIDE_TO_MB((const TCHAR*)logfont->elfFullName) : ret_font_name; + return ret_font_name == NULL ? WIDE_TO_MB((const TCHAR*)logfont->lfFaceName) : ret_font_name; } -class FontList { -protected: - TCHAR **fonts; - uint items; - uint capacity; - -public: - FontList() : fonts(NULL), items(0), capacity(0) { }; - - ~FontList() { - if (this->fonts == NULL) return; - - for (uint i = 0; i < this->items; i++) { - free(this->fonts[i]); - } +struct WindowsFont : FontInfo { + LOGFONT logfont; - free(this->fonts); - } - - bool Add(const TCHAR *font) { - for (uint i = 0; i < this->items; i++) { - if (_tcscmp(this->fonts[i], font) == 0) return false; - } - - if (this->items == this->capacity) { - this->capacity += 10; - this->fonts = ReallocT(this->fonts, this->capacity); - } - - this->fonts[this->items++] = _tcsdup(font); - - return true; - } + WindowsFont(const char *name, bool monospace, const LOGFONT &logfont) : FontInfo(name, monospace), logfont(logfont) {} }; +/** Parameters for #EnumFontCallback. */ struct EFCParam { - FreeTypeSettings *settings; - LOCALESIGNATURE locale; - MissingGlyphSearcher *callback; - FontList fonts; + LOCALESIGNATURE locale; + FontList *fonts; }; static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam) { EFCParam *info = (EFCParam *)lParam; - /* Skip duplicates */ - if (!info->fonts.Add((const TCHAR*)logfont->elfFullName)) return 1; - /* Only use TrueType fonts */ + /* Skip duplicates. */ + for (FontInfo * const *i = info->fonts->Begin(); i != info->fonts->End(); i++) { + if (_tcscmp(static_cast(*i)->logfont.lfFaceName, logfont->elfLogFont.lfFaceName) == 0) return 1; + } + + /* Only use TrueType fonts. */ if (!(type & TRUETYPE_FONTTYPE)) return 1; - /* Don't use SYMBOL fonts */ + /* Don't use SYMBOL fonts. */ if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET) return 1; - /* Use monospaced fonts when asked for it. */ - if (info->callback->Monospace() && (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) != (FF_MODERN | FIXED_PITCH)) return 1; + /* Exclude font names starting with an @. These are special generated fonts for printing vertically oriented languages. */ + if (logfont->elfFullName[0] == _T('@')) return 1; /* The font has to have at least one of the supported locales to be usable. */ if ((metric->ntmFontSig.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (metric->ntmFontSig.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) { - /* On win9x metric->ntmFontSig seems to contain garbage. */ + /* On Win9x metric->ntmFontSig seems to contain garbage. */ + if (!IsWindows9x()) return 1; + FONTSIGNATURE fs; memset(&fs, 0, sizeof(fs)); HFONT font = CreateFontIndirect(&logfont->elfLogFont); @@ -376,62 +351,92 @@ if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) return 1; } - char font_name[MAX_PATH]; -#if defined(UNICODE) - WIDE_TO_MB_BUFFER((const TCHAR*)logfont->elfFullName, font_name, lengthof(font_name)); -#else - strecpy(font_name, (const TCHAR*)logfont->elfFullName, lastof(font_name)); -#endif - - /* Add english name after font name */ - const char *english_name = GetEnglishFontName(logfont); - strecpy(font_name + strlen(font_name) + 1, english_name, lastof(font_name)); + /* Store font data in the list. */ + bool monospace = (logfont->elfLogFont.lfPitchAndFamily & (FF_MODERN | FIXED_PITCH)) == (FF_MODERN | FIXED_PITCH); + WindowsFont *font = new WindowsFont(WIDE_TO_MB(logfont->elfLogFont.lfFaceName), monospace, logfont->elfLogFont); + *info->fonts->Append() = font; - /* Check whether we can actually load the font. */ - bool ft_init = _library != NULL; - bool found = false; - FT_Face face; - /* Init FreeType if needed. */ - if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) && GetFontByFaceName(font_name, &face) == FT_Err_Ok) { - FT_Done_Face(face); - found = true; - } - if (!ft_init) { - /* Uninit FreeType if we did the init. */ - FT_Done_FreeType(_library); - _library = NULL; - } + DEBUG(freetype, 2, "Adding font %s (%s)", font->name); - if (!found) return 1; - - info->callback->SetFontNames(info->settings, font_name); - if (info->callback->FindMissingGlyphs(NULL)) return 1; - DEBUG(freetype, 1, "Fallback font: %s (%s)", font_name, english_name); - return 0; // stop enumerating + return 1; } -bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) +FontList *GetFallbackFontList(const char *language_isocode, int winlangid) { - DEBUG(freetype, 1, "Trying fallback fonts"); + DEBUG(freetype, 1, "Getting list of fonts for %s (0x%x)", language_isocode, winlangid); + EFCParam langInfo; if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPTSTR)&langInfo.locale, sizeof(langInfo.locale) / sizeof(TCHAR)) == 0) { /* Invalid langid or some other mysterious error, can't determine fallback font. */ DEBUG(freetype, 1, "Can't get locale info for fallback font (langid=0x%x)", winlangid); - return false; + return NULL; } - langInfo.settings = settings; - langInfo.callback = callback; + langInfo.fonts = new FontList; + /* Enumerate all font families. */ LOGFONT font; - /* Enumerate all fonts. */ font.lfCharSet = DEFAULT_CHARSET; font.lfFaceName[0] = '\0'; font.lfPitchAndFamily = 0; HDC dc = GetDC(NULL); - int ret = EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0); + EnumFontFamiliesEx(dc, &font, (FONTENUMPROC)&EnumFontCallback, (LPARAM)&langInfo, 0); ReleaseDC(NULL, dc); - return ret == 0; + + return langInfo.fonts; +} + +bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) +{ + FontList *font_list = GetFallbackFontList(language_isocode, winlangid); + if (font_list == NULL) return false; + + for (FontInfo * const * font = font_list->Begin(); font != font_list->End(); font++) { + const WindowsFont * winfont = static_cast(*font); + + /* Use only monospace fonts if requested. */ + if (callback->Monospace() && !winfont->monospace) continue; + + char font_name[MAX_PATH]; +#if defined(UNICODE) + WIDE_TO_MB_BUFFER((const TCHAR*)winfont->logfont.lfFaceName, font_name, lengthof(font_name)); +#else + strecpy(font_name, (const TCHAR*)winfont->logfont.lfFaceName, lastof(font_name)); +#endif + + /* Add english name after font name */ + const char *english_name = GetEnglishFontName(&winfont->logfont); + strecpy(font_name + strlen(font_name) + 1, english_name, lastof(font_name)); + + /* Check whether we can actually load the font. */ + bool ft_init = _library != NULL; + bool found = false; + FT_Face face; + /* Init FreeType if needed. */ + if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) && GetFontByFaceName(font_name, &face) == FT_Err_Ok) { + FT_Done_Face(face); + found = true; + } + if (!ft_init) { + /* Uninit FreeType if we did the init. */ + FT_Done_FreeType(_library); + _library = NULL; + } + + if (!found) continue; + + /* Still missing glyphs? Try next on the list. */ + callback->SetFontNames(settings, font_name); + if (callback->FindMissingGlyphs(NULL)) continue; + + DEBUG(freetype, 1, "Fallback font for %s: %s (%s)", language_isocode, font_name, english_name); + + delete font_list; + return true; + } + + delete font_list; + return false; } #elif defined(__APPLE__) /* end ifdef Win32 */ diff --git a/src/fontcache.h b/src/fontcache.h --- a/src/fontcache.h +++ b/src/fontcache.h @@ -13,6 +13,22 @@ #define FONTCACHE_H #include "spritecache.h" +#include "core/smallvec_type.hpp" + +/** Information about a font family. */ +struct FontInfo { + const char *name; ///< Name of the font. + bool monospace; ///< Font is monospaced? + + FontInfo(const char *name, bool monospace) : name(strdup(name)), monospace(monospace) {} + virtual ~FontInfo() + { + free(name); + } +}; + +/** Vector of fonts. */ +typedef AutoDeleteSmallVector FontList; /** Get the SpriteID mapped to the given font size and key */ SpriteID GetUnicodeGlyph(FontSize size, uint32 key);