@rowsncolumns/toolkit 24.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +100 -0
- package/Readme.md +27 -0
- package/dist/cjs/autofilter-condition.d.ts +43 -0
- package/dist/cjs/autofilter-condition.d.ts.map +1 -0
- package/dist/cjs/autofilter-condition.js +155 -0
- package/dist/cjs/csv-export.d.ts +49 -0
- package/dist/cjs/csv-export.d.ts.map +1 -0
- package/dist/cjs/csv-export.js +142 -0
- package/dist/cjs/csv-import-worker.d.ts +16 -0
- package/dist/cjs/csv-import-worker.d.ts.map +1 -0
- package/dist/cjs/csv-import-worker.js +60 -0
- package/dist/cjs/csv-import.d.ts +38 -0
- package/dist/cjs/csv-import.d.ts.map +1 -0
- package/dist/cjs/csv-import.js +166 -0
- package/dist/cjs/csv-parser/helpers.d.ts +14 -0
- package/dist/cjs/csv-parser/helpers.d.ts.map +1 -0
- package/dist/cjs/csv-parser/helpers.js +145 -0
- package/dist/cjs/csv-parser/types.d.ts +66 -0
- package/dist/cjs/csv-parser/types.d.ts.map +1 -0
- package/dist/cjs/csv-parser/types.js +14 -0
- package/dist/cjs/csv-parser/worker.d.ts +2 -0
- package/dist/cjs/csv-parser/worker.d.ts.map +1 -0
- package/dist/cjs/csv-parser/worker.js +200 -0
- package/dist/cjs/excel-export.d.ts +11 -0
- package/dist/cjs/excel-export.d.ts.map +1 -0
- package/dist/cjs/excel-export.js +22 -0
- package/dist/cjs/excel-import-session.d.ts +51 -0
- package/dist/cjs/excel-import-session.d.ts.map +1 -0
- package/dist/cjs/excel-import-session.js +307 -0
- package/dist/cjs/excel-import-worker.d.ts +7 -0
- package/dist/cjs/excel-import-worker.d.ts.map +1 -0
- package/dist/cjs/excel-import-worker.js +119 -0
- package/dist/cjs/excel-parser/builtin-numfmts.d.ts +24 -0
- package/dist/cjs/excel-parser/builtin-numfmts.d.ts.map +1 -0
- package/dist/cjs/excel-parser/builtin-numfmts.js +63 -0
- package/dist/cjs/excel-parser/cfb-decryptor.d.ts +53 -0
- package/dist/cjs/excel-parser/cfb-decryptor.d.ts.map +1 -0
- package/dist/cjs/excel-parser/cfb-decryptor.js +553 -0
- package/dist/cjs/excel-parser/cfb-encryptor.d.ts +23 -0
- package/dist/cjs/excel-parser/cfb-encryptor.d.ts.map +1 -0
- package/dist/cjs/excel-parser/cfb-encryptor.js +200 -0
- package/dist/cjs/excel-parser/dv-cell-stamp.d.ts +29 -0
- package/dist/cjs/excel-parser/dv-cell-stamp.d.ts.map +1 -0
- package/dist/cjs/excel-parser/dv-cell-stamp.js +239 -0
- package/dist/cjs/excel-parser/helpers.d.ts +28 -0
- package/dist/cjs/excel-parser/helpers.d.ts.map +1 -0
- package/dist/cjs/excel-parser/helpers.js +188 -0
- package/dist/cjs/excel-parser/interface.d.ts +179 -0
- package/dist/cjs/excel-parser/interface.d.ts.map +1 -0
- package/dist/cjs/excel-parser/interface.js +1 -0
- package/dist/cjs/excel-parser/plugins/base/index.d.ts +31 -0
- package/dist/cjs/excel-parser/plugins/base/index.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/base/index.js +101 -0
- package/dist/cjs/excel-parser/plugins/ods/conditional-formatting.d.ts +16 -0
- package/dist/cjs/excel-parser/plugins/ods/conditional-formatting.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/ods/conditional-formatting.js +488 -0
- package/dist/cjs/excel-parser/plugins/ods/data-validations.d.ts +15 -0
- package/dist/cjs/excel-parser/plugins/ods/data-validations.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/ods/data-validations.js +353 -0
- package/dist/cjs/excel-parser/plugins/ods/formula.d.ts +8 -0
- package/dist/cjs/excel-parser/plugins/ods/formula.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/ods/formula.js +82 -0
- package/dist/cjs/excel-parser/plugins/ods/index.d.ts +122 -0
- package/dist/cjs/excel-parser/plugins/ods/index.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/ods/index.js +3642 -0
- package/dist/cjs/excel-parser/plugins/ods/sparklines.d.ts +25 -0
- package/dist/cjs/excel-parser/plugins/ods/sparklines.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/ods/sparklines.js +221 -0
- package/dist/cjs/excel-parser/plugins/ods/styles.d.ts +60 -0
- package/dist/cjs/excel-parser/plugins/ods/styles.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/ods/styles.js +808 -0
- package/dist/cjs/excel-parser/plugins/ods/types.d.ts +7 -0
- package/dist/cjs/excel-parser/plugins/ods/types.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/ods/types.js +1 -0
- package/dist/cjs/excel-parser/plugins/xls/index.d.ts +5 -0
- package/dist/cjs/excel-parser/plugins/xls/index.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xls/index.js +19 -0
- package/dist/cjs/excel-parser/plugins/xlsb/biff-record-reader.d.ts +13 -0
- package/dist/cjs/excel-parser/plugins/xlsb/biff-record-reader.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsb/biff-record-reader.js +49 -0
- package/dist/cjs/excel-parser/plugins/xlsb/index.d.ts +117 -0
- package/dist/cjs/excel-parser/plugins/xlsb/index.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsb/index.js +2972 -0
- package/dist/cjs/excel-parser/plugins/xlsb/rgce-parser.d.ts +18 -0
- package/dist/cjs/excel-parser/plugins/xlsb/rgce-parser.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsb/rgce-parser.js +527 -0
- package/dist/cjs/excel-parser/plugins/xlsm/index.d.ts +5 -0
- package/dist/cjs/excel-parser/plugins/xlsm/index.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsm/index.js +10 -0
- package/dist/cjs/excel-parser/plugins/xlsx/charts.d.ts +60 -0
- package/dist/cjs/excel-parser/plugins/xlsx/charts.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/charts.js +2178 -0
- package/dist/cjs/excel-parser/plugins/xlsx/conditional-formatting.d.ts +18 -0
- package/dist/cjs/excel-parser/plugins/xlsx/conditional-formatting.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/conditional-formatting.js +620 -0
- package/dist/cjs/excel-parser/plugins/xlsx/data-validations.d.ts +4 -0
- package/dist/cjs/excel-parser/plugins/xlsx/data-validations.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/data-validations.js +515 -0
- package/dist/cjs/excel-parser/plugins/xlsx/drawing.d.ts +39 -0
- package/dist/cjs/excel-parser/plugins/xlsx/drawing.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/drawing.js +941 -0
- package/dist/cjs/excel-parser/plugins/xlsx/index.d.ts +320 -0
- package/dist/cjs/excel-parser/plugins/xlsx/index.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/index.js +6056 -0
- package/dist/cjs/excel-parser/plugins/xlsx/numfmt.d.ts +2 -0
- package/dist/cjs/excel-parser/plugins/xlsx/numfmt.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/numfmt.js +3 -0
- package/dist/cjs/excel-parser/plugins/xlsx/pivots.d.ts +40 -0
- package/dist/cjs/excel-parser/plugins/xlsx/pivots.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/pivots.js +342 -0
- package/dist/cjs/excel-parser/plugins/xlsx/relationships.d.ts +8 -0
- package/dist/cjs/excel-parser/plugins/xlsx/relationships.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/relationships.js +26 -0
- package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.d.ts +26 -0
- package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.js +59 -0
- package/dist/cjs/excel-parser/plugins/xlsx/styles.d.ts +44 -0
- package/dist/cjs/excel-parser/plugins/xlsx/styles.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/styles.js +1377 -0
- package/dist/cjs/excel-parser/plugins/xlsx/theme.d.ts +50 -0
- package/dist/cjs/excel-parser/plugins/xlsx/theme.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/theme.js +150 -0
- package/dist/cjs/excel-parser/plugins/xlsx/threaded-comments.d.ts +47 -0
- package/dist/cjs/excel-parser/plugins/xlsx/threaded-comments.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/threaded-comments.js +141 -0
- package/dist/cjs/excel-parser/plugins/xlsx/value-type-detection.d.ts +44 -0
- package/dist/cjs/excel-parser/plugins/xlsx/value-type-detection.d.ts.map +1 -0
- package/dist/cjs/excel-parser/plugins/xlsx/value-type-detection.js +407 -0
- package/dist/cjs/excel-parser/registry.d.ts +5 -0
- package/dist/cjs/excel-parser/registry.d.ts.map +1 -0
- package/dist/cjs/excel-parser/registry.js +21 -0
- package/dist/cjs/excel-parser/style-registry.d.ts +51 -0
- package/dist/cjs/excel-parser/style-registry.d.ts.map +1 -0
- package/dist/cjs/excel-parser/style-registry.js +175 -0
- package/dist/cjs/excel-parser/theme-default.d.ts +3 -0
- package/dist/cjs/excel-parser/theme-default.d.ts.map +1 -0
- package/dist/cjs/excel-parser/theme-default.js +19 -0
- package/dist/cjs/excel-parser/types.d.ts +365 -0
- package/dist/cjs/excel-parser/types.d.ts.map +1 -0
- package/dist/cjs/excel-parser/types.js +59 -0
- package/dist/cjs/excel-parser/utils.d.ts +6 -0
- package/dist/cjs/excel-parser/utils.d.ts.map +1 -0
- package/dist/cjs/excel-parser/utils.js +30 -0
- package/dist/cjs/excel-parser/workbook.d.ts +51 -0
- package/dist/cjs/excel-parser/workbook.d.ts.map +1 -0
- package/dist/cjs/excel-parser/workbook.js +317 -0
- package/dist/cjs/excel-parser/worker.d.ts +2 -0
- package/dist/cjs/excel-parser/worker.d.ts.map +1 -0
- package/dist/cjs/excel-parser/worker.js +1149 -0
- package/dist/cjs/excel-parser/zip-parser-streaming.d.ts +13 -0
- package/dist/cjs/excel-parser/zip-parser-streaming.d.ts.map +1 -0
- package/dist/cjs/excel-parser/zip-parser-streaming.js +200 -0
- package/dist/cjs/excel-parser/zip-parser.d.ts +11 -0
- package/dist/cjs/excel-parser/zip-parser.d.ts.map +1 -0
- package/dist/cjs/excel-parser/zip-parser.js +49 -0
- package/dist/cjs/exporter/invalid-data-validation.d.ts +19 -0
- package/dist/cjs/exporter/invalid-data-validation.d.ts.map +1 -0
- package/dist/cjs/exporter/invalid-data-validation.js +39 -0
- package/dist/cjs/exporter/ods/ods-export.d.ts +26 -0
- package/dist/cjs/exporter/ods/ods-export.d.ts.map +1 -0
- package/dist/cjs/exporter/ods/ods-export.js +3714 -0
- package/dist/cjs/exporter/ods/sparkline-builder.d.ts +33 -0
- package/dist/cjs/exporter/ods/sparkline-builder.d.ts.map +1 -0
- package/dist/cjs/exporter/ods/sparkline-builder.js +221 -0
- package/dist/cjs/exporter/xlsx/chart-builder.d.ts +3 -0
- package/dist/cjs/exporter/xlsx/chart-builder.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/chart-builder.js +1901 -0
- package/dist/cjs/exporter/xlsx/chart-templates.d.ts +1781 -0
- package/dist/cjs/exporter/xlsx/chart-templates.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/chart-templates.js +1798 -0
- package/dist/cjs/exporter/xlsx/future-functions.d.ts +10 -0
- package/dist/cjs/exporter/xlsx/future-functions.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/future-functions.js +264 -0
- package/dist/cjs/exporter/xlsx/pivot-builder.d.ts +58 -0
- package/dist/cjs/exporter/xlsx/pivot-builder.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/pivot-builder.js +277 -0
- package/dist/cjs/exporter/xlsx/power-query-builder.d.ts +26 -0
- package/dist/cjs/exporter/xlsx/power-query-builder.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/power-query-builder.js +306 -0
- package/dist/cjs/exporter/xlsx/slicer-builder.d.ts +11 -0
- package/dist/cjs/exporter/xlsx/slicer-builder.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/slicer-builder.js +87 -0
- package/dist/cjs/exporter/xlsx/sparkline-builder.d.ts +61 -0
- package/dist/cjs/exporter/xlsx/sparkline-builder.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/sparkline-builder.js +489 -0
- package/dist/cjs/exporter/xlsx/style-builder.d.ts +93 -0
- package/dist/cjs/exporter/xlsx/style-builder.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/style-builder.js +813 -0
- package/dist/cjs/exporter/xlsx/threaded-comments-builder.d.ts +58 -0
- package/dist/cjs/exporter/xlsx/threaded-comments-builder.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/threaded-comments-builder.js +189 -0
- package/dist/cjs/exporter/xlsx/xlsx-export.d.ts +121 -0
- package/dist/cjs/exporter/xlsx/xlsx-export.d.ts.map +1 -0
- package/dist/cjs/exporter/xlsx/xlsx-export.js +4596 -0
- package/dist/cjs/helpers/theme-xml-generator.d.ts +8 -0
- package/dist/cjs/helpers/theme-xml-generator.d.ts.map +1 -0
- package/dist/cjs/helpers/theme-xml-generator.js +33 -0
- package/dist/cjs/helpers.d.ts +34 -0
- package/dist/cjs/helpers.d.ts.map +1 -0
- package/dist/cjs/helpers.js +189 -0
- package/dist/cjs/index.d.ts +16 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +15 -0
- package/dist/cjs/indexed-colors.d.ts +2 -0
- package/dist/cjs/indexed-colors.d.ts.map +1 -0
- package/dist/cjs/indexed-colors.js +75 -0
- package/dist/cjs/ods-export.d.ts +24 -0
- package/dist/cjs/ods-export.d.ts.map +1 -0
- package/dist/cjs/ods-export.js +23 -0
- package/dist/cjs/parse.d.ts +24 -0
- package/dist/cjs/parse.d.ts.map +1 -0
- package/dist/cjs/parse.js +68 -0
- package/dist/cjs/power-query.d.ts +41 -0
- package/dist/cjs/power-query.d.ts.map +1 -0
- package/dist/cjs/power-query.js +22 -0
- package/dist/cjs/server.d.ts +9 -0
- package/dist/cjs/server.d.ts.map +1 -0
- package/dist/cjs/server.js +8 -0
- package/dist/cjs/theme1.d.ts +3 -0
- package/dist/cjs/theme1.d.ts.map +1 -0
- package/dist/cjs/theme1.js +2 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/cjs/types.d.ts +14 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +14 -0
- package/dist/cjs/vitest.config.d.ts +3 -0
- package/dist/cjs/vitest.config.d.ts.map +1 -0
- package/dist/cjs/vitest.config.js +16 -0
- package/dist/esm/autofilter-condition.d.ts +43 -0
- package/dist/esm/autofilter-condition.d.ts.map +1 -0
- package/dist/esm/autofilter-condition.js +145 -0
- package/dist/esm/csv-export.d.ts +49 -0
- package/dist/esm/csv-export.d.ts.map +1 -0
- package/dist/esm/csv-export.js +126 -0
- package/dist/esm/csv-import-worker.d.ts +16 -0
- package/dist/esm/csv-import-worker.d.ts.map +1 -0
- package/dist/esm/csv-import-worker.js +60 -0
- package/dist/esm/csv-import.d.ts +38 -0
- package/dist/esm/csv-import.d.ts.map +1 -0
- package/dist/esm/csv-import.js +132 -0
- package/dist/esm/csv-parser/helpers.d.ts +14 -0
- package/dist/esm/csv-parser/helpers.d.ts.map +1 -0
- package/dist/esm/csv-parser/helpers.js +0 -0
- package/dist/esm/csv-parser/types.d.ts +66 -0
- package/dist/esm/csv-parser/types.d.ts.map +1 -0
- package/dist/esm/csv-parser/types.js +14 -0
- package/dist/esm/csv-parser/worker.d.ts +2 -0
- package/dist/esm/csv-parser/worker.d.ts.map +1 -0
- package/dist/esm/csv-parser/worker.js +154 -0
- package/dist/esm/excel-export.d.ts +11 -0
- package/dist/esm/excel-export.d.ts.map +1 -0
- package/dist/esm/excel-export.js +13 -0
- package/dist/esm/excel-import-session.d.ts +51 -0
- package/dist/esm/excel-import-session.d.ts.map +1 -0
- package/dist/esm/excel-import-session.js +255 -0
- package/dist/esm/excel-import-worker.d.ts +7 -0
- package/dist/esm/excel-import-worker.d.ts.map +1 -0
- package/dist/esm/excel-import-worker.js +116 -0
- package/dist/esm/excel-parser/builtin-numfmts.d.ts +24 -0
- package/dist/esm/excel-parser/builtin-numfmts.d.ts.map +1 -0
- package/dist/esm/excel-parser/builtin-numfmts.js +59 -0
- package/dist/esm/excel-parser/cfb-decryptor.d.ts +53 -0
- package/dist/esm/excel-parser/cfb-decryptor.d.ts.map +1 -0
- package/dist/esm/excel-parser/cfb-decryptor.js +493 -0
- package/dist/esm/excel-parser/cfb-encryptor.d.ts +23 -0
- package/dist/esm/excel-parser/cfb-encryptor.d.ts.map +1 -0
- package/dist/esm/excel-parser/cfb-encryptor.js +181 -0
- package/dist/esm/excel-parser/dv-cell-stamp.d.ts +29 -0
- package/dist/esm/excel-parser/dv-cell-stamp.d.ts.map +1 -0
- package/dist/esm/excel-parser/dv-cell-stamp.js +155 -0
- package/dist/esm/excel-parser/helpers.d.ts +28 -0
- package/dist/esm/excel-parser/helpers.d.ts.map +1 -0
- package/dist/esm/excel-parser/helpers.js +160 -0
- package/dist/esm/excel-parser/interface.d.ts +179 -0
- package/dist/esm/excel-parser/interface.d.ts.map +1 -0
- package/dist/esm/excel-parser/interface.js +1 -0
- package/dist/esm/excel-parser/plugins/base/index.d.ts +31 -0
- package/dist/esm/excel-parser/plugins/base/index.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/base/index.js +64 -0
- package/dist/esm/excel-parser/plugins/ods/conditional-formatting.d.ts +16 -0
- package/dist/esm/excel-parser/plugins/ods/conditional-formatting.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/ods/conditional-formatting.js +405 -0
- package/dist/esm/excel-parser/plugins/ods/data-validations.d.ts +15 -0
- package/dist/esm/excel-parser/plugins/ods/data-validations.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/ods/data-validations.js +331 -0
- package/dist/esm/excel-parser/plugins/ods/formula.d.ts +8 -0
- package/dist/esm/excel-parser/plugins/ods/formula.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/ods/formula.js +82 -0
- package/dist/esm/excel-parser/plugins/ods/index.d.ts +122 -0
- package/dist/esm/excel-parser/plugins/ods/index.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/ods/index.js +2896 -0
- package/dist/esm/excel-parser/plugins/ods/sparklines.d.ts +25 -0
- package/dist/esm/excel-parser/plugins/ods/sparklines.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/ods/sparklines.js +188 -0
- package/dist/esm/excel-parser/plugins/ods/styles.d.ts +60 -0
- package/dist/esm/excel-parser/plugins/ods/styles.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/ods/styles.js +732 -0
- package/dist/esm/excel-parser/plugins/ods/types.d.ts +7 -0
- package/dist/esm/excel-parser/plugins/ods/types.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/ods/types.js +1 -0
- package/dist/esm/excel-parser/plugins/xls/index.d.ts +5 -0
- package/dist/esm/excel-parser/plugins/xls/index.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xls/index.js +7 -0
- package/dist/esm/excel-parser/plugins/xlsb/biff-record-reader.d.ts +13 -0
- package/dist/esm/excel-parser/plugins/xlsb/biff-record-reader.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsb/biff-record-reader.js +48 -0
- package/dist/esm/excel-parser/plugins/xlsb/index.d.ts +117 -0
- package/dist/esm/excel-parser/plugins/xlsb/index.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsb/index.js +2339 -0
- package/dist/esm/excel-parser/plugins/xlsb/rgce-parser.d.ts +18 -0
- package/dist/esm/excel-parser/plugins/xlsb/rgce-parser.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsb/rgce-parser.js +522 -0
- package/dist/esm/excel-parser/plugins/xlsm/index.d.ts +5 -0
- package/dist/esm/excel-parser/plugins/xlsm/index.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsm/index.js +6 -0
- package/dist/esm/excel-parser/plugins/xlsx/charts.d.ts +60 -0
- package/dist/esm/excel-parser/plugins/xlsx/charts.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/charts.js +2036 -0
- package/dist/esm/excel-parser/plugins/xlsx/conditional-formatting.d.ts +18 -0
- package/dist/esm/excel-parser/plugins/xlsx/conditional-formatting.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/conditional-formatting.js +621 -0
- package/dist/esm/excel-parser/plugins/xlsx/data-validations.d.ts +4 -0
- package/dist/esm/excel-parser/plugins/xlsx/data-validations.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/data-validations.js +493 -0
- package/dist/esm/excel-parser/plugins/xlsx/drawing.d.ts +39 -0
- package/dist/esm/excel-parser/plugins/xlsx/drawing.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/drawing.js +905 -0
- package/dist/esm/excel-parser/plugins/xlsx/index.d.ts +320 -0
- package/dist/esm/excel-parser/plugins/xlsx/index.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/index.js +4654 -0
- package/dist/esm/excel-parser/plugins/xlsx/numfmt.d.ts +2 -0
- package/dist/esm/excel-parser/plugins/xlsx/numfmt.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/numfmt.js +3 -0
- package/dist/esm/excel-parser/plugins/xlsx/pivots.d.ts +40 -0
- package/dist/esm/excel-parser/plugins/xlsx/pivots.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/pivots.js +254 -0
- package/dist/esm/excel-parser/plugins/xlsx/relationships.d.ts +8 -0
- package/dist/esm/excel-parser/plugins/xlsx/relationships.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/relationships.js +13 -0
- package/dist/esm/excel-parser/plugins/xlsx/sheet-views.d.ts +26 -0
- package/dist/esm/excel-parser/plugins/xlsx/sheet-views.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/sheet-views.js +64 -0
- package/dist/esm/excel-parser/plugins/xlsx/styles.d.ts +44 -0
- package/dist/esm/excel-parser/plugins/xlsx/styles.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/styles.js +1365 -0
- package/dist/esm/excel-parser/plugins/xlsx/theme.d.ts +50 -0
- package/dist/esm/excel-parser/plugins/xlsx/theme.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/theme.js +127 -0
- package/dist/esm/excel-parser/plugins/xlsx/threaded-comments.d.ts +47 -0
- package/dist/esm/excel-parser/plugins/xlsx/threaded-comments.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/threaded-comments.js +103 -0
- package/dist/esm/excel-parser/plugins/xlsx/value-type-detection.d.ts +44 -0
- package/dist/esm/excel-parser/plugins/xlsx/value-type-detection.d.ts.map +1 -0
- package/dist/esm/excel-parser/plugins/xlsx/value-type-detection.js +399 -0
- package/dist/esm/excel-parser/registry.d.ts +5 -0
- package/dist/esm/excel-parser/registry.d.ts.map +1 -0
- package/dist/esm/excel-parser/registry.js +20 -0
- package/dist/esm/excel-parser/style-registry.d.ts +51 -0
- package/dist/esm/excel-parser/style-registry.d.ts.map +1 -0
- package/dist/esm/excel-parser/style-registry.js +163 -0
- package/dist/esm/excel-parser/theme-default.d.ts +3 -0
- package/dist/esm/excel-parser/theme-default.d.ts.map +1 -0
- package/dist/esm/excel-parser/theme-default.js +19 -0
- package/dist/esm/excel-parser/types.d.ts +365 -0
- package/dist/esm/excel-parser/types.d.ts.map +1 -0
- package/dist/esm/excel-parser/types.js +59 -0
- package/dist/esm/excel-parser/utils.d.ts +6 -0
- package/dist/esm/excel-parser/utils.d.ts.map +1 -0
- package/dist/esm/excel-parser/utils.js +25 -0
- package/dist/esm/excel-parser/workbook.d.ts +51 -0
- package/dist/esm/excel-parser/workbook.d.ts.map +1 -0
- package/dist/esm/excel-parser/workbook.js +192 -0
- package/dist/esm/excel-parser/worker.d.ts +2 -0
- package/dist/esm/excel-parser/worker.d.ts.map +1 -0
- package/dist/esm/excel-parser/worker.js +862 -0
- package/dist/esm/excel-parser/zip-parser-streaming.d.ts +13 -0
- package/dist/esm/excel-parser/zip-parser-streaming.d.ts.map +1 -0
- package/dist/esm/excel-parser/zip-parser-streaming.js +119 -0
- package/dist/esm/excel-parser/zip-parser.d.ts +11 -0
- package/dist/esm/excel-parser/zip-parser.d.ts.map +1 -0
- package/dist/esm/excel-parser/zip-parser.js +43 -0
- package/dist/esm/exporter/invalid-data-validation.d.ts +19 -0
- package/dist/esm/exporter/invalid-data-validation.d.ts.map +1 -0
- package/dist/esm/exporter/invalid-data-validation.js +37 -0
- package/dist/esm/exporter/ods/ods-export.d.ts +26 -0
- package/dist/esm/exporter/ods/ods-export.d.ts.map +1 -0
- package/dist/esm/exporter/ods/ods-export.js +3238 -0
- package/dist/esm/exporter/ods/sparkline-builder.d.ts +33 -0
- package/dist/esm/exporter/ods/sparkline-builder.d.ts.map +1 -0
- package/dist/esm/exporter/ods/sparkline-builder.js +165 -0
- package/dist/esm/exporter/xlsx/chart-builder.d.ts +3 -0
- package/dist/esm/exporter/xlsx/chart-builder.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/chart-builder.js +1805 -0
- package/dist/esm/exporter/xlsx/chart-templates.d.ts +1781 -0
- package/dist/esm/exporter/xlsx/chart-templates.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/chart-templates.js +1798 -0
- package/dist/esm/exporter/xlsx/future-functions.d.ts +10 -0
- package/dist/esm/exporter/xlsx/future-functions.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/future-functions.js +264 -0
- package/dist/esm/exporter/xlsx/pivot-builder.d.ts +58 -0
- package/dist/esm/exporter/xlsx/pivot-builder.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/pivot-builder.js +230 -0
- package/dist/esm/exporter/xlsx/power-query-builder.d.ts +26 -0
- package/dist/esm/exporter/xlsx/power-query-builder.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/power-query-builder.js +257 -0
- package/dist/esm/exporter/xlsx/slicer-builder.d.ts +11 -0
- package/dist/esm/exporter/xlsx/slicer-builder.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/slicer-builder.js +84 -0
- package/dist/esm/exporter/xlsx/sparkline-builder.d.ts +61 -0
- package/dist/esm/exporter/xlsx/sparkline-builder.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/sparkline-builder.js +437 -0
- package/dist/esm/exporter/xlsx/style-builder.d.ts +93 -0
- package/dist/esm/exporter/xlsx/style-builder.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/style-builder.js +795 -0
- package/dist/esm/exporter/xlsx/threaded-comments-builder.d.ts +58 -0
- package/dist/esm/exporter/xlsx/threaded-comments-builder.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/threaded-comments-builder.js +175 -0
- package/dist/esm/exporter/xlsx/xlsx-export.d.ts +121 -0
- package/dist/esm/exporter/xlsx/xlsx-export.d.ts.map +1 -0
- package/dist/esm/exporter/xlsx/xlsx-export.js +4062 -0
- package/dist/esm/helpers/theme-xml-generator.d.ts +8 -0
- package/dist/esm/helpers/theme-xml-generator.d.ts.map +1 -0
- package/dist/esm/helpers/theme-xml-generator.js +33 -0
- package/dist/esm/helpers.d.ts +34 -0
- package/dist/esm/helpers.d.ts.map +1 -0
- package/dist/esm/helpers.js +182 -0
- package/dist/esm/index.d.ts +16 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +15 -0
- package/dist/esm/indexed-colors.d.ts +2 -0
- package/dist/esm/indexed-colors.d.ts.map +1 -0
- package/dist/esm/indexed-colors.js +75 -0
- package/dist/esm/ods-export.d.ts +24 -0
- package/dist/esm/ods-export.d.ts.map +1 -0
- package/dist/esm/ods-export.js +12 -0
- package/dist/esm/parse.d.ts +24 -0
- package/dist/esm/parse.d.ts.map +1 -0
- package/dist/esm/parse.js +55 -0
- package/dist/esm/power-query.d.ts +41 -0
- package/dist/esm/power-query.d.ts.map +1 -0
- package/dist/esm/power-query.js +19 -0
- package/dist/esm/server.d.ts +9 -0
- package/dist/esm/server.d.ts.map +1 -0
- package/dist/esm/server.js +8 -0
- package/dist/esm/theme1.d.ts +3 -0
- package/dist/esm/theme1.d.ts.map +1 -0
- package/dist/esm/theme1.js +2 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -0
- package/dist/esm/types.d.ts +14 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +14 -0
- package/dist/esm/vitest.config.d.ts +3 -0
- package/dist/esm/vitest.config.d.ts.map +1 -0
- package/dist/esm/vitest.config.js +16 -0
- package/package.json +62 -0
- package/xlsx.d.ts +52 -0
|
@@ -0,0 +1,2339 @@
|
|
|
1
|
+
import { getCellXfsKey, ssfFormat, uuid } from "@rowsncolumns/utils";
|
|
2
|
+
import { IndexedColors } from "@rowsncolumns/utils";
|
|
3
|
+
import { XMLParser } from "fast-xml-parser";
|
|
4
|
+
import { get } from "lodash-es";
|
|
5
|
+
import { parseRgce } from "./rgce-parser";
|
|
6
|
+
import { StreamingZipParser } from "../../zip-parser-streaming";
|
|
7
|
+
import { createFilterSpecsFromAutoFilter, excelColumnWidthToPixels, getRangeFromAddressRef, } from "../../helpers";
|
|
8
|
+
import { defaultSpreadsheetTheme } from "../../theme-default";
|
|
9
|
+
import { BaseWorkbookPlugin } from "../base/index";
|
|
10
|
+
import { processChart } from "../xlsx/charts";
|
|
11
|
+
import { processAnchors } from "../xlsx/drawing";
|
|
12
|
+
import { readAllRecordHeaders } from "./biff-record-reader";
|
|
13
|
+
import { standardExcelFormatCodeById } from "../xlsx/numfmt";
|
|
14
|
+
import { extractThemeColors, extractThemeFonts, generateDarkThemeColors, } from "../xlsx/theme";
|
|
15
|
+
const RT = {
|
|
16
|
+
BrtCellRk: 0x0002,
|
|
17
|
+
BrtCellBlank: 0x0001,
|
|
18
|
+
BrtCellError: 0x0003,
|
|
19
|
+
BrtCellBool: 0x0004,
|
|
20
|
+
BrtCellReal: 0x0005,
|
|
21
|
+
BrtCellSt: 0x0006,
|
|
22
|
+
BrtCellIsst: 0x0007,
|
|
23
|
+
BrtFmlaString: 0x0008,
|
|
24
|
+
BrtFmlaNum: 0x0009,
|
|
25
|
+
BrtFmlaBool: 0x000a,
|
|
26
|
+
BrtFmlaError: 0x000b,
|
|
27
|
+
BrtShortBlank: 0x000c,
|
|
28
|
+
BrtShortRk: 0x000d,
|
|
29
|
+
BrtShortError: 0x000e,
|
|
30
|
+
BrtShortBool: 0x000f,
|
|
31
|
+
BrtShortReal: 0x0010,
|
|
32
|
+
BrtShortSt: 0x0011,
|
|
33
|
+
BrtShortIsst: 0x0012,
|
|
34
|
+
BrtSSTItem: 0x0013,
|
|
35
|
+
BrtRowHdr: 0x0000,
|
|
36
|
+
BrtMergeCell: 0x00b0,
|
|
37
|
+
BrtFmt: 0x002c,
|
|
38
|
+
BrtFont: 0x002b,
|
|
39
|
+
BrtFill: 0x002d,
|
|
40
|
+
BrtBorder: 0x002e,
|
|
41
|
+
BrtXF: 0x002f,
|
|
42
|
+
BrtColInfo: 0x003c,
|
|
43
|
+
BrtBeginFonts: 0x0263,
|
|
44
|
+
BrtEndFonts: 0x0264,
|
|
45
|
+
BrtBeginFills: 0x025b,
|
|
46
|
+
BrtEndFills: 0x025c,
|
|
47
|
+
BrtBeginBorders: 0x0265,
|
|
48
|
+
BrtEndBorders: 0x0266,
|
|
49
|
+
BrtBeginFmts: 0x0267,
|
|
50
|
+
BrtEndFmts: 0x0268,
|
|
51
|
+
BrtBeginCellStyleXFs: 0x0272,
|
|
52
|
+
BrtEndCellStyleXFs: 0x0273,
|
|
53
|
+
BrtBeginCellXFs: 0x0269,
|
|
54
|
+
BrtEndCellXFs: 0x026a,
|
|
55
|
+
BrtBundleSh: 0x009c,
|
|
56
|
+
BrtSupBookSrc: 0x0163,
|
|
57
|
+
BrtSupSelf: 0x0165,
|
|
58
|
+
BrtSupSame: 0x0166,
|
|
59
|
+
BrtExternSheet: 0x016a,
|
|
60
|
+
BrtDrawing: 0x0226,
|
|
61
|
+
BrtBeginAFilter: 0x00a1,
|
|
62
|
+
BrtListPart: 0x0295,
|
|
63
|
+
BrtWsView: 0x0089,
|
|
64
|
+
BrtPane: 0x0097,
|
|
65
|
+
BrtName: 0x0027,
|
|
66
|
+
BrtBeginConditionalFormatting: 0x01e5,
|
|
67
|
+
BrtEndConditionalFormatting: 0x01e6,
|
|
68
|
+
BrtBeginCFRule: 0x01e7,
|
|
69
|
+
BrtEndCFRule: 0x01e8,
|
|
70
|
+
BrtBeginDVals: 0x0105,
|
|
71
|
+
BrtEndDVals: 0x0106,
|
|
72
|
+
BrtBeginDVal: 0x0103,
|
|
73
|
+
BrtEndDVal: 0x0104,
|
|
74
|
+
BrtWbProp: 0x0099,
|
|
75
|
+
BrtCalcProp: 0x009d,
|
|
76
|
+
BrtShrFmla: 0x0018,
|
|
77
|
+
BrtCFRule: 0x01df,
|
|
78
|
+
BrtDVal: 0x0103,
|
|
79
|
+
};
|
|
80
|
+
class Reader {
|
|
81
|
+
view;
|
|
82
|
+
offset = 0;
|
|
83
|
+
bytes;
|
|
84
|
+
constructor(bytes) {
|
|
85
|
+
this.bytes = bytes;
|
|
86
|
+
this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
87
|
+
}
|
|
88
|
+
readUInt8() {
|
|
89
|
+
const value = this.view.getUint8(this.offset);
|
|
90
|
+
this.offset += 1;
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
readUInt16LE() {
|
|
94
|
+
const value = this.view.getUint16(this.offset, true);
|
|
95
|
+
this.offset += 2;
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
readInt16LE() {
|
|
99
|
+
const value = this.view.getInt16(this.offset, true);
|
|
100
|
+
this.offset += 2;
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
readUInt32LE() {
|
|
104
|
+
const value = this.view.getUint32(this.offset, true);
|
|
105
|
+
this.offset += 4;
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
readInt32LE() {
|
|
109
|
+
const value = this.view.getInt32(this.offset, true);
|
|
110
|
+
this.offset += 4;
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
readFloat64LE() {
|
|
114
|
+
const value = this.view.getFloat64(this.offset, true);
|
|
115
|
+
this.offset += 8;
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
readUtf16Chars(charCount) {
|
|
119
|
+
if (charCount <= 0)
|
|
120
|
+
return "";
|
|
121
|
+
const byteCount = charCount * 2;
|
|
122
|
+
const slice = this.bytes.subarray(this.offset, this.offset + byteCount);
|
|
123
|
+
this.offset += byteCount;
|
|
124
|
+
return new TextDecoder("utf-16le").decode(slice);
|
|
125
|
+
}
|
|
126
|
+
readXLWideString() {
|
|
127
|
+
const cch = this.readUInt32LE();
|
|
128
|
+
return cch === 0 ? "" : this.readUtf16Chars(cch);
|
|
129
|
+
}
|
|
130
|
+
readXLNullableWideString() {
|
|
131
|
+
const cch = this.readUInt32LE();
|
|
132
|
+
return cch === 0 || cch === 0xffffffff ? "" : this.readUtf16Chars(cch);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const normalizePath = (path) => path.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/^\/+/, "");
|
|
136
|
+
const resolveRelativePath = (basePath, target) => {
|
|
137
|
+
if (target.startsWith("/")) {
|
|
138
|
+
return normalizePath(target);
|
|
139
|
+
}
|
|
140
|
+
const baseParts = normalizePath(basePath).split("/");
|
|
141
|
+
baseParts.pop();
|
|
142
|
+
const parts = target.split("/");
|
|
143
|
+
for (const part of parts) {
|
|
144
|
+
if (!part || part === ".")
|
|
145
|
+
continue;
|
|
146
|
+
if (part === "..") {
|
|
147
|
+
baseParts.pop();
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
baseParts.push(part);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return normalizePath(baseParts.join("/"));
|
|
154
|
+
};
|
|
155
|
+
const parseBundleSheet = (payload) => {
|
|
156
|
+
const reader = new Reader(payload);
|
|
157
|
+
const hidden = reader.readUInt32LE();
|
|
158
|
+
const tabId = reader.readUInt32LE();
|
|
159
|
+
const rid = reader.readXLNullableWideString();
|
|
160
|
+
const title = reader.readXLWideString();
|
|
161
|
+
return { hidden, tabId, rid, title };
|
|
162
|
+
};
|
|
163
|
+
const parseRowHeader = (payload) => {
|
|
164
|
+
const reader = new Reader(payload);
|
|
165
|
+
const row = reader.readUInt32LE() + 1;
|
|
166
|
+
reader.readUInt32LE(); // ixfe
|
|
167
|
+
const miyRw = reader.readUInt16LE();
|
|
168
|
+
reader.readUInt8(); // top/bottom padding
|
|
169
|
+
const flags = reader.readUInt8();
|
|
170
|
+
return {
|
|
171
|
+
row,
|
|
172
|
+
hidden: Boolean(flags & 0x10),
|
|
173
|
+
hpt: Boolean(flags & 0x20) ? miyRw / 20 : undefined,
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
const parseMergeRange = (payload) => {
|
|
177
|
+
const reader = new Reader(payload);
|
|
178
|
+
const sRow = reader.readUInt32LE() + 1;
|
|
179
|
+
const eRow = reader.readUInt32LE() + 1;
|
|
180
|
+
const sCol = reader.readUInt32LE() + 1;
|
|
181
|
+
const eCol = reader.readUInt32LE() + 1;
|
|
182
|
+
return {
|
|
183
|
+
startRowIndex: sRow,
|
|
184
|
+
endRowIndex: eRow,
|
|
185
|
+
startColumnIndex: sCol,
|
|
186
|
+
endColumnIndex: eCol,
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
const parseRange = (payload) => parseMergeRange(payload);
|
|
190
|
+
const normalizeSheetTabIndex = (value) => value === 0xffff || value === 0xffffffff ? -1 : value;
|
|
191
|
+
const parseBrtExternSheet = (payload) => {
|
|
192
|
+
const parseEntries = (startOffset, entrySize, readEntry) => {
|
|
193
|
+
if (startOffset > payload.length)
|
|
194
|
+
return [];
|
|
195
|
+
const bytes = payload.subarray(startOffset);
|
|
196
|
+
if (bytes.length === 0 || bytes.length % entrySize !== 0) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
const reader = new Reader(bytes);
|
|
200
|
+
const out = [];
|
|
201
|
+
while (reader.offset + entrySize <= reader.bytes.length) {
|
|
202
|
+
out.push(readEntry(reader));
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
};
|
|
206
|
+
const read12 = (reader) => ({
|
|
207
|
+
iSupBook: reader.readUInt32LE(),
|
|
208
|
+
itabFirst: normalizeSheetTabIndex(reader.readUInt32LE()),
|
|
209
|
+
itabLast: normalizeSheetTabIndex(reader.readUInt32LE()),
|
|
210
|
+
});
|
|
211
|
+
const read6 = (reader) => ({
|
|
212
|
+
iSupBook: reader.readUInt16LE(),
|
|
213
|
+
itabFirst: normalizeSheetTabIndex(reader.readUInt16LE()),
|
|
214
|
+
itabLast: normalizeSheetTabIndex(reader.readUInt16LE()),
|
|
215
|
+
});
|
|
216
|
+
const direct12 = parseEntries(0, 12, read12);
|
|
217
|
+
if (direct12.length > 0)
|
|
218
|
+
return direct12;
|
|
219
|
+
if (payload.length >= 4) {
|
|
220
|
+
const reader = new Reader(payload);
|
|
221
|
+
const count = reader.readUInt32LE();
|
|
222
|
+
const withCount12 = parseEntries(4, 12, read12);
|
|
223
|
+
if (withCount12.length === count && withCount12.length > 0) {
|
|
224
|
+
return withCount12;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const direct6 = parseEntries(0, 6, read6);
|
|
228
|
+
if (direct6.length > 0)
|
|
229
|
+
return direct6;
|
|
230
|
+
if (payload.length >= 2) {
|
|
231
|
+
const reader = new Reader(payload);
|
|
232
|
+
const count = reader.readUInt16LE();
|
|
233
|
+
const withCount6 = parseEntries(2, 6, read6);
|
|
234
|
+
if (withCount6.length === count && withCount6.length > 0) {
|
|
235
|
+
return withCount6;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return [];
|
|
239
|
+
};
|
|
240
|
+
const parseBrtShrFmla = (payload) => {
|
|
241
|
+
if (payload.length < 20)
|
|
242
|
+
return null;
|
|
243
|
+
const reader = new Reader(payload);
|
|
244
|
+
const startRowZero = reader.readUInt32LE();
|
|
245
|
+
const endRowZero = reader.readUInt32LE();
|
|
246
|
+
const startColZero = reader.readUInt32LE();
|
|
247
|
+
const endColZero = reader.readUInt32LE();
|
|
248
|
+
// cce can be preceded by small metadata in some files; try a few offsets.
|
|
249
|
+
const candidateOffsets = [16, 18, 20, 24];
|
|
250
|
+
let rgceBytes;
|
|
251
|
+
for (const offset of candidateOffsets) {
|
|
252
|
+
if (offset + 4 > payload.length)
|
|
253
|
+
continue;
|
|
254
|
+
const cce = new DataView(payload.buffer, payload.byteOffset + offset, 4).getUint32(0, true);
|
|
255
|
+
if (cce <= 0)
|
|
256
|
+
continue;
|
|
257
|
+
const start = offset + 4;
|
|
258
|
+
const end = start + cce;
|
|
259
|
+
if (end <= payload.length) {
|
|
260
|
+
rgceBytes = payload.subarray(start, end);
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (!rgceBytes || rgceBytes.length === 0)
|
|
265
|
+
return null;
|
|
266
|
+
return {
|
|
267
|
+
startRow: startRowZero + 1,
|
|
268
|
+
endRow: endRowZero + 1,
|
|
269
|
+
startCol: startColZero + 1,
|
|
270
|
+
endCol: endColZero + 1,
|
|
271
|
+
rgceBytes,
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
const parseWsView = (payload) => {
|
|
275
|
+
const reader = new Reader(payload);
|
|
276
|
+
const flags = reader.readUInt16LE();
|
|
277
|
+
// Bit 1 (0x0002): fShowGridLines - if 0, gridlines are hidden
|
|
278
|
+
const showGridLines = (flags & 0x0002) !== 0;
|
|
279
|
+
return { showGridLines };
|
|
280
|
+
};
|
|
281
|
+
const parsePane = (payload) => {
|
|
282
|
+
const reader = new Reader(payload);
|
|
283
|
+
const xSplit = reader.readFloat64LE();
|
|
284
|
+
const ySplit = reader.readFloat64LE();
|
|
285
|
+
reader.readUInt32LE(); // rwTop
|
|
286
|
+
reader.readUInt32LE(); // colLeft
|
|
287
|
+
const pnnAcct = reader.readUInt8();
|
|
288
|
+
// Frozen panes have pnnAcct set to specific values
|
|
289
|
+
const isFrozen = pnnAcct > 0;
|
|
290
|
+
return {
|
|
291
|
+
frozenColumnCount: isFrozen && xSplit > 0 ? Math.round(xSplit) : 0,
|
|
292
|
+
frozenRowCount: isFrozen && ySplit > 0 ? Math.round(ySplit) : 0,
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
export const parseBrtNamePayload = (payload, resolveSheetNameByXti) => {
|
|
296
|
+
try {
|
|
297
|
+
const reader = new Reader(payload);
|
|
298
|
+
const flags = reader.readUInt32LE();
|
|
299
|
+
const fHidden = (flags & 0x00000001) !== 0;
|
|
300
|
+
reader.readUInt8(); // chKey
|
|
301
|
+
const itab = reader.readUInt32LE(); // sheet index (0xFFFFFFFF for workbook scope)
|
|
302
|
+
// Read the name string
|
|
303
|
+
if (reader.offset + 4 > payload.length) {
|
|
304
|
+
return null; // Not enough data
|
|
305
|
+
}
|
|
306
|
+
const name = reader.readXLWideString();
|
|
307
|
+
// NameParsedFormula: cce (UInt32) followed by cce bytes of Rgce
|
|
308
|
+
let formula = "";
|
|
309
|
+
if (reader.offset + 4 <= payload.length) {
|
|
310
|
+
const cce = reader.readUInt32LE();
|
|
311
|
+
if (cce > 0 && reader.offset + cce <= payload.length) {
|
|
312
|
+
const rgceBytes = payload.subarray(reader.offset, reader.offset + cce);
|
|
313
|
+
formula = parseRgce(rgceBytes, resolveSheetNameByXti);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
name,
|
|
318
|
+
formula,
|
|
319
|
+
hidden: fHidden,
|
|
320
|
+
sheetIndex: itab === 0xffffffff ? undefined : itab,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
catch (err) {
|
|
324
|
+
// If parsing fails, return null to skip this named range
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
/**
|
|
329
|
+
* Parse the `BrtWbProp` workbook-properties record (MS-XLSB 2.4.312).
|
|
330
|
+
* The payload starts with a 32-bit flags field; `fDate1904` is bit 0 of the
|
|
331
|
+
* first flag byte and switches the workbook's date serials to the 1904 epoch
|
|
332
|
+
* (Mac-origin workbooks), shifting every date by 1462 days.
|
|
333
|
+
*/
|
|
334
|
+
export const parseBrtWbPropPayload = (payload) => {
|
|
335
|
+
if (payload.length < 1) {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
use1904: (payload[0] & 0x01) !== 0,
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
const parseBrtCalcPropPayload = (payload) => {
|
|
343
|
+
try {
|
|
344
|
+
const reader = new Reader(payload);
|
|
345
|
+
reader.readUInt32LE(); // recalcID
|
|
346
|
+
const flags = reader.readUInt16LE();
|
|
347
|
+
const fFullCalcOnLoad = (flags & 0x0002) !== 0;
|
|
348
|
+
const fIter = (flags & 0x0008) !== 0;
|
|
349
|
+
const fForceFullCalc = (flags & 0x0100) !== 0;
|
|
350
|
+
const cCalcCount = reader.readUInt32LE(); // iteration count
|
|
351
|
+
const xnumDelta = reader.readFloat64LE(); // iteration delta
|
|
352
|
+
return {
|
|
353
|
+
fullCalcOnLoad: fFullCalcOnLoad || fForceFullCalc,
|
|
354
|
+
iterate: fIter,
|
|
355
|
+
iterateCount: cCalcCount,
|
|
356
|
+
iterateDelta: xnumDelta,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
catch (err) {
|
|
360
|
+
// If parsing fails, return default values
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
/**
|
|
365
|
+
* Parse conditional formatting range and rules from BrtBeginConditionalFormatting
|
|
366
|
+
*/
|
|
367
|
+
const parseBrtBeginConditionalFormatting = (payload) => {
|
|
368
|
+
try {
|
|
369
|
+
const reader = new Reader(payload);
|
|
370
|
+
// Read the range reference (sqref)
|
|
371
|
+
const sqref = reader.readXLWideString();
|
|
372
|
+
return { sqref };
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
/**
|
|
379
|
+
* Parse CF rule from BrtCFRule / BrtBeginCFRule
|
|
380
|
+
*/
|
|
381
|
+
const parseBrtCFRule = (payload) => {
|
|
382
|
+
try {
|
|
383
|
+
const reader = new Reader(payload);
|
|
384
|
+
const flags = reader.readUInt32LE();
|
|
385
|
+
const ruleType = reader.readUInt8(); // CF rule type
|
|
386
|
+
const dxfId = reader.readInt32LE(); // DXF format ID (-1 if none)
|
|
387
|
+
const priority = reader.readUInt32LE();
|
|
388
|
+
// Parse flags
|
|
389
|
+
const stopIfTrue = (flags & 0x02) !== 0;
|
|
390
|
+
// For now, return basic structure
|
|
391
|
+
// Full implementation would parse formulas, operators, values based on rule type
|
|
392
|
+
return {
|
|
393
|
+
ruleType,
|
|
394
|
+
dxfId: dxfId >= 0 ? dxfId : undefined,
|
|
395
|
+
priority,
|
|
396
|
+
stopIfTrue,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
/**
|
|
404
|
+
* Parse data validation from BrtBeginDVal
|
|
405
|
+
*/
|
|
406
|
+
const parseBrtBeginDVal = (payload) => {
|
|
407
|
+
try {
|
|
408
|
+
const reader = new Reader(payload);
|
|
409
|
+
const flags = reader.readUInt32LE();
|
|
410
|
+
// Parse validation type and operator
|
|
411
|
+
const valType = reader.readUInt8(); // 0=none, 1=whole, 2=decimal, 3=list, 4=date, 5=time, 6=textLength, 7=custom
|
|
412
|
+
const operator = reader.readUInt8(); // 0=between, 1=notBetween, 2=equal, 3=notEqual, etc.
|
|
413
|
+
// Parse flags
|
|
414
|
+
const ignoreBlank = (flags & 0x01) !== 0;
|
|
415
|
+
const inCellDropdown = (flags & 0x02) === 0; // Inverted logic
|
|
416
|
+
const showInputMessage = (flags & 0x04) !== 0;
|
|
417
|
+
const showErrorMessage = (flags & 0x08) !== 0;
|
|
418
|
+
// Error style: 0=stop, 1=warning, 2=information
|
|
419
|
+
const errorStyle = (flags >> 4) & 0x03;
|
|
420
|
+
// Read strings
|
|
421
|
+
const promptTitle = reader.readXLWideString();
|
|
422
|
+
const errorTitle = reader.readXLWideString();
|
|
423
|
+
const prompt = reader.readXLWideString();
|
|
424
|
+
const error = reader.readXLWideString();
|
|
425
|
+
// Range would be in the parent record or following data
|
|
426
|
+
// For now, return parsed data
|
|
427
|
+
return {
|
|
428
|
+
valType,
|
|
429
|
+
operator,
|
|
430
|
+
ignoreBlank,
|
|
431
|
+
inCellDropdown,
|
|
432
|
+
showInputMessage,
|
|
433
|
+
showErrorMessage,
|
|
434
|
+
errorStyle,
|
|
435
|
+
promptTitle,
|
|
436
|
+
errorTitle,
|
|
437
|
+
prompt,
|
|
438
|
+
error,
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
const parseRkNumber = (payload, offset) => {
|
|
446
|
+
const b0 = payload[offset];
|
|
447
|
+
const fX100 = (b0 & 1) !== 0;
|
|
448
|
+
const fInt = (b0 & 2) !== 0;
|
|
449
|
+
let value;
|
|
450
|
+
if (fInt) {
|
|
451
|
+
const view = new DataView(payload.buffer, payload.byteOffset + offset, payload.byteLength - offset);
|
|
452
|
+
value = (view.getInt32(0, true) >> 2);
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
const out = new Uint8Array(8);
|
|
456
|
+
out.set(payload.subarray(offset, offset + 4), 4);
|
|
457
|
+
out[4] &= 0xfc;
|
|
458
|
+
value = new DataView(out.buffer).getFloat64(0, true);
|
|
459
|
+
}
|
|
460
|
+
return fX100 ? value / 100 : value;
|
|
461
|
+
};
|
|
462
|
+
const XLSB_FILL_PATTERN_NAMES = [
|
|
463
|
+
"none",
|
|
464
|
+
"solid",
|
|
465
|
+
"mediumGray",
|
|
466
|
+
"darkGray",
|
|
467
|
+
"lightGray",
|
|
468
|
+
"darkHorizontal",
|
|
469
|
+
"darkVertical",
|
|
470
|
+
"darkDown",
|
|
471
|
+
"darkUp",
|
|
472
|
+
"darkGrid",
|
|
473
|
+
"darkTrellis",
|
|
474
|
+
"lightHorizontal",
|
|
475
|
+
"lightVertical",
|
|
476
|
+
"lightDown",
|
|
477
|
+
"lightUp",
|
|
478
|
+
"lightGrid",
|
|
479
|
+
"lightTrellis",
|
|
480
|
+
"gray125",
|
|
481
|
+
"gray0625",
|
|
482
|
+
];
|
|
483
|
+
const getNumberFormatType = (pattern) => {
|
|
484
|
+
const lower = pattern.toLowerCase();
|
|
485
|
+
if (lower.includes("yy") || lower.includes("dd"))
|
|
486
|
+
return "DATE";
|
|
487
|
+
if (lower.includes("h:") || lower.includes("am/pm"))
|
|
488
|
+
return "TIME";
|
|
489
|
+
if (lower.includes("%"))
|
|
490
|
+
return "PERCENT";
|
|
491
|
+
if (lower.includes("$") ||
|
|
492
|
+
lower.includes("€") ||
|
|
493
|
+
lower.includes("£") ||
|
|
494
|
+
lower.includes("¥")) {
|
|
495
|
+
return "CURRENCY";
|
|
496
|
+
}
|
|
497
|
+
if (lower.includes("0") || lower.includes("#"))
|
|
498
|
+
return "NUMBER";
|
|
499
|
+
return "GENERAL";
|
|
500
|
+
};
|
|
501
|
+
const parseCellFromRecord = (type, payload, row, prevCol, resolveSheetNameByXti, resolveSharedFormula, parseFormula = true) => {
|
|
502
|
+
if (row <= 0)
|
|
503
|
+
return null;
|
|
504
|
+
const reader = new Reader(payload);
|
|
505
|
+
let colZero;
|
|
506
|
+
let styleRef;
|
|
507
|
+
const isShort = type === RT.BrtShortRk ||
|
|
508
|
+
type === RT.BrtShortError ||
|
|
509
|
+
type === RT.BrtShortBool ||
|
|
510
|
+
type === RT.BrtShortReal ||
|
|
511
|
+
type === RT.BrtShortSt ||
|
|
512
|
+
type === RT.BrtShortIsst;
|
|
513
|
+
if (isShort) {
|
|
514
|
+
styleRef = reader.readUInt16LE() + (reader.readUInt8() << 16);
|
|
515
|
+
reader.readUInt8();
|
|
516
|
+
colZero = prevCol + 1;
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
colZero = reader.readUInt32LE();
|
|
520
|
+
styleRef = reader.readUInt16LE() + (reader.readUInt8() << 16);
|
|
521
|
+
reader.readUInt8();
|
|
522
|
+
}
|
|
523
|
+
if (type === RT.BrtCellIsst || type === RT.BrtShortIsst) {
|
|
524
|
+
const sharedStringIndex = reader.readUInt32LE();
|
|
525
|
+
return {
|
|
526
|
+
row,
|
|
527
|
+
col: colZero + 1,
|
|
528
|
+
styleRef,
|
|
529
|
+
kind: "s",
|
|
530
|
+
value: sharedStringIndex,
|
|
531
|
+
sharedStringIndex,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
if (type === RT.BrtCellBlank || type === RT.BrtShortBlank) {
|
|
535
|
+
return {
|
|
536
|
+
row,
|
|
537
|
+
col: colZero + 1,
|
|
538
|
+
styleRef,
|
|
539
|
+
kind: "blank",
|
|
540
|
+
value: "",
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
const readFormula = () => {
|
|
544
|
+
if (!parseFormula)
|
|
545
|
+
return undefined;
|
|
546
|
+
const candidateStarts = [
|
|
547
|
+
reader.offset - 2,
|
|
548
|
+
reader.offset,
|
|
549
|
+
reader.offset + 2,
|
|
550
|
+
reader.offset + 4,
|
|
551
|
+
reader.offset + 6,
|
|
552
|
+
reader.offset + 8,
|
|
553
|
+
reader.offset + 10,
|
|
554
|
+
reader.offset + 12,
|
|
555
|
+
].filter((start, idx, arr) => start >= 0 && arr.indexOf(start) === idx);
|
|
556
|
+
const decodeAt = (start, cceSize) => {
|
|
557
|
+
if (cceSize === 4) {
|
|
558
|
+
if (start + 4 > payload.length)
|
|
559
|
+
return undefined;
|
|
560
|
+
const cce = new DataView(payload.buffer, payload.byteOffset + start, 4).getUint32(0, true);
|
|
561
|
+
if (cce <= 0)
|
|
562
|
+
return undefined;
|
|
563
|
+
const rgceStart = start + 4;
|
|
564
|
+
const rgceEnd = rgceStart + cce;
|
|
565
|
+
if (rgceEnd > payload.length)
|
|
566
|
+
return undefined;
|
|
567
|
+
return payload.subarray(rgceStart, rgceEnd);
|
|
568
|
+
}
|
|
569
|
+
if (start + 2 > payload.length)
|
|
570
|
+
return undefined;
|
|
571
|
+
const cce = new DataView(payload.buffer, payload.byteOffset + start, 2).getUint16(0, true);
|
|
572
|
+
if (cce <= 0)
|
|
573
|
+
return undefined;
|
|
574
|
+
const rgceStart = start + 2;
|
|
575
|
+
const rgceEnd = rgceStart + cce;
|
|
576
|
+
if (rgceEnd > payload.length)
|
|
577
|
+
return undefined;
|
|
578
|
+
return payload.subarray(rgceStart, rgceEnd);
|
|
579
|
+
};
|
|
580
|
+
const tryResolve = (parsed) => {
|
|
581
|
+
const expMatch = parsed.match(/^__PTGEXP__:(\d+):(\d+)$/);
|
|
582
|
+
if (!expMatch)
|
|
583
|
+
return parsed;
|
|
584
|
+
if (!resolveSharedFormula)
|
|
585
|
+
return undefined;
|
|
586
|
+
const sourceRow = Number(expMatch[1]);
|
|
587
|
+
const sourceCol = Number(expMatch[2]);
|
|
588
|
+
return resolveSharedFormula(sourceRow, sourceCol, row, colZero + 1);
|
|
589
|
+
};
|
|
590
|
+
for (const start of candidateStarts) {
|
|
591
|
+
for (const cceSize of [4, 2]) {
|
|
592
|
+
const rgceBytes = decodeAt(start, cceSize);
|
|
593
|
+
if (!rgceBytes || rgceBytes.length === 0)
|
|
594
|
+
continue;
|
|
595
|
+
const parsed = parseRgce(rgceBytes, resolveSheetNameByXti, { row, col: colZero + 1 }, true);
|
|
596
|
+
if (!parsed)
|
|
597
|
+
continue;
|
|
598
|
+
const resolved = tryResolve(parsed);
|
|
599
|
+
if (resolved && resolved.length > 0) {
|
|
600
|
+
return resolved;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
return undefined;
|
|
605
|
+
};
|
|
606
|
+
if (type === RT.BrtFmlaNum) {
|
|
607
|
+
const value = reader.readFloat64LE();
|
|
608
|
+
reader.readUInt16LE(); // flags
|
|
609
|
+
const formula = readFormula();
|
|
610
|
+
return {
|
|
611
|
+
row,
|
|
612
|
+
col: colZero + 1,
|
|
613
|
+
styleRef,
|
|
614
|
+
kind: "n",
|
|
615
|
+
value,
|
|
616
|
+
formula,
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
if (type === RT.BrtFmlaString) {
|
|
620
|
+
const value = reader.readXLWideString();
|
|
621
|
+
reader.readUInt16LE(); // flags
|
|
622
|
+
const formula = readFormula();
|
|
623
|
+
return {
|
|
624
|
+
row,
|
|
625
|
+
col: colZero + 1,
|
|
626
|
+
styleRef,
|
|
627
|
+
kind: "str",
|
|
628
|
+
value,
|
|
629
|
+
formula,
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
if (type === RT.BrtFmlaBool) {
|
|
633
|
+
const value = reader.readUInt8() !== 0;
|
|
634
|
+
reader.readUInt8(); // padding
|
|
635
|
+
reader.readUInt16LE(); // flags
|
|
636
|
+
const formula = readFormula();
|
|
637
|
+
return {
|
|
638
|
+
row,
|
|
639
|
+
col: colZero + 1,
|
|
640
|
+
styleRef,
|
|
641
|
+
kind: "b",
|
|
642
|
+
value,
|
|
643
|
+
formula,
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
if (type === RT.BrtFmlaError) {
|
|
647
|
+
const value = reader.readUInt8();
|
|
648
|
+
reader.readUInt8(); // padding
|
|
649
|
+
reader.readUInt16LE(); // flags
|
|
650
|
+
const formula = readFormula();
|
|
651
|
+
return {
|
|
652
|
+
row,
|
|
653
|
+
col: colZero + 1,
|
|
654
|
+
styleRef,
|
|
655
|
+
kind: "e",
|
|
656
|
+
value,
|
|
657
|
+
formula,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
if (type === RT.BrtCellSt || type === RT.BrtShortSt) {
|
|
661
|
+
return {
|
|
662
|
+
row,
|
|
663
|
+
col: colZero + 1,
|
|
664
|
+
styleRef,
|
|
665
|
+
kind: "str",
|
|
666
|
+
value: reader.readXLWideString(),
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
if (type === RT.BrtCellReal || type === RT.BrtShortReal) {
|
|
670
|
+
return {
|
|
671
|
+
row,
|
|
672
|
+
col: colZero + 1,
|
|
673
|
+
styleRef,
|
|
674
|
+
kind: "n",
|
|
675
|
+
value: reader.readFloat64LE(),
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
if (type === RT.BrtCellRk || type === RT.BrtShortRk) {
|
|
679
|
+
return {
|
|
680
|
+
row,
|
|
681
|
+
col: colZero + 1,
|
|
682
|
+
styleRef,
|
|
683
|
+
kind: "n",
|
|
684
|
+
value: parseRkNumber(payload, reader.offset),
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
if (type === RT.BrtCellBool || type === RT.BrtShortBool) {
|
|
688
|
+
return {
|
|
689
|
+
row,
|
|
690
|
+
col: colZero + 1,
|
|
691
|
+
styleRef,
|
|
692
|
+
kind: "b",
|
|
693
|
+
value: reader.readUInt8() !== 0,
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
if (type === RT.BrtCellError || type === RT.BrtShortError) {
|
|
697
|
+
return {
|
|
698
|
+
row,
|
|
699
|
+
col: colZero + 1,
|
|
700
|
+
styleRef,
|
|
701
|
+
kind: "e",
|
|
702
|
+
value: reader.readUInt8(),
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
return null;
|
|
706
|
+
};
|
|
707
|
+
const parseBrtColor = (reader) => {
|
|
708
|
+
const d = reader.readUInt8();
|
|
709
|
+
const xColorType = d >>> 1;
|
|
710
|
+
const index = reader.readUInt8();
|
|
711
|
+
const tintRaw = reader.readInt16LE();
|
|
712
|
+
const r = reader.readUInt8();
|
|
713
|
+
const g = reader.readUInt8();
|
|
714
|
+
const b = reader.readUInt8();
|
|
715
|
+
reader.readUInt8(); // alpha
|
|
716
|
+
const tint = tintRaw === 0 ? undefined : tintRaw > 0 ? tintRaw / 32767 : tintRaw / 32768;
|
|
717
|
+
if (xColorType === 2) {
|
|
718
|
+
return `#${[r, g, b]
|
|
719
|
+
.map((v) => v.toString(16).padStart(2, "0"))
|
|
720
|
+
.join("")
|
|
721
|
+
.toUpperCase()}`;
|
|
722
|
+
}
|
|
723
|
+
if (xColorType === 3) {
|
|
724
|
+
return {
|
|
725
|
+
theme: index,
|
|
726
|
+
...(tint != null ? { tint } : {}),
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
if (xColorType === 1 && index === 64) {
|
|
730
|
+
return { theme: 1 };
|
|
731
|
+
}
|
|
732
|
+
if (xColorType === 1) {
|
|
733
|
+
return IndexedColors[index];
|
|
734
|
+
}
|
|
735
|
+
return undefined;
|
|
736
|
+
};
|
|
737
|
+
const parseBrtFill = (payload) => {
|
|
738
|
+
const reader = new Reader(payload);
|
|
739
|
+
const fill = reader.readUInt32LE();
|
|
740
|
+
const result = {
|
|
741
|
+
patternType: XLSB_FILL_PATTERN_NAMES[fill] ?? (fill === 0x28 ? "gradient" : undefined),
|
|
742
|
+
};
|
|
743
|
+
if (fill !== 0x28 && payload.length >= 20) {
|
|
744
|
+
result.fgColor = parseBrtColor(reader);
|
|
745
|
+
parseBrtColor(reader);
|
|
746
|
+
}
|
|
747
|
+
return result;
|
|
748
|
+
};
|
|
749
|
+
const parseBrtFmt = (payload) => {
|
|
750
|
+
const reader = new Reader(payload);
|
|
751
|
+
const id = reader.readUInt16LE();
|
|
752
|
+
const formatCode = reader.readXLWideString();
|
|
753
|
+
return { id, formatCode };
|
|
754
|
+
};
|
|
755
|
+
const parseBrtXf = (payload) => {
|
|
756
|
+
const reader = new Reader(payload);
|
|
757
|
+
const parentId = reader.readUInt16LE();
|
|
758
|
+
const numFmtId = reader.readUInt16LE();
|
|
759
|
+
const fontId = reader.readUInt16LE();
|
|
760
|
+
const fillId = reader.readUInt16LE();
|
|
761
|
+
const borderId = payload.length >= 10 ? reader.readUInt16LE() : 0;
|
|
762
|
+
const result = { parentId, numFmtId, fontId, fillId, borderId };
|
|
763
|
+
// Parse alignment if enough data is available
|
|
764
|
+
if (payload.length >= 14) {
|
|
765
|
+
const trot = reader.readUInt8(); // text rotation
|
|
766
|
+
const indent = reader.readUInt8();
|
|
767
|
+
const flags = reader.readUInt16LE();
|
|
768
|
+
// Extract alignment from flags (bits 0-2: horizontal, bits 3-5: vertical)
|
|
769
|
+
const alH = flags & 0x07; // bits 0-2
|
|
770
|
+
const alV = (flags >> 3) & 0x07; // bits 3-5
|
|
771
|
+
const fWrap = (flags >> 6) & 0x01; // bit 6
|
|
772
|
+
// Map horizontal alignment
|
|
773
|
+
const horizontalAlignments = [
|
|
774
|
+
"general",
|
|
775
|
+
"left",
|
|
776
|
+
"center",
|
|
777
|
+
"right",
|
|
778
|
+
"fill",
|
|
779
|
+
"justify",
|
|
780
|
+
"centerContinuous",
|
|
781
|
+
"distributed",
|
|
782
|
+
];
|
|
783
|
+
if (alH > 0 && alH < horizontalAlignments.length) {
|
|
784
|
+
result.horizontalAlignment = horizontalAlignments[alH];
|
|
785
|
+
}
|
|
786
|
+
// Map vertical alignment
|
|
787
|
+
const verticalAlignments = [
|
|
788
|
+
"top",
|
|
789
|
+
"middle",
|
|
790
|
+
"bottom",
|
|
791
|
+
"justify",
|
|
792
|
+
"distributed",
|
|
793
|
+
];
|
|
794
|
+
if (alV > 0 && alV < verticalAlignments.length) {
|
|
795
|
+
const mapped = verticalAlignments[alV];
|
|
796
|
+
result.verticalAlignment = mapped === "middle" ? "middle" : mapped;
|
|
797
|
+
}
|
|
798
|
+
// Wrap text
|
|
799
|
+
if (fWrap) {
|
|
800
|
+
result.wrapText = true;
|
|
801
|
+
}
|
|
802
|
+
// Text rotation
|
|
803
|
+
if (trot === 0xff) {
|
|
804
|
+
result.textRotation = "vertical";
|
|
805
|
+
}
|
|
806
|
+
else if (trot > 0 && trot <= 90) {
|
|
807
|
+
result.textRotation = trot;
|
|
808
|
+
}
|
|
809
|
+
else if (trot >= 91 && trot <= 180) {
|
|
810
|
+
result.textRotation = -(trot - 90);
|
|
811
|
+
}
|
|
812
|
+
// Indent
|
|
813
|
+
if (indent > 0) {
|
|
814
|
+
result.indent = indent;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return result;
|
|
818
|
+
};
|
|
819
|
+
const parseBrtColInfo = (payload) => {
|
|
820
|
+
const reader = new Reader(payload);
|
|
821
|
+
const startCol = reader.readUInt32LE() + 1;
|
|
822
|
+
const endCol = reader.readUInt32LE() + 1;
|
|
823
|
+
const coldx = reader.readUInt32LE();
|
|
824
|
+
reader.readUInt32LE(); // ixfe/style
|
|
825
|
+
const flags = reader.readUInt16LE();
|
|
826
|
+
const hidden = (flags & 0x01) !== 0;
|
|
827
|
+
const customWidth = (flags & 0x02) !== 0;
|
|
828
|
+
const widthChars = coldx / 256;
|
|
829
|
+
const size = excelColumnWidthToPixels(widthChars);
|
|
830
|
+
return { startCol, endCol, hidden, customWidth, size };
|
|
831
|
+
};
|
|
832
|
+
const parseBrtListPartRelId = (payload) => {
|
|
833
|
+
const reader = new Reader(payload);
|
|
834
|
+
return reader.readXLNullableWideString();
|
|
835
|
+
};
|
|
836
|
+
const parseBrtFont = (payload) => {
|
|
837
|
+
const reader = new Reader(payload);
|
|
838
|
+
const size = reader.readUInt16LE() / 20;
|
|
839
|
+
const flags = reader.readUInt8();
|
|
840
|
+
reader.readUInt8(); // reserved
|
|
841
|
+
reader.readUInt16LE(); // bold weight
|
|
842
|
+
reader.readUInt16LE(); // script style
|
|
843
|
+
const underline = reader.readUInt8();
|
|
844
|
+
reader.readUInt8(); // family
|
|
845
|
+
reader.readUInt8(); // charset
|
|
846
|
+
reader.readUInt8(); // reserved
|
|
847
|
+
const color = parseBrtColor(reader);
|
|
848
|
+
reader.readUInt8(); // scheme
|
|
849
|
+
const name = reader.readXLWideString();
|
|
850
|
+
return {
|
|
851
|
+
...(name ? { name } : {}),
|
|
852
|
+
...(Number.isFinite(size) ? { size } : {}),
|
|
853
|
+
...(flags & 0x01 ? { bold: true } : {}),
|
|
854
|
+
...(flags & 0x02 ? { italic: true } : {}),
|
|
855
|
+
...(flags & 0x08 ? { strikethrough: true } : {}),
|
|
856
|
+
...(underline !== 0 ? { underline: true } : {}),
|
|
857
|
+
...(color ? { color } : {}),
|
|
858
|
+
};
|
|
859
|
+
};
|
|
860
|
+
const XLSB_BORDER_STYLE_MAP = {
|
|
861
|
+
1: { style: "solid", width: 1 },
|
|
862
|
+
2: { style: "solid_medium", width: 2 },
|
|
863
|
+
3: { style: "dashed", width: 1 },
|
|
864
|
+
4: { style: "dotted", width: 1 },
|
|
865
|
+
5: { style: "solid_thick", width: 3 },
|
|
866
|
+
6: { style: "double", width: 2 },
|
|
867
|
+
7: { style: "thin", width: 1 },
|
|
868
|
+
8: { style: "dashed", width: 2 },
|
|
869
|
+
9: { style: "dashed", width: 1 },
|
|
870
|
+
10: { style: "dashed", width: 2 },
|
|
871
|
+
11: { style: "dotted", width: 1 },
|
|
872
|
+
12: { style: "dotted", width: 2 },
|
|
873
|
+
13: { style: "dashed", width: 2 },
|
|
874
|
+
};
|
|
875
|
+
const parseBrtBorder = (payload) => {
|
|
876
|
+
if (payload.length < 41)
|
|
877
|
+
return {};
|
|
878
|
+
const reader = new Reader(payload);
|
|
879
|
+
reader.readUInt8(); // diagonal flags
|
|
880
|
+
const side = () => {
|
|
881
|
+
const styleId = reader.readUInt8();
|
|
882
|
+
reader.readUInt8(); // reserved
|
|
883
|
+
const color = parseBrtColor(reader);
|
|
884
|
+
const mapped = XLSB_BORDER_STYLE_MAP[styleId];
|
|
885
|
+
if (!mapped)
|
|
886
|
+
return undefined;
|
|
887
|
+
return {
|
|
888
|
+
style: mapped.style,
|
|
889
|
+
width: mapped.width,
|
|
890
|
+
...(color ? { color } : {}),
|
|
891
|
+
};
|
|
892
|
+
};
|
|
893
|
+
const top = side();
|
|
894
|
+
const bottom = side();
|
|
895
|
+
const left = side();
|
|
896
|
+
const right = side();
|
|
897
|
+
side(); // diagonal, unused
|
|
898
|
+
return {
|
|
899
|
+
...(top ? { top } : {}),
|
|
900
|
+
...(right ? { right } : {}),
|
|
901
|
+
...(bottom ? { bottom } : {}),
|
|
902
|
+
...(left ? { left } : {}),
|
|
903
|
+
};
|
|
904
|
+
};
|
|
905
|
+
const resolveXf = (styles, xfIndex) => {
|
|
906
|
+
const xf = styles.cellXfs[xfIndex];
|
|
907
|
+
if (!xf)
|
|
908
|
+
return undefined;
|
|
909
|
+
const parent = styles.styleXfs[xf.parentId];
|
|
910
|
+
if (!parent)
|
|
911
|
+
return xf;
|
|
912
|
+
const inheritIndex = (child, base) => child ?? base;
|
|
913
|
+
// Special handling for numFmtId: when child is 0 and parent has a non-zero format,
|
|
914
|
+
// inherit from parent. This handles cases where minimal XF records (without apply flags)
|
|
915
|
+
// use 0 to indicate "inherit from parent" rather than explicitly using format 0 (General).
|
|
916
|
+
const inheritNumFmtId = (child, base) => {
|
|
917
|
+
if (child === 0 && base !== 0) {
|
|
918
|
+
return base;
|
|
919
|
+
}
|
|
920
|
+
return child ?? base;
|
|
921
|
+
};
|
|
922
|
+
return {
|
|
923
|
+
parentId: xf.parentId,
|
|
924
|
+
numFmtId: inheritNumFmtId(xf.numFmtId, parent.numFmtId),
|
|
925
|
+
fontId: inheritIndex(xf.fontId, parent.fontId),
|
|
926
|
+
fillId: inheritIndex(xf.fillId, parent.fillId),
|
|
927
|
+
borderId: inheritIndex(xf.borderId, parent.borderId),
|
|
928
|
+
horizontalAlignment: xf.horizontalAlignment ?? parent.horizontalAlignment,
|
|
929
|
+
verticalAlignment: xf.verticalAlignment ?? parent.verticalAlignment,
|
|
930
|
+
wrapText: xf.wrapText ?? parent.wrapText,
|
|
931
|
+
textRotation: xf.textRotation ?? parent.textRotation,
|
|
932
|
+
indent: xf.indent ?? parent.indent,
|
|
933
|
+
};
|
|
934
|
+
};
|
|
935
|
+
const toCellData = (parsed, styles, sharedStrings, enableCellXfsRegistry, enableSharedStrings, cellXfs) => {
|
|
936
|
+
const xf = resolveXf(styles, parsed.styleRef);
|
|
937
|
+
const format = {};
|
|
938
|
+
const pattern = xf
|
|
939
|
+
? styles.numberFormats.get(xf.numFmtId) ??
|
|
940
|
+
standardExcelFormatCodeById.get(xf.numFmtId)
|
|
941
|
+
: undefined;
|
|
942
|
+
if (pattern) {
|
|
943
|
+
format.numberFormat = {
|
|
944
|
+
type: getNumberFormatType(pattern),
|
|
945
|
+
pattern,
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
if (xf?.fillId != null) {
|
|
949
|
+
const fill = styles.fills[xf.fillId];
|
|
950
|
+
if (fill?.fgColor && fill.patternType === "solid") {
|
|
951
|
+
format.backgroundColor = fill.fgColor;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
if (xf?.fontId != null) {
|
|
955
|
+
const font = styles.fonts[xf.fontId];
|
|
956
|
+
if (font) {
|
|
957
|
+
const textFormat = {
|
|
958
|
+
...(font.name ? { fontFamily: font.name } : {}),
|
|
959
|
+
...(font.size != null ? { fontSize: font.size } : {}),
|
|
960
|
+
...(font.bold ? { bold: true } : {}),
|
|
961
|
+
...(font.italic ? { italic: true } : {}),
|
|
962
|
+
...(font.underline ? { underline: true } : {}),
|
|
963
|
+
...(font.strikethrough ? { strikethrough: true } : {}),
|
|
964
|
+
...(font.color ? { color: font.color } : {}),
|
|
965
|
+
};
|
|
966
|
+
if (Object.keys(textFormat).length > 0) {
|
|
967
|
+
format.textFormat = textFormat;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
if (xf?.borderId != null) {
|
|
972
|
+
const border = styles.borders[xf.borderId];
|
|
973
|
+
if (border && Object.keys(border).length > 0) {
|
|
974
|
+
format.borders = border;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
// Apply alignment properties
|
|
978
|
+
// Only set valid alignment values (left, right, center for horizontal; top, middle, bottom for vertical)
|
|
979
|
+
if (xf?.horizontalAlignment) {
|
|
980
|
+
const validHorizontal = ["left", "right", "center"];
|
|
981
|
+
if (validHorizontal.includes(xf.horizontalAlignment)) {
|
|
982
|
+
format.horizontalAlignment = xf.horizontalAlignment;
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
if (xf?.verticalAlignment) {
|
|
986
|
+
const validVertical = ["top", "middle", "bottom"];
|
|
987
|
+
if (validVertical.includes(xf.verticalAlignment)) {
|
|
988
|
+
format.verticalAlignment = xf.verticalAlignment;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
if (xf?.wrapText) {
|
|
992
|
+
format.wrapStrategy = "wrap";
|
|
993
|
+
}
|
|
994
|
+
if (xf?.textRotation !== undefined) {
|
|
995
|
+
format.textRotation = xf.textRotation;
|
|
996
|
+
}
|
|
997
|
+
if (xf?.indent !== undefined && xf.indent > 0) {
|
|
998
|
+
format.indent = xf.indent;
|
|
999
|
+
}
|
|
1000
|
+
const hasFormat = Object.keys(format).length > 0;
|
|
1001
|
+
const style = enableCellXfsRegistry && hasFormat
|
|
1002
|
+
? { sid: `xlsb:${parsed.styleRef}` }
|
|
1003
|
+
: undefined;
|
|
1004
|
+
if (style && !cellXfs.has(style.sid)) {
|
|
1005
|
+
cellXfs.set(style.sid, format);
|
|
1006
|
+
}
|
|
1007
|
+
if (parsed.kind === "s") {
|
|
1008
|
+
const idx = parsed.sharedStringIndex ?? Number(parsed.value);
|
|
1009
|
+
const text = sharedStrings[idx] ?? "";
|
|
1010
|
+
if (enableSharedStrings) {
|
|
1011
|
+
return {
|
|
1012
|
+
...(style
|
|
1013
|
+
? { ef: style, uf: style }
|
|
1014
|
+
: hasFormat
|
|
1015
|
+
? { ef: format, uf: format }
|
|
1016
|
+
: {}),
|
|
1017
|
+
ss: String(idx),
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
return {
|
|
1021
|
+
...(style
|
|
1022
|
+
? { ef: style, uf: style }
|
|
1023
|
+
: hasFormat
|
|
1024
|
+
? { ef: format, uf: format }
|
|
1025
|
+
: {}),
|
|
1026
|
+
ue: { sv: text },
|
|
1027
|
+
ev: { sv: text },
|
|
1028
|
+
fv: text,
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
if (parsed.kind === "str") {
|
|
1032
|
+
const text = String(parsed.value);
|
|
1033
|
+
// If there's a formula, use it in ue.fv, otherwise use the text value
|
|
1034
|
+
const ue = parsed.formula ? { fv: `=${parsed.formula}` } : { sv: text };
|
|
1035
|
+
return {
|
|
1036
|
+
...(style
|
|
1037
|
+
? { ef: style, uf: style }
|
|
1038
|
+
: hasFormat
|
|
1039
|
+
? { ef: format, uf: format }
|
|
1040
|
+
: {}),
|
|
1041
|
+
ue,
|
|
1042
|
+
ev: { sv: text },
|
|
1043
|
+
// Skip `fv` for formula cells; renderer derives at paint time.
|
|
1044
|
+
...(parsed.formula ? {} : { fv: text }),
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
if (parsed.kind === "n") {
|
|
1048
|
+
const value = Number(parsed.value);
|
|
1049
|
+
const pattern = format.numberFormat?.pattern ?? "GENERAL";
|
|
1050
|
+
const fv = ssfFormat(pattern, value);
|
|
1051
|
+
// If there's a formula, use it in ue.fv, otherwise use the numeric value
|
|
1052
|
+
const ue = parsed.formula ? { fv: `=${parsed.formula}` } : { nv: value };
|
|
1053
|
+
return {
|
|
1054
|
+
...(style
|
|
1055
|
+
? { ef: style, uf: style }
|
|
1056
|
+
: hasFormat
|
|
1057
|
+
? { ef: format, uf: format }
|
|
1058
|
+
: {}),
|
|
1059
|
+
ue,
|
|
1060
|
+
ev: { nv: value },
|
|
1061
|
+
...(parsed.formula ? {} : { fv }),
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
if (parsed.kind === "b") {
|
|
1065
|
+
const value = Boolean(parsed.value);
|
|
1066
|
+
// If there's a formula, use it in ue.fv, otherwise use the boolean value
|
|
1067
|
+
const ue = parsed.formula ? { fv: `=${parsed.formula}` } : { bv: value };
|
|
1068
|
+
return {
|
|
1069
|
+
...(style
|
|
1070
|
+
? { ef: style, uf: style }
|
|
1071
|
+
: hasFormat
|
|
1072
|
+
? { ef: format, uf: format }
|
|
1073
|
+
: {}),
|
|
1074
|
+
ue,
|
|
1075
|
+
ev: { bv: value },
|
|
1076
|
+
...(parsed.formula ? {} : { fv: value ? "TRUE" : "FALSE" }),
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
if (parsed.kind === "e") {
|
|
1080
|
+
// Error value
|
|
1081
|
+
const errorCodes = {
|
|
1082
|
+
0x00: "#NULL!",
|
|
1083
|
+
0x07: "#DIV/0!",
|
|
1084
|
+
0x0f: "#VALUE!",
|
|
1085
|
+
0x17: "#REF!",
|
|
1086
|
+
0x1d: "#NAME?",
|
|
1087
|
+
0x24: "#NUM!",
|
|
1088
|
+
0x2a: "#N/A",
|
|
1089
|
+
0x2b: "#GETTING_DATA",
|
|
1090
|
+
};
|
|
1091
|
+
const code = errorCodes[Number(parsed.value)] || "#VALUE!";
|
|
1092
|
+
const errorExtendedValue = {
|
|
1093
|
+
ev: { type: "Error", name: code, message: code },
|
|
1094
|
+
};
|
|
1095
|
+
// If there's a formula, use it in ue.fv. Errors get a typed
|
|
1096
|
+
// `ev.ev` (not `sv`) so formula propagation treats them as errors.
|
|
1097
|
+
const ue = parsed.formula
|
|
1098
|
+
? { fv: `=${parsed.formula}` }
|
|
1099
|
+
: errorExtendedValue;
|
|
1100
|
+
return {
|
|
1101
|
+
...(style
|
|
1102
|
+
? { ef: style, uf: style }
|
|
1103
|
+
: hasFormat
|
|
1104
|
+
? { ef: format, uf: format }
|
|
1105
|
+
: {}),
|
|
1106
|
+
ue,
|
|
1107
|
+
ev: errorExtendedValue,
|
|
1108
|
+
...(parsed.formula ? {} : { fv: code }),
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
if (parsed.kind === "blank") {
|
|
1112
|
+
if (!hasFormat && !style)
|
|
1113
|
+
return null;
|
|
1114
|
+
return {
|
|
1115
|
+
...(style
|
|
1116
|
+
? { ef: style, uf: style }
|
|
1117
|
+
: hasFormat
|
|
1118
|
+
? { ef: format, uf: format }
|
|
1119
|
+
: {}),
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
return {
|
|
1123
|
+
...(style
|
|
1124
|
+
? { ef: style, uf: style }
|
|
1125
|
+
: hasFormat
|
|
1126
|
+
? { ef: format, uf: format }
|
|
1127
|
+
: {}),
|
|
1128
|
+
ue: { sv: "#VALUE!" },
|
|
1129
|
+
ev: { sv: "#VALUE!" },
|
|
1130
|
+
fv: "#VALUE!",
|
|
1131
|
+
};
|
|
1132
|
+
};
|
|
1133
|
+
const isCellRecord = (type) => type === RT.BrtCellBlank ||
|
|
1134
|
+
type === RT.BrtCellRk ||
|
|
1135
|
+
type === RT.BrtCellError ||
|
|
1136
|
+
type === RT.BrtCellBool ||
|
|
1137
|
+
type === RT.BrtCellReal ||
|
|
1138
|
+
type === RT.BrtCellSt ||
|
|
1139
|
+
type === RT.BrtCellIsst ||
|
|
1140
|
+
type === RT.BrtFmlaString ||
|
|
1141
|
+
type === RT.BrtFmlaNum ||
|
|
1142
|
+
type === RT.BrtFmlaBool ||
|
|
1143
|
+
type === RT.BrtFmlaError ||
|
|
1144
|
+
type === RT.BrtShortBlank ||
|
|
1145
|
+
type === RT.BrtShortRk ||
|
|
1146
|
+
type === RT.BrtShortError ||
|
|
1147
|
+
type === RT.BrtShortBool ||
|
|
1148
|
+
type === RT.BrtShortReal ||
|
|
1149
|
+
type === RT.BrtShortSt ||
|
|
1150
|
+
type === RT.BrtShortIsst;
|
|
1151
|
+
export class XlsbPlugin extends BaseWorkbookPlugin {
|
|
1152
|
+
zipParser = new StreamingZipParser();
|
|
1153
|
+
xmlParser = new XMLParser({
|
|
1154
|
+
ignoreAttributes: false,
|
|
1155
|
+
attributesGroupName: "a$",
|
|
1156
|
+
attributeNamePrefix: "",
|
|
1157
|
+
textNodeName: "t$",
|
|
1158
|
+
trimValues: false,
|
|
1159
|
+
ignoreDeclaration: true,
|
|
1160
|
+
});
|
|
1161
|
+
sheets = [];
|
|
1162
|
+
sharedStrings = [];
|
|
1163
|
+
sharedFormulaMap = new Map();
|
|
1164
|
+
cellXfs = new Map();
|
|
1165
|
+
styles = {
|
|
1166
|
+
numberFormats: new Map(standardExcelFormatCodeById),
|
|
1167
|
+
styleXfs: [],
|
|
1168
|
+
cellXfs: [],
|
|
1169
|
+
fills: [],
|
|
1170
|
+
fonts: [],
|
|
1171
|
+
borders: [],
|
|
1172
|
+
};
|
|
1173
|
+
theme = defaultSpreadsheetTheme;
|
|
1174
|
+
namedRanges = [];
|
|
1175
|
+
calcProp = {};
|
|
1176
|
+
supportingLinks = [];
|
|
1177
|
+
externSheetRefs = [];
|
|
1178
|
+
use1904 = false;
|
|
1179
|
+
async load(buffer, password) {
|
|
1180
|
+
await this.zipParser.load(buffer, password);
|
|
1181
|
+
if (!this.zipParser.has("xl/workbook.bin")) {
|
|
1182
|
+
throw new Error("Invalid XLSB file: xl/workbook.bin not found");
|
|
1183
|
+
}
|
|
1184
|
+
// Initialize shared formula map
|
|
1185
|
+
if (!this.sharedFormulaMap) {
|
|
1186
|
+
this.sharedFormulaMap = new Map();
|
|
1187
|
+
}
|
|
1188
|
+
this.sharedFormulaMap.clear();
|
|
1189
|
+
const workbookBinary = await this.zipParser.getBinary("xl/workbook.bin");
|
|
1190
|
+
const records = readAllRecordHeaders(workbookBinary);
|
|
1191
|
+
const workbookRels = await this.loadWorkbookRelationships();
|
|
1192
|
+
const sheets = [];
|
|
1193
|
+
const namedRanges = [];
|
|
1194
|
+
const supportingLinks = [];
|
|
1195
|
+
let externSheetRefs = [];
|
|
1196
|
+
for (const record of records) {
|
|
1197
|
+
const payload = workbookBinary.subarray(record.dataOffset, record.nextOffset);
|
|
1198
|
+
if (record.type === RT.BrtBundleSh) {
|
|
1199
|
+
const parsed = parseBundleSheet(payload);
|
|
1200
|
+
// Skip veryHidden sheets (hidden === 2) - these are internal sheets not meant for users
|
|
1201
|
+
if (parsed.hidden === 2) {
|
|
1202
|
+
continue;
|
|
1203
|
+
}
|
|
1204
|
+
const relTarget = workbookRels.get(parsed.rid);
|
|
1205
|
+
const path = relTarget
|
|
1206
|
+
? resolveRelativePath("xl/workbook.bin", relTarget)
|
|
1207
|
+
: undefined;
|
|
1208
|
+
sheets.push({
|
|
1209
|
+
sheetId: parsed.tabId || sheets.length + 1,
|
|
1210
|
+
title: parsed.title || `Sheet${sheets.length + 1}`,
|
|
1211
|
+
hidden: parsed.hidden === 1,
|
|
1212
|
+
rid: parsed.rid,
|
|
1213
|
+
path,
|
|
1214
|
+
rowCount: 0,
|
|
1215
|
+
columnCount: 0,
|
|
1216
|
+
tablePartRelIds: [],
|
|
1217
|
+
merges: [],
|
|
1218
|
+
rowMetadata: [],
|
|
1219
|
+
columnMetadata: [],
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
else if (record.type === RT.BrtSupSelf) {
|
|
1223
|
+
supportingLinks.push("self");
|
|
1224
|
+
}
|
|
1225
|
+
else if (record.type === RT.BrtSupSame) {
|
|
1226
|
+
supportingLinks.push("same");
|
|
1227
|
+
}
|
|
1228
|
+
else if (record.type === RT.BrtSupBookSrc) {
|
|
1229
|
+
supportingLinks.push("external");
|
|
1230
|
+
}
|
|
1231
|
+
else if (record.type === RT.BrtExternSheet) {
|
|
1232
|
+
const parsed = parseBrtExternSheet(payload);
|
|
1233
|
+
if (parsed.length > 0) {
|
|
1234
|
+
externSheetRefs = parsed;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
else if (record.type === RT.BrtName) {
|
|
1238
|
+
const namedRange = this.parseBrtName(payload, sheets);
|
|
1239
|
+
if (namedRange) {
|
|
1240
|
+
namedRanges.push(namedRange);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
else if (record.type === RT.BrtCalcProp) {
|
|
1244
|
+
this.parseBrtCalcProp(payload);
|
|
1245
|
+
}
|
|
1246
|
+
else if (record.type === RT.BrtWbProp) {
|
|
1247
|
+
const parsed = parseBrtWbPropPayload(payload);
|
|
1248
|
+
if (parsed) {
|
|
1249
|
+
this.use1904 = parsed.use1904;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
this.namedRanges = namedRanges;
|
|
1254
|
+
this.supportingLinks = supportingLinks;
|
|
1255
|
+
this.externSheetRefs = externSheetRefs;
|
|
1256
|
+
this.sheets = sheets;
|
|
1257
|
+
this.sharedStrings = await this.loadSharedStrings();
|
|
1258
|
+
this.styles = await this.loadStyles();
|
|
1259
|
+
this.theme = await this.loadTheme();
|
|
1260
|
+
for (const sheet of this.sheets) {
|
|
1261
|
+
if (!sheet.path || !this.zipParser.has(sheet.path))
|
|
1262
|
+
continue;
|
|
1263
|
+
const stats = await this.scanSheetStats(sheet.path);
|
|
1264
|
+
sheet.rowCount = stats.rowCount;
|
|
1265
|
+
sheet.columnCount = stats.columnCount;
|
|
1266
|
+
sheet.merges = stats.merges;
|
|
1267
|
+
sheet.rowMetadata = stats.rowMetadata;
|
|
1268
|
+
sheet.columnMetadata = stats.columnMetadata;
|
|
1269
|
+
sheet.showGridLines = stats.showGridLines;
|
|
1270
|
+
sheet.frozenRowCount = stats.frozenRowCount;
|
|
1271
|
+
sheet.frozenColumnCount = stats.frozenColumnCount;
|
|
1272
|
+
sheet.basicFilterRange = stats.basicFilterRange;
|
|
1273
|
+
sheet.tablePartRelIds = stats.tablePartRelIds;
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
dispose() {
|
|
1277
|
+
void this.zipParser.dispose();
|
|
1278
|
+
}
|
|
1279
|
+
async loadWorkbookRelationships() {
|
|
1280
|
+
const relsPath = "xl/_rels/workbook.bin.rels";
|
|
1281
|
+
const relMap = new Map();
|
|
1282
|
+
if (!this.zipParser.has(relsPath)) {
|
|
1283
|
+
return relMap;
|
|
1284
|
+
}
|
|
1285
|
+
const relText = await this.zipParser.getText(relsPath);
|
|
1286
|
+
const rels = this.xmlParser.parse(relText);
|
|
1287
|
+
const relations = get(rels, "Relationships.Relationship") ?? [];
|
|
1288
|
+
const list = Array.isArray(relations) ? relations : [relations];
|
|
1289
|
+
for (const rel of list) {
|
|
1290
|
+
const id = rel?.a$?.Id ?? rel?.Id;
|
|
1291
|
+
const target = rel?.a$?.Target ?? rel?.Target;
|
|
1292
|
+
if (typeof id === "string" && typeof target === "string") {
|
|
1293
|
+
relMap.set(id, target);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
return relMap;
|
|
1297
|
+
}
|
|
1298
|
+
async loadSharedStrings() {
|
|
1299
|
+
const path = "xl/sharedStrings.bin";
|
|
1300
|
+
if (!this.zipParser.has(path))
|
|
1301
|
+
return [];
|
|
1302
|
+
const binary = await this.zipParser.getBinary(path);
|
|
1303
|
+
const records = readAllRecordHeaders(binary);
|
|
1304
|
+
const result = [];
|
|
1305
|
+
for (const record of records) {
|
|
1306
|
+
if (record.type !== RT.BrtSSTItem)
|
|
1307
|
+
continue;
|
|
1308
|
+
const payload = binary.subarray(record.dataOffset, record.nextOffset);
|
|
1309
|
+
const reader = new Reader(payload);
|
|
1310
|
+
reader.readUInt8(); // flags
|
|
1311
|
+
result.push(reader.readXLWideString());
|
|
1312
|
+
}
|
|
1313
|
+
return result;
|
|
1314
|
+
}
|
|
1315
|
+
async loadStyles() {
|
|
1316
|
+
const path = "xl/styles.bin";
|
|
1317
|
+
const styles = {
|
|
1318
|
+
numberFormats: new Map(standardExcelFormatCodeById),
|
|
1319
|
+
styleXfs: [],
|
|
1320
|
+
cellXfs: [],
|
|
1321
|
+
fills: [],
|
|
1322
|
+
fonts: [],
|
|
1323
|
+
borders: [],
|
|
1324
|
+
};
|
|
1325
|
+
if (!this.zipParser.has(path))
|
|
1326
|
+
return styles;
|
|
1327
|
+
const binary = await this.zipParser.getBinary(path);
|
|
1328
|
+
const records = readAllRecordHeaders(binary);
|
|
1329
|
+
let inCellXfs = false;
|
|
1330
|
+
let inStyleXfs = false;
|
|
1331
|
+
let inFonts = false;
|
|
1332
|
+
let inFills = false;
|
|
1333
|
+
let inBorders = false;
|
|
1334
|
+
let inFmts = false;
|
|
1335
|
+
for (const record of records) {
|
|
1336
|
+
const payload = binary.subarray(record.dataOffset, record.nextOffset);
|
|
1337
|
+
if (record.type === RT.BrtBeginFonts) {
|
|
1338
|
+
inFonts = true;
|
|
1339
|
+
continue;
|
|
1340
|
+
}
|
|
1341
|
+
if (record.type === RT.BrtEndFonts) {
|
|
1342
|
+
inFonts = false;
|
|
1343
|
+
continue;
|
|
1344
|
+
}
|
|
1345
|
+
if (record.type === RT.BrtBeginFills) {
|
|
1346
|
+
inFills = true;
|
|
1347
|
+
continue;
|
|
1348
|
+
}
|
|
1349
|
+
if (record.type === RT.BrtEndFills) {
|
|
1350
|
+
inFills = false;
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
if (record.type === RT.BrtBeginBorders) {
|
|
1354
|
+
inBorders = true;
|
|
1355
|
+
continue;
|
|
1356
|
+
}
|
|
1357
|
+
if (record.type === RT.BrtEndBorders) {
|
|
1358
|
+
inBorders = false;
|
|
1359
|
+
continue;
|
|
1360
|
+
}
|
|
1361
|
+
if (record.type === RT.BrtBeginFmts) {
|
|
1362
|
+
inFmts = true;
|
|
1363
|
+
continue;
|
|
1364
|
+
}
|
|
1365
|
+
if (record.type === RT.BrtEndFmts) {
|
|
1366
|
+
inFmts = false;
|
|
1367
|
+
continue;
|
|
1368
|
+
}
|
|
1369
|
+
if (record.type === RT.BrtBeginCellXFs) {
|
|
1370
|
+
inCellXfs = true;
|
|
1371
|
+
continue;
|
|
1372
|
+
}
|
|
1373
|
+
if (record.type === RT.BrtEndCellXFs) {
|
|
1374
|
+
inCellXfs = false;
|
|
1375
|
+
continue;
|
|
1376
|
+
}
|
|
1377
|
+
if (record.type === RT.BrtBeginCellStyleXFs) {
|
|
1378
|
+
inStyleXfs = true;
|
|
1379
|
+
continue;
|
|
1380
|
+
}
|
|
1381
|
+
if (record.type === RT.BrtEndCellStyleXFs) {
|
|
1382
|
+
inStyleXfs = false;
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
if (record.type === RT.BrtFmt && inFmts) {
|
|
1386
|
+
const { id, formatCode } = parseBrtFmt(payload);
|
|
1387
|
+
styles.numberFormats.set(id, formatCode);
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
if (record.type === RT.BrtFont && inFonts) {
|
|
1391
|
+
styles.fonts.push(parseBrtFont(payload));
|
|
1392
|
+
continue;
|
|
1393
|
+
}
|
|
1394
|
+
if (record.type === RT.BrtFill && inFills) {
|
|
1395
|
+
styles.fills.push(parseBrtFill(payload));
|
|
1396
|
+
continue;
|
|
1397
|
+
}
|
|
1398
|
+
if (record.type === RT.BrtBorder && inBorders) {
|
|
1399
|
+
styles.borders.push(parseBrtBorder(payload));
|
|
1400
|
+
continue;
|
|
1401
|
+
}
|
|
1402
|
+
if (record.type === RT.BrtXF && inStyleXfs) {
|
|
1403
|
+
styles.styleXfs.push(parseBrtXf(payload));
|
|
1404
|
+
continue;
|
|
1405
|
+
}
|
|
1406
|
+
if (record.type === RT.BrtXF && inCellXfs) {
|
|
1407
|
+
styles.cellXfs.push(parseBrtXf(payload));
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
return styles;
|
|
1411
|
+
}
|
|
1412
|
+
async loadTheme() {
|
|
1413
|
+
try {
|
|
1414
|
+
if (!this.zipParser.has("xl/theme/theme1.xml")) {
|
|
1415
|
+
return defaultSpreadsheetTheme;
|
|
1416
|
+
}
|
|
1417
|
+
const themeContent = await this.zipParser.getText("xl/theme/theme1.xml");
|
|
1418
|
+
const themeXml = this.xmlParser.parse(themeContent);
|
|
1419
|
+
const themeName = get(themeXml, "a:theme.a$.name") || "Office";
|
|
1420
|
+
const { primaryFontFamily, headingFontFamily } = extractThemeFonts(themeXml);
|
|
1421
|
+
const themeColors = extractThemeColors(themeXml, defaultSpreadsheetTheme);
|
|
1422
|
+
const darkThemeColors = generateDarkThemeColors(themeColors);
|
|
1423
|
+
return {
|
|
1424
|
+
name: themeName,
|
|
1425
|
+
primaryFontFamily,
|
|
1426
|
+
headingFontFamily,
|
|
1427
|
+
themeColors,
|
|
1428
|
+
darkThemeColors,
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
catch {
|
|
1432
|
+
return defaultSpreadsheetTheme;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
getTheme() {
|
|
1436
|
+
return this.theme ?? defaultSpreadsheetTheme;
|
|
1437
|
+
}
|
|
1438
|
+
parseRelIdFromPayload(payload) {
|
|
1439
|
+
const reader = new Reader(payload);
|
|
1440
|
+
const relId = reader.readXLNullableWideString();
|
|
1441
|
+
return relId || undefined;
|
|
1442
|
+
}
|
|
1443
|
+
async loadRelationshipsForPart(basePath) {
|
|
1444
|
+
const base = normalizePath(basePath);
|
|
1445
|
+
const name = base.split("/").pop();
|
|
1446
|
+
if (!name)
|
|
1447
|
+
return new Map();
|
|
1448
|
+
const dir = base.substring(0, base.lastIndexOf("/"));
|
|
1449
|
+
const relsPath = `${dir}/_rels/${name}.rels`;
|
|
1450
|
+
if (!this.zipParser.has(relsPath))
|
|
1451
|
+
return new Map();
|
|
1452
|
+
const relText = await this.zipParser.getText(relsPath);
|
|
1453
|
+
const rels = this.xmlParser.parse(relText);
|
|
1454
|
+
const relations = get(rels, "Relationships.Relationship") ?? [];
|
|
1455
|
+
const list = Array.isArray(relations) ? relations : [relations];
|
|
1456
|
+
const out = new Map();
|
|
1457
|
+
for (const rel of list) {
|
|
1458
|
+
const id = rel?.a$?.Id ?? rel?.Id;
|
|
1459
|
+
const target = rel?.a$?.Target ?? rel?.Target;
|
|
1460
|
+
if (typeof id === "string" && typeof target === "string") {
|
|
1461
|
+
out.set(id, target);
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
return out;
|
|
1465
|
+
}
|
|
1466
|
+
async getDrawingPathsForSheet(sheet) {
|
|
1467
|
+
if (!sheet.path || !this.zipParser.has(sheet.path))
|
|
1468
|
+
return [];
|
|
1469
|
+
const binary = await this.zipParser.getBinary(sheet.path);
|
|
1470
|
+
const records = readAllRecordHeaders(binary);
|
|
1471
|
+
const relIds = [];
|
|
1472
|
+
for (const record of records) {
|
|
1473
|
+
if (record.type !== RT.BrtDrawing)
|
|
1474
|
+
continue;
|
|
1475
|
+
const payload = binary.subarray(record.dataOffset, record.nextOffset);
|
|
1476
|
+
const relId = this.parseRelIdFromPayload(payload);
|
|
1477
|
+
if (relId)
|
|
1478
|
+
relIds.push(relId);
|
|
1479
|
+
}
|
|
1480
|
+
if (relIds.length === 0)
|
|
1481
|
+
return [];
|
|
1482
|
+
const relMap = await this.loadRelationshipsForPart(sheet.path);
|
|
1483
|
+
const paths = [];
|
|
1484
|
+
for (const relId of relIds) {
|
|
1485
|
+
const target = relMap.get(relId);
|
|
1486
|
+
if (!target)
|
|
1487
|
+
continue;
|
|
1488
|
+
paths.push(resolveRelativePath(sheet.path, target));
|
|
1489
|
+
}
|
|
1490
|
+
return Array.from(new Set(paths));
|
|
1491
|
+
}
|
|
1492
|
+
async getTablesFromSheet(sheet) {
|
|
1493
|
+
if (!sheet.path || !sheet.tablePartRelIds.length)
|
|
1494
|
+
return [];
|
|
1495
|
+
const relMap = await this.loadRelationshipsForPart(sheet.path);
|
|
1496
|
+
const tables = [];
|
|
1497
|
+
for (const tableRelId of sheet.tablePartRelIds) {
|
|
1498
|
+
const target = relMap.get(tableRelId);
|
|
1499
|
+
if (!target)
|
|
1500
|
+
continue;
|
|
1501
|
+
const tablePath = resolveRelativePath(sheet.path, target);
|
|
1502
|
+
if (!this.zipParser.has(tablePath))
|
|
1503
|
+
continue;
|
|
1504
|
+
// XLSB workbooks written by Excel store table parts as BIFF12
|
|
1505
|
+
// binaries (xl/tables/tableN.bin) — those need a binary parser and
|
|
1506
|
+
// must not be fed to the XML parser. Only XML table parts (produced
|
|
1507
|
+
// by some third-party writers) are handled here; anything else is
|
|
1508
|
+
// skipped so one table part can never fail the whole import.
|
|
1509
|
+
if (tablePath.endsWith(".bin"))
|
|
1510
|
+
continue;
|
|
1511
|
+
const tableContent = await this.zipParser.getText(tablePath);
|
|
1512
|
+
let tableData;
|
|
1513
|
+
try {
|
|
1514
|
+
tableData = this.xmlParser.parse(tableContent || "");
|
|
1515
|
+
}
|
|
1516
|
+
catch {
|
|
1517
|
+
continue;
|
|
1518
|
+
}
|
|
1519
|
+
const tableDefinition = get(tableData, "table") || get(tableData, "x:table");
|
|
1520
|
+
if (!tableDefinition)
|
|
1521
|
+
continue;
|
|
1522
|
+
const attrs = tableDefinition.a$ || {};
|
|
1523
|
+
const range = getRangeFromAddressRef(attrs.ref);
|
|
1524
|
+
if (!range)
|
|
1525
|
+
continue;
|
|
1526
|
+
const columnDefinitions = get(tableDefinition, "tableColumns.tableColumn") ||
|
|
1527
|
+
get(tableDefinition, "x:tableColumns.x:tableColumn");
|
|
1528
|
+
const columnDefsArray = Array.isArray(columnDefinitions)
|
|
1529
|
+
? columnDefinitions
|
|
1530
|
+
: columnDefinitions
|
|
1531
|
+
? [columnDefinitions]
|
|
1532
|
+
: [];
|
|
1533
|
+
const columns = columnDefsArray.map((colDef) => {
|
|
1534
|
+
const name = colDef?.a$?.name || "";
|
|
1535
|
+
const calculatedColumnFormula = get(colDef, "calculatedColumnFormula") ||
|
|
1536
|
+
get(colDef, "x:calculatedColumnFormula");
|
|
1537
|
+
if (calculatedColumnFormula) {
|
|
1538
|
+
return { name, formula: calculatedColumnFormula };
|
|
1539
|
+
}
|
|
1540
|
+
return { name };
|
|
1541
|
+
});
|
|
1542
|
+
const autoFilter = tableDefinition.autoFilter ?? tableDefinition["x:autoFilter"];
|
|
1543
|
+
const filterSpecs = createFilterSpecsFromAutoFilter(autoFilter);
|
|
1544
|
+
const tableStyle = get(tableDefinition, "tableStyleInfo") ||
|
|
1545
|
+
get(tableDefinition, "x:tableStyleInfo") ||
|
|
1546
|
+
{};
|
|
1547
|
+
const tableStyleAttrs = tableStyle.a$ || {};
|
|
1548
|
+
const tableView = {
|
|
1549
|
+
id: attrs.id || uuid(),
|
|
1550
|
+
title: attrs.name || attrs.displayName || `Table_${tables.length + 1}`,
|
|
1551
|
+
sheetId: sheet.sheetId,
|
|
1552
|
+
headerRow: attrs.headerRowCount !== "0",
|
|
1553
|
+
totalRow: attrs.totalsRowCount === "1",
|
|
1554
|
+
showRowStripes: attrs.showRowStripes !== "0",
|
|
1555
|
+
showColumnStripes: attrs.showColumnStripes === "1",
|
|
1556
|
+
showFirstColumn: attrs.showFirstColumn === "1",
|
|
1557
|
+
showLastColumn: attrs.showLastColumn === "1",
|
|
1558
|
+
filterButton: attrs.filterButton !== "0",
|
|
1559
|
+
columns: columns,
|
|
1560
|
+
range,
|
|
1561
|
+
filterSpecs,
|
|
1562
|
+
};
|
|
1563
|
+
if (tableStyleAttrs.name) {
|
|
1564
|
+
tableView.theme = tableStyleAttrs.name;
|
|
1565
|
+
}
|
|
1566
|
+
tables.push(tableView);
|
|
1567
|
+
}
|
|
1568
|
+
return tables;
|
|
1569
|
+
}
|
|
1570
|
+
async scanSheetStats(path) {
|
|
1571
|
+
const binary = await this.zipParser.getBinary(path);
|
|
1572
|
+
const records = readAllRecordHeaders(binary);
|
|
1573
|
+
let currentRow = 0;
|
|
1574
|
+
let maxRow = 0;
|
|
1575
|
+
let maxCol = 0;
|
|
1576
|
+
let prevCol = -1;
|
|
1577
|
+
const merges = [];
|
|
1578
|
+
const rowMetadata = [];
|
|
1579
|
+
const columnMetadata = [];
|
|
1580
|
+
let showGridLines;
|
|
1581
|
+
let frozenRowCount;
|
|
1582
|
+
let frozenColumnCount;
|
|
1583
|
+
let basicFilterRange;
|
|
1584
|
+
const tablePartRelIds = [];
|
|
1585
|
+
for (const record of records) {
|
|
1586
|
+
const payload = binary.subarray(record.dataOffset, record.nextOffset);
|
|
1587
|
+
if (record.type === RT.BrtWsView) {
|
|
1588
|
+
const view = parseWsView(payload);
|
|
1589
|
+
showGridLines = view.showGridLines;
|
|
1590
|
+
continue;
|
|
1591
|
+
}
|
|
1592
|
+
if (record.type === RT.BrtPane) {
|
|
1593
|
+
const pane = parsePane(payload);
|
|
1594
|
+
if (pane.frozenRowCount > 0)
|
|
1595
|
+
frozenRowCount = pane.frozenRowCount;
|
|
1596
|
+
if (pane.frozenColumnCount > 0)
|
|
1597
|
+
frozenColumnCount = pane.frozenColumnCount;
|
|
1598
|
+
continue;
|
|
1599
|
+
}
|
|
1600
|
+
if (record.type === RT.BrtBeginAFilter) {
|
|
1601
|
+
basicFilterRange = parseRange(payload);
|
|
1602
|
+
continue;
|
|
1603
|
+
}
|
|
1604
|
+
if (record.type === RT.BrtListPart) {
|
|
1605
|
+
const relId = parseBrtListPartRelId(payload);
|
|
1606
|
+
if (relId)
|
|
1607
|
+
tablePartRelIds.push(relId);
|
|
1608
|
+
continue;
|
|
1609
|
+
}
|
|
1610
|
+
if (record.type === RT.BrtRowHdr) {
|
|
1611
|
+
const parsedRow = parseRowHeader(payload);
|
|
1612
|
+
currentRow = parsedRow.row;
|
|
1613
|
+
prevCol = -1;
|
|
1614
|
+
if (currentRow > maxRow)
|
|
1615
|
+
maxRow = currentRow;
|
|
1616
|
+
if (parsedRow.hidden || parsedRow.hpt != null) {
|
|
1617
|
+
rowMetadata[currentRow] = {
|
|
1618
|
+
...(parsedRow.hidden ? { hiddenByUser: true } : {}),
|
|
1619
|
+
...(parsedRow.hpt != null
|
|
1620
|
+
? {
|
|
1621
|
+
size: Math.round((parsedRow.hpt * 96) / 72),
|
|
1622
|
+
resizedByUser: true,
|
|
1623
|
+
}
|
|
1624
|
+
: {}),
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
continue;
|
|
1628
|
+
}
|
|
1629
|
+
if (record.type === RT.BrtColInfo) {
|
|
1630
|
+
const parsedCol = parseBrtColInfo(payload);
|
|
1631
|
+
for (let col = parsedCol.startCol; col <= parsedCol.endCol; col++) {
|
|
1632
|
+
const dimensionProps = {};
|
|
1633
|
+
if (parsedCol.hidden)
|
|
1634
|
+
dimensionProps.hiddenByUser = true;
|
|
1635
|
+
if (parsedCol.size > 0) {
|
|
1636
|
+
dimensionProps.size = parsedCol.size;
|
|
1637
|
+
if (parsedCol.customWidth) {
|
|
1638
|
+
dimensionProps.resizedByUser = true;
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
if (Object.keys(dimensionProps).length > 0) {
|
|
1642
|
+
columnMetadata[col] = dimensionProps;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
continue;
|
|
1646
|
+
}
|
|
1647
|
+
if (record.type === RT.BrtMergeCell) {
|
|
1648
|
+
const merge = parseMergeRange(payload);
|
|
1649
|
+
merges.push(merge);
|
|
1650
|
+
if (merge.endRowIndex > maxRow)
|
|
1651
|
+
maxRow = merge.endRowIndex;
|
|
1652
|
+
if (merge.endColumnIndex > maxCol)
|
|
1653
|
+
maxCol = merge.endColumnIndex;
|
|
1654
|
+
continue;
|
|
1655
|
+
}
|
|
1656
|
+
if (!isCellRecord(record.type))
|
|
1657
|
+
continue;
|
|
1658
|
+
const parsed = parseCellFromRecord(record.type, payload, currentRow, prevCol, (ixti) => this.resolveSheetNameByXti(ixti), undefined, false);
|
|
1659
|
+
if (!parsed)
|
|
1660
|
+
continue;
|
|
1661
|
+
prevCol = parsed.col - 1;
|
|
1662
|
+
if (parsed.row > maxRow)
|
|
1663
|
+
maxRow = parsed.row;
|
|
1664
|
+
if (parsed.col > maxCol)
|
|
1665
|
+
maxCol = parsed.col;
|
|
1666
|
+
}
|
|
1667
|
+
return {
|
|
1668
|
+
rowCount: maxRow,
|
|
1669
|
+
columnCount: maxCol,
|
|
1670
|
+
merges,
|
|
1671
|
+
rowMetadata,
|
|
1672
|
+
columnMetadata,
|
|
1673
|
+
showGridLines,
|
|
1674
|
+
frozenRowCount,
|
|
1675
|
+
frozenColumnCount,
|
|
1676
|
+
basicFilterRange,
|
|
1677
|
+
tablePartRelIds,
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1680
|
+
getSheetCount() {
|
|
1681
|
+
return this.sheets.length;
|
|
1682
|
+
}
|
|
1683
|
+
getSheetNames() {
|
|
1684
|
+
return this.sheets.map((s) => s.title);
|
|
1685
|
+
}
|
|
1686
|
+
resolveSheetNameByXti(ixti) {
|
|
1687
|
+
const sheetNames = this.getSheetNames();
|
|
1688
|
+
const fallback = sheetNames[ixti] ?? sheetNames[ixti - 1];
|
|
1689
|
+
const xti = this.externSheetRefs[ixti];
|
|
1690
|
+
if (!xti)
|
|
1691
|
+
return fallback;
|
|
1692
|
+
const linkType = this.supportingLinks[xti.iSupBook];
|
|
1693
|
+
if (linkType !== "self" && linkType !== "same") {
|
|
1694
|
+
return fallback;
|
|
1695
|
+
}
|
|
1696
|
+
const first = xti.itabFirst;
|
|
1697
|
+
const last = xti.itabLast;
|
|
1698
|
+
if (first < 0 || first >= sheetNames.length)
|
|
1699
|
+
return fallback;
|
|
1700
|
+
if (last < 0 || last >= sheetNames.length)
|
|
1701
|
+
return sheetNames[first];
|
|
1702
|
+
if (first === last)
|
|
1703
|
+
return sheetNames[first];
|
|
1704
|
+
return `${sheetNames[first]}:${sheetNames[last]}`;
|
|
1705
|
+
}
|
|
1706
|
+
async getSheets(minRowCount = 0, minColumnCount = 0) {
|
|
1707
|
+
const tables = [];
|
|
1708
|
+
const sheets = [];
|
|
1709
|
+
for (const sheet of this.sheets) {
|
|
1710
|
+
const sheetTables = await this.getTablesFromSheet(sheet);
|
|
1711
|
+
tables.push(...sheetTables);
|
|
1712
|
+
const matchedFilterSpecs = sheetTables.find((t) => sheet.basicFilterRange &&
|
|
1713
|
+
t.range.startRowIndex === sheet.basicFilterRange.startRowIndex &&
|
|
1714
|
+
t.range.endRowIndex === sheet.basicFilterRange.endRowIndex &&
|
|
1715
|
+
t.range.startColumnIndex ===
|
|
1716
|
+
sheet.basicFilterRange.startColumnIndex &&
|
|
1717
|
+
t.range.endColumnIndex === sheet.basicFilterRange.endColumnIndex)?.filterSpecs;
|
|
1718
|
+
sheets.push({
|
|
1719
|
+
sheetId: sheet.sheetId,
|
|
1720
|
+
title: sheet.title,
|
|
1721
|
+
hidden: sheet.hidden,
|
|
1722
|
+
rowCount: Math.max(minRowCount, sheet.rowCount),
|
|
1723
|
+
columnCount: Math.max(minColumnCount, sheet.columnCount),
|
|
1724
|
+
rowMetadata: sheet.rowMetadata,
|
|
1725
|
+
columnMetadata: sheet.columnMetadata,
|
|
1726
|
+
merges: sheet.merges,
|
|
1727
|
+
...(sheet.showGridLines !== undefined
|
|
1728
|
+
? { showGridLines: sheet.showGridLines }
|
|
1729
|
+
: {}),
|
|
1730
|
+
...(sheet.frozenRowCount !== undefined && sheet.frozenRowCount > 0
|
|
1731
|
+
? { frozenRowCount: sheet.frozenRowCount }
|
|
1732
|
+
: {}),
|
|
1733
|
+
...(sheet.frozenColumnCount !== undefined && sheet.frozenColumnCount > 0
|
|
1734
|
+
? { frozenColumnCount: sheet.frozenColumnCount }
|
|
1735
|
+
: {}),
|
|
1736
|
+
...(sheet.basicFilterRange
|
|
1737
|
+
? {
|
|
1738
|
+
basicFilter: {
|
|
1739
|
+
id: uuid(),
|
|
1740
|
+
range: sheet.basicFilterRange,
|
|
1741
|
+
filterSpecs: matchedFilterSpecs ?? [],
|
|
1742
|
+
},
|
|
1743
|
+
}
|
|
1744
|
+
: {}),
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1747
|
+
return [sheets, tables];
|
|
1748
|
+
}
|
|
1749
|
+
getSharedStrings() {
|
|
1750
|
+
return this.sharedStrings;
|
|
1751
|
+
}
|
|
1752
|
+
getStyles() {
|
|
1753
|
+
return this.styles;
|
|
1754
|
+
}
|
|
1755
|
+
getFonts() {
|
|
1756
|
+
const out = new Set();
|
|
1757
|
+
for (const font of this.styles.fonts) {
|
|
1758
|
+
if (font.name)
|
|
1759
|
+
out.add(font.name);
|
|
1760
|
+
}
|
|
1761
|
+
return [...out];
|
|
1762
|
+
}
|
|
1763
|
+
getCellXfs() {
|
|
1764
|
+
return this.cellXfs;
|
|
1765
|
+
}
|
|
1766
|
+
registerFormat(format) {
|
|
1767
|
+
const key = getCellXfsKey(format);
|
|
1768
|
+
if (!this.cellXfs.has(key)) {
|
|
1769
|
+
this.cellXfs.set(key, format);
|
|
1770
|
+
}
|
|
1771
|
+
return key;
|
|
1772
|
+
}
|
|
1773
|
+
async *processSheetData(chunkSize = 100, enableCellXfsRegistry = false, enableSharedStrings = true) {
|
|
1774
|
+
const totalSheets = this.sheets.length;
|
|
1775
|
+
for (let index = 0; index < this.sheets.length; index++) {
|
|
1776
|
+
const sheet = this.sheets[index];
|
|
1777
|
+
if (!sheet.path || !this.zipParser.has(sheet.path))
|
|
1778
|
+
continue;
|
|
1779
|
+
const binary = await this.zipParser.getBinary(sheet.path);
|
|
1780
|
+
const records = readAllRecordHeaders(binary);
|
|
1781
|
+
const sharedFormulaBytes = new Map();
|
|
1782
|
+
const sharedFormulaRanges = [];
|
|
1783
|
+
// Pre-scan shared formulas so PtgExp can resolve even when BrtShrFmla
|
|
1784
|
+
// appears later in the record stream.
|
|
1785
|
+
for (const record of records) {
|
|
1786
|
+
if (record.type !== RT.BrtShrFmla)
|
|
1787
|
+
continue;
|
|
1788
|
+
const payload = binary.subarray(record.dataOffset, record.nextOffset);
|
|
1789
|
+
const parsedShared = parseBrtShrFmla(payload);
|
|
1790
|
+
if (!parsedShared)
|
|
1791
|
+
continue;
|
|
1792
|
+
sharedFormulaBytes.set(`${parsedShared.startRow}:${parsedShared.startCol}`, parsedShared.rgceBytes);
|
|
1793
|
+
sharedFormulaRanges.push(parsedShared);
|
|
1794
|
+
}
|
|
1795
|
+
let currentRow = 0;
|
|
1796
|
+
let prevCol = -1;
|
|
1797
|
+
const rows = new Map();
|
|
1798
|
+
for (const record of records) {
|
|
1799
|
+
const payload = binary.subarray(record.dataOffset, record.nextOffset);
|
|
1800
|
+
if (record.type === RT.BrtShrFmla) {
|
|
1801
|
+
continue;
|
|
1802
|
+
}
|
|
1803
|
+
if (record.type === RT.BrtRowHdr) {
|
|
1804
|
+
const parsedRow = parseRowHeader(payload);
|
|
1805
|
+
currentRow = parsedRow.row;
|
|
1806
|
+
prevCol = -1;
|
|
1807
|
+
if (!rows.has(currentRow)) {
|
|
1808
|
+
rows.set(currentRow, { values: [] });
|
|
1809
|
+
}
|
|
1810
|
+
continue;
|
|
1811
|
+
}
|
|
1812
|
+
if (!isCellRecord(record.type))
|
|
1813
|
+
continue;
|
|
1814
|
+
const parsed = parseCellFromRecord(record.type, payload, currentRow, prevCol, (ixti) => this.resolveSheetNameByXti(ixti), (sourceRow, sourceCol, targetRow, targetCol) => {
|
|
1815
|
+
let rgce = sharedFormulaBytes.get(`${sourceRow}:${sourceCol}`);
|
|
1816
|
+
if (!rgce) {
|
|
1817
|
+
const sourceRange = sharedFormulaRanges.find((range) => sourceRow >= range.startRow &&
|
|
1818
|
+
sourceRow <= range.endRow &&
|
|
1819
|
+
sourceCol >= range.startCol &&
|
|
1820
|
+
sourceCol <= range.endCol);
|
|
1821
|
+
rgce = sourceRange?.rgceBytes;
|
|
1822
|
+
}
|
|
1823
|
+
if (!rgce)
|
|
1824
|
+
return undefined;
|
|
1825
|
+
return parseRgce(rgce, (ixti) => this.resolveSheetNameByXti(ixti), {
|
|
1826
|
+
row: targetRow,
|
|
1827
|
+
col: targetCol,
|
|
1828
|
+
}, true); // Enable debug logging
|
|
1829
|
+
});
|
|
1830
|
+
if (!parsed)
|
|
1831
|
+
continue;
|
|
1832
|
+
if (!parsed.formula && sharedFormulaRanges.length > 0) {
|
|
1833
|
+
const sharedRange = sharedFormulaRanges.find((range) => parsed.row >= range.startRow &&
|
|
1834
|
+
parsed.row <= range.endRow &&
|
|
1835
|
+
parsed.col >= range.startCol &&
|
|
1836
|
+
parsed.col <= range.endCol);
|
|
1837
|
+
if (sharedRange) {
|
|
1838
|
+
const resolved = parseRgce(sharedRange.rgceBytes, (ixti) => this.resolveSheetNameByXti(ixti), { row: parsed.row, col: parsed.col }, true);
|
|
1839
|
+
if (resolved) {
|
|
1840
|
+
parsed.formula = resolved;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
prevCol = parsed.col - 1;
|
|
1845
|
+
let row = rows.get(parsed.row);
|
|
1846
|
+
if (!row) {
|
|
1847
|
+
row = { values: [] };
|
|
1848
|
+
rows.set(parsed.row, row);
|
|
1849
|
+
}
|
|
1850
|
+
row.values[parsed.col] = toCellData(parsed, this.styles, this.sharedStrings, enableCellXfsRegistry, enableSharedStrings, this.cellXfs);
|
|
1851
|
+
}
|
|
1852
|
+
// Apply merge cell formatting
|
|
1853
|
+
// In Excel, merged cells should have formatting from the top-left (anchor) cell
|
|
1854
|
+
// propagated to all cells in the merge range, but borders need special handling
|
|
1855
|
+
for (const merge of sheet.merges) {
|
|
1856
|
+
const topLeftRow = rows.get(merge.startRowIndex);
|
|
1857
|
+
const topLeftCell = topLeftRow?.values?.[merge.startColumnIndex];
|
|
1858
|
+
// Get the format from the top-left cell (if it exists)
|
|
1859
|
+
const sourceFormat = topLeftCell?.ef || topLeftCell?.uf;
|
|
1860
|
+
// Process all cells in the merge range
|
|
1861
|
+
// This ensures the merge structure exists even if there's no explicit formatting
|
|
1862
|
+
for (let r = merge.startRowIndex; r <= merge.endRowIndex; r++) {
|
|
1863
|
+
for (let c = merge.startColumnIndex; c <= merge.endColumnIndex; c++) {
|
|
1864
|
+
// Skip the top-left cell as it already has its data/format
|
|
1865
|
+
if (r === merge.startRowIndex && c === merge.startColumnIndex) {
|
|
1866
|
+
continue;
|
|
1867
|
+
}
|
|
1868
|
+
// Ensure row exists
|
|
1869
|
+
let row = rows.get(r);
|
|
1870
|
+
if (!row) {
|
|
1871
|
+
row = { values: [] };
|
|
1872
|
+
rows.set(r, row);
|
|
1873
|
+
}
|
|
1874
|
+
// Create format without borders for non-anchor cells
|
|
1875
|
+
// Borders should only appear on the outer edges of merged cells, not internally
|
|
1876
|
+
let format = sourceFormat;
|
|
1877
|
+
if (format && typeof format === "object" && "borders" in format) {
|
|
1878
|
+
const { borders, ...formatWithoutBorders } = format;
|
|
1879
|
+
format =
|
|
1880
|
+
Object.keys(formatWithoutBorders).length > 0
|
|
1881
|
+
? formatWithoutBorders
|
|
1882
|
+
: undefined;
|
|
1883
|
+
}
|
|
1884
|
+
const existingCell = row.values[c];
|
|
1885
|
+
if (existingCell) {
|
|
1886
|
+
// Cell exists - we need to remove borders even if there's no other format
|
|
1887
|
+
// Preserve cell content (values, formulas, etc.) but update formatting
|
|
1888
|
+
const { ef, uf, ...cellContent } = existingCell;
|
|
1889
|
+
if (format) {
|
|
1890
|
+
// Apply the format without borders
|
|
1891
|
+
row.values[c] = {
|
|
1892
|
+
...cellContent,
|
|
1893
|
+
ef: format,
|
|
1894
|
+
uf: format,
|
|
1895
|
+
};
|
|
1896
|
+
}
|
|
1897
|
+
else {
|
|
1898
|
+
// No format to apply, but remove any existing formatting to clear borders
|
|
1899
|
+
row.values[c] = cellContent;
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
else {
|
|
1903
|
+
// Create a blank cell with format (if available) for proper merge rendering
|
|
1904
|
+
if (format) {
|
|
1905
|
+
row.values[c] = {
|
|
1906
|
+
ef: format,
|
|
1907
|
+
uf: format,
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
else {
|
|
1911
|
+
// Create a blank cell even without format to preserve merge structure
|
|
1912
|
+
row.values[c] = {};
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
const sortedRows = Array.from(rows.entries()).sort((a, b) => a[0] - b[0]);
|
|
1919
|
+
let chunkRows = [];
|
|
1920
|
+
let chunkRowIndices = [];
|
|
1921
|
+
let chunkIndex = 0;
|
|
1922
|
+
let startRowIndex = -1;
|
|
1923
|
+
let endRowIndex = -1;
|
|
1924
|
+
for (const [rowIndex, rowData] of sortedRows) {
|
|
1925
|
+
if (chunkRows.length === 0) {
|
|
1926
|
+
startRowIndex = rowIndex;
|
|
1927
|
+
}
|
|
1928
|
+
chunkRows.push(rowData);
|
|
1929
|
+
chunkRowIndices.push(rowIndex);
|
|
1930
|
+
endRowIndex = rowIndex;
|
|
1931
|
+
if (chunkRows.length >= chunkSize) {
|
|
1932
|
+
yield {
|
|
1933
|
+
sheetId: sheet.sheetId,
|
|
1934
|
+
title: sheet.title,
|
|
1935
|
+
currentSheetIndex: index,
|
|
1936
|
+
totalSheets,
|
|
1937
|
+
chunkIndex,
|
|
1938
|
+
rows: chunkRows,
|
|
1939
|
+
rowIndices: chunkRowIndices,
|
|
1940
|
+
startRowIndex,
|
|
1941
|
+
endRowIndex,
|
|
1942
|
+
totalRows: sheet.rowCount,
|
|
1943
|
+
isLastChunk: false,
|
|
1944
|
+
cellXfs: this.cellXfs,
|
|
1945
|
+
};
|
|
1946
|
+
chunkRows = [];
|
|
1947
|
+
chunkRowIndices = [];
|
|
1948
|
+
chunkIndex += 1;
|
|
1949
|
+
startRowIndex = -1;
|
|
1950
|
+
endRowIndex = -1;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
if (chunkRows.length) {
|
|
1954
|
+
yield {
|
|
1955
|
+
sheetId: sheet.sheetId,
|
|
1956
|
+
title: sheet.title,
|
|
1957
|
+
currentSheetIndex: index,
|
|
1958
|
+
totalSheets,
|
|
1959
|
+
chunkIndex,
|
|
1960
|
+
rows: chunkRows,
|
|
1961
|
+
rowIndices: chunkRowIndices,
|
|
1962
|
+
startRowIndex,
|
|
1963
|
+
endRowIndex,
|
|
1964
|
+
totalRows: sheet.rowCount,
|
|
1965
|
+
isLastChunk: true,
|
|
1966
|
+
cellXfs: this.cellXfs,
|
|
1967
|
+
};
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
async getDrawings() {
|
|
1972
|
+
const drawings = [];
|
|
1973
|
+
for (const sheet of this.sheets) {
|
|
1974
|
+
const drawingPaths = await this.getDrawingPathsForSheet(sheet);
|
|
1975
|
+
for (const drawingPath of drawingPaths) {
|
|
1976
|
+
if (!this.zipParser.has(drawingPath))
|
|
1977
|
+
continue;
|
|
1978
|
+
const drawingContent = await this.zipParser.getText(drawingPath);
|
|
1979
|
+
if (!drawingContent)
|
|
1980
|
+
continue;
|
|
1981
|
+
const drawingXml = this.xmlParser.parse(drawingContent);
|
|
1982
|
+
const oneCellAnchors = get(drawingXml, "xdr:wsDr.xdr:oneCellAnchor") ||
|
|
1983
|
+
get(drawingXml, "wsDr.oneCellAnchor");
|
|
1984
|
+
const twoCellAnchors = get(drawingXml, "xdr:wsDr.xdr:twoCellAnchor") ||
|
|
1985
|
+
get(drawingXml, "wsDr.twoCellAnchor");
|
|
1986
|
+
const absoluteAnchors = get(drawingXml, "xdr:wsDr.xdr:absoluteAnchor") ||
|
|
1987
|
+
get(drawingXml, "wsDr.absoluteAnchor");
|
|
1988
|
+
if (oneCellAnchors) {
|
|
1989
|
+
drawings.push(...(await processAnchors(oneCellAnchors, "oneCellAnchor", drawingPath, sheet.sheetId, this.zipParser, this.xmlParser)));
|
|
1990
|
+
}
|
|
1991
|
+
if (twoCellAnchors) {
|
|
1992
|
+
drawings.push(...(await processAnchors(twoCellAnchors, "twoCellAnchors", drawingPath, sheet.sheetId, this.zipParser, this.xmlParser)));
|
|
1993
|
+
}
|
|
1994
|
+
if (absoluteAnchors) {
|
|
1995
|
+
drawings.push(...(await processAnchors(absoluteAnchors, "absoluteAnchors", drawingPath, sheet.sheetId, this.zipParser, this.xmlParser)));
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
return drawings;
|
|
2000
|
+
}
|
|
2001
|
+
async getCharts() {
|
|
2002
|
+
const charts = [];
|
|
2003
|
+
// sheet-name → sheetId map for cross-sheet ref resolution in chart
|
|
2004
|
+
// formulas (mirrors the xlsx plugin).
|
|
2005
|
+
const sheetIdByName = {};
|
|
2006
|
+
for (const sheet of this.sheets) {
|
|
2007
|
+
if (sheet?.title && sheet?.sheetId !== undefined) {
|
|
2008
|
+
sheetIdByName[String(sheet.title)] = Number(sheet.sheetId);
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
const resolveSheetId = (sheetName) => sheetName ? sheetIdByName[String(sheetName)] : undefined;
|
|
2012
|
+
for (const sheet of this.sheets) {
|
|
2013
|
+
const drawingPaths = await this.getDrawingPathsForSheet(sheet);
|
|
2014
|
+
for (const drawingPath of drawingPaths) {
|
|
2015
|
+
if (!this.zipParser.has(drawingPath))
|
|
2016
|
+
continue;
|
|
2017
|
+
const drawingContent = await this.zipParser.getText(drawingPath);
|
|
2018
|
+
if (!drawingContent)
|
|
2019
|
+
continue;
|
|
2020
|
+
const drawingXml = this.xmlParser.parse(drawingContent);
|
|
2021
|
+
const drawingDir = drawingPath.substring(0, drawingPath.lastIndexOf("/"));
|
|
2022
|
+
const drawingName = drawingPath.split("/").pop();
|
|
2023
|
+
if (!drawingName)
|
|
2024
|
+
continue;
|
|
2025
|
+
const drawingRelsPath = `${drawingDir}/_rels/${drawingName}.rels`;
|
|
2026
|
+
if (!this.zipParser.has(drawingRelsPath))
|
|
2027
|
+
continue;
|
|
2028
|
+
const drawingRelsContent = await this.zipParser.getText(drawingRelsPath);
|
|
2029
|
+
const drawingRelsXml = this.xmlParser.parse(drawingRelsContent);
|
|
2030
|
+
const drawingRelsList = get(drawingRelsXml, "Relationships.Relationship") || [];
|
|
2031
|
+
const drawingRelsArray = Array.isArray(drawingRelsList)
|
|
2032
|
+
? drawingRelsList
|
|
2033
|
+
: [drawingRelsList];
|
|
2034
|
+
const oneCellAnchors = get(drawingXml, "xdr:wsDr.xdr:oneCellAnchor") ||
|
|
2035
|
+
get(drawingXml, "wsDr.oneCellAnchor");
|
|
2036
|
+
const twoCellAnchors = get(drawingXml, "xdr:wsDr.xdr:twoCellAnchor") ||
|
|
2037
|
+
get(drawingXml, "wsDr.twoCellAnchor");
|
|
2038
|
+
const anchors = [
|
|
2039
|
+
...(Array.isArray(oneCellAnchors)
|
|
2040
|
+
? oneCellAnchors
|
|
2041
|
+
: oneCellAnchors
|
|
2042
|
+
? [oneCellAnchors]
|
|
2043
|
+
: []),
|
|
2044
|
+
...(Array.isArray(twoCellAnchors)
|
|
2045
|
+
? twoCellAnchors
|
|
2046
|
+
: twoCellAnchors
|
|
2047
|
+
? [twoCellAnchors]
|
|
2048
|
+
: []),
|
|
2049
|
+
];
|
|
2050
|
+
for (const anchor of anchors) {
|
|
2051
|
+
const chartId = get(anchor, "xdr:graphicFrame.a:graphic.a:graphicData.c:chart.a$.r:id") || get(anchor, "graphicFrame.graphic.graphicData.chart.a$.r:id");
|
|
2052
|
+
if (!chartId)
|
|
2053
|
+
continue;
|
|
2054
|
+
const chartRel = drawingRelsArray.find((rel) => rel?.a$?.Id === chartId ||
|
|
2055
|
+
rel?.a$?.id === chartId ||
|
|
2056
|
+
rel?.a$?.ID === chartId);
|
|
2057
|
+
if (!chartRel?.a$?.Target)
|
|
2058
|
+
continue;
|
|
2059
|
+
const chart = await processChart(chartRel.a$.Target, drawingPath, anchor, sheet.sheetId, this.zipParser, this.xmlParser, undefined, resolveSheetId);
|
|
2060
|
+
if (chart)
|
|
2061
|
+
charts.push(chart);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
return charts;
|
|
2066
|
+
}
|
|
2067
|
+
async getConditionalFormatting() {
|
|
2068
|
+
const rules = [];
|
|
2069
|
+
let ruleIdCounter = 0;
|
|
2070
|
+
for (const sheet of this.sheets) {
|
|
2071
|
+
if (!sheet.path || !this.zipParser.has(sheet.path))
|
|
2072
|
+
continue;
|
|
2073
|
+
try {
|
|
2074
|
+
const binary = await this.zipParser.getBinary(sheet.path);
|
|
2075
|
+
const records = readAllRecordHeaders(binary);
|
|
2076
|
+
let inCF = false;
|
|
2077
|
+
let currentCFRanges = null;
|
|
2078
|
+
let cfRuleIndex = 0;
|
|
2079
|
+
for (const record of records) {
|
|
2080
|
+
const payload = binary.subarray(record.dataOffset, record.nextOffset);
|
|
2081
|
+
if (record.type === RT.BrtBeginConditionalFormatting) {
|
|
2082
|
+
inCF = true;
|
|
2083
|
+
cfRuleIndex = 0;
|
|
2084
|
+
const parsed = parseBrtBeginConditionalFormatting(payload);
|
|
2085
|
+
currentCFRanges = parsed?.sqref || null;
|
|
2086
|
+
continue;
|
|
2087
|
+
}
|
|
2088
|
+
if (record.type === RT.BrtEndConditionalFormatting) {
|
|
2089
|
+
inCF = false;
|
|
2090
|
+
currentCFRanges = null;
|
|
2091
|
+
continue;
|
|
2092
|
+
}
|
|
2093
|
+
if (inCF && record.type === RT.BrtBeginCFRule && currentCFRanges) {
|
|
2094
|
+
const cfRule = parseBrtCFRule(payload);
|
|
2095
|
+
if (cfRule) {
|
|
2096
|
+
// Parse ranges
|
|
2097
|
+
const rangeAddresses = currentCFRanges.split(" ");
|
|
2098
|
+
const ranges = [];
|
|
2099
|
+
for (const address of rangeAddresses) {
|
|
2100
|
+
const range = getRangeFromAddressRef(address);
|
|
2101
|
+
if (range) {
|
|
2102
|
+
ranges.push({
|
|
2103
|
+
...range,
|
|
2104
|
+
sheetId: sheet.sheetId,
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
if (ranges.length > 0) {
|
|
2109
|
+
const rule = {
|
|
2110
|
+
id: `${sheet.sheetId}_${ruleIdCounter++}_${cfRuleIndex++}`,
|
|
2111
|
+
ranges,
|
|
2112
|
+
priority: cfRule.priority,
|
|
2113
|
+
};
|
|
2114
|
+
if (cfRule.stopIfTrue) {
|
|
2115
|
+
rule.stopIfTrue = true;
|
|
2116
|
+
}
|
|
2117
|
+
// Note: Full implementation would parse:
|
|
2118
|
+
// - Rule type and convert to booleanRule/gradientRule/topBottomRule/distinctRule
|
|
2119
|
+
// - DXF styles for formatting
|
|
2120
|
+
// - Formulas and values from Rgce data
|
|
2121
|
+
// This is a basic implementation that captures the structure
|
|
2122
|
+
rules.push(rule);
|
|
2123
|
+
}
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
catch (err) {
|
|
2129
|
+
// Skip sheet if parsing fails
|
|
2130
|
+
console.warn(`Failed to parse CF for sheet ${sheet.title}:`, err);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
return rules;
|
|
2134
|
+
}
|
|
2135
|
+
async getDataValidations() {
|
|
2136
|
+
const validations = [];
|
|
2137
|
+
for (const sheet of this.sheets) {
|
|
2138
|
+
if (!sheet.path || !this.zipParser.has(sheet.path))
|
|
2139
|
+
continue;
|
|
2140
|
+
try {
|
|
2141
|
+
const binary = await this.zipParser.getBinary(sheet.path);
|
|
2142
|
+
const records = readAllRecordHeaders(binary);
|
|
2143
|
+
let inDVals = false;
|
|
2144
|
+
let inDVal = false;
|
|
2145
|
+
let currentDVal = null;
|
|
2146
|
+
let currentRanges = null;
|
|
2147
|
+
for (const record of records) {
|
|
2148
|
+
const payload = binary.subarray(record.dataOffset, record.nextOffset);
|
|
2149
|
+
if (record.type === RT.BrtBeginDVals) {
|
|
2150
|
+
inDVals = true;
|
|
2151
|
+
continue;
|
|
2152
|
+
}
|
|
2153
|
+
if (record.type === RT.BrtEndDVals) {
|
|
2154
|
+
inDVals = false;
|
|
2155
|
+
continue;
|
|
2156
|
+
}
|
|
2157
|
+
if (inDVals && record.type === RT.BrtBeginDVal) {
|
|
2158
|
+
inDVal = true;
|
|
2159
|
+
currentDVal = parseBrtBeginDVal(payload);
|
|
2160
|
+
// Next record should contain the range
|
|
2161
|
+
continue;
|
|
2162
|
+
}
|
|
2163
|
+
if (inDVal && currentDVal) {
|
|
2164
|
+
// Try to parse range from payload
|
|
2165
|
+
// In XLSB, the range typically follows in the same or next record
|
|
2166
|
+
try {
|
|
2167
|
+
const reader = new Reader(payload);
|
|
2168
|
+
const maybeRange = reader.readXLWideString();
|
|
2169
|
+
if (maybeRange && maybeRange.includes(":")) {
|
|
2170
|
+
currentRanges = maybeRange;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
catch {
|
|
2174
|
+
// Not a range, continue
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
if (record.type === RT.BrtEndDVal) {
|
|
2178
|
+
inDVal = false;
|
|
2179
|
+
if (currentDVal && currentRanges) {
|
|
2180
|
+
// Parse ranges
|
|
2181
|
+
const rangeAddresses = currentRanges.split(" ");
|
|
2182
|
+
const ranges = [];
|
|
2183
|
+
for (const address of rangeAddresses) {
|
|
2184
|
+
const range = getRangeFromAddressRef(address);
|
|
2185
|
+
if (range) {
|
|
2186
|
+
ranges.push({
|
|
2187
|
+
...range,
|
|
2188
|
+
sheetId: sheet.sheetId,
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
if (ranges.length > 0) {
|
|
2193
|
+
// Map validation type to condition type
|
|
2194
|
+
const typeMap = {
|
|
2195
|
+
1: "NUMBER", // whole
|
|
2196
|
+
2: "NUMBER", // decimal
|
|
2197
|
+
3: "LIST", // list
|
|
2198
|
+
4: "DATE", // date
|
|
2199
|
+
5: "TIME", // time
|
|
2200
|
+
6: "TEXT_LENGTH", // textLength
|
|
2201
|
+
7: "CUSTOM_FORMULA", // custom
|
|
2202
|
+
};
|
|
2203
|
+
const validation = {
|
|
2204
|
+
id: uuid(),
|
|
2205
|
+
ranges,
|
|
2206
|
+
};
|
|
2207
|
+
// Add condition if we have a recognized type
|
|
2208
|
+
const conditionType = typeMap[currentDVal.valType];
|
|
2209
|
+
if (conditionType && currentDVal.operator !== undefined) {
|
|
2210
|
+
// Note: Full implementation would parse formulas and create proper BooleanCondition
|
|
2211
|
+
// This is a basic structure
|
|
2212
|
+
}
|
|
2213
|
+
// Add allow blank flag
|
|
2214
|
+
if (currentDVal.ignoreBlank) {
|
|
2215
|
+
validation.allowBlank = true;
|
|
2216
|
+
}
|
|
2217
|
+
// Add input message if present
|
|
2218
|
+
if (currentDVal.showInputMessage &&
|
|
2219
|
+
(currentDVal.promptTitle || currentDVal.prompt)) {
|
|
2220
|
+
validation.inputMessage = {
|
|
2221
|
+
...(currentDVal.promptTitle
|
|
2222
|
+
? { title: currentDVal.promptTitle }
|
|
2223
|
+
: {}),
|
|
2224
|
+
...(currentDVal.prompt
|
|
2225
|
+
? { message: currentDVal.prompt }
|
|
2226
|
+
: {}),
|
|
2227
|
+
};
|
|
2228
|
+
}
|
|
2229
|
+
// Add error alert if present. showErrorMessage without any
|
|
2230
|
+
// custom title/text still means "reject invalid input with
|
|
2231
|
+
// the default alert", so keep the style-only alert.
|
|
2232
|
+
if (currentDVal.showErrorMessage) {
|
|
2233
|
+
const alertStyles = [
|
|
2234
|
+
"stop",
|
|
2235
|
+
"warning",
|
|
2236
|
+
"information",
|
|
2237
|
+
];
|
|
2238
|
+
validation.alert = {
|
|
2239
|
+
style: alertStyles[currentDVal.errorStyle] || "stop",
|
|
2240
|
+
...(currentDVal.errorTitle || currentDVal.error
|
|
2241
|
+
? {
|
|
2242
|
+
message: {
|
|
2243
|
+
...(currentDVal.errorTitle
|
|
2244
|
+
? { title: currentDVal.errorTitle }
|
|
2245
|
+
: {}),
|
|
2246
|
+
...(currentDVal.error
|
|
2247
|
+
? { message: currentDVal.error }
|
|
2248
|
+
: {}),
|
|
2249
|
+
},
|
|
2250
|
+
}
|
|
2251
|
+
: {}),
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
validations.push(validation);
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
currentDVal = null;
|
|
2258
|
+
currentRanges = null;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
catch (err) {
|
|
2263
|
+
// Skip sheet if parsing fails
|
|
2264
|
+
console.warn(`Failed to parse DV for sheet ${sheet.title}:`, err);
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
return validations;
|
|
2268
|
+
}
|
|
2269
|
+
parseBrtName(payload, sheets) {
|
|
2270
|
+
const parsed = parseBrtNamePayload(payload, (ixti) => this.resolveSheetNameByXti(ixti));
|
|
2271
|
+
// Skip if parsing failed or hidden names
|
|
2272
|
+
if (!parsed || parsed.hidden) {
|
|
2273
|
+
return undefined;
|
|
2274
|
+
}
|
|
2275
|
+
// Try to parse the formula as a range reference
|
|
2276
|
+
const gridRange = getRangeFromAddressRef(parsed.formula);
|
|
2277
|
+
// Determine the sheet ID if this is a sheet-scoped name
|
|
2278
|
+
let sheetId;
|
|
2279
|
+
if (parsed.sheetIndex !== undefined && parsed.sheetIndex < sheets.length) {
|
|
2280
|
+
sheetId = sheets[parsed.sheetIndex].sheetId;
|
|
2281
|
+
}
|
|
2282
|
+
// Create the NamedRange object
|
|
2283
|
+
const namedRange = {
|
|
2284
|
+
namedRangeId: uuid(),
|
|
2285
|
+
name: parsed.name,
|
|
2286
|
+
};
|
|
2287
|
+
// Set either range or value depending on what we parsed
|
|
2288
|
+
if (gridRange) {
|
|
2289
|
+
// Convert GridRange to SheetRange by adding sheetId
|
|
2290
|
+
// Use the sheet-scoped sheetId if available, otherwise use the first sheet
|
|
2291
|
+
const rangeSheetId = sheetId ?? (sheets.length > 0 ? sheets[0].sheetId : 1);
|
|
2292
|
+
namedRange.range = {
|
|
2293
|
+
...gridRange,
|
|
2294
|
+
sheetId: rangeSheetId,
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
else if (parsed.formula) {
|
|
2298
|
+
// If we can't parse it as a range, store it as a formula value
|
|
2299
|
+
namedRange.value = parsed.formula;
|
|
2300
|
+
}
|
|
2301
|
+
return namedRange;
|
|
2302
|
+
}
|
|
2303
|
+
parseBrtCalcProp(payload) {
|
|
2304
|
+
const parsed = parseBrtCalcPropPayload(payload);
|
|
2305
|
+
if (!parsed) {
|
|
2306
|
+
return; // Skip if parsing failed
|
|
2307
|
+
}
|
|
2308
|
+
this.calcProp = {
|
|
2309
|
+
fullCalcOnLoad: parsed.fullCalcOnLoad,
|
|
2310
|
+
iterate: parsed.iterate,
|
|
2311
|
+
iterateCount: parsed.iterateCount,
|
|
2312
|
+
iterateDelta: parsed.iterateDelta,
|
|
2313
|
+
};
|
|
2314
|
+
}
|
|
2315
|
+
getNamedRanges() {
|
|
2316
|
+
return this.namedRanges;
|
|
2317
|
+
}
|
|
2318
|
+
requiresRecalc() {
|
|
2319
|
+
return this.calcProp.fullCalcOnLoad ?? false;
|
|
2320
|
+
}
|
|
2321
|
+
/**
|
|
2322
|
+
* `BrtWbProp.fDate1904` shifts every date serial by 1462 days
|
|
2323
|
+
* (Mac-origin workbooks). Defaults to the 1900 epoch when the record is
|
|
2324
|
+
* absent, matching Excel's default behavior for newly-created workbooks.
|
|
2325
|
+
*/
|
|
2326
|
+
getUse1904Epoch() {
|
|
2327
|
+
return this.use1904;
|
|
2328
|
+
}
|
|
2329
|
+
getIterativeCalculationOptions() {
|
|
2330
|
+
if (!this.calcProp.iterate) {
|
|
2331
|
+
return undefined;
|
|
2332
|
+
}
|
|
2333
|
+
return {
|
|
2334
|
+
enabled: true,
|
|
2335
|
+
maxIterations: this.calcProp.iterateCount ?? 100,
|
|
2336
|
+
maxChange: this.calcProp.iterateDelta ?? 0.001,
|
|
2337
|
+
};
|
|
2338
|
+
}
|
|
2339
|
+
}
|