@cj-tech-master/excelts 4.2.0 → 4.2.1-canary.20260112134913.a3cecdd
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/THIRD_PARTY_NOTICES.md +0 -31
- package/dist/browser/index.browser.d.ts +1 -0
- package/dist/browser/index.browser.js +12 -0
- package/dist/{types/modules/archive → browser/modules/archive/compression}/compress.base.d.ts +1 -0
- package/dist/browser/modules/archive/{compress.base.js → compression/compress.base.js} +2 -1
- package/dist/{types/modules/archive → browser/modules/archive/compression}/compress.browser.d.ts +10 -8
- package/dist/{esm/modules/archive → browser/modules/archive/compression}/compress.browser.js +18 -19
- package/dist/browser/modules/archive/{compress.d.ts → compression/compress.d.ts} +2 -2
- package/dist/browser/modules/archive/{compress.js → compression/compress.js} +1 -1
- package/dist/browser/modules/archive/{crc32.browser.d.ts → compression/crc32.browser.d.ts} +1 -1
- package/dist/browser/modules/archive/{crc32.d.ts → compression/crc32.d.ts} +1 -1
- package/dist/browser/modules/archive/{crc32.js → compression/crc32.js} +1 -1
- package/dist/browser/modules/archive/{deflate-fallback.js → compression/deflate-fallback.js} +1 -1
- package/dist/browser/modules/archive/{streaming-compress.browser.d.ts → compression/streaming-compress.browser.d.ts} +2 -2
- package/dist/browser/modules/archive/{streaming-compress.browser.js → compression/streaming-compress.browser.js} +3 -3
- package/dist/browser/modules/archive/{streaming-compress.d.ts → compression/streaming-compress.d.ts} +2 -2
- package/dist/browser/modules/archive/{streaming-compress.js → compression/streaming-compress.js} +2 -2
- package/dist/browser/modules/archive/defaults.d.ts +1 -0
- package/dist/browser/modules/archive/defaults.js +6 -3
- package/dist/browser/modules/archive/index.base.d.ts +4 -4
- package/dist/browser/modules/archive/index.base.js +3 -6
- package/dist/browser/modules/archive/index.browser.d.ts +3 -4
- package/dist/browser/modules/archive/index.browser.js +3 -7
- package/dist/browser/modules/archive/index.d.ts +3 -4
- package/dist/browser/modules/archive/index.js +3 -5
- package/dist/browser/modules/archive/internal/byte-queue.d.ts +33 -0
- package/dist/browser/modules/archive/internal/byte-queue.js +407 -0
- package/dist/browser/modules/archive/io/archive-sink.d.ts +8 -0
- package/dist/browser/modules/archive/io/archive-sink.js +45 -0
- package/dist/browser/modules/archive/io/archive-source.d.ts +6 -0
- package/dist/browser/modules/archive/io/archive-source.js +100 -0
- package/dist/browser/modules/archive/{extract.d.ts → unzip/extract.d.ts} +2 -2
- package/dist/browser/modules/archive/unzip/index.d.ts +40 -0
- package/dist/browser/modules/archive/unzip/index.js +164 -0
- package/dist/browser/modules/archive/{parse.base.d.ts → unzip/stream.base.d.ts} +58 -3
- package/dist/browser/modules/archive/unzip/stream.base.js +1022 -0
- package/dist/browser/modules/archive/{parse.browser.d.ts → unzip/stream.browser.d.ts} +1 -1
- package/dist/browser/modules/archive/{parse.browser.js → unzip/stream.browser.js} +376 -110
- package/dist/browser/modules/archive/{parse.d.ts → unzip/stream.d.ts} +2 -2
- package/dist/{esm/modules/archive/parse.js → browser/modules/archive/unzip/stream.js} +7 -6
- package/dist/{types/modules/archive → browser/modules/archive/unzip}/zip-parser.d.ts +1 -1
- package/dist/{esm/modules/archive → browser/modules/archive/unzip}/zip-parser.js +38 -24
- package/dist/browser/modules/archive/utils/async-queue.d.ts +7 -0
- package/dist/browser/modules/archive/utils/async-queue.js +103 -0
- package/dist/browser/modules/archive/utils/bytes.js +16 -16
- package/dist/browser/modules/archive/utils/compressibility.d.ts +10 -0
- package/dist/browser/modules/archive/utils/compressibility.js +57 -0
- package/dist/browser/modules/archive/utils/parse-buffer.js +21 -23
- package/dist/browser/modules/archive/utils/pattern-scanner.d.ts +21 -0
- package/dist/browser/modules/archive/utils/pattern-scanner.js +27 -0
- package/dist/browser/modules/archive/utils/timestamps.js +62 -1
- package/dist/browser/modules/archive/utils/zip-extra-fields.d.ts +1 -1
- package/dist/browser/modules/archive/utils/zip-extra-fields.js +26 -14
- package/dist/browser/modules/archive/zip/index.d.ts +42 -0
- package/dist/browser/modules/archive/zip/index.js +157 -0
- package/dist/browser/modules/archive/{streaming-zip.d.ts → zip/stream.d.ts} +28 -5
- package/dist/browser/modules/archive/{streaming-zip.js → zip/stream.js} +192 -48
- package/dist/browser/modules/archive/zip/zip-bytes.d.ts +73 -0
- package/dist/browser/modules/archive/zip/zip-bytes.js +239 -0
- package/dist/{esm/modules/archive → browser/modules/archive/zip}/zip-entry-metadata.js +3 -3
- package/dist/browser/modules/archive/{zip-records.d.ts → zip-spec/zip-records.d.ts} +20 -0
- package/dist/browser/modules/archive/zip-spec/zip-records.js +126 -0
- package/dist/browser/modules/excel/form-control.d.ts +2 -0
- package/dist/browser/modules/excel/form-control.js +54 -16
- package/dist/browser/modules/excel/stream/workbook-reader.browser.js +1 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +1 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +1 -1
- package/dist/browser/modules/excel/utils/ooxml-validator.d.ts +48 -0
- package/dist/browser/modules/excel/utils/ooxml-validator.js +469 -0
- package/dist/browser/modules/excel/worksheet.js +5 -2
- package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +13 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/sp-xform.d.ts +18 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/sp-xform.js +112 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.d.ts +6 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +30 -2
- package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +11 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/page-setup-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/page-setup-xform.js +16 -2
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +117 -5
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +3 -6
- package/dist/browser/modules/excel/xlsx/xlsx.js +1 -1
- package/dist/browser/modules/stream/base-transform.d.ts +3 -0
- package/dist/browser/modules/stream/base-transform.js +34 -20
- package/dist/browser/modules/stream/buffered-stream.d.ts +2 -12
- package/dist/browser/modules/stream/chunked-builder.js +4 -4
- package/dist/browser/modules/stream/index.browser.d.ts +13 -19
- package/dist/browser/modules/stream/index.browser.js +10 -22
- package/dist/browser/modules/stream/index.d.ts +18 -41
- package/dist/browser/modules/stream/index.js +15 -44
- package/dist/browser/modules/stream/internal/event-utils.d.ts +17 -0
- package/dist/browser/modules/stream/internal/event-utils.js +40 -0
- package/dist/browser/modules/stream/internal/type-guards.d.ts +9 -0
- package/dist/browser/modules/stream/internal/type-guards.js +24 -0
- package/dist/browser/modules/stream/pull-stream.d.ts +5 -6
- package/dist/browser/modules/stream/pull-stream.js +107 -43
- package/dist/browser/modules/stream/shared.d.ts +1 -1
- package/dist/browser/modules/stream/shared.js +7 -4
- package/dist/browser/modules/stream/streams.browser.d.ts +32 -42
- package/dist/browser/modules/stream/streams.browser.js +941 -823
- package/dist/browser/modules/stream/streams.d.ts +4 -20
- package/dist/browser/modules/stream/streams.js +146 -95
- package/dist/browser/modules/stream/utils.js +5 -38
- package/dist/cjs/modules/archive/{compress.base.js → compression/compress.base.js} +2 -1
- package/dist/cjs/modules/archive/{compress.browser.js → compression/compress.browser.js} +18 -19
- package/dist/cjs/modules/archive/{compress.js → compression/compress.js} +1 -1
- package/dist/cjs/modules/archive/{crc32.js → compression/crc32.js} +1 -1
- package/dist/cjs/modules/archive/{deflate-fallback.js → compression/deflate-fallback.js} +1 -1
- package/dist/cjs/modules/archive/{streaming-compress.browser.js → compression/streaming-compress.browser.js} +3 -3
- package/dist/cjs/modules/archive/{streaming-compress.js → compression/streaming-compress.js} +2 -2
- package/dist/cjs/modules/archive/defaults.js +7 -4
- package/dist/cjs/modules/archive/index.base.js +9 -19
- package/dist/cjs/modules/archive/index.browser.js +4 -10
- package/dist/cjs/modules/archive/index.js +4 -8
- package/dist/cjs/modules/archive/internal/byte-queue.js +411 -0
- package/dist/cjs/modules/archive/io/archive-sink.js +49 -0
- package/dist/cjs/modules/archive/io/archive-source.js +105 -0
- package/dist/cjs/modules/archive/unzip/index.js +170 -0
- package/dist/cjs/modules/archive/unzip/stream.base.js +1044 -0
- package/dist/cjs/modules/archive/{parse.browser.js → unzip/stream.browser.js} +377 -111
- package/dist/cjs/modules/archive/{parse.js → unzip/stream.js} +9 -8
- package/dist/cjs/modules/archive/{zip-parser.js → unzip/zip-parser.js} +47 -33
- package/dist/cjs/modules/archive/utils/async-queue.js +106 -0
- package/dist/cjs/modules/archive/utils/bytes.js +16 -16
- package/dist/cjs/modules/archive/utils/compressibility.js +60 -0
- package/dist/cjs/modules/archive/utils/parse-buffer.js +21 -23
- package/dist/cjs/modules/archive/utils/pattern-scanner.js +31 -0
- package/dist/cjs/modules/archive/utils/timestamps.js +64 -3
- package/dist/cjs/modules/archive/utils/zip-extra-fields.js +26 -14
- package/dist/cjs/modules/archive/zip/index.js +162 -0
- package/dist/cjs/modules/archive/{streaming-zip.js → zip/stream.js} +194 -50
- package/dist/cjs/modules/archive/zip/zip-bytes.js +242 -0
- package/dist/cjs/modules/archive/{zip-entry-metadata.js → zip/zip-entry-metadata.js} +5 -5
- package/dist/cjs/modules/archive/zip-spec/zip-records.js +136 -0
- package/dist/cjs/modules/excel/form-control.js +54 -16
- package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +2 -2
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +4 -4
- package/dist/cjs/modules/excel/utils/ooxml-validator.js +475 -0
- package/dist/cjs/modules/excel/worksheet.js +5 -2
- package/dist/cjs/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +13 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/sp-xform.js +115 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +30 -2
- package/dist/cjs/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +11 -0
- package/dist/cjs/modules/excel/xlsx/xform/sheet/page-setup-xform.js +16 -2
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +117 -5
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +6 -9
- package/dist/cjs/modules/excel/xlsx/xlsx.js +2 -2
- package/dist/cjs/modules/stream/base-transform.js +34 -20
- package/dist/cjs/modules/stream/chunked-builder.js +4 -4
- package/dist/cjs/modules/stream/index.browser.js +10 -17
- package/dist/cjs/modules/stream/index.js +15 -39
- package/dist/cjs/modules/stream/internal/event-utils.js +43 -0
- package/dist/cjs/modules/stream/internal/type-guards.js +30 -0
- package/dist/cjs/modules/stream/pull-stream.js +107 -43
- package/dist/cjs/modules/stream/shared.js +7 -4
- package/dist/cjs/modules/stream/streams.browser.js +947 -834
- package/dist/cjs/modules/stream/streams.js +156 -107
- package/dist/cjs/modules/stream/utils.js +3 -36
- package/dist/esm/index.browser.js +12 -0
- package/dist/esm/modules/archive/{compress.base.js → compression/compress.base.js} +2 -1
- package/dist/{browser/modules/archive → esm/modules/archive/compression}/compress.browser.js +18 -19
- package/dist/esm/modules/archive/{compress.js → compression/compress.js} +1 -1
- package/dist/esm/modules/archive/{crc32.js → compression/crc32.js} +1 -1
- package/dist/esm/modules/archive/{deflate-fallback.js → compression/deflate-fallback.js} +1 -1
- package/dist/esm/modules/archive/{streaming-compress.browser.js → compression/streaming-compress.browser.js} +3 -3
- package/dist/esm/modules/archive/{streaming-compress.js → compression/streaming-compress.js} +2 -2
- package/dist/esm/modules/archive/defaults.js +6 -3
- package/dist/esm/modules/archive/index.base.js +3 -6
- package/dist/esm/modules/archive/index.browser.js +3 -7
- package/dist/esm/modules/archive/index.js +3 -5
- package/dist/esm/modules/archive/internal/byte-queue.js +407 -0
- package/dist/esm/modules/archive/io/archive-sink.js +45 -0
- package/dist/esm/modules/archive/io/archive-source.js +100 -0
- package/dist/esm/modules/archive/unzip/index.js +164 -0
- package/dist/esm/modules/archive/unzip/stream.base.js +1022 -0
- package/dist/esm/modules/archive/{parse.browser.js → unzip/stream.browser.js} +376 -110
- package/dist/{browser/modules/archive/parse.js → esm/modules/archive/unzip/stream.js} +7 -6
- package/dist/{browser/modules/archive → esm/modules/archive/unzip}/zip-parser.js +38 -24
- package/dist/esm/modules/archive/utils/async-queue.js +103 -0
- package/dist/esm/modules/archive/utils/bytes.js +16 -16
- package/dist/esm/modules/archive/utils/compressibility.js +57 -0
- package/dist/esm/modules/archive/utils/parse-buffer.js +21 -23
- package/dist/esm/modules/archive/utils/pattern-scanner.js +27 -0
- package/dist/esm/modules/archive/utils/timestamps.js +62 -1
- package/dist/esm/modules/archive/utils/zip-extra-fields.js +26 -14
- package/dist/esm/modules/archive/zip/index.js +157 -0
- package/dist/esm/modules/archive/{streaming-zip.js → zip/stream.js} +192 -48
- package/dist/esm/modules/archive/zip/zip-bytes.js +239 -0
- package/dist/{browser/modules/archive → esm/modules/archive/zip}/zip-entry-metadata.js +3 -3
- package/dist/esm/modules/archive/zip-spec/zip-records.js +126 -0
- package/dist/esm/modules/excel/form-control.js +54 -16
- package/dist/esm/modules/excel/stream/workbook-reader.browser.js +1 -1
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +1 -1
- package/dist/esm/modules/excel/utils/ooxml-validator.js +469 -0
- package/dist/esm/modules/excel/worksheet.js +5 -2
- package/dist/esm/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +13 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/sp-xform.js +112 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +30 -2
- package/dist/esm/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +11 -0
- package/dist/esm/modules/excel/xlsx/xform/sheet/page-setup-xform.js +16 -2
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +117 -5
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +3 -6
- package/dist/esm/modules/excel/xlsx/xlsx.js +1 -1
- package/dist/esm/modules/stream/base-transform.js +34 -20
- package/dist/esm/modules/stream/chunked-builder.js +4 -4
- package/dist/esm/modules/stream/index.browser.js +10 -22
- package/dist/esm/modules/stream/index.js +15 -44
- package/dist/esm/modules/stream/internal/event-utils.js +40 -0
- package/dist/esm/modules/stream/internal/type-guards.js +24 -0
- package/dist/esm/modules/stream/pull-stream.js +107 -43
- package/dist/esm/modules/stream/shared.js +7 -4
- package/dist/esm/modules/stream/streams.browser.js +941 -823
- package/dist/esm/modules/stream/streams.js +146 -95
- package/dist/esm/modules/stream/utils.js +5 -38
- package/dist/iife/THIRD_PARTY_NOTICES.md +81 -0
- package/dist/iife/excelts.iife.js +4979 -2800
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +103 -31
- package/dist/types/index.browser.d.ts +1 -0
- package/dist/{browser/modules/archive → types/modules/archive/compression}/compress.base.d.ts +1 -0
- package/dist/{browser/modules/archive → types/modules/archive/compression}/compress.browser.d.ts +10 -8
- package/dist/types/modules/archive/{streaming-compress.browser.d.ts → compression/streaming-compress.browser.d.ts} +1 -1
- package/dist/types/modules/archive/defaults.d.ts +1 -0
- package/dist/types/modules/archive/index.base.d.ts +4 -4
- package/dist/types/modules/archive/index.browser.d.ts +3 -4
- package/dist/types/modules/archive/index.d.ts +3 -4
- package/dist/types/modules/archive/internal/byte-queue.d.ts +33 -0
- package/dist/types/modules/archive/io/archive-sink.d.ts +8 -0
- package/dist/types/modules/archive/io/archive-source.d.ts +6 -0
- package/dist/types/modules/archive/unzip/index.d.ts +40 -0
- package/dist/types/modules/archive/{parse.base.d.ts → unzip/stream.base.d.ts} +60 -5
- package/dist/types/modules/archive/{parse.browser.d.ts → unzip/stream.browser.d.ts} +2 -2
- package/dist/types/modules/archive/{parse.d.ts → unzip/stream.d.ts} +3 -3
- package/dist/{browser/modules/archive → types/modules/archive/unzip}/zip-parser.d.ts +1 -1
- package/dist/types/modules/archive/utils/async-queue.d.ts +7 -0
- package/dist/types/modules/archive/utils/compressibility.d.ts +10 -0
- package/dist/types/modules/archive/utils/pattern-scanner.d.ts +21 -0
- package/dist/types/modules/archive/utils/zip-extra-fields.d.ts +1 -1
- package/dist/types/modules/archive/zip/index.d.ts +42 -0
- package/dist/types/modules/archive/{streaming-zip.d.ts → zip/stream.d.ts} +29 -6
- package/dist/types/modules/archive/zip/zip-bytes.d.ts +73 -0
- package/dist/types/modules/archive/{zip-entry-metadata.d.ts → zip/zip-entry-metadata.d.ts} +1 -1
- package/dist/types/modules/archive/{zip-records.d.ts → zip-spec/zip-records.d.ts} +20 -0
- package/dist/types/modules/excel/form-control.d.ts +2 -0
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +1 -1
- package/dist/types/modules/excel/utils/ooxml-validator.d.ts +48 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/sp-xform.d.ts +18 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.d.ts +6 -1
- package/dist/types/modules/excel/xlsx/xform/sheet/page-setup-xform.d.ts +1 -0
- package/dist/types/modules/stream/base-transform.d.ts +3 -0
- package/dist/types/modules/stream/buffered-stream.d.ts +2 -12
- package/dist/types/modules/stream/index.browser.d.ts +13 -19
- package/dist/types/modules/stream/index.d.ts +18 -41
- package/dist/types/modules/stream/internal/event-utils.d.ts +17 -0
- package/dist/types/modules/stream/internal/type-guards.d.ts +9 -0
- package/dist/types/modules/stream/pull-stream.d.ts +5 -6
- package/dist/types/modules/stream/shared.d.ts +1 -1
- package/dist/types/modules/stream/streams.browser.d.ts +32 -42
- package/dist/types/modules/stream/streams.d.ts +4 -20
- package/package.json +19 -15
- package/dist/browser/modules/archive/byte-queue.d.ts +0 -18
- package/dist/browser/modules/archive/byte-queue.js +0 -125
- package/dist/browser/modules/archive/parse.base.js +0 -610
- package/dist/browser/modules/archive/utils/zip-extra.d.ts +0 -18
- package/dist/browser/modules/archive/utils/zip-extra.js +0 -68
- package/dist/browser/modules/archive/zip-builder.d.ts +0 -117
- package/dist/browser/modules/archive/zip-builder.js +0 -292
- package/dist/browser/modules/archive/zip-constants.d.ts +0 -18
- package/dist/browser/modules/archive/zip-constants.js +0 -23
- package/dist/browser/modules/archive/zip-records.js +0 -84
- package/dist/cjs/modules/archive/byte-queue.js +0 -129
- package/dist/cjs/modules/archive/parse.base.js +0 -632
- package/dist/cjs/modules/archive/utils/zip-extra.js +0 -74
- package/dist/cjs/modules/archive/zip-builder.js +0 -297
- package/dist/cjs/modules/archive/zip-constants.js +0 -26
- package/dist/cjs/modules/archive/zip-records.js +0 -90
- package/dist/esm/modules/archive/byte-queue.js +0 -125
- package/dist/esm/modules/archive/parse.base.js +0 -610
- package/dist/esm/modules/archive/utils/zip-extra.js +0 -68
- package/dist/esm/modules/archive/zip-builder.js +0 -292
- package/dist/esm/modules/archive/zip-constants.js +0 -23
- package/dist/esm/modules/archive/zip-records.js +0 -84
- package/dist/types/modules/archive/byte-queue.d.ts +0 -18
- package/dist/types/modules/archive/utils/zip-extra.d.ts +0 -18
- package/dist/types/modules/archive/zip-builder.d.ts +0 -117
- package/dist/types/modules/archive/zip-constants.d.ts +0 -18
- /package/dist/browser/modules/archive/{crc32.base.d.ts → compression/crc32.base.d.ts} +0 -0
- /package/dist/browser/modules/archive/{crc32.base.js → compression/crc32.base.js} +0 -0
- /package/dist/browser/modules/archive/{crc32.browser.js → compression/crc32.browser.js} +0 -0
- /package/dist/browser/modules/archive/{deflate-fallback.d.ts → compression/deflate-fallback.d.ts} +0 -0
- /package/dist/browser/modules/archive/{streaming-compress.base.d.ts → compression/streaming-compress.base.d.ts} +0 -0
- /package/dist/browser/modules/archive/{streaming-compress.base.js → compression/streaming-compress.base.js} +0 -0
- /package/dist/browser/modules/archive/{extract.js → unzip/extract.js} +0 -0
- /package/dist/browser/modules/archive/{zip-entry-metadata.d.ts → zip/zip-entry-metadata.d.ts} +0 -0
- /package/dist/browser/modules/archive/{zip-entry-info.d.ts → zip-spec/zip-entry-info.d.ts} +0 -0
- /package/dist/browser/modules/archive/{zip-entry-info.js → zip-spec/zip-entry-info.js} +0 -0
- /package/dist/cjs/modules/archive/{crc32.base.js → compression/crc32.base.js} +0 -0
- /package/dist/cjs/modules/archive/{crc32.browser.js → compression/crc32.browser.js} +0 -0
- /package/dist/cjs/modules/archive/{streaming-compress.base.js → compression/streaming-compress.base.js} +0 -0
- /package/dist/cjs/modules/archive/{extract.js → unzip/extract.js} +0 -0
- /package/dist/cjs/modules/archive/{zip-entry-info.js → zip-spec/zip-entry-info.js} +0 -0
- /package/dist/esm/modules/archive/{crc32.base.js → compression/crc32.base.js} +0 -0
- /package/dist/esm/modules/archive/{crc32.browser.js → compression/crc32.browser.js} +0 -0
- /package/dist/esm/modules/archive/{streaming-compress.base.js → compression/streaming-compress.base.js} +0 -0
- /package/dist/esm/modules/archive/{extract.js → unzip/extract.js} +0 -0
- /package/dist/esm/modules/archive/{zip-entry-info.js → zip-spec/zip-entry-info.js} +0 -0
- /package/dist/{LICENSE → iife/LICENSE} +0 -0
- /package/dist/types/modules/archive/{compress.d.ts → compression/compress.d.ts} +0 -0
- /package/dist/types/modules/archive/{crc32.base.d.ts → compression/crc32.base.d.ts} +0 -0
- /package/dist/types/modules/archive/{crc32.browser.d.ts → compression/crc32.browser.d.ts} +0 -0
- /package/dist/types/modules/archive/{crc32.d.ts → compression/crc32.d.ts} +0 -0
- /package/dist/types/modules/archive/{deflate-fallback.d.ts → compression/deflate-fallback.d.ts} +0 -0
- /package/dist/types/modules/archive/{streaming-compress.base.d.ts → compression/streaming-compress.base.d.ts} +0 -0
- /package/dist/types/modules/archive/{streaming-compress.d.ts → compression/streaming-compress.d.ts} +0 -0
- /package/dist/types/modules/archive/{extract.d.ts → unzip/extract.d.ts} +0 -0
- /package/dist/types/modules/archive/{zip-entry-info.d.ts → zip-spec/zip-entry-info.d.ts} +0 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { extractAll } from "../../archive/unzip/extract.js";
|
|
3
|
+
import { parseSax } from "./parse-sax.js";
|
|
4
|
+
function pushProblem(problems, problem, maxProblems) {
|
|
5
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
problems.push(problem);
|
|
9
|
+
}
|
|
10
|
+
function stripLeadingSlash(p) {
|
|
11
|
+
return p.startsWith("/") ? p.slice(1) : p;
|
|
12
|
+
}
|
|
13
|
+
function isXmlLike(pathName) {
|
|
14
|
+
return pathName.endsWith(".xml") || pathName.endsWith(".rels") || pathName.endsWith(".vml");
|
|
15
|
+
}
|
|
16
|
+
function getRelsSourceDir(relsPath) {
|
|
17
|
+
// In OPC, relationship targets are resolved relative to the SOURCE part directory,
|
|
18
|
+
// not the .rels part directory.
|
|
19
|
+
//
|
|
20
|
+
// Examples:
|
|
21
|
+
// - _rels/.rels -> base "" (package root)
|
|
22
|
+
// - xl/_rels/workbook.xml.rels -> base "xl"
|
|
23
|
+
// - xl/worksheets/_rels/sheet1.xml.rels -> base "xl/worksheets"
|
|
24
|
+
if (relsPath === "_rels/.rels") {
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
const relsMarker = "/_rels/";
|
|
28
|
+
const idx = relsPath.indexOf(relsMarker);
|
|
29
|
+
if (idx === -1) {
|
|
30
|
+
return relsPath.includes("/") ? relsPath.slice(0, relsPath.lastIndexOf("/")) : "";
|
|
31
|
+
}
|
|
32
|
+
return relsPath.slice(0, idx);
|
|
33
|
+
}
|
|
34
|
+
function resolveRelTarget(relsPath, target) {
|
|
35
|
+
const baseDir = getRelsSourceDir(relsPath);
|
|
36
|
+
const resolved = path.posix.normalize(path.posix.join(baseDir, target));
|
|
37
|
+
return resolved.replace(/^\//, "");
|
|
38
|
+
}
|
|
39
|
+
function isSafeResolvedPath(resolved) {
|
|
40
|
+
// Prevent path traversal out of package root.
|
|
41
|
+
// `normalize()` can produce paths starting with "../".
|
|
42
|
+
return !(resolved === ".." || resolved.startsWith("../") || resolved.includes("/../"));
|
|
43
|
+
}
|
|
44
|
+
async function assertXmlWellFormed(xmlText) {
|
|
45
|
+
// parseSax throws on malformed XML.
|
|
46
|
+
for await (const _events of parseSax([xmlText])) {
|
|
47
|
+
// no-op
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function parseContentTypes(xml) {
|
|
51
|
+
const defaults = [];
|
|
52
|
+
const overrides = [];
|
|
53
|
+
const defaultRe = /<Default\s+[^>]*Extension="([^"]+)"[^>]*ContentType="([^"]+)"[^>]*\/>/g;
|
|
54
|
+
const overrideRe = /<Override\s+[^>]*PartName="([^"]+)"[^>]*ContentType="([^"]+)"[^>]*\/>/g;
|
|
55
|
+
for (let match = defaultRe.exec(xml); match; match = defaultRe.exec(xml)) {
|
|
56
|
+
defaults.push({ extension: match[1], contentType: match[2] });
|
|
57
|
+
}
|
|
58
|
+
for (let match = overrideRe.exec(xml); match; match = overrideRe.exec(xml)) {
|
|
59
|
+
overrides.push({ partName: match[1], contentType: match[2] });
|
|
60
|
+
}
|
|
61
|
+
const parseOk = xml.includes("<Types") && (defaults.length > 0 || overrides.length > 0);
|
|
62
|
+
return { defaults, overrides, parseOk };
|
|
63
|
+
}
|
|
64
|
+
function getExtension(p) {
|
|
65
|
+
const base = path.posix.basename(p);
|
|
66
|
+
const idx = base.lastIndexOf(".");
|
|
67
|
+
return idx === -1 ? "" : base.slice(idx + 1);
|
|
68
|
+
}
|
|
69
|
+
function isPackagePart(pathName) {
|
|
70
|
+
// Exclude directories and the [Content_Types].xml pseudo-root.
|
|
71
|
+
return pathName !== "" && pathName !== "[Content_Types].xml";
|
|
72
|
+
}
|
|
73
|
+
function parseRelationships(xml) {
|
|
74
|
+
// Ensure the rels XML itself is parseable.
|
|
75
|
+
// We still use regex to extract rels for speed/portability.
|
|
76
|
+
const relRe = /<Relationship\s+[^>]*Id="([^"]+)"[^>]*Type="([^"]+)"[^>]*Target="([^"]*)"(?:[^>]*TargetMode="([^"]+)")?[^>]*\/>/g;
|
|
77
|
+
const rels = [];
|
|
78
|
+
for (let match = relRe.exec(xml); match; match = relRe.exec(xml)) {
|
|
79
|
+
rels.push({ id: match[1], type: match[2], target: match[3], targetMode: match[4] });
|
|
80
|
+
}
|
|
81
|
+
// Basic sanity: should have <Relationships ...> root.
|
|
82
|
+
const parseOk = xml.includes("<Relationships") && xml.includes("Relationship");
|
|
83
|
+
return { rels, parseOk };
|
|
84
|
+
}
|
|
85
|
+
export async function validateXlsxBuffer(xlsxBuffer, options = {}) {
|
|
86
|
+
const { checkXmlWellFormed = true, checkRelationshipTargets = true, checkContentTypesOverrides = true, checkWorksheetControlWiring = true, maxProblems } = options;
|
|
87
|
+
const problems = [];
|
|
88
|
+
const entries = await extractAll(xlsxBuffer);
|
|
89
|
+
const has = (p) => entries.has(p);
|
|
90
|
+
const mustExist = [
|
|
91
|
+
"[Content_Types].xml",
|
|
92
|
+
"_rels/.rels",
|
|
93
|
+
"xl/workbook.xml",
|
|
94
|
+
"xl/_rels/workbook.xml.rels"
|
|
95
|
+
];
|
|
96
|
+
for (const p of mustExist) {
|
|
97
|
+
if (!has(p)) {
|
|
98
|
+
pushProblem(problems, { kind: "missing-part", file: p, message: `Missing required part: ${p}` }, maxProblems);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// XML well-formedness for all XML-like parts.
|
|
102
|
+
if (checkXmlWellFormed) {
|
|
103
|
+
for (const [p, entry] of entries) {
|
|
104
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
if (entry.isDirectory || !isXmlLike(p)) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const xml = new TextDecoder().decode(entry.data);
|
|
111
|
+
try {
|
|
112
|
+
await assertXmlWellFormed(xml);
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
pushProblem(problems, {
|
|
116
|
+
kind: "xml-malformed",
|
|
117
|
+
file: p,
|
|
118
|
+
message: `Malformed XML: ${err?.message || String(err)}`
|
|
119
|
+
}, maxProblems);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Content types overrides must point to existing parts.
|
|
124
|
+
if (checkContentTypesOverrides && has("[Content_Types].xml")) {
|
|
125
|
+
const ctXml = new TextDecoder().decode(entries.get("[Content_Types].xml").data);
|
|
126
|
+
const { defaults, overrides, parseOk } = parseContentTypes(ctXml);
|
|
127
|
+
if (!parseOk) {
|
|
128
|
+
pushProblem(problems, {
|
|
129
|
+
kind: "content-types-malformed",
|
|
130
|
+
file: "[Content_Types].xml",
|
|
131
|
+
message: "Content types XML missing expected root/entries"
|
|
132
|
+
}, maxProblems);
|
|
133
|
+
}
|
|
134
|
+
const defaultByExt = new Map(defaults.map(d => [d.extension.toLowerCase(), d.contentType]));
|
|
135
|
+
const overrideByPart = new Map();
|
|
136
|
+
for (const ov of overrides) {
|
|
137
|
+
const key = stripLeadingSlash(ov.partName);
|
|
138
|
+
if (overrideByPart.has(key)) {
|
|
139
|
+
pushProblem(problems, {
|
|
140
|
+
kind: "content-types-duplicate-override",
|
|
141
|
+
file: "[Content_Types].xml",
|
|
142
|
+
message: `Duplicate Override PartName: ${ov.partName}`
|
|
143
|
+
}, maxProblems);
|
|
144
|
+
}
|
|
145
|
+
overrideByPart.set(key, ov.contentType);
|
|
146
|
+
}
|
|
147
|
+
// RFC: .rels and .xml defaults are expected in valid packages.
|
|
148
|
+
const relsDefault = defaultByExt.get("rels");
|
|
149
|
+
if (relsDefault !== "application/vnd.openxmlformats-package.relationships+xml") {
|
|
150
|
+
pushProblem(problems, {
|
|
151
|
+
kind: "content-types-missing-default",
|
|
152
|
+
file: "[Content_Types].xml",
|
|
153
|
+
message: "Missing/incorrect Default for .rels (expected application/vnd.openxmlformats-package.relationships+xml)"
|
|
154
|
+
}, maxProblems);
|
|
155
|
+
}
|
|
156
|
+
const xmlDefault = defaultByExt.get("xml");
|
|
157
|
+
if (xmlDefault !== "application/xml") {
|
|
158
|
+
pushProblem(problems, {
|
|
159
|
+
kind: "content-types-missing-default",
|
|
160
|
+
file: "[Content_Types].xml",
|
|
161
|
+
message: "Missing/incorrect Default for .xml (expected application/xml)"
|
|
162
|
+
}, maxProblems);
|
|
163
|
+
}
|
|
164
|
+
for (const ov of overrides) {
|
|
165
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
const zipPath = stripLeadingSlash(ov.partName);
|
|
169
|
+
if (!has(zipPath)) {
|
|
170
|
+
pushProblem(problems, {
|
|
171
|
+
kind: "content-types-missing",
|
|
172
|
+
file: "[Content_Types].xml",
|
|
173
|
+
message: `Override PartName points to missing file: ${ov.partName}`
|
|
174
|
+
}, maxProblems);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Strong check: every part in the zip should have a content type via Default or Override.
|
|
178
|
+
for (const [p, entry] of entries) {
|
|
179
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
if (entry.isDirectory || !isPackagePart(p)) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const overrideType = overrideByPart.get(p);
|
|
186
|
+
if (overrideType) {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const ext = getExtension(p).toLowerCase();
|
|
190
|
+
if (!ext) {
|
|
191
|
+
pushProblem(problems, {
|
|
192
|
+
kind: "content-types-missing-for-part",
|
|
193
|
+
file: "[Content_Types].xml",
|
|
194
|
+
message: `No content type for part without extension: ${p}`
|
|
195
|
+
}, maxProblems);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (!defaultByExt.has(ext)) {
|
|
199
|
+
pushProblem(problems, {
|
|
200
|
+
kind: "content-types-missing-for-part",
|
|
201
|
+
file: "[Content_Types].xml",
|
|
202
|
+
message: `No Default/Override content type for part: ${p} (extension .${ext})`
|
|
203
|
+
}, maxProblems);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// Root relationships must point to the workbook (OPC officeDocument).
|
|
208
|
+
if (has("_rels/.rels")) {
|
|
209
|
+
const rootRelsXml = new TextDecoder().decode(entries.get("_rels/.rels").data);
|
|
210
|
+
const { rels } = parseRelationships(rootRelsXml);
|
|
211
|
+
const hasOfficeDocument = rels.some(r => r.type.includes("/relationships/officeDocument") && r.target === "xl/workbook.xml");
|
|
212
|
+
if (!hasOfficeDocument) {
|
|
213
|
+
pushProblem(problems, {
|
|
214
|
+
kind: "root-rels-missing-officeDocument",
|
|
215
|
+
file: "_rels/.rels",
|
|
216
|
+
message: "Missing officeDocument relationship to xl/workbook.xml"
|
|
217
|
+
}, maxProblems);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Relationships: validate target existence and basic ID uniqueness.
|
|
221
|
+
if (checkRelationshipTargets) {
|
|
222
|
+
for (const [p, entry] of entries) {
|
|
223
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
if (entry.isDirectory || !p.endsWith(".rels")) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
const relsXml = new TextDecoder().decode(entry.data);
|
|
230
|
+
const { rels, parseOk } = parseRelationships(relsXml);
|
|
231
|
+
if (!parseOk) {
|
|
232
|
+
pushProblem(problems, {
|
|
233
|
+
kind: "rels-malformed",
|
|
234
|
+
file: p,
|
|
235
|
+
message: "Relationships XML missing expected root/entries"
|
|
236
|
+
}, maxProblems);
|
|
237
|
+
}
|
|
238
|
+
const ids = new Set();
|
|
239
|
+
for (const rel of rels) {
|
|
240
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
if (ids.has(rel.id)) {
|
|
244
|
+
pushProblem(problems, {
|
|
245
|
+
kind: "rels-duplicate-id",
|
|
246
|
+
file: p,
|
|
247
|
+
message: `Duplicate relationship Id: ${rel.id}`
|
|
248
|
+
}, maxProblems);
|
|
249
|
+
}
|
|
250
|
+
ids.add(rel.id);
|
|
251
|
+
if (rel.targetMode === "External") {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (!rel.target) {
|
|
255
|
+
pushProblem(problems, {
|
|
256
|
+
kind: "rels-empty-target",
|
|
257
|
+
file: p,
|
|
258
|
+
message: `Relationship ${rel.id} (${rel.type}) has empty Target`
|
|
259
|
+
}, maxProblems);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const resolvedTarget = resolveRelTarget(p, rel.target);
|
|
263
|
+
if (!isSafeResolvedPath(resolvedTarget)) {
|
|
264
|
+
pushProblem(problems, {
|
|
265
|
+
kind: "rels-invalid-target-path",
|
|
266
|
+
file: p,
|
|
267
|
+
message: `Rel ${rel.id} (${rel.type}) target escapes package root: ${rel.target} -> ${resolvedTarget}`
|
|
268
|
+
}, maxProblems);
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (!has(resolvedTarget)) {
|
|
272
|
+
pushProblem(problems, {
|
|
273
|
+
kind: "rels-missing-target",
|
|
274
|
+
file: p,
|
|
275
|
+
message: `Rel ${rel.id} (${rel.type}) target missing: ${rel.target} -> ${resolvedTarget}`
|
|
276
|
+
}, maxProblems);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Optional: ensure the source part exists for non-root rels.
|
|
280
|
+
if (p !== "_rels/.rels") {
|
|
281
|
+
// Convert: xl/_rels/workbook.xml.rels -> xl/workbook.xml
|
|
282
|
+
// Convert: xl/worksheets/_rels/sheet1.xml.rels -> xl/worksheets/sheet1.xml
|
|
283
|
+
const srcDir = getRelsSourceDir(p);
|
|
284
|
+
const relsBaseName = path.posix.basename(p);
|
|
285
|
+
const sourceName = relsBaseName.replace(/\.rels$/, "");
|
|
286
|
+
const sourcePath = srcDir ? `${srcDir}/${sourceName}` : sourceName;
|
|
287
|
+
if (!has(sourcePath)) {
|
|
288
|
+
pushProblem(problems, {
|
|
289
|
+
kind: "rels-source-missing",
|
|
290
|
+
file: p,
|
|
291
|
+
message: `Relationships part has no corresponding source part: ${sourcePath}`
|
|
292
|
+
}, maxProblems);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// Workbook -> worksheets wiring.
|
|
298
|
+
if (has("xl/workbook.xml") && has("xl/_rels/workbook.xml.rels")) {
|
|
299
|
+
const workbookXml = new TextDecoder().decode(entries.get("xl/workbook.xml").data);
|
|
300
|
+
const workbookRelsXml = new TextDecoder().decode(entries.get("xl/_rels/workbook.xml.rels").data);
|
|
301
|
+
const { rels: wbRels } = parseRelationships(workbookRelsXml);
|
|
302
|
+
const wbById = new Map(wbRels.map(r => [r.id, r]));
|
|
303
|
+
// Uniqueness checks: sheetId and r:id should not be duplicated.
|
|
304
|
+
const sheetIdRe = /<sheet\b[^>]*\bsheetId="(\d+)"[^>]*\/>/g;
|
|
305
|
+
const seenSheetIds = new Set();
|
|
306
|
+
for (let match = sheetIdRe.exec(workbookXml); match; match = sheetIdRe.exec(workbookXml)) {
|
|
307
|
+
const id = match[1];
|
|
308
|
+
if (seenSheetIds.has(id)) {
|
|
309
|
+
pushProblem(problems, {
|
|
310
|
+
kind: "workbook-duplicate-sheetId",
|
|
311
|
+
file: "xl/workbook.xml",
|
|
312
|
+
message: `Duplicate sheetId in workbook: ${id}`
|
|
313
|
+
}, maxProblems);
|
|
314
|
+
}
|
|
315
|
+
seenSheetIds.add(id);
|
|
316
|
+
}
|
|
317
|
+
const sheetRidSeen = new Set();
|
|
318
|
+
const sheetRidRe = /<sheet\b[^>]*\br:id="(rId\d+)"[^>]*\/>/g;
|
|
319
|
+
for (let match = sheetRidRe.exec(workbookXml); match; match = sheetRidRe.exec(workbookXml)) {
|
|
320
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
321
|
+
break;
|
|
322
|
+
}
|
|
323
|
+
const rid = match[1];
|
|
324
|
+
if (sheetRidSeen.has(rid)) {
|
|
325
|
+
pushProblem(problems, {
|
|
326
|
+
kind: "workbook-duplicate-sheet-rid",
|
|
327
|
+
file: "xl/workbook.xml",
|
|
328
|
+
message: `Duplicate sheet r:id in workbook: ${rid}`
|
|
329
|
+
}, maxProblems);
|
|
330
|
+
}
|
|
331
|
+
sheetRidSeen.add(rid);
|
|
332
|
+
const rel = wbById.get(rid);
|
|
333
|
+
if (!rel) {
|
|
334
|
+
pushProblem(problems, {
|
|
335
|
+
kind: "workbook-sheet-missing-rel",
|
|
336
|
+
file: "xl/workbook.xml",
|
|
337
|
+
message: `Workbook <sheet> references missing relationship: ${rid} (in xl/_rels/workbook.xml.rels)`
|
|
338
|
+
}, maxProblems);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (!rel.type.includes("/relationships/worksheet")) {
|
|
342
|
+
pushProblem(problems, {
|
|
343
|
+
kind: "workbook-sheet-wrong-rel-type",
|
|
344
|
+
file: "xl/workbook.xml",
|
|
345
|
+
message: `Workbook <sheet> ${rid} relationship is not worksheet: ${rel.type}`
|
|
346
|
+
}, maxProblems);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// Worksheet <controls>/<legacyDrawing> wiring.
|
|
351
|
+
if (checkWorksheetControlWiring) {
|
|
352
|
+
for (const [p, entry] of entries) {
|
|
353
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
if (entry.isDirectory || !p.startsWith("xl/worksheets/sheet") || !p.endsWith(".xml")) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
const sheetXml = new TextDecoder().decode(entry.data);
|
|
360
|
+
const relsPath = `xl/worksheets/_rels/${path.posix.basename(p)}.rels`;
|
|
361
|
+
// Excel is sensitive to worksheet child element ordering. In particular,
|
|
362
|
+
// legacyDrawing must come before controls when both are present.
|
|
363
|
+
const legacyDrawingIdx = sheetXml.indexOf("<legacyDrawing");
|
|
364
|
+
const controlsIdx = sheetXml.indexOf("<controls");
|
|
365
|
+
if (legacyDrawingIdx !== -1 && controlsIdx !== -1 && legacyDrawingIdx > controlsIdx) {
|
|
366
|
+
pushProblem(problems, {
|
|
367
|
+
kind: "sheet-legacyDrawing-after-controls",
|
|
368
|
+
file: p,
|
|
369
|
+
message: "Worksheet has <legacyDrawing> after <controls>; Excel may repair or reject this sheet"
|
|
370
|
+
}, maxProblems);
|
|
371
|
+
}
|
|
372
|
+
// Match <control ...> elements (not necessarily self-closing).
|
|
373
|
+
const controlRidRe = /<control\b[^>]*\br:id="(rId\d+)"[^>]*>/g;
|
|
374
|
+
const controlRids = [];
|
|
375
|
+
for (let match = controlRidRe.exec(sheetXml); match; match = controlRidRe.exec(sheetXml)) {
|
|
376
|
+
controlRids.push(match[1]);
|
|
377
|
+
}
|
|
378
|
+
// Excel Online / strict Excel builds may reject or "repair" legacy form controls
|
|
379
|
+
// if the sheet doesn't also have a DrawingML <drawing> part.
|
|
380
|
+
if (controlRids.length > 0 && sheetXml.indexOf("<drawing") === -1) {
|
|
381
|
+
pushProblem(problems, {
|
|
382
|
+
kind: "sheet-controls-missing-drawing",
|
|
383
|
+
file: p,
|
|
384
|
+
message: "Worksheet has legacy <controls> but no <drawing>; Excel may repair/reject legacy form controls"
|
|
385
|
+
}, maxProblems);
|
|
386
|
+
}
|
|
387
|
+
const legacyDrawingRidRe = /<legacyDrawing\b[^>]*\br:id="(rId\d+)"[^>]*\/>/g;
|
|
388
|
+
const legacyDrawingRids = [];
|
|
389
|
+
for (let match = legacyDrawingRidRe.exec(sheetXml); match; match = legacyDrawingRidRe.exec(sheetXml)) {
|
|
390
|
+
legacyDrawingRids.push(match[1]);
|
|
391
|
+
}
|
|
392
|
+
if ((controlRids.length > 0 || legacyDrawingRids.length > 0) && !has(relsPath)) {
|
|
393
|
+
pushProblem(problems, {
|
|
394
|
+
kind: "sheet-missing-rels",
|
|
395
|
+
file: p,
|
|
396
|
+
message: `Worksheet has controls/legacyDrawing but missing rels part: ${relsPath}`
|
|
397
|
+
}, maxProblems);
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
if (!has(relsPath)) {
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
const sheetRelsXml = new TextDecoder().decode(entries.get(relsPath).data);
|
|
404
|
+
const { rels: sheetRels } = parseRelationships(sheetRelsXml);
|
|
405
|
+
const byId = new Map(sheetRels.map(r => [r.id, r]));
|
|
406
|
+
const assertRidType = (rid, expectedTypeIncludes, kindMissing, kindWrong, nodeLabel) => {
|
|
407
|
+
const rel = byId.get(rid);
|
|
408
|
+
if (!rel) {
|
|
409
|
+
pushProblem(problems, {
|
|
410
|
+
kind: kindMissing,
|
|
411
|
+
file: p,
|
|
412
|
+
message: `Sheet ${nodeLabel} references missing relationship: ${rid} (in ${relsPath})`
|
|
413
|
+
}, maxProblems);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (!rel.type.includes(expectedTypeIncludes)) {
|
|
417
|
+
pushProblem(problems, {
|
|
418
|
+
kind: kindWrong,
|
|
419
|
+
file: p,
|
|
420
|
+
message: `Sheet ${nodeLabel} ${rid} relationship is not ${expectedTypeIncludes}: ${rel.type}`
|
|
421
|
+
}, maxProblems);
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
for (const rid of controlRids) {
|
|
425
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
assertRidType(rid, "/relationships/ctrlProp", "sheet-control-missing-rel", "sheet-control-wrong-rel-type", "<control>");
|
|
429
|
+
}
|
|
430
|
+
for (const rid of legacyDrawingRids) {
|
|
431
|
+
if (maxProblems !== undefined && problems.length >= maxProblems) {
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
assertRidType(rid, "/relationships/vmlDrawing", "sheet-legacyDrawing-missing-rel", "sheet-legacyDrawing-wrong-rel-type", "<legacyDrawing>");
|
|
435
|
+
}
|
|
436
|
+
// Common worksheet nodes that are wired via r:id
|
|
437
|
+
// - <drawing r:id="..."/> -> drawing
|
|
438
|
+
// - <comments r:id="..."/> -> comments
|
|
439
|
+
// - <tableParts><tablePart r:id="..."/></tableParts> -> table
|
|
440
|
+
// - <hyperlink r:id="..."/> -> hyperlink
|
|
441
|
+
const drawingRidRe = /<drawing\b[^>]*\br:id="(rId\d+)"[^>]*\/>/g;
|
|
442
|
+
for (let match = drawingRidRe.exec(sheetXml); match; match = drawingRidRe.exec(sheetXml)) {
|
|
443
|
+
assertRidType(match[1], "/relationships/drawing", "sheet-drawing-missing-rel", "sheet-drawing-wrong-rel-type", "<drawing>");
|
|
444
|
+
}
|
|
445
|
+
const commentsRidRe = /<comments\b[^>]*\br:id="(rId\d+)"[^>]*\/>/g;
|
|
446
|
+
for (let match = commentsRidRe.exec(sheetXml); match; match = commentsRidRe.exec(sheetXml)) {
|
|
447
|
+
assertRidType(match[1], "/relationships/comments", "sheet-comments-missing-rel", "sheet-comments-wrong-rel-type", "<comments>");
|
|
448
|
+
}
|
|
449
|
+
const tablePartRidRe = /<tablePart\b[^>]*\br:id="(rId\d+)"[^>]*\/>/g;
|
|
450
|
+
for (let match = tablePartRidRe.exec(sheetXml); match; match = tablePartRidRe.exec(sheetXml)) {
|
|
451
|
+
assertRidType(match[1], "/relationships/table", "sheet-tablePart-missing-rel", "sheet-tablePart-wrong-rel-type", "<tablePart>");
|
|
452
|
+
}
|
|
453
|
+
const hyperlinkRidRe = /<hyperlink\b[^>]*\br:id="(rId\d+)"[^>]*\/>/g;
|
|
454
|
+
for (let match = hyperlinkRidRe.exec(sheetXml); match; match = hyperlinkRidRe.exec(sheetXml)) {
|
|
455
|
+
assertRidType(match[1], "/relationships/hyperlink", "sheet-hyperlink-missing-rel", "sheet-hyperlink-wrong-rel-type", "<hyperlink>");
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
const stats = {
|
|
460
|
+
entryCount: entries.size,
|
|
461
|
+
xmlLikeCount: [...entries.values()].filter((f) => !f.isDirectory && isXmlLike(f.path)).length,
|
|
462
|
+
relsCount: [...entries.values()].filter((f) => !f.isDirectory && f.path.endsWith(".rels")).length
|
|
463
|
+
};
|
|
464
|
+
return {
|
|
465
|
+
ok: problems.length === 0,
|
|
466
|
+
problems,
|
|
467
|
+
stats
|
|
468
|
+
};
|
|
469
|
+
}
|
|
@@ -49,8 +49,11 @@ class Worksheet {
|
|
|
49
49
|
this.pageSetup = Object.assign({}, {
|
|
50
50
|
margins: { left: 0.7, right: 0.7, top: 0.75, bottom: 0.75, header: 0.3, footer: 0.3 },
|
|
51
51
|
orientation: "portrait",
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
// Excel does not normally write these unless explicitly set.
|
|
53
|
+
// Historically we used 4294967295 as a sentinel when parsing, but emitting it
|
|
54
|
+
// can cause strict Excel parsers to treat the workbook as corrupted.
|
|
55
|
+
horizontalDpi: undefined,
|
|
56
|
+
verticalDpi: undefined,
|
|
54
57
|
fitToPage: !!(options.pageSetup &&
|
|
55
58
|
(options.pageSetup.fitToWidth || options.pageSetup.fitToHeight) &&
|
|
56
59
|
!options.pageSetup.scale),
|
|
@@ -7,6 +7,17 @@ import { BaseXform } from "../base-xform.js";
|
|
|
7
7
|
* that stores its properties like objectType, checked state, and linked cell.
|
|
8
8
|
*/
|
|
9
9
|
class CtrlPropXform extends BaseXform {
|
|
10
|
+
_checkedToXmlValue(checked) {
|
|
11
|
+
switch (checked) {
|
|
12
|
+
case "Checked":
|
|
13
|
+
return "1";
|
|
14
|
+
case "Mixed":
|
|
15
|
+
return "2";
|
|
16
|
+
case "Unchecked":
|
|
17
|
+
default:
|
|
18
|
+
return "0";
|
|
19
|
+
}
|
|
20
|
+
}
|
|
10
21
|
get tag() {
|
|
11
22
|
return "formControlPr";
|
|
12
23
|
}
|
|
@@ -15,7 +26,8 @@ class CtrlPropXform extends BaseXform {
|
|
|
15
26
|
const attrs = {
|
|
16
27
|
xmlns: "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main",
|
|
17
28
|
objectType: "CheckBox",
|
|
18
|
-
|
|
29
|
+
// Excel tends to serialize this as numeric state (0/1/2), matching VML x:Checked.
|
|
30
|
+
checked: this._checkedToXmlValue(renderModel.checked),
|
|
19
31
|
lockText: "1"
|
|
20
32
|
};
|
|
21
33
|
// Add linked cell reference
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { BaseXform } from "../base-xform.js";
|
|
2
|
+
class SpXform extends BaseXform {
|
|
3
|
+
get tag() {
|
|
4
|
+
return "xdr:sp";
|
|
5
|
+
}
|
|
6
|
+
render(xmlStream, model) {
|
|
7
|
+
if (!model) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
xmlStream.openNode(this.tag, { macro: "", textlink: "" });
|
|
11
|
+
// Non-visual properties
|
|
12
|
+
xmlStream.openNode("xdr:nvSpPr");
|
|
13
|
+
xmlStream.openNode("xdr:cNvPr", {
|
|
14
|
+
id: model.cNvPrId,
|
|
15
|
+
name: model.name,
|
|
16
|
+
hidden: model.hidden ? "1" : undefined
|
|
17
|
+
});
|
|
18
|
+
xmlStream.openNode("a:extLst");
|
|
19
|
+
xmlStream.openNode("a:ext", {
|
|
20
|
+
uri: "{63B3BB69-23CF-44E3-9099-C40C66FF867C}"
|
|
21
|
+
});
|
|
22
|
+
xmlStream.leafNode("a14:compatExt", {
|
|
23
|
+
spid: model.spid,
|
|
24
|
+
"xmlns:a14": "http://schemas.microsoft.com/office/drawing/2010/main"
|
|
25
|
+
}, undefined);
|
|
26
|
+
xmlStream.closeNode(); // a:ext
|
|
27
|
+
xmlStream.closeNode(); // a:extLst
|
|
28
|
+
xmlStream.closeNode(); // xdr:cNvPr
|
|
29
|
+
xmlStream.leafNode("xdr:cNvSpPr");
|
|
30
|
+
xmlStream.closeNode(); // xdr:nvSpPr
|
|
31
|
+
// Shape properties
|
|
32
|
+
xmlStream.openNode("xdr:spPr", { bwMode: "auto" });
|
|
33
|
+
xmlStream.openNode("a:xfrm");
|
|
34
|
+
xmlStream.leafNode("a:off", { x: 0, y: 0 });
|
|
35
|
+
xmlStream.leafNode("a:ext", { cx: 0, cy: 0 });
|
|
36
|
+
xmlStream.closeNode(); // a:xfrm
|
|
37
|
+
xmlStream.openNode("a:prstGeom", { prst: "rect" });
|
|
38
|
+
xmlStream.leafNode("a:avLst");
|
|
39
|
+
xmlStream.closeNode(); // a:prstGeom
|
|
40
|
+
xmlStream.leafNode("a:noFill");
|
|
41
|
+
xmlStream.openNode("a:ln");
|
|
42
|
+
xmlStream.leafNode("a:noFill");
|
|
43
|
+
xmlStream.closeNode(); // a:ln
|
|
44
|
+
xmlStream.openNode("a:extLst");
|
|
45
|
+
xmlStream.openNode("a:ext", { uri: "{909E8E84-426E-40DD-AFC4-6F175D3DCCD1}" });
|
|
46
|
+
xmlStream.openNode("a14:hiddenFill", {
|
|
47
|
+
"xmlns:a14": "http://schemas.microsoft.com/office/drawing/2010/main"
|
|
48
|
+
});
|
|
49
|
+
xmlStream.openNode("a:solidFill");
|
|
50
|
+
xmlStream.leafNode("a:srgbClr", { val: "F0F0F0" });
|
|
51
|
+
xmlStream.closeNode(); // a:solidFill
|
|
52
|
+
xmlStream.closeNode(); // a14:hiddenFill
|
|
53
|
+
xmlStream.closeNode(); // a:ext
|
|
54
|
+
xmlStream.openNode("a:ext", { uri: "{91240B29-F687-4F45-9708-019B960494DF}" });
|
|
55
|
+
xmlStream.openNode("a14:hiddenLine", {
|
|
56
|
+
w: 9525,
|
|
57
|
+
"xmlns:a14": "http://schemas.microsoft.com/office/drawing/2010/main"
|
|
58
|
+
});
|
|
59
|
+
xmlStream.openNode("a:solidFill");
|
|
60
|
+
xmlStream.leafNode("a:srgbClr", { val: "000000" });
|
|
61
|
+
xmlStream.closeNode(); // a:solidFill
|
|
62
|
+
xmlStream.leafNode("a:miter", { lim: 800000 });
|
|
63
|
+
xmlStream.leafNode("a:headEnd");
|
|
64
|
+
xmlStream.leafNode("a:tailEnd");
|
|
65
|
+
xmlStream.closeNode(); // a14:hiddenLine
|
|
66
|
+
xmlStream.closeNode(); // a:ext
|
|
67
|
+
xmlStream.closeNode(); // a:extLst
|
|
68
|
+
xmlStream.closeNode(); // xdr:spPr
|
|
69
|
+
// Text body (label)
|
|
70
|
+
xmlStream.openNode("xdr:txBody");
|
|
71
|
+
xmlStream.leafNode("a:bodyPr", {
|
|
72
|
+
vertOverflow: "clip",
|
|
73
|
+
wrap: "square",
|
|
74
|
+
lIns: 18288,
|
|
75
|
+
tIns: 18288,
|
|
76
|
+
rIns: 0,
|
|
77
|
+
bIns: 18288,
|
|
78
|
+
anchor: "ctr",
|
|
79
|
+
upright: 1
|
|
80
|
+
});
|
|
81
|
+
xmlStream.leafNode("a:lstStyle");
|
|
82
|
+
xmlStream.openNode("a:p");
|
|
83
|
+
xmlStream.openNode("a:pPr", { algn: "l", rtl: 0 });
|
|
84
|
+
xmlStream.leafNode("a:defRPr", { sz: 1000 });
|
|
85
|
+
xmlStream.closeNode(); // a:pPr
|
|
86
|
+
xmlStream.openNode("a:r");
|
|
87
|
+
xmlStream.openNode("a:rPr", {
|
|
88
|
+
lang: "en-US",
|
|
89
|
+
sz: 800,
|
|
90
|
+
b: 0,
|
|
91
|
+
i: 0,
|
|
92
|
+
u: "none",
|
|
93
|
+
strike: "noStrike",
|
|
94
|
+
baseline: 0
|
|
95
|
+
});
|
|
96
|
+
xmlStream.openNode("a:solidFill");
|
|
97
|
+
xmlStream.leafNode("a:srgbClr", { val: "000000" });
|
|
98
|
+
xmlStream.closeNode(); // a:solidFill
|
|
99
|
+
xmlStream.leafNode("a:latin", { typeface: "Tahoma", pitchFamily: 2, charset: 0 });
|
|
100
|
+
xmlStream.leafNode("a:ea", { typeface: "Tahoma", pitchFamily: 2, charset: 0 });
|
|
101
|
+
xmlStream.leafNode("a:cs", { typeface: "Tahoma", pitchFamily: 2, charset: 0 });
|
|
102
|
+
xmlStream.closeNode(); // a:rPr
|
|
103
|
+
xmlStream.openNode("a:t");
|
|
104
|
+
xmlStream.writeText(model.text ?? "");
|
|
105
|
+
xmlStream.closeNode(); // a:t
|
|
106
|
+
xmlStream.closeNode(); // a:r
|
|
107
|
+
xmlStream.closeNode(); // a:p
|
|
108
|
+
xmlStream.closeNode(); // xdr:txBody
|
|
109
|
+
xmlStream.closeNode(); // xdr:sp
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export { SpXform };
|