Type Design

Live Reload UFOs

One fun thing that coldtype can do out of the box is load .ufo and .designspace files, as well as monitor those files for changes.

Here’s an example of creating a somewhat complex text setting using the assets/ColdtypeObviously_BlackItalic.ufo UFO source, then automatically monitoring the UFO source and refreshing the rendered composition based on any changes saved to the UFO.

from coldtype import *

ufo_ttf = Font("assets/ColdtypeObviously_BlackItalic.ufo")

@renderable((1200, 300), watch=[ufo_ttf])
def ufo_monitor(r):
    return [
        DATPen().rect(r).f(0),
        (StyledString("CDELOPTY",
            Style(ufo_ttf, 250, tu=-150, r=1))
            .pens()
            .f(Gradient.H(r, hsl(0.05, s=0.75), hsl(0.8, s=0.75)))
            .understroke(s=0, sw=15)
            .align(r))]
../_images/type_design_ufo_monitor.png

Now if you run the code above (via coldtype <your-filename-here>.py) and then open up the source UFO in RoboFont or another UFO-capable font editor, any changes you save to the source will update automatically in the preview window, if you leave the process hanging.

This works because the font is passed to watch= keyword argument of the @renderable decorator. This instructs the renderer to monitor that font’s source UFO for changes. (This also works for standard fonts, i.e. .ttf and .otf files.)

Also worth noting: because the UFO font is loaded above by compiling it to a ttf (coldtype does this automatically via FontGoggles when you pass a .ufo to Font), you can also use a coldtype program as a way to test OT feature code in realtime — simply edit the feature code, hit save, & the coldtype program above with automatically update.

If you want to address glyphs in a UFO directly by their glyph names, you can also load a defcon.Font directly, as an alternate representation of the UFO source. (DefconFont is provided by coldtype as an alternate name for defcon.Font) This method skips any typesetting code and uses the glyphs in the UFO as pens, via the helper method glyph provided by DATPen (which records a defcon.Glyph to the given pen).

ufo = DefconFont("assets/ColdtypeObviously_BlackItalic.ufo")

@renderable((500, 500), watch=[ufo.path])
def defcon_monitor(r):
    return [
        DATPen().rect(r).f(1),
        (DATPen()
            .glyph(ufo["C"])
            .scale(0.5)
            .align(r)
            .f(0))]
../_images/type_design_defcon_monitor.png

N.B. Rather than passing the ufo object directly to the watch= argument, we’ve passed it’s .path property — in the first example we passed the Font object directly, but coldtype knows how to handle that natively. For anything other than a Font, you can pass its filesystem representation, meaning you can monitor any file on your computer.

generic_txt = Path("docs/tutorials/scratch.txt")

@renderable((800, 200), watch=[generic_txt])
def txt(r):
    return DATPenSet([
        (StyledString("> " + generic_txt.read_text() + " <",
            Style("assets/RecMono-CasualItalic.ttf", 50))
            .pens()
            .f(0.25)
            .align(r))])
../_images/type_design_txt.png

Live Reload Designspaces

You can also quickly get an interactive designspace preview going, by reading from a designspace file.

If you click around in the coldtype window that pops up when you run code like the code below, you’ll see the font change size & wdth.

obv_ds = Font("assets/ColdtypeObviously.designspace")

@renderable((1200, 350), rstate=1)
def ds_mouse(r, rs):
    ri = r.inset(50)
    sx, sy = ri.ipos(rs.mouse, (0.5, 1))
    return [
        (StyledString("COLDTYPE",
            Style(obv_ds, 150+sy*100, wdth=sx))
            .pens()
            .align(r)
            .f(0))]
../_images/type_design_ds_mouse.png