This commit is contained in:
Kovid Goyal 2020-12-17 11:02:26 +05:30
commit 6681652c21
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
8 changed files with 88 additions and 13 deletions

View File

@ -21,6 +21,7 @@ from .conf.utils import (
)
from .config_data import all_options, parse_mods, type_convert
from .constants import cache_dir, defconf, is_macos
from .fonts import FontFeature
from .key_names import get_key_name_lookup, key_name_aliases
from .options_stub import Options as OptionsStub
from .typing import TypedDict
@ -524,14 +525,6 @@ def handle_symbol_map(key: str, val: str, ans: Dict[str, Any]) -> None:
ans['symbol_map'].update(parse_symbol_map(val))
class FontFeature(str):
def __new__(cls, name: str, parsed: bytes) -> 'FontFeature':
ans: FontFeature = str.__new__(cls, name) # type: ignore
ans.parsed = parsed # type: ignore
return ans
@special_handler
def handle_font_features(key: str, val: str, ans: Dict[str, Any]) -> None:
if val != 'none':

View File

@ -325,6 +325,8 @@ Note that this code is indexed by PostScript name, and not the 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.
By default they are derived automatically, by the OSes font system (linux only).
To get the PostScript name for a font, use :code:`kitty + list-fonts --psnames`:
.. code-block:: sh

View File

@ -410,6 +410,7 @@ class FontConfigPattern(TypedDict):
postscript_name: str
style: str
spacing: str
fontfeatures: List[str]
weight: int
width: int
slant: int
@ -441,6 +442,12 @@ def fc_match(
pass
def fc_match_postscript_name(
postscript_name: str
) -> FontConfigPattern:
pass
class CoreTextFont(TypedDict):
path: str
postscript_name: str

View File

@ -37,7 +37,21 @@ pattern_as_dict(FcPattern *pat) {
if (PyDict_SetItemString(ans, #name, p) != 0) { Py_CLEAR(p); Py_CLEAR(ans); return NULL; } \
Py_CLEAR(p); \
}}
#define L(type, get, which, conv, name) { \
type out; PyObject *p; int n = 0; \
PyObject *l = PyList_New(0); \
while (get(pat, which, n, &out) == FcResultMatch) { \
p = conv(out); if (p == NULL) { Py_CLEAR(l); Py_CLEAR(ans); return NULL; } \
if (PyList_Append(l, p) != 0) { Py_CLEAR(p); Py_CLEAR(l); Py_CLEAR(ans); return NULL; } \
Py_CLEAR(p); \
n++; \
} \
if (PyDict_SetItemString(ans, #name, l) != 0) { Py_CLEAR(l); Py_CLEAR(ans); return NULL; } \
Py_CLEAR(l); \
}
#define S(which, key) G(FcChar8*, FcPatternGetString, which, PS, key)
#define LS(which, key) L(FcChar8*, FcPatternGetString, which, PS, key)
#define I(which, key) G(int, FcPatternGetInteger, which, PyLong_FromLong, key)
#define B(which, key) G(int, FcPatternGetBool, which, pybool, key)
#define E(which, key, conv) G(int, FcPatternGetInteger, which, conv, key)
@ -46,6 +60,7 @@ pattern_as_dict(FcPattern *pat) {
S(FC_STYLE, style);
S(FC_FULLNAME, full_name);
S(FC_POSTSCRIPT_NAME, postscript_name);
LS(FC_FONT_FEATURES, fontfeatures);
I(FC_WEIGHT, weight);
I(FC_WIDTH, width)
I(FC_SLANT, slant);
@ -179,6 +194,26 @@ end:
return ans;
}
static PyObject*
fc_match_postscript_name(PyObject UNUSED *self, PyObject *args) {
char *postscript_name = NULL;
FcPattern *pat = NULL;
PyObject *ans = NULL;
if (!PyArg_ParseTuple(args, "|z", &postscript_name)) return NULL;
pat = FcPatternCreate();
if (pat == NULL) return PyErr_NoMemory();
if (!postscript_name || strlen(postscript_name) == 0) return NULL;
AP(FcPatternAddString, FC_POSTSCRIPT_NAME, (const FcChar8*)postscript_name, "postscript_name");
ans = _fc_match(pat);
end:
if (pat != NULL) FcPatternDestroy(pat);
return ans;
}
PyObject*
specialize_font_descriptor(PyObject *base_descriptor, FONTS_DATA_HANDLE fg) {
PyObject *p = PyDict_GetItemString(base_descriptor, "path"), *ans = NULL;
@ -224,6 +259,7 @@ end:
static PyMethodDef module_methods[] = {
METHODB(fc_list, METH_VARARGS),
METHODB(fc_match, METH_VARARGS),
METHODB(fc_match_postscript_name, METH_VARARGS),
{NULL, NULL, 0, NULL} /* Sentinel */
};

View File

@ -9,3 +9,11 @@ class ListedFont(TypedDict):
full_name: str
postscript_name: str
is_monospace: bool
class FontFeature(str):
def __new__(cls, name: str, parsed: bytes) -> 'FontFeature':
ans: FontFeature = str.__new__(cls, name) # type: ignore
ans.parsed = parsed # type: ignore
return ans

View File

@ -50,6 +50,11 @@ def list_fonts() -> Generator[ListedFont, None, None]:
yield {'family': f, 'full_name': fn, 'postscript_name': fd['postscript_name'] or '', 'is_monospace': is_mono}
def find_font_features(postscript_name: str) -> Tuple[str, ...]:
"""Not Implemented"""
return tuple()
def find_best_match(family: str, bold: bool = False, italic: bool = False) -> CoreTextFont:
q = re.sub(r'\s+', ' ', family.lower())
font_map = all_fonts_map()

View File

@ -8,12 +8,14 @@ from typing import Dict, Generator, List, Optional, Tuple, cast
from kitty.fast_data_types import (
FC_DUAL, FC_MONO, FC_SLANT_ITALIC, FC_SLANT_ROMAN, FC_WEIGHT_BOLD,
FC_WEIGHT_REGULAR, FC_WIDTH_NORMAL, fc_list, fc_match as fc_match_impl
FC_WEIGHT_REGULAR, FC_WIDTH_NORMAL, fc_list, fc_match as fc_match_impl,
fc_match_postscript_name, parse_font_feature
)
from kitty.options_stub import Options
from kitty.typing import FontConfigPattern
from kitty.utils import log_error
from . import ListedFont
from . import ListedFont, FontFeature
attr_map = {(False, False): 'font_family',
(True, False): 'bold_font',
@ -69,6 +71,24 @@ def fc_match(family: str, bold: bool, italic: bool, spacing: int = FC_MONO) -> F
return fc_match_impl(family, bold, italic, spacing)
def find_font_features(postscript_name: str) -> Tuple[str, ...]:
pat = fc_match_postscript_name(postscript_name)
if 'postscript_name' not in pat or pat['postscript_name'] != postscript_name or 'fontfeatures' not in pat:
return tuple()
features = []
for feat in pat['fontfeatures']:
try:
parsed = parse_font_feature(feat)
except ValueError:
log_error('Ignoring invalid font feature: {}'.format(feat))
else:
features.append(FontFeature(feat, parsed))
return tuple(features)
def find_best_match(family: str, bold: bool = False, italic: bool = False, monospaced: bool = True) -> FontConfigPattern:
q = family_name_to_key(family)
font_map = all_fonts_map(monospaced)

View File

@ -25,9 +25,9 @@ from kitty.typing import CoreTextFont, FontConfigPattern
from kitty.utils import log_error
if is_macos:
from .core_text import get_font_files as get_font_files_coretext, font_for_family as font_for_family_macos
from .core_text import get_font_files as get_font_files_coretext, font_for_family as font_for_family_macos, find_font_features
else:
from .fontconfig import get_font_files as get_font_files_fontconfig, font_for_family as font_for_family_fontconfig
from .fontconfig import get_font_files as get_font_files_fontconfig, font_for_family as font_for_family_fontconfig, find_font_features
FontObject = Union[CoreTextFont, FontConfigPattern]
current_faces: List[Tuple[FontObject, bool, bool]] = []
@ -189,12 +189,16 @@ def set_font_family(opts: Optional[OptionsStub] = None, override_font_size: Opti
before = len(current_faces)
sm = create_symbol_map(opts)
num_symbol_fonts = len(current_faces) - before
font_features = {}
for face, _, _ in current_faces:
font_features[face['postscript_name']] = find_font_features(face['postscript_name'])
font_features.update(opts.font_features)
if debug_font_matching:
dump_faces(ftypes, indices)
set_font_data(
render_box_drawing, prerender_function, descriptor_for_idx,
indices['bold'], indices['italic'], indices['bi'], num_symbol_fonts,
sm, sz, opts.font_features
sm, sz, font_features
)