Changeset - r16560:73269894b72b
[Not reviewed]
master
0 3 0
glx - 14 years ago 2010-11-23 17:59:50
glx@openttd.org
(svn r21298) -Fix [FS#4261]: fonts set in openttd.cfg were not properly checked for missing glyphs on language change
3 files changed with 79 insertions and 81 deletions:
0 comments (0 inline, 0 general)
src/fontcache.cpp
Show inline comments
 
@@ -287,12 +287,13 @@ err1:
 
	return ret_font_name == NULL ? WIDE_TO_MB((const TCHAR*)logfont->elfFullName) : ret_font_name;
 
}
 

	
 
struct EFCParam {
 
	FreeTypeSettings *settings;
 
	LOCALESIGNATURE  locale;
 
	SetFallbackFontCallback *callback;
 
};
 

	
 
static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXTMETRICEX *metric, DWORD type, LPARAM lParam)
 
{
 
	EFCParam *info = (EFCParam *)lParam;
 

	
 
@@ -343,29 +344,31 @@ static int CALLBACK EnumFontCallback(con
 
		FT_Done_FreeType(_library);
 
		_library = NULL;
 
	}
 

	
 
	if (!found) return 1;
 

	
 
	DEBUG(freetype, 1, "Fallback font: %s (%s)", font_name, english_name);
 
	strecpy(info->settings->small_font,  font_name, lastof(info->settings->small_font));
 
	strecpy(info->settings->medium_font, font_name, lastof(info->settings->medium_font));
 
	strecpy(info->settings->large_font,  font_name, lastof(info->settings->large_font));
 
	if (info->callback(NULL)) return 1;
 
	DEBUG(freetype, 1, "Fallback font: %s (%s)", font_name, english_name);
 
	return 0; // stop enumerating
 
}
 

	
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, const char *str)
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, SetFallbackFontCallback *callback)
 
{
 
	DEBUG(freetype, 1, "Trying fallback fonts");
 
	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;
 
	}
 
	langInfo.settings = settings;
 
	langInfo.callback = callback;
 

	
 
	LOGFONT font;
 
	/* Enumerate all fonts. */
 
	font.lfCharSet = DEFAULT_CHARSET;
 
	font.lfFaceName[0] = '\0';
 
	font.lfPitchAndFamily = 0;
 
@@ -423,16 +426,19 @@ FT_Error GetFontByFaceName(const char *f
 
		}
 
	}
 

	
 
	return err;
 
}
 

	
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, const char *str)
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, SetFallbackFontCallback *callback)
 
{
 
	const char *str;
 
	bool result = false;
 

	
 
	callback(&str);
 

	
 
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
 
	if (MacOSVersionIsAtLeast(10, 5, 0)) {
 
		/* Determine fallback font using CoreText. This uses the language isocode
 
		 * to find a suitable font. CoreText is available from 10.5 onwards. */
 
		char lang[16];
 
		if (strcmp(language_isocode, "zh_TW") == 0) {
 
@@ -601,12 +607,13 @@ bool SetFallbackFont(FreeTypeSettings *s
 
			/* Uninit FreeType if we did the init. */
 
			FT_Done_FreeType(_library);
 
			_library = NULL;
 
		}
 
	 }
 

	
 
	callback(NULL);
 
	return result;
 
}
 

	
 
#elif defined(WITH_FONTCONFIG) /* end ifdef __APPLE__ */
 
/* ========================================================================================
 
 * FontConfig (unix) support
 
@@ -674,79 +681,71 @@ static FT_Error GetFontByFaceName(const 
 
		FcFini();
 
	}
 

	
 
	return err;
 
}
 

	
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, const char *str)
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, SetFallbackFontCallback *callback)
 
{
 
	if (!FcInit()) return false;
 

	
 
	bool ret = false;
 

	
 
	/* Fontconfig doesn't handle full language isocodes, only the part
 
	 * before the _ of e.g. en_GB is used, so "remove" everything after
 
	 * the _. */
 
	char lang[16];
 
	strecpy(lang, language_isocode, lastof(lang));
 
	seprintf(lang, lastof(lang), ":lang=%s", language_isocode);
 
	char *split = strchr(lang, '_');
 
	if (split != NULL) *split = '\0';
 

	
 
	FcPattern *pat;
 
	FcPattern *match;
 
	FcResult result;
 
	FcChar8 *file;
 
	FcFontSet *fs;
 
	FcValue val;
 
	val.type = FcTypeString;
 
	val.u.s = (FcChar8*)lang;
 
	/* First create a pattern to match the wanted language. */
 
	FcPattern *pat = FcNameParse((FcChar8*)lang);
 
	/* We only want to know the filename. */
 
	FcObjectSet *os = FcObjectSetBuild(FC_FILE, NULL);
 
	/* Get the list of filenames matching the wanted language. */
 
	FcFontSet *fs = FcFontList(NULL, pat, os);
 

	
 
	/* We don't need these anymore. */
 
	FcObjectSetDestroy(os);
 
	FcPatternDestroy(pat);
 

	
 
	if (fs != NULL) {
 
		for (int i = 0; i < fs->nfont; i++) {
 
			FcPattern *font = fs->fonts[i];
 

	
 
	/* First create a pattern to match the wanted language */
 
	pat = FcPatternCreate();
 
	/* And fill it with the language and other defaults */
 
	if (pat == NULL ||
 
			!FcPatternAdd(pat, "lang", val, false) ||
 
			!FcConfigSubstitute(0, pat, FcMatchPattern)) {
 
		goto error_pattern;
 
			FcChar8 *file = NULL;
 
			FcResult res = FcPatternGetString(font, FC_FILE, 0, &file);
 
			if (res != FcResultMatch || file == NULL) {
 
				continue;
 
			}
 

	
 
			strecpy(settings->small_font,  (const char*)file, lastof(settings->small_font));
 
			strecpy(settings->medium_font, (const char*)file, lastof(settings->medium_font));
 
			strecpy(settings->large_font,  (const char*)file, lastof(settings->large_font));
 

	
 
			bool missing = callback(NULL);
 
			DEBUG(freetype, 1, "Font \"%s\" misses%s glyphs", file, missing ? "" : " no");
 

	
 
			if (!missing) {
 
				ret = true;
 
				break;
 
			}
 
		}
 

	
 
		/* Clean up the list of filenames. */
 
		FcFontSetDestroy(fs);
 
	}
 

	
 
	FcDefaultSubstitute(pat);
 

	
 
	/* Then create a font set and match that */
 
	match = FcFontMatch(0, pat, &result);
 

	
 
	if (match == NULL) {
 
		goto error_pattern;
 
	}
 

	
 
	/* Find all fonts that do match */
 
	fs = FcFontSetCreate();
 
	FcFontSetAdd(fs, match);
 

	
 
	/* And take the first, if it exists */
 
	if (fs->nfont <= 0 || FcPatternGetString(fs->fonts[0], FC_FILE, 0, &file)) {
 
		goto error_fontset;
 
	}
 

	
 
	strecpy(settings->small_font,  (const char*)file, lastof(settings->small_font));
 
	strecpy(settings->medium_font, (const char*)file, lastof(settings->medium_font));
 
	strecpy(settings->large_font,  (const char*)file, lastof(settings->large_font));
 

	
 
	ret = true;
 

	
 
error_fontset:
 
	FcFontSetDestroy(fs);
 
error_pattern:
 
	if (pat != NULL) FcPatternDestroy(pat);
 
	FcFini();
 
	return ret;
 
}
 

	
 
#else /* without WITH_FONTCONFIG */
 
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) {return FT_Err_Cannot_Open_Resource;}
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, const char *str) { return false; }
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, SetFallbackFontCallback *callback) { return false }
 
#endif /* WITH_FONTCONFIG */
 

	
 
static void SetFontGeometry(FT_Face face, FontSize size, int pixels)
 
{
 
	FT_Set_Pixel_Sizes(face, 0, pixels);
 

	
 
@@ -888,12 +887,13 @@ static FT_Face GetFontFace(FontSize size
 
}
 

	
 

	
 
struct GlyphEntry {
 
	Sprite *sprite;
 
	byte width;
 
	bool duplicate;
 
};
 

	
 

	
 
/* The glyph cache. This is structured to reduce memory consumption.
 
 * 1) There is a 'segment' table for each font size.
 
 * 2) Each segment table is a discrete block of characters.
 
@@ -915,12 +915,13 @@ static void ResetGlyphCache()
 
		if (_glyph_ptr[i] == NULL) continue;
 

	
 
		for (int j = 0; j < 256; j++) {
 
			if (_glyph_ptr[i][j] == NULL) continue;
 

	
 
			for (int k = 0; k < 256; k++) {
 
				if (_glyph_ptr[i][j][k].duplicate) continue;
 
				free(_glyph_ptr[i][j][k].sprite);
 
			}
 

	
 
			free(_glyph_ptr[i][j]);
 
		}
 

	
 
@@ -934,27 +935,28 @@ static GlyphEntry *GetGlyphPtr(FontSize 
 
	if (_glyph_ptr[size] == NULL) return NULL;
 
	if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) return NULL;
 
	return &_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)];
 
}
 

	
 

	
 
static void SetGlyphPtr(FontSize size, WChar key, const GlyphEntry *glyph)
 
static void SetGlyphPtr(FontSize size, WChar key, const GlyphEntry *glyph, bool duplicate = false)
 
{
 
	if (_glyph_ptr[size] == NULL) {
 
		DEBUG(freetype, 3, "Allocating root glyph cache for size %u", size);
 
		_glyph_ptr[size] = CallocT<GlyphEntry*>(256);
 
	}
 

	
 
	if (_glyph_ptr[size][GB(key, 8, 8)] == NULL) {
 
		DEBUG(freetype, 3, "Allocating glyph cache for range 0x%02X00, size %u", GB(key, 8, 8), size);
 
		_glyph_ptr[size][GB(key, 8, 8)] = CallocT<GlyphEntry>(256);
 
	}
 

	
 
	DEBUG(freetype, 4, "Set glyph for unicode character 0x%04X, size %u", key, size);
 
	_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].sprite = glyph->sprite;
 
	_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].width  = glyph->width;
 
	_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].sprite    = glyph->sprite;
 
	_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].width     = glyph->width;
 
	_glyph_ptr[size][GB(key, 8, 8)][GB(key, 0, 8)].duplicate = duplicate;
 
}
 

	
 
static void *AllocateFont(size_t size)
 
{
 
	return MallocT<byte>(size);
 
}
 
@@ -1001,13 +1003,20 @@ const Sprite *GetGlyph(FontSize size, WC
 
	if (glyph != NULL && glyph->sprite != NULL) return glyph->sprite;
 

	
 
	slot = face->glyph;
 

	
 
	bool aa = GetFontAAState(size);
 

	
 
	FT_Load_Char(face, key, FT_LOAD_DEFAULT);
 
	FT_UInt glyph_index = FT_Get_Char_Index(face, key);
 
	if (glyph_index == 0) {
 
		GetGlyph(size, '?');
 
		glyph = GetGlyphPtr(size, '?');
 
		SetGlyphPtr(size, key, glyph, true);
 
		return glyph->sprite;
 
	}
 
	FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
 
	FT_Render_Glyph(face->glyph, aa ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO);
 

	
 
	/* Despite requesting a normal glyph, FreeType may have returned a bitmap */
 
	aa = (slot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY);
 

	
 
	/* Add 1 pixel for the shadow on the medium font. Our sprite must be at least 1x1 pixel */
src/fontcache.h
Show inline comments
 
@@ -43,23 +43,24 @@ extern FreeTypeSettings _freetype;
 

	
 
void InitFreeType();
 
void UninitFreeType();
 
const Sprite *GetGlyph(FontSize size, uint32 key);
 
uint GetGlyphWidth(FontSize size, uint32 key);
 

	
 
typedef bool (SetFallbackFontCallback)(const char **);
 
/**
 
 * We would like to have a fallback font as the current one
 
 * doesn't contain all characters we need.
 
 * This function must set all fonts of settings.
 
 * @param settings the settings to overwrite the fontname of.
 
 * @param language_isocode the language, e.g. en_GB.
 
 * @param winlangid the language ID windows style.
 
 * @param str Sample string.
 
 * @param callback The function to call to check for missing glyphs.
 
 * @return true if a font has been set, false otherwise.
 
 */
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, const char *str);
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, SetFallbackFontCallback *callback);
 

	
 
#else
 

	
 
/* Stub for initializiation */
 
static inline void InitFreeType() { ResetFontSizes(); }
 
static inline void UninitFreeType() { ResetFontSizes(); }
src/strings.cpp
Show inline comments
 
@@ -1565,22 +1565,25 @@ const char *GetCurrentLanguageIsoCode()
 
}
 

	
 
/**
 
 * Check whether there are glyphs missing in the current language.
 
 * @param Pointer to an address for storing the text pointer.
 
 * @return If glyphs are missing, return \c true, else return \false.
 
 * @pre  *str must not be \c NULL.
 
 * @post If \c true is returned, *str points to a string that is found to contain at least one missing glyph.
 
 * @post If \c true is returned and str is not NULL, *str points to a string that is found to contain at least one missing glyph.
 
 */
 
static bool FindMissingGlyphs(const char **str)
 
{
 
#ifdef WITH_FREETYPE
 
	UninitFreeType();
 
	InitFreeType();
 
#endif
 
	const Sprite *question_mark = GetGlyph(FS_NORMAL, '?');
 
	for (uint i = 0; i != 32; i++) {
 
		for (uint j = 0; j < _langtab_num[i]; j++) {
 
			const char *text = _langpack_offs[_langtab_start[i] + j];
 
			*str = text;
 
			if (str != NULL) *str = text;
 
			for (WChar c = Utf8Consume(&text); c != '\0'; c = Utf8Consume(&text)) {
 
				if (c == SCC_SETX) {
 
					/* SetX is, together with SetXY as special character that
 
					 * uses the next (two) characters as data points. We have
 
					 * to skip those, otherwise the UTF8 reading will go haywire. */
 
					text++;
 
@@ -1605,45 +1608,30 @@ static bool FindMissingGlyphs(const char
 
 * mean it might use characters that are not in the
 
 * font, which is the whole reason this check has
 
 * been added.
 
 */
 
void CheckForMissingGlyphsInLoadedLanguagePack()
 
{
 
#ifdef WITH_FREETYPE
 
	/* Reset to the original state; switching languages might cause us to
 
	 * automatically choose another font. This resets that choice. */
 
	UninitFreeType();
 
	InitFreeType();
 
#endif
 

	
 
	const char *str;
 
	bool bad_font = FindMissingGlyphs(&str);
 
	bool bad_font = FindMissingGlyphs(NULL);
 
#ifdef WITH_FREETYPE
 
	if (bad_font) {
 
		/* We found an unprintable character... lets try whether we can find
 
		 * a fallback font that can print the characters in the current language. */
 
		FreeTypeSettings backup;
 
		memcpy(&backup, &_freetype, sizeof(backup));
 

	
 
		bool success = SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, str);
 
		if (success) {
 
			UninitFreeType();
 
			InitFreeType();
 
		}
 
		bad_font = !SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid, &FindMissingGlyphs);
 

	
 
		memcpy(&_freetype, &backup, sizeof(backup));
 

	
 
		if (success) {
 
			bad_font = FindMissingGlyphs(&str);
 
			if (bad_font) {
 
				/* Our fallback font does miss characters too, so keep the
 
				 * user chosen font as that is more likely to be any good than
 
				 * the wild guess we made */
 
				UninitFreeType();
 
				InitFreeType();
 
			}
 
		if (bad_font) {
 
			/* Our fallback font does miss characters too, so keep the
 
				* user chosen font as that is more likely to be any good than
 
				* the wild guess we made */
 
			UninitFreeType();
 
			InitFreeType();
 
		}
 
	}
 
#endif
 

	
 
	if (bad_font) {
 
		/* All attempts have failed. Display an error. As we do not want the string to be translated by
0 comments (0 inline, 0 general)