Skip to content

Repository files navigation

rpic logo — a lowercase r annotated like an engineering blueprint, drawn in rpic itself

rpic

A modern reimplementation of the pic picture-drawing language in Rust —
SVG / PNG / PDF output, GSAP animation, and a native circuit-element library.

Even the logo is a pic program: assets/logo.pic, rendered by rpic.

📖 Documentation: rpic.dev — language tour, every extension with live examples, spec and the pic-family history.

CI Release crates.io License PyPI npm Rust 2024 DOI

A pic diagram rendered by rpic

The figure above is rendered by rpic from this source — the very example Brian Kernighan used to introduce pic in 1982:

ellipse "document"; arrow; box "PIC"; arrow
box "TBL/EQN" "(optional)" dashed; arrow
box "TROFF"; arrow; ellipse "typesetter"

Why rpic

rpic keeps Kernighan's original pic paradigm — you describe a drawing by "walking around a plane dropping primitives", with relative positioning, default dimensions, compass corners, ordinals, blocks and macros — and brings it to the modern web era:

  • SVG / PNG / PDF, all pure-Rust — no troff, no LaTeX, no ImageMagick, no system libraries.
  • Fast: single-digit-millisecond cold renders, flat with diagram size — ~140× faster than mermaid-cli in a docs pipeline (benchmark, reproducible via tools/bench).
  • A declarative animation layer (animate …) that plays in the browser with GSAP — enter/exit effects (fade, pop, draw, slide), motion along a path, colour highlights, shape morphs, block staggering and a scroll-scrub hint, all emitted as a plain JSON timeline.
  • A native circuit-element library (79 elements) — a from-scratch re-imagining of circuit_macros.
  • Real typography: per-string bold / italic / mono, any font family and size, rotated labels, and native rgb() / #hex colours — all feeding the layout so fit and bounding boxes stay correct.
  • Built for editors: structured diagnostics with exact spans and did-you-mean hints, per-object geometry in the --json output (objects: kind, bbox, source span), and a fixed-canvas mode for a stable viewBox — a base visual editors can build on without DOM heuristics.
  • Safe on untrusted input: the parser and evaluator are hardened against malformed or adversarial source — bounded recursion and expression depth, configurable loop/shape budgets, and XML-escaped output — so an app can compile pasted or shared .pic without crashing, hanging, or emitting unsafe SVG. Every binding, down to the C ABI, turns a fault into an error rather than aborting the host.
  • One core, many targets: a native CLI, WebAssembly, and bindings for Python, R, JavaScript/TypeScript, and C.

The compatibility rule is deliberately Kernighan-first: the 1982 paper and manual set the language philosophy and the meaning of classic pic constructs. When the texts leave room for interpretation, dpic is the practical oracle: we compare against dpic -v, keep its well-tested geometry and macro behavior in view, and document any intentional rpic extension separately.

The language

.PS
boxht = 0.3; boxwid = 0.6
A: box "input"
arrow
B: box "process" fill 0.9
arrow
ellipse "output"
arc -> from A.n to last ellipse.n
.PE

Primitives: box circle ellipse arc line arrow move spline + text. Positioning: named labels, compass corners (.n .ne .center …), ordinals (last, 2nd last box), with … at, fractions (1/3 between A and B), blocks [ … ]. Programmability: define macros with $1…$9, for, if, sprintf, environment variables.

Explicit rpic extensions — margin, canvas, fit, behind, close, brace, hatch, gradient, opacity, class hooks, dot, thin strokes, font attributes (bold/italic/mono/font/fontsize/big/small), rotated & aligned labels, rgb()/0xRRGGBB colour literals (also held in variables or computed), texlabels (KaTeX-grade TeX math in labels, rendered natively) and the animate layer — are opt-in and inert for classic pic/dpic-compatible input. Each has a page with live examples at rpic.dev; the design notes live in docs/extensions.md.

Outputs

rpic diagram.pic                       # SVG to stdout
rpic --png --scale 2 -o out.png diagram.pic
rpic --pdf -o out.pdf diagram.pic
rpic -c circuit.pic                    # load the circuit-element library
rpic -t paper.pic                      # typeset $…$ labels as TeX math
rpic --json diagram.pic                # {svg, animations, diagnostics, warnings, objects}
rpic --html -o page.html animated.pic  # self-contained page: SVG + GSAP player inline

--html writes a single page that plays the drawing's animate timeline on open: the SVG, the manifest and the player are inlined, and GSAP (plus only the plugins the used effects need) is pulled from a pinned, integrity-checked CDN tag. A drawing with no animations becomes a plain static page with no scripts at all.

Examples

A gallery of diagrams from the dpic distribution — including reproductions of Brian Kernighan's original pic-manual figures — rendered by rpic itself lives in examples/dpic/. Each .pic is paired with its rendered .svg, with full credits and a parity matrix in examples/dpic/STATUS.md.

rpic --svg examples/dpic/manual/man16.pic -o man16.svg

examples/figuras/ collects circuit_macros figures from André Leite's personal collection, rendered by rpic. examples/lib3d/ shows 3D drawings (axonometric projection, à la circuit_macros' lib3D) rendered to flat SVG.

Animation (GSAP)

A declarative extension, faithful to pic's style:

box "A"; arrow; box "B"
animate 1st box   with "pop"   for 0.4
animate 1st arrow with "draw"
animate 2nd box   with "fade"  after 1st arrow delay 0.2

The full effect palette:

Effect What it does
fade / pop / draw opacity / scale-in / stroke-on entrances
slide translate in from a compass direction (from left)
move travel along another object's path (along L)
highlight recolour + pulse the outline (to "crimson")
morph tween the outline into another shape (into B)
type reveal a label a character (or by word) at a time — a typewriter
scramble glyphs cycle through random characters and resolve — a decode reveal
wiggle a quick oscillating shake that returns to rest — "look here" (wiggles 6)
"Describe the picture." at (0,0);   animate last with "type" for 1.4
box "SECRET" fit at (0,-0.6);       animate last with "scramble" for 1.2

Any effect can play as an exit with out, loop with repeat/yoyo, take a custom ease, or fan across a block's children with stagger; animate scroll hints the host to scrub the timeline on scroll. Timing is sequential by default, or absolute (at) / relative (after), with an optional delay. Separately, draggable <obj> [inertia] [bounds B] [x|y] makes an object grabbable in the browser (GSAP Draggable) — interaction rather than a timeline, so it's its own directive.

This compiles to an SVG plus a flat JSON manifest ({id, effect, start, duration, …}, and interactions for draggable); the browser layer turns it into a GSAP timeline. Full reference: rpic.dev/docs/extensions/animate. Try it in the playground:

./web/build.sh && (cd web && python3 -m http.server 8080)   # http://localhost:8080

Circuit library

Enable with -c, or in-source with copy "circuits" (the analog of texlabels = 1 for -t). Two-terminal elements take two named points; centered devices take one and expose their terminals as variables.

.PS
SW:(0,0); NW:(0,1.4); NE:(2.6,1.4); SE:(2.6,0)
battery(SW,NW); resistor(NW,NE); capacitor(NE,SE); inductor(SE,SW)
.PE

RLC circuit    Logic gates

79 elements across analog parts, distinctive & IEEE logic gates, BJT/MOSFET/ JFET transistors, op-amps, sources, meters, transducers, grounds and annotations. See crates/core/src/std/circuits.pic.

Install

# from source (any platform)
cargo install --git https://github.com/milkway/rpic-lang rpic-cli

# Homebrew (macOS / Linux)
brew install milkway/rpic/rpic

# Scoop (Windows)
scoop install https://raw.githubusercontent.com/milkway/rpic-lang/main/packaging/scoop/rpic.json

# Debian/Ubuntu — download the .deb from the Releases page, then:
sudo dpkg -i rpic_*.deb

Prebuilt binaries for macOS / Linux / Windows are attached to each release. See CHANGELOG.md for what changed in each version.

Language bindings

Python — bindings/python

pip install rpiclang          # distribution name; the module is `rpic`
import rpic
svg = rpic.render_svg('box "hi"; arrow; circle "x"')
open("out.png", "wb").write(rpic.render_png('box "hi"', scale=2.0))
bundle = rpic.compile('box\nanimate last box with "pop"')
# bundle["diagnostics"] = pic `print` output; bundle["warnings"] = structured
# warnings; errors raise rpic.CompileError with the diagnostic on `exc.info`

R — milkway/rpic-r (separate repo)

remotes::install_github("milkway/rpic-r")
rpic::rpic_svg('A:(0,0); B:(2,0)\nresistor(A,B)', circuits = TRUE)
rpic::rpic_register_knitr()        # ```{rpic} chunks in R Markdown / Quarto

JavaScript / TypeScript — bindings/js

import * as rpic from '@strategicprojects/rpic';
await rpic.ready();                      // or ready(undefined, { math: true })
const { svg, animations, diagnostics, warnings, objects } = rpic.compile('box "A"; arrow; box "B"');
rpic.animate(stage, animations, gsap);   // GSAP timeline

The default wasm is lean; ready(undefined, { math: true }) lazy-loads a math-enabled build so texlabels typeset $…$ labels in the browser. Compile errors throw with structured err.errorInfo (span, kind, did-you-mean hint) for editor integrations.

Build from source

cargo build --release      # CLI in target/release/rpic
cargo test                 # full test suite
Path What
crates/core engine: lexer, parser, eval, IR, SVG backend, std/circuits.pic
crates/render PNG/PDF (resvg, svg2pdf)
crates/cli the rpic binary
crates/capi stable C ABI (rpic.h)
crates/wasm WebAssembly bindings
bindings/{python,js} Python & JS/TS bindings (R lives at milkway/rpic-r)
web/ browser playground (GSAP)
packaging/ deb / Homebrew / Scoop config

How to cite

If you use rpic in academic work, please cite it via its Zenodo record. The concept DOI 10.5281/zenodo.21209915 always resolves to the latest release; each version also has its own DOI. GitHub's “Cite this repository” reads CITATION.cff for BibTeX/APA.

@software{leite_rpic,
  author    = {Leite, André},
  title     = {{rpic: the pic picture-drawing language, reimplemented in Rust}},
  publisher = {Zenodo},
  doi       = {10.5281/zenodo.21209915},
  url       = {https://rpic.dev}
}

Acknowledgments

rpic stands on the shoulders of giants — see ACKNOWLEDGMENTS.md: Brian W. Kernighan (pic), Dwight Aplevich (dpic, circuit_macros), and D. Richard Hipp (pikchr).

License

BSD-2-Clause.

About

A modern reimplementation of the pic graphics language in Rust — SVG/PNG/PDF output, GSAP animation, and an 79-element circuit library. Bindings for Python, R and JS/TS.

Topics

Resources

Code of conduct

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages