changeset 19954:1593a72bcfaa draft ui

-Codechange: [Win32] Create a list of all possible fallback fonts and use it to select one.
author Michael Lutz <michi@icosahedron.de>
date Sat, 15 Dec 2012 18:48:50 +0100
parents e7954aa34d47
children
files src/fontcache.cpp src/fontcache.h
diffstat 2 files changed, 105 insertions(+), 84 deletions(-) [+]
line wrap: on
line diff
--- 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<const WindowsFont *>(*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<const WindowsFont *>(*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 */
--- 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<FontInfo *, 32> FontList;
 
 /** Get the SpriteID mapped to the given font size and key */
 SpriteID GetUnicodeGlyph(FontSize size, uint32 key);