Add support for emoji presentation when choosing fallback fonts

This commit is contained in:
Kovid Goyal 2018-02-06 11:11:22 +05:30
parent e830b7edf7
commit 96c93fa252
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
8 changed files with 28 additions and 41 deletions

View File

@ -283,7 +283,6 @@ type_map = {
'inactive_tab_font_style': tab_font_style, 'inactive_tab_font_style': tab_font_style,
'inactive_text_alpha': unit_float, 'inactive_text_alpha': unit_float,
'url_style': url_style, 'url_style': url_style,
'prefer_color_emoji': to_bool,
'copy_on_select': to_bool, 'copy_on_select': to_bool,
'tab_bar_edge': tab_bar_edge, 'tab_bar_edge': tab_bar_edge,
} }

View File

@ -178,14 +178,18 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) {
} }
PyObject* PyObject*
create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic) { create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic, bool emoji_presentation) {
CTFace *self = (CTFace*)base_face; CTFace *self = (CTFace*)base_face;
CTFontRef new_font;
if (emoji_presentation) new_font = CTFontCreateWithName((CFStringRef)@"AppleColorEmoji", self->scaled_point_sz, NULL);
else {
char text[256] = {0}; char text[256] = {0};
cell_as_utf8(cell, true, text, ' '); cell_as_utf8(cell, true, text, ' ');
CFStringRef str = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8); CFStringRef str = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8);
if (str == NULL) return PyErr_NoMemory(); if (str == NULL) return PyErr_NoMemory();
CTFontRef new_font = find_substitute_face(str, self->ct_font); new_font = find_substitute_face(str, self->ct_font);
CFRelease(str); CFRelease(str);
}
if (new_font == NULL) return NULL; if (new_font == NULL) return NULL;
return (PyObject*)ct_face(new_font); return (PyObject*)ct_face(new_font);
} }

View File

@ -189,15 +189,14 @@ end:
} }
PyObject* PyObject*
create_fallback_face(PyObject UNUSED *base_face, Cell* cell, bool bold, bool italic) { create_fallback_face(PyObject UNUSED *base_face, Cell* cell, bool bold, bool italic, bool emoji_presentation) {
PyObject *ans = NULL; PyObject *ans = NULL;
FcPattern *pat = FcPatternCreate(); FcPattern *pat = FcPatternCreate();
if (pat == NULL) return PyErr_NoMemory(); if (pat == NULL) return PyErr_NoMemory();
bool emoji = (cell->attrs & WIDTH_MASK) == 2 && is_emoji(cell->ch); AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)(emoji_presentation ? "emoji" : "monospace"), "family");
AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)(emoji ? "emoji" : "monospace"), "family"); if (!emoji_presentation && bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); }
if (!emoji && bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); } if (!emoji_presentation && italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); }
if (!emoji && italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); } if (emoji_presentation) { AP(FcPatternAddBool, FC_COLOR, true, "color"); }
if (emoji) { AP(FcPatternAddBool, FC_COLOR, OPT(prefer_color_emoji), "color"); }
size_t num = cell_as_unicode(cell, true, char_buf, ' '); size_t num = cell_as_unicode(cell, true, char_buf, ' ');
add_charset(pat, num); add_charset(pat, num);
PyObject *d = _fc_match(pat); PyObject *d = _fc_match(pat);

View File

@ -65,7 +65,7 @@ typedef struct {
// Map glyphs to sprite map co-ords // Map glyphs to sprite map co-ords
SpritePosition sprite_map[1024]; SpritePosition sprite_map[1024];
SpecialGlyphCache special_glyph_cache[SPECIAL_GLYPH_CACHE_SIZE]; SpecialGlyphCache special_glyph_cache[SPECIAL_GLYPH_CACHE_SIZE];
bool bold, italic; bool bold, italic, emoji_presentation;
} Font; } Font;
typedef struct { typedef struct {
@ -250,12 +250,12 @@ desc_to_face(PyObject *desc) {
static inline bool static inline bool
init_font(Font *f, PyObject *descriptor, bool bold, bool italic, bool is_face) { init_font(Font *f, PyObject *descriptor, bool bold, bool italic, bool is_face, bool emoji_presentation) {
PyObject *face; PyObject *face;
if (is_face) { face = descriptor; Py_INCREF(face); } if (is_face) { face = descriptor; Py_INCREF(face); }
else { face = desc_to_face(descriptor); if (face == NULL) return false; } else { face = desc_to_face(descriptor); if (face == NULL) return false; }
f->face = face; f->face = face;
f->bold = bold; f->italic = italic; f->bold = bold; f->italic = italic; f->emoji_presentation = emoji_presentation;
return true; return true;
} }
@ -340,7 +340,7 @@ has_cell_text(Font *self, Cell *cell) {
static inline ssize_t static inline ssize_t
load_fallback_font(Cell *cell, bool bold, bool italic) { load_fallback_font(Cell *cell, bool bold, bool italic, bool emoji_presentation) {
if (fonts.fallback_fonts_count > 100) { fprintf(stderr, "Too many fallback fonts\n"); return MISSING_FONT; } if (fonts.fallback_fonts_count > 100) { fprintf(stderr, "Too many fallback fonts\n"); return MISSING_FONT; }
ssize_t f; ssize_t f;
@ -348,7 +348,7 @@ load_fallback_font(Cell *cell, bool bold, bool italic) {
else f = italic ? fonts.italic_font_idx : fonts.medium_font_idx; else f = italic ? fonts.italic_font_idx : fonts.medium_font_idx;
if (f < 0) f = fonts.medium_font_idx; if (f < 0) f = fonts.medium_font_idx;
PyObject *face = create_fallback_face(fonts.fonts[f].face, cell, bold, italic); PyObject *face = create_fallback_face(fonts.fonts[f].face, cell, bold, italic, emoji_presentation);
if (face == NULL) { PyErr_Print(); return MISSING_FONT; } if (face == NULL) { PyErr_Print(); return MISSING_FONT; }
if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; } if (face == Py_None) { Py_DECREF(face); return MISSING_FONT; }
set_size_for_face(face, cell_height, true); set_size_for_face(face, cell_height, true);
@ -356,7 +356,7 @@ load_fallback_font(Cell *cell, bool bold, bool italic) {
ensure_space_for(&fonts, fonts, Font, fonts.fonts_count + 1, fonts_capacity, 5, true); ensure_space_for(&fonts, fonts, Font, fonts.fonts_count + 1, fonts_capacity, 5, true);
ssize_t ans = fonts.first_fallback_font_idx + fonts.fallback_fonts_count; ssize_t ans = fonts.first_fallback_font_idx + fonts.fallback_fonts_count;
Font *af = &fonts.fonts[ans]; Font *af = &fonts.fonts[ans];
if (!init_font(af, face, bold, italic, true)) fatal("Out of memory"); if (!init_font(af, face, bold, italic, true, emoji_presentation)) fatal("Out of memory");
Py_DECREF(face); Py_DECREF(face);
fonts.fallback_fonts_count++; fonts.fallback_fonts_count++;
fonts.fonts_count++; fonts.fonts_count++;
@ -368,24 +368,17 @@ static inline ssize_t
fallback_font(Cell *cell) { fallback_font(Cell *cell) {
bool bold = (cell->attrs >> BOLD_SHIFT) & 1; bool bold = (cell->attrs >> BOLD_SHIFT) & 1;
bool italic = (cell->attrs >> ITALIC_SHIFT) & 1; bool italic = (cell->attrs >> ITALIC_SHIFT) & 1;
bool emoji_presentation = (cell->attrs & WIDTH_MASK) == 2 && is_emoji(cell->ch) && cell->cc_idx[0] != mark_for_codepoint(0xfe0e);
// Load the emoji fallback font first as on Linux there are a bunch of
// non-color fonts that provide some emoji glyphs.
if (fonts.fallback_fonts_count < 1) {
Cell c = {0};
c.ch = 0x1f648; // 🙈
load_fallback_font(&c, false, false);
}
// Check if one of the existing fallback fonts has this text // Check if one of the existing fallback fonts has this text
for (size_t i = 0, j = fonts.first_fallback_font_idx; i < fonts.fallback_fonts_count; i++, j++) { for (size_t i = 0, j = fonts.first_fallback_font_idx; i < fonts.fallback_fonts_count; i++, j++) {
Font *ff = fonts.fonts +j; Font *ff = fonts.fonts +j;
if (ff->bold == bold && ff->italic == italic && has_cell_text(ff, cell)) { if (ff->bold == bold && ff->italic == italic && ff->emoji_presentation == emoji_presentation && has_cell_text(ff, cell)) {
return j; return j;
} }
} }
return load_fallback_font(cell, bold, italic); return load_fallback_font(cell, bold, italic, emoji_presentation);
} }
static inline ssize_t static inline ssize_t
@ -882,7 +875,7 @@ set_font(PyObject UNUSED *m, PyObject *args) {
for (size_t i = 0; i < fonts.fonts_count; i++) del_font(fonts.fonts + i); for (size_t i = 0; i < fonts.fonts_count; i++) del_font(fonts.fonts + i);
ensure_space_for(&fonts, fonts, Font, num_fonts, fonts_capacity, 5, true); ensure_space_for(&fonts, fonts, Font, num_fonts, fonts_capacity, 5, true);
fonts.fonts_count = 1; fonts.fonts_count = 1;
#define A(attr, bold, italic) { if(attr) { if (!init_font(&fonts.fonts[fonts.fonts_count], attr, bold, italic, false)) return NULL; fonts.attr##_font_idx = fonts.fonts_count++; } else fonts.attr##_font_idx = -1; } #define A(attr, bold, italic) { if(attr) { if (!init_font(&fonts.fonts[fonts.fonts_count], attr, bold, italic, false, false)) return NULL; fonts.attr##_font_idx = fonts.fonts_count++; } else fonts.attr##_font_idx = -1; }
A(medium, false, false); A(medium, false, false);
A(bold, true, false); A(italic, false, true); A(bi, true, true); A(bold, true, false); A(italic, false, true); A(bi, true, true);
#undef A #undef A
@ -894,7 +887,7 @@ set_font(PyObject UNUSED *m, PyObject *args) {
PyObject *face; PyObject *face;
int bold, italic; int bold, italic;
if (!PyArg_ParseTuple(PyTuple_GET_ITEM(smf, i), "Opp", &face, &bold, &italic)) return NULL; if (!PyArg_ParseTuple(PyTuple_GET_ITEM(smf, i), "Opp", &face, &bold, &italic)) return NULL;
if (!init_font(fonts.fonts + fonts.fonts_count++, face, bold != 0, italic != 0, false)) return NULL; if (!init_font(fonts.fonts + fonts.fonts_count++, face, bold != 0, italic != 0, false, false)) return NULL;
} }
for (size_t i = 0; i < fonts.symbol_maps_count; i++) { for (size_t i = 0; i < fonts.symbol_maps_count; i++) {
unsigned int left, right, font_idx; unsigned int left, right, font_idx;

View File

@ -21,7 +21,7 @@ hb_font_t* harfbuzz_font_for_face(PyObject*);
bool set_size_for_face(PyObject*, unsigned int, bool); bool set_size_for_face(PyObject*, unsigned int, bool);
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*);
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); 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);
PyObject* create_fallback_face(PyObject *base_face, Cell* cell, bool bold, bool italic); PyObject* create_fallback_face(PyObject *base_face, Cell* cell, bool bold, bool italic, bool emoji_presentation);
PyObject* specialize_font_descriptor(PyObject *base_descriptor); PyObject* specialize_font_descriptor(PyObject *base_descriptor);
PyObject* face_from_path(const char *path, int index); PyObject* face_from_path(const char *path, int index);
PyObject* face_from_descriptor(PyObject*); PyObject* face_from_descriptor(PyObject*);

View File

@ -382,9 +382,3 @@ macos_option_as_alt yes
# The number is a percentage of maximum volume. # The number is a percentage of maximum volume.
# See man XBell for details. # See man XBell for details.
x11_bell_volume 80 x11_bell_volume 80
# Prefer color emoji fonts when available. Note that this only works
# on systems such as Linux that use fontconfig. On other OSes, the emoji
# font used is system dependent. It can be overriden using symbol_map in the kitty
# configuration.
prefer_color_emoji yes

View File

@ -331,7 +331,6 @@ PYWRAP1(set_options) {
#define S(name, convert) { GA(name); global_state.opts.name = convert(ret); Py_DECREF(ret); if (PyErr_Occurred()) return NULL; } #define S(name, convert) { GA(name); global_state.opts.name = convert(ret); Py_DECREF(ret); if (PyErr_Occurred()) return NULL; }
S(visual_bell_duration, PyFloat_AsDouble); S(visual_bell_duration, PyFloat_AsDouble);
S(enable_audio_bell, PyObject_IsTrue); S(enable_audio_bell, PyObject_IsTrue);
S(prefer_color_emoji, PyObject_IsTrue);
S(focus_follows_mouse, PyObject_IsTrue); S(focus_follows_mouse, PyObject_IsTrue);
S(cursor_blink_interval, PyFloat_AsDouble); S(cursor_blink_interval, PyFloat_AsDouble);
S(cursor_stop_blinking_after, PyFloat_AsDouble); S(cursor_stop_blinking_after, PyFloat_AsDouble);

View File

@ -24,7 +24,6 @@ typedef struct {
double repaint_delay, input_delay; double repaint_delay, input_delay;
bool focus_follows_mouse; bool focus_follows_mouse;
bool macos_option_as_alt, macos_hide_titlebar; bool macos_option_as_alt, macos_hide_titlebar;
bool prefer_color_emoji;
int adjust_line_height_px, adjust_column_width_px; int adjust_line_height_px, adjust_column_width_px;
float adjust_line_height_frac, adjust_column_width_frac; float adjust_line_height_frac, adjust_column_width_frac;
int x11_bell_volume; int x11_bell_volume;