Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion packages/ooxml.js/src/typed/pptx/chart.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest";
import type { XmlElement } from "../../model/node";
import { readChartResidue } from "./chart";
import { el, txt } from "../../xml/fragment";
import { readChartResidue, readChartTable } from "./chart";

function chartRoot(): XmlElement {
return {
Expand Down Expand Up @@ -29,3 +30,58 @@ describe("readChartResidue", () => {
expect(second.xml).toBe(first.xml);
});
});

// One bar chart with a single series, its category labels and values in the caches PowerPoint writes
// beside the data reference.
function barChartRoot(): XmlElement {
const cachedPoint = (idx: string, value: string) =>
el("c:pt", { idx }, [el("c:v", {}, [txt(value)])]);
return el("c:chartSpace", {}, [
el("c:chart", {}, [
el("c:plotArea", {}, [
el("c:barChart", {}, [
el("c:ser", {}, [
el("c:tx", {}, [
el("c:strRef", {}, [
el("c:strCache", {}, [cachedPoint("0", "FY26")]),
]),
]),
el("c:cat", {}, [
el("c:strRef", {}, [
el("c:strCache", {}, [
cachedPoint("0", "EMEA"),
cachedPoint("1", "APAC"),
]),
]),
]),
el("c:val", {}, [
el("c:numRef", {}, [
el("c:numCache", {}, [
cachedPoint("0", "42"),
cachedPoint("1", "51"),
]),
]),
]),
]),
]),
]),
]),
]);
}

describe("readChartTable", () => {
it('marks the table it produces as origin "chart"', () => {
// A ContentTable is a native table, a chart's cached data, or a spreadsheet range, and a consumer
// holding one cannot otherwise tell which. It matters: a chart's cached numbers are exact and
// quotable, where a vision reading of the same chart would be approximate -- so the two have to be
// distinguishable by something other than a consumer's guess.
const table = readChartTable(barChartRoot(), {
xPt: 0,
yPt: 0,
widthPt: 400,
heightPt: 300,
});

expect(table?.origin).toBe("chart");
});
});
1 change: 1 addition & 0 deletions packages/ooxml.js/src/typed/pptx/chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export function readChartTable(
const columnWidthPt = frame.widthPt / (series.length + 1);
return {
kind: "table",
origin: "chart",
rows,
columnWidthsPt: Array.from(
{ length: series.length + 1 },
Expand Down
36 changes: 35 additions & 1 deletion packages/ooxml.js/src/typed/pptx/diagram.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest";
import type { XmlElement } from "../../model/node";
import { readDiagramResidue } from "./diagram";
import { el, txt } from "../../xml/fragment";
import { readDiagramResidue, readDiagramText } from "./diagram";

function part(tag: string): XmlElement {
return { type: "element", tag, attributes: [], children: [] };
Expand Down Expand Up @@ -31,3 +32,36 @@ describe("readDiagramResidue", () => {
expect(readDiagramResidue(undefined, undefined, undefined)).toBeUndefined();
});
});

// A two-node data model: a doc root, two content nodes, and the parOf connections making it a tree.
function dataModelRoot(): XmlElement {
const point = (id: string, text: string, type?: string) =>
el("dgm:pt", type === undefined ? { modelId: id } : { modelId: id, type }, [
el("dgm:t", {}, [
el("a:p", {}, [el("a:r", {}, [el("a:t", {}, [txt(text)])])]),
]),
]);
const cxn = (srcId: string, destId: string, srcOrd: string) =>
el("dgm:cxn", { srcId, destId, type: "parOf", srcOrd });
return el("dgm:dataModel", {}, [
el("dgm:ptLst", {}, [
point("root", "", "doc"),
point("a", "Ad hoc"),
point("b", "Repeatable"),
]),
el("dgm:cxnLst", {}, [cxn("root", "a", "0"), cxn("root", "b", "1")]),
]);
}

describe("readDiagramText", () => {
it('marks every node paragraph as origin "diagram"', () => {
// SmartArt node text reaches the model as ordinary paragraphs, so nothing otherwise distinguishes a
// process flow's step labels from body prose -- and they are not the same thing: the relationships
// between the nodes (the arrows, the hierarchy) are not recovered, which a consumer reading them as
// prose needs to know.
const paragraphs = readDiagramText(dataModelRoot());

expect(paragraphs.length).toBeGreaterThan(0);
expect(paragraphs.every((p) => p.origin === "diagram")).toBe(true);
});
});
2 changes: 1 addition & 1 deletion packages/ooxml.js/src/typed/pptx/diagram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ function diagramTextParagraphs(
runs.push({ text: "\n" });
}
}
return { kind: "paragraph", runs };
return { kind: "paragraph", origin: "diagram", runs };
});
}

Expand Down