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

This commit is contained in:
rubidium
2008-11-24 18:53:17 +00:00
parent 0f6583216b
commit dccd7886a8
52 changed files with 342 additions and 47 deletions

View File

@@ -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 char *font_name, FT_Face *face)
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.
@@ -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)
{