Add support for emoji presentation when choosing fallback fonts
This commit is contained in:
parent
e830b7edf7
commit
96c93fa252
@ -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,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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*);
|
||||||
|
|||||||
@ -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
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user