Skip to main content

rotulo

npm PyPI license

Monoline typeface generated from skeletons: strokes of lines and arcs expanded with a round pen.

Rotulo Display and Rotulo Lectura specimen

Every letter is a skeleton of straight lines and arcs drawn in code, never a hand-drawn outline. The generator expands each skeleton with a round pen and writes TrueType and WOFF2 fonts. Weight is the pen width, so the family is a variable font from 200 to 700. A reading cut, Lectura, adds weight, spacing and letters that are hard to mirror.

Install

npm install rotulo
pip install rotulo
Package What it is Needs
rotulo (npm) WOFF2 files and a stylesheet with the @font-face rules A bundler that resolves CSS imports, or a plain <link>
rotulo (PyPI) The generator: builds every font from the skeletons Python 3.12 or later

The TTF files are attached to each GitHub release.

Quick start

@import 'rotulo';

body {
    font-family: 'Rotulo', system-ui, sans-serif;
    font-weight: 200;
}

.reading {
    font-family: 'Rotulo Lectura', 'Rotulo', sans-serif;
}
python -m rotulo --out build --web build/web

Fonts

Family File Weight Use
Rotulo Rotulo-Variable.woff2, .ttf 200 to 700 Map labels, interface, titles
Rotulo Rotulo-Display.ttf 200, static and hinted Renderers without variable fonts, such as GeoServer or PDF libraries
Rotulo Lectura Rotulo-Lectura.woff2, .ttf 500, static and hinted Long reading

Color details

The fonts are one color. Built with color_details=True, dots, accents, the zero slash and punctuation move to a color layer (COLR/CPAL): the body takes the text color and the detail a palette color, #3d8a8f by default and #7fb7b5 on dark backgrounds (base-palette: 1).

Reading forms

ss01 switches four letters to the forms of Lectura: l with a tail, q with a crossed tail, a curved flag on 1 and a straight 9.

.labels {
    font-feature-settings: 'ss01';
}

Using the fonts outside the web

Where How
GeoServer Copy Rotulo-Display.ttf into the font directory and name Rotulo in the SLD font-family
PDF Embed Rotulo-Display.ttf or Rotulo-Lectura.ttf

Parameters

Params holds every design decision, in units of a 1000 em:

Field Default What it controls
weight 40 Pen width
x_height, cap_height 425, 700 Body and capital heights
ascender, descender 840, −180 Shared by every letter with a tail
corner 50 Radius used to round every skeleton vertex
gap 110 Space between a stroke and a floating detail
spacing, word_spacing 1.5, 1.0 Side bearings and space width
narrow_air 0.3 Extra room around narrow letters such as i, l, t
from dataclasses import replace
from pathlib import Path

from rotulo import DISPLAY, build_font

print(build_font(replace(DISPLAY, weight=60), "Medium", 400, Path("build")))

Known limitations

  • Latin script: Spanish, Portuguese, French and Catalan, figures and common symbols.
  • The variable font keeps overlapping strokes and is not hinted; use the static files at small sizes on Windows.

Development

python -m venv .venv && . .venv/bin/activate
pip install -e '.[dev]'
pytest
ruff check src tests && ruff format --check src tests
mypy

The tests build every font and check heights against the skeleton, kerning, the optional color layers, ss01, hinting and that the variable font interpolates every glyph.

Contributing

Issues are welcome. Please open one before sending a pull request.

License

OFL-1.1 © GHEgar

Metadata

Release files for rotulo 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rotulo 0.2.0
File Size Uploaded
rotulo-0.2.0.tar.gz 99.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rotulo 0.2.0
File Interpreter ABI Platform
rotulo-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 126.6 kB

Release files / rotulo-0.2.0.tar.gz

Download URL rotulo-0.2.0.tar.gz
Size 99.3 kB
Tags Source
SHA-256 checksum
How to use checksums
97c055f26c6fa102421ddfdcbba6650a42570ddd0870f2aee1912085032c7cef
BLAKE2b-256 checksum
How to use checksums
fc1aa1f0dcf627790140af3987b47ce95d3944dba709c562e05872c048f0fa6a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release files / rotulo-0.2.0-py3-none-any.whl

Download URL rotulo-0.2.0-py3-none-any.whl
Size 27.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5edac642166418169b678de1d564612e8a104223d64d106656614c7342e10142
BLAKE2b-256 checksum
How to use checksums
a6117185ebc1ea4ae2823b56a15a05b3dc5d3236853b62bd411033901796430c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 16, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.0

2 release files

This release

0.2.0 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page