{"id":18107419,"url":"https://github.com/mark2mark/skedge","last_synced_at":"2025-10-25T13:04:15.233Z","repository":{"id":74477828,"uuid":"108310650","full_name":"Mark2Mark/Skedge","owner":"Mark2Mark","description":"Live python sketcher for Glyphsapp","archived":false,"fork":false,"pushed_at":"2025-04-10T07:24:35.000Z","size":3974,"stargazers_count":12,"open_issues_count":3,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-13T22:13:01.256Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Mark2Mark.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-25T18:38:41.000Z","updated_at":"2025-04-10T07:24:39.000Z","dependencies_parsed_at":"2024-01-10T14:17:26.968Z","dependency_job_id":"7b8d569e-35b6-47b2-8652-e730cc20b782","html_url":"https://github.com/Mark2Mark/Skedge","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mark2Mark%2FSkedge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mark2Mark%2FSkedge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mark2Mark%2FSkedge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Mark2Mark%2FSkedge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Mark2Mark","download_url":"https://codeload.github.com/Mark2Mark/Skedge/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248788915,"owners_count":21161728,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-31T23:11:57.644Z","updated_at":"2025-10-25T13:04:10.211Z","avatar_url":"https://github.com/Mark2Mark.png","language":"Python","funding_links":["https://ko-fi.com/M4M580HG'"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003ca href='https://ko-fi.com/M4M580HG' target='_blank'\u003e\u003cimg height='36' style='border:0px;height:36px;' src='https://az743702.vo.msecnd.net/cdn/kofi1.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# Skedge\n\n\u003c!-- \u003ca href=\"https://glyphsapp.com/\"\u003e\u003cimg src=\"https://img.shields.io/badge/environment%20-GlyphsApp-brightgreen.svg\"\u003e\u003c/a\u003e \u003cimg src=\"https://img.shields.io/badge/type%20-Plugin-blue.svg\"\u003e \u003ca href=\"http://ts-vanilla.readthedocs.io/en/latest/\"\u003e \u003cimg src=\"https://img.shields.io/badge/dependencies%20-Vanilla-lightgray.svg\"\u003e\u003c/a\u003e \u003ca href=\"http://www.apache.org/licenses/LICENSE-2.0\"\u003e \u003cimg src=\"https://img.shields.io/badge/license%20-Apache 2.0-lightgray.svg\"\u003e\u003c/a\u003e --\u003e\n\n## What is it about?\n\n- 👉 Have you ever wanted to **make a reporter[^1] plugin** for Glyphs, but the developer kit and the plugin file structure looks too intimidating to you?  \n- 👉 Maybe you’re never willing to get your head around it and skip developing even though you have great ideas **you’d love to just sketch out**.  \n- 👉 Or do you create plugins from time to time, but you’re annoyed that you have to restart Glyphs for every change? This can take a loooot of time, especially when the plugin is packed with formulas and algorithms that you need to get straight and test.\n- 👉 You want to **see immediately which numbers and operators have which effect**. You want to **properly position your to be displayed objects**, maybe design them to provide an optimal user experience. Or **find the best colors** for your graphics.  \n[^1]: A plugin which draws something to your active Edit Tab\n\n#### 🎉 *Well, wait no longer! “Skedge” let’s you do exactly this!* 🎉\n\n🤓 “Skedge” lets you focus on the essence of code you need in order to get your idea to the canvas.\n- No GlyphsApp restart for every change you make.\n- No extra code that you don’t understand the use for.  \n- No file and folder overload.  \n\n**“Skedge” is your playground,** your tool to explore how to use python to build incredible tools for your type design workflow.\nVisual feedback in realtime is something that we designers always strive for.\n\n**“Skedge” tears down the inhibition level** for beginners and is a companion on the way to learn coding. The sense of achievement will make you happy.\nBut this tool will help you anytime, no matter if you just started with python or if you’re an experienced developer already.\n\n## How does it work?\n\nIn Skedge you just need to write the code that would go into any of the drawing callback methods of a reporter plugin.\n\n\u003e [!NOTE]\n\u003e The point of Skedge is to reduce all the overhead of a plugin and get to the barebone drawing procedure immediately.\n\n## Live Example\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/Mark2Mark/Skedge/master/Images/Skedge%2003.gif\" alt=\"Skedge\" height=\"\"\u003e\n\u003c/p\u003e\n\n\n## Getting Started with Skedge\n\nIf you haven't already, install Skedge using Glyphs Plugin Manager.\n\n1. Open the \"Skedge\" plugin from the Window menu. It provides a simple sample code to help you get started quickly.\n1. Now write your own code instead of the sample code. Only the code that will go into your Reporter plugin’s drawing method is required.\n1. You will see the result in realtime in the GlyphsApp Edit View.\n1. Save and open your code by using \u003ckbd\u003eCmd+S\u003c/kbd\u003e for save and \u003ckbd\u003eCmd+O\u003c/kbd\u003e for open.\n1. Toggle the \"Live\" checkbox to preview changes in real-time. If not, click the \"Run\" button (or use \u003ckbd\u003eCmd+R\u003c/kbd\u003e) to execute your code.\n1. Press \u003ckbd\u003eCmd+K\u003c/kbd\u003e to reset the drawing in your Edit Tab. The same effect occurs when you close the Skedge window.\n1. Press \u003ckbd\u003eCmd+P\u003c/kbd\u003e to either print your code or save it as a PDF.\n1. Python etiquette: Please use **tabs**. While Skedge currently doesn't support spaces, future updates might address this. For now, let's stick with tabs. Learn more about the great spaces vs tabs debate.\n1. The code written in Skedge can be seamlessly transferred into an actual reporter plugin. Paste the Skedge code into your glyphsReporter’s drawing method.\n1. Skedge provides access to all GlyphsApp Python objects and Cocoa UI objects. Import them explicitly in your code, for example, `from AppKit import NSColor, NSRect`.\n\n\n\n## Reset to Default Code\nIn case something goes wrong and you want Skedge to launch again with the default code, rather than your last state, run this once in GlyphApp’s Macro Panel:\n```py\ndel(Glyphs.defaults[\"SkedgeCode\"])\n```\n\n## Help\n\nYou find **help** and **code examples** here:\n\n👉 [Glyphs Documentation](https://docu.glyphsapp.com/)\n\n👉 [Glyphs Developer Kit (SDK)](https://github.com/schriftgestalt/GlyphsSDK)\n\nIt’s also always possible to peek into public plugins:\n\n👉 [my plugins](https://github.com/Mark2Mark/Glyphsapp-Plugins)\n\n👉 [@mekkablue’s plugins](https://github.com/mekkablue)\n\nand other people who are endlessly kind to share their skills with the world. :)\n\n## Sample Codes\n\nYou can dump these snippets right into “Skedge” and they will (hopefully) just do what they claim to do:\n\n### 01) Draw Layer Bounds\n```python\n###################\n# Draw Layer Bounds\n###################\nfrom AppKit import NSRectFill, NSRect, NSMakeRect\n\nNSColor.yellowColor().set()\n\nbounds = layer.bounds\nx = bounds.origin.x\ny = bounds.origin.y\nwidth = bounds.size.width\nheight = bounds.size.height\n\nrect = NSMakeRect(x, y, width, height)\nNSRectFill(rect)\n```\n\n### 02) Draw filled Path with red outline and highlight every second Node\n```python\n###################################################################\n# Draw filled Path with red outline and highlight every second Node\n###################################################################\nimport traceback\n\nscale = Glyphs.font.currentTab.scale\n\ndef badge(x, y, size):\n    myPath = NSBezierPath.alloc().init()\n    myRect = NSRect( ( x-size/2, y-size/2 ), ( size, size ) )\n    thisPath = NSBezierPath.bezierPathWithOvalInRect_( myRect )\n    myPath.appendBezierPath_( thisPath )\n    NSColor.colorWithCalibratedRed_green_blue_alpha_( 0.5, .5, 0.5, .3 ).set()\n    myPath.fill()\n\nfor path in layer.paths:\n    NSColor.grayColor().colorWithAlphaComponent_(0.3).set()\n    bp = path.bezierPath\n    bp.fill()\n    bp.setLineWidth_(5/scale)\n    NSColor.redColor().set()\n    bp.stroke()\n    for i, node in enumerate(path.nodes):\n        if i % 2:\n            badge(node.x, node.y, 20/scale )\n```\n\n### 03) Draw plumblines at each path’s center (x and y)\n```python\n#################################################\n# Draw plumblines at each path’s center (x and y)\n#################################################\nimport traceback\n\nglobal layer, scale, drawLine ## Skedge-Hack\n\nscale = Glyphs.font.currentTab.scale\nlayer = Glyphs.font.selectedLayers[0]\nNSColor.blueColor().set()\n\n\ndef drawLine((x1, y1), (x2, y2)):\n    strokeWidth = 1/scale\n    path = NSBezierPath.bezierPath()\n    path.moveToPoint_((x1, y1))\n    path.lineToPoint_((x2, y2))\n    path.setLineWidth_(strokeWidth)\n    path.setLineDash_count_phase_((10, 2), 2, 0.0)\n    path.stroke()\n\ndef DrawCross((x, y), (width, height)):\n    ### BOUNDS DIMENSIONS\n    xRight = x + width\n    yTop = y + height\n    xCenter = (x + width/2)\n    yCenter = (y + height/2)\n\n    ### LAYER/METRIC DIMENSIONS\n    left = 0\n    right = layer.width\n    ascender = layer.glyphMetrics()[1]\n    descender = layer.glyphMetrics()[3]\n\n    drawLine((left, yCenter), (right, yCenter))\n    drawLine((xCenter, descender), (xCenter, ascender))\n\n\nfor path in layer.paths:\n    DrawCross(*[p for p in path.bounds])\n```\n\n### 04) Draw line @ half Cap Height\n```python\n#################################################\n# Draw line @ half Cap Height\n#################################################\n\nfrom AppKit import NSColor, NSBezierPath\nscale = Glyphs.font.currentTab.scale\nlayer = Glyphs.font.selectedLayers[0]\n \ndef myColor(a, b, c, d):\n    c = NSColor.colorWithHue_saturation_brightness_alpha_(a, b, c, d)\n    return c\n\ndef line(x1, y1, x2, y2, scale):\n    myPath = NSBezierPath.alloc().init()\n    myPath.moveTo_((x1, y1))\n    myPath.lineTo_((x2, y2))\n    NSColor.systemPurpleColor().colorWithAlphaComponent_(0.9).set()\n    myPath.setLineWidth_(.5/scale)\n    myPath.stroke()\n\ncapHeight = layer.associatedFontMaster().capHeight\nwidth = layer.width\n\nline(0, capHeight/2, width, capHeight/2, scale)\n```\n\n## Other Info\n\n### Quirks\n\nDue to how the plugin is designed, the code you write does not add global variables to the main python namespace as you might be used to. Hence, if you want to access a global variable inside of a method you define, either pass it into the method as a parameter, or add it with the `global` keyword inside the method. For example\n```python\nmy_variable = 42\n\ndef my_method():\n    global my_variable # \u003c- See here.\n    ...\n```\n\n### Important\n\n\u003e [!WARNING]\n\u003e Skedge is in beta. Please backup your files. No guarantee for destroying your files.\n\n\u003e [!WARNING]\n\u003e Take care when doing transforms or things alike on your layer's bezierPath. Since it will actually address the real path, be sure to make a `.copy()` of your layer before proceeding with those.\n\u003e If you’re just reading data and drawing new objects from that data, you should be fine.\n\n\n## TODO\n\n- [x] Autosave text edits. Reopening Skedge now remembers your code. Thanks Georg!\n- [x] Fix encoding. Cannot save a file with words like »don’t«.\n- [ ] Display change of file in Window Title (Completely different file handling).\n- [ ] Work around some peculiar quirks that don’t need to be transferred to the actual reporterPlugin code later. (For instance calling some variables and functions `global`)\n- [ ] Provide more code snippets.\n- [x] Sophisticated syntax highlighting.\n- [x] Add license to Repo.\n\n\n## Pull Requests\n\nFeel free to comment or pull requests for any improvements.\n\n## License\n\nCopyright 2017–2024 [Mark Frömberg](https://www.markfromberg.com/) *@Mark2Mark*\n\nMade possible with the [Glyphs SDK](https://github.com/schriftgestalt/GlyphsSDK) by Georg Seifert [(@schriftgestalt)](https://github.com/schriftgestalt) and Rainer Erich Scheichelbauer [(@mekkablue)](https://github.com/mekkablue).\nThanks to Georg Seifert [(@schriftgestalt)](https://github.com/schriftgestalt) for streamlining and helping to make this tool still work after a lot of recent API changes!\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nSee the License file included in this repository for further details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmark2mark%2Fskedge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmark2mark%2Fskedge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmark2mark%2Fskedge/lists"}