@cj-tech-master/excelts 1.6.3 → 2.0.0-canary.20251228013952.4f2c3c6
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/README.md +139 -24
- package/README_zh.md +140 -26
- package/dist/browser/excelts.esm.js +18468 -0
- package/dist/browser/excelts.esm.js.map +1 -0
- package/dist/browser/excelts.esm.min.js +125 -0
- package/dist/browser/excelts.iife.js +13107 -47146
- package/dist/browser/excelts.iife.js.map +1 -1
- package/dist/browser/excelts.iife.min.js +24 -106
- package/dist/cjs/csv/csv-core.js +701 -0
- package/dist/cjs/csv/csv-stream.js +646 -0
- package/dist/cjs/csv/csv.base.js +137 -0
- package/dist/cjs/csv/csv.browser.js +68 -0
- package/dist/cjs/csv/csv.js +218 -162
- package/dist/cjs/doc/anchor.js +2 -2
- package/dist/cjs/doc/cell.js +22 -22
- package/dist/cjs/doc/column.js +28 -7
- package/dist/cjs/doc/data-validations.js +3 -3
- package/dist/cjs/doc/defined-names.js +13 -13
- package/dist/cjs/doc/image.js +7 -7
- package/dist/cjs/doc/modelcontainer.js +2 -2
- package/dist/cjs/doc/note.js +2 -2
- package/dist/cjs/doc/pivot-table.js +5 -5
- package/dist/cjs/doc/range.js +11 -11
- package/dist/cjs/doc/row.js +16 -16
- package/dist/cjs/doc/table.js +5 -5
- package/dist/cjs/doc/workbook.base.js +211 -0
- package/dist/cjs/doc/workbook.browser.js +62 -0
- package/dist/cjs/doc/workbook.js +68 -179
- package/dist/cjs/doc/worksheet.js +45 -41
- package/dist/cjs/index.js +49 -32
- package/dist/cjs/stream/xlsx/hyperlink-reader.js +6 -6
- package/dist/cjs/stream/xlsx/sheet-comments-writer.js +12 -12
- package/dist/cjs/stream/xlsx/sheet-rels-writer.js +4 -4
- package/dist/cjs/stream/xlsx/workbook-reader.js +22 -22
- package/dist/cjs/stream/xlsx/workbook-writer.js +38 -38
- package/dist/cjs/stream/xlsx/worksheet-reader.js +17 -17
- package/dist/cjs/stream/xlsx/worksheet-writer.js +67 -60
- package/dist/cjs/utils/browser-buffer.js +75 -0
- package/dist/cjs/utils/cell-format.js +2 -2
- package/dist/cjs/utils/cell-matrix.js +5 -5
- package/dist/cjs/utils/datetime.js +499 -0
- package/dist/cjs/utils/encryptor.browser.js +240 -0
- package/dist/cjs/utils/parse-sax.js +1191 -13
- package/dist/cjs/utils/shared-formula.js +5 -5
- package/dist/cjs/utils/sheet-utils.js +13 -13
- package/dist/cjs/utils/stream-buf.browser.js +355 -0
- package/dist/cjs/utils/stream-buf.js +5 -5
- package/dist/cjs/utils/unzip/extract.js +11 -11
- package/dist/cjs/utils/unzip/index.js +21 -21
- package/dist/cjs/utils/unzip/parse-extra-field.js +3 -3
- package/dist/cjs/utils/unzip/parse.js +16 -16
- package/dist/cjs/utils/unzip/zip-parser.js +14 -3
- package/dist/cjs/utils/utils.base.js +161 -0
- package/dist/cjs/utils/utils.browser.js +89 -0
- package/dist/cjs/utils/utils.js +46 -154
- package/dist/cjs/utils/xml-stream.js +3 -3
- package/dist/cjs/utils/zip/compress.base.js +88 -0
- package/dist/cjs/utils/zip/compress.browser.js +127 -0
- package/dist/cjs/utils/zip/compress.js +18 -198
- package/dist/cjs/utils/zip/crc32.browser.js +88 -0
- package/dist/cjs/utils/zip/deflate-fallback.js +575 -0
- package/dist/cjs/utils/zip/index.js +17 -17
- package/dist/cjs/utils/zip/streaming-zip.js +264 -0
- package/dist/cjs/utils/zip/zip-builder.js +10 -10
- package/dist/cjs/utils/zip-stream.browser.js +135 -0
- package/dist/cjs/utils/zip-stream.js +4 -4
- package/dist/cjs/xlsx/xform/base-xform.js +4 -4
- package/dist/cjs/xlsx/xform/book/defined-name-xform.js +4 -4
- package/dist/cjs/xlsx/xform/book/sheet-xform.js +4 -4
- package/dist/cjs/xlsx/xform/book/workbook-calc-properties-xform.js +2 -2
- package/dist/cjs/xlsx/xform/book/workbook-pivot-cache-xform.js +2 -2
- package/dist/cjs/xlsx/xform/book/workbook-properties-xform.js +2 -2
- package/dist/cjs/xlsx/xform/book/workbook-view-xform.js +2 -2
- package/dist/cjs/xlsx/xform/book/workbook-xform.js +24 -24
- package/dist/cjs/xlsx/xform/comment/comment-xform.js +4 -4
- package/dist/cjs/xlsx/xform/comment/comments-xform.js +6 -6
- package/dist/cjs/xlsx/xform/comment/style/vml-position-xform.js +2 -2
- package/dist/cjs/xlsx/xform/comment/style/vml-protection-xform.js +2 -2
- package/dist/cjs/xlsx/xform/comment/vml-anchor-xform.js +2 -2
- package/dist/cjs/xlsx/xform/comment/vml-client-data-xform.js +10 -10
- package/dist/cjs/xlsx/xform/comment/vml-notes-xform.js +6 -6
- package/dist/cjs/xlsx/xform/comment/vml-shape-xform.js +6 -6
- package/dist/cjs/xlsx/xform/comment/vml-textbox-xform.js +2 -2
- package/dist/cjs/xlsx/xform/composite-xform.js +2 -2
- package/dist/cjs/xlsx/xform/core/app-heading-pairs-xform.js +2 -2
- package/dist/cjs/xlsx/xform/core/app-titles-of-parts-xform.js +2 -2
- package/dist/cjs/xlsx/xform/core/app-xform.js +11 -11
- package/dist/cjs/xlsx/xform/core/content-types-xform.js +4 -4
- package/dist/cjs/xlsx/xform/core/core-xform.js +23 -23
- package/dist/cjs/xlsx/xform/core/relationship-xform.js +2 -2
- package/dist/cjs/xlsx/xform/core/relationships-xform.js +6 -6
- package/dist/cjs/xlsx/xform/drawing/base-cell-anchor-xform.js +2 -2
- package/dist/cjs/xlsx/xform/drawing/blip-fill-xform.js +4 -4
- package/dist/cjs/xlsx/xform/drawing/blip-xform.js +2 -2
- package/dist/cjs/xlsx/xform/drawing/c-nv-pic-pr-xform.js +2 -2
- package/dist/cjs/xlsx/xform/drawing/c-nv-pr-xform.js +6 -6
- package/dist/cjs/xlsx/xform/drawing/cell-position-xform.js +7 -7
- package/dist/cjs/xlsx/xform/drawing/drawing-xform.js +10 -10
- package/dist/cjs/xlsx/xform/drawing/ext-lst-xform.js +2 -2
- package/dist/cjs/xlsx/xform/drawing/ext-xform.js +2 -2
- package/dist/cjs/xlsx/xform/drawing/hlink-click-xform.js +2 -2
- package/dist/cjs/xlsx/xform/drawing/nv-pic-pr-xform.js +6 -6
- package/dist/cjs/xlsx/xform/drawing/one-cell-anchor-xform.js +10 -10
- package/dist/cjs/xlsx/xform/drawing/pic-xform.js +9 -9
- package/dist/cjs/xlsx/xform/drawing/two-cell-anchor-xform.js +9 -9
- package/dist/cjs/xlsx/xform/list-xform.js +2 -2
- package/dist/cjs/xlsx/xform/pivot-table/cache-field-xform.js +5 -5
- package/dist/cjs/xlsx/xform/pivot-table/cache-field.js +3 -3
- package/dist/cjs/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +10 -10
- package/dist/cjs/xlsx/xform/pivot-table/pivot-cache-records-xform.js +9 -9
- package/dist/cjs/xlsx/xform/pivot-table/pivot-table-xform.js +10 -11
- package/dist/cjs/xlsx/xform/sheet/auto-filter-xform.js +4 -4
- package/dist/cjs/xlsx/xform/sheet/cell-xform.js +65 -65
- package/dist/cjs/xlsx/xform/sheet/cf/cf-rule-xform.js +27 -27
- package/dist/cjs/xlsx/xform/sheet/cf/cfvo-xform.js +3 -3
- package/dist/cjs/xlsx/xform/sheet/cf/color-scale-xform.js +6 -6
- package/dist/cjs/xlsx/xform/sheet/cf/conditional-formatting-xform.js +6 -6
- package/dist/cjs/xlsx/xform/sheet/cf/conditional-formattings-xform.js +4 -4
- package/dist/cjs/xlsx/xform/sheet/cf/databar-xform.js +6 -6
- package/dist/cjs/xlsx/xform/sheet/cf/ext-lst-ref-xform.js +5 -5
- package/dist/cjs/xlsx/xform/sheet/cf/formula-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/cf/icon-set-xform.js +11 -11
- package/dist/cjs/xlsx/xform/sheet/cf-ext/cf-icon-ext-xform.js +3 -3
- package/dist/cjs/xlsx/xform/sheet/cf-ext/cf-rule-ext-xform.js +11 -12
- package/dist/cjs/xlsx/xform/sheet/cf-ext/cfvo-ext-xform.js +4 -4
- package/dist/cjs/xlsx/xform/sheet/cf-ext/conditional-formatting-ext-xform.js +8 -8
- package/dist/cjs/xlsx/xform/sheet/cf-ext/conditional-formattings-ext-xform.js +6 -6
- package/dist/cjs/xlsx/xform/sheet/cf-ext/databar-ext-xform.js +26 -26
- package/dist/cjs/xlsx/xform/sheet/cf-ext/f-ext-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/cf-ext/icon-set-ext-xform.js +14 -14
- package/dist/cjs/xlsx/xform/sheet/cf-ext/sqref-ext-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/col-breaks-xform.js +38 -0
- package/dist/cjs/xlsx/xform/sheet/col-xform.js +6 -6
- package/dist/cjs/xlsx/xform/sheet/data-validations-xform.js +18 -18
- package/dist/cjs/xlsx/xform/sheet/dimension-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/drawing-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/ext-lst-xform.js +5 -5
- package/dist/cjs/xlsx/xform/sheet/header-footer-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/hyperlink-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/merge-cell-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/merges.js +8 -8
- package/dist/cjs/xlsx/xform/sheet/outline-properties-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/page-breaks-xform.js +15 -3
- package/dist/cjs/xlsx/xform/sheet/page-margins-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/page-setup-properties-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/page-setup-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/picture-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/print-options-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/row-breaks-xform.js +15 -17
- package/dist/cjs/xlsx/xform/sheet/row-xform.js +11 -11
- package/dist/cjs/xlsx/xform/sheet/sheet-format-properties-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/sheet-properties-xform.js +8 -8
- package/dist/cjs/xlsx/xform/sheet/sheet-protection-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/sheet-view-xform.js +4 -4
- package/dist/cjs/xlsx/xform/sheet/table-part-xform.js +2 -2
- package/dist/cjs/xlsx/xform/sheet/worksheet-xform.js +75 -70
- package/dist/cjs/xlsx/xform/simple/boolean-xform.js +2 -2
- package/dist/cjs/xlsx/xform/simple/date-xform.js +2 -2
- package/dist/cjs/xlsx/xform/simple/float-xform.js +2 -2
- package/dist/cjs/xlsx/xform/simple/integer-xform.js +2 -2
- package/dist/cjs/xlsx/xform/simple/string-xform.js +2 -2
- package/dist/cjs/xlsx/xform/static-xform.js +4 -4
- package/dist/cjs/xlsx/xform/strings/phonetic-text-xform.js +6 -6
- package/dist/cjs/xlsx/xform/strings/rich-text-xform.js +6 -6
- package/dist/cjs/xlsx/xform/strings/shared-string-xform.js +8 -8
- package/dist/cjs/xlsx/xform/strings/shared-strings-xform.js +6 -6
- package/dist/cjs/xlsx/xform/strings/text-xform.js +2 -2
- package/dist/cjs/xlsx/xform/style/alignment-xform.js +8 -8
- package/dist/cjs/xlsx/xform/style/border-xform.js +8 -8
- package/dist/cjs/xlsx/xform/style/color-xform.js +2 -2
- package/dist/cjs/xlsx/xform/style/dxf-xform.js +14 -14
- package/dist/cjs/xlsx/xform/style/fill-xform.js +9 -9
- package/dist/cjs/xlsx/xform/style/font-xform.js +22 -22
- package/dist/cjs/xlsx/xform/style/numfmt-xform.js +5 -5
- package/dist/cjs/xlsx/xform/style/protection-xform.js +2 -2
- package/dist/cjs/xlsx/xform/style/style-xform.js +6 -6
- package/dist/cjs/xlsx/xform/style/styles-xform.js +39 -39
- package/dist/cjs/xlsx/xform/style/underline-xform.js +2 -2
- package/dist/cjs/xlsx/xform/table/auto-filter-xform.js +4 -4
- package/dist/cjs/xlsx/xform/table/custom-filter-xform.js +2 -2
- package/dist/cjs/xlsx/xform/table/filter-column-xform.js +9 -9
- package/dist/cjs/xlsx/xform/table/filter-xform.js +2 -2
- package/dist/cjs/xlsx/xform/table/table-column-xform.js +2 -2
- package/dist/cjs/xlsx/xform/table/table-style-info-xform.js +2 -2
- package/dist/cjs/xlsx/xform/table/table-xform.js +12 -12
- package/dist/cjs/xlsx/xlsx.base.js +742 -0
- package/dist/cjs/xlsx/xlsx.browser.js +162 -0
- package/dist/cjs/xlsx/xlsx.js +118 -892
- package/dist/esm/csv/csv-core.js +694 -0
- package/dist/esm/csv/csv-stream.js +638 -0
- package/dist/esm/csv/csv.base.js +127 -0
- package/dist/esm/csv/csv.browser.js +65 -0
- package/dist/esm/csv/csv.js +181 -159
- package/dist/esm/doc/column.js +21 -0
- package/dist/esm/doc/workbook.base.js +207 -0
- package/dist/esm/doc/workbook.browser.js +59 -0
- package/dist/esm/doc/workbook.js +64 -175
- package/dist/esm/doc/worksheet.js +4 -0
- package/dist/esm/index.browser.js +33 -1
- package/dist/esm/index.js +23 -8
- package/dist/esm/local.js +0 -1
- package/dist/esm/stream/xlsx/workbook-writer.js +1 -1
- package/dist/esm/stream/xlsx/worksheet-writer.js +8 -1
- package/dist/esm/utils/browser-buffer.js +67 -0
- package/dist/esm/utils/datetime.js +493 -0
- package/dist/esm/utils/encryptor.browser.js +237 -0
- package/dist/esm/utils/parse-sax.js +1188 -12
- package/dist/esm/utils/stream-buf.browser.js +352 -0
- package/dist/esm/utils/unzip/zip-parser.js +11 -0
- package/dist/esm/utils/utils.base.js +142 -0
- package/dist/esm/utils/utils.browser.js +68 -0
- package/dist/esm/utils/utils.js +15 -123
- package/dist/esm/utils/zip/compress.base.js +83 -0
- package/dist/esm/utils/zip/compress.browser.js +121 -0
- package/dist/esm/utils/zip/compress.js +16 -164
- package/dist/esm/utils/zip/crc32.browser.js +82 -0
- package/dist/esm/utils/zip/deflate-fallback.js +570 -0
- package/dist/esm/utils/zip/streaming-zip.js +259 -0
- package/dist/esm/utils/zip-stream.browser.js +132 -0
- package/dist/esm/xlsx/xform/pivot-table/pivot-table-xform.js +2 -3
- package/dist/esm/xlsx/xform/sheet/cf-ext/cf-rule-ext-xform.js +2 -3
- package/dist/esm/xlsx/xform/sheet/col-breaks-xform.js +35 -0
- package/dist/esm/xlsx/xform/sheet/page-breaks-xform.js +13 -1
- package/dist/esm/xlsx/xform/sheet/row-breaks-xform.js +11 -13
- package/dist/esm/xlsx/xform/sheet/worksheet-xform.js +7 -2
- package/dist/esm/xlsx/xlsx.base.js +739 -0
- package/dist/esm/xlsx/xlsx.browser.js +159 -0
- package/dist/esm/xlsx/xlsx.js +114 -888
- package/dist/types/csv/csv-core.d.ts +207 -0
- package/dist/types/csv/csv-stream.d.ts +114 -0
- package/dist/types/csv/csv.base.d.ts +62 -0
- package/dist/types/csv/csv.browser.d.ts +33 -0
- package/dist/types/csv/csv.d.ts +97 -71
- package/dist/types/doc/anchor.d.ts +1 -1
- package/dist/types/doc/cell.d.ts +7 -7
- package/dist/types/doc/column.d.ts +9 -3
- package/dist/types/doc/defined-names.d.ts +4 -4
- package/dist/types/doc/image.d.ts +2 -2
- package/dist/types/doc/modelcontainer.d.ts +1 -1
- package/dist/types/doc/pivot-table.d.ts +1 -1
- package/dist/types/doc/range.d.ts +1 -1
- package/dist/types/doc/row.d.ts +3 -3
- package/dist/types/doc/table.d.ts +2 -2
- package/dist/types/doc/workbook.base.d.ts +111 -0
- package/dist/types/doc/workbook.browser.d.ts +38 -0
- package/dist/types/doc/workbook.d.ts +62 -92
- package/dist/types/doc/worksheet.d.ts +12 -10
- package/dist/types/index.browser.d.ts +19 -5
- package/dist/types/index.d.ts +24 -23
- package/dist/types/local.d.ts +0 -1
- package/dist/types/stream/xlsx/hyperlink-reader.d.ts +1 -1
- package/dist/types/stream/xlsx/workbook-reader.d.ts +4 -4
- package/dist/types/stream/xlsx/workbook-writer.d.ts +7 -7
- package/dist/types/stream/xlsx/worksheet-reader.d.ts +5 -5
- package/dist/types/stream/xlsx/worksheet-writer.d.ts +11 -9
- package/dist/types/types.d.ts +6 -0
- package/dist/types/utils/browser-buffer.d.ts +28 -0
- package/dist/types/utils/col-cache.d.ts +1 -1
- package/dist/types/utils/datetime.d.ts +56 -0
- package/dist/types/utils/encryptor.browser.d.ts +28 -0
- package/dist/types/utils/parse-sax.d.ts +108 -1
- package/dist/types/utils/sheet-utils.d.ts +3 -3
- package/dist/types/utils/stream-buf.browser.d.ts +41 -0
- package/dist/types/utils/unzip/extract.d.ts +6 -6
- package/dist/types/utils/unzip/index.d.ts +8 -8
- package/dist/types/utils/unzip/parse.d.ts +3 -3
- package/dist/types/utils/unzip/zip-parser.d.ts +5 -0
- package/dist/types/utils/utils.base.d.ts +29 -0
- package/dist/types/utils/utils.browser.d.ts +29 -0
- package/dist/types/utils/utils.d.ts +6 -25
- package/dist/types/utils/zip/compress.base.d.ts +45 -0
- package/dist/types/utils/zip/compress.browser.d.ts +63 -0
- package/dist/types/utils/zip/compress.d.ts +13 -45
- package/dist/types/utils/zip/crc32.browser.d.ts +52 -0
- package/dist/types/utils/zip/deflate-fallback.d.ts +39 -0
- package/dist/types/utils/zip/index.d.ts +5 -5
- package/dist/types/utils/zip/streaming-zip.d.ts +96 -0
- package/dist/types/utils/zip/zip-builder.d.ts +1 -1
- package/dist/types/utils/zip-stream.browser.d.ts +39 -0
- package/dist/types/xlsx/xform/base-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/book/defined-name-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/book/sheet-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/book/workbook-calc-properties-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/book/workbook-pivot-cache-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/book/workbook-properties-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/book/workbook-view-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/book/workbook-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/comment/comment-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/comment/comments-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/comment/style/vml-position-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/comment/style/vml-protection-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/comment/vml-anchor-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/comment/vml-client-data-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/comment/vml-notes-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/comment/vml-shape-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/comment/vml-textbox-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/composite-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/core/app-heading-pairs-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/core/app-titles-of-parts-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/core/app-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/core/content-types-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/core/core-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/core/relationship-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/core/relationships-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/drawing/base-cell-anchor-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/drawing/blip-fill-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/drawing/blip-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/drawing/c-nv-pic-pr-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/drawing/c-nv-pr-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/drawing/cell-position-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/drawing/drawing-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/drawing/ext-lst-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/drawing/ext-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/drawing/hlink-click-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/drawing/nv-pic-pr-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/drawing/pic-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/drawing/two-cell-anchor-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/list-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/pivot-table/cache-field-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/pivot-table/pivot-cache-definition-xform.d.ts +3 -3
- package/dist/types/xlsx/xform/pivot-table/pivot-cache-records-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/pivot-table/pivot-table-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/auto-filter-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/cell-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/cf/cf-rule-xform.d.ts +6 -6
- package/dist/types/xlsx/xform/sheet/cf/cfvo-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/cf/color-scale-xform.d.ts +3 -3
- package/dist/types/xlsx/xform/sheet/cf/conditional-formatting-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/cf/conditional-formattings-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/sheet/cf/databar-xform.d.ts +3 -3
- package/dist/types/xlsx/xform/sheet/cf/ext-lst-ref-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/cf/formula-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/cf/icon-set-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/sheet/cf-ext/cf-icon-ext-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/cf-ext/cf-rule-ext-xform.d.ts +3 -3
- package/dist/types/xlsx/xform/sheet/cf-ext/cfvo-ext-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/sheet/cf-ext/conditional-formatting-ext-xform.d.ts +3 -3
- package/dist/types/xlsx/xform/sheet/cf-ext/conditional-formattings-ext-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/sheet/cf-ext/databar-ext-xform.d.ts +3 -3
- package/dist/types/xlsx/xform/sheet/cf-ext/f-ext-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/cf-ext/icon-set-ext-xform.d.ts +3 -3
- package/dist/types/xlsx/xform/sheet/cf-ext/sqref-ext-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/col-breaks-xform.d.ts +16 -0
- package/dist/types/xlsx/xform/sheet/col-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/data-validations-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/dimension-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/drawing-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/ext-lst-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/header-footer-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/hyperlink-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/merge-cell-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/outline-properties-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/page-breaks-xform.d.ts +5 -1
- package/dist/types/xlsx/xform/sheet/page-margins-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/page-setup-properties-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/page-setup-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/picture-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/print-options-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/row-breaks-xform.d.ts +5 -1
- package/dist/types/xlsx/xform/sheet/row-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/sheet-format-properties-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/sheet-properties-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/sheet-protection-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/sheet-view-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/table-part-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/sheet/worksheet-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/simple/boolean-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/simple/date-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/simple/float-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/simple/integer-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/simple/string-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/static-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/strings/phonetic-text-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/strings/rich-text-xform.d.ts +3 -3
- package/dist/types/xlsx/xform/strings/shared-string-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/strings/shared-strings-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/strings/text-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/style/alignment-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/style/border-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/style/color-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/style/dxf-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/style/fill-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/style/font-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/style/numfmt-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/style/protection-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/style/style-xform.d.ts +3 -3
- package/dist/types/xlsx/xform/style/styles-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/style/underline-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/table/auto-filter-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/table/custom-filter-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/table/filter-column-xform.d.ts +2 -2
- package/dist/types/xlsx/xform/table/filter-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/table/table-column-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/table/table-style-info-xform.d.ts +1 -1
- package/dist/types/xlsx/xform/table/table-xform.d.ts +1 -1
- package/dist/types/xlsx/xlsx.base.d.ts +134 -0
- package/dist/types/xlsx/xlsx.browser.d.ts +31 -0
- package/dist/types/xlsx/xlsx.d.ts +20 -80
- package/package.json +16 -39
package/dist/esm/xlsx/xlsx.js
CHANGED
|
@@ -1,27 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XLSX - Node.js version with full functionality
|
|
3
|
+
*
|
|
4
|
+
* Extends XLSXBase with:
|
|
5
|
+
* - readFile: Read from file path
|
|
6
|
+
* - writeFile: Write to file path
|
|
7
|
+
* - read: Read from stream
|
|
8
|
+
* - write: Write to stream
|
|
9
|
+
* - load: Load from buffer
|
|
10
|
+
* - writeBuffer: Write to buffer
|
|
11
|
+
*/
|
|
1
12
|
import fs from "fs";
|
|
2
|
-
import { Unzip, UnzipInflate } from "fflate";
|
|
3
13
|
import { PassThrough } from "stream";
|
|
14
|
+
import { ZipParser } from "../utils/unzip/zip-parser.js";
|
|
4
15
|
import { ZipWriter } from "../utils/zip-stream.js";
|
|
5
16
|
import { StreamBuf } from "../utils/stream-buf.js";
|
|
6
17
|
import { fileExists, bufferToString } from "../utils/utils.js";
|
|
7
|
-
import {
|
|
8
|
-
import { StylesXform } from "./xform/style/styles-xform.js";
|
|
9
|
-
import { CoreXform } from "./xform/core/core-xform.js";
|
|
10
|
-
import { SharedStringsXform } from "./xform/strings/shared-strings-xform.js";
|
|
11
|
-
import { RelationshipsXform } from "./xform/core/relationships-xform.js";
|
|
12
|
-
import { ContentTypesXform } from "./xform/core/content-types-xform.js";
|
|
13
|
-
import { AppXform } from "./xform/core/app-xform.js";
|
|
14
|
-
import { WorkbookXform } from "./xform/book/workbook-xform.js";
|
|
15
|
-
import { WorkSheetXform } from "./xform/sheet/worksheet-xform.js";
|
|
16
|
-
import { DrawingXform } from "./xform/drawing/drawing-xform.js";
|
|
17
|
-
import { TableXform } from "./xform/table/table-xform.js";
|
|
18
|
-
import { PivotCacheRecordsXform } from "./xform/pivot-table/pivot-cache-records-xform.js";
|
|
19
|
-
import { PivotCacheDefinitionXform } from "./xform/pivot-table/pivot-cache-definition-xform.js";
|
|
20
|
-
import { PivotTableXform } from "./xform/pivot-table/pivot-table-xform.js";
|
|
21
|
-
import { CommentsXform } from "./xform/comment/comments-xform.js";
|
|
22
|
-
import { VmlNotesXform } from "./xform/comment/vml-notes-xform.js";
|
|
23
|
-
import { theme1Xml } from "./xml/theme1.js";
|
|
24
|
-
import { RelType } from "./rel-type.js";
|
|
18
|
+
import { XLSXBase } from "./xlsx.base.js";
|
|
25
19
|
function fsReadFileAsync(filename, options) {
|
|
26
20
|
return new Promise((resolve, reject) => {
|
|
27
21
|
fs.readFile(filename, options, (error, data) => {
|
|
@@ -34,14 +28,32 @@ function fsReadFileAsync(filename, options) {
|
|
|
34
28
|
});
|
|
35
29
|
});
|
|
36
30
|
}
|
|
37
|
-
class XLSX {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
class XLSX extends XLSXBase {
|
|
32
|
+
// ===========================================================================
|
|
33
|
+
// Abstract method implementations
|
|
34
|
+
// ===========================================================================
|
|
35
|
+
createStreamBuf() {
|
|
36
|
+
return new StreamBuf();
|
|
37
|
+
}
|
|
38
|
+
createBinaryStream(data) {
|
|
39
|
+
const stream = new PassThrough();
|
|
40
|
+
stream.end(Buffer.from(data));
|
|
41
|
+
return stream;
|
|
42
|
+
}
|
|
43
|
+
createTextStream(content) {
|
|
44
|
+
const stream = new PassThrough({
|
|
45
|
+
readableObjectMode: true,
|
|
46
|
+
writableObjectMode: true
|
|
47
|
+
});
|
|
48
|
+
stream.end(content);
|
|
49
|
+
return stream;
|
|
50
|
+
}
|
|
51
|
+
bufferToString(data) {
|
|
52
|
+
return bufferToString(data);
|
|
53
|
+
}
|
|
54
|
+
// ===========================================================================
|
|
55
|
+
// Node.js specific: File operations
|
|
56
|
+
// ===========================================================================
|
|
45
57
|
async readFile(filename, options) {
|
|
46
58
|
if (!(await fileExists(filename))) {
|
|
47
59
|
throw new Error(`File not found: ${filename}`);
|
|
@@ -57,425 +69,102 @@ class XLSX {
|
|
|
57
69
|
throw error;
|
|
58
70
|
}
|
|
59
71
|
}
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
return
|
|
63
|
-
}
|
|
64
|
-
parseWorkbook(stream) {
|
|
65
|
-
const xform = new WorkbookXform();
|
|
66
|
-
return xform.parseStream(stream);
|
|
67
|
-
}
|
|
68
|
-
parseSharedStrings(stream) {
|
|
69
|
-
const xform = new SharedStringsXform();
|
|
70
|
-
return xform.parseStream(stream);
|
|
71
|
-
}
|
|
72
|
-
reconcile(model, options) {
|
|
73
|
-
const workbookXform = new WorkbookXform();
|
|
74
|
-
const worksheetXform = new WorkSheetXform(options);
|
|
75
|
-
const drawingXform = new DrawingXform();
|
|
76
|
-
const tableXform = new TableXform();
|
|
77
|
-
workbookXform.reconcile(model);
|
|
78
|
-
// reconcile drawings with their rels
|
|
79
|
-
const drawingOptions = {
|
|
80
|
-
media: model.media,
|
|
81
|
-
mediaIndex: model.mediaIndex
|
|
82
|
-
};
|
|
83
|
-
Object.keys(model.drawings).forEach(name => {
|
|
84
|
-
const drawing = model.drawings[name];
|
|
85
|
-
const drawingRel = model.drawingRels[name];
|
|
86
|
-
if (drawingRel) {
|
|
87
|
-
drawingOptions.rels = drawingRel.reduce((o, rel) => {
|
|
88
|
-
o[rel.Id] = rel;
|
|
89
|
-
return o;
|
|
90
|
-
}, {});
|
|
91
|
-
(drawing.anchors || []).forEach((anchor) => {
|
|
92
|
-
const hyperlinks = anchor.picture && anchor.picture.hyperlinks;
|
|
93
|
-
if (hyperlinks && drawingOptions.rels[hyperlinks.rId]) {
|
|
94
|
-
hyperlinks.hyperlink = drawingOptions.rels[hyperlinks.rId].Target;
|
|
95
|
-
delete hyperlinks.rId;
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
drawingXform.reconcile(drawing, drawingOptions);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
// reconcile tables with the default styles
|
|
102
|
-
const tableOptions = {
|
|
103
|
-
styles: model.styles
|
|
104
|
-
};
|
|
105
|
-
Object.values(model.tables).forEach((table) => {
|
|
106
|
-
tableXform.reconcile(table, tableOptions);
|
|
107
|
-
});
|
|
108
|
-
// Reconcile pivot tables - link pivot tables to worksheets and cache data
|
|
109
|
-
this._reconcilePivotTables(model);
|
|
110
|
-
const sheetOptions = {
|
|
111
|
-
styles: model.styles,
|
|
112
|
-
sharedStrings: model.sharedStrings,
|
|
113
|
-
media: model.media,
|
|
114
|
-
mediaIndex: model.mediaIndex,
|
|
115
|
-
date1904: model.properties && model.properties.date1904,
|
|
116
|
-
drawings: model.drawings,
|
|
117
|
-
comments: model.comments,
|
|
118
|
-
tables: model.tables,
|
|
119
|
-
vmlDrawings: model.vmlDrawings,
|
|
120
|
-
pivotTables: model.pivotTablesIndexed
|
|
121
|
-
};
|
|
122
|
-
model.worksheets.forEach((worksheet) => {
|
|
123
|
-
worksheet.relationships = model.worksheetRels[worksheet.sheetNo];
|
|
124
|
-
worksheetXform.reconcile(worksheet, sheetOptions);
|
|
125
|
-
});
|
|
126
|
-
// delete unnecessary parts
|
|
127
|
-
delete model.worksheetHash;
|
|
128
|
-
delete model.worksheetRels;
|
|
129
|
-
delete model.globalRels;
|
|
130
|
-
delete model.sharedStrings;
|
|
131
|
-
delete model.workbookRels;
|
|
132
|
-
delete model.sheetDefs;
|
|
133
|
-
delete model.styles;
|
|
134
|
-
delete model.mediaIndex;
|
|
135
|
-
delete model.drawings;
|
|
136
|
-
delete model.drawingRels;
|
|
137
|
-
delete model.vmlDrawings;
|
|
138
|
-
// Clean up raw pivot table data after reconciliation
|
|
139
|
-
delete model.pivotTableRels;
|
|
140
|
-
delete model.pivotCacheDefinitionRels;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Reconcile pivot tables by linking them to worksheets and their cache data.
|
|
144
|
-
* This builds a complete pivot table model ready for writing.
|
|
145
|
-
*/
|
|
146
|
-
_reconcilePivotTables(model) {
|
|
147
|
-
// Skip if no pivot tables were loaded (object is empty or undefined)
|
|
148
|
-
const rawPivotTables = model.pivotTables || {};
|
|
149
|
-
if (typeof rawPivotTables !== "object" || Object.keys(rawPivotTables).length === 0) {
|
|
150
|
-
// Ensure pivotTables is an empty array (not an object)
|
|
151
|
-
model.pivotTables = [];
|
|
152
|
-
// Also create an empty indexed object for worksheet reconciliation
|
|
153
|
-
model.pivotTablesIndexed = {};
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
// Build mapping from definition name to cacheId
|
|
157
|
-
const definitionToCacheId = this._buildDefinitionToCacheIdMap(model);
|
|
158
|
-
// Build a map of cache IDs to their definitions and records
|
|
159
|
-
const cacheMap = new Map();
|
|
160
|
-
// Process cache definitions
|
|
161
|
-
Object.entries(model.pivotCacheDefinitions || {}).forEach(([name, definition]) => {
|
|
162
|
-
// Get the cacheId from the mapping (derived from workbook.xml pivotCaches)
|
|
163
|
-
const cacheId = definitionToCacheId.get(name);
|
|
164
|
-
if (cacheId !== undefined) {
|
|
165
|
-
const recordsName = name.replace("Definition", "Records");
|
|
166
|
-
cacheMap.set(cacheId, {
|
|
167
|
-
definition,
|
|
168
|
-
records: model.pivotCacheRecords?.[recordsName],
|
|
169
|
-
definitionName: name
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
// Process pivot tables and link to worksheets
|
|
174
|
-
const loadedPivotTables = [];
|
|
175
|
-
// Create indexed object for worksheet reconciliation (keyed by relative path)
|
|
176
|
-
const pivotTablesIndexed = {};
|
|
177
|
-
Object.entries(rawPivotTables).forEach(([pivotName, pivotTable]) => {
|
|
178
|
-
const pt = pivotTable;
|
|
179
|
-
const tableNumber = this._extractTableNumber(pivotName);
|
|
180
|
-
// Get cache data for this pivot table
|
|
181
|
-
const cacheData = cacheMap.get(pt.cacheId);
|
|
182
|
-
// Build complete pivot table model
|
|
183
|
-
const completePivotTable = {
|
|
184
|
-
// Core model data
|
|
185
|
-
...pt,
|
|
186
|
-
tableNumber,
|
|
187
|
-
// Link to cache data
|
|
188
|
-
cacheDefinition: cacheData?.definition,
|
|
189
|
-
cacheRecords: cacheData?.records,
|
|
190
|
-
// Reconstruct cacheFields from definition for compatibility
|
|
191
|
-
cacheFields: cacheData?.definition?.cacheFields || [],
|
|
192
|
-
// Determine rows, columns, values from parsed data
|
|
193
|
-
rows: pt.rowFields.filter(f => f >= 0),
|
|
194
|
-
columns: pt.colFields.filter(f => f >= 0 && f !== -2),
|
|
195
|
-
values: pt.dataFields.map(df => df.fld),
|
|
196
|
-
// Determine metric from dataFields
|
|
197
|
-
metric: this._determineMetric(pt.dataFields),
|
|
198
|
-
// Preserve formatting options
|
|
199
|
-
applyWidthHeightFormats: pt.applyWidthHeightFormats || "0"
|
|
200
|
-
};
|
|
201
|
-
loadedPivotTables.push(completePivotTable);
|
|
202
|
-
// Index by relative path for worksheet reconciliation
|
|
203
|
-
pivotTablesIndexed[`../pivotTables/${pivotName}.xml`] = completePivotTable;
|
|
204
|
-
});
|
|
205
|
-
// Sort by table number to maintain order
|
|
206
|
-
loadedPivotTables.sort((a, b) => a.tableNumber - b.tableNumber);
|
|
207
|
-
// Replace pivotTables object with the processed array
|
|
208
|
-
// This is what the Workbook model setter expects
|
|
209
|
-
model.pivotTables = loadedPivotTables;
|
|
210
|
-
// Also keep indexed version for worksheet reconciliation
|
|
211
|
-
model.pivotTablesIndexed = pivotTablesIndexed;
|
|
212
|
-
// Also keep as loadedPivotTables for backward compatibility
|
|
213
|
-
model.loadedPivotTables = loadedPivotTables;
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Extract table number from pivot table name (e.g., "pivotTable1" -> 1)
|
|
217
|
-
*/
|
|
218
|
-
_extractTableNumber(name) {
|
|
219
|
-
const match = name.match(/pivotTable(\d+)/);
|
|
220
|
-
return match ? parseInt(match[1], 10) : 1;
|
|
221
|
-
}
|
|
222
|
-
/**
|
|
223
|
-
* Build a mapping from rId to cacheId using pivotCaches from workbook.xml
|
|
224
|
-
* and workbookRels to determine which definition file corresponds to which cacheId
|
|
225
|
-
*/
|
|
226
|
-
_buildCacheIdMap(model) {
|
|
227
|
-
const rIdToCacheId = new Map();
|
|
228
|
-
// pivotCaches from workbook.xml contains {cacheId, rId} mappings
|
|
229
|
-
const pivotCaches = model.pivotCaches || [];
|
|
230
|
-
for (const cache of pivotCaches) {
|
|
231
|
-
if (cache.cacheId && cache.rId) {
|
|
232
|
-
rIdToCacheId.set(cache.rId, parseInt(cache.cacheId, 10));
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
return rIdToCacheId;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Build a mapping from definition name to cacheId
|
|
239
|
-
*/
|
|
240
|
-
_buildDefinitionToCacheIdMap(model) {
|
|
241
|
-
const definitionToCacheId = new Map();
|
|
242
|
-
const rIdToCacheId = this._buildCacheIdMap(model);
|
|
243
|
-
const workbookRels = model.workbookRels || [];
|
|
244
|
-
// Map workbook rels to get definitionNumber -> cacheId mapping
|
|
245
|
-
for (const rel of workbookRels) {
|
|
246
|
-
if (rel.Type === XLSX.RelType.PivotCacheDefinition && rel.Target) {
|
|
247
|
-
// Extract definition number from target (e.g., "pivotCache/pivotCacheDefinition1.xml" -> 1)
|
|
248
|
-
const match = rel.Target.match(/pivotCacheDefinition(\d+)\.xml/);
|
|
249
|
-
if (match) {
|
|
250
|
-
const defName = `pivotCacheDefinition${match[1]}`;
|
|
251
|
-
const cacheId = rIdToCacheId.get(rel.Id);
|
|
252
|
-
if (cacheId !== undefined) {
|
|
253
|
-
definitionToCacheId.set(defName, cacheId);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return definitionToCacheId;
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* Determine the aggregation metric from dataFields
|
|
262
|
-
*/
|
|
263
|
-
_determineMetric(dataFields) {
|
|
264
|
-
if (dataFields.length > 0 && dataFields[0].subtotal === "count") {
|
|
265
|
-
return "count";
|
|
266
|
-
}
|
|
267
|
-
return "sum";
|
|
268
|
-
}
|
|
269
|
-
async _processWorksheetEntry(stream, model, sheetNo, options, path) {
|
|
270
|
-
const xform = new WorkSheetXform(options);
|
|
271
|
-
const worksheet = await xform.parseStream(stream);
|
|
272
|
-
if (!worksheet) {
|
|
273
|
-
throw new Error(`Failed to parse worksheet ${path}`);
|
|
274
|
-
}
|
|
275
|
-
worksheet.sheetNo = sheetNo;
|
|
276
|
-
model.worksheetHash[path] = worksheet;
|
|
277
|
-
model.worksheets.push(worksheet);
|
|
278
|
-
}
|
|
279
|
-
async _processCommentEntry(stream, model, name) {
|
|
280
|
-
const xform = new CommentsXform();
|
|
281
|
-
const comments = await xform.parseStream(stream);
|
|
282
|
-
model.comments[`../${name}.xml`] = comments;
|
|
283
|
-
}
|
|
284
|
-
async _processTableEntry(stream, model, name) {
|
|
285
|
-
const xform = new TableXform();
|
|
286
|
-
const table = await xform.parseStream(stream);
|
|
287
|
-
model.tables[`../tables/${name}.xml`] = table;
|
|
288
|
-
}
|
|
289
|
-
async _processWorksheetRelsEntry(stream, model, sheetNo) {
|
|
290
|
-
const xform = new RelationshipsXform();
|
|
291
|
-
const relationships = await xform.parseStream(stream);
|
|
292
|
-
model.worksheetRels[sheetNo] = relationships;
|
|
293
|
-
}
|
|
294
|
-
async _processMediaEntry(stream, model, filename) {
|
|
295
|
-
const lastDot = filename.lastIndexOf(".");
|
|
296
|
-
// if we can't determine extension, ignore it
|
|
297
|
-
if (lastDot >= 1) {
|
|
298
|
-
const extension = filename.substr(lastDot + 1);
|
|
299
|
-
const name = filename.substr(0, lastDot);
|
|
300
|
-
await new Promise((resolve, reject) => {
|
|
301
|
-
const streamBuf = new StreamBuf();
|
|
302
|
-
const cleanup = () => {
|
|
303
|
-
stream.removeListener("error", onError);
|
|
304
|
-
streamBuf.removeListener("error", onError);
|
|
305
|
-
streamBuf.removeListener("finish", onFinish);
|
|
306
|
-
};
|
|
307
|
-
const onFinish = () => {
|
|
308
|
-
cleanup();
|
|
309
|
-
model.mediaIndex[filename] = model.media.length;
|
|
310
|
-
model.mediaIndex[name] = model.media.length;
|
|
311
|
-
const medium = {
|
|
312
|
-
type: "image",
|
|
313
|
-
name,
|
|
314
|
-
extension,
|
|
315
|
-
buffer: streamBuf.toBuffer()
|
|
316
|
-
};
|
|
317
|
-
model.media.push(medium);
|
|
318
|
-
resolve();
|
|
319
|
-
};
|
|
320
|
-
const onError = (error) => {
|
|
321
|
-
cleanup();
|
|
322
|
-
reject(error);
|
|
323
|
-
};
|
|
324
|
-
streamBuf.once("finish", onFinish);
|
|
325
|
-
stream.on("error", onError);
|
|
326
|
-
streamBuf.on("error", onError);
|
|
327
|
-
stream.pipe(streamBuf);
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
async _processDrawingEntry(entry, model, name) {
|
|
332
|
-
const xform = new DrawingXform();
|
|
333
|
-
const drawing = await xform.parseStream(entry);
|
|
334
|
-
model.drawings[name] = drawing;
|
|
335
|
-
}
|
|
336
|
-
async _processDrawingRelsEntry(entry, model, name) {
|
|
337
|
-
const xform = new RelationshipsXform();
|
|
338
|
-
const relationships = await xform.parseStream(entry);
|
|
339
|
-
model.drawingRels[name] = relationships;
|
|
340
|
-
}
|
|
341
|
-
async _processVmlDrawingEntry(entry, model, name) {
|
|
342
|
-
const xform = new VmlNotesXform();
|
|
343
|
-
const vmlDrawing = await xform.parseStream(entry);
|
|
344
|
-
model.vmlDrawings[`../drawings/${name}.vml`] = vmlDrawing;
|
|
345
|
-
}
|
|
346
|
-
async _processThemeEntry(stream, model, name) {
|
|
347
|
-
await new Promise((resolve, reject) => {
|
|
348
|
-
// TODO: stream entry into buffer and store the xml in the model.themes[]
|
|
349
|
-
const streamBuf = new StreamBuf();
|
|
72
|
+
writeFile(filename, options) {
|
|
73
|
+
const stream = fs.createWriteStream(filename);
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
350
75
|
const cleanup = () => {
|
|
76
|
+
stream.removeListener("finish", onFinish);
|
|
351
77
|
stream.removeListener("error", onError);
|
|
352
|
-
streamBuf.removeListener("error", onError);
|
|
353
|
-
streamBuf.removeListener("finish", onFinish);
|
|
354
78
|
};
|
|
355
79
|
const onFinish = () => {
|
|
356
80
|
cleanup();
|
|
357
|
-
model.themes[name] = streamBuf.read().toString();
|
|
358
81
|
resolve();
|
|
359
82
|
};
|
|
360
|
-
const onError = (
|
|
83
|
+
const onError = (error) => {
|
|
361
84
|
cleanup();
|
|
362
|
-
reject(
|
|
85
|
+
reject(error);
|
|
363
86
|
};
|
|
364
|
-
|
|
87
|
+
stream.once("finish", onFinish);
|
|
365
88
|
stream.on("error", onError);
|
|
366
|
-
|
|
367
|
-
|
|
89
|
+
this.write(stream, options)
|
|
90
|
+
.then(() => {
|
|
91
|
+
stream.end();
|
|
92
|
+
})
|
|
93
|
+
.catch(err => {
|
|
94
|
+
cleanup();
|
|
95
|
+
reject(err);
|
|
96
|
+
});
|
|
368
97
|
});
|
|
369
98
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
if (pivotTable) {
|
|
374
|
-
model.pivotTables[name] = pivotTable;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
async _processPivotTableRelsEntry(stream, model, name) {
|
|
378
|
-
const xform = new RelationshipsXform();
|
|
379
|
-
const relationships = await xform.parseStream(stream);
|
|
380
|
-
model.pivotTableRels[name] = relationships;
|
|
381
|
-
}
|
|
382
|
-
async _processPivotCacheDefinitionEntry(stream, model, name) {
|
|
383
|
-
const xform = new PivotCacheDefinitionXform();
|
|
384
|
-
const cacheDefinition = await xform.parseStream(stream);
|
|
385
|
-
if (cacheDefinition) {
|
|
386
|
-
model.pivotCacheDefinitions[name] = cacheDefinition;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
async _processPivotCacheDefinitionRelsEntry(stream, model, name) {
|
|
390
|
-
const xform = new RelationshipsXform();
|
|
391
|
-
const relationships = await xform.parseStream(stream);
|
|
392
|
-
model.pivotCacheDefinitionRels[name] = relationships;
|
|
393
|
-
}
|
|
394
|
-
async _processPivotCacheRecordsEntry(stream, model, name) {
|
|
395
|
-
const xform = new PivotCacheRecordsXform();
|
|
396
|
-
const cacheRecords = await xform.parseStream(stream);
|
|
397
|
-
if (cacheRecords) {
|
|
398
|
-
model.pivotCacheRecords[name] = cacheRecords;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
99
|
+
// ===========================================================================
|
|
100
|
+
// Node.js specific: Stream operations
|
|
101
|
+
// ===========================================================================
|
|
401
102
|
async read(stream, options) {
|
|
402
|
-
//
|
|
403
|
-
const
|
|
103
|
+
// Collect all stream data into a single buffer
|
|
104
|
+
const chunks = [];
|
|
404
105
|
await new Promise((resolve, reject) => {
|
|
405
|
-
let filesProcessed = 0;
|
|
406
|
-
let zipEnded = false;
|
|
407
|
-
let filesStarted = 0;
|
|
408
|
-
const cleanup = () => {
|
|
409
|
-
stream.removeListener("data", onData);
|
|
410
|
-
stream.removeListener("end", onEnd);
|
|
411
|
-
stream.removeListener("error", onError);
|
|
412
|
-
};
|
|
413
|
-
const checkCompletion = () => {
|
|
414
|
-
if (zipEnded && filesProcessed === filesStarted) {
|
|
415
|
-
cleanup();
|
|
416
|
-
resolve();
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
const unzipper = new Unzip((file) => {
|
|
420
|
-
filesStarted++;
|
|
421
|
-
const fileChunks = [];
|
|
422
|
-
let totalLength = 0;
|
|
423
|
-
file.ondata = (err, data, final) => {
|
|
424
|
-
if (err) {
|
|
425
|
-
cleanup();
|
|
426
|
-
reject(err);
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
if (data) {
|
|
430
|
-
fileChunks.push(data);
|
|
431
|
-
totalLength += data.length;
|
|
432
|
-
}
|
|
433
|
-
if (final) {
|
|
434
|
-
// Optimize for single chunk case
|
|
435
|
-
if (fileChunks.length === 1) {
|
|
436
|
-
allFiles[file.name] = fileChunks[0];
|
|
437
|
-
}
|
|
438
|
-
else if (fileChunks.length > 1) {
|
|
439
|
-
const fullData = new Uint8Array(totalLength);
|
|
440
|
-
let offset = 0;
|
|
441
|
-
for (const chunk of fileChunks) {
|
|
442
|
-
fullData.set(chunk, offset);
|
|
443
|
-
offset += chunk.length;
|
|
444
|
-
}
|
|
445
|
-
allFiles[file.name] = fullData;
|
|
446
|
-
}
|
|
447
|
-
else {
|
|
448
|
-
allFiles[file.name] = new Uint8Array(0);
|
|
449
|
-
}
|
|
450
|
-
filesProcessed++;
|
|
451
|
-
fileChunks.length = 0;
|
|
452
|
-
checkCompletion();
|
|
453
|
-
}
|
|
454
|
-
};
|
|
455
|
-
file.start();
|
|
456
|
-
});
|
|
457
|
-
unzipper.register(UnzipInflate);
|
|
458
106
|
const onData = (chunk) => {
|
|
459
|
-
|
|
107
|
+
chunks.push(chunk);
|
|
460
108
|
};
|
|
461
109
|
const onEnd = () => {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
110
|
+
stream.removeListener("data", onData);
|
|
111
|
+
stream.removeListener("end", onEnd);
|
|
112
|
+
stream.removeListener("error", onError);
|
|
113
|
+
resolve();
|
|
465
114
|
};
|
|
466
115
|
const onError = (err) => {
|
|
467
|
-
|
|
116
|
+
stream.removeListener("data", onData);
|
|
117
|
+
stream.removeListener("end", onEnd);
|
|
118
|
+
stream.removeListener("error", onError);
|
|
468
119
|
reject(err);
|
|
469
120
|
};
|
|
470
121
|
stream.on("data", onData);
|
|
471
122
|
stream.on("end", onEnd);
|
|
472
123
|
stream.on("error", onError);
|
|
473
124
|
});
|
|
125
|
+
// Combine chunks into a single buffer
|
|
126
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
127
|
+
const buffer = new Uint8Array(totalLength);
|
|
128
|
+
let offset = 0;
|
|
129
|
+
for (const chunk of chunks) {
|
|
130
|
+
buffer.set(chunk, offset);
|
|
131
|
+
offset += chunk.length;
|
|
132
|
+
}
|
|
133
|
+
// Use native ZipParser for extraction
|
|
134
|
+
const parser = new ZipParser(buffer);
|
|
135
|
+
const filesMap = await parser.extractAll();
|
|
136
|
+
// Convert Map to Record for loadFromFiles
|
|
137
|
+
const allFiles = {};
|
|
138
|
+
for (const [path, content] of filesMap) {
|
|
139
|
+
allFiles[path] = content;
|
|
140
|
+
}
|
|
474
141
|
return this.loadFromFiles(allFiles, options);
|
|
475
142
|
}
|
|
143
|
+
async write(stream, options) {
|
|
144
|
+
options = options || {};
|
|
145
|
+
const { model } = this.workbook;
|
|
146
|
+
const zip = new ZipWriter(options.zip);
|
|
147
|
+
zip.pipe(stream);
|
|
148
|
+
this.prepareModel(model, options);
|
|
149
|
+
await this.addContentTypes(zip, model);
|
|
150
|
+
await this.addOfficeRels(zip, model);
|
|
151
|
+
await this.addWorkbookRels(zip, model);
|
|
152
|
+
await this.addWorksheets(zip, model);
|
|
153
|
+
await this.addSharedStrings(zip, model);
|
|
154
|
+
this.addDrawings(zip, model);
|
|
155
|
+
this.addTables(zip, model);
|
|
156
|
+
this.addPivotTables(zip, model);
|
|
157
|
+
await Promise.all([this.addThemes(zip, model), this.addStyles(zip, model)]);
|
|
158
|
+
await this.addMedia(zip, model);
|
|
159
|
+
await Promise.all([this.addApp(zip, model), this.addCore(zip, model)]);
|
|
160
|
+
await this.addWorkbook(zip, model);
|
|
161
|
+
return this._finalize(zip);
|
|
162
|
+
}
|
|
163
|
+
// ===========================================================================
|
|
164
|
+
// Node.js specific: Buffer operations
|
|
165
|
+
// ===========================================================================
|
|
476
166
|
async load(data, options) {
|
|
477
167
|
let buffer;
|
|
478
|
-
// Validate input type
|
|
479
168
|
if (!data ||
|
|
480
169
|
(typeof data === "object" &&
|
|
481
170
|
!Buffer.isBuffer(data) &&
|
|
@@ -489,299 +178,18 @@ class XLSX {
|
|
|
489
178
|
else {
|
|
490
179
|
buffer = data;
|
|
491
180
|
}
|
|
492
|
-
|
|
493
|
-
const PassThroughStream = PassThrough;
|
|
494
|
-
const stream = new PassThroughStream();
|
|
181
|
+
const stream = new PassThrough();
|
|
495
182
|
stream.end(buffer);
|
|
496
183
|
return this.read(stream, options);
|
|
497
184
|
}
|
|
498
|
-
async
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
worksheetRels: [],
|
|
503
|
-
themes: {},
|
|
504
|
-
media: [],
|
|
505
|
-
mediaIndex: {},
|
|
506
|
-
drawings: {},
|
|
507
|
-
drawingRels: {},
|
|
508
|
-
comments: {},
|
|
509
|
-
tables: {},
|
|
510
|
-
vmlDrawings: {},
|
|
511
|
-
// Pivot table storage for loaded files
|
|
512
|
-
pivotTables: {},
|
|
513
|
-
pivotTableRels: {},
|
|
514
|
-
pivotCacheDefinitions: {},
|
|
515
|
-
pivotCacheDefinitionRels: {},
|
|
516
|
-
pivotCacheRecords: {}
|
|
517
|
-
};
|
|
518
|
-
// Convert fflate format to JSZip-like structure for compatibility
|
|
519
|
-
const entries = Object.keys(zipData).map(name => ({
|
|
520
|
-
name,
|
|
521
|
-
dir: name.endsWith("/"),
|
|
522
|
-
data: zipData[name]
|
|
523
|
-
}));
|
|
524
|
-
for (const entry of entries) {
|
|
525
|
-
if (!entry.dir) {
|
|
526
|
-
let entryName = entry.name;
|
|
527
|
-
if (entryName[0] === "/") {
|
|
528
|
-
entryName = entryName.substr(1);
|
|
529
|
-
}
|
|
530
|
-
let stream;
|
|
531
|
-
if (entryName.match(/xl\/media\//) ||
|
|
532
|
-
// themes are not parsed as stream
|
|
533
|
-
entryName.match(/xl\/theme\/([a-zA-Z0-9]+)[.]xml/)) {
|
|
534
|
-
stream = new PassThrough();
|
|
535
|
-
stream.end(Buffer.from(entry.data));
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
// use object mode to avoid buffer-string convention
|
|
539
|
-
stream = new PassThrough({
|
|
540
|
-
readableObjectMode: true,
|
|
541
|
-
writableObjectMode: true
|
|
542
|
-
});
|
|
543
|
-
const content = bufferToString(Buffer.from(entry.data));
|
|
544
|
-
stream.end(content);
|
|
545
|
-
}
|
|
546
|
-
let match;
|
|
547
|
-
match = entryName.match(/xl\/worksheets\/sheet(\d+)[.]xml/);
|
|
548
|
-
if (match) {
|
|
549
|
-
const sheetNo = parseInt(match[1], 10);
|
|
550
|
-
await this._processWorksheetEntry(stream, model, sheetNo, options, entryName);
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
553
|
-
switch (entryName) {
|
|
554
|
-
case "_rels/.rels":
|
|
555
|
-
model.globalRels = await this.parseRels(stream);
|
|
556
|
-
break;
|
|
557
|
-
case "xl/workbook.xml": {
|
|
558
|
-
const workbook = await this.parseWorkbook(stream);
|
|
559
|
-
model.sheets = workbook.sheets;
|
|
560
|
-
model.definedNames = workbook.definedNames;
|
|
561
|
-
model.views = workbook.views;
|
|
562
|
-
model.properties = workbook.properties;
|
|
563
|
-
model.calcProperties = workbook.calcProperties;
|
|
564
|
-
// pivotCaches contains the mapping from rId to cacheId
|
|
565
|
-
// needed for linking pivot tables to their cache data
|
|
566
|
-
model.pivotCaches = workbook.pivotCaches;
|
|
567
|
-
break;
|
|
568
|
-
}
|
|
569
|
-
case "xl/sharedStrings.xml":
|
|
570
|
-
model.sharedStrings = new SharedStringsXform();
|
|
571
|
-
await model.sharedStrings.parseStream(stream);
|
|
572
|
-
break;
|
|
573
|
-
case "xl/_rels/workbook.xml.rels":
|
|
574
|
-
model.workbookRels = await this.parseRels(stream);
|
|
575
|
-
break;
|
|
576
|
-
case "docProps/app.xml": {
|
|
577
|
-
const appXform = new AppXform();
|
|
578
|
-
const appProperties = await appXform.parseStream(stream);
|
|
579
|
-
model.company = appProperties.company;
|
|
580
|
-
model.manager = appProperties.manager;
|
|
581
|
-
break;
|
|
582
|
-
}
|
|
583
|
-
case "docProps/core.xml": {
|
|
584
|
-
const coreXform = new CoreXform();
|
|
585
|
-
const coreProperties = await coreXform.parseStream(stream);
|
|
586
|
-
Object.assign(model, coreProperties);
|
|
587
|
-
break;
|
|
588
|
-
}
|
|
589
|
-
case "xl/styles.xml":
|
|
590
|
-
model.styles = new StylesXform();
|
|
591
|
-
await model.styles.parseStream(stream);
|
|
592
|
-
break;
|
|
593
|
-
default: {
|
|
594
|
-
match = entryName.match(/xl\/worksheets\/_rels\/sheet(\d+)[.]xml[.]rels/);
|
|
595
|
-
if (match) {
|
|
596
|
-
const sheetNo = parseInt(match[1], 10);
|
|
597
|
-
await this._processWorksheetRelsEntry(stream, model, sheetNo);
|
|
598
|
-
break;
|
|
599
|
-
}
|
|
600
|
-
match = entryName.match(/xl\/media\/([a-zA-Z0-9]+[.][a-zA-Z0-9]{3,4})$/);
|
|
601
|
-
if (match) {
|
|
602
|
-
await this._processMediaEntry(stream, model, match[1]);
|
|
603
|
-
break;
|
|
604
|
-
}
|
|
605
|
-
match = entryName.match(/xl\/drawings\/(drawing\d+)[.]xml/);
|
|
606
|
-
if (match) {
|
|
607
|
-
await this._processDrawingEntry(stream, model, match[1]);
|
|
608
|
-
break;
|
|
609
|
-
}
|
|
610
|
-
match = entryName.match(/xl\/drawings\/_rels\/(drawing\d+)[.]xml[.]rels/);
|
|
611
|
-
if (match) {
|
|
612
|
-
await this._processDrawingRelsEntry(stream, model, match[1]);
|
|
613
|
-
break;
|
|
614
|
-
}
|
|
615
|
-
match = entryName.match(/xl\/drawings\/(vmlDrawing\d+)[.]vml/);
|
|
616
|
-
if (match) {
|
|
617
|
-
await this._processVmlDrawingEntry(stream, model, match[1]);
|
|
618
|
-
break;
|
|
619
|
-
}
|
|
620
|
-
match = entryName.match(/xl\/comments(\d+)[.]xml/);
|
|
621
|
-
if (match) {
|
|
622
|
-
await this._processCommentEntry(stream, model, `comments${match[1]}`);
|
|
623
|
-
break;
|
|
624
|
-
}
|
|
625
|
-
match = entryName.match(/xl\/tables\/(table\d+)[.]xml/);
|
|
626
|
-
if (match) {
|
|
627
|
-
await this._processTableEntry(stream, model, match[1]);
|
|
628
|
-
break;
|
|
629
|
-
}
|
|
630
|
-
match = entryName.match(/xl\/theme\/([a-zA-Z0-9]+)[.]xml/);
|
|
631
|
-
if (match) {
|
|
632
|
-
await this._processThemeEntry(stream, model, match[1]);
|
|
633
|
-
break;
|
|
634
|
-
}
|
|
635
|
-
// Pivot table files
|
|
636
|
-
match = entryName.match(/xl\/pivotTables\/(pivotTable\d+)[.]xml/);
|
|
637
|
-
if (match) {
|
|
638
|
-
await this._processPivotTableEntry(stream, model, match[1]);
|
|
639
|
-
break;
|
|
640
|
-
}
|
|
641
|
-
match = entryName.match(/xl\/pivotTables\/_rels\/(pivotTable\d+)[.]xml[.]rels/);
|
|
642
|
-
if (match) {
|
|
643
|
-
await this._processPivotTableRelsEntry(stream, model, match[1]);
|
|
644
|
-
break;
|
|
645
|
-
}
|
|
646
|
-
// Pivot cache files
|
|
647
|
-
match = entryName.match(/xl\/pivotCache\/(pivotCacheDefinition\d+)[.]xml/);
|
|
648
|
-
if (match) {
|
|
649
|
-
await this._processPivotCacheDefinitionEntry(stream, model, match[1]);
|
|
650
|
-
break;
|
|
651
|
-
}
|
|
652
|
-
match = entryName.match(/xl\/pivotCache\/_rels\/(pivotCacheDefinition\d+)[.]xml[.]rels/);
|
|
653
|
-
if (match) {
|
|
654
|
-
await this._processPivotCacheDefinitionRelsEntry(stream, model, match[1]);
|
|
655
|
-
break;
|
|
656
|
-
}
|
|
657
|
-
match = entryName.match(/xl\/pivotCache\/(pivotCacheRecords\d+)[.]xml/);
|
|
658
|
-
if (match) {
|
|
659
|
-
await this._processPivotCacheRecordsEntry(stream, model, match[1]);
|
|
660
|
-
break;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
this.reconcile(model, options);
|
|
668
|
-
// apply model
|
|
669
|
-
this.workbook.model = model;
|
|
670
|
-
return this.workbook;
|
|
671
|
-
}
|
|
672
|
-
// =========================================================================
|
|
673
|
-
// Write
|
|
674
|
-
async addContentTypes(zip, model) {
|
|
675
|
-
const xform = new ContentTypesXform();
|
|
676
|
-
const xml = xform.toXml(model);
|
|
677
|
-
zip.append(xml, { name: "[Content_Types].xml" });
|
|
678
|
-
}
|
|
679
|
-
async addApp(zip, model) {
|
|
680
|
-
const xform = new AppXform();
|
|
681
|
-
const xml = xform.toXml(model);
|
|
682
|
-
zip.append(xml, { name: "docProps/app.xml" });
|
|
683
|
-
}
|
|
684
|
-
async addCore(zip, model) {
|
|
685
|
-
const xform = new CoreXform();
|
|
686
|
-
zip.append(xform.toXml(model), { name: "docProps/core.xml" });
|
|
687
|
-
}
|
|
688
|
-
async addThemes(zip, model) {
|
|
689
|
-
const themes = model.themes || { theme1: theme1Xml };
|
|
690
|
-
Object.keys(themes).forEach(name => {
|
|
691
|
-
const xml = themes[name];
|
|
692
|
-
const path = `xl/theme/${name}.xml`;
|
|
693
|
-
zip.append(xml, { name: path });
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
async addOfficeRels(zip, _model) {
|
|
697
|
-
const xform = new RelationshipsXform();
|
|
698
|
-
const xml = xform.toXml([
|
|
699
|
-
{ Id: "rId1", Type: XLSX.RelType.OfficeDocument, Target: "xl/workbook.xml" },
|
|
700
|
-
{ Id: "rId2", Type: XLSX.RelType.CoreProperties, Target: "docProps/core.xml" },
|
|
701
|
-
{ Id: "rId3", Type: XLSX.RelType.ExtenderProperties, Target: "docProps/app.xml" }
|
|
702
|
-
]);
|
|
703
|
-
zip.append(xml, { name: "_rels/.rels" });
|
|
704
|
-
}
|
|
705
|
-
async addWorkbookRels(zip, model) {
|
|
706
|
-
let count = 1;
|
|
707
|
-
const relationships = [
|
|
708
|
-
{ Id: `rId${count++}`, Type: XLSX.RelType.Styles, Target: "styles.xml" },
|
|
709
|
-
{ Id: `rId${count++}`, Type: XLSX.RelType.Theme, Target: "theme/theme1.xml" }
|
|
710
|
-
];
|
|
711
|
-
if (model.sharedStrings.count) {
|
|
712
|
-
relationships.push({
|
|
713
|
-
Id: `rId${count++}`,
|
|
714
|
-
Type: XLSX.RelType.SharedStrings,
|
|
715
|
-
Target: "sharedStrings.xml"
|
|
716
|
-
});
|
|
717
|
-
}
|
|
718
|
-
// Add relationships for all pivot tables
|
|
719
|
-
(model.pivotTables || []).forEach((pivotTable) => {
|
|
720
|
-
pivotTable.rId = `rId${count++}`;
|
|
721
|
-
relationships.push({
|
|
722
|
-
Id: pivotTable.rId,
|
|
723
|
-
Type: XLSX.RelType.PivotCacheDefinition,
|
|
724
|
-
Target: `pivotCache/pivotCacheDefinition${pivotTable.tableNumber}.xml`
|
|
725
|
-
});
|
|
726
|
-
});
|
|
727
|
-
model.worksheets.forEach((worksheet, index) => {
|
|
728
|
-
// Use sequential index (1-based) for file naming, not worksheet.id (sheetId)
|
|
729
|
-
// sheetId can be non-sequential (e.g., 1, 3, 5) which would create gaps in filenames
|
|
730
|
-
worksheet.rId = `rId${count++}`;
|
|
731
|
-
worksheet.fileIndex = index + 1; // Store for use in addWorksheets and content types
|
|
732
|
-
relationships.push({
|
|
733
|
-
Id: worksheet.rId,
|
|
734
|
-
Type: XLSX.RelType.Worksheet,
|
|
735
|
-
Target: `worksheets/sheet${worksheet.fileIndex}.xml`
|
|
736
|
-
});
|
|
737
|
-
});
|
|
738
|
-
const xform = new RelationshipsXform();
|
|
739
|
-
const xml = xform.toXml(relationships);
|
|
740
|
-
zip.append(xml, { name: "xl/_rels/workbook.xml.rels" });
|
|
741
|
-
}
|
|
742
|
-
async addSharedStrings(zip, model) {
|
|
743
|
-
if (model.sharedStrings && model.sharedStrings.count) {
|
|
744
|
-
zip.append(model.sharedStrings.xml, { name: "xl/sharedStrings.xml" });
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
async addStyles(zip, model) {
|
|
748
|
-
const { xml } = model.styles;
|
|
749
|
-
if (xml) {
|
|
750
|
-
zip.append(xml, { name: "xl/styles.xml" });
|
|
751
|
-
}
|
|
752
|
-
}
|
|
753
|
-
async addWorkbook(zip, model) {
|
|
754
|
-
const xform = new WorkbookXform();
|
|
755
|
-
zip.append(xform.toXml(model), { name: "xl/workbook.xml" });
|
|
756
|
-
}
|
|
757
|
-
async addWorksheets(zip, model) {
|
|
758
|
-
// preparation phase
|
|
759
|
-
const worksheetXform = new WorkSheetXform();
|
|
760
|
-
const relationshipsXform = new RelationshipsXform();
|
|
761
|
-
const commentsXform = new CommentsXform();
|
|
762
|
-
const vmlNotesXform = new VmlNotesXform();
|
|
763
|
-
// write sheets
|
|
764
|
-
model.worksheets.forEach((worksheet, index) => {
|
|
765
|
-
// Use fileIndex if set by addWorkbookRels, otherwise use index + 1
|
|
766
|
-
const fileIndex = worksheet.fileIndex || index + 1;
|
|
767
|
-
let xmlStream = new XmlStream();
|
|
768
|
-
worksheetXform.render(xmlStream, worksheet);
|
|
769
|
-
zip.append(xmlStream.xml, { name: `xl/worksheets/sheet${fileIndex}.xml` });
|
|
770
|
-
if (worksheet.rels && worksheet.rels.length) {
|
|
771
|
-
xmlStream = new XmlStream();
|
|
772
|
-
relationshipsXform.render(xmlStream, worksheet.rels);
|
|
773
|
-
zip.append(xmlStream.xml, { name: `xl/worksheets/_rels/sheet${fileIndex}.xml.rels` });
|
|
774
|
-
}
|
|
775
|
-
if (worksheet.comments.length > 0) {
|
|
776
|
-
xmlStream = new XmlStream();
|
|
777
|
-
commentsXform.render(xmlStream, worksheet);
|
|
778
|
-
zip.append(xmlStream.xml, { name: `xl/comments${fileIndex}.xml` });
|
|
779
|
-
xmlStream = new XmlStream();
|
|
780
|
-
vmlNotesXform.render(xmlStream, worksheet);
|
|
781
|
-
zip.append(xmlStream.xml, { name: `xl/drawings/vmlDrawing${fileIndex}.vml` });
|
|
782
|
-
}
|
|
783
|
-
});
|
|
185
|
+
async writeBuffer(options) {
|
|
186
|
+
const stream = new StreamBuf();
|
|
187
|
+
await this.write(stream, options);
|
|
188
|
+
return stream.read();
|
|
784
189
|
}
|
|
190
|
+
// ===========================================================================
|
|
191
|
+
// Node.js specific: Media handling with file support
|
|
192
|
+
// ===========================================================================
|
|
785
193
|
async addMedia(zip, model) {
|
|
786
194
|
await Promise.all(model.media.map(async (medium) => {
|
|
787
195
|
if (medium.type === "image") {
|
|
@@ -802,187 +210,5 @@ class XLSX {
|
|
|
802
210
|
throw new Error("Unsupported media");
|
|
803
211
|
}));
|
|
804
212
|
}
|
|
805
|
-
addDrawings(zip, model) {
|
|
806
|
-
const drawingXform = new DrawingXform();
|
|
807
|
-
const relsXform = new RelationshipsXform();
|
|
808
|
-
model.worksheets.forEach((worksheet) => {
|
|
809
|
-
const { drawing } = worksheet;
|
|
810
|
-
if (drawing) {
|
|
811
|
-
drawingXform.prepare(drawing);
|
|
812
|
-
let xml = drawingXform.toXml(drawing);
|
|
813
|
-
zip.append(xml, { name: `xl/drawings/${drawing.name}.xml` });
|
|
814
|
-
xml = relsXform.toXml(drawing.rels);
|
|
815
|
-
zip.append(xml, { name: `xl/drawings/_rels/${drawing.name}.xml.rels` });
|
|
816
|
-
}
|
|
817
|
-
});
|
|
818
|
-
}
|
|
819
|
-
addTables(zip, model) {
|
|
820
|
-
const tableXform = new TableXform();
|
|
821
|
-
model.worksheets.forEach((worksheet) => {
|
|
822
|
-
const { tables } = worksheet;
|
|
823
|
-
tables.forEach((table) => {
|
|
824
|
-
tableXform.prepare(table, {});
|
|
825
|
-
const tableXml = tableXform.toXml(table);
|
|
826
|
-
zip.append(tableXml, { name: `xl/tables/${table.target}` });
|
|
827
|
-
});
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
addPivotTables(zip, model) {
|
|
831
|
-
if (!model.pivotTables.length) {
|
|
832
|
-
return;
|
|
833
|
-
}
|
|
834
|
-
const pivotCacheRecordsXform = new PivotCacheRecordsXform();
|
|
835
|
-
const pivotCacheDefinitionXform = new PivotCacheDefinitionXform();
|
|
836
|
-
const pivotTableXform = new PivotTableXform();
|
|
837
|
-
const relsXform = new RelationshipsXform();
|
|
838
|
-
// Generate files for each pivot table
|
|
839
|
-
model.pivotTables.forEach((pivotTable) => {
|
|
840
|
-
const n = pivotTable.tableNumber;
|
|
841
|
-
// For loaded pivot tables, use the stored cache data
|
|
842
|
-
// For new pivot tables, use the source data
|
|
843
|
-
const isLoaded = pivotTable.isLoaded;
|
|
844
|
-
if (isLoaded) {
|
|
845
|
-
// Loaded pivot table - use stored cache definition and records
|
|
846
|
-
if (pivotTable.cacheDefinition) {
|
|
847
|
-
const xml = pivotCacheDefinitionXform.toXml(pivotTable.cacheDefinition);
|
|
848
|
-
zip.append(xml, { name: `xl/pivotCache/pivotCacheDefinition${n}.xml` });
|
|
849
|
-
}
|
|
850
|
-
if (pivotTable.cacheRecords) {
|
|
851
|
-
const xml = pivotCacheRecordsXform.toXml(pivotTable.cacheRecords);
|
|
852
|
-
zip.append(xml, { name: `xl/pivotCache/pivotCacheRecords${n}.xml` });
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
else {
|
|
856
|
-
// New pivot table - generate from source
|
|
857
|
-
let xml = pivotCacheRecordsXform.toXml(pivotTable);
|
|
858
|
-
zip.append(xml, { name: `xl/pivotCache/pivotCacheRecords${n}.xml` });
|
|
859
|
-
xml = pivotCacheDefinitionXform.toXml(pivotTable);
|
|
860
|
-
zip.append(xml, { name: `xl/pivotCache/pivotCacheDefinition${n}.xml` });
|
|
861
|
-
}
|
|
862
|
-
// pivot cache definition rels (same for both)
|
|
863
|
-
let xml = relsXform.toXml([
|
|
864
|
-
{
|
|
865
|
-
Id: "rId1",
|
|
866
|
-
Type: XLSX.RelType.PivotCacheRecords,
|
|
867
|
-
Target: `pivotCacheRecords${n}.xml`
|
|
868
|
-
}
|
|
869
|
-
]);
|
|
870
|
-
zip.append(xml, { name: `xl/pivotCache/_rels/pivotCacheDefinition${n}.xml.rels` });
|
|
871
|
-
// pivot table
|
|
872
|
-
xml = pivotTableXform.toXml(pivotTable);
|
|
873
|
-
zip.append(xml, { name: `xl/pivotTables/pivotTable${n}.xml` });
|
|
874
|
-
xml = relsXform.toXml([
|
|
875
|
-
{
|
|
876
|
-
Id: "rId1",
|
|
877
|
-
Type: XLSX.RelType.PivotCacheDefinition,
|
|
878
|
-
Target: `../pivotCache/pivotCacheDefinition${n}.xml`
|
|
879
|
-
}
|
|
880
|
-
]);
|
|
881
|
-
zip.append(xml, { name: `xl/pivotTables/_rels/pivotTable${n}.xml.rels` });
|
|
882
|
-
});
|
|
883
|
-
}
|
|
884
|
-
_finalize(zip) {
|
|
885
|
-
return new Promise((resolve, reject) => {
|
|
886
|
-
zip.on("finish", () => {
|
|
887
|
-
resolve(this);
|
|
888
|
-
});
|
|
889
|
-
zip.on("error", reject);
|
|
890
|
-
zip.finalize();
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
prepareModel(model, options) {
|
|
894
|
-
// ensure following properties have sane values
|
|
895
|
-
model.creator = model.creator || "ExcelTS";
|
|
896
|
-
model.lastModifiedBy = model.lastModifiedBy || "ExcelTS";
|
|
897
|
-
model.created = model.created || new Date();
|
|
898
|
-
model.modified = model.modified || new Date();
|
|
899
|
-
model.useSharedStrings =
|
|
900
|
-
options.useSharedStrings !== undefined ? options.useSharedStrings : true;
|
|
901
|
-
model.useStyles = options.useStyles !== undefined ? options.useStyles : true;
|
|
902
|
-
// Manage the shared strings
|
|
903
|
-
model.sharedStrings = new SharedStringsXform();
|
|
904
|
-
// add a style manager to handle cell formats, fonts, etc.
|
|
905
|
-
model.styles = model.useStyles ? new StylesXform(true) : new StylesXform.Mock();
|
|
906
|
-
// prepare all of the things before the render
|
|
907
|
-
const workbookXform = new WorkbookXform();
|
|
908
|
-
const worksheetXform = new WorkSheetXform();
|
|
909
|
-
workbookXform.prepare(model);
|
|
910
|
-
const worksheetOptions = {
|
|
911
|
-
sharedStrings: model.sharedStrings,
|
|
912
|
-
styles: model.styles,
|
|
913
|
-
date1904: model.properties.date1904,
|
|
914
|
-
drawingsCount: 0,
|
|
915
|
-
media: model.media
|
|
916
|
-
};
|
|
917
|
-
worksheetOptions.drawings = model.drawings = [];
|
|
918
|
-
worksheetOptions.commentRefs = model.commentRefs = [];
|
|
919
|
-
let tableCount = 0;
|
|
920
|
-
model.tables = [];
|
|
921
|
-
model.worksheets.forEach((worksheet) => {
|
|
922
|
-
// assign unique filenames to tables
|
|
923
|
-
worksheet.tables.forEach((table) => {
|
|
924
|
-
tableCount++;
|
|
925
|
-
table.target = `table${tableCount}.xml`;
|
|
926
|
-
table.id = tableCount;
|
|
927
|
-
model.tables.push(table);
|
|
928
|
-
});
|
|
929
|
-
worksheetXform.prepare(worksheet, worksheetOptions);
|
|
930
|
-
});
|
|
931
|
-
// TODO: workbook drawing list
|
|
932
|
-
}
|
|
933
|
-
async write(stream, options) {
|
|
934
|
-
options = options || {};
|
|
935
|
-
const { model } = this.workbook;
|
|
936
|
-
const zip = new ZipWriter(options.zip);
|
|
937
|
-
zip.pipe(stream);
|
|
938
|
-
this.prepareModel(model, options);
|
|
939
|
-
// render
|
|
940
|
-
await this.addContentTypes(zip, model);
|
|
941
|
-
await this.addOfficeRels(zip, model);
|
|
942
|
-
await this.addWorkbookRels(zip, model);
|
|
943
|
-
await this.addWorksheets(zip, model);
|
|
944
|
-
await this.addSharedStrings(zip, model); // always after worksheets
|
|
945
|
-
this.addDrawings(zip, model);
|
|
946
|
-
this.addTables(zip, model);
|
|
947
|
-
this.addPivotTables(zip, model);
|
|
948
|
-
await Promise.all([this.addThemes(zip, model), this.addStyles(zip, model)]);
|
|
949
|
-
await this.addMedia(zip, model);
|
|
950
|
-
await Promise.all([this.addApp(zip, model), this.addCore(zip, model)]);
|
|
951
|
-
await this.addWorkbook(zip, model);
|
|
952
|
-
return this._finalize(zip);
|
|
953
|
-
}
|
|
954
|
-
writeFile(filename, options) {
|
|
955
|
-
const stream = fs.createWriteStream(filename);
|
|
956
|
-
return new Promise((resolve, reject) => {
|
|
957
|
-
const cleanup = () => {
|
|
958
|
-
stream.removeListener("finish", onFinish);
|
|
959
|
-
stream.removeListener("error", onError);
|
|
960
|
-
};
|
|
961
|
-
const onFinish = () => {
|
|
962
|
-
cleanup();
|
|
963
|
-
resolve();
|
|
964
|
-
};
|
|
965
|
-
const onError = (error) => {
|
|
966
|
-
cleanup();
|
|
967
|
-
reject(error);
|
|
968
|
-
};
|
|
969
|
-
stream.once("finish", onFinish);
|
|
970
|
-
stream.on("error", onError);
|
|
971
|
-
this.write(stream, options)
|
|
972
|
-
.then(() => {
|
|
973
|
-
stream.end();
|
|
974
|
-
})
|
|
975
|
-
.catch(err => {
|
|
976
|
-
cleanup();
|
|
977
|
-
reject(err);
|
|
978
|
-
});
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
async writeBuffer(options) {
|
|
982
|
-
const stream = new StreamBuf();
|
|
983
|
-
await this.write(stream, options);
|
|
984
|
-
return stream.read();
|
|
985
|
-
}
|
|
986
213
|
}
|
|
987
|
-
XLSX.RelType = RelType;
|
|
988
214
|
export { XLSX };
|