st-ligatures-scrollback-ringbuffer-20230105-0.9.diff (17667B)
1 diff --git a/Makefile b/Makefile 2 index 470ac86..38240da 100644 3 --- a/Makefile 4 +++ b/Makefile 5 @@ -4,7 +4,7 @@ 6 7 include config.mk 8 9 -SRC = st.c x.c 10 +SRC = st.c x.c hb.c 11 OBJ = $(SRC:.c=.o) 12 13 all: options st 14 @@ -22,7 +22,8 @@ config.h: 15 $(CC) $(STCFLAGS) -c $< 16 17 st.o: config.h st.h win.h 18 -x.o: arg.h config.h st.h win.h 19 +x.o: arg.h config.h st.h win.h hb.h 20 +hb.o: st.h 21 22 $(OBJ): config.h config.mk 23 24 diff --git a/config.mk b/config.mk 25 index 1e306f8..3e13e53 100644 26 --- a/config.mk 27 +++ b/config.mk 28 @@ -15,10 +15,12 @@ PKG_CONFIG = pkg-config 29 # includes and libs 30 INCS = -I$(X11INC) \ 31 `$(PKG_CONFIG) --cflags fontconfig` \ 32 - `$(PKG_CONFIG) --cflags freetype2` 33 + `$(PKG_CONFIG) --cflags freetype2` \ 34 + `$(PKG_CONFIG) --cflags harfbuzz` 35 LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \ 36 `$(PKG_CONFIG) --libs fontconfig` \ 37 - `$(PKG_CONFIG) --libs freetype2` 38 + `$(PKG_CONFIG) --libs freetype2` \ 39 + `$(PKG_CONFIG) --libs harfbuzz` 40 41 # flags 42 STCPPFLAGS = -DVERSION=\"$(VERSION)\" -D_XOPEN_SOURCE=600 43 diff --git a/hb.c b/hb.c 44 new file mode 100644 45 index 0000000..528c040 46 --- /dev/null 47 +++ b/hb.c 48 @@ -0,0 +1,124 @@ 49 +#include <stdlib.h> 50 +#include <stdio.h> 51 +#include <math.h> 52 +#include <X11/Xft/Xft.h> 53 +#include <X11/cursorfont.h> 54 +#include <hb.h> 55 +#include <hb-ft.h> 56 + 57 +#include "st.h" 58 +#include "hb.h" 59 + 60 +#define FEATURE(c1,c2,c3,c4) { .tag = HB_TAG(c1,c2,c3,c4), .value = 1, .start = HB_FEATURE_GLOBAL_START, .end = HB_FEATURE_GLOBAL_END } 61 +#define BUFFER_STEP 256 62 + 63 +hb_font_t *hbfindfont(XftFont *match); 64 + 65 +typedef struct { 66 + XftFont *match; 67 + hb_font_t *font; 68 +} HbFontMatch; 69 + 70 +typedef struct { 71 + size_t capacity; 72 + HbFontMatch *fonts; 73 +} HbFontCache; 74 + 75 +static HbFontCache hbfontcache = { 0, NULL }; 76 + 77 +typedef struct { 78 + size_t capacity; 79 + Rune *runes; 80 +} RuneBuffer; 81 + 82 +static RuneBuffer hbrunebuffer = { 0, NULL }; 83 + 84 +/* 85 + * Poplulate the array with a list of font features, wrapped in FEATURE macro, 86 + * e. g. 87 + * FEATURE('c', 'a', 'l', 't'), FEATURE('d', 'l', 'i', 'g') 88 + */ 89 +hb_feature_t features[] = { }; 90 + 91 +void 92 +hbunloadfonts() 93 +{ 94 + for (int i = 0; i < hbfontcache.capacity; i++) { 95 + hb_font_destroy(hbfontcache.fonts[i].font); 96 + XftUnlockFace(hbfontcache.fonts[i].match); 97 + } 98 + 99 + if (hbfontcache.fonts != NULL) { 100 + free(hbfontcache.fonts); 101 + hbfontcache.fonts = NULL; 102 + } 103 + hbfontcache.capacity = 0; 104 +} 105 + 106 +hb_font_t * 107 +hbfindfont(XftFont *match) 108 +{ 109 + for (int i = 0; i < hbfontcache.capacity; i++) { 110 + if (hbfontcache.fonts[i].match == match) 111 + return hbfontcache.fonts[i].font; 112 + } 113 + 114 + /* Font not found in cache, caching it now. */ 115 + hbfontcache.fonts = realloc(hbfontcache.fonts, sizeof(HbFontMatch) * (hbfontcache.capacity + 1)); 116 + FT_Face face = XftLockFace(match); 117 + hb_font_t *font = hb_ft_font_create(face, NULL); 118 + if (font == NULL) 119 + die("Failed to load Harfbuzz font."); 120 + 121 + hbfontcache.fonts[hbfontcache.capacity].match = match; 122 + hbfontcache.fonts[hbfontcache.capacity].font = font; 123 + hbfontcache.capacity += 1; 124 + 125 + return font; 126 +} 127 + 128 +void hbtransform(HbTransformData *data, XftFont *xfont, const Glyph *glyphs, int start, int length) { 129 + ushort mode = USHRT_MAX; 130 + unsigned int glyph_count; 131 + int rune_idx, glyph_idx, end = start + length; 132 + 133 + hb_font_t *font = hbfindfont(xfont); 134 + if (font == NULL) 135 + return; 136 + 137 + hb_buffer_t *buffer = hb_buffer_create(); 138 + hb_buffer_set_direction(buffer, HB_DIRECTION_LTR); 139 + 140 + /* Resize the buffer if required length is larger. */ 141 + if (hbrunebuffer.capacity < length) { 142 + hbrunebuffer.capacity = (length / BUFFER_STEP + 1) * BUFFER_STEP; 143 + hbrunebuffer.runes = realloc(hbrunebuffer.runes, hbrunebuffer.capacity * sizeof(Rune)); 144 + } 145 + 146 + /* Fill buffer with codepoints. */ 147 + for (rune_idx = 0, glyph_idx = start; glyph_idx < end; glyph_idx++, rune_idx++) { 148 + hbrunebuffer.runes[rune_idx] = glyphs[glyph_idx].u; 149 + mode = glyphs[glyph_idx].mode; 150 + if (mode & ATTR_WDUMMY) 151 + hbrunebuffer.runes[rune_idx] = 0x0020; 152 + } 153 + hb_buffer_add_codepoints(buffer, hbrunebuffer.runes, length, 0, length); 154 + 155 + /* Shape the segment. */ 156 + hb_shape(font, buffer, features, sizeof(features)/sizeof(hb_feature_t)); 157 + 158 + /* Get new glyph info. */ 159 + hb_glyph_info_t *info = hb_buffer_get_glyph_infos(buffer, &glyph_count); 160 + hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(buffer, &glyph_count); 161 + 162 + /* Fill the output. */ 163 + data->buffer = buffer; 164 + data->glyphs = info; 165 + data->positions = pos; 166 + data->count = glyph_count; 167 +} 168 + 169 +void hbcleanup(HbTransformData *data) { 170 + hb_buffer_destroy(data->buffer); 171 + memset(data, 0, sizeof(HbTransformData)); 172 +} 173 diff --git a/hb.h b/hb.h 174 new file mode 100644 175 index 0000000..88de9bd 176 --- /dev/null 177 +++ b/hb.h 178 @@ -0,0 +1,14 @@ 179 +#include <X11/Xft/Xft.h> 180 +#include <hb.h> 181 +#include <hb-ft.h> 182 + 183 +typedef struct { 184 + hb_buffer_t *buffer; 185 + hb_glyph_info_t *glyphs; 186 + hb_glyph_position_t *positions; 187 + unsigned int count; 188 +} HbTransformData; 189 + 190 +void hbunloadfonts(); 191 +void hbtransform(HbTransformData *, XftFont *, const Glyph *, int, int); 192 +void hbcleanup(HbTransformData *); 193 diff --git a/st.c b/st.c 194 index c44797b..91f54dc 100644 195 --- a/st.c 196 +++ b/st.c 197 @@ -2759,7 +2759,8 @@ draw(void) 198 drawregion(0, 0, term.col, term.row); 199 if (TSCREEN.off == 0) 200 xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], 201 - term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]); 202 + term.ocx, term.ocy, TLINE(term.ocy)[term.ocx], 203 + TLINE(term.ocy), term.col); 204 term.ocx = cx; 205 term.ocy = term.c.y; 206 xfinishdraw(); 207 diff --git a/st.h b/st.h 208 index 3cea73b..709a369 100644 209 --- a/st.h 210 +++ b/st.h 211 @@ -11,7 +11,8 @@ 212 #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) 213 #define DEFAULT(a, b) (a) = (a) ? (a) : (b) 214 #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) 215 -#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \ 216 +#define ATTRCMP(a, b) (((a).mode & (~ATTR_WRAP)) != ((b).mode & (~ATTR_WRAP)) || \ 217 + (a).fg != (b).fg || \ 218 (a).bg != (b).bg) 219 #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \ 220 (t1.tv_nsec-t2.tv_nsec)/1E6) 221 diff --git a/win.h b/win.h 222 index 6de960d..94679e4 100644 223 --- a/win.h 224 +++ b/win.h 225 @@ -25,7 +25,7 @@ enum win_mode { 226 227 void xbell(void); 228 void xclipcopy(void); 229 -void xdrawcursor(int, int, Glyph, int, int, Glyph); 230 +void xdrawcursor(int, int, Glyph, int, int, Glyph, Line, int); 231 void xdrawline(Line, int, int, int); 232 void xfinishdraw(void); 233 void xloadcols(void); 234 diff --git a/x.c b/x.c 235 index 9891e91..ec3567a 100644 236 --- a/x.c 237 +++ b/x.c 238 @@ -19,6 +19,7 @@ char *argv0; 239 #include "arg.h" 240 #include "st.h" 241 #include "win.h" 242 +#include "hb.h" 243 244 /* types used in config.h */ 245 typedef struct { 246 @@ -143,6 +144,7 @@ typedef struct { 247 } DC; 248 249 static inline ushort sixd_to_16bit(int); 250 +static void xresetfontsettings(ushort mode, Font **font, int *frcflags); 251 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int); 252 static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int); 253 static void xdrawglyph(Glyph, int, int); 254 @@ -759,7 +761,7 @@ xresize(int col, int row) 255 xclear(0, 0, win.w, win.h); 256 257 /* resize to new width */ 258 - xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec)); 259 + xw.specbuf = xrealloc(xw.specbuf, col * sizeof(GlyphFontSpec) * 4); 260 } 261 262 ushort 263 @@ -1064,6 +1066,9 @@ xunloadfont(Font *f) 264 void 265 xunloadfonts(void) 266 { 267 + /* Clear Harfbuzz font cache. */ 268 + hbunloadfonts(); 269 + 270 /* Free the loaded fonts in the font cache. */ 271 while (frclen > 0) 272 XftFontClose(xw.dpy, frc[--frclen].font); 273 @@ -1187,7 +1192,7 @@ xinit(int cols, int rows) 274 XFillRectangle(xw.dpy, xw.buf, dc.gc, 0, 0, win.w, win.h); 275 276 /* font spec buffer */ 277 - xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec)); 278 + xw.specbuf = xmalloc(cols * sizeof(GlyphFontSpec) * 4); 279 280 /* Xft rendering context */ 281 xw.draw = XftDrawCreate(xw.dpy, xw.buf, xw.vis, xw.cmap); 282 @@ -1241,6 +1246,22 @@ xinit(int cols, int rows) 283 xsel.xtarget = XA_STRING; 284 } 285 286 +void 287 +xresetfontsettings(ushort mode, Font **font, int *frcflags) 288 +{ 289 + *font = &dc.font; 290 + if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { 291 + *font = &dc.ibfont; 292 + *frcflags = FRC_ITALICBOLD; 293 + } else if (mode & ATTR_ITALIC) { 294 + *font = &dc.ifont; 295 + *frcflags = FRC_ITALIC; 296 + } else if (mode & ATTR_BOLD) { 297 + *font = &dc.bfont; 298 + *frcflags = FRC_BOLD; 299 + } 300 +} 301 + 302 int 303 xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y) 304 { 305 @@ -1255,119 +1276,148 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x 306 FcPattern *fcpattern, *fontpattern; 307 FcFontSet *fcsets[] = { NULL }; 308 FcCharSet *fccharset; 309 - int i, f, numspecs = 0; 310 + int i, f, length = 0, start = 0, numspecs = 0; 311 + float cluster_xp = xp, cluster_yp = yp; 312 + HbTransformData shaped = { 0 }; 313 + 314 + /* Initial values. */ 315 + mode = prevmode = glyphs[0].mode; 316 + xresetfontsettings(mode, &font, &frcflags); 317 318 for (i = 0, xp = winx, yp = winy + font->ascent; i < len; ++i) { 319 - /* Fetch rune and mode for current glyph. */ 320 - rune = glyphs[i].u; 321 mode = glyphs[i].mode; 322 323 /* Skip dummy wide-character spacing. */ 324 - if (mode == ATTR_WDUMMY) 325 + if (mode & ATTR_WDUMMY && i < (len - 1)) 326 continue; 327 328 - /* Determine font for glyph if different from previous glyph. */ 329 - if (prevmode != mode) { 330 - prevmode = mode; 331 - font = &dc.font; 332 - frcflags = FRC_NORMAL; 333 - runewidth = win.cw * ((mode & ATTR_WIDE) ? 2.0f : 1.0f); 334 - if ((mode & ATTR_ITALIC) && (mode & ATTR_BOLD)) { 335 - font = &dc.ibfont; 336 - frcflags = FRC_ITALICBOLD; 337 - } else if (mode & ATTR_ITALIC) { 338 - font = &dc.ifont; 339 - frcflags = FRC_ITALIC; 340 - } else if (mode & ATTR_BOLD) { 341 - font = &dc.bfont; 342 - frcflags = FRC_BOLD; 343 + if ( 344 + prevmode != mode 345 + || ATTRCMP(glyphs[start], glyphs[i]) 346 + || selected(x + i, y) != selected(x + start, y) 347 + || i == (len - 1) 348 + ) { 349 + /* Handle 1-character wide segments and end of line */ 350 + length = i - start; 351 + if (i == start) { 352 + length = 1; 353 + } else if (i == (len - 1)) { 354 + length = (i - start + 1); 355 } 356 - yp = winy + font->ascent; 357 - } 358 - 359 - /* Lookup character index with default font. */ 360 - glyphidx = XftCharIndex(xw.dpy, font->match, rune); 361 - if (glyphidx) { 362 - specs[numspecs].font = font->match; 363 - specs[numspecs].glyph = glyphidx; 364 - specs[numspecs].x = (short)xp; 365 - specs[numspecs].y = (short)yp; 366 - xp += runewidth; 367 - numspecs++; 368 - continue; 369 - } 370 371 - /* Fallback on font cache, search the font cache for match. */ 372 - for (f = 0; f < frclen; f++) { 373 - glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); 374 - /* Everything correct. */ 375 - if (glyphidx && frc[f].flags == frcflags) 376 - break; 377 - /* We got a default font for a not found glyph. */ 378 - if (!glyphidx && frc[f].flags == frcflags 379 - && frc[f].unicodep == rune) { 380 - break; 381 + /* Shape the segment. */ 382 + hbtransform(&shaped, font->match, glyphs, start, length); 383 + runewidth = win.cw * ((glyphs[start].mode & ATTR_WIDE) ? 2.0f : 1.0f); 384 + cluster_xp = xp; cluster_yp = yp; 385 + for (int code_idx = 0; code_idx < shaped.count; code_idx++) { 386 + int idx = shaped.glyphs[code_idx].cluster; 387 + 388 + if (glyphs[start + idx].mode & ATTR_WDUMMY) 389 + continue; 390 + 391 + /* Advance the drawing cursor if we've moved to a new cluster */ 392 + if (code_idx > 0 && idx != shaped.glyphs[code_idx - 1].cluster) { 393 + xp += runewidth; 394 + cluster_xp = xp; 395 + cluster_yp = yp; 396 + runewidth = win.cw * ((glyphs[start + idx].mode & ATTR_WIDE) ? 2.0f : 1.0f); 397 + } 398 + 399 + if (shaped.glyphs[code_idx].codepoint != 0) { 400 + /* If symbol is found, put it into the specs. */ 401 + specs[numspecs].font = font->match; 402 + specs[numspecs].glyph = shaped.glyphs[code_idx].codepoint; 403 + specs[numspecs].x = cluster_xp + (short)(shaped.positions[code_idx].x_offset / 64.); 404 + specs[numspecs].y = cluster_yp - (short)(shaped.positions[code_idx].y_offset / 64.); 405 + cluster_xp += shaped.positions[code_idx].x_advance / 64.; 406 + cluster_yp += shaped.positions[code_idx].y_advance / 64.; 407 + numspecs++; 408 + } else { 409 + /* If it's not found, try to fetch it through the font cache. */ 410 + rune = glyphs[start + idx].u; 411 + for (f = 0; f < frclen; f++) { 412 + glyphidx = XftCharIndex(xw.dpy, frc[f].font, rune); 413 + /* Everything correct. */ 414 + if (glyphidx && frc[f].flags == frcflags) 415 + break; 416 + /* We got a default font for a not found glyph. */ 417 + if (!glyphidx && frc[f].flags == frcflags 418 + && frc[f].unicodep == rune) { 419 + break; 420 + } 421 + } 422 + 423 + /* Nothing was found. Use fontconfig to find matching font. */ 424 + if (f >= frclen) { 425 + if (!font->set) 426 + font->set = FcFontSort(0, font->pattern, 427 + 1, 0, &fcres); 428 + fcsets[0] = font->set; 429 + 430 + /* 431 + * Nothing was found in the cache. Now use 432 + * some dozen of Fontconfig calls to get the 433 + * font for one single character. 434 + * 435 + * Xft and fontconfig are design failures. 436 + */ 437 + fcpattern = FcPatternDuplicate(font->pattern); 438 + fccharset = FcCharSetCreate(); 439 + 440 + FcCharSetAddChar(fccharset, rune); 441 + FcPatternAddCharSet(fcpattern, FC_CHARSET, 442 + fccharset); 443 + FcPatternAddBool(fcpattern, FC_SCALABLE, 1); 444 + 445 + FcConfigSubstitute(0, fcpattern, 446 + FcMatchPattern); 447 + FcDefaultSubstitute(fcpattern); 448 + 449 + fontpattern = FcFontSetMatch(0, fcsets, 1, 450 + fcpattern, &fcres); 451 + 452 + /* Allocate memory for the new cache entry. */ 453 + if (frclen >= frccap) { 454 + frccap += 16; 455 + frc = xrealloc(frc, frccap * sizeof(Fontcache)); 456 + } 457 + 458 + frc[frclen].font = XftFontOpenPattern(xw.dpy, 459 + fontpattern); 460 + if (!frc[frclen].font) 461 + die("XftFontOpenPattern failed seeking fallback font: %s\n", 462 + strerror(errno)); 463 + frc[frclen].flags = frcflags; 464 + frc[frclen].unicodep = rune; 465 + 466 + glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); 467 + 468 + f = frclen; 469 + frclen++; 470 + 471 + FcPatternDestroy(fcpattern); 472 + FcCharSetDestroy(fccharset); 473 + } 474 + 475 + specs[numspecs].font = frc[f].font; 476 + specs[numspecs].glyph = glyphidx; 477 + specs[numspecs].x = (short)xp; 478 + specs[numspecs].y = (short)yp; 479 + numspecs++; 480 + } 481 } 482 - } 483 - 484 - /* Nothing was found. Use fontconfig to find matching font. */ 485 - if (f >= frclen) { 486 - if (!font->set) 487 - font->set = FcFontSort(0, font->pattern, 488 - 1, 0, &fcres); 489 - fcsets[0] = font->set; 490 491 - /* 492 - * Nothing was found in the cache. Now use 493 - * some dozen of Fontconfig calls to get the 494 - * font for one single character. 495 - * 496 - * Xft and fontconfig are design failures. 497 - */ 498 - fcpattern = FcPatternDuplicate(font->pattern); 499 - fccharset = FcCharSetCreate(); 500 - 501 - FcCharSetAddChar(fccharset, rune); 502 - FcPatternAddCharSet(fcpattern, FC_CHARSET, 503 - fccharset); 504 - FcPatternAddBool(fcpattern, FC_SCALABLE, 1); 505 + /* Cleanup and get ready for next segment. */ 506 + hbcleanup(&shaped); 507 + start = i; 508 509 - FcConfigSubstitute(0, fcpattern, 510 - FcMatchPattern); 511 - FcDefaultSubstitute(fcpattern); 512 - 513 - fontpattern = FcFontSetMatch(0, fcsets, 1, 514 - fcpattern, &fcres); 515 - 516 - /* Allocate memory for the new cache entry. */ 517 - if (frclen >= frccap) { 518 - frccap += 16; 519 - frc = xrealloc(frc, frccap * sizeof(Fontcache)); 520 + /* Determine font for glyph if different from previous glyph. */ 521 + if (prevmode != mode) { 522 + prevmode = mode; 523 + xresetfontsettings(mode, &font, &frcflags); 524 + yp = winy + font->ascent; 525 } 526 - 527 - frc[frclen].font = XftFontOpenPattern(xw.dpy, 528 - fontpattern); 529 - if (!frc[frclen].font) 530 - die("XftFontOpenPattern failed seeking fallback font: %s\n", 531 - strerror(errno)); 532 - frc[frclen].flags = frcflags; 533 - frc[frclen].unicodep = rune; 534 - 535 - glyphidx = XftCharIndex(xw.dpy, frc[frclen].font, rune); 536 - 537 - f = frclen; 538 - frclen++; 539 - 540 - FcPatternDestroy(fcpattern); 541 - FcCharSetDestroy(fccharset); 542 } 543 - 544 - specs[numspecs].font = frc[f].font; 545 - specs[numspecs].glyph = glyphidx; 546 - specs[numspecs].x = (short)xp; 547 - specs[numspecs].y = (short)yp; 548 - xp += runewidth; 549 - numspecs++; 550 } 551 552 return numspecs; 553 @@ -1519,14 +1569,17 @@ xdrawglyph(Glyph g, int x, int y) 554 } 555 556 void 557 -xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) 558 +xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og, Line line, int len) 559 { 560 Color drawcol; 561 562 /* remove the old cursor */ 563 if (selected(ox, oy)) 564 og.mode ^= ATTR_REVERSE; 565 - xdrawglyph(og, ox, oy); 566 + 567 + /* Redraw the line where cursor was previously. 568 + * It will restore the ligatures broken by the cursor. */ 569 + xdrawline(line, 0, oy, len); 570 571 if (IS_SET(MODE_HIDE)) 572 return; 573 @@ -1654,18 +1707,16 @@ xdrawline(Line line, int x1, int y1, int x2) 574 Glyph base, new; 575 XftGlyphFontSpec *specs = xw.specbuf; 576 577 - numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1); 578 i = ox = 0; 579 - for (x = x1; x < x2 && i < numspecs; x++) { 580 + for (x = x1; x < x2; x++) { 581 new = line[x]; 582 if (new.mode == ATTR_WDUMMY) 583 continue; 584 if (selected(x, y1)) 585 new.mode ^= ATTR_REVERSE; 586 - if (i > 0 && ATTRCMP(base, new)) { 587 - xdrawglyphfontspecs(specs, base, i, ox, y1); 588 - specs += i; 589 - numspecs -= i; 590 + if ((i > 0) && ATTRCMP(base, new)) { 591 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x - ox, ox, y1); 592 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1); 593 i = 0; 594 } 595 if (i == 0) { 596 @@ -1674,8 +1725,10 @@ xdrawline(Line line, int x1, int y1, int x2) 597 } 598 i++; 599 } 600 - if (i > 0) 601 - xdrawglyphfontspecs(specs, base, i, ox, y1); 602 + if (i > 0) { 603 + numspecs = xmakeglyphfontspecs(specs, &line[ox], x2 - ox, ox, y1); 604 + xdrawglyphfontspecs(specs, base, numspecs, ox, y1); 605 + } 606 } 607 608 void