1
0
Fork 0

(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.

release/0.7
rubidium 2008-11-24 18:53:17 +00:00
parent 6878b181c7
commit fea78fbfbb
52 changed files with 342 additions and 47 deletions

View File

@ -154,8 +154,68 @@ registry_no_font_found:
RegCloseKey(hKey); RegCloseKey(hKey);
return err; 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) static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
{ {
FT_Error err = FT_Err_Cannot_Open_Resource; FT_Error err = FT_Err_Cannot_Open_Resource;
@ -221,11 +281,75 @@ static FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
return err; return err;
} }
# else
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, 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;}
bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode) { return false; }
#endif /* WITH_FONTCONFIG */
/** /**
* Loads the freetype font. * Loads the freetype font.
@ -303,6 +427,35 @@ void InitFreeType()
if (_face_large != NULL) FT_Set_Pixel_Sizes(_face_large, 0, _freetype.large_size); 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) static FT_Face GetFontFace(FontSize size)
{ {
@ -335,6 +488,27 @@ struct GlyphEntry {
*/ */
static GlyphEntry **_glyph_ptr[FS_END]; 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) static GlyphEntry *GetGlyphPtr(FontSize size, WChar key)
{ {

View File

@ -19,9 +19,9 @@ void InitializeUnicodeGlyphMap();
#ifdef WITH_FREETYPE #ifdef WITH_FREETYPE
struct FreeTypeSettings { struct FreeTypeSettings {
char small_font[260]; char small_font[MAX_PATH];
char medium_font[260]; char medium_font[MAX_PATH];
char large_font[260]; char large_font[MAX_PATH];
uint small_size; uint small_size;
uint medium_size; uint medium_size;
uint large_size; uint large_size;
@ -33,13 +33,26 @@ struct FreeTypeSettings {
extern FreeTypeSettings _freetype; extern FreeTypeSettings _freetype;
void InitFreeType(); void InitFreeType();
void UninitFreeType();
const struct Sprite *GetGlyph(FontSize size, uint32 key); const struct Sprite *GetGlyph(FontSize size, uint32 key);
uint GetGlyphWidth(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 #else
/* Stub for initializiation */ /* Stub for initializiation */
static inline void InitFreeType() {} static inline void InitFreeType() {}
static inline void UninitFreeType() {}
/** Get the Sprite for a glyph */ /** Get the Sprite for a glyph */
static inline const Sprite *GetGlyph(FontSize size, uint32 key) static inline const Sprite *GetGlyph(FontSize size, uint32 key)

View File

@ -1,6 +1,7 @@
##name Afrikaans ##name Afrikaans
##ownname Jaybee ##ownname Jaybee
##isocode af_ZA ##isocode af_ZA
##winlangid 0x0436
##plural 0 ##plural 0
##gender male ##gender male

View File

@ -1,6 +1,7 @@
##name Brazilian_Portuguese ##name Brazilian_Portuguese
##ownname Português (BR) ##ownname Português (BR)
##isocode pt_BR ##isocode pt_BR
##winlangid 0x0416
##plural 2 ##plural 2
##gender m f ##gender m f

View File

@ -1,6 +1,7 @@
##name Bulgarian ##name Bulgarian
##ownname Български ##ownname Български
##isocode bg_BG ##isocode bg_BG
##winlangid 0x0402
##plural 0 ##plural 0
##case m f n p ##case m f n p
##gender m f n p ##gender m f n p

View File

@ -1,6 +1,7 @@
##name Catalan ##name Catalan
##ownname Català ##ownname Català
##isocode ca_ES ##isocode ca_ES
##winlangid 0x0403
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name Croatian ##name Croatian
##ownname Hrvatski ##ownname Hrvatski
##isocode hr_HR ##isocode hr_HR
##winlangid 0x041a
##plural 6 ##plural 6
##case nom gen dat aku vok lok ins ##case nom gen dat aku vok lok ins
##gender male female middle ##gender male female middle

View File

@ -1,6 +1,7 @@
##name Czech ##name Czech
##ownname Čeština ##ownname Čeština
##isocode cs_CZ ##isocode cs_CZ
##winlangid 0x0405
##plural 6 ##plural 6
##case nom gen dat acc voc loc ins big small ##case nom gen dat acc voc loc ins big small
##gender m f n ##gender m f n

View File

@ -1,6 +1,7 @@
##name Danish ##name Danish
##ownname Dansk ##ownname Dansk
##isocode da_DA ##isocode da_DA
##winlangid 0x0406
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name Dutch ##name Dutch
##ownname Nederlands ##ownname Nederlands
##isocode nl_NL ##isocode nl_NL
##winlangid 0x0413
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name English (UK) ##name English (UK)
##ownname English (UK) ##ownname English (UK)
##isocode en_GB ##isocode en_GB
##winlangid 0x0809
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name English (US) ##name English (US)
##ownname English (US) ##ownname English (US)
##isocode en_US ##isocode en_US
##winlangid 0x0409
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name Esperanto ##name Esperanto
##ownname Esperanto ##ownname Esperanto
##isocode eo_EO ##isocode eo_EO
##winlangid 0x0000
##plural 0 ##plural 0
##case n ##case n

View File

@ -1,6 +1,7 @@
##name Estonian ##name Estonian
##ownname Eesti keel ##ownname Eesti keel
##isocode et_ET ##isocode et_ET
##winlangid 0x0425
##plural 0 ##plural 0
##case g in ##case g in

View File

@ -1,6 +1,7 @@
##name Finnish ##name Finnish
##ownname Suomi ##ownname Suomi
##isocode fi_FI ##isocode fi_FI
##winlangid 0x040b
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name French ##name French
##ownname Français ##ownname Français
##isocode fr_FR ##isocode fr_FR
##winlangid 0x040c
##plural 2 ##plural 2
##gender m m2 f ##gender m m2 f

View File

@ -1,6 +1,7 @@
##name Galician ##name Galician
##ownname Galego ##ownname Galego
##isocode gl_ES ##isocode gl_ES
##winlangid 0x0456
##plural 0 ##plural 0
##gender m f n ##gender m f n

View File

@ -1,6 +1,7 @@
##name German ##name German
##ownname Deutsch ##ownname Deutsch
##isocode de_DE ##isocode de_DE
##winlangid 0x0407
##plural 0 ##plural 0
##gender m w n p ##gender m w n p

View File

@ -1,6 +1,7 @@
##name Hungarian ##name Hungarian
##ownname Magyar ##ownname Magyar
##isocode hu_HU ##isocode hu_HU
##winlangid 0x040e
##plural 1 ##plural 1
##case t ba ##case t ba

View File

@ -1,6 +1,7 @@
##name Icelandic ##name Icelandic
##ownname Íslenska ##ownname Íslenska
##isocode is_IS ##isocode is_IS
##winlangid 0x040f
##plural 0 ##plural 0
##gender karlkyn kvenkyn hvorugkyn ##gender karlkyn kvenkyn hvorugkyn

View File

@ -1,6 +1,7 @@
##name Italian ##name Italian
##ownname Italiano ##ownname Italiano
##isocode it_IT ##isocode it_IT
##winlangid 0x0410
##plural 0 ##plural 0
##case ms mp fs fp ##case ms mp fs fp
##gender m f ##gender m f

View File

@ -1,6 +1,7 @@
##name Japanese ##name Japanese
##ownname 日本語 ##ownname 日本語
##isocode ja_JP ##isocode ja_JP
##winlangid 0x0411
##plural 1 ##plural 1
# #

View File

@ -1,6 +1,7 @@
##name Korean ##name Korean
##ownname 한국어 ##ownname 한국어
##isocode ko_KR ##isocode ko_KR
##winlangid 0x0412
##plural 1 ##plural 1
# #

View File

@ -1,6 +1,7 @@
##name Lithuanian ##name Lithuanian
##ownname Lietuvių ##ownname Lietuvių
##isocode lt_LT ##isocode lt_LT
##winlangid 0x0427
##plural 5 ##plural 5
##case kas ko kam ka kuo kur kreip ##case kas ko kam ka kuo kur kreip
##gender vyr mot ##gender vyr mot

View File

@ -1,6 +1,7 @@
##name Norwegian ##name Norwegian
##ownname Norsk (bokmål) ##ownname Norsk (bokmål)
##isocode nb_NO ##isocode nb_NO
##winlangid 0x0414
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name Norwegian new ##name Norwegian new
##ownname Norsk, Nynorsk ##ownname Norsk, Nynorsk
##isocode nn_NO ##isocode nn_NO
##winlangid 0x0814
##plural 0 ##plural 0
##gender masculine feminine neuter ##gender masculine feminine neuter

View File

@ -1,6 +1,7 @@
##name Original vehicle names (ENG) ##name Original vehicle names (ENG)
##ownname Original vehicle names (ENG) ##ownname Original vehicle names (ENG)
##isocode xx ##isocode xx_OV
##winlangid 0x0000
##id 0x8000 ##id 0x8000
STR_8000_KIRBY_PAUL_TANK_STEAM :Collett Pannier Tank (Steam) STR_8000_KIRBY_PAUL_TANK_STEAM :Collett Pannier Tank (Steam)

View File

@ -1,6 +1,7 @@
##name Pig latin ##name Pig latin
##ownname Igpay atinlay ##ownname Igpay atinlay
##isocode xx_PL ##isocode xx_PL
##winlangid 0x0000
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name Polish ##name Polish
##ownname Polski ##ownname Polski
##isocode pl_PL ##isocode pl_PL
##winlangid 0x0415
##plural 7 ##plural 7
##case d c b n m w ##case d c b n m w
##gender m f n ##gender m f n

View File

@ -1,6 +1,7 @@
##name Portuguese ##name Portuguese
##ownname Português ##ownname Português
##isocode pt_PT ##isocode pt_PT
##winlangid 0x0816
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name Romanian ##name Romanian
##ownname Românã ##ownname Românã
##isocode ro_RO ##isocode ro_RO
##winlangid 0x0418
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name Russian ##name Russian
##ownname Русский ##ownname Русский
##isocode ru_RU ##isocode ru_RU
##winlangid 0x0419
##plural 6 ##plural 6
##case m f n p ##case m f n p
##gender m f n p ##gender m f n p

View File

@ -1,6 +1,7 @@
##name Chinese (Simplified) ##name Chinese (Simplified)
##ownname 简体中文 ##ownname 简体中文
##isocode zh_CN ##isocode zh_CN
##winlangid 0x0804
##plural 1 ##plural 1
# #

View File

@ -1,6 +1,7 @@
##name Slovak ##name Slovak
##ownname Slovensky ##ownname Slovensky
##isocode sk_SK ##isocode sk_SK
##winlangid 0x041b
##plural 6 ##plural 6
##case g ##case g
##gender m z s ##gender m z s

View File

@ -1,6 +1,7 @@
##name Slovenian ##name Slovenian
##ownname Slovenščina ##ownname Slovenščina
##isocode sl_SL ##isocode sl_SL
##winlangid 0x0424
##plural 8 ##plural 8
##case r d t ##case r d t

View File

@ -1,6 +1,7 @@
##name Spanish ##name Spanish
##ownname Español (ES) ##ownname Español (ES)
##isocode es_ES ##isocode es_ES
##winlangid 0x0c0a
##plural 0 ##plural 0
##gender masculino femenino ##gender masculino femenino

View File

@ -1,6 +1,7 @@
##name Swedish ##name Swedish
##ownname Svenska ##ownname Svenska
##isocode sv_SE ##isocode sv_SE
##winlangid 0x081d
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name Chinese (Traditional) ##name Chinese (Traditional)
##ownname 中文 ##ownname 中文
##isocode zh_TW ##isocode zh_TW
##winlangid 0x0404
##plural 1 ##plural 1
# #

View File

@ -1,6 +1,7 @@
##name Turkish ##name Turkish
##ownname Türkçe ##ownname Türkçe
##isocode tr_TR ##isocode tr_TR
##winlangid 0x041f
##plural 1 ##plural 1
# #

View File

@ -1,6 +1,7 @@
##name Ukrainian ##name Ukrainian
##ownname Українська ##ownname Українська
##isocode uk_UA ##isocode uk_UA
##winlangid 0x0422
##plural 6 ##plural 6
##gender m f s mn ##gender m f s mn
##case r d z ##case r d z

View File

@ -1,6 +1,7 @@
##name Frisian ##name Frisian
##ownname Frysk ##ownname Frysk
##isocode fy_NL ##isocode fy_NL
##winlangid 0x0462
##id 0x0000 ##id 0x0000
STR_NULL : STR_NULL :

View File

@ -1,6 +1,7 @@
##name Greek ##name Greek
##ownname Ελληνικά ##ownname Ελληνικά
##isocode el_GR ##isocode el_GR
##winlangid 0x0408
##plural 0 ##plural 0
##gender m f n ##gender m f n

View File

@ -1,6 +1,7 @@
##name Ido ##name Ido
##ownname Ido ##ownname Ido
##isocode io_XX ##isocode io_XX
##winlangid 0x0000
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name Indonesian ##name Indonesian
##ownname Indonesian ##ownname Indonesian
##isocode id_ID ##isocode id_ID
##winlangid 0x0421
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name Latvian ##name Latvian
##ownname Latviešu ##ownname Latviešu
##isocode lv_LV ##isocode lv_LV
##winlangid 0x0426
##plural 3 ##plural 3
##case kas ##case kas
##gender m f ##gender m f

View File

@ -1,6 +1,7 @@
##name Macedonian ##name Macedonian
##ownname Македонски ##ownname Македонски
##isocode mk_MK ##isocode mk_MK
##winlangid 0x042f
##plural 0 ##plural 0
# #

View File

@ -1,6 +1,7 @@
##name Persian ##name Persian
##ownname Farsi ##ownname Farsi
##isocode fa_IR ##isocode fa_IR
##winlangid 0x0429
##plural 0 ##plural 0
##textdir rtl ##textdir rtl

View File

@ -1,6 +1,7 @@
##name Serbian ##name Serbian
##ownname Srpski ##ownname Srpski
##isocode sr_YU ##isocode sr_YU
##winlangid 0x7c1a
##plural 0 ##plural 0
##case ih a ova ca ci ka ća va ao u om im e ke on ona to ##case ih a ova ca ci ka ća va ao u om im e ke on ona to
##gender muški ženski srednji ##gender muški ženski srednji

View File

@ -1,6 +1,7 @@
##name Welsh ##name Welsh
##ownname Cymraeg ##ownname Cymraeg
##isocode cy_GB ##isocode cy_GB
##winlangid 0x0452
##plural 0 ##plural 0
# #

View File

@ -87,6 +87,7 @@ static uint32 _hash;
static char _lang_name[32], _lang_ownname[32], _lang_isocode[16]; static char _lang_name[32], _lang_ownname[32], _lang_isocode[16];
static byte _lang_pluralform; static byte _lang_pluralform;
static byte _lang_textdir; static byte _lang_textdir;
static uint16 _lang_winlangid;
#define MAX_NUM_GENDER 8 #define MAX_NUM_GENDER 8
static char _genders[MAX_NUM_GENDER][16]; static char _genders[MAX_NUM_GENDER][16];
static int _numgenders; static int _numgenders;
@ -649,6 +650,13 @@ static void HandlePragma(char *str)
} else { } else {
error("Invalid textdir %s", str + 8); 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)) { } else if (!memcmp(str, "gender ", 7)) {
char* buf = str + 7; char* buf = str + 7;
@ -912,6 +920,7 @@ static void ParseFile(const char *file, bool english)
_numgenders = 0; _numgenders = 0;
_lang_name[0] = _lang_ownname[0] = _lang_isocode[0] = '\0'; _lang_name[0] = _lang_ownname[0] = _lang_isocode[0] = '\0';
_lang_textdir = TD_LTR; _lang_textdir = TD_LTR;
_lang_winlangid = 0x0000; // neutral language code
// TODO:!! We can't reset the cases. In case the translated strings // TODO:!! We can't reset the cases. In case the translated strings
// derive some strings from english.... // derive some strings from english....
@ -1161,6 +1170,7 @@ static void WriteLangfile(const char *filename)
hdr.version = TO_LE32(_hash); hdr.version = TO_LE32(_hash);
hdr.plural_form = _lang_pluralform; hdr.plural_form = _lang_pluralform;
hdr.text_dir = _lang_textdir; hdr.text_dir = _lang_textdir;
hdr.winlangid = TO_LE16(_lang_winlangid);
strcpy(hdr.name, _lang_name); strcpy(hdr.name, _lang_name);
strcpy(hdr.own_name, _lang_ownname); strcpy(hdr.own_name, _lang_ownname);
strcpy(hdr.isocode, _lang_isocode); strcpy(hdr.isocode, _lang_isocode);

View File

@ -14,7 +14,16 @@ struct LanguagePackHeader {
uint16 offsets[32]; // the offsets uint16 offsets[32]; // the offsets
byte plural_form; // plural form index byte plural_form; // plural form index
byte text_dir; // default direction of the text 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); assert_compile(sizeof(LanguagePackHeader) % 4 == 0);

View File

@ -1358,9 +1358,13 @@ static bool GetLanguageFileHeader(const char *file, LanguagePack *hdr)
size_t read = fread(hdr, sizeof(*hdr), 1, f); size_t read = fread(hdr, sizeof(*hdr), 1, f);
fclose(f); fclose(f);
return read == 1 && bool ret = read == 1 &&
hdr->ident == TO_LE32(LANGUAGE_PACK_IDENT) && hdr->ident == TO_LE32(LANGUAGE_PACK_IDENT) &&
hdr->version == TO_LE32(LANGUAGE_PACK_VERSION); 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() 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
for (uint i = 0; i != 32; i++) { bool retry = false;
for (uint j = 0; j < _langtab_num[i]; j++) { for (;;) {
const char *string = _langpack_offs[_langtab_start[i] + j]; const Sprite *question_mark = GetGlyph(FS_NORMAL, '?');
WChar c;
while ((c = Utf8Consume(&string)) != '\0') { for (uint i = 0; i != 32; i++) {
if (c == SCC_SETX) { for (uint j = 0; j < _langtab_num[i]; j++) {
/* const char *string = _langpack_offs[_langtab_start[i] + j];
* SetX is, together with SetXY as special character that WChar c;
* uses the next (two) characters as data points. We have while ((c = Utf8Consume(&string)) != '\0') {
* to skip those, otherwise the UTF8 reading will go if (c == SCC_SETX) {
* haywire. /*
*/ * SetX is, together with SetXY as special character that
string++; * uses the next (two) characters as data points. We have
} else if (c == SCC_SETXY) { * to skip those, otherwise the UTF8 reading will go
string += 2; * haywire.
} else if (IsPrintable(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) { */
/* string++;
* The character is printable, but not in the normal font. } else if (c == SCC_SETXY) {
* This is the case we were testing for. In this case we string += 2;
* have to show the error. As we do not want the string to } else if (IsPrintable(c) && c != '?' && GetGlyph(FS_NORMAL, c) == question_mark) {
* be translated by the translators, we 'force' it into the #ifdef WITH_FREETYPE
* binary and 'load' it via a BindCString. To do this if (!retry) {
* properly we have to set the color of the string, /* We found an unprintable character... lets try whether we can
* otherwise we end up with a lot of artefacts. The color * find a fallback font that can print the characters in the
* 'character' might change in the future, so for safety * current language. */
* we just Utf8 Encode it into the string, which takes retry = true;
* exactly three characters, so it replaces the "XXX" with
* the color marker. FreeTypeSettings backup;
*/ memcpy(&backup, &_freetype, sizeof(backup));
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); bool success = SetFallbackFont(&_freetype, _langpack->isocode, _langpack->winlangid);
SetDParamStr(0, err_str); if (success) {
ShowErrorMessage(INVALID_STRING_ID, STR_JUST_RAW_STRING, 0, 0); UninitFreeType();
return; 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) #if !defined(WITH_ICU)