Skip to content

Commit fd36ffb

Browse files
committed
Add dependency-free text renderers for go.Figure (text-utf/ascii/ansi/html)
Add four pure-Python, dependency-free plotly.io renderers that draw an existing go.Figure as plain text: - text-utf — braille/block-char (default) - text-ascii — 7-bit ASCII fallback - text-ansi — 24-bit truecolor ANSI escapes - text-html — self-contained class-based HTML fragment Usable via fig.show(renderer="text-utf") (etc.). Supports scatter/scattergl (lines & markers), bar (honouring orientation, with grouped fan-out), histogram, and heatmap/histogram2d (colorscale-sampled density shading). Per-series colour is taken from each trace's marker/line colour or the default qualitative palette. Unsupported traces and undersized canvases degrade to a one-line note instead of raising. Adds unit tests and a CHANGELOG entry.
1 parent 9611be4 commit fd36ffb

17 files changed

Lines changed: 4409 additions & 0 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
44

55
## Unreleased
66

7+
### Added
8+
- Add pure-Python, dependency-free text renderers `text-utf` (braille/block-char, default) and `text-ascii` (7-bit fallback) that draw an existing `go.Figure` as plain text — `fig.show(renderer="text-utf")` / `"text-ascii"`. Supports scatter/scattergl (lines & markers), bar (honouring `orientation`), and histogram (bins reproduced client-side, numpy-accelerated when available); multi-series bars fan out into sub-columns, unsupported traces and undersized canvases degrade to a one-line note instead of crashing, and output is written as forced UTF-8.
9+
- Add colour text renderers `text-ansi` (24-bit truecolor ANSI escapes) and `text-html` (self-contained class-based HTML fragment) — `fig.show(renderer="text-ansi")` / `"text-html"` — and `heatmap` / `histogram2d` trace support (density shaded via a colorscale-sampled ramp, `histogram2d` binned client-side). Each series is coloured from its own `marker`/`line` colour (normalized from `#hex`, `rgb()/rgba()`, or a CSS name) or the default qualitative palette.
10+
711

812
## [6.9.0] - 2026-07-09
913

plotly/io/_renderers.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,13 @@ def show(fig, renderer=None, validate=True, **kwargs):
467467
renderers["sphinx_gallery"] = SphinxGalleryHtmlRenderer()
468468
renderers["sphinx_gallery_png"] = SphinxGalleryOrcaRenderer()
469469

470+
# Text renderers: text-utf (default), text-ascii, and the colour modes
471+
# text-ansi / text-html.
472+
# Registered via a hook so the _text subpackage owns its own renderer classes.
473+
from plotly.io._text import register_text_renderers # noqa: E402
474+
475+
register_text_renderers(renderers)
476+
470477
# Set default renderer
471478
# --------------------
472479
# Version 4 renderer configuration

plotly/io/_text/__init__.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""Text (Unicode/braille) renderers for ``go.Figure``.
2+
3+
Draws an existing figure as a braille/block-char chart in plain text, as
4+
registered ``plotly.io`` renderer strings (``text-utf`` default, ``text-ascii``;
5+
plus the colour modes ``text-ansi`` and ``text-html``). Pure-Python, **no
6+
external dependency** — the braille + block-char rasterizer is built in here (see
7+
:mod:`~plotly.io._text.rasterizer`).
8+
9+
Architecture ("one grid, many serializers"):
10+
11+
* :mod:`~plotly.io._text.canvas` — Plotly-agnostic drawing surface -> abstract
12+
cell grid.
13+
* :mod:`~plotly.io._text.serializers` — per-mode grid -> string.
14+
* :mod:`~plotly.io._text.adapters` — ``go.Figure`` -> Canvas calls.
15+
* :mod:`~plotly.io._text.renderers` — ``plotly.io`` registration.
16+
17+
This ``__init__`` is the stable public surface of the subpackage;
18+
:mod:`plotly.io._renderers` imports :func:`register_text_renderers` from here.
19+
"""
20+
21+
from __future__ import annotations
22+
23+
from plotly.io._text.canvas import (
24+
Canvas,
25+
Cell,
26+
CellGrid,
27+
CellRole,
28+
Tick,
29+
)
30+
from plotly.io._text.serializers import (
31+
SERIALIZERS,
32+
Serializer,
33+
get_serializer,
34+
register_serializer,
35+
)
36+
from plotly.io._text.adapters import (
37+
ADAPTERS,
38+
AdapterContext,
39+
TraceAdapter,
40+
figure_to_canvas,
41+
get_adapter,
42+
register_adapter,
43+
)
44+
from plotly.io._text.renderers import TextRenderer, register_text_renderers
45+
46+
__all__ = [
47+
"Canvas",
48+
"Cell",
49+
"CellGrid",
50+
"CellRole",
51+
"Tick",
52+
"Serializer",
53+
"SERIALIZERS",
54+
"register_serializer",
55+
"get_serializer",
56+
"ADAPTERS",
57+
"AdapterContext",
58+
"TraceAdapter",
59+
"register_adapter",
60+
"get_adapter",
61+
"figure_to_canvas",
62+
"TextRenderer",
63+
"register_text_renderers",
64+
]

0 commit comments

Comments
 (0)