This commit is contained in:
Kovid Goyal 2020-01-07 07:18:31 +05:30
commit db85e07d41
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 88 additions and 13 deletions

View File

@ -410,6 +410,19 @@ def handle_symbol_map(key, val, ans):
ans['symbol_map'].update(parse_symbol_map(val))
@special_handler
def handle_font_feature_settings(key, val, ans):
parts = val.split(':', maxsplit=2)
if len(parts) != 2:
if parts[0] == "none":
return
else:
log_error("Ignoring invalid font_feature_settings for font {}".format(parts[0]))
else:
font, features = parts
ans['font_feature_settings'].update({font.strip(): features.strip().split()})
@special_handler
def handle_kitten_alias(key, val, ans):
parts = val.split(maxsplit=2)
@ -495,7 +508,7 @@ def option_names_for_completion():
def parse_config(lines, check_keys=True, accumulate_bad_lines=None):
ans = {'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': [], 'env': {}, 'kitten_aliases': {}}
ans = {'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': [], 'env': {}, 'kitten_aliases': {}, 'font_feature_settings': {}}
parse_config_base(
lines,
defaults,

View File

@ -276,6 +276,45 @@ or by defining shortcuts for it in kitty.conf, for example::
map alt+1 disable_ligatures_in active always
map alt+2 disable_ligatures_in all never
map alt+3 disable_ligatures_in tab cursor
Note: This function is equivalent to setting :code:`font_feature_settings` to
:code:`-liga -dlig -calt`.
'''))
o('font_feature_settings', 'none', long_text=_('''
Choose exactly which OpenType features to enable or disable. This is useful as
some fonts might have many features worthwhile in a terminalfor example, Fira
Code Retina includes a discretionary feature, :code:`zero`, which in that font
changes the appearance of the zero (0), to make it more easily distinguishable
from Ø. Fira Code Retina also includes other discretionary features known as
Stylistic Sets which have the tags :code:`ss01` through :code:`ss20`.
Note that this code is indexed by PostScript name, and not TTF name or font
family; this allows you to define very precise feature settings; e.g. you can
disable a feature in the italic font but not in the regular font.
To get the PostScript name for a font, ask Fontconfig for it, using your family
name::
$ fc-match "Fira Code" postscriptname
:postscriptname=FiraCode-Regular
$ fc-match "Fira Code Retina" postscriptname
:postscriptname=FiraCode-Retina
$ fc-match "TT2020Base:style=italic" postscriptname
:postscriptname=TT2020Base-Italic
Enable alternate zero and oldstyle numerals::
font_feature_settings FiraCode-Retina: +zero +onum
Enable only alternate zero::
font_feature_settings FiraCode-Retina: +zero
Disable the normal ligatures, but keep the :code:`calt` feature which (in this
font) breaks up monotony::
font_feature_settings TT2020StyleB-Regular: -liga +calt
'''))

View File

@ -62,6 +62,7 @@ static hb_feature_t hb_features[3] = {{0}};
static char_type shape_buffer[4096] = {0};
static size_t max_texture_size = 1024, max_array_len = 1024;
typedef enum { LIGA_FEATURE, DLIG_FEATURE, CALT_FEATURE } HBFeature;
static PyObject* font_feature_settings = NULL;
typedef struct {
char_type left, right;
@ -71,14 +72,14 @@ typedef struct {
static SymbolMap *symbol_maps = NULL;
static size_t num_symbol_maps = 0;
typedef struct {
PyObject *face;
// Map glyphs to sprite map co-ords
SpritePosition sprite_map[1024];
hb_feature_t hb_features[8];
hb_feature_t* ffs_hb_features;
size_t num_hb_features;
size_t num_ffs_hb_features;
SpecialGlyphCache special_glyph_cache[SPECIAL_GLYPH_CACHE_SIZE];
bool bold, italic, emoji_presentation;
} Font;
@ -359,16 +360,34 @@ init_font(Font *f, PyObject *face, bool bold, bool italic, bool emoji_presentati
f->bold = bold; f->italic = italic; f->emoji_presentation = emoji_presentation;
f->num_hb_features = 0;
const char *psname = postscript_name_for_face(face);
if (strstr(psname, "NimbusMonoPS-") == psname) {
copy_hb_feature(f, LIGA_FEATURE); copy_hb_feature(f, DLIG_FEATURE);
}
copy_hb_feature(f, LIGA_FEATURE);
copy_hb_feature(f, DLIG_FEATURE);
copy_hb_feature(f, CALT_FEATURE);
if (font_feature_settings != NULL){
PyObject* o = PyDict_GetItemString(font_feature_settings, psname);
if (o != NULL) {
long len = PyList_Size(o);
if (len==0) return true;
f->num_ffs_hb_features = len + f->num_hb_features;
hb_feature_t* hb_feat = calloc(f->num_ffs_hb_features, sizeof(hb_feature_t));
for (long i = len-1; i >= 0; i--) {
PyObject* item = PyList_GetItem(o, i);
const char* feat = PyUnicode_AsUTF8(item);
hb_feature_from_string(feat, -1, &hb_feat[i]);
}
for (size_t i = 0; i < f->num_hb_features; i++)
hb_feat[len+i] = hb_features[i];
f->ffs_hb_features = hb_feat;
}
}
return true;
}
static inline void
del_font(Font *f) {
Py_CLEAR(f->face);
if (f->num_ffs_hb_features > 0)
free(f->ffs_hb_features);
free_maps(f);
f->bold = false; f->italic = false;
}
@ -776,7 +795,10 @@ shape(CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, index_type num_cells, hb
group_state.last_gpu_cell = first_gpu_cell + (num_cells ? num_cells - 1 : 0);
load_hb_buffer(first_cpu_cell, first_gpu_cell, num_cells);
hb_shape(font, harfbuzz_buffer, fobj->hb_features, fobj->num_hb_features - (disable_ligature ? 0 : 1));
if (fobj->num_ffs_hb_features > 0)
hb_shape(font, harfbuzz_buffer, fobj->ffs_hb_features, fobj->num_ffs_hb_features - (disable_ligature ? 0 : fobj->num_hb_features));
else
hb_shape(font, harfbuzz_buffer, fobj->hb_features, fobj->num_hb_features - (disable_ligature ? 0 : fobj->num_hb_features));
unsigned int info_length, positions_length;
group_state.info = hb_buffer_get_glyph_infos(harfbuzz_buffer, &info_length);
@ -1059,7 +1081,7 @@ render_run(FontGroup *fg, CPUCell *first_cpu_cell, GPUCell *first_gpu_cell, inde
default:
shape_run(first_cpu_cell, first_gpu_cell, num_cells, &fg->fonts[font_idx], disable_ligature_strategy == DISABLE_LIGATURES_ALWAYS);
if (pua_space_ligature) merge_groups_for_pua_space_ligature();
else if (cursor_offset > -1) {
else if (cursor_offset > -1) { // false if DISABLE_LIGATURES_NEVER
index_type left, right;
split_run_at_offset(cursor_offset, &left, &right);
if (right > left) {
@ -1183,12 +1205,12 @@ DescriptorIndices descriptor_indices = {0};
static PyObject*
set_font_data(PyObject UNUSED *m, PyObject *args) {
PyObject *sm;
Py_CLEAR(box_drawing_function); Py_CLEAR(prerender_function); Py_CLEAR(descriptor_for_idx);
if (!PyArg_ParseTuple(args, "OOOIIIIO!d",
Py_CLEAR(box_drawing_function); Py_CLEAR(prerender_function); Py_CLEAR(descriptor_for_idx); Py_CLEAR(font_feature_settings);
if (!PyArg_ParseTuple(args, "OOOIIIIO!dO",
&box_drawing_function, &prerender_function, &descriptor_for_idx,
&descriptor_indices.bold, &descriptor_indices.italic, &descriptor_indices.bi, &descriptor_indices.num_symbol_fonts,
&PyTuple_Type, &sm, &global_state.font_sz_in_pts)) return NULL;
Py_INCREF(box_drawing_function); Py_INCREF(prerender_function); Py_INCREF(descriptor_for_idx);
&PyTuple_Type, &sm, &global_state.font_sz_in_pts, &font_feature_settings)) return NULL;
Py_INCREF(box_drawing_function); Py_INCREF(prerender_function); Py_INCREF(descriptor_for_idx); Py_INCREF(font_feature_settings);
free_font_groups();
clear_symbol_maps();
num_symbol_maps = PyTuple_GET_SIZE(sm);
@ -1290,6 +1312,7 @@ finalize(void) {
Py_CLEAR(box_drawing_function);
Py_CLEAR(prerender_function);
Py_CLEAR(descriptor_for_idx);
Py_CLEAR(font_feature_settings);
free_font_groups();
if (harfbuzz_buffer) { hb_buffer_destroy(harfbuzz_buffer); harfbuzz_buffer = NULL; }
free(group_state.groups); group_state.groups = NULL; group_state.groups_capacity = 0;

View File

@ -103,7 +103,7 @@ def set_font_family(opts=None, override_font_size=None, debug_font_matching=Fals
set_font_data(
render_box_drawing, prerender_function, descriptor_for_idx,
indices['bold'], indices['italic'], indices['bi'], num_symbol_fonts,
sm, sz
sm, sz, opts['font_feature_settings']
)