├── DesignspaceEditor2.roboFontExt ├── lib │ ├── designspaceEditor │ │ ├── parsers │ │ │ ├── __init__.py │ │ │ ├── testParser.py │ │ │ ├── glyphNameParser.py │ │ │ ├── mapParser.py │ │ │ ├── parserTools.py │ │ │ ├── rulesParser.py │ │ │ ├── variableFontsParser.py │ │ │ └── labelsParser.py │ │ ├── __init__.py │ │ ├── designspaceLexer.py │ │ ├── designspaceSubscribers.py │ │ ├── tools.py │ │ └── locationPreview.py │ ├── newDesignspaceEditor.py │ ├── openDesignspaceEditor.py │ ├── main.py │ └── install.py ├── html │ ├── previewWindow.jpg │ ├── sparse_master.png │ ├── previewWindow_options2.png │ ├── questionmark.circle2x.png │ ├── square.and.arrow.down2x.png │ ├── chart.bar.doc.horizontal2x.png │ ├── toolbar_500_500_icon_axes.png │ ├── toolbar_500_500_icon_notes.png │ ├── toolbar_500_500_icon_rules.png │ ├── toolbar_500_500_icon_save.png │ ├── toolbar_500_500_icon_problems.png │ ├── toolbar_500_500_icon_settings.png │ ├── toolbar_500_500_icon_sources.png │ ├── screenshot_mutatorsans_axestab.jpg │ ├── screenshot_mutatorsans_axestab.png │ ├── screenshot_mutatorsans_rulestab.jpg │ ├── toolbar_500_500_icon_instances.png │ ├── screenshot_mutatorsans_problemstab.jpg │ ├── screenshot_mutatorsans_sourcestab.jpg │ ├── toolbar_500_500_icon_notifications.png │ ├── screenshot_mutatorsans_instancestab.png │ ├── screenshot_mutatorsans_previewwindow1.jpg │ ├── toolbar_500_500_icon_location_labels.png │ ├── toolbar_500_500_icon_variable_fonts.png │ ├── screenshot_mutatorsans_variablefontstab.jpg │ ├── toolbar_25_25_info.circle.fill.svg │ └── styles.css ├── resources │ ├── toolbar_30_30_atom.pdf │ ├── toolbar_30_30_icon_axes.pdf │ ├── toolbar_30_30_icon_notes.pdf │ ├── toolbar_30_30_icon_rules.pdf │ ├── toolbar_30_30_icon_problems.pdf │ ├── toolbar_30_30_icon_sources.pdf │ ├── toolbar_30_30_icon_instances.pdf │ ├── toolbar_30_30_wand.and.stars.pdf │ ├── toolbar_30_30_icon_notifications.pdf │ ├── toolbar_30_30_icon_variablefonts.pdf │ ├── toolbar_30_30_mappin.and.ellipse.pdf │ ├── toolbar_30_30_icon_location_labels.pdf │ ├── toolbar_30_30_icon_variable_fonts.pdf │ ├── toolbar_30_30_questionmark.circle.pdf │ ├── toolbar_30_30_square.and.arrow.down.pdf │ ├── toolbar_30_30_chart.bar.doc.horizontal.pdf │ ├── toolbar_30_30_smallcircle.filled.circle.pdf │ ├── toolbar_30_30_smallcircle.filled.circle.fill.pdf │ └── toolbar_30_30_arrow.right.filled.filter.arrow.right_arrow.right.filled.filter.arrow.right.pdf └── info.plist ├── icons ├── toolbar_30_30_atom.pdf ├── toolbar_1000_1000_icon_axes.png ├── toolbar_30_30_info.circle.pdf ├── toolbar_500_500_info.circle.png ├── toolbar_1000_1000_icon_notes.png ├── toolbar_1000_1000_icon_rules.png ├── toolbar_1000_1000_info.circle.pdf ├── toolbar_30_30_wand.and.stars.pdf ├── all_icons_1000_1000_dse2_preview.pdf ├── toolbar_1000_1000_icon_instances.png ├── toolbar_1000_1000_icon_problems.png ├── toolbar_1000_1000_icon_sources.png ├── toolbar_30_30_info.bubble.fill.pdf ├── toolbar_30_30_info.circle.fill.pdf ├── toolbar_500_500_info.bubble.fill.png ├── toolbar_500_500_info.circle.fill.png ├── toolbar_1000_1000_info.bubble.fill.pdf ├── toolbar_1000_1000_info.circle.fill.pdf ├── toolbar_30_30_questionmark.circle.pdf ├── toolbar_1000_1000_icon_notifications.png ├── toolbar_1000_1000_icon_variable_fonts.png ├── toolbar_1000_1000_questionmark.circle.pdf ├── toolbar_30_30_square.and.arrow.down.pdf ├── toolbar_500_500_questionmark.circle.png ├── toolbar_500_500_square.and.arrow.down.png ├── toolbar_1000_1000_icon_location_labels.png ├── toolbar_1000_1000_square.and.arrow.down.pdf ├── toolbar_1000_1000_icon_variable_notifications.png ├── toolbar_30_30_arrow.right.filled.filter.arrow.right_arrow.right.filled.filter.arrow.right.pdf ├── export_SF_Symbol.py ├── square.and.arrow.down_square.and.arrow.down.svg ├── info.circle.fill_info.circle.fill.svg ├── info.bubble.fill_info.bubble.fill.svg ├── info.circle_info.circle.svg ├── questionmark.circle_questionmark.circle.svg ├── roundedRect.py ├── wand.and.stars_wand.and.stars.svg ├── makeIcons_dse1.py └── makeIcons_dse2.py ├── assets ├── designSpaceFileIcon.png ├── toolbar_100_100_icon_axes.png ├── toolbar_100_100_icon_notes.png ├── toolbar_100_100_icon_rules.png ├── toolbar_100_100_icon_save.png ├── toolbar_500_500_icon_axes.png ├── toolbar_500_500_icon_notes.png ├── toolbar_500_500_icon_rules.png ├── toolbar_100_100_icon_sources.png ├── toolbar_100_100_info.circle.png ├── toolbar_500_500_icon_sources.png ├── toolbar_100_100_icon_instances.png ├── toolbar_100_100_icon_problems.png ├── toolbar_100_100_icon_settings.png ├── toolbar_500_500_icon_instances.png ├── toolbar_500_500_icon_problems.png ├── toolbar_100_100_info.bubble.fill.png ├── toolbar_100_100_info.circle.fill.png ├── toolbar_100_100_icon_location_labels.png ├── toolbar_100_100_icon_notifications.png ├── toolbar_100_100_icon_variable_fonts.png ├── toolbar_100_100_questionmark.circle.png ├── toolbar_500_500_icon_location_labels.png ├── toolbar_500_500_icon_notifications.png ├── toolbar_500_500_icon_variable_fonts.png ├── toolbar_100_100_square.and.arrow.down.png ├── toolbar_100_100_icon_variable_notifications.png ├── toolBar_100_100_icon_ chart.bar.doc.horizontal.png └── info.circle.fill.svg ├── instancestab_mathmodel_dialog.png ├── .gitignore ├── Scripting examples ├── rf_change_previewLocation_of_all_open_designspaces.py ├── rf_getBasicValues.py ├── db_simpleGlyphPreview.py ├── rf_close_all_sources_from_designspace.py ├── db_currentDesignspace.py ├── rf_makeOneKerning.py ├── rf_open_all_sources_from_designspace.py ├── db_werkman.py ├── db_sliders.py ├── db_currentDesignspace_grid.py └── rf_previewFont_to_spacecenter.py ├── LICENSE ├── .github └── workflows │ └── static.yml ├── README.md └── DSEezui.py /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/parsers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/__init__.py: -------------------------------------------------------------------------------- 1 | extensionIdentifier = "com.letterror.designspaceEditor" -------------------------------------------------------------------------------- /icons/toolbar_30_30_atom.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_30_30_atom.pdf -------------------------------------------------------------------------------- /assets/designSpaceFileIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/designSpaceFileIcon.png -------------------------------------------------------------------------------- /instancestab_mathmodel_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/instancestab_mathmodel_dialog.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_icon_axes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_icon_axes.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_icon_notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_icon_notes.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_icon_rules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_icon_rules.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_icon_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_icon_save.png -------------------------------------------------------------------------------- /assets/toolbar_500_500_icon_axes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_500_500_icon_axes.png -------------------------------------------------------------------------------- /assets/toolbar_500_500_icon_notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_500_500_icon_notes.png -------------------------------------------------------------------------------- /assets/toolbar_500_500_icon_rules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_500_500_icon_rules.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_icon_axes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_icon_axes.png -------------------------------------------------------------------------------- /icons/toolbar_30_30_info.circle.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_30_30_info.circle.pdf -------------------------------------------------------------------------------- /icons/toolbar_500_500_info.circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_500_500_info.circle.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_icon_sources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_icon_sources.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_info.circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_info.circle.png -------------------------------------------------------------------------------- /assets/toolbar_500_500_icon_sources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_500_500_icon_sources.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_icon_notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_icon_notes.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_icon_rules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_icon_rules.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_info.circle.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_info.circle.pdf -------------------------------------------------------------------------------- /icons/toolbar_30_30_wand.and.stars.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_30_30_wand.and.stars.pdf -------------------------------------------------------------------------------- /assets/toolbar_100_100_icon_instances.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_icon_instances.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_icon_problems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_icon_problems.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_icon_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_icon_settings.png -------------------------------------------------------------------------------- /assets/toolbar_500_500_icon_instances.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_500_500_icon_instances.png -------------------------------------------------------------------------------- /assets/toolbar_500_500_icon_problems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_500_500_icon_problems.png -------------------------------------------------------------------------------- /icons/all_icons_1000_1000_dse2_preview.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/all_icons_1000_1000_dse2_preview.pdf -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_icon_instances.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_icon_instances.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_icon_problems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_icon_problems.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_icon_sources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_icon_sources.png -------------------------------------------------------------------------------- /icons/toolbar_30_30_info.bubble.fill.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_30_30_info.bubble.fill.pdf -------------------------------------------------------------------------------- /icons/toolbar_30_30_info.circle.fill.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_30_30_info.circle.fill.pdf -------------------------------------------------------------------------------- /icons/toolbar_500_500_info.bubble.fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_500_500_info.bubble.fill.png -------------------------------------------------------------------------------- /icons/toolbar_500_500_info.circle.fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_500_500_info.circle.fill.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/newDesignspaceEditor.py: -------------------------------------------------------------------------------- 1 | from designspaceEditor.ui import DesignspaceEditorController 2 | 3 | DesignspaceEditorController() 4 | -------------------------------------------------------------------------------- /assets/toolbar_100_100_info.bubble.fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_info.bubble.fill.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_info.circle.fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_info.circle.fill.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_info.bubble.fill.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_info.bubble.fill.pdf -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_info.circle.fill.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_info.circle.fill.pdf -------------------------------------------------------------------------------- /icons/toolbar_30_30_questionmark.circle.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_30_30_questionmark.circle.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test.designspace 2 | build.py 3 | __pycache__ 4 | *.pyc 5 | DesignSpaceEditor.roboFontExt/lib/.pytest_cache 6 | issues 7 | .DS_Store 8 | .pytest_cache 9 | -------------------------------------------------------------------------------- /assets/toolbar_100_100_icon_location_labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_icon_location_labels.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_icon_notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_icon_notifications.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_icon_variable_fonts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_icon_variable_fonts.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_questionmark.circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_questionmark.circle.png -------------------------------------------------------------------------------- /assets/toolbar_500_500_icon_location_labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_500_500_icon_location_labels.png -------------------------------------------------------------------------------- /assets/toolbar_500_500_icon_notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_500_500_icon_notifications.png -------------------------------------------------------------------------------- /assets/toolbar_500_500_icon_variable_fonts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_500_500_icon_variable_fonts.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_icon_notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_icon_notifications.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_icon_variable_fonts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_icon_variable_fonts.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_questionmark.circle.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_questionmark.circle.pdf -------------------------------------------------------------------------------- /icons/toolbar_30_30_square.and.arrow.down.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_30_30_square.and.arrow.down.pdf -------------------------------------------------------------------------------- /icons/toolbar_500_500_questionmark.circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_500_500_questionmark.circle.png -------------------------------------------------------------------------------- /icons/toolbar_500_500_square.and.arrow.down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_500_500_square.and.arrow.down.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_square.and.arrow.down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_square.and.arrow.down.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_icon_location_labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_icon_location_labels.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_square.and.arrow.down.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_square.and.arrow.down.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/previewWindow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/previewWindow.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/sparse_master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/sparse_master.png -------------------------------------------------------------------------------- /assets/toolbar_100_100_icon_variable_notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolbar_100_100_icon_variable_notifications.png -------------------------------------------------------------------------------- /Scripting examples/rf_change_previewLocation_of_all_open_designspaces.py: -------------------------------------------------------------------------------- 1 | for ds in AllDesignspaces(): 2 | loc = ds.randomLocation() 3 | print(loc) 4 | ds.setPreviewLocation(loc) -------------------------------------------------------------------------------- /assets/toolBar_100_100_icon_ chart.bar.doc.horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/assets/toolBar_100_100_icon_ chart.bar.doc.horizontal.png -------------------------------------------------------------------------------- /icons/toolbar_1000_1000_icon_variable_notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_1000_1000_icon_variable_notifications.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/previewWindow_options2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/previewWindow_options2.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/questionmark.circle2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/questionmark.circle2x.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/square.and.arrow.down2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/square.and.arrow.down2x.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_atom.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_atom.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/chart.bar.doc.horizontal2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/chart.bar.doc.horizontal2x.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_axes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_axes.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_notes.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_rules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_rules.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_save.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_problems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_problems.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_settings.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_sources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_sources.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_axes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_axes.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_notes.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_rules.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_rules.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_axestab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_axestab.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_axestab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_axestab.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_rulestab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_rulestab.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_instances.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_instances.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_problems.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_problems.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_sources.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_sources.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_problemstab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_problemstab.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_sourcestab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_sourcestab.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_notifications.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_instances.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_instances.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_wand.and.stars.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_wand.and.stars.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_instancestab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_instancestab.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_previewwindow1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_previewwindow1.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_location_labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_location_labels.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_variable_fonts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_variable_fonts.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_notifications.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_notifications.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_variablefonts.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_variablefonts.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_mappin.and.ellipse.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_mappin.and.ellipse.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_variablefontstab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_variablefontstab.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_location_labels.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_location_labels.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_variable_fonts.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_icon_variable_fonts.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_questionmark.circle.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_questionmark.circle.pdf -------------------------------------------------------------------------------- /Scripting examples/rf_getBasicValues.py: -------------------------------------------------------------------------------- 1 | 2 | d = CurrentDesignspace() 3 | 4 | # go through all axes and see what we have 5 | for a in d.axes: 6 | print(a) 7 | 8 | # ask for discrete axes specifically 9 | print(d.discreteAxes()) -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_square.and.arrow.down.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_square.and.arrow.down.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_chart.bar.doc.horizontal.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_chart.bar.doc.horizontal.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_smallcircle.filled.circle.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_smallcircle.filled.circle.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_smallcircle.filled.circle.fill.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_smallcircle.filled.circle.fill.pdf -------------------------------------------------------------------------------- /icons/toolbar_30_30_arrow.right.filled.filter.arrow.right_arrow.right.filled.filter.arrow.right.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/icons/toolbar_30_30_arrow.right.filled.filter.arrow.right_arrow.right.filled.filter.arrow.right.pdf -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_arrow.right.filled.filter.arrow.right_arrow.right.filled.filter.arrow.right.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/HEAD/DesignspaceEditor2.roboFontExt/resources/toolbar_30_30_arrow.right.filled.filter.arrow.right_arrow.right.filled.filter.arrow.right.pdf -------------------------------------------------------------------------------- /Scripting examples/db_simpleGlyphPreview.py: -------------------------------------------------------------------------------- 1 | #drawbot 2 | d = CurrentDesignspace() 3 | if d is not None: 4 | previewLoc = d.randomLocation() 5 | d.setPreviewLocation(previewLoc) 6 | 7 | loc = dict(weight=800, width=800) 8 | r = d.makeOneGlyph("R", loc) 9 | 10 | translate(100, 100) 11 | scale(0.8) 12 | drawGlyph(r) -------------------------------------------------------------------------------- /Scripting examples/rf_close_all_sources_from_designspace.py: -------------------------------------------------------------------------------- 1 | 2 | # save and close all open fonts that belong 3 | # to the current designspace. 4 | 5 | ds = CurrentDesignspace() 6 | 7 | dsFontPaths = [f.path for f, l in ds.getFonts()] 8 | 9 | for f in AllFonts(): 10 | if f.path in dsFontPaths: 11 | f.save() 12 | f.close() -------------------------------------------------------------------------------- /Scripting examples/db_currentDesignspace.py: -------------------------------------------------------------------------------- 1 | #drawbot 2 | size(1000, 600) 3 | d = CurrentDesignspace() 4 | fill(None) 5 | stroke(0) 6 | strokeWidth(.5) 7 | with savedState(): 8 | translate(100, 100) 9 | scale(0.5) 10 | for i in range(20): 11 | loc = d.randomLocation() 12 | g = d.makeOneGlyph("A", location=loc) 13 | drawGlyph(g) -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/openDesignspaceEditor.py: -------------------------------------------------------------------------------- 1 | from mojo.UI import GetFile 2 | from designspaceEditor.ui import DesignspaceEditorController 3 | 4 | 5 | paths = GetFile( 6 | message="Open a designspace document:", 7 | allowsMultipleSelection=True, 8 | fileTypes=['designspace'], 9 | ) 10 | 11 | if paths: 12 | for path in paths: 13 | DesignspaceEditorController(path) 14 | -------------------------------------------------------------------------------- /Scripting examples/rf_makeOneKerning.py: -------------------------------------------------------------------------------- 1 | d = CurrentDesignspace() 2 | 3 | #loc = d.newDefaultLocation() 4 | loc = {'width': 140, 'weight': 400} 5 | print(loc) 6 | 7 | pairs = [('T', 'A')] 8 | #pairs = None 9 | 10 | k = d.makeOneKerning(loc, pairs) 11 | k.round() 12 | print(k.items()) 13 | 14 | info = d.makeOneInfo(loc) 15 | info.round() 16 | print("info.ascender", info.ascender, "info.descender", info.descender) -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/main.py: -------------------------------------------------------------------------------- 1 | 2 | try: 3 | import install 4 | except ImportError: 5 | # fails cause of the old version is alreayd installed 6 | from mojo.UI import dontShowAgainMessage 7 | 8 | dontShowAgainMessage( 9 | messageText="Designspace editor requires a RoboFont restart.", 10 | informativeText='', 11 | dontShowAgainKey="com.letterror.designspaceEditor" 12 | ) -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/parsers/testParser.py: -------------------------------------------------------------------------------- 1 | 2 | def run(): 3 | import os 4 | import sys 5 | import glob 6 | 7 | import pytest 8 | 9 | root = os.path.dirname(os.path.abspath(__file__)) 10 | if root not in sys.path: 11 | sys.path.append(root) 12 | args = [ 13 | "--doctest-modules", 14 | ] 15 | args += [file for file in glob.glob(f"{root}/*.py")] 16 | return not bool(pytest.main(args)) 17 | 18 | 19 | if __name__ == '__main__': 20 | run() 21 | -------------------------------------------------------------------------------- /Scripting examples/rf_open_all_sources_from_designspace.py: -------------------------------------------------------------------------------- 1 | # open all the ufos used in the current designspace 2 | # See how DSE2 keeps track of which fonts are open 3 | # and that edits in the fonts are reflected in the previews. 4 | ds = CurrentDesignspace() 5 | done = [] 6 | 7 | discreteLocation = {'slab': 0} 8 | discreteLocation = None 9 | 10 | for font, loc in ds.getFonts(): 11 | if discreteLocation is not None: 12 | if loc | discreteLocation != loc: 13 | continue 14 | if font.path not in done: 15 | font.asFontParts().openInterface() 16 | # a ufo can appear multiple times in the designspace 17 | # and we don't want to open duplicate font windows 18 | # so, keep track of what fonts we've opened. 19 | done.append(font.path) 20 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | addToMenu 6 | 7 | 8 | developer 9 | Letterror 10 | developerURL 11 | https://letterror.com 12 | html 13 | 14 | launchAtStartUp 15 | 1 16 | mainScript 17 | main.py 18 | name 19 | Designspace Editor 2 20 | requiresVersionMajor 21 | 4 22 | requiresVersionMinor 23 | 5b 24 | timeStamp 25 | 1653936779.9141068 26 | version 27 | 2.8.2 28 | 29 | 30 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/parsers/glyphNameParser.py: -------------------------------------------------------------------------------- 1 | """ 2 | # glyphname text spec 3 | 4 | ... 5 | """ 6 | 7 | 8 | def parseGlyphNames(text): 9 | return [glyphName.strip() for glyphName in text.split()] 10 | 11 | 12 | def dumpGlyphNames(glyphNames): 13 | return " ".join(glyphNames) 14 | 15 | 16 | # tests 17 | 18 | def test_parseGlyphNames(): 19 | text = "a b c agrave \tb.alt ccedilla" 20 | expected = ["a", "b", "c", "agrave", "b.alt", "ccedilla"] 21 | result = parseGlyphNames(text) 22 | assert expected == result 23 | 24 | 25 | def test_dumpGlyphNames(): 26 | glyphNames = ["a", "b", "c", "agrave", "b.alt", "ccedilla"] 27 | expected = "a b c agrave b.alt ccedilla" 28 | result = dumpGlyphNames(glyphNames) 29 | assert expected == result 30 | 31 | 32 | if __name__ == '__main__': 33 | import pytest 34 | pytest.main([__file__]) 35 | -------------------------------------------------------------------------------- /Scripting examples/db_werkman.py: -------------------------------------------------------------------------------- 1 | #drawbot 2 | 3 | # choose a random glyph 4 | # and a random designspace location 5 | # and be surprised! 6 | 7 | size(1000, 600) 8 | d = CurrentDesignspace() 9 | fill(.5) 10 | stroke(None) 11 | blendMode("multiply") 12 | names = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "arrowdown", "arrowleft", "arrowright", "arrowup"] 13 | 14 | clr = [(1,0,0), (0,0,1), (1,.9,0)] 15 | 16 | with savedState(): 17 | translate(0, 100) 18 | for c in clr: 19 | with savedState(): 20 | translate(.5 * width(), 0) 21 | scale(0.5) 22 | fill(*c) 23 | loc = d.randomLocation() 24 | glyphName = choice(names) 25 | g = d.makeOneGlyph(glyphName, decomposeComponents=True, location=loc) 26 | if g is not None: 27 | translate(-.5*g.width,0) 28 | bp = BezierPath() 29 | g.draw(bp) 30 | drawPath(bp) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Erik van Blokland 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Scripting examples/db_sliders.py: -------------------------------------------------------------------------------- 1 | # drawbot 2 | ds = CurrentDesignspace() 3 | 4 | settings = [ 5 | dict(name="glyphName", ui="EditText", args=dict(text="A")) 6 | ] 7 | 8 | for axis in ds.getOrderedContinuousAxes(): 9 | aD_minimum, aD_default, aD_maximum = ds.getAxisExtremes(axis) 10 | settings.append(dict( 11 | name=axis.name, 12 | ui="Slider", 13 | args=dict( 14 | value=aD_default, 15 | minValue=aD_minimum, 16 | maxValue=aD_maximum 17 | ) 18 | ) 19 | ) 20 | for axis in ds.getOrderedDiscreteAxes(): 21 | print(axis.name) 22 | settings.append(dict( 23 | name=axis.name, 24 | ui="PopUpButton", 25 | args=dict( 26 | items=[str(v) for v in axis.values], 27 | ) 28 | ) 29 | ) 30 | location = dict() 31 | Variable(settings, location) 32 | 33 | for axis in ds.getOrderedDiscreteAxes(): 34 | if axis.name in location: 35 | location[axis.name] = axis.values[location[axis.name]] 36 | 37 | glyphName = location.pop("glyphName") 38 | if glyphName: 39 | result = ds.makeOneGlyph(glyphName=glyphName, location=location) 40 | drawGlyph(result) -------------------------------------------------------------------------------- /icons/export_SF_Symbol.py: -------------------------------------------------------------------------------- 1 | #for n in installedFonts(): 2 | # if "SF" in n: 3 | # print(n) 4 | 5 | 6 | # width and height of the square output document 7 | dim = 275 8 | 9 | names = [ 10 | ("􀅴", "SFPro-Regular", "info.circle"), 11 | ("􀈄", "SFPro-Regular", "square.and.arrow.down"), 12 | ("􀁜", "SFPro-Regular", "questionmark.circle"), 13 | ("􀅵", "SFPro-Regular", "info.circle.fill"), 14 | ("􁌵", "SFPro-Regular", "info.bubble.fill"), 15 | ("􀜍", "SFPro-Regular", "wand.and.stars"), 16 | ("􂧿", "SFPro-Regular", "arrow.right.filled.filter.arrow.right"), 17 | ("􀬚", "SFPro-Regular", "atom"), 18 | ] 19 | 20 | for sym, fontName, symbolName in names: 21 | newDrawing() 22 | newPage(dim,dim) 23 | margin = 54/325 * width() 24 | bp = BezierPath() 25 | fs = FormattedString() 26 | fs.font(fontName) 27 | fs.fontSize(250/275 * width()) 28 | fs.append(sym) 29 | bp.text(fs) 30 | xMin, yMin, xMax, yMax = bp.bounds() 31 | w = xMax-xMin 32 | h = yMax-yMin 33 | s = w/(width()-2*margin) 34 | with savedState(): 35 | fill(0) 36 | translate(width()*.5, height()*.5) 37 | translate(-xMin, -yMin) 38 | translate(-.5*w, -.5*h) 39 | drawPath(bp) 40 | saveImage(f"{symbolName}_{symbolName}.svg") 41 | 42 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/designspaceLexer.py: -------------------------------------------------------------------------------- 1 | from pygments.lexer import RegexLexer, include, bygroups 2 | from pygments.lexers.special import TextLexer 3 | from pygments.token import * 4 | 5 | 6 | class DesignspaceLexer(RegexLexer): 7 | name = "Designspace" 8 | aliases = ['designspace'] 9 | filenames = ['*.designspace'] 10 | 11 | tokens = { 12 | 'root': [ 13 | (r'\n', Whitespace), 14 | (r'#.*$', Comment), 15 | (r'[\'|\"].*[\'|\"]', String), 16 | (r'(\?)\s+((?:[a-zA-Z0-9\-]+))\s+', bygroups(Name.Builtin, Name.Variable)), 17 | (r'\((elidable|olderSibling)\)', Name.Variable), 18 | (r'\[([0-9\.]+)\]', Name.Variable), 19 | (r'\>|\?|\-|\*', Name.Builtin), 20 | include('numbers'), 21 | (r'^[^\s].*$', Keyword.Namespace), # Name.Class 22 | (r'(weight|width|italic|optical|slant)\b', Keyword), 23 | (r'[\w\.\*\+\-\:\^\|\~]+', Text), 24 | ], 25 | 'numbers': [ 26 | (r'(\d+\.\d*|\d*\.\d+)([eE][+-]?[0-9]+)?j?', Number.Float), 27 | (r'\d+[eE][+-]?[0-9]+j?', Number.Float), 28 | (r'0[0-7]+j?', Number.Oct), 29 | (r'0[xX][a-fA-F0-9]+', Number.Hex), 30 | (r'\d+L', Number.Integer.Long), 31 | (r'\d+j?', Number.Integer) 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["master"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Single deploy job since we're just deploying 26 | deploy: 27 | environment: 28 | name: github-pages 29 | url: ${{ steps.deployment.outputs.page_url }} 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v3 34 | - name: Setup Pages 35 | uses: actions/configure-pages@v3 36 | - name: Upload artifact 37 | uses: actions/upload-pages-artifact@v3 38 | with: 39 | # Upload entire repository 40 | path: './DesignspaceEditor2.roboFontExt/html' 41 | - name: Deploy to GitHub Pages 42 | id: deployment 43 | uses: actions/deploy-pages@v4 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ![DSE2 location labels icon](assets/toolbar_500_500_icon_location_labels.png) 3 | 4 | DesignSpaceEditor2 5 | ================== 6 | 7 | Create and edit designspaces with Robofont 4.5+, with support for scripting. 8 | 9 | ## Documentation 10 | 11 | [DesignspaceEditor2 documentation](https://letterror.github.io/designSpaceRoboFontExtension/) (or click the (?) symbol in the toolbar.) 12 | 13 | ## Goals 14 | 15 | * Open, edit and save existing [designspaces format 5](https://fonttools.readthedocs.io/en/latest/designspaceLib/index.html) files. 16 | * You can create a new designspace and fill it with axes, sources and instances. 17 | * You can edit all sorts of axis labels, location labels and localisations. 18 | * You can quickly open the UFOs listed as sources. 19 | * You can generate UFOs for defined instances. 20 | * You can preview and add instances. 21 | * You can check designspaces and find structural problems and compatibility issues. 22 | * DSE2 collects and sends designspace-related notifications about changes to the designspace, and also changes in the source font data. Other tools in RF can request interpolated glyphs, kerning and font info at specific locations, using MutatorMath as well as Varlib interpolation models. 23 | * You can experiment with *extrapolation* and *anisotropic interpolations* and see previews for such exotic things. *Even if these are not supported as Variable fonts, you can export them as UFO.* 24 | * You can write scripts that use the current designspace. 25 | 26 | 27 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_25_25_info.circle.fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /icons/square.and.arrow.down_square.and.arrow.down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/parsers/mapParser.py: -------------------------------------------------------------------------------- 1 | """ 2 | # map text spac 3 | 4 | > 5 | > 6 | > 7 | ... 8 | """ 9 | 10 | import pytest 11 | import re 12 | 13 | from .parserTools import getLines, stringToNumber 14 | 15 | 16 | mapRE = re.compile(r"^(-?[0-9\.]+)\s*\>\s*(-?[0-9\.]+)\s*$") 17 | 18 | 19 | def parseMap(text): 20 | mapData = list() 21 | for line in getLines(text): 22 | for result in re.finditer(mapRE, line): 23 | inputValue, outputValue = result.groups() 24 | mapData.append((float(inputValue), float(outputValue))) 25 | return mapData 26 | 27 | 28 | def dumpMap(mapData): 29 | return "\n".join([f"{stringToNumber(inputValue)} > {stringToNumber(outputValue)}" for inputValue, outputValue in mapData]) 30 | 31 | 32 | # tests 33 | 34 | @pytest.mark.parametrize("text,expected", [ 35 | ("", []), 36 | ("10 > 20", [(10, 20)]), 37 | ("10 > 20\n15 > 25", [(10, 20), (15, 25)]), 38 | ("10>20\n15> 25", [(10, 20), (15, 25)]), 39 | ("-10>10", [(-10, 10)]), 40 | ("-10>-10", [(-10, -10)]), 41 | ("10>-10", [(10, -10)]), 42 | ]) 43 | def test_parseMap(text, expected): 44 | result = parseMap(text) 45 | assert expected == result 46 | 47 | 48 | @pytest.mark.parametrize("amap,expected", [ 49 | ([], ""), 50 | ([(10, 20)], "10 > 20"), 51 | ([(10, 20), (15, 25)], "10 > 20\n15 > 25"), 52 | ]) 53 | def test_dumpMap(amap, expected): 54 | result = dumpMap(amap) 55 | assert expected == result 56 | 57 | 58 | if __name__ == '__main__': 59 | pytest.main([__file__]) 60 | -------------------------------------------------------------------------------- /icons/info.circle.fill_info.circle.fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /icons/info.bubble.fill_info.bubble.fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Scripting examples/db_currentDesignspace_grid.py: -------------------------------------------------------------------------------- 1 | #drawbot 2 | 3 | # draw a grid of interpolated glyphs 4 | # along 2 continuous axes 5 | # from the current designspace. 6 | 7 | size(1159, 1070) 8 | 9 | def ip(a, b, f): 10 | return a+f*(b-a) 11 | 12 | def grid(ds, glyphName, horizontalAxis, verticalAxis, discreteLocation, columns, rows): 13 | items = [] 14 | for x in range(columns): 15 | xf = x/(columns-1) 16 | for y in range(rows): 17 | yf = y/(rows-1) 18 | # assume axes are continuous 19 | ah = ds.getAxis(horizontalAxis) 20 | ahValue = ip(ah.minimum, ah.maximum, xf) 21 | av = ds.getAxis(verticalAxis) 22 | avValue = ip(av.minimum, av.maximum, yf) 23 | loc = {horizontalAxis:ahValue, verticalAxis:avValue} 24 | if discreteLocation is not None: 25 | loc.update(discreteLocation) 26 | glyph = ds.makeOneGlyph(glyphName, loc) 27 | items.append(((x, y), loc, glyph)) 28 | return items 29 | 30 | # parameters 31 | glyphName = "A" 32 | horizontalAxis = "width" 33 | verticalAxis = "weight" 34 | discreteLoc = dict(slab=1) 35 | columns = 7 36 | rows = 7 37 | margin = 100 38 | xunit = (width()-2*margin)/columns 39 | yunit = (height()-2*margin)/rows 40 | 41 | d = CurrentDesignspace() 42 | print(d) 43 | fill(0) 44 | stroke(None) 45 | with savedState(): 46 | translate(margin,margin) 47 | for (x,y), loc, glyph in grid(d, glyphName, horizontalAxis, verticalAxis, discreteLoc, columns, rows): 48 | with savedState(): 49 | translate(x*xunit, y*yunit) 50 | scale(0.09) 51 | drawGlyph(glyph) 52 | 53 | # save the image 54 | saveImage(f"grid_{horizontalAxis}_{columns}_{verticalAxis}_{rows}.png") 55 | -------------------------------------------------------------------------------- /Scripting examples/rf_previewFont_to_spacecenter.py: -------------------------------------------------------------------------------- 1 | from fontTools.designspaceLib import InstanceDescriptor 2 | from mojo.UI import CurrentSpaceCenter, OpenSpaceCenter, splitText 3 | 4 | # Scripting with live designspaces 5 | # demo erik@letterror.com 12.12.2023 6 | 7 | proofText = "DS FIVE" 8 | 9 | # have a desigspace open in DSE2 10 | ds = CurrentDesignspace() 11 | 12 | # you probably want more control over what axis values to look at 13 | # and maybe even a fancy interface 14 | # but that is beyond the scope of this demo 15 | # so for now, a random location 16 | loc = ds.randomLocation() 17 | # the location is just a dict with axisName: axisValue pairs. 18 | print("location", loc) 19 | 20 | # we can split this random location into its continuous and discrete parts 21 | continuousPart, discretePart = ds.splitLocation(loc) 22 | print("continuous part of the location:", continuousPart) 23 | print("discrete part of the location:", discretePart) 24 | 25 | # get the default font 26 | # note we ask for the default of a specific discrete location here 27 | defaultFont = ds.findDefaultFont(discreteLocation=discretePart) 28 | glyphNames = splitText(proofText, defaultFont.asFontParts().getCharacterMapping()) 29 | print("glyphNames", glyphNames) 30 | 31 | # make an "instance descriptor" object to specify what we want to see 32 | preview = InstanceDescriptor() 33 | preview.designLocation = loc 34 | preview.familyName = defaultFont.info.familyName 35 | 36 | # now we're asking the designspace to make a font object with these specs 37 | # This makes the whole font. If you're just looking at a couple of characters 38 | # there are faster ways to do that. 39 | previewFont = ds.makeInstance(preview, 40 | glyphNames=set(glyphNames), 41 | decomposeComponents=True) 42 | previewFont = previewFont.asFontParts() 43 | 44 | # finally, we give the resulting font to the space center. 45 | # have a spacecenter open 46 | sp = CurrentSpaceCenter() 47 | if sp is None: 48 | sp = OpenSpaceCenter(previewFont) 49 | 50 | sp.setFont(previewFont) 51 | spString = "/" + "/".join(glyphNames) 52 | print('spString', spString) 53 | sp.setRaw(spString) 54 | 55 | # optionally, open the font window 56 | #previewFont.openInterface() 57 | -------------------------------------------------------------------------------- /icons/info.circle_info.circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /icons/questionmark.circle_questionmark.circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /icons/roundedRect.py: -------------------------------------------------------------------------------- 1 | from drawBot import * 2 | 3 | def roundedRect(x, y, w, h, radius, radiusBottomRight=None, radiusTopRight=None, radiusTopLeft=None): 4 | """ 5 | Draw a rounded rect from position `x`, `y` with given width and height and given `radius`. 6 | 7 | A radiuses that exceeds the width or height of the rectangle will be clipped. 8 | 9 | Optionally a radius could be provided for each corner the following order: 10 | bottom left, bottom right, top right, top left 11 | 12 | .. downloadcode:: roundedRect.py 13 | 14 | # draw a rounding rect 15 | # x y w h radius 16 | roundedRect(100, 100, 200, 200, 10) 17 | 18 | # x y w h bl br tr tl 19 | roundedRect(100, 330, 200, 200, 10, 30, 40, 50) 20 | """ 21 | # do some checking on the radiuses 22 | radiusBottomLeft = radius 23 | if radiusTopLeft is None and radiusTopRight is None and radiusBottomRight is None: 24 | radiusTopLeft = radiusTopRight = radiusBottomRight = radius 25 | 26 | if radiusBottomLeft + radiusBottomRight > w: 27 | diff = (radiusBottomLeft + radiusBottomRight - w) * .5 28 | radiusBottomLeft -= diff 29 | radiusBottomRight -= diff 30 | if radiusTopLeft + radiusTopRight > w: 31 | diff = (radiusTopLeft + radiusTopRight - w) * .5 32 | radiusTopLeft -= diff 33 | radiusTopRight -= diff 34 | if radiusBottomLeft + radiusTopLeft > h: 35 | diff = (radiusBottomLeft + radiusTopLeft - h) * .5 36 | radiusBottomLeft -= diff 37 | radiusTopLeft -= diff 38 | if radiusBottomRight + radiusTopRight > h: 39 | diff = (radiusBottomRight + radiusTopRight - h) * .5 40 | radiusBottomRight -= diff 41 | radiusTopRight -= diff 42 | 43 | minValue = min(w, h) 44 | 45 | if radiusBottomRight < 0: 46 | radiusBottomRight = 0 47 | if radiusTopRight < 0: 48 | radiusTopRight = 0 49 | if radiusBottomLeft < 0: 50 | radiusBottomLeft = 0 51 | if radiusTopLeft < 0: 52 | radiusTopLeft = 0 53 | 54 | if radiusBottomRight > minValue: 55 | radiusBottomRight = minValue 56 | if radiusTopRight > minValue: 57 | radiusTopRight = minValue 58 | if radiusBottomLeft > minValue: 59 | radiusBottomLeft = minValue 60 | if radiusTopLeft > minValue: 61 | radiusTopLeft = minValue 62 | 63 | # start drawing 64 | path = BezierPath() 65 | path.moveTo((x + radiusBottomLeft, y)) 66 | path.lineTo((x + w - radiusBottomRight, y)) 67 | path.arcTo((x + w, y), (x + w, y + radiusBottomRight), radiusBottomRight) 68 | path.lineTo((x + w, y + h - radiusTopRight)) 69 | path.arcTo((x + w, y + h), (x + w - radiusTopRight, y + h), radiusTopRight) 70 | path.lineTo((x + radiusTopLeft, y + h)) 71 | path.arcTo((x, y + h), (x, y + h - radiusTopLeft), radiusTopLeft) 72 | path.lineTo((x, y + radiusBottomLeft)) 73 | path.arcTo((x, y), (x + radiusBottomLeft, y), radiusBottomLeft) 74 | path.closePath() 75 | drawPath(path) 76 | 77 | # draw a rounded rect 78 | roundedRect(10, 10, 100, 100, 20, 20, 70, 90) -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/parsers/parserTools.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import textwrap 3 | 4 | 5 | commentSign = "#" 6 | 7 | 8 | def getBlocks(text): 9 | """ 10 | Split a text into indented blocks, intends could be a single space or multiple spaces or a tab 11 | """ 12 | blocks = {} 13 | currentTag = None 14 | currentBlock = [] 15 | for line in text.splitlines(): 16 | if commentSign in line: 17 | line, comment = line.split(commentSign) 18 | line = line.rstrip() 19 | if not line: 20 | continue 21 | if line.startswith((" ", "\t")): 22 | currentBlock.append(line) 23 | else: 24 | if currentTag is not None: 25 | blocks[currentTag] = textwrap.dedent("\n".join(currentBlock)) 26 | currentBlock = [] 27 | currentTag = line 28 | 29 | if currentTag is not None: 30 | blocks[currentTag] = textwrap.dedent("\n".join(currentBlock)) 31 | return blocks 32 | 33 | 34 | def getLines(text): 35 | """ 36 | Split a string in lines and strip white space. Ignore empty lines. 37 | """ 38 | lines = [] 39 | for line in text.splitlines(): 40 | if commentSign in line: 41 | line, comment = line.split(commentSign) 42 | line = line.strip() 43 | if line: 44 | lines.append(line) 45 | return lines 46 | 47 | 48 | def stringToNumber(n, fallback=None): 49 | if not n and isinstance(n, str): 50 | return fallback 51 | n = float(n) 52 | if n.is_integer(): 53 | return int(n) 54 | return n 55 | 56 | 57 | def numberToString(s): 58 | s = stringToNumber(s) 59 | if s is None: 60 | return None 61 | return str(s) 62 | 63 | 64 | # tests 65 | 66 | @pytest.mark.parametrize("text,expected", [ 67 | ("", {}), 68 | ("foo\n bar\n more", {"foo": "bar\nmore"}), 69 | ("foo\n bar\n more", {"foo": "bar\n more"}), 70 | ("foo\n bar\n more", {'foo': 'bar\nmore'}), 71 | ("foo\n bar\n more", {'foo': ' bar\nmore'}), 72 | ("foo\n\tbar\n more", {'foo': '\tbar\n more'}), 73 | 74 | ("foo\n bar\n more\nhello\n world\nsecond\n one\n two\n three", {"foo": "bar\nmore", "hello": "world", "second": "one\ntwo\nthree"}), 75 | ]) 76 | def test_getBlocks(text, expected): 77 | result = getBlocks(text) 78 | assert result == expected 79 | 80 | 81 | @pytest.mark.parametrize("text,expected", [ 82 | ("", []), 83 | ("this\nshould be\nsplitted\t\nin\u0020\u0020\u0020\nparts", ['this', 'should be', 'splitted', 'in', 'parts']) 84 | ]) 85 | def test_getLines(text, expected): 86 | result = getLines(text) 87 | assert result == expected 88 | 89 | 90 | @pytest.mark.parametrize("astring,expected", [ 91 | ("", None), 92 | ("10", 10), 93 | ("10.0", 10), 94 | ("10.5", 10.5), 95 | ]) 96 | def test_stringToNumber(astring, expected): 97 | result = stringToNumber(astring) 98 | assert result == expected 99 | 100 | 101 | @pytest.mark.parametrize("anumber,expected", [ 102 | ("", None), 103 | (10, "10"), 104 | (10.0, "10"), 105 | (10.5, "10.5"), 106 | ]) 107 | def test_numberToString(anumber, expected): 108 | result = numberToString(anumber) 109 | print(anumber, result) 110 | assert result == expected 111 | 112 | 113 | if __name__ == '__main__': 114 | pytest.main([__file__]) 115 | -------------------------------------------------------------------------------- /icons/wand.and.stars_wand.and.stars.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/parsers/rulesParser.py: -------------------------------------------------------------------------------- 1 | """ 2 | # rules text spec 3 | 4 | 5 | # indent 6 | # list of subsititions 7 | > 8 | ... 9 | 10 | # conditions 11 | - 12 | ... 13 | # condition set 14 | - - 15 | ... 16 | """ 17 | 18 | import re 19 | from fontTools import designspaceLib 20 | 21 | from .parserTools import getBlocks, getLines, stringToNumber, numberToString 22 | 23 | substitionRE = re.compile(r"([a-zA-Z0-9\.\*\+\-\:\^\|\~_]+)\s+\>\s+([a-zA-Z0-9\.\*\+\-\:\^\|\~_]+)") 24 | conditionsRE = re.compile(r"([a-zA-Z]+)\s+([0-9\.]+)-([0-9\.]+)") 25 | 26 | 27 | rulesLibKey = "com.letterror.designspaceEditor.rules.text" 28 | 29 | 30 | def parseRules(text, ruleDescriptorClass=None): 31 | if ruleDescriptorClass is None: 32 | ruleDescriptorClass = designspaceLib.RuleDescriptor 33 | rules = [] 34 | for name, lines in getBlocks(text).items(): 35 | rule = ruleDescriptorClass() 36 | rules.append(rule) 37 | 38 | rule.name = name 39 | 40 | for line in getLines(lines): 41 | for r in re.finditer(substitionRE, line): 42 | glyphName1, glyphName2 = r.groups() 43 | rule.subs.append((glyphName1, glyphName2)) 44 | 45 | conditionSet = [] 46 | for f in re.finditer(conditionsRE, line): 47 | axisName, minimumValue, maximumValue = f.groups() 48 | conditionSet.append(dict(name=axisName, minimum=stringToNumber(minimumValue), maximum=stringToNumber(maximumValue))) 49 | if conditionSet: 50 | rule.conditionSets.append(conditionSet) 51 | return rules 52 | 53 | 54 | def dumpRules(rules, indent=" "): 55 | text = [] 56 | for rule in rules: 57 | text.append(rule.name) 58 | 59 | for glyphName1, glyphName2 in rule.subs: 60 | text.append(f"{indent}{glyphName1} > {glyphName2}") 61 | text.append("") 62 | 63 | for conditionSet in rule.conditionSets: 64 | conditionSetText = [] 65 | for condition in conditionSet: 66 | conditionSetText.append(f"{condition['name']} {numberToString(condition['minimum'])}-{numberToString(condition['maximum'])}") 67 | text.append(indent + " ".join(conditionSetText)) 68 | text.append("") 69 | return "\n".join(text) 70 | 71 | 72 | def extractRules(operator, indent=" "): 73 | """ 74 | Extract rules to a string for a given operator: 75 | check if rules is stored as a string in the lib, 76 | parse that stored string and compare with the internal rules. 77 | 78 | Compare with ignore the rules order. 79 | 80 | If there is no difference, use the string representation. 81 | 82 | This will preseve comments and white space. 83 | """ 84 | storedText = operator.lib.get(rulesLibKey, "") 85 | parsed = parseRules(storedText) 86 | 87 | if sorted([list(item.asdict().items()) for item in parsed]) == sorted([list(item.asdict().items()) for item in operator.rules]): 88 | return storedText 89 | return dumpRules(operator.rules, indent=indent) 90 | 91 | 92 | def storeRules(text, operator): 93 | """ 94 | Store rules as objects from a string for a given operator and 95 | store the rules string representation in the operator lib. 96 | """ 97 | parsed = parseRules(text, operator.writerClass.ruleDescriptorClass) 98 | operator.lib[rulesLibKey] = text 99 | operator.rules.clear() 100 | operator.rules.extend(parsed) 101 | 102 | 103 | # tests 104 | 105 | def test_parseRules(): 106 | expected = """ 107 | ruleName 108 | a > a.alt agrave > agrave.alt 109 | b > b.alt 110 | 111 | weight 800-1000 opsz 200-250 112 | width 100-300 113 | """ 114 | result = parseRules(expected, designspaceLib.RuleDescriptor) 115 | assert len(result) == 1 116 | descriptor = result[0] 117 | assert descriptor.name == "ruleName" 118 | assert descriptor.subs == [("a", "a.alt"), ("agrave", "agrave.alt"), ("b", "b.alt")] 119 | 120 | 121 | def test_Rules(): 122 | expected = [designspaceLib.RuleDescriptor(name="ruleName", subs=[("a", "a.alt")])] 123 | result = dumpRules(expected) 124 | rules = parseRules(result, designspaceLib.RuleDescriptor) 125 | assert len(expected) == len(rules) 126 | assert expected[0].name == rules[0].name 127 | assert expected[0].subs == rules[0].subs 128 | 129 | 130 | if __name__ == '__main__': 131 | import pytest 132 | pytest.main([__file__]) 133 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/parsers/variableFontsParser.py: -------------------------------------------------------------------------------- 1 | """ 2 | # variable font text spec 3 | 4 | 5 | # indent 6 | > '' # optional 7 | 8 | # a full axis subset 9 | 10 | ... 11 | 12 | # an axis subset at user value 13 | 14 | ... 15 | 16 | # an axis subset with a sub range 17 | 18 | ... 19 | 20 | """ 21 | import re 22 | import math 23 | from fontTools import designspaceLib 24 | 25 | from .parserTools import getLines, getBlocks, stringToNumber, numberToString 26 | 27 | axisSubsetRE = re.compile(r"([a-zA-Z0-9\-]+)\s*([\-0-9\.]*)\s*([\-0-9\.]*)\s*([\-0-9\.]*)") 28 | filenameRE = re.compile(r"\>\s+[\"|\'](.*)[\"|\']") 29 | 30 | variableFontsLibKey = "com.letterror.designspaceEditor.variableFonts.text" 31 | 32 | 33 | def parseVariableFonts(text, variableFontDescriptorClass=None): 34 | if variableFontDescriptorClass is None: 35 | variableFontDescriptorClass = designspaceLib.VariableFontDescriptor 36 | 37 | variableFonts = list() 38 | for variableFontName, lines in getBlocks(text).items(): 39 | variableFont = variableFontDescriptorClass(name=variableFontName) 40 | variableFonts.append(variableFont) 41 | 42 | for line in getLines(lines): 43 | filenameFound = re.match(filenameRE, line) 44 | if filenameFound: 45 | variableFont.filename = filenameFound.groups()[0] 46 | 47 | axisSubsetFound = re.match(axisSubsetRE, line) 48 | if axisSubsetFound: 49 | axisName, minValue, value, maxValue = axisSubsetFound.groups() 50 | if minValue and not value and not maxValue: 51 | variableFont.axisSubsets.append( 52 | designspaceLib.ValueAxisSubsetDescriptor( 53 | name=axisName, 54 | userValue=stringToNumber(minValue), 55 | ) 56 | ) 57 | else: 58 | variableFont.axisSubsets.append( 59 | designspaceLib.RangeAxisSubsetDescriptor( 60 | name=axisName, 61 | userMinimum=stringToNumber(minValue, -math.inf), 62 | userDefault=stringToNumber(value), 63 | userMaximum=stringToNumber(maxValue, math.inf) 64 | ) 65 | ) 66 | 67 | return variableFonts 68 | 69 | 70 | def dumpVariableFonts(variableFonts, indent=" "): 71 | text = [] 72 | for variableFont in variableFonts: 73 | text.append(variableFont.name) 74 | if variableFont.filename is not None: 75 | text.append(f"{indent}> '{variableFont.filename}'") 76 | text.append("") 77 | for axisSubset in variableFont.axisSubsets: 78 | line = f"{indent}{axisSubset.name}" 79 | if hasattr(axisSubset, "userValue"): 80 | line += f" {numberToString(axisSubset.userValue)}" 81 | else: 82 | if axisSubset.userMinimum is not None and axisSubset.userDefault is not None and axisSubset.userMaximum is not None: 83 | line += f" {numberToString(axisSubset.userMinimum)} {numberToString(axisSubset.userDefault)} {numberToString(axisSubset.userMaximum)}" 84 | 85 | text.append(line) 86 | text.append("") 87 | 88 | return "\n".join(text) 89 | 90 | 91 | def extractVariableFonts(operator, indent=" "): 92 | """ 93 | Extract variable fonts to a string for a given operator: 94 | check if variableFonts is stored as a string in the lib, 95 | parse that stored string and compare with the internal variable fonts. 96 | 97 | Compare with ignore the variableFonts order. 98 | 99 | If there is no difference, use the string representation. 100 | 101 | This will preseve comments and white space. 102 | """ 103 | storedText = operator.lib.get(variableFontsLibKey, "") 104 | parsed = parseVariableFonts(storedText) 105 | 106 | if sorted([list(item.asdict().items()) for item in parsed]) == sorted([list(item.asdict().items()) for item in operator.variableFonts]): 107 | return storedText 108 | return dumpVariableFonts(operator.variableFonts, indent=indent) 109 | 110 | 111 | def storeVariableFonts(text, operator): 112 | """ 113 | Store variable fonts as objects from a string for a given operator and 114 | store the variable fonts string representation in the operator lib. 115 | """ 116 | parsed = parseVariableFonts(text, operator.writerClass.variableFontDescriptorClass) 117 | operator.lib[variableFontsLibKey] = text 118 | operator.variableFonts.clear() 119 | operator.variableFonts.extend(parsed) 120 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/designspaceSubscribers.py: -------------------------------------------------------------------------------- 1 | import AppKit 2 | from objc import python_method, super 3 | 4 | from lib.tools.debugTools import ClassNameIncrementer 5 | 6 | from mojo.subscriber import Subscriber, registerGlyphEditorSubscriber, registerCurrentFontSubscriber, registerRoboFontSubscriber 7 | 8 | from designspaceEditor.tools import SendNotification 9 | 10 | 11 | class OperatorRegistry(AppKit.NSObject, metaclass=ClassNameIncrementer): 12 | 13 | def init(self): 14 | self = super().init() 15 | self.operators = [] 16 | self.currentOperator = None 17 | 18 | center = AppKit.NSNotificationCenter.defaultCenter() 19 | center.addObserver_selector_name_object_(self, "windowBecomeMain:", AppKit.NSWindowDidBecomeMainNotification, None) 20 | center.addObserver_selector_name_object_(self, "windowResignMain:", AppKit.NSWindowDidResignMainNotification, None) 21 | return self 22 | 23 | @python_method 24 | def append(self, operator): 25 | if operator not in self.operators: 26 | self.operators.append(operator) 27 | 28 | @python_method 29 | def remove(self, operator): 30 | if operator in self.operators: 31 | self.operators.remove(operator) 32 | 33 | def windowBecomeMain_(self, notification): 34 | window = notification.object() 35 | delegate = window.delegate() 36 | if hasattr(delegate, "vanillaWrapper"): 37 | controller = delegate.vanillaWrapper() 38 | if controller.__class__.__name__ == "DesignspaceEditorController": 39 | self.updateCurrentDesignspace_(controller.operator) 40 | 41 | def windowResignMain_(self, notification): 42 | window = notification.object() 43 | delegate = window.delegate() 44 | if hasattr(delegate, "vanillaWrapper"): 45 | controller = delegate.vanillaWrapper() 46 | if controller.__class__.__name__ == "DesignspaceEditorController": 47 | self.updateCurrentDesignspace_(controller.operator) 48 | 49 | def updateCurrentDesignspace_(self, operator): 50 | if operator != self.currentOperator: 51 | if self.currentOperator is not None: 52 | SendNotification.single(action="ResignCurrent", designspace=self.currentOperator) 53 | if operator is not None: 54 | SendNotification.single(action="BecomeCurrent", designspace=operator) 55 | self.currentOperator = operator 56 | 57 | 58 | _operatorRegistry = OperatorRegistry.alloc().init() 59 | 60 | 61 | def registerOperator(operator): 62 | _operatorRegistry.append(operator) 63 | 64 | 65 | def unregisterOperator(operator): 66 | _operatorRegistry.remove(operator) 67 | 68 | 69 | def notifyOperator(font, who, action="Change", operatorMethod="changed", operatorKwargs=dict(), notificationKwargs=dict()): 70 | for operator in _operatorRegistry.operators: 71 | for sourceDescriptor in operator.sources: 72 | if sourceDescriptor.path == font.path: 73 | if operatorMethod: 74 | callback = getattr(operator, operatorMethod) 75 | callback(**operatorKwargs) 76 | SendNotification.single(who=who, action=action, designspace=operator, **notificationKwargs) 77 | return 78 | 79 | 80 | class DesignspaceEditorPreviewGlyphSubscriber(Subscriber): 81 | 82 | debug = True 83 | 84 | operators = [] 85 | 86 | def glyphDidChange(self, info): 87 | glyph = info["glyph"] 88 | font = glyph.font 89 | notifyOperator( 90 | font, 91 | who="SourceGlyph", 92 | operatorMethod="glyphChanged", 93 | operatorKwargs=dict( 94 | glyphName=glyph.name, 95 | includeDependencies=True 96 | ), 97 | notificationKwargs=dict( 98 | glyph=glyph 99 | ) 100 | ) 101 | 102 | 103 | class DesignspaceEditorCurrentFontSubscriber(Subscriber): 104 | 105 | debug = True 106 | 107 | def currentFontInfoDidChange(self, info): 108 | font = info["font"] 109 | notifyOperator( 110 | font, 111 | who="SourceInfo", 112 | notificationKwargs=dict( 113 | font=font 114 | ) 115 | ) 116 | 117 | def currentFontKerningDidChange(self, info): 118 | font = info["font"] 119 | notifyOperator( 120 | font, 121 | who="SourceKerning", 122 | notificationKwargs=dict( 123 | font=font 124 | ) 125 | ) 126 | 127 | def currentFontGroupsDidChange(self, info): 128 | font = info["font"] 129 | notifyOperator( 130 | font, 131 | who="SourceGroups", 132 | notificationKwargs=dict( 133 | font=font 134 | ) 135 | ) 136 | 137 | 138 | class DesignspaceEditorFontDocumentSubscriber(Subscriber): 139 | 140 | debug = True 141 | 142 | def fontDocumentDidChangeExternally(self, info): 143 | font = info["font"] 144 | notifyOperator( 145 | font, 146 | who="SourceFont", 147 | action="ChangedExternally", 148 | notificationKwargs=dict( 149 | font=font 150 | ) 151 | ) 152 | 153 | 154 | registerGlyphEditorSubscriber(DesignspaceEditorPreviewGlyphSubscriber) 155 | registerCurrentFontSubscriber(DesignspaceEditorCurrentFontSubscriber) 156 | registerRoboFontSubscriber(DesignspaceEditorFontDocumentSubscriber) 157 | -------------------------------------------------------------------------------- /icons/makeIcons_dse1.py: -------------------------------------------------------------------------------- 1 | # with a wink to https://nl.wikipedia.org/wiki/Yaacov_Agam 2 | # generated in 2017 3 | # then I lost the script 4 | # so, rebuilt in 2023. 5 | 6 | iconSize = (30,30) 7 | output = ".pdf" 8 | 9 | def aiColor(r, g, b): 10 | # convert color reported in illustrator to drawbot 11 | return (r/255, g/255, b/255) 12 | 13 | def ip(a, b, f): 14 | # interpolate single value 15 | return a+f*(b-a) 16 | 17 | def ip2(a, b, f): 18 | # interpolate 2 tuple 19 | return ip(a[0], b[0], f), ip(a[1], b[1], f) 20 | 21 | def ip3(a, b, f): 22 | # interpolate 3-tuple 23 | # I'm sure this can be more effish, but it is cold 24 | # and I like the laptop warm. 25 | return ip(a[0], b[0], f), ip(a[1], b[1], f), ip(a[2], b[2], f) 26 | 27 | # some basic values for all icons 28 | clr1 = aiColor(217, 87, 204) 29 | clr3 = aiColor(217, 87, 126) 30 | clr2 = aiColor(38, 15, 22) 31 | clr4 = aiColor(38, 15, 36) 32 | 33 | # adjusted colors 34 | r = 157 35 | g = 97 36 | clr1 = aiColor(r, g, 204) 37 | clr3 = aiColor(r, g, 126) 38 | clr2 = aiColor(38, 15, 22) 39 | clr4 = aiColor(38, 15, 36) 40 | 41 | tp = 0.8 # transparency for some things 42 | m = 1.5 # outside margin 43 | d = 6.2 # line diameter 44 | fs = 6.5 # text button font size 45 | 46 | forReals = False # set to True to make individual files 47 | if forReals: 48 | # save individual files in the resources folder 49 | folder = "../resources/" 50 | #folder = "../DesignspaceEditor2.roboFontExt/resources/" 51 | else: 52 | # save one pdf preview in the folder with this drawbot script 53 | folder = "" 54 | 55 | # axes icon 56 | if forReals: 57 | newDrawing() 58 | newPage(*iconSize) 59 | left, right = m+.5*d, width()-m-0.5*d 60 | top, bottom = height()-m-.5*d, m+0.5*d 61 | steps = 103 62 | items = {} 63 | with savedState(): 64 | for i in range(steps): 65 | f = i/steps 66 | c1 = ip3(clr1, clr2, f) 67 | c2 = ip3(clr3, clr4, f) 68 | p1 = ip2((left, top), (right, bottom), f) 69 | p2 = ip2((right, top), (left, bottom), f) 70 | fill(*c1) 71 | oval(p1[0]-.5*d,p1[1]-.5*d, d, d) 72 | fill(*c2) 73 | oval(p2[0]-.5*d,p2[1]-.5*d, d, d) 74 | if forReals: 75 | name = f"{folder}toolbar_icon_axes{output}" 76 | saveImage(name) 77 | 78 | # instances icon 79 | if forReals: 80 | newDrawing() 81 | newPage(*iconSize) 82 | with savedState(): 83 | steps = 4 84 | for y in range(steps): 85 | fy = y / (steps-1) 86 | p1 = ip2((left, top), (right, top), fy) 87 | p2 = ip2((left, bottom), (right, bottom), fy) 88 | c1 = ip3(clr1, clr3, fy) 89 | c2 = ip3(clr2, clr4, fy) 90 | for x in range(steps): 91 | fx = x / (steps-1) 92 | p = ip2(p1, p2, fx) 93 | c = ip3(c1, c2, fx) 94 | fill(*c, tp) # transparency 95 | oval(p[0]-.5*d,p[1]-.5*d, d, d) 96 | if forReals: 97 | name = f"{folder}toolbar_icon_instances{output}" 98 | saveImage(name) 99 | 100 | # sources icon 101 | if forReals: 102 | newDrawing() 103 | newPage(*iconSize) 104 | with savedState(): 105 | steps = 4 106 | for y in range(steps): 107 | fy = y / (steps-1) 108 | p1 = ip2((left, top), (right, top), fy) 109 | p2 = ip2((left, bottom), (right, bottom), fy) 110 | c1 = ip3(clr1, clr3, fy) 111 | c2 = ip3(clr2, clr4, fy) 112 | for x in range(steps): 113 | fx = x / (steps-1) 114 | p = ip2(p1, p2, fx) 115 | c = ip3(c1, c2, fx) 116 | if 0 < x < steps-1 or 0 < y < steps-1: 117 | tps = 0.2 118 | else: 119 | tps = 0.7 120 | fill(*c, tps) # transparency 121 | oval(p[0]-.5*d,p[1]-.5*d, d, d) 122 | if forReals: 123 | name = f"{folder}toolbar_icon_sources{output}" 124 | saveImage(name) 125 | 126 | # labels icon 127 | # defines groups and relations between locations 128 | if forReals: 129 | newDrawing() 130 | newPage(*iconSize) 131 | with savedState(): 132 | steps = 4 133 | stops = {} 134 | fills = {} 135 | for y in range(steps): 136 | fy = y / (steps-1) 137 | p1 = ip2((left, top), (right, top), fy) 138 | p2 = ip2((left, bottom), (right, bottom), fy) 139 | c1 = ip3(clr1, clr3, fy) 140 | c2 = ip3(clr2, clr4, fy) 141 | for x in range(steps): 142 | fx = x / (steps-1) 143 | p = ip2(p1, p2, fx) 144 | c = ip3(c1, c2, fx) 145 | fill(*c, 0.7*tp) # transparency 146 | oval(p[0]-.5*d,p[1]-.5*d, d, d) 147 | stops[(y, x)] = p 148 | fills[(y, x)] = c 149 | with savedState(): 150 | fill(None) 151 | strokeWidth(d) 152 | lineCap("round") 153 | ltp = 0.5 154 | stroke(*fills[(0,0)],ltp) 155 | line(stops[(0,0)], stops[(0,1)]) 156 | stroke(*fills[(1,1)],ltp) 157 | line(stops[(1,1)], stops[(1,3)]) 158 | stroke(*fills[(2,3)],ltp) 159 | line(stops[(2,1)], stops[(2,3)]) 160 | stroke(*fills[(1,0)],ltp) 161 | line(stops[(1,0)], stops[(1,0)]) 162 | stroke(*fills[(3,0)],ltp) 163 | line(stops[(2,1)], stops[(3,1)]) 164 | if forReals: 165 | name = f"{folder}toolbar_icon_labels{output}" 166 | saveImage(name) 167 | 168 | # problems icon 169 | if forReals: 170 | newDrawing() 171 | newPage(*iconSize) 172 | with savedState(): 173 | # gridlines 174 | strokeWidth(d) 175 | lineCap("round") 176 | stroke(*clr1, tp) 177 | line((left, top), (right, top)) 178 | stroke(*clr2, tp) 179 | line((left, top), (left, bottom)) 180 | stroke(*clr2, tp) 181 | line((right, top), (right, bottom)) 182 | stroke(*clr3, tp) 183 | line((left, bottom), (right, bottom)) 184 | steps = 4 185 | # white rects 186 | fill(1) 187 | stroke(None) 188 | rd = 0.4*d 189 | for y in range(steps): 190 | fy = y / (steps-1) 191 | p1 = ip2((left, top), (right, top), fy) 192 | p2 = ip2((left, bottom), (right, bottom), fy) 193 | for x in range(steps): 194 | fx = x / (steps-1) 195 | p = ip2(p1, p2, fx) 196 | if 0 < fx < 1 and 0 < fy < 1: 197 | fill(*clr3) # transparency 198 | else: 199 | fill(1) 200 | rect(p[0]-.5*rd,p[1]-.5*rd, rd, rd) 201 | if forReals: 202 | name = f"{folder}toolbar_icon_problems{output}" 203 | saveImage(name) 204 | 205 | # rules icon 206 | if forReals: 207 | newDrawing() 208 | newPage(*iconSize) 209 | with savedState(): 210 | steps = 4 211 | for y in range(steps): 212 | fy = y / (steps-1) 213 | p1 = ip2((left, top), (right, top), fy) 214 | p2 = ip2((left, bottom), (right, bottom), fy) 215 | c1 = ip3(clr1, clr3, fy) 216 | c2 = ip3(clr2, clr4, fy) 217 | for x in range(steps): 218 | fx = x / (steps-1) 219 | p = ip2(p1, p2, fx) 220 | c = ip3(c1, c2, fx) 221 | fill(*c, tp) # transparency 222 | oval(p[0]-.5*d,p[1]-.5*d, d, d) 223 | 224 | # white rects 225 | fill(1) 226 | stroke(None) 227 | rd = 0.4*d 228 | for y in range(2, steps): 229 | fy = y / (steps-1) 230 | p1 = ip2((left, top), (right, top), fy) 231 | p2 = ip2((left, bottom), (right, bottom), fy) 232 | for x in range(steps): 233 | fx = x / (steps-1) 234 | p = ip2(p1, p2, fx) 235 | fill(1) 236 | rect(p[0]-.5*rd,p[1]-.5*rd, rd, rd) 237 | if forReals: 238 | name = f"{folder}toolbar_icon_rules{output}" 239 | saveImage(name) 240 | 241 | textX = 2 242 | textY = 12.7 243 | 244 | # rules icon 245 | if forReals: 246 | newDrawing() 247 | newPage(*iconSize) 248 | with savedState(): 249 | pass 250 | font("ActionTextDark-Bold") 251 | fontSize(fs) 252 | fill(*clr2) 253 | text("Save", (textX,textY)) 254 | if forReals: 255 | name = f"{folder}toolbar_icon_save{output}" 256 | saveImage(name) 257 | 258 | # rules icon 259 | if forReals: 260 | newDrawing() 261 | newPage(*iconSize) 262 | with savedState(): 263 | pass 264 | font("ActionTextDark-Bold") 265 | fontSize(fs) 266 | fill(*clr2) 267 | text("Settings", (textX,textY)) 268 | if forReals: 269 | name = f"{folder}toolbar_icon_settings{output}" 270 | saveImage(name) 271 | 272 | if not forReals: 273 | saveImage("all_icons_preview.pdf") -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/parsers/labelsParser.py: -------------------------------------------------------------------------------- 1 | """ 2 | # axis label text spec 3 | 4 | # add localised axis label for language tag 5 | ? '' 6 | ... 7 | 8 | # add axis label in a between minimum value, default value, maximum value 9 | 10 | # add localised axis lables for language tag 11 | ? ' # optional 12 | ... 13 | 14 | # add axis label add a uservalue 15 | 16 | # add localised axis lables for language tag 17 | ? ' # optional 18 | ... 19 | 20 | # optionally add (elidable) or (olderSibling) or [linkedUserValue] 21 | (elidable) (olderSibling) [] 22 | 23 | 24 | # location label text spec 25 | 26 |