APPROVE COMMIT
NOTE: This patch has been committed.
Giacomo, this should make the crash you were seeing on startup go away.
Font names and foundries in fontconfig are always UTF-8--see
FcFreeTypeQuery, in fcfreetype.c, for where it does this conversion. I don’t
believe this is documented anywhere, but accepting this is the only way to
get sane non-ASCII font name behaviour.
Unfortunately, the madly verbose version of the font specification with full
character coverage information as a bitmap, which we have been using up to
this point, can’t be interpreted as UTF-8. So, we use a slightly less
verbose font spec instead.
src/ChangeLog addition:
2005-04-01 Aidan Kehoe <kehoea(a)parhasard.net>
* objects-x.c:
* objects-x.c (x_initialize_font_instance):
* objects-x.c (CHECKING_LANG):
General mule-sanity cleanup for the debug messages, to eliminate
the issue Giacomo Boffi saw in
16970.44359.621213.994821(a)boffi95.stru.polimi.it.
* objects-x.c (x_find_charset_font):
Font names are also treated as UTF-8; relatedly, when passing back
the font's full name, the character coverage bitmap isn't included
any more, because that would make it an invalid UTF-8 string.
XEmacs sjt-xft source patch:
Diff command: cvs -q diff -u
Files affected: src/objects-x.c
Index: src/objects-x.c
===================================================================
RCS file: /pack/xemacscvs/XEmacs/xemacs/src/objects-x.c,v
retrieving revision 1.26.2.15
diff -u -u -r1.26.2.15 objects-x.c
--- src/objects-x.c 2005/03/15 05:24:35 1.26.2.15
+++ src/objects-x.c 2005/04/01 00:18:16
@@ -221,7 +221,8 @@
#ifdef USE_XFT
if (debug_xft > 2)
- stderr_out ("attempting to initialize font spec %s\n", extname);
+ stderr_out ("attempting to initialize font spec %s\n",
+ XSTRING_DATA(f->name));
/* #### serialize (optimize) these later... */
/* #### This function really needs to go away.
The problem is that the fontconfig/Xft functions work much too hard
@@ -288,13 +289,15 @@
stderr_out ("initialized metrics ascent %d descent %d width %d height %d\n",
f->ascent, f->descent, f->width, f->height);
if (debug_xft > 2) /* we also output on initialization of any font below */
- stderr_out ("initialized Xft font %s\n", extname);
+ stderr_out ("initialized Xft font %s\n",
+ XSTRING_DATA(f->name));
fs = NULL; /* we don' need no steenkin' X font */
}
else
{
if (debug_xft > 0)
- stderr_out ("couldn't initialize Xft font %s\n", extname);
+ stderr_out ("couldn't initialize Xft font %s\n",
+ XSTRING_DATA(f->name));
}
#endif
@@ -369,11 +372,11 @@
/* #### check for weirdness */
if (n * f->height < d * f->width)
stderr_out ("font %s: width:height is %d:%d, larger than %d:%d\n",
- extname, f->width, f->height, n, d);
+ XSTRING_DATA(f->name), f->width, f->height, n, d);
if (f->height <= 0 || f->width <= 0)
stderr_out ("bogus dimensions of font %s: width = %d, height = %d\n",
- extname, f->width, f->height);
- stderr_out ("initialized font %s\n", extname);
+ XSTRING_DATA(f->name), f->width, f->height);
+ stderr_out ("initialized font %s\n", XSTRING_DATA(f->name));
}
#else
#undef rf
@@ -1114,12 +1117,19 @@
/* print an Xft pattern to stderr
LEVEL is the debug level (to compare to debug_xft)
FORMAT is a newline-terminated printf format with one %s for the pattern
- PATTERN is an FcPattern *. */
-#define PRINT_XFT_PATTERN(level,format,pattern) \
- do { \
- FcChar8 *name = FcNameUnparse (pattern); \
- DEBUG_XFT1 (level, format, name); \
- free (name); \
+ PATTERN is an FcPattern *.
+
+ Qbinary is the only coding system that's appropriate; while the font
+ names themselves are UTF-8, the coverage information is a pure bitmap
+ that won't be in any valid multibyte encoding. */
+#define PRINT_XFT_PATTERN(level,format,pattern) \
+ do { \
+ DECLARE_EISTRING (eistrpxft_name); \
+ FcChar8 *name = FcNameUnparse (pattern); \
+ \
+ eicpy_ext(eistrpxft_name, name, Qbinary); \
+ DEBUG_XFT1 (level, format, eidata(eistrpxft_name)); \
+ free (name); \
} while (0)
/* print a progress message
@@ -1127,8 +1137,13 @@
FORMAT is a newline-terminated printf format with two %s for font and lang
FONT is the Xft font
LANG is the language being checked for support. */
-#define CHECKING_LANG(level,font,lang) \
- DEBUG_XFT2 (level, "checking if %s handles %s\n", font, lang)
+#define CHECKING_LANG(level,font,lang) \
+ do { \
+ DECLARE_EISTRING (eistrcl_name); \
+ eicpy_ext(eistrcl_name, font, Qbinary); \
+ DEBUG_XFT2 (level, "checking if %s handles %s\n", \
+ eidata(eistrcl_name), lang); \
+ } while (0)
struct charset_reporter {
Lisp_Object *charset;
@@ -1195,10 +1210,14 @@
return Qnil;
#ifdef USE_XFT
- /* #### does Xft permit/require a different encoding? */
- LISP_STRING_TO_EXTERNAL (font, patternext, Qx_font_name_encoding);
+ /* Fontconfig converts all strings to UTF-8 before passing them back to
+ callers--see FcFreeTypeQuery, in fcfreetype.c, for where it does
+ this. I don't believe this is documented. */
+
+ LISP_STRING_TO_EXTERNAL (font, patternext, Qutf_8);
- DEBUG_XFT1 (1, "confirming charset for font instance %s\n", patternext);
+ DEBUG_XFT1 (1, "confirming charset for font instance %s\n",
+ XSTRING_DATA(font));
/* #### this looks like a fair amount of work, but the basic design
has never been rethought, and it should be
@@ -1216,9 +1235,11 @@
FcChar8 *lang = "en";
FcCharSet *fccs = NULL;
FcChar8 *shortname;
+ DECLARE_EISTRING (eistr_shortname);
fcc = FcConfigGetCurrent ();
patternxft = FcNameParse (patternext); /* #### needs freeing */
+
PRINT_XFT_PATTERN (1,"FcNameParse'ed name is %s\n",patternxft);
/* #### Next two return FcBool, but what does the return mean? */
/* The order is correct according the fontconfig docs. */
@@ -1236,6 +1257,8 @@
FcPatternDel (p, FC_LANG);
shortname = FcNameUnparse (p); /* #### needs freeing */
FcPatternDestroy (p);
+ eicpy_ext(eistr_shortname, shortname, Qutf_8);
+ free(shortname);
}
/* The language approach may better in the long run, but we can't use
@@ -1254,7 +1277,7 @@
if (cr->rfc3066)
{
- CHECKING_LANG (0, shortname, cr->language);
+ CHECKING_LANG (0, eidata(eistr_shortname), cr->language);
lang = cr->rfc3066;
}
else if (cr->charset)
@@ -1275,6 +1298,8 @@
/* default to "en" */
}
+ ASSERT_ASCTEXT_ASCII(lang);
+
if (fccs)
{
/* check for character set coverage */
@@ -1285,6 +1310,7 @@
if (r == FcResultTypeMismatch)
{
+ free(patternxft);
/* Urk! Fall back and punt to core font. */
DEBUG_XFT0 (0, "Unexpected type return in charset value\n");
/* uncomment this to not try core font */
@@ -1292,14 +1318,43 @@
}
else if (r == FcResultMatch && FcCharSetIsSubset (fccs, v))
{
- DEBUG_XFT2 (0, "Xft font %s supports %s\n", shortname, lang);
- /* #### would it be better to return shortname? */
- retval = (build_string (FcNameUnparse (patternxft)));
+ FcChar8 *unparsed = FcNameUnparse(patternxft);
+ DECLARE_EISTRING (eistr_longname);
+
+#ifdef __GNUC__
+#warning "Perhaps the shortname won't identify the font uniquely--see comment."
+#endif
+ /* If we're going to use the full font name above, we can't
+ treat it as any other encoding than Qbinary, because the
+ bitmap of character coverage which forms part of it gives
+ no guarantees that it'll encode as anything else.
+
+ However, fontconfig returns foundry and name information in
+ UTF-8. So the shortname above will better correspond to the
+ font's name, because we can treat it as valid UTF-8, and
+ non-ASCII characters will show up properly as Mule
+ characters.
+
+ So, in the interest of getting XEmacs' font selection
+ almost coherent, I'm passing back the short name, as a Lisp
+ String, decoded from UTF-8. */
+
+ eicpy_ext(eistr_longname, unparsed, Qbinary);
+ free(unparsed);
+ free(patternxft);
+
+ DEBUG_XFT2 (0, "Xft font, full specification, %s supports %s\n",
+ eidata(eistr_longname), lang);
+ DEBUG_XFT1 (0, "We're returning the short specification %s, in the interest of preserving the encoding of the font's name. ",
+ eidata(eistr_shortname));
+
+ retval = eimake_string(eistr_shortname);
}
else
{
+ free(patternxft);
DEBUG_XFT2 (0, "Xft font %s doesn't support %s\n",
- shortname, lang);
+ eidata(eistr_shortname), lang);
/* comment this to try the core font */
retval = Qnil;
}
@@ -1316,10 +1371,13 @@
FcValue v;
/* the main event */
FcResult r = FcPatternGet (patternxft, FC_LANG, i, &v);
+ /* Not using patternxft beyond here. */
+
if (r == FcResultMatch)
{
if (v.type != FcTypeLangSet) /* excessive paranoia */
{
+ ASSERT_ASCTEXT_ASCII(FcTypeOfValueToString(v));
/* Urk! Fall back and punt to core font. */
DEBUG_XFT1 (0, "Unexpected type of lang value (%s)\n",
FcTypeOfValueToString (v));
@@ -1328,23 +1386,32 @@
}
else if (FcLangSetHasLang (v.u.l, lang) != FcLangDifferentLang)
{
- DEBUG_XFT2 (0, "Xft font %s supports %s\n", shortname, lang);
- /* #### would it be better to return shortname? */
- return (build_string (FcNameUnparse (patternxft)));
+ DEBUG_XFT2 (0, "Xft font %s supports %s\n",
+ eidata(eistr_shortname), lang);
+ free(patternxft);
+ /* #### would it be better to return shortname?
+
+ Aidan says yes. The full pattern with the bitmap
+ coverage is massively unwieldy. */
+
+ return eimake_string(eistr_shortname);
}
else
{
DEBUG_XFT2 (0, "Xft font %s doesn't support %s\n",
- shortname, lang);
+ eidata(eistr_shortname), lang);
return Qnil;
}
}
+ ASSERT_ASCTEXT_ASCII(FcResultToString(r));
DEBUG_XFT1 (0, "Unexpected result getting lang=%s\n",
FcResultToString (r));
+ free(patternxft);
}
}
- DEBUG_XFT1 (0, "shit happens, try X11 charset match for %s\n", patternext);
+ DEBUG_XFT1 (0, "shit happens, try X11 charset match for %s\n",
+ XSTRING_DATA(font));
#endif /* USE_XFT */
names = XListFonts (DEVICE_X_DISPLAY (XDEVICE (device)),
--
“I, for instance, am gung-ho about open source because my family is being
held hostage in Rob Malda’s basement. But who fact-checks me, or Enderle,
when we say something in public? No-one!” -- Danny O’Brien