Skip to content

feat(xlsx): charts drawn as SVG, their values from the cells, in the file's look - #12

Open
mkroemer wants to merge 7 commits into
p10node:mainfrom
mkroemer:xlsx-charts
Open

mkroemer wants to merge 7 commits into
p10node:mainfrom
mkroemer:xlsx-charts

Conversation

@mkroemer

@mkroemer mkroemer commented Nov 14, 2026 •

Copy link
Copy Markdown

Builds on the pictures PR (#11). Until it is merged this PR shows its commit too; the charts part is the commits after it.

This draws a spreadsheet's charts instead of the placeholder box, from the live cells, in the look the file gives them. It is large (about 2,250 lines, 315 of them tests), but nearly all of it is two new files: src/core/xlsx/chart.ts reads a chart part into a model in the worker, and src/core/xlsx/chart-svg.ts draws that model as a self-contained SVG with no DOM, shown over the cells like a picture. The commits split it into the reader and drawing, review fixes, the looks, and the last four kinds, if you'd rather review it in steps; I can also split it into separate PRs along those lines.

  • Kinds: bar and column (clustered, stacked, 100%, upright or horizontal), line (markers, smoothing, gaps for empty cells), area, pie and doughnut (exploded slices, first-slice angle), scatter, bubble (by area or width, bubbleScale, negative bubbles), radar (lines, markers, filled), stock (high-low lines, up-down bars), surface seen from above as Excel's contour chart or as a wireframe, and combinations on primary and secondary axes; 3D charts as their flat form. Only the chartex kinds (waterfall, treemap, ...) keep the placeholder.
  • Values: series references are read from the workbook's cells, loading a sheet not shown yet, so charts follow edits. A defined name, another workbook or an unreadable reference falls back to the chart's cached values. Hidden rows and columns are left out when plotVisOnly is set.
  • Layout: Excel-like automatic axis bounds (zero kept, steps of 1/2/5, a 100% axis ending at 100%); fixed min, max and major unit; reversed and log axes; gridlines; tick and data labels in their number formats, tilted where they don't fit flat; the title (Excel's automatic one included, and a title taken from a cell); the legend (positions, overlay, deleted entries); manual plot layout.
  • Look: series, point, chart area and plot area fills as colors, gradients (linear, radial) or pattern fills (the DrawingML presets); line colors, widths and dashes; markers; the text size, weight and color of the title, axes, legend and data labels; gridline and axis lines or none; the built-in chart style (c:style, c14:style: its palettes, tinted plot areas and dark backgrounds, not bevels and shadows). Where the file gives nothing, the grays are shades of the theme's text color, as Excel derives them.
  • Pie and doughnut size a negative value by its magnitude, as Excel does, and keep the sign in its label. In a scatter chart a point missing its X value is not drawn. Everything plotted is clipped to the plot area, so a fixed axis range can't run over the title and legend. Each chart is its own <img>, so gradient, pattern and clip ids only need to be unique within one picture.

test/xlsx-chart.test.ts covers series read from the cells and the cache fallback, hidden rows, a chart read again after an edit, Excel's axis bounds and the 100% axis, column, stacked bar, line and pie drawings, cell titles, negative pie values, scatter gaps, clipping, each of radar, bubble, stock and surface, gradient and pattern fills, text and line colors, the plot area fill, the theme's grays and the built-in styles. bun run typecheck, bun test and the e2e suite pass in CI.

🤖 Generated with Claude Code

markus-kroemer and others added 7 commits November 14, 2026 07:58
A sheet's drawing part (xl/drawings/drawingN.xml) is read for its
pictures and charts with their anchors: two cells, one cell and a size,
or a position and a size. The new `xlsx.drawings(sheet)` call returns
them; the grid lays them over the cells, placed by the row and column
axes, so a picture moves and grows with resized or hidden rows and
columns, and the sheet extends far enough to reach every drawing.

A picture the browser can draw (PNG, JPEG, GIF, SVG, ...) is shown as
itself, through the client's media URLs. A chart, and a picture in a
format the browser cannot draw (EMF, WMF), is shown as a box of its
size naming what is there: the chart's kind (column, bar, line, pie,
...) and title. Shapes and text boxes are left out. Clicks go through
to the cells below.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A sheet's charts were placeholders. Now src/core/xlsx/chart.ts reads a
chart part into a model in the worker, and src/core/xlsx/chart-svg.ts
draws it as a self-contained SVG (DOM-free), shown over the cells like a
picture.

- Kinds: bar and column (clustered, stacked, 100%, upright or
  horizontal), line (markers, smoothing, gaps for empty cells), area,
  pie and doughnut (exploded slices, first-slice angle), scatter, and
  combinations on primary and secondary axes; 3D charts as their flat
  form. Radar, bubble, stock, surface and chartex keep the placeholder.
- Values: series references are read from the workbook's cells, loading
  a sheet not shown yet, so charts follow edits (the view reads them
  again after an edit result). A defined name, another workbook or an
  unreadable reference falls back to the chart's numCache / strCache.
  Hidden rows and columns are left out when plotVisOnly is set.
- Look: Excel-like automatic axis bounds (zero kept, steps of 1/2/5),
  fixed min/max/major unit, reversed and log axes, gridlines, tick and
  data labels in their number formats, tilted category labels where they
  do not fit flat, title (Excel's automatic one included), legend
  (positions, overlay, deleted entries), theme accents for series
  without colors of their own, manual plot layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pie negatives, cell titles

- scatter: a point whose X value is missing is not drawn (it was placed at
  its ordinal position); value labels follow their own points when a Y
  value is missing
- bars, areas, lines and points are clipped to the plot area, so a fixed
  axis range does not let them run over the title and legend
- pie and doughnut: a negative value is a slice of its magnitude, as in
  Excel; its value label keeps the sign
- a title whose text is a cell (`c:tx/c:strRef`) shows that cell's text,
  else its cached text, before the automatic title

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… row stays in it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The automatic bounds add a step above a maximum that falls on a
gridline, so a 100% stacked axis ran to 120%. An axis carrying only
100% stacks now ends at 100% (and -100% with negatives), as Excel's does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- gradient fills (linear and radial) and pattern fills (DrawingML presets)
  for series, points, the chart area and the plot area
- the text size, weight and color of axis labels, legend and data labels
- gridline and axis lines: color, width, dash, or none
- the plot area's fill and border
- the built-in chart style (c:style, c14:style): gray, accent and
  one-accent palettes, tinted plot areas, dark backgrounds
- default grays (text, gridlines, axis lines) as shades of the theme's
  text color rather than fixed values

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kinds that were placeholders until now:
- radar: lines, lines with markers, filled; a spoke per category, rings
  at the value axis' ticks
- bubble: sizes read from the cells, by area or by width, scaled by
  bubbleScale; negative bubbles as outlines where the chart shows them;
  the axes leave the bubbles room
- stock: high-low lines and up-down bars, the series over them
- surface (2D and 3D): seen from above as Excel's contour chart, its
  value bands in the legend; or the wireframe

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mkroemer mkroemer changed the title feat(xlsx): charts drawn as SVG, their values from the cells feat(xlsx): charts drawn as SVG, their values from the cells, in the file's look Nov 15, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants