Support default font_features from fontconfig

This commit is contained in:
Antoine Bertin 2020-12-16 14:58:38 +01:00
parent e96ff19a7a
commit 822c9367c6
7 changed files with 72 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

@ -410,6 +410,7 @@ class FontConfigPattern(TypedDict):
postscript_name: str
style: str
spacing: str
fontfeatures: 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

@ -46,6 +46,7 @@ pattern_as_dict(FcPattern *pat) {
S(FC_STYLE, style);
S(FC_FULLNAME, full_name);
S(FC_POSTSCRIPT_NAME, postscript_name);
S(FC_FONT_FEATURES, fontfeatures);
I(FC_WEIGHT, weight);
I(FC_WIDTH, width)
I(FC_SLANT, slant);
@ -179,6 +180,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 +245,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'].split():
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
)