Xlib tutorial part 8 -- a different way to reach wide characters

by Alan at Mon 2nd Mar 2009 1:00AM EST

Hello,

It's unfortunate, but even today, almost twenty years after XFontSet was created, sometimes it doesn't work properly. You might not have the proper fonts installed, the locale isn't installed properly, whatever. So we're going to try another tack, going back to XFontStruct and friends, but using XChar2b to reach beyond the ASCII characters.

First we'll need a way of translating UTF8 to XChar2b. This works with the knowledge of how UTF8 is structured. First, ASCII characters map to themselvs. For everything else up to 21 bits, the first byte has the high x bits all set to 1 with a 0 in the next highest bit where x is the number of bytes to display the item using UTF8. 8-11 bits requires two bytes, 12-16 bits requires 3 bytes and 17-21 bits requires 4 bytes. Unfortunately, XChar2b only supports up to 16 bits, so 17-21 will be ignored (which means all Chinese, Japanese and Korean characters). The remaining bits in the first byte are the high bits of the character code. Subsequent bytes have 1 in the high bit and 0 in the next highest bit, followed by 6 bits.


	...
int utf8toXChar2b(XChar2b *output_r, int outsize, const char *input, int inlen){
	int j, k;
	for(j =0, k=0; j < inlen && k < outsize; j ++){
		unsigned char c = input[j];
		if (c < 128)  {
			output_r[k].byte1 = 0;
			output_r[k].byte2 = c; 
			k++;
		} else if (c < 0xC0) {
			/* we're inside a character we don't know  */
			continue;
		} else switch(c&0xF0){
		case 0xC0: case 0xD0: /* two bytes 5+6 = 11 bits */
			if (inlen < j+1){ return k; }
			output_r[k].byte1 = (c&0x1C) >> 2;
			j++;
			output_r[k].byte2 = ((c&0x3) << 6) + (input[j]&0x3F);
			k++;
			break;
		case 0xE0: /* three bytes 4+6+6 = 16 bits */ 
			if (inlen < j+2){ return k; }
			j++;
			output_r[k].byte1 = ((c&0xF) << 4) + ((input[j]&0x3C) >> 2);
			c = input[j];
			j++;
			output_r[k].byte2 = ((c&0x3) << 6) + (input[j]&0x3F);
			k++;
			break;
		case 0xFF:
			/* the character uses more than 16 bits */
			continue;
		}
	}
	return k;
}
	...
	

Most of the rest of the differences since the last section are to move back from not using XFontSet to XFontStruct *. What is different is in the main_loop() function.


	...
	int strlength = strlen(text);

	/* may be too big, but definitely big enough */
	string = malloc(sizeof(*string) * strlen(text));
	strlength = utf8toXChar2b(string, strlength, text, strlength);
	printf("%d
", strlength);

	text_width = XTextWidth16(font, string, strlength);
	printf("%d
", text_width);
	font_ascent = font->ascent;

	/* as each event that we asked about occurs, we respond. */
	...
	

What we're doing here is converting our string to an array of XChar2b, calculating its width and storing the font ascent. Notice the use of XTextWidth16() instead of XTextWidth() or Xutf8TextEscapement()

Then in the response to an Expose event,


		...
		case Expose:
			if (ev.xexpose.count > 0) break;
			XDrawLine(dpy, ev.xany.window, pen, 0, 0, width/2-text_width/2, height/2);
			XDrawLine(dpy, ev.xany.window, pen, width, 0, width/2+text_width/2, height/2);
			XDrawLine(dpy, ev.xany.window, pen, 0, height, width/2-text_width/2, height/2);
			XDrawLine(dpy, ev.xany.window, pen, width, height, width/2+text_width/2, height/2);
   			textx = (width - text_width)/2;
   			texty = (height + font_ascent)/2;
   			XDrawString16(dpy, ev.xany.window, pen, textx, texty, string, strlength);
			break;
		...
	

We are again drawing lines as we did before, and use XDrawString16() instead of Xutf8DrawString() or XDrawString(). Download the full code.

And that's it. Next lesson will be a bit more interesting since we're going to start interacting with the user.

Things to try:

Comments are closed.