@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.
- package/LICENSE +21 -0
- package/README.md +470 -0
- package/THIRD_PARTY_NOTICES.md +33 -0
- package/dist/0~1403.js +245 -0
- package/dist/0~152.js +94 -0
- package/dist/0~3539.js +1856 -0
- package/dist/0~3557.js +179 -0
- package/dist/0~3635.js +302 -0
- package/dist/0~4595.js +1162 -0
- package/dist/0~5050.js +308 -0
- package/dist/0~6552.js +787 -0
- package/dist/0~7048.js +2110 -0
- package/dist/0~7614.js +1037 -0
- package/dist/0~8155.js +299 -0
- package/dist/0~9445.js +283 -0
- package/dist/0~9512.js +743 -0
- package/dist/0~document-editor.js +10967 -0
- package/dist/0~markdown-editor.js +2260 -0
- package/dist/0~pdf-viewer.js +2156 -0
- package/dist/0~presentation-editor.js +8359 -0
- package/dist/0~rslib-runtime.js +27 -0
- package/dist/0~spreadsheet-editor.js +10388 -0
- package/dist/0~use-office-draft.js +46 -0
- package/dist/0~work-document-extensions.js +2470 -0
- package/dist/0~work-document-links.js +14 -0
- package/dist/0~work-document-model-codec.js +42 -0
- package/dist/0~work-document-page-color.js +11 -0
- package/dist/0~work-document-paragraph-formatting.js +425 -0
- package/dist/0~work-docx-bibliography.js +347 -0
- package/dist/0~work-docx-export.js +1151 -0
- package/dist/0~work-docx-import.js +3116 -0
- package/dist/0~work-docx-page-color.js +42 -0
- package/dist/0~work-office-diagnostics.js +1377 -0
- package/dist/0~work-pptx-export.js +1009 -0
- package/dist/0~work-pptx-import.js +1138 -0
- package/dist/0~work-presentation-charts.js +442 -0
- package/dist/0~work-presentation-transition.js +38 -0
- package/dist/0~work-presentation-visual-scale.js +38 -0
- package/dist/0~work-spreadsheet-formula-support.js +9 -0
- package/dist/1134.js +445 -0
- package/dist/4104.js +5786 -0
- package/dist/432.js +211 -0
- package/dist/4704.js +1364 -0
- package/dist/6489.js +182 -0
- package/dist/8715.js +3959 -0
- package/dist/8728.js +723 -0
- package/dist/9356.js +10 -0
- package/dist/core.d.ts +15 -0
- package/dist/core.js +4 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/internal/design-system/primitives/button/button.d.ts +9 -0
- package/dist/internal/design-system/primitives/button/icon-button.d.ts +7 -0
- package/dist/internal/design-system/primitives/dialog/dialog.d.ts +12 -0
- package/dist/internal/design-system/primitives/feedback/collection-state.d.ts +9 -0
- package/dist/internal/design-system/primitives/feedback/inline-notice.d.ts +10 -0
- package/dist/internal/design-system/primitives/feedback/state-view.d.ts +13 -0
- package/dist/internal/design-system/primitives/form/field.d.ts +19 -0
- package/dist/internal/design-system/primitives/index.d.ts +14 -0
- package/dist/internal/design-system/primitives/layout/split-handle.d.ts +17 -0
- package/dist/internal/design-system/primitives/navigation/segmented-control.d.ts +19 -0
- package/dist/internal/design-system/primitives/navigation/tab-navigation.d.ts +13 -0
- package/dist/internal/design-system/primitives/navigation/tabs.d.ts +21 -0
- package/dist/internal/design-system/primitives/overlay/dialog-focus-scope.d.ts +17 -0
- package/dist/internal/design-system/primitives/overlay/popover.d.ts +34 -0
- package/dist/internal/design-system/primitives/overlay/portal-root.d.ts +1 -0
- package/dist/internal/design-system/primitives/status/status-badge.d.ts +6 -0
- package/dist/internal/features/work/components/work-document-pages.d.ts +4 -0
- package/dist/internal/features/work/components/work-editor-agent-menus.d.ts +12 -0
- package/dist/internal/features/work/components/work-editor-loading-state.d.ts +3 -0
- package/dist/internal/features/work/editors/document-changes-panel.d.ts +9 -0
- package/dist/internal/features/work/editors/document-citation-source-form.d.ts +33 -0
- package/dist/internal/features/work/editors/document-citations-panel.d.ts +8 -0
- package/dist/internal/features/work/editors/document-columns-panel.d.ts +5 -0
- package/dist/internal/features/work/editors/document-comment-composer.d.ts +13 -0
- package/dist/internal/features/work/editors/document-comments-panel.d.ts +17 -0
- package/dist/internal/features/work/editors/document-editor-focus.d.ts +1 -0
- package/dist/internal/features/work/editors/document-editor-pagination.d.ts +3 -0
- package/dist/internal/features/work/editors/document-editor-support.d.ts +34 -0
- package/dist/internal/features/work/editors/document-editor.d.ts +20 -0
- package/dist/internal/features/work/editors/document-find-highlight.d.ts +8 -0
- package/dist/internal/features/work/editors/document-find-replace-panel.d.ts +10 -0
- package/dist/internal/features/work/editors/document-formatting-options.d.ts +113 -0
- package/dist/internal/features/work/editors/document-home-ribbon.d.ts +9 -0
- package/dist/internal/features/work/editors/document-layout-panel.d.ts +10 -0
- package/dist/internal/features/work/editors/document-list-gallery.d.ts +4 -0
- package/dist/internal/features/work/editors/document-navigation-panel.d.ts +9 -0
- package/dist/internal/features/work/editors/document-page-chrome-editor.d.ts +39 -0
- package/dist/internal/features/work/editors/document-page-chrome-panel.d.ts +5 -0
- package/dist/internal/features/work/editors/document-page-chrome-ribbon.d.ts +10 -0
- package/dist/internal/features/work/editors/document-page-navigation.d.ts +14 -0
- package/dist/internal/features/work/editors/document-page-stack.d.ts +8 -0
- package/dist/internal/features/work/editors/document-pagination-popover.d.ts +4 -0
- package/dist/internal/features/work/editors/document-paragraph-spacing-popover.d.ts +4 -0
- package/dist/internal/features/work/editors/document-picture-ribbon.d.ts +4 -0
- package/dist/internal/features/work/editors/document-ruler-tab-stops.d.ts +12 -0
- package/dist/internal/features/work/editors/document-ruler.d.ts +15 -0
- package/dist/internal/features/work/editors/document-selection-context-menu.d.ts +22 -0
- package/dist/internal/features/work/editors/document-selection-toolbar.d.ts +8 -0
- package/dist/internal/features/work/editors/document-status-bar.d.ts +19 -0
- package/dist/internal/features/work/editors/document-style-gallery.d.ts +4 -0
- package/dist/internal/features/work/editors/document-table-insert-popover.d.ts +4 -0
- package/dist/internal/features/work/editors/document-table-ribbon.d.ts +7 -0
- package/dist/internal/features/work/editors/document-task-pane.d.ts +11 -0
- package/dist/internal/features/work/editors/document-toolbar.d.ts +100 -0
- package/dist/internal/features/work/editors/document-vertical-ruler.d.ts +6 -0
- package/dist/internal/features/work/editors/markdown-editor-focus.d.ts +2 -0
- package/dist/internal/features/work/editors/markdown-editor.d.ts +16 -0
- package/dist/internal/features/work/editors/markdown-insert-dialog.d.ts +19 -0
- package/dist/internal/features/work/editors/markdown-selection-context-menu.d.ts +14 -0
- package/dist/internal/features/work/editors/markdown-source-commands.d.ts +41 -0
- package/dist/internal/features/work/editors/markdown-status.d.ts +7 -0
- package/dist/internal/features/work/editors/markdown-toolbar.d.ts +22 -0
- package/dist/internal/features/work/editors/markdown-workspace.d.ts +26 -0
- package/dist/internal/features/work/editors/office-checkbox.d.ts +9 -0
- package/dist/internal/features/work/editors/office-color-picker.d.ts +11 -0
- package/dist/internal/features/work/editors/office-controls.d.ts +7 -0
- package/dist/internal/features/work/editors/office-dialog.d.ts +35 -0
- package/dist/internal/features/work/editors/office-editor-extension.d.ts +86 -0
- package/dist/internal/features/work/editors/office-file-data.d.ts +1 -0
- package/dist/internal/features/work/editors/office-font-families.d.ts +187 -0
- package/dist/internal/features/work/editors/office-menu-keyboard.d.ts +2 -0
- package/dist/internal/features/work/editors/office-number-field.d.ts +21 -0
- package/dist/internal/features/work/editors/office-number-normalization.d.ts +9 -0
- package/dist/internal/features/work/editors/office-select.d.ts +19 -0
- package/dist/internal/features/work/editors/office-shortcuts.d.ts +1 -0
- package/dist/internal/features/work/editors/office-slider.d.ts +10 -0
- package/dist/internal/features/work/editors/office-table-dimensions.d.ts +4 -0
- package/dist/internal/features/work/editors/office-table-insert-popover.d.ts +7 -0
- package/dist/internal/features/work/editors/office-task-pane.d.ts +7 -0
- package/dist/internal/features/work/editors/office-text-field.d.ts +20 -0
- package/dist/internal/features/work/editors/office-tiptap-extensions.d.ts +2 -0
- package/dist/internal/features/work/editors/pdf-annotation-controller.d.ts +23 -0
- package/dist/internal/features/work/editors/pdf-editor-extensions.d.ts +36 -0
- package/dist/internal/features/work/editors/pdf-thumbnail-rail.d.ts +26 -0
- package/dist/internal/features/work/editors/pdf-toolbar.d.ts +23 -0
- package/dist/internal/features/work/editors/pdf-viewer-controller.d.ts +48 -0
- package/dist/internal/features/work/editors/pdf-viewer.d.ts +11 -0
- package/dist/internal/features/work/editors/presentation-arrangement-controller.d.ts +19 -0
- package/dist/internal/features/work/editors/presentation-chart-analysis-canvas.d.ts +21 -0
- package/dist/internal/features/work/editors/presentation-chart-axis-canvas.d.ts +33 -0
- package/dist/internal/features/work/editors/presentation-chart-axis-editor.d.ts +5 -0
- package/dist/internal/features/work/editors/presentation-chart-canvas.d.ts +5 -0
- package/dist/internal/features/work/editors/presentation-chart-cartesian-canvas.d.ts +3 -0
- package/dist/internal/features/work/editors/presentation-chart-data-label-editor.d.ts +6 -0
- package/dist/internal/features/work/editors/presentation-chart-data-labels.d.ts +26 -0
- package/dist/internal/features/work/editors/presentation-chart-layout-editor.d.ts +5 -0
- package/dist/internal/features/work/editors/presentation-chart-legend-canvas.d.ts +15 -0
- package/dist/internal/features/work/editors/presentation-chart-panel.d.ts +7 -0
- package/dist/internal/features/work/editors/presentation-chart-series-analysis-editor.d.ts +6 -0
- package/dist/internal/features/work/editors/presentation-chart-series-canvas.d.ts +15 -0
- package/dist/internal/features/work/editors/presentation-command-controller.d.ts +3 -0
- package/dist/internal/features/work/editors/presentation-command-types.d.ts +206 -0
- package/dist/internal/features/work/editors/presentation-comments-panel.d.ts +9 -0
- package/dist/internal/features/work/editors/presentation-context-menu.d.ts +11 -0
- package/dist/internal/features/work/editors/presentation-design-panel.d.ts +13 -0
- package/dist/internal/features/work/editors/presentation-editor-focus.d.ts +12 -0
- package/dist/internal/features/work/editors/presentation-editor-operations.d.ts +10 -0
- package/dist/internal/features/work/editors/presentation-editor-types.d.ts +22 -0
- package/dist/internal/features/work/editors/presentation-editor.d.ts +3 -0
- package/dist/internal/features/work/editors/presentation-keyboard-extension.d.ts +3 -0
- package/dist/internal/features/work/editors/presentation-player.d.ts +7 -0
- package/dist/internal/features/work/editors/presentation-presenter-view.d.ts +15 -0
- package/dist/internal/features/work/editors/presentation-selection.d.ts +21 -0
- package/dist/internal/features/work/editors/presentation-slide-canvas.d.ts +26 -0
- package/dist/internal/features/work/editors/presentation-slide-thumbnail.d.ts +18 -0
- package/dist/internal/features/work/editors/presentation-status-bar.d.ts +10 -0
- package/dist/internal/features/work/editors/presentation-text-editor.d.ts +22 -0
- package/dist/internal/features/work/editors/presentation-text-formatting.d.ts +3 -0
- package/dist/internal/features/work/editors/presentation-thumbnail-rail.d.ts +20 -0
- package/dist/internal/features/work/editors/presentation-toolbar.d.ts +19 -0
- package/dist/internal/features/work/editors/presentation-transition-panel.d.ts +9 -0
- package/dist/internal/features/work/editors/presentation-workspace.d.ts +38 -0
- package/dist/internal/features/work/editors/spreadsheet-calculation-model.d.ts +8 -0
- package/dist/internal/features/work/editors/spreadsheet-calculation-projection.d.ts +32 -0
- package/dist/internal/features/work/editors/spreadsheet-chart-axis-editor.d.ts +9 -0
- package/dist/internal/features/work/editors/spreadsheet-chart-draft.d.ts +17 -0
- package/dist/internal/features/work/editors/spreadsheet-chart-layout-editor.d.ts +13 -0
- package/dist/internal/features/work/editors/spreadsheet-chart-panel.d.ts +10 -0
- package/dist/internal/features/work/editors/spreadsheet-chart-series-style-editor.d.ts +9 -0
- package/dist/internal/features/work/editors/spreadsheet-clipboard-shortcuts.d.ts +9 -0
- package/dist/internal/features/work/editors/spreadsheet-command-controller.d.ts +121 -0
- package/dist/internal/features/work/editors/spreadsheet-conditional-comparison-fields.d.ts +7 -0
- package/dist/internal/features/work/editors/spreadsheet-conditional-format-model.d.ts +72 -0
- package/dist/internal/features/work/editors/spreadsheet-conditional-format-panel.d.ts +7 -0
- package/dist/internal/features/work/editors/spreadsheet-conditional-threshold-fields.d.ts +10 -0
- package/dist/internal/features/work/editors/spreadsheet-context-menu.d.ts +36 -0
- package/dist/internal/features/work/editors/spreadsheet-data-label-editor.d.ts +9 -0
- package/dist/internal/features/work/editors/spreadsheet-editor-ribbon.d.ts +46 -0
- package/dist/internal/features/work/editors/spreadsheet-editor-support.d.ts +31 -0
- package/dist/internal/features/work/editors/spreadsheet-editor.d.ts +16 -0
- package/dist/internal/features/work/editors/spreadsheet-error-bar-editor.d.ts +10 -0
- package/dist/internal/features/work/editors/spreadsheet-find-bar.d.ts +8 -0
- package/dist/internal/features/work/editors/spreadsheet-find.d.ts +9 -0
- package/dist/internal/features/work/editors/spreadsheet-formula-panel.d.ts +10 -0
- package/dist/internal/features/work/editors/spreadsheet-header-footer-fields.d.ts +5 -0
- package/dist/internal/features/work/editors/spreadsheet-keyboard-navigation.d.ts +18 -0
- package/dist/internal/features/work/editors/spreadsheet-number-format.d.ts +11 -0
- package/dist/internal/features/work/editors/spreadsheet-pivot-panel.d.ts +10 -0
- package/dist/internal/features/work/editors/spreadsheet-print-settings-panel.d.ts +6 -0
- package/dist/internal/features/work/editors/spreadsheet-protection-panel.d.ts +7 -0
- package/dist/internal/features/work/editors/spreadsheet-sheet-bar.d.ts +17 -0
- package/dist/internal/features/work/editors/spreadsheet-sheet-model.d.ts +13 -0
- package/dist/internal/features/work/editors/spreadsheet-trendline-editor.d.ts +8 -0
- package/dist/internal/features/work/editors/spreadsheet-workbook-panel.d.ts +16 -0
- package/dist/internal/features/work/editors/use-document-comments.d.ts +26 -0
- package/dist/internal/features/work/editors/use-document-insert-commands.d.ts +19 -0
- package/dist/internal/features/work/editors/use-document-layout-fonts.d.ts +2 -0
- package/dist/internal/features/work/editors/use-document-page-chrome.d.ts +33 -0
- package/dist/internal/features/work/editors/use-document-pagination.d.ts +46 -0
- package/dist/internal/features/work/editors/use-markdown-source-history.d.ts +16 -0
- package/dist/internal/features/work/editors/use-office-draft.d.ts +10 -0
- package/dist/internal/features/work/editors/use-office-editor-keyboard-shortcuts.d.ts +9 -0
- package/dist/internal/features/work/editors/use-office-editor-runtime.d.ts +6 -0
- package/dist/internal/features/work/editors/use-office-editor-wheel-zoom.d.ts +19 -0
- package/dist/internal/features/work/editors/use-office-history.d.ts +10 -0
- package/dist/internal/features/work/editors/use-presentation-clipboard.d.ts +22 -0
- package/dist/internal/features/work/editors/use-presentation-design-commands.d.ts +33 -0
- package/dist/internal/features/work/editors/use-presentation-element-commands.d.ts +29 -0
- package/dist/internal/features/work/editors/use-presentation-geometry.d.ts +17 -0
- package/dist/internal/features/work/editors/use-presentation-history.d.ts +12 -0
- package/dist/internal/features/work/editors/use-presentation-review-commands.d.ts +22 -0
- package/dist/internal/features/work/editors/use-presentation-selection.d.ts +11 -0
- package/dist/internal/features/work/editors/use-presentation-slide-commands.d.ts +18 -0
- package/dist/internal/features/work/editors/use-presentation-thumbnail-visibility.d.ts +4 -0
- package/dist/internal/features/work/editors/use-presentation-thumbnail-window.d.ts +17 -0
- package/dist/internal/features/work/editors/use-presentation-transform.d.ts +53 -0
- package/dist/internal/features/work/editors/use-spreadsheet-calculation.d.ts +17 -0
- package/dist/internal/features/work/editors/use-spreadsheet-workbook-sync.d.ts +10 -0
- package/dist/internal/features/work/editors/work-office-chrome.d.ts +61 -0
- package/dist/internal/features/work/editors/work-office-ribbon-overflow.d.ts +18 -0
- package/dist/internal/features/work/work-agent-proposal-apply.d.ts +8 -0
- package/dist/internal/features/work/work-agent-proposal.d.ts +55 -0
- package/dist/internal/features/work/work-agent-request.d.ts +6 -0
- package/dist/internal/features/work/work-chart-series-analysis-context.d.ts +3 -0
- package/dist/internal/features/work/work-document-caption-nodes.d.ts +13 -0
- package/dist/internal/features/work/work-document-captions.d.ts +14 -0
- package/dist/internal/features/work/work-document-changes.d.ts +35 -0
- package/dist/internal/features/work/work-document-character-formatting.d.ts +13 -0
- package/dist/internal/features/work/work-document-citation-nodes.d.ts +25 -0
- package/dist/internal/features/work/work-document-citations.d.ts +21 -0
- package/dist/internal/features/work/work-document-columns.d.ts +15 -0
- package/dist/internal/features/work/work-document-comments.d.ts +48 -0
- package/dist/internal/features/work/work-document-extensions.d.ts +11 -0
- package/dist/internal/features/work/work-document-field-node.d.ts +12 -0
- package/dist/internal/features/work/work-document-fields.d.ts +15 -0
- package/dist/internal/features/work/work-document-file-io.d.ts +4 -0
- package/dist/internal/features/work/work-document-fonts.d.ts +16 -0
- package/dist/internal/features/work/work-document-image-layout.d.ts +29 -0
- package/dist/internal/features/work/work-document-incremental-layout.d.ts +17 -0
- package/dist/internal/features/work/work-document-layout.d.ts +5 -0
- package/dist/internal/features/work/work-document-line-measurement.d.ts +24 -0
- package/dist/internal/features/work/work-document-links.d.ts +2 -0
- package/dist/internal/features/work/work-document-lists.d.ts +25 -0
- package/dist/internal/features/work/work-document-model-codec.d.ts +3 -0
- package/dist/internal/features/work/work-document-model.d.ts +12 -0
- package/dist/internal/features/work/work-document-note-nodes.d.ts +11 -0
- package/dist/internal/features/work/work-document-notes.d.ts +18 -0
- package/dist/internal/features/work/work-document-outline.d.ts +14 -0
- package/dist/internal/features/work/work-document-page-break.d.ts +2 -0
- package/dist/internal/features/work/work-document-page-chrome-commands.d.ts +15 -0
- package/dist/internal/features/work/work-document-page-chrome.d.ts +19 -0
- package/dist/internal/features/work/work-document-page-color.d.ts +3 -0
- package/dist/internal/features/work/work-document-pages.d.ts +19 -0
- package/dist/internal/features/work/work-document-pagination-decorations.d.ts +3 -0
- package/dist/internal/features/work/work-document-pagination-dom.d.ts +35 -0
- package/dist/internal/features/work/work-document-pagination-measurement.d.ts +9 -0
- package/dist/internal/features/work/work-document-pagination-types.d.ts +88 -0
- package/dist/internal/features/work/work-document-pagination.d.ts +17 -0
- package/dist/internal/features/work/work-document-paragraph-formatting.d.ts +61 -0
- package/dist/internal/features/work/work-document-search.d.ts +11 -0
- package/dist/internal/features/work/work-document-section-editor.d.ts +20 -0
- package/dist/internal/features/work/work-document-section-node.d.ts +13 -0
- package/dist/internal/features/work/work-document-section.d.ts +34 -0
- package/dist/internal/features/work/work-document-selection-menu.d.ts +54 -0
- package/dist/internal/features/work/work-document-tab-node.d.ts +6 -0
- package/dist/internal/features/work/work-document-tab-stops.d.ts +29 -0
- package/dist/internal/features/work/work-document-table-cell-formatting.d.ts +46 -0
- package/dist/internal/features/work/work-document-table-commands.d.ts +17 -0
- package/dist/internal/features/work/work-document-table-pagination.d.ts +5 -0
- package/dist/internal/features/work/work-document-table-row.d.ts +37 -0
- package/dist/internal/features/work/work-document-table-sizing.d.ts +27 -0
- package/dist/internal/features/work/work-document-text-layout.d.ts +14 -0
- package/dist/internal/features/work/work-docx-bibliography.d.ts +12 -0
- package/dist/internal/features/work/work-docx-caption-diagnostics.d.ts +6 -0
- package/dist/internal/features/work/work-docx-caption-export.d.ts +4 -0
- package/dist/internal/features/work/work-docx-caption-fields.d.ts +5 -0
- package/dist/internal/features/work/work-docx-caption-import.d.ts +20 -0
- package/dist/internal/features/work/work-docx-change-import.d.ts +12 -0
- package/dist/internal/features/work/work-docx-citation-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-citation-export.d.ts +3 -0
- package/dist/internal/features/work/work-docx-citation-import.d.ts +23 -0
- package/dist/internal/features/work/work-docx-column-export.d.ts +14 -0
- package/dist/internal/features/work/work-docx-column-import.d.ts +2 -0
- package/dist/internal/features/work/work-docx-comment-import.d.ts +14 -0
- package/dist/internal/features/work/work-docx-export-formatting.d.ts +30 -0
- package/dist/internal/features/work/work-docx-export-image.d.ts +2 -0
- package/dist/internal/features/work/work-docx-export.d.ts +2 -0
- package/dist/internal/features/work/work-docx-field-export.d.ts +2 -0
- package/dist/internal/features/work/work-docx-field-import.d.ts +16 -0
- package/dist/internal/features/work/work-docx-field-instructions.d.ts +8 -0
- package/dist/internal/features/work/work-docx-image-layout-import.d.ts +12 -0
- package/dist/internal/features/work/work-docx-import.d.ts +50 -0
- package/dist/internal/features/work/work-docx-list-import.d.ts +14 -0
- package/dist/internal/features/work/work-docx-note-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-note-import.d.ts +3 -0
- package/dist/internal/features/work/work-docx-page-chrome-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-docx-page-chrome-import.d.ts +11 -0
- package/dist/internal/features/work/work-docx-page-color.d.ts +2 -0
- package/dist/internal/features/work/work-docx-paragraph-alignment-import.d.ts +12 -0
- package/dist/internal/features/work/work-docx-paragraph-direction-import.d.ts +12 -0
- package/dist/internal/features/work/work-docx-paragraph-indent-import.d.ts +12 -0
- package/dist/internal/features/work/work-docx-paragraph-pagination-import.d.ts +12 -0
- package/dist/internal/features/work/work-docx-paragraph-spacing-import.d.ts +12 -0
- package/dist/internal/features/work/work-docx-paragraph-styles.d.ts +18 -0
- package/dist/internal/features/work/work-docx-run-formatting-import.d.ts +19 -0
- package/dist/internal/features/work/work-docx-tab-stop-import.d.ts +13 -0
- package/dist/internal/features/work/work-docx-table-cell-export.d.ts +4 -0
- package/dist/internal/features/work/work-docx-table-cell-import.d.ts +16 -0
- package/dist/internal/features/work/work-docx-table-row-import.d.ts +13 -0
- package/dist/internal/features/work/work-docx-table-sizing-export.d.ts +8 -0
- package/dist/internal/features/work/work-docx-table-sizing-import.d.ts +12 -0
- package/dist/internal/features/work/work-docx-theme.d.ts +9 -0
- package/dist/internal/features/work/work-file-data.d.ts +1 -0
- package/dist/internal/features/work/work-file-download.d.ts +3 -0
- package/dist/internal/features/work/work-file-io.d.ts +8 -0
- package/dist/internal/features/work/work-markdown-extensions.d.ts +19 -0
- package/dist/internal/features/work/work-markdown-file-io.d.ts +3 -0
- package/dist/internal/features/work/work-markdown-selection-menu.d.ts +57 -0
- package/dist/internal/features/work/work-office-diagnostics.d.ts +9 -0
- package/dist/internal/features/work/work-ooxml-package.d.ts +28 -0
- package/dist/internal/features/work/work-pdf-export.d.ts +23 -0
- package/dist/internal/features/work/work-pptx-chart-axes.d.ts +3 -0
- package/dist/internal/features/work/work-pptx-chart-axis-diagnostics.d.ts +5 -0
- package/dist/internal/features/work/work-pptx-chart-data-labels.d.ts +12 -0
- package/dist/internal/features/work/work-pptx-chart-import.d.ts +11 -0
- package/dist/internal/features/work/work-pptx-chart-layout-style.d.ts +15 -0
- package/dist/internal/features/work/work-pptx-chart-series-analysis.d.ts +13 -0
- package/dist/internal/features/work/work-pptx-chart-xy.d.ts +3 -0
- package/dist/internal/features/work/work-pptx-comments.d.ts +17 -0
- package/dist/internal/features/work/work-pptx-export.d.ts +4 -0
- package/dist/internal/features/work/work-pptx-groups.d.ts +19 -0
- package/dist/internal/features/work/work-pptx-import.d.ts +6 -0
- package/dist/internal/features/work/work-pptx-layout-export.d.ts +13 -0
- package/dist/internal/features/work/work-pptx-layout-import.d.ts +30 -0
- package/dist/internal/features/work/work-pptx-style.d.ts +13 -0
- package/dist/internal/features/work/work-pptx-transition.d.ts +13 -0
- package/dist/internal/features/work/work-presentation-agent-context.d.ts +6 -0
- package/dist/internal/features/work/work-presentation-chart-axes.d.ts +14 -0
- package/dist/internal/features/work/work-presentation-charts.d.ts +37 -0
- package/dist/internal/features/work/work-presentation-clipboard.d.ts +26 -0
- package/dist/internal/features/work/work-presentation-file-io.d.ts +15 -0
- package/dist/internal/features/work/work-presentation-groups.d.ts +17 -0
- package/dist/internal/features/work/work-presentation-layouts.d.ts +13 -0
- package/dist/internal/features/work/work-presentation-transition.d.ts +4 -0
- package/dist/internal/features/work/work-presentation-visual-scale.d.ts +2 -0
- package/dist/internal/features/work/work-repository.d.ts +4 -0
- package/dist/internal/features/work/work-spreadsheet-agent-context.d.ts +15 -0
- package/dist/internal/features/work/work-spreadsheet-chart-axis.d.ts +42 -0
- package/dist/internal/features/work/work-spreadsheet-chart-layout.d.ts +29 -0
- package/dist/internal/features/work/work-spreadsheet-chart-legend-svg.d.ts +18 -0
- package/dist/internal/features/work/work-spreadsheet-chart-series-layout.d.ts +24 -0
- package/dist/internal/features/work/work-spreadsheet-chart-series-style.d.ts +29 -0
- package/dist/internal/features/work/work-spreadsheet-chart-svg-utils.d.ts +7 -0
- package/dist/internal/features/work/work-spreadsheet-chart-svg.d.ts +3 -0
- package/dist/internal/features/work/work-spreadsheet-chart-types.d.ts +147 -0
- package/dist/internal/features/work/work-spreadsheet-chart-validation.d.ts +2 -0
- package/dist/internal/features/work/work-spreadsheet-charts.d.ts +17 -0
- package/dist/internal/features/work/work-spreadsheet-combination-chart-svg.d.ts +9 -0
- package/dist/internal/features/work/work-spreadsheet-comments.d.ts +7 -0
- package/dist/internal/features/work/work-spreadsheet-conditional-canvas.d.ts +5 -0
- package/dist/internal/features/work/work-spreadsheet-conditional-colors.d.ts +9 -0
- package/dist/internal/features/work/work-spreadsheet-conditional-comparisons.d.ts +4 -0
- package/dist/internal/features/work/work-spreadsheet-conditional-data-bar.d.ts +8 -0
- package/dist/internal/features/work/work-spreadsheet-conditional-format.d.ts +10 -0
- package/dist/internal/features/work/work-spreadsheet-conditional-icons.d.ts +107 -0
- package/dist/internal/features/work/work-spreadsheet-conditional-values.d.ts +19 -0
- package/dist/internal/features/work/work-spreadsheet-data-label-svg.d.ts +27 -0
- package/dist/internal/features/work/work-spreadsheet-error-bar-svg.d.ts +5 -0
- package/dist/internal/features/work/work-spreadsheet-error-bars.d.ts +8 -0
- package/dist/internal/features/work/work-spreadsheet-formula-analysis.d.ts +30 -0
- package/dist/internal/features/work/work-spreadsheet-formula-support.d.ts +1 -0
- package/dist/internal/features/work/work-spreadsheet-formulas.d.ts +19 -0
- package/dist/internal/features/work/work-spreadsheet-header-footer.d.ts +14 -0
- package/dist/internal/features/work/work-spreadsheet-line-chart-svg.d.ts +10 -0
- package/dist/internal/features/work/work-spreadsheet-page-breaks.d.ts +4 -0
- package/dist/internal/features/work/work-spreadsheet-page-setup.d.ts +26 -0
- package/dist/internal/features/work/work-spreadsheet-pivot-engine.d.ts +21 -0
- package/dist/internal/features/work/work-spreadsheet-pivot-values.d.ts +7 -0
- package/dist/internal/features/work/work-spreadsheet-pivots.d.ts +27 -0
- package/dist/internal/features/work/work-spreadsheet-protection.d.ts +55 -0
- package/dist/internal/features/work/work-spreadsheet-ranges.d.ts +25 -0
- package/dist/internal/features/work/work-spreadsheet-trendline-svg.d.ts +9 -0
- package/dist/internal/features/work/work-spreadsheet-trendlines.d.ts +11 -0
- package/dist/internal/features/work/work-spreadsheet-xy-chart-axes-svg.d.ts +10 -0
- package/dist/internal/features/work/work-templates.d.ts +4 -0
- package/dist/internal/features/work/work-types.d.ts +513 -0
- package/dist/internal/features/work/work-xlsx-chart-axes.d.ts +4 -0
- package/dist/internal/features/work/work-xlsx-chart-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-xlsx-chart-layout.d.ts +16 -0
- package/dist/internal/features/work/work-xlsx-chart-series-style.d.ts +6 -0
- package/dist/internal/features/work/work-xlsx-chart-values.d.ts +10 -0
- package/dist/internal/features/work/work-xlsx-charts.d.ts +32 -0
- package/dist/internal/features/work/work-xlsx-conditional-format-diagnostics.d.ts +6 -0
- package/dist/internal/features/work/work-xlsx-conditional-format-write.d.ts +11 -0
- package/dist/internal/features/work/work-xlsx-conditional-format.d.ts +29 -0
- package/dist/internal/features/work/work-xlsx-defined-names.d.ts +4 -0
- package/dist/internal/features/work/work-xlsx-drawing-geometry.d.ts +27 -0
- package/dist/internal/features/work/work-xlsx-formula-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-xlsx-formulas.d.ts +40 -0
- package/dist/internal/features/work/work-xlsx-header-footer.d.ts +12 -0
- package/dist/internal/features/work/work-xlsx-images.d.ts +17 -0
- package/dist/internal/features/work/work-xlsx-interop.d.ts +39 -0
- package/dist/internal/features/work/work-xlsx-page-breaks.d.ts +7 -0
- package/dist/internal/features/work/work-xlsx-page-setup-diagnostics.d.ts +2 -0
- package/dist/internal/features/work/work-xlsx-page-setup.d.ts +5 -0
- package/dist/internal/features/work/work-xlsx-pivot-diagnostics.d.ts +3 -0
- package/dist/internal/features/work/work-xlsx-pivot-xml.d.ts +9 -0
- package/dist/internal/features/work/work-xlsx-pivots.d.ts +32 -0
- package/dist/internal/features/work/work-xlsx-protection.d.ts +24 -0
- package/dist/internal/features/workspace/components/workspace-context-menu.d.ts +29 -0
- package/dist/internal/features/workspace/components/workspace-text-control-selection.d.ts +7 -0
- package/dist/internal/kernel/office-kernel-client-types.d.ts +40 -0
- package/dist/internal/kernel/office-kernel-client.d.ts +4 -0
- package/dist/internal/kernel/office-kernel-fallback.d.ts +2 -0
- package/dist/internal/kernel/office-kernel-presentation-fallback.d.ts +2 -0
- package/dist/internal/kernel/office-kernel-protocol.d.ts +229 -0
- package/dist/internal/kernel/office-kernel-spreadsheet-fallback-formula.d.ts +3 -0
- package/dist/internal/kernel/office-kernel-spreadsheet-fallback-validation.d.ts +5 -0
- package/dist/internal/kernel/office-kernel-spreadsheet-fallback-value.d.ts +6 -0
- package/dist/internal/kernel/office-kernel-spreadsheet-fallback.d.ts +2 -0
- package/dist/internal/kernel/office-kernel-spreadsheet-protocol.d.ts +112 -0
- package/dist/internal/kernel/office-kernel-spreadsheet-session-fallback.d.ts +8 -0
- package/dist/internal/kernel/office-kernel-text-fallback.d.ts +10 -0
- package/dist/internal/kernel/office-kernel-version.d.ts +1 -0
- package/dist/internal/kernel/office-kernel.worker.d.ts +1 -0
- package/dist/internal/keyboard-shortcuts.d.ts +2 -0
- package/dist/internal/state/app-state.d.ts +8 -0
- package/dist/noto-naskh-arabic-regular.ttf +0 -0
- package/dist/noto-naskh-arabic.LICENSE.txt +94 -0
- package/dist/noto-sans-hans-regular.otf +0 -0
- package/dist/noto-sans-hans.LICENSE.txt +94 -0
- package/dist/noto-sans-hebrew-regular.ttf +0 -0
- package/dist/noto-sans-hebrew.LICENSE.txt +94 -0
- package/dist/noto-sans-regular.ttf +0 -0
- package/dist/noto-sans.LICENSE.txt +94 -0
- package/dist/office-kernel.wasm +0 -0
- package/dist/office-kernel.worker.js +14605 -0
- package/dist/office-surface.d.ts +10 -0
- package/dist/pdfium.LICENSE.txt +196 -0
- package/dist/pdfium.wasm +0 -0
- package/dist/pptxgen.LICENSE.txt +21 -0
- package/dist/pptxgen.bundle.js +11244 -0
- package/dist/react.d.ts +51 -0
- package/dist/react.js +1 -0
- package/dist/styles.css +17435 -0
- package/dist/styles.js +0 -0
- package/dist/vue.d.ts +282 -0
- package/dist/vue.js +231 -0
- package/dist/web-component.d.ts +98 -0
- package/dist/web-component.js +335 -0
- package/docs/browser-editor-architecture.md +947 -0
- package/package.json +165 -0
|
@@ -0,0 +1,1377 @@
|
|
|
1
|
+
import { docxCaptionSequenceKind, docxFieldOccurrences, docxCaptionBookmark, docxCaptionReferenceTarget, docxFieldInstructions } from "./0~152.js";
|
|
2
|
+
import { documentCitationTagsFromInstruction, docxDocumentFieldKind } from "./4704.js";
|
|
3
|
+
import { contentTypeForPart, directChildren, formulaHasStructuredReference, OoxmlPackage, formulaHasExternalReference, firstDescendant, parseSpreadsheetPrintTitles, stripSpreadsheetSheetQualifier, descendants, directChild, attribute, volatileSpreadsheetFormulaFunctions } from "./8715.js";
|
|
4
|
+
import { readDocxBibliography } from "./0~work-docx-bibliography.js";
|
|
5
|
+
import { isSupportedXlsxCombinationChartNodes, MAX_XLSX_WORKSHEET_IMAGE_BYTES, inspectXlsxPivotTables, isSupportedXlsxWorksheetImageContentType, isSupportedXlsxChartLegend, isSupportedXlsxChartPlotLayout, xlsxChartNodeUsesStackedGrouping, isSupportedXlsxChartSeriesFormatting, readXlsxFormulaFeaturesFromPackage, inspectXlsxHeaderFooterText, isEditableXlsxPaperSizeCode, diagnoseXlsxProtection, xlsxChartSeriesFormattingShapeProperties } from "./4104.js";
|
|
6
|
+
import { spreadsheetConditionalComparisonNeedsUpperValue, isSpreadsheetConditionalIconSetName, isSpreadsheetConditionalComparisonOperator, spreadsheetConditionalIconSetCount } from "./8728.js";
|
|
7
|
+
import { unsupportedSpreadsheetFormulaFunctions } from "./0~work-spreadsheet-formula-support.js";
|
|
8
|
+
function diagnoseDocxCaptions(document) {
|
|
9
|
+
const fields = docxFieldOccurrences(document);
|
|
10
|
+
const sequences = fields.filter((field)=>docxCaptionSequenceKind(field.instruction));
|
|
11
|
+
const bodyFields = fields.filter((field)=>docxDocumentFieldKind(field.instruction));
|
|
12
|
+
const bookmarkNames = captionBookmarkNames(sequences);
|
|
13
|
+
return {
|
|
14
|
+
issues: [
|
|
15
|
+
...sequences.length ? [
|
|
16
|
+
{
|
|
17
|
+
code: 'docx.captions',
|
|
18
|
+
feature: 'Captions and cross-references',
|
|
19
|
+
message: 'Figure and table SEQ fields, independent numbering, caption bookmarks, and caption REF fields are preserved, editable, and updated in native DOCX output.',
|
|
20
|
+
severity: 'info'
|
|
21
|
+
}
|
|
22
|
+
] : [],
|
|
23
|
+
...bodyFields.length ? [
|
|
24
|
+
{
|
|
25
|
+
code: 'docx.fields.body',
|
|
26
|
+
feature: 'Body fields',
|
|
27
|
+
message: 'PAGE, NUMPAGES, SECTION, SECTIONPAGES, DATE, and TIME fields remain live, editable body fields and update in preview, PDF, and native DOCX output.',
|
|
28
|
+
severity: 'info'
|
|
29
|
+
}
|
|
30
|
+
] : []
|
|
31
|
+
],
|
|
32
|
+
hasUnsupportedFields: fields.some((field)=>!isSupportedCaptionField(field.instruction, bookmarkNames))
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function captionBookmarkNames(fields) {
|
|
36
|
+
const names = new Set();
|
|
37
|
+
for (const field of fields){
|
|
38
|
+
const paragraph = closestAncestor(field.start, 'p');
|
|
39
|
+
if (!paragraph) continue;
|
|
40
|
+
const bookmark = docxCaptionBookmark(paragraph, field);
|
|
41
|
+
const name = attribute(bookmark ?? paragraph, 'name')?.trim();
|
|
42
|
+
if (name) names.add(name);
|
|
43
|
+
}
|
|
44
|
+
return names;
|
|
45
|
+
}
|
|
46
|
+
function isSupportedCaptionField(instruction, bookmarkNames) {
|
|
47
|
+
if (documentCitationTagsFromInstruction(instruction).length || /^\s*BIBLIOGRAPHY\b/i.test(instruction)) return true;
|
|
48
|
+
if (docxDocumentFieldKind(instruction)) return true;
|
|
49
|
+
if (docxCaptionSequenceKind(instruction)) return true;
|
|
50
|
+
const target = docxCaptionReferenceTarget(instruction);
|
|
51
|
+
return Boolean(target && bookmarkNames.has(target));
|
|
52
|
+
}
|
|
53
|
+
function closestAncestor(element, localName) {
|
|
54
|
+
let current = element;
|
|
55
|
+
while(current){
|
|
56
|
+
if (current.localName === localName) return current;
|
|
57
|
+
current = current.parentElement;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
async function diagnoseDocxCitations(archive, document) {
|
|
62
|
+
const fields = docxFieldOccurrences(document);
|
|
63
|
+
const citationFields = fields.map((field)=>({
|
|
64
|
+
instruction: field.instruction,
|
|
65
|
+
tags: documentCitationTagsFromInstruction(field.instruction)
|
|
66
|
+
})).filter((field)=>field.tags.length);
|
|
67
|
+
const bibliographyFields = fields.filter((field)=>/^\s*BIBLIOGRAPHY\b/i.test(field.instruction));
|
|
68
|
+
const result = await readDocxBibliography(archive);
|
|
69
|
+
const sourceTags = new Set(result.bibliography?.sources.map((source)=>source.tag) ?? []);
|
|
70
|
+
const citedTags = Array.from(new Set(citationFields.flatMap((field)=>field.tags)));
|
|
71
|
+
const missingTags = citedTags.filter((tag)=>!sourceTags.has(tag));
|
|
72
|
+
const issues = [];
|
|
73
|
+
if (citationFields.length || result.bibliography?.sources.length) issues.push({
|
|
74
|
+
code: 'docx.citations',
|
|
75
|
+
feature: 'Citations and bibliography sources',
|
|
76
|
+
message: `${citationFields.length} citation field(s) and ${result.bibliography?.sources.length ?? 0} bibliography source(s) retain tags, authors, source metadata, style selection, cached display values, and native DOCX field/custom-XML structure.`,
|
|
77
|
+
severity: 'info'
|
|
78
|
+
});
|
|
79
|
+
if (bibliographyFields.length) issues.push({
|
|
80
|
+
code: 'docx.bibliography',
|
|
81
|
+
feature: 'Bibliography',
|
|
82
|
+
message: `${bibliographyFields.length} live bibliography field(s) remain editable and regenerate from the document source library.`,
|
|
83
|
+
severity: 'info'
|
|
84
|
+
});
|
|
85
|
+
if (missingTags.length) issues.push({
|
|
86
|
+
code: 'docx.citations.missing-source',
|
|
87
|
+
feature: 'Citations and bibliography sources',
|
|
88
|
+
message: `Citation source${1 === missingTags.length ? '' : 's'} ${missingTags.join(', ')} could not be found in a connected Word bibliography source part and will be shown as missing.`,
|
|
89
|
+
severity: 'warning'
|
|
90
|
+
});
|
|
91
|
+
if (result.unreadablePartCount) issues.push({
|
|
92
|
+
code: 'docx.citations.unreadable',
|
|
93
|
+
feature: 'Citations and bibliography sources',
|
|
94
|
+
message: `${result.unreadablePartCount} bibliography custom-XML part(s) could not be read and remain available only in the original file.`,
|
|
95
|
+
severity: 'warning'
|
|
96
|
+
});
|
|
97
|
+
if (result.duplicateTags.length) issues.push({
|
|
98
|
+
code: 'docx.citations.duplicate-tags',
|
|
99
|
+
feature: 'Citations and bibliography sources',
|
|
100
|
+
message: `Duplicate bibliography tags ${result.duplicateTags.join(', ')} are assigned unique editable tags during import.`,
|
|
101
|
+
severity: 'warning'
|
|
102
|
+
});
|
|
103
|
+
if (result.uncommonSourceTypes.length) issues.push({
|
|
104
|
+
code: 'docx.citations.source-types',
|
|
105
|
+
feature: 'Citations and bibliography sources',
|
|
106
|
+
message: `Uncommon source type${1 === result.uncommonSourceTypes.length ? '' : 's'} ${result.uncommonSourceTypes.join(', ')} retain their original type and additional metadata; the editor exposes the common source fields directly.`,
|
|
107
|
+
severity: 'info'
|
|
108
|
+
});
|
|
109
|
+
if (result.uncommonStyle) issues.push({
|
|
110
|
+
code: 'docx.citations.style',
|
|
111
|
+
feature: 'Citations and bibliography sources',
|
|
112
|
+
message: `Citation style ${result.uncommonStyle} is retained for DOCX output; Work preview uses its editable APA fallback until a supported APA, MLA, Chicago, or IEEE style is selected.`,
|
|
113
|
+
severity: 'warning'
|
|
114
|
+
});
|
|
115
|
+
return issues;
|
|
116
|
+
}
|
|
117
|
+
async function diagnoseDocxNotes(archive, document) {
|
|
118
|
+
const noteParts = [
|
|
119
|
+
'word/footnotes.xml',
|
|
120
|
+
'word/endnotes.xml'
|
|
121
|
+
].filter((part)=>archive.has(part));
|
|
122
|
+
const noteDocuments = await Promise.all(noteParts.map((part)=>archive.xml(part)));
|
|
123
|
+
const hasNotes = Boolean(document && (descendants(document, 'footnoteReference').length || descendants(document, 'endnoteReference').length)) || noteDocuments.some((notes)=>[
|
|
124
|
+
...descendants(notes, 'footnote'),
|
|
125
|
+
...descendants(notes, 'endnote')
|
|
126
|
+
].some((note)=>Number(attribute(note, 'id')) > 0));
|
|
127
|
+
if (!hasNotes) return [];
|
|
128
|
+
const issues = [
|
|
129
|
+
noteIssue('docx.notes', 'Footnote and endnote references, editable note text, common inline formatting, preview placement, and native DOCX note parts are preserved.', 'info')
|
|
130
|
+
];
|
|
131
|
+
if (noteDocuments.some((notes)=>descendants(notes, 'tbl').length > 0 || descendants(notes, 'drawing').length > 0 || descendants(notes, 'pict').length > 0)) issues.push(noteIssue('docx.notes.rich-content', 'Tables, drawings, and embedded media inside notes may be flattened or converted to inline content.'));
|
|
132
|
+
const settings = archive.has('word/settings.xml') ? await archive.xml('word/settings.xml') : null;
|
|
133
|
+
const noteProperties = [
|
|
134
|
+
...document ? descendants(document, 'footnotePr') : [],
|
|
135
|
+
...document ? descendants(document, 'endnotePr') : [],
|
|
136
|
+
...settings ? descendants(settings, 'footnotePr') : [],
|
|
137
|
+
...settings ? descendants(settings, 'endnotePr') : []
|
|
138
|
+
];
|
|
139
|
+
if (noteProperties.some((properties)=>directChildren(properties).length > 0)) issues.push(noteIssue('docx.notes.numbering', 'Custom note symbols, numbering formats, restart rules, separators, and placement settings normalize to continuous Arabic numbering with footnotes per page and endnotes at document end.'));
|
|
140
|
+
return issues;
|
|
141
|
+
}
|
|
142
|
+
function noteIssue(code, message, severity = 'warning') {
|
|
143
|
+
return {
|
|
144
|
+
code,
|
|
145
|
+
feature: 'Footnotes and endnotes',
|
|
146
|
+
message,
|
|
147
|
+
severity
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const PAGE_FIELD = /^\s*PAGE(?:\s|\\|$)/i;
|
|
151
|
+
const ADVANCED_POSITIONING = new Set([
|
|
152
|
+
'anchor',
|
|
153
|
+
'framePr',
|
|
154
|
+
'ind',
|
|
155
|
+
'pict',
|
|
156
|
+
'positionH',
|
|
157
|
+
'positionV',
|
|
158
|
+
'simplePos',
|
|
159
|
+
'tabs',
|
|
160
|
+
'tblpPr',
|
|
161
|
+
'txbxContent',
|
|
162
|
+
'wrapNone',
|
|
163
|
+
'wrapSquare',
|
|
164
|
+
'wrapThrough',
|
|
165
|
+
'wrapTight',
|
|
166
|
+
'wrapTopAndBottom'
|
|
167
|
+
]);
|
|
168
|
+
async function diagnoseDocxPageChrome(archive) {
|
|
169
|
+
const parts = [
|
|
170
|
+
...archive.paths('word/header'),
|
|
171
|
+
...archive.paths('word/footer')
|
|
172
|
+
].filter((path)=>path.endsWith('.xml'));
|
|
173
|
+
if (!parts.length) return [];
|
|
174
|
+
const documents = await Promise.all(parts.map(async (path)=>({
|
|
175
|
+
path,
|
|
176
|
+
document: await archive.xml(path)
|
|
177
|
+
})));
|
|
178
|
+
const issues = [
|
|
179
|
+
pageChromeIssue('docx.headers', 'Rich default, first-page, and even-page headers and footers, common formatting, tables, links, inline raster images, and PAGE numbering are preserved, editable, and used by preview, PDF, and native DOCX output.', 'info')
|
|
180
|
+
];
|
|
181
|
+
const instructions = documents.flatMap(({ document })=>docxFieldInstructions(document));
|
|
182
|
+
if (instructions.some((instruction)=>!PAGE_FIELD.test(instruction))) issues.push(pageChromeIssue('docx.headers.fields', 'PAGE numbering is preserved. Other header/footer field codes are flattened to their current displayed text and no longer update automatically.'));
|
|
183
|
+
if (documents.some(({ path, document })=>path.startsWith('word/header') && docxFieldInstructions(document).some((instruction)=>PAGE_FIELD.test(instruction)))) issues.push(pageChromeIssue('docx.headers.page-field-position', "PAGE fields placed in a header are preserved through Work's centered footer page-number slot."));
|
|
184
|
+
if (documents.some(({ document })=>descendants(document, 'sdt').length > 0)) issues.push(pageChromeIssue('docx.headers.content-controls', 'Header/footer content controls are flattened to editable content; bindings, locking, and repeating behavior are not preserved.'));
|
|
185
|
+
if (documents.some(({ document })=>Array.from(document.querySelectorAll('*')).some((element)=>ADVANCED_POSITIONING.has(element.localName)))) issues.push(pageChromeIssue('docx.headers.positioning', 'Floating drawings, text boxes, tab stops, indents, and advanced header/footer positioning are normalized to inline flow.'));
|
|
186
|
+
return issues;
|
|
187
|
+
}
|
|
188
|
+
function pageChromeIssue(code, message, severity = 'warning') {
|
|
189
|
+
return {
|
|
190
|
+
code,
|
|
191
|
+
feature: 'Headers and footers',
|
|
192
|
+
message,
|
|
193
|
+
severity
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
const SUPPORTED_CHART_ELEMENTS = new Set([
|
|
197
|
+
'barChart',
|
|
198
|
+
'lineChart',
|
|
199
|
+
'pieChart',
|
|
200
|
+
'doughnutChart',
|
|
201
|
+
'areaChart',
|
|
202
|
+
'radarChart',
|
|
203
|
+
'scatterChart',
|
|
204
|
+
'bubbleChart'
|
|
205
|
+
]);
|
|
206
|
+
const TRENDLINE_CHART_ELEMENTS = new Set([
|
|
207
|
+
'barChart',
|
|
208
|
+
'lineChart',
|
|
209
|
+
'areaChart',
|
|
210
|
+
'scatterChart',
|
|
211
|
+
'bubbleChart'
|
|
212
|
+
]);
|
|
213
|
+
const ERROR_BAR_CHART_ELEMENTS = new Set([
|
|
214
|
+
'barChart',
|
|
215
|
+
'lineChart',
|
|
216
|
+
'areaChart',
|
|
217
|
+
'scatterChart',
|
|
218
|
+
'bubbleChart'
|
|
219
|
+
]);
|
|
220
|
+
const ERROR_BAR_VALUE_TYPES = new Set([
|
|
221
|
+
'cust',
|
|
222
|
+
'fixedVal',
|
|
223
|
+
'percentage',
|
|
224
|
+
'stdDev',
|
|
225
|
+
'stdErr'
|
|
226
|
+
]);
|
|
227
|
+
const ERROR_BAR_CHILDREN = new Set([
|
|
228
|
+
'errDir',
|
|
229
|
+
'errBarType',
|
|
230
|
+
'errValType',
|
|
231
|
+
'noEndCap',
|
|
232
|
+
'plus',
|
|
233
|
+
'minus',
|
|
234
|
+
'val'
|
|
235
|
+
]);
|
|
236
|
+
const SUPPORTED_TRENDLINE_TYPES = new Set([
|
|
237
|
+
'exp',
|
|
238
|
+
'linear',
|
|
239
|
+
'log',
|
|
240
|
+
'movingAvg',
|
|
241
|
+
'poly',
|
|
242
|
+
'power'
|
|
243
|
+
]);
|
|
244
|
+
const SUPPORTED_TRENDLINE_CHILDREN = new Set([
|
|
245
|
+
'name',
|
|
246
|
+
'spPr',
|
|
247
|
+
'trendlineType',
|
|
248
|
+
'order',
|
|
249
|
+
'period',
|
|
250
|
+
'forward',
|
|
251
|
+
'backward',
|
|
252
|
+
'intercept',
|
|
253
|
+
'dispRSq',
|
|
254
|
+
'dispEq',
|
|
255
|
+
'trendlineLbl'
|
|
256
|
+
]);
|
|
257
|
+
const SUPPORTED_DATA_LABEL_CHILDREN = new Set([
|
|
258
|
+
'delete',
|
|
259
|
+
'spPr',
|
|
260
|
+
'txPr',
|
|
261
|
+
'dLblPos',
|
|
262
|
+
'showLegendKey',
|
|
263
|
+
'showVal',
|
|
264
|
+
'showCatName',
|
|
265
|
+
'showSerName',
|
|
266
|
+
'showPercent',
|
|
267
|
+
'showBubbleSize',
|
|
268
|
+
'separator',
|
|
269
|
+
'showLeaderLines'
|
|
270
|
+
]);
|
|
271
|
+
const SUPPORTED_DATA_LABEL_POSITIONS = new Set([
|
|
272
|
+
'bestFit',
|
|
273
|
+
'b',
|
|
274
|
+
'ctr',
|
|
275
|
+
'inBase',
|
|
276
|
+
'inEnd',
|
|
277
|
+
'l',
|
|
278
|
+
'outEnd',
|
|
279
|
+
'r',
|
|
280
|
+
't'
|
|
281
|
+
]);
|
|
282
|
+
async function diagnoseXlsxCharts(archive) {
|
|
283
|
+
const chartPaths = archive.paths('xl/charts/').filter((path)=>/^xl\/charts\/chart\d+\.xml$/i.test(path));
|
|
284
|
+
if (!chartPaths.length) return [];
|
|
285
|
+
const anchoredPaths = await anchoredChartPaths(archive);
|
|
286
|
+
let supported = 0;
|
|
287
|
+
let unsupportedTypes = 0;
|
|
288
|
+
let combinations = 0;
|
|
289
|
+
let trendlines = 0;
|
|
290
|
+
let errorBars = 0;
|
|
291
|
+
let dataLabels = 0;
|
|
292
|
+
let advancedFormatting = 0;
|
|
293
|
+
let advancedPlotLayout = 0;
|
|
294
|
+
let unsupportedLegends = 0;
|
|
295
|
+
let advancedAxes = 0;
|
|
296
|
+
let axisTitles = 0;
|
|
297
|
+
let axisSettings = 0;
|
|
298
|
+
let categoryAxisSettings = 0;
|
|
299
|
+
let unsupportedReferences = 0;
|
|
300
|
+
let externalData = 0;
|
|
301
|
+
for (const path of chartPaths){
|
|
302
|
+
const chart = await archive.xml(path);
|
|
303
|
+
const plotArea = firstDescendant(chart, 'plotArea');
|
|
304
|
+
const chartNodes = plotArea ? directChildren(plotArea).filter((element)=>/Chart$/i.test(element.localName)) : [];
|
|
305
|
+
const supportedCombination = isSupportedXlsxCombinationChartNodes(chartNodes);
|
|
306
|
+
if (chartNodes.length > 1 && !supportedCombination) combinations += 1;
|
|
307
|
+
if ((1 === chartNodes.length && SUPPORTED_CHART_ELEMENTS.has(chartNodes[0].localName) || supportedCombination) && anchoredPaths.has(path)) supported += 1;
|
|
308
|
+
else unsupportedTypes += 1;
|
|
309
|
+
trendlines += unsupportedTrendlineCount(chartNodes);
|
|
310
|
+
errorBars += unsupportedErrorBarCount(chartNodes);
|
|
311
|
+
dataLabels += unsupportedDataLabelCount(chartNodes);
|
|
312
|
+
if (hasAdvancedChartFormatting(chart, chartNodes)) advancedFormatting += 1;
|
|
313
|
+
if (chartNodes.some((node)=>!isSupportedXlsxChartPlotLayout(node))) advancedPlotLayout += 1;
|
|
314
|
+
if (!isSupportedXlsxChartLegend(chart)) unsupportedLegends += 1;
|
|
315
|
+
if (hasAdvancedAxes(chart, chartNodes)) advancedAxes += 1;
|
|
316
|
+
axisTitles += unsupportedAxisTitleCount(chart, chartNodes);
|
|
317
|
+
axisSettings += unsupportedValueAxisSettingsCount(chart);
|
|
318
|
+
categoryAxisSettings += unsupportedCategoryAxisSettingsCount(chart);
|
|
319
|
+
if (descendants(chart, 'multiLvlStrRef').length || descendants(chart, 'f').some((formula)=>!isSupportedChartReference(formula.textContent ?? ''))) unsupportedReferences += 1;
|
|
320
|
+
if (descendants(chart, 'externalData').length) externalData += 1;
|
|
321
|
+
}
|
|
322
|
+
const issues = [];
|
|
323
|
+
if (supported) issues.push(issue('xlsx.charts', `${supported} anchored column, bar, line, pie, doughnut, area, radar, scatter, bubble, or column/line/area combination chart(s), cached data, live cell references, simple series fills, transparency, lines, dashes, and markers, data labels, error bars, trendlines, chart and axis titles, axis order, label positions, major tick marks, category-label intervals, value-axis bounds, major units, major gridlines, number formats, legend positions and overlay, grouping, stacking, plot spacing, smoothing, names, alternative text, positions, sizes, and supported primary or secondary axes are preserved and editable.`, 'info'));
|
|
324
|
+
if (unsupportedTypes) issues.push(issue('xlsx.charts.unsupported-type', `${unsupportedTypes} unsupported, unanchored, 3D, stock, surface, or other chart part(s) cannot be edited and will not be included in a regenerated XLSX.`));
|
|
325
|
+
if (combinations) issues.push(issue('xlsx.charts.combination', `${combinations} combination chart(s) include unsupported plot families or axis layouts; Work does not flatten them into a misleading editable chart.`));
|
|
326
|
+
if (trendlines) issues.push(issue('xlsx.charts.trendline', `${trendlines} trendline(s) use an unsupported chart family, type, parameter, extension, or manual label setting and cannot be represented exactly by the editable chart model.`));
|
|
327
|
+
if (errorBars) issues.push(issue('xlsx.charts.error-bars', `${errorBars} error-bar setting(s) use an unsupported chart family, direction, type, value, custom range, duplicate direction, formatting, or extension and cannot be represented exactly by the editable chart model.`));
|
|
328
|
+
if (dataLabels) issues.push(issue('xlsx.charts.data-labels', `${dataLabels} data-label setting(s) use per-point overrides, legend keys, number formats, leader lines, extensions, invalid values, or chart-specific content that cannot be represented exactly by the editable chart model.`));
|
|
329
|
+
if (advancedAxes) issues.push(issue('xlsx.charts.axes', `${advancedAxes} chart(s) use date, series, or additional axes beyond the supported category/value and one-primary/one-secondary combination layouts.`));
|
|
330
|
+
if (axisTitles) issues.push(issue('xlsx.charts.axis-titles', `${axisTitles} axis-title setting(s) use an unsupported axis position, duplicate title, manual layout, overlay, rich-text structure, field, line break, or extension and cannot be represented exactly by the editable chart model.`));
|
|
331
|
+
if (axisSettings) issues.push(issue('xlsx.charts.axis-settings', `${axisSettings} value-axis setting(s) use a logarithmic scale, invalid bounds or major unit, minor unit or gridlines, display units, unsupported crossing or minor ticks, malformed label or major-tick placement, malformed number format, or extension that cannot be represented exactly by the editable chart model.`));
|
|
332
|
+
if (categoryAxisSettings) issues.push(issue('xlsx.charts.category-axis-settings', `${categoryAxisSettings} category-axis setting(s) use an invalid orientation, label position, major tick, label interval, tick interval, alignment, offset, crossing, number format, extension, or another setting that cannot be represented exactly by the editable chart model.`));
|
|
333
|
+
if (unsupportedLegends) issues.push(issue('xlsx.charts.legend', `${unsupportedLegends} chart(s) use a manual legend layout, per-entry override, invalid position or overlay, formatting, extension, or duplicate legend setting that cannot be represented exactly by the editable chart model.`));
|
|
334
|
+
if (advancedPlotLayout) issues.push(issue('xlsx.charts.layout', `${advancedPlotLayout} chart(s) use an invalid grouping, gap, overlap, mixed per-series smoothing, 3D bubble layout, explosion, slice rotation, unsupported combination layout, or another plot setting that cannot be represented exactly by the editable chart model.`));
|
|
335
|
+
if (unsupportedReferences) issues.push(issue('xlsx.charts.references', `${unsupportedReferences} chart(s) use external, named, multi-level, union, or otherwise unsupported data references; only cached values can be previewed where available.`));
|
|
336
|
+
if (externalData) issues.push(issue('xlsx.charts.external-data', `${externalData} chart(s) reference an embedded or external workbook; Work uses worksheet references and cached values without refreshing external data.`));
|
|
337
|
+
if (advancedFormatting && supported) issues.push(issue('xlsx.charts.format', `${advancedFormatting} chart(s) contain theme, label, fill, line, effect, wall, or other advanced formatting that is normalized to the Work chart style on export.`));
|
|
338
|
+
return issues;
|
|
339
|
+
}
|
|
340
|
+
async function anchoredChartPaths(archive) {
|
|
341
|
+
const paths = new Set();
|
|
342
|
+
const drawings = archive.paths('xl/drawings/').filter((path)=>/^xl\/drawings\/drawing\d+\.xml$/i.test(path));
|
|
343
|
+
for (const path of drawings){
|
|
344
|
+
const document = await archive.xml(path);
|
|
345
|
+
const relationships = await archive.relationships(path);
|
|
346
|
+
for (const frame of descendants(document, 'graphicFrame')){
|
|
347
|
+
const reference = firstDescendant(frame, 'chart');
|
|
348
|
+
const relationshipId = reference ? attribute(reference, 'r:id') ?? attribute(reference, 'id') : null;
|
|
349
|
+
const relationship = relationshipId ? relationships.get(relationshipId) : void 0;
|
|
350
|
+
if (relationship && 'External' !== relationship.targetMode && relationship.type.endsWith('/chart') && archive.has(relationship.target)) paths.add(relationship.target);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return paths;
|
|
354
|
+
}
|
|
355
|
+
function hasAdvancedChartFormatting(document, chartNodes) {
|
|
356
|
+
if ([
|
|
357
|
+
'upDownBars',
|
|
358
|
+
'pictureOptions',
|
|
359
|
+
'view3D',
|
|
360
|
+
'floor',
|
|
361
|
+
'sideWall',
|
|
362
|
+
'backWall'
|
|
363
|
+
].some((name)=>descendants(document, name).length > 0) || descendants(document, 'txPr').length > 0 || descendants(document, 'style').length > 0) return true;
|
|
364
|
+
const supportedShapeProperties = new Set();
|
|
365
|
+
for (const chart of chartNodes)for (const series of directChildren(chart, 'ser')){
|
|
366
|
+
if (!isSupportedXlsxChartSeriesFormatting(series, chart.localName)) return true;
|
|
367
|
+
for (const shape of xlsxChartSeriesFormattingShapeProperties(series))supportedShapeProperties.add(shape);
|
|
368
|
+
}
|
|
369
|
+
return descendants(document, 'spPr').some((shape)=>!supportedShapeProperties.has(shape));
|
|
370
|
+
}
|
|
371
|
+
function isSupportedChartReference(value) {
|
|
372
|
+
const formula = value.trim().replace(/^=/, '');
|
|
373
|
+
if (formula.includes('[') || formula.includes(']')) return false;
|
|
374
|
+
return /^(?:(?:'(?:(?:[^']|'')+)'|[^!]+)!)?\$?[A-Z]{1,3}\$?[1-9]\d*(?::\$?[A-Z]{1,3}\$?[1-9]\d*)?$/i.test(formula);
|
|
375
|
+
}
|
|
376
|
+
function unsupportedTrendlineCount(chartNodes) {
|
|
377
|
+
return chartNodes.reduce((count, chart)=>{
|
|
378
|
+
const supportsTrendlines = TRENDLINE_CHART_ELEMENTS.has(chart.localName) && !xlsxChartNodeUsesStackedGrouping(chart);
|
|
379
|
+
const trendlines = directChildren(chart, 'ser').flatMap((series)=>directChildren(series, 'trendline'));
|
|
380
|
+
return count + trendlines.filter((trendline)=>!supportsTrendlines || !isSupportedTrendline(trendline)).length;
|
|
381
|
+
}, 0);
|
|
382
|
+
}
|
|
383
|
+
function unsupportedDataLabelCount(chartNodes) {
|
|
384
|
+
return chartNodes.reduce((count, chart)=>{
|
|
385
|
+
const dataLabels = [
|
|
386
|
+
...directChildren(chart, 'dLbls'),
|
|
387
|
+
...directChildren(chart, 'ser').flatMap((series)=>directChildren(series, 'dLbls'))
|
|
388
|
+
];
|
|
389
|
+
return count + dataLabels.filter((labels)=>!isSupportedDataLabels(labels, chart.localName)).length;
|
|
390
|
+
}, 0);
|
|
391
|
+
}
|
|
392
|
+
function unsupportedErrorBarCount(chartNodes) {
|
|
393
|
+
return chartNodes.reduce((count, chart)=>{
|
|
394
|
+
const supportsErrorBars = ERROR_BAR_CHART_ELEMENTS.has(chart.localName) && !xlsxChartNodeUsesStackedGrouping(chart);
|
|
395
|
+
const seriesNodes = directChildren(chart, 'ser');
|
|
396
|
+
const directErrorBars = seriesNodes.flatMap((series)=>directChildren(series, 'errBars'));
|
|
397
|
+
const misplacedErrorBars = Math.max(0, descendants(chart, 'errBars').length - directErrorBars.length);
|
|
398
|
+
const unsupported = seriesNodes.reduce((seriesCount, series)=>{
|
|
399
|
+
const directions = new Set();
|
|
400
|
+
return seriesCount + directChildren(series, 'errBars').filter((errorBars)=>{
|
|
401
|
+
const direction = errorBarDirection(errorBars);
|
|
402
|
+
const duplicateDirection = Boolean(direction && directions.has(direction));
|
|
403
|
+
if (direction) directions.add(direction);
|
|
404
|
+
return duplicateDirection || !supportsErrorBars || !isSupportedErrorBars(errorBars, chart.localName);
|
|
405
|
+
}).length;
|
|
406
|
+
}, 0);
|
|
407
|
+
return count + misplacedErrorBars + unsupported;
|
|
408
|
+
}, 0);
|
|
409
|
+
}
|
|
410
|
+
function isSupportedErrorBars(errorBars, chartLocalName) {
|
|
411
|
+
const children = directChildren(errorBars);
|
|
412
|
+
if (children.some((child)=>!ERROR_BAR_CHILDREN.has(child.localName))) return false;
|
|
413
|
+
const direction = errorBarDirection(errorBars);
|
|
414
|
+
if (!direction) return false;
|
|
415
|
+
if ('x' === direction && 'scatterChart' !== chartLocalName && 'bubbleChart' !== chartLocalName) return false;
|
|
416
|
+
const barTypeElements = directChildren(errorBars, 'errBarType');
|
|
417
|
+
if (1 !== barTypeElements.length) return false;
|
|
418
|
+
const barType = attribute(barTypeElements[0], 'val');
|
|
419
|
+
if ('both' !== barType && 'plus' !== barType && 'minus' !== barType) return false;
|
|
420
|
+
const valueTypeElements = directChildren(errorBars, 'errValType');
|
|
421
|
+
if (1 !== valueTypeElements.length) return false;
|
|
422
|
+
const valueType = attribute(valueTypeElements[0], 'val');
|
|
423
|
+
if (!valueType || !ERROR_BAR_VALUE_TYPES.has(valueType)) return false;
|
|
424
|
+
if (!validOptionalBooleanAttribute(errorBars, 'noEndCap')) return false;
|
|
425
|
+
const values = directChildren(errorBars, 'val');
|
|
426
|
+
if (values.length > 1) return false;
|
|
427
|
+
const usesScalarValue = 'fixedVal' === valueType || 'percentage' === valueType || 'stdDev' === valueType;
|
|
428
|
+
if (values.length && (!usesScalarValue || !validNonNegativeNumberAttribute(values[0]))) return false;
|
|
429
|
+
const plus = directChildren(errorBars, 'plus');
|
|
430
|
+
const minus = directChildren(errorBars, 'minus');
|
|
431
|
+
if (plus.length > 1 || minus.length > 1) return false;
|
|
432
|
+
if ('cust' !== valueType) return 0 === plus.length && 0 === minus.length;
|
|
433
|
+
if (values.length) return false;
|
|
434
|
+
if ('minus' !== barType && (1 !== plus.length || !isSupportedErrorBarSource(plus[0]))) return false;
|
|
435
|
+
if ('minus' === barType && plus.length) return false;
|
|
436
|
+
if ('plus' !== barType && (1 !== minus.length || !isSupportedErrorBarSource(minus[0]))) return false;
|
|
437
|
+
return 'plus' !== barType || 0 === minus.length;
|
|
438
|
+
}
|
|
439
|
+
function errorBarDirection(errorBars) {
|
|
440
|
+
const elements = directChildren(errorBars, 'errDir');
|
|
441
|
+
if (1 !== elements.length) return null;
|
|
442
|
+
const direction = attribute(elements[0], 'val');
|
|
443
|
+
return 'x' === direction || 'y' === direction ? direction : null;
|
|
444
|
+
}
|
|
445
|
+
function isSupportedErrorBarSource(source) {
|
|
446
|
+
const children = directChildren(source);
|
|
447
|
+
if (1 !== children.length || 'numRef' !== children[0].localName && 'numLit' !== children[0].localName) return false;
|
|
448
|
+
const data = children[0];
|
|
449
|
+
if ('numRef' === data.localName) {
|
|
450
|
+
const dataChildren = directChildren(data);
|
|
451
|
+
if (dataChildren.some((child)=>'f' !== child.localName && 'numCache' !== child.localName)) return false;
|
|
452
|
+
const formulas = directChildren(data, 'f');
|
|
453
|
+
const caches = directChildren(data, 'numCache');
|
|
454
|
+
if (1 !== formulas.length || caches.length > 1 || !isSupportedChartReference(formulas[0].textContent ?? '')) return false;
|
|
455
|
+
return !caches.length || isSupportedErrorBarNumberCache(caches[0], false);
|
|
456
|
+
}
|
|
457
|
+
return isSupportedErrorBarNumberCache(data, true);
|
|
458
|
+
}
|
|
459
|
+
function isSupportedErrorBarNumberCache(cache, requireValues) {
|
|
460
|
+
const children = directChildren(cache);
|
|
461
|
+
if (children.some((child)=>![
|
|
462
|
+
'formatCode',
|
|
463
|
+
'ptCount',
|
|
464
|
+
'pt'
|
|
465
|
+
].includes(child.localName))) return false;
|
|
466
|
+
if (directChildren(cache, 'formatCode').length > 1 || directChildren(cache, 'ptCount').length > 1) return false;
|
|
467
|
+
const points = directChildren(cache, 'pt');
|
|
468
|
+
if (requireValues && !points.length) return false;
|
|
469
|
+
const indexes = new Set();
|
|
470
|
+
for (const point of points){
|
|
471
|
+
const index = Number(attribute(point, 'idx'));
|
|
472
|
+
const values = directChildren(point, 'v');
|
|
473
|
+
if (!Number.isInteger(index) || index < 0 || indexes.has(index) || 1 !== values.length) return false;
|
|
474
|
+
indexes.add(index);
|
|
475
|
+
const value = Number(values[0].textContent);
|
|
476
|
+
if (!Number.isFinite(value) || value < 0) return false;
|
|
477
|
+
}
|
|
478
|
+
const pointCounts = directChildren(cache, 'ptCount');
|
|
479
|
+
if (!pointCounts.length) return true;
|
|
480
|
+
const pointCount = Number(attribute(pointCounts[0], 'val'));
|
|
481
|
+
return Number.isInteger(pointCount) && pointCount >= points.length;
|
|
482
|
+
}
|
|
483
|
+
function validNonNegativeNumberAttribute(element) {
|
|
484
|
+
const raw = attribute(element, 'val');
|
|
485
|
+
const value = null === raw || '' === raw.trim() ? NaN : Number(raw);
|
|
486
|
+
return Number.isFinite(value) && value >= 0;
|
|
487
|
+
}
|
|
488
|
+
function isSupportedDataLabels(labels, chartLocalName) {
|
|
489
|
+
const children = directChildren(labels);
|
|
490
|
+
if (children.some((child)=>!SUPPORTED_DATA_LABEL_CHILDREN.has(child.localName))) return false;
|
|
491
|
+
if ([
|
|
492
|
+
'spPr',
|
|
493
|
+
'txPr',
|
|
494
|
+
'separator'
|
|
495
|
+
].some((name)=>directChildren(labels, name).length > 1)) return false;
|
|
496
|
+
const positions = directChildren(labels, 'dLblPos');
|
|
497
|
+
if (positions.length > 1 || 1 === positions.length && !SUPPORTED_DATA_LABEL_POSITIONS.has(attribute(positions[0], 'val') ?? '')) return false;
|
|
498
|
+
const booleanNames = [
|
|
499
|
+
'delete',
|
|
500
|
+
'showLegendKey',
|
|
501
|
+
'showVal',
|
|
502
|
+
'showCatName',
|
|
503
|
+
'showSerName',
|
|
504
|
+
'showPercent',
|
|
505
|
+
'showBubbleSize',
|
|
506
|
+
'showLeaderLines'
|
|
507
|
+
];
|
|
508
|
+
if (booleanNames.some((name)=>!validOptionalBooleanAttribute(labels, name))) return false;
|
|
509
|
+
if (booleanElementIsTrue(labels, 'showLegendKey') || booleanElementIsTrue(labels, 'showLeaderLines')) return false;
|
|
510
|
+
if (booleanElementIsTrue(labels, 'showPercent') && 'pieChart' !== chartLocalName && 'doughnutChart' !== chartLocalName) return false;
|
|
511
|
+
return !booleanElementIsTrue(labels, 'showBubbleSize') || 'bubbleChart' === chartLocalName;
|
|
512
|
+
}
|
|
513
|
+
function booleanElementIsTrue(parent, localName) {
|
|
514
|
+
const element = directChildren(parent, localName)[0];
|
|
515
|
+
if (!element) return false;
|
|
516
|
+
const value = attribute(element, 'val');
|
|
517
|
+
return null === value || '1' === value || 'true' === value;
|
|
518
|
+
}
|
|
519
|
+
function isSupportedTrendline(trendline) {
|
|
520
|
+
const children = directChildren(trendline);
|
|
521
|
+
if (children.some((child)=>!SUPPORTED_TRENDLINE_CHILDREN.has(child.localName))) return false;
|
|
522
|
+
if (directChildren(trendline, 'name').length > 1 || directChildren(trendline, 'spPr').length > 1) return false;
|
|
523
|
+
const typeElements = directChildren(trendline, 'trendlineType');
|
|
524
|
+
if (1 !== typeElements.length) return false;
|
|
525
|
+
const type = attribute(typeElements[0], 'val');
|
|
526
|
+
if (!type || !SUPPORTED_TRENDLINE_TYPES.has(type)) return false;
|
|
527
|
+
const order = directChildren(trendline, 'order');
|
|
528
|
+
if ('poly' === type) {
|
|
529
|
+
if (1 !== order.length || !validIntegerAttribute(order[0], 2, 6)) return false;
|
|
530
|
+
} else if (order.length) return false;
|
|
531
|
+
const period = directChildren(trendline, 'period');
|
|
532
|
+
if ('movingAvg' === type) {
|
|
533
|
+
if (1 !== period.length || !validIntegerAttribute(period[0], 2, 255)) return false;
|
|
534
|
+
} else if (period.length) return false;
|
|
535
|
+
if (!validOptionalNumberAttribute(trendline, 'forward', (value)=>value >= 0)) return false;
|
|
536
|
+
if (!validOptionalNumberAttribute(trendline, 'backward', (value)=>value >= 0)) return false;
|
|
537
|
+
if (!validOptionalNumberAttribute(trendline, 'intercept', ()=>true)) return false;
|
|
538
|
+
if (!validOptionalBooleanAttribute(trendline, 'dispEq')) return false;
|
|
539
|
+
if (!validOptionalBooleanAttribute(trendline, 'dispRSq')) return false;
|
|
540
|
+
const labels = directChildren(trendline, 'trendlineLbl');
|
|
541
|
+
if (labels.length > 1) return false;
|
|
542
|
+
return !labels.some((label)=>[
|
|
543
|
+
'manualLayout',
|
|
544
|
+
'tx',
|
|
545
|
+
'numFmt'
|
|
546
|
+
].some((name)=>descendants(label, name).length > 0));
|
|
547
|
+
}
|
|
548
|
+
function validIntegerAttribute(element, minimum, maximum) {
|
|
549
|
+
const value = Number(attribute(element, 'val'));
|
|
550
|
+
return Number.isInteger(value) && value >= minimum && value <= maximum;
|
|
551
|
+
}
|
|
552
|
+
function validOptionalNumberAttribute(parent, localName, predicate) {
|
|
553
|
+
const elements = directChildren(parent, localName);
|
|
554
|
+
if (elements.length > 1) return false;
|
|
555
|
+
if (!elements.length) return true;
|
|
556
|
+
const raw = attribute(elements[0], 'val');
|
|
557
|
+
const value = null === raw || '' === raw.trim() ? NaN : Number(raw);
|
|
558
|
+
return Number.isFinite(value) && predicate(value);
|
|
559
|
+
}
|
|
560
|
+
function validOptionalBooleanAttribute(parent, localName) {
|
|
561
|
+
const elements = directChildren(parent, localName);
|
|
562
|
+
if (elements.length > 1) return false;
|
|
563
|
+
if (!elements.length) return true;
|
|
564
|
+
const value = attribute(elements[0], 'val');
|
|
565
|
+
return null === value || [
|
|
566
|
+
'0',
|
|
567
|
+
'1',
|
|
568
|
+
'false',
|
|
569
|
+
'true'
|
|
570
|
+
].includes(value);
|
|
571
|
+
}
|
|
572
|
+
function hasAdvancedAxes(document, chartNodes) {
|
|
573
|
+
if (descendants(document, 'serAx').length || descendants(document, 'dateAx').length) return true;
|
|
574
|
+
const valueAxes = descendants(document, 'valAx').length;
|
|
575
|
+
const categoryAxes = descendants(document, 'catAx').length;
|
|
576
|
+
if (isSupportedXlsxCombinationChartNodes(chartNodes)) return valueAxes > 2 || categoryAxes > 2;
|
|
577
|
+
const chartType = 1 === chartNodes.length ? chartNodes[0].localName : '';
|
|
578
|
+
const expectedValueAxes = 'scatterChart' === chartType || 'bubbleChart' === chartType ? 2 : 'pieChart' === chartType || 'doughnutChart' === chartType ? 0 : 1;
|
|
579
|
+
const expectedCategoryAxes = 'scatterChart' === chartType || 'bubbleChart' === chartType || 'pieChart' === chartType || 'doughnutChart' === chartType ? 0 : 1;
|
|
580
|
+
return valueAxes > expectedValueAxes || categoryAxes > expectedCategoryAxes;
|
|
581
|
+
}
|
|
582
|
+
function unsupportedAxisTitleCount(document, chartNodes) {
|
|
583
|
+
const plotArea = firstDescendant(document, 'plotArea');
|
|
584
|
+
if (!plotArea) return 0;
|
|
585
|
+
const axes = directChildren(plotArea).filter((axis)=>[
|
|
586
|
+
'catAx',
|
|
587
|
+
'valAx',
|
|
588
|
+
'serAx'
|
|
589
|
+
].includes(axis.localName));
|
|
590
|
+
const supportedCombination = isSupportedXlsxCombinationChartNodes(chartNodes);
|
|
591
|
+
const chartType = 1 === chartNodes.length ? chartNodes[0].localName : '';
|
|
592
|
+
const supportsAxisTitles = supportedCombination || SUPPORTED_CHART_ELEMENTS.has(chartType) && 'pieChart' !== chartType && 'doughnutChart' !== chartType;
|
|
593
|
+
const supportsSecondaryTitles = supportedCombination;
|
|
594
|
+
const positions = new Set();
|
|
595
|
+
return axes.reduce((count, axis)=>{
|
|
596
|
+
const titles = directChildren(axis, 'title');
|
|
597
|
+
if (!titles.length) return count;
|
|
598
|
+
const positionElements = directChildren(axis, 'axPos');
|
|
599
|
+
const position = 1 === positionElements.length ? attribute(positionElements[0], 'val') : null;
|
|
600
|
+
const positionSupported = 'b' === position || 'l' === position || supportsSecondaryTitles && ('t' === position || 'r' === position);
|
|
601
|
+
const duplicatePosition = Boolean(position && positions.has(position));
|
|
602
|
+
if (position) positions.add(position);
|
|
603
|
+
const supported = supportsAxisTitles && 'serAx' !== axis.localName && 1 === titles.length && positionSupported && !duplicatePosition && isSupportedAxisTitle(titles[0]);
|
|
604
|
+
return count + (supported ? 0 : titles.length);
|
|
605
|
+
}, 0);
|
|
606
|
+
}
|
|
607
|
+
function unsupportedValueAxisSettingsCount(document) {
|
|
608
|
+
const plotArea = firstDescendant(document, 'plotArea');
|
|
609
|
+
if (!plotArea) return 0;
|
|
610
|
+
const hasCategoryAxes = directChildren(plotArea, 'catAx').length > 0;
|
|
611
|
+
return directChildren(plotArea, 'valAx').filter((axis)=>!isSupportedValueAxisSettings(axis, hasCategoryAxes)).length;
|
|
612
|
+
}
|
|
613
|
+
function isSupportedValueAxisSettings(axis, hasCategoryAxes) {
|
|
614
|
+
const scaling = directChildren(axis, 'scaling');
|
|
615
|
+
if (scaling.length > 1) return false;
|
|
616
|
+
if (1 === scaling.length) {
|
|
617
|
+
const children = directChildren(scaling[0]);
|
|
618
|
+
if (children.some((child)=>![
|
|
619
|
+
'orientation',
|
|
620
|
+
'max',
|
|
621
|
+
'min'
|
|
622
|
+
].includes(child.localName))) return false;
|
|
623
|
+
const orientation = directChildren(scaling[0], 'orientation');
|
|
624
|
+
if (orientation.length > 1 || orientation.some((item)=>![
|
|
625
|
+
'minMax',
|
|
626
|
+
'maxMin'
|
|
627
|
+
].includes(attribute(item, 'val') ?? ''))) return false;
|
|
628
|
+
const minimum = axisSettingNumber(scaling[0], 'min');
|
|
629
|
+
const maximum = axisSettingNumber(scaling[0], 'max');
|
|
630
|
+
if (!minimum.valid || !maximum.valid) return false;
|
|
631
|
+
if (void 0 !== minimum.value && void 0 !== maximum.value && minimum.value >= maximum.value) return false;
|
|
632
|
+
}
|
|
633
|
+
const majorUnit = axisSettingNumber(axis, 'majorUnit');
|
|
634
|
+
if (!majorUnit.valid || void 0 !== majorUnit.value && majorUnit.value <= 0) return false;
|
|
635
|
+
if (directChildren(axis, 'majorGridlines').length > 1) return false;
|
|
636
|
+
if ([
|
|
637
|
+
'minorUnit',
|
|
638
|
+
'minorGridlines',
|
|
639
|
+
'dispUnits',
|
|
640
|
+
'crossAt',
|
|
641
|
+
'extLst'
|
|
642
|
+
].some((name)=>directChildren(axis, name).length > 0)) return false;
|
|
643
|
+
const majorTicks = directChildren(axis, 'majorTickMark');
|
|
644
|
+
if (majorTicks.length > 1 || majorTicks.some((tick)=>![
|
|
645
|
+
'none',
|
|
646
|
+
'in',
|
|
647
|
+
'out',
|
|
648
|
+
'cross'
|
|
649
|
+
].includes(attribute(tick, 'val') ?? ''))) return false;
|
|
650
|
+
const minorTicks = directChildren(axis, 'minorTickMark');
|
|
651
|
+
if (minorTicks.length > 1 || minorTicks.some((tick)=>'none' !== attribute(tick, 'val'))) return false;
|
|
652
|
+
const tickLabelPositions = directChildren(axis, 'tickLblPos');
|
|
653
|
+
if (tickLabelPositions.length > 1 || tickLabelPositions.some((position)=>![
|
|
654
|
+
'nextTo',
|
|
655
|
+
'high',
|
|
656
|
+
'low',
|
|
657
|
+
'none'
|
|
658
|
+
].includes(attribute(position, 'val') ?? ''))) return false;
|
|
659
|
+
const crosses = directChildren(axis, 'crosses');
|
|
660
|
+
const axisPosition = attribute(directChildren(axis, 'axPos')[0] ?? axis, 'val');
|
|
661
|
+
if (crosses.length > 1) return false;
|
|
662
|
+
if (crosses.some((crossing)=>{
|
|
663
|
+
const value = attribute(crossing, 'val');
|
|
664
|
+
return 'autoZero' !== value && !('max' === value && 'r' === axisPosition);
|
|
665
|
+
})) return false;
|
|
666
|
+
const crossBetween = directChildren(axis, 'crossBetween');
|
|
667
|
+
const expectedCrossBetween = hasCategoryAxes ? 'between' : 'midCat';
|
|
668
|
+
if (crossBetween.length > 1 || crossBetween.some((crossing)=>attribute(crossing, 'val') !== expectedCrossBetween)) return false;
|
|
669
|
+
const deletion = directChildren(axis, 'delete');
|
|
670
|
+
if (deletion.length > 1 || deletion.some(booleanAttributeIsTrue)) return false;
|
|
671
|
+
const numberFormats = directChildren(axis, 'numFmt');
|
|
672
|
+
if (numberFormats.length > 1) return false;
|
|
673
|
+
if (1 === numberFormats.length) {
|
|
674
|
+
const formatCode = attribute(numberFormats[0], 'formatCode');
|
|
675
|
+
const sourceLinked = attribute(numberFormats[0], 'sourceLinked');
|
|
676
|
+
if (!formatCode?.trim() || formatCode.length > 255 || !sourceLinked || ![
|
|
677
|
+
'0',
|
|
678
|
+
'1',
|
|
679
|
+
'false',
|
|
680
|
+
'true'
|
|
681
|
+
].includes(sourceLinked)) return false;
|
|
682
|
+
}
|
|
683
|
+
return true;
|
|
684
|
+
}
|
|
685
|
+
function unsupportedCategoryAxisSettingsCount(document) {
|
|
686
|
+
const plotArea = firstDescendant(document, 'plotArea');
|
|
687
|
+
if (!plotArea) return 0;
|
|
688
|
+
return directChildren(plotArea, 'catAx').filter((axis)=>!isSupportedCategoryAxisSettings(axis)).length;
|
|
689
|
+
}
|
|
690
|
+
function isSupportedCategoryAxisSettings(axis) {
|
|
691
|
+
const scaling = directChildren(axis, 'scaling');
|
|
692
|
+
if (scaling.length > 1) return false;
|
|
693
|
+
if (1 === scaling.length) {
|
|
694
|
+
const children = directChildren(scaling[0]);
|
|
695
|
+
if (children.some((child)=>'orientation' !== child.localName)) return false;
|
|
696
|
+
const orientations = directChildren(scaling[0], 'orientation');
|
|
697
|
+
if (orientations.length > 1 || orientations.some((item)=>![
|
|
698
|
+
'minMax',
|
|
699
|
+
'maxMin'
|
|
700
|
+
].includes(attribute(item, 'val') ?? ''))) return false;
|
|
701
|
+
}
|
|
702
|
+
const majorTicks = directChildren(axis, 'majorTickMark');
|
|
703
|
+
if (majorTicks.length > 1 || majorTicks.some((tick)=>![
|
|
704
|
+
'none',
|
|
705
|
+
'in',
|
|
706
|
+
'out',
|
|
707
|
+
'cross'
|
|
708
|
+
].includes(attribute(tick, 'val') ?? ''))) return false;
|
|
709
|
+
const minorTicks = directChildren(axis, 'minorTickMark');
|
|
710
|
+
if (minorTicks.length > 1 || minorTicks.some((tick)=>'none' !== attribute(tick, 'val'))) return false;
|
|
711
|
+
const labelPositions = directChildren(axis, 'tickLblPos');
|
|
712
|
+
if (labelPositions.length > 1 || labelPositions.some((item)=>![
|
|
713
|
+
'nextTo',
|
|
714
|
+
'high',
|
|
715
|
+
'low',
|
|
716
|
+
'none'
|
|
717
|
+
].includes(attribute(item, 'val') ?? ''))) return false;
|
|
718
|
+
const labelIntervals = directChildren(axis, 'tickLblSkip');
|
|
719
|
+
if (labelIntervals.length > 1 || labelIntervals.some((item)=>!validIntegerAttribute(item, 1, 31999))) return false;
|
|
720
|
+
if ([
|
|
721
|
+
'tickMarkSkip',
|
|
722
|
+
'numFmt',
|
|
723
|
+
'majorGridlines',
|
|
724
|
+
'minorGridlines',
|
|
725
|
+
'crossAt',
|
|
726
|
+
'noMultiLvlLbl',
|
|
727
|
+
'extLst'
|
|
728
|
+
].some((name)=>directChildren(axis, name).length > 0)) return false;
|
|
729
|
+
const crosses = directChildren(axis, 'crosses');
|
|
730
|
+
const axisPosition = attribute(directChildren(axis, 'axPos')[0] ?? axis, 'val');
|
|
731
|
+
if (crosses.length > 1) return false;
|
|
732
|
+
if (crosses.some((item)=>{
|
|
733
|
+
const value = attribute(item, 'val');
|
|
734
|
+
return 'autoZero' !== value && !('max' === value && 't' === axisPosition);
|
|
735
|
+
})) return false;
|
|
736
|
+
const auto = directChildren(axis, 'auto');
|
|
737
|
+
if (auto.length > 1 || auto.some((item)=>!booleanAttributeIsTrue(item))) return false;
|
|
738
|
+
const alignments = directChildren(axis, 'lblAlgn');
|
|
739
|
+
if (alignments.length > 1 || alignments.some((item)=>'ctr' !== attribute(item, 'val'))) return false;
|
|
740
|
+
const offsets = directChildren(axis, 'lblOffset');
|
|
741
|
+
if (offsets.length > 1 || offsets.some((item)=>'100' !== attribute(item, 'val'))) return false;
|
|
742
|
+
const deletion = directChildren(axis, 'delete');
|
|
743
|
+
if (deletion.length > 1 || !deletion.every((item)=>validBooleanAttributeValue(item))) return false;
|
|
744
|
+
return !deletion.some(booleanAttributeIsTrue) || 't' === axisPosition;
|
|
745
|
+
}
|
|
746
|
+
function validBooleanAttributeValue(element) {
|
|
747
|
+
const value = attribute(element, 'val');
|
|
748
|
+
return null === value || [
|
|
749
|
+
'0',
|
|
750
|
+
'1',
|
|
751
|
+
'false',
|
|
752
|
+
'true'
|
|
753
|
+
].includes(value);
|
|
754
|
+
}
|
|
755
|
+
function axisSettingNumber(parent, name) {
|
|
756
|
+
const elements = directChildren(parent, name);
|
|
757
|
+
if (!elements.length) return {
|
|
758
|
+
valid: true
|
|
759
|
+
};
|
|
760
|
+
if (elements.length > 1) return {
|
|
761
|
+
valid: false
|
|
762
|
+
};
|
|
763
|
+
const source = attribute(elements[0], 'val');
|
|
764
|
+
if (!source?.trim()) return {
|
|
765
|
+
valid: false
|
|
766
|
+
};
|
|
767
|
+
const value = Number(source);
|
|
768
|
+
return Number.isFinite(value) ? {
|
|
769
|
+
valid: true,
|
|
770
|
+
value
|
|
771
|
+
} : {
|
|
772
|
+
valid: false
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
function isSupportedAxisTitle(title) {
|
|
776
|
+
const children = directChildren(title);
|
|
777
|
+
if (children.some((child)=>![
|
|
778
|
+
'tx',
|
|
779
|
+
'layout',
|
|
780
|
+
'overlay'
|
|
781
|
+
].includes(child.localName))) return false;
|
|
782
|
+
const text = directChildren(title, 'tx');
|
|
783
|
+
if (1 !== text.length) return false;
|
|
784
|
+
const layouts = directChildren(title, 'layout');
|
|
785
|
+
if (layouts.length > 1 || layouts.some((layout)=>descendants(layout, 'manualLayout').length > 0)) return false;
|
|
786
|
+
const overlays = directChildren(title, 'overlay');
|
|
787
|
+
if (overlays.length > 1 || overlays.some((overlay)=>booleanAttributeIsTrue(overlay))) return false;
|
|
788
|
+
const sources = directChildren(text[0]);
|
|
789
|
+
if (1 !== sources.length) return false;
|
|
790
|
+
const source = sources[0];
|
|
791
|
+
if ('strRef' === source.localName) {
|
|
792
|
+
const sourceChildren = directChildren(source);
|
|
793
|
+
if (sourceChildren.some((child)=>'f' !== child.localName && 'strCache' !== child.localName)) return false;
|
|
794
|
+
const formulas = directChildren(source, 'f');
|
|
795
|
+
return 1 === formulas.length && directChildren(source, 'strCache').length <= 1 && isSupportedChartReference(formulas[0].textContent ?? '');
|
|
796
|
+
}
|
|
797
|
+
if ('rich' !== source.localName) return false;
|
|
798
|
+
if (1 !== descendants(source, 'p').length || !descendants(source, 't').length) return false;
|
|
799
|
+
return ![
|
|
800
|
+
'br',
|
|
801
|
+
'fld',
|
|
802
|
+
'hlinkClick',
|
|
803
|
+
'hlinkMouseOver'
|
|
804
|
+
].some((name)=>descendants(source, name).length > 0);
|
|
805
|
+
}
|
|
806
|
+
function booleanAttributeIsTrue(element) {
|
|
807
|
+
const value = attribute(element, 'val');
|
|
808
|
+
return null === value || '1' === value || 'true' === value;
|
|
809
|
+
}
|
|
810
|
+
function issue(code, message, severity = 'warning') {
|
|
811
|
+
return {
|
|
812
|
+
code,
|
|
813
|
+
severity,
|
|
814
|
+
feature: 'Workbook charts',
|
|
815
|
+
message
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
const SIMPLE_RULE_TYPES = new Set([
|
|
819
|
+
'containsText',
|
|
820
|
+
'duplicateValues',
|
|
821
|
+
'uniqueValues',
|
|
822
|
+
'top10',
|
|
823
|
+
'aboveAverage'
|
|
824
|
+
]);
|
|
825
|
+
function diagnoseXlsxConditionalFormatting(worksheet, styles) {
|
|
826
|
+
const allContainers = descendants(worksheet, 'conditionalFormatting');
|
|
827
|
+
if (!allContainers.length) return [];
|
|
828
|
+
const containers = directChildren(worksheet.documentElement, 'conditionalFormatting');
|
|
829
|
+
const dxfs = styles ? directChildren(firstDescendant(styles, 'dxfs') ?? styles, 'dxf') : [];
|
|
830
|
+
const unsupported = new Set();
|
|
831
|
+
let supported = 0;
|
|
832
|
+
let colorNormalization = false;
|
|
833
|
+
let formulaNormalization = false;
|
|
834
|
+
let optionNormalization = allContainers.length > containers.length;
|
|
835
|
+
const priorities = new Set();
|
|
836
|
+
if (allContainers.length > containers.length) unsupported.add('extension-based rules');
|
|
837
|
+
if (descendants(worksheet, 'dataBar').some(hasAdvancedDataBarVisuals)) unsupported.add('x14 data-bar direction, axis, border, gradient, or negative-color options');
|
|
838
|
+
for (const container of containers)for (const rule of directChildren(container, 'cfRule')){
|
|
839
|
+
const type = attribute(rule, 'type') ?? 'unknown';
|
|
840
|
+
const formulas = directChildren(rule, 'formula').map((formula)=>formula.textContent?.trim() ?? '');
|
|
841
|
+
if (formulas.some(isAdvancedFormula)) formulaNormalization = true;
|
|
842
|
+
const priority = positiveIntegerAttribute(rule, 'priority');
|
|
843
|
+
if (null === priority || priorities.has(priority)) optionNormalization = true;
|
|
844
|
+
else priorities.add(priority);
|
|
845
|
+
if ('cellIs' === type) {
|
|
846
|
+
const operator = attribute(rule, 'operator') ?? '';
|
|
847
|
+
const supportedOperator = isSpreadsheetConditionalComparisonOperator(operator);
|
|
848
|
+
const formulaCount = supportedOperator && spreadsheetConditionalComparisonNeedsUpperValue(operator) ? 2 : 1;
|
|
849
|
+
if (supportedOperator && formulas.length >= formulaCount) supported += 1;
|
|
850
|
+
else unsupported.add(operator ? `cellIs/${operator}` : 'cellIs');
|
|
851
|
+
} else if (SIMPLE_RULE_TYPES.has(type)) {
|
|
852
|
+
supported += 1;
|
|
853
|
+
if ('aboveAverage' === type && (null !== attribute(rule, 'equalAverage') || null !== attribute(rule, 'stdDev'))) optionNormalization = true;
|
|
854
|
+
} else if ('expression' === type) if (formulas.length) supported += 1;
|
|
855
|
+
else unsupported.add('empty formula rule');
|
|
856
|
+
else if ('colorScale' === type) {
|
|
857
|
+
const scale = directChild(rule, 'colorScale');
|
|
858
|
+
const colors = scale ? directChildren(scale, 'color') : [];
|
|
859
|
+
const thresholds = scale ? directChildren(scale, 'cfvo') : [];
|
|
860
|
+
const supportedThresholds = colors.length >= 2 && colors.length <= 3 && (0 === thresholds.length || thresholds.length === colors.length && thresholds.every(isSupportedVisualThreshold));
|
|
861
|
+
if (colors.length >= 2 && colors.length <= 3 && colors.every(hasRgbColor) && supportedThresholds) supported += 1;
|
|
862
|
+
else unsupported.add('colorScale');
|
|
863
|
+
if (colors.some(needsColorNormalization)) colorNormalization = true;
|
|
864
|
+
if (!thresholds.length) optionNormalization = true;
|
|
865
|
+
} else if ('dataBar' === type) {
|
|
866
|
+
const dataBar = directChild(rule, 'dataBar');
|
|
867
|
+
const colors = dataBar ? directChildren(dataBar, 'color') : [];
|
|
868
|
+
const thresholds = dataBar ? directChildren(dataBar, 'cfvo') : [];
|
|
869
|
+
const supportedThresholds = 0 === thresholds.length || 2 === thresholds.length && thresholds.every(isSupportedVisualThreshold);
|
|
870
|
+
const supportedOptions = Boolean(dataBar && hasSupportedDataBarOptions(dataBar));
|
|
871
|
+
if (1 === colors.length && colors.every(hasRgbColor) && supportedThresholds && supportedOptions) supported += 1;
|
|
872
|
+
else unsupported.add('dataBar');
|
|
873
|
+
if (colors.some(needsColorNormalization)) colorNormalization = true;
|
|
874
|
+
if (!thresholds.length) optionNormalization = true;
|
|
875
|
+
if (dataBar && Array.from(dataBar.attributes).some((item)=>![
|
|
876
|
+
'showValue',
|
|
877
|
+
'minLength',
|
|
878
|
+
'maxLength'
|
|
879
|
+
].includes(item.localName))) optionNormalization = true;
|
|
880
|
+
} else if ('iconSet' === type) {
|
|
881
|
+
const iconSet = directChild(rule, 'iconSet');
|
|
882
|
+
const name = iconSet ? attribute(iconSet, 'iconSet') ?? '3TrafficLights1' : '';
|
|
883
|
+
if (iconSet && (booleanAttribute(iconSet, 'custom') || directChildren(iconSet, 'cfIcon').length > 0)) unsupported.add('custom icon sets');
|
|
884
|
+
else if (iconSet && isSpreadsheetConditionalIconSetName(name)) {
|
|
885
|
+
const thresholds = directChildren(iconSet, 'cfvo');
|
|
886
|
+
const validThresholds = 0 === thresholds.length || thresholds.length === spreadsheetConditionalIconSetCount(name) && thresholds.every(isSupportedIconThreshold);
|
|
887
|
+
if (validThresholds) supported += 1;
|
|
888
|
+
else unsupported.add('iconSet thresholds');
|
|
889
|
+
if (!thresholds.length) optionNormalization = true;
|
|
890
|
+
if (Array.from(iconSet.attributes).some((item)=>![
|
|
891
|
+
'iconSet',
|
|
892
|
+
'showValue',
|
|
893
|
+
'reverse',
|
|
894
|
+
'percent'
|
|
895
|
+
].includes(item.localName)) || [
|
|
896
|
+
'showValue',
|
|
897
|
+
'reverse',
|
|
898
|
+
'percent'
|
|
899
|
+
].some((name)=>!validBooleanAttribute(iconSet, name))) optionNormalization = true;
|
|
900
|
+
} else unsupported.add(name ? `iconSet/${name}` : 'iconSet');
|
|
901
|
+
} else unsupported.add(type);
|
|
902
|
+
const dxfId = attribute(rule, 'dxfId');
|
|
903
|
+
if (null !== dxfId) {
|
|
904
|
+
const dxfIndex = /^\d+$/.test(dxfId) ? Number(dxfId) : -1;
|
|
905
|
+
const dxf = dxfs[dxfIndex];
|
|
906
|
+
if (dxf) {
|
|
907
|
+
const colors = [
|
|
908
|
+
...descendants(dxf, 'color'),
|
|
909
|
+
...descendants(dxf, 'fgColor')
|
|
910
|
+
];
|
|
911
|
+
if (colors.some(needsColorNormalization)) colorNormalization = true;
|
|
912
|
+
if (hasAdvancedDifferentialStyle(dxf)) optionNormalization = true;
|
|
913
|
+
} else unsupported.add('missing differential style');
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
const diagnostics = [
|
|
917
|
+
{
|
|
918
|
+
code: 'xlsx.conditional-formatting',
|
|
919
|
+
message: supported ? `${supported} supported conditional-formatting rule(s) are preserved in editable sheet state; common cell rules can be changed in Work.` : 'Conditional formatting was detected, but no rule could be converted to editable sheet state.',
|
|
920
|
+
severity: supported ? 'info' : 'warning'
|
|
921
|
+
}
|
|
922
|
+
];
|
|
923
|
+
if (unsupported.size) diagnostics.push({
|
|
924
|
+
code: 'xlsx.conditional-formatting.unsupported',
|
|
925
|
+
message: `Unsupported conditional-formatting features remain in the original workbook only: ${Array.from(unsupported).join(', ')}.`,
|
|
926
|
+
severity: 'warning'
|
|
927
|
+
});
|
|
928
|
+
if (colorNormalization) diagnostics.push({
|
|
929
|
+
code: 'xlsx.conditional-formatting.colors',
|
|
930
|
+
message: 'Theme, indexed, automatic, or tinted conditional-format colors are normalized to supported RGB colors.',
|
|
931
|
+
severity: 'warning'
|
|
932
|
+
});
|
|
933
|
+
if (formulaNormalization) diagnostics.push({
|
|
934
|
+
code: 'xlsx.conditional-formatting.formulas',
|
|
935
|
+
message: 'External, structured, cross-sheet, or advanced conditional formulas may be recalculated differently.',
|
|
936
|
+
severity: 'warning'
|
|
937
|
+
});
|
|
938
|
+
if (optionNormalization) diagnostics.push({
|
|
939
|
+
code: 'xlsx.conditional-formatting.options',
|
|
940
|
+
message: 'Invalid or duplicate priorities, advanced differential styles, omitted thresholds, or x14 visual extensions may be normalized.',
|
|
941
|
+
severity: 'warning'
|
|
942
|
+
});
|
|
943
|
+
return diagnostics;
|
|
944
|
+
}
|
|
945
|
+
function hasRgbColor(element) {
|
|
946
|
+
return /^[0-9a-f]{6,8}$/i.test(attribute(element, 'rgb') ?? '');
|
|
947
|
+
}
|
|
948
|
+
function needsColorNormalization(element) {
|
|
949
|
+
return !hasRgbColor(element) && [
|
|
950
|
+
'theme',
|
|
951
|
+
'indexed',
|
|
952
|
+
'auto',
|
|
953
|
+
'tint'
|
|
954
|
+
].some((name)=>null !== attribute(element, name));
|
|
955
|
+
}
|
|
956
|
+
function isAdvancedFormula(value) {
|
|
957
|
+
return /(?:^|[^A-Z0-9_.])(?:INDIRECT|OFFSET|LET|LAMBDA|_XLFN\.)\s*\(/i.test(value) || /(?:\[[^\]]+\]|!|[#@])/.test(value);
|
|
958
|
+
}
|
|
959
|
+
function hasAdvancedDifferentialStyle(dxf) {
|
|
960
|
+
return directChildren(dxf).some((child)=>{
|
|
961
|
+
if ('font' === child.localName) return directChildren(child).some((item)=>'color' !== item.localName);
|
|
962
|
+
if ('fill' !== child.localName) return true;
|
|
963
|
+
const pattern = directChild(child, 'patternFill');
|
|
964
|
+
return !pattern || 'solid' !== attribute(pattern, 'patternType');
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
function booleanAttribute(element, name) {
|
|
968
|
+
const value = attribute(element, name)?.toLowerCase();
|
|
969
|
+
return '1' === value || 'true' === value;
|
|
970
|
+
}
|
|
971
|
+
function validBooleanAttribute(element, name) {
|
|
972
|
+
const value = attribute(element, name);
|
|
973
|
+
return null === value || [
|
|
974
|
+
'0',
|
|
975
|
+
'1',
|
|
976
|
+
'false',
|
|
977
|
+
'true'
|
|
978
|
+
].includes(value.toLowerCase());
|
|
979
|
+
}
|
|
980
|
+
function isSupportedIconThreshold(element) {
|
|
981
|
+
const type = attribute(element, 'type');
|
|
982
|
+
if ('min' === type || 'max' === type) return true;
|
|
983
|
+
if (![
|
|
984
|
+
'num',
|
|
985
|
+
'percent',
|
|
986
|
+
'percentile'
|
|
987
|
+
].includes(type ?? '')) return false;
|
|
988
|
+
const value = attribute(element, 'val');
|
|
989
|
+
return null !== value && Number.isFinite(Number(value));
|
|
990
|
+
}
|
|
991
|
+
function isSupportedVisualThreshold(element) {
|
|
992
|
+
const type = attribute(element, 'type');
|
|
993
|
+
if ('min' === type || 'max' === type) return true;
|
|
994
|
+
if (![
|
|
995
|
+
'num',
|
|
996
|
+
'percent',
|
|
997
|
+
'percentile'
|
|
998
|
+
].includes(type ?? '')) return false;
|
|
999
|
+
const value = attribute(element, 'val');
|
|
1000
|
+
return null !== value && Number.isFinite(Number(value));
|
|
1001
|
+
}
|
|
1002
|
+
function hasSupportedDataBarOptions(element) {
|
|
1003
|
+
if (!validBooleanAttribute(element, 'showValue')) return false;
|
|
1004
|
+
const minimum = percentageAttribute(element, 'minLength', 10);
|
|
1005
|
+
const maximum = percentageAttribute(element, 'maxLength', 90);
|
|
1006
|
+
return null !== minimum && null !== maximum && minimum <= maximum;
|
|
1007
|
+
}
|
|
1008
|
+
function percentageAttribute(element, name, defaultValue) {
|
|
1009
|
+
const source = attribute(element, name);
|
|
1010
|
+
if (null === source) return defaultValue;
|
|
1011
|
+
const value = Number(source);
|
|
1012
|
+
return Number.isFinite(value) && value >= 0 && value <= 100 ? value : null;
|
|
1013
|
+
}
|
|
1014
|
+
function positiveIntegerAttribute(element, name) {
|
|
1015
|
+
const source = attribute(element, name);
|
|
1016
|
+
if (!source || !/^[1-9]\d*$/.test(source)) return null;
|
|
1017
|
+
const value = Number(source);
|
|
1018
|
+
return Number.isSafeInteger(value) ? value : null;
|
|
1019
|
+
}
|
|
1020
|
+
function hasAdvancedDataBarVisuals(element) {
|
|
1021
|
+
return [
|
|
1022
|
+
'direction',
|
|
1023
|
+
'axisPosition',
|
|
1024
|
+
'border',
|
|
1025
|
+
'gradient',
|
|
1026
|
+
'negativeBarColorSameAsPositive'
|
|
1027
|
+
].some((name)=>null !== attribute(element, name)) || [
|
|
1028
|
+
'borderColor',
|
|
1029
|
+
'negativeFillColor',
|
|
1030
|
+
'negativeBorderColor',
|
|
1031
|
+
'axisColor'
|
|
1032
|
+
].some((name)=>descendants(element, name).length > 0);
|
|
1033
|
+
}
|
|
1034
|
+
async function diagnoseXlsxFormulas(archive) {
|
|
1035
|
+
const features = await readXlsxFormulaFeaturesFromPackage(archive);
|
|
1036
|
+
const formulas = Array.from(features.sheets.entries()).flatMap(([sheetName, sheet])=>sheet.formulas.map((formula)=>({
|
|
1037
|
+
...formula,
|
|
1038
|
+
sheetName
|
|
1039
|
+
})));
|
|
1040
|
+
const ranges = Array.from(features.sheets.entries()).flatMap(([sheetName, sheet])=>sheet.ranges.map((range)=>({
|
|
1041
|
+
...range,
|
|
1042
|
+
sheetName
|
|
1043
|
+
})));
|
|
1044
|
+
const sharedGroups = Array.from(features.sheets.values()).reduce((count, sheet)=>count + sheet.sharedFormulaGroups, 0);
|
|
1045
|
+
const sharedCells = Array.from(features.sheets.values()).reduce((count, sheet)=>count + sheet.sharedFormulaCells, 0);
|
|
1046
|
+
const arrayRanges = ranges.filter((range)=>'array' === range.type).length;
|
|
1047
|
+
const dynamicRanges = ranges.filter((range)=>'dynamic-array' === range.type).length;
|
|
1048
|
+
const dataTables = ranges.filter((range)=>'data-table' === range.type).length;
|
|
1049
|
+
const cachedErrors = formulas.filter((formula)=>formula.cachedError).length;
|
|
1050
|
+
const externalReferences = formulas.filter((formula)=>formulaHasExternalReference(formula.formula));
|
|
1051
|
+
const structuredReferences = formulas.filter((formula)=>formulaHasStructuredReference(formula.formula));
|
|
1052
|
+
const volatileFormulas = formulas.filter((formula)=>volatileSpreadsheetFormulaFunctions(formula.formula).length);
|
|
1053
|
+
const unsupportedFunctions = new Set(formulas.flatMap((formula)=>unsupportedSpreadsheetFormulaFunctions(formula.formula)));
|
|
1054
|
+
const unsupportedFormulaAttributes = new Set(Array.from(features.sheets.values()).flatMap((sheet)=>sheet.unsupportedFormulaAttributes));
|
|
1055
|
+
const issues = [];
|
|
1056
|
+
if (formulas.length) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas', `${formulas.length} formula cell(s), cached results, error values, and workbook calculation settings are preserved for XLSX round-trips.`, 'info'));
|
|
1057
|
+
if (arrayRanges || dynamicRanges) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.arrays', `${arrayRanges} legacy array range(s) and ${dynamicRanges} dynamic-array range(s) are preserved as native grouped XLSX formulas; modern spill functions may require Excel or another compatible application to recalculate.`, dynamicRanges ? 'warning' : 'info'));
|
|
1058
|
+
if (sharedGroups) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.shared', `${sharedGroups} shared-formula group(s) covering ${sharedCells} cell(s) are expanded to equivalent per-cell formulas so edits remain deterministic.`));
|
|
1059
|
+
if (dataTables) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.data-tables', `${dataTables} what-if data table(s), input references, and cached results are preserved, but Work does not recalculate scenario tables in the browser.`));
|
|
1060
|
+
if (cachedErrors) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.cached-errors', `${cachedErrors} formula cell(s) contain cached Excel error values; the error type and displayed value are preserved in editing, preview, print, and export.`));
|
|
1061
|
+
if (externalReferences.length) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.external-references', `${externalReferences.length} formula cell(s) reference external workbooks; formulas and cached values are preserved without refreshing external files.`));
|
|
1062
|
+
if (structuredReferences.length) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.structured-references', `${structuredReferences.length} formula cell(s) use structured table references; formulas round-trip unchanged, but the Work calculation engine does not resolve Excel table objects.`));
|
|
1063
|
+
if (unsupportedFunctions.size) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.unsupported-functions', `The current browser calculation engine does not evaluate ${Array.from(unsupportedFunctions).sort().join(', ')}; source formulas and cached results are preserved for compatible desktop recalculation.`));
|
|
1064
|
+
if (volatileFormulas.length) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.volatile', `${volatileFormulas.length} volatile formula cell(s) can change when the workbook is explicitly recalculated.`, 'info'));
|
|
1065
|
+
if (unsupportedFormulaAttributes.size) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.formulas.attributes', `Advanced formula attributes are not represented by the editable model and normalize on export: ${Array.from(unsupportedFormulaAttributes).sort().join(', ')}.`));
|
|
1066
|
+
if ('automatic' !== features.calculation.mode) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.calculation.mode', 'manual' === features.calculation.mode ? 'Manual workbook calculation mode is preserved; cached values remain visible until an explicit recalculation.' : 'Automatic calculation except for data tables is preserved; scenario tables require an explicit compatible recalculation.', 'info'));
|
|
1067
|
+
if (features.calculation.iterativeCalculation) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.calculation.iteration', `Iterative calculation is preserved with at most ${features.calculation.maximumIterations} iterations and a ${features.calculation.maximumChange} convergence threshold.`, 'info'));
|
|
1068
|
+
if (!features.calculation.fullPrecision) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.calculation.precision', 'Calculation using displayed precision is preserved and may produce results different from full-precision calculation.'));
|
|
1069
|
+
if (features.unsupportedCalculationAttributes.length) issues.push(work_xlsx_formula_diagnostics_issue('xlsx.calculation.advanced', `Advanced workbook calculation attributes remain source-only: ${features.unsupportedCalculationAttributes.join(', ')}.`));
|
|
1070
|
+
return issues;
|
|
1071
|
+
}
|
|
1072
|
+
function work_xlsx_formula_diagnostics_issue(code, message, severity = 'warning') {
|
|
1073
|
+
return {
|
|
1074
|
+
code,
|
|
1075
|
+
severity,
|
|
1076
|
+
feature: 'Formulas and calculation',
|
|
1077
|
+
message
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
function diagnoseXlsxPageSetup(document) {
|
|
1081
|
+
const issues = [];
|
|
1082
|
+
const root = document.documentElement;
|
|
1083
|
+
const pageSetup = directChild(root, 'pageSetup');
|
|
1084
|
+
const headerFooter = directChild(root, 'headerFooter');
|
|
1085
|
+
const hasPageSetup = Boolean(pageSetup) || Boolean(directChild(root, 'pageMargins')) || Boolean(directChild(root, 'printOptions')) || descendants(root, 'pageSetUpPr').length > 0;
|
|
1086
|
+
if (hasPageSetup) issues.push(work_xlsx_page_setup_diagnostics_issue('xlsx.page-setup', 'Page setup', 'Paper size, orientation, percentage or fit-to-page scaling, margins, centering, first-page numbering, and page order are preserved, editable, and honored by PDF pagination.', 'info'));
|
|
1087
|
+
if (pageSetup) inspectPrinterSetup(pageSetup, issues);
|
|
1088
|
+
if (headerFooter) inspectHeaderFooter(headerFooter, issues);
|
|
1089
|
+
return issues;
|
|
1090
|
+
}
|
|
1091
|
+
function inspectPrinterSetup(pageSetup, issues) {
|
|
1092
|
+
const paperSize = attribute(pageSetup, 'paperSize');
|
|
1093
|
+
if (paperSize && !isEditableXlsxPaperSizeCode(paperSize)) issues.push(work_xlsx_page_setup_diagnostics_issue('xlsx.page-setup.paper-size', 'Page setup', `Paper-size code ${paperSize} is not editable yet and falls back to A4 in Work PDF output.`));
|
|
1094
|
+
const orientation = attribute(pageSetup, 'orientation')?.toLowerCase();
|
|
1095
|
+
if (orientation && 'portrait' !== orientation && 'landscape' !== orientation) issues.push(work_xlsx_page_setup_diagnostics_issue('xlsx.page-setup.orientation', 'Page setup', `Page orientation "${orientation}" uses a printer-specific default and falls back to landscape in Work PDF output.`));
|
|
1096
|
+
const unsupportedAttributes = [
|
|
1097
|
+
'blackAndWhite',
|
|
1098
|
+
'cellComments',
|
|
1099
|
+
'copies',
|
|
1100
|
+
'draft',
|
|
1101
|
+
'errors',
|
|
1102
|
+
'horizontalDpi',
|
|
1103
|
+
'paperHeight',
|
|
1104
|
+
'paperWidth',
|
|
1105
|
+
'usePrinterDefaults',
|
|
1106
|
+
'verticalDpi',
|
|
1107
|
+
'r:id'
|
|
1108
|
+
].filter((name)=>null !== attribute(pageSetup, name));
|
|
1109
|
+
if (unsupportedAttributes.length) issues.push(work_xlsx_page_setup_diagnostics_issue('xlsx.page-setup.printer-settings', 'Page setup', `Printer-specific page-setup controls remain in the original workbook only: ${unsupportedAttributes.join(', ')}.`));
|
|
1110
|
+
}
|
|
1111
|
+
function inspectHeaderFooter(headerFooter, issues) {
|
|
1112
|
+
issues.push(work_xlsx_page_setup_diagnostics_issue('xlsx.header-footer', 'Print headers and footers', 'Odd-page plain-text header and footer sections, dynamic fields, page-number start, scaling, and margin alignment are preserved, editable, and rendered in PDF output.', 'info'));
|
|
1113
|
+
const firstOrEvenParts = [
|
|
1114
|
+
'firstHeader',
|
|
1115
|
+
'firstFooter',
|
|
1116
|
+
'evenHeader',
|
|
1117
|
+
'evenFooter'
|
|
1118
|
+
];
|
|
1119
|
+
const hasVariants = work_xlsx_page_setup_diagnostics_booleanAttribute(headerFooter, 'differentFirst') || work_xlsx_page_setup_diagnostics_booleanAttribute(headerFooter, 'differentOddEven') || firstOrEvenParts.some((name)=>Boolean(directChild(headerFooter, name)));
|
|
1120
|
+
if (hasVariants) issues.push(work_xlsx_page_setup_diagnostics_issue('xlsx.header-footer.variants', 'Print headers and footers', 'First-page and even-page header/footer variants remain in the original workbook only; Work edits the odd-page template.'));
|
|
1121
|
+
const inspection = directChildren(headerFooter).reduce((combined, element)=>{
|
|
1122
|
+
const current = inspectXlsxHeaderFooterText(element.textContent);
|
|
1123
|
+
return {
|
|
1124
|
+
hasFormatting: combined.hasFormatting || current.hasFormatting,
|
|
1125
|
+
hasImage: combined.hasImage || current.hasImage
|
|
1126
|
+
};
|
|
1127
|
+
}, {
|
|
1128
|
+
hasFormatting: false,
|
|
1129
|
+
hasImage: false
|
|
1130
|
+
});
|
|
1131
|
+
if (inspection.hasFormatting) issues.push(work_xlsx_page_setup_diagnostics_issue('xlsx.header-footer.formatting', 'Print headers and footers', 'Rich header/footer font formatting is normalized to editable plain text.'));
|
|
1132
|
+
if (inspection.hasImage) issues.push(work_xlsx_page_setup_diagnostics_issue('xlsx.header-footer.images', 'Print headers and footers', 'Header and footer images remain in the original workbook only.'));
|
|
1133
|
+
}
|
|
1134
|
+
function work_xlsx_page_setup_diagnostics_booleanAttribute(element, name) {
|
|
1135
|
+
const value = attribute(element, name)?.toLowerCase();
|
|
1136
|
+
return '1' === value || 'true' === value;
|
|
1137
|
+
}
|
|
1138
|
+
function work_xlsx_page_setup_diagnostics_issue(code, feature, message, severity = 'warning') {
|
|
1139
|
+
return {
|
|
1140
|
+
code,
|
|
1141
|
+
feature,
|
|
1142
|
+
message,
|
|
1143
|
+
severity
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
async function diagnoseXlsxPivots(archive) {
|
|
1147
|
+
const inspection = await inspectXlsxPivotTables(archive);
|
|
1148
|
+
const issues = [];
|
|
1149
|
+
if (inspection.tables.length) issues.push({
|
|
1150
|
+
code: 'xlsx.pivots',
|
|
1151
|
+
feature: 'Pivot tables',
|
|
1152
|
+
severity: 'info',
|
|
1153
|
+
message: `${inspection.tables.length} worksheet-backed pivot table(s) are editable, refreshable in Work, and regenerated with native XLSX definitions and caches.`
|
|
1154
|
+
});
|
|
1155
|
+
for (const unsupported of inspection.unsupported)issues.push({
|
|
1156
|
+
code: unsupported.code,
|
|
1157
|
+
feature: 'Pivot tables',
|
|
1158
|
+
severity: 'warning',
|
|
1159
|
+
message: `${unsupported.message} Cached worksheet results remain visible, but a new Work export contains them as ordinary cells rather than an editable native pivot.`,
|
|
1160
|
+
location: unsupported.location
|
|
1161
|
+
});
|
|
1162
|
+
return issues;
|
|
1163
|
+
}
|
|
1164
|
+
async function analyzeDocxCompatibility(file, messages) {
|
|
1165
|
+
const issues = [
|
|
1166
|
+
work_office_diagnostics_issue('docx.page-layout', 'Page layout', 'Per-section paper size, orientation, basic margins, one-to-six equal or custom-width columns, section breaks, and explicit page breaks are preserved; exact pagination and line wrapping may normalize.')
|
|
1167
|
+
];
|
|
1168
|
+
for (const message of messages)issues.push(work_office_diagnostics_issue(`docx.converter.${message.type}`, 'Document conversion', message.message, 'error' === message.type ? 'error' : 'warning'));
|
|
1169
|
+
try {
|
|
1170
|
+
const archive = await OoxmlPackage.load(await file.arrayBuffer());
|
|
1171
|
+
const document = archive.has('word/document.xml') ? await archive.xml('word/document.xml') : null;
|
|
1172
|
+
if (archive.has('word/comments.xml')) {
|
|
1173
|
+
const comments = await archive.xml('word/comments.xml');
|
|
1174
|
+
const commentDefinitions = descendants(comments, 'comment');
|
|
1175
|
+
if (commentDefinitions.length) {
|
|
1176
|
+
issues.push(work_office_diagnostics_issue('docx.comments', 'Comments', `${commentDefinitions.length} comment or reply record(s), text anchors, authors, dates, thread relationships, and resolved state are preserved and editable.`, 'info'));
|
|
1177
|
+
if ([
|
|
1178
|
+
'tbl',
|
|
1179
|
+
'drawing',
|
|
1180
|
+
'pict',
|
|
1181
|
+
'sdt',
|
|
1182
|
+
'altChunk'
|
|
1183
|
+
].some((name)=>descendants(comments, name).length) || descendants(comments, 'rPr').length) issues.push(work_office_diagnostics_issue('docx.comments.formatting', 'Comment formatting', 'Comment text is editable, but rich formatting, tables, images, and content controls inside comments are normalized to plain text.'));
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
issues.push(...await diagnoseDocxNotes(archive, document));
|
|
1187
|
+
issues.push(...await diagnoseDocxPageChrome(archive));
|
|
1188
|
+
if (archive.paths('word/embeddings/').length) issues.push(work_office_diagnostics_issue('docx.embedded', 'Embedded objects', 'Embedded Office and OLE objects remain in the original file only.'));
|
|
1189
|
+
if (document) {
|
|
1190
|
+
issues.push(...await diagnoseDocxCitations(archive, document));
|
|
1191
|
+
const captionDiagnostics = diagnoseDocxCaptions(document);
|
|
1192
|
+
issues.push(...captionDiagnostics.issues);
|
|
1193
|
+
if (descendants(document, 'tbl').length) issues.push(work_office_diagnostics_issue('docx.tables', 'Tables', 'Table rows, non-splitting rows, repeated headers, common cell shading, vertical alignment, and uniform borders remain editable; per-edge or theme borders, exact cell widths, nested tables, and rows that split internally may be normalized.'));
|
|
1194
|
+
if (descendants(document, 'drawing').length || descendants(document, 'pict').length) issues.push(work_office_diagnostics_issue('docx.images', 'Images', 'Inline images and supported square or top-and-bottom floating images remain embedded with editable size, alternative text, alignment, and wrap distance. Precise offsets, crop, contour wrapping, layering, and unsupported drawing types may be normalized.'));
|
|
1195
|
+
const textRevisions = [
|
|
1196
|
+
...descendants(document, 'ins'),
|
|
1197
|
+
...descendants(document, 'del')
|
|
1198
|
+
];
|
|
1199
|
+
if (textRevisions.some((revision)=>descendants(revision, 't').length || descendants(revision, 'delText').length)) issues.push(work_office_diagnostics_issue('docx.revisions', 'Tracked changes', 'Body-text insertions and deletions preserve their author and date, remain reviewable in Work, and round-trip as native DOCX revisions.', 'info'));
|
|
1200
|
+
if (textRevisions.some((revision)=>!descendants(revision, 't').length && !descendants(revision, 'delText').length) || [
|
|
1201
|
+
'moveFrom',
|
|
1202
|
+
'moveTo',
|
|
1203
|
+
'rPrChange',
|
|
1204
|
+
'pPrChange',
|
|
1205
|
+
'tblPrChange',
|
|
1206
|
+
'trPrChange',
|
|
1207
|
+
'tcPrChange',
|
|
1208
|
+
'sectPrChange',
|
|
1209
|
+
'numberingChange'
|
|
1210
|
+
].some((name)=>descendants(document, name).length)) issues.push(work_office_diagnostics_issue('docx.revisions.structural', 'Structural revisions', 'Moved content plus formatting, numbering, section, row, cell, and table-property revisions may be normalized; Work currently reviews body-text insertions and deletions.'));
|
|
1211
|
+
if (captionDiagnostics.hasUnsupportedFields) issues.push(work_office_diagnostics_issue('docx.fields', 'Fields', 'Fields beyond supported body fields, citations, bibliographies, caption SEQ fields, and caption REF fields are converted to their current displayed value.'));
|
|
1212
|
+
const sectionProperties = descendants(document, 'sectPr');
|
|
1213
|
+
const columnProperties = descendants(document, 'cols');
|
|
1214
|
+
if (sectionProperties.length > 1 || columnProperties.length) issues.push(work_office_diagnostics_issue('docx.sections', 'Sections and columns', `${sectionProperties.length || 1} section(s) and equal or custom-width column settings are preserved, editable, and applied to document PDF output.`, 'info'));
|
|
1215
|
+
const incompleteUnequalColumns = columnProperties.some((columns)=>('0' === attribute(columns, 'equalWidth') || 'false' === attribute(columns, 'equalWidth')) && 0 === directChildren(columns, 'col').length);
|
|
1216
|
+
const excessiveColumns = columnProperties.some((columns)=>Number(attribute(columns, 'num')) > 6);
|
|
1217
|
+
if (incompleteUnequalColumns || excessiveColumns) issues.push(work_office_diagnostics_issue('docx.sections.unsupported', 'Sections and columns', `${[
|
|
1218
|
+
incompleteUnequalColumns ? 'custom columns without explicit width definitions' : '',
|
|
1219
|
+
excessiveColumns ? 'sections with more than six columns' : ''
|
|
1220
|
+
].filter(Boolean).join(' and ')} are normalized to at most six editable columns.`));
|
|
1221
|
+
if (descendants(document, 'type').some((sectionType)=>sectionType.parentElement?.localName === 'sectPr' && 'nextColumn' === attribute(sectionType, 'val'))) issues.push(work_office_diagnostics_issue('docx.sections.next-column-preview', 'Sections and columns', 'Next-column section breaks survive DOCX round-trips; Work preview and PDF render them as continuous section blocks.'));
|
|
1222
|
+
}
|
|
1223
|
+
} catch {
|
|
1224
|
+
issues.push(work_office_diagnostics_issue('docx.inspect', 'Package inspection', 'Some DOCX features could not be inspected before conversion.'));
|
|
1225
|
+
}
|
|
1226
|
+
return report(file, 'DOCX', issues);
|
|
1227
|
+
}
|
|
1228
|
+
async function analyzeSpreadsheetCompatibility(file, extension, workbook) {
|
|
1229
|
+
if ('csv' === extension) return null;
|
|
1230
|
+
const sourceFormat = extension.toUpperCase();
|
|
1231
|
+
const issues = [];
|
|
1232
|
+
if ('xls' === extension) issues.push(work_office_diagnostics_issue('xls.legacy', 'Legacy workbook', 'The binary XLS workbook is converted to the modern native sheet model.'));
|
|
1233
|
+
else if ('ods' === extension) issues.push(work_office_diagnostics_issue('ods.styles', 'OpenDocument formatting', 'Advanced ODS formatting and chart behavior may be normalized on export to XLSX.'));
|
|
1234
|
+
const definedNames = workbook.Workbook?.Names ?? [];
|
|
1235
|
+
const names = definedNames.filter((name)=>!/^_xlnm\./i.test(name.Name));
|
|
1236
|
+
if (names.length) issues.push(work_office_diagnostics_issue('sheet.names', 'Named ranges', `${names.length} workbook or worksheet named range(s) are preserved and editable; external references are not refreshed.`, 'info'));
|
|
1237
|
+
const printAreas = definedNames.filter((name)=>'_xlnm.print_area' === name.Name.toLowerCase());
|
|
1238
|
+
if (printAreas.length) issues.push(work_office_diagnostics_issue('sheet.print-area', 'Print areas', `${printAreas.length} worksheet print area(s) are preserved and used by PDF export; disjoint regions may be combined into one bounding layout.`, 'info'));
|
|
1239
|
+
const printTitles = definedNames.filter((name)=>'_xlnm.print_titles' === name.Name.toLowerCase());
|
|
1240
|
+
const validPrintTitles = printTitles.filter((name)=>{
|
|
1241
|
+
if ('number' != typeof name.Sheet) return false;
|
|
1242
|
+
const sheetName = workbook.SheetNames[name.Sheet];
|
|
1243
|
+
return Boolean(sheetName && parseSpreadsheetPrintTitles(stripSpreadsheetSheetQualifier(name.Ref, sheetName)));
|
|
1244
|
+
});
|
|
1245
|
+
if (validPrintTitles.length) issues.push(work_office_diagnostics_issue('sheet.print-titles', 'Print titles', `${validPrintTitles.length} worksheet print-title setting(s) are preserved, editable, and repeated in PDF output.`, 'info'));
|
|
1246
|
+
if (validPrintTitles.length < printTitles.length) issues.push(work_office_diagnostics_issue('sheet.print-titles.invalid', 'Print titles', 'One or more malformed print-title definitions remain in the original workbook only.'));
|
|
1247
|
+
for (const [index, name] of workbook.SheetNames.entries()){
|
|
1248
|
+
const worksheet = workbook.Sheets[name];
|
|
1249
|
+
inspectWorksheetModel(worksheet, name, issues, 'xlsx' === extension);
|
|
1250
|
+
if ((workbook.Workbook?.Sheets?.[index]?.Hidden ?? 0) > 0) issues.push(work_office_diagnostics_issue('sheet.hidden', 'Hidden worksheets', 'Hidden worksheet state is preserved but can be changed by the editor.', 'info', name));
|
|
1251
|
+
}
|
|
1252
|
+
if ('xlsx' === extension) try {
|
|
1253
|
+
const archive = await OoxmlPackage.load(await file.arrayBuffer());
|
|
1254
|
+
await inspectXlsxPackage(archive, issues);
|
|
1255
|
+
} catch {
|
|
1256
|
+
issues.push(work_office_diagnostics_issue('xlsx.inspect', 'Package inspection', 'Some XLSX features could not be inspected before conversion.'));
|
|
1257
|
+
}
|
|
1258
|
+
return issues.length ? report(file, sourceFormat, deduplicate(issues)) : null;
|
|
1259
|
+
}
|
|
1260
|
+
function inspectWorksheetModel(worksheet, name, issues, nativeXlsxFormulaInspection) {
|
|
1261
|
+
if (!worksheet) return;
|
|
1262
|
+
if (worksheet['!autofilter']) issues.push(work_office_diagnostics_issue('sheet.filter', 'Filters', 'The auto-filter range and hidden rows are preserved; advanced active criteria may be normalized.', 'info', name));
|
|
1263
|
+
let hasComments = false;
|
|
1264
|
+
let hasCommentThreads = false;
|
|
1265
|
+
let hasLinks = false;
|
|
1266
|
+
let hasArrayFormulas = false;
|
|
1267
|
+
let hasRichText = false;
|
|
1268
|
+
for (const [address, cell] of Object.entries(worksheet)){
|
|
1269
|
+
if (address.startsWith('!') || !cell || 'object' != typeof cell) continue;
|
|
1270
|
+
const source = cell;
|
|
1271
|
+
if (source.c?.length) {
|
|
1272
|
+
hasComments = true;
|
|
1273
|
+
if (source.c.length > 1 || source.c.some((comment)=>comment.T)) hasCommentThreads = true;
|
|
1274
|
+
}
|
|
1275
|
+
if (source.l?.Target) hasLinks = true;
|
|
1276
|
+
if (source.F) hasArrayFormulas = true;
|
|
1277
|
+
if (source.r) hasRichText = true;
|
|
1278
|
+
}
|
|
1279
|
+
if (hasComments) issues.push(work_office_diagnostics_issue('sheet.comments', 'Cell comments', 'Plain-text cell comments and one author are preserved and editable through the spreadsheet comment tools.', 'info', name));
|
|
1280
|
+
if (hasCommentThreads) issues.push(work_office_diagnostics_issue('sheet.comment-threads', 'Comment threads', 'Multiple comment blocks and threaded replies are flattened into one editable legacy comment.', 'warning', name));
|
|
1281
|
+
if (hasLinks) issues.push(work_office_diagnostics_issue('sheet.links', 'Cell hyperlinks', 'Web and in-workbook hyperlinks are preserved; advanced screen tips may be normalized.', 'info', name));
|
|
1282
|
+
if (hasArrayFormulas && !nativeXlsxFormulaInspection) issues.push(work_office_diagnostics_issue('sheet.array-formula', 'Array formulas', 'Array-formula ranges may be recalculated as ordinary formulas.', 'warning', name));
|
|
1283
|
+
if (hasRichText) issues.push(work_office_diagnostics_issue('sheet.rich-text', 'Rich cell text', 'Rich text inside a cell is converted to uniform text.', 'warning', name));
|
|
1284
|
+
}
|
|
1285
|
+
async function inspectXlsxPackage(archive, issues) {
|
|
1286
|
+
issues.push(...await diagnoseXlsxCharts(archive));
|
|
1287
|
+
issues.push(...await diagnoseXlsxFormulas(archive));
|
|
1288
|
+
issues.push(...await diagnoseXlsxPivots(archive));
|
|
1289
|
+
const drawingParts = archive.paths('xl/drawings/').filter((path)=>/^xl\/drawings\/drawing\d+\.xml$/i.test(path));
|
|
1290
|
+
let preservedImages = 0;
|
|
1291
|
+
let hasUnsupportedImages = false;
|
|
1292
|
+
let hasImagesBeyondBudget = false;
|
|
1293
|
+
let hasNormalizedImageFormatting = false;
|
|
1294
|
+
let hasUnsupportedDrawings = false;
|
|
1295
|
+
let embeddedImageBytes = 0;
|
|
1296
|
+
for (const part of drawingParts){
|
|
1297
|
+
const drawing = await archive.xml(part);
|
|
1298
|
+
const relationships = await archive.relationships(part);
|
|
1299
|
+
for (const picture of descendants(drawing, 'pic')){
|
|
1300
|
+
const blip = firstDescendant(picture, 'blip');
|
|
1301
|
+
const relationshipId = blip ? attribute(blip, 'r:embed') ?? attribute(blip, 'embed') : null;
|
|
1302
|
+
const relationship = relationshipId ? relationships.get(relationshipId) : void 0;
|
|
1303
|
+
const contentType = relationship ? contentTypeForPart(relationship.target) : '';
|
|
1304
|
+
if (relationship && 'External' !== relationship.targetMode && relationship.type.endsWith('/image') && archive.has(relationship.target) && isSupportedXlsxWorksheetImageContentType(contentType)) {
|
|
1305
|
+
const bytes = await archive.bytes(relationship.target);
|
|
1306
|
+
if (embeddedImageBytes + bytes.byteLength <= MAX_XLSX_WORKSHEET_IMAGE_BYTES) {
|
|
1307
|
+
embeddedImageBytes += bytes.byteLength;
|
|
1308
|
+
preservedImages += 1;
|
|
1309
|
+
} else hasImagesBeyondBudget = true;
|
|
1310
|
+
} else hasUnsupportedImages = true;
|
|
1311
|
+
const sourceRectangle = firstDescendant(picture, 'srcRect');
|
|
1312
|
+
const transform = firstDescendant(directChild(picture, 'spPr'), 'xfrm');
|
|
1313
|
+
if (sourceRectangle && Array.from(sourceRectangle.attributes).some((item)=>0 !== Number(item.value)) || transform && (attribute(transform, 'rot') || '1' === attribute(transform, 'flipH') || '1' === attribute(transform, 'flipV'))) hasNormalizedImageFormatting = true;
|
|
1314
|
+
}
|
|
1315
|
+
hasUnsupportedDrawings ||= [
|
|
1316
|
+
'sp',
|
|
1317
|
+
'cxnSp',
|
|
1318
|
+
'grpSp',
|
|
1319
|
+
'contentPart'
|
|
1320
|
+
].some((name)=>descendants(drawing, name).length > 0);
|
|
1321
|
+
hasUnsupportedDrawings ||= descendants(drawing, 'graphicFrame').some((frame)=>!firstDescendant(frame, 'chart'));
|
|
1322
|
+
}
|
|
1323
|
+
if (preservedImages) issues.push(work_office_diagnostics_issue('xlsx.images', 'Worksheet images', `${preservedImages} embedded raster worksheet image(s), positions, sizes, names, and alternative text are preserved and editable.`, 'info'));
|
|
1324
|
+
if (hasUnsupportedImages) issues.push(work_office_diagnostics_issue('xlsx.images.unsupported', 'Worksheet images', 'Linked, missing, or browser-incompatible worksheet images remain in the original XLSX only.'));
|
|
1325
|
+
if (hasImagesBeyondBudget) issues.push(work_office_diagnostics_issue('xlsx.images.limit', 'Large worksheet images', 'Worksheet images beyond the 10 MiB editable image budget remain in the original XLSX only.'));
|
|
1326
|
+
if (hasNormalizedImageFormatting) issues.push(work_office_diagnostics_issue('xlsx.images.format', 'Worksheet image formatting', 'Worksheet image crop, rotation, or flip settings are normalized to an editable unrotated image.'));
|
|
1327
|
+
if (hasUnsupportedDrawings || archive.paths('xl/drawings/').length > 0 && 0 === drawingParts.length) issues.push(work_office_diagnostics_issue('xlsx.drawings.unsupported', 'Worksheet drawings', 'Worksheet shapes, connectors, SmartArt, and non-chart drawing objects remain in the original XLSX only.'));
|
|
1328
|
+
if (archive.paths('xl/comments').length || archive.paths('xl/threadedComments/').length) issues.push(work_office_diagnostics_issue('xlsx.comments', 'Comments', 'Legacy cell comments are imported and editable; rich formatting and threaded conversations may normalize.', 'info'));
|
|
1329
|
+
if (archive.paths('xl/tables/').length) issues.push(work_office_diagnostics_issue('xlsx.tables', 'Structured tables', 'Table values are preserved, but table names, styles, and totals are normalized.'));
|
|
1330
|
+
if (archive.paths('xl/externalLinks/').length) issues.push(work_office_diagnostics_issue('xlsx.external-links', 'External links', 'External workbook links are not refreshed by Work.'));
|
|
1331
|
+
if (archive.has('xl/vbaProject.bin')) issues.push(work_office_diagnostics_issue('xlsx.macros', 'Macros', 'VBA macros remain in the original workbook and are never executed.'));
|
|
1332
|
+
let styles = null;
|
|
1333
|
+
if (archive.has('xl/styles.xml')) {
|
|
1334
|
+
styles = await archive.xml('xl/styles.xml');
|
|
1335
|
+
const cellFormats = firstDescendant(styles, 'cellXfs');
|
|
1336
|
+
if (cellFormats && directChildren(cellFormats, 'xf').length > 1) issues.push(work_office_diagnostics_issue('xlsx.styles', 'Cell formatting', 'Common cell formatting is imported; advanced borders and number formats may be normalized.'));
|
|
1337
|
+
}
|
|
1338
|
+
for (const part of archive.paths('xl/worksheets/')){
|
|
1339
|
+
if (!part.endsWith('.xml')) continue;
|
|
1340
|
+
const worksheet = await archive.xml(part);
|
|
1341
|
+
if (descendants(worksheet, 'conditionalFormatting').length) for (const diagnostic of diagnoseXlsxConditionalFormatting(worksheet, styles))issues.push(work_office_diagnostics_issue(diagnostic.code, 'Conditional formatting', diagnostic.message, diagnostic.severity));
|
|
1342
|
+
if (descendants(worksheet, 'dataValidation').length) issues.push(work_office_diagnostics_issue('xlsx.validation', 'Data validation', 'Common list, numeric, date, and text-length rules are editable; custom formulas and very large ranges may be normalized.', 'info'));
|
|
1343
|
+
if (descendants(worksheet, 'sheetProtection').length || descendants(worksheet, 'protectedRange').length) for (const diagnostic of diagnoseXlsxProtection(worksheet, styles))issues.push(work_office_diagnostics_issue(diagnostic.code, 'Sheet protection', diagnostic.message, diagnostic.severity));
|
|
1344
|
+
issues.push(...diagnoseXlsxPageSetup(worksheet));
|
|
1345
|
+
if (descendants(worksheet, 'brk').some((pageBreak)=>{
|
|
1346
|
+
const manual = attribute(pageBreak, 'man')?.toLowerCase();
|
|
1347
|
+
return '1' === manual || 'true' === manual;
|
|
1348
|
+
})) issues.push(work_office_diagnostics_issue('xlsx.manual-page-breaks', 'Manual page breaks', 'Manual row and column page breaks are preserved, editable, and honored by PDF pagination.', 'info'));
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
function report(file, sourceFormat, issues) {
|
|
1352
|
+
return {
|
|
1353
|
+
sourceFormat,
|
|
1354
|
+
sourceName: file.name,
|
|
1355
|
+
assessedAt: Date.now(),
|
|
1356
|
+
issues: deduplicate(issues)
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
function work_office_diagnostics_issue(code, feature, message, severity = 'warning', location) {
|
|
1360
|
+
return {
|
|
1361
|
+
code,
|
|
1362
|
+
feature,
|
|
1363
|
+
message,
|
|
1364
|
+
severity,
|
|
1365
|
+
location
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
function deduplicate(issues) {
|
|
1369
|
+
const seen = new Set();
|
|
1370
|
+
return issues.filter((item)=>{
|
|
1371
|
+
const key = `${item.code}:${item.location ?? ''}`;
|
|
1372
|
+
if (seen.has(key)) return false;
|
|
1373
|
+
seen.add(key);
|
|
1374
|
+
return true;
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
export { analyzeDocxCompatibility, analyzeSpreadsheetCompatibility };
|