diff src/redisplay.c @ 819:6504113e7c2d

[xemacs-hg @ 2002-04-25 18:03:23 by andyp] sync up windows branch from 21.4
author andyp
date Thu, 25 Apr 2002 18:04:24 +0000
parents a5954632b187
children 6728e641994e
line wrap: on
line diff
--- a/src/redisplay.c	Thu Apr 25 06:09:18 2002 +0000
+++ b/src/redisplay.c	Thu Apr 25 18:04:24 2002 +0000
@@ -145,9 +145,9 @@
 			   to be skipped before anything is displayed. */
   Bytebpos bi_start_col_enabled;
   int start_col_xoffset;	/* Number of pixels that still need to
-			   be skipped.  This is used for
-			   horizontal scrolling of glyphs, where we want
-			   to be able to scroll over part of the glyph. */
+                                   be skipped.  This is used for
+                                   horizontal scrolling of glyphs, where we want
+                                   to be able to scroll over part of the glyph. */
 
   int hscroll_glyph_width_adjust;  /* how much the width of the hscroll
 				      glyph differs from space_width (w).
@@ -161,18 +161,20 @@
   struct extent_fragment *ef;
   face_index findex;
 
-  /* The height of a pixmap may either be predetermined if the user
-     has set a baseline value, or it may be dependent on whatever the
-     line ascent and descent values end up being, based just on font
-     information.  In the first case we can immediately update the
-     values, thus their inclusion here.  In the last case we cannot
-     determine the actual contribution to the line height until we
-     have finished laying out all text on the line.  Thus we propagate
-     the max height of such pixmaps and do a final calculation after
-     all text has been added to the line. */
+  /* The height of a pixmap may either be predetermined if the user has set a
+     baseline value, or it may be dependent on whatever the line ascent and
+     descent values end up being, based just on font and pixmap-ascent
+     information.  In the first case we can immediately update the values, thus
+     their inclusion here.  In the last case we cannot determine the actual
+     contribution to the line height until we have finished laying out all text
+     on the line.  Thus we propagate the max height of such pixmaps and do a
+     final calculation (in calculate_baseline()) after all text has been added
+     to the line. */
   int new_ascent;
   int new_descent;
   int max_pixmap_height;
+  int need_baseline_computation;
+  int end_glyph_width;		/* Well, it is the kitchen sink after all ... */
 
   Lisp_Object result_str; /* String where we put the result of
 			     generating a formatted string in the modeline. */
@@ -190,15 +192,24 @@
   PROP_STRING,
   PROP_CHAR,
   PROP_MINIBUF_PROMPT,
-  PROP_BLANK
+  PROP_BLANK,
+  PROP_GLYPH
 };
 
 /* Data that should be propagated to the next line.  Either a single
-   Emchar or a string of Intbyte's.
+   Emchar, a string of Intbyte's or a glyph.
 
    The actual data that is propagated ends up as a Dynarr of these
    blocks.
 
+   prop_blocks are used to indicate that data that was supposed to go
+   on the previous line couldn't actually be displayed. Generally this
+   shouldn't happen if we are clipping the end of lines. If we are
+   wrapping then we need to display the propagation data before moving
+   on. Its questionable whether we should wrap or clip glyphs in this
+   instance. Most e-lisp relies on clipping so we preserve this
+   behavior.
+
    #### It's unclean that both Emchars and Intbytes are here.
    */
 
@@ -227,6 +238,14 @@
       int width;
       face_index findex;
     } p_blank;
+
+    struct
+    {
+      /* Not used as yet, but could be used to wrap rather than clip glyphs. */
+      int width;		
+      Lisp_Object glyph;
+    } p_glyph;
+
   } data;
 };
 
@@ -263,6 +282,9 @@
 static void update_line_start_cache (struct window *w, Charbpos from, Charbpos to,
 				     Charbpos point, int no_regen);
 static int point_visible (struct window *w, Charbpos point, int type);
+static void calculate_yoffset (struct display_line *dl,
+                               struct display_block *fixup);
+static void calculate_baseline (pos_data *data);
 
 static void sledgehammer_check_redisplay_structs (void);
 
@@ -680,6 +702,108 @@
   return bounds;
 }
 
+/* This takes a display_block and its containing line and corrects the yoffset
+   of each glyph in the block to cater for the ascent of the line as a
+   whole. Must be called *after* the line-ascent is known! */
+
+static void
+calculate_yoffset (struct display_line *dl, struct display_block *fixup)
+{
+  int i;
+  for (i=0; i<Dynarr_length (fixup->runes); i++)
+    {
+      struct rune *r = Dynarr_atp (fixup->runes,i);
+      if (r->type == RUNE_DGLYPH)
+        {
+          if (r->object.dglyph.ascent < dl->ascent)
+            r->object.dglyph.yoffset = dl->ascent - r->object.dglyph.ascent +
+	      r->object.dglyph.descent;
+        }
+    }
+}
+
+/* Calculate the textual baseline (the ascent and descent values for the
+   display_line as a whole).
+
+   If the baseline is completely blank, or contains no manually positioned
+   glyphs, then the textual baseline is simply the baseline of the default font.
+   (The `contains no manually positioned glyphs' part is actually done for
+   us by `add_emchar_rune'.)
+
+   If the baseline contains pixmaps, and they're all manually positioned, then
+   the textual baseline location is constrained that way, and we need do no
+   work.
+
+   If the baseline contains pixmaps, and at least one is automatically
+   positioned, then the textual ascent is the largest ascent on the line, and
+   the textual descent is the largest descent (which is how things are set up at
+   entry to this function anyway): except that if the max_ascent + max_descent
+   is too small for the height of the line (say you've adjusted the baseline of
+   a short glyph, and there's a tall one next to it), then take the ascent and
+   descent for the line individually from the largest of the explicitly set
+   ascent/descent, and the rescaled ascent/descent of the default font, scaled
+   such that the largest glyph will fit.
+
+   This means that if you have a short glyph (but taller than the default
+   font's descent) forced right under the baseline, and a really tall
+   automatically positioned glyph, that the descent for the line is just big
+   enough for the manually positioned short glyph, and the tall one uses as
+   much of that space as the default font would were it as tall as the tall
+   glyph; but that the ascent is big enough for the tall glyph to fit.
+
+   This behaviour means that under no circumstances will changing the baseline
+   of a short glyph cause a tall glyph to move around; nor will it move the
+   textual baseline more than necessary. (Changing a tall glyph's baseline
+   might move the text's baseline arbitrarily, of course.) */
+
+static void
+calculate_baseline (pos_data *data)
+{
+  /* Blank line: baseline is default font's baseline. */
+
+  if (!data->new_ascent && !data->new_descent)
+    {
+      /* We've got a blank line so initialize these values from the default
+         face. */
+      default_face_font_info (data->window, &data->new_ascent,
+			      &data->new_descent, 0, 0, 0);
+    }
+  
+  /* No automatically positioned glyphs? Return at once. */
+  if (!data->need_baseline_computation)
+    return;
+
+  /* Is the tallest glyph on the line automatically positioned?
+     If it's manually positioned, or it's automatically positioned
+     and there's enough room for it anyway, we need do no more work. */
+  if (data->max_pixmap_height > data->new_ascent + data->new_descent)
+    {
+      int default_font_ascent, default_font_descent, default_font_height;
+      int scaled_default_font_ascent, scaled_default_font_descent;
+      
+      default_face_font_info (data->window, &default_font_ascent,
+			      &default_font_descent, &default_font_height,
+			      0, 0);
+
+      scaled_default_font_ascent = data->max_pixmap_height *
+	default_font_ascent / default_font_height;
+
+      data->new_ascent = max (data->new_ascent, scaled_default_font_ascent);
+
+      /* The ascent may have expanded now. Do we still need to grow the descent,
+         or are things big enough?
+
+         The +1 caters for the baseline row itself. */
+      if (data->max_pixmap_height > data->new_ascent + data->new_descent)
+        {
+          scaled_default_font_descent = (data->max_pixmap_height *
+					 default_font_descent / default_font_height) + 1;
+
+          data->new_descent = max (data->new_descent, scaled_default_font_descent);
+        }
+    }
+}
+
 /* Given a display line and a starting position, ensure that the
    contents of the display line accurately represent the visual
    representation of the buffer contents starting from the given
@@ -875,11 +999,13 @@
 	    }
 	  else
 	    data->last_char_width = -1;
+
 	  if (!no_contribute_to_line_height)
 	    {
 	      data->new_ascent  = max (data->new_ascent,  (int) fi->ascent);
 	      data->new_descent = max (data->new_descent, (int) fi->descent);
 	    }
+
 	  data->last_charset = charset;
 	  data->last_findex = data->findex;
 	}
@@ -1130,12 +1256,11 @@
 static prop_block_dynarr *
 add_octal_runes (pos_data *data)
 {
-  prop_block_dynarr *prop, *add_failed;
+  prop_block_dynarr *add_failed, *prop = 0;
   Emchar orig_char = data->ch;
   int orig_cursor_type = data->cursor_type;
 
   /* Initialize */
-  prop = NULL;
   add_failed = NULL;
 
   if (data->start_col)
@@ -1195,7 +1320,7 @@
   ADD_NEXT_OCTAL_RUNE_CHAR;
 
   data->cursor_type = orig_cursor_type;
-  return prop;
+  return NULL;
 }
 
 #undef ADD_NEXT_OCTAL_RUNE_CHAR
@@ -1568,6 +1693,7 @@
       Lisp_Object face;
       Lisp_Object instance;
       face_index findex;
+      prop_block_dynarr *retval = 0;
 
       if (cachel)
 	width = cachel->width;
@@ -1579,7 +1705,6 @@
 
       if (data->start_col || data->start_col_xoffset)
 	{
-	  prop_block_dynarr *retval;
 	  int glyph_char_width = width / space_width (w);
 
 	  /* If we still have not fully scrolled horizontally after
@@ -1615,17 +1740,41 @@
       if (data->pixpos + width > data->max_pixpos)
 	{
 	  /* If this is the first object we are attempting to add to
-             the line then we ignore the horizontal_clip threshold.
-             Otherwise we will loop until the bottom of the window
-             continually failing to add this glyph because it is wider
-             than the window.  We could alternatively just completely
-             ignore the glyph and proceed from there but I think that
-             this is a better solution. */
+	     the line then we ignore the horizontal_clip threshold.
+	     Otherwise we will loop until the bottom of the window
+	     continually failing to add this glyph because it is wider
+	     than the window.  We could alternatively just completely
+	     ignore the glyph and proceed from there but I think that
+	     this is a better solution.
+	     
+	     This does, however, create a different problem in that we
+	     can end up adding the object to every single line, never
+	     getting any further - for instance an extent with a long
+	     start-glyph that covers multitple following
+	     characters.  */
 	  if (Dynarr_length (data->db->runes)
 	      && data->max_pixpos - data->pixpos < horizontal_clip)
 	    return ADD_FAILED;
-	  else
+	  else {
+	    struct prop_block pb;
+
+	    /* We need to account for the width of the end-of-line
+	       glyph if there is nothing more in the line to display,
+	       since we will not display it in this instance. It seems
+	       kind of gross doing it here, but otherwise we have to
+	       search the runes in create_text_block(). */
+	    if (data->ch == '\n')
+	      data->max_pixpos += data->end_glyph_width;
 	    width = data->max_pixpos - data->pixpos;
+	    /* Add the glyph we are displaying, but clipping, to the
+	       propagation data so that we don't try and do it
+	       again. */ 
+	    retval = Dynarr_new (prop_block);
+	    pb.type = PROP_GLYPH;
+	    pb.data.p_glyph.glyph = gb->glyph;
+	    pb.data.p_glyph.width = width;
+	    Dynarr_add (retval, pb);
+	  }
 	}
 
       if (cachel)
@@ -1641,6 +1790,8 @@
 
       baseline = glyph_baseline (gb->glyph, data->window);
 
+      rb.object.dglyph.descent = 0; /* Gets reset lower down, if it is known. */
+
       if (glyph_contrib_p (gb->glyph, data->window))
 	{
 	  /* A pixmap that has not had a baseline explicitly set.  Its
@@ -1648,6 +1799,7 @@
 	  if (NILP (baseline))
 	    {
 	      int height = ascent + descent;
+	      data->need_baseline_computation = 1;
 	      data->max_pixmap_height = max (data->max_pixmap_height, height);
 	    }
 
@@ -1670,6 +1822,9 @@
 
 	      data->new_ascent = max (data->new_ascent, pix_ascent);
 	      data->new_descent = max (data->new_descent, pix_descent);
+	      data->max_pixmap_height = max (data->max_pixmap_height, height);
+	      
+	      rb.object.dglyph.descent = pix_descent;
 	    }
 
 	  /* Otherwise something is screwed up. */
@@ -1701,7 +1856,7 @@
 	  data->findex = orig_findex;
 	  data->bi_charbpos = orig_charbpos;
 	  data->bi_start_col_enabled = orig_start_col_enabled;
-	  return NULL;
+	  return retval;
 	}
 
       rb.findex = findex;
@@ -1718,6 +1873,9 @@
       rb.object.dglyph.glyph = gb->glyph;
       rb.object.dglyph.extent = gb->extent;
       rb.object.dglyph.xoffset = xoffset;
+      rb.object.dglyph.ascent = ascent;
+      rb.object.dglyph.yoffset = 0;   /* Until we know better, assume that it has
+					 a normal (textual) baseline. */
 
       if (allow_cursor)
 	{
@@ -1753,7 +1911,7 @@
       Dynarr_add (data->db->runes, rb);
       data->pixpos += width;
 
-      return NULL;
+      return retval;
     }
   else
     {
@@ -1782,7 +1940,7 @@
 	abort ();	/* there are no unknown types */
     }
 
-  return NULL;	/* shut up compiler */
+  return NULL;
 }
 
 /* Add all glyphs at position POS_TYPE that are contained in the given
@@ -1842,7 +2000,6 @@
 			   is_surrogate_for_selected_frame (f));
 
   int truncate_win = window_truncation_on (w);
-  int end_glyph_width;
 
   /* If the buffer's value of selective_display is an integer then
      only lines that start with less than selective_display columns of
@@ -1940,10 +2097,10 @@
      glyph.  Save the width of the end glyph for later use. */
   data.max_pixpos = dl->bounds.right_in;
   if (truncate_win)
-    end_glyph_width = GLYPH_CACHEL_WIDTH (w, TRUN_GLYPH_INDEX);
+    data.end_glyph_width = GLYPH_CACHEL_WIDTH (w, TRUN_GLYPH_INDEX);
   else
-    end_glyph_width = GLYPH_CACHEL_WIDTH (w, CONT_GLYPH_INDEX);
-  data.max_pixpos -= end_glyph_width;
+    data.end_glyph_width = GLYPH_CACHEL_WIDTH (w, CONT_GLYPH_INDEX);
+  data.max_pixpos -= data.end_glyph_width;
 
   if (cursor_in_echo_area && MINI_WINDOW_P (w) && echo_area_active (f))
     {
@@ -2036,10 +2193,25 @@
       /* Check for face changes. */
       if (initial || (!no_more_frags && data.bi_charbpos == data.ef->end))
 	{
+	  Lisp_Object last_glyph = Qnil;
+
+	  /* Deal with glyphs that we have already displayed. The
+	     theory is that if we end up with a PROP_GLYPH in the
+	     propagation data then we are clipping the glyph and there
+	     can be no propagation data before that point. The theory
+	     works because we always recalculate the extent-fragments
+	     for propagated data, we never actually propagate the
+	     fragments that still need to be displayed. */
+	  if (*prop && Dynarr_atp (*prop, 0)->type == PROP_GLYPH) 
+	    {
+	      last_glyph = Dynarr_atp (*prop, 0)->data.p_glyph.glyph;
+	      Dynarr_free (*prop);
+	      *prop = 0;
+	    }
 	  /* Now compute the face and begin/end-glyph information. */
 	  data.findex =
 	    /* Remember that the extent-fragment routines deal in Bytebpos's. */
-	    extent_fragment_update (w, data.ef, data.bi_charbpos);
+	    extent_fragment_update (w, data.ef, data.bi_charbpos, last_glyph);
 
 	  get_display_tables (w, data.findex, &face_dt, &window_dt);
 
@@ -2109,9 +2281,9 @@
 	}
 
       /* If there is propagation data, then it represents the current
-         buffer position being displayed.  Add them and advance the
-         position counter.  This might also add the minibuffer
-         prompt. */
+	 buffer position being displayed.  Add them and advance the
+	 position counter.  This might also add the minibuffer
+	 prompt. */
       else if (*prop)
 	{
 	  dl->used_prop_data = 1;
@@ -2133,19 +2305,59 @@
 	 here rather than doing them at the end of handling the
 	 previous run so that glyphs at the beginning and end of
 	 a line are handled correctly. */
-      else if (Dynarr_length (data.ef->end_glyphs) > 0)
-	{
-	  *prop = add_glyph_runes (&data, END_GLYPHS);
-	  if (*prop)
-	    goto done;
-	}
-
-      /* If there are begin glyphs, add them to the line. */
-      else if (Dynarr_length (data.ef->begin_glyphs) > 0)
-	{
-	  *prop = add_glyph_runes (&data, BEGIN_GLYPHS);
-	  if (*prop)
-	    goto done;
+      else if (Dynarr_length (data.ef->end_glyphs) > 0
+	       || Dynarr_length (data.ef->begin_glyphs) > 0)
+	{
+	  glyph_block_dynarr* tmpglyphs = 0;
+	  /* #### I think this is safe, but could be wrong. */
+	  data.ch = BI_BUF_FETCH_CHAR (b, data.bi_charbpos);
+
+	  if (Dynarr_length (data.ef->end_glyphs) > 0) 
+	    {
+	      *prop = add_glyph_runes (&data, END_GLYPHS);
+	      tmpglyphs = data.ef->end_glyphs;
+	    }
+
+	  /* If there are begin glyphs, add them to the line. */
+	  if (!*prop && Dynarr_length (data.ef->begin_glyphs) > 0) 
+	    {
+	      *prop = add_glyph_runes (&data, BEGIN_GLYPHS);
+	      tmpglyphs = data.ef->begin_glyphs;
+	    }
+
+	  if (*prop) 
+	    {
+	      /* If we just clipped a glyph and we are at the end of a
+		 line and there are more glyphs to display then do
+		 appropriate processing to not get a continuation
+		 glyph. */
+	      if (*prop != ADD_FAILED 
+		  && Dynarr_atp (*prop, 0)->type == PROP_GLYPH
+		  && data.ch == '\n')
+		{ 
+		  /* If there are no more glyphs then do the normal
+		     processing. 
+
+		     #### This doesn't actually work if the same glyph is
+		     present more than once in the block. To solve
+		     this we would have to carry the index around
+		     which might be problematic since the fragment is
+		     recalculated for each line. */
+		  if (EQ (Dynarr_end (tmpglyphs)->glyph,
+			  Dynarr_atp (*prop, 0)->data.p_glyph.glyph))
+		    {
+		      Dynarr_free (*prop);
+		      *prop = 0;
+		    }
+		  else {
+		    data.blank_width = DEVMETH (d, eol_cursor_width, ());
+		    add_emchar_rune (&data); /* discard prop data. */
+		    goto done;
+		  }
+		}
+	      else
+		goto done;
+	    }
 	}
 
       /* If at end-of-buffer, we've already processed begin and
@@ -2179,7 +2391,7 @@
 	      /* We aren't going to be adding an end glyph so give its
                  space back in order to make sure that the cursor can
                  fit. */
-	      data.max_pixpos += end_glyph_width;
+	      data.max_pixpos += data.end_glyph_width;
 
 	      if (selective > 0
 		  && (bi_spaces_at_point
@@ -2260,7 +2472,7 @@
 
 	      /* We won't be adding a truncation or continuation glyph
                  so give up the room allocated for them. */
-	      data.max_pixpos += end_glyph_width;
+	      data.max_pixpos += data.end_glyph_width;
 
 	      if (!NILP (b->selective_display_ellipses))
 		{
@@ -2469,7 +2681,7 @@
              for the next newline.  We also add the end-of-line glyph which
              we know will fit because we adjusted the right border before
              we starting laying out the line. */
-	  data.max_pixpos += end_glyph_width;
+	  data.max_pixpos += data.end_glyph_width;
 	  data.findex = DEFAULT_INDEX;
 	  gb.extent = Qnil;
 
@@ -2604,26 +2816,7 @@
   else
     db->end_pos = dl->bounds.right_white;
 
-  /* update line height parameters */
-  if (!data.new_ascent && !data.new_descent)
-    {
-      /* We've got a blank line so initialize these values from the default
-         face. */
-      default_face_font_info (data.window, &data.new_ascent,
-			      &data.new_descent, 0, 0, 0);
-    }
-
-  if (data.max_pixmap_height)
-    {
-      int height = data.new_ascent + data.new_descent;
-      int pix_ascent, pix_descent;
-
-      pix_descent = data.max_pixmap_height * data.new_descent / height;
-      pix_ascent = data.max_pixmap_height - pix_descent;
-
-      data.new_ascent = max (data.new_ascent, pix_ascent);
-      data.new_descent = max (data.new_descent, pix_descent);
-    }
+  calculate_baseline (&data);
 
   dl->ascent = data.new_ascent;
   dl->descent = data.new_descent;
@@ -2641,6 +2834,8 @@
       dl->descent = descent;
   }
 
+  calculate_yoffset (dl, db);
+
   dl->cursor_elt = data.cursor_x;
   /* #### lossage lossage lossage! Fix this shit! */
   if (data.bi_charbpos > BI_BUF_ZV (b))
@@ -2729,14 +2924,15 @@
       gb.extent = Qnil;
       add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0, 0);
     }
-
+  
   if (data.max_pixmap_height)
     {
       int height = data.new_ascent + data.new_descent;
       int pix_ascent, pix_descent;
-
+      
       pix_descent = data.max_pixmap_height * data.new_descent / height;
       pix_ascent = data.max_pixmap_height - pix_descent;
+      calculate_baseline (&data);
 
       data.new_ascent = max (data.new_ascent, pix_ascent);
       data.new_descent = max (data.new_descent, pix_descent);
@@ -2748,6 +2944,8 @@
   data.db->start_pos = dl->bounds.left_in;
   data.db->end_pos = data.pixpos;
 
+  calculate_yoffset (dl, data.db);
+
   return data.pixpos - dl->bounds.left_in;
 }
 
@@ -2819,20 +3017,13 @@
       (reverse ? elt-- : elt++);
     }
 
-  if (data.max_pixmap_height)
-    {
-      int height = data.new_ascent + data.new_descent;
-      int pix_ascent, pix_descent;
-
-      pix_descent = data.max_pixmap_height * data.new_descent / height;
-      pix_ascent = data.max_pixmap_height - pix_descent;
-      data.new_ascent = max (data.new_ascent, pix_ascent);
-      data.new_descent = max (data.new_descent, pix_descent);
-    }
+  calculate_baseline (&data);
 
   dl->ascent = data.new_ascent;
   dl->descent = data.new_descent;
 
+  calculate_yoffset (dl, data.db);
+
   return data.pixpos;
 }
 
@@ -4269,9 +4460,9 @@
 
 
 /***************************************************************************/
-/*								*/
-/*                            displayable string routines			*/
-/*								*/
+/*                                                                         */
+/*                            displayable string routines                  */
+/*                                                                         */
 /***************************************************************************/
 
 /* Given a position for a string in a window, ensure that the given
@@ -4312,7 +4503,6 @@
   pos_data data;
 
   int truncate_win = b ? window_truncation_on (w) : 0;
-  int end_glyph_width = 0;
 
   /* We're going to ditch selective display for static text, it's an
      FSF thing and invisible extents are the way to go here.
@@ -4420,13 +4610,7 @@
   /* Set the right boundary adjusting it to take into account any end
      glyph.  Save the width of the end glyph for later use. */
   data.max_pixpos = dl->bounds.right_in;
-#if 0
-  if (truncate_win)
-    end_glyph_width = GLYPH_CACHEL_WIDTH (w, TRUN_GLYPH_INDEX);
-  else
-    end_glyph_width = GLYPH_CACHEL_WIDTH (w, CONT_GLYPH_INDEX);
-#endif
-  data.max_pixpos -= end_glyph_width;
+  data.max_pixpos -= data.end_glyph_width;
 
   data.cursor_type = NO_CURSOR;
   data.cursor_x = -1;
@@ -4482,11 +4666,19 @@
       /* Check for face changes. */
       if (initial || (!no_more_frags && data.bi_charbpos == data.ef->end))
 	{
+	  Lisp_Object last_glyph = Qnil;
+	  /* Deal with clipped glyphs that we have already displayed. */
+	  if (*prop && Dynarr_atp (*prop, 0)->type == PROP_GLYPH) 
+	    {
+	      last_glyph = Dynarr_atp (*prop, 0)->data.p_glyph.glyph;
+	      Dynarr_free (*prop);
+	      *prop = 0;
+	    }
 	  /* Now compute the face and begin/end-glyph information. */
 	  data.findex =
 	    /* Remember that the extent-fragment routines deal in
                Bytebpos's. */
-	    extent_fragment_update (w, data.ef, data.bi_charbpos);
+	    extent_fragment_update (w, data.ef, data.bi_charbpos, last_glyph);
 	  /* This is somewhat cheesy but the alternative is to
              propagate default_face into extent_fragment_update. */
 	  if (data.findex == DEFAULT_INDEX)
@@ -4578,17 +4770,23 @@
 	 a line are handled correctly. */
       else if (Dynarr_length (data.ef->end_glyphs) > 0)
 	{
+	  data.ch = XSTRING_CHAR (disp_string, data.bi_charbpos);
 	  *prop = add_glyph_runes (&data, END_GLYPHS);
-	  if (*prop)
+
+	  if (*prop) {
 	    goto done;
+	  }
 	}
 
       /* If there are begin glyphs, add them to the line. */
       else if (Dynarr_length (data.ef->begin_glyphs) > 0)
 	{
+	  data.ch = XSTRING_CHAR (disp_string, data.bi_charbpos);
 	  *prop = add_glyph_runes (&data, BEGIN_GLYPHS);
-	  if (*prop)
+
+	  if (*prop) {
 	    goto done;
+	  }
 	}
 
       /* If at end-of-buffer, we've already processed begin and
@@ -4622,7 +4820,7 @@
 	      /* We aren't going to be adding an end glyph so give its
                  space back in order to make sure that the cursor can
                  fit. */
-	      data.max_pixpos += end_glyph_width;
+	      data.max_pixpos += data.end_glyph_width;
 	      goto done;
 	    }
 
@@ -4664,7 +4862,7 @@
 	      *prop = add_blank_rune (&data, w, char_tab_width);
 
 	      /* add_blank_rune is only supposed to be called with
-                 sizes guaranteed to fit in the available space. */
+		 sizes guaranteed to fit in the available space. */
 	      assert (!(*prop));
 
 	      if (prop_width)
@@ -4720,7 +4918,7 @@
 	}
     }
 
-done:
+ done:
 
   /* Determine the starting point of the next line if we did not hit the
      end of the buffer. */
@@ -4753,7 +4951,7 @@
              for the next newline.  We also add the end-of-line glyph which
              we know will fit because we adjusted the right border before
              we starting laying out the line. */
-	  data.max_pixpos += end_glyph_width;
+	  data.max_pixpos += data.end_glyph_width;
 	  data.findex = default_face;
 	  gb.extent = Qnil;
 
@@ -4782,7 +4980,7 @@
 	      cachel = GLYPH_CACHEL (w, CONT_GLYPH_INDEX);
 	    }
 
-	  if (end_glyph_width)
+	  if (data.end_glyph_width)
 	    add_glyph_rune (&data, &gb, BEGIN_GLYPHS, 0, cachel);
 
 	  if (truncate_win && data.bi_charbpos == bi_string_zv)
@@ -4862,26 +5060,7 @@
   else
     db->end_pos = dl->bounds.right_white;
 
-  /* update line height parameters */
-  if (!data.new_ascent && !data.new_descent)
-    {
-      /* We've got a blank line so initialize these values from the default
-         face. */
-      default_face_font_info (data.window, &data.new_ascent,
-			      &data.new_descent, 0, 0, 0);
-    }
-
-  if (data.max_pixmap_height)
-    {
-      int height = data.new_ascent + data.new_descent;
-      int pix_ascent, pix_descent;
-
-      pix_descent = data.max_pixmap_height * data.new_descent / height;
-      pix_ascent = data.max_pixmap_height - pix_descent;
-
-      data.new_ascent = max (data.new_ascent, pix_ascent);
-      data.new_descent = max (data.new_descent, pix_descent);
-    }
+  calculate_baseline (&data);
 
   dl->ascent = data.new_ascent;
   dl->descent = data.new_descent;
@@ -4899,6 +5078,8 @@
       dl->descent = descent;
   }
 
+  calculate_yoffset (dl, db);
+
   dl->cursor_elt = data.cursor_x;
   /* #### lossage lossage lossage! Fix this shit! */
   if (data.bi_charbpos > bi_string_zv)