A markup language for receipts. Parse it, render it to pixels, HTML, plain text, or ESC/POS binary.
Jump to: Syntax · JavaScript Quick Start · CLI Quick Start · Rust Quick Start
@printer-width(80mm)
@style(title, mona_sans_black.ttf, 22)
|10 | Order \#1042 |> 02/25/2026 |10
|10 | Cashier: Homer S. |> 12:34 PM |10
|10 | Table: 7 |> Dine-In |10
### \#234 ###
|> @logo(receipt.png, 125) <|
|> 742 Evergreen Terrace <|
|> Springfield, IL 62704 <|
|> (555) 867-5309 <|
| *Qty* |< *Item* |> *Price*
|-x.x x2 |-x.x< Classic Burger |-x.x> $17.98
| x1 |< Cheese Fries |> $4.50
| |< `add bacon` |> $1.50
| |< `add jalapeño` |> $0.75
|.x x2 |.x< Lg Lemonade |.x> $6.50
|-x x1 |-x< Kids Meal\* |-x> $5.99
|40 |40> Subtotal |> $37.22
|40 |40> Discount (10%) |> ~-$3.72~
|40 |x=xx40> *TOTAL* |> *$36.83*
|10 |--x-80> Payment <|10
|10 |-xx- |-xxx Visa x4821 |-xxx20 $36.83 |--xx> |10
|10 |xx-- |xx-x _Tip_ |xx-x20 _$6.00_ |x--x> |10
@feed(1/2)
|> @barcode(CODE128, ORD-1042-20260225) <|
@feed(1/2)
## How Did We Do? ##
@feed(1/2)
|10 |. Rate your experience! |10
|10 |. Visit burgerbarn.com/feedback |10
|10 |. Use code: *BARN1042* |10
@feed(1/2)
|> @qr(https://burgerbarn.com/receipt/1042, 75) <|
|> Thank you for dining with us! <|
|> *See you soon!* <|
@feed(3)
@cut()
@drawer()
See SPEC.md for the full language reference. The short version:
- Text: just type it
- Styles:
*bold*_underline_`italic`~strikethrough~ - Sizes:
## header ##(more#= bigger),++ body ++(more+= bigger) - Columns:
| left | right |with>/<for alignment - Dividers:
---thin,===thick,...dotted - Directives:
@image()@qr()@barcode()@cut()@feed()@drawer()@style()@printer-width()@printer-dpi()@printer-threshold()
npm install rip-receiptimport { parse, renderHtml, renderText, renderImage, renderRaster, renderEscpos } from 'rip-receipt';
// Parse once, render many ways
const doc = parse("## Hello\n---\n| Item |> $5.00 |");
// HTML — standalone document with inline styles and SVG barcodes/QR
const html = await renderHtml(doc);
// Plain text
const text = await renderText(doc);
// 1-bit PNG image (matches thermal printer output)
const png = await renderImage(doc, { resourceDir: './assets' });
// ESC/POS raster print commands
const raster = await renderRaster(doc, { resourceDir: './assets' });
// ESC/POS binary commands (text engine + inline images)
const escpos = await renderEscpos(doc, { resourceDir: './assets' });Local images are loaded by the native Rust runtime — no sharp or JS-side image handling needed. For remote URLs, use resolveResources() to discover what needs fetching and pass bytes via config.resources.
cargo install --path rip_cli
rip receipt.rip output.png # grayscale PNG
rip receipt.rip output.html # HTML
rip receipt.rip output.txt # plain text
rip receipt.rip output.bin # ESC/POS binary
rip receipt.rip output.raster # 1-bit packed raster
rip receipt.rip --bench # benchmark all renderers
let nodes = rip::parse("## Hello\n---\n| Item |> $5.00 |");
let config = rip::ResourceConfig::default();
// 1-bit PNG image
let png = rip::render_image(&nodes, &config).unwrap();
// HTML
let html = rip::render_html(&nodes);
// Plain text
let text = rip::render_text(&nodes);
// ESC/POS binary (thermal printer commands)
let escpos = rip::render_escpos(&nodes, &config);| Crate | What it does |
|---|---|
rip |
Unified API — start here |
rip_parser |
Parses .rip markup into an AST |
rip_image |
Renders to grayscale pixels |
rip_html |
Renders to standalone HTML |
rip_text |
Renders to plain text |
rip_escpos |
Renders to ESC/POS binary |
rip_resources |
Local file loading, image decoding, caching |
rip_cli |
CLI tool for rendering files |
rip_nodejs |
Native Node.js addon → npm rip-receipt |
rip_android |
Android/Kotlin bindings via JNI |
Images, QR codes, and barcodes referenced in markup are handled by rip_resources:
- Local files — resolved relative to a
resource_diryou provide - Remote URLs — the host fetches them; use
resolve_resources()to discover what's needed, then pass bytes viaconfig.resources - Caching — optional
cache_direnables disk caching of downloads and processed images
use rip::{parse, resolve_resources, render_image, ResourceConfig};
let nodes = parse("@image(logo.png)\n## Receipt");
let mut config = ResourceConfig {
resource_dir: Some("./assets".into()),
cache_dir: Some("./cache".into()),
..Default::default()
};
// For remote URLs: fetch what's needed, then populate config.resources
let needed = resolve_resources(&nodes, &config);
for url in &needed {
// fetch url bytes with your preferred HTTP client...
// config.resources.insert(url.clone(), bytes);
}
let png = render_image(&nodes, &config).unwrap();Each host brings its own HTTP client — Node.js uses fetch(), Android uses OkHttp, the CLI uses ureq. The core library does zero network I/O, keeping the binary small and cross-compilation simple.
MIT OR Apache-2.0
Claude Code was heavily used in the creation of the code in this project.
