Changeset - r10367:2521c24ffcf6
[Not reviewed]
master
0 52 0
rubidium - 16 years ago 2008-11-24 18:53:17
rubidium@openttd.org
(svn r14618) -Feature: when the chosen language isn't supported by the current font, try to find a font that does and use that instead. Thanks to glx/michi_cc for the Windows implementation.
52 files changed with 342 insertions and 47 deletions:
0 comments (0 inline, 0 general)
src/fontcache.cpp
Show inline comments
 
@@ -154,8 +154,68 @@ registry_no_font_found:
 
	RegCloseKey(hKey);
 
	return err;
 
}
 
#else
 
# ifdef WITH_FONTCONFIG
 

	
 

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

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

	
 
	/* Only use TrueType fonts */
 
	if (!(type & TRUETYPE_FONTTYPE)) return 1;
 
	/* Don't use SYMBOL fonts */
 
	if (logfont->elfLogFont.lfCharSet == SYMBOL_CHARSET) 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. */
 
		FONTSIGNATURE fs;
 
		memset(&fs, 0, sizeof(fs));
 
		HFONT font = CreateFontIndirect(&logfont->elfLogFont);
 
		if (font != NULL) {
 
			HDC dc = GetDC(NULL);
 
			HGDIOBJ oldfont = SelectObject(dc, font);
 
			GetTextCharsetInfo(dc, &fs, 0);
 
			SelectObject(dc, oldfont);
 
			ReleaseDC(NULL, dc);
 
			DeleteObject(font);
 
		}
 
		if ((fs.fsCsb[0] & info->locale.lsCsbSupported[0]) == 0 && (fs.fsCsb[1] & info->locale.lsCsbSupported[1]) == 0) return 1;
 
	}
 

	
 
	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));
 
	return 0; // stop enumerating
 
}
 

	
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid)
 
{
 
	EFCParam langInfo;
 
	if (GetLocaleInfo(MAKELCID(winlangid, SORT_DEFAULT), LOCALE_FONTSIGNATURE, (LPWSTR)&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;
 

	
 
	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);
 
	ReleaseDC(NULL, dc);
 
	return ret == 0;
 
}
 

	
 
#elif defined(WITH_FONTCONFIG)
 
static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
 
{
 
	FT_Error err = FT_Err_Cannot_Open_Resource;
 
@@ -221,11 +281,75 @@ static FT_Error GetFontByFaceName(const 
 

	
 
	return err;
 
}
 
# else
 

	
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid)
 
{
 
	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));
 
	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 */
 
	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;
 
	}
 

	
 
	FcDefaultSubstitute(pat);
 

	
 
	/* The 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;}
 
# endif /* WITH_FONTCONFIG */
 

	
 
#endif
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode) { return false; }
 
#endif /* WITH_FONTCONFIG */
 

	
 
/**
 
 * Loads the freetype font.
 
@@ -303,6 +427,35 @@ void InitFreeType()
 
	if (_face_large  != NULL) FT_Set_Pixel_Sizes(_face_large,  0, _freetype.large_size);
 
}
 

	
 
static void ResetGlyphCache();
 

	
 
/**
 
 * Unload a face and set it to NULL.
 
 * @param face the face to unload
 
 */
 
static void UnloadFace(FT_Face *face)
 
{
 
	if (*face == NULL) return;
 

	
 
	FT_Done_Face(*face);
 
	*face = NULL;
 
}
 

	
 
/**
 
 * Free everything allocated w.r.t. fonts.
 
 */
 
void UninitFreeType()
 
{
 
	ResetGlyphCache();
 

	
 
	UnloadFace(&_face_small);
 
	UnloadFace(&_face_medium);
 
	UnloadFace(&_face_large);
 

	
 
	FT_Done_FreeType(_library);
 
	_library = NULL;
 
}
 

	
 

	
 
static FT_Face GetFontFace(FontSize size)
 
{
 
@@ -335,6 +488,27 @@ struct GlyphEntry {
 
 */
 
static GlyphEntry **_glyph_ptr[FS_END];
 

	
 
/** Clear the complete cache */
 
static void ResetGlyphCache()
 
{
 
	for (int i = 0; i < FS_END; i++) {
 
		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].sprite == NULL) continue;
 
				free(_glyph_ptr[i][j][k].sprite);
 
			}
 

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

	
 
		free(_glyph_ptr[i]);
 
		_glyph_ptr[i] = NULL;
 
	}
 
}
 

	
 
static GlyphEntry *GetGlyphPtr(FontSize size, WChar key)
 
{
src/fontcache.h
Show inline comments
 
@@ -19,9 +19,9 @@ void InitializeUnicodeGlyphMap();
 
#ifdef WITH_FREETYPE
 

	
 
struct FreeTypeSettings {
 
	char small_font[260];
 
	char medium_font[260];
 
	char large_font[260];
 
	char small_font[MAX_PATH];
 
	char medium_font[MAX_PATH];
 
	char large_font[MAX_PATH];
 
	uint small_size;
 
	uint medium_size;
 
	uint large_size;
 
@@ -33,13 +33,26 @@ struct FreeTypeSettings {
 
extern FreeTypeSettings _freetype;
 

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

	
 
/**
 
 * 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.
 
 * @return true if a font has been set, false otherwise.
 
 */
 
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid);
 

	
 
#else
 

	
 
/* Stub for initializiation */
 
static inline void InitFreeType() {}
 
static inline void UninitFreeType() {}
 

	
 
/** Get the Sprite for a glyph */
 
static inline const Sprite *GetGlyph(FontSize size, uint32 key)
src/lang/afrikaans.txt
Show inline comments
 
##name Afrikaans
 
##ownname Jaybee
 
##isocode af_ZA
 
##winlangid 0x0436
 
##plural 0
 
##gender male
 

	
src/lang/brazilian_portuguese.txt
Show inline comments
 
##name Brazilian_Portuguese
 
##ownname Português (BR)
 
##isocode pt_BR
 
##winlangid 0x0416
 
##plural 2
 
##gender m f
 

	
src/lang/bulgarian.txt
Show inline comments
 
##name Bulgarian
 
##ownname Български
 
##isocode bg_BG
 
##winlangid 0x0402
 
##plural 0
 
##case m f n p
 
##gender m f n p
src/lang/catalan.txt
Show inline comments
 
##name Catalan
 
##ownname Català
 
##isocode ca_ES
 
##winlangid 0x0403
 
##plural 0
 

	
 
#
src/lang/croatian.txt
Show inline comments
 
##name Croatian
 
##ownname Hrvatski
 
##isocode hr_HR
 
##winlangid 0x041a
 
##plural 6
 
##case nom gen dat aku vok lok ins
 
##gender male female middle
src/lang/czech.txt
Show inline comments
 
##name Czech
 
##ownname Čeština
 
##isocode cs_CZ
 
##winlangid 0x0405
 
##plural 6
 
##case nom gen dat acc voc loc ins big small
 
##gender m f n
src/lang/danish.txt
Show inline comments
 
##name Danish
 
##ownname Dansk
 
##isocode da_DA
 
##winlangid 0x0406
 
##plural 0
 

	
 
#
src/lang/dutch.txt
Show inline comments
 
##name Dutch
 
##ownname Nederlands
 
##isocode nl_NL
 
##winlangid 0x0413
 
##plural 0
 

	
 
#
src/lang/english.txt
Show inline comments
 
##name English (UK)
 
##ownname English (UK)
 
##isocode en_GB
 
##winlangid 0x0809
 
##plural 0
 

	
 
#
src/lang/english_US.txt
Show inline comments
 
##name English (US)
 
##ownname English (US)
 
##isocode en_US
 
##winlangid 0x0409
 
##plural 0
 

	
 
#
src/lang/esperanto.txt
Show inline comments
 
##name Esperanto
 
##ownname Esperanto
 
##isocode eo_EO
 
##winlangid 0x0000
 
##plural 0
 
##case n
 

	
src/lang/estonian.txt
Show inline comments
 
##name Estonian
 
##ownname Eesti keel
 
##isocode et_ET
 
##winlangid 0x0425
 
##plural 0
 
##case g in
 

	
src/lang/finnish.txt
Show inline comments
 
##name Finnish
 
##ownname Suomi
 
##isocode fi_FI
 
##winlangid 0x040b
 
##plural 0
 

	
 
#
src/lang/french.txt
Show inline comments
 
##name French
 
##ownname Français
 
##isocode fr_FR
 
##winlangid 0x040c
 
##plural 2
 
##gender m m2 f
 

	
src/lang/galician.txt
Show inline comments
 
##name Galician
 
##ownname Galego
 
##isocode gl_ES
 
##winlangid 0x0456
 
##plural 0
 
##gender m f n
 

	
src/lang/german.txt
Show inline comments
 
##name German
 
##ownname Deutsch
 
##isocode de_DE
 
##winlangid 0x0407
 
##plural 0
 
##gender m w n p
 

	
src/lang/hungarian.txt
Show inline comments
 
##name Hungarian
 
##ownname Magyar
 
##isocode hu_HU
 
##winlangid 0x040e
 
##plural 1
 
##case t ba
 

	
src/lang/icelandic.txt
Show inline comments
 
##name Icelandic
 
##ownname Íslenska
 
##isocode is_IS
 
##winlangid 0x040f
 
##plural 0
 
##gender karlkyn kvenkyn hvorugkyn
 

	
src/lang/italian.txt
Show inline comments
 
##name Italian
 
##ownname Italiano
 
##isocode it_IT
 
##winlangid 0x0410
 
##plural 0
 
##case ms mp fs fp
 
##gender m f
src/lang/japanese.txt
Show inline comments
 
##name Japanese
 
##ownname 日本語
 
##isocode ja_JP
 
##winlangid 0x0411
 
##plural 1
 

	
 
#
src/lang/korean.txt
Show inline comments
 
##name Korean
 
##ownname 한국어
 
##isocode ko_KR
 
##winlangid 0x0412
 
##plural 1
 

	
 
#
src/lang/lithuanian.txt
Show inline comments
 
##name Lithuanian
 
##ownname Lietuvių
 
##isocode lt_LT
 
##winlangid 0x0427
 
##plural 5
 
##case kas ko kam ka kuo kur kreip
 
##gender vyr mot
src/lang/norwegian_bokmal.txt
Show inline comments
 
##name Norwegian
 
##ownname Norsk (bokmål)
 
##isocode nb_NO
 
##winlangid 0x0414
 
##plural 0
 

	
 
#
src/lang/norwegian_nynorsk.txt
Show inline comments
 
##name Norwegian new
 
##ownname Norsk, Nynorsk
 
##isocode nn_NO
 
##winlangid 0x0814
 
##plural 0
 
##gender masculine feminine neuter
 

	
src/lang/origveh.txt
Show inline comments
 
##name Original vehicle names (ENG)
 
##ownname Original vehicle names (ENG)
 
##isocode xx
 
##isocode xx_OV
 
##winlangid 0x0000
 

	
 
##id 0x8000
 
STR_8000_KIRBY_PAUL_TANK_STEAM                                  :Collett Pannier Tank (Steam)
src/lang/piglatin.txt
Show inline comments
 
##name Pig latin
 
##ownname Igpay atinlay
 
##isocode xx_PL
 
##winlangid 0x0000
 
##plural 0
 

	
 
#
src/lang/polish.txt
Show inline comments
 
##name Polish
 
##ownname Polski
 
##isocode pl_PL
 
##winlangid 0x0415
 
##plural 7
 
##case d c b n m w
 
##gender m f n
src/lang/portuguese.txt
Show inline comments
 
##name Portuguese
 
##ownname Português
 
##isocode pt_PT
 
##winlangid 0x0816
 
##plural 0
 

	
 
#
src/lang/romanian.txt
Show inline comments
 
##name Romanian
 
##ownname Românã
 
##isocode ro_RO
 
##winlangid 0x0418
 
##plural 0
 

	
 
#
src/lang/russian.txt
Show inline comments
 
##name Russian
 
##ownname Русский
 
##isocode ru_RU
 
##winlangid 0x0419
 
##plural 6
 
##case m f n p
 
##gender m f n p
src/lang/simplified_chinese.txt
Show inline comments
 
##name Chinese (Simplified)
 
##ownname 简体中文
 
##isocode zh_CN
 
##winlangid 0x0804
 
##plural 1
 

	
 
#
src/lang/slovak.txt
Show inline comments
 
##name Slovak
 
##ownname Slovensky
 
##isocode sk_SK
 
##winlangid 0x041b
 
##plural 6
 
##case g
 
##gender m z s
src/lang/slovenian.txt
Show inline comments
 
##name Slovenian
 
##ownname Slovenščina
 
##isocode sl_SL
 
##winlangid 0x0424
 
##plural 8
 
##case r d t
 

	
src/lang/spanish.txt
Show inline comments
 
##name Spanish
 
##ownname Español (ES)
 
##isocode es_ES
 
##winlangid 0x0c0a
 
##plural 0
 
##gender masculino femenino
 

	
src/lang/swedish.txt
Show inline comments
 
##name Swedish
 
##ownname Svenska
 
##isocode sv_SE
 
##winlangid 0x081d
 
##plural 0
 

	
 
#
src/lang/traditional_chinese.txt
Show inline comments
 
##name Chinese (Traditional)
 
##ownname 中文
 
##isocode zh_TW
 
##winlangid 0x0404
 
##plural 1
 

	
 
#
src/lang/turkish.txt
Show inline comments
 
##name Turkish
 
##ownname Türkçe
 
##isocode tr_TR
 
##winlangid 0x041f
 
##plural 1
 

	
 
#
src/lang/ukrainian.txt
Show inline comments
 
##name Ukrainian
 
##ownname Українська
 
##isocode uk_UA
 
##winlangid 0x0422
 
##plural 6
 
##gender m f s mn
 
##case r d z
src/lang/unfinished/frisian.txt
Show inline comments
 
##name Frisian
 
##ownname Frysk
 
##isocode fy_NL
 
##winlangid 0x0462
 

	
 
##id 0x0000
 
STR_NULL                                                        :
src/lang/unfinished/greek.txt
Show inline comments
 
##name Greek
 
##ownname Ελληνικά
 
##isocode el_GR
 
##winlangid 0x0408
 
##plural 0
 
##gender m f n
 

	
src/lang/unfinished/ido.txt
Show inline comments
 
##name Ido
 
##ownname Ido
 
##isocode io_XX
 
##winlangid 0x0000
 
##plural 0
 

	
 
#
src/lang/unfinished/indonesian.txt
Show inline comments
 
##name Indonesian
 
##ownname Indonesian
 
##isocode id_ID
 
##winlangid 0x0421
 
##plural 0
 

	
 
#
src/lang/unfinished/latvian.txt
Show inline comments
 
##name Latvian
 
##ownname Latviešu
 
##isocode lv_LV
 
##winlangid 0x0426
 
##plural 3
 
##case kas
 
##gender m f
src/lang/unfinished/macedonian.txt
Show inline comments
 
##name Macedonian
 
##ownname Македонски
 
##isocode mk_MK
 
##winlangid 0x042f
 
##plural 0
 

	
 
#
src/lang/unfinished/persian.txt
Show inline comments
 
##name Persian
 
##ownname Farsi
 
##isocode fa_IR
 
##winlangid 0x0429
 
##plural 0
 
##textdir rtl
 

	
src/lang/unfinished/serbian.txt
Show inline comments
 
##name Serbian
 
##ownname Srpski
 
##isocode sr_YU
 
##winlangid 0x7c1a
 
##plural 0
 
##case ih a ova ca ci ka ća va ao u om im e ke on ona to
 
##gender muški ženski srednji
src/lang/welsh.txt
Show inline comments
 
##name Welsh
 
##ownname Cymraeg
 
##isocode cy_GB
 
##winlangid 0x0452
 
##plural 0
 

	
 
#
src/strgen/strgen.cpp
Show inline comments
 
@@ -87,6 +87,7 @@ static uint32 _hash;
 
static char _lang_name[32], _lang_ownname[32], _lang_isocode[16];
 
static byte _lang_pluralform;
 
static byte _lang_textdir;
 
static uint16 _lang_winlangid;
 
#define MAX_NUM_GENDER 8
 
static char _genders[MAX_NUM_GENDER][16];
 
static int _numgenders;
 
@@ -649,6 +650,13 @@ static void HandlePragma(char *str)
 
		} else {
 
			error("Invalid textdir %s", str + 8);
 
		}
 
	} else if (!memcmp(str, "winlangid ", 10)) {
 
		char *buf = str + 10;
 
		long langid = strtol(buf, NULL, 16);
 
		if (langid > UINT16_MAX || langid < 0) {
 
			error("Invalid winlangid %s", buf);
 
		}
 
		_lang_winlangid = (uint16)langid;
 
	} else if (!memcmp(str, "gender ", 7)) {
 
		char* buf = str + 7;
 

	
 
@@ -912,6 +920,7 @@ static void ParseFile(const char *file, 
 
	_numgenders = 0;
 
	_lang_name[0] = _lang_ownname[0] = _lang_isocode[0] = '\0';
 
	_lang_textdir = TD_LTR;
 
	_lang_winlangid = 0x0000; // neutral language code
 
	// TODO:!! We can't reset the cases. In case the translated strings
 
	// derive some strings from english....
 

	
 
@@ -1161,6 +1170,7 @@ static void WriteLangfile(const char *fi
 
	hdr.version = TO_LE32(_hash);
 
	hdr.plural_form = _lang_pluralform;
 
	hdr.text_dir = _lang_textdir;
 
	hdr.winlangid = TO_LE16(_lang_winlangid);
 
	strcpy(hdr.name, _lang_name);
 
	strcpy(hdr.own_name, _lang_ownname);
 
	strcpy(hdr.isocode, _lang_isocode);
src/strgen/strgen.h
Show inline comments
 
@@ -14,7 +14,16 @@ struct LanguagePackHeader {
 
	uint16 offsets[32]; // the offsets
 
	byte plural_form;   // plural form index
 
	byte text_dir;      // default direction of the text
 
	byte pad[2];        // pad header to be a multiple of 4
 
	/**
 
	 * Windows language ID:
 
	 * Windows cannot and will not convert isocodes to something it can use to
 
	 * determine whether a font can be used for the language or not. As a result
 
	 * of that we need to pass the language id via strgen to OpenTTD to tell
 
	 * what language it is in "Windows". The ID is the 'locale identifier' on:
 
	 *   http://msdn.microsoft.com/en-us/library/ms776294.aspx
 
	 */
 
	uint16 winlangid;   // windows language id
 
	/* byte pad[0];        // pad header to be a multiple of 4 */
 
};
 

	
 
assert_compile(sizeof(LanguagePackHeader) % 4 == 0);
src/strings.cpp
Show inline comments
 
@@ -1358,9 +1358,13 @@ static bool GetLanguageFileHeader(const 
 
	size_t read = fread(hdr, sizeof(*hdr), 1, f);
 
	fclose(f);
 

	
 
	return read == 1 &&
 
	bool ret = read == 1 &&
 
			hdr->ident == TO_LE32(LANGUAGE_PACK_IDENT) &&
 
			hdr->version == TO_LE32(LANGUAGE_PACK_VERSION);
 

	
 
	/* Convert endianness for the windows language ID */
 
	if (ret) hdr->winlangid = FROM_LE16(hdr->winlangid);
 
	return ret;
 
}
 

	
 
/**
 
@@ -1478,45 +1482,83 @@ void InitializeLanguagePacks()
 
 */
 
void CheckForMissingGlyphsInLoadedLanguagePack()
 
{
 
	const Sprite *question_mark = GetGlyph(FS_NORMAL, '?');
 
#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
 

	
 
	bool retry = false;
 
	for (;;) {
 
		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 *string = _langpack_offs[_langtab_start[i] + j];
 
				WChar c;
 
				while ((c = Utf8Consume(&string)) != '\0') {
 
					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.
 
						 */
 
						string++;
 
					} else if (c == SCC_SETXY) {
 
						string += 2;
 
					} else if (IsPrintable(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) {
 
#ifdef WITH_FREETYPE
 
						if (!retry) {
 
							/* We found an unprintable character... lets try whether we can
 
							 * find a fallback font that can print the characters in the
 
							 * current language. */
 
							retry = true;
 

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

	
 
	for (uint i = 0; i != 32; i++) {
 
		for (uint j = 0; j < _langtab_num[i]; j++) {
 
			const char *string = _langpack_offs[_langtab_start[i] + j];
 
			WChar c;
 
			while ((c = Utf8Consume(&string)) != '\0') {
 
				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.
 
					 */
 
					string++;
 
				} else if (c == SCC_SETXY) {
 
					string += 2;
 
				} else if (IsPrintable(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) {
 
					/*
 
					 * The character is printable, but not in the normal font.
 
					 * This is the case we were testing for. In this case we
 
					 * have to show the error. As we do not want the string to
 
					 * be translated by the translators, we 'force' it into the
 
					 * binary and 'load' it via a BindCString. To do this
 
					 * properly we have to set the color of the string,
 
					 * otherwise we end up with a lot of artefacts. The color
 
					 * 'character' might change in the future, so for safety
 
					 * we just Utf8 Encode it into the string, which takes
 
					 * exactly three characters, so it replaces the "XXX" with
 
					 * the color marker.
 
					 */
 
					static char *err_str = strdup("XXXThe current font is missing some of the characters used in the texts for this language. Read the readme to see how to solve this.");
 
					Utf8Encode(err_str, SCC_YELLOW);
 
					SetDParamStr(0, err_str);
 
					ShowErrorMessage(INVALID_STRING_ID, STR_JUST_RAW_STRING, 0, 0);
 
					return;
 
							bool success = SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid);
 
							if (success) {
 
								UninitFreeType();
 
								InitFreeType();
 
							}
 

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

	
 
							if (success) continue;
 
						} else {
 
							/* 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
 
						/*
 
						 * The character is printable, but not in the normal font.
 
						 * This is the case we were testing for. In this case we
 
						 * have to show the error. As we do not want the string to
 
						 * be translated by the translators, we 'force' it into the
 
						 * binary and 'load' it via a BindCString. To do this
 
						 * properly we have to set the color of the string,
 
						 * otherwise we end up with a lot of artefacts. The color
 
						 * 'character' might change in the future, so for safety
 
						 * we just Utf8 Encode it into the string, which takes
 
						 * exactly three characters, so it replaces the "XXX" with
 
						 * the color marker.
 
						 */
 
						static char *err_str = strdup("XXXThe current font is missing some of the characters used in the texts for this language. Read the readme to see how to solve this.");
 
						Utf8Encode(err_str, SCC_YELLOW);
 
						SetDParamStr(0, err_str);
 
						ShowErrorMessage(INVALID_STRING_ID, STR_JUST_RAW_STRING, 0, 0);
 
						return;
 
					}
 
				}
 
			}
 
		}
 
		break;
 
	}
 

	
 
#if !defined(WITH_ICU)
0 comments (0 inline, 0 general)