@a3s-lab/office 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (463) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +470 -0
  3. package/THIRD_PARTY_NOTICES.md +33 -0
  4. package/dist/0~1403.js +245 -0
  5. package/dist/0~152.js +94 -0
  6. package/dist/0~3539.js +1856 -0
  7. package/dist/0~3557.js +179 -0
  8. package/dist/0~3635.js +302 -0
  9. package/dist/0~4595.js +1162 -0
  10. package/dist/0~5050.js +308 -0
  11. package/dist/0~6552.js +787 -0
  12. package/dist/0~7048.js +2110 -0
  13. package/dist/0~7614.js +1037 -0
  14. package/dist/0~8155.js +299 -0
  15. package/dist/0~9445.js +283 -0
  16. package/dist/0~9512.js +743 -0
  17. package/dist/0~document-editor.js +10967 -0
  18. package/dist/0~markdown-editor.js +2260 -0
  19. package/dist/0~pdf-viewer.js +2156 -0
  20. package/dist/0~presentation-editor.js +8359 -0
  21. package/dist/0~rslib-runtime.js +27 -0
  22. package/dist/0~spreadsheet-editor.js +10388 -0
  23. package/dist/0~use-office-draft.js +46 -0
  24. package/dist/0~work-document-extensions.js +2470 -0
  25. package/dist/0~work-document-links.js +14 -0
  26. package/dist/0~work-document-model-codec.js +42 -0
  27. package/dist/0~work-document-page-color.js +11 -0
  28. package/dist/0~work-document-paragraph-formatting.js +425 -0
  29. package/dist/0~work-docx-bibliography.js +347 -0
  30. package/dist/0~work-docx-export.js +1151 -0
  31. package/dist/0~work-docx-import.js +3116 -0
  32. package/dist/0~work-docx-page-color.js +42 -0
  33. package/dist/0~work-office-diagnostics.js +1377 -0
  34. package/dist/0~work-pptx-export.js +1009 -0
  35. package/dist/0~work-pptx-import.js +1138 -0
  36. package/dist/0~work-presentation-charts.js +442 -0
  37. package/dist/0~work-presentation-transition.js +38 -0
  38. package/dist/0~work-presentation-visual-scale.js +38 -0
  39. package/dist/0~work-spreadsheet-formula-support.js +9 -0
  40. package/dist/1134.js +445 -0
  41. package/dist/4104.js +5786 -0
  42. package/dist/432.js +211 -0
  43. package/dist/4704.js +1364 -0
  44. package/dist/6489.js +182 -0
  45. package/dist/8715.js +3959 -0
  46. package/dist/8728.js +723 -0
  47. package/dist/9356.js +10 -0
  48. package/dist/core.d.ts +15 -0
  49. package/dist/core.js +4 -0
  50. package/dist/index.d.ts +2 -0
  51. package/dist/index.js +5 -0
  52. package/dist/internal/design-system/primitives/button/button.d.ts +9 -0
  53. package/dist/internal/design-system/primitives/button/icon-button.d.ts +7 -0
  54. package/dist/internal/design-system/primitives/dialog/dialog.d.ts +12 -0
  55. package/dist/internal/design-system/primitives/feedback/collection-state.d.ts +9 -0
  56. package/dist/internal/design-system/primitives/feedback/inline-notice.d.ts +10 -0
  57. package/dist/internal/design-system/primitives/feedback/state-view.d.ts +13 -0
  58. package/dist/internal/design-system/primitives/form/field.d.ts +19 -0
  59. package/dist/internal/design-system/primitives/index.d.ts +14 -0
  60. package/dist/internal/design-system/primitives/layout/split-handle.d.ts +17 -0
  61. package/dist/internal/design-system/primitives/navigation/segmented-control.d.ts +19 -0
  62. package/dist/internal/design-system/primitives/navigation/tab-navigation.d.ts +13 -0
  63. package/dist/internal/design-system/primitives/navigation/tabs.d.ts +21 -0
  64. package/dist/internal/design-system/primitives/overlay/dialog-focus-scope.d.ts +17 -0
  65. package/dist/internal/design-system/primitives/overlay/popover.d.ts +34 -0
  66. package/dist/internal/design-system/primitives/overlay/portal-root.d.ts +1 -0
  67. package/dist/internal/design-system/primitives/status/status-badge.d.ts +6 -0
  68. package/dist/internal/features/work/components/work-document-pages.d.ts +4 -0
  69. package/dist/internal/features/work/components/work-editor-agent-menus.d.ts +12 -0
  70. package/dist/internal/features/work/components/work-editor-loading-state.d.ts +3 -0
  71. package/dist/internal/features/work/editors/document-changes-panel.d.ts +9 -0
  72. package/dist/internal/features/work/editors/document-citation-source-form.d.ts +33 -0
  73. package/dist/internal/features/work/editors/document-citations-panel.d.ts +8 -0
  74. package/dist/internal/features/work/editors/document-columns-panel.d.ts +5 -0
  75. package/dist/internal/features/work/editors/document-comment-composer.d.ts +13 -0
  76. package/dist/internal/features/work/editors/document-comments-panel.d.ts +17 -0
  77. package/dist/internal/features/work/editors/document-editor-focus.d.ts +1 -0
  78. package/dist/internal/features/work/editors/document-editor-pagination.d.ts +3 -0
  79. package/dist/internal/features/work/editors/document-editor-support.d.ts +34 -0
  80. package/dist/internal/features/work/editors/document-editor.d.ts +20 -0
  81. package/dist/internal/features/work/editors/document-find-highlight.d.ts +8 -0
  82. package/dist/internal/features/work/editors/document-find-replace-panel.d.ts +10 -0
  83. package/dist/internal/features/work/editors/document-formatting-options.d.ts +113 -0
  84. package/dist/internal/features/work/editors/document-home-ribbon.d.ts +9 -0
  85. package/dist/internal/features/work/editors/document-layout-panel.d.ts +10 -0
  86. package/dist/internal/features/work/editors/document-list-gallery.d.ts +4 -0
  87. package/dist/internal/features/work/editors/document-navigation-panel.d.ts +9 -0
  88. package/dist/internal/features/work/editors/document-page-chrome-editor.d.ts +39 -0
  89. package/dist/internal/features/work/editors/document-page-chrome-panel.d.ts +5 -0
  90. package/dist/internal/features/work/editors/document-page-chrome-ribbon.d.ts +10 -0
  91. package/dist/internal/features/work/editors/document-page-navigation.d.ts +14 -0
  92. package/dist/internal/features/work/editors/document-page-stack.d.ts +8 -0
  93. package/dist/internal/features/work/editors/document-pagination-popover.d.ts +4 -0
  94. package/dist/internal/features/work/editors/document-paragraph-spacing-popover.d.ts +4 -0
  95. package/dist/internal/features/work/editors/document-picture-ribbon.d.ts +4 -0
  96. package/dist/internal/features/work/editors/document-ruler-tab-stops.d.ts +12 -0
  97. package/dist/internal/features/work/editors/document-ruler.d.ts +15 -0
  98. package/dist/internal/features/work/editors/document-selection-context-menu.d.ts +22 -0
  99. package/dist/internal/features/work/editors/document-selection-toolbar.d.ts +8 -0
  100. package/dist/internal/features/work/editors/document-status-bar.d.ts +19 -0
  101. package/dist/internal/features/work/editors/document-style-gallery.d.ts +4 -0
  102. package/dist/internal/features/work/editors/document-table-insert-popover.d.ts +4 -0
  103. package/dist/internal/features/work/editors/document-table-ribbon.d.ts +7 -0
  104. package/dist/internal/features/work/editors/document-task-pane.d.ts +11 -0
  105. package/dist/internal/features/work/editors/document-toolbar.d.ts +100 -0
  106. package/dist/internal/features/work/editors/document-vertical-ruler.d.ts +6 -0
  107. package/dist/internal/features/work/editors/markdown-editor-focus.d.ts +2 -0
  108. package/dist/internal/features/work/editors/markdown-editor.d.ts +16 -0
  109. package/dist/internal/features/work/editors/markdown-insert-dialog.d.ts +19 -0
  110. package/dist/internal/features/work/editors/markdown-selection-context-menu.d.ts +14 -0
  111. package/dist/internal/features/work/editors/markdown-source-commands.d.ts +41 -0
  112. package/dist/internal/features/work/editors/markdown-status.d.ts +7 -0
  113. package/dist/internal/features/work/editors/markdown-toolbar.d.ts +22 -0
  114. package/dist/internal/features/work/editors/markdown-workspace.d.ts +26 -0
  115. package/dist/internal/features/work/editors/office-checkbox.d.ts +9 -0
  116. package/dist/internal/features/work/editors/office-color-picker.d.ts +11 -0
  117. package/dist/internal/features/work/editors/office-controls.d.ts +7 -0
  118. package/dist/internal/features/work/editors/office-dialog.d.ts +35 -0
  119. package/dist/internal/features/work/editors/office-editor-extension.d.ts +86 -0
  120. package/dist/internal/features/work/editors/office-file-data.d.ts +1 -0
  121. package/dist/internal/features/work/editors/office-font-families.d.ts +187 -0
  122. package/dist/internal/features/work/editors/office-menu-keyboard.d.ts +2 -0
  123. package/dist/internal/features/work/editors/office-number-field.d.ts +21 -0
  124. package/dist/internal/features/work/editors/office-number-normalization.d.ts +9 -0
  125. package/dist/internal/features/work/editors/office-select.d.ts +19 -0
  126. package/dist/internal/features/work/editors/office-shortcuts.d.ts +1 -0
  127. package/dist/internal/features/work/editors/office-slider.d.ts +10 -0
  128. package/dist/internal/features/work/editors/office-table-dimensions.d.ts +4 -0
  129. package/dist/internal/features/work/editors/office-table-insert-popover.d.ts +7 -0
  130. package/dist/internal/features/work/editors/office-task-pane.d.ts +7 -0
  131. package/dist/internal/features/work/editors/office-text-field.d.ts +20 -0
  132. package/dist/internal/features/work/editors/office-tiptap-extensions.d.ts +2 -0
  133. package/dist/internal/features/work/editors/pdf-annotation-controller.d.ts +23 -0
  134. package/dist/internal/features/work/editors/pdf-editor-extensions.d.ts +36 -0
  135. package/dist/internal/features/work/editors/pdf-thumbnail-rail.d.ts +26 -0
  136. package/dist/internal/features/work/editors/pdf-toolbar.d.ts +23 -0
  137. package/dist/internal/features/work/editors/pdf-viewer-controller.d.ts +48 -0
  138. package/dist/internal/features/work/editors/pdf-viewer.d.ts +11 -0
  139. package/dist/internal/features/work/editors/presentation-arrangement-controller.d.ts +19 -0
  140. package/dist/internal/features/work/editors/presentation-chart-analysis-canvas.d.ts +21 -0
  141. package/dist/internal/features/work/editors/presentation-chart-axis-canvas.d.ts +33 -0
  142. package/dist/internal/features/work/editors/presentation-chart-axis-editor.d.ts +5 -0
  143. package/dist/internal/features/work/editors/presentation-chart-canvas.d.ts +5 -0
  144. package/dist/internal/features/work/editors/presentation-chart-cartesian-canvas.d.ts +3 -0
  145. package/dist/internal/features/work/editors/presentation-chart-data-label-editor.d.ts +6 -0
  146. package/dist/internal/features/work/editors/presentation-chart-data-labels.d.ts +26 -0
  147. package/dist/internal/features/work/editors/presentation-chart-layout-editor.d.ts +5 -0
  148. package/dist/internal/features/work/editors/presentation-chart-legend-canvas.d.ts +15 -0
  149. package/dist/internal/features/work/editors/presentation-chart-panel.d.ts +7 -0
  150. package/dist/internal/features/work/editors/presentation-chart-series-analysis-editor.d.ts +6 -0
  151. package/dist/internal/features/work/editors/presentation-chart-series-canvas.d.ts +15 -0
  152. package/dist/internal/features/work/editors/presentation-command-controller.d.ts +3 -0
  153. package/dist/internal/features/work/editors/presentation-command-types.d.ts +206 -0
  154. package/dist/internal/features/work/editors/presentation-comments-panel.d.ts +9 -0
  155. package/dist/internal/features/work/editors/presentation-context-menu.d.ts +11 -0
  156. package/dist/internal/features/work/editors/presentation-design-panel.d.ts +13 -0
  157. package/dist/internal/features/work/editors/presentation-editor-focus.d.ts +12 -0
  158. package/dist/internal/features/work/editors/presentation-editor-operations.d.ts +10 -0
  159. package/dist/internal/features/work/editors/presentation-editor-types.d.ts +22 -0
  160. package/dist/internal/features/work/editors/presentation-editor.d.ts +3 -0
  161. package/dist/internal/features/work/editors/presentation-keyboard-extension.d.ts +3 -0
  162. package/dist/internal/features/work/editors/presentation-player.d.ts +7 -0
  163. package/dist/internal/features/work/editors/presentation-presenter-view.d.ts +15 -0
  164. package/dist/internal/features/work/editors/presentation-selection.d.ts +21 -0
  165. package/dist/internal/features/work/editors/presentation-slide-canvas.d.ts +26 -0
  166. package/dist/internal/features/work/editors/presentation-slide-thumbnail.d.ts +18 -0
  167. package/dist/internal/features/work/editors/presentation-status-bar.d.ts +10 -0
  168. package/dist/internal/features/work/editors/presentation-text-editor.d.ts +22 -0
  169. package/dist/internal/features/work/editors/presentation-text-formatting.d.ts +3 -0
  170. package/dist/internal/features/work/editors/presentation-thumbnail-rail.d.ts +20 -0
  171. package/dist/internal/features/work/editors/presentation-toolbar.d.ts +19 -0
  172. package/dist/internal/features/work/editors/presentation-transition-panel.d.ts +9 -0
  173. package/dist/internal/features/work/editors/presentation-workspace.d.ts +38 -0
  174. package/dist/internal/features/work/editors/spreadsheet-calculation-model.d.ts +8 -0
  175. package/dist/internal/features/work/editors/spreadsheet-calculation-projection.d.ts +32 -0
  176. package/dist/internal/features/work/editors/spreadsheet-chart-axis-editor.d.ts +9 -0
  177. package/dist/internal/features/work/editors/spreadsheet-chart-draft.d.ts +17 -0
  178. package/dist/internal/features/work/editors/spreadsheet-chart-layout-editor.d.ts +13 -0
  179. package/dist/internal/features/work/editors/spreadsheet-chart-panel.d.ts +10 -0
  180. package/dist/internal/features/work/editors/spreadsheet-chart-series-style-editor.d.ts +9 -0
  181. package/dist/internal/features/work/editors/spreadsheet-clipboard-shortcuts.d.ts +9 -0
  182. package/dist/internal/features/work/editors/spreadsheet-command-controller.d.ts +121 -0
  183. package/dist/internal/features/work/editors/spreadsheet-conditional-comparison-fields.d.ts +7 -0
  184. package/dist/internal/features/work/editors/spreadsheet-conditional-format-model.d.ts +72 -0
  185. package/dist/internal/features/work/editors/spreadsheet-conditional-format-panel.d.ts +7 -0
  186. package/dist/internal/features/work/editors/spreadsheet-conditional-threshold-fields.d.ts +10 -0
  187. package/dist/internal/features/work/editors/spreadsheet-context-menu.d.ts +36 -0
  188. package/dist/internal/features/work/editors/spreadsheet-data-label-editor.d.ts +9 -0
  189. package/dist/internal/features/work/editors/spreadsheet-editor-ribbon.d.ts +46 -0
  190. package/dist/internal/features/work/editors/spreadsheet-editor-support.d.ts +31 -0
  191. package/dist/internal/features/work/editors/spreadsheet-editor.d.ts +16 -0
  192. package/dist/internal/features/work/editors/spreadsheet-error-bar-editor.d.ts +10 -0
  193. package/dist/internal/features/work/editors/spreadsheet-find-bar.d.ts +8 -0
  194. package/dist/internal/features/work/editors/spreadsheet-find.d.ts +9 -0
  195. package/dist/internal/features/work/editors/spreadsheet-formula-panel.d.ts +10 -0
  196. package/dist/internal/features/work/editors/spreadsheet-header-footer-fields.d.ts +5 -0
  197. package/dist/internal/features/work/editors/spreadsheet-keyboard-navigation.d.ts +18 -0
  198. package/dist/internal/features/work/editors/spreadsheet-number-format.d.ts +11 -0
  199. package/dist/internal/features/work/editors/spreadsheet-pivot-panel.d.ts +10 -0
  200. package/dist/internal/features/work/editors/spreadsheet-print-settings-panel.d.ts +6 -0
  201. package/dist/internal/features/work/editors/spreadsheet-protection-panel.d.ts +7 -0
  202. package/dist/internal/features/work/editors/spreadsheet-sheet-bar.d.ts +17 -0
  203. package/dist/internal/features/work/editors/spreadsheet-sheet-model.d.ts +13 -0
  204. package/dist/internal/features/work/editors/spreadsheet-trendline-editor.d.ts +8 -0
  205. package/dist/internal/features/work/editors/spreadsheet-workbook-panel.d.ts +16 -0
  206. package/dist/internal/features/work/editors/use-document-comments.d.ts +26 -0
  207. package/dist/internal/features/work/editors/use-document-insert-commands.d.ts +19 -0
  208. package/dist/internal/features/work/editors/use-document-layout-fonts.d.ts +2 -0
  209. package/dist/internal/features/work/editors/use-document-page-chrome.d.ts +33 -0
  210. package/dist/internal/features/work/editors/use-document-pagination.d.ts +46 -0
  211. package/dist/internal/features/work/editors/use-markdown-source-history.d.ts +16 -0
  212. package/dist/internal/features/work/editors/use-office-draft.d.ts +10 -0
  213. package/dist/internal/features/work/editors/use-office-editor-keyboard-shortcuts.d.ts +9 -0
  214. package/dist/internal/features/work/editors/use-office-editor-runtime.d.ts +6 -0
  215. package/dist/internal/features/work/editors/use-office-editor-wheel-zoom.d.ts +19 -0
  216. package/dist/internal/features/work/editors/use-office-history.d.ts +10 -0
  217. package/dist/internal/features/work/editors/use-presentation-clipboard.d.ts +22 -0
  218. package/dist/internal/features/work/editors/use-presentation-design-commands.d.ts +33 -0
  219. package/dist/internal/features/work/editors/use-presentation-element-commands.d.ts +29 -0
  220. package/dist/internal/features/work/editors/use-presentation-geometry.d.ts +17 -0
  221. package/dist/internal/features/work/editors/use-presentation-history.d.ts +12 -0
  222. package/dist/internal/features/work/editors/use-presentation-review-commands.d.ts +22 -0
  223. package/dist/internal/features/work/editors/use-presentation-selection.d.ts +11 -0
  224. package/dist/internal/features/work/editors/use-presentation-slide-commands.d.ts +18 -0
  225. package/dist/internal/features/work/editors/use-presentation-thumbnail-visibility.d.ts +4 -0
  226. package/dist/internal/features/work/editors/use-presentation-thumbnail-window.d.ts +17 -0
  227. package/dist/internal/features/work/editors/use-presentation-transform.d.ts +53 -0
  228. package/dist/internal/features/work/editors/use-spreadsheet-calculation.d.ts +17 -0
  229. package/dist/internal/features/work/editors/use-spreadsheet-workbook-sync.d.ts +10 -0
  230. package/dist/internal/features/work/editors/work-office-chrome.d.ts +61 -0
  231. package/dist/internal/features/work/editors/work-office-ribbon-overflow.d.ts +18 -0
  232. package/dist/internal/features/work/work-agent-proposal-apply.d.ts +8 -0
  233. package/dist/internal/features/work/work-agent-proposal.d.ts +55 -0
  234. package/dist/internal/features/work/work-agent-request.d.ts +6 -0
  235. package/dist/internal/features/work/work-chart-series-analysis-context.d.ts +3 -0
  236. package/dist/internal/features/work/work-document-caption-nodes.d.ts +13 -0
  237. package/dist/internal/features/work/work-document-captions.d.ts +14 -0
  238. package/dist/internal/features/work/work-document-changes.d.ts +35 -0
  239. package/dist/internal/features/work/work-document-character-formatting.d.ts +13 -0
  240. package/dist/internal/features/work/work-document-citation-nodes.d.ts +25 -0
  241. package/dist/internal/features/work/work-document-citations.d.ts +21 -0
  242. package/dist/internal/features/work/work-document-columns.d.ts +15 -0
  243. package/dist/internal/features/work/work-document-comments.d.ts +48 -0
  244. package/dist/internal/features/work/work-document-extensions.d.ts +11 -0
  245. package/dist/internal/features/work/work-document-field-node.d.ts +12 -0
  246. package/dist/internal/features/work/work-document-fields.d.ts +15 -0
  247. package/dist/internal/features/work/work-document-file-io.d.ts +4 -0
  248. package/dist/internal/features/work/work-document-fonts.d.ts +16 -0
  249. package/dist/internal/features/work/work-document-image-layout.d.ts +29 -0
  250. package/dist/internal/features/work/work-document-incremental-layout.d.ts +17 -0
  251. package/dist/internal/features/work/work-document-layout.d.ts +5 -0
  252. package/dist/internal/features/work/work-document-line-measurement.d.ts +24 -0
  253. package/dist/internal/features/work/work-document-links.d.ts +2 -0
  254. package/dist/internal/features/work/work-document-lists.d.ts +25 -0
  255. package/dist/internal/features/work/work-document-model-codec.d.ts +3 -0
  256. package/dist/internal/features/work/work-document-model.d.ts +12 -0
  257. package/dist/internal/features/work/work-document-note-nodes.d.ts +11 -0
  258. package/dist/internal/features/work/work-document-notes.d.ts +18 -0
  259. package/dist/internal/features/work/work-document-outline.d.ts +14 -0
  260. package/dist/internal/features/work/work-document-page-break.d.ts +2 -0
  261. package/dist/internal/features/work/work-document-page-chrome-commands.d.ts +15 -0
  262. package/dist/internal/features/work/work-document-page-chrome.d.ts +19 -0
  263. package/dist/internal/features/work/work-document-page-color.d.ts +3 -0
  264. package/dist/internal/features/work/work-document-pages.d.ts +19 -0
  265. package/dist/internal/features/work/work-document-pagination-decorations.d.ts +3 -0
  266. package/dist/internal/features/work/work-document-pagination-dom.d.ts +35 -0
  267. package/dist/internal/features/work/work-document-pagination-measurement.d.ts +9 -0
  268. package/dist/internal/features/work/work-document-pagination-types.d.ts +88 -0
  269. package/dist/internal/features/work/work-document-pagination.d.ts +17 -0
  270. package/dist/internal/features/work/work-document-paragraph-formatting.d.ts +61 -0
  271. package/dist/internal/features/work/work-document-search.d.ts +11 -0
  272. package/dist/internal/features/work/work-document-section-editor.d.ts +20 -0
  273. package/dist/internal/features/work/work-document-section-node.d.ts +13 -0
  274. package/dist/internal/features/work/work-document-section.d.ts +34 -0
  275. package/dist/internal/features/work/work-document-selection-menu.d.ts +54 -0
  276. package/dist/internal/features/work/work-document-tab-node.d.ts +6 -0
  277. package/dist/internal/features/work/work-document-tab-stops.d.ts +29 -0
  278. package/dist/internal/features/work/work-document-table-cell-formatting.d.ts +46 -0
  279. package/dist/internal/features/work/work-document-table-commands.d.ts +17 -0
  280. package/dist/internal/features/work/work-document-table-pagination.d.ts +5 -0
  281. package/dist/internal/features/work/work-document-table-row.d.ts +37 -0
  282. package/dist/internal/features/work/work-document-table-sizing.d.ts +27 -0
  283. package/dist/internal/features/work/work-document-text-layout.d.ts +14 -0
  284. package/dist/internal/features/work/work-docx-bibliography.d.ts +12 -0
  285. package/dist/internal/features/work/work-docx-caption-diagnostics.d.ts +6 -0
  286. package/dist/internal/features/work/work-docx-caption-export.d.ts +4 -0
  287. package/dist/internal/features/work/work-docx-caption-fields.d.ts +5 -0
  288. package/dist/internal/features/work/work-docx-caption-import.d.ts +20 -0
  289. package/dist/internal/features/work/work-docx-change-import.d.ts +12 -0
  290. package/dist/internal/features/work/work-docx-citation-diagnostics.d.ts +3 -0
  291. package/dist/internal/features/work/work-docx-citation-export.d.ts +3 -0
  292. package/dist/internal/features/work/work-docx-citation-import.d.ts +23 -0
  293. package/dist/internal/features/work/work-docx-column-export.d.ts +14 -0
  294. package/dist/internal/features/work/work-docx-column-import.d.ts +2 -0
  295. package/dist/internal/features/work/work-docx-comment-import.d.ts +14 -0
  296. package/dist/internal/features/work/work-docx-export-formatting.d.ts +30 -0
  297. package/dist/internal/features/work/work-docx-export-image.d.ts +2 -0
  298. package/dist/internal/features/work/work-docx-export.d.ts +2 -0
  299. package/dist/internal/features/work/work-docx-field-export.d.ts +2 -0
  300. package/dist/internal/features/work/work-docx-field-import.d.ts +16 -0
  301. package/dist/internal/features/work/work-docx-field-instructions.d.ts +8 -0
  302. package/dist/internal/features/work/work-docx-image-layout-import.d.ts +12 -0
  303. package/dist/internal/features/work/work-docx-import.d.ts +50 -0
  304. package/dist/internal/features/work/work-docx-list-import.d.ts +14 -0
  305. package/dist/internal/features/work/work-docx-note-diagnostics.d.ts +3 -0
  306. package/dist/internal/features/work/work-docx-note-import.d.ts +3 -0
  307. package/dist/internal/features/work/work-docx-page-chrome-diagnostics.d.ts +3 -0
  308. package/dist/internal/features/work/work-docx-page-chrome-import.d.ts +11 -0
  309. package/dist/internal/features/work/work-docx-page-color.d.ts +2 -0
  310. package/dist/internal/features/work/work-docx-paragraph-alignment-import.d.ts +12 -0
  311. package/dist/internal/features/work/work-docx-paragraph-direction-import.d.ts +12 -0
  312. package/dist/internal/features/work/work-docx-paragraph-indent-import.d.ts +12 -0
  313. package/dist/internal/features/work/work-docx-paragraph-pagination-import.d.ts +12 -0
  314. package/dist/internal/features/work/work-docx-paragraph-spacing-import.d.ts +12 -0
  315. package/dist/internal/features/work/work-docx-paragraph-styles.d.ts +18 -0
  316. package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +19 -0
  317. package/dist/internal/features/work/work-docx-tab-stop-import.d.ts +13 -0
  318. package/dist/internal/features/work/work-docx-table-cell-export.d.ts +4 -0
  319. package/dist/internal/features/work/work-docx-table-cell-import.d.ts +16 -0
  320. package/dist/internal/features/work/work-docx-table-row-import.d.ts +13 -0
  321. package/dist/internal/features/work/work-docx-table-sizing-export.d.ts +8 -0
  322. package/dist/internal/features/work/work-docx-table-sizing-import.d.ts +12 -0
  323. package/dist/internal/features/work/work-docx-theme.d.ts +9 -0
  324. package/dist/internal/features/work/work-file-data.d.ts +1 -0
  325. package/dist/internal/features/work/work-file-download.d.ts +3 -0
  326. package/dist/internal/features/work/work-file-io.d.ts +8 -0
  327. package/dist/internal/features/work/work-markdown-extensions.d.ts +19 -0
  328. package/dist/internal/features/work/work-markdown-file-io.d.ts +3 -0
  329. package/dist/internal/features/work/work-markdown-selection-menu.d.ts +57 -0
  330. package/dist/internal/features/work/work-office-diagnostics.d.ts +9 -0
  331. package/dist/internal/features/work/work-ooxml-package.d.ts +28 -0
  332. package/dist/internal/features/work/work-pdf-export.d.ts +23 -0
  333. package/dist/internal/features/work/work-pptx-chart-axes.d.ts +3 -0
  334. package/dist/internal/features/work/work-pptx-chart-axis-diagnostics.d.ts +5 -0
  335. package/dist/internal/features/work/work-pptx-chart-data-labels.d.ts +12 -0
  336. package/dist/internal/features/work/work-pptx-chart-import.d.ts +11 -0
  337. package/dist/internal/features/work/work-pptx-chart-layout-style.d.ts +15 -0
  338. package/dist/internal/features/work/work-pptx-chart-series-analysis.d.ts +13 -0
  339. package/dist/internal/features/work/work-pptx-chart-xy.d.ts +3 -0
  340. package/dist/internal/features/work/work-pptx-comments.d.ts +17 -0
  341. package/dist/internal/features/work/work-pptx-export.d.ts +4 -0
  342. package/dist/internal/features/work/work-pptx-groups.d.ts +19 -0
  343. package/dist/internal/features/work/work-pptx-import.d.ts +6 -0
  344. package/dist/internal/features/work/work-pptx-layout-export.d.ts +13 -0
  345. package/dist/internal/features/work/work-pptx-layout-import.d.ts +30 -0
  346. package/dist/internal/features/work/work-pptx-style.d.ts +13 -0
  347. package/dist/internal/features/work/work-pptx-transition.d.ts +13 -0
  348. package/dist/internal/features/work/work-presentation-agent-context.d.ts +6 -0
  349. package/dist/internal/features/work/work-presentation-chart-axes.d.ts +14 -0
  350. package/dist/internal/features/work/work-presentation-charts.d.ts +37 -0
  351. package/dist/internal/features/work/work-presentation-clipboard.d.ts +26 -0
  352. package/dist/internal/features/work/work-presentation-file-io.d.ts +15 -0
  353. package/dist/internal/features/work/work-presentation-groups.d.ts +17 -0
  354. package/dist/internal/features/work/work-presentation-layouts.d.ts +13 -0
  355. package/dist/internal/features/work/work-presentation-transition.d.ts +4 -0
  356. package/dist/internal/features/work/work-presentation-visual-scale.d.ts +2 -0
  357. package/dist/internal/features/work/work-repository.d.ts +4 -0
  358. package/dist/internal/features/work/work-spreadsheet-agent-context.d.ts +15 -0
  359. package/dist/internal/features/work/work-spreadsheet-chart-axis.d.ts +42 -0
  360. package/dist/internal/features/work/work-spreadsheet-chart-layout.d.ts +29 -0
  361. package/dist/internal/features/work/work-spreadsheet-chart-legend-svg.d.ts +18 -0
  362. package/dist/internal/features/work/work-spreadsheet-chart-series-layout.d.ts +24 -0
  363. package/dist/internal/features/work/work-spreadsheet-chart-series-style.d.ts +29 -0
  364. package/dist/internal/features/work/work-spreadsheet-chart-svg-utils.d.ts +7 -0
  365. package/dist/internal/features/work/work-spreadsheet-chart-svg.d.ts +3 -0
  366. package/dist/internal/features/work/work-spreadsheet-chart-types.d.ts +147 -0
  367. package/dist/internal/features/work/work-spreadsheet-chart-validation.d.ts +2 -0
  368. package/dist/internal/features/work/work-spreadsheet-charts.d.ts +17 -0
  369. package/dist/internal/features/work/work-spreadsheet-combination-chart-svg.d.ts +9 -0
  370. package/dist/internal/features/work/work-spreadsheet-comments.d.ts +7 -0
  371. package/dist/internal/features/work/work-spreadsheet-conditional-canvas.d.ts +5 -0
  372. package/dist/internal/features/work/work-spreadsheet-conditional-colors.d.ts +9 -0
  373. package/dist/internal/features/work/work-spreadsheet-conditional-comparisons.d.ts +4 -0
  374. package/dist/internal/features/work/work-spreadsheet-conditional-data-bar.d.ts +8 -0
  375. package/dist/internal/features/work/work-spreadsheet-conditional-format.d.ts +10 -0
  376. package/dist/internal/features/work/work-spreadsheet-conditional-icons.d.ts +107 -0
  377. package/dist/internal/features/work/work-spreadsheet-conditional-values.d.ts +19 -0
  378. package/dist/internal/features/work/work-spreadsheet-data-label-svg.d.ts +27 -0
  379. package/dist/internal/features/work/work-spreadsheet-error-bar-svg.d.ts +5 -0
  380. package/dist/internal/features/work/work-spreadsheet-error-bars.d.ts +8 -0
  381. package/dist/internal/features/work/work-spreadsheet-formula-analysis.d.ts +30 -0
  382. package/dist/internal/features/work/work-spreadsheet-formula-support.d.ts +1 -0
  383. package/dist/internal/features/work/work-spreadsheet-formulas.d.ts +19 -0
  384. package/dist/internal/features/work/work-spreadsheet-header-footer.d.ts +14 -0
  385. package/dist/internal/features/work/work-spreadsheet-line-chart-svg.d.ts +10 -0
  386. package/dist/internal/features/work/work-spreadsheet-page-breaks.d.ts +4 -0
  387. package/dist/internal/features/work/work-spreadsheet-page-setup.d.ts +26 -0
  388. package/dist/internal/features/work/work-spreadsheet-pivot-engine.d.ts +21 -0
  389. package/dist/internal/features/work/work-spreadsheet-pivot-values.d.ts +7 -0
  390. package/dist/internal/features/work/work-spreadsheet-pivots.d.ts +27 -0
  391. package/dist/internal/features/work/work-spreadsheet-protection.d.ts +55 -0
  392. package/dist/internal/features/work/work-spreadsheet-ranges.d.ts +25 -0
  393. package/dist/internal/features/work/work-spreadsheet-trendline-svg.d.ts +9 -0
  394. package/dist/internal/features/work/work-spreadsheet-trendlines.d.ts +11 -0
  395. package/dist/internal/features/work/work-spreadsheet-xy-chart-axes-svg.d.ts +10 -0
  396. package/dist/internal/features/work/work-templates.d.ts +4 -0
  397. package/dist/internal/features/work/work-types.d.ts +513 -0
  398. package/dist/internal/features/work/work-xlsx-chart-axes.d.ts +4 -0
  399. package/dist/internal/features/work/work-xlsx-chart-diagnostics.d.ts +3 -0
  400. package/dist/internal/features/work/work-xlsx-chart-layout.d.ts +16 -0
  401. package/dist/internal/features/work/work-xlsx-chart-series-style.d.ts +6 -0
  402. package/dist/internal/features/work/work-xlsx-chart-values.d.ts +10 -0
  403. package/dist/internal/features/work/work-xlsx-charts.d.ts +32 -0
  404. package/dist/internal/features/work/work-xlsx-conditional-format-diagnostics.d.ts +6 -0
  405. package/dist/internal/features/work/work-xlsx-conditional-format-write.d.ts +11 -0
  406. package/dist/internal/features/work/work-xlsx-conditional-format.d.ts +29 -0
  407. package/dist/internal/features/work/work-xlsx-defined-names.d.ts +4 -0
  408. package/dist/internal/features/work/work-xlsx-drawing-geometry.d.ts +27 -0
  409. package/dist/internal/features/work/work-xlsx-formula-diagnostics.d.ts +3 -0
  410. package/dist/internal/features/work/work-xlsx-formulas.d.ts +40 -0
  411. package/dist/internal/features/work/work-xlsx-header-footer.d.ts +12 -0
  412. package/dist/internal/features/work/work-xlsx-images.d.ts +17 -0
  413. package/dist/internal/features/work/work-xlsx-interop.d.ts +39 -0
  414. package/dist/internal/features/work/work-xlsx-page-breaks.d.ts +7 -0
  415. package/dist/internal/features/work/work-xlsx-page-setup-diagnostics.d.ts +2 -0
  416. package/dist/internal/features/work/work-xlsx-page-setup.d.ts +5 -0
  417. package/dist/internal/features/work/work-xlsx-pivot-diagnostics.d.ts +3 -0
  418. package/dist/internal/features/work/work-xlsx-pivot-xml.d.ts +9 -0
  419. package/dist/internal/features/work/work-xlsx-pivots.d.ts +32 -0
  420. package/dist/internal/features/work/work-xlsx-protection.d.ts +24 -0
  421. package/dist/internal/features/workspace/components/workspace-context-menu.d.ts +29 -0
  422. package/dist/internal/features/workspace/components/workspace-text-control-selection.d.ts +7 -0
  423. package/dist/internal/kernel/office-kernel-client-types.d.ts +40 -0
  424. package/dist/internal/kernel/office-kernel-client.d.ts +4 -0
  425. package/dist/internal/kernel/office-kernel-fallback.d.ts +2 -0
  426. package/dist/internal/kernel/office-kernel-presentation-fallback.d.ts +2 -0
  427. package/dist/internal/kernel/office-kernel-protocol.d.ts +229 -0
  428. package/dist/internal/kernel/office-kernel-spreadsheet-fallback-formula.d.ts +3 -0
  429. package/dist/internal/kernel/office-kernel-spreadsheet-fallback-validation.d.ts +5 -0
  430. package/dist/internal/kernel/office-kernel-spreadsheet-fallback-value.d.ts +6 -0
  431. package/dist/internal/kernel/office-kernel-spreadsheet-fallback.d.ts +2 -0
  432. package/dist/internal/kernel/office-kernel-spreadsheet-protocol.d.ts +112 -0
  433. package/dist/internal/kernel/office-kernel-spreadsheet-session-fallback.d.ts +8 -0
  434. package/dist/internal/kernel/office-kernel-text-fallback.d.ts +10 -0
  435. package/dist/internal/kernel/office-kernel-version.d.ts +1 -0
  436. package/dist/internal/kernel/office-kernel.worker.d.ts +1 -0
  437. package/dist/internal/keyboard-shortcuts.d.ts +2 -0
  438. package/dist/internal/state/app-state.d.ts +8 -0
  439. package/dist/noto-naskh-arabic-regular.ttf +0 -0
  440. package/dist/noto-naskh-arabic.LICENSE.txt +94 -0
  441. package/dist/noto-sans-hans-regular.otf +0 -0
  442. package/dist/noto-sans-hans.LICENSE.txt +94 -0
  443. package/dist/noto-sans-hebrew-regular.ttf +0 -0
  444. package/dist/noto-sans-hebrew.LICENSE.txt +94 -0
  445. package/dist/noto-sans-regular.ttf +0 -0
  446. package/dist/noto-sans.LICENSE.txt +94 -0
  447. package/dist/office-kernel.wasm +0 -0
  448. package/dist/office-kernel.worker.js +14605 -0
  449. package/dist/office-surface.d.ts +10 -0
  450. package/dist/pdfium.LICENSE.txt +196 -0
  451. package/dist/pdfium.wasm +0 -0
  452. package/dist/pptxgen.LICENSE.txt +21 -0
  453. package/dist/pptxgen.bundle.js +11244 -0
  454. package/dist/react.d.ts +51 -0
  455. package/dist/react.js +1 -0
  456. package/dist/styles.css +17435 -0
  457. package/dist/styles.js +0 -0
  458. package/dist/vue.d.ts +282 -0
  459. package/dist/vue.js +231 -0
  460. package/dist/web-component.d.ts +98 -0
  461. package/dist/web-component.js +335 -0
  462. package/docs/browser-editor-architecture.md +947 -0
  463. package/package.json +165 -0
package/dist/8715.js ADDED
@@ -0,0 +1,3959 @@
1
+ import jszip from "jszip";
2
+ import { createOfficeId as createWorkId } from "./1134.js";
3
+ function normalizeWorkSpreadsheetDoughnutHoleSize(value) {
4
+ const size = Number(value);
5
+ if (!Number.isFinite(size)) return 50;
6
+ return Math.min(90, Math.max(10, Math.round(size)));
7
+ }
8
+ function normalizeWorkSpreadsheetRadarStyle(value) {
9
+ return 'standard' === value || 'filled' === value || 'marker' === value ? value : 'marker';
10
+ }
11
+ function normalizeWorkSpreadsheetScatterStyle(value) {
12
+ return 'line' === value || 'lineMarker' === value || 'smooth' === value || 'smoothMarker' === value || 'marker' === value ? value : 'marker';
13
+ }
14
+ function normalizeWorkSpreadsheetBubbleScale(value) {
15
+ const scale = Number(value);
16
+ if (!Number.isFinite(scale)) return 100;
17
+ return Math.min(300, Math.max(0, Math.round(scale)));
18
+ }
19
+ function normalizeWorkSpreadsheetBubbleSizeRepresents(value) {
20
+ return 'width' === value || 'w' === value ? 'width' : 'area';
21
+ }
22
+ function workSpreadsheetChartUsesNumericXAxis(type) {
23
+ return 'scatter' === type || 'bubble' === type;
24
+ }
25
+ function workSpreadsheetChartSupportsAxes(type) {
26
+ return 'pie' !== type && 'doughnut' !== type;
27
+ }
28
+ function normalizeWorkSpreadsheetCombinationSeriesType(value) {
29
+ return 'line' === value || 'area' === value || 'column' === value ? value : 'column';
30
+ }
31
+ function normalizeWorkSpreadsheetChartAxisGroup(value) {
32
+ return 'secondary' === value ? 'secondary' : 'primary';
33
+ }
34
+ function workSpreadsheetCombinationSeriesTypeLabel(type) {
35
+ if ('line' === type) return '折线图';
36
+ if ('area' === type) return '面积图';
37
+ return '柱形图';
38
+ }
39
+ function workSpreadsheetChartSupportsTrendlines(type) {
40
+ return 'pie' !== type && 'doughnut' !== type && 'radar' !== type;
41
+ }
42
+ function workSpreadsheetChartSupportsErrorBars(type) {
43
+ return 'pie' !== type && 'doughnut' !== type && 'radar' !== type;
44
+ }
45
+ function normalizeWorkSpreadsheetErrorBars(source, chartType) {
46
+ const direction = workSpreadsheetChartUsesNumericXAxis(chartType) && 'x' === source.direction ? 'x' : 'y';
47
+ const barType = 'plus' === source.barType || 'minus' === source.barType ? source.barType : 'both';
48
+ const valueType = 'percentage' === source.valueType || 'standardDeviation' === source.valueType || 'standardError' === source.valueType || 'custom' === source.valueType || 'fixedValue' === source.valueType ? source.valueType : 'fixedValue';
49
+ const fallbackValue = 'percentage' === valueType ? 5 : 1;
50
+ const numericValue = Number(source.value);
51
+ const value = Number.isFinite(numericValue) ? Math.max(0, numericValue) : fallbackValue;
52
+ const plusValues = source.plusValues?.map(normalizedErrorBarAmount);
53
+ const minusValues = source.minusValues?.map(normalizedErrorBarAmount);
54
+ return {
55
+ direction,
56
+ barType,
57
+ valueType,
58
+ ...'fixedValue' === valueType || 'percentage' === valueType || 'standardDeviation' === valueType ? {
59
+ value
60
+ } : {},
61
+ ...false === source.showEndCaps ? {
62
+ showEndCaps: false
63
+ } : {},
64
+ ...'custom' === valueType && 'minus' !== barType && plusValues?.length ? {
65
+ plusValues
66
+ } : {},
67
+ ...'custom' === valueType && 'minus' !== barType && source.plusReference?.trim() ? {
68
+ plusReference: source.plusReference.trim().replace(/^=/, '')
69
+ } : {},
70
+ ...'custom' === valueType && 'plus' !== barType && minusValues?.length ? {
71
+ minusValues
72
+ } : {},
73
+ ...'custom' === valueType && 'plus' !== barType && source.minusReference?.trim() ? {
74
+ minusReference: source.minusReference.trim().replace(/^=/, '')
75
+ } : {}
76
+ };
77
+ }
78
+ function workSpreadsheetErrorBarValueTypeLabel(type) {
79
+ if ('percentage' === type) return '百分比';
80
+ if ('standardDeviation' === type) return '标准差';
81
+ if ('standardError' === type) return '标准误差';
82
+ if ('custom' === type) return '自定义';
83
+ return '固定值';
84
+ }
85
+ function workSpreadsheetErrorBarTypeLabel(type) {
86
+ if ('plus' === type) return '正向';
87
+ if ('minus' === type) return '负向';
88
+ return '双向';
89
+ }
90
+ function normalizedErrorBarAmount(value) {
91
+ return Number.isFinite(value) ? Math.max(0, value) : 0;
92
+ }
93
+ function normalizeWorkSpreadsheetDataLabelPosition(value) {
94
+ return 'center' === value || 'insideBase' === value || 'insideEnd' === value || 'outsideEnd' === value || 'left' === value || 'right' === value || 'above' === value || 'below' === value || 'bestFit' === value ? value : 'bestFit';
95
+ }
96
+ function normalizeWorkSpreadsheetDataLabels(source, chartType) {
97
+ const position = source.position ? normalizeWorkSpreadsheetDataLabelPosition(source.position) : void 0;
98
+ const separator = 'string' == typeof source.separator ? source.separator.slice(0, 64) : void 0;
99
+ return {
100
+ ...true === source.showValue ? {
101
+ showValue: true
102
+ } : {},
103
+ ...true === source.showCategoryName ? {
104
+ showCategoryName: true
105
+ } : {},
106
+ ...true === source.showSeriesName ? {
107
+ showSeriesName: true
108
+ } : {},
109
+ ...true === source.showPercentage && ('pie' === chartType || 'doughnut' === chartType) ? {
110
+ showPercentage: true
111
+ } : {},
112
+ ...true === source.showBubbleSize && 'bubble' === chartType ? {
113
+ showBubbleSize: true
114
+ } : {},
115
+ ...void 0 !== separator ? {
116
+ separator
117
+ } : {},
118
+ ...position ? {
119
+ position
120
+ } : {}
121
+ };
122
+ }
123
+ function workSpreadsheetDataLabelPositionLabel(position) {
124
+ if ('center' === position) return '居中';
125
+ if ('insideBase' === position) return '内侧基部';
126
+ if ('insideEnd' === position) return '内侧末端';
127
+ if ('outsideEnd' === position) return '外侧末端';
128
+ if ('left' === position) return '左侧';
129
+ if ('right' === position) return '右侧';
130
+ if ('above' === position) return '上方';
131
+ if ('below' === position) return '下方';
132
+ return '最佳匹配';
133
+ }
134
+ function normalizeWorkSpreadsheetTrendlineType(value) {
135
+ return 'exponential' === value || 'logarithmic' === value || 'polynomial' === value || 'power' === value || 'movingAverage' === value || 'linear' === value ? value : 'linear';
136
+ }
137
+ function normalizeWorkSpreadsheetTrendline(trendline) {
138
+ const type = normalizeWorkSpreadsheetTrendlineType(trendline.type);
139
+ const name = trendline.name?.trim().slice(0, 255);
140
+ const order = normalizedInteger(trendline.order, 2, 6, 2);
141
+ const period = normalizedInteger(trendline.period, 2, 255, 2);
142
+ const forward = normalizedNonNegativeNumber(trendline.forward);
143
+ const backward = normalizedNonNegativeNumber(trendline.backward);
144
+ const intercept = Number.isFinite(trendline.intercept) ? Number(trendline.intercept) : void 0;
145
+ return {
146
+ type,
147
+ ...name ? {
148
+ name
149
+ } : {},
150
+ ...'polynomial' === type ? {
151
+ order
152
+ } : {},
153
+ ...'movingAverage' === type ? {
154
+ period
155
+ } : {},
156
+ ...forward > 0 ? {
157
+ forward
158
+ } : {},
159
+ ...backward > 0 ? {
160
+ backward
161
+ } : {},
162
+ ...void 0 !== intercept ? {
163
+ intercept
164
+ } : {},
165
+ ...true === trendline.displayEquation ? {
166
+ displayEquation: true
167
+ } : {},
168
+ ...true === trendline.displayRSquared ? {
169
+ displayRSquared: true
170
+ } : {}
171
+ };
172
+ }
173
+ function workSpreadsheetTrendlineTypeLabel(type) {
174
+ if ('exponential' === type) return '指数';
175
+ if ('logarithmic' === type) return '对数';
176
+ if ('polynomial' === type) return '多项式';
177
+ if ('power' === type) return '幂';
178
+ if ('movingAverage' === type) return '移动平均';
179
+ return '线性';
180
+ }
181
+ function normalizedInteger(value, minimum, maximum, fallback) {
182
+ const number = Number(value);
183
+ if (!Number.isFinite(number)) return fallback;
184
+ return Math.min(maximum, Math.max(minimum, Math.round(number)));
185
+ }
186
+ function normalizedNonNegativeNumber(value) {
187
+ const number = Number(value);
188
+ return Number.isFinite(number) ? Math.max(0, number) : 0;
189
+ }
190
+ function workSpreadsheetChartTypeLabel(type) {
191
+ if ('bar' === type) return '条形图';
192
+ if ('line' === type) return '折线图';
193
+ if ('pie' === type) return '饼图';
194
+ if ('doughnut' === type) return '圆环图';
195
+ if ('area' === type) return '面积图';
196
+ if ('radar' === type) return '雷达图';
197
+ if ('scatter' === type) return '散点图';
198
+ if ('bubble' === type) return '气泡图';
199
+ if ('combination' === type) return '组合图';
200
+ return '柱形图';
201
+ }
202
+ const CELL_REFERENCE = /\$?([A-Z]{1,3})\$?([1-9]\d*)/i;
203
+ const CELL_OR_RANGE = new RegExp(`^${CELL_REFERENCE.source}(?::${CELL_REFERENCE.source})?$`, 'i');
204
+ const COLUMN_RANGE = /^\$?([A-Z]{1,3}):\$?([A-Z]{1,3})$/i;
205
+ const ROW_RANGE = /^\$?([1-9]\d*):\$?([1-9]\d*)$/;
206
+ const DEFINED_NAME = /^[\p{L}_\\][\p{L}\p{N}_.\\]*$/u;
207
+ function isValidSpreadsheetDefinedName(value) {
208
+ const name = value.trim();
209
+ if (!name || name.length > 255 || !DEFINED_NAME.test(name) || /^_xlnm\./i.test(name)) return false;
210
+ if (/^[A-Z]{1,3}[1-9]\d*$/i.test(name) || /^R\d+C\d+$/i.test(name)) return false;
211
+ return true;
212
+ }
213
+ function normalizeSpreadsheetPrintArea(value) {
214
+ const parts = splitRangeList(value.trim().replace(/^=/, ''));
215
+ if (!parts.length) return null;
216
+ const normalized = parts.map((part)=>{
217
+ const reference = unqualifiedReference(part);
218
+ if (!CELL_OR_RANGE.test(reference) && !COLUMN_RANGE.test(reference) && !ROW_RANGE.test(reference)) return null;
219
+ return reference.replace(/[A-Z]+/gi, (column)=>column.toUpperCase());
220
+ });
221
+ return normalized.every((part)=>Boolean(part)) ? normalized.join(',') : null;
222
+ }
223
+ function normalizeSpreadsheetPrintTitleRows(value) {
224
+ const match = ROW_RANGE.exec(unqualifiedReference(value.trim().replace(/^=/, '')));
225
+ if (!match) return null;
226
+ const start = Number(match[1]);
227
+ const end = Number(match[2]);
228
+ return `$${Math.min(start, end)}:$${Math.max(start, end)}`;
229
+ }
230
+ function normalizeSpreadsheetPrintTitleColumns(value) {
231
+ const match = COLUMN_RANGE.exec(unqualifiedReference(value.trim().replace(/^=/, '')));
232
+ if (!match) return null;
233
+ const start = decodeColumn(match[1]);
234
+ const end = decodeColumn(match[2]);
235
+ return `$${encodeColumn(Math.min(start, end))}:$${encodeColumn(Math.max(start, end))}`;
236
+ }
237
+ function parseSpreadsheetPrintTitles(value) {
238
+ let rows;
239
+ let columns;
240
+ for (const part of splitRangeList(value.trim().replace(/^=/, ''))){
241
+ const rowReference = normalizeSpreadsheetPrintTitleRows(part);
242
+ if (rowReference) {
243
+ if (rows) return null;
244
+ const match = ROW_RANGE.exec(rowReference);
245
+ rows = [
246
+ Number(match[1]) - 1,
247
+ Number(match[2]) - 1
248
+ ];
249
+ continue;
250
+ }
251
+ const columnReference = normalizeSpreadsheetPrintTitleColumns(part);
252
+ if (!columnReference || columns) return null;
253
+ const match = COLUMN_RANGE.exec(columnReference);
254
+ columns = [
255
+ decodeColumn(match[1]),
256
+ decodeColumn(match[2])
257
+ ];
258
+ }
259
+ return rows || columns ? {
260
+ rows,
261
+ columns
262
+ } : null;
263
+ }
264
+ function parseSpreadsheetCellRanges(value) {
265
+ const parts = splitRangeList(value.trim().replace(/^=/, ''));
266
+ if (!parts.length) return null;
267
+ const ranges = parts.map((part)=>{
268
+ const cells = CELL_OR_RANGE.exec(unqualifiedReference(part));
269
+ if (!cells) return null;
270
+ const first = decodeCell(cells[1], cells[2]);
271
+ const second = cells[3] && cells[4] ? decodeCell(cells[3], cells[4]) : first;
272
+ return {
273
+ row: [
274
+ Math.min(first.row, second.row),
275
+ Math.max(first.row, second.row)
276
+ ],
277
+ column: [
278
+ Math.min(first.column, second.column),
279
+ Math.max(first.column, second.column)
280
+ ]
281
+ };
282
+ });
283
+ return ranges.every((range)=>Boolean(range)) ? ranges : null;
284
+ }
285
+ function formatSpreadsheetCellRanges(ranges) {
286
+ return ranges.map((range)=>{
287
+ const start = encodeCell(range.row[0], range.column[0]);
288
+ const end = encodeCell(range.row[1], range.column[1]);
289
+ return start === end ? start : `${start}:${end}`;
290
+ }).join(',');
291
+ }
292
+ function qualifySpreadsheetRange(value, sheetName) {
293
+ const prefix = `'${sheetName.replaceAll("'", "''")}'!`;
294
+ return splitRangeList(value).map((part)=>part.includes('!') ? part.trim() : `${prefix}${part.trim()}`).join(',');
295
+ }
296
+ function stripSpreadsheetSheetQualifier(value, sheetName) {
297
+ const quotedPrefix = `'${sheetName.replaceAll("'", "''")}'!`;
298
+ const plainPrefix = `${sheetName}!`;
299
+ return splitRangeList(value.trim().replace(/^=/, '')).map((part)=>{
300
+ const reference = part.trim();
301
+ if (reference.toLowerCase().startsWith(quotedPrefix.toLowerCase())) return reference.slice(quotedPrefix.length);
302
+ if (reference.toLowerCase().startsWith(plainPrefix.toLowerCase())) return reference.slice(plainPrefix.length);
303
+ return reference;
304
+ }).join(',');
305
+ }
306
+ function splitRangeList(value) {
307
+ const parts = [];
308
+ let current = '';
309
+ let quoted = false;
310
+ for(let index = 0; index < value.length; index += 1){
311
+ const character = value[index];
312
+ if ("'" === character) {
313
+ if (quoted && "'" === value[index + 1]) {
314
+ current += "''";
315
+ index += 1;
316
+ continue;
317
+ }
318
+ quoted = !quoted;
319
+ }
320
+ if (',' !== character || quoted) current += character;
321
+ else {
322
+ if (current.trim()) parts.push(current.trim());
323
+ current = '';
324
+ }
325
+ }
326
+ if (current.trim()) parts.push(current.trim());
327
+ return parts;
328
+ }
329
+ function unqualifiedReference(value) {
330
+ const reference = value.trim().replace(/^=/, '');
331
+ const separator = reference.lastIndexOf('!');
332
+ return separator >= 0 ? reference.slice(separator + 1) : reference;
333
+ }
334
+ function decodeCell(column, row) {
335
+ return {
336
+ row: Math.max(0, Number(row) - 1),
337
+ column: decodeColumn(column)
338
+ };
339
+ }
340
+ function decodeColumn(value) {
341
+ let result = 0;
342
+ for (const character of value.toUpperCase())result = 26 * result + character.charCodeAt(0) - 64;
343
+ return Math.max(0, result - 1);
344
+ }
345
+ function encodeCell(row, column) {
346
+ return `${encodeColumn(column)}${Math.max(0, row) + 1}`;
347
+ }
348
+ function encodeColumn(column) {
349
+ let value = Math.max(0, column) + 1;
350
+ let label = '';
351
+ while(value > 0){
352
+ value -= 1;
353
+ label = String.fromCharCode(65 + value % 26) + label;
354
+ value = Math.floor(value / 26);
355
+ }
356
+ return label;
357
+ }
358
+ function spreadsheetPivotCellValue(cell) {
359
+ return cell?.v ?? cell?.m ?? null;
360
+ }
361
+ function normalizeSpreadsheetPivotFilterValue(value) {
362
+ if (null == value || '' === value) return null;
363
+ if (value instanceof Date) return value.toISOString();
364
+ if ('number' == typeof value) return Number.isFinite(value) ? value : String(value);
365
+ if ('boolean' == typeof value) return value;
366
+ return String(value);
367
+ }
368
+ function spreadsheetPivotFilterValueKey(value) {
369
+ if (null === value) return 'blank:';
370
+ return `${typeof value}:${String(value)}`;
371
+ }
372
+ function displaySpreadsheetPivotValue(value) {
373
+ if (null == value || '' === value) return '(空白)';
374
+ if (value instanceof Date) return value.toISOString().slice(0, 10);
375
+ if ('boolean' == typeof value) return value ? 'TRUE' : 'FALSE';
376
+ return String(value);
377
+ }
378
+ function finiteSpreadsheetPivotNumber(value) {
379
+ if ('number' == typeof value) return Number.isFinite(value) ? value : null;
380
+ if (value instanceof Date) return value.getTime();
381
+ return null;
382
+ }
383
+ function spreadsheetPivotFilterItemsFromSource(sourceSheet, sourceBounds, fieldIndex) {
384
+ if (fieldIndex < 0 || fieldIndex > sourceBounds.endColumn - sourceBounds.startColumn) return [];
385
+ const items = new Map();
386
+ for(let row = sourceBounds.startRow + 1; row <= sourceBounds.endRow; row += 1){
387
+ const value = normalizeSpreadsheetPivotFilterValue(spreadsheetPivotCellValue(sourceSheet.data?.[row]?.[sourceBounds.startColumn + fieldIndex]));
388
+ const item = {
389
+ value,
390
+ label: displaySpreadsheetPivotValue(value)
391
+ };
392
+ items.set(spreadsheetPivotFilterValueKey(value), item);
393
+ }
394
+ return Array.from(items.values()).sort((left, right)=>left.label.localeCompare(right.label, 'zh-CN', {
395
+ numeric: true,
396
+ sensitivity: 'base'
397
+ }));
398
+ }
399
+ function buildSpreadsheetPivotOutput(sourceSheet, sourceBounds, fields, pivot) {
400
+ const rowCombinations = new Map();
401
+ const columnCombinations = new Map();
402
+ const cells = new Map();
403
+ const rowTotals = new Map();
404
+ const columnTotals = new Map();
405
+ const totals = pivot.values.map(()=>accumulator());
406
+ const singleColumn = combination([]);
407
+ if (!pivot.columnFields.length) columnCombinations.set(singleColumn.key, singleColumn);
408
+ for(let row = sourceBounds.startRow + 1; row <= sourceBounds.endRow; row += 1){
409
+ const values = fields.map((field)=>spreadsheetPivotCellValue(sourceSheet.data?.[row]?.[sourceBounds.startColumn + field.index]));
410
+ if (values.every((value)=>null == value || '' === value)) continue;
411
+ if (!matchesPivotReportFilters(values, pivot)) continue;
412
+ const rowCombination = combination(pivot.rowFields.map((fieldIndex)=>values[fieldIndex]));
413
+ const columnCombination = pivot.columnFields.length ? combination(pivot.columnFields.map((fieldIndex)=>values[fieldIndex])) : singleColumn;
414
+ rowCombinations.set(rowCombination.key, rowCombination);
415
+ columnCombinations.set(columnCombination.key, columnCombination);
416
+ for (const [valueIndex, valueField] of pivot.values.entries()){
417
+ const value = values[valueField.fieldIndex];
418
+ updateAccumulator(mapAccumulator(cells, cellKey(rowCombination.key, columnCombination.key, valueIndex)), value);
419
+ updateAccumulator(mapAccumulator(rowTotals, cellKey(rowCombination.key, '', valueIndex)), value);
420
+ updateAccumulator(mapAccumulator(columnTotals, cellKey('', columnCombination.key, valueIndex)), value);
421
+ updateAccumulator(totals[valueIndex], value);
422
+ }
423
+ }
424
+ const sortedRows = Array.from(rowCombinations.values()).sort(compareCombinations);
425
+ const sortedColumns = Array.from(columnCombinations.values()).sort(compareCombinations);
426
+ if (!sortedRows.length) return [];
427
+ const headers = [
428
+ ...pivot.rowFields.map((fieldIndex)=>fields[fieldIndex].name),
429
+ ...sortedColumns.flatMap((column)=>pivot.values.map((value)=>columnValueCaption(column, pivot.columnFields, fields, value))),
430
+ ...pivot.rowGrandTotals && pivot.columnFields.length ? pivot.values.map((value)=>`总计 · ${valueCaption(value, fields)}`) : []
431
+ ];
432
+ const output = [
433
+ headers.map((value)=>pivotHeaderCell(value))
434
+ ];
435
+ for (const row of sortedRows){
436
+ const result = row.values.map((value)=>pivotDimensionCell(value));
437
+ 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));
438
+ 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));
439
+ output.push(result);
440
+ }
441
+ if (pivot.columnGrandTotals) {
442
+ const result = pivot.rowFields.map((_field, index)=>0 === index ? pivotTotalLabelCell() : pivotValueCell(null, true));
443
+ 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));
444
+ if (pivot.rowGrandTotals && pivot.columnFields.length) for (const [valueIndex, value] of pivot.values.entries())result.push(pivotValueCell(accumulatorValue(totals[valueIndex], value.aggregation), true));
445
+ output.push(result);
446
+ }
447
+ const reportFilters = pivot.reportFilters ?? [];
448
+ if (!reportFilters.length) return output;
449
+ const width = output[0]?.length ?? 0;
450
+ const filterRows = reportFilters.map((filter)=>{
451
+ const row = Array(width).fill(null);
452
+ row[0] = pivotFilterLabelCell(fields[filter.fieldIndex].name);
453
+ row[1] = pivotFilterSelectionCell(void 0 === filter.selectedItem ? '(全部)' : displaySpreadsheetPivotValue(filter.selectedItem));
454
+ return row;
455
+ });
456
+ filterRows.push(Array(width).fill(null));
457
+ return [
458
+ ...filterRows,
459
+ ...output
460
+ ];
461
+ }
462
+ function accumulator() {
463
+ return {
464
+ nonEmptyCount: 0,
465
+ numberCount: 0,
466
+ sum: 0,
467
+ product: 1,
468
+ mean: 0,
469
+ m2: 0
470
+ };
471
+ }
472
+ function mapAccumulator(map, key) {
473
+ const existing = map.get(key);
474
+ if (existing) return existing;
475
+ const next = accumulator();
476
+ map.set(key, next);
477
+ return next;
478
+ }
479
+ function updateAccumulator(state, value) {
480
+ if (null != value && '' !== value) state.nonEmptyCount += 1;
481
+ const number = finiteSpreadsheetPivotNumber(value);
482
+ if (null === number) return;
483
+ state.numberCount += 1;
484
+ state.sum += number;
485
+ state.product *= number;
486
+ state.minimum = void 0 === state.minimum ? number : Math.min(state.minimum, number);
487
+ state.maximum = void 0 === state.maximum ? number : Math.max(state.maximum, number);
488
+ const delta = number - state.mean;
489
+ state.mean += delta / state.numberCount;
490
+ state.m2 += delta * (number - state.mean);
491
+ }
492
+ function accumulatorValue(state, aggregation) {
493
+ if (!state) return 'sum' === aggregation || 'count' === aggregation || 'counta' === aggregation ? 0 : null;
494
+ if ('count' === aggregation) return state.numberCount;
495
+ if ('counta' === aggregation) return state.nonEmptyCount;
496
+ if ('average' === aggregation) return state.numberCount ? state.sum / state.numberCount : null;
497
+ if ('max' === aggregation) return state.maximum ?? null;
498
+ if ('min' === aggregation) return state.minimum ?? null;
499
+ if ('product' === aggregation) return state.numberCount ? state.product : null;
500
+ if ('stdDev' === aggregation) return state.numberCount > 1 ? Math.sqrt(state.m2 / (state.numberCount - 1)) : null;
501
+ if ('stdDevP' === aggregation) return state.numberCount ? Math.sqrt(state.m2 / state.numberCount) : null;
502
+ if ('var' === aggregation) return state.numberCount > 1 ? state.m2 / (state.numberCount - 1) : null;
503
+ if ('varP' === aggregation) return state.numberCount ? state.m2 / state.numberCount : null;
504
+ return state.sum;
505
+ }
506
+ function combination(values) {
507
+ return {
508
+ key: JSON.stringify(values.map(normalizedPivotKeyValue)),
509
+ values
510
+ };
511
+ }
512
+ function normalizedPivotKeyValue(value) {
513
+ if (value instanceof Date) return [
514
+ 'date',
515
+ value.toISOString()
516
+ ];
517
+ if ('number' == typeof value) return [
518
+ 'number',
519
+ Number.isFinite(value) ? value : null
520
+ ];
521
+ if ('boolean' == typeof value) return [
522
+ 'boolean',
523
+ value
524
+ ];
525
+ if (null == value || '' === value) return [
526
+ 'blank'
527
+ ];
528
+ return [
529
+ 'string',
530
+ String(value)
531
+ ];
532
+ }
533
+ function compareCombinations(left, right) {
534
+ const count = Math.max(left.values.length, right.values.length);
535
+ for(let index = 0; index < count; index += 1){
536
+ const difference = displaySpreadsheetPivotValue(left.values[index]).localeCompare(displaySpreadsheetPivotValue(right.values[index]), 'zh-CN', {
537
+ numeric: true,
538
+ sensitivity: 'base'
539
+ });
540
+ if (difference) return difference;
541
+ }
542
+ return 0;
543
+ }
544
+ function cellKey(rowKey, columnKey, valueIndex) {
545
+ return `${rowKey.length}:${rowKey}${columnKey.length}:${columnKey}${valueIndex}`;
546
+ }
547
+ function columnValueCaption(column, columnFields, fields, value) {
548
+ const dimensions = column.values.map((item, index)=>{
549
+ const label = displaySpreadsheetPivotValue(item);
550
+ return columnFields.length > 1 ? `${fields[columnFields[index]].name}: ${label}` : label;
551
+ });
552
+ return [
553
+ ...dimensions,
554
+ valueCaption(value, fields)
555
+ ].filter(Boolean).join(' · ');
556
+ }
557
+ function valueCaption(value, fields) {
558
+ return value.caption?.trim() || defaultPivotValueCaption(fields[value.fieldIndex]?.name ?? `字段 ${value.fieldIndex + 1}`, value.aggregation);
559
+ }
560
+ function defaultPivotValueCaption(fieldName, aggregation) {
561
+ return `${fieldName}(${spreadsheetPivotAggregationLabel(aggregation)})`;
562
+ }
563
+ function spreadsheetPivotAggregationLabel(aggregation) {
564
+ if ('count' === aggregation) return '计数';
565
+ if ('counta' === aggregation) return '非空计数';
566
+ if ('average' === aggregation) return '平均值';
567
+ if ('max' === aggregation) return '最大值';
568
+ if ('min' === aggregation) return '最小值';
569
+ if ('product' === aggregation) return '乘积';
570
+ if ('stdDev' === aggregation) return '样本标准差';
571
+ if ('stdDevP' === aggregation) return '总体标准差';
572
+ if ('var' === aggregation) return '样本方差';
573
+ if ('varP' === aggregation) return '总体方差';
574
+ return '求和';
575
+ }
576
+ function pivotHeaderCell(value) {
577
+ return {
578
+ v: value,
579
+ m: value,
580
+ bl: 1,
581
+ bg: '#e7f1ec',
582
+ fc: '#173d2b'
583
+ };
584
+ }
585
+ function pivotDimensionCell(value) {
586
+ const display = displaySpreadsheetPivotValue(value);
587
+ return {
588
+ v: null == value || '' === value ? display : value,
589
+ m: display
590
+ };
591
+ }
592
+ function pivotValueCell(value, total) {
593
+ if (null === value) return total ? {
594
+ v: '',
595
+ m: '',
596
+ bl: 1,
597
+ bg: '#f3f7f5'
598
+ } : null;
599
+ return {
600
+ v: value,
601
+ m: formatPivotNumber(value),
602
+ bl: total ? 1 : void 0,
603
+ bg: total ? '#f3f7f5' : void 0,
604
+ ht: 2
605
+ };
606
+ }
607
+ function pivotTotalLabelCell() {
608
+ return {
609
+ v: '总计',
610
+ m: '总计',
611
+ bl: 1,
612
+ bg: '#f3f7f5'
613
+ };
614
+ }
615
+ function pivotFilterLabelCell(value) {
616
+ return {
617
+ v: value,
618
+ m: value,
619
+ bl: 1,
620
+ bg: '#f3f7f5',
621
+ fc: '#173d2b'
622
+ };
623
+ }
624
+ function pivotFilterSelectionCell(value) {
625
+ return {
626
+ v: value,
627
+ m: value,
628
+ bg: '#f3f7f5'
629
+ };
630
+ }
631
+ function formatPivotNumber(value) {
632
+ return Number.isInteger(value) ? String(value) : String(Number(value.toPrecision(15)));
633
+ }
634
+ function matchesPivotReportFilters(values, pivot) {
635
+ return (pivot.reportFilters ?? []).every((filter)=>void 0 === filter.selectedItem || spreadsheetPivotFilterValueKey(normalizeSpreadsheetPivotFilterValue(values[filter.fieldIndex])) === spreadsheetPivotFilterValueKey(filter.selectedItem));
636
+ }
637
+ const MAXIMUM_SOURCE_CELLS = 100000;
638
+ const MAXIMUM_OUTPUT_CELLS = 20000;
639
+ const MAXIMUM_XLSX_ROW = 1048575;
640
+ const MAXIMUM_XLSX_COLUMN = 16383;
641
+ function spreadsheetPivotCount(content) {
642
+ return content.sheets.reduce((count, sheet)=>count + (sheet.pivotTables?.length ?? 0), 0);
643
+ }
644
+ function spreadsheetPivotFields(content, pivot) {
645
+ const sourceSheet = content.sheets.find((sheet)=>sheet.id === pivot.sourceSheetId);
646
+ const range = singleRange(pivot.sourceReference);
647
+ if (!sourceSheet || !range) return [];
648
+ const fields = [];
649
+ for(let column = range.startColumn; column <= range.endColumn; column += 1){
650
+ const header = spreadsheetPivotCellValue(sourceSheet.data?.[range.startRow]?.[column]);
651
+ if (null == header || '' === String(header).trim()) return [];
652
+ const name = displaySpreadsheetPivotValue(header).trim();
653
+ let numeric = false;
654
+ for(let row = range.startRow + 1; row <= range.endRow; row += 1){
655
+ const value = spreadsheetPivotCellValue(sourceSheet.data?.[row]?.[column]);
656
+ if (null != value && '' !== value) {
657
+ if (null !== finiteSpreadsheetPivotNumber(value)) {
658
+ numeric = true;
659
+ break;
660
+ }
661
+ }
662
+ }
663
+ fields.push({
664
+ index: column - range.startColumn,
665
+ name,
666
+ numeric
667
+ });
668
+ }
669
+ return uniqueFieldNames(fields) ? fields : [];
670
+ }
671
+ function spreadsheetPivotFilterItems(content, pivot, fieldIndex) {
672
+ const sourceSheet = content.sheets.find((sheet)=>sheet.id === pivot.sourceSheetId);
673
+ const range = singleRange(pivot.sourceReference);
674
+ if (!sourceSheet || !range || fieldIndex < 0 || fieldIndex > range.endColumn - range.startColumn) return [];
675
+ return spreadsheetPivotFilterItemsFromSource(sourceSheet, range, fieldIndex);
676
+ }
677
+ function createSpreadsheetPivotFromSelection(content, sourceSheetId, selection) {
678
+ const sourceSheet = content.sheets.find((sheet)=>sheet.id === sourceSheetId);
679
+ const bounds = selectionBounds(selection);
680
+ if (!sourceSheet || !bounds || bounds.endRow <= bounds.startRow) return {
681
+ content,
682
+ error: '请选择包含标题行和至少一行数据的连续区域。'
683
+ };
684
+ if (spreadsheetPivotIntersects(sourceSheet, {
685
+ startRow: bounds.startRow,
686
+ endRow: bounds.endRow,
687
+ startColumn: bounds.startColumn,
688
+ endColumn: bounds.endColumn
689
+ }).length) return {
690
+ content,
691
+ error: '不能把现有数据透视表的输出区域作为新的透视表源。'
692
+ };
693
+ const sourceReference = formatSpreadsheetCellRanges([
694
+ {
695
+ row: [
696
+ bounds.startRow,
697
+ bounds.endRow
698
+ ],
699
+ column: [
700
+ bounds.startColumn,
701
+ bounds.endColumn
702
+ ]
703
+ }
704
+ ]);
705
+ const draftPivot = {
706
+ id: createWorkId('pivot'),
707
+ name: nextPivotName(content),
708
+ sourceSheetId,
709
+ sourceReference,
710
+ anchor: 'A1',
711
+ rowFields: [],
712
+ columnFields: [],
713
+ reportFilters: [],
714
+ values: [],
715
+ rowGrandTotals: true,
716
+ columnGrandTotals: true,
717
+ styleName: 'PivotStyleLight16',
718
+ refreshOnLoad: true
719
+ };
720
+ const fields = spreadsheetPivotFields(content, draftPivot);
721
+ if (!fields.length) return {
722
+ content,
723
+ error: '透视表源区域的标题必须非空且不重复。'
724
+ };
725
+ const valueField = fields.find((field)=>field.numeric) ?? fields.at(-1);
726
+ const dimensions = fields.filter((field)=>field.index !== valueField.index);
727
+ const rowField = dimensions[0] ?? fields.find((field)=>field.index !== valueField.index);
728
+ if (!rowField) return {
729
+ content,
730
+ error: '透视表至少需要一个分类字段和一个值字段。'
731
+ };
732
+ const columnField = dimensions.find((field)=>field.index !== rowField.index);
733
+ draftPivot.rowFields = [
734
+ rowField.index
735
+ ];
736
+ draftPivot.columnFields = columnField ? [
737
+ columnField.index
738
+ ] : [];
739
+ draftPivot.values = [
740
+ {
741
+ fieldIndex: valueField.index,
742
+ aggregation: valueField.numeric ? 'sum' : 'counta',
743
+ caption: defaultPivotValueCaption(valueField.name, valueField.numeric ? 'sum' : 'counta')
744
+ }
745
+ ];
746
+ const ownerSheetId = createWorkId('sheet');
747
+ const ownerSheet = {
748
+ id: ownerSheetId,
749
+ name: nextSheetName(content, '数据透视表'),
750
+ order: content.sheets.length,
751
+ status: 0,
752
+ row: 40,
753
+ column: 12,
754
+ data: Array.from({
755
+ length: 40
756
+ }, ()=>Array(12).fill(null)),
757
+ config: {},
758
+ pivotTables: [
759
+ draftPivot
760
+ ]
761
+ };
762
+ const withReport = {
763
+ ...content,
764
+ sheets: [
765
+ ...content.sheets,
766
+ ownerSheet
767
+ ]
768
+ };
769
+ const validation = spreadsheetPivotValidation(withReport, ownerSheetId, draftPivot);
770
+ if (!validation.valid) return {
771
+ content,
772
+ error: validation.message
773
+ };
774
+ const next = refreshSpreadsheetPivotTables(withReport);
775
+ return {
776
+ content: next,
777
+ ownerSheetId,
778
+ pivotId: draftPivot.id
779
+ };
780
+ }
781
+ function refreshSpreadsheetPivotTables(content) {
782
+ if (!spreadsheetPivotCount(content)) return content;
783
+ const sheets = content.sheets.map((sheet)=>({
784
+ ...sheet,
785
+ pivotTables: sheet.pivotTables?.map((pivot)=>({
786
+ ...pivot,
787
+ rowFields: [
788
+ ...pivot.rowFields
789
+ ],
790
+ columnFields: [
791
+ ...pivot.columnFields
792
+ ],
793
+ reportFilters: pivot.reportFilters?.map((filter)=>({
794
+ ...filter
795
+ })),
796
+ values: pivot.values.map((value)=>({
797
+ ...value
798
+ }))
799
+ }))
800
+ }));
801
+ const next = {
802
+ ...content,
803
+ sheets
804
+ };
805
+ for (const ownerSheet of sheets){
806
+ if (!ownerSheet.pivotTables?.length || !ownerSheet.id) continue;
807
+ let data = ownerSheet.data;
808
+ let dataChanged = false;
809
+ for (const pivot of ownerSheet.pivotTables){
810
+ const resolved = resolvePivot(next, ownerSheet.id, pivot);
811
+ if (resolved) {
812
+ if (!dataChanged) {
813
+ data = cloneCellMatrix(ownerSheet.data);
814
+ ownerSheet.data = data;
815
+ dataChanged = true;
816
+ }
817
+ clearRange(data, singleRange(pivot.outputReference ?? ''));
818
+ writePivotOutput(data, resolved.outputBounds, resolved.output);
819
+ pivot.outputReference = resolved.outputReference;
820
+ ownerSheet.row = Math.max(ownerSheet.row ?? 0, resolved.outputBounds.endRow + 1, data.length);
821
+ ownerSheet.column = Math.max(ownerSheet.column ?? 0, resolved.outputBounds.endColumn + 1);
822
+ }
823
+ }
824
+ }
825
+ return next;
826
+ }
827
+ function reconcileSpreadsheetPivots(content, changedSheets) {
828
+ const sourceById = new Map(content.sheets.flatMap((sheet)=>sheet.id ? [
829
+ [
830
+ sheet.id,
831
+ sheet
832
+ ]
833
+ ] : []));
834
+ const sheets = changedSheets.map((sheet)=>{
835
+ const source = sheet.id ? sourceById.get(sheet.id) : void 0;
836
+ return source?.pivotTables?.length ? {
837
+ ...sheet,
838
+ pivotTables: source.pivotTables
839
+ } : sheet;
840
+ });
841
+ return refreshSpreadsheetPivotTables({
842
+ ...content,
843
+ sheets
844
+ });
845
+ }
846
+ function deleteSpreadsheetPivotTable(content, ownerSheetId, pivotId) {
847
+ const sheets = content.sheets.map((sheet)=>{
848
+ if (sheet.id !== ownerSheetId) return sheet;
849
+ const pivot = sheet.pivotTables?.find((candidate)=>candidate.id === pivotId);
850
+ if (!pivot) return sheet;
851
+ const data = cloneCellMatrix(sheet.data);
852
+ clearRange(data, singleRange(pivot.outputReference ?? ''));
853
+ const pivotTables = (sheet.pivotTables ?? []).filter((candidate)=>candidate.id !== pivotId);
854
+ return {
855
+ ...sheet,
856
+ data,
857
+ pivotTables: pivotTables.length ? pivotTables : void 0
858
+ };
859
+ });
860
+ return {
861
+ ...content,
862
+ sheets
863
+ };
864
+ }
865
+ function spreadsheetPivotValidation(content, ownerSheetId, pivot) {
866
+ const failure = pivotFailure(content, ownerSheetId, pivot);
867
+ if (failure) return failure;
868
+ const resolved = resolvePivot(content, ownerSheetId, pivot);
869
+ return resolved ? {
870
+ valid: true,
871
+ outputReference: resolved.outputReference
872
+ } : {
873
+ valid: false,
874
+ code: 'pivot.invalid',
875
+ message: '无法根据当前字段和源数据生成透视表。'
876
+ };
877
+ }
878
+ function spreadsheetPivotOutputContains(sheet, row, column) {
879
+ return (sheet.pivotTables ?? []).some((pivot)=>{
880
+ const bounds = singleRange(pivot.outputReference ?? '');
881
+ return Boolean(bounds && containsCell(bounds, row, column));
882
+ });
883
+ }
884
+ function spreadsheetPivotIntersects(sheet, range) {
885
+ return (sheet.pivotTables ?? []).filter((pivot)=>{
886
+ const bounds = singleRange(pivot.outputReference ?? '');
887
+ return Boolean(bounds && rangesOverlap(bounds, range));
888
+ });
889
+ }
890
+ function resolvePivot(content, ownerSheetId, pivot) {
891
+ const failure = pivotFailure(content, ownerSheetId, pivot);
892
+ if (failure) return null;
893
+ const ownerSheet = content.sheets.find((sheet)=>sheet.id === ownerSheetId);
894
+ const sourceSheet = content.sheets.find((sheet)=>sheet.id === pivot.sourceSheetId);
895
+ const sourceBounds = singleRange(pivot.sourceReference);
896
+ const anchor = singleRange(pivot.anchor);
897
+ const fields = spreadsheetPivotFields(content, pivot);
898
+ const output = buildSpreadsheetPivotOutput(sourceSheet, sourceBounds, fields, pivot);
899
+ if (!output.length || !output[0]?.length) return null;
900
+ const outputBounds = {
901
+ startRow: anchor.startRow,
902
+ endRow: anchor.startRow + output.length - 1,
903
+ startColumn: anchor.startColumn,
904
+ endColumn: anchor.startColumn + output[0].length - 1
905
+ };
906
+ const outputReference = formatBounds(outputBounds);
907
+ if (output.length * output[0].length > MAXIMUM_OUTPUT_CELLS) return null;
908
+ if (outputBounds.endRow > MAXIMUM_XLSX_ROW || outputBounds.endColumn > MAXIMUM_XLSX_COLUMN) return null;
909
+ if (ownerSheet.id === sourceSheet.id && rangesOverlap(sourceBounds, outputBounds)) return null;
910
+ const oldOutput = singleRange(pivot.outputReference ?? '');
911
+ for (const other of ownerSheet.pivotTables ?? []){
912
+ if (other.id === pivot.id) continue;
913
+ const otherOutput = singleRange(other.outputReference ?? '');
914
+ if (otherOutput && rangesOverlap(otherOutput, outputBounds)) return null;
915
+ }
916
+ for(let row = outputBounds.startRow; row <= outputBounds.endRow; row += 1)for(let column = outputBounds.startColumn; column <= outputBounds.endColumn; column += 1)if (!(oldOutput && containsCell(oldOutput, row, column))) {
917
+ if (ownerSheet.data?.[row]?.[column]) return null;
918
+ }
919
+ return {
920
+ ownerSheet,
921
+ sourceSheet,
922
+ sourceBounds,
923
+ outputBounds,
924
+ outputReference,
925
+ fields,
926
+ output
927
+ };
928
+ }
929
+ function pivotFailure(content, ownerSheetId, pivot) {
930
+ const ownerSheet = content.sheets.find((sheet)=>sheet.id === ownerSheetId);
931
+ const sourceSheet = content.sheets.find((sheet)=>sheet.id === pivot.sourceSheetId);
932
+ if (!ownerSheet || !sourceSheet) return invalid('pivot.source-sheet-missing', '找不到透视表的源工作表或目标工作表。');
933
+ const sourceBounds = singleRange(pivot.sourceReference);
934
+ if (!sourceBounds || sourceBounds.endRow <= sourceBounds.startRow) return invalid('pivot.source-reference-invalid', '源区域必须是包含标题和数据的连续单元格范围。');
935
+ const sourceCellCount = (sourceBounds.endRow - sourceBounds.startRow + 1) * (sourceBounds.endColumn - sourceBounds.startColumn + 1);
936
+ if (sourceCellCount > MAXIMUM_SOURCE_CELLS) return invalid('pivot.source-too-large', `源区域不能超过 ${MAXIMUM_SOURCE_CELLS.toLocaleString()} 个单元格。`);
937
+ const anchor = singleRange(pivot.anchor);
938
+ if (!anchor || anchor.startRow !== anchor.endRow || anchor.startColumn !== anchor.endColumn) return invalid('pivot.anchor-invalid', '输出位置必须是一个 A1 单元格地址。');
939
+ const fields = spreadsheetPivotFields(content, pivot);
940
+ if (!fields.length) return invalid('pivot.headers-invalid', '源区域的标题必须非空且不重复。');
941
+ const reportFilters = pivot.reportFilters ?? [];
942
+ const reportFilterFields = reportFilters.map((filter)=>filter.fieldIndex);
943
+ if (new Set(reportFilterFields).size !== reportFilterFields.length || reportFilterFields.some((index)=>!fields[index]) || reportFilterFields.some((index)=>pivot.rowFields.includes(index) || pivot.columnFields.includes(index))) return invalid('pivot.filters-invalid', '报表筛选字段必须存在、互不重复,且不能同时作为行或列字段。');
944
+ for (const filter of reportFilters)if (void 0 !== filter.selectedItem && !spreadsheetPivotFilterItems(content, pivot, filter.fieldIndex).some((item)=>spreadsheetPivotFilterValueKey(item.value) === spreadsheetPivotFilterValueKey(filter.selectedItem))) return invalid('pivot.filter-item-invalid', '报表筛选器选择的项目已不在源数据中。');
945
+ const dimensionFields = [
946
+ ...pivot.rowFields,
947
+ ...pivot.columnFields,
948
+ ...reportFilterFields
949
+ ];
950
+ if (!pivot.rowFields.length) return invalid('pivot.rows-empty', '至少选择一个行字段。');
951
+ if (!pivot.values.length) return invalid('pivot.values-empty', '至少选择一个值字段。');
952
+ if (new Set(dimensionFields).size !== dimensionFields.length || dimensionFields.some((index)=>!fields[index]) || pivot.values.some((value)=>!fields[value.fieldIndex])) return invalid('pivot.fields-invalid', '透视表包含重复、缺失或超出源区域的字段。');
953
+ const output = buildSpreadsheetPivotOutput(sourceSheet, sourceBounds, fields, pivot);
954
+ if (!output.length || !output[0]?.length) return invalid('pivot.output-empty', '当前源数据不能生成透视表结果。');
955
+ if (output.length * output[0].length > MAXIMUM_OUTPUT_CELLS) return invalid('pivot.output-too-large', `透视表结果不能超过 ${MAXIMUM_OUTPUT_CELLS.toLocaleString()} 个单元格。`);
956
+ const outputBounds = {
957
+ startRow: anchor.startRow,
958
+ endRow: anchor.startRow + output.length - 1,
959
+ startColumn: anchor.startColumn,
960
+ endColumn: anchor.startColumn + output[0].length - 1
961
+ };
962
+ if (outputBounds.endRow > MAXIMUM_XLSX_ROW || outputBounds.endColumn > MAXIMUM_XLSX_COLUMN) return invalid('pivot.output-out-of-bounds', '透视表结果超出 XLSX 工作表边界。');
963
+ if (ownerSheet.id === sourceSheet.id && rangesOverlap(sourceBounds, outputBounds)) return invalid('pivot.output-overlaps-source', '透视表输出区域与源数据重叠,请改用其他位置或工作表。');
964
+ if (Object.values(ownerSheet.config?.merge ?? {}).some((merge)=>rangesOverlap(outputBounds, {
965
+ startRow: merge.r,
966
+ endRow: merge.r + merge.rs - 1,
967
+ startColumn: merge.c,
968
+ endColumn: merge.c + merge.cs - 1
969
+ }))) return invalid('pivot.output-overlaps-merge', '透视表输出区域与合并单元格重叠。');
970
+ const oldOutput = singleRange(pivot.outputReference ?? '');
971
+ for (const other of ownerSheet.pivotTables ?? []){
972
+ if (other.id === pivot.id) continue;
973
+ const otherOutput = singleRange(other.outputReference ?? '');
974
+ if (otherOutput && rangesOverlap(otherOutput, outputBounds)) return invalid('pivot.output-overlaps-pivot', '透视表输出区域与另一个透视表重叠。');
975
+ }
976
+ for(let row = outputBounds.startRow; row <= outputBounds.endRow; row += 1)for(let column = outputBounds.startColumn; column <= outputBounds.endColumn; column += 1)if (!(oldOutput && containsCell(oldOutput, row, column))) {
977
+ if (ownerSheet.data?.[row]?.[column]) return invalid('pivot.output-not-empty', '透视表输出区域包含现有内容,请选择空白位置。');
978
+ }
979
+ return null;
980
+ }
981
+ function uniqueFieldNames(fields) {
982
+ const names = fields.map((field)=>field.name.trim().toLocaleLowerCase());
983
+ return names.every(Boolean) && new Set(names).size === names.length;
984
+ }
985
+ function singleRange(reference) {
986
+ const ranges = parseSpreadsheetCellRanges(reference);
987
+ if (ranges?.length !== 1) return null;
988
+ return {
989
+ startRow: ranges[0].row[0],
990
+ endRow: ranges[0].row[1],
991
+ startColumn: ranges[0].column[0],
992
+ endColumn: ranges[0].column[1]
993
+ };
994
+ }
995
+ function selectionBounds(selection) {
996
+ if (selection.row.length < 2 || selection.column.length < 2) return null;
997
+ return {
998
+ startRow: Math.max(0, Math.min(selection.row[0], selection.row[1])),
999
+ endRow: Math.max(0, Math.max(selection.row[0], selection.row[1])),
1000
+ startColumn: Math.max(0, Math.min(selection.column[0], selection.column[1])),
1001
+ endColumn: Math.max(0, Math.max(selection.column[0], selection.column[1]))
1002
+ };
1003
+ }
1004
+ function containsCell(bounds, row, column) {
1005
+ return row >= bounds.startRow && row <= bounds.endRow && column >= bounds.startColumn && column <= bounds.endColumn;
1006
+ }
1007
+ function rangesOverlap(left, right) {
1008
+ return left.startRow <= right.endRow && left.endRow >= right.startRow && left.startColumn <= right.endColumn && left.endColumn >= right.startColumn;
1009
+ }
1010
+ function formatBounds(bounds) {
1011
+ return formatSpreadsheetCellRanges([
1012
+ {
1013
+ row: [
1014
+ bounds.startRow,
1015
+ bounds.endRow
1016
+ ],
1017
+ column: [
1018
+ bounds.startColumn,
1019
+ bounds.endColumn
1020
+ ]
1021
+ }
1022
+ ]);
1023
+ }
1024
+ function cloneCellMatrix(source) {
1025
+ return (source ?? []).map((row)=>[
1026
+ ...row
1027
+ ]);
1028
+ }
1029
+ function clearRange(data, bounds) {
1030
+ if (!bounds) return;
1031
+ 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;
1032
+ }
1033
+ function writePivotOutput(data, bounds, output) {
1034
+ const width = output[0]?.length ?? 0;
1035
+ while(data.length <= bounds.endRow)data.push([]);
1036
+ for(let row = 0; row < output.length; row += 1){
1037
+ const target = data[bounds.startRow + row] ?? [];
1038
+ data[bounds.startRow + row] = target;
1039
+ while(target.length < bounds.startColumn + width)target.push(null);
1040
+ for(let column = 0; column < width; column += 1)target[bounds.startColumn + column] = output[row][column] ?? null;
1041
+ }
1042
+ }
1043
+ function nextSheetName(content, base) {
1044
+ const existing = new Set(content.sheets.map((sheet)=>sheet.name.toLocaleLowerCase()));
1045
+ if (!existing.has(base.toLocaleLowerCase())) return base;
1046
+ let index = 2;
1047
+ while(existing.has(`${base} ${index}`.toLocaleLowerCase()))index += 1;
1048
+ return `${base} ${index}`;
1049
+ }
1050
+ function nextPivotName(content) {
1051
+ const existing = new Set(content.sheets.flatMap((sheet)=>(sheet.pivotTables ?? []).map((pivot)=>pivot.name.toLocaleLowerCase())));
1052
+ let index = 1;
1053
+ while(existing.has(`pivottable${index}`))index += 1;
1054
+ return `PivotTable${index}`;
1055
+ }
1056
+ function invalid(code, message) {
1057
+ return {
1058
+ valid: false,
1059
+ code,
1060
+ message
1061
+ };
1062
+ }
1063
+ class OoxmlPackage {
1064
+ zip;
1065
+ constructor(zip){
1066
+ this.zip = zip;
1067
+ }
1068
+ static async load(buffer) {
1069
+ return new OoxmlPackage(await jszip.loadAsync(buffer));
1070
+ }
1071
+ has(partPath) {
1072
+ return Boolean(this.zip.file(partPath));
1073
+ }
1074
+ paths(prefix) {
1075
+ return Object.keys(this.zip.files).filter((path)=>path.startsWith(prefix) && !this.zip.files[path]?.dir);
1076
+ }
1077
+ async text(partPath) {
1078
+ const entry = this.zip.file(partPath);
1079
+ if (!entry) throw new Error(`Office package part is missing: ${partPath}`);
1080
+ return entry.async('text');
1081
+ }
1082
+ async xml(partPath) {
1083
+ return parseXml(await this.text(partPath), partPath);
1084
+ }
1085
+ async bytes(partPath) {
1086
+ const entry = this.zip.file(partPath);
1087
+ if (!entry) throw new Error(`Office package part is missing: ${partPath}`);
1088
+ return entry.async('uint8array');
1089
+ }
1090
+ async relationships(sourcePart) {
1091
+ const partPath = relationshipsPartPath(sourcePart);
1092
+ if (!this.has(partPath)) return new Map();
1093
+ const document = await this.xml(partPath);
1094
+ return new Map(descendants(document, 'Relationship').map((element)=>{
1095
+ const relationship = {
1096
+ id: attribute(element, 'Id') ?? '',
1097
+ target: resolvePartTarget(sourcePart, attribute(element, 'Target') ?? ''),
1098
+ type: attribute(element, 'Type') ?? '',
1099
+ targetMode: attribute(element, 'TargetMode') ?? void 0
1100
+ };
1101
+ return [
1102
+ relationship.id,
1103
+ relationship
1104
+ ];
1105
+ }));
1106
+ }
1107
+ }
1108
+ function parseXml(source, label = 'Office XML') {
1109
+ const document = new DOMParser().parseFromString(source, 'application/xml');
1110
+ const error = descendants(document, 'parsererror')[0];
1111
+ if (error) throw new Error(`${label} is not valid XML: ${error.textContent?.trim() || 'parse error'}`);
1112
+ return document;
1113
+ }
1114
+ function attribute(element, name) {
1115
+ const direct = element.getAttribute(name);
1116
+ if (null !== direct) return direct;
1117
+ const localName = name.includes(':') ? name.slice(name.indexOf(':') + 1) : name;
1118
+ return Array.from(element.attributes).find((item)=>{
1119
+ const itemLocalName = item.localName.includes(':') ? item.localName.slice(item.localName.indexOf(':') + 1) : item.localName;
1120
+ return itemLocalName === localName && (!name.includes(':') || item.name === name);
1121
+ })?.value ?? null;
1122
+ }
1123
+ function xmlNamespacePrefix(element, namespace) {
1124
+ if (!namespace) return element.prefix;
1125
+ if ('function' == typeof element.lookupPrefix) {
1126
+ const prefix = element.lookupPrefix(namespace);
1127
+ if (prefix) return prefix;
1128
+ }
1129
+ let current = element;
1130
+ while(current){
1131
+ if (current.namespaceURI === namespace && current.prefix) return current.prefix;
1132
+ const declaration = Array.from(current.attributes).find((item)=>item.value === namespace && ('xmlns' === item.name || item.name.startsWith('xmlns:')));
1133
+ if (declaration?.name.startsWith('xmlns:')) return declaration.name.slice(6);
1134
+ current = current.parentElement;
1135
+ }
1136
+ return null;
1137
+ }
1138
+ function directChildren(parent, localName) {
1139
+ return Array.from(parent.children).filter((element)=>!localName || element.localName === localName);
1140
+ }
1141
+ function directChild(parent, localName) {
1142
+ return directChildren(parent, localName)[0];
1143
+ }
1144
+ function descendants(parent, localName) {
1145
+ return Array.from(parent.querySelectorAll('*')).filter((element)=>element.localName === localName);
1146
+ }
1147
+ function firstDescendant(parent, localName) {
1148
+ if (!parent) return;
1149
+ return descendants(parent, localName)[0];
1150
+ }
1151
+ function childPath(parent, ...localNames) {
1152
+ let current = parent;
1153
+ for (const name of localNames){
1154
+ if (!current) return;
1155
+ current = directChild(current, name);
1156
+ }
1157
+ return current instanceof Element ? current : void 0;
1158
+ }
1159
+ function resolvePartTarget(sourcePart, target) {
1160
+ if (/^[a-z][a-z0-9+.-]*:/i.test(target)) return target;
1161
+ const segments = target.startsWith('/') ? [] : sourcePart.split('/').slice(0, -1);
1162
+ for (const segment of target.replace(/^\/+/, '').split('/'))if (segment && '.' !== segment) if ('..' === segment) segments.pop();
1163
+ else segments.push(segment);
1164
+ return segments.join('/');
1165
+ }
1166
+ function contentTypeForPart(partPath) {
1167
+ const extension = partPath.split('.').pop()?.toLowerCase();
1168
+ const types = {
1169
+ apng: 'image/apng',
1170
+ bmp: 'image/bmp',
1171
+ emf: 'image/emf',
1172
+ gif: 'image/gif',
1173
+ jpeg: 'image/jpeg',
1174
+ jpg: 'image/jpeg',
1175
+ png: 'image/png',
1176
+ svg: 'image/svg+xml',
1177
+ tif: 'image/tiff',
1178
+ tiff: 'image/tiff',
1179
+ webp: 'image/webp',
1180
+ wmf: 'image/wmf'
1181
+ };
1182
+ return types[extension ?? ''] ?? 'application/octet-stream';
1183
+ }
1184
+ function bytesToDataUrl(bytes, contentType) {
1185
+ let binary = '';
1186
+ const chunkSize = 32768;
1187
+ for(let offset = 0; offset < bytes.length; offset += chunkSize)binary += String.fromCharCode(...bytes.subarray(offset, offset + chunkSize));
1188
+ return `data:${contentType};base64,${btoa(binary)}`;
1189
+ }
1190
+ function relationshipsPartPath(sourcePart) {
1191
+ const separator = sourcePart.lastIndexOf('/');
1192
+ const directory = separator >= 0 ? sourcePart.slice(0, separator + 1) : '';
1193
+ const fileName = separator >= 0 ? sourcePart.slice(separator + 1) : sourcePart;
1194
+ return `${directory}_rels/${fileName}.rels`;
1195
+ }
1196
+ const WORK_SPREADSHEET_CHART_COLORS = [
1197
+ '#4f6bed',
1198
+ '#e66c37',
1199
+ '#8f95a3',
1200
+ '#f4b41a',
1201
+ '#36a2ae',
1202
+ '#6a9f4e'
1203
+ ];
1204
+ function finiteChartNumber(value) {
1205
+ return Number.isFinite(value) ? value : 0;
1206
+ }
1207
+ function compactChartNumber(value) {
1208
+ const absolute = Math.abs(value);
1209
+ if (absolute >= 1000000) return `${roundChartNumber(value / 1000000)}M`;
1210
+ if (absolute >= 1000) return `${roundChartNumber(value / 1000)}K`;
1211
+ return String(roundChartNumber(value));
1212
+ }
1213
+ function truncateChartText(value, length) {
1214
+ const text = String(value);
1215
+ return text.length > length ? `${text.slice(0, length - 1)}…` : text;
1216
+ }
1217
+ function roundChartNumber(value) {
1218
+ return Math.round(100 * value) / 100;
1219
+ }
1220
+ function escapeChartXml(value) {
1221
+ return value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll("'", '&apos;');
1222
+ }
1223
+ function normalizeWorkSpreadsheetChartLegendPosition(value) {
1224
+ return 'left' === value || 'top' === value || 'bottom' === value || 'topRight' === value || 'right' === value ? value : 'right';
1225
+ }
1226
+ function normalizeWorkSpreadsheetChartLegendOverlay(value) {
1227
+ return true === value;
1228
+ }
1229
+ function workSpreadsheetChartSupportsGrouping(type) {
1230
+ return 'column' === type || 'bar' === type || 'line' === type || 'area' === type;
1231
+ }
1232
+ function workSpreadsheetChartSupportsBarSpacing(type) {
1233
+ return 'column' === type || 'bar' === type;
1234
+ }
1235
+ function workSpreadsheetChartSupportsSmoothLines(type) {
1236
+ return 'line' === type;
1237
+ }
1238
+ function workSpreadsheetChartDefaultGrouping(type) {
1239
+ return workSpreadsheetChartSupportsBarSpacing(type) ? 'clustered' : 'standard';
1240
+ }
1241
+ function normalizeWorkSpreadsheetChartGrouping(value, type) {
1242
+ const fallback = workSpreadsheetChartDefaultGrouping(type);
1243
+ if (!workSpreadsheetChartSupportsGrouping(type)) return fallback;
1244
+ if ('stacked' === value || 'percentStacked' === value) return value;
1245
+ if (workSpreadsheetChartSupportsBarSpacing(type)) return 'standard' === value || 'clustered' === value ? value : fallback;
1246
+ return 'standard' === value ? value : fallback;
1247
+ }
1248
+ function workSpreadsheetChartGroupingIsStacked(grouping) {
1249
+ return 'stacked' === grouping || 'percentStacked' === grouping;
1250
+ }
1251
+ function normalizeWorkSpreadsheetChartGapWidth(value) {
1252
+ return work_spreadsheet_chart_layout_normalizedInteger(value, 0, 500, 150);
1253
+ }
1254
+ function normalizeWorkSpreadsheetChartOverlap(value, grouping) {
1255
+ return work_spreadsheet_chart_layout_normalizedInteger(value, -100, 100, workSpreadsheetChartGroupingIsStacked(grouping) ? 100 : 0);
1256
+ }
1257
+ function normalizeWorkSpreadsheetChartSmoothLines(value) {
1258
+ return true === value;
1259
+ }
1260
+ function normalizeWorkSpreadsheetChartLayout(source) {
1261
+ const grouping = normalizeWorkSpreadsheetChartGrouping(source.grouping, source.type);
1262
+ return {
1263
+ legendPosition: normalizeWorkSpreadsheetChartLegendPosition(source.legendPosition),
1264
+ legendOverlay: normalizeWorkSpreadsheetChartLegendOverlay(source.legendOverlay),
1265
+ grouping: workSpreadsheetChartSupportsGrouping(source.type) ? grouping : void 0,
1266
+ gapWidth: workSpreadsheetChartSupportsBarSpacing(source.type) ? normalizeWorkSpreadsheetChartGapWidth(source.gapWidth) : void 0,
1267
+ overlap: workSpreadsheetChartSupportsBarSpacing(source.type) ? normalizeWorkSpreadsheetChartOverlap(source.overlap, grouping) : void 0,
1268
+ smoothLines: workSpreadsheetChartSupportsSmoothLines(source.type) ? normalizeWorkSpreadsheetChartSmoothLines(source.smoothLines) : void 0
1269
+ };
1270
+ }
1271
+ function workSpreadsheetChartSupportsSeriesAnalysis(source) {
1272
+ if (!workSpreadsheetChartSupportsGrouping(source.type)) return true;
1273
+ return !workSpreadsheetChartGroupingIsStacked(normalizeWorkSpreadsheetChartGrouping(source.grouping, source.type));
1274
+ }
1275
+ function workSpreadsheetChartLegendPositionLabel(position) {
1276
+ if ('left' === position) return '左侧';
1277
+ if ('top' === position) return '顶部';
1278
+ if ('bottom' === position) return '底部';
1279
+ if ('topRight' === position) return '右上角';
1280
+ return '右侧';
1281
+ }
1282
+ function workSpreadsheetChartGroupingLabel(grouping) {
1283
+ if ('stacked' === grouping) return '堆积';
1284
+ if ('percentStacked' === grouping) return '百分比堆积';
1285
+ if ('standard' === grouping) return '标准';
1286
+ return '簇状';
1287
+ }
1288
+ function work_spreadsheet_chart_layout_normalizedInteger(value, minimum, maximum, fallback) {
1289
+ if (null == value || '' === value) return fallback;
1290
+ const number = Number(value);
1291
+ if (!Number.isFinite(number)) return fallback;
1292
+ return Math.min(maximum, Math.max(minimum, Math.round(number)));
1293
+ }
1294
+ const LINE_DASHES = new Set([
1295
+ 'solid',
1296
+ 'dash',
1297
+ 'dot',
1298
+ 'dashDot'
1299
+ ]);
1300
+ const MARKER_SYMBOLS = new Set([
1301
+ 'none',
1302
+ 'circle',
1303
+ 'square',
1304
+ 'diamond',
1305
+ 'triangle',
1306
+ 'plus',
1307
+ 'x',
1308
+ 'star'
1309
+ ]);
1310
+ function normalizeWorkSpreadsheetChartSeriesStyle(source) {
1311
+ if (!source || 'object' != typeof source) return;
1312
+ const fillColor = normalizeWorkSpreadsheetChartColor(source.fillColor);
1313
+ const fillTransparency = optionalBoundedNumber(source.fillTransparency, 0, 100, 0);
1314
+ const lineColor = normalizeWorkSpreadsheetChartColor(source.lineColor);
1315
+ const lineWidth = optionalBoundedNumber(source.lineWidth, 0.25, 20, 2.25);
1316
+ const lineDash = LINE_DASHES.has(source.lineDash) ? source.lineDash : void 0;
1317
+ const marker = normalizeWorkSpreadsheetChartMarkerStyle(source.marker);
1318
+ const normalized = {
1319
+ ...fillColor ? {
1320
+ fillColor
1321
+ } : {},
1322
+ ...void 0 !== fillTransparency ? {
1323
+ fillTransparency
1324
+ } : {},
1325
+ ...lineColor ? {
1326
+ lineColor
1327
+ } : {},
1328
+ ...void 0 !== lineWidth ? {
1329
+ lineWidth
1330
+ } : {},
1331
+ ...lineDash ? {
1332
+ lineDash
1333
+ } : {},
1334
+ ...marker ? {
1335
+ marker
1336
+ } : {}
1337
+ };
1338
+ return Object.keys(normalized).length ? normalized : void 0;
1339
+ }
1340
+ function normalizeWorkSpreadsheetChartColor(value) {
1341
+ if ('string' != typeof value) return;
1342
+ const text = value.trim().replace(/^#/, '');
1343
+ if (/^[0-9a-f]{3}$/i.test(text)) return `#${text.split('').map((character)=>character.repeat(2)).join('')}`.toUpperCase();
1344
+ return /^[0-9a-f]{6}$/i.test(text) ? `#${text.toUpperCase()}` : void 0;
1345
+ }
1346
+ function defaultWorkSpreadsheetChartSeriesStyle(seriesIndex) {
1347
+ const color = workSpreadsheetChartSeriesDefaultColor(seriesIndex);
1348
+ return {
1349
+ fillColor: color,
1350
+ fillTransparency: 0,
1351
+ lineColor: color,
1352
+ lineWidth: 2.5,
1353
+ lineDash: 'solid',
1354
+ marker: {
1355
+ symbol: 'circle',
1356
+ size: 5,
1357
+ fillColor: '#FFFFFF',
1358
+ lineColor: color
1359
+ }
1360
+ };
1361
+ }
1362
+ function workSpreadsheetChartSeriesDefaultColor(seriesIndex) {
1363
+ return WORK_SPREADSHEET_CHART_COLORS[seriesIndex % WORK_SPREADSHEET_CHART_COLORS.length].toUpperCase();
1364
+ }
1365
+ function spreadsheetChartSeriesFillStyle(series, seriesIndex, defaultOpacity = 1, defaultColor = workSpreadsheetChartSeriesDefaultColor(seriesIndex)) {
1366
+ const style = normalizeWorkSpreadsheetChartSeriesStyle(series.style);
1367
+ const color = style?.fillColor ?? defaultColor;
1368
+ const opacity = style?.fillTransparency === void 0 ? boundedNumber(defaultOpacity, 0, 1, 1) : 1 - style.fillTransparency / 100;
1369
+ return {
1370
+ color,
1371
+ opacity,
1372
+ attributes: `fill="${escapeChartXml(color)}"${opacity < 1 ? ` fill-opacity="${compactDecimal(opacity)}"` : ''}`
1373
+ };
1374
+ }
1375
+ function spreadsheetChartSeriesLineStyle(series, seriesIndex, defaultWidth, defaultColor = workSpreadsheetChartSeriesDefaultColor(seriesIndex)) {
1376
+ const style = normalizeWorkSpreadsheetChartSeriesStyle(series.style);
1377
+ const hasCustomLine = Boolean(style?.lineColor || style?.lineWidth !== void 0 || style?.lineDash);
1378
+ const color = style?.lineColor ?? defaultColor;
1379
+ const width = style?.lineWidth ?? (hasCustomLine && defaultWidth <= 0 ? 1 : defaultWidth);
1380
+ const dash = style?.lineDash ?? 'solid';
1381
+ const dashArray = spreadsheetChartLineDashArray(dash);
1382
+ return {
1383
+ color,
1384
+ width,
1385
+ dash,
1386
+ attributes: !hasCustomLine && defaultWidth <= 0 ? 'stroke="none"' : `stroke="${escapeChartXml(color)}" stroke-width="${compactDecimal(width)}"${dashArray ? ` stroke-dasharray="${dashArray}"` : ''}`
1387
+ };
1388
+ }
1389
+ function spreadsheetChartSeriesLegendColor(series, seriesIndex) {
1390
+ const style = normalizeWorkSpreadsheetChartSeriesStyle(series.style);
1391
+ return style?.fillColor ?? style?.lineColor ?? workSpreadsheetChartSeriesDefaultColor(seriesIndex);
1392
+ }
1393
+ function spreadsheetChartSeriesMarkerSvg(series, seriesIndex, x, y, options) {
1394
+ const style = normalizeWorkSpreadsheetChartSeriesStyle(series.style);
1395
+ const marker = style?.marker;
1396
+ const visible = marker ? 'none' !== marker.symbol : options.visible;
1397
+ if (!visible) return '';
1398
+ const symbol = marker?.symbol && 'none' !== marker.symbol ? marker.symbol : 'circle';
1399
+ const size = marker?.size ?? options.defaultSize ?? 5;
1400
+ const radius = Math.max(1, 0.6 * size);
1401
+ const line = spreadsheetChartSeriesLineStyle(series, seriesIndex, 2);
1402
+ const fillColor = marker?.fillColor ?? '#FFFFFF';
1403
+ const lineColor = marker?.lineColor ?? line.color;
1404
+ const common = `data-marker-symbol="${symbol}" data-marker-size="${compactDecimal(size)}"${options.attributes ? ` ${options.attributes}` : ''}`;
1405
+ const paint = `fill="${escapeChartXml(fillColor)}" stroke="${escapeChartXml(lineColor)}" stroke-width="2"`;
1406
+ const pointX = roundChartNumber(x);
1407
+ const pointY = roundChartNumber(y);
1408
+ if ('square' === symbol) {
1409
+ const side = 1.7 * radius;
1410
+ return `<rect ${common} x="${roundChartNumber(x - side / 2)}" y="${roundChartNumber(y - side / 2)}" width="${roundChartNumber(side)}" height="${roundChartNumber(side)}" rx="0.6" ${paint}/>`;
1411
+ }
1412
+ if ('diamond' === symbol) return `<path ${common} d="M ${pointX} ${roundChartNumber(y - radius)} L ${roundChartNumber(x + radius)} ${pointY} L ${pointX} ${roundChartNumber(y + radius)} L ${roundChartNumber(x - radius)} ${pointY} Z" ${paint}/>`;
1413
+ if ('triangle' === symbol) return `<path ${common} d="M ${pointX} ${roundChartNumber(y - radius)} L ${roundChartNumber(x + 0.9 * radius)} ${roundChartNumber(y + 0.75 * radius)} L ${roundChartNumber(x - 0.9 * radius)} ${roundChartNumber(y + 0.75 * radius)} Z" ${paint}/>`;
1414
+ if ('plus' === symbol || 'x' === symbol) {
1415
+ const paths = 'plus' === symbol ? `M ${roundChartNumber(x - radius)} ${pointY} L ${roundChartNumber(x + radius)} ${pointY} M ${pointX} ${roundChartNumber(y - radius)} L ${pointX} ${roundChartNumber(y + radius)}` : `M ${roundChartNumber(x - radius)} ${roundChartNumber(y - radius)} L ${roundChartNumber(x + radius)} ${roundChartNumber(y + radius)} M ${roundChartNumber(x + radius)} ${roundChartNumber(y - radius)} L ${roundChartNumber(x - radius)} ${roundChartNumber(y + radius)}`;
1416
+ return `<path ${common} d="${paths}" fill="none" stroke="${escapeChartXml(lineColor)}" stroke-width="2" stroke-linecap="round"/>`;
1417
+ }
1418
+ if ('star' === symbol) {
1419
+ const points = Array.from({
1420
+ length: 10
1421
+ }, (_, index)=>{
1422
+ const angle = -Math.PI / 2 + index * Math.PI / 5;
1423
+ const pointRadius = index % 2 ? 0.42 * radius : radius;
1424
+ return `${roundChartNumber(x + Math.cos(angle) * pointRadius)},${roundChartNumber(y + Math.sin(angle) * pointRadius)}`;
1425
+ }).join(' ');
1426
+ return `<polygon ${common} points="${points}" ${paint}/>`;
1427
+ }
1428
+ return `<circle ${common} cx="${pointX}" cy="${pointY}" r="${roundChartNumber(radius)}" ${paint}/>`;
1429
+ }
1430
+ function spreadsheetChartSeriesStyleContext(style) {
1431
+ const normalized = normalizeWorkSpreadsheetChartSeriesStyle(style);
1432
+ if (!normalized) return '';
1433
+ const descriptions = [];
1434
+ if (normalized.fillColor || void 0 !== normalized.fillTransparency) descriptions.push(`填充 ${normalized.fillColor ?? '默认颜色'}${void 0 === normalized.fillTransparency ? '' : `(透明度 ${compactDecimal(normalized.fillTransparency)}%)`}`);
1435
+ if (normalized.lineColor || void 0 !== normalized.lineWidth || normalized.lineDash) descriptions.push([
1436
+ `线条 ${normalized.lineColor ?? '默认颜色'}`,
1437
+ void 0 === normalized.lineWidth ? '' : `${compactDecimal(normalized.lineWidth)} 磅`,
1438
+ normalized.lineDash ? workSpreadsheetChartLineDashLabel(normalized.lineDash) : ''
1439
+ ].filter(Boolean).join('、'));
1440
+ if (normalized.marker) {
1441
+ const marker = normalized.marker;
1442
+ descriptions.push([
1443
+ `数据标记 ${workSpreadsheetChartMarkerSymbolLabel(marker.symbol ?? 'circle')}`,
1444
+ void 0 === marker.size ? '' : `${compactDecimal(marker.size)} 磅`,
1445
+ marker.fillColor ? `填充 ${marker.fillColor}` : '',
1446
+ marker.lineColor ? `轮廓 ${marker.lineColor}` : ''
1447
+ ].filter(Boolean).join('、'));
1448
+ }
1449
+ return descriptions.length ? `;系列外观:${descriptions.join(',')}` : '';
1450
+ }
1451
+ function workSpreadsheetChartLineDashLabel(dash) {
1452
+ if ('dash' === dash) return '虚线';
1453
+ if ('dot' === dash) return '点线';
1454
+ if ('dashDot' === dash) return '点划线';
1455
+ return '实线';
1456
+ }
1457
+ function workSpreadsheetChartMarkerSymbolLabel(symbol) {
1458
+ if ('none' === symbol) return '无';
1459
+ if ('square' === symbol) return '方形';
1460
+ if ('diamond' === symbol) return '菱形';
1461
+ if ('triangle' === symbol) return '三角形';
1462
+ if ('plus' === symbol) return '加号';
1463
+ if ('x' === symbol) return '叉号';
1464
+ if ('star' === symbol) return '星形';
1465
+ return '圆形';
1466
+ }
1467
+ function normalizeWorkSpreadsheetChartMarkerStyle(value) {
1468
+ if (!value || 'object' != typeof value) return;
1469
+ const source = value;
1470
+ const symbol = MARKER_SYMBOLS.has(source.symbol) ? source.symbol : void 0;
1471
+ const size = optionalBoundedNumber(source.size, 2, 72, 5);
1472
+ const fillColor = normalizeWorkSpreadsheetChartColor(source.fillColor);
1473
+ const lineColor = normalizeWorkSpreadsheetChartColor(source.lineColor);
1474
+ const marker = {
1475
+ ...symbol ? {
1476
+ symbol
1477
+ } : {},
1478
+ ...void 0 !== size ? {
1479
+ size
1480
+ } : {},
1481
+ ...fillColor ? {
1482
+ fillColor
1483
+ } : {},
1484
+ ...lineColor ? {
1485
+ lineColor
1486
+ } : {}
1487
+ };
1488
+ return Object.keys(marker).length ? marker : void 0;
1489
+ }
1490
+ function spreadsheetChartLineDashArray(dash) {
1491
+ if ('dash' === dash) return '8 4';
1492
+ if ('dot' === dash) return '2 3';
1493
+ if ('dashDot' === dash) return '8 4 2 4';
1494
+ return '';
1495
+ }
1496
+ function optionalBoundedNumber(value, minimum, maximum, fallback) {
1497
+ if (null == value || '' === value) return;
1498
+ const number = Number(value);
1499
+ return boundedNumber(number, minimum, maximum, fallback);
1500
+ }
1501
+ function boundedNumber(value, minimum, maximum, fallback) {
1502
+ return Math.min(maximum, Math.max(minimum, Number.isFinite(value) ? value : fallback));
1503
+ }
1504
+ function compactDecimal(value) {
1505
+ return String(Math.round(100 * value) / 100);
1506
+ }
1507
+ const VERTICAL_RESERVE = 118;
1508
+ const HORIZONTAL_RESERVE = 40;
1509
+ function spreadsheetChartLegendLayout(chart, sourcePlot, frame) {
1510
+ if (!chart.showLegend) return {
1511
+ plot: sourcePlot,
1512
+ svg: ''
1513
+ };
1514
+ const position = normalizeWorkSpreadsheetChartLegendPosition(chart.legendPosition);
1515
+ const overlay = normalizeWorkSpreadsheetChartLegendOverlay(chart.legendOverlay);
1516
+ const plot = reservedPlot(sourcePlot, position, overlay);
1517
+ const bounds = legendBounds(sourcePlot, plot, frame, position, overlay);
1518
+ const items = spreadsheetChartLegendItems(chart).slice(0, 8);
1519
+ const entries = 'top' === position || 'bottom' === position ? horizontalLegendEntries(items, bounds) : verticalLegendEntries(items, bounds);
1520
+ return {
1521
+ plot,
1522
+ svg: `<g data-chart-legend-position="${position}" data-chart-legend-overlay="${overlay}" aria-label="图例">${entries}</g>`
1523
+ };
1524
+ }
1525
+ function reservedPlot(source, position, overlay) {
1526
+ if (overlay) return source;
1527
+ if ('left' === position) return {
1528
+ ...source,
1529
+ x: source.x + VERTICAL_RESERVE,
1530
+ width: Math.max(40, source.width - VERTICAL_RESERVE)
1531
+ };
1532
+ if ('right' === position || 'topRight' === position) return {
1533
+ ...source,
1534
+ width: Math.max(40, source.width - VERTICAL_RESERVE)
1535
+ };
1536
+ if ('top' === position) return {
1537
+ ...source,
1538
+ y: source.y + HORIZONTAL_RESERVE,
1539
+ height: Math.max(40, source.height - HORIZONTAL_RESERVE)
1540
+ };
1541
+ return {
1542
+ ...source,
1543
+ height: Math.max(40, source.height - HORIZONTAL_RESERVE)
1544
+ };
1545
+ }
1546
+ function legendBounds(source, plot, frame, position, overlay) {
1547
+ if ('left' === position) return {
1548
+ x: 12,
1549
+ y: frame.titleHeight + 18,
1550
+ width: VERTICAL_RESERVE - 18,
1551
+ height: frame.height - frame.titleHeight - 32
1552
+ };
1553
+ if ('right' === position || 'topRight' === position) return {
1554
+ x: overlay ? source.x + source.width - VERTICAL_RESERVE + 12 : plot.x + plot.width + 12,
1555
+ y: 'topRight' === position ? frame.titleHeight + 4 : frame.titleHeight + 18,
1556
+ width: VERTICAL_RESERVE - 18,
1557
+ height: frame.height - frame.titleHeight - 32
1558
+ };
1559
+ if ('top' === position) return {
1560
+ x: plot.x,
1561
+ y: frame.titleHeight + 4,
1562
+ width: plot.width,
1563
+ height: HORIZONTAL_RESERVE - 4
1564
+ };
1565
+ return {
1566
+ x: plot.x,
1567
+ y: frame.height - HORIZONTAL_RESERVE + 6,
1568
+ width: plot.width,
1569
+ height: HORIZONTAL_RESERVE - 8
1570
+ };
1571
+ }
1572
+ function spreadsheetChartLegendItems(chart) {
1573
+ if ('pie' === chart.type || 'doughnut' === chart.type) {
1574
+ const values = chart.series[0]?.values ?? [];
1575
+ const seriesColor = normalizeWorkSpreadsheetChartSeriesStyle(chart.series[0]?.style)?.fillColor;
1576
+ return chart.categories.map((category, index)=>({
1577
+ color: seriesColor ?? WORK_SPREADSHEET_CHART_COLORS[index % WORK_SPREADSHEET_CHART_COLORS.length],
1578
+ label: `${category}${void 0 === values[index] ? '' : ` ${compactChartNumber(values[index])}`}`
1579
+ }));
1580
+ }
1581
+ return chart.series.map((series, index)=>({
1582
+ color: spreadsheetChartSeriesLegendColor(series, index),
1583
+ label: series.name || `系列 ${index + 1}`
1584
+ }));
1585
+ }
1586
+ function verticalLegendEntries(items, bounds) {
1587
+ return items.map((item, index)=>legendEntrySvg(item, index, bounds.x, bounds.y + 21 * index, 16)).join('');
1588
+ }
1589
+ function horizontalLegendEntries(items, bounds) {
1590
+ if (!items.length) return '';
1591
+ const columns = Math.max(1, Math.min(items.length, Math.floor(bounds.width / 96)));
1592
+ const itemWidth = bounds.width / columns;
1593
+ return items.map((item, index)=>{
1594
+ const column = index % columns;
1595
+ const row = Math.floor(index / columns);
1596
+ return legendEntrySvg(item, index, bounds.x + column * itemWidth, bounds.y + 17 * row + 9, Math.max(8, Math.floor(itemWidth / 7)));
1597
+ }).join('');
1598
+ }
1599
+ function legendEntrySvg(item, index, x, baselineY, maximumCharacters) {
1600
+ return [
1601
+ `<rect data-chart-legend-entry="${index}" x="${roundChartNumber(x)}" y="${roundChartNumber(baselineY - 9)}" width="10" height="10" rx="2" fill="${item.color}"/>`,
1602
+ `<text x="${roundChartNumber(x + 16)}" y="${roundChartNumber(baselineY)}" fill="#536078" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="10">${escapeChartXml(truncateChartText(item.label, maximumCharacters))}</text>`
1603
+ ].join('');
1604
+ }
1605
+ function spreadsheetChartSeriesLayout(chart) {
1606
+ const grouping = normalizeWorkSpreadsheetChartGrouping(chart.grouping, chart.type);
1607
+ const stacked = workSpreadsheetChartGroupingIsStacked(grouping);
1608
+ const categoryCount = Math.max(1, chart.categories.length, ...chart.series.map((series)=>series.values.length));
1609
+ const result = chart.series.map(()=>[]);
1610
+ for(let categoryIndex = 0; categoryIndex < categoryCount; categoryIndex += 1){
1611
+ const rawValues = chart.series.map((series)=>finiteChartNumber(series.values[categoryIndex] ?? 0));
1612
+ const positiveTotal = rawValues.reduce((sum, value)=>sum + Math.max(0, value), 0);
1613
+ const negativeTotal = rawValues.reduce((sum, value)=>sum + Math.abs(Math.min(0, value)), 0);
1614
+ let positive = 0;
1615
+ let negative = 0;
1616
+ rawValues.forEach((rawValue, seriesIndex)=>{
1617
+ const value = 'percentStacked' === grouping ? rawValue >= 0 ? positiveTotal ? rawValue / positiveTotal : 0 : negativeTotal ? rawValue / negativeTotal : 0 : rawValue;
1618
+ const start = stacked ? value >= 0 ? positive : negative : 0;
1619
+ const end = start + value;
1620
+ if (stacked) if (value >= 0) positive = end;
1621
+ else negative = end;
1622
+ if (categoryIndex < (chart.series[seriesIndex]?.values.length ?? 0)) result[seriesIndex].push({
1623
+ categoryIndex,
1624
+ rawValue,
1625
+ start,
1626
+ end
1627
+ });
1628
+ });
1629
+ }
1630
+ const scaleValues = result.flatMap((series)=>series.flatMap((point)=>[
1631
+ point.start,
1632
+ point.end
1633
+ ]));
1634
+ return {
1635
+ grouping,
1636
+ stacked,
1637
+ categoryCount,
1638
+ series: result,
1639
+ scaleValues
1640
+ };
1641
+ }
1642
+ function spreadsheetChartBarGeometry(groupSize, seriesCount, chart, maximumBarSize) {
1643
+ const grouping = normalizeWorkSpreadsheetChartGrouping(chart.grouping, chart.type);
1644
+ const gapWidth = normalizeWorkSpreadsheetChartGapWidth(chart.gapWidth);
1645
+ const overlap = normalizeWorkSpreadsheetChartOverlap(chart.overlap, grouping);
1646
+ const count = Math.max(1, seriesCount);
1647
+ const clusterSize = Math.max(1, 100 * groupSize / (100 + gapWidth));
1648
+ const stepRatio = 1 - overlap / 100;
1649
+ const idealBarSize = clusterSize / Math.max(1, 1 + (count - 1) * stepRatio);
1650
+ const barSize = Math.max(1, Math.min(maximumBarSize, idealBarSize));
1651
+ const step = barSize * stepRatio;
1652
+ const totalSize = barSize + step * (count - 1);
1653
+ const start = (groupSize - totalSize) / 2;
1654
+ return {
1655
+ barSize,
1656
+ renderedSize: Math.max(0.75, barSize - Math.min(1.5, 0.08 * barSize)),
1657
+ offset: (seriesIndex)=>start + seriesIndex * step
1658
+ };
1659
+ }
1660
+ function spreadsheetDataLabelSvg(chart, seriesIndex, pointIndex, geometry) {
1661
+ const series = chart.series[seriesIndex];
1662
+ if (!series?.dataLabels) return '';
1663
+ const labels = normalizeWorkSpreadsheetDataLabels(series.dataLabels, chart.type);
1664
+ const text = spreadsheetDataLabelText(chart, seriesIndex, pointIndex);
1665
+ if (!text) return '';
1666
+ const position = normalizeWorkSpreadsheetDataLabelPosition(labels.position);
1667
+ const placed = placeDataLabel(geometry, position);
1668
+ const visualText = truncateChartText(text.replaceAll(/\r?\n/g, ' '), 32);
1669
+ return `<text data-data-label-series="${seriesIndex}:${pointIndex}" data-data-label-position="${position}" data-data-label-text="${escapeChartXml(text)}" x="${roundChartNumber(placed.x)}" y="${roundChartNumber(placed.y)}" fill="#27344d" stroke="#ffffff" stroke-width="3" stroke-linejoin="round" paint-order="stroke" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="9" font-weight="500" text-anchor="${placed.anchor}">${escapeChartXml(visualText)}</text>`;
1670
+ }
1671
+ function spreadsheetDataLabelText(chart, seriesIndex, pointIndex) {
1672
+ const series = chart.series[seriesIndex];
1673
+ if (!series?.dataLabels) return '';
1674
+ const labels = normalizeWorkSpreadsheetDataLabels(series.dataLabels, chart.type);
1675
+ const parts = [];
1676
+ if (labels.showSeriesName) parts.push(series.name || `系列 ${seriesIndex + 1}`);
1677
+ if (labels.showCategoryName) {
1678
+ const category = 'scatter' === chart.type || 'bubble' === chart.type ? compactChartNumber(series.xValues?.[pointIndex] ?? pointIndex + 1) : chart.categories[pointIndex] ?? String(pointIndex + 1);
1679
+ parts.push(category);
1680
+ }
1681
+ if (labels.showValue) parts.push(compactChartNumber(series.values[pointIndex]));
1682
+ if (labels.showPercentage) {
1683
+ const values = series.values.map((value)=>Math.max(0, finiteChartNumber(value)));
1684
+ const total = values.reduce((sum, value)=>sum + value, 0);
1685
+ if (total > 0) parts.push(`${compactChartNumber(Math.round(values[pointIndex] / total * 1000) / 10)}%`);
1686
+ }
1687
+ if (labels.showBubbleSize) parts.push(compactChartNumber(series.bubbleSizes?.[pointIndex] ?? 0));
1688
+ return parts.join(labels.separator ?? ', ');
1689
+ }
1690
+ function placeDataLabel(geometry, position) {
1691
+ if ('verticalBar' === geometry.kind) return placeVerticalBarLabel(geometry, position);
1692
+ if ('horizontalBar' === geometry.kind) return placeHorizontalBarLabel(geometry, position);
1693
+ if ('circular' === geometry.kind) return placeCircularLabel(geometry, position);
1694
+ if ('left' === position) return {
1695
+ x: geometry.x - 6,
1696
+ y: geometry.y + 3,
1697
+ anchor: 'end'
1698
+ };
1699
+ if ('right' === position) return {
1700
+ x: geometry.x + 6,
1701
+ y: geometry.y + 3,
1702
+ anchor: 'start'
1703
+ };
1704
+ if ('below' === position) return {
1705
+ x: geometry.x,
1706
+ y: geometry.y + 13,
1707
+ anchor: 'middle'
1708
+ };
1709
+ if ('center' === position || 'insideBase' === position || 'insideEnd' === position) return {
1710
+ x: geometry.x,
1711
+ y: geometry.y + 3,
1712
+ anchor: 'middle'
1713
+ };
1714
+ return {
1715
+ x: geometry.x,
1716
+ y: geometry.y - 7,
1717
+ anchor: 'middle'
1718
+ };
1719
+ }
1720
+ function placeVerticalBarLabel(geometry, position) {
1721
+ const positive = geometry.value >= 0;
1722
+ if ('left' === position) return {
1723
+ x: geometry.x - 5,
1724
+ y: geometry.valueY + 3,
1725
+ anchor: 'end'
1726
+ };
1727
+ if ('right' === position) return {
1728
+ x: geometry.x + 5,
1729
+ y: geometry.valueY + 3,
1730
+ anchor: 'start'
1731
+ };
1732
+ if ('center' === position) return {
1733
+ x: geometry.x,
1734
+ y: (geometry.valueY + geometry.baselineY) / 2 + 3,
1735
+ anchor: 'middle'
1736
+ };
1737
+ if ('insideBase' === position) return {
1738
+ x: geometry.x,
1739
+ y: geometry.baselineY + (positive ? -5 : 12),
1740
+ anchor: 'middle'
1741
+ };
1742
+ if ('insideEnd' === position) return {
1743
+ x: geometry.x,
1744
+ y: geometry.valueY + (positive ? 11 : -5),
1745
+ anchor: 'middle'
1746
+ };
1747
+ if ('below' === position) return {
1748
+ x: geometry.x,
1749
+ y: geometry.valueY + 13,
1750
+ anchor: 'middle'
1751
+ };
1752
+ return {
1753
+ x: geometry.x,
1754
+ y: geometry.valueY + (positive ? -6 : 13),
1755
+ anchor: 'middle'
1756
+ };
1757
+ }
1758
+ function placeHorizontalBarLabel(geometry, position) {
1759
+ const positive = geometry.value >= 0;
1760
+ if ('above' === position) return {
1761
+ x: geometry.valueX,
1762
+ y: geometry.y - 5,
1763
+ anchor: 'middle'
1764
+ };
1765
+ if ('below' === position) return {
1766
+ x: geometry.valueX,
1767
+ y: geometry.y + 12,
1768
+ anchor: 'middle'
1769
+ };
1770
+ if ('center' === position) return {
1771
+ x: (geometry.valueX + geometry.baselineX) / 2,
1772
+ y: geometry.y + 3,
1773
+ anchor: 'middle'
1774
+ };
1775
+ if ('insideBase' === position) return {
1776
+ x: geometry.baselineX + (positive ? 5 : -5),
1777
+ y: geometry.y + 3,
1778
+ anchor: positive ? 'start' : 'end'
1779
+ };
1780
+ if ('insideEnd' === position) return {
1781
+ x: geometry.valueX + (positive ? -5 : 5),
1782
+ y: geometry.y + 3,
1783
+ anchor: positive ? 'end' : 'start'
1784
+ };
1785
+ if ('left' === position) return {
1786
+ x: geometry.valueX - 6,
1787
+ y: geometry.y + 3,
1788
+ anchor: 'end'
1789
+ };
1790
+ if ('right' === position) return {
1791
+ x: geometry.valueX + 6,
1792
+ y: geometry.y + 3,
1793
+ anchor: 'start'
1794
+ };
1795
+ return {
1796
+ x: geometry.valueX + (positive ? 6 : -6),
1797
+ y: geometry.y + 3,
1798
+ anchor: positive ? 'start' : 'end'
1799
+ };
1800
+ }
1801
+ function placeCircularLabel(geometry, position) {
1802
+ const thickness = geometry.outerRadius - geometry.innerRadius;
1803
+ const radius = 'outsideEnd' === position ? geometry.outerRadius + 14 : 'insideBase' === position ? geometry.innerRadius + 0.28 * thickness : 'insideEnd' === position ? geometry.innerRadius + 0.76 * thickness : geometry.innerRadius + 0.58 * thickness;
1804
+ const x = geometry.centerX + Math.cos(geometry.angle) * radius;
1805
+ const y = geometry.centerY + Math.sin(geometry.angle) * radius;
1806
+ if ('left' === position) return {
1807
+ x: x - 8,
1808
+ y: y + 3,
1809
+ anchor: 'end'
1810
+ };
1811
+ if ('right' === position) return {
1812
+ x: x + 8,
1813
+ y: y + 3,
1814
+ anchor: 'start'
1815
+ };
1816
+ if ('above' === position) return {
1817
+ x,
1818
+ y: y - 7,
1819
+ anchor: 'middle'
1820
+ };
1821
+ if ('below' === position) return {
1822
+ x,
1823
+ y: y + 13,
1824
+ anchor: 'middle'
1825
+ };
1826
+ const anchor = 'outsideEnd' === position ? Math.cos(geometry.angle) >= 0 ? 'start' : 'end' : 'middle';
1827
+ return {
1828
+ x,
1829
+ y: y + 3,
1830
+ anchor
1831
+ };
1832
+ }
1833
+ function spreadsheetErrorBarAmounts(series, source, chartType) {
1834
+ const errorBars = normalizeWorkSpreadsheetErrorBars(source, chartType);
1835
+ const values = errorBarSourceValues(series, errorBars);
1836
+ const sharedAmount = sharedErrorAmount(values, errorBars);
1837
+ return values.map((value, index)=>{
1838
+ const amount = 'percentage' === errorBars.valueType ? Math.abs(value) * ((errorBars.value ?? 5) / 100) : 'custom' === errorBars.valueType ? 0 : sharedAmount;
1839
+ const minus = 'custom' === errorBars.valueType ? finiteNonNegative(errorBars.minusValues?.[index]) : amount;
1840
+ const plus = 'custom' === errorBars.valueType ? finiteNonNegative(errorBars.plusValues?.[index]) : amount;
1841
+ return {
1842
+ minus: 'plus' === errorBars.barType ? 0 : minus,
1843
+ plus: 'minus' === errorBars.barType ? 0 : plus
1844
+ };
1845
+ });
1846
+ }
1847
+ function spreadsheetSeriesErrorBarBounds(series, chartType, direction) {
1848
+ const values = 'x' === direction ? numericXValues(series) : series.values;
1849
+ const matching = (series.errorBars ?? []).filter((source)=>normalizeWorkSpreadsheetErrorBars(source, chartType).direction === direction);
1850
+ if (!matching.length) return values.filter(Number.isFinite);
1851
+ return matching.flatMap((source)=>{
1852
+ const amounts = spreadsheetErrorBarAmounts(series, source, chartType);
1853
+ return values.flatMap((value, index)=>{
1854
+ if (!Number.isFinite(value)) return [];
1855
+ const amount = amounts[index] ?? {
1856
+ minus: 0,
1857
+ plus: 0
1858
+ };
1859
+ return [
1860
+ value - amount.minus,
1861
+ value,
1862
+ value + amount.plus
1863
+ ];
1864
+ });
1865
+ });
1866
+ }
1867
+ function errorBarSourceValues(series, errorBars) {
1868
+ return 'x' === errorBars.direction ? numericXValues(series) : series.values;
1869
+ }
1870
+ function numericXValues(series) {
1871
+ return series.values.map((_, index)=>series.xValues?.[index] ?? index + 1);
1872
+ }
1873
+ function sharedErrorAmount(values, errorBars) {
1874
+ if ('fixedValue' === errorBars.valueType) return errorBars.value ?? 1;
1875
+ if ('standardDeviation' === errorBars.valueType) return populationStandardDeviation(values) * (errorBars.value ?? 1);
1876
+ if ('standardError' === errorBars.valueType) {
1877
+ const usableCount = values.filter(Number.isFinite).length;
1878
+ return usableCount ? populationStandardDeviation(values) / Math.sqrt(usableCount) : 0;
1879
+ }
1880
+ return 0;
1881
+ }
1882
+ function populationStandardDeviation(values) {
1883
+ const usable = values.filter(Number.isFinite);
1884
+ if (!usable.length) return 0;
1885
+ const mean = usable.reduce((sum, value)=>sum + value, 0) / usable.length;
1886
+ return Math.sqrt(usable.reduce((sum, value)=>sum + (value - mean) ** 2, 0) / usable.length);
1887
+ }
1888
+ function finiteNonNegative(value) {
1889
+ return 'number' == typeof value && Number.isFinite(value) ? Math.max(0, value) : 0;
1890
+ }
1891
+ function spreadsheetSeriesErrorBarsSvg(chart, seriesIndex, position) {
1892
+ const series = chart.series[seriesIndex];
1893
+ if (!series?.errorBars?.length) return '';
1894
+ const color = WORK_SPREADSHEET_CHART_COLORS[seriesIndex % WORK_SPREADSHEET_CHART_COLORS.length];
1895
+ return series.errorBars.map((source, errorBarIndex)=>{
1896
+ const errorBars = normalizeWorkSpreadsheetErrorBars(source, chart.type);
1897
+ const values = errorBarSourceValues(series, errorBars);
1898
+ const amounts = spreadsheetErrorBarAmounts(series, errorBars, chart.type);
1899
+ const points = values.map((value, pointIndex)=>{
1900
+ if (!Number.isFinite(value)) return '';
1901
+ const amount = amounts[pointIndex] ?? {
1902
+ minus: 0,
1903
+ plus: 0
1904
+ };
1905
+ const start = position(errorBars.direction, pointIndex, value - amount.minus);
1906
+ const end = position(errorBars.direction, pointIndex, value + amount.plus);
1907
+ const horizontal = Math.abs(end[0] - start[0]) >= Math.abs(end[1] - start[1]);
1908
+ const caps = false === errorBars.showEndCaps ? '' : `${amount.minus > 0 ? errorBarCap(start, horizontal) : ''}${amount.plus > 0 ? errorBarCap(end, horizontal) : ''}`;
1909
+ return `<g data-error-bar-point="${seriesIndex}:${errorBarIndex}:${pointIndex}" data-error-minus="${roundChartNumber(amount.minus)}" data-error-plus="${roundChartNumber(amount.plus)}"><path d="M ${roundChartNumber(start[0])} ${roundChartNumber(start[1])} L ${roundChartNumber(end[0])} ${roundChartNumber(end[1])}" fill="none" stroke="${color}" stroke-width="1.4"/>${caps}</g>`;
1910
+ }).join('');
1911
+ return `<g data-error-bars-series="${seriesIndex}:${errorBarIndex}" data-error-bars-direction="${errorBars.direction}" data-error-bars-type="${errorBars.barType}" data-error-bars-value-type="${errorBars.valueType}" data-error-bars-end-caps="${false !== errorBars.showEndCaps}" color="${color}">${points}</g>`;
1912
+ }).join('');
1913
+ }
1914
+ function errorBarCap(point, horizontal) {
1915
+ return horizontal ? `<path d="M ${roundChartNumber(point[0])} ${roundChartNumber(point[1] - 4)} L ${roundChartNumber(point[0])} ${roundChartNumber(point[1] + 4)}" fill="none" stroke="currentColor" stroke-width="1.4"/>` : `<path d="M ${roundChartNumber(point[0] - 4)} ${roundChartNumber(point[1])} L ${roundChartNumber(point[0] + 4)} ${roundChartNumber(point[1])}" fill="none" stroke="currentColor" stroke-width="1.4"/>`;
1916
+ }
1917
+ const DEFAULT_TICK_COUNT = 5;
1918
+ const MAX_RENDERED_TICKS = 64;
1919
+ function workSpreadsheetChartAxisIsValueAxis(chartType, position) {
1920
+ if (!workSpreadsheetChartSupportsAxes(chartType) || 'top' === position) return false;
1921
+ if (workSpreadsheetChartUsesNumericXAxis(chartType)) return 'bottom' === position || 'left' === position;
1922
+ if ('bar' === chartType) return 'bottom' === position;
1923
+ if ('combination' === chartType) return 'left' === position || 'right' === position;
1924
+ return 'left' === position;
1925
+ }
1926
+ function workSpreadsheetChartAxisIsCategoryAxis(chartType, position) {
1927
+ if (!workSpreadsheetChartSupportsAxes(chartType) || workSpreadsheetChartUsesNumericXAxis(chartType)) return false;
1928
+ if ('bar' === chartType) return 'left' === position;
1929
+ if ('combination' === chartType) return 'bottom' === position || 'top' === position;
1930
+ return 'bottom' === position;
1931
+ }
1932
+ function workSpreadsheetChartAxisShowsMajorGridlinesByDefault(chartType, position) {
1933
+ if (!workSpreadsheetChartAxisIsValueAxis(chartType, position)) return false;
1934
+ if (workSpreadsheetChartUsesNumericXAxis(chartType)) return 'left' === position;
1935
+ if ('bar' === chartType) return 'bottom' === position;
1936
+ return 'left' === position;
1937
+ }
1938
+ function workSpreadsheetChartAxisDefaultLabelPosition(chartType, position) {
1939
+ return 'combination' === chartType && 'top' === position ? 'none' : 'nextTo';
1940
+ }
1941
+ function workSpreadsheetChartAxisPositionLabel(position) {
1942
+ if ('left' === position) return '纵轴';
1943
+ if ('top' === position) return '次横轴';
1944
+ if ('right' === position) return '次纵轴';
1945
+ return '横轴';
1946
+ }
1947
+ function workSpreadsheetChartAxisLabelPositionLabel(position) {
1948
+ if ('high' === position) return '高位';
1949
+ if ('low' === position) return '低位';
1950
+ if ('none' === position) return '不显示';
1951
+ return '轴旁';
1952
+ }
1953
+ function workSpreadsheetChartAxisTickMarkLabel(tickMark) {
1954
+ if ('inside' === tickMark) return '向内';
1955
+ if ('outside' === tickMark) return '向外';
1956
+ if ('cross' === tickMark) return '交叉';
1957
+ return '无';
1958
+ }
1959
+ function normalizeWorkSpreadsheetChartAxes(source, chartType, hasSecondaryAxes = false) {
1960
+ if (!source || !workSpreadsheetChartSupportsAxes(chartType)) return;
1961
+ const bottom = normalizeWorkSpreadsheetChartAxis(source.bottom, chartType, 'bottom');
1962
+ const left = normalizeWorkSpreadsheetChartAxis(source.left, chartType, 'left');
1963
+ const top = 'combination' === chartType && hasSecondaryAxes ? normalizeWorkSpreadsheetChartAxis(source.top, chartType, 'top') : void 0;
1964
+ const right = 'combination' === chartType && hasSecondaryAxes ? normalizeWorkSpreadsheetChartAxis(source.right, chartType, 'right') : void 0;
1965
+ return bottom || left || top || right ? {
1966
+ ...bottom ? {
1967
+ bottom
1968
+ } : {},
1969
+ ...left ? {
1970
+ left
1971
+ } : {},
1972
+ ...top ? {
1973
+ top
1974
+ } : {},
1975
+ ...right ? {
1976
+ right
1977
+ } : {}
1978
+ } : void 0;
1979
+ }
1980
+ function normalizeWorkSpreadsheetChartAxis(source, chartType, position) {
1981
+ if (!source) return;
1982
+ const title = source.title?.trim();
1983
+ const titleReference = source.titleReference?.trim().replace(/^=/, '');
1984
+ const valueAxis = workSpreadsheetChartAxisIsValueAxis(chartType, position);
1985
+ const categoryAxis = workSpreadsheetChartAxisIsCategoryAxis(chartType, position);
1986
+ const minimum = valueAxis && Number.isFinite(source.minimum) ? Number(source.minimum) : void 0;
1987
+ const maximum = valueAxis && Number.isFinite(source.maximum) ? Number(source.maximum) : void 0;
1988
+ const validRange = void 0 === minimum || void 0 === maximum || minimum < maximum;
1989
+ const majorUnit = valueAxis && Number.isFinite(source.majorUnit) && Number(source.majorUnit) > 0 ? Number(source.majorUnit) : void 0;
1990
+ const showMajorGridlines = valueAxis && 'boolean' == typeof source.showMajorGridlines ? source.showMajorGridlines : void 0;
1991
+ const numberFormat = valueAxis ? source.numberFormat?.trim().slice(0, 255) : void 0;
1992
+ const numberFormatSourceLinked = valueAxis && 'boolean' == typeof source.numberFormatSourceLinked ? source.numberFormatSourceLinked : void 0;
1993
+ const labelPosition = normalizedAxisLabelPosition(source.labelPosition);
1994
+ const defaultLabelPosition = workSpreadsheetChartAxisDefaultLabelPosition(chartType, position);
1995
+ const majorTickMark = normalizedAxisTickMark(source.majorTickMark);
1996
+ const labelInterval = categoryAxis ? normalizedLabelInterval(source.labelInterval) : void 0;
1997
+ const normalized = {
1998
+ ...title ? {
1999
+ title
2000
+ } : {},
2001
+ ...titleReference ? {
2002
+ titleReference
2003
+ } : {},
2004
+ ...true === source.reverseOrder ? {
2005
+ reverseOrder: true
2006
+ } : {},
2007
+ ...labelPosition && labelPosition !== defaultLabelPosition ? {
2008
+ labelPosition
2009
+ } : {},
2010
+ ...majorTickMark && 'none' !== majorTickMark ? {
2011
+ majorTickMark
2012
+ } : {},
2013
+ ...void 0 !== labelInterval ? {
2014
+ labelInterval
2015
+ } : {},
2016
+ ...validRange && void 0 !== minimum ? {
2017
+ minimum
2018
+ } : {},
2019
+ ...validRange && void 0 !== maximum ? {
2020
+ maximum
2021
+ } : {},
2022
+ ...void 0 !== majorUnit ? {
2023
+ majorUnit
2024
+ } : {},
2025
+ ...void 0 !== showMajorGridlines ? {
2026
+ showMajorGridlines
2027
+ } : {},
2028
+ ...numberFormat ? {
2029
+ numberFormat
2030
+ } : {},
2031
+ ...void 0 !== numberFormatSourceLinked ? {
2032
+ numberFormatSourceLinked
2033
+ } : {}
2034
+ };
2035
+ return Object.keys(normalized).length ? normalized : void 0;
2036
+ }
2037
+ function normalizedAxisLabelPosition(value) {
2038
+ return 'nextTo' === value || 'high' === value || 'low' === value || 'none' === value ? value : void 0;
2039
+ }
2040
+ function normalizedAxisTickMark(value) {
2041
+ return 'none' === value || 'inside' === value || 'outside' === value || 'cross' === value ? value : void 0;
2042
+ }
2043
+ function normalizedLabelInterval(value) {
2044
+ const interval = Number(value);
2045
+ return Number.isInteger(interval) && interval >= 1 && interval <= 31999 ? interval : void 0;
2046
+ }
2047
+ function spreadsheetChartAxisDisplayAttributes(axis, chartType, position) {
2048
+ const labelPosition = axis?.labelPosition ?? workSpreadsheetChartAxisDefaultLabelPosition(chartType, position);
2049
+ const majorTickMark = axis?.majorTickMark ?? 'none';
2050
+ return [
2051
+ `data-axis-display="${position}"`,
2052
+ `data-axis-reverse-order="${axis?.reverseOrder === true}"`,
2053
+ `data-axis-label-position="${labelPosition}"`,
2054
+ `data-axis-major-tick-mark="${majorTickMark}"`,
2055
+ workSpreadsheetChartAxisIsCategoryAxis(chartType, position) ? `data-axis-label-interval="${axis?.labelInterval ?? 'auto'}"` : ''
2056
+ ].filter(Boolean).join(' ');
2057
+ }
2058
+ function spreadsheetChartAxisValueRatio(value, scale, axis) {
2059
+ const ratio = Math.min(1, Math.max(0, (finiteChartNumber(value) - scale.minimum) / scale.span));
2060
+ return axis?.reverseOrder === true ? 1 - ratio : ratio;
2061
+ }
2062
+ function spreadsheetChartCategoryVisualIndex(index, count, axis) {
2063
+ return axis?.reverseOrder === true ? count - 1 - index : index;
2064
+ }
2065
+ function spreadsheetChartCategoryLabelVisible(index, count, axis) {
2066
+ const explicitInterval = normalizedLabelInterval(axis?.labelInterval);
2067
+ const interval = explicitInterval ?? Math.max(1, Math.ceil(count / 7));
2068
+ return index % interval === 0 || void 0 === explicitInterval && index === count - 1;
2069
+ }
2070
+ function spreadsheetChartAxisLabelLayout(axis, chartType, position, plot, tickX, tickY) {
2071
+ const labelPosition = axis?.labelPosition ?? workSpreadsheetChartAxisDefaultLabelPosition(chartType, position);
2072
+ if ('none' === labelPosition) return null;
2073
+ if ('bottom' === position) return {
2074
+ x: tickX,
2075
+ y: 'high' === labelPosition ? plot.y - 7 : plot.y + plot.height + 18,
2076
+ textAnchor: 'middle'
2077
+ };
2078
+ if ('top' === position) return {
2079
+ x: tickX,
2080
+ y: 'low' === labelPosition ? plot.y + plot.height + 18 : plot.y - 7,
2081
+ textAnchor: 'middle'
2082
+ };
2083
+ if ('left' === position) {
2084
+ const high = 'high' === labelPosition;
2085
+ return {
2086
+ x: high ? plot.x + plot.width + 8 : plot.x - 8,
2087
+ y: tickY + 4,
2088
+ textAnchor: high ? 'start' : 'end'
2089
+ };
2090
+ }
2091
+ const low = 'low' === labelPosition;
2092
+ return {
2093
+ x: low ? plot.x - 8 : plot.x + plot.width + 8,
2094
+ y: tickY + 4,
2095
+ textAnchor: low ? 'end' : 'start'
2096
+ };
2097
+ }
2098
+ function spreadsheetChartMajorTickSvg(axis, position, index, x, y) {
2099
+ const mark = axis?.majorTickMark ?? 'none';
2100
+ if ('none' === mark) return '';
2101
+ const inward = 'bottom' === position ? [
2102
+ 0,
2103
+ -1
2104
+ ] : 'top' === position ? [
2105
+ 0,
2106
+ 1
2107
+ ] : 'left' === position ? [
2108
+ 1,
2109
+ 0
2110
+ ] : [
2111
+ -1,
2112
+ 0
2113
+ ];
2114
+ const insideLength = 'outside' === mark ? 0 : 'cross' === mark ? 3 : 5;
2115
+ const outsideLength = 'inside' === mark ? 0 : 'cross' === mark ? 3 : 5;
2116
+ const x1 = x - inward[0] * outsideLength;
2117
+ const y1 = y - inward[1] * outsideLength;
2118
+ const x2 = x + inward[0] * insideLength;
2119
+ const y2 = y + inward[1] * insideLength;
2120
+ return `<line data-axis-major-tick="${position}:${index}" x1="${roundChartNumber(x1)}" y1="${roundChartNumber(y1)}" x2="${roundChartNumber(x2)}" y2="${roundChartNumber(y2)}" stroke="#7d8798" stroke-width="1"/>`;
2121
+ }
2122
+ function spreadsheetChartAxisScale(values, axis, options = {}) {
2123
+ const finiteValues = values.filter(Number.isFinite).map(finiteChartNumber);
2124
+ const sourceValues = finiteValues.length ? finiteValues : [
2125
+ 0,
2126
+ 1
2127
+ ];
2128
+ let automaticMinimum = Math.min(...sourceValues);
2129
+ let automaticMaximum = Math.max(...sourceValues);
2130
+ if (options.includeZero) {
2131
+ automaticMinimum = Math.min(0, automaticMinimum);
2132
+ automaticMaximum = Math.max(0, automaticMaximum);
2133
+ }
2134
+ const sourceSpan = automaticMaximum - automaticMinimum;
2135
+ const paddingRatio = Math.max(0, options.paddingRatio ?? 0);
2136
+ const padding = sourceSpan > 0 ? sourceSpan * paddingRatio : Math.max(1, Math.abs(automaticMinimum) * Math.max(paddingRatio, 0.05));
2137
+ automaticMinimum -= padding;
2138
+ automaticMaximum += padding;
2139
+ let minimum = Number.isFinite(axis?.minimum) ? Number(axis?.minimum) : automaticMinimum;
2140
+ let maximum = Number.isFinite(axis?.maximum) ? Number(axis?.maximum) : automaticMaximum;
2141
+ const fallbackSpan = Math.max(Number.isFinite(axis?.majorUnit) && Number(axis?.majorUnit) > 0 ? Number(axis?.majorUnit) : 0, 0.1 * Math.abs(minimum), 0.1 * Math.abs(maximum), 1);
2142
+ if (!(minimum < maximum)) if (axis?.minimum !== void 0 && void 0 === axis.maximum) maximum = minimum + fallbackSpan;
2143
+ else if (axis?.maximum !== void 0 && void 0 === axis.minimum) minimum = maximum - fallbackSpan;
2144
+ else {
2145
+ minimum = automaticMinimum;
2146
+ maximum = automaticMaximum;
2147
+ }
2148
+ if (!(minimum < maximum)) maximum = minimum + fallbackSpan;
2149
+ const span = Math.max(Number.EPSILON, maximum - minimum);
2150
+ const majorUnit = Number.isFinite(axis?.majorUnit) && Number(axis?.majorUnit) > 0 ? Number(axis?.majorUnit) : void 0;
2151
+ return {
2152
+ minimum,
2153
+ maximum,
2154
+ span,
2155
+ ...void 0 !== majorUnit ? {
2156
+ majorUnit
2157
+ } : {},
2158
+ ticks: axisTicks(minimum, maximum, majorUnit)
2159
+ };
2160
+ }
2161
+ function spreadsheetChartAxisGridlinesVisible(axis, chartType, position) {
2162
+ return axis?.showMajorGridlines ?? workSpreadsheetChartAxisShowsMajorGridlinesByDefault(chartType, position);
2163
+ }
2164
+ function spreadsheetChartAxisScaleAttributes(axis, scale, chartType, position) {
2165
+ return [
2166
+ `data-axis-scale="${position}"`,
2167
+ `data-axis-minimum="${roundChartNumber(scale.minimum)}"`,
2168
+ `data-axis-maximum="${roundChartNumber(scale.maximum)}"`,
2169
+ void 0 === scale.majorUnit ? '' : `data-axis-major-unit="${roundChartNumber(scale.majorUnit)}"`,
2170
+ `data-axis-gridlines="${spreadsheetChartAxisGridlinesVisible(axis, chartType, position)}"`,
2171
+ axis?.numberFormat ? `data-axis-number-format="${escapeChartXml(axis.numberFormat)}"` : '',
2172
+ axis?.numberFormatSourceLinked === void 0 ? '' : `data-axis-number-format-source-linked="${axis.numberFormatSourceLinked}"`,
2173
+ spreadsheetChartAxisDisplayAttributes(axis, chartType, position)
2174
+ ].filter(Boolean).join(' ');
2175
+ }
2176
+ function formatSpreadsheetChartAxisNumber(value, formatCode) {
2177
+ const code = formatCode?.trim();
2178
+ if (!code || /^general$/i.test(code)) return compactChartNumber(value);
2179
+ const section = code.split(';')[value < 0 ? 1 : 0] || code.split(';')[0] || code;
2180
+ const percent = section.includes('%');
2181
+ const scientific = /e[+-]/i.test(section);
2182
+ const decimalPattern = /[0#]+\.([0#]+)/.exec(section);
2183
+ const decimals = Math.min(12, decimalPattern?.[1].length ?? 0);
2184
+ const scaled = percent ? 100 * value : value;
2185
+ let formatted;
2186
+ if (scientific) {
2187
+ const [mantissa, exponentText] = scaled.toExponential(decimals).split('e');
2188
+ const exponent = Number(exponentText);
2189
+ const exponentWidth = /e[+-](0+)/i.exec(section)?.[1].length ?? 1;
2190
+ formatted = `${mantissa}E${exponent >= 0 ? '+' : '-'}${String(Math.abs(exponent)).padStart(exponentWidth, '0')}`;
2191
+ } else formatted = new Intl.NumberFormat('en-US', {
2192
+ useGrouping: section.includes(','),
2193
+ minimumFractionDigits: decimals,
2194
+ maximumFractionDigits: decimals
2195
+ }).format(scaled);
2196
+ const currency = /[$¥€£]/.exec(section)?.[0] ?? '';
2197
+ return `${currency}${formatted}${percent ? '%' : ''}`;
2198
+ }
2199
+ function axisTicks(minimum, maximum, majorUnit) {
2200
+ if (void 0 === majorUnit) return Array.from({
2201
+ length: DEFAULT_TICK_COUNT
2202
+ }, (_, index)=>normalizedTick(minimum + index / (DEFAULT_TICK_COUNT - 1) * (maximum - minimum)));
2203
+ const epsilon = Math.max(Number.EPSILON, 1e-9 * Math.abs(majorUnit));
2204
+ const first = Math.ceil((minimum - epsilon) / majorUnit) * majorUnit;
2205
+ const count = Math.floor((maximum - first + epsilon) / majorUnit) + 1;
2206
+ if (count <= 0) return [
2207
+ normalizedTick(minimum),
2208
+ normalizedTick(maximum)
2209
+ ];
2210
+ if (count > MAX_RENDERED_TICKS) {
2211
+ const step = Math.ceil(count / MAX_RENDERED_TICKS);
2212
+ return Array.from({
2213
+ length: Math.ceil(count / step)
2214
+ }, (_, index)=>normalizedTick(first + index * step * majorUnit)).filter((value)=>value <= maximum + epsilon);
2215
+ }
2216
+ return Array.from({
2217
+ length: count
2218
+ }, (_, index)=>normalizedTick(first + index * majorUnit));
2219
+ }
2220
+ function normalizedTick(value) {
2221
+ return Math.abs(value) < 1e-12 ? 0 : Math.round(1e12 * value) / 1e12;
2222
+ }
2223
+ function spreadsheetLineChartSvg(chart, plot, valuePosition, layout) {
2224
+ const pointX = (index)=>1 === layout.categoryCount ? plot.x + plot.width / 2 : plot.x + (spreadsheetChartCategoryVisualIndex(index, layout.categoryCount, chart.axes?.bottom) + 0.5) / layout.categoryCount * plot.width;
2225
+ const smooth = 'line' === chart.type && normalizeWorkSpreadsheetChartSmoothLines(chart.smoothLines);
2226
+ return layout.series.map((points, seriesIndex)=>{
2227
+ const positioned = points.map((point)=>({
2228
+ ...point,
2229
+ x: pointX(point.categoryIndex),
2230
+ startY: valuePosition(point.start),
2231
+ endY: valuePosition(point.end)
2232
+ }));
2233
+ if (!positioned.length) return '';
2234
+ const path = chartLinePath(positioned.map((point)=>[
2235
+ point.x,
2236
+ point.endY
2237
+ ]), smooth);
2238
+ const series = chart.series[seriesIndex];
2239
+ const fill = spreadsheetChartSeriesFillStyle(series, seriesIndex, 0.2);
2240
+ const line = spreadsheetChartSeriesLineStyle(series, seriesIndex, 2.5);
2241
+ const area = 'area' === chart.type ? `<path data-area-series="${seriesIndex}" data-area-baseline="${layout.stacked ? 'stacked' : 'zero'}" d="${stackedAreaPath(positioned)}" ${fill.attributes}/>` : '';
2242
+ const markers = positioned.map((point)=>spreadsheetChartSeriesMarkerSvg(series, seriesIndex, point.x, point.endY, {
2243
+ visible: true,
2244
+ defaultSize: 5,
2245
+ attributes: `data-chart-point="${seriesIndex}:${point.categoryIndex}" data-stack-start="${roundChartNumber(point.start)}" data-stack-end="${roundChartNumber(point.end)}"`
2246
+ })).join('');
2247
+ const dataLabels = positioned.map((point)=>spreadsheetDataLabelSvg(chart, seriesIndex, point.categoryIndex, {
2248
+ kind: 'point',
2249
+ x: point.x,
2250
+ y: point.endY
2251
+ })).join('');
2252
+ const errorBars = layout.stacked ? '' : spreadsheetSeriesErrorBarsSvg(chart, seriesIndex, (_direction, pointIndex, value)=>[
2253
+ pointX(pointIndex),
2254
+ valuePosition(value)
2255
+ ]);
2256
+ return `${area}${errorBars}<path data-line-series="${seriesIndex}" data-smooth="${smooth}" d="${path}" fill="none" ${line.attributes} stroke-linejoin="round" stroke-linecap="round"/>${markers}${dataLabels}`;
2257
+ }).join('');
2258
+ }
2259
+ function stackedAreaPath(points) {
2260
+ const upper = points.map((point, index)=>`${index ? 'L' : 'M'} ${roundChartNumber(point.x)} ${roundChartNumber(point.endY)}`);
2261
+ const lower = [
2262
+ ...points
2263
+ ].reverse().map((point)=>`L ${roundChartNumber(point.x)} ${roundChartNumber(point.startY)}`);
2264
+ return [
2265
+ ...upper,
2266
+ ...lower,
2267
+ 'Z'
2268
+ ].join(' ');
2269
+ }
2270
+ function chartLinePath(points, smooth) {
2271
+ if (!points.length) return '';
2272
+ if (!smooth || points.length < 3) return points.map(([x, y], index)=>`${index ? 'L' : 'M'} ${roundChartNumber(x)} ${roundChartNumber(y)}`).join(' ');
2273
+ const commands = [
2274
+ `M ${roundChartNumber(points[0][0])} ${roundChartNumber(points[0][1])}`
2275
+ ];
2276
+ for(let index = 0; index < points.length - 1; index += 1){
2277
+ const previous = points[index - 1] ?? points[index];
2278
+ const current = points[index];
2279
+ const next = points[index + 1];
2280
+ const following = points[index + 2] ?? next;
2281
+ commands.push(`C ${roundChartNumber(current[0] + (next[0] - previous[0]) / 6)} ${roundChartNumber(current[1] + (next[1] - previous[1]) / 6)} ${roundChartNumber(next[0] - (following[0] - current[0]) / 6)} ${roundChartNumber(next[1] - (following[1] - current[1]) / 6)} ${roundChartNumber(next[0])} ${roundChartNumber(next[1])}`);
2282
+ }
2283
+ return commands.join(' ');
2284
+ }
2285
+ function fitSpreadsheetTrendline(xValues, yValues, source) {
2286
+ const trendline = normalizeWorkSpreadsheetTrendline(source);
2287
+ const observed = yValues.flatMap((y, index)=>{
2288
+ const x = xValues[index];
2289
+ return Number.isFinite(x) && Number.isFinite(y) ? [
2290
+ {
2291
+ x,
2292
+ y
2293
+ }
2294
+ ] : [];
2295
+ });
2296
+ if (observed.length < 2) return null;
2297
+ if ('movingAverage' === trendline.type) return movingAverageFit(observed, trendline.period ?? 2);
2298
+ const model = regressionModel(observed, trendline);
2299
+ if (!model) return null;
2300
+ const minimum = Math.min(...observed.map((point)=>point.x)) - (trendline.backward ?? 0);
2301
+ const maximum = Math.max(...observed.map((point)=>point.x)) + (trendline.forward ?? 0);
2302
+ const points = sampledPoints(minimum, maximum, model.predict);
2303
+ if (points.length < 2) return null;
2304
+ return {
2305
+ points,
2306
+ equation: model.equation,
2307
+ rSquared: coefficientOfDetermination(observed, model.predict)
2308
+ };
2309
+ }
2310
+ function regressionModel(observed, trendline) {
2311
+ if ('linear' === trendline.type) return polynomialModel(observed, 1, trendline.intercept);
2312
+ if ('polynomial' === trendline.type) return polynomialModel(observed, Math.min(trendline.order ?? 2, observed.length - 1), trendline.intercept);
2313
+ if ('exponential' === trendline.type) {
2314
+ const usable = observed.filter((point)=>point.y > 0);
2315
+ const coefficients = linearCoefficients(usable.map((point)=>({
2316
+ x: point.x,
2317
+ y: Math.log(point.y)
2318
+ })));
2319
+ if (!coefficients) return null;
2320
+ const [intercept, slope] = coefficients;
2321
+ const factor = Math.exp(intercept);
2322
+ return {
2323
+ predict: (x)=>factor * Math.exp(slope * x),
2324
+ equation: `y = ${formatCoefficient(factor)}e^(${formatCoefficient(slope)}x)`
2325
+ };
2326
+ }
2327
+ if ('logarithmic' === trendline.type) {
2328
+ const usable = observed.filter((point)=>point.x > 0);
2329
+ const coefficients = linearCoefficients(usable.map((point)=>({
2330
+ x: Math.log(point.x),
2331
+ y: point.y
2332
+ })));
2333
+ if (!coefficients) return null;
2334
+ const [intercept, slope] = coefficients;
2335
+ return {
2336
+ predict: (x)=>x > 0 ? intercept + slope * Math.log(x) : NaN,
2337
+ equation: `y = ${formatCoefficient(slope)}ln(x) ${signedConstant(intercept)}`
2338
+ };
2339
+ }
2340
+ if ('power' === trendline.type) {
2341
+ const usable = observed.filter((point)=>point.x > 0 && point.y > 0);
2342
+ const coefficients = linearCoefficients(usable.map((point)=>({
2343
+ x: Math.log(point.x),
2344
+ y: Math.log(point.y)
2345
+ })));
2346
+ if (!coefficients) return null;
2347
+ const [intercept, exponent] = coefficients;
2348
+ const factor = Math.exp(intercept);
2349
+ return {
2350
+ predict: (x)=>x > 0 ? factor * x ** exponent : NaN,
2351
+ equation: `y = ${formatCoefficient(factor)}x^${formatCoefficient(exponent)}`
2352
+ };
2353
+ }
2354
+ return null;
2355
+ }
2356
+ function polynomialModel(observed, order, fixedIntercept) {
2357
+ const startPower = void 0 === fixedIntercept ? 0 : 1;
2358
+ const coefficientCount = order - startPower + 1;
2359
+ if (coefficientCount < 1 || observed.length < coefficientCount) return null;
2360
+ const matrix = Array.from({
2361
+ length: coefficientCount
2362
+ }, (_, row)=>Array.from({
2363
+ length: coefficientCount
2364
+ }, (_, column)=>observed.reduce((sum, point)=>sum + point.x ** (row + column + 2 * startPower), 0)));
2365
+ const vector = Array.from({
2366
+ length: coefficientCount
2367
+ }, (_, row)=>observed.reduce((sum, point)=>sum + point.x ** (row + startPower) * (point.y - (fixedIntercept ?? 0)), 0));
2368
+ const solved = solveLinearSystem(matrix, vector);
2369
+ if (!solved) return null;
2370
+ const coefficients = void 0 === fixedIntercept ? solved : [
2371
+ fixedIntercept,
2372
+ ...solved
2373
+ ];
2374
+ const predict = (x)=>coefficients.reduce((sum, coefficient, power)=>sum + coefficient * x ** power, 0);
2375
+ return {
2376
+ predict,
2377
+ equation: polynomialEquation(coefficients)
2378
+ };
2379
+ }
2380
+ function linearCoefficients(observed) {
2381
+ if (observed.length < 2) return null;
2382
+ const count = observed.length;
2383
+ const sumX = observed.reduce((sum, point)=>sum + point.x, 0);
2384
+ const sumY = observed.reduce((sum, point)=>sum + point.y, 0);
2385
+ const sumXX = observed.reduce((sum, point)=>sum + point.x * point.x, 0);
2386
+ const sumXY = observed.reduce((sum, point)=>sum + point.x * point.y, 0);
2387
+ const denominator = count * sumXX - sumX * sumX;
2388
+ if (Math.abs(denominator) < 1e-12) return null;
2389
+ const slope = (count * sumXY - sumX * sumY) / denominator;
2390
+ return [
2391
+ (sumY - slope * sumX) / count,
2392
+ slope
2393
+ ];
2394
+ }
2395
+ function solveLinearSystem(matrix, vector) {
2396
+ const size = vector.length;
2397
+ const augmented = matrix.map((row, index)=>[
2398
+ ...row,
2399
+ vector[index]
2400
+ ]);
2401
+ for(let column = 0; column < size; column += 1){
2402
+ let pivot = column;
2403
+ for(let row = column + 1; row < size; row += 1)if (Math.abs(augmented[row][column]) > Math.abs(augmented[pivot][column])) pivot = row;
2404
+ if (Math.abs(augmented[pivot][column]) < 1e-12) return null;
2405
+ [augmented[column], augmented[pivot]] = [
2406
+ augmented[pivot],
2407
+ augmented[column]
2408
+ ];
2409
+ const divisor = augmented[column][column];
2410
+ for(let index = column; index <= size; index += 1)augmented[column][index] /= divisor;
2411
+ for(let row = 0; row < size; row += 1){
2412
+ if (row === column) continue;
2413
+ const factor = augmented[row][column];
2414
+ for(let index = column; index <= size; index += 1)augmented[row][index] -= factor * augmented[column][index];
2415
+ }
2416
+ }
2417
+ return augmented.map((row)=>row[size]);
2418
+ }
2419
+ function sampledPoints(minimum, maximum, predict) {
2420
+ const span = maximum - minimum;
2421
+ if (!Number.isFinite(span) || span <= 0) return [];
2422
+ return Array.from({
2423
+ length: 65
2424
+ }, (_, index)=>{
2425
+ const x = minimum + index / 64 * span;
2426
+ return {
2427
+ x,
2428
+ y: predict(x)
2429
+ };
2430
+ }).filter((point)=>Number.isFinite(point.y));
2431
+ }
2432
+ function movingAverageFit(observed, period) {
2433
+ if (observed.length < period) return null;
2434
+ const points = observed.slice(period - 1).map((point, index)=>({
2435
+ x: point.x,
2436
+ y: observed.slice(index, index + period).reduce((sum, item)=>sum + item.y, 0) / period
2437
+ }));
2438
+ return {
2439
+ points,
2440
+ equation: `Moving average (${period})`
2441
+ };
2442
+ }
2443
+ function coefficientOfDetermination(observed, predict) {
2444
+ const usable = observed.map((point)=>({
2445
+ actual: point.y,
2446
+ predicted: predict(point.x)
2447
+ })).filter((point)=>Number.isFinite(point.predicted));
2448
+ if (usable.length < 2) return;
2449
+ const mean = usable.reduce((sum, point)=>sum + point.actual, 0) / usable.length;
2450
+ const total = usable.reduce((sum, point)=>sum + (point.actual - mean) ** 2, 0);
2451
+ const residual = usable.reduce((sum, point)=>sum + (point.actual - point.predicted) ** 2, 0);
2452
+ if (total < 1e-12) return residual < 1e-12 ? 1 : 0;
2453
+ return Math.max(0, Math.min(1, 1 - residual / total));
2454
+ }
2455
+ function polynomialEquation(coefficients) {
2456
+ const terms = coefficients.map((coefficient, power)=>{
2457
+ if (Math.abs(coefficient) < 1e-10) return '';
2458
+ const absolute = formatCoefficient(Math.abs(coefficient));
2459
+ const variable = 0 === power ? '' : 1 === power ? 'x' : `x^${power}`;
2460
+ return `${coefficient < 0 ? '-' : '+'}${absolute}${variable}`;
2461
+ }).filter(Boolean);
2462
+ if (!terms.length) return 'y = 0';
2463
+ return `y = ${terms.join(' ').replace(/^\+/, '').replaceAll('+', '+ ').replaceAll('-', '- ').trim()}`;
2464
+ }
2465
+ function formatCoefficient(value) {
2466
+ return String(roundChartNumber(value));
2467
+ }
2468
+ function signedConstant(value) {
2469
+ return `${value < 0 ? '-' : '+'} ${formatCoefficient(Math.abs(value))}`;
2470
+ }
2471
+ function spreadsheetSeriesTrendlineFits(series, xValues) {
2472
+ return (series.trendlines ?? []).flatMap((source, sourceIndex)=>{
2473
+ const fit = fitSpreadsheetTrendline(xValues, series.values, source);
2474
+ return fit ? [
2475
+ {
2476
+ sourceIndex,
2477
+ fit
2478
+ }
2479
+ ] : [];
2480
+ });
2481
+ }
2482
+ function spreadsheetSeriesTrendlinesSvg(series, seriesIndex, xValues, position) {
2483
+ const color = WORK_SPREADSHEET_CHART_COLORS[seriesIndex % WORK_SPREADSHEET_CHART_COLORS.length];
2484
+ return spreadsheetSeriesTrendlineFits(series, xValues).map(({ sourceIndex, fit })=>{
2485
+ const trendline = normalizeWorkSpreadsheetTrendline(series.trendlines?.[sourceIndex] ?? {
2486
+ type: 'linear'
2487
+ });
2488
+ const points = fit.points.map((point)=>position(point.x, point.y));
2489
+ if (points.length < 2) return '';
2490
+ const path = points.map(([x, y], index)=>`${index ? 'L' : 'M'} ${roundChartNumber(x)} ${roundChartNumber(y)}`).join(' ');
2491
+ const equation = trendline.displayEquation && fit.equation ? fit.equation : void 0;
2492
+ const rSquared = trendline.displayRSquared && void 0 !== fit.rSquared ? roundChartNumber(fit.rSquared) : void 0;
2493
+ const label = [
2494
+ equation,
2495
+ void 0 === rSquared ? '' : `R² = ${rSquared}`
2496
+ ].filter(Boolean).join(' · ');
2497
+ const labelPoint = points.at(-1);
2498
+ return [
2499
+ `<g data-trendline-series="${seriesIndex}:${sourceIndex}" data-trendline-type="${trendline.type}"${trendline.name ? ` data-trendline-name="${escapeChartXml(trendline.name)}"` : ''}${equation ? ` data-trendline-equation="${escapeChartXml(equation)}"` : ''}${void 0 === rSquared ? '' : ` data-trendline-r-squared="${rSquared}"`}>`,
2500
+ `<path d="${path}" fill="none" stroke="${color}" stroke-width="2" stroke-dasharray="${sourceIndex % 2 ? '3 3' : '7 4'}" stroke-linecap="round"/>`,
2501
+ label ? `<text x="${roundChartNumber(labelPoint[0] - 4)}" y="${roundChartNumber(labelPoint[1] - 7 - 12 * sourceIndex)}" fill="${color}" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="9" text-anchor="end">${escapeChartXml(label)}</text>` : '',
2502
+ '</g>'
2503
+ ].join('');
2504
+ }).join('');
2505
+ }
2506
+ function spreadsheetCombinationChartSvg(chart, plot) {
2507
+ const values = chart.series.flatMap((series)=>combinationSeriesValues(series));
2508
+ if (!values.length) return emptyCombinationChartSvg(plot);
2509
+ const hasSecondaryAxis = chart.series.some((series)=>'secondary' === normalizeWorkSpreadsheetChartAxisGroup(series.axisGroup));
2510
+ const primaryValues = chart.series.filter((series)=>'primary' === normalizeWorkSpreadsheetChartAxisGroup(series.axisGroup)).flatMap((series)=>combinationSeriesValues(series));
2511
+ const secondaryValues = chart.series.filter((series)=>'secondary' === normalizeWorkSpreadsheetChartAxisGroup(series.axisGroup)).flatMap((series)=>combinationSeriesValues(series));
2512
+ const primaryAxis = combinationAxis(primaryValues.length ? primaryValues : values, plot, chart.axes?.left);
2513
+ const secondaryAxis = combinationAxis(secondaryValues.length ? secondaryValues : values, plot, chart.axes?.right);
2514
+ const categoryCount = Math.max(1, chart.categories.length, ...chart.series.map((series)=>series.values.length));
2515
+ const pointX = (index, axis)=>1 === categoryCount ? plot.x + plot.width / 2 : plot.x + (spreadsheetChartCategoryVisualIndex(index, categoryCount, axis) + 0.5) / categoryCount * plot.width;
2516
+ const axisForSeries = (seriesIndex)=>'secondary' === normalizeWorkSpreadsheetChartAxisGroup(chart.series[seriesIndex]?.axisGroup) ? secondaryAxis : primaryAxis;
2517
+ const pointXForSeries = (seriesIndex, index)=>pointX(index, 'secondary' === normalizeWorkSpreadsheetChartAxisGroup(chart.series[seriesIndex]?.axisGroup) ? chart.axes?.top : chart.axes?.bottom);
2518
+ const areas = chart.series.map((series, seriesIndex)=>'area' === normalizeWorkSpreadsheetCombinationSeriesType(series.chartType) ? combinationAreaSeriesSvg(chart, seriesIndex, (index)=>pointXForSeries(seriesIndex, index), axisForSeries(seriesIndex)) : '').join('');
2519
+ const columns = combinationColumnSeriesSvg(chart, plot, categoryCount, axisForSeries, pointXForSeries);
2520
+ const lines = chart.series.map((series, seriesIndex)=>'line' === normalizeWorkSpreadsheetCombinationSeriesType(series.chartType) ? combinationLineSeriesSvg(chart, seriesIndex, (index)=>pointXForSeries(seriesIndex, index), axisForSeries(seriesIndex)) : '').join('');
2521
+ const trendlines = chart.series.map((series, seriesIndex)=>spreadsheetSeriesTrendlinesSvg(series, seriesIndex, combinationTrendlineXValues(series), (x, y)=>[
2522
+ pointXForSeries(seriesIndex, x - 1),
2523
+ axisForSeries(seriesIndex).position(y)
2524
+ ])).join('');
2525
+ return `<g data-combination-chart="true">${combinationAxesSvg(chart, plot, primaryAxis, secondaryAxis, hasSecondaryAxis)}${areas}${columns}${lines}${trendlines}${combinationCategoryLabelsSvg(chart, plot, categoryCount)}</g>`;
2526
+ }
2527
+ function combinationTrendlineXValues(series) {
2528
+ return series.values.map((_, index)=>index + 1);
2529
+ }
2530
+ function combinationTrendlineValues(series) {
2531
+ return spreadsheetSeriesTrendlineFits(series, combinationTrendlineXValues(series)).flatMap((item)=>item.fit.points.map((point)=>point.y));
2532
+ }
2533
+ function combinationSeriesValues(series) {
2534
+ return [
2535
+ ...series.values.map(finiteChartNumber),
2536
+ ...spreadsheetSeriesErrorBarBounds(series, 'combination', 'y'),
2537
+ ...combinationTrendlineValues(series)
2538
+ ];
2539
+ }
2540
+ function combinationAxis(values, plot, axis) {
2541
+ const scale = spreadsheetChartAxisScale(values, axis, {
2542
+ includeZero: true
2543
+ });
2544
+ return {
2545
+ ...scale,
2546
+ position: (value)=>plot.y + (1 - spreadsheetChartAxisValueRatio(value, scale, axis)) * plot.height
2547
+ };
2548
+ }
2549
+ function combinationAxesSvg(chart, plot, primary, secondary, hasSecondary) {
2550
+ const primaryAxis = chart.axes?.left;
2551
+ const secondaryAxis = chart.axes?.right;
2552
+ const primaryGridlines = spreadsheetChartAxisGridlinesVisible(primaryAxis, chart.type, 'left');
2553
+ const secondaryGridlines = spreadsheetChartAxisGridlinesVisible(secondaryAxis, chart.type, 'right');
2554
+ const grid = primary.ticks.map((value, index)=>{
2555
+ const y = primary.position(value);
2556
+ const label = spreadsheetChartAxisLabelLayout(primaryAxis, chart.type, 'left', plot, plot.x, y);
2557
+ return `${primaryGridlines ? `<line x1="${plot.x}" y1="${roundChartNumber(y)}" x2="${roundChartNumber(plot.x + plot.width)}" y2="${roundChartNumber(y)}" stroke="#e8ebf0" stroke-width="1"/>` : ''}${spreadsheetChartMajorTickSvg(primaryAxis, 'left', index, plot.x, y)}${label ? `<text data-axis-tick="left" x="${roundChartNumber(label.x)}" y="${roundChartNumber(label.y)}" fill="#717b8f" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="10" text-anchor="${label.textAnchor}">${escapeChartXml(formatSpreadsheetChartAxisNumber(value, primaryAxis?.numberFormat))}</text>` : ''}`;
2558
+ }).join('');
2559
+ const secondaryLabels = hasSecondary ? `<g data-secondary-axis="true" ${spreadsheetChartAxisScaleAttributes(secondaryAxis, secondary, chart.type, 'right')}>${secondary.ticks.map((value, index)=>{
2560
+ const y = secondary.position(value);
2561
+ const label = spreadsheetChartAxisLabelLayout(secondaryAxis, chart.type, 'right', plot, plot.x + plot.width, y);
2562
+ return `${secondaryGridlines ? `<line x1="${plot.x}" y1="${roundChartNumber(y)}" x2="${roundChartNumber(plot.x + plot.width)}" y2="${roundChartNumber(y)}" stroke="#eef0f4" stroke-width="1" stroke-dasharray="2 2"/>` : ''}${spreadsheetChartMajorTickSvg(secondaryAxis, 'right', index, plot.x + plot.width, y)}${label ? `<text data-axis-tick="right" x="${roundChartNumber(label.x)}" y="${roundChartNumber(label.y)}" fill="#717b8f" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="10" text-anchor="${label.textAnchor}">${escapeChartXml(formatSpreadsheetChartAxisNumber(value, secondaryAxis?.numberFormat))}</text>` : ''}`;
2563
+ }).join('')}<line x1="${roundChartNumber(plot.x + plot.width)}" y1="${plot.y}" x2="${roundChartNumber(plot.x + plot.width)}" y2="${roundChartNumber(plot.y + plot.height)}" stroke="#aeb6c4" stroke-width="1.2"/></g>` : '';
2564
+ return `<g ${spreadsheetChartAxisScaleAttributes(primaryAxis, primary, chart.type, 'left')}>${grid}<line x1="${plot.x}" y1="${plot.y}" x2="${plot.x}" y2="${roundChartNumber(plot.y + plot.height)}" stroke="#aeb6c4" stroke-width="1.2"/></g><g ${spreadsheetChartAxisDisplayAttributes(chart.axes?.bottom, chart.type, 'bottom')}><line x1="${plot.x}" y1="${roundChartNumber(plot.y + plot.height)}" x2="${roundChartNumber(plot.x + plot.width)}" y2="${roundChartNumber(plot.y + plot.height)}" stroke="#aeb6c4" stroke-width="1.2"/></g>${secondaryLabels}`;
2565
+ }
2566
+ function combinationAreaSeriesSvg(chart, seriesIndex, pointX, axis) {
2567
+ const series = chart.series[seriesIndex];
2568
+ const points = series.values.map((value, index)=>[
2569
+ pointX(index),
2570
+ axis.position(value)
2571
+ ]);
2572
+ if (!points.length) return '';
2573
+ const fill = spreadsheetChartSeriesFillStyle(series, seriesIndex, 0.2);
2574
+ const lineStyle = spreadsheetChartSeriesLineStyle(series, seriesIndex, 2);
2575
+ const line = points.map(([x, y], index)=>`${index ? 'L' : 'M'} ${roundChartNumber(x)} ${roundChartNumber(y)}`).join(' ');
2576
+ const baseline = axis.position(0);
2577
+ const path = `${line} L ${roundChartNumber(points.at(-1)?.[0] ?? pointX(0))} ${roundChartNumber(baseline)} L ${roundChartNumber(points[0][0])} ${roundChartNumber(baseline)} Z`;
2578
+ const dataLabels = points.map(([x, y], pointIndex)=>spreadsheetDataLabelSvg(chart, seriesIndex, pointIndex, {
2579
+ kind: 'point',
2580
+ x,
2581
+ y
2582
+ })).join('');
2583
+ const errorBars = spreadsheetSeriesErrorBarsSvg(chart, seriesIndex, (_direction, pointIndex, value)=>[
2584
+ pointX(pointIndex),
2585
+ axis.position(value)
2586
+ ]);
2587
+ return `<g data-combination-series="${seriesIndex}" data-series-chart-type="area" data-axis-group="${normalizeWorkSpreadsheetChartAxisGroup(series.axisGroup)}"><path data-combination-area="${seriesIndex}" d="${path}" ${fill.attributes} ${lineStyle.attributes} stroke-linejoin="round"/>${errorBars}${dataLabels}</g>`;
2588
+ }
2589
+ function combinationColumnSeriesSvg(chart, plot, categoryCount, axisForSeries, pointXForSeries) {
2590
+ const columnSeries = chart.series.flatMap((series, seriesIndex)=>'column' === normalizeWorkSpreadsheetCombinationSeriesType(series.chartType) ? [
2591
+ {
2592
+ series,
2593
+ seriesIndex
2594
+ }
2595
+ ] : []);
2596
+ if (!columnSeries.length) return '';
2597
+ const groupWidth = plot.width / categoryCount;
2598
+ const barWidth = Math.max(2, Math.min(36, 0.72 * groupWidth / columnSeries.length));
2599
+ return columnSeries.map(({ series, seriesIndex }, columnIndex)=>{
2600
+ const axis = axisForSeries(seriesIndex);
2601
+ const baseline = axis.position(0);
2602
+ const fill = spreadsheetChartSeriesFillStyle(series, seriesIndex);
2603
+ const line = spreadsheetChartSeriesLineStyle(series, seriesIndex, 0);
2604
+ const seriesX = (pointIndex)=>pointXForSeries(seriesIndex, pointIndex) - barWidth * columnSeries.length / 2 + columnIndex * barWidth;
2605
+ const renderedWidth = Math.max(1, barWidth - 1.5);
2606
+ const bars = series.values.map((value, index)=>{
2607
+ const y = axis.position(value);
2608
+ const x = seriesX(index);
2609
+ return `<rect data-combination-column="${seriesIndex}:${index}" x="${roundChartNumber(x)}" y="${roundChartNumber(Math.min(y, baseline))}" width="${roundChartNumber(renderedWidth)}" height="${roundChartNumber(Math.max(1, Math.abs(y - baseline)))}" rx="1.5" ${fill.attributes} ${line.attributes}/>${spreadsheetDataLabelSvg(chart, seriesIndex, index, {
2610
+ kind: 'verticalBar',
2611
+ x: x + renderedWidth / 2,
2612
+ valueY: y,
2613
+ baselineY: baseline,
2614
+ value
2615
+ })}`;
2616
+ }).join('');
2617
+ const errorBars = spreadsheetSeriesErrorBarsSvg(chart, seriesIndex, (_direction, pointIndex, value)=>[
2618
+ seriesX(pointIndex) + renderedWidth / 2,
2619
+ axis.position(value)
2620
+ ]);
2621
+ return `<g data-combination-series="${seriesIndex}" data-series-chart-type="column" data-axis-group="${normalizeWorkSpreadsheetChartAxisGroup(series.axisGroup)}">${bars}${errorBars}</g>`;
2622
+ }).join('');
2623
+ }
2624
+ function combinationLineSeriesSvg(chart, seriesIndex, pointX, axis) {
2625
+ const series = chart.series[seriesIndex];
2626
+ const points = series.values.map((value, index)=>[
2627
+ pointX(index),
2628
+ axis.position(value)
2629
+ ]);
2630
+ if (!points.length) return '';
2631
+ const line = spreadsheetChartSeriesLineStyle(series, seriesIndex, 2.5);
2632
+ const path = points.map(([x, y], index)=>`${index ? 'L' : 'M'} ${roundChartNumber(x)} ${roundChartNumber(y)}`).join(' ');
2633
+ const markers = points.map(([x, y], pointIndex)=>spreadsheetChartSeriesMarkerSvg(series, seriesIndex, x, y, {
2634
+ visible: true,
2635
+ defaultSize: 5,
2636
+ attributes: `data-combination-marker="${seriesIndex}:${pointIndex}"`
2637
+ })).join('');
2638
+ const dataLabels = points.map(([x, y], pointIndex)=>spreadsheetDataLabelSvg(chart, seriesIndex, pointIndex, {
2639
+ kind: 'point',
2640
+ x,
2641
+ y
2642
+ })).join('');
2643
+ const errorBars = spreadsheetSeriesErrorBarsSvg(chart, seriesIndex, (_direction, pointIndex, value)=>[
2644
+ pointX(pointIndex),
2645
+ axis.position(value)
2646
+ ]);
2647
+ return `<g data-combination-series="${seriesIndex}" data-series-chart-type="line" data-axis-group="${normalizeWorkSpreadsheetChartAxisGroup(series.axisGroup)}">${errorBars}<path data-combination-line="${seriesIndex}" d="${path}" fill="none" ${line.attributes} stroke-linejoin="round" stroke-linecap="round"/>${markers}${dataLabels}</g>`;
2648
+ }
2649
+ function combinationCategoryLabelsSvg(chart, plot, categoryCount) {
2650
+ const labels = (position, axis)=>{
2651
+ const axisY = 'bottom' === position ? plot.y + plot.height : plot.y;
2652
+ return Array.from({
2653
+ length: categoryCount
2654
+ }, (_, index)=>{
2655
+ const visible = spreadsheetChartCategoryLabelVisible(index, categoryCount, axis);
2656
+ const label = chart.categories[index] ?? String(index + 1);
2657
+ const visualIndex = spreadsheetChartCategoryVisualIndex(index, categoryCount, axis);
2658
+ const x = plot.x + (visualIndex + 0.5) / categoryCount * plot.width;
2659
+ const labelLayout = spreadsheetChartAxisLabelLayout(axis, chart.type, position, plot, x, axisY);
2660
+ return `${spreadsheetChartMajorTickSvg(axis, position, index, x, axisY)}${visible && labelLayout ? `<text data-axis-category-label="${position}:${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>` : ''}`;
2661
+ }).join('');
2662
+ };
2663
+ const bottom = `<g ${spreadsheetChartAxisDisplayAttributes(chart.axes?.bottom, chart.type, 'bottom')}>${labels('bottom', chart.axes?.bottom)}</g>`;
2664
+ const topAxis = chart.axes?.top;
2665
+ const showTopAxis = (topAxis?.labelPosition ?? 'none') !== 'none' || (topAxis?.majorTickMark ?? 'none') !== 'none';
2666
+ const top = showTopAxis ? `<g ${spreadsheetChartAxisDisplayAttributes(topAxis, chart.type, 'top')}><line x1="${plot.x}" y1="${plot.y}" x2="${roundChartNumber(plot.x + plot.width)}" y2="${plot.y}" stroke="#aeb6c4" stroke-width="1.2"/>${labels('top', topAxis)}</g>` : '';
2667
+ return `${bottom}${top}`;
2668
+ }
2669
+ function emptyCombinationChartSvg(plot) {
2670
+ 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">没有可绘制的数据</text>`;
2671
+ }
2672
+ function spreadsheetXyChartAxesSvg(chart, plot, xBounds, yBounds) {
2673
+ const xAxis = chart.axes?.bottom;
2674
+ const yAxis = chart.axes?.left;
2675
+ const xGridlines = spreadsheetChartAxisGridlinesVisible(xAxis, chart.type, 'bottom');
2676
+ const yGridlines = spreadsheetChartAxisGridlinesVisible(yAxis, chart.type, 'left');
2677
+ const xTicks = xBounds.ticks.map((value, index)=>{
2678
+ const x = plot.x + spreadsheetChartAxisValueRatio(value, xBounds, xAxis) * plot.width;
2679
+ const label = spreadsheetChartAxisLabelLayout(xAxis, chart.type, 'bottom', plot, x, plot.y + plot.height);
2680
+ return `${xGridlines ? `<line x1="${roundChartNumber(x)}" y1="${plot.y}" x2="${roundChartNumber(x)}" y2="${roundChartNumber(plot.y + plot.height)}" stroke="#eef0f4" stroke-width="1"/>` : ''}${spreadsheetChartMajorTickSvg(xAxis, 'bottom', index, x, plot.y + plot.height)}${label ? `<text data-axis-tick="bottom" x="${roundChartNumber(label.x)}" y="${roundChartNumber(label.y)}" fill="#717b8f" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="10" text-anchor="${label.textAnchor}">${escapeChartXml(formatSpreadsheetChartAxisNumber(value, xAxis?.numberFormat))}</text>` : ''}`;
2681
+ }).join('');
2682
+ const yTicks = yBounds.ticks.map((value, index)=>{
2683
+ const y = plot.y + (1 - spreadsheetChartAxisValueRatio(value, yBounds, yAxis)) * plot.height;
2684
+ const label = spreadsheetChartAxisLabelLayout(yAxis, chart.type, 'left', plot, plot.x, y);
2685
+ return `${yGridlines ? `<line x1="${plot.x}" y1="${roundChartNumber(y)}" x2="${roundChartNumber(plot.x + plot.width)}" y2="${roundChartNumber(y)}" stroke="#e8ebf0" stroke-width="1"/>` : ''}${spreadsheetChartMajorTickSvg(yAxis, 'left', index, plot.x, y)}${label ? `<text data-axis-tick="left" x="${roundChartNumber(label.x)}" y="${roundChartNumber(label.y)}" fill="#717b8f" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="10" text-anchor="${label.textAnchor}">${escapeChartXml(formatSpreadsheetChartAxisNumber(value, yAxis?.numberFormat))}</text>` : ''}`;
2686
+ }).join('');
2687
+ return `<g ${spreadsheetChartAxisScaleAttributes(xAxis, xBounds, chart.type, 'bottom')}>${xTicks}<line x1="${plot.x}" y1="${roundChartNumber(plot.y + plot.height)}" x2="${roundChartNumber(plot.x + plot.width)}" y2="${roundChartNumber(plot.y + plot.height)}" stroke="#aeb6c4" stroke-width="1.2"/></g><g ${spreadsheetChartAxisScaleAttributes(yAxis, yBounds, chart.type, 'left')}>${yTicks}<line x1="${plot.x}" y1="${plot.y}" x2="${plot.x}" y2="${roundChartNumber(plot.y + plot.height)}" stroke="#aeb6c4" stroke-width="1.2"/></g>`;
2688
+ }
2689
+ const WIDTH = 640;
2690
+ const HEIGHT = 360;
2691
+ function spreadsheetChartSvgDataUrl(chart) {
2692
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(spreadsheetChartSvg(chart))}`;
2693
+ }
2694
+ function spreadsheetChartSvg(chart) {
2695
+ const title = chart.title?.trim();
2696
+ const titleHeight = title ? 42 : 16;
2697
+ const circular = 'pie' === chart.type || 'doughnut' === chart.type;
2698
+ const polar = 'radar' === chart.type;
2699
+ const xy = 'scatter' === chart.type || 'bubble' === chart.type;
2700
+ const hasSecondaryAxis = 'combination' === chart.type && chart.series.some((series)=>'secondary' === normalizeWorkSpreadsheetChartAxisGroup(series.axisGroup));
2701
+ const axes = normalizeWorkSpreadsheetChartAxes(chart.axes, chart.type, hasSecondaryAxis);
2702
+ const grouping = normalizeWorkSpreadsheetChartGrouping(chart.grouping, chart.type);
2703
+ const renderedChart = {
2704
+ ...chart,
2705
+ axes,
2706
+ ...workSpreadsheetChartSupportsGrouping(chart.type) ? {
2707
+ grouping
2708
+ } : {},
2709
+ ...workSpreadsheetChartSupportsBarSpacing(chart.type) ? {
2710
+ gapWidth: normalizeWorkSpreadsheetChartGapWidth(chart.gapWidth),
2711
+ overlap: normalizeWorkSpreadsheetChartOverlap(chart.overlap, grouping)
2712
+ } : {},
2713
+ ...workSpreadsheetChartSupportsSmoothLines(chart.type) ? {
2714
+ smoothLines: normalizeWorkSpreadsheetChartSmoothLines(chart.smoothLines)
2715
+ } : {}
2716
+ };
2717
+ const topAxisTitleHeight = axes?.top?.title ? 18 : 0;
2718
+ const secondaryAxisWidth = hasSecondaryAxis ? axes?.right?.title ? 58 : 44 : 0;
2719
+ const plotLeft = 'bar' === chart.type ? 88 : circular || polar ? 24 : xy ? 64 : 56;
2720
+ const basePlot = {
2721
+ x: plotLeft,
2722
+ y: titleHeight + 12 + topAxisTitleHeight,
2723
+ width: WIDTH - plotLeft - 18 - secondaryAxisWidth,
2724
+ height: HEIGHT - titleHeight - 54 - topAxisTitleHeight
2725
+ };
2726
+ const legend = spreadsheetChartLegendLayout(renderedChart, basePlot, {
2727
+ width: WIDTH,
2728
+ height: HEIGHT,
2729
+ titleHeight
2730
+ });
2731
+ const plot = legend.plot;
2732
+ const plotAttributes = spreadsheetChartPlotAttributes(renderedChart, plot);
2733
+ const plotSvg = 'pie' === chart.type ? pieChartSvg(renderedChart, plot) : 'doughnut' === chart.type ? doughnutChartSvg(renderedChart, plot) : 'radar' === chart.type ? radarChartSvg(renderedChart, plot) : xy ? xyChartSvg(renderedChart, plot) : 'combination' === chart.type ? spreadsheetCombinationChartSvg(renderedChart, plot) : cartesianChartSvg(renderedChart, plot);
2734
+ return [
2735
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${WIDTH} ${HEIGHT}" preserveAspectRatio="none" data-chart-type="${escapeChartXml(String(chart.type))}">`,
2736
+ '<rect width="640" height="360" rx="8" fill="#ffffff"/>',
2737
+ '<rect x="0.75" y="0.75" width="638.5" height="358.5" rx="7.25" fill="none" stroke="#d9dee8" stroke-width="1.5"/>',
2738
+ title ? `<text x="${WIDTH / 2}" y="27" fill="#172033" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="17" font-weight="600" text-anchor="middle">${escapeChartXml(title)}</text>` : '',
2739
+ `<g ${plotAttributes}>${plotSvg}</g>`,
2740
+ axes ? chartAxisTitlesSvg(axes, plot) : '',
2741
+ legend.svg,
2742
+ '</svg>'
2743
+ ].join('');
2744
+ }
2745
+ function spreadsheetChartPlotAttributes(chart, plot) {
2746
+ const attributes = [
2747
+ 'data-plot-area="true"',
2748
+ `data-plot-x="${roundChartNumber(plot.x)}"`,
2749
+ `data-plot-y="${roundChartNumber(plot.y)}"`,
2750
+ `data-plot-width="${roundChartNumber(plot.width)}"`,
2751
+ `data-plot-height="${roundChartNumber(plot.height)}"`
2752
+ ];
2753
+ if (workSpreadsheetChartSupportsGrouping(chart.type)) attributes.push(`data-chart-grouping="${normalizeWorkSpreadsheetChartGrouping(chart.grouping, chart.type)}"`);
2754
+ if (workSpreadsheetChartSupportsBarSpacing(chart.type)) {
2755
+ const grouping = normalizeWorkSpreadsheetChartGrouping(chart.grouping, chart.type);
2756
+ attributes.push(`data-chart-gap-width="${normalizeWorkSpreadsheetChartGapWidth(chart.gapWidth)}"`);
2757
+ attributes.push(`data-chart-overlap="${normalizeWorkSpreadsheetChartOverlap(chart.overlap, grouping)}"`);
2758
+ }
2759
+ if (workSpreadsheetChartSupportsSmoothLines(chart.type)) attributes.push(`data-chart-smooth-lines="${normalizeWorkSpreadsheetChartSmoothLines(chart.smoothLines)}"`);
2760
+ return attributes.join(' ');
2761
+ }
2762
+ function chartAxisTitlesSvg(axes, plot) {
2763
+ const bottom = chartAxisTitleText(axes.bottom?.title);
2764
+ const left = chartAxisTitleText(axes.left?.title);
2765
+ const top = chartAxisTitleText(axes.top?.title);
2766
+ const right = chartAxisTitleText(axes.right?.title);
2767
+ const font = 'font-family="-apple-system,BlinkMacSystemFont,\'Segoe UI\',sans-serif" font-size="11" font-weight="600"';
2768
+ const bottomY = Math.min(HEIGHT - 7, plot.y + plot.height + 35);
2769
+ const leftX = Math.max(13, plot.x - 43);
2770
+ const rightX = plot.x + plot.width + 45;
2771
+ return [
2772
+ bottom ? `<text data-axis-title="bottom" x="${roundChartNumber(plot.x + plot.width / 2)}" y="${roundChartNumber(bottomY)}" fill="#3f4a60" ${font} text-anchor="middle">${escapeChartXml(bottom)}</text>` : '',
2773
+ left ? `<text data-axis-title="left" x="${roundChartNumber(leftX)}" y="${roundChartNumber(plot.y + plot.height / 2)}" fill="#3f4a60" ${font} text-anchor="middle" transform="rotate(-90 ${roundChartNumber(leftX)} ${roundChartNumber(plot.y + plot.height / 2)})">${escapeChartXml(left)}</text>` : '',
2774
+ top ? `<text data-axis-title="top" x="${roundChartNumber(plot.x + plot.width / 2)}" y="${roundChartNumber(plot.y - 7)}" fill="#3f4a60" ${font} text-anchor="middle">${escapeChartXml(top)}</text>` : '',
2775
+ right ? `<text data-axis-title="right" x="${roundChartNumber(rightX)}" y="${roundChartNumber(plot.y + plot.height / 2)}" fill="#3f4a60" ${font} text-anchor="middle" transform="rotate(90 ${roundChartNumber(rightX)} ${roundChartNumber(plot.y + plot.height / 2)})">${escapeChartXml(right)}</text>` : ''
2776
+ ].join('');
2777
+ }
2778
+ function chartAxisTitleText(title) {
2779
+ return title?.trim() ? truncateChartText(title.trim(), 54) : '';
2780
+ }
2781
+ function doughnutChartSvg(chart, plot) {
2782
+ const hasValues = chart.series.some((series)=>series.values.some((value)=>Math.max(0, finiteChartNumber(value)) > 0));
2783
+ if (!hasValues) return emptyChartSvg(plot, '没有可绘制的数据');
2784
+ const centerX = plot.x + plot.width / 2;
2785
+ const centerY = plot.y + plot.height / 2;
2786
+ const radius = Math.max(24, 0.4 * Math.min(plot.width, plot.height));
2787
+ const holeSize = normalizeWorkSpreadsheetDoughnutHoleSize(chart.doughnutHoleSize);
2788
+ const holeRadius = holeSize / 100 * radius;
2789
+ const ringWidth = (radius - holeRadius) / Math.max(1, chart.series.length);
2790
+ const ringGap = Math.min(1, 0.18 * ringWidth);
2791
+ const rings = chart.series.map((_, seriesIndex)=>{
2792
+ const outerRadius = radius - seriesIndex * ringWidth;
2793
+ const innerRadius = holeRadius + (chart.series.length - seriesIndex - 1) * ringWidth + (seriesIndex < chart.series.length - 1 ? ringGap : 0);
2794
+ return `<g data-doughnut-series="${seriesIndex}">${doughnutRingSvg(chart, seriesIndex, centerX, centerY, innerRadius, outerRadius)}</g>`;
2795
+ }).join('');
2796
+ return `<g data-hole-size="${holeSize}">${rings}</g>`;
2797
+ }
2798
+ function doughnutRingSvg(chart, seriesIndex, centerX, centerY, innerRadius, outerRadius) {
2799
+ const values = chart.series[seriesIndex]?.values ?? [];
2800
+ const usableValues = values.map((value)=>Math.max(0, finiteChartNumber(value)));
2801
+ const total = usableValues.reduce((sum, value)=>sum + value, 0);
2802
+ if (!total) return '';
2803
+ const positiveIndices = usableValues.flatMap((value, index)=>value > 0 ? [
2804
+ index
2805
+ ] : []);
2806
+ if (1 === positiveIndices.length) {
2807
+ const index = positiveIndices[0];
2808
+ const middleRadius = (innerRadius + outerRadius) / 2;
2809
+ const fill = spreadsheetChartSeriesFillStyle(chart.series[seriesIndex], seriesIndex, 1, WORK_SPREADSHEET_CHART_COLORS[index % WORK_SPREADSHEET_CHART_COLORS.length]);
2810
+ return `<circle cx="${roundChartNumber(centerX)}" cy="${roundChartNumber(centerY)}" r="${roundChartNumber(middleRadius)}" fill="none" stroke="${fill.color}" stroke-opacity="${fill.opacity}" stroke-width="${roundChartNumber(outerRadius - innerRadius)}" data-chart-slice="${seriesIndex}:${index}"/>${spreadsheetDataLabelSvg(chart, seriesIndex, index, {
2811
+ kind: 'circular',
2812
+ centerX,
2813
+ centerY,
2814
+ angle: 0,
2815
+ innerRadius,
2816
+ outerRadius
2817
+ })}`;
2818
+ }
2819
+ let angle = -Math.PI / 2;
2820
+ return usableValues.map((value, index)=>{
2821
+ const nextAngle = angle + value / total * Math.PI * 2;
2822
+ const middleAngle = angle + (nextAngle - angle) / 2;
2823
+ if (!value) {
2824
+ angle = nextAngle;
2825
+ return '';
2826
+ }
2827
+ const largeArc = nextAngle - angle > Math.PI ? 1 : 0;
2828
+ const outerStartX = centerX + Math.cos(angle) * outerRadius;
2829
+ const outerStartY = centerY + Math.sin(angle) * outerRadius;
2830
+ const outerEndX = centerX + Math.cos(nextAngle) * outerRadius;
2831
+ const outerEndY = centerY + Math.sin(nextAngle) * outerRadius;
2832
+ const innerEndX = centerX + Math.cos(nextAngle) * innerRadius;
2833
+ const innerEndY = centerY + Math.sin(nextAngle) * innerRadius;
2834
+ const innerStartX = centerX + Math.cos(angle) * innerRadius;
2835
+ const innerStartY = centerY + Math.sin(angle) * innerRadius;
2836
+ const path = [
2837
+ `M ${roundChartNumber(outerStartX)} ${roundChartNumber(outerStartY)}`,
2838
+ `A ${roundChartNumber(outerRadius)} ${roundChartNumber(outerRadius)} 0 ${largeArc} 1 ${roundChartNumber(outerEndX)} ${roundChartNumber(outerEndY)}`,
2839
+ `L ${roundChartNumber(innerEndX)} ${roundChartNumber(innerEndY)}`,
2840
+ `A ${roundChartNumber(innerRadius)} ${roundChartNumber(innerRadius)} 0 ${largeArc} 0 ${roundChartNumber(innerStartX)} ${roundChartNumber(innerStartY)}`,
2841
+ 'Z'
2842
+ ].join(' ');
2843
+ angle = nextAngle;
2844
+ const fill = spreadsheetChartSeriesFillStyle(chart.series[seriesIndex], seriesIndex, 1, WORK_SPREADSHEET_CHART_COLORS[index % WORK_SPREADSHEET_CHART_COLORS.length]);
2845
+ const line = spreadsheetChartSeriesLineStyle(chart.series[seriesIndex], seriesIndex, 1, '#FFFFFF');
2846
+ return `<path data-chart-slice="${seriesIndex}:${index}" d="${path}" ${fill.attributes} ${line.attributes}/>${spreadsheetDataLabelSvg(chart, seriesIndex, index, {
2847
+ kind: 'circular',
2848
+ centerX,
2849
+ centerY,
2850
+ angle: middleAngle,
2851
+ innerRadius,
2852
+ outerRadius
2853
+ })}`;
2854
+ }).join('');
2855
+ }
2856
+ function radarChartSvg(chart, plot) {
2857
+ const categoryCount = Math.max(0, chart.categories.length, ...chart.series.map((series)=>series.values.length));
2858
+ if (categoryCount < 3) return emptyChartSvg(plot, '雷达图至少需要三个分类');
2859
+ const values = chart.series.flatMap((series)=>series.values).map(finiteChartNumber);
2860
+ if (!values.length) return emptyChartSvg(plot, '没有可绘制的数据');
2861
+ const axis = chart.axes?.left;
2862
+ const categoryAxis = chart.axes?.bottom;
2863
+ const scale = spreadsheetChartAxisScale(values, axis, {
2864
+ includeZero: true
2865
+ });
2866
+ const centerX = plot.x + plot.width / 2;
2867
+ const centerY = plot.y + plot.height / 2;
2868
+ const radius = Math.max(24, 0.36 * Math.min(plot.width, plot.height));
2869
+ const point = (index, ratio)=>{
2870
+ const visualIndex = spreadsheetChartCategoryVisualIndex(index, categoryCount, categoryAxis);
2871
+ const angle = -Math.PI / 2 + visualIndex / categoryCount * Math.PI * 2;
2872
+ return [
2873
+ centerX + Math.cos(angle) * radius * ratio,
2874
+ centerY + Math.sin(angle) * radius * ratio
2875
+ ];
2876
+ };
2877
+ const polygon = (ratio)=>Array.from({
2878
+ length: categoryCount
2879
+ }, (_, index)=>point(index, ratio).map(roundChartNumber).join(',')).join(' ');
2880
+ const showGridlines = spreadsheetChartAxisGridlinesVisible(axis, chart.type, 'left');
2881
+ const grid = scale.ticks.filter((tick)=>tick > scale.minimum).map((tick, index)=>{
2882
+ const ratio = spreadsheetChartAxisValueRatio(tick, scale, axis);
2883
+ const labelVisible = (axis?.labelPosition ?? 'nextTo') !== 'none';
2884
+ const tickY = centerY - radius * ratio;
2885
+ return `${showGridlines ? `<polygon points="${polygon(ratio)}" fill="none" stroke="#dfe4ec" stroke-width="1"/>` : ''}${spreadsheetChartMajorTickSvg(axis, 'left', index, centerX, tickY)}${labelVisible ? `<text data-axis-tick="left" x="${roundChartNumber(centerX + 4)}" y="${roundChartNumber(tickY + 10)}" fill="#717b8f" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="9" text-anchor="start">${escapeChartXml(formatSpreadsheetChartAxisNumber(tick, axis?.numberFormat))}</text>` : ''}`;
2886
+ }).join('');
2887
+ const spokes = Array.from({
2888
+ length: categoryCount
2889
+ }, (_, index)=>{
2890
+ const [x, y] = point(index, 1);
2891
+ return `<line x1="${roundChartNumber(centerX)}" y1="${roundChartNumber(centerY)}" x2="${roundChartNumber(x)}" y2="${roundChartNumber(y)}" stroke="#e8ebf0" stroke-width="1"/>`;
2892
+ }).join('');
2893
+ const labels = Array.from({
2894
+ length: categoryCount
2895
+ }, (_, index)=>{
2896
+ const [x, y] = point(index, 1.14);
2897
+ const [tickX, tickY] = point(index, 1);
2898
+ const horizontal = x - centerX;
2899
+ const anchor = Math.abs(horizontal) < 8 ? 'middle' : horizontal > 0 ? 'start' : 'end';
2900
+ const visible = (categoryAxis?.labelPosition ?? 'nextTo') !== 'none' && spreadsheetChartCategoryLabelVisible(index, categoryCount, categoryAxis);
2901
+ return `${spreadsheetChartMajorTickSvg(categoryAxis, 'bottom', index, tickX, tickY)}${visible ? `<text data-axis-category-label="${index}" x="${roundChartNumber(x)}" y="${roundChartNumber(y + 4)}" fill="#536078" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="10" text-anchor="${anchor}">${escapeChartXml(truncateChartText(chart.categories[index] ?? String(index + 1), 13))}</text>` : ''}`;
2902
+ }).join('');
2903
+ const style = normalizeWorkSpreadsheetRadarStyle(chart.radarStyle);
2904
+ const series = chart.series.map((item, seriesIndex)=>{
2905
+ const customStyle = normalizeWorkSpreadsheetChartSeriesStyle(item.style);
2906
+ const fill = spreadsheetChartSeriesFillStyle(item, seriesIndex, customStyle?.fillColor ? 1 : 'filled' === style ? 0.24 : 0);
2907
+ const line = spreadsheetChartSeriesLineStyle(item, seriesIndex, 2.2);
2908
+ const points = Array.from({
2909
+ length: categoryCount
2910
+ }, (_, index)=>{
2911
+ const value = finiteChartNumber(item.values[index] ?? 0);
2912
+ return point(index, spreadsheetChartAxisValueRatio(value, scale, axis));
2913
+ });
2914
+ const pointList = points.map(([x, y])=>`${roundChartNumber(x)},${roundChartNumber(y)}`).join(' ');
2915
+ const markers = points.map(([x, y])=>spreadsheetChartSeriesMarkerSvg(item, seriesIndex, x, y, {
2916
+ visible: 'marker' === style,
2917
+ defaultSize: 5
2918
+ })).join('');
2919
+ const dataLabels = points.map(([x, y], pointIndex)=>spreadsheetDataLabelSvg(chart, seriesIndex, pointIndex, {
2920
+ kind: 'point',
2921
+ x,
2922
+ y
2923
+ })).join('');
2924
+ return `<g data-radar-series="${seriesIndex}"><polygon points="${pointList}" ${fill.attributes} ${line.attributes} stroke-linejoin="round"/>${markers}${dataLabels}</g>`;
2925
+ }).join('');
2926
+ return `<g data-radar-style="${style}"><g ${spreadsheetChartAxisScaleAttributes(axis, scale, chart.type, 'left')}>${grid}</g>${spokes}${series}<g ${spreadsheetChartAxisDisplayAttributes(categoryAxis, chart.type, 'bottom')}>${labels}</g></g>`;
2927
+ }
2928
+ function pieChartSvg(chart, plot) {
2929
+ const values = chart.series[0]?.values ?? [];
2930
+ const usableValues = values.map((value)=>Math.max(0, finiteChartNumber(value)));
2931
+ const total = usableValues.reduce((sum, value)=>sum + value, 0);
2932
+ if (!total) return emptyChartSvg(plot, '没有可绘制的数据');
2933
+ const centerX = plot.x + plot.width / 2;
2934
+ const centerY = plot.y + plot.height / 2;
2935
+ const radius = Math.max(24, 0.38 * Math.min(plot.width, plot.height));
2936
+ let angle = -Math.PI / 2;
2937
+ return usableValues.map((value, index)=>{
2938
+ const nextAngle = angle + value / total * Math.PI * 2;
2939
+ const middleAngle = angle + (nextAngle - angle) / 2;
2940
+ const largeArc = nextAngle - angle > Math.PI ? 1 : 0;
2941
+ const startX = centerX + Math.cos(angle) * radius;
2942
+ const startY = centerY + Math.sin(angle) * radius;
2943
+ const endX = centerX + Math.cos(nextAngle) * radius;
2944
+ const endY = centerY + Math.sin(nextAngle) * radius;
2945
+ const series = chart.series[0];
2946
+ const fill = spreadsheetChartSeriesFillStyle(series, 0, 1, WORK_SPREADSHEET_CHART_COLORS[index % WORK_SPREADSHEET_CHART_COLORS.length]);
2947
+ const line = spreadsheetChartSeriesLineStyle(series, 0, 0, '#FFFFFF');
2948
+ const path = 1 === usableValues.length ? `<circle data-chart-slice="0:${index}" cx="${roundChartNumber(centerX)}" cy="${roundChartNumber(centerY)}" r="${roundChartNumber(radius)}" ${fill.attributes} ${line.attributes}/>` : `<path data-chart-slice="0:${index}" d="M ${roundChartNumber(centerX)} ${roundChartNumber(centerY)} L ${roundChartNumber(startX)} ${roundChartNumber(startY)} A ${roundChartNumber(radius)} ${roundChartNumber(radius)} 0 ${largeArc} 1 ${roundChartNumber(endX)} ${roundChartNumber(endY)} Z" ${fill.attributes} ${line.attributes}/>`;
2949
+ angle = nextAngle;
2950
+ return `${path}${spreadsheetDataLabelSvg(chart, 0, index, {
2951
+ kind: 'circular',
2952
+ centerX,
2953
+ centerY,
2954
+ angle: middleAngle,
2955
+ innerRadius: 0,
2956
+ outerRadius: radius
2957
+ })}`;
2958
+ }).join('');
2959
+ }
2960
+ function xyChartSvg(chart, plot) {
2961
+ const showNegativeBubbles = true === chart.showNegativeBubbles;
2962
+ const seriesPoints = chart.series.map((series)=>spreadsheetXySeriesPoints(series, 'bubble' === chart.type, showNegativeBubbles));
2963
+ const points = seriesPoints.flat();
2964
+ if (!points.length) return emptyChartSvg(plot, 'bubble' === chart.type ? '气泡图需要 X、Y 和非零气泡大小数据' : '散点图需要 X 和 Y 数值');
2965
+ const trendlinePoints = chart.series.flatMap((series)=>spreadsheetSeriesTrendlineFits(series, spreadsheetTrendlineXValues(series)).flatMap((item)=>item.fit.points));
2966
+ const scaledPoints = [
2967
+ ...points,
2968
+ ...trendlinePoints
2969
+ ];
2970
+ const paddingRatio = 'bubble' === chart.type ? 0.18 : 0.06;
2971
+ const xBounds = spreadsheetChartAxisScale([
2972
+ ...scaledPoints.map((point)=>point.x),
2973
+ ...chart.series.flatMap((series)=>spreadsheetSeriesErrorBarBounds(series, chart.type, 'x'))
2974
+ ], chart.axes?.bottom, {
2975
+ paddingRatio
2976
+ });
2977
+ const yBounds = spreadsheetChartAxisScale([
2978
+ ...scaledPoints.map((point)=>point.y),
2979
+ ...chart.series.flatMap((series)=>spreadsheetSeriesErrorBarBounds(series, chart.type, 'y'))
2980
+ ], chart.axes?.left, {
2981
+ paddingRatio
2982
+ });
2983
+ const pointX = (value)=>plot.x + spreadsheetChartAxisValueRatio(value, xBounds, chart.axes?.bottom) * plot.width;
2984
+ const pointY = (value)=>plot.y + (1 - spreadsheetChartAxisValueRatio(value, yBounds, chart.axes?.left)) * plot.height;
2985
+ const axes = spreadsheetXyChartAxesSvg(chart, plot, xBounds, yBounds);
2986
+ const errorBars = chart.series.map((series, seriesIndex)=>spreadsheetSeriesErrorBarsSvg(chart, seriesIndex, (direction, pointIndex, value)=>{
2987
+ const x = series.xValues?.[pointIndex] ?? pointIndex + 1;
2988
+ const y = series.values[pointIndex] ?? 0;
2989
+ return 'x' === direction ? [
2990
+ pointX(value),
2991
+ pointY(y)
2992
+ ] : [
2993
+ pointX(x),
2994
+ pointY(value)
2995
+ ];
2996
+ })).join('');
2997
+ const trendlines = chart.series.map((series, seriesIndex)=>spreadsheetSeriesTrendlinesSvg(series, seriesIndex, spreadsheetTrendlineXValues(series), (x, y)=>[
2998
+ pointX(x),
2999
+ pointY(y)
3000
+ ])).join('');
3001
+ if ('bubble' === chart.type) return `${axes}${errorBars}${bubbleSeriesSvg(chart, seriesPoints, pointX, pointY)}${trendlines}`;
3002
+ return `${axes}${errorBars}${scatterSeriesSvg(chart, seriesPoints, pointX, pointY)}${trendlines}`;
3003
+ }
3004
+ function spreadsheetTrendlineXValues(series) {
3005
+ return series.values.map((_, index)=>series.xValues?.[index] ?? index + 1);
3006
+ }
3007
+ function spreadsheetXySeriesPoints(series, requireBubbleSize, showNegativeBubbles) {
3008
+ return series.values.flatMap((rawY, sourceIndex)=>{
3009
+ const rawX = series.xValues?.[sourceIndex] ?? sourceIndex + 1;
3010
+ if (!Number.isFinite(rawX) || !Number.isFinite(rawY)) return [];
3011
+ if (!requireBubbleSize) return [
3012
+ {
3013
+ x: rawX,
3014
+ y: rawY,
3015
+ sourceIndex
3016
+ }
3017
+ ];
3018
+ const bubbleSize = series.bubbleSizes?.[sourceIndex];
3019
+ if ('number' != typeof bubbleSize || !Number.isFinite(bubbleSize) || 0 === bubbleSize || !showNegativeBubbles && bubbleSize < 0) return [];
3020
+ return [
3021
+ {
3022
+ x: rawX,
3023
+ y: rawY,
3024
+ sourceIndex,
3025
+ bubbleSize
3026
+ }
3027
+ ];
3028
+ });
3029
+ }
3030
+ function scatterSeriesSvg(chart, seriesPoints, pointX, pointY) {
3031
+ const style = normalizeWorkSpreadsheetScatterStyle(chart.scatterStyle);
3032
+ const showLine = 'marker' !== style;
3033
+ const showMarkers = 'marker' === style || 'lineMarker' === style || 'smoothMarker' === style;
3034
+ const smooth = 'smooth' === style || 'smoothMarker' === style;
3035
+ const series = seriesPoints.map((points, seriesIndex)=>{
3036
+ const sourceSeries = chart.series[seriesIndex];
3037
+ const lineStyle = spreadsheetChartSeriesLineStyle(sourceSeries, seriesIndex, 2.3);
3038
+ const positioned = points.map((point)=>({
3039
+ ...point,
3040
+ plotX: pointX(point.x),
3041
+ plotY: pointY(point.y)
3042
+ }));
3043
+ const line = showLine && positioned.length > 1 ? `<path data-scatter-line="${seriesIndex}" d="${scatterPath(positioned, smooth)}" fill="none" ${lineStyle.attributes} stroke-linejoin="round" stroke-linecap="round"/>` : '';
3044
+ const markers = positioned.map((point)=>spreadsheetChartSeriesMarkerSvg(sourceSeries, seriesIndex, point.plotX, point.plotY, {
3045
+ visible: showMarkers,
3046
+ defaultSize: 5.67,
3047
+ attributes: `data-scatter-marker="${seriesIndex}:${point.sourceIndex}" data-point-x="${roundChartNumber(point.x)}" data-point-y="${roundChartNumber(point.y)}"`
3048
+ })).join('');
3049
+ const dataLabels = positioned.map((point)=>spreadsheetDataLabelSvg(chart, seriesIndex, point.sourceIndex, {
3050
+ kind: 'point',
3051
+ x: point.plotX,
3052
+ y: point.plotY
3053
+ })).join('');
3054
+ return `<g data-scatter-series="${seriesIndex}">${line}${markers}${dataLabels}</g>`;
3055
+ }).join('');
3056
+ return `<g data-scatter-style="${style}">${series}</g>`;
3057
+ }
3058
+ function scatterPath(points, smooth) {
3059
+ if (!points.length) return '';
3060
+ if (!smooth || points.length < 3) return points.map((point, index)=>`${index ? 'L' : 'M'} ${roundChartNumber(point.plotX)} ${roundChartNumber(point.plotY)}`).join(' ');
3061
+ const commands = [
3062
+ `M ${roundChartNumber(points[0].plotX)} ${roundChartNumber(points[0].plotY)}`
3063
+ ];
3064
+ for(let index = 0; index < points.length - 1; index += 1){
3065
+ const previous = points[index - 1] ?? points[index];
3066
+ const current = points[index];
3067
+ const next = points[index + 1];
3068
+ const following = points[index + 2] ?? next;
3069
+ const control1X = current.plotX + (next.plotX - previous.plotX) / 6;
3070
+ const control1Y = current.plotY + (next.plotY - previous.plotY) / 6;
3071
+ const control2X = next.plotX - (following.plotX - current.plotX) / 6;
3072
+ const control2Y = next.plotY - (following.plotY - current.plotY) / 6;
3073
+ commands.push(`C ${roundChartNumber(control1X)} ${roundChartNumber(control1Y)} ${roundChartNumber(control2X)} ${roundChartNumber(control2Y)} ${roundChartNumber(next.plotX)} ${roundChartNumber(next.plotY)}`);
3074
+ }
3075
+ return commands.join(' ');
3076
+ }
3077
+ function bubbleSeriesSvg(chart, seriesPoints, pointX, pointY) {
3078
+ const scale = normalizeWorkSpreadsheetBubbleScale(chart.bubbleScale);
3079
+ const sizeRepresents = normalizeWorkSpreadsheetBubbleSizeRepresents(chart.bubbleSizeRepresents);
3080
+ const maximumSize = Math.max(1, ...seriesPoints.flatMap((points)=>points.map((point)=>Math.abs(point.bubbleSize ?? 0))));
3081
+ const maximumRadius = Math.max(0, Math.min(54, scale / 100 * 18));
3082
+ const series = seriesPoints.map((points, seriesIndex)=>{
3083
+ const sourceSeries = chart.series[seriesIndex];
3084
+ const sourceStyle = normalizeWorkSpreadsheetChartSeriesStyle(sourceSeries.style);
3085
+ const fill = spreadsheetChartSeriesFillStyle(sourceSeries, seriesIndex, 0.34);
3086
+ const line = spreadsheetChartSeriesLineStyle(sourceSeries, seriesIndex, 1.8);
3087
+ const bubbles = points.map((point)=>{
3088
+ const size = point.bubbleSize ?? 0;
3089
+ const ratio = Math.abs(size) / maximumSize;
3090
+ const radius = maximumRadius * ('area' === sizeRepresents ? Math.sqrt(ratio) : ratio);
3091
+ const negative = size < 0;
3092
+ const bubbleOpacity = sourceStyle?.fillTransparency === void 0 ? negative ? 0.2 : 0.34 : fill.opacity;
3093
+ return `<circle data-bubble-point="${seriesIndex}:${point.sourceIndex}" data-point-x="${roundChartNumber(point.x)}" data-point-y="${roundChartNumber(point.y)}" data-bubble-size="${roundChartNumber(size)}" data-negative-bubble="${negative}" cx="${roundChartNumber(pointX(point.x))}" cy="${roundChartNumber(pointY(point.y))}" r="${roundChartNumber(radius)}" fill="${fill.color}" fill-opacity="${bubbleOpacity}" ${line.attributes}${negative && 'solid' === line.dash ? ' stroke-dasharray="3 2"' : ''}/>${spreadsheetDataLabelSvg(chart, seriesIndex, point.sourceIndex, {
3094
+ kind: 'point',
3095
+ x: pointX(point.x),
3096
+ y: pointY(point.y) - radius
3097
+ })}`;
3098
+ }).join('');
3099
+ return `<g data-bubble-series="${seriesIndex}">${bubbles}</g>`;
3100
+ }).join('');
3101
+ return `<g data-bubble-scale="${scale}" data-show-negative-bubbles="${true === chart.showNegativeBubbles}" data-bubble-size-represents="${sizeRepresents}">${series}</g>`;
3102
+ }
3103
+ function cartesianChartSvg(chart, plot) {
3104
+ const layout = spreadsheetChartSeriesLayout(chart);
3105
+ const trendlineValues = layout.stacked ? [] : chart.series.flatMap((series)=>spreadsheetSeriesTrendlineFits(series, categoryTrendlineXValues(series)).flatMap((item)=>item.fit.points.map((point)=>point.y)));
3106
+ const values = [
3107
+ ...layout.scaleValues,
3108
+ ...layout.stacked ? [] : chart.series.flatMap((series)=>spreadsheetSeriesErrorBarBounds(series, chart.type, 'y')),
3109
+ ...trendlineValues
3110
+ ];
3111
+ if (!values.length) return emptyChartSvg(plot, '没有可绘制的数据');
3112
+ if ('bar' === chart.type) return horizontalBarChartSvg(chart, plot, layout, values);
3113
+ const axis = chart.axes?.left;
3114
+ const scale = spreadsheetChartAxisScale(values, axis, {
3115
+ includeZero: true
3116
+ });
3117
+ const valuePosition = (value)=>plot.y + (1 - spreadsheetChartAxisValueRatio(value, scale, axis)) * plot.height;
3118
+ const baseline = valuePosition(0);
3119
+ const numberFormat = axis?.numberFormat ?? ('percentStacked' === layout.grouping ? '0%' : void 0);
3120
+ const showGridlines = spreadsheetChartAxisGridlinesVisible(axis, chart.type, 'left');
3121
+ const grid = scale.ticks.map((value, index)=>{
3122
+ const y = valuePosition(value);
3123
+ const label = spreadsheetChartAxisLabelLayout(axis, chart.type, 'left', plot, plot.x, y);
3124
+ return `${showGridlines ? `<line x1="${plot.x}" y1="${roundChartNumber(y)}" x2="${roundChartNumber(plot.x + plot.width)}" y2="${roundChartNumber(y)}" stroke="#e8ebf0" stroke-width="1"/>` : ''}${spreadsheetChartMajorTickSvg(axis, 'left', index, plot.x, y)}${label ? `<text data-axis-tick="left" x="${roundChartNumber(label.x)}" y="${roundChartNumber(label.y)}" fill="#717b8f" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="10" text-anchor="${label.textAnchor}">${escapeChartXml(formatSpreadsheetChartAxisNumber(value, numberFormat))}</text>` : ''}`;
3125
+ }).join('');
3126
+ const axes = [
3127
+ `<g ${spreadsheetChartAxisScaleAttributes(axis, scale, chart.type, 'left')}>${grid}<line x1="${plot.x}" y1="${plot.y}" x2="${plot.x}" y2="${roundChartNumber(plot.y + plot.height)}" stroke="#aeb6c4" stroke-width="1.2"/></g>`,
3128
+ `<g ${spreadsheetChartAxisDisplayAttributes(chart.axes?.bottom, chart.type, 'bottom')}><line x1="${plot.x}" y1="${roundChartNumber(baseline)}" x2="${roundChartNumber(plot.x + plot.width)}" y2="${roundChartNumber(baseline)}" stroke="#aeb6c4" stroke-width="1.2"/></g>`
3129
+ ].join('');
3130
+ const trendlines = layout.stacked ? '' : cartesianTrendlinesSvg(chart, plot, valuePosition);
3131
+ if ('line' === chart.type || 'area' === chart.type) return `${axes}${spreadsheetLineChartSvg(chart, plot, valuePosition, layout)}${trendlines}${categoryLabelsSvg(chart, plot, baseline)}`;
3132
+ return `${axes}${barChartSvg(chart, plot, valuePosition, baseline, layout)}${trendlines}${categoryLabelsSvg(chart, plot, baseline)}`;
3133
+ }
3134
+ function cartesianTrendlinesSvg(chart, plot, valuePosition) {
3135
+ const categoryCount = Math.max(1, chart.categories.length, ...chart.series.map((series)=>series.values.length));
3136
+ const categoryAxis = chart.axes?.bottom;
3137
+ const categoryPosition = (value)=>1 === categoryCount ? plot.x + plot.width / 2 : plot.x + spreadsheetChartCategoryVisualIndex(value - 1, categoryCount, categoryAxis) / Math.max(1, categoryCount - 1) * plot.width;
3138
+ return chart.series.map((series, seriesIndex)=>spreadsheetSeriesTrendlinesSvg(series, seriesIndex, categoryTrendlineXValues(series), (x, y)=>[
3139
+ categoryPosition(x),
3140
+ valuePosition(y)
3141
+ ])).join('');
3142
+ }
3143
+ function categoryTrendlineXValues(series) {
3144
+ return series.values.map((_, index)=>index + 1);
3145
+ }
3146
+ function barChartSvg(chart, plot, valuePosition, baseline, layout) {
3147
+ const categoryCount = layout.categoryCount;
3148
+ const seriesCount = Math.max(1, chart.series.length);
3149
+ const groupWidth = plot.width / categoryCount;
3150
+ const categoryAxis = chart.axes?.bottom;
3151
+ const geometry = spreadsheetChartBarGeometry(groupWidth, seriesCount, chart, 44);
3152
+ return layout.series.map((points, seriesIndex)=>{
3153
+ const seriesX = (pointIndex)=>plot.x + spreadsheetChartCategoryVisualIndex(pointIndex, categoryCount, categoryAxis) * groupWidth + geometry.offset(seriesIndex);
3154
+ const renderedWidth = geometry.renderedSize;
3155
+ const bars = points.map((point)=>{
3156
+ const startY = layout.stacked ? valuePosition(point.start) : baseline;
3157
+ const endY = valuePosition(point.end);
3158
+ const x = seriesX(point.categoryIndex);
3159
+ return `<rect data-chart-bar="${seriesIndex}:${point.categoryIndex}" data-stack-start="${roundChartNumber(point.start)}" data-stack-end="${roundChartNumber(point.end)}" x="${roundChartNumber(x)}" y="${roundChartNumber(Math.min(endY, startY))}" width="${roundChartNumber(renderedWidth)}" height="${roundChartNumber(Math.max(1, Math.abs(endY - startY)))}" rx="1.5" ${spreadsheetChartSeriesFillStyle(chart.series[seriesIndex], seriesIndex).attributes} ${spreadsheetChartSeriesLineStyle(chart.series[seriesIndex], seriesIndex, 0).attributes}/>${spreadsheetDataLabelSvg(chart, seriesIndex, point.categoryIndex, {
3160
+ kind: 'verticalBar',
3161
+ x: x + renderedWidth / 2,
3162
+ valueY: endY,
3163
+ baselineY: startY,
3164
+ value: point.rawValue
3165
+ })}`;
3166
+ }).join('');
3167
+ const errorBars = layout.stacked ? '' : spreadsheetSeriesErrorBarsSvg(chart, seriesIndex, (_direction, pointIndex, value)=>[
3168
+ seriesX(pointIndex) + renderedWidth / 2,
3169
+ valuePosition(value)
3170
+ ]);
3171
+ return `${bars}${errorBars}`;
3172
+ }).join('');
3173
+ }
3174
+ function horizontalBarChartSvg(chart, plot, layout, values) {
3175
+ const axis = chart.axes?.bottom;
3176
+ const scale = spreadsheetChartAxisScale(values, axis, {
3177
+ includeZero: true
3178
+ });
3179
+ const valuePosition = (value)=>plot.x + spreadsheetChartAxisValueRatio(value, scale, axis) * plot.width;
3180
+ const zero = valuePosition(0);
3181
+ const categoryCount = layout.categoryCount;
3182
+ const seriesCount = Math.max(1, chart.series.length);
3183
+ const groupHeight = plot.height / categoryCount;
3184
+ const geometry = spreadsheetChartBarGeometry(groupHeight, seriesCount, chart, 30);
3185
+ const categoryAxis = chart.axes?.left;
3186
+ const numberFormat = axis?.numberFormat ?? ('percentStacked' === layout.grouping ? '0%' : void 0);
3187
+ const showGridlines = spreadsheetChartAxisGridlinesVisible(axis, chart.type, 'bottom');
3188
+ const grid = scale.ticks.map((value, index)=>{
3189
+ const x = valuePosition(value);
3190
+ const label = spreadsheetChartAxisLabelLayout(axis, chart.type, 'bottom', plot, x, plot.y + plot.height);
3191
+ return `${showGridlines ? `<line x1="${roundChartNumber(x)}" y1="${plot.y}" x2="${roundChartNumber(x)}" y2="${roundChartNumber(plot.y + plot.height)}" stroke="#e8ebf0" stroke-width="1"/>` : ''}${spreadsheetChartMajorTickSvg(axis, 'bottom', index, x, plot.y + plot.height)}${label ? `<text data-axis-tick="bottom" x="${roundChartNumber(label.x)}" y="${roundChartNumber(label.y)}" fill="#717b8f" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif" font-size="10" text-anchor="${label.textAnchor}">${escapeChartXml(formatSpreadsheetChartAxisNumber(value, numberFormat))}</text>` : ''}`;
3192
+ }).join('');
3193
+ const labels = Array.from({
3194
+ length: categoryCount
3195
+ }, (_, index)=>{
3196
+ const visible = spreadsheetChartCategoryLabelVisible(index, categoryCount, categoryAxis);
3197
+ const label = chart.categories[index] ?? String(index + 1);
3198
+ const visualIndex = spreadsheetChartCategoryVisualIndex(index, categoryCount, categoryAxis);
3199
+ const y = plot.y + (visualIndex + 0.5) * groupHeight;
3200
+ const labelLayout = spreadsheetChartAxisLabelLayout(categoryAxis, chart.type, 'left', plot, zero, y);
3201
+ return `${spreadsheetChartMajorTickSvg(categoryAxis, 'left', index, zero, y)}${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, 11))}</text>` : ''}`;
3202
+ }).join('');
3203
+ const bars = layout.series.map((points, seriesIndex)=>{
3204
+ const seriesY = (pointIndex)=>plot.y + spreadsheetChartCategoryVisualIndex(pointIndex, categoryCount, categoryAxis) * groupHeight + geometry.offset(seriesIndex);
3205
+ const renderedHeight = geometry.renderedSize;
3206
+ const fill = spreadsheetChartSeriesFillStyle(chart.series[seriesIndex], seriesIndex);
3207
+ const line = spreadsheetChartSeriesLineStyle(chart.series[seriesIndex], seriesIndex, 0);
3208
+ const seriesBars = points.map((point)=>{
3209
+ const startX = layout.stacked ? valuePosition(point.start) : zero;
3210
+ const endX = valuePosition(point.end);
3211
+ const y = seriesY(point.categoryIndex);
3212
+ return `<rect data-chart-bar="${seriesIndex}:${point.categoryIndex}" data-stack-start="${roundChartNumber(point.start)}" data-stack-end="${roundChartNumber(point.end)}" x="${roundChartNumber(Math.min(endX, startX))}" y="${roundChartNumber(y)}" width="${roundChartNumber(Math.max(1, Math.abs(endX - startX)))}" height="${roundChartNumber(renderedHeight)}" rx="1.5" ${fill.attributes} ${line.attributes}/>${spreadsheetDataLabelSvg(chart, seriesIndex, point.categoryIndex, {
3213
+ kind: 'horizontalBar',
3214
+ y: y + renderedHeight / 2,
3215
+ valueX: endX,
3216
+ baselineX: startX,
3217
+ value: point.rawValue
3218
+ })}`;
3219
+ }).join('');
3220
+ const errorBars = layout.stacked ? '' : spreadsheetSeriesErrorBarsSvg(chart, seriesIndex, (_direction, pointIndex, value)=>[
3221
+ valuePosition(value),
3222
+ seriesY(pointIndex) + renderedHeight / 2
3223
+ ]);
3224
+ return `${seriesBars}${errorBars}`;
3225
+ }).join('');
3226
+ const trendlines = layout.stacked ? '' : chart.series.map((series, seriesIndex)=>spreadsheetSeriesTrendlinesSvg(series, seriesIndex, categoryTrendlineXValues(series), (category, value)=>[
3227
+ valuePosition(value),
3228
+ plot.y + (spreadsheetChartCategoryVisualIndex(category - 1, categoryCount, categoryAxis) + 0.5) / categoryCount * plot.height
3229
+ ])).join('');
3230
+ 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}`;
3231
+ }
3232
+ function categoryLabelsSvg(chart, plot, axisY) {
3233
+ const categoryCount = Math.max(1, chart.categories.length, ...chart.series.map((series)=>series.values.length));
3234
+ const axis = chart.axes?.bottom;
3235
+ const labels = Array.from({
3236
+ length: categoryCount
3237
+ }, (_, index)=>{
3238
+ const visible = spreadsheetChartCategoryLabelVisible(index, categoryCount, axis);
3239
+ const label = chart.categories[index] ?? String(index + 1);
3240
+ const visualIndex = spreadsheetChartCategoryVisualIndex(index, categoryCount, axis);
3241
+ const x = plot.x + (visualIndex + 0.5) / categoryCount * plot.width;
3242
+ const labelLayout = spreadsheetChartAxisLabelLayout(axis, chart.type, 'bottom', plot, x, axisY);
3243
+ 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>` : ''}`;
3244
+ }).join('');
3245
+ return `<g ${spreadsheetChartAxisDisplayAttributes(axis, chart.type, 'bottom')}>${labels}</g>`;
3246
+ }
3247
+ function emptyChartSvg(plot, message) {
3248
+ 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>`;
3249
+ }
3250
+ const XLSX_EMU_PER_PIXEL = 9525;
3251
+ const DEFAULT_COLUMN_WIDTH = 96;
3252
+ const DEFAULT_ROW_HEIGHT = 24;
3253
+ function readXlsxDrawingAnchor(anchor) {
3254
+ const extent = directChild(anchor, 'ext');
3255
+ const position = directChild(anchor, 'pos');
3256
+ return {
3257
+ from: readMarker(directChild(anchor, 'from')),
3258
+ to: readMarker(directChild(anchor, 'to')),
3259
+ leftEmu: numberAttribute(position, 'x'),
3260
+ topEmu: numberAttribute(position, 'y'),
3261
+ widthEmu: numberAttribute(extent, 'cx'),
3262
+ heightEmu: numberAttribute(extent, 'cy')
3263
+ };
3264
+ }
3265
+ function xlsxDrawingAnchorToBounds(anchor, config) {
3266
+ const left = anchor.from ? markerPosition(anchor.from.column, anchor.from.columnOffsetEmu, (index)=>spreadsheetColumnWidth(config, index)) : emuToPixels(anchor.leftEmu);
3267
+ const top = anchor.from ? markerPosition(anchor.from.row, anchor.from.rowOffsetEmu, (index)=>spreadsheetRowHeight(config, index)) : emuToPixels(anchor.topEmu);
3268
+ const right = anchor.to ? markerPosition(anchor.to.column, anchor.to.columnOffsetEmu, (index)=>spreadsheetColumnWidth(config, index)) : left + emuToPixels(anchor.widthEmu);
3269
+ const bottom = anchor.to ? markerPosition(anchor.to.row, anchor.to.rowOffsetEmu, (index)=>spreadsheetRowHeight(config, index)) : top + emuToPixels(anchor.heightEmu);
3270
+ if (![
3271
+ left,
3272
+ top,
3273
+ right,
3274
+ bottom
3275
+ ].every(Number.isFinite)) return null;
3276
+ return {
3277
+ left: roundPosition(Math.max(0, left)),
3278
+ top: roundPosition(Math.max(0, top)),
3279
+ width: roundPosition(Math.max(1, right - left)),
3280
+ height: roundPosition(Math.max(1, bottom - top))
3281
+ };
3282
+ }
3283
+ function xlsxTwoCellAnchorMarkers(bounds, sheet) {
3284
+ const fromColumn = axisMarker(bounds.left, (index)=>spreadsheetColumnWidth(sheet.config, index));
3285
+ const toColumn = axisMarker(bounds.left + bounds.width, (index)=>spreadsheetColumnWidth(sheet.config, index));
3286
+ const fromRow = axisMarker(bounds.top, (index)=>spreadsheetRowHeight(sheet.config, index));
3287
+ const toRow = axisMarker(bounds.top + bounds.height, (index)=>spreadsheetRowHeight(sheet.config, index));
3288
+ return `${markerXml('from', fromColumn, fromRow)}${markerXml('to', toColumn, toRow)}`;
3289
+ }
3290
+ function spreadsheetColumnWidth(config, index) {
3291
+ const key = String(index);
3292
+ if (Object.hasOwn(config?.colhidden ?? {}, key)) return 0;
3293
+ const width = config?.columnlen?.[key];
3294
+ return 'number' == typeof width && width > 0 ? width : DEFAULT_COLUMN_WIDTH;
3295
+ }
3296
+ function spreadsheetRowHeight(config, index) {
3297
+ const key = String(index);
3298
+ if (Object.hasOwn(config?.rowhidden ?? {}, key)) return 0;
3299
+ const height = config?.rowlen?.[key];
3300
+ return 'number' == typeof height && height > 0 ? height : DEFAULT_ROW_HEIGHT;
3301
+ }
3302
+ function readMarker(element) {
3303
+ if (!element) return;
3304
+ return {
3305
+ column: nonNegativeInteger(directChild(element, 'col')?.textContent),
3306
+ row: nonNegativeInteger(directChild(element, 'row')?.textContent),
3307
+ columnOffsetEmu: nonNegativeNumber(directChild(element, 'colOff')?.textContent),
3308
+ rowOffsetEmu: nonNegativeNumber(directChild(element, 'rowOff')?.textContent)
3309
+ };
3310
+ }
3311
+ function numberAttribute(element, name) {
3312
+ if (!element) return;
3313
+ const value = Number(attribute(element, name));
3314
+ return Number.isFinite(value) && value >= 0 ? value : void 0;
3315
+ }
3316
+ function nonNegativeInteger(value) {
3317
+ const parsed = Number(value);
3318
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : 0;
3319
+ }
3320
+ function nonNegativeNumber(value) {
3321
+ const parsed = Number(value);
3322
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
3323
+ }
3324
+ function markerPosition(index, offsetEmu, size) {
3325
+ let position = 0;
3326
+ for(let current = 0; current < index; current += 1)position += size(current);
3327
+ return position + emuToPixels(offsetEmu);
3328
+ }
3329
+ function emuToPixels(value) {
3330
+ return (value ?? 0) / XLSX_EMU_PER_PIXEL;
3331
+ }
3332
+ function axisMarker(position, size) {
3333
+ let remaining = Math.max(0, Number.isFinite(position) ? position : 0);
3334
+ let index = 0;
3335
+ while(index < 1048576){
3336
+ const currentSize = Math.max(0, size(index));
3337
+ if (currentSize > 0 && remaining < currentSize) break;
3338
+ if (currentSize > 0) remaining -= currentSize;
3339
+ index += 1;
3340
+ }
3341
+ return {
3342
+ index,
3343
+ offsetEmu: Math.round(remaining * XLSX_EMU_PER_PIXEL)
3344
+ };
3345
+ }
3346
+ function markerXml(kind, column, row) {
3347
+ return [
3348
+ `<xdr:${kind}>`,
3349
+ `<xdr:col>${column.index}</xdr:col><xdr:colOff>${column.offsetEmu}</xdr:colOff>`,
3350
+ `<xdr:row>${row.index}</xdr:row><xdr:rowOff>${row.offsetEmu}</xdr:rowOff>`,
3351
+ `</xdr:${kind}>`
3352
+ ].join('');
3353
+ }
3354
+ function roundPosition(value) {
3355
+ return Math.round(1000 * value) / 1000;
3356
+ }
3357
+ const CHART_PREVIEW_IMAGE_PREFIX = 'work-chart-preview-';
3358
+ function spreadsheetChartCount(content) {
3359
+ return content.sheets.reduce((count, sheet)=>count + (sheet.charts?.length ?? 0), 0);
3360
+ }
3361
+ function spreadsheetSheetsWithChartPreviews(content) {
3362
+ return content.sheets.map((sheet)=>{
3363
+ const chartImages = (sheet.charts ?? []).map((chart)=>chartPreviewImage(resolveSpreadsheetChart(content, sheet, chart)));
3364
+ if (!chartImages.length) return sheet;
3365
+ return {
3366
+ ...sheet,
3367
+ images: [
3368
+ ...sheet.images ?? [],
3369
+ ...chartImages
3370
+ ]
3371
+ };
3372
+ });
3373
+ }
3374
+ function reconcileSpreadsheetChartPreviews(content, changedSheets) {
3375
+ const sourceById = new Map(content.sheets.flatMap((sheet)=>sheet.id ? [
3376
+ [
3377
+ sheet.id,
3378
+ sheet
3379
+ ]
3380
+ ] : []));
3381
+ const renamedSheets = new Map(changedSheets.flatMap((sheet)=>{
3382
+ const sourceName = sheet.id ? sourceById.get(sheet.id)?.name : void 0;
3383
+ return sourceName && sourceName !== sheet.name ? [
3384
+ [
3385
+ sourceName,
3386
+ sheet.name
3387
+ ]
3388
+ ] : [];
3389
+ }));
3390
+ const sheets = changedSheets.map((changedSheet)=>{
3391
+ const source = (changedSheet.id ? sourceById.get(changedSheet.id) : void 0) ?? changedSheet;
3392
+ const previewImages = new Map((changedSheet.images ?? []).filter((image)=>image.id.startsWith(CHART_PREVIEW_IMAGE_PREFIX)).map((image)=>[
3393
+ image.id,
3394
+ image
3395
+ ]));
3396
+ const charts = (source.charts ?? []).flatMap((sourceChart)=>{
3397
+ const chart = renameSpreadsheetChartReferences(sourceChart, renamedSheets);
3398
+ const preview = previewImages.get(chartPreviewImageId(chart.id));
3399
+ if (!preview) return [];
3400
+ return [
3401
+ {
3402
+ ...chart,
3403
+ left: finiteDimension(preview.left, chart.left),
3404
+ top: finiteDimension(preview.top, chart.top),
3405
+ width: finiteDimension(preview.width, chart.width, 1),
3406
+ height: finiteDimension(preview.height, chart.height, 1)
3407
+ }
3408
+ ];
3409
+ });
3410
+ const images = (changedSheet.images ?? []).filter((image)=>!image.id.startsWith(CHART_PREVIEW_IMAGE_PREFIX));
3411
+ return {
3412
+ ...changedSheet,
3413
+ images: images.length ? images : void 0,
3414
+ charts: charts.length ? charts : void 0,
3415
+ ...changedSheet.formulaMetadata ?? source.formulaMetadata ? {
3416
+ formulaMetadata: changedSheet.formulaMetadata ?? source.formulaMetadata
3417
+ } : {}
3418
+ };
3419
+ });
3420
+ return {
3421
+ ...content,
3422
+ sheets
3423
+ };
3424
+ }
3425
+ function resolveSpreadsheetChart(content, ownerSheet, chart) {
3426
+ const titleValue = chart.titleReference ? spreadsheetReferenceValues(content, ownerSheet, chart.titleReference)?.[0] : void 0;
3427
+ const categories = chart.categoryReference ? spreadsheetReferenceValues(content, ownerSheet, chart.categoryReference)?.map(displayCellValue) : void 0;
3428
+ const series = chart.series.map((item)=>resolveSpreadsheetChartSeries(content, ownerSheet, item));
3429
+ const axes = resolveSpreadsheetChartAxes(content, ownerSheet, chart.axes);
3430
+ return {
3431
+ ...chart,
3432
+ title: void 0 === titleValue ? chart.title : displayCellValue(titleValue),
3433
+ categories: categories ?? chart.categories,
3434
+ series,
3435
+ ...axes ? {
3436
+ axes
3437
+ } : {}
3438
+ };
3439
+ }
3440
+ function createSpreadsheetChartFromSelection(content, sheetId, selection) {
3441
+ const sheet = content.sheets.find((candidate)=>candidate.id === sheetId);
3442
+ if (!sheet) return null;
3443
+ const bounds = normalizedSelection(selection ?? sheet.luckysheet_select_save?.at(-1));
3444
+ if (!bounds) return null;
3445
+ const rowCount = bounds.endRow - bounds.startRow + 1;
3446
+ const columnCount = bounds.endColumn - bounds.startColumn + 1;
3447
+ const hasHeader = rowCount > 1 && selectionHasHeader(sheet, bounds);
3448
+ const dataStartRow = hasHeader ? bounds.startRow + 1 : bounds.startRow;
3449
+ const chartNumber = spreadsheetChartCount(content) + 1;
3450
+ const chartName = `图表 ${chartNumber}`;
3451
+ let categoryReference;
3452
+ let categories;
3453
+ let series;
3454
+ if (columnCount > 1) {
3455
+ categoryReference = cellRangeFormula(sheet.name, dataStartRow, bounds.startColumn, bounds.endRow, bounds.startColumn);
3456
+ categories = rangeValues(sheet, dataStartRow, bounds.startColumn, bounds.endRow, bounds.startColumn).map(displayCellValue);
3457
+ series = inclusiveNumbers(bounds.startColumn + 1, bounds.endColumn).map((column, index)=>({
3458
+ name: hasHeader ? displayCellValue(sheet.data?.[bounds.startRow]?.[column]?.v ?? `系列 ${index + 1}`) : `系列 ${index + 1}`,
3459
+ nameReference: hasHeader ? cellRangeFormula(sheet.name, bounds.startRow, column, bounds.startRow, column) : void 0,
3460
+ valuesReference: cellRangeFormula(sheet.name, dataStartRow, column, bounds.endRow, column),
3461
+ values: rangeValues(sheet, dataStartRow, column, bounds.endRow, column).map(numericCellValue)
3462
+ }));
3463
+ } else {
3464
+ categories = inclusiveNumbers(dataStartRow, bounds.endRow).map((row)=>String(row + 1));
3465
+ series = [
3466
+ {
3467
+ name: hasHeader ? displayCellValue(sheet.data?.[bounds.startRow]?.[bounds.startColumn]?.v ?? '系列 1') : '系列 1',
3468
+ nameReference: hasHeader ? cellRangeFormula(sheet.name, bounds.startRow, bounds.startColumn, bounds.startRow, bounds.startColumn) : void 0,
3469
+ valuesReference: cellRangeFormula(sheet.name, dataStartRow, bounds.startColumn, bounds.endRow, bounds.startColumn),
3470
+ values: rangeValues(sheet, dataStartRow, bounds.startColumn, bounds.endRow, bounds.startColumn).map(numericCellValue)
3471
+ }
3472
+ ];
3473
+ }
3474
+ const left = axisPosition(bounds.endColumn + 1, (index)=>spreadsheetColumnWidth(sheet.config, index)) + 16;
3475
+ const top = axisPosition(bounds.startRow, (index)=>spreadsheetRowHeight(sheet.config, index));
3476
+ return {
3477
+ id: createWorkId('chart'),
3478
+ name: chartName,
3479
+ altText: `${chartName},来源于 ${sheet.name} 的当前选区`,
3480
+ type: 'column',
3481
+ title: chartName,
3482
+ categories,
3483
+ categoryReference,
3484
+ series,
3485
+ showLegend: series.length > 1,
3486
+ legendPosition: 'right',
3487
+ legendOverlay: false,
3488
+ grouping: 'clustered',
3489
+ gapWidth: 150,
3490
+ overlap: 0,
3491
+ left,
3492
+ top,
3493
+ width: 480,
3494
+ height: 288
3495
+ };
3496
+ }
3497
+ function parseSpreadsheetChartReference(content, ownerSheet, reference) {
3498
+ const formula = reference.trim().replace(/^=/, '');
3499
+ const match = /^(?:(?:'((?:[^']|'')+)'|([^!]+))!)?(\$?[A-Z]{1,3}\$?[1-9]\d*)(?::(\$?[A-Z]{1,3}\$?[1-9]\d*))?$/i.exec(formula);
3500
+ if (!match) return null;
3501
+ const sheetName = (match[1]?.replaceAll("''", "'") ?? match[2])?.trim();
3502
+ if (sheetName?.includes('[') || sheetName?.includes(']')) return null;
3503
+ const sheet = sheetName ? content.sheets.find((candidate)=>candidate.name === sheetName) : ownerSheet;
3504
+ if (!sheet) return null;
3505
+ const start = decodeCellAddress(match[3]);
3506
+ const end = decodeCellAddress(match[4] ?? match[3]);
3507
+ if (!start || !end) return null;
3508
+ return {
3509
+ sheet,
3510
+ startRow: Math.min(start.row, end.row),
3511
+ endRow: Math.max(start.row, end.row),
3512
+ startColumn: Math.min(start.column, end.column),
3513
+ endColumn: Math.max(start.column, end.column)
3514
+ };
3515
+ }
3516
+ function spreadsheetReferenceValues(content, ownerSheet, reference) {
3517
+ const range = parseSpreadsheetChartReference(content, ownerSheet, reference);
3518
+ if (!range) return;
3519
+ return rangeValues(range.sheet, range.startRow, range.startColumn, range.endRow, range.endColumn);
3520
+ }
3521
+ function quoteSpreadsheetSheetName(name) {
3522
+ return /^[A-Za-z_][A-Za-z0-9_.]*$/.test(name) ? name : `'${name.replaceAll("'", "''")}'`;
3523
+ }
3524
+ function renameSpreadsheetChartReferences(chart, renamedSheets) {
3525
+ if (!renamedSheets.size) return chart;
3526
+ return {
3527
+ ...chart,
3528
+ titleReference: renameSpreadsheetReference(chart.titleReference, renamedSheets),
3529
+ axes: renameSpreadsheetChartAxesReferences(chart.axes, renamedSheets),
3530
+ categoryReference: renameSpreadsheetReference(chart.categoryReference, renamedSheets),
3531
+ series: chart.series.map((series)=>({
3532
+ ...series,
3533
+ nameReference: renameSpreadsheetReference(series.nameReference, renamedSheets),
3534
+ valuesReference: renameSpreadsheetReference(series.valuesReference, renamedSheets),
3535
+ xValuesReference: renameSpreadsheetReference(series.xValuesReference, renamedSheets),
3536
+ bubbleSizesReference: renameSpreadsheetReference(series.bubbleSizesReference, renamedSheets),
3537
+ ...series.errorBars ? {
3538
+ errorBars: series.errorBars.map((errorBars)=>({
3539
+ ...errorBars,
3540
+ plusReference: renameSpreadsheetReference(errorBars.plusReference, renamedSheets),
3541
+ minusReference: renameSpreadsheetReference(errorBars.minusReference, renamedSheets)
3542
+ }))
3543
+ } : {}
3544
+ }))
3545
+ };
3546
+ }
3547
+ function renameSpreadsheetChartAxesReferences(axes, renamedSheets) {
3548
+ if (!axes) return;
3549
+ const bottom = renameSpreadsheetChartAxisReference(axes.bottom, renamedSheets);
3550
+ const left = renameSpreadsheetChartAxisReference(axes.left, renamedSheets);
3551
+ const top = renameSpreadsheetChartAxisReference(axes.top, renamedSheets);
3552
+ const right = renameSpreadsheetChartAxisReference(axes.right, renamedSheets);
3553
+ return {
3554
+ ...bottom ? {
3555
+ bottom
3556
+ } : {},
3557
+ ...left ? {
3558
+ left
3559
+ } : {},
3560
+ ...top ? {
3561
+ top
3562
+ } : {},
3563
+ ...right ? {
3564
+ right
3565
+ } : {}
3566
+ };
3567
+ }
3568
+ function renameSpreadsheetChartAxisReference(axis, renamedSheets) {
3569
+ return axis ? {
3570
+ ...axis,
3571
+ titleReference: renameSpreadsheetReference(axis.titleReference, renamedSheets)
3572
+ } : void 0;
3573
+ }
3574
+ function renameSpreadsheetReference(reference, renamedSheets) {
3575
+ if (!reference) return;
3576
+ const formula = reference.trim().replace(/^=/, '');
3577
+ const match = /^(?:'((?:[^']|'')+)'|([^!]+))!(.+)$/.exec(formula);
3578
+ const sheetName = match ? match[1]?.replaceAll("''", "'") ?? match[2] : void 0;
3579
+ const renamed = sheetName ? renamedSheets.get(sheetName) : void 0;
3580
+ return renamed && match ? `${quoteSpreadsheetSheetName(renamed)}!${match[3]}` : formula;
3581
+ }
3582
+ function resolveSpreadsheetChartSeries(content, ownerSheet, series) {
3583
+ const nameValue = series.nameReference ? spreadsheetReferenceValues(content, ownerSheet, series.nameReference)?.[0] : void 0;
3584
+ const values = series.valuesReference ? spreadsheetReferenceValues(content, ownerSheet, series.valuesReference)?.map(numericCellValue) : void 0;
3585
+ const xValues = series.xValuesReference ? spreadsheetReferenceValues(content, ownerSheet, series.xValuesReference)?.map(numericCellValue) : void 0;
3586
+ const bubbleSizes = series.bubbleSizesReference ? spreadsheetReferenceValues(content, ownerSheet, series.bubbleSizesReference)?.map(numericCellValue) : void 0;
3587
+ const errorBars = series.errorBars?.map((source)=>({
3588
+ ...source,
3589
+ plusValues: source.plusReference ? spreadsheetReferenceValues(content, ownerSheet, source.plusReference)?.map(numericCellValue) ?? source.plusValues : source.plusValues,
3590
+ minusValues: source.minusReference ? spreadsheetReferenceValues(content, ownerSheet, source.minusReference)?.map(numericCellValue) ?? source.minusValues : source.minusValues
3591
+ }));
3592
+ return {
3593
+ ...series,
3594
+ name: void 0 === nameValue ? series.name : displayCellValue(nameValue),
3595
+ values: values ?? series.values,
3596
+ xValues: xValues ?? series.xValues,
3597
+ bubbleSizes: bubbleSizes ?? series.bubbleSizes,
3598
+ ...errorBars ? {
3599
+ errorBars
3600
+ } : {}
3601
+ };
3602
+ }
3603
+ function resolveSpreadsheetChartAxes(content, ownerSheet, axes) {
3604
+ if (!axes) return;
3605
+ const bottom = resolveSpreadsheetChartAxis(content, ownerSheet, axes.bottom);
3606
+ const left = resolveSpreadsheetChartAxis(content, ownerSheet, axes.left);
3607
+ const top = resolveSpreadsheetChartAxis(content, ownerSheet, axes.top);
3608
+ const right = resolveSpreadsheetChartAxis(content, ownerSheet, axes.right);
3609
+ return {
3610
+ ...bottom ? {
3611
+ bottom
3612
+ } : {},
3613
+ ...left ? {
3614
+ left
3615
+ } : {},
3616
+ ...top ? {
3617
+ top
3618
+ } : {},
3619
+ ...right ? {
3620
+ right
3621
+ } : {}
3622
+ };
3623
+ }
3624
+ function resolveSpreadsheetChartAxis(content, ownerSheet, axis) {
3625
+ if (!axis) return;
3626
+ const titleValue = axis.titleReference ? spreadsheetReferenceValues(content, ownerSheet, axis.titleReference)?.[0] : void 0;
3627
+ return {
3628
+ ...axis,
3629
+ title: void 0 === titleValue ? axis.title : displayCellValue(titleValue)
3630
+ };
3631
+ }
3632
+ function chartPreviewImage(chart) {
3633
+ return {
3634
+ id: chartPreviewImageId(chart.id),
3635
+ name: chart.name,
3636
+ altText: chart.altText ?? chart.title ?? chart.name,
3637
+ contentType: 'image/svg+xml',
3638
+ src: spreadsheetChartSvgDataUrl(chart),
3639
+ left: chart.left,
3640
+ top: chart.top,
3641
+ width: chart.width,
3642
+ height: chart.height
3643
+ };
3644
+ }
3645
+ function chartPreviewImageId(chartId) {
3646
+ return `${CHART_PREVIEW_IMAGE_PREFIX}${chartId}`;
3647
+ }
3648
+ function rangeValues(sheet, startRow, startColumn, endRow, endColumn) {
3649
+ const values = [];
3650
+ for(let row = startRow; row <= endRow; row += 1)for(let column = startColumn; column <= endColumn; column += 1){
3651
+ const cell = sheet.data?.[row]?.[column];
3652
+ values.push(cell?.v ?? cell?.m ?? '');
3653
+ }
3654
+ return values;
3655
+ }
3656
+ function selectionHasHeader(sheet, bounds) {
3657
+ const seriesStartColumn = bounds.endColumn > bounds.startColumn ? bounds.startColumn + 1 : bounds.startColumn;
3658
+ const firstRowValues = rangeValues(sheet, bounds.startRow, seriesStartColumn, bounds.startRow, bounds.endColumn);
3659
+ const bodyValues = rangeValues(sheet, bounds.startRow + 1, seriesStartColumn, bounds.endRow, bounds.endColumn);
3660
+ return firstRowValues.some((value)=>String(value ?? '').trim() && !isNumericValue(value)) && bodyValues.some(isNumericValue);
3661
+ }
3662
+ function normalizedSelection(selection) {
3663
+ if (!selection) return null;
3664
+ const startRow = Math.max(0, Math.min(selection.row[0], selection.row[1]));
3665
+ const endRow = Math.max(startRow, Math.max(selection.row[0], selection.row[1]));
3666
+ const startColumn = Math.max(0, Math.min(selection.column[0], selection.column[1]));
3667
+ const endColumn = Math.max(startColumn, Math.max(selection.column[0], selection.column[1]));
3668
+ if (![
3669
+ startRow,
3670
+ endRow,
3671
+ startColumn,
3672
+ endColumn
3673
+ ].every(Number.isSafeInteger)) return null;
3674
+ return {
3675
+ startRow,
3676
+ endRow,
3677
+ startColumn,
3678
+ endColumn
3679
+ };
3680
+ }
3681
+ function cellRangeFormula(sheetName, startRow, startColumn, endRow, endColumn) {
3682
+ const start = absoluteCellAddress(startRow, startColumn);
3683
+ const end = absoluteCellAddress(endRow, endColumn);
3684
+ return `${quoteSpreadsheetSheetName(sheetName)}!${start}${start === end ? '' : `:${end}`}`;
3685
+ }
3686
+ function absoluteCellAddress(row, column) {
3687
+ return `$${columnName(column)}$${row + 1}`;
3688
+ }
3689
+ function decodeCellAddress(address) {
3690
+ const match = /^\$?([A-Z]{1,3})\$?([1-9]\d*)$/i.exec(address);
3691
+ if (!match) return null;
3692
+ let column = 0;
3693
+ for (const character of match[1].toUpperCase())column = 26 * column + character.charCodeAt(0) - 64;
3694
+ const row = Number(match[2]);
3695
+ if (column < 1 || column > 16384 || row < 1 || row > 1048576) return null;
3696
+ return {
3697
+ row: row - 1,
3698
+ column: column - 1
3699
+ };
3700
+ }
3701
+ function columnName(index) {
3702
+ let value = index + 1;
3703
+ let result = '';
3704
+ while(value > 0){
3705
+ value -= 1;
3706
+ result = String.fromCharCode(65 + value % 26) + result;
3707
+ value = Math.floor(value / 26);
3708
+ }
3709
+ return result;
3710
+ }
3711
+ function axisPosition(index, size) {
3712
+ let position = 0;
3713
+ for(let current = 0; current < index; current += 1)position += size(current);
3714
+ return position;
3715
+ }
3716
+ function inclusiveNumbers(start, end) {
3717
+ return Array.from({
3718
+ length: Math.max(0, end - start + 1)
3719
+ }, (_, index)=>start + index);
3720
+ }
3721
+ function displayCellValue(value) {
3722
+ if (value instanceof Date) return value.toLocaleDateString('zh-CN');
3723
+ if (null == value) return '';
3724
+ return String(value);
3725
+ }
3726
+ function numericCellValue(value) {
3727
+ if (value instanceof Date) return value.getTime();
3728
+ const number = 'number' == typeof value ? value : Number(value);
3729
+ return Number.isFinite(number) ? number : 0;
3730
+ }
3731
+ function isNumericValue(value) {
3732
+ if (value instanceof Date || 'number' == typeof value) return true;
3733
+ if ('string' != typeof value || !value.trim()) return false;
3734
+ return Number.isFinite(Number(value));
3735
+ }
3736
+ function finiteDimension(value, fallback, minimum = 0) {
3737
+ return Number.isFinite(value) ? Math.max(minimum, value) : fallback;
3738
+ }
3739
+ const DEFAULT_SPREADSHEET_CALCULATION_SETTINGS = {
3740
+ mode: 'automatic',
3741
+ fullCalculationOnLoad: false,
3742
+ forceFullCalculation: false,
3743
+ iterativeCalculation: false,
3744
+ maximumIterations: 100,
3745
+ maximumChange: 0.001,
3746
+ fullPrecision: true
3747
+ };
3748
+ const VOLATILE_FUNCTIONS = new Set([
3749
+ 'CELL',
3750
+ 'INFO',
3751
+ 'INDIRECT',
3752
+ 'NOW',
3753
+ 'OFFSET',
3754
+ 'RAND',
3755
+ 'RANDBETWEEN',
3756
+ 'TODAY'
3757
+ ]);
3758
+ const FUTURE_FUNCTIONS = new Set([
3759
+ 'ARRAYTOTEXT',
3760
+ 'BYCOL',
3761
+ 'BYROW',
3762
+ 'CHOOSECOLS',
3763
+ 'CHOOSEROWS',
3764
+ 'DROP',
3765
+ 'EXPAND',
3766
+ 'FIELDVALUE',
3767
+ 'FILTER',
3768
+ 'HSTACK',
3769
+ 'IMAGE',
3770
+ 'ISOMITTED',
3771
+ 'LAMBDA',
3772
+ 'LET',
3773
+ 'MAKEARRAY',
3774
+ 'MAP',
3775
+ 'RANDARRAY',
3776
+ 'REDUCE',
3777
+ 'SCAN',
3778
+ 'SEQUENCE',
3779
+ 'SORT',
3780
+ 'SORTBY',
3781
+ 'STOCKHISTORY',
3782
+ 'TAKE',
3783
+ 'TEXTAFTER',
3784
+ 'TEXTBEFORE',
3785
+ 'TEXTSPLIT',
3786
+ 'TOCOL',
3787
+ 'TOROW',
3788
+ 'UNIQUE',
3789
+ 'VALUETOTEXT',
3790
+ 'VSTACK',
3791
+ 'WRAPCOLS',
3792
+ 'WRAPROWS',
3793
+ 'XLOOKUP',
3794
+ 'XMATCH'
3795
+ ]);
3796
+ const FUTURE_FUNCTION_PREFIXES = new Map([
3797
+ [
3798
+ 'FILTER',
3799
+ '_xlfn._xlws.'
3800
+ ],
3801
+ [
3802
+ 'SORT',
3803
+ '_xlfn._xlws.'
3804
+ ],
3805
+ [
3806
+ 'SORTBY',
3807
+ '_xlfn._xlws.'
3808
+ ],
3809
+ [
3810
+ 'UNIQUE',
3811
+ '_xlfn.'
3812
+ ],
3813
+ ...Array.from(FUTURE_FUNCTIONS).filter((name)=>![
3814
+ 'FILTER',
3815
+ 'SORT',
3816
+ 'SORTBY',
3817
+ 'UNIQUE'
3818
+ ].includes(name)).map((name)=>[
3819
+ name,
3820
+ '_xlfn.'
3821
+ ])
3822
+ ]);
3823
+ function effectiveSpreadsheetCalculationSettings(settings) {
3824
+ if (!settings) return {
3825
+ ...DEFAULT_SPREADSHEET_CALCULATION_SETTINGS
3826
+ };
3827
+ return {
3828
+ mode: settings.mode,
3829
+ fullCalculationOnLoad: Boolean(settings.fullCalculationOnLoad),
3830
+ forceFullCalculation: Boolean(settings.forceFullCalculation),
3831
+ iterativeCalculation: Boolean(settings.iterativeCalculation),
3832
+ maximumIterations: clampedInteger(settings.maximumIterations, 1, 10000, 100),
3833
+ maximumChange: positiveNumber(settings.maximumChange, 0.001),
3834
+ fullPrecision: false !== settings.fullPrecision
3835
+ };
3836
+ }
3837
+ function editableSpreadsheetFormula(source) {
3838
+ 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, ''));
3839
+ }
3840
+ function spreadsheetFormulaForXlsx(currentFormula, sourceFormula) {
3841
+ const current = currentFormula.trim().replace(/^=/, '');
3842
+ const source = sourceFormula?.trim().replace(/^=/, '');
3843
+ if (source && comparableFormula(source) === comparableFormula(current)) return source;
3844
+ return futureFunctionPrefixes(current, source);
3845
+ }
3846
+ function spreadsheetFormulaRangeForCell(sheet, row, column) {
3847
+ const address = spreadsheetCellAddress(row, column);
3848
+ return (sheet.formulaMetadata?.ranges ?? []).find((range)=>range.anchor.toUpperCase().replaceAll('$', '') === address);
3849
+ }
3850
+ function spreadsheetFormulaRangesForSelection(sheet, selection) {
3851
+ return (sheet.formulaMetadata?.ranges ?? []).filter((range)=>work_spreadsheet_formulas_rangesOverlap(selection, parseSpreadsheetFormulaRange(range.reference)));
3852
+ }
3853
+ function spreadsheetFormulaRangeConflict(sheet, range) {
3854
+ const bounds = parseSpreadsheetFormulaRange(range.reference);
3855
+ if (!bounds) return '引用范围无效';
3856
+ const anchor = parseSpreadsheetCellAddress(range.anchor);
3857
+ if (!anchor || !work_spreadsheet_formulas_containsCell(bounds, anchor.row, anchor.column)) return '锚点不在引用范围内';
3858
+ const anchorCell = sheet.data?.[anchor.row]?.[anchor.column];
3859
+ if (!anchorCell) return '锚点单元格不存在';
3860
+ const overlaps = (sheet.formulaMetadata?.ranges ?? []).filter((candidate)=>candidate !== range && work_spreadsheet_formulas_rangesOverlap(bounds, parseSpreadsheetFormulaRange(candidate.reference)));
3861
+ if (overlaps.length) return '与其他公式范围重叠';
3862
+ for (const [row, cells] of (sheet.data ?? []).entries())if (!(row < bounds.startRow) && !(row > bounds.endRow)) {
3863
+ for (const [column, cell] of cells.entries())if (!(column < bounds.startColumn) && !(column > bounds.endColumn)) {
3864
+ if (row !== anchor.row || column !== anchor.column) {
3865
+ if (cell?.f) return `${spreadsheetCellAddress(row, column)} 包含独立公式`;
3866
+ }
3867
+ }
3868
+ }
3869
+ if (Object.values(sheet.config?.merge ?? {}).some((merge)=>work_spreadsheet_formulas_rangesOverlap(bounds, {
3870
+ startRow: merge.r,
3871
+ endRow: merge.r + merge.rs - 1,
3872
+ startColumn: merge.c,
3873
+ endColumn: merge.c + merge.cs - 1
3874
+ }))) return '范围内包含合并单元格';
3875
+ if ('data-table' !== range.type && !anchorCell.f) return '锚点公式已被删除';
3876
+ return null;
3877
+ }
3878
+ function spreadsheetFormulaFunctions(formula) {
3879
+ const withoutStrings = formula.replace(/"(?:[^"]|"")*"/g, '""');
3880
+ const functions = new Set();
3881
+ for (const match of withoutStrings.matchAll(/(?:_xlfn\.)?(?:_xlws\.)?([A-Z][A-Z0-9_.]*)\s*\(/gi))if (match[1]) functions.add(match[1].toUpperCase());
3882
+ return Array.from(functions);
3883
+ }
3884
+ function formulaHasExternalReference(formula) {
3885
+ return /\[[^\]]+\][^!]*!/i.test(formula.replace(/"(?:[^"]|"")*"/g, '""'));
3886
+ }
3887
+ function formulaHasStructuredReference(formula) {
3888
+ const withoutExternalReferences = formula.replace(/"(?:[^"]|"")*"/g, '""').replace(/\[[^\]]+\][^!]*!/gi, '');
3889
+ return /\[(?:@|#)[^\]]+\]/i.test(withoutExternalReferences) || /\b[A-Z_\\][A-Z0-9_.]*\s*\[[^\]]+\]/i.test(withoutExternalReferences);
3890
+ }
3891
+ function volatileSpreadsheetFormulaFunctions(formula) {
3892
+ return spreadsheetFormulaFunctions(formula).filter((name)=>VOLATILE_FUNCTIONS.has(name));
3893
+ }
3894
+ function spreadsheetCellAddress(row, column) {
3895
+ let value = column + 1;
3896
+ let label = '';
3897
+ while(value > 0){
3898
+ value -= 1;
3899
+ label = String.fromCharCode(65 + value % 26) + label;
3900
+ value = Math.floor(value / 26);
3901
+ }
3902
+ return `${label}${row + 1}`;
3903
+ }
3904
+ function parseSpreadsheetFormulaRange(reference) {
3905
+ const normalized = reference.trim().replaceAll('$', '').split('!').at(-1) ?? '';
3906
+ const [startText, endText = startText] = normalized.split(':');
3907
+ const start = parseSpreadsheetCellAddress(startText);
3908
+ const end = parseSpreadsheetCellAddress(endText);
3909
+ if (!start || !end) return null;
3910
+ return {
3911
+ startRow: Math.min(start.row, end.row),
3912
+ endRow: Math.max(start.row, end.row),
3913
+ startColumn: Math.min(start.column, end.column),
3914
+ endColumn: Math.max(start.column, end.column)
3915
+ };
3916
+ }
3917
+ function parseSpreadsheetCellAddress(address) {
3918
+ const match = /^([A-Z]{1,3})([1-9]\d*)$/i.exec(address.trim().replaceAll('$', ''));
3919
+ if (!match) return null;
3920
+ let column = 0;
3921
+ for (const character of match[1].toUpperCase())column = 26 * column + character.charCodeAt(0) - 64;
3922
+ const row = Number(match[2]) - 1;
3923
+ if (row > 1048575 || column > 16384) return null;
3924
+ return {
3925
+ row,
3926
+ column: column - 1
3927
+ };
3928
+ }
3929
+ function work_spreadsheet_formulas_containsCell(bounds, row, column) {
3930
+ return row >= bounds.startRow && row <= bounds.endRow && column >= bounds.startColumn && column <= bounds.endColumn;
3931
+ }
3932
+ function work_spreadsheet_formulas_rangesOverlap(first, second) {
3933
+ if (!second) return false;
3934
+ return first.startRow <= second.endRow && first.endRow >= second.startRow && first.startColumn <= second.endColumn && first.endColumn >= second.startColumn;
3935
+ }
3936
+ function comparableFormula(formula) {
3937
+ return editableSpreadsheetFormula(formula).trim();
3938
+ }
3939
+ function futureFunctionPrefixes(formula, sourceFormula) {
3940
+ const sourcePrefixes = new Map();
3941
+ const sourceFunctions = (sourceFormula ?? '').replace(/"(?:[^"]|"")*"/g, '""');
3942
+ 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]);
3943
+ return transformFormulaOutsideStrings(formula, (segment)=>segment.replace(/(?<![A-Z0-9_.])([A-Z][A-Z0-9_.]*)\s*(?=\()/gi, (match, functionName)=>{
3944
+ const normalized = functionName.toUpperCase();
3945
+ const prefix = sourcePrefixes.get(normalized) ?? FUTURE_FUNCTION_PREFIXES.get(normalized);
3946
+ return prefix ? `${prefix}${match}` : match;
3947
+ }));
3948
+ }
3949
+ function clampedInteger(value, minimum, maximum, fallback) {
3950
+ if (!Number.isFinite(value)) return fallback;
3951
+ return Math.min(maximum, Math.max(minimum, Math.round(value)));
3952
+ }
3953
+ function positiveNumber(value, fallback) {
3954
+ return Number.isFinite(value) && value > 0 ? value : fallback;
3955
+ }
3956
+ function transformFormulaOutsideStrings(formula, transform) {
3957
+ return formula.split(/("(?:[^"]|"")*")/).map((segment, index)=>index % 2 ? segment : transform(segment)).join('');
3958
+ }
3959
+ export { OoxmlPackage, attribute, bytesToDataUrl, childPath, contentTypeForPart, createSpreadsheetChartFromSelection, createSpreadsheetPivotFromSelection, defaultPivotValueCaption, defaultWorkSpreadsheetChartSeriesStyle, deleteSpreadsheetPivotTable, descendants, directChild, directChildren, editableSpreadsheetFormula, effectiveSpreadsheetCalculationSettings, errorBarSourceValues, firstDescendant, 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, parseXml, qualifySpreadsheetRange, readXlsxDrawingAnchor, reconcileSpreadsheetChartPreviews, reconcileSpreadsheetPivots, refreshSpreadsheetPivotTables, resolveSpreadsheetChart, roundChartNumber, 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, xmlNamespacePrefix };