Scripting Glyphs, part 1

Tutorial
by Rafał Buchner & Rainer Scheichelbauer
en zh

16 January 2026 Published on 22 June 2012

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!

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.

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. Click into the upper half, delete everything that is there (select all and delete), and type:

print("Hello World!")

This is what we call a function. Functions do things. You know it is a function because it is a word followed by parentheses, in our case print(). Inside the parentheses, a function can have either nothing or something. If there is something, in our case it is "Hello World!", we call it the argument of the function. We say the argument is ‘passed’ to the function, and the function can then process the argument.

Good to know: Python is case-sensitive, i.e., uppercase vs. lowercase is important, so write print(), not Print(). The quotes are just straight dumb quotes, they can be either single quotes '...' or double quotes "...", but never ever curly (a.k.a. ‘typographic’) quotes. Every time you open a quote, you need to close it again with the same quote.

Remember I said that functions do things. Let’s see what this one does. Click on the Run button (Cmd-Return, fn-Return, or Enter on extended keyboards) and you should see something like this:

str, int, float, and operators

Now, the print() function makes Python print something in the result area. Whatever is in between the parentheses is being reproduced below the separator. Programmers call the text enclosed within quotes a string, usually shortened to str in typing, because, well, we are a lazy bunch and hate typing too much.

‘String’ is a funny word for text, I’ll grant you that. The individual characters H, e, l, and so on, are put on a Python string just like pearls on a real-world string. Each of them takes up a specific fixed position, so the pearls (or characters) have a distinct order. There are other kinds of collections in Python where order does not matter. But we’ll cross the bridge when we get there.

But you can not only print strings, you can also print numbers. What’s more, you can have Python print the result of any calculation. Python differentiates between whole numbers (‘integers’, typed int) and decimal numbers (‘floating-point numbers’, typed float). For calculations, use the operators + for addition, the hyphen - for subtraction, the slash / for division, and the asterisk * for multiplication.

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. Objects can be anything. In our world, the app, fonts, glyphs, components, paths, nodes and anchors are all different kinds of objects. Now, Glyphs is an object that contains other objects. To refer to those sub-objects a.k.a. properties, we add a period to Glyphs and the name of that sub-object. Try this:

print(Glyphs.fonts)
print(Glyphs.font)

And Python will list the currently opened fonts between parentheses, i.e., the result of print(Glyphs.fonts); plus the frontmost font, which happens to be the result of print(Glyphs.font). If you have no fonts open, you’ll receive an empty list (parentheses with nothing in between) and None as answer:

Collections

Open a few fonts, go back to Window > 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 collection of fonts. Because it is an ordered collection, you can access the individual fonts in the collection by adding digits in square brackets, starting at 0, like this:

print(Glyphs.fonts[0]) # 1st open font, same as Glyphs.font
print(Glyphs.fonts[1]) # 2nd open font

Yes, unlike humans, computers start counting at zero. That’s just how they are wired. You’ll need to get used to it.

And yes: the hashtag a.k.a. number sign a.k.a. octothorpe # marks a comment. Everything following it on the same line 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 shortcut for Glyphs.fonts[0].

And as you can imagine, even a font has sub-objects (properties). 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 (starting at zero again) or their glyph name in brackets. Try these:

print(Glyphs.font.glyphs[7])
print(Glyphs.font.glyphs["a"])

Good to know: When there is a number in the square brackets after a collection, we call it an index. When it is a string, we call it a key. Within the objects of Glyphs.app, you know an object is a collection of other objects when its name is a plural word, e.g., fonts.

Again, we can add another 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)

For this, we use the single equals sign = a.k.a. the assignment operator. It takes the word on its left side, creates a variable in its name, and assigns it the value on the right side of the equals sign.

If you want all the data on one line, open the parenthesis, and add all the bits and pieces with commas between them before you close the parenthesis. And while we are at it, here’s a great trick for better code legibility. Between parentheses, you can add linebreaks and indents to your heart’s content, e.g., like this:

glyphName = "b"
print(
    Glyphs.font.glyphs[glyphName].name,
    Glyphs.font.glyphs[glyphName].category,
    Glyphs.font.glyphs[glyphName].subCategory,
    Glyphs.font.glyphs[glyphName].unicode,
    )

Good to know: The final comma is optional. It is, however, a good idea to keep it because that will save you some trouble when you want to reorder the bits and pieces at a later time.

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, myGlyph, for that:

glyphName = "b"
myGlyph = Glyphs.font.glyphs[glyphName]
print(
    myGlyph.name,
    myGlyph.category,
    myGlyph.subCategory,
    myGlyph.unicode,
    )

Good to know: variable names are arbitrary, but need to start with a letter. Afterwards you can also use figures 1234567890 and underscores _ if you like. Some people like to keep it short and just use single letters, like n for the name and g for the glyph. Good for typing, but bad for legibility. That is why I recommend descriptive variable names. Prefixes like my or this are popular for making clear that it is a variable and that it does not collide with a reserved word, like the name of a function. E.g., myPrint is a variable name, but print is the function print() misspelled, because it lacks its parentheses and will cause an error.

Now our code is compact enough that we can move the glyph properties back on a single line:

glyphName = "b"
myGlyph = Glyphs.font.glyphs[glyphName]
print(myGlyph.name, myGlyph.category, myGlyph.subCategory, myGlyph.unicode)

The comma after .unicode is still optional. That is why I left it out here. Looks neater if all that stuff is on a single line.

If we want to access the info for other glyphs, all we need to change now is the glyph name in the first line, and run the script again.

Looping

But let’s be honest, why should we do the changing of the glyph name? In other words, if 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 us non-robots, so let’s have Python do that for us:

for myGlyph in Glyphs.font.glyphs:
    print(myGlyph.name, myGlyph.category, myGlyph.subCategory, myGlyph.unicode)

This is called a loop or iteration. This is where the real power of programming lies, because you can loop (or iterate) over any collection of data, and thus quickly process huge amounts of data.

The word for is not followed by parentheses. So it is clearly not a function, but a statement. After for comes the loop variable, in this case myGlyph. It is dynamically reassigned for every piece of the collection that comes after the statement in, in our case that collection is Glyphs.font.glyphs, which is our expression for every glyph in the frontmost font. The for line ends with a colon :, announcing the content of the loop.

That content is the print() line. Note how it is indented. You can either use a certain number of spaces or a tab, as long as you always indent the same way. Python is pretty picky about how you indent (unless it is between parentheses like above), so decide for one style and stick to it. Personally, I like tabs because it puts the tab key in the top left of my Mac keyboard to good use. So I’ll continue with tab indentation in the upcoming examples.

Now, back to the code, and the inner workings of our first loop: the for...in statement makes Python reassign the loop variable myGlyph again and again, for every glyph in Glyphs.font.glyphs. And every time it gets reassigned, the indented code gets executed, with myGlyph containing a new value.

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 and paste our code into an e-mail or instant message for enabling our poor non-programming type designer friends to create a status report for their fonts.

Nested loops

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("Report for:", myFont.familyName)
    for myGlyph in myFont.glyphs:
        print(myGlyph.name, myGlyph.category, myGlyph.subCategory, myGlyph.unicode)
    print()

Note the plural .fonts: at the end of the first for line, denoting the collection of all currently opened font files. Needless to say we will iterate, or loop, over the fonts, with the indented code that follows. The first indented line with the print() function just outputs a report message with the family name of the current font in our iteration. The next indented line, i.e., the second for line, however, initiates a second, nested loop. A nested loop is a loop inside another loop. In our case, we loop over every glyph of whatever is the current font in the first, outer loop. And in the inner, or nested loop, we have the same code as before, just double-indented.

See how we move back to a single indent for the final print() function? Single indent means that the code is part of the first, outer loop over the fonts. So, once we are done with looping over all the glyphs of that font, print() will print an empty line, and we’ll move on to the next font in the loop.

Want to customize your script, and report other info? Head on over to the Glyphs Python Documentation and see what else the glyph object (spelled GSGlyph) and the font object (spelled GSFont) can do for you. Look for their properties, and hang them with a dot after your glyph variable or font variable, respectively.

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-08-18: updated title, minor formatting changes, related article.
Update 2022-09-23: fixed a typo (thx Kaliata).
Update 2026-01-16: rewrite, simpler, better wording.