Last active
November 26, 2025 21:49
-
-
Save frankrolf/a4fe38d51a05fe8a56743d1472779136 to your computer and use it in GitHub Desktop.
Filter font overview by Unicode category in Robofont 3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from mojo.UI import CurrentFontWindow | |
| from mojo.subscriber import ( | |
| Subscriber, listRegisteredSubscribers, | |
| registerRoboFontSubscriber, unregisterRoboFontSubscriber) | |
| import unicodedata | |
| import AppKit | |
| from vanilla import CheckBox, FloatingWindow, List | |
| category_to_codepoints = {} | |
| for code_point in range(0, 0xFFFF + 1): | |
| category = unicodedata.category(chr(code_point)) | |
| category_to_codepoints.setdefault(category, []).append(code_point) | |
| all_categories = { | |
| 'Cc': 'Other, Control', | |
| 'Cf': 'Other, Format', | |
| 'Cn': 'Other, Not Assigned', | |
| 'Co': 'Other, Private Use', | |
| 'Cs': 'Other, Surrogate', | |
| 'LC': 'Letter, Cased', | |
| 'Ll': 'Letter, Lowercase', | |
| 'Lm': 'Letter, Modifier', | |
| 'Lo': 'Letter, Other', | |
| 'Lt': 'Letter, Titlecase', | |
| 'Lu': 'Letter, Uppercase', | |
| 'Mc': 'Mark, Spacing Combining', | |
| 'Me': 'Mark, Enclosing', | |
| 'Mn': 'Mark, Nonspacing', | |
| 'Nd': 'Number, Decimal Digit', | |
| 'Nl': 'Number, Letter', | |
| 'No': 'Number, Other', | |
| 'Pc': 'Punctuation, Connector', | |
| 'Pd': 'Punctuation, Dash', | |
| 'Pe': 'Punctuation, Close', | |
| 'Pf': 'Punctuation, Final quote', | |
| 'Pi': 'Punctuation, Initial quote', | |
| 'Po': 'Punctuation, Other', | |
| 'Ps': 'Punctuation, Open', | |
| 'Sc': 'Symbol, Currency', | |
| 'Sk': 'Symbol, Modifier', | |
| 'Sm': 'Symbol, Math', | |
| 'So': 'Symbol, Other', | |
| 'Zl': 'Separator, Line', | |
| 'Zp': 'Separator, Paragraph', | |
| 'Zs': 'Separator, Space', | |
| } | |
| class CatList(Subscriber): | |
| def build(self): | |
| self.select_suffix = False | |
| self.selected_cats = [] | |
| # initialize window and list with dummy values, | |
| # which are replaced on update() | |
| self.w = FloatingWindow((350, 100), title='uni😸😸😸') | |
| self.w.myList = List( | |
| (0, 0, -0, 0), | |
| [], | |
| columnDescriptions=[ | |
| {'title': 'cat name'}, | |
| {'title': 'description'} | |
| ], | |
| selectionCallback=self.sel_callback, | |
| allowsMultipleSelection=True | |
| ) | |
| self.update(CurrentFont()) | |
| self.w.bind('close', self.close_callback) | |
| self.w.myCheckBox = CheckBox( | |
| (10, -23, -10, 20), | |
| "Show Suffixed", | |
| callback=self.check_callback, | |
| value=False) | |
| # xxx trying to get the window to toggle quicker, however | |
| # this does not do anything | |
| # self.w.getNSWindow().setAnimationBehavior_(1) | |
| self.w.open() | |
| # select the first item in the list and filter the glyph set | |
| # according to that selection | |
| self.w.myList.setSelection([0]) | |
| self.sel_callback(self.w.myList) | |
| def update(self, font): | |
| self.f = font | |
| self.all_glyphs = [g for g in font] + list(font.templateGlyphs) | |
| self.cat_list = [] # use internally | |
| self.display_cat_list = [] # use in window | |
| cat_names = self.collect_cat_names() | |
| for cat_name in cat_names: | |
| description = all_categories.get(cat_name, '') | |
| self.cat_list.append(cat_name) | |
| self.display_cat_list.append({ | |
| 'cat name': cat_name, | |
| 'description': description, | |
| }) | |
| self.list_height = 24 + 18 * (len(self.display_cat_list) + 1) | |
| self.window_height = self.list_height + 24 | |
| self.w.resize(350, self.window_height) | |
| self.w.myList.set(self.display_cat_list) | |
| self.w.myList.resize(-0, self.list_height) | |
| def collect_cat_names(self): | |
| ''' | |
| Find Unicode categories found in the font at hand | |
| ''' | |
| cp_list = [g.unicodes for g in self.all_glyphs if g.unicodes] | |
| all_codepoints = sum(cp_list, ()) | |
| cat_names = [] | |
| for cat_name, cpoint_list in category_to_codepoints.items(): | |
| if set(all_codepoints) & set(cpoint_list): | |
| cat_names.append(cat_name) | |
| return sorted(cat_names) | |
| def sel_callback(self, sender): | |
| cat_selections = sender.getSelection() | |
| cat_names = [] | |
| for sel_index in cat_selections: | |
| cat_name = self.cat_list[sel_index] | |
| cat_names.append(cat_name) | |
| self.selected_cats = cat_names | |
| self.select_cats(cat_names) | |
| def close_callback(self, sender): | |
| print('unregistering') | |
| if CurrentFontWindow(): | |
| CurrentFontWindow().getGlyphCollection().setQuery(None) | |
| # self.destroy() | |
| for s in listRegisteredSubscribers(subscriberClassName='CatList'): | |
| unregisterRoboFontSubscriber(s) | |
| def check_callback(self, sender): | |
| self.select_suffix = sender.get() | |
| if self.selected_cats: | |
| self.select_cats(self.selected_cats) | |
| else: | |
| self.select_cats(['Ll']) | |
| def select_cats(self, cat_names): | |
| g_name_list = [] | |
| for cat_name in cat_names: | |
| g_name_list.extend( | |
| g.name for g in self.all_glyphs if | |
| set(g.unicodes) & set(category_to_codepoints[cat_name])) | |
| if self.select_suffix: | |
| sel_list = [ | |
| g.name for g in self.all_glyphs if | |
| g.name.split('.')[0] in g_name_list] | |
| else: | |
| sel_list = g_name_list | |
| query = 'Name in {"%s"}' % '", "'.join(sel_list) | |
| query = AppKit.NSPredicate.predicateWithFormat_(query) | |
| CurrentFontWindow().getGlyphCollection().setQuery(query) | |
| def fontDocumentDidBecomeCurrent(self, sender): | |
| self.update(sender.get('font')) | |
| def roboFontDidSwitchCurrentGlyph(self, sender): | |
| glyph = sender.get('glyph') | |
| if glyph: | |
| self.update(glyph.font) | |
| def destroy(self): | |
| # unregisterRoboFontSubscriber(self) | |
| print('destruction') | |
| if __name__ == '__main__': | |
| if CurrentFont() is not None: | |
| registerRoboFontSubscriber(CatList) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment