Add font_feature_settings

Close #2247
This commit is contained in:
Fredrick Brennan 2020-01-04 14:36:20 +08:00
parent 70071fe1f6
commit 1db613e95b
4 changed files with 84 additions and 9 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,48 @@ 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: If font_feature_settings is enabled, this feature has no effect.
'''))
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.
Note that this feature ignores the value of :code:`disable_ligatures`, because
it assumes you want to fine tune exactly which OpenType tags are
enabled/disabled. See examples below.
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,16 +72,16 @@ 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;
bool bold, italic, emoji_presentation, ffs_set;
} Font;
typedef struct {
@ -363,6 +364,22 @@ init_font(Font *f, PyObject *face, bool bold, bool italic, bool emoji_presentati
copy_hb_feature(f, LIGA_FEATURE); copy_hb_feature(f, DLIG_FEATURE);
}
copy_hb_feature(f, CALT_FEATURE);
if (font_feature_settings != NULL){
const char* face = postscript_name_for_face(f->face);
PyObject* o = PyDict_GetItemString(font_feature_settings, face);
if (o != NULL) {
long len = PyList_Size(o);
hb_feature_t* hb_feat = calloc(len, 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]);
}
f->ffs_hb_features = hb_feat;
f->num_ffs_hb_features = len;
}
}
return true;
}
@ -776,6 +793,9 @@ 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);
if (fobj->num_ffs_hb_features > 0)
hb_shape(font, harfbuzz_buffer, fobj->ffs_hb_features, fobj->num_ffs_hb_features);
else
hb_shape(font, harfbuzz_buffer, fobj->hb_features, fobj->num_hb_features - (disable_ligature ? 0 : 1));
unsigned int info_length, positions_length;
@ -1184,11 +1204,11 @@ 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",
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);

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']
)