Subject: Re: bug#67533: SVG images confound position pixel measurements

From: Eli Zaretskii <[email-masked]>
Date: Sun, 03 Dec 2023 15:02:21 +0200

> From: JD Smith <[email-masked]>
> Date: Sat, 2 Dec 2023 22:04:25 -0500
> Cc: [email-masked]
> 
> Progress.  The latest patch had two small hunks fail (see below), but they were simple enough that I was able to insert them by hand and recompile.
> 
> The good news is that this patch does on my end fix all the problems with the generated SVGs buffer from my recent test, at all window widths.  Unfortunately, checking my original file where this issue was revealed, there are still quite a few “false zero height above” reports.  Happily in that file there are no longer any of the “overly large height” variety, so this particular issue has apparently has been fixed by your patch.
> 
> I’ve placed a new test that generates a minimal buffer with a single :file-based SVG overlay at the same gist, and copied/attached them below, for posterity.  I’ve also fixed the bug in my/check-buffer-pixel-values which caused the report buffer not to appear (due to end-of-buffer being signaled), so please update that.  And please note the new svg_test.svg file, to be placed in the same directory.
> 
> I don’t think there should be anything special about this particular SVG file; it was automatically generated from the underlying latex fragment.  But it seems to be confusing the display engine somehow (as do many others).

There _is_ something special about that SVG: it is wider that 1/4th of
the default frame width.  If you widen the frame to be wider than
4*209=836 pixels, the problem disappears.  Such wide images are
handles specially by the display engine.

The cumulative patch below should fix all the problems you threw on me
till now.

diff --git a/src/xdisp.c b/src/xdisp.c
index 0b2508c..ca85838 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -11436,7 +11436,7 @@ window_text_pixel_size (Lisp_Object window, Lisp_Object from, Lisp_Object to,
       /* Start at the beginning of the line containing FROM.  Otherwise
 	 IT.current_x will be incorrectly set to zero at some arbitrary
 	 non-zero X coordinate.  */
-      reseat_at_previous_visible_line_start (&it);
+      move_it_by_lines (&it, 0);
       it.current_x = it.hpos = 0;
       if (IT_CHARPOS (it) != start)
 	{
@@ -11513,6 +11513,8 @@ window_text_pixel_size (Lisp_Object window, Lisp_Object from, Lisp_Object to,
      the width of the last buffer position manually.  */
   if (IT_CHARPOS (it) > end)
     {
+      int end_y = it.current_y;
+
       end--;
       RESTORE_IT (&it, &it2, it2data);
       x = move_it_to (&it, end, to_x, max_y, -1, move_op);
@@ -11525,14 +11527,29 @@ window_text_pixel_size (Lisp_Object window, Lisp_Object from, Lisp_Object to,
 
 	  /* DTRT if ignore_line_at_end is t.  */
 	  if (!NILP (ignore_line_at_end))
-	    doff = (max (it.max_ascent, it.ascent)
-		    + max (it.max_descent, it.descent));
+	    {
+	      /* If END-1 is on the previous screen line, we need to
+                 account for the vertical dimensions of previous line.  */
+	      if (it.current_y < end_y)
+		doff = (max (it.max_ascent, it.ascent)
+			+ max (it.max_descent, it.descent));
+	    }
 	  else
 	    {
 	      it.max_ascent = max (it.max_ascent, it.ascent);
 	      it.max_descent = max (it.max_descent, it.descent);
 	    }
 	}
+      else if (IT_CHARPOS (it) > end
+	       && it.line_wrap == TRUNCATE
+	       && it.current_x - it.first_visible_x >= it.last_visible_x)
+	{
+          /* If the display property at END is at the beginning of the
+             line, and the previous line was truncated, we are at END,
+             but it.current_y is not yet updated to reflect that.  */
+          it.current_y += max (it.max_ascent, it.ascent)
+                          + max (it.max_descent, it.descent);
+	}
     }
   else
     bidi_unshelve_cache (it2data, true);
@@ -31343,9 +31360,13 @@ produce_image_glyph (struct it *it)
 
   take_vertical_position_into_account (it);
 
-  /* Automatically crop wide image glyphs at right edge so we can
-     draw the cursor on same display row.  */
-  if ((crop = it->pixel_width - (it->last_visible_x - it->current_x), crop > 0)
+  /* Automatically crop wide image glyphs at right edge so we can draw
+     the cursor on same display row.  But don't do that under
+     word-wrap, unless the image starts at column zero, because
+     wrapping correctly needs the real pixel width of the image.  */
+  if ((it->line_wrap != WORD_WRAP || it->hpos == 0)
+      && (crop = it->pixel_width - (it->last_visible_x - it->current_x),
+	  crop > 0)
       && (it->hpos == 0 || it->pixel_width > it->last_visible_x / 4))
     {
       it->pixel_width -= crop;