@cj-tech-master/excelts 9.4.2 → 9.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.browser.d.ts +8 -5
- package/dist/browser/index.browser.js +19 -1
- package/dist/browser/index.d.ts +4 -2
- package/dist/browser/index.js +9 -1
- package/dist/browser/modules/excel/chart/cache-populator.d.ts +49 -0
- package/dist/browser/modules/excel/chart/cache-populator.js +1171 -0
- package/dist/browser/modules/excel/chart/chart-api.d.ts +92 -0
- package/dist/browser/modules/excel/chart/chart-api.js +364 -0
- package/dist/browser/modules/excel/chart/chart-builder.d.ts +48 -0
- package/dist/browser/modules/excel/chart/chart-builder.js +2432 -0
- package/dist/browser/modules/excel/chart/chart-ex-builder.d.ts +36 -0
- package/dist/browser/modules/excel/chart/chart-ex-builder.js +903 -0
- package/dist/browser/modules/excel/chart/chart-ex-parser.d.ts +8 -0
- package/dist/browser/modules/excel/chart/chart-ex-parser.js +1205 -0
- package/dist/browser/modules/excel/chart/chart-ex-renderer.d.ts +187 -0
- package/dist/browser/modules/excel/chart/chart-ex-renderer.js +5352 -0
- package/dist/browser/modules/excel/chart/chart-ex-types.d.ts +531 -0
- package/dist/browser/modules/excel/chart/chart-ex-types.js +11 -0
- package/dist/browser/modules/excel/chart/chart-images.d.ts +78 -0
- package/dist/browser/modules/excel/chart/chart-images.js +363 -0
- package/dist/browser/modules/excel/chart/chart-presets.d.ts +392 -0
- package/dist/browser/modules/excel/chart/chart-presets.js +179 -0
- package/dist/browser/modules/excel/chart/chart-renderer.d.ts +550 -0
- package/dist/browser/modules/excel/chart/chart-renderer.js +6440 -0
- package/dist/browser/modules/excel/chart/chart-sidecar.d.ts +21 -0
- package/dist/browser/modules/excel/chart/chart-sidecar.js +427 -0
- package/dist/browser/modules/excel/chart/chart-utils.d.ts +306 -0
- package/dist/browser/modules/excel/chart/chart-utils.js +821 -0
- package/dist/browser/modules/excel/chart/chart.d.ts +504 -0
- package/dist/browser/modules/excel/chart/chart.js +1320 -0
- package/dist/browser/modules/excel/chart/glyph-rasterizer.d.ts +62 -0
- package/dist/browser/modules/excel/chart/glyph-rasterizer.js +658 -0
- package/dist/browser/modules/excel/chart/index.d.ts +54 -0
- package/dist/browser/modules/excel/chart/index.js +46 -0
- package/dist/browser/modules/excel/chart/install.d.ts +44 -0
- package/dist/browser/modules/excel/chart/install.js +91 -0
- package/dist/browser/modules/excel/chart/shape-properties.d.ts +156 -0
- package/dist/browser/modules/excel/chart/shape-properties.js +1557 -0
- package/dist/browser/modules/excel/chart/stroke-font.d.ts +36 -0
- package/dist/browser/modules/excel/chart/stroke-font.js +1556 -0
- package/dist/browser/modules/excel/chart/topojson.d.ts +98 -0
- package/dist/browser/modules/excel/chart/topojson.js +236 -0
- package/dist/browser/modules/excel/chart/types.d.ts +2559 -0
- package/dist/browser/modules/excel/chart/types.js +8 -0
- package/dist/browser/modules/excel/chart-host-registry.d.ts +157 -0
- package/dist/browser/modules/excel/chart-host-registry.js +90 -0
- package/dist/browser/modules/excel/chartsheet.d.ts +102 -0
- package/dist/browser/modules/excel/chartsheet.js +196 -0
- package/dist/browser/modules/excel/defined-names.d.ts +35 -0
- package/dist/browser/modules/excel/defined-names.js +44 -4
- package/dist/browser/modules/excel/errors.d.ts +6 -0
- package/dist/browser/modules/excel/errors.js +9 -0
- package/dist/browser/modules/excel/form-control.d.ts +6 -0
- package/dist/browser/modules/excel/form-control.js +17 -0
- package/dist/browser/modules/excel/image.js +12 -2
- package/dist/browser/modules/excel/pivot-chart.d.ts +7 -0
- package/dist/browser/modules/excel/pivot-chart.js +53 -0
- package/dist/browser/modules/excel/pivot-table.d.ts +55 -0
- package/dist/browser/modules/excel/pivot-table.js +35 -0
- package/dist/browser/modules/excel/range.js +5 -1
- package/dist/browser/modules/excel/sparkline/index.d.ts +7 -0
- package/dist/browser/modules/excel/sparkline/index.js +7 -0
- package/dist/browser/modules/excel/sparkline/sparkline.d.ts +206 -0
- package/dist/browser/modules/excel/sparkline/sparkline.js +750 -0
- package/dist/browser/modules/excel/stream/worksheet-writer.js +3 -2
- package/dist/browser/modules/excel/table.js +42 -6
- package/dist/browser/modules/excel/types.d.ts +72 -0
- package/dist/browser/modules/excel/utils/address.d.ts +18 -0
- package/dist/browser/modules/excel/utils/address.js +28 -0
- package/dist/browser/modules/excel/utils/drawing-utils.js +11 -6
- package/dist/browser/modules/excel/utils/guid.d.ts +15 -0
- package/dist/browser/modules/excel/utils/guid.js +35 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +74 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.js +206 -9
- package/dist/browser/modules/excel/utils/ooxml-validator/check-chart-sidecar.d.ts +35 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-chart-sidecar.js +101 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-chart.d.ts +32 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-chart.js +2125 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-chartsheet.d.ts +9 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-chartsheet.js +26 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-content-types.d.ts +16 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-content-types.js +181 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-drawing.d.ts +34 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-drawing.js +267 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-pivot.d.ts +14 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-pivot.js +104 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-relationships.d.ts +18 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-relationships.js +184 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-structure.d.ts +21 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-structure.js +56 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-styles.d.ts +15 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-styles.js +89 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-table.d.ts +31 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-table.js +177 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-workbook.d.ts +19 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-workbook.js +163 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-worksheet.d.ts +25 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/check-worksheet.js +569 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/context.d.ts +85 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/context.js +191 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/index.d.ts +31 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/index.js +102 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/path-utils.d.ts +67 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/path-utils.js +156 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/reporter.d.ts +41 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/reporter.js +61 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/types.d.ts +109 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/types.js +12 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/xml-utils.d.ts +38 -0
- package/dist/browser/modules/excel/utils/ooxml-validator/xml-utils.js +100 -0
- package/dist/browser/modules/excel/workbook.browser.d.ts +248 -30
- package/dist/browser/modules/excel/workbook.browser.js +966 -31
- package/dist/browser/modules/excel/workbook.d.ts +43 -0
- package/dist/browser/modules/excel/workbook.js +48 -0
- package/dist/browser/modules/excel/worksheet.d.ts +157 -3
- package/dist/browser/modules/excel/worksheet.js +394 -35
- package/dist/browser/modules/excel/xlsx/rel-type.d.ts +40 -0
- package/dist/browser/modules/excel/xlsx/rel-type.js +41 -1
- package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/book/defined-name-xform.js +11 -2
- package/dist/browser/modules/excel/xlsx/xform/book/external-link-xform.js +12 -10
- package/dist/browser/modules/excel/xlsx/xform/book/workbook-xform.js +96 -22
- package/dist/browser/modules/excel/xlsx/xform/chart/chart-space-xform.d.ts +353 -0
- package/dist/browser/modules/excel/xlsx/xform/chart/chart-space-xform.js +6000 -0
- package/dist/browser/modules/excel/xlsx/xform/comment/threaded-comments-xform.d.ts +60 -0
- package/dist/browser/modules/excel/xlsx/xform/comment/threaded-comments-xform.js +213 -0
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +150 -11
- package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +20 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +1 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/drawing-xform.d.ts +30 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/drawing-xform.js +109 -5
- package/dist/browser/modules/excel/xlsx/xform/drawing/graphic-frame-xform.d.ts +54 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/graphic-frame-xform.js +225 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +3 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +18 -3
- package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.d.ts +46 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +294 -12
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +13 -2
- package/dist/browser/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +32 -6
- package/dist/browser/modules/excel/xlsx/xform/sheet/chartsheet-xform.d.ts +185 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/chartsheet-xform.js +441 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/ext-lst-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/ext-lst-xform.js +51 -2
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +196 -20
- package/dist/browser/modules/excel/xlsx/xform/table/auto-filter-xform.js +16 -1
- package/dist/browser/modules/excel/xlsx/xform/table/table-column-xform.js +17 -2
- package/dist/browser/modules/excel/xlsx/xform/xsd-values.d.ts +63 -0
- package/dist/browser/modules/excel/xlsx/xform/xsd-values.js +101 -0
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +115 -21
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +4422 -78
- package/dist/browser/modules/pdf/builder/document-builder.d.ts +74 -0
- package/dist/browser/modules/pdf/builder/document-builder.js +507 -2
- package/dist/browser/modules/pdf/builder/pdf-editor.js +48 -3
- package/dist/browser/modules/pdf/excel-bridge.d.ts +69 -0
- package/dist/browser/modules/pdf/excel-bridge.js +683 -12
- package/dist/browser/modules/pdf/font/font-manager.d.ts +25 -0
- package/dist/browser/modules/pdf/font/font-manager.js +39 -0
- package/dist/browser/modules/pdf/index.d.ts +5 -2
- package/dist/browser/modules/pdf/index.js +3 -1
- package/dist/browser/modules/pdf/render/chart-surface.d.ts +33 -0
- package/dist/browser/modules/pdf/render/chart-surface.js +200 -0
- package/dist/browser/modules/pdf/render/layout-engine.d.ts +22 -1
- package/dist/browser/modules/pdf/render/layout-engine.js +436 -56
- package/dist/browser/modules/pdf/render/page-renderer.js +169 -28
- package/dist/browser/modules/pdf/render/pdf-exporter.js +117 -7
- package/dist/browser/modules/pdf/types.d.ts +227 -23
- package/dist/browser/modules/pdf/types.js +4 -0
- package/dist/browser/modules/pdf/word-bridge.d.ts +47 -0
- package/dist/browser/modules/pdf/word-bridge.js +304 -0
- package/dist/browser/modules/word/constants.d.ts +179 -0
- package/dist/browser/modules/word/constants.js +231 -0
- package/dist/browser/modules/word/content-types.d.ts +27 -0
- package/dist/browser/modules/word/content-types.js +53 -0
- package/dist/browser/modules/word/digital-signatures.d.ts +87 -0
- package/dist/browser/modules/word/digital-signatures.js +134 -0
- package/dist/browser/modules/word/document.d.ts +728 -0
- package/dist/browser/modules/word/document.js +1795 -0
- package/dist/browser/modules/word/docx-packager.d.ts +14 -0
- package/dist/browser/modules/word/docx-packager.js +822 -0
- package/dist/browser/modules/word/docx-reader.d.ts +11 -0
- package/dist/browser/modules/word/docx-reader.js +4929 -0
- package/dist/browser/modules/word/encryption.d.ts +102 -0
- package/dist/browser/modules/word/encryption.js +274 -0
- package/dist/browser/modules/word/errors.d.ts +49 -0
- package/dist/browser/modules/word/errors.js +68 -0
- package/dist/browser/modules/word/font-obfuscation.d.ts +31 -0
- package/dist/browser/modules/word/font-obfuscation.js +83 -0
- package/dist/browser/modules/word/html-renderer.d.ts +38 -0
- package/dist/browser/modules/word/html-renderer.js +782 -0
- package/dist/browser/modules/word/index.base.d.ts +19 -0
- package/dist/browser/modules/word/index.base.js +51 -0
- package/dist/browser/modules/word/index.browser.d.ts +4 -0
- package/dist/browser/modules/word/index.browser.js +4 -0
- package/dist/browser/modules/word/index.d.ts +4 -0
- package/dist/browser/modules/word/index.js +4 -0
- package/dist/browser/modules/word/internal-utils.d.ts +23 -0
- package/dist/browser/modules/word/internal-utils.js +54 -0
- package/dist/browser/modules/word/relationships.d.ts +31 -0
- package/dist/browser/modules/word/relationships.js +56 -0
- package/dist/browser/modules/word/types.d.ts +2325 -0
- package/dist/browser/modules/word/types.js +10 -0
- package/dist/browser/modules/word/units.d.ts +49 -0
- package/dist/browser/modules/word/units.js +111 -0
- package/dist/browser/modules/word/writers/chart-writer.d.ts +10 -0
- package/dist/browser/modules/word/writers/chart-writer.js +385 -0
- package/dist/browser/modules/word/writers/checkbox-writer.d.ts +9 -0
- package/dist/browser/modules/word/writers/checkbox-writer.js +42 -0
- package/dist/browser/modules/word/writers/comment-writer.d.ts +15 -0
- package/dist/browser/modules/word/writers/comment-writer.js +70 -0
- package/dist/browser/modules/word/writers/document-writer.d.ts +16 -0
- package/dist/browser/modules/word/writers/document-writer.js +461 -0
- package/dist/browser/modules/word/writers/footnote-writer.d.ts +11 -0
- package/dist/browser/modules/word/writers/footnote-writer.js +72 -0
- package/dist/browser/modules/word/writers/header-footer-writer.d.ts +13 -0
- package/dist/browser/modules/word/writers/header-footer-writer.js +129 -0
- package/dist/browser/modules/word/writers/image-writer.d.ts +10 -0
- package/dist/browser/modules/word/writers/image-writer.js +185 -0
- package/dist/browser/modules/word/writers/math-writer.d.ts +9 -0
- package/dist/browser/modules/word/writers/math-writer.js +428 -0
- package/dist/browser/modules/word/writers/numbering-writer.d.ts +10 -0
- package/dist/browser/modules/word/writers/numbering-writer.js +125 -0
- package/dist/browser/modules/word/writers/paragraph-writer.d.ts +13 -0
- package/dist/browser/modules/word/writers/paragraph-writer.js +516 -0
- package/dist/browser/modules/word/writers/parts-writer.d.ts +26 -0
- package/dist/browser/modules/word/writers/parts-writer.js +660 -0
- package/dist/browser/modules/word/writers/run-writer.d.ts +15 -0
- package/dist/browser/modules/word/writers/run-writer.js +649 -0
- package/dist/browser/modules/word/writers/section-writer.d.ts +10 -0
- package/dist/browser/modules/word/writers/section-writer.js +238 -0
- package/dist/browser/modules/word/writers/styles-writer.d.ts +10 -0
- package/dist/browser/modules/word/writers/styles-writer.js +242 -0
- package/dist/browser/modules/word/writers/table-writer.d.ts +10 -0
- package/dist/browser/modules/word/writers/table-writer.js +503 -0
- package/dist/browser/modules/word/writers/textbox-writer.d.ts +9 -0
- package/dist/browser/modules/word/writers/textbox-writer.js +53 -0
- package/dist/browser/modules/word/writers/toc-writer.d.ts +9 -0
- package/dist/browser/modules/word/writers/toc-writer.js +79 -0
- package/dist/browser/modules/xml/encode.d.ts +56 -7
- package/dist/browser/modules/xml/encode.js +157 -11
- package/dist/cjs/index.js +13 -2
- package/dist/cjs/modules/excel/chart/cache-populator.js +1178 -0
- package/dist/cjs/modules/excel/chart/chart-api.js +371 -0
- package/dist/cjs/modules/excel/chart/chart-builder.js +2440 -0
- package/dist/cjs/modules/excel/chart/chart-ex-builder.js +907 -0
- package/dist/cjs/modules/excel/chart/chart-ex-parser.js +1208 -0
- package/dist/cjs/modules/excel/chart/chart-ex-renderer.js +5364 -0
- package/dist/cjs/modules/excel/chart/chart-ex-types.js +12 -0
- package/dist/cjs/modules/excel/chart/chart-images.js +366 -0
- package/dist/cjs/modules/excel/chart/chart-presets.js +184 -0
- package/dist/cjs/modules/excel/chart/chart-renderer.js +6450 -0
- package/dist/cjs/modules/excel/chart/chart-sidecar.js +433 -0
- package/dist/cjs/modules/excel/chart/chart-utils.js +845 -0
- package/dist/cjs/modules/excel/chart/chart.js +1324 -0
- package/dist/cjs/modules/excel/chart/glyph-rasterizer.js +664 -0
- package/dist/cjs/modules/excel/chart/index.js +101 -0
- package/dist/cjs/modules/excel/chart/install.js +95 -0
- package/dist/cjs/modules/excel/chart/shape-properties.js +1577 -0
- package/dist/cjs/modules/excel/chart/stroke-font.js +1559 -0
- package/dist/cjs/modules/excel/chart/topojson.js +239 -0
- package/dist/cjs/modules/excel/chart/types.js +9 -0
- package/dist/cjs/modules/excel/chart-host-registry.js +96 -0
- package/dist/cjs/modules/excel/chartsheet.js +199 -0
- package/dist/cjs/modules/excel/defined-names.js +44 -4
- package/dist/cjs/modules/excel/errors.js +11 -1
- package/dist/cjs/modules/excel/form-control.js +17 -0
- package/dist/cjs/modules/excel/image.js +12 -2
- package/dist/cjs/modules/excel/pivot-chart.js +56 -0
- package/dist/cjs/modules/excel/pivot-table.js +35 -0
- package/dist/cjs/modules/excel/range.js +5 -1
- package/dist/cjs/modules/excel/sparkline/index.js +23 -0
- package/dist/cjs/modules/excel/sparkline/sparkline.js +756 -0
- package/dist/cjs/modules/excel/stream/worksheet-writer.js +3 -2
- package/dist/cjs/modules/excel/table.js +42 -6
- package/dist/cjs/modules/excel/utils/address.js +29 -0
- package/dist/cjs/modules/excel/utils/drawing-utils.js +11 -6
- package/dist/cjs/modules/excel/utils/guid.js +38 -0
- package/dist/cjs/modules/excel/utils/ooxml-paths.js +246 -9
- package/dist/cjs/modules/excel/utils/ooxml-validator/check-chart-sidecar.js +103 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/check-chart.js +2128 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/check-chartsheet.js +29 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/check-content-types.js +184 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/check-drawing.js +270 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/check-pivot.js +107 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/check-relationships.js +188 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/check-structure.js +60 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/check-styles.js +92 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/check-table.js +180 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/check-workbook.js +166 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/check-worksheet.js +572 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/context.js +196 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/index.js +105 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/path-utils.js +168 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/reporter.js +66 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/types.js +13 -0
- package/dist/cjs/modules/excel/utils/ooxml-validator/xml-utils.js +110 -0
- package/dist/cjs/modules/excel/workbook.browser.js +973 -38
- package/dist/cjs/modules/excel/workbook.js +48 -0
- package/dist/cjs/modules/excel/worksheet.js +393 -34
- package/dist/cjs/modules/excel/xlsx/rel-type.js +41 -1
- package/dist/cjs/modules/excel/xlsx/xform/book/defined-name-xform.js +11 -2
- package/dist/cjs/modules/excel/xlsx/xform/book/external-link-xform.js +12 -10
- package/dist/cjs/modules/excel/xlsx/xform/book/workbook-xform.js +96 -22
- package/dist/cjs/modules/excel/xlsx/xform/chart/chart-space-xform.js +6003 -0
- package/dist/cjs/modules/excel/xlsx/xform/comment/threaded-comments-xform.js +219 -0
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +149 -10
- package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +20 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +1 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/drawing-xform.js +109 -5
- package/dist/cjs/modules/excel/xlsx/xform/drawing/graphic-frame-xform.js +228 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +18 -3
- package/dist/cjs/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +294 -12
- package/dist/cjs/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +32 -6
- package/dist/cjs/modules/excel/xlsx/xform/sheet/chartsheet-xform.js +444 -0
- package/dist/cjs/modules/excel/xlsx/xform/sheet/ext-lst-xform.js +51 -2
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +195 -19
- package/dist/cjs/modules/excel/xlsx/xform/table/auto-filter-xform.js +16 -1
- package/dist/cjs/modules/excel/xlsx/xform/table/table-column-xform.js +17 -2
- package/dist/cjs/modules/excel/xlsx/xform/xsd-values.js +106 -0
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +4420 -76
- package/dist/cjs/modules/pdf/builder/document-builder.js +506 -1
- package/dist/cjs/modules/pdf/builder/pdf-editor.js +48 -3
- package/dist/cjs/modules/pdf/excel-bridge.js +684 -12
- package/dist/cjs/modules/pdf/font/font-manager.js +39 -0
- package/dist/cjs/modules/pdf/index.js +5 -1
- package/dist/cjs/modules/pdf/render/chart-surface.js +203 -0
- package/dist/cjs/modules/pdf/render/layout-engine.js +437 -56
- package/dist/cjs/modules/pdf/render/page-renderer.js +169 -28
- package/dist/cjs/modules/pdf/render/pdf-exporter.js +115 -5
- package/dist/cjs/modules/pdf/types.js +5 -0
- package/dist/cjs/modules/pdf/word-bridge.js +307 -0
- package/dist/cjs/modules/word/constants.js +234 -0
- package/dist/cjs/modules/word/content-types.js +57 -0
- package/dist/cjs/modules/word/digital-signatures.js +140 -0
- package/dist/cjs/modules/word/document.js +1909 -0
- package/dist/cjs/modules/word/docx-packager.js +825 -0
- package/dist/cjs/modules/word/docx-reader.js +4932 -0
- package/dist/cjs/modules/word/encryption.js +282 -0
- package/dist/cjs/modules/word/errors.js +88 -0
- package/dist/cjs/modules/word/font-obfuscation.js +88 -0
- package/dist/cjs/modules/word/html-renderer.js +785 -0
- package/dist/cjs/modules/word/index.base.js +199 -0
- package/dist/cjs/modules/word/index.browser.js +20 -0
- package/dist/cjs/modules/word/index.js +20 -0
- package/dist/cjs/modules/word/internal-utils.js +59 -0
- package/dist/cjs/modules/word/relationships.js +60 -0
- package/dist/cjs/modules/word/types.js +11 -0
- package/dist/cjs/modules/word/units.js +135 -0
- package/dist/cjs/modules/word/writers/chart-writer.js +388 -0
- package/dist/cjs/modules/word/writers/checkbox-writer.js +45 -0
- package/dist/cjs/modules/word/writers/comment-writer.js +74 -0
- package/dist/cjs/modules/word/writers/document-writer.js +465 -0
- package/dist/cjs/modules/word/writers/footnote-writer.js +76 -0
- package/dist/cjs/modules/word/writers/header-footer-writer.js +134 -0
- package/dist/cjs/modules/word/writers/image-writer.js +188 -0
- package/dist/cjs/modules/word/writers/math-writer.js +431 -0
- package/dist/cjs/modules/word/writers/numbering-writer.js +128 -0
- package/dist/cjs/modules/word/writers/paragraph-writer.js +521 -0
- package/dist/cjs/modules/word/writers/parts-writer.js +671 -0
- package/dist/cjs/modules/word/writers/run-writer.js +655 -0
- package/dist/cjs/modules/word/writers/section-writer.js +241 -0
- package/dist/cjs/modules/word/writers/styles-writer.js +245 -0
- package/dist/cjs/modules/word/writers/table-writer.js +506 -0
- package/dist/cjs/modules/word/writers/textbox-writer.js +56 -0
- package/dist/cjs/modules/word/writers/toc-writer.js +82 -0
- package/dist/cjs/modules/xml/encode.js +158 -11
- package/dist/esm/index.browser.js +20 -2
- package/dist/esm/index.js +9 -1
- package/dist/esm/modules/excel/chart/cache-populator.js +1171 -0
- package/dist/esm/modules/excel/chart/chart-api.js +364 -0
- package/dist/esm/modules/excel/chart/chart-builder.js +2432 -0
- package/dist/esm/modules/excel/chart/chart-ex-builder.js +903 -0
- package/dist/esm/modules/excel/chart/chart-ex-parser.js +1205 -0
- package/dist/esm/modules/excel/chart/chart-ex-renderer.js +5352 -0
- package/dist/esm/modules/excel/chart/chart-ex-types.js +11 -0
- package/dist/esm/modules/excel/chart/chart-images.js +363 -0
- package/dist/esm/modules/excel/chart/chart-presets.js +179 -0
- package/dist/esm/modules/excel/chart/chart-renderer.js +6440 -0
- package/dist/esm/modules/excel/chart/chart-sidecar.js +427 -0
- package/dist/esm/modules/excel/chart/chart-utils.js +821 -0
- package/dist/esm/modules/excel/chart/chart.js +1320 -0
- package/dist/esm/modules/excel/chart/glyph-rasterizer.js +658 -0
- package/dist/esm/modules/excel/chart/index.js +46 -0
- package/dist/esm/modules/excel/chart/install.js +91 -0
- package/dist/esm/modules/excel/chart/shape-properties.js +1557 -0
- package/dist/esm/modules/excel/chart/stroke-font.js +1556 -0
- package/dist/esm/modules/excel/chart/topojson.js +236 -0
- package/dist/esm/modules/excel/chart/types.js +8 -0
- package/dist/esm/modules/excel/chart-host-registry.js +90 -0
- package/dist/esm/modules/excel/chartsheet.js +196 -0
- package/dist/esm/modules/excel/defined-names.js +44 -4
- package/dist/esm/modules/excel/errors.js +9 -0
- package/dist/esm/modules/excel/form-control.js +17 -0
- package/dist/esm/modules/excel/image.js +12 -2
- package/dist/esm/modules/excel/pivot-chart.js +53 -0
- package/dist/esm/modules/excel/pivot-table.js +35 -0
- package/dist/esm/modules/excel/range.js +5 -1
- package/dist/esm/modules/excel/sparkline/index.js +7 -0
- package/dist/esm/modules/excel/sparkline/sparkline.js +750 -0
- package/dist/esm/modules/excel/stream/worksheet-writer.js +3 -2
- package/dist/esm/modules/excel/table.js +42 -6
- package/dist/esm/modules/excel/utils/address.js +28 -0
- package/dist/esm/modules/excel/utils/drawing-utils.js +11 -6
- package/dist/esm/modules/excel/utils/guid.js +35 -0
- package/dist/esm/modules/excel/utils/ooxml-paths.js +206 -9
- package/dist/esm/modules/excel/utils/ooxml-validator/check-chart-sidecar.js +101 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/check-chart.js +2125 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/check-chartsheet.js +26 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/check-content-types.js +181 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/check-drawing.js +267 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/check-pivot.js +104 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/check-relationships.js +184 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/check-structure.js +56 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/check-styles.js +89 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/check-table.js +177 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/check-workbook.js +163 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/check-worksheet.js +569 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/context.js +191 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/index.js +102 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/path-utils.js +156 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/reporter.js +61 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/types.js +12 -0
- package/dist/esm/modules/excel/utils/ooxml-validator/xml-utils.js +100 -0
- package/dist/esm/modules/excel/workbook.browser.js +969 -34
- package/dist/esm/modules/excel/workbook.js +48 -0
- package/dist/esm/modules/excel/worksheet.js +394 -35
- package/dist/esm/modules/excel/xlsx/rel-type.js +41 -1
- package/dist/esm/modules/excel/xlsx/xform/book/defined-name-xform.js +11 -2
- package/dist/esm/modules/excel/xlsx/xform/book/external-link-xform.js +12 -10
- package/dist/esm/modules/excel/xlsx/xform/book/workbook-xform.js +96 -22
- package/dist/esm/modules/excel/xlsx/xform/chart/chart-space-xform.js +6000 -0
- package/dist/esm/modules/excel/xlsx/xform/comment/threaded-comments-xform.js +213 -0
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +150 -11
- package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +20 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +1 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/drawing-xform.js +109 -5
- package/dist/esm/modules/excel/xlsx/xform/drawing/graphic-frame-xform.js +225 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +18 -3
- package/dist/esm/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +294 -12
- package/dist/esm/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.js +32 -6
- package/dist/esm/modules/excel/xlsx/xform/sheet/chartsheet-xform.js +441 -0
- package/dist/esm/modules/excel/xlsx/xform/sheet/ext-lst-xform.js +51 -2
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +196 -20
- package/dist/esm/modules/excel/xlsx/xform/table/auto-filter-xform.js +16 -1
- package/dist/esm/modules/excel/xlsx/xform/table/table-column-xform.js +17 -2
- package/dist/esm/modules/excel/xlsx/xform/xsd-values.js +101 -0
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +4422 -78
- package/dist/esm/modules/pdf/builder/document-builder.js +507 -2
- package/dist/esm/modules/pdf/builder/pdf-editor.js +48 -3
- package/dist/esm/modules/pdf/excel-bridge.js +683 -12
- package/dist/esm/modules/pdf/font/font-manager.js +39 -0
- package/dist/esm/modules/pdf/index.js +3 -1
- package/dist/esm/modules/pdf/render/chart-surface.js +200 -0
- package/dist/esm/modules/pdf/render/layout-engine.js +436 -56
- package/dist/esm/modules/pdf/render/page-renderer.js +169 -28
- package/dist/esm/modules/pdf/render/pdf-exporter.js +117 -7
- package/dist/esm/modules/pdf/types.js +4 -0
- package/dist/esm/modules/pdf/word-bridge.js +304 -0
- package/dist/esm/modules/word/constants.js +231 -0
- package/dist/esm/modules/word/content-types.js +53 -0
- package/dist/esm/modules/word/digital-signatures.js +134 -0
- package/dist/esm/modules/word/document.js +1795 -0
- package/dist/esm/modules/word/docx-packager.js +822 -0
- package/dist/esm/modules/word/docx-reader.js +4929 -0
- package/dist/esm/modules/word/encryption.js +274 -0
- package/dist/esm/modules/word/errors.js +68 -0
- package/dist/esm/modules/word/font-obfuscation.js +83 -0
- package/dist/esm/modules/word/html-renderer.js +782 -0
- package/dist/esm/modules/word/index.base.js +51 -0
- package/dist/esm/modules/word/index.browser.js +4 -0
- package/dist/esm/modules/word/index.js +4 -0
- package/dist/esm/modules/word/internal-utils.js +54 -0
- package/dist/esm/modules/word/relationships.js +56 -0
- package/dist/esm/modules/word/types.js +10 -0
- package/dist/esm/modules/word/units.js +111 -0
- package/dist/esm/modules/word/writers/chart-writer.js +385 -0
- package/dist/esm/modules/word/writers/checkbox-writer.js +42 -0
- package/dist/esm/modules/word/writers/comment-writer.js +70 -0
- package/dist/esm/modules/word/writers/document-writer.js +461 -0
- package/dist/esm/modules/word/writers/footnote-writer.js +72 -0
- package/dist/esm/modules/word/writers/header-footer-writer.js +129 -0
- package/dist/esm/modules/word/writers/image-writer.js +185 -0
- package/dist/esm/modules/word/writers/math-writer.js +428 -0
- package/dist/esm/modules/word/writers/numbering-writer.js +125 -0
- package/dist/esm/modules/word/writers/paragraph-writer.js +516 -0
- package/dist/esm/modules/word/writers/parts-writer.js +660 -0
- package/dist/esm/modules/word/writers/run-writer.js +649 -0
- package/dist/esm/modules/word/writers/section-writer.js +238 -0
- package/dist/esm/modules/word/writers/styles-writer.js +242 -0
- package/dist/esm/modules/word/writers/table-writer.js +503 -0
- package/dist/esm/modules/word/writers/textbox-writer.js +53 -0
- package/dist/esm/modules/word/writers/toc-writer.js +79 -0
- package/dist/esm/modules/xml/encode.js +157 -11
- package/dist/iife/excelts.iife.js +11789 -687
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +52 -44
- package/dist/types/index.browser.d.ts +8 -5
- package/dist/types/index.d.ts +4 -2
- package/dist/types/modules/excel/chart/cache-populator.d.ts +49 -0
- package/dist/types/modules/excel/chart/chart-api.d.ts +92 -0
- package/dist/types/modules/excel/chart/chart-builder.d.ts +48 -0
- package/dist/types/modules/excel/chart/chart-ex-builder.d.ts +36 -0
- package/dist/types/modules/excel/chart/chart-ex-parser.d.ts +8 -0
- package/dist/types/modules/excel/chart/chart-ex-renderer.d.ts +187 -0
- package/dist/types/modules/excel/chart/chart-ex-types.d.ts +531 -0
- package/dist/types/modules/excel/chart/chart-images.d.ts +78 -0
- package/dist/types/modules/excel/chart/chart-presets.d.ts +392 -0
- package/dist/types/modules/excel/chart/chart-renderer.d.ts +550 -0
- package/dist/types/modules/excel/chart/chart-sidecar.d.ts +21 -0
- package/dist/types/modules/excel/chart/chart-utils.d.ts +306 -0
- package/dist/types/modules/excel/chart/chart.d.ts +504 -0
- package/dist/types/modules/excel/chart/glyph-rasterizer.d.ts +62 -0
- package/dist/types/modules/excel/chart/index.d.ts +54 -0
- package/dist/types/modules/excel/chart/install.d.ts +44 -0
- package/dist/types/modules/excel/chart/shape-properties.d.ts +156 -0
- package/dist/types/modules/excel/chart/stroke-font.d.ts +36 -0
- package/dist/types/modules/excel/chart/topojson.d.ts +98 -0
- package/dist/types/modules/excel/chart/types.d.ts +2559 -0
- package/dist/types/modules/excel/chart-host-registry.d.ts +157 -0
- package/dist/types/modules/excel/chartsheet.d.ts +102 -0
- package/dist/types/modules/excel/defined-names.d.ts +35 -0
- package/dist/types/modules/excel/errors.d.ts +6 -0
- package/dist/types/modules/excel/form-control.d.ts +6 -0
- package/dist/types/modules/excel/pivot-chart.d.ts +7 -0
- package/dist/types/modules/excel/pivot-table.d.ts +55 -0
- package/dist/types/modules/excel/sparkline/index.d.ts +7 -0
- package/dist/types/modules/excel/sparkline/sparkline.d.ts +206 -0
- package/dist/types/modules/excel/types.d.ts +72 -0
- package/dist/types/modules/excel/utils/address.d.ts +18 -0
- package/dist/types/modules/excel/utils/guid.d.ts +15 -0
- package/dist/types/modules/excel/utils/ooxml-paths.d.ts +74 -0
- package/dist/types/modules/excel/utils/ooxml-validator/check-chart-sidecar.d.ts +35 -0
- package/dist/types/modules/excel/utils/ooxml-validator/check-chart.d.ts +32 -0
- package/dist/types/modules/excel/utils/ooxml-validator/check-chartsheet.d.ts +9 -0
- package/dist/types/modules/excel/utils/ooxml-validator/check-content-types.d.ts +16 -0
- package/dist/types/modules/excel/utils/ooxml-validator/check-drawing.d.ts +34 -0
- package/dist/types/modules/excel/utils/ooxml-validator/check-pivot.d.ts +14 -0
- package/dist/types/modules/excel/utils/ooxml-validator/check-relationships.d.ts +18 -0
- package/dist/types/modules/excel/utils/ooxml-validator/check-structure.d.ts +21 -0
- package/dist/types/modules/excel/utils/ooxml-validator/check-styles.d.ts +15 -0
- package/dist/types/modules/excel/utils/ooxml-validator/check-table.d.ts +31 -0
- package/dist/types/modules/excel/utils/ooxml-validator/check-workbook.d.ts +19 -0
- package/dist/types/modules/excel/utils/ooxml-validator/check-worksheet.d.ts +25 -0
- package/dist/types/modules/excel/utils/ooxml-validator/context.d.ts +85 -0
- package/dist/types/modules/excel/utils/ooxml-validator/index.d.ts +31 -0
- package/dist/types/modules/excel/utils/ooxml-validator/path-utils.d.ts +67 -0
- package/dist/types/modules/excel/utils/ooxml-validator/reporter.d.ts +41 -0
- package/dist/types/modules/excel/utils/ooxml-validator/types.d.ts +109 -0
- package/dist/types/modules/excel/utils/ooxml-validator/xml-utils.d.ts +38 -0
- package/dist/types/modules/excel/workbook.browser.d.ts +248 -30
- package/dist/types/modules/excel/workbook.d.ts +43 -0
- package/dist/types/modules/excel/worksheet.d.ts +157 -3
- package/dist/types/modules/excel/xlsx/rel-type.d.ts +40 -0
- package/dist/types/modules/excel/xlsx/xform/book/defined-name-xform.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/chart/chart-space-xform.d.ts +353 -0
- package/dist/types/modules/excel/xlsx/xform/comment/threaded-comments-xform.d.ts +60 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/drawing-xform.d.ts +30 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/graphic-frame-xform.d.ts +54 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +3 -1
- package/dist/types/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.d.ts +46 -0
- package/dist/types/modules/excel/xlsx/xform/pivot-table/pivot-table-xform.d.ts +13 -2
- package/dist/types/modules/excel/xlsx/xform/sheet/chartsheet-xform.d.ts +185 -0
- package/dist/types/modules/excel/xlsx/xform/sheet/ext-lst-xform.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/xsd-values.d.ts +63 -0
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +115 -21
- package/dist/types/modules/pdf/builder/document-builder.d.ts +74 -0
- package/dist/types/modules/pdf/excel-bridge.d.ts +69 -0
- package/dist/types/modules/pdf/font/font-manager.d.ts +25 -0
- package/dist/types/modules/pdf/index.d.ts +5 -2
- package/dist/types/modules/pdf/render/chart-surface.d.ts +33 -0
- package/dist/types/modules/pdf/render/layout-engine.d.ts +22 -1
- package/dist/types/modules/pdf/types.d.ts +227 -23
- package/dist/types/modules/pdf/word-bridge.d.ts +47 -0
- package/dist/types/modules/word/constants.d.ts +179 -0
- package/dist/types/modules/word/content-types.d.ts +27 -0
- package/dist/types/modules/word/digital-signatures.d.ts +87 -0
- package/dist/types/modules/word/document.d.ts +728 -0
- package/dist/types/modules/word/docx-packager.d.ts +14 -0
- package/dist/types/modules/word/docx-reader.d.ts +11 -0
- package/dist/types/modules/word/encryption.d.ts +102 -0
- package/dist/types/modules/word/errors.d.ts +49 -0
- package/dist/types/modules/word/font-obfuscation.d.ts +31 -0
- package/dist/types/modules/word/html-renderer.d.ts +38 -0
- package/dist/types/modules/word/index.base.d.ts +19 -0
- package/dist/types/modules/word/index.browser.d.ts +4 -0
- package/dist/types/modules/word/index.d.ts +4 -0
- package/dist/types/modules/word/internal-utils.d.ts +23 -0
- package/dist/types/modules/word/relationships.d.ts +31 -0
- package/dist/types/modules/word/types.d.ts +2325 -0
- package/dist/types/modules/word/units.d.ts +49 -0
- package/dist/types/modules/word/writers/chart-writer.d.ts +10 -0
- package/dist/types/modules/word/writers/checkbox-writer.d.ts +9 -0
- package/dist/types/modules/word/writers/comment-writer.d.ts +15 -0
- package/dist/types/modules/word/writers/document-writer.d.ts +16 -0
- package/dist/types/modules/word/writers/footnote-writer.d.ts +11 -0
- package/dist/types/modules/word/writers/header-footer-writer.d.ts +13 -0
- package/dist/types/modules/word/writers/image-writer.d.ts +10 -0
- package/dist/types/modules/word/writers/math-writer.d.ts +9 -0
- package/dist/types/modules/word/writers/numbering-writer.d.ts +10 -0
- package/dist/types/modules/word/writers/paragraph-writer.d.ts +13 -0
- package/dist/types/modules/word/writers/parts-writer.d.ts +26 -0
- package/dist/types/modules/word/writers/run-writer.d.ts +15 -0
- package/dist/types/modules/word/writers/section-writer.d.ts +10 -0
- package/dist/types/modules/word/writers/styles-writer.d.ts +10 -0
- package/dist/types/modules/word/writers/table-writer.d.ts +10 -0
- package/dist/types/modules/word/writers/textbox-writer.d.ts +9 -0
- package/dist/types/modules/word/writers/toc-writer.d.ts +9 -0
- package/dist/types/modules/xml/encode.d.ts +56 -7
- package/package.json +29 -11
- package/dist/browser/modules/excel/utils/ooxml-validator.d.ts +0 -48
- package/dist/browser/modules/excel/utils/ooxml-validator.js +0 -493
- package/dist/browser/modules/excel/utils/passthrough-manager.d.ts +0 -77
- package/dist/browser/modules/excel/utils/passthrough-manager.js +0 -129
- package/dist/cjs/modules/excel/utils/ooxml-validator.js +0 -499
- package/dist/cjs/modules/excel/utils/passthrough-manager.js +0 -133
- package/dist/esm/modules/excel/utils/ooxml-validator.js +0 -493
- package/dist/esm/modules/excel/utils/passthrough-manager.js +0 -129
- package/dist/types/modules/excel/utils/ooxml-validator.d.ts +0 -48
- package/dist/types/modules/excel/utils/passthrough-manager.d.ts +0 -77
|
@@ -0,0 +1,2125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chart / chartEx internal structure check.
|
|
3
|
+
*
|
|
4
|
+
* Classic charts (`xl/charts/chartN.xml`) must contain both `c:chart` and
|
|
5
|
+
* `c:plotArea`. ChartEx charts must contain `cx:chart`, `cx:plotArea`
|
|
6
|
+
* and at least one `cx:series`; series must carry `layoutId` and their
|
|
7
|
+
* `dataId`/`axisId` back-references must resolve inside the chart.
|
|
8
|
+
* `cx:externalData` nodes must refer to a declared relationship.
|
|
9
|
+
*
|
|
10
|
+
* In addition we catch five chartEx schema-violation patterns that
|
|
11
|
+
* cause Excel 2016+ to drop the whole chartEx part with "Removed Part:
|
|
12
|
+
* /xl/drawings/drawingN.xml (Drawing shape)":
|
|
13
|
+
*
|
|
14
|
+
* - `<cx:series>` with more than one `<cx:dataId>` child
|
|
15
|
+
* (`CT_Series/dataId` has `maxOccurs="1"`; multi-dim series must
|
|
16
|
+
* use a single `<cx:data>` wrapper with multiple strDim/numDim).
|
|
17
|
+
* - `<cx:axisId>N</cx:axisId>`, `<cx:dataId>N</cx:dataId>`,
|
|
18
|
+
* `<cx:binCount>N</cx:binCount>`, `<cx:binSize>N</cx:binSize>`
|
|
19
|
+
* emitted as text content instead of `val="N"` attribute. The
|
|
20
|
+
* underlying types are `CT_UnsignedInteger`/`CT_Double`, which
|
|
21
|
+
* Excel's strict loader only accepts via the attribute form.
|
|
22
|
+
* - `<cx:auto/>` element anywhere in the chartEx. Auto binning is
|
|
23
|
+
* expressed by the ABSENCE of `binSize`/`binCount`, not a
|
|
24
|
+
* dedicated `<cx:auto/>` tag. The tag is schema-invalid.
|
|
25
|
+
* - `<cx:paretoLine>` child of `<cx:layoutPr>`. Not in the
|
|
26
|
+
* CT_SeriesLayoutProperties schema. A real pareto chart adds a
|
|
27
|
+
* second series with `layoutId="paretoLine"`.
|
|
28
|
+
* - `<cx:title>` with a direct `<cx:layout>` child. Title layout
|
|
29
|
+
* belongs in `extLst`-based extensions.
|
|
30
|
+
*/
|
|
31
|
+
import { attrByLocalName, collectDescendantsLocal, findChildLocal, findChildrenLocal, hasDescendantLocal, localName, matchesLocal } from "./xml-utils.js";
|
|
32
|
+
const CHART_PATH_RE = /^xl\/charts\/chart\d+\.xml$/;
|
|
33
|
+
const CHARTEX_PATH_RE = /^xl\/charts\/chartEx\d+\.xml$/;
|
|
34
|
+
export function checkChart(ctx) {
|
|
35
|
+
for (const [path, entry] of ctx.files()) {
|
|
36
|
+
if (ctx.reporter.capped) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (entry.type === "directory") {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (CHART_PATH_RE.test(path)) {
|
|
43
|
+
checkClassicChart(ctx, path);
|
|
44
|
+
}
|
|
45
|
+
else if (CHARTEX_PATH_RE.test(path)) {
|
|
46
|
+
checkChartEx(ctx, path);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function checkClassicChart(ctx, path) {
|
|
51
|
+
const dom = ctx.readDom(path);
|
|
52
|
+
if (!dom) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const root = dom.root;
|
|
56
|
+
if (!hasDescendantLocal(root, "chart")) {
|
|
57
|
+
ctx.reporter.error("chart-missing-chart", `${path}: missing c:chart`, path);
|
|
58
|
+
}
|
|
59
|
+
if (!hasDescendantLocal(root, "plotArea")) {
|
|
60
|
+
ctx.reporter.error("chart-missing-plotArea", `${path}: missing c:plotArea`, path);
|
|
61
|
+
}
|
|
62
|
+
// Schema-conformance pass: verifies child-element order, required
|
|
63
|
+
// child counts, enumerated attribute values and numeric ranges
|
|
64
|
+
// against ECMA-376 Part 1 §21.2.x / Microsoft OpenXML SDK
|
|
65
|
+
// ChildElementInfo. See the tables below.
|
|
66
|
+
checkClassicChartSchema(ctx, path, root);
|
|
67
|
+
}
|
|
68
|
+
const CT_CHART_TYPE_TAGS = [
|
|
69
|
+
"areaChart",
|
|
70
|
+
"area3DChart",
|
|
71
|
+
"lineChart",
|
|
72
|
+
"line3DChart",
|
|
73
|
+
"stockChart",
|
|
74
|
+
"radarChart",
|
|
75
|
+
"scatterChart",
|
|
76
|
+
"pieChart",
|
|
77
|
+
"pie3DChart",
|
|
78
|
+
"doughnutChart",
|
|
79
|
+
"barChart",
|
|
80
|
+
"bar3DChart",
|
|
81
|
+
"ofPieChart",
|
|
82
|
+
"surfaceChart",
|
|
83
|
+
"surface3DChart",
|
|
84
|
+
"bubbleChart"
|
|
85
|
+
];
|
|
86
|
+
const CT_AXIS_TAGS = ["catAx", "valAx", "dateAx", "serAx"];
|
|
87
|
+
const CLASSIC_ORDER_RULES = [
|
|
88
|
+
// CT_ChartSpace (§21.2.2.29)
|
|
89
|
+
{
|
|
90
|
+
parent: "chartSpace",
|
|
91
|
+
order: [
|
|
92
|
+
"lang",
|
|
93
|
+
"roundedCorners",
|
|
94
|
+
"style",
|
|
95
|
+
"clrMapOvr",
|
|
96
|
+
"pivotSource",
|
|
97
|
+
"protection",
|
|
98
|
+
"chart",
|
|
99
|
+
"spPr",
|
|
100
|
+
"txPr",
|
|
101
|
+
"externalData",
|
|
102
|
+
"printSettings",
|
|
103
|
+
"userShapes",
|
|
104
|
+
"extLst"
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
// CT_Chart (§21.2.2.27)
|
|
108
|
+
{
|
|
109
|
+
parent: "chart",
|
|
110
|
+
order: [
|
|
111
|
+
"title",
|
|
112
|
+
"autoTitleDeleted",
|
|
113
|
+
"pivotFmts",
|
|
114
|
+
"view3D",
|
|
115
|
+
"floor",
|
|
116
|
+
"sideWall",
|
|
117
|
+
"backWall",
|
|
118
|
+
"plotArea",
|
|
119
|
+
"legend",
|
|
120
|
+
"plotVisOnly",
|
|
121
|
+
"dispBlanksAs",
|
|
122
|
+
"showDLblsOverMax",
|
|
123
|
+
"extLst"
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
// CT_PlotArea (§21.2.2.145). Two `<xsd:choice maxOccurs="unbounded">`
|
|
127
|
+
// groups — chart-type elements and axis elements — collapse into
|
|
128
|
+
// bucket placeholders.
|
|
129
|
+
{
|
|
130
|
+
parent: "plotArea",
|
|
131
|
+
order: ["layout", "__CHART_TYPES__", "__AXES__", "dTable", "spPr", "extLst"],
|
|
132
|
+
choiceGroups: [CT_CHART_TYPE_TAGS, CT_AXIS_TAGS]
|
|
133
|
+
},
|
|
134
|
+
// CT_DLbls (§21.2.2.49) — the biggest historical offender.
|
|
135
|
+
// Separator MUST come before showLeaderLines / leaderLines.
|
|
136
|
+
{
|
|
137
|
+
parent: "dLbls",
|
|
138
|
+
order: [
|
|
139
|
+
"dLbl",
|
|
140
|
+
"delete",
|
|
141
|
+
"numFmt",
|
|
142
|
+
"spPr",
|
|
143
|
+
"txPr",
|
|
144
|
+
"dLblPos",
|
|
145
|
+
"showLegendKey",
|
|
146
|
+
"showVal",
|
|
147
|
+
"showCatName",
|
|
148
|
+
"showSerName",
|
|
149
|
+
"showPercent",
|
|
150
|
+
"showBubbleSize",
|
|
151
|
+
"separator",
|
|
152
|
+
"showLeaderLines",
|
|
153
|
+
"leaderLines",
|
|
154
|
+
"extLst"
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
// CT_DLbl (§21.2.2.47). The `delete` branch is mutually exclusive
|
|
158
|
+
// with the display-flag branch but we allow the tag here for
|
|
159
|
+
// ordering purposes; the choice violation is caught separately by
|
|
160
|
+
// `checkClassicDLblChoice` below.
|
|
161
|
+
{
|
|
162
|
+
parent: "dLbl",
|
|
163
|
+
order: [
|
|
164
|
+
"idx",
|
|
165
|
+
"delete",
|
|
166
|
+
"layout",
|
|
167
|
+
"tx",
|
|
168
|
+
"numFmt",
|
|
169
|
+
"spPr",
|
|
170
|
+
"txPr",
|
|
171
|
+
"dLblPos",
|
|
172
|
+
"showLegendKey",
|
|
173
|
+
"showVal",
|
|
174
|
+
"showCatName",
|
|
175
|
+
"showSerName",
|
|
176
|
+
"showPercent",
|
|
177
|
+
"showBubbleSize",
|
|
178
|
+
"separator",
|
|
179
|
+
"extLst"
|
|
180
|
+
]
|
|
181
|
+
},
|
|
182
|
+
// CT_Scaling (§21.2.2.195). `max` precedes `min`.
|
|
183
|
+
{ parent: "scaling", order: ["logBase", "orientation", "max", "min", "extLst"] },
|
|
184
|
+
// CT_CatAx (§21.2.2.25)
|
|
185
|
+
{
|
|
186
|
+
parent: "catAx",
|
|
187
|
+
order: [
|
|
188
|
+
"axId",
|
|
189
|
+
"scaling",
|
|
190
|
+
"delete",
|
|
191
|
+
"axPos",
|
|
192
|
+
"majorGridlines",
|
|
193
|
+
"minorGridlines",
|
|
194
|
+
"title",
|
|
195
|
+
"numFmt",
|
|
196
|
+
"majorTickMark",
|
|
197
|
+
"minorTickMark",
|
|
198
|
+
"tickLblPos",
|
|
199
|
+
"spPr",
|
|
200
|
+
"txPr",
|
|
201
|
+
"crossAx",
|
|
202
|
+
"crosses",
|
|
203
|
+
"crossesAt",
|
|
204
|
+
"auto",
|
|
205
|
+
"lblAlgn",
|
|
206
|
+
"lblOffset",
|
|
207
|
+
"tickLblSkip",
|
|
208
|
+
"tickMarkSkip",
|
|
209
|
+
"noMultiLvlLbl",
|
|
210
|
+
"extLst"
|
|
211
|
+
]
|
|
212
|
+
},
|
|
213
|
+
// CT_ValAx (§21.2.2.226)
|
|
214
|
+
{
|
|
215
|
+
parent: "valAx",
|
|
216
|
+
order: [
|
|
217
|
+
"axId",
|
|
218
|
+
"scaling",
|
|
219
|
+
"delete",
|
|
220
|
+
"axPos",
|
|
221
|
+
"majorGridlines",
|
|
222
|
+
"minorGridlines",
|
|
223
|
+
"title",
|
|
224
|
+
"numFmt",
|
|
225
|
+
"majorTickMark",
|
|
226
|
+
"minorTickMark",
|
|
227
|
+
"tickLblPos",
|
|
228
|
+
"spPr",
|
|
229
|
+
"txPr",
|
|
230
|
+
"crossAx",
|
|
231
|
+
"crosses",
|
|
232
|
+
"crossesAt",
|
|
233
|
+
"crossBetween",
|
|
234
|
+
"majorUnit",
|
|
235
|
+
"minorUnit",
|
|
236
|
+
"dispUnits",
|
|
237
|
+
"extLst"
|
|
238
|
+
]
|
|
239
|
+
},
|
|
240
|
+
// CT_DateAx (§21.2.2.39)
|
|
241
|
+
{
|
|
242
|
+
parent: "dateAx",
|
|
243
|
+
order: [
|
|
244
|
+
"axId",
|
|
245
|
+
"scaling",
|
|
246
|
+
"delete",
|
|
247
|
+
"axPos",
|
|
248
|
+
"majorGridlines",
|
|
249
|
+
"minorGridlines",
|
|
250
|
+
"title",
|
|
251
|
+
"numFmt",
|
|
252
|
+
"majorTickMark",
|
|
253
|
+
"minorTickMark",
|
|
254
|
+
"tickLblPos",
|
|
255
|
+
"spPr",
|
|
256
|
+
"txPr",
|
|
257
|
+
"crossAx",
|
|
258
|
+
"crosses",
|
|
259
|
+
"crossesAt",
|
|
260
|
+
"auto",
|
|
261
|
+
"lblOffset",
|
|
262
|
+
"baseTimeUnit",
|
|
263
|
+
"majorUnit",
|
|
264
|
+
"majorTimeUnit",
|
|
265
|
+
"minorUnit",
|
|
266
|
+
"minorTimeUnit",
|
|
267
|
+
"extLst"
|
|
268
|
+
]
|
|
269
|
+
},
|
|
270
|
+
// CT_SerAx (§21.2.2.175)
|
|
271
|
+
{
|
|
272
|
+
parent: "serAx",
|
|
273
|
+
order: [
|
|
274
|
+
"axId",
|
|
275
|
+
"scaling",
|
|
276
|
+
"delete",
|
|
277
|
+
"axPos",
|
|
278
|
+
"majorGridlines",
|
|
279
|
+
"minorGridlines",
|
|
280
|
+
"title",
|
|
281
|
+
"numFmt",
|
|
282
|
+
"majorTickMark",
|
|
283
|
+
"minorTickMark",
|
|
284
|
+
"tickLblPos",
|
|
285
|
+
"spPr",
|
|
286
|
+
"txPr",
|
|
287
|
+
"crossAx",
|
|
288
|
+
"crosses",
|
|
289
|
+
"crossesAt",
|
|
290
|
+
"tickLblSkip",
|
|
291
|
+
"tickMarkSkip",
|
|
292
|
+
"extLst"
|
|
293
|
+
]
|
|
294
|
+
},
|
|
295
|
+
// CT_Trendline (§21.2.2.211)
|
|
296
|
+
{
|
|
297
|
+
parent: "trendline",
|
|
298
|
+
order: [
|
|
299
|
+
"name",
|
|
300
|
+
"spPr",
|
|
301
|
+
"trendlineType",
|
|
302
|
+
"order",
|
|
303
|
+
"period",
|
|
304
|
+
"forward",
|
|
305
|
+
"backward",
|
|
306
|
+
"intercept",
|
|
307
|
+
"dispRSqr",
|
|
308
|
+
"dispEq",
|
|
309
|
+
"trendlineLbl",
|
|
310
|
+
"extLst"
|
|
311
|
+
]
|
|
312
|
+
},
|
|
313
|
+
// CT_ErrBars (§21.2.2.55)
|
|
314
|
+
{
|
|
315
|
+
parent: "errBars",
|
|
316
|
+
order: [
|
|
317
|
+
"errDir",
|
|
318
|
+
"errBarType",
|
|
319
|
+
"errValType",
|
|
320
|
+
"noEndCap",
|
|
321
|
+
"plus",
|
|
322
|
+
"minus",
|
|
323
|
+
"val",
|
|
324
|
+
"spPr",
|
|
325
|
+
"extLst"
|
|
326
|
+
]
|
|
327
|
+
},
|
|
328
|
+
// CT_View3D (§21.2.2.228)
|
|
329
|
+
{
|
|
330
|
+
parent: "view3D",
|
|
331
|
+
order: ["rotX", "hPercent", "rotY", "depthPercent", "rAngAx", "perspective", "extLst"]
|
|
332
|
+
},
|
|
333
|
+
// CT_Legend (§21.2.2.93)
|
|
334
|
+
{
|
|
335
|
+
parent: "legend",
|
|
336
|
+
order: ["legendPos", "legendEntry", "layout", "overlay", "spPr", "txPr", "extLst"]
|
|
337
|
+
},
|
|
338
|
+
// CT_Title (§21.2.2.210)
|
|
339
|
+
{ parent: "title", order: ["tx", "layout", "overlay", "spPr", "txPr", "extLst"] },
|
|
340
|
+
// ---------------------------------------------------------------------------
|
|
341
|
+
// Chart-type elements (§21.2.2.4 — §21.2.2.198). Each plotArea
|
|
342
|
+
// child must list its children in the canonical sequence. Excel
|
|
343
|
+
// tolerates many mis-orderings but LibreOffice strict mode
|
|
344
|
+
// rejects them; third-party OOXML validators (Microsoft's SDK,
|
|
345
|
+
// Calligra, xmllint + XSD) refuse too.
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
// CT_AreaChart (§21.2.2.5)
|
|
348
|
+
{
|
|
349
|
+
parent: "areaChart",
|
|
350
|
+
order: ["grouping", "varyColors", "ser", "dLbls", "dropLines", "axId", "extLst"]
|
|
351
|
+
},
|
|
352
|
+
// CT_Area3DChart (§21.2.2.4)
|
|
353
|
+
{
|
|
354
|
+
parent: "area3DChart",
|
|
355
|
+
order: ["grouping", "varyColors", "ser", "dLbls", "dropLines", "gapDepth", "axId", "extLst"]
|
|
356
|
+
},
|
|
357
|
+
// CT_BarChart (§21.2.2.16)
|
|
358
|
+
{
|
|
359
|
+
parent: "barChart",
|
|
360
|
+
order: [
|
|
361
|
+
"barDir",
|
|
362
|
+
"grouping",
|
|
363
|
+
"varyColors",
|
|
364
|
+
"ser",
|
|
365
|
+
"dLbls",
|
|
366
|
+
"gapWidth",
|
|
367
|
+
"overlap",
|
|
368
|
+
"serLines",
|
|
369
|
+
"axId",
|
|
370
|
+
"extLst"
|
|
371
|
+
]
|
|
372
|
+
},
|
|
373
|
+
// CT_Bar3DChart (§21.2.2.15)
|
|
374
|
+
{
|
|
375
|
+
parent: "bar3DChart",
|
|
376
|
+
order: [
|
|
377
|
+
"barDir",
|
|
378
|
+
"grouping",
|
|
379
|
+
"varyColors",
|
|
380
|
+
"ser",
|
|
381
|
+
"dLbls",
|
|
382
|
+
"gapWidth",
|
|
383
|
+
"gapDepth",
|
|
384
|
+
"shape",
|
|
385
|
+
"axId",
|
|
386
|
+
"extLst"
|
|
387
|
+
]
|
|
388
|
+
},
|
|
389
|
+
// CT_LineChart (§21.2.2.97)
|
|
390
|
+
{
|
|
391
|
+
parent: "lineChart",
|
|
392
|
+
order: [
|
|
393
|
+
"grouping",
|
|
394
|
+
"varyColors",
|
|
395
|
+
"ser",
|
|
396
|
+
"dLbls",
|
|
397
|
+
"dropLines",
|
|
398
|
+
"hiLowLines",
|
|
399
|
+
"upDownBars",
|
|
400
|
+
"marker",
|
|
401
|
+
"smooth",
|
|
402
|
+
"axId",
|
|
403
|
+
"extLst"
|
|
404
|
+
]
|
|
405
|
+
},
|
|
406
|
+
// CT_Line3DChart (§21.2.2.96)
|
|
407
|
+
{
|
|
408
|
+
parent: "line3DChart",
|
|
409
|
+
order: ["grouping", "varyColors", "ser", "dLbls", "dropLines", "gapDepth", "axId", "extLst"]
|
|
410
|
+
},
|
|
411
|
+
// CT_PieChart (§21.2.2.141)
|
|
412
|
+
{ parent: "pieChart", order: ["varyColors", "ser", "dLbls", "firstSliceAng", "extLst"] },
|
|
413
|
+
// CT_Pie3DChart (§21.2.2.140) — no firstSliceAng, no holeSize.
|
|
414
|
+
{ parent: "pie3DChart", order: ["varyColors", "ser", "dLbls", "extLst"] },
|
|
415
|
+
// CT_DoughnutChart (§21.2.2.50)
|
|
416
|
+
{
|
|
417
|
+
parent: "doughnutChart",
|
|
418
|
+
order: ["varyColors", "ser", "dLbls", "firstSliceAng", "holeSize", "extLst"]
|
|
419
|
+
},
|
|
420
|
+
// CT_OfPieChart (§21.2.2.126)
|
|
421
|
+
{
|
|
422
|
+
parent: "ofPieChart",
|
|
423
|
+
order: [
|
|
424
|
+
"ofPieType",
|
|
425
|
+
"varyColors",
|
|
426
|
+
"ser",
|
|
427
|
+
"dLbls",
|
|
428
|
+
"gapWidth",
|
|
429
|
+
"splitType",
|
|
430
|
+
"splitPos",
|
|
431
|
+
"custSplit",
|
|
432
|
+
"secondPieSize",
|
|
433
|
+
"serLines",
|
|
434
|
+
"extLst"
|
|
435
|
+
]
|
|
436
|
+
},
|
|
437
|
+
// CT_ScatterChart (§21.2.2.161)
|
|
438
|
+
{
|
|
439
|
+
parent: "scatterChart",
|
|
440
|
+
order: ["scatterStyle", "varyColors", "ser", "dLbls", "axId", "extLst"]
|
|
441
|
+
},
|
|
442
|
+
// CT_BubbleChart (§21.2.2.20)
|
|
443
|
+
{
|
|
444
|
+
parent: "bubbleChart",
|
|
445
|
+
order: [
|
|
446
|
+
"varyColors",
|
|
447
|
+
"ser",
|
|
448
|
+
"dLbls",
|
|
449
|
+
"bubble3D",
|
|
450
|
+
"bubbleScale",
|
|
451
|
+
"showNegBubbles",
|
|
452
|
+
"sizeRepresents",
|
|
453
|
+
"axId",
|
|
454
|
+
"extLst"
|
|
455
|
+
]
|
|
456
|
+
},
|
|
457
|
+
// CT_RadarChart (§21.2.2.153)
|
|
458
|
+
{
|
|
459
|
+
parent: "radarChart",
|
|
460
|
+
order: ["radarStyle", "varyColors", "ser", "dLbls", "axId", "extLst"]
|
|
461
|
+
},
|
|
462
|
+
// CT_StockChart (§21.2.2.198)
|
|
463
|
+
{
|
|
464
|
+
parent: "stockChart",
|
|
465
|
+
order: ["ser", "dLbls", "dropLines", "hiLowLines", "upDownBars", "axId", "extLst"]
|
|
466
|
+
},
|
|
467
|
+
// CT_SurfaceChart (§21.2.2.193) / CT_Surface3DChart
|
|
468
|
+
{ parent: "surfaceChart", order: ["wireframe", "ser", "bandFmts", "axId", "extLst"] },
|
|
469
|
+
{ parent: "surface3DChart", order: ["wireframe", "ser", "bandFmts", "axId", "extLst"] },
|
|
470
|
+
// ---------------------------------------------------------------------------
|
|
471
|
+
// Auxiliary complex types (§21.2.2.x). Sequences that appear
|
|
472
|
+
// throughout the chart graph — getting them out of order is a
|
|
473
|
+
// common bug source (and historically Excel tolerates it while
|
|
474
|
+
// LibreOffice strict refuses the file).
|
|
475
|
+
// ---------------------------------------------------------------------------
|
|
476
|
+
// CT_DPt (§21.2.2.52)
|
|
477
|
+
{
|
|
478
|
+
parent: "dPt",
|
|
479
|
+
order: [
|
|
480
|
+
"idx",
|
|
481
|
+
"invertIfNegative",
|
|
482
|
+
"marker",
|
|
483
|
+
"bubble3D",
|
|
484
|
+
"explosion",
|
|
485
|
+
"spPr",
|
|
486
|
+
"pictureOptions",
|
|
487
|
+
"extLst"
|
|
488
|
+
]
|
|
489
|
+
},
|
|
490
|
+
// CT_Marker (§21.2.2.106)
|
|
491
|
+
{ parent: "marker", order: ["symbol", "size", "spPr", "extLst"] },
|
|
492
|
+
// CT_UpDownBars (§21.2.2.218)
|
|
493
|
+
{ parent: "upDownBars", order: ["gapWidth", "upBars", "downBars", "extLst"] },
|
|
494
|
+
// CT_NumRef / CT_StrRef / CT_MultiLvlStrRef (§21.2.2.121 / .189 / .113)
|
|
495
|
+
{ parent: "numRef", order: ["f", "numCache", "extLst"] },
|
|
496
|
+
{ parent: "strRef", order: ["f", "strCache", "extLst"] },
|
|
497
|
+
{ parent: "multiLvlStrRef", order: ["f", "multiLvlStrCache", "extLst"] },
|
|
498
|
+
// CT_NumData / CT_StrData (§21.2.2.122 / .188)
|
|
499
|
+
{ parent: "numCache", order: ["formatCode", "ptCount", "pt", "extLst"] },
|
|
500
|
+
{ parent: "strCache", order: ["ptCount", "pt", "extLst"] },
|
|
501
|
+
{ parent: "multiLvlStrCache", order: ["ptCount", "lvl", "extLst"] },
|
|
502
|
+
// CT_NumLit / CT_StrLit
|
|
503
|
+
{ parent: "numLit", order: ["formatCode", "ptCount", "pt", "extLst"] },
|
|
504
|
+
{ parent: "strLit", order: ["ptCount", "pt", "extLst"] },
|
|
505
|
+
// CT_Pt (§21.2.2.146): v element; idx is an attribute.
|
|
506
|
+
{ parent: "pt", order: ["v"] },
|
|
507
|
+
// CT_Layout (§21.2.2.88) / CT_ManualLayout (§21.2.2.105)
|
|
508
|
+
{ parent: "layout", order: ["manualLayout", "extLst"] },
|
|
509
|
+
{
|
|
510
|
+
parent: "manualLayout",
|
|
511
|
+
order: ["layoutTarget", "xMode", "yMode", "wMode", "hMode", "x", "y", "w", "h", "extLst"]
|
|
512
|
+
},
|
|
513
|
+
// CT_DTable (§21.2.2.54)
|
|
514
|
+
{
|
|
515
|
+
parent: "dTable",
|
|
516
|
+
order: ["showHorzBorder", "showVertBorder", "showOutline", "showKeys", "spPr", "txPr", "extLst"]
|
|
517
|
+
},
|
|
518
|
+
// CT_BandFormats (§21.2.2.12) / CT_BandFormat (§21.2.2.11)
|
|
519
|
+
{ parent: "bandFmts", order: ["bandFmt"] },
|
|
520
|
+
{ parent: "bandFmt", order: ["idx", "spPr"] },
|
|
521
|
+
// CT_TrendlineLbl (§21.2.2.212) — layout? tx? numFmt? spPr? txPr? extLst?
|
|
522
|
+
{
|
|
523
|
+
parent: "trendlineLbl",
|
|
524
|
+
order: ["layout", "tx", "numFmt", "spPr", "txPr", "extLst"]
|
|
525
|
+
},
|
|
526
|
+
// CT_PictureOptions (§21.2.2.144)
|
|
527
|
+
{
|
|
528
|
+
parent: "pictureOptions",
|
|
529
|
+
order: ["applyToFront", "applyToSides", "applyToEnd", "pictureFormat", "pictureStackUnit"]
|
|
530
|
+
}
|
|
531
|
+
];
|
|
532
|
+
const SERIES_ORDER_RULES = [
|
|
533
|
+
// CT_BarSer (§21.2.2.17)
|
|
534
|
+
{
|
|
535
|
+
chartTypes: ["barChart", "bar3DChart"],
|
|
536
|
+
order: [
|
|
537
|
+
"idx",
|
|
538
|
+
"order",
|
|
539
|
+
"tx",
|
|
540
|
+
"spPr",
|
|
541
|
+
"invertIfNegative",
|
|
542
|
+
"pictureOptions",
|
|
543
|
+
"dPt",
|
|
544
|
+
"dLbls",
|
|
545
|
+
"trendline",
|
|
546
|
+
"errBars",
|
|
547
|
+
"cat",
|
|
548
|
+
"val",
|
|
549
|
+
"shape",
|
|
550
|
+
"extLst"
|
|
551
|
+
]
|
|
552
|
+
},
|
|
553
|
+
// CT_LineSer (§21.2.2.99) — line3D uses same structure.
|
|
554
|
+
{
|
|
555
|
+
chartTypes: ["lineChart", "line3DChart", "stockChart"],
|
|
556
|
+
order: [
|
|
557
|
+
"idx",
|
|
558
|
+
"order",
|
|
559
|
+
"tx",
|
|
560
|
+
"spPr",
|
|
561
|
+
"marker",
|
|
562
|
+
"dPt",
|
|
563
|
+
"dLbls",
|
|
564
|
+
"trendline",
|
|
565
|
+
"errBars",
|
|
566
|
+
"cat",
|
|
567
|
+
"val",
|
|
568
|
+
"smooth",
|
|
569
|
+
"extLst"
|
|
570
|
+
]
|
|
571
|
+
},
|
|
572
|
+
// CT_PieSer (§21.2.2.149) — pie / pie3D / doughnut / ofPie share.
|
|
573
|
+
{
|
|
574
|
+
chartTypes: ["pieChart", "pie3DChart", "doughnutChart", "ofPieChart"],
|
|
575
|
+
order: ["idx", "order", "tx", "spPr", "explosion", "dPt", "dLbls", "cat", "val", "extLst"]
|
|
576
|
+
},
|
|
577
|
+
// CT_AreaSer (§21.2.2.3)
|
|
578
|
+
{
|
|
579
|
+
chartTypes: ["areaChart", "area3DChart"],
|
|
580
|
+
order: [
|
|
581
|
+
"idx",
|
|
582
|
+
"order",
|
|
583
|
+
"tx",
|
|
584
|
+
"spPr",
|
|
585
|
+
"pictureOptions",
|
|
586
|
+
"dPt",
|
|
587
|
+
"dLbls",
|
|
588
|
+
"trendline",
|
|
589
|
+
"errBars",
|
|
590
|
+
"cat",
|
|
591
|
+
"val",
|
|
592
|
+
"extLst"
|
|
593
|
+
]
|
|
594
|
+
},
|
|
595
|
+
// CT_ScatterSer (§21.2.2.167)
|
|
596
|
+
{
|
|
597
|
+
chartTypes: ["scatterChart"],
|
|
598
|
+
order: [
|
|
599
|
+
"idx",
|
|
600
|
+
"order",
|
|
601
|
+
"tx",
|
|
602
|
+
"spPr",
|
|
603
|
+
"marker",
|
|
604
|
+
"dPt",
|
|
605
|
+
"dLbls",
|
|
606
|
+
"trendline",
|
|
607
|
+
"errBars",
|
|
608
|
+
"xVal",
|
|
609
|
+
"yVal",
|
|
610
|
+
"smooth",
|
|
611
|
+
"extLst"
|
|
612
|
+
]
|
|
613
|
+
},
|
|
614
|
+
// CT_BubbleSer (§21.2.2.19)
|
|
615
|
+
{
|
|
616
|
+
chartTypes: ["bubbleChart"],
|
|
617
|
+
order: [
|
|
618
|
+
"idx",
|
|
619
|
+
"order",
|
|
620
|
+
"tx",
|
|
621
|
+
"spPr",
|
|
622
|
+
"invertIfNegative",
|
|
623
|
+
"dPt",
|
|
624
|
+
"dLbls",
|
|
625
|
+
"trendline",
|
|
626
|
+
"errBars",
|
|
627
|
+
"xVal",
|
|
628
|
+
"yVal",
|
|
629
|
+
"bubbleSize",
|
|
630
|
+
"bubble3D",
|
|
631
|
+
"extLst"
|
|
632
|
+
]
|
|
633
|
+
},
|
|
634
|
+
// CT_RadarSer (§21.2.2.153)
|
|
635
|
+
{
|
|
636
|
+
chartTypes: ["radarChart"],
|
|
637
|
+
order: ["idx", "order", "tx", "spPr", "marker", "dPt", "dLbls", "cat", "val", "extLst"]
|
|
638
|
+
},
|
|
639
|
+
// CT_SurfaceSer (§21.2.2.191)
|
|
640
|
+
{
|
|
641
|
+
chartTypes: ["surfaceChart", "surface3DChart"],
|
|
642
|
+
order: ["idx", "order", "tx", "spPr", "cat", "val", "extLst"]
|
|
643
|
+
}
|
|
644
|
+
];
|
|
645
|
+
const CLASSIC_REQUIRED_CHILDREN = [
|
|
646
|
+
// Chart types that plot against a Cartesian coordinate system
|
|
647
|
+
// reference their axes by `c:axId`. 2-D variants need exactly two
|
|
648
|
+
// axis references, 3-D variants need three (X / Y / Z / series).
|
|
649
|
+
{ parent: "barChart", child: "axId", min: 2, max: 2 },
|
|
650
|
+
{ parent: "barChart", child: "barDir", min: 1, max: 1 },
|
|
651
|
+
{ parent: "bar3DChart", child: "axId", min: 3, max: 3 },
|
|
652
|
+
{ parent: "bar3DChart", child: "barDir", min: 1, max: 1 },
|
|
653
|
+
{ parent: "lineChart", child: "axId", min: 2, max: 2 },
|
|
654
|
+
{ parent: "lineChart", child: "grouping", min: 1, max: 1 },
|
|
655
|
+
{ parent: "line3DChart", child: "axId", min: 3, max: 3 },
|
|
656
|
+
{ parent: "line3DChart", child: "grouping", min: 1, max: 1 },
|
|
657
|
+
{ parent: "areaChart", child: "axId", min: 2, max: 2 },
|
|
658
|
+
{ parent: "area3DChart", child: "axId", min: 3, max: 3 },
|
|
659
|
+
{ parent: "scatterChart", child: "axId", min: 2, max: 2 },
|
|
660
|
+
{ parent: "scatterChart", child: "scatterStyle", min: 1, max: 1 },
|
|
661
|
+
{ parent: "bubbleChart", child: "axId", min: 2, max: 2 },
|
|
662
|
+
{ parent: "radarChart", child: "axId", min: 2, max: 2 },
|
|
663
|
+
{ parent: "radarChart", child: "radarStyle", min: 1, max: 1 },
|
|
664
|
+
{ parent: "stockChart", child: "axId", min: 2, max: 2 },
|
|
665
|
+
{ parent: "surfaceChart", child: "axId", min: 3, max: 3 },
|
|
666
|
+
{ parent: "surface3DChart", child: "axId", min: 3, max: 3 },
|
|
667
|
+
{ parent: "ofPieChart", child: "ofPieType", min: 1, max: 1 },
|
|
668
|
+
// Axes require axId / scaling / axPos / crossAx.
|
|
669
|
+
{ parent: "catAx", child: "axId", min: 1, max: 1 },
|
|
670
|
+
{ parent: "catAx", child: "scaling", min: 1, max: 1 },
|
|
671
|
+
{ parent: "catAx", child: "axPos", min: 1, max: 1 },
|
|
672
|
+
{ parent: "catAx", child: "crossAx", min: 1, max: 1 },
|
|
673
|
+
{ parent: "valAx", child: "axId", min: 1, max: 1 },
|
|
674
|
+
{ parent: "valAx", child: "scaling", min: 1, max: 1 },
|
|
675
|
+
{ parent: "valAx", child: "axPos", min: 1, max: 1 },
|
|
676
|
+
{ parent: "valAx", child: "crossAx", min: 1, max: 1 },
|
|
677
|
+
{ parent: "dateAx", child: "axId", min: 1, max: 1 },
|
|
678
|
+
{ parent: "dateAx", child: "scaling", min: 1, max: 1 },
|
|
679
|
+
{ parent: "dateAx", child: "axPos", min: 1, max: 1 },
|
|
680
|
+
{ parent: "dateAx", child: "crossAx", min: 1, max: 1 },
|
|
681
|
+
{ parent: "serAx", child: "axId", min: 1, max: 1 },
|
|
682
|
+
{ parent: "serAx", child: "scaling", min: 1, max: 1 },
|
|
683
|
+
{ parent: "serAx", child: "axPos", min: 1, max: 1 },
|
|
684
|
+
{ parent: "serAx", child: "crossAx", min: 1, max: 1 },
|
|
685
|
+
// Every series carries idx + order.
|
|
686
|
+
{ parent: "ser", child: "idx", min: 1, max: 1 },
|
|
687
|
+
{ parent: "ser", child: "order", min: 1, max: 1 },
|
|
688
|
+
// Trendline / errBars / dPt / dLbl header attributes.
|
|
689
|
+
{ parent: "trendline", child: "trendlineType", min: 1, max: 1 },
|
|
690
|
+
{ parent: "errBars", child: "errBarType", min: 1, max: 1 },
|
|
691
|
+
{ parent: "errBars", child: "errValType", min: 1, max: 1 },
|
|
692
|
+
{ parent: "dPt", child: "idx", min: 1, max: 1 },
|
|
693
|
+
{ parent: "dLbl", child: "idx", min: 1, max: 1 }
|
|
694
|
+
];
|
|
695
|
+
const CLASSIC_ENUM_RULES = [
|
|
696
|
+
{ element: "barDir", attr: "val", allowed: ["bar", "col"] },
|
|
697
|
+
{
|
|
698
|
+
element: "grouping",
|
|
699
|
+
attr: "val",
|
|
700
|
+
allowed: ["standard", "stacked", "percentStacked", "clustered"]
|
|
701
|
+
},
|
|
702
|
+
{ element: "orientation", attr: "val", allowed: ["minMax", "maxMin"] },
|
|
703
|
+
{ element: "ofPieType", attr: "val", allowed: ["pie", "bar"] },
|
|
704
|
+
{
|
|
705
|
+
element: "dLblPos",
|
|
706
|
+
attr: "val",
|
|
707
|
+
allowed: ["b", "bestFit", "ctr", "inBase", "inEnd", "l", "outEnd", "r", "t"]
|
|
708
|
+
},
|
|
709
|
+
{ element: "legendPos", attr: "val", allowed: ["b", "l", "r", "t", "tr"] },
|
|
710
|
+
{
|
|
711
|
+
element: "scatterStyle",
|
|
712
|
+
attr: "val",
|
|
713
|
+
allowed: ["none", "line", "lineMarker", "marker", "smooth", "smoothMarker"]
|
|
714
|
+
},
|
|
715
|
+
{ element: "radarStyle", attr: "val", allowed: ["standard", "marker", "filled"] },
|
|
716
|
+
{ element: "dispBlanksAs", attr: "val", allowed: ["span", "gap", "zero"] },
|
|
717
|
+
{ element: "splitType", attr: "val", allowed: ["auto", "cust", "percent", "pos", "val"] },
|
|
718
|
+
{
|
|
719
|
+
element: "shape",
|
|
720
|
+
attr: "val",
|
|
721
|
+
allowed: ["cone", "coneToMax", "box", "cylinder", "pyramid", "pyramidToMax"]
|
|
722
|
+
},
|
|
723
|
+
{ element: "crosses", attr: "val", allowed: ["autoZero", "min", "max"] },
|
|
724
|
+
{ element: "crossBetween", attr: "val", allowed: ["between", "midCat"] },
|
|
725
|
+
{ element: "lblAlgn", attr: "val", allowed: ["ctr", "l", "r"] },
|
|
726
|
+
{ element: "axPos", attr: "val", allowed: ["b", "l", "r", "t"] },
|
|
727
|
+
{ element: "majorTickMark", attr: "val", allowed: ["cross", "in", "none", "out"] },
|
|
728
|
+
{ element: "minorTickMark", attr: "val", allowed: ["cross", "in", "none", "out"] },
|
|
729
|
+
{ element: "tickLblPos", attr: "val", allowed: ["high", "low", "nextTo", "none"] },
|
|
730
|
+
{ element: "baseTimeUnit", attr: "val", allowed: ["days", "months", "years"] },
|
|
731
|
+
{ element: "majorTimeUnit", attr: "val", allowed: ["days", "months", "years"] },
|
|
732
|
+
{ element: "minorTimeUnit", attr: "val", allowed: ["days", "months", "years"] },
|
|
733
|
+
{
|
|
734
|
+
element: "trendlineType",
|
|
735
|
+
attr: "val",
|
|
736
|
+
allowed: ["exp", "linear", "log", "movingAvg", "poly", "power"]
|
|
737
|
+
},
|
|
738
|
+
{ element: "errDir", attr: "val", allowed: ["x", "y"] },
|
|
739
|
+
{ element: "errBarType", attr: "val", allowed: ["both", "minus", "plus"] },
|
|
740
|
+
{
|
|
741
|
+
element: "errValType",
|
|
742
|
+
attr: "val",
|
|
743
|
+
allowed: ["cust", "fixedVal", "percentage", "stdDev", "stdErr"]
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
element: "symbol",
|
|
747
|
+
attr: "val",
|
|
748
|
+
allowed: [
|
|
749
|
+
"auto",
|
|
750
|
+
"circle",
|
|
751
|
+
"dash",
|
|
752
|
+
"diamond",
|
|
753
|
+
"dot",
|
|
754
|
+
"none",
|
|
755
|
+
"picture",
|
|
756
|
+
"plus",
|
|
757
|
+
"square",
|
|
758
|
+
"star",
|
|
759
|
+
"triangle",
|
|
760
|
+
"x"
|
|
761
|
+
]
|
|
762
|
+
},
|
|
763
|
+
{ element: "sizeRepresents", attr: "val", allowed: ["area", "w"] },
|
|
764
|
+
// DrawingML theme-palette slots — `<a:schemeClr val>` must be one
|
|
765
|
+
// of the 17 canonical slot names per ECMA-376 §20.1.10.54
|
|
766
|
+
// (`ST_SchemeColorVal`). Typos here either inherit Excel's
|
|
767
|
+
// "Accent 1" default or strip the fill entirely depending on
|
|
768
|
+
// build.
|
|
769
|
+
{
|
|
770
|
+
element: "schemeClr",
|
|
771
|
+
attr: "val",
|
|
772
|
+
allowed: [
|
|
773
|
+
"bg1",
|
|
774
|
+
"bg2",
|
|
775
|
+
"tx1",
|
|
776
|
+
"tx2",
|
|
777
|
+
"accent1",
|
|
778
|
+
"accent2",
|
|
779
|
+
"accent3",
|
|
780
|
+
"accent4",
|
|
781
|
+
"accent5",
|
|
782
|
+
"accent6",
|
|
783
|
+
"hlink",
|
|
784
|
+
"folHlink",
|
|
785
|
+
"phClr",
|
|
786
|
+
"dk1",
|
|
787
|
+
"dk2",
|
|
788
|
+
"lt1",
|
|
789
|
+
"lt2"
|
|
790
|
+
]
|
|
791
|
+
}
|
|
792
|
+
];
|
|
793
|
+
const CLASSIC_RANGE_RULES = [
|
|
794
|
+
{ element: "holeSize", attr: "val", min: 10, max: 90 },
|
|
795
|
+
{ element: "firstSliceAng", attr: "val", min: 0, max: 360 },
|
|
796
|
+
{ element: "overlap", attr: "val", min: -100, max: 100 },
|
|
797
|
+
{ element: "gapWidth", attr: "val", min: 0, max: 500 },
|
|
798
|
+
{ element: "gapDepth", attr: "val", min: 0, max: 500 },
|
|
799
|
+
{ element: "rotX", attr: "val", min: -90, max: 90 },
|
|
800
|
+
{ element: "rotY", attr: "val", min: 0, max: 360 },
|
|
801
|
+
{ element: "perspective", attr: "val", min: 0, max: 240 },
|
|
802
|
+
{ element: "hPercent", attr: "val", min: 5, max: 500 },
|
|
803
|
+
{ element: "depthPercent", attr: "val", min: 20, max: 2000 },
|
|
804
|
+
{ element: "bubbleScale", attr: "val", min: 0, max: 300 },
|
|
805
|
+
{ element: "secondPieSize", attr: "val", min: 5, max: 200 },
|
|
806
|
+
// Axis units must be strictly positive. `Number.EPSILON` is a
|
|
807
|
+
// close-to-zero sentinel that rejects 0 and negatives while
|
|
808
|
+
// permitting the smallest finite positive a double can express.
|
|
809
|
+
{ element: "majorUnit", attr: "val", min: Number.EPSILON, max: Number.MAX_VALUE },
|
|
810
|
+
{ element: "minorUnit", attr: "val", min: Number.EPSILON, max: Number.MAX_VALUE },
|
|
811
|
+
// Tick-label / tick-mark skip: 1 = every tick, so >= 1 per schema
|
|
812
|
+
// (CT_Skip uses `xsd:unsignedInt` with a required minimum of 1).
|
|
813
|
+
{ element: "tickLblSkip", attr: "val", min: 1, max: Number.MAX_SAFE_INTEGER },
|
|
814
|
+
{ element: "tickMarkSkip", attr: "val", min: 1, max: Number.MAX_SAFE_INTEGER },
|
|
815
|
+
// DrawingML `<a:alpha val>` is CT_PositiveFixedPercentage
|
|
816
|
+
// (0 to 100000 representing 0% to 100%).
|
|
817
|
+
{ element: "alpha", attr: "val", min: 0, max: 100000 }
|
|
818
|
+
];
|
|
819
|
+
function checkClassicChartSchema(ctx, path, root) {
|
|
820
|
+
checkClassicChildOrder(ctx, path, root);
|
|
821
|
+
checkClassicRequiredChildren(ctx, path, root);
|
|
822
|
+
checkClassicEnumValues(ctx, path, root);
|
|
823
|
+
checkClassicNumericRanges(ctx, path, root);
|
|
824
|
+
checkClassicDLblChoice(ctx, path, root);
|
|
825
|
+
checkContextAwareChartRules(ctx, path, root);
|
|
826
|
+
checkDataReferenceStructure(ctx, path, root);
|
|
827
|
+
checkSeriesChildOrder(ctx, path, root);
|
|
828
|
+
checkAxIdResolution(ctx, path, root);
|
|
829
|
+
checkErrBarsConditionalChildren(ctx, path, root);
|
|
830
|
+
checkTxChoice(ctx, path, root);
|
|
831
|
+
checkSeriesChildWhitelist(ctx, path, root);
|
|
832
|
+
checkSrgbClrFormat(ctx, path, root);
|
|
833
|
+
checkNumFmtFormatCode(ctx, path, root);
|
|
834
|
+
checkRichStructure(ctx, path, root);
|
|
835
|
+
// Cross-part reference checks — these reach into `xl/theme/theme1.xml`
|
|
836
|
+
// and `xl/workbook.xml` via ctx.readDom (cached).
|
|
837
|
+
checkThemeSchemeColorSlots(ctx, path, root);
|
|
838
|
+
checkFormulaSyntax(ctx, path, root);
|
|
839
|
+
checkDefinedNameResolution(ctx, path, root);
|
|
840
|
+
}
|
|
841
|
+
function checkClassicChildOrder(ctx, path, root) {
|
|
842
|
+
for (const rule of CLASSIC_ORDER_RULES) {
|
|
843
|
+
for (const parent of collectDescendantsLocal(root, rule.parent)) {
|
|
844
|
+
if (ctx.reporter.capped) {
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const children = parent.children.filter(c => c.type === "element");
|
|
848
|
+
const rankOf = (tag) => {
|
|
849
|
+
if (rule.choiceGroups) {
|
|
850
|
+
for (let i = 0; i < rule.choiceGroups.length; i++) {
|
|
851
|
+
if (rule.choiceGroups[i].includes(tag)) {
|
|
852
|
+
// Every tag in the same choice group maps to the same
|
|
853
|
+
// virtual bucket ("__CHART_TYPES__" / "__AXES__") so
|
|
854
|
+
// they compare equal in sibling-order checks.
|
|
855
|
+
return rule.order.indexOf(`__${i === 0 ? "CHART_TYPES" : "AXES"}__`);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return rule.order.indexOf(tag);
|
|
860
|
+
};
|
|
861
|
+
let lastRank = -1;
|
|
862
|
+
let lastTag = "";
|
|
863
|
+
for (const child of children) {
|
|
864
|
+
const name = localName(child.name);
|
|
865
|
+
const rank = rankOf(name);
|
|
866
|
+
if (rank < 0) {
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
if (rank < lastRank) {
|
|
870
|
+
ctx.reporter.error("chart-child-out-of-order", `${path}: <c:${rule.parent}> child <c:${name}> appears after <c:${lastTag}> — ECMA-376 requires it before per CT_${capitalise(rule.parent)}.`, path);
|
|
871
|
+
}
|
|
872
|
+
if (rank >= lastRank) {
|
|
873
|
+
lastRank = rank;
|
|
874
|
+
lastTag = name;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
function checkClassicRequiredChildren(ctx, path, root) {
|
|
881
|
+
for (const rule of CLASSIC_REQUIRED_CHILDREN) {
|
|
882
|
+
for (const parent of collectDescendantsLocal(root, rule.parent)) {
|
|
883
|
+
if (ctx.reporter.capped) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
const n = findChildrenLocal(parent, rule.child).length;
|
|
887
|
+
if (n < rule.min) {
|
|
888
|
+
ctx.reporter.error("chart-missing-required-child", `${path}: <c:${rule.parent}> has ${n} <c:${rule.child}> child(ren); schema requires at least ${rule.min}.`, path);
|
|
889
|
+
}
|
|
890
|
+
if (rule.max !== undefined && n > rule.max) {
|
|
891
|
+
ctx.reporter.error("chart-wrong-child-count", `${path}: <c:${rule.parent}> has ${n} <c:${rule.child}> child(ren); schema permits at most ${rule.max}.`, path);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
function checkClassicEnumValues(ctx, path, root) {
|
|
897
|
+
for (const rule of CLASSIC_ENUM_RULES) {
|
|
898
|
+
for (const el of collectDescendantsLocal(root, rule.element)) {
|
|
899
|
+
if (ctx.reporter.capped) {
|
|
900
|
+
return;
|
|
901
|
+
}
|
|
902
|
+
const val = attrByLocalName(el, rule.attr);
|
|
903
|
+
if (val === undefined) {
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
if (!rule.allowed.includes(val)) {
|
|
907
|
+
ctx.reporter.error("chart-invalid-enum-value", `${path}: <c:${rule.element} ${rule.attr}="${val}"> — ${val} is not in {${rule.allowed.join(", ")}}.`, path);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
function checkClassicNumericRanges(ctx, path, root) {
|
|
913
|
+
for (const rule of CLASSIC_RANGE_RULES) {
|
|
914
|
+
for (const el of collectDescendantsLocal(root, rule.element)) {
|
|
915
|
+
if (ctx.reporter.capped) {
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
const raw = attrByLocalName(el, rule.attr);
|
|
919
|
+
if (raw === undefined) {
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
922
|
+
const num = parseFloat(raw);
|
|
923
|
+
if (!Number.isFinite(num)) {
|
|
924
|
+
ctx.reporter.error("chart-value-out-of-range", `${path}: <c:${rule.element} ${rule.attr}="${raw}"> is not a finite number.`, path);
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
if (num < rule.min || num > rule.max) {
|
|
928
|
+
ctx.reporter.error("chart-value-out-of-range", `${path}: <c:${rule.element} ${rule.attr}="${num}"> outside [${rule.min}, ${rule.max}].`, path);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* `CT_DLbl` is `idx, choice(delete | (layout, tx, numFmt, spPr,
|
|
935
|
+
* txPr, dLblPos, show*..., separator)), extLst?`. The two choice
|
|
936
|
+
* branches are mutually exclusive — emitting `delete` alongside
|
|
937
|
+
* any display-flag child is a schema violation that Excel's
|
|
938
|
+
* loader has been observed to handle inconsistently (some builds
|
|
939
|
+
* strip the label wholesale, others silently drop `delete`).
|
|
940
|
+
*/
|
|
941
|
+
function checkClassicDLblChoice(ctx, path, root) {
|
|
942
|
+
const displayBranchTags = new Set([
|
|
943
|
+
"layout",
|
|
944
|
+
"tx",
|
|
945
|
+
"numFmt",
|
|
946
|
+
"spPr",
|
|
947
|
+
"txPr",
|
|
948
|
+
"dLblPos",
|
|
949
|
+
"showLegendKey",
|
|
950
|
+
"showVal",
|
|
951
|
+
"showCatName",
|
|
952
|
+
"showSerName",
|
|
953
|
+
"showPercent",
|
|
954
|
+
"showBubbleSize",
|
|
955
|
+
"separator"
|
|
956
|
+
]);
|
|
957
|
+
for (const dLbl of collectDescendantsLocal(root, "dLbl")) {
|
|
958
|
+
if (ctx.reporter.capped) {
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
const hasDelete = findChildLocal(dLbl, "delete") !== undefined;
|
|
962
|
+
if (!hasDelete) {
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
const conflictingChild = dLbl.children.find(c => c.type === "element" && displayBranchTags.has(localName(c.name)));
|
|
966
|
+
if (conflictingChild) {
|
|
967
|
+
ctx.reporter.error("chart-child-out-of-order", `${path}: <c:dLbl> has both <c:delete> and <c:${localName(conflictingChild.name)}>; CT_DLbl's choice group requires one branch or the other, never both.`, path);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
function capitalise(s) {
|
|
972
|
+
return s.length === 0 ? s : s.charAt(0).toUpperCase() + s.slice(1);
|
|
973
|
+
}
|
|
974
|
+
// -----------------------------------------------------------------------------
|
|
975
|
+
// Context-aware rules — chart-type-specific restrictions that the
|
|
976
|
+
// generic schema tables above cannot express. Each rule scopes itself
|
|
977
|
+
// to one or more `c:*Chart` parents so the same child element gets
|
|
978
|
+
// validated differently based on where it lives.
|
|
979
|
+
// -----------------------------------------------------------------------------
|
|
980
|
+
/**
|
|
981
|
+
* Per-chart-type `c:dLblPos` allow-list. Although ECMA-376 `ST_DLblPos`
|
|
982
|
+
* permits all nine values globally, Excel's reader applies a stricter
|
|
983
|
+
* per-context filter that mirrors the "Label Position" picker in the
|
|
984
|
+
* Format Data Labels panel. Emitting a value outside the allow-list
|
|
985
|
+
* triggers "Repaired Records: Drawing" on open, and — for doughnut —
|
|
986
|
+
* causes the entire `drawing*.xml` part to be stripped.
|
|
987
|
+
*
|
|
988
|
+
* - `doughnut`: Excel's UI exposes no position choices at all; any
|
|
989
|
+
* `c:dLblPos` in a doughnut chart's `c:dLbls` causes the drawing
|
|
990
|
+
* part to be removed on open.
|
|
991
|
+
* - `bar` / `bar3D`: only the four "in/out/base/centre" positions.
|
|
992
|
+
* `inBase` is unique to bar and anchors the label to the axis end.
|
|
993
|
+
* - `line`, `line3D`, `scatter`, `bubble`, `radar`, `stock`: only the
|
|
994
|
+
* five cartesian positions (centre, above, below, left, right).
|
|
995
|
+
* - `pie`, `pie3D`, `ofPie`: the pie label set — bestFit is Excel's
|
|
996
|
+
* default and the only value that lets Excel place labels with
|
|
997
|
+
* automatic leader lines.
|
|
998
|
+
* - `area`, `area3D`: Excel only accepts `ctr` for area fills.
|
|
999
|
+
* - `surface`, `surface3D`: data labels are forbidden entirely (see
|
|
1000
|
+
* `FORBIDDEN_CHILDREN_BY_CHART_TYPE` below).
|
|
1001
|
+
*/
|
|
1002
|
+
const VALID_DLBL_POSITIONS_BY_CHART_TYPE = {
|
|
1003
|
+
barChart: new Set(["ctr", "inBase", "inEnd", "outEnd"]),
|
|
1004
|
+
bar3DChart: new Set(["ctr", "inBase", "inEnd", "outEnd"]),
|
|
1005
|
+
lineChart: new Set(["ctr", "l", "r", "t", "b"]),
|
|
1006
|
+
line3DChart: new Set(["ctr", "l", "r", "t", "b"]),
|
|
1007
|
+
scatterChart: new Set(["ctr", "l", "r", "t", "b"]),
|
|
1008
|
+
bubbleChart: new Set(["ctr", "l", "r", "t", "b"]),
|
|
1009
|
+
radarChart: new Set(["ctr", "l", "r", "t", "b"]),
|
|
1010
|
+
stockChart: new Set(["ctr", "l", "r", "t", "b"]),
|
|
1011
|
+
pieChart: new Set(["bestFit", "ctr", "inEnd", "outEnd"]),
|
|
1012
|
+
pie3DChart: new Set(["bestFit", "ctr", "inEnd", "outEnd"]),
|
|
1013
|
+
ofPieChart: new Set(["bestFit", "ctr", "inEnd", "outEnd"]),
|
|
1014
|
+
doughnutChart: new Set(),
|
|
1015
|
+
areaChart: new Set(["ctr"]),
|
|
1016
|
+
area3DChart: new Set(["ctr"])
|
|
1017
|
+
};
|
|
1018
|
+
/**
|
|
1019
|
+
* `c:trendline` and `c:errBars` are forbidden inside pie-family
|
|
1020
|
+
* series (`CT_PieSer` has neither slot per schema). They are also
|
|
1021
|
+
* forbidden on surface series. Emitting one produces a series Excel
|
|
1022
|
+
* refuses to render. `c:dataLabels` is forbidden on surface series.
|
|
1023
|
+
*/
|
|
1024
|
+
const FORBIDDEN_CHILDREN_BY_CHART_TYPE = {
|
|
1025
|
+
pieChart: new Set(["trendline", "errBars"]),
|
|
1026
|
+
pie3DChart: new Set(["trendline", "errBars"]),
|
|
1027
|
+
doughnutChart: new Set(["trendline", "errBars"]),
|
|
1028
|
+
ofPieChart: new Set(["trendline", "errBars"]),
|
|
1029
|
+
surfaceChart: new Set(["trendline", "errBars", "dLbls", "marker"]),
|
|
1030
|
+
surface3DChart: new Set(["trendline", "errBars", "dLbls", "marker"])
|
|
1031
|
+
};
|
|
1032
|
+
/**
|
|
1033
|
+
* `c:errBars` maxOccurs per series, per parent chart type.
|
|
1034
|
+
*
|
|
1035
|
+
* Scatter and bubble series use `CT_ScatterSer` / `CT_BubbleSer` whose
|
|
1036
|
+
* `errBars` element has `maxOccurs="2"` — one for direction `x`, one
|
|
1037
|
+
* for direction `y`. Every other series type caps at 1. Two entries
|
|
1038
|
+
* with the same `c:errDir` inside the same series are a schema
|
|
1039
|
+
* violation; Excel silently drops the duplicate (first one wins).
|
|
1040
|
+
*/
|
|
1041
|
+
const ERRBARS_MAX_BY_CHART_TYPE = {
|
|
1042
|
+
scatterChart: 2,
|
|
1043
|
+
bubbleChart: 2,
|
|
1044
|
+
barChart: 1,
|
|
1045
|
+
bar3DChart: 1,
|
|
1046
|
+
lineChart: 1,
|
|
1047
|
+
line3DChart: 1,
|
|
1048
|
+
areaChart: 1,
|
|
1049
|
+
area3DChart: 1,
|
|
1050
|
+
radarChart: 1,
|
|
1051
|
+
stockChart: 1
|
|
1052
|
+
};
|
|
1053
|
+
/**
|
|
1054
|
+
* Required series count per chart type. `stockChart` needs 3 or 4
|
|
1055
|
+
* series (HLC or OHLC); everything else needs at least 1.
|
|
1056
|
+
*/
|
|
1057
|
+
const SERIES_COUNT_BY_CHART_TYPE = {
|
|
1058
|
+
barChart: { min: 1 },
|
|
1059
|
+
bar3DChart: { min: 1 },
|
|
1060
|
+
lineChart: { min: 1 },
|
|
1061
|
+
line3DChart: { min: 1 },
|
|
1062
|
+
areaChart: { min: 1 },
|
|
1063
|
+
area3DChart: { min: 1 },
|
|
1064
|
+
scatterChart: { min: 1 },
|
|
1065
|
+
bubbleChart: { min: 1 },
|
|
1066
|
+
radarChart: { min: 1 },
|
|
1067
|
+
pieChart: { min: 1 },
|
|
1068
|
+
pie3DChart: { min: 1 },
|
|
1069
|
+
doughnutChart: { min: 1 },
|
|
1070
|
+
ofPieChart: { min: 1 },
|
|
1071
|
+
surfaceChart: { min: 1 },
|
|
1072
|
+
surface3DChart: { min: 1 },
|
|
1073
|
+
stockChart: { min: 3, max: 4 }
|
|
1074
|
+
};
|
|
1075
|
+
/**
|
|
1076
|
+
* Walk each chart-type group in the plot area and apply the
|
|
1077
|
+
* context-sensitive rules above:
|
|
1078
|
+
* - `c:dLblPos` value matches the allow-list for this type.
|
|
1079
|
+
* - `c:trendline` / `c:errBars` not present when forbidden.
|
|
1080
|
+
* - `c:errBars` cardinality + `c:errDir` uniqueness.
|
|
1081
|
+
* - Series count within the schema-permitted range.
|
|
1082
|
+
* - Series `c:idx` / `c:order` values unique inside the group.
|
|
1083
|
+
*/
|
|
1084
|
+
function checkContextAwareChartRules(ctx, path, root) {
|
|
1085
|
+
for (const chartType of Object.keys(VALID_DLBL_POSITIONS_BY_CHART_TYPE)) {
|
|
1086
|
+
for (const group of collectDescendantsLocal(root, chartType)) {
|
|
1087
|
+
if (ctx.reporter.capped) {
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
checkDLblPosForChartType(ctx, path, group, chartType);
|
|
1091
|
+
checkForbiddenSeriesChildren(ctx, path, group, chartType);
|
|
1092
|
+
checkErrBarsCardinality(ctx, path, group, chartType);
|
|
1093
|
+
checkSeriesIdxOrderUnique(ctx, path, group, chartType);
|
|
1094
|
+
checkSeriesCount(ctx, path, group, chartType);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
// Surface / surface3D only participate in series-count + forbidden-
|
|
1098
|
+
// children checks — no dLblPos allow-list because Excel rejects
|
|
1099
|
+
// data labels on them wholesale.
|
|
1100
|
+
for (const chartType of ["surfaceChart", "surface3DChart"]) {
|
|
1101
|
+
for (const group of collectDescendantsLocal(root, chartType)) {
|
|
1102
|
+
if (ctx.reporter.capped) {
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
checkForbiddenSeriesChildren(ctx, path, group, chartType);
|
|
1106
|
+
checkSeriesCount(ctx, path, group, chartType);
|
|
1107
|
+
checkSeriesIdxOrderUnique(ctx, path, group, chartType);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
function checkDLblPosForChartType(ctx, path, group, chartType) {
|
|
1112
|
+
const allowed = VALID_DLBL_POSITIONS_BY_CHART_TYPE[chartType];
|
|
1113
|
+
if (!allowed) {
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
for (const pos of collectDescendantsLocal(group, "dLblPos")) {
|
|
1117
|
+
if (ctx.reporter.capped) {
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
const val = attrByLocalName(pos, "val");
|
|
1121
|
+
if (val === undefined) {
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
if (!allowed.has(val)) {
|
|
1125
|
+
const allowedList = allowed.size === 0
|
|
1126
|
+
? "(none — this chart type does not accept c:dLblPos)"
|
|
1127
|
+
: [...allowed].sort().join(", ");
|
|
1128
|
+
ctx.reporter.error("chart-invalid-enum-value", `${path}: <c:dLblPos val="${val}"> inside <c:${chartType}> — Excel only accepts {${allowedList}} for this chart type.`, path);
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
function checkForbiddenSeriesChildren(ctx, path, group, chartType) {
|
|
1133
|
+
const forbidden = FORBIDDEN_CHILDREN_BY_CHART_TYPE[chartType];
|
|
1134
|
+
if (!forbidden || forbidden.size === 0) {
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
for (const series of findChildrenLocal(group, "ser")) {
|
|
1138
|
+
for (const child of series.children) {
|
|
1139
|
+
if (ctx.reporter.capped) {
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
if (child.type !== "element") {
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
1145
|
+
const name = localName(child.name);
|
|
1146
|
+
if (forbidden.has(name)) {
|
|
1147
|
+
ctx.reporter.error("chart-forbidden-child", `${path}: <c:ser> inside <c:${chartType}> contains <c:${name}>, which is not allowed for this chart type per ECMA-376.`, path);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
// Surface charts forbid dLbls at the GROUP level too.
|
|
1152
|
+
if (forbidden.has("dLbls")) {
|
|
1153
|
+
for (const dLbls of findChildrenLocal(group, "dLbls")) {
|
|
1154
|
+
if (ctx.reporter.capped) {
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
void dLbls;
|
|
1158
|
+
ctx.reporter.error("chart-forbidden-child", `${path}: <c:${chartType}> contains group-level <c:dLbls>, which is not allowed for this chart type.`, path);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
function checkErrBarsCardinality(ctx, path, group, chartType) {
|
|
1163
|
+
const max = ERRBARS_MAX_BY_CHART_TYPE[chartType];
|
|
1164
|
+
if (max === undefined) {
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
for (const series of findChildrenLocal(group, "ser")) {
|
|
1168
|
+
if (ctx.reporter.capped) {
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
const errBars = findChildrenLocal(series, "errBars");
|
|
1172
|
+
if (errBars.length > max) {
|
|
1173
|
+
ctx.reporter.error("chart-wrong-child-count", `${path}: <c:ser> inside <c:${chartType}> has ${errBars.length} <c:errBars> children; schema permits at most ${max}.`, path);
|
|
1174
|
+
}
|
|
1175
|
+
// When maxOccurs=2 (scatter/bubble), the two entries must carry
|
|
1176
|
+
// distinct `c:errDir` values.
|
|
1177
|
+
if (errBars.length === 2) {
|
|
1178
|
+
const dirs = errBars.map(eb => {
|
|
1179
|
+
const dirEl = findChildLocal(eb, "errDir");
|
|
1180
|
+
return dirEl ? attrByLocalName(dirEl, "val") : undefined;
|
|
1181
|
+
});
|
|
1182
|
+
if (dirs[0] !== undefined && dirs[1] !== undefined && dirs[0] === dirs[1]) {
|
|
1183
|
+
ctx.reporter.error("chart-duplicate-errBars-direction", `${path}: <c:ser> inside <c:${chartType}> has two <c:errBars> with the same <c:errDir val="${dirs[0]}"> — schema requires distinct directions (x + y).`, path);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
function checkSeriesIdxOrderUnique(ctx, path, group, chartType) {
|
|
1189
|
+
const series = findChildrenLocal(group, "ser");
|
|
1190
|
+
const seenIdx = new Set();
|
|
1191
|
+
const seenOrder = new Set();
|
|
1192
|
+
for (const s of series) {
|
|
1193
|
+
if (ctx.reporter.capped) {
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
const idxEl = findChildLocal(s, "idx");
|
|
1197
|
+
const orderEl = findChildLocal(s, "order");
|
|
1198
|
+
const idx = idxEl ? attrByLocalName(idxEl, "val") : undefined;
|
|
1199
|
+
const order = orderEl ? attrByLocalName(orderEl, "val") : undefined;
|
|
1200
|
+
if (idx !== undefined) {
|
|
1201
|
+
if (seenIdx.has(idx)) {
|
|
1202
|
+
ctx.reporter.error("chart-duplicate-series-idx", `${path}: <c:${chartType}> has two <c:ser> entries with <c:idx val="${idx}">; idx must be unique within a chart-type group.`, path);
|
|
1203
|
+
}
|
|
1204
|
+
seenIdx.add(idx);
|
|
1205
|
+
}
|
|
1206
|
+
if (order !== undefined) {
|
|
1207
|
+
if (seenOrder.has(order)) {
|
|
1208
|
+
ctx.reporter.error("chart-duplicate-series-order", `${path}: <c:${chartType}> has two <c:ser> entries with <c:order val="${order}">; order must be unique within a chart-type group.`, path);
|
|
1209
|
+
}
|
|
1210
|
+
seenOrder.add(order);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
function checkSeriesCount(ctx, path, group, chartType) {
|
|
1215
|
+
const bounds = SERIES_COUNT_BY_CHART_TYPE[chartType];
|
|
1216
|
+
if (!bounds) {
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
const count = findChildrenLocal(group, "ser").length;
|
|
1220
|
+
if (count < bounds.min) {
|
|
1221
|
+
ctx.reporter.error("chart-missing-required-child", `${path}: <c:${chartType}> has ${count} <c:ser> children; schema requires at least ${bounds.min}${bounds.max !== undefined ? ` (and at most ${bounds.max})` : ""}.`, path);
|
|
1222
|
+
}
|
|
1223
|
+
if (bounds.max !== undefined && count > bounds.max) {
|
|
1224
|
+
ctx.reporter.error("chart-wrong-child-count", `${path}: <c:${chartType}> has ${count} <c:ser> children; schema permits at most ${bounds.max}.`, path);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
// -----------------------------------------------------------------------------
|
|
1228
|
+
// Data-reference structural checks — `c:numRef` / `c:strRef` /
|
|
1229
|
+
// `c:multiLvlStrRef` require `c:f`; their caches require `c:ptCount`;
|
|
1230
|
+
// every `c:pt` inside a cache needs `@idx` and a `c:v` child; `c:pt
|
|
1231
|
+
// idx` must stay inside the declared cache range.
|
|
1232
|
+
// -----------------------------------------------------------------------------
|
|
1233
|
+
const REF_ELEMENTS = ["numRef", "strRef", "multiLvlStrRef"];
|
|
1234
|
+
const LIT_ELEMENTS = ["numLit", "strLit"];
|
|
1235
|
+
const CACHE_ELEMENTS = ["numCache", "strCache", "multiLvlStrCache"];
|
|
1236
|
+
function checkDataReferenceStructure(ctx, path, root) {
|
|
1237
|
+
// `c:numRef`, `c:strRef`, `c:multiLvlStrRef` must carry `c:f`.
|
|
1238
|
+
for (const refName of REF_ELEMENTS) {
|
|
1239
|
+
for (const ref of collectDescendantsLocal(root, refName)) {
|
|
1240
|
+
if (ctx.reporter.capped) {
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
if (!findChildLocal(ref, "f")) {
|
|
1244
|
+
ctx.reporter.error("chart-missing-required-child", `${path}: <c:${refName}> is missing the required <c:f> formula child.`, path);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
// `c:numLit` / `c:strLit` should carry `c:ptCount`.
|
|
1249
|
+
for (const litName of LIT_ELEMENTS) {
|
|
1250
|
+
for (const lit of collectDescendantsLocal(root, litName)) {
|
|
1251
|
+
if (ctx.reporter.capped) {
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
if (!findChildLocal(lit, "ptCount")) {
|
|
1255
|
+
ctx.reporter.error("chart-missing-required-child", `${path}: <c:${litName}> is missing the required <c:ptCount> child.`, path);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
// Caches + `c:pt` integrity.
|
|
1260
|
+
for (const cacheName of CACHE_ELEMENTS) {
|
|
1261
|
+
for (const cache of collectDescendantsLocal(root, cacheName)) {
|
|
1262
|
+
if (ctx.reporter.capped) {
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
const ptCountEl = findChildLocal(cache, "ptCount");
|
|
1266
|
+
const ptCountVal = ptCountEl ? parseInt(attrByLocalName(ptCountEl, "val") ?? "", 10) : NaN;
|
|
1267
|
+
const points = findChildrenLocal(cache, "pt");
|
|
1268
|
+
for (const pt of points) {
|
|
1269
|
+
const idxRaw = attrByLocalName(pt, "idx");
|
|
1270
|
+
if (idxRaw === undefined) {
|
|
1271
|
+
ctx.reporter.error("chart-missing-required-child", `${path}: <c:pt> inside <c:${cacheName}> is missing the required idx attribute.`, path);
|
|
1272
|
+
continue;
|
|
1273
|
+
}
|
|
1274
|
+
const idx = parseInt(idxRaw, 10);
|
|
1275
|
+
if (Number.isFinite(ptCountVal) && (idx < 0 || idx >= ptCountVal)) {
|
|
1276
|
+
ctx.reporter.error("chart-pt-idx-out-of-range", `${path}: <c:pt idx="${idx}"> outside declared range [0, ${ptCountVal - 1}] (from <c:ptCount val="${ptCountVal}">).`, path);
|
|
1277
|
+
}
|
|
1278
|
+
// `numCache` / `strCache` points need a `c:v` child carrying
|
|
1279
|
+
// the cached value.
|
|
1280
|
+
if (cacheName !== "multiLvlStrCache" && !findChildLocal(pt, "v")) {
|
|
1281
|
+
ctx.reporter.error("chart-missing-required-child", `${path}: <c:pt idx="${idx}"> inside <c:${cacheName}> is missing the required <c:v> value child.`, path);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
// -----------------------------------------------------------------------------
|
|
1288
|
+
// End of classic chart schema conformance
|
|
1289
|
+
// -----------------------------------------------------------------------------
|
|
1290
|
+
function checkChartEx(ctx, path) {
|
|
1291
|
+
const dom = ctx.readDom(path);
|
|
1292
|
+
if (!dom) {
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
const root = dom.root;
|
|
1296
|
+
if (!hasDescendantLocal(root, "chart")) {
|
|
1297
|
+
ctx.reporter.error("chartEx-missing-chart", `${path}: missing cx:chart`, path);
|
|
1298
|
+
}
|
|
1299
|
+
if (!hasDescendantLocal(root, "plotArea")) {
|
|
1300
|
+
ctx.reporter.error("chartEx-missing-plotArea", `${path}: missing cx:plotArea`, path);
|
|
1301
|
+
}
|
|
1302
|
+
const seriesList = collectDescendantsLocal(root, "series");
|
|
1303
|
+
if (seriesList.length === 0) {
|
|
1304
|
+
ctx.reporter.error("chartEx-missing-series", `${path}: missing cx:series`, path);
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
const dataIds = new Set(collectDescendantsLocal(root, "data")
|
|
1308
|
+
.map(el => parseInt(attrByLocalName(el, "id") ?? "", 10))
|
|
1309
|
+
.filter(Number.isFinite));
|
|
1310
|
+
const axisIds = new Set(collectDescendantsLocal(root, "axis")
|
|
1311
|
+
.map(el => parseInt(attrByLocalName(el, "id") ?? "", 10))
|
|
1312
|
+
.filter(Number.isFinite));
|
|
1313
|
+
for (const series of seriesList) {
|
|
1314
|
+
if (!attrByLocalName(series, "layoutId")) {
|
|
1315
|
+
ctx.reporter.error("chartEx-series-missing-layoutId", `${path}: cx:series missing layoutId`, path);
|
|
1316
|
+
}
|
|
1317
|
+
const dataIdChildren = findChildrenLocal(series, "dataId");
|
|
1318
|
+
// Schema cardinality: `CT_Series/dataId` has `maxOccurs="1"`.
|
|
1319
|
+
// Multi-dimensional series (box-whisker, sunburst, treemap) must
|
|
1320
|
+
// point at a single `<cx:data>` entry that holds every strDim /
|
|
1321
|
+
// numDim they need; emitting multiple dataIds tells Excel the
|
|
1322
|
+
// series references multiple data entries which is the "Removed
|
|
1323
|
+
// Part: drawingN.xml" trigger.
|
|
1324
|
+
if (dataIdChildren.length > 1) {
|
|
1325
|
+
ctx.reporter.error("chartEx-series-too-many-dataId", `${path}: cx:series has ${dataIdChildren.length} <cx:dataId> children; schema permits at most 1. ` +
|
|
1326
|
+
`Consolidate the referenced <cx:data> entries into a single entry.`, path);
|
|
1327
|
+
}
|
|
1328
|
+
for (const dataId of dataIdChildren) {
|
|
1329
|
+
const id = parseInt(attrByLocalName(dataId, "val") ?? "", 10);
|
|
1330
|
+
if (!dataIds.has(id)) {
|
|
1331
|
+
ctx.reporter.error("chartEx-series-missing-data-id", `${path}: cx:series references missing cx:data id ${attrByLocalName(dataId, "val")}`, path);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
for (const axisId of findChildrenLocal(series, "axisId")) {
|
|
1335
|
+
const id = parseInt(attrByLocalName(axisId, "val") ?? "", 10);
|
|
1336
|
+
if (!axisIds.has(id)) {
|
|
1337
|
+
ctx.reporter.error("chartEx-series-missing-axis-id", `${path}: cx:series references missing cx:axis id ${attrByLocalName(axisId, "val")}`, path);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
// externalData (e.g. cx:externalData r:id="...") must resolve in the chart's rels.
|
|
1342
|
+
const externalDataRids = collectDescendantsLocal(root, "externalData")
|
|
1343
|
+
.map(el => attrByLocalName(el, "id"))
|
|
1344
|
+
.filter((id) => !!id);
|
|
1345
|
+
if (externalDataRids.length > 0) {
|
|
1346
|
+
const relsPath = chartRelsPath(path);
|
|
1347
|
+
const rels = ctx.readRels(relsPath);
|
|
1348
|
+
for (const rid of externalDataRids) {
|
|
1349
|
+
if (!rels.byId.has(rid)) {
|
|
1350
|
+
ctx.reporter.error("chartEx-externalData-missing-rel", `${path}: cx:externalData references missing relationship ${rid}`, path);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
// Schema-conformance checks: text-form violations of typed elements,
|
|
1355
|
+
// invalid `<cx:auto/>` element, `<cx:paretoLine>` in layoutPr, and
|
|
1356
|
+
// direct `<cx:layout>` child of `<cx:title>`.
|
|
1357
|
+
checkTypedElementAttrForm(ctx, path, root);
|
|
1358
|
+
checkInvalidAutoElement(ctx, path, root);
|
|
1359
|
+
checkParetoLineInLayoutPr(ctx, path, root);
|
|
1360
|
+
checkTitleDirectLayoutChild(ctx, path, root);
|
|
1361
|
+
// Tier-2 semantic checks.
|
|
1362
|
+
checkAxisPosAndType(ctx, path, root);
|
|
1363
|
+
checkSeriesFDefinedName(ctx, path, root);
|
|
1364
|
+
checkWaterfallSubtotals(ctx, path, root);
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* `<cx:axisId>`, `<cx:dataId>`, `<cx:binCount>`, `<cx:binSize>` and
|
|
1368
|
+
* their siblings use the `val="N"` attribute form. Earlier writer
|
|
1369
|
+
* revisions serialised them as text content (`<cx:axisId>2</cx:axisId>`),
|
|
1370
|
+
* which Excel's strict loader rejects. Flag every occurrence so the
|
|
1371
|
+
* output never regresses to the broken shape.
|
|
1372
|
+
*/
|
|
1373
|
+
const TYPED_ATTR_ONLY_ELEMENTS = ["axisId", "dataId", "binCount", "binSize"];
|
|
1374
|
+
function checkTypedElementAttrForm(ctx, path, root) {
|
|
1375
|
+
for (const name of TYPED_ATTR_ONLY_ELEMENTS) {
|
|
1376
|
+
for (const el of collectDescendantsLocal(root, name)) {
|
|
1377
|
+
if (ctx.reporter.capped) {
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
const val = attrByLocalName(el, "val");
|
|
1381
|
+
const text = directTextContent(el).trim();
|
|
1382
|
+
// Missing `val` AND present non-empty text = the broken text-form.
|
|
1383
|
+
if (val === undefined && text.length > 0) {
|
|
1384
|
+
ctx.reporter.error("chartEx-typed-element-text-form", `${path}: <cx:${name}>${text}</cx:${name}> uses text-content form; schema requires val="${text}" attribute.`, path);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* `<cx:auto/>` is NOT a valid element — auto binning is expressed by
|
|
1391
|
+
* the absence of both `binSize` and `binCount` inside `<cx:binning>`.
|
|
1392
|
+
* A literal `<cx:auto/>` tag anywhere in the chartEx makes Excel drop
|
|
1393
|
+
* the part on load.
|
|
1394
|
+
*/
|
|
1395
|
+
function checkInvalidAutoElement(ctx, path, root) {
|
|
1396
|
+
for (const _el of collectDescendantsLocal(root, "auto")) {
|
|
1397
|
+
if (ctx.reporter.capped) {
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
ctx.reporter.error("chartEx-invalid-auto-element", `${path}: <cx:auto/> element is not in the chartEx schema. ` +
|
|
1401
|
+
`Auto binning is expressed by omitting both <cx:binSize> and <cx:binCount>.`, path);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* `<cx:paretoLine>` is not a child of `<cx:layoutPr>` in the schema.
|
|
1406
|
+
* A real pareto chart expresses the line as a second `<cx:series>` with
|
|
1407
|
+
* `layoutId="paretoLine"`. The mis-placed child made earlier Excel
|
|
1408
|
+
* builds reject the chartEx.
|
|
1409
|
+
*/
|
|
1410
|
+
function checkParetoLineInLayoutPr(ctx, path, root) {
|
|
1411
|
+
for (const lp of collectDescendantsLocal(root, "layoutPr")) {
|
|
1412
|
+
if (ctx.reporter.capped) {
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
const pl = findChildLocal(lp, "paretoLine");
|
|
1416
|
+
if (pl) {
|
|
1417
|
+
ctx.reporter.error("chartEx-paretoLine-in-layoutPr", `${path}: <cx:paretoLine> is not a valid child of <cx:layoutPr>. ` +
|
|
1418
|
+
`Add a second <cx:series layoutId="paretoLine"/> instead.`, path);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* `<cx:title><cx:layout/></cx:title>` is schema-invalid. Title layout
|
|
1424
|
+
* lives in `extLst`-based extensions or (in some clients) `<cx:offset>`
|
|
1425
|
+
* — never as a direct `<cx:layout>` child.
|
|
1426
|
+
*/
|
|
1427
|
+
function checkTitleDirectLayoutChild(ctx, path, root) {
|
|
1428
|
+
for (const title of collectDescendantsLocal(root, "title")) {
|
|
1429
|
+
if (ctx.reporter.capped) {
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
const layout = findChildLocal(title, "layout");
|
|
1433
|
+
if (layout) {
|
|
1434
|
+
ctx.reporter.error("chartEx-title-direct-layout", `${path}: <cx:title> has a direct <cx:layout> child. Title layout ` +
|
|
1435
|
+
`information belongs in extLst-based extensions.`, path);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Concatenate direct text/cdata children of an element, ignoring any
|
|
1441
|
+
* nested elements. Useful for "typed element with stray text content"
|
|
1442
|
+
* detection where a nested element's text should NOT count as the
|
|
1443
|
+
* offending text form.
|
|
1444
|
+
*/
|
|
1445
|
+
function directTextContent(el) {
|
|
1446
|
+
let out = "";
|
|
1447
|
+
for (const child of el.children) {
|
|
1448
|
+
if (child.type === "text" || child.type === "cdata") {
|
|
1449
|
+
out += child.value;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
return out;
|
|
1453
|
+
}
|
|
1454
|
+
// -----------------------------------------------------------------------------
|
|
1455
|
+
// Tier-2 semantic checks
|
|
1456
|
+
// -----------------------------------------------------------------------------
|
|
1457
|
+
/**
|
|
1458
|
+
* `<cx:axis>` must declare its axis role — either via a structural
|
|
1459
|
+
* `<cx:catScaling>` / `<cx:valScaling>` CHILD element (the form Excel
|
|
1460
|
+
* itself emits; the role is inferred from which scaling child is
|
|
1461
|
+
* present) OR via legacy `pos` / `type` attributes. When NONE of
|
|
1462
|
+
* these are present, Excel's loader cannot disambiguate the axis
|
|
1463
|
+
* role and drops the whole `<cx:chartSpace>` on open, cascading
|
|
1464
|
+
* into "Removed Part: /xl/charts/chartExN.xml".
|
|
1465
|
+
*
|
|
1466
|
+
* Verified against Excel 2021's own output (`tmp/aaaaa.xlsx`,
|
|
1467
|
+
* `tmp/ttttt.xlsx`): every `<cx:axis>` it emits omits the
|
|
1468
|
+
* `pos` / `type` attributes and relies on the scaling child.
|
|
1469
|
+
*/
|
|
1470
|
+
function checkAxisPosAndType(ctx, path, root) {
|
|
1471
|
+
for (const axis of collectDescendantsLocal(root, "axis")) {
|
|
1472
|
+
if (ctx.reporter.capped) {
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
const pos = attrByLocalName(axis, "pos");
|
|
1476
|
+
const type = attrByLocalName(axis, "type");
|
|
1477
|
+
const id = attrByLocalName(axis, "id") ?? "?";
|
|
1478
|
+
// Accept either legacy attribute form OR the schema-native
|
|
1479
|
+
// `<cx:catScaling>` / `<cx:valScaling>` child.
|
|
1480
|
+
const hasCatScaling = axis.children.some(c => c.type === "element" && matchesLocal(c.name, "catScaling"));
|
|
1481
|
+
const hasValScaling = axis.children.some(c => c.type === "element" && matchesLocal(c.name, "valScaling"));
|
|
1482
|
+
if (pos === undefined && type === undefined && !hasCatScaling && !hasValScaling) {
|
|
1483
|
+
ctx.reporter.error("chartEx-axis-missing-pos-and-type", `${path}: <cx:axis id="${id}"> has no role marker — emit either a ` +
|
|
1484
|
+
`<cx:catScaling>/<cx:valScaling> child (preferred, matches Excel) ` +
|
|
1485
|
+
`or a pos/type attribute. Excel's loader drops the chartEx otherwise.`, path);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* `<cx:f>` formulas must point at hidden defined names (the
|
|
1491
|
+
* `_xlchart.v1.0`, `_xlchart.v1.1`, … convention Excel itself uses),
|
|
1492
|
+
* NOT directly at worksheet ranges. A bare `<cx:f>Sheet1!$A$1:$A$3</cx:f>`
|
|
1493
|
+
* is rejected on open with "Removed Part: /xl/drawings/drawingN.xml".
|
|
1494
|
+
*
|
|
1495
|
+
* Detection heuristic: a formula body that contains `!$` or `!` followed
|
|
1496
|
+
* by absolute cell references is a direct sheet reference. Defined-name
|
|
1497
|
+
* references are bare identifiers like `_xlchart.v1.0` (no `!`).
|
|
1498
|
+
*/
|
|
1499
|
+
function checkSeriesFDefinedName(ctx, path, root) {
|
|
1500
|
+
for (const f of collectDescendantsLocal(root, "f")) {
|
|
1501
|
+
if (ctx.reporter.capped) {
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
const formula = directTextContent(f).trim();
|
|
1505
|
+
if (formula === "") {
|
|
1506
|
+
continue;
|
|
1507
|
+
}
|
|
1508
|
+
// Heuristic for a direct sheet-qualified range:
|
|
1509
|
+
// - Contains "!" (sheet qualifier), AND
|
|
1510
|
+
// - Does NOT start with `_xl` or other defined-name prefix.
|
|
1511
|
+
if (!formula.includes("!")) {
|
|
1512
|
+
continue; // bare defined name like `_xlchart.v1.0`
|
|
1513
|
+
}
|
|
1514
|
+
if (formula.startsWith("_xlchart.") || formula.startsWith("_xlfn.")) {
|
|
1515
|
+
continue; // defined-name-qualified alias
|
|
1516
|
+
}
|
|
1517
|
+
// Looks like `Sheet1!$A$1:$A$3` or `'Some Name'!$A$1`.
|
|
1518
|
+
ctx.reporter.error("chartEx-f-uses-direct-range-not-defined-name", `${path}: <cx:f>${formula}</cx:f> points at a worksheet range directly. ` +
|
|
1519
|
+
`ChartEx requires an indirection through hidden defined names ` +
|
|
1520
|
+
`(e.g. _xlchart.v1.0) — otherwise Excel 2016+ drops the chartEx part on load.`, path);
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
/**
|
|
1524
|
+
* Waterfall charts must have `layoutPr.subtotals` on their series —
|
|
1525
|
+
* even empty `<cx:subtotals/>` is meaningful: it marks the plot as
|
|
1526
|
+
* subtotals-aware. Without it Excel falls back to generic series
|
|
1527
|
+
* rendering and has been observed to reject the chartEx as malformed
|
|
1528
|
+
* at load time.
|
|
1529
|
+
*/
|
|
1530
|
+
function checkWaterfallSubtotals(ctx, path, root) {
|
|
1531
|
+
for (const series of collectDescendantsLocal(root, "series")) {
|
|
1532
|
+
if (ctx.reporter.capped) {
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1535
|
+
const layoutId = attrByLocalName(series, "layoutId");
|
|
1536
|
+
if (layoutId !== "waterfall") {
|
|
1537
|
+
continue;
|
|
1538
|
+
}
|
|
1539
|
+
const layoutPr = findChildLocal(series, "layoutPr");
|
|
1540
|
+
const hasSubtotals = !!layoutPr && findChildLocal(layoutPr, "subtotals") !== undefined;
|
|
1541
|
+
if (!hasSubtotals) {
|
|
1542
|
+
ctx.reporter.error("chartEx-waterfall-missing-subtotals", `${path}: waterfall series has no <cx:layoutPr><cx:subtotals/> marker. ` +
|
|
1543
|
+
`Emit the element (even empty) so Excel renders the series as ` +
|
|
1544
|
+
`waterfall-aware instead of rejecting the chartEx.`, path);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
// -----------------------------------------------------------------------------
|
|
1549
|
+
// Series child order — context-aware, since `c:ser` has a different
|
|
1550
|
+
// content model under each chart-type element (CT_BarSer, CT_LineSer,
|
|
1551
|
+
// CT_PieSer, CT_AreaSer, CT_ScatterSer, CT_BubbleSer, CT_RadarSer,
|
|
1552
|
+
// CT_SurfaceSer). See {@link SERIES_ORDER_RULES} for the table.
|
|
1553
|
+
// -----------------------------------------------------------------------------
|
|
1554
|
+
function checkSeriesChildOrder(ctx, path, root) {
|
|
1555
|
+
for (const rule of SERIES_ORDER_RULES) {
|
|
1556
|
+
for (const chartType of rule.chartTypes) {
|
|
1557
|
+
for (const group of collectDescendantsLocal(root, chartType)) {
|
|
1558
|
+
for (const series of findChildrenLocal(group, "ser")) {
|
|
1559
|
+
if (ctx.reporter.capped) {
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
const children = series.children.filter(c => c.type === "element");
|
|
1563
|
+
let lastRank = -1;
|
|
1564
|
+
let lastTag = "";
|
|
1565
|
+
for (const child of children) {
|
|
1566
|
+
const name = localName(child.name);
|
|
1567
|
+
const rank = rule.order.indexOf(name);
|
|
1568
|
+
if (rank < 0) {
|
|
1569
|
+
continue;
|
|
1570
|
+
}
|
|
1571
|
+
if (rank < lastRank) {
|
|
1572
|
+
ctx.reporter.error("chart-child-out-of-order", `${path}: <c:ser> inside <c:${chartType}> has <c:${name}> after <c:${lastTag}>; CT_${seriesTypeNameFor(chartType)}Ser requires <c:${name}> earlier in the sequence.`, path);
|
|
1573
|
+
}
|
|
1574
|
+
if (rank >= lastRank) {
|
|
1575
|
+
lastRank = rank;
|
|
1576
|
+
lastTag = name;
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
/** Map a chart-type tag to the series content-type short name. */
|
|
1585
|
+
function seriesTypeNameFor(chartType) {
|
|
1586
|
+
if (chartType.startsWith("bar")) {
|
|
1587
|
+
return "Bar";
|
|
1588
|
+
}
|
|
1589
|
+
if (chartType.startsWith("line") || chartType === "stockChart") {
|
|
1590
|
+
return "Line";
|
|
1591
|
+
}
|
|
1592
|
+
if (chartType.startsWith("pie") || chartType === "doughnutChart" || chartType === "ofPieChart") {
|
|
1593
|
+
return "Pie";
|
|
1594
|
+
}
|
|
1595
|
+
if (chartType.startsWith("area")) {
|
|
1596
|
+
return "Area";
|
|
1597
|
+
}
|
|
1598
|
+
if (chartType === "scatterChart") {
|
|
1599
|
+
return "Scatter";
|
|
1600
|
+
}
|
|
1601
|
+
if (chartType === "bubbleChart") {
|
|
1602
|
+
return "Bubble";
|
|
1603
|
+
}
|
|
1604
|
+
if (chartType === "radarChart") {
|
|
1605
|
+
return "Radar";
|
|
1606
|
+
}
|
|
1607
|
+
if (chartType.startsWith("surface")) {
|
|
1608
|
+
return "Surface";
|
|
1609
|
+
}
|
|
1610
|
+
return chartType;
|
|
1611
|
+
}
|
|
1612
|
+
// -----------------------------------------------------------------------------
|
|
1613
|
+
// axId cross-reference resolution — every `c:axId` inside a chart-type
|
|
1614
|
+
// element must match one of the axis `c:axId` values in the enclosing
|
|
1615
|
+
// plot area; every axis `c:crossAx` must reference another axis in
|
|
1616
|
+
// the same plot area. Unresolved references are schema-valid (they
|
|
1617
|
+
// parse) but produce charts Excel renders with phantom / missing
|
|
1618
|
+
// axes — a common symptom when combo charts are hand-edited.
|
|
1619
|
+
// -----------------------------------------------------------------------------
|
|
1620
|
+
const ALL_CHART_TYPE_NAMES = new Set(CT_CHART_TYPE_TAGS);
|
|
1621
|
+
const ALL_AXIS_NAMES = new Set(CT_AXIS_TAGS);
|
|
1622
|
+
function checkAxIdResolution(ctx, path, root) {
|
|
1623
|
+
for (const plotArea of collectDescendantsLocal(root, "plotArea")) {
|
|
1624
|
+
if (ctx.reporter.capped) {
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
// Gather every axis axId declared in this plot area.
|
|
1628
|
+
const axisIds = new Set();
|
|
1629
|
+
for (const child of plotArea.children) {
|
|
1630
|
+
if (child.type !== "element") {
|
|
1631
|
+
continue;
|
|
1632
|
+
}
|
|
1633
|
+
const name = localName(child.name);
|
|
1634
|
+
if (!ALL_AXIS_NAMES.has(name)) {
|
|
1635
|
+
continue;
|
|
1636
|
+
}
|
|
1637
|
+
const axIdEl = findChildLocal(child, "axId");
|
|
1638
|
+
const val = axIdEl ? attrByLocalName(axIdEl, "val") : undefined;
|
|
1639
|
+
if (val !== undefined) {
|
|
1640
|
+
axisIds.add(val);
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
// Chart-type groups must reference an axis that exists.
|
|
1644
|
+
for (const child of plotArea.children) {
|
|
1645
|
+
if (child.type !== "element") {
|
|
1646
|
+
continue;
|
|
1647
|
+
}
|
|
1648
|
+
const name = localName(child.name);
|
|
1649
|
+
if (!ALL_CHART_TYPE_NAMES.has(name)) {
|
|
1650
|
+
continue;
|
|
1651
|
+
}
|
|
1652
|
+
for (const axIdEl of findChildrenLocal(child, "axId")) {
|
|
1653
|
+
const val = attrByLocalName(axIdEl, "val");
|
|
1654
|
+
if (val === undefined) {
|
|
1655
|
+
continue;
|
|
1656
|
+
}
|
|
1657
|
+
if (!axisIds.has(val)) {
|
|
1658
|
+
ctx.reporter.error("chart-axid-unresolved", `${path}: <c:${name}> references <c:axId val="${val}"> but no matching axis exists in the enclosing <c:plotArea>.`, path);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
// Each axis's `crossAx` must resolve to another axis in the
|
|
1663
|
+
// same plot area.
|
|
1664
|
+
for (const child of plotArea.children) {
|
|
1665
|
+
if (child.type !== "element") {
|
|
1666
|
+
continue;
|
|
1667
|
+
}
|
|
1668
|
+
const name = localName(child.name);
|
|
1669
|
+
if (!ALL_AXIS_NAMES.has(name)) {
|
|
1670
|
+
continue;
|
|
1671
|
+
}
|
|
1672
|
+
const crossAxEl = findChildLocal(child, "crossAx");
|
|
1673
|
+
const val = crossAxEl ? attrByLocalName(crossAxEl, "val") : undefined;
|
|
1674
|
+
if (val !== undefined && !axisIds.has(val)) {
|
|
1675
|
+
ctx.reporter.error("chart-axid-unresolved", `${path}: <c:${name}> has <c:crossAx val="${val}"> but no matching axis exists in the enclosing <c:plotArea>.`, path);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
// -----------------------------------------------------------------------------
|
|
1681
|
+
// ErrBars conditional children — `c:val` is required when
|
|
1682
|
+
// `c:errValType` is NOT "cust", and forbidden when it IS. `c:plus`
|
|
1683
|
+
// and `c:minus` are required for custom error bars and forbidden
|
|
1684
|
+
// otherwise. stdErr allows `c:val` to be absent (Excel defaults to
|
|
1685
|
+
// 1) so we skip the required-child check for that type.
|
|
1686
|
+
// -----------------------------------------------------------------------------
|
|
1687
|
+
function checkErrBarsConditionalChildren(ctx, path, root) {
|
|
1688
|
+
for (const eb of collectDescendantsLocal(root, "errBars")) {
|
|
1689
|
+
if (ctx.reporter.capped) {
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1692
|
+
const typeEl = findChildLocal(eb, "errValType");
|
|
1693
|
+
const type = typeEl ? attrByLocalName(typeEl, "val") : undefined;
|
|
1694
|
+
const hasVal = findChildLocal(eb, "val") !== undefined;
|
|
1695
|
+
const hasPlus = findChildLocal(eb, "plus") !== undefined;
|
|
1696
|
+
const hasMinus = findChildLocal(eb, "minus") !== undefined;
|
|
1697
|
+
if (type === "cust") {
|
|
1698
|
+
if (!hasPlus || !hasMinus) {
|
|
1699
|
+
ctx.reporter.error("chart-missing-required-child", `${path}: <c:errBars> with <c:errValType val="cust"> requires both <c:plus> and <c:minus> children.`, path);
|
|
1700
|
+
}
|
|
1701
|
+
if (hasVal) {
|
|
1702
|
+
ctx.reporter.error("chart-forbidden-child", `${path}: <c:errBars> with <c:errValType val="cust"> must not contain <c:val>; use <c:plus> / <c:minus> for custom bounds.`, path);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
else if (type === "fixedVal" || type === "percentage" || type === "stdDev") {
|
|
1706
|
+
if (!hasVal) {
|
|
1707
|
+
ctx.reporter.error("chart-missing-required-child", `${path}: <c:errBars> with <c:errValType val="${type}"> requires a <c:val> child.`, path);
|
|
1708
|
+
}
|
|
1709
|
+
if (hasPlus || hasMinus) {
|
|
1710
|
+
ctx.reporter.error("chart-forbidden-child", `${path}: <c:errBars> with <c:errValType val="${type}"> must not contain <c:plus> or <c:minus> — those belong to "cust" only.`, path);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
// `stdErr` leaves `val` optional and disallows plus/minus — enforce
|
|
1714
|
+
// the disallowed side only.
|
|
1715
|
+
if (type === "stdErr" && (hasPlus || hasMinus)) {
|
|
1716
|
+
ctx.reporter.error("chart-forbidden-child", `${path}: <c:errBars> with <c:errValType val="stdErr"> must not contain <c:plus> or <c:minus>.`, path);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
// -----------------------------------------------------------------------------
|
|
1721
|
+
// CT_Tx / CT_SerTx choice exclusivity — `c:tx` is
|
|
1722
|
+
// `choice(strRef | rich)` at chart / axis / legend-entry level and
|
|
1723
|
+
// `choice(strRef | v)` at series level. Having more than one branch
|
|
1724
|
+
// is a schema violation; Excel silently keeps the first and drops
|
|
1725
|
+
// the rest, so the feature authors intended gets lost on save.
|
|
1726
|
+
// -----------------------------------------------------------------------------
|
|
1727
|
+
function checkTxChoice(ctx, path, root) {
|
|
1728
|
+
for (const tx of collectDescendantsLocal(root, "tx")) {
|
|
1729
|
+
if (ctx.reporter.capped) {
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
const hasStrRef = findChildLocal(tx, "strRef") !== undefined;
|
|
1733
|
+
const hasRich = findChildLocal(tx, "rich") !== undefined;
|
|
1734
|
+
const hasV = findChildLocal(tx, "v") !== undefined;
|
|
1735
|
+
const branches = [hasStrRef, hasRich, hasV].filter(Boolean).length;
|
|
1736
|
+
// Zero branches is tolerated by Excel (auto-generated title / blank
|
|
1737
|
+
// series name) — we intentionally don't flag it to avoid noise on
|
|
1738
|
+
// round-tripped files. More than one branch is a real schema
|
|
1739
|
+
// violation.
|
|
1740
|
+
if (branches > 1) {
|
|
1741
|
+
ctx.reporter.error("chart-child-out-of-order", `${path}: <c:tx> must contain at most one of <c:strRef>, <c:rich>, or <c:v>; found ${branches} branches.`, path);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
// -----------------------------------------------------------------------------
|
|
1746
|
+
// Series-child whitelist — every `c:ser` element may only contain
|
|
1747
|
+
// children declared in its per-chart-type CT_*Ser content model.
|
|
1748
|
+
// Elements from the global "known chart child" set that aren't
|
|
1749
|
+
// declared for this parent are schema violations even though the
|
|
1750
|
+
// tag name is otherwise valid OOXML. Catches e.g. `<c:bubbleSize>`
|
|
1751
|
+
// inside a `<c:barChart>` series, `<c:explosion>` inside a line
|
|
1752
|
+
// series, or `<c:smooth>` on a bar series.
|
|
1753
|
+
// -----------------------------------------------------------------------------
|
|
1754
|
+
/**
|
|
1755
|
+
* Union of every element name that appears in at least one
|
|
1756
|
+
* `SERIES_ORDER_RULES` entry. A series child inside this set that
|
|
1757
|
+
* isn't in the current chart type's allow-list is a schema
|
|
1758
|
+
* violation; a child outside this set is probably a vendor
|
|
1759
|
+
* extension we should leave alone.
|
|
1760
|
+
*/
|
|
1761
|
+
const ALL_KNOWN_SERIES_CHILDREN = new Set(SERIES_ORDER_RULES.flatMap(r => r.order));
|
|
1762
|
+
function checkSeriesChildWhitelist(ctx, path, root) {
|
|
1763
|
+
for (const rule of SERIES_ORDER_RULES) {
|
|
1764
|
+
const allowed = new Set(rule.order);
|
|
1765
|
+
for (const chartType of rule.chartTypes) {
|
|
1766
|
+
for (const group of collectDescendantsLocal(root, chartType)) {
|
|
1767
|
+
for (const series of findChildrenLocal(group, "ser")) {
|
|
1768
|
+
if (ctx.reporter.capped) {
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
for (const child of series.children) {
|
|
1772
|
+
if (child.type !== "element") {
|
|
1773
|
+
continue;
|
|
1774
|
+
}
|
|
1775
|
+
const name = localName(child.name);
|
|
1776
|
+
if (!ALL_KNOWN_SERIES_CHILDREN.has(name)) {
|
|
1777
|
+
continue; // unknown tag — vendor extension, leave alone
|
|
1778
|
+
}
|
|
1779
|
+
if (!allowed.has(name)) {
|
|
1780
|
+
ctx.reporter.error("chart-forbidden-child", `${path}: <c:ser> inside <c:${chartType}> contains <c:${name}>, which is not in CT_${seriesTypeNameFor(chartType)}Ser's schema.`, path);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
// -----------------------------------------------------------------------------
|
|
1789
|
+
// `<a:srgbClr val>` — CT_SRgbColor requires a 6-hex-digit value.
|
|
1790
|
+
// Excel reliably ignores malformed colours; callers pay the cost of
|
|
1791
|
+
// a silently-grey series. Matches uppercase and lowercase hex.
|
|
1792
|
+
// -----------------------------------------------------------------------------
|
|
1793
|
+
const SRGB_HEX_RE = /^[0-9A-Fa-f]{6}$/;
|
|
1794
|
+
function checkSrgbClrFormat(ctx, path, root) {
|
|
1795
|
+
for (const el of collectDescendantsLocal(root, "srgbClr")) {
|
|
1796
|
+
if (ctx.reporter.capped) {
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
const val = attrByLocalName(el, "val");
|
|
1800
|
+
if (val === undefined) {
|
|
1801
|
+
ctx.reporter.error("chart-missing-required-child", `${path}: <a:srgbClr> is missing the required val attribute.`, path);
|
|
1802
|
+
continue;
|
|
1803
|
+
}
|
|
1804
|
+
if (!SRGB_HEX_RE.test(val)) {
|
|
1805
|
+
ctx.reporter.error("chart-invalid-enum-value", `${path}: <a:srgbClr val="${val}"> must be a 6-digit hex colour (e.g. "4472C4").`, path);
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
// -----------------------------------------------------------------------------
|
|
1810
|
+
// `<c:numFmt formatCode>` is required by CT_NumFmt. The OOXML
|
|
1811
|
+
// serialiser always emits it, but round-tripped / hand-edited files
|
|
1812
|
+
// sometimes drop the attribute; Excel falls back to "General" and
|
|
1813
|
+
// loses the author's intended format.
|
|
1814
|
+
// -----------------------------------------------------------------------------
|
|
1815
|
+
function checkNumFmtFormatCode(ctx, path, root) {
|
|
1816
|
+
for (const el of collectDescendantsLocal(root, "numFmt")) {
|
|
1817
|
+
if (ctx.reporter.capped) {
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
const formatCode = attrByLocalName(el, "formatCode");
|
|
1821
|
+
if (formatCode === undefined) {
|
|
1822
|
+
ctx.reporter.error("chart-missing-required-child", `${path}: <c:numFmt> is missing the required formatCode attribute.`, path);
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
// -----------------------------------------------------------------------------
|
|
1827
|
+
// `<c:rich>` wraps a DrawingML `CT_TextBody`: required children are
|
|
1828
|
+
// `<a:bodyPr>` (first, exactly 1) and `<a:p>` (1+). The list style
|
|
1829
|
+
// `<a:lstStyle>` is optional and goes between them. Without
|
|
1830
|
+
// `<a:bodyPr>` or `<a:p>` Excel renders the title / label blank.
|
|
1831
|
+
// -----------------------------------------------------------------------------
|
|
1832
|
+
function checkRichStructure(ctx, path, root) {
|
|
1833
|
+
for (const rich of collectDescendantsLocal(root, "rich")) {
|
|
1834
|
+
if (ctx.reporter.capped) {
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1837
|
+
const hasBodyPr = findChildLocal(rich, "bodyPr") !== undefined;
|
|
1838
|
+
const paragraphs = findChildrenLocal(rich, "p").length;
|
|
1839
|
+
if (!hasBodyPr) {
|
|
1840
|
+
ctx.reporter.error("chart-missing-required-child", `${path}: <c:rich> is missing the required <a:bodyPr> child (CT_TextBody requires it first).`, path);
|
|
1841
|
+
}
|
|
1842
|
+
if (paragraphs === 0) {
|
|
1843
|
+
ctx.reporter.error("chart-missing-required-child", `${path}: <c:rich> has no <a:p> children; CT_TextBody requires at least one paragraph.`, path);
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
// -----------------------------------------------------------------------------
|
|
1848
|
+
// Cross-part references
|
|
1849
|
+
//
|
|
1850
|
+
// These checks leave the chart XML boundary and validate links
|
|
1851
|
+
// against sibling parts in the xlsx package:
|
|
1852
|
+
// - `<a:schemeClr val>` must reference a colour slot actually
|
|
1853
|
+
// declared in `xl/theme/theme1.xml`'s `<a:clrScheme>`.
|
|
1854
|
+
// - `<c:f>` formula bodies must parse as a valid cell reference,
|
|
1855
|
+
// range, or defined-name identifier.
|
|
1856
|
+
// - `<c:f>` defined-name references must resolve against
|
|
1857
|
+
// `xl/workbook.xml`'s `<definedNames>` or be a reserved
|
|
1858
|
+
// Excel-internal name (`_xlnm.*`, `_xlchart.*`, `_xlfn.*`).
|
|
1859
|
+
// -----------------------------------------------------------------------------
|
|
1860
|
+
/**
|
|
1861
|
+
* Theme's `<a:clrScheme>` declares twelve colour slots under the
|
|
1862
|
+
* short names `dk1, lt1, dk2, lt2, accent1..6, hlink, folHlink`. The
|
|
1863
|
+
* four workbook-facing aliases (`bg1, tx1, bg2, tx2`) resolve through
|
|
1864
|
+
* `<a:clrMap>` — the default Office map is `bg1=lt1, tx1=dk1,
|
|
1865
|
+
* bg2=lt2, tx2=dk2`. `phClr` is a run-time placeholder that's always
|
|
1866
|
+
* valid. Anything else is theme-missing.
|
|
1867
|
+
*/
|
|
1868
|
+
const SCHEME_COLOR_ALIASES = {
|
|
1869
|
+
bg1: "lt1",
|
|
1870
|
+
tx1: "dk1",
|
|
1871
|
+
bg2: "lt2",
|
|
1872
|
+
tx2: "dk2"
|
|
1873
|
+
};
|
|
1874
|
+
function collectThemeColorSlots(ctx) {
|
|
1875
|
+
const dom = ctx.readDom("xl/theme/theme1.xml");
|
|
1876
|
+
if (!dom) {
|
|
1877
|
+
return undefined;
|
|
1878
|
+
}
|
|
1879
|
+
const clrScheme = findFirstDescendantLocal(dom.root, "clrScheme");
|
|
1880
|
+
if (!clrScheme) {
|
|
1881
|
+
return undefined;
|
|
1882
|
+
}
|
|
1883
|
+
const slots = new Set();
|
|
1884
|
+
for (const child of clrScheme.children) {
|
|
1885
|
+
if (child.type === "element") {
|
|
1886
|
+
slots.add(localName(child.name));
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
return slots;
|
|
1890
|
+
}
|
|
1891
|
+
function findFirstDescendantLocal(root, local) {
|
|
1892
|
+
const hits = collectDescendantsLocal(root, local);
|
|
1893
|
+
return hits.length > 0 ? hits[0] : undefined;
|
|
1894
|
+
}
|
|
1895
|
+
function checkThemeSchemeColorSlots(ctx, path, root) {
|
|
1896
|
+
const slots = collectThemeColorSlots(ctx);
|
|
1897
|
+
// Skip silently when the theme is absent or doesn't declare a
|
|
1898
|
+
// `<a:clrScheme>`: either situation is flagged by the content-
|
|
1899
|
+
// types / relationships checkers and we'd just produce duplicate
|
|
1900
|
+
// noise here. We also skip when the slot set is empty — that
|
|
1901
|
+
// would reject every `schemeClr` reference as "missing" which
|
|
1902
|
+
// isn't the author's mistake.
|
|
1903
|
+
if (!slots || slots.size === 0) {
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
for (const el of collectDescendantsLocal(root, "schemeClr")) {
|
|
1907
|
+
if (ctx.reporter.capped) {
|
|
1908
|
+
return;
|
|
1909
|
+
}
|
|
1910
|
+
const val = attrByLocalName(el, "val");
|
|
1911
|
+
if (!val) {
|
|
1912
|
+
continue;
|
|
1913
|
+
}
|
|
1914
|
+
if (val === "phClr") {
|
|
1915
|
+
continue; // placeholder colour — always valid at run time
|
|
1916
|
+
}
|
|
1917
|
+
const resolved = SCHEME_COLOR_ALIASES[val] ?? val;
|
|
1918
|
+
if (!slots.has(resolved)) {
|
|
1919
|
+
ctx.reporter.error("chart-theme-missing-schemeClr-slot", `${path}: <a:schemeClr val="${val}"> references theme slot <a:${resolved}> which is not declared in xl/theme/theme1.xml's <a:clrScheme>.`, path);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
/**
|
|
1924
|
+
* `<c:f>` bodies used by classic charts come in several flavours:
|
|
1925
|
+
* - `Sheet1!$A$1[:$B$2]` — qualified cell / range
|
|
1926
|
+
* - `'Quoted Sheet'!$A$1` — quoted sheet with spaces / specials
|
|
1927
|
+
* - `DefinedName` — bare defined-name identifier
|
|
1928
|
+
* - `Sheet1!LocalName` — sheet-qualified defined name
|
|
1929
|
+
* - `TableName[ColumnName]` — Excel structured reference
|
|
1930
|
+
* - `TableName[[#Headers],[Col]]` — structured ref with specifier
|
|
1931
|
+
* - `(f1,f2)` — multi-range (scatter / combo)
|
|
1932
|
+
* - `SUM(A1:A10)` — function call (rare but valid)
|
|
1933
|
+
* - `_xl*.*` — reserved Excel-internal prefix
|
|
1934
|
+
* (`_xlnm.Print_Area`, `_xlchart.v1.0`, `_xlfn.IFS`…)
|
|
1935
|
+
*
|
|
1936
|
+
* Full formula parsing would require the `@formula` tokenizer;
|
|
1937
|
+
* instead we apply a structural sanity check (balanced brackets /
|
|
1938
|
+
* parens / quotes, non-empty body) plus quick classification for
|
|
1939
|
+
* the two common forms that route into the defined-name resolver
|
|
1940
|
+
* (`bare identifier` and `Sheet!identifier`). Everything else is
|
|
1941
|
+
* assumed to be a valid complex expression — false-negatives here
|
|
1942
|
+
* are preferable to flagging legitimate structured references like
|
|
1943
|
+
* `Transactions[Revenue]`.
|
|
1944
|
+
*/
|
|
1945
|
+
/** Cell or range reference like `A1`, `$A$1`, or `A1:B10`. */
|
|
1946
|
+
const A1_REF_RE = /^\$?[A-Z]+\$?\d+(?::\$?[A-Z]+\$?\d+)?$/;
|
|
1947
|
+
/** Unicode-aware identifier regex matching Excel defined-name rules. */
|
|
1948
|
+
const IDENTIFIER_RE = /^[A-Za-z_\\\u00C0-\uFFFF][A-Za-z0-9_.\\\u00C0-\uFFFF]*$/;
|
|
1949
|
+
/**
|
|
1950
|
+
* Balanced-bracket / paren / quote check — returns `false` when
|
|
1951
|
+
* the formula is syntactically broken (unclosed bracket, orphan
|
|
1952
|
+
* quote, etc.). Excel silently swallows such formulas on open; the
|
|
1953
|
+
* chart loses its backing data.
|
|
1954
|
+
*/
|
|
1955
|
+
function isFormulaBalanced(body) {
|
|
1956
|
+
let parens = 0;
|
|
1957
|
+
let brackets = 0;
|
|
1958
|
+
let inQuote = false;
|
|
1959
|
+
for (let i = 0; i < body.length; i++) {
|
|
1960
|
+
const c = body[i];
|
|
1961
|
+
if (c === "'") {
|
|
1962
|
+
inQuote = !inQuote;
|
|
1963
|
+
continue;
|
|
1964
|
+
}
|
|
1965
|
+
if (inQuote) {
|
|
1966
|
+
continue;
|
|
1967
|
+
}
|
|
1968
|
+
if (c === "(") {
|
|
1969
|
+
parens++;
|
|
1970
|
+
}
|
|
1971
|
+
else if (c === ")") {
|
|
1972
|
+
parens--;
|
|
1973
|
+
}
|
|
1974
|
+
else if (c === "[") {
|
|
1975
|
+
brackets++;
|
|
1976
|
+
}
|
|
1977
|
+
else if (c === "]") {
|
|
1978
|
+
brackets--;
|
|
1979
|
+
}
|
|
1980
|
+
if (parens < 0 || brackets < 0) {
|
|
1981
|
+
return false;
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
return parens === 0 && brackets === 0 && !inQuote;
|
|
1985
|
+
}
|
|
1986
|
+
function parseCFBody(raw) {
|
|
1987
|
+
let body = raw.trim();
|
|
1988
|
+
if (body.startsWith("=")) {
|
|
1989
|
+
body = body.substring(1).trim();
|
|
1990
|
+
}
|
|
1991
|
+
if (body === "") {
|
|
1992
|
+
return { kind: "invalid" };
|
|
1993
|
+
}
|
|
1994
|
+
if (!isFormulaBalanced(body)) {
|
|
1995
|
+
return { kind: "invalid" };
|
|
1996
|
+
}
|
|
1997
|
+
// `_xl`-prefixed names are reserved: `_xlnm.*`, `_xlchart.*`,
|
|
1998
|
+
// `_xlfn.*`. Always treat as valid without cross-reference.
|
|
1999
|
+
if (body.startsWith("_xl")) {
|
|
2000
|
+
return { kind: "special" };
|
|
2001
|
+
}
|
|
2002
|
+
// Expressions wrapped in parens cover multi-range references
|
|
2003
|
+
// (`(Sheet1!$A$1:$A$5,Sheet1!$C$1:$C$5)` on scatter / combo charts)
|
|
2004
|
+
// and function-call formulas (`SUM(A1:A10)`). The balanced-bracket
|
|
2005
|
+
// check above already caught unclosed parens; the full inner
|
|
2006
|
+
// grammar is out of scope for this schema check, so trust the
|
|
2007
|
+
// author and skip.
|
|
2008
|
+
if (body.startsWith("(")) {
|
|
2009
|
+
return { kind: "special" };
|
|
2010
|
+
}
|
|
2011
|
+
// Bare defined-name identifier (no punctuation, no !).
|
|
2012
|
+
if (IDENTIFIER_RE.test(body)) {
|
|
2013
|
+
return { kind: "name", name: body };
|
|
2014
|
+
}
|
|
2015
|
+
// Sheet-qualified form `Sheet!xxx`. Only classify as a defined-
|
|
2016
|
+
// name reference when the RHS is a pure identifier — structured
|
|
2017
|
+
// refs like `Table[Col]` or cell ranges like `$A$1:$B$10` route
|
|
2018
|
+
// through the generic "special" bucket so the defined-name check
|
|
2019
|
+
// skips them.
|
|
2020
|
+
const bangIdx = body.indexOf("!");
|
|
2021
|
+
if (bangIdx >= 0) {
|
|
2022
|
+
const sheet = body.substring(0, bangIdx);
|
|
2023
|
+
const ref = body.substring(bangIdx + 1);
|
|
2024
|
+
const sheetValid = (sheet.startsWith("'") && sheet.endsWith("'") && sheet.length >= 3) ||
|
|
2025
|
+
IDENTIFIER_RE.test(sheet);
|
|
2026
|
+
if (!sheetValid || ref === "") {
|
|
2027
|
+
return { kind: "invalid" };
|
|
2028
|
+
}
|
|
2029
|
+
if (A1_REF_RE.test(ref)) {
|
|
2030
|
+
return { kind: ref.includes(":") ? "range" : "cell" };
|
|
2031
|
+
}
|
|
2032
|
+
// Reserved `_xl*` names also appear sheet-qualified
|
|
2033
|
+
// (`Sheet1!_xlchart.v1.0`). Treat them as special regardless of
|
|
2034
|
+
// which side of the `!` they sit on.
|
|
2035
|
+
if (ref.startsWith("_xl")) {
|
|
2036
|
+
return { kind: "special" };
|
|
2037
|
+
}
|
|
2038
|
+
if (IDENTIFIER_RE.test(ref)) {
|
|
2039
|
+
return { kind: "name", name: ref };
|
|
2040
|
+
}
|
|
2041
|
+
// Anything else (structured table ref, function call, complex
|
|
2042
|
+
// expression) — assume valid, skip further validation.
|
|
2043
|
+
return { kind: "special" };
|
|
2044
|
+
}
|
|
2045
|
+
// Unqualified and not a bare identifier: could be a structured
|
|
2046
|
+
// reference (`Table[Col]`, `Table[#All]`) or an expression. Both
|
|
2047
|
+
// legal in `<c:f>`; accept without further validation.
|
|
2048
|
+
return { kind: "special" };
|
|
2049
|
+
}
|
|
2050
|
+
function extractTextContent(el) {
|
|
2051
|
+
let out = "";
|
|
2052
|
+
for (const child of el.children) {
|
|
2053
|
+
if (child.type === "text" || child.type === "cdata") {
|
|
2054
|
+
out += child.value;
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
return out;
|
|
2058
|
+
}
|
|
2059
|
+
function checkFormulaSyntax(ctx, path, root) {
|
|
2060
|
+
for (const f of collectDescendantsLocal(root, "f")) {
|
|
2061
|
+
if (ctx.reporter.capped) {
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
const body = extractTextContent(f);
|
|
2065
|
+
const parsed = parseCFBody(body);
|
|
2066
|
+
if (parsed.kind === "invalid") {
|
|
2067
|
+
ctx.reporter.error("chart-f-invalid-syntax", `${path}: <c:f>${body}</c:f> is not a valid cell reference, range, or defined name.`, path);
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
/**
|
|
2072
|
+
* Collect every defined-name declared in `xl/workbook.xml`, plus the
|
|
2073
|
+
* well-known built-in reserved names that don't appear in the file
|
|
2074
|
+
* but Excel always recognises (`_xlnm.*` family).
|
|
2075
|
+
*/
|
|
2076
|
+
function collectWorkbookDefinedNames(ctx) {
|
|
2077
|
+
const names = new Set();
|
|
2078
|
+
// Built-in reserved names Excel treats as always-defined.
|
|
2079
|
+
names.add("_xlnm.Print_Area");
|
|
2080
|
+
names.add("_xlnm.Print_Titles");
|
|
2081
|
+
names.add("_xlnm.Database");
|
|
2082
|
+
names.add("_xlnm.Criteria");
|
|
2083
|
+
names.add("_xlnm.Extract");
|
|
2084
|
+
names.add("_xlnm.Sheet_Title");
|
|
2085
|
+
names.add("_xlnm._FilterDatabase");
|
|
2086
|
+
names.add("_xlnm.Auto_Open");
|
|
2087
|
+
names.add("_xlnm.Auto_Close");
|
|
2088
|
+
const dom = ctx.readDom("xl/workbook.xml");
|
|
2089
|
+
if (!dom) {
|
|
2090
|
+
return names;
|
|
2091
|
+
}
|
|
2092
|
+
for (const dn of collectDescendantsLocal(dom.root, "definedName")) {
|
|
2093
|
+
const name = attrByLocalName(dn, "name");
|
|
2094
|
+
if (name) {
|
|
2095
|
+
names.add(name);
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
return names;
|
|
2099
|
+
}
|
|
2100
|
+
function checkDefinedNameResolution(ctx, path, root) {
|
|
2101
|
+
let names;
|
|
2102
|
+
for (const f of collectDescendantsLocal(root, "f")) {
|
|
2103
|
+
if (ctx.reporter.capped) {
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
const body = extractTextContent(f);
|
|
2107
|
+
const parsed = parseCFBody(body);
|
|
2108
|
+
if (parsed.kind !== "name" || !parsed.name) {
|
|
2109
|
+
continue;
|
|
2110
|
+
}
|
|
2111
|
+
// Lazily load the workbook's defined names on first need.
|
|
2112
|
+
if (!names) {
|
|
2113
|
+
names = collectWorkbookDefinedNames(ctx);
|
|
2114
|
+
}
|
|
2115
|
+
if (!names.has(parsed.name)) {
|
|
2116
|
+
ctx.reporter.error("chart-f-undefined-name", `${path}: <c:f>${body}</c:f> references defined name "${parsed.name}" but no matching <definedName> exists in xl/workbook.xml.`, path);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
function chartRelsPath(chartPath) {
|
|
2121
|
+
const slash = chartPath.lastIndexOf("/");
|
|
2122
|
+
const dir = slash >= 0 ? chartPath.slice(0, slash) : "";
|
|
2123
|
+
const name = slash >= 0 ? chartPath.slice(slash + 1) : chartPath;
|
|
2124
|
+
return dir ? `${dir}/_rels/${name}.rels` : `_rels/${name}.rels`;
|
|
2125
|
+
}
|