Charts

Witan supports embedded Excel charts as ordinary OOXML chart parts. The compatibility goal is that supported charts authored in Witan and supported charts authored in Excel are interchangeable and practically indistinguishable: Excel opens Witan-authored charts as normal workbook charts, and Witan can inspect, edit, preserve, and render Excel-authored charts through the same model.

Use the chart APIs when a script needs to inspect or author charts, and use rendering when a script or CI check needs to verify the visible result.

Chart support is best understood in three layers:

Layer What it covers
Supported chart families The chart groups Witan models directly: column, bar, line, area, pie, doughnut, scatter, bubble, radar, surface, stock, waterfall, histogram, Pareto, funnel, box and whisker, and Excel-compatible combo charts
Public chart model A stable chart spec for chart names, positions, groups, series, axes, titles, legends, labels, markers, styles, and formatting
OOXML interchange and verification Real Excel chart package parts, Excel-compatible serialization, preservation of additional chart XML during updates, and visual comparison against Excel-rendered baselines

The public chart spec is not a raw OOXML DOM. It is the stable authoring model for supported chart families. Underneath that model, Witan reads and writes the workbook's actual chart parts rather than converting charts into images, custom metadata, or Witan-only objects.

Supported Chart Families

Witan models and renders the embedded workbook chart families in the support matrix below. “Full support” means the public chart model can author, inspect, preserve, and render the supported Excel family variants. “2-D support” means the common 2-D family is supported, but some 3-D or decorative Excel subtypes are not authorable through the chart model.

Full Support

Family OOXML Status
Scatter / XY Classic c:scatterChart Marker, smooth, smooth without markers, lines, and lines without markers
Radar Classic c:radarChart Standard radar, radar with markers, and filled radar
Stock Classic c:stockChart, plus c:barChart for volume variants HLC, OHLC, volume-HLC, and volume-OHLC
Waterfall ChartEx cx:chartSpace, layoutId=waterfall Single-series Excel waterfall charts with total columns and connector lines
Histogram ChartEx cx:chartSpace statistical chart Numeric and category-bin histograms, including automatic, count, width, underflow, and overflow bin options
Pareto ChartEx cx:chartSpace statistical chart Histogram columns, cumulative percentage line, secondary percentage axis, and histogram-equivalent bin options
Funnel ChartEx cx:chartSpace, layoutId=funnel Single-series Excel funnel charts with category labels, value labels, legends, and ChartEx axis options supported by Excel
Box and whisker ChartEx cx:chartSpace, layoutId=boxWhisker Excel box-and-whisker charts with exclusive/inclusive quartiles, mean markers/lines, inner points, outlier points, and ChartEx axis options supported by Excel
Combo Classic mixed chart groups Supported classic chart groups in one plot area, including primary and secondary axes and stock-volume combinations

2-D Support

Family OOXML Supported Not supported
Column Classic c:barChart with barDir=col Clustered, stacked, and 100% stacked column charts Cylinder, cone, pyramid, and 3-D column variants
Bar Classic c:barChart with barDir=bar Clustered, stacked, and 100% stacked bar charts Cylinder, cone, pyramid, and 3-D bar variants
Line Classic c:lineChart 2-D line variants, including stacked, 100% stacked, markers, and smooth lines 3-D line authoring; loaded 3-D line charts are rendered as 2-D line charts
Area Classic c:areaChart 2-D area, stacked area, and 100% stacked area charts 3-D area authoring; loaded 3-D area charts are rendered as 2-D area charts
Pie Classic c:pieChart Standard pie charts Exploded pie, pie-of-pie, bar-of-pie, and 3-D pie authoring
Doughnut Classic c:doughnutChart Standard doughnut charts Exploded doughnut
Bubble Classic c:bubbleChart 2-D bubble charts 3-D bubble-effect authoring
Surface Classic c:surfaceChart Top-view filled contour and top-view wireframe contour charts 3-D surface and 3-D wireframe surface variants

Not Supported

Family OOXML
Treemap ChartEx
Sunburst ChartEx
Map / RegionMap ChartEx

Representative Renders

The examples below are rendered by Witan from chart fixtures and cropped to the chart object anchor, so the images show the chart bounds rather than surrounding worksheet rows or columns.

Family Witan render Example coverage
Column Stacked column chart rendered by Witan Stacked columns, multiple series, legend, gridlines, and value axis
Bar 100 percent stacked bar chart rendered by Witan 100% stacked horizontal bars, category axis, value axis, and legend
Line Smooth line chart with markers rendered by Witan Smooth line series, markers, dashed styling, gridlines, and legend
Area Area chart with styled drop lines rendered by Witan Area fills, multiple series, styled drop lines, gridlines, and legend
Pie Pie chart with gradient slice formatting rendered by Witan Slice geometry, theme colors, custom slice fill, and legend
Doughnut Multi-ring doughnut chart rendered by Witan Multi-series rings, hole sizing, slice colors, and legend
Scatter / XY Scatter chart with power trendline rendered by Witan XY plotting, markers, axes, gridlines, legend, and trendline rendering
Bubble Bubble chart with variable marker sizes rendered by Witan X/Y values, bubble-size values, variable marker areas, labels, axes, and legend
Radar Filled radar chart rendered by Witan Filled radar polygons, series order, radial gridlines, labels, and legend
Surface Top-view contour surface chart rendered by Witan Top-view contour bands, value-band legend, axes, and gridline styling
Stock Volume OHLC stock chart with multi-level category labels rendered by Witan Volume columns, OHLC price marks, secondary axis, multi-level categories, and legend
Waterfall Waterfall chart with total columns rendered by Witan Positive/negative steps, total columns, connectors, axes, and gridlines
Histogram Fixed-bin histogram rendered by Witan Fixed-width bins, bin labels, count axis, gridlines, and ChartEx rendering
Pareto Pareto chart with cumulative percentage line rendered by Witan Sorted columns, cumulative percentage line, secondary axis, and category bins
Funnel Funnel chart with bottom legend rendered by Witan Funnel segment geometry, category/value data, labels, and legend placement
Box and whisker Box and whisker chart with mean line rendered by Witan Quartiles, whiskers, outliers, mean markers, mean line, axes, and legend
Combo Column and line combo chart with secondary axis rendered by Witan Mixed column and line groups, primary and secondary value axes, gridlines, and legend

Public Chart Model

Use listCharts to discover charts without reading full chart specs:

witan xlsx exec dashboard.xlsx --expr 'await xlsx.listCharts(wb)'

Each summary includes the chart sheet, name, type, group count, series count, per-group type and axis, and the two-cell position anchor.

Use getChart when a script needs the canonical spec:

witan xlsx exec dashboard.xlsx --expr 'await xlsx.getChart(wb, "Summary", "Revenue")'

A chart spec contains:

  • position: two cell anchors with optional point offsets.
  • groups: one or more chart groups. Combo charts have multiple groups.
  • series: references for names, categories, values, X values, Y values, and bubble sizes.
  • totalIndexes: zero-based total-column indexes for waterfall series.
  • binOptions: histogram and Pareto binning mode and bin settings.
  • quartileCalculation, showInnerPoints, showMeanLine, showMeanMarker, and showOutlierPoints: box-and-whisker statistical options.
  • categoriesRefType: category reference kind, including "multiLevelString" for Excel multi-level category labels.
  • radarStyle: radar subtype for radar groups: "standard", "marker", or "filled".
  • surfaceVariant: surface subtype for surface groups: "topView" or "topViewWireframe".
  • stockRole: semantic role for stock chart series: volume, open, high, low, or close.
  • axes: category, value, secondary category, and secondary value axes.
  • title and legend: visible text, references, positions, and overlay behavior.
  • dataLabels, marker, styleId, displayBlanksAs, and common chart formatting.

The same shape is used for authoring:

witan xlsx exec dashboard.xlsx --save --stdin <<'WITAN'
await xlsx.addChart(wb, "Summary", {
  name: "Revenue",
  position: { from: { cell: "F2" }, to: { cell: "N18" } },
  groups: [
    {
      type: "column",
      grouping: "standard",
      series: [
        {
          name: { ref: "Summary!B1" },
          categories: "Summary!A2:A9",
          values: "Summary!B2:B9",
          dataLabels: { showValue: true, position: "insideEnd" }
        }
      ]
    }
  ],
  title: { text: "Revenue" },
  legend: { position: "right" },
  axes: {
    category: { title: { text: "Quarter" } },
    value: { title: { text: "Revenue" }, numberFormat: "$#,##0" }
  }
})

await xlsx.previewStyles(wb, "Summary!F2:N18")
WITAN

2-D Cartesian and Combo Charts

Column, bar, line, and area charts share the same category/value series shape. Use grouping for stacked and 100% stacked variants:

await xlsx.addChart(wb, "Summary", {
  name: "Sales and Target",
  position: { from: { cell: "F2" }, to: { cell: "N18" } },
  groups: [
    {
      type: "column",
      grouping: "standard",
      gapWidth: 150,
      series: [
        {
          name: { ref: "Summary!B1" },
          categories: "Summary!A2:A9",
          values: "Summary!B2:B9"
        }
      ]
    },
    {
      type: "line",
      axis: "secondary",
      smooth: true,
      series: [
        {
          name: { ref: "Summary!C1" },
          categories: "Summary!A2:A9",
          values: "Summary!C2:C9",
          marker: { style: "circle", size: 6 }
        }
      ]
    }
  ],
  axes: {
    category: { title: { text: "Quarter" } },
    value: { title: { text: "Sales" }, numberFormat: "$#,##0" },
    secondaryValue: { title: { text: "Target" }, position: "right" }
  }
})

Combo charts are represented as multiple chart groups in one groups array. Each group can bind to the primary or secondary axis with axis: "primary" or axis: "secondary". Witan creates the secondary value axis automatically when a secondary group is present, and you can provide axes.secondaryValue to control its title, number format, bounds, units, gridlines, and position.

For column and bar groups, gapWidth controls spacing between categories and overlap controls spacing between series in the same category. Bar charts are horizontal and use the same public category and value axis names as column charts, even though Excel draws the category axis vertically.

Scatter and Bubble Charts

Scatter and bubble charts use numeric X/Y data rather than category/value data. Use xValues and yValues; for bubble charts, also provide bubbleSizes:

await xlsx.addChart(wb, "Samples", {
  name: "Yield vs Cost",
  position: { from: { cell: "H2" }, to: { cell: "P18" } },
  groups: [
    {
      type: "bubble",
      bubbleScale: 80,
      sizeRepresents: "area",
      series: [
        {
          name: { ref: "Samples!D1" },
          xValues: "Samples!A2:A25",
          yValues: "Samples!B2:B25",
          bubbleSizes: "Samples!C2:C25"
        }
      ]
    }
  ],
  axes: {
    category: { title: { text: "Cost" }, numberFormat: "$#,##0" },
    value: { title: { text: "Yield" }, numberFormat: "0.0%" }
  }
})

For scatter charts, set scatterStyle to "marker", "line", "lineMarker", "smooth", or "smoothMarker". For bubble charts, bubbleScale controls relative bubble size, sizeRepresents can be "area" or "width", and showNegativeBubbles controls whether negative bubble sizes are drawn.

Pie and Doughnut Charts

Pie and doughnut charts use one category/value series and do not use axes:

await xlsx.addChart(wb, "Summary", {
  name: "Revenue Mix",
  position: { from: { cell: "F2" }, to: { cell: "N18" } },
  groups: [
    {
      type: "doughnut",
      holeSize: 55,
      firstSliceAngle: 270,
      varyColors: true,
      series: [
        {
          name: { ref: "Summary!B1" },
          categories: "Summary!A2:A7",
          values: "Summary!B2:B7",
          dataLabels: { showCategory: true, showPercent: true, position: "bestFit" }
        }
      ]
    }
  ],
  legend: { position: "right" }
})

Use type: "pie" for a standard pie chart and type: "doughnut" for a doughnut chart. firstSliceAngle rotates the chart, holeSize applies to doughnut charts, and varyColors gives each slice a different theme color. Percent labels are calculated by Excel and rendered from the chart data.

Radar Charts

Radar charts use one chart group with type: "radar". Set radarStyle to choose the Excel radar variant:

  • "standard": line radar without visible point markers.
  • "marker": line radar with visible point markers.
  • "filled": filled radar polygons.
await xlsx.addChart(wb, "Summary", {
  name: "Capability Radar",
  position: { from: { cell: "F2" }, to: { cell: "N18" } },
  groups: [
    {
      type: "radar",
      radarStyle: "marker",
      series: [
        {
          name: { ref: "Summary!B1" },
          categories: "Summary!A2:A7",
          values: "Summary!B2:B7"
        },
        {
          name: { ref: "Summary!C1" },
          categories: "Summary!A2:A7",
          values: "Summary!C2:C7"
        }
      ]
    }
  ],
  title: { text: "Capability Radar" },
  legend: { position: "top" },
  axes: {
    category: { visible: true },
    value: { min: 0, max: 100, majorUnit: 20, majorGridlines: true }
  },
  styleId: 317
})

Excel stores both standard radar and radar-with-markers as c:radarChart with radarStyle="marker"; Witan exposes them as the public radarStyle values above and handles the marker defaults needed for Excel-compatible serialization. Filled radar uses radarStyle: "filled" and is rendered in Excel series z-order, with later filled series painted over earlier ones.

Surface Charts

Surface charts use classic c:surfaceChart parts for Excel's two top-view 2-D contour variants. Set surfaceVariant to choose the variant:

  • "topView": filled contour surface chart.
  • "topViewWireframe": wireframe contour surface chart.

Surface series use value ranges only. Do not provide categories, markers, data labels, secondary axes, or combo groups for surface charts.

await xlsx.addChart(wb, "Summary", {
  name: "Response Surface",
  position: { from: { cell: "F2" }, to: { cell: "N18" } },
  groups: [
    {
      type: "surface",
      surfaceVariant: "topViewWireframe",
      series: [
        { values: "Summary!B2:G2" },
        { values: "Summary!B3:G3" },
        { values: "Summary!B4:G4" },
        { values: "Summary!B5:G5" },
        { values: "Summary!B6:G6" },
        { values: "Summary!B7:G7" }
      ]
    }
  ],
  title: { text: "Response Surface" },
  legend: { position: "right" },
  axes: {
    value: { min: 0, max: 100, majorUnit: 20, numberFormat: "0" }
  },
  styleId: 2
})

The surface legend represents value bands rather than series names. Wireframe legends use outline-only band swatches, matching Excel's contour legend.

Waterfall Charts

Waterfall charts use Excel's modern ChartEx chart parts. They are authorable, inspectable, renderable, and saved as ordinary Excel waterfall charts.

Waterfall authoring supports one waterfall group with one series. Mark subtotal or total bars with zero-based totalIndexes:

await xlsx.addChart(wb, "Summary", {
  name: "Profit Bridge",
  position: { from: { cell: "F2" }, to: { cell: "N18" } },
  groups: [
    {
      type: "waterfall",
      series: [
        {
          name: { ref: "Summary!B1" },
          categories: "Summary!A2:A9",
          values: "Summary!B2:B9",
          totalIndexes: [3, 7]
        }
      ]
    }
  ],
  title: { text: "Profit Bridge" },
  legend: { position: "topRight" },
  axes: {
    category: {
      title: { text: "Step" },
      numberFormat: "@",
      numberFormatLinked: false
    },
    value: {
      title: { text: "USD" },
      min: 0,
      majorUnit: 25000,
      numberFormat: "$#,##0",
      numberFormatLinked: false,
      majorGridlines: true
    }
  },
  styleId: 395
})

Waterfall axes support the same features accepted by Excel:

  • Category axis: literal text title, visibility, number format/linking, major and minor gridlines.
  • Value axis: literal text title, visibility, min/max bounds, major/minor units, number format/linking, major and minor gridlines.

Histogram and Pareto Charts

Histogram and Pareto charts use Excel's modern ChartEx chart parts. They are authorable, inspectable, renderable, and saved as ordinary Excel statistical charts.

Use one chart group with type: "histogram" or type: "pareto", and one series. Numeric histograms use values; category-bin histograms use both categories and values.

await xlsx.addChart(wb, "Summary", {
  name: "Age Distribution",
  position: { from: { cell: "F2" }, to: { cell: "N18" } },
  groups: [
    {
      type: "histogram",
      series: [
        {
          name: { text: "Ages" },
          values: "Summary!B2:B101",
          binOptions: {
            type: "binWidth",
            width: 5,
            allowUnderflow: true,
            underflowValue: 18,
            allowOverflow: true,
            overflowValue: 80
          }
        }
      ]
    }
  ],
  title: { text: "Age Distribution" },
  axes: {
    category: { title: { text: "Age bin" } },
    value: { title: { text: "Count" }, majorGridlines: true }
  },
  styleId: 366
})

Pareto charts use the same public series shape. Witan writes the histogram columns plus Excel's Pareto cumulative line and secondary percentage axis:

await xlsx.addChart(wb, "Summary", {
  name: "Issue Pareto",
  position: { from: { cell: "F20" }, to: { cell: "N36" } },
  groups: [
    {
      type: "pareto",
      series: [
        {
          name: { text: "Issues" },
          categories: "Summary!A2:A20",
          values: "Summary!B2:B20",
          binOptions: { type: "category" }
        }
      ]
    }
  ],
  title: { text: "Issue Pareto" },
  axes: {
    value: { title: { text: "Count" }, majorGridlines: true },
    secondaryValue: {
      min: 0,
      max: 1,
      numberFormat: "0%",
      numberFormatLinked: false,
      position: "right"
    }
  }
})

Supported binOptions modes are:

Mode Required fields Source shape
"auto" none Numeric values; Witan uses Excel-compatible automatic bin width behavior for rendering
"binCount" count Numeric values; divides the numeric span into the requested number of bins
"binWidth" width Numeric values; uses fixed-width bins
"category" none categories plus values; groups equal category labels and sums their values

For numeric modes, allowUnderflow with underflowValue adds a <= value bin, and allowOverflow with overflowValue adds a > value bin. Category-bin mode does not accept numeric bin count, width, underflow, or overflow settings. Pareto charts sort bins by descending count or value before drawing the cumulative percentage line.

Box and Whisker Charts

Box-and-whisker charts use Excel's modern ChartEx chart parts. They are authorable, inspectable, renderable, and saved as ordinary Excel box-and-whisker charts.

Use one chart group with type: "boxWhisker". Each series supplies the observations for one distribution across the category buckets. Use quartileCalculation to choose Excel's quartile method:

  • "exclusive": Excel's default exclusive-median quartiles.
  • "inclusive": Excel's inclusive-median quartiles.
await xlsx.addChart(wb, "Scores", {
  name: "Exam Distribution",
  position: { from: { cell: "F2" }, to: { cell: "N18" } },
  groups: [
    {
      type: "boxWhisker",
      series: [
        {
          name: { ref: "Scores!B1" },
          categories: "Scores!A2:A41",
          values: "Scores!B2:B41",
          quartileCalculation: "inclusive",
          showMeanMarker: true,
          showMeanLine: false,
          showInnerPoints: false,
          showOutlierPoints: true
        },
        {
          name: { ref: "Scores!C1" },
          categories: "Scores!A2:A41",
          values: "Scores!C2:C41"
        }
      ]
    }
  ],
  title: { text: "Exam Distribution" },
  legend: { position: "top" },
  axes: {
    category: { title: { text: "Subject" } },
    value: { title: { text: "Score" }, min: 0, max: 100, majorGridlines: true }
  },
  styleId: 406
})

Supported box-and-whisker series options are:

Field Behavior
quartileCalculation "exclusive" or "inclusive"; defaults to "exclusive"
showInnerPoints Draws non-outlier source points inside the whisker range
showMeanLine Connects mean markers across categories in the same series
showMeanMarker Draws the mean marker; defaults to visible
showOutlierPoints Draws outlier points when Excel would display them; defaults to visible

Whiskers use Excel's 1.5×IQR fence calculation. Very small buckets can have values outside the fences that still participate in axis scaling even when no outlier marker is drawn; Witan keeps those values in the statistical model for Excel-compatible scaling while suppressing marker drawing separately.

Office Script uses Excel's enum spelling, so sheet.addChart(ExcelScript.ChartType.boxwhisker, range, ...) maps to the public chart type boxWhisker. One-column and one-row box-and-whisker source ranges infer a text header as the series name and use the remaining cells as values.

Funnel Charts

Funnel charts use Excel's modern ChartEx chart parts. They are authorable, inspectable, renderable, and saved as ordinary Excel funnel charts.

Use one chart group with type: "funnel" and one category/value series. Funnel charts preserve source order; Witan does not sort stages by value.

await xlsx.addChart(wb, "Summary", {
  name: "Pipeline",
  position: { from: { cell: "F2" }, to: { cell: "N18" } },
  groups: [
    {
      type: "funnel",
      series: [
        {
          name: { ref: "Summary!B1" },
          categories: "Summary!A2:A7",
          values: "Summary!B2:B7",
          dataLabels: { showValue: true, position: "center" }
        }
      ]
    }
  ],
  title: { text: "Pipeline" },
  legend: { position: "bottom" },
  axes: {
    category: {
      title: { text: "Stage" },
      numberFormat: "@",
      numberFormatLinked: false
    }
  },
  styleId: 419
})

Funnel source data is a single category/value shape:

Source shape Behavior
categories plus values Uses the category labels and value magnitudes directly
values only Draws the values and lets Excel use default point categories
Office Script ChartCollection.add(Excel.ChartType.funnel, range, Excel.ChartSeriesBy.columns) with one column Treats the column as values, with an optional header row for the series name
Office Script ChartCollection.add(Excel.ChartType.funnel, range, Excel.ChartSeriesBy.columns) with two columns Treats the first column as categories and the second as values, with the value-column header as the series name

Office Script row-oriented funnel sources are rejected because Excel-authored ChartEx row formulas collapse to a single point rather than the expected multi-stage funnel. Funnel charts do not use a value axis, secondary axes, combo groups, markers, smoothing, bubble sizes, bin options, or waterfall total/connector settings. Non-positive, blank, text, and error values keep their category position but do not draw a visible funnel segment.

Stock Charts

Stock price groups use first-class chart types: stockHLC and stockOHLC. Stock series are positional and follow Excel's UI order:

  • HLC: high, low, close
  • OHLC: open, high, low, close

For an OHLC chart:

await xlsx.addChart(wb, "Prices", {
  name: "OHLC",
  position: { from: { cell: "H2" }, to: { cell: "N18" } },
  groups: [
    {
      type: "stockOHLC",
      series: [
        { stockRole: "open", name: { ref: "Prices!B1" }, categories: "Prices!A2:A30", values: "Prices!B2:B30" },
        { stockRole: "high", name: { ref: "Prices!C1" }, categories: "Prices!A2:A30", values: "Prices!C2:C30" },
        { stockRole: "low", name: { ref: "Prices!D1" }, categories: "Prices!A2:A30", values: "Prices!D2:D30" },
        { stockRole: "close", name: { ref: "Prices!E1" }, categories: "Prices!A2:A30", values: "Prices!E2:E30" }
      ]
    }
  ],
  title: { text: "OHLC" },
  legend: { position: "bottom" }
})

Volume stock charts are combo charts. Put a primary-axis column group with stockRole: "volume" before a secondary-axis stockHLC or stockOHLC group:

groups: [
  {
    type: "column",
    axis: "primary",
    series: [
      { stockRole: "volume", name: { ref: "Prices!B1" }, categories: "Prices!A2:A30", values: "Prices!B2:B30" }
    ]
  },
  {
    type: "stockHLC",
    axis: "secondary",
    series: [
      { stockRole: "high", name: { ref: "Prices!C1" }, categories: "Prices!A2:A30", values: "Prices!C2:C30" },
      { stockRole: "low", name: { ref: "Prices!D1" }, categories: "Prices!A2:A30", values: "Prices!D2:D30" },
      { stockRole: "close", name: { ref: "Prices!E1" }, categories: "Prices!A2:A30", values: "Prices!E2:E30" }
    ]
  }
]

Multi-Level Category Labels

Use categoriesRefType: "multiLevelString" when categories points at a rectangular category-label range, such as ticker plus date columns:

{
  type: "column",
  series: [
    {
      name: { ref: "Sheet1!C1" },
      categories: "Sheet1!A2:B9",
      categoriesRefType: "multiLevelString",
      values: "Sheet1!C2:C9"
    }
  ]
}

Witan writes this as Excel c:multiLvlStrRef / c:multiLvlStrCache and renders the repeated label rows and category separators similarly to Excel. Multi-level categories are a general category-axis feature; they are not limited to stock charts.

For full signatures and field types, see xlsx API.

Excel Interchangeability

Charts authored by Witan are written as normal Excel chart package parts. They open as ordinary editable charts in Excel, with Excel-style chart part locations, relationships, anchors, style parts, color parts, defaults, and chart XML ordering.

For supported chart families, Witan also reads Excel-authored charts back into the public model. A script can call getChart, make a targeted model edit, call setChart, save the workbook, and continue working with the chart in Excel or Witan.

The chart update path rebuilds the modeled chart content and merges it with the existing OOXML chart part. That merge is designed to preserve chart details that are valid Excel chart XML but not necessarily first-class fields in ChartSpec.

Preserved or normalized compatibility details include:

  • Chart package topology and drawing anchors.
  • Series references and formatted chart data caches.
  • Linked number formats for axes, series, and data labels.
  • Radar chart subtype semantics, marker defaults, polar axes, fills, and series z-order.
  • Waterfall ChartEx parts, including total data points, connector lines, ChartEx legends, and supported ChartEx axis options.
  • Histogram and Pareto ChartEx parts, including statistical binning options, category bins, Pareto cumulative-line topology, and secondary percentage axes.
  • Funnel ChartEx parts, including category/value source references, legends, labels, and supported ChartEx category-axis options.
  • Box-and-whisker ChartEx parts, including quartile calculation, mean marker/line settings, inner/outlier point settings, category/value source references, legends, and supported ChartEx axis options.
  • Stock chart topology, including high-low lines, OHLC up/down bars, and volume-stock combo grouping.
  • Multi-level category references and caches.
  • Modern and legacy chart style/color parts.
  • Hidden chart elements such as hidden axes or hidden legends.
  • Chart XML for features such as trendlines, error bars, manual layouts, custom title subtrees, and additional shape properties when the modeled edit does not replace that feature.

The result is not a Witan-only artifact. For the supported chart families, the workbook remains a normal Excel workbook with normal Excel charts.

Development Validation

Chart support is developed against Excel behavior, not just against Witan's own data model.

  • Authored charts are checked for Excel-compatible package topology, chart relationship paths, style/color part names, drawing anchors, series indexes, and Open XML validation.
  • Defaults and serialization are pinned against Excel behavior for bar/column, line, area, pie, doughnut, scatter, bubble, radar, surface, stock, percent-stacked, secondary-axis, and combo cases.
  • Witan-authored ChartEx charts, including waterfall, histogram, Pareto, funnel, and box-and-whisker charts, are manually checked through Excel open-save when serialization changes; meaningful chart semantics must survive Excel save.
  • Office Script chart behavior is tested against native Excel/Office Script oracle behavior for chart creation, lookup, naming, deletion, positioning, styles, data sources, axes, legends, labels, trendlines, error bars, and formatting surfaces.
  • Chart rendering is tested against Excel-captured PNG baselines. The current fixture matrix covers hundreds of cases across area, axis, bar, box-and-whisker, data label, error bar, funnel, histogram, layout, legend, line, linked-format, Pareto, pie, radar, stock, style, surface, title, trendline, and waterfall workbooks, including stock and non-stock multi-level category labels.

Visual Verification

Charts are verified through the renderer. Use previewStyles inside exec when chart verification is part of a larger script:

witan xlsx exec dashboard.xlsx --stdin <<'WITAN'
const charts = await xlsx.listCharts(wb, { sheet: "Summary" })
const chart = charts.find(c => c.name === "Revenue")
if (!chart) throw new Error("Revenue chart not found")

await xlsx.previewStyles(wb, "Summary!F2:N18")
return chart
WITAN

Use the standalone renderer when you need an image file or pixel diff:

witan xlsx render dashboard.xlsx -r "Summary!F2:N18" -o revenue-before.png
# ... edit chart data, formatting, or the chart spec ...
witan xlsx render dashboard.xlsx -r "Summary!F2:N18" --diff revenue-before.png

The chart renderer covers the supported chart families above, plus common titles, axes, axis titles, legends, data labels, gridlines, trendlines, error bars, chart styles, colors, fills, line styles, markers, multi-level category labels, and linked number formats. For ChartEx charts, the renderer handles waterfall axes, totals, connector lines, histogram and Pareto bins, Pareto cumulative percentage lines, funnel segments, box-and-whisker statistics, linked number formats, and Excel-style staggered category labels when labels need additional horizontal space.

Renderer changes are checked by comparing Witan output with Excel-captured baselines and tracking render-gap metrics. This is separate from the chart model round-trip tests: one set checks that the workbook package remains Excel-compatible, and the other checks that the visible output stays close to Excel's raster output.

For general range-rendering behavior, output formats, device pixel ratio, limits, and pixel-diff output, see Witan Render.