├── .gitignore
├── Atom
├── package.json
└── snippets
│ └── Atom.cson
├── LICENSE
├── README.md
└── TextMate
└── Python for Glyphs.tmbundle
├── Snippets
├── Glyphs gui button help.tmSnippet
├── Glyphs gui button popup.tmSnippet
├── Glyphs gui button square.tmSnippet
├── Glyphs gui checkbox.tmSnippet
├── Glyphs gui combo box.tmSnippet
├── Glyphs gui pref load.tmSnippet
├── Glyphs gui pref register & load.tmSnippet
├── Glyphs gui pref register.tmSnippet
├── Glyphs gui pref save.tmSnippet
├── Glyphs gui progress bar.tmSnippet
├── Glyphs gui radiobuttons.tmSnippet
├── Glyphs gui text box.tmSnippet
├── Glyphs gui text edit.tmSnippet
├── Glyphs gui tooltip.tmSnippet
├── Glyphs gui.tmSnippet
├── Glyphs plugin conditions for drawing.tmSnippet
├── Glyphs plugin def.tmSnippet
├── Glyphs plugin keyboard shortcut.tmSnippet
├── Glyphs plugin log.tmSnippet
├── Glyphs plugin productPageURL.tmSnippet
├── Glyphs plugin updateFeedURL.tmSnippet
├── Glyphs py UI update.tmSnippet
├── Glyphs py angle.tmSnippet
├── Glyphs py circle.tmSnippet
├── Glyphs py clear log.tmSnippet
├── Glyphs py create ot class.tmSnippet
├── Glyphs py create ot feature.tmSnippet
├── Glyphs py distance.tmSnippet
├── Glyphs py italic.tmSnippet
├── Glyphs py layer offset.tmSnippet
├── Glyphs py layer round.tmSnippet
├── Glyphs py macro window.tmSnippet
├── Glyphs py measurement.tmSnippet
├── Glyphs py menutitle.tmSnippet
├── Glyphs py msg.tmSnippet
├── Glyphs py notification.tmSnippet
├── Glyphs py path intersect.tmSnippet
├── Glyphs py path subtract.tmSnippet
├── Glyphs py report.tmSnippet
├── Glyphs py tab with layers.tmSnippet
├── Glyphs py tab with text.tmSnippet
├── Glyphs py transform.tmSnippet
├── Glyphs py.tmSnippet
├── Glyphs2:3 if:then.tmSnippet
├── PyObjC Decorators.tmSnippet
├── py NSColor RGB.tmSnippet
├── py NSEvent Modifier Keys.tmSnippet
├── py abc.tmSnippet
├── py bezier.tmSnippet
├── py bezierQ.tmSnippet
├── py center of NSRect.tmSnippet
├── py clipboard.tmSnippet
├── py future imports.tmSnippet
├── py intersect two lines.tmSnippet
├── py random.tmSnippet
├── py sort.tmSnippet
├── py terminal.tmSnippet
├── py timer.tmSnippet
├── py traceback.tmSnippet
├── py write file.tmSnippet
└── vanilla help.tmSnippet
└── info.plist
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | TextMate/.DS_Store
3 |
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/Atom/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "python-for-glyphs",
3 | "version": "0.0.0",
4 | "description": "These are text editor snippets intended to facilitate Python development for Glyphs.app. Have fun.",
5 | "repository": "https://github.com/mekkablue/python-for-glyphs",
6 | "license": "Apache License, Version 2.0",
7 | "engines": {
8 | "atom": ">0.50.0"
9 | },
10 | "dependencies": {
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Atom/snippets/Atom.cson:
--------------------------------------------------------------------------------
1 | '*':
2 | 'Glyphs gui checkbox':
3 | 'prefix': 'checkbox'
4 | 'body': 'self.w.${1:myCheckBox} = vanilla.CheckBox( (15, ${2:170}, -15, 20), "${3:Activate option}", value=${4:False}, callback=self.SavePreferences, sizeStyle=\'small\' )\n$0'
5 | 'Glyphs gui pref load':
6 | 'prefix': 'prefload'
7 | 'body': 'self.w.${2:itemClassName}.set( Glyphs.defaults["com.mekkablue.${1:mainClassName}.$2"] )$0'
8 | 'Glyphs gui pref save':
9 | 'prefix': 'prefsave'
10 | 'body': 'Glyphs.defaults["com.mekkablue.${1:mainClassName}.${2:itemClassName}"] = self.w.$2.get()$0'
11 | 'Glyphs gui radiobuttons':
12 | 'prefix': 'radio'
13 | 'body': 'listOfOptions = [ ${1:"Option 1", "Option 2", "Option 3"} ]\nself.w.${2:radioButtons} = vanilla.RadioGroup( (15, ${3:100}, -15, 20*len(listOfOptions) ), listOfOptions, callback=self.SavePreferences, sizeStyle = \'small\' )\nself.w.$2.set( 0 )\n$0'
14 | 'Glyphs gui':
15 | 'prefix': 'gsgui'
16 | 'body': '#MenuTitle: ${1:$TM_FILENAME}\n# -*- coding: utf-8 -*-\n__doc__="""\n${2:Create effect for selected glyphs.}\n"""\n\nimport vanilla\nimport GlyphsApp\n\nclass ${3:${1/ //g}}( object ):\n\tdef __init__( self ):\n\t\t# Window \'self.w\':\n\t\twindowWidth = 350\n\t\twindowHeight = 260\n\t\twindowWidthResize = 100 # user can resize width by this value\n\t\twindowHeightResize = 0 # user can resize height by this value\n\t\tself.w = vanilla.FloatingWindow(\n\t\t\t( windowWidth, windowHeight ), # default window size\n\t\t\t"$1", # window title\n\t\t\tminSize = ( windowWidth, windowHeight ), # minimum size (for resizing)\n\t\t\tmaxSize = ( windowWidth + windowWidthResize, windowHeight + windowHeightResize ), # maximum size (for resizing)\n\t\t\tautosaveName = "com.${4:mekkablue}.$3.mainwindow" # stores last window position and size\n\t\t)\n\t\t\n\t\t# UI elements:\n\t\tself.w.text_1 = vanilla.TextBox( (15-1, 12+2, 75, 14), "Insert", sizeStyle=\'small\' )\n\t\tself.w.popup_1 = vanilla.PopUpButton( (15+40, 12, 50, 17), [str(x) for x in range( 3, 12 )], callback=self.SavePreferences, sizeStyle=\'small\' )\n\t\tself.w.edit_1 = vanilla.EditText( (15+40+50+10, 12, -15, 15+3), "insert text here", sizeStyle = \'small\')\n\t\t\n\t\t# Run Button:\n\t\tself.w.runButton = vanilla.Button((-80-15, -20-15, -15, -15), "Run", sizeStyle=\'regular\', callback=self.$5 )\n\t\tself.w.setDefaultButton( self.w.runButton )\n\t\t\n\t\t# Load Settings:\n\t\tif not self.LoadPreferences():\n\t\t\tprint "Note: \'$1\' could not load preferences. Will resort to defaults"\n\t\t\n\t\t# Open window and focus on it:\n\t\tself.w.open()\n\t\tself.w.makeKey()\n\t\t\n\tdef SavePreferences( self, sender ):\n\t\ttry:\n\t\t\tGlyphs.defaults["com.$4.$3.popup_1"] = self.w.popup_1.get()\n\t\texcept:\n\t\t\treturn False\n\t\t\t\n\t\treturn True\n\n\tdef LoadPreferences( self ):\n\t\ttry:\n\t\t\tNSUserDefaults.standardUserDefaults().registerDefaults_(\n\t\t\t\t{\n\t\t\t\t\t"com.$4.$3.popup_1": "0"\n\t\t\t\t}\n\t\t\t)\n\t\t\tself.w.popup_1.set( Glyphs.defaults["com.$4.$3.popup_1"] )\n\t\texcept:\n\t\t\treturn False\n\t\t\t\n\t\treturn True\n\n\tdef ${5:$3Main}( self, sender ):\n\t\ttry:\n\t\t\t${7:thisFont = Glyphs.font # frontmost font\n\t\t\tlistOfSelectedLayers = thisFont.selectedLayers # active layers of currently selected glyphs\n\t\t\tfor thisLayer in listOfSelectedLayers: # loop through layers\n\t\t\t\tthisGlyph = thisLayer.parent\n\t\t\t\tprint thisGlyph.name, thisLayer.name\n\t\t\t\t# output all node coordinates:\n\t\t\t\tfor thisPath in thisLayer.paths:\n\t\t\t\t\tfor thisNode in thisLayer.nodes:\n\t\t\t\t\t\tprint "-- %.1f %.1f" % ( thisNode.x, thisNode.y )\n\t\t\t}\n\t\t\t$0\n\t\t\t\n\t\t\tif not self.SavePreferences( self ):\n\t\t\t\tprint "Note: \'$1\' could not write preferences."\n\t\t\t\n\t\t\t${6:self.w.close() # delete if you want window to stay open}\n\t\texcept Exception, e:\n\t\t\t# brings macro window to front and reports error:\n\t\t\tGlyphs.showMacroWindow()\n\t\t\tprint "$1 Error: %s" % e\n\n$3()'
17 | 'Glyphs plugin def':
18 | 'prefix': 'plugindef'
19 | 'body': 'def ${1:fname}( ${2:`if [ "$TM_CURRENT_LINE" != "" ]\n\t\t\t\t# poor man\'s way ... check if there is an indent or not\n\t\t\t\t# (cuz we would have lost the class scope by this point)\n\t\t\t\tthen\n\t\t\t\t\techo "self"\n\t\t\t\tfi`} ):\n\t${3/.+/"""/}${3:docstring for $1}${3/.+/"""\\n/}${3/.+/\\t\\t/}try:\n\t\t${0:pass}\n\texcept Exception as e:\n\t\tself.logToConsole( "$1: %s" % str(e) )\n\t\t'
20 | 'Glyphs plugin log':
21 | 'prefix': 'log'
22 | 'body': 'self.logToConsole( "${1:method name}: %s" % str(e) )$0'
23 | 'Glyphs py angle':
24 | 'prefix': 'angle'
25 | 'body': '${1:import math}\n\ndef angle( firstPoint, secondPoint ):\n\txDiff = firstPoint.x - secondPoint.x\n\tyDiff = firstPoint.y - secondPoint.y\n\ttangens = yDiff / xDiff\n\tangle = math.atan( tangens ) * 180.0 / math.pi\n\treturn angle\n$0'
26 | 'Glyphs py clear log':
27 | 'prefix': 'clear'
28 | 'body': 'Glyphs.clearLog() # clears macro window log$0'
29 | 'Glyphs py create ot class':
30 | 'prefix': 'otclass'
31 | 'body': '${1:import GlyphsApp\nFont = Glyphs.font\n}\ndef create_otclass( classname = "@default", \n classglyphs = [ x.parent.name for x in Font.selectedLayers ], \n targetfont = Font ):\n\t"""\n\tCreates an OpenType class in the font.\n\tDefault: class @default with currently selected glyphs in the current font.\n\tReturns a status message in form of a string.\n\t"""\n\t\n\t# strip \'@\' from beginning:\n\tif classname[0] == "@":\n\t\tclassname = classname[1:]\n\t\n\tclassCode = " ".join( classglyphs )\n\t\n\tif classname in [ c.name for c in targetfont.classes ]:\n\t\ttargetfont.classes[classname].code = classCode\n\t\treturn "Updated existing OT class \'%s\'." % classname\n\telse:\n\t\tnewClass = GSClass()\n\t\tnewClass.name = classname\n\t\tnewClass.code = classCode\n\t\ttargetfont.classes.append( newClass )\n\t\treturn "Created new OT class: \'%s\'" % classname\n\n$0'
32 | 'Glyphs py create ot feature':
33 | 'prefix': 'otfeature'
34 | 'body': '${1:import GlyphsApp\nFont = Glyphs.font\n}\ndef updated_code( oldcode, beginsig, endsig, newcode ):\n\t"""Replaces text in oldcode with newcode, but only between beginsig and endsig."""\n\tbegin_offset = oldcode.find( beginsig )\n\tend_offset = oldcode.find( endsig ) + len( endsig )\n\tnewcode = oldcode[:begin_offset] + beginsig + newcode + "\\n" + endsig + oldcode[end_offset:]\n\treturn newcode\n\ndef create_otfeature( featurename = "calt", \n featurecode = "# empty feature code", \n targetfont = Font,\n codesig = "DEFAULT-CODE-SIGNATURE" ):\n\t"""\n\tCreates or updates an OpenType feature in the font.\n\tReturns a status message in form of a string.\n\t"""\n\t\n\tbeginSig = "# BEGIN " + codesig + "\\n"\n\tendSig = "# END " + codesig + "\\n"\n\t\n\tif featurename in [ f.name for f in targetfont.features ]:\n\t\t# feature already exists:\n\t\ttargetfeature = targetfont.features[ featurename ]\n\t\t\n\t\tif beginSig in targetfeature.code:\n\t\t\t# replace old code with new code:\n\t\t\ttargetfeature.code = updated_code( targetfeature.code, beginSig, endSig, featurecode )\n\t\telse:\n\t\t\t# append new code:\n\t\t\ttargetfeature.code += "\\n" + beginSig + featurecode + "\\n" + endSig\n\t\t\t\n\t\treturn "Updated existing OT feature \'%s\'." % featurename\n\telse:\n\t\t# create feature with new code:\n\t\tnewFeature = GSFeature()\n\t\tnewFeature.name = featurename\n\t\tnewFeature.code = beginSig + featurecode + "\\n" + endSig\n\t\ttargetfont.features.append( newFeature )\n\t\treturn "Created new OT feature \'%s\'" % featurename\n\n$0'
35 | 'Glyphs py distance':
36 | 'prefix': 'distance'
37 | 'body': '${1:import math}\n\ndef distance( node1, node2 ):\n\treturn math.hypot( node1.x - node2.x, node1.y - node2.y )\n$0'
38 | 'Glyphs py doc':
39 | 'prefix': 'doc'
40 | 'body': 'thisDoc = Glyphs.currentDocument'
41 | 'Glyphs py macro window':
42 | 'prefix': 'macro'
43 | 'body': '# brings macro window to front and clears its log:\nGlyphs.clearLog()\nGlyphs.showMacroWindow()\n${1:print "${2:Output}:"}$0'
44 | 'Glyphs py measurement':
45 | 'prefix': 'measurement'
46 | 'body': '${1:from Foundation import *}\n\ndef intersectionsBetweenPoints( thisLayer, startPoint, endPoint ):\n\t"""\n\tReturns list of intersection NSPoints from startPoint to endPoint.\n\tthisLayer ... a glyph layer\n\tstartPoint, endPoint ... NSPoints\n\t"""\n\t\n\t# prepare layer copy for measurement:\n\tcleanLayer = thisLayer.copyDecomposedLayer()\n\tcleanLayer.removeOverlap()\n\t\n\t# initiate the measurement tool and measure:\n\tmeasurementTool = NSClassFromString("GlyphsToolMeasurement").alloc().init()\n\treturnList = [ NSPoint( p.pointValue().x, p.pointValue().y ) for p in listOfIntersections ]\n\treturn returnList\n$0'
47 | 'Glyphs py menutitle':
48 | 'prefix': 'title'
49 | 'body': '#MenuTitle: ${1:$TM_FILENAME}\n# -*- coding: utf-8 -*-\n__doc__="""\n${2:$1}.\n"""\n$0'
50 | 'Glyphs py select':
51 | 'prefix': 'select'
52 | 'body': '# selects items on thisLayer:\nitemsToBeSelected = NSMutableArray.arrayWithObject_( ${1:myObjects} )\n${2:thisLayer}.setSelection_( itemsToBeSelected )\n$0'
53 | 'Glyphs py tab':
54 | 'prefix': 'tab'
55 | 'body': '# opens new Edit tab:\n${3:from PyObjCTools.AppHelper import callAfter}\ncallAfter( ${1:Glyphs.currentDocument}.windowController().addTabWithString_, ${2:"abc"} )$0\n'
56 | 'Glyphs py':
57 | 'prefix': 'gspy'
58 | 'body': '#MenuTitle: ${1:$TM_FILENAME}\n# -*- coding: utf-8 -*-\n__doc__="""\n${2:Create effect for selected glyphs.}\n"""\n\nimport GlyphsApp\n${3:import random\nrandom.seed()}\n\nthisFont = Glyphs.font # frontmost font\nthisFontMaster = thisFont.selectedFontMaster # active master\nlistOfSelectedLayers = thisFont.selectedLayers # active layers of selected glyphs\n${4:selection = listOfSelectedLayers[0].selection() # node selection in edit mode}\n${5:thisDoc = Glyphs.currentDocument}\n\ndef process( thisLayer ):\n\tfor thisPath in thisLayer.paths:\n\t\tfor thisNode in thisPath.nodes:\n\t\t\tprint "-- node %.1f %.1f (type %i)" % ( thisNode.x, thisNode.y, thisNode.type )\n\t\t\t$0\n\nthisFont.disableUpdateInterface() # suppresses UI updates in Font View\n\nfor thisLayer in listOfSelectedLayers:\n\tthisGlyph = thisLayer.parent\n\tprint "Processing", thisGlyph.name\n\tthisGlyph.beginUndo() # begin undo grouping\n\tprocess( thisLayer )\n\tthisGlyph.endUndo() # end undo grouping\n\nthisFont.enableUpdateInterface() # re-enables UI updates in Font View\n'
59 | 'py bezier':
60 | 'prefix': 'bezier'
61 | 'body': 'def bezier( x1,y1, x2,y2, x3,y3, x4,y4, t ):\n\t"""\n\tReturns coordinates for t (=0.0...1.0) on curve segment.\n\tx1,y1 and x4,y4: coordinates of on-curve nodes\n\tx2,y2 and x3,y3: coordinates of BCPs\n\t"""\n\tx = x1*(1-t)**3 + x2*3*t*(1-t)**2 + x3*3*t**2*(1-t) + x4*t**3\n\ty = y1*(1-t)**3 + y2*3*t*(1-t)**2 + y3*3*t**2*(1-t) + y4*t**3\n\n\treturn x, y\n'
62 | 'py clipboard':
63 | 'prefix': 'clipboard'
64 | 'body': '${1:from AppKit import *}\n\ndef setClipboard( myText ):\n\t"""\n\tSets the contents of the clipboard to myText.\n\tReturns True if successful, False if unsuccessful.\n\t"""\n\ttry:\n\t\tmyClipboard = NSPasteboard.generalPasteboard()\n\t\tmyClipboard.declareTypes_owner_( [NSStringPboardType], None )\n\t\tmyClipboard.setString_forType_( myText, NSStringPboardType )\n\t\treturn True\n\texcept Exception as e:\n\t\treturn False\n\n${2:if not setClipboard(${3:"clipboard text"}):\n\tprint "Warning: could not set clipboard to %s" % ( $3 )}\n$0'
65 | 'py intersect':
66 | 'prefix': 'intersect'
67 | 'body': 'def intersect( x1, y1, x2, y2, x3, y3, x4, y4 ):\n\t"""Calculates the intersection of line P1-P2 with P3-P4."""\n\tslope12 = ( float(y2) - float(y1) ) / ( float(x2) - float(x1) )\n\tslope34 = ( float(y4) - float(y3) ) / ( float(x4) - float(x3) )\n\t\n\tx = ( slope12 * x1 - y1 - slope34 * x3 + y3 ) / ( slope12 - slope34 )\n\ty = slope12 * ( x - x1 ) + y1\n\t\n\treturn x, y\n\t\n'
68 | 'py random':
69 | 'prefix': 'random'
70 | 'body': 'import random\nrandom.seed()\n${1:randomNumber = random.randint( -100, 100 )\n}$0'
71 | 'py rotate coordinates':
72 | 'prefix': 'rotate'
73 | 'body': '${1:import math\n}\ndef rotate( x, y, angle=180.0, x_orig=0.0, y_orig=0.0):\n\t"""Rotates x/y around x_orig/y_orig by angle and returns result as [x,y]."""\n\t\n\tnew_angle = ( angle / 180.0 ) * math.pi\n\tnew_x = ( x - x_orig ) * math.cos( new_angle ) - ( y - y_orig ) * math.sin( new_angle ) + x_orig\n\tnew_y = ( x - x_orig ) * math.sin( new_angle ) + ( y - y_orig ) * math.cos( new_angle ) + y_orig\n\t\n\treturn [ new_x, new_y ]\n$0'
74 | 'py skew coordinates':
75 | 'prefix': 'skew'
76 | 'body': '${1:import math}\n\ndef italicSkew( x, y, angle=10.0, height=0.0 ):\n\t"""Skews x/y along the x axis and returns skewed x value. If angle is the italic angle, then height should be half x-height."""\n\t\n\tnew_angle = ( angle / 180.0 ) * math.pi\n\tnew_x = x + ( y - height ) * math.tan( new_angle )\n\t\n\treturn new_x\n\t\n\t# [1 tan(angle) 0 1 0 0]\n\t# x\' = x*a + y*b + e\n\t# y\' = x*c + y*d + f\n$0'
77 | 'py sort':
78 | 'prefix': 'sort'
79 | 'body': 'sorted( ${1:[ ${2:[l.parent.name, l.bounds.origin.y]} for l in selectedLayers]}, key = lambda thisListItem: ${3:thisListItem[1]} )$0'
80 | 'py terminal':
81 | 'prefix': 'terminal'
82 | 'body': '${1:import os}\nterminalCommand = \'${2:uptime}\'\nterminalCommandResult = os.system( terminalCommand )\n${3:print terminalCommandResult}$0\n'
83 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Python development for Glyphs.app
2 |
3 | These are text editor snippets intended to facilitate Python (both 2 and 3) development for Glyphs.app. Have fun.
4 |
5 | **TextMate:** Since I use TextMate myself, the TextMate snippets can safely be assumed to be up to date most of the time.
6 |
7 | **Sublime Text:** Newer versions of Sublime Text can read TextMate bundles, so you are up to date automatically. See installation instructions below.
8 |
9 | **Atom:** Since I don’t use Atom, I depend on other people’s pull requests. Hence, the Atom snippets are usually out of date. If you want up-to-date snippets for Atom, feel free to contribute to this repository on a regular basis.
10 |
11 | ### Snippets
12 |
13 | * `gspy⇥` Basic structure of a Glyphs script that iterates through selected layers.
14 | * `title⇥` MenuTitle (name of the script as it appears in the *Script* menu) and docstring (text for the tooltip).
15 | * `clear⇥` Clears the Macro Window log.
16 | * `macro⇥` Clears the Macro Window log and brings the Macro Window to the front.
17 | * `angle⇥` Function returning the angle between two points.
18 | * `center⇥` Convenience function returning the center (as NSPoint) of an NSRect.
19 | * `circle⇥` Function returning a GSPath containing a circle.
20 | * `otclass⇥` Function that creates or (if it already exists) updates an OpenType class.
21 | * `otfeature⇥` Function that creates or (if it already exists) updates an OpenType feature.
22 | * `italic⇥` Function that returns a point position that respects an italic angle.
23 | * `offset⇥` Function that offsets a GSLayer.
24 | * `round⇥` Function that rounds the corners of a GSLayer.
25 | * `measurement⇥` Function that returns intersection points (like the Measurement tool does) of the decomposed and overlap-removed layer.
26 | * `msg⇥` Function for displaying a simple (non-Vanilla) message dialog.
27 | * `select⇥` Adds item on a layer to the user selection.
28 | * `tab⇥` Opens a new tab with supplied text or collection of layers.
29 | * `transform⇥` Function for creating a NSAffineTransform object, necessary for shifting, scaling, skewing, rotating.
30 | * `bezier⇥` Function for returning the x,y for a given t on a cubic Bézier curve segment.
31 | * `clipboard⇥` Function for setting the clipboard.
32 | * `random⇥` Import the *random* library, seed, and create a random integer.
33 | * `sort⇥` Sort a list of objects by a certain attribute of those objects (e.g. layers by their max y).
34 | * `terminal⇥` Execute a Terminal (bash) command.
35 | * `trace⇥` Import the *traceback* library and print a traceback. Useful for *except* clauses.
36 | * `subtract⇥` Subtract GSPath objects.
37 | * `intersect⇥` Intersect GSPath objects.
38 | * `timer⇥` Method for taking and reporting elapsed time.
39 | * `notification⇥` Display a floating notification in top right corner of screen.
40 | * `write⇥` Method *saveFileInLocation()* for writing UTF-8 string into a new file.
41 | * `keys⇥` NSEvent algorithm for determining which modifier keys are pressed by the user.
42 | * `g23⇥` Differentiating code for Glyphs 2 and 3.
43 | * `fut⇥` The *from future* imports for Glyphs 2/3 and Python 2/3 compatibility.
44 | * `update⇥` Send an interface update notification to the Notification Center. Only use this to force a UI update if Glyphs does not redraw as it should. (Glyphs 2 only.)
45 |
46 | #### Vanilla-specific snippets
47 |
48 | * `gsgui⇥` Basic structure of a Glyphs script with a dialog (GUI). Uses Vanilla.
49 | * `pref⇥` Register, load, register & load, or save preferences for Vanilla GUI items.
50 | * `button⇥` Vanilla popup and square button.
51 | * `text⇥` Vanilla text box or text edit field.
52 | * `checkbox⇥` Vanilla checkbox.
53 | * `radio⇥` Vanilla radiobuttons.
54 | * `progress⇥` Vanilla progress bar.
55 | * `tooltip⇥` Tooltip for a Vanilla object.
56 | * `vanilla⇥` Show help for Vanilla classes.
57 |
58 | #### Plugin SDK-specific snippets
59 |
60 | * `dec⇥` Decorators.
61 | * `rgb⇥` NSColor object with RGB definition.
62 | * `plugindef⇥` Function definition structure better suited for the [Plugin SDK](https://github.com/schriftgestalt/GlyphsSDK/).
63 | * `log⇥` Log a message to the console.
64 | * `url⇥` Prefill the *productPageURL* or *updateFeedURL* entry of a plug-in’s *Info.plist* with a GitHub URL.
65 | * `key⇥` Define a keyboard shortcut for a plug-in, including the modifier keys.
66 | * `cond⇥` Inserts the `conditionsAreMetForDrawing()` method for [Reporter plug-ins](https://github.com/schriftgestalt/GlyphsSDK/tree/master/Python%20Templates/Reporter). It will return `False` if the user is in text mode or switched to the Hand tool (H).
67 |
68 | ### Installation
69 |
70 | * TextMate bundle: double click to install
71 | * Sublime Text: Open *Sublime Text > Preferences > Browse Packages…* and move the `Python for Glyphs.tmbundle` into the folder that appears
72 | * Atom: move to `~/.atom/packages/python-to-glyphs`
73 |
74 | ### License
75 |
76 | Copyright 2014 Rainer Erich Scheichelbauer (@mekkablue).
77 | Some code by Georg Seifert (@schriftgestalt). Atom adaptation by Kenneth Ormandy (@kennethormandy).
78 |
79 | Licensed under the Apache License, Version 2.0 (the "License");
80 | you may not use this file except in compliance with the License.
81 | You may obtain a copy of the License at
82 |
83 | http://www.apache.org/licenses/LICENSE-2.0
84 |
85 | See the License file included in this repository for further details.
86 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui button help.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | self.w.${1:helpButton} = vanilla.HelpButton((${2:inset+${3:100}}, linePos, 21, 21), callback=self.${4:openURL})
7 | ${5:linePos += lineHeight
8 | }$0
9 |
10 | name
11 | Glyphs gui button help
12 | tabTrigger
13 | button
14 | uuid
15 | BBCDD180-BDAE-4D73-B812-8F4DC9CA6DD2
16 |
17 |
18 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui button popup.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | self.w.${1:popupButton} = vanilla.PopUpButton((${2:inset+${3:100}}, linePos, ${4:-inset}, 17), ${5:("Option 1", "Option 2")}, sizeStyle="small", callback=self.SavePreferences)
7 | ${6:linePos += lineHeight
8 | }$0
9 |
10 | name
11 | Glyphs gui button popup
12 | tabTrigger
13 | button
14 | uuid
15 | D046CB98-4894-4F6F-8359-3FCADC7696D4
16 |
17 |
18 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui button square.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | self.w.${1:updateButton} = vanilla.SquareButton((${2:-inset-20}, linePos, ${3:-inset}, 18), "${4:↺}", sizeStyle="small", callback=self.${5:update})
7 | ${6:linePos += lineHeight
8 | }$0
9 | name
10 | Glyphs gui button square
11 | tabTrigger
12 | button
13 | uuid
14 | C2963F4D-7E80-4D76-9F3E-F91C236E20E5
15 |
16 |
17 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui checkbox.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | self.w.${1:myCheckBox} = vanilla.CheckBox((${2:inset}, ${3:linePos-1}, ${4:-$2}, 20), "${5:Activate option}", value=${6|False,True|}, callback=self.SavePreferences, sizeStyle="small")
7 | ${7:linePos += lineHeight
8 | }$0
9 | name
10 | Glyphs gui checkbox
11 | tabTrigger
12 | checkbox
13 | uuid
14 | 843CAD52-41F4-4351-B332-477882EAE808
15 |
16 |
17 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui combo box.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | self.w.${1:comboBox} = vanilla.ComboBox((${2:inset}, linePos-1, ${3:-$2}, 19), ${4:[g.name for g in Glyphs.font.glyphs]}, sizeStyle="small", callback=self.SavePreferences)
7 | ${5:linePos += lineHeight
8 | }$0
9 | name
10 | Glyphs gui combo box
11 | tabTrigger
12 | combo
13 | uuid
14 | FDC049BA-634A-4878-B29D-6B7507E371D1
15 |
16 |
17 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui pref load.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | self.w.${1:itemName}.set(self.pref("$1"))$0
7 | name
8 | Glyphs gui pref load
9 | tabTrigger
10 | pref
11 | uuid
12 | 0767D626-5D92-41C1-8607-598A5AEA4177
13 |
14 |
15 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui pref register & load.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | Glyphs.registerDefault(self.domain("${1:itemName}"), ${2:0})
7 | ${3:self.w.$1.set(self.pref("$1"))}$0
8 | name
9 | Glyphs gui pref register & load
10 | tabTrigger
11 | pref
12 | uuid
13 | 21A48C34-CCAF-4905-85C3-1324F8340F0D
14 |
15 |
16 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui pref register.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | Glyphs.registerDefault(self.domain("${1:itemName}"), ${2:0})$0
7 |
8 | name
9 | Glyphs gui pref register
10 | tabTrigger
11 | pref
12 | uuid
13 | F9B4762D-B48F-4325-B57B-DA8B2790AB70
14 |
15 |
16 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui pref save.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | Glyphs.defaults[self.domain("${1:itemName}")] = self.w.$1.get()$0
7 |
8 | name
9 | Glyphs gui pref save
10 | tabTrigger
11 | pref
12 | uuid
13 | 4E5AE413-E026-44AE-B5AC-6169EF6056BC
14 |
15 |
16 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui progress bar.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | self.w.progress = vanilla.ProgressBar((inset, linePos, -inset, 16))
7 | self.w.progress.set(0) # set progress indicator to zero
8 | ${1:linePos+=lineHeight}$0
9 |
10 | name
11 | Glyphs gui progress bar
12 | tabTrigger
13 | progress
14 | uuid
15 | EE4778B2-A0E7-4A0D-93DD-B133AC2BCA0D
16 |
17 |
18 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui radiobuttons.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | radioOptions = (${1:"Option 1", "Option 2", "Option 3"})
7 | self.w.${2:radioButtons} = vanilla.RadioGroup((inset${3:*2}, linePos, -inset, lineHeight*len(radioOptions)), radioOptions, callback=self.SavePreferences, sizeStyle="small")
8 | self.w.$2.set( 0 )
9 | ${4:linePos += lineHeight*len(radioOptions)}$0
10 |
11 | name
12 | Glyphs gui radiobuttons
13 | tabTrigger
14 | radio
15 | uuid
16 | 79C8FE42-5BB9-4529-AEA7-6C5A9BDA92AC
17 |
18 |
19 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui text box.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | self.w.${1:descriptionText} = vanilla.TextBox((${2:inset}, linePos+2, ${3:-$2}, 14), "${4:Apply function to the font}", sizeStyle="small", selectable=True)
7 | ${5:linePos += lineHeight
8 | }$0
9 | name
10 | Glyphs gui text box
11 | tabTrigger
12 | text
13 | uuid
14 | 5BA43232-D3E7-42D6-8744-2A87A8193192
15 |
16 |
17 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui text edit.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | self.w.${1:textEntry} = vanilla.EditText((${2:inset${3:+80}}, linePos, ${4:-inset}, 19), "${5:insert text here}", callback=self.SavePreferences, sizeStyle="small")
7 | ${6:linePos += lineHeight
8 | }$0
9 |
10 | name
11 | Glyphs gui text edit
12 | tabTrigger
13 | text
14 | uuid
15 | C1706817-3A24-43DB-9B9F-B2A54CD9DAF6
16 |
17 |
18 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui tooltip.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | self.w.${1:element}.getNS${2|TextField,Button,ComboBox,PopUpButton,ProgressIndicator,Slider,Window,Box,ColorWell,DatePicker,Drawer,ImageView,LevelIndicator,Matrix,OutlineView,ScrollView,SearchField,SecureTextField,SegmentedButton,SplitView,TableView,TabView,View,TextView,VisualEffectView,WindowController|}().setToolTip_(${3:"Tooltip text."})$0
7 | name
8 | Glyphs gui tooltip
9 | tabTrigger
10 | tooltip
11 | uuid
12 | EE0FDDF3-0AB2-477D-B901-AE26BA3816F8
13 |
14 |
15 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs gui.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | #MenuTitle: ${1:$TM_FILENAME}
7 | # -*- coding: utf-8 -*-
8 | from __future__ import division, print_function, unicode_literals
9 | __doc__="""
10 | ${2:Create effect for selected glyphs.}
11 | """
12 |
13 | import vanilla, sys
14 |
15 | class ${3:${1/ //g}}(object):
16 | prefID = "com.${4:mekkablue}.$3"
17 | prefDict = {
18 | # "prefName": defaultValue,
19 | "popup_1": 0,
20 | }
21 |
22 | def __init__( self ):
23 | # Window 'self.w':
24 | windowWidth = 350
25 | windowHeight = 260
26 | windowWidthResize = 100 # user can resize width by this value
27 | windowHeightResize = 0 # user can resize height by this value
28 | self.w = vanilla.FloatingWindow(
29 | (windowWidth, windowHeight), # default window size
30 | "$1", # window title
31 | minSize = (windowWidth, windowHeight), # minimum size (for resizing)
32 | maxSize = (windowWidth + windowWidthResize, windowHeight + windowHeightResize), # maximum size (for resizing)
33 | autosaveName = self.domain("mainwindow") # stores last window position and size
34 | )
35 |
36 | # UI elements:
37 | linePos, inset, lineHeight = 12, 15, 22
38 | self.w.descriptionText = vanilla.TextBox((inset, linePos+2, -inset, 14), "Apply function to the font", sizeStyle="small", selectable=True)
39 | linePos += lineHeight
40 |
41 | # Run Button:
42 | self.w.runButton = vanilla.Button((-80-inset, -20-inset, -inset, -inset), "Run", sizeStyle="regular", callback=self.$5)
43 | self.w.setDefaultButton(self.w.runButton)
44 |
45 | # Load Settings:
46 | if not self.LoadPreferences():
47 | print("⚠️ ‘$1’ could not load preferences. Will resort to defaults.")
48 |
49 | # Open window and focus on it:
50 | self.w.open()
51 | self.w.makeKey()
52 |
53 | def domain(self, prefName):
54 | prefName = prefName.strip().strip(".")
55 | return self.prefID + "." + prefName.strip()
56 |
57 | def pref(self, prefName):
58 | prefDomain = self.domain(prefName)
59 | return Glyphs.defaults[prefDomain]
60 |
61 | def SavePreferences(self, sender=None):
62 | try:
63 | # write current settings into prefs:
64 | for prefName in self.prefDict.keys():
65 | Glyphs.defaults[self.domain(prefName)] = getattr(self.w, prefName).get()
66 | return True
67 | except:
68 | import traceback
69 | print(traceback.format_exc())
70 | return False
71 |
72 | def LoadPreferences(self):
73 | try:
74 | for prefName in self.prefDict.keys():
75 | # register defaults:
76 | Glyphs.registerDefault(self.domain(prefName), self.prefDict[prefName])
77 | # load previously written prefs:
78 | getattr(self.w, prefName).set(self.pref(prefName))
79 | return True
80 | except:
81 | import traceback
82 | print(traceback.format_exc())
83 | return False
84 |
85 | def ${5:$3Main}(self, sender=None):
86 | try:
87 | # clear macro window log:
88 | Glyphs.clearLog()
89 |
90 | # update settings to the latest user input:
91 | if not self.SavePreferences():
92 | print("⚠️ ‘$1’ could not write preferences.")
93 |
94 | # read prefs:
95 | for prefName in self.prefDict.keys():
96 | try:
97 | setattr(sys.modules[__name__], prefName, self.pref(prefName))
98 | except:
99 | fallbackValue = self.prefDict[prefName]
100 | print(f"⚠️ Could not set pref ‘{prefName}’, resorting to default value: ‘{fallbackValue}’.")
101 | setattr(sys.modules[__name__], prefName, fallbackValue)
102 |
103 | thisFont = Glyphs.font # frontmost font
104 | if thisFont is None:
105 | Message(title="No Font Open", message="The script requires a font. Open a font and run the script again.", OKButton=None)
106 | else:
107 | filePath = thisFont.filepath
108 | if filePath:
109 | reportName = f"{filePath.lastPathComponent()}\n📄 {filePath}"
110 | else:
111 | reportName = f"{thisFont.familyName}\n⚠️ The font file has not been saved yet."
112 | print(f"$1 Report for {reportName}")
113 | print()
114 |
115 | ${7:listOfSelectedLayers = thisFont.selectedLayers
116 | for thisLayer in listOfSelectedLayers:
117 | thisGlyph = thisLayer.parent
118 | print(thisGlyph.name, thisLayer.name)
119 | for thisPath in thisLayer.paths:
120 | for thisNode in thisLayer.nodes:
121 | print("\t", thisNode.x, thisNode.y)
122 | }
123 | $0
124 |
125 | ${6: self.w.close() # delete if you want window to stay open
126 | }
127 | print("\nDone.")
128 |
129 | except Exception as e:
130 | # brings macro window to front and reports error:
131 | Glyphs.showMacroWindow()
132 | print(f"$1 Error: {e}")
133 | import traceback
134 | print(traceback.format_exc())
135 |
136 | $3()
137 | name
138 | Glyphs gui
139 | tabTrigger
140 | gsgui
141 | uuid
142 | EB1EA211-2FA0-4C31-BBB4-818161C0977A
143 |
144 |
145 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs plugin conditions for drawing.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | @objc.python_method
7 | def conditionsAreMetForDrawing(self):
8 | """
9 | Don't activate if text or pan (hand) tool are active.
10 | """
11 | currentController = self.controller.view().window().windowController()
12 | if currentController:
13 | tool = currentController.toolDrawDelegate()
14 | textToolIsActive = tool.isKindOfClass_(NSClassFromString("GlyphsToolText"))
15 | handToolIsActive = tool.isKindOfClass_(NSClassFromString("GlyphsToolHand"))
16 | if not textToolIsActive and not handToolIsActive:
17 | return True
18 | return False
19 | $0
20 | name
21 | Glyphs plugin conditions for drawing
22 | tabTrigger
23 | cond
24 | uuid
25 | 188106AA-A839-4B79-95EF-362A80276641
26 |
27 |
28 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs plugin def.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | @objc.python_method
7 | def ${1:fname}(${2:`if [ "$TM_CURRENT_LINE" != "" ]
8 | # poor man's way ... check if there is an indent or not
9 | # (cuz we would have lost the class scope by this point)
10 | then
11 | echo "self"
12 | fi`}):
13 | ${3/.+/"""/}${3:docstring for $1}${3/.+/"""\n/}${3/.+/\t\t/}try:
14 | ${0:pass}
15 | except Exception as e:
16 | self.logToConsole(f"$1: {str(e)}")
17 |
18 | name
19 | Glyphs plugin def
20 | tabTrigger
21 | plugindef
22 | uuid
23 | DBB9E029-8E61-4E7F-9FDD-ECEE1097358B
24 |
25 |
26 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs plugin keyboard shortcut.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | self.keyboardShortcut = "$1"
7 | self.keyboardShortcutModifier = ${2:NSShiftKeyMask | }${3:NSControlKeyMask | }${4:NSAlternateKeyMask | }${5:NSCommandKeyMask}$0
8 |
9 | name
10 | Glyphs plugin keyboard shortcut
11 | tabTrigger
12 | key
13 | uuid
14 | FB9F3769-FC13-4E14-9BCA-50DCF4944E4A
15 |
16 |
17 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs plugin log.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | self.logToConsole(f"${1:method name}: {str(e)}")$0
7 | name
8 | Glyphs plugin log
9 | tabTrigger
10 | log
11 | uuid
12 | 2EC85DB8-6F40-43A9-BDF6-CD9C37C054CF
13 |
14 |
15 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs plugin productPageURL.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | https://github.com/${1:github username}/${2:plugin name}$0
7 | name
8 | Glyphs plugin productPageURL
9 | tabTrigger
10 | url
11 | uuid
12 | 5E4C10DA-0947-4453-91A1-A0FC5CA250EC
13 |
14 |
15 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs plugin updateFeedURL.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | https://raw.githubusercontent.com/${1:github username}/${2:plugin name}/master/$2.${3|glyphsReporter,glyphsFilter,glyphsTool,glyphsFileFormat,glyphsPlugin,glyphsPalette|}/Contents/Info.plist$0
7 | name
8 | Glyphs plugin updateFeedURL
9 | tabTrigger
10 | url
11 | uuid
12 | 2A905466-09E0-429D-BD01-268964BFABCD
13 |
14 |
15 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py UI update.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:from AppKit import NSNotificationCenter}
7 | if ${2:Glyphs.versionNumber>=3 and }${3:Glyphs.font} and $3.currentTab:
8 | NSNotificationCenter.defaultCenter().postNotificationName_object_("GSUpdateInterface", $3.currentTab)
9 | $0
10 | name
11 | Glyphs py UI update
12 | tabTrigger
13 | update
14 | uuid
15 | 9E641F44-77F1-44E9-9BA4-B3805ED59F60
16 |
17 |
18 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py angle.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:import math}
7 |
8 | def angle(firstPoint, secondPoint):
9 | """
10 | Returns the angle (in degrees) of the straight line between firstPoint and secondPoint,
11 | 0 degrees being the second point to the right of first point.
12 | firstPoint, secondPoint: must be NSPoint or GSNode
13 | """
14 | xDiff = secondPoint.x - firstPoint.x
15 | yDiff = secondPoint.y - firstPoint.y
16 | return math.degrees(math.atan2(yDiff, xDiff))
17 | $0
18 | name
19 | Glyphs py angle
20 | tabTrigger
21 | angle
22 | uuid
23 | 041A438A-830E-4E28-96E4-CAC9BC3A0816
24 |
25 |
26 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py circle.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | from Foundation import NSPoint
7 |
8 | def circlePath(center=NSPoint(0,0), radius=50, bcp=4.0*(2.0**0.5-1.0)/3.0):
9 | circle = GSPath()
10 | x = center.x
11 | y = center.y
12 | handle = radius*bcp
13 | points=(
14 | ((x+handle, y-radius), (x+radius, y-handle), (x+radius, y)),
15 | ((x+radius, y+handle), (x+handle, y+radius), (x, y+radius)),
16 | ((x-handle, y+radius), (x-radius, y+handle), (x-radius, y)),
17 | ((x-radius, y-handle), (x-handle, y-radius), (x, y-radius))
18 | )
19 | # Add the segments:
20 | for triplet in points:
21 | # Add the two BCPs of the segment:
22 | bcp1 = NSPoint(triplet[0][0], triplet[0][1])
23 | bcp2 = NSPoint(triplet[1][0], triplet[1][1])
24 | for bcpPosition in (bcp1,bcp2):
25 | newBCP = GSNode()
26 | newBCP.type = OFFCURVE
27 | newBCP.position = bcpPosition
28 | circle.nodes.append(newBCP)
29 |
30 | # Add the On-Curve of the segment:
31 | newCurvepoint = GSNode()
32 | newCurvepoint.type = CURVE
33 | newCurvepoint.smooth = True
34 | newCurvepoint.position = NSPoint(triplet[2][0], triplet[2][1])
35 | circle.nodes.append(newCurvepoint)
36 |
37 | circle.closed = True
38 | return circle
39 | $0
40 | name
41 | Glyphs py circle
42 | tabTrigger
43 | circle
44 | uuid
45 | C9BB77B6-FB1C-48FE-A5C4-522F07D761FC
46 |
47 |
48 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py clear log.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | Glyphs.clearLog() # clears macro window log$0
7 | name
8 | Glyphs py clear log
9 | tabTrigger
10 | clear
11 | uuid
12 | 3DC14F55-B90A-4E03-B97C-96BCD61857CC
13 |
14 |
15 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py create ot class.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | def createOTClass(className = "@default",
7 | classGlyphs = [l.parent.name for l in Glyphs.font.selectedLayers],
8 | targetFont = Glyphs.font):
9 | """
10 | Creates an OpenType class called className in targetFont
11 | containing classGlyphs, or updates it if it already exists.
12 | className: name of the OT class, with or without leading at sign,
13 | classGlyphs: list of glyph names,
14 | targetFont: the GSFont that receives the OT class.
15 | """
16 |
17 | # strip '@' from beginning:
18 | if className[0] == "@":
19 | className = className[1:]
20 |
21 | classCode = " ".join(classGlyphs)
22 |
23 | if className in [c.name for c in targetFont.classes]:
24 | targetFont.classes[className].code = classCode
25 | return "Updated existing OT class ‘{className}’."
26 | else:
27 | newClass = GSClass()
28 | newClass.name = className
29 | newClass.code = classCode
30 | targetFont.classes.append(newClass)
31 | return "Created new OT class: ‘{className}’"
32 |
33 | $0
34 | name
35 | Glyphs py create ot class
36 | tabTrigger
37 | otclass
38 | uuid
39 | E4198030-3DF0-4783-A946-AA926BC6C3C3
40 |
41 |
42 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py create ot feature.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | def updatedCode( oldCode, beginSig, endSig, newCode ):
7 | """Replaces text in oldCode with newCode, but only between beginSig and endSig."""
8 | beginOffset = oldCode.find( beginSig )
9 | endOffset = oldCode.find( endSig ) + len( endSig )
10 | newCode = oldCode[:beginOffset] + beginSig + newCode + "\n" + endSig + oldCode[endOffset:]
11 | return newCode
12 |
13 | def createOTFeature(featureName = "calt",
14 | featureCode = "# empty feature code",
15 | targetFont = Glyphs.font,
16 | codeSig = "CUSTOM-CONTEXTUAL-ALTERNATES"):
17 | """
18 | Creates or updates an OpenType feature in the font.
19 | Returns a status message in form of a string.
20 | featureName: name of the feature (str),
21 | featureCode: the AFDKO feature code (str),
22 | targetFont: the GSFont object receiving the feature,
23 | codeSig: the code signature (str) used as delimiters.
24 | """
25 |
26 | beginSig = "# BEGIN " + codeSig + "\n"
27 | endSig = "# END " + codeSig + "\n"
28 |
29 | if featureName in [ f.name for f in targetFont.features ]:
30 | # feature already exists:
31 | targetFeature = targetFont.features[ featureName ]
32 |
33 | if beginSig in targetFeature.code:
34 | # replace old code with new code:
35 | targetFeature.code = updatedCode( targetFeature.code, beginSig, endSig, featureCode )
36 | else:
37 | # append new code:
38 | targetFeature.code += "\n" + beginSig + featureCode + "\n" + endSig
39 |
40 | return "Updated existing OT feature ‘{featureName}’."
41 | else:
42 | # create feature with new code:
43 | newFeature = GSFeature()
44 | newFeature.name = featureName
45 | newFeature.code = beginSig + featureCode + "\n" + endSig
46 | targetFont.features.append( newFeature )
47 | return "Created new OT feature ‘{featureName}’"
48 |
49 | $0
50 | name
51 | Glyphs py create ot feature
52 | tabTrigger
53 | otfeature
54 | uuid
55 | FDD8912B-3BDA-405D-9878-E007520B3A85
56 |
57 |
58 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py distance.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:import math}
7 |
8 | def distance(node1, node2):
9 | return math.hypot(node1.x - node2.x, node1.y - node2.y)
10 | $0
11 | name
12 | Glyphs py distance
13 | tabTrigger
14 | distance
15 | uuid
16 | 459BCD0B-E20C-4EF8-9768-40F945E24C39
17 |
18 |
19 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py italic.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:import math}
7 | ${2:from Foundation import NSPoint}
8 |
9 | def italicize(thisPoint, italicAngle=0.0, pivotalY=0.0):
10 | """
11 | Returns the italicized position of an NSPoint 'thisPoint'
12 | for a given angle 'italicAngle' and the pivotal height 'pivotalY',
13 | around which the italic slanting is executed, usually half x-height.
14 | Usage: myPoint = italicize(myPoint,10,xHeight*0.5)
15 | """
16 | x = thisPoint.x
17 | yOffset = thisPoint.y - pivotalY # calculate vertical offset
18 | italicAngle = math.radians(italicAngle) # convert to radians
19 | tangens = math.tan(italicAngle) # math.tan needs radians
20 | horizontalDeviance = tangens * yOffset # vertical distance from pivotal point
21 | x += horizontalDeviance # x of point that is yOffset from pivotal point
22 | return NSPoint(x, thisPoint.y)
23 | $0
24 | name
25 | Glyphs py italic
26 | tabTrigger
27 | italic
28 | uuid
29 | 9BAF48FA-2469-4EED-9DA6-A2D40D0A5000
30 |
31 |
32 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py layer offset.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:from Foundation import NSClassFromString}
7 |
8 | def offsetLayer(thisLayer, offset, makeStroke=False, position=0.5, autoStroke=False):
9 | offsetFilter = NSClassFromString("GlyphsFilterOffsetCurve")
10 | try:
11 | # GLYPHS 3:
12 | offsetFilter.offsetLayer_offsetX_offsetY_makeStroke_autoStroke_position_metrics_error_shadow_capStyleStart_capStyleEnd_keepCompatibleOutlines_(
13 | thisLayer,
14 | offset, offset, # horizontal and vertical offset
15 | makeStroke, # if True, creates a stroke
16 | autoStroke, # if True, distorts resulting shape to vertical metrics
17 | position, # stroke distribution to the left and right, 0.5 = middle
18 | None, None, None, 0, 0, False)
19 | except:
20 | # GLYPHS 2:
21 | offsetFilter.offsetLayer_offsetX_offsetY_makeStroke_autoStroke_position_error_shadow_(
22 | thisLayer,
23 | offset, offset, # horizontal and vertical offset
24 | makeStroke, # if True, creates a stroke
25 | autoStroke, # if True, distorts resulting shape to vertical metrics
26 | position, # stroke distribution to the left and right, 0.5 = middle
27 | None, None)
28 | $0
29 | name
30 | Glyphs py layer offset
31 | tabTrigger
32 | offset
33 | uuid
34 | A8D22845-230E-493A-AE0C-6D0B4CF10BAD
35 |
36 |
37 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py layer round.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:from Foundation import NSClassFromString}
7 |
8 | def roundLayer(thisLayer, radius, checkSelection=False, visualCorrection=True, respectGrid=False):
9 | roundFilter = NSClassFromString("GlyphsFilterRoundCorner")
10 | roundFilter.roundLayer_radius_checkSelection_visualCorrect_grid_(
11 | thisLayer, # GSLayer
12 | radius, # float
13 | checkSelection, # bool: if True, only rounds user-selected points
14 | visualCorrection, # bool: visual correction of non-perpendicular angles
15 | respectGrid # bool: whether it the resulting path should stick to the grid
16 | )
17 |
18 | def roundLayerForCompatibility(layer, radius, checkSelection=False, visualCorrect=True, keepCompatible=True, roundToGrid=False):
19 | roundFilter = NSClassFromString("GlyphsFilterRoundCorner")
20 | roundFilter.roundLayer_radius_checkSelection_visualCorrect_forceCompatibilty_grid_(
21 | layer, # GSLayer
22 | radius, # float
23 | checkSelection, # BOOL
24 | visualCorrect, # BOOL
25 | keepCompatible, # BOOL
26 | roundToGrid, # BOOL
27 | )
28 | $0
29 |
30 | name
31 | Glyphs py layer round
32 | tabTrigger
33 | round
34 | uuid
35 | CCE23F72-F832-47BD-BBBF-5363201EC30A
36 |
37 |
38 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py macro window.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | # brings macro window to front and clears its log:
7 | Glyphs.clearLog()
8 | Glyphs.showMacroWindow()
9 | ${1:print "${2:Output}:"}$0
10 | name
11 | Glyphs py macro window
12 | tabTrigger
13 | macro
14 | uuid
15 | E771FC7C-50E6-4CAA-A906-33A680C1F8E3
16 |
17 |
18 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py measurement.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | def intersectionsBetweenPoints(thisLayer, startPoint, endPoint):
7 | """
8 | Returns list of intersection NSPoints from startPoint to endPoint.
9 | thisLayer ... a glyph layer
10 | startPoint, endPoint ... NSPoints
11 | """
12 |
13 | # prepare layer copy for measurement:
14 | cleanLayer = thisLayer.copyDecomposedLayer()
15 | cleanLayer.removeOverlap()
16 |
17 | # measure and return tuple:
18 | listOfIntersections = cleanLayer.intersectionsBetweenPoints(startPoint, endPoint)
19 | return listOfIntersections
20 | $0
21 | name
22 | Glyphs py measurement
23 | tabTrigger
24 | measurement
25 | uuid
26 | 104B4B66-F24F-4436-A302-444A7B72F1A0
27 |
28 |
29 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py menutitle.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | #MenuTitle: ${1:$TM_FILENAME}
7 | # -*- coding: utf-8 -*-
8 | from __future__ import division, print_function, unicode_literals
9 | __doc__="""
10 | ${2:$1}.
11 | """
12 | $0
13 | name
14 | Glyphs py menutitle
15 | tabTrigger
16 | title
17 | uuid
18 | 3CCCB797-92CC-4A32-B2C1-53BCB7B595F3
19 |
20 |
21 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py msg.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | Message(
7 | title="${1:Script Error}",
8 | message="${2:An error occurred during the execution of the script.}",
9 | OKButton=${3|None,"Apply"|},
10 | )$0
11 | name
12 | Glyphs py msg
13 | tabTrigger
14 | msg
15 | uuid
16 | 4982A23A-493D-4FFA-8036-8CFBC6CB2548
17 |
18 |
19 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py notification.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | # Floating notification:
7 | Glyphs.showNotification(
8 | "$1{thisFont.familyName$1}",
9 | ${3:"${2:Script is finished.}",}
10 | )$0
11 |
12 | name
13 | Glyphs py notification
14 | tabTrigger
15 | notification
16 | uuid
17 | CBFD7759-0A45-4AA7-BDCA-C75CB056B9CA
18 |
19 |
20 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py path intersect.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | paths = ${1:[Layer.paths[2]]} # list of original paths
7 | intersectPaths = ${2:[Layer.paths[0], Layer.paths[1]]} # list of paths to be intersected with original paths
8 | pathOperator = GSPathOperator.alloc().init() # needs to be initialized only once
9 | pathOperator.intersectPaths_from_error_(intersectPaths, paths, None)
10 | ${3:thisLayer.paths} = paths # result of boolean operation is in paths
11 | $0
12 | name
13 | Glyphs py path intersect
14 | tabTrigger
15 | intersect
16 | uuid
17 | FF4C5BEA-2BCC-48FE-82CB-3BCA5663EEC8
18 |
19 |
20 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py path subtract.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | paths = ${1:[Layer.paths[2]]} # list of original paths
7 | subtractPaths = ${2:[Layer.paths[0], Layer.paths[1]]} # list of paths to be subtracted from original paths
8 | pathOperator = GSPathOperator.alloc().init() # needs to be initialized only once
9 | pathOperator.subtractPaths_from_error_debug_(subtractPaths, paths, None, False)
10 | ${3:thisLayer.paths} = paths # result of boolean operation is in paths
11 | $0
12 | name
13 | Glyphs py path subtract
14 | tabTrigger
15 | subtract
16 | uuid
17 | B2070AD7-1990-4927-9C70-F9E1C5DDADB6
18 |
19 |
20 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py report.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | #MenuTitle: ${1:$TM_FILENAME}
7 | # -*- coding: utf-8 -*-
8 | from __future__ import division, print_function, unicode_literals
9 | __doc__="""
10 | Opens a new Edit tab containing all glyphs ${2:exceeding the ascender height.}
11 | """
12 |
13 | def isLayerAffected(thisLayer):
14 | ascender = thisLayer.master.ascender
15 | for thisPath in thisLayer.paths:
16 | for thisNode in thisPath.nodes:
17 | if thisNode.y > ascender:
18 | return True$0
19 | return False
20 |
21 | Glyphs.clearLog() # clears log of Macro window
22 | thisFont = Glyphs.font # frontmost font
23 | affectedLayers = []
24 | for thisGlyph in thisFont.glyphs: # loop through all glyphs
25 | for thisLayer in thisGlyph.layers: # loop through all layers
26 | print("Analysing {thisGlyph.name}") # report status in Macro window
27 | # collect affected layers:
28 | if isLayerAffected(thisLayer):
29 | affectedLayers.append(thisLayer)
30 |
31 | # open a new tab with the affected layers:
32 | if affectedLayers:
33 | newTab = thisFont.newTab()
34 | newTab.layers = affectedLayers
35 | # otherwise send a message:
36 | else:
37 | Message(
38 | title = "Nothing Found",
39 | message = "Could not find any glyphs $2",
40 | OKButton = None
41 | )
42 |
43 |
44 | name
45 | Glyphs py report
46 | tabTrigger
47 | gspy
48 | uuid
49 | 519D1078-9594-4037-876B-6813C2010F98
50 |
51 |
52 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py tab with layers.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | # opens new Edit tab:
7 | newTab = ${1:thisFont}.newTab()
8 | newTab.layers = ${2:$1.selectedLayers}$0
9 |
10 | name
11 | Glyphs py tab with layers
12 | tabTrigger
13 | tab
14 | uuid
15 | E44AAE2E-F6AE-4E8B-8891-8488DC5CC5E5
16 |
17 |
18 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py tab with text.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | # opens new Edit tab:
7 | ${1:thisFont}.newTab(${2:"abc"})$0
8 |
9 | name
10 | Glyphs py tab with text
11 | tabTrigger
12 | tab
13 | uuid
14 | 6362E8C7-4931-4011-8782-B91FC63DFB53
15 |
16 |
17 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py transform.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:import math
7 | }${2:from AppKit import NSAffineTransform, NSAffineTransformStruct
8 | }
9 | def transform(shiftX=0.0, shiftY=0.0, rotate=0.0, skew=0.0, scale=1.0):
10 | """
11 | Returns an NSAffineTransform object for transforming layers.
12 | Apply an NSAffineTransform t object like this:
13 | Layer.transform_checkForSelection_doComponents_(t,False,True)
14 | Access its transformation matrix like this:
15 | tMatrix = t.transformStruct() # returns the 6-float tuple
16 | Apply the matrix tuple like this:
17 | Layer.applyTransform(tMatrix)
18 | Component.applyTransform(tMatrix)
19 | Path.applyTransform(tMatrix)
20 | Chain multiple NSAffineTransform objects t1, t2 like this:
21 | t1.appendTransform_(t2)
22 | """
23 | myTransform = NSAffineTransform.transform()
24 | if rotate:
25 | myTransform.rotateByDegrees_(rotate)
26 | if scale != 1.0:
27 | myTransform.scaleBy_(scale)
28 | if not (shiftX == 0.0 and shiftY == 0.0):
29 | myTransform.translateXBy_yBy_(shiftX,shiftY)
30 | if skew:
31 | myTransform.shearBy_(math.tan(math.radians(skew)))
32 | return myTransform
33 | $0
34 | name
35 | Glyphs py transform
36 | tabTrigger
37 | transform
38 | uuid
39 | B5109A06-D1AA-4F24-BC46-27EFDE50B31E
40 |
41 |
42 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs py.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | #MenuTitle: ${1:$TM_FILENAME}
7 | # -*- coding: utf-8 -*-
8 | from __future__ import division, print_function, unicode_literals
9 | __doc__="""
10 | ${2:Create effect for selected glyphs.}
11 | """
12 | ${3:
13 | import random
14 | random.seed()
15 | }
16 | def process(thisLayer):
17 | for thisPath in thisLayer.paths:
18 | if thisPath.closed:
19 | print("- closed path:")
20 | else:
21 | print("- open path:")
22 | for thisNode in thisPath.nodes:
23 | print(f"-- node {thisNode.x:.1f} {thisNode.y:.1f} (type {thisNode.type})")
24 | for thisComponent in thisLayer.components:
25 | print(f"- component {thisComponent.componentName} at {thisComponent.position.x:.1f} {thisComponent.position.y:.1f}")
26 | for thisAnchor in thisLayer.anchors:
27 | print(f"- anchor {thisAnchor.name} at {thisAnchor.x:.1f} {thisAnchor.y:.1f}")
28 | $0
29 |
30 | thisFont = Glyphs.font # frontmost font
31 | ${4:thisFontMaster = thisFont.selectedFontMaster # active master
32 | }selectedLayers = thisFont.selectedLayers # active layers of selected glyphs
33 | ${5:selection = selectedLayers[0].selection # node selection in edit mode
34 | }Glyphs.clearLog() # clears log in Macro window
35 |
36 | ${6:thisFont.disableUpdateInterface() # suppresses UI updates in Font View
37 | }try:
38 | for thisLayer in selectedLayers:
39 | thisGlyph = thisLayer.parent
40 | print(f"Processing {thisGlyph.name}")
41 | ${7: thisGlyph.beginUndo() # begin undo grouping
42 | } process(thisLayer)
43 | ${8: thisGlyph.endUndo() # end undo grouping
44 | }except Exception as e:
45 | Glyphs.showMacroWindow()
46 | print("\n⚠️ Error in script: $1\n")
47 | import traceback
48 | print(traceback.format_exc())
49 | print()
50 | raise e
51 | ${9:finally:
52 | thisFont.enableUpdateInterface() # re-enables UI updates in Font View}
53 |
54 | name
55 | Glyphs py
56 | tabTrigger
57 | gspy
58 | uuid
59 | E578EB41-51AC-4101-8DF3-034F6E9CE6B4
60 |
61 |
62 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/Glyphs2:3 if:then.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | if Glyphs.versionNumber >= 3:
7 | # GLYPHS 3
8 | ${1:$TM_SELECTED_TEXT}
9 | else:
10 | # GLYPHS 2
11 | ${2:$TM_SELECTED_TEXT}
12 | $0
13 | keyEquivalent
14 | ^~@G
15 | name
16 | Glyphs2/3 if/then
17 | tabTrigger
18 | g23
19 | uuid
20 | 638A98AB-F9D5-494A-81A6-62509B3E8A7B
21 |
22 |
23 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/PyObjC Decorators.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1|@objc.python_method,@IBAction,@IBOutlet|}$0
7 | name
8 | PyObjC Decorators
9 | tabTrigger
10 | dec
11 | uuid
12 | AF2326C0-21FD-4747-8D59-3DB45C6EFFB1
13 |
14 |
15 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py NSColor RGB.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:from AppKit import NSColor}
7 | ${2:thisColor} = NSColor.colorWithRed_green_blue_alpha_(${3:1.0}, ${4:0.1}, ${5:0.3}, ${6:0.3})$0
8 | name
9 | py NSColor RGB
10 | tabTrigger
11 | rgb
12 | uuid
13 | 8A8ADA16-81EC-4086-B142-E0EE21D61B77
14 |
15 |
16 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py NSEvent Modifier Keys.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${2:from AppKit import ${1:NSEvent}}
7 |
8 | keysPressed = NSEvent.modifierFlags()
9 | ${3:optionKey = 524288
10 | }${5:commandKey = 1048576
11 | }${7:ctrlKey = 262144
12 | }${9:fnKey = 8388608
13 | }${11:shiftKey = 131072
14 | }${4:optionKeyPressed = keysPressed & optionKey == optionKey
15 | }${6:commandKeyPressed = keysPressed & commandKey == commandKey
16 | }${8:ctrlKeyPressed = keysPressed & ctrlKey == ctrlKey
17 | }${10:fnKeyPressed = keysPressed & fnKey == fnKey
18 | }${12:shiftKeyPressed = keysPressed & shiftKey == shiftKey
19 | }$0
20 | name
21 | py NSEvent Modifier Keys
22 | tabTrigger
23 | keys
24 | uuid
25 | 056A298C-4F4C-4632-B496-BA32852DD41F
26 |
27 |
28 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py abc.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | from string import ascii_uppercase, ascii_lowercase
7 | alphabet = ascii_uppercase+ascii_lowercase
8 | $0
9 | name
10 | py abc
11 | tabTrigger
12 | abc
13 | uuid
14 | AB4509EC-47B1-4301-963C-54AFCB3E19BC
15 |
16 |
17 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py bezier.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | def bezier(x1,y1, x2,y2, x3,y3, x4,y4, t):
7 | """
8 | Returns coordinates for t (=0.0...1.0) on curve segment.
9 | x1,y1 and x4,y4: coordinates of on-curve nodes
10 | x2,y2 and x3,y3: coordinates of BCPs
11 | """
12 | x = x1*(1-t)**3 + x2*3*t*(1-t)**2 + x3*3*t**2*(1-t) + x4*t**3
13 | y = y1*(1-t)**3 + y2*3*t*(1-t)**2 + y3*3*t**2*(1-t) + y4*t**3
14 |
15 | return x, y
16 |
17 | name
18 | py bezier
19 | tabTrigger
20 | bezier
21 | uuid
22 | A92363FC-E425-419A-8F0B-452C9603E8BC
23 |
24 |
25 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py bezierQ.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | def bezierQ(x1,y1, x2,y2, x3,y3, t):
7 | """
8 | Returns coordinates for t (=0.0...1.0) on curve segment.
9 | x1,y1 and x3,y3: coordinates of (implied) on-curve nodes
10 | x2,y2: coordinates of off-curve
11 | """
12 | x = x1*(1-t)**2 + x2*2*t*(1-t) + x3*t**2
13 | y = y1*(1-t)**2 + y2*2*t*(1-t) + y3*t**2
14 |
15 | return x, y
16 |
17 |
18 | name
19 | py bezierQ
20 | tabTrigger
21 | bezier
22 | uuid
23 | 51956992-5760-496A-8919-2812C33CE539
24 |
25 |
26 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py center of NSRect.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:from Foundation import NSPoint, NSRect
7 | }def centerOfRect(rect):
8 | """
9 | Returns the center of NSRect rect as an NSPoint.
10 | """
11 | center = NSPoint(rect.origin.x + rect.size.width/2, rect.origin.y + rect.size.height/2)
12 | return center
13 | $0
14 | name
15 | py center of NSRect
16 | tabTrigger
17 | center
18 | uuid
19 | FDE9D8DD-19CD-450C-A3CE-9837FA2A419B
20 |
21 |
22 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py clipboard.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:from AppKit import NSPasteboard, NSStringPboardType
7 | }
8 | def setClipboard(myText):
9 | """
10 | Sets the contents of the clipboard to myText.
11 | Returns True if successful, False if unsuccessful.
12 | """
13 | try:
14 | myClipboard = NSPasteboard.generalPasteboard()
15 | myClipboard.declareTypes_owner_([NSStringPboardType], None)
16 | myClipboard.setString_forType_(myText, NSStringPboardType)
17 | return True
18 | except Exception as e:
19 | return False
20 |
21 | ${2:if not setClipboard(${3:"clipboard text"}):
22 | print("Warning: could not set clipboard to {$3}")
23 | $0
24 | name
25 | py clipboard
26 | tabTrigger
27 | clipboard
28 | uuid
29 | 79CAECE7-25C2-4F7A-A2DD-72152006D94F
30 |
31 |
32 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py future imports.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | from __future__ import division, print_function, unicode_literals
7 | $0
8 | name
9 | py future imports
10 | tabTrigger
11 | fut
12 | uuid
13 | 4D66C425-84C1-42D7-A734-7151EF8422AF
14 |
15 |
16 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py intersect two lines.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:from Foundation import NSPoint
7 | }
8 | def intersect(pointA, pointB, pointC, pointD):
9 | """
10 | Returns an NSPoint of the intersection AB with CD,
11 | or None if there is no intersection.
12 | pointA, pointB: NSPoints or GSNodes representing the first line AB,
13 | pointC, pointD: NSPoints or GSNodes representing the second line CD.
14 | """
15 | xA, yA = pointA.x, pointA.y
16 | xB, yB = pointB.x, pointB.y
17 | xC, yC = pointC.x, pointC.y
18 | xD, yD = pointD.x, pointD.y
19 |
20 | try:
21 | slopeAB = (float(yB) - float(yA)) / (float(xB) - float(xA))
22 | except:
23 | slopeAB = None # division by zero if vertical
24 |
25 | try:
26 | slopeCD = (float(yD) - float(yC)) / (float(xD) - float(xC))
27 | except:
28 | slopeCD = None # division by zero if vertical
29 |
30 | if slopeAB == slopeCD: # parallel, no intersection
31 | return None
32 | elif slopeAB is None: # first line is vertical
33 | x = xA
34 | y = slopeCD * (x - xC) + yC
35 | elif slopeCD is None: # second line is vertical
36 | x = xC
37 | y = slopeAB * (x - xA) + yA
38 | else: # both lines have different angles other than vertical
39 | x = (slopeAB * xA - yA - slopeCD * xC + yC) / (slopeAB - slopeCD)
40 | y = slopeAB * (x - xA) + yA
41 |
42 | return NSPoint(x, y)
43 | $0
44 | name
45 | py intersect two lines
46 | tabTrigger
47 | intersect
48 | uuid
49 | 73E8D678-EE48-4BA2-B7CC-BDB76113E635
50 |
51 |
52 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py random.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | import random
7 | random.seed()
8 | ${1:randomNumber = random.randint(-100, 100)
9 | }$0
10 | name
11 | py random
12 | tabTrigger
13 | random
14 | uuid
15 | E2A706E9-ADC6-4265-A39C-E80784BC11BB
16 |
17 |
18 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py sort.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | sorted(${1:[ ${2:[l.parent.name, l.bounds.origin.y]} for l in selectedLayers]}, key=lambda thisListItem: ${3:thisListItem[1]})$0
7 | name
8 | py sort
9 | tabTrigger
10 | sort
11 | uuid
12 | 5225E965-C129-4C77-A7FE-C667F7D45566
13 |
14 |
15 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py terminal.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:import subprocess}
7 | command = '${2:uptime}'
8 | commandArgs = [${3/.+/"/}${3:~/Library/Application Support/Glyphs/Scripts}${3/.+/"/}] # arguments (may contain spaces) as lists of strings
9 | commandExecution = subprocess.Popen(command.split(" ")+commandArgs, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
10 | output, error = commandExecution.communicate()
11 | ${4:print("RETURNCODE (0=OK): {commandExecution.returncode}")}
12 | if output:
13 | ${5:print("\nOUTPUT:\n{output}")}
14 | if error:
15 | ${6:print("\nERROR:\n{error}")}
16 | $0
17 |
18 | name
19 | py terminal
20 | tabTrigger
21 | terminal
22 | uuid
23 | 35EC5588-93F3-4657-AA2D-B5F7D7F667FB
24 |
25 |
26 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py timer.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | from timeit import default_timer as timer
7 |
8 | def reportTimeInNaturalLanguage( seconds ):
9 | if seconds > 60.0:
10 | timereport = "{seconds//60}:{seconds%60:02i} minutes"
11 | elif seconds < 1.0:
12 | timereport = "{seconds:.2f} seconds"
13 | elif seconds < 20.0:
14 | timereport = "{seconds:.1f} seconds"
15 | else:
16 | timereport = "{seconds} seconds"
17 | return timereport
18 |
19 | ${2:# start taking time:
20 | start = timer()
21 |
22 | # take time:
23 | end = timer()
24 | timereport = reportTimeInNaturalLanguage(end - start)
25 | ${1:print("Time elapsed: {timereport}.")}
26 | }$0
27 | name
28 | py timer
29 | tabTrigger
30 | timer
31 | uuid
32 | 3BD03C70-490F-478A-99C7-1B2E8945D09E
33 |
34 |
35 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py traceback.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:import traceback
7 | }print(traceback.format_exc())$0
8 | name
9 | py traceback
10 | tabTrigger
11 | trace
12 | uuid
13 | 2C753F76-767A-45FD-90D0-42C68659B23D
14 |
15 |
16 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/py write file.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | ${1:import codecs
7 | }def saveFileInLocation(content="", fileName="test.txt", filePath="~/Desktop"):
8 | saveFileLocation = f"{filePath}/{fileName}"
9 | saveFileLocation = saveFileLocation.replace( "//", "/" )
10 | with codecs.open(saveFileLocation, "w", "utf-8-sig") as thisFile:
11 | print(f"Exporting to: {thisFile.name}")
12 | thisFile.write(content)
13 | thisFile.close()
14 | return True
15 |
16 | name
17 | py write file
18 | tabTrigger
19 | write
20 | uuid
21 | F8A9B749-480B-480A-A181-5755C93F34E6
22 |
23 |
24 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/Snippets/vanilla help.tmSnippet:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | content
6 | import vanilla
7 | help(vanilla.${1|ActionButton,Box,Button,CheckBox,CheckBoxListCell,ColorWell,ComboBox,DatePicker,Drawer,EditText,FloatingWindow,GradientButton,Group,HUDFloatingWindow,HelpButton,HorizontalLine,ImageButton,ImageListCell,ImageView,LevelIndicator,LevelIndicatorListCell,List,ObjectBrowser,PathControl,PopUpButton,PopUpButtonListCell,Popover,ProgressBar,ProgressSpinner,RadioGroup,ScrollView,SearchBox,SecureEditText,SegmentedButton,SegmentedButtonListCell,Sheet,Slider,SliderListCell,SplitView,SplitView2,SquareButton,Tabs,TextBox,TextEditor,VanillaBaseControl,VanillaBaseObject,VanillaError,VerticalLine,Window|})
8 | $0
9 | name
10 | vanilla help
11 | tabTrigger
12 | vanilla
13 | uuid
14 | 240EFB4E-E02A-45FD-AA0F-BDB148FBD2BE
15 |
16 |
17 |
--------------------------------------------------------------------------------
/TextMate/Python for Glyphs.tmbundle/info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | contactEmailRot13
6 | erf ng tylcufncc qbg pbz
7 | contactName
8 | Rainer Erich Scheichelbauer
9 | description
10 | Python snippets for Glyphs.app coding.
11 | http://glyphsapp.com/
12 | https://github.com/mekkablue/Python-for-Glyphs
13 |
14 | name
15 | Python for Glyphs
16 | uuid
17 | 9A5B7518-7352-4AE4-8CD9-9F16EF3B825D
18 |
19 |
20 |
--------------------------------------------------------------------------------