src/fontcache.cpp
changeset 10367 0098392a5560
parent 10056 48659f7d4fa5
child 10369 66a67e98e67f
--- a/src/fontcache.cpp	Mon Nov 24 11:20:07 2008 +0000
+++ b/src/fontcache.cpp	Mon Nov 24 18:53:17 2008 +0000
@@ -154,8 +154,68 @@
 	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 @@
 
 	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 @@
 	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 @@
  */
 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)
 {