From 1d6cd27c6ff54051a489dba7598df07671796451 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 May 2024 15:38:44 +0530 Subject: [PATCH] Code to get specs from options --- kittens/choose_fonts/backend.py | 25 ++++++++-- kittens/choose_fonts/family_list.go | 10 ++++ kittens/choose_fonts/types.go | 17 +++++++ kittens/choose_fonts/ui.go | 16 ++++--- kitty/fonts/common.py | 74 +++++++++++++++++++++++++++-- 5 files changed, 128 insertions(+), 14 deletions(-) diff --git a/kittens/choose_fonts/backend.py b/kittens/choose_fonts/backend.py index cd1a8fbab..1bf52406c 100644 --- a/kittens/choose_fonts/backend.py +++ b/kittens/choose_fonts/backend.py @@ -5,12 +5,13 @@ import json import string import sys import tempfile -from typing import Any, Dict, Tuple, TypedDict +from typing import Any, Dict, Literal, Tuple, TypedDict +from kitty.cli import create_default_opts from kitty.conf.utils import to_color from kitty.constants import kitten_exe from kitty.fonts import Descriptor -from kitty.fonts.common import face_from_descriptor, get_font_files, get_variable_data_for_descriptor +from kitty.fonts.common import face_from_descriptor, get_font_files, get_variable_data_for_descriptor, spec_for_descriptor from kitty.fonts.list import create_family_groups from kitty.fonts.render import display_bitmap from kitty.options.types import Options @@ -95,13 +96,31 @@ def render_family_sample( return ans +OptNames = Literal['font_family', 'bold_font', 'italic_font', 'bold_italic_font'] +ResolvedFace = Dict[Literal['family', 'spec'], str] + + +def resolved_faces(opts: Options) -> Dict[OptNames, ResolvedFace]: + font_files = get_font_files(opts) + ans: Dict[OptNames, ResolvedFace] = {} + def d(key: Literal['medium', 'bold', 'italic', 'bi'], opt_name: OptNames) -> None: + descriptor = font_files[key] + ans[opt_name] = {'family': descriptor['family'], 'spec': spec_for_descriptor(descriptor)} + d('medium', 'font_family') + d('bold', 'bold_font') + d('italic', 'italic_font') + d('bi', 'bold_italic_font') + return ans + + def main() -> None: cache: Dict[FaceKey, str] = {} for line in sys.stdin.buffer: cmd = json.loads(line) action = cmd.get('action', '') if action == 'list_monospaced_fonts': - send_to_kitten(create_family_groups()) + opts = create_default_opts() + send_to_kitten({'fonts': create_family_groups(), 'resolved_faces': resolved_faces(opts)}) elif action == 'read_variable_data': ans = [] for descriptor in cmd['descriptors']: diff --git a/kittens/choose_fonts/family_list.go b/kittens/choose_fonts/family_list.go index 64d559930..bfbb46750 100644 --- a/kittens/choose_fonts/family_list.go +++ b/kittens/choose_fonts/family_list.go @@ -142,6 +142,16 @@ func (self *FamilyList) Lines(num_rows int) []Line { return ans } +func (self *FamilyList) SelectFamily(family string) bool { + for i, f := range self.families { + if f == family { + self.current_idx = i + return true + } + } + return false +} + func (self *FamilyList) CurrentFamily() string { if self.current_idx >= 0 && self.current_idx < len(self.families) { return self.families[self.current_idx] diff --git a/kittens/choose_fonts/types.go b/kittens/choose_fonts/types.go index af8374011..e64efee57 100644 --- a/kittens/choose_fonts/types.go +++ b/kittens/choose_fonts/types.go @@ -69,6 +69,23 @@ type VariableData struct { Multi_axis_styles []MultiAxisStyle `json:"multi_axis_styles"` } +type ResolvedFace struct { + Family string `json:"family"` + Spec string `json:"spec"` +} + +type ResolvedFaces struct { + Font_family ResolvedFace `json:"font_family"` + Bold_font ResolvedFace `json:"bold_font"` + Italic_font ResolvedFace `json:"italic_font"` + Bold_italic_font ResolvedFace `json:"bold_italic_font"` +} + +type ListResult struct { + Fonts map[string][]ListedFont `json:"fonts"` + Resolved_faces ResolvedFaces `json:"resolved_faces"` +} + var variable_data_cache map[string]VariableData var variable_data_cache_mutex sync.Mutex diff --git a/kittens/choose_fonts/ui.go b/kittens/choose_fonts/ui.go index 69b51f461..5110edb48 100644 --- a/kittens/choose_fonts/ui.go +++ b/kittens/choose_fonts/ui.go @@ -35,7 +35,6 @@ type TextStyle struct { type handler struct { lp *loop.Loop - fonts map[string][]ListedFont state State err_mutex sync.Mutex err_in_worker_thread error @@ -46,9 +45,11 @@ type handler struct { graphics_manager graphics_manager // Listing - rl *readline.Readline - family_list FamilyList - variable_data_requested_for *utils.Set[string] + rl *readline.Readline + family_list FamilyList + variable_data_requested_for *utils.Set[string] + fonts map[string][]ListedFont + resolved_faces_from_kitty_conf ResolvedFaces } func (h *handler) set_worker_error(err error) { @@ -279,7 +280,10 @@ func (h *handler) initialize() { initialize_variable_data_cache() h.graphics_manager.initialize(h.lp) go func() { - h.set_worker_error(kitty_font_backend.query("list_monospaced_fonts", nil, &h.fonts)) + var r ListResult + h.set_worker_error(kitty_font_backend.query("list_monospaced_fonts", nil, &r)) + h.fonts = r.Fonts + h.resolved_faces_from_kitty_conf = r.Resolved_faces h.lp.WakeupMainThread() }() } @@ -351,7 +355,7 @@ func (h *handler) on_wakeup() (err error) { case SCANNING_FAMILIES: h.state = LISTING_FAMILIES h.family_list.UpdateFamilies(utils.StableSortWithKey(utils.Keys(h.fonts), strings.ToLower)) - case LISTING_FAMILIES: + h.family_list.SelectFamily(h.resolved_faces_from_kitty_conf.Font_family.Family) } return h.draw_screen() } diff --git a/kitty/fonts/common.py b/kitty/fonts/common.py index 76fbeca14..0c433f726 100644 --- a/kitty/fonts/common.py +++ b/kitty/fonts/common.py @@ -306,11 +306,7 @@ def axis_values_are_equal(defaults: Dict[str, float], a: Dict[str, float], b: Di return ad == bd -def get_named_style(face: Face) -> Optional[NamedStyle]: - axis_map = face.get_variation() - if axis_map is None: - return None - vd = get_variable_data_for_face(face) +def _get_named_style(axis_map: Dict[str, float], vd: VariableData) -> Optional[NamedStyle]: defaults = {ax['tag']: ax['default'] for ax in vd['axes']} for ns in vd['named_styles']: if axis_values_are_equal(defaults, ns['axis_values'], axis_map): @@ -318,5 +314,73 @@ def get_named_style(face: Face) -> Optional[NamedStyle]: return None +def get_named_style(face_or_descriptor: Union[Face, Descriptor]) -> Optional[NamedStyle]: + if isinstance(face_or_descriptor, dict): + d: Descriptor = face_or_descriptor + vd = get_variable_data_for_descriptor(d) + if d['descriptor_type'] == 'fontconfig': + ns = d.get('named_instance', -1) + if ns > -1 and ns < len(vd['named_styles']): + return vd['named_styles'][ns] + axis_map = {} + axes = vd['axes'] + for i, val in enumerate(d.get('axes', ())): + if i < len(axes): + axis_map[axes[i]['tag']] = val + else: + axis_map = d.get('axis_map', {}).copy() + else: + face: Face = face_or_descriptor + q = face.get_variation() + if q is None: + return None + axis_map = q + return _get_named_style(axis_map, vd) + + +def get_axis_map(face_or_descriptor: Union[Face, Descriptor]) -> Dict[str, float]: + base_axis_map = {} + axis_map: Dict[str, float] = {} + if isinstance(face_or_descriptor, dict): + d: Descriptor = face_or_descriptor + vd = get_variable_data_for_descriptor(d) + if d['descriptor_type'] == 'fontconfig': + ns = d.get('named_instance', -1) + if ns > -1 and ns < len(vd['named_styles']): + base_axis_map = vd['named_styles'][ns]['axis_values'].copy() + axis_map = {} + axes = vd['axes'] + for i, val in enumerate(d.get('axes', ())): + if i < len(axes): + axis_map[axes[i]['tag']] = val + + else: + axis_map = d.get('axis_map', {}).copy() + else: + face: Face = face_or_descriptor + q = face.get_variation() + if q is not None: + axis_map = q + base_axis_map.update(axis_map) + return base_axis_map + + +def spec_for_descriptor(descriptor: Descriptor) -> str: + from shlex import quote as q + if is_variable(descriptor): + vd = get_variable_data_for_descriptor(descriptor) + spec = f'family={q(descriptor["family"])}' + if vd['variations_postscript_name_prefix']: + spec += f' variable_name={q(vd["variations_postscript_name_prefix"])}' + ns = get_named_style(descriptor) + if ns is None: + for key, val in get_axis_map(descriptor).items(): + spec += f' {key}={val:g}' + else: + spec = f'{spec} style={q(ns["psname"])}' + return spec + return descriptor['postscript_name'] + + if __name__ == '__main__': develop()