@a3s-lab/office 0.12.0 → 0.13.1

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 (124) hide show
  1. package/README.md +112 -7
  2. package/dist/0~1403.js +20 -12
  3. package/dist/0~2805.js +241 -241
  4. package/dist/0~4980.js +1 -1
  5. package/dist/0~5050.js +1 -20
  6. package/dist/0~5093.js +547 -547
  7. package/dist/0~6090.js +63 -4
  8. package/dist/0~7048.js +31 -29
  9. package/dist/0~document-editor.js +694 -90
  10. package/dist/0~pdf-viewer.js +41 -10
  11. package/dist/0~presentation-editor.js +8 -6
  12. package/dist/0~spreadsheet-editor.js +11892 -9682
  13. package/dist/0~work-document-import-worker-client.js +237 -0
  14. package/dist/0~work-docx-export.js +1 -1
  15. package/dist/0~work-docx-import.js +1 -1
  16. package/dist/0~work-docx-large-document-import.js +559 -0
  17. package/dist/0~work-office-diagnostics.js +46 -32
  18. package/dist/4104.js +5070 -3112
  19. package/dist/432.js +51 -7
  20. package/dist/4476.js +201 -7
  21. package/dist/5184.js +24 -2
  22. package/dist/{8928.js → 6282.js} +2338 -93
  23. package/dist/8715.js +895 -888
  24. package/dist/9356.js +3 -1
  25. package/dist/core.js +1 -1
  26. package/dist/index.js +1 -1
  27. package/dist/internal/features/work/editors/document-editor-support.d.ts +1 -9
  28. package/dist/internal/features/work/editors/document-external-content.d.ts +1 -1
  29. package/dist/internal/features/work/editors/office-menu-keyboard.d.ts +1 -0
  30. package/dist/internal/features/work/editors/presentation-slide-canvas.d.ts +3 -1
  31. package/dist/internal/features/work/editors/presentation-slide-thumbnail.d.ts +3 -2
  32. package/dist/internal/features/work/editors/presentation-thumbnail-rail.d.ts +2 -1
  33. package/dist/internal/features/work/editors/presentation-workspace.d.ts +2 -1
  34. package/dist/internal/features/work/editors/spreadsheet-border-ribbon.d.ts +5 -0
  35. package/dist/internal/features/work/editors/spreadsheet-calculation-projection.d.ts +10 -0
  36. package/dist/internal/features/work/editors/spreadsheet-cell-border-command.d.ts +3 -0
  37. package/dist/internal/features/work/editors/spreadsheet-cell-border-per-cell.d.ts +5 -0
  38. package/dist/internal/features/work/editors/spreadsheet-cell-border.d.ts +28 -0
  39. package/dist/internal/features/work/editors/spreadsheet-cell-fill-command.d.ts +3 -0
  40. package/dist/internal/features/work/editors/spreadsheet-cell-fill.d.ts +12 -0
  41. package/dist/internal/features/work/editors/spreadsheet-cell-range.d.ts +14 -0
  42. package/dist/internal/features/work/editors/spreadsheet-cell-style-command.d.ts +3 -0
  43. package/dist/internal/features/work/editors/spreadsheet-cell-style-ribbon.d.ts +9 -0
  44. package/dist/internal/features/work/editors/spreadsheet-cell-style.d.ts +41 -0
  45. package/dist/internal/features/work/editors/spreadsheet-command-catalog.d.ts +293 -0
  46. package/dist/internal/features/work/editors/spreadsheet-command-controller.d.ts +10 -4
  47. package/dist/internal/features/work/editors/spreadsheet-editor-ribbon.d.ts +3 -1
  48. package/dist/internal/features/work/editors/spreadsheet-editor-support.d.ts +8 -2
  49. package/dist/internal/features/work/editors/spreadsheet-home-format-ribbon.d.ts +10 -0
  50. package/dist/internal/features/work/editors/spreadsheet-in-place-workbook-sync.d.ts +10 -0
  51. package/dist/internal/features/work/editors/spreadsheet-number-format.d.ts +15 -3
  52. package/dist/internal/features/work/editors/spreadsheet-operation-projection.d.ts +14 -0
  53. package/dist/internal/features/work/editors/spreadsheet-structure-selection.d.ts +7 -0
  54. package/dist/internal/features/work/editors/use-document-pagination.d.ts +5 -0
  55. package/dist/internal/features/work/editors/use-presentation-design-commands.d.ts +2 -1
  56. package/dist/internal/features/work/editors/use-spreadsheet-workbook-sync.d.ts +2 -1
  57. package/dist/internal/features/work/spreadsheet-history-value.d.ts +1 -0
  58. package/dist/internal/features/work/spreadsheet-sparse.d.ts +6 -1
  59. package/dist/internal/features/work/work-document-chunk-geometry.d.ts +28 -0
  60. package/dist/internal/features/work/work-document-chunk-node.d.ts +23 -0
  61. package/dist/internal/features/work/work-document-extensions.d.ts +1 -0
  62. package/dist/internal/features/work/work-document-html-fingerprint.d.ts +15 -0
  63. package/dist/internal/features/work/work-document-import-worker-client.d.ts +13 -0
  64. package/dist/internal/features/work/work-document-import-worker-protocol.d.ts +36 -0
  65. package/dist/internal/features/work/work-document-import.worker.d.ts +1 -0
  66. package/dist/internal/features/work/work-document-incremental-layout.d.ts +1 -1
  67. package/dist/internal/features/work/work-document-integrity-index.d.ts +14 -0
  68. package/dist/internal/features/work/work-document-lazy-block.d.ts +3 -0
  69. package/dist/internal/features/work/work-document-lazy-html.d.ts +21 -0
  70. package/dist/internal/features/work/work-document-lazy-model.d.ts +29 -0
  71. package/dist/internal/features/work/work-document-lazy-pagination-measurement.d.ts +3 -0
  72. package/dist/internal/features/work/work-document-lazy-preview.d.ts +21 -0
  73. package/dist/internal/features/work/work-document-lazy-update.d.ts +14 -0
  74. package/dist/internal/features/work/work-document-model.d.ts +18 -2
  75. package/dist/internal/features/work/work-document-page-surface-registry.d.ts +16 -0
  76. package/dist/internal/features/work/work-document-pagination-dom.d.ts +1 -0
  77. package/dist/internal/features/work/work-document-pagination-types.d.ts +1 -0
  78. package/dist/internal/features/work/work-document-pagination.d.ts +1 -1
  79. package/dist/internal/features/work/work-document-text-statistics.d.ts +25 -0
  80. package/dist/internal/features/work/work-document-windowing.d.ts +23 -0
  81. package/dist/internal/features/work/work-docx-large-document-import.d.ts +35 -0
  82. package/dist/internal/features/work/work-docx-large-document-parser.d.ts +29 -0
  83. package/dist/internal/features/work/work-file-import.d.ts +5 -0
  84. package/dist/internal/features/work/work-file-io.d.ts +3 -3
  85. package/dist/internal/features/work/work-file-kind.d.ts +5 -0
  86. package/dist/internal/features/work/work-office-diagnostics.d.ts +10 -1
  87. package/dist/internal/features/work/work-ooxml-package.d.ts +8 -0
  88. package/dist/internal/features/work/work-presentation-layouts.d.ts +7 -1
  89. package/dist/internal/features/work/work-repository.d.ts +1 -0
  90. package/dist/internal/features/work/work-spreadsheet-compatibility-summary.d.ts +10 -0
  91. package/dist/internal/features/work/work-spreadsheet-file-export.d.ts +2 -0
  92. package/dist/internal/features/work/work-spreadsheet-file-import.d.ts +3 -0
  93. package/dist/internal/features/work/work-spreadsheet-import-diagnostics.d.ts +2 -0
  94. package/dist/internal/features/work/work-spreadsheet-import-worker-client.d.ts +6 -0
  95. package/dist/internal/features/work/work-spreadsheet-import-worker-protocol.d.ts +32 -0
  96. package/dist/internal/features/work/work-spreadsheet-import.worker.d.ts +1 -0
  97. package/dist/internal/features/work/work-spreadsheet-matrix-profile.d.ts +64 -0
  98. package/dist/internal/features/work/work-spreadsheet-package-scan-worker-client.d.ts +6 -0
  99. package/dist/internal/features/work/work-spreadsheet-package-scan-worker-protocol.d.ts +49 -0
  100. package/dist/internal/features/work/work-spreadsheet-package-scan.worker.d.ts +1 -0
  101. package/dist/internal/features/work/work-xlsx-cell-borders.d.ts +25 -0
  102. package/dist/internal/features/work/work-xlsx-cell-style-writer.d.ts +21 -0
  103. package/dist/internal/features/work/work-xlsx-cell-style-xml.d.ts +17 -0
  104. package/dist/internal/features/work/work-xlsx-cell-styles.d.ts +13 -0
  105. package/dist/internal/features/work/work-xlsx-colors.d.ts +6 -0
  106. package/dist/internal/features/work/work-xlsx-formula-diagnostics.d.ts +3 -1
  107. package/dist/internal/features/work/work-xlsx-formulas.d.ts +2 -1
  108. package/dist/internal/features/work/work-xlsx-interop.d.ts +8 -5
  109. package/dist/internal/features/work/work-xlsx-pivot-diagnostics.d.ts +2 -1
  110. package/dist/internal/features/work/work-xlsx-plain-fast-path.d.ts +54 -0
  111. package/dist/internal/features/work/work-xlsx-style-gate.d.ts +1 -0
  112. package/dist/internal/features/work/work-xlsx-worksheet-scan.d.ts +13 -0
  113. package/dist/internal/features/work/work-xlsx-worksheet.d.ts +12 -0
  114. package/dist/office-kernel.wasm +0 -0
  115. package/dist/office-kernel.worker.js +31 -29
  116. package/dist/react.d.ts +7 -1
  117. package/dist/styles.css +557 -3
  118. package/dist/work-document-import.worker.js +558 -0
  119. package/dist/work-spreadsheet-import.worker.js +24745 -0
  120. package/dist/work-spreadsheet-import.worker.js.LICENSE.txt +3 -0
  121. package/dist/work-spreadsheet-package-scan.worker.js +4161 -0
  122. package/dist/work-spreadsheet-package-scan.worker.js.LICENSE.txt +11 -0
  123. package/docs/latest/en/browser-editor-architecture.md +351 -15
  124. package/package.json +23 -3
package/dist/8715.js CHANGED
@@ -25,6 +25,13 @@ function sparseMatrixColumnCount(matrix) {
25
25
  for (const [, row] of sparseArrayEntries(matrix))maximum = Math.max(maximum, row.length);
26
26
  return maximum;
27
27
  }
28
+ function spreadsheetGridSize(sheet) {
29
+ if (!sheet) return null;
30
+ return {
31
+ rowCount: Math.max(sheet.row ?? 0, sheet.data?.length ?? 0),
32
+ columnCount: Math.max(sheet.column ?? 0, sparseMatrixColumnCount(sheet.data))
33
+ };
34
+ }
28
35
  function cloneSparseMatrix(source) {
29
36
  const clone = [];
30
37
  if (!source) return clone;
@@ -37,204 +44,284 @@ function cloneSparseMatrix(source) {
37
44
  }
38
45
  return clone;
39
46
  }
40
- function normalizeWorkSpreadsheetDoughnutHoleSize(value) {
41
- const size = Number(value);
42
- if (!Number.isFinite(size)) return 50;
43
- return Math.min(90, Math.max(10, Math.round(size)));
44
- }
45
- function normalizeWorkSpreadsheetRadarStyle(value) {
46
- return 'standard' === value || 'filled' === value || 'marker' === value ? value : 'marker';
47
+ function spreadsheetPivotCellValue(cell) {
48
+ return cell?.v ?? cell?.m ?? null;
47
49
  }
48
- function normalizeWorkSpreadsheetScatterStyle(value) {
49
- return 'line' === value || 'lineMarker' === value || 'smooth' === value || 'smoothMarker' === value || 'marker' === value ? value : 'marker';
50
+ function normalizeSpreadsheetPivotFilterValue(value) {
51
+ if (null == value || '' === value) return null;
52
+ if (value instanceof Date) return value.toISOString();
53
+ if ('number' == typeof value) return Number.isFinite(value) ? value : String(value);
54
+ if ('boolean' == typeof value) return value;
55
+ return String(value);
50
56
  }
51
- function normalizeWorkSpreadsheetBubbleScale(value) {
52
- const scale = Number(value);
53
- if (!Number.isFinite(scale)) return 100;
54
- return Math.min(300, Math.max(0, Math.round(scale)));
57
+ function spreadsheetPivotFilterValueKey(value) {
58
+ if (null === value) return 'blank:';
59
+ return `${typeof value}:${String(value)}`;
55
60
  }
56
- function normalizeWorkSpreadsheetBubbleSizeRepresents(value) {
57
- return 'width' === value || 'w' === value ? 'width' : 'area';
61
+ function displaySpreadsheetPivotValue(value) {
62
+ if (null == value || '' === value) return '(空白)';
63
+ if (value instanceof Date) return value.toISOString().slice(0, 10);
64
+ if ('boolean' == typeof value) return value ? 'TRUE' : 'FALSE';
65
+ return String(value);
58
66
  }
59
- function workSpreadsheetChartUsesNumericXAxis(type) {
60
- return 'scatter' === type || 'bubble' === type;
67
+ function finiteSpreadsheetPivotNumber(value) {
68
+ if ('number' == typeof value) return Number.isFinite(value) ? value : null;
69
+ if (value instanceof Date) return value.getTime();
70
+ return null;
61
71
  }
62
- function workSpreadsheetChartSupportsAxes(type) {
63
- return 'pie' !== type && 'doughnut' !== type;
72
+ function spreadsheetPivotFilterItemsFromSource(sourceSheet, sourceBounds, fieldIndex) {
73
+ if (fieldIndex < 0 || fieldIndex > sourceBounds.endColumn - sourceBounds.startColumn) return [];
74
+ const items = new Map();
75
+ for(let row = sourceBounds.startRow + 1; row <= sourceBounds.endRow; row += 1){
76
+ const value = normalizeSpreadsheetPivotFilterValue(spreadsheetPivotCellValue(sourceSheet.data?.[row]?.[sourceBounds.startColumn + fieldIndex]));
77
+ const item = {
78
+ value,
79
+ label: displaySpreadsheetPivotValue(value)
80
+ };
81
+ items.set(spreadsheetPivotFilterValueKey(value), item);
82
+ }
83
+ return Array.from(items.values()).sort((left, right)=>left.label.localeCompare(right.label, 'zh-CN', {
84
+ numeric: true,
85
+ sensitivity: 'base'
86
+ }));
64
87
  }
65
- function normalizeWorkSpreadsheetCombinationSeriesType(value) {
66
- return 'line' === value || 'area' === value || 'column' === value ? value : 'column';
88
+ function buildSpreadsheetPivotOutput(sourceSheet, sourceBounds, fields, pivot) {
89
+ const rowCombinations = new Map();
90
+ const columnCombinations = new Map();
91
+ const cells = new Map();
92
+ const rowTotals = new Map();
93
+ const columnTotals = new Map();
94
+ const totals = pivot.values.map(()=>accumulator());
95
+ const singleColumn = combination([]);
96
+ if (!pivot.columnFields.length) columnCombinations.set(singleColumn.key, singleColumn);
97
+ for(let row = sourceBounds.startRow + 1; row <= sourceBounds.endRow; row += 1){
98
+ const values = fields.map((field)=>spreadsheetPivotCellValue(sourceSheet.data?.[row]?.[sourceBounds.startColumn + field.index]));
99
+ if (values.every((value)=>null == value || '' === value)) continue;
100
+ if (!matchesPivotReportFilters(values, pivot)) continue;
101
+ const rowCombination = combination(pivot.rowFields.map((fieldIndex)=>values[fieldIndex]));
102
+ const columnCombination = pivot.columnFields.length ? combination(pivot.columnFields.map((fieldIndex)=>values[fieldIndex])) : singleColumn;
103
+ rowCombinations.set(rowCombination.key, rowCombination);
104
+ columnCombinations.set(columnCombination.key, columnCombination);
105
+ for (const [valueIndex, valueField] of pivot.values.entries()){
106
+ const value = values[valueField.fieldIndex];
107
+ updateAccumulator(mapAccumulator(cells, cellKey(rowCombination.key, columnCombination.key, valueIndex)), value);
108
+ updateAccumulator(mapAccumulator(rowTotals, cellKey(rowCombination.key, '', valueIndex)), value);
109
+ updateAccumulator(mapAccumulator(columnTotals, cellKey('', columnCombination.key, valueIndex)), value);
110
+ updateAccumulator(totals[valueIndex], value);
111
+ }
112
+ }
113
+ const sortedRows = Array.from(rowCombinations.values()).sort(compareCombinations);
114
+ const sortedColumns = Array.from(columnCombinations.values()).sort(compareCombinations);
115
+ if (!sortedRows.length) return [];
116
+ const headers = [
117
+ ...pivot.rowFields.map((fieldIndex)=>fields[fieldIndex].name),
118
+ ...sortedColumns.flatMap((column)=>pivot.values.map((value)=>columnValueCaption(column, pivot.columnFields, fields, value))),
119
+ ...pivot.rowGrandTotals && pivot.columnFields.length ? pivot.values.map((value)=>`总计 · ${valueCaption(value, fields)}`) : []
120
+ ];
121
+ const output = [
122
+ headers.map((value)=>pivotHeaderCell(value))
123
+ ];
124
+ for (const row of sortedRows){
125
+ const result = row.values.map((value)=>pivotDimensionCell(value));
126
+ for (const column of sortedColumns)for (const [valueIndex, value] of pivot.values.entries())result.push(pivotValueCell(accumulatorValue(cells.get(cellKey(row.key, column.key, valueIndex)), value.aggregation), false));
127
+ if (pivot.rowGrandTotals && pivot.columnFields.length) for (const [valueIndex, value] of pivot.values.entries())result.push(pivotValueCell(accumulatorValue(rowTotals.get(cellKey(row.key, '', valueIndex)), value.aggregation), true));
128
+ output.push(result);
129
+ }
130
+ if (pivot.columnGrandTotals) {
131
+ const result = pivot.rowFields.map((_field, index)=>0 === index ? pivotTotalLabelCell() : pivotValueCell(null, true));
132
+ for (const column of sortedColumns)for (const [valueIndex, value] of pivot.values.entries())result.push(pivotValueCell(accumulatorValue(columnTotals.get(cellKey('', column.key, valueIndex)), value.aggregation), true));
133
+ if (pivot.rowGrandTotals && pivot.columnFields.length) for (const [valueIndex, value] of pivot.values.entries())result.push(pivotValueCell(accumulatorValue(totals[valueIndex], value.aggregation), true));
134
+ output.push(result);
135
+ }
136
+ const reportFilters = pivot.reportFilters ?? [];
137
+ if (!reportFilters.length) return output;
138
+ const width = output[0]?.length ?? 0;
139
+ const filterRows = reportFilters.map((filter)=>{
140
+ const row = Array(width).fill(null);
141
+ row[0] = pivotFilterLabelCell(fields[filter.fieldIndex].name);
142
+ row[1] = pivotFilterSelectionCell(void 0 === filter.selectedItem ? '(全部)' : displaySpreadsheetPivotValue(filter.selectedItem));
143
+ return row;
144
+ });
145
+ filterRows.push(Array(width).fill(null));
146
+ return [
147
+ ...filterRows,
148
+ ...output
149
+ ];
67
150
  }
68
- function normalizeWorkSpreadsheetChartAxisGroup(value) {
69
- return 'secondary' === value ? 'secondary' : 'primary';
151
+ function accumulator() {
152
+ return {
153
+ nonEmptyCount: 0,
154
+ numberCount: 0,
155
+ sum: 0,
156
+ product: 1,
157
+ mean: 0,
158
+ m2: 0
159
+ };
70
160
  }
71
- function workSpreadsheetCombinationSeriesTypeLabel(type) {
72
- if ('line' === type) return '折线图';
73
- if ('area' === type) return '面积图';
74
- return '柱形图';
161
+ function mapAccumulator(map, key) {
162
+ const existing = map.get(key);
163
+ if (existing) return existing;
164
+ const next = accumulator();
165
+ map.set(key, next);
166
+ return next;
75
167
  }
76
- function workSpreadsheetChartSupportsTrendlines(type) {
77
- return 'pie' !== type && 'doughnut' !== type && 'radar' !== type;
168
+ function updateAccumulator(state, value) {
169
+ if (null != value && '' !== value) state.nonEmptyCount += 1;
170
+ const number = finiteSpreadsheetPivotNumber(value);
171
+ if (null === number) return;
172
+ state.numberCount += 1;
173
+ state.sum += number;
174
+ state.product *= number;
175
+ state.minimum = void 0 === state.minimum ? number : Math.min(state.minimum, number);
176
+ state.maximum = void 0 === state.maximum ? number : Math.max(state.maximum, number);
177
+ const delta = number - state.mean;
178
+ state.mean += delta / state.numberCount;
179
+ state.m2 += delta * (number - state.mean);
78
180
  }
79
- function workSpreadsheetChartSupportsErrorBars(type) {
80
- return 'pie' !== type && 'doughnut' !== type && 'radar' !== type;
181
+ function accumulatorValue(state, aggregation) {
182
+ if (!state) return 'sum' === aggregation || 'count' === aggregation || 'counta' === aggregation ? 0 : null;
183
+ if ('count' === aggregation) return state.numberCount;
184
+ if ('counta' === aggregation) return state.nonEmptyCount;
185
+ if ('average' === aggregation) return state.numberCount ? state.sum / state.numberCount : null;
186
+ if ('max' === aggregation) return state.maximum ?? null;
187
+ if ('min' === aggregation) return state.minimum ?? null;
188
+ if ('product' === aggregation) return state.numberCount ? state.product : null;
189
+ if ('stdDev' === aggregation) return state.numberCount > 1 ? Math.sqrt(state.m2 / (state.numberCount - 1)) : null;
190
+ if ('stdDevP' === aggregation) return state.numberCount ? Math.sqrt(state.m2 / state.numberCount) : null;
191
+ if ('var' === aggregation) return state.numberCount > 1 ? state.m2 / (state.numberCount - 1) : null;
192
+ if ('varP' === aggregation) return state.numberCount ? state.m2 / state.numberCount : null;
193
+ return state.sum;
81
194
  }
82
- function normalizeWorkSpreadsheetErrorBars(source, chartType) {
83
- const direction = workSpreadsheetChartUsesNumericXAxis(chartType) && 'x' === source.direction ? 'x' : 'y';
84
- const barType = 'plus' === source.barType || 'minus' === source.barType ? source.barType : 'both';
85
- const valueType = 'percentage' === source.valueType || 'standardDeviation' === source.valueType || 'standardError' === source.valueType || 'custom' === source.valueType || 'fixedValue' === source.valueType ? source.valueType : 'fixedValue';
86
- const fallbackValue = 'percentage' === valueType ? 5 : 1;
87
- const numericValue = Number(source.value);
88
- const value = Number.isFinite(numericValue) ? Math.max(0, numericValue) : fallbackValue;
89
- const plusValues = source.plusValues?.map(normalizedErrorBarAmount);
90
- const minusValues = source.minusValues?.map(normalizedErrorBarAmount);
195
+ function combination(values) {
91
196
  return {
92
- direction,
93
- barType,
94
- valueType,
95
- ...'fixedValue' === valueType || 'percentage' === valueType || 'standardDeviation' === valueType ? {
96
- value
97
- } : {},
98
- ...false === source.showEndCaps ? {
99
- showEndCaps: false
100
- } : {},
101
- ...'custom' === valueType && 'minus' !== barType && plusValues?.length ? {
102
- plusValues
103
- } : {},
104
- ...'custom' === valueType && 'minus' !== barType && source.plusReference?.trim() ? {
105
- plusReference: source.plusReference.trim().replace(/^=/, '')
106
- } : {},
107
- ...'custom' === valueType && 'plus' !== barType && minusValues?.length ? {
108
- minusValues
109
- } : {},
110
- ...'custom' === valueType && 'plus' !== barType && source.minusReference?.trim() ? {
111
- minusReference: source.minusReference.trim().replace(/^=/, '')
112
- } : {}
197
+ key: JSON.stringify(values.map(normalizedPivotKeyValue)),
198
+ values
113
199
  };
114
200
  }
115
- function workSpreadsheetErrorBarValueTypeLabel(type) {
116
- if ('percentage' === type) return '百分比';
117
- if ('standardDeviation' === type) return '标准差';
118
- if ('standardError' === type) return '标准误差';
119
- if ('custom' === type) return '自定义';
120
- return '固定值';
121
- }
122
- function workSpreadsheetErrorBarTypeLabel(type) {
123
- if ('plus' === type) return '正向';
124
- if ('minus' === type) return '负向';
125
- return '双向';
201
+ function normalizedPivotKeyValue(value) {
202
+ if (value instanceof Date) return [
203
+ 'date',
204
+ value.toISOString()
205
+ ];
206
+ if ('number' == typeof value) return [
207
+ 'number',
208
+ Number.isFinite(value) ? value : null
209
+ ];
210
+ if ('boolean' == typeof value) return [
211
+ 'boolean',
212
+ value
213
+ ];
214
+ if (null == value || '' === value) return [
215
+ 'blank'
216
+ ];
217
+ return [
218
+ 'string',
219
+ String(value)
220
+ ];
126
221
  }
127
- function normalizedErrorBarAmount(value) {
128
- return Number.isFinite(value) ? Math.max(0, value) : 0;
222
+ function compareCombinations(left, right) {
223
+ const count = Math.max(left.values.length, right.values.length);
224
+ for(let index = 0; index < count; index += 1){
225
+ const difference = displaySpreadsheetPivotValue(left.values[index]).localeCompare(displaySpreadsheetPivotValue(right.values[index]), 'zh-CN', {
226
+ numeric: true,
227
+ sensitivity: 'base'
228
+ });
229
+ if (difference) return difference;
230
+ }
231
+ return 0;
129
232
  }
130
- function normalizeWorkSpreadsheetDataLabelPosition(value) {
131
- return 'center' === value || 'insideBase' === value || 'insideEnd' === value || 'outsideEnd' === value || 'left' === value || 'right' === value || 'above' === value || 'below' === value || 'bestFit' === value ? value : 'bestFit';
233
+ function cellKey(rowKey, columnKey, valueIndex) {
234
+ return `${rowKey.length}:${rowKey}${columnKey.length}:${columnKey}${valueIndex}`;
132
235
  }
133
- function normalizeWorkSpreadsheetDataLabels(source, chartType) {
134
- const position = source.position ? normalizeWorkSpreadsheetDataLabelPosition(source.position) : void 0;
135
- const separator = 'string' == typeof source.separator ? source.separator.slice(0, 64) : void 0;
136
- return {
137
- ...true === source.showValue ? {
138
- showValue: true
139
- } : {},
140
- ...true === source.showCategoryName ? {
141
- showCategoryName: true
142
- } : {},
143
- ...true === source.showSeriesName ? {
144
- showSeriesName: true
145
- } : {},
146
- ...true === source.showPercentage && ('pie' === chartType || 'doughnut' === chartType) ? {
147
- showPercentage: true
148
- } : {},
149
- ...true === source.showBubbleSize && 'bubble' === chartType ? {
150
- showBubbleSize: true
151
- } : {},
152
- ...void 0 !== separator ? {
153
- separator
154
- } : {},
155
- ...position ? {
156
- position
157
- } : {}
236
+ function columnValueCaption(column, columnFields, fields, value) {
237
+ const dimensions = column.values.map((item, index)=>{
238
+ const label = displaySpreadsheetPivotValue(item);
239
+ return columnFields.length > 1 ? `${fields[columnFields[index]].name}: ${label}` : label;
240
+ });
241
+ return [
242
+ ...dimensions,
243
+ valueCaption(value, fields)
244
+ ].filter(Boolean).join(' · ');
245
+ }
246
+ function valueCaption(value, fields) {
247
+ return value.caption?.trim() || defaultPivotValueCaption(fields[value.fieldIndex]?.name ?? `字段 ${value.fieldIndex + 1}`, value.aggregation);
248
+ }
249
+ function defaultPivotValueCaption(fieldName, aggregation) {
250
+ return `${fieldName}(${spreadsheetPivotAggregationLabel(aggregation)})`;
251
+ }
252
+ function spreadsheetPivotAggregationLabel(aggregation) {
253
+ if ('count' === aggregation) return '计数';
254
+ if ('counta' === aggregation) return '非空计数';
255
+ if ('average' === aggregation) return '平均值';
256
+ if ('max' === aggregation) return '最大值';
257
+ if ('min' === aggregation) return '最小值';
258
+ if ('product' === aggregation) return '乘积';
259
+ if ('stdDev' === aggregation) return '样本标准差';
260
+ if ('stdDevP' === aggregation) return '总体标准差';
261
+ if ('var' === aggregation) return '样本方差';
262
+ if ('varP' === aggregation) return '总体方差';
263
+ return '求和';
264
+ }
265
+ function pivotHeaderCell(value) {
266
+ return {
267
+ v: value,
268
+ m: value,
269
+ bl: 1,
270
+ bg: '#e7f1ec',
271
+ fc: '#173d2b'
158
272
  };
159
273
  }
160
- function workSpreadsheetDataLabelPositionLabel(position) {
161
- if ('center' === position) return '居中';
162
- if ('insideBase' === position) return '内侧基部';
163
- if ('insideEnd' === position) return '内侧末端';
164
- if ('outsideEnd' === position) return '外侧末端';
165
- if ('left' === position) return '左侧';
166
- if ('right' === position) return '右侧';
167
- if ('above' === position) return '上方';
168
- if ('below' === position) return '下方';
169
- return '最佳匹配';
274
+ function pivotDimensionCell(value) {
275
+ const display = displaySpreadsheetPivotValue(value);
276
+ return {
277
+ v: null == value || '' === value ? display : value,
278
+ m: display
279
+ };
170
280
  }
171
- function normalizeWorkSpreadsheetTrendlineType(value) {
172
- return 'exponential' === value || 'logarithmic' === value || 'polynomial' === value || 'power' === value || 'movingAverage' === value || 'linear' === value ? value : 'linear';
281
+ function pivotValueCell(value, total) {
282
+ if (null === value) return total ? {
283
+ v: '',
284
+ m: '',
285
+ bl: 1,
286
+ bg: '#f3f7f5'
287
+ } : null;
288
+ return {
289
+ v: value,
290
+ m: formatPivotNumber(value),
291
+ bl: total ? 1 : void 0,
292
+ bg: total ? '#f3f7f5' : void 0,
293
+ ht: 2
294
+ };
173
295
  }
174
- function normalizeWorkSpreadsheetTrendline(trendline) {
175
- const type = normalizeWorkSpreadsheetTrendlineType(trendline.type);
176
- const name = trendline.name?.trim().slice(0, 255);
177
- const order = normalizedInteger(trendline.order, 2, 6, 2);
178
- const period = normalizedInteger(trendline.period, 2, 255, 2);
179
- const forward = normalizedNonNegativeNumber(trendline.forward);
180
- const backward = normalizedNonNegativeNumber(trendline.backward);
181
- const intercept = Number.isFinite(trendline.intercept) ? Number(trendline.intercept) : void 0;
296
+ function pivotTotalLabelCell() {
182
297
  return {
183
- type,
184
- ...name ? {
185
- name
186
- } : {},
187
- ...'polynomial' === type ? {
188
- order
189
- } : {},
190
- ...'movingAverage' === type ? {
191
- period
192
- } : {},
193
- ...forward > 0 ? {
194
- forward
195
- } : {},
196
- ...backward > 0 ? {
197
- backward
198
- } : {},
199
- ...void 0 !== intercept ? {
200
- intercept
201
- } : {},
202
- ...true === trendline.displayEquation ? {
203
- displayEquation: true
204
- } : {},
205
- ...true === trendline.displayRSquared ? {
206
- displayRSquared: true
207
- } : {}
298
+ v: '总计',
299
+ m: '总计',
300
+ bl: 1,
301
+ bg: '#f3f7f5'
208
302
  };
209
303
  }
210
- function workSpreadsheetTrendlineTypeLabel(type) {
211
- if ('exponential' === type) return '指数';
212
- if ('logarithmic' === type) return '对数';
213
- if ('polynomial' === type) return '多项式';
214
- if ('power' === type) return '幂';
215
- if ('movingAverage' === type) return '移动平均';
216
- return '线性';
304
+ function pivotFilterLabelCell(value) {
305
+ return {
306
+ v: value,
307
+ m: value,
308
+ bl: 1,
309
+ bg: '#f3f7f5',
310
+ fc: '#173d2b'
311
+ };
217
312
  }
218
- function normalizedInteger(value, minimum, maximum, fallback) {
219
- const number = Number(value);
220
- if (!Number.isFinite(number)) return fallback;
221
- return Math.min(maximum, Math.max(minimum, Math.round(number)));
313
+ function pivotFilterSelectionCell(value) {
314
+ return {
315
+ v: value,
316
+ m: value,
317
+ bg: '#f3f7f5'
318
+ };
222
319
  }
223
- function normalizedNonNegativeNumber(value) {
224
- const number = Number(value);
225
- return Number.isFinite(number) ? Math.max(0, number) : 0;
320
+ function formatPivotNumber(value) {
321
+ return Number.isInteger(value) ? String(value) : String(Number(value.toPrecision(15)));
226
322
  }
227
- function workSpreadsheetChartTypeLabel(type) {
228
- if ('bar' === type) return '条形图';
229
- if ('line' === type) return '折线图';
230
- if ('pie' === type) return '饼图';
231
- if ('doughnut' === type) return '圆环图';
232
- if ('area' === type) return '面积图';
233
- if ('radar' === type) return '雷达图';
234
- if ('scatter' === type) return '散点图';
235
- if ('bubble' === type) return '气泡图';
236
- if ('combination' === type) return '组合图';
237
- return '柱形图';
323
+ function matchesPivotReportFilters(values, pivot) {
324
+ return (pivot.reportFilters ?? []).every((filter)=>void 0 === filter.selectedItem || spreadsheetPivotFilterValueKey(normalizeSpreadsheetPivotFilterValue(values[filter.fieldIndex])) === spreadsheetPivotFilterValueKey(filter.selectedItem));
238
325
  }
239
326
  const CELL_REFERENCE = /\$?([A-Z]{1,3})\$?([1-9]\d*)/i;
240
327
  const CELL_OR_RANGE = new RegExp(`^${CELL_REFERENCE.source}(?::${CELL_REFERENCE.source})?$`, 'i');
@@ -392,318 +479,39 @@ function encodeColumn(column) {
392
479
  }
393
480
  return label;
394
481
  }
395
- function spreadsheetPivotCellValue(cell) {
396
- return cell?.v ?? cell?.m ?? null;
397
- }
398
- function normalizeSpreadsheetPivotFilterValue(value) {
399
- if (null == value || '' === value) return null;
400
- if (value instanceof Date) return value.toISOString();
401
- if ('number' == typeof value) return Number.isFinite(value) ? value : String(value);
402
- if ('boolean' == typeof value) return value;
403
- return String(value);
404
- }
405
- function spreadsheetPivotFilterValueKey(value) {
406
- if (null === value) return 'blank:';
407
- return `${typeof value}:${String(value)}`;
408
- }
409
- function displaySpreadsheetPivotValue(value) {
410
- if (null == value || '' === value) return '(空白)';
411
- if (value instanceof Date) return value.toISOString().slice(0, 10);
412
- if ('boolean' == typeof value) return value ? 'TRUE' : 'FALSE';
413
- return String(value);
414
- }
415
- function finiteSpreadsheetPivotNumber(value) {
416
- if ('number' == typeof value) return Number.isFinite(value) ? value : null;
417
- if (value instanceof Date) return value.getTime();
418
- return null;
482
+ const MAXIMUM_SOURCE_CELLS = 100000;
483
+ const MAXIMUM_OUTPUT_CELLS = 20000;
484
+ const MAXIMUM_XLSX_ROW = 1048575;
485
+ const MAXIMUM_XLSX_COLUMN = 16383;
486
+ function spreadsheetPivotCount(content) {
487
+ return content.sheets.reduce((count, sheet)=>count + (sheet.pivotTables?.length ?? 0), 0);
419
488
  }
420
- function spreadsheetPivotFilterItemsFromSource(sourceSheet, sourceBounds, fieldIndex) {
421
- if (fieldIndex < 0 || fieldIndex > sourceBounds.endColumn - sourceBounds.startColumn) return [];
422
- const items = new Map();
423
- for(let row = sourceBounds.startRow + 1; row <= sourceBounds.endRow; row += 1){
424
- const value = normalizeSpreadsheetPivotFilterValue(spreadsheetPivotCellValue(sourceSheet.data?.[row]?.[sourceBounds.startColumn + fieldIndex]));
425
- const item = {
426
- value,
427
- label: displaySpreadsheetPivotValue(value)
428
- };
429
- items.set(spreadsheetPivotFilterValueKey(value), item);
489
+ function spreadsheetPivotFields(content, pivot) {
490
+ const sourceSheet = content.sheets.find((sheet)=>sheet.id === pivot.sourceSheetId);
491
+ const range = singleRange(pivot.sourceReference);
492
+ if (!sourceSheet || !range) return [];
493
+ const fields = [];
494
+ for(let column = range.startColumn; column <= range.endColumn; column += 1){
495
+ const header = spreadsheetPivotCellValue(sourceSheet.data?.[range.startRow]?.[column]);
496
+ if (null == header || '' === String(header).trim()) return [];
497
+ const name = displaySpreadsheetPivotValue(header).trim();
498
+ let numeric = false;
499
+ for(let row = range.startRow + 1; row <= range.endRow; row += 1){
500
+ const value = spreadsheetPivotCellValue(sourceSheet.data?.[row]?.[column]);
501
+ if (null != value && '' !== value) {
502
+ if (null !== finiteSpreadsheetPivotNumber(value)) {
503
+ numeric = true;
504
+ break;
505
+ }
506
+ }
507
+ }
508
+ fields.push({
509
+ index: column - range.startColumn,
510
+ name,
511
+ numeric
512
+ });
430
513
  }
431
- return Array.from(items.values()).sort((left, right)=>left.label.localeCompare(right.label, 'zh-CN', {
432
- numeric: true,
433
- sensitivity: 'base'
434
- }));
435
- }
436
- function buildSpreadsheetPivotOutput(sourceSheet, sourceBounds, fields, pivot) {
437
- const rowCombinations = new Map();
438
- const columnCombinations = new Map();
439
- const cells = new Map();
440
- const rowTotals = new Map();
441
- const columnTotals = new Map();
442
- const totals = pivot.values.map(()=>accumulator());
443
- const singleColumn = combination([]);
444
- if (!pivot.columnFields.length) columnCombinations.set(singleColumn.key, singleColumn);
445
- for(let row = sourceBounds.startRow + 1; row <= sourceBounds.endRow; row += 1){
446
- const values = fields.map((field)=>spreadsheetPivotCellValue(sourceSheet.data?.[row]?.[sourceBounds.startColumn + field.index]));
447
- if (values.every((value)=>null == value || '' === value)) continue;
448
- if (!matchesPivotReportFilters(values, pivot)) continue;
449
- const rowCombination = combination(pivot.rowFields.map((fieldIndex)=>values[fieldIndex]));
450
- const columnCombination = pivot.columnFields.length ? combination(pivot.columnFields.map((fieldIndex)=>values[fieldIndex])) : singleColumn;
451
- rowCombinations.set(rowCombination.key, rowCombination);
452
- columnCombinations.set(columnCombination.key, columnCombination);
453
- for (const [valueIndex, valueField] of pivot.values.entries()){
454
- const value = values[valueField.fieldIndex];
455
- updateAccumulator(mapAccumulator(cells, cellKey(rowCombination.key, columnCombination.key, valueIndex)), value);
456
- updateAccumulator(mapAccumulator(rowTotals, cellKey(rowCombination.key, '', valueIndex)), value);
457
- updateAccumulator(mapAccumulator(columnTotals, cellKey('', columnCombination.key, valueIndex)), value);
458
- updateAccumulator(totals[valueIndex], value);
459
- }
460
- }
461
- const sortedRows = Array.from(rowCombinations.values()).sort(compareCombinations);
462
- const sortedColumns = Array.from(columnCombinations.values()).sort(compareCombinations);
463
- if (!sortedRows.length) return [];
464
- const headers = [
465
- ...pivot.rowFields.map((fieldIndex)=>fields[fieldIndex].name),
466
- ...sortedColumns.flatMap((column)=>pivot.values.map((value)=>columnValueCaption(column, pivot.columnFields, fields, value))),
467
- ...pivot.rowGrandTotals && pivot.columnFields.length ? pivot.values.map((value)=>`总计 · ${valueCaption(value, fields)}`) : []
468
- ];
469
- const output = [
470
- headers.map((value)=>pivotHeaderCell(value))
471
- ];
472
- for (const row of sortedRows){
473
- const result = row.values.map((value)=>pivotDimensionCell(value));
474
- for (const column of sortedColumns)for (const [valueIndex, value] of pivot.values.entries())result.push(pivotValueCell(accumulatorValue(cells.get(cellKey(row.key, column.key, valueIndex)), value.aggregation), false));
475
- if (pivot.rowGrandTotals && pivot.columnFields.length) for (const [valueIndex, value] of pivot.values.entries())result.push(pivotValueCell(accumulatorValue(rowTotals.get(cellKey(row.key, '', valueIndex)), value.aggregation), true));
476
- output.push(result);
477
- }
478
- if (pivot.columnGrandTotals) {
479
- const result = pivot.rowFields.map((_field, index)=>0 === index ? pivotTotalLabelCell() : pivotValueCell(null, true));
480
- for (const column of sortedColumns)for (const [valueIndex, value] of pivot.values.entries())result.push(pivotValueCell(accumulatorValue(columnTotals.get(cellKey('', column.key, valueIndex)), value.aggregation), true));
481
- if (pivot.rowGrandTotals && pivot.columnFields.length) for (const [valueIndex, value] of pivot.values.entries())result.push(pivotValueCell(accumulatorValue(totals[valueIndex], value.aggregation), true));
482
- output.push(result);
483
- }
484
- const reportFilters = pivot.reportFilters ?? [];
485
- if (!reportFilters.length) return output;
486
- const width = output[0]?.length ?? 0;
487
- const filterRows = reportFilters.map((filter)=>{
488
- const row = Array(width).fill(null);
489
- row[0] = pivotFilterLabelCell(fields[filter.fieldIndex].name);
490
- row[1] = pivotFilterSelectionCell(void 0 === filter.selectedItem ? '(全部)' : displaySpreadsheetPivotValue(filter.selectedItem));
491
- return row;
492
- });
493
- filterRows.push(Array(width).fill(null));
494
- return [
495
- ...filterRows,
496
- ...output
497
- ];
498
- }
499
- function accumulator() {
500
- return {
501
- nonEmptyCount: 0,
502
- numberCount: 0,
503
- sum: 0,
504
- product: 1,
505
- mean: 0,
506
- m2: 0
507
- };
508
- }
509
- function mapAccumulator(map, key) {
510
- const existing = map.get(key);
511
- if (existing) return existing;
512
- const next = accumulator();
513
- map.set(key, next);
514
- return next;
515
- }
516
- function updateAccumulator(state, value) {
517
- if (null != value && '' !== value) state.nonEmptyCount += 1;
518
- const number = finiteSpreadsheetPivotNumber(value);
519
- if (null === number) return;
520
- state.numberCount += 1;
521
- state.sum += number;
522
- state.product *= number;
523
- state.minimum = void 0 === state.minimum ? number : Math.min(state.minimum, number);
524
- state.maximum = void 0 === state.maximum ? number : Math.max(state.maximum, number);
525
- const delta = number - state.mean;
526
- state.mean += delta / state.numberCount;
527
- state.m2 += delta * (number - state.mean);
528
- }
529
- function accumulatorValue(state, aggregation) {
530
- if (!state) return 'sum' === aggregation || 'count' === aggregation || 'counta' === aggregation ? 0 : null;
531
- if ('count' === aggregation) return state.numberCount;
532
- if ('counta' === aggregation) return state.nonEmptyCount;
533
- if ('average' === aggregation) return state.numberCount ? state.sum / state.numberCount : null;
534
- if ('max' === aggregation) return state.maximum ?? null;
535
- if ('min' === aggregation) return state.minimum ?? null;
536
- if ('product' === aggregation) return state.numberCount ? state.product : null;
537
- if ('stdDev' === aggregation) return state.numberCount > 1 ? Math.sqrt(state.m2 / (state.numberCount - 1)) : null;
538
- if ('stdDevP' === aggregation) return state.numberCount ? Math.sqrt(state.m2 / state.numberCount) : null;
539
- if ('var' === aggregation) return state.numberCount > 1 ? state.m2 / (state.numberCount - 1) : null;
540
- if ('varP' === aggregation) return state.numberCount ? state.m2 / state.numberCount : null;
541
- return state.sum;
542
- }
543
- function combination(values) {
544
- return {
545
- key: JSON.stringify(values.map(normalizedPivotKeyValue)),
546
- values
547
- };
548
- }
549
- function normalizedPivotKeyValue(value) {
550
- if (value instanceof Date) return [
551
- 'date',
552
- value.toISOString()
553
- ];
554
- if ('number' == typeof value) return [
555
- 'number',
556
- Number.isFinite(value) ? value : null
557
- ];
558
- if ('boolean' == typeof value) return [
559
- 'boolean',
560
- value
561
- ];
562
- if (null == value || '' === value) return [
563
- 'blank'
564
- ];
565
- return [
566
- 'string',
567
- String(value)
568
- ];
569
- }
570
- function compareCombinations(left, right) {
571
- const count = Math.max(left.values.length, right.values.length);
572
- for(let index = 0; index < count; index += 1){
573
- const difference = displaySpreadsheetPivotValue(left.values[index]).localeCompare(displaySpreadsheetPivotValue(right.values[index]), 'zh-CN', {
574
- numeric: true,
575
- sensitivity: 'base'
576
- });
577
- if (difference) return difference;
578
- }
579
- return 0;
580
- }
581
- function cellKey(rowKey, columnKey, valueIndex) {
582
- return `${rowKey.length}:${rowKey}${columnKey.length}:${columnKey}${valueIndex}`;
583
- }
584
- function columnValueCaption(column, columnFields, fields, value) {
585
- const dimensions = column.values.map((item, index)=>{
586
- const label = displaySpreadsheetPivotValue(item);
587
- return columnFields.length > 1 ? `${fields[columnFields[index]].name}: ${label}` : label;
588
- });
589
- return [
590
- ...dimensions,
591
- valueCaption(value, fields)
592
- ].filter(Boolean).join(' · ');
593
- }
594
- function valueCaption(value, fields) {
595
- return value.caption?.trim() || defaultPivotValueCaption(fields[value.fieldIndex]?.name ?? `字段 ${value.fieldIndex + 1}`, value.aggregation);
596
- }
597
- function defaultPivotValueCaption(fieldName, aggregation) {
598
- return `${fieldName}(${spreadsheetPivotAggregationLabel(aggregation)})`;
599
- }
600
- function spreadsheetPivotAggregationLabel(aggregation) {
601
- if ('count' === aggregation) return '计数';
602
- if ('counta' === aggregation) return '非空计数';
603
- if ('average' === aggregation) return '平均值';
604
- if ('max' === aggregation) return '最大值';
605
- if ('min' === aggregation) return '最小值';
606
- if ('product' === aggregation) return '乘积';
607
- if ('stdDev' === aggregation) return '样本标准差';
608
- if ('stdDevP' === aggregation) return '总体标准差';
609
- if ('var' === aggregation) return '样本方差';
610
- if ('varP' === aggregation) return '总体方差';
611
- return '求和';
612
- }
613
- function pivotHeaderCell(value) {
614
- return {
615
- v: value,
616
- m: value,
617
- bl: 1,
618
- bg: '#e7f1ec',
619
- fc: '#173d2b'
620
- };
621
- }
622
- function pivotDimensionCell(value) {
623
- const display = displaySpreadsheetPivotValue(value);
624
- return {
625
- v: null == value || '' === value ? display : value,
626
- m: display
627
- };
628
- }
629
- function pivotValueCell(value, total) {
630
- if (null === value) return total ? {
631
- v: '',
632
- m: '',
633
- bl: 1,
634
- bg: '#f3f7f5'
635
- } : null;
636
- return {
637
- v: value,
638
- m: formatPivotNumber(value),
639
- bl: total ? 1 : void 0,
640
- bg: total ? '#f3f7f5' : void 0,
641
- ht: 2
642
- };
643
- }
644
- function pivotTotalLabelCell() {
645
- return {
646
- v: '总计',
647
- m: '总计',
648
- bl: 1,
649
- bg: '#f3f7f5'
650
- };
651
- }
652
- function pivotFilterLabelCell(value) {
653
- return {
654
- v: value,
655
- m: value,
656
- bl: 1,
657
- bg: '#f3f7f5',
658
- fc: '#173d2b'
659
- };
660
- }
661
- function pivotFilterSelectionCell(value) {
662
- return {
663
- v: value,
664
- m: value,
665
- bg: '#f3f7f5'
666
- };
667
- }
668
- function formatPivotNumber(value) {
669
- return Number.isInteger(value) ? String(value) : String(Number(value.toPrecision(15)));
670
- }
671
- function matchesPivotReportFilters(values, pivot) {
672
- return (pivot.reportFilters ?? []).every((filter)=>void 0 === filter.selectedItem || spreadsheetPivotFilterValueKey(normalizeSpreadsheetPivotFilterValue(values[filter.fieldIndex])) === spreadsheetPivotFilterValueKey(filter.selectedItem));
673
- }
674
- const MAXIMUM_SOURCE_CELLS = 100000;
675
- const MAXIMUM_OUTPUT_CELLS = 20000;
676
- const MAXIMUM_XLSX_ROW = 1048575;
677
- const MAXIMUM_XLSX_COLUMN = 16383;
678
- function spreadsheetPivotCount(content) {
679
- return content.sheets.reduce((count, sheet)=>count + (sheet.pivotTables?.length ?? 0), 0);
680
- }
681
- function spreadsheetPivotFields(content, pivot) {
682
- const sourceSheet = content.sheets.find((sheet)=>sheet.id === pivot.sourceSheetId);
683
- const range = singleRange(pivot.sourceReference);
684
- if (!sourceSheet || !range) return [];
685
- const fields = [];
686
- for(let column = range.startColumn; column <= range.endColumn; column += 1){
687
- const header = spreadsheetPivotCellValue(sourceSheet.data?.[range.startRow]?.[column]);
688
- if (null == header || '' === String(header).trim()) return [];
689
- const name = displaySpreadsheetPivotValue(header).trim();
690
- let numeric = false;
691
- for(let row = range.startRow + 1; row <= range.endRow; row += 1){
692
- const value = spreadsheetPivotCellValue(sourceSheet.data?.[row]?.[column]);
693
- if (null != value && '' !== value) {
694
- if (null !== finiteSpreadsheetPivotNumber(value)) {
695
- numeric = true;
696
- break;
697
- }
698
- }
699
- }
700
- fields.push({
701
- index: column - range.startColumn,
702
- name,
703
- numeric
704
- });
705
- }
706
- return uniqueFieldNames(fields) ? fields : [];
514
+ return uniqueFieldNames(fields) ? fields : [];
707
515
  }
708
516
  function spreadsheetPivotFilterItems(content, pivot, fieldIndex) {
709
517
  const sourceSheet = content.sheets.find((sheet)=>sheet.id === pivot.sourceSheetId);
@@ -1038,64 +846,590 @@ function selectionBounds(selection) {
1038
846
  endColumn: Math.max(0, Math.max(selection.column[0], selection.column[1]))
1039
847
  };
1040
848
  }
1041
- function containsCell(bounds, row, column) {
1042
- return row >= bounds.startRow && row <= bounds.endRow && column >= bounds.startColumn && column <= bounds.endColumn;
1043
- }
1044
- function rangesOverlap(left, right) {
1045
- return left.startRow <= right.endRow && left.endRow >= right.startRow && left.startColumn <= right.endColumn && left.endColumn >= right.startColumn;
1046
- }
1047
- function formatBounds(bounds) {
1048
- return formatSpreadsheetCellRanges([
1049
- {
1050
- row: [
1051
- bounds.startRow,
1052
- bounds.endRow
1053
- ],
1054
- column: [
1055
- bounds.startColumn,
1056
- bounds.endColumn
1057
- ]
1058
- }
1059
- ]);
849
+ function containsCell(bounds, row, column) {
850
+ return row >= bounds.startRow && row <= bounds.endRow && column >= bounds.startColumn && column <= bounds.endColumn;
851
+ }
852
+ function rangesOverlap(left, right) {
853
+ return left.startRow <= right.endRow && left.endRow >= right.startRow && left.startColumn <= right.endColumn && left.endColumn >= right.startColumn;
854
+ }
855
+ function formatBounds(bounds) {
856
+ return formatSpreadsheetCellRanges([
857
+ {
858
+ row: [
859
+ bounds.startRow,
860
+ bounds.endRow
861
+ ],
862
+ column: [
863
+ bounds.startColumn,
864
+ bounds.endColumn
865
+ ]
866
+ }
867
+ ]);
868
+ }
869
+ function cloneCellMatrix(source) {
870
+ return (source ?? []).map((row)=>[
871
+ ...row
872
+ ]);
873
+ }
874
+ function clearRange(data, bounds) {
875
+ if (!bounds) return;
876
+ for(let row = bounds.startRow; row <= bounds.endRow; row += 1)if (data[row]) for(let column = bounds.startColumn; column <= bounds.endColumn; column += 1)data[row][column] = null;
877
+ }
878
+ function writePivotOutput(data, bounds, output) {
879
+ const width = output[0]?.length ?? 0;
880
+ while(data.length <= bounds.endRow)data.push([]);
881
+ for(let row = 0; row < output.length; row += 1){
882
+ const target = data[bounds.startRow + row] ?? [];
883
+ data[bounds.startRow + row] = target;
884
+ while(target.length < bounds.startColumn + width)target.push(null);
885
+ for(let column = 0; column < width; column += 1)target[bounds.startColumn + column] = output[row][column] ?? null;
886
+ }
887
+ }
888
+ function nextSheetName(content, base) {
889
+ const existing = new Set(content.sheets.map((sheet)=>sheet.name.toLocaleLowerCase()));
890
+ if (!existing.has(base.toLocaleLowerCase())) return base;
891
+ let index = 2;
892
+ while(existing.has(`${base} ${index}`.toLocaleLowerCase()))index += 1;
893
+ return `${base} ${index}`;
894
+ }
895
+ function nextPivotName(content) {
896
+ const existing = new Set(content.sheets.flatMap((sheet)=>(sheet.pivotTables ?? []).map((pivot)=>pivot.name.toLocaleLowerCase())));
897
+ let index = 1;
898
+ while(existing.has(`pivottable${index}`))index += 1;
899
+ return `PivotTable${index}`;
900
+ }
901
+ function invalid(code, message) {
902
+ return {
903
+ valid: false,
904
+ code,
905
+ message
906
+ };
907
+ }
908
+ const DEFAULT_SPREADSHEET_CALCULATION_SETTINGS = {
909
+ mode: 'automatic',
910
+ fullCalculationOnLoad: false,
911
+ forceFullCalculation: false,
912
+ iterativeCalculation: false,
913
+ maximumIterations: 100,
914
+ maximumChange: 0.001,
915
+ fullPrecision: true
916
+ };
917
+ const VOLATILE_FUNCTIONS = new Set([
918
+ 'CELL',
919
+ 'INFO',
920
+ 'INDIRECT',
921
+ 'NOW',
922
+ 'OFFSET',
923
+ 'RAND',
924
+ 'RANDBETWEEN',
925
+ 'TODAY'
926
+ ]);
927
+ const FUTURE_FUNCTIONS = new Set([
928
+ 'ARRAYTOTEXT',
929
+ 'BYCOL',
930
+ 'BYROW',
931
+ 'CHOOSECOLS',
932
+ 'CHOOSEROWS',
933
+ 'DROP',
934
+ 'EXPAND',
935
+ 'FIELDVALUE',
936
+ 'FILTER',
937
+ 'HSTACK',
938
+ 'IMAGE',
939
+ 'ISOMITTED',
940
+ 'LAMBDA',
941
+ 'LET',
942
+ 'MAKEARRAY',
943
+ 'MAP',
944
+ 'RANDARRAY',
945
+ 'REDUCE',
946
+ 'SCAN',
947
+ 'SEQUENCE',
948
+ 'SORT',
949
+ 'SORTBY',
950
+ 'STOCKHISTORY',
951
+ 'TAKE',
952
+ 'TEXTAFTER',
953
+ 'TEXTBEFORE',
954
+ 'TEXTSPLIT',
955
+ 'TOCOL',
956
+ 'TOROW',
957
+ 'UNIQUE',
958
+ 'VALUETOTEXT',
959
+ 'VSTACK',
960
+ 'WRAPCOLS',
961
+ 'WRAPROWS',
962
+ 'XLOOKUP',
963
+ 'XMATCH'
964
+ ]);
965
+ const FUTURE_FUNCTION_PREFIXES = new Map([
966
+ [
967
+ 'FILTER',
968
+ '_xlfn._xlws.'
969
+ ],
970
+ [
971
+ 'SORT',
972
+ '_xlfn._xlws.'
973
+ ],
974
+ [
975
+ 'SORTBY',
976
+ '_xlfn._xlws.'
977
+ ],
978
+ [
979
+ 'UNIQUE',
980
+ '_xlfn.'
981
+ ],
982
+ ...Array.from(FUTURE_FUNCTIONS).filter((name)=>![
983
+ 'FILTER',
984
+ 'SORT',
985
+ 'SORTBY',
986
+ 'UNIQUE'
987
+ ].includes(name)).map((name)=>[
988
+ name,
989
+ '_xlfn.'
990
+ ])
991
+ ]);
992
+ function effectiveSpreadsheetCalculationSettings(settings) {
993
+ if (!settings) return {
994
+ ...DEFAULT_SPREADSHEET_CALCULATION_SETTINGS
995
+ };
996
+ return {
997
+ mode: settings.mode,
998
+ fullCalculationOnLoad: Boolean(settings.fullCalculationOnLoad),
999
+ forceFullCalculation: Boolean(settings.forceFullCalculation),
1000
+ iterativeCalculation: Boolean(settings.iterativeCalculation),
1001
+ maximumIterations: clampedInteger(settings.maximumIterations, 1, 10000, 100),
1002
+ maximumChange: positiveNumber(settings.maximumChange, 0.001),
1003
+ fullPrecision: false !== settings.fullPrecision
1004
+ };
1005
+ }
1006
+ function editableSpreadsheetFormula(source) {
1007
+ return transformFormulaOutsideStrings(source, (segment)=>segment.replace(/_xlfn\._xlws\.(?=[A-Z][A-Z0-9_.]*\s*\()/gi, '').replace(/_xlfn\.(?=[A-Z][A-Z0-9_.]*\s*\()/gi, '').replace(/_xlws\.(?=[A-Z][A-Z0-9_.]*\s*\()/gi, ''));
1008
+ }
1009
+ function spreadsheetFormulaForXlsx(currentFormula, sourceFormula) {
1010
+ const current = currentFormula.trim().replace(/^=/, '');
1011
+ const source = sourceFormula?.trim().replace(/^=/, '');
1012
+ if (source && comparableFormula(source) === comparableFormula(current)) return source;
1013
+ return futureFunctionPrefixes(current, source);
1014
+ }
1015
+ function spreadsheetFormulaRangeForCell(sheet, row, column) {
1016
+ const address = spreadsheetCellAddress(row, column);
1017
+ return (sheet.formulaMetadata?.ranges ?? []).find((range)=>range.anchor.toUpperCase().replaceAll('$', '') === address);
1018
+ }
1019
+ function spreadsheetFormulaRangesForSelection(sheet, selection) {
1020
+ return (sheet.formulaMetadata?.ranges ?? []).filter((range)=>work_spreadsheet_formulas_rangesOverlap(selection, parseSpreadsheetFormulaRange(range.reference)));
1021
+ }
1022
+ function spreadsheetFormulaRangeConflict(sheet, range) {
1023
+ const bounds = parseSpreadsheetFormulaRange(range.reference);
1024
+ if (!bounds) return '引用范围无效';
1025
+ const anchor = parseSpreadsheetCellAddress(range.anchor);
1026
+ if (!anchor || !work_spreadsheet_formulas_containsCell(bounds, anchor.row, anchor.column)) return '锚点不在引用范围内';
1027
+ const anchorCell = sheet.data?.[anchor.row]?.[anchor.column];
1028
+ if (!anchorCell) return '锚点单元格不存在';
1029
+ const overlaps = (sheet.formulaMetadata?.ranges ?? []).filter((candidate)=>candidate !== range && work_spreadsheet_formulas_rangesOverlap(bounds, parseSpreadsheetFormulaRange(candidate.reference)));
1030
+ if (overlaps.length) return '与其他公式范围重叠';
1031
+ for (const [row, cells] of sparseArrayEntries(sheet.data))if (!(row < bounds.startRow) && !(row > bounds.endRow)) {
1032
+ for (const [column, cell] of sparseArrayEntries(cells))if (!(column < bounds.startColumn) && !(column > bounds.endColumn)) {
1033
+ if (row !== anchor.row || column !== anchor.column) {
1034
+ if (cell?.f) return `${spreadsheetCellAddress(row, column)} 包含独立公式`;
1035
+ }
1036
+ }
1037
+ }
1038
+ if (Object.values(sheet.config?.merge ?? {}).some((merge)=>work_spreadsheet_formulas_rangesOverlap(bounds, {
1039
+ startRow: merge.r,
1040
+ endRow: merge.r + merge.rs - 1,
1041
+ startColumn: merge.c,
1042
+ endColumn: merge.c + merge.cs - 1
1043
+ }))) return '范围内包含合并单元格';
1044
+ if ('data-table' !== range.type && !anchorCell.f) return '锚点公式已被删除';
1045
+ return null;
1046
+ }
1047
+ function spreadsheetFormulaFunctions(formula) {
1048
+ const withoutStrings = formula.replace(/"(?:[^"]|"")*"/g, '""');
1049
+ const functions = new Set();
1050
+ for (const match of withoutStrings.matchAll(/(?:_xlfn\.)?(?:_xlws\.)?([A-Z][A-Z0-9_.]*)\s*\(/gi))if (match[1]) functions.add(match[1].toUpperCase());
1051
+ return Array.from(functions);
1052
+ }
1053
+ function formulaHasExternalReference(formula) {
1054
+ return /\[[^\]]+\][^!]*!/i.test(formula.replace(/"(?:[^"]|"")*"/g, '""'));
1055
+ }
1056
+ function formulaHasStructuredReference(formula) {
1057
+ const withoutExternalReferences = formula.replace(/"(?:[^"]|"")*"/g, '""').replace(/\[[^\]]+\][^!]*!/gi, '');
1058
+ return /\[(?:@|#)[^\]]+\]/i.test(withoutExternalReferences) || /\b[A-Z_\\][A-Z0-9_.]*\s*\[[^\]]+\]/i.test(withoutExternalReferences);
1059
+ }
1060
+ function volatileSpreadsheetFormulaFunctions(formula) {
1061
+ return spreadsheetFormulaFunctions(formula).filter((name)=>VOLATILE_FUNCTIONS.has(name));
1062
+ }
1063
+ function spreadsheetCellAddress(row, column) {
1064
+ let value = column + 1;
1065
+ let label = '';
1066
+ while(value > 0){
1067
+ value -= 1;
1068
+ label = String.fromCharCode(65 + value % 26) + label;
1069
+ value = Math.floor(value / 26);
1070
+ }
1071
+ return `${label}${row + 1}`;
1072
+ }
1073
+ function parseSpreadsheetFormulaRange(reference) {
1074
+ const normalized = reference.trim().replaceAll('$', '').split('!').at(-1) ?? '';
1075
+ const [startText, endText = startText] = normalized.split(':');
1076
+ const start = parseSpreadsheetCellAddress(startText);
1077
+ const end = parseSpreadsheetCellAddress(endText);
1078
+ if (!start || !end) return null;
1079
+ return {
1080
+ startRow: Math.min(start.row, end.row),
1081
+ endRow: Math.max(start.row, end.row),
1082
+ startColumn: Math.min(start.column, end.column),
1083
+ endColumn: Math.max(start.column, end.column)
1084
+ };
1085
+ }
1086
+ function parseSpreadsheetCellAddress(address) {
1087
+ const match = /^([A-Z]{1,3})([1-9]\d*)$/i.exec(address.trim().replaceAll('$', ''));
1088
+ if (!match) return null;
1089
+ let column = 0;
1090
+ for (const character of match[1].toUpperCase())column = 26 * column + character.charCodeAt(0) - 64;
1091
+ const row = Number(match[2]) - 1;
1092
+ if (row > 1048575 || column > 16384) return null;
1093
+ return {
1094
+ row,
1095
+ column: column - 1
1096
+ };
1097
+ }
1098
+ function work_spreadsheet_formulas_containsCell(bounds, row, column) {
1099
+ return row >= bounds.startRow && row <= bounds.endRow && column >= bounds.startColumn && column <= bounds.endColumn;
1100
+ }
1101
+ function work_spreadsheet_formulas_rangesOverlap(first, second) {
1102
+ if (!second) return false;
1103
+ return first.startRow <= second.endRow && first.endRow >= second.startRow && first.startColumn <= second.endColumn && first.endColumn >= second.startColumn;
1104
+ }
1105
+ function comparableFormula(formula) {
1106
+ return editableSpreadsheetFormula(formula).trim();
1107
+ }
1108
+ function futureFunctionPrefixes(formula, sourceFormula) {
1109
+ const sourcePrefixes = new Map();
1110
+ const sourceFunctions = (sourceFormula ?? '').replace(/"(?:[^"]|"")*"/g, '""');
1111
+ for (const match of sourceFunctions.matchAll(/(_xlfn\.(?:_xlws\.)?)([A-Z][A-Z0-9_.]*)\s*(?=\()/gi))if (match[1] && match[2]) sourcePrefixes.set(match[2].toUpperCase(), match[1]);
1112
+ return transformFormulaOutsideStrings(formula, (segment)=>segment.replace(/(?<![A-Z0-9_.])([A-Z][A-Z0-9_.]*)\s*(?=\()/gi, (match, functionName)=>{
1113
+ const normalized = functionName.toUpperCase();
1114
+ const prefix = sourcePrefixes.get(normalized) ?? FUTURE_FUNCTION_PREFIXES.get(normalized);
1115
+ return prefix ? `${prefix}${match}` : match;
1116
+ }));
1117
+ }
1118
+ function clampedInteger(value, minimum, maximum, fallback) {
1119
+ if (!Number.isFinite(value)) return fallback;
1120
+ return Math.min(maximum, Math.max(minimum, Math.round(value)));
1121
+ }
1122
+ function positiveNumber(value, fallback) {
1123
+ return Number.isFinite(value) && value > 0 ? value : fallback;
1124
+ }
1125
+ function transformFormulaOutsideStrings(formula, transform) {
1126
+ return formula.split(/("(?:[^"]|"")*")/).map((segment, index)=>index % 2 ? segment : transform(segment)).join('');
1127
+ }
1128
+ const XLSX_EMU_PER_PIXEL = 9525;
1129
+ const DEFAULT_COLUMN_WIDTH = 96;
1130
+ const DEFAULT_ROW_HEIGHT = 24;
1131
+ function readXlsxDrawingAnchor(anchor) {
1132
+ const extent = directChild(anchor, 'ext');
1133
+ const position = directChild(anchor, 'pos');
1134
+ return {
1135
+ from: readMarker(directChild(anchor, 'from')),
1136
+ to: readMarker(directChild(anchor, 'to')),
1137
+ leftEmu: numberAttribute(position, 'x'),
1138
+ topEmu: numberAttribute(position, 'y'),
1139
+ widthEmu: numberAttribute(extent, 'cx'),
1140
+ heightEmu: numberAttribute(extent, 'cy')
1141
+ };
1142
+ }
1143
+ function xlsxDrawingAnchorToBounds(anchor, config) {
1144
+ const left = anchor.from ? markerPosition(anchor.from.column, anchor.from.columnOffsetEmu, (index)=>spreadsheetColumnWidth(config, index)) : emuToPixels(anchor.leftEmu);
1145
+ const top = anchor.from ? markerPosition(anchor.from.row, anchor.from.rowOffsetEmu, (index)=>spreadsheetRowHeight(config, index)) : emuToPixels(anchor.topEmu);
1146
+ const right = anchor.to ? markerPosition(anchor.to.column, anchor.to.columnOffsetEmu, (index)=>spreadsheetColumnWidth(config, index)) : left + emuToPixels(anchor.widthEmu);
1147
+ const bottom = anchor.to ? markerPosition(anchor.to.row, anchor.to.rowOffsetEmu, (index)=>spreadsheetRowHeight(config, index)) : top + emuToPixels(anchor.heightEmu);
1148
+ if (![
1149
+ left,
1150
+ top,
1151
+ right,
1152
+ bottom
1153
+ ].every(Number.isFinite)) return null;
1154
+ return {
1155
+ left: roundPosition(Math.max(0, left)),
1156
+ top: roundPosition(Math.max(0, top)),
1157
+ width: roundPosition(Math.max(1, right - left)),
1158
+ height: roundPosition(Math.max(1, bottom - top))
1159
+ };
1160
+ }
1161
+ function xlsxTwoCellAnchorMarkers(bounds, sheet) {
1162
+ const fromColumn = axisMarker(bounds.left, (index)=>spreadsheetColumnWidth(sheet.config, index));
1163
+ const toColumn = axisMarker(bounds.left + bounds.width, (index)=>spreadsheetColumnWidth(sheet.config, index));
1164
+ const fromRow = axisMarker(bounds.top, (index)=>spreadsheetRowHeight(sheet.config, index));
1165
+ const toRow = axisMarker(bounds.top + bounds.height, (index)=>spreadsheetRowHeight(sheet.config, index));
1166
+ return `${markerXml('from', fromColumn, fromRow)}${markerXml('to', toColumn, toRow)}`;
1167
+ }
1168
+ function spreadsheetColumnWidth(config, index) {
1169
+ const key = String(index);
1170
+ if (Object.hasOwn(config?.colhidden ?? {}, key)) return 0;
1171
+ const width = config?.columnlen?.[key];
1172
+ return 'number' == typeof width && width > 0 ? width : DEFAULT_COLUMN_WIDTH;
1173
+ }
1174
+ function spreadsheetRowHeight(config, index) {
1175
+ const key = String(index);
1176
+ if (Object.hasOwn(config?.rowhidden ?? {}, key)) return 0;
1177
+ const height = config?.rowlen?.[key];
1178
+ return 'number' == typeof height && height > 0 ? height : DEFAULT_ROW_HEIGHT;
1179
+ }
1180
+ function readMarker(element) {
1181
+ if (!element) return;
1182
+ return {
1183
+ column: nonNegativeInteger(directChild(element, 'col')?.textContent),
1184
+ row: nonNegativeInteger(directChild(element, 'row')?.textContent),
1185
+ columnOffsetEmu: nonNegativeNumber(directChild(element, 'colOff')?.textContent),
1186
+ rowOffsetEmu: nonNegativeNumber(directChild(element, 'rowOff')?.textContent)
1187
+ };
1188
+ }
1189
+ function numberAttribute(element, name) {
1190
+ if (!element) return;
1191
+ const value = Number(attribute(element, name));
1192
+ return Number.isFinite(value) && value >= 0 ? value : void 0;
1193
+ }
1194
+ function nonNegativeInteger(value) {
1195
+ const parsed = Number(value);
1196
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : 0;
1197
+ }
1198
+ function nonNegativeNumber(value) {
1199
+ const parsed = Number(value);
1200
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
1201
+ }
1202
+ function markerPosition(index, offsetEmu, size) {
1203
+ let position = 0;
1204
+ for(let current = 0; current < index; current += 1)position += size(current);
1205
+ return position + emuToPixels(offsetEmu);
1206
+ }
1207
+ function emuToPixels(value) {
1208
+ return (value ?? 0) / XLSX_EMU_PER_PIXEL;
1209
+ }
1210
+ function axisMarker(position, size) {
1211
+ let remaining = Math.max(0, Number.isFinite(position) ? position : 0);
1212
+ let index = 0;
1213
+ while(index < 1048576){
1214
+ const currentSize = Math.max(0, size(index));
1215
+ if (currentSize > 0 && remaining < currentSize) break;
1216
+ if (currentSize > 0) remaining -= currentSize;
1217
+ index += 1;
1218
+ }
1219
+ return {
1220
+ index,
1221
+ offsetEmu: Math.round(remaining * XLSX_EMU_PER_PIXEL)
1222
+ };
1223
+ }
1224
+ function markerXml(kind, column, row) {
1225
+ return [
1226
+ `<xdr:${kind}>`,
1227
+ `<xdr:col>${column.index}</xdr:col><xdr:colOff>${column.offsetEmu}</xdr:colOff>`,
1228
+ `<xdr:row>${row.index}</xdr:row><xdr:rowOff>${row.offsetEmu}</xdr:rowOff>`,
1229
+ `</xdr:${kind}>`
1230
+ ].join('');
1231
+ }
1232
+ function roundPosition(value) {
1233
+ return Math.round(1000 * value) / 1000;
1234
+ }
1235
+ function normalizeWorkSpreadsheetDoughnutHoleSize(value) {
1236
+ const size = Number(value);
1237
+ if (!Number.isFinite(size)) return 50;
1238
+ return Math.min(90, Math.max(10, Math.round(size)));
1239
+ }
1240
+ function normalizeWorkSpreadsheetRadarStyle(value) {
1241
+ return 'standard' === value || 'filled' === value || 'marker' === value ? value : 'marker';
1242
+ }
1243
+ function normalizeWorkSpreadsheetScatterStyle(value) {
1244
+ return 'line' === value || 'lineMarker' === value || 'smooth' === value || 'smoothMarker' === value || 'marker' === value ? value : 'marker';
1245
+ }
1246
+ function normalizeWorkSpreadsheetBubbleScale(value) {
1247
+ const scale = Number(value);
1248
+ if (!Number.isFinite(scale)) return 100;
1249
+ return Math.min(300, Math.max(0, Math.round(scale)));
1250
+ }
1251
+ function normalizeWorkSpreadsheetBubbleSizeRepresents(value) {
1252
+ return 'width' === value || 'w' === value ? 'width' : 'area';
1253
+ }
1254
+ function workSpreadsheetChartUsesNumericXAxis(type) {
1255
+ return 'scatter' === type || 'bubble' === type;
1256
+ }
1257
+ function workSpreadsheetChartSupportsAxes(type) {
1258
+ return 'pie' !== type && 'doughnut' !== type;
1259
+ }
1260
+ function normalizeWorkSpreadsheetCombinationSeriesType(value) {
1261
+ return 'line' === value || 'area' === value || 'column' === value ? value : 'column';
1262
+ }
1263
+ function normalizeWorkSpreadsheetChartAxisGroup(value) {
1264
+ return 'secondary' === value ? 'secondary' : 'primary';
1265
+ }
1266
+ function workSpreadsheetCombinationSeriesTypeLabel(type) {
1267
+ if ('line' === type) return '折线图';
1268
+ if ('area' === type) return '面积图';
1269
+ return '柱形图';
1270
+ }
1271
+ function workSpreadsheetChartSupportsTrendlines(type) {
1272
+ return 'pie' !== type && 'doughnut' !== type && 'radar' !== type;
1273
+ }
1274
+ function workSpreadsheetChartSupportsErrorBars(type) {
1275
+ return 'pie' !== type && 'doughnut' !== type && 'radar' !== type;
1276
+ }
1277
+ function normalizeWorkSpreadsheetErrorBars(source, chartType) {
1278
+ const direction = workSpreadsheetChartUsesNumericXAxis(chartType) && 'x' === source.direction ? 'x' : 'y';
1279
+ const barType = 'plus' === source.barType || 'minus' === source.barType ? source.barType : 'both';
1280
+ const valueType = 'percentage' === source.valueType || 'standardDeviation' === source.valueType || 'standardError' === source.valueType || 'custom' === source.valueType || 'fixedValue' === source.valueType ? source.valueType : 'fixedValue';
1281
+ const fallbackValue = 'percentage' === valueType ? 5 : 1;
1282
+ const numericValue = Number(source.value);
1283
+ const value = Number.isFinite(numericValue) ? Math.max(0, numericValue) : fallbackValue;
1284
+ const plusValues = source.plusValues?.map(normalizedErrorBarAmount);
1285
+ const minusValues = source.minusValues?.map(normalizedErrorBarAmount);
1286
+ return {
1287
+ direction,
1288
+ barType,
1289
+ valueType,
1290
+ ...'fixedValue' === valueType || 'percentage' === valueType || 'standardDeviation' === valueType ? {
1291
+ value
1292
+ } : {},
1293
+ ...false === source.showEndCaps ? {
1294
+ showEndCaps: false
1295
+ } : {},
1296
+ ...'custom' === valueType && 'minus' !== barType && plusValues?.length ? {
1297
+ plusValues
1298
+ } : {},
1299
+ ...'custom' === valueType && 'minus' !== barType && source.plusReference?.trim() ? {
1300
+ plusReference: source.plusReference.trim().replace(/^=/, '')
1301
+ } : {},
1302
+ ...'custom' === valueType && 'plus' !== barType && minusValues?.length ? {
1303
+ minusValues
1304
+ } : {},
1305
+ ...'custom' === valueType && 'plus' !== barType && source.minusReference?.trim() ? {
1306
+ minusReference: source.minusReference.trim().replace(/^=/, '')
1307
+ } : {}
1308
+ };
1309
+ }
1310
+ function workSpreadsheetErrorBarValueTypeLabel(type) {
1311
+ if ('percentage' === type) return '百分比';
1312
+ if ('standardDeviation' === type) return '标准差';
1313
+ if ('standardError' === type) return '标准误差';
1314
+ if ('custom' === type) return '自定义';
1315
+ return '固定值';
1316
+ }
1317
+ function workSpreadsheetErrorBarTypeLabel(type) {
1318
+ if ('plus' === type) return '正向';
1319
+ if ('minus' === type) return '负向';
1320
+ return '双向';
1321
+ }
1322
+ function normalizedErrorBarAmount(value) {
1323
+ return Number.isFinite(value) ? Math.max(0, value) : 0;
1324
+ }
1325
+ function normalizeWorkSpreadsheetDataLabelPosition(value) {
1326
+ return 'center' === value || 'insideBase' === value || 'insideEnd' === value || 'outsideEnd' === value || 'left' === value || 'right' === value || 'above' === value || 'below' === value || 'bestFit' === value ? value : 'bestFit';
1327
+ }
1328
+ function normalizeWorkSpreadsheetDataLabels(source, chartType) {
1329
+ const position = source.position ? normalizeWorkSpreadsheetDataLabelPosition(source.position) : void 0;
1330
+ const separator = 'string' == typeof source.separator ? source.separator.slice(0, 64) : void 0;
1331
+ return {
1332
+ ...true === source.showValue ? {
1333
+ showValue: true
1334
+ } : {},
1335
+ ...true === source.showCategoryName ? {
1336
+ showCategoryName: true
1337
+ } : {},
1338
+ ...true === source.showSeriesName ? {
1339
+ showSeriesName: true
1340
+ } : {},
1341
+ ...true === source.showPercentage && ('pie' === chartType || 'doughnut' === chartType) ? {
1342
+ showPercentage: true
1343
+ } : {},
1344
+ ...true === source.showBubbleSize && 'bubble' === chartType ? {
1345
+ showBubbleSize: true
1346
+ } : {},
1347
+ ...void 0 !== separator ? {
1348
+ separator
1349
+ } : {},
1350
+ ...position ? {
1351
+ position
1352
+ } : {}
1353
+ };
1354
+ }
1355
+ function workSpreadsheetDataLabelPositionLabel(position) {
1356
+ if ('center' === position) return '居中';
1357
+ if ('insideBase' === position) return '内侧基部';
1358
+ if ('insideEnd' === position) return '内侧末端';
1359
+ if ('outsideEnd' === position) return '外侧末端';
1360
+ if ('left' === position) return '左侧';
1361
+ if ('right' === position) return '右侧';
1362
+ if ('above' === position) return '上方';
1363
+ if ('below' === position) return '下方';
1364
+ return '最佳匹配';
1060
1365
  }
1061
- function cloneCellMatrix(source) {
1062
- return (source ?? []).map((row)=>[
1063
- ...row
1064
- ]);
1366
+ function normalizeWorkSpreadsheetTrendlineType(value) {
1367
+ return 'exponential' === value || 'logarithmic' === value || 'polynomial' === value || 'power' === value || 'movingAverage' === value || 'linear' === value ? value : 'linear';
1065
1368
  }
1066
- function clearRange(data, bounds) {
1067
- if (!bounds) return;
1068
- for(let row = bounds.startRow; row <= bounds.endRow; row += 1)if (data[row]) for(let column = bounds.startColumn; column <= bounds.endColumn; column += 1)data[row][column] = null;
1369
+ function normalizeWorkSpreadsheetTrendline(trendline) {
1370
+ const type = normalizeWorkSpreadsheetTrendlineType(trendline.type);
1371
+ const name = trendline.name?.trim().slice(0, 255);
1372
+ const order = normalizedInteger(trendline.order, 2, 6, 2);
1373
+ const period = normalizedInteger(trendline.period, 2, 255, 2);
1374
+ const forward = normalizedNonNegativeNumber(trendline.forward);
1375
+ const backward = normalizedNonNegativeNumber(trendline.backward);
1376
+ const intercept = Number.isFinite(trendline.intercept) ? Number(trendline.intercept) : void 0;
1377
+ return {
1378
+ type,
1379
+ ...name ? {
1380
+ name
1381
+ } : {},
1382
+ ...'polynomial' === type ? {
1383
+ order
1384
+ } : {},
1385
+ ...'movingAverage' === type ? {
1386
+ period
1387
+ } : {},
1388
+ ...forward > 0 ? {
1389
+ forward
1390
+ } : {},
1391
+ ...backward > 0 ? {
1392
+ backward
1393
+ } : {},
1394
+ ...void 0 !== intercept ? {
1395
+ intercept
1396
+ } : {},
1397
+ ...true === trendline.displayEquation ? {
1398
+ displayEquation: true
1399
+ } : {},
1400
+ ...true === trendline.displayRSquared ? {
1401
+ displayRSquared: true
1402
+ } : {}
1403
+ };
1069
1404
  }
1070
- function writePivotOutput(data, bounds, output) {
1071
- const width = output[0]?.length ?? 0;
1072
- while(data.length <= bounds.endRow)data.push([]);
1073
- for(let row = 0; row < output.length; row += 1){
1074
- const target = data[bounds.startRow + row] ?? [];
1075
- data[bounds.startRow + row] = target;
1076
- while(target.length < bounds.startColumn + width)target.push(null);
1077
- for(let column = 0; column < width; column += 1)target[bounds.startColumn + column] = output[row][column] ?? null;
1078
- }
1405
+ function workSpreadsheetTrendlineTypeLabel(type) {
1406
+ if ('exponential' === type) return '指数';
1407
+ if ('logarithmic' === type) return '对数';
1408
+ if ('polynomial' === type) return '多项式';
1409
+ if ('power' === type) return '幂';
1410
+ if ('movingAverage' === type) return '移动平均';
1411
+ return '线性';
1079
1412
  }
1080
- function nextSheetName(content, base) {
1081
- const existing = new Set(content.sheets.map((sheet)=>sheet.name.toLocaleLowerCase()));
1082
- if (!existing.has(base.toLocaleLowerCase())) return base;
1083
- let index = 2;
1084
- while(existing.has(`${base} ${index}`.toLocaleLowerCase()))index += 1;
1085
- return `${base} ${index}`;
1413
+ function normalizedInteger(value, minimum, maximum, fallback) {
1414
+ const number = Number(value);
1415
+ if (!Number.isFinite(number)) return fallback;
1416
+ return Math.min(maximum, Math.max(minimum, Math.round(number)));
1086
1417
  }
1087
- function nextPivotName(content) {
1088
- const existing = new Set(content.sheets.flatMap((sheet)=>(sheet.pivotTables ?? []).map((pivot)=>pivot.name.toLocaleLowerCase())));
1089
- let index = 1;
1090
- while(existing.has(`pivottable${index}`))index += 1;
1091
- return `PivotTable${index}`;
1418
+ function normalizedNonNegativeNumber(value) {
1419
+ const number = Number(value);
1420
+ return Number.isFinite(number) ? Math.max(0, number) : 0;
1092
1421
  }
1093
- function invalid(code, message) {
1094
- return {
1095
- valid: false,
1096
- code,
1097
- message
1098
- };
1422
+ function workSpreadsheetChartTypeLabel(type) {
1423
+ if ('bar' === type) return '条形图';
1424
+ if ('line' === type) return '折线图';
1425
+ if ('pie' === type) return '饼图';
1426
+ if ('doughnut' === type) return '圆环图';
1427
+ if ('area' === type) return '面积图';
1428
+ if ('radar' === type) return '雷达图';
1429
+ if ('scatter' === type) return '散点图';
1430
+ if ('bubble' === type) return '气泡图';
1431
+ if ('combination' === type) return '组合图';
1432
+ return '柱形图';
1099
1433
  }
1100
1434
  const WORK_SPREADSHEET_CHART_COLORS = [
1101
1435
  '#4f6bed',
@@ -3121,142 +3455,35 @@ function horizontalBarChartSvg(chart, plot, layout, values) {
3121
3455
  value: point.rawValue
3122
3456
  })}`;
3123
3457
  }).join('');
3124
- const errorBars = layout.stacked ? '' : spreadsheetSeriesErrorBarsSvg(chart, seriesIndex, (_direction, pointIndex, value)=>[
3125
- valuePosition(value),
3126
- seriesY(pointIndex) + renderedHeight / 2
3127
- ]);
3128
- return `${seriesBars}${errorBars}`;
3129
- }).join('');
3130
- const trendlines = layout.stacked ? '' : chart.series.map((series, seriesIndex)=>spreadsheetSeriesTrendlinesSvg(series, seriesIndex, categoryTrendlineXValues(series), (category, value)=>[
3131
- valuePosition(value),
3132
- plot.y + (spreadsheetChartCategoryVisualIndex(category - 1, categoryCount, categoryAxis) + 0.5) / categoryCount * plot.height
3133
- ])).join('');
3134
- return `<g ${spreadsheetChartAxisScaleAttributes(axis, scale, chart.type, 'bottom')}>${grid}<line x1="${roundChartNumber(zero)}" y1="${plot.y}" x2="${roundChartNumber(zero)}" y2="${roundChartNumber(plot.y + plot.height)}" stroke="#aeb6c4" stroke-width="1.2"/></g><g ${spreadsheetChartAxisDisplayAttributes(categoryAxis, chart.type, 'left')}>${labels}</g>${bars}${trendlines}`;
3135
- }
3136
- function categoryLabelsSvg(chart, plot, axisY) {
3137
- const categoryCount = Math.max(1, chart.categories.length, ...chart.series.map((series)=>series.values.length));
3138
- const axis = chart.axes?.bottom;
3139
- const labels = Array.from({
3140
- length: categoryCount
3141
- }, (_, index)=>{
3142
- const visible = spreadsheetChartCategoryLabelVisible(index, categoryCount, axis);
3143
- const label = chart.categories[index] ?? String(index + 1);
3144
- const visualIndex = spreadsheetChartCategoryVisualIndex(index, categoryCount, axis);
3145
- const x = plot.x + (visualIndex + 0.5) / categoryCount * plot.width;
3146
- const labelLayout = spreadsheetChartAxisLabelLayout(axis, chart.type, 'bottom', plot, x, axisY);
3147
- return `${spreadsheetChartMajorTickSvg(axis, 'bottom', index, x, axisY)}${visible && labelLayout ? `<text data-axis-category-label="${index}" x="${roundChartNumber(labelLayout.x)}" y="${roundChartNumber(labelLayout.y)}" fill="#536078" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="10" text-anchor="${labelLayout.textAnchor}">${escapeChartXml(truncateChartText(label, 13))}</text>` : ''}`;
3148
- }).join('');
3149
- return `<g ${spreadsheetChartAxisDisplayAttributes(axis, chart.type, 'bottom')}>${labels}</g>`;
3150
- }
3151
- function emptyChartSvg(plot, message) {
3152
- return `<text x="${roundChartNumber(plot.x + plot.width / 2)}" y="${roundChartNumber(plot.y + plot.height / 2)}" fill="#8791a3" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="13" text-anchor="middle">${escapeChartXml(message)}</text>`;
3153
- }
3154
- const XLSX_EMU_PER_PIXEL = 9525;
3155
- const DEFAULT_COLUMN_WIDTH = 96;
3156
- const DEFAULT_ROW_HEIGHT = 24;
3157
- function readXlsxDrawingAnchor(anchor) {
3158
- const extent = directChild(anchor, 'ext');
3159
- const position = directChild(anchor, 'pos');
3160
- return {
3161
- from: readMarker(directChild(anchor, 'from')),
3162
- to: readMarker(directChild(anchor, 'to')),
3163
- leftEmu: numberAttribute(position, 'x'),
3164
- topEmu: numberAttribute(position, 'y'),
3165
- widthEmu: numberAttribute(extent, 'cx'),
3166
- heightEmu: numberAttribute(extent, 'cy')
3167
- };
3168
- }
3169
- function xlsxDrawingAnchorToBounds(anchor, config) {
3170
- const left = anchor.from ? markerPosition(anchor.from.column, anchor.from.columnOffsetEmu, (index)=>spreadsheetColumnWidth(config, index)) : emuToPixels(anchor.leftEmu);
3171
- const top = anchor.from ? markerPosition(anchor.from.row, anchor.from.rowOffsetEmu, (index)=>spreadsheetRowHeight(config, index)) : emuToPixels(anchor.topEmu);
3172
- const right = anchor.to ? markerPosition(anchor.to.column, anchor.to.columnOffsetEmu, (index)=>spreadsheetColumnWidth(config, index)) : left + emuToPixels(anchor.widthEmu);
3173
- const bottom = anchor.to ? markerPosition(anchor.to.row, anchor.to.rowOffsetEmu, (index)=>spreadsheetRowHeight(config, index)) : top + emuToPixels(anchor.heightEmu);
3174
- if (![
3175
- left,
3176
- top,
3177
- right,
3178
- bottom
3179
- ].every(Number.isFinite)) return null;
3180
- return {
3181
- left: roundPosition(Math.max(0, left)),
3182
- top: roundPosition(Math.max(0, top)),
3183
- width: roundPosition(Math.max(1, right - left)),
3184
- height: roundPosition(Math.max(1, bottom - top))
3185
- };
3186
- }
3187
- function xlsxTwoCellAnchorMarkers(bounds, sheet) {
3188
- const fromColumn = axisMarker(bounds.left, (index)=>spreadsheetColumnWidth(sheet.config, index));
3189
- const toColumn = axisMarker(bounds.left + bounds.width, (index)=>spreadsheetColumnWidth(sheet.config, index));
3190
- const fromRow = axisMarker(bounds.top, (index)=>spreadsheetRowHeight(sheet.config, index));
3191
- const toRow = axisMarker(bounds.top + bounds.height, (index)=>spreadsheetRowHeight(sheet.config, index));
3192
- return `${markerXml('from', fromColumn, fromRow)}${markerXml('to', toColumn, toRow)}`;
3193
- }
3194
- function spreadsheetColumnWidth(config, index) {
3195
- const key = String(index);
3196
- if (Object.hasOwn(config?.colhidden ?? {}, key)) return 0;
3197
- const width = config?.columnlen?.[key];
3198
- return 'number' == typeof width && width > 0 ? width : DEFAULT_COLUMN_WIDTH;
3199
- }
3200
- function spreadsheetRowHeight(config, index) {
3201
- const key = String(index);
3202
- if (Object.hasOwn(config?.rowhidden ?? {}, key)) return 0;
3203
- const height = config?.rowlen?.[key];
3204
- return 'number' == typeof height && height > 0 ? height : DEFAULT_ROW_HEIGHT;
3205
- }
3206
- function readMarker(element) {
3207
- if (!element) return;
3208
- return {
3209
- column: nonNegativeInteger(directChild(element, 'col')?.textContent),
3210
- row: nonNegativeInteger(directChild(element, 'row')?.textContent),
3211
- columnOffsetEmu: nonNegativeNumber(directChild(element, 'colOff')?.textContent),
3212
- rowOffsetEmu: nonNegativeNumber(directChild(element, 'rowOff')?.textContent)
3213
- };
3214
- }
3215
- function numberAttribute(element, name) {
3216
- if (!element) return;
3217
- const value = Number(attribute(element, name));
3218
- return Number.isFinite(value) && value >= 0 ? value : void 0;
3219
- }
3220
- function nonNegativeInteger(value) {
3221
- const parsed = Number(value);
3222
- return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : 0;
3223
- }
3224
- function nonNegativeNumber(value) {
3225
- const parsed = Number(value);
3226
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
3227
- }
3228
- function markerPosition(index, offsetEmu, size) {
3229
- let position = 0;
3230
- for(let current = 0; current < index; current += 1)position += size(current);
3231
- return position + emuToPixels(offsetEmu);
3232
- }
3233
- function emuToPixels(value) {
3234
- return (value ?? 0) / XLSX_EMU_PER_PIXEL;
3235
- }
3236
- function axisMarker(position, size) {
3237
- let remaining = Math.max(0, Number.isFinite(position) ? position : 0);
3238
- let index = 0;
3239
- while(index < 1048576){
3240
- const currentSize = Math.max(0, size(index));
3241
- if (currentSize > 0 && remaining < currentSize) break;
3242
- if (currentSize > 0) remaining -= currentSize;
3243
- index += 1;
3244
- }
3245
- return {
3246
- index,
3247
- offsetEmu: Math.round(remaining * XLSX_EMU_PER_PIXEL)
3248
- };
3458
+ const errorBars = layout.stacked ? '' : spreadsheetSeriesErrorBarsSvg(chart, seriesIndex, (_direction, pointIndex, value)=>[
3459
+ valuePosition(value),
3460
+ seriesY(pointIndex) + renderedHeight / 2
3461
+ ]);
3462
+ return `${seriesBars}${errorBars}`;
3463
+ }).join('');
3464
+ const trendlines = layout.stacked ? '' : chart.series.map((series, seriesIndex)=>spreadsheetSeriesTrendlinesSvg(series, seriesIndex, categoryTrendlineXValues(series), (category, value)=>[
3465
+ valuePosition(value),
3466
+ plot.y + (spreadsheetChartCategoryVisualIndex(category - 1, categoryCount, categoryAxis) + 0.5) / categoryCount * plot.height
3467
+ ])).join('');
3468
+ return `<g ${spreadsheetChartAxisScaleAttributes(axis, scale, chart.type, 'bottom')}>${grid}<line x1="${roundChartNumber(zero)}" y1="${plot.y}" x2="${roundChartNumber(zero)}" y2="${roundChartNumber(plot.y + plot.height)}" stroke="#aeb6c4" stroke-width="1.2"/></g><g ${spreadsheetChartAxisDisplayAttributes(categoryAxis, chart.type, 'left')}>${labels}</g>${bars}${trendlines}`;
3249
3469
  }
3250
- function markerXml(kind, column, row) {
3251
- return [
3252
- `<xdr:${kind}>`,
3253
- `<xdr:col>${column.index}</xdr:col><xdr:colOff>${column.offsetEmu}</xdr:colOff>`,
3254
- `<xdr:row>${row.index}</xdr:row><xdr:rowOff>${row.offsetEmu}</xdr:rowOff>`,
3255
- `</xdr:${kind}>`
3256
- ].join('');
3470
+ function categoryLabelsSvg(chart, plot, axisY) {
3471
+ const categoryCount = Math.max(1, chart.categories.length, ...chart.series.map((series)=>series.values.length));
3472
+ const axis = chart.axes?.bottom;
3473
+ const labels = Array.from({
3474
+ length: categoryCount
3475
+ }, (_, index)=>{
3476
+ const visible = spreadsheetChartCategoryLabelVisible(index, categoryCount, axis);
3477
+ const label = chart.categories[index] ?? String(index + 1);
3478
+ const visualIndex = spreadsheetChartCategoryVisualIndex(index, categoryCount, axis);
3479
+ const x = plot.x + (visualIndex + 0.5) / categoryCount * plot.width;
3480
+ const labelLayout = spreadsheetChartAxisLabelLayout(axis, chart.type, 'bottom', plot, x, axisY);
3481
+ return `${spreadsheetChartMajorTickSvg(axis, 'bottom', index, x, axisY)}${visible && labelLayout ? `<text data-axis-category-label="${index}" x="${roundChartNumber(labelLayout.x)}" y="${roundChartNumber(labelLayout.y)}" fill="#536078" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="10" text-anchor="${labelLayout.textAnchor}">${escapeChartXml(truncateChartText(label, 13))}</text>` : ''}`;
3482
+ }).join('');
3483
+ return `<g ${spreadsheetChartAxisDisplayAttributes(axis, chart.type, 'bottom')}>${labels}</g>`;
3257
3484
  }
3258
- function roundPosition(value) {
3259
- return Math.round(1000 * value) / 1000;
3485
+ function emptyChartSvg(plot, message) {
3486
+ return `<text x="${roundChartNumber(plot.x + plot.width / 2)}" y="${roundChartNumber(plot.y + plot.height / 2)}" fill="#8791a3" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="13" text-anchor="middle">${escapeChartXml(message)}</text>`;
3260
3487
  }
3261
3488
  const CHART_PREVIEW_IMAGE_PREFIX = 'work-chart-preview-';
3262
3489
  function spreadsheetChartCount(content) {
@@ -3640,224 +3867,4 @@ function isNumericValue(value) {
3640
3867
  function finiteDimension(value, fallback, minimum = 0) {
3641
3868
  return Number.isFinite(value) ? Math.max(minimum, value) : fallback;
3642
3869
  }
3643
- const DEFAULT_SPREADSHEET_CALCULATION_SETTINGS = {
3644
- mode: 'automatic',
3645
- fullCalculationOnLoad: false,
3646
- forceFullCalculation: false,
3647
- iterativeCalculation: false,
3648
- maximumIterations: 100,
3649
- maximumChange: 0.001,
3650
- fullPrecision: true
3651
- };
3652
- const VOLATILE_FUNCTIONS = new Set([
3653
- 'CELL',
3654
- 'INFO',
3655
- 'INDIRECT',
3656
- 'NOW',
3657
- 'OFFSET',
3658
- 'RAND',
3659
- 'RANDBETWEEN',
3660
- 'TODAY'
3661
- ]);
3662
- const FUTURE_FUNCTIONS = new Set([
3663
- 'ARRAYTOTEXT',
3664
- 'BYCOL',
3665
- 'BYROW',
3666
- 'CHOOSECOLS',
3667
- 'CHOOSEROWS',
3668
- 'DROP',
3669
- 'EXPAND',
3670
- 'FIELDVALUE',
3671
- 'FILTER',
3672
- 'HSTACK',
3673
- 'IMAGE',
3674
- 'ISOMITTED',
3675
- 'LAMBDA',
3676
- 'LET',
3677
- 'MAKEARRAY',
3678
- 'MAP',
3679
- 'RANDARRAY',
3680
- 'REDUCE',
3681
- 'SCAN',
3682
- 'SEQUENCE',
3683
- 'SORT',
3684
- 'SORTBY',
3685
- 'STOCKHISTORY',
3686
- 'TAKE',
3687
- 'TEXTAFTER',
3688
- 'TEXTBEFORE',
3689
- 'TEXTSPLIT',
3690
- 'TOCOL',
3691
- 'TOROW',
3692
- 'UNIQUE',
3693
- 'VALUETOTEXT',
3694
- 'VSTACK',
3695
- 'WRAPCOLS',
3696
- 'WRAPROWS',
3697
- 'XLOOKUP',
3698
- 'XMATCH'
3699
- ]);
3700
- const FUTURE_FUNCTION_PREFIXES = new Map([
3701
- [
3702
- 'FILTER',
3703
- '_xlfn._xlws.'
3704
- ],
3705
- [
3706
- 'SORT',
3707
- '_xlfn._xlws.'
3708
- ],
3709
- [
3710
- 'SORTBY',
3711
- '_xlfn._xlws.'
3712
- ],
3713
- [
3714
- 'UNIQUE',
3715
- '_xlfn.'
3716
- ],
3717
- ...Array.from(FUTURE_FUNCTIONS).filter((name)=>![
3718
- 'FILTER',
3719
- 'SORT',
3720
- 'SORTBY',
3721
- 'UNIQUE'
3722
- ].includes(name)).map((name)=>[
3723
- name,
3724
- '_xlfn.'
3725
- ])
3726
- ]);
3727
- function effectiveSpreadsheetCalculationSettings(settings) {
3728
- if (!settings) return {
3729
- ...DEFAULT_SPREADSHEET_CALCULATION_SETTINGS
3730
- };
3731
- return {
3732
- mode: settings.mode,
3733
- fullCalculationOnLoad: Boolean(settings.fullCalculationOnLoad),
3734
- forceFullCalculation: Boolean(settings.forceFullCalculation),
3735
- iterativeCalculation: Boolean(settings.iterativeCalculation),
3736
- maximumIterations: clampedInteger(settings.maximumIterations, 1, 10000, 100),
3737
- maximumChange: positiveNumber(settings.maximumChange, 0.001),
3738
- fullPrecision: false !== settings.fullPrecision
3739
- };
3740
- }
3741
- function editableSpreadsheetFormula(source) {
3742
- return transformFormulaOutsideStrings(source, (segment)=>segment.replace(/_xlfn\._xlws\.(?=[A-Z][A-Z0-9_.]*\s*\()/gi, '').replace(/_xlfn\.(?=[A-Z][A-Z0-9_.]*\s*\()/gi, '').replace(/_xlws\.(?=[A-Z][A-Z0-9_.]*\s*\()/gi, ''));
3743
- }
3744
- function spreadsheetFormulaForXlsx(currentFormula, sourceFormula) {
3745
- const current = currentFormula.trim().replace(/^=/, '');
3746
- const source = sourceFormula?.trim().replace(/^=/, '');
3747
- if (source && comparableFormula(source) === comparableFormula(current)) return source;
3748
- return futureFunctionPrefixes(current, source);
3749
- }
3750
- function spreadsheetFormulaRangeForCell(sheet, row, column) {
3751
- const address = spreadsheetCellAddress(row, column);
3752
- return (sheet.formulaMetadata?.ranges ?? []).find((range)=>range.anchor.toUpperCase().replaceAll('$', '') === address);
3753
- }
3754
- function spreadsheetFormulaRangesForSelection(sheet, selection) {
3755
- return (sheet.formulaMetadata?.ranges ?? []).filter((range)=>work_spreadsheet_formulas_rangesOverlap(selection, parseSpreadsheetFormulaRange(range.reference)));
3756
- }
3757
- function spreadsheetFormulaRangeConflict(sheet, range) {
3758
- const bounds = parseSpreadsheetFormulaRange(range.reference);
3759
- if (!bounds) return '引用范围无效';
3760
- const anchor = parseSpreadsheetCellAddress(range.anchor);
3761
- if (!anchor || !work_spreadsheet_formulas_containsCell(bounds, anchor.row, anchor.column)) return '锚点不在引用范围内';
3762
- const anchorCell = sheet.data?.[anchor.row]?.[anchor.column];
3763
- if (!anchorCell) return '锚点单元格不存在';
3764
- const overlaps = (sheet.formulaMetadata?.ranges ?? []).filter((candidate)=>candidate !== range && work_spreadsheet_formulas_rangesOverlap(bounds, parseSpreadsheetFormulaRange(candidate.reference)));
3765
- if (overlaps.length) return '与其他公式范围重叠';
3766
- for (const [row, cells] of sparseArrayEntries(sheet.data))if (!(row < bounds.startRow) && !(row > bounds.endRow)) {
3767
- for (const [column, cell] of sparseArrayEntries(cells))if (!(column < bounds.startColumn) && !(column > bounds.endColumn)) {
3768
- if (row !== anchor.row || column !== anchor.column) {
3769
- if (cell?.f) return `${spreadsheetCellAddress(row, column)} 包含独立公式`;
3770
- }
3771
- }
3772
- }
3773
- if (Object.values(sheet.config?.merge ?? {}).some((merge)=>work_spreadsheet_formulas_rangesOverlap(bounds, {
3774
- startRow: merge.r,
3775
- endRow: merge.r + merge.rs - 1,
3776
- startColumn: merge.c,
3777
- endColumn: merge.c + merge.cs - 1
3778
- }))) return '范围内包含合并单元格';
3779
- if ('data-table' !== range.type && !anchorCell.f) return '锚点公式已被删除';
3780
- return null;
3781
- }
3782
- function spreadsheetFormulaFunctions(formula) {
3783
- const withoutStrings = formula.replace(/"(?:[^"]|"")*"/g, '""');
3784
- const functions = new Set();
3785
- for (const match of withoutStrings.matchAll(/(?:_xlfn\.)?(?:_xlws\.)?([A-Z][A-Z0-9_.]*)\s*\(/gi))if (match[1]) functions.add(match[1].toUpperCase());
3786
- return Array.from(functions);
3787
- }
3788
- function formulaHasExternalReference(formula) {
3789
- return /\[[^\]]+\][^!]*!/i.test(formula.replace(/"(?:[^"]|"")*"/g, '""'));
3790
- }
3791
- function formulaHasStructuredReference(formula) {
3792
- const withoutExternalReferences = formula.replace(/"(?:[^"]|"")*"/g, '""').replace(/\[[^\]]+\][^!]*!/gi, '');
3793
- return /\[(?:@|#)[^\]]+\]/i.test(withoutExternalReferences) || /\b[A-Z_\\][A-Z0-9_.]*\s*\[[^\]]+\]/i.test(withoutExternalReferences);
3794
- }
3795
- function volatileSpreadsheetFormulaFunctions(formula) {
3796
- return spreadsheetFormulaFunctions(formula).filter((name)=>VOLATILE_FUNCTIONS.has(name));
3797
- }
3798
- function spreadsheetCellAddress(row, column) {
3799
- let value = column + 1;
3800
- let label = '';
3801
- while(value > 0){
3802
- value -= 1;
3803
- label = String.fromCharCode(65 + value % 26) + label;
3804
- value = Math.floor(value / 26);
3805
- }
3806
- return `${label}${row + 1}`;
3807
- }
3808
- function parseSpreadsheetFormulaRange(reference) {
3809
- const normalized = reference.trim().replaceAll('$', '').split('!').at(-1) ?? '';
3810
- const [startText, endText = startText] = normalized.split(':');
3811
- const start = parseSpreadsheetCellAddress(startText);
3812
- const end = parseSpreadsheetCellAddress(endText);
3813
- if (!start || !end) return null;
3814
- return {
3815
- startRow: Math.min(start.row, end.row),
3816
- endRow: Math.max(start.row, end.row),
3817
- startColumn: Math.min(start.column, end.column),
3818
- endColumn: Math.max(start.column, end.column)
3819
- };
3820
- }
3821
- function parseSpreadsheetCellAddress(address) {
3822
- const match = /^([A-Z]{1,3})([1-9]\d*)$/i.exec(address.trim().replaceAll('$', ''));
3823
- if (!match) return null;
3824
- let column = 0;
3825
- for (const character of match[1].toUpperCase())column = 26 * column + character.charCodeAt(0) - 64;
3826
- const row = Number(match[2]) - 1;
3827
- if (row > 1048575 || column > 16384) return null;
3828
- return {
3829
- row,
3830
- column: column - 1
3831
- };
3832
- }
3833
- function work_spreadsheet_formulas_containsCell(bounds, row, column) {
3834
- return row >= bounds.startRow && row <= bounds.endRow && column >= bounds.startColumn && column <= bounds.endColumn;
3835
- }
3836
- function work_spreadsheet_formulas_rangesOverlap(first, second) {
3837
- if (!second) return false;
3838
- return first.startRow <= second.endRow && first.endRow >= second.startRow && first.startColumn <= second.endColumn && first.endColumn >= second.startColumn;
3839
- }
3840
- function comparableFormula(formula) {
3841
- return editableSpreadsheetFormula(formula).trim();
3842
- }
3843
- function futureFunctionPrefixes(formula, sourceFormula) {
3844
- const sourcePrefixes = new Map();
3845
- const sourceFunctions = (sourceFormula ?? '').replace(/"(?:[^"]|"")*"/g, '""');
3846
- for (const match of sourceFunctions.matchAll(/(_xlfn\.(?:_xlws\.)?)([A-Z][A-Z0-9_.]*)\s*(?=\()/gi))if (match[1] && match[2]) sourcePrefixes.set(match[2].toUpperCase(), match[1]);
3847
- return transformFormulaOutsideStrings(formula, (segment)=>segment.replace(/(?<![A-Z0-9_.])([A-Z][A-Z0-9_.]*)\s*(?=\()/gi, (match, functionName)=>{
3848
- const normalized = functionName.toUpperCase();
3849
- const prefix = sourcePrefixes.get(normalized) ?? FUTURE_FUNCTION_PREFIXES.get(normalized);
3850
- return prefix ? `${prefix}${match}` : match;
3851
- }));
3852
- }
3853
- function clampedInteger(value, minimum, maximum, fallback) {
3854
- if (!Number.isFinite(value)) return fallback;
3855
- return Math.min(maximum, Math.max(minimum, Math.round(value)));
3856
- }
3857
- function positiveNumber(value, fallback) {
3858
- return Number.isFinite(value) && value > 0 ? value : fallback;
3859
- }
3860
- function transformFormulaOutsideStrings(formula, transform) {
3861
- return formula.split(/("(?:[^"]|"")*")/).map((segment, index)=>index % 2 ? segment : transform(segment)).join('');
3862
- }
3863
- export { cloneSparseMatrix, createSpreadsheetChartFromSelection, createSpreadsheetPivotFromSelection, defaultPivotValueCaption, defaultWorkSpreadsheetChartSeriesStyle, deleteSpreadsheetPivotTable, editableSpreadsheetFormula, effectiveSpreadsheetCalculationSettings, errorBarSourceValues, fitSpreadsheetTrendline, formatSpreadsheetCellRanges, formatSpreadsheetChartAxisNumber, formulaHasExternalReference, formulaHasStructuredReference, isValidSpreadsheetDefinedName, normalizeSpreadsheetPrintArea, normalizeSpreadsheetPrintTitleColumns, normalizeSpreadsheetPrintTitleRows, normalizeWorkSpreadsheetBubbleScale, normalizeWorkSpreadsheetBubbleSizeRepresents, normalizeWorkSpreadsheetChartAxes, normalizeWorkSpreadsheetChartAxisGroup, normalizeWorkSpreadsheetChartColor, normalizeWorkSpreadsheetChartGapWidth, normalizeWorkSpreadsheetChartGrouping, normalizeWorkSpreadsheetChartLayout, normalizeWorkSpreadsheetChartLegendOverlay, normalizeWorkSpreadsheetChartLegendPosition, normalizeWorkSpreadsheetChartOverlap, normalizeWorkSpreadsheetChartSeriesStyle, normalizeWorkSpreadsheetChartSmoothLines, normalizeWorkSpreadsheetCombinationSeriesType, normalizeWorkSpreadsheetDataLabelPosition, normalizeWorkSpreadsheetDataLabels, normalizeWorkSpreadsheetDoughnutHoleSize, normalizeWorkSpreadsheetErrorBars, normalizeWorkSpreadsheetRadarStyle, normalizeWorkSpreadsheetScatterStyle, normalizeWorkSpreadsheetTrendline, normalizeWorkSpreadsheetTrendlineType, parseSpreadsheetCellRanges, parseSpreadsheetChartReference, parseSpreadsheetPrintTitles, qualifySpreadsheetRange, readXlsxDrawingAnchor, reconcileSpreadsheetChartPreviews, reconcileSpreadsheetPivots, refreshSpreadsheetPivotTables, resolveSpreadsheetChart, roundChartNumber, sparseArrayEntries, sparseArrayIndexes, sparseMatrixColumnCount, spreadsheetCellAddress, spreadsheetChartAxisGridlinesVisible, spreadsheetChartAxisLabelLayout, spreadsheetChartAxisScale, spreadsheetChartAxisValueRatio, spreadsheetChartBarGeometry, spreadsheetChartCategoryLabelVisible, spreadsheetChartCategoryVisualIndex, spreadsheetChartCount, spreadsheetChartSeriesFillStyle, spreadsheetChartSeriesLayout, spreadsheetChartSeriesLegendColor, spreadsheetChartSeriesLineStyle, spreadsheetChartSeriesStyleContext, spreadsheetErrorBarAmounts, spreadsheetFormulaForXlsx, spreadsheetFormulaFunctions, spreadsheetFormulaRangeConflict, spreadsheetFormulaRangeForCell, spreadsheetFormulaRangesForSelection, spreadsheetPivotAggregationLabel, spreadsheetPivotCount, spreadsheetPivotFields, spreadsheetPivotFilterItems, spreadsheetPivotFilterValueKey, spreadsheetPivotIntersects, spreadsheetPivotOutputContains, spreadsheetPivotValidation, spreadsheetSheetsWithChartPreviews, stripSpreadsheetSheetQualifier, volatileSpreadsheetFormulaFunctions, workSpreadsheetChartAxisDefaultLabelPosition, workSpreadsheetChartAxisIsCategoryAxis, workSpreadsheetChartAxisIsValueAxis, workSpreadsheetChartAxisLabelPositionLabel, workSpreadsheetChartAxisPositionLabel, workSpreadsheetChartAxisShowsMajorGridlinesByDefault, workSpreadsheetChartAxisTickMarkLabel, workSpreadsheetChartGroupingIsStacked, workSpreadsheetChartGroupingLabel, workSpreadsheetChartLegendPositionLabel, workSpreadsheetChartSupportsAxes, workSpreadsheetChartSupportsBarSpacing, workSpreadsheetChartSupportsErrorBars, workSpreadsheetChartSupportsGrouping, workSpreadsheetChartSupportsSeriesAnalysis, workSpreadsheetChartSupportsSmoothLines, workSpreadsheetChartSupportsTrendlines, workSpreadsheetChartTypeLabel, workSpreadsheetChartUsesNumericXAxis, workSpreadsheetCombinationSeriesTypeLabel, workSpreadsheetDataLabelPositionLabel, workSpreadsheetErrorBarTypeLabel, workSpreadsheetErrorBarValueTypeLabel, workSpreadsheetTrendlineTypeLabel, xlsxDrawingAnchorToBounds, xlsxTwoCellAnchorMarkers };
3870
+ export { cloneSparseMatrix, createSpreadsheetChartFromSelection, createSpreadsheetPivotFromSelection, defaultPivotValueCaption, defaultWorkSpreadsheetChartSeriesStyle, deleteSpreadsheetPivotTable, editableSpreadsheetFormula, effectiveSpreadsheetCalculationSettings, errorBarSourceValues, fitSpreadsheetTrendline, formatSpreadsheetCellRanges, formatSpreadsheetChartAxisNumber, formulaHasExternalReference, formulaHasStructuredReference, isValidSpreadsheetDefinedName, normalizeSpreadsheetPrintArea, normalizeSpreadsheetPrintTitleColumns, normalizeSpreadsheetPrintTitleRows, normalizeWorkSpreadsheetBubbleScale, normalizeWorkSpreadsheetBubbleSizeRepresents, normalizeWorkSpreadsheetChartAxes, normalizeWorkSpreadsheetChartAxisGroup, normalizeWorkSpreadsheetChartColor, normalizeWorkSpreadsheetChartGapWidth, normalizeWorkSpreadsheetChartGrouping, normalizeWorkSpreadsheetChartLayout, normalizeWorkSpreadsheetChartLegendOverlay, normalizeWorkSpreadsheetChartLegendPosition, normalizeWorkSpreadsheetChartOverlap, normalizeWorkSpreadsheetChartSeriesStyle, normalizeWorkSpreadsheetChartSmoothLines, normalizeWorkSpreadsheetCombinationSeriesType, normalizeWorkSpreadsheetDataLabelPosition, normalizeWorkSpreadsheetDataLabels, normalizeWorkSpreadsheetDoughnutHoleSize, normalizeWorkSpreadsheetErrorBars, normalizeWorkSpreadsheetRadarStyle, normalizeWorkSpreadsheetScatterStyle, normalizeWorkSpreadsheetTrendline, normalizeWorkSpreadsheetTrendlineType, parseSpreadsheetCellRanges, parseSpreadsheetChartReference, parseSpreadsheetPrintTitles, qualifySpreadsheetRange, readXlsxDrawingAnchor, reconcileSpreadsheetChartPreviews, reconcileSpreadsheetPivots, refreshSpreadsheetPivotTables, resolveSpreadsheetChart, roundChartNumber, sparseArrayEntries, sparseArrayIndexes, sparseMatrixColumnCount, spreadsheetCellAddress, spreadsheetChartAxisGridlinesVisible, spreadsheetChartAxisLabelLayout, spreadsheetChartAxisScale, spreadsheetChartAxisValueRatio, spreadsheetChartBarGeometry, spreadsheetChartCategoryLabelVisible, spreadsheetChartCategoryVisualIndex, spreadsheetChartCount, spreadsheetChartSeriesFillStyle, spreadsheetChartSeriesLayout, spreadsheetChartSeriesLegendColor, spreadsheetChartSeriesLineStyle, spreadsheetChartSeriesStyleContext, spreadsheetErrorBarAmounts, spreadsheetFormulaForXlsx, spreadsheetFormulaFunctions, spreadsheetFormulaRangeConflict, spreadsheetFormulaRangeForCell, spreadsheetFormulaRangesForSelection, spreadsheetGridSize, spreadsheetPivotAggregationLabel, spreadsheetPivotCount, spreadsheetPivotFields, spreadsheetPivotFilterItems, spreadsheetPivotFilterValueKey, spreadsheetPivotIntersects, spreadsheetPivotOutputContains, spreadsheetPivotValidation, spreadsheetSheetsWithChartPreviews, stripSpreadsheetSheetQualifier, volatileSpreadsheetFormulaFunctions, workSpreadsheetChartAxisDefaultLabelPosition, workSpreadsheetChartAxisIsCategoryAxis, workSpreadsheetChartAxisIsValueAxis, workSpreadsheetChartAxisLabelPositionLabel, workSpreadsheetChartAxisPositionLabel, workSpreadsheetChartAxisShowsMajorGridlinesByDefault, workSpreadsheetChartAxisTickMarkLabel, workSpreadsheetChartGroupingIsStacked, workSpreadsheetChartGroupingLabel, workSpreadsheetChartLegendPositionLabel, workSpreadsheetChartSupportsAxes, workSpreadsheetChartSupportsBarSpacing, workSpreadsheetChartSupportsErrorBars, workSpreadsheetChartSupportsGrouping, workSpreadsheetChartSupportsSeriesAnalysis, workSpreadsheetChartSupportsSmoothLines, workSpreadsheetChartSupportsTrendlines, workSpreadsheetChartTypeLabel, workSpreadsheetChartUsesNumericXAxis, workSpreadsheetCombinationSeriesTypeLabel, workSpreadsheetDataLabelPositionLabel, workSpreadsheetErrorBarTypeLabel, workSpreadsheetErrorBarValueTypeLabel, workSpreadsheetTrendlineTypeLabel, xlsxDrawingAnchorToBounds, xlsxTwoCellAnchorMarkers };