The scripts below demonstrate the mojo.smartSet API.

Inspecting Smart Sets

If you only need to inspect smart sets without altering the data, you can use getSmartSets()

from mojo.smartSet import getSmartSets

# get default sets
defaultSets = getSmartSets()
print(defaultSets)

# get font sets
f = CurrentFont()
fontSets = getSmartSets(f)
print(fontSets)

Modifying Smart Sets

Instead, if you need to alter the smart sets, we strongly advice to use the with smartSetsEditor() context, to force an update of the RoboFont UI at the end of the process. The with context exposes a list that can be modified with standard methods like .append(), .extend() or .remove()

Append one smart set

from mojo.smartSet import SmartSetsEditor, SmartSet

with SmartSetsEditor() as smartSets:
    smartSets.append(
        SmartSet(smartSetName="foo", glyphNames=["a", "b"]),
    )

Append several smart sets

from mojo.smartSet import SmartSet, SmartSetsEditor

# a bunch of fresh new smart sets
items = [
    SmartSet(smartSetName="foo", glyphNames=["a", "b"]),
    SmartSet(smartSetName="bar", query="Width < 200")
]

with SmartSetsEditor() as smartSets:
    smartSets.extend(items)

Append smart set per mark color name

from mojo.UI import getDefault
from mojo.smartSet import SmartSet, SmartSetsEditor
from lib.tools.misc import rgbaToString

# get all colors
colors = getDefault("markColors")

with SmartSetsEditor() as smartSets:
    group = None
    # look if there is already a Mark Color smart set group
    for smartset in smartSets:
        if smartset.name == "Mark Colors":
            group = smartset
            # remove all the group items
            group.removeGroups()
            break
    
    # if nothing is found, create a new group smart set
    if group is None:
        group = SmartSet()    
        group.name = "Mark Colors"
        smartSets.append(group)
    # loop over all colors and name and add a smart set query
    for rgba, name in colors:
        group.addGroupSmartSet(SmartSet(smartSetName=name, query=f"MarkColor == '{rgbaToString(rgba)}'"))

Clear all smart sets

The following example will delete all existing smart sets, be aware!

from mojo.smartSet import SmartSetsEditor

with SmartSetsEditor() as smartSets:
    smartSets.clear()

Font-specific smart sets

If you want to create a font-specific smart set, remember to pass the font object to the with smartSetsEditor() context. The SmartSet object itself does not know if it belongs to a font or not. Check the example:

from mojo.smartSet import SmartSetsEditor, SmartSet
from mojo.roboFont import CurrentFont

fontSmartSet = SmartSet(smartSetName="alternates",
                        glyphNames=["a.001", "b.001"])
myFont = CurrentFont()

with SmartSetsEditor(myFont) as smartSets:
    smartSets.append(fontSmartSet)

Remove Smart Sets

If you want to remove a SmartSet by using its name, you can do it either globally:

from mojo.smartSet import SmartSetsEditor

removeName = 'mySmartset'

# global sets!
with SmartSetsEditor() as smartSets:
    for smartset in smartSets:
        if smartset.name == removeName:
            smartSets.remove(smartset)

or locally:

from mojo.smartSet import SmartSetsEditor
from mojo.roboFont import CurrentFont

removeName = 'mySmartset'

# font sets!
font = CurrentFont()
with SmartSetsEditor(font) as smartSets:
    for smartset in smartSets:
        if smartset.name == removeName:
            smartSets.remove(smartset)

Query-based Smart Sets

Queries are expressed using Apple’s Predicate Format String Syntax.

Language tokens

These are the main building blocks of the predicate language:

construct examples
literals False True 'a' 42 {'a', 'b'}
compound expressions and or not
aggregate operations any some all none in
basic comparisons = >= <= > < != between
string comparisons contains beginswith endswith like matches

The matches comparison requires a regular expression. (see the example below)

Glyph attributes and supported comparisons

These are the glyph attribute names, their data types and supported comparisons.

type attributes comparisons
str Name
ComponentsNames
AnchorNames
in contains beginswith endswith like matches
int or float Width
LeftMargin
RightMargin
Unicode
Contours
Components
Anchors
< = > != between in
str MarkColor = != contains matches
bool Empty
GlyphChanged
Template
SkipExport
True False
str Note contains

Example queries

"Name == 'a'"
"Name in {'a', 'b'}"
"Empty == 'True'"
"Name contains 'a' AND Width < 300"
"Name matches '[A-z]'"
"Width in {120, 240, 360}")
Last edited on 01/09/2021