@json-to-office/shared 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capabilities-DrHJ6_4G.d.ts +210 -0
- package/dist/chunk-BJNBJSQG.js +691 -0
- package/dist/chunk-BJNBJSQG.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/rendering/index.d.ts +177 -181
- package/dist/rendering/index.js +29 -3
- package/package.json +1 -1
- package/dist/chunk-JM5KTMNL.js +0 -240
- package/dist/chunk-JM5KTMNL.js.map +0 -1
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
// src/rendering/types.ts
|
|
2
|
+
function assertNever(value, context) {
|
|
3
|
+
const described = describeUnhandled(value);
|
|
4
|
+
throw new Error(
|
|
5
|
+
context ? `Unhandled variant in ${context}: ${described}` : `Unhandled variant: ${described}`
|
|
6
|
+
);
|
|
7
|
+
}
|
|
8
|
+
function describeUnhandled(value) {
|
|
9
|
+
if (value === null || typeof value !== "object") {
|
|
10
|
+
return String(value);
|
|
11
|
+
}
|
|
12
|
+
const kind = value.kind;
|
|
13
|
+
const type = value.type;
|
|
14
|
+
if (typeof kind === "string") return `kind="${kind}"`;
|
|
15
|
+
if (typeof type === "string") return `type="${type}"`;
|
|
16
|
+
try {
|
|
17
|
+
return JSON.stringify(value);
|
|
18
|
+
} catch {
|
|
19
|
+
return Object.prototype.toString.call(value);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/rendering/diagnostics.ts
|
|
24
|
+
var UnsupportedRendererFeatureError = class _UnsupportedRendererFeatureError extends Error {
|
|
25
|
+
code = "UNSUPPORTED_RENDERER_FEATURE";
|
|
26
|
+
format;
|
|
27
|
+
rendererId;
|
|
28
|
+
/** Distinct unsupported features, in first-seen order. */
|
|
29
|
+
features;
|
|
30
|
+
/** Distinct IR paths that required them, in first-seen order. */
|
|
31
|
+
paths;
|
|
32
|
+
/** Every error-severity diagnostic that produced this failure. */
|
|
33
|
+
diagnostics;
|
|
34
|
+
constructor(init) {
|
|
35
|
+
const { format, rendererId, diagnostics } = init;
|
|
36
|
+
const features = distinct(diagnostics.map((d) => d.feature));
|
|
37
|
+
const paths = distinct(diagnostics.map((d) => d.path));
|
|
38
|
+
super(formatMessage(format, rendererId, diagnostics, features));
|
|
39
|
+
this.name = "UnsupportedRendererFeatureError";
|
|
40
|
+
this.format = format;
|
|
41
|
+
this.rendererId = rendererId;
|
|
42
|
+
this.features = features;
|
|
43
|
+
this.paths = paths;
|
|
44
|
+
this.diagnostics = [...diagnostics];
|
|
45
|
+
if (Error.captureStackTrace) {
|
|
46
|
+
Error.captureStackTrace(this, _UnsupportedRendererFeatureError);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var UnknownRendererError = class _UnknownRendererError extends Error {
|
|
51
|
+
code = "UNKNOWN_RENDERER";
|
|
52
|
+
format;
|
|
53
|
+
/** What the caller asked for. */
|
|
54
|
+
rendererId;
|
|
55
|
+
/** Every id registered for this format, in registration order. */
|
|
56
|
+
availableIds;
|
|
57
|
+
constructor(format, rendererId, availableIds) {
|
|
58
|
+
const known = availableIds.map((id) => `"${id}"`).join(", ");
|
|
59
|
+
super(
|
|
60
|
+
`Unknown ${format} renderer "${rendererId}". Available renderers: ${known}.`
|
|
61
|
+
);
|
|
62
|
+
this.name = "UnknownRendererError";
|
|
63
|
+
this.format = format;
|
|
64
|
+
this.rendererId = rendererId;
|
|
65
|
+
this.availableIds = [...availableIds];
|
|
66
|
+
if (Error.captureStackTrace) {
|
|
67
|
+
Error.captureStackTrace(this, _UnknownRendererError);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
function distinct(values) {
|
|
72
|
+
return [...new Set(values)];
|
|
73
|
+
}
|
|
74
|
+
function formatMessage(format, rendererId, diagnostics, features) {
|
|
75
|
+
const featureList = features.map((f) => `"${f}"`).join(", ");
|
|
76
|
+
const lines = diagnostics.map(
|
|
77
|
+
(d) => ` - ${d.feature} at ${d.path}: ${d.message}`
|
|
78
|
+
);
|
|
79
|
+
return `The "${rendererId}" ${format} renderer does not support ${features.length} required feature(s): ${featureList}.
|
|
80
|
+
${lines.join("\n")}`;
|
|
81
|
+
}
|
|
82
|
+
function rendererError(feature, path, message) {
|
|
83
|
+
return { feature, path, severity: "error", message };
|
|
84
|
+
}
|
|
85
|
+
function rendererWarning(feature, path, message) {
|
|
86
|
+
return { feature, path, severity: "warning", message };
|
|
87
|
+
}
|
|
88
|
+
function partitionDiagnostics(diagnostics) {
|
|
89
|
+
const errors = [];
|
|
90
|
+
const warnings = [];
|
|
91
|
+
for (const diagnostic of diagnostics) {
|
|
92
|
+
if (diagnostic.severity === "error") errors.push(diagnostic);
|
|
93
|
+
else warnings.push(diagnostic);
|
|
94
|
+
}
|
|
95
|
+
return { errors, warnings };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/rendering/capabilities.ts
|
|
99
|
+
var FeatureRequirementCollector = class {
|
|
100
|
+
requirements = [];
|
|
101
|
+
seen = /* @__PURE__ */ new Set();
|
|
102
|
+
/**
|
|
103
|
+
* Record that `feature` is needed at `path`.
|
|
104
|
+
*
|
|
105
|
+
* Duplicate (feature, path) pairs collapse, so a compiler can call this
|
|
106
|
+
* unconditionally inside a loop without inflating the diagnostics.
|
|
107
|
+
*/
|
|
108
|
+
require(feature, path, detail) {
|
|
109
|
+
const key = `${feature}\0${path}`;
|
|
110
|
+
if (this.seen.has(key)) return;
|
|
111
|
+
this.seen.add(key);
|
|
112
|
+
this.requirements.push(
|
|
113
|
+
detail === void 0 ? { feature, path } : { feature, path, detail }
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
/** Every recorded requirement, in first-seen order. */
|
|
117
|
+
list() {
|
|
118
|
+
return this.requirements;
|
|
119
|
+
}
|
|
120
|
+
/** Distinct required features, in first-seen order. */
|
|
121
|
+
features() {
|
|
122
|
+
return [...new Set(this.requirements.map((r) => r.feature))];
|
|
123
|
+
}
|
|
124
|
+
/** True when nothing has been required yet. */
|
|
125
|
+
isEmpty() {
|
|
126
|
+
return this.requirements.length === 0;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
function diagnoseUnsupportedFeatures(required, capabilities, rendererId) {
|
|
130
|
+
const diagnostics = [];
|
|
131
|
+
for (const requirement of required) {
|
|
132
|
+
if (capabilities.has(requirement.feature)) continue;
|
|
133
|
+
diagnostics.push(
|
|
134
|
+
rendererError(
|
|
135
|
+
requirement.feature,
|
|
136
|
+
requirement.path,
|
|
137
|
+
buildMessage(requirement, rendererId)
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
return diagnostics;
|
|
142
|
+
}
|
|
143
|
+
function buildMessage(requirement, rendererId) {
|
|
144
|
+
const base = `the "${rendererId}" renderer cannot express "${requirement.feature}"`;
|
|
145
|
+
return requirement.detail ? `${base} (${requirement.detail})` : base;
|
|
146
|
+
}
|
|
147
|
+
function assertRendererSupports(required, renderer) {
|
|
148
|
+
const diagnostics = diagnoseUnsupportedFeatures(
|
|
149
|
+
required,
|
|
150
|
+
renderer.capabilities,
|
|
151
|
+
renderer.id
|
|
152
|
+
);
|
|
153
|
+
if (diagnostics.length === 0) return;
|
|
154
|
+
throw new UnsupportedRendererFeatureError({
|
|
155
|
+
format: renderer.format,
|
|
156
|
+
rendererId: renderer.id,
|
|
157
|
+
diagnostics
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
var RendererRegistry = class {
|
|
161
|
+
constructor(format, defaultId) {
|
|
162
|
+
this.format = format;
|
|
163
|
+
this.defaultId = defaultId;
|
|
164
|
+
}
|
|
165
|
+
renderers = /* @__PURE__ */ new Map();
|
|
166
|
+
/**
|
|
167
|
+
* Register a lazily-constructed renderer.
|
|
168
|
+
*
|
|
169
|
+
* The factory is async and only invoked on selection, so an adapter whose
|
|
170
|
+
* backend is an optional dependency is never imported unless it is chosen.
|
|
171
|
+
*/
|
|
172
|
+
register(id, factory) {
|
|
173
|
+
this.renderers.set(id, factory);
|
|
174
|
+
}
|
|
175
|
+
/** Renderer ids registered for this format, in registration order. */
|
|
176
|
+
ids() {
|
|
177
|
+
return [...this.renderers.keys()];
|
|
178
|
+
}
|
|
179
|
+
/** The id used when a caller does not pass one. */
|
|
180
|
+
getDefaultId() {
|
|
181
|
+
return this.defaultId;
|
|
182
|
+
}
|
|
183
|
+
has(id) {
|
|
184
|
+
return this.renderers.has(id);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Resolve a renderer, defaulting when `id` is omitted.
|
|
188
|
+
*
|
|
189
|
+
* An unknown id is `UnknownRendererError`, which carries the id asked for and
|
|
190
|
+
* the ones that exist, so a caller boundary can answer "bad request" rather
|
|
191
|
+
* than "the server broke". A missing optional dependency is re-thrown with an
|
|
192
|
+
* actionable install hint.
|
|
193
|
+
*/
|
|
194
|
+
async resolve(id) {
|
|
195
|
+
const selected = id ?? this.defaultId;
|
|
196
|
+
const factory = this.renderers.get(selected);
|
|
197
|
+
if (!factory) {
|
|
198
|
+
throw new UnknownRendererError(this.format, selected, this.ids());
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
return await factory();
|
|
202
|
+
} catch (error) {
|
|
203
|
+
throw enrichLoadFailure(error, this.format, selected);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
function enrichLoadFailure(error, format, rendererId) {
|
|
208
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
209
|
+
const isMissingModule = /Cannot find (?:module|package)|ERR_MODULE_NOT_FOUND|Failed to resolve/i.test(
|
|
210
|
+
message
|
|
211
|
+
);
|
|
212
|
+
if (!isMissingModule) {
|
|
213
|
+
return error instanceof Error ? error : new Error(message);
|
|
214
|
+
}
|
|
215
|
+
const pkg = missingPackageName(message) ?? `the "${rendererId}" backend`;
|
|
216
|
+
const enriched = new Error(
|
|
217
|
+
`The "${rendererId}" ${format} renderer requires ${pkg}, which is not installed. Install it with: pnpm add ${pkg}
|
|
218
|
+
Original error: ${message}`
|
|
219
|
+
);
|
|
220
|
+
enriched.name = "RendererDependencyMissingError";
|
|
221
|
+
return enriched;
|
|
222
|
+
}
|
|
223
|
+
function missingPackageName(message) {
|
|
224
|
+
const match = /Cannot find (?:module|package) ['"]([^'"]+)['"]/.exec(message) ?? /Failed to resolve (?:module|import)[: ]+['"]?([^'"\s]+)/.exec(message);
|
|
225
|
+
return match?.[1];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/rendering/chart-parts.ts
|
|
229
|
+
var CHART_WORKBOOK_SHEET_NAME = "Sheet1";
|
|
230
|
+
var CHART_PACKAGE_RELATIONSHIP = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package";
|
|
231
|
+
var CHART_WORKBOOK_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
232
|
+
function escapeXml(value) {
|
|
233
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
234
|
+
}
|
|
235
|
+
function columnLetter(index) {
|
|
236
|
+
let remaining = index;
|
|
237
|
+
let letters = "";
|
|
238
|
+
while (remaining > 0) {
|
|
239
|
+
const rest = (remaining - 1) % 26;
|
|
240
|
+
letters = String.fromCharCode(65 + rest) + letters;
|
|
241
|
+
remaining = Math.floor((remaining - 1) / 26);
|
|
242
|
+
}
|
|
243
|
+
return letters;
|
|
244
|
+
}
|
|
245
|
+
function cellNumber(value) {
|
|
246
|
+
return Number.isFinite(value) ? String(value) : "0";
|
|
247
|
+
}
|
|
248
|
+
function inlineStringCell(reference, text) {
|
|
249
|
+
return `<c r="${reference}" t="inlineStr"><is><t>${escapeXml(text)}</t></is></c>`;
|
|
250
|
+
}
|
|
251
|
+
function numberCell(reference, value) {
|
|
252
|
+
return `<c r="${reference}"><v>${cellNumber(value)}</v></c>`;
|
|
253
|
+
}
|
|
254
|
+
function sheetXml(series) {
|
|
255
|
+
const rowCount = Math.max(
|
|
256
|
+
series[0]?.labels.length ?? 0,
|
|
257
|
+
...series.map((entry) => entry.values.length)
|
|
258
|
+
);
|
|
259
|
+
const lastColumn = columnLetter(series.length + 1);
|
|
260
|
+
const rows = [];
|
|
261
|
+
const header = [
|
|
262
|
+
`<c r="A1"/>`,
|
|
263
|
+
...series.map(
|
|
264
|
+
(entry, index) => inlineStringCell(
|
|
265
|
+
`${columnLetter(index + 2)}1`,
|
|
266
|
+
entry.name ?? `Series ${index + 1}`
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
];
|
|
270
|
+
rows.push(`<row r="1">${header.join("")}</row>`);
|
|
271
|
+
for (let row = 0; row < rowCount; row++) {
|
|
272
|
+
const reference = row + 2;
|
|
273
|
+
const label = series[0]?.labels[row];
|
|
274
|
+
const cells = [
|
|
275
|
+
...label !== void 0 ? [inlineStringCell(`A${reference}`, label)] : [],
|
|
276
|
+
...series.flatMap(
|
|
277
|
+
(entry, index) => row < entry.values.length ? [
|
|
278
|
+
numberCell(
|
|
279
|
+
`${columnLetter(index + 2)}${reference}`,
|
|
280
|
+
entry.values[row]
|
|
281
|
+
)
|
|
282
|
+
] : []
|
|
283
|
+
)
|
|
284
|
+
];
|
|
285
|
+
if (cells.length === 0) continue;
|
|
286
|
+
rows.push(`<row r="${reference}">${cells.join("")}</row>`);
|
|
287
|
+
}
|
|
288
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><dimension ref="A1:${lastColumn}${Math.max(rowCount + 1, 1)}"/><sheetData>${rows.join("")}</sheetData></worksheet>`;
|
|
289
|
+
}
|
|
290
|
+
var WORKBOOK_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="${CHART_WORKBOOK_SHEET_NAME}" sheetId="1" r:id="rId1"/></sheets></workbook>`;
|
|
291
|
+
var WORKBOOK_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/></Relationships>`;
|
|
292
|
+
var ROOT_RELS = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>`;
|
|
293
|
+
var CONTENT_TYPES = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/></Types>`;
|
|
294
|
+
function chartWorkbookParts(series) {
|
|
295
|
+
return [
|
|
296
|
+
["[Content_Types].xml", CONTENT_TYPES],
|
|
297
|
+
["_rels/.rels", ROOT_RELS],
|
|
298
|
+
["xl/workbook.xml", WORKBOOK_XML],
|
|
299
|
+
["xl/_rels/workbook.xml.rels", WORKBOOK_RELS],
|
|
300
|
+
["xl/worksheets/sheet1.xml", sheetXml(series)]
|
|
301
|
+
];
|
|
302
|
+
}
|
|
303
|
+
function seriesValueReference(seriesIndex, pointCount) {
|
|
304
|
+
const column = columnLetter(seriesIndex + 2);
|
|
305
|
+
return `${CHART_WORKBOOK_SHEET_NAME}!$${column}$2:$${column}$${pointCount + 1}`;
|
|
306
|
+
}
|
|
307
|
+
function categoryReference(pointCount) {
|
|
308
|
+
return `${CHART_WORKBOOK_SHEET_NAME}!$A$2:$A$${pointCount + 1}`;
|
|
309
|
+
}
|
|
310
|
+
function seriesNameReference(seriesIndex) {
|
|
311
|
+
return `${CHART_WORKBOOK_SHEET_NAME}!$${columnLetter(seriesIndex + 2)}$1`;
|
|
312
|
+
}
|
|
313
|
+
function chartWorkbookRelsXml(workbookName) {
|
|
314
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="${CHART_PACKAGE_RELATIONSHIP}" Target="../embeddings/${escapeXml(workbookName)}"/></Relationships>`;
|
|
315
|
+
}
|
|
316
|
+
function fillSeriesFormulas(seriesXml, seriesIndex, categoryCount, valueCount) {
|
|
317
|
+
if (categoryCount === 0 || valueCount === 0) return seriesXml;
|
|
318
|
+
const references = [
|
|
319
|
+
seriesNameReference(seriesIndex),
|
|
320
|
+
categoryReference(categoryCount),
|
|
321
|
+
// This series' own length, not the chart's: a range longer than the cells
|
|
322
|
+
// behind it claims data the workbook does not hold, and disagrees with the
|
|
323
|
+
// `c:ptCount` the backend already cached.
|
|
324
|
+
seriesValueReference(seriesIndex, valueCount)
|
|
325
|
+
];
|
|
326
|
+
let next = 0;
|
|
327
|
+
return seriesXml.replace(/<c:f\/>/g, () => {
|
|
328
|
+
const reference = references[next++];
|
|
329
|
+
return reference === void 0 ? "<c:f/>" : `<c:f>${escapeXml(reference)}</c:f>`;
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
var STROKE_COLORED = /* @__PURE__ */ new Set([
|
|
333
|
+
"line",
|
|
334
|
+
"scatter",
|
|
335
|
+
"radar"
|
|
336
|
+
]);
|
|
337
|
+
var POINT_COLORED = /* @__PURE__ */ new Set(["pie", "doughnut"]);
|
|
338
|
+
function dataPoint(index, hex, border) {
|
|
339
|
+
return `<c:dPt><c:idx val="${index}"/><c:bubble3D val="0"/><c:spPr><a:solidFill><a:srgbClr val="${hex}"/></a:solidFill>` + (border ? outline(border.widthPoints, border.color) : "") + `</c:spPr></c:dPt>`;
|
|
340
|
+
}
|
|
341
|
+
function paintSeries(seriesXml, color, chartType, palette, pointCount, chart) {
|
|
342
|
+
const fillFor = (hex) => `<a:solidFill><a:srgbClr val="${hex.toUpperCase()}"/></a:solidFill>`;
|
|
343
|
+
const stroke = STROKE_COLORED.has(chartType);
|
|
344
|
+
const border = stroke ? void 0 : chart.dataBorder;
|
|
345
|
+
if (POINT_COLORED.has(chartType)) {
|
|
346
|
+
if (palette.length === 0 || pointCount === 0) return seriesXml;
|
|
347
|
+
const points = Array.from(
|
|
348
|
+
{ length: pointCount },
|
|
349
|
+
(_, index) => dataPoint(index, palette[index % palette.length].toUpperCase(), border)
|
|
350
|
+
).join("");
|
|
351
|
+
for (const anchor of ["<c:dLbls>", "<c:cat>", "<c:val>"]) {
|
|
352
|
+
if (seriesXml.includes(anchor)) {
|
|
353
|
+
return seriesXml.replace(anchor, `${points}${anchor}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return seriesXml;
|
|
357
|
+
}
|
|
358
|
+
const parts = [];
|
|
359
|
+
if (!stroke) {
|
|
360
|
+
if (color) parts.push(fillFor(color));
|
|
361
|
+
if (border) parts.push(outline(border.widthPoints, border.color));
|
|
362
|
+
if (parts.length === 0) return seriesXml;
|
|
363
|
+
return seriesXml.replace("<c:spPr/>", `<c:spPr>${parts.join("")}</c:spPr>`);
|
|
364
|
+
}
|
|
365
|
+
if (!color && chart.lineWidthPoints === void 0) return seriesXml;
|
|
366
|
+
const fill = color ? fillFor(color) : "";
|
|
367
|
+
const line = outline(chart.lineWidthPoints, color);
|
|
368
|
+
const painted = seriesXml.replace("<c:spPr/>", `<c:spPr>${line}</c:spPr>`);
|
|
369
|
+
if (!color) return painted;
|
|
370
|
+
const markerSpPr = `<c:spPr>${fill}<a:ln>${fill}</a:ln></c:spPr>`;
|
|
371
|
+
const existing = painted.match(/<c:marker>[\s\S]*?<\/c:marker>/);
|
|
372
|
+
if (!existing) {
|
|
373
|
+
return painted.replace(
|
|
374
|
+
`<c:spPr>${line}</c:spPr>`,
|
|
375
|
+
`<c:spPr>${line}</c:spPr><c:marker>${markerSpPr}</c:marker>`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
if (existing[0].includes("<c:spPr>")) return painted;
|
|
379
|
+
return painted.replace(
|
|
380
|
+
existing[0],
|
|
381
|
+
existing[0].replace("</c:marker>", `${markerSpPr}</c:marker>`)
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
function axisTitle(text) {
|
|
385
|
+
return `<c:title><c:tx><c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t>${escapeXml(text)}</a:t></a:r></a:p></c:rich></c:tx><c:overlay val="0"/></c:title>`;
|
|
386
|
+
}
|
|
387
|
+
var POINTS_TO_EMU = 12700;
|
|
388
|
+
var DASH_STYLES = {
|
|
389
|
+
solid: "solid",
|
|
390
|
+
dash: "dash",
|
|
391
|
+
dot: "sysDot"
|
|
392
|
+
};
|
|
393
|
+
function outline(widthPoints, hex) {
|
|
394
|
+
const width = widthPoints !== void 0 ? ` w="${Math.round(widthPoints * POINTS_TO_EMU)}"` : "";
|
|
395
|
+
const fill = hex ? `<a:solidFill><a:srgbClr val="${hex.toUpperCase()}"/></a:solidFill>` : "";
|
|
396
|
+
return `<a:ln${width}>${fill}</a:ln>`;
|
|
397
|
+
}
|
|
398
|
+
function gridLinesElement(gridLine) {
|
|
399
|
+
if (gridLine.style === "none") return "";
|
|
400
|
+
const parts = [];
|
|
401
|
+
if (gridLine.color) {
|
|
402
|
+
parts.push(
|
|
403
|
+
`<a:solidFill><a:srgbClr val="${gridLine.color.toUpperCase()}"/></a:solidFill>`
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
const dash = gridLine.style ? DASH_STYLES[gridLine.style] : void 0;
|
|
407
|
+
if (dash) parts.push(`<a:prstDash val="${dash}"/>`);
|
|
408
|
+
if (parts.length === 0 && gridLine.size === void 0) {
|
|
409
|
+
return "<c:majorGridlines/>";
|
|
410
|
+
}
|
|
411
|
+
const width = gridLine.size !== void 0 ? ` w="${Math.round(gridLine.size * POINTS_TO_EMU)}"` : "";
|
|
412
|
+
return `<c:majorGridlines><c:spPr><a:ln${width}>${parts.join("")}</a:ln></c:spPr></c:majorGridlines>`;
|
|
413
|
+
}
|
|
414
|
+
function defaultRunProperties(font) {
|
|
415
|
+
if (!font) return "<a:defRPr/>";
|
|
416
|
+
const attrs = (font.fontSize !== void 0 ? ` sz="${Math.round(font.fontSize * 100)}"` : "") + (font.bold !== void 0 ? ` b="${font.bold ? 1 : 0}"` : "");
|
|
417
|
+
const children = (font.color ? `<a:solidFill><a:srgbClr val="${font.color.toUpperCase()}"/></a:solidFill>` : "") + (font.fontFamily ? `<a:latin typeface="${escapeXml(font.fontFamily)}"/>` : "");
|
|
418
|
+
return children ? `<a:defRPr${attrs}>${children}</a:defRPr>` : `<a:defRPr${attrs}/>`;
|
|
419
|
+
}
|
|
420
|
+
function hasTextStyle(font) {
|
|
421
|
+
return !!font && Object.keys(font).length > 0;
|
|
422
|
+
}
|
|
423
|
+
function textProperties(rotation, font) {
|
|
424
|
+
const bodyPr = rotation !== void 0 ? `<a:bodyPr rot="${Math.round(rotation * 6e4)}" spcFirstLastPara="1" vertOverflow="ellipsis" vert="horz" wrap="square" anchorCtr="1"/>` : "<a:bodyPr/>";
|
|
425
|
+
return `<c:txPr>${bodyPr}<a:lstStyle/><a:p><a:pPr>` + defaultRunProperties(font) + `</a:pPr><a:endParaRPr lang="en-US"/></a:p></c:txPr>`;
|
|
426
|
+
}
|
|
427
|
+
function rewriteAxis(axisXml, edits) {
|
|
428
|
+
const axPos = axisXml.match(/<c:axPos[^>]*\/>/);
|
|
429
|
+
const crossAxAt = axisXml.indexOf("<c:crossAx");
|
|
430
|
+
if (!axPos || crossAxAt < 0) return axisXml;
|
|
431
|
+
const headEnd = axisXml.indexOf(axPos[0]) + axPos[0].length;
|
|
432
|
+
let head = axisXml.slice(0, headEnd);
|
|
433
|
+
const middle = axisXml.slice(headEnd, crossAxAt);
|
|
434
|
+
let tail = axisXml.slice(crossAxAt);
|
|
435
|
+
if (edits.hidden !== void 0) {
|
|
436
|
+
head = head.replace(
|
|
437
|
+
/<c:delete val="[^"]*"\/>/,
|
|
438
|
+
`<c:delete val="${edits.hidden ? 1 : 0}"/>`
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
if (edits.max !== void 0 || edits.min !== void 0) {
|
|
442
|
+
const bounds = (edits.max !== void 0 ? `<c:max val="${edits.max}"/>` : "") + (edits.min !== void 0 ? `<c:min val="${edits.min}"/>` : "");
|
|
443
|
+
head = head.replace("</c:scaling>", `${bounds}</c:scaling>`);
|
|
444
|
+
}
|
|
445
|
+
const existingMajorGrid = middle.match(
|
|
446
|
+
/<c:majorGridlines(?:\/>|>[\s\S]*?<\/c:majorGridlines>)/
|
|
447
|
+
)?.[0];
|
|
448
|
+
const existingMinorGrid = middle.match(
|
|
449
|
+
/<c:minorGridlines(?:\/>|>[\s\S]*?<\/c:minorGridlines>)/
|
|
450
|
+
)?.[0];
|
|
451
|
+
const existingTitle = middle.match(/<c:title>[\s\S]*?<\/c:title>/)?.[0];
|
|
452
|
+
const existingNumFmt = middle.match(/<c:numFmt[^>]*\/>/)?.[0];
|
|
453
|
+
const existingMajorTick = middle.match(/<c:majorTickMark[^>]*\/>/)?.[0];
|
|
454
|
+
const existingMinorTick = middle.match(/<c:minorTickMark[^>]*\/>/)?.[0];
|
|
455
|
+
const existingTickLblPos = middle.match(/<c:tickLblPos[^>]*\/>/)?.[0];
|
|
456
|
+
const existingSpPr = middle.match(/<c:spPr>[\s\S]*?<\/c:spPr>/)?.[0];
|
|
457
|
+
const existingTxPr = middle.match(/<c:txPr>[\s\S]*?<\/c:txPr>/)?.[0];
|
|
458
|
+
const rebuilt = [
|
|
459
|
+
edits.gridLine ? gridLinesElement(edits.gridLine) : existingMajorGrid ?? "",
|
|
460
|
+
existingMinorGrid ?? "",
|
|
461
|
+
// An axis that already carries a title keeps it: writing a second one is a
|
|
462
|
+
// repair prompt, not a duplicated label.
|
|
463
|
+
existingTitle ?? (edits.title ? axisTitle(edits.title) : ""),
|
|
464
|
+
edits.numberFormat !== void 0 ? `<c:numFmt formatCode="${escapeXml(edits.numberFormat)}" sourceLinked="0"/>` : existingNumFmt ?? "",
|
|
465
|
+
existingMajorTick ?? "",
|
|
466
|
+
existingMinorTick ?? "",
|
|
467
|
+
existingTickLblPos ?? "",
|
|
468
|
+
edits.lineVisible === false ? "<c:spPr><a:ln><a:noFill/></a:ln></c:spPr>" : existingSpPr ?? "",
|
|
469
|
+
edits.labelRotation !== void 0 || hasTextStyle(edits.labelFont) ? textProperties(edits.labelRotation, edits.labelFont) : existingTxPr ?? ""
|
|
470
|
+
].join("");
|
|
471
|
+
if (edits.majorUnit !== void 0 && !tail.includes("<c:majorUnit")) {
|
|
472
|
+
const crosses = tail.match(/<c:cross(?:es|esAt|Between)[^>]*\/>/g);
|
|
473
|
+
const anchor = crosses?.[crosses.length - 1];
|
|
474
|
+
if (anchor) {
|
|
475
|
+
const at = tail.lastIndexOf(anchor) + anchor.length;
|
|
476
|
+
tail = tail.slice(0, at) + `<c:majorUnit val="${edits.majorUnit}"/>` + tail.slice(at);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
return head + rebuilt + tail;
|
|
480
|
+
}
|
|
481
|
+
function editAxis(chartXml, tag, edits, occurrence = 0) {
|
|
482
|
+
if (!edits || Object.keys(edits).length === 0) return chartXml;
|
|
483
|
+
const open = `<c:${tag}>`;
|
|
484
|
+
let start = -1;
|
|
485
|
+
for (let seen = 0; seen <= occurrence; seen++) {
|
|
486
|
+
start = chartXml.indexOf(open, start + 1);
|
|
487
|
+
if (start < 0) return chartXml;
|
|
488
|
+
}
|
|
489
|
+
const end = chartXml.indexOf(`</c:${tag}>`, start);
|
|
490
|
+
if (end < 0) return chartXml;
|
|
491
|
+
return chartXml.slice(0, start) + rewriteAxis(chartXml.slice(start, end), edits) + chartXml.slice(end);
|
|
492
|
+
}
|
|
493
|
+
function setVaryColors(chartXml, chartType) {
|
|
494
|
+
if (POINT_COLORED.has(chartType)) return chartXml;
|
|
495
|
+
if (chartXml.includes("<c:varyColors")) return chartXml;
|
|
496
|
+
return chartXml.replace("<c:ser>", '<c:varyColors val="0"/><c:ser>');
|
|
497
|
+
}
|
|
498
|
+
function setBarGrouping(chartXml, grouping) {
|
|
499
|
+
const start = chartXml.indexOf("<c:barChart>");
|
|
500
|
+
if (start < 0) {
|
|
501
|
+
return chartXml.replace(
|
|
502
|
+
/<c:grouping val="[^"]*"\/>/,
|
|
503
|
+
`<c:grouping val="${escapeXml(grouping)}"/>`
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
const end = chartXml.indexOf("</c:barChart>", start);
|
|
507
|
+
if (end < 0) return chartXml;
|
|
508
|
+
let plot = chartXml.slice(start, end).replace(
|
|
509
|
+
/<c:grouping val="[^"]*"\/>/,
|
|
510
|
+
`<c:grouping val="${escapeXml(grouping)}"/>`
|
|
511
|
+
);
|
|
512
|
+
if (!plot.includes("<c:overlap")) {
|
|
513
|
+
const gapWidth = plot.match(/<c:gapWidth val="[^"]*"\/>/)?.[0];
|
|
514
|
+
if (gapWidth) {
|
|
515
|
+
const at = plot.indexOf(gapWidth) + gapWidth.length;
|
|
516
|
+
plot = plot.slice(0, at) + '<c:overlap val="100"/>' + plot.slice(at);
|
|
517
|
+
} else {
|
|
518
|
+
const axId = plot.indexOf("<c:axId");
|
|
519
|
+
if (axId >= 0) {
|
|
520
|
+
plot = plot.slice(0, axId) + '<c:overlap val="100"/>' + plot.slice(axId);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
return chartXml.slice(0, start) + plot + chartXml.slice(end);
|
|
525
|
+
}
|
|
526
|
+
function styleChartTitle(chartXml, font) {
|
|
527
|
+
if (!hasTextStyle(font)) return chartXml;
|
|
528
|
+
const plotAreaAt = chartXml.indexOf("<c:plotArea>");
|
|
529
|
+
if (plotAreaAt < 0) return chartXml;
|
|
530
|
+
const head = chartXml.slice(0, plotAreaAt);
|
|
531
|
+
if (!head.includes("<c:title>")) return chartXml;
|
|
532
|
+
const styled = head.replace(
|
|
533
|
+
"<a:p><a:r>",
|
|
534
|
+
`<a:p><a:pPr>${defaultRunProperties(font)}</a:pPr><a:r>`
|
|
535
|
+
);
|
|
536
|
+
return styled + chartXml.slice(plotAreaAt);
|
|
537
|
+
}
|
|
538
|
+
function styleLegend(chartXml, font) {
|
|
539
|
+
if (!hasTextStyle(font)) return chartXml;
|
|
540
|
+
const start = chartXml.indexOf("<c:legend>");
|
|
541
|
+
if (start < 0) return chartXml;
|
|
542
|
+
const end = chartXml.indexOf("</c:legend>", start);
|
|
543
|
+
if (end < 0) return chartXml;
|
|
544
|
+
const legend = chartXml.slice(start, end).replace("<a:defRPr/>", defaultRunProperties(font));
|
|
545
|
+
return chartXml.slice(0, start) + legend + chartXml.slice(end);
|
|
546
|
+
}
|
|
547
|
+
function styleDataLabels(chartXml, font) {
|
|
548
|
+
if (!hasTextStyle(font)) return chartXml;
|
|
549
|
+
return chartXml.replace(
|
|
550
|
+
/<c:dLbls>(?!<c:txPr>)/g,
|
|
551
|
+
`<c:dLbls>${textProperties(void 0, font)}`
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
function spliceChartXml(chartXml, chart, relationshipId = "rId1") {
|
|
555
|
+
const pointCount = chart.series[0]?.labels.length ?? 0;
|
|
556
|
+
let seriesIndex = 0;
|
|
557
|
+
let result = chartXml.replace(/<c:ser>[\s\S]*?<\/c:ser>/g, (seriesXml) => {
|
|
558
|
+
const index = seriesIndex++;
|
|
559
|
+
const withFormulas = fillSeriesFormulas(
|
|
560
|
+
seriesXml,
|
|
561
|
+
index,
|
|
562
|
+
pointCount,
|
|
563
|
+
chart.series[index]?.values.length ?? pointCount
|
|
564
|
+
);
|
|
565
|
+
const color = chart.colors.length > 0 ? chart.colors[index % chart.colors.length] : void 0;
|
|
566
|
+
return paintSeries(
|
|
567
|
+
withFormulas,
|
|
568
|
+
color,
|
|
569
|
+
chart.chartType,
|
|
570
|
+
chart.colors,
|
|
571
|
+
pointCount,
|
|
572
|
+
chart
|
|
573
|
+
);
|
|
574
|
+
});
|
|
575
|
+
if (chart.chartType === "scatter") {
|
|
576
|
+
result = editAxis(result, "valAx", chart.categoryAxis, 0);
|
|
577
|
+
result = editAxis(result, "valAx", chart.valueAxis, 1);
|
|
578
|
+
} else {
|
|
579
|
+
result = editAxis(result, "catAx", chart.categoryAxis);
|
|
580
|
+
result = editAxis(result, "valAx", chart.valueAxis);
|
|
581
|
+
}
|
|
582
|
+
result = styleChartTitle(result, chart.titleFont);
|
|
583
|
+
result = styleLegend(result, chart.legendFont);
|
|
584
|
+
result = styleDataLabels(result, chart.dataLabelFont);
|
|
585
|
+
if (chart.radarStyle) {
|
|
586
|
+
result = result.replace(
|
|
587
|
+
/<c:radarStyle val="[^"]*"\/>/,
|
|
588
|
+
`<c:radarStyle val="${escapeXml(chart.radarStyle)}"/>`
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
if (chart.legendPosition) {
|
|
592
|
+
result = result.replace(
|
|
593
|
+
/<c:legendPos val="[^"]*"\/>/,
|
|
594
|
+
`<c:legendPos val="${escapeXml(chart.legendPosition)}"/>`
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
if (chart.barGrouping && chart.barGrouping !== "clustered") {
|
|
598
|
+
result = setBarGrouping(result, chart.barGrouping);
|
|
599
|
+
}
|
|
600
|
+
result = setVaryColors(result, chart.chartType);
|
|
601
|
+
if (result.includes("<c:externalData")) return result;
|
|
602
|
+
return result.replace(
|
|
603
|
+
"</c:chartSpace>",
|
|
604
|
+
`<c:externalData r:id="${escapeXml(relationshipId)}"><c:autoUpdate val="0"/></c:externalData></c:chartSpace>`
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
var VALUE_SEPARATOR = "";
|
|
608
|
+
var SERIES_SEPARATOR = "";
|
|
609
|
+
var NAMED_ENTITIES = {
|
|
610
|
+
amp: "&",
|
|
611
|
+
lt: "<",
|
|
612
|
+
gt: ">",
|
|
613
|
+
quot: '"',
|
|
614
|
+
apos: "'"
|
|
615
|
+
};
|
|
616
|
+
function decodeXmlEntities(value) {
|
|
617
|
+
return value.replace(
|
|
618
|
+
/&(#x[0-9a-fA-F]+|#[0-9]+|[a-zA-Z]+);/g,
|
|
619
|
+
(match, body) => {
|
|
620
|
+
if (body.startsWith("#x") || body.startsWith("#X")) {
|
|
621
|
+
return String.fromCodePoint(Number.parseInt(body.slice(2), 16));
|
|
622
|
+
}
|
|
623
|
+
if (body.startsWith("#")) {
|
|
624
|
+
return String.fromCodePoint(Number.parseInt(body.slice(1), 10));
|
|
625
|
+
}
|
|
626
|
+
return NAMED_ENTITIES[body] ?? match;
|
|
627
|
+
}
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
function chartPartSignature(chartXml) {
|
|
631
|
+
return (chartXml.match(/<c:ser>[\s\S]*?<\/c:ser>/g) ?? []).map(
|
|
632
|
+
(series) => [...series.matchAll(/<c:v>([\s\S]*?)<\/c:v>/g)].map((match) => decodeXmlEntities(match[1])).join(VALUE_SEPARATOR)
|
|
633
|
+
).join(SERIES_SEPARATOR);
|
|
634
|
+
}
|
|
635
|
+
function chartInputSignature(chart) {
|
|
636
|
+
const categories = chart.series[0]?.labels ?? [];
|
|
637
|
+
return chart.series.map(
|
|
638
|
+
(series, index) => [
|
|
639
|
+
series.name ?? `Series ${index + 1}`,
|
|
640
|
+
...categories,
|
|
641
|
+
...series.values.map((value) => String(value))
|
|
642
|
+
].join(VALUE_SEPARATOR)
|
|
643
|
+
).join(SERIES_SEPARATOR);
|
|
644
|
+
}
|
|
645
|
+
function matchChartParts(parts, charts) {
|
|
646
|
+
const unmatched = new Set(charts.keys());
|
|
647
|
+
const matched = [];
|
|
648
|
+
for (const [ordinal, xml] of parts) {
|
|
649
|
+
const signature = chartPartSignature(xml);
|
|
650
|
+
const index = [...unmatched].find(
|
|
651
|
+
(candidate) => chartInputSignature(charts[candidate]) === signature
|
|
652
|
+
);
|
|
653
|
+
if (index === void 0) continue;
|
|
654
|
+
unmatched.delete(index);
|
|
655
|
+
matched.push({ ordinal, xml, chart: charts[index] });
|
|
656
|
+
}
|
|
657
|
+
if (unmatched.size > 0) {
|
|
658
|
+
const names = [...unmatched].map((index) => charts[index].series[0]?.name ?? `chart ${index + 1}`).join(", ");
|
|
659
|
+
throw new Error(
|
|
660
|
+
`Could not match ${unmatched.size} chart(s) to an emitted chart part (${names}). The package would ship a chart without its workbook.`
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
return matched;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
export {
|
|
667
|
+
assertNever,
|
|
668
|
+
UnsupportedRendererFeatureError,
|
|
669
|
+
UnknownRendererError,
|
|
670
|
+
rendererError,
|
|
671
|
+
rendererWarning,
|
|
672
|
+
partitionDiagnostics,
|
|
673
|
+
FeatureRequirementCollector,
|
|
674
|
+
diagnoseUnsupportedFeatures,
|
|
675
|
+
assertRendererSupports,
|
|
676
|
+
RendererRegistry,
|
|
677
|
+
CHART_WORKBOOK_SHEET_NAME,
|
|
678
|
+
CHART_PACKAGE_RELATIONSHIP,
|
|
679
|
+
CHART_WORKBOOK_CONTENT_TYPE,
|
|
680
|
+
columnLetter,
|
|
681
|
+
chartWorkbookParts,
|
|
682
|
+
seriesValueReference,
|
|
683
|
+
categoryReference,
|
|
684
|
+
seriesNameReference,
|
|
685
|
+
chartWorkbookRelsXml,
|
|
686
|
+
spliceChartXml,
|
|
687
|
+
chartPartSignature,
|
|
688
|
+
chartInputSignature,
|
|
689
|
+
matchChartParts
|
|
690
|
+
};
|
|
691
|
+
//# sourceMappingURL=chunk-BJNBJSQG.js.map
|