Terminal output in, self-contained SVG out. Real ANSI colour, exact monospace alignment, and not one byte of embedded font.
my-program | npx @tayomi/term2svg --font ./fonts/jetbrains-mono > output.svg
npx @tayomi/term2svg --font ./fonts/jetbrains-mono --theme one-light input.ansi out.svgimport { term2svg } from "@tayomi/term2svg";
const { svg } = await term2svg(ansi, { font: "./fonts/jetbrains-mono", theme: "one-dark" });GitHub renders no ANSI, so colour plus guaranteed alignment means an image. The tools that exist take one of two bad trades: they embed the whole font as base64, at ~366 KB per image, or they name a font family and hope the reader has it, which is why a box-drawing frame comes apart on somebody else's machine.
term2svg vectorises only the glyphs the input actually uses, declares each one once in
<defs>, and places it with <use x y>. Colour is carried by the enclosing group, so one
drawing serves every tint. A banner comes out at 8 KB, identical on every machine.
--font <spec> |
required. A .ttf/.otf/.woff file, a directory, or a URL cached in ~/.cache/term2svg |
--theme <name> |
one-dark (default) or one-light |
--background <hex> |
the paper colour, otherwise the theme's |
--quiet |
no statistics on stderr |
TERM2SVG_FONT |
the default for --font |
Input comes from stdin or from a file named first, - meaning stdin when you also want to
name an output file. The API takes the same options, plus loadFont() for many renders.
| Colour | all 256 ANSI slots and 24-bit, foreground and background |
| Attributes | bold, dim, italic, underline, reverse |
| Emoji | real colour emoji, kept to their columns, drawn by the reader's own system |
| Wide characters | CJK and emoji measured as 2 columns, so frames close |
| Control characters | tab, carriage return and backspace, through a real cursor |
Whatever a terminal would show, this shows: the graphic state outlives the line, \r and
\b go back and overwrite, and a non-SGR escape is swallowed rather than printed. Cursor
addressing is the deliberate limit, since obeying it means holding a screen buffer.
term2svg ships no font and --font has no default: you choose the typeface and you answer
for its licence. A file gives that one face, so bold and italic come out as the roman; a
directory gives four, read off each font's OS/2 tables and never off its filename.
Point it at one file, or at a directory holding one family: a system font directory holds many unrelated families, and pairing one family's roman with another's bold would pass without a word.
pnpm verify # lint, typecheck, suite
pnpm demo # renders samples/attributes.ansi into out/, both themesTypeScript throughout, with types shipped. Every module answers for itself: foo.ts is
tested by foo.test.ts beside it, the CLI suite under tests/ drives the built binary,
and the suite builds its own fonts rather than depending on the machine or the network.
MIT. No font ships here: the typeface is supplied at run time by whoever runs the tool, and the terms of the SVG it produces are that font's terms, not this project's.