v0.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.
- Semantic output: role classes,
data-*identity and accessibility metadata in SVG, plus tagged PDF. mark_pathandmark_polygonare 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.
RenderBackendhas semantic scopes instead of clip scopes.begin_clip_scope/end_clip_scopeare gone;begin_group/end_grouptake acore::semantic::Groupcarrying the role, the identity and, optionally, the clip rectangle.begin_element/end_elementannotate a single observation. Custom backends must be updated.RenderBackendhas one vector-path primitive.draw_polygonandPolygonConfigwere removed;draw_pathnow takessubpaths: Vec<Vec<(Precision, Precision)>>and aPathTopology::{Simple, Convex, Complex}hint.Simpleis open and stroked,Convexis a single closed convex ring,Complexis one or more closed rings filled with the even-odd rule.- The geo aliases are gone.
MarkGeoPath,mark_geoshapeandconfigure_geoshapewere removed.mark_path/configure_pathdraw open, stroked paths, whilemark_polygon/configure_polygondraw closed, filled polygons.mark_type()now returnspathorpolygon, which also changes the SVG class anddata-markvalue of those layers. Geographic maps usemark_polygonwithCoordSystem::Geo: projection belongs to the coordinate system, not to the mark. GradientRectConfig::id_suffixis nowfield. The value never chose the gradient id — the document namespaces ids — it names the data field the ramp visualises.IntoLayeredgained required methods:to_svg_with, and with thepdffeatureto_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-0instead ofplot-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_factoralso defaults to1.0, sowith_size(w, h)produces aw × himage; passwith_scale_factor(2.0)to ask for the previous density. RenderBackend::draw_textnow returnsResult<(), ChartonError>, so backends that resolve fonts themselves (PNG, PDF) can report an unknown family. SVG and WGPU, which defer font selection, returnOk(()).
- 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, …) plusdata-*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 gainsrole="img"orgraphics-document,<title>/<desc>, andaria-labelledby/aria-describedby; decorative groups are hidden in document mode. SvgOptionsandA11y, also available from the prelude, plusIntoLayered::to_svg_withandwith_description().to_svg_withselects 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"plusdata-series="Ford") inside their layer, and elements standing for a single observation — each bar, each tick label — carry that value asdata-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_elementis the backend-neutral way to annotate them. - Multi-ring paths and real holes.
PathConfigcarries 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_datasetnow emits_ring_idin addition to_path_idand no longer drops interior rings. Geographic maps usepath_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/Figurewhose/Altis the title and description, the title becomes a/Caption, and axes and legend become/Pblocks.PdfOptionsselects tagging,pretty()writes an inspectable, uncompressed document, andto_pdf_with/LayeredChart::to_pdf_withexpose 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.mdandexamples/semantic_demo.htmlshow the markup in context.
<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::AxisViewresolves which data scale sits on which physical side, includingcoord_flip, its title, label rotation, explicit ticks and band geometry. The layout engine and the renderers read the same object;render::axis_rendererdraws 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_lengthis 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_scalescales 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_paddingis the whole tick-label to axis-title gap. The hidden 5px constant the renderer added is gone and the default is10px, 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_factoris 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.
- 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
AxisBandgeometry. - 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_ticksvalues 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.
Theme::with_tick_min_spacing, itstick_min_spacingfield, andTheme::suggest_tick_count. A fixed pixel budget cannot know how wide a label is; leave the default for the adaptive fit, or usewith_tick_countto pin a density.MarkGeoPath,mark_geoshapeandconfigure_geoshape;draw_polygonandPolygonConfig. Their replacements are listed under breaking changes.
--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-ticksstill 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/--heightno longer makes the axis denser.--font-scalechanges the count naturally, because bigger text gets fewer labels. - Explicit
--x-ticks/--y-tickslabels are measured into the margin and are no longer clipped. charton geodraws through the same axis pipeline as the other marks, so its grid, labels, tick density, theme colours and label rotation match cartesian charts.charton geoandcharton pathpick up the library fixes above: GeoJSON holes render as real holes, andcharton pathnow applies--colorto the stroke and honours--dashinstead 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.
v0.8.5
Category axes now fit the geometry marks actually draw, and the raster/PDF font pipeline is consistent with SVG.
- Automatic category-axis headroom. Axes built with
with_category_labelsare 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-expandremain 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 returnChartonError::Fontinstead of silently falling back to Inter. - WYSIWYG raster output.
with_size(w, h)now produces aw × himage;scale_factordefaults to1.0and is purely a pixel-density knob. Sharpness is handled internally by supersampling. MarkTextfont controls.with_font_family,with_italic, andwith_font_stylecomplete the text mark.
with_font_scalenow 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.5is now visually proportional to a1.0chart.Theme::label_paddingis now the whole tick-label ↔ axis-title gap. The renderer previously added a hidden5pxconstant; that constant is gone and the defaultlabel_paddingis10px. The default gap is unchanged, but a customwith_label_paddingvalue now describes the whole gap rather than half of it.
- 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_textnow returnsResult<(), ChartonError>.ScaleDomaingained aDiscretePositionsvariant; downstream exhaustive matches must handle it.- Reversing a numeric category axis (
with_reversetogether withwith_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_factoronly changes density, not the configured output size.
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 violinis 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, defaulthalfblock) and no longer sniffs terminal-brand environment variables. - The default raster scale is
1.0, matching the library;--scale 2.0requests a denser asset. charton text --italic.- Ridgeline recipes no longer need
--x-expand. - Waterfall fixes:
assets/waterfall.csvnow matches the example, and--y2is preserved on the--transformroute.
- New Fonts & Text Rendering chapter covering family stacks, fallback, em sizing, text scaling, unknown-family errors, and per-backend behaviour.
Full changelogs
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.
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 (default0.5).RowAccessor::entries()— returns the current row as(column name, value)pairs, so atransform_calculatecallback can serialise the whole row (e.g. to hand it to an external expression evaluator).
- 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_2dalso overlap tiles by the defaultcell_paddingso anti-aliasing cannot leave gaps between neighbours.
- Marks are now subcommands.
charton -g bar …→charton bar …. Each mark declares only the flags it understands, sohelp charton barand tab-completion no longer list unrelated options. The-g/--geomflag 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, andwaterfallsubcommands are removed; use--transform,--layer,--path-group, and floatingbar(--y2). The README and book show each recipe written out. beeswarmis nowcharton point --layout beeswarm, matching Charton (swarm is a point layout, not a mark).path/polygonare available with the--path-groupchannel;barsupports 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. --layeraccepts a bare mark name (--layer pointalongside--layer {geom: point}); layers inherit the primary encoding and lists may mix names and records.
--transform {op: calculate, as: <col>, expr: {|row| …}}runs a Nushell closure per row, matchingtransform_calculate. The row is a record, so it can read columns produced by earlier transforms — this is how the waterfall recipe computesbase.--font-scale(and thefont_scaleconfig key) scales every chart font. Default is now 1.5 (terminal charts),1.0for original sizes, up to2.0.--x-expand/--y-expandaccept[low, high]for asymmetric additive padding in axis units, and now apply to the--transform/path/polygonroute (gives ridgelines headroom).
- 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-boxlabel the x axis withcenter/category. - A
--layerstylerecord accepts every styling field (mark-width,cell-padding,cap-length,outlier-size,loess,loess-bandwidth, booleancenter/outliers); keys accept kebab- or snake-case, and unknown keys or wrong types now error instead of silently no-op.
v0.8.3
- 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 bumpkeep 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 candlestickdistinguishes up days from down days.assets/ohlc.csvgained adirection(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.
dumbbell,lollipop, andrangerecipes add-flipto 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.
- New sample tables:
dumbbell.csv,lollipop.csv,range.csv,slope.csv,bump.csv,waterfall.csv;ohlc.csvgained adirectioncolumn.
- Version bump only; no changes since 0.8.2.
v0.8.2
v0.8.2
v0.8.1
charton 0.8.1 — convenience marks, floating bars, reversed axes, and a cleaner position model. nu_plugin_charton 0.8.1 ships in lockstep.
- 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.
- 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.
- 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.
- 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.
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).
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).
- 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.
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.
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).
- -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.
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.
No API changes. The version bump keeps the library in lockstep with nu_plugin_charton 0.7.1.
- Bundled sample datasets under assets/: data.csv, sales.csv, and world.geojson.
- 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.
- 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.
v0.7.0
This release ships the charton library and the nu_plugin_charton Nushell plugin together, at the same version:
charton0.7.0** — the plotting librarynu_plugin_charton0.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.
- Light/dark theme presets.
ThemeMode(Light/Dark) withTheme::light(),Theme::dark(),Theme::with_mode()andTheme::is_dark(). A mode changes colors only — layout, fonts and sizing are untouched, so switching is non-destructive.
- Tighter default canvas margins for a larger plot area:
top0.05 → 0.025,right0.03 → 0.02,bottom0.08 → 0.03,left0.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).
- 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.
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.
v0.6.4
This release focuses on trimming dependencies and fixing issues. The user-facing API is unchanged.
- 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).
- 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.
- 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.
- 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).