diff --git a/kitty/core_text.m b/kitty/core_text.m index 23d1bcf5b..15d58c460 100644 --- a/kitty/core_text.m +++ b/kitty/core_text.m @@ -312,7 +312,7 @@ harfbuzz_font_for_face(PyObject* s) { } void -cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness) { +cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness, unsigned int* strikethrough_position, unsigned int* strikethrough_thickness) { // See https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/TypoFeatures/TextSystemFeatures.html CTFace *self = (CTFace*)s; #define count (128 - 32) @@ -332,6 +332,8 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u *underline_position = (unsigned int)floor(self->ascent - self->underline_position + 0.5); *underline_thickness = (unsigned int)ceil(MAX(0.1, self->underline_thickness)); *baseline = (unsigned int)self->ascent; + *strikethrough_position = (unsigned int)floor(*baseline * 0.65); + *strikethrough_thickness = *underline_thickness; // 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. #define W "AQWMH_gyl " diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 580f29969..814c4179e 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -902,7 +902,7 @@ def set_font_data( box_drawing_func: Callable[[int, int, int, float], Tuple[int, Union[bytearray, bytes, Array]]], prerender_func: Callable[ - [int, int, int, int, int, float, float, float, float], + [int, int, int, int, int, int, int, float, float, float, float], Tuple[Union[Array, int], ...]], descriptor_for_idx: Callable[[int], Tuple[FontObject, bool, bool]], bold: int, italic: int, bold_italic: int, num_symbol_fonts: int, diff --git a/kitty/fonts.c b/kitty/fonts.c index b045587f5..d35dc902a 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -85,7 +85,7 @@ typedef struct { typedef struct { FONTS_DATA_HEAD id_type id; - unsigned int baseline, underline_position, underline_thickness; + unsigned int baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness; size_t fonts_capacity, fonts_count, fallback_fonts_count; ssize_t medium_font_idx, bold_font_idx, italic_font_idx, bi_font_idx, first_symbol_font_idx, first_fallback_font_idx; Font *fonts; @@ -422,8 +422,8 @@ python_send_to_gpu(FONTS_DATA_HANDLE fg, unsigned int x, unsigned int y, unsigne static inline void calc_cell_metrics(FontGroup *fg) { - unsigned int cell_height, cell_width, baseline, underline_position, underline_thickness; - cell_metrics(fg->fonts[fg->medium_font_idx].face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness); + unsigned int cell_height, cell_width, baseline, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness; + cell_metrics(fg->fonts[fg->medium_font_idx].face, &cell_width, &cell_height, &baseline, &underline_position, &underline_thickness, &strikethrough_position, &strikethrough_thickness); if (!cell_width) fatal("Failed to calculate cell width for the specified font"); unsigned int before_cell_height = cell_height; int cw = cell_width, ch = cell_height; @@ -455,7 +455,7 @@ calc_cell_metrics(FontGroup *fg) { } sprite_tracker_set_layout(&fg->sprite_tracker, cell_width, cell_height); fg->cell_width = cell_width; fg->cell_height = cell_height; - fg->baseline = baseline; fg->underline_position = underline_position; fg->underline_thickness = underline_thickness; + fg->baseline = baseline; fg->underline_position = underline_position; fg->underline_thickness = underline_thickness, fg->strikethrough_position = strikethrough_position, fg->strikethrough_thickness = strikethrough_thickness; free(fg->canvas); fg->canvas = calloc(CELLS_IN_CANVAS * fg->cell_width * fg->cell_height, sizeof(pixel)); if (!fg->canvas) fatal("Out of memory allocating canvas for font group"); @@ -1243,7 +1243,7 @@ send_prerendered_sprites(FontGroup *fg) { current_send_sprite_to_gpu((FONTS_DATA_HANDLE)fg, x, y, z, fg->canvas); do_increment(fg, &error); if (error != 0) { sprite_map_set_error(error); PyErr_Print(); fatal("Failed"); } - PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIffdd", fg->cell_width, fg->cell_height, fg->baseline, fg->underline_position, fg->underline_thickness, OPT(cursor_beam_thickness), OPT(cursor_underline_thickness), fg->logical_dpi_x, fg->logical_dpi_y); + PyObject *args = PyObject_CallFunction(prerender_function, "IIIIIIIffdd", fg->cell_width, fg->cell_height, fg->baseline, fg->underline_position, fg->underline_thickness, fg->strikethrough_position, fg->strikethrough_thickness, OPT(cursor_beam_thickness), OPT(cursor_underline_thickness), fg->logical_dpi_x, fg->logical_dpi_y); if (args == NULL) { PyErr_Print(); fatal("Failed to pre-render cells"); } for (ssize_t i = 0; i < PyTuple_GET_SIZE(args) - 1; i++) { x = fg->sprite_tracker.x; y = fg->sprite_tracker.y; z = fg->sprite_tracker.z; diff --git a/kitty/fonts.h b/kitty/fonts.h index 361597cd6..7a9cb6f21 100644 --- a/kitty/fonts.h +++ b/kitty/fonts.h @@ -25,7 +25,7 @@ int get_glyph_width(PyObject *, glyph_index); bool is_glyph_empty(PyObject *, glyph_index); hb_font_t* harfbuzz_font_for_face(PyObject*); bool set_size_for_face(PyObject*, unsigned int, bool, FONTS_DATA_HANDLE); -void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*); +void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*); bool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored, FONTS_DATA_HANDLE, bool center_glyph); PyObject* create_fallback_face(PyObject *base_face, CPUCell* cell, bool bold, bool italic, bool emoji_presentation, FONTS_DATA_HANDLE fg); PyObject* specialize_font_descriptor(PyObject *base_descriptor, FONTS_DATA_HANDLE); diff --git a/kitty/fonts/render.py b/kitty/fonts/render.py index 157834845..bd9d02bff 100644 --- a/kitty/fonts/render.py +++ b/kitty/fonts/render.py @@ -191,7 +191,9 @@ def render_special( cell_width: int = 0, cell_height: int = 0, baseline: int = 0, underline_position: int = 0, - underline_thickness: int = 0 + underline_thickness: int = 0, + strikethrough_position: int = 0, + strikethrough_thickness: int = 0 ) -> ctypes.Array: underline_position = min(underline_position, cell_height - underline_thickness) CharTexture = ctypes.c_ubyte * (cell_width * cell_height) @@ -216,8 +218,7 @@ def render_special( t = max(1, min(cell_height - underline_position - 1, t)) dl([add_line, add_line, add_dline, add_curl][underline], underline_position, t, cell_height) if strikethrough: - pos = int(0.65 * baseline) - dl(add_line, pos, underline_thickness, cell_height) + dl(add_line, strikethrough_position, strikethrough_thickness, cell_height) return ans @@ -268,6 +269,8 @@ def prerender_function( baseline: int, underline_position: int, underline_thickness: int, + strikethrough_position: int, + strikethrough_thickness: int, cursor_beam_thickness: float, cursor_underline_thickness: float, dpi_x: float, @@ -276,7 +279,8 @@ def prerender_function( # Pre-render the special underline, strikethrough and missing and cursor cells f = partial( render_special, cell_width=cell_width, cell_height=cell_height, baseline=baseline, - underline_position=underline_position, underline_thickness=underline_thickness) + underline_position=underline_position, underline_thickness=underline_thickness, + strikethrough_position=strikethrough_position, strikethrough_thickness=strikethrough_thickness) c = partial( render_cursor, cursor_beam_thickness=cursor_beam_thickness, cursor_underline_thickness=cursor_underline_thickness, cell_width=cell_width, diff --git a/kitty/freetype.c b/kitty/freetype.c index 90ee84a9a..741b4229a 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -22,12 +22,13 @@ #include FT_FREETYPE_H #include FT_BITMAP_H +#include FT_TRUETYPE_TABLES_H typedef struct { PyObject_HEAD FT_Face face; unsigned int units_per_EM; - int ascender, descender, height, max_advance_width, max_advance_height, underline_position, underline_thickness; + int ascender, descender, height, max_advance_width, max_advance_height, underline_position, underline_thickness, strikethrough_position, strikethrough_thickness; int hinting, hintstyle, index; bool is_scalable, has_color; float size_in_pts; @@ -207,6 +208,12 @@ init_ft_face(Face *self, PyObject *path, int hinting, int hintstyle, FONTS_DATA_ if (self->harfbuzz_font == NULL) { PyErr_NoMemory(); return false; } hb_ft_font_set_load_flags(self->harfbuzz_font, get_load_flags(self->hinting, self->hintstyle, FT_LOAD_DEFAULT)); + TT_OS2 *os2 = (TT_OS2*)FT_Get_Sfnt_Table(self->face, FT_SFNT_OS2); + if (os2 != NULL) { + self->strikethrough_position = os2->yStrikeoutPosition; + self->strikethrough_thickness = os2->yStrikeoutSize; + } + self->path = path; Py_INCREF(self->path); self->index = self->face->face_index & 0xFFFF; @@ -263,11 +270,11 @@ static PyObject * repr(Face *self) { const char *ps_name = FT_Get_Postscript_Name(self->face); return PyUnicode_FromFormat( - "Face(family=%s, style=%s, ps_name=%s, path=%S, index=%d, is_scalable=%S, has_color=%S, ascender=%i, descender=%i, height=%i, underline_position=%i, underline_thickness=%i)", + "Face(family=%s, style=%s, ps_name=%s, path=%S, index=%d, is_scalable=%S, has_color=%S, ascender=%i, descender=%i, height=%i, underline_position=%i, underline_thickness=%i, strikethrough_position=%i, strikethrough_thickness=%i)", self->face->family_name ? self->face->family_name : "", self->face->style_name ? self->face->style_name : "", ps_name ? ps_name: "", self->path, self->index, self->is_scalable ? Py_True : Py_False, self->has_color ? Py_True : Py_False, - self->ascender, self->descender, self->height, self->underline_position, self->underline_thickness + self->ascender, self->descender, self->height, self->underline_position, self->underline_thickness, self->strikethrough_position, self->strikethrough_thickness ); } @@ -292,13 +299,24 @@ calc_cell_width(Face *self) { } void -cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness) { +cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness, unsigned int* strikethrough_position, unsigned int* strikethrough_thickness) { Face *self = (Face*)s; *cell_width = calc_cell_width(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)); + + if (self->strikethrough_position != 0) { + *strikethrough_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels_y(self, MAX(0, self->ascender - self->strikethrough_position))); + } else { + *strikethrough_position = (unsigned int)floor(*baseline * 0.65); + } + if (self->strikethrough_thickness != 0) { + *strikethrough_thickness = MAX(1, font_units_to_pixels_y(self, self->strikethrough_thickness)); + } else { + *strikethrough_thickness = *underline_thickness; + } } unsigned int @@ -659,6 +677,8 @@ static PyMemberDef members[] = { MEM(max_advance_height, T_INT), MEM(underline_position, T_INT), MEM(underline_thickness, T_INT), + MEM(strikethrough_position, T_INT), + MEM(strikethrough_thickness, T_INT), MEM(is_scalable, T_BOOL), MEM(path, T_OBJECT_EX), {NULL} /* Sentinel */