Scripting Glyphs, part 1
This is the first of a series of Python tutorials. No prior knowledge required. In this first installment, we’ll make our first steps in the Macro window.
This multi-part tutorial will get you started with Python. You can read through it all in one morning, have lunch, and write your first scripts in the afternoon of the same day. Have fun!
The macro panel
Go to the Window menu and choose Macro Panel (Opt-Cmd-M). You’ll get a window like this:
The upper half is for you to write Python code in. In the lower half, Glyphs can react and answer to your code. You can drag the horizontal separator line to a position you’re comfortable with. Alright, let’s give Glyphs something to react to. Start by typing this line into the upper half:
print( "Hello World!" )
Uppercase/lowercase is important, so write print
, not Print
.
It is crucial to open the brackets after print
command. The quotes are just straight quotes (actually inch signs, typographically speaking), no curly quotes. The space between print en the straight quotes is not necessary, but the closing quote is. You can use single '...'
or double "..."
quotes.
Click on the Run button (Cmd-Return, fn-Return, or Enter on extended keyboards) and you should see something like this:
Strings, integers, floats and objects
Now, the print
command makes Python print something in the result area. Whatever is in between this command's brackets is being printed. Text needs to be enclosed within quotes. Programmers call this a string as opposed to a number.
Speaking of numbers, you can have Python print the result of any calculation. Python differentiates between whole numbers (‘integers’) and decimal numbers (‘floats’), and will keep the result in integers or decimals depending of what you entered. 5 divided by 2 will result in 2, but 5.0 divided by 2.0 will give you 2.5:
If you click on Run (Cmd-Return) a couple of times, you’ll see the result area cluttering up quickly. Click Clear (Cmd-K) to wipe the slate clean.
Objects
Now, let’s have Python print a few other things. Try this:
print( Glyphs )
The result is not very surprising. But Glyphs
is neither a string nor a number calculation, but what we call an object. Now, Glyphs
is an object that contains other objects. To refer to those sub-objects, we add a period to Glyphs
and the name of that sub-object. Try this:
print( Glyphs.fonts )
print( Glyphs.font )
And Python will print the list of currently opened fonts between brackets (the result of print Glyphs.fonts
), plus the frontmost font (the result of print Glyphs.font
). If you have no fonts open, you’ll receive an empty list (brackets with nothing in between) and None
as answer:
Lists
Open a few fonts, go back to the Macro Panel and click Clear (Cmd-K) and Run (Cmd-Return) again. Bring different fonts to the front and see what happens if you run your code again.
So, Glyphs.fonts returns a list of fonts. You can access the individual fonts in the list by adding digits in brackets, starting at 0, like this:
print( Glyphs.fonts[0] ) # 1st open font, same as Glyphs.font
print( Glyphs.fonts[1] ) # 2nd open font
Oh, yes: the number sign #
marks a comment. Everything following it will be ignored. It’s a good idea to insert descriptive comments into your code. If you try to fix or adapt your script years later, you will thank yourself for your commentary. Really.
And you probably guessed it already: You can use Glyphs.font
as a short cut for Glyphs.fonts[0]
.
And as you can imagine, even a font has sub-objects. Again, we can address those by adding a period and the object name. Try this:
print( Glyphs.font.glyphs )
You can access individual glyphs by adding either their index number or their name in brackets. Try these:
print( Glyphs.font.glyphs[7] )
print( Glyphs.font.glyphs["a"] )
Again, we can add a period and access whatever information a glyph contains. Try these:
print( Glyphs.font.glyphs["a"].name )
print( Glyphs.font.glyphs["a"].category )
print( Glyphs.font.glyphs["a"].subCategory )
print( Glyphs.font.glyphs["a"].unicode )
Remember, you can use Copy and Paste. You don’t have to retype every line.
Variables
Sure enough, this is a pretty inefficient way of accessing glyph info. If we want the same info about b, we’d have to change a to b in four lines. So let’s introduce a variable:
glyphname = "b"
print( Glyphs.font.glyphs[glyphname].name )
print( Glyphs.font.glyphs[glyphname].category )
print( Glyphs.font.glyphs[glyphname].subCategory )
print( Glyphs.font.glyphs[glyphname].unicode )
Add a end=" "
after the comma to the end of a print command if you want to avoid a linebreak and keep all the info on one line:
glyphname = "b"
print( Glyphs.font.glyphs[glyphname].name, end=" " )
print( Glyphs.font.glyphs[glyphname].category, end=" " )
print( Glyphs.font.glyphs[glyphname].subCategory, end=" " )
print( Glyphs.font.glyphs[glyphname].unicode, end=" " )
Now, programming is about not having to retype everything. We should let Python do the repetitive stuff. You see the stuff that stays the same in all lines? Let’s add another variable for that and perhaps a blank line for clarity:
glyphname = "b"
myGlyph = Glyphs.font.glyphs[glyphname]
print( myGlyph.name, end=" " )
print( myGlyph.category, end=" " )
print( myGlyph.subCategory, end=" " )
print( myGlyph.unicode )
All we need to change now is the glyph name in the first line.
Looping
But let’s say, we want those infos for every glyph in the font, what do we do? Sure, we could copy and paste the whole block a hundred times and change the glyph name everywhere. But that’s boring for non-robots, so let’s have Python do that for us:
for myGlyph in Glyphs.font.glyphs:
print( myGlyph.name, end=" " )
print( myGlyph.category, end=" " )
print( myGlyph.subCategory, end=" " )
print( myGlyph.unicode )
Note that all the print
lines are indented. You can either use a certain number of spaces or a tab, as long as all the lines are indented the same way. Python is pretty picky about how you indent, so decide for one style and stick to it. I like tabs, so I’ll continue that way in the upcoming examples.
Python steps through all the glyphs, and prints an info line each time. Hey! This is, like, the first result in this tutorial we can actually use for something. We can, for instance, copy it and paste it into an e-mail for a status report. Let’s make it a little more versatile, get rid of all but one print
command and squish our code into two lines:
for myGlyph in Glyphs.font.glyphs:
print( myGlyph.name, myGlyph.category, myGlyph.subCategory, myGlyph.unicode )
Now, we want our glyph info report for all open fonts. Luckily, we can nest indentations. How about this:
for myFont in Glyphs.fonts:
print()
print( "Report for:", myFont.familyName )
for myGlyph in myFont.glyphs:
print( myGlyph.name, myGlyph.category, myGlyph.subCategory, myGlyph.unicode )
Want your report to contain other info? Head on over to the Glyphs Python Documentation and see what else the Glyph object (‘GSGlyph’) and the Font object (‘GSFont’) can do for you.
In the next part, we’ll dig a little deeper into GSGlyph and drill all the way down to the nodes.
Update 2014-10-04: added shortcut Glyphs.font, and link to part 2.
Update 2015-07-30: updated Screenshots for Glyphs 2.
Update 2016-12-08: updated screenshots, fixed formatting, use Glyphs.font instead of Glyphs.currentDocument.
Update 2016-12-09: added first paragraph.
Update 2020-12-02: updated for Python3.
Update 2022-18-08: updated title, minor formatting changes, related article.
Update 2022-09-23: fixed a typo (thx Kaliata).