--- xftfont.c.orig 2012-04-12 15:18:25.108011233 +0800 +++ xftfont.c 2012-04-12 16:35:50.993433916 +0800 @@ -61,6 +61,7 @@ Display *display; int screen; XftFont *xftfont; + FRAME_PTR frame; /* hold frame ptr, cjk double width fix need it */ }; /* Structure pointed by (struct face *)->extra */ @@ -137,6 +138,70 @@ } +static int default_font_width; + +static int is_cjk_font(struct xftfont_info *); +static int calc_cjk_padding(int char_width); +static void get_default_font_width(struct xftfont_info *); + +/* Check whether the font contains CJK Ideograph 'number one', 0x4E00, + It should be ok for Chinese/Japanese font. + Or font contains Korean script syllable 'Ka',0xAC00, + because Korean fonts may not have any Chinese characters at all. + codes from xterm.*/ +static int +is_cjk_font(struct xftfont_info *xftfont_info) +{ + if(XftCharExists(xftfont_info->display, xftfont_info->xftfont, 0x4E00) || + XftCharExists(xftfont_info->display, xftfont_info->xftfont, 0xAC00)) + return 1; + return 0; +} + +/* Caculate the width padding according to default font width */ +static int +calc_cjk_padding(int char_width) +{ + int padding = 0; + if( default_font_width == 0 || /* the default font is not a monospace font */ + char_width < default_font_width || /* almost impossible */ + char_width == default_font_width) /* ascii glyph from real monospace CJK font */ + return 0; + /* get the padding, all cjk symbols is double width */ + padding = default_font_width * 2 - char_width; + /* 1, auto matched CJK pcf/bdf fonts may bigger than 2 * default_font_width. + 2, User may set a very big font size for script HAN. + Keep it unchanged, should NOT adjust default monospace font width. */ + return padding > 0 ? padding : 0; +} + +/* Hmm, This function is dirty.... + Anyway, it fix text-scale-increase/decrease CJK double width issue. + Emacs should provide a way to get the default font width of frame when scale happened, but how? + After I tried FRAME_* macros, I give up, It is initialized when first font loaded and never changed. + + This is the root cause why we need this function and "default_font_width" global variable. + here we get the default font family and compare with every xftfont_info structure, + If they are same, we catch the default font, then set "default_font_width" to new value. +*/ +static void +get_default_font_width(struct xftfont_info *xftfont_info) +{ + struct font *defaultfont = NULL; + if(defaultfont = FRAME_FONT(xftfont_info->frame)){ /* default font of frame */ + Lisp_Object default_font_object; + Lisp_Object xftfont_object; + XSETFONT (default_font_object, defaultfont); + XSETFONT (xftfont_object, &xftfont_info->font); + if(!NILP(AREF (default_font_object, FONT_FAMILY_INDEX)) && + !NILP(Fstring_equal (AREF (default_font_object, FONT_FAMILY_INDEX), + AREF (xftfont_object, FONT_FAMILY_INDEX) )) && + INTEGERP (AREF (default_font_object, FONT_SPACING_INDEX)) && + XINT(AREF (default_font_object, FONT_SPACING_INDEX)) == FONT_SPACING_MONO) /* ensure monospace */ + default_font_width = xftfont_info->font.space_width; + } +} + static Lisp_Object xftfont_list (Lisp_Object, Lisp_Object); static Lisp_Object xftfont_match (Lisp_Object, Lisp_Object); static Lisp_Object xftfont_open (FRAME_PTR, Lisp_Object, int); @@ -391,6 +456,10 @@ xftfont_info->display = display; xftfont_info->screen = FRAME_X_SCREEN_NUMBER (f); xftfont_info->xftfont = xftfont; + /* to fix CJK double width alignment issue. + pass FRAME_PTR to every xftfont_info structure + Later, we can get default font of the frame via the ptr */ + xftfont_info->frame = f; /* This means that there's no need of transformation. */ xftfont_info->matrix.xx = 0; if (FcPatternGetMatrix (xftfont->pattern, FC_MATRIX, 0, &matrix) @@ -593,20 +662,30 @@ { struct xftfont_info *xftfont_info = (struct xftfont_info *) font; XGlyphInfo extents; - + int cjk_padding = 0; + int l_padding = 0; + int r_padding = 0; BLOCK_INPUT; XftGlyphExtents (xftfont_info->display, xftfont_info->xftfont, code, nglyphs, &extents); + /* get the default font width of the frame + and set default_font_width global variable used later by calc_cjk_padding. */ + get_default_font_width(xftfont_info); + if(is_cjk_font(xftfont_info)) + cjk_padding = calc_cjk_padding(extents.xOff); + /* cjk_padding may equals to 0, then all is zero, still ok */ + l_padding = cjk_padding >> 1; /* get half */ + r_padding = cjk_padding - l_padding; /* cjk_padding maybe not divided by 2 exactly */ UNBLOCK_INPUT; if (metrics) { - metrics->lbearing = - extents.x; - metrics->rbearing = - extents.x + extents.width; - metrics->width = extents.xOff; + metrics->lbearing = - extents.x - l_padding; + metrics->rbearing = - extents.x + extents.width + r_padding; + metrics->width = extents.xOff + cjk_padding; metrics->ascent = extents.y; metrics->descent = extents.height - extents.y; } - return extents.xOff; + return extents.xOff + cjk_padding; } static XftDraw * @@ -660,13 +739,33 @@ code[i] = ((XCHAR2B_BYTE1 (s->char2b + from + i) << 8) | XCHAR2B_BYTE2 (s->char2b + from + i)); + /* get the default font width of the frame + and set default_font_width global variable used later by calc_cjk_padding. */ + get_default_font_width(xftfont_info); + if (s->padding_p) for (i = 0; i < len; i++) XftDrawGlyphs (xft_draw, &fg, xftfont_info->xftfont, x + i, y, code + i, 1); - else - XftDrawGlyphs (xft_draw, &fg, xftfont_info->xftfont, - x, y, code, len); + else { + if(!is_cjk_font(xftfont_info)) /*not CJK, draw glyphs like before*/ + XftDrawGlyphs (xft_draw, &fg, xftfont_info->xftfont, x, y, code, len); + else /* draw glyphs one by one and adjust the offset */ + for (i = 0; i < len; i++) { + int cjk_padding = 0; + int offset = 0; + XGlyphInfo extents; + XftGlyphExtents (xftfont_info->display, xftfont_info->xftfont, code+i, 1, + &extents); + cjk_padding = calc_cjk_padding(extents.xOff); + if(cjk_padding) /* CJK symbol and have padding */ + offset = default_font_width * i * 2 + (cjk_padding>>1); + else /* No padding, just use extents.xOff directly, real monospace CJK font works */ + offset = extents.xOff * i; + XftDrawGlyphs (xft_draw, &fg, xftfont_info->xftfont, + x+offset, y, code+i, 1); + } + } UNBLOCK_INPUT; return len;