Tokens
Of course you can type all your feature code yourself. But you don’t have to. Tokens help you automate your feature code in a smart way.
Add dynamic feature code with tokens. Syntactically, tokens are pieces of code that follow a $
dollar sign. You can use tokens for two purposes: interpolated number values and glyph class predicates for collecting glyph names.
Number values
First, you can use the token syntax for a number value, sometimes sloppily dubbed ‘number token’. You define number values in File > Font Info > Masters > Number Values. In a nutshell, number values are variables that interpolate. For instance, in Masters, you define a number value called pad
as 50 in the light master, and 5 in the bold master:
In your feature code, you can add a kern
(Kerning) feature with something like this:
pos @L @A $pad;
And in the resulting GPOS feature code, the respective interpolated value of pad
would get inserted in the token’s place. Nice. But what if you need something more complex? Let’s add a cpsp
(Capital Spacing) feature with a positioning rule like this:
pos @brackets <$pad 0 ${pad*2} 0>;
Yes, that’s right. Inside a ${...}
structure, you can add calculations. This is very useful for all kinds of manual GPOS code. There is a good example in the Positioning with Number Values tutorial.
Glyph class predicates
Glyph class predicates, also referred to as ‘class tokens’ or ‘predicate tokens’, have a $[...]
structure, and it will dynamically expand to a space-separated list of glyph names, useful for class definitions in your OpenType feature code. Between the square brackets you put Apple’s NSPredicate code for filtering glyphs. This works very much like a smart filter in Font view (Cmd-Opt-1). In fact, under the hood, smart filters actually use NSPredicate.
One example:
sub [ $[case==smallCaps] ] slash' by slash.sc;
The structure $[case==smallCaps]
consists of the object case
, a comparison operator ==
, and a value smallCaps
. In this special case, the value is one of these predefined constants: noCase
, upper
, lower
, smallCaps
, minor
, and other
. The constants correspond with the options in the glyph info accessible through Edit > Info for Selection. So the token expands to a space-separated list of glyph names that match this comparison, for which the predicate evaluates as true: in this case, all glyphs where the case is equal to small cap. In other words, we will get a.sc b.sc c.sc d.sc
and so on. So, the whole line expands to:
sub [ a.sc b.sc c.sc d.sc ] slash' by slash.sc;
… and very likely many, many more glyphs than just those four. The best thing is that it does that dynamically, so you do not need to collect all small caps glyphs yourself, or worry about subsetting, because that feature code is updated automatically at all times. And you can insert that token anywhere in your Prefix, Classes or Features code. To verify a predicate token, Opt-click on the token code, and a glyph list will pop up:
The overview is valid for the current set of exporting glyphs, and will be shown with glyphs of the currently selected master.
Okay, so let’s recapitulate. A predicate token starts with a dollar sign $
, followed by square brackets []
, inside which we put an object comparison. An object comparison is a logical expression that compares a specific property of each exporting glyph (the ‘predicate object’) to a value, usually a number or a string. For the comparison, it uses one of the available NSPredicate comparison operators. Each glyph for which the expression turns out to be true will make it into the eventual list of glyph names.
Predicate objects
In principle, any attribute of the GSGlyph
object is a valid object for a predicate expression. You can run print("\n".join(dir(GSGlyph)))
in the Macro window, and start fishing for possible predicates in the output. Or look in the GSGlyph documentation on docu.glyphsapp.com.
Boolean objects
A boolean glyph property can evaluate to true
or false
, or in an alternative spelling, yes
or no
. So, you can compare it like $[hasComponents == true]
. Available are the following glyph properties:
hasAlignedWidth
hasAnnotations
hasComponents
hasCorners
hasCustomGlyphInfo
hasHints
hasPostScriptHints
hasSpecialLayers
hasTrueTypeHints
isAligned
isAppleColorGlyph
isColorGlyph
isCornerGlyph
isHangulKeyGlyph
isSVGColorGlyph
isSmartGlyph
justLocked
locked
mastersCompatible
outlineHasChanges
Number objects
Numbers can be integers or floating point numbers. They can be compared to other numbers with the usual mathematical operators, for instance $[countOfUnicodes > 0]
. Available are the following glyph properties:
case
changeCount
colorIndex
countOfLayers
countOfPartsSettings
countOfTags
countOfUnicodes
direction
Yes, case
technically is an integer, but you would usually compare it to the constants I mentioned above already.
Many of the number values you really care about are attached to the layers, not the glyphs, e.g. layer0.width
, layer0.LSB
, etc. For details on which layer properties are available, take a look at the GSLayer documentation on docu.glyphsapp.com.
String objects
Strings are pieces of text. You can compare them to a reference string like this: $[name like "*.ss01*"]
. That reference string must be typed between dumb quotes "..."
and can contain jokers like *
. The following glyph properties are available as strings:
baseString
bottomKerningGroup
bottomMetricsKey
category
charName
charString
description
glyphDataEntryString
lastChange
leftKerningGroup
leftKerningKey
leftMetricsKey
name
note
production
productionName
rightKerningGroup
rightKerningKey
rightMetricsKey
script
sortName
sortNameKeep
string
subCategory
topKerningGroup
topMetricsKey
unicode
unicodeChar
unicodeString
vertWidthMetricsKey
widthMetricsKey
Complex objects
You would usually access their attributes, like layer0.width
.
glyphInfo
layer0
userData
The only one of those that really makes sense is layer0
which represents the first layer of the glyph, i.e., the first master. You can access its GSLayer properties with the familiar dot notation. You can even drill down in the object tree and do stuff like $[layer0.anchors.top.y > 600]
.
Almost all glyphInfo
objects like unicode
, script
or subCategory
are accessible directly as glyph attributes, so you will not need glyphInfo
often, except maybe for glyphInfo.accents
, but I would not know what that would make sense for. And userData
can be anything, but it is only really accessible through scripting. So it will only make sense in very, very specific circumstances.
Comparison operators and compounds
According to NSPredicate documentation, you can use the following comparison operators with as much whitespace as you like, and in lowercase or uppercase, whichever way you prefer.
- BEGINSWITH The object begins with the value, e.g.,
$[name beginswith "x"]
- CONTAINS The object contains the value, e.g.,
$[name contains ".alt"]
- ENDSWITH The object ends with the value, e.g.,
$[name endswith ".sc"]
- LIKE The object equals the value:
?
and*
are allowed as wildcard characters, where?
matches 1 character and*
matches 0 or more characters. E.g.,$[name like "?.sc" or name like "*.sc*"]
- MATCHES The object equals the right hand expression using a regex-style comparison according to ICU v3. This only makes sense for regex nerds. If you do not know what regular expressions are, ignore this.
- =, == The object is equal to the value.
$[layer0.LSB = 0]
- >=, => The object is greater than or equal to the value.
$[layer0.RSB >= 0]
- <=, =< The object is less than or equal to the value.
$[layer0.countOfPaths <= 3]
- > The object is greater than the value:
$[countOfUnicodes > 1]
- < The object is less than the value. $[layer0.LSB < 0]
- !=, <> The object is not equal to the value.
$[layer0.countOfHints != 0]
- BETWEEN The object is between, or equal to either of, two comma-separated values between curly braces. E.g.,
$[layer0.width between {400,600}]
or$[name between {"V","Z"}]
- IN The object must appear in the collection of comma-separated values between curly braces.
$[layer0.width in {200, 400, 600}]
(Technically, IN is an aggregation, but I’ll add it here anyway.)
Compounds are logical concatenations of multiple comparisons. You may know them from coding:
- AND, && Logical AND, combines two or more comparisons. The whole expression evaluates as true only if all comparisons are true. E.g.,
$[layer0.LSB < 0 and layer0.RSB < 0]
- OR, || Logical OR, combines two or more comparisons. The whole expression evaluates as true if at least one comparison is true. E.g.,
$[name endswith "superior" or name beginswith "ord"]
- NOT, ! Logical NOT operator, negates the comparison that follows. The tricky thing is to prefix an existing predicate to negate it. E.g.,
$[not layer0.width in {200, 400, 600}]
gets you all glyphs for which the first layer width is not one of the three numbers.
Sample code
To give you a little inspiration, here is a handy cheat sheet for predicate tokens:
$[name endswith '.sc'] # will expand to all glyph names that end in ".sc"
$[layer0.width < 500] # layer0 = first master
$[layers.count > 1] # compare numbers with: == != <= >= < >
$[direction == 2] # 0=LTR, 1=BiDi, 2=RTL
$[colorIndex == 5]
$[case == smallCaps] # predefined constants: noCase, upper, lower, smallCaps, minor, other
$[name matches "S|s.*"] # "matches": regular expression
$[leftMetricsKey like "*"] # "like": wildcard search
$[name like "*e*"] # e anywhere in the glyph name
$[script like "latin"]
$[category like "Separator"]
$[leftKerningGroup like "H"]
$[rightKerningGroup like "L"]
$[unicode beginswith "03"] # beginswith, endswith, contains
$[note contains "love it"] # glyph note
$[countOfUnicodes > 1]
$[countOfLayers > 1]
$[subCategory like "Arrow"]
$[hasHints == 0] # boolean: false, no, 0 versus true, yes, 1
$[isColorGlyph == true]
$[hasComponents == true and script == "latin"] # connect multiple conditions with ORor AND
$[hasTrueTypeHints == false]
$[hasAlignedWidth == true]
$[hasPostScriptHints == true]
$[hasAnnotations == true]
$[hasCorners == true] # corners = corner components
$[hasSpecialLayers == yes] # special layers = color, brace and bracket layers
$[isHangulKeyGlyph == no]
Whoa, that was something for the geeks among us, which leaves me really curious what you are going to use this for. Have fun.
Update 2024-01-23: fixed the visibility of asterisks in the documentation for LIKE.