#plot #monochrome #svg #graphics #technical-drawing

no-std bin+lib bland

Pure-Rust library for paper-ready, monochrome, hatch-patterned technical plots in the visual tradition of 1960s-80s engineering reports

3 unstable releases

Uses new Rust 2024

0.2.1 Apr 30, 2026
0.2.0 Apr 29, 2026
0.1.0 Apr 29, 2026

#125 in Visualization


Used in 2 crates

MIT license

5.5MB
193K SLoC

BLAND

Pure-Rust library for paper-ready, monochrome, hatch-patterned technical plots in the visual tradition of 1960s–80s engineering reports.

BLAND emits SVG. Plots look at home next to a title block, a set of fastener callouts, and a stack of punched cards — black ink on white paper, serif type, thin rules, hatched fills. No color, no gradients, no drop shadows.

use bland::{Figure, PaperSize, Stroke, TitleBlock};

let xs: Vec<f64> = (0..=200).map(|i| i as f64 / 20.0).collect();
let response: Vec<f64> = xs.iter().map(|t| (-t / 4.0).exp() * t.cos()).collect();
let envelope: Vec<f64> = xs.iter().map(|t| (-t / 4.0).exp()).collect();

let fig = Figure::new()
    .size(PaperSize::A5Landscape)
    .title("Damped oscillation")
    .xlabel("t [s]")
    .ylabel("x(t)")
    .line(&xs, &response, |s| s.label("response"))
    .line(&xs, &envelope, |s| s.label("envelope").stroke(Stroke::Dashed))
    .hline(0.0, |s| s.stroke(Stroke::Dotted))
    .legend_top_right()
    .title_block(
        TitleBlock::new()
            .project("BLAND Reference")
            .title("Fig. 1 · Damped oscillation")
            .drawn_by("JM")
            .date("2026-04-21")
            .scale("1:1")
            .sheet("1 of 1")
            .rev("A"),
    );

std::fs::write("oscillation.svg", fig.to_svg()).unwrap();

See the docs.rs page for embedded example images of every plot type.

Why monochrome?

  • Prints clean. Plots look the same on a laser printer, a color printer, and a photocopy. No "figure unreadable in proceedings" surprises.
  • Accessible by default. Hatching, stroke dashing, and marker shape distinguish series — so plots survive grayscale rendering and are legible to readers with color vision deficiency.

Features

  • Series: line, scatter, bar (grouped), area, histogram, heatmap, polygon, error bars, box plots, stem plots, quiver / vector fields, contour iso-lines, reference rules
  • Hatch patterns: 13 presets — diagonals, crosshatch, dots, brick, zigzag, checker
  • Markers: 12 shape presets, alternating open / filled
  • Stroke dashes: solid, dashed, dotted, dash-dot, long-dash, fine
  • Paper presets: A4, A5, Letter, Legal, Square — portrait & landscape
  • Themes: report_1972 (serif), blueprint (monospace), gazette (newspaper)
  • Engineering title block with project / drawn-by / date / scale / sheet / revision
  • Annotations: in-data text and arrows with anti-overlap halos
  • Linear and log axes with nice-rounded tick placement
  • Projections: polar, Smith chart, Mercator, equirectangular
  • Built-in basemaps: Earth coastlines & borders (Natural Earth 1:110m, optional 1:50m), reference parallels, lunar maria
  • Multi-panel layouts and composite helpers (Bode, Q-Q)
  • Pure Rust, zero runtime dependencies

Cargo features

  • gui — adds Figure::show(): a matplotlib-style blocking display window that renders the figure in an OS-native webview, with a Save SVG button and zoom controls. Pulls in tao and wry. On Linux you must have libwebkit2gtk-4.1-dev installed system-wide.
  • high-res-basemaps — adds Natural Earth 1:50m coastline and country data (~3 MB compiled). Off by default; the 1:110m datasets and the schematic outlines are always available.
  • regen-basemaps — builds the dev-only regen_basemaps binary that re-converts Natural Earth GeoJSON into the static Rust data modules. Off by default so cargo install bland does not install a developer tool.

Quick GUI usage

use bland::{Figure, PaperSize};

# fn main() {
let xs: Vec<f64> = (0..=100).map(|i| i as f64 / 10.0).collect();
let ys: Vec<f64> = xs.iter().map(|t| t.sin()).collect();

Figure::new()
    .size(PaperSize::A5Landscape)
    .title("sin(t)")
    .line(&xs, &ys, |s| s)
    .show();  // blocks until the window is closed
# }

Run the bundled demo with:

cargo run --features gui --example show_demo

Examples

The repo ships 22 runnable examples covering every plot type. Run any of them via:

cargo run --example damped_oscillation
cargo run --example world_map
cargo run --example smith

Output goes to out/.

License

MIT.

This crate ports the Elixir BLAND library; the Natural Earth data embedded under src/basemaps/data/ is in the public domain.

Dependencies

~0–43MB
~539K SLoC