Accidentally wrapped some lines on patch in the previous email. Fixed
patch attached.

Eric

Author: Eric Pruitt, https://github.com/ericpruitt/
Description: This patch adds Unicode and fallback font support on top of the
Xft patch. The _drw_utf8* functions were written by Damian Okrasa for the MIT/X
licensed (http://opensource.org/licenses/MIT) terminal emulator st
(http://st.suckless.org/). While mostly functional, this patch still needs a
little work: the logic for character-grouping can probably be simplified, a
function needs to be created for calculating the drawn width of a string that
relies on fallback fonts and the TEXTW macro updated accordingly, and the
ability to manually choose fallback fonts will likely be implemented at some
point in the future.

--- drw.c	2015-02-21 11:00:34.570272492 -0800
+++ a/drw.c	2015-02-21 10:59:05.984593174 -0800
@@ -8,6 +8,54 @@
 #include "drw.h"
 #include "util.h"
 
+#define UTF_INVALID 0xFFFD
+#define UTF_SIZ 4
+
+static unsigned char utfbyte[UTF_SIZ + 1] = {0x80,    0, 0xC0, 0xE0, 0xF0};
+static unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
+static long utfmin[UTF_SIZ + 1] = {       0,    0,  0x80,  0x800,  0x10000};
+static long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+
+static long
+_drw_utf8decodebyte(const char c, size_t *i) {
+	for(*i = 0; *i < (UTF_SIZ + 1); ++(*i))
+		if(((unsigned char)c & utfmask[*i]) == utfbyte[*i])
+			return (unsigned char)c & ~utfmask[*i];
+	return 0;
+}
+
+static size_t
+_drw_utf8validate(long *u, size_t i) {
+	if(!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
+		*u = UTF_INVALID;
+	for(i = 1; *u > utfmax[i]; ++i)
+		;
+	return i;
+}
+
+static size_t
+_drw_utf8decode(const char *c, long *u, size_t clen) {
+	size_t i, j, len, type;
+	long udecoded;
+
+	*u = UTF_INVALID;
+	if(!clen)
+		return 0;
+	udecoded = _drw_utf8decodebyte(c[0], &len);
+	if(!BETWEEN(len, 1, UTF_SIZ))
+		return 1;
+	for(i = 1, j = 1; i < clen && j < len; ++i, ++j) {
+		udecoded = (udecoded << 6) | _drw_utf8decodebyte(c[i], &type);
+		if(type != 0)
+			return j;
+	}
+	if(j < len)
+		return 0;
+	*u = udecoded;
+	_drw_utf8validate(u, len);
+	return len;
+}
+
 Drw *
 drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) {
 	Drw *drw = (Drw *)calloc(1, sizeof(Drw));
@@ -20,6 +68,7 @@
 	drw->h = h;
 	drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
 	drw->gc = XCreateGC(dpy, root, 0, NULL);
+	drw->cachelen = 0;
 	XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
 	return drw;
 }
@@ -37,6 +86,12 @@
 
 void
 drw_free(Drw *drw) {
+	size_t i;
+	for (i = 0; i < drw->cachelen; i++) {
+		if (drw->fontcache[i] != drw->font) {
+			drw_font_free(drw->dpy, drw->fontcache[i]);
+		}
+	}
 	XFreePixmap(drw->dpy, drw->drawable);
 	XFreeGC(drw->dpy, drw->gc);
 	free(drw);
@@ -52,6 +107,8 @@
 	if(!(font->xfont = XftFontOpenName(dpy,screen,fontname))
 	&& !(font->xfont = XftFontOpenName(dpy,screen,"fixed")))
 		die("error, cannot load font: '%s'\n", fontname);
+	if (!(font->pattern = FcNameParse((FcChar8 *)fontname))) 
+		die("error, cannot load font pattern for font: '%s'\n", fontname);
 	font->ascent = font->xfont->ascent;
 	font->descent = font->xfont->descent;
 	font->h = font->ascent + font->descent;
@@ -94,8 +151,13 @@
 
 void
 drw_setfont(Drw *drw, Fnt *font) {
-	if(drw)
+	if(drw) {
 		drw->font = font;
+		if (!drw->cachelen) {
+			drw->fontcache[0] = font;
+			drw->cachelen = 1;
+		}
+	}
 }
 
 void
@@ -119,39 +181,136 @@
 }
 
 void
-drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) {
-	char buf[256];
-	int i, tx, ty, th, len, olen;
+drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, const char *text, int invert) {
+	char buf[1024];
+	int tx, ty, th;
 	Extnts tex;
 	Colormap cmap;
 	Visual *vis;
 	XftDraw *d;
+	Fnt *curfont, *iterfont, *nextfont;
+	size_t i, len;
+	int utf8strlen, utf8charlen;
+	long utf8codepoint = 0;
+	char *fontname;
+	const char *utf8str;
+	FcCharSet *fccharset;
+	FcPattern *fcpattern;
+	FcPattern *match;
+	XftResult result;
+	int fallback;
+	int charexists = 0;
 
 	if(!drw || !drw->scheme)
 		return;
 	XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme->fg->pix : drw->scheme->bg->pix);
 	XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
-	if(!text || !drw->font)
+	if(!text || !drw->font)
 		return;
 
-	olen = strlen(text);
-	drw_font_getexts(drw->font, text, olen, &tex);
-	th = drw->font->ascent + drw->font->descent;
-	ty = y + (h / 2) - (th / 2) + drw->font->ascent;
-	tx = x + (h / 2);
-	/* shorten text if necessary */
-	for(len = MIN(olen, sizeof buf); len && (tex.w > w - tex.h || w < tex.h); len--)
-		drw_font_getexts(drw->font, text, len, &tex);
-	if(!len)
-		return;
-	memcpy(buf, text, len);
-	if(len < olen)
-		for(i = len; i && i > len - 3; buf[--i] = '.');
+	if (!drw->cachelen) {
+		drw->fontcache[0] = drw->font;
+		drw->cachelen = 1;
+	}
 
 	cmap = DefaultColormap(drw->dpy, drw->screen);
 	vis = DefaultVisual(drw->dpy, drw->screen);
 	d = XftDrawCreate(drw->dpy, drw->drawable, vis, cmap);
-	XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, drw->font->xfont, tx, ty, (XftChar8 *)buf, len);
+	curfont = drw->font;
+
+	while (1) {
+		utf8strlen = 0;
+		utf8str = text;
+		nextfont = NULL;
+
+		while (*text) {
+			utf8charlen = _drw_utf8decode(text, &utf8codepoint, UTF_SIZ);
+			fallback = 1;
+			for (i = 0; i < drw->cachelen; i++) {
+				iterfont = drw->fontcache[i];
+				charexists = charexists || XftCharExists(drw->dpy, iterfont->xfont, utf8codepoint);
+				if (charexists) {
+					if (iterfont == curfont) {
+						utf8strlen += utf8charlen;
+						text += utf8charlen;
+						fallback = 0;
+					} else {
+						nextfont = iterfont;
+					}
+
+					break;
+				}
+			}
+
+			if (fallback || (nextfont && nextfont != curfont)) {
+				break;
+			}
+
+			charexists = 0;
+		}
+
+		if (utf8strlen) {
+			drw_font_getexts(curfont, utf8str, utf8strlen, &tex);
+			/* shorten text if necessary */
+			for(len = MIN(utf8strlen, sizeof buf); len && (tex.w > w - tex.h || w < tex.h); len--)
+				drw_font_getexts(curfont, utf8str, len, &tex);
+			if(!len)
+				break;
+			memcpy(buf, utf8str, len);
+			buf[len] = '\0';
+			if(len < utf8strlen)
+				for(i = len; i && i > len - 3; buf[--i] = '.');
+
+			th = curfont->ascent + curfont->descent;
+			ty = y + (h / 2) - (th / 2) + curfont->ascent;
+			tx = x + (h / 2);
+
+			XftDrawStringUtf8(d, invert ? &drw->scheme->bg->rgb : &drw->scheme->fg->rgb, curfont->xfont, tx, ty, (XftChar8 *)buf, len);
+			x += tex.w;
+			w -= tex.w;
+		}
+
+		if (!*text) {
+			break;
+		} else if (nextfont) {
+			curfont = nextfont;
+			charexists = 0;
+		} else {
+			// Regardless of whether or not a fallback font is found, the
+			// character must be drawn.
+			charexists = 1;
+
+			if (drw->cachelen >= DRW_FONT_CACHE_SIZE) {
+				continue;
+			}
+
+			fccharset = FcCharSetCreate();
+			FcCharSetAddChar(fccharset, utf8codepoint);
+
+			fcpattern = FcPatternDuplicate(drw->font->pattern);
+			FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
+
+			FcConfigSubstitute(0, fcpattern, FcMatchPattern);
+			FcDefaultSubstitute(fcpattern);
+			match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
+			FcPatternDestroy(fcpattern);
+
+			if (match) {
+				FcPatternDel(match, FC_CHARSET);
+				fontname = (char *)FcNameUnparse(match);
+				FcPatternDestroy(match);
+
+				curfont = drw_font_create(drw->dpy, drw->screen, fontname);
+				if (XftCharExists(drw->dpy, curfont->xfont, utf8codepoint)) {
+					drw->fontcache[drw->cachelen++] = curfont;
+				} else {
+					drw_font_free(drw->dpy, curfont);
+					curfont = drw->font;
+				}
+			}
+		}
+	}
+
 	XftDrawDestroy(d);
 }
 
--- drw.h.old	2015-02-21 11:07:03.052095420 -0800
+++ drw.h	2015-02-21 11:09:03.444941484 -0800
@@ -1,4 +1,5 @@
 /* See LICENSE file for copyright and license details. */
+#define DRW_FONT_CACHE_SIZE 32
 
 typedef struct {
 	unsigned long pix;
@@ -15,6 +16,7 @@
 	int descent;
 	unsigned int h;
 	XftFont *xfont;
+	FcPattern *pattern;
 } Fnt;
 
 typedef struct {
@@ -32,6 +34,8 @@
 	GC gc;
 	ClrScheme *scheme;
 	Fnt *font;
+	size_t cachelen;
+	Fnt *fontcache[DRW_FONT_CACHE_SIZE];
 } Drw;
 
 typedef struct {
diff --git a/util.h b/util.h
index 033700c..f7ce721 100644
--- a/util.h
+++ b/util.h
@@ -2,5 +2,6 @@
 
 #define MAX(A, B)               ((A) > (B) ? (A) : (B))
 #define MIN(A, B)               ((A) < (B) ? (A) : (B))
+#define BETWEEN(X, A, B)        ((A) <= (X) && (X) <= (B))
 
 void die(const char *errstr, ...);

Reply via email to