@a3s-lab/office 0.31.0 → 0.33.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.
Files changed (63) hide show
  1. package/README.md +63 -8
  2. package/dist/0~3539.js +2 -2
  3. package/dist/0~3557.js +1 -1
  4. package/dist/0~3635.js +1 -1
  5. package/dist/0~4808.js +1 -1
  6. package/dist/0~4980.js +1 -1
  7. package/dist/0~7048.js +1 -2
  8. package/dist/0~7240.js +1 -1
  9. package/dist/0~7614.js +1 -1
  10. package/dist/{0~6090.js → 0~8136.js} +71 -2
  11. package/dist/{0~4595.js → 0~9073.js} +3 -72
  12. package/dist/0~9445.js +1 -1
  13. package/dist/0~document-editor.js +1087 -6
  14. package/dist/0~markdown-editor.js +2 -2
  15. package/dist/0~pdf-page-organization-engine.js +187 -0
  16. package/dist/0~pdf-viewer.js +1014 -30
  17. package/dist/0~presentation-editor.js +2 -2
  18. package/dist/0~spreadsheet-editor.js +4 -3
  19. package/dist/0~work-docx-export.js +1 -1
  20. package/dist/0~work-docx-import.js +10 -27
  21. package/dist/0~work-docx-large-document-import.js +1 -1
  22. package/dist/0~work-office-diagnostics.js +3 -3
  23. package/dist/0~work-pptx-import.js +1 -1
  24. package/dist/{1544.js → 3266.js} +17 -1862
  25. package/dist/4121.js +13 -0
  26. package/dist/4166.js +3848 -0
  27. package/dist/{6282.js → 4174.js} +383 -10
  28. package/dist/9333.js +1849 -0
  29. package/dist/core.d.ts +1 -0
  30. package/dist/core.js +5259 -4
  31. package/dist/index.js +3 -3
  32. package/dist/internal/features/work/editors/document-compare-dialog.d.ts +14 -0
  33. package/dist/internal/features/work/editors/document-toolbar.d.ts +3 -1
  34. package/dist/internal/features/work/editors/pdf-editor-extensions.d.ts +11 -0
  35. package/dist/internal/features/work/editors/pdf-page-organization-engine.d.ts +2 -0
  36. package/dist/internal/features/work/editors/pdf-page-organization-plan.d.ts +10 -0
  37. package/dist/internal/features/work/editors/pdf-page-organization-types.d.ts +68 -0
  38. package/dist/internal/features/work/editors/pdf-page-organization-worker-client.d.ts +2 -0
  39. package/dist/internal/features/work/editors/pdf-page-organization-worker-protocol.d.ts +13 -0
  40. package/dist/internal/features/work/editors/pdf-page-organization.d.ts +3 -0
  41. package/dist/internal/features/work/editors/pdf-page-organization.worker.d.ts +1 -0
  42. package/dist/internal/features/work/editors/pdf-page-organizer-dialog.d.ts +17 -0
  43. package/dist/internal/features/work/editors/pdf-thumbnail-rail.d.ts +4 -0
  44. package/dist/internal/features/work/editors/pdf-toolbar.d.ts +2 -1
  45. package/dist/internal/features/work/editors/pdf-viewer.d.ts +3 -1
  46. package/dist/internal/features/work/editors/use-document-comparison.d.ts +9 -0
  47. package/dist/internal/features/work/editors/use-pdf-page-organization.d.ts +38 -0
  48. package/dist/internal/features/work/work-document-changes.d.ts +5 -0
  49. package/dist/internal/features/work/work-document-compare-diff.d.ts +38 -0
  50. package/dist/internal/features/work/work-document-compare-stability.d.ts +5 -0
  51. package/dist/internal/features/work/work-document-compare.d.ts +27 -0
  52. package/dist/office-kernel.wasm +0 -0
  53. package/dist/pdf-page-organization.worker.js +19667 -0
  54. package/dist/pdf-page-organization.worker.js.LICENSE.txt +14 -0
  55. package/dist/react.d.ts +1 -0
  56. package/dist/styles.css +509 -0
  57. package/dist/vue.d.ts +3 -1
  58. package/dist/vue.js +2 -0
  59. package/dist/web-component.d.ts +3 -1
  60. package/dist/web-component.js +9 -0
  61. package/docs/latest/en/browser-editor-architecture.md +52 -6
  62. package/package.json +10 -3
  63. package/dist/4104.js +0 -9336
package/dist/4166.js ADDED
@@ -0,0 +1,3848 @@
1
+ import jszip from "jszip";
2
+ import { normalizeWorkSpreadsheetCombinationSeriesType, workSpreadsheetChartSupportsGrouping, normalizeWorkSpreadsheetChartColor, readXlsxDrawingAnchor, spreadsheetCellAddress, formatSpreadsheetCellRanges, normalizeWorkSpreadsheetChartSmoothLines, spreadsheetFormulaRangeForCell, sparseArrayEntries, spreadsheetPivotFilterValueKey, normalizeWorkSpreadsheetBubbleScale, normalizeWorkSpreadsheetChartSeriesStyle, normalizeWorkSpreadsheetErrorBars, normalizeWorkSpreadsheetBubbleSizeRepresents, normalizeWorkSpreadsheetChartLegendOverlay, normalizeWorkSpreadsheetChartGapWidth, workSpreadsheetChartSupportsTrendlines, normalizeWorkSpreadsheetChartGrouping, workSpreadsheetChartSupportsBarSpacing, workSpreadsheetChartSupportsSmoothLines, workSpreadsheetChartGroupingIsStacked, xlsxTwoCellAnchorMarkers, xlsxDrawingAnchorToBounds, workSpreadsheetChartSupportsErrorBars, normalizeWorkSpreadsheetChartAxisGroup, normalizeWorkSpreadsheetScatterStyle, workSpreadsheetChartAxisDefaultLabelPosition, spreadsheetPivotValidation, resolveSpreadsheetChart, workSpreadsheetChartAxisIsCategoryAxis, parseSpreadsheetCellRanges, spreadsheetPivotFields, normalizeWorkSpreadsheetDoughnutHoleSize, normalizeWorkSpreadsheetChartAxes, normalizeWorkSpreadsheetChartOverlap, editableSpreadsheetFormula, spreadsheetFormulaRangeConflict, spreadsheetFormulaForXlsx, workSpreadsheetChartUsesNumericXAxis, normalizeWorkSpreadsheetChartLegendPosition, workSpreadsheetChartSupportsAxes, workSpreadsheetChartAxisShowsMajorGridlinesByDefault, effectiveSpreadsheetCalculationSettings, normalizeWorkSpreadsheetTrendline, normalizeWorkSpreadsheetRadarStyle, normalizeWorkSpreadsheetDataLabels } from "./8715.js";
3
+ import { contentTypeForPart, directChildren, bytesToDataUrl, OoxmlPackage as work_ooxml_package_OoxmlPackage, parseXml, firstDescendant, xmlNamespacePrefix, descendants, directChild, xmlContainsAnyElement, attribute, createOfficeId as createWorkId } from "./4121.js";
4
+ import { DEFAULT_PROTECTION_HINT, editableRangeRequiresCredentials, normalizeSpreadsheetPaperSize, normalizeSheetProtectionAuthority, xlsxWorksheetCellEntries } from "./9333.js";
5
+ async function readXlsxFormulaFeaturesFromPackage(archive, worksheetScans) {
6
+ if (!archive.has('xl/workbook.xml')) return {
7
+ calculation: effectiveSpreadsheetCalculationSettings(void 0),
8
+ sheets: new Map(),
9
+ unsupportedCalculationAttributes: []
10
+ };
11
+ const workbook = await archive.xml('xl/workbook.xml');
12
+ const worksheetParts = await readWorksheetParts(archive, workbook);
13
+ const dynamicMetadata = await readDynamicArrayMetadata(archive);
14
+ const sheets = new Map();
15
+ for (const [sheetName, partPath] of worksheetParts){
16
+ if (!archive.has(partPath)) continue;
17
+ const scan = worksheetScans?.[partPath];
18
+ if (scan && !scan.hasFormulaFeatures) {
19
+ sheets.set(sheetName, emptyXlsxFormulaSheetFeatures());
20
+ continue;
21
+ }
22
+ const source = await archive.text(partPath);
23
+ if (!scan && !xmlContainsAnyElement(source, [
24
+ 'f'
25
+ ])) {
26
+ sheets.set(sheetName, emptyXlsxFormulaSheetFeatures());
27
+ continue;
28
+ }
29
+ sheets.set(sheetName, readWorksheetFormulaFeatures(parseXml(source, partPath), dynamicMetadata));
30
+ }
31
+ return {
32
+ calculation: readCalculationSettings(workbook),
33
+ sheets,
34
+ unsupportedCalculationAttributes: readUnsupportedCalculationAttributes(workbook)
35
+ };
36
+ }
37
+ function emptyXlsxFormulaSheetFeatures() {
38
+ return {
39
+ formulas: [],
40
+ ranges: [],
41
+ sourceFormulas: {},
42
+ sharedFormulaGroups: 0,
43
+ sharedFormulaCells: 0,
44
+ unsupportedFormulaAttributes: []
45
+ };
46
+ }
47
+ function createSpreadsheetFormulaMetadata(worksheet, features) {
48
+ const sourceFormulas = {};
49
+ const rangeByReference = new Map((features?.ranges ?? []).map((range)=>[
50
+ range.reference.toUpperCase().replaceAll('$', ''),
51
+ range
52
+ ]));
53
+ for (const { address, cell } of xlsxWorksheetCellEntries(worksheet)){
54
+ const formulaCell = cell;
55
+ if (formulaCell.f) {
56
+ const source = features?.sourceFormulas[address.toUpperCase()] ?? formulaCell.f;
57
+ if (editableSpreadsheetFormula(source) !== source) sourceFormulas[address.toUpperCase()] = source;
58
+ }
59
+ if (!formulaCell.f || !formulaCell.F) continue;
60
+ const reference = formulaCell.F.toUpperCase().replaceAll('$', '');
61
+ const existing = rangeByReference.get(reference);
62
+ if (existing) {
63
+ if (formulaCell.D && 'array' === existing.type) rangeByReference.set(reference, {
64
+ ...existing,
65
+ type: 'dynamic-array'
66
+ });
67
+ continue;
68
+ }
69
+ rangeByReference.set(reference, {
70
+ type: formulaCell.D ? 'dynamic-array' : 'array',
71
+ anchor: address.toUpperCase(),
72
+ reference: formulaCell.F,
73
+ formula: features?.sourceFormulas[address.toUpperCase()] ?? formulaCell.f
74
+ });
75
+ }
76
+ const ranges = Array.from(rangeByReference.values());
77
+ const metadata = {
78
+ ranges: ranges.length ? ranges : void 0,
79
+ sourceFormulas: Object.keys(sourceFormulas).length ? sourceFormulas : void 0,
80
+ normalizedSharedFormulaGroups: features?.sharedFormulaGroups || void 0,
81
+ normalizedSharedFormulaCells: features?.sharedFormulaCells || void 0
82
+ };
83
+ return Object.values(metadata).some((value)=>void 0 !== value) ? metadata : void 0;
84
+ }
85
+ function createXlsxFormulaCell(cell, row, column, sheet) {
86
+ const address = spreadsheetCellAddress(row, column);
87
+ const range = spreadsheetFormulaRangeForCell(sheet, row, column);
88
+ const sourceFormula = sheet.formulaMetadata?.sourceFormulas?.[address] ?? range?.formula;
89
+ const result = {
90
+ f: spreadsheetFormulaForXlsx(cell.f ?? '', sourceFormula),
91
+ v: cell.ct?.t === 'e' ? xlsxErrorValue(cell.v, cell.m) : cell.v
92
+ };
93
+ if (cell.ct?.t) result.t = cell.ct.t;
94
+ if (range && 'data-table' !== range.type && !spreadsheetFormulaRangeConflict(sheet, range)) {
95
+ result.F = range.reference;
96
+ if ('dynamic-array' === range.type) result.D = true;
97
+ }
98
+ return result;
99
+ }
100
+ function createXlsxErrorCell(cell) {
101
+ return {
102
+ t: 'e',
103
+ v: xlsxErrorValue(cell.v, cell.m)
104
+ };
105
+ }
106
+ async function patchXlsxFormulaFeatures(buffer, content) {
107
+ const archive = await work_ooxml_package_OoxmlPackage.load(buffer);
108
+ const zip = await jszip.loadAsync(buffer);
109
+ if (archive.has('xl/workbook.xml')) {
110
+ const entry = zip.file('xl/workbook.xml');
111
+ if (entry) {
112
+ const workbook = parseXml(await entry.async('text'), 'xl/workbook.xml');
113
+ writeCalculationSettings(workbook, content.calculation);
114
+ zip.file('xl/workbook.xml', new XMLSerializer().serializeToString(workbook));
115
+ }
116
+ }
117
+ const dataTables = content.sheets.flatMap((sheet)=>(sheet.formulaMetadata?.ranges ?? []).flatMap((range)=>'data-table' !== range.type || spreadsheetFormulaRangeConflict(sheet, range) ? [] : [
118
+ {
119
+ sheet,
120
+ range
121
+ }
122
+ ]));
123
+ if (!dataTables.length || !archive.has('xl/workbook.xml')) return zip.generateAsync({
124
+ type: 'arraybuffer',
125
+ compression: 'DEFLATE'
126
+ });
127
+ const workbook = await archive.xml('xl/workbook.xml');
128
+ const worksheetParts = await readWorksheetParts(archive, workbook);
129
+ for (const sheet of content.sheets){
130
+ const ranges = dataTables.filter((item)=>item.sheet === sheet).map((item)=>item.range);
131
+ if (!ranges.length) continue;
132
+ const partPath = worksheetParts.get(sheet.name.slice(0, 31) || '工作表');
133
+ const entry = partPath ? zip.file(partPath) : null;
134
+ if (!partPath || !entry) continue;
135
+ const document = parseXml(await entry.async('text'), partPath);
136
+ for (const range of ranges)writeDataTableFormula(document, range);
137
+ zip.file(partPath, new XMLSerializer().serializeToString(document));
138
+ }
139
+ return zip.generateAsync({
140
+ type: 'arraybuffer',
141
+ compression: 'DEFLATE'
142
+ });
143
+ }
144
+ function readWorksheetFormulaFeatures(document, dynamicMetadataIndexes) {
145
+ const formulas = [];
146
+ const ranges = [];
147
+ const sourceFormulas = {};
148
+ const sharedGroups = new Set();
149
+ const unsupportedFormulaAttributes = new Set();
150
+ let sharedFormulaCells = 0;
151
+ for (const cell of descendants(document, 'c')){
152
+ const formula = directChild(cell, 'f');
153
+ if (!formula) continue;
154
+ const address = (attribute(cell, 'r') ?? '').toUpperCase();
155
+ if (!address) continue;
156
+ const type = attribute(formula, 't') ?? 'normal';
157
+ for (const name of unsupportedFormulaAttributesForElement(formula, type))unsupportedFormulaAttributes.add(name);
158
+ const source = formula.textContent ?? '';
159
+ if (source) sourceFormulas[address] = source;
160
+ const cachedError = 'e' === attribute(cell, 't') ? directChild(cell, 'v')?.textContent ?? '' : void 0;
161
+ formulas.push({
162
+ address,
163
+ formula: source,
164
+ type,
165
+ cachedError
166
+ });
167
+ if ('shared' === type) {
168
+ sharedFormulaCells += 1;
169
+ sharedGroups.add(attribute(formula, 'si') ?? `cell:${address}`);
170
+ continue;
171
+ }
172
+ const reference = attribute(formula, 'ref');
173
+ if ('array' === type && reference) {
174
+ const metadataIndex = positiveInteger(attribute(cell, 'cm'));
175
+ ranges.push({
176
+ type: metadataIndex && dynamicMetadataIndexes.has(metadataIndex) ? 'dynamic-array' : 'array',
177
+ anchor: address,
178
+ reference,
179
+ formula: source || void 0
180
+ });
181
+ } else if ('dataTable' === type) ranges.push({
182
+ type: 'data-table',
183
+ anchor: address,
184
+ reference: reference || address,
185
+ formula: source || void 0,
186
+ dataTable: readDataTableOptions(formula)
187
+ });
188
+ }
189
+ return {
190
+ formulas,
191
+ ranges,
192
+ sourceFormulas,
193
+ sharedFormulaGroups: sharedGroups.size,
194
+ sharedFormulaCells,
195
+ unsupportedFormulaAttributes: Array.from(unsupportedFormulaAttributes).sort()
196
+ };
197
+ }
198
+ function readCalculationSettings(workbook) {
199
+ const calculation = directChild(workbook.documentElement, 'calcPr');
200
+ return effectiveSpreadsheetCalculationSettings({
201
+ mode: calculationMode(attribute(calculation ?? workbook.documentElement, 'calcMode')),
202
+ fullCalculationOnLoad: booleanAttribute(calculation, 'fullCalcOnLoad', false),
203
+ forceFullCalculation: booleanAttribute(calculation, 'forceFullCalc', false),
204
+ iterativeCalculation: booleanAttribute(calculation, 'iterate', false),
205
+ maximumIterations: numericAttribute(calculation, 'iterateCount', 100),
206
+ maximumChange: numericAttribute(calculation, 'iterateDelta', 0.001),
207
+ fullPrecision: booleanAttribute(calculation, 'fullPrecision', true)
208
+ });
209
+ }
210
+ function readUnsupportedCalculationAttributes(workbook) {
211
+ const calculation = directChild(workbook.documentElement, 'calcPr');
212
+ if (!calculation) return [];
213
+ const supported = new Set([
214
+ 'calcId',
215
+ 'calcMode',
216
+ 'forceFullCalc',
217
+ 'fullCalcOnLoad',
218
+ 'fullPrecision',
219
+ 'iterate',
220
+ 'iterateCount',
221
+ 'iterateDelta'
222
+ ]);
223
+ return Array.from(calculation.attributes).map((item)=>item.localName).filter((name)=>!supported.has(name)).sort();
224
+ }
225
+ function writeCalculationSettings(workbook, source) {
226
+ const settings = effectiveSpreadsheetCalculationSettings(source);
227
+ const root = workbook.documentElement;
228
+ let calculation = directChild(root, 'calcPr');
229
+ if (!calculation) {
230
+ calculation = workbook.createElementNS(root.namespaceURI, 'calcPr');
231
+ root.insertBefore(calculation, directChild(root, 'extLst') ?? null);
232
+ }
233
+ calculation.setAttribute('calcMode', xlsxCalculationMode(settings.mode));
234
+ calculation.setAttribute('fullCalcOnLoad', booleanValue(settings.fullCalculationOnLoad));
235
+ calculation.setAttribute('forceFullCalc', booleanValue(settings.forceFullCalculation));
236
+ calculation.setAttribute('iterate', booleanValue(settings.iterativeCalculation));
237
+ calculation.setAttribute('iterateCount', String(settings.maximumIterations));
238
+ calculation.setAttribute('iterateDelta', String(settings.maximumChange));
239
+ calculation.setAttribute('fullPrecision', booleanValue(settings.fullPrecision));
240
+ }
241
+ async function readWorksheetParts(archive, workbook) {
242
+ const relationships = await archive.relationships('xl/workbook.xml');
243
+ const parts = new Map();
244
+ for (const sheet of firstDescendant(workbook, 'sheets')?.children ?? []){
245
+ if (!(sheet instanceof Element) || 'sheet' !== sheet.localName) continue;
246
+ const name = attribute(sheet, 'name');
247
+ const relationship = relationships.get(attribute(sheet, 'r:id') ?? '');
248
+ if (name && relationship?.type.endsWith('/worksheet')) parts.set(name, relationship.target);
249
+ }
250
+ return parts;
251
+ }
252
+ async function readDynamicArrayMetadata(archive) {
253
+ if (!archive.has('xl/metadata.xml')) return new Set();
254
+ const metadata = await archive.xml('xl/metadata.xml');
255
+ const metadataTypes = directChildren(firstDescendant(metadata, 'metadataTypes') ?? metadata.documentElement);
256
+ const dynamicTypeIndexes = new Set(metadataTypes.flatMap((item, index)=>'metadataType' === item.localName && attribute(item, 'name')?.toUpperCase() === 'XLDAPR' ? [
257
+ index + 1
258
+ ] : []));
259
+ if (!dynamicTypeIndexes.size) return new Set();
260
+ const cellMetadata = firstDescendant(metadata, 'cellMetadata');
261
+ if (!cellMetadata) return new Set();
262
+ return new Set(directChildren(cellMetadata, 'bk').flatMap((block, index)=>directChildren(block, 'rc').some((record)=>dynamicTypeIndexes.has(positiveInteger(attribute(record, 't')) ?? -1)) ? [
263
+ index + 1
264
+ ] : []));
265
+ }
266
+ function readDataTableOptions(formula) {
267
+ const options = {
268
+ input1Reference: attribute(formula, 'r1') ?? void 0,
269
+ input2Reference: attribute(formula, 'r2') ?? void 0,
270
+ twoDimensional: optionalBooleanAttribute(formula, 'dt2D'),
271
+ rowOriented: optionalBooleanAttribute(formula, 'dtr'),
272
+ input1Deleted: optionalBooleanAttribute(formula, 'del1'),
273
+ input2Deleted: optionalBooleanAttribute(formula, 'del2'),
274
+ calculateOnLoad: optionalBooleanAttribute(formula, 'ca')
275
+ };
276
+ return Object.values(options).some((value)=>void 0 !== value) ? options : void 0;
277
+ }
278
+ function unsupportedFormulaAttributesForElement(formula, type) {
279
+ const common = [
280
+ 't'
281
+ ];
282
+ const supported = 'shared' === type ? new Set([
283
+ ...common,
284
+ 'ref',
285
+ 'si'
286
+ ]) : 'array' === type ? new Set([
287
+ ...common,
288
+ 'ref'
289
+ ]) : 'dataTable' === type ? new Set([
290
+ ...common,
291
+ 'ca',
292
+ 'del1',
293
+ 'del2',
294
+ 'dt2D',
295
+ 'dtr',
296
+ 'r1',
297
+ 'r2',
298
+ 'ref'
299
+ ]) : new Set(common);
300
+ return Array.from(formula.attributes).map((item)=>item.localName).filter((name)=>!supported.has(name));
301
+ }
302
+ function writeDataTableFormula(document, range) {
303
+ const anchor = range.anchor.toUpperCase().replaceAll('$', '');
304
+ const cell = descendants(document, 'c').find((candidate)=>(attribute(candidate, 'r') ?? '').toUpperCase().replaceAll('$', '') === anchor);
305
+ if (!cell) return;
306
+ let formula = directChild(cell, 'f');
307
+ if (!formula) {
308
+ formula = document.createElementNS(document.documentElement.namespaceURI, 'f');
309
+ cell.insertBefore(formula, directChild(cell, 'v') ?? cell.firstChild);
310
+ }
311
+ for (const item of Array.from(formula.attributes))formula.removeAttribute(item.name);
312
+ formula.setAttribute('t', 'dataTable');
313
+ formula.setAttribute('ref', range.reference);
314
+ formula.textContent = range.formula ?? '';
315
+ const options = range.dataTable;
316
+ if (!options) return;
317
+ setOptionalAttribute(formula, 'r1', options.input1Reference);
318
+ setOptionalAttribute(formula, 'r2', options.input2Reference);
319
+ setOptionalBooleanAttribute(formula, 'dt2D', options.twoDimensional);
320
+ setOptionalBooleanAttribute(formula, 'dtr', options.rowOriented);
321
+ setOptionalBooleanAttribute(formula, 'del1', options.input1Deleted);
322
+ setOptionalBooleanAttribute(formula, 'del2', options.input2Deleted);
323
+ setOptionalBooleanAttribute(formula, 'ca', options.calculateOnLoad);
324
+ }
325
+ function calculationMode(value) {
326
+ if ('manual' === value) return 'manual';
327
+ if ('autoNoTable' === value) return 'automatic-except-data-tables';
328
+ return 'automatic';
329
+ }
330
+ function xlsxCalculationMode(mode) {
331
+ if ('manual' === mode) return 'manual';
332
+ if ('automatic-except-data-tables' === mode) return 'autoNoTable';
333
+ return 'auto';
334
+ }
335
+ function booleanAttribute(element, name, fallback) {
336
+ const value = element ? attribute(element, name)?.toLowerCase() : null;
337
+ if (null == value) return fallback;
338
+ return '1' === value || 'true' === value;
339
+ }
340
+ function optionalBooleanAttribute(element, name) {
341
+ return null === attribute(element, name) ? void 0 : booleanAttribute(element, name, false);
342
+ }
343
+ function numericAttribute(element, name, fallback) {
344
+ const value = Number(element ? attribute(element, name) : null);
345
+ return Number.isFinite(value) && value > 0 ? value : fallback;
346
+ }
347
+ function positiveInteger(value) {
348
+ const parsed = Number(value);
349
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : null;
350
+ }
351
+ function booleanValue(value) {
352
+ return value ? '1' : '0';
353
+ }
354
+ function setOptionalAttribute(element, name, value) {
355
+ if (void 0 !== value) element.setAttribute(name, value);
356
+ }
357
+ function setOptionalBooleanAttribute(element, name, value) {
358
+ if (void 0 !== value) element.setAttribute(name, booleanValue(value));
359
+ }
360
+ function xlsxErrorValue(value, formatted) {
361
+ if ('number' == typeof value) return value;
362
+ const codes = {
363
+ '#NULL!': 0,
364
+ '#DIV/0!': 7,
365
+ '#VALUE!': 15,
366
+ '#REF!': 23,
367
+ '#NAME?': 29,
368
+ '#NUM!': 36,
369
+ '#N/A': 42,
370
+ '#GETTING_DATA': 43
371
+ };
372
+ return codes[String(formatted ?? value ?? '').toUpperCase()] ?? value;
373
+ }
374
+ function cachedText(parent) {
375
+ const directValue = directChild(parent, 'v')?.textContent?.trim();
376
+ return directValue || cachedValues(parent)[0] || '';
377
+ }
378
+ function richText(parent) {
379
+ return descendants(parent, 't').map((node)=>node.textContent ?? '').join('');
380
+ }
381
+ function cachedValues(parent) {
382
+ const cache = firstDescendant(parent, 'strCache') ?? firstDescendant(parent, 'numCache') ?? firstDescendant(parent, 'strLit') ?? firstDescendant(parent, 'numLit');
383
+ if (!cache) return [];
384
+ const indexed = descendants(cache, 'pt').map((point, order)=>({
385
+ index: finiteNumber(attribute(point, 'idx'), order),
386
+ value: directChild(point, 'v')?.textContent ?? ''
387
+ })).sort((left, right)=>left.index - right.index);
388
+ if (!indexed.length) return [];
389
+ const values = Array(indexed.at(-1).index + 1).fill('');
390
+ for (const point of indexed)values[point.index] = point.value;
391
+ return values;
392
+ }
393
+ function formulaReference(parent) {
394
+ return firstDescendant(parent, 'f')?.textContent?.trim().replace(/^=/, '') || void 0;
395
+ }
396
+ function stringCacheXml(values) {
397
+ return [
398
+ '<c:strCache>',
399
+ `<c:ptCount val="${values.length}"/>`,
400
+ ...values.map((value, index)=>`<c:pt idx="${index}"><c:v>${escapeXml(String(value))}</c:v></c:pt>`),
401
+ '</c:strCache>'
402
+ ].join('');
403
+ }
404
+ function numberCacheXml(values) {
405
+ return [
406
+ '<c:numCache><c:formatCode>General</c:formatCode>',
407
+ `<c:ptCount val="${values.length}"/>`,
408
+ ...values.map((value, index)=>`<c:pt idx="${index}"><c:v>${finiteNumber(value)}</c:v></c:pt>`),
409
+ '</c:numCache>'
410
+ ].join('');
411
+ }
412
+ function stringLiteralXml(values) {
413
+ return [
414
+ '<c:strLit>',
415
+ `<c:ptCount val="${values.length}"/>`,
416
+ ...values.map((value, index)=>`<c:pt idx="${index}"><c:v>${escapeXml(String(value))}</c:v></c:pt>`),
417
+ '</c:strLit>'
418
+ ].join('');
419
+ }
420
+ function numberLiteralXml(values) {
421
+ return [
422
+ '<c:numLit><c:formatCode>General</c:formatCode>',
423
+ `<c:ptCount val="${values.length}"/>`,
424
+ ...values.map((value, index)=>`<c:pt idx="${index}"><c:v>${finiteNumber(value)}</c:v></c:pt>`),
425
+ '</c:numLit>'
426
+ ].join('');
427
+ }
428
+ function finiteNumber(value, fallback = 0) {
429
+ const number = Number(value);
430
+ return Number.isFinite(number) ? number : fallback;
431
+ }
432
+ function escapeXml(value) {
433
+ return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&apos;');
434
+ }
435
+ function parseXlsxChartAxes(document, chartType, hasSecondaryAxes, preserveVisibleDefaultGridlines = false) {
436
+ if (!workSpreadsheetChartSupportsAxes(chartType)) return;
437
+ const plotArea = firstDescendant(document, 'plotArea');
438
+ if (!plotArea) return;
439
+ const axes = {};
440
+ for (const axis of directChildren(plotArea).filter((node)=>'catAx' === node.localName || 'valAx' === node.localName || 'dateAx' === node.localName)){
441
+ const position = attribute(directChild(axis, 'axPos') ?? axis, 'val');
442
+ if ('b' !== position && 'l' !== position && 't' !== position && 'r' !== position) continue;
443
+ const key = 'b' === position ? 'bottom' : 'l' === position ? 'left' : 't' === position ? 'top' : 'right';
444
+ const title = directChild(axis, 'title');
445
+ const text = title ? cachedText(title) || richText(title) || void 0 : void 0;
446
+ const titleReference = title ? formulaReference(title) : void 0;
447
+ const displaySettings = parseXlsxAxisDisplaySettings(axis, chartType, key);
448
+ const valueSettings = 'valAx' === axis.localName ? parseXlsxValueAxisSettings(axis, chartType, key, preserveVisibleDefaultGridlines) : void 0;
449
+ if (text || titleReference || displaySettings || valueSettings) axes[key] = {
450
+ ...text ? {
451
+ title: text
452
+ } : {},
453
+ ...titleReference ? {
454
+ titleReference
455
+ } : {},
456
+ ...displaySettings,
457
+ ...valueSettings
458
+ };
459
+ }
460
+ return normalizeWorkSpreadsheetChartAxes(axes, chartType, hasSecondaryAxes);
461
+ }
462
+ function parseXlsxAxisDisplaySettings(axis, chartType, position) {
463
+ const orientation = attribute(directChild(directChild(axis, 'scaling') ?? axis, 'orientation') ?? axis, 'val');
464
+ const rawLabelPosition = attribute(directChild(axis, 'tickLblPos') ?? axis, 'val');
465
+ const labelPosition = 'nextTo' === rawLabelPosition || 'high' === rawLabelPosition || 'low' === rawLabelPosition || 'none' === rawLabelPosition ? rawLabelPosition : void 0;
466
+ const rawTickMark = attribute(directChild(axis, 'majorTickMark') ?? axis, 'val');
467
+ const majorTickMark = 'in' === rawTickMark ? 'inside' : 'out' === rawTickMark ? 'outside' : 'cross' === rawTickMark ? 'cross' : 'none' === rawTickMark ? 'none' : void 0;
468
+ const rawInterval = workSpreadsheetChartAxisIsCategoryAxis(chartType, position) ? finiteAttributeValue(directChild(axis, 'tickLblSkip')) : void 0;
469
+ const labelInterval = Number.isInteger(rawInterval) && Number(rawInterval) >= 1 && Number(rawInterval) <= 31999 ? Number(rawInterval) : void 0;
470
+ const defaultLabelPosition = workSpreadsheetChartAxisDefaultLabelPosition(chartType, position);
471
+ const settings = {
472
+ ...'maxMin' === orientation ? {
473
+ reverseOrder: true
474
+ } : {},
475
+ ...labelPosition && labelPosition !== defaultLabelPosition ? {
476
+ labelPosition
477
+ } : {},
478
+ ...majorTickMark && 'none' !== majorTickMark ? {
479
+ majorTickMark
480
+ } : {},
481
+ ...void 0 !== labelInterval ? {
482
+ labelInterval
483
+ } : {}
484
+ };
485
+ return Object.keys(settings).length ? settings : void 0;
486
+ }
487
+ function chartTextTitleXml(title, reference) {
488
+ const text = reference?.trim() ? `<c:strRef><c:f>${escapeXml(reference.replace(/^=/, ''))}</c:f>${stringCacheXml([
489
+ title
490
+ ])}</c:strRef>` : [
491
+ '<c:rich><a:bodyPr/><a:lstStyle/><a:p><a:r><a:rPr lang="zh-CN" sz="1400" b="0"/>',
492
+ `<a:t>${escapeXml(title)}</a:t></a:r><a:endParaRPr lang="zh-CN"/></a:p></c:rich>`
493
+ ].join('');
494
+ return `<c:title><c:tx>${text}</c:tx><c:layout/><c:overlay val="0"/></c:title>`;
495
+ }
496
+ function chartAxisScalingXml(axis, includeBounds) {
497
+ return [
498
+ `<c:scaling><c:orientation val="${axis?.reverseOrder === true ? 'maxMin' : 'minMax'}"/>`,
499
+ includeBounds && Number.isFinite(axis?.maximum) ? `<c:max val="${Number(axis?.maximum)}"/>` : '',
500
+ includeBounds && Number.isFinite(axis?.minimum) ? `<c:min val="${Number(axis?.minimum)}"/>` : '',
501
+ '</c:scaling>'
502
+ ].join('');
503
+ }
504
+ function chartAxisTickLabelPositionXml(axis, chartType, position) {
505
+ const value = axis?.labelPosition ?? workSpreadsheetChartAxisDefaultLabelPosition(chartType, position);
506
+ return `<c:tickLblPos val="${value}"/>`;
507
+ }
508
+ function chartAxisMajorTickMarkXml(axis) {
509
+ const value = axis?.majorTickMark;
510
+ if (!value || 'none' === value) return '';
511
+ const nativeValue = 'inside' === value ? 'in' : 'outside' === value ? 'out' : 'cross';
512
+ return `<c:majorTickMark val="${nativeValue}"/>`;
513
+ }
514
+ function chartCategoryAxisLabelIntervalXml(axis) {
515
+ return Number.isInteger(axis?.labelInterval) && Number(axis?.labelInterval) >= 1 ? `<c:tickLblSkip val="${Number(axis?.labelInterval)}"/>` : '';
516
+ }
517
+ function chartAxesXml(chart, categoryAxisId, valueAxisId, secondaryCategoryAxisId, secondaryValueAxisId) {
518
+ if (workSpreadsheetChartUsesNumericXAxis(chart.type)) return xyChartAxesXml(chart, categoryAxisId, valueAxisId);
519
+ if ('combination' === chart.type) return combinationChartAxesXml(chart, categoryAxisId, valueAxisId, secondaryCategoryAxisId, secondaryValueAxisId);
520
+ const horizontalBars = 'bar' === chart.type;
521
+ const categoryAxis = horizontalBars ? chart.axes?.left : chart.axes?.bottom;
522
+ const categoryAxisPosition = horizontalBars ? 'left' : 'bottom';
523
+ const categoryAxisTitle = chartAxisTitleXml(categoryAxis);
524
+ const valueAxisPosition = horizontalBars ? 'bottom' : 'left';
525
+ const valueAxis = horizontalBars ? chart.axes?.bottom : chart.axes?.left;
526
+ return [
527
+ '<c:catAx>',
528
+ `<c:axId val="${categoryAxisId}"/>`,
529
+ `${chartAxisScalingXml(categoryAxis, false)}<c:delete val="0"/>`,
530
+ `<c:axPos val="${horizontalBars ? 'l' : 'b'}"/>`,
531
+ categoryAxisTitle,
532
+ chartAxisMajorTickMarkXml(categoryAxis),
533
+ chartAxisTickLabelPositionXml(categoryAxis, chart.type, categoryAxisPosition),
534
+ `<c:crossAx val="${valueAxisId}"/><c:crosses val="autoZero"/>`,
535
+ `<c:auto val="1"/><c:lblAlgn val="ctr"/><c:lblOffset val="100"/>${chartCategoryAxisLabelIntervalXml(categoryAxis)}`,
536
+ '</c:catAx>',
537
+ '<c:valAx>',
538
+ `<c:axId val="${valueAxisId}"/>`,
539
+ `${chartAxisScalingXml(valueAxis, true)}<c:delete val="0"/>`,
540
+ `<c:axPos val="${horizontalBars ? 'b' : 'l'}"/>`,
541
+ chartValueAxisGridlinesXml(valueAxis, chart.type, valueAxisPosition),
542
+ chartAxisTitleXml(valueAxis),
543
+ `${chartValueAxisNumberFormatXml(valueAxis)}${chartAxisMajorTickMarkXml(valueAxis)}${chartAxisTickLabelPositionXml(valueAxis, chart.type, valueAxisPosition)}`,
544
+ `<c:crossAx val="${categoryAxisId}"/><c:crosses val="autoZero"/><c:crossBetween val="between"/>`,
545
+ chartValueAxisMajorUnitXml(valueAxis),
546
+ '</c:valAx>'
547
+ ].join('');
548
+ }
549
+ function parseXlsxValueAxisSettings(axis, chartType, position, preserveVisibleDefaultGridlines) {
550
+ const scaling = directChild(axis, 'scaling');
551
+ const minimum = finiteAttributeValue(scaling ? directChild(scaling, 'min') : null);
552
+ const maximum = finiteAttributeValue(scaling ? directChild(scaling, 'max') : null);
553
+ const majorUnit = finiteAttributeValue(directChild(axis, 'majorUnit'));
554
+ const showMajorGridlines = 1 === directChildren(axis, 'majorGridlines').length;
555
+ const defaultGridlines = workSpreadsheetChartAxisShowsMajorGridlinesByDefault(chartType, position);
556
+ const numberFormatNode = directChild(axis, 'numFmt');
557
+ const numberFormat = attribute(numberFormatNode ?? axis, 'formatCode')?.trim();
558
+ const numberFormatSourceLinked = numberFormatNode ? readBooleanValue(attribute(numberFormatNode, 'sourceLinked')) : void 0;
559
+ const defaultNumberFormat = (!numberFormat || 'General' === numberFormat) && false !== numberFormatSourceLinked;
560
+ const settings = {
561
+ ...void 0 !== minimum ? {
562
+ minimum
563
+ } : {},
564
+ ...void 0 !== maximum ? {
565
+ maximum
566
+ } : {},
567
+ ...void 0 !== majorUnit && majorUnit > 0 ? {
568
+ majorUnit
569
+ } : {},
570
+ ...showMajorGridlines !== defaultGridlines || showMajorGridlines && preserveVisibleDefaultGridlines ? {
571
+ showMajorGridlines
572
+ } : {},
573
+ ...!defaultNumberFormat && numberFormat ? {
574
+ numberFormat
575
+ } : {},
576
+ ...!defaultNumberFormat && void 0 !== numberFormatSourceLinked ? {
577
+ numberFormatSourceLinked
578
+ } : {}
579
+ };
580
+ return Object.keys(settings).length ? settings : void 0;
581
+ }
582
+ function chartAxisTitleXml(axis) {
583
+ if (!axis) return '';
584
+ const title = axis.title?.trim();
585
+ if (!title && !axis.titleReference?.trim()) return '';
586
+ return chartTextTitleXml(title ?? '', axis.titleReference);
587
+ }
588
+ function chartValueAxisGridlinesXml(axis, chartType, position) {
589
+ const visible = axis?.showMajorGridlines ?? workSpreadsheetChartAxisShowsMajorGridlinesByDefault(chartType, position);
590
+ return visible ? '<c:majorGridlines/>' : '';
591
+ }
592
+ function chartValueAxisNumberFormatXml(axis) {
593
+ const formatCode = axis?.numberFormat?.trim() || 'General';
594
+ const sourceLinked = axis?.numberFormat ? true === axis.numberFormatSourceLinked : axis?.numberFormatSourceLinked !== false;
595
+ return `<c:numFmt formatCode="${escapeXml(formatCode)}" sourceLinked="${sourceLinked ? 1 : 0}"/>`;
596
+ }
597
+ function chartValueAxisMajorUnitXml(axis) {
598
+ return Number.isFinite(axis?.majorUnit) && Number(axis?.majorUnit) > 0 ? `<c:majorUnit val="${Number(axis?.majorUnit)}"/>` : '';
599
+ }
600
+ function combinationChartAxesXml(chart, categoryAxisId, valueAxisId, secondaryCategoryAxisId, secondaryValueAxisId) {
601
+ const primaryCategoryAxis = chart.axes?.bottom;
602
+ const primaryValueAxis = chart.axes?.left;
603
+ const primary = [
604
+ '<c:catAx>',
605
+ `<c:axId val="${categoryAxisId}"/>`,
606
+ `${chartAxisScalingXml(primaryCategoryAxis, false)}<c:delete val="0"/><c:axPos val="b"/>`,
607
+ chartAxisTitleXml(primaryCategoryAxis),
608
+ chartAxisMajorTickMarkXml(primaryCategoryAxis),
609
+ chartAxisTickLabelPositionXml(primaryCategoryAxis, chart.type, 'bottom'),
610
+ `<c:crossAx val="${valueAxisId}"/><c:crosses val="autoZero"/>`,
611
+ `<c:auto val="1"/><c:lblAlgn val="ctr"/><c:lblOffset val="100"/>${chartCategoryAxisLabelIntervalXml(primaryCategoryAxis)}`,
612
+ '</c:catAx>',
613
+ '<c:valAx>',
614
+ `<c:axId val="${valueAxisId}"/>`,
615
+ `${chartAxisScalingXml(primaryValueAxis, true)}<c:delete val="0"/><c:axPos val="l"/>`,
616
+ chartValueAxisGridlinesXml(primaryValueAxis, chart.type, 'left'),
617
+ chartAxisTitleXml(primaryValueAxis),
618
+ `${chartValueAxisNumberFormatXml(primaryValueAxis)}${chartAxisMajorTickMarkXml(primaryValueAxis)}${chartAxisTickLabelPositionXml(primaryValueAxis, chart.type, 'left')}`,
619
+ `<c:crossAx val="${categoryAxisId}"/><c:crosses val="autoZero"/><c:crossBetween val="between"/>`,
620
+ chartValueAxisMajorUnitXml(primaryValueAxis),
621
+ '</c:valAx>'
622
+ ].join('');
623
+ const hasSecondary = chart.series.some((series)=>'secondary' === normalizeWorkSpreadsheetChartAxisGroup(series.axisGroup));
624
+ if (!hasSecondary) return primary;
625
+ const secondaryCategoryAxis = chart.axes?.top;
626
+ const secondaryValueAxis = chart.axes?.right;
627
+ const secondaryCategoryLabelPosition = secondaryCategoryAxis?.labelPosition ?? workSpreadsheetChartAxisDefaultLabelPosition(chart.type, 'top');
628
+ const secondary = [
629
+ '<c:catAx>',
630
+ `<c:axId val="${secondaryCategoryAxisId}"/>`,
631
+ `${chartAxisScalingXml(secondaryCategoryAxis, false)}<c:delete val="${'none' === secondaryCategoryLabelPosition ? 1 : 0}"/><c:axPos val="t"/>`,
632
+ chartAxisTitleXml(secondaryCategoryAxis),
633
+ chartAxisMajorTickMarkXml(secondaryCategoryAxis),
634
+ chartAxisTickLabelPositionXml(secondaryCategoryAxis, chart.type, 'top'),
635
+ `<c:crossAx val="${secondaryValueAxisId}"/><c:crosses val="max"/>`,
636
+ `<c:auto val="1"/><c:lblAlgn val="ctr"/><c:lblOffset val="100"/>${chartCategoryAxisLabelIntervalXml(secondaryCategoryAxis)}`,
637
+ '</c:catAx>',
638
+ '<c:valAx>',
639
+ `<c:axId val="${secondaryValueAxisId}"/>`,
640
+ `${chartAxisScalingXml(secondaryValueAxis, true)}<c:delete val="0"/><c:axPos val="r"/>`,
641
+ chartValueAxisGridlinesXml(secondaryValueAxis, chart.type, 'right'),
642
+ chartAxisTitleXml(secondaryValueAxis),
643
+ `${chartValueAxisNumberFormatXml(secondaryValueAxis)}${chartAxisMajorTickMarkXml(secondaryValueAxis)}${chartAxisTickLabelPositionXml(secondaryValueAxis, chart.type, 'right')}`,
644
+ `<c:crossAx val="${secondaryCategoryAxisId}"/><c:crosses val="max"/><c:crossBetween val="between"/>`,
645
+ chartValueAxisMajorUnitXml(secondaryValueAxis),
646
+ '</c:valAx>'
647
+ ].join('');
648
+ return `${primary}${secondary}`;
649
+ }
650
+ function xyChartAxesXml(chart, horizontalAxisId, verticalAxisId) {
651
+ const horizontalAxis = chart.axes?.bottom;
652
+ const verticalAxis = chart.axes?.left;
653
+ return [
654
+ '<c:valAx>',
655
+ `<c:axId val="${horizontalAxisId}"/>`,
656
+ `${chartAxisScalingXml(horizontalAxis, true)}<c:delete val="0"/>`,
657
+ '<c:axPos val="b"/>',
658
+ chartValueAxisGridlinesXml(horizontalAxis, chart.type, 'bottom'),
659
+ chartAxisTitleXml(horizontalAxis),
660
+ `${chartValueAxisNumberFormatXml(horizontalAxis)}${chartAxisMajorTickMarkXml(horizontalAxis)}${chartAxisTickLabelPositionXml(horizontalAxis, chart.type, 'bottom')}`,
661
+ `<c:crossAx val="${verticalAxisId}"/><c:crosses val="autoZero"/><c:crossBetween val="midCat"/>`,
662
+ chartValueAxisMajorUnitXml(horizontalAxis),
663
+ '</c:valAx>',
664
+ '<c:valAx>',
665
+ `<c:axId val="${verticalAxisId}"/>`,
666
+ `${chartAxisScalingXml(verticalAxis, true)}<c:delete val="0"/>`,
667
+ '<c:axPos val="l"/>',
668
+ chartValueAxisGridlinesXml(verticalAxis, chart.type, 'left'),
669
+ chartAxisTitleXml(verticalAxis),
670
+ `${chartValueAxisNumberFormatXml(verticalAxis)}${chartAxisMajorTickMarkXml(verticalAxis)}${chartAxisTickLabelPositionXml(verticalAxis, chart.type, 'left')}`,
671
+ `<c:crossAx val="${horizontalAxisId}"/><c:crosses val="autoZero"/><c:crossBetween val="midCat"/>`,
672
+ chartValueAxisMajorUnitXml(verticalAxis),
673
+ '</c:valAx>'
674
+ ].join('');
675
+ }
676
+ function finiteAttributeValue(element) {
677
+ const source = element ? attribute(element, 'val') : null;
678
+ const value = source?.trim() ? Number(source) : NaN;
679
+ return Number.isFinite(value) ? value : void 0;
680
+ }
681
+ function readBooleanValue(value) {
682
+ return '1' === value || 'true' === value;
683
+ }
684
+ const LEGEND_POSITIONS = new Set([
685
+ 'r',
686
+ 'l',
687
+ 't',
688
+ 'b',
689
+ 'tr'
690
+ ]);
691
+ const LEGEND_CHILDREN = new Set([
692
+ 'legendPos',
693
+ 'layout',
694
+ 'overlay'
695
+ ]);
696
+ const BAR_GROUPINGS = new Set([
697
+ 'clustered',
698
+ 'standard',
699
+ 'stacked',
700
+ 'percentStacked'
701
+ ]);
702
+ const LINE_AREA_GROUPINGS = new Set([
703
+ 'standard',
704
+ 'stacked',
705
+ 'percentStacked'
706
+ ]);
707
+ function parseXlsxChartLegend(document) {
708
+ const legend = firstDescendant(document, 'legend');
709
+ if (!legend) return {
710
+ showLegend: false
711
+ };
712
+ const position = xlsxLegendPosition(attribute(directChild(legend, 'legendPos') ?? legend, 'val'));
713
+ const overlayElement = directChild(legend, 'overlay');
714
+ const overlay = overlayElement ? xlsxBooleanValue(attribute(overlayElement, 'val')) : false;
715
+ return {
716
+ showLegend: true,
717
+ legendPosition: normalizeWorkSpreadsheetChartLegendPosition(position),
718
+ legendOverlay: normalizeWorkSpreadsheetChartLegendOverlay(overlay)
719
+ };
720
+ }
721
+ function xlsxChartLegendXml(chart) {
722
+ if (!chart.showLegend) return '';
723
+ const position = xlsxLegendPositionValue(normalizeWorkSpreadsheetChartLegendPosition(chart.legendPosition));
724
+ const overlay = normalizeWorkSpreadsheetChartLegendOverlay(chart.legendOverlay) ? 1 : 0;
725
+ return `<c:legend><c:legendPos val="${position}"/><c:layout/><c:overlay val="${overlay}"/></c:legend>`;
726
+ }
727
+ function parseXlsxChartPlotLayout(chartNode, chartType) {
728
+ if (!workSpreadsheetChartSupportsGrouping(chartType)) return {};
729
+ const grouping = normalizeWorkSpreadsheetChartGrouping(attribute(directChild(chartNode, 'grouping') ?? chartNode, 'val'), chartType);
730
+ const gapWidth = directChild(chartNode, 'gapWidth');
731
+ const overlap = directChild(chartNode, 'overlap');
732
+ return {
733
+ grouping,
734
+ ...workSpreadsheetChartSupportsBarSpacing(chartType) ? {
735
+ gapWidth: normalizeWorkSpreadsheetChartGapWidth(gapWidth ? attribute(gapWidth, 'val') : void 0),
736
+ overlap: normalizeWorkSpreadsheetChartOverlap(overlap ? attribute(overlap, 'val') : void 0, grouping)
737
+ } : {},
738
+ ...workSpreadsheetChartSupportsSmoothLines(chartType) ? {
739
+ smoothLines: normalizeWorkSpreadsheetChartSmoothLines(consistentLineSmooth(chartNode))
740
+ } : {}
741
+ };
742
+ }
743
+ function xlsxChartGroupingValue(chart) {
744
+ return normalizeWorkSpreadsheetChartGrouping(chart.grouping, chart.type);
745
+ }
746
+ function xlsxChartGapWidthValue(chart) {
747
+ return normalizeWorkSpreadsheetChartGapWidth(chart.gapWidth);
748
+ }
749
+ function xlsxChartOverlapValue(chart) {
750
+ const grouping = xlsxChartGroupingValue(chart);
751
+ return normalizeWorkSpreadsheetChartOverlap(chart.overlap, grouping);
752
+ }
753
+ function xlsxChartSmoothLinesValue(chart) {
754
+ return normalizeWorkSpreadsheetChartSmoothLines(chart.smoothLines);
755
+ }
756
+ function xlsxChartNodeUsesStackedGrouping(chartNode) {
757
+ const grouping = attribute(directChild(chartNode, 'grouping') ?? chartNode, 'val');
758
+ return 'stacked' === grouping || 'percentStacked' === grouping;
759
+ }
760
+ function isSupportedXlsxChartLegend(document) {
761
+ const chart = firstDescendant(document, 'chart');
762
+ if (!chart) return true;
763
+ const legends = directChildren(chart, 'legend');
764
+ if (legends.length > 1) return false;
765
+ const legend = legends[0];
766
+ if (!legend) return true;
767
+ const children = directChildren(legend);
768
+ if (children.some((child)=>!LEGEND_CHILDREN.has(child.localName))) return false;
769
+ const positions = directChildren(legend, 'legendPos');
770
+ if (positions.length > 1 || positions[0] && !LEGEND_POSITIONS.has(attribute(positions[0], 'val') ?? '')) return false;
771
+ const layouts = directChildren(legend, 'layout');
772
+ if (layouts.length > 1 || layouts.some((layout)=>directChildren(layout).length > 0)) return false;
773
+ return validOptionalBooleanElement(legend, 'overlay');
774
+ }
775
+ function isSupportedXlsxChartPlotLayout(chart) {
776
+ const grouping = directChildren(chart, 'grouping');
777
+ if (grouping.length > 1) return false;
778
+ if ('barChart' === chart.localName) {
779
+ if (!grouping.length || !BAR_GROUPINGS.has(attribute(grouping[0], 'val') ?? '')) return false;
780
+ if (!validOptionalIntegerElement(chart, 'gapWidth', 0, 500)) return false;
781
+ if (!validOptionalIntegerElement(chart, 'overlap', -100, 100)) return false;
782
+ } else if ('lineChart' === chart.localName || 'areaChart' === chart.localName) {
783
+ if (!grouping.length || !LINE_AREA_GROUPINGS.has(attribute(grouping[0], 'val') ?? '')) return false;
784
+ if (directChildren(chart, 'gapWidth').length || directChildren(chart, 'overlap').length) return false;
785
+ } else if (grouping.length || directChildren(chart, 'gapWidth').length || directChildren(chart, 'overlap').length) return false;
786
+ if ('lineChart' === chart.localName) {
787
+ if (!hasConsistentLineSmooth(chart)) return false;
788
+ } else if ('scatterChart' !== chart.localName && descendants(chart, 'smooth').some((element)=>xlsxBooleanValue(attribute(element, 'val')))) return false;
789
+ if ('scatterChart' === chart.localName && ![
790
+ 'marker',
791
+ 'line',
792
+ 'lineMarker',
793
+ 'smooth',
794
+ 'smoothMarker'
795
+ ].includes(attribute(firstDescendant(chart, 'scatterStyle') ?? chart, 'val') ?? '')) return false;
796
+ if (descendants(chart, 'bubble3D').some((element)=>xlsxBooleanValue(attribute(element, 'val')))) return false;
797
+ if (descendants(chart, 'explosion').some((element)=>0 !== Number(attribute(element, 'val')))) return false;
798
+ return !descendants(chart, 'firstSliceAng').some((element)=>0 !== Number(attribute(element, 'val')));
799
+ }
800
+ function isSupportedXlsxCombinationPlotNode(chart) {
801
+ if (!isSupportedXlsxChartPlotLayout(chart)) return false;
802
+ const grouping = attribute(directChild(chart, 'grouping') ?? chart, 'val');
803
+ if ('lineChart' === chart.localName || 'areaChart' === chart.localName) return 'standard' === grouping && ('lineChart' !== chart.localName || false === consistentLineSmooth(chart));
804
+ if ('barChart' !== chart.localName || 'clustered' !== grouping) return false;
805
+ const gapWidth = directChild(chart, 'gapWidth');
806
+ const overlap = directChild(chart, 'overlap');
807
+ return 150 === Number(attribute(gapWidth ?? chart, 'val') ?? 150) && 0 === Number(attribute(overlap ?? chart, 'val') ?? 0);
808
+ }
809
+ function xlsxChartGroupingIsStacked(chart) {
810
+ return workSpreadsheetChartGroupingIsStacked(xlsxChartGroupingValue(chart));
811
+ }
812
+ function xlsxLegendPosition(value) {
813
+ if ('l' === value) return 'left';
814
+ if ('t' === value) return 'top';
815
+ if ('b' === value) return 'bottom';
816
+ if ('tr' === value) return 'topRight';
817
+ return 'right';
818
+ }
819
+ function xlsxLegendPositionValue(position) {
820
+ if ('left' === position) return 'l';
821
+ if ('top' === position) return 't';
822
+ if ('bottom' === position) return 'b';
823
+ if ('topRight' === position) return 'tr';
824
+ return 'r';
825
+ }
826
+ function hasConsistentLineSmooth(chart) {
827
+ const chartSmooth = directChildren(chart, 'smooth');
828
+ if (chartSmooth.length > 1 || !chartSmooth.every(validBooleanElement)) return false;
829
+ const series = directChildren(chart, 'ser');
830
+ if (series.some((item)=>directChildren(item, 'smooth').length > 1)) return false;
831
+ const explicit = [
832
+ ...chartSmooth,
833
+ ...series.flatMap((item)=>directChildren(item, 'smooth'))
834
+ ];
835
+ if (!explicit.every(validBooleanElement)) return false;
836
+ if (!explicit.length) return true;
837
+ const values = explicit.map((element)=>xlsxBooleanValue(attribute(element, 'val')));
838
+ return values.every((value)=>value === values[0]);
839
+ }
840
+ function consistentLineSmooth(chart) {
841
+ const explicit = [
842
+ ...directChildren(chart, 'smooth'),
843
+ ...directChildren(chart, 'ser').flatMap((series)=>directChildren(series, 'smooth'))
844
+ ];
845
+ if (!explicit.length || !hasConsistentLineSmooth(chart)) return false;
846
+ return xlsxBooleanValue(attribute(explicit[0], 'val'));
847
+ }
848
+ function validOptionalIntegerElement(parent, localName, minimum, maximum) {
849
+ const elements = directChildren(parent, localName);
850
+ if (elements.length > 1) return false;
851
+ if (!elements.length) return true;
852
+ const value = Number(attribute(elements[0], 'val'));
853
+ return Number.isInteger(value) && value >= minimum && value <= maximum;
854
+ }
855
+ function validOptionalBooleanElement(parent, localName) {
856
+ const elements = directChildren(parent, localName);
857
+ return elements.length <= 1 && elements.every(validBooleanElement);
858
+ }
859
+ function validBooleanElement(element) {
860
+ const value = attribute(element, 'val');
861
+ return null === value || '0' === value || '1' === value || 'false' === value || 'true' === value;
862
+ }
863
+ function xlsxBooleanValue(value) {
864
+ return null === value || '1' === value || 'true' === value;
865
+ }
866
+ const EMUS_PER_POINT = 12700;
867
+ const DASH_VALUES = new Set([
868
+ 'solid',
869
+ 'dash',
870
+ 'dot',
871
+ 'dashDot'
872
+ ]);
873
+ const MARKER_VALUES = new Set([
874
+ 'none',
875
+ 'circle',
876
+ 'square',
877
+ 'diamond',
878
+ 'triangle',
879
+ 'plus',
880
+ 'x',
881
+ 'star'
882
+ ]);
883
+ const MARKER_CHART_ELEMENTS = new Set([
884
+ 'lineChart',
885
+ 'radarChart',
886
+ 'scatterChart'
887
+ ]);
888
+ function parseXlsxChartSeriesStyle(series) {
889
+ const shape = directChild(series, 'spPr');
890
+ const base = shape ? parseShapeProperties(shape) : void 0;
891
+ const markerNode = directChild(series, 'marker');
892
+ const marker = markerNode ? parseMarkerStyle(markerNode) : void 0;
893
+ return normalizeWorkSpreadsheetChartSeriesStyle({
894
+ ...base,
895
+ ...marker ? {
896
+ marker
897
+ } : {}
898
+ });
899
+ }
900
+ function xlsxChartSeriesShapePropertiesXml(style) {
901
+ const normalized = normalizeWorkSpreadsheetChartSeriesStyle(style);
902
+ if (!normalized) return '';
903
+ const fill = normalized.fillColor ? xlsxSolidFillXml(normalized.fillColor, normalized.fillTransparency) : '';
904
+ const line = xlsxLineXml(normalized);
905
+ return fill || line ? `<c:spPr>${fill}${line}</c:spPr>` : '';
906
+ }
907
+ function xlsxChartSeriesMarkerXml(style, defaultVisible, defaultSize = 5) {
908
+ const marker = normalizeWorkSpreadsheetChartSeriesStyle(style)?.marker;
909
+ if (!marker) return defaultVisible ? `<c:marker><c:symbol val="circle"/><c:size val="${defaultSize}"/></c:marker>` : '<c:marker><c:symbol val="none"/></c:marker>';
910
+ const symbol = marker.symbol ?? 'circle';
911
+ if ('none' === symbol) return '<c:marker><c:symbol val="none"/></c:marker>';
912
+ const size = marker.size ?? defaultSize;
913
+ const shape = xlsxMarkerShapePropertiesXml(marker);
914
+ return `<c:marker><c:symbol val="${symbol}"/><c:size val="${size}"/>${shape}</c:marker>`;
915
+ }
916
+ function isSupportedXlsxChartSeriesFormatting(series, chartLocalName) {
917
+ const shapeProperties = directChildren(series, 'spPr');
918
+ if (shapeProperties.length > 1 || shapeProperties.some((shape)=>!isSupportedSeriesShapeProperties(shape))) return false;
919
+ const markers = directChildren(series, 'marker');
920
+ if (markers.length > 1) return false;
921
+ if (!markers.length) return true;
922
+ if (!MARKER_CHART_ELEMENTS.has(chartLocalName)) return false;
923
+ return isSupportedMarker(markers[0]);
924
+ }
925
+ function xlsxChartSeriesFormattingShapeProperties(series) {
926
+ return [
927
+ ...directChildren(series, 'spPr'),
928
+ ...directChildren(series, 'marker').flatMap((marker)=>directChildren(marker, 'spPr'))
929
+ ];
930
+ }
931
+ function parseShapeProperties(shape) {
932
+ const fill = parseSolidFill(directChild(shape, 'solidFill'));
933
+ const line = directChild(shape, 'ln');
934
+ const lineFill = line ? parseSolidFill(directChild(line, 'solidFill')) : void 0;
935
+ const width = line ? xlsxLineWidth(line) : void 0;
936
+ const dash = line ? xlsxLineDash(line) : void 0;
937
+ return normalizeWorkSpreadsheetChartSeriesStyle({
938
+ ...fill ? {
939
+ fillColor: fill.color,
940
+ ...void 0 !== fill.transparency ? {
941
+ fillTransparency: fill.transparency
942
+ } : {}
943
+ } : {},
944
+ ...lineFill ? {
945
+ lineColor: lineFill.color
946
+ } : {},
947
+ ...void 0 !== width ? {
948
+ lineWidth: width
949
+ } : {},
950
+ ...dash ? {
951
+ lineDash: dash
952
+ } : {}
953
+ });
954
+ }
955
+ function parseMarkerStyle(marker) {
956
+ const symbol = attribute(directChild(marker, 'symbol') ?? marker, 'val');
957
+ const sizeSource = attribute(directChild(marker, 'size') ?? marker, 'val');
958
+ const size = null === sizeSource ? void 0 : Number(sizeSource);
959
+ const shape = directChild(marker, 'spPr');
960
+ const fill = shape ? parseSolidFill(directChild(shape, 'solidFill')) : void 0;
961
+ const line = shape ? directChild(shape, 'ln') : void 0;
962
+ const lineFill = line ? parseSolidFill(directChild(line, 'solidFill')) : void 0;
963
+ const parsed = {
964
+ ...MARKER_VALUES.has(symbol) ? {
965
+ symbol: symbol
966
+ } : {},
967
+ ...Number.isFinite(size) ? {
968
+ size
969
+ } : {},
970
+ ...fill ? {
971
+ fillColor: fill.color
972
+ } : {},
973
+ ...lineFill ? {
974
+ lineColor: lineFill.color
975
+ } : {}
976
+ };
977
+ return Object.keys(parsed).length ? parsed : void 0;
978
+ }
979
+ function parseSolidFill(fill) {
980
+ if (!fill) return;
981
+ const color = directChild(fill, 'srgbClr');
982
+ const normalized = normalizeWorkSpreadsheetChartColor(attribute(color ?? fill, 'val'));
983
+ if (!color || !normalized) return;
984
+ const alphaElement = directChild(color, 'alpha');
985
+ if (!alphaElement) return {
986
+ color: normalized
987
+ };
988
+ const alphaSource = attribute(alphaElement, 'val');
989
+ if (null === alphaSource) return {
990
+ color: normalized
991
+ };
992
+ const alpha = Number(alphaSource);
993
+ if (!Number.isFinite(alpha)) return {
994
+ color: normalized
995
+ };
996
+ return {
997
+ color: normalized,
998
+ transparency: Math.round((100 - alpha / 1000) * 100) / 100
999
+ };
1000
+ }
1001
+ function xlsxLineWidth(line) {
1002
+ const source = attribute(line, 'w');
1003
+ if (null === source) return;
1004
+ const width = Number(source);
1005
+ return Number.isFinite(width) ? Math.round(width / EMUS_PER_POINT * 100) / 100 : void 0;
1006
+ }
1007
+ function xlsxLineDash(line) {
1008
+ const value = attribute(directChild(line, 'prstDash') ?? line, 'val');
1009
+ return DASH_VALUES.has(value) ? value : void 0;
1010
+ }
1011
+ function xlsxSolidFillXml(color, transparency) {
1012
+ const value = color.replace(/^#/, '');
1013
+ if (void 0 === transparency) return `<a:solidFill><a:srgbClr val="${value}"/></a:solidFill>`;
1014
+ return `<a:solidFill><a:srgbClr val="${value}"><a:alpha val="${Math.round((100 - transparency) * 1000)}"/></a:srgbClr></a:solidFill>`;
1015
+ }
1016
+ function xlsxLineXml(style) {
1017
+ if (!style.lineColor && void 0 === style.lineWidth && !style.lineDash) return '';
1018
+ const width = void 0 === style.lineWidth ? '' : ` w="${Math.round(style.lineWidth * EMUS_PER_POINT)}"`;
1019
+ const fill = style.lineColor ? xlsxSolidFillXml(style.lineColor) : '';
1020
+ const dash = style.lineDash ? `<a:prstDash val="${style.lineDash}"/>` : '';
1021
+ return `<a:ln${width}>${fill}${dash}</a:ln>`;
1022
+ }
1023
+ function xlsxMarkerShapePropertiesXml(marker) {
1024
+ const fill = marker.fillColor ? xlsxSolidFillXml(marker.fillColor) : '';
1025
+ const line = marker.lineColor ? `<a:ln>${xlsxSolidFillXml(marker.lineColor)}</a:ln>` : '';
1026
+ return fill || line ? `<c:spPr>${fill}${line}</c:spPr>` : '';
1027
+ }
1028
+ function isSupportedSeriesShapeProperties(shape) {
1029
+ if (!hasOnlyAttributes(shape, [])) return false;
1030
+ const children = directChildren(shape);
1031
+ if (children.some((child)=>'solidFill' !== child.localName && 'ln' !== child.localName)) return false;
1032
+ const fills = directChildren(shape, 'solidFill');
1033
+ const lines = directChildren(shape, 'ln');
1034
+ if (fills.length > 1 || lines.length > 1) return false;
1035
+ if (fills.some((fill)=>!isSupportedSolidFill(fill, true))) return false;
1036
+ return !lines.length || isSupportedSeriesLine(lines[0]);
1037
+ }
1038
+ function isSupportedSeriesLine(line) {
1039
+ if (!hasOnlyAttributes(line, [
1040
+ 'w'
1041
+ ])) return false;
1042
+ const children = directChildren(line);
1043
+ if (children.some((child)=>'solidFill' !== child.localName && 'prstDash' !== child.localName)) return false;
1044
+ const fills = directChildren(line, 'solidFill');
1045
+ const dashes = directChildren(line, 'prstDash');
1046
+ if (fills.length > 1 || dashes.length > 1 || fills.some((fill)=>!isSupportedSolidFill(fill, false))) return false;
1047
+ const widthSource = attribute(line, 'w');
1048
+ if (null !== widthSource) {
1049
+ const width = Number(widthSource);
1050
+ if (!Number.isInteger(width) || width < 0.25 * EMUS_PER_POINT || width > 20 * EMUS_PER_POINT) return false;
1051
+ }
1052
+ return !dashes.length || DASH_VALUES.has(attribute(dashes[0], 'val'));
1053
+ }
1054
+ function isSupportedMarker(marker) {
1055
+ if (!hasOnlyAttributes(marker, [])) return false;
1056
+ const children = directChildren(marker);
1057
+ if (children.some((child)=>![
1058
+ 'symbol',
1059
+ 'size',
1060
+ 'spPr'
1061
+ ].includes(child.localName))) return false;
1062
+ const symbols = directChildren(marker, 'symbol');
1063
+ const sizes = directChildren(marker, 'size');
1064
+ const shapes = directChildren(marker, 'spPr');
1065
+ if (1 !== symbols.length || sizes.length > 1 || shapes.length > 1) return false;
1066
+ const symbol = attribute(symbols[0], 'val');
1067
+ if (!hasOnlyAttributes(symbols[0], [
1068
+ 'val'
1069
+ ])) return false;
1070
+ if (!MARKER_VALUES.has(symbol)) return false;
1071
+ if (sizes.length) {
1072
+ if (!hasOnlyAttributes(sizes[0], [
1073
+ 'val'
1074
+ ])) return false;
1075
+ const size = Number(attribute(sizes[0], 'val'));
1076
+ if (!Number.isInteger(size) || size < 2 || size > 72 || 'none' === symbol) return false;
1077
+ }
1078
+ return !shapes.length || isSupportedMarkerShapeProperties(shapes[0]);
1079
+ }
1080
+ function isSupportedMarkerShapeProperties(shape) {
1081
+ if (!hasOnlyAttributes(shape, [])) return false;
1082
+ const children = directChildren(shape);
1083
+ if (children.some((child)=>'solidFill' !== child.localName && 'ln' !== child.localName)) return false;
1084
+ const fills = directChildren(shape, 'solidFill');
1085
+ const lines = directChildren(shape, 'ln');
1086
+ if (fills.length > 1 || lines.length > 1 || fills.some((fill)=>!isSupportedSolidFill(fill, false))) return false;
1087
+ if (!lines.length) return true;
1088
+ const line = lines[0];
1089
+ const lineChildren = directChildren(line);
1090
+ return hasOnlyAttributes(line, []) && lineChildren.length <= 1 && lineChildren.every((child)=>'solidFill' === child.localName && isSupportedSolidFill(child, false));
1091
+ }
1092
+ function isSupportedSolidFill(fill, allowAlpha) {
1093
+ if (!hasOnlyAttributes(fill, [])) return false;
1094
+ const children = directChildren(fill);
1095
+ if (1 !== children.length || 'srgbClr' !== children[0].localName) return false;
1096
+ const color = children[0];
1097
+ if (!hasOnlyAttributes(color, [
1098
+ 'val'
1099
+ ])) return false;
1100
+ if (!/^[0-9a-f]{6}$/i.test(attribute(color, 'val') ?? '')) return false;
1101
+ const transforms = directChildren(color);
1102
+ if (transforms.some((transform)=>'alpha' !== transform.localName) || transforms.length > (allowAlpha ? 1 : 0)) return false;
1103
+ if (!transforms.length) return true;
1104
+ if (!hasOnlyAttributes(transforms[0], [
1105
+ 'val'
1106
+ ])) return false;
1107
+ const alpha = Number(attribute(transforms[0], 'val'));
1108
+ return Number.isInteger(alpha) && alpha >= 0 && alpha <= 100000;
1109
+ }
1110
+ function hasOnlyAttributes(element, allowed) {
1111
+ return Array.from(element.attributes).every((item)=>'xmlns' === item.name || 'xmlns' === item.prefix || allowed.includes(item.localName));
1112
+ }
1113
+ const XLSX_CHART_RELATIONSHIP = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart';
1114
+ const XLSX_CHART_CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml';
1115
+ const RELATIONSHIP_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
1116
+ const CHART_NAMESPACE = 'http://schemas.openxmlformats.org/drawingml/2006/chart';
1117
+ const DRAWINGML_NAMESPACE = 'http://schemas.openxmlformats.org/drawingml/2006/main';
1118
+ const CHART_GRAPHIC_URI = 'http://schemas.openxmlformats.org/drawingml/2006/chart';
1119
+ const SUPPORTED_CHART_ELEMENTS = new Set([
1120
+ 'barChart',
1121
+ 'lineChart',
1122
+ 'pieChart',
1123
+ 'doughnutChart',
1124
+ 'areaChart',
1125
+ 'radarChart',
1126
+ 'scatterChart',
1127
+ 'bubbleChart'
1128
+ ]);
1129
+ function isSupportedXlsxCombinationChartNodes(chartNodes) {
1130
+ if (chartNodes.length < 2 || !chartNodes.every((node)=>null !== xlsxCombinationSeriesType(node) && isSupportedXlsxCombinationPlotNode(node))) return false;
1131
+ const categoryNodes = chartNodes.flatMap((node)=>directChildren(node, 'ser').map((series)=>directChild(series, 'cat')));
1132
+ if (!categoryNodes.length || categoryNodes.some((category)=>!category)) return false;
1133
+ const signatures = categoryNodes.map((category)=>categorySourceSignature(category));
1134
+ return signatures.every((signature)=>signature === signatures[0]);
1135
+ }
1136
+ function categorySourceSignature(category) {
1137
+ const reference = formulaReference(category);
1138
+ return reference ? `reference:${reference}` : `literal:${JSON.stringify(cachedValues(category))}`;
1139
+ }
1140
+ async function readXlsxWorksheetCharts(archive, worksheetPart, worksheet) {
1141
+ const drawingReference = directChildren(worksheet.documentElement, 'drawing')[0];
1142
+ const relationshipId = drawingReference ? attribute(drawingReference, 'r:id') ?? attribute(drawingReference, 'id') : null;
1143
+ if (!relationshipId) return [];
1144
+ const worksheetRelationships = await archive.relationships(worksheetPart);
1145
+ const drawingRelationship = worksheetRelationships.get(relationshipId);
1146
+ if (!drawingRelationship || 'External' === drawingRelationship.targetMode || !drawingRelationship.type.endsWith('/drawing') || !archive.has(drawingRelationship.target)) return [];
1147
+ const drawing = await archive.xml(drawingRelationship.target);
1148
+ const relationships = await archive.relationships(drawingRelationship.target);
1149
+ const charts = [];
1150
+ for (const [anchorIndex, anchor] of directChildren(drawing.documentElement).entries()){
1151
+ if (![
1152
+ 'twoCellAnchor',
1153
+ 'oneCellAnchor',
1154
+ 'absoluteAnchor'
1155
+ ].includes(anchor.localName)) continue;
1156
+ const frame = directChild(anchor, 'graphicFrame');
1157
+ const chartReference = firstDescendant(frame, 'chart');
1158
+ const chartRelationshipId = chartReference ? attribute(chartReference, 'r:id') ?? attribute(chartReference, 'id') : null;
1159
+ const relationship = chartRelationshipId ? relationships.get(chartRelationshipId) : void 0;
1160
+ if (!frame || !relationship || 'External' === relationship.targetMode || !relationship.type.endsWith('/chart') || !archive.has(relationship.target)) continue;
1161
+ const parsed = parseXlsxChart(await archive.xml(relationship.target));
1162
+ if (!parsed) continue;
1163
+ const properties = firstDescendant(frame, 'cNvPr');
1164
+ const sourceId = attribute(properties ?? frame, 'id') ?? String(anchorIndex + 1);
1165
+ charts.push({
1166
+ id: `xlsx-chart-${partNumber(drawingRelationship.target)}-${sourceId}`,
1167
+ name: attribute(properties ?? frame, 'name')?.trim() || `Worksheet chart ${anchorIndex + 1}`,
1168
+ altText: attribute(properties ?? frame, 'descr')?.trim() || void 0,
1169
+ ...parsed,
1170
+ ...readXlsxDrawingAnchor(anchor)
1171
+ });
1172
+ }
1173
+ return charts;
1174
+ }
1175
+ function xlsxWorksheetChartsToSheet(charts, config) {
1176
+ return charts.flatMap((chart)=>{
1177
+ const bounds = xlsxDrawingAnchorToBounds(chart, config);
1178
+ if (!bounds) return [];
1179
+ return [
1180
+ {
1181
+ ...chart,
1182
+ ...bounds
1183
+ }
1184
+ ];
1185
+ });
1186
+ }
1187
+ function xlsxChartGraphicFrameXml(chart, relationshipId, objectId, sheet) {
1188
+ return [
1189
+ '<xdr:twoCellAnchor editAs="oneCell">',
1190
+ xlsxTwoCellAnchorMarkers(chart, sheet),
1191
+ '<xdr:graphicFrame macro="">',
1192
+ '<xdr:nvGraphicFramePr>',
1193
+ `<xdr:cNvPr id="${objectId}" name="${escapeXml(chart.name.trim() || `Chart ${objectId}`)}"${chart.altText?.trim() ? ` descr="${escapeXml(chart.altText.trim())}"` : ''}/>`,
1194
+ '<xdr:cNvGraphicFramePr/>',
1195
+ '</xdr:nvGraphicFramePr>',
1196
+ '<xdr:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/></xdr:xfrm>',
1197
+ `<a:graphic><a:graphicData uri="${CHART_GRAPHIC_URI}">`,
1198
+ `<c:chart xmlns:c="${CHART_NAMESPACE}" xmlns:r="${RELATIONSHIP_NAMESPACE}" r:id="${relationshipId}"/>`,
1199
+ '</a:graphicData></a:graphic>',
1200
+ '</xdr:graphicFrame>',
1201
+ '<xdr:clientData/>',
1202
+ '</xdr:twoCellAnchor>'
1203
+ ].join('');
1204
+ }
1205
+ function xlsxChartPartXml(chart, content, ownerSheet, chartIndex) {
1206
+ const resolved = resolveSpreadsheetChart(content, ownerSheet, chart);
1207
+ const categoryAxisId = 10000000 + 2 * chartIndex;
1208
+ const valueAxisId = categoryAxisId + 1;
1209
+ const secondaryCategoryAxisId = categoryAxisId + 2;
1210
+ const secondaryValueAxisId = categoryAxisId + 3;
1211
+ return [
1212
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',
1213
+ `<c:chartSpace xmlns:c="${CHART_NAMESPACE}" xmlns:a="${DRAWINGML_NAMESPACE}" xmlns:r="${RELATIONSHIP_NAMESPACE}">`,
1214
+ '<c:date1904 val="0"/><c:lang val="zh-CN"/><c:roundedCorners val="0"/>',
1215
+ '<c:chart>',
1216
+ chartTitleXml(resolved),
1217
+ `<c:autoTitleDeleted val="${resolved.title?.trim() ? 0 : 1}"/>`,
1218
+ '<c:plotArea><c:layout/>',
1219
+ chartPlotXml(resolved, categoryAxisId, valueAxisId, secondaryCategoryAxisId, secondaryValueAxisId),
1220
+ 'pie' === resolved.type || 'doughnut' === resolved.type ? '' : chartAxesXml(resolved, categoryAxisId, valueAxisId, secondaryCategoryAxisId, secondaryValueAxisId),
1221
+ '</c:plotArea>',
1222
+ xlsxChartLegendXml(resolved),
1223
+ '<c:plotVisOnly val="1"/><c:dispBlanksAs val="gap"/><c:showDLblsOverMax val="0"/>',
1224
+ '</c:chart>',
1225
+ '<c:printSettings><c:headerFooter/><c:pageMargins b="0.75" l="0.7" r="0.7" t="0.75" header="0.3" footer="0.3"/><c:pageSetup/></c:printSettings>',
1226
+ '</c:chartSpace>'
1227
+ ].join('');
1228
+ }
1229
+ function parseXlsxChart(document) {
1230
+ const plotArea = firstDescendant(document, 'plotArea');
1231
+ if (!plotArea) return null;
1232
+ const chartNodes = directChildren(plotArea).filter((element)=>/Chart$/i.test(element.localName));
1233
+ if (isSupportedXlsxCombinationChartNodes(chartNodes)) return parseXlsxCombinationChart(document, chartNodes);
1234
+ if (1 !== chartNodes.length || !SUPPORTED_CHART_ELEMENTS.has(chartNodes[0].localName)) return null;
1235
+ const chartNode = chartNodes[0];
1236
+ const type = xlsxChartType(chartNode);
1237
+ const seriesNodes = directChildren(chartNode, 'ser');
1238
+ if (!seriesNodes.length) return null;
1239
+ const chartDataLabels = parseXlsxDataLabels(directChild(chartNode, 'dLbls'), type);
1240
+ const parsedSeries = seriesNodes.map((node, index)=>parseXlsxChartSeries(node, index, type, chartDataLabels));
1241
+ const series = xlsxChartNodeUsesStackedGrouping(chartNode) ? parsedSeries.map(withoutStackedChartAnalysis) : parsedSeries;
1242
+ const categoryNode = workSpreadsheetChartUsesNumericXAxis(type) ? null : directChild(seriesNodes[0], 'cat');
1243
+ const title = xlsxChartTitle(document);
1244
+ const axes = parseXlsxChartAxes(document, type, false);
1245
+ const legend = parseXlsxChartLegend(document);
1246
+ const layout = parseXlsxChartPlotLayout(chartNode, type);
1247
+ return {
1248
+ type,
1249
+ title: title ? cachedText(title) || richText(title) || void 0 : void 0,
1250
+ titleReference: title ? formulaReference(title) : void 0,
1251
+ categories: categoryNode ? cachedValues(categoryNode) : [],
1252
+ categoryReference: categoryNode ? formulaReference(categoryNode) : void 0,
1253
+ series,
1254
+ ...legend,
1255
+ ...layout,
1256
+ ...axes ? {
1257
+ axes
1258
+ } : {},
1259
+ ...'doughnut' === type ? {
1260
+ doughnutHoleSize: readDoughnutHoleSize(chartNode)
1261
+ } : {},
1262
+ ...'radar' === type ? {
1263
+ radarStyle: readRadarStyle(chartNode)
1264
+ } : {},
1265
+ ...'scatter' === type ? {
1266
+ scatterStyle: readScatterStyle(chartNode)
1267
+ } : {},
1268
+ ...'bubble' === type ? {
1269
+ bubbleScale: readBubbleScale(chartNode),
1270
+ showNegativeBubbles: readShowNegativeBubbles(chartNode),
1271
+ bubbleSizeRepresents: readBubbleSizeRepresents(chartNode)
1272
+ } : {}
1273
+ };
1274
+ }
1275
+ function parseXlsxCombinationChart(document, chartNodes) {
1276
+ const entries = chartNodes.flatMap((chartNode)=>{
1277
+ const chartType = xlsxCombinationSeriesType(chartNode);
1278
+ if (!chartType) return [];
1279
+ const axisGroup = xlsxCombinationAxisGroup(document, chartNode);
1280
+ const chartDataLabels = parseXlsxDataLabels(directChild(chartNode, 'dLbls'), chartType);
1281
+ return directChildren(chartNode, 'ser').map((seriesNode, sourceIndex)=>({
1282
+ seriesNode,
1283
+ chartType,
1284
+ axisGroup,
1285
+ chartDataLabels,
1286
+ order: finiteNumber(attribute(directChild(seriesNode, 'order') ?? seriesNode, 'val') ?? sourceIndex, sourceIndex)
1287
+ }));
1288
+ }).sort((left, right)=>left.order - right.order);
1289
+ if (!entries.length) return null;
1290
+ const categoryNode = directChild(entries[0].seriesNode, 'cat');
1291
+ const title = xlsxChartTitle(document);
1292
+ const hasSecondaryAxes = entries.some((entry)=>'secondary' === entry.axisGroup);
1293
+ const axes = parseXlsxChartAxes(document, 'combination', hasSecondaryAxes);
1294
+ const legend = parseXlsxChartLegend(document);
1295
+ return {
1296
+ type: 'combination',
1297
+ title: title ? cachedText(title) || richText(title) || void 0 : void 0,
1298
+ titleReference: title ? formulaReference(title) : void 0,
1299
+ categories: categoryNode ? cachedValues(categoryNode) : [],
1300
+ categoryReference: categoryNode ? formulaReference(categoryNode) : void 0,
1301
+ series: entries.map((entry, index)=>({
1302
+ ...parseXlsxChartSeries(entry.seriesNode, index, entry.chartType, entry.chartDataLabels),
1303
+ chartType: entry.chartType,
1304
+ axisGroup: entry.axisGroup
1305
+ })),
1306
+ ...legend,
1307
+ ...axes ? {
1308
+ axes
1309
+ } : {}
1310
+ };
1311
+ }
1312
+ function withoutStackedChartAnalysis(series) {
1313
+ const editable = {
1314
+ ...series
1315
+ };
1316
+ delete editable.errorBars;
1317
+ delete editable.trendlines;
1318
+ return editable;
1319
+ }
1320
+ function xlsxChartTitle(document) {
1321
+ const chart = directChild(document.documentElement, 'chart') ?? firstDescendant(document, 'chart');
1322
+ return chart ? directChild(chart, 'title') : void 0;
1323
+ }
1324
+ function xlsxCombinationSeriesType(node) {
1325
+ if ('lineChart' === node.localName) return 'line';
1326
+ if ('areaChart' === node.localName) return 'area';
1327
+ if ('barChart' === node.localName && 'bar' !== attribute(firstDescendant(node, 'barDir') ?? node, 'val')) return 'column';
1328
+ return null;
1329
+ }
1330
+ function xlsxCombinationAxisGroup(document, chartNode) {
1331
+ const axisIds = new Set(directChildren(chartNode, 'axId').map((axis)=>attribute(axis, 'val')).filter((value)=>Boolean(value)));
1332
+ const usesRightAxis = descendants(document, 'valAx').some((axis)=>{
1333
+ const axisId = attribute(firstDescendant(axis, 'axId') ?? axis, 'val');
1334
+ const position = attribute(firstDescendant(axis, 'axPos') ?? axis, 'val');
1335
+ return Boolean(axisId && axisIds.has(axisId) && 'r' === position);
1336
+ });
1337
+ return usesRightAxis ? 'secondary' : 'primary';
1338
+ }
1339
+ function parseXlsxChartSeries(node, index, type, inheritedDataLabels) {
1340
+ const title = directChild(node, 'tx');
1341
+ const values = directChild(node, workSpreadsheetChartUsesNumericXAxis(type) ? 'yVal' : 'val');
1342
+ const xValues = workSpreadsheetChartUsesNumericXAxis(type) ? directChild(node, 'xVal') : null;
1343
+ const bubbleSizes = 'bubble' === type ? directChild(node, 'bubbleSize') : null;
1344
+ const dataLabelsNode = directChild(node, 'dLbls');
1345
+ const dataLabels = dataLabelsNode ? parseXlsxDataLabels(dataLabelsNode, type) : inheritedDataLabels;
1346
+ const errorBars = workSpreadsheetChartSupportsErrorBars(type) ? directChildren(node, 'errBars').flatMap((errorBarNode)=>{
1347
+ const parsed = parseXlsxErrorBars(errorBarNode, type);
1348
+ return parsed ? [
1349
+ parsed
1350
+ ] : [];
1351
+ }) : [];
1352
+ const trendlines = workSpreadsheetChartSupportsTrendlines(type) ? directChildren(node, 'trendline').flatMap((trendline)=>{
1353
+ const parsed = parseXlsxTrendline(trendline);
1354
+ return parsed ? [
1355
+ parsed
1356
+ ] : [];
1357
+ }) : [];
1358
+ const style = parseXlsxChartSeriesStyle(node);
1359
+ return {
1360
+ name: (title ? cachedText(title) || richText(title) : '') || `Series ${index + 1}`,
1361
+ nameReference: title ? formulaReference(title) : void 0,
1362
+ values: values ? cachedValues(values).map((value)=>finiteNumber(value)) : [],
1363
+ valuesReference: values ? formulaReference(values) : void 0,
1364
+ ...xValues ? {
1365
+ xValues: cachedValues(xValues).map((value)=>finiteNumber(value)),
1366
+ xValuesReference: formulaReference(xValues)
1367
+ } : {},
1368
+ ...bubbleSizes ? {
1369
+ bubbleSizes: cachedValues(bubbleSizes).map((value)=>finiteNumber(value)),
1370
+ bubbleSizesReference: formulaReference(bubbleSizes)
1371
+ } : {},
1372
+ ...dataLabels ? {
1373
+ dataLabels: {
1374
+ ...dataLabels
1375
+ }
1376
+ } : {},
1377
+ ...errorBars.length ? {
1378
+ errorBars
1379
+ } : {},
1380
+ ...trendlines.length ? {
1381
+ trendlines
1382
+ } : {},
1383
+ ...style ? {
1384
+ style
1385
+ } : {}
1386
+ };
1387
+ }
1388
+ function parseXlsxDataLabels(node, chartType) {
1389
+ if (!node || xlsxBooleanElement(node, 'delete')) return;
1390
+ const separatorElement = directChild(node, 'separator');
1391
+ const position = xlsxDataLabelPosition(attribute(directChild(node, 'dLblPos') ?? node, 'val'));
1392
+ return normalizeWorkSpreadsheetDataLabels({
1393
+ ...xlsxBooleanElement(node, 'showVal') ? {
1394
+ showValue: true
1395
+ } : {},
1396
+ ...xlsxBooleanElement(node, 'showCatName') ? {
1397
+ showCategoryName: true
1398
+ } : {},
1399
+ ...xlsxBooleanElement(node, 'showSerName') ? {
1400
+ showSeriesName: true
1401
+ } : {},
1402
+ ...xlsxBooleanElement(node, 'showPercent') ? {
1403
+ showPercentage: true
1404
+ } : {},
1405
+ ...xlsxBooleanElement(node, 'showBubbleSize') ? {
1406
+ showBubbleSize: true
1407
+ } : {},
1408
+ ...separatorElement ? {
1409
+ separator: separatorElement.textContent ?? ''
1410
+ } : {},
1411
+ ...position ? {
1412
+ position
1413
+ } : {}
1414
+ }, chartType);
1415
+ }
1416
+ function xlsxDataLabelPosition(value) {
1417
+ if ('ctr' === value) return 'center';
1418
+ if ('inBase' === value) return 'insideBase';
1419
+ if ('inEnd' === value) return 'insideEnd';
1420
+ if ('outEnd' === value) return 'outsideEnd';
1421
+ if ('l' === value) return 'left';
1422
+ if ('r' === value) return 'right';
1423
+ if ('t' === value) return 'above';
1424
+ if ('b' === value) return 'below';
1425
+ if ('bestFit' === value) return 'bestFit';
1426
+ }
1427
+ function xlsxBooleanElement(parent, localName) {
1428
+ const element = directChild(parent, localName);
1429
+ if (!element) return false;
1430
+ const value = attribute(element, 'val');
1431
+ return null === value || work_xlsx_charts_readBooleanValue(value);
1432
+ }
1433
+ function parseXlsxErrorBars(node, chartType) {
1434
+ if (!workSpreadsheetChartSupportsErrorBars(chartType)) return null;
1435
+ const directionValue = attribute(directChild(node, 'errDir') ?? node, 'val');
1436
+ const direction = 'x' === directionValue ? 'x' : 'y' === directionValue ? 'y' : null;
1437
+ if (!direction || 'x' === direction && !workSpreadsheetChartUsesNumericXAxis(chartType)) return null;
1438
+ const barTypeValue = attribute(directChild(node, 'errBarType') ?? node, 'val');
1439
+ const barType = 'plus' === barTypeValue || 'minus' === barTypeValue || 'both' === barTypeValue ? barTypeValue : null;
1440
+ const valueType = workSpreadsheetErrorBarValueType(attribute(directChild(node, 'errValType') ?? node, 'val'));
1441
+ if (!barType || !valueType) return null;
1442
+ const value = numericElementValue(node, 'val');
1443
+ const plus = directChild(node, 'plus');
1444
+ const minus = directChild(node, 'minus');
1445
+ return normalizeWorkSpreadsheetErrorBars({
1446
+ direction,
1447
+ barType,
1448
+ valueType,
1449
+ ...void 0 !== value ? {
1450
+ value
1451
+ } : {},
1452
+ ...xlsxBooleanElement(node, 'noEndCap') ? {
1453
+ showEndCaps: false
1454
+ } : {},
1455
+ ...plus ? {
1456
+ plusValues: cachedValues(plus).map((item)=>finiteNumber(item)),
1457
+ plusReference: formulaReference(plus)
1458
+ } : {},
1459
+ ...minus ? {
1460
+ minusValues: cachedValues(minus).map((item)=>finiteNumber(item)),
1461
+ minusReference: formulaReference(minus)
1462
+ } : {}
1463
+ }, chartType);
1464
+ }
1465
+ function workSpreadsheetErrorBarValueType(value) {
1466
+ if ('fixedVal' === value) return 'fixedValue';
1467
+ if ('percentage' === value) return 'percentage';
1468
+ if ('stdDev' === value) return 'standardDeviation';
1469
+ if ('stdErr' === value) return 'standardError';
1470
+ if ('cust' === value) return 'custom';
1471
+ return null;
1472
+ }
1473
+ function parseXlsxTrendline(node) {
1474
+ const type = xlsxTrendlineType(attribute(firstDescendant(node, 'trendlineType') ?? node, 'val'));
1475
+ if (!type) return null;
1476
+ const name = directChild(node, 'name')?.textContent?.trim();
1477
+ const order = numericElementValue(node, 'order');
1478
+ const period = numericElementValue(node, 'period');
1479
+ const forward = numericElementValue(node, 'forward');
1480
+ const backward = numericElementValue(node, 'backward');
1481
+ const intercept = numericElementValue(node, 'intercept');
1482
+ return normalizeWorkSpreadsheetTrendline({
1483
+ type,
1484
+ ...name ? {
1485
+ name
1486
+ } : {},
1487
+ ...void 0 !== order ? {
1488
+ order
1489
+ } : {},
1490
+ ...void 0 !== period ? {
1491
+ period
1492
+ } : {},
1493
+ ...void 0 !== forward ? {
1494
+ forward
1495
+ } : {},
1496
+ ...void 0 !== backward ? {
1497
+ backward
1498
+ } : {},
1499
+ ...void 0 !== intercept ? {
1500
+ intercept
1501
+ } : {},
1502
+ ...xlsxBooleanElement(node, 'dispEq') ? {
1503
+ displayEquation: true
1504
+ } : {},
1505
+ ...xlsxBooleanElement(node, 'dispRSq') ? {
1506
+ displayRSquared: true
1507
+ } : {}
1508
+ });
1509
+ }
1510
+ function xlsxTrendlineType(value) {
1511
+ if ('exp' === value) return 'exponential';
1512
+ if ('log' === value) return 'logarithmic';
1513
+ if ('poly' === value) return 'polynomial';
1514
+ if ('power' === value) return 'power';
1515
+ if ('movingAvg' === value) return 'movingAverage';
1516
+ if ('linear' === value) return 'linear';
1517
+ return null;
1518
+ }
1519
+ function numericElementValue(parent, localName) {
1520
+ const element = firstDescendant(parent, localName);
1521
+ if (!element) return;
1522
+ const value = Number(attribute(element, 'val'));
1523
+ return Number.isFinite(value) ? value : void 0;
1524
+ }
1525
+ function xlsxChartType(node) {
1526
+ if ('lineChart' === node.localName) return 'line';
1527
+ if ('pieChart' === node.localName) return 'pie';
1528
+ if ('doughnutChart' === node.localName) return 'doughnut';
1529
+ if ('areaChart' === node.localName) return 'area';
1530
+ if ('radarChart' === node.localName) return 'radar';
1531
+ if ('scatterChart' === node.localName) return 'scatter';
1532
+ if ('bubbleChart' === node.localName) return 'bubble';
1533
+ return 'bar' === attribute(firstDescendant(node, 'barDir') ?? node, 'val') ? 'bar' : 'column';
1534
+ }
1535
+ function readDoughnutHoleSize(node) {
1536
+ return normalizeWorkSpreadsheetDoughnutHoleSize(attribute(firstDescendant(node, 'holeSize') ?? node, 'val'));
1537
+ }
1538
+ function readRadarStyle(node) {
1539
+ return normalizeWorkSpreadsheetRadarStyle(attribute(firstDescendant(node, 'radarStyle') ?? node, 'val'));
1540
+ }
1541
+ function readScatterStyle(node) {
1542
+ return normalizeWorkSpreadsheetScatterStyle(attribute(firstDescendant(node, 'scatterStyle') ?? node, 'val'));
1543
+ }
1544
+ function readBubbleScale(node) {
1545
+ return normalizeWorkSpreadsheetBubbleScale(attribute(firstDescendant(node, 'bubbleScale') ?? node, 'val'));
1546
+ }
1547
+ function readShowNegativeBubbles(node) {
1548
+ return work_xlsx_charts_readBooleanValue(attribute(firstDescendant(node, 'showNegBubbles') ?? node, 'val'));
1549
+ }
1550
+ function readBubbleSizeRepresents(node) {
1551
+ return normalizeWorkSpreadsheetBubbleSizeRepresents(attribute(firstDescendant(node, 'sizeRepresents') ?? node, 'val'));
1552
+ }
1553
+ function chartTitleXml(chart) {
1554
+ const title = chart.title?.trim();
1555
+ if (!title && !chart.titleReference?.trim()) return '';
1556
+ return chartTextTitleXml(title ?? '', chart.titleReference);
1557
+ }
1558
+ function chartPlotXml(chart, categoryAxisId, valueAxisId, secondaryCategoryAxisId, secondaryValueAxisId) {
1559
+ if ('combination' === chart.type) return combinationChartPlotXml(chart, categoryAxisId, valueAxisId, secondaryCategoryAxisId, secondaryValueAxisId);
1560
+ const series = chart.series.map((item, index)=>workSpreadsheetChartUsesNumericXAxis(chart.type) ? xyChartSeriesXml(item, chart, index) : chartSeriesXml(item, chart, index)).join('');
1561
+ if ('pie' === chart.type) return `<c:pieChart><c:varyColors val="1"/>${series}<c:firstSliceAng val="0"/></c:pieChart>`;
1562
+ if ('doughnut' === chart.type) {
1563
+ const holeSize = normalizeWorkSpreadsheetDoughnutHoleSize(chart.doughnutHoleSize);
1564
+ return `<c:doughnutChart><c:varyColors val="1"/>${series}<c:firstSliceAng val="0"/><c:holeSize val="${holeSize}"/></c:doughnutChart>`;
1565
+ }
1566
+ const axes = `<c:axId val="${categoryAxisId}"/><c:axId val="${valueAxisId}"/>`;
1567
+ if ('line' === chart.type) return `<c:lineChart><c:grouping val="${xlsxChartGroupingValue(chart)}"/><c:varyColors val="0"/>${series}<c:marker val="1"/><c:smooth val="${xlsxChartSmoothLinesValue(chart) ? 1 : 0}"/>${axes}</c:lineChart>`;
1568
+ if ('area' === chart.type) return `<c:areaChart><c:grouping val="${xlsxChartGroupingValue(chart)}"/><c:varyColors val="0"/>${series}${axes}</c:areaChart>`;
1569
+ if ('radar' === chart.type) {
1570
+ const style = normalizeWorkSpreadsheetRadarStyle(chart.radarStyle);
1571
+ return `<c:radarChart><c:radarStyle val="${style}"/><c:varyColors val="0"/>${series}${axes}</c:radarChart>`;
1572
+ }
1573
+ if ('scatter' === chart.type) {
1574
+ const style = normalizeWorkSpreadsheetScatterStyle(chart.scatterStyle);
1575
+ return `<c:scatterChart><c:scatterStyle val="${style}"/><c:varyColors val="0"/>${series}${axes}</c:scatterChart>`;
1576
+ }
1577
+ if ('bubble' === chart.type) {
1578
+ const scale = normalizeWorkSpreadsheetBubbleScale(chart.bubbleScale);
1579
+ const showNegative = true === chart.showNegativeBubbles ? 1 : 0;
1580
+ const sizeRepresents = 'width' === normalizeWorkSpreadsheetBubbleSizeRepresents(chart.bubbleSizeRepresents) ? 'w' : 'area';
1581
+ return `<c:bubbleChart><c:varyColors val="0"/>${series}<c:bubble3D val="0"/><c:bubbleScale val="${scale}"/><c:showNegBubbles val="${showNegative}"/><c:sizeRepresents val="${sizeRepresents}"/>${axes}</c:bubbleChart>`;
1582
+ }
1583
+ const direction = 'bar' === chart.type ? 'bar' : 'col';
1584
+ return `<c:barChart><c:barDir val="${direction}"/><c:grouping val="${xlsxChartGroupingValue(chart)}"/><c:varyColors val="0"/>${series}<c:gapWidth val="${xlsxChartGapWidthValue(chart)}"/><c:overlap val="${xlsxChartOverlapValue(chart)}"/>${axes}</c:barChart>`;
1585
+ }
1586
+ function combinationChartPlotXml(chart, categoryAxisId, valueAxisId, secondaryCategoryAxisId, secondaryValueAxisId) {
1587
+ const groups = new Map();
1588
+ chart.series.forEach((item, index)=>{
1589
+ const type = normalizeWorkSpreadsheetCombinationSeriesType(item.chartType);
1590
+ const axisGroup = normalizeWorkSpreadsheetChartAxisGroup(item.axisGroup);
1591
+ const key = `${type}:${axisGroup}`;
1592
+ const group = groups.get(key) ?? {
1593
+ type,
1594
+ axisGroup,
1595
+ series: []
1596
+ };
1597
+ group.series.push({
1598
+ item,
1599
+ index
1600
+ });
1601
+ groups.set(key, group);
1602
+ });
1603
+ return Array.from(groups.values()).map((group)=>{
1604
+ const axes = 'secondary' === group.axisGroup ? `<c:axId val="${secondaryCategoryAxisId}"/><c:axId val="${secondaryValueAxisId}"/>` : `<c:axId val="${categoryAxisId}"/><c:axId val="${valueAxisId}"/>`;
1605
+ const seriesChart = {
1606
+ ...chart,
1607
+ type: group.type,
1608
+ grouping: 'column' === group.type ? 'clustered' : 'standard',
1609
+ gapWidth: 'column' === group.type ? 150 : void 0,
1610
+ overlap: 'column' === group.type ? 0 : void 0,
1611
+ smoothLines: false
1612
+ };
1613
+ const series = group.series.map(({ item, index })=>chartSeriesXml(item, seriesChart, index)).join('');
1614
+ if ('line' === group.type) return `<c:lineChart><c:grouping val="standard"/><c:varyColors val="0"/>${series}<c:marker val="1"/><c:smooth val="0"/>${axes}</c:lineChart>`;
1615
+ if ('area' === group.type) return `<c:areaChart><c:grouping val="standard"/><c:varyColors val="0"/>${series}${axes}</c:areaChart>`;
1616
+ return `<c:barChart><c:barDir val="col"/><c:grouping val="clustered"/><c:varyColors val="0"/>${series}<c:gapWidth val="150"/><c:overlap val="0"/>${axes}</c:barChart>`;
1617
+ }).join('');
1618
+ }
1619
+ function chartSeriesXml(series, chart, index) {
1620
+ const title = chartSeriesTitleXml(series, index);
1621
+ const categories = chart.categoryReference?.trim() ? `<c:strRef><c:f>${escapeXml(chart.categoryReference.replace(/^=/, ''))}</c:f>${stringCacheXml(chart.categories)}</c:strRef>` : stringLiteralXml(chart.categories);
1622
+ const values = series.valuesReference?.trim() ? `<c:numRef><c:f>${escapeXml(series.valuesReference.replace(/^=/, ''))}</c:f>${numberCacheXml(series.values)}</c:numRef>` : numberLiteralXml(series.values);
1623
+ const showMarkers = 'line' === chart.type || 'radar' === chart.type && 'marker' === normalizeWorkSpreadsheetRadarStyle(chart.radarStyle);
1624
+ const shapeProperties = xlsxChartSeriesShapePropertiesXml(series.style);
1625
+ const marker = 'line' === chart.type || 'radar' === chart.type ? xlsxChartSeriesMarkerXml(series.style, showMarkers) : '';
1626
+ const dataLabels = chartDataLabelsXml(series, chart.type);
1627
+ const supportsAnalysis = !xlsxChartGroupingIsStacked(chart);
1628
+ const trendlines = supportsAnalysis && workSpreadsheetChartSupportsTrendlines(chart.type) ? chartTrendlinesXml(series) : '';
1629
+ const errorBars = supportsAnalysis ? chartErrorBarsXml(series, chart.type) : '';
1630
+ return [
1631
+ '<c:ser>',
1632
+ `<c:idx val="${index}"/><c:order val="${index}"/>`,
1633
+ `<c:tx>${title}</c:tx>`,
1634
+ shapeProperties,
1635
+ marker,
1636
+ dataLabels,
1637
+ trendlines,
1638
+ errorBars,
1639
+ `<c:cat>${categories}</c:cat><c:val>${values}</c:val>`,
1640
+ 'line' === chart.type ? `<c:smooth val="${xlsxChartSmoothLinesValue(chart) ? 1 : 0}"/>` : '',
1641
+ '</c:ser>'
1642
+ ].join('');
1643
+ }
1644
+ function xyChartSeriesXml(series, chart, index) {
1645
+ const title = chartSeriesTitleXml(series, index);
1646
+ const xValues = series.xValues?.length ? series.xValues : Array.from({
1647
+ length: series.values.length
1648
+ }, (_, valueIndex)=>valueIndex + 1);
1649
+ const xValuesXml = numberReferenceOrLiteralXml(series.xValuesReference, xValues);
1650
+ const yValuesXml = numberReferenceOrLiteralXml(series.valuesReference, series.values);
1651
+ const dataLabels = chartDataLabelsXml(series, chart.type);
1652
+ const trendlines = chartTrendlinesXml(series);
1653
+ const errorBars = chartErrorBarsXml(series, chart.type);
1654
+ const shapeProperties = xlsxChartSeriesShapePropertiesXml(series.style);
1655
+ if ('bubble' === chart.type) {
1656
+ const bubbleSizesXml = numberReferenceOrLiteralXml(series.bubbleSizesReference, series.bubbleSizes ?? []);
1657
+ return [
1658
+ '<c:ser>',
1659
+ `<c:idx val="${index}"/><c:order val="${index}"/>`,
1660
+ `<c:tx>${title}</c:tx>`,
1661
+ shapeProperties,
1662
+ dataLabels,
1663
+ trendlines,
1664
+ errorBars,
1665
+ `<c:xVal>${xValuesXml}</c:xVal><c:yVal>${yValuesXml}</c:yVal>`,
1666
+ `<c:bubbleSize>${bubbleSizesXml}</c:bubbleSize><c:bubble3D val="0"/>`,
1667
+ '</c:ser>'
1668
+ ].join('');
1669
+ }
1670
+ const style = normalizeWorkSpreadsheetScatterStyle(chart.scatterStyle);
1671
+ const showMarkers = 'marker' === style || 'lineMarker' === style || 'smoothMarker' === style;
1672
+ const smooth = 'smooth' === style || 'smoothMarker' === style;
1673
+ return [
1674
+ '<c:ser>',
1675
+ `<c:idx val="${index}"/><c:order val="${index}"/>`,
1676
+ `<c:tx>${title}</c:tx>`,
1677
+ shapeProperties,
1678
+ xlsxChartSeriesMarkerXml(series.style, showMarkers),
1679
+ dataLabels,
1680
+ trendlines,
1681
+ errorBars,
1682
+ `<c:xVal>${xValuesXml}</c:xVal><c:yVal>${yValuesXml}</c:yVal>`,
1683
+ `<c:smooth val="${smooth ? 1 : 0}"/>`,
1684
+ '</c:ser>'
1685
+ ].join('');
1686
+ }
1687
+ function chartDataLabelsXml(series, chartType) {
1688
+ if (!series.dataLabels) return '';
1689
+ const labels = normalizeWorkSpreadsheetDataLabels(series.dataLabels, chartType);
1690
+ return [
1691
+ '<c:dLbls>',
1692
+ labels.position ? `<c:dLblPos val="${xlsxDataLabelPositionValue(labels.position)}"/>` : '',
1693
+ '<c:showLegendKey val="0"/>',
1694
+ `<c:showVal val="${labels.showValue ? 1 : 0}"/>`,
1695
+ `<c:showCatName val="${labels.showCategoryName ? 1 : 0}"/>`,
1696
+ `<c:showSerName val="${labels.showSeriesName ? 1 : 0}"/>`,
1697
+ `<c:showPercent val="${labels.showPercentage ? 1 : 0}"/>`,
1698
+ `<c:showBubbleSize val="${labels.showBubbleSize ? 1 : 0}"/>`,
1699
+ void 0 !== labels.separator ? `<c:separator>${escapeXml(labels.separator)}</c:separator>` : '',
1700
+ '<c:showLeaderLines val="0"/>',
1701
+ '</c:dLbls>'
1702
+ ].join('');
1703
+ }
1704
+ function xlsxDataLabelPositionValue(position) {
1705
+ if ('center' === position) return 'ctr';
1706
+ if ('insideBase' === position) return 'inBase';
1707
+ if ('insideEnd' === position) return 'inEnd';
1708
+ if ('outsideEnd' === position) return 'outEnd';
1709
+ if ('left' === position) return 'l';
1710
+ if ('right' === position) return 'r';
1711
+ if ('above' === position) return 't';
1712
+ if ('below' === position) return 'b';
1713
+ return 'bestFit';
1714
+ }
1715
+ function chartTrendlinesXml(series) {
1716
+ return (series.trendlines ?? []).map(chartTrendlineXml).join('');
1717
+ }
1718
+ function chartErrorBarsXml(series, chartType) {
1719
+ if (!workSpreadsheetChartSupportsErrorBars(chartType)) return '';
1720
+ const directions = new Set();
1721
+ return (series.errorBars ?? []).flatMap((source)=>{
1722
+ if ('x' === source.direction && !workSpreadsheetChartUsesNumericXAxis(chartType)) return [];
1723
+ const errorBars = normalizeWorkSpreadsheetErrorBars(source, chartType);
1724
+ if (directions.has(errorBars.direction)) return [];
1725
+ directions.add(errorBars.direction);
1726
+ return [
1727
+ chartErrorBarXml(errorBars)
1728
+ ];
1729
+ }).join('');
1730
+ }
1731
+ function chartErrorBarXml(errorBars) {
1732
+ const plus = 'custom' === errorBars.valueType && 'minus' !== errorBars.barType ? chartCustomErrorBarSourceXml('plus', errorBars.plusReference, errorBars.plusValues) : '';
1733
+ const minus = 'custom' === errorBars.valueType && 'plus' !== errorBars.barType ? chartCustomErrorBarSourceXml('minus', errorBars.minusReference, errorBars.minusValues) : '';
1734
+ const value = 'fixedValue' === errorBars.valueType || 'percentage' === errorBars.valueType || 'standardDeviation' === errorBars.valueType ? `<c:val val="${finiteNumber(errorBars.value, 'percentage' === errorBars.valueType ? 5 : 1)}"/>` : '';
1735
+ return [
1736
+ '<c:errBars>',
1737
+ `<c:errDir val="${errorBars.direction}"/>`,
1738
+ `<c:errBarType val="${errorBars.barType}"/>`,
1739
+ `<c:errValType val="${xlsxErrorBarValueType(errorBars.valueType)}"/>`,
1740
+ false === errorBars.showEndCaps ? '<c:noEndCap val="1"/>' : '',
1741
+ plus,
1742
+ minus,
1743
+ value,
1744
+ '</c:errBars>'
1745
+ ].join('');
1746
+ }
1747
+ function chartCustomErrorBarSourceXml(localName, reference, values) {
1748
+ if (!reference?.trim() && !values?.length) return '';
1749
+ return `<c:${localName}>${numberReferenceOrLiteralXml(reference, values ?? [])}</c:${localName}>`;
1750
+ }
1751
+ function xlsxErrorBarValueType(type) {
1752
+ if ('fixedValue' === type) return 'fixedVal';
1753
+ if ('standardDeviation' === type) return 'stdDev';
1754
+ if ('standardError' === type) return 'stdErr';
1755
+ if ('custom' === type) return 'cust';
1756
+ return 'percentage';
1757
+ }
1758
+ function chartTrendlineXml(source) {
1759
+ const trendline = normalizeWorkSpreadsheetTrendline(source);
1760
+ const type = xlsxTrendlineTypeValue(trendline.type);
1761
+ return [
1762
+ '<c:trendline>',
1763
+ trendline.name ? `<c:name>${escapeXml(trendline.name)}</c:name>` : '',
1764
+ `<c:trendlineType val="${type}"/>`,
1765
+ 'polynomial' === trendline.type ? `<c:order val="${trendline.order ?? 2}"/>` : '',
1766
+ 'movingAverage' === trendline.type ? `<c:period val="${trendline.period ?? 2}"/>` : '',
1767
+ trendline.forward ? `<c:forward val="${finiteNumber(trendline.forward)}"/>` : '',
1768
+ trendline.backward ? `<c:backward val="${finiteNumber(trendline.backward)}"/>` : '',
1769
+ void 0 !== trendline.intercept ? `<c:intercept val="${finiteNumber(trendline.intercept)}"/>` : '',
1770
+ trendline.displayRSquared ? '<c:dispRSq val="1"/>' : '',
1771
+ trendline.displayEquation ? '<c:dispEq val="1"/>' : '',
1772
+ '</c:trendline>'
1773
+ ].join('');
1774
+ }
1775
+ function xlsxTrendlineTypeValue(type) {
1776
+ if ('exponential' === type) return 'exp';
1777
+ if ('logarithmic' === type) return 'log';
1778
+ if ('polynomial' === type) return 'poly';
1779
+ if ('movingAverage' === type) return 'movingAvg';
1780
+ return type;
1781
+ }
1782
+ function chartSeriesTitleXml(series, index) {
1783
+ return series.nameReference?.trim() ? `<c:strRef><c:f>${escapeXml(series.nameReference.replace(/^=/, ''))}</c:f>${stringCacheXml([
1784
+ series.name
1785
+ ])}</c:strRef>` : `<c:v>${escapeXml(series.name || `Series ${index + 1}`)}</c:v>`;
1786
+ }
1787
+ function numberReferenceOrLiteralXml(reference, values) {
1788
+ return reference?.trim() ? `<c:numRef><c:f>${escapeXml(reference.replace(/^=/, ''))}</c:f>${numberCacheXml(values)}</c:numRef>` : numberLiteralXml(values);
1789
+ }
1790
+ function work_xlsx_charts_readBooleanValue(value) {
1791
+ return '1' === value || 'true' === value;
1792
+ }
1793
+ function partNumber(path) {
1794
+ return /(\d+)(?:\.xml)?$/i.exec(path)?.[1] ?? '1';
1795
+ }
1796
+ const MAX_XLSX_WORKSHEET_IMAGE_BYTES = 10485760;
1797
+ const DRAWING_RELATIONSHIP = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing';
1798
+ const IMAGE_RELATIONSHIP = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image';
1799
+ const work_xlsx_images_RELATIONSHIP_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
1800
+ const PACKAGE_RELATIONSHIP_NAMESPACE = 'http://schemas.openxmlformats.org/package/2006/relationships';
1801
+ const DRAWING_NAMESPACE = 'http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing';
1802
+ const work_xlsx_images_DRAWINGML_NAMESPACE = 'http://schemas.openxmlformats.org/drawingml/2006/main';
1803
+ const DRAWING_CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.drawing+xml';
1804
+ const SUPPORTED_IMAGE_TYPES = new Set([
1805
+ 'image/bmp',
1806
+ 'image/gif',
1807
+ 'image/jpeg',
1808
+ 'image/png',
1809
+ 'image/svg+xml',
1810
+ 'image/webp'
1811
+ ]);
1812
+ function isSupportedXlsxWorksheetImageContentType(contentType) {
1813
+ return SUPPORTED_IMAGE_TYPES.has(contentType.toLowerCase());
1814
+ }
1815
+ async function readXlsxWorksheetImages(archive, worksheetPart, worksheet, imageBudget) {
1816
+ const drawingReference = directChildren(worksheet.documentElement, 'drawing')[0];
1817
+ const relationshipId = drawingReference ? attribute(drawingReference, 'r:id') ?? attribute(drawingReference, 'id') : null;
1818
+ if (!relationshipId) return [];
1819
+ const worksheetRelationships = await archive.relationships(worksheetPart);
1820
+ const drawingRelationship = worksheetRelationships.get(relationshipId);
1821
+ if (!drawingRelationship || 'External' === drawingRelationship.targetMode || !drawingRelationship.type.endsWith('/drawing') || !archive.has(drawingRelationship.target)) return [];
1822
+ const drawing = await archive.xml(drawingRelationship.target);
1823
+ const relationships = await archive.relationships(drawingRelationship.target);
1824
+ const images = [];
1825
+ for (const [anchorIndex, anchor] of directChildren(drawing.documentElement).entries()){
1826
+ if (![
1827
+ 'twoCellAnchor',
1828
+ 'oneCellAnchor',
1829
+ 'absoluteAnchor'
1830
+ ].includes(anchor.localName)) continue;
1831
+ const picture = directChild(anchor, 'pic');
1832
+ const blip = firstDescendant(picture, 'blip');
1833
+ const imageRelationshipId = blip ? attribute(blip, 'r:embed') ?? attribute(blip, 'embed') : null;
1834
+ const imageRelationship = imageRelationshipId ? relationships.get(imageRelationshipId) : void 0;
1835
+ if (!picture || !imageRelationship || !readableImageRelationship(archive, imageRelationship)) continue;
1836
+ const contentType = contentTypeForPart(imageRelationship.target);
1837
+ if (!isSupportedXlsxWorksheetImageContentType(contentType)) continue;
1838
+ const bytes = await archive.bytes(imageRelationship.target);
1839
+ if (imageBudget.bytes + bytes.byteLength > MAX_XLSX_WORKSHEET_IMAGE_BYTES) continue;
1840
+ imageBudget.bytes += bytes.byteLength;
1841
+ const properties = firstDescendant(picture, 'cNvPr');
1842
+ const sourceId = attribute(properties ?? picture, 'id') ?? String(anchorIndex + 1);
1843
+ const drawingNumber = work_xlsx_images_partNumber(drawingRelationship.target);
1844
+ images.push({
1845
+ id: `xlsx-image-${drawingNumber}-${sourceId}`,
1846
+ name: attribute(properties ?? picture, 'name')?.trim() || `Worksheet image ${anchorIndex + 1}`,
1847
+ altText: attribute(properties ?? picture, 'descr')?.trim() || void 0,
1848
+ contentType,
1849
+ dataUrl: bytesToDataUrl(bytes, contentType),
1850
+ ...readXlsxDrawingAnchor(anchor)
1851
+ });
1852
+ }
1853
+ return images;
1854
+ }
1855
+ function xlsxWorksheetImagesToSheet(images, config) {
1856
+ return images.flatMap((image)=>{
1857
+ const bounds = xlsxDrawingAnchorToBounds(image, config);
1858
+ if (!bounds) return [];
1859
+ return [
1860
+ {
1861
+ id: image.id,
1862
+ name: image.name,
1863
+ altText: image.altText,
1864
+ contentType: image.contentType,
1865
+ src: image.dataUrl,
1866
+ ...bounds
1867
+ }
1868
+ ];
1869
+ });
1870
+ }
1871
+ async function patchXlsxWorksheetDrawings(buffer, content) {
1872
+ if (!content.sheets.some((sheet)=>sheet.images?.some((image)=>serializableImage(image)) || (sheet.charts?.length ?? 0) > 0)) return buffer;
1873
+ const archive = await work_ooxml_package_OoxmlPackage.load(buffer);
1874
+ const worksheetParts = await exportedWorksheetParts(archive);
1875
+ const zip = await jszip.loadAsync(buffer);
1876
+ const drawingPaths = [];
1877
+ const chartPaths = [];
1878
+ const imageTypes = new Map();
1879
+ let drawingIndex = 1;
1880
+ let mediaIndex = 1;
1881
+ let chartIndex = 1;
1882
+ for (const sheet of content.sheets){
1883
+ const images = (sheet.images ?? []).flatMap((image)=>{
1884
+ const data = serializableImage(image);
1885
+ return data ? [
1886
+ {
1887
+ image,
1888
+ data
1889
+ }
1890
+ ] : [];
1891
+ });
1892
+ const charts = sheet.charts ?? [];
1893
+ if (!images.length && !charts.length) continue;
1894
+ const worksheetPart = worksheetParts.get(sheet.name.slice(0, 31) || '工作表');
1895
+ const worksheetEntry = worksheetPart ? zip.file(worksheetPart) : null;
1896
+ if (!worksheetPart || !worksheetEntry) continue;
1897
+ const drawingPath = `xl/drawings/drawing${drawingIndex}.xml`;
1898
+ const drawingRelationshipsPath = `xl/drawings/_rels/drawing${drawingIndex}.xml.rels`;
1899
+ drawingIndex += 1;
1900
+ const media = images.map(({ image, data }, index)=>{
1901
+ const path = `xl/media/image${mediaIndex}.${data.extension}`;
1902
+ mediaIndex += 1;
1903
+ imageTypes.set(data.extension, data.contentType);
1904
+ zip.file(path, data.bytes);
1905
+ return {
1906
+ image,
1907
+ path,
1908
+ relationshipId: `rId${index + 1}`,
1909
+ objectId: index + 1
1910
+ };
1911
+ });
1912
+ const chartParts = charts.map((chart, index)=>{
1913
+ const path = `xl/charts/chart${chartIndex}.xml`;
1914
+ const currentChartIndex = chartIndex;
1915
+ chartIndex += 1;
1916
+ chartPaths.push(path);
1917
+ zip.file(path, xlsxChartPartXml(chart, content, sheet, currentChartIndex));
1918
+ return {
1919
+ chart,
1920
+ path,
1921
+ relationshipId: `rId${media.length + index + 1}`,
1922
+ objectId: media.length + index + 1
1923
+ };
1924
+ });
1925
+ zip.file(drawingPath, drawingXml(media, chartParts, sheet));
1926
+ zip.file(drawingRelationshipsPath, drawingRelationshipsXml(media, chartParts));
1927
+ drawingPaths.push(drawingPath);
1928
+ await bindDrawingToWorksheet(zip, worksheetPart, worksheetEntry, drawingPath);
1929
+ }
1930
+ await updateContentTypes(zip, drawingPaths, chartPaths, imageTypes);
1931
+ return zip.generateAsync({
1932
+ type: 'arraybuffer',
1933
+ compression: 'DEFLATE'
1934
+ });
1935
+ }
1936
+ function readableImageRelationship(archive, relationship) {
1937
+ return 'External' !== relationship.targetMode && relationship.type.endsWith('/image') && archive.has(relationship.target);
1938
+ }
1939
+ function serializableImage(image) {
1940
+ const match = /^data:([^;,]+);base64,([a-z0-9+/=\s]+)$/i.exec(image.src);
1941
+ if (!match) return null;
1942
+ const contentType = image.contentType?.toLowerCase() || match[1].toLowerCase();
1943
+ const extension = imageExtension(contentType);
1944
+ if (!extension || !isSupportedXlsxWorksheetImageContentType(contentType)) return null;
1945
+ try {
1946
+ const binary = atob(match[2].replace(/\s+/g, ''));
1947
+ return {
1948
+ contentType,
1949
+ extension,
1950
+ bytes: Uint8Array.from(binary, (character)=>character.charCodeAt(0))
1951
+ };
1952
+ } catch {
1953
+ return null;
1954
+ }
1955
+ }
1956
+ function imageExtension(contentType) {
1957
+ const extensions = {
1958
+ 'image/bmp': 'bmp',
1959
+ 'image/gif': 'gif',
1960
+ 'image/jpeg': 'jpeg',
1961
+ 'image/png': 'png',
1962
+ 'image/svg+xml': 'svg',
1963
+ 'image/webp': 'webp'
1964
+ };
1965
+ return extensions[contentType] ?? null;
1966
+ }
1967
+ async function exportedWorksheetParts(archive) {
1968
+ if (!archive.has('xl/workbook.xml')) return new Map();
1969
+ const workbook = await archive.xml('xl/workbook.xml');
1970
+ const relationships = await archive.relationships('xl/workbook.xml');
1971
+ const parts = new Map();
1972
+ for (const sheet of directChildren(firstDescendant(workbook, 'sheets') ?? workbook.documentElement, 'sheet')){
1973
+ const name = attribute(sheet, 'name');
1974
+ const relationship = relationships.get(attribute(sheet, 'r:id') ?? attribute(sheet, 'id') ?? '');
1975
+ if (name && relationship?.type.endsWith('/worksheet')) parts.set(name, relationship.target);
1976
+ }
1977
+ return parts;
1978
+ }
1979
+ function drawingXml(media, charts, sheet) {
1980
+ return [
1981
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',
1982
+ `<xdr:wsDr xmlns:xdr="${DRAWING_NAMESPACE}" xmlns:a="${work_xlsx_images_DRAWINGML_NAMESPACE}" xmlns:r="${work_xlsx_images_RELATIONSHIP_NAMESPACE}">`,
1983
+ ...media.map(({ image, relationshipId, objectId })=>[
1984
+ '<xdr:twoCellAnchor editAs="oneCell">',
1985
+ xlsxTwoCellAnchorMarkers(image, sheet),
1986
+ '<xdr:pic><xdr:nvPicPr>',
1987
+ `<xdr:cNvPr id="${objectId}" name="${work_xlsx_images_escapeXml(image.name?.trim() || `Image ${objectId}`)}"${image.altText?.trim() ? ` descr="${work_xlsx_images_escapeXml(image.altText.trim())}"` : ''}/>`,
1988
+ '<xdr:cNvPicPr><a:picLocks noChangeAspect="1"/></xdr:cNvPicPr></xdr:nvPicPr>',
1989
+ `<xdr:blipFill><a:blip r:embed="${relationshipId}"/><a:stretch><a:fillRect/></a:stretch></xdr:blipFill>`,
1990
+ '<xdr:spPr><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></xdr:spPr>',
1991
+ '</xdr:pic><xdr:clientData/></xdr:twoCellAnchor>'
1992
+ ].join('')),
1993
+ ...charts.map(({ chart, relationshipId, objectId })=>xlsxChartGraphicFrameXml(chart, relationshipId, objectId, sheet)),
1994
+ '</xdr:wsDr>'
1995
+ ].join('');
1996
+ }
1997
+ function drawingRelationshipsXml(media, charts) {
1998
+ return [
1999
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',
2000
+ `<Relationships xmlns="${PACKAGE_RELATIONSHIP_NAMESPACE}">`,
2001
+ ...media.map(({ path, relationshipId })=>`<Relationship Id="${relationshipId}" Type="${IMAGE_RELATIONSHIP}" Target="../media/${path.split('/').at(-1)}"/>`),
2002
+ ...charts.map(({ path, relationshipId })=>`<Relationship Id="${relationshipId}" Type="${XLSX_CHART_RELATIONSHIP}" Target="../charts/${path.split('/').at(-1)}"/>`),
2003
+ '</Relationships>'
2004
+ ].join('');
2005
+ }
2006
+ async function bindDrawingToWorksheet(zip, worksheetPart, worksheetEntry, drawingPath) {
2007
+ const worksheet = parseXml(await worksheetEntry.async('text'), worksheetPart);
2008
+ for (const existing of directChildren(worksheet.documentElement, 'drawing'))existing.remove();
2009
+ const relationshipPath = relationshipsPartPath(worksheetPart);
2010
+ const relationshipEntry = zip.file(relationshipPath);
2011
+ const relationships = relationshipEntry ? parseXml(await relationshipEntry.async('text'), relationshipPath) : parseXml(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="${PACKAGE_RELATIONSHIP_NAMESPACE}"/>`, relationshipPath);
2012
+ for (const existing of directChildren(relationships.documentElement, 'Relationship'))if (attribute(existing, 'Type')?.endsWith('/drawing')) existing.remove();
2013
+ const relationshipId = nextRelationshipId(relationships);
2014
+ const relationship = relationships.createElementNS(PACKAGE_RELATIONSHIP_NAMESPACE, 'Relationship');
2015
+ relationship.setAttribute('Id', relationshipId);
2016
+ relationship.setAttribute('Type', DRAWING_RELATIONSHIP);
2017
+ relationship.setAttribute('Target', relativePartTarget(worksheetPart, drawingPath));
2018
+ relationships.documentElement.append(relationship);
2019
+ const drawing = worksheet.createElementNS(worksheet.documentElement.namespaceURI, 'drawing');
2020
+ drawing.setAttributeNS(work_xlsx_images_RELATIONSHIP_NAMESPACE, 'r:id', relationshipId);
2021
+ const insertionPoint = directChildren(worksheet.documentElement).find((element)=>[
2022
+ 'legacyDrawing',
2023
+ 'legacyDrawingHF',
2024
+ 'picture',
2025
+ 'oleObjects',
2026
+ 'controls',
2027
+ 'webPublishItems',
2028
+ 'tableParts',
2029
+ 'extLst'
2030
+ ].includes(element.localName));
2031
+ worksheet.documentElement.insertBefore(drawing, insertionPoint ?? null);
2032
+ zip.file(worksheetPart, new XMLSerializer().serializeToString(worksheet));
2033
+ zip.file(relationshipPath, new XMLSerializer().serializeToString(relationships));
2034
+ }
2035
+ function nextRelationshipId(document) {
2036
+ const ids = new Set(directChildren(document.documentElement, 'Relationship').map((relationship)=>attribute(relationship, 'Id')).filter((id)=>Boolean(id)));
2037
+ let index = 1;
2038
+ while(ids.has(`rId${index}`))index += 1;
2039
+ return `rId${index}`;
2040
+ }
2041
+ async function updateContentTypes(zip, drawingPaths, chartPaths, imageTypes) {
2042
+ const entry = zip.file('[Content_Types].xml');
2043
+ if (!entry) return;
2044
+ const document = parseXml(await entry.async('text'), '[Content_Types].xml');
2045
+ const namespace = document.documentElement.namespaceURI;
2046
+ for (const [extension, contentType] of imageTypes){
2047
+ const existing = directChildren(document.documentElement, 'Default').find((element)=>attribute(element, 'Extension')?.toLowerCase() === extension);
2048
+ if (existing) {
2049
+ existing.setAttribute('ContentType', contentType);
2050
+ continue;
2051
+ }
2052
+ const definition = document.createElementNS(namespace, 'Default');
2053
+ definition.setAttribute('Extension', extension);
2054
+ definition.setAttribute('ContentType', contentType);
2055
+ document.documentElement.append(definition);
2056
+ }
2057
+ for (const path of drawingPaths){
2058
+ const partName = `/${path}`;
2059
+ const existing = directChildren(document.documentElement, 'Override').find((element)=>attribute(element, 'PartName') === partName);
2060
+ const override = existing ?? document.createElementNS(namespace, 'Override');
2061
+ override.setAttribute('PartName', partName);
2062
+ override.setAttribute('ContentType', DRAWING_CONTENT_TYPE);
2063
+ if (!existing) document.documentElement.append(override);
2064
+ }
2065
+ for (const path of chartPaths){
2066
+ const partName = `/${path}`;
2067
+ const existing = directChildren(document.documentElement, 'Override').find((element)=>attribute(element, 'PartName') === partName);
2068
+ const override = existing ?? document.createElementNS(namespace, 'Override');
2069
+ override.setAttribute('PartName', partName);
2070
+ override.setAttribute('ContentType', XLSX_CHART_CONTENT_TYPE);
2071
+ if (!existing) document.documentElement.append(override);
2072
+ }
2073
+ zip.file('[Content_Types].xml', new XMLSerializer().serializeToString(document));
2074
+ }
2075
+ function relationshipsPartPath(sourcePart) {
2076
+ const segments = sourcePart.split('/');
2077
+ const name = segments.pop() ?? '';
2078
+ return [
2079
+ ...segments,
2080
+ '_rels',
2081
+ `${name}.rels`
2082
+ ].join('/');
2083
+ }
2084
+ function relativePartTarget(sourcePart, targetPart) {
2085
+ const source = sourcePart.split('/').slice(0, -1);
2086
+ const target = targetPart.split('/');
2087
+ while(source.length && target.length && source[0] === target[0]){
2088
+ source.shift();
2089
+ target.shift();
2090
+ }
2091
+ return [
2092
+ ...source.map(()=>'..'),
2093
+ ...target
2094
+ ].join('/');
2095
+ }
2096
+ function work_xlsx_images_partNumber(path) {
2097
+ return /(\d+)(?:\.xml)?$/i.exec(path)?.[1] ?? '1';
2098
+ }
2099
+ function work_xlsx_images_escapeXml(value) {
2100
+ return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&apos;');
2101
+ }
2102
+ const OOXML_TO_FRIENDLY_TOKEN = {
2103
+ P: '{page}',
2104
+ N: '{pages}',
2105
+ A: '{sheet}',
2106
+ F: '{file}',
2107
+ Z: '{path}',
2108
+ D: '{date}',
2109
+ T: '{time}'
2110
+ };
2111
+ const FRIENDLY_TO_OOXML_TOKEN = Object.fromEntries(Object.entries(OOXML_TO_FRIENDLY_TOKEN).map(([ooxml, friendly])=>[
2112
+ friendly,
2113
+ `&${ooxml}`
2114
+ ]));
2115
+ const RICH_FORMAT_CODES = new Set([
2116
+ 'B',
2117
+ 'I',
2118
+ 'U',
2119
+ 'E',
2120
+ 'S',
2121
+ 'X',
2122
+ 'Y',
2123
+ 'O',
2124
+ 'H',
2125
+ '+',
2126
+ '-'
2127
+ ]);
2128
+ function parseXlsxHeaderFooterSections(source) {
2129
+ if (!source) return;
2130
+ const sections = {
2131
+ left: '',
2132
+ center: '',
2133
+ right: ''
2134
+ };
2135
+ let section = 'center';
2136
+ for(let index = 0; index < source.length; index += 1){
2137
+ if ('&' !== source[index] || index === source.length - 1) {
2138
+ sections[section] += source[index];
2139
+ continue;
2140
+ }
2141
+ const code = source[index + 1];
2142
+ if ('&' === code) {
2143
+ sections[section] += '&';
2144
+ index += 1;
2145
+ continue;
2146
+ }
2147
+ if ('L' === code || 'C' === code || 'R' === code) {
2148
+ section = 'L' === code ? 'left' : 'C' === code ? 'center' : 'right';
2149
+ index += 1;
2150
+ continue;
2151
+ }
2152
+ const friendlyToken = OOXML_TO_FRIENDLY_TOKEN[code];
2153
+ if (friendlyToken) {
2154
+ sections[section] += friendlyToken;
2155
+ index += 1;
2156
+ continue;
2157
+ }
2158
+ const formattingEnd = formattingCodeEnd(source, index);
2159
+ if (null !== formattingEnd || 'G' === code) {
2160
+ index = formattingEnd ?? index + 1;
2161
+ continue;
2162
+ }
2163
+ sections[section] += `&${code}`;
2164
+ index += 1;
2165
+ }
2166
+ return Object.values(sections).some(Boolean) ? sections : void 0;
2167
+ }
2168
+ function serializeXlsxHeaderFooterSections(sections) {
2169
+ if (!sections) return '';
2170
+ return [
2171
+ [
2172
+ 'L',
2173
+ sections.left
2174
+ ],
2175
+ [
2176
+ 'C',
2177
+ sections.center
2178
+ ],
2179
+ [
2180
+ 'R',
2181
+ sections.right
2182
+ ]
2183
+ ].flatMap(([code, template])=>template ? [
2184
+ `&${code}${serializeTemplate(template)}`
2185
+ ] : []).join('');
2186
+ }
2187
+ function readXlsxHeaderFooter(element) {
2188
+ if (!element) return {};
2189
+ const settings = {};
2190
+ const header = parseXlsxHeaderFooterSections(directChild(element, 'oddHeader')?.textContent);
2191
+ const footer = parseXlsxHeaderFooterSections(directChild(element, 'oddFooter')?.textContent);
2192
+ if (header) settings.header = header;
2193
+ if (footer) settings.footer = footer;
2194
+ if (null !== attribute(element, 'scaleWithDoc')) settings.scaleWithDocument = work_xlsx_header_footer_booleanAttribute(element, 'scaleWithDoc');
2195
+ if (null !== attribute(element, 'alignWithMargins')) settings.alignWithMargins = work_xlsx_header_footer_booleanAttribute(element, 'alignWithMargins');
2196
+ return settings;
2197
+ }
2198
+ function writeXlsxHeaderFooter(document, pageSetup) {
2199
+ const header = serializeXlsxHeaderFooterSections(pageSetup.header);
2200
+ const footer = serializeXlsxHeaderFooterSections(pageSetup.footer);
2201
+ if (!header && !footer && void 0 === pageSetup.scaleWithDocument && void 0 === pageSetup.alignWithMargins) return null;
2202
+ const element = document.createElementNS(document.documentElement.namespaceURI, 'headerFooter');
2203
+ if (void 0 !== pageSetup.scaleWithDocument) element.setAttribute('scaleWithDoc', pageSetup.scaleWithDocument ? '1' : '0');
2204
+ if (void 0 !== pageSetup.alignWithMargins) element.setAttribute('alignWithMargins', pageSetup.alignWithMargins ? '1' : '0');
2205
+ appendTextChild(document, element, 'oddHeader', header);
2206
+ appendTextChild(document, element, 'oddFooter', footer);
2207
+ return element;
2208
+ }
2209
+ function inspectXlsxHeaderFooterText(source) {
2210
+ const inspection = {
2211
+ hasFormatting: false,
2212
+ hasImage: false
2213
+ };
2214
+ if (!source) return inspection;
2215
+ for(let index = 0; index < source.length - 1; index += 1){
2216
+ if ('&' !== source[index]) continue;
2217
+ const code = source[index + 1];
2218
+ if ('&' === code) {
2219
+ index += 1;
2220
+ continue;
2221
+ }
2222
+ if ('G' === code) inspection.hasImage = true;
2223
+ if (null !== formattingCodeEnd(source, index)) inspection.hasFormatting = true;
2224
+ }
2225
+ return inspection;
2226
+ }
2227
+ function serializeTemplate(template) {
2228
+ const escaped = template.replaceAll('&', '&&');
2229
+ return escaped.replace(/\{(?:page|pages|sheet|file|path|date|time)\}/g, (token)=>FRIENDLY_TO_OOXML_TOKEN[token] ?? token);
2230
+ }
2231
+ function formattingCodeEnd(source, ampersandIndex) {
2232
+ const code = source[ampersandIndex + 1];
2233
+ if (RICH_FORMAT_CODES.has(code)) return ampersandIndex + 1;
2234
+ if (/^\d$/.test(code)) {
2235
+ let end = ampersandIndex + 1;
2236
+ while(end + 1 < source.length && /^\d$/.test(source[end + 1]))end += 1;
2237
+ return end;
2238
+ }
2239
+ if ('"' === code) {
2240
+ const closingQuote = source.indexOf('"', ampersandIndex + 2);
2241
+ return closingQuote < 0 ? source.length - 1 : closingQuote;
2242
+ }
2243
+ if ('K' === code) {
2244
+ let end = ampersandIndex + 1;
2245
+ while(end + 1 < source.length && end < ampersandIndex + 7 && /^[0-9A-Fa-f+-]$/.test(source[end + 1]))end += 1;
2246
+ return end;
2247
+ }
2248
+ return null;
2249
+ }
2250
+ function appendTextChild(document, parent, name, value) {
2251
+ if (!value) return;
2252
+ const child = document.createElementNS(document.documentElement.namespaceURI, name);
2253
+ child.textContent = value;
2254
+ parent.append(child);
2255
+ }
2256
+ function work_xlsx_header_footer_booleanAttribute(element, name) {
2257
+ const value = attribute(element, name)?.toLowerCase();
2258
+ return '1' === value || 'true' === value;
2259
+ }
2260
+ const PAPER_SIZE_CODES = {
2261
+ letter: '1',
2262
+ tabloid: '3',
2263
+ legal: '5',
2264
+ a3: '8',
2265
+ a4: '9',
2266
+ a5: '11'
2267
+ };
2268
+ const PAPER_SIZES_BY_CODE = new Map(Object.entries(PAPER_SIZE_CODES).map(([paperSize, code])=>[
2269
+ code,
2270
+ paperSize
2271
+ ]));
2272
+ function readXlsxPageSetup(document) {
2273
+ const root = document.documentElement;
2274
+ const pageSetupElement = directChild(root, 'pageSetup');
2275
+ const marginsElement = directChild(root, 'pageMargins');
2276
+ const printOptions = directChild(root, 'printOptions');
2277
+ const headerFooterElement = directChild(root, 'headerFooter');
2278
+ const sheetProperties = directChild(root, 'sheetPr');
2279
+ const setupProperties = sheetProperties ? directChild(sheetProperties, 'pageSetUpPr') : void 0;
2280
+ if (!pageSetupElement && !marginsElement && !printOptions && !setupProperties && !headerFooterElement) return;
2281
+ const pageSetup = {};
2282
+ const paperSize = pageSetupElement ? attribute(pageSetupElement, 'paperSize') : null;
2283
+ const editablePaperSize = paperSize ? PAPER_SIZES_BY_CODE.get(paperSize) : void 0;
2284
+ if (editablePaperSize) pageSetup.paperSize = editablePaperSize;
2285
+ const orientation = pageSetupElement ? attribute(pageSetupElement, 'orientation')?.toLowerCase() : void 0;
2286
+ if ('portrait' === orientation || 'landscape' === orientation) pageSetup.orientation = orientation;
2287
+ const scale = boundedIntegerAttribute(pageSetupElement, 'scale', 10, 400);
2288
+ if (void 0 !== scale) pageSetup.scale = scale;
2289
+ const fitToWidth = boundedIntegerAttribute(pageSetupElement, 'fitToWidth', 0, 32767);
2290
+ if (void 0 !== fitToWidth) pageSetup.fitToWidth = fitToWidth;
2291
+ const fitToHeight = boundedIntegerAttribute(pageSetupElement, 'fitToHeight', 0, 32767);
2292
+ if (void 0 !== fitToHeight) pageSetup.fitToHeight = fitToHeight;
2293
+ const pageOrder = pageSetupElement ? attribute(pageSetupElement, 'pageOrder') : null;
2294
+ if ('downThenOver' === pageOrder || 'overThenDown' === pageOrder) pageSetup.pageOrder = pageOrder;
2295
+ const pageNumberStart = boundedIntegerAttribute(pageSetupElement, 'firstPageNumber', 1, 32767);
2296
+ const useFirstPageNumber = pageSetupElement ? attribute(pageSetupElement, 'useFirstPageNumber') : null;
2297
+ if (void 0 !== pageNumberStart && (null === useFirstPageNumber || work_xlsx_page_setup_booleanAttribute(pageSetupElement, 'useFirstPageNumber'))) pageSetup.pageNumberStart = pageNumberStart;
2298
+ if (setupProperties && null !== attribute(setupProperties, 'fitToPage')) pageSetup.fitToPage = work_xlsx_page_setup_booleanAttribute(setupProperties, 'fitToPage');
2299
+ if (printOptions && null !== attribute(printOptions, 'horizontalCentered')) pageSetup.horizontalCentered = work_xlsx_page_setup_booleanAttribute(printOptions, 'horizontalCentered');
2300
+ if (printOptions && null !== attribute(printOptions, 'verticalCentered')) pageSetup.verticalCentered = work_xlsx_page_setup_booleanAttribute(printOptions, 'verticalCentered');
2301
+ const margins = readMargins(marginsElement);
2302
+ if (margins) pageSetup.margins = margins;
2303
+ Object.assign(pageSetup, readXlsxHeaderFooter(headerFooterElement));
2304
+ return pageSetup;
2305
+ }
2306
+ function writeXlsxPageSetup(document, pageSetup) {
2307
+ const root = document.documentElement;
2308
+ for (const existing of directChildren(root).filter((element)=>[
2309
+ 'printOptions',
2310
+ 'pageMargins',
2311
+ 'pageSetup',
2312
+ 'headerFooter'
2313
+ ].includes(element.localName)))existing.remove();
2314
+ writeFitToPage(document, pageSetup?.fitToPage);
2315
+ if (!pageSetup) return;
2316
+ const elements = [];
2317
+ if (void 0 !== pageSetup.horizontalCentered || void 0 !== pageSetup.verticalCentered) {
2318
+ const printOptions = document.createElementNS(root.namespaceURI, 'printOptions');
2319
+ if (void 0 !== pageSetup.horizontalCentered) printOptions.setAttribute('horizontalCentered', pageSetup.horizontalCentered ? '1' : '0');
2320
+ if (void 0 !== pageSetup.verticalCentered) printOptions.setAttribute('verticalCentered', pageSetup.verticalCentered ? '1' : '0');
2321
+ elements.push(printOptions);
2322
+ }
2323
+ if (pageSetup.margins) elements.push(writeMargins(document, pageSetup.margins));
2324
+ const setup = document.createElementNS(root.namespaceURI, 'pageSetup');
2325
+ if (pageSetup.paperSize) setup.setAttribute('paperSize', PAPER_SIZE_CODES[normalizeSpreadsheetPaperSize(pageSetup.paperSize)]);
2326
+ if (pageSetup.orientation) setup.setAttribute('orientation', pageSetup.orientation);
2327
+ if (void 0 !== pageSetup.scale) setup.setAttribute('scale', String(pageSetup.scale));
2328
+ if (void 0 !== pageSetup.fitToWidth) setup.setAttribute('fitToWidth', String(pageSetup.fitToWidth));
2329
+ if (void 0 !== pageSetup.fitToHeight) setup.setAttribute('fitToHeight', String(pageSetup.fitToHeight));
2330
+ if (pageSetup.pageOrder) setup.setAttribute('pageOrder', pageSetup.pageOrder);
2331
+ if (void 0 !== pageSetup.pageNumberStart && Number.isInteger(pageSetup.pageNumberStart) && pageSetup.pageNumberStart >= 1 && pageSetup.pageNumberStart <= 32767) {
2332
+ setup.setAttribute('firstPageNumber', String(pageSetup.pageNumberStart));
2333
+ setup.setAttribute('useFirstPageNumber', '1');
2334
+ }
2335
+ if (setup.attributes.length) elements.push(setup);
2336
+ const headerFooter = writeXlsxHeaderFooter(document, pageSetup);
2337
+ if (headerFooter) elements.push(headerFooter);
2338
+ const anchor = directChildren(root).find((element)=>[
2339
+ 'rowBreaks',
2340
+ 'colBreaks',
2341
+ 'customProperties',
2342
+ 'cellWatches',
2343
+ 'ignoredErrors',
2344
+ 'smartTags',
2345
+ 'drawing',
2346
+ 'legacyDrawing',
2347
+ 'legacyDrawingHF',
2348
+ 'picture',
2349
+ 'oleObjects',
2350
+ 'controls',
2351
+ 'webPublishItems',
2352
+ 'tableParts',
2353
+ 'extLst'
2354
+ ].includes(element.localName)) ?? null;
2355
+ for (const element of elements)root.insertBefore(element, anchor);
2356
+ }
2357
+ function isEditableXlsxPaperSizeCode(value) {
2358
+ return PAPER_SIZES_BY_CODE.has(value);
2359
+ }
2360
+ function writeFitToPage(document, fitToPage) {
2361
+ const root = document.documentElement;
2362
+ let sheetProperties = directChild(root, 'sheetPr');
2363
+ let setupProperties = sheetProperties ? directChild(sheetProperties, 'pageSetUpPr') : void 0;
2364
+ if (void 0 !== fitToPage) {
2365
+ if (!sheetProperties) {
2366
+ sheetProperties = document.createElementNS(root.namespaceURI, 'sheetPr');
2367
+ root.insertBefore(sheetProperties, root.firstElementChild);
2368
+ }
2369
+ if (!setupProperties) {
2370
+ setupProperties = document.createElementNS(root.namespaceURI, 'pageSetUpPr');
2371
+ sheetProperties.append(setupProperties);
2372
+ }
2373
+ setupProperties.setAttribute('fitToPage', fitToPage ? '1' : '0');
2374
+ return;
2375
+ }
2376
+ setupProperties?.removeAttribute('fitToPage');
2377
+ if (setupProperties && !setupProperties.attributes.length && !setupProperties.children.length) setupProperties.remove();
2378
+ }
2379
+ function readMargins(element) {
2380
+ if (!element) return null;
2381
+ const values = [
2382
+ 'top',
2383
+ 'right',
2384
+ 'bottom',
2385
+ 'left',
2386
+ 'header',
2387
+ 'footer'
2388
+ ].map((name)=>nonnegativeNumberAttribute(element, name));
2389
+ if (values.some((value)=>null === value)) return null;
2390
+ const [top, right, bottom, left, header, footer] = values;
2391
+ return {
2392
+ top: inchesToMillimeters(top),
2393
+ right: inchesToMillimeters(right),
2394
+ bottom: inchesToMillimeters(bottom),
2395
+ left: inchesToMillimeters(left),
2396
+ header: inchesToMillimeters(header),
2397
+ footer: inchesToMillimeters(footer)
2398
+ };
2399
+ }
2400
+ function writeMargins(document, margins) {
2401
+ const element = document.createElementNS(document.documentElement.namespaceURI, 'pageMargins');
2402
+ for (const [name, value] of Object.entries({
2403
+ left: margins.left,
2404
+ right: margins.right,
2405
+ top: margins.top,
2406
+ bottom: margins.bottom,
2407
+ header: margins.header,
2408
+ footer: margins.footer
2409
+ }))element.setAttribute(name, millimetersToInches(value));
2410
+ return element;
2411
+ }
2412
+ function boundedIntegerAttribute(element, name, minimum, maximum) {
2413
+ if (!element) return;
2414
+ const source = attribute(element, name);
2415
+ if (null === source) return;
2416
+ const value = Number(source);
2417
+ return Number.isInteger(value) && value >= minimum && value <= maximum ? value : void 0;
2418
+ }
2419
+ function nonnegativeNumberAttribute(element, name) {
2420
+ const source = attribute(element, name);
2421
+ if (null === source) return null;
2422
+ const value = Number(source);
2423
+ return Number.isFinite(value) && value >= 0 ? value : null;
2424
+ }
2425
+ function work_xlsx_page_setup_booleanAttribute(element, name) {
2426
+ const value = element ? attribute(element, name)?.toLowerCase() : void 0;
2427
+ return '1' === value || 'true' === value;
2428
+ }
2429
+ function inchesToMillimeters(value) {
2430
+ return Number((25.4 * value).toFixed(4));
2431
+ }
2432
+ function millimetersToInches(value) {
2433
+ return String(Number((value / 25.4).toFixed(4)));
2434
+ }
2435
+ function readXlsxProtection(worksheet, styles) {
2436
+ const protectedRanges = readProtectedRanges(worksheet);
2437
+ const protection = directChild(worksheet.documentElement, 'sheetProtection');
2438
+ const authority = protection || protectedRanges.length ? {
2439
+ ...authorityFromElement(protection),
2440
+ allowRangeList: protectedRanges
2441
+ } : void 0;
2442
+ return {
2443
+ authority,
2444
+ cellProtectionRanges: readCellProtectionRanges(worksheet, styles)
2445
+ };
2446
+ }
2447
+ function writeXlsxProtection(worksheet, sheet, styles) {
2448
+ const root = worksheet.documentElement;
2449
+ for (const element of directChildren(root, 'sheetProtection'))element.remove();
2450
+ for (const element of directChildren(root, 'protectedRanges'))element.remove();
2451
+ const authority = normalizeSheetProtectionAuthority(sheet.config?.authority);
2452
+ if (1 === authority.sheet) {
2453
+ const element = worksheet.createElementNS(root.namespaceURI, 'sheetProtection');
2454
+ copyAttributes(element, authority.xlsxAttributes);
2455
+ writeProtectionAttributes(element, authority);
2456
+ insertWorksheetFeature(root, element, [
2457
+ 'protectedRanges',
2458
+ 'scenarios',
2459
+ 'autoFilter',
2460
+ 'sortState',
2461
+ 'dataConsolidate',
2462
+ 'customSheetViews',
2463
+ 'mergeCells',
2464
+ 'phoneticPr',
2465
+ 'conditionalFormatting',
2466
+ 'dataValidations',
2467
+ 'hyperlinks',
2468
+ 'printOptions',
2469
+ 'pageMargins',
2470
+ 'pageSetup',
2471
+ 'headerFooter',
2472
+ 'drawing',
2473
+ 'legacyDrawing',
2474
+ 'ignoredErrors',
2475
+ 'extLst'
2476
+ ]);
2477
+ }
2478
+ writeProtectedRanges(worksheet, [
2479
+ ...authority.allowRangeList,
2480
+ ...generatedEditableProtectionRanges(authority)
2481
+ ]);
2482
+ if (styles) writeCellProtectionStyles(worksheet, sheet, styles);
2483
+ }
2484
+ class XlsxCellProtectionWriter {
2485
+ styles;
2486
+ cellXfs;
2487
+ generated = new Map();
2488
+ changed = false;
2489
+ constructor(styles){
2490
+ this.styles = styles;
2491
+ const root = styles.documentElement;
2492
+ this.cellXfs = directChild(root, 'cellXfs') ?? styles.createElementNS(root.namespaceURI, 'cellXfs');
2493
+ if (!this.cellXfs.parentElement) {
2494
+ const anchor = directChildren(root).find((child)=>[
2495
+ 'cellStyles',
2496
+ 'dxfs',
2497
+ 'tableStyles',
2498
+ 'colors',
2499
+ 'extLst'
2500
+ ].includes(child.localName));
2501
+ root.insertBefore(this.cellXfs, anchor ?? null);
2502
+ }
2503
+ if (!directChildren(this.cellXfs, 'xf').length) {
2504
+ const base = styles.createElementNS(root.namespaceURI, 'xf');
2505
+ base.setAttribute('numFmtId', '0');
2506
+ base.setAttribute('fontId', '0');
2507
+ base.setAttribute('fillId', '0');
2508
+ base.setAttribute('borderId', '0');
2509
+ base.setAttribute('xfId', '0');
2510
+ this.cellXfs.append(base);
2511
+ this.changed = true;
2512
+ }
2513
+ this.updateCount();
2514
+ }
2515
+ styleId(baseStyleId, locked, hidden) {
2516
+ const styles = directChildren(this.cellXfs, 'xf');
2517
+ const baseIndex = Number.isInteger(baseStyleId) && styles[baseStyleId] ? baseStyleId : 0;
2518
+ if (sameProtection(readProtectionStyle(styles[baseIndex]), {
2519
+ locked,
2520
+ hidden
2521
+ })) return baseIndex;
2522
+ const key = `${baseIndex}:${locked ? 1 : 0}:${hidden ? 1 : 0}`;
2523
+ const cached = this.generated.get(key);
2524
+ if (void 0 !== cached) return cached;
2525
+ const clone = styles[baseIndex].cloneNode(true);
2526
+ for (const element of directChildren(clone, 'protection'))element.remove();
2527
+ clone.setAttribute('applyProtection', '1');
2528
+ const protection = this.styles.createElementNS(this.styles.documentElement.namespaceURI, 'protection');
2529
+ protection.setAttribute('locked', locked ? '1' : '0');
2530
+ protection.setAttribute('hidden', hidden ? '1' : '0');
2531
+ clone.insertBefore(protection, directChild(clone, 'extLst') ?? null);
2532
+ const index = styles.length;
2533
+ this.cellXfs.append(clone);
2534
+ this.generated.set(key, index);
2535
+ this.changed = true;
2536
+ this.updateCount();
2537
+ return index;
2538
+ }
2539
+ serialize() {
2540
+ return new XMLSerializer().serializeToString(this.styles);
2541
+ }
2542
+ updateCount() {
2543
+ this.cellXfs.setAttribute('count', String(directChildren(this.cellXfs, 'xf').length));
2544
+ }
2545
+ }
2546
+ function diagnoseXlsxProtection(worksheet, styles) {
2547
+ const protection = directChild(worksheet.documentElement, 'sheetProtection');
2548
+ const protectedRanges = readProtectedRanges(worksheet);
2549
+ if (!protection && !protectedRanges.length) return [];
2550
+ const diagnostics = [
2551
+ {
2552
+ code: 'xlsx.protection',
2553
+ message: 'Sheet lock state, locked and unlocked cells, selection rules, and passwordless editable ranges are preserved and enforced.',
2554
+ severity: 'info'
2555
+ }
2556
+ ];
2557
+ const sourceAttributes = protection ? work_xlsx_protection_attributes(protection) : {};
2558
+ if (hasCredentialAttributes(sourceAttributes)) diagnostics.push({
2559
+ code: 'xlsx.protection.password',
2560
+ message: 'The source password verifier is preserved, but Work cannot authenticate it; locked cells remain protected.',
2561
+ severity: 'warning'
2562
+ });
2563
+ if (protectedRanges.some(editableRangeRequiresCredentials)) diagnostics.push({
2564
+ code: 'xlsx.protection.range-credentials',
2565
+ message: 'Password- or permission-backed editable ranges are preserved but remain locked because Work cannot authenticate their credentials.',
2566
+ severity: 'warning'
2567
+ });
2568
+ if (protection) diagnostics.push({
2569
+ code: 'xlsx.protection.permissions',
2570
+ message: 'Action-specific protection permissions are preserved; Work enforces cell editing and selection but not every Excel row, column, object, filter, or pivot permission.',
2571
+ severity: 'warning'
2572
+ });
2573
+ if (hasHiddenFormula(worksheet, styles)) diagnostics.push({
2574
+ code: 'xlsx.protection.hidden-formulas',
2575
+ message: 'Formula-hidden protection is preserved for XLSX export, but formulas can still appear in the Work formula bar.',
2576
+ severity: 'warning'
2577
+ });
2578
+ return diagnostics;
2579
+ }
2580
+ function authorityFromElement(element) {
2581
+ const defaults = normalizeSheetProtectionAuthority(void 0);
2582
+ if (!element) return defaults;
2583
+ const source = work_xlsx_protection_attributes(element);
2584
+ return {
2585
+ ...defaults,
2586
+ sheet: work_xlsx_protection_booleanAttribute(element, 'sheet', false) ? 1 : 0,
2587
+ selectLockedCells: work_xlsx_protection_booleanAttribute(element, 'selectLockedCells', false) ? 0 : 1,
2588
+ selectunLockedCells: work_xlsx_protection_booleanAttribute(element, 'selectUnlockedCells', false) ? 0 : 1,
2589
+ formatCells: work_xlsx_protection_booleanAttribute(element, 'formatCells', true) ? 0 : 1,
2590
+ formatColumns: work_xlsx_protection_booleanAttribute(element, 'formatColumns', true) ? 0 : 1,
2591
+ formatRows: work_xlsx_protection_booleanAttribute(element, 'formatRows', true) ? 0 : 1,
2592
+ insertColumns: work_xlsx_protection_booleanAttribute(element, 'insertColumns', true) ? 0 : 1,
2593
+ insertRows: work_xlsx_protection_booleanAttribute(element, 'insertRows', true) ? 0 : 1,
2594
+ insertHyperlinks: work_xlsx_protection_booleanAttribute(element, 'insertHyperlinks', true) ? 0 : 1,
2595
+ deleteColumns: work_xlsx_protection_booleanAttribute(element, 'deleteColumns', true) ? 0 : 1,
2596
+ deleteRows: work_xlsx_protection_booleanAttribute(element, 'deleteRows', true) ? 0 : 1,
2597
+ sort: work_xlsx_protection_booleanAttribute(element, 'sort', true) ? 0 : 1,
2598
+ filter: work_xlsx_protection_booleanAttribute(element, 'autoFilter', true) ? 0 : 1,
2599
+ usePivotTablereports: work_xlsx_protection_booleanAttribute(element, 'pivotTables', true) ? 0 : 1,
2600
+ editObjects: work_xlsx_protection_booleanAttribute(element, 'objects', false) ? 0 : 1,
2601
+ editScenarios: work_xlsx_protection_booleanAttribute(element, 'scenarios', false) ? 0 : 1,
2602
+ defaultSheetHintText: DEFAULT_PROTECTION_HINT,
2603
+ xlsxAttributes: Object.keys(source).length ? source : void 0
2604
+ };
2605
+ }
2606
+ function readProtectedRanges(worksheet) {
2607
+ const container = directChild(worksheet.documentElement, 'protectedRanges');
2608
+ if (!container) return [];
2609
+ return directChildren(container, 'protectedRange').flatMap((element, index)=>{
2610
+ const ranges = parseSqref(attribute(element, 'sqref'));
2611
+ if (!ranges.length) return [];
2612
+ return [
2613
+ {
2614
+ name: attribute(element, 'name')?.trim() || `Range ${index + 1}`,
2615
+ sqref: formatSpreadsheetCellRanges(ranges),
2616
+ xlsxAttributes: work_xlsx_protection_attributes(element)
2617
+ }
2618
+ ];
2619
+ });
2620
+ }
2621
+ function readCellProtectionRanges(worksheet, styles) {
2622
+ const xfs = styles ? directChildren(firstDescendant(styles, 'cellXfs') ?? styles.documentElement, 'xf') : [];
2623
+ if (!xfs.length) return [];
2624
+ const styleList = xfs.map(readProtectionStyle);
2625
+ const bounds = worksheetBounds(worksheet);
2626
+ const ranges = [];
2627
+ if (!sameProtection(styleList[0], {
2628
+ locked: true,
2629
+ hidden: false
2630
+ })) ranges.push({
2631
+ range: bounds,
2632
+ ...styleList[0]
2633
+ });
2634
+ for (const column of descendants(worksheet, 'col')){
2635
+ const style = indexedStyle(styleList, attribute(column, 'style'));
2636
+ const minimum = work_xlsx_protection_positiveInteger(attribute(column, 'min'));
2637
+ const maximum = work_xlsx_protection_positiveInteger(attribute(column, 'max'));
2638
+ if (style && null !== minimum && null !== maximum) ranges.push({
2639
+ range: {
2640
+ row: [
2641
+ ...bounds.row
2642
+ ],
2643
+ column: [
2644
+ minimum - 1,
2645
+ maximum - 1
2646
+ ]
2647
+ },
2648
+ ...style
2649
+ });
2650
+ }
2651
+ for (const row of descendants(worksheet, 'row')){
2652
+ const style = indexedStyle(styleList, attribute(row, 's'));
2653
+ const rowNumber = work_xlsx_protection_positiveInteger(attribute(row, 'r'));
2654
+ if (style && null !== rowNumber) ranges.push({
2655
+ range: {
2656
+ row: [
2657
+ rowNumber - 1,
2658
+ rowNumber - 1
2659
+ ],
2660
+ column: [
2661
+ ...bounds.column
2662
+ ]
2663
+ },
2664
+ ...style
2665
+ });
2666
+ }
2667
+ for (const cell of descendants(worksheet, 'c')){
2668
+ const style = indexedStyle(styleList, attribute(cell, 's'));
2669
+ const coordinate = parseCellReference(attribute(cell, 'r'));
2670
+ if (style && coordinate) ranges.push({
2671
+ range: {
2672
+ row: [
2673
+ coordinate.row,
2674
+ coordinate.row
2675
+ ],
2676
+ column: [
2677
+ coordinate.column,
2678
+ coordinate.column
2679
+ ]
2680
+ },
2681
+ ...style
2682
+ });
2683
+ }
2684
+ return ranges;
2685
+ }
2686
+ function writeProtectionAttributes(element, authority) {
2687
+ element.setAttribute('sheet', '1');
2688
+ element.setAttribute('selectLockedCells', authority.selectLockedCells ? '0' : '1');
2689
+ element.setAttribute('selectUnlockedCells', authority.selectunLockedCells ? '0' : '1');
2690
+ const mappings = [
2691
+ [
2692
+ 'formatCells',
2693
+ 'formatCells'
2694
+ ],
2695
+ [
2696
+ 'formatColumns',
2697
+ 'formatColumns'
2698
+ ],
2699
+ [
2700
+ 'formatRows',
2701
+ 'formatRows'
2702
+ ],
2703
+ [
2704
+ 'insertColumns',
2705
+ 'insertColumns'
2706
+ ],
2707
+ [
2708
+ 'insertRows',
2709
+ 'insertRows'
2710
+ ],
2711
+ [
2712
+ 'insertHyperlinks',
2713
+ 'insertHyperlinks'
2714
+ ],
2715
+ [
2716
+ 'deleteColumns',
2717
+ 'deleteColumns'
2718
+ ],
2719
+ [
2720
+ 'deleteRows',
2721
+ 'deleteRows'
2722
+ ],
2723
+ [
2724
+ 'sort',
2725
+ 'sort'
2726
+ ],
2727
+ [
2728
+ 'filter',
2729
+ 'autoFilter'
2730
+ ],
2731
+ [
2732
+ 'usePivotTablereports',
2733
+ 'pivotTables'
2734
+ ],
2735
+ [
2736
+ 'editObjects',
2737
+ 'objects'
2738
+ ],
2739
+ [
2740
+ 'editScenarios',
2741
+ 'scenarios'
2742
+ ]
2743
+ ];
2744
+ for (const [key, name] of mappings)element.setAttribute(name, authority[key] ? '0' : '1');
2745
+ }
2746
+ function writeProtectedRanges(document, ranges) {
2747
+ const root = document.documentElement;
2748
+ const container = document.createElementNS(root.namespaceURI, 'protectedRanges');
2749
+ const written = new Set();
2750
+ for (const range of ranges){
2751
+ const parsed = parseSpreadsheetCellRanges(range.sqref);
2752
+ if (!range.name.trim() || !parsed?.length) continue;
2753
+ const reference = formatSpreadsheetCellRanges(parsed).replaceAll(',', ' ');
2754
+ if (written.has(reference)) continue;
2755
+ written.add(reference);
2756
+ const element = document.createElementNS(root.namespaceURI, 'protectedRange');
2757
+ copyAttributes(element, range.xlsxAttributes);
2758
+ element.setAttribute('name', range.name.trim());
2759
+ element.setAttribute('sqref', reference);
2760
+ container.append(element);
2761
+ }
2762
+ if (!container.children.length) return;
2763
+ insertWorksheetFeature(root, container, [
2764
+ 'scenarios',
2765
+ 'autoFilter',
2766
+ 'sortState',
2767
+ 'dataConsolidate',
2768
+ 'customSheetViews',
2769
+ 'mergeCells',
2770
+ 'phoneticPr',
2771
+ 'conditionalFormatting',
2772
+ 'dataValidations',
2773
+ 'hyperlinks',
2774
+ 'printOptions',
2775
+ 'pageMargins',
2776
+ 'pageSetup',
2777
+ 'headerFooter',
2778
+ 'drawing',
2779
+ 'legacyDrawing',
2780
+ 'ignoredErrors',
2781
+ 'extLst'
2782
+ ]);
2783
+ }
2784
+ function writeCellProtectionStyles(worksheet, sheet, styles) {
2785
+ const authority = normalizeSheetProtectionAuthority(sheet.config?.authority);
2786
+ for (const [row, values] of sparseArrayEntries(sheet.data))for (const [column, source] of sparseArrayEntries(values)){
2787
+ const cell = source;
2788
+ if (!cell) continue;
2789
+ const compact = compactProtectionAt(authority, row, column);
2790
+ if (void 0 === cell.lo && void 0 === cell.hi && void 0 === compact) continue;
2791
+ const element = ensureCellElement(worksheet, row, column);
2792
+ const baseStyle = nonNegativeInteger(attribute(element, 's')) ?? 0;
2793
+ const styleId = styles.styleId(baseStyle, void 0 === cell.lo ? compact?.locked ?? true : 0 !== cell.lo, void 0 === cell.hi ? compact?.hidden ?? false : 1 === cell.hi);
2794
+ if (styleId) element.setAttribute('s', String(styleId));
2795
+ else element.removeAttribute('s');
2796
+ }
2797
+ }
2798
+ function generatedEditableProtectionRanges(authority) {
2799
+ let editable = [];
2800
+ for (const item of authority.cellProtectionRanges)if (item.locked) editable = editable.flatMap((range)=>subtractSpreadsheetRange(range, item.range));
2801
+ else editable.push(item.range);
2802
+ return editable.map((range, index)=>({
2803
+ name: `Work editable range ${index + 1}`,
2804
+ sqref: formatSpreadsheetCellRanges([
2805
+ range
2806
+ ])
2807
+ }));
2808
+ }
2809
+ function compactProtectionAt(authority, row, column) {
2810
+ for(let index = authority.cellProtectionRanges.length - 1; index >= 0; index -= 1){
2811
+ const item = authority.cellProtectionRanges[index];
2812
+ if (row >= item.range.row[0] && row <= item.range.row[1] && column >= item.range.column[0] && column <= item.range.column[1]) return {
2813
+ locked: item.locked,
2814
+ hidden: item.hidden
2815
+ };
2816
+ }
2817
+ }
2818
+ function subtractSpreadsheetRange(source, removed) {
2819
+ const top = Math.max(source.row[0], removed.row[0]);
2820
+ const bottom = Math.min(source.row[1], removed.row[1]);
2821
+ const left = Math.max(source.column[0], removed.column[0]);
2822
+ const right = Math.min(source.column[1], removed.column[1]);
2823
+ if (top > bottom || left > right) return [
2824
+ source
2825
+ ];
2826
+ const remaining = [];
2827
+ if (source.row[0] < top) remaining.push({
2828
+ row: [
2829
+ source.row[0],
2830
+ top - 1
2831
+ ],
2832
+ column: [
2833
+ ...source.column
2834
+ ]
2835
+ });
2836
+ if (bottom < source.row[1]) remaining.push({
2837
+ row: [
2838
+ bottom + 1,
2839
+ source.row[1]
2840
+ ],
2841
+ column: [
2842
+ ...source.column
2843
+ ]
2844
+ });
2845
+ if (source.column[0] < left) remaining.push({
2846
+ row: [
2847
+ top,
2848
+ bottom
2849
+ ],
2850
+ column: [
2851
+ source.column[0],
2852
+ left - 1
2853
+ ]
2854
+ });
2855
+ if (right < source.column[1]) remaining.push({
2856
+ row: [
2857
+ top,
2858
+ bottom
2859
+ ],
2860
+ column: [
2861
+ right + 1,
2862
+ source.column[1]
2863
+ ]
2864
+ });
2865
+ return remaining;
2866
+ }
2867
+ function ensureCellElement(document, rowIndex, columnIndex) {
2868
+ const sheetData = directChild(document.documentElement, 'sheetData');
2869
+ if (!sheetData) throw new Error('XLSX worksheet does not contain sheetData');
2870
+ const rowNumber = rowIndex + 1;
2871
+ let row = directChildren(sheetData, 'row').find((element)=>attribute(element, 'r') === String(rowNumber));
2872
+ if (!row) {
2873
+ row = document.createElementNS(document.documentElement.namespaceURI, 'row');
2874
+ row.setAttribute('r', String(rowNumber));
2875
+ const anchor = directChildren(sheetData, 'row').find((element)=>(work_xlsx_protection_positiveInteger(attribute(element, 'r')) ?? Number.MAX_SAFE_INTEGER) > rowNumber);
2876
+ sheetData.insertBefore(row, anchor ?? null);
2877
+ }
2878
+ const reference = encodeCell(rowIndex, columnIndex);
2879
+ let cell = directChildren(row, 'c').find((element)=>attribute(element, 'r') === reference);
2880
+ if (!cell) {
2881
+ cell = document.createElementNS(document.documentElement.namespaceURI, 'c');
2882
+ cell.setAttribute('r', reference);
2883
+ const anchor = directChildren(row, 'c').find((element)=>{
2884
+ const coordinate = parseCellReference(attribute(element, 'r'));
2885
+ return coordinate ? coordinate.column > columnIndex : false;
2886
+ });
2887
+ row.insertBefore(cell, anchor ?? null);
2888
+ expandWorksheetDimension(document, rowIndex, columnIndex);
2889
+ }
2890
+ return cell;
2891
+ }
2892
+ function worksheetBounds(document) {
2893
+ const reference = attribute(directChild(document.documentElement, 'dimension') ?? document.documentElement, 'ref');
2894
+ const parsed = reference ? parseSpreadsheetCellRanges(reference)?.[0] : void 0;
2895
+ if (parsed) return {
2896
+ row: [
2897
+ 0,
2898
+ parsed.row[1]
2899
+ ],
2900
+ column: [
2901
+ 0,
2902
+ parsed.column[1]
2903
+ ]
2904
+ };
2905
+ const coordinates = descendants(document, 'c').map((cell)=>parseCellReference(attribute(cell, 'r'))).filter((value)=>Boolean(value));
2906
+ return {
2907
+ row: [
2908
+ 0,
2909
+ Math.max(0, ...coordinates.map((value)=>value.row))
2910
+ ],
2911
+ column: [
2912
+ 0,
2913
+ Math.max(0, ...coordinates.map((value)=>value.column))
2914
+ ]
2915
+ };
2916
+ }
2917
+ function expandWorksheetDimension(document, row, column) {
2918
+ const root = document.documentElement;
2919
+ let dimension = directChild(root, 'dimension');
2920
+ const bounds = worksheetBounds(document);
2921
+ bounds.row[1] = Math.max(bounds.row[1], row);
2922
+ bounds.column[1] = Math.max(bounds.column[1], column);
2923
+ if (!dimension) {
2924
+ dimension = document.createElementNS(root.namespaceURI, 'dimension');
2925
+ root.insertBefore(dimension, directChildren(root)[0] ?? null);
2926
+ }
2927
+ dimension.setAttribute('ref', `${encodeCell(bounds.row[0], bounds.column[0])}:${encodeCell(bounds.row[1], bounds.column[1])}`);
2928
+ }
2929
+ function readProtectionStyle(xf) {
2930
+ const protection = directChild(xf, 'protection');
2931
+ if (!protection || false === work_xlsx_protection_booleanAttribute(xf, 'applyProtection', true)) return {
2932
+ locked: true,
2933
+ hidden: false
2934
+ };
2935
+ return {
2936
+ locked: work_xlsx_protection_booleanAttribute(protection, 'locked', true),
2937
+ hidden: work_xlsx_protection_booleanAttribute(protection, 'hidden', false)
2938
+ };
2939
+ }
2940
+ function hasHiddenFormula(worksheet, styles) {
2941
+ const formulas = descendants(worksheet, 'c').filter((cell)=>Boolean(directChild(cell, 'f')));
2942
+ if (!formulas.length) return false;
2943
+ const ranges = readCellProtectionRanges(worksheet, styles);
2944
+ return formulas.some((cell)=>{
2945
+ const coordinate = parseCellReference(attribute(cell, 'r'));
2946
+ if (!coordinate) return false;
2947
+ let hidden = false;
2948
+ for (const item of ranges)if (coordinate.row >= item.range.row[0] && coordinate.row <= item.range.row[1] && coordinate.column >= item.range.column[0] && coordinate.column <= item.range.column[1]) hidden = item.hidden;
2949
+ return hidden;
2950
+ });
2951
+ }
2952
+ function insertWorksheetFeature(root, element, anchors) {
2953
+ root.insertBefore(element, directChildren(root).find((child)=>anchors.includes(child.localName)) ?? null);
2954
+ }
2955
+ function parseSqref(value) {
2956
+ return value ? parseSpreadsheetCellRanges(value.trim().replace(/\s+/g, ',')) ?? [] : [];
2957
+ }
2958
+ function indexedStyle(styles, value) {
2959
+ const index = nonNegativeInteger(value);
2960
+ return null === index ? null : styles[index] ?? null;
2961
+ }
2962
+ function parseCellReference(value) {
2963
+ const match = /^\$?([A-Z]{1,3})\$?([1-9]\d*)$/i.exec(value ?? '');
2964
+ if (!match) return null;
2965
+ let column = 0;
2966
+ for (const character of match[1].toUpperCase())column = 26 * column + character.charCodeAt(0) - 64;
2967
+ return {
2968
+ row: Number(match[2]) - 1,
2969
+ column: column - 1
2970
+ };
2971
+ }
2972
+ function encodeCell(row, column) {
2973
+ let value = column + 1;
2974
+ let label = '';
2975
+ while(value > 0){
2976
+ value -= 1;
2977
+ label = String.fromCharCode(65 + value % 26) + label;
2978
+ value = Math.floor(value / 26);
2979
+ }
2980
+ return `${label}${row + 1}`;
2981
+ }
2982
+ function work_xlsx_protection_attributes(element) {
2983
+ return Object.fromEntries(Array.from(element.attributes).map((item)=>[
2984
+ item.name,
2985
+ item.value
2986
+ ]));
2987
+ }
2988
+ function copyAttributes(element, source) {
2989
+ for (const [name, value] of Object.entries(source ?? {}))if (/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(name)) element.setAttribute(name, value);
2990
+ }
2991
+ function hasCredentialAttributes(source) {
2992
+ return Boolean(source.password || source.hashValue || source.saltValue || source.securityDescriptor);
2993
+ }
2994
+ function work_xlsx_protection_booleanAttribute(element, name, fallback) {
2995
+ const value = attribute(element, name)?.toLowerCase();
2996
+ if ('1' === value || 'true' === value || 'on' === value) return true;
2997
+ if ('0' === value || 'false' === value || 'off' === value) return false;
2998
+ return fallback;
2999
+ }
3000
+ function nonNegativeInteger(value) {
3001
+ if (null === value || !/^\d+$/.test(value)) return null;
3002
+ return Number(value);
3003
+ }
3004
+ function work_xlsx_protection_positiveInteger(value) {
3005
+ const parsed = nonNegativeInteger(value);
3006
+ return null !== parsed && parsed > 0 ? parsed : null;
3007
+ }
3008
+ function sameProtection(left, right) {
3009
+ return left.locked === right.locked && left.hidden === right.hidden;
3010
+ }
3011
+ const SPREADSHEET_NAMESPACE = 'http://schemas.openxmlformats.org/spreadsheetml/2006/main';
3012
+ const work_xlsx_pivot_xml_RELATIONSHIP_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
3013
+ function createXlsxPivotXmlParts(content, pivot, cacheId) {
3014
+ const sourceSheet = content.sheets.find((sheet)=>sheet.id === pivot.sourceSheetId);
3015
+ const ranges = parseSpreadsheetCellRanges(pivot.sourceReference);
3016
+ const fields = spreadsheetPivotFields(content, pivot);
3017
+ if (!sourceSheet || ranges?.length !== 1 || !fields.length || !pivot.outputReference) return null;
3018
+ const sourceRange = ranges[0];
3019
+ const dimensionIndexes = new Set([
3020
+ ...pivot.rowFields,
3021
+ ...pivot.columnFields,
3022
+ ...(pivot.reportFilters ?? []).map((filter)=>filter.fieldIndex)
3023
+ ]);
3024
+ const cacheFields = fields.map((field)=>{
3025
+ const values = [];
3026
+ for(let row = sourceRange.row[0] + 1; row <= sourceRange.row[1]; row += 1)values.push(pivotSourceValue(sourceSheet.data?.[row]?.[sourceRange.column[0] + field.index]));
3027
+ return {
3028
+ name: field.name,
3029
+ values,
3030
+ sharedItems: dimensionIndexes.has(field.index) ? uniqueSortedPivotValues(values) : void 0
3031
+ };
3032
+ });
3033
+ return {
3034
+ table: pivotTableXml(pivot, cacheFields, cacheId),
3035
+ cacheDefinition: pivotCacheDefinitionXml(sourceSheet, pivot, cacheFields),
3036
+ cacheRecords: pivotCacheRecordsXml(cacheFields)
3037
+ };
3038
+ }
3039
+ function xlsxPivotAggregation(aggregation) {
3040
+ if ('count' === aggregation) return 'countNums';
3041
+ if ('counta' === aggregation) return 'count';
3042
+ if ('stdDevP' === aggregation) return 'stdDevp';
3043
+ if ('varP' === aggregation) return 'varp';
3044
+ return aggregation;
3045
+ }
3046
+ function workPivotAggregation(value) {
3047
+ if (!value || 'sum' === value) return 'sum';
3048
+ if ('countNums' === value) return 'count';
3049
+ if ('count' === value) return 'counta';
3050
+ if ('average' === value) return 'average';
3051
+ if ('max' === value) return 'max';
3052
+ if ('min' === value) return 'min';
3053
+ if ('product' === value) return 'product';
3054
+ if ('stdDev' === value) return 'stdDev';
3055
+ if ('stdDevp' === value) return 'stdDevP';
3056
+ if ('var' === value) return 'var';
3057
+ if ('varp' === value) return 'varP';
3058
+ return null;
3059
+ }
3060
+ function pivotTableXml(pivot, fields, cacheId) {
3061
+ const dataFieldIndexes = new Set(pivot.values.map((value)=>value.fieldIndex));
3062
+ const reportFilterIndexes = new Set((pivot.reportFilters ?? []).map((filter)=>filter.fieldIndex));
3063
+ const pivotFields = fields.map((field, fieldIndex)=>{
3064
+ const row = pivot.rowFields.includes(fieldIndex);
3065
+ const column = pivot.columnFields.includes(fieldIndex);
3066
+ const page = reportFilterIndexes.has(fieldIndex);
3067
+ const attributes = [
3068
+ row ? 'axis="axisRow"' : '',
3069
+ column ? 'axis="axisCol"' : '',
3070
+ page ? 'axis="axisPage"' : '',
3071
+ dataFieldIndexes.has(fieldIndex) ? 'dataField="1"' : '',
3072
+ 'compact="0"',
3073
+ 'outline="0"',
3074
+ 'showAll="0"',
3075
+ 'defaultSubtotal="0"'
3076
+ ].filter(Boolean).join(' ');
3077
+ if (!row && !column && !page) return `<pivotField ${attributes}/>`;
3078
+ const items = field.sharedItems ?? [];
3079
+ return [
3080
+ `<pivotField ${attributes}>`,
3081
+ `<items count="${items.length + 1}">`,
3082
+ ...items.map((_item, index)=>`<item x="${index}"/>`),
3083
+ '<item t="default"/>',
3084
+ '</items>',
3085
+ '</pivotField>'
3086
+ ].join('');
3087
+ }).join('');
3088
+ const rowFields = pivot.rowFields.map((fieldIndex)=>`<field x="${fieldIndex}"/>`).join('');
3089
+ const columnFieldIndexes = [
3090
+ ...pivot.columnFields
3091
+ ];
3092
+ if (pivot.values.length > 1) columnFieldIndexes.push(-2);
3093
+ const columnFields = columnFieldIndexes.map((fieldIndex)=>`<field x="${fieldIndex}"/>`).join('');
3094
+ const pageFields = (pivot.reportFilters ?? []).map((filter)=>{
3095
+ const items = fields[filter.fieldIndex]?.sharedItems ?? [];
3096
+ const selectedIndex = void 0 === filter.selectedItem ? items.length : items.findIndex((item)=>pivotSourceFilterKey(item) === spreadsheetPivotFilterValueKey(filter.selectedItem));
3097
+ return `<pageField fld="${filter.fieldIndex}" hier="-1" item="${selectedIndex >= 0 ? selectedIndex : items.length}"/>`;
3098
+ }).join('');
3099
+ const dataFields = pivot.values.map((value)=>{
3100
+ const caption = value.caption?.trim() || fields[value.fieldIndex]?.name || `Value ${value.fieldIndex + 1}`;
3101
+ return `<dataField name="${work_xlsx_pivot_xml_escapeXml(caption)}" fld="${value.fieldIndex}" subtotal="${xlsxPivotAggregation(value.aggregation)}" baseField="0" baseItem="0"/>`;
3102
+ }).join('');
3103
+ return [
3104
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',
3105
+ `<pivotTableDefinition xmlns="${SPREADSHEET_NAMESPACE}" name="${work_xlsx_pivot_xml_escapeXml(pivot.name)}" cacheId="${cacheId}"`,
3106
+ ' applyNumberFormats="0" applyBorderFormats="0" applyFontFormats="0" applyPatternFormats="0"',
3107
+ ' applyAlignmentFormats="0" applyWidthHeightFormats="1" dataCaption="值" updatedVersion="8"',
3108
+ ' minRefreshableVersion="3" createdVersion="8" useAutoFormatting="1" compact="0" compactData="0"',
3109
+ ` rowGrandTotals="${pivot.rowGrandTotals ? 1 : 0}" colGrandTotals="${pivot.columnGrandTotals ? 1 : 0}">`,
3110
+ `<location ref="${work_xlsx_pivot_xml_escapeXml(pivotTableReference(pivot))}" firstHeaderRow="1" firstDataRow="1" firstDataCol="${Math.max(1, pivot.rowFields.length)}"/>`,
3111
+ `<pivotFields count="${fields.length}">${pivotFields}</pivotFields>`,
3112
+ `<rowFields count="${pivot.rowFields.length}">${rowFields}</rowFields>`,
3113
+ '<rowItems count="1"><i t="grand"><x/></i></rowItems>',
3114
+ columnFieldIndexes.length ? `<colFields count="${columnFieldIndexes.length}">${columnFields}</colFields>` : '',
3115
+ '<colItems count="1"><i t="grand"><x/></i></colItems>',
3116
+ pageFields ? `<pageFields count="${pivot.reportFilters.length}">${pageFields}</pageFields>` : '',
3117
+ `<dataFields count="${pivot.values.length}">${dataFields}</dataFields>`,
3118
+ `<pivotTableStyleInfo name="${work_xlsx_pivot_xml_escapeXml(pivot.styleName || 'PivotStyleLight16')}" showRowHeaders="1" showColHeaders="1" showRowStripes="0" showColStripes="0" showLastColumn="1"/>`,
3119
+ '</pivotTableDefinition>'
3120
+ ].join('');
3121
+ }
3122
+ function pivotTableReference(pivot) {
3123
+ const ranges = parseSpreadsheetCellRanges(pivot.outputReference ?? '');
3124
+ const reportFilterRows = pivot.reportFilters?.length ? pivot.reportFilters.length + 1 : 0;
3125
+ if (ranges?.length !== 1 || !reportFilterRows) return pivot.outputReference ?? pivot.anchor;
3126
+ const range = ranges[0];
3127
+ const tableStartRow = range.row[0] + reportFilterRows;
3128
+ if (tableStartRow > range.row[1]) return pivot.outputReference ?? pivot.anchor;
3129
+ return formatSpreadsheetCellRanges([
3130
+ {
3131
+ row: [
3132
+ tableStartRow,
3133
+ range.row[1]
3134
+ ],
3135
+ column: [
3136
+ range.column[0],
3137
+ range.column[1]
3138
+ ]
3139
+ }
3140
+ ]);
3141
+ }
3142
+ function pivotCacheDefinitionXml(sourceSheet, pivot, fields) {
3143
+ const recordCount = fields[0]?.values.length ?? 0;
3144
+ return [
3145
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',
3146
+ `<pivotCacheDefinition xmlns="${SPREADSHEET_NAMESPACE}" xmlns:r="${work_xlsx_pivot_xml_RELATIONSHIP_NAMESPACE}" r:id="rId1"`,
3147
+ ` saveData="1" refreshOnLoad="${pivot.refreshOnLoad ? 1 : 0}" enableRefresh="1"`,
3148
+ ` createdVersion="8" refreshedVersion="8" minRefreshableVersion="3" recordCount="${recordCount}">`,
3149
+ '<cacheSource type="worksheet">',
3150
+ `<worksheetSource ref="${work_xlsx_pivot_xml_escapeXml(pivot.sourceReference)}" sheet="${work_xlsx_pivot_xml_escapeXml(sourceSheet.name)}"/>`,
3151
+ '</cacheSource>',
3152
+ `<cacheFields count="${fields.length}">`,
3153
+ ...fields.map((field)=>pivotCacheFieldXml(field)),
3154
+ '</cacheFields>',
3155
+ '</pivotCacheDefinition>'
3156
+ ].join('');
3157
+ }
3158
+ function pivotCacheFieldXml(field) {
3159
+ const values = field.sharedItems ?? field.values;
3160
+ const statistics = pivotValueStatistics(values);
3161
+ const attributes = [
3162
+ `containsBlank="${statistics.blank ? 1 : 0}"`,
3163
+ `containsString="${statistics.string ? 1 : 0}"`,
3164
+ `containsNumber="${statistics.number ? 1 : 0}"`,
3165
+ `containsInteger="${statistics.integer ? 1 : 0}"`,
3166
+ `containsDate="${statistics.date ? 1 : 0}"`,
3167
+ `containsMixedTypes="${statistics.typeCount > 1 ? 1 : 0}"`,
3168
+ `containsSemiMixedTypes="${statistics.string && statistics.typeCount > 1 ? 1 : 0}"`
3169
+ ];
3170
+ if (!field.sharedItems) return `<cacheField name="${work_xlsx_pivot_xml_escapeXml(field.name)}" numFmtId="0"><sharedItems ${attributes.join(' ')}/></cacheField>`;
3171
+ return [
3172
+ `<cacheField name="${work_xlsx_pivot_xml_escapeXml(field.name)}" numFmtId="0">`,
3173
+ `<sharedItems count="${field.sharedItems.length}" ${attributes.join(' ')}>`,
3174
+ ...field.sharedItems.map((value)=>pivotCacheValueXml(value)),
3175
+ '</sharedItems>',
3176
+ '</cacheField>'
3177
+ ].join('');
3178
+ }
3179
+ function pivotCacheRecordsXml(fields) {
3180
+ const recordCount = fields[0]?.values.length ?? 0;
3181
+ const sharedIndexes = fields.map((field)=>field.sharedItems ? new Map(field.sharedItems.map((value, index)=>[
3182
+ pivotValueKey(value),
3183
+ index
3184
+ ])) : void 0);
3185
+ const records = [];
3186
+ for(let row = 0; row < recordCount; row += 1){
3187
+ records.push('<r>');
3188
+ for(let column = 0; column < fields.length; column += 1){
3189
+ const value = fields[column].values[row];
3190
+ const sharedIndex = sharedIndexes[column]?.get(pivotValueKey(value));
3191
+ records.push(void 0 === sharedIndex ? pivotCacheValueXml(value) : `<x v="${sharedIndex}"/>`);
3192
+ }
3193
+ records.push('</r>');
3194
+ }
3195
+ return [
3196
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',
3197
+ `<pivotCacheRecords xmlns="${SPREADSHEET_NAMESPACE}" count="${recordCount}">`,
3198
+ ...records,
3199
+ '</pivotCacheRecords>'
3200
+ ].join('');
3201
+ }
3202
+ function pivotSourceValue(cell) {
3203
+ const value = cell?.v ?? cell?.m ?? null;
3204
+ if (null == value || '' === value) return {
3205
+ value: null,
3206
+ type: 'blank'
3207
+ };
3208
+ if (cell?.ct?.t === 'e') return {
3209
+ value: String(cell.m ?? cell.v ?? '#VALUE!'),
3210
+ type: 'error'
3211
+ };
3212
+ if (value instanceof Date) return {
3213
+ value,
3214
+ type: 'date'
3215
+ };
3216
+ if ('number' == typeof value && Number.isFinite(value)) return {
3217
+ value,
3218
+ type: 'number'
3219
+ };
3220
+ if ('boolean' == typeof value) return {
3221
+ value,
3222
+ type: 'boolean'
3223
+ };
3224
+ return {
3225
+ value: String(value),
3226
+ type: 'string'
3227
+ };
3228
+ }
3229
+ function uniqueSortedPivotValues(values) {
3230
+ const unique = new Map(values.map((value)=>[
3231
+ pivotValueKey(value),
3232
+ value
3233
+ ]));
3234
+ return Array.from(unique.values()).sort((left, right)=>pivotValueDisplay(left).localeCompare(pivotValueDisplay(right), 'zh-CN', {
3235
+ numeric: true,
3236
+ sensitivity: 'base'
3237
+ }));
3238
+ }
3239
+ function pivotCacheValueXml(value) {
3240
+ if ('blank' === value.type) return '<m/>';
3241
+ if ('number' === value.type) return `<n v="${Number(value.value)}"/>`;
3242
+ if ('boolean' === value.type) return `<b v="${value.value ? 1 : 0}"/>`;
3243
+ if ('date' === value.type) {
3244
+ const date = value.value instanceof Date ? value.value.toISOString() : String(value.value);
3245
+ return `<d v="${work_xlsx_pivot_xml_escapeXml(date)}"/>`;
3246
+ }
3247
+ if ('error' === value.type) return `<e v="${work_xlsx_pivot_xml_escapeXml(String(value.value))}"/>`;
3248
+ return `<s v="${work_xlsx_pivot_xml_escapeXml(String(value.value))}"/>`;
3249
+ }
3250
+ function pivotValueKey(value) {
3251
+ const raw = value.value instanceof Date ? value.value.toISOString() : value.value;
3252
+ return `${value.type}:${String(raw ?? '')}`;
3253
+ }
3254
+ function pivotSourceFilterKey(value) {
3255
+ if ('blank' === value.type) return 'blank:';
3256
+ if ('date' === value.type) return `string:${value.value instanceof Date ? value.value.toISOString() : String(value.value)}`;
3257
+ if ('boolean' === value.type) return `boolean:${String(Boolean(value.value))}`;
3258
+ if ('number' === value.type) return `number:${String(Number(value.value))}`;
3259
+ return `string:${String(value.value ?? '')}`;
3260
+ }
3261
+ function pivotValueDisplay(value) {
3262
+ if (value.value instanceof Date) return value.value.toISOString();
3263
+ return String(value.value ?? '');
3264
+ }
3265
+ function pivotValueStatistics(values) {
3266
+ const types = new Set(values.filter((value)=>'blank' !== value.type).map((value)=>value.type));
3267
+ return {
3268
+ blank: values.some((value)=>'blank' === value.type),
3269
+ string: types.has('string') || types.has('error'),
3270
+ number: types.has('number'),
3271
+ integer: types.has('number') && values.filter((value)=>'number' === value.type).every((value)=>Number.isInteger(Number(value.value))),
3272
+ date: types.has('date'),
3273
+ typeCount: types.size
3274
+ };
3275
+ }
3276
+ function work_xlsx_pivot_xml_escapeXml(value) {
3277
+ return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&apos;');
3278
+ }
3279
+ const work_xlsx_pivots_PACKAGE_RELATIONSHIP_NAMESPACE = 'http://schemas.openxmlformats.org/package/2006/relationships';
3280
+ const DOCUMENT_RELATIONSHIP_NAMESPACE = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships';
3281
+ const XMLNS_NAMESPACE = 'http://www.w3.org/2000/xmlns/';
3282
+ const PIVOT_TABLE_RELATIONSHIP = `${DOCUMENT_RELATIONSHIP_NAMESPACE}/pivotTable`;
3283
+ const PIVOT_CACHE_DEFINITION_RELATIONSHIP = `${DOCUMENT_RELATIONSHIP_NAMESPACE}/pivotCacheDefinition`;
3284
+ const PIVOT_CACHE_RECORDS_RELATIONSHIP = `${DOCUMENT_RELATIONSHIP_NAMESPACE}/pivotCacheRecords`;
3285
+ const PIVOT_TABLE_CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml';
3286
+ const PIVOT_CACHE_DEFINITION_CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml';
3287
+ const PIVOT_CACHE_RECORDS_CONTENT_TYPE = 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml';
3288
+ async function patchXlsxPivotTables(buffer, content) {
3289
+ if (!content.sheets.some((sheet)=>sheet.pivotTables?.length)) return buffer;
3290
+ const archive = await work_ooxml_package_OoxmlPackage.load(buffer);
3291
+ const worksheetParts = await workbookWorksheetParts(archive);
3292
+ const candidates = content.sheets.flatMap((ownerSheet)=>(ownerSheet.pivotTables ?? []).flatMap((pivot)=>{
3293
+ if (!ownerSheet.id || !spreadsheetPivotValidation(content, ownerSheet.id, pivot).valid) return [];
3294
+ const worksheetPart = worksheetParts.get(ownerSheet.name.slice(0, 31) || '工作表');
3295
+ return worksheetPart ? [
3296
+ {
3297
+ ownerSheet,
3298
+ pivot,
3299
+ worksheetPart
3300
+ }
3301
+ ] : [];
3302
+ }));
3303
+ if (!candidates.length) return buffer;
3304
+ const zip = await jszip.loadAsync(buffer);
3305
+ const workbookEntry = zip.file('xl/workbook.xml');
3306
+ const workbookRelationshipsEntry = zip.file('xl/_rels/workbook.xml.rels');
3307
+ if (!workbookEntry || !workbookRelationshipsEntry) return buffer;
3308
+ const workbook = parseXml(await workbookEntry.async('text'), 'xl/workbook.xml');
3309
+ const workbookRelationships = parseXml(await workbookRelationshipsEntry.async('text'), 'xl/_rels/workbook.xml.rels');
3310
+ removeRelationships(workbookRelationships, '/pivotCacheDefinition');
3311
+ for (const existing of directChildren(workbook.documentElement, 'pivotCaches'))existing.remove();
3312
+ const pivotCaches = workbook.createElementNS(workbook.documentElement.namespaceURI, 'pivotCaches');
3313
+ ensureRelationshipNamespace(workbook);
3314
+ const worksheetRelationships = new Map();
3315
+ const contentTypeParts = [];
3316
+ let exported = 0;
3317
+ for (const [index, candidate] of candidates.entries()){
3318
+ const partIndex = index + 1;
3319
+ const cacheId = partIndex;
3320
+ const parts = createXlsxPivotXmlParts(content, candidate.pivot, cacheId);
3321
+ if (!parts) continue;
3322
+ const pivotTablePath = `xl/pivotTables/pivotTable${partIndex}.xml`;
3323
+ const pivotCacheDefinitionPath = `xl/pivotCache/pivotCacheDefinition${partIndex}.xml`;
3324
+ const pivotCacheRecordsPath = `xl/pivotCache/pivotCacheRecords${partIndex}.xml`;
3325
+ const workbookRelationshipId = appendRelationship(workbookRelationships, PIVOT_CACHE_DEFINITION_RELATIONSHIP, `pivotCache/pivotCacheDefinition${partIndex}.xml`);
3326
+ const pivotCache = workbook.createElementNS(workbook.documentElement.namespaceURI, 'pivotCache');
3327
+ pivotCache.setAttribute('cacheId', String(cacheId));
3328
+ pivotCache.setAttributeNS(DOCUMENT_RELATIONSHIP_NAMESPACE, 'r:id', workbookRelationshipId);
3329
+ pivotCaches.append(pivotCache);
3330
+ const worksheetRelationshipDocument = worksheetRelationships.get(candidate.worksheetPart) ?? await readRelationshipsDocument(zip, work_xlsx_pivots_relationshipsPartPath(candidate.worksheetPart));
3331
+ if (!worksheetRelationships.has(candidate.worksheetPart)) {
3332
+ removeRelationships(worksheetRelationshipDocument, '/pivotTable');
3333
+ worksheetRelationships.set(candidate.worksheetPart, worksheetRelationshipDocument);
3334
+ }
3335
+ appendRelationship(worksheetRelationshipDocument, PIVOT_TABLE_RELATIONSHIP, `../pivotTables/pivotTable${partIndex}.xml`);
3336
+ zip.file(pivotTablePath, parts.table);
3337
+ zip.file(pivotCacheDefinitionPath, parts.cacheDefinition);
3338
+ zip.file(pivotCacheRecordsPath, parts.cacheRecords);
3339
+ zip.file(`xl/pivotTables/_rels/pivotTable${partIndex}.xml.rels`, relationshipsXml([
3340
+ {
3341
+ id: 'rId1',
3342
+ type: PIVOT_CACHE_DEFINITION_RELATIONSHIP,
3343
+ target: `../pivotCache/pivotCacheDefinition${partIndex}.xml`
3344
+ }
3345
+ ]));
3346
+ zip.file(`xl/pivotCache/_rels/pivotCacheDefinition${partIndex}.xml.rels`, relationshipsXml([
3347
+ {
3348
+ id: 'rId1',
3349
+ type: PIVOT_CACHE_RECORDS_RELATIONSHIP,
3350
+ target: `pivotCacheRecords${partIndex}.xml`
3351
+ }
3352
+ ]));
3353
+ contentTypeParts.push({
3354
+ path: pivotTablePath,
3355
+ contentType: PIVOT_TABLE_CONTENT_TYPE
3356
+ }, {
3357
+ path: pivotCacheDefinitionPath,
3358
+ contentType: PIVOT_CACHE_DEFINITION_CONTENT_TYPE
3359
+ }, {
3360
+ path: pivotCacheRecordsPath,
3361
+ contentType: PIVOT_CACHE_RECORDS_CONTENT_TYPE
3362
+ });
3363
+ exported += 1;
3364
+ }
3365
+ if (!exported) return buffer;
3366
+ insertWorkbookPivotCaches(workbook, pivotCaches);
3367
+ zip.file('xl/workbook.xml', new XMLSerializer().serializeToString(workbook));
3368
+ zip.file('xl/_rels/workbook.xml.rels', new XMLSerializer().serializeToString(workbookRelationships));
3369
+ for (const [worksheetPart, relationships] of worksheetRelationships)zip.file(work_xlsx_pivots_relationshipsPartPath(worksheetPart), new XMLSerializer().serializeToString(relationships));
3370
+ await work_xlsx_pivots_updateContentTypes(zip, contentTypeParts);
3371
+ return zip.generateAsync({
3372
+ type: 'arraybuffer',
3373
+ compression: 'DEFLATE'
3374
+ });
3375
+ }
3376
+ async function inspectXlsxPivotTables(archive) {
3377
+ if (!archive.has('xl/workbook.xml')) return {
3378
+ tables: [],
3379
+ unsupported: [],
3380
+ detected: 0
3381
+ };
3382
+ const workbook = await archive.xml('xl/workbook.xml');
3383
+ const workbookRelationships = await archive.relationships('xl/workbook.xml');
3384
+ const cacheById = new Map();
3385
+ for (const cache of directChildren(firstDescendant(workbook, 'pivotCaches') ?? workbook.documentElement, 'pivotCache')){
3386
+ const cacheId = integerAttribute(cache, 'cacheId');
3387
+ const relationship = workbookRelationships.get(attribute(cache, 'r:id') ?? attribute(cache, 'id') ?? '');
3388
+ if (null !== cacheId && relationship && archive.has(relationship.target)) cacheById.set(cacheId, parsePivotCache(await archive.xml(relationship.target)));
3389
+ }
3390
+ const worksheetParts = await workbookWorksheetParts(archive);
3391
+ const tables = [];
3392
+ const unsupported = [];
3393
+ let detected = 0;
3394
+ for (const [destinationSheetName, worksheetPart] of worksheetParts){
3395
+ const relationships = await archive.relationships(worksheetPart);
3396
+ for (const relationship of relationships.values()){
3397
+ if (!relationship.type.endsWith('/pivotTable')) continue;
3398
+ detected += 1;
3399
+ if ('External' === relationship.targetMode || !archive.has(relationship.target)) {
3400
+ unsupported.push({
3401
+ code: 'xlsx.pivots.relationship',
3402
+ message: 'The pivot-table relationship is external or missing.',
3403
+ location: destinationSheetName
3404
+ });
3405
+ continue;
3406
+ }
3407
+ const parsed = parsePivotTable(await archive.xml(relationship.target), destinationSheetName, cacheById);
3408
+ if ('unsupported' in parsed) unsupported.push(parsed.unsupported);
3409
+ else tables.push(parsed.table);
3410
+ }
3411
+ }
3412
+ if (!detected && (archive.paths('xl/pivotTables/').length || archive.paths('xl/pivotCache/').length)) unsupported.push({
3413
+ code: 'xlsx.pivots.orphaned',
3414
+ message: 'Pivot-table package parts are present without a readable worksheet relationship.'
3415
+ });
3416
+ if (archive.paths('xl/slicers/').length || archive.paths('xl/slicerCaches/').length) unsupported.push({
3417
+ code: 'xlsx.pivots.slicers',
3418
+ message: 'Pivot slicers and timelines are not editable in Work.'
3419
+ });
3420
+ return {
3421
+ tables,
3422
+ unsupported,
3423
+ detected: Math.max(detected, tables.length + unsupported.length)
3424
+ };
3425
+ }
3426
+ function applyImportedXlsxPivotTables(content, imported) {
3427
+ if (!imported?.tables.length) return content;
3428
+ const sheets = content.sheets.map((sheet)=>({
3429
+ ...sheet,
3430
+ pivotTables: sheet.pivotTables ? [
3431
+ ...sheet.pivotTables
3432
+ ] : void 0
3433
+ }));
3434
+ const byName = new Map(sheets.map((sheet)=>[
3435
+ sheet.name,
3436
+ sheet
3437
+ ]));
3438
+ for (const table of imported.tables){
3439
+ const ownerSheet = byName.get(table.destinationSheetName);
3440
+ const sourceSheet = byName.get(table.sourceSheetName);
3441
+ if (!ownerSheet || !sourceSheet?.id) continue;
3442
+ const pivot = {
3443
+ id: createWorkId('pivot'),
3444
+ name: table.name,
3445
+ sourceSheetId: sourceSheet.id,
3446
+ sourceReference: table.sourceReference,
3447
+ anchor: table.anchor,
3448
+ rowFields: [
3449
+ ...table.rowFields
3450
+ ],
3451
+ columnFields: [
3452
+ ...table.columnFields
3453
+ ],
3454
+ reportFilters: table.reportFilters.map((filter)=>({
3455
+ ...filter
3456
+ })),
3457
+ values: table.values.map((value)=>({
3458
+ ...value
3459
+ })),
3460
+ rowGrandTotals: table.rowGrandTotals,
3461
+ columnGrandTotals: table.columnGrandTotals,
3462
+ styleName: table.styleName,
3463
+ refreshOnLoad: table.refreshOnLoad,
3464
+ outputReference: table.outputReference
3465
+ };
3466
+ ownerSheet.pivotTables = [
3467
+ ...ownerSheet.pivotTables ?? [],
3468
+ pivot
3469
+ ];
3470
+ }
3471
+ return {
3472
+ ...content,
3473
+ sheets
3474
+ };
3475
+ }
3476
+ async function workbookWorksheetParts(archive) {
3477
+ if (!archive.has('xl/workbook.xml')) return new Map();
3478
+ const workbook = await archive.xml('xl/workbook.xml');
3479
+ const relationships = await archive.relationships('xl/workbook.xml');
3480
+ const parts = new Map();
3481
+ for (const sheet of directChildren(firstDescendant(workbook, 'sheets') ?? workbook.documentElement, 'sheet')){
3482
+ const name = attribute(sheet, 'name');
3483
+ const relationship = relationships.get(attribute(sheet, 'r:id') ?? attribute(sheet, 'id') ?? '');
3484
+ if (name && relationship?.type.endsWith('/worksheet')) parts.set(name, relationship.target);
3485
+ }
3486
+ return parts;
3487
+ }
3488
+ function parsePivotCache(document) {
3489
+ const root = document.documentElement;
3490
+ const cacheSource = directChild(root, 'cacheSource');
3491
+ const sourceType = attribute(cacheSource ?? root, 'type');
3492
+ const worksheetSource = directChild(cacheSource ?? root, 'worksheetSource');
3493
+ const sourceSheetName = attribute(worksheetSource ?? root, 'sheet')?.trim() || void 0;
3494
+ const sourceReference = attribute(worksheetSource ?? root, 'ref')?.trim() || void 0;
3495
+ const cacheFields = directChildren(directChild(root, 'cacheFields') ?? root, 'cacheField');
3496
+ const fields = cacheFields.map((field, index)=>attribute(field, 'name')?.trim() || `Field ${index + 1}`);
3497
+ const fieldItems = cacheFields.map((field)=>directChildren(directChild(field, 'sharedItems') ?? field).flatMap((item)=>{
3498
+ const value = pivotCacheItemValue(item);
3499
+ return value ? [
3500
+ value.value
3501
+ ] : [];
3502
+ }));
3503
+ let unsupported;
3504
+ if ('worksheet' !== sourceType || !sourceSheetName || !sourceReference || attribute(worksheetSource ?? root, 'name')) unsupported = {
3505
+ code: 'external' === sourceType ? 'xlsx.pivots.external-source' : 'xlsx.pivots.source',
3506
+ message: 'Only worksheet pivots with an explicit cell-range source are editable.'
3507
+ };
3508
+ else if (descendants(root, 'fieldGroup').length || directChildren(directChild(root, 'cacheFields') ?? root, 'cacheField').some((field)=>attribute(field, 'formula'))) unsupported = {
3509
+ code: 'xlsx.pivots.grouping',
3510
+ message: 'Grouped or calculated pivot-cache fields remain cached values only.'
3511
+ };
3512
+ else if (descendants(root, 'calculatedItems').length || descendants(root, 'calculatedMembers').length) unsupported = {
3513
+ code: 'xlsx.pivots.calculated-items',
3514
+ message: 'Calculated pivot items and members remain cached values only.'
3515
+ };
3516
+ return {
3517
+ sourceSheetName,
3518
+ sourceReference,
3519
+ fields,
3520
+ fieldItems,
3521
+ refreshOnLoad: work_xlsx_pivots_booleanAttribute(root, 'refreshOnLoad'),
3522
+ unsupported
3523
+ };
3524
+ }
3525
+ function pivotCacheItemValue(element) {
3526
+ const source = attribute(element, 'v');
3527
+ if ('m' === element.localName) return {
3528
+ value: null
3529
+ };
3530
+ if ('b' === element.localName) return {
3531
+ value: '1' === source || source?.toLowerCase() === 'true'
3532
+ };
3533
+ if ('n' === element.localName) {
3534
+ const value = Number(source);
3535
+ return Number.isFinite(value) ? {
3536
+ value
3537
+ } : null;
3538
+ }
3539
+ if ('s' === element.localName || 'e' === element.localName || 'd' === element.localName) return {
3540
+ value: source ?? ''
3541
+ };
3542
+ return null;
3543
+ }
3544
+ function parsePivotReportFilters(root, cache, location) {
3545
+ const pageFields = directChild(root, 'pageFields');
3546
+ if (!pageFields) return {
3547
+ reportFilters: []
3548
+ };
3549
+ const pageWrap = integerAttribute(root, 'pageWrap') ?? 0;
3550
+ if (pageWrap > 0 || work_xlsx_pivots_booleanAttribute(root, 'pageOverThenDown')) return {
3551
+ unsupported: {
3552
+ code: 'xlsx.pivots.report-filter-layout',
3553
+ message: 'Wrapped or horizontal report-filter layouts remain cached values only.',
3554
+ location
3555
+ }
3556
+ };
3557
+ const pivotFields = directChildren(directChild(root, 'pivotFields') ?? root, 'pivotField');
3558
+ const reportFilters = [];
3559
+ for (const pageField of directChildren(pageFields, 'pageField')){
3560
+ const fieldIndex = integerAttribute(pageField, 'fld');
3561
+ const pivotField = null === fieldIndex ? void 0 : pivotFields[fieldIndex];
3562
+ if (null === fieldIndex || !pivotField || !cache.fields[fieldIndex]) return {
3563
+ unsupported: {
3564
+ code: 'xlsx.pivots.report-filter-field',
3565
+ message: 'A report filter refers to a missing pivot-cache field.',
3566
+ location
3567
+ }
3568
+ };
3569
+ const items = directChildren(directChild(pivotField, 'items') ?? pivotField, 'item');
3570
+ if (work_xlsx_pivots_booleanAttribute(pivotField, 'multipleItemSelectionAllowed') || items.some((item)=>work_xlsx_pivots_booleanAttribute(item, 'h'))) return {
3571
+ unsupported: {
3572
+ code: 'xlsx.pivots.report-filter-multi-select',
3573
+ message: 'Multi-selection report filters remain cached values only.',
3574
+ location
3575
+ }
3576
+ };
3577
+ const itemIndex = integerAttribute(pageField, 'item');
3578
+ if (null === itemIndex) {
3579
+ reportFilters.push({
3580
+ fieldIndex
3581
+ });
3582
+ continue;
3583
+ }
3584
+ const pivotItem = items[itemIndex];
3585
+ if (!pivotItem) return {
3586
+ unsupported: {
3587
+ code: 'xlsx.pivots.report-filter-item',
3588
+ message: 'A report filter refers to a missing pivot item.',
3589
+ location
3590
+ }
3591
+ };
3592
+ if ('default' === attribute(pivotItem, 't')) {
3593
+ reportFilters.push({
3594
+ fieldIndex
3595
+ });
3596
+ continue;
3597
+ }
3598
+ const sharedItemIndex = integerAttribute(pivotItem, 'x');
3599
+ const sharedItems = cache.fieldItems[fieldIndex] ?? [];
3600
+ if (null === sharedItemIndex || sharedItemIndex < 0 || sharedItemIndex >= sharedItems.length) return {
3601
+ unsupported: {
3602
+ code: 'xlsx.pivots.report-filter-item',
3603
+ message: 'A report filter item is missing from the pivot cache.',
3604
+ location
3605
+ }
3606
+ };
3607
+ reportFilters.push({
3608
+ fieldIndex,
3609
+ selectedItem: sharedItems[sharedItemIndex]
3610
+ });
3611
+ }
3612
+ return {
3613
+ reportFilters
3614
+ };
3615
+ }
3616
+ function parsePivotTable(document, destinationSheetName, cacheById) {
3617
+ const root = document.documentElement;
3618
+ const name = attribute(root, 'name')?.trim() || 'PivotTable';
3619
+ const locationLabel = destinationSheetName ? `${destinationSheetName} · ${name}` : name;
3620
+ const cacheId = integerAttribute(root, 'cacheId');
3621
+ const cache = null === cacheId ? void 0 : cacheById.get(cacheId);
3622
+ if (!cache || cache.unsupported || !cache.sourceSheetName || !cache.sourceReference) return {
3623
+ unsupported: {
3624
+ ...cache?.unsupported ?? {
3625
+ code: 'xlsx.pivots.cache',
3626
+ message: 'The pivot cache definition is missing or unreadable.'
3627
+ },
3628
+ location: locationLabel
3629
+ }
3630
+ };
3631
+ if (directChildren(root, 'filters').length || work_xlsx_pivots_booleanAttribute(root, 'dataOnRows')) return {
3632
+ unsupported: {
3633
+ code: 'xlsx.pivots.filters-layout',
3634
+ message: 'Label or value filters and values-on-rows layouts remain cached values only.',
3635
+ location: locationLabel
3636
+ }
3637
+ };
3638
+ const parsedReportFilters = parsePivotReportFilters(root, cache, locationLabel);
3639
+ if ('unsupported' in parsedReportFilters) return parsedReportFilters;
3640
+ const reportFilters = parsedReportFilters.reportFilters;
3641
+ const rowFields = readPivotFieldIndexes(directChild(root, 'rowFields'));
3642
+ const columnFields = readPivotFieldIndexes(directChild(root, 'colFields')).filter((index)=>-2 !== index);
3643
+ if (rowFields.includes(-2)) return {
3644
+ unsupported: {
3645
+ code: 'xlsx.pivots.values-on-rows',
3646
+ message: 'Values-on-rows pivot layouts remain cached values only.',
3647
+ location: locationLabel
3648
+ }
3649
+ };
3650
+ const values = [];
3651
+ for (const dataField of directChildren(directChild(root, 'dataFields') ?? root, 'dataField')){
3652
+ const fieldIndex = integerAttribute(dataField, 'fld');
3653
+ const aggregation = workPivotAggregation(attribute(dataField, 'subtotal'));
3654
+ if (null === fieldIndex || !aggregation || !cache.fields[fieldIndex]) return {
3655
+ unsupported: {
3656
+ code: 'xlsx.pivots.aggregation',
3657
+ message: 'The pivot uses an unsupported value calculation or field reference.',
3658
+ location: locationLabel
3659
+ }
3660
+ };
3661
+ values.push({
3662
+ fieldIndex,
3663
+ aggregation,
3664
+ caption: attribute(dataField, 'name')?.trim() || void 0
3665
+ });
3666
+ }
3667
+ const location = directChild(root, 'location');
3668
+ const outputReference = attribute(location ?? root, 'ref')?.trim() ?? '';
3669
+ const outputRange = parseSpreadsheetCellRanges(outputReference);
3670
+ const reportFilterFields = reportFilters.map((filter)=>filter.fieldIndex);
3671
+ if (!rowFields.length || !values.length || outputRange?.length !== 1 || [
3672
+ ...rowFields,
3673
+ ...columnFields,
3674
+ ...reportFilterFields
3675
+ ].some((index)=>index < 0 || !cache.fields[index]) || new Set([
3676
+ ...rowFields,
3677
+ ...columnFields,
3678
+ ...reportFilterFields
3679
+ ]).size !== rowFields.length + columnFields.length + reportFilterFields.length) return {
3680
+ unsupported: {
3681
+ code: 'xlsx.pivots.layout',
3682
+ message: 'The pivot layout cannot be represented by the editable Work pivot model.',
3683
+ location: locationLabel
3684
+ }
3685
+ };
3686
+ const reportFilterRows = reportFilters.length ? reportFilters.length + 1 : 0;
3687
+ if (outputRange[0].row[0] < reportFilterRows) return {
3688
+ unsupported: {
3689
+ code: 'xlsx.pivots.report-filter-layout',
3690
+ message: 'The report-filter placement cannot be represented by the Work report layout.',
3691
+ location: locationLabel
3692
+ }
3693
+ };
3694
+ const ownedRange = {
3695
+ row: [
3696
+ outputRange[0].row[0] - reportFilterRows,
3697
+ outputRange[0].row[1]
3698
+ ],
3699
+ column: [
3700
+ outputRange[0].column[0],
3701
+ outputRange[0].column[1]
3702
+ ]
3703
+ };
3704
+ const anchor = formatSpreadsheetCellRanges([
3705
+ {
3706
+ row: [
3707
+ ownedRange.row[0],
3708
+ ownedRange.row[0]
3709
+ ],
3710
+ column: [
3711
+ ownedRange.column[0],
3712
+ ownedRange.column[0]
3713
+ ]
3714
+ }
3715
+ ]);
3716
+ return {
3717
+ table: {
3718
+ destinationSheetName,
3719
+ sourceSheetName: cache.sourceSheetName,
3720
+ sourceReference: cache.sourceReference,
3721
+ name,
3722
+ anchor,
3723
+ outputReference: formatSpreadsheetCellRanges([
3724
+ ownedRange
3725
+ ]),
3726
+ rowFields,
3727
+ columnFields,
3728
+ reportFilters,
3729
+ values,
3730
+ rowGrandTotals: work_xlsx_pivots_booleanAttribute(root, 'rowGrandTotals', true),
3731
+ columnGrandTotals: work_xlsx_pivots_booleanAttribute(root, 'colGrandTotals', true),
3732
+ styleName: attribute(directChild(root, 'pivotTableStyleInfo') ?? root, 'name') || 'PivotStyleLight16',
3733
+ refreshOnLoad: cache.refreshOnLoad
3734
+ }
3735
+ };
3736
+ }
3737
+ function readPivotFieldIndexes(parent) {
3738
+ if (!parent) return [];
3739
+ return directChildren(parent, 'field').flatMap((field)=>{
3740
+ const index = integerAttribute(field, 'x');
3741
+ return null === index ? [] : [
3742
+ index
3743
+ ];
3744
+ });
3745
+ }
3746
+ async function readRelationshipsDocument(zip, path) {
3747
+ const entry = zip.file(path);
3748
+ return entry ? parseXml(await entry.async('text'), path) : parseXml(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="${work_xlsx_pivots_PACKAGE_RELATIONSHIP_NAMESPACE}"/>`, path);
3749
+ }
3750
+ function appendRelationship(document, type, target) {
3751
+ const id = work_xlsx_pivots_nextRelationshipId(document);
3752
+ const relationship = document.createElementNS(work_xlsx_pivots_PACKAGE_RELATIONSHIP_NAMESPACE, 'Relationship');
3753
+ relationship.setAttribute('Id', id);
3754
+ relationship.setAttribute('Type', type);
3755
+ relationship.setAttribute('Target', target);
3756
+ document.documentElement.append(relationship);
3757
+ return id;
3758
+ }
3759
+ function removeRelationships(document, typeSuffix) {
3760
+ for (const relationship of directChildren(document.documentElement, 'Relationship'))if (attribute(relationship, 'Type')?.endsWith(typeSuffix)) relationship.remove();
3761
+ }
3762
+ function work_xlsx_pivots_nextRelationshipId(document) {
3763
+ const ids = new Set(directChildren(document.documentElement, 'Relationship').map((relationship)=>attribute(relationship, 'Id')).filter((id)=>Boolean(id)));
3764
+ let index = 1;
3765
+ while(ids.has(`rId${index}`))index += 1;
3766
+ return `rId${index}`;
3767
+ }
3768
+ function relationshipsXml(relationships) {
3769
+ return [
3770
+ '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>',
3771
+ `<Relationships xmlns="${work_xlsx_pivots_PACKAGE_RELATIONSHIP_NAMESPACE}">`,
3772
+ ...relationships.map((relationship)=>`<Relationship Id="${relationship.id}" Type="${relationship.type}" Target="${relationship.target}"/>`),
3773
+ '</Relationships>'
3774
+ ].join('');
3775
+ }
3776
+ function insertWorkbookPivotCaches(document, pivotCaches) {
3777
+ const laterElements = new Set([
3778
+ 'smartTagPr',
3779
+ 'smartTagTypes',
3780
+ 'webPublishing',
3781
+ 'fileRecoveryPr',
3782
+ 'webPublishObjects',
3783
+ 'extLst'
3784
+ ]);
3785
+ const insertionPoint = directChildren(document.documentElement).find((element)=>laterElements.has(element.localName));
3786
+ document.documentElement.insertBefore(pivotCaches, insertionPoint ?? null);
3787
+ }
3788
+ function ensureRelationshipNamespace(document) {
3789
+ if (xmlNamespacePrefix(document.documentElement, DOCUMENT_RELATIONSHIP_NAMESPACE)) return;
3790
+ document.documentElement.setAttributeNS(XMLNS_NAMESPACE, 'xmlns:r', DOCUMENT_RELATIONSHIP_NAMESPACE);
3791
+ }
3792
+ async function work_xlsx_pivots_updateContentTypes(zip, parts) {
3793
+ const entry = zip.file('[Content_Types].xml');
3794
+ if (!entry) return;
3795
+ const document = parseXml(await entry.async('text'), '[Content_Types].xml');
3796
+ const namespace = document.documentElement.namespaceURI;
3797
+ for (const override of directChildren(document.documentElement, 'Override')){
3798
+ const partName = attribute(override, 'PartName') ?? '';
3799
+ if (partName.startsWith('/xl/pivotTables/') || partName.startsWith('/xl/pivotCache/')) override.remove();
3800
+ }
3801
+ for (const part of parts){
3802
+ const override = document.createElementNS(namespace, 'Override');
3803
+ override.setAttribute('PartName', `/${part.path}`);
3804
+ override.setAttribute('ContentType', part.contentType);
3805
+ document.documentElement.append(override);
3806
+ }
3807
+ zip.file('[Content_Types].xml', new XMLSerializer().serializeToString(document));
3808
+ }
3809
+ function work_xlsx_pivots_relationshipsPartPath(sourcePart) {
3810
+ const segments = sourcePart.split('/');
3811
+ const name = segments.pop() ?? '';
3812
+ return [
3813
+ ...segments,
3814
+ '_rels',
3815
+ `${name}.rels`
3816
+ ].join('/');
3817
+ }
3818
+ function integerAttribute(element, name) {
3819
+ const source = attribute(element, name);
3820
+ if (null === source || !source.trim()) return null;
3821
+ const value = Number(source);
3822
+ return Number.isInteger(value) ? value : null;
3823
+ }
3824
+ function work_xlsx_pivots_booleanAttribute(element, name, fallback = false) {
3825
+ const value = attribute(element, name);
3826
+ if (null === value) return fallback;
3827
+ return '1' === value || 'true' === value.toLowerCase();
3828
+ }
3829
+ function emptySpreadsheetWorksheetCompatibilitySummary() {
3830
+ return {
3831
+ hasArrayFormulas: false,
3832
+ hasComments: false,
3833
+ hasCommentThreads: false,
3834
+ hasLinks: false,
3835
+ hasRichText: false
3836
+ };
3837
+ }
3838
+ function updateSpreadsheetWorksheetCompatibilitySummary(summary, cell) {
3839
+ const source = cell;
3840
+ if (source.c?.length) {
3841
+ summary.hasComments = true;
3842
+ if (source.c.length > 1 || source.c.some((comment)=>comment.T)) summary.hasCommentThreads = true;
3843
+ }
3844
+ if (source.l?.Target) summary.hasLinks = true;
3845
+ if (source.F) summary.hasArrayFormulas = true;
3846
+ if (source.r) summary.hasRichText = true;
3847
+ }
3848
+ export { MAX_XLSX_WORKSHEET_IMAGE_BYTES, XlsxCellProtectionWriter, applyImportedXlsxPivotTables, createSpreadsheetFormulaMetadata, createXlsxErrorCell, createXlsxFormulaCell, diagnoseXlsxProtection, emptySpreadsheetWorksheetCompatibilitySummary, inspectXlsxHeaderFooterText, inspectXlsxPivotTables, isEditableXlsxPaperSizeCode, isSupportedXlsxChartLegend, isSupportedXlsxChartPlotLayout, isSupportedXlsxChartSeriesFormatting, isSupportedXlsxCombinationChartNodes, isSupportedXlsxWorksheetImageContentType, parseXlsxChartAxes, parseXlsxChartLegend, parseXlsxChartPlotLayout, parseXlsxChartSeriesStyle, parseXlsxErrorBars, parseXlsxTrendline, patchXlsxFormulaFeatures, patchXlsxPivotTables, patchXlsxWorksheetDrawings, readXlsxFormulaFeaturesFromPackage, readXlsxPageSetup, readXlsxProtection, readXlsxWorksheetCharts, readXlsxWorksheetImages, updateSpreadsheetWorksheetCompatibilitySummary, writeXlsxPageSetup, writeXlsxProtection, xlsxChartNodeUsesStackedGrouping, xlsxChartSeriesFormattingShapeProperties, xlsxChartSeriesMarkerXml, xlsxChartSeriesShapePropertiesXml, xlsxWorksheetChartsToSheet, xlsxWorksheetImagesToSheet };