Fontista Lua API

A Lua script works on a copy of the open font; when it ends, all its changes apply at once, as a single undo step (Cmd or Ctrl + Z reverts it). Lua 5.4, with the standard library (math, string, table, utf8, os, io). Scripts need a licence (site edition) or the App Store version; the trial refuses them.

Running a script

  • Scripts window (Scripts › Scripts window…): paste the code, then Run (Cmd or Ctrl + Enter). The output of print shows below.
  • Scripts › Run a Lua script…: pick a .lua file.
  • Scripts folder: every .lua in it becomes an item of the Scripts menu.
  • Plugin: a folder inside the plugins folder, with a plugin.txt file, becomes a submenu.
  • Command line (site edition with a licence): fontista --lua script.lua font.fontista [out.fontista].
SystemScripts folderPlugins folder
macOS, site edition~/Library/Application Support/Fontista/scripts~/Library/Application Support/Fontista/plugins
macOS, App Store~/Library/Containers/dev.thinkspark.fontista/Data/Library/Application Support/Fontista/scriptssame folder, plugins
Windows%APPDATA%\Fontista\scripts%APPDATA%\Fontista\plugins
Linux~/.local/share/Fontista/scripts~/.local/share/Fontista/plugins

Scripts › Open the plugins folder opens the right folder. A plugin.txt looks like this:

name = My tools
script = round.lua = Round every corner
script = proof.lua = Export a proof

Each script = file = label line becomes an item of the submenu.

The font: font

font is a snapshot of the font taken when the script starts (read only).

FieldContent
font.family, font.upmfamily name, units per em (often 1000: check it)
font.masters[i]id, name, ascender, descender, x_height, cap_height, location ({wght = 400})
font.axes[i]tag, name, min, default, max
font.glyphsglyph names, in font order
langinterface language, "fr" or "en" (to answer in the user's language)

Master ids look like m01. Every function that takes a master wants its id, not its name.

Functions

FunctionEffect
glyph(name){name, unicode, note, export, mark, layers}, or nil
layer(name, master){width, contours, empty, ink}, or nil; it is a copy: change it, then set_contours
set_contours(name, master, contours)replaces the drawing of the layer (creates it if needed)
set_width(name, master, width)advance width
add_glyph(name [, code])adds an empty glyph in every master; false if it exists
remove_glyph(name), rename_glyph(old, new)components follow a rename
set_unicode(name, code or nil)character of the glyph
set_note(name, text), set_mark(name, 0..7 or nil), set_export(name, bool)note, colour label (0 red, 1 orange, 2 yellow, 3 green, 4 cyan, 5 blue, 6 purple, 7 grey), export
anchors(name, master){top = {x = 250, y = 700}, _top = …}
set_anchor(name, master, anchor, x, y)creates or moves an anchor (top, bottom, _top, entry, exit…)
components(name, master){{base, dx, dy}, …} (read only)
set_ink(name, master, brush or nil)turns the layer into skeletons inked by this brush, or stops inking
kern(master, left, right, value)sets a pair (glyph or group names)
kerning(master){left = {right = value}}
batch("line")runs a batch command (below) and returns its report
compile()the compiled TTF, as a byte string
export_ttf(path)compiles and writes a TTF; returns its size
save(path)saves a .fontista copy
print(…)output in the Scripts window, or on the terminal with --lua

Glyph names are production names: A, a, zero…nine, period, comma, hyphen, eacute, acutecomb, f_i, a.alt1, uni202F… A new font already holds the Latin-1 set (empty).

Contour format

{ closed = true, nodes = {
    {x = 100, y = 0,   kind = "line"},
    {x = 100, y = 300, kind = "line"},
    {x = 155, y = 400, kind = "off"},          -- control points precede the point they bend
    {x = 245, y = 400, kind = "off"},
    {x = 300, y = 300, kind = "curve", smooth = true},
} }
  • Font units, y up, baseline at 0, x from the glyph origin.
  • A closed contour starts at its first on-curve point and wraps around; off points at the end of the list curve the closing segment.
  • Two off points: cubic Bézier; one: quadratic; none: straight line.
  • kind: "line" or "curve" for an on-curve point (the kind of the segment ending there), "off" for a control point, "move" for the start of an open contour. smooth = true keeps the handles aligned.
  • Direction: outer contours counter-clockwise, counters clockwise. batch("correct") fixes directions, batch("overlap") merges overlaps.
  • An open contour (closed = false) does not fill: it is a stroke, to thicken with batch("offset rx ry stroke round"), or a skeleton inked by a brush (set_ink).

Skeletons and brush

An inked layer (set_ink) treats its open contours as skeletons: the brush sweeps along them on screen, in the Preview and at export; its closed contours stay filled shapes. A brush is a table {nib = "round" | "broad", width = 80, angle = 40, edge = 9} (also min, max, gamma, taper, smoothing, melt; width sets min and max together). Per skeleton node: w, the pressure from 0 to 1, and cap, an end cut straight along a line at that angle in degrees (absent: round end). Per contour: ink gives that stroke its own brush.

set_contours("l", m, {{closed = false, nodes = {{x = 250, y = 760, kind = "line", cap = 0}, {x = 250, y = 0, kind = "line", cap = 0}}}})
set_ink("l", m, {nib = "round", width = 72})   -- a 72-unit monoline stem, ends cut flat on the zones

Batch commands: batch("…")

batch works on a selection of glyphs (all by default) and a master (the first by default). The selection stays for the rest of the script: run batch("select all") again after a targeted operation.

CommandEffect
select all / upper / lower / digits / name name name…glyphs targeted by the next commands
master id-or-namemaster targeted
offset dx dy [expand|contract|stroke] [round] [keep]offset the outline; stroke turns every contour into a band of half-width dx (horizontal) and dy (vertical)
round rround every corner with radius r
fit pct, simplify tolrefit curves; fewer nodes
smooth, tidy, extremessmooth nodes; tidy up; add extreme points
overlap, correctremove overlaps; correct directions
transform rotate deg / scale pct / slant degaround each glyph's centre
autospace [white] [depth]automatic spacing (average white per side, counter depth in %)
autokern [strength] [replace] [all]automatic kerning of base letters
metrics-update, kern-clean [threshold]apply metric keys; remove useless pairs
weight name wght dx dy [exact], italic name deg, width name pctnew bolder, slanted or narrower master
smallcaps, tabular, smallforms [sups] [subs] [frac]derived glyphs and their features
skeletonize [editable|faithful|exact], weightaxis light boldbrush skeletons; weight axis from the brush
anchors [height], formscursive connection anchors (entry and exit), init and fina forms for joined scripts
autofeatures on|offfeatures generated at compile time (kern, mark, liga, calt…)

Command line

fontista --lua script.lua font.fontista [out.fontista]

The script sees the font; if it changed something, the result is saved to out.fontista (or in place without an output). print writes to the terminal. Exit code 1 and the Lua message on error. Program on macOS: /Applications/Fontista.app/Contents/MacOS/fontista; on Windows, fontista.exe; on Linux, fontista.

Tips

  • Read what you need first (font.upm, the master's zones); never assume 1000 units.
  • layer() returns a copy: change it, then set_contours; read it again after a batch command.
  • An error stops the script and nothing is applied: read the message in the output.
  • overlap is a union (non-zero rule): a reversed contour only cuts where it lies inside ink.
  • Lua keeps only the first value of a call that is not the last argument: f(g(), x) passes one value from g. Return tables rather than several values.
  • A script can reach the computer's files like any program: only run scripts whose source you know. Those of the library are reviewed and tested by Thinkspark.