Fix display when a font claims large values of ascent and descent

This fixes bug#20628.

* src/xdisp.c (get_phys_cursor_geometry): Correct the Y
coordinate of a hollow cursor glyph when the original glyph's
ascent is too small.
(get_font_ascent_descent, normal_char_ascent_descent)
(normal_char_height): New functions.
(handle_single_display_spec, append_space_for_newline)
(calc_pixel_width_or_height, produce_stretch_glyph)
(calc_line_height_property): Use normal_char_ascent_descent and
normal_char_height.
(x_produce_glyphs): When font-global values of ascent and descent
are too large, use per-character glyph metrics instead, if
possible.  But don't allow the glyph row's ascent and descent
values become smaller than the values from the metrics of the
font's "normal" character.

* src/xftfont.c (xftfont_draw):
* src/w32font.c (w32font_draw): Correct the values of ascent and
descent used to draw glyphless characters' hex code in a box.

* src/xterm.c (x_draw_glyph_string_background):
* src/xdisp.c (x_produce_glyphs):
* src/w32term.c (x_draw_glyph_string_background):
* src/nsterm.m (ns_maybe_dumpglyphs_background): Use FONT_TOO_HIGH
to detect fonts whose global ascent and descent values are too
large to be used in layout decision, and redraw the background
when that happens.

* src/dispextern.h (FONT_TOO_HIGH): New macro.
(get_font_ascent_descent): Add prototype.

* src/xterm.c (x_new_font):
* src/w32term.c (x_new_font):
* src/nsterm.m (x_new_font):
* src/font.c (font_open_entity):
* src/composite.c (composition_gstring_width): Use
get_font_ascent_descent to obtain reasonable values for ascent and
descent of a font.
This commit is contained in:
Eli Zaretskii 2015-06-06 15:55:15 +03:00
commit f1baa15650
9 changed files with 273 additions and 34 deletions

View file

@ -732,9 +732,11 @@ composition_gstring_width (Lisp_Object gstring, ptrdiff_t from, ptrdiff_t to,
if (FONT_OBJECT_P (font_object))
{
struct font *font = XFONT_OBJECT (font_object);
int font_ascent, font_descent;
metrics->ascent = font->ascent;
metrics->descent = font->descent;
get_font_ascent_descent (font, &font_ascent, &font_descent);
metrics->ascent = font_ascent;
metrics->descent = font_descent;
}
else
{

View file

@ -1526,6 +1526,12 @@ struct glyph_string
+ (FRAME_LINE_HEIGHT ((F)) > FONT_HEIGHT ((FONT)))) / 2 \
- (FONT_DESCENT (FRAME_FONT (F)) - FRAME_BASELINE_OFFSET (F)))
/* A heuristic test for fonts that claim they need a preposterously
large vertical space. The heuristics is in the factor of 3. We
ignore the ascent and descent values reported by such fonts, and
instead go by the values reported for individual glyphs. */
#define FONT_TOO_HIGH(ft) ((ft)->ascent + (ft)->descent > 3*(ft)->pixel_size)
/***********************************************************************
Faces
@ -3240,6 +3246,8 @@ extern ptrdiff_t compute_display_string_end (ptrdiff_t,
extern void produce_stretch_glyph (struct it *);
extern int merge_glyphless_glyph_face (struct it *);
extern void get_font_ascent_descent (struct font *, int *, int *);
#ifdef HAVE_WINDOW_SYSTEM
#ifdef GLYPH_DEBUG

View file

@ -2908,7 +2908,12 @@ font_open_entity (struct frame *f, Lisp_Object entity, int pixel_size)
: font->average_width ? font->average_width
: font->space_width ? font->space_width
: 1);
height = (font->height ? font->height : 1);
int font_ascent, font_descent;
get_font_ascent_descent (font, &font_ascent, &font_descent);
height = font_ascent + font_descent;
if (height <= 0)
height = 1;
#ifdef HAVE_WINDOW_SYSTEM
FRAME_DISPLAY_INFO (f)->n_fonts++;
if (FRAME_DISPLAY_INFO (f)->n_fonts == 1)

View file

@ -2928,6 +2928,11 @@ Function modeled after x_draw_glyph_string_box ().
{
int box_line_width = max (s->face->box_line_width, 0);
if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
/* When xdisp.c ignores FONT_HEIGHT, we cannot trust font
dimensions, since the actual glyphs might be much
smaller. So in that case we always clear the rectangle
with background color. */
|| FONT_TOO_HIGH (s->font)
|| s->font_not_found_p || s->extends_to_end_of_line_p || force_p)
{
struct face *face;
@ -7687,6 +7692,7 @@ @implementation EmacsDocument
{
struct font *font = XFONT_OBJECT (font_object);
EmacsView *view = FRAME_NS_VIEW (f);
int font_ascent, font_descent;
if (fontset < 0)
fontset = fontset_from_font (font_object);
@ -7701,7 +7707,8 @@ @implementation EmacsDocument
FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
FRAME_COLUMN_WIDTH (f) = font->average_width;
FRAME_LINE_HEIGHT (f) = font->height;
get_font_ascent_descent (font, &font_ascent, &font_descent);
FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
/* Compute the scroll bar width in character columns. */
if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)

View file

@ -650,12 +650,31 @@ w32font_draw (struct glyph_string *s, int from, int to,
HBRUSH brush;
RECT rect;
struct font *font = s->font;
int ascent = font->ascent, descent = font->descent;
/* Font's global ascent and descent values might be
preposterously large for some fonts. We fix here the case
when those fonts are used for display of glyphless
characters, because drawing background with font dimensions
in those cases makes the display illegible. There's only one
more call to the draw method with with_background set to
true, and that's in x_draw_glyph_string_foreground, when
drawing the cursor, where we have no such heuristics
available. FIXME. */
if (s->first_glyph->type == GLYPHLESS_GLYPH
&& (s->first_glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEX_CODE
|| s->first_glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM))
{
ascent =
s->first_glyph->slice.glyphless.lower_yoff
- s->first_glyph->slice.glyphless.upper_yoff;
descent = 0;
}
brush = CreateSolidBrush (s->gc->background);
rect.left = x;
rect.top = y - font->ascent;
rect.top = y - ascent;
rect.right = x + s->width;
rect.bottom = y + font->descent;
rect.bottom = y + descent;
FillRect (s->hdc, &rect, brush);
DeleteObject (brush);
}

View file

@ -1218,7 +1218,12 @@ x_draw_glyph_string_background (struct glyph_string *s, bool force_p)
}
else
#endif
if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
/* When xdisp.c ignores FONT_HEIGHT, we cannot trust
font dimensions, since the actual glyphs might be
much smaller. So in that case we always clear the
rectangle with background color. */
|| FONT_TOO_HIGH (s->font)
|| s->font_not_found_p
|| s->extends_to_end_of_line_p
|| force_p)
@ -5827,7 +5832,7 @@ Lisp_Object
x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
{
struct font *font = XFONT_OBJECT (font_object);
int unit;
int unit, font_ascent, font_descent;
if (fontset < 0)
fontset = fontset_from_font (font_object);
@ -5840,7 +5845,8 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
FRAME_FONT (f) = font;
FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
FRAME_COLUMN_WIDTH (f) = unit = font->average_width;
FRAME_LINE_HEIGHT (f) = font->height;
get_font_ascent_descent (font, &font_ascent, &font_descent);
FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
/* Compute number of scrollbar columns. */
unit = FRAME_COLUMN_WIDTH (f);

View 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
}
/***********************************************************************
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;

View file

@ -617,8 +617,26 @@ xftfont_draw (struct glyph_string *s, int from, int to, int x, int y,
XftDrawSetClip (xft_draw, NULL);
if (with_background)
XftDrawRect (xft_draw, &bg,
x, y - s->font->ascent, s->width, s->font->height);
{
int height = FONT_HEIGHT (s->font), ascent = FONT_BASE (s->font);
/* Font's global height and ascent values might be
preposterously large for some fonts. We fix here the case
when those fonts are used for display of glyphless
characters, because drawing background with font dimensions
in those cases makes the display illegible. There's only one
more call to the draw method with with_background set to
true, and that's in x_draw_glyph_string_foreground, when
drawing the cursor, where we have no such heuristics
available. FIXME. */
if (s->first_glyph->type == GLYPHLESS_GLYPH
&& (s->first_glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEX_CODE
|| s->first_glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM))
height = ascent =
s->first_glyph->slice.glyphless.lower_yoff
- s->first_glyph->slice.glyphless.upper_yoff;
XftDrawRect (xft_draw, &bg, x, y - ascent, s->width, height);
}
code = alloca (sizeof (FT_UInt) * len);
for (i = 0; i < len; i++)
code[i] = ((XCHAR2B_BYTE1 (s->char2b + from + i) << 8)

View file

@ -1724,6 +1724,11 @@ x_draw_glyph_string_background (struct glyph_string *s, bool force_p)
s->background_filled_p = true;
}
else if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
/* When xdisp.c ignores FONT_HEIGHT, we cannot trust
font dimensions, since the actual glyphs might be
much smaller. So in that case we always clear the
rectangle with background color. */
|| FONT_TOO_HIGH (s->font)
|| s->font_not_found_p
|| s->extends_to_end_of_line_p
|| force_p)
@ -9405,7 +9410,7 @@ Lisp_Object
x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
{
struct font *font = XFONT_OBJECT (font_object);
int unit;
int unit, font_ascent, font_descent;
if (fontset < 0)
fontset = fontset_from_font (font_object);
@ -9418,7 +9423,8 @@ x_new_font (struct frame *f, Lisp_Object font_object, int fontset)
FRAME_FONT (f) = font;
FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
FRAME_COLUMN_WIDTH (f) = font->average_width;
FRAME_LINE_HEIGHT (f) = FONT_HEIGHT (font);
get_font_ascent_descent (font, &font_ascent, &font_descent);
FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
#ifndef USE_X_TOOLKIT
FRAME_MENU_BAR_HEIGHT (f) = FRAME_MENU_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);