diff --git a/README.asciidoc b/README.asciidoc index 44da11e75..4a09f742b 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -452,11 +452,9 @@ Some programs, like powerline, vim with fancy gutter symbols/status-bar, etc. use unicode characters from the private use area to represent symbols. Often these symbols are square and should be rendered in two cells. However, since private use area symbols all have their width set to one in the unicode -standard, kitty renders them either smaller or truncated. The correct solution -for this is to use either use different symbols that are not square, or to use -a font that defines ligatures with the space character for these symbols. See -link:https://github.com/kovidgoyal/kitty/issues/182[#182] for a discussion of -the approach using ligatures. +standard, kitty renders them either smaller or truncated. The exception (as of +kitty 0.8.1) is if these characters are followed by a space or empty cell in +which case kitty makes use of the extra cell to render them in two cells. === How do I build kitty.app on macOS? diff --git a/kitty/fonts.c b/kitty/fonts.c index 235b925c2..b831c73ea 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -794,6 +794,17 @@ shape_run(Cell *first_cell, index_type num_cells, Font *font) { #undef MAX_GLYPHS_IN_GROUP } +static inline void +merge_groups_for_pua_space_ligature() { + if (G(group_idx) == 1) { + Group *g = G(groups), *g1 = G(groups) + 1; + g->num_cells += g1->num_cells; + g->num_glyphs += g1->num_glyphs; + g->num_glyphs = MIN(g->num_glyphs, MAX_NUM_EXTRA_GLYPHS + 1); + G(group_idx) = 0; + } +} + static inline void render_groups(Font *font) { unsigned idx = 0; @@ -847,10 +858,11 @@ test_shape(PyObject UNUSED *self, PyObject *args) { #undef G static inline void -render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx) { +render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx, bool pua_space_ligature) { switch(font_idx) { default: shape_run(first_cell, num_cells, &fonts.fonts[font_idx]); + if (pua_space_ligature) merge_groups_for_pua_space_ligature(); render_groups(&fonts.fonts[font_idx]); break; case BLANK_FONT: @@ -865,9 +877,14 @@ render_run(Cell *first_cell, index_type num_cells, ssize_t font_idx) { } } +static inline bool +is_private_use(char_type ch) { + return (0xe000 <= ch && ch <= 0xf8ff) || (0xF0000 <= ch && ch <= 0xFFFFF) || (0x100000 <= ch && ch <= 0x10FFFF); +} + void render_line(Line *line) { -#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(line->cells + first_cell_in_run, i - first_cell_in_run, run_font_idx); +#define RENDER if (run_font_idx != NO_FONT && i > first_cell_in_run) render_run(line->cells + first_cell_in_run, i - first_cell_in_run, run_font_idx, false); ssize_t run_font_idx = NO_FONT; index_type first_cell_in_run, i; attrs_type prev_width = 0; @@ -875,6 +892,16 @@ render_line(Line *line) { if (prev_width == 2) { prev_width = 0; continue; } Cell *cell = line->cells + i; ssize_t cell_font_idx = font_for_cell(cell); + if (is_private_use(cell->ch) && i + 1 < line->xnum && (line->cells[i+1].ch == ' ' || line->cells[i+1].ch == 0) && cell_font_idx != BOX_FONT && cell_font_idx != MISSING_FONT) { + // We have a private use char followed by a space char, render it as a two cell ligature. + RENDER; + render_run(line->cells + i, 2, cell_font_idx, true); + run_font_idx = NO_FONT; + first_cell_in_run = i + 2; + prev_width = line->cells[i+1].attrs & WIDTH_MASK; + i++; + continue; + } prev_width = cell->attrs & WIDTH_MASK; if (run_font_idx == NO_FONT) run_font_idx = cell_font_idx; if (run_font_idx == cell_font_idx) continue;