wangjiawen2013/charton
 Watch   
 Star   
 Fork   
5 days ago
charton

v0.9.0

charton 0.9.0

0.9.0 turns a chart from a picture into a document you can style and describe. SVG and PDF output now carry semantic structure, paths and polygons became first-class marks with real holes, and axis ticks are chosen from the labels that were actually rendered. charton and nu_plugin_charton ship together at 0.9.0; the plugin targets Nushell 0.116.

The picture itself does not change. Every new class and attribute is inert markup until a stylesheet matches it, and the default PDF looks the same — it just carries a structure tree as well.

Highlights

  • Semantic output: role classes, data-* identity and accessibility metadata in SVG, plus tagged PDF.
  • mark_path and mark_polygon are separate marks, with multi-ring paths and true holes.
  • Tick counts follow the rendered labels instead of a fixed pixel budget.
  • Deterministic, collision-free SVG ids, so several charts can share one page.
  • Axis layout and axis drawing read the same object, so the reserved band and the drawn axis cannot disagree.

Breaking changes

  • RenderBackend has semantic scopes instead of clip scopes. begin_clip_scope / end_clip_scope are gone; begin_group / end_group take a core::semantic::Group carrying the role, the identity and, optionally, the clip rectangle. begin_element / end_element annotate a single observation. Custom backends must be updated.
  • RenderBackend has one vector-path primitive. draw_polygon and PolygonConfig were removed; draw_path now takes subpaths: Vec<Vec<(Precision, Precision)>> and a PathTopology::{Simple, Convex, Complex} hint. Simple is open and stroked, Convex is a single closed convex ring, Complex is one or more closed rings filled with the even-odd rule.
  • The geo aliases are gone. MarkGeoPath, mark_geoshape and configure_geoshape were removed. mark_path / configure_path draw open, stroked paths, while mark_polygon / configure_polygon draw closed, filled polygons. mark_type() now returns path or polygon, which also changes the SVG class and data-mark value of those layers. Geographic maps use mark_polygon with CoordSystem::Geo: projection belongs to the coordinate system, not to the mark.
  • GradientRectConfig::id_suffix is now field. The value never chose the gradient id — the document namespaces ids — it names the data field the ramp visualises.
  • IntoLayered gained required methods: to_svg_with, and with the pdf feature to_pdf / to_pdf_with. Implementations outside this crate need bodies for them.
  • The default SVG document changed. Groups carry semantic classes and data-* attributes, ids are namespaced (charton-<hash>-clip-0 instead of plot-clip-area-0), and <defs> is emitted once before the body. Code that parses SVG text must address classes instead of fixed ids; code that only renders it is unaffected.
  • PDF output is tagged by default. The picture is identical, but the file now carries a structure tree. PdfOptions::with_a11y(A11y::Off) writes the plain document.
  • Text drawn to PNG is larger at the same font_size, because raster text is now sized in ems like SVG and PDF — about 17% with the default Inter stack, more with a CJK fallback face. scale_factor also defaults to 1.0, so with_size(w, h) produces a w × h image; pass with_scale_factor(2.0) to ask for the previous density.
  • RenderBackend::draw_text now returns Result<(), ChartonError>, so backends that resolve fonts themselves (PNG, PDF) can report an unknown family. SVG and WGPU, which defer font selection, return Ok(()).

Added

  • Semantic output: classes, identity attributes and accessibility metadata. Every rendered group carries its role as a CSS class (charton-tick-label, charton-grid, charton-mark-<layer>, charton-legend, …) plus data-* identity (data-layer, data-mark, data-series, data-axis), so a page stylesheet can restyle a chart by role instead of by element type. The root element gains role="img" or graphics-document, <title> / <desc>, and aria-labelledby / aria-describedby; decorative groups are hidden in document mode.
  • SvgOptions and A11y, also available from the prelude, plus IntoLayered::to_svg_with and with_description(). to_svg_with selects the accessibility mode, adds custom <defs> fragments and fixes the id namespace.
  • Per-series and per-value identity. Marks are grouped per series (class="charton-series" plus data-series="Ford") inside their layer, and elements standing for a single observation — each bar, each tick label — carry that value as data-value="2020" on the drawn element itself, so one selector can restyle a series or the bar and tick label of one category. begin_element / end_element is the backend-neutral way to annotate them.
  • Multi-ring paths and real holes. PathConfig carries independent subpaths, and closed paths use the even-odd fill rule on SVG, raster, PDF and WGPU. An outer ring plus its inner rings becomes one filled region with real holes; PathGroup::with_subpath() selects the column that separates rings.
  • GeoJSON keeps every ring. geojson_to_dataset now emits _ring_id in addition to _path_id and no longer drops interior rings. Geographic maps use path_group("_path_id").with_subpath("_ring_id").
  • Tagged PDF. Role::structure() maps the same semantic roles onto PDF structure elements: background, grid and frame become /Artifacts, the chart body becomes a /Figure whose /Alt is the title and description, the title becomes a /Caption, and axes and legend become /P blocks. PdfOptions selects tagging, pretty() writes an inspectable, uncompressed document, and to_pdf_with / LayeredChart::to_pdf_with expose it.
  • Deterministic, collision-free SVG ids. Ids are namespaced by a hash of the finished drawing instead of the fixed plot-clip-area-0 / grad_<field> names, so several charts can be inlined into one page without stealing each other's clip paths and gradients. The same chart still renders to the same bytes.
  • Theme::with_tick_count(n) pins how many ticks every axis aims for, for charts that must match a reference figure; with_auto_tick_count() returns to the default.
  • A wide logarithmic axis subdivides its decades. With room, it labels the 2x and 5x multiples as well (1, 2, 5, 10, 20, 50, ...) instead of only powers of the base.
  • New documentation: a "Fonts & Text Rendering" chapter and docs/src/export/semantic_output.md, which explains the classes, attributes and accessibility modes. examples/semantic_output.md and examples/semantic_demo.html show the markup in context.

Changed

  • <defs> is emitted once, before the body. Clip paths and gradients used to be written inline where they were needed.
  • Attribute values are escaped. A theme font stack containing & or " used to produce a document that no XML parser accepts.
  • Axes share one pipeline. core::axis::AxisView resolves which data scale sits on which physical side, including coord_flip, its title, label rotation, explicit ticks and band geometry. The layout engine and the renderers read the same object; render::axis_renderer draws Cartesian and Geo panels, replacing the duplicated Geo renderer, so Geo gains label-aware tick density, theme tick and axis colours, label rotation and consistent explicit-tick handling. Axis titles are owned by the coordinate system instead of being passed alongside it.
  • Tick counts follow the rendered labels instead of a fixed pixel budget. The panel length sets a budget of one label per four tick-font sizes, clamped to 3..12; the scale snaps it to its own nice values; the labels that were really produced are measured, rotation included, and the count is lowered until neighbours no longer touch. Short labels pack an axis, long dates or category names thin it out, and a larger canvas no longer adds labels whose text keeps the same physical size. If even the coarsest step does not fit, only the two ends are labelled; values always stay on the scale's own ladder.
  • Theme::tick_length is applied. Tick marks were hard-coded to 6px; layout and drawing now both read the theme value, and axis titles are placed from the ticks that are actually drawn, explicit values included.
  • with_font_scale scales the text layout, not just the glyphs. Axis title and tick padding, the main title padding, legend spacing and facet strip padding scale with the font, so enlarging text no longer crowds it.
  • Theme::label_padding is the whole tick-label to axis-title gap. The hidden 5px constant the renderer added is gone and the default is 10px, so the default gap is unchanged; a custom value now describes the whole gap rather than half of it.
  • Raster text uses no hidden letter spacing. The raster backend added 0.3px of tracking after every character that SVG and PDF never applied, which pushed centre- and right-anchored text past its own measured width; it is gone.
  • Raster output is WYSIWYG: to_png() and the wgpu PNG export render internally at 2x and box-downsample, so sharpness is no longer the caller's problem. with_scale_factor is now purely a pixel-density knob for explicitly requesting a higher-density asset (Retina, print, terminal fitting) and no longer affects the default size or anti-aliasing.
  • Raster size arithmetic no longer truncates fractional scale factors with as u32; internal and output sizes are rounded consistently.

Fixed

  • Axis titles sit exactly in the strip measured for them. The x title ignored the tick-label padding, and the left title and tick labels carried their own magic offsets; both now come from the shared AxisBand geometry.
  • GeoJSON interior rings are no longer discarded. A polygon with holes used to render as its solid outer ring; the dataset keeps every ring and the renderer sends them as subpaths of one even-odd path.
  • The star point glyph no longer uses the convex fast path. Its inner vertices make it concave, so it now uses PathTopology::Complex; regular polygons keep the fast path.
  • Explicit tick labels are measured into the axis band. with_x_ticks / with_y_ticks values were drawn but not measured, so a long explicit label could be clipped at the canvas edge.
  • An axis whose labels are too long no longer stacks them: it moves to the scale's next coarser step and labels only the two ends as a last resort.

Removed

  • Theme::with_tick_min_spacing, its tick_min_spacing field, and Theme::suggest_tick_count. A fixed pixel budget cannot know how wide a label is; leave the default for the adaptive fit, or use with_tick_count to pin a density.
  • MarkGeoPath, mark_geoshape and configure_geoshape; draw_polygon and PolygonConfig. Their replacements are listed under breaking changes.

Nushell plugin 0.9.0

  • --description <string> sits next to -t/--title: it describes the chart for assistive technology, is exported as the SVG <desc> and as the PDF figure's alternative text, and is never drawn.
  • --tick-count <int> asks every axis for about that many ticks, for figures that must match a reference or a row of panels. It is a per-command flag, not a config key, so omitting it keeps the automatic, label-aware fit; explicit --x-ticks / --y-ticks still win.
  • Exported SVG files are semantic documents, carrying role classes, identity attributes and an accessible name, so a page stylesheet can restyle an exported chart without re-rendering it.
  • Ticks follow the rendered labels, so long dates or category names get fewer ticks, and a larger --width / --height no longer makes the axis denser. --font-scale changes the count naturally, because bigger text gets fewer labels.
  • Explicit --x-ticks / --y-ticks labels are measured into the margin and are no longer clipped.
  • charton geo draws through the same axis pipeline as the other marks, so its grid, labels, tick density, theme colours and label rotation match cartesian charts.
  • charton geo and charton path pick up the library fixes above: GeoJSON holes render as real holes, and charton path now applies --color to the stroke and honours --dash instead of writing the colour to a fill an open path never paints.
  • Reversing a numeric category axis is rejected instead of drawing a wrong axis.
12 days ago
charton

v0.8.5

Category axes now fit the geometry marks actually draw, and the raster/PDF font pipeline is consistent with SVG.

Highlights

  • Automatic category-axis headroom. Axes built with with_category_labels are trained from the numeric positions the marks actually occupy, not just the category indices. An overlapping ridgeline, a dodged lane at the edge, or any composed geometry now expands the axis instead of being clipped. with_expansion / --x-expand remain availableas optional fine-tuning for the breathing room.
  • Font stacks, fallback and text metrics. The raster and PDF backends now resolve CSS-style family stacks left to right, fall back per character for missing glyphs (including m² and CJK), draw text at the requested em size, and measure CJK labels at their real width. Unknown families now return ChartonError::Font instead of silently falling back to Inter.
  • WYSIWYG raster output. with_size(w, h) now produces a w × h image; scale_factor defaults to 1.0 and is purely a pixel-density knob. Sharpness is handled internally by supersampling.
  • MarkText font controls. with_font_family, with_italic, and with_font_style complete the text mark.

Text layout

  • with_font_scale now scales the whole text layout. Font sizes and the gaps around that text scale together: main title padding, axis title/tick padding, legend spacing, and facet strip padding. Enlarging glyphs alone made axis titles crowd their tick labels; the Nushell plugin's default --font-scale 1.5 is now visually proportional to a 1.0 chart.
  • Theme::label_padding is now the whole tick-label ↔ axis-title gap. The renderer previously added a hidden 5px constant; that constant is gone and the default label_padding is 10px. The default gap is unchanged, but a custom with_label_padding value now describes the whole gap rather than half of it.

Breaking / behavior changes

  • PNG text is about 17% larger for the default stack (the font's line-box ratio; fonts such as Microsoft YaHei grow more).
  • An unknown font family is now an error for PNG/PDF instead of a silent substitution.
  • RenderBackend::draw_text now returns Result<(), ChartonError>.
  • ScaleDomain gained a DiscretePositions variant; downstream exhaustive matches must handle it.
  • Reversing a numeric category axis (with_reverse together with with_category_labels) is rejected, because flipping only the labels would mislabel every mark. Reorder the categories instead.
  • Raster output no longer defaults to 2× size; with_scale_factor only changes density, not the configured output size.

Nushell plugin (nu_plugin_charton 0.8.5)

Targets Nushell 0.116.

  • Y-axis titles no longer crowd their tick labels at the default --font-scale 1.5; text spacing now scales with the font.
  • charton violin is now the plain density outline; the inner-box form is a recipe built with --layer.
  • Inline rendering uses an explicit transport (--inline-style, CHARTON_INLINE_STYLE, default halfblock) and no longer sniffs terminal-brand environment variables.
  • The default raster scale is 1.0, matching the library; --scale 2.0 requests a denser asset.
  • charton text --italic.
  • Ridgeline recipes no longer need --x-expand.
  • Waterfall fixes: assets/waterfall.csv now matches the example, and --y2 is preserved on the --transform route.

Documentation

  • New Fonts & Text Rendering chapter covering family stacks, fallback, em sizing, text scaling, unknown-family errors, and per-backend behaviour.

Full changelogs

13 days ago
charton

v0.8.4

This release adds font scaling and cell padding to the library, and reworks the Nushell plugin around Charton's grammar primitives. The charton crate and nu_plugin_charton keep a single lockstep version number.

Library (charton)

Added

  • Theme::with_font_scale(factor) — scales every font size (title, axis labels, tick labels, legend, facet labels) in one call. Useful when rendering the same chart at a larger text ratio, e.g. fitting it into a terminal.
  • MarkRect::with_cell_padding(padding) — grows/shrinks each heatmap tile on every side, in device pixels (default 0.5).
  • RowAccessor::entries() — returns the current row as (column name, value) pairs, so a transform_calculate callback can serialise the whole row (e.g. to hand it to an external expression evaluator).

Fixed

  • Heatmap tiles no longer show white seams. The raster backend drew a hairline for zero-width strokes; it now skips them, matching the SVG backend. rect / density_2d also overlap tiles by the default cell_padding so anti-aliasing cannot leave gaps between neighbours.

Nushell plugin (nu_plugin_charton)

Changed (breaking)

  • Marks are now subcommands. charton -g bar … → charton bar …. Each mark declares only the flags it understands, so help charton bar and tab-completion no longer list unrelated options. The -g/--geom flag is gone.
  • The mark list now matches Charton's — only real marks remain (point, line, path, polygon, bar, area, rect, rule, text, tick, hist, boxplot, violin, errorbar, density, density-2d, contour, geo). Tier-3 recipe commands are removed.
  • Recipes are written from grammar primitives. The ridge, ecdf, candlestick, dumbbell, lollipop, range, slope, bump, and waterfall subcommands are removed; use --transform, --layer, --path-group, and floating bar (--y2). The README and book show each recipe written out.
  • beeswarm is now charton point --layout beeswarm, matching Charton (swarm is a point layout, not a mark).
  • path/polygon are available with the --path-group channel; bar supports floating bars via --y2.
  • Flag values autocomplete for --stack, --aggregate, --coord, --density-kernel, --shape, --interpolation, --layout, --x-scale/--y-scale, --legend, --theme, --inline-style, --facet-strategy, --color-map, and others.
  • --layer accepts a bare mark name (--layer point alongside --layer {geom: point}); layers inherit the primary encoding and lists may mix names and records.

Added

  • --transform {op: calculate, as: <col>, expr: {|row| …}} runs a Nushell closure per row, matching transform_calculate. The row is a record, so it can read columns produced by earlier transforms — this is how the waterfall recipe computes base.
  • --font-scale (and the font_scale config key) scales every chart font. Default is now 1.5 (terminal charts), 1.0 for original sizes, up to 2.0.
  • --x-expand / --y-expand accept [low, high] for asymmetric additive padding in axis units, and now apply to the --transform / path / polygon route (gives ridgelines headroom).

Fixed

  • A typo in a column name now reports the available columns instead of a low-level Column 'x' not found.
  • Heatmaps no longer show white seams; --cell-padding (default 0.5) controls overlap — negative values open a deliberate gap.
  • Hand-written violins and ridges share the category axis with points and boxes; band / quantile-box label the x axis with center / category.
  • A --layer style record accepts every styling field (mark-width, cell-padding, cap-length, outlier-size, loess, loess-bandwidth, boolean center/outliers); keys accept kebab- or snake-case, and unknown keys or wrong types now error instead of silently no-op.
24 days ago
charton

v0.8.3

nu_plugin_charton

Fixed

  • Cookbook recipes now use the right data shape. Every recipe in the README and the book reads a purpose-built table from assets/ instead of the single data.csv, which only fits the distribution marks. Previously the series charts degenerated: bump ranked one row per period (every series stuck at rank 1), waterfall accumulated all 150 rows, dumbbell/lollipop/range drew dozens of overlapping marks per category, and slope became a plain line chart.
  • -g slope / -g bump keep their dots on the line. With a discrete x-axis the dots were dodged into colour lanes while the line was not, so points and trajectories didn't meet. Colour is now an attribute, not a lane.
  • -g candlestick distinguishes up days from down days. assets/ohlc.csv gained a direction (Up/Down) column, and the recipe uses -c direction --x-angle -45, so up/down days get different colours and the date labels no longer overlap.

Changed

  • dumbbell, lollipop, and range recipes add -flip to match the horizontal layout of the library's cookbook examples.
  • The README now documents every sample table in assets/ (columns + which chart uses it), and the source comments describe each composition's function.

Added

  • New sample tables: dumbbell.csv, lollipop.csv, range.csv, slope.csv, bump.csv, waterfall.csv; ohlc.csv gained a direction column.

charton

  • Version bump only; no changes since 0.8.2.
25 days ago
charton

v0.8.2

v0.8.2

25 days ago
charton

v0.8.1

Release Note — v0.8.1 (2026-10-05)

charton 0.8.1 — convenience marks, floating bars, reversed axes, and a cleaner position model. nu_plugin_charton 0.8.1 ships in lockstep.

✨ Added

  • Convenience composite marks — mark_violin, mark_density, mark_contour, mark_density_2d. Each is a thin recipe over the public transforms and renderers (no new statistic, no new drawing path), byte-for-byte identical to the hand-written composition, and tunable with configure_violin / configure_density / configure_contour / configure_density_2d.
  • Dashed paths and polygons — mark_path / mark_polygon gained with_dash([dash, gap]), measured along the arc length, on SVG, raster, PDF and WGPU.
  • Per-channel display labels — with_label(…) on X, Y, Color, Shape, Size; titles resolve as chart label → channel label → field name.
  • Three new general capabilities, one per grammar layer:
    • geometry — mark_bar accepts a y2 bound for floating bars (waterfall steps, candlestick bodies); colour stays an attribute and the axis is not forced to zero.
    • statistic — transform_window gains WindowOnlyOp::CumulativeSum (running totals).
    • scale — alt::y(…).with_reverse(true) flips an axis (rank 1 at the top); reversed domains still expand and tick correctly.
  • One-sided and overlapping bands — BandTransform::with_side(BandSide::{Both,Left,Right}) and with_overlap(f); with_split is now explicitly a two-group option. A ridgeline is just transform_density + a one-sided band + with_overlap + coord_flip.
  • Position model tightened — mark_point now dodges only on a categorical x axis (with_dodge(false) opts out even there); Position::DEFAULT_DODGE_SPACING is the single default for the outline/point family, while fill/interval marks tile.

🐛 Fixed

  • WGPU closed-polygon outlines no longer drop their closing edge (violin/box/custom outline was left open on the GPU backend).
  • WGPU dashed strokes now honour the dash pattern instead of drawing solid.

🔧 Changed

  • Missing values follow the ggplot2 / Vega-Lite rule everywhere — a null in a positional channel (x/y) drops the row (and breaks a line/area), a null in color is kept as a grey NA level, a null in a transform group is kept as an NA group, and a null in shape/size falls back to the default.

🧩 nu_plugin_charton 0.8.1

  • Eight new -g recipe geoms, all compositions of the existing marks: ridge, dumbbell, lollipop, range, slope, bump, waterfall, candlestick (with the new --low / --high flags). They inherit the shared lane/gap conventions, so a candlestick body lines up with -g bar and a ridge with -g violin.
26 days ago
charton

v0.8.0

Violin plots and contour plots, built from general parts — plus a new Cookbook. Two crates ship as a pair: charton 0.8.0 and nu_plugin_charton 0.8.0 (targets Nushell 0.116).

charton 0.8.0

Violin plots, composed from primitives

There is still no violin mark and no violin-specific transform. A violin is a short recipe you can also write by hand:

  • transform_density + mark_area with with_stack("mirror") — the Vega-Lite/Altair recipe.
  • transform_density + transform_band + transform_quantile_box — the ggplot2 stat_ydensity recipe, with dodging and split halves.

DensityTransform::with_trim(true) evaluates each group over its own observed range, so violins of different spread get different heights and no thin tails (the density-plot default stays untrimmed).

Contour plots, from general parts

  • mark_path — the general open polyline geometry (vs the closed mark_polygon).
  • transform_contour — marching-squares iso-lines over any regular x/y/z grid.
  • transform_density_2d — a bivariate KDE grid, so scatter → density_2d → contour → mark_path is the classic density contour.

Cookbook

A new Cookbook section in the book: a recipe index plus one page per chart family (Violin, Distributions, Bars & Error Bars, Line, Area, Strip/Rug, Circular, Heatmaps, Scatter, Contours, Faceting, Geospatial). Every code block is a real compiled example, included with {{#include}}, so the docs cannot drift from the API.

Facet statistics + free scales

Statistics now run per panel, and Free/FreeX/FreeY let each panel train its own scale.

Breaking changes

  • FacetMetrics gained per_column_left and per_row_bottom (Vec). It still derives Default, so struct-update syntax keeps working.
  • The Layer trait gained a required without_source_data method. External Layer implementations must provide it.

Also added

Position (dodge), BandTransform::with_split, DensityTransform::with_groupbys, StackMode::Mirror, alt::x(...).with_category_labels(...), hand-labelled ticks (ExplicitTick::Labeled), and Dataset::column_arc.

Fixed

  • A mirrored/centered area (a mark_area violin) no longer sits flush against the axis — it gets symmetric 5 % padding.
  • The polygon renderer no longer paints a layer with another layer's colour scale (a violin's inner box used to inherit the outline's colours).

nu_plugin_charton 0.8.0

  • -g violin: density outline + inner quartile box; -c dodges one per group.
  • -g contour: iso-lines of a regular x/y/z grid; --z names the value column, --stroke gives a single-colour contour, and omitting --z estimates a 2D density first.
  • Fixed: --help now lists every accepted chart type, and --interpolation no longer advertises a nonexistent monotone option.
28 days ago
charton

v0.7.1

charton and nu_plugin_charton stay on the same version line. This release is a plugin-focused patch: runnable sample data, a clearer install/quick-start, and two display fixes.

charton 0.7.1

No API changes. The version bump keeps the library in lockstep with nu_plugin_charton 0.7.1.

nu_plugin_charton 0.7.1

Added

  • Bundled sample datasets under assets/: data.csv, sales.csv, and world.geojson.

Changed

  • Quick start and Demo examples now read the bundled assets/ files, so they run out of the box from a clone.
  • Simplified the install instructions for end users.

Fixed

  • Inline charts now leave the cursor at the start of the next line, so the shell prompt is no longer indented by the image width. All inline frames end with CRLF and a cursor resync for terminals whose image protocols park the cursor at the image edge.
  • Polar bar charts now distinguish the two shapes charton defines: omitting -x produces a pie, or a donut with --inner-radius; keeping -x maps it to the angle for a rose / Nightingale chart. Previously every polar bar chart was a rose.
29 days ago
charton

v0.7.0

This release ships the charton library and the nu_plugin_charton Nushell plugin together, at the same version:

  • charton 0.7.0** — the plotting library
  • nu_plugin_charton 0.7.0** — the Nushell plugin (first public release)

Both crates now share one version number and one tag, so a release always covers the matching pair.

charton 0.7.0

Added

  • Light/dark theme presets. ThemeMode (Light / Dark) with Theme::light(), Theme::dark(), Theme::with_mode() and Theme::is_dark(). A mode changes colors only — layout, fonts and sizing are untouched, so switching is non-destructive.

Changed

  • Tighter default canvas margins for a larger plot area: top 0.05 → 0.025, right 0.03 → 0.02, bottom 0.08 → 0.03, left 0.06 → 0.025. Charts that relied on the old defaults now render a bigger panel; set margins explicitly to restore the previous look.
  • Reduced the per-axis edge buffer from 10px to 4px (the canvas margins already provide the outer breathing room).

Fixed

  • Legend colors on dark backgrounds. Size-only and shape-only legend symbols now use the theme's legend ink instead of a hard-coded #333333, which was nearly invisible against a dark canvas.

nu_plugin_charton 0.7.0

Turn any Nushell pipeline table into a chart — drawn inline in the terminal, or saved as SVG/PNG. No CSV round-trip: the table goes straight into a charton Dataset.

2026-09-27 14:50:43
charton

v0.6.4

This release focuses on trimming dependencies and fixing issues. The user-facing API is unchanged.

Dependency Reduction

  • PDF now uses a native krilla backend, removing the archived svg2pdf plus the entire usvg / resvg SVG-parsing stack.
  • Arrow integration switched from the full arrow umbrella crate to arrow-array + arrow-schema + arrow-select, no longer pulling in arrow-cast / arrow-string and their regex / lexical trees.
  • Dropped html-escape (replaced by a built-in XML escaper); csscolorparser now runs without named-colors, using a bundled color table instead, which removes the phf proc-macro stack.
  • Removed the unused direct serde dependency; pdf and raster now share a single tiny-skia (no more duplicate 0.11 / 0.12).

New Native PDF Backend

  • Drawing goes straight into the PDF page instead of "build SVG then re-parse it", reusing the same layout and colors as the other backends.
  • Text is embedded as selectable, searchable real text, with font subsetting.
  • Supports fill/stroke, dashes and line caps/joins, rectangles/circles/paths/polygons, linear gradients, clipping, and rotated text.
  • Output is compressed by default and carries a tagged document structure.

Fixes

  • Fixed the arrow feature, which previously did not compile at all: incorrect imports in scope, mismatched fields and enum variants, and temporal units not normalized to nanoseconds. The related tests were rewritten as well.

Compatibility Notes

  • The user API and example usage are unchanged from 0.6.3.
  • charton::arrow is now a lightweight re-export module (array / datatypes / record_batch / compute) rather than exposing the full arrow crate.
  • Enabling the pdf feature requires a minimum Rust version of 1.92 (krilla's requirement).