mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-25 17:52:02 +02:00
Add tests to validate feature-from-spec
This commit is contained in:
@@ -953,6 +953,20 @@ get_variation(CTFace *self) {
|
|||||||
return variation_to_python(src);
|
return variation_to_python(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
applied_features(CTFace *self, PyObject *a UNUSED) {
|
||||||
|
RAII_PyObject(ans, PyTuple_New(self->font_features.count));
|
||||||
|
if (!ans) return NULL;
|
||||||
|
char buf[256];
|
||||||
|
for (size_t i = 0; i < self->font_features.count; i++) {
|
||||||
|
hb_feature_to_string(&self->font_features.features[i], buf, arraysz(buf));
|
||||||
|
PyObject *t = PyUnicode_FromString(buf);
|
||||||
|
if (!t) return NULL;
|
||||||
|
PyTuple_SET_ITEM(ans, i, t);
|
||||||
|
}
|
||||||
|
Py_INCREF(ans); return ans;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
get_features(CTFace *self, PyObject *a UNUSED) {
|
get_features(CTFace *self, PyObject *a UNUSED) {
|
||||||
if (!ensure_name_table(self)) return NULL;
|
if (!ensure_name_table(self)) return NULL;
|
||||||
@@ -1012,6 +1026,7 @@ static PyMethodDef methods[] = {
|
|||||||
METHODB(display_name, METH_NOARGS),
|
METHODB(display_name, METH_NOARGS),
|
||||||
METHODB(postscript_name, METH_NOARGS),
|
METHODB(postscript_name, METH_NOARGS),
|
||||||
METHODB(get_variable_data, METH_NOARGS),
|
METHODB(get_variable_data, METH_NOARGS),
|
||||||
|
METHODB(applied_features, METH_NOARGS),
|
||||||
METHODB(get_features, METH_NOARGS),
|
METHODB(get_features, METH_NOARGS),
|
||||||
METHODB(get_variation, METH_NOARGS),
|
METHODB(get_variation, METH_NOARGS),
|
||||||
METHODB(identify_for_debug, METH_NOARGS),
|
METHODB(identify_for_debug, METH_NOARGS),
|
||||||
|
|||||||
@@ -442,6 +442,7 @@ class Face:
|
|||||||
def render_sample_text(self, text: str, width: int, height: int, fg_color: int = 0xffffff) -> bytes: ...
|
def render_sample_text(self, text: str, width: int, height: int, fg_color: int = 0xffffff) -> bytes: ...
|
||||||
def get_variation(self) -> Optional[Dict[str, float]]: ...
|
def get_variation(self) -> Optional[Dict[str, float]]: ...
|
||||||
def get_features(self) -> Dict[str, Optional[FeatureData]]: ...
|
def get_features(self) -> Dict[str, Optional[FeatureData]]: ...
|
||||||
|
def applied_features(self) -> Tuple[str, ...]: ...
|
||||||
|
|
||||||
|
|
||||||
class CoreTextFont(TypedDict):
|
class CoreTextFont(TypedDict):
|
||||||
@@ -478,6 +479,7 @@ class CTFace:
|
|||||||
def render_sample_text(self, text: str, width: int, height: int, fg_color: int = 0xffffff) -> bytes: ...
|
def render_sample_text(self, text: str, width: int, height: int, fg_color: int = 0xffffff) -> bytes: ...
|
||||||
def get_variation(self) -> Optional[Dict[str, float]]: ...
|
def get_variation(self) -> Optional[Dict[str, float]]: ...
|
||||||
def get_features(self) -> Dict[str, Optional[FeatureData]]: ...
|
def get_features(self) -> Dict[str, Optional[FeatureData]]: ...
|
||||||
|
def applied_features(self) -> Tuple[str, ...]: ...
|
||||||
|
|
||||||
|
|
||||||
def coretext_all_fonts(monospaced_only: bool) -> Tuple[CoreTextFont, ...]:
|
def coretext_all_fonts(monospaced_only: bool) -> Tuple[CoreTextFont, ...]:
|
||||||
|
|||||||
@@ -290,6 +290,17 @@ desc_to_face(PyObject *desc, FONTS_DATA_HANDLE fg) {
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_feature(FontFeatures *output, const hb_feature_t *feature) {
|
||||||
|
for (size_t i = 0; i < output->count; i++) {
|
||||||
|
if (output->features[i].tag == feature->tag) {
|
||||||
|
output->features[i] = *feature;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output->features[output->count++] = *feature;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
create_features_for_face(const char *psname, PyObject *features, FontFeatures *output) {
|
create_features_for_face(const char *psname, PyObject *features, FontFeatures *output) {
|
||||||
size_t count_from_descriptor = features ? PyTuple_GET_SIZE(features): 0;
|
size_t count_from_descriptor = features ? PyTuple_GET_SIZE(features): 0;
|
||||||
@@ -304,16 +315,16 @@ create_features_for_face(const char *psname, PyObject *features, FontFeatures *o
|
|||||||
output->features = calloc(MAX(2u, count_from_opts + count_from_descriptor), sizeof(output->features[0]));
|
output->features = calloc(MAX(2u, count_from_opts + count_from_descriptor), sizeof(output->features[0]));
|
||||||
if (!output->features) { PyErr_NoMemory(); return false; }
|
if (!output->features) { PyErr_NoMemory(); return false; }
|
||||||
for (size_t i = 0; i < count_from_opts; i++) {
|
for (size_t i = 0; i < count_from_opts; i++) {
|
||||||
output->features[output->count++] = from_opts->features[i];
|
add_feature(output, &from_opts->features[i]);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < count_from_descriptor; i++) {
|
for (size_t i = 0; i < count_from_descriptor; i++) {
|
||||||
ParsedFontFeature *f = (ParsedFontFeature*)PyTuple_GET_ITEM(features, i);
|
ParsedFontFeature *f = (ParsedFontFeature*)PyTuple_GET_ITEM(features, i);
|
||||||
output->features[output->count++] = f->feature;
|
add_feature(output, &f->feature);
|
||||||
}
|
}
|
||||||
if (!output->count) {
|
if (!output->count) {
|
||||||
if (strstr(psname, "NimbusMonoPS-") == psname) {
|
if (strstr(psname, "NimbusMonoPS-") == psname) {
|
||||||
output->features[output->count++] = hb_features[LIGA_FEATURE];
|
add_feature(output, &hb_features[LIGA_FEATURE]);
|
||||||
output->features[output->count++] = hb_features[DLIG_FEATURE];
|
add_feature(output, &hb_features[DLIG_FEATURE]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -315,8 +315,11 @@ def get_font_from_spec(
|
|||||||
resolved_medium_font: Optional[Descriptor] = None, match_is_more_specific_than_family: Event = Event()
|
resolved_medium_font: Optional[Descriptor] = None, match_is_more_specific_than_family: Event = Event()
|
||||||
) -> Descriptor:
|
) -> Descriptor:
|
||||||
if not spec.is_system:
|
if not spec.is_system:
|
||||||
return get_fine_grained_font(spec, bold, italic, resolved_medium_font=resolved_medium_font, family_axis_values=family_axis_values,
|
ans = get_fine_grained_font(spec, bold, italic, resolved_medium_font=resolved_medium_font, family_axis_values=family_axis_values,
|
||||||
match_is_more_specific_than_family=match_is_more_specific_than_family)
|
match_is_more_specific_than_family=match_is_more_specific_than_family)
|
||||||
|
if spec.features:
|
||||||
|
ans['features'] = spec.features
|
||||||
|
return ans
|
||||||
family = spec.system or ''
|
family = spec.system or ''
|
||||||
if family == 'auto':
|
if family == 'auto':
|
||||||
if bold or italic:
|
if bold or italic:
|
||||||
|
|||||||
@@ -847,6 +847,20 @@ get_variation(Face *self, PyObject *a UNUSED) {
|
|||||||
Py_INCREF(ans); return ans;
|
Py_INCREF(ans); return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
applied_features(Face *self, PyObject *a UNUSED) {
|
||||||
|
RAII_PyObject(ans, PyTuple_New(self->font_features.count));
|
||||||
|
if (!ans) return NULL;
|
||||||
|
char buf[256];
|
||||||
|
for (size_t i = 0; i < self->font_features.count; i++) {
|
||||||
|
hb_feature_to_string(&self->font_features.features[i], buf, arraysz(buf));
|
||||||
|
PyObject *t = PyUnicode_FromString(buf);
|
||||||
|
if (!t) return NULL;
|
||||||
|
PyTuple_SET_ITEM(ans, i, t);
|
||||||
|
}
|
||||||
|
Py_INCREF(ans); return ans;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
get_features(Face *self, PyObject *a UNUSED) {
|
get_features(Face *self, PyObject *a UNUSED) {
|
||||||
FT_Error err;
|
FT_Error err;
|
||||||
@@ -1023,6 +1037,7 @@ static PyMethodDef methods[] = {
|
|||||||
METHODB(identify_for_debug, METH_NOARGS),
|
METHODB(identify_for_debug, METH_NOARGS),
|
||||||
METHODB(extra_data, METH_NOARGS),
|
METHODB(extra_data, METH_NOARGS),
|
||||||
METHODB(get_variable_data, METH_NOARGS),
|
METHODB(get_variable_data, METH_NOARGS),
|
||||||
|
METHODB(applied_features, METH_NOARGS),
|
||||||
METHODB(get_features, METH_NOARGS),
|
METHODB(get_features, METH_NOARGS),
|
||||||
METHODB(get_variation, METH_NOARGS),
|
METHODB(get_variation, METH_NOARGS),
|
||||||
METHODB(get_best_name, METH_O),
|
METHODB(get_best_name, METH_O),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from functools import partial
|
|||||||
from kitty.constants import is_macos, read_kitty_resource
|
from kitty.constants import is_macos, read_kitty_resource
|
||||||
from kitty.fast_data_types import (
|
from kitty.fast_data_types import (
|
||||||
DECAWM,
|
DECAWM,
|
||||||
|
ParsedFontFeature,
|
||||||
get_fallback_font,
|
get_fallback_font,
|
||||||
sprite_map_set_layout,
|
sprite_map_set_layout,
|
||||||
sprite_map_set_limits,
|
sprite_map_set_limits,
|
||||||
@@ -33,6 +34,7 @@ def parse_font_spec(spec):
|
|||||||
class Selection(BaseTest):
|
class Selection(BaseTest):
|
||||||
|
|
||||||
def test_font_selection(self):
|
def test_font_selection(self):
|
||||||
|
self.set_options({'font_features': {'LiberationMono': (ParsedFontFeature('-dlig'),)}})
|
||||||
opts = Options()
|
opts = Options()
|
||||||
fonts_map = all_fonts_map(True)
|
fonts_map = all_fonts_map(True)
|
||||||
names = set(fonts_map['family_map']) | set(fonts_map['variable_map'])
|
names = set(fonts_map['family_map']) | set(fonts_map['variable_map'])
|
||||||
@@ -140,6 +142,17 @@ class Selection(BaseTest):
|
|||||||
t('bold', spec='variable_name=CascadiaCodeRoman wght=603')
|
t('bold', spec='variable_name=CascadiaCodeRoman wght=603')
|
||||||
t('bi', spec='variable_name= wght=603')
|
t('bi', spec='variable_name= wght=603')
|
||||||
|
|
||||||
|
# Test font features
|
||||||
|
if has('liberation mono'):
|
||||||
|
opts = Options()
|
||||||
|
opts.font_family = parse_font_spec('family="liberation mono"')
|
||||||
|
ff = get_font_files(opts)
|
||||||
|
self.ae(face_from_descriptor(ff['medium']).applied_features(), ('-dlig',))
|
||||||
|
self.ae(face_from_descriptor(ff['bold']).applied_features(), ())
|
||||||
|
opts.font_family = parse_font_spec('family="liberation mono" features="dlig test"')
|
||||||
|
ff = get_font_files(opts)
|
||||||
|
self.ae(face_from_descriptor(ff['medium']).applied_features(), ('dlig', 'test'))
|
||||||
|
|
||||||
|
|
||||||
class Rendering(BaseTest):
|
class Rendering(BaseTest):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user