Render font feature list in UI
This commit is contained in:
parent
57edb412e6
commit
a69d71d416
10 changed files with 74 additions and 28 deletions
|
|
@ -127,6 +127,7 @@ def render_face_sample(font: Descriptor, opts: Options, dpi_x: float, dpi_y: flo
|
|||
'style': font['style'],
|
||||
'psname': face.postscript_name(),
|
||||
'features': get_features(face.get_features()),
|
||||
'applied_features': face.applied_features(),
|
||||
}
|
||||
if is_variable(font):
|
||||
ns = get_named_style(face)
|
||||
|
|
|
|||
|
|
@ -125,6 +125,37 @@ func (self *face_panel) draw_family_style_select(_ loop.ScreenSize, start_y int,
|
|||
return y, nil
|
||||
}
|
||||
|
||||
func (self *face_panel) draw_font_features(_ loop.ScreenSize, start_y int, preview RenderedSampleTransmit) (y int, err error) {
|
||||
lp := self.handler.lp
|
||||
y = start_y
|
||||
if len(preview.Features) == 0 {
|
||||
return
|
||||
}
|
||||
formatted := make([]string, 0, len(preview.Features))
|
||||
for feat_tag, data := range preview.Features {
|
||||
var text string
|
||||
if preview.Applied_features[feat_tag] != "" {
|
||||
text = preview.Applied_features[feat_tag]
|
||||
text = strings.Replace(text, "+", lp.SprintStyled("fg=green", "+"), 1)
|
||||
text = strings.Replace(text, "-", lp.SprintStyled("fg=red", "-"), 1)
|
||||
text = strings.Replace(text, "=", lp.SprintStyled("fg=cyan", "="), 1)
|
||||
if data.Name != "" {
|
||||
text = fmt.Sprintf("%s: %s", data.Name, text)
|
||||
}
|
||||
} else {
|
||||
text = utils.IfElse(data.Name == "", feat_tag, data.Name)
|
||||
text = lp.SprintStyled("dim", text)
|
||||
}
|
||||
formatted = append(formatted, tui.InternalHyperlink(text, "feature:"+feat_tag))
|
||||
}
|
||||
utils.SortWithKey(formatted, func(a string) string {
|
||||
return strings.ToLower(wcswidth.StripEscapeCodes(a))
|
||||
})
|
||||
line := lp.SprintStyled(control_name_style, `Features`) + ": " + strings.Join(formatted, ", ")
|
||||
y = self.render_lines(start_y, ``, line)
|
||||
return
|
||||
}
|
||||
|
||||
func (self *handler) draw_preview_header(x int) {
|
||||
sz, _ := self.lp.ScreenSize()
|
||||
width := int(sz.WidthCells) - x
|
||||
|
|
@ -197,6 +228,9 @@ func (self *face_panel) draw_screen() (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if y, err = self.draw_font_features(sz, y, preview); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if int(sz.HeightCells)-y >= num_lines+2 {
|
||||
y += 1
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ type RenderedSampleTransmit struct {
|
|||
Style string `json:"style"`
|
||||
Psname string `json:"psname"`
|
||||
Features map[string]FeatureData `json:"features"`
|
||||
Applied_features map[string]string `json:"applied_features"`
|
||||
Variable_named_style NamedStyle `json:"variable_named_style"`
|
||||
Variable_axis_map map[string]float64 `json:"variable_axis_map"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -955,16 +955,7 @@ static CTFontRef nerd_font(CGFloat sz) {
|
|||
|
||||
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;
|
||||
return font_features_as_dict(&self->font_features);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ class Face:
|
|||
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_features(self) -> Dict[str, Optional[FeatureData]]: ...
|
||||
def applied_features(self) -> Tuple[str, ...]: ...
|
||||
def applied_features(self) -> Dict[str, str]: ...
|
||||
|
||||
|
||||
class CoreTextFont(TypedDict):
|
||||
|
|
@ -479,7 +479,7 @@ class CTFace:
|
|||
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_features(self) -> Dict[str, Optional[FeatureData]]: ...
|
||||
def applied_features(self) -> Tuple[str, ...]: ...
|
||||
def applied_features(self) -> Dict[str, str]: ...
|
||||
|
||||
|
||||
def coretext_all_fonts(monospaced_only: bool) -> Tuple[CoreTextFont, ...]:
|
||||
|
|
|
|||
|
|
@ -301,6 +301,32 @@ add_feature(FontFeatures *output, const hb_feature_t *feature) {
|
|||
output->features[output->count++] = *feature;
|
||||
}
|
||||
|
||||
static const char*
|
||||
tag_to_string(uint32_t tag, uint8_t bytes[5]) {
|
||||
bytes[0] = (tag >> 24) & 0xff;
|
||||
bytes[1] = (tag >> 16) & 0xff;
|
||||
bytes[2] = (tag >> 8) & 0xff;
|
||||
bytes[3] = (tag) & 0xff;
|
||||
bytes[4] = 0;
|
||||
return (const char*)bytes;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
font_features_as_dict(const FontFeatures *font_features) {
|
||||
RAII_PyObject(ans, PyDict_New());
|
||||
if (!ans) return NULL;
|
||||
char buf[256];
|
||||
char tag[5] = {0};
|
||||
for (size_t i = 0; i < font_features->count; i++) {
|
||||
tag_to_string(font_features->features[i].tag, (unsigned char*)tag);
|
||||
hb_feature_to_string(&font_features->features[i], buf, arraysz(buf));
|
||||
PyObject *t = PyUnicode_FromString(buf);
|
||||
if (!t) return NULL;
|
||||
if (PyDict_SetItemString(ans, tag, t) != 0) return NULL;
|
||||
}
|
||||
Py_INCREF(ans); return ans;
|
||||
}
|
||||
|
||||
bool
|
||||
create_features_for_face(const char *psname, PyObject *features, FontFeatures *output) {
|
||||
size_t count_from_descriptor = features ? PyTuple_GET_SIZE(features): 0;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ bool
|
|||
read_features_from_font_table(const uint8_t *table, size_t table_len, PyObject *name_lookup_table, PyObject *output);
|
||||
FontFeatures* features_for_face(PyObject *);
|
||||
bool create_features_for_face(const char* psname, PyObject *features, FontFeatures* output);
|
||||
PyObject*
|
||||
font_features_as_dict(const FontFeatures *font_features);
|
||||
|
||||
static inline void
|
||||
right_shift_canvas(pixel *canvas, size_t width, size_t height, size_t amt) {
|
||||
|
|
|
|||
|
|
@ -141,9 +141,9 @@ class FeatureDefinition(NamedTuple):
|
|||
'zero': FeatureDefinition('Slashed Zero', Type.boolean),
|
||||
}
|
||||
for i in range(1, 100):
|
||||
known_features[f'cv{i:2d}'] = FeatureDefinition(f'Character Variant {i}', Type.index)
|
||||
known_features[f'cv{i:02d}'] = FeatureDefinition(f'Character Variant {i}', Type.index)
|
||||
for i in range(1, 20):
|
||||
known_features[f'ss{i:2d}'] = FeatureDefinition(f'Stylistic Set {i}', Type.boolean)
|
||||
known_features[f'ss{i:02d}'] = FeatureDefinition(f'Stylistic Set {i}', Type.boolean)
|
||||
# }}}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -849,16 +849,7 @@ get_variation(Face *self, PyObject *a UNUSED) {
|
|||
|
||||
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;
|
||||
return font_features_as_dict(&self->font_features);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
|
|
|||
|
|
@ -147,11 +147,11 @@ def t(x, **kw):
|
|||
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"')
|
||||
self.ae(face_from_descriptor(ff['medium']).applied_features(), {'dlig': '-dlig'})
|
||||
self.ae(face_from_descriptor(ff['bold']).applied_features(), {})
|
||||
opts.font_family = parse_font_spec('family="liberation mono" features="dlig test=3"')
|
||||
ff = get_font_files(opts)
|
||||
self.ae(face_from_descriptor(ff['medium']).applied_features(), ('dlig', 'test'))
|
||||
self.ae(face_from_descriptor(ff['medium']).applied_features(), {'dlig': 'dlig', 'test': 'test=3'})
|
||||
|
||||
|
||||
class Rendering(BaseTest):
|
||||
|
|
|
|||
Loading…
Reference in a new issue