Subject: bug#73752: 29.4; Ligatures are randomly rendered with extra spaces

From: Tim Ruffing <[email-masked]>
Date: Sat, 02 Nov 2024 04:34:17 +0100

Okay, here's what I think is a breakthrough:

> > There are a few calls to lglyph-set-adjustment in composite.el,
> > which
> > could be responsible for this.  So if the breakpoint inside
> > composition_gstring_adjust_zero_width never breaks in your
> > sessions,
> > either, the next step is to put a watchpoint on the cached
> > composition
> > created by font-shape-gstring and wait for some code to modify the
> > adjustment part.
> 

Neither the critical line in composition_gstring_adjust_zero_width nor
the calls to lglyph-set-adjustment in composite.el are triggered. But
this hack in hbfont_shape means I can't reproduce the bug anymore using
Yixuan's init file:

diff --git a/src/hbfont.c b/src/hbfont.c
index 37ed4132492..6fae513069a 100644
--- a/src/hbfont.c
+++ b/src/hbfont.c
@@ -592,11 +592,12 @@ hbfont_shape (Lisp_Object lgstring, Lisp_Object
direction)
       LGLYPH_SET_ASCENT (lglyph, metrics.ascent);
       LGLYPH_SET_DESCENT (lglyph, metrics.descent);
 
       xoff = lround (pos[i].x_offset * position_unit);
       yoff = - lround (pos[i].y_offset * position_unit);
-      wadjust = lround (pos[i].x_advance * position_unit);
+      /* wadjust = lround (pos[i].x_advance * position_unit); */
+      wadjust = metrics.width;
       if (xoff || yoff || wadjust != metrics.width)
 	LGLYPH_SET_ADJUSTMENT (lglyph, CALLN (Fvector,
 					     make_fixnum (xoff),
 					     make_fixnum (yoff),
 					     make_fixnum (wadjust)));

Without this patch, what I see with his init file matches his
screenshots in the initial bug report (with the same ligatures
affected, as far as I can see), namely a single additional "space" at
the end of the ligature. Let me note again that this is different from
I typically see when I use emacs daily, namely a space after (almost?)
every character which is part of the ligature. We may or may not be
chasing multiple bugs here.

Anyway, I tried adding a printf inside the critical "if" in hbfont.c.
This is what I see when the bug triggers:

xoff: 0, yoff: 0, wadjust: 18, metrics.width: 9, pos[i].x_advance * position_unit: 18.000000
xoff: 0, yoff: 0, wadjust: 18, metrics.width: 9, pos[i].x_advance * position_unit: 18.000000
xoff: 0, yoff: 0, wadjust: 18, metrics.width: 9, pos[i].x_advance * position_unit: 18.000000
xoff: 0, yoff: 0, wadjust: 18, metrics.width: 9, pos[i].x_advance * position_unit: 18.000000
xoff: 0, yoff: 0, wadjust: 18, metrics.width: 9, pos[i].x_advance * position_unit: 18.000000
xoff: 0, yoff: 0, wadjust: 18, metrics.width: 9, pos[i].x_advance * position_unit: 18.000000
xoff: 0, yoff: 0, wadjust: 18, metrics.width: 9, pos[i].x_advance * position_unit: 18.000000

So this is not a rounding error at least. ;)

Much more interesting:
I can reproduce this only when "enough" (or I guess, the right)
ligatures are visible in the buffer. With Yixuan's init file, when I
not only load but also visit this init file -- just for testing the
rendering, because it contains relevant ligatures -- then I can trigger
the bug. But I can't trigger it with a reduced version (that notably
doesn't contain arrow ligatures)!

Eli, perhaps this is precise enough already so that you can reproduce
it? See the attached archive: Use it as init-dir. It contains the init
script (but I removed the automatic repeat). Visit bad.el. Press C=# to
initialize the font randomization, and then keep pressing C-RET until
you hopefully hit the bug. And I can't reproduce the bug with good.el
that has fewer ligatures.

So it seems that harfbuzz gives different results for some composition
depending on the "context", i.e., the other characters (and ligatures?)
that appear in the buffer passed to harfbuzz. Is this a bug in
harfbuzz?

If you still can't reproduce, what's a good way to pretty print the
lgstring passed to hbfont_shape? (I'd prefer changing the code, that's
a bit easier than gdb, at least for me.)