Skip to content

Commit 6681652

Browse files
committed
Merge branch 'master' of https://github.com/Diaoul/kitty
2 parents ed4b5f1 + cf7eaea commit 6681652

File tree

8 files changed

+88
-13
lines changed

8 files changed

+88
-13
lines changed

kitty/config.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
)
2222
from .config_data import all_options, parse_mods, type_convert
2323
from .constants import cache_dir, defconf, is_macos
24+
from .fonts import FontFeature
2425
from .key_names import get_key_name_lookup, key_name_aliases
2526
from .options_stub import Options as OptionsStub
2627
from .typing import TypedDict
@@ -524,14 +525,6 @@ def handle_symbol_map(key: str, val: str, ans: Dict[str, Any]) -> None:
524525
ans['symbol_map'].update(parse_symbol_map(val))
525526

526527

527-
class FontFeature(str):
528-
529-
def __new__(cls, name: str, parsed: bytes) -> 'FontFeature':
530-
ans: FontFeature = str.__new__(cls, name) # type: ignore
531-
ans.parsed = parsed # type: ignore
532-
return ans
533-
534-
535528
@special_handler
536529
def handle_font_features(key: str, val: str, ans: Dict[str, Any]) -> None:
537530
if val != 'none':

kitty/config_data.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,8 @@ def disable_ligatures(x: str) -> int:
325325
family. This allows you to define very precise feature settings; e.g. you can
326326
disable a feature in the italic font but not in the regular font.
327327
328+
By default they are derived automatically, by the OSes font system (linux only).
329+
328330
To get the PostScript name for a font, use :code:`kitty + list-fonts --psnames`:
329331
330332
.. code-block:: sh

kitty/fast_data_types.pyi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ class FontConfigPattern(TypedDict):
410410
postscript_name: str
411411
style: str
412412
spacing: str
413+
fontfeatures: List[str]
413414
weight: int
414415
width: int
415416
slant: int
@@ -441,6 +442,12 @@ def fc_match(
441442
pass
442443

443444

445+
def fc_match_postscript_name(
446+
postscript_name: str
447+
) -> FontConfigPattern:
448+
pass
449+
450+
444451
class CoreTextFont(TypedDict):
445452
path: str
446453
postscript_name: str

kitty/fontconfig.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,21 @@ pattern_as_dict(FcPattern *pat) {
3737
if (PyDict_SetItemString(ans, #name, p) != 0) { Py_CLEAR(p); Py_CLEAR(ans); return NULL; } \
3838
Py_CLEAR(p); \
3939
}}
40+
41+
#define L(type, get, which, conv, name) { \
42+
type out; PyObject *p; int n = 0; \
43+
PyObject *l = PyList_New(0); \
44+
while (get(pat, which, n, &out) == FcResultMatch) { \
45+
p = conv(out); if (p == NULL) { Py_CLEAR(l); Py_CLEAR(ans); return NULL; } \
46+
if (PyList_Append(l, p) != 0) { Py_CLEAR(p); Py_CLEAR(l); Py_CLEAR(ans); return NULL; } \
47+
Py_CLEAR(p); \
48+
n++; \
49+
} \
50+
if (PyDict_SetItemString(ans, #name, l) != 0) { Py_CLEAR(l); Py_CLEAR(ans); return NULL; } \
51+
Py_CLEAR(l); \
52+
}
4053
#define S(which, key) G(FcChar8*, FcPatternGetString, which, PS, key)
54+
#define LS(which, key) L(FcChar8*, FcPatternGetString, which, PS, key)
4155
#define I(which, key) G(int, FcPatternGetInteger, which, PyLong_FromLong, key)
4256
#define B(which, key) G(int, FcPatternGetBool, which, pybool, key)
4357
#define E(which, key, conv) G(int, FcPatternGetInteger, which, conv, key)
@@ -46,6 +60,7 @@ pattern_as_dict(FcPattern *pat) {
4660
S(FC_STYLE, style);
4761
S(FC_FULLNAME, full_name);
4862
S(FC_POSTSCRIPT_NAME, postscript_name);
63+
LS(FC_FONT_FEATURES, fontfeatures);
4964
I(FC_WEIGHT, weight);
5065
I(FC_WIDTH, width)
5166
I(FC_SLANT, slant);
@@ -179,6 +194,26 @@ fc_match(PyObject UNUSED *self, PyObject *args) {
179194
return ans;
180195
}
181196

197+
static PyObject*
198+
fc_match_postscript_name(PyObject UNUSED *self, PyObject *args) {
199+
char *postscript_name = NULL;
200+
FcPattern *pat = NULL;
201+
PyObject *ans = NULL;
202+
203+
if (!PyArg_ParseTuple(args, "|z", &postscript_name)) return NULL;
204+
pat = FcPatternCreate();
205+
if (pat == NULL) return PyErr_NoMemory();
206+
if (!postscript_name || strlen(postscript_name) == 0) return NULL;
207+
208+
AP(FcPatternAddString, FC_POSTSCRIPT_NAME, (const FcChar8*)postscript_name, "postscript_name");
209+
210+
ans = _fc_match(pat);
211+
212+
end:
213+
if (pat != NULL) FcPatternDestroy(pat);
214+
return ans;
215+
}
216+
182217
PyObject*
183218
specialize_font_descriptor(PyObject *base_descriptor, FONTS_DATA_HANDLE fg) {
184219
PyObject *p = PyDict_GetItemString(base_descriptor, "path"), *ans = NULL;
@@ -224,6 +259,7 @@ create_fallback_face(PyObject UNUSED *base_face, CPUCell* cell, bool bold, bool
224259
static PyMethodDef module_methods[] = {
225260
METHODB(fc_list, METH_VARARGS),
226261
METHODB(fc_match, METH_VARARGS),
262+
METHODB(fc_match_postscript_name, METH_VARARGS),
227263
{NULL, NULL, 0, NULL} /* Sentinel */
228264
};
229265

kitty/fonts/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,11 @@ class ListedFont(TypedDict):
99
full_name: str
1010
postscript_name: str
1111
is_monospace: bool
12+
13+
14+
class FontFeature(str):
15+
16+
def __new__(cls, name: str, parsed: bytes) -> 'FontFeature':
17+
ans: FontFeature = str.__new__(cls, name) # type: ignore
18+
ans.parsed = parsed # type: ignore
19+
return ans

kitty/fonts/core_text.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ def list_fonts() -> Generator[ListedFont, None, None]:
5050
yield {'family': f, 'full_name': fn, 'postscript_name': fd['postscript_name'] or '', 'is_monospace': is_mono}
5151

5252

53+
def find_font_features(postscript_name: str) -> Tuple[str, ...]:
54+
"""Not Implemented"""
55+
return tuple()
56+
57+
5358
def find_best_match(family: str, bold: bool = False, italic: bool = False) -> CoreTextFont:
5459
q = re.sub(r'\s+', ' ', family.lower())
5560
font_map = all_fonts_map()

kitty/fonts/fontconfig.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88

99
from kitty.fast_data_types import (
1010
FC_DUAL, FC_MONO, FC_SLANT_ITALIC, FC_SLANT_ROMAN, FC_WEIGHT_BOLD,
11-
FC_WEIGHT_REGULAR, FC_WIDTH_NORMAL, fc_list, fc_match as fc_match_impl
11+
FC_WEIGHT_REGULAR, FC_WIDTH_NORMAL, fc_list, fc_match as fc_match_impl,
12+
fc_match_postscript_name, parse_font_feature
1213
)
1314
from kitty.options_stub import Options
1415
from kitty.typing import FontConfigPattern
16+
from kitty.utils import log_error
1517

16-
from . import ListedFont
18+
from . import ListedFont, FontFeature
1719

1820
attr_map = {(False, False): 'font_family',
1921
(True, False): 'bold_font',
@@ -69,6 +71,24 @@ def fc_match(family: str, bold: bool, italic: bool, spacing: int = FC_MONO) -> F
6971
return fc_match_impl(family, bold, italic, spacing)
7072

7173

74+
def find_font_features(postscript_name: str) -> Tuple[str, ...]:
75+
pat = fc_match_postscript_name(postscript_name)
76+
77+
if 'postscript_name' not in pat or pat['postscript_name'] != postscript_name or 'fontfeatures' not in pat:
78+
return tuple()
79+
80+
features = []
81+
for feat in pat['fontfeatures']:
82+
try:
83+
parsed = parse_font_feature(feat)
84+
except ValueError:
85+
log_error('Ignoring invalid font feature: {}'.format(feat))
86+
else:
87+
features.append(FontFeature(feat, parsed))
88+
89+
return tuple(features)
90+
91+
7292
def find_best_match(family: str, bold: bool = False, italic: bool = False, monospaced: bool = True) -> FontConfigPattern:
7393
q = family_name_to_key(family)
7494
font_map = all_fonts_map(monospaced)

kitty/fonts/render.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
from kitty.utils import log_error
2626

2727
if is_macos:
28-
from .core_text import get_font_files as get_font_files_coretext, font_for_family as font_for_family_macos
28+
from .core_text import get_font_files as get_font_files_coretext, font_for_family as font_for_family_macos, find_font_features
2929
else:
30-
from .fontconfig import get_font_files as get_font_files_fontconfig, font_for_family as font_for_family_fontconfig
30+
from .fontconfig import get_font_files as get_font_files_fontconfig, font_for_family as font_for_family_fontconfig, find_font_features
3131

3232
FontObject = Union[CoreTextFont, FontConfigPattern]
3333
current_faces: List[Tuple[FontObject, bool, bool]] = []
@@ -189,12 +189,16 @@ def set_font_family(opts: Optional[OptionsStub] = None, override_font_size: Opti
189189
before = len(current_faces)
190190
sm = create_symbol_map(opts)
191191
num_symbol_fonts = len(current_faces) - before
192+
font_features = {}
193+
for face, _, _ in current_faces:
194+
font_features[face['postscript_name']] = find_font_features(face['postscript_name'])
195+
font_features.update(opts.font_features)
192196
if debug_font_matching:
193197
dump_faces(ftypes, indices)
194198
set_font_data(
195199
render_box_drawing, prerender_function, descriptor_for_idx,
196200
indices['bold'], indices['italic'], indices['bi'], num_symbol_fonts,
197-
sm, sz, opts.font_features
201+
sm, sz, font_features
198202
)
199203

200204

0 commit comments

Comments
 (0)