@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,795 @@
|
|
|
1
|
+
import { getCellEffectiveFormat, getCellUserEnteredFormat, } from "@rowsncolumns/utils";
|
|
2
|
+
import { BUILTIN_NUMFMT_BY_PATTERN } from "../../excel-parser/builtin-numfmts";
|
|
3
|
+
const isStyleReference = (value) => !!value && typeof value === "object" && "sid" in value;
|
|
4
|
+
// Built-in number-format dictionary is shared with the importer — see
|
|
5
|
+
// `excel-parser/builtin-numfmts.ts`. Pattern strings are the canonical
|
|
6
|
+
// Excel-writer form (quoted "$" for currency/accounting, `mmss.0` for
|
|
7
|
+
// numFmtId 47 per ECMA-376). Re-aliased for backwards compat with the
|
|
8
|
+
// previous local name in this file.
|
|
9
|
+
const BUILT_IN_NUMFMT_BY_PATTERN = BUILTIN_NUMFMT_BY_PATTERN;
|
|
10
|
+
export class StyleBuilder {
|
|
11
|
+
numberFormats = [];
|
|
12
|
+
fonts = [
|
|
13
|
+
{
|
|
14
|
+
name: "Calibri",
|
|
15
|
+
size: 11,
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
fills = [{ pattern: "none" }, { pattern: "gray125" }];
|
|
19
|
+
borders = [{}];
|
|
20
|
+
namedCellStyles = [
|
|
21
|
+
{ name: "Normal", xfId: 0, builtinId: 0 },
|
|
22
|
+
];
|
|
23
|
+
customTableStyles = [];
|
|
24
|
+
/**
|
|
25
|
+
* Replace the named-cell-styles list emitted in `<cellStyles>`.
|
|
26
|
+
* Used for round-tripping the OOXML Heading 1 / Accent / Total /
|
|
27
|
+
* Good-Bad-Neutral gallery — the importer parses these from
|
|
28
|
+
* `xl/styles.xml` and exposes them on `workbook.workbookStyles.
|
|
29
|
+
* namedCellStyles`; hosts can forward that list back here.
|
|
30
|
+
*
|
|
31
|
+
* Excel requires at least a "Normal" entry pointing at xfId=0,
|
|
32
|
+
* builtinId=0 — when the supplied list doesn't contain one, we
|
|
33
|
+
* keep the default at the head of the list rather than risk
|
|
34
|
+
* emitting a file Excel rejects.
|
|
35
|
+
*/
|
|
36
|
+
setNamedCellStyles(entries) {
|
|
37
|
+
if (!entries || entries.length === 0)
|
|
38
|
+
return;
|
|
39
|
+
const hasNormal = entries.some((e) => e.builtinId === 0 || e.name === "Normal");
|
|
40
|
+
this.namedCellStyles = hasNormal
|
|
41
|
+
? entries.slice()
|
|
42
|
+
: [{ name: "Normal", xfId: 0, builtinId: 0 }, ...entries];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Replace the user-authored table-style list emitted in the
|
|
46
|
+
* `<tableStyles>` block. The exporter registers each element's
|
|
47
|
+
* format as a dxf at `toXml()` time, so callers pass self-contained
|
|
48
|
+
* records (no dxfIds to manage). Used for round-tripping the
|
|
49
|
+
* OOXML custom-table-style list from
|
|
50
|
+
* `workbook.workbookStyles.getCustomTableStyleExports()`.
|
|
51
|
+
*/
|
|
52
|
+
setCustomTableStyles(entries) {
|
|
53
|
+
if (!entries || entries.length === 0) {
|
|
54
|
+
this.customTableStyles = [];
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.customTableStyles = entries.slice();
|
|
58
|
+
}
|
|
59
|
+
cellXfs = [
|
|
60
|
+
{
|
|
61
|
+
numFmtId: 0,
|
|
62
|
+
fontId: 0,
|
|
63
|
+
fillId: 0,
|
|
64
|
+
borderId: 0,
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
fontMap = new Map();
|
|
68
|
+
fillMap = new Map();
|
|
69
|
+
borderMap = new Map();
|
|
70
|
+
formatMap = new Map();
|
|
71
|
+
xfMap = new Map();
|
|
72
|
+
dxfs = [];
|
|
73
|
+
dxfMap = new Map();
|
|
74
|
+
constructor() {
|
|
75
|
+
this.fontMap.set(this.serializeFont(this.fonts[0]), 0);
|
|
76
|
+
this.fillMap.set(this.serializeFill(this.fills[0]), 0);
|
|
77
|
+
this.fillMap.set(this.serializeFill(this.fills[1]), 1);
|
|
78
|
+
this.borderMap.set(this.serializeBorder(this.borders[0]), 0);
|
|
79
|
+
this.xfMap.set(serializeXf(this.cellXfs[0]), 0);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Replace the workbook default font (font id 0 — what every cell without
|
|
83
|
+
* an explicit font inherits). Callers pass the source workbook's parsed
|
|
84
|
+
* default (`WorkBook.getDefaultFont()`) so an imported workbook keeps its
|
|
85
|
+
* default typeface/size instead of the hardcoded Calibri 11. Must be
|
|
86
|
+
* called before any cell registration: cells explicitly styled with the
|
|
87
|
+
* old default must not collapse into font id 0.
|
|
88
|
+
*/
|
|
89
|
+
setDefaultFont(format) {
|
|
90
|
+
if (this.fonts.length > 1)
|
|
91
|
+
return;
|
|
92
|
+
const record = this.buildFontRecord(format);
|
|
93
|
+
if (!record)
|
|
94
|
+
return;
|
|
95
|
+
this.fonts[0] = record;
|
|
96
|
+
this.fontMap.clear();
|
|
97
|
+
this.fontMap.set(this.serializeFont(record), 0);
|
|
98
|
+
}
|
|
99
|
+
registerCell(cell, cellXfs,
|
|
100
|
+
/**
|
|
101
|
+
* Number format to use when the cell itself has none. Used by the
|
|
102
|
+
* exporter to layer in the precedent-derived number format on formula
|
|
103
|
+
* cells so Excel displays them correctly without requiring a recalc.
|
|
104
|
+
*/
|
|
105
|
+
derivedNumberFormat) {
|
|
106
|
+
if (!cell)
|
|
107
|
+
return 0;
|
|
108
|
+
// Match Excel's model: persist only the user-entered format. Auto-
|
|
109
|
+
// derived alignment / pattern color / formula precedent format are
|
|
110
|
+
// recomputed at render time and never serialized to disk. Legacy `ef`
|
|
111
|
+
// is still read as a fallback so old files continue to export with the
|
|
112
|
+
// formatting they originally had.
|
|
113
|
+
const userEnteredFormat = normalizeFormat(getCellUserEnteredFormat(cell), cellXfs);
|
|
114
|
+
let effectiveFormat = userEnteredFormat ??
|
|
115
|
+
normalizeFormat(getCellEffectiveFormat(cell), cellXfs) ??
|
|
116
|
+
undefined;
|
|
117
|
+
// Layer in the derived number format only when the cell's own format
|
|
118
|
+
// has nothing in that slot. User-set formats always win.
|
|
119
|
+
if (derivedNumberFormat && !effectiveFormat?.numberFormat) {
|
|
120
|
+
effectiveFormat = {
|
|
121
|
+
...(effectiveFormat ?? {}),
|
|
122
|
+
numberFormat: derivedNumberFormat,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const numberFormatId = this.getNumberFormatId(effectiveFormat);
|
|
126
|
+
const fontId = this.getFontId(effectiveFormat?.textFormat);
|
|
127
|
+
const fillId = this.getFillId(effectiveFormat);
|
|
128
|
+
const borderId = this.getBorderId(effectiveFormat?.borders);
|
|
129
|
+
const xfRecord = {
|
|
130
|
+
numFmtId: numberFormatId,
|
|
131
|
+
fontId,
|
|
132
|
+
fillId,
|
|
133
|
+
borderId,
|
|
134
|
+
wrapText: effectiveFormat?.wrapStrategy === "wrap",
|
|
135
|
+
horizontalAlignment: normalizeHorizontalAlignment(effectiveFormat?.horizontalAlignment),
|
|
136
|
+
verticalAlignment: normalizeVerticalAlignment(effectiveFormat?.verticalAlignment),
|
|
137
|
+
indent: effectiveFormat?.indent ?? undefined,
|
|
138
|
+
relativeIndent: effectiveFormat?.relativeIndent ?? undefined,
|
|
139
|
+
textRotation: effectiveFormat?.textRotation ?? undefined,
|
|
140
|
+
shrinkToFit: effectiveFormat?.shrinkToFit ?? undefined,
|
|
141
|
+
// Protection lives top-level on the cell (not on the format) — read
|
|
142
|
+
// it off the cell directly. The xf still carries the flags because
|
|
143
|
+
// that's where Excel persists them; we synthesize a unique xf per
|
|
144
|
+
// protection variant via the cache key below.
|
|
145
|
+
protectionLocked: cell?.protection?.locked === false ? false : undefined,
|
|
146
|
+
protectionHidden: cell?.protection?.hidden === true ? true : undefined,
|
|
147
|
+
};
|
|
148
|
+
const key = serializeXf(xfRecord);
|
|
149
|
+
const existing = this.xfMap.get(key);
|
|
150
|
+
if (typeof existing === "number") {
|
|
151
|
+
return existing;
|
|
152
|
+
}
|
|
153
|
+
const nextId = this.cellXfs.length;
|
|
154
|
+
this.cellXfs.push(xfRecord);
|
|
155
|
+
this.xfMap.set(key, nextId);
|
|
156
|
+
return nextId;
|
|
157
|
+
}
|
|
158
|
+
registerDifferentialFormat(format) {
|
|
159
|
+
if (!format)
|
|
160
|
+
return undefined;
|
|
161
|
+
const record = {};
|
|
162
|
+
if (format.backgroundColor) {
|
|
163
|
+
record.fill = {
|
|
164
|
+
color: format.backgroundColor,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const font = this.buildFontRecord(format.textFormat);
|
|
168
|
+
if (font) {
|
|
169
|
+
record.font = font;
|
|
170
|
+
}
|
|
171
|
+
if (!record.fill && !record.font) {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
const key = this.serializeDxf(record);
|
|
175
|
+
const existing = this.dxfMap.get(key);
|
|
176
|
+
if (typeof existing === "number") {
|
|
177
|
+
return existing;
|
|
178
|
+
}
|
|
179
|
+
const nextId = this.dxfs.length;
|
|
180
|
+
this.dxfs.push(record);
|
|
181
|
+
this.dxfMap.set(key, nextId);
|
|
182
|
+
return nextId;
|
|
183
|
+
}
|
|
184
|
+
getNumberFormatId(effectiveFormat) {
|
|
185
|
+
const pattern = effectiveFormat?.numberFormat?.pattern;
|
|
186
|
+
if (!pattern)
|
|
187
|
+
return 0;
|
|
188
|
+
// Preserve trailing whitespace in the pattern verbatim — number-format
|
|
189
|
+
// strings legitimately end with significant `\xa0` (non-breaking space)
|
|
190
|
+
// chars for column-aligned currency/accounting formats. Trimming here
|
|
191
|
+
// mangled those formats on round-trip and broke comparator equality
|
|
192
|
+
// against the source `<numFmts>` table. We still dedupe via the map
|
|
193
|
+
// using the raw pattern as key.
|
|
194
|
+
const normalizedPattern = pattern;
|
|
195
|
+
// Try Excel's built-in numFmtId table (0–49). When our pattern matches
|
|
196
|
+
// a built-in exactly, emit that ID — Excel and other readers render
|
|
197
|
+
// identically and we avoid bloating <numFmts> with a redundant
|
|
198
|
+
// custom entry.
|
|
199
|
+
const builtInId = BUILT_IN_NUMFMT_BY_PATTERN.get(normalizedPattern);
|
|
200
|
+
if (typeof builtInId === "number") {
|
|
201
|
+
return builtInId;
|
|
202
|
+
}
|
|
203
|
+
const existing = this.formatMap.get(normalizedPattern);
|
|
204
|
+
if (typeof existing === "number") {
|
|
205
|
+
return existing;
|
|
206
|
+
}
|
|
207
|
+
// ECMA-376 §18.8.30: built-in numFmtIds occupy 0–163. Custom IDs
|
|
208
|
+
// start at 164 by convention (Excel's own writer does the same).
|
|
209
|
+
const nextId = 164 + this.numberFormats.length;
|
|
210
|
+
this.numberFormats.push({
|
|
211
|
+
id: nextId,
|
|
212
|
+
format: normalizedPattern,
|
|
213
|
+
});
|
|
214
|
+
this.formatMap.set(normalizedPattern, nextId);
|
|
215
|
+
return nextId;
|
|
216
|
+
}
|
|
217
|
+
getFontId(format) {
|
|
218
|
+
const record = this.buildFontRecord(format);
|
|
219
|
+
if (!record)
|
|
220
|
+
return 0;
|
|
221
|
+
const key = this.serializeFont(record);
|
|
222
|
+
const existing = this.fontMap.get(key);
|
|
223
|
+
if (typeof existing === "number") {
|
|
224
|
+
return existing;
|
|
225
|
+
}
|
|
226
|
+
const nextId = this.fonts.length;
|
|
227
|
+
this.fonts.push(record);
|
|
228
|
+
this.fontMap.set(key, nextId);
|
|
229
|
+
return nextId;
|
|
230
|
+
}
|
|
231
|
+
getFillId(format) {
|
|
232
|
+
const fillPattern = format?.fillPattern ?? undefined;
|
|
233
|
+
const fgColor = format?.foregroundColor ?? undefined;
|
|
234
|
+
const bgColor = format?.backgroundColor ?? undefined;
|
|
235
|
+
const gradient = format?.gradient ?? undefined;
|
|
236
|
+
// Gradient fill takes precedence over pattern — Excel's UI treats
|
|
237
|
+
// them as mutually exclusive on a single cell.
|
|
238
|
+
if (gradient && gradient.stops?.length) {
|
|
239
|
+
const record = { gradient };
|
|
240
|
+
const key = this.serializeFill(record);
|
|
241
|
+
const existing = this.fillMap.get(key);
|
|
242
|
+
if (typeof existing === "number")
|
|
243
|
+
return existing;
|
|
244
|
+
const nextId = this.fills.length;
|
|
245
|
+
this.fills.push(record);
|
|
246
|
+
this.fillMap.set(key, nextId);
|
|
247
|
+
return nextId;
|
|
248
|
+
}
|
|
249
|
+
// Patterned (non-solid) fill: `color` carries the foreground (pattern)
|
|
250
|
+
// color and `bgColor` carries the under-layer that shows through the
|
|
251
|
+
// pattern gaps. Solid fill keeps the legacy single-color path.
|
|
252
|
+
if (fillPattern &&
|
|
253
|
+
fillPattern !== "none" &&
|
|
254
|
+
fillPattern !== "solid") {
|
|
255
|
+
const record = {
|
|
256
|
+
color: fgColor ?? bgColor,
|
|
257
|
+
bgColor: fgColor ? bgColor : undefined,
|
|
258
|
+
pattern: fillPattern,
|
|
259
|
+
};
|
|
260
|
+
const key = this.serializeFill(record);
|
|
261
|
+
const existing = this.fillMap.get(key);
|
|
262
|
+
if (typeof existing === "number")
|
|
263
|
+
return existing;
|
|
264
|
+
const nextId = this.fills.length;
|
|
265
|
+
this.fills.push(record);
|
|
266
|
+
this.fillMap.set(key, nextId);
|
|
267
|
+
return nextId;
|
|
268
|
+
}
|
|
269
|
+
// Solid fill: prefer explicit foregroundColor, fall back to
|
|
270
|
+
// backgroundColor for the common authored-as-bg case.
|
|
271
|
+
const color = fgColor ?? bgColor;
|
|
272
|
+
const record = {
|
|
273
|
+
color,
|
|
274
|
+
pattern: color ? "solid" : "none",
|
|
275
|
+
};
|
|
276
|
+
const key = this.serializeFill(record);
|
|
277
|
+
const existing = this.fillMap.get(key);
|
|
278
|
+
if (typeof existing === "number") {
|
|
279
|
+
return existing;
|
|
280
|
+
}
|
|
281
|
+
const nextId = this.fills.length;
|
|
282
|
+
this.fills.push(record);
|
|
283
|
+
this.fillMap.set(key, nextId);
|
|
284
|
+
return nextId;
|
|
285
|
+
}
|
|
286
|
+
getBorderId(borders) {
|
|
287
|
+
// OOXML uses a single <diagonal> child plus diagonalUp/diagonalDown
|
|
288
|
+
// attributes. If both diagonals are present and identically styled the
|
|
289
|
+
// first non-null one defines the edge; otherwise each flag is set
|
|
290
|
+
// independently and both share the same painted style. This matches
|
|
291
|
+
// how Excel collapses diagonal pairs.
|
|
292
|
+
const diagonalEdge = convertBorderEdge(borders?.diagonalUp ?? borders?.diagonalDown);
|
|
293
|
+
const record = {
|
|
294
|
+
top: convertBorderEdge(borders?.top),
|
|
295
|
+
bottom: convertBorderEdge(borders?.bottom),
|
|
296
|
+
left: convertBorderEdge(borders?.left),
|
|
297
|
+
right: convertBorderEdge(borders?.right),
|
|
298
|
+
diagonal: diagonalEdge,
|
|
299
|
+
diagonalUp: !!borders?.diagonalUp,
|
|
300
|
+
diagonalDown: !!borders?.diagonalDown,
|
|
301
|
+
};
|
|
302
|
+
const key = this.serializeBorder(record);
|
|
303
|
+
const existing = this.borderMap.get(key);
|
|
304
|
+
if (typeof existing === "number") {
|
|
305
|
+
return existing;
|
|
306
|
+
}
|
|
307
|
+
const nextId = this.borders.length;
|
|
308
|
+
this.borders.push(record);
|
|
309
|
+
this.borderMap.set(key, nextId);
|
|
310
|
+
return nextId;
|
|
311
|
+
}
|
|
312
|
+
serializeFont(font) {
|
|
313
|
+
return [
|
|
314
|
+
font.name ?? "",
|
|
315
|
+
font.size ?? "",
|
|
316
|
+
font.bold ? "1" : "",
|
|
317
|
+
font.italic ? "1" : "",
|
|
318
|
+
font.underline ? "1" : "",
|
|
319
|
+
font.strike ? "1" : "",
|
|
320
|
+
serializeColor(font.color),
|
|
321
|
+
font.vertAlign ?? "",
|
|
322
|
+
].join("|");
|
|
323
|
+
}
|
|
324
|
+
buildFontRecord(format) {
|
|
325
|
+
if (!format?.bold &&
|
|
326
|
+
!format?.italic &&
|
|
327
|
+
!format?.underline &&
|
|
328
|
+
!format?.strikethrough &&
|
|
329
|
+
!format?.fontSize &&
|
|
330
|
+
!format?.fontFamily &&
|
|
331
|
+
!format?.color &&
|
|
332
|
+
!format?.baselineOffset) {
|
|
333
|
+
return undefined;
|
|
334
|
+
}
|
|
335
|
+
const vertAlign = format?.baselineOffset === "superscript" ||
|
|
336
|
+
format?.baselineOffset === "subscript"
|
|
337
|
+
? format.baselineOffset
|
|
338
|
+
: undefined;
|
|
339
|
+
return {
|
|
340
|
+
bold: format?.bold ?? undefined,
|
|
341
|
+
italic: format?.italic ?? undefined,
|
|
342
|
+
underline: format?.underline ?? undefined,
|
|
343
|
+
strike: format?.strikethrough ?? undefined,
|
|
344
|
+
size: format?.fontSize ?? undefined,
|
|
345
|
+
name: format?.fontFamily ?? undefined,
|
|
346
|
+
color: format?.color ?? undefined,
|
|
347
|
+
vertAlign,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
buildFontXml(font) {
|
|
351
|
+
const color = serializeColorXml(font.color);
|
|
352
|
+
const name = sanitizeFontName(font.name);
|
|
353
|
+
return `<font>${font.size ? `<sz val="${font.size}"/>` : ""}${name ? `<name val="${escapeXml(name)}"/>` : ""}${font.bold ? "<b/>" : ""}${font.italic ? "<i/>" : ""}${font.underline ? "<u/>" : ""}${font.strike ? "<strike/>" : ""}${font.vertAlign ? `<vertAlign val="${font.vertAlign}"/>` : ""}${color ? `<color ${color}/>` : ""}</font>`;
|
|
354
|
+
}
|
|
355
|
+
buildFillXml(fill, forDxf = false) {
|
|
356
|
+
// Gradient fill — emits `<gradientFill>` with `<stop>` children.
|
|
357
|
+
// Excel accepts gradients in cell fills (not dxf), so when this is
|
|
358
|
+
// requested for a dxf record we degrade to a solid using the first
|
|
359
|
+
// stop's color so the conditional format still has SOMETHING to paint.
|
|
360
|
+
if (fill.gradient && fill.gradient.stops?.length) {
|
|
361
|
+
if (forDxf) {
|
|
362
|
+
const first = fill.gradient.stops[0].color;
|
|
363
|
+
const fallback = serializeColorXml(first);
|
|
364
|
+
if (fallback) {
|
|
365
|
+
return `<fill><patternFill patternType="solid"><fgColor ${fallback}/><bgColor ${fallback}/></patternFill></fill>`;
|
|
366
|
+
}
|
|
367
|
+
return `<fill><patternFill patternType="none"/></fill>`;
|
|
368
|
+
}
|
|
369
|
+
return this.buildGradientFillXml(fill.gradient);
|
|
370
|
+
}
|
|
371
|
+
const pattern = fill.pattern ?? (fill.color ? "solid" : "none");
|
|
372
|
+
if (pattern === "gray125" && !fill.color) {
|
|
373
|
+
return `<fill><patternFill patternType="gray125"/></fill>`;
|
|
374
|
+
}
|
|
375
|
+
const color = serializeColorXml(fill.color);
|
|
376
|
+
if (pattern === "none" || !color) {
|
|
377
|
+
return `<fill><patternFill patternType="none"/></fill>`;
|
|
378
|
+
}
|
|
379
|
+
if (pattern === "solid") {
|
|
380
|
+
if (forDxf) {
|
|
381
|
+
return `<fill><patternFill patternType="solid"><fgColor ${color}/><bgColor ${color}/></patternFill></fill>`;
|
|
382
|
+
}
|
|
383
|
+
return `<fill><patternFill patternType="solid"><fgColor ${color}/><bgColor indexed="64"/></patternFill></fill>`;
|
|
384
|
+
}
|
|
385
|
+
// True patterned fill — emit both fg and bg if available.
|
|
386
|
+
const bgColor = serializeColorXml(fill.bgColor);
|
|
387
|
+
const bgAttr = bgColor ? `<bgColor ${bgColor}/>` : `<bgColor indexed="64"/>`;
|
|
388
|
+
return `<fill><patternFill patternType="${pattern}"><fgColor ${color}/>${bgAttr}</patternFill></fill>`;
|
|
389
|
+
}
|
|
390
|
+
buildGradientFillXml(gradient) {
|
|
391
|
+
const stopsXml = gradient.stops
|
|
392
|
+
.map((s) => {
|
|
393
|
+
const color = serializeColorXml(s.color);
|
|
394
|
+
return `<stop position="${s.position}">${color ? `<color ${color}/>` : ""}</stop>`;
|
|
395
|
+
})
|
|
396
|
+
.join("");
|
|
397
|
+
const attrs = [];
|
|
398
|
+
if (gradient.type === "path") {
|
|
399
|
+
attrs.push('type="path"');
|
|
400
|
+
if (gradient.left != null)
|
|
401
|
+
attrs.push(`left="${gradient.left}"`);
|
|
402
|
+
if (gradient.right != null)
|
|
403
|
+
attrs.push(`right="${gradient.right}"`);
|
|
404
|
+
if (gradient.top != null)
|
|
405
|
+
attrs.push(`top="${gradient.top}"`);
|
|
406
|
+
if (gradient.bottom != null)
|
|
407
|
+
attrs.push(`bottom="${gradient.bottom}"`);
|
|
408
|
+
}
|
|
409
|
+
else if (gradient.degree != null && gradient.degree !== 0) {
|
|
410
|
+
attrs.push(`degree="${gradient.degree}"`);
|
|
411
|
+
}
|
|
412
|
+
const attrStr = attrs.length ? " " + attrs.join(" ") : "";
|
|
413
|
+
return `<fill><gradientFill${attrStr}>${stopsXml}</gradientFill></fill>`;
|
|
414
|
+
}
|
|
415
|
+
serializeFill(fill) {
|
|
416
|
+
const gradientKey = fill.gradient
|
|
417
|
+
? `G:${fill.gradient.type}:${fill.gradient.degree ?? ""}:${fill.gradient.left ?? ""}:${fill.gradient.right ?? ""}:${fill.gradient.top ?? ""}:${fill.gradient.bottom ?? ""}:${fill.gradient.stops
|
|
418
|
+
.map((s) => `${s.position},${s.color}`)
|
|
419
|
+
.join("|")}`
|
|
420
|
+
: "";
|
|
421
|
+
return `${fill.pattern ?? ""}:${serializeColor(fill.color)}:${serializeColor(fill.bgColor)}:${gradientKey}`;
|
|
422
|
+
}
|
|
423
|
+
serializeBorder(border) {
|
|
424
|
+
return [
|
|
425
|
+
serializeBorderEdge(border.top),
|
|
426
|
+
serializeBorderEdge(border.bottom),
|
|
427
|
+
serializeBorderEdge(border.left),
|
|
428
|
+
serializeBorderEdge(border.right),
|
|
429
|
+
serializeBorderEdge(border.diagonal),
|
|
430
|
+
border.diagonalUp ? "1" : "",
|
|
431
|
+
border.diagonalDown ? "1" : "",
|
|
432
|
+
].join("|");
|
|
433
|
+
}
|
|
434
|
+
serializeDxf(dxf) {
|
|
435
|
+
return `${dxf.fill ? this.serializeFill(dxf.fill) : ""}|${dxf.font ? this.serializeFont(dxf.font) : ""}`;
|
|
436
|
+
}
|
|
437
|
+
toXml() {
|
|
438
|
+
const numFmtXml = this.numberFormats.length
|
|
439
|
+
? `<numFmts count="${this.numberFormats.length}">${this.numberFormats
|
|
440
|
+
.map((fmt) => `<numFmt numFmtId="${fmt.id}" formatCode="${escapeXml(fmt.format)}"/>`)
|
|
441
|
+
.join("")}</numFmts>`
|
|
442
|
+
: `<numFmts count="0"/>`;
|
|
443
|
+
const fontXml = `<fonts count="${this.fonts.length}">${this.fonts
|
|
444
|
+
.map((font) => this.buildFontXml(font))
|
|
445
|
+
.join("")}</fonts>`;
|
|
446
|
+
const fillXml = `<fills count="${this.fills.length}">${this.fills
|
|
447
|
+
.map((fill) => this.buildFillXml(fill, false))
|
|
448
|
+
.join("")}</fills>`;
|
|
449
|
+
const borderXml = `<borders count="${this.borders.length}">${this.borders
|
|
450
|
+
.map((border) => {
|
|
451
|
+
const top = serializeBorderEdgeXml(border.top, "top");
|
|
452
|
+
const bottom = serializeBorderEdgeXml(border.bottom, "bottom");
|
|
453
|
+
const left = serializeBorderEdgeXml(border.left, "left");
|
|
454
|
+
const right = serializeBorderEdgeXml(border.right, "right");
|
|
455
|
+
const diagonal = border.diagonal
|
|
456
|
+
? serializeBorderEdgeXml(border.diagonal, "diagonal")
|
|
457
|
+
: "<diagonal/>";
|
|
458
|
+
const diagonalAttrs = `${border.diagonalUp ? ` diagonalUp="1"` : ""}${border.diagonalDown ? ` diagonalDown="1"` : ""}`;
|
|
459
|
+
return `<border${diagonalAttrs}>${left}${right}${top}${bottom}${diagonal}</border>`;
|
|
460
|
+
})
|
|
461
|
+
.join("")}</borders>`;
|
|
462
|
+
const cellXfsXml = `<cellXfs count="${this.cellXfs.length}">${this.cellXfs
|
|
463
|
+
.map((record) => {
|
|
464
|
+
const alignment = serializeAlignment(record);
|
|
465
|
+
const protection = serializeProtection(record);
|
|
466
|
+
// ECMA-376 §18.8.45: `applyX="1"` signals that this xf overrides
|
|
467
|
+
// the named-style parent (xfId=0) for that aspect. Excel tolerates
|
|
468
|
+
// missing applyX, but stricter readers (older LibreOffice, some
|
|
469
|
+
// headless engines) honor only the parent's value when applyX is
|
|
470
|
+
// absent. Emit each flag when the corresponding id deviates from
|
|
471
|
+
// the parent's defaults (0 across the board) or when alignment is
|
|
472
|
+
// present.
|
|
473
|
+
const applyNumberFormat = record.numFmtId !== 0 ? ` applyNumberFormat="1"` : "";
|
|
474
|
+
const applyFont = record.fontId !== 0 ? ` applyFont="1"` : "";
|
|
475
|
+
const applyFill = record.fillId !== 0 ? ` applyFill="1"` : "";
|
|
476
|
+
const applyBorder = record.borderId !== 0 ? ` applyBorder="1"` : "";
|
|
477
|
+
const applyAlignment = alignment ? ` applyAlignment="1"` : "";
|
|
478
|
+
const applyProtection = protection ? ` applyProtection="1"` : "";
|
|
479
|
+
return `<xf numFmtId="${record.numFmtId}" fontId="${record.fontId}" fillId="${record.fillId}" borderId="${record.borderId}" xfId="0"${applyNumberFormat}${applyFont}${applyFill}${applyBorder}${applyAlignment}${applyProtection}>${alignment}${protection}</xf>`;
|
|
480
|
+
})
|
|
481
|
+
.join("")}</cellXfs>`;
|
|
482
|
+
const resolvedTableStyles = this.customTableStyles.map((style) => {
|
|
483
|
+
const resolvedElements = [];
|
|
484
|
+
for (const el of style.elements) {
|
|
485
|
+
const dxfId = this.registerDifferentialFormat({
|
|
486
|
+
...(el.backgroundColor
|
|
487
|
+
? { backgroundColor: el.backgroundColor }
|
|
488
|
+
: {}),
|
|
489
|
+
...(el.textColor || el.bold || el.italic
|
|
490
|
+
? {
|
|
491
|
+
textFormat: {
|
|
492
|
+
...(el.textColor ? { color: el.textColor } : {}),
|
|
493
|
+
...(el.bold ? { bold: true } : {}),
|
|
494
|
+
...(el.italic ? { italic: true } : {}),
|
|
495
|
+
},
|
|
496
|
+
}
|
|
497
|
+
: {}),
|
|
498
|
+
});
|
|
499
|
+
// Drop elements that didn't contribute any recognized format —
|
|
500
|
+
// an undefined dxfId means `registerDifferentialFormat` saw
|
|
501
|
+
// an empty record. (dxfId === 0 is the valid first-slot
|
|
502
|
+
// entry, so we explicitly exclude only `undefined`.)
|
|
503
|
+
if (dxfId === undefined)
|
|
504
|
+
continue;
|
|
505
|
+
resolvedElements.push({
|
|
506
|
+
type: el.type,
|
|
507
|
+
dxfId,
|
|
508
|
+
...(el.size != null ? { size: el.size } : {}),
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
return {
|
|
512
|
+
name: style.name,
|
|
513
|
+
pivot: style.pivot,
|
|
514
|
+
elements: resolvedElements,
|
|
515
|
+
};
|
|
516
|
+
});
|
|
517
|
+
const dxfsXml = this.dxfs.length
|
|
518
|
+
? `<dxfs count="${this.dxfs.length}">${this.dxfs
|
|
519
|
+
.map((dxf) => {
|
|
520
|
+
const font = dxf.font ? this.buildFontXml(dxf.font) : "";
|
|
521
|
+
const fill = dxf.fill ? this.buildFillXml(dxf.fill, true) : "";
|
|
522
|
+
return `<dxf>${font}${fill}</dxf>`;
|
|
523
|
+
})
|
|
524
|
+
.join("")}</dxfs>`
|
|
525
|
+
: `<dxfs count="0"/>`;
|
|
526
|
+
const cellStylesXml = `<cellStyles count="${this.namedCellStyles.length}">${this.namedCellStyles
|
|
527
|
+
.map((s) => {
|
|
528
|
+
const parts = [`name="${escapeXml(s.name)}"`];
|
|
529
|
+
parts.push(`xfId="${s.xfId}"`);
|
|
530
|
+
if (s.builtinId != null)
|
|
531
|
+
parts.push(`builtinId="${s.builtinId}"`);
|
|
532
|
+
if (s.iLevel != null)
|
|
533
|
+
parts.push(`iLevel="${s.iLevel}"`);
|
|
534
|
+
if (s.hidden)
|
|
535
|
+
parts.push(`hidden="1"`);
|
|
536
|
+
if (s.customBuiltin)
|
|
537
|
+
parts.push(`customBuiltin="1"`);
|
|
538
|
+
return `<cellStyle ${parts.join(" ")}/>`;
|
|
539
|
+
})
|
|
540
|
+
.join("")}</cellStyles>`;
|
|
541
|
+
// Emit user-authored table styles. Excel REQUIRES the
|
|
542
|
+
// `defaultTableStyle` + `defaultPivotStyle` attributes to be the
|
|
543
|
+
// names of built-in styles (or styles defined here) — we keep
|
|
544
|
+
// the built-in defaults regardless of whether any custom styles
|
|
545
|
+
// are present.
|
|
546
|
+
const customTableStylesXml = resolvedTableStyles
|
|
547
|
+
.filter((s) => s.elements.length > 0)
|
|
548
|
+
.map((s) => {
|
|
549
|
+
const pivotAttr = s.pivot ? ` pivot="1"` : ` pivot="0"`;
|
|
550
|
+
const elementsXml = s.elements
|
|
551
|
+
.map((el) => {
|
|
552
|
+
const sizeAttr = el.size != null ? ` size="${el.size}"` : "";
|
|
553
|
+
return `<tableStyleElement type="${escapeXml(el.type)}" dxfId="${el.dxfId}"${sizeAttr}/>`;
|
|
554
|
+
})
|
|
555
|
+
.join("");
|
|
556
|
+
return `<tableStyle name="${escapeXml(s.name)}"${pivotAttr} count="${s.elements.length}">${elementsXml}</tableStyle>`;
|
|
557
|
+
})
|
|
558
|
+
.join("");
|
|
559
|
+
const tableStylesXml = `<tableStyles count="${resolvedTableStyles.filter((s) => s.elements.length > 0).length}" defaultTableStyle="TableStyleMedium2" defaultPivotStyle="PivotStyleLight16">${customTableStylesXml}</tableStyles>`;
|
|
560
|
+
const styleXml = `${XML_DECLARATION}<styleSheet xmlns="${WORKSHEET_NS}">${numFmtXml}${fontXml}${fillXml}${borderXml}<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>${cellXfsXml}${cellStylesXml}${dxfsXml}${tableStylesXml}</styleSheet>`;
|
|
561
|
+
return styleXml;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
const convertBorderEdge = (border) => {
|
|
565
|
+
if (!border || !border.style)
|
|
566
|
+
return undefined;
|
|
567
|
+
return {
|
|
568
|
+
style: mapBorderStyle(border.style),
|
|
569
|
+
color: border.color ?? undefined,
|
|
570
|
+
};
|
|
571
|
+
};
|
|
572
|
+
const mapBorderStyle = (style) => {
|
|
573
|
+
switch (style) {
|
|
574
|
+
case "dashed":
|
|
575
|
+
return "dashed";
|
|
576
|
+
case "dotted":
|
|
577
|
+
return "dotted";
|
|
578
|
+
case "double":
|
|
579
|
+
return "double";
|
|
580
|
+
case "solid_medium":
|
|
581
|
+
return "medium";
|
|
582
|
+
case "solid_thick":
|
|
583
|
+
return "thick";
|
|
584
|
+
case "hair":
|
|
585
|
+
return "hair";
|
|
586
|
+
case "mediumDashed":
|
|
587
|
+
return "mediumDashed";
|
|
588
|
+
case "mediumDashDot":
|
|
589
|
+
return "mediumDashDot";
|
|
590
|
+
case "dashDot":
|
|
591
|
+
return "dashDot";
|
|
592
|
+
case "dashDotDot":
|
|
593
|
+
return "dashDotDot";
|
|
594
|
+
case "slantDashDot":
|
|
595
|
+
return "slantDashDot";
|
|
596
|
+
case "thin":
|
|
597
|
+
// openpyxl/Excel's "thin" is the regular 1px border. The previous
|
|
598
|
+
// mapping returned Excel's "hair" (the lightest sub-1px border),
|
|
599
|
+
// which produced near-invisible borders for any caller — including
|
|
600
|
+
// athena-openpyxl — that explicitly asks for `Side(style="thin")`.
|
|
601
|
+
return "thin";
|
|
602
|
+
case "solid":
|
|
603
|
+
default:
|
|
604
|
+
// rowsncolumns' generic "solid" remains aliased to Excel's "thin"
|
|
605
|
+
// (the regular 1px border) when no explicit thickness is requested.
|
|
606
|
+
return "thin";
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
const serializeBorderEdge = (edge) => {
|
|
610
|
+
if (!edge)
|
|
611
|
+
return "";
|
|
612
|
+
return `${edge.style || ""}:${serializeColor(edge.color)}`;
|
|
613
|
+
};
|
|
614
|
+
const serializeBorderEdgeXml = (edge, side) => {
|
|
615
|
+
if (!edge?.style) {
|
|
616
|
+
return `<${side}/>`;
|
|
617
|
+
}
|
|
618
|
+
const color = serializeColorXml(edge.color);
|
|
619
|
+
return `<${side} style="${edge.style}">${color ? `<color ${color}/>` : ""}</${side}>`;
|
|
620
|
+
};
|
|
621
|
+
const serializeColor = (color) => {
|
|
622
|
+
if (!color)
|
|
623
|
+
return "";
|
|
624
|
+
if (typeof color === "string") {
|
|
625
|
+
return normalizeHex(color) ?? "";
|
|
626
|
+
}
|
|
627
|
+
return `theme${color.theme}:${color.tint ?? ""}`;
|
|
628
|
+
};
|
|
629
|
+
const serializeColorXml = (color) => {
|
|
630
|
+
if (!color)
|
|
631
|
+
return undefined;
|
|
632
|
+
if (typeof color === "string") {
|
|
633
|
+
const hex = normalizeHex(color);
|
|
634
|
+
return hex ? `rgb="${hex}"` : undefined;
|
|
635
|
+
}
|
|
636
|
+
const tintAttr = typeof color.tint === "number" ? ` tint="${color.tint}"` : "";
|
|
637
|
+
return `theme="${color.theme}"${tintAttr}`;
|
|
638
|
+
};
|
|
639
|
+
// Excel <name val=""/> expects a single font family ≤31 chars. CSS font stacks
|
|
640
|
+
// (`InterVariable, system-ui, sans-serif`) make Excel reject the entire styles
|
|
641
|
+
// part. Take the first family and strip surrounding quotes.
|
|
642
|
+
const sanitizeFontName = (name) => {
|
|
643
|
+
if (!name || typeof name !== "string")
|
|
644
|
+
return undefined;
|
|
645
|
+
const first = name.split(",")[0].trim().replace(/^['"]|['"]$/g, "");
|
|
646
|
+
if (!first)
|
|
647
|
+
return undefined;
|
|
648
|
+
return first.length > 31 ? first.slice(0, 31) : first;
|
|
649
|
+
};
|
|
650
|
+
const HEX6_RE = /^[0-9A-Fa-f]{6}$/;
|
|
651
|
+
const HEX8_RE = /^[0-9A-Fa-f]{8}$/;
|
|
652
|
+
const RGB_FUNC_RE = /^rgba?\s*\(\s*(\d+)\s*[, ]\s*(\d+)\s*[, ]\s*(\d+)(?:\s*[, /]\s*([\d.]+))?\s*\)$/i;
|
|
653
|
+
// Returns AARRGGBB hex for any recognized color string; undefined otherwise.
|
|
654
|
+
// Excel rejects the whole styles.xml if rgb="" contains non-hex or unescaped
|
|
655
|
+
// quotes (which CSS strings like `font-family:"Times New Roman"` would produce).
|
|
656
|
+
const normalizeHex = (color) => {
|
|
657
|
+
if (typeof color !== "string")
|
|
658
|
+
return undefined;
|
|
659
|
+
const trimmed = color.trim();
|
|
660
|
+
if (!trimmed)
|
|
661
|
+
return undefined;
|
|
662
|
+
const noHash = trimmed.startsWith("#") ? trimmed.slice(1) : trimmed;
|
|
663
|
+
if (HEX8_RE.test(noHash))
|
|
664
|
+
return noHash.toUpperCase();
|
|
665
|
+
if (HEX6_RE.test(noHash))
|
|
666
|
+
return `FF${noHash.toUpperCase()}`;
|
|
667
|
+
const m = RGB_FUNC_RE.exec(trimmed);
|
|
668
|
+
if (m) {
|
|
669
|
+
const toHex = (s) => {
|
|
670
|
+
const n = Math.max(0, Math.min(255, parseInt(s, 10) | 0));
|
|
671
|
+
return n.toString(16).padStart(2, "0").toUpperCase();
|
|
672
|
+
};
|
|
673
|
+
const alpha = m[4] != null
|
|
674
|
+
? Math.max(0, Math.min(255, Math.round(parseFloat(m[4]) * 255)))
|
|
675
|
+
.toString(16)
|
|
676
|
+
.padStart(2, "0")
|
|
677
|
+
.toUpperCase()
|
|
678
|
+
: "FF";
|
|
679
|
+
return `${alpha}${toHex(m[1])}${toHex(m[2])}${toHex(m[3])}`;
|
|
680
|
+
}
|
|
681
|
+
return undefined;
|
|
682
|
+
};
|
|
683
|
+
const serializeAlignment = (record) => {
|
|
684
|
+
const attrs = [];
|
|
685
|
+
if (record.horizontalAlignment) {
|
|
686
|
+
attrs.push(`horizontal="${record.horizontalAlignment}"`);
|
|
687
|
+
}
|
|
688
|
+
if (record.verticalAlignment) {
|
|
689
|
+
attrs.push(`vertical="${record.verticalAlignment}"`);
|
|
690
|
+
}
|
|
691
|
+
if (record.wrapText) {
|
|
692
|
+
attrs.push(`wrapText="1"`);
|
|
693
|
+
}
|
|
694
|
+
if (typeof record.indent === "number" &&
|
|
695
|
+
Number.isFinite(record.indent) &&
|
|
696
|
+
record.indent > 0) {
|
|
697
|
+
attrs.push(`indent="${record.indent}"`);
|
|
698
|
+
}
|
|
699
|
+
if (typeof record.relativeIndent === "number" &&
|
|
700
|
+
Number.isFinite(record.relativeIndent) &&
|
|
701
|
+
record.relativeIndent !== 0) {
|
|
702
|
+
attrs.push(`relativeIndent="${record.relativeIndent}"`);
|
|
703
|
+
}
|
|
704
|
+
if (record.textRotation !== undefined && record.textRotation !== null) {
|
|
705
|
+
// Excel encodes "vertical (stacked)" text as the magic value 255.
|
|
706
|
+
const rotation = record.textRotation === "vertical" ? 255 : record.textRotation;
|
|
707
|
+
if (Number.isFinite(rotation) && rotation !== 0) {
|
|
708
|
+
attrs.push(`textRotation="${rotation}"`);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
if (record.shrinkToFit) {
|
|
712
|
+
attrs.push(`shrinkToFit="1"`);
|
|
713
|
+
}
|
|
714
|
+
if (!attrs.length)
|
|
715
|
+
return "";
|
|
716
|
+
return `<alignment ${attrs.join(" ")}/>`;
|
|
717
|
+
};
|
|
718
|
+
const serializeXf = (record) => {
|
|
719
|
+
return [
|
|
720
|
+
record.numFmtId,
|
|
721
|
+
record.fontId,
|
|
722
|
+
record.fillId,
|
|
723
|
+
record.borderId,
|
|
724
|
+
record.wrapText ? 1 : 0,
|
|
725
|
+
record.horizontalAlignment ?? "",
|
|
726
|
+
record.verticalAlignment ?? "",
|
|
727
|
+
record.indent ?? "",
|
|
728
|
+
record.relativeIndent ?? "",
|
|
729
|
+
record.textRotation ?? "",
|
|
730
|
+
record.shrinkToFit ? 1 : 0,
|
|
731
|
+
record.protectionLocked === false ? 0 : "",
|
|
732
|
+
record.protectionHidden === true ? 1 : "",
|
|
733
|
+
].join(":");
|
|
734
|
+
};
|
|
735
|
+
const serializeProtection = (record) => {
|
|
736
|
+
const attrs = [];
|
|
737
|
+
// Only emit when the value deviates from Excel's defaults (locked=true,
|
|
738
|
+
// hidden=false) — otherwise we'd flag every xf as having protection.
|
|
739
|
+
if (record.protectionLocked === false) {
|
|
740
|
+
attrs.push(`locked="0"`);
|
|
741
|
+
}
|
|
742
|
+
if (record.protectionHidden === true) {
|
|
743
|
+
attrs.push(`hidden="1"`);
|
|
744
|
+
}
|
|
745
|
+
if (!attrs.length)
|
|
746
|
+
return "";
|
|
747
|
+
return `<protection ${attrs.join(" ")}/>`;
|
|
748
|
+
};
|
|
749
|
+
const normalizeHorizontalAlignment = (value) => {
|
|
750
|
+
const allowed = new Set([
|
|
751
|
+
"general",
|
|
752
|
+
"left",
|
|
753
|
+
"center",
|
|
754
|
+
"right",
|
|
755
|
+
"fill",
|
|
756
|
+
"justify",
|
|
757
|
+
"centerContinuous",
|
|
758
|
+
"distributed",
|
|
759
|
+
]);
|
|
760
|
+
if (!value)
|
|
761
|
+
return undefined;
|
|
762
|
+
return allowed.has(value) ? value : undefined;
|
|
763
|
+
};
|
|
764
|
+
const normalizeVerticalAlignment = (value) => {
|
|
765
|
+
if (!value)
|
|
766
|
+
return undefined;
|
|
767
|
+
if (value === "middle")
|
|
768
|
+
return "center";
|
|
769
|
+
const allowed = new Set([
|
|
770
|
+
"top",
|
|
771
|
+
"center",
|
|
772
|
+
"bottom",
|
|
773
|
+
"justify",
|
|
774
|
+
"distributed",
|
|
775
|
+
]);
|
|
776
|
+
return allowed.has(value) ? value : undefined;
|
|
777
|
+
};
|
|
778
|
+
const XML_DECLARATION = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>`;
|
|
779
|
+
const WORKSHEET_NS = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
|
|
780
|
+
const normalizeFormat = (format, cellXfs) => {
|
|
781
|
+
if (format && isStyleReference(format)) {
|
|
782
|
+
const styleId = format.sid ?? format.styleId;
|
|
783
|
+
if (styleId) {
|
|
784
|
+
return cellXfs?.get(styleId);
|
|
785
|
+
}
|
|
786
|
+
return undefined;
|
|
787
|
+
}
|
|
788
|
+
return format ?? undefined;
|
|
789
|
+
};
|
|
790
|
+
const escapeXml = (value) => value
|
|
791
|
+
.replace(/&/g, "&")
|
|
792
|
+
.replace(/</g, "<")
|
|
793
|
+
.replace(/>/g, ">")
|
|
794
|
+
.replace(/"/g, """)
|
|
795
|
+
.replace(/'/g, "'");
|