Skip to content

Bump kuva from 0.4.0 to 0.5.0 - #342

Merged
JSorngard merged 1 commit into
mainfrom
dependabot/cargo/kuva-0.5.0
Aug 15, 2026
Merged

JSorngard merged 1 commit into
mainfrom
dependabot/cargo/kuva-0.5.0

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Aug 10, 2026

Copy link
Copy Markdown
Contributor

Bumps kuva from 0.4.0 to 0.5.0.

Release notes

Sourced from kuva's releases.

kuva v0.5.0

[0.5.0] — 2026-08-07

Added

  • PDF backend migrated from svg2pdf to krilla/krilla-svg, plus multi-page output — svg2pdf was archived upstream by its own maintainer as unmaintained, recommending krilla/krilla-svg (same author, used by Typst) as the successor. PdfBackend::render_scenes(&[Scene]) and the one-shot render_to_pdf_multi(pages) render one kuva canvas per PDF page into a single document (like R's pdf() device, e.g. for fgbio/Picard-style reports); nothing is rasterized. By default each page takes its scene's natural size; PdfBackend::with_page_size(PageSize::inches(11.0, 8.5)) instead coerces every page to a fixed size, scaling each scene proportionally to fit and centering it, with the scene's own background color filling the letterbox margin. The pdf feature now requires Rust >= 1.92 (krilla's MSRV) — higher than kuva's own crate-level rust-version (kept at 1.87 deliberately; see README.md's note on why), tracked separately as [package.metadata.msrv] pdf_feature in Cargo.toml. CI's msrv job now builds cli,png,embed_font (not cli,full) against rust-version, plus a new pdf-msrv job that builds cli,full against pdf_feature specifically — the pre-existing msrv job would otherwise have silently asserted a false claim (that full builds at 1.87) forever, since nothing else exercises that combination at the older toolchain. PdfBackend is no longer a zero-sized unit struct usable as a bare value (PdfBackend.render_scene(...)) — use PdfBackend::new().
  • kuva twin-y CLI subcommand — dual-axis plot from the command line: two series sharing an x-axis but with independent primary (left) and secondary (right) y-scales, e.g. temperature vs. rainfall. Supports line and scatter as the plot type on either axis (--primary-type/--secondary-type); per-side color and legend label flags; new shared --y2-label/--y2-min/--y2-max/--log-y2/--y2-tick-format flags. Closes #106.
  • Layout::with_y2_axis_min/with_y2_axis_max — unconditional secondary-Y-axis bound overrides, mirroring with_y_axis_min/with_y_axis_max. Found needed while building the twin-y CLI: the existing with_y2_range still gets nice-rounded and capped near the secondary plots' own data (the same path the auto-computed range takes), so a requested bound far from the data was largely ignored.
  • CLI date/time X axis (--x-date-format, --x-date-unit, --x-date-tick-format, --x-date-tick-step) on scatter and line — parses the X column as a date/time using a strftime-style format instead of a plain number, then ticks it with a DateTimeAxis (auto-selected unit/format by default, or an explicit unit with a sensible default tick format, overridable). Closes #107.
  • Layout::with_label_background — a semi-opaque background rect behind in-fill value labels (Treemap, Sunburst, Mosaic, Funnel, Gantt), for readability over busy fills or BW-mode hatch patterns. Off by default in color mode; on automatically in BW mode; overridable either way. CLI: --label-background. Closes #102.
  • Layout::with_subtitle — render a secondary line centred under the title for a one-line data summary (e.g. n = 1,234 cells). Sized at round(0.7 × title_size) by default or set explicitly with with_subtitle_size; coloured by muting the title colour toward the background so it adapts to light and dark themes rather than a fixed grey; word-wrapped independently of the title via with_subtitle_wrap. The title block reserves the extra height automatically so the plot is pushed down rather than overlapped. CLI: --subtitle and --subtitle-wrap on every subcommand. See Reference → Layout.
  • ParetoPlot — bar chart of category values, sorted descending by default, with a superimposed cumulative-percentage line on a secondary axis (fixed 0-100%, the "80/20 rule" chart). Optional dashed threshold reference line (default 80%, labeled with its percentage) and per-point cumulative-percentage labels. Legend shown by default ("Value" / "Cumulative %"). Secondary-axis ticks are formatted as percentages (0%, 20%, …). Categorical axis defaults to rotated (-45°), collision-thinned labels. .with_max_categories(n) collapses a long tail of small categories into one stacked "Other" bar, decoded via per-segment legend entries, instead of cluttering the axis. .with_horizontal(bool) puts categories on Y and values on X. CLI: kuva pareto.
  • Secondary X-axis (Layout::with_x2_range/with_x2_label/with_log_x2/with_x2_tick_format, ComputedLayout::map_x2) — a top-drawn counterpart to the existing secondary Y-axis (right side), for plots whose secondary encoding pairs with the value axis rather than the category axis (used by horizontal ParetoPlot). Third-party plot types can use it directly via the same Layout/ComputedLayout fields.
  • CLI --header flag — forces the first row to be treated as a header even when it looks like data, the explicit counterpart to the existing --no-header. The two are mutually exclusive. Useful for inputs whose column names are all-numeric (e.g. years). Ignored, with a warning, for parquet input (self-describing). Part of #111.

Fixed

  • CLI header auto-detection missed a header masked by a leading numeric column (#111) — the detector only checked whether the first field of the first row was non-numeric, so an input like 5,data over 0,1 / 1,2 treated the header row as data and failed with cannot parse 'data' as a number. Detection now also flags a header when any column holds a non-numeric label atop an otherwise all-numeric column. The rule is a strict superset of the old first-cell check, so existing detections are unchanged; genuinely ambiguous inputs are covered by the new --header / existing --no-header overrides.
  • examples/all_plots_simple.rs/all_plots_complex.rs (the "every plot type in one figure" gallery assets) were missing ParetoPlot, BandPlot, and LegendPlot — Replaced some plot repeats to include missing plot types.
  • man/kuva.1 was missing the twin-y subcommand — regenerated (kuva man > man/kuva.1).
  • Scatter3D/Surface3D instances combined in one panel now share one 3D coordinate box — each instance previously called data_ranges()/drew its own wireframe box independently, so two Scatter3D (or a mix with Surface3D) in the same render_multiple call each normalized to their own min/max and could project completely different data onto identical screen coordinates, with the box itself drawn twice. render_multiple now computes one merged DataRanges3D and draws the box once, shared by every 3D instance in the call.
  • Twin-Y x-axis no longer clips a secondary series that extends further than the primary series — Layout::auto_from_twin_y_plots's with_y2_auto unioned the padded x-range across primary and secondary, but left data_x_range (the raw extent used by the axis-range capping added for #98) pinned to primary's range alone. When the capped branch triggered, the x-axis max was computed from primary's raw max instead of the true combined max, rounding the axis short and clipping secondary's data past that point. data_x_range is now unioned across both series.

Security

  • Data-derived text (group names, legend/category labels) is now XML-escaped before being written into SVG attribute values (e.g. data-group="...", data-x="...") in interactive-mode output. Previously these were interpolated raw, so a crafted data file's label could break out of the attribute and inject arbitrary markup (a stored-XSS-style issue) into the rendered SVG. Fixed at every extra_attrs call site in src/render/render.rs via a new render_utils::escape_attr.
  • Arbitrary/unrecognized color strings (Color::Css, e.g. an unrecognized --color-by value) are now XML-escaped when written into fill/stroke attributes, and stroke-dasharray, root font-family/fill, and the background-rect fill in the SVG backend are now escaped as well, closing the same class of attribute-breakout issue for CSS-derived values.
  • --terminal output now filters control characters (ESC, C0/C1, DEL) out of data-derived labels before they reach the character grid, replacing them with U+FFFD. Previously a label containing a raw escape sequence (e.g. from an untrusted data file) could be replayed into the operator's real terminal when the rendered grid was printed, potentially triggering an ANSI/OSC-based terminal escape injection.

Reported via private disclosure (GHSA-3c48-9r95-hqhr). See the advisory for full details once published.

What's Changed

New Contributors

Full Changelog: Psy-Fer/kuva@v0.4.0...v0.5.0

kuva v0.4.1

Full Changelog: Psy-Fer/kuva@v0.4.0...v0.4.1

Changelog

Sourced from kuva's changelog.

[0.5.0] — 2026-08-07

Added

  • PDF backend migrated from svg2pdf to krilla/krilla-svg, plus multi-page output — svg2pdf was archived upstream by its own maintainer as unmaintained, recommending krilla/krilla-svg (same author, used by Typst) as the successor. PdfBackend::render_scenes(&[Scene]) and the one-shot render_to_pdf_multi(pages) render one kuva canvas per PDF page into a single document (like R's pdf() device, e.g. for fgbio/Picard-style reports); nothing is rasterized. By default each page takes its scene's natural size; PdfBackend::with_page_size(PageSize::inches(11.0, 8.5)) instead coerces every page to a fixed size, scaling each scene proportionally to fit and centering it, with the scene's own background color filling the letterbox margin. The pdf feature now requires Rust >= 1.92 (krilla's MSRV) — higher than kuva's own crate-level rust-version (kept at 1.87 deliberately; see README.md's note on why), tracked separately as [package.metadata.msrv] pdf_feature in Cargo.toml. CI's msrv job now builds cli,png,embed_font (not cli,full) against rust-version, plus a new pdf-msrv job that builds cli,full against pdf_feature specifically — the pre-existing msrv job would otherwise have silently asserted a false claim (that full builds at 1.87) forever, since nothing else exercises that combination at the older toolchain. PdfBackend is no longer a zero-sized unit struct usable as a bare value (PdfBackend.render_scene(...)) — use PdfBackend::new().
  • kuva twin-y CLI subcommand — dual-axis plot from the command line: two series sharing an x-axis but with independent primary (left) and secondary (right) y-scales, e.g. temperature vs. rainfall. Supports line and scatter as the plot type on either axis (--primary-type/--secondary-type); per-side color and legend label flags; new shared --y2-label/--y2-min/--y2-max/--log-y2/--y2-tick-format flags. Closes #106.
  • Layout::with_y2_axis_min/with_y2_axis_max — unconditional secondary-Y-axis bound overrides, mirroring with_y_axis_min/with_y_axis_max. Found needed while building the twin-y CLI: the existing with_y2_range still gets nice-rounded and capped near the secondary plots' own data (the same path the auto-computed range takes), so a requested bound far from the data was largely ignored.
  • CLI date/time X axis (--x-date-format, --x-date-unit, --x-date-tick-format, --x-date-tick-step) on scatter and line — parses the X column as a date/time using a strftime-style format instead of a plain number, then ticks it with a DateTimeAxis (auto-selected unit/format by default, or an explicit unit with a sensible default tick format, overridable). Closes #107.
  • Layout::with_label_background — a semi-opaque background rect behind in-fill value labels (Treemap, Sunburst, Mosaic, Funnel, Gantt), for readability over busy fills or BW-mode hatch patterns. Off by default in color mode; on automatically in BW mode; overridable either way. CLI: --label-background. Closes #102.
  • Layout::with_subtitle — render a secondary line centred under the title for a one-line data summary (e.g. n = 1,234 cells). Sized at round(0.7 × title_size) by default or set explicitly with with_subtitle_size; coloured by muting the title colour toward the background so it adapts to light and dark themes rather than a fixed grey; word-wrapped independently of the title via with_subtitle_wrap. The title block reserves the extra height automatically so the plot is pushed down rather than overlapped. CLI: --subtitle and --subtitle-wrap on every subcommand. See Reference → Layout.
  • ParetoPlot — bar chart of category values, sorted descending by default, with a superimposed cumulative-percentage line on a secondary axis (fixed 0-100%, the "80/20 rule" chart). Optional dashed threshold reference line (default 80%, labeled with its percentage) and per-point cumulative-percentage labels. Legend shown by default ("Value" / "Cumulative %"). Secondary-axis ticks are formatted as percentages (0%, 20%, …). Categorical axis defaults to rotated (-45°), collision-thinned labels. .with_max_categories(n) collapses a long tail of small categories into one stacked "Other" bar, decoded via per-segment legend entries, instead of cluttering the axis. .with_horizontal(bool) puts categories on Y and values on X. CLI: kuva pareto.
  • Secondary X-axis (Layout::with_x2_range/with_x2_label/with_log_x2/with_x2_tick_format, ComputedLayout::map_x2) — a top-drawn counterpart to the existing secondary Y-axis (right side), for plots whose secondary encoding pairs with the value axis rather than the category axis (used by horizontal ParetoPlot). Third-party plot types can use it directly via the same Layout/ComputedLayout fields.
  • CLI --header flag — forces the first row to be treated as a header even when it looks like data, the explicit counterpart to the existing --no-header. The two are mutually exclusive. Useful for inputs whose column names are all-numeric (e.g. years). Ignored, with a warning, for parquet input (self-describing). Part of #111.

Fixed

  • CLI header auto-detection missed a header masked by a leading numeric column (#111) — the detector only checked whether the first field of the first row was non-numeric, so an input like 5,data over 0,1 / 1,2 treated the header row as data and failed with cannot parse 'data' as a number. Detection now also flags a header when any column holds a non-numeric label atop an otherwise all-numeric column. The rule is a strict superset of the old first-cell check, so existing detections are unchanged; genuinely ambiguous inputs are covered by the new --header / existing --no-header overrides.
  • examples/all_plots_simple.rs/all_plots_complex.rs (the "every plot type in one figure" gallery assets) were missing ParetoPlot, BandPlot, and LegendPlot — Replaced some plot repeats to include missing plot types.
  • man/kuva.1 was missing the twin-y subcommand — regenerated (kuva man > man/kuva.1).
  • Scatter3D/Surface3D instances combined in one panel now share one 3D coordinate box — each instance previously called data_ranges()/drew its own wireframe box independently, so two Scatter3D (or a mix with Surface3D) in the same render_multiple call each normalized to their own min/max and could project completely different data onto identical screen coordinates, with the box itself drawn twice. render_multiple now computes one merged DataRanges3D and draws the box once, shared by every 3D instance in the call.
  • Twin-Y x-axis no longer clips a secondary series that extends further than the primary series — Layout::auto_from_twin_y_plots's with_y2_auto unioned the padded x-range across primary and secondary, but left data_x_range (the raw extent used by the axis-range capping added for #98) pinned to primary's range alone. When the capped branch triggered, the x-axis max was computed from primary's raw max instead of the true combined max, rounding the axis short and clipping secondary's data past that point. data_x_range is now unioned across both series.

Security

  • Data-derived text (group names, legend/category labels) is now XML-escaped before being written into SVG attribute values (e.g. data-group="...", data-x="...") in interactive-mode output. Previously these were interpolated raw, so a crafted data file's label could break out of the attribute and inject arbitrary markup (a stored-XSS-style issue) into the rendered SVG. Fixed at every extra_attrs call site in src/render/render.rs via a new render_utils::escape_attr.
  • Arbitrary/unrecognized color strings (Color::Css, e.g. an unrecognized --color-by value) are now XML-escaped when written into fill/stroke attributes, and stroke-dasharray, root font-family/fill, and the background-rect fill in the SVG backend are now escaped as well, closing the same class of attribute-breakout issue for CSS-derived values.
  • --terminal output now filters control characters (ESC, C0/C1, DEL) out of data-derived labels before they reach the character grid, replacing them with U+FFFD. Previously a label containing a raw escape sequence (e.g. from an untrusted data file) could be replayed into the operator's real terminal when the rendered grid was printed, potentially triggering an ANSI/OSC-based terminal escape injection.

Reported via private disclosure (GHSA-3c48-9r95-hqhr). See the advisory for full details once published.

Commits
  • 497f135 v0.5.0 release (#116)
  • e61adce Merge branch 'main' into dev
  • f20898b Release v0.5.0
  • b464c00 Add doc comments for pub defs and pub scripts in rendere.rs
  • 2b53ceb Escape data-derived text in SVG/terminal output (GHSA-3c48-9r95-hqhr)
  • 73c07ad Reduce emit-code test disk cost with feature-split checks. (#114)
  • 9d007a0 Tidy emit-code test fixtures: drop vestigial cases(), dedupe helper
  • 8f8cd53 Reject unsupported output extensions (#115)
  • 83446af Merge branch 'dev' into fix/output-format-validation
  • 99962be --header and --no-header
  • Additional commits viewable in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [kuva](https://github.com/Psy-Fer/kuva) from 0.4.0 to 0.5.0.
- [Release notes](https://github.com/Psy-Fer/kuva/releases)
- [Changelog](https://github.com/Psy-Fer/kuva/blob/main/CHANGELOG.md)
- [Commits](Psy-Fer/kuva@v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: kuva
  dependency-version: 0.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file rust Pull requests that update rust code labels Aug 10, 2026
@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (e155ce9) to head (e49cfff).

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #342   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            8         8           
  Lines         1506      1506           
=========================================
  Hits          1506      1506           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@JSorngard

Copy link
Copy Markdown
Owner

Verified that the example looks the same.

@JSorngard
JSorngard merged commit 2e7bd4e into main Aug 15, 2026
27 checks passed
@JSorngard
JSorngard deleted the dependabot/cargo/kuva-0.5.0 branch August 15, 2026 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file rust Pull requests that update rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant