Version: 4.0.1 (also present in current master src/gen-charts.ts)
Issue
When a chart has string category labels, genXmlChartData always emits the category axis data (<c:cat>) as <c:multiLvlStrRef> — even when the categories are a flat, single-level list. There is no code path that emits <c:strRef> for string categories:
// gen-charts.ts — "2: Categories" block (dist/pptxgen.cjs.js ~L3774)
strXml += ' <c:multiLvlStrRef>';
strXml += ` <c:f>Sheet1!$A$2:$${getExcelColName(obj.labels.length)}$${obj.labels[0].length + 1}</c:f>`;
strXml += ' <c:multiLvlStrCache>';
...
Per ECMA-376, <c:multiLvlStrRef> is intended for hierarchical (multi-level) category axes. PowerPoint itself writes flat string categories as <c:strRef>/<c:strCache>, and that is what most third-party OOXML chart parsers implement.
Impact
PowerPoint handles multiLvlStrRef fine, but several importers/renderers do not implement the multiLvlStrRef/<c:lvl> structure and silently fall back to sequential numbers, so a chart created with categories: ["Q1","Q2","Q3","Q4"] renders with category labels 1, 2, 3, 4 instead. We reproduced this with Google Slides' pptx import and with a custom OOXML chart viewer.
Repro
const pptxgen = require("pptxgenjs");
const pptx = new pptxgen();
const slide = pptx.addSlide();
slide.addChart(pptx.ChartType.bar, [
{ name: "Sales", labels: ["Q1", "Q2", "Q3", "Q4"], values: [120, 135, 150, 180] },
], { x: 1, y: 1, w: 8, h: 4 });
pptx.writeFile({ fileName: "repro.pptx" });
// unzip repro.pptx → ppt/charts/chart1.xml contains <c:multiLvlStrRef> under <c:cat>
Suggested fix
In the <c:cat> block of genXmlChartData, emit <c:strRef>/<c:strCache> when obj.labels.length === 1, and keep the existing <c:multiLvlStrRef> output for genuinely multi-level labels (obj.labels.length > 1):
if (obj.labels.length === 1) {
strXml += ' <c:strRef>';
strXml += ` <c:f>Sheet1!$A$2:$A$${obj.labels[0].length + 1}</c:f>`;
strXml += ' <c:strCache>';
strXml += ` <c:ptCount val="${obj.labels[0].length}"/>`;
obj.labels[0].forEach((label, idx) => (strXml += `<c:pt idx="${idx}"><c:v>${encodeXmlEntities(label)}</c:v></c:pt>`));
strXml += ' </c:strCache>';
strXml += ' </c:strRef>';
} else {
// existing multiLvlStrRef output
}
We are currently applying this as a local postinstall patch to the dist files and verified that with it, the affected importers show the correct category labels (Q1…Q4 instead of 1…4), while PowerPoint (desktop and web) continues to render the charts correctly. Happy to open a PR if this direction sounds good.
Version: 4.0.1 (also present in current
mastersrc/gen-charts.ts)Issue
When a chart has string category labels,
genXmlChartDataalways emits the category axis data (<c:cat>) as<c:multiLvlStrRef>— even when the categories are a flat, single-level list. There is no code path that emits<c:strRef>for string categories:Per ECMA-376,
<c:multiLvlStrRef>is intended for hierarchical (multi-level) category axes. PowerPoint itself writes flat string categories as<c:strRef>/<c:strCache>, and that is what most third-party OOXML chart parsers implement.Impact
PowerPoint handles
multiLvlStrReffine, but several importers/renderers do not implement themultiLvlStrRef/<c:lvl>structure and silently fall back to sequential numbers, so a chart created withcategories: ["Q1","Q2","Q3","Q4"]renders with category labels1, 2, 3, 4instead. We reproduced this with Google Slides' pptx import and with a custom OOXML chart viewer.Repro
Suggested fix
In the
<c:cat>block ofgenXmlChartData, emit<c:strRef>/<c:strCache>whenobj.labels.length === 1, and keep the existing<c:multiLvlStrRef>output for genuinely multi-level labels (obj.labels.length > 1):We are currently applying this as a local postinstall patch to the dist files and verified that with it, the affected importers show the correct category labels (
Q1…Q4instead of1…4), while PowerPoint (desktop and web) continues to render the charts correctly. Happy to open a PR if this direction sounds good.