diff --git a/docs/changelog.rst b/docs/changelog.rst index a7138463d..68b6d091d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -29,6 +29,9 @@ To update |kitty|, :doc:`follow the instructions `. - When resizing and only a single window is present in the current layout, use that window's background color to fill in the blank areas. +- Linux: Automatically increase cell height if the font being used is broken + and draws the underscore outside the bounding box (:iss:`690`) + 0.14.0 [2019-05-24] --------------------- diff --git a/kitty/core_text.m b/kitty/core_text.m index 7b374bd95..352185db8 100644 --- a/kitty/core_text.m +++ b/kitty/core_text.m @@ -280,7 +280,9 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u *baseline = (unsigned int)self->ascent; // float line_height = MAX(1, floor(self->ascent + self->descent + MAX(0, self->leading) + 0.5)); // Let CoreText's layout engine calculate the line height. Slower, but hopefully more accurate. - CFStringRef ts = CFSTR("test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test"); +#define W "AQWMH_gyl " + CFStringRef ts = CFSTR(W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W W); +#undef W CFMutableAttributedStringRef test_string = CFAttributedStringCreateMutable(kCFAllocatorDefault, CFStringGetLength(ts)); CFAttributedStringReplaceString(test_string, CFRangeMake(0, 0), ts); CFAttributedStringSetAttribute(test_string, CFRangeMake(0, CFStringGetLength(ts)), kCTFontAttributeName, self->ct_font); diff --git a/kitty/freetype.c b/kitty/freetype.c index 63f195ccf..198f737b9 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -75,8 +75,6 @@ set_freetype_error(const char* prefix, int err_code) { static FT_Library library; -#define CALC_CELL_HEIGHT(self) font_units_to_pixels_y(self, self->height) - static inline int font_units_to_pixels_y(Face *self, int x) { return ceil((double)FT_MulFix(x, self->face->size->metrics.y_scale) / 64.0); @@ -87,11 +85,56 @@ font_units_to_pixels_x(Face *self, int x) { return ceil((double)FT_MulFix(x, self->face->size->metrics.x_scale) / 64.0); } + +static inline int +get_load_flags(int hinting, int hintstyle, int base) { + int flags = base; + if (hinting) { + if (hintstyle >= 3) flags |= FT_LOAD_TARGET_NORMAL; + else if (0 < hintstyle && hintstyle < 3) flags |= FT_LOAD_TARGET_LIGHT; + } else flags |= FT_LOAD_NO_HINTING; + return flags; +} + + +static inline bool +load_glyph(Face *self, int glyph_index, int load_type) { + int flags = get_load_flags(self->hinting, self->hintstyle, load_type); + int error = FT_Load_Glyph(self->face, glyph_index, flags); + if (error) { set_freetype_error("Failed to load glyph, with error:", error); return false; } + return true; +} + +static inline unsigned int +get_height_for_char(Face *self, char ch) { + unsigned int ans = 0; + int glyph_index = FT_Get_Char_Index(self->face, ch); + if (load_glyph(self, glyph_index, FT_LOAD_DEFAULT)) { + unsigned int baseline = font_units_to_pixels_y(self, self->ascender); + FT_GlyphSlotRec *glyph = self->face->glyph; + FT_Bitmap *bm = &glyph->bitmap; + if (glyph->bitmap_top <= 0 || (glyph->bitmap_top > 0 && (unsigned int)glyph->bitmap_top < baseline)) { + ans = baseline - glyph->bitmap_top + bm->rows; + } + } + return ans; +} + +static inline unsigned int +calc_cell_height(Face *self, bool for_metrics) { + unsigned int ans = font_units_to_pixels_y(self, self->height); + unsigned int underscore_height = get_height_for_char(self, '_'); + if (for_metrics && global_state.debug_font_fallback && underscore_height > ans) { + printf("Increasing cell height by %u pixels to work around buggy font that renders underscore outside the bounding box\n", underscore_height - ans); + } + return MAX(ans, underscore_height); +} + static inline bool set_font_size(Face *self, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt xdpi, FT_UInt ydpi, unsigned int desired_height, unsigned int cell_height) { int error = FT_Set_Char_Size(self->face, 0, char_height, xdpi, ydpi); if (!error) { - unsigned int ch = CALC_CELL_HEIGHT(self); + unsigned int ch = calc_cell_height(self, false); if (desired_height && ch != desired_height) { FT_F26Dot6 h = floor((double)char_height * (double)desired_height / (double) ch); return set_font_size(self, 0, h, xdpi, ydpi, 0, cell_height); @@ -147,17 +190,6 @@ set_size_for_face(PyObject *s, unsigned int desired_height, bool force, FONTS_DA return set_font_size(self, w, w, xdpi, ydpi, desired_height, fg->cell_height); } -static inline int -get_load_flags(int hinting, int hintstyle, int base) { - int flags = base; - if (hinting) { - if (hintstyle >= 3) flags |= FT_LOAD_TARGET_NORMAL; - else if (0 < hintstyle && hintstyle < 3) flags |= FT_LOAD_TARGET_LIGHT; - } else flags |= FT_LOAD_NO_HINTING; - return flags; -} - - static inline bool init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle, FONTS_DATA_HANDLE fg) { #define CPY(n) self->n = self->face->n; @@ -236,14 +268,6 @@ repr(Face *self) { } -static inline bool -load_glyph(Face *self, int glyph_index, int load_type) { - int flags = get_load_flags(self->hinting, self->hintstyle, load_type); - int error = FT_Load_Glyph(self->face, glyph_index, flags); - if (error) { set_freetype_error("Failed to load glyph, with error:", error); return false; } - return true; -} - static inline unsigned int calc_cell_width(Face *self) { unsigned int ans = 0; @@ -260,7 +284,7 @@ void cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness) { Face *self = (Face*)s; *cell_width = calc_cell_width(self); - *cell_height = CALC_CELL_HEIGHT(self); + *cell_height = calc_cell_height(self, true); *baseline = font_units_to_pixels_y(self, self->ascender); *underline_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->underline_position))); *underline_thickness = MAX(1, font_units_to_pixels_y(self, self->underline_thickness));