├── .github └── workflows │ └── static.yml ├── .gitignore ├── DSEezui.py ├── DesignspaceEditor2.roboFontExt ├── html │ ├── chart.bar.doc.horizontal2x.png │ ├── index.html │ ├── previewWindow.jpg │ ├── previewWindow_options2.png │ ├── questionmark.circle2x.png │ ├── screenshot_mutatorsans_axestab.jpg │ ├── screenshot_mutatorsans_axestab.png │ ├── screenshot_mutatorsans_instancestab.png │ ├── screenshot_mutatorsans_previewwindow1.jpg │ ├── screenshot_mutatorsans_problemstab.jpg │ ├── screenshot_mutatorsans_rulestab.jpg │ ├── screenshot_mutatorsans_sourcestab.jpg │ ├── screenshot_mutatorsans_variablefontstab.jpg │ ├── sparse_master.png │ ├── square.and.arrow.down2x.png │ ├── styles.css │ ├── toolbar_25_25_info.circle.fill.svg │ ├── toolbar_500_500_icon_axes.png │ ├── toolbar_500_500_icon_instances.png │ ├── toolbar_500_500_icon_location_labels.png │ ├── toolbar_500_500_icon_notes.png │ ├── toolbar_500_500_icon_notifications.png │ ├── toolbar_500_500_icon_problems.png │ ├── toolbar_500_500_icon_rules.png │ ├── toolbar_500_500_icon_save.png │ ├── toolbar_500_500_icon_settings.png │ ├── toolbar_500_500_icon_sources.png │ └── toolbar_500_500_icon_variable_fonts.png ├── info.plist ├── lib │ ├── designspaceEditor │ │ ├── __init__.py │ │ ├── designspaceLexer.py │ │ ├── designspaceSubscribers.py │ │ ├── locationPreview.py │ │ ├── parsers │ │ │ ├── __init__.py │ │ │ ├── glyphNameParser.py │ │ │ ├── labelsParser.py │ │ │ ├── mapParser.py │ │ │ ├── parserTools.py │ │ │ ├── rulesParser.py │ │ │ ├── testParser.py │ │ │ └── variableFontsParser.py │ │ ├── tools.py │ │ └── ui.py │ ├── install.py │ ├── main.py │ ├── newDesignspaceEditor.py │ └── openDesignspaceEditor.py └── resources │ ├── toolbar_30_30_arrow.right.filled.filter.arrow.right_arrow.right.filled.filter.arrow.right.pdf │ ├── toolbar_30_30_atom.pdf │ ├── toolbar_30_30_chart.bar.doc.horizontal.pdf │ ├── toolbar_30_30_icon_axes.pdf │ ├── toolbar_30_30_icon_instances.pdf │ ├── toolbar_30_30_icon_location_labels.pdf │ ├── toolbar_30_30_icon_notes.pdf │ ├── toolbar_30_30_icon_notifications.pdf │ ├── toolbar_30_30_icon_problems.pdf │ ├── toolbar_30_30_icon_rules.pdf │ ├── toolbar_30_30_icon_sources.pdf │ ├── toolbar_30_30_icon_variable_fonts.pdf │ ├── toolbar_30_30_icon_variablefonts.pdf │ ├── toolbar_30_30_mappin.and.ellipse.pdf │ ├── toolbar_30_30_questionmark.circle.pdf │ ├── toolbar_30_30_smallcircle.filled.circle.fill.pdf │ ├── toolbar_30_30_smallcircle.filled.circle.pdf │ ├── toolbar_30_30_square.and.arrow.down.pdf │ └── toolbar_30_30_wand.and.stars.pdf ├── LICENSE ├── README.md ├── Scripting examples ├── db_currentDesignspace.py ├── db_currentDesignspace_grid.py ├── db_simpleGlyphPreview.py ├── db_sliders.py ├── db_werkman.py ├── rf_change_previewLocation_of_all_open_designspaces.py ├── rf_close_all_sources_from_designspace.py ├── rf_getBasicValues.py ├── rf_makeOneKerning.py ├── rf_open_all_sources_from_designspace.py └── rf_previewFont_to_spacecenter.py ├── assets ├── designSpaceFileIcon.png ├── info.circle.fill.svg ├── questionmark.circle.svg ├── toolBar_100_100_icon_ chart.bar.doc.horizontal.png ├── toolbar_100_100_icon_axes.png ├── toolbar_100_100_icon_instances.png ├── toolbar_100_100_icon_location_labels.png ├── toolbar_100_100_icon_notes.png ├── toolbar_100_100_icon_notifications.png ├── toolbar_100_100_icon_problems.png ├── toolbar_100_100_icon_rules.png ├── toolbar_100_100_icon_save.png ├── toolbar_100_100_icon_settings.png ├── toolbar_100_100_icon_sources.png ├── toolbar_100_100_icon_variable_fonts.png ├── toolbar_100_100_icon_variable_notifications.png ├── toolbar_100_100_info.bubble.fill.png ├── toolbar_100_100_info.circle.fill.png ├── toolbar_100_100_info.circle.png ├── toolbar_100_100_questionmark.circle.png ├── toolbar_100_100_square.and.arrow.down.png ├── toolbar_500_500_icon_axes.png ├── toolbar_500_500_icon_instances.png ├── toolbar_500_500_icon_location_labels.png ├── toolbar_500_500_icon_notes.png ├── toolbar_500_500_icon_notifications.png ├── toolbar_500_500_icon_problems.png ├── toolbar_500_500_icon_rules.png ├── toolbar_500_500_icon_sources.png └── toolbar_500_500_icon_variable_fonts.png ├── icons ├── all_icons_1000_1000_dse2_preview.pdf ├── export_SF_Symbol.py ├── info.bubble.fill_info.bubble.fill.svg ├── info.circle.fill_info.circle.fill.svg ├── info.circle_info.circle.svg ├── makeIcons_dse1.py ├── makeIcons_dse2.py ├── questionmark.circle_questionmark.circle.svg ├── roundedRect.py ├── square.and.arrow.down_square.and.arrow.down.svg ├── toolbar_1000_1000_icon_axes.png ├── toolbar_1000_1000_icon_instances.png ├── toolbar_1000_1000_icon_location_labels.png ├── toolbar_1000_1000_icon_notes.png ├── toolbar_1000_1000_icon_notifications.png ├── toolbar_1000_1000_icon_problems.png ├── toolbar_1000_1000_icon_rules.png ├── toolbar_1000_1000_icon_sources.png ├── toolbar_1000_1000_icon_variable_fonts.png ├── toolbar_1000_1000_icon_variable_notifications.png ├── toolbar_1000_1000_info.bubble.fill.pdf ├── toolbar_1000_1000_info.circle.fill.pdf ├── toolbar_1000_1000_info.circle.pdf ├── toolbar_1000_1000_questionmark.circle.pdf ├── toolbar_1000_1000_square.and.arrow.down.pdf ├── toolbar_30_30_arrow.right.filled.filter.arrow.right_arrow.right.filled.filter.arrow.right.pdf ├── toolbar_30_30_atom.pdf ├── toolbar_30_30_info.bubble.fill.pdf ├── toolbar_30_30_info.circle.fill.pdf ├── toolbar_30_30_info.circle.pdf ├── toolbar_30_30_questionmark.circle.pdf ├── toolbar_30_30_square.and.arrow.down.pdf ├── toolbar_30_30_wand.and.stars.pdf ├── toolbar_500_500_info.bubble.fill.png ├── toolbar_500_500_info.circle.fill.png ├── toolbar_500_500_info.circle.png ├── toolbar_500_500_questionmark.circle.png ├── toolbar_500_500_square.and.arrow.down.png └── wand.and.stars_wand.and.stars.svg └── instancestab_mathmodel_dialog.png /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /DSEezui.py: -------------------------------------------------------------------------------- 1 | import AppKit 2 | import ezui 3 | 4 | from lib.cells.doubleClickCell import RFDoubleClickCell 5 | 6 | from mojo.extensions import getExtensionDefault, ExtensionBundle 7 | 8 | 9 | designspaceBundle = ExtensionBundle("DesignspaceEditor2") 10 | 11 | numberFormatter = AppKit.NSNumberFormatter.alloc().init() 12 | numberFormatter.setNumberStyle_(AppKit.NSNumberFormatterDecimalStyle) 13 | numberFormatter.setAllowsFloats_(True) 14 | numberFormatter.setLocalizesFormat_(False) 15 | numberFormatter.setUsesGroupingSeparator_(False) 16 | 17 | infoImage = AppKit.NSImage.imageWithSystemSymbolName_accessibilityDescription_("info.circle.fill", None) 18 | 19 | 20 | def doubleClickCell(callback, image=None): 21 | cell = RFDoubleClickCell.alloc().init() 22 | cell.setDoubleClickCallback_(callback) 23 | cell.setImage_(image) 24 | return cell 25 | 26 | 27 | class Controller(ezui.WindowController): 28 | 29 | def build(self): 30 | content = """ 31 | = ToolbarTabs 32 | * ToolbarTab: Axis @axisTab 33 | > |---| @axisTable 34 | > * HorizontalStack @axisStack 35 | >> (+-) 36 | >> (...) @axisActions 37 | 38 | * ToolbarTab: Sources @sourcesTab 39 | >|---| @sourcesTable 40 | > * HorizontalStack @sourcesStack 41 | >> (+-) 42 | >> (...) @sourcesActions 43 | 44 | * ToolbarTab: Intances @instancesTab 45 | > |---| @instancesTable 46 | > * HorizontalStack @instancesStack 47 | >> (+-) 48 | >> ( Preview Instances ) @instancesPreview 49 | >> (...) @instancesActions 50 | 51 | * ToolbarTab: Rules @rulesTab 52 | > * CodeEditor @rulesEditor 53 | 54 | * ToolbarTab: Labels @labelsTab 55 | > * CodeEditor @labelsEditor 56 | > * HorizontalStack @labelsStack 57 | >> ( Preview Labels ) @labelsPreviewButton 58 | 59 | 60 | * ToolbarTab: Problems @problemsTab 61 | > |---| @problemsTable 62 | > * HorizontalStack @problemsStack 63 | >> ( Validate Designspace ) @problemsValidateButton 64 | 65 | * ToolbarTab: Notes @notesTab 66 | > [[__]] @notesEditor 67 | """ 68 | 69 | marginDescriptions = dict(margins=(10, 0, 10, 10)) 70 | descriptionData = dict( 71 | axisTab=dict( 72 | image=designspaceBundle.getResourceImage("toolbar_30_30_icon_axes") 73 | ), 74 | axisStack=marginDescriptions, 75 | axisTable=dict( 76 | width="fill", 77 | height="fill", 78 | columnDescriptions=[ 79 | dict(title="", identifier="genericInfoButton", width=20, editable=False, cell=doubleClickCell(self.axisListDoubleClickCallback, infoImage)),#, cell=axisDoubleClickCell), 80 | dict(title="Ⓡ", identifier="axisRegisterd", width=20, allowsSorting=False, editable=False), 81 | dict(title="Name", identifier="axisName", allowsSorting=False, editable=True), 82 | dict(title="Tag", identifier="axisTag", width=70, allowsSorting=False, editable=True), 83 | dict(title="Minimum", identifier="axisMinimum", width=70, allowsSorting=False, editable=True, formatter=numberFormatter), 84 | dict(title="Default", identifier="axisDefault", width=70, allowsSorting=False, editable=True, formatter=numberFormatter), 85 | dict(title="Maximum", identifier="axisMaximum", width=70, allowsSorting=False, editable=True, formatter=numberFormatter), 86 | dict(title="Discrete Values", identifier="axisDiscreteValues", width=100, allowsSorting=False, editable=True), 87 | 88 | dict(title="Hidden", identifier="axisHidden", width=50, cellType="Checkbox", allowsSorting=False, editable=True), 89 | dict(title="📈", identifier="axisHasMap", width=20, allowsSorting=False, editable=False), 90 | dict(title="🏷️", identifier="axisHasLabels", width=20, allowsSorting=False, editable=False), 91 | ] 92 | ), 93 | axisActions=dict( 94 | itemDescriptions=[ 95 | dict(identifier="axisAddWeightAxis", text="Add Weight Axis"), 96 | dict(identifier="axisAddWidthAxis", text="Add Width Axis"), 97 | dict(identifier="axisAddOpticalAxis", text="Add Optical Axis"), 98 | ] 99 | ), 100 | 101 | sourcesTab=dict( 102 | image=designspaceBundle.getResourceImage("toolbar_30_30_icon_sources") 103 | ), 104 | sourcesStack=marginDescriptions, 105 | sourcesTable=dict( 106 | width="fill", 107 | height="fill", 108 | columnDescriptions=[ 109 | dict(title="", identifier="genericInfoButton", width=20, editable=False, cell=doubleClickCell(self.sourceListDoubleClickCallback, infoImage)), 110 | dict(title="💾", identifier="sourceHasPath", width=20, editable=False), 111 | dict(title="📍", identifier="sourceIsDefault", width=20, editable=False), 112 | dict(title="UFO", identifier="sourceUFOFileName", width=200, minWidth=100, maxWidth=350, editable=False), 113 | dict(title="Family Name", identifier="sourceFamilyName", editable=True, width=130, minWidth=130, maxWidth=250), 114 | dict(title="Style Name", identifier="sourceStyleName", editable=True, width=130, minWidth=130, maxWidth=250), 115 | dict(title="Layer Name", identifier="sourceLayerName", editable=True, width=130, minWidth=130, maxWidth=250), 116 | dict(title="🌐", identifier="sourceHasLocalisedFamilyNames", width=20, allowsSorting=False, editable=False), 117 | dict(title="🔕", identifier="sourceHasMutedGlyphs", width=20, allowsSorting=False, editable=False), 118 | ] 119 | ), 120 | sourcesActions=dict( 121 | itemDescriptions=[ 122 | dict(identifier="basicItem", text="Open source UFO"), 123 | "----", 124 | dict(identifier="basicItem", text="Add Open UFOs"), 125 | dict(identifier="basicItem", text="Replace UFO"), 126 | ] 127 | ), 128 | 129 | instancesTab=dict( 130 | image=designspaceBundle.getResourceImage("toolbar_30_30_icon_instances") 131 | ), 132 | instancesStack=marginDescriptions, 133 | instancesTable=dict( 134 | width="fill", 135 | height="fill", 136 | columnDescriptions=[ 137 | dict(title="UFO", identifier="instanceUFOFileName", width=200, minWidth=100, maxWidth=350, editable=False), 138 | dict(title="Family Name", identifier="instanceFamilyName", editable=True, width=130, minWidth=130, maxWidth=250), 139 | dict(title="Style Name", identifier="instanceStyleName", editable=True, width=130, minWidth=130, maxWidth=250), 140 | ] 141 | ), 142 | instancesActions=dict( 143 | itemDescriptions=[ 144 | dict(identifier="basicItem", text="Duplicate Instance"), 145 | dict(identifier="basicItem", text="Add Sources as Instances"), 146 | "----", 147 | dict(identifier="basicItem", text="Generate With MutatorMath"), 148 | dict(identifier="basicItem", text="Generate With VarLib"), 149 | ] 150 | ), 151 | 152 | rulesTab=dict( 153 | image=designspaceBundle.getResourceImage("toolbar_30_30_icon_rules") 154 | ), 155 | rulesEditor=dict( 156 | width="fill", 157 | height="fill", 158 | showLineNumbers=False 159 | ), 160 | 161 | labelsTab=dict( 162 | image=designspaceBundle.getResourceImage("toolbar_30_30_icon_labels") 163 | ), 164 | labelsStack=marginDescriptions, 165 | labelsEditor=dict( 166 | width="fill", 167 | height="fill", 168 | showLineNumbers=False 169 | ), 170 | labelsActions=dict( 171 | itemDescriptions=[ 172 | dict(identifier="basicItem", text="Preview Labels"), 173 | ] 174 | ), 175 | 176 | problemsTab=dict( 177 | image=designspaceBundle.getResourceImage("toolbar_30_30_icon_problems") 178 | ), 179 | problemsStack=marginDescriptions, 180 | problemsTable=dict( 181 | width="fill", 182 | height="fill", 183 | columnDescriptions = [ 184 | dict(title="", identifier="problemIcon", width=20), 185 | dict(title="Where", identifier="problemClass", width=130), 186 | dict(title="What", identifier="problemDescription", minWidth=200, width=200, maxWidth=1000), 187 | dict(title="Specifically", identifier="problemData", minWidth=200, width=200, maxWidth=1000), 188 | ] 189 | ), 190 | 191 | notesTab=dict( 192 | image=designspaceBundle.getResourceImage("toolbar_30_30_icon_notes") 193 | ), 194 | notesEditor=dict( 195 | width="fill", 196 | height="fill", 197 | ), 198 | 199 | 200 | ) 201 | self.w = ezui.EZWindow( 202 | title="Title", 203 | content=content, 204 | descriptionData=descriptionData, 205 | size=(800, 500), 206 | minSize=(800, 500), 207 | controller=self, 208 | margins=0 209 | ) 210 | 211 | self.w.addToolbarItem(dict(itemIdentifier=AppKit.NSToolbarSpaceItemIdentifier)) 212 | self.w.addToolbarItem(dict( 213 | itemIdentifier="save", 214 | label="Save", 215 | imageObject=ezui.makeImage(symbolName="square.and.arrow.down", symbolConfiguration=dict(renderingMode="hierarchical", colors=[(1, 0, 1, 1), ])), 216 | callback=self.toobarSaveCallback 217 | )) 218 | self.w.addToolbarItem(dict( 219 | itemIdentifier="help", 220 | label="Help", 221 | imageObject=ezui.makeImage(symbolName="questionmark.circle", symbolConfiguration=dict(renderingMode="hierarchical", colors=[(1, 0, 1, 1), ])), 222 | callback=self.toolbarHelpCallback 223 | )) 224 | 225 | def started(self): 226 | self.w.open() 227 | 228 | # axis 229 | 230 | def axisListDoubleClickCallback(self, sender): 231 | print("axisListDoubleClickCallback") 232 | 233 | def axisAddWeightAxisCallback(self, sender): 234 | print("axisAddWeightAxisCallback") 235 | 236 | def axisAddWidthAxisCallback(self, sender): 237 | print("axisAddWidthAxisCallback") 238 | 239 | def axisAddOpticalAxisCallback(self, sender): 240 | print("axisAddOpticalAxisCallback") 241 | 242 | # sources 243 | 244 | def sourceListDoubleClickCallback(self, sender): 245 | print("sourceListDoubleClickCallback") 246 | 247 | # instances 248 | 249 | 250 | # toolbar 251 | 252 | def toobarSaveCallback(self, sender): 253 | print("save") 254 | 255 | def toolbarHelpCallback(self, sender): 256 | print("help") 257 | 258 | 259 | Controller() 260 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/chart.bar.doc.horizontal2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/chart.bar.doc.horizontal2x.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/previewWindow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/previewWindow.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/previewWindow_options2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/previewWindow_options2.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/questionmark.circle2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/questionmark.circle2x.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_axestab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_axestab.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_axestab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_axestab.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_instancestab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_instancestab.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_previewwindow1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_previewwindow1.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_problemstab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_problemstab.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_rulestab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_rulestab.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_sourcestab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_sourcestab.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_variablefontstab.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/screenshot_mutatorsans_variablefontstab.jpg -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/sparse_master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/sparse_master.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/square.and.arrow.down2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/square.and.arrow.down2x.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/styles.css: -------------------------------------------------------------------------------- 1 | html { 2 | max-width: 800px; 3 | padding: 15px; 4 | margin-left: auto; 5 | margin-right: auto; 6 | } 7 | body { 8 | -ms-text-size-adjust: 100%; 9 | -webkit-text-size-adjust: 100%; 10 | line-height: 1.5; 11 | color: #333; 12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 13 | font-size: 16px; 14 | line-height: 1.5; 15 | word-wrap: break-word; 16 | } 17 | .smallicon{ 18 | width: 80pt; 19 | padding: 20pt; 20 | font-size: 10pt; 21 | margin-left: auto; 22 | margin-right: auto; 23 | } 24 | .bigicon{ 25 | margin-top: 80pt; 26 | } 27 | .caption{ 28 | font-size:10pt; 29 | } 30 | body .pl-c { 31 | color: #969896; 32 | } 33 | body .pl-c1, 34 | body .pl-s .pl-v { 35 | color: #0086b3; 36 | } 37 | body .pl-e, 38 | body .pl-en { 39 | color: #795da3; 40 | } 41 | body .pl-s .pl-s1, 42 | body .pl-smi { 43 | color: #333; 44 | } 45 | body .pl-ent { 46 | color: #63a35c; 47 | } 48 | body .pl-k { 49 | color: #a71d5d; 50 | } 51 | body .pl-pds, 52 | body .pl-s, 53 | body .pl-s .pl-pse .pl-s1, 54 | body .pl-sr, 55 | body .pl-sr .pl-cce, 56 | body .pl-sr .pl-sra, 57 | body .pl-sr .pl-sre { 58 | color: #183691; 59 | } 60 | body .pl-v { 61 | color: #ed6a43; 62 | } 63 | body .pl-id { 64 | color: #b52a1d; 65 | } 66 | body .pl-ii { 67 | color: #f8f8f8; 68 | background-color: #b52a1d; 69 | } 70 | body .pl-sr .pl-cce { 71 | font-weight: 700; 72 | color: #63a35c; 73 | } 74 | body .pl-ml { 75 | color: #693a17; 76 | } 77 | body .pl-mh, 78 | body .pl-mh .pl-en, 79 | body .pl-ms { 80 | font-weight: 700; 81 | color: #1d3e81; 82 | } 83 | body .pl-mq { 84 | color: teal; 85 | } 86 | body .pl-mi { 87 | font-style: italic; 88 | color: #333; 89 | } 90 | body .pl-mb { 91 | font-weight: 700; 92 | color: #333; 93 | } 94 | body .pl-md { 95 | color: #bd2c00; 96 | background-color: #ffecec; 97 | } 98 | body .pl-mi1 { 99 | color: #55a532; 100 | background-color: #eaffea; 101 | } 102 | body .pl-mdr { 103 | font-weight: 700; 104 | color: #795da3; 105 | } 106 | body .pl-mo { 107 | color: #1d3e81; 108 | } 109 | body .octicon { 110 | display: inline-block; 111 | vertical-align: text-top; 112 | fill: currentColor; 113 | } 114 | body a { 115 | background-color: transparent; 116 | -webkit-text-decoration-skip: objects; 117 | } 118 | body a:active, 119 | body a:hover { 120 | outline-width: 0; 121 | } 122 | body strong { 123 | font-weight: inherit; 124 | } 125 | body strong { 126 | font-weight: bolder; 127 | } 128 | body h1 { 129 | font-size: 2em; 130 | margin: 0.67em 0; 131 | } 132 | body img { 133 | border-style: none; 134 | } 135 | body svg:not(:root) { 136 | overflow: hidden; 137 | } 138 | body code, 139 | body kbd, 140 | body pre { 141 | font-family: monospace, monospace; 142 | font-size: 1em; 143 | } 144 | body hr { 145 | box-sizing: content-box; 146 | height: 0; 147 | overflow: visible; 148 | } 149 | body input { 150 | font: inherit; 151 | margin: 0; 152 | } 153 | body input { 154 | overflow: visible; 155 | } 156 | body [type="checkbox"] { 157 | box-sizing: border-box; 158 | padding: 0; 159 | } 160 | body * { 161 | box-sizing: border-box; 162 | } 163 | body input { 164 | font-family: inherit; 165 | font-size: inherit; 166 | line-height: inherit; 167 | } 168 | body a { 169 | color: #4078c0; 170 | text-decoration: none; 171 | } 172 | body a:active, 173 | body a:hover { 174 | text-decoration: underline; 175 | } 176 | body strong { 177 | font-weight: 600; 178 | } 179 | body hr { 180 | height: 0; 181 | margin: 15px 0; 182 | overflow: hidden; 183 | background: 0 0; 184 | border: 0; 185 | border-bottom: 1px solid #ddd; 186 | } 187 | body hr::before { 188 | display: table; 189 | content: ""; 190 | } 191 | body hr::after { 192 | display: table; 193 | clear: both; 194 | content: ""; 195 | } 196 | body table { 197 | border-spacing: 0; 198 | border-collapse: collapse; 199 | } 200 | body td, 201 | body th { 202 | padding: 0; 203 | } 204 | body h1, 205 | body h2, 206 | body h3, 207 | body h4, 208 | body h5, 209 | body h6 { 210 | margin-top: 0; 211 | margin-bottom: 0; 212 | } 213 | body h1 { 214 | font-size: 32px; 215 | font-weight: 600; 216 | } 217 | body h2 { 218 | font-size: 24px; 219 | font-weight: 600; 220 | } 221 | body h3 { 222 | font-size: 20px; 223 | font-weight: 600; 224 | } 225 | body h4 { 226 | font-size: 16px; 227 | font-weight: 600; 228 | } 229 | body h5 { 230 | font-size: 14px; 231 | font-weight: 600; 232 | } 233 | body h6 { 234 | font-size: 12px; 235 | font-weight: 600; 236 | } 237 | body p { 238 | margin-top: 0; 239 | margin-bottom: 10px; 240 | } 241 | body blockquote { 242 | margin: 0; 243 | } 244 | body ol, 245 | body ul { 246 | padding-left: 0; 247 | margin-top: 0; 248 | margin-bottom: 0; 249 | } 250 | body ol ol, 251 | body ul ol { 252 | list-style-type: lower-roman; 253 | } 254 | body ol ol ol, 255 | body ol ul ol, 256 | body ul ol ol, 257 | body ul ul ol { 258 | list-style-type: lower-alpha; 259 | } 260 | body dd { 261 | margin-left: 0; 262 | } 263 | body code { 264 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 265 | font-size: 12px; 266 | } 267 | body pre { 268 | margin-top: 0; 269 | margin-bottom: 0; 270 | font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace; 271 | } 272 | body .octicon { 273 | vertical-align: text-bottom; 274 | } 275 | body input { 276 | -webkit-font-feature-settings: "liga" 0; 277 | font-feature-settings: "liga" 0; 278 | } 279 | body::before { 280 | display: table; 281 | content: ""; 282 | } 283 | body::after { 284 | display: table; 285 | clear: both; 286 | content: ""; 287 | } 288 | body > :first-child { 289 | margin-top: 0 !important; 290 | } 291 | body > :last-child { 292 | margin-bottom: 0 !important; 293 | } 294 | body a:not([href]) { 295 | color: inherit; 296 | text-decoration: none; 297 | } 298 | body .anchor { 299 | float: left; 300 | padding-right: 4px; 301 | margin-left: -20px; 302 | line-height: 1; 303 | } 304 | body .anchor:focus { 305 | outline: 0; 306 | } 307 | body blockquote, 308 | body dl, 309 | body ol, 310 | body p, 311 | body pre, 312 | body table, 313 | body ul { 314 | margin-top: 0; 315 | margin-bottom: 16px; 316 | } 317 | body hr { 318 | height: 0.25em; 319 | padding: 0; 320 | margin: 24px 0; 321 | background-color: #e7e7e7; 322 | border: 0; 323 | } 324 | body blockquote { 325 | padding: 0 1em; 326 | color: #777; 327 | border-left: 0.25em solid #ddd; 328 | } 329 | body blockquote > :first-child { 330 | margin-top: 0; 331 | } 332 | body blockquote > :last-child { 333 | margin-bottom: 0; 334 | } 335 | body kbd { 336 | display: inline-block; 337 | padding: 3px 5px; 338 | font-size: 11px; 339 | line-height: 10px; 340 | color: #555; 341 | vertical-align: middle; 342 | background-color: #fcfcfc; 343 | border: solid 1px #ccc; 344 | border-bottom-color: #bbb; 345 | border-radius: 3px; 346 | box-shadow: inset 0 -1px 0 #bbb; 347 | } 348 | body h1, 349 | body h2, 350 | body h3, 351 | body h4, 352 | body h5, 353 | body h6 { 354 | margin-top: 30pt; 355 | margin-bottom: 16px; 356 | font-weight: 600; 357 | line-height: 1.25; 358 | } 359 | body h1 .octicon-link, 360 | body h2 .octicon-link, 361 | body h3 .octicon-link, 362 | body h4 .octicon-link, 363 | body h5 .octicon-link, 364 | body h6 .octicon-link { 365 | color: #000; 366 | vertical-align: middle; 367 | visibility: hidden; 368 | } 369 | body h1:hover .anchor, 370 | body h2:hover .anchor, 371 | body h3:hover .anchor, 372 | body h4:hover .anchor, 373 | body h5:hover .anchor, 374 | body h6:hover .anchor { 375 | text-decoration: none; 376 | } 377 | body h1:hover .anchor .octicon-link, 378 | body h2:hover .anchor .octicon-link, 379 | body h3:hover .anchor .octicon-link, 380 | body h4:hover .anchor .octicon-link, 381 | body h5:hover .anchor .octicon-link, 382 | body h6:hover .anchor .octicon-link { 383 | visibility: visible; 384 | } 385 | body h1 { 386 | padding-bottom: 0.3em; 387 | font-size: 2em; 388 | border-bottom: 1px solid #eee; 389 | } 390 | body h2 { 391 | padding-bottom: 0.3em; 392 | font-size: 1.5em; 393 | border-bottom: 1px solid #eee; 394 | } 395 | body h3 { 396 | font-size: 1.25em; 397 | } 398 | body h4 { 399 | font-size: 1em; 400 | } 401 | body h5 { 402 | font-size: 0.875em; 403 | } 404 | body h6 { 405 | font-size: 0.85em; 406 | color: #777; 407 | } 408 | body ol, 409 | body ul { 410 | padding-left: 2em; 411 | } 412 | body ol ol, 413 | body ol ul, 414 | body ul ol, 415 | body ul ul { 416 | margin-top: 0; 417 | margin-bottom: 0; 418 | } 419 | body li > p { 420 | margin-top: 16px; 421 | } 422 | body li + li { 423 | margin-top: 0.25em; 424 | } 425 | body dl { 426 | padding: 0; 427 | } 428 | body dl dt { 429 | padding: 0; 430 | margin-top: 16px; 431 | font-size: 1em; 432 | font-style: italic; 433 | font-weight: 700; 434 | } 435 | body dl dd { 436 | padding: 0 16px; 437 | margin-bottom: 16px; 438 | } 439 | body table { 440 | display: block; 441 | width: 100%; 442 | overflow: auto; 443 | } 444 | body table th { 445 | font-weight: 700; 446 | } 447 | body table td, 448 | body table th { 449 | padding: 6px 13px; 450 | border: 1px solid #ddd; 451 | } 452 | body table tr { 453 | background-color: #fff; 454 | border-top: 1px solid #ccc; 455 | } 456 | body table tr:nth-child(2n) { 457 | background-color: #f8f8f8; 458 | } 459 | body img { 460 | max-width: 100%; 461 | box-sizing: content-box; 462 | background-color: #fff; 463 | } 464 | body code { 465 | padding: 0; 466 | padding-top: 0.2em; 467 | padding-bottom: 0.2em; 468 | margin: 0; 469 | font-size: 85%; 470 | background-color: rgba(0, 0, 0, 0.04); 471 | border-radius: 3px; 472 | } 473 | body pre { 474 | word-wrap: normal; 475 | } 476 | body pre > code { 477 | padding: 0; 478 | margin: 0; 479 | font-size: 100%; 480 | word-break: normal; 481 | white-space: pre; 482 | background: 0 0; 483 | border: 0; 484 | } 485 | body .highlight { 486 | margin-bottom: 16px; 487 | } 488 | body .highlight pre { 489 | margin-bottom: 0; 490 | word-break: normal; 491 | } 492 | body .highlight pre, 493 | body pre { 494 | padding: 16px; 495 | overflow: auto; 496 | font-size: 85%; 497 | line-height: 1.45; 498 | background-color: #f7f7f7; 499 | border-radius: 3px; 500 | } 501 | body pre code { 502 | display: inline; 503 | max-width: auto; 504 | padding: 0; 505 | margin: 0; 506 | overflow: visible; 507 | line-height: inherit; 508 | word-wrap: normal; 509 | background-color: transparent; 510 | border: 0; 511 | } 512 | body pre code::after, 513 | body pre code::before { 514 | content: normal; 515 | } 516 | body .pl-0 { 517 | padding-left: 0 !important; 518 | } 519 | body .pl-1 { 520 | padding-left: 3px !important; 521 | } 522 | body .pl-2 { 523 | padding-left: 6px !important; 524 | } 525 | body .pl-3 { 526 | padding-left: 12px !important; 527 | } 528 | body .pl-4 { 529 | padding-left: 24px !important; 530 | } 531 | body .pl-5 { 532 | padding-left: 36px !important; 533 | } 534 | body .pl-6 { 535 | padding-left: 48px !important; 536 | } 537 | body .full-commit .btn-outline:not(:disabled):hover { 538 | color: #4078c0; 539 | border: 1px solid #4078c0; 540 | } 541 | body kbd { 542 | display: inline-block; 543 | padding: 3px 5px; 544 | font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace; 545 | line-height: 10px; 546 | color: #555; 547 | vertical-align: middle; 548 | background-color: #fcfcfc; 549 | border: solid 1px #ccc; 550 | border-bottom-color: #bbb; 551 | border-radius: 3px; 552 | box-shadow: inset 0 -1px 0 #bbb; 553 | } 554 | body :checked + .radio-label { 555 | position: relative; 556 | z-index: 1; 557 | border-color: #4078c0; 558 | } 559 | body .task-list-item { 560 | list-style-type: none; 561 | } 562 | body .task-list-item + .task-list-item { 563 | margin-top: 3px; 564 | } 565 | body .task-list-item input { 566 | margin: 0 0.2em 0.25em -1.6em; 567 | vertical-align: middle; 568 | } 569 | body hr { 570 | border-bottom-color: #eee; 571 | } 572 | .codehilite .c { 573 | color: #999; 574 | } 575 | .codehilite .err { 576 | color: red; 577 | } 578 | .codehilite .g { 579 | color: #363636; 580 | } 581 | .codehilite .k { 582 | color: #4998ff; 583 | } 584 | .codehilite .l { 585 | color: #93a1a1; 586 | } 587 | .codehilite .n { 588 | color: #363636; 589 | } 590 | .codehilite .o { 591 | color: #aa25ff; 592 | } 593 | .codehilite .x { 594 | color: #cb4b16; 595 | } 596 | .codehilite .p { 597 | color: #93a1a1; 598 | } 599 | .codehilite .cm { 600 | color: #586e75; 601 | } 602 | .codehilite .cp { 603 | color: #aa25ff; 604 | } 605 | .codehilite .c1 { 606 | color: #586e75; 607 | } 608 | .codehilite .cs { 609 | color: #aa25ff; 610 | } 611 | .codehilite .gd { 612 | color: #2aa198; 613 | } 614 | .codehilite .ge { 615 | color: #93a1a1; 616 | font-style: italic; 617 | } 618 | .codehilite .gr { 619 | color: #dc322f; 620 | } 621 | .codehilite .gh { 622 | color: #cb4b16; 623 | } 624 | .codehilite .gi { 625 | color: #aa25ff; 626 | } 627 | .codehilite .go { 628 | color: #93a1a1; 629 | } 630 | .codehilite .gp { 631 | color: #93a1a1; 632 | } 633 | .codehilite .gs { 634 | color: #93a1a1; 635 | font-weight: 700; 636 | } 637 | .codehilite .gu { 638 | color: #cb4b16; 639 | } 640 | .codehilite .gt { 641 | color: #93a1a1; 642 | } 643 | .codehilite .kc { 644 | color: #cb4b16; 645 | } 646 | .codehilite .kd { 647 | color: #268bd2; 648 | } 649 | .codehilite .kn { 650 | color: #4998ff; 651 | } 652 | .codehilite .kp { 653 | color: #aa25ff; 654 | } 655 | .codehilite .kr { 656 | color: #268bd2; 657 | } 658 | .codehilite .kt { 659 | color: #dc322f; 660 | } 661 | .codehilite .ld { 662 | color: #c42f07; 663 | } 664 | .codehilite .m { 665 | color: #c42f07; 666 | } 667 | .codehilite .s { 668 | color: #ff05da; 669 | } 670 | .codehilite .na { 671 | color: #93a1a1; 672 | } 673 | .codehilite .nb { 674 | color: #0bd51e; 675 | } 676 | .codehilite .nc { 677 | color: #ff3ca8; 678 | } 679 | .codehilite .no { 680 | color: #ff3ca8; 681 | } 682 | .codehilite .nd { 683 | color: #ff3ca8; 684 | } 685 | .codehilite .ni { 686 | color: #ff3ca8; 687 | } 688 | .codehilite .ne { 689 | color: #ff3ca8; 690 | } 691 | .codehilite .nf { 692 | color: #ff3ca8; 693 | } 694 | .codehilite .nl { 695 | color: #ff3ca8; 696 | } 697 | .codehilite .nn { 698 | color: #354980; 699 | } 700 | .codehilite .nx { 701 | color: #555; 702 | } 703 | .codehilite .py { 704 | color: #93a1a1; 705 | } 706 | .codehilite .nt { 707 | color: #268bd2; 708 | } 709 | .codehilite .nv { 710 | color: #268bd2; 711 | } 712 | .codehilite .ow { 713 | color: #aa25ff; 714 | } 715 | .codehilite .w { 716 | color: #93a1a1; 717 | } 718 | .codehilite .mf { 719 | color: #c42f07; 720 | } 721 | .codehilite .mh { 722 | color: #c42f07; 723 | } 724 | .codehilite .mi { 725 | color: #c42f07; 726 | } 727 | .codehilite .mo { 728 | color: #c42f07; 729 | } 730 | .codehilite .sb { 731 | color: #ff05da; 732 | } 733 | .codehilite .sc { 734 | color: #ff05da; 735 | } 736 | .codehilite .sd { 737 | color: #ff05da; 738 | } 739 | .codehilite .s2 { 740 | color: #ff05da; 741 | } 742 | .codehilite .se { 743 | color: #ff05da; 744 | } 745 | .codehilite .sh { 746 | color: #ff05da; 747 | } 748 | .codehilite .si { 749 | color: #ff05da; 750 | } 751 | .codehilite .sx { 752 | color: #ff05da; 753 | } 754 | .codehilite .sr { 755 | color: #ff05da; 756 | } 757 | .codehilite .s1 { 758 | color: #ff05da; 759 | } 760 | .codehilite .ss { 761 | color: #ff05da; 762 | } 763 | .codehilite .bp { 764 | color: #f29108; 765 | } 766 | .codehilite .vc { 767 | color: #268bd2; 768 | } 769 | .codehilite .vg { 770 | color: #268bd2; 771 | } 772 | .codehilite .vi { 773 | color: #268bd2; 774 | } 775 | .codehilite .il { 776 | color: #c42f07; 777 | } 778 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_25_25_info.circle.fill.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_axes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_axes.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_instances.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_instances.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_location_labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_location_labels.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_notes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_notes.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_notifications.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_notifications.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_problems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_problems.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_rules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_rules.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_save.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_settings.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_sources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_sources.png -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_variable_fonts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/html/toolbar_500_500_icon_variable_fonts.png -------------------------------------------------------------------------------- /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 28 | 29 | 30 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/__init__.py: -------------------------------------------------------------------------------- 1 | extensionIdentifier = "com.letterror.designspaceEditor" -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/locationPreview.py: -------------------------------------------------------------------------------- 1 | from operator import itemgetter 2 | 3 | import vanilla 4 | import ezui 5 | 6 | import mojo.drawingTools as ctx 7 | 8 | from mojo.UI import MultiLineView, splitText, GlyphRecord, StatusBar 9 | from mojo.extensions import getExtensionDefault, setExtensionDefault 10 | from mojo.subscriber import WindowController, Subscriber 11 | from mojo.events import addObserver, removeObserver 12 | from mojo.roboFont import RFont, internalFontClasses, CurrentFont, CurrentGlyph 13 | 14 | from designspaceEditor.tools import UseVarLib, symbolImage 15 | from designspaceEditor.parsers.parserTools import numberToString 16 | 17 | from mutatorMath import Location 18 | 19 | from designspaceEditor import extensionIdentifier 20 | 21 | 22 | skateboardPreviewTextLibKey = "com.letterror.skateboard.previewText" 23 | previewTextLibKey = f"{extensionIdentifier}.previewText" 24 | 25 | inverseDefaultKey = f"{extensionIdentifier}.inverse" 26 | singleLineDefaultKey = f"{extensionIdentifier}.singleLine" 27 | showSourcesDefaultKey = f"{extensionIdentifier}.showSources" 28 | showInstancesDefaultKey = f"{extensionIdentifier}.showInstances" 29 | shouldSortByDefaultKey = f"{extensionIdentifier}.shouldSortBy" 30 | 31 | 32 | previewLocationButtonImage = symbolImage("mappin.and.ellipse", "primary") 33 | 34 | indicatorImageMap = dict( 35 | source=symbolImage("smallcircle.filled.circle.fill", (1, .5, 0, 1)), 36 | instance=symbolImage("smallcircle.filled.circle", (0, 0, 1, 1)), 37 | previewLocation=symbolImage("mappin.and.ellipse", (1, 0, 0, 1), flipped=True) 38 | ) 39 | 40 | 41 | class PreviewLocationFinder(ezui.WindowController): 42 | 43 | def build(self, parent, operator, previewLocation): 44 | self.operator = operator 45 | content = """ 46 | = TwoColumnForm 47 | : 48 | [ ] Show Preview Location @showPreviewLocation 49 | """ 50 | descriptionData = dict( 51 | content=dict( 52 | itemColumnWidth=200 53 | ), 54 | showPreviewLocation=dict( 55 | value=bool(previewLocation) 56 | ) 57 | ) 58 | 59 | for axisDescriptor in operator.axes: 60 | value = axisDescriptor.default 61 | if previewLocation: 62 | value = previewLocation.get(axisDescriptor.name, value) 63 | 64 | if hasattr(axisDescriptor, "values"): 65 | # discrete axis 66 | content += f""" 67 | : {axisDescriptor.name}: 68 | ( ...) @{axisDescriptor.name} 69 | """ 70 | descriptionData[axisDescriptor.name] = dict( 71 | items=[numberToString(value) for value in axisDescriptor.values] 72 | ) 73 | else: 74 | content += f""" 75 | : {axisDescriptor.name}: 76 | ---X--- [__] @{axisDescriptor.name} 77 | """ 78 | minimum, default, maximum = operator.getAxisExtremes(axisDescriptor) 79 | descriptionData[axisDescriptor.name] = dict( 80 | minValue=minimum, 81 | maxValue=maximum, 82 | value=value 83 | ) 84 | 85 | content += """ 86 | ===== 87 | ( Add as Instance ) @addAsInstance 88 | """ 89 | self.w = ezui.EZPopover( 90 | content=content, 91 | descriptionData=descriptionData, 92 | parent=parent, 93 | parentAlignment="bottom", 94 | behavior="transient", 95 | size="auto", 96 | controller=self 97 | ) 98 | 99 | def started(self): 100 | self.w.open() 101 | 102 | def destroy(self): 103 | self.operator = None 104 | 105 | def getLocation(self): 106 | location = self.w.getItemValues() 107 | for axisDescriptor in self.operator.axes: 108 | if hasattr(axisDescriptor, "values"): 109 | index = location[axisDescriptor.name] 110 | location[axisDescriptor.name] = axisDescriptor.values[index] 111 | del location["showPreviewLocation"] 112 | return location 113 | 114 | def contentCallback(self, sender): 115 | if self.w.getItemValue("showPreviewLocation"): 116 | self.operator.setPreviewLocation(location=self.getLocation()) 117 | 118 | def addAsInstanceCallback(self, sender): 119 | self.operator.addInstanceDescriptor( 120 | designLocation=self.getLocation() 121 | ) 122 | 123 | def showPreviewLocationCallback(self, sender): 124 | if sender.get(): 125 | self.operator.setPreviewLocation(location=self.getLocation()) 126 | else: 127 | self.operator.setPreviewLocation(location=None) 128 | 129 | 130 | class PreviewInstance: 131 | 132 | flavor = "previewLocation" 133 | 134 | def __init__(self, designLocation): 135 | self.designLocation = designLocation 136 | 137 | def getFullDesignLocation(self, doc): 138 | return self.designLocation 139 | 140 | 141 | class LocationPreview(Subscriber, WindowController): 142 | 143 | debug = True 144 | 145 | def build(self, operator=None, selectedSources=None, selectedInstances=None, previewString=None): 146 | self.operator = operator 147 | 148 | self.dummyFont = RFont(showInterface=False) 149 | 150 | upms = set() 151 | for font in self.operator.fonts.values(): 152 | if font is not None: 153 | upms.add(font.info.unitsPerEm) 154 | 155 | with UseVarLib(self.operator, useVarLib=False): 156 | for instance in self.operator.instances: 157 | #continuousLocation, discreteLocation = self.operator.splitLocation(instance.location) 158 | #infoMutator = self.operator.getInfoMutator(discreteLocation) 159 | #info = infoMutator.makeInstance(continuousLocation) 160 | info = self.operator.makeOneInfo(instance.getFullDesignLocation(self.operator)) 161 | upms.add(info.unitsPerEm) 162 | 163 | self.dummyFont.info.unitsPerEm = max(upms) if upms else 1000 164 | 165 | self.displayPrefs = {} 166 | self.displayPrefs['Inverse'] = getExtensionDefault(inverseDefaultKey, False) 167 | self.displayPrefs['Beam'] = False 168 | self.displayPrefs['displayMode'] = "Single Line" if getExtensionDefault(singleLineDefaultKey, False) else "Multi Line" 169 | self.displayPrefs['Stroke'] = False 170 | self.displayPrefs['Fill'] = True 171 | 172 | self.shouldShowSources = getExtensionDefault(showSourcesDefaultKey, False) 173 | self.shouldShowInstances = getExtensionDefault(showInstancesDefaultKey, True) 174 | self.shouldShowPreviewLocation = operator.getPreviewLocation() 175 | 176 | self.shouldSortBy = set(getExtensionDefault(shouldSortByDefaultKey, [])) 177 | 178 | self.w = vanilla.FloatingWindow((700, 400), "Location Preview", minSize=(500, 300)) 179 | self.w.input = vanilla.EditText((10, 10, -80, 22), callback=self.inputCallback) 180 | self.w.options = vanilla.ActionButton( 181 | (-80, 10, 30, 22), 182 | [ 183 | dict(title="Single Line", callback=self.singleLineMenuItemCallback, state=self.displayPrefs['displayMode'] == "Single Line"), 184 | dict(title="Invert", callback=self.invertMenuItemCallback, state=self.displayPrefs['Inverse']), 185 | "----", 186 | dict(title="Show Sources", callback=self.showSourcesMenuItemCallback, state=self.shouldShowSources), 187 | dict(title="Show Instances", callback=self.showInstancesMenuItemCallback, state=self.shouldShowInstances), 188 | "----", 189 | dict(title="Sort Line by Area", callback=self.sortByLineAreaMenuItemCallback, state="area" in self.shouldSortBy), 190 | dict(title="Sort Line by Length", callback=self.sortByLineLengthMenuItemCallback, state="length" in self.shouldSortBy), 191 | dict(title="Sort Line by Density", callback=self.sortByLineDensityMenuItemCallback, state="density" in self.shouldSortBy), 192 | ], 193 | bordered=False 194 | ) 195 | self.w.currentLocation = vanilla.Button((-40, 10, 30, 22), "", callback=self.currentLocationCallback) 196 | self.w.currentLocation.getNSButton().setImage_(previewLocationButtonImage) 197 | self.w.hl = vanilla.HorizontalLine((0, 41, 0, 1)) 198 | self.w.preview = MultiLineView( 199 | (0, 42, 0, -20), 200 | pointSize=60, 201 | displayOptions=self.displayPrefs, 202 | selectionCallback=self.previewSelectionCallback 203 | ) 204 | self.w.infoText = StatusBar((0, -20, -0, 20)) 205 | self.w.preview.setFont(self.dummyFont) 206 | 207 | self.selectedSources = selectedSources or [] 208 | self.selectedInstances = selectedInstances or [] 209 | self.previewLocation = None 210 | 211 | if previewString is None: 212 | # check if there is an old skateboard previewtext 213 | if skateboardPreviewTextLibKey in self.operator.lib: 214 | previewString = self.operator.lib[previewTextLibKey] = self.operator.lib[skateboardPreviewTextLibKey] 215 | del self.operator.lib[skateboardPreviewTextLibKey] 216 | else: 217 | previewString = self.operator.lib.get(previewTextLibKey, "Abc") 218 | addObserver(self, "locationPreviewLineViewDidDrawGlyph", "spaceCenterDraw") 219 | self.setPreviewString(previewString) 220 | 221 | def started(self): 222 | self.w.open() 223 | 224 | def destroy(self): 225 | removeObserver(self, "spaceCenterDraw") 226 | self.operator = None 227 | self.selectedInstances = None 228 | self.selectedSources = None 229 | 230 | def setPreviewString(self, value): 231 | self.w.input.set(value) 232 | self.updatePreview() 233 | 234 | def updatePreview(self): 235 | self.inputCallback(self.w.input) 236 | self.populateInfoStatusBar() 237 | 238 | def inputCallback(self, sender): 239 | previewString = sender.get() 240 | self.operator.lib[previewTextLibKey] = previewString 241 | glyphNames = [] 242 | for glyphName in splitText(previewString, self.operator.getCharacterMapping()): 243 | if glyphName == "/?": 244 | currentGlyph = CurrentGlyph() 245 | if currentGlyph is not None: 246 | glyphNames.append(currentGlyph.name) 247 | elif glyphName == "/!": 248 | currentFont = CurrentFont() 249 | if currentFont is not None: 250 | glyphNames.extend(currentFont.selectedGlyphNames) 251 | else: 252 | glyphNames.append(glyphName) 253 | 254 | possibleKerningPairs = ((side1, side2) for side1, side2 in zip(glyphNames[:-1], glyphNames[1:])) 255 | possibleKerningPairs = list(possibleKerningPairs) 256 | 257 | selectedDescriptors = [] 258 | if self.shouldShowSources: 259 | if self.selectedSources: 260 | selectedDescriptors.extend(self.selectedSources) 261 | else: 262 | selectedDescriptors.extend(self.operator.sources) 263 | 264 | if self.shouldShowInstances: 265 | if self.selectedInstances: 266 | selectedDescriptors.extend(self.selectedInstances) 267 | else: 268 | selectedDescriptors.extend(self.operator.instances) 269 | 270 | if self.shouldShowPreviewLocation is not None: 271 | selectedDescriptors.append(PreviewInstance(self.shouldShowPreviewLocation)) 272 | 273 | lines = [] 274 | 275 | with UseVarLib(self.operator, useVarLib=False): 276 | try: 277 | for descriptor in selectedDescriptors: 278 | lineItem = dict( 279 | area=0, 280 | length=0, 281 | density=0, 282 | glyphRecords=[] 283 | ) 284 | 285 | previousGlyphName = None 286 | fullDesignLocation = descriptor.getFullDesignLocation(self.operator) 287 | kerningObject = self.operator.makeOneKerning(fullDesignLocation, pairs=possibleKerningPairs) 288 | for glyphName in glyphNames: 289 | # do not bend, reasoning: the descriptor locations are in designspace values. 290 | mathGlyph = self.operator.makeOneGlyph(glyphName, fullDesignLocation, decomposeComponents=True) 291 | if mathGlyph is not None: 292 | dest = internalFontClasses.createGlyphObject() 293 | mathGlyph.extractGlyph(dest) 294 | dest.tempLib['designLocation'] = Location(fullDesignLocation).asString() 295 | dest.tempLib['descriptor'] = descriptor 296 | 297 | glyphRecord = GlyphRecord(dest) 298 | 299 | if previousGlyphName and lineItem["glyphRecords"]: 300 | lineItem["glyphRecords"][-1].xAdvance = kerningObject.get((previousGlyphName, glyphName)) 301 | else: 302 | # mark the first glyph 303 | dest.tempLib["indicator"] = descriptor.flavor 304 | 305 | lineItem["glyphRecords"].append(glyphRecord) 306 | 307 | lineItem["length"] += dest.width 308 | lineItem["area"] += dest.area 309 | 310 | previousGlyphName = glyphName 311 | 312 | if lineItem["length"] != 0: 313 | lineItem["density"] = lineItem["area"] / lineItem["length"] 314 | lines.append(lineItem) 315 | except Exception: 316 | lines = [] 317 | self.w.infoText.set(["This designspace may not work as expected, check the Designspace Problems."], warning=True) 318 | 319 | glyphRecords = [] 320 | iterator = lines 321 | if self.shouldSortBy: 322 | iterator = sorted(lines, key=itemgetter(*self.shouldSortBy)) 323 | for line in iterator: 324 | glyphRecords.extend(line["glyphRecords"]) 325 | glyphRecords.append(GlyphRecord(self.w.preview.createNewLineGlyph())) 326 | 327 | self.w.preview.setGlyphRecords(glyphRecords) 328 | # hacking into the multiline view 329 | self.w.preview._glyphLineView._shouldSendEvents = True 330 | 331 | def previewSelectionCallback(self, sender): 332 | self.populateInfoStatusBar() 333 | 334 | def populateInfoStatusBar(self): 335 | selection = self.w.preview.getSelectedGlyph() 336 | if selection: 337 | # selection.removeOverlap() 338 | self.w.infoText.set([ 339 | f"location: {selection.tempLib['designLocation']}", 340 | f"glyph: {selection.name}", 341 | f"width: {selection.width:3.1f}", 342 | f"area: {selection.area:3.1f}" 343 | ]) 344 | else: 345 | self.w.infoText.set([]) 346 | 347 | def currentLocationCallback(self, sender): 348 | PreviewLocationFinder(sender, self.operator, self.shouldShowPreviewLocation) 349 | 350 | # menu callbacks 351 | 352 | def invertMenuItemCallback(self, sender): 353 | choice = not sender.state() 354 | sender.setState_(choice) 355 | setExtensionDefault(inverseDefaultKey, choice) 356 | if choice: 357 | self.displayPrefs['Inverse'] = True 358 | else: 359 | self.displayPrefs['Inverse'] = False 360 | self.w.preview.setDisplayStates(self.displayPrefs) 361 | 362 | def singleLineMenuItemCallback(self, sender): 363 | choice = not sender.state() 364 | sender.setState_(choice) 365 | setExtensionDefault(singleLineDefaultKey, choice) 366 | if choice: 367 | self.displayPrefs['displayMode'] = "Single Line" 368 | else: 369 | self.displayPrefs['displayMode'] = "Multi Line" 370 | self.w.preview.setDisplayStates(dict(displayMode=self.displayPrefs['displayMode'])) 371 | 372 | def showSourcesMenuItemCallback(self, sender): 373 | self.shouldShowSources = not sender.state() 374 | setExtensionDefault(showSourcesDefaultKey, self.shouldShowSources) 375 | sender.setState_(self.shouldShowSources) 376 | self.updatePreview() 377 | 378 | def showInstancesMenuItemCallback(self, sender): 379 | self.shouldShowInstances = not sender.state() 380 | setExtensionDefault(showInstancesDefaultKey, self.shouldShowInstances) 381 | sender.setState_(self.shouldShowInstances) 382 | self.updatePreview() 383 | 384 | def _resolveSortBy(self, sender, key): 385 | choice = not sender.state() 386 | sender.setState_(choice) 387 | if choice: 388 | self.shouldSortBy.add(key) 389 | elif not choice and key in self.shouldSortBy: 390 | self.shouldSortBy.remove(key) 391 | setExtensionDefault(shouldSortByDefaultKey, list(self.shouldSortBy)) 392 | self.updatePreview() 393 | 394 | def sortByLineAreaMenuItemCallback(self, sender): 395 | self._resolveSortBy(sender, "area") 396 | 397 | def sortByLineLengthMenuItemCallback(self, sender): 398 | self._resolveSortBy(sender, "length") 399 | 400 | def sortByLineDensityMenuItemCallback(self, sender): 401 | self._resolveSortBy(sender, "density") 402 | 403 | # robofont notifications 404 | 405 | def locationPreviewLineViewDidDrawGlyph(self, notification): 406 | # old style drawing! 407 | glyph = notification["glyph"] 408 | if "indicator" in glyph.tempLib: 409 | indicator = indicatorImageMap[glyph.tempLib["indicator"]] 410 | ctx.save() 411 | if self.displayPrefs['displayMode'] == "Single Line": 412 | ctx.translate(glyph.width / 2, self.dummyFont.info.descender) 413 | x = -indicator.size().width / 2 414 | else: 415 | ctx.translate(0, self.dummyFont.info.unitsPerEm * .4) 416 | x = 0 417 | ctx.scale(-notification["scale"]) 418 | ctx.image(indicator, (x, 0)) 419 | ctx.restore() 420 | 421 | # subscriber notifications 422 | 423 | designspaceEditorInstancesDidChangeDelay = 0.1 424 | 425 | def designspaceEditorInstancesDidChange(self, notification): 426 | self.updatePreview() 427 | 428 | def designspaceEditorSourcesDidChanged(self, notification): 429 | self.updatePreview() 430 | 431 | def designspaceEditorAxesDidChange(self, notification): 432 | self.updatePreview() 433 | 434 | def designspaceEditorSourceGlyphDidChange(self, notification): 435 | self.updatePreview() 436 | 437 | def designspaceEditorInfoKerningDidChange(self, notification): 438 | self.updatePreview() 439 | 440 | def designspaceEditorSourceKerningDidChange(self, notification): 441 | self.updatePreview() 442 | 443 | def designspaceEditorGroupsKerningDidChange(self, notification): 444 | self.updatePreview() 445 | 446 | def designspaceEditorGroupsFontDidChangedExternally(self, notification): 447 | self.updatePreview() 448 | 449 | designspaceEditorPreviewLocationDidChangeDelay = 0.01 450 | 451 | def designspaceEditorPreviewLocationDidChange(self, notification): 452 | self.shouldShowPreviewLocation = notification["location"] 453 | self.updatePreview() 454 | 455 | def designspaceEditorInstancesDidChangeSelection(self, notification): 456 | self.selectedInstances = notification["selectedItems"] 457 | self.updatePreview() 458 | 459 | def designspaceEditorSourcesDidChangeSelection(self, notification): 460 | self.selectedSources = notification["selectedItems"] 461 | self.updatePreview() 462 | 463 | def roboFontDidSwitchCurrentGlyph(self, notification): 464 | if not self.w.getNSWindow().isKeyWindow(): 465 | self.updatePreview() 466 | 467 | 468 | if __name__ == '__main__': 469 | designspace = CurrentDesignspace() 470 | if designspace is None: 471 | print("Open a design space!") 472 | else: 473 | c = LocationPreview(operator=designspace) 474 | # c.setPreviewString("HELLOVAH") 475 | -------------------------------------------------------------------------------- /DesignspaceEditor2.roboFontExt/lib/designspaceEditor/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LettError/designSpaceRoboFontExtension/b32cb50a976c63def69e6b69fd5327ed374b31a0/DesignspaceEditor2.roboFontExt/lib/designspaceEditor/parsers/__init__.py -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 |