Skip to content

Saving & export

Ferrum charts render to SVG, PNG, HTML, and JSON — no system dependencies, no display server, no matplotlib. Every chart object (base charts, compound views, helper output, diagnostic charts) supports the same export surface.

Output formats

Format Method File extension Notes
SVG .show_svg() .svg Vector graphics. Default render path.
PNG .show_png() .png Rasterized via resvg in Rust. No Cairo/Pillow needed.
HTML .save("out.html") .html Self-contained interactive page with inlined WASM renderer.
JSON .save("out.json") .json Chart spec as JSON — the same format as .to_json().

Saving to disk

.save() infers the format from the file extension:

import ferrum as fm
import polars as pl

df = pl.DataFrame({"x": [1.0, 2.0, 3.0], "y": [2.0, 4.0, 3.0]})
chart = fm.Chart(df).mark_point().encode(x="x", y="y")

chart.save("scatter.svg")   # vector
chart.save("scatter.png")   # raster
chart.save("scatter.html")  # interactive (WASM inlined)
chart.save("scatter.json")  # spec

Pass format= explicitly to override the extension:

chart.save("output", format="svg")

Controlling auto-raster

At high mark counts (default threshold: 500,000), Ferrum transparently substitutes a raster image for per-element SVG marks. Override this per-call with raster=:

chart.show_svg(raster=False)   # force vector even at high counts
chart.save("out.svg", raster=False)
chart.show_png(raster=True)    # force raster even at low counts

For persistent control, attach a RenderConfig to the chart:

from ferrum import RenderConfig

config = RenderConfig(raster_threshold=1_000_000, raster_behavior="silent")
chart = chart.render_config(config)
chart.save("out.svg")  # auto-raster fires at 1M marks, silently

RenderConfig parameters: raster_threshold (mark count or None to disable), raster_behavior ("warn", "silent", "error"), raster_aggregate, and raster_cmap.

Getting raw bytes

For programmatic use (embedding in notebooks, serving from a web app, writing to S3):

svg_str = chart.show_svg()   # str — complete <svg>…</svg> document
png_bytes = chart.show_png()  # bytes — raw PNG data

Displaying in Jupyter

In a Jupyter notebook, charts render automatically via _repr_svg_ — just put the chart as the last expression in a cell:

chart  # renders inline as SVG

For interactive rendering (selections, zoom/pan), call .interactive() instead — see Interactive rendering.

Outside of a notebook, .show() writes a temporary SVG and opens it in the system browser.

HTML export

.save("file.html") produces a self-contained HTML file with the WASM GPU renderer and scene data inlined. No server, no CDN, no external dependencies — the file works offline in any modern browser.

This is the right format for sharing interactive charts via email, Slack, or static hosting.

Compound views

All composition operators produce objects with the same export surface. A four-panel report saves exactly like a single chart:

report = (roc | calibration) & (confusion | residuals)
report.save("model_report.svg")
report.save("model_report.png")

No system dependencies

Ferrum's rendering pipeline is pure Rust. SVG rendering, PNG rasterization (resvg), and WASM compilation all happen inside the wheel. There is no dependency on Cairo, X11, Ghostscript, or any display server. pip install ferrum is the entire setup — charts render in Kubernetes, CI, SSH sessions, and headless containers.

Where to go next