Plug-ins

What Fontra expects from a plug-in: the manifest, the address rules, the objects an editor plug-in receives, and the Python entry points. For a guided introduction, see Writing your own plug-ins.

Editor plug-ins

plugin.json

KeyValue
nameDisplay name. Used in the console error if the plug-in fails to run.
descriptionFree text. Not used by Fontra.
initPath of the ES module to import, relative to the plug-in folder (for example init.js).
functionName of the function exported by that module. Called as function(editor, pluginPath).

Addresses

Added in Application settings › Plugins. Stored in the browser’s localStorage under the key fontra.pluginsplugins as [{"address": "…"}, …], so the list belongs to one browser and one server origin (host and port).

AddressResolved to
owner/repo (exactly one slash)https://cdn.jsdelivr.net/gh/owner/repo@latest — a GitHub repository with the plug-in files at its root and at least one tagged release: @latest is the latest tag, so a repository without tags is refused (An error occured when fetching the plugin). jsDelivr caches the resolution of @latest per file for up to a day, failures included: purge with https://purge.jsdelivr.net/gh/owner/repo@latest/<file> after tagging. See Publishing your plug-in.
https://…/folderUsed as is. The server must send CORS headers and serve the scripts with a JavaScript media type (GitHub Pages and jsDelivr do; github.com and raw.githubusercontent.com do not).
/folderA path on the Fontra server itself, for example /localplugins/my-plugin or a fontra.webcontent package.

A trailing slash is removed. The resolved address is what init() receives as pluginPath.

Loading

When the editor starts, for each address in order:

  1. GET <pluginPath>/plugin.json — on failure: console error “… Plugin metadata not found.”, next plug-in.
  2. import(<pluginPath>/<init>) — on failure: “Module didn’t load” and the exception, next plug-in.
  3. module[function](editor, pluginPath) — on exception: “Error occured when running () plugin.” and the exception.

Plug-ins load in the glyph editor only, after Fontra’s own sidebars, tools, actions and menus have been set up. A plug-in cannot import Fontra’s modules (@fontra/core/…): they are bundled and have no URL.

Files served from the Fontra server (a server path address) are limited to the extensions .js, .json, .svg, .css, .html, .txt, .csv, .ico, .woff2 and .wasm, and are dated at server start-up: the browser’s cached copy is reused (304 Not Modified) until the server restarts or the cache is disabled.

The editor object

MemberDescription
fontControllerThe font. glyphMap (glyph name → code points), unitsPerEm, axes, await getGlyph(name).
sceneSettingsCurrent state: selectedGlyphName, selectedGlyph, selection, fontLocationUser, positionedLines, text, …
sceneSettingsControlleraddKeyListener(keyOrKeys, callback, immediate), waitForKeyChange(key).
sceneModelgetSelectedPositionedGlyph() → { x, y, glyph, … }; glyph has name, path, components, xAdvance, leftMargin, rightMargin, canEdit.
sceneControllereditLayersAndRecordChanges(fn), editGlyphAndRecordChanges(fn), addCurrentGlyphChangeListener(fn), selectedTool, setSelectedTool(id).
canvasControllerrequestUpdate(), magnification, origin, localPoint(event), canvas.
visualizationLayersdefinitions (array sorted by zIndex), toggle(id, on), visibleLayerIds, darkTheme.
visualizationLayersSettingsObservable of the user-switchable layers: model[id] is true or false.
addSidebarPanel(panel, side)side is "left" or "right". Throws if the side is unknown.
addEditTool(tool)Appends a button to the toolbar.
toggleSidebar(id, focus)Opens or closes a panel by identifier.
toolsAll tools by identifier.

Edits with undo

editLayersAndRecordChanges(fn) calls fn(layerGlyphs) with the layers being edited, keyed by layer name (one entry, or several when several sources are edited together). fn changes them in place and returns the undo label (a string). The change is recorded, sent to the server, and listed in Edit › Undo.

An HTML element (usually with a shadow root) with:

MemberDescription
identifierUnique among all panels. Used in data-sidebar-name on the tab and content elements, and in the localStorage key fontra-selected-sidebar-<side>.
iconPathURL of the tab's SVG icon. Must be set before addSidebarPanel().
toggle(on, focus)Optional. Called when the panel is shown (on is true) or hidden.

The tab’s tooltip is translate("sidebar.<identifier>"); for a plug-in that returns the key itself, so set title on .sidebar-tab[data-sidebar-name="<identifier>"] yourself. Fontra’s CSS variables (--ui-element-foreground-color, --ui-element-background-color, …) and the font family fontra-ui-regular are available inside the shadow root.

Edit tool

An object with:

MemberDescription
identifierUnique among all tools. Used in data-tool on the button and in editor.tools.
iconPathURL of the button's SVG icon.
activate(), deactivate()Called when the tool is selected or another tool is.
async handleDrag(eventStream, initialEvent)Called on mouse down; iterate eventStream until mouse up.
handleHover(event)Mouse move without a button pressed.
handleKeyDown(event)Keys other than arrows. Define handleArrowKeys(event) to receive arrow keys too; otherwise Fontra nudges the selection.
getContextMenuItems()Items appended to the canvas context menu.

The button’s tooltip is translate("editor.<identifier>"), so set title on [data-tool="<identifier>"] yourself. The button is created with the selected class; remove it unless the tool is the active one. editor.canvasController.localPoint(event) converts an event to glyph coordinates.

Visualization layer

A definition inserted into editor.visualizationLayers.definitions at the position given by its zIndex, then enabled with toggle(identifier, true):

KeyDescription
identifierUnique. Prefix it with your plug-in name.
nameTranslation key of the layer's title (used in the View menu for user-switchable layers).
zIndexDrawing order, higher on top. Fontra's own layers use 0 (UPM grid) to 700 (glyph lock).
userSwitchable, defaultOnWhether the layer is listed in View › Glyph editor appearance, and its initial state. Those menu actions are registered before plug-ins load, so a plug-in layer should keep userSwitchable: false.
screenParametersNumbers in screen pixels, divided by the magnification before drawing (strokeWidth, nodeSize, …).
glyphParametersNumbers in glyph units, passed unchanged.
colors, colorsDarkModeColors for the light and dark themes. All four groups are merged into parameters.
selectionFunc(visContext, layer)Returns the positioned glyphs to draw for. visContext.glyphsBySelectionMode has all, unselected, hovered, selected, editing and notediting.
selectionFilter(positionedGlyph)Optional filter applied by Fontra's own selectors.
draw({ context, positionedGlyph, parameters, model, controller })Called once per glyph with the canvas context translated to the glyph's origin, baseline at y = 0, y up, in glyph units.

Call editor.canvasController.requestUpdate() after changing a layer’s visibility.

Server plug-ins

Python packages declaring entry points in pyproject.toml, installed in the same environment as Fontra.

GroupEntry point name → valueUsed
fontra.filesystem.backendsFile extension → class with fromPath(path), optionally createFromPath(path), implementing ReadableFontBackend / WritableFontBackend (fontra.core.protocols)When a font file with that extension is opened or created
fontra.projectmanagersSubcommand name → project manager factoryfontra <name> …
fontra.webcontentURL prefix → package (dotted path to a folder)Served as static files under /<name>/
fontra.workflow.actionsAny name → module to importLoaded by fontra-workflow; the module registers its actions with @registerFilterAction("…") / @registerOutputAction("…")
fontra.viewsView name → packageListed as View plugins in Server info only

Fontra itself registers designspace, ufo, ttf, otf, ttx, woff and woff2 backends, the filesystem project manager, and localplugins = "fontra.localplugins" (the folder src/fontra/localplugins/, not in git).