]> code.delx.au - gnu-emacs/blobdiff - src/xdisp.c
Fix display when a font claims large values of ascent and descent
[gnu-emacs] / src / xdisp.c
index 05862d276ef3e1aa31b2f76de9ac6e8028550430..14385fa84214bd3c19216787478a43381ba43fc2 100644 (file)
@@ -833,6 +833,9 @@ static void x_draw_bottom_divider (struct window *w);
 static void notice_overwritten_cursor (struct window *,
                                        enum glyph_row_area,
                                        int, int, int, int);
+static int  normal_char_height (struct font *, int);
+static void normal_char_ascent_descent (struct font *, int, int *, int *);
+
 static void append_stretch_glyph (struct it *, Lisp_Object,
                                   int, int, int);
 
@@ -1761,7 +1764,7 @@ estimate_mode_line_height (struct frame *f, enum face_id face_id)
          if (face)
            {
              if (face->font)
-               height = FONT_HEIGHT (face->font);
+               height = normal_char_height (face->font, -1);
              if (face->box_line_width > 0)
                height += 2 * face->box_line_width;
            }
@@ -2150,7 +2153,7 @@ get_phys_cursor_geometry (struct window *w, struct glyph_row *row,
                          struct glyph *glyph, int *xp, int *yp, int *heightp)
 {
   struct frame *f = XFRAME (WINDOW_FRAME (w));
-  int x, y, wd, h, h0, y0;
+  int x, y, wd, h, h0, y0, ascent;
 
   /* Compute the width of the rectangle to draw.  If on a stretch
      glyph, and `x-stretch-block-cursor' is nil, don't draw a
@@ -2170,13 +2173,21 @@ get_phys_cursor_geometry (struct window *w, struct glyph_row *row,
     wd = min (FRAME_COLUMN_WIDTH (f), wd);
   w->phys_cursor_width = wd;
 
-  y = w->phys_cursor.y + row->ascent - glyph->ascent;
+  /* Don't let the hollow cursor glyph descend below the glyph row's
+     ascent value, lest the hollow cursor looks funny.  */
+  y = w->phys_cursor.y;
+  ascent = row->ascent;
+  if (row->ascent < glyph->ascent)
+    {
+      y =- glyph->ascent - row->ascent;
+      ascent = glyph->ascent;
+    }
 
   /* If y is below window bottom, ensure that we still see a cursor.  */
   h0 = min (FRAME_LINE_HEIGHT (f), row->visible_height);
 
-  h = max (h0, glyph->ascent + glyph->descent);
-  h0 = min (h0, glyph->ascent + glyph->descent);
+  h = max (h0, ascent + glyph->descent);
+  h0 = min (h0, ascent + glyph->descent);
 
   y0 = WINDOW_HEADER_LINE_HEIGHT (w);
   if (y < y0)
@@ -4880,7 +4891,7 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
            {
              struct face *face = FACE_FROM_ID (it->f, it->face_id);
              it->voffset = - (XFLOATINT (value)
-                              * (FONT_HEIGHT (face->font)));
+                              * (normal_char_height (face->font, -1)));
            }
 #endif /* HAVE_WINDOW_SYSTEM */
        }
@@ -19157,6 +19168,7 @@ append_space_for_newline (struct it *it, bool default_face_p)
          struct text_pos saved_pos;
          Lisp_Object saved_object;
          struct face *face;
+         struct glyph *g;
 
          saved_object = it->object;
          saved_pos = it->position;
@@ -19188,6 +19200,23 @@ append_space_for_newline (struct it *it, bool default_face_p)
 
          PRODUCE_GLYPHS (it);
 
+         /* Make sure this space glyph has the right ascent and
+            descent values, or else cursor at end of line will look
+            funny.  */
+         g = it->glyph_row->glyphs[TEXT_AREA] + n;
+         struct font *font = face->font ? face->font : FRAME_FONT (it->f);
+         if (n == 0 || it->glyph_row->height < font->pixel_size)
+           {
+             normal_char_ascent_descent (font, -1, &it->ascent, &it->descent);
+             it->max_ascent = it->ascent;
+             it->max_descent = it->descent;
+             /* Make sure compute_line_metrics recomputes the row height.  */
+             it->glyph_row->height = 0;
+           }
+
+         g->ascent = it->max_ascent;
+         g->descent = it->max_descent;
+
          it->override_ascent = -1;
          it->constrain_row_ascent_descent_p = false;
          it->current_x = saved_x;
@@ -23900,9 +23929,13 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
 
 #ifdef HAVE_WINDOW_SYSTEM
       if (EQ (prop, Qheight))
-       return OK_PIXELS (font ? FONT_HEIGHT (font) : FRAME_LINE_HEIGHT (it->f));
+       return OK_PIXELS (font
+                         ? normal_char_height (font, -1)
+                         : FRAME_LINE_HEIGHT (it->f));
       if (EQ (prop, Qwidth))
-       return OK_PIXELS (font ? FONT_WIDTH (font) : FRAME_COLUMN_WIDTH (it->f));
+       return OK_PIXELS (font
+                         ? FONT_WIDTH (font)
+                         : FRAME_COLUMN_WIDTH (it->f));
 #else
       if (EQ (prop, Qheight) || EQ (prop, Qwidth))
        return OK_PIXELS (1);
@@ -24032,6 +24065,17 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
   return false;
 }
 
+void
+get_font_ascent_descent (struct font *font, int *ascent, int *descent)
+{
+#ifdef HAVE_WINDOW_SYSTEM
+  normal_char_ascent_descent (font, -1, ascent, descent);
+#else
+  *ascent = 1;
+  *descent = 0;
+#endif
+}
+
 \f
 /***********************************************************************
                             Glyph Display
@@ -24536,6 +24580,55 @@ get_per_char_metric (struct font *font, XChar2b *char2b)
   return &metrics;
 }
 
+/* A subroutine that computes "normal" values of ASCENT and DESCENT
+   for FONT.  Values are taken from font-global ones, except for fonts
+   that claim preposterously large values, but whose glyphs actually
+   have reasonable dimensions.  C is the character to use for metrics
+   if the font-global values are too large; if C is negative, the
+   function selects a default character.  */
+static void
+normal_char_ascent_descent (struct font *font, int c, int *ascent, int *descent)
+{
+  *ascent = FONT_BASE (font);
+  *descent = FONT_DESCENT (font);
+
+  if (FONT_TOO_HIGH (font))
+    {
+      XChar2b char2b;
+
+      /* Get metrics of C, defaulting to a reasonably sized ASCII
+        character.  */
+      if (get_char_glyph_code (c >= 0 ? c : '{', font, &char2b))
+       {
+         struct font_metrics *pcm = get_per_char_metric (font, &char2b);
+
+         if (!(pcm->width == 0 && pcm->rbearing == 0 && pcm->lbearing == 0))
+           {
+             /* We add 1 pixel to character dimensions as heuristics
+                that produces nicer display, e.g. when the face has
+                the box attribute.  */
+             *ascent = pcm->ascent + 1;
+             *descent = pcm->descent + 1;
+           }
+       }
+    }
+}
+
+/* A subroutine that computes a reasonable "normal character height"
+   for fonts that claim preposterously large vertical dimensions, but
+   whose glyphs are actually reasonably sized.  C is the charcater
+   whose metrics to use for those fonts, or -1 for default
+   character.  */
+static int
+normal_char_height (struct font *font, int c)
+{
+  int ascent, descent;
+
+  normal_char_ascent_descent (font, c, &ascent, &descent);
+
+  return ascent + descent;
+}
+
 /* EXPORT for RIF:
    Set *LEFT and *RIGHT to the left and right overhang of GLYPH on
    frame F.  Overhangs of glyphs other than type CHAR_GLYPH are
@@ -25835,6 +25928,8 @@ produce_stretch_glyph (struct it *it)
   /* Compute height.  */
   if (FRAME_WINDOW_P (it->f))
     {
+      int default_height = normal_char_height (font, ' ');
+
       if ((prop = Fplist_get (plist, QCheight), !NILP (prop))
          && calc_pixel_width_or_height (&tem, it, prop, font, false, 0))
        {
@@ -25843,9 +25938,9 @@ produce_stretch_glyph (struct it *it)
        }
       else if (prop = Fplist_get (plist, QCrelative_height),
               NUMVAL (prop) > 0)
-       height = FONT_HEIGHT (font) * NUMVAL (prop);
+       height = default_height * NUMVAL (prop);
       else
-       height = FONT_HEIGHT (font);
+       height = default_height;
 
       if (height <= 0 && (height < 0 || !zero_height_ok_p))
        height = 1;
@@ -26069,8 +26164,7 @@ calc_line_height_property (struct it *it, Lisp_Object val, struct font *font,
        boff = VCENTER_BASELINE_OFFSET (font, it->f) - boff;
     }
 
-  ascent = FONT_BASE (font) + boff;
-  descent = FONT_DESCENT (font) - boff;
+  normal_char_ascent_descent (font, -1, &ascent, &descent);
 
   if (override)
     {
@@ -26196,8 +26290,9 @@ produce_glyphless_glyph (struct it *it, bool for_no_font, Lisp_Object acronym)
      ASCII face.  */
   face = FACE_FROM_ID (it->f, it->face_id)->ascii_face;
   font = face->font ? face->font : FRAME_FONT (it->f);
-  it->ascent = FONT_BASE (font) + font->baseline_offset;
-  it->descent = FONT_DESCENT (font) - font->baseline_offset;
+  normal_char_ascent_descent (font, -1, &it->ascent, &it->descent);
+  it->ascent += font->baseline_offset;
+  it->descent -= font->baseline_offset;
   base_height = it->ascent + it->descent;
   base_width = font->average_width;
 
@@ -26384,6 +26479,22 @@ x_produce_glyphs (struct it *it)
              it->phys_ascent = pcm->ascent + boff;
              it->phys_descent = pcm->descent - boff;
              it->pixel_width = pcm->width;
+             /* Don't use font-global values for ascent and descent
+                if they result in an exceedingly large line height.  */
+             if (it->override_ascent < 0)
+               {
+                 if (FONT_TOO_HIGH (font))
+                   {
+                     it->ascent = it->phys_ascent;
+                     it->descent = it->phys_descent;
+                     /* These limitations are enforced by an
+                        assertion near the end of this function.  */
+                     if (it->ascent < 0)
+                       it->ascent = 0;
+                     if (it->descent < 0)
+                       it->descent = 0;
+                   }
+               }
            }
          else
            {
@@ -26511,8 +26622,18 @@ x_produce_glyphs (struct it *it)
            }
          else
            {
-             it->ascent = FONT_BASE (font) + boff;
-             it->descent = FONT_DESCENT (font) - boff;
+             if (FONT_TOO_HIGH (font))
+               {
+                 it->ascent = font->pixel_size + boff - 1;
+                 it->descent = -boff + 1;
+                 if (it->descent < 0)
+                   it->descent = 0;
+               }
+             else
+               {
+                 it->ascent = FONT_BASE (font) + boff;
+                 it->descent = FONT_DESCENT (font) - boff;
+               }
            }
 
          if (EQ (height, Qt))
@@ -26583,8 +26704,38 @@ x_produce_glyphs (struct it *it)
 
              it->pixel_width = next_tab_x - x;
              it->nglyphs = 1;
-             it->ascent = it->phys_ascent = FONT_BASE (font) + boff;
-             it->descent = it->phys_descent = FONT_DESCENT (font) - boff;
+             if (FONT_TOO_HIGH (font))
+               {
+                 if (get_char_glyph_code (' ', font, &char2b))
+                   {
+                     pcm = get_per_char_metric (font, &char2b);
+                     if (pcm->width == 0
+                         && pcm->rbearing == 0 && pcm->lbearing == 0)
+                       pcm = NULL;
+                   }
+
+                 if (pcm)
+                   {
+                     it->ascent = pcm->ascent + boff;
+                     it->descent = pcm->descent - boff;
+                   }
+                 else
+                   {
+                     it->ascent = font->pixel_size + boff - 1;
+                     it->descent = -boff + 1;
+                   }
+                 if (it->ascent < 0)
+                   it->ascent = 0;
+                 if (it->descent < 0)
+                   it->descent = 0;
+               }
+             else
+               {
+                 it->ascent = FONT_BASE (font) + boff;
+                 it->descent = FONT_DESCENT (font) - boff;
+               }
+             it->phys_ascent = it->ascent;
+             it->phys_descent = it->descent;
 
              if (it->glyph_row)
                {
@@ -26598,6 +26749,22 @@ x_produce_glyphs (struct it *it)
              it->nglyphs = 1;
            }
        }
+
+      if (FONT_TOO_HIGH (font))
+       {
+         int font_ascent, font_descent;
+
+         /* For very large fonts, where we ignore the declared font
+            dimensions, and go by per-character metrics instead,
+            don't let the row ascent and descent values (and the row
+            height computed from them) be smaller than the "normal"
+            character metrics.  This avoids unpleasant effects
+            whereby lines on display would change their heigh
+            depending on which characters are shown.  */
+         normal_char_ascent_descent (font, -1, &font_ascent, &font_descent);
+         it->max_ascent = max (it->max_ascent, font_ascent);
+         it->max_descent = max (it->max_descent, font_descent);
+       }
     }
   else if (it->what == IT_COMPOSITION && it->cmp_it.ch < 0)
     {
@@ -26664,9 +26831,10 @@ x_produce_glyphs (struct it *it)
          boff = font->baseline_offset;
          if (font->vertical_centering)
            boff = VCENTER_BASELINE_OFFSET (font, it->f) - boff;
-         font_ascent = FONT_BASE (font) + boff;
-         font_descent = FONT_DESCENT (font) - boff;
-         font_height = FONT_HEIGHT (font);
+         normal_char_ascent_descent (font, -1, &font_ascent, &font_descent);
+         font_ascent +=  boff;
+         font_descent -= boff;
+         font_height = font_ascent + font_descent;
 
          cmp->font = font;